@alloc/tree-kill kills a root process and, when a numeric pid is available,
walks the process tree so descendants are signaled before the parent.
This package is a rewrite of the original
tree-kill. It keeps the upstream
platform strategy of using pgrep on macOS, ps on Linux, and taskkill on
Windows, but its public API is intentionally different: treeKill(proc, signal?) is promise-based for normal application code, and
treeKillSync(proc, signal?) is a separate synchronous fallback for exit-time
cleanup.
- You already have a
ChildProcessor equivalent handle and want to terminate the whole tree from application code. - You need a best-effort synchronous shutdown fallback inside a
process.on("exit")handler. - You want one small helper that works across macOS, Linux, and Windows.
- You want the root process signaled even when descendant enumeration finds no children.
- You only have a raw pid and need drop-in compatibility with upstream
tree-kill. - You need a callback-based API or a CLI.
- You need signal-specific behavior on Windows; Windows always uses
taskkill /T /Ffirst.
ProcessLike: any object with akill(signal?)method and an optional numericpid.treeKill(proc, signal?): the promise-based entrypoint for killing the root process and, when possible, its descendants.treeKillSync(proc, signal?): the synchronous entrypoint for shutdown hooks that cannot await asynchronous work.- Numeric
pid: the capability that enables tree traversal. Without it, both APIs degrade to a directkill()call on the provided object.
- If
proc.pidis missing or not numeric, callproc.kill(signal)directly and stop. - On Windows, run
taskkill /pid <pid> /T /F; if that fails, fall back toproc.kill(signal). - On macOS, recurse through child processes with
pgrep -P <pid>. - On Linux, recurse through child processes with
ps -o pid --no-headers --ppid <pid>. - Kill descendants before parents, ignore
ESRCHfor already-exited child processes, then callproc.kill(signal)once more so the root process is still signaled if traversal did not finish it off.
- Kill a spawned child tree with the default signal:
await treeKill(child) - Use a stronger Unix-like signal:
await treeKill(child, "SIGKILL") - Add an exit-hook fallback:
process.on("exit", () => treeKillSync(child, "SIGTERM")) - Support a custom process wrapper without a pid:
await treeKill(proc)ortreeKillSync(proc)and let the wrapper's ownkill()implementation decide what to do
proc.killis required; there is no pid-only overload.- Descendant traversal only happens when
proc.pidis a valid number. - macOS and Linux rely on
pgrepandpsbeing available in the runtime environment. - Windows does not preserve POSIX signal semantics because
taskkill /Fis the primary kill mechanism. treeKill()resolves withvoid; success is defined by the absence of a thrown error, not by the boolean returned fromproc.kill().treeKillSync()returnsvoidand follows the same error model without any asynchronous work.
ESRCHfromprocess.kill()is ignored for descendants that already exited.- Other
process.kill()failures are thrown. - On Windows,
taskkillfailures are swallowed so the fallbackproc.kill()call can still run. - A missing or non-numeric
pidis not an error if the provided object implementskill().
- Root process: the process referenced by
proc. - Descendant: any child, grandchild, or deeper process spawned under the root.
- Process tree: the root plus all descendants that can be discovered from the root pid.
- ProcessLike: the minimal interface this package needs in order to signal the root process.
- Providing a CLI like the original
tree-killpackage. - Preserving upstream's pid-first callback signature.
- Acting as a supervisor, restart loop, or timeout manager.