@nice-code/process
v0.86.0
Published
One child process, fully managed — identical semantics on Windows, Linux, WSL, and macOS.
Readme
@nice-code/process
One child process, managed properly: argv-first spawn, an output pipeline that never blocks the child, readiness you can await, and a stop ladder that ends the whole tree — with the same semantics on Windows, Linux, WSL, and macOS.
Docs: nicecode.io
bun add @nice-code/processimport { createNiceProcess } from "@nice-code/process";
const web = createNiceProcess({
id: "web",
run: ["bun", "run", "dev"],
cwd: "./apps/web",
ready: { kind: "port", port: 5173 },
});
await web.start();
await web.whenReady();
for await (const line of web.lines()) process.stdout.write(`${line.text}\n`);
await web.stop();The parts that are easy to get subtly wrong
- Killing the tree, not the pid.
npm run deviscmd → node → your server; ending the shim leaves the server holding the port. POSIX process groups and Windows Job Objects end the tree, andcapabilityreports honestly when a platform only gives you the degraded tier. - A dead root is not a dead tree. After a voluntary root exit,
stop()still reaps survivors. - Never killing on a pid match. Pids are reused. Every ledger record carries a start-time fingerprint; a mismatch is quarantined and surfaced, never killed.
- Readiness that means something. Port/HTTP/log-marker contracts that latch only after a
stability window, can regress to
unready, and never kill anything on timeout. - Output that cannot blow up. Always drained, per-line byte caps, bounded ring,
\rprogress bars collapsed to replace-ops on one line id, and explicit gap markers for a consumer that falls behind — never silent loss, never unbounded memory. - Monotonic time. Backoff and stop deadlines survive a laptop sleeping and NTP stepping the clock.
Node ≥ 22 or Bun.
