procband 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -3
- package/dist/index.d.mts +21 -23
- package/dist/index.mjs +22 -17
- package/docs/context.md +29 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,9 +29,6 @@ const result = await proc
|
|
|
29
29
|
console.log(result)
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
`stdin` is disabled by default. Set `stdin: true` to write to `proc.stdin`, or
|
|
33
|
-
pass a readable stream to `stdin` to pipe input automatically.
|
|
34
|
-
|
|
35
32
|
## Documentation Map
|
|
36
33
|
|
|
37
34
|
- Concepts and lifecycle: [docs/context.md](docs/context.md)
|
package/dist/index.d.mts
CHANGED
|
@@ -7,7 +7,7 @@ import { Readable, Writable } from "node:stream";
|
|
|
7
7
|
*/
|
|
8
8
|
type Signals = NodeJS.Signals;
|
|
9
9
|
/**
|
|
10
|
-
* Signal values accepted by `kill()
|
|
10
|
+
* Signal values accepted by `kill()`.
|
|
11
11
|
*/
|
|
12
12
|
type KillSignal = number | Signals;
|
|
13
13
|
/**
|
|
@@ -47,6 +47,12 @@ interface ProcessConfig {
|
|
|
47
47
|
* Environment variables for the child process.
|
|
48
48
|
*/
|
|
49
49
|
env?: Record<string, string | undefined>;
|
|
50
|
+
/**
|
|
51
|
+
* Whether to spawn the child in a detached process group/session.
|
|
52
|
+
*
|
|
53
|
+
* This is passed through to Node.js `spawn()` unchanged.
|
|
54
|
+
*/
|
|
55
|
+
detached?: boolean;
|
|
50
56
|
/**
|
|
51
57
|
* Human-facing label used in prefixed output.
|
|
52
58
|
*
|
|
@@ -75,6 +81,15 @@ interface ProcessConfig {
|
|
|
75
81
|
* pipe bytes into the child automatically.
|
|
76
82
|
*/
|
|
77
83
|
stdin?: boolean | Readable;
|
|
84
|
+
/**
|
|
85
|
+
* Signal to send when the parent process is shutting down.
|
|
86
|
+
*
|
|
87
|
+
* When provided, `procband` uses this signal for parent-driven cleanup on
|
|
88
|
+
* `SIGINT`, `SIGTERM`, and `exit` instead of mirroring the parent signal or
|
|
89
|
+
* relying on the platform default. This only affects parent-driven cleanup;
|
|
90
|
+
* `proc.kill()` still uses its own explicit signal argument.
|
|
91
|
+
*/
|
|
92
|
+
parentExitSignal?: KillSignal;
|
|
78
93
|
/**
|
|
79
94
|
* Automatic restart behavior for terminal child exits.
|
|
80
95
|
*
|
|
@@ -171,23 +186,6 @@ type MatchCallback = (event: MatchEvent) => void;
|
|
|
171
186
|
* Stops a callback subscription created by `match()`.
|
|
172
187
|
*/
|
|
173
188
|
type Unsubscribe = () => void;
|
|
174
|
-
/**
|
|
175
|
-
* Options for `stop()`.
|
|
176
|
-
*/
|
|
177
|
-
interface StopOptions {
|
|
178
|
-
/**
|
|
179
|
-
* Signal used for the initial process-tree shutdown attempt.
|
|
180
|
-
*
|
|
181
|
-
* Defaults to `"SIGTERM"`.
|
|
182
|
-
*/
|
|
183
|
-
signal?: KillSignal;
|
|
184
|
-
/**
|
|
185
|
-
* Delay before escalating to `SIGKILL`.
|
|
186
|
-
*
|
|
187
|
-
* Defaults to `5000`.
|
|
188
|
-
*/
|
|
189
|
-
killAfterMs?: number;
|
|
190
|
-
}
|
|
191
189
|
/**
|
|
192
190
|
* Final state for a supervised process after all restart attempts are done.
|
|
193
191
|
*/
|
|
@@ -264,11 +262,11 @@ interface ProcbandProcess extends ChildProcess, PromiseLike<ProcessResult> {
|
|
|
264
262
|
/**
|
|
265
263
|
* Disable future restarts and terminate the active process tree.
|
|
266
264
|
*
|
|
267
|
-
* `
|
|
268
|
-
*
|
|
269
|
-
* `
|
|
265
|
+
* For supervised processes, `kill()` targets the current child process and
|
|
266
|
+
* any descendants it spawned instead of only the direct child.
|
|
267
|
+
* `kill(0)` preserves the normal `ChildProcess` existence-check behavior.
|
|
270
268
|
*/
|
|
271
|
-
|
|
269
|
+
kill(signal?: KillSignal): boolean;
|
|
272
270
|
}
|
|
273
271
|
//#endregion
|
|
274
272
|
//#region src/process.d.ts
|
|
@@ -301,4 +299,4 @@ interface ProcbandProcess extends ChildProcess, PromiseLike<ProcessResult> {
|
|
|
301
299
|
*/
|
|
302
300
|
declare function supervise(config: ProcessConfig): ProcbandProcess;
|
|
303
301
|
//#endregion
|
|
304
|
-
export { type MatchCallback, type MatchEvent, type MatchOptions, type ProcbandProcess, type ProcessConfig, type ProcessResult, type RestartPolicy, type RgbColor, type
|
|
302
|
+
export { type MatchCallback, type MatchEvent, type MatchOptions, type ProcbandProcess, type ProcessConfig, type ProcessResult, type RestartPolicy, type RgbColor, type Unsubscribe, type WaitForOptions, supervise };
|
package/dist/index.mjs
CHANGED
|
@@ -387,7 +387,7 @@ var ProcbandProcessImpl = class extends EventEmitter {
|
|
|
387
387
|
stderrSink;
|
|
388
388
|
stderrSinkActive = true;
|
|
389
389
|
stderrSinkCleanup = null;
|
|
390
|
-
|
|
390
|
+
shutdownPromise = null;
|
|
391
391
|
shutdownRequested = false;
|
|
392
392
|
restartDisabled = false;
|
|
393
393
|
finalized = false;
|
|
@@ -466,9 +466,12 @@ var ProcbandProcessImpl = class extends EventEmitter {
|
|
|
466
466
|
return this.finalPromise.then(onfulfilled, onrejected);
|
|
467
467
|
}
|
|
468
468
|
kill(signal) {
|
|
469
|
-
|
|
470
|
-
if (!
|
|
471
|
-
|
|
469
|
+
if (signal === 0) return this.currentChild?.kill(0) ?? false;
|
|
470
|
+
if (!this.attempt || this.finalized) return false;
|
|
471
|
+
this.beginShutdown(signal ?? "SIGTERM", 5e3).catch((error) => {
|
|
472
|
+
this.emit("error", error);
|
|
473
|
+
});
|
|
474
|
+
return true;
|
|
472
475
|
}
|
|
473
476
|
ref() {
|
|
474
477
|
this.currentChild?.ref();
|
|
@@ -481,26 +484,24 @@ var ProcbandProcessImpl = class extends EventEmitter {
|
|
|
481
484
|
disconnect() {
|
|
482
485
|
this.currentChild?.disconnect();
|
|
483
486
|
}
|
|
484
|
-
|
|
485
|
-
if (this.stopPromise) return this.stopPromise;
|
|
487
|
+
prepareShutdown() {
|
|
486
488
|
this.shutdownRequested = true;
|
|
487
489
|
this.restartDisabled = true;
|
|
488
490
|
this.restart.cancelDelay();
|
|
489
|
-
|
|
490
|
-
|
|
491
|
+
}
|
|
492
|
+
beginShutdown(signal, killAfterMs) {
|
|
493
|
+
this.prepareShutdown();
|
|
494
|
+
if (this.shutdownPromise) return this.shutdownPromise;
|
|
495
|
+
this.shutdownPromise = this.stopActiveTree(signal, killAfterMs);
|
|
496
|
+
return this.shutdownPromise;
|
|
491
497
|
}
|
|
492
498
|
cleanupFromExit() {
|
|
493
|
-
this.
|
|
494
|
-
this.restartDisabled = true;
|
|
495
|
-
this.restart.cancelDelay();
|
|
499
|
+
this.prepareShutdown();
|
|
496
500
|
const child = this.currentChild;
|
|
497
|
-
if (child) killTreeBestEffort(child);
|
|
501
|
+
if (child) killTreeBestEffort(child, this.getParentCleanupSignal());
|
|
498
502
|
}
|
|
499
503
|
async cleanupFromSignal(signal) {
|
|
500
|
-
this.
|
|
501
|
-
this.restartDisabled = true;
|
|
502
|
-
this.restart.cancelDelay();
|
|
503
|
-
await this.stopActiveTree(signal, 1e3);
|
|
504
|
+
await this.beginShutdown(this.getParentCleanupSignal(signal), 1e3);
|
|
504
505
|
}
|
|
505
506
|
async stopActiveTree(signal, killAfterMs) {
|
|
506
507
|
const attempt = this.attempt;
|
|
@@ -510,10 +511,14 @@ var ProcbandProcessImpl = class extends EventEmitter {
|
|
|
510
511
|
}
|
|
511
512
|
await stopChildTree(attempt.child, attempt.close, () => attempt.closed, signal, killAfterMs);
|
|
512
513
|
}
|
|
514
|
+
getParentCleanupSignal(fallback) {
|
|
515
|
+
return this.config.parentExitSignal ?? fallback;
|
|
516
|
+
}
|
|
513
517
|
spawnAttempt() {
|
|
514
518
|
this.generation += 1;
|
|
515
519
|
const child = spawn(this.config.command, this.config.args ?? [], {
|
|
516
520
|
cwd: this.config.cwd,
|
|
521
|
+
detached: this.config.detached,
|
|
517
522
|
env: this.config.env,
|
|
518
523
|
stdio: [
|
|
519
524
|
this.config.stdin ? "pipe" : "ignore",
|
|
@@ -717,7 +722,7 @@ function propagateFailure(exitCode) {
|
|
|
717
722
|
if (propagatedFailure) return;
|
|
718
723
|
propagatedFailure = true;
|
|
719
724
|
if (process.exitCode == null || process.exitCode === 0) process.exitCode = exitCode;
|
|
720
|
-
for (const proc of liveProcesses) proc.
|
|
725
|
+
for (const proc of liveProcesses) proc.kill();
|
|
721
726
|
}
|
|
722
727
|
//#endregion
|
|
723
728
|
export { supervise };
|
package/docs/context.md
CHANGED
|
@@ -7,7 +7,7 @@ The returned `ProcbandProcess` is both:
|
|
|
7
7
|
- a `ChildProcess`-compatible handle for the current active child attempt
|
|
8
8
|
- a thenable wrapper that resolves to a `ProcessResult` when supervision is done
|
|
9
9
|
|
|
10
|
-
Supervision adds
|
|
10
|
+
Supervision adds five behaviors on top of raw `spawn()`:
|
|
11
11
|
|
|
12
12
|
- prefixed `stdout` and `stderr`
|
|
13
13
|
- line-based matching for future output
|
|
@@ -36,7 +36,8 @@ Supervision adds four behaviors on top of raw `spawn()`:
|
|
|
36
36
|
|
|
37
37
|
- `ProcessConfig`
|
|
38
38
|
Declares one supervised subprocess plus its label, color, restart policy,
|
|
39
|
-
optional stdin behavior,
|
|
39
|
+
optional stdin behavior, optional detached spawn mode, optional
|
|
40
|
+
parent-exit signal override, and optional raw `stderr` tee.
|
|
40
41
|
- `ProcbandProcess`
|
|
41
42
|
The live wrapper returned by `supervise()`. It is a `ChildProcess`-compatible
|
|
42
43
|
handle, a matching surface, a shutdown surface, and a thenable final result.
|
|
@@ -72,7 +73,7 @@ Supervision adds four behaviors on top of raw `spawn()`:
|
|
|
72
73
|
- React to repeated matching output:
|
|
73
74
|
`proc.match(pattern, callback, options)`
|
|
74
75
|
- Stop the process and its descendants:
|
|
75
|
-
`proc.
|
|
76
|
+
`proc.kill()`
|
|
76
77
|
- Inspect final exit state:
|
|
77
78
|
`await proc` or `await proc.wait()`
|
|
78
79
|
- Take ownership of a process failure:
|
|
@@ -89,6 +90,25 @@ Supervision adds four behaviors on top of raw `spawn()`:
|
|
|
89
90
|
`restart: true`
|
|
90
91
|
- Use explicit retry rules:
|
|
91
92
|
`restart: { when, delayMs, maxFailures, windowMs }`
|
|
93
|
+
- Force a specific signal during parent shutdown:
|
|
94
|
+
`parentExitSignal: 'SIGHUP'`
|
|
95
|
+
|
|
96
|
+
# Recommended Patterns
|
|
97
|
+
|
|
98
|
+
- Use `proc.kill()` for deliberate shutdown initiated by your own script.
|
|
99
|
+
- Reserve `parentExitSignal` for children that expect a specific signal from
|
|
100
|
+
their supervisor during parent-driven cleanup.
|
|
101
|
+
- Await `proc` or call `proc.wait()` when your script intends to own failure
|
|
102
|
+
handling instead of inheriting procband's default parent-exit propagation.
|
|
103
|
+
|
|
104
|
+
# Patterns to Avoid
|
|
105
|
+
|
|
106
|
+
- Do not treat `parentExitSignal` as a replacement for the signal passed to
|
|
107
|
+
`proc.kill(signal)`. The former only changes parent-driven cleanup.
|
|
108
|
+
- Do not expect `kill(0)` to stop supervision. `kill(0)` remains an existence
|
|
109
|
+
check for the current child attempt.
|
|
110
|
+
- Do not expect historical log replay from `match()` or `waitFor()`. Matching
|
|
111
|
+
is future-only by design.
|
|
92
112
|
|
|
93
113
|
# Invariants and Constraints
|
|
94
114
|
|
|
@@ -110,10 +130,12 @@ Supervision adds four behaviors on top of raw `spawn()`:
|
|
|
110
130
|
- The wrapper survives restarts, but inherited `pid`, `stdin`, `stdout`,
|
|
111
131
|
`stderr`, and related `ChildProcess` fields always refer to the current active
|
|
112
132
|
child attempt. `stdin` is `null` unless `ProcessConfig.stdin` is enabled.
|
|
113
|
-
- `kill()`
|
|
114
|
-
|
|
133
|
+
- `kill()` disables future restarts and kills the full process tree, except for
|
|
134
|
+
`kill(0)`, which only checks whether the current child attempt exists.
|
|
115
135
|
- Parent cleanup installs both `SIGINT` and `SIGTERM` handlers while any live
|
|
116
|
-
supervised process exists.
|
|
136
|
+
supervised process exists. Set `ProcessConfig.parentExitSignal` to override
|
|
137
|
+
which signal is sent to the child tree during parent-driven cleanup. This
|
|
138
|
+
does not change the signal used by explicit `proc.kill()` calls.
|
|
117
139
|
- `stderr` prefixes always use the reserved red, even when a custom process
|
|
118
140
|
color is configured.
|
|
119
141
|
- `ProcessConfig.name` is optional. When omitted, it falls back to the
|
|
@@ -130,7 +152,7 @@ Supervision adds four behaviors on top of raw `spawn()`:
|
|
|
130
152
|
supervision.
|
|
131
153
|
- Unobserved terminal failures do not reject promises. They set the parent
|
|
132
154
|
`process.exitCode` and start stopping sibling `procband` processes.
|
|
133
|
-
- `
|
|
155
|
+
- `kill()` emits `error` if tree-kill fails with a non-`ESRCH` error.
|
|
134
156
|
|
|
135
157
|
# Terminology
|
|
136
158
|
|
package/package.json
CHANGED