@orkestrel/process 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/core/index.cjs +17 -0
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +121 -19
- package/dist/src/core/index.d.ts +121 -19
- package/dist/src/core/index.js +17 -1
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +127 -66
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +72 -55
- package/dist/src/server/index.d.ts +72 -55
- package/dist/src/server/index.js +128 -68
- package/dist/src/server/index.js.map +1 -1
- package/package.json +2 -2
package/dist/src/core/index.cjs
CHANGED
|
@@ -5,6 +5,22 @@ let _orkestrel_contract = require("@orkestrel/contract");
|
|
|
5
5
|
var PROCESS_GRACE = 5e3;
|
|
6
6
|
/** The window in milliseconds a termination waits for the child's native exit after the final kill. */
|
|
7
7
|
var PROCESS_CONFIRMATION = 5e3;
|
|
8
|
+
/**
|
|
9
|
+
* The default window in milliseconds the package waits for the child's read ends to close after
|
|
10
|
+
* the child's native exit or after a termination this package initiated, before cutting them off.
|
|
11
|
+
*
|
|
12
|
+
* @remarks
|
|
13
|
+
* Measured on Windows 11 with Node v24.18.1 on 2026-08-21, spawning the `tests/src/server/fixtures`
|
|
14
|
+
* child modes as real children and timing the host `close` event against the host `exit` event.
|
|
15
|
+
* Every ordinary run closed within 0.02ms of the native exit, whether the child ended itself or a
|
|
16
|
+
* kill ended it, and a `taskkill /F /T` that reaped a descendant while the root was still alive
|
|
17
|
+
* closed within 0.01ms. The only finite late close came from a descendant that outlived its root and
|
|
18
|
+
* then ended on its own, at 127.44ms to 193.41ms — that lag is the descendant's own remaining life
|
|
19
|
+
* rather than a flush cost. A descendant that never ends never closes, so the distribution has no
|
|
20
|
+
* finite tail to cover and this value bounds it instead: 1000ms is over five times the slowest
|
|
21
|
+
* finite close measured, and it keeps an unreachable descendant from stretching a shutdown.
|
|
22
|
+
*/
|
|
23
|
+
var PROCESS_DRAIN = 1e3;
|
|
8
24
|
/** The default maximum retained stderr tail in bytes for a supervised {@link ProcessInterface}. */
|
|
9
25
|
var PROCESS_EVIDENCE = 2048;
|
|
10
26
|
/** The default soft high-water mark in bytes for a supervised {@link ProcessInterface} line backlog. */
|
|
@@ -151,6 +167,7 @@ function createExecuteError(result, cause) {
|
|
|
151
167
|
//#endregion
|
|
152
168
|
exports.PROCESS_BACKLOG = PROCESS_BACKLOG;
|
|
153
169
|
exports.PROCESS_CONFIRMATION = PROCESS_CONFIRMATION;
|
|
170
|
+
exports.PROCESS_DRAIN = PROCESS_DRAIN;
|
|
154
171
|
exports.PROCESS_ERROR_CODES = PROCESS_ERROR_CODES;
|
|
155
172
|
exports.PROCESS_EVIDENCE = PROCESS_EVIDENCE;
|
|
156
173
|
exports.PROCESS_GRACE = PROCESS_GRACE;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts"],"sourcesContent":["/** The default cooperative POSIX window in milliseconds between `SIGTERM` and `SIGKILL` during termination. */\nexport const PROCESS_GRACE = 5_000\n\n/** The window in milliseconds a termination waits for the child's native exit after the final kill. */\nexport const PROCESS_CONFIRMATION = 5_000\n\n/** The default maximum retained stderr tail in bytes for a supervised {@link ProcessInterface}. */\nexport const PROCESS_EVIDENCE = 2_048\n\n/** The default soft high-water mark in bytes for a supervised {@link ProcessInterface} line backlog. */\nexport const PROCESS_BACKLOG = 10_485_760\n\n/** The default maximum captured bytes for a one-shot run's stdout and stderr, each. */\nexport const PROCESS_OUTPUT = 10_485_760\n\n/** The largest timer delay in milliseconds the host schedules without truncating it to one. */\nexport const PROCESS_TIMER = 2_147_483_647\n\n/** The executable extensions a Windows lookup applies when the environment declares no `PATHEXT`. */\nexport const PROCESS_PATHEXT = '.COM;.EXE;.BAT;.CMD'\n\n/** The machine-readable failure categories a {@link ProcessError} carries, in declaration order. */\nexport const PROCESS_ERROR_CODES = Object.freeze([\n\t'spawn',\n\t'timeout',\n\t'input',\n\t'duplicate',\n\t'protocol',\n\t'invalid',\n] as const)\n","import type {\n\tProcessErrorCode,\n\tProcessErrorContext,\n\tProcessErrorOptions,\n\tExecuteResult,\n} from './types.js'\nimport { holds, isError } from '@orkestrel/contract'\nimport { PROCESS_ERROR_CODES } from './constants.js'\n\n/** A child-process failure with a stable machine-readable category. */\nexport class ProcessError extends Error {\n\toverride readonly name = 'ProcessError'\n\treadonly code: ProcessErrorCode\n\treadonly context?: ProcessErrorContext\n\t/** The buffered run outcome, present when a one-shot run produced the failure. */\n\treadonly result?: ExecuteResult\n\n\t/**\n\t * Create a process error.\n\t *\n\t * @param message - Human-readable failure description\n\t * @param options - Machine-readable category, optional context, optional cause, and optional run result\n\t */\n\tconstructor(message: string, options: ProcessErrorOptions) {\n\t\tconst cause = options.cause\n\t\tsuper(message, cause === undefined ? undefined : { cause })\n\t\tObject.defineProperty(this, Symbol.for('@orkestrel/process.error'), { value: true })\n\t\tthis.code = options.code\n\t\tif (options.context !== undefined) this.context = options.context\n\t\tif (options.result !== undefined) this.result = options.result\n\t}\n}\n\n/**\n * Checks whether an unknown value is a {@link ProcessError}.\n *\n * @remarks\n * Recognition combines a global own-property brand with the native `Error` base, the subclass\n * prototype, the fixed name, and a code {@link PROCESS_ERROR_CODES} declares. The brand is\n * recognized across duplicate installations and ESM/CommonJS module copies at 0.0.4 or later. A copy\n * earlier than 0.0.4 stamps no brand, so an error it throws stays outside the type, and so does a\n * plain or property-only lookalike.\n *\n * @param value - The value to inspect\n * @returns True only for a `ProcessError` instance; false otherwise\n *\n * @example\n * ```ts\n * isProcessError(new ProcessError('spawn failed', { code: 'spawn' })) // true\n * isProcessError(new Error('spawn failed')) // false\n * ```\n */\nexport function isProcessError(value: unknown): value is ProcessError {\n\tif (!isError(value)) return false\n\treturn holds(() => {\n\t\tif (Object.getPrototypeOf(value) === Error.prototype) return false\n\t\tif (value.name !== 'ProcessError' || !('code' in value)) return false\n\t\tconst descriptor = Object.getOwnPropertyDescriptor(\n\t\t\tvalue,\n\t\t\tSymbol.for('@orkestrel/process.error'),\n\t\t)\n\t\tif (descriptor?.value !== true) return false\n\t\tconst code: unknown = value.code\n\t\treturn PROCESS_ERROR_CODES.some((declared) => declared === code)\n\t})\n}\n\n/**\n * Creates the failure raised when a manager launch reuses a live id.\n *\n * @param id - The id already occupied by a live child\n * @returns A typed duplicate-id failure\n */\nexport function createDuplicateError(id: string): ProcessError {\n\treturn new ProcessError(`Process '${id}' is already live`, {\n\t\tcode: 'duplicate',\n\t\tcontext: { id },\n\t})\n}\n\n/**\n * Creates the failure raised when a launch is attempted on a registry that is being destroyed.\n *\n * @param id - The id the refused launch asked for\n * @returns A typed protocol failure\n */\nexport function createProtocolError(id: string): ProcessError {\n\treturn new ProcessError(`Process '${id}' cannot launch: the registry is destroyed`, {\n\t\tcode: 'protocol',\n\t\tcontext: { id },\n\t})\n}\n\n/**\n * Creates the failure raised when a public input is refused before anything is spawned.\n *\n * @param subject - The rejected input named as the caller wrote it, such as `option 'grace'`\n * @param value - The rejected value, carried as `context.value`\n * @returns A typed validation failure\n *\n * @example\n * ```ts\n * createInvalidError(\"option 'grace'\", -1).code // 'invalid'\n * ```\n */\nexport function createInvalidError(subject: string, value: unknown): ProcessError {\n\treturn new ProcessError(`Invalid ${subject}`, { code: 'invalid', context: { value } })\n}\n\n/**\n * Creates the failure raised when a run does not complete successfully and rejection is requested.\n *\n * @remarks\n * The category is `timeout` only when the run's own timeout elapsed; every other failure,\n * including an abort and an output overflow, is a `spawn` failure carrying its\n * {@link ExecuteResult}.\n *\n * @param result - The buffered run outcome that failed\n * @param cause - The underlying host fault, when one ended the run\n * @returns A typed run failure carrying its {@link ExecuteResult}\n */\nexport function createExecuteError(result: ExecuteResult, cause?: unknown): ProcessError {\n\tlet reason = `exited with code ${String(result.code)}`\n\tif (result.signal !== null) reason = `was killed by ${result.signal}`\n\tif (result.truncated) reason = 'exceeded its output limit'\n\tif (result.aborted) reason = 'was aborted'\n\tif (result.expired) reason = 'timed out'\n\treturn new ProcessError(`Command '${result.command}' ${reason}`, {\n\t\tcode: result.expired ? 'timeout' : 'spawn',\n\t\tcontext: { command: result.command, code: result.code, signal: result.signal },\n\t\t...(cause === undefined ? {} : { cause }),\n\t\tresult,\n\t})\n}\n"],"mappings":";;;;AACA,IAAa,gBAAgB;;AAG7B,IAAa,uBAAuB;;
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts"],"sourcesContent":["/** The default cooperative POSIX window in milliseconds between `SIGTERM` and `SIGKILL` during termination. */\nexport const PROCESS_GRACE = 5_000\n\n/** The window in milliseconds a termination waits for the child's native exit after the final kill. */\nexport const PROCESS_CONFIRMATION = 5_000\n\n/**\n * The default window in milliseconds the package waits for the child's read ends to close after\n * the child's native exit or after a termination this package initiated, before cutting them off.\n *\n * @remarks\n * Measured on Windows 11 with Node v24.18.1 on 2026-08-21, spawning the `tests/src/server/fixtures`\n * child modes as real children and timing the host `close` event against the host `exit` event.\n * Every ordinary run closed within 0.02ms of the native exit, whether the child ended itself or a\n * kill ended it, and a `taskkill /F /T` that reaped a descendant while the root was still alive\n * closed within 0.01ms. The only finite late close came from a descendant that outlived its root and\n * then ended on its own, at 127.44ms to 193.41ms — that lag is the descendant's own remaining life\n * rather than a flush cost. A descendant that never ends never closes, so the distribution has no\n * finite tail to cover and this value bounds it instead: 1000ms is over five times the slowest\n * finite close measured, and it keeps an unreachable descendant from stretching a shutdown.\n */\nexport const PROCESS_DRAIN = 1_000\n\n/** The default maximum retained stderr tail in bytes for a supervised {@link ProcessInterface}. */\nexport const PROCESS_EVIDENCE = 2_048\n\n/** The default soft high-water mark in bytes for a supervised {@link ProcessInterface} line backlog. */\nexport const PROCESS_BACKLOG = 10_485_760\n\n/** The default maximum captured bytes for a one-shot run's stdout and stderr, each. */\nexport const PROCESS_OUTPUT = 10_485_760\n\n/** The largest timer delay in milliseconds the host schedules without truncating it to one. */\nexport const PROCESS_TIMER = 2_147_483_647\n\n/** The executable extensions a Windows lookup applies when the environment declares no `PATHEXT`. */\nexport const PROCESS_PATHEXT = '.COM;.EXE;.BAT;.CMD'\n\n/** The machine-readable failure categories a {@link ProcessError} carries, in declaration order. */\nexport const PROCESS_ERROR_CODES = Object.freeze([\n\t'spawn',\n\t'timeout',\n\t'input',\n\t'duplicate',\n\t'protocol',\n\t'invalid',\n] as const)\n","import type {\n\tProcessErrorCode,\n\tProcessErrorContext,\n\tProcessErrorOptions,\n\tExecuteResult,\n} from './types.js'\nimport { holds, isError } from '@orkestrel/contract'\nimport { PROCESS_ERROR_CODES } from './constants.js'\n\n/** A child-process failure with a stable machine-readable category. */\nexport class ProcessError extends Error {\n\toverride readonly name = 'ProcessError'\n\treadonly code: ProcessErrorCode\n\treadonly context?: ProcessErrorContext\n\t/** The buffered run outcome, present when a one-shot run produced the failure. */\n\treadonly result?: ExecuteResult\n\n\t/**\n\t * Create a process error.\n\t *\n\t * @param message - Human-readable failure description\n\t * @param options - Machine-readable category, optional context, optional cause, and optional run result\n\t */\n\tconstructor(message: string, options: ProcessErrorOptions) {\n\t\tconst cause = options.cause\n\t\tsuper(message, cause === undefined ? undefined : { cause })\n\t\tObject.defineProperty(this, Symbol.for('@orkestrel/process.error'), { value: true })\n\t\tthis.code = options.code\n\t\tif (options.context !== undefined) this.context = options.context\n\t\tif (options.result !== undefined) this.result = options.result\n\t}\n}\n\n/**\n * Checks whether an unknown value is a {@link ProcessError}.\n *\n * @remarks\n * Recognition combines a global own-property brand with the native `Error` base, the subclass\n * prototype, the fixed name, and a code {@link PROCESS_ERROR_CODES} declares. The brand is\n * recognized across duplicate installations and ESM/CommonJS module copies at 0.0.4 or later. A copy\n * earlier than 0.0.4 stamps no brand, so an error it throws stays outside the type, and so does a\n * plain or property-only lookalike.\n *\n * @param value - The value to inspect\n * @returns True only for a `ProcessError` instance; false otherwise\n *\n * @example\n * ```ts\n * isProcessError(new ProcessError('spawn failed', { code: 'spawn' })) // true\n * isProcessError(new Error('spawn failed')) // false\n * ```\n */\nexport function isProcessError(value: unknown): value is ProcessError {\n\tif (!isError(value)) return false\n\treturn holds(() => {\n\t\tif (Object.getPrototypeOf(value) === Error.prototype) return false\n\t\tif (value.name !== 'ProcessError' || !('code' in value)) return false\n\t\tconst descriptor = Object.getOwnPropertyDescriptor(\n\t\t\tvalue,\n\t\t\tSymbol.for('@orkestrel/process.error'),\n\t\t)\n\t\tif (descriptor?.value !== true) return false\n\t\tconst code: unknown = value.code\n\t\treturn PROCESS_ERROR_CODES.some((declared) => declared === code)\n\t})\n}\n\n/**\n * Creates the failure raised when a manager launch reuses a live id.\n *\n * @param id - The id already occupied by a live child\n * @returns A typed duplicate-id failure\n */\nexport function createDuplicateError(id: string): ProcessError {\n\treturn new ProcessError(`Process '${id}' is already live`, {\n\t\tcode: 'duplicate',\n\t\tcontext: { id },\n\t})\n}\n\n/**\n * Creates the failure raised when a launch is attempted on a registry that is being destroyed.\n *\n * @param id - The id the refused launch asked for\n * @returns A typed protocol failure\n */\nexport function createProtocolError(id: string): ProcessError {\n\treturn new ProcessError(`Process '${id}' cannot launch: the registry is destroyed`, {\n\t\tcode: 'protocol',\n\t\tcontext: { id },\n\t})\n}\n\n/**\n * Creates the failure raised when a public input is refused before anything is spawned.\n *\n * @param subject - The rejected input named as the caller wrote it, such as `option 'grace'`\n * @param value - The rejected value, carried as `context.value`\n * @returns A typed validation failure\n *\n * @example\n * ```ts\n * createInvalidError(\"option 'grace'\", -1).code // 'invalid'\n * ```\n */\nexport function createInvalidError(subject: string, value: unknown): ProcessError {\n\treturn new ProcessError(`Invalid ${subject}`, { code: 'invalid', context: { value } })\n}\n\n/**\n * Creates the failure raised when a run does not complete successfully and rejection is requested.\n *\n * @remarks\n * The category is `timeout` only when the run's own timeout elapsed; every other failure,\n * including an abort and an output overflow, is a `spawn` failure carrying its\n * {@link ExecuteResult}.\n *\n * @param result - The buffered run outcome that failed\n * @param cause - The underlying host fault, when one ended the run\n * @returns A typed run failure carrying its {@link ExecuteResult}\n */\nexport function createExecuteError(result: ExecuteResult, cause?: unknown): ProcessError {\n\tlet reason = `exited with code ${String(result.code)}`\n\tif (result.signal !== null) reason = `was killed by ${result.signal}`\n\tif (result.truncated) reason = 'exceeded its output limit'\n\tif (result.aborted) reason = 'was aborted'\n\tif (result.expired) reason = 'timed out'\n\treturn new ProcessError(`Command '${result.command}' ${reason}`, {\n\t\tcode: result.expired ? 'timeout' : 'spawn',\n\t\tcontext: { command: result.command, code: result.code, signal: result.signal },\n\t\t...(cause === undefined ? {} : { cause }),\n\t\tresult,\n\t})\n}\n"],"mappings":";;;;AACA,IAAa,gBAAgB;;AAG7B,IAAa,uBAAuB;;;;;;;;;;;;;;;;AAiBpC,IAAa,gBAAgB;;AAG7B,IAAa,mBAAmB;;AAGhC,IAAa,kBAAkB;;AAG/B,IAAa,iBAAiB;;AAG9B,IAAa,gBAAgB;;AAG7B,IAAa,kBAAkB;;AAG/B,IAAa,sBAAsB,OAAO,OAAO;CAChD;CACA;CACA;CACA;CACA;CACA;AACD,CAAU;;;;ACpCV,IAAa,eAAb,cAAkC,MAAM;CACvC,OAAyB;CACzB;CACA;;CAEA;;;;;;;CAQA,YAAY,SAAiB,SAA8B;EAC1D,MAAM,QAAQ,QAAQ;EACtB,MAAM,SAAS,UAAU,KAAA,IAAY,KAAA,IAAY,EAAE,MAAM,CAAC;EAC1D,OAAO,eAAe,MAAM,OAAO,IAAI,0BAA0B,GAAG,EAAE,OAAO,KAAK,CAAC;EACnF,KAAK,OAAO,QAAQ;EACpB,IAAI,QAAQ,YAAY,KAAA,GAAW,KAAK,UAAU,QAAQ;EAC1D,IAAI,QAAQ,WAAW,KAAA,GAAW,KAAK,SAAS,QAAQ;CACzD;AACD;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,eAAe,OAAuC;CACrE,IAAI,EAAA,GAAC,oBAAA,QAAA,CAAQ,KAAK,GAAG,OAAO;CAC5B,QAAA,GAAO,oBAAA,MAAA,OAAY;EAClB,IAAI,OAAO,eAAe,KAAK,MAAM,MAAM,WAAW,OAAO;EAC7D,IAAI,MAAM,SAAS,kBAAkB,EAAE,UAAU,QAAQ,OAAO;EAKhE,IAJmB,OAAO,yBACzB,OACA,OAAO,IAAI,0BAA0B,CAElC,CAAA,EAAY,UAAU,MAAM,OAAO;EACvC,MAAM,OAAgB,MAAM;EAC5B,OAAO,oBAAoB,MAAM,aAAa,aAAa,IAAI;CAChE,CAAC;AACF;;;;;;;AAQA,SAAgB,qBAAqB,IAA0B;CAC9D,OAAO,IAAI,aAAa,YAAY,GAAG,oBAAoB;EAC1D,MAAM;EACN,SAAS,EAAE,GAAG;CACf,CAAC;AACF;;;;;;;AAQA,SAAgB,oBAAoB,IAA0B;CAC7D,OAAO,IAAI,aAAa,YAAY,GAAG,6CAA6C;EACnF,MAAM;EACN,SAAS,EAAE,GAAG;CACf,CAAC;AACF;;;;;;;;;;;;;AAcA,SAAgB,mBAAmB,SAAiB,OAA8B;CACjF,OAAO,IAAI,aAAa,WAAW,WAAW;EAAE,MAAM;EAAW,SAAS,EAAE,MAAM;CAAE,CAAC;AACtF;;;;;;;;;;;;;AAcA,SAAgB,mBAAmB,QAAuB,OAA+B;CACxF,IAAI,SAAS,oBAAoB,OAAO,OAAO,IAAI;CACnD,IAAI,OAAO,WAAW,MAAM,SAAS,iBAAiB,OAAO;CAC7D,IAAI,OAAO,WAAW,SAAS;CAC/B,IAAI,OAAO,SAAS,SAAS;CAC7B,IAAI,OAAO,SAAS,SAAS;CAC7B,OAAO,IAAI,aAAa,YAAY,OAAO,QAAQ,IAAI,UAAU;EAChE,MAAM,OAAO,UAAU,YAAY;EACnC,SAAS;GAAE,SAAS,OAAO;GAAS,MAAM,OAAO;GAAM,QAAQ,OAAO;EAAO;EAC7E,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;EACvC;CACD,CAAC;AACF"}
|
|
@@ -222,6 +222,23 @@ export declare const PROCESS_BACKLOG = 10485760;
|
|
|
222
222
|
/** The window in milliseconds a termination waits for the child's native exit after the final kill. */
|
|
223
223
|
export declare const PROCESS_CONFIRMATION = 5000;
|
|
224
224
|
|
|
225
|
+
/**
|
|
226
|
+
* The default window in milliseconds the package waits for the child's read ends to close after
|
|
227
|
+
* the child's native exit or after a termination this package initiated, before cutting them off.
|
|
228
|
+
*
|
|
229
|
+
* @remarks
|
|
230
|
+
* Measured on Windows 11 with Node v24.18.1 on 2026-08-21, spawning the `tests/src/server/fixtures`
|
|
231
|
+
* child modes as real children and timing the host `close` event against the host `exit` event.
|
|
232
|
+
* Every ordinary run closed within 0.02ms of the native exit, whether the child ended itself or a
|
|
233
|
+
* kill ended it, and a `taskkill /F /T` that reaped a descendant while the root was still alive
|
|
234
|
+
* closed within 0.01ms. The only finite late close came from a descendant that outlived its root and
|
|
235
|
+
* then ended on its own, at 127.44ms to 193.41ms — that lag is the descendant's own remaining life
|
|
236
|
+
* rather than a flush cost. A descendant that never ends never closes, so the distribution has no
|
|
237
|
+
* finite tail to cover and this value bounds it instead: 1000ms is over five times the slowest
|
|
238
|
+
* finite close measured, and it keeps an unreachable descendant from stretching a shutdown.
|
|
239
|
+
*/
|
|
240
|
+
export declare const PROCESS_DRAIN = 1000;
|
|
241
|
+
|
|
225
242
|
/** The machine-readable failure categories a {@link ProcessError} carries, in declaration order. */
|
|
226
243
|
export declare const PROCESS_ERROR_CODES: readonly ["spawn", "timeout", "input", "duplicate", "protocol", "invalid"];
|
|
227
244
|
|
|
@@ -349,12 +366,24 @@ export declare type ProcessEventMap = {
|
|
|
349
366
|
readonly exit: readonly [exit: ProcessExit];
|
|
350
367
|
};
|
|
351
368
|
|
|
352
|
-
/**
|
|
369
|
+
/**
|
|
370
|
+
* The observed terminal state of a child process: its exit code or the signal that ended it, and
|
|
371
|
+
* how its observation ended.
|
|
372
|
+
*
|
|
373
|
+
* @remarks
|
|
374
|
+
* This value exists only at the terminal moment, so every field on it is already final and no
|
|
375
|
+
* consumer can read one too early. `drained` reports whether the diagnostics are complete;
|
|
376
|
+
* {@link ProcessInterface.truncated} reports that the `lines` stream omitted stdout lines. They are
|
|
377
|
+
* independent facts about different streams, and one child reports both when a retention bound
|
|
378
|
+
* dropped lines and the drain bound cut stderr off.
|
|
379
|
+
*/
|
|
353
380
|
export declare interface ProcessExit {
|
|
354
381
|
/** The exit code, or `null` when a signal ended the process. A spawn fault reports the host's negative errno for `Process` and `execute`. */
|
|
355
382
|
readonly code: number | null;
|
|
356
383
|
/** The terminating signal name, or `null` when the process exited on its own. */
|
|
357
384
|
readonly signal: string | null;
|
|
385
|
+
/** True when the terminal moment arrived because the child's streams closed; false when the `drain` bound elapsed first and later diagnostics may have existed. */
|
|
386
|
+
readonly drained: boolean;
|
|
358
387
|
}
|
|
359
388
|
|
|
360
389
|
/**
|
|
@@ -375,14 +404,22 @@ export declare interface ProcessExit {
|
|
|
375
404
|
* its own capture `limit`. The typed `emitter` carries the live `stderr` chunks, the child `error` cause, and the
|
|
376
405
|
* terminal `exit`, alongside the `exit` promise. `stop` and `destroy` are idempotent and never reject.
|
|
377
406
|
*
|
|
407
|
+
* The same terminal moment governs every observation surface. It arrives when the child's streams close,
|
|
408
|
+
* or when the `drain` bound armed by the native exit or by a termination this package initiated
|
|
409
|
+
* elapses first, and
|
|
410
|
+
* {@link ProcessExit.drained} reports which. At that moment `evidence` freezes, `lines` ends, `exit`
|
|
411
|
+
* settles, and `settled` turns true — together, so a consumer reading `evidence` and a consumer
|
|
412
|
+
* iterating `lines` never see a different child. `stop`, `destroy`, an abort of the `signal` option,
|
|
413
|
+
* a natural exit, and a spawn fault all reach it.
|
|
414
|
+
*
|
|
378
415
|
* The spawn is eager, so `pid` is fixed by the time construction returns; a spawn that produced no
|
|
379
416
|
* child reports `undefined` for that child's whole lifetime. An assigned id survives the exit and
|
|
380
417
|
* reports no liveness on its own, because the host reuses a dead child's id. Derive liveness as
|
|
381
418
|
* `pid !== undefined && code === null && signal === null`, and derive it again before every use of
|
|
382
419
|
* the id. `code` and `signal` mirror the host child's own terminal record, so they carry the native
|
|
383
|
-
* exit as soon as the host has it, while the `exit` promise settles
|
|
384
|
-
* holding inherited stdio keeps
|
|
385
|
-
* that window reads the terminal state here.
|
|
420
|
+
* exit as soon as the host has it, while the `exit` promise settles at the terminal moment — a
|
|
421
|
+
* descendant holding inherited stdio keeps the stream close pending past the native exit, and a
|
|
422
|
+
* supervisor inside that window reads the terminal state here.
|
|
386
423
|
*/
|
|
387
424
|
export declare interface ProcessInterface {
|
|
388
425
|
/** The host process id, fixed when construction returns, or `undefined` when the spawn produced none. */
|
|
@@ -394,7 +431,7 @@ export declare interface ProcessInterface {
|
|
|
394
431
|
/** The typed lifecycle observation surface. */
|
|
395
432
|
readonly emitter: EmitterInterface<ProcessEventMap>;
|
|
396
433
|
/**
|
|
397
|
-
* The captured stdout lines, in arrival order, for one consumer, ending
|
|
434
|
+
* The captured stdout lines, in arrival order, for one consumer, ending at the terminal moment.
|
|
398
435
|
*
|
|
399
436
|
* @remarks
|
|
400
437
|
* A line feed, a CRLF pair, and a bare carriage return each terminate a line, and a CRLF split
|
|
@@ -402,13 +439,58 @@ export declare interface ProcessInterface {
|
|
|
402
439
|
* return therefore yields one line per redraw, and consecutive carriage returns yield an empty
|
|
403
440
|
* line between them. A final line written with no trailing terminator is delivered when stdout
|
|
404
441
|
* closes.
|
|
442
|
+
*
|
|
443
|
+
* The stream ends at the terminal moment rather than throwing there, so teardown is not an error
|
|
444
|
+
* path: a pending `next()` resolves `done: true` and a `for await` loop exits normally. Lines
|
|
445
|
+
* already framed and queued are delivered before that end, so a consumer that stops a chatty
|
|
446
|
+
* child still reads what the child had produced, and only bytes that would have arrived after the
|
|
447
|
+
* terminal moment are lost — whether that moment came from the read ends closing, from the
|
|
448
|
+
* `drain` bound after a native exit, or from a requested termination. An unframed trailing partial
|
|
449
|
+
* written before an undrained cutoff is not promised, because only the stream's own end flushes
|
|
450
|
+
* one. A read started after the end yields nothing further.
|
|
405
451
|
*/
|
|
406
452
|
readonly lines: AsyncIterable<string>;
|
|
407
|
-
/**
|
|
453
|
+
/**
|
|
454
|
+
* The decoded byte-bounded stderr tail.
|
|
455
|
+
*
|
|
456
|
+
* @remarks
|
|
457
|
+
* The live tail before the terminal moment and the frozen value after it, on every path — a
|
|
458
|
+
* natural exit, `stop`, `destroy`, an abort of the `signal` option, and a spawn fault. Every read
|
|
459
|
+
* after that moment returns the same string, so a consumer needs no private copy and cannot
|
|
460
|
+
* observe the tail moving under it. When {@link ProcessExit.drained} is false the frozen value is
|
|
461
|
+
* the tail as of the cutoff, and later diagnostics may have existed.
|
|
462
|
+
*/
|
|
408
463
|
readonly evidence: string;
|
|
409
464
|
/** True when the `lines` stream omitted output because a retention bound was reached. */
|
|
410
465
|
readonly truncated: boolean;
|
|
411
|
-
/**
|
|
466
|
+
/**
|
|
467
|
+
* True after the `exit` promise settled.
|
|
468
|
+
*
|
|
469
|
+
* @remarks
|
|
470
|
+
* The terminal moment has arrived: `evidence` is frozen, `lines` has ended, and the
|
|
471
|
+
* {@link ProcessExit} value exists. Reached on every path, including a spawn that produced no
|
|
472
|
+
* child.
|
|
473
|
+
*/
|
|
474
|
+
readonly settled: boolean;
|
|
475
|
+
/**
|
|
476
|
+
* True after a termination began.
|
|
477
|
+
*
|
|
478
|
+
* @remarks
|
|
479
|
+
* Monotonic. It turns true when `stop`, `destroy`, or an abort of the `signal` option begins a
|
|
480
|
+
* termination, and it stays true from then on, including after `settled` turns true. It reports
|
|
481
|
+
* that a termination was initiated, not that one is in flight, because the initiation is the fact
|
|
482
|
+
* a consumer acts on: a child that was asked to end is not a child to send new work to. A child
|
|
483
|
+
* that exited on its own reports `false` here with `settled` true.
|
|
484
|
+
*/
|
|
485
|
+
readonly stopping: boolean;
|
|
486
|
+
/**
|
|
487
|
+
* The terminal child state, delivered once.
|
|
488
|
+
*
|
|
489
|
+
* @remarks
|
|
490
|
+
* Never rejects. It settles at the terminal moment: when the child's streams close, or when the
|
|
491
|
+
* `drain` bound elapsed first. The native exit arms that bound as well as an initiated
|
|
492
|
+
* termination, so a child nobody terminates still settles it within `drain` of ending.
|
|
493
|
+
*/
|
|
412
494
|
readonly exit: Promise<ProcessExit>;
|
|
413
495
|
/**
|
|
414
496
|
* Write one line to the open standard-input channel.
|
|
@@ -429,7 +511,7 @@ export declare interface ProcessInterface {
|
|
|
429
511
|
*/
|
|
430
512
|
send(text: string): Promise<boolean>;
|
|
431
513
|
/**
|
|
432
|
-
* Terminate the child process tree
|
|
514
|
+
* Terminate the child process tree, await its observed exit, and reach the terminal moment.
|
|
433
515
|
*
|
|
434
516
|
* @remarks
|
|
435
517
|
* Never rejects. On Windows the whole tree is killed immediately through `taskkill`, with a
|
|
@@ -437,16 +519,25 @@ export declare interface ProcessInterface {
|
|
|
437
519
|
* `SIGKILL` after `grace`. No signal is initiated after the child's native exit is observed; the
|
|
438
520
|
* window between initiating a signal and the host delivering it belongs to the operating system.
|
|
439
521
|
*
|
|
522
|
+
* Observation ends here as well as under `destroy`, bounded by `drain`: `evidence` freezes,
|
|
523
|
+
* `lines` ends, and `exit` settles. A caller that stops a child and keeps reading it therefore
|
|
524
|
+
* reaches the end of the stream instead of waiting on a child it already ended, and needs no
|
|
525
|
+
* second call to release it.
|
|
526
|
+
*
|
|
440
527
|
* @returns True when the child's native exit was observed; false when the confirmation deadline elapsed without it
|
|
441
528
|
*/
|
|
442
529
|
stop(): Promise<boolean>;
|
|
443
530
|
/**
|
|
444
|
-
* Stop the child, close its standard-input channel, and destroy the
|
|
531
|
+
* Stop the child, close its standard-input channel, reach the terminal moment, and destroy the
|
|
532
|
+
* observation emitter.
|
|
445
533
|
*
|
|
446
534
|
* @remarks
|
|
447
|
-
* Always resolves, including when termination was never confirmed.
|
|
448
|
-
*
|
|
449
|
-
*
|
|
535
|
+
* Always resolves, including when termination was never confirmed. The barrier settles after the
|
|
536
|
+
* terminal moment, so `evidence` is frozen, `lines` has ended, and `exit` has settled by the time
|
|
537
|
+
* a caller resumes. The wait for the child's streams is bounded by `drain`, so a descendant
|
|
538
|
+
* holding an inherited pipe cannot hold this barrier open, and {@link ProcessExit.drained} reports
|
|
539
|
+
* which way the moment arrived. The emitter is destroyed after the frozen state exists, so a
|
|
540
|
+
* consumer watching the `stderr` event and a consumer reading `evidence` end on the same bytes.
|
|
450
541
|
*
|
|
451
542
|
* @returns The stable barrier shared by every call
|
|
452
543
|
*/
|
|
@@ -503,11 +594,11 @@ export declare interface ProcessManagerInterface {
|
|
|
503
594
|
* @remarks
|
|
504
595
|
* Every option is read before the child is spawned, so a caller's own option getter runs while
|
|
505
596
|
* nothing has started and a throw from one strands no process. A getter that begins `destroy`
|
|
506
|
-
* without throwing is the one remaining race
|
|
507
|
-
*
|
|
508
|
-
*
|
|
509
|
-
*
|
|
510
|
-
*
|
|
597
|
+
* without throwing is the one remaining race: the child is already spawned, so the launch is
|
|
598
|
+
* refused with `protocol` and that child is destroyed rather than adopted, its teardown bounded
|
|
599
|
+
* by `grace` plus the confirmation window. The `protocol` refusal throws synchronously, and the
|
|
600
|
+
* `destroy` barrier covers that teardown, so the refused child reaches its terminal moment before
|
|
601
|
+
* the barrier resolves.
|
|
511
602
|
*
|
|
512
603
|
* @param id - The registry key, unique among live children
|
|
513
604
|
* @param options - The child construction options
|
|
@@ -566,8 +657,17 @@ export declare interface ProcessManagerOptions {
|
|
|
566
657
|
*
|
|
567
658
|
* @remarks
|
|
568
659
|
* `grace` is the cooperative window between `SIGTERM` and `SIGKILL` on a POSIX host; Windows has no
|
|
569
|
-
* cooperative phase, so the value is unused there. There is no completion deadline
|
|
570
|
-
*
|
|
660
|
+
* cooperative phase, so the value is unused there. There is no completion deadline for a running
|
|
661
|
+
* child: nothing here ends a child that is still working, the `exit` promise carries no deadline of
|
|
662
|
+
* its own, and a caller that wants one arms its own timer and calls `stop`. `drain` does not weaken
|
|
663
|
+
* that. It bounds a single window — between the child's ending and the release of that child's read
|
|
664
|
+
* ends — so it cannot end a running child. The child's native exit arms it, and so does the return
|
|
665
|
+
* of a termination this package initiated through `stop`, `destroy`, or an abort of `signal`; a
|
|
666
|
+
* termination whose confirmation window elapsed can therefore reach the cutoff while the child still
|
|
667
|
+
* runs. The bound exists because a descendant holding an inherited pipe defers the host's stream
|
|
668
|
+
* close for that descendant's whole life, which the package cannot reach and cannot outwait: without
|
|
669
|
+
* the bound, a child that ended would hand the caller an unbounded wait.
|
|
670
|
+
* `backlog` bounds the unconsumed `lines` backlog.
|
|
571
671
|
* During termination, retained lines are capped at twice `backlog`; later lines are dropped without
|
|
572
672
|
* pausing stdout, and {@link ProcessInterface.truncated} reports the omission. `on` installs initial
|
|
573
673
|
* {@link ProcessEventMap} listeners and `error` receives isolated listener failures. Every numeric
|
|
@@ -585,6 +685,8 @@ export declare interface ProcessOptions {
|
|
|
585
685
|
readonly workspace: string;
|
|
586
686
|
/** Cooperative POSIX window in milliseconds between `SIGTERM` and `SIGKILL`. Default: {@link PROCESS_GRACE}. */
|
|
587
687
|
readonly grace?: number;
|
|
688
|
+
/** Milliseconds the package waits for the child's read ends to close after the native exit or an initiated termination, before cutting them off; `0` cuts them off as soon as the bound is armed. Default: {@link PROCESS_DRAIN}. */
|
|
689
|
+
readonly drain?: number;
|
|
588
690
|
/** Maximum retained stderr tail in bytes. Default: {@link PROCESS_EVIDENCE}. */
|
|
589
691
|
readonly evidence?: number;
|
|
590
692
|
/** Soft high-water mark in bytes for the unconsumed `lines` backlog; termination retains at most twice this value. Default: {@link PROCESS_BACKLOG}. */
|
package/dist/src/core/index.d.ts
CHANGED
|
@@ -222,6 +222,23 @@ export declare const PROCESS_BACKLOG = 10485760;
|
|
|
222
222
|
/** The window in milliseconds a termination waits for the child's native exit after the final kill. */
|
|
223
223
|
export declare const PROCESS_CONFIRMATION = 5000;
|
|
224
224
|
|
|
225
|
+
/**
|
|
226
|
+
* The default window in milliseconds the package waits for the child's read ends to close after
|
|
227
|
+
* the child's native exit or after a termination this package initiated, before cutting them off.
|
|
228
|
+
*
|
|
229
|
+
* @remarks
|
|
230
|
+
* Measured on Windows 11 with Node v24.18.1 on 2026-08-21, spawning the `tests/src/server/fixtures`
|
|
231
|
+
* child modes as real children and timing the host `close` event against the host `exit` event.
|
|
232
|
+
* Every ordinary run closed within 0.02ms of the native exit, whether the child ended itself or a
|
|
233
|
+
* kill ended it, and a `taskkill /F /T` that reaped a descendant while the root was still alive
|
|
234
|
+
* closed within 0.01ms. The only finite late close came from a descendant that outlived its root and
|
|
235
|
+
* then ended on its own, at 127.44ms to 193.41ms — that lag is the descendant's own remaining life
|
|
236
|
+
* rather than a flush cost. A descendant that never ends never closes, so the distribution has no
|
|
237
|
+
* finite tail to cover and this value bounds it instead: 1000ms is over five times the slowest
|
|
238
|
+
* finite close measured, and it keeps an unreachable descendant from stretching a shutdown.
|
|
239
|
+
*/
|
|
240
|
+
export declare const PROCESS_DRAIN = 1000;
|
|
241
|
+
|
|
225
242
|
/** The machine-readable failure categories a {@link ProcessError} carries, in declaration order. */
|
|
226
243
|
export declare const PROCESS_ERROR_CODES: readonly ["spawn", "timeout", "input", "duplicate", "protocol", "invalid"];
|
|
227
244
|
|
|
@@ -349,12 +366,24 @@ export declare type ProcessEventMap = {
|
|
|
349
366
|
readonly exit: readonly [exit: ProcessExit];
|
|
350
367
|
};
|
|
351
368
|
|
|
352
|
-
/**
|
|
369
|
+
/**
|
|
370
|
+
* The observed terminal state of a child process: its exit code or the signal that ended it, and
|
|
371
|
+
* how its observation ended.
|
|
372
|
+
*
|
|
373
|
+
* @remarks
|
|
374
|
+
* This value exists only at the terminal moment, so every field on it is already final and no
|
|
375
|
+
* consumer can read one too early. `drained` reports whether the diagnostics are complete;
|
|
376
|
+
* {@link ProcessInterface.truncated} reports that the `lines` stream omitted stdout lines. They are
|
|
377
|
+
* independent facts about different streams, and one child reports both when a retention bound
|
|
378
|
+
* dropped lines and the drain bound cut stderr off.
|
|
379
|
+
*/
|
|
353
380
|
export declare interface ProcessExit {
|
|
354
381
|
/** The exit code, or `null` when a signal ended the process. A spawn fault reports the host's negative errno for `Process` and `execute`. */
|
|
355
382
|
readonly code: number | null;
|
|
356
383
|
/** The terminating signal name, or `null` when the process exited on its own. */
|
|
357
384
|
readonly signal: string | null;
|
|
385
|
+
/** True when the terminal moment arrived because the child's streams closed; false when the `drain` bound elapsed first and later diagnostics may have existed. */
|
|
386
|
+
readonly drained: boolean;
|
|
358
387
|
}
|
|
359
388
|
|
|
360
389
|
/**
|
|
@@ -375,14 +404,22 @@ export declare interface ProcessExit {
|
|
|
375
404
|
* its own capture `limit`. The typed `emitter` carries the live `stderr` chunks, the child `error` cause, and the
|
|
376
405
|
* terminal `exit`, alongside the `exit` promise. `stop` and `destroy` are idempotent and never reject.
|
|
377
406
|
*
|
|
407
|
+
* The same terminal moment governs every observation surface. It arrives when the child's streams close,
|
|
408
|
+
* or when the `drain` bound armed by the native exit or by a termination this package initiated
|
|
409
|
+
* elapses first, and
|
|
410
|
+
* {@link ProcessExit.drained} reports which. At that moment `evidence` freezes, `lines` ends, `exit`
|
|
411
|
+
* settles, and `settled` turns true — together, so a consumer reading `evidence` and a consumer
|
|
412
|
+
* iterating `lines` never see a different child. `stop`, `destroy`, an abort of the `signal` option,
|
|
413
|
+
* a natural exit, and a spawn fault all reach it.
|
|
414
|
+
*
|
|
378
415
|
* The spawn is eager, so `pid` is fixed by the time construction returns; a spawn that produced no
|
|
379
416
|
* child reports `undefined` for that child's whole lifetime. An assigned id survives the exit and
|
|
380
417
|
* reports no liveness on its own, because the host reuses a dead child's id. Derive liveness as
|
|
381
418
|
* `pid !== undefined && code === null && signal === null`, and derive it again before every use of
|
|
382
419
|
* the id. `code` and `signal` mirror the host child's own terminal record, so they carry the native
|
|
383
|
-
* exit as soon as the host has it, while the `exit` promise settles
|
|
384
|
-
* holding inherited stdio keeps
|
|
385
|
-
* that window reads the terminal state here.
|
|
420
|
+
* exit as soon as the host has it, while the `exit` promise settles at the terminal moment — a
|
|
421
|
+
* descendant holding inherited stdio keeps the stream close pending past the native exit, and a
|
|
422
|
+
* supervisor inside that window reads the terminal state here.
|
|
386
423
|
*/
|
|
387
424
|
export declare interface ProcessInterface {
|
|
388
425
|
/** The host process id, fixed when construction returns, or `undefined` when the spawn produced none. */
|
|
@@ -394,7 +431,7 @@ export declare interface ProcessInterface {
|
|
|
394
431
|
/** The typed lifecycle observation surface. */
|
|
395
432
|
readonly emitter: EmitterInterface<ProcessEventMap>;
|
|
396
433
|
/**
|
|
397
|
-
* The captured stdout lines, in arrival order, for one consumer, ending
|
|
434
|
+
* The captured stdout lines, in arrival order, for one consumer, ending at the terminal moment.
|
|
398
435
|
*
|
|
399
436
|
* @remarks
|
|
400
437
|
* A line feed, a CRLF pair, and a bare carriage return each terminate a line, and a CRLF split
|
|
@@ -402,13 +439,58 @@ export declare interface ProcessInterface {
|
|
|
402
439
|
* return therefore yields one line per redraw, and consecutive carriage returns yield an empty
|
|
403
440
|
* line between them. A final line written with no trailing terminator is delivered when stdout
|
|
404
441
|
* closes.
|
|
442
|
+
*
|
|
443
|
+
* The stream ends at the terminal moment rather than throwing there, so teardown is not an error
|
|
444
|
+
* path: a pending `next()` resolves `done: true` and a `for await` loop exits normally. Lines
|
|
445
|
+
* already framed and queued are delivered before that end, so a consumer that stops a chatty
|
|
446
|
+
* child still reads what the child had produced, and only bytes that would have arrived after the
|
|
447
|
+
* terminal moment are lost — whether that moment came from the read ends closing, from the
|
|
448
|
+
* `drain` bound after a native exit, or from a requested termination. An unframed trailing partial
|
|
449
|
+
* written before an undrained cutoff is not promised, because only the stream's own end flushes
|
|
450
|
+
* one. A read started after the end yields nothing further.
|
|
405
451
|
*/
|
|
406
452
|
readonly lines: AsyncIterable<string>;
|
|
407
|
-
/**
|
|
453
|
+
/**
|
|
454
|
+
* The decoded byte-bounded stderr tail.
|
|
455
|
+
*
|
|
456
|
+
* @remarks
|
|
457
|
+
* The live tail before the terminal moment and the frozen value after it, on every path — a
|
|
458
|
+
* natural exit, `stop`, `destroy`, an abort of the `signal` option, and a spawn fault. Every read
|
|
459
|
+
* after that moment returns the same string, so a consumer needs no private copy and cannot
|
|
460
|
+
* observe the tail moving under it. When {@link ProcessExit.drained} is false the frozen value is
|
|
461
|
+
* the tail as of the cutoff, and later diagnostics may have existed.
|
|
462
|
+
*/
|
|
408
463
|
readonly evidence: string;
|
|
409
464
|
/** True when the `lines` stream omitted output because a retention bound was reached. */
|
|
410
465
|
readonly truncated: boolean;
|
|
411
|
-
/**
|
|
466
|
+
/**
|
|
467
|
+
* True after the `exit` promise settled.
|
|
468
|
+
*
|
|
469
|
+
* @remarks
|
|
470
|
+
* The terminal moment has arrived: `evidence` is frozen, `lines` has ended, and the
|
|
471
|
+
* {@link ProcessExit} value exists. Reached on every path, including a spawn that produced no
|
|
472
|
+
* child.
|
|
473
|
+
*/
|
|
474
|
+
readonly settled: boolean;
|
|
475
|
+
/**
|
|
476
|
+
* True after a termination began.
|
|
477
|
+
*
|
|
478
|
+
* @remarks
|
|
479
|
+
* Monotonic. It turns true when `stop`, `destroy`, or an abort of the `signal` option begins a
|
|
480
|
+
* termination, and it stays true from then on, including after `settled` turns true. It reports
|
|
481
|
+
* that a termination was initiated, not that one is in flight, because the initiation is the fact
|
|
482
|
+
* a consumer acts on: a child that was asked to end is not a child to send new work to. A child
|
|
483
|
+
* that exited on its own reports `false` here with `settled` true.
|
|
484
|
+
*/
|
|
485
|
+
readonly stopping: boolean;
|
|
486
|
+
/**
|
|
487
|
+
* The terminal child state, delivered once.
|
|
488
|
+
*
|
|
489
|
+
* @remarks
|
|
490
|
+
* Never rejects. It settles at the terminal moment: when the child's streams close, or when the
|
|
491
|
+
* `drain` bound elapsed first. The native exit arms that bound as well as an initiated
|
|
492
|
+
* termination, so a child nobody terminates still settles it within `drain` of ending.
|
|
493
|
+
*/
|
|
412
494
|
readonly exit: Promise<ProcessExit>;
|
|
413
495
|
/**
|
|
414
496
|
* Write one line to the open standard-input channel.
|
|
@@ -429,7 +511,7 @@ export declare interface ProcessInterface {
|
|
|
429
511
|
*/
|
|
430
512
|
send(text: string): Promise<boolean>;
|
|
431
513
|
/**
|
|
432
|
-
* Terminate the child process tree
|
|
514
|
+
* Terminate the child process tree, await its observed exit, and reach the terminal moment.
|
|
433
515
|
*
|
|
434
516
|
* @remarks
|
|
435
517
|
* Never rejects. On Windows the whole tree is killed immediately through `taskkill`, with a
|
|
@@ -437,16 +519,25 @@ export declare interface ProcessInterface {
|
|
|
437
519
|
* `SIGKILL` after `grace`. No signal is initiated after the child's native exit is observed; the
|
|
438
520
|
* window between initiating a signal and the host delivering it belongs to the operating system.
|
|
439
521
|
*
|
|
522
|
+
* Observation ends here as well as under `destroy`, bounded by `drain`: `evidence` freezes,
|
|
523
|
+
* `lines` ends, and `exit` settles. A caller that stops a child and keeps reading it therefore
|
|
524
|
+
* reaches the end of the stream instead of waiting on a child it already ended, and needs no
|
|
525
|
+
* second call to release it.
|
|
526
|
+
*
|
|
440
527
|
* @returns True when the child's native exit was observed; false when the confirmation deadline elapsed without it
|
|
441
528
|
*/
|
|
442
529
|
stop(): Promise<boolean>;
|
|
443
530
|
/**
|
|
444
|
-
* Stop the child, close its standard-input channel, and destroy the
|
|
531
|
+
* Stop the child, close its standard-input channel, reach the terminal moment, and destroy the
|
|
532
|
+
* observation emitter.
|
|
445
533
|
*
|
|
446
534
|
* @remarks
|
|
447
|
-
* Always resolves, including when termination was never confirmed.
|
|
448
|
-
*
|
|
449
|
-
*
|
|
535
|
+
* Always resolves, including when termination was never confirmed. The barrier settles after the
|
|
536
|
+
* terminal moment, so `evidence` is frozen, `lines` has ended, and `exit` has settled by the time
|
|
537
|
+
* a caller resumes. The wait for the child's streams is bounded by `drain`, so a descendant
|
|
538
|
+
* holding an inherited pipe cannot hold this barrier open, and {@link ProcessExit.drained} reports
|
|
539
|
+
* which way the moment arrived. The emitter is destroyed after the frozen state exists, so a
|
|
540
|
+
* consumer watching the `stderr` event and a consumer reading `evidence` end on the same bytes.
|
|
450
541
|
*
|
|
451
542
|
* @returns The stable barrier shared by every call
|
|
452
543
|
*/
|
|
@@ -503,11 +594,11 @@ export declare interface ProcessManagerInterface {
|
|
|
503
594
|
* @remarks
|
|
504
595
|
* Every option is read before the child is spawned, so a caller's own option getter runs while
|
|
505
596
|
* nothing has started and a throw from one strands no process. A getter that begins `destroy`
|
|
506
|
-
* without throwing is the one remaining race
|
|
507
|
-
*
|
|
508
|
-
*
|
|
509
|
-
*
|
|
510
|
-
*
|
|
597
|
+
* without throwing is the one remaining race: the child is already spawned, so the launch is
|
|
598
|
+
* refused with `protocol` and that child is destroyed rather than adopted, its teardown bounded
|
|
599
|
+
* by `grace` plus the confirmation window. The `protocol` refusal throws synchronously, and the
|
|
600
|
+
* `destroy` barrier covers that teardown, so the refused child reaches its terminal moment before
|
|
601
|
+
* the barrier resolves.
|
|
511
602
|
*
|
|
512
603
|
* @param id - The registry key, unique among live children
|
|
513
604
|
* @param options - The child construction options
|
|
@@ -566,8 +657,17 @@ export declare interface ProcessManagerOptions {
|
|
|
566
657
|
*
|
|
567
658
|
* @remarks
|
|
568
659
|
* `grace` is the cooperative window between `SIGTERM` and `SIGKILL` on a POSIX host; Windows has no
|
|
569
|
-
* cooperative phase, so the value is unused there. There is no completion deadline
|
|
570
|
-
*
|
|
660
|
+
* cooperative phase, so the value is unused there. There is no completion deadline for a running
|
|
661
|
+
* child: nothing here ends a child that is still working, the `exit` promise carries no deadline of
|
|
662
|
+
* its own, and a caller that wants one arms its own timer and calls `stop`. `drain` does not weaken
|
|
663
|
+
* that. It bounds a single window — between the child's ending and the release of that child's read
|
|
664
|
+
* ends — so it cannot end a running child. The child's native exit arms it, and so does the return
|
|
665
|
+
* of a termination this package initiated through `stop`, `destroy`, or an abort of `signal`; a
|
|
666
|
+
* termination whose confirmation window elapsed can therefore reach the cutoff while the child still
|
|
667
|
+
* runs. The bound exists because a descendant holding an inherited pipe defers the host's stream
|
|
668
|
+
* close for that descendant's whole life, which the package cannot reach and cannot outwait: without
|
|
669
|
+
* the bound, a child that ended would hand the caller an unbounded wait.
|
|
670
|
+
* `backlog` bounds the unconsumed `lines` backlog.
|
|
571
671
|
* During termination, retained lines are capped at twice `backlog`; later lines are dropped without
|
|
572
672
|
* pausing stdout, and {@link ProcessInterface.truncated} reports the omission. `on` installs initial
|
|
573
673
|
* {@link ProcessEventMap} listeners and `error` receives isolated listener failures. Every numeric
|
|
@@ -585,6 +685,8 @@ export declare interface ProcessOptions {
|
|
|
585
685
|
readonly workspace: string;
|
|
586
686
|
/** Cooperative POSIX window in milliseconds between `SIGTERM` and `SIGKILL`. Default: {@link PROCESS_GRACE}. */
|
|
587
687
|
readonly grace?: number;
|
|
688
|
+
/** Milliseconds the package waits for the child's read ends to close after the native exit or an initiated termination, before cutting them off; `0` cuts them off as soon as the bound is armed. Default: {@link PROCESS_DRAIN}. */
|
|
689
|
+
readonly drain?: number;
|
|
588
690
|
/** Maximum retained stderr tail in bytes. Default: {@link PROCESS_EVIDENCE}. */
|
|
589
691
|
readonly evidence?: number;
|
|
590
692
|
/** Soft high-water mark in bytes for the unconsumed `lines` backlog; termination retains at most twice this value. Default: {@link PROCESS_BACKLOG}. */
|
package/dist/src/core/index.js
CHANGED
|
@@ -4,6 +4,22 @@ import { holds, isError } from "@orkestrel/contract";
|
|
|
4
4
|
var PROCESS_GRACE = 5e3;
|
|
5
5
|
/** The window in milliseconds a termination waits for the child's native exit after the final kill. */
|
|
6
6
|
var PROCESS_CONFIRMATION = 5e3;
|
|
7
|
+
/**
|
|
8
|
+
* The default window in milliseconds the package waits for the child's read ends to close after
|
|
9
|
+
* the child's native exit or after a termination this package initiated, before cutting them off.
|
|
10
|
+
*
|
|
11
|
+
* @remarks
|
|
12
|
+
* Measured on Windows 11 with Node v24.18.1 on 2026-08-21, spawning the `tests/src/server/fixtures`
|
|
13
|
+
* child modes as real children and timing the host `close` event against the host `exit` event.
|
|
14
|
+
* Every ordinary run closed within 0.02ms of the native exit, whether the child ended itself or a
|
|
15
|
+
* kill ended it, and a `taskkill /F /T` that reaped a descendant while the root was still alive
|
|
16
|
+
* closed within 0.01ms. The only finite late close came from a descendant that outlived its root and
|
|
17
|
+
* then ended on its own, at 127.44ms to 193.41ms — that lag is the descendant's own remaining life
|
|
18
|
+
* rather than a flush cost. A descendant that never ends never closes, so the distribution has no
|
|
19
|
+
* finite tail to cover and this value bounds it instead: 1000ms is over five times the slowest
|
|
20
|
+
* finite close measured, and it keeps an unreachable descendant from stretching a shutdown.
|
|
21
|
+
*/
|
|
22
|
+
var PROCESS_DRAIN = 1e3;
|
|
7
23
|
/** The default maximum retained stderr tail in bytes for a supervised {@link ProcessInterface}. */
|
|
8
24
|
var PROCESS_EVIDENCE = 2048;
|
|
9
25
|
/** The default soft high-water mark in bytes for a supervised {@link ProcessInterface} line backlog. */
|
|
@@ -148,6 +164,6 @@ function createExecuteError(result, cause) {
|
|
|
148
164
|
});
|
|
149
165
|
}
|
|
150
166
|
//#endregion
|
|
151
|
-
export { PROCESS_BACKLOG, PROCESS_CONFIRMATION, PROCESS_ERROR_CODES, PROCESS_EVIDENCE, PROCESS_GRACE, PROCESS_OUTPUT, PROCESS_PATHEXT, PROCESS_TIMER, ProcessError, createDuplicateError, createExecuteError, createInvalidError, createProtocolError, isProcessError };
|
|
167
|
+
export { PROCESS_BACKLOG, PROCESS_CONFIRMATION, PROCESS_DRAIN, PROCESS_ERROR_CODES, PROCESS_EVIDENCE, PROCESS_GRACE, PROCESS_OUTPUT, PROCESS_PATHEXT, PROCESS_TIMER, ProcessError, createDuplicateError, createExecuteError, createInvalidError, createProtocolError, isProcessError };
|
|
152
168
|
|
|
153
169
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts"],"sourcesContent":["/** The default cooperative POSIX window in milliseconds between `SIGTERM` and `SIGKILL` during termination. */\nexport const PROCESS_GRACE = 5_000\n\n/** The window in milliseconds a termination waits for the child's native exit after the final kill. */\nexport const PROCESS_CONFIRMATION = 5_000\n\n/** The default maximum retained stderr tail in bytes for a supervised {@link ProcessInterface}. */\nexport const PROCESS_EVIDENCE = 2_048\n\n/** The default soft high-water mark in bytes for a supervised {@link ProcessInterface} line backlog. */\nexport const PROCESS_BACKLOG = 10_485_760\n\n/** The default maximum captured bytes for a one-shot run's stdout and stderr, each. */\nexport const PROCESS_OUTPUT = 10_485_760\n\n/** The largest timer delay in milliseconds the host schedules without truncating it to one. */\nexport const PROCESS_TIMER = 2_147_483_647\n\n/** The executable extensions a Windows lookup applies when the environment declares no `PATHEXT`. */\nexport const PROCESS_PATHEXT = '.COM;.EXE;.BAT;.CMD'\n\n/** The machine-readable failure categories a {@link ProcessError} carries, in declaration order. */\nexport const PROCESS_ERROR_CODES = Object.freeze([\n\t'spawn',\n\t'timeout',\n\t'input',\n\t'duplicate',\n\t'protocol',\n\t'invalid',\n] as const)\n","import type {\n\tProcessErrorCode,\n\tProcessErrorContext,\n\tProcessErrorOptions,\n\tExecuteResult,\n} from './types.js'\nimport { holds, isError } from '@orkestrel/contract'\nimport { PROCESS_ERROR_CODES } from './constants.js'\n\n/** A child-process failure with a stable machine-readable category. */\nexport class ProcessError extends Error {\n\toverride readonly name = 'ProcessError'\n\treadonly code: ProcessErrorCode\n\treadonly context?: ProcessErrorContext\n\t/** The buffered run outcome, present when a one-shot run produced the failure. */\n\treadonly result?: ExecuteResult\n\n\t/**\n\t * Create a process error.\n\t *\n\t * @param message - Human-readable failure description\n\t * @param options - Machine-readable category, optional context, optional cause, and optional run result\n\t */\n\tconstructor(message: string, options: ProcessErrorOptions) {\n\t\tconst cause = options.cause\n\t\tsuper(message, cause === undefined ? undefined : { cause })\n\t\tObject.defineProperty(this, Symbol.for('@orkestrel/process.error'), { value: true })\n\t\tthis.code = options.code\n\t\tif (options.context !== undefined) this.context = options.context\n\t\tif (options.result !== undefined) this.result = options.result\n\t}\n}\n\n/**\n * Checks whether an unknown value is a {@link ProcessError}.\n *\n * @remarks\n * Recognition combines a global own-property brand with the native `Error` base, the subclass\n * prototype, the fixed name, and a code {@link PROCESS_ERROR_CODES} declares. The brand is\n * recognized across duplicate installations and ESM/CommonJS module copies at 0.0.4 or later. A copy\n * earlier than 0.0.4 stamps no brand, so an error it throws stays outside the type, and so does a\n * plain or property-only lookalike.\n *\n * @param value - The value to inspect\n * @returns True only for a `ProcessError` instance; false otherwise\n *\n * @example\n * ```ts\n * isProcessError(new ProcessError('spawn failed', { code: 'spawn' })) // true\n * isProcessError(new Error('spawn failed')) // false\n * ```\n */\nexport function isProcessError(value: unknown): value is ProcessError {\n\tif (!isError(value)) return false\n\treturn holds(() => {\n\t\tif (Object.getPrototypeOf(value) === Error.prototype) return false\n\t\tif (value.name !== 'ProcessError' || !('code' in value)) return false\n\t\tconst descriptor = Object.getOwnPropertyDescriptor(\n\t\t\tvalue,\n\t\t\tSymbol.for('@orkestrel/process.error'),\n\t\t)\n\t\tif (descriptor?.value !== true) return false\n\t\tconst code: unknown = value.code\n\t\treturn PROCESS_ERROR_CODES.some((declared) => declared === code)\n\t})\n}\n\n/**\n * Creates the failure raised when a manager launch reuses a live id.\n *\n * @param id - The id already occupied by a live child\n * @returns A typed duplicate-id failure\n */\nexport function createDuplicateError(id: string): ProcessError {\n\treturn new ProcessError(`Process '${id}' is already live`, {\n\t\tcode: 'duplicate',\n\t\tcontext: { id },\n\t})\n}\n\n/**\n * Creates the failure raised when a launch is attempted on a registry that is being destroyed.\n *\n * @param id - The id the refused launch asked for\n * @returns A typed protocol failure\n */\nexport function createProtocolError(id: string): ProcessError {\n\treturn new ProcessError(`Process '${id}' cannot launch: the registry is destroyed`, {\n\t\tcode: 'protocol',\n\t\tcontext: { id },\n\t})\n}\n\n/**\n * Creates the failure raised when a public input is refused before anything is spawned.\n *\n * @param subject - The rejected input named as the caller wrote it, such as `option 'grace'`\n * @param value - The rejected value, carried as `context.value`\n * @returns A typed validation failure\n *\n * @example\n * ```ts\n * createInvalidError(\"option 'grace'\", -1).code // 'invalid'\n * ```\n */\nexport function createInvalidError(subject: string, value: unknown): ProcessError {\n\treturn new ProcessError(`Invalid ${subject}`, { code: 'invalid', context: { value } })\n}\n\n/**\n * Creates the failure raised when a run does not complete successfully and rejection is requested.\n *\n * @remarks\n * The category is `timeout` only when the run's own timeout elapsed; every other failure,\n * including an abort and an output overflow, is a `spawn` failure carrying its\n * {@link ExecuteResult}.\n *\n * @param result - The buffered run outcome that failed\n * @param cause - The underlying host fault, when one ended the run\n * @returns A typed run failure carrying its {@link ExecuteResult}\n */\nexport function createExecuteError(result: ExecuteResult, cause?: unknown): ProcessError {\n\tlet reason = `exited with code ${String(result.code)}`\n\tif (result.signal !== null) reason = `was killed by ${result.signal}`\n\tif (result.truncated) reason = 'exceeded its output limit'\n\tif (result.aborted) reason = 'was aborted'\n\tif (result.expired) reason = 'timed out'\n\treturn new ProcessError(`Command '${result.command}' ${reason}`, {\n\t\tcode: result.expired ? 'timeout' : 'spawn',\n\t\tcontext: { command: result.command, code: result.code, signal: result.signal },\n\t\t...(cause === undefined ? {} : { cause }),\n\t\tresult,\n\t})\n}\n"],"mappings":";;;AACA,IAAa,gBAAgB;;AAG7B,IAAa,uBAAuB;;
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts"],"sourcesContent":["/** The default cooperative POSIX window in milliseconds between `SIGTERM` and `SIGKILL` during termination. */\nexport const PROCESS_GRACE = 5_000\n\n/** The window in milliseconds a termination waits for the child's native exit after the final kill. */\nexport const PROCESS_CONFIRMATION = 5_000\n\n/**\n * The default window in milliseconds the package waits for the child's read ends to close after\n * the child's native exit or after a termination this package initiated, before cutting them off.\n *\n * @remarks\n * Measured on Windows 11 with Node v24.18.1 on 2026-08-21, spawning the `tests/src/server/fixtures`\n * child modes as real children and timing the host `close` event against the host `exit` event.\n * Every ordinary run closed within 0.02ms of the native exit, whether the child ended itself or a\n * kill ended it, and a `taskkill /F /T` that reaped a descendant while the root was still alive\n * closed within 0.01ms. The only finite late close came from a descendant that outlived its root and\n * then ended on its own, at 127.44ms to 193.41ms — that lag is the descendant's own remaining life\n * rather than a flush cost. A descendant that never ends never closes, so the distribution has no\n * finite tail to cover and this value bounds it instead: 1000ms is over five times the slowest\n * finite close measured, and it keeps an unreachable descendant from stretching a shutdown.\n */\nexport const PROCESS_DRAIN = 1_000\n\n/** The default maximum retained stderr tail in bytes for a supervised {@link ProcessInterface}. */\nexport const PROCESS_EVIDENCE = 2_048\n\n/** The default soft high-water mark in bytes for a supervised {@link ProcessInterface} line backlog. */\nexport const PROCESS_BACKLOG = 10_485_760\n\n/** The default maximum captured bytes for a one-shot run's stdout and stderr, each. */\nexport const PROCESS_OUTPUT = 10_485_760\n\n/** The largest timer delay in milliseconds the host schedules without truncating it to one. */\nexport const PROCESS_TIMER = 2_147_483_647\n\n/** The executable extensions a Windows lookup applies when the environment declares no `PATHEXT`. */\nexport const PROCESS_PATHEXT = '.COM;.EXE;.BAT;.CMD'\n\n/** The machine-readable failure categories a {@link ProcessError} carries, in declaration order. */\nexport const PROCESS_ERROR_CODES = Object.freeze([\n\t'spawn',\n\t'timeout',\n\t'input',\n\t'duplicate',\n\t'protocol',\n\t'invalid',\n] as const)\n","import type {\n\tProcessErrorCode,\n\tProcessErrorContext,\n\tProcessErrorOptions,\n\tExecuteResult,\n} from './types.js'\nimport { holds, isError } from '@orkestrel/contract'\nimport { PROCESS_ERROR_CODES } from './constants.js'\n\n/** A child-process failure with a stable machine-readable category. */\nexport class ProcessError extends Error {\n\toverride readonly name = 'ProcessError'\n\treadonly code: ProcessErrorCode\n\treadonly context?: ProcessErrorContext\n\t/** The buffered run outcome, present when a one-shot run produced the failure. */\n\treadonly result?: ExecuteResult\n\n\t/**\n\t * Create a process error.\n\t *\n\t * @param message - Human-readable failure description\n\t * @param options - Machine-readable category, optional context, optional cause, and optional run result\n\t */\n\tconstructor(message: string, options: ProcessErrorOptions) {\n\t\tconst cause = options.cause\n\t\tsuper(message, cause === undefined ? undefined : { cause })\n\t\tObject.defineProperty(this, Symbol.for('@orkestrel/process.error'), { value: true })\n\t\tthis.code = options.code\n\t\tif (options.context !== undefined) this.context = options.context\n\t\tif (options.result !== undefined) this.result = options.result\n\t}\n}\n\n/**\n * Checks whether an unknown value is a {@link ProcessError}.\n *\n * @remarks\n * Recognition combines a global own-property brand with the native `Error` base, the subclass\n * prototype, the fixed name, and a code {@link PROCESS_ERROR_CODES} declares. The brand is\n * recognized across duplicate installations and ESM/CommonJS module copies at 0.0.4 or later. A copy\n * earlier than 0.0.4 stamps no brand, so an error it throws stays outside the type, and so does a\n * plain or property-only lookalike.\n *\n * @param value - The value to inspect\n * @returns True only for a `ProcessError` instance; false otherwise\n *\n * @example\n * ```ts\n * isProcessError(new ProcessError('spawn failed', { code: 'spawn' })) // true\n * isProcessError(new Error('spawn failed')) // false\n * ```\n */\nexport function isProcessError(value: unknown): value is ProcessError {\n\tif (!isError(value)) return false\n\treturn holds(() => {\n\t\tif (Object.getPrototypeOf(value) === Error.prototype) return false\n\t\tif (value.name !== 'ProcessError' || !('code' in value)) return false\n\t\tconst descriptor = Object.getOwnPropertyDescriptor(\n\t\t\tvalue,\n\t\t\tSymbol.for('@orkestrel/process.error'),\n\t\t)\n\t\tif (descriptor?.value !== true) return false\n\t\tconst code: unknown = value.code\n\t\treturn PROCESS_ERROR_CODES.some((declared) => declared === code)\n\t})\n}\n\n/**\n * Creates the failure raised when a manager launch reuses a live id.\n *\n * @param id - The id already occupied by a live child\n * @returns A typed duplicate-id failure\n */\nexport function createDuplicateError(id: string): ProcessError {\n\treturn new ProcessError(`Process '${id}' is already live`, {\n\t\tcode: 'duplicate',\n\t\tcontext: { id },\n\t})\n}\n\n/**\n * Creates the failure raised when a launch is attempted on a registry that is being destroyed.\n *\n * @param id - The id the refused launch asked for\n * @returns A typed protocol failure\n */\nexport function createProtocolError(id: string): ProcessError {\n\treturn new ProcessError(`Process '${id}' cannot launch: the registry is destroyed`, {\n\t\tcode: 'protocol',\n\t\tcontext: { id },\n\t})\n}\n\n/**\n * Creates the failure raised when a public input is refused before anything is spawned.\n *\n * @param subject - The rejected input named as the caller wrote it, such as `option 'grace'`\n * @param value - The rejected value, carried as `context.value`\n * @returns A typed validation failure\n *\n * @example\n * ```ts\n * createInvalidError(\"option 'grace'\", -1).code // 'invalid'\n * ```\n */\nexport function createInvalidError(subject: string, value: unknown): ProcessError {\n\treturn new ProcessError(`Invalid ${subject}`, { code: 'invalid', context: { value } })\n}\n\n/**\n * Creates the failure raised when a run does not complete successfully and rejection is requested.\n *\n * @remarks\n * The category is `timeout` only when the run's own timeout elapsed; every other failure,\n * including an abort and an output overflow, is a `spawn` failure carrying its\n * {@link ExecuteResult}.\n *\n * @param result - The buffered run outcome that failed\n * @param cause - The underlying host fault, when one ended the run\n * @returns A typed run failure carrying its {@link ExecuteResult}\n */\nexport function createExecuteError(result: ExecuteResult, cause?: unknown): ProcessError {\n\tlet reason = `exited with code ${String(result.code)}`\n\tif (result.signal !== null) reason = `was killed by ${result.signal}`\n\tif (result.truncated) reason = 'exceeded its output limit'\n\tif (result.aborted) reason = 'was aborted'\n\tif (result.expired) reason = 'timed out'\n\treturn new ProcessError(`Command '${result.command}' ${reason}`, {\n\t\tcode: result.expired ? 'timeout' : 'spawn',\n\t\tcontext: { command: result.command, code: result.code, signal: result.signal },\n\t\t...(cause === undefined ? {} : { cause }),\n\t\tresult,\n\t})\n}\n"],"mappings":";;;AACA,IAAa,gBAAgB;;AAG7B,IAAa,uBAAuB;;;;;;;;;;;;;;;;AAiBpC,IAAa,gBAAgB;;AAG7B,IAAa,mBAAmB;;AAGhC,IAAa,kBAAkB;;AAG/B,IAAa,iBAAiB;;AAG9B,IAAa,gBAAgB;;AAG7B,IAAa,kBAAkB;;AAG/B,IAAa,sBAAsB,OAAO,OAAO;CAChD;CACA;CACA;CACA;CACA;CACA;AACD,CAAU;;;;ACpCV,IAAa,eAAb,cAAkC,MAAM;CACvC,OAAyB;CACzB;CACA;;CAEA;;;;;;;CAQA,YAAY,SAAiB,SAA8B;EAC1D,MAAM,QAAQ,QAAQ;EACtB,MAAM,SAAS,UAAU,KAAA,IAAY,KAAA,IAAY,EAAE,MAAM,CAAC;EAC1D,OAAO,eAAe,MAAM,OAAO,IAAI,0BAA0B,GAAG,EAAE,OAAO,KAAK,CAAC;EACnF,KAAK,OAAO,QAAQ;EACpB,IAAI,QAAQ,YAAY,KAAA,GAAW,KAAK,UAAU,QAAQ;EAC1D,IAAI,QAAQ,WAAW,KAAA,GAAW,KAAK,SAAS,QAAQ;CACzD;AACD;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,eAAe,OAAuC;CACrE,IAAI,CAAC,QAAQ,KAAK,GAAG,OAAO;CAC5B,OAAO,YAAY;EAClB,IAAI,OAAO,eAAe,KAAK,MAAM,MAAM,WAAW,OAAO;EAC7D,IAAI,MAAM,SAAS,kBAAkB,EAAE,UAAU,QAAQ,OAAO;EAKhE,IAJmB,OAAO,yBACzB,OACA,OAAO,IAAI,0BAA0B,CAElC,CAAA,EAAY,UAAU,MAAM,OAAO;EACvC,MAAM,OAAgB,MAAM;EAC5B,OAAO,oBAAoB,MAAM,aAAa,aAAa,IAAI;CAChE,CAAC;AACF;;;;;;;AAQA,SAAgB,qBAAqB,IAA0B;CAC9D,OAAO,IAAI,aAAa,YAAY,GAAG,oBAAoB;EAC1D,MAAM;EACN,SAAS,EAAE,GAAG;CACf,CAAC;AACF;;;;;;;AAQA,SAAgB,oBAAoB,IAA0B;CAC7D,OAAO,IAAI,aAAa,YAAY,GAAG,6CAA6C;EACnF,MAAM;EACN,SAAS,EAAE,GAAG;CACf,CAAC;AACF;;;;;;;;;;;;;AAcA,SAAgB,mBAAmB,SAAiB,OAA8B;CACjF,OAAO,IAAI,aAAa,WAAW,WAAW;EAAE,MAAM;EAAW,SAAS,EAAE,MAAM;CAAE,CAAC;AACtF;;;;;;;;;;;;;AAcA,SAAgB,mBAAmB,QAAuB,OAA+B;CACxF,IAAI,SAAS,oBAAoB,OAAO,OAAO,IAAI;CACnD,IAAI,OAAO,WAAW,MAAM,SAAS,iBAAiB,OAAO;CAC7D,IAAI,OAAO,WAAW,SAAS;CAC/B,IAAI,OAAO,SAAS,SAAS;CAC7B,IAAI,OAAO,SAAS,SAAS;CAC7B,OAAO,IAAI,aAAa,YAAY,OAAO,QAAQ,IAAI,UAAU;EAChE,MAAM,OAAO,UAAU,YAAY;EACnC,SAAS;GAAE,SAAS,OAAO;GAAS,MAAM,OAAO;GAAM,QAAQ,OAAO;EAAO;EAC7E,GAAI,UAAU,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM;EACvC;CACD,CAAC;AACF"}
|