@orkestrel/process 0.0.4 → 0.0.5

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.
@@ -19,6 +19,7 @@ var PROCESS_PATHEXT = ".COM;.EXE;.BAT;.CMD";
19
19
  var PROCESS_ERROR_CODES = Object.freeze([
20
20
  "spawn",
21
21
  "timeout",
22
+ "input",
22
23
  "duplicate",
23
24
  "protocol",
24
25
  "invalid"
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts"],"sourcesContent":["/** The default cooperative POSIX window in milliseconds between `SIGTERM` and `SIGKILL` during termination. */\nexport const PROCESS_GRACE = 5_000\n\n/** The window in milliseconds a termination waits for the child's native exit after the final kill. */\nexport const PROCESS_CONFIRMATION = 5_000\n\n/** The default maximum retained stderr tail in bytes for a supervised {@link ProcessInterface}. */\nexport const PROCESS_EVIDENCE = 2_048\n\n/** The default soft high-water mark in bytes for a supervised {@link ProcessInterface} line backlog. */\nexport const PROCESS_BACKLOG = 10_485_760\n\n/** The default maximum captured bytes for a one-shot run's stdout and stderr, each. */\nexport const PROCESS_OUTPUT = 10_485_760\n\n/** The largest timer delay in milliseconds the host schedules without truncating it to one. */\nexport const PROCESS_TIMER = 2_147_483_647\n\n/** The executable extensions a Windows lookup applies when the environment declares no `PATHEXT`. */\nexport const PROCESS_PATHEXT = '.COM;.EXE;.BAT;.CMD'\n\n/** The machine-readable failure categories a {@link ProcessError} carries, in declaration order. */\nexport const PROCESS_ERROR_CODES = Object.freeze([\n\t'spawn',\n\t'timeout',\n\t'duplicate',\n\t'protocol',\n\t'invalid',\n] as const)\n","import type {\n\tProcessErrorCode,\n\tProcessErrorContext,\n\tProcessErrorOptions,\n\tExecuteResult,\n} from './types.js'\nimport { holds, isError } from '@orkestrel/contract'\nimport { PROCESS_ERROR_CODES } from './constants.js'\n\n/** A child-process failure with a stable machine-readable category. */\nexport class ProcessError extends Error {\n\toverride readonly name = 'ProcessError'\n\treadonly code: ProcessErrorCode\n\treadonly context?: ProcessErrorContext\n\t/** The buffered run outcome, present when a one-shot run produced the failure. */\n\treadonly result?: ExecuteResult\n\n\t/**\n\t * Create a process error.\n\t *\n\t * @param message - Human-readable failure description\n\t * @param options - Machine-readable category, optional context, optional cause, and optional run result\n\t */\n\tconstructor(message: string, options: ProcessErrorOptions) {\n\t\tconst cause = options.cause\n\t\tsuper(message, cause === undefined ? undefined : { cause })\n\t\tObject.defineProperty(this, Symbol.for('@orkestrel/process.error'), { value: true })\n\t\tthis.code = options.code\n\t\tif (options.context !== undefined) this.context = options.context\n\t\tif (options.result !== undefined) this.result = options.result\n\t}\n}\n\n/**\n * Checks whether an unknown value is a {@link ProcessError}.\n *\n * @remarks\n * Recognition combines a global own-property brand with the native `Error` base, the subclass\n * prototype, the fixed name, and a code {@link PROCESS_ERROR_CODES} declares. The brand is\n * recognized across duplicate installations and ESM/CommonJS module copies at 0.0.4 or later. A copy\n * earlier than 0.0.4 stamps no brand, so an error it throws stays outside the type, and so does a\n * plain or property-only lookalike.\n *\n * @param value - The value to inspect\n * @returns True only for a `ProcessError` instance; false otherwise\n *\n * @example\n * ```ts\n * isProcessError(new ProcessError('spawn failed', { code: 'spawn' })) // true\n * isProcessError(new Error('spawn failed')) // false\n * ```\n */\nexport function isProcessError(value: unknown): value is ProcessError {\n\tif (!isError(value)) return false\n\treturn holds(() => {\n\t\tif (Object.getPrototypeOf(value) === Error.prototype) return false\n\t\tif (value.name !== 'ProcessError' || !('code' in value)) return false\n\t\tconst descriptor = Object.getOwnPropertyDescriptor(\n\t\t\tvalue,\n\t\t\tSymbol.for('@orkestrel/process.error'),\n\t\t)\n\t\tif (descriptor?.value !== true) return false\n\t\tconst code: unknown = value.code\n\t\treturn PROCESS_ERROR_CODES.some((declared) => declared === code)\n\t})\n}\n\n/**\n * Creates the failure raised when a manager launch reuses a live id.\n *\n * @param id - The id already occupied by a live child\n * @returns A typed duplicate-id failure\n */\nexport function createDuplicateError(id: string): ProcessError {\n\treturn new ProcessError(`Process '${id}' is already live`, {\n\t\tcode: 'duplicate',\n\t\tcontext: { id },\n\t})\n}\n\n/**\n * Creates the failure raised when a launch is attempted on a registry that is being destroyed.\n *\n * @param id - The id the refused launch asked for\n * @returns A typed protocol failure\n */\nexport function createProtocolError(id: string): ProcessError {\n\treturn new ProcessError(`Process '${id}' cannot launch: the registry is destroyed`, {\n\t\tcode: 'protocol',\n\t\tcontext: { id },\n\t})\n}\n\n/**\n * Creates the failure raised when a public input is refused before anything is spawned.\n *\n * @param subject - The rejected input named as the caller wrote it, such as `option 'grace'`\n * @param value - The rejected value, carried as `context.value`\n * @returns A typed validation failure\n *\n * @example\n * ```ts\n * createInvalidError(\"option 'grace'\", -1).code // 'invalid'\n * ```\n */\nexport function createInvalidError(subject: string, value: unknown): ProcessError {\n\treturn new ProcessError(`Invalid ${subject}`, { code: 'invalid', context: { value } })\n}\n\n/**\n * Creates the failure raised when a run does not complete successfully and rejection is requested.\n *\n * @remarks\n * The category is `timeout` only when the run's own timeout elapsed; every other failure,\n * including an abort and an output overflow, is a `spawn` failure carrying its\n * {@link ExecuteResult}.\n *\n * @param result - The buffered run outcome that failed\n * @param cause - The underlying host fault, when one ended the run\n * @returns A typed run failure carrying its {@link ExecuteResult}\n */\nexport function createExecuteError(result: ExecuteResult, cause?: unknown): ProcessError {\n\tlet reason = `exited with code ${String(result.code)}`\n\tif (result.signal !== null) reason = `was killed by ${result.signal}`\n\tif (result.truncated) reason = 'exceeded its output limit'\n\tif (result.aborted) reason = 'was aborted'\n\tif (result.expired) reason = 'timed out'\n\treturn new ProcessError(`Command '${result.command}' ${reason}`, {\n\t\tcode: result.expired ? 'timeout' : 'spawn',\n\t\tcontext: { command: result.command, code: result.code, signal: result.signal },\n\t\t...(cause === undefined ? {} : { cause }),\n\t\tresult,\n\t})\n}\n"],"mappings":";;;;AACA,IAAa,gBAAgB;;AAG7B,IAAa,uBAAuB;;AAGpC,IAAa,mBAAmB;;AAGhC,IAAa,kBAAkB;;AAG/B,IAAa,iBAAiB;;AAG9B,IAAa,gBAAgB;;AAG7B,IAAa,kBAAkB;;AAG/B,IAAa,sBAAsB,OAAO,OAAO;CAChD;CACA;CACA;CACA;CACA;AACD,CAAU;;;;AClBV,IAAa,eAAb,cAAkC,MAAM;CACvC,OAAyB;CACzB;CACA;;CAEA;;;;;;;CAQA,YAAY,SAAiB,SAA8B;EAC1D,MAAM,QAAQ,QAAQ;EACtB,MAAM,SAAS,UAAU,KAAA,IAAY,KAAA,IAAY,EAAE,MAAM,CAAC;EAC1D,OAAO,eAAe,MAAM,OAAO,IAAI,0BAA0B,GAAG,EAAE,OAAO,KAAK,CAAC;EACnF,KAAK,OAAO,QAAQ;EACpB,IAAI,QAAQ,YAAY,KAAA,GAAW,KAAK,UAAU,QAAQ;EAC1D,IAAI,QAAQ,WAAW,KAAA,GAAW,KAAK,SAAS,QAAQ;CACzD;AACD;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,eAAe,OAAuC;CACrE,IAAI,EAAA,GAAC,oBAAA,QAAA,CAAQ,KAAK,GAAG,OAAO;CAC5B,QAAA,GAAO,oBAAA,MAAA,OAAY;EAClB,IAAI,OAAO,eAAe,KAAK,MAAM,MAAM,WAAW,OAAO;EAC7D,IAAI,MAAM,SAAS,kBAAkB,EAAE,UAAU,QAAQ,OAAO;EAKhE,IAJmB,OAAO,yBACzB,OACA,OAAO,IAAI,0BAA0B,CAElC,CAAA,EAAY,UAAU,MAAM,OAAO;EACvC,MAAM,OAAgB,MAAM;EAC5B,OAAO,oBAAoB,MAAM,aAAa,aAAa,IAAI;CAChE,CAAC;AACF;;;;;;;AAQA,SAAgB,qBAAqB,IAA0B;CAC9D,OAAO,IAAI,aAAa,YAAY,GAAG,oBAAoB;EAC1D,MAAM;EACN,SAAS,EAAE,GAAG;CACf,CAAC;AACF;;;;;;;AAQA,SAAgB,oBAAoB,IAA0B;CAC7D,OAAO,IAAI,aAAa,YAAY,GAAG,6CAA6C;EACnF,MAAM;EACN,SAAS,EAAE,GAAG;CACf,CAAC;AACF;;;;;;;;;;;;;AAcA,SAAgB,mBAAmB,SAAiB,OAA8B;CACjF,OAAO,IAAI,aAAa,WAAW,WAAW;EAAE,MAAM;EAAW,SAAS,EAAE,MAAM;CAAE,CAAC;AACtF;;;;;;;;;;;;;AAcA,SAAgB,mBAAmB,QAAuB,OAA+B;CACxF,IAAI,SAAS,oBAAoB,OAAO,OAAO,IAAI;CACnD,IAAI,OAAO,WAAW,MAAM,SAAS,iBAAiB,OAAO;CAC7D,IAAI,OAAO,WAAW,SAAS;CAC/B,IAAI,OAAO,SAAS,SAAS;CAC7B,IAAI,OAAO,SAAS,SAAS;CAC7B,OAAO,IAAI,aAAa,YAAY,OAAO,QAAQ,IAAI,UAAU;EAChE,MAAM,OAAO,UAAU,YAAY;EACnC,SAAS;GAAE,SAAS,OAAO;GAAS,MAAM,OAAO;GAAM,QAAQ,OAAO;EAAO;EAC7E,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;EACvC;CACD,CAAC;AACF"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts"],"sourcesContent":["/** The default cooperative POSIX window in milliseconds between `SIGTERM` and `SIGKILL` during termination. */\nexport const PROCESS_GRACE = 5_000\n\n/** The window in milliseconds a termination waits for the child's native exit after the final kill. */\nexport const PROCESS_CONFIRMATION = 5_000\n\n/** The default maximum retained stderr tail in bytes for a supervised {@link ProcessInterface}. */\nexport const PROCESS_EVIDENCE = 2_048\n\n/** The default soft high-water mark in bytes for a supervised {@link ProcessInterface} line backlog. */\nexport const PROCESS_BACKLOG = 10_485_760\n\n/** The default maximum captured bytes for a one-shot run's stdout and stderr, each. */\nexport const PROCESS_OUTPUT = 10_485_760\n\n/** The largest timer delay in milliseconds the host schedules without truncating it to one. */\nexport const PROCESS_TIMER = 2_147_483_647\n\n/** The executable extensions a Windows lookup applies when the environment declares no `PATHEXT`. */\nexport const PROCESS_PATHEXT = '.COM;.EXE;.BAT;.CMD'\n\n/** The machine-readable failure categories a {@link ProcessError} carries, in declaration order. */\nexport const PROCESS_ERROR_CODES = Object.freeze([\n\t'spawn',\n\t'timeout',\n\t'input',\n\t'duplicate',\n\t'protocol',\n\t'invalid',\n] as const)\n","import type {\n\tProcessErrorCode,\n\tProcessErrorContext,\n\tProcessErrorOptions,\n\tExecuteResult,\n} from './types.js'\nimport { holds, isError } from '@orkestrel/contract'\nimport { PROCESS_ERROR_CODES } from './constants.js'\n\n/** A child-process failure with a stable machine-readable category. */\nexport class ProcessError extends Error {\n\toverride readonly name = 'ProcessError'\n\treadonly code: ProcessErrorCode\n\treadonly context?: ProcessErrorContext\n\t/** The buffered run outcome, present when a one-shot run produced the failure. */\n\treadonly result?: ExecuteResult\n\n\t/**\n\t * Create a process error.\n\t *\n\t * @param message - Human-readable failure description\n\t * @param options - Machine-readable category, optional context, optional cause, and optional run result\n\t */\n\tconstructor(message: string, options: ProcessErrorOptions) {\n\t\tconst cause = options.cause\n\t\tsuper(message, cause === undefined ? undefined : { cause })\n\t\tObject.defineProperty(this, Symbol.for('@orkestrel/process.error'), { value: true })\n\t\tthis.code = options.code\n\t\tif (options.context !== undefined) this.context = options.context\n\t\tif (options.result !== undefined) this.result = options.result\n\t}\n}\n\n/**\n * Checks whether an unknown value is a {@link ProcessError}.\n *\n * @remarks\n * Recognition combines a global own-property brand with the native `Error` base, the subclass\n * prototype, the fixed name, and a code {@link PROCESS_ERROR_CODES} declares. The brand is\n * recognized across duplicate installations and ESM/CommonJS module copies at 0.0.4 or later. A copy\n * earlier than 0.0.4 stamps no brand, so an error it throws stays outside the type, and so does a\n * plain or property-only lookalike.\n *\n * @param value - The value to inspect\n * @returns True only for a `ProcessError` instance; false otherwise\n *\n * @example\n * ```ts\n * isProcessError(new ProcessError('spawn failed', { code: 'spawn' })) // true\n * isProcessError(new Error('spawn failed')) // false\n * ```\n */\nexport function isProcessError(value: unknown): value is ProcessError {\n\tif (!isError(value)) return false\n\treturn holds(() => {\n\t\tif (Object.getPrototypeOf(value) === Error.prototype) return false\n\t\tif (value.name !== 'ProcessError' || !('code' in value)) return false\n\t\tconst descriptor = Object.getOwnPropertyDescriptor(\n\t\t\tvalue,\n\t\t\tSymbol.for('@orkestrel/process.error'),\n\t\t)\n\t\tif (descriptor?.value !== true) return false\n\t\tconst code: unknown = value.code\n\t\treturn PROCESS_ERROR_CODES.some((declared) => declared === code)\n\t})\n}\n\n/**\n * Creates the failure raised when a manager launch reuses a live id.\n *\n * @param id - The id already occupied by a live child\n * @returns A typed duplicate-id failure\n */\nexport function createDuplicateError(id: string): ProcessError {\n\treturn new ProcessError(`Process '${id}' is already live`, {\n\t\tcode: 'duplicate',\n\t\tcontext: { id },\n\t})\n}\n\n/**\n * Creates the failure raised when a launch is attempted on a registry that is being destroyed.\n *\n * @param id - The id the refused launch asked for\n * @returns A typed protocol failure\n */\nexport function createProtocolError(id: string): ProcessError {\n\treturn new ProcessError(`Process '${id}' cannot launch: the registry is destroyed`, {\n\t\tcode: 'protocol',\n\t\tcontext: { id },\n\t})\n}\n\n/**\n * Creates the failure raised when a public input is refused before anything is spawned.\n *\n * @param subject - The rejected input named as the caller wrote it, such as `option 'grace'`\n * @param value - The rejected value, carried as `context.value`\n * @returns A typed validation failure\n *\n * @example\n * ```ts\n * createInvalidError(\"option 'grace'\", -1).code // 'invalid'\n * ```\n */\nexport function createInvalidError(subject: string, value: unknown): ProcessError {\n\treturn new ProcessError(`Invalid ${subject}`, { code: 'invalid', context: { value } })\n}\n\n/**\n * Creates the failure raised when a run does not complete successfully and rejection is requested.\n *\n * @remarks\n * The category is `timeout` only when the run's own timeout elapsed; every other failure,\n * including an abort and an output overflow, is a `spawn` failure carrying its\n * {@link ExecuteResult}.\n *\n * @param result - The buffered run outcome that failed\n * @param cause - The underlying host fault, when one ended the run\n * @returns A typed run failure carrying its {@link ExecuteResult}\n */\nexport function createExecuteError(result: ExecuteResult, cause?: unknown): ProcessError {\n\tlet reason = `exited with code ${String(result.code)}`\n\tif (result.signal !== null) reason = `was killed by ${result.signal}`\n\tif (result.truncated) reason = 'exceeded its output limit'\n\tif (result.aborted) reason = 'was aborted'\n\tif (result.expired) reason = 'timed out'\n\treturn new ProcessError(`Command '${result.command}' ${reason}`, {\n\t\tcode: result.expired ? 'timeout' : 'spawn',\n\t\tcontext: { command: result.command, code: result.code, signal: result.signal },\n\t\t...(cause === undefined ? {} : { cause }),\n\t\tresult,\n\t})\n}\n"],"mappings":";;;;AACA,IAAa,gBAAgB;;AAG7B,IAAa,uBAAuB;;AAGpC,IAAa,mBAAmB;;AAGhC,IAAa,kBAAkB;;AAG/B,IAAa,iBAAiB;;AAG9B,IAAa,gBAAgB;;AAG7B,IAAa,kBAAkB;;AAG/B,IAAa,sBAAsB,OAAO,OAAO;CAChD;CACA;CACA;CACA;CACA;CACA;AACD,CAAU;;;;ACnBV,IAAa,eAAb,cAAkC,MAAM;CACvC,OAAyB;CACzB;CACA;;CAEA;;;;;;;CAQA,YAAY,SAAiB,SAA8B;EAC1D,MAAM,QAAQ,QAAQ;EACtB,MAAM,SAAS,UAAU,KAAA,IAAY,KAAA,IAAY,EAAE,MAAM,CAAC;EAC1D,OAAO,eAAe,MAAM,OAAO,IAAI,0BAA0B,GAAG,EAAE,OAAO,KAAK,CAAC;EACnF,KAAK,OAAO,QAAQ;EACpB,IAAI,QAAQ,YAAY,KAAA,GAAW,KAAK,UAAU,QAAQ;EAC1D,IAAI,QAAQ,WAAW,KAAA,GAAW,KAAK,SAAS,QAAQ;CACzD;AACD;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,eAAe,OAAuC;CACrE,IAAI,EAAA,GAAC,oBAAA,QAAA,CAAQ,KAAK,GAAG,OAAO;CAC5B,QAAA,GAAO,oBAAA,MAAA,OAAY;EAClB,IAAI,OAAO,eAAe,KAAK,MAAM,MAAM,WAAW,OAAO;EAC7D,IAAI,MAAM,SAAS,kBAAkB,EAAE,UAAU,QAAQ,OAAO;EAKhE,IAJmB,OAAO,yBACzB,OACA,OAAO,IAAI,0BAA0B,CAElC,CAAA,EAAY,UAAU,MAAM,OAAO;EACvC,MAAM,OAAgB,MAAM;EAC5B,OAAO,oBAAoB,MAAM,aAAa,aAAa,IAAI;CAChE,CAAC;AACF;;;;;;;AAQA,SAAgB,qBAAqB,IAA0B;CAC9D,OAAO,IAAI,aAAa,YAAY,GAAG,oBAAoB;EAC1D,MAAM;EACN,SAAS,EAAE,GAAG;CACf,CAAC;AACF;;;;;;;AAQA,SAAgB,oBAAoB,IAA0B;CAC7D,OAAO,IAAI,aAAa,YAAY,GAAG,6CAA6C;EACnF,MAAM;EACN,SAAS,EAAE,GAAG;CACf,CAAC;AACF;;;;;;;;;;;;;AAcA,SAAgB,mBAAmB,SAAiB,OAA8B;CACjF,OAAO,IAAI,aAAa,WAAW,WAAW;EAAE,MAAM;EAAW,SAAS,EAAE,MAAM;CAAE,CAAC;AACtF;;;;;;;;;;;;;AAcA,SAAgB,mBAAmB,QAAuB,OAA+B;CACxF,IAAI,SAAS,oBAAoB,OAAO,OAAO,IAAI;CACnD,IAAI,OAAO,WAAW,MAAM,SAAS,iBAAiB,OAAO;CAC7D,IAAI,OAAO,WAAW,SAAS;CAC/B,IAAI,OAAO,SAAS,SAAS;CAC7B,IAAI,OAAO,SAAS,SAAS;CAC7B,OAAO,IAAI,aAAa,YAAY,OAAO,QAAQ,IAAI,UAAU;EAChE,MAAM,OAAO,UAAU,YAAY;EACnC,SAAS;GAAE,SAAS,OAAO;GAAS,MAAM,OAAO;GAAM,QAAQ,OAAO;EAAO;EAC7E,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;EACvC;CACD,CAAC;AACF"}
@@ -139,13 +139,15 @@ export declare interface ExecuteOptions {
139
139
  * The settled outcome of a one-shot run: the buffered output and the terminal state.
140
140
  *
141
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`.
142
+ * `failed` is `true` when the run timed out, was aborted, ended on a host fault, was ended by a
143
+ * signal, or exited with a code other than `0`. `expired` and `aborted` name the ways the run ended
144
+ * the child rather than the child ending itself, and only the earliest observed is recorded.
145
+ * `truncated` reports that a captured stream omitted output because it exceeded `limit`, which
146
+ * fails a synchronous run and does not fail an asynchronous one. `ProcessInterface` carries the
147
+ * same name for the same fact against a supervised child's retention bounds. For a `strict: false`
148
+ * caller, `failed: true` with `expired`, `aborted`, and `truncated` false, `code: 0`, and
149
+ * `signal: null` is the residual signature that a host fault ended the run. A spawn fault reports
150
+ * the host's negative errno for `execute`. A spawn fault reports `null` for `executeSync`.
149
151
  */
150
152
  export declare interface ExecuteResult {
151
153
  /** The command line that was run, for diagnostics. */
@@ -221,7 +223,7 @@ export declare const PROCESS_BACKLOG = 10485760;
221
223
  export declare const PROCESS_CONFIRMATION = 5000;
222
224
 
223
225
  /** 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"];
226
+ export declare const PROCESS_ERROR_CODES: readonly ["spawn", "timeout", "input", "duplicate", "protocol", "invalid"];
225
227
 
226
228
  /** The default maximum retained stderr tail in bytes for a supervised {@link ProcessInterface}. */
227
229
  export declare const PROCESS_EVIDENCE = 2048;
@@ -330,14 +332,18 @@ export declare interface ProcessErrorOptions {
330
332
  *
331
333
  * @remarks
332
334
  * Declared as a `type` alias so it satisfies the emitter's `EventMap` constraint structurally. The
333
- * `error` event carries a child fault — a failure to spawn or a process-level error. It is distinct
334
- * from the `error` handler in {@link ProcessOptions}: a listener throw is isolated by the emitter and
335
+ * `error` event carries a child fault — a failure to spawn, a process-level error, or a
336
+ * host-reported standard-input channel fault after the constructor input phase. A fault arising
337
+ * from the constructor-supplied `input` write or its closing `end` stays quiet because the package
338
+ * initiated that sequence. A surfaced standard-input fault is wrapped in a
339
+ * {@link ProcessError} coded `protocol` with the host fault as its cause. The event is distinct from
340
+ * the `error` handler in {@link ProcessOptions}: a listener throw is isolated by the emitter and
335
341
  * routed to that handler, never emitted as this `error` event.
336
342
  */
337
343
  export declare type ProcessEventMap = {
338
344
  /** A decoded standard-error chunk arrived. */
339
345
  readonly stderr: readonly [chunk: string];
340
- /** The child emitted an error — a spawn fault or process-level failure — carrying its cause. */
346
+ /** The child or its open standard-input channel reported a fault, carrying the host cause directly or through a `protocol` {@link ProcessError}. */
341
347
  readonly error: readonly [error: unknown];
342
348
  /** The child settled — its terminal state, delivered once. */
343
349
  readonly exit: readonly [exit: ProcessExit];
@@ -387,7 +393,16 @@ export declare interface ProcessInterface {
387
393
  readonly signal: string | null;
388
394
  /** The typed lifecycle observation surface. */
389
395
  readonly emitter: EmitterInterface<ProcessEventMap>;
390
- /** The captured stdout lines, in arrival order, for one consumer, ending when the child's stdout closes. */
396
+ /**
397
+ * The captured stdout lines, in arrival order, for one consumer, ending when the child's stdout closes.
398
+ *
399
+ * @remarks
400
+ * A line feed, a CRLF pair, and a bare carriage return each terminate a line, and a CRLF split
401
+ * across delivered chunks joins as one break. A child that redraws a progress bar with a carriage
402
+ * return therefore yields one line per redraw, and consecutive carriage returns yield an empty
403
+ * line between them. A final line written with no trailing terminator is delivered when stdout
404
+ * closes.
405
+ */
391
406
  readonly lines: AsyncIterable<string>;
392
407
  /** The decoded byte-bounded stderr tail. */
393
408
  readonly evidence: string;
@@ -399,13 +414,18 @@ export declare interface ProcessInterface {
399
414
  * Write one line to the open standard-input channel.
400
415
  *
401
416
  * @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`.
417
+ * Never rejects. `true` means the host accepted the bytes without reporting a fault; it does not
418
+ * prove that the child read them. An ordinary write settles when the kernel accepts it. Only a
419
+ * full pipe can hold the write unconfirmed. The `delivery` option can bound that wait, and every
420
+ * terminal teardown path settles pending writes. On Windows 11 with Node v24.18.1, measured on
421
+ * 2026-08-21, a child that closes its own file descriptor 0 can leave the parent pipe writable:
422
+ * the write can settle `true` without a callback error or a stream error while the child remains
423
+ * alive. After `stop` or `destroy` begins, a later call settles `false`. Version 0.0.4 could settle
424
+ * that call `true` before teardown destroyed the pipe; returning `false` avoids claiming delivery
425
+ * for bytes the package is about to discard.
406
426
  *
407
427
  * @param text - The line text without its trailing newline
408
- * @returns True when the line reached the host without error; false when the channel was closed, destroyed, ended, or the write failed
428
+ * @returns True when the host accepted the bytes without reporting a fault; false when the channel was closed, destroyed, ended, failed, or remained unconfirmed through `delivery`
409
429
  */
410
430
  send(text: string): Promise<boolean>;
411
431
  /**
@@ -569,6 +589,8 @@ export declare interface ProcessOptions {
569
589
  readonly evidence?: number;
570
590
  /** Soft high-water mark in bytes for the unconsumed `lines` backlog; termination retains at most twice this value. Default: {@link PROCESS_BACKLOG}. */
571
591
  readonly backlog?: number;
592
+ /** Milliseconds an unconfirmed {@link ProcessInterface.send} waits before resolving `false`. `0` or omitted disables the bound. */
593
+ readonly delivery?: number;
572
594
  /** If `true`, stdin stays open for {@link ProcessInterface.send}; if `false` or omitted, stdin closes after any initial `input`. */
573
595
  readonly writable?: boolean;
574
596
  /** Aborting this signal terminates the child through the same bounded `stop`. */
@@ -139,13 +139,15 @@ export declare interface ExecuteOptions {
139
139
  * The settled outcome of a one-shot run: the buffered output and the terminal state.
140
140
  *
141
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`.
142
+ * `failed` is `true` when the run timed out, was aborted, ended on a host fault, was ended by a
143
+ * signal, or exited with a code other than `0`. `expired` and `aborted` name the ways the run ended
144
+ * the child rather than the child ending itself, and only the earliest observed is recorded.
145
+ * `truncated` reports that a captured stream omitted output because it exceeded `limit`, which
146
+ * fails a synchronous run and does not fail an asynchronous one. `ProcessInterface` carries the
147
+ * same name for the same fact against a supervised child's retention bounds. For a `strict: false`
148
+ * caller, `failed: true` with `expired`, `aborted`, and `truncated` false, `code: 0`, and
149
+ * `signal: null` is the residual signature that a host fault ended the run. A spawn fault reports
150
+ * the host's negative errno for `execute`. A spawn fault reports `null` for `executeSync`.
149
151
  */
150
152
  export declare interface ExecuteResult {
151
153
  /** The command line that was run, for diagnostics. */
@@ -221,7 +223,7 @@ export declare const PROCESS_BACKLOG = 10485760;
221
223
  export declare const PROCESS_CONFIRMATION = 5000;
222
224
 
223
225
  /** 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"];
226
+ export declare const PROCESS_ERROR_CODES: readonly ["spawn", "timeout", "input", "duplicate", "protocol", "invalid"];
225
227
 
226
228
  /** The default maximum retained stderr tail in bytes for a supervised {@link ProcessInterface}. */
227
229
  export declare const PROCESS_EVIDENCE = 2048;
@@ -330,14 +332,18 @@ export declare interface ProcessErrorOptions {
330
332
  *
331
333
  * @remarks
332
334
  * Declared as a `type` alias so it satisfies the emitter's `EventMap` constraint structurally. The
333
- * `error` event carries a child fault — a failure to spawn or a process-level error. It is distinct
334
- * from the `error` handler in {@link ProcessOptions}: a listener throw is isolated by the emitter and
335
+ * `error` event carries a child fault — a failure to spawn, a process-level error, or a
336
+ * host-reported standard-input channel fault after the constructor input phase. A fault arising
337
+ * from the constructor-supplied `input` write or its closing `end` stays quiet because the package
338
+ * initiated that sequence. A surfaced standard-input fault is wrapped in a
339
+ * {@link ProcessError} coded `protocol` with the host fault as its cause. The event is distinct from
340
+ * the `error` handler in {@link ProcessOptions}: a listener throw is isolated by the emitter and
335
341
  * routed to that handler, never emitted as this `error` event.
336
342
  */
337
343
  export declare type ProcessEventMap = {
338
344
  /** A decoded standard-error chunk arrived. */
339
345
  readonly stderr: readonly [chunk: string];
340
- /** The child emitted an error — a spawn fault or process-level failure — carrying its cause. */
346
+ /** The child or its open standard-input channel reported a fault, carrying the host cause directly or through a `protocol` {@link ProcessError}. */
341
347
  readonly error: readonly [error: unknown];
342
348
  /** The child settled — its terminal state, delivered once. */
343
349
  readonly exit: readonly [exit: ProcessExit];
@@ -387,7 +393,16 @@ export declare interface ProcessInterface {
387
393
  readonly signal: string | null;
388
394
  /** The typed lifecycle observation surface. */
389
395
  readonly emitter: EmitterInterface<ProcessEventMap>;
390
- /** The captured stdout lines, in arrival order, for one consumer, ending when the child's stdout closes. */
396
+ /**
397
+ * The captured stdout lines, in arrival order, for one consumer, ending when the child's stdout closes.
398
+ *
399
+ * @remarks
400
+ * A line feed, a CRLF pair, and a bare carriage return each terminate a line, and a CRLF split
401
+ * across delivered chunks joins as one break. A child that redraws a progress bar with a carriage
402
+ * return therefore yields one line per redraw, and consecutive carriage returns yield an empty
403
+ * line between them. A final line written with no trailing terminator is delivered when stdout
404
+ * closes.
405
+ */
391
406
  readonly lines: AsyncIterable<string>;
392
407
  /** The decoded byte-bounded stderr tail. */
393
408
  readonly evidence: string;
@@ -399,13 +414,18 @@ export declare interface ProcessInterface {
399
414
  * Write one line to the open standard-input channel.
400
415
  *
401
416
  * @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`.
417
+ * Never rejects. `true` means the host accepted the bytes without reporting a fault; it does not
418
+ * prove that the child read them. An ordinary write settles when the kernel accepts it. Only a
419
+ * full pipe can hold the write unconfirmed. The `delivery` option can bound that wait, and every
420
+ * terminal teardown path settles pending writes. On Windows 11 with Node v24.18.1, measured on
421
+ * 2026-08-21, a child that closes its own file descriptor 0 can leave the parent pipe writable:
422
+ * the write can settle `true` without a callback error or a stream error while the child remains
423
+ * alive. After `stop` or `destroy` begins, a later call settles `false`. Version 0.0.4 could settle
424
+ * that call `true` before teardown destroyed the pipe; returning `false` avoids claiming delivery
425
+ * for bytes the package is about to discard.
406
426
  *
407
427
  * @param text - The line text without its trailing newline
408
- * @returns True when the line reached the host without error; false when the channel was closed, destroyed, ended, or the write failed
428
+ * @returns True when the host accepted the bytes without reporting a fault; false when the channel was closed, destroyed, ended, failed, or remained unconfirmed through `delivery`
409
429
  */
410
430
  send(text: string): Promise<boolean>;
411
431
  /**
@@ -569,6 +589,8 @@ export declare interface ProcessOptions {
569
589
  readonly evidence?: number;
570
590
  /** Soft high-water mark in bytes for the unconsumed `lines` backlog; termination retains at most twice this value. Default: {@link PROCESS_BACKLOG}. */
571
591
  readonly backlog?: number;
592
+ /** Milliseconds an unconfirmed {@link ProcessInterface.send} waits before resolving `false`. `0` or omitted disables the bound. */
593
+ readonly delivery?: number;
572
594
  /** If `true`, stdin stays open for {@link ProcessInterface.send}; if `false` or omitted, stdin closes after any initial `input`. */
573
595
  readonly writable?: boolean;
574
596
  /** Aborting this signal terminates the child through the same bounded `stop`. */
@@ -18,6 +18,7 @@ var PROCESS_PATHEXT = ".COM;.EXE;.BAT;.CMD";
18
18
  var PROCESS_ERROR_CODES = Object.freeze([
19
19
  "spawn",
20
20
  "timeout",
21
+ "input",
21
22
  "duplicate",
22
23
  "protocol",
23
24
  "invalid"
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts"],"sourcesContent":["/** The default cooperative POSIX window in milliseconds between `SIGTERM` and `SIGKILL` during termination. */\nexport const PROCESS_GRACE = 5_000\n\n/** The window in milliseconds a termination waits for the child's native exit after the final kill. */\nexport const PROCESS_CONFIRMATION = 5_000\n\n/** The default maximum retained stderr tail in bytes for a supervised {@link ProcessInterface}. */\nexport const PROCESS_EVIDENCE = 2_048\n\n/** The default soft high-water mark in bytes for a supervised {@link ProcessInterface} line backlog. */\nexport const PROCESS_BACKLOG = 10_485_760\n\n/** The default maximum captured bytes for a one-shot run's stdout and stderr, each. */\nexport const PROCESS_OUTPUT = 10_485_760\n\n/** The largest timer delay in milliseconds the host schedules without truncating it to one. */\nexport const PROCESS_TIMER = 2_147_483_647\n\n/** The executable extensions a Windows lookup applies when the environment declares no `PATHEXT`. */\nexport const PROCESS_PATHEXT = '.COM;.EXE;.BAT;.CMD'\n\n/** The machine-readable failure categories a {@link ProcessError} carries, in declaration order. */\nexport const PROCESS_ERROR_CODES = Object.freeze([\n\t'spawn',\n\t'timeout',\n\t'duplicate',\n\t'protocol',\n\t'invalid',\n] as const)\n","import type {\n\tProcessErrorCode,\n\tProcessErrorContext,\n\tProcessErrorOptions,\n\tExecuteResult,\n} from './types.js'\nimport { holds, isError } from '@orkestrel/contract'\nimport { PROCESS_ERROR_CODES } from './constants.js'\n\n/** A child-process failure with a stable machine-readable category. */\nexport class ProcessError extends Error {\n\toverride readonly name = 'ProcessError'\n\treadonly code: ProcessErrorCode\n\treadonly context?: ProcessErrorContext\n\t/** The buffered run outcome, present when a one-shot run produced the failure. */\n\treadonly result?: ExecuteResult\n\n\t/**\n\t * Create a process error.\n\t *\n\t * @param message - Human-readable failure description\n\t * @param options - Machine-readable category, optional context, optional cause, and optional run result\n\t */\n\tconstructor(message: string, options: ProcessErrorOptions) {\n\t\tconst cause = options.cause\n\t\tsuper(message, cause === undefined ? undefined : { cause })\n\t\tObject.defineProperty(this, Symbol.for('@orkestrel/process.error'), { value: true })\n\t\tthis.code = options.code\n\t\tif (options.context !== undefined) this.context = options.context\n\t\tif (options.result !== undefined) this.result = options.result\n\t}\n}\n\n/**\n * Checks whether an unknown value is a {@link ProcessError}.\n *\n * @remarks\n * Recognition combines a global own-property brand with the native `Error` base, the subclass\n * prototype, the fixed name, and a code {@link PROCESS_ERROR_CODES} declares. The brand is\n * recognized across duplicate installations and ESM/CommonJS module copies at 0.0.4 or later. A copy\n * earlier than 0.0.4 stamps no brand, so an error it throws stays outside the type, and so does a\n * plain or property-only lookalike.\n *\n * @param value - The value to inspect\n * @returns True only for a `ProcessError` instance; false otherwise\n *\n * @example\n * ```ts\n * isProcessError(new ProcessError('spawn failed', { code: 'spawn' })) // true\n * isProcessError(new Error('spawn failed')) // false\n * ```\n */\nexport function isProcessError(value: unknown): value is ProcessError {\n\tif (!isError(value)) return false\n\treturn holds(() => {\n\t\tif (Object.getPrototypeOf(value) === Error.prototype) return false\n\t\tif (value.name !== 'ProcessError' || !('code' in value)) return false\n\t\tconst descriptor = Object.getOwnPropertyDescriptor(\n\t\t\tvalue,\n\t\t\tSymbol.for('@orkestrel/process.error'),\n\t\t)\n\t\tif (descriptor?.value !== true) return false\n\t\tconst code: unknown = value.code\n\t\treturn PROCESS_ERROR_CODES.some((declared) => declared === code)\n\t})\n}\n\n/**\n * Creates the failure raised when a manager launch reuses a live id.\n *\n * @param id - The id already occupied by a live child\n * @returns A typed duplicate-id failure\n */\nexport function createDuplicateError(id: string): ProcessError {\n\treturn new ProcessError(`Process '${id}' is already live`, {\n\t\tcode: 'duplicate',\n\t\tcontext: { id },\n\t})\n}\n\n/**\n * Creates the failure raised when a launch is attempted on a registry that is being destroyed.\n *\n * @param id - The id the refused launch asked for\n * @returns A typed protocol failure\n */\nexport function createProtocolError(id: string): ProcessError {\n\treturn new ProcessError(`Process '${id}' cannot launch: the registry is destroyed`, {\n\t\tcode: 'protocol',\n\t\tcontext: { id },\n\t})\n}\n\n/**\n * Creates the failure raised when a public input is refused before anything is spawned.\n *\n * @param subject - The rejected input named as the caller wrote it, such as `option 'grace'`\n * @param value - The rejected value, carried as `context.value`\n * @returns A typed validation failure\n *\n * @example\n * ```ts\n * createInvalidError(\"option 'grace'\", -1).code // 'invalid'\n * ```\n */\nexport function createInvalidError(subject: string, value: unknown): ProcessError {\n\treturn new ProcessError(`Invalid ${subject}`, { code: 'invalid', context: { value } })\n}\n\n/**\n * Creates the failure raised when a run does not complete successfully and rejection is requested.\n *\n * @remarks\n * The category is `timeout` only when the run's own timeout elapsed; every other failure,\n * including an abort and an output overflow, is a `spawn` failure carrying its\n * {@link ExecuteResult}.\n *\n * @param result - The buffered run outcome that failed\n * @param cause - The underlying host fault, when one ended the run\n * @returns A typed run failure carrying its {@link ExecuteResult}\n */\nexport function createExecuteError(result: ExecuteResult, cause?: unknown): ProcessError {\n\tlet reason = `exited with code ${String(result.code)}`\n\tif (result.signal !== null) reason = `was killed by ${result.signal}`\n\tif (result.truncated) reason = 'exceeded its output limit'\n\tif (result.aborted) reason = 'was aborted'\n\tif (result.expired) reason = 'timed out'\n\treturn new ProcessError(`Command '${result.command}' ${reason}`, {\n\t\tcode: result.expired ? 'timeout' : 'spawn',\n\t\tcontext: { command: result.command, code: result.code, signal: result.signal },\n\t\t...(cause === undefined ? {} : { cause }),\n\t\tresult,\n\t})\n}\n"],"mappings":";;;AACA,IAAa,gBAAgB;;AAG7B,IAAa,uBAAuB;;AAGpC,IAAa,mBAAmB;;AAGhC,IAAa,kBAAkB;;AAG/B,IAAa,iBAAiB;;AAG9B,IAAa,gBAAgB;;AAG7B,IAAa,kBAAkB;;AAG/B,IAAa,sBAAsB,OAAO,OAAO;CAChD;CACA;CACA;CACA;CACA;AACD,CAAU;;;;AClBV,IAAa,eAAb,cAAkC,MAAM;CACvC,OAAyB;CACzB;CACA;;CAEA;;;;;;;CAQA,YAAY,SAAiB,SAA8B;EAC1D,MAAM,QAAQ,QAAQ;EACtB,MAAM,SAAS,UAAU,KAAA,IAAY,KAAA,IAAY,EAAE,MAAM,CAAC;EAC1D,OAAO,eAAe,MAAM,OAAO,IAAI,0BAA0B,GAAG,EAAE,OAAO,KAAK,CAAC;EACnF,KAAK,OAAO,QAAQ;EACpB,IAAI,QAAQ,YAAY,KAAA,GAAW,KAAK,UAAU,QAAQ;EAC1D,IAAI,QAAQ,WAAW,KAAA,GAAW,KAAK,SAAS,QAAQ;CACzD;AACD;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,eAAe,OAAuC;CACrE,IAAI,CAAC,QAAQ,KAAK,GAAG,OAAO;CAC5B,OAAO,YAAY;EAClB,IAAI,OAAO,eAAe,KAAK,MAAM,MAAM,WAAW,OAAO;EAC7D,IAAI,MAAM,SAAS,kBAAkB,EAAE,UAAU,QAAQ,OAAO;EAKhE,IAJmB,OAAO,yBACzB,OACA,OAAO,IAAI,0BAA0B,CAElC,CAAA,EAAY,UAAU,MAAM,OAAO;EACvC,MAAM,OAAgB,MAAM;EAC5B,OAAO,oBAAoB,MAAM,aAAa,aAAa,IAAI;CAChE,CAAC;AACF;;;;;;;AAQA,SAAgB,qBAAqB,IAA0B;CAC9D,OAAO,IAAI,aAAa,YAAY,GAAG,oBAAoB;EAC1D,MAAM;EACN,SAAS,EAAE,GAAG;CACf,CAAC;AACF;;;;;;;AAQA,SAAgB,oBAAoB,IAA0B;CAC7D,OAAO,IAAI,aAAa,YAAY,GAAG,6CAA6C;EACnF,MAAM;EACN,SAAS,EAAE,GAAG;CACf,CAAC;AACF;;;;;;;;;;;;;AAcA,SAAgB,mBAAmB,SAAiB,OAA8B;CACjF,OAAO,IAAI,aAAa,WAAW,WAAW;EAAE,MAAM;EAAW,SAAS,EAAE,MAAM;CAAE,CAAC;AACtF;;;;;;;;;;;;;AAcA,SAAgB,mBAAmB,QAAuB,OAA+B;CACxF,IAAI,SAAS,oBAAoB,OAAO,OAAO,IAAI;CACnD,IAAI,OAAO,WAAW,MAAM,SAAS,iBAAiB,OAAO;CAC7D,IAAI,OAAO,WAAW,SAAS;CAC/B,IAAI,OAAO,SAAS,SAAS;CAC7B,IAAI,OAAO,SAAS,SAAS;CAC7B,OAAO,IAAI,aAAa,YAAY,OAAO,QAAQ,IAAI,UAAU;EAChE,MAAM,OAAO,UAAU,YAAY;EACnC,SAAS;GAAE,SAAS,OAAO;GAAS,MAAM,OAAO;GAAM,QAAQ,OAAO;EAAO;EAC7E,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;EACvC;CACD,CAAC;AACF"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts"],"sourcesContent":["/** The default cooperative POSIX window in milliseconds between `SIGTERM` and `SIGKILL` during termination. */\nexport const PROCESS_GRACE = 5_000\n\n/** The window in milliseconds a termination waits for the child's native exit after the final kill. */\nexport const PROCESS_CONFIRMATION = 5_000\n\n/** The default maximum retained stderr tail in bytes for a supervised {@link ProcessInterface}. */\nexport const PROCESS_EVIDENCE = 2_048\n\n/** The default soft high-water mark in bytes for a supervised {@link ProcessInterface} line backlog. */\nexport const PROCESS_BACKLOG = 10_485_760\n\n/** The default maximum captured bytes for a one-shot run's stdout and stderr, each. */\nexport const PROCESS_OUTPUT = 10_485_760\n\n/** The largest timer delay in milliseconds the host schedules without truncating it to one. */\nexport const PROCESS_TIMER = 2_147_483_647\n\n/** The executable extensions a Windows lookup applies when the environment declares no `PATHEXT`. */\nexport const PROCESS_PATHEXT = '.COM;.EXE;.BAT;.CMD'\n\n/** The machine-readable failure categories a {@link ProcessError} carries, in declaration order. */\nexport const PROCESS_ERROR_CODES = Object.freeze([\n\t'spawn',\n\t'timeout',\n\t'input',\n\t'duplicate',\n\t'protocol',\n\t'invalid',\n] as const)\n","import type {\n\tProcessErrorCode,\n\tProcessErrorContext,\n\tProcessErrorOptions,\n\tExecuteResult,\n} from './types.js'\nimport { holds, isError } from '@orkestrel/contract'\nimport { PROCESS_ERROR_CODES } from './constants.js'\n\n/** A child-process failure with a stable machine-readable category. */\nexport class ProcessError extends Error {\n\toverride readonly name = 'ProcessError'\n\treadonly code: ProcessErrorCode\n\treadonly context?: ProcessErrorContext\n\t/** The buffered run outcome, present when a one-shot run produced the failure. */\n\treadonly result?: ExecuteResult\n\n\t/**\n\t * Create a process error.\n\t *\n\t * @param message - Human-readable failure description\n\t * @param options - Machine-readable category, optional context, optional cause, and optional run result\n\t */\n\tconstructor(message: string, options: ProcessErrorOptions) {\n\t\tconst cause = options.cause\n\t\tsuper(message, cause === undefined ? undefined : { cause })\n\t\tObject.defineProperty(this, Symbol.for('@orkestrel/process.error'), { value: true })\n\t\tthis.code = options.code\n\t\tif (options.context !== undefined) this.context = options.context\n\t\tif (options.result !== undefined) this.result = options.result\n\t}\n}\n\n/**\n * Checks whether an unknown value is a {@link ProcessError}.\n *\n * @remarks\n * Recognition combines a global own-property brand with the native `Error` base, the subclass\n * prototype, the fixed name, and a code {@link PROCESS_ERROR_CODES} declares. The brand is\n * recognized across duplicate installations and ESM/CommonJS module copies at 0.0.4 or later. A copy\n * earlier than 0.0.4 stamps no brand, so an error it throws stays outside the type, and so does a\n * plain or property-only lookalike.\n *\n * @param value - The value to inspect\n * @returns True only for a `ProcessError` instance; false otherwise\n *\n * @example\n * ```ts\n * isProcessError(new ProcessError('spawn failed', { code: 'spawn' })) // true\n * isProcessError(new Error('spawn failed')) // false\n * ```\n */\nexport function isProcessError(value: unknown): value is ProcessError {\n\tif (!isError(value)) return false\n\treturn holds(() => {\n\t\tif (Object.getPrototypeOf(value) === Error.prototype) return false\n\t\tif (value.name !== 'ProcessError' || !('code' in value)) return false\n\t\tconst descriptor = Object.getOwnPropertyDescriptor(\n\t\t\tvalue,\n\t\t\tSymbol.for('@orkestrel/process.error'),\n\t\t)\n\t\tif (descriptor?.value !== true) return false\n\t\tconst code: unknown = value.code\n\t\treturn PROCESS_ERROR_CODES.some((declared) => declared === code)\n\t})\n}\n\n/**\n * Creates the failure raised when a manager launch reuses a live id.\n *\n * @param id - The id already occupied by a live child\n * @returns A typed duplicate-id failure\n */\nexport function createDuplicateError(id: string): ProcessError {\n\treturn new ProcessError(`Process '${id}' is already live`, {\n\t\tcode: 'duplicate',\n\t\tcontext: { id },\n\t})\n}\n\n/**\n * Creates the failure raised when a launch is attempted on a registry that is being destroyed.\n *\n * @param id - The id the refused launch asked for\n * @returns A typed protocol failure\n */\nexport function createProtocolError(id: string): ProcessError {\n\treturn new ProcessError(`Process '${id}' cannot launch: the registry is destroyed`, {\n\t\tcode: 'protocol',\n\t\tcontext: { id },\n\t})\n}\n\n/**\n * Creates the failure raised when a public input is refused before anything is spawned.\n *\n * @param subject - The rejected input named as the caller wrote it, such as `option 'grace'`\n * @param value - The rejected value, carried as `context.value`\n * @returns A typed validation failure\n *\n * @example\n * ```ts\n * createInvalidError(\"option 'grace'\", -1).code // 'invalid'\n * ```\n */\nexport function createInvalidError(subject: string, value: unknown): ProcessError {\n\treturn new ProcessError(`Invalid ${subject}`, { code: 'invalid', context: { value } })\n}\n\n/**\n * Creates the failure raised when a run does not complete successfully and rejection is requested.\n *\n * @remarks\n * The category is `timeout` only when the run's own timeout elapsed; every other failure,\n * including an abort and an output overflow, is a `spawn` failure carrying its\n * {@link ExecuteResult}.\n *\n * @param result - The buffered run outcome that failed\n * @param cause - The underlying host fault, when one ended the run\n * @returns A typed run failure carrying its {@link ExecuteResult}\n */\nexport function createExecuteError(result: ExecuteResult, cause?: unknown): ProcessError {\n\tlet reason = `exited with code ${String(result.code)}`\n\tif (result.signal !== null) reason = `was killed by ${result.signal}`\n\tif (result.truncated) reason = 'exceeded its output limit'\n\tif (result.aborted) reason = 'was aborted'\n\tif (result.expired) reason = 'timed out'\n\treturn new ProcessError(`Command '${result.command}' ${reason}`, {\n\t\tcode: result.expired ? 'timeout' : 'spawn',\n\t\tcontext: { command: result.command, code: result.code, signal: result.signal },\n\t\t...(cause === undefined ? {} : { cause }),\n\t\tresult,\n\t})\n}\n"],"mappings":";;;AACA,IAAa,gBAAgB;;AAG7B,IAAa,uBAAuB;;AAGpC,IAAa,mBAAmB;;AAGhC,IAAa,kBAAkB;;AAG/B,IAAa,iBAAiB;;AAG9B,IAAa,gBAAgB;;AAG7B,IAAa,kBAAkB;;AAG/B,IAAa,sBAAsB,OAAO,OAAO;CAChD;CACA;CACA;CACA;CACA;CACA;AACD,CAAU;;;;ACnBV,IAAa,eAAb,cAAkC,MAAM;CACvC,OAAyB;CACzB;CACA;;CAEA;;;;;;;CAQA,YAAY,SAAiB,SAA8B;EAC1D,MAAM,QAAQ,QAAQ;EACtB,MAAM,SAAS,UAAU,KAAA,IAAY,KAAA,IAAY,EAAE,MAAM,CAAC;EAC1D,OAAO,eAAe,MAAM,OAAO,IAAI,0BAA0B,GAAG,EAAE,OAAO,KAAK,CAAC;EACnF,KAAK,OAAO,QAAQ;EACpB,IAAI,QAAQ,YAAY,KAAA,GAAW,KAAK,UAAU,QAAQ;EAC1D,IAAI,QAAQ,WAAW,KAAA,GAAW,KAAK,SAAS,QAAQ;CACzD;AACD;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,eAAe,OAAuC;CACrE,IAAI,CAAC,QAAQ,KAAK,GAAG,OAAO;CAC5B,OAAO,YAAY;EAClB,IAAI,OAAO,eAAe,KAAK,MAAM,MAAM,WAAW,OAAO;EAC7D,IAAI,MAAM,SAAS,kBAAkB,EAAE,UAAU,QAAQ,OAAO;EAKhE,IAJmB,OAAO,yBACzB,OACA,OAAO,IAAI,0BAA0B,CAElC,CAAA,EAAY,UAAU,MAAM,OAAO;EACvC,MAAM,OAAgB,MAAM;EAC5B,OAAO,oBAAoB,MAAM,aAAa,aAAa,IAAI;CAChE,CAAC;AACF;;;;;;;AAQA,SAAgB,qBAAqB,IAA0B;CAC9D,OAAO,IAAI,aAAa,YAAY,GAAG,oBAAoB;EAC1D,MAAM;EACN,SAAS,EAAE,GAAG;CACf,CAAC;AACF;;;;;;;AAQA,SAAgB,oBAAoB,IAA0B;CAC7D,OAAO,IAAI,aAAa,YAAY,GAAG,6CAA6C;EACnF,MAAM;EACN,SAAS,EAAE,GAAG;CACf,CAAC;AACF;;;;;;;;;;;;;AAcA,SAAgB,mBAAmB,SAAiB,OAA8B;CACjF,OAAO,IAAI,aAAa,WAAW,WAAW;EAAE,MAAM;EAAW,SAAS,EAAE,MAAM;CAAE,CAAC;AACtF;;;;;;;;;;;;;AAcA,SAAgB,mBAAmB,QAAuB,OAA+B;CACxF,IAAI,SAAS,oBAAoB,OAAO,OAAO,IAAI;CACnD,IAAI,OAAO,WAAW,MAAM,SAAS,iBAAiB,OAAO;CAC7D,IAAI,OAAO,WAAW,SAAS;CAC/B,IAAI,OAAO,SAAS,SAAS;CAC7B,IAAI,OAAO,SAAS,SAAS;CAC7B,OAAO,IAAI,aAAa,YAAY,OAAO,QAAQ,IAAI,UAAU;EAChE,MAAM,OAAO,UAAU,YAAY;EACnC,SAAS;GAAE,SAAS,OAAO;GAAS,MAAM,OAAO;GAAM,QAAQ,OAAO;EAAO;EAC7E,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;EACvC;CACD,CAAC;AACF"}
@@ -684,8 +684,11 @@ function buildExecuteResult(input) {
684
684
  * overshoot it by the line that crossed it plus the rest of its delivered chunk. Termination never
685
685
  * reapplies backpressure, and retained lines are capped at twice `backlog`; later lines are dropped
686
686
  * and `truncated` reports the omission. Standard error is decoded and forwarded live as the `stderr`
687
- * event while a byte-bounded raw tail is retained as `evidence`. The typed `emitter` also carries the
688
- * child `error` cause on a spawn fault and the terminal `exit`, alongside the `exit` promise. `pid`,
687
+ * event while a byte-bounded raw tail is retained as `evidence`. The typed `emitter` also carries a
688
+ * child `error` cause on a spawn fault, a `protocol` {@link ProcessError} on a host-reported stdin
689
+ * fault after the constructor input phase, and the terminal `exit`, alongside the `exit` promise.
690
+ * A fault arising from the constructor-supplied `input` write or its closing `end` stays quiet
691
+ * because the package initiated that sequence. `pid`,
689
692
  * `code`, and `signal` read the spawned child directly, so the native exit is readable before the
690
693
  * `exit` promise settles on stdio close. `stop` ends the whole tree and reports whether the native
691
694
  * exit was observed; `destroy` stops the child, destroys stdin so every pending `send` settles, and
@@ -711,6 +714,7 @@ var Process = class {
711
714
  #grace;
712
715
  #evidence;
713
716
  #backlog;
717
+ #delivery;
714
718
  #decoder = new node_string_decoder.StringDecoder("utf8");
715
719
  #exit = Promise.withResolvers();
716
720
  #lines;
@@ -718,6 +722,7 @@ var Process = class {
718
722
  #waiters = [];
719
723
  #signal;
720
724
  #abort;
725
+ #writes = /* @__PURE__ */ new Map();
721
726
  #tail = node_buffer.Buffer.alloc(0);
722
727
  #head = 0;
723
728
  #pending = 0;
@@ -727,6 +732,8 @@ var Process = class {
727
732
  #terminating = false;
728
733
  #closed = false;
729
734
  #ended = false;
735
+ #input = 0;
736
+ #failure;
730
737
  #stopping;
731
738
  #ending;
732
739
  /**
@@ -741,6 +748,7 @@ var Process = class {
741
748
  const grace = options.grace;
742
749
  const evidence = options.evidence;
743
750
  const backlog = options.backlog;
751
+ const delivery = options.delivery;
744
752
  const writable = options.writable;
745
753
  const signal = options.signal;
746
754
  const on = options.on;
@@ -750,6 +758,7 @@ var Process = class {
750
758
  validateCommand(command);
751
759
  validateWorkspace(workspace);
752
760
  validateTimer(grace, "option 'grace'");
761
+ validateTimer(delivery, "option 'delivery'");
753
762
  validateBytes(evidence, "option 'evidence'", 0);
754
763
  validateBytes(backlog, "option 'backlog'", 1);
755
764
  this.#emitter = new _orkestrel_emitter.Emitter({
@@ -759,6 +768,7 @@ var Process = class {
759
768
  this.#grace = grace ?? _src_core.PROCESS_GRACE;
760
769
  this.#evidence = evidence ?? _src_core.PROCESS_EVIDENCE;
761
770
  this.#backlog = backlog ?? _src_core.PROCESS_BACKLOG;
771
+ this.#delivery = delivery ?? 0;
762
772
  this.#signal = signal;
763
773
  this.#lines = Object.freeze({ [Symbol.asyncIterator]: this.#iterate.bind(this) });
764
774
  const childEnvironment = mergeEnvironment(command.isolated === true, command.environment);
@@ -786,10 +796,16 @@ var Process = class {
786
796
  this.#reader.once("close", this.#finish.bind(this));
787
797
  this.#child.once("error", (cause) => this.#emitter.emit("error", cause));
788
798
  this.#child.once("close", this.#close.bind(this));
789
- this.#child.stdin.on("error", () => void 0);
799
+ this.#child.stdin.on("error", (cause) => this.#failInputStream(cause));
790
800
  this.#child.stderr.on("data", this.#retain.bind(this));
791
- if (input !== void 0) this.#child.stdin.write(input);
792
- if (writable !== true) this.#child.stdin.end();
801
+ if (input !== void 0) {
802
+ this.#input += 1;
803
+ this.#child.stdin.write(input, this.#completeInput.bind(this));
804
+ }
805
+ if (writable !== true) {
806
+ this.#input += 1;
807
+ this.#child.stdin.end(this.#completeInput.bind(this));
808
+ }
793
809
  if (signal !== void 0) {
794
810
  this.#abort = this.#terminate.bind(this);
795
811
  signal.addEventListener("abort", this.#abort, { once: true });
@@ -832,24 +848,34 @@ var Process = class {
832
848
  * Write one line to the open standard-input channel.
833
849
  *
834
850
  * @remarks
835
- * Never rejects. The promise settles when the host reports the line handled, so a line written to
836
- * a child that is not reading stays pending until the child drains it or the channel closes. Every
837
- * terminal teardown path destroys stdin, so a pending write always settles by teardown; a caller
838
- * that needs its own deadline arms a timer and calls `stop`.
851
+ * Never rejects. `true` means the host accepted the bytes without reporting a fault; it does not
852
+ * prove that the child read them. An ordinary write settles when the kernel accepts it. Only a
853
+ * full pipe can hold the write unconfirmed. The `delivery` option can bound that wait, and every
854
+ * terminal teardown path settles pending writes. On Windows 11 with Node v24.18.1, measured on
855
+ * 2026-08-21, a child that closes its own file descriptor 0 can leave the parent pipe writable:
856
+ * the write can settle `true` without a callback error or a stream error while the child remains
857
+ * alive. After `stop` or `destroy` begins, a later call settles `false`. Version 0.0.4 could settle
858
+ * that call `true` before teardown destroyed the pipe; returning `false` avoids claiming delivery
859
+ * for bytes the package is about to discard.
839
860
  *
840
861
  * @param text - The line text without its trailing newline
841
- * @returns True when the line reached the host without error; false when the channel was closed, destroyed, ended, or the write failed
862
+ * @returns True when the host accepted the bytes without reporting a fault; false when the channel was closed, destroyed, ended, failed, or remained unconfirmed through `delivery`
842
863
  */
843
864
  send(text) {
844
865
  const stdin = this.#child.stdin;
845
- if (this.#closed || !stdin.writable) return Promise.resolve(false);
866
+ if (this.#closed || this.#terminating || this.#failure !== void 0 || !stdin.writable) return Promise.resolve(false);
846
867
  const settled = Promise.withResolvers();
868
+ this.#writes.set(settled, void 0);
847
869
  try {
848
- stdin.write(`${text}\n`, (error) => {
849
- settled.resolve(error === void 0 || error === null);
850
- });
870
+ stdin.write(`${text}\n`, this.#confirmWrite.bind(this, settled));
851
871
  } catch {
852
- return Promise.resolve(false);
872
+ this.#settleWrite(settled, false);
873
+ return settled.promise;
874
+ }
875
+ if (this.#delivery > 0 && this.#writes.has(settled)) {
876
+ const timer = setTimeout(() => this.#settleWrite(settled, false), this.#delivery);
877
+ timer.unref();
878
+ this.#writes.set(settled, timer);
853
879
  }
854
880
  return settled.promise;
855
881
  }
@@ -980,6 +1006,42 @@ var Process = class {
980
1006
  if (this.#signal === void 0 || this.#abort === void 0) return;
981
1007
  this.#signal.removeEventListener("abort", this.#abort);
982
1008
  }
1009
+ #confirmWrite(settled, error) {
1010
+ if (error === void 0 || error === null) {
1011
+ this.#settleWrite(settled, true);
1012
+ return;
1013
+ }
1014
+ this.#failInputCallback(error);
1015
+ }
1016
+ #completeInput() {
1017
+ this.#input -= 1;
1018
+ }
1019
+ #settleWrite(settled, accepted) {
1020
+ if (!this.#writes.has(settled)) return;
1021
+ const timer = this.#writes.get(settled);
1022
+ this.#writes.delete(settled);
1023
+ clearTimeout(timer);
1024
+ settled.resolve(accepted);
1025
+ }
1026
+ #settleWrites() {
1027
+ for (const settled of this.#writes.keys()) this.#settleWrite(settled, false);
1028
+ }
1029
+ #failInputStream(cause) {
1030
+ if (this.#child.stdin.writableEnded || this.#input > 0) {
1031
+ this.#settleWrites();
1032
+ return;
1033
+ }
1034
+ this.#failInputCallback(cause);
1035
+ }
1036
+ #failInputCallback(cause) {
1037
+ if (this.#failure !== void 0 || this.#terminating) return;
1038
+ this.#failure = cause;
1039
+ this.#settleWrites();
1040
+ this.#emitter.emit("error", new _src_core.ProcessError("The standard-input channel failed", {
1041
+ code: "protocol",
1042
+ cause
1043
+ }));
1044
+ }
983
1045
  #capBacklog() {
984
1046
  const limit = this.#backlog * 2;
985
1047
  while (this.#pending > limit && this.#queue.length > this.#head) {
@@ -994,6 +1056,7 @@ var Process = class {
994
1056
  this.#capBacklog();
995
1057
  this.#relieve();
996
1058
  const confirmed = await stopChild(this.#child, this.#grace, _src_core.PROCESS_CONFIRMATION);
1059
+ this.#settleWrites();
997
1060
  this.#child.stdin.destroy();
998
1061
  return confirmed;
999
1062
  }
@@ -1258,9 +1321,13 @@ var Retention = class {
1258
1321
  * rather than on process exit, so a descendant that inherited the child's stdio holds the run open
1259
1322
  * after the child itself has gone. Give such a run a `timeout`. The child's `environment` merges over
1260
1323
  * the parent unless the command is `isolated`, then `options.environment` on top, and `options.input`
1261
- * overrides `command.input`. Unless `strict` is `false`, a failed run rejects with a
1262
- * {@link createExecuteError} carrying the {@link ExecuteResult}. An invalid option or command string rejects
1263
- * before the child is spawned, because an async function cannot throw synchronously.
1324
+ * overrides `command.input`. A host fault while writing that input terminates the run by design and
1325
+ * marks its result failed; a strict rejection carries the host fault as its cause. This differs
1326
+ * from `Process` constructor input, whose package-initiated input phase stays quiet. Unless `strict`
1327
+ * is `false`, a failed run rejects with a {@link ProcessError} carrying the {@link ExecuteResult};
1328
+ * {@link createExecuteError} constructs every rejection outside the input-fault door. An invalid
1329
+ * option or command string rejects before the child is spawned, because an async function cannot
1330
+ * throw synchronously.
1264
1331
  *
1265
1332
  * @param command - The executable, arguments, and optional environment and input
1266
1333
  * @param options - Working directory, timeout, grace, signal, capture limit, and failure delivery
@@ -1304,6 +1371,7 @@ async function execute(command, options) {
1304
1371
  const terminate = new AbortController();
1305
1372
  const cleanup = new AbortController();
1306
1373
  const finish = new AbortController();
1374
+ const inputFailure = new AbortController();
1307
1375
  const outChunks = [];
1308
1376
  const errChunks = [];
1309
1377
  const outRetention = new Retention();
@@ -1351,7 +1419,12 @@ async function execute(command, options) {
1351
1419
  confirmTimer = setTimeout(() => finish.abort(), _src_core.PROCESS_CONFIRMATION);
1352
1420
  });
1353
1421
  }, { once: true });
1354
- child.stdin.on("error", () => void 0);
1422
+ inputFailure.signal.addEventListener("abort", () => {
1423
+ if (finish.signal.aborted) return;
1424
+ cause = inputFailure.signal.reason;
1425
+ terminate.abort();
1426
+ }, { once: true });
1427
+ child.stdin.on("error", (error) => inputFailure.abort(error));
1355
1428
  child.stdout.on("data", (chunk) => {
1356
1429
  const retained = outRetention.retain(chunk, limit);
1357
1430
  if (retained !== void 0) outChunks.push(retained);
@@ -1369,7 +1442,9 @@ async function execute(command, options) {
1369
1442
  finish.abort();
1370
1443
  });
1371
1444
  child.once("close", () => finish.abort());
1372
- if (text !== void 0) child.stdin.write(text);
1445
+ if (text !== void 0) child.stdin.write(text, (error) => {
1446
+ if (error !== void 0 && error !== null) inputFailure.abort(error);
1447
+ });
1373
1448
  child.stdin.end();
1374
1449
  if (timeout > 0) timeoutTimer = setTimeout(() => {
1375
1450
  expired = true;
@@ -1389,7 +1464,19 @@ async function execute(command, options) {
1389
1464
  }
1390
1465
  }
1391
1466
  const result = await settled.promise;
1392
- if (result.failed && strict) throw (0, _src_core.createExecuteError)(result, cause);
1467
+ if (result.failed && strict) {
1468
+ if (cause !== void 0 && cause === inputFailure.signal.reason) throw new _src_core.ProcessError(`Command '${result.command}' failed while writing standard input`, {
1469
+ code: "input",
1470
+ context: {
1471
+ command: result.command,
1472
+ code: result.code,
1473
+ signal: result.signal
1474
+ },
1475
+ cause: inputFailure.signal.reason,
1476
+ result
1477
+ });
1478
+ throw (0, _src_core.createExecuteError)(result, cause);
1479
+ }
1393
1480
  return result;
1394
1481
  }
1395
1482
  //#endregion