procband 0.3.1 → 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/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()` and `stop()`.
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
  *
@@ -81,7 +87,7 @@ interface ProcessConfig {
81
87
  * When provided, `procband` uses this signal for parent-driven cleanup on
82
88
  * `SIGINT`, `SIGTERM`, and `exit` instead of mirroring the parent signal or
83
89
  * relying on the platform default. This only affects parent-driven cleanup;
84
- * `proc.stop()` and `proc.kill()` still use their own explicit signals.
90
+ * `proc.kill()` still uses its own explicit signal argument.
85
91
  */
86
92
  parentExitSignal?: KillSignal;
87
93
  /**
@@ -180,23 +186,6 @@ type MatchCallback = (event: MatchEvent) => void;
180
186
  * Stops a callback subscription created by `match()`.
181
187
  */
182
188
  type Unsubscribe = () => void;
183
- /**
184
- * Options for `stop()`.
185
- */
186
- interface StopOptions {
187
- /**
188
- * Signal used for the initial process-tree shutdown attempt.
189
- *
190
- * Defaults to `"SIGTERM"`.
191
- */
192
- signal?: KillSignal;
193
- /**
194
- * Delay before escalating to `SIGKILL`.
195
- *
196
- * Defaults to `5000`.
197
- */
198
- killAfterMs?: number;
199
- }
200
189
  /**
201
190
  * Final state for a supervised process after all restart attempts are done.
202
191
  */
@@ -273,11 +262,11 @@ interface ProcbandProcess extends ChildProcess, PromiseLike<ProcessResult> {
273
262
  /**
274
263
  * Disable future restarts and terminate the active process tree.
275
264
  *
276
- * `stop()` targets the current child process and any descendants it spawned.
277
- * It first uses the configured `signal`, then escalates to `SIGKILL` after
278
- * `killAfterMs` if the tree is still alive.
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.
279
268
  */
280
- stop(options?: StopOptions): Promise<void>;
269
+ kill(signal?: KillSignal): boolean;
281
270
  }
282
271
  //#endregion
283
272
  //#region src/process.d.ts
@@ -310,4 +299,4 @@ interface ProcbandProcess extends ChildProcess, PromiseLike<ProcessResult> {
310
299
  */
311
300
  declare function supervise(config: ProcessConfig): ProcbandProcess;
312
301
  //#endregion
313
- export { type MatchCallback, type MatchEvent, type MatchOptions, type ProcbandProcess, type ProcessConfig, type ProcessResult, type RestartPolicy, type RgbColor, type StopOptions, type Unsubscribe, type WaitForOptions, supervise };
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
- stopPromise = null;
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
- const child = this.currentChild;
470
- if (!child) return false;
471
- return child.kill(signal);
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
- async stop(options) {
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
- this.stopPromise = this.stopActiveTree(options?.signal ?? "SIGTERM", options?.killAfterMs ?? 5e3);
490
- return this.stopPromise;
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.shutdownRequested = true;
494
- this.restartDisabled = true;
495
- this.restart.cancelDelay();
499
+ this.prepareShutdown();
496
500
  const child = this.currentChild;
497
501
  if (child) killTreeBestEffort(child, this.getParentCleanupSignal());
498
502
  }
499
503
  async cleanupFromSignal(signal) {
500
- this.shutdownRequested = true;
501
- this.restartDisabled = true;
502
- this.restart.cancelDelay();
503
- await this.stopActiveTree(this.getParentCleanupSignal(signal), 1e3);
504
+ await this.beginShutdown(this.getParentCleanupSignal(signal), 1e3);
504
505
  }
505
506
  async stopActiveTree(signal, killAfterMs) {
506
507
  const attempt = this.attempt;
@@ -517,6 +518,7 @@ var ProcbandProcessImpl = class extends EventEmitter {
517
518
  this.generation += 1;
518
519
  const child = spawn(this.config.command, this.config.args ?? [], {
519
520
  cwd: this.config.cwd,
521
+ detached: this.config.detached,
520
522
  env: this.config.env,
521
523
  stdio: [
522
524
  this.config.stdin ? "pipe" : "ignore",
@@ -720,7 +722,7 @@ function propagateFailure(exitCode) {
720
722
  if (propagatedFailure) return;
721
723
  propagatedFailure = true;
722
724
  if (process.exitCode == null || process.exitCode === 0) process.exitCode = exitCode;
723
- for (const proc of liveProcesses) proc.stop().catch(() => {});
725
+ for (const proc of liveProcesses) proc.kill();
724
726
  }
725
727
  //#endregion
726
728
  export { supervise };
package/docs/context.md CHANGED
@@ -36,8 +36,8 @@ Supervision adds five 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, optional parent-exit signal override, and optional
40
- raw `stderr` tee.
39
+ optional stdin behavior, optional detached spawn mode, optional
40
+ parent-exit signal override, and optional raw `stderr` tee.
41
41
  - `ProcbandProcess`
42
42
  The live wrapper returned by `supervise()`. It is a `ChildProcess`-compatible
43
43
  handle, a matching surface, a shutdown surface, and a thenable final result.
@@ -73,7 +73,7 @@ Supervision adds five behaviors on top of raw `spawn()`:
73
73
  - React to repeated matching output:
74
74
  `proc.match(pattern, callback, options)`
75
75
  - Stop the process and its descendants:
76
- `proc.stop()`
76
+ `proc.kill()`
77
77
  - Inspect final exit state:
78
78
  `await proc` or `await proc.wait()`
79
79
  - Take ownership of a process failure:
@@ -95,7 +95,7 @@ Supervision adds five behaviors on top of raw `spawn()`:
95
95
 
96
96
  # Recommended Patterns
97
97
 
98
- - Use `proc.stop()` for deliberate shutdown initiated by your own script.
98
+ - Use `proc.kill()` for deliberate shutdown initiated by your own script.
99
99
  - Reserve `parentExitSignal` for children that expect a specific signal from
100
100
  their supervisor during parent-driven cleanup.
101
101
  - Await `proc` or call `proc.wait()` when your script intends to own failure
@@ -103,11 +103,10 @@ Supervision adds five behaviors on top of raw `spawn()`:
103
103
 
104
104
  # Patterns to Avoid
105
105
 
106
- - Do not treat `parentExitSignal` as a replacement for `StopOptions.signal`.
107
- The former only changes parent-driven cleanup; the latter controls
108
- explicit `proc.stop()` calls.
109
- - Do not rely on `kill()` for full shutdown when descendants may still be
110
- running. `kill()` only targets the current direct child.
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.
111
110
  - Do not expect historical log replay from `match()` or `waitFor()`. Matching
112
111
  is future-only by design.
113
112
 
@@ -131,12 +130,12 @@ Supervision adds five behaviors on top of raw `spawn()`:
131
130
  - The wrapper survives restarts, but inherited `pid`, `stdin`, `stdout`,
132
131
  `stderr`, and related `ChildProcess` fields always refer to the current active
133
132
  child attempt. `stdin` is `null` unless `ProcessConfig.stdin` is enabled.
134
- - `kill()` only signals the current direct child. `stop()` disables restart and
135
- kills the full process tree.
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.
136
135
  - Parent cleanup installs both `SIGINT` and `SIGTERM` handlers while any live
137
136
  supervised process exists. Set `ProcessConfig.parentExitSignal` to override
138
137
  which signal is sent to the child tree during parent-driven cleanup. This
139
- does not change the signal used by explicit `proc.stop()` calls.
138
+ does not change the signal used by explicit `proc.kill()` calls.
140
139
  - `stderr` prefixes always use the reserved red, even when a custom process
141
140
  color is configured.
142
141
  - `ProcessConfig.name` is optional. When omitted, it falls back to the
@@ -153,7 +152,7 @@ Supervision adds five behaviors on top of raw `spawn()`:
153
152
  supervision.
154
153
  - Unobserved terminal failures do not reject promises. They set the parent
155
154
  `process.exitCode` and start stopping sibling `procband` processes.
156
- - `stop()` may reject if tree-kill fails with a non-`ESRCH` error.
155
+ - `kill()` emits `error` if tree-kill fails with a non-`ESRCH` error.
157
156
 
158
157
  # Terminology
159
158
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "procband",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Supervise subprocesses from TypeScript: prefix logs, wait for output patterns, and manage lifecycle/restarts.",
5
5
  "license": "MIT",
6
6
  "author": "Alec Larson",