@orkestrel/process 0.0.1 → 0.0.2

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.
@@ -61,9 +61,10 @@ function createDuplicateError(id) {
61
61
  * Creates the failure raised when a run does not complete successfully and rejection is requested.
62
62
  *
63
63
  * @param result - The buffered run outcome that failed
64
+ * @param cause - The underlying spawn error, when one ended the run
64
65
  * @returns A typed run failure carrying its {@link RunResult}
65
66
  */
66
- function createRunError(result) {
67
+ function createRunError(result, cause) {
67
68
  const reason = result.timedOut ? "timed out" : result.signal !== null ? `was killed by ${result.signal}` : `exited with code ${String(result.code)}`;
68
69
  return new ProcessError(`Command '${result.command}' ${reason}`, {
69
70
  code: result.timedOut ? "timeout" : "spawn",
@@ -72,6 +73,7 @@ function createRunError(result) {
72
73
  code: result.code,
73
74
  signal: result.signal
74
75
  },
76
+ ...cause === void 0 ? {} : { cause },
75
77
  result
76
78
  });
77
79
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts"],"sourcesContent":["/** The default cooperative window in milliseconds between `SIGTERM` and `SIGKILL` during termination. */\nexport const PROCESS_GRACE = 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 maximum captured bytes for a one-shot run's stdout and stderr, each. */\nexport const PROCESS_OUTPUT = 10_485_760\n","import { holds } from '@orkestrel/contract'\nimport type {\n\tProcessErrorCode,\n\tProcessErrorContext,\n\tProcessErrorOptions,\n\tRunResult,\n} from './types.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?: RunResult\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\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 * @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\treturn holds(() => value instanceof ProcessError)\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 run does not complete successfully and rejection is requested.\n *\n * @param result - The buffered run outcome that failed\n * @returns A typed run failure carrying its {@link RunResult}\n */\nexport function createRunError(result: RunResult): ProcessError {\n\tconst reason = result.timedOut\n\t\t? 'timed out'\n\t\t: result.signal !== null\n\t\t\t? `was killed by ${result.signal}`\n\t\t\t: `exited with code ${String(result.code)}`\n\treturn new ProcessError(`Command '${result.command}' ${reason}`, {\n\t\tcode: result.timedOut ? 'timeout' : 'spawn',\n\t\tcontext: { command: result.command, code: result.code, signal: result.signal },\n\t\tresult,\n\t})\n}\n"],"mappings":";;;;AACA,IAAa,gBAAgB;;AAG7B,IAAa,mBAAmB;;AAGhC,IAAa,iBAAiB;;;;ACE9B,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,KAAK,OAAO,QAAQ;EACpB,IAAI,QAAQ,YAAY,KAAA,GAAW,KAAK,UAAU,QAAQ;EAC1D,IAAI,QAAQ,WAAW,KAAA,GAAW,KAAK,SAAS,QAAQ;CACzD;AACD;;;;;;;;;;;;;AAcA,SAAgB,eAAe,OAAuC;CACrE,QAAA,GAAO,oBAAA,MAAA,OAAY,iBAAiB,YAAY;AACjD;;;;;;;AAQA,SAAgB,qBAAqB,IAA0B;CAC9D,OAAO,IAAI,aAAa,YAAY,GAAG,oBAAoB;EAC1D,MAAM;EACN,SAAS,EAAE,GAAG;CACf,CAAC;AACF;;;;;;;AAQA,SAAgB,eAAe,QAAiC;CAC/D,MAAM,SAAS,OAAO,WACnB,cACA,OAAO,WAAW,OACjB,iBAAiB,OAAO,WACxB,oBAAoB,OAAO,OAAO,IAAI;CAC1C,OAAO,IAAI,aAAa,YAAY,OAAO,QAAQ,IAAI,UAAU;EAChE,MAAM,OAAO,WAAW,YAAY;EACpC,SAAS;GAAE,SAAS,OAAO;GAAS,MAAM,OAAO;GAAM,QAAQ,OAAO;EAAO;EAC7E;CACD,CAAC;AACF"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts"],"sourcesContent":["/** The default cooperative window in milliseconds between `SIGTERM` and `SIGKILL` during termination. */\nexport const PROCESS_GRACE = 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 maximum captured bytes for a one-shot run's stdout and stderr, each. */\nexport const PROCESS_OUTPUT = 10_485_760\n","import { holds } from '@orkestrel/contract'\nimport type {\n\tProcessErrorCode,\n\tProcessErrorContext,\n\tProcessErrorOptions,\n\tRunResult,\n} from './types.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?: RunResult\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\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 * @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\treturn holds(() => value instanceof ProcessError)\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 run does not complete successfully and rejection is requested.\n *\n * @param result - The buffered run outcome that failed\n * @param cause - The underlying spawn error, when one ended the run\n * @returns A typed run failure carrying its {@link RunResult}\n */\nexport function createRunError(result: RunResult, cause?: unknown): ProcessError {\n\tconst reason = result.timedOut\n\t\t? 'timed out'\n\t\t: result.signal !== null\n\t\t\t? `was killed by ${result.signal}`\n\t\t\t: `exited with code ${String(result.code)}`\n\treturn new ProcessError(`Command '${result.command}' ${reason}`, {\n\t\tcode: result.timedOut ? '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,mBAAmB;;AAGhC,IAAa,iBAAiB;;;;ACE9B,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,KAAK,OAAO,QAAQ;EACpB,IAAI,QAAQ,YAAY,KAAA,GAAW,KAAK,UAAU,QAAQ;EAC1D,IAAI,QAAQ,WAAW,KAAA,GAAW,KAAK,SAAS,QAAQ;CACzD;AACD;;;;;;;;;;;;;AAcA,SAAgB,eAAe,OAAuC;CACrE,QAAA,GAAO,oBAAA,MAAA,OAAY,iBAAiB,YAAY;AACjD;;;;;;;AAQA,SAAgB,qBAAqB,IAA0B;CAC9D,OAAO,IAAI,aAAa,YAAY,GAAG,oBAAoB;EAC1D,MAAM;EACN,SAAS,EAAE,GAAG;CACf,CAAC;AACF;;;;;;;;AASA,SAAgB,eAAe,QAAmB,OAA+B;CAChF,MAAM,SAAS,OAAO,WACnB,cACA,OAAO,WAAW,OACjB,iBAAiB,OAAO,WACxB,oBAAoB,OAAO,OAAO,IAAI;CAC1C,OAAO,IAAI,aAAa,YAAY,OAAO,QAAQ,IAAI,UAAU;EAChE,MAAM,OAAO,WAAW,YAAY;EACpC,SAAS;GAAE,SAAS,OAAO;GAAS,MAAM,OAAO;GAAM,QAAQ,OAAO;EAAO;EAC7E,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;EACvC;CACD,CAAC;AACF"}
@@ -14,9 +14,10 @@ export declare function createDuplicateError(id: string): ProcessError;
14
14
  * Creates the failure raised when a run does not complete successfully and rejection is requested.
15
15
  *
16
16
  * @param result - The buffered run outcome that failed
17
+ * @param cause - The underlying spawn error, when one ended the run
17
18
  * @returns A typed run failure carrying its {@link RunResult}
18
19
  */
19
- export declare function createRunError(result: RunResult): ProcessError;
20
+ export declare function createRunError(result: RunResult, cause?: unknown): ProcessError;
20
21
 
21
22
  /**
22
23
  * Checks whether an unknown value is a {@link ProcessError}.
@@ -124,12 +125,16 @@ export declare interface ProcessErrorOptions {
124
125
  * observer subscribes to, alongside the `lines` stream and the `exit` promise.
125
126
  *
126
127
  * @remarks
127
- * Declared as a `type` alias so it satisfies the emitter's `EventMap` constraint structurally. A
128
- * listener throw is isolated by the emitter and routed to its `error` handler, never onto this map.
128
+ * Declared as a `type` alias so it satisfies the emitter's `EventMap` constraint structurally. The
129
+ * `error` event carries a child fault — a failure to spawn or a process-level error. It is distinct
130
+ * from the `error` handler in {@link ProcessOptions}: a listener throw is isolated by the emitter and
131
+ * routed to that handler, never emitted as this `error` event.
129
132
  */
130
133
  export declare type ProcessEventMap = {
131
134
  /** A decoded standard-error chunk arrived. */
132
135
  readonly stderr: readonly [chunk: string];
136
+ /** The child emitted an error — a spawn fault or process-level failure — carrying its cause. */
137
+ readonly error: readonly [error: unknown];
133
138
  /** The child settled — its terminal state, delivered once. */
134
139
  readonly exit: readonly [exit: ProcessExit];
135
140
  };
@@ -149,8 +154,8 @@ export declare interface ProcessExit {
149
154
  * `lines` is pumped eagerly: stdout is drained whether or not a consumer iterates, so `exit`
150
155
  * resolves and a late consumer still receives every line. `evidence` is the decoded, byte-bounded
151
156
  * stderr tail — the diagnostic to attach to a failed exit. The typed `emitter` carries the live
152
- * `stderr` chunks and the terminal `exit`, alongside the `exit` promise. `send` never throws; it
153
- * returns whether the line reached an open channel. `stop` is idempotent.
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.
154
159
  */
155
160
  export declare interface ProcessInterface {
156
161
  /** The typed lifecycle observation surface. */
@@ -14,9 +14,10 @@ export declare function createDuplicateError(id: string): ProcessError;
14
14
  * Creates the failure raised when a run does not complete successfully and rejection is requested.
15
15
  *
16
16
  * @param result - The buffered run outcome that failed
17
+ * @param cause - The underlying spawn error, when one ended the run
17
18
  * @returns A typed run failure carrying its {@link RunResult}
18
19
  */
19
- export declare function createRunError(result: RunResult): ProcessError;
20
+ export declare function createRunError(result: RunResult, cause?: unknown): ProcessError;
20
21
 
21
22
  /**
22
23
  * Checks whether an unknown value is a {@link ProcessError}.
@@ -124,12 +125,16 @@ export declare interface ProcessErrorOptions {
124
125
  * observer subscribes to, alongside the `lines` stream and the `exit` promise.
125
126
  *
126
127
  * @remarks
127
- * Declared as a `type` alias so it satisfies the emitter's `EventMap` constraint structurally. A
128
- * listener throw is isolated by the emitter and routed to its `error` handler, never onto this map.
128
+ * Declared as a `type` alias so it satisfies the emitter's `EventMap` constraint structurally. The
129
+ * `error` event carries a child fault — a failure to spawn or a process-level error. It is distinct
130
+ * from the `error` handler in {@link ProcessOptions}: a listener throw is isolated by the emitter and
131
+ * routed to that handler, never emitted as this `error` event.
129
132
  */
130
133
  export declare type ProcessEventMap = {
131
134
  /** A decoded standard-error chunk arrived. */
132
135
  readonly stderr: readonly [chunk: string];
136
+ /** The child emitted an error — a spawn fault or process-level failure — carrying its cause. */
137
+ readonly error: readonly [error: unknown];
133
138
  /** The child settled — its terminal state, delivered once. */
134
139
  readonly exit: readonly [exit: ProcessExit];
135
140
  };
@@ -149,8 +154,8 @@ export declare interface ProcessExit {
149
154
  * `lines` is pumped eagerly: stdout is drained whether or not a consumer iterates, so `exit`
150
155
  * resolves and a late consumer still receives every line. `evidence` is the decoded, byte-bounded
151
156
  * stderr tail — the diagnostic to attach to a failed exit. The typed `emitter` carries the live
152
- * `stderr` chunks and the terminal `exit`, alongside the `exit` promise. `send` never throws; it
153
- * returns whether the line reached an open channel. `stop` is idempotent.
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.
154
159
  */
155
160
  export declare interface ProcessInterface {
156
161
  /** The typed lifecycle observation surface. */
@@ -60,9 +60,10 @@ function createDuplicateError(id) {
60
60
  * Creates the failure raised when a run does not complete successfully and rejection is requested.
61
61
  *
62
62
  * @param result - The buffered run outcome that failed
63
+ * @param cause - The underlying spawn error, when one ended the run
63
64
  * @returns A typed run failure carrying its {@link RunResult}
64
65
  */
65
- function createRunError(result) {
66
+ function createRunError(result, cause) {
66
67
  const reason = result.timedOut ? "timed out" : result.signal !== null ? `was killed by ${result.signal}` : `exited with code ${String(result.code)}`;
67
68
  return new ProcessError(`Command '${result.command}' ${reason}`, {
68
69
  code: result.timedOut ? "timeout" : "spawn",
@@ -71,6 +72,7 @@ function createRunError(result) {
71
72
  code: result.code,
72
73
  signal: result.signal
73
74
  },
75
+ ...cause === void 0 ? {} : { cause },
74
76
  result
75
77
  });
76
78
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts"],"sourcesContent":["/** The default cooperative window in milliseconds between `SIGTERM` and `SIGKILL` during termination. */\nexport const PROCESS_GRACE = 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 maximum captured bytes for a one-shot run's stdout and stderr, each. */\nexport const PROCESS_OUTPUT = 10_485_760\n","import { holds } from '@orkestrel/contract'\nimport type {\n\tProcessErrorCode,\n\tProcessErrorContext,\n\tProcessErrorOptions,\n\tRunResult,\n} from './types.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?: RunResult\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\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 * @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\treturn holds(() => value instanceof ProcessError)\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 run does not complete successfully and rejection is requested.\n *\n * @param result - The buffered run outcome that failed\n * @returns A typed run failure carrying its {@link RunResult}\n */\nexport function createRunError(result: RunResult): ProcessError {\n\tconst reason = result.timedOut\n\t\t? 'timed out'\n\t\t: result.signal !== null\n\t\t\t? `was killed by ${result.signal}`\n\t\t\t: `exited with code ${String(result.code)}`\n\treturn new ProcessError(`Command '${result.command}' ${reason}`, {\n\t\tcode: result.timedOut ? 'timeout' : 'spawn',\n\t\tcontext: { command: result.command, code: result.code, signal: result.signal },\n\t\tresult,\n\t})\n}\n"],"mappings":";;;AACA,IAAa,gBAAgB;;AAG7B,IAAa,mBAAmB;;AAGhC,IAAa,iBAAiB;;;;ACE9B,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,KAAK,OAAO,QAAQ;EACpB,IAAI,QAAQ,YAAY,KAAA,GAAW,KAAK,UAAU,QAAQ;EAC1D,IAAI,QAAQ,WAAW,KAAA,GAAW,KAAK,SAAS,QAAQ;CACzD;AACD;;;;;;;;;;;;;AAcA,SAAgB,eAAe,OAAuC;CACrE,OAAO,YAAY,iBAAiB,YAAY;AACjD;;;;;;;AAQA,SAAgB,qBAAqB,IAA0B;CAC9D,OAAO,IAAI,aAAa,YAAY,GAAG,oBAAoB;EAC1D,MAAM;EACN,SAAS,EAAE,GAAG;CACf,CAAC;AACF;;;;;;;AAQA,SAAgB,eAAe,QAAiC;CAC/D,MAAM,SAAS,OAAO,WACnB,cACA,OAAO,WAAW,OACjB,iBAAiB,OAAO,WACxB,oBAAoB,OAAO,OAAO,IAAI;CAC1C,OAAO,IAAI,aAAa,YAAY,OAAO,QAAQ,IAAI,UAAU;EAChE,MAAM,OAAO,WAAW,YAAY;EACpC,SAAS;GAAE,SAAS,OAAO;GAAS,MAAM,OAAO;GAAM,QAAQ,OAAO;EAAO;EAC7E;CACD,CAAC;AACF"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts"],"sourcesContent":["/** The default cooperative window in milliseconds between `SIGTERM` and `SIGKILL` during termination. */\nexport const PROCESS_GRACE = 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 maximum captured bytes for a one-shot run's stdout and stderr, each. */\nexport const PROCESS_OUTPUT = 10_485_760\n","import { holds } from '@orkestrel/contract'\nimport type {\n\tProcessErrorCode,\n\tProcessErrorContext,\n\tProcessErrorOptions,\n\tRunResult,\n} from './types.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?: RunResult\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\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 * @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\treturn holds(() => value instanceof ProcessError)\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 run does not complete successfully and rejection is requested.\n *\n * @param result - The buffered run outcome that failed\n * @param cause - The underlying spawn error, when one ended the run\n * @returns A typed run failure carrying its {@link RunResult}\n */\nexport function createRunError(result: RunResult, cause?: unknown): ProcessError {\n\tconst reason = result.timedOut\n\t\t? 'timed out'\n\t\t: result.signal !== null\n\t\t\t? `was killed by ${result.signal}`\n\t\t\t: `exited with code ${String(result.code)}`\n\treturn new ProcessError(`Command '${result.command}' ${reason}`, {\n\t\tcode: result.timedOut ? '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,mBAAmB;;AAGhC,IAAa,iBAAiB;;;;ACE9B,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,KAAK,OAAO,QAAQ;EACpB,IAAI,QAAQ,YAAY,KAAA,GAAW,KAAK,UAAU,QAAQ;EAC1D,IAAI,QAAQ,WAAW,KAAA,GAAW,KAAK,SAAS,QAAQ;CACzD;AACD;;;;;;;;;;;;;AAcA,SAAgB,eAAe,OAAuC;CACrE,OAAO,YAAY,iBAAiB,YAAY;AACjD;;;;;;;AAQA,SAAgB,qBAAqB,IAA0B;CAC9D,OAAO,IAAI,aAAa,YAAY,GAAG,oBAAoB;EAC1D,MAAM;EACN,SAAS,EAAE,GAAG;CACf,CAAC;AACF;;;;;;;;AASA,SAAgB,eAAe,QAAmB,OAA+B;CAChF,MAAM,SAAS,OAAO,WACnB,cACA,OAAO,WAAW,OACjB,iBAAiB,OAAO,WACxB,oBAAoB,OAAO,OAAO,IAAI;CAC1C,OAAO,IAAI,aAAa,YAAY,OAAO,QAAQ,IAAI,UAAU;EAChE,MAAM,OAAO,WAAW,YAAY;EACpC,SAAS;GAAE,SAAS,OAAO;GAAS,MAAM,OAAO;GAAM,QAAQ,OAAO;EAAO;EAC7E,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;EACvC;CACD,CAAC;AACF"}
@@ -217,6 +217,7 @@ async function run(command, options) {
217
217
  let stdout = node_buffer.Buffer.alloc(0);
218
218
  let stderr = node_buffer.Buffer.alloc(0);
219
219
  let timedOut = false;
220
+ let launchCause;
220
221
  let killTimer;
221
222
  let timeoutTimer;
222
223
  let done = false;
@@ -243,9 +244,10 @@ async function run(command, options) {
243
244
  child.stderr.on("data", (chunk) => {
244
245
  if (node_buffer.Buffer.isBuffer(chunk)) stderr = trimHead(node_buffer.Buffer.concat([stderr, chunk]), limit);
245
246
  });
246
- child.once("error", () => {
247
+ child.once("error", (cause) => {
247
248
  if (done) return;
248
249
  done = true;
250
+ launchCause = cause;
249
251
  cleanup.abort();
250
252
  clearTimeout(killTimer);
251
253
  clearTimeout(timeoutTimer);
@@ -274,7 +276,7 @@ async function run(command, options) {
274
276
  if (signal.aborted) terminate.abort();
275
277
  }
276
278
  const result = await settled.promise;
277
- if (result.failed && reject) throw (0, _src_core.createRunError)(result);
279
+ if (result.failed && reject) throw (0, _src_core.createRunError)(result, launchCause);
278
280
  return result;
279
281
  }
280
282
  /**
@@ -321,7 +323,7 @@ function runSync(command, options) {
321
323
  const error = outcome.error;
322
324
  const timedOut = error !== void 0 && "code" in error && error.code === "ETIMEDOUT";
323
325
  const result = buildRunResult(line, node_buffer.Buffer.isBuffer(outcome.stdout) ? outcome.stdout : node_buffer.Buffer.alloc(0), node_buffer.Buffer.isBuffer(outcome.stderr) ? outcome.stderr : node_buffer.Buffer.alloc(0), outcome.status, outcome.signal, timedOut, limit);
324
- if (result.failed && reject) throw (0, _src_core.createRunError)(result);
326
+ if (result.failed && reject) throw (0, _src_core.createRunError)(result, outcome.error);
325
327
  return result;
326
328
  }
327
329
  //#endregion
@@ -333,8 +335,9 @@ function runSync(command, options) {
333
335
  * Standard output is drained eagerly through `readline`, so `exit` resolves and a late consumer of
334
336
  * `lines` still receives every framed line, including a final line written without a trailing
335
337
  * newline. Standard error is decoded and forwarded live as the `stderr` event while a byte-bounded
336
- * raw tail is retained as `evidence`. The typed `emitter` also carries the terminal `exit`,
337
- * alongside the `exit` promise. `stop` terminates the child through `SIGTERM`, then `SIGKILL` after
338
+ * raw tail is retained as `evidence`. The typed `emitter` also carries the child `error` cause on a
339
+ * spawn fault and the terminal `exit`, alongside the `exit` promise. `stop` terminates the child
340
+ * through `SIGTERM`, then `SIGKILL` after
338
341
  * `grace`, and is idempotent; `destroy` stops the child and destroys the emitter last.
339
342
  *
340
343
  * @example
@@ -401,7 +404,7 @@ var Process = class {
401
404
  });
402
405
  this.#reader.on("line", this.#push.bind(this));
403
406
  this.#reader.once("close", this.#finish.bind(this));
404
- this.#child.once("error", () => void 0);
407
+ this.#child.once("error", (cause) => this.#emitter.emit("error", cause));
405
408
  this.#child.once("close", this.#close.bind(this));
406
409
  this.#child.stdin.on("error", () => void 0);
407
410
  this.#child.stderr.on("data", this.#retain.bind(this));
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["#emitter","#child","#reader","#grace","#limit","#decoder","#exit","#lines","#buffer","#waiters","#signal","#abort","#iterate","#push","#finish","#close","#retain","#terminate","#tail","#closed","#stopping","#kill","#ending","#teardown","#next","#ended","#emitter","#children","#evict","#stopAll","#stopOne","#stopMany","#ending","#teardown"],"sources":["../../../src/server/helpers.ts","../../../src/server/Process.ts","../../../src/server/ProcessManager.ts","../../../src/server/factories.ts"],"sourcesContent":["import type { ChildProcessWithoutNullStreams } from 'node:child_process'\nimport type { ProcessCommand, RunOptions, RunResult } from '@src/core'\nimport { Buffer } from 'node:buffer'\nimport { spawn, spawnSync } from 'node:child_process'\nimport { extname } from 'node:path'\nimport { createRunError, PROCESS_GRACE, PROCESS_OUTPUT } from '@src/core'\n\n/**\n * Signals one owned child process, or its detached process group on a POSIX host.\n *\n * @remarks\n * On a POSIX host a child spawned `detached` leads its own process group, so signalling the\n * negated pid reaches the whole tree. On Windows, or when no pid is available, the signal goes to\n * the child directly. A throw during signalling is swallowed: the process can exit and win the\n * signal race, and the child's close event remains the authoritative terminal state.\n *\n * @param child - The child process signal boundary\n * @param signal - The signal to deliver\n * @returns Nothing\n *\n * @example\n * ```ts\n * killProcess(child, 'SIGTERM')\n * ```\n */\nexport function killProcess(\n\tchild: { readonly pid?: number | undefined; kill(signal: NodeJS.Signals): boolean },\n\tsignal: NodeJS.Signals,\n): void {\n\ttry {\n\t\tif (process.platform === 'win32' || child.pid === undefined) {\n\t\t\tchild.kill(signal)\n\t\t\treturn\n\t\t}\n\t\tprocess.kill(-child.pid, signal)\n\t} catch {\n\t\t// Exit can win the signal race; the close event remains authoritative.\n\t}\n}\n\n/**\n * Trims a buffer to at most `limit` trailing bytes without splitting a UTF-8 sequence.\n *\n * @remarks\n * Keeps the tail — the diagnostic end of a stderr stream. When the cut point lands inside a\n * multibyte sequence, the start advances past the leading continuation bytes so the retained\n * bytes always begin on a code-point boundary.\n *\n * @param buffer - The accumulated bytes\n * @param limit - The maximum retained byte count\n * @returns The trailing bytes, at most `limit` and never starting mid-sequence\n *\n * @example\n * ```ts\n * trimTail(Buffer.from('hello'), 3) // <Buffer 6c 6c 6f>\n * ```\n */\nexport function trimTail(buffer: Buffer, limit: number): Buffer {\n\tif (buffer.byteLength <= limit) return buffer\n\tlet start = buffer.byteLength - limit\n\twhile (start < buffer.byteLength) {\n\t\tconst byte = buffer[start]\n\t\tif (byte === undefined || (byte & 0xc0) !== 0x80) break\n\t\tstart += 1\n\t}\n\treturn Buffer.from(buffer.subarray(start))\n}\n\n/**\n * Trims a buffer to at most `limit` leading bytes without splitting a UTF-8 sequence.\n *\n * @remarks\n * Keeps the head — the captured start of a one-shot run's output. When the cut point lands inside\n * a multibyte sequence, the end retreats to the sequence's start so the retained bytes always end\n * on a code-point boundary and decode without a replacement character.\n *\n * @param buffer - The accumulated bytes\n * @param limit - The maximum retained byte count\n * @returns The leading bytes, at most `limit` and never ending mid-sequence\n *\n * @example\n * ```ts\n * trimHead(Buffer.from('hello'), 3) // <Buffer 68 65 6c>\n * ```\n */\nexport function trimHead(buffer: Buffer, limit: number): Buffer {\n\tif (buffer.byteLength <= limit) return buffer\n\tlet end = limit\n\twhile (end > 0) {\n\t\tconst byte = buffer[end]\n\t\tif (byte === undefined || (byte & 0xc0) !== 0x80) break\n\t\tend -= 1\n\t}\n\treturn Buffer.from(buffer.subarray(0, end))\n}\n\n/**\n * Checks whether a command file must be spawned through a shell on the current host.\n *\n * @remarks\n * Node v22+ refuses to spawn a Windows `.cmd` or `.bat` batch file directly and returns `EINVAL`,\n * and a bare command name such as `git` resolves through `PATHEXT` to a batch shim rather than an\n * executable. Both cases run through the shell. A POSIX host, and a Windows path carrying a real\n * executable extension such as `.exe`, spawn directly. When the result is `true`, treat the\n * command file and arguments as trusted: shell invocation exposes shell metacharacters.\n *\n * @param file - The command executable name or path\n * @returns True when the file must run through a shell; false when it spawns directly\n *\n * @example\n * ```ts\n * requiresShell('git') // true on Windows, false on POSIX\n * ```\n */\nexport function requiresShell(file: string): boolean {\n\tif (process.platform !== 'win32') return false\n\tconst lower = file.toLowerCase()\n\treturn lower.endsWith('.cmd') || lower.endsWith('.bat') || extname(file) === ''\n}\n\n/**\n * Renders one command into its diagnostic command line.\n *\n * @param command - The executable and its argument vector\n * @returns The space-joined command line, for a {@link RunResult} and error messages\n *\n * @example\n * ```ts\n * commandLine({ file: 'git', arguments: ['status'] }) // 'git status'\n * ```\n */\nexport function commandLine(command: ProcessCommand): string {\n\treturn [command.file, ...command.arguments].join(' ')\n}\n\n/**\n * Merges environment overrides over the current process environment.\n *\n * @remarks\n * Later maps override earlier ones and an `undefined` value unsets a key. The base is the parent\n * process environment, so a child inherits it unless a key is overridden or unset.\n *\n * @param base - The command's own environment overrides\n * @param override - Per-invocation overrides applied last\n * @returns The merged environment for a spawn\n *\n * @example\n * ```ts\n * mergeEnvironment({ TOKEN: 'a' }, { TOKEN: undefined }) // TOKEN unset\n * ```\n */\nexport function mergeEnvironment(\n\tbase?: Readonly<Record<string, string | undefined>>,\n\toverride?: Readonly<Record<string, string | undefined>>,\n): NodeJS.ProcessEnv {\n\treturn { ...process.env, ...base, ...override }\n}\n\n/**\n * Builds one settled {@link RunResult} from a completed one-shot run's captured bytes and exit.\n *\n * @remarks\n * `failed` is derived: a run failed when it timed out, was ended by a signal, or exited with a\n * code other than `0` — a `null` code from a spawn fault is therefore a failure. Both output\n * buffers are byte-bounded by `limit` before decoding.\n *\n * @param command - The diagnostic command line\n * @param stdout - The captured standard output bytes\n * @param stderr - The captured standard error bytes\n * @param code - The exit code, or `null` when a signal or spawn fault ended the run\n * @param signal - The terminating signal, or `null`\n * @param timedOut - If `true`, the run's own timeout elapsed; if `false`, it did not\n * @param limit - The maximum captured bytes for each stream\n * @returns The frozen run outcome\n *\n * @example\n * ```ts\n * buildRunResult('git status', Buffer.from('ok'), Buffer.alloc(0), 0, null, false, 1024)\n * ```\n */\nexport function buildRunResult(\n\tcommand: string,\n\tstdout: Buffer,\n\tstderr: Buffer,\n\tcode: number | null,\n\tsignal: string | null,\n\ttimedOut: boolean,\n\tlimit: number,\n): RunResult {\n\treturn Object.freeze({\n\t\tcommand,\n\t\tstdout: trimHead(stdout, limit).toString('utf8'),\n\t\tstderr: trimHead(stderr, limit).toString('utf8'),\n\t\tcode,\n\t\tsignal,\n\t\tfailed: timedOut || signal !== null || code !== 0,\n\t\ttimedOut,\n\t})\n}\n\n/**\n * Runs one command to completion, buffering its output, and settles with the outcome.\n *\n * @remarks\n * The child is spawned through the cross-platform command resolver ({@link requiresShell}) and, on\n * a POSIX host, detached so its whole process group can be terminated. Standard output and error\n * are each byte-bounded by `limit` (default {@link PROCESS_OUTPUT}). A positive `timeout` arms a\n * timer that terminates the child through `SIGTERM`, then `SIGKILL` after `grace`\n * (default {@link PROCESS_GRACE}); aborting `signal` terminates the same way. The child's\n * `environment` merges over the parent, then `options.environment` on top, and `options.input`\n * overrides `command.input`. Unless `reject` is `false`, a failed run rejects with a\n * {@link createRunError} carrying the {@link RunResult}.\n *\n * @param command - The executable, arguments, and optional environment and input\n * @param options - Working directory, timeout, grace, signal, capture limit, and reject behavior\n * @returns The settled run outcome\n * @throws A {@link ProcessError} carrying the {@link RunResult} when the run failed and `reject` is not `false`\n *\n * @example\n * ```ts\n * const result = await run({ file: 'git', arguments: ['status'] }, { workspace: process.cwd() })\n * ```\n */\nexport async function run(command: ProcessCommand, options?: RunOptions): Promise<RunResult> {\n\tconst limit = options?.limit ?? PROCESS_OUTPUT\n\tconst grace = options?.grace ?? PROCESS_GRACE\n\tconst timeout = options?.timeout ?? 0\n\tconst reject = options?.reject ?? true\n\tconst input = options?.input ?? command.input\n\tconst line = commandLine(command)\n\tconst settled = Promise.withResolvers<RunResult>()\n\tconst terminate = new AbortController()\n\tconst cleanup = new AbortController()\n\tlet stdout: Buffer = Buffer.alloc(0)\n\tlet stderr: Buffer = Buffer.alloc(0)\n\tlet timedOut = false\n\tlet killTimer: ReturnType<typeof setTimeout> | undefined\n\tlet timeoutTimer: ReturnType<typeof setTimeout> | undefined\n\tlet done = false\n\n\tconst child: ChildProcessWithoutNullStreams = spawn(command.file, [...command.arguments], {\n\t\tcwd: options?.workspace ?? process.cwd(),\n\t\tdetached: process.platform !== 'win32',\n\t\tenv: mergeEnvironment(command.environment, options?.environment),\n\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\tshell: requiresShell(command.file),\n\t\twindowsHide: true,\n\t})\n\n\tterminate.signal.addEventListener(\n\t\t'abort',\n\t\t() => {\n\t\t\tkillProcess(child, 'SIGTERM')\n\t\t\tkillTimer = setTimeout(() => killProcess(child, 'SIGKILL'), grace)\n\t\t},\n\t\t{ once: true },\n\t)\n\n\tchild.stdin.on('error', () => undefined)\n\tchild.stdout.on('data', (chunk: unknown) => {\n\t\tif (Buffer.isBuffer(chunk)) stdout = trimHead(Buffer.concat([stdout, chunk]), limit)\n\t})\n\tchild.stderr.on('data', (chunk: unknown) => {\n\t\tif (Buffer.isBuffer(chunk)) stderr = trimHead(Buffer.concat([stderr, chunk]), limit)\n\t})\n\tchild.once('error', () => {\n\t\tif (done) return\n\t\tdone = true\n\t\tcleanup.abort()\n\t\tclearTimeout(killTimer)\n\t\tclearTimeout(timeoutTimer)\n\t\tsettled.resolve(buildRunResult(line, stdout, stderr, null, null, false, limit))\n\t})\n\tchild.once('close', (code: number | null, signal: NodeJS.Signals | null) => {\n\t\tif (done) return\n\t\tdone = true\n\t\tcleanup.abort()\n\t\tclearTimeout(killTimer)\n\t\tclearTimeout(timeoutTimer)\n\t\tsettled.resolve(buildRunResult(line, stdout, stderr, code, signal, timedOut, limit))\n\t})\n\n\tif (input !== undefined) child.stdin.write(input)\n\tchild.stdin.end()\n\tif (timeout > 0) {\n\t\ttimeoutTimer = setTimeout(() => {\n\t\t\ttimedOut = true\n\t\t\tterminate.abort()\n\t\t}, timeout)\n\t}\n\tif (options?.signal !== undefined) {\n\t\tconst signal = options.signal\n\t\tsignal.addEventListener('abort', () => terminate.abort(), {\n\t\t\tonce: true,\n\t\t\tsignal: cleanup.signal,\n\t\t})\n\t\tif (signal.aborted) terminate.abort()\n\t}\n\n\tconst result = await settled.promise\n\tif (result.failed && reject) throw createRunError(result)\n\treturn result\n}\n\n/**\n * Runs one command to completion synchronously, buffering its output, and returns the outcome.\n *\n * @remarks\n * The synchronous counterpart of {@link run}. The child is spawned through the same cross-platform\n * command resolver ({@link requiresShell}). Standard output and error are each byte-bounded by\n * `limit` (default {@link PROCESS_OUTPUT}). A positive `timeout` is enforced by the host, which\n * ends the child with `SIGKILL` when it elapses. The environment and input follow the same merge\n * as {@link run}. Unless `reject` is `false`, a failed run throws a {@link createRunError} carrying\n * the {@link RunResult}.\n *\n * @param command - The executable, arguments, and optional environment and input\n * @param options - Working directory, timeout, signal, capture limit, and reject behavior\n * @returns The run outcome\n * @throws A {@link ProcessError} carrying the {@link RunResult} when the run failed and `reject` is not `false`\n *\n * @example\n * ```ts\n * const result = runSync({ file: 'git', arguments: ['--version'] }, { reject: false })\n * ```\n */\nexport function runSync(command: ProcessCommand, options?: RunOptions): RunResult {\n\tconst limit = options?.limit ?? PROCESS_OUTPUT\n\tconst timeout = options?.timeout ?? 0\n\tconst reject = options?.reject ?? true\n\tconst input = options?.input ?? command.input\n\tconst line = commandLine(command)\n\tconst outcome = spawnSync(command.file, [...command.arguments], {\n\t\tcwd: options?.workspace ?? process.cwd(),\n\t\tenv: mergeEnvironment(command.environment, options?.environment),\n\t\tencoding: 'buffer',\n\t\tmaxBuffer: limit,\n\t\tshell: requiresShell(command.file),\n\t\twindowsHide: true,\n\t\t...(input !== undefined ? { input } : {}),\n\t\t...(options?.signal !== undefined ? { signal: options.signal } : {}),\n\t\t...(timeout > 0 ? { timeout, killSignal: 'SIGKILL' } : {}),\n\t})\n\tconst error = outcome.error\n\tconst timedOut = error !== undefined && 'code' in error && error.code === 'ETIMEDOUT'\n\tconst stdout = Buffer.isBuffer(outcome.stdout) ? outcome.stdout : Buffer.alloc(0)\n\tconst stderr = Buffer.isBuffer(outcome.stderr) ? outcome.stderr : Buffer.alloc(0)\n\tconst result = buildRunResult(\n\t\tline,\n\t\tstdout,\n\t\tstderr,\n\t\toutcome.status,\n\t\toutcome.signal,\n\t\ttimedOut,\n\t\tlimit,\n\t)\n\tif (result.failed && reject) throw createRunError(result)\n\treturn result\n}\n","import type { ChildProcessWithoutNullStreams } from 'node:child_process'\nimport type { Interface as ReadLineInterface } from 'node:readline'\nimport type { EmitterInterface } from '@orkestrel/emitter'\nimport type { ProcessEventMap, ProcessExit, ProcessInterface, ProcessOptions } from '@src/core'\nimport { Buffer } from 'node:buffer'\nimport { spawn } from 'node:child_process'\nimport { createInterface } from 'node:readline'\nimport { StringDecoder } from 'node:string_decoder'\nimport { Emitter } from '@orkestrel/emitter'\nimport { PROCESS_EVIDENCE } from '@src/core'\nimport { killProcess, mergeEnvironment, trimTail } from './helpers.js'\n\n/**\n * One supervised child process with eagerly framed output and bounded termination.\n *\n * @remarks\n * Standard output is drained eagerly through `readline`, so `exit` resolves and a late consumer of\n * `lines` still receives every framed line, including a final line written without a trailing\n * newline. Standard error is decoded and forwarded live as the `stderr` event while a byte-bounded\n * raw tail is retained as `evidence`. The typed `emitter` also carries the terminal `exit`,\n * alongside the `exit` promise. `stop` terminates the child through `SIGTERM`, then `SIGKILL` after\n * `grace`, and is idempotent; `destroy` stops the child and destroys the emitter last.\n *\n * @example\n * ```ts\n * import { Process } from '@orkestrel/process/server'\n *\n * const child = new Process({\n * \tcommand: { file: 'node', arguments: ['--version'] },\n * \tworkspace: process.cwd(),\n * \tgrace: 5000,\n * })\n * const exit = await child.exit\n * await child.destroy()\n * ```\n */\nexport class Process implements ProcessInterface {\n\treadonly #emitter: Emitter<ProcessEventMap>\n\treadonly #child: ChildProcessWithoutNullStreams\n\treadonly #reader: ReadLineInterface\n\treadonly #grace: number\n\treadonly #limit: number\n\treadonly #decoder = new StringDecoder('utf8')\n\treadonly #exit = Promise.withResolvers<ProcessExit>()\n\treadonly #lines: AsyncIterable<string>\n\treadonly #buffer: string[] = []\n\treadonly #waiters: Array<PromiseWithResolvers<IteratorResult<string, void>>> = []\n\treadonly #signal: AbortSignal | undefined\n\treadonly #abort: EventListener | undefined\n\t#tail: Buffer = Buffer.alloc(0)\n\t#closed = false\n\t#ended = false\n\t#stopping: Promise<void> | undefined\n\t#ending: Promise<void> | undefined\n\n\t/**\n\t * Spawn one child process and begin eager stream capture.\n\t *\n\t * @param options - Command, workspace, termination, evidence, stdin, and observation settings\n\t */\n\tconstructor(options: ProcessOptions) {\n\t\tconst on = options.on\n\t\tconst error = options.error\n\t\tthis.#emitter = new Emitter<ProcessEventMap>({\n\t\t\t...(on === undefined ? {} : { on }),\n\t\t\t...(error === undefined ? {} : { error }),\n\t\t})\n\t\tthis.#grace = options.grace\n\t\tthis.#limit = options.evidence ?? PROCESS_EVIDENCE\n\t\tthis.#signal = options.signal\n\t\tthis.#lines = Object.freeze({ [Symbol.asyncIterator]: this.#iterate.bind(this) })\n\t\tthis.#child = spawn(options.command.file, [...options.command.arguments], {\n\t\t\tcwd: options.workspace,\n\t\t\tdetached: process.platform !== 'win32',\n\t\t\tenv: mergeEnvironment(options.command.environment),\n\t\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\t\twindowsHide: true,\n\t\t})\n\t\tthis.#reader = createInterface({ input: this.#child.stdout, crlfDelay: Infinity })\n\t\tthis.#reader.on('line', this.#push.bind(this))\n\t\tthis.#reader.once('close', this.#finish.bind(this))\n\t\tthis.#child.once('error', () => undefined)\n\t\tthis.#child.once('close', this.#close.bind(this))\n\t\tthis.#child.stdin.on('error', () => undefined)\n\t\tthis.#child.stderr.on('data', this.#retain.bind(this))\n\t\tif (options.command.input !== undefined) this.#child.stdin.write(options.command.input)\n\t\tif (options.writable !== true) this.#child.stdin.end()\n\t\tif (this.#signal !== undefined) {\n\t\t\tthis.#abort = this.#terminate.bind(this)\n\t\t\tthis.#signal.addEventListener('abort', this.#abort, { once: true })\n\t\t\tif (this.#signal.aborted) void this.stop()\n\t\t}\n\t}\n\n\t/** The typed lifecycle observation surface. */\n\tget emitter(): EmitterInterface<ProcessEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\t/** The eagerly captured stdout lines, in arrival order, ending when the child's stdout closes. */\n\tget lines(): AsyncIterable<string> {\n\t\treturn this.#lines\n\t}\n\n\t/** The decoded byte-bounded stderr tail. */\n\tget evidence(): string {\n\t\treturn this.#tail.toString('utf8')\n\t}\n\n\t/** The terminal child state, observed once from the close event. */\n\tget exit(): Promise<ProcessExit> {\n\t\treturn this.#exit.promise\n\t}\n\n\t/**\n\t * Write one line to the open standard-input channel.\n\t *\n\t * @param text - The line text without its trailing newline\n\t * @returns True when the channel accepted the line without backpressure; false otherwise\n\t */\n\tsend(text: string): boolean {\n\t\tif (this.#closed || this.#child.stdin.destroyed || this.#child.stdin.writableEnded) return false\n\t\ttry {\n\t\t\treturn this.#child.stdin.write(`${text}\\n`)\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Terminate the child process tree and await its observed exit.\n\t *\n\t * @returns A promise that resolves after bounded termination\n\t */\n\tstop(): Promise<void> {\n\t\tif (this.#stopping !== undefined) return this.#stopping\n\t\tthis.#stopping = this.#kill()\n\t\treturn this.#stopping\n\t}\n\n\t/**\n\t * Stop the child and destroy the observation emitter.\n\t *\n\t * @returns The stable barrier shared by every call\n\t */\n\tdestroy(): Promise<void> {\n\t\tif (this.#ending !== undefined) return this.#ending\n\t\tthis.#ending = this.#teardown()\n\t\treturn this.#ending\n\t}\n\n\t#iterate(): AsyncIterator<string, void, void> {\n\t\treturn Object.freeze({ next: this.#next.bind(this) })\n\t}\n\n\t#next(): Promise<IteratorResult<string, void>> {\n\t\tconst line = this.#buffer.shift()\n\t\tif (line !== undefined) return Promise.resolve(Object.freeze({ done: false, value: line }))\n\t\tif (this.#ended) return Promise.resolve(Object.freeze({ done: true, value: undefined }))\n\t\tconst waiter = Promise.withResolvers<IteratorResult<string, void>>()\n\t\tthis.#waiters.push(waiter)\n\t\treturn waiter.promise\n\t}\n\n\t#push(line: string): void {\n\t\tif (this.#ended) return\n\t\tconst waiter = this.#waiters.shift()\n\t\tif (waiter === undefined) {\n\t\t\tthis.#buffer.push(line)\n\t\t\treturn\n\t\t}\n\t\twaiter.resolve(Object.freeze({ done: false, value: line }))\n\t}\n\n\t#finish(): void {\n\t\tif (this.#ended) return\n\t\tthis.#ended = true\n\t\tfor (const waiter of this.#waiters) {\n\t\t\twaiter.resolve(Object.freeze({ done: true, value: undefined }))\n\t\t}\n\t\tthis.#waiters.length = 0\n\t}\n\n\t#retain(chunk: unknown): void {\n\t\tif (!Buffer.isBuffer(chunk) || this.#closed) return\n\t\tthis.#tail = trimTail(Buffer.concat([this.#tail, chunk]), this.#limit)\n\t\tconst text = this.#decoder.write(chunk)\n\t\tif (text.length > 0) this.#emitter.emit('stderr', text)\n\t}\n\n\t#close(code: number | null, signal: NodeJS.Signals | null): void {\n\t\tif (this.#closed) return\n\t\tconst suffix = this.#decoder.end()\n\t\tif (suffix.length > 0) this.#emitter.emit('stderr', suffix)\n\t\tthis.#closed = true\n\t\tif (this.#signal !== undefined && this.#abort !== undefined) {\n\t\t\tthis.#signal.removeEventListener('abort', this.#abort)\n\t\t}\n\t\tthis.#reader.close()\n\t\tconst exit = Object.freeze({ code, signal })\n\t\tthis.#exit.resolve(exit)\n\t\tthis.#emitter.emit('exit', exit)\n\t}\n\n\t#terminate(): void {\n\t\tvoid this.stop()\n\t}\n\n\tasync #kill(): Promise<void> {\n\t\tif (this.#closed) return\n\t\tkillProcess(this.#child, 'SIGTERM')\n\t\tconst grace = Promise.withResolvers<void>()\n\t\tconst timer = setTimeout(grace.resolve, this.#grace)\n\t\tawait Promise.race([this.#exit.promise, grace.promise])\n\t\tclearTimeout(timer)\n\t\tif (this.#closed) return\n\t\tkillProcess(this.#child, 'SIGKILL')\n\t\tawait this.#exit.promise\n\t}\n\n\tasync #teardown(): Promise<void> {\n\t\tawait this.stop()\n\t\tthis.#emitter.destroy()\n\t}\n}\n","import type { EmitterInterface } from '@orkestrel/emitter'\nimport type {\n\tProcessExit,\n\tProcessInterface,\n\tProcessManagerEventMap,\n\tProcessManagerInterface,\n\tProcessManagerOptions,\n\tProcessOptions,\n} from '@src/core'\nimport { Emitter } from '@orkestrel/emitter'\nimport { createDuplicateError } from '@src/core'\nimport { Process } from './Process.js'\n\n/**\n * A keyed registry of live supervised child processes.\n *\n * @remarks\n * A child launched under an id joins the registry and emits `launch`; when it settles it removes\n * itself and emits `exit`, so `count` and `processes` reflect only live children. `launch` throws\n * a {@link createDuplicateError} when the id is already live — a spawn fault surfaces through the\n * returned child's `exit`, never from `launch`. `destroy` stops every child, then destroys the\n * registry emitter last.\n *\n * @example\n * ```ts\n * import { ProcessManager } from '@orkestrel/process/server'\n *\n * const manager = new ProcessManager()\n * const child = manager.launch('build', {\n * \tcommand: { file: 'node', arguments: ['build.js'] },\n * \tworkspace: process.cwd(),\n * \tgrace: 5000,\n * })\n * await child.exit\n * await manager.destroy()\n * ```\n */\nexport class ProcessManager implements ProcessManagerInterface {\n\treadonly #emitter: Emitter<ProcessManagerEventMap>\n\treadonly #children = new Map<string, ProcessInterface>()\n\t#ending: Promise<void> | undefined\n\n\t/**\n\t * Construct a process registry.\n\t *\n\t * @param options - Initial fleet-level observation hooks and listener-error handling\n\t */\n\tconstructor(options?: ProcessManagerOptions) {\n\t\tconst on = options?.on\n\t\tconst error = options?.error\n\t\tthis.#emitter = new Emitter<ProcessManagerEventMap>({\n\t\t\t...(on === undefined ? {} : { on }),\n\t\t\t...(error === undefined ? {} : { error }),\n\t\t})\n\t}\n\n\t/** The typed fleet-level observation surface. */\n\tget emitter(): EmitterInterface<ProcessManagerEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\t/** The number of live children. */\n\tget count(): number {\n\t\treturn this.#children.size\n\t}\n\n\t/**\n\t * The live child under `id`, or `undefined` when none is.\n\t *\n\t * @param id - The registry key\n\t * @returns The child, or `undefined`\n\t */\n\tprocess(id: string): ProcessInterface | undefined {\n\t\treturn this.#children.get(id)\n\t}\n\n\t/**\n\t * A snapshot of every live child.\n\t *\n\t * @returns The live children in launch order\n\t */\n\tprocesses(): readonly ProcessInterface[] {\n\t\treturn [...this.#children.values()]\n\t}\n\n\t/**\n\t * Spawn and register one child under `id`.\n\t *\n\t * @param id - The registry key, unique among live children\n\t * @param options - The child construction options\n\t * @returns The launched child\n\t * @throws A {@link ProcessError} coded `duplicate` when `id` is already live\n\t */\n\tlaunch(id: string, options: ProcessOptions): ProcessInterface {\n\t\tif (this.#children.has(id)) throw createDuplicateError(id)\n\t\tconst child = new Process(options)\n\t\tthis.#children.set(id, child)\n\t\tchild.emitter.on('exit', (exit) => this.#evict(id, child, exit))\n\t\tthis.#emitter.emit('launch', id)\n\t\treturn child\n\t}\n\n\t/**\n\t * Terminate the named children and await their exit.\n\t *\n\t * @param ids - The registry keys to stop\n\t * @returns True when every named child stopped, false when any id was not live\n\t */\n\tstop(ids: readonly string[]): Promise<boolean>\n\t/**\n\t * Terminate one child and await its exit.\n\t *\n\t * @param id - The registry key to stop\n\t * @returns True when the child was live and stopped, false when the id was not live\n\t */\n\tstop(id: string): Promise<boolean>\n\t/**\n\t * Terminate every live child and await their exit.\n\t *\n\t * @returns A promise that resolves after all children stop\n\t */\n\tstop(): Promise<void>\n\tstop(target?: string | readonly string[]): Promise<boolean | void> {\n\t\tif (target === undefined) return this.#stopAll()\n\t\tif (typeof target === 'string') return this.#stopOne(target)\n\t\treturn this.#stopMany(target)\n\t}\n\n\t/**\n\t * Stop every child, then destroy the registry emitter last.\n\t *\n\t * @returns The stable barrier shared by every call\n\t */\n\tdestroy(): Promise<void> {\n\t\tif (this.#ending !== undefined) return this.#ending\n\t\tthis.#ending = this.#teardown()\n\t\treturn this.#ending\n\t}\n\n\t#evict(id: string, child: ProcessInterface, exit: ProcessExit): void {\n\t\tif (this.#children.get(id) !== child) return\n\t\tthis.#children.delete(id)\n\t\tthis.#emitter.emit('exit', id, exit)\n\t}\n\n\tasync #stopOne(id: string): Promise<boolean> {\n\t\tconst child = this.#children.get(id)\n\t\tif (child === undefined) return false\n\t\tawait child.stop()\n\t\treturn true\n\t}\n\n\tasync #stopMany(ids: readonly string[]): Promise<boolean> {\n\t\tconst stopped = await Promise.all(ids.map((id) => this.#stopOne(id)))\n\t\treturn stopped.every((ok) => ok)\n\t}\n\n\tasync #stopAll(): Promise<void> {\n\t\tawait Promise.all([...this.#children.values()].map((child) => child.stop()))\n\t}\n\n\tasync #teardown(): Promise<void> {\n\t\tawait Promise.all([...this.#children.values()].map((child) => child.destroy()))\n\t\tthis.#emitter.destroy()\n\t}\n}\n","import type {\n\tProcessInterface,\n\tProcessManagerInterface,\n\tProcessManagerOptions,\n\tProcessOptions,\n} from '@src/core'\nimport { Process } from './Process.js'\nimport { ProcessManager } from './ProcessManager.js'\n\n/**\n * Creates one supervised child process.\n *\n * @param options - Command, workspace, termination, evidence, stdin, and observation settings\n * @returns The launched {@link ProcessInterface}\n *\n * @example\n * ```ts\n * import { createProcess } from '@orkestrel/process/server'\n *\n * const child = createProcess({\n * \tcommand: { file: 'node', arguments: ['--version'] },\n * \tworkspace: process.cwd(),\n * \tgrace: 5000,\n * })\n * ```\n */\nexport function createProcess(options: ProcessOptions): ProcessInterface {\n\treturn new Process(options)\n}\n\n/**\n * Creates one keyed registry of supervised child processes.\n *\n * @param options - Initial fleet-level observation hooks and listener-error handling\n * @returns The {@link ProcessManagerInterface} registry\n *\n * @example\n * ```ts\n * import { createProcessManager } from '@orkestrel/process/server'\n *\n * const manager = createProcessManager()\n * ```\n */\nexport function createProcessManager(options?: ProcessManagerOptions): ProcessManagerInterface {\n\treturn new ProcessManager(options)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["#emitter","#child","#reader","#grace","#limit","#decoder","#exit","#lines","#buffer","#waiters","#signal","#abort","#iterate","#push","#finish","#close","#retain","#terminate","#tail","#closed","#stopping","#kill","#ending","#teardown","#next","#ended","#emitter","#children","#evict","#stopAll","#stopOne","#stopMany","#ending","#teardown"],"sources":["../../../src/server/helpers.ts","../../../src/server/Process.ts","../../../src/server/ProcessManager.ts","../../../src/server/factories.ts"],"sourcesContent":["import type { ChildProcessWithoutNullStreams } from 'node:child_process'\nimport type { ProcessCommand, RunOptions, RunResult } from '@src/core'\nimport { Buffer } from 'node:buffer'\nimport { spawn, spawnSync } from 'node:child_process'\nimport { extname } from 'node:path'\nimport { createRunError, PROCESS_GRACE, PROCESS_OUTPUT } from '@src/core'\n\n/**\n * Signals one owned child process, or its detached process group on a POSIX host.\n *\n * @remarks\n * On a POSIX host a child spawned `detached` leads its own process group, so signalling the\n * negated pid reaches the whole tree. On Windows, or when no pid is available, the signal goes to\n * the child directly. A throw during signalling is swallowed: the process can exit and win the\n * signal race, and the child's close event remains the authoritative terminal state.\n *\n * @param child - The child process signal boundary\n * @param signal - The signal to deliver\n * @returns Nothing\n *\n * @example\n * ```ts\n * killProcess(child, 'SIGTERM')\n * ```\n */\nexport function killProcess(\n\tchild: { readonly pid?: number | undefined; kill(signal: NodeJS.Signals): boolean },\n\tsignal: NodeJS.Signals,\n): void {\n\ttry {\n\t\tif (process.platform === 'win32' || child.pid === undefined) {\n\t\t\tchild.kill(signal)\n\t\t\treturn\n\t\t}\n\t\tprocess.kill(-child.pid, signal)\n\t} catch {\n\t\t// Exit can win the signal race; the close event remains authoritative.\n\t}\n}\n\n/**\n * Trims a buffer to at most `limit` trailing bytes without splitting a UTF-8 sequence.\n *\n * @remarks\n * Keeps the tail — the diagnostic end of a stderr stream. When the cut point lands inside a\n * multibyte sequence, the start advances past the leading continuation bytes so the retained\n * bytes always begin on a code-point boundary.\n *\n * @param buffer - The accumulated bytes\n * @param limit - The maximum retained byte count\n * @returns The trailing bytes, at most `limit` and never starting mid-sequence\n *\n * @example\n * ```ts\n * trimTail(Buffer.from('hello'), 3) // <Buffer 6c 6c 6f>\n * ```\n */\nexport function trimTail(buffer: Buffer, limit: number): Buffer {\n\tif (buffer.byteLength <= limit) return buffer\n\tlet start = buffer.byteLength - limit\n\twhile (start < buffer.byteLength) {\n\t\tconst byte = buffer[start]\n\t\tif (byte === undefined || (byte & 0xc0) !== 0x80) break\n\t\tstart += 1\n\t}\n\treturn Buffer.from(buffer.subarray(start))\n}\n\n/**\n * Trims a buffer to at most `limit` leading bytes without splitting a UTF-8 sequence.\n *\n * @remarks\n * Keeps the head — the captured start of a one-shot run's output. When the cut point lands inside\n * a multibyte sequence, the end retreats to the sequence's start so the retained bytes always end\n * on a code-point boundary and decode without a replacement character.\n *\n * @param buffer - The accumulated bytes\n * @param limit - The maximum retained byte count\n * @returns The leading bytes, at most `limit` and never ending mid-sequence\n *\n * @example\n * ```ts\n * trimHead(Buffer.from('hello'), 3) // <Buffer 68 65 6c>\n * ```\n */\nexport function trimHead(buffer: Buffer, limit: number): Buffer {\n\tif (buffer.byteLength <= limit) return buffer\n\tlet end = limit\n\twhile (end > 0) {\n\t\tconst byte = buffer[end]\n\t\tif (byte === undefined || (byte & 0xc0) !== 0x80) break\n\t\tend -= 1\n\t}\n\treturn Buffer.from(buffer.subarray(0, end))\n}\n\n/**\n * Checks whether a command file must be spawned through a shell on the current host.\n *\n * @remarks\n * Node v22+ refuses to spawn a Windows `.cmd` or `.bat` batch file directly and returns `EINVAL`,\n * and a bare command name such as `git` resolves through `PATHEXT` to a batch shim rather than an\n * executable. Both cases run through the shell. A POSIX host, and a Windows path carrying a real\n * executable extension such as `.exe`, spawn directly. When the result is `true`, treat the\n * command file and arguments as trusted: shell invocation exposes shell metacharacters.\n *\n * @param file - The command executable name or path\n * @returns True when the file must run through a shell; false when it spawns directly\n *\n * @example\n * ```ts\n * requiresShell('git') // true on Windows, false on POSIX\n * ```\n */\nexport function requiresShell(file: string): boolean {\n\tif (process.platform !== 'win32') return false\n\tconst lower = file.toLowerCase()\n\treturn lower.endsWith('.cmd') || lower.endsWith('.bat') || extname(file) === ''\n}\n\n/**\n * Renders one command into its diagnostic command line.\n *\n * @param command - The executable and its argument vector\n * @returns The space-joined command line, for a {@link RunResult} and error messages\n *\n * @example\n * ```ts\n * commandLine({ file: 'git', arguments: ['status'] }) // 'git status'\n * ```\n */\nexport function commandLine(command: ProcessCommand): string {\n\treturn [command.file, ...command.arguments].join(' ')\n}\n\n/**\n * Merges environment overrides over the current process environment.\n *\n * @remarks\n * Later maps override earlier ones and an `undefined` value unsets a key. The base is the parent\n * process environment, so a child inherits it unless a key is overridden or unset.\n *\n * @param base - The command's own environment overrides\n * @param override - Per-invocation overrides applied last\n * @returns The merged environment for a spawn\n *\n * @example\n * ```ts\n * mergeEnvironment({ TOKEN: 'a' }, { TOKEN: undefined }) // TOKEN unset\n * ```\n */\nexport function mergeEnvironment(\n\tbase?: Readonly<Record<string, string | undefined>>,\n\toverride?: Readonly<Record<string, string | undefined>>,\n): NodeJS.ProcessEnv {\n\treturn { ...process.env, ...base, ...override }\n}\n\n/**\n * Builds one settled {@link RunResult} from a completed one-shot run's captured bytes and exit.\n *\n * @remarks\n * `failed` is derived: a run failed when it timed out, was ended by a signal, or exited with a\n * code other than `0` — a `null` code from a spawn fault is therefore a failure. Both output\n * buffers are byte-bounded by `limit` before decoding.\n *\n * @param command - The diagnostic command line\n * @param stdout - The captured standard output bytes\n * @param stderr - The captured standard error bytes\n * @param code - The exit code, or `null` when a signal or spawn fault ended the run\n * @param signal - The terminating signal, or `null`\n * @param timedOut - If `true`, the run's own timeout elapsed; if `false`, it did not\n * @param limit - The maximum captured bytes for each stream\n * @returns The frozen run outcome\n *\n * @example\n * ```ts\n * buildRunResult('git status', Buffer.from('ok'), Buffer.alloc(0), 0, null, false, 1024)\n * ```\n */\nexport function buildRunResult(\n\tcommand: string,\n\tstdout: Buffer,\n\tstderr: Buffer,\n\tcode: number | null,\n\tsignal: string | null,\n\ttimedOut: boolean,\n\tlimit: number,\n): RunResult {\n\treturn Object.freeze({\n\t\tcommand,\n\t\tstdout: trimHead(stdout, limit).toString('utf8'),\n\t\tstderr: trimHead(stderr, limit).toString('utf8'),\n\t\tcode,\n\t\tsignal,\n\t\tfailed: timedOut || signal !== null || code !== 0,\n\t\ttimedOut,\n\t})\n}\n\n/**\n * Runs one command to completion, buffering its output, and settles with the outcome.\n *\n * @remarks\n * The child is spawned through the cross-platform command resolver ({@link requiresShell}) and, on\n * a POSIX host, detached so its whole process group can be terminated. Standard output and error\n * are each byte-bounded by `limit` (default {@link PROCESS_OUTPUT}). A positive `timeout` arms a\n * timer that terminates the child through `SIGTERM`, then `SIGKILL` after `grace`\n * (default {@link PROCESS_GRACE}); aborting `signal` terminates the same way. The child's\n * `environment` merges over the parent, then `options.environment` on top, and `options.input`\n * overrides `command.input`. Unless `reject` is `false`, a failed run rejects with a\n * {@link createRunError} carrying the {@link RunResult}.\n *\n * @param command - The executable, arguments, and optional environment and input\n * @param options - Working directory, timeout, grace, signal, capture limit, and reject behavior\n * @returns The settled run outcome\n * @throws A {@link ProcessError} carrying the {@link RunResult} when the run failed and `reject` is not `false`\n *\n * @example\n * ```ts\n * const result = await run({ file: 'git', arguments: ['status'] }, { workspace: process.cwd() })\n * ```\n */\nexport async function run(command: ProcessCommand, options?: RunOptions): Promise<RunResult> {\n\tconst limit = options?.limit ?? PROCESS_OUTPUT\n\tconst grace = options?.grace ?? PROCESS_GRACE\n\tconst timeout = options?.timeout ?? 0\n\tconst reject = options?.reject ?? true\n\tconst input = options?.input ?? command.input\n\tconst line = commandLine(command)\n\tconst settled = Promise.withResolvers<RunResult>()\n\tconst terminate = new AbortController()\n\tconst cleanup = new AbortController()\n\tlet stdout: Buffer = Buffer.alloc(0)\n\tlet stderr: Buffer = Buffer.alloc(0)\n\tlet timedOut = false\n\tlet launchCause: unknown\n\tlet killTimer: ReturnType<typeof setTimeout> | undefined\n\tlet timeoutTimer: ReturnType<typeof setTimeout> | undefined\n\tlet done = false\n\n\tconst child: ChildProcessWithoutNullStreams = spawn(command.file, [...command.arguments], {\n\t\tcwd: options?.workspace ?? process.cwd(),\n\t\tdetached: process.platform !== 'win32',\n\t\tenv: mergeEnvironment(command.environment, options?.environment),\n\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\tshell: requiresShell(command.file),\n\t\twindowsHide: true,\n\t})\n\n\tterminate.signal.addEventListener(\n\t\t'abort',\n\t\t() => {\n\t\t\tkillProcess(child, 'SIGTERM')\n\t\t\tkillTimer = setTimeout(() => killProcess(child, 'SIGKILL'), grace)\n\t\t},\n\t\t{ once: true },\n\t)\n\n\tchild.stdin.on('error', () => undefined)\n\tchild.stdout.on('data', (chunk: unknown) => {\n\t\tif (Buffer.isBuffer(chunk)) stdout = trimHead(Buffer.concat([stdout, chunk]), limit)\n\t})\n\tchild.stderr.on('data', (chunk: unknown) => {\n\t\tif (Buffer.isBuffer(chunk)) stderr = trimHead(Buffer.concat([stderr, chunk]), limit)\n\t})\n\tchild.once('error', (cause: unknown) => {\n\t\tif (done) return\n\t\tdone = true\n\t\tlaunchCause = cause\n\t\tcleanup.abort()\n\t\tclearTimeout(killTimer)\n\t\tclearTimeout(timeoutTimer)\n\t\tsettled.resolve(buildRunResult(line, stdout, stderr, null, null, false, limit))\n\t})\n\tchild.once('close', (code: number | null, signal: NodeJS.Signals | null) => {\n\t\tif (done) return\n\t\tdone = true\n\t\tcleanup.abort()\n\t\tclearTimeout(killTimer)\n\t\tclearTimeout(timeoutTimer)\n\t\tsettled.resolve(buildRunResult(line, stdout, stderr, code, signal, timedOut, limit))\n\t})\n\n\tif (input !== undefined) child.stdin.write(input)\n\tchild.stdin.end()\n\tif (timeout > 0) {\n\t\ttimeoutTimer = setTimeout(() => {\n\t\t\ttimedOut = true\n\t\t\tterminate.abort()\n\t\t}, timeout)\n\t}\n\tif (options?.signal !== undefined) {\n\t\tconst signal = options.signal\n\t\tsignal.addEventListener('abort', () => terminate.abort(), {\n\t\t\tonce: true,\n\t\t\tsignal: cleanup.signal,\n\t\t})\n\t\tif (signal.aborted) terminate.abort()\n\t}\n\n\tconst result = await settled.promise\n\tif (result.failed && reject) throw createRunError(result, launchCause)\n\treturn result\n}\n\n/**\n * Runs one command to completion synchronously, buffering its output, and returns the outcome.\n *\n * @remarks\n * The synchronous counterpart of {@link run}. The child is spawned through the same cross-platform\n * command resolver ({@link requiresShell}). Standard output and error are each byte-bounded by\n * `limit` (default {@link PROCESS_OUTPUT}). A positive `timeout` is enforced by the host, which\n * ends the child with `SIGKILL` when it elapses. The environment and input follow the same merge\n * as {@link run}. Unless `reject` is `false`, a failed run throws a {@link createRunError} carrying\n * the {@link RunResult}.\n *\n * @param command - The executable, arguments, and optional environment and input\n * @param options - Working directory, timeout, signal, capture limit, and reject behavior\n * @returns The run outcome\n * @throws A {@link ProcessError} carrying the {@link RunResult} when the run failed and `reject` is not `false`\n *\n * @example\n * ```ts\n * const result = runSync({ file: 'git', arguments: ['--version'] }, { reject: false })\n * ```\n */\nexport function runSync(command: ProcessCommand, options?: RunOptions): RunResult {\n\tconst limit = options?.limit ?? PROCESS_OUTPUT\n\tconst timeout = options?.timeout ?? 0\n\tconst reject = options?.reject ?? true\n\tconst input = options?.input ?? command.input\n\tconst line = commandLine(command)\n\tconst outcome = spawnSync(command.file, [...command.arguments], {\n\t\tcwd: options?.workspace ?? process.cwd(),\n\t\tenv: mergeEnvironment(command.environment, options?.environment),\n\t\tencoding: 'buffer',\n\t\tmaxBuffer: limit,\n\t\tshell: requiresShell(command.file),\n\t\twindowsHide: true,\n\t\t...(input !== undefined ? { input } : {}),\n\t\t...(options?.signal !== undefined ? { signal: options.signal } : {}),\n\t\t...(timeout > 0 ? { timeout, killSignal: 'SIGKILL' } : {}),\n\t})\n\tconst error = outcome.error\n\tconst timedOut = error !== undefined && 'code' in error && error.code === 'ETIMEDOUT'\n\tconst stdout = Buffer.isBuffer(outcome.stdout) ? outcome.stdout : Buffer.alloc(0)\n\tconst stderr = Buffer.isBuffer(outcome.stderr) ? outcome.stderr : Buffer.alloc(0)\n\tconst result = buildRunResult(\n\t\tline,\n\t\tstdout,\n\t\tstderr,\n\t\toutcome.status,\n\t\toutcome.signal,\n\t\ttimedOut,\n\t\tlimit,\n\t)\n\tif (result.failed && reject) throw createRunError(result, outcome.error)\n\treturn result\n}\n","import type { ChildProcessWithoutNullStreams } from 'node:child_process'\nimport type { Interface as ReadLineInterface } from 'node:readline'\nimport type { EmitterInterface } from '@orkestrel/emitter'\nimport type { ProcessEventMap, ProcessExit, ProcessInterface, ProcessOptions } from '@src/core'\nimport { Buffer } from 'node:buffer'\nimport { spawn } from 'node:child_process'\nimport { createInterface } from 'node:readline'\nimport { StringDecoder } from 'node:string_decoder'\nimport { Emitter } from '@orkestrel/emitter'\nimport { PROCESS_EVIDENCE } from '@src/core'\nimport { killProcess, mergeEnvironment, trimTail } from './helpers.js'\n\n/**\n * One supervised child process with eagerly framed output and bounded termination.\n *\n * @remarks\n * Standard output is drained eagerly through `readline`, so `exit` resolves and a late consumer of\n * `lines` still receives every framed line, including a final line written without a trailing\n * newline. Standard error is decoded and forwarded live as the `stderr` event while a byte-bounded\n * raw tail is retained as `evidence`. The typed `emitter` also carries the child `error` cause on a\n * spawn fault and the terminal `exit`, alongside the `exit` promise. `stop` terminates the child\n * through `SIGTERM`, then `SIGKILL` after\n * `grace`, and is idempotent; `destroy` stops the child and destroys the emitter last.\n *\n * @example\n * ```ts\n * import { Process } from '@orkestrel/process/server'\n *\n * const child = new Process({\n * \tcommand: { file: 'node', arguments: ['--version'] },\n * \tworkspace: process.cwd(),\n * \tgrace: 5000,\n * })\n * const exit = await child.exit\n * await child.destroy()\n * ```\n */\nexport class Process implements ProcessInterface {\n\treadonly #emitter: Emitter<ProcessEventMap>\n\treadonly #child: ChildProcessWithoutNullStreams\n\treadonly #reader: ReadLineInterface\n\treadonly #grace: number\n\treadonly #limit: number\n\treadonly #decoder = new StringDecoder('utf8')\n\treadonly #exit = Promise.withResolvers<ProcessExit>()\n\treadonly #lines: AsyncIterable<string>\n\treadonly #buffer: string[] = []\n\treadonly #waiters: Array<PromiseWithResolvers<IteratorResult<string, void>>> = []\n\treadonly #signal: AbortSignal | undefined\n\treadonly #abort: EventListener | undefined\n\t#tail: Buffer = Buffer.alloc(0)\n\t#closed = false\n\t#ended = false\n\t#stopping: Promise<void> | undefined\n\t#ending: Promise<void> | undefined\n\n\t/**\n\t * Spawn one child process and begin eager stream capture.\n\t *\n\t * @param options - Command, workspace, termination, evidence, stdin, and observation settings\n\t */\n\tconstructor(options: ProcessOptions) {\n\t\tconst on = options.on\n\t\tconst error = options.error\n\t\tthis.#emitter = new Emitter<ProcessEventMap>({\n\t\t\t...(on === undefined ? {} : { on }),\n\t\t\t...(error === undefined ? {} : { error }),\n\t\t})\n\t\tthis.#grace = options.grace\n\t\tthis.#limit = options.evidence ?? PROCESS_EVIDENCE\n\t\tthis.#signal = options.signal\n\t\tthis.#lines = Object.freeze({ [Symbol.asyncIterator]: this.#iterate.bind(this) })\n\t\tthis.#child = spawn(options.command.file, [...options.command.arguments], {\n\t\t\tcwd: options.workspace,\n\t\t\tdetached: process.platform !== 'win32',\n\t\t\tenv: mergeEnvironment(options.command.environment),\n\t\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\t\twindowsHide: true,\n\t\t})\n\t\tthis.#reader = createInterface({ input: this.#child.stdout, crlfDelay: Infinity })\n\t\tthis.#reader.on('line', this.#push.bind(this))\n\t\tthis.#reader.once('close', this.#finish.bind(this))\n\t\tthis.#child.once('error', (cause: unknown) => this.#emitter.emit('error', cause))\n\t\tthis.#child.once('close', this.#close.bind(this))\n\t\tthis.#child.stdin.on('error', () => undefined)\n\t\tthis.#child.stderr.on('data', this.#retain.bind(this))\n\t\tif (options.command.input !== undefined) this.#child.stdin.write(options.command.input)\n\t\tif (options.writable !== true) this.#child.stdin.end()\n\t\tif (this.#signal !== undefined) {\n\t\t\tthis.#abort = this.#terminate.bind(this)\n\t\t\tthis.#signal.addEventListener('abort', this.#abort, { once: true })\n\t\t\tif (this.#signal.aborted) void this.stop()\n\t\t}\n\t}\n\n\t/** The typed lifecycle observation surface. */\n\tget emitter(): EmitterInterface<ProcessEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\t/** The eagerly captured stdout lines, in arrival order, ending when the child's stdout closes. */\n\tget lines(): AsyncIterable<string> {\n\t\treturn this.#lines\n\t}\n\n\t/** The decoded byte-bounded stderr tail. */\n\tget evidence(): string {\n\t\treturn this.#tail.toString('utf8')\n\t}\n\n\t/** The terminal child state, observed once from the close event. */\n\tget exit(): Promise<ProcessExit> {\n\t\treturn this.#exit.promise\n\t}\n\n\t/**\n\t * Write one line to the open standard-input channel.\n\t *\n\t * @param text - The line text without its trailing newline\n\t * @returns True when the channel accepted the line without backpressure; false otherwise\n\t */\n\tsend(text: string): boolean {\n\t\tif (this.#closed || this.#child.stdin.destroyed || this.#child.stdin.writableEnded) return false\n\t\ttry {\n\t\t\treturn this.#child.stdin.write(`${text}\\n`)\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Terminate the child process tree and await its observed exit.\n\t *\n\t * @returns A promise that resolves after bounded termination\n\t */\n\tstop(): Promise<void> {\n\t\tif (this.#stopping !== undefined) return this.#stopping\n\t\tthis.#stopping = this.#kill()\n\t\treturn this.#stopping\n\t}\n\n\t/**\n\t * Stop the child and destroy the observation emitter.\n\t *\n\t * @returns The stable barrier shared by every call\n\t */\n\tdestroy(): Promise<void> {\n\t\tif (this.#ending !== undefined) return this.#ending\n\t\tthis.#ending = this.#teardown()\n\t\treturn this.#ending\n\t}\n\n\t#iterate(): AsyncIterator<string, void, void> {\n\t\treturn Object.freeze({ next: this.#next.bind(this) })\n\t}\n\n\t#next(): Promise<IteratorResult<string, void>> {\n\t\tconst line = this.#buffer.shift()\n\t\tif (line !== undefined) return Promise.resolve(Object.freeze({ done: false, value: line }))\n\t\tif (this.#ended) return Promise.resolve(Object.freeze({ done: true, value: undefined }))\n\t\tconst waiter = Promise.withResolvers<IteratorResult<string, void>>()\n\t\tthis.#waiters.push(waiter)\n\t\treturn waiter.promise\n\t}\n\n\t#push(line: string): void {\n\t\tif (this.#ended) return\n\t\tconst waiter = this.#waiters.shift()\n\t\tif (waiter === undefined) {\n\t\t\tthis.#buffer.push(line)\n\t\t\treturn\n\t\t}\n\t\twaiter.resolve(Object.freeze({ done: false, value: line }))\n\t}\n\n\t#finish(): void {\n\t\tif (this.#ended) return\n\t\tthis.#ended = true\n\t\tfor (const waiter of this.#waiters) {\n\t\t\twaiter.resolve(Object.freeze({ done: true, value: undefined }))\n\t\t}\n\t\tthis.#waiters.length = 0\n\t}\n\n\t#retain(chunk: unknown): void {\n\t\tif (!Buffer.isBuffer(chunk) || this.#closed) return\n\t\tthis.#tail = trimTail(Buffer.concat([this.#tail, chunk]), this.#limit)\n\t\tconst text = this.#decoder.write(chunk)\n\t\tif (text.length > 0) this.#emitter.emit('stderr', text)\n\t}\n\n\t#close(code: number | null, signal: NodeJS.Signals | null): void {\n\t\tif (this.#closed) return\n\t\tconst suffix = this.#decoder.end()\n\t\tif (suffix.length > 0) this.#emitter.emit('stderr', suffix)\n\t\tthis.#closed = true\n\t\tif (this.#signal !== undefined && this.#abort !== undefined) {\n\t\t\tthis.#signal.removeEventListener('abort', this.#abort)\n\t\t}\n\t\tthis.#reader.close()\n\t\tconst exit = Object.freeze({ code, signal })\n\t\tthis.#exit.resolve(exit)\n\t\tthis.#emitter.emit('exit', exit)\n\t}\n\n\t#terminate(): void {\n\t\tvoid this.stop()\n\t}\n\n\tasync #kill(): Promise<void> {\n\t\tif (this.#closed) return\n\t\tkillProcess(this.#child, 'SIGTERM')\n\t\tconst grace = Promise.withResolvers<void>()\n\t\tconst timer = setTimeout(grace.resolve, this.#grace)\n\t\tawait Promise.race([this.#exit.promise, grace.promise])\n\t\tclearTimeout(timer)\n\t\tif (this.#closed) return\n\t\tkillProcess(this.#child, 'SIGKILL')\n\t\tawait this.#exit.promise\n\t}\n\n\tasync #teardown(): Promise<void> {\n\t\tawait this.stop()\n\t\tthis.#emitter.destroy()\n\t}\n}\n","import type { EmitterInterface } from '@orkestrel/emitter'\nimport type {\n\tProcessExit,\n\tProcessInterface,\n\tProcessManagerEventMap,\n\tProcessManagerInterface,\n\tProcessManagerOptions,\n\tProcessOptions,\n} from '@src/core'\nimport { Emitter } from '@orkestrel/emitter'\nimport { createDuplicateError } from '@src/core'\nimport { Process } from './Process.js'\n\n/**\n * A keyed registry of live supervised child processes.\n *\n * @remarks\n * A child launched under an id joins the registry and emits `launch`; when it settles it removes\n * itself and emits `exit`, so `count` and `processes` reflect only live children. `launch` throws\n * a {@link createDuplicateError} when the id is already live — a spawn fault surfaces through the\n * returned child's `exit`, never from `launch`. `destroy` stops every child, then destroys the\n * registry emitter last.\n *\n * @example\n * ```ts\n * import { ProcessManager } from '@orkestrel/process/server'\n *\n * const manager = new ProcessManager()\n * const child = manager.launch('build', {\n * \tcommand: { file: 'node', arguments: ['build.js'] },\n * \tworkspace: process.cwd(),\n * \tgrace: 5000,\n * })\n * await child.exit\n * await manager.destroy()\n * ```\n */\nexport class ProcessManager implements ProcessManagerInterface {\n\treadonly #emitter: Emitter<ProcessManagerEventMap>\n\treadonly #children = new Map<string, ProcessInterface>()\n\t#ending: Promise<void> | undefined\n\n\t/**\n\t * Construct a process registry.\n\t *\n\t * @param options - Initial fleet-level observation hooks and listener-error handling\n\t */\n\tconstructor(options?: ProcessManagerOptions) {\n\t\tconst on = options?.on\n\t\tconst error = options?.error\n\t\tthis.#emitter = new Emitter<ProcessManagerEventMap>({\n\t\t\t...(on === undefined ? {} : { on }),\n\t\t\t...(error === undefined ? {} : { error }),\n\t\t})\n\t}\n\n\t/** The typed fleet-level observation surface. */\n\tget emitter(): EmitterInterface<ProcessManagerEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\t/** The number of live children. */\n\tget count(): number {\n\t\treturn this.#children.size\n\t}\n\n\t/**\n\t * The live child under `id`, or `undefined` when none is.\n\t *\n\t * @param id - The registry key\n\t * @returns The child, or `undefined`\n\t */\n\tprocess(id: string): ProcessInterface | undefined {\n\t\treturn this.#children.get(id)\n\t}\n\n\t/**\n\t * A snapshot of every live child.\n\t *\n\t * @returns The live children in launch order\n\t */\n\tprocesses(): readonly ProcessInterface[] {\n\t\treturn [...this.#children.values()]\n\t}\n\n\t/**\n\t * Spawn and register one child under `id`.\n\t *\n\t * @param id - The registry key, unique among live children\n\t * @param options - The child construction options\n\t * @returns The launched child\n\t * @throws A {@link ProcessError} coded `duplicate` when `id` is already live\n\t */\n\tlaunch(id: string, options: ProcessOptions): ProcessInterface {\n\t\tif (this.#children.has(id)) throw createDuplicateError(id)\n\t\tconst child = new Process(options)\n\t\tthis.#children.set(id, child)\n\t\tchild.emitter.on('exit', (exit) => this.#evict(id, child, exit))\n\t\tthis.#emitter.emit('launch', id)\n\t\treturn child\n\t}\n\n\t/**\n\t * Terminate the named children and await their exit.\n\t *\n\t * @param ids - The registry keys to stop\n\t * @returns True when every named child stopped, false when any id was not live\n\t */\n\tstop(ids: readonly string[]): Promise<boolean>\n\t/**\n\t * Terminate one child and await its exit.\n\t *\n\t * @param id - The registry key to stop\n\t * @returns True when the child was live and stopped, false when the id was not live\n\t */\n\tstop(id: string): Promise<boolean>\n\t/**\n\t * Terminate every live child and await their exit.\n\t *\n\t * @returns A promise that resolves after all children stop\n\t */\n\tstop(): Promise<void>\n\tstop(target?: string | readonly string[]): Promise<boolean | void> {\n\t\tif (target === undefined) return this.#stopAll()\n\t\tif (typeof target === 'string') return this.#stopOne(target)\n\t\treturn this.#stopMany(target)\n\t}\n\n\t/**\n\t * Stop every child, then destroy the registry emitter last.\n\t *\n\t * @returns The stable barrier shared by every call\n\t */\n\tdestroy(): Promise<void> {\n\t\tif (this.#ending !== undefined) return this.#ending\n\t\tthis.#ending = this.#teardown()\n\t\treturn this.#ending\n\t}\n\n\t#evict(id: string, child: ProcessInterface, exit: ProcessExit): void {\n\t\tif (this.#children.get(id) !== child) return\n\t\tthis.#children.delete(id)\n\t\tthis.#emitter.emit('exit', id, exit)\n\t}\n\n\tasync #stopOne(id: string): Promise<boolean> {\n\t\tconst child = this.#children.get(id)\n\t\tif (child === undefined) return false\n\t\tawait child.stop()\n\t\treturn true\n\t}\n\n\tasync #stopMany(ids: readonly string[]): Promise<boolean> {\n\t\tconst stopped = await Promise.all(ids.map((id) => this.#stopOne(id)))\n\t\treturn stopped.every((ok) => ok)\n\t}\n\n\tasync #stopAll(): Promise<void> {\n\t\tawait Promise.all([...this.#children.values()].map((child) => child.stop()))\n\t}\n\n\tasync #teardown(): Promise<void> {\n\t\tawait Promise.all([...this.#children.values()].map((child) => child.destroy()))\n\t\tthis.#emitter.destroy()\n\t}\n}\n","import type {\n\tProcessInterface,\n\tProcessManagerInterface,\n\tProcessManagerOptions,\n\tProcessOptions,\n} from '@src/core'\nimport { Process } from './Process.js'\nimport { ProcessManager } from './ProcessManager.js'\n\n/**\n * Creates one supervised child process.\n *\n * @param options - Command, workspace, termination, evidence, stdin, and observation settings\n * @returns The launched {@link ProcessInterface}\n *\n * @example\n * ```ts\n * import { createProcess } from '@orkestrel/process/server'\n *\n * const child = createProcess({\n * \tcommand: { file: 'node', arguments: ['--version'] },\n * \tworkspace: process.cwd(),\n * \tgrace: 5000,\n * })\n * ```\n */\nexport function createProcess(options: ProcessOptions): ProcessInterface {\n\treturn new Process(options)\n}\n\n/**\n * Creates one keyed registry of supervised child processes.\n *\n * @param options - Initial fleet-level observation hooks and listener-error handling\n * @returns The {@link ProcessManagerInterface} registry\n *\n * @example\n * ```ts\n * import { createProcessManager } from '@orkestrel/process/server'\n *\n * const manager = createProcessManager()\n * ```\n */\nexport function createProcessManager(options?: ProcessManagerOptions): ProcessManagerInterface {\n\treturn new ProcessManager(options)\n}\n"],"mappings":"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"}
@@ -130,8 +130,9 @@ export declare function mergeEnvironment(base?: Readonly<Record<string, string |
130
130
  * Standard output is drained eagerly through `readline`, so `exit` resolves and a late consumer of
131
131
  * `lines` still receives every framed line, including a final line written without a trailing
132
132
  * newline. Standard error is decoded and forwarded live as the `stderr` event while a byte-bounded
133
- * raw tail is retained as `evidence`. The typed `emitter` also carries the terminal `exit`,
134
- * alongside the `exit` promise. `stop` terminates the child through `SIGTERM`, then `SIGKILL` after
133
+ * raw tail is retained as `evidence`. The typed `emitter` also carries the child `error` cause on a
134
+ * spawn fault and the terminal `exit`, alongside the `exit` promise. `stop` terminates the child
135
+ * through `SIGTERM`, then `SIGKILL` after
135
136
  * `grace`, and is idempotent; `destroy` stops the child and destroys the emitter last.
136
137
  *
137
138
  * @example
@@ -130,8 +130,9 @@ export declare function mergeEnvironment(base?: Readonly<Record<string, string |
130
130
  * Standard output is drained eagerly through `readline`, so `exit` resolves and a late consumer of
131
131
  * `lines` still receives every framed line, including a final line written without a trailing
132
132
  * newline. Standard error is decoded and forwarded live as the `stderr` event while a byte-bounded
133
- * raw tail is retained as `evidence`. The typed `emitter` also carries the terminal `exit`,
134
- * alongside the `exit` promise. `stop` terminates the child through `SIGTERM`, then `SIGKILL` after
133
+ * raw tail is retained as `evidence`. The typed `emitter` also carries the child `error` cause on a
134
+ * spawn fault and the terminal `exit`, alongside the `exit` promise. `stop` terminates the child
135
+ * through `SIGTERM`, then `SIGKILL` after
135
136
  * `grace`, and is idempotent; `destroy` stops the child and destroys the emitter last.
136
137
  *
137
138
  * @example
@@ -216,6 +216,7 @@ async function run(command, options) {
216
216
  let stdout = Buffer.alloc(0);
217
217
  let stderr = Buffer.alloc(0);
218
218
  let timedOut = false;
219
+ let launchCause;
219
220
  let killTimer;
220
221
  let timeoutTimer;
221
222
  let done = false;
@@ -242,9 +243,10 @@ async function run(command, options) {
242
243
  child.stderr.on("data", (chunk) => {
243
244
  if (Buffer.isBuffer(chunk)) stderr = trimHead(Buffer.concat([stderr, chunk]), limit);
244
245
  });
245
- child.once("error", () => {
246
+ child.once("error", (cause) => {
246
247
  if (done) return;
247
248
  done = true;
249
+ launchCause = cause;
248
250
  cleanup.abort();
249
251
  clearTimeout(killTimer);
250
252
  clearTimeout(timeoutTimer);
@@ -273,7 +275,7 @@ async function run(command, options) {
273
275
  if (signal.aborted) terminate.abort();
274
276
  }
275
277
  const result = await settled.promise;
276
- if (result.failed && reject) throw createRunError(result);
278
+ if (result.failed && reject) throw createRunError(result, launchCause);
277
279
  return result;
278
280
  }
279
281
  /**
@@ -320,7 +322,7 @@ function runSync(command, options) {
320
322
  const error = outcome.error;
321
323
  const timedOut = error !== void 0 && "code" in error && error.code === "ETIMEDOUT";
322
324
  const result = buildRunResult(line, Buffer.isBuffer(outcome.stdout) ? outcome.stdout : Buffer.alloc(0), Buffer.isBuffer(outcome.stderr) ? outcome.stderr : Buffer.alloc(0), outcome.status, outcome.signal, timedOut, limit);
323
- if (result.failed && reject) throw createRunError(result);
325
+ if (result.failed && reject) throw createRunError(result, outcome.error);
324
326
  return result;
325
327
  }
326
328
  //#endregion
@@ -332,8 +334,9 @@ function runSync(command, options) {
332
334
  * Standard output is drained eagerly through `readline`, so `exit` resolves and a late consumer of
333
335
  * `lines` still receives every framed line, including a final line written without a trailing
334
336
  * newline. Standard error is decoded and forwarded live as the `stderr` event while a byte-bounded
335
- * raw tail is retained as `evidence`. The typed `emitter` also carries the terminal `exit`,
336
- * alongside the `exit` promise. `stop` terminates the child through `SIGTERM`, then `SIGKILL` after
337
+ * raw tail is retained as `evidence`. The typed `emitter` also carries the child `error` cause on a
338
+ * spawn fault and the terminal `exit`, alongside the `exit` promise. `stop` terminates the child
339
+ * through `SIGTERM`, then `SIGKILL` after
337
340
  * `grace`, and is idempotent; `destroy` stops the child and destroys the emitter last.
338
341
  *
339
342
  * @example
@@ -400,7 +403,7 @@ var Process = class {
400
403
  });
401
404
  this.#reader.on("line", this.#push.bind(this));
402
405
  this.#reader.once("close", this.#finish.bind(this));
403
- this.#child.once("error", () => void 0);
406
+ this.#child.once("error", (cause) => this.#emitter.emit("error", cause));
404
407
  this.#child.once("close", this.#close.bind(this));
405
408
  this.#child.stdin.on("error", () => void 0);
406
409
  this.#child.stderr.on("data", this.#retain.bind(this));
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["#emitter","#child","#reader","#grace","#limit","#decoder","#exit","#lines","#buffer","#waiters","#signal","#abort","#iterate","#push","#finish","#close","#retain","#terminate","#tail","#closed","#stopping","#kill","#ending","#teardown","#next","#ended","#emitter","#children","#evict","#stopAll","#stopOne","#stopMany","#ending","#teardown"],"sources":["../../../src/server/helpers.ts","../../../src/server/Process.ts","../../../src/server/ProcessManager.ts","../../../src/server/factories.ts"],"sourcesContent":["import type { ChildProcessWithoutNullStreams } from 'node:child_process'\nimport type { ProcessCommand, RunOptions, RunResult } from '@src/core'\nimport { Buffer } from 'node:buffer'\nimport { spawn, spawnSync } from 'node:child_process'\nimport { extname } from 'node:path'\nimport { createRunError, PROCESS_GRACE, PROCESS_OUTPUT } from '@src/core'\n\n/**\n * Signals one owned child process, or its detached process group on a POSIX host.\n *\n * @remarks\n * On a POSIX host a child spawned `detached` leads its own process group, so signalling the\n * negated pid reaches the whole tree. On Windows, or when no pid is available, the signal goes to\n * the child directly. A throw during signalling is swallowed: the process can exit and win the\n * signal race, and the child's close event remains the authoritative terminal state.\n *\n * @param child - The child process signal boundary\n * @param signal - The signal to deliver\n * @returns Nothing\n *\n * @example\n * ```ts\n * killProcess(child, 'SIGTERM')\n * ```\n */\nexport function killProcess(\n\tchild: { readonly pid?: number | undefined; kill(signal: NodeJS.Signals): boolean },\n\tsignal: NodeJS.Signals,\n): void {\n\ttry {\n\t\tif (process.platform === 'win32' || child.pid === undefined) {\n\t\t\tchild.kill(signal)\n\t\t\treturn\n\t\t}\n\t\tprocess.kill(-child.pid, signal)\n\t} catch {\n\t\t// Exit can win the signal race; the close event remains authoritative.\n\t}\n}\n\n/**\n * Trims a buffer to at most `limit` trailing bytes without splitting a UTF-8 sequence.\n *\n * @remarks\n * Keeps the tail — the diagnostic end of a stderr stream. When the cut point lands inside a\n * multibyte sequence, the start advances past the leading continuation bytes so the retained\n * bytes always begin on a code-point boundary.\n *\n * @param buffer - The accumulated bytes\n * @param limit - The maximum retained byte count\n * @returns The trailing bytes, at most `limit` and never starting mid-sequence\n *\n * @example\n * ```ts\n * trimTail(Buffer.from('hello'), 3) // <Buffer 6c 6c 6f>\n * ```\n */\nexport function trimTail(buffer: Buffer, limit: number): Buffer {\n\tif (buffer.byteLength <= limit) return buffer\n\tlet start = buffer.byteLength - limit\n\twhile (start < buffer.byteLength) {\n\t\tconst byte = buffer[start]\n\t\tif (byte === undefined || (byte & 0xc0) !== 0x80) break\n\t\tstart += 1\n\t}\n\treturn Buffer.from(buffer.subarray(start))\n}\n\n/**\n * Trims a buffer to at most `limit` leading bytes without splitting a UTF-8 sequence.\n *\n * @remarks\n * Keeps the head — the captured start of a one-shot run's output. When the cut point lands inside\n * a multibyte sequence, the end retreats to the sequence's start so the retained bytes always end\n * on a code-point boundary and decode without a replacement character.\n *\n * @param buffer - The accumulated bytes\n * @param limit - The maximum retained byte count\n * @returns The leading bytes, at most `limit` and never ending mid-sequence\n *\n * @example\n * ```ts\n * trimHead(Buffer.from('hello'), 3) // <Buffer 68 65 6c>\n * ```\n */\nexport function trimHead(buffer: Buffer, limit: number): Buffer {\n\tif (buffer.byteLength <= limit) return buffer\n\tlet end = limit\n\twhile (end > 0) {\n\t\tconst byte = buffer[end]\n\t\tif (byte === undefined || (byte & 0xc0) !== 0x80) break\n\t\tend -= 1\n\t}\n\treturn Buffer.from(buffer.subarray(0, end))\n}\n\n/**\n * Checks whether a command file must be spawned through a shell on the current host.\n *\n * @remarks\n * Node v22+ refuses to spawn a Windows `.cmd` or `.bat` batch file directly and returns `EINVAL`,\n * and a bare command name such as `git` resolves through `PATHEXT` to a batch shim rather than an\n * executable. Both cases run through the shell. A POSIX host, and a Windows path carrying a real\n * executable extension such as `.exe`, spawn directly. When the result is `true`, treat the\n * command file and arguments as trusted: shell invocation exposes shell metacharacters.\n *\n * @param file - The command executable name or path\n * @returns True when the file must run through a shell; false when it spawns directly\n *\n * @example\n * ```ts\n * requiresShell('git') // true on Windows, false on POSIX\n * ```\n */\nexport function requiresShell(file: string): boolean {\n\tif (process.platform !== 'win32') return false\n\tconst lower = file.toLowerCase()\n\treturn lower.endsWith('.cmd') || lower.endsWith('.bat') || extname(file) === ''\n}\n\n/**\n * Renders one command into its diagnostic command line.\n *\n * @param command - The executable and its argument vector\n * @returns The space-joined command line, for a {@link RunResult} and error messages\n *\n * @example\n * ```ts\n * commandLine({ file: 'git', arguments: ['status'] }) // 'git status'\n * ```\n */\nexport function commandLine(command: ProcessCommand): string {\n\treturn [command.file, ...command.arguments].join(' ')\n}\n\n/**\n * Merges environment overrides over the current process environment.\n *\n * @remarks\n * Later maps override earlier ones and an `undefined` value unsets a key. The base is the parent\n * process environment, so a child inherits it unless a key is overridden or unset.\n *\n * @param base - The command's own environment overrides\n * @param override - Per-invocation overrides applied last\n * @returns The merged environment for a spawn\n *\n * @example\n * ```ts\n * mergeEnvironment({ TOKEN: 'a' }, { TOKEN: undefined }) // TOKEN unset\n * ```\n */\nexport function mergeEnvironment(\n\tbase?: Readonly<Record<string, string | undefined>>,\n\toverride?: Readonly<Record<string, string | undefined>>,\n): NodeJS.ProcessEnv {\n\treturn { ...process.env, ...base, ...override }\n}\n\n/**\n * Builds one settled {@link RunResult} from a completed one-shot run's captured bytes and exit.\n *\n * @remarks\n * `failed` is derived: a run failed when it timed out, was ended by a signal, or exited with a\n * code other than `0` — a `null` code from a spawn fault is therefore a failure. Both output\n * buffers are byte-bounded by `limit` before decoding.\n *\n * @param command - The diagnostic command line\n * @param stdout - The captured standard output bytes\n * @param stderr - The captured standard error bytes\n * @param code - The exit code, or `null` when a signal or spawn fault ended the run\n * @param signal - The terminating signal, or `null`\n * @param timedOut - If `true`, the run's own timeout elapsed; if `false`, it did not\n * @param limit - The maximum captured bytes for each stream\n * @returns The frozen run outcome\n *\n * @example\n * ```ts\n * buildRunResult('git status', Buffer.from('ok'), Buffer.alloc(0), 0, null, false, 1024)\n * ```\n */\nexport function buildRunResult(\n\tcommand: string,\n\tstdout: Buffer,\n\tstderr: Buffer,\n\tcode: number | null,\n\tsignal: string | null,\n\ttimedOut: boolean,\n\tlimit: number,\n): RunResult {\n\treturn Object.freeze({\n\t\tcommand,\n\t\tstdout: trimHead(stdout, limit).toString('utf8'),\n\t\tstderr: trimHead(stderr, limit).toString('utf8'),\n\t\tcode,\n\t\tsignal,\n\t\tfailed: timedOut || signal !== null || code !== 0,\n\t\ttimedOut,\n\t})\n}\n\n/**\n * Runs one command to completion, buffering its output, and settles with the outcome.\n *\n * @remarks\n * The child is spawned through the cross-platform command resolver ({@link requiresShell}) and, on\n * a POSIX host, detached so its whole process group can be terminated. Standard output and error\n * are each byte-bounded by `limit` (default {@link PROCESS_OUTPUT}). A positive `timeout` arms a\n * timer that terminates the child through `SIGTERM`, then `SIGKILL` after `grace`\n * (default {@link PROCESS_GRACE}); aborting `signal` terminates the same way. The child's\n * `environment` merges over the parent, then `options.environment` on top, and `options.input`\n * overrides `command.input`. Unless `reject` is `false`, a failed run rejects with a\n * {@link createRunError} carrying the {@link RunResult}.\n *\n * @param command - The executable, arguments, and optional environment and input\n * @param options - Working directory, timeout, grace, signal, capture limit, and reject behavior\n * @returns The settled run outcome\n * @throws A {@link ProcessError} carrying the {@link RunResult} when the run failed and `reject` is not `false`\n *\n * @example\n * ```ts\n * const result = await run({ file: 'git', arguments: ['status'] }, { workspace: process.cwd() })\n * ```\n */\nexport async function run(command: ProcessCommand, options?: RunOptions): Promise<RunResult> {\n\tconst limit = options?.limit ?? PROCESS_OUTPUT\n\tconst grace = options?.grace ?? PROCESS_GRACE\n\tconst timeout = options?.timeout ?? 0\n\tconst reject = options?.reject ?? true\n\tconst input = options?.input ?? command.input\n\tconst line = commandLine(command)\n\tconst settled = Promise.withResolvers<RunResult>()\n\tconst terminate = new AbortController()\n\tconst cleanup = new AbortController()\n\tlet stdout: Buffer = Buffer.alloc(0)\n\tlet stderr: Buffer = Buffer.alloc(0)\n\tlet timedOut = false\n\tlet killTimer: ReturnType<typeof setTimeout> | undefined\n\tlet timeoutTimer: ReturnType<typeof setTimeout> | undefined\n\tlet done = false\n\n\tconst child: ChildProcessWithoutNullStreams = spawn(command.file, [...command.arguments], {\n\t\tcwd: options?.workspace ?? process.cwd(),\n\t\tdetached: process.platform !== 'win32',\n\t\tenv: mergeEnvironment(command.environment, options?.environment),\n\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\tshell: requiresShell(command.file),\n\t\twindowsHide: true,\n\t})\n\n\tterminate.signal.addEventListener(\n\t\t'abort',\n\t\t() => {\n\t\t\tkillProcess(child, 'SIGTERM')\n\t\t\tkillTimer = setTimeout(() => killProcess(child, 'SIGKILL'), grace)\n\t\t},\n\t\t{ once: true },\n\t)\n\n\tchild.stdin.on('error', () => undefined)\n\tchild.stdout.on('data', (chunk: unknown) => {\n\t\tif (Buffer.isBuffer(chunk)) stdout = trimHead(Buffer.concat([stdout, chunk]), limit)\n\t})\n\tchild.stderr.on('data', (chunk: unknown) => {\n\t\tif (Buffer.isBuffer(chunk)) stderr = trimHead(Buffer.concat([stderr, chunk]), limit)\n\t})\n\tchild.once('error', () => {\n\t\tif (done) return\n\t\tdone = true\n\t\tcleanup.abort()\n\t\tclearTimeout(killTimer)\n\t\tclearTimeout(timeoutTimer)\n\t\tsettled.resolve(buildRunResult(line, stdout, stderr, null, null, false, limit))\n\t})\n\tchild.once('close', (code: number | null, signal: NodeJS.Signals | null) => {\n\t\tif (done) return\n\t\tdone = true\n\t\tcleanup.abort()\n\t\tclearTimeout(killTimer)\n\t\tclearTimeout(timeoutTimer)\n\t\tsettled.resolve(buildRunResult(line, stdout, stderr, code, signal, timedOut, limit))\n\t})\n\n\tif (input !== undefined) child.stdin.write(input)\n\tchild.stdin.end()\n\tif (timeout > 0) {\n\t\ttimeoutTimer = setTimeout(() => {\n\t\t\ttimedOut = true\n\t\t\tterminate.abort()\n\t\t}, timeout)\n\t}\n\tif (options?.signal !== undefined) {\n\t\tconst signal = options.signal\n\t\tsignal.addEventListener('abort', () => terminate.abort(), {\n\t\t\tonce: true,\n\t\t\tsignal: cleanup.signal,\n\t\t})\n\t\tif (signal.aborted) terminate.abort()\n\t}\n\n\tconst result = await settled.promise\n\tif (result.failed && reject) throw createRunError(result)\n\treturn result\n}\n\n/**\n * Runs one command to completion synchronously, buffering its output, and returns the outcome.\n *\n * @remarks\n * The synchronous counterpart of {@link run}. The child is spawned through the same cross-platform\n * command resolver ({@link requiresShell}). Standard output and error are each byte-bounded by\n * `limit` (default {@link PROCESS_OUTPUT}). A positive `timeout` is enforced by the host, which\n * ends the child with `SIGKILL` when it elapses. The environment and input follow the same merge\n * as {@link run}. Unless `reject` is `false`, a failed run throws a {@link createRunError} carrying\n * the {@link RunResult}.\n *\n * @param command - The executable, arguments, and optional environment and input\n * @param options - Working directory, timeout, signal, capture limit, and reject behavior\n * @returns The run outcome\n * @throws A {@link ProcessError} carrying the {@link RunResult} when the run failed and `reject` is not `false`\n *\n * @example\n * ```ts\n * const result = runSync({ file: 'git', arguments: ['--version'] }, { reject: false })\n * ```\n */\nexport function runSync(command: ProcessCommand, options?: RunOptions): RunResult {\n\tconst limit = options?.limit ?? PROCESS_OUTPUT\n\tconst timeout = options?.timeout ?? 0\n\tconst reject = options?.reject ?? true\n\tconst input = options?.input ?? command.input\n\tconst line = commandLine(command)\n\tconst outcome = spawnSync(command.file, [...command.arguments], {\n\t\tcwd: options?.workspace ?? process.cwd(),\n\t\tenv: mergeEnvironment(command.environment, options?.environment),\n\t\tencoding: 'buffer',\n\t\tmaxBuffer: limit,\n\t\tshell: requiresShell(command.file),\n\t\twindowsHide: true,\n\t\t...(input !== undefined ? { input } : {}),\n\t\t...(options?.signal !== undefined ? { signal: options.signal } : {}),\n\t\t...(timeout > 0 ? { timeout, killSignal: 'SIGKILL' } : {}),\n\t})\n\tconst error = outcome.error\n\tconst timedOut = error !== undefined && 'code' in error && error.code === 'ETIMEDOUT'\n\tconst stdout = Buffer.isBuffer(outcome.stdout) ? outcome.stdout : Buffer.alloc(0)\n\tconst stderr = Buffer.isBuffer(outcome.stderr) ? outcome.stderr : Buffer.alloc(0)\n\tconst result = buildRunResult(\n\t\tline,\n\t\tstdout,\n\t\tstderr,\n\t\toutcome.status,\n\t\toutcome.signal,\n\t\ttimedOut,\n\t\tlimit,\n\t)\n\tif (result.failed && reject) throw createRunError(result)\n\treturn result\n}\n","import type { ChildProcessWithoutNullStreams } from 'node:child_process'\nimport type { Interface as ReadLineInterface } from 'node:readline'\nimport type { EmitterInterface } from '@orkestrel/emitter'\nimport type { ProcessEventMap, ProcessExit, ProcessInterface, ProcessOptions } from '@src/core'\nimport { Buffer } from 'node:buffer'\nimport { spawn } from 'node:child_process'\nimport { createInterface } from 'node:readline'\nimport { StringDecoder } from 'node:string_decoder'\nimport { Emitter } from '@orkestrel/emitter'\nimport { PROCESS_EVIDENCE } from '@src/core'\nimport { killProcess, mergeEnvironment, trimTail } from './helpers.js'\n\n/**\n * One supervised child process with eagerly framed output and bounded termination.\n *\n * @remarks\n * Standard output is drained eagerly through `readline`, so `exit` resolves and a late consumer of\n * `lines` still receives every framed line, including a final line written without a trailing\n * newline. Standard error is decoded and forwarded live as the `stderr` event while a byte-bounded\n * raw tail is retained as `evidence`. The typed `emitter` also carries the terminal `exit`,\n * alongside the `exit` promise. `stop` terminates the child through `SIGTERM`, then `SIGKILL` after\n * `grace`, and is idempotent; `destroy` stops the child and destroys the emitter last.\n *\n * @example\n * ```ts\n * import { Process } from '@orkestrel/process/server'\n *\n * const child = new Process({\n * \tcommand: { file: 'node', arguments: ['--version'] },\n * \tworkspace: process.cwd(),\n * \tgrace: 5000,\n * })\n * const exit = await child.exit\n * await child.destroy()\n * ```\n */\nexport class Process implements ProcessInterface {\n\treadonly #emitter: Emitter<ProcessEventMap>\n\treadonly #child: ChildProcessWithoutNullStreams\n\treadonly #reader: ReadLineInterface\n\treadonly #grace: number\n\treadonly #limit: number\n\treadonly #decoder = new StringDecoder('utf8')\n\treadonly #exit = Promise.withResolvers<ProcessExit>()\n\treadonly #lines: AsyncIterable<string>\n\treadonly #buffer: string[] = []\n\treadonly #waiters: Array<PromiseWithResolvers<IteratorResult<string, void>>> = []\n\treadonly #signal: AbortSignal | undefined\n\treadonly #abort: EventListener | undefined\n\t#tail: Buffer = Buffer.alloc(0)\n\t#closed = false\n\t#ended = false\n\t#stopping: Promise<void> | undefined\n\t#ending: Promise<void> | undefined\n\n\t/**\n\t * Spawn one child process and begin eager stream capture.\n\t *\n\t * @param options - Command, workspace, termination, evidence, stdin, and observation settings\n\t */\n\tconstructor(options: ProcessOptions) {\n\t\tconst on = options.on\n\t\tconst error = options.error\n\t\tthis.#emitter = new Emitter<ProcessEventMap>({\n\t\t\t...(on === undefined ? {} : { on }),\n\t\t\t...(error === undefined ? {} : { error }),\n\t\t})\n\t\tthis.#grace = options.grace\n\t\tthis.#limit = options.evidence ?? PROCESS_EVIDENCE\n\t\tthis.#signal = options.signal\n\t\tthis.#lines = Object.freeze({ [Symbol.asyncIterator]: this.#iterate.bind(this) })\n\t\tthis.#child = spawn(options.command.file, [...options.command.arguments], {\n\t\t\tcwd: options.workspace,\n\t\t\tdetached: process.platform !== 'win32',\n\t\t\tenv: mergeEnvironment(options.command.environment),\n\t\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\t\twindowsHide: true,\n\t\t})\n\t\tthis.#reader = createInterface({ input: this.#child.stdout, crlfDelay: Infinity })\n\t\tthis.#reader.on('line', this.#push.bind(this))\n\t\tthis.#reader.once('close', this.#finish.bind(this))\n\t\tthis.#child.once('error', () => undefined)\n\t\tthis.#child.once('close', this.#close.bind(this))\n\t\tthis.#child.stdin.on('error', () => undefined)\n\t\tthis.#child.stderr.on('data', this.#retain.bind(this))\n\t\tif (options.command.input !== undefined) this.#child.stdin.write(options.command.input)\n\t\tif (options.writable !== true) this.#child.stdin.end()\n\t\tif (this.#signal !== undefined) {\n\t\t\tthis.#abort = this.#terminate.bind(this)\n\t\t\tthis.#signal.addEventListener('abort', this.#abort, { once: true })\n\t\t\tif (this.#signal.aborted) void this.stop()\n\t\t}\n\t}\n\n\t/** The typed lifecycle observation surface. */\n\tget emitter(): EmitterInterface<ProcessEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\t/** The eagerly captured stdout lines, in arrival order, ending when the child's stdout closes. */\n\tget lines(): AsyncIterable<string> {\n\t\treturn this.#lines\n\t}\n\n\t/** The decoded byte-bounded stderr tail. */\n\tget evidence(): string {\n\t\treturn this.#tail.toString('utf8')\n\t}\n\n\t/** The terminal child state, observed once from the close event. */\n\tget exit(): Promise<ProcessExit> {\n\t\treturn this.#exit.promise\n\t}\n\n\t/**\n\t * Write one line to the open standard-input channel.\n\t *\n\t * @param text - The line text without its trailing newline\n\t * @returns True when the channel accepted the line without backpressure; false otherwise\n\t */\n\tsend(text: string): boolean {\n\t\tif (this.#closed || this.#child.stdin.destroyed || this.#child.stdin.writableEnded) return false\n\t\ttry {\n\t\t\treturn this.#child.stdin.write(`${text}\\n`)\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Terminate the child process tree and await its observed exit.\n\t *\n\t * @returns A promise that resolves after bounded termination\n\t */\n\tstop(): Promise<void> {\n\t\tif (this.#stopping !== undefined) return this.#stopping\n\t\tthis.#stopping = this.#kill()\n\t\treturn this.#stopping\n\t}\n\n\t/**\n\t * Stop the child and destroy the observation emitter.\n\t *\n\t * @returns The stable barrier shared by every call\n\t */\n\tdestroy(): Promise<void> {\n\t\tif (this.#ending !== undefined) return this.#ending\n\t\tthis.#ending = this.#teardown()\n\t\treturn this.#ending\n\t}\n\n\t#iterate(): AsyncIterator<string, void, void> {\n\t\treturn Object.freeze({ next: this.#next.bind(this) })\n\t}\n\n\t#next(): Promise<IteratorResult<string, void>> {\n\t\tconst line = this.#buffer.shift()\n\t\tif (line !== undefined) return Promise.resolve(Object.freeze({ done: false, value: line }))\n\t\tif (this.#ended) return Promise.resolve(Object.freeze({ done: true, value: undefined }))\n\t\tconst waiter = Promise.withResolvers<IteratorResult<string, void>>()\n\t\tthis.#waiters.push(waiter)\n\t\treturn waiter.promise\n\t}\n\n\t#push(line: string): void {\n\t\tif (this.#ended) return\n\t\tconst waiter = this.#waiters.shift()\n\t\tif (waiter === undefined) {\n\t\t\tthis.#buffer.push(line)\n\t\t\treturn\n\t\t}\n\t\twaiter.resolve(Object.freeze({ done: false, value: line }))\n\t}\n\n\t#finish(): void {\n\t\tif (this.#ended) return\n\t\tthis.#ended = true\n\t\tfor (const waiter of this.#waiters) {\n\t\t\twaiter.resolve(Object.freeze({ done: true, value: undefined }))\n\t\t}\n\t\tthis.#waiters.length = 0\n\t}\n\n\t#retain(chunk: unknown): void {\n\t\tif (!Buffer.isBuffer(chunk) || this.#closed) return\n\t\tthis.#tail = trimTail(Buffer.concat([this.#tail, chunk]), this.#limit)\n\t\tconst text = this.#decoder.write(chunk)\n\t\tif (text.length > 0) this.#emitter.emit('stderr', text)\n\t}\n\n\t#close(code: number | null, signal: NodeJS.Signals | null): void {\n\t\tif (this.#closed) return\n\t\tconst suffix = this.#decoder.end()\n\t\tif (suffix.length > 0) this.#emitter.emit('stderr', suffix)\n\t\tthis.#closed = true\n\t\tif (this.#signal !== undefined && this.#abort !== undefined) {\n\t\t\tthis.#signal.removeEventListener('abort', this.#abort)\n\t\t}\n\t\tthis.#reader.close()\n\t\tconst exit = Object.freeze({ code, signal })\n\t\tthis.#exit.resolve(exit)\n\t\tthis.#emitter.emit('exit', exit)\n\t}\n\n\t#terminate(): void {\n\t\tvoid this.stop()\n\t}\n\n\tasync #kill(): Promise<void> {\n\t\tif (this.#closed) return\n\t\tkillProcess(this.#child, 'SIGTERM')\n\t\tconst grace = Promise.withResolvers<void>()\n\t\tconst timer = setTimeout(grace.resolve, this.#grace)\n\t\tawait Promise.race([this.#exit.promise, grace.promise])\n\t\tclearTimeout(timer)\n\t\tif (this.#closed) return\n\t\tkillProcess(this.#child, 'SIGKILL')\n\t\tawait this.#exit.promise\n\t}\n\n\tasync #teardown(): Promise<void> {\n\t\tawait this.stop()\n\t\tthis.#emitter.destroy()\n\t}\n}\n","import type { EmitterInterface } from '@orkestrel/emitter'\nimport type {\n\tProcessExit,\n\tProcessInterface,\n\tProcessManagerEventMap,\n\tProcessManagerInterface,\n\tProcessManagerOptions,\n\tProcessOptions,\n} from '@src/core'\nimport { Emitter } from '@orkestrel/emitter'\nimport { createDuplicateError } from '@src/core'\nimport { Process } from './Process.js'\n\n/**\n * A keyed registry of live supervised child processes.\n *\n * @remarks\n * A child launched under an id joins the registry and emits `launch`; when it settles it removes\n * itself and emits `exit`, so `count` and `processes` reflect only live children. `launch` throws\n * a {@link createDuplicateError} when the id is already live — a spawn fault surfaces through the\n * returned child's `exit`, never from `launch`. `destroy` stops every child, then destroys the\n * registry emitter last.\n *\n * @example\n * ```ts\n * import { ProcessManager } from '@orkestrel/process/server'\n *\n * const manager = new ProcessManager()\n * const child = manager.launch('build', {\n * \tcommand: { file: 'node', arguments: ['build.js'] },\n * \tworkspace: process.cwd(),\n * \tgrace: 5000,\n * })\n * await child.exit\n * await manager.destroy()\n * ```\n */\nexport class ProcessManager implements ProcessManagerInterface {\n\treadonly #emitter: Emitter<ProcessManagerEventMap>\n\treadonly #children = new Map<string, ProcessInterface>()\n\t#ending: Promise<void> | undefined\n\n\t/**\n\t * Construct a process registry.\n\t *\n\t * @param options - Initial fleet-level observation hooks and listener-error handling\n\t */\n\tconstructor(options?: ProcessManagerOptions) {\n\t\tconst on = options?.on\n\t\tconst error = options?.error\n\t\tthis.#emitter = new Emitter<ProcessManagerEventMap>({\n\t\t\t...(on === undefined ? {} : { on }),\n\t\t\t...(error === undefined ? {} : { error }),\n\t\t})\n\t}\n\n\t/** The typed fleet-level observation surface. */\n\tget emitter(): EmitterInterface<ProcessManagerEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\t/** The number of live children. */\n\tget count(): number {\n\t\treturn this.#children.size\n\t}\n\n\t/**\n\t * The live child under `id`, or `undefined` when none is.\n\t *\n\t * @param id - The registry key\n\t * @returns The child, or `undefined`\n\t */\n\tprocess(id: string): ProcessInterface | undefined {\n\t\treturn this.#children.get(id)\n\t}\n\n\t/**\n\t * A snapshot of every live child.\n\t *\n\t * @returns The live children in launch order\n\t */\n\tprocesses(): readonly ProcessInterface[] {\n\t\treturn [...this.#children.values()]\n\t}\n\n\t/**\n\t * Spawn and register one child under `id`.\n\t *\n\t * @param id - The registry key, unique among live children\n\t * @param options - The child construction options\n\t * @returns The launched child\n\t * @throws A {@link ProcessError} coded `duplicate` when `id` is already live\n\t */\n\tlaunch(id: string, options: ProcessOptions): ProcessInterface {\n\t\tif (this.#children.has(id)) throw createDuplicateError(id)\n\t\tconst child = new Process(options)\n\t\tthis.#children.set(id, child)\n\t\tchild.emitter.on('exit', (exit) => this.#evict(id, child, exit))\n\t\tthis.#emitter.emit('launch', id)\n\t\treturn child\n\t}\n\n\t/**\n\t * Terminate the named children and await their exit.\n\t *\n\t * @param ids - The registry keys to stop\n\t * @returns True when every named child stopped, false when any id was not live\n\t */\n\tstop(ids: readonly string[]): Promise<boolean>\n\t/**\n\t * Terminate one child and await its exit.\n\t *\n\t * @param id - The registry key to stop\n\t * @returns True when the child was live and stopped, false when the id was not live\n\t */\n\tstop(id: string): Promise<boolean>\n\t/**\n\t * Terminate every live child and await their exit.\n\t *\n\t * @returns A promise that resolves after all children stop\n\t */\n\tstop(): Promise<void>\n\tstop(target?: string | readonly string[]): Promise<boolean | void> {\n\t\tif (target === undefined) return this.#stopAll()\n\t\tif (typeof target === 'string') return this.#stopOne(target)\n\t\treturn this.#stopMany(target)\n\t}\n\n\t/**\n\t * Stop every child, then destroy the registry emitter last.\n\t *\n\t * @returns The stable barrier shared by every call\n\t */\n\tdestroy(): Promise<void> {\n\t\tif (this.#ending !== undefined) return this.#ending\n\t\tthis.#ending = this.#teardown()\n\t\treturn this.#ending\n\t}\n\n\t#evict(id: string, child: ProcessInterface, exit: ProcessExit): void {\n\t\tif (this.#children.get(id) !== child) return\n\t\tthis.#children.delete(id)\n\t\tthis.#emitter.emit('exit', id, exit)\n\t}\n\n\tasync #stopOne(id: string): Promise<boolean> {\n\t\tconst child = this.#children.get(id)\n\t\tif (child === undefined) return false\n\t\tawait child.stop()\n\t\treturn true\n\t}\n\n\tasync #stopMany(ids: readonly string[]): Promise<boolean> {\n\t\tconst stopped = await Promise.all(ids.map((id) => this.#stopOne(id)))\n\t\treturn stopped.every((ok) => ok)\n\t}\n\n\tasync #stopAll(): Promise<void> {\n\t\tawait Promise.all([...this.#children.values()].map((child) => child.stop()))\n\t}\n\n\tasync #teardown(): Promise<void> {\n\t\tawait Promise.all([...this.#children.values()].map((child) => child.destroy()))\n\t\tthis.#emitter.destroy()\n\t}\n}\n","import type {\n\tProcessInterface,\n\tProcessManagerInterface,\n\tProcessManagerOptions,\n\tProcessOptions,\n} from '@src/core'\nimport { Process } from './Process.js'\nimport { ProcessManager } from './ProcessManager.js'\n\n/**\n * Creates one supervised child process.\n *\n * @param options - Command, workspace, termination, evidence, stdin, and observation settings\n * @returns The launched {@link ProcessInterface}\n *\n * @example\n * ```ts\n * import { createProcess } from '@orkestrel/process/server'\n *\n * const child = createProcess({\n * \tcommand: { file: 'node', arguments: ['--version'] },\n * \tworkspace: process.cwd(),\n * \tgrace: 5000,\n * })\n * ```\n */\nexport function createProcess(options: ProcessOptions): ProcessInterface {\n\treturn new Process(options)\n}\n\n/**\n * Creates one keyed registry of supervised child processes.\n *\n * @param options - Initial fleet-level observation hooks and listener-error handling\n * @returns The {@link ProcessManagerInterface} registry\n *\n * @example\n * ```ts\n * import { createProcessManager } from '@orkestrel/process/server'\n *\n * const manager = createProcessManager()\n * ```\n */\nexport function createProcessManager(options?: ProcessManagerOptions): ProcessManagerInterface {\n\treturn new ProcessManager(options)\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["#emitter","#child","#reader","#grace","#limit","#decoder","#exit","#lines","#buffer","#waiters","#signal","#abort","#iterate","#push","#finish","#close","#retain","#terminate","#tail","#closed","#stopping","#kill","#ending","#teardown","#next","#ended","#emitter","#children","#evict","#stopAll","#stopOne","#stopMany","#ending","#teardown"],"sources":["../../../src/server/helpers.ts","../../../src/server/Process.ts","../../../src/server/ProcessManager.ts","../../../src/server/factories.ts"],"sourcesContent":["import type { ChildProcessWithoutNullStreams } from 'node:child_process'\nimport type { ProcessCommand, RunOptions, RunResult } from '@src/core'\nimport { Buffer } from 'node:buffer'\nimport { spawn, spawnSync } from 'node:child_process'\nimport { extname } from 'node:path'\nimport { createRunError, PROCESS_GRACE, PROCESS_OUTPUT } from '@src/core'\n\n/**\n * Signals one owned child process, or its detached process group on a POSIX host.\n *\n * @remarks\n * On a POSIX host a child spawned `detached` leads its own process group, so signalling the\n * negated pid reaches the whole tree. On Windows, or when no pid is available, the signal goes to\n * the child directly. A throw during signalling is swallowed: the process can exit and win the\n * signal race, and the child's close event remains the authoritative terminal state.\n *\n * @param child - The child process signal boundary\n * @param signal - The signal to deliver\n * @returns Nothing\n *\n * @example\n * ```ts\n * killProcess(child, 'SIGTERM')\n * ```\n */\nexport function killProcess(\n\tchild: { readonly pid?: number | undefined; kill(signal: NodeJS.Signals): boolean },\n\tsignal: NodeJS.Signals,\n): void {\n\ttry {\n\t\tif (process.platform === 'win32' || child.pid === undefined) {\n\t\t\tchild.kill(signal)\n\t\t\treturn\n\t\t}\n\t\tprocess.kill(-child.pid, signal)\n\t} catch {\n\t\t// Exit can win the signal race; the close event remains authoritative.\n\t}\n}\n\n/**\n * Trims a buffer to at most `limit` trailing bytes without splitting a UTF-8 sequence.\n *\n * @remarks\n * Keeps the tail — the diagnostic end of a stderr stream. When the cut point lands inside a\n * multibyte sequence, the start advances past the leading continuation bytes so the retained\n * bytes always begin on a code-point boundary.\n *\n * @param buffer - The accumulated bytes\n * @param limit - The maximum retained byte count\n * @returns The trailing bytes, at most `limit` and never starting mid-sequence\n *\n * @example\n * ```ts\n * trimTail(Buffer.from('hello'), 3) // <Buffer 6c 6c 6f>\n * ```\n */\nexport function trimTail(buffer: Buffer, limit: number): Buffer {\n\tif (buffer.byteLength <= limit) return buffer\n\tlet start = buffer.byteLength - limit\n\twhile (start < buffer.byteLength) {\n\t\tconst byte = buffer[start]\n\t\tif (byte === undefined || (byte & 0xc0) !== 0x80) break\n\t\tstart += 1\n\t}\n\treturn Buffer.from(buffer.subarray(start))\n}\n\n/**\n * Trims a buffer to at most `limit` leading bytes without splitting a UTF-8 sequence.\n *\n * @remarks\n * Keeps the head — the captured start of a one-shot run's output. When the cut point lands inside\n * a multibyte sequence, the end retreats to the sequence's start so the retained bytes always end\n * on a code-point boundary and decode without a replacement character.\n *\n * @param buffer - The accumulated bytes\n * @param limit - The maximum retained byte count\n * @returns The leading bytes, at most `limit` and never ending mid-sequence\n *\n * @example\n * ```ts\n * trimHead(Buffer.from('hello'), 3) // <Buffer 68 65 6c>\n * ```\n */\nexport function trimHead(buffer: Buffer, limit: number): Buffer {\n\tif (buffer.byteLength <= limit) return buffer\n\tlet end = limit\n\twhile (end > 0) {\n\t\tconst byte = buffer[end]\n\t\tif (byte === undefined || (byte & 0xc0) !== 0x80) break\n\t\tend -= 1\n\t}\n\treturn Buffer.from(buffer.subarray(0, end))\n}\n\n/**\n * Checks whether a command file must be spawned through a shell on the current host.\n *\n * @remarks\n * Node v22+ refuses to spawn a Windows `.cmd` or `.bat` batch file directly and returns `EINVAL`,\n * and a bare command name such as `git` resolves through `PATHEXT` to a batch shim rather than an\n * executable. Both cases run through the shell. A POSIX host, and a Windows path carrying a real\n * executable extension such as `.exe`, spawn directly. When the result is `true`, treat the\n * command file and arguments as trusted: shell invocation exposes shell metacharacters.\n *\n * @param file - The command executable name or path\n * @returns True when the file must run through a shell; false when it spawns directly\n *\n * @example\n * ```ts\n * requiresShell('git') // true on Windows, false on POSIX\n * ```\n */\nexport function requiresShell(file: string): boolean {\n\tif (process.platform !== 'win32') return false\n\tconst lower = file.toLowerCase()\n\treturn lower.endsWith('.cmd') || lower.endsWith('.bat') || extname(file) === ''\n}\n\n/**\n * Renders one command into its diagnostic command line.\n *\n * @param command - The executable and its argument vector\n * @returns The space-joined command line, for a {@link RunResult} and error messages\n *\n * @example\n * ```ts\n * commandLine({ file: 'git', arguments: ['status'] }) // 'git status'\n * ```\n */\nexport function commandLine(command: ProcessCommand): string {\n\treturn [command.file, ...command.arguments].join(' ')\n}\n\n/**\n * Merges environment overrides over the current process environment.\n *\n * @remarks\n * Later maps override earlier ones and an `undefined` value unsets a key. The base is the parent\n * process environment, so a child inherits it unless a key is overridden or unset.\n *\n * @param base - The command's own environment overrides\n * @param override - Per-invocation overrides applied last\n * @returns The merged environment for a spawn\n *\n * @example\n * ```ts\n * mergeEnvironment({ TOKEN: 'a' }, { TOKEN: undefined }) // TOKEN unset\n * ```\n */\nexport function mergeEnvironment(\n\tbase?: Readonly<Record<string, string | undefined>>,\n\toverride?: Readonly<Record<string, string | undefined>>,\n): NodeJS.ProcessEnv {\n\treturn { ...process.env, ...base, ...override }\n}\n\n/**\n * Builds one settled {@link RunResult} from a completed one-shot run's captured bytes and exit.\n *\n * @remarks\n * `failed` is derived: a run failed when it timed out, was ended by a signal, or exited with a\n * code other than `0` — a `null` code from a spawn fault is therefore a failure. Both output\n * buffers are byte-bounded by `limit` before decoding.\n *\n * @param command - The diagnostic command line\n * @param stdout - The captured standard output bytes\n * @param stderr - The captured standard error bytes\n * @param code - The exit code, or `null` when a signal or spawn fault ended the run\n * @param signal - The terminating signal, or `null`\n * @param timedOut - If `true`, the run's own timeout elapsed; if `false`, it did not\n * @param limit - The maximum captured bytes for each stream\n * @returns The frozen run outcome\n *\n * @example\n * ```ts\n * buildRunResult('git status', Buffer.from('ok'), Buffer.alloc(0), 0, null, false, 1024)\n * ```\n */\nexport function buildRunResult(\n\tcommand: string,\n\tstdout: Buffer,\n\tstderr: Buffer,\n\tcode: number | null,\n\tsignal: string | null,\n\ttimedOut: boolean,\n\tlimit: number,\n): RunResult {\n\treturn Object.freeze({\n\t\tcommand,\n\t\tstdout: trimHead(stdout, limit).toString('utf8'),\n\t\tstderr: trimHead(stderr, limit).toString('utf8'),\n\t\tcode,\n\t\tsignal,\n\t\tfailed: timedOut || signal !== null || code !== 0,\n\t\ttimedOut,\n\t})\n}\n\n/**\n * Runs one command to completion, buffering its output, and settles with the outcome.\n *\n * @remarks\n * The child is spawned through the cross-platform command resolver ({@link requiresShell}) and, on\n * a POSIX host, detached so its whole process group can be terminated. Standard output and error\n * are each byte-bounded by `limit` (default {@link PROCESS_OUTPUT}). A positive `timeout` arms a\n * timer that terminates the child through `SIGTERM`, then `SIGKILL` after `grace`\n * (default {@link PROCESS_GRACE}); aborting `signal` terminates the same way. The child's\n * `environment` merges over the parent, then `options.environment` on top, and `options.input`\n * overrides `command.input`. Unless `reject` is `false`, a failed run rejects with a\n * {@link createRunError} carrying the {@link RunResult}.\n *\n * @param command - The executable, arguments, and optional environment and input\n * @param options - Working directory, timeout, grace, signal, capture limit, and reject behavior\n * @returns The settled run outcome\n * @throws A {@link ProcessError} carrying the {@link RunResult} when the run failed and `reject` is not `false`\n *\n * @example\n * ```ts\n * const result = await run({ file: 'git', arguments: ['status'] }, { workspace: process.cwd() })\n * ```\n */\nexport async function run(command: ProcessCommand, options?: RunOptions): Promise<RunResult> {\n\tconst limit = options?.limit ?? PROCESS_OUTPUT\n\tconst grace = options?.grace ?? PROCESS_GRACE\n\tconst timeout = options?.timeout ?? 0\n\tconst reject = options?.reject ?? true\n\tconst input = options?.input ?? command.input\n\tconst line = commandLine(command)\n\tconst settled = Promise.withResolvers<RunResult>()\n\tconst terminate = new AbortController()\n\tconst cleanup = new AbortController()\n\tlet stdout: Buffer = Buffer.alloc(0)\n\tlet stderr: Buffer = Buffer.alloc(0)\n\tlet timedOut = false\n\tlet launchCause: unknown\n\tlet killTimer: ReturnType<typeof setTimeout> | undefined\n\tlet timeoutTimer: ReturnType<typeof setTimeout> | undefined\n\tlet done = false\n\n\tconst child: ChildProcessWithoutNullStreams = spawn(command.file, [...command.arguments], {\n\t\tcwd: options?.workspace ?? process.cwd(),\n\t\tdetached: process.platform !== 'win32',\n\t\tenv: mergeEnvironment(command.environment, options?.environment),\n\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\tshell: requiresShell(command.file),\n\t\twindowsHide: true,\n\t})\n\n\tterminate.signal.addEventListener(\n\t\t'abort',\n\t\t() => {\n\t\t\tkillProcess(child, 'SIGTERM')\n\t\t\tkillTimer = setTimeout(() => killProcess(child, 'SIGKILL'), grace)\n\t\t},\n\t\t{ once: true },\n\t)\n\n\tchild.stdin.on('error', () => undefined)\n\tchild.stdout.on('data', (chunk: unknown) => {\n\t\tif (Buffer.isBuffer(chunk)) stdout = trimHead(Buffer.concat([stdout, chunk]), limit)\n\t})\n\tchild.stderr.on('data', (chunk: unknown) => {\n\t\tif (Buffer.isBuffer(chunk)) stderr = trimHead(Buffer.concat([stderr, chunk]), limit)\n\t})\n\tchild.once('error', (cause: unknown) => {\n\t\tif (done) return\n\t\tdone = true\n\t\tlaunchCause = cause\n\t\tcleanup.abort()\n\t\tclearTimeout(killTimer)\n\t\tclearTimeout(timeoutTimer)\n\t\tsettled.resolve(buildRunResult(line, stdout, stderr, null, null, false, limit))\n\t})\n\tchild.once('close', (code: number | null, signal: NodeJS.Signals | null) => {\n\t\tif (done) return\n\t\tdone = true\n\t\tcleanup.abort()\n\t\tclearTimeout(killTimer)\n\t\tclearTimeout(timeoutTimer)\n\t\tsettled.resolve(buildRunResult(line, stdout, stderr, code, signal, timedOut, limit))\n\t})\n\n\tif (input !== undefined) child.stdin.write(input)\n\tchild.stdin.end()\n\tif (timeout > 0) {\n\t\ttimeoutTimer = setTimeout(() => {\n\t\t\ttimedOut = true\n\t\t\tterminate.abort()\n\t\t}, timeout)\n\t}\n\tif (options?.signal !== undefined) {\n\t\tconst signal = options.signal\n\t\tsignal.addEventListener('abort', () => terminate.abort(), {\n\t\t\tonce: true,\n\t\t\tsignal: cleanup.signal,\n\t\t})\n\t\tif (signal.aborted) terminate.abort()\n\t}\n\n\tconst result = await settled.promise\n\tif (result.failed && reject) throw createRunError(result, launchCause)\n\treturn result\n}\n\n/**\n * Runs one command to completion synchronously, buffering its output, and returns the outcome.\n *\n * @remarks\n * The synchronous counterpart of {@link run}. The child is spawned through the same cross-platform\n * command resolver ({@link requiresShell}). Standard output and error are each byte-bounded by\n * `limit` (default {@link PROCESS_OUTPUT}). A positive `timeout` is enforced by the host, which\n * ends the child with `SIGKILL` when it elapses. The environment and input follow the same merge\n * as {@link run}. Unless `reject` is `false`, a failed run throws a {@link createRunError} carrying\n * the {@link RunResult}.\n *\n * @param command - The executable, arguments, and optional environment and input\n * @param options - Working directory, timeout, signal, capture limit, and reject behavior\n * @returns The run outcome\n * @throws A {@link ProcessError} carrying the {@link RunResult} when the run failed and `reject` is not `false`\n *\n * @example\n * ```ts\n * const result = runSync({ file: 'git', arguments: ['--version'] }, { reject: false })\n * ```\n */\nexport function runSync(command: ProcessCommand, options?: RunOptions): RunResult {\n\tconst limit = options?.limit ?? PROCESS_OUTPUT\n\tconst timeout = options?.timeout ?? 0\n\tconst reject = options?.reject ?? true\n\tconst input = options?.input ?? command.input\n\tconst line = commandLine(command)\n\tconst outcome = spawnSync(command.file, [...command.arguments], {\n\t\tcwd: options?.workspace ?? process.cwd(),\n\t\tenv: mergeEnvironment(command.environment, options?.environment),\n\t\tencoding: 'buffer',\n\t\tmaxBuffer: limit,\n\t\tshell: requiresShell(command.file),\n\t\twindowsHide: true,\n\t\t...(input !== undefined ? { input } : {}),\n\t\t...(options?.signal !== undefined ? { signal: options.signal } : {}),\n\t\t...(timeout > 0 ? { timeout, killSignal: 'SIGKILL' } : {}),\n\t})\n\tconst error = outcome.error\n\tconst timedOut = error !== undefined && 'code' in error && error.code === 'ETIMEDOUT'\n\tconst stdout = Buffer.isBuffer(outcome.stdout) ? outcome.stdout : Buffer.alloc(0)\n\tconst stderr = Buffer.isBuffer(outcome.stderr) ? outcome.stderr : Buffer.alloc(0)\n\tconst result = buildRunResult(\n\t\tline,\n\t\tstdout,\n\t\tstderr,\n\t\toutcome.status,\n\t\toutcome.signal,\n\t\ttimedOut,\n\t\tlimit,\n\t)\n\tif (result.failed && reject) throw createRunError(result, outcome.error)\n\treturn result\n}\n","import type { ChildProcessWithoutNullStreams } from 'node:child_process'\nimport type { Interface as ReadLineInterface } from 'node:readline'\nimport type { EmitterInterface } from '@orkestrel/emitter'\nimport type { ProcessEventMap, ProcessExit, ProcessInterface, ProcessOptions } from '@src/core'\nimport { Buffer } from 'node:buffer'\nimport { spawn } from 'node:child_process'\nimport { createInterface } from 'node:readline'\nimport { StringDecoder } from 'node:string_decoder'\nimport { Emitter } from '@orkestrel/emitter'\nimport { PROCESS_EVIDENCE } from '@src/core'\nimport { killProcess, mergeEnvironment, trimTail } from './helpers.js'\n\n/**\n * One supervised child process with eagerly framed output and bounded termination.\n *\n * @remarks\n * Standard output is drained eagerly through `readline`, so `exit` resolves and a late consumer of\n * `lines` still receives every framed line, including a final line written without a trailing\n * newline. Standard error is decoded and forwarded live as the `stderr` event while a byte-bounded\n * raw tail is retained as `evidence`. The typed `emitter` also carries the child `error` cause on a\n * spawn fault and the terminal `exit`, alongside the `exit` promise. `stop` terminates the child\n * through `SIGTERM`, then `SIGKILL` after\n * `grace`, and is idempotent; `destroy` stops the child and destroys the emitter last.\n *\n * @example\n * ```ts\n * import { Process } from '@orkestrel/process/server'\n *\n * const child = new Process({\n * \tcommand: { file: 'node', arguments: ['--version'] },\n * \tworkspace: process.cwd(),\n * \tgrace: 5000,\n * })\n * const exit = await child.exit\n * await child.destroy()\n * ```\n */\nexport class Process implements ProcessInterface {\n\treadonly #emitter: Emitter<ProcessEventMap>\n\treadonly #child: ChildProcessWithoutNullStreams\n\treadonly #reader: ReadLineInterface\n\treadonly #grace: number\n\treadonly #limit: number\n\treadonly #decoder = new StringDecoder('utf8')\n\treadonly #exit = Promise.withResolvers<ProcessExit>()\n\treadonly #lines: AsyncIterable<string>\n\treadonly #buffer: string[] = []\n\treadonly #waiters: Array<PromiseWithResolvers<IteratorResult<string, void>>> = []\n\treadonly #signal: AbortSignal | undefined\n\treadonly #abort: EventListener | undefined\n\t#tail: Buffer = Buffer.alloc(0)\n\t#closed = false\n\t#ended = false\n\t#stopping: Promise<void> | undefined\n\t#ending: Promise<void> | undefined\n\n\t/**\n\t * Spawn one child process and begin eager stream capture.\n\t *\n\t * @param options - Command, workspace, termination, evidence, stdin, and observation settings\n\t */\n\tconstructor(options: ProcessOptions) {\n\t\tconst on = options.on\n\t\tconst error = options.error\n\t\tthis.#emitter = new Emitter<ProcessEventMap>({\n\t\t\t...(on === undefined ? {} : { on }),\n\t\t\t...(error === undefined ? {} : { error }),\n\t\t})\n\t\tthis.#grace = options.grace\n\t\tthis.#limit = options.evidence ?? PROCESS_EVIDENCE\n\t\tthis.#signal = options.signal\n\t\tthis.#lines = Object.freeze({ [Symbol.asyncIterator]: this.#iterate.bind(this) })\n\t\tthis.#child = spawn(options.command.file, [...options.command.arguments], {\n\t\t\tcwd: options.workspace,\n\t\t\tdetached: process.platform !== 'win32',\n\t\t\tenv: mergeEnvironment(options.command.environment),\n\t\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\t\twindowsHide: true,\n\t\t})\n\t\tthis.#reader = createInterface({ input: this.#child.stdout, crlfDelay: Infinity })\n\t\tthis.#reader.on('line', this.#push.bind(this))\n\t\tthis.#reader.once('close', this.#finish.bind(this))\n\t\tthis.#child.once('error', (cause: unknown) => this.#emitter.emit('error', cause))\n\t\tthis.#child.once('close', this.#close.bind(this))\n\t\tthis.#child.stdin.on('error', () => undefined)\n\t\tthis.#child.stderr.on('data', this.#retain.bind(this))\n\t\tif (options.command.input !== undefined) this.#child.stdin.write(options.command.input)\n\t\tif (options.writable !== true) this.#child.stdin.end()\n\t\tif (this.#signal !== undefined) {\n\t\t\tthis.#abort = this.#terminate.bind(this)\n\t\t\tthis.#signal.addEventListener('abort', this.#abort, { once: true })\n\t\t\tif (this.#signal.aborted) void this.stop()\n\t\t}\n\t}\n\n\t/** The typed lifecycle observation surface. */\n\tget emitter(): EmitterInterface<ProcessEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\t/** The eagerly captured stdout lines, in arrival order, ending when the child's stdout closes. */\n\tget lines(): AsyncIterable<string> {\n\t\treturn this.#lines\n\t}\n\n\t/** The decoded byte-bounded stderr tail. */\n\tget evidence(): string {\n\t\treturn this.#tail.toString('utf8')\n\t}\n\n\t/** The terminal child state, observed once from the close event. */\n\tget exit(): Promise<ProcessExit> {\n\t\treturn this.#exit.promise\n\t}\n\n\t/**\n\t * Write one line to the open standard-input channel.\n\t *\n\t * @param text - The line text without its trailing newline\n\t * @returns True when the channel accepted the line without backpressure; false otherwise\n\t */\n\tsend(text: string): boolean {\n\t\tif (this.#closed || this.#child.stdin.destroyed || this.#child.stdin.writableEnded) return false\n\t\ttry {\n\t\t\treturn this.#child.stdin.write(`${text}\\n`)\n\t\t} catch {\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Terminate the child process tree and await its observed exit.\n\t *\n\t * @returns A promise that resolves after bounded termination\n\t */\n\tstop(): Promise<void> {\n\t\tif (this.#stopping !== undefined) return this.#stopping\n\t\tthis.#stopping = this.#kill()\n\t\treturn this.#stopping\n\t}\n\n\t/**\n\t * Stop the child and destroy the observation emitter.\n\t *\n\t * @returns The stable barrier shared by every call\n\t */\n\tdestroy(): Promise<void> {\n\t\tif (this.#ending !== undefined) return this.#ending\n\t\tthis.#ending = this.#teardown()\n\t\treturn this.#ending\n\t}\n\n\t#iterate(): AsyncIterator<string, void, void> {\n\t\treturn Object.freeze({ next: this.#next.bind(this) })\n\t}\n\n\t#next(): Promise<IteratorResult<string, void>> {\n\t\tconst line = this.#buffer.shift()\n\t\tif (line !== undefined) return Promise.resolve(Object.freeze({ done: false, value: line }))\n\t\tif (this.#ended) return Promise.resolve(Object.freeze({ done: true, value: undefined }))\n\t\tconst waiter = Promise.withResolvers<IteratorResult<string, void>>()\n\t\tthis.#waiters.push(waiter)\n\t\treturn waiter.promise\n\t}\n\n\t#push(line: string): void {\n\t\tif (this.#ended) return\n\t\tconst waiter = this.#waiters.shift()\n\t\tif (waiter === undefined) {\n\t\t\tthis.#buffer.push(line)\n\t\t\treturn\n\t\t}\n\t\twaiter.resolve(Object.freeze({ done: false, value: line }))\n\t}\n\n\t#finish(): void {\n\t\tif (this.#ended) return\n\t\tthis.#ended = true\n\t\tfor (const waiter of this.#waiters) {\n\t\t\twaiter.resolve(Object.freeze({ done: true, value: undefined }))\n\t\t}\n\t\tthis.#waiters.length = 0\n\t}\n\n\t#retain(chunk: unknown): void {\n\t\tif (!Buffer.isBuffer(chunk) || this.#closed) return\n\t\tthis.#tail = trimTail(Buffer.concat([this.#tail, chunk]), this.#limit)\n\t\tconst text = this.#decoder.write(chunk)\n\t\tif (text.length > 0) this.#emitter.emit('stderr', text)\n\t}\n\n\t#close(code: number | null, signal: NodeJS.Signals | null): void {\n\t\tif (this.#closed) return\n\t\tconst suffix = this.#decoder.end()\n\t\tif (suffix.length > 0) this.#emitter.emit('stderr', suffix)\n\t\tthis.#closed = true\n\t\tif (this.#signal !== undefined && this.#abort !== undefined) {\n\t\t\tthis.#signal.removeEventListener('abort', this.#abort)\n\t\t}\n\t\tthis.#reader.close()\n\t\tconst exit = Object.freeze({ code, signal })\n\t\tthis.#exit.resolve(exit)\n\t\tthis.#emitter.emit('exit', exit)\n\t}\n\n\t#terminate(): void {\n\t\tvoid this.stop()\n\t}\n\n\tasync #kill(): Promise<void> {\n\t\tif (this.#closed) return\n\t\tkillProcess(this.#child, 'SIGTERM')\n\t\tconst grace = Promise.withResolvers<void>()\n\t\tconst timer = setTimeout(grace.resolve, this.#grace)\n\t\tawait Promise.race([this.#exit.promise, grace.promise])\n\t\tclearTimeout(timer)\n\t\tif (this.#closed) return\n\t\tkillProcess(this.#child, 'SIGKILL')\n\t\tawait this.#exit.promise\n\t}\n\n\tasync #teardown(): Promise<void> {\n\t\tawait this.stop()\n\t\tthis.#emitter.destroy()\n\t}\n}\n","import type { EmitterInterface } from '@orkestrel/emitter'\nimport type {\n\tProcessExit,\n\tProcessInterface,\n\tProcessManagerEventMap,\n\tProcessManagerInterface,\n\tProcessManagerOptions,\n\tProcessOptions,\n} from '@src/core'\nimport { Emitter } from '@orkestrel/emitter'\nimport { createDuplicateError } from '@src/core'\nimport { Process } from './Process.js'\n\n/**\n * A keyed registry of live supervised child processes.\n *\n * @remarks\n * A child launched under an id joins the registry and emits `launch`; when it settles it removes\n * itself and emits `exit`, so `count` and `processes` reflect only live children. `launch` throws\n * a {@link createDuplicateError} when the id is already live — a spawn fault surfaces through the\n * returned child's `exit`, never from `launch`. `destroy` stops every child, then destroys the\n * registry emitter last.\n *\n * @example\n * ```ts\n * import { ProcessManager } from '@orkestrel/process/server'\n *\n * const manager = new ProcessManager()\n * const child = manager.launch('build', {\n * \tcommand: { file: 'node', arguments: ['build.js'] },\n * \tworkspace: process.cwd(),\n * \tgrace: 5000,\n * })\n * await child.exit\n * await manager.destroy()\n * ```\n */\nexport class ProcessManager implements ProcessManagerInterface {\n\treadonly #emitter: Emitter<ProcessManagerEventMap>\n\treadonly #children = new Map<string, ProcessInterface>()\n\t#ending: Promise<void> | undefined\n\n\t/**\n\t * Construct a process registry.\n\t *\n\t * @param options - Initial fleet-level observation hooks and listener-error handling\n\t */\n\tconstructor(options?: ProcessManagerOptions) {\n\t\tconst on = options?.on\n\t\tconst error = options?.error\n\t\tthis.#emitter = new Emitter<ProcessManagerEventMap>({\n\t\t\t...(on === undefined ? {} : { on }),\n\t\t\t...(error === undefined ? {} : { error }),\n\t\t})\n\t}\n\n\t/** The typed fleet-level observation surface. */\n\tget emitter(): EmitterInterface<ProcessManagerEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\t/** The number of live children. */\n\tget count(): number {\n\t\treturn this.#children.size\n\t}\n\n\t/**\n\t * The live child under `id`, or `undefined` when none is.\n\t *\n\t * @param id - The registry key\n\t * @returns The child, or `undefined`\n\t */\n\tprocess(id: string): ProcessInterface | undefined {\n\t\treturn this.#children.get(id)\n\t}\n\n\t/**\n\t * A snapshot of every live child.\n\t *\n\t * @returns The live children in launch order\n\t */\n\tprocesses(): readonly ProcessInterface[] {\n\t\treturn [...this.#children.values()]\n\t}\n\n\t/**\n\t * Spawn and register one child under `id`.\n\t *\n\t * @param id - The registry key, unique among live children\n\t * @param options - The child construction options\n\t * @returns The launched child\n\t * @throws A {@link ProcessError} coded `duplicate` when `id` is already live\n\t */\n\tlaunch(id: string, options: ProcessOptions): ProcessInterface {\n\t\tif (this.#children.has(id)) throw createDuplicateError(id)\n\t\tconst child = new Process(options)\n\t\tthis.#children.set(id, child)\n\t\tchild.emitter.on('exit', (exit) => this.#evict(id, child, exit))\n\t\tthis.#emitter.emit('launch', id)\n\t\treturn child\n\t}\n\n\t/**\n\t * Terminate the named children and await their exit.\n\t *\n\t * @param ids - The registry keys to stop\n\t * @returns True when every named child stopped, false when any id was not live\n\t */\n\tstop(ids: readonly string[]): Promise<boolean>\n\t/**\n\t * Terminate one child and await its exit.\n\t *\n\t * @param id - The registry key to stop\n\t * @returns True when the child was live and stopped, false when the id was not live\n\t */\n\tstop(id: string): Promise<boolean>\n\t/**\n\t * Terminate every live child and await their exit.\n\t *\n\t * @returns A promise that resolves after all children stop\n\t */\n\tstop(): Promise<void>\n\tstop(target?: string | readonly string[]): Promise<boolean | void> {\n\t\tif (target === undefined) return this.#stopAll()\n\t\tif (typeof target === 'string') return this.#stopOne(target)\n\t\treturn this.#stopMany(target)\n\t}\n\n\t/**\n\t * Stop every child, then destroy the registry emitter last.\n\t *\n\t * @returns The stable barrier shared by every call\n\t */\n\tdestroy(): Promise<void> {\n\t\tif (this.#ending !== undefined) return this.#ending\n\t\tthis.#ending = this.#teardown()\n\t\treturn this.#ending\n\t}\n\n\t#evict(id: string, child: ProcessInterface, exit: ProcessExit): void {\n\t\tif (this.#children.get(id) !== child) return\n\t\tthis.#children.delete(id)\n\t\tthis.#emitter.emit('exit', id, exit)\n\t}\n\n\tasync #stopOne(id: string): Promise<boolean> {\n\t\tconst child = this.#children.get(id)\n\t\tif (child === undefined) return false\n\t\tawait child.stop()\n\t\treturn true\n\t}\n\n\tasync #stopMany(ids: readonly string[]): Promise<boolean> {\n\t\tconst stopped = await Promise.all(ids.map((id) => this.#stopOne(id)))\n\t\treturn stopped.every((ok) => ok)\n\t}\n\n\tasync #stopAll(): Promise<void> {\n\t\tawait Promise.all([...this.#children.values()].map((child) => child.stop()))\n\t}\n\n\tasync #teardown(): Promise<void> {\n\t\tawait Promise.all([...this.#children.values()].map((child) => child.destroy()))\n\t\tthis.#emitter.destroy()\n\t}\n}\n","import type {\n\tProcessInterface,\n\tProcessManagerInterface,\n\tProcessManagerOptions,\n\tProcessOptions,\n} from '@src/core'\nimport { Process } from './Process.js'\nimport { ProcessManager } from './ProcessManager.js'\n\n/**\n * Creates one supervised child process.\n *\n * @param options - Command, workspace, termination, evidence, stdin, and observation settings\n * @returns The launched {@link ProcessInterface}\n *\n * @example\n * ```ts\n * import { createProcess } from '@orkestrel/process/server'\n *\n * const child = createProcess({\n * \tcommand: { file: 'node', arguments: ['--version'] },\n * \tworkspace: process.cwd(),\n * \tgrace: 5000,\n * })\n * ```\n */\nexport function createProcess(options: ProcessOptions): ProcessInterface {\n\treturn new Process(options)\n}\n\n/**\n * Creates one keyed registry of supervised child processes.\n *\n * @param options - Initial fleet-level observation hooks and listener-error handling\n * @returns The {@link ProcessManagerInterface} registry\n *\n * @example\n * ```ts\n * import { createProcessManager } from '@orkestrel/process/server'\n *\n * const manager = createProcessManager()\n * ```\n */\nexport function createProcessManager(options?: ProcessManagerOptions): ProcessManagerInterface {\n\treturn new ProcessManager(options)\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orkestrel/process",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "The @orkestrel/process package.",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/orkestrel/process#readme",