{"id":28564142,"url":"https://github.com/appium/node-teen_process","last_synced_at":"2026-04-25T09:04:14.115Z","repository":{"id":22544851,"uuid":"25885779","full_name":"appium/node-teen_process","owner":"appium","description":"A slightly more grown-up version of Node's child_process","archived":false,"fork":false,"pushed_at":"2025-05-21T06:38:46.000Z","size":2344,"stargazers_count":28,"open_issues_count":0,"forks_count":13,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-05-21T06:40:21.990Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/appium.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"open_collective":"appium"}},"created_at":"2014-10-28T19:12:35.000Z","updated_at":"2025-05-21T06:38:48.000Z","dependencies_parsed_at":"2023-10-04T16:01:32.435Z","dependency_job_id":"4cf3b488-5dec-4b84-96e4-531d75224db0","html_url":"https://github.com/appium/node-teen_process","commit_stats":{"total_commits":572,"total_committers":21,"mean_commits":"27.238095238095237","dds":0.4527972027972028,"last_synced_commit":"a645c49259638f07c5fb9c04fd12d0d3d1dbe672"},"previous_names":[],"tags_count":161,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appium%2Fnode-teen_process","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appium%2Fnode-teen_process/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appium%2Fnode-teen_process/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appium%2Fnode-teen_process/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/appium","download_url":"https://codeload.github.com/appium/node-teen_process/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appium%2Fnode-teen_process/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259080990,"owners_count":22802404,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-10T13:09:40.234Z","updated_at":"2026-02-16T09:19:18.790Z","avatar_url":"https://github.com/appium.png","language":"JavaScript","funding_links":["https://opencollective.com/appium"],"categories":[],"sub_categories":[],"readme":"node-teen_process\n=================\n\nA grown-up version of Node's child_process. `exec` is really useful, but it\nsuffers many limitations. This is an es7 (`async`/`await`) implementation of\n`exec` that uses `spawn` under the hood. It takes care of wrapping commands and\narguments so we don't have to care about escaping spaces. It can also return\nstdout/stderr even when the command fails, or times out. Importantly, it's also\nnot susceptible to max buffer issues.\n\n## teen_process.exec\n\nExamples:\n\n```js\nimport { exec } from 'teen_process';\n\n// basic usage\nlet {stdout, stderr, code} = await exec('ls', ['/usr/local/bin']);\nconsole.log(stdout.split(\"\\n\"));  // array of files\nconsole.log(stderr);              // ''\nconsole.log(code);                // 0\n\n// works with spaces\nawait exec('/command/with spaces.sh', ['foo', 'argument with spaces'])\n// as though we had run: \"/command/with spaces.sh\" foo \"argument with spaces\"\n\n// nice error handling that still includes stderr/stdout/code\ntry {\n  await exec('echo_and_exit', ['foo', '10']);\n} catch (e) {\n  console.log(e.message);  // \"Exited with code 10\"\n  console.log(e.stdout);   // \"foo\"\n  console.log(e.code);     // 10\n}\n```\n\nThe `exec` function takes some options, with these defaults:\n\n```js\n{\n  cwd: undefined,\n  env: process.env,\n  timeout: null,\n  killSignal: 'SIGTERM',\n  encoding: 'utf8',\n  ignoreOutput: false,\n  stdio: \"inherit\",\n  isBuffer: false,\n  shell: undefined,\n  logger: undefined,\n  maxStdoutBufferSize: 100 * 1024 * 1024, // 100 MB\n  maxStderrBufferSize: 100 * 1024 * 1024, // 100 MB\n}\n```\n\nMost of these are self-explanatory. `ignoreOutput` is useful if you have a very\nchatty process whose output you don't care about and don't want to add it to\nthe memory consumed by your program.\n\nBoth buffer size limits are needed to avoid memory overflow while collecting\nprocess output. If the overall size of output chunks for different stream types exceeds the the given one then the oldest chunks will be pulled out in order to keep the memory load within the acceptable ranges.\n\nIf you're on Windows, you'll want to pass `shell: true`, because `exec`\nactually uses `spawn` under the hood, and is therefore subject to the issues\nnoted about Windows + `spawn` in [the Node\ndocs](https://nodejs.org/api/child_process.html).\n\nIf `stdio` option is not set to `inheirt`, you may not get colored output from your process. In this case you can explore the subprocess's documentation to see if an option like `--colors` or `FORCE_COLORS` can be specified. you can also try to set `env.FORCE_COLOR = true` and see if it works.\n\nExample:\n\n```js\ntry {\n  await exec('sleep', ['10'], {timeout: 500, killSignal: 'SIGINT'});\n} catch (e) {\n  console.log(e.message);  // \"'sleep 10' timed out after 500ms\"\n}\n```\n\nThe `isBuffer` option specifies that the returned standard I/O is an instance\nof a [Buffer](https://nodejs.org/api/buffer.html).\n\nExample:\n\n```js\nlet {stdout, stderr} = await exec('cat', [filename], {isBuffer: true});\nBuffer.isBuffer(stdout); // true\n```\n\nThe `logger` option allows stdout and stderr to be sent to a particular logger,\nas it it received. This is overridden by the `ignoreOutput` option.\n\n\n## teen_process.SubProcess\n\n`spawn` is already pretty great but for some uses there's a fair amount of\nboilerplate, especially when using in an `async/await` context. `teen_process`\nalso exposes a `SubProcess` class, which can be used to cut down on some\nboilerplate. It has 2 methods, `start` and `stop`:\n\n```js\nimport { SubProcess } from 'teen_process';\n\nasync function tailFileForABit () {\n  let proc = new SubProcess('tail', ['-f', '/var/log/foo.log']);\n  await proc.start();\n  await proc.stop();\n}\n```\n\nErrors with start/stop are thrown in the calling context.\n\n### Options\n\nThe instance constructor accepts same options that Node.js's\n[spawn](https://nodejs.org/api/child_process.html#child_processspawncommand-args-options)\nAPI plus some custom options:\n\n* `isBuffer` - If set to `true` then both stdout and stderr chunks are delivered\n  as buffers rather than strings to the following entities:\n  - `startDetector` argument\n  - `output` event\n* `encoding` - If provided then arguments of the `startDetector` argument and of the\n  `output` event will be encoded to strings using this given encoding, unless `isBuffer`\n  is set to `true`. If no explicit encoding is provided then `utf8` encoding is used by\n  default.\n\n### Events\n\nYou can listen to 8 events:\n\n* `exit`\n* `stop`\n* `end`\n* `die`\n* `output`\n* `lines-stdout`\n* `lines-stderr`\n* `stream-line`\n\n```js\nproc.on('exit', (code, signal) =\u003e {\n  // if we get here, all we know is that the proc exited\n  console.log(`exited with code ${code} from signal ${signal}`);\n  // exited with code 127 from signal SIGHUP\n});\n\nproc.on('stop', (code, signal) =\u003e {\n  // if we get here, we know that we intentionally stopped the proc\n  // by calling proc.stop\n});\n\nproc.on('end', (code, signal) =\u003e {\n  // if we get here, we know that the process stopped outside of our control\n  // but with a 0 exit code\n});\n\nproc.on('die', (code, signal) =\u003e {\n  // if we get here, we know that the process stopped outside of our control\n  // with a non-zero exit code\n});\n\nproc.on('output', (stdout, stderr) =\u003e {\n  console.log(`stdout: ${stdout}`);\n  console.log(`stderr: ${stderr}`);\n});\n\n// line-stderr is just the same\nproc.on('line-stdout', (line) =\u003e {\n  console.log(line);\n});\n\n// stream-line gives you one line at a time, with [STDOUT] or [STDERR]\n// prepended\nproc.on('stream-line', line =\u003e {\n  console.log(line);\n  // [STDOUT] foo\n});\n// so we could do: proc.on('stream-line', console.log.bind(console))\n```\n\n### Start Detectors\n\nHow does `SubProcess` know when to return control from `start()`? Well, the\ndefault is to wait until there is some output. You can also pass in a number,\nwhich will cause it to wait for that number of ms, or a function (which I call\na `startDetector`) which takes stdout and stderr and returns true when you want\ncontrol back. Examples:\n\n```js\nawait proc.start(); // will continue when stdout or stderr has received data\nawait proc.start(0); // will continue immediately\n\nlet sd = (stdout, stderr) =\u003e {\n  return stderr.indexOf('blarg') !== -1;\n};\nawait proc.start(sd); // will continue when stderr receives 'blarg'\n```\n\nA custom `startDetector` can also throw an error if it wants to declare the\nstart unsuccessful. For example, if we know that the first output might contain\na string which invalidates the process (for us), we could define a custom\n`startDetector` as follows:\n\n```js\nlet sd = (stdout, stderr) =\u003e {\n  if (/fail/.test(stderr)) {\n    throw new Error(\"Encountered failure condition\");\n  }\n  return stdout || stderr;\n};\nawait proc.start(sd); // will continue when output is received that doesn't\n                      // match 'fail'\n```\n\nFinally, if you want to specify a maximum time to wait for a process to start,\nyou can do that by passing a second parameter in milliseconds to `start()`:\n\n```js\n// use the default startDetector and throw an error if we wait for more than\n// 1000ms for output\nawait proc.start(null, 1000);\n```\n\n### Finishing Processes\n\nAfter the process has been started you can use `join()` to wait for it to\nfinish on its own:\n\n```js\nawait proc.join(); // will throw on exitcode not 0\nawait proc.join([0, 1]); // will throw on exitcode not 0 or 1\n```\n\nAnd how about killing the processes? Can you provide a custom signal, instead\nof using the default `SIGTERM`? Why yes:\n\n```js\nawait proc.stop('SIGHUP');\n```\n\nIf your process might not be killable and you don't really care, you can also\npass a timeout, which will return control to you in the form of an error after\nthe timeout has passed:\n\n```js\ntry {\n  await proc.stop('SIGHUP', 1000);\n} catch (e) {\n  console.log(\"Proc failed to stop, ignoring cause YOLO\");\n}\n```\n\nAll in all, this makes it super simple to, say, write a script that tails\na file for X seconds and then stops, using async/await and pretty\nstraightforward error handling.\n\n```js\nasync function boredTail (filePath, boredAfter = 10000) {\n  let p = new SubProcess('tail', ['-f', filePath]);\n  p.on('stream-line', console.log);\n  await p.start();\n  await Bluebird.delay(boredAfter);\n  await p.stop();\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fappium%2Fnode-teen_process","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fappium%2Fnode-teen_process","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fappium%2Fnode-teen_process/lists"}