@orkestrel/process 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -17
- package/dist/src/core/index.cjs +58 -4
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +250 -51
- package/dist/src/core/index.d.ts +250 -51
- package/dist/src/core/index.js +53 -5
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +865 -220
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +494 -88
- package/dist/src/server/index.d.ts +494 -88
- package/dist/src/server/index.js +850 -221
- package/dist/src/server/index.js.map +1 -1
- package/package.json +3 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["#emitter","#child","#reader","#grace","#limit","#decoder","#exit","#lines","#buffer","#waiters","#signal","#abort","#iterate","#push","#finish","#close","#retain","#terminate","#tail","#closed","#stopping","#kill","#ending","#teardown","#next","#ended","#emitter","#children","#evict","#stopAll","#stopOne","#stopMany","#ending","#teardown"],"sources":["../../../src/server/helpers.ts","../../../src/server/Process.ts","../../../src/server/ProcessManager.ts","../../../src/server/factories.ts"],"sourcesContent":["import type { ChildProcessWithoutNullStreams } from 'node:child_process'\nimport type { ProcessCommand, RunOptions, RunResult } from '@src/core'\nimport { Buffer } from 'node:buffer'\nimport { spawn, spawnSync } from 'node:child_process'\nimport { extname } from 'node:path'\nimport { createRunError, PROCESS_GRACE, PROCESS_OUTPUT } from '@src/core'\n\n/**\n * Signals one owned child process, or its detached process group on a POSIX host.\n *\n * @remarks\n * On a POSIX host a child spawned `detached` leads its own process group, so signalling the\n * negated pid reaches the whole tree. On Windows, or when no pid is available, the signal goes to\n * the child directly. A throw during signalling is swallowed: the process can exit and win the\n * signal race, and the child's close event remains the authoritative terminal state.\n *\n * @param child - The child process signal boundary\n * @param signal - The signal to deliver\n * @returns Nothing\n *\n * @example\n * ```ts\n * killProcess(child, 'SIGTERM')\n * ```\n */\nexport function killProcess(\n\tchild: { readonly pid?: number | undefined; kill(signal: NodeJS.Signals): boolean },\n\tsignal: NodeJS.Signals,\n): void {\n\ttry {\n\t\tif (process.platform === 'win32' || child.pid === undefined) {\n\t\t\tchild.kill(signal)\n\t\t\treturn\n\t\t}\n\t\tprocess.kill(-child.pid, signal)\n\t} catch {\n\t\t// Exit can win the signal race; the close event remains authoritative.\n\t}\n}\n\n/**\n * Trims a buffer to at most `limit` trailing bytes without splitting a UTF-8 sequence.\n *\n * @remarks\n * Keeps the tail — the diagnostic end of a stderr stream. When the cut point lands inside a\n * multibyte sequence, the start advances past the leading continuation bytes so the retained\n * bytes always begin on a code-point boundary.\n *\n * @param buffer - The accumulated bytes\n * @param limit - The maximum retained byte count\n * @returns The trailing bytes, at most `limit` and never starting mid-sequence\n *\n * @example\n * ```ts\n * trimTail(Buffer.from('hello'), 3) // <Buffer 6c 6c 6f>\n * ```\n */\nexport function trimTail(buffer: Buffer, limit: number): Buffer {\n\tif (buffer.byteLength <= limit) return buffer\n\tlet start = buffer.byteLength - limit\n\twhile (start < buffer.byteLength) {\n\t\tconst byte = buffer[start]\n\t\tif (byte === undefined || (byte & 0xc0) !== 0x80) break\n\t\tstart += 1\n\t}\n\treturn Buffer.from(buffer.subarray(start))\n}\n\n/**\n * Trims a buffer to at most `limit` leading bytes without splitting a UTF-8 sequence.\n *\n * @remarks\n * Keeps the head — the captured start of a one-shot run's output. When the cut point lands inside\n * a multibyte sequence, the end retreats to the sequence's start so the retained bytes always end\n * on a code-point boundary and decode without a replacement character.\n *\n * @param buffer - The accumulated bytes\n * @param limit - The maximum retained byte count\n * @returns The leading bytes, at most `limit` and never ending mid-sequence\n *\n * @example\n * ```ts\n * trimHead(Buffer.from('hello'), 3) // <Buffer 68 65 6c>\n * ```\n */\nexport function trimHead(buffer: Buffer, limit: number): Buffer {\n\tif (buffer.byteLength <= limit) return buffer\n\tlet end = limit\n\twhile (end > 0) {\n\t\tconst byte = buffer[end]\n\t\tif (byte === undefined || (byte & 0xc0) !== 0x80) break\n\t\tend -= 1\n\t}\n\treturn Buffer.from(buffer.subarray(0, end))\n}\n\n/**\n * Checks whether a command file must be spawned through a shell on the current host.\n *\n * @remarks\n * Node v22+ refuses to spawn a Windows `.cmd` or `.bat` batch file directly and returns `EINVAL`,\n * and a bare command name such as `git` resolves through `PATHEXT` to a batch shim rather than an\n * executable. Both cases run through the shell. A POSIX host, and a Windows path carrying a real\n * executable extension such as `.exe`, spawn directly. When the result is `true`, treat the\n * command file and arguments as trusted: shell invocation exposes shell metacharacters.\n *\n * @param file - The command executable name or path\n * @returns True when the file must run through a shell; false when it spawns directly\n *\n * @example\n * ```ts\n * requiresShell('git') // true on Windows, false on POSIX\n * ```\n */\nexport function requiresShell(file: string): boolean {\n\tif (process.platform !== 'win32') return false\n\tconst lower = file.toLowerCase()\n\treturn lower.endsWith('.cmd') || lower.endsWith('.bat') || extname(file) === ''\n}\n\n/**\n * Renders one command into its diagnostic command line.\n *\n * @param command - The executable and its argument vector\n * @returns The space-joined command line, for a {@link RunResult} and error messages\n *\n * @example\n * ```ts\n * commandLine({ file: 'git', arguments: ['status'] }) // 'git status'\n * ```\n */\nexport function commandLine(command: ProcessCommand): string {\n\treturn [command.file, ...command.arguments].join(' ')\n}\n\n/**\n * Merges environment overrides over the current process environment.\n *\n * @remarks\n * Later maps override earlier ones and an `undefined` value unsets a key. The base is the parent\n * process environment, so a child inherits it unless a key is overridden or unset.\n *\n * @param base - The command's own environment overrides\n * @param override - Per-invocation overrides applied last\n * @returns The merged environment for a spawn\n *\n * @example\n * ```ts\n * mergeEnvironment({ TOKEN: 'a' }, { TOKEN: undefined }) // TOKEN unset\n * ```\n */\nexport function mergeEnvironment(\n\tbase?: Readonly<Record<string, string | undefined>>,\n\toverride?: Readonly<Record<string, string | undefined>>,\n): NodeJS.ProcessEnv {\n\treturn { ...process.env, ...base, ...override }\n}\n\n/**\n * Builds one settled {@link RunResult} from a completed one-shot run's captured bytes and exit.\n *\n * @remarks\n * `failed` is derived: a run failed when it timed out, was ended by a signal, or exited with a\n * code other than `0` — a `null` code from a spawn fault is therefore a failure. Both output\n * buffers are byte-bounded by `limit` before decoding.\n *\n * @param command - The diagnostic command line\n * @param stdout - The captured standard output bytes\n * @param stderr - The captured standard error bytes\n * @param code - The exit code, or `null` when a signal or spawn fault ended the run\n * @param signal - The terminating signal, or `null`\n * @param timedOut - If `true`, the run's own timeout elapsed; if `false`, it did not\n * @param limit - The maximum captured bytes for each stream\n * @returns The frozen run outcome\n *\n * @example\n * ```ts\n * buildRunResult('git status', Buffer.from('ok'), Buffer.alloc(0), 0, null, false, 1024)\n * ```\n */\nexport function buildRunResult(\n\tcommand: string,\n\tstdout: Buffer,\n\tstderr: Buffer,\n\tcode: number | null,\n\tsignal: string | null,\n\ttimedOut: boolean,\n\tlimit: number,\n): RunResult {\n\treturn Object.freeze({\n\t\tcommand,\n\t\tstdout: trimHead(stdout, limit).toString('utf8'),\n\t\tstderr: trimHead(stderr, limit).toString('utf8'),\n\t\tcode,\n\t\tsignal,\n\t\tfailed: timedOut || signal !== null || code !== 0,\n\t\ttimedOut,\n\t})\n}\n\n/**\n * Runs one command to completion, buffering its output, and settles with the outcome.\n *\n * @remarks\n * The child is spawned through the cross-platform command resolver ({@link requiresShell}) and, on\n * a POSIX host, detached so its whole process group can be terminated. Standard output and error\n * are each byte-bounded by `limit` (default {@link PROCESS_OUTPUT}). A positive `timeout` arms a\n * timer that terminates the child through `SIGTERM`, then `SIGKILL` after `grace`\n * (default {@link PROCESS_GRACE}); aborting `signal` terminates the same way. The child's\n * `environment` merges over the parent, then `options.environment` on top, and `options.input`\n * overrides `command.input`. Unless `reject` is `false`, a failed run rejects with a\n * {@link createRunError} carrying the {@link RunResult}.\n *\n * @param command - The executable, arguments, and optional environment and input\n * @param options - Working directory, timeout, grace, signal, capture limit, and reject behavior\n * @returns The settled run outcome\n * @throws A {@link ProcessError} carrying the {@link RunResult} when the run failed and `reject` is not `false`\n *\n * @example\n * ```ts\n * const result = await run({ file: 'git', arguments: ['status'] }, { workspace: process.cwd() })\n * ```\n */\nexport async function run(command: ProcessCommand, options?: RunOptions): Promise<RunResult> {\n\tconst limit = options?.limit ?? PROCESS_OUTPUT\n\tconst grace = options?.grace ?? PROCESS_GRACE\n\tconst timeout = options?.timeout ?? 0\n\tconst reject = options?.reject ?? true\n\tconst input = options?.input ?? command.input\n\tconst line = commandLine(command)\n\tconst settled = Promise.withResolvers<RunResult>()\n\tconst terminate = new AbortController()\n\tconst cleanup = new AbortController()\n\tlet stdout: Buffer = Buffer.alloc(0)\n\tlet stderr: Buffer = Buffer.alloc(0)\n\tlet timedOut = false\n\tlet killTimer: ReturnType<typeof setTimeout> | undefined\n\tlet timeoutTimer: ReturnType<typeof setTimeout> | undefined\n\tlet done = false\n\n\tconst child: ChildProcessWithoutNullStreams = spawn(command.file, [...command.arguments], {\n\t\tcwd: options?.workspace ?? process.cwd(),\n\t\tdetached: process.platform !== 'win32',\n\t\tenv: mergeEnvironment(command.environment, options?.environment),\n\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\tshell: requiresShell(command.file),\n\t\twindowsHide: true,\n\t})\n\n\tterminate.signal.addEventListener(\n\t\t'abort',\n\t\t() => {\n\t\t\tkillProcess(child, 'SIGTERM')\n\t\t\tkillTimer = setTimeout(() => killProcess(child, 'SIGKILL'), grace)\n\t\t},\n\t\t{ once: true },\n\t)\n\n\tchild.stdin.on('error', () => undefined)\n\tchild.stdout.on('data', (chunk: unknown) => {\n\t\tif (Buffer.isBuffer(chunk)) stdout = trimHead(Buffer.concat([stdout, chunk]), limit)\n\t})\n\tchild.stderr.on('data', (chunk: unknown) => {\n\t\tif (Buffer.isBuffer(chunk)) stderr = trimHead(Buffer.concat([stderr, chunk]), limit)\n\t})\n\tchild.once('error', () => {\n\t\tif (done) return\n\t\tdone = true\n\t\tcleanup.abort()\n\t\tclearTimeout(killTimer)\n\t\tclearTimeout(timeoutTimer)\n\t\tsettled.resolve(buildRunResult(line, stdout, stderr, null, null, false, limit))\n\t})\n\tchild.once('close', (code: number | null, signal: NodeJS.Signals | null) => {\n\t\tif (done) return\n\t\tdone = true\n\t\tcleanup.abort()\n\t\tclearTimeout(killTimer)\n\t\tclearTimeout(timeoutTimer)\n\t\tsettled.resolve(buildRunResult(line, stdout, stderr, code, signal, timedOut, limit))\n\t})\n\n\tif (input !== undefined) child.stdin.write(input)\n\tchild.stdin.end()\n\tif (timeout > 0) {\n\t\ttimeoutTimer = setTimeout(() => {\n\t\t\ttimedOut = true\n\t\t\tterminate.abort()\n\t\t}, timeout)\n\t}\n\tif (options?.signal !== undefined) {\n\t\tconst signal = options.signal\n\t\tsignal.addEventListener('abort', () => terminate.abort(), {\n\t\t\tonce: true,\n\t\t\tsignal: cleanup.signal,\n\t\t})\n\t\tif (signal.aborted) terminate.abort()\n\t}\n\n\tconst result = await settled.promise\n\tif (result.failed && reject) throw createRunError(result)\n\treturn result\n}\n\n/**\n * Runs one command to completion synchronously, buffering its output, and returns the outcome.\n *\n * @remarks\n * The synchronous counterpart of {@link run}. The child is spawned through the same cross-platform\n * command resolver ({@link requiresShell}). Standard output and error are each byte-bounded by\n * `limit` (default {@link PROCESS_OUTPUT}). A positive `timeout` is enforced by the host, which\n * ends the child with `SIGKILL` when it elapses. The environment and input follow the same merge\n * as {@link run}. Unless `reject` is `false`, a failed run throws a {@link createRunError} carrying\n * the {@link RunResult}.\n *\n * @param command - The executable, arguments, and optional environment and input\n * @param options - Working directory, timeout, signal, capture limit, and reject behavior\n * @returns The run outcome\n * @throws A {@link ProcessError} carrying the {@link RunResult} when the run failed and `reject` is not `false`\n *\n * @example\n * ```ts\n * const result = runSync({ file: 'git', arguments: ['--version'] }, { reject: false })\n * ```\n */\nexport function runSync(command: ProcessCommand, options?: RunOptions): RunResult {\n\tconst limit = options?.limit ?? PROCESS_OUTPUT\n\tconst timeout = options?.timeout ?? 0\n\tconst reject = options?.reject ?? true\n\tconst input = options?.input ?? command.input\n\tconst line = commandLine(command)\n\tconst outcome = spawnSync(command.file, [...command.arguments], {\n\t\tcwd: options?.workspace ?? process.cwd(),\n\t\tenv: mergeEnvironment(command.environment, options?.environment),\n\t\tencoding: 'buffer',\n\t\tmaxBuffer: limit,\n\t\tshell: requiresShell(command.file),\n\t\twindowsHide: true,\n\t\t...(input !== undefined ? { input } : {}),\n\t\t...(options?.signal !== undefined ? { signal: options.signal } : {}),\n\t\t...(timeout > 0 ? { timeout, killSignal: 'SIGKILL' } : {}),\n\t})\n\tconst error = outcome.error\n\tconst timedOut = error !== undefined && 'code' in error && error.code === 'ETIMEDOUT'\n\tconst stdout = Buffer.isBuffer(outcome.stdout) ? outcome.stdout : Buffer.alloc(0)\n\tconst stderr = Buffer.isBuffer(outcome.stderr) ? outcome.stderr : Buffer.alloc(0)\n\tconst result = buildRunResult(\n\t\tline,\n\t\tstdout,\n\t\tstderr,\n\t\toutcome.status,\n\t\toutcome.signal,\n\t\ttimedOut,\n\t\tlimit,\n\t)\n\tif (result.failed && reject) throw createRunError(result)\n\treturn result\n}\n","import type { ChildProcessWithoutNullStreams } from 'node:child_process'\nimport type { Interface as ReadLineInterface } from 'node:readline'\nimport type { EmitterInterface } from '@orkestrel/emitter'\nimport type { ProcessEventMap, ProcessExit, ProcessInterface, ProcessOptions } from '@src/core'\nimport { Buffer } from 'node:buffer'\nimport { spawn } from 'node:child_process'\nimport { createInterface } from 'node:readline'\nimport { StringDecoder } from 'node:string_decoder'\nimport { Emitter } from '@orkestrel/emitter'\nimport { PROCESS_EVIDENCE } from '@src/core'\nimport { killProcess, mergeEnvironment, trimTail } from './helpers.js'\n\n/**\n * One supervised child process with eagerly framed output and bounded termination.\n *\n * @remarks\n * Standard output is drained eagerly through `readline`, so `exit` resolves and a late consumer of\n * `lines` still receives every framed line, including a final line written without a trailing\n * newline. Standard error is decoded and forwarded live as the `stderr` event while a byte-bounded\n * raw tail is retained as `evidence`. The typed `emitter` also carries the terminal `exit`,\n * alongside the `exit` promise. `stop` terminates the child through `SIGTERM`, then `SIGKILL` after\n * `grace`, and is idempotent; `destroy` stops the child and destroys the emitter last.\n *\n * @example\n * ```ts\n * import { Process } from '@orkestrel/process/server'\n *\n * const child = new Process({\n * \tcommand: { file: 'node', arguments: ['--version'] },\n * \tworkspace: process.cwd(),\n * \tgrace: 5000,\n * })\n * const exit = await child.exit\n * await child.destroy()\n * ```\n */\nexport class Process implements ProcessInterface {\n\treadonly #emitter: Emitter<ProcessEventMap>\n\treadonly #child: ChildProcessWithoutNullStreams\n\treadonly #reader: ReadLineInterface\n\treadonly #grace: number\n\treadonly #limit: number\n\treadonly #decoder = new StringDecoder('utf8')\n\treadonly #exit = Promise.withResolvers<ProcessExit>()\n\treadonly #lines: AsyncIterable<string>\n\treadonly #buffer: string[] = []\n\treadonly #waiters: Array<PromiseWithResolvers<IteratorResult<string, void>>> = []\n\treadonly #signal: AbortSignal | undefined\n\treadonly #abort: EventListener | undefined\n\t#tail: Buffer = Buffer.alloc(0)\n\t#closed = false\n\t#ended = false\n\t#stopping: Promise<void> | undefined\n\t#ending: Promise<void> | undefined\n\n\t/**\n\t * Spawn one child process and begin eager stream capture.\n\t *\n\t * @param options - Command, workspace, termination, evidence, stdin, and observation settings\n\t */\n\tconstructor(options: ProcessOptions) {\n\t\tconst on = options.on\n\t\tconst error = options.error\n\t\tthis.#emitter = new Emitter<ProcessEventMap>({\n\t\t\t...(on === undefined ? {} : { on }),\n\t\t\t...(error === undefined ? {} : { error }),\n\t\t})\n\t\tthis.#grace = options.grace\n\t\tthis.#limit = options.evidence ?? PROCESS_EVIDENCE\n\t\tthis.#signal = options.signal\n\t\tthis.#lines = Object.freeze({ [Symbol.asyncIterator]: this.#iterate.bind(this) })\n\t\tthis.#child = spawn(options.command.file, [...options.command.arguments], {\n\t\t\tcwd: options.workspace,\n\t\t\tdetached: process.platform !== 'win32',\n\t\t\tenv: mergeEnvironment(options.command.environment),\n\t\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\t\twindowsHide: true,\n\t\t})\n\t\tthis.#reader = createInterface({ input: this.#child.stdout, crlfDelay: Infinity })\n\t\tthis.#reader.on('line', this.#push.bind(this))\n\t\tthis.#reader.once('close', this.#finish.bind(this))\n\t\tthis.#child.once('error', () => undefined)\n\t\tthis.#child.once('close', this.#close.bind(this))\n\t\tthis.#child.stdin.on('error', () => undefined)\n\t\tthis.#child.stderr.on('data', this.#retain.bind(this))\n\t\tif (options.command.input !== undefined) this.#child.stdin.write(options.command.input)\n\t\tif (options.writable !== true) this.#child.stdin.end()\n\t\tif (this.#signal !== undefined) {\n\t\t\tthis.#abort = this.#terminate.bind(this)\n\t\t\tthis.#signal.addEventListener('abort', this.#abort, { once: true })\n\t\t\tif (this.#signal.aborted) void this.stop()\n\t\t}\n\t}\n\n\t/** The typed lifecycle observation surface. */\n\tget emitter(): EmitterInterface<ProcessEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\t/** The eagerly captured stdout lines, in arrival order, ending when the child's stdout closes. */\n\tget lines(): AsyncIterable<string> {\n\t\treturn this.#lines\n\t}\n\n\t/** The decoded byte-bounded stderr tail. */\n\tget evidence(): string {\n\t\treturn this.#tail.toString('utf8')\n\t}\n\n\t/** The terminal child state, observed once from the close event. */\n\tget exit(): Promise<ProcessExit> {\n\t\treturn this.#exit.promise\n\t}\n\n\t/**\n\t * Write one line to the open standard-input channel.\n\t *\n\t * @param text - The line text without its trailing newline\n\t * @returns True when the channel accepted the line without backpressure; false otherwise\n\t */\n\tsend(text: string): boolean {\n\t\tif (this.#closed || this.#child.stdin.destroyed || this.#child.stdin.writableEnded) return false\n\t\ttry {\n\t\t\treturn this.#child.stdin.write(`${text}\\n`)\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Terminate the child process tree and await its observed exit.\n\t *\n\t * @returns A promise that resolves after bounded termination\n\t */\n\tstop(): Promise<void> {\n\t\tif (this.#stopping !== undefined) return this.#stopping\n\t\tthis.#stopping = this.#kill()\n\t\treturn this.#stopping\n\t}\n\n\t/**\n\t * Stop the child and destroy the observation emitter.\n\t *\n\t * @returns The stable barrier shared by every call\n\t */\n\tdestroy(): Promise<void> {\n\t\tif (this.#ending !== undefined) return this.#ending\n\t\tthis.#ending = this.#teardown()\n\t\treturn this.#ending\n\t}\n\n\t#iterate(): AsyncIterator<string, void, void> {\n\t\treturn Object.freeze({ next: this.#next.bind(this) })\n\t}\n\n\t#next(): Promise<IteratorResult<string, void>> {\n\t\tconst line = this.#buffer.shift()\n\t\tif (line !== undefined) return Promise.resolve(Object.freeze({ done: false, value: line }))\n\t\tif (this.#ended) return Promise.resolve(Object.freeze({ done: true, value: undefined }))\n\t\tconst waiter = Promise.withResolvers<IteratorResult<string, void>>()\n\t\tthis.#waiters.push(waiter)\n\t\treturn waiter.promise\n\t}\n\n\t#push(line: string): void {\n\t\tif (this.#ended) return\n\t\tconst waiter = this.#waiters.shift()\n\t\tif (waiter === undefined) {\n\t\t\tthis.#buffer.push(line)\n\t\t\treturn\n\t\t}\n\t\twaiter.resolve(Object.freeze({ done: false, value: line }))\n\t}\n\n\t#finish(): void {\n\t\tif (this.#ended) return\n\t\tthis.#ended = true\n\t\tfor (const waiter of this.#waiters) {\n\t\t\twaiter.resolve(Object.freeze({ done: true, value: undefined }))\n\t\t}\n\t\tthis.#waiters.length = 0\n\t}\n\n\t#retain(chunk: unknown): void {\n\t\tif (!Buffer.isBuffer(chunk) || this.#closed) return\n\t\tthis.#tail = trimTail(Buffer.concat([this.#tail, chunk]), this.#limit)\n\t\tconst text = this.#decoder.write(chunk)\n\t\tif (text.length > 0) this.#emitter.emit('stderr', text)\n\t}\n\n\t#close(code: number | null, signal: NodeJS.Signals | null): void {\n\t\tif (this.#closed) return\n\t\tconst suffix = this.#decoder.end()\n\t\tif (suffix.length > 0) this.#emitter.emit('stderr', suffix)\n\t\tthis.#closed = true\n\t\tif (this.#signal !== undefined && this.#abort !== undefined) {\n\t\t\tthis.#signal.removeEventListener('abort', this.#abort)\n\t\t}\n\t\tthis.#reader.close()\n\t\tconst exit = Object.freeze({ code, signal })\n\t\tthis.#exit.resolve(exit)\n\t\tthis.#emitter.emit('exit', exit)\n\t}\n\n\t#terminate(): void {\n\t\tvoid this.stop()\n\t}\n\n\tasync #kill(): Promise<void> {\n\t\tif (this.#closed) return\n\t\tkillProcess(this.#child, 'SIGTERM')\n\t\tconst grace = Promise.withResolvers<void>()\n\t\tconst timer = setTimeout(grace.resolve, this.#grace)\n\t\tawait Promise.race([this.#exit.promise, grace.promise])\n\t\tclearTimeout(timer)\n\t\tif (this.#closed) return\n\t\tkillProcess(this.#child, 'SIGKILL')\n\t\tawait this.#exit.promise\n\t}\n\n\tasync #teardown(): Promise<void> {\n\t\tawait this.stop()\n\t\tthis.#emitter.destroy()\n\t}\n}\n","import type { EmitterInterface } from '@orkestrel/emitter'\nimport type {\n\tProcessExit,\n\tProcessInterface,\n\tProcessManagerEventMap,\n\tProcessManagerInterface,\n\tProcessManagerOptions,\n\tProcessOptions,\n} from '@src/core'\nimport { Emitter } from '@orkestrel/emitter'\nimport { createDuplicateError } from '@src/core'\nimport { Process } from './Process.js'\n\n/**\n * A keyed registry of live supervised child processes.\n *\n * @remarks\n * A child launched under an id joins the registry and emits `launch`; when it settles it removes\n * itself and emits `exit`, so `count` and `processes` reflect only live children. `launch` throws\n * a {@link createDuplicateError} when the id is already live — a spawn fault surfaces through the\n * returned child's `exit`, never from `launch`. `destroy` stops every child, then destroys the\n * registry emitter last.\n *\n * @example\n * ```ts\n * import { ProcessManager } from '@orkestrel/process/server'\n *\n * const manager = new ProcessManager()\n * const child = manager.launch('build', {\n * \tcommand: { file: 'node', arguments: ['build.js'] },\n * \tworkspace: process.cwd(),\n * \tgrace: 5000,\n * })\n * await child.exit\n * await manager.destroy()\n * ```\n */\nexport class ProcessManager implements ProcessManagerInterface {\n\treadonly #emitter: Emitter<ProcessManagerEventMap>\n\treadonly #children = new Map<string, ProcessInterface>()\n\t#ending: Promise<void> | undefined\n\n\t/**\n\t * Construct a process registry.\n\t *\n\t * @param options - Initial fleet-level observation hooks and listener-error handling\n\t */\n\tconstructor(options?: ProcessManagerOptions) {\n\t\tconst on = options?.on\n\t\tconst error = options?.error\n\t\tthis.#emitter = new Emitter<ProcessManagerEventMap>({\n\t\t\t...(on === undefined ? {} : { on }),\n\t\t\t...(error === undefined ? {} : { error }),\n\t\t})\n\t}\n\n\t/** The typed fleet-level observation surface. */\n\tget emitter(): EmitterInterface<ProcessManagerEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\t/** The number of live children. */\n\tget count(): number {\n\t\treturn this.#children.size\n\t}\n\n\t/**\n\t * The live child under `id`, or `undefined` when none is.\n\t *\n\t * @param id - The registry key\n\t * @returns The child, or `undefined`\n\t */\n\tprocess(id: string): ProcessInterface | undefined {\n\t\treturn this.#children.get(id)\n\t}\n\n\t/**\n\t * A snapshot of every live child.\n\t *\n\t * @returns The live children in launch order\n\t */\n\tprocesses(): readonly ProcessInterface[] {\n\t\treturn [...this.#children.values()]\n\t}\n\n\t/**\n\t * Spawn and register one child under `id`.\n\t *\n\t * @param id - The registry key, unique among live children\n\t * @param options - The child construction options\n\t * @returns The launched child\n\t * @throws A {@link ProcessError} coded `duplicate` when `id` is already live\n\t */\n\tlaunch(id: string, options: ProcessOptions): ProcessInterface {\n\t\tif (this.#children.has(id)) throw createDuplicateError(id)\n\t\tconst child = new Process(options)\n\t\tthis.#children.set(id, child)\n\t\tchild.emitter.on('exit', (exit) => this.#evict(id, child, exit))\n\t\tthis.#emitter.emit('launch', id)\n\t\treturn child\n\t}\n\n\t/**\n\t * Terminate the named children and await their exit.\n\t *\n\t * @param ids - The registry keys to stop\n\t * @returns True when every named child stopped, false when any id was not live\n\t */\n\tstop(ids: readonly string[]): Promise<boolean>\n\t/**\n\t * Terminate one child and await its exit.\n\t *\n\t * @param id - The registry key to stop\n\t * @returns True when the child was live and stopped, false when the id was not live\n\t */\n\tstop(id: string): Promise<boolean>\n\t/**\n\t * Terminate every live child and await their exit.\n\t *\n\t * @returns A promise that resolves after all children stop\n\t */\n\tstop(): Promise<void>\n\tstop(target?: string | readonly string[]): Promise<boolean | void> {\n\t\tif (target === undefined) return this.#stopAll()\n\t\tif (typeof target === 'string') return this.#stopOne(target)\n\t\treturn this.#stopMany(target)\n\t}\n\n\t/**\n\t * Stop every child, then destroy the registry emitter last.\n\t *\n\t * @returns The stable barrier shared by every call\n\t */\n\tdestroy(): Promise<void> {\n\t\tif (this.#ending !== undefined) return this.#ending\n\t\tthis.#ending = this.#teardown()\n\t\treturn this.#ending\n\t}\n\n\t#evict(id: string, child: ProcessInterface, exit: ProcessExit): void {\n\t\tif (this.#children.get(id) !== child) return\n\t\tthis.#children.delete(id)\n\t\tthis.#emitter.emit('exit', id, exit)\n\t}\n\n\tasync #stopOne(id: string): Promise<boolean> {\n\t\tconst child = this.#children.get(id)\n\t\tif (child === undefined) return false\n\t\tawait child.stop()\n\t\treturn true\n\t}\n\n\tasync #stopMany(ids: readonly string[]): Promise<boolean> {\n\t\tconst stopped = await Promise.all(ids.map((id) => this.#stopOne(id)))\n\t\treturn stopped.every((ok) => ok)\n\t}\n\n\tasync #stopAll(): Promise<void> {\n\t\tawait Promise.all([...this.#children.values()].map((child) => child.stop()))\n\t}\n\n\tasync #teardown(): Promise<void> {\n\t\tawait Promise.all([...this.#children.values()].map((child) => child.destroy()))\n\t\tthis.#emitter.destroy()\n\t}\n}\n","import type {\n\tProcessInterface,\n\tProcessManagerInterface,\n\tProcessManagerOptions,\n\tProcessOptions,\n} from '@src/core'\nimport { Process } from './Process.js'\nimport { ProcessManager } from './ProcessManager.js'\n\n/**\n * Creates one supervised child process.\n *\n * @param options - Command, workspace, termination, evidence, stdin, and observation settings\n * @returns The launched {@link ProcessInterface}\n *\n * @example\n * ```ts\n * import { createProcess } from '@orkestrel/process/server'\n *\n * const child = createProcess({\n * \tcommand: { file: 'node', arguments: ['--version'] },\n * \tworkspace: process.cwd(),\n * \tgrace: 5000,\n * })\n * ```\n */\nexport function createProcess(options: ProcessOptions): ProcessInterface {\n\treturn new Process(options)\n}\n\n/**\n * Creates one keyed registry of supervised child processes.\n *\n * @param options - Initial fleet-level observation hooks and listener-error handling\n * @returns The {@link ProcessManagerInterface} registry\n *\n * @example\n * ```ts\n * import { createProcessManager } from '@orkestrel/process/server'\n *\n * const manager = createProcessManager()\n * ```\n */\nexport function createProcessManager(options?: ProcessManagerOptions): ProcessManagerInterface {\n\treturn new ProcessManager(options)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["#emitter","#child","#reader","#grace","#evidence","#backlog","#decoder","#exit","#lines","#queue","#waiters","#signal","#abort","#iterate","#push","#finish","#close","#retain","#terminate","#tail","#closed","#stopping","#kill","#ending","#teardown","#requested","#next","#take","#ended","#head","#pending","#relieve","#restrain","#paused","#terminating","#emitter","#children","#ids","#destroying","#construct","#evict","#stopAll","#stopOne","#stopMany","#ending","#teardown"],"sources":["../../../src/server/helpers.ts","../../../src/server/Process.ts","../../../src/server/ProcessManager.ts","../../../src/server/factories.ts"],"sourcesContent":["import type { ChildProcessWithoutNullStreams } from 'node:child_process'\nimport type {\n\tDetachOptions,\n\tExecutableOptions,\n\tProcessCommand,\n\tRunInput,\n\tRunOptions,\n\tRunResult,\n\tRunSyncOptions,\n\tSpawnInput,\n} from '@src/core'\nimport type { ProcessChild } from './types.js'\nimport { Buffer } from 'node:buffer'\nimport { spawn, spawnSync } from 'node:child_process'\nimport { statSync } from 'node:fs'\nimport { delimiter, extname, join, resolve } from 'node:path'\nimport {\n\tboundsOf,\n\tholds,\n\tisNonEmptyString,\n\tisNonNegativeInteger,\n\tisString,\n} from '@orkestrel/contract'\nimport {\n\tcreateInvalidError,\n\tcreateRunError,\n\tPROCESS_CONFIRMATION,\n\tPROCESS_GRACE,\n\tPROCESS_OUTPUT,\n\tPROCESS_PATHEXT,\n\tPROCESS_TIMER,\n} from '@src/core'\n\n/**\n * Trims a buffer to at most `limit` trailing bytes without splitting a UTF-8 sequence.\n *\n * @remarks\n * Keeps the tail — the diagnostic end of a stderr stream. When the cut point lands inside a\n * multibyte sequence, the start advances past the leading continuation bytes so the retained\n * bytes always begin on a code-point boundary.\n *\n * @param bytes - The accumulated bytes\n * @param limit - The maximum retained byte count\n * @returns The trailing bytes, at most `limit` and never starting mid-sequence\n *\n * @example\n * ```ts\n * trimTail(Buffer.from('hello'), 3) // <Buffer 6c 6c 6f>\n * ```\n */\nexport function trimTail(bytes: Uint8Array, limit: number): Buffer {\n\tconst buffer = Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes)\n\tif (buffer.byteLength <= limit) return buffer\n\tlet start = buffer.byteLength - limit\n\twhile (start < buffer.byteLength) {\n\t\tconst byte = buffer[start]\n\t\tif (byte === undefined || (byte & 0xc0) !== 0x80) break\n\t\tstart += 1\n\t}\n\treturn Buffer.from(buffer.subarray(start))\n}\n\n/**\n * Trims a buffer to at most `limit` leading bytes without splitting a UTF-8 sequence.\n *\n * @remarks\n * Keeps the head — the captured start of a one-shot run's output. When the cut point lands inside\n * a multibyte sequence, the end retreats to the sequence's start so the retained bytes always end\n * on a code-point boundary and decode without a replacement character.\n *\n * @param bytes - The accumulated bytes\n * @param limit - The maximum retained byte count\n * @returns The leading bytes, at most `limit` and never ending mid-sequence\n *\n * @example\n * ```ts\n * trimHead(Buffer.from('hello'), 3) // <Buffer 68 65 6c>\n * ```\n */\nexport function trimHead(bytes: Uint8Array, limit: number): Buffer {\n\tconst buffer = Buffer.isBuffer(bytes) ? bytes : Buffer.from(bytes)\n\tif (buffer.byteLength <= limit) return buffer\n\tlet end = limit\n\twhile (end > 0) {\n\t\tconst byte = buffer[end]\n\t\tif (byte === undefined || (byte & 0xc0) !== 0x80) break\n\t\tend -= 1\n\t}\n\treturn Buffer.from(buffer.subarray(0, end))\n}\n\n/**\n * Renders one command into its diagnostic command line.\n *\n * @param command - The executable and its argument vector\n * @returns The space-joined command line, for a {@link RunResult} and error messages\n *\n * @example\n * ```ts\n * formatCommand({ file: 'git', arguments: ['status'] }) // 'git status'\n * ```\n */\nexport function formatCommand(command: ProcessCommand): string {\n\treturn [command.file, ...command.arguments].join(' ')\n}\n\n/**\n * Reads one environment variable the way the host resolves it.\n *\n * @remarks\n * Windows environment keys are case-insensitive, so a merged record can hold `Path` where the\n * caller asks for `PATH`. The exact key is tried first and a folded scan runs only on Windows.\n *\n * @param environment - The environment record to read\n * @param name - The variable name\n * @returns The value, or `undefined` when the environment declares no such variable\n *\n * @example\n * ```ts\n * readVariable({ Path: 'C:\\\\Windows' }, 'PATH') // 'C:\\\\Windows' on Windows\n * ```\n */\nexport function readVariable(\n\tenvironment: Readonly<Record<string, string | undefined>>,\n\tname: string,\n): string | undefined {\n\tconst direct = environment[name]\n\tif (direct !== undefined) return direct\n\tif (process.platform !== 'win32') return undefined\n\tconst target = name.toUpperCase()\n\tfor (const [key, value] of Object.entries(environment)) {\n\t\tif (key.toUpperCase() === target) return value\n\t}\n\treturn undefined\n}\n\n/**\n * Merges environment overrides into the environment one child receives.\n *\n * @remarks\n * Later maps override earlier ones and an `undefined` value unsets a key. On Windows the keys fold\n * case-insensitively and the last writer wins, so `PATH` followed by `Path` yields one variable\n * rather than two the host would resolve unpredictably.\n *\n * @param isolated - If `true`, the parent environment is excluded; if `false`, the overrides layer over it\n * @param base - The command's own environment overrides\n * @param override - Per-invocation overrides applied last\n * @returns The environment for a spawn, carrying no unset key\n *\n * @example\n * ```ts\n * mergeEnvironment(false, { TOKEN: 'a' }, { TOKEN: undefined }) // TOKEN unset\n * ```\n */\nexport function mergeEnvironment(\n\tisolated: boolean,\n\tbase?: Readonly<Record<string, string | undefined>>,\n\toverride?: Readonly<Record<string, string | undefined>>,\n): NodeJS.ProcessEnv {\n\tconst folded = process.platform === 'win32'\n\tconst layers: ReadonlyArray<Readonly<Record<string, string | undefined>>> = [\n\t\tisolated ? {} : process.env,\n\t\tbase ?? {},\n\t\toverride ?? {},\n\t]\n\tconst entries = new Map<string, readonly [key: string, value: string]>()\n\tfor (const layer of layers) {\n\t\tfor (const [key, value] of Object.entries(layer)) {\n\t\t\tconst token = folded ? key.toUpperCase() : key\n\t\t\tif (value === undefined) {\n\t\t\t\tentries.delete(token)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tentries.set(token, [key, value])\n\t\t}\n\t}\n\tconst merged: NodeJS.ProcessEnv = {}\n\tfor (const [key, value] of entries.values()) merged[key] = value\n\treturn merged\n}\n\n/**\n * Checks whether a path names a regular file.\n *\n * @remarks\n * Existence alone does not make a lookup hit: a directory named `git` on `PATH` is not the `git`\n * command. An unreadable or malformed path reports `false` rather than throwing.\n *\n * @param target - The absolute or relative path to inspect\n * @returns True when the path resolves to a regular file; false otherwise\n *\n * @example\n * ```ts\n * isFile(process.execPath) // true\n * ```\n */\nexport function isFile(target: string): boolean {\n\treturn holds(() => statSync(target, { throwIfNoEntry: false })?.isFile() === true)\n}\n\n/**\n * Resolves a command file to the executable path the host would launch.\n *\n * @remarks\n * Windows alone needs this: the host searches the working directory before `PATH` and applies\n * `PATHEXT`, and Node reproduces neither for a direct spawn. Within each searched directory the\n * literal name is tried first and each `PATHEXT` candidate after it, whether or not the name already\n * carries an extension — so `report.txt` resolves to a `report.txt` file where one exists and to\n * `report.txt.cmd` where none does. The lookup reads the child's effective environment, so an\n * overridden `PATH` selects the executable the child would have found. A POSIX host resolves the\n * file itself, so the answer there is always `undefined`. An appended extension is spelled the way\n * `PATHEXT` spells it rather than the way the directory entry does, which the case-insensitive host\n * treats as the same file.\n *\n * @param file - The command executable name or path\n * @param options - The directory searched first and the child's effective environment\n * @returns The resolved absolute path, or `undefined` when no candidate is a regular file\n *\n * @example\n * ```ts\n * resolveExecutable('git', {}) ?? 'git' // an absolute path on Windows, 'git' elsewhere\n * ```\n */\nexport function resolveExecutable(file: string, options?: ExecutableOptions): string | undefined {\n\tif (process.platform !== 'win32') return undefined\n\tconst environment = options?.environment ?? process.env\n\tconst workspace = options?.workspace ?? process.cwd()\n\tconst extensions = (readVariable(environment, 'PATHEXT') ?? PROCESS_PATHEXT)\n\t\t.split(';')\n\t\t.filter((extension) => extension.length > 0)\n\tconst candidates = [file, ...extensions.map((extension) => `${file}${extension}`)]\n\tconst rooted = file.includes('/') || file.includes('\\\\')\n\tconst directories = rooted\n\t\t? [workspace]\n\t\t: [workspace, ...(readVariable(environment, 'PATH') ?? '').split(delimiter)]\n\tfor (const directory of directories) {\n\t\tif (directory.length === 0) continue\n\t\tfor (const candidate of candidates) {\n\t\t\tconst target = resolve(directory, candidate)\n\t\t\tif (isFile(target)) return target\n\t\t}\n\t}\n\treturn undefined\n}\n\n/**\n * Quotes one command-line token for a `cmd.exe` command line.\n *\n * @remarks\n * A token carrying whitespace or a shell metacharacter is wrapped in double quotes so `cmd.exe`\n * passes it as one literal argument; an embedded double quote is doubled, which is how `cmd.exe`\n * reads a literal quote inside a quoted token. Every other token is left exactly as written, so a\n * batch script still receives `%1` without added quotes.\n *\n * @param value - The token to quote\n * @returns The token, quoted only when `cmd.exe` would otherwise split or interpret it\n *\n * @example\n * ```ts\n * quoteArgument('a&b') // '\"a&b\"'\n * ```\n */\nexport function quoteArgument(value: string): string {\n\tif (value.length > 0 && !/[\\s\"&|<>^()%!]/.test(value)) return value\n\treturn `\"${value.replaceAll('\"', '\"\"')}\"`\n}\n\n/**\n * Builds the resolved spawn form of one command.\n *\n * @remarks\n * The executable is resolved against the child's effective environment. A resolved `.cmd` or `.bat`\n * script cannot be spawned directly **on Windows**, so it runs there through an explicitly quoted\n * `cmd.exe /d /s /c` command line with the argument vector passed verbatim. No path uses a shell,\n * so a metacharacter in an argument is never interpreted. `cmd.exe` expands `%NAME%` before it\n * parses quotes, so no quoting can carry a percent sign through to a Windows batch target: an\n * argument carrying one is refused there rather than silently rewritten. The whole batch path is\n * Windows-only. A POSIX host has no `cmd.exe` and no restriction on spawning a file directly, so a\n * target named `worker.cmd` spawns directly there and receives a percent sign as literal text.\n *\n * @param command - The executable and its argument vector\n * @param options - The directory searched first and the child's effective environment\n * @returns The file, argument vector, and verbatim flag to spawn with\n * @throws A {@link ProcessError} coded `invalid` when the host is Windows, the resolved target is a batch script, and an argument carries a percent sign\n *\n * @example\n * ```ts\n * buildSpawn({ file: 'node', arguments: ['--version'] }).verbatim // false\n * ```\n */\nexport function buildSpawn(command: ProcessCommand, options?: ExecutableOptions): SpawnInput {\n\tconst file = resolveExecutable(command.file, options) ?? command.file\n\tconst extension = extname(file).toLowerCase()\n\t// Windows alone cannot spawn a batch script directly, and `cmd.exe` is what expands `%NAME%`.\n\t// A POSIX host has neither restriction, so a file whose name ends in `.cmd` is an ordinary\n\t// executable there and its extension changes nothing about how it spawns.\n\tconst batch = process.platform === 'win32' && (extension === '.cmd' || extension === '.bat')\n\tif (!batch) {\n\t\treturn Object.freeze({\n\t\t\tfile,\n\t\t\targuments: Object.freeze([...command.arguments]),\n\t\t\tverbatim: false,\n\t\t})\n\t}\n\tfor (const argument of command.arguments) {\n\t\tif (argument.includes('%')) throw createInvalidError('command argument', argument)\n\t}\n\tconst line = [`\"${file}\"`, ...command.arguments.map(quoteArgument)].join(' ')\n\tconst environment = options?.environment ?? process.env\n\treturn Object.freeze({\n\t\tfile: readVariable(environment, 'ComSpec') ?? 'cmd.exe',\n\t\targuments: Object.freeze(['/d', '/s', '/c', `\"${line}\"`]),\n\t\tverbatim: true,\n\t})\n}\n\n/**\n * Validates one spawn-bound string.\n *\n * @remarks\n * A NUL character terminates a string for the host, so it is refused everywhere rather than\n * reaching the spawn as a raw `ERR_INVALID_ARG_VALUE`.\n *\n * @param value - The value to inspect\n * @param subject - The rejected input named as the caller wrote it, such as `command file`\n * @param required - If `true`, an empty string is refused; if `false`, it is accepted\n * @returns Nothing; a successful return means the string is spawn-safe\n * @throws A {@link ProcessError} coded `invalid` when the value is not a string, carries a NUL, or is required and empty\n *\n * @example\n * ```ts\n * validateText('status', 'command argument', false) // returns\n * ```\n */\nexport function validateText(value: unknown, subject: string, required: boolean): void {\n\tif (!isString(value) || value.includes('\\0')) throw createInvalidError(subject, value)\n\tif (required && !isNonEmptyString(value)) throw createInvalidError(subject, value)\n}\n\n/**\n * Validates one timer-valued option in milliseconds.\n *\n * @remarks\n * The host truncates a fractional delay and converts anything above {@link PROCESS_TIMER} to one\n * millisecond, so a requested 25-day timeout would otherwise fire immediately.\n *\n * @param value - The value to inspect, or `undefined` when the option was omitted\n * @param subject - The rejected input named as the caller wrote it, such as `option 'grace'`\n * @returns Nothing; a successful return means the value schedules as written\n * @throws A {@link ProcessError} coded `invalid` when the value is not a non-negative integer at or below `PROCESS_TIMER`\n *\n * @example\n * ```ts\n * validateTimer(5_000, \"option 'grace'\") // returns\n * ```\n */\nexport function validateTimer(value: number | undefined, subject: string): void {\n\tif (value === undefined) return\n\tif (!isNonNegativeInteger(value) || !boundsOf(0, PROCESS_TIMER)(value)) {\n\t\tthrow createInvalidError(subject, value)\n\t}\n}\n\n/**\n * Validates one byte-valued option.\n *\n * @param value - The value to inspect, or `undefined` when the option was omitted\n * @param subject - The rejected input named as the caller wrote it, such as `option 'limit'`\n * @param minimum - The smallest accepted value\n * @returns Nothing; a successful return means the value bounds a real byte count\n * @throws A {@link ProcessError} coded `invalid` when the value is not a safe integer at or above `minimum`\n *\n * @example\n * ```ts\n * validateBytes(1_024, \"option 'limit'\", 0) // returns\n * ```\n */\nexport function validateBytes(value: number | undefined, subject: string, minimum: number): void {\n\tif (value === undefined) return\n\tif (!isNonNegativeInteger(value) || !boundsOf(minimum, Number.MAX_SAFE_INTEGER)(value)) {\n\t\tthrow createInvalidError(subject, value)\n\t}\n}\n\n/**\n * Validates every spawn-bound string of one environment override map.\n *\n * @remarks\n * The host refuses a NUL in a variable name or value with a raw `ERR_INVALID_ARG_VALUE`, so the same\n * refusal runs here for a command's own overrides and for a per-invocation override map alike.\n *\n * @param environment - The override map, or `undefined` when none was supplied\n * @returns Nothing; a successful return means every name and value reaches the host intact\n * @throws A {@link ProcessError} coded `invalid` when a name is empty or a name or value carries a NUL\n *\n * @example\n * ```ts\n * validateEnvironment({ TOKEN: 'a' }) // returns\n * ```\n */\nexport function validateEnvironment(\n\tenvironment: Readonly<Record<string, string | undefined>> | undefined,\n): void {\n\tif (environment === undefined) return\n\tfor (const [key, value] of Object.entries(environment)) {\n\t\tvalidateText(key, 'environment key', true)\n\t\tif (value !== undefined) validateText(value, 'environment value', false)\n\t}\n}\n\n/**\n * Validates every spawn-bound string of one command.\n *\n * @param command - The executable, arguments, and optional environment overrides\n * @returns Nothing; a successful return means the command reaches the host intact\n * @throws A {@link ProcessError} coded `invalid` when the file is empty or any string carries a NUL\n *\n * @example\n * ```ts\n * validateCommand({ file: 'git', arguments: ['status'] }) // returns\n * ```\n */\nexport function validateCommand(command: ProcessCommand): void {\n\tvalidateText(command.file, 'command file', true)\n\tfor (const argument of command.arguments) validateText(argument, 'command argument', false)\n\tvalidateEnvironment(command.environment)\n}\n\n/**\n * Validates the working directory one child starts in.\n *\n * @param workspace - The directory, or `undefined` when the option was omitted\n * @returns Nothing; a successful return means the directory reaches the host intact\n * @throws A {@link ProcessError} coded `invalid` when the directory is empty or carries a NUL\n *\n * @example\n * ```ts\n * validateWorkspace(process.cwd()) // returns\n * ```\n */\nexport function validateWorkspace(workspace: string | undefined): void {\n\tif (workspace === undefined) return\n\tvalidateText(workspace, 'workspace', true)\n}\n\n/**\n * Retains one delivered chunk up to a byte limit and records what the stream delivered.\n *\n * @remarks\n * The retained head is kept as chunk slices and joined once, so a long stream never repeatedly\n * concatenates a growing buffer. `counts` is a two-slot tally: slot `0` counts every delivered byte\n * and slot `1` counts the retained bytes, so a caller compares slot `0` against `limit` to learn\n * whether the capture was truncated.\n *\n * @param chunk - The delivered chunk, ignored when it is not a buffer\n * @param chunks - The retained head, appended to in place\n * @param counts - The `[delivered, retained]` tally, updated in place\n * @param limit - The maximum retained byte count\n * @returns Nothing\n *\n * @example\n * ```ts\n * const chunks: Buffer[] = []\n * const counts = [0, 0]\n * retainChunk(Buffer.from('hello'), chunks, counts, 3)\n * counts[1] // 3\n * ```\n */\nexport function retainChunk(\n\tchunk: unknown,\n\tchunks: Buffer[],\n\tcounts: number[],\n\tlimit: number,\n): void {\n\tif (!Buffer.isBuffer(chunk)) return\n\tconst delivered = counts[0] ?? 0\n\tconst retained = counts[1] ?? 0\n\tcounts[0] = delivered + chunk.byteLength\n\tconst room = limit - retained\n\tif (room <= 0) return\n\tconst slice = chunk.byteLength <= room ? chunk : Buffer.from(chunk.subarray(0, room))\n\tchunks.push(slice)\n\tcounts[1] = retained + slice.byteLength\n}\n\n/**\n * Checks whether a child process has reached its native exit.\n *\n * @remarks\n * The host sets exactly one of these fields when the process ends, so together they are the\n * authoritative liveness answer. Reading them before each signal is what keeps a signal from being\n * initiated against a pid the host may have reused.\n *\n * @param child - The child boundary\n * @returns True when the process has exited; false while it is live\n *\n * @example\n * ```ts\n * isExited({ exitCode: 0, signalCode: null }) // true\n * ```\n */\nexport function isExited(child: Pick<ProcessChild, 'exitCode' | 'signalCode'>): boolean {\n\treturn child.exitCode !== null || child.signalCode !== null\n}\n\n/**\n * Signals one owned child process, or its detached process group on a POSIX host.\n *\n * @remarks\n * On a POSIX host a child spawned `detached` leads its own process group, so signalling the\n * negated pid reaches the whole tree. On Windows, or when no pid is available, the signal goes to\n * the child directly. A throw during signalling is swallowed: the process can exit between the\n * caller's liveness check and this call, and the child's native exit remains the authoritative\n * terminal state.\n *\n * @param child - The child boundary\n * @param signal - The signal to deliver\n * @returns Nothing\n *\n * @example\n * ```ts\n * killProcess(child, 'SIGTERM')\n * ```\n */\nexport function killProcess(\n\tchild: Pick<ProcessChild, 'pid' | 'kill'>,\n\tsignal: NodeJS.Signals,\n): void {\n\ttry {\n\t\tif (process.platform === 'win32' || child.pid === undefined) {\n\t\t\tchild.kill(signal)\n\t\t\treturn\n\t\t}\n\t\tprocess.kill(-child.pid, signal)\n\t} catch {\n\t\t// Exit can win the signal race; the native exit remains authoritative.\n\t}\n}\n\n/**\n * Kills one Windows process tree through `taskkill`.\n *\n * @remarks\n * Windows has no process group a signal can reach, so the whole tree is ended by the host utility,\n * addressed by its absolute `System32` path so a `PATH` override cannot substitute another program.\n * The call is bounded: it is killed itself when `timeout` elapses, which is what keeps a termination\n * from waiting on the host indefinitely. A tree is discoverable only while its root lives, so a\n * descendant that outlives the root is beyond this mechanism.\n *\n * @param pid - The root process id of the tree to end\n * @param timeout - The milliseconds the utility is given before it is killed\n * @returns True when the utility reported success; false when it failed, was unavailable, or was cut off\n *\n * @example\n * ```ts\n * await killTree(child.pid ?? 0, 5_000)\n * ```\n */\nexport function killTree(pid: number, timeout: number): Promise<boolean> {\n\tconst settled = Promise.withResolvers<boolean>()\n\tconst executable = join(process.env.SystemRoot ?? 'C:\\\\Windows', 'System32', 'taskkill.exe')\n\tconst killer = spawn(executable, ['/F', '/T', '/PID', String(pid)], {\n\t\tstdio: 'ignore',\n\t\twindowsHide: true,\n\t\ttimeout,\n\t})\n\tkiller.once('error', () => settled.resolve(false))\n\tkiller.once('close', (code: number | null) => settled.resolve(code === 0))\n\treturn settled.promise\n}\n\n/**\n * Waits for one child process's native exit, bounded by a deadline.\n *\n * @param child - The child boundary\n * @param timeout - The milliseconds to wait before giving up\n * @returns A promise that resolves on the native exit or when the deadline elapses, whichever comes first\n *\n * @example\n * ```ts\n * await waitForExit(child, 5_000)\n * ```\n */\nexport function waitForExit(\n\tchild: Pick<ProcessChild, 'exitCode' | 'signalCode' | 'once'>,\n\ttimeout: number,\n): Promise<void> {\n\tif (isExited(child)) return Promise.resolve()\n\tconst settled = Promise.withResolvers<void>()\n\tconst timer = setTimeout(settled.resolve, timeout)\n\tchild.once('exit', () => {\n\t\tclearTimeout(timer)\n\t\tsettled.resolve()\n\t})\n\treturn settled.promise\n}\n\n/**\n * Terminates one child process tree and reports whether its native exit was observed.\n *\n * @remarks\n * Windows ends the tree at once through {@link killTree}, falling back to a direct kill when the\n * utility fails, because the host has no cooperative termination to offer. A POSIX host signals the\n * process group `SIGTERM`, waits `grace`, then signals `SIGKILL`. No signal is initiated once the\n * native exit is observed; the window between initiating a signal and the host delivering it belongs\n * to the operating system. `confirm` bounds each awaited step rather than the call as a whole: on\n * Windows it bounds the `taskkill` call and then the final wait, and on a POSIX host `grace` bounds\n * the cooperative wait and `confirm` the final one. Never rejects.\n *\n * @param child - The child boundary\n * @param grace - The cooperative POSIX window in milliseconds between `SIGTERM` and `SIGKILL`\n * @param confirm - The milliseconds the native exit is awaited after the final kill\n * @returns True when the native exit was observed; false when `confirm` elapsed without it\n *\n * @example\n * ```ts\n * const confirmed = await stopChild(child, 5_000, 5_000)\n * ```\n */\nexport async function stopChild(\n\tchild: ProcessChild,\n\tgrace: number,\n\tconfirm: number,\n): Promise<boolean> {\n\tif (isExited(child)) return true\n\tif (process.platform === 'win32') {\n\t\tconst killed = child.pid === undefined ? false : await killTree(child.pid, confirm)\n\t\tif (!killed && !isExited(child)) killProcess(child, 'SIGKILL')\n\t} else {\n\t\tkillProcess(child, 'SIGTERM')\n\t\tawait waitForExit(child, grace)\n\t\tif (!isExited(child)) killProcess(child, 'SIGKILL')\n\t}\n\tif (isExited(child)) return true\n\tawait waitForExit(child, confirm)\n\treturn isExited(child)\n}\n\n/**\n * Builds one settled {@link RunResult} from a completed run's captured bytes and terminal facts.\n *\n * @remarks\n * `failed` is derived: a run failed when it timed out, was aborted, ended on a host fault, was ended\n * by a signal, or exited with a code other than `0` — a `null` code from a spawn fault is therefore\n * a failure. Both byte fields are bounded by `limit` on a code-point boundary here, which is the one\n * place the captured bytes are decoded.\n *\n * @param input - The captured bytes, terminal state, and capture limit\n * @returns The frozen run outcome\n *\n * @example\n * ```ts\n * buildRunResult({\n * \tcommand: 'git status',\n * \tstdout: Buffer.from('ok'),\n * \tstderr: Buffer.alloc(0),\n * \tcode: 0,\n * \tsignal: null,\n * \texpired: false,\n * \taborted: false,\n * \ttruncated: false,\n * \tlimit: 1_024,\n * }).failed // false\n * ```\n */\nexport function buildRunResult(input: RunInput): RunResult {\n\treturn Object.freeze({\n\t\tcommand: input.command,\n\t\tstdout: trimHead(input.stdout, input.limit).toString('utf8'),\n\t\tstderr: trimHead(input.stderr, input.limit).toString('utf8'),\n\t\tcode: input.code,\n\t\tsignal: input.signal,\n\t\tfailed:\n\t\t\tinput.expired ||\n\t\t\tinput.aborted ||\n\t\t\tinput.cause !== undefined ||\n\t\t\tinput.signal !== null ||\n\t\t\tinput.code !== 0,\n\t\texpired: input.expired,\n\t\taborted: input.aborted,\n\t\ttruncated: input.truncated,\n\t})\n}\n\n/**\n * Runs one command to completion, buffering its output, and settles with the outcome.\n *\n * @remarks\n * The executable is resolved through {@link buildSpawn}, so no run uses a shell. On a POSIX host the\n * child is detached, which is what lets its whole process group be terminated. Standard output and\n * error are each byte-bounded by `limit` (default {@link PROCESS_OUTPUT}) and `truncated` reports\n * whether either stream exceeded it. A positive `timeout` and an aborting `signal` both terminate the\n * child through the same bounded stop, and only the first of them is recorded: a timeout reports\n * `expired` and an abort reports `aborted`, never both. After termination the outcome is awaited for\n * a bounded window, so a descendant holding the child's stdio cannot keep the run pending. That bound\n * covers a terminated run alone: a run with no `timeout` and no `signal` settles on stdio completion\n * rather than on process exit, so a descendant that inherited the child's stdio holds the run open\n * after the child itself has gone. Give such a run a `timeout`. The child's `environment` merges over\n * the parent unless the command is `isolated`, then `options.environment` on top, and `options.input`\n * overrides `command.input`. Unless `strict` is `false`, a failed run rejects with a\n * {@link createRunError} carrying the {@link RunResult}. An invalid option or command string rejects\n * before the child is spawned, because an async function cannot throw synchronously.\n *\n * @param command - The executable, arguments, and optional environment and input\n * @param options - Working directory, timeout, grace, signal, capture limit, and failure delivery\n * @returns The settled run outcome\n * @throws A {@link ProcessError} coded `invalid` for a malformed option, command string, or batch-bound argument, or one carrying the {@link RunResult} when the run failed and `strict` is not `false`\n *\n * @example\n * ```ts\n * const result = await run({ file: 'git', arguments: ['status'] }, { workspace: process.cwd() })\n * ```\n */\nexport async function run(command: ProcessCommand, options?: RunOptions): Promise<RunResult> {\n\tvalidateCommand(command)\n\tvalidateEnvironment(options?.environment)\n\tvalidateWorkspace(options?.workspace)\n\tvalidateTimer(options?.timeout, \"option 'timeout'\")\n\tvalidateTimer(options?.grace, \"option 'grace'\")\n\tvalidateBytes(options?.limit, \"option 'limit'\", 0)\n\tconst limit = options?.limit ?? PROCESS_OUTPUT\n\tconst grace = options?.grace ?? PROCESS_GRACE\n\tconst timeout = options?.timeout ?? 0\n\tconst strict = options?.strict ?? true\n\tconst text = options?.input ?? command.input\n\tconst line = formatCommand(command)\n\tconst workspace = options?.workspace ?? process.cwd()\n\tconst environment = mergeEnvironment(\n\t\tcommand.isolated === true,\n\t\tcommand.environment,\n\t\toptions?.environment,\n\t)\n\tconst plan = buildSpawn(command, { workspace, environment })\n\tconst settled = Promise.withResolvers<RunResult>()\n\tconst terminate = new AbortController()\n\tconst cleanup = new AbortController()\n\tconst finish = new AbortController()\n\tconst outChunks: Buffer[] = []\n\tconst errChunks: Buffer[] = []\n\tconst outCounts: number[] = [0, 0]\n\tconst errCounts: number[] = [0, 0]\n\tlet spawned = false\n\tlet expired = false\n\tlet aborted = false\n\tlet cause: unknown\n\tlet timeoutTimer: ReturnType<typeof setTimeout> | undefined\n\tlet confirmTimer: ReturnType<typeof setTimeout> | undefined\n\n\tconst child: ChildProcessWithoutNullStreams = spawn(plan.file, [...plan.arguments], {\n\t\tcwd: workspace,\n\t\tdetached: process.platform !== 'win32',\n\t\tenv: environment,\n\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\twindowsHide: true,\n\t\twindowsVerbatimArguments: plan.verbatim,\n\t})\n\n\tfinish.signal.addEventListener(\n\t\t'abort',\n\t\t() => {\n\t\t\tclearTimeout(timeoutTimer)\n\t\t\tclearTimeout(confirmTimer)\n\t\t\tcleanup.abort()\n\t\t\tsettled.resolve(\n\t\t\t\tbuildRunResult({\n\t\t\t\t\tcommand: line,\n\t\t\t\t\tstdout: Buffer.concat(outChunks),\n\t\t\t\t\tstderr: Buffer.concat(errChunks),\n\t\t\t\t\tcode: child.exitCode,\n\t\t\t\t\tsignal: child.signalCode,\n\t\t\t\t\texpired,\n\t\t\t\t\taborted,\n\t\t\t\t\ttruncated: (outCounts[0] ?? 0) > limit || (errCounts[0] ?? 0) > limit,\n\t\t\t\t\tlimit,\n\t\t\t\t\t...(cause === undefined ? {} : { cause }),\n\t\t\t\t}),\n\t\t\t)\n\t\t},\n\t\t{ once: true },\n\t)\n\n\tterminate.signal.addEventListener(\n\t\t'abort',\n\t\t() => {\n\t\t\tclearTimeout(timeoutTimer)\n\t\t\tcleanup.abort()\n\t\t\tvoid stopChild(child, grace, PROCESS_CONFIRMATION).then(() => {\n\t\t\t\tif (finish.signal.aborted) return\n\t\t\t\tconfirmTimer = setTimeout(() => finish.abort(), PROCESS_CONFIRMATION)\n\t\t\t})\n\t\t},\n\t\t{ once: true },\n\t)\n\n\tchild.stdin.on('error', () => undefined)\n\tchild.stdout.on('data', (chunk: unknown) => retainChunk(chunk, outChunks, outCounts, limit))\n\tchild.stderr.on('data', (chunk: unknown) => retainChunk(chunk, errChunks, errCounts, limit))\n\tchild.once('spawn', () => {\n\t\tspawned = true\n\t})\n\tchild.once('error', (error: unknown) => {\n\t\tif (spawned) return\n\t\tcause = error\n\t\tfinish.abort()\n\t})\n\tchild.once('close', () => finish.abort())\n\n\tif (text !== undefined) child.stdin.write(text)\n\tchild.stdin.end()\n\tif (timeout > 0) {\n\t\ttimeoutTimer = setTimeout(() => {\n\t\t\texpired = true\n\t\t\tterminate.abort()\n\t\t}, timeout)\n\t}\n\tif (options?.signal !== undefined) {\n\t\tconst signal = options.signal\n\t\tsignal.addEventListener(\n\t\t\t'abort',\n\t\t\t() => {\n\t\t\t\taborted = true\n\t\t\t\tterminate.abort()\n\t\t\t},\n\t\t\t{ once: true, signal: cleanup.signal },\n\t\t)\n\t\tif (signal.aborted) {\n\t\t\taborted = true\n\t\t\tterminate.abort()\n\t\t}\n\t}\n\n\tconst result = await settled.promise\n\tif (result.failed && strict) throw createRunError(result, cause)\n\treturn result\n}\n\n/**\n * Runs one command to completion synchronously, buffering its output, and returns the outcome.\n *\n * @remarks\n * The synchronous counterpart of {@link run}, spawned through the same resolver and never through a\n * shell. The host offers no cooperative termination window and no in-flight cancellation, so this\n * contract carries neither. A positive `timeout` and an output overflow both end the child with\n * `SIGKILL`: an overflow reports `truncated` and `failed` together and trims the partial output to\n * `limit`, where {@link run} keeps reading and reports `truncated` without failing. The environment\n * and input follow the same merge as {@link run}. Unless `strict` is `false`, a failed run throws a\n * {@link createRunError} carrying the {@link RunResult}.\n *\n * @param command - The executable, arguments, and optional environment and input\n * @param options - Working directory, timeout, capture limit, and failure delivery\n * @returns The run outcome\n * @throws A {@link ProcessError} coded `invalid` for a malformed option, command string, or batch-bound argument, or one carrying the {@link RunResult} when the run failed and `strict` is not `false`\n *\n * @example\n * ```ts\n * const result = runSync({ file: 'git', arguments: ['--version'] }, { strict: false })\n * ```\n */\nexport function runSync(command: ProcessCommand, options?: RunSyncOptions): RunResult {\n\tvalidateCommand(command)\n\tvalidateEnvironment(options?.environment)\n\tvalidateWorkspace(options?.workspace)\n\tvalidateTimer(options?.timeout, \"option 'timeout'\")\n\tvalidateBytes(options?.limit, \"option 'limit'\", 0)\n\tconst limit = options?.limit ?? PROCESS_OUTPUT\n\tconst timeout = options?.timeout ?? 0\n\tconst strict = options?.strict ?? true\n\tconst text = options?.input ?? command.input\n\tconst line = formatCommand(command)\n\tconst workspace = options?.workspace ?? process.cwd()\n\tconst environment = mergeEnvironment(\n\t\tcommand.isolated === true,\n\t\tcommand.environment,\n\t\toptions?.environment,\n\t)\n\tconst plan = buildSpawn(command, { workspace, environment })\n\tconst outcome = spawnSync(plan.file, [...plan.arguments], {\n\t\tcwd: workspace,\n\t\tenv: environment,\n\t\tencoding: 'buffer',\n\t\tmaxBuffer: limit,\n\t\tkillSignal: 'SIGKILL',\n\t\twindowsHide: true,\n\t\twindowsVerbatimArguments: plan.verbatim,\n\t\t...(text !== undefined ? { input: text } : {}),\n\t\t...(timeout > 0 ? { timeout } : {}),\n\t})\n\tconst error = outcome.error\n\tconst fault = error !== undefined && 'code' in error && isString(error.code) ? error.code : ''\n\tconst result = buildRunResult({\n\t\tcommand: line,\n\t\tstdout: Buffer.isBuffer(outcome.stdout) ? outcome.stdout : Buffer.alloc(0),\n\t\tstderr: Buffer.isBuffer(outcome.stderr) ? outcome.stderr : Buffer.alloc(0),\n\t\tcode: outcome.status,\n\t\tsignal: outcome.signal,\n\t\texpired: fault === 'ETIMEDOUT',\n\t\taborted: false,\n\t\ttruncated: fault === 'ENOBUFS',\n\t\tlimit,\n\t\t...(error === undefined ? {} : { cause: error }),\n\t})\n\tif (result.failed && strict) throw createRunError(result, error)\n\treturn result\n}\n\n/**\n * Spawns one command as a detached process and returns without waiting for it.\n *\n * @remarks\n * The child owns no stdio and is unreferenced, so it outlives this process and nothing here observes\n * its outcome. The error listener is attached before the child is unreferenced, so a post-spawn host\n * fault is swallowed rather than crashing the caller; an invalid command is refused before anything\n * is spawned.\n *\n * @param command - The executable, arguments, and optional environment\n * @param options - The working directory the detached child starts in\n * @returns Nothing\n * @throws A {@link ProcessError} coded `invalid` when the working directory or a command string is malformed\n *\n * @example\n * ```ts\n * detach({ file: 'node', arguments: ['daemon.js'] }, { workspace: process.cwd() })\n * ```\n */\nexport function detach(command: ProcessCommand, options?: DetachOptions): void {\n\tvalidateCommand(command)\n\tvalidateWorkspace(options?.workspace)\n\tconst workspace = options?.workspace ?? process.cwd()\n\tconst environment = mergeEnvironment(command.isolated === true, command.environment)\n\tconst plan = buildSpawn(command, { workspace, environment })\n\tconst child = spawn(plan.file, [...plan.arguments], {\n\t\tcwd: workspace,\n\t\tdetached: true,\n\t\tenv: environment,\n\t\tstdio: 'ignore',\n\t\twindowsHide: true,\n\t\twindowsVerbatimArguments: plan.verbatim,\n\t})\n\tchild.once('error', () => undefined)\n\tchild.unref()\n}\n","import type { ChildProcessWithoutNullStreams } from 'node:child_process'\nimport type { Interface as ReadLineInterface } from 'node:readline'\nimport type { EmitterInterface } from '@orkestrel/emitter'\nimport type { ProcessEventMap, ProcessExit, ProcessInterface, ProcessOptions } from '@src/core'\nimport { Buffer } from 'node:buffer'\nimport { spawn } from 'node:child_process'\nimport { createInterface } from 'node:readline'\nimport { StringDecoder } from 'node:string_decoder'\nimport { Emitter } from '@orkestrel/emitter'\nimport { PROCESS_BACKLOG, PROCESS_CONFIRMATION, PROCESS_EVIDENCE, PROCESS_GRACE } from '@src/core'\nimport {\n\tbuildSpawn,\n\tmergeEnvironment,\n\tstopChild,\n\ttrimTail,\n\tvalidateBytes,\n\tvalidateCommand,\n\tvalidateTimer,\n\tvalidateWorkspace,\n} from './helpers.js'\n\n/**\n * One supervised child process with framed output, a bounded backlog, and bounded termination.\n *\n * @remarks\n * Standard output is framed through `readline` into a single-consumer stream: each line goes to\n * exactly one waiting iterator, so two iterators split the output rather than each receiving all of\n * it. While no consumer has ever requested an iterator,\n * stdout keeps draining so `exit` still resolves, and retention stops at the `backlog` mark: a\n * consumer attaching later receives the retained head, a gap, then the live stream. Once an iterator\n * has been requested, stdout pauses at the mark and resumes at half of it, so that consumer loses\n * nothing and the child feels real backpressure. The mark is soft — the host delivers whole chunks,\n * so the backlog overshoots it by up to one delivered chunk plus the drain that termination\n * releases. Standard error is decoded and forwarded live as the `stderr` event while a byte-bounded\n * raw tail is retained as `evidence`. The typed `emitter` also carries the child `error` cause on a\n * spawn fault and the terminal `exit`, alongside the `exit` promise. `stop` ends the whole tree and\n * reports whether the native exit was observed; `destroy` stops the child, destroys stdin so every\n * pending `send` settles, and destroys the emitter last. `destroy` resolving does not imply the\n * child's stdio has closed, so `exit` and `lines` can still be outstanding when a descendant holds\n * an inherited pipe.\n *\n * @example\n * ```ts\n * import { Process } from '@orkestrel/process/server'\n *\n * const child = new Process({\n * \tcommand: { file: 'node', arguments: ['--version'] },\n * \tworkspace: process.cwd(),\n * })\n * const exit = await child.exit\n * await child.destroy()\n * ```\n */\nexport class Process implements ProcessInterface {\n\treadonly #emitter: Emitter<ProcessEventMap>\n\treadonly #child: ChildProcessWithoutNullStreams\n\treadonly #reader: ReadLineInterface\n\treadonly #grace: number\n\treadonly #evidence: number\n\treadonly #backlog: number\n\treadonly #decoder = new StringDecoder('utf8')\n\treadonly #exit = Promise.withResolvers<ProcessExit>()\n\treadonly #lines: AsyncIterable<string>\n\treadonly #queue: string[] = []\n\treadonly #waiters: Array<PromiseWithResolvers<IteratorResult<string, void>>> = []\n\treadonly #signal: AbortSignal | undefined\n\treadonly #abort: EventListener | undefined\n\t#tail: Buffer = Buffer.alloc(0)\n\t#head = 0\n\t#pending = 0\n\t#requested = false\n\t#paused = false\n\t#terminating = false\n\t#closed = false\n\t#ended = false\n\t#stopping: Promise<boolean> | undefined\n\t#ending: Promise<void> | undefined\n\n\t/**\n\t * Spawn one child process and begin stream capture.\n\t *\n\t * @param options - Command, workspace, termination, capture, stdin, and observation settings\n\t * @throws A {@link ProcessError} coded `invalid` when an option or command string is malformed\n\t */\n\tconstructor(options: ProcessOptions) {\n\t\t// Every option and command property is read once, here, before anything is spawned. Reading a\n\t\t// property runs the caller's own getter, so a read after the spawn would let that getter throw\n\t\t// while a live child exists and no one holds a reference to it. Hoisting the reads is what\n\t\t// makes a construction failure unable to strand a process.\n\t\tconst command = options.command\n\t\tconst workspace = options.workspace\n\t\tconst grace = options.grace\n\t\tconst evidence = options.evidence\n\t\tconst backlog = options.backlog\n\t\tconst writable = options.writable\n\t\tconst signal = options.signal\n\t\tconst on = options.on\n\t\tconst error = options.error\n\t\tconst isolated = command.isolated\n\t\tconst overrides = command.environment\n\t\tconst input = command.input\n\t\tvalidateCommand(command)\n\t\tvalidateWorkspace(workspace)\n\t\tvalidateTimer(grace, \"option 'grace'\")\n\t\tvalidateBytes(evidence, \"option 'evidence'\", 0)\n\t\tvalidateBytes(backlog, \"option 'backlog'\", 1)\n\t\tthis.#emitter = new Emitter<ProcessEventMap>({\n\t\t\t...(on === undefined ? {} : { on }),\n\t\t\t...(error === undefined ? {} : { error }),\n\t\t})\n\t\tthis.#grace = grace ?? PROCESS_GRACE\n\t\tthis.#evidence = evidence ?? PROCESS_EVIDENCE\n\t\tthis.#backlog = backlog ?? PROCESS_BACKLOG\n\t\tthis.#signal = signal\n\t\tthis.#lines = Object.freeze({ [Symbol.asyncIterator]: this.#iterate.bind(this) })\n\t\tconst environment = mergeEnvironment(isolated === true, overrides)\n\t\tconst plan = buildSpawn(command, { workspace, environment })\n\t\tthis.#child = spawn(plan.file, [...plan.arguments], {\n\t\t\tcwd: workspace,\n\t\t\tdetached: process.platform !== 'win32',\n\t\t\tenv: environment,\n\t\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\t\twindowsHide: true,\n\t\t\twindowsVerbatimArguments: plan.verbatim,\n\t\t})\n\t\tthis.#reader = createInterface({ input: this.#child.stdout, crlfDelay: Infinity })\n\t\tthis.#reader.on('line', this.#push.bind(this))\n\t\tthis.#reader.once('close', this.#finish.bind(this))\n\t\tthis.#child.once('error', (cause: unknown) => this.#emitter.emit('error', cause))\n\t\tthis.#child.once('close', this.#close.bind(this))\n\t\tthis.#child.stdin.on('error', () => undefined)\n\t\tthis.#child.stderr.on('data', this.#retain.bind(this))\n\t\tif (input !== undefined) this.#child.stdin.write(input)\n\t\tif (writable !== true) this.#child.stdin.end()\n\t\tif (signal !== undefined) {\n\t\t\tthis.#abort = this.#terminate.bind(this)\n\t\t\tsignal.addEventListener('abort', this.#abort, { once: true })\n\t\t\tif (signal.aborted) void this.stop()\n\t\t}\n\t}\n\n\t/** The typed lifecycle observation surface. */\n\tget emitter(): EmitterInterface<ProcessEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\t/** The captured stdout lines, in arrival order, ending when the child's stdout closes. */\n\tget lines(): AsyncIterable<string> {\n\t\treturn this.#lines\n\t}\n\n\t/** The decoded byte-bounded stderr tail. */\n\tget evidence(): string {\n\t\treturn this.#tail.toString('utf8')\n\t}\n\n\t/** The terminal child state, observed once from the close event. */\n\tget exit(): Promise<ProcessExit> {\n\t\treturn this.#exit.promise\n\t}\n\n\t/**\n\t * Write one line to the open standard-input channel.\n\t *\n\t * @remarks\n\t * Never rejects. The promise settles when the host reports the line handled, so a line written to\n\t * a child that is not reading stays pending until the child drains it or the channel closes. Every\n\t * terminal teardown path destroys stdin, so a pending write always settles by teardown; a caller\n\t * that needs its own deadline arms a timer and calls `stop`.\n\t *\n\t * @param text - The line text without its trailing newline\n\t * @returns True when the line reached the host without error; false when the channel was closed, destroyed, ended, or the write failed\n\t */\n\tsend(text: string): Promise<boolean> {\n\t\tconst stdin = this.#child.stdin\n\t\tif (this.#closed || !stdin.writable) return Promise.resolve(false)\n\t\tconst settled = Promise.withResolvers<boolean>()\n\t\ttry {\n\t\t\tstdin.write(`${text}\\n`, (error?: Error | null) => {\n\t\t\t\tsettled.resolve(error === undefined || error === null)\n\t\t\t})\n\t\t} catch {\n\t\t\treturn Promise.resolve(false)\n\t\t}\n\t\treturn settled.promise\n\t}\n\n\t/**\n\t * Terminate the child process tree and await its observed exit.\n\t *\n\t * @remarks\n\t * Never rejects, and every call shares one termination.\n\t *\n\t * @returns True when the child's native exit was observed; false when the confirmation deadline elapsed without it\n\t */\n\tstop(): Promise<boolean> {\n\t\tif (this.#stopping !== undefined) return this.#stopping\n\t\tthis.#stopping = this.#kill()\n\t\treturn this.#stopping\n\t}\n\n\t/**\n\t * Stop the child, close its standard-input channel, and destroy the observation emitter.\n\t *\n\t * @remarks\n\t * Always resolves, including when termination was never confirmed.\n\t *\n\t * @returns The stable barrier shared by every call\n\t */\n\tdestroy(): Promise<void> {\n\t\tif (this.#ending !== undefined) return this.#ending\n\t\tthis.#ending = this.#teardown()\n\t\treturn this.#ending\n\t}\n\n\t#iterate(): AsyncIterator<string, void, void> {\n\t\tthis.#requested = true\n\t\treturn Object.freeze({ next: this.#next.bind(this) })\n\t}\n\n\t#next(): Promise<IteratorResult<string, void>> {\n\t\tconst line = this.#take()\n\t\tif (line !== undefined) return Promise.resolve(Object.freeze({ done: false, value: line }))\n\t\tif (this.#ended) return Promise.resolve(Object.freeze({ done: true, value: undefined }))\n\t\tconst waiter = Promise.withResolvers<IteratorResult<string, void>>()\n\t\tthis.#waiters.push(waiter)\n\t\treturn waiter.promise\n\t}\n\n\t// Dequeues through a read index, so draining a long backlog stays linear.\n\t#take(): string | undefined {\n\t\tif (this.#head >= this.#queue.length) return undefined\n\t\tconst line = this.#queue[this.#head]\n\t\tthis.#queue[this.#head] = ''\n\t\tthis.#head += 1\n\t\tif (this.#head >= this.#queue.length) {\n\t\t\tthis.#queue.length = 0\n\t\t\tthis.#head = 0\n\t\t}\n\t\tif (line === undefined) return undefined\n\t\tthis.#pending -= Buffer.byteLength(line) + 1\n\t\tthis.#relieve()\n\t\treturn line\n\t}\n\n\t#push(line: string): void {\n\t\tif (this.#ended) return\n\t\tconst waiter = this.#waiters.shift()\n\t\tif (waiter !== undefined) {\n\t\t\twaiter.resolve(Object.freeze({ done: false, value: line }))\n\t\t\treturn\n\t\t}\n\t\t// A retained line costs its payload plus the newline that framed it, so an empty line still\n\t\t// costs a byte and a flood of them reaches the mark.\n\t\tconst bytes = Buffer.byteLength(line) + 1\n\t\t// With no consumer ever attached, retention stops at the mark and the stream keeps draining\n\t\t// so the child can exit; a consumer attaching later receives the head, a gap, then the live\n\t\t// stream.\n\t\tif (!this.#requested && this.#pending + bytes > this.#backlog) return\n\t\tthis.#queue.push(line)\n\t\tthis.#pending += bytes\n\t\tthis.#restrain()\n\t}\n\n\t#restrain(): void {\n\t\tif (this.#paused || this.#terminating || !this.#requested) return\n\t\tif (this.#pending < this.#backlog) return\n\t\tthis.#paused = true\n\t\tthis.#reader.pause()\n\t}\n\n\t#relieve(): void {\n\t\tif (!this.#paused || this.#ended) return\n\t\tif (!this.#terminating && this.#pending > this.#backlog / 2) return\n\t\tthis.#paused = false\n\t\tthis.#reader.resume()\n\t}\n\n\t#finish(): void {\n\t\tif (this.#ended) return\n\t\tthis.#ended = true\n\t\tthis.#paused = false\n\t\tfor (const waiter of this.#waiters) {\n\t\t\twaiter.resolve(Object.freeze({ done: true, value: undefined }))\n\t\t}\n\t\tthis.#waiters.length = 0\n\t}\n\n\t#retain(chunk: unknown): void {\n\t\tif (!Buffer.isBuffer(chunk) || this.#closed) return\n\t\tthis.#tail = trimTail(Buffer.concat([this.#tail, chunk]), this.#evidence)\n\t\tconst text = this.#decoder.write(chunk)\n\t\tif (text.length > 0) this.#emitter.emit('stderr', text)\n\t}\n\n\t#close(code: number | null, signal: NodeJS.Signals | null): void {\n\t\tif (this.#closed) return\n\t\tconst suffix = this.#decoder.end()\n\t\tif (suffix.length > 0) this.#emitter.emit('stderr', suffix)\n\t\tthis.#closed = true\n\t\tif (this.#signal !== undefined && this.#abort !== undefined) {\n\t\t\tthis.#signal.removeEventListener('abort', this.#abort)\n\t\t}\n\t\tthis.#reader.close()\n\t\tconst exit = Object.freeze({ code, signal })\n\t\tthis.#exit.resolve(exit)\n\t\tthis.#emitter.emit('exit', exit)\n\t}\n\n\t#terminate(): void {\n\t\tvoid this.stop()\n\t}\n\n\tasync #kill(): Promise<boolean> {\n\t\t// A paused stdout holds the child's own write, and therefore its exit, so backpressure is\n\t\t// released before anything is signalled and never reapplied.\n\t\tthis.#terminating = true\n\t\tthis.#relieve()\n\t\tconst confirmed = await stopChild(this.#child, this.#grace, PROCESS_CONFIRMATION)\n\t\tthis.#child.stdin.destroy()\n\t\treturn confirmed\n\t}\n\n\tasync #teardown(): Promise<void> {\n\t\tawait this.stop()\n\t\tthis.#emitter.destroy()\n\t}\n}\n","import type { EmitterInterface } from '@orkestrel/emitter'\nimport type {\n\tProcessExit,\n\tProcessInterface,\n\tProcessManagerEventMap,\n\tProcessManagerInterface,\n\tProcessManagerOptions,\n\tProcessOptions,\n} from '@src/core'\nimport { Emitter } from '@orkestrel/emitter'\nimport { createDuplicateError, createProtocolError } from '@src/core'\nimport { Process } from './Process.js'\n\n/**\n * A keyed registry of live supervised child processes.\n *\n * @remarks\n * A child launched under an id joins the registry and emits `launch`; when it settles it removes\n * itself and emits `exit`, so `count` and `processes` reflect only live children. The id is reserved\n * before the child is constructed and released when construction throws, so a refused launch never\n * strands its key. Eviction follows the child's own `exit` promise, which no listener can forge, so\n * it lands one microtask after the child's public `exit` event: a listener on that event still sees\n * the child registered. `launch` throws a {@link createDuplicateError} when the id is already live\n * and a {@link createProtocolError} after `destroy` has begun, including when the caller's own option\n * getter begins that teardown mid-construction — a spawn fault surfaces through the returned child's\n * `exit`, never from `launch`. `destroy` awaits every child's own teardown, which destroys each\n * child's observation emitter and leaves every subscription on it silently inert, then destroys the\n * registry emitter last.\n *\n * @example\n * ```ts\n * import { ProcessManager } from '@orkestrel/process/server'\n *\n * const manager = new ProcessManager()\n * const child = manager.launch('build', {\n * \tcommand: { file: 'node', arguments: ['build.js'] },\n * \tworkspace: process.cwd(),\n * })\n * await child.exit\n * await manager.destroy()\n * ```\n */\nexport class ProcessManager implements ProcessManagerInterface {\n\treadonly #emitter: Emitter<ProcessManagerEventMap>\n\treadonly #children = new Map<string, ProcessInterface>()\n\treadonly #ids = new Set<string>()\n\t#destroying = false\n\t#ending: Promise<void> | undefined\n\n\t/**\n\t * Construct a process registry.\n\t *\n\t * @param options - Initial fleet-level observation hooks and listener-error handling\n\t */\n\tconstructor(options?: ProcessManagerOptions) {\n\t\tconst on = options?.on\n\t\tconst error = options?.error\n\t\tthis.#emitter = new Emitter<ProcessManagerEventMap>({\n\t\t\t...(on === undefined ? {} : { on }),\n\t\t\t...(error === undefined ? {} : { error }),\n\t\t})\n\t}\n\n\t/** The typed fleet-level observation surface. */\n\tget emitter(): EmitterInterface<ProcessManagerEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\t/** The number of live children. */\n\tget count(): number {\n\t\treturn this.#children.size\n\t}\n\n\t/**\n\t * The live child under `id`, or `undefined` when none is.\n\t *\n\t * @param id - The registry key\n\t * @returns The child, or `undefined`\n\t */\n\tprocess(id: string): ProcessInterface | undefined {\n\t\treturn this.#children.get(id)\n\t}\n\n\t/**\n\t * A snapshot of every live child.\n\t *\n\t * @returns The live children in launch order\n\t */\n\tprocesses(): readonly ProcessInterface[] {\n\t\treturn [...this.#children.values()]\n\t}\n\n\t/**\n\t * Spawn and register one child under `id`.\n\t *\n\t * @remarks\n\t * {@link Process} reads every option before it spawns, so a caller's own option getter runs while\n\t * nothing has started and a throw from one strands no process. A getter that begins `destroy`\n\t * without throwing is the one remaining race, and it leaves a bounded residual: the child is\n\t * already spawned, so the launch is refused with a {@link createProtocolError} and that child is\n\t * torn down asynchronously, bounded by `grace` plus the confirmation window. The `destroy` barrier\n\t * settled before the refusal, so awaiting it does not cover that teardown.\n\t *\n\t * @param id - The registry key, unique among live children\n\t * @param options - The child construction options\n\t * @returns The launched child\n\t * @throws A {@link ProcessError} coded `duplicate` when `id` is already live, `protocol` when the registry is being destroyed, or `invalid` when an option or command string is malformed\n\t */\n\tlaunch(id: string, options: ProcessOptions): ProcessInterface {\n\t\tif (this.#destroying) throw createProtocolError(id)\n\t\tif (this.#ids.has(id)) throw createDuplicateError(id)\n\t\tthis.#ids.add(id)\n\t\tconst child = this.#construct(id, options)\n\t\t// Reading an option runs the caller's own code, so teardown can begin between the check above\n\t\t// and the child that is now spawned. A registry being destroyed adopts nothing: the child is\n\t\t// torn down here, the reservation goes back, and the launch is refused.\n\t\tif (this.#destroying) {\n\t\t\tthis.#ids.delete(id)\n\t\t\tvoid child.destroy()\n\t\t\tthrow createProtocolError(id)\n\t\t}\n\t\tthis.#children.set(id, child)\n\t\tvoid child.exit.then((exit) => this.#evict(id, child, exit))\n\t\tthis.#emitter.emit('launch', id)\n\t\treturn child\n\t}\n\n\t/**\n\t * Terminate the named children and await their exit.\n\t *\n\t * @param ids - The registry keys to stop\n\t * @returns True when every named child was live and its exit was confirmed; false otherwise\n\t */\n\tstop(ids: readonly string[]): Promise<boolean>\n\t/**\n\t * Terminate one child and await its exit.\n\t *\n\t * @param id - The registry key to stop\n\t * @returns True when the child was live and its exit was confirmed; false when the id was not live or the confirmation deadline elapsed\n\t */\n\tstop(id: string): Promise<boolean>\n\t/**\n\t * Terminate every live child and await their exit.\n\t *\n\t * @returns A promise that resolves after every child stops\n\t */\n\tstop(): Promise<void>\n\tstop(target?: string | readonly string[]): Promise<boolean | void> {\n\t\tif (target === undefined) return this.#stopAll()\n\t\tif (typeof target === 'string') return this.#stopOne(target)\n\t\treturn this.#stopMany(target)\n\t}\n\n\t/**\n\t * Stop every child, then destroy the registry emitter last.\n\t *\n\t * @remarks\n\t * Always resolves, and refuses a later `launch` with a {@link createProtocolError}. Each child is\n\t * destroyed rather than merely stopped, so its own observation emitter is destroyed too and every\n\t * subscription on it goes silently inert.\n\t *\n\t * @returns The stable barrier shared by every call\n\t */\n\tdestroy(): Promise<void> {\n\t\tif (this.#ending !== undefined) return this.#ending\n\t\tthis.#destroying = true\n\t\tthis.#ending = this.#teardown()\n\t\treturn this.#ending\n\t}\n\n\t// Releases the reservation when construction throws, so a refused launch strands no id.\n\t#construct(id: string, options: ProcessOptions): ProcessInterface {\n\t\ttry {\n\t\t\treturn new Process(options)\n\t\t} catch (error) {\n\t\t\tthis.#ids.delete(id)\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t#evict(id: string, child: ProcessInterface, exit: ProcessExit): void {\n\t\tif (this.#children.get(id) !== child) return\n\t\tthis.#children.delete(id)\n\t\tthis.#ids.delete(id)\n\t\tthis.#emitter.emit('exit', id, exit)\n\t}\n\n\tasync #stopOne(id: string): Promise<boolean> {\n\t\tconst child = this.#children.get(id)\n\t\tif (child === undefined) return false\n\t\treturn child.stop()\n\t}\n\n\tasync #stopMany(ids: readonly string[]): Promise<boolean> {\n\t\tconst stopped = await Promise.all(ids.map((id) => this.#stopOne(id)))\n\t\treturn stopped.every((ok) => ok)\n\t}\n\n\tasync #stopAll(): Promise<void> {\n\t\tawait Promise.allSettled([...this.#children.values()].map((child) => child.stop()))\n\t}\n\n\tasync #teardown(): Promise<void> {\n\t\tawait Promise.allSettled([...this.#children.values()].map((child) => child.destroy()))\n\t\t// A destroyed registry holds nothing: a child whose stdio a descendant still holds would\n\t\t// otherwise linger here until a close event that may never arrive.\n\t\tthis.#children.clear()\n\t\tthis.#ids.clear()\n\t\tthis.#emitter.destroy()\n\t}\n}\n","import type {\n\tProcessInterface,\n\tProcessManagerInterface,\n\tProcessManagerOptions,\n\tProcessOptions,\n} from '@src/core'\nimport { Process } from './Process.js'\nimport { ProcessManager } from './ProcessManager.js'\n\n/**\n * Creates one supervised child process.\n *\n * @param options - Command, workspace, termination, evidence, stdin, and observation settings\n * @returns The launched {@link ProcessInterface}\n *\n * @example\n * ```ts\n * import { createProcess } from '@orkestrel/process/server'\n *\n * const child = createProcess({\n * \tcommand: { file: 'node', arguments: ['--version'] },\n * \tworkspace: process.cwd(),\n * \tgrace: 5000,\n * })\n * ```\n */\nexport function createProcess(options: ProcessOptions): ProcessInterface {\n\treturn new Process(options)\n}\n\n/**\n * Creates one keyed registry of supervised child processes.\n *\n * @param options - Initial fleet-level observation hooks and listener-error handling\n * @returns The {@link ProcessManagerInterface} registry\n *\n * @example\n * ```ts\n * import { createProcessManager } from '@orkestrel/process/server'\n *\n * const manager = createProcessManager()\n * ```\n */\nexport function createProcessManager(options?: ProcessManagerOptions): ProcessManagerInterface {\n\treturn new ProcessManager(options)\n}\n"],"mappings":"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"}
|