superchild
Version:
A smarter replacement for node.js's child_process module.
152 lines (115 loc) • 7.4 kB
Markdown
[](https://circleci.com/gh/mayanklahiri/node-superchild)
[](https://github.com/mayanklahiri/node-superchild/issues)
[](https://github.com/mayanklahiri/node-superchild/stargazers)
[](https://raw.githubusercontent.com/mayanklahiri/node-superchild/master/LICENSE)
[](https://badge.fury.io/js/superchild)
[](https://david-dm.org/mayanklahiri/node-superchild.svg)
[](https://www.bithound.io/github/mayanklahiri/node-superchild)
[](https://www.bithound.io/github/mayanklahiri/node-superchild)
[](https://twitter.com/intent/tweet?text=Wow:&url=%5Bobject%20Object%5D)
**Superchild** is a POSIX-only (e.g., Linux, Mac OS X) wrapper around
node.js's built-in `child_process` module, which requires a lot of
care and attention to use correctly.
Links:
* [Annotated source code](http://mayanklahiri.github.io/node-superchild/superchild.html)
* [Current test coverage](http://mayanklahiri.github.io/node-superchild/coverage/lib/index.html)
* [Github page](https://github.com/mayanklahiri/node-superchild)
* [NPM page](https://www.npmjs.com/package/superchild)
The purpose of Superchild is to allow large node.js programs
to be split into independent processes (and sub-processes, resulting
in **process trees**), while handling the tedious parts of process
management and communication.
Superchild aims to be **compatible** with any program
that reads from and writes to their `stdin`, `stdout`, and `stderr`
streams, regardless of what language the program is written in.
This allows interesting hacks like using `ssh` to execute a
module on a remote host, written in another language, over an
encrypted link, while using the near-universal format of
line-delimited JSON messages over `stdio`.
**Features** that make Superchild different from node's built-in
`child_process` module include the following (many of these
are currently possible only due to restricting focus to POSIX
platforms, i.e., not Windows):
1. A single function to replace `fork()`, `exec()`,
and `spawn()` from the built-in `child_process` module.
2. Waits for `stdout` and `stderr` streams to end before
emitting an `exit` event, unlike `child_process.ChildProcess`.
3. Handles isolating child process and its children in a
separate, __detached process group__ that can be terminated
as a subtree using the POSIX `kill` command. This means
that calling `close()` on a Superchild instance will kill
not just the child process, but all _its_ child processes
and so on (i.e., the entire process group lead by the child).
Note that if any processes in the sub-tree detach themselves
into a new process group, they will not be part of our
child's process group, and will not be killed.
4. Handles __graceful termination__ of child's entire process group
using `SIGTERM` -> `SIGKILL` signals with a configurable timeout.
5. Handles __unexpected termination__ of the *current* process by
killing the child's entire process group immediately with `SIGKILL`.
6. Automatically serializes and deserializes __line-delimited JSON__
values (LD-JSON) sent to and received from child, intermixed
with `stdout`. `stderr` is passed through unbuffered. Effectively,
this means that the child's `stdout` stream is demultiplexed
into the child streams `stdout_line` (parsed raw text lines),
`json_object` (parsed JSON objects), and `json_array` (parsed JSON
arrays). Regular processes have 3 I/O streams (stdin, stdout,
stderr); Superchildren have 6 streams (stdin, stdout, stderr,
stdout_line, json_object, json_array).
## Install
npm install superchild
## Usage
##### Run a shell command
Get a directory listing line-by-line using `ls`.
var superchild = require('superchild');
var child = superchild('ls -lh');
child.on('stdout_line', function(line) {
console.log('[stdout]: ', line);
});
##### Spawn and communicate with a module
Spawn a node.js module in another process and communicate with it.
Note that the child uses the `superchild.unlogger` helper function
to parse its standard input for LD-JSON arrays and objects.
_master.js_
var assert = require('assert');
var superchild = require('superchild');
var child = superchild('node echo.js');
child.send({
some: 'data',
});
child.on('json_object', function(jsonObj) {
assert.equal(jsonObj.some, 'data');
});
_echo.js_
var unlogger = require('superchild').unlogger;
unlogger().on('json_object', function(jsonObj) {
// Echo JSON object from parent back to parent.
console.log(JSON.stringify(jsonObj));
});
## Events emitted
Superchild is an EventEmitter. The following events can be listened for
using `child.on()` and `child.once()` functions.
| Event | Arguments | Description |
| ---------------| -------------------------|---------------------------------------------------------|
| `exit` | `code`, `signal` | Child process exited, identical to `child_process.exit` |
| `stderr_data` | `dataStr` | Received unbuffered data on child's `stderr` stream. |
| `stdout_line` | `lineStr` | Received a full line of text from the child process. |
| `json_object` | `jsonObj` | Parsed a line-delimited JSON object from child's `stdout` stream. |
| `json_array` | `jsonArr` | Parsed a line-delimited JSON array from child's `stdout` stream. |
## Methods
| Method | Description |
| ----------------| -------------------------------------------------------------------------------|
| `send(jsonVal)` | Serialize and send a JSON-serializable object or array to the child as LD-JSON.|
| `close(cb)` | Gracefully terminate the child, invoking the callback when the child has died. |
## Requirements
* `node.js` version 0.11.13 or higher, due to the use of `spawnSync`.
* POSIX-compliant platform, such as Linux or Mac OS.
## Source Code
The full annotated source code of `superchild.js` follows, generated
automatically by Docco.
#### Helper utilities
* [`Class JSONSieve`](http://mayanklahiri.github.io/node-superchild/json-sieve.html),
parses a readable stream into JSON objects and arrays, and stdout lines.
* [`function unlogger()`](http://mayanklahiri.github.io/node-superchild/unlogger.html),
an example of establishing bi-directional communication between a parent
and child that can be easily ported to many other languages.