@orkestrel/process 0.0.4 → 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 +18 -0
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +159 -35
- package/dist/src/core/index.d.ts +159 -35
- package/dist/src/core/index.js +18 -1
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +228 -80
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +89 -60
- package/dist/src/server/index.d.ts +89 -60
- package/dist/src/server/index.js +229 -82
- package/dist/src/server/index.js.map +1 -1
- package/package.json +11 -9
package/dist/src/core/index.d.ts
CHANGED
|
@@ -139,13 +139,15 @@ export declare interface ExecuteOptions {
|
|
|
139
139
|
* The settled outcome of a one-shot run: the buffered output and the terminal state.
|
|
140
140
|
*
|
|
141
141
|
* @remarks
|
|
142
|
-
* `failed` is `true` when the
|
|
143
|
-
*
|
|
144
|
-
* child ending itself, and only the earliest observed is recorded.
|
|
145
|
-
*
|
|
146
|
-
* synchronous run and does not fail an asynchronous one. `ProcessInterface` carries the
|
|
147
|
-
* the same fact against a supervised child's retention bounds.
|
|
148
|
-
* `
|
|
142
|
+
* `failed` is `true` when the run timed out, was aborted, ended on a host fault, was ended by a
|
|
143
|
+
* signal, or exited with a code other than `0`. `expired` and `aborted` name the ways the run ended
|
|
144
|
+
* the child rather than the child ending itself, and only the earliest observed is recorded.
|
|
145
|
+
* `truncated` reports that a captured stream omitted output because it exceeded `limit`, which
|
|
146
|
+
* fails a synchronous run and does not fail an asynchronous one. `ProcessInterface` carries the
|
|
147
|
+
* same name for the same fact against a supervised child's retention bounds. For a `strict: false`
|
|
148
|
+
* caller, `failed: true` with `expired`, `aborted`, and `truncated` false, `code: 0`, and
|
|
149
|
+
* `signal: null` is the residual signature that a host fault ended the run. A spawn fault reports
|
|
150
|
+
* the host's negative errno for `execute`. A spawn fault reports `null` for `executeSync`.
|
|
149
151
|
*/
|
|
150
152
|
export declare interface ExecuteResult {
|
|
151
153
|
/** The command line that was run, for diagnostics. */
|
|
@@ -220,8 +222,25 @@ export declare const PROCESS_BACKLOG = 10485760;
|
|
|
220
222
|
/** The window in milliseconds a termination waits for the child's native exit after the final kill. */
|
|
221
223
|
export declare const PROCESS_CONFIRMATION = 5000;
|
|
222
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
|
+
|
|
223
242
|
/** The machine-readable failure categories a {@link ProcessError} carries, in declaration order. */
|
|
224
|
-
export declare const PROCESS_ERROR_CODES: readonly ["spawn", "timeout", "duplicate", "protocol", "invalid"];
|
|
243
|
+
export declare const PROCESS_ERROR_CODES: readonly ["spawn", "timeout", "input", "duplicate", "protocol", "invalid"];
|
|
225
244
|
|
|
226
245
|
/** The default maximum retained stderr tail in bytes for a supervised {@link ProcessInterface}. */
|
|
227
246
|
export declare const PROCESS_EVIDENCE = 2048;
|
|
@@ -330,25 +349,41 @@ export declare interface ProcessErrorOptions {
|
|
|
330
349
|
*
|
|
331
350
|
* @remarks
|
|
332
351
|
* Declared as a `type` alias so it satisfies the emitter's `EventMap` constraint structurally. The
|
|
333
|
-
* `error` event carries a child fault — a failure to spawn
|
|
334
|
-
*
|
|
352
|
+
* `error` event carries a child fault — a failure to spawn, a process-level error, or a
|
|
353
|
+
* host-reported standard-input channel fault after the constructor input phase. A fault arising
|
|
354
|
+
* from the constructor-supplied `input` write or its closing `end` stays quiet because the package
|
|
355
|
+
* initiated that sequence. A surfaced standard-input fault is wrapped in a
|
|
356
|
+
* {@link ProcessError} coded `protocol` with the host fault as its cause. The event is distinct from
|
|
357
|
+
* the `error` handler in {@link ProcessOptions}: a listener throw is isolated by the emitter and
|
|
335
358
|
* routed to that handler, never emitted as this `error` event.
|
|
336
359
|
*/
|
|
337
360
|
export declare type ProcessEventMap = {
|
|
338
361
|
/** A decoded standard-error chunk arrived. */
|
|
339
362
|
readonly stderr: readonly [chunk: string];
|
|
340
|
-
/** The child
|
|
363
|
+
/** The child or its open standard-input channel reported a fault, carrying the host cause directly or through a `protocol` {@link ProcessError}. */
|
|
341
364
|
readonly error: readonly [error: unknown];
|
|
342
365
|
/** The child settled — its terminal state, delivered once. */
|
|
343
366
|
readonly exit: readonly [exit: ProcessExit];
|
|
344
367
|
};
|
|
345
368
|
|
|
346
|
-
/**
|
|
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
|
+
*/
|
|
347
380
|
export declare interface ProcessExit {
|
|
348
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`. */
|
|
349
382
|
readonly code: number | null;
|
|
350
383
|
/** The terminating signal name, or `null` when the process exited on its own. */
|
|
351
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;
|
|
352
387
|
}
|
|
353
388
|
|
|
354
389
|
/**
|
|
@@ -369,14 +404,22 @@ export declare interface ProcessExit {
|
|
|
369
404
|
* its own capture `limit`. The typed `emitter` carries the live `stderr` chunks, the child `error` cause, and the
|
|
370
405
|
* terminal `exit`, alongside the `exit` promise. `stop` and `destroy` are idempotent and never reject.
|
|
371
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
|
+
*
|
|
372
415
|
* The spawn is eager, so `pid` is fixed by the time construction returns; a spawn that produced no
|
|
373
416
|
* child reports `undefined` for that child's whole lifetime. An assigned id survives the exit and
|
|
374
417
|
* reports no liveness on its own, because the host reuses a dead child's id. Derive liveness as
|
|
375
418
|
* `pid !== undefined && code === null && signal === null`, and derive it again before every use of
|
|
376
419
|
* the id. `code` and `signal` mirror the host child's own terminal record, so they carry the native
|
|
377
|
-
* exit as soon as the host has it, while the `exit` promise settles
|
|
378
|
-
* holding inherited stdio keeps
|
|
379
|
-
* 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.
|
|
380
423
|
*/
|
|
381
424
|
export declare interface ProcessInterface {
|
|
382
425
|
/** The host process id, fixed when construction returns, or `undefined` when the spawn produced none. */
|
|
@@ -387,29 +430,88 @@ export declare interface ProcessInterface {
|
|
|
387
430
|
readonly signal: string | null;
|
|
388
431
|
/** The typed lifecycle observation surface. */
|
|
389
432
|
readonly emitter: EmitterInterface<ProcessEventMap>;
|
|
390
|
-
/**
|
|
433
|
+
/**
|
|
434
|
+
* The captured stdout lines, in arrival order, for one consumer, ending at the terminal moment.
|
|
435
|
+
*
|
|
436
|
+
* @remarks
|
|
437
|
+
* A line feed, a CRLF pair, and a bare carriage return each terminate a line, and a CRLF split
|
|
438
|
+
* across delivered chunks joins as one break. A child that redraws a progress bar with a carriage
|
|
439
|
+
* return therefore yields one line per redraw, and consecutive carriage returns yield an empty
|
|
440
|
+
* line between them. A final line written with no trailing terminator is delivered when stdout
|
|
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.
|
|
451
|
+
*/
|
|
391
452
|
readonly lines: AsyncIterable<string>;
|
|
392
|
-
/**
|
|
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
|
+
*/
|
|
393
463
|
readonly evidence: string;
|
|
394
464
|
/** True when the `lines` stream omitted output because a retention bound was reached. */
|
|
395
465
|
readonly truncated: boolean;
|
|
396
|
-
/**
|
|
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
|
+
*/
|
|
397
494
|
readonly exit: Promise<ProcessExit>;
|
|
398
495
|
/**
|
|
399
496
|
* Write one line to the open standard-input channel.
|
|
400
497
|
*
|
|
401
498
|
* @remarks
|
|
402
|
-
* Never rejects.
|
|
403
|
-
*
|
|
404
|
-
*
|
|
405
|
-
*
|
|
499
|
+
* Never rejects. `true` means the host accepted the bytes without reporting a fault; it does not
|
|
500
|
+
* prove that the child read them. An ordinary write settles when the kernel accepts it. Only a
|
|
501
|
+
* full pipe can hold the write unconfirmed. The `delivery` option can bound that wait, and every
|
|
502
|
+
* terminal teardown path settles pending writes. On Windows 11 with Node v24.18.1, measured on
|
|
503
|
+
* 2026-08-21, a child that closes its own file descriptor 0 can leave the parent pipe writable:
|
|
504
|
+
* the write can settle `true` without a callback error or a stream error while the child remains
|
|
505
|
+
* alive. After `stop` or `destroy` begins, a later call settles `false`. Version 0.0.4 could settle
|
|
506
|
+
* that call `true` before teardown destroyed the pipe; returning `false` avoids claiming delivery
|
|
507
|
+
* for bytes the package is about to discard.
|
|
406
508
|
*
|
|
407
509
|
* @param text - The line text without its trailing newline
|
|
408
|
-
* @returns True when the
|
|
510
|
+
* @returns True when the host accepted the bytes without reporting a fault; false when the channel was closed, destroyed, ended, failed, or remained unconfirmed through `delivery`
|
|
409
511
|
*/
|
|
410
512
|
send(text: string): Promise<boolean>;
|
|
411
513
|
/**
|
|
412
|
-
* Terminate the child process tree
|
|
514
|
+
* Terminate the child process tree, await its observed exit, and reach the terminal moment.
|
|
413
515
|
*
|
|
414
516
|
* @remarks
|
|
415
517
|
* Never rejects. On Windows the whole tree is killed immediately through `taskkill`, with a
|
|
@@ -417,16 +519,25 @@ export declare interface ProcessInterface {
|
|
|
417
519
|
* `SIGKILL` after `grace`. No signal is initiated after the child's native exit is observed; the
|
|
418
520
|
* window between initiating a signal and the host delivering it belongs to the operating system.
|
|
419
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
|
+
*
|
|
420
527
|
* @returns True when the child's native exit was observed; false when the confirmation deadline elapsed without it
|
|
421
528
|
*/
|
|
422
529
|
stop(): Promise<boolean>;
|
|
423
530
|
/**
|
|
424
|
-
* 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.
|
|
425
533
|
*
|
|
426
534
|
* @remarks
|
|
427
|
-
* Always resolves, including when termination was never confirmed.
|
|
428
|
-
*
|
|
429
|
-
*
|
|
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.
|
|
430
541
|
*
|
|
431
542
|
* @returns The stable barrier shared by every call
|
|
432
543
|
*/
|
|
@@ -483,11 +594,11 @@ export declare interface ProcessManagerInterface {
|
|
|
483
594
|
* @remarks
|
|
484
595
|
* Every option is read before the child is spawned, so a caller's own option getter runs while
|
|
485
596
|
* nothing has started and a throw from one strands no process. A getter that begins `destroy`
|
|
486
|
-
* without throwing is the one remaining race
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
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.
|
|
491
602
|
*
|
|
492
603
|
* @param id - The registry key, unique among live children
|
|
493
604
|
* @param options - The child construction options
|
|
@@ -546,8 +657,17 @@ export declare interface ProcessManagerOptions {
|
|
|
546
657
|
*
|
|
547
658
|
* @remarks
|
|
548
659
|
* `grace` is the cooperative window between `SIGTERM` and `SIGKILL` on a POSIX host; Windows has no
|
|
549
|
-
* cooperative phase, so the value is unused there. There is no completion deadline
|
|
550
|
-
*
|
|
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.
|
|
551
671
|
* During termination, retained lines are capped at twice `backlog`; later lines are dropped without
|
|
552
672
|
* pausing stdout, and {@link ProcessInterface.truncated} reports the omission. `on` installs initial
|
|
553
673
|
* {@link ProcessEventMap} listeners and `error` receives isolated listener failures. Every numeric
|
|
@@ -565,10 +685,14 @@ export declare interface ProcessOptions {
|
|
|
565
685
|
readonly workspace: string;
|
|
566
686
|
/** Cooperative POSIX window in milliseconds between `SIGTERM` and `SIGKILL`. Default: {@link PROCESS_GRACE}. */
|
|
567
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;
|
|
568
690
|
/** Maximum retained stderr tail in bytes. Default: {@link PROCESS_EVIDENCE}. */
|
|
569
691
|
readonly evidence?: number;
|
|
570
692
|
/** Soft high-water mark in bytes for the unconsumed `lines` backlog; termination retains at most twice this value. Default: {@link PROCESS_BACKLOG}. */
|
|
571
693
|
readonly backlog?: number;
|
|
694
|
+
/** Milliseconds an unconfirmed {@link ProcessInterface.send} waits before resolving `false`. `0` or omitted disables the bound. */
|
|
695
|
+
readonly delivery?: number;
|
|
572
696
|
/** If `true`, stdin stays open for {@link ProcessInterface.send}; if `false` or omitted, stdin closes after any initial `input`. */
|
|
573
697
|
readonly writable?: boolean;
|
|
574
698
|
/** Aborting this signal terminates the child through the same bounded `stop`. */
|
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. */
|
|
@@ -18,6 +34,7 @@ var PROCESS_PATHEXT = ".COM;.EXE;.BAT;.CMD";
|
|
|
18
34
|
var PROCESS_ERROR_CODES = Object.freeze([
|
|
19
35
|
"spawn",
|
|
20
36
|
"timeout",
|
|
37
|
+
"input",
|
|
21
38
|
"duplicate",
|
|
22
39
|
"protocol",
|
|
23
40
|
"invalid"
|
|
@@ -147,6 +164,6 @@ function createExecuteError(result, cause) {
|
|
|
147
164
|
});
|
|
148
165
|
}
|
|
149
166
|
//#endregion
|
|
150
|
-
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 };
|
|
151
168
|
|
|
152
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'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"}
|