import { SpawnOptions } from "node:child_process"; export type PlatformProcessKillerDeps = { platform: NodeJS.Platform; spawnProcess: (command: string, args: string[], options: SpawnOptions) => SpawnedKillerProcess; killProcess: (pid: number, signal: NodeJS.Signals) => void; log: (...args: unknown[]) => void; }; type SpawnedKillerProcess = { on: (event: "error", listener: (error: Error) => void) => unknown; }; export function killProcessTree(pid: number, signal: NodeJS.Signals, deps: PlatformProcessKillerDeps): boolean { if (!Number.isSafeInteger(pid) || pid <= 0) { deps.log(`Invalid pid for process tree termination: ${pid}`); return false; } if (deps.platform === "win32") { return killWindowsProcessTree(pid, signal, deps); } return killPosixProcessTree(pid, signal, deps.killProcess); } function killWindowsProcessTree( pid: number, signal: NodeJS.Signals, deps: Pick, ): boolean { const args = ["/pid", String(pid), "/T", ...(signal === "SIGKILL" ? ["/F"] : [])]; const killer = deps.spawnProcess("taskkill", args, { stdio: "ignore", shell: false, windowsHide: true, }); killer.on("error", (error) => { deps.log(`taskkill error for pid=${pid}`, error); }); return true; } function killPosixProcessTree(pid: number, signal: NodeJS.Signals, killProcess: PlatformProcessKillerDeps["killProcess"]): boolean { try { killProcess(-pid, signal); return true; } catch { try { killProcess(pid, signal); return true; } catch { return false; } } }