@orkestrel/process 0.0.2 → 0.0.4

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.
@@ -13,15 +13,196 @@ export declare function createDuplicateError(id: string): ProcessError;
13
13
  /**
14
14
  * Creates the failure raised when a run does not complete successfully and rejection is requested.
15
15
  *
16
+ * @remarks
17
+ * The category is `timeout` only when the run's own timeout elapsed; every other failure,
18
+ * including an abort and an output overflow, is a `spawn` failure carrying its
19
+ * {@link ExecuteResult}.
20
+ *
16
21
  * @param result - The buffered run outcome that failed
17
- * @param cause - The underlying spawn error, when one ended the run
18
- * @returns A typed run failure carrying its {@link RunResult}
22
+ * @param cause - The underlying host fault, when one ended the run
23
+ * @returns A typed run failure carrying its {@link ExecuteResult}
24
+ */
25
+ export declare function createExecuteError(result: ExecuteResult, cause?: unknown): ProcessError;
26
+
27
+ /**
28
+ * Creates the failure raised when a public input is refused before anything is spawned.
29
+ *
30
+ * @param subject - The rejected input named as the caller wrote it, such as `option 'grace'`
31
+ * @param value - The rejected value, carried as `context.value`
32
+ * @returns A typed validation failure
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * createInvalidError("option 'grace'", -1).code // 'invalid'
37
+ * ```
38
+ */
39
+ export declare function createInvalidError(subject: string, value: unknown): ProcessError;
40
+
41
+ /**
42
+ * Creates the failure raised when a launch is attempted on a registry that is being destroyed.
43
+ *
44
+ * @param id - The id the refused launch asked for
45
+ * @returns A typed protocol failure
19
46
  */
20
- export declare function createRunError(result: RunResult, cause?: unknown): ProcessError;
47
+ export declare function createProtocolError(id: string): ProcessError;
48
+
49
+ /**
50
+ * Options for a detached fire-and-forget spawn.
51
+ *
52
+ * @remarks
53
+ * A detached child owns no stdio and is never awaited, so the contract carries only the directory it
54
+ * starts in. Its environment comes from the command's own `environment` and `isolated`.
55
+ */
56
+ export declare interface DetachOptions {
57
+ /** The working directory. Default: the current working directory. */
58
+ readonly workspace?: string;
59
+ }
60
+
61
+ /**
62
+ * Lookup inputs for resolving a command file to an executable path.
63
+ *
64
+ * @remarks
65
+ * `workspace` is searched before `PATH`, matching Windows command semantics. `environment` is the
66
+ * child's effective environment, so the lookup reads the same `PATH` and `PATHEXT` the child will.
67
+ */
68
+ export declare interface ExecutableOptions {
69
+ /** The directory searched first. Default: the current working directory. */
70
+ readonly workspace?: string;
71
+ /** The child's effective environment. Default: the parent process environment. */
72
+ readonly environment?: Readonly<Record<string, string | undefined>>;
73
+ }
74
+
75
+ /**
76
+ * The captured bytes and terminal facts one settled {@link ExecuteResult} is built from.
77
+ *
78
+ * @remarks
79
+ * Each byte field is trimmed to `limit` on a code-point boundary when the result is built, so a
80
+ * caller passes the raw retained head and never a decoded string. `cause` carries the host fault
81
+ * that ended the run, when one did; its presence alone marks the run failed.
82
+ */
83
+ export declare interface ExecuteInput {
84
+ /** The diagnostic command line. */
85
+ readonly command: string;
86
+ /** The retained standard-output bytes. */
87
+ readonly stdout: Uint8Array;
88
+ /** The retained standard-error bytes. */
89
+ readonly stderr: Uint8Array;
90
+ readonly code: number | null;
91
+ readonly signal: string | null;
92
+ /** If `true`, the run's own timeout elapsed; if `false`, it did not. */
93
+ readonly expired: boolean;
94
+ /** If `true`, the caller's signal aborted the run; if `false`, it did not. */
95
+ readonly aborted: boolean;
96
+ /** If `true`, a stream exceeded `limit`; if `false`, neither did. */
97
+ readonly truncated: boolean;
98
+ /** The maximum retained bytes for each stream. */
99
+ readonly limit: number;
100
+ /** The host fault that ended the run, when one did. */
101
+ readonly cause?: unknown;
102
+ }
103
+
104
+ /**
105
+ * Options for a one-shot run.
106
+ *
107
+ * @remarks
108
+ * A run is a fire-and-collect function, not a lifecycle entity, so it carries no emitter. `strict`
109
+ * decides how a failure is delivered: by default a non-zero exit rejects with a
110
+ * {@link ProcessError} carrying the {@link ExecuteResult}; `strict: false` resolves with the result
111
+ * instead, so the caller inspects `failed`. An invalid option or command rejects before the child is
112
+ * spawned, with a {@link ProcessError} coded `invalid`. `input` is standard-input payload and carries
113
+ * no NUL restriction. An unbounded run awaits stdio completion rather than process exit, so give
114
+ * `timeout` a value wherever a descendant may inherit the child's stdio and hold the pipe open past
115
+ * the child's own exit. Every option is read once, before the child is spawned, so the value
116
+ * validated is the value spawned and a caller's own getter runs while nothing has started: a getter
117
+ * that throws strands no process.
118
+ */
119
+ export declare interface ExecuteOptions {
120
+ /** The working directory. Default: the current working directory. */
121
+ readonly workspace?: string;
122
+ /** Environment overrides merged over the parent environment; an `undefined` value unsets a key. */
123
+ readonly environment?: Readonly<Record<string, string | undefined>>;
124
+ /** Standard-input payload written to the child, including any NUL characters. */
125
+ readonly input?: string;
126
+ /** Milliseconds before the child is terminated. `0` or omitted disables the timeout. */
127
+ readonly timeout?: number;
128
+ /** Cooperative POSIX window between `SIGTERM` and `SIGKILL` when terminating. Default: {@link PROCESS_GRACE}. */
129
+ readonly grace?: number;
130
+ /** Aborting this signal terminates the run and reports `aborted`. */
131
+ readonly signal?: AbortSignal;
132
+ /** If `false`, resolve with the result on failure instead of rejecting. Default: `true`. */
133
+ readonly strict?: boolean;
134
+ /** Maximum captured bytes for stdout and for stderr, each. Default: {@link PROCESS_OUTPUT}. */
135
+ readonly limit?: number;
136
+ }
137
+
138
+ /**
139
+ * The settled outcome of a one-shot run: the buffered output and the terminal state.
140
+ *
141
+ * @remarks
142
+ * `failed` is `true` when the child exited non-zero, was killed by a signal, expired, was aborted, or
143
+ * failed to spawn. `expired` and `aborted` name the ways the run ended the child rather than the
144
+ * child ending itself, and only the earliest observed is recorded. `truncated` is independent of
145
+ * both: it reports that a captured stream omitted output because it exceeded `limit`, which fails a
146
+ * synchronous run and does not fail an asynchronous one. `ProcessInterface` carries the same name for
147
+ * the same fact against a supervised child's retention bounds. A spawn fault reports the host's negative errno for `execute`. A spawn fault reports
148
+ * `null` for `executeSync`.
149
+ */
150
+ export declare interface ExecuteResult {
151
+ /** The command line that was run, for diagnostics. */
152
+ readonly command: string;
153
+ /** The captured standard output, byte-bounded by `limit`. */
154
+ readonly stdout: string;
155
+ /** The captured standard error, byte-bounded by `limit`. */
156
+ readonly stderr: string;
157
+ /** The exit code. A spawn fault reports the host's negative errno for `execute` and `null` for `executeSync`. */
158
+ readonly code: number | null;
159
+ readonly signal: string | null;
160
+ /** True if the run did not complete successfully. */
161
+ readonly failed: boolean;
162
+ /** True if the run's `timeout` elapsed before completion. */
163
+ readonly expired: boolean;
164
+ /** True if the caller's `signal` aborted the run before completion. */
165
+ readonly aborted: boolean;
166
+ /** True when either captured stream omitted output because it exceeded `limit`. */
167
+ readonly truncated: boolean;
168
+ }
169
+
170
+ /**
171
+ * Options for a synchronous one-shot run.
172
+ *
173
+ * @remarks
174
+ * The synchronous host offers neither a cooperative termination window nor in-flight cancellation,
175
+ * so this contract carries no `grace` and no `signal`. A `timeout` ends only the root process and can
176
+ * leave descendants running; use `execute` or {@link ProcessInterface} when timeout must terminate the
177
+ * tree. A `timeout` and an output overflow both end the root with `SIGKILL`; an overflow reports
178
+ * `truncated` and `failed` together, which is where `execute` and `executeSync` genuinely differ.
179
+ * `input` is standard-input payload and carries no NUL restriction.
180
+ */
181
+ export declare interface ExecuteSyncOptions {
182
+ /** The working directory. Default: the current working directory. */
183
+ readonly workspace?: string;
184
+ /** Environment overrides merged over the parent environment; an `undefined` value unsets a key. */
185
+ readonly environment?: Readonly<Record<string, string | undefined>>;
186
+ /** Standard-input payload written to the child, including any NUL characters. */
187
+ readonly input?: string;
188
+ /** Milliseconds before the host kills the root process alone. `0` or omitted disables the timeout. */
189
+ readonly timeout?: number;
190
+ /** If `false`, return the result on failure instead of throwing. Default: `true`. */
191
+ readonly strict?: boolean;
192
+ /** Maximum captured bytes for stdout and for stderr, each. Default: {@link PROCESS_OUTPUT}. */
193
+ readonly limit?: number;
194
+ }
21
195
 
22
196
  /**
23
197
  * Checks whether an unknown value is a {@link ProcessError}.
24
198
  *
199
+ * @remarks
200
+ * Recognition combines a global own-property brand with the native `Error` base, the subclass
201
+ * prototype, the fixed name, and a code {@link PROCESS_ERROR_CODES} declares. The brand is
202
+ * recognized across duplicate installations and ESM/CommonJS module copies at 0.0.4 or later. A copy
203
+ * earlier than 0.0.4 stamps no brand, so an error it throws stays outside the type, and so does a
204
+ * plain or property-only lookalike.
205
+ *
25
206
  * @param value - The value to inspect
26
207
  * @returns True only for a `ProcessError` instance; false otherwise
27
208
  *
@@ -33,46 +214,69 @@ export declare function createRunError(result: RunResult, cause?: unknown): Proc
33
214
  */
34
215
  export declare function isProcessError(value: unknown): value is ProcessError;
35
216
 
217
+ /** The default soft high-water mark in bytes for a supervised {@link ProcessInterface} line backlog. */
218
+ export declare const PROCESS_BACKLOG = 10485760;
219
+
220
+ /** The window in milliseconds a termination waits for the child's native exit after the final kill. */
221
+ export declare const PROCESS_CONFIRMATION = 5000;
222
+
223
+ /** The machine-readable failure categories a {@link ProcessError} carries, in declaration order. */
224
+ export declare const PROCESS_ERROR_CODES: readonly ["spawn", "timeout", "duplicate", "protocol", "invalid"];
225
+
36
226
  /** The default maximum retained stderr tail in bytes for a supervised {@link ProcessInterface}. */
37
227
  export declare const PROCESS_EVIDENCE = 2048;
38
228
 
39
- /** The default cooperative window in milliseconds between `SIGTERM` and `SIGKILL` during termination. */
229
+ /** The default cooperative POSIX window in milliseconds between `SIGTERM` and `SIGKILL` during termination. */
40
230
  export declare const PROCESS_GRACE = 5000;
41
231
 
42
232
  /** The default maximum captured bytes for a one-shot run's stdout and stderr, each. */
43
233
  export declare const PROCESS_OUTPUT = 10485760;
44
234
 
235
+ /** The executable extensions a Windows lookup applies when the environment declares no `PATHEXT`. */
236
+ export declare const PROCESS_PATHEXT = ".COM;.EXE;.BAT;.CMD";
237
+
238
+ /** The largest timer delay in milliseconds the host schedules without truncating it to one. */
239
+ export declare const PROCESS_TIMER = 2147483647;
240
+
45
241
  /**
46
242
  * The public contracts for `@orkestrel/process`: a typed child-process toolkit.
47
243
  *
48
244
  * @remarks
49
- * Three tiers, smallest to largest:
245
+ * The tiers divide by lifetime:
50
246
  *
51
- * - **{@link ProcessInterface}** — one supervised child with eagerly framed stdout lines, a
52
- * byte-bounded stderr tail, a live stderr event, a writable stdin channel, a typed lifecycle
53
- * {@link ProcessEventMap} emitter, and bounded `SIGTERM` → grace → `SIGKILL` termination. The
54
- * low-level streaming primitive.
55
- * - **{@link RunResult} / run** — a one-shot runner that buffers a child to completion and settles
247
+ * - **{@link ProcessInterface}** — one supervised child with framed stdout lines under a bounded
248
+ * backlog, a byte-bounded stderr tail, a live stderr event, a writable stdin channel, a typed
249
+ * lifecycle {@link ProcessEventMap} emitter, and bounded termination. The low-level streaming
250
+ * primitive.
251
+ * - **{@link ExecuteResult} / execute** — a one-shot spawn that buffers a child to completion and settles
56
252
  * with its output and exit, the ergonomic layer for fire-and-collect commands.
57
253
  * - **{@link ProcessManagerInterface}** — a keyed registry of live {@link ProcessInterface}
58
254
  * children, launched and stopped by id, observed through its own {@link ProcessManagerEventMap}.
59
255
  *
60
- * Every contract here is host-independent (`AbortSignal`, `AsyncIterable`, `Promise`, and plain
61
- * `string` signal names). The Node implementations live in `@orkestrel/process/server`.
256
+ * Every contract here is host-independent (`AbortSignal`, `AsyncIterable`, `Promise`, `Uint8Array`,
257
+ * and plain `string` signal names). The Node implementations live in `@orkestrel/process/server`.
62
258
  */
63
259
  /**
64
260
  * One spawnable command: the executable, its argument vector, and optional environment overrides
65
261
  * and initial standard input.
66
262
  *
67
263
  * @remarks
68
- * `environment` merges over the parent process environment; an `undefined` value unsets a key.
69
- * `input` is written to the child's stdin immediately after spawn.
264
+ * `environment` merges over the parent process environment unless `isolated` is `true`, and an
265
+ * `undefined` value unsets a key. On Windows the merge is case-insensitive and the last writer
266
+ * wins, matching how the host resolves an environment variable. `input` is standard-input payload
267
+ * written immediately after spawn, so it carries no NUL restriction. Every spawn-bound string here
268
+ * is validated: an empty `file`, or a NUL character in `file`, `arguments`, or `environment`, is
269
+ * refused with a {@link ProcessError} coded `invalid`. On a POSIX host, `isolated: true` leaves no
270
+ * `PATH`, so pass an absolute `file` or include `PATH` in `environment`. On Windows, libuv injects a
271
+ * host environment set even when `isolated` is `true`.
70
272
  */
71
273
  export declare interface ProcessCommand {
72
274
  readonly file: string;
73
275
  readonly arguments: readonly string[];
74
276
  readonly environment?: Readonly<Record<string, string | undefined>>;
75
277
  readonly input?: string;
278
+ /** If `true`, exclude the parent environment; on POSIX this leaves no `PATH`, while Windows libuv still injects a host set. */
279
+ readonly isolated?: boolean;
76
280
  }
77
281
 
78
282
  /** A child-process failure with a stable machine-readable category. */
@@ -81,7 +285,7 @@ export declare class ProcessError extends Error {
81
285
  readonly code: ProcessErrorCode;
82
286
  readonly context?: ProcessErrorContext;
83
287
  /** The buffered run outcome, present when a one-shot run produced the failure. */
84
- readonly result?: RunResult;
288
+ readonly result?: ExecuteResult;
85
289
  /**
86
290
  * Create a process error.
87
291
  *
@@ -91,8 +295,8 @@ export declare class ProcessError extends Error {
91
295
  constructor(message: string, options: ProcessErrorOptions);
92
296
  }
93
297
 
94
- /** The machine-readable {@link ProcessError} categories. */
95
- export declare type ProcessErrorCode = 'spawn' | 'timeout' | 'duplicate' | 'protocol';
298
+ /** The machine-readable {@link ProcessError} categories, derived from {@link PROCESS_ERROR_CODES}. */
299
+ export declare type ProcessErrorCode = (typeof PROCESS_ERROR_CODES)[number];
96
300
 
97
301
  /** Structured context carried by a {@link ProcessError}. */
98
302
  export declare interface ProcessErrorContext {
@@ -116,8 +320,8 @@ export declare interface ProcessErrorOptions {
116
320
  readonly context?: ProcessErrorContext;
117
321
  /** The underlying cause, when one exists. */
118
322
  readonly cause?: unknown;
119
- /** The buffered run outcome, present when a {@link RunResult} produced the failure. */
120
- readonly result?: RunResult;
323
+ /** The buffered run outcome, present when an {@link ExecuteResult} produced the failure. */
324
+ readonly result?: ExecuteResult;
121
325
  }
122
326
 
123
327
  /**
@@ -141,46 +345,88 @@ export declare type ProcessEventMap = {
141
345
 
142
346
  /** The observed terminal state of a child process: its exit code, or the signal that ended it. */
143
347
  export declare interface ProcessExit {
144
- /** The exit code, or `null` when a signal ended the process. */
348
+ /** The exit code, or `null` when a signal ended the process. A spawn fault reports the host's negative errno for `Process` and `execute`. */
145
349
  readonly code: number | null;
146
350
  /** The terminating signal name, or `null` when the process exited on its own. */
147
351
  readonly signal: string | null;
148
352
  }
149
353
 
150
354
  /**
151
- * One supervised child process with eagerly framed output and bounded termination.
355
+ * One supervised child process with framed output, a bounded backlog, and bounded termination.
152
356
  *
153
357
  * @remarks
154
- * `lines` is pumped eagerly: stdout is drained whether or not a consumer iterates, so `exit`
155
- * resolves and a late consumer still receives every line. `evidence` is the decoded, byte-bounded
156
- * stderr tail — the diagnostic to attach to a failed exit. The typed `emitter` carries the live
157
- * `stderr` chunks, the child `error` cause, and the terminal `exit`, alongside the `exit` promise.
158
- * `send` never throws; it returns whether the line reached an open channel. `stop` is idempotent.
358
+ * `lines` is pumped as soon as the child writes, and it is a single-consumer stream: each line goes
359
+ * to exactly one waiting iterator, so concurrent iterators split the output between them rather
360
+ * than each receiving all of it. The policy for an unconsumed backlog follows consumer intent. After an iterator has been requested, stdout pauses at the `backlog` mark and
361
+ * resumes at half of it, so the consumer loses nothing before termination and the child feels real
362
+ * backpressure. Termination never reapplies the pause, so from the moment a stop begins retention is
363
+ * capped at twice `backlog` and later lines are dropped. While
364
+ * no iterator has ever been requested, stdout keeps draining so `exit` still resolves, and retention
365
+ * stops at the mark: a consumer attaching after that point receives the retained head, a gap, then
366
+ * the live stream. `evidence` is the decoded, byte-bounded stderr tail — the diagnostic to attach to
367
+ * a failed exit. `truncated` reports that the `lines` stream omitted output, because a retention
368
+ * bound was reached; the one-shot `ExecuteResult` carries the same name for the same fact against
369
+ * its own capture `limit`. The typed `emitter` carries the live `stderr` chunks, the child `error` cause, and the
370
+ * terminal `exit`, alongside the `exit` promise. `stop` and `destroy` are idempotent and never reject.
371
+ *
372
+ * The spawn is eager, so `pid` is fixed by the time construction returns; a spawn that produced no
373
+ * child reports `undefined` for that child's whole lifetime. An assigned id survives the exit and
374
+ * reports no liveness on its own, because the host reuses a dead child's id. Derive liveness as
375
+ * `pid !== undefined && code === null && signal === null`, and derive it again before every use of
376
+ * the id. `code` and `signal` mirror the host child's own terminal record, so they carry the native
377
+ * exit as soon as the host has it, while the `exit` promise settles on stdio close — a descendant
378
+ * holding inherited stdio keeps that close pending past the native exit, and a supervisor inside
379
+ * that window reads the terminal state here.
159
380
  */
160
381
  export declare interface ProcessInterface {
382
+ /** The host process id, fixed when construction returns, or `undefined` when the spawn produced none. */
383
+ readonly pid: number | undefined;
384
+ /** The exit code the host recorded, or `null` while the child has not exited and when a signal ended it. A spawn fault reports the host's negative errno. */
385
+ readonly code: number | null;
386
+ /** The terminating signal name the host recorded, or `null` while the child has not exited and when it exited on its own. */
387
+ readonly signal: string | null;
161
388
  /** The typed lifecycle observation surface. */
162
389
  readonly emitter: EmitterInterface<ProcessEventMap>;
163
- /** The eagerly captured stdout lines, in arrival order, ending when the child's stdout closes. */
390
+ /** The captured stdout lines, in arrival order, for one consumer, ending when the child's stdout closes. */
164
391
  readonly lines: AsyncIterable<string>;
165
392
  /** The decoded byte-bounded stderr tail. */
166
393
  readonly evidence: string;
394
+ /** True when the `lines` stream omitted output because a retention bound was reached. */
395
+ readonly truncated: boolean;
167
396
  /** The terminal child state, observed once from the close event. */
168
397
  readonly exit: Promise<ProcessExit>;
169
398
  /**
170
399
  * Write one line to the open standard-input channel.
171
400
  *
401
+ * @remarks
402
+ * Never rejects. The promise settles when the host reports the line handled, so a line written
403
+ * to a child that is not reading stays pending until the child drains it or the channel closes.
404
+ * Every terminal teardown path destroys stdin, so a pending write always settles by teardown. A
405
+ * caller that needs its own deadline arms a timer and calls `stop`.
406
+ *
172
407
  * @param text - The line text without its trailing newline
173
- * @returns True when the channel accepted the line without backpressure; false otherwise
408
+ * @returns True when the line reached the host without error; false when the channel was closed, destroyed, ended, or the write failed
174
409
  */
175
- send(text: string): boolean;
410
+ send(text: string): Promise<boolean>;
176
411
  /**
177
412
  * Terminate the child process tree and await its observed exit.
178
413
  *
179
- * @returns A promise that resolves after bounded termination
414
+ * @remarks
415
+ * Never rejects. On Windows the whole tree is killed immediately through `taskkill`, with a
416
+ * direct kill as the fallback; on a POSIX host the process group receives `SIGTERM`, then
417
+ * `SIGKILL` after `grace`. No signal is initiated after the child's native exit is observed; the
418
+ * window between initiating a signal and the host delivering it belongs to the operating system.
419
+ *
420
+ * @returns True when the child's native exit was observed; false when the confirmation deadline elapsed without it
180
421
  */
181
- stop(): Promise<void>;
422
+ stop(): Promise<boolean>;
182
423
  /**
183
- * Stop the child and destroy the observation emitter.
424
+ * Stop the child, close its standard-input channel, and destroy the observation emitter.
425
+ *
426
+ * @remarks
427
+ * Always resolves, including when termination was never confirmed. Resolving does not imply the
428
+ * child's stdio has closed: a descendant holding an inherited pipe keeps the close event pending,
429
+ * so `exit` and `lines` can still be outstanding after this barrier settles.
184
430
  *
185
431
  * @returns The stable barrier shared by every call
186
432
  */
@@ -206,10 +452,12 @@ export declare type ProcessManagerEventMap = {
206
452
  *
207
453
  * @remarks
208
454
  * A child that settles removes itself from the registry, so `count` and `processes` reflect only
209
- * live children, and its departure emits `exit`. `launch` throws a {@link ProcessError} coded
210
- * `duplicate` when the id is already live — spawn faults surface through the returned child's
211
- * `exit`, not from `launch`. The typed `emitter` carries the `launch` and `exit` moments across
212
- * every child.
455
+ * live children, and its departure emits `exit`. Eviction follows the child's own `exit` promise,
456
+ * which no listener can forge, so it lands one microtask after the child's public `exit` event: a
457
+ * listener on that event still sees the child registered. `launch` throws a {@link ProcessError}
458
+ * coded `duplicate` when the id is already live and `protocol` after `destroy` has begun — spawn
459
+ * faults surface through the returned child's `exit`, not from `launch`. The typed `emitter` carries
460
+ * the `launch` and `exit` moments across every child.
213
461
  */
214
462
  export declare interface ProcessManagerInterface {
215
463
  /** The typed fleet-level observation surface. */
@@ -232,35 +480,50 @@ export declare interface ProcessManagerInterface {
232
480
  /**
233
481
  * Spawn and register one child under `id`.
234
482
  *
483
+ * @remarks
484
+ * Every option is read before the child is spawned, so a caller's own option getter runs while
485
+ * nothing has started and a throw from one strands no process. A getter that begins `destroy`
486
+ * without throwing is the one remaining race, and it leaves a bounded residual: the child is
487
+ * already spawned, so the launch is refused with `protocol` and that child is torn down
488
+ * asynchronously, bounded by `grace` plus the confirmation window. The `protocol` refusal throws
489
+ * synchronously before the `destroy` barrier settles. The barrier does not cover that child's
490
+ * asynchronous teardown.
491
+ *
235
492
  * @param id - The registry key, unique among live children
236
493
  * @param options - The child construction options
237
494
  * @returns The launched child
238
- * @throws A {@link ProcessError} coded `duplicate` when `id` is already live
495
+ * @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
239
496
  */
240
497
  launch(id: string, options: ProcessOptions): ProcessInterface;
241
498
  /**
242
499
  * Terminate the named children and await their exit.
243
500
  *
244
501
  * @param ids - The registry keys to stop
245
- * @returns True when every named child stopped, false when any id was not live
502
+ * @returns True when every named child was live and its exit was confirmed; false otherwise
246
503
  */
247
504
  stop(ids: readonly string[]): Promise<boolean>;
248
505
  /**
249
506
  * Terminate one child and await its exit.
250
507
  *
251
508
  * @param id - The registry key to stop
252
- * @returns True when the child was live and stopped, false when the id was not live
509
+ * @returns True when the child was live and its exit was confirmed; false when the id was not live or the confirmation deadline elapsed
253
510
  */
254
511
  stop(id: string): Promise<boolean>;
255
512
  /**
256
513
  * Terminate every live child and await their exit.
257
514
  *
258
- * @returns A promise that resolves after all children stop
515
+ * @returns A promise that resolves after every child stops
259
516
  */
260
517
  stop(): Promise<void>;
261
518
  /**
262
519
  * Stop every child, then destroy the registry emitter last.
263
520
  *
521
+ * @remarks
522
+ * Always resolves, and refuses a later `launch` with a {@link ProcessError} coded `protocol`.
523
+ * Every child is destroyed, not merely stopped, so each child's own observation emitter is
524
+ * destroyed too and every subscription on it goes silently inert. The registry emitter is
525
+ * destroyed last.
526
+ *
264
527
  * @returns The stable barrier shared by every call
265
528
  */
266
529
  destroy(): Promise<void>;
@@ -282,10 +545,17 @@ export declare interface ProcessManagerOptions {
282
545
  * Construction options for one supervised child process.
283
546
  *
284
547
  * @remarks
285
- * `grace` is the sole timing knob: after `stop`, the child is sent `SIGTERM`, then `SIGKILL` if it
286
- * has not exited within `grace` milliseconds. There is no completion deadline — a caller that wants
287
- * one arms its own timer and calls `stop`. `on` installs initial {@link ProcessEventMap} listeners
288
- * and `error` receives isolated listener failures.
548
+ * `grace` is the cooperative window between `SIGTERM` and `SIGKILL` on a POSIX host; Windows has no
549
+ * cooperative phase, so the value is unused there. There is no completion deadline — a caller that
550
+ * wants one arms its own timer and calls `stop`. `backlog` bounds the unconsumed `lines` backlog.
551
+ * During termination, retained lines are capped at twice `backlog`; later lines are dropped without
552
+ * pausing stdout, and {@link ProcessInterface.truncated} reports the omission. `on` installs initial
553
+ * {@link ProcessEventMap} listeners and `error` receives isolated listener failures. Every numeric
554
+ * option is validated at construction: a timer value outside
555
+ * `[0, PROCESS_TIMER]`, a negative or fractional byte value, or a `backlog` below `1` throws a
556
+ * {@link ProcessError} coded `invalid`. POSIX detachment creates the process group used for tree
557
+ * termination, so the child survives the supervisor's `SIGKILL` and does not receive the terminal's
558
+ * `SIGINT`. A consumer must call `stop` or `destroy` during an orderly shutdown.
289
559
  */
290
560
  export declare interface ProcessOptions {
291
561
  readonly on?: EmitterHooks<ProcessEventMap>;
@@ -293,10 +563,12 @@ export declare interface ProcessOptions {
293
563
  readonly command: ProcessCommand;
294
564
  /** The working directory the child runs in. */
295
565
  readonly workspace: string;
296
- /** Cooperative termination window in milliseconds between `SIGTERM` and `SIGKILL`. */
297
- readonly grace: number;
566
+ /** Cooperative POSIX window in milliseconds between `SIGTERM` and `SIGKILL`. Default: {@link PROCESS_GRACE}. */
567
+ readonly grace?: number;
298
568
  /** Maximum retained stderr tail in bytes. Default: {@link PROCESS_EVIDENCE}. */
299
569
  readonly evidence?: number;
570
+ /** Soft high-water mark in bytes for the unconsumed `lines` backlog; termination retains at most twice this value. Default: {@link PROCESS_BACKLOG}. */
571
+ readonly backlog?: number;
300
572
  /** If `true`, stdin stays open for {@link ProcessInterface.send}; if `false` or omitted, stdin closes after any initial `input`. */
301
573
  readonly writable?: boolean;
302
574
  /** Aborting this signal terminates the child through the same bounded `stop`. */
@@ -304,53 +576,21 @@ export declare interface ProcessOptions {
304
576
  }
305
577
 
306
578
  /**
307
- * Options for a one-shot run.
308
- *
309
- * @remarks
310
- * A run is a fire-and-collect function, not a lifecycle entity, so it carries no emitter. `reject`
311
- * follows the command-runner convention: by default a non-zero exit rejects with a
312
- * {@link ProcessError} carrying the {@link RunResult}; `reject: false` resolves with the result
313
- * instead, so the caller inspects `failed`.
314
- */
315
- export declare interface RunOptions {
316
- /** The working directory. Default: the current working directory. */
317
- readonly workspace?: string;
318
- /** Environment overrides merged over the parent environment; an `undefined` value unsets a key. */
319
- readonly environment?: Readonly<Record<string, string | undefined>>;
320
- /** Standard input written to the child. */
321
- readonly input?: string;
322
- /** Milliseconds before the child is terminated. `0` or omitted disables the timeout. */
323
- readonly timeout?: number;
324
- /** Cooperative window between `SIGTERM` and `SIGKILL` when terminating. Default: {@link PROCESS_GRACE}. */
325
- readonly grace?: number;
326
- /** Aborting this signal terminates the run. */
327
- readonly signal?: AbortSignal;
328
- /** If `false`, resolve with the result on failure instead of rejecting. Default: `true`. */
329
- readonly reject?: boolean;
330
- /** Maximum captured bytes for stdout and for stderr, each. Default: {@link PROCESS_OUTPUT}. */
331
- readonly limit?: number;
332
- }
333
-
334
- /**
335
- * The settled outcome of a one-shot run: the buffered output and the terminal state.
579
+ * The resolved spawn form of one command: the executable to launch, the argument vector to pass,
580
+ * and whether the host receives that vector verbatim.
336
581
  *
337
582
  * @remarks
338
- * `failed` is `true` when the child exited non-zero, was killed by a signal, timed out, or failed to
339
- * spawn. `timedOut` is `true` only when the run's own `timeout` elapsed.
583
+ * No spawn in this package uses an implicit shell. `verbatim` is `true` only for a Windows `.cmd` or
584
+ * `.bat` script, which runs through an explicitly quoted `cmd.exe /d /s /c` command line, and an
585
+ * argument that batch target could corrupt — one carrying `%`, which `cmd.exe` expands before it
586
+ * parses quotes — is refused rather than passed. A shell metacharacter in an argument is therefore
587
+ * data rather than syntax on every path.
340
588
  */
341
- export declare interface RunResult {
342
- /** The command line that was run, for diagnostics. */
343
- readonly command: string;
344
- /** The complete captured standard output, byte-bounded by `limit`. */
345
- readonly stdout: string;
346
- /** The complete captured standard error, byte-bounded by `limit`. */
347
- readonly stderr: string;
348
- readonly code: number | null;
349
- readonly signal: string | null;
350
- /** True if the run did not complete successfully. */
351
- readonly failed: boolean;
352
- /** True if the run's `timeout` elapsed before completion. */
353
- readonly timedOut: boolean;
589
+ export declare interface SpawnInput {
590
+ readonly file: string;
591
+ readonly arguments: readonly string[];
592
+ /** If `true`, pass the argument vector to the host verbatim; if `false`, let the host quote it. */
593
+ readonly verbatim: boolean;
354
594
  }
355
595
 
356
596
  export { }