git call, a model CLI, a test
command. Each is the one thing that can wait forever, and a hang carries no
error. @obversa/process runs one child with a deadline and returns a
result you can read whatever happened: it finished, it hung, it filled a
pipe, or it ignored a signal.
What you get
- A deadline that always means timeout. When the time passes, the child
is stopped and the result says so, even when the stopped child reports no
exit code. By default only the child is signalled, so a process the child
started and left behind is not stopped. Pass
detached: trueto stop the child’s whole process group. - No pipe can stall it. Standard input is closed after the optional input, and both output streams are drained until they close or for a short grace after the child exits, under one combined byte cap. The exit is the result; the pipes are a bounded extra.
- A result, not a race. The exit code, the output bytes and the timed-out and aborted flags come from facts the helper recorded, never from whichever callback fired first.
Run one
The call
runChild(options) takes executable, args, cwd, env, an optional
stdin, timeoutMs, killGraceMs, maxOutputBytes, an optional
AbortSignal, and inheritParentEnv (default true). It returns
exitCode (a number, or null when a signal stopped the child), stdout
and stderr as bytes, timedOut and aborted. It throws RunChildError
with code SPAWN_FAILED when the executable cannot start,
OUTPUT_LIMIT when the child writes past the cap, and
TEARDOWN_INCOMPLETE when the child does not stop within the grace period
after its deadline.
When your process dies
Children the helper started are stopped when your process exits or is interrupted, the way the signal-exit library does it: a terminate signal on exit, and on an interrupt the helper stops its children and re-raises the signal so your process ends as it would have. Your own handler for a signal takes precedence. A child sits in your process group by default so a terminal interrupt reaches it;detached: true gives it its own group.
Things that catch people out
- Bytes written after the grace are not captured; the result holds what arrived before the child exited plus the short drain.
- The output cap is combined across standard output and standard error.
- The environment is merged over the parent’s unless you say otherwise.
- A timed-out child still has output: what it wrote before the deadline is in the result, up to the cap.