[
  {
    "path": ".appveyor.yml",
    "content": "build: off\nversion: '{build}'\nenvironment:\n  matrix:\n    - nodejs_version: '8'\n    - nodejs_version: '10'\nplatform:\n  - x86\n  - x64\ncache:\n  - node_modules\ninstall:\n  - ps: Install-Product node $env:nodejs_version\n  - npm install\ntest_script:\n  - node --version\n  - npm --version\n  - npm test\nafter_test:\n- npx codecov\n"
  },
  {
    "path": ".codecov.yml",
    "content": "parsers:\n  javascript:\n    enable_partials: yes\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\nindent_size = 2\nindent_style = space\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto\n*.js text eol=lf\n"
  },
  {
    "path": ".github/code-of-conduct.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment include:\n\n- Using welcoming and inclusive language\n- Being respectful of differing viewpoints and experiences\n- Gracefully accepting constructive criticism\n- Focusing on what is best for the community\n- Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n- The use of sexualized language or imagery and unwelcome sexual attention or advances\n- Trolling, insulting/derogatory comments, and personal or political attacks\n- Public or private harassment\n- Publishing others' private information, such as a physical or electronic address, without explicit permission\n- Other conduct which could reasonably be considered inappropriate in a professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [simonepri@outlook.com](mailto:simonepri@outlook.com). The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": ".github/contributing.md",
    "content": "# Contributing\n\nFirst and foremost, thank you! We appreciate that you want to contribute to this project, your time is valuable, and your contributions mean a lot to us.\n\n## Important!\n\nBy contributing to this project, you:\n\n- Agree that you have authored 100% of the content\n- Agree that you have the necessary rights to the content\n- Agree that you have received the necessary permissions from your employer to make the contributions (if applicable)\n- Agree that the content you contribute may be provided under the Project license(s)\n\n## Getting started\n\n**What does \"contributing\" mean?**\n\nCreating an issue is the simplest form of contributing to a project. But there are many ways to contribute, including the following:\n\n- Updating or correcting documentation\n- Feature requests\n- Bug reports\n\nIf you'd like to learn more about contributing in general, the [Guide to Idiomatic Contributing](https://github.com/jonschlinkert/idiomatic-contributing) has a lot of useful information.\n\n## Issues\n\n### Before creating an issue\n\nPlease try to determine if the issue is caused by an underlying library, and if so, create the issue there. Sometimes this is difficult to know. We only ask that you attempt to give a reasonable attempt to find out. Oftentimes the readme will have advice about where to go to create issues.\n\nTry to follow these guidelines\n\n- **Avoid creating issues for implementation help**. It's much better for discoverability, SEO, and semantics - to keep the issue tracker focused on bugs and feature requests - to ask implementation-related questions on [so](stackoverflow.com)\n- **Investigate the issue**:\n- **Check the readme** - oftentimes you will find notes about creating issues, and where to go depending on the type of issue.\n- Create the issue in the appropriate repository.\n\n### Creating an issue\n\nPlease be as descriptive as possible when creating an issue. Give us the information we need to successfully answer your question or address your issue by answering the following in your issue:\n\n- **version**: please note the version of the project are you using\n- **extensions, plugins, helpers, etc** (if applicable): please list any extensions you're using\n- **error messages**: please paste any error messages into the issue, or a [gist](https://gist.github.com/)\n\n### Closing issues\n\nThe original poster or the maintainer's of the project may close an issue at any time. Typically, but not exclusively, issues are closed when:\n\n- The issue is resolved\n- The project's maintainers have determined the issue is out of scope\n- An issue is clearly a duplicate of another issue, in which case the duplicate issue will be linked.\n- A discussion has clearly run its course\n\n## Next steps\n\n**Tips for creating idiomatic issues**\n\nSpending just a little extra time to review best practices and brush up on your contributing skills will, at minimum, make your issue easier to read, easier to resolve, and more likely to be found by others who have the same or similar issue in the future. At best, it will open up doors and potential career opportunities by helping you be at your best.\n\nThe following resources were hand-picked to help you be the most effective contributor you can be:\n\n- The [Guide to Idiomatic Contributing](https://github.com/jonschlinkert/idiomatic-contributing) is a great place for newcomers to start, but there is also information for experienced contributors there.\n- Take some time to learn basic markdown. We can't stress this enough. Don't start pasting code into GitHub issues before you've taken a moment to review this [markdown cheatsheet](https://gist.github.com/jonschlinkert/5854601)\n- The GitHub guide to [basic markdown](https://help.github.com/articles/markdown-basics/) is another great markdown resource.\n- Learn about [GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown/). And if you want to really go above and beyond, read [mastering markdown](https://guides.github.com/features/mastering-markdown/).\n\nAt the very least, please try to:\n\n- Use backticks to wrap code. This ensures that it retains its formatting and isn't modified when it's rendered by GitHub, and makes the code more readable to others\n- When applicable, use syntax highlighting by adding the correct language name after the first \"code fence\"\n"
  },
  {
    "path": ".github/issue-template.md",
    "content": "<!--\nThanks for reporting an Issue!  \nIf you haven't already read the contributing guidelines,\nPlease do that now then proceed.\n-->\n\n## I'm submitting a\n\n- [ ] bug report\n- [ ] feature request\n- [ ] support request => Please do not submit support request here, see note at the top of this template.\n\n<!-- Please mark with [x] the appropriate answer. -->\n\n## Checklist\n\n- [ ] Searched both open and closed issues for duplicates of this issue\n- [ ] Title adequately and *concisely* reflects the feature or the bug\n\n<!-- Please mark with [x] the appropriate answer. -->\n\n## Information\n\n<!--\nPlease provide detailed explanation, stacktraces, related issues, suggestions how to fix, links for us to have context, eg. stackoverflow, gitter, etc\n -->\n"
  },
  {
    "path": ".github/pull-request-template.md",
    "content": "<!--\nThanks for submitting a Pull Request!  \nIf you haven't already read the contributing guidelines,\nPlease do that now then proceed.\n-->\n\n## Checklist\n\n- [ ] All tests are passing\n- [ ] New tests were created to address changes in pr (and tests are passing)\n- [ ] Updated README and/or documentation, if necessary\n- [ ] Added myself / the `contributors` array in package.json\n\n<!-- Please mark with [x] the appropriate answer. -->\n\n## Changes\n\n<!--\nPlease describe the purpose of the pull request.\nThanks for contributing!\n-->\n"
  },
  {
    "path": ".gitignore",
    "content": "\n# Created by https://www.gitignore.io/api/linux,macos,windows,node\n\n### Linux ###\n*~\n\n# temporary files which can be created if a process still has a handle open of a deleted file\n.fuse_hidden*\n\n# KDE directory preferences\n.directory\n\n# Linux trash folder which might appear on any partition or disk\n.Trash-*\n\n# .nfs files are created when an open file is removed but is still being accessed\n.nfs*\n\n### macOS ###\n*.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must end with two \\r\nIcon\n\n# Thumbnails\n._*\n\n# Files that might appear in the root of a volume\n.DocumentRevisions-V100\n.fseventsd\n.Spotlight-V100\n.TemporaryItems\n.Trashes\n.VolumeIcon.icns\n.com.apple.timemachine.donotpresent\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n\n### Node ###\n# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directory for instrumented libs generated by jscoverage/JSCover\nlib-cov\n\n# Coverage directory used by tools like istanbul\ncoverage\n\n# nyc test coverage\n.nyc_output\n\n# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)\n.grunt\n\n# Bower dependency directory (https://bower.io/)\nbower_components\n\n# node-waf configuration\n.lock-wscript\n\n# Compiled binary addons (http://nodejs.org/api/addons.html)\nbuild/Release\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# Typescript v1 declaration files\ntypings/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n\n\n### Windows ###\n# Windows thumbnail cache files\nThumbs.db\nehthumbs.db\nehthumbs_vista.db\n\n# Folder config file\nDesktop.ini\n\n# Recycle Bin used on file shares\n$RECYCLE.BIN/\n\n# Windows Installer files\n*.cab\n*.msi\n*.msm\n*.msp\n\n# Windows shortcuts\n*.lnk\n\n\n# End of https://www.gitignore.io/api/linux,macos,windows,node\n"
  },
  {
    "path": ".npmrc",
    "content": "package-lock=false\nloglevel=silent\n"
  },
  {
    "path": ".travis.yml",
    "content": "sudo: false\nlanguage: node_js\nnode_js:\n  - '8'\n  - '10'\nos:\n  - linux\n  - osx\ncache:\n  directories:\n  - node_modules\nscript:\n  - node --version\n  - npm --version\n  - npm test\nafter_test:\n  - npx codecov\n"
  },
  {
    "path": "cli.js",
    "content": "#!/usr/bin/env node\n/* eslint-disable promise/prefer-await-to-then */\n'use strict';\n\nconst si = require('systeminformation');\n\nconst chalk = require('chalk');\nconst meow = require('meow');\nconst logSymbols = require('log-symbols');\nconst updateNotifier = require('update-notifier');\n\nconst Table = require('easy-table');\n\nconst impact = require('.');\n\nconst cli = meow(\n  `\n  Usage\n    $ impact <script>\n\n  Options\n    --raw                      Show the report as raw object instead of using\n                               the GUI.\n    --interval <number>        The sampling rate in milliseconds.\n\n  Examples\n    $ impact \"console.log('Hello World')\"\n    $ impact --interval=5 \"console.log('Hello World')\"\n`,\n  {\n    flags: {\n      raw: {type: 'boolean', default: false},\n      interval: {type: 'number', default: 125},\n      cwd: {type: 'string', default: process.cwd()}\n    }\n  }\n);\n\nPromise.resolve()\n  .then(() => {\n    updateNotifier({pkg: cli.pkg}).notify();\n  })\n  .then(() => {\n    if (cli.input.length !== 1) {\n      cli.showHelp(0);\n    }\n    return impact(cli.input[0], {\n      interval: cli.flags.interval,\n      cwd: cli.flags.cwd\n    });\n  })\n  .then(async report => {\n    if (cli.flags.raw) {\n      return JSON.stringify(report, null, 2);\n    }\n\n    // SO INFO - BEGIN\n    const osInfo = await si.osInfo();\n\n    const soT = new Table();\n    soT.cell('Distro', `${osInfo.distro}`);\n    soT.cell('Release', `${osInfo.release}`);\n    soT.cell('Release', `${osInfo.release}`);\n    soT.cell('Platform', `${osInfo.platform}`);\n    soT.cell('Arch', `${osInfo.arch}`);\n    soT.newRow();\n    // SO INFO - END\n\n    // CPU INFO - BEGIN\n    const osCpu = await si.cpu();\n\n    const cpuT = new Table();\n    cpuT.cell('CPU', `${osCpu.manufacturer}`);\n    cpuT.cell('Brand', `${osCpu.brand}`);\n    cpuT.cell('Clock', `${osCpu.speed} GHz`);\n    cpuT.cell('Cores', `${osCpu.cores}`);\n    cpuT.newRow();\n    // CPU INFO - BEGIN\n\n    // MEM INFO - BEGIN\n    const osMemLayout = await si.memLayout();\n\n    const memT = new Table();\n    osMemLayout.forEach(mem => {\n      memT.cell('Memory', `${mem.manufacturer}`);\n      memT.cell('Type', `${mem.type}`);\n      memT.cell('Size', `${(mem.size / 1e6).toFixed(3)} MB`);\n      memT.cell('Clock', `${mem.clockSpeed} MHz`);\n      memT.newRow();\n    });\n    // MEM INFO - BEGIN\n\n    // CPU TABLE - BEGIN\n    const [cpu] = [report.stats.cpu];\n    const ctAvarageStr = chalk.keyword('orangered')('avarage');\n    const ctStdevStr = chalk.yellow('σ');\n    const ctAvarageVal = chalk.keyword('orangered')(cpu.mean.toFixed(2));\n    const ctStdevVal = chalk.yellow(cpu.stdev.toFixed(2));\n    const ctMinStr = chalk.cyan('min');\n    const ctMaxStr = chalk.red('max');\n    const ctMinVal = chalk.cyan(cpu.min.toFixed(2));\n    const ctMaxVal = chalk.red(cpu.max.toFixed(2));\n\n    const cuseT = new Table();\n    cuseT.cell(\n      `CPU Usage (${ctAvarageStr} ± ${ctStdevStr})`,\n      `${ctAvarageVal} % ± ${ctStdevVal} %`\n    );\n    cuseT.cell(\n      `CPU Usage Range (${ctMinStr} … ${ctMaxStr})`,\n      `${ctMinVal} % … ${ctMaxVal} %`\n    );\n    cuseT.newRow();\n    // CPU TABLE - END\n\n    // RAM TABLE - BEGIN\n    const [ram] = [report.stats.memory];\n    const rtAvarageStr = chalk.keyword('olivedrab')('avarage');\n    const rtStdevStr = chalk.yellow('σ');\n    const rtAvarageVal = chalk.keyword('olivedrab')(\n      (ram.mean / 1e6).toFixed(3)\n    );\n    const rtStdevVal = chalk.yellow((ram.stdev / 1e6).toFixed(3));\n    const rtMinStr = chalk.cyan('min');\n    const rtMaxStr = chalk.red('max');\n    const rtMinVal = chalk.cyan((ram.min / 1e6).toFixed(3));\n    const rtMaxVal = chalk.red((ram.max / 1e6).toFixed(3));\n\n    const museT = new Table();\n    museT.cell(\n      `RAM Usage (${rtAvarageStr} ± ${rtStdevStr})`,\n      `${rtAvarageVal} MB ± ${rtStdevVal} MB`\n    );\n    museT.cell(\n      `RAM Usage Range (${rtMinStr} … ${rtMaxStr})`,\n      `${rtMinVal} MB … ${rtMaxVal} MB`\n    );\n    museT.newRow();\n    // RAM TABLE - END\n\n    // SAMPLES INFO TABLE - BEGIN\n    const [times] = [report.times];\n    const [samples] = [report.samples];\n    const stVal = chalk.magenta(samples.count);\n    const stRealStr = chalk.keyword('deeppink')('real');\n    const stTargetStr = chalk.blue('target');\n    const stTargetFrequencyVal = chalk.blue((1000 / samples.period).toFixed(3));\n    const stRealFrequencyVal = chalk.keyword('deeppink')(\n      (\n        1000 /\n        ((times.execution.end - times.execution.start) / samples.count)\n      ).toFixed(3)\n    );\n    const stTargetPeriodVal = chalk.blue((samples.period / 1000).toFixed(3));\n    const stRealPeriodVal = chalk.keyword('deeppink')(\n      (\n        (times.execution.end - times.execution.start) /\n        samples.count /\n        1000\n      ).toFixed(3)\n    );\n\n    const tt = new Table();\n    tt.cell(\n      'Execution time',\n      `${chalk.cyan(\n        ((times.execution.end - times.execution.start) / 1000).toFixed(3)\n      )} s`\n    );\n    tt.cell(\n      'Sampling time',\n      `${chalk.cyan(\n        (times.sampling.end - times.sampling.start) / (1000).toFixed(3)\n      )} s`\n    );\n    tt.cell('Samples', `${stVal} samples`);\n    tt.newRow();\n    const st = new Table();\n    st.cell(\n      `Frequency (${stRealStr} ∴ ${stTargetStr})`,\n      `${stRealFrequencyVal} sample/s ∴ ${stTargetFrequencyVal} sample/s`\n    );\n    st.cell(\n      `Period (${stRealStr} ∴ ${stTargetStr})`,\n      `${stRealPeriodVal} s/sample ∴ ${stTargetPeriodVal} s/sample`\n    );\n    st.newRow();\n    // SAMPLES INFO TABLE - END\n\n    // SAMPLES - BEGIN\n    const frames = Object.keys(samples.list);\n\n    const samT = new Table();\n    frames.forEach(frame => {\n      const tfInstantVal = chalk.cyan((frame / 1000).toFixed(3));\n      const tfCPUVal = chalk.keyword('orangered')(\n        samples.list[frame].cpu.toFixed(2)\n      );\n      const tfRAMVal = chalk.keyword('olivedrab')(\n        (samples.list[frame].memory / 1e6).toFixed(3)\n      );\n      const tfPIDSVal = samples.list[frame].processes\n        .map(proc => chalk.gray(proc.pid))\n        .sort();\n\n      samT.cell('Instant', `${tfInstantVal} s`);\n      samT.cell('CPU Usage', `${tfCPUVal} %`);\n      samT.cell('RAM Usage', `${tfRAMVal} MB`);\n      samT.cell('PIDS', `${tfPIDSVal}`);\n      samT.newRow();\n    });\n    // SAMPLES - END\n\n    return (\n      `\\n` +\n      `► ${chalk.bold('SYSTEM REPORT')}\\n\\n` +\n      `${soT}\\n` +\n      `${cpuT}\\n` +\n      `${memT}\\n` +\n      `► ${chalk.bold('USAGE STATISTICS')}\\n\\n` +\n      `${cuseT}\\n` +\n      `${museT}\\n` +\n      `► ${chalk.bold('SAMPLING STATISTICS')}\\n\\n` +\n      `${tt}\\n` +\n      `${st}\\n` +\n      `► ${chalk.bold('SAMPLES')}\\n\\n` +\n      `${samT}`\n    );\n  })\n  .then(console.log)\n  .catch(err => {\n    console.error(`\\n${logSymbols.error} ${err.stack}`);\n    process.exit(1);\n  });\n"
  },
  {
    "path": "index.js",
    "content": "'use strict';\n\nconst path = require('path');\nconst caller = require('caller');\n\nconst Worker = require('./lib/worker.js');\nconst Profiler = require('./lib/profiler.js');\n\n/**\n * Measures the impact of running a certain script on your system.\n * Monitors the cpu and memory usage of the whole tree of processes generated by\n * the script provided.\n * @public\n * @param  {string} code The source code to test.\n * @param  {Object} [options] Optional configurations.\n * @param  {number} [options.interval=125] Sampling interval in milliseconds.\n * @param  {string} [options.cwd=caller] CWD for the script.\n * @return {Promise.<Object>} An object containing the results.\n */\nasync function sympact(code, options) {\n  if (typeof code !== 'string') {\n    throw new TypeError(\"The 'code' paramater must a string'\");\n  }\n  if (typeof options === 'undefined') {\n    options = {};\n  }\n  if (typeof options !== 'object') {\n    throw new TypeError(\"The 'options' paramater must an object'\");\n  }\n  const interval = options.interval || 125;\n  const cwd = options.cwd || path.dirname(caller());\n\n  if (interval < 1) {\n    throw new TypeError(\"The 'interval' paramater must be greater than 0'\");\n  }\n\n  return new Promise((resolve, reject) => {\n    const slave = new Worker(code, cwd);\n    const probe = new Profiler(slave.pid(), interval);\n\n    slave.on('ready', async () => {\n      await probe.watch();\n      slave.run();\n    });\n\n    slave.on('after', async (start, end) => {\n      await probe.unwatch();\n      slave.kill();\n\n      resolve(probe.report(start, end));\n    });\n\n    slave.on('error', async err => {\n      await probe.unwatch();\n      slave.kill();\n\n      reject(err);\n    });\n  });\n}\n\nmodule.exports = sympact;\n"
  },
  {
    "path": "lib/profiler.js",
    "content": "'use strict';\n\nconst stats = require('stats-lite');\nconst pidusage = require('pidusage');\nconst pidtree = require('pidtree');\n\nfunction getAverages(values) {\n  return {\n    mean: stats.mean(values),\n    median: stats.median(values),\n    stdev: stats.stdev(values),\n    max: Math.max(...values),\n    min: Math.min(...values)\n  };\n}\n\nfunction Profiler(ppid, period) {\n  const snapshots = {};\n\n  let ending = false;\n\n  let interval = null;\n  let start = null;\n  let end = null;\n\n  async function read() {\n    if (!start || end) return;\n\n    try {\n      const procl = await pidtree(ppid, {root: true});\n      const statistics = await pidusage(procl);\n\n      const frame = Date.now() - start;\n      snapshots[frame] = Object.values(statistics);\n    } catch (err) {\n      console.log(err);\n    }\n  }\n\n  async function watch() {\n    if (start || end) return;\n    start = Date.now();\n    await read();\n    interval = setInterval(read, period);\n  }\n\n  async function unwatch() {\n    if (!start || end || ending) return;\n    ending = true;\n    clearInterval(interval);\n    await read();\n    end = Date.now();\n    interval = null;\n  }\n\n  async function report(from, to) {\n    const frames = Object.keys(snapshots);\n\n    const samples = {};\n    frames.forEach(frame => {\n      samples[frame] = {\n        cpu: snapshots[frame].reduce((acc, cur) => acc + cur.cpu, 0),\n        memory: snapshots[frame].reduce((acc, cur) => acc + cur.memory, 0),\n        processes: snapshots[frame]\n      };\n    });\n\n    const usage = {\n      cpu: getAverages(frames.map(frame => samples[frame].cpu)),\n      memory: getAverages(frames.map(frame => samples[frame].memory))\n    };\n\n    const report = {\n      times: {\n        sampling: {\n          start,\n          end\n        },\n        execution: {\n          start: from || start,\n          end: to || end\n        }\n      },\n      stats: {\n        cpu: usage.cpu,\n        memory: usage.memory\n      },\n      samples: {\n        period,\n        count: frames.length,\n        list: samples\n      }\n    };\n    return report;\n  }\n\n  return {\n    watch,\n    unwatch,\n    report\n  };\n}\n\nmodule.exports = Profiler;\n"
  },
  {
    "path": "lib/vm.js",
    "content": "process.on('uncaughtException', err => {\n  process.send({event: 'error', error: plain(err)});\n});\n\nprocess.on('unhandledRejection', reason => {\n  process.send({event: 'error', error: plain(reason)});\n});\n\nprocess.on('message', async msg => {\n  if (typeof msg !== 'object' && msg.event !== 'run') return;\n  try {\n    const start = Date.now();\n    await run();\n    const end = Date.now();\n    process.send({event: 'after', start, end});\n  } catch (err) {\n    process.send({event: 'error', error: plain(err)});\n  }\n});\n\nsetTimeout(() => {\n  process.send({event: 'ready'});\n}, 1000);\n\nfunction plain(obj) {\n  const plainObject = {};\n  Object.getOwnPropertyNames(obj).forEach(key => {\n    plainObject[key] = obj[key];\n  });\n  return plainObject;\n}\n\nconst req = require;\n// eslint-disable-next-line no-global-assign\nrequire = module => {\n  return module === '.' || module.startsWith('./')\n    ? req(req('path').join(process.cwd(), module))\n    : req(module);\n};\n\nasync function run() {\n  /* CODE */\n}\n"
  },
  {
    "path": "lib/worker.js",
    "content": "'use strict';\n\nconst EventEmitter = require('events');\nconst {fork} = require('child_process');\nconst fs = require('fs-extra');\nconst tempy = require('tempy');\n\nconst VM = fs.readFileSync(`${__dirname}/vm.js`, 'utf8');\n\nfunction Worker(script, cwd) {\n  const emitter = new EventEmitter();\n\n  const path = tempy.file({name: 'code.js'});\n  const code = VM.replace('/* CODE */', script);\n  fs.writeFileSync(path, code);\n  const child = fork(path, {windowsHide: true, cwd});\n\n  child.on('message', async message => {\n    if (message.event === 'error') {\n      const err = new Error('unable to execute');\n      err.stack = `${err.stack}\\nCaused By: ${message.error.stack}`;\n      emitter.emit(message.event, err);\n    } else if (message.event === 'after') {\n      emitter.emit(message.event, message.start, message.end);\n    } else {\n      emitter.emit(message.event);\n    }\n  });\n\n  child.on('error', error => {\n    const err = new Error('unable to execute');\n    err.stack = `${err.stack}\\nCaused By: ${error.stack}`;\n    emitter.emit('error', err);\n  });\n\n  emitter.pid = () => child.pid;\n  emitter.run = () => {\n    child.send({event: 'run'});\n  };\n  emitter.kill = () => {\n    child.kill();\n  };\n  return emitter;\n}\n\nmodule.exports = Worker;\n"
  },
  {
    "path": "license",
    "content": "MIT License\n\nCopyright (c) 2018 Simone Primarosa\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"sympact\",\n  \"version\": \"0.0.7\",\n  \"description\": \"An easy way to calculate the 'impact' of running a task in Node.JS\",\n  \"license\": \"MIT\",\n  \"homepage\": \"https://github.com/simonepri/sympact#readme\",\n  \"repository\": \"github:simonepri/sympact\",\n  \"bugs\": {\n    \"url\": \"https://github.com/simonepri/sympact/issues\",\n    \"email\": \"simonepri@outlook.com\"\n  },\n  \"author\": \"Simone Primarosa <simonepri@outlook.com> (https://simoneprimarosa.com)\",\n  \"contributors\": [\n    \"Simone Primarosa <simonepri@outlook.com> (https://simoneprimarosa.com)\"\n  ],\n  \"keywords\": [\n    \"benchmark\",\n    \"benchmarking\",\n    \"profile\",\n    \"profiler\",\n    \"profiling\",\n    \"sys\",\n    \"system\",\n    \"compute\",\n    \"calculate\",\n    \"impact\",\n    \"impactjs\",\n    \"damage\",\n    \"process\",\n    \"processes\",\n    \"isolation\",\n    \"vm\",\n    \"fork\",\n    \"script\",\n    \"task\",\n    \"tree\",\n    \"pid\",\n    \"pids\",\n    \"cpu\",\n    \"ram\",\n    \"processor\",\n    \"memory\",\n    \"cli\"\n  ],\n  \"main\": \"index.js\",\n  \"bin\": {\n    \"sympact\": \"cli.js\"\n  },\n  \"files\": [\n    \"lib\",\n    \"index.js\",\n    \"cli.js\"\n  ],\n  \"engines\": {\n    \"node\": \">=8\"\n  },\n  \"scripts\": {\n    \"start\": \"node cli.js\",\n    \"test\": \"xo &&nyc ava\",\n    \"release\": \"np\",\n    \"update\": \"npm-check -u\"\n  },\n  \"dependencies\": {\n    \"caller\": \"^1.0.1\",\n    \"chalk\": \"^2.4.1\",\n    \"easy-table\": \"^1.1.1\",\n    \"fs-extra\": \"^6.0.1\",\n    \"log-symbols\": \"^2.2.0\",\n    \"meow\": \"^5.0.0\",\n    \"pidtree\": \"^0.3.0\",\n    \"pidusage\": \"^2.0.6\",\n    \"stats-lite\": \"^2.1.1\",\n    \"systeminformation\": \"^3.41.3\",\n    \"tempy\": \"^0.2.1\",\n    \"update-notifier\": \"^2.5.0\"\n  },\n  \"devDependencies\": {\n    \"ava\": \"*\",\n    \"joi\": \"^13.3.0\",\n    \"np\": \"^3.0.1\",\n    \"npm-check\": \"*\",\n    \"nyc\": \"*\",\n    \"xo\": \"*\"\n  },\n  \"ava\": {\n    \"verbose\": true\n  },\n  \"nyc\": {\n    \"reporter\": [\n      \"lcovonly\",\n      \"text\"\n    ]\n  },\n  \"xo\": {\n    \"prettier\": true,\n    \"space\": true\n  }\n}\n"
  },
  {
    "path": "readme.md",
    "content": "<p align=\"center\">\n  <a href=\"https://github.com/simonepri/sympact\">\n    <img src=\"https://github.com/simonepri/sympact/raw/master/media/sympact.png\" alt=\"sympact\" width=\"150\"/>\n  </a>\n</p>\n<p align=\"center\">\n  <!-- CI - TravisCI -->\n  <a href=\"https://travis-ci.org/simonepri/sympact\">\n    <img src=\"https://img.shields.io/travis/simonepri/sympact/master.svg?label=MacOS%20%26%20Linux\" alt=\"Mac/Linux Build Status\" />\n  </a>\n  <!-- CI - AppVeyor -->\n  <a href=\"https://ci.appveyor.com/project/simonepri/sympact\">\n    <img src=\"https://img.shields.io/appveyor/ci/simonepri/sympact/master.svg?label=Windows\" alt=\"Windows Build status\" />\n  </a>\n  <!-- Coverage - Codecov -->\n  <a href=\"https://codecov.io/gh/simonepri/sympact\">\n    <img src=\"https://img.shields.io/codecov/c/github/simonepri/sympact/master.svg\" alt=\"Codecov Coverage report\" />\n  </a>\n  <!-- DM - Snyk -->\n  <a href=\"https://snyk.io/test/github/simonepri/sympact?targetFile=package.json\">\n    <img src=\"https://snyk.io/test/github/simonepri/sympact/badge.svg?targetFile=package.json\" alt=\"Known Vulnerabilities\" />\n  </a>\n  <!-- DM - David -->\n  <a href=\"https://david-dm.org/simonepri/sympact\">\n    <img src=\"https://david-dm.org/simonepri/sympact/status.svg\" alt=\"Dependency Status\" />\n  </a>\n\n  <br/>\n\n  <!-- Code Style - XO-Prettier -->\n  <a href=\"https://github.com/xojs/xo\">\n    <img src=\"https://img.shields.io/badge/code_style-XO+Prettier-5ed9c7.svg\" alt=\"XO Code Style used\" />\n  </a>\n  <!-- Test Runner - AVA -->\n  <a href=\"https://github.com/avajs/ava\">\n    <img src=\"https://img.shields.io/badge/test_runner-AVA-fb3170.svg\" alt=\"AVA Test Runner used\" />\n  </a>\n  <!-- Test Coverage - Istanbul -->\n  <a href=\"https://github.com/istanbuljs/nyc\">\n    <img src=\"https://img.shields.io/badge/test_coverage-NYC-fec606.svg\" alt=\"Istanbul Test Coverage used\" />\n  </a>\n  <!-- Init - ni -->\n  <a href=\"https://github.com/simonepri/ni\">\n    <img src=\"https://img.shields.io/badge/initialized_with-ni-e74c3c.svg\" alt=\"NI Scaffolding System used\" />\n  </a>\n  <!-- Release - np -->\n  <a href=\"https://github.com/sindresorhus/np\">\n    <img src=\"https://img.shields.io/badge/released_with-np-6c8784.svg\" alt=\"NP Release System used\" />\n  </a>\n\n  <br/>\n\n  <!-- Version - npm -->\n  <a href=\"https://www.npmjs.com/package/sympact\">\n    <img src=\"https://img.shields.io/npm/v/sympact.svg\" alt=\"Latest version on npm\" />\n  </a>\n  <!-- License - MIT -->\n  <a href=\"https://github.com/simonepri/sympact/tree/master/license\">\n    <img src=\"https://img.shields.io/github/license/simonepri/sympact.svg\" alt=\"Project license\" />\n  </a>\n</p>\n<p align=\"center\">\n  🔥 An easy way to calculate the 'impact' of running a task in Node.JS\n  <br/>\n\n  <sub>\n    Coded with ❤️ by <a href=\"#authors\">Simone Primarosa</a>.\n  </sub>\n</p>\n\n## Synopsis\n\nSympact runs a script and profiles its execution time, CPU usage, and memory usage. Sympact then returns an execution report containing the averages of the results.\n\nDo you believe that this is *useful*?\nHas it *saved you time*?\nOr maybe you simply *like it*?  \nIf so, [show your appreciation with a Star ⭐️][start].\n\n## How it works\n\nsympact spawns a separate process and runs your script in an isolated\nnode process and then collects statistics about the system's resource used by\nyour script.\n\nThe data are collected using [pidusage][gh:pidusage] in combination with\n[pidtree][gh:pidtree].  \n**The main difference between other projects is that sympact will also\n\"profile\" processes spawned by your script or by any of its children.**\n\nFinally a report of the samples taken is computed and returned to you.\n\n## Install\n\n```bash\nnpm install --save sympact\n```\n\n## Usage\n\n```js\nconst impact = require('sympact');\n\nconst report = await impact(`\n  let r = 2;\n  let c = 10e7;\n  while (c--) r = Math.pow(r, r);\n  return r;\n`, {interval: 125}); // 125 ms of sampling rate\n\nconsole.log(report.times.execution.end - report.times.execution.start);\n// => 2700 ms\nconsole.log(report.stats.cpu.mean);\n// => 90.45 % on my machine\nconsole.log(report.stats.memory.mean);\n// => 27903317.33 bytes on my machine\n```\n\n## CLI\n\n<img src=\"https://github.com/simonepri/sympact/raw/master/media/cli.gif\" alt=\"sympact CLI\" width=\"475\" align=\"right\"/>\n\nTo make it more usable, a CLI is bundled with the package allowing for an aesthetically pleasing report.\n\n```bash\n  npx sympact \"console.log('Hello World')\"\n```\n\nYou can even require other files.\n```bash\n  npx sympact \"\n    const {spawn} = require('child_process');\n    let childno = 10;\n    let childs = [];\n    for (let i = 0; i < childno; i++) {\n      childs.push(spawn('node', ['-e', 'setInterval(()=>{let c=10e3;while(c--);},10)']));\n    }\n    let c = 10e6;\n    let m = {};\n    while (c--)  m[c] = c;\n    for (let i = 0; i < childno; i++) {\n      childs[i].kill();\n    }\n  \"\n```\n<br/><br/>\n\n## Report object\n\nThe object returned by the promise will look like this.\n```js\n{\n  \"times\": {\n    \"sampling\": {\n      \"start\": 1521666020917,          // ms since epoch\n      \"end\": 1521666036041             // ms since epoch\n    },\n    \"execution\": {\n      \"start\": 1521666020958,          // ms since epoch\n      \"end\": 1521666036006             // ms since epoch\n    }\n  },\n  \"stats\": {\n    \"cpu\": {                           // CPU usage statistics (percentage)\n      \"mean\": 74.17368421052636,\n      \"median\": 75.1,\n      \"stdev\": 11.820700343128212,\n      \"max\": 94.7,\n      \"min\": 0.7\n    },\n    \"memory\": {                        // RAM usage statistics (bytes)\n      \"mean\": 1080202186.1052632,\n      \"median\": 1327509504,\n      \"stdev\": 416083837.44653314,\n      \"max\": 1327513600,\n      \"min\": 23441408\n    }\n  },\n  \"samples\": {                         // List of all the samples taken\n    \"period\": 125,                     // Sampling period\n    \"count\": 114,                      // Number of samples taken\n    \"list\": {\n      \"39\": {                          // Taken after 39ms after the start of the watch command\n        \"cpu\": 0.7,                    // Sum of the usages of all the processes\n        \"memory\": 23441408,            // Sum of the memory of all the processes\n        \"processes\": [{                // List of processes profiled in this timeframe\n          \"cpu\": 0.7,\n          \"memory\": 23441408,\n          \"ppid\": 837,\n          \"pid\": 839,\n          \"ctime\": 6000,\n          \"elapsed\": 1000,\n          \"timestamp\": 1521666020955   // ms since epoch\n        }]\n      },\n      \"205\": {\n        \"cpu\": 14.8,\n        \"memory\": 55685120,\n        \"processes\": [{\n          \"cpu\": 14.8,\n          \"memory\": 55685120,\n          \"ppid\": 837,\n          \"pid\": 839,\n          \"ctime\": 15000,\n          \"elapsed\": 2000,\n          \"timestamp\": 1521666021122\n        }]\n      },\n\n      [...]\n\n      \"15124\": {\n        \"cpu\": 81.2,\n        \"memory\": 878133248,\n        \"processes\": [{\n          \"cpu\": 81.2,\n          \"memory\": 878133248,\n          \"ppid\": 837,\n          \"pid\": 839,\n          \"ctime\": 47600,\n          \"elapsed\": 17000,\n          \"timestamp\": 1521666036041\n        }]\n      }\n    }\n  }\n}\n```\n\n## API\n\n<a name=\"sympact\"></a>\n\n### sympact(code, [options]) ⇒ <code>Promise.&lt;Object&gt;</code>\nMeasures the impact of running a certain script on your system.\nMonitors the cpu and memory usage of the whole tree of processes generated by\nthe script provided.\n\n**Kind**: global function  \n**Returns**: <code>Promise.&lt;Object&gt;</code> - An object containing the results.  \n**Access**: public  \n\n| Param | Type | Default | Description |\n| --- | --- | --- | --- |\n| code | <code>string</code> |  | The source code to test. |\n| [options] | <code>Object</code> |  | Optional configurations. |\n| [options.interval] | <code>number</code> | <code>125</code> | Sampling interval in milliseconds. |\n| [options.cwd] | <code>string</code> | <code>&quot;caller path&quot;</code> | CWD for the script. |\n\n## Contributing\n\nContributions are REALLY welcome and if you find a security flaw in this code, PLEASE [report it][new issue].  \nPlease check the [contributing guidelines][contributing] for more details. Thanks!\n\n## Authors\n\n- **Simone Primarosa** -  *Follow* me on *Github* ([:octocat:@simonepri][github:simonepri]) and on  *Twitter* ([🐦@simonepri][twitter:simoneprimarosa])\n\nSee also the list of [contributors][contributors] who participated in this project.\n\n## License\n\nThis project is licensed under the MIT License - see the [license][license] file for details.\n\n<!-- Links -->\n[start]: https://github.com/simonepri/sympact#start-of-content\n[new issue]: https://github.com/simonepri/sympact/issues/new\n[contributors]: https://github.com/simonepri/sympact/contributors\n\n[license]: https://github.com/simonepri/sympact/tree/master/license\n[contributing]: https://github.com/simonepri/sympact/tree/master/.github/contributing.md\n\n[github:simonepri]: https://github.com/simonepri\n[twitter:simoneprimarosa]: http://twitter.com/intent/user?screen_name=simoneprimarosa\n\n[gh:pidusage]: https://github.com/soyuka/pidusage\n[gh:pidtree]: https://github.com/simonepri/pidtree\n"
  },
  {
    "path": "test.js",
    "content": "import test from 'ava';\n\nimport joi from 'joi';\n\nimport m from '.';\n\nconst schema = {\n  integer: joi\n    .number()\n    .integer()\n    .min(0),\n  time: joi.object().keys({\n    start: joi\n      .number()\n      .integer()\n      .min(0),\n    end: joi\n      .number()\n      .integer()\n      .min(0)\n  }),\n  avarage: joi.object().keys({\n    mean: joi.number().min(0),\n    median: joi.number().min(0),\n    stdev: joi.number().min(0),\n    max: joi.number().min(0),\n    min: joi.number().min(0)\n  }),\n  sample: joi.object().keys({\n    cpu: joi.number().min(0),\n    memory: joi\n      .number()\n      .integer()\n      .min(0),\n    processes: joi.array().items(\n      joi.object().keys({\n        cpu: joi.number().min(0),\n        memory: joi\n          .number()\n          .integer()\n          .min(0),\n        ppid: joi\n          .number()\n          .integer()\n          .min(0),\n        pid: joi\n          .number()\n          .integer()\n          .min(0),\n        ctime: joi\n          .number()\n          .integer()\n          .min(0),\n        elapsed: joi\n          .number()\n          .integer()\n          .min(0),\n        timestamp: joi\n          .number()\n          .integer()\n          .min(0)\n      })\n    )\n  })\n};\n\ntest('should monitor pid without childs correctly', async t => {\n  const report = await m(`\n    let r = 2;\n    let c = 10e5;\n    while (c--) r = Math.pow(r, r);\n    return r;\n  `);\n  t.is(typeof report, 'object');\n  t.is(typeof report.times, 'object');\n  t.is(joi.validate(report.times.sampling, schema.time).error, null);\n  t.is(joi.validate(report.times.execution, schema.time).error, null);\n  t.is(typeof report.stats, 'object');\n  t.is(joi.validate(report.stats.cpu, schema.avarage).error, null);\n  t.is(joi.validate(report.stats.memory, schema.avarage).error, null);\n  t.is(typeof report.samples, 'object');\n  t.is(joi.validate(report.samples.count, schema.integer).error, null);\n  t.is(joi.validate(report.samples.period, schema.integer).error, null);\n  t.is(typeof report.samples.list, 'object');\n  // eslint-disable-next-line guard-for-in\n  for (const key in report.samples.list) {\n    t.is(joi.validate(report.samples.list[key], schema.sample).error, null);\n  }\n});\n\ntest('should monitor pid with childs correctly', async t => {\n  const report = await m(`\n    const {spawn} = require('child_process');\n    let childno = 10;\n    let childs = [];\n    for (let i = 0; i < childno; i++) {\n      childs.push(spawn('node', ['-e', 'setInterval(()=>{let c=10e2;while(c--);},1000)']));\n    }\n    let c = 10e6;\n    let m = {};\n    while (c--)  m[c] = c;\n    for (let i = 0; i < childno; i++) {\n      childs[i].kill();\n    }\n  `);\n  t.is(typeof report, 'object');\n  t.is(typeof report.times, 'object');\n  t.is(joi.validate(report.times.sampling, schema.time).error, null);\n  t.is(joi.validate(report.times.execution, schema.time).error, null);\n  t.is(typeof report.stats, 'object');\n  t.is(joi.validate(report.stats.cpu, schema.avarage).error, null);\n  t.is(joi.validate(report.stats.memory, schema.avarage).error, null);\n  t.is(typeof report.samples, 'object');\n  t.is(joi.validate(report.samples.count, schema.integer).error, null);\n  t.is(joi.validate(report.samples.period, schema.integer).error, null);\n  t.is(typeof report.samples.list, 'object');\n  // eslint-disable-next-line guard-for-in\n  for (const key in report.samples.list) {\n    t.is(joi.validate(report.samples.list[key], schema.sample).error, null);\n  }\n});\n"
  }
]