procband 0.3.5 → 0.3.7
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 +97 -15
- package/dist/index.d.mts +81 -6
- package/dist/index.mjs +128 -10
- package/docs/context.md +262 -178
- package/docs/guides/foreground-commands.md +80 -0
- package/docs/guides/readiness.md +104 -0
- package/docs/guides/restarts.md +115 -0
- package/docs/index.md +61 -0
- package/package.json +14 -11
package/README.md
CHANGED
|
@@ -1,38 +1,120 @@
|
|
|
1
1
|
# procband
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Supervise subprocesses from TypeScript when a script needs prefixed output,
|
|
4
|
+
> readiness matching, restart policy, and process-tree shutdown without becoming
|
|
5
|
+
> a standalone process manager.
|
|
4
6
|
|
|
5
|
-
`procband`
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
`procband` wraps one child process per `supervise()` call. It starts the child
|
|
8
|
+
immediately, prefixes `stdout` and `stderr`, lets your script wait for future
|
|
9
|
+
log lines, and resolves to a final `ProcessResult` when no more restart attempt
|
|
10
|
+
will run.
|
|
9
11
|
|
|
10
|
-
##
|
|
12
|
+
## Install
|
|
11
13
|
|
|
12
14
|
```sh
|
|
13
15
|
pnpm add procband
|
|
14
16
|
```
|
|
15
17
|
|
|
16
|
-
##
|
|
18
|
+
## First Process
|
|
19
|
+
|
|
20
|
+
Use `waitFor()` when the next step depends on output that the child will print
|
|
21
|
+
after supervision starts:
|
|
17
22
|
|
|
18
23
|
```ts
|
|
19
24
|
import process from 'node:process'
|
|
20
25
|
import { supervise } from 'procband'
|
|
21
26
|
|
|
22
27
|
const proc = supervise({
|
|
28
|
+
name: 'worker',
|
|
23
29
|
command: process.execPath,
|
|
24
30
|
args: ['-e', 'console.log("ready")'],
|
|
25
31
|
})
|
|
26
32
|
|
|
27
|
-
await proc.waitFor('ready')
|
|
33
|
+
const ready = await proc.waitFor('ready')
|
|
34
|
+
console.log(`matched ${ready.line}`)
|
|
35
|
+
|
|
28
36
|
const result = await proc
|
|
29
|
-
console.log(result)
|
|
37
|
+
console.log(result.exitCode)
|
|
30
38
|
```
|
|
31
39
|
|
|
32
|
-
|
|
40
|
+
The child output is written to the parent process with a `worker` prefix, the
|
|
41
|
+
readiness wait resolves when a future line contains `ready`, and `await proc`
|
|
42
|
+
returns the terminal result.
|
|
43
|
+
|
|
44
|
+
## Choose the Failure Behavior
|
|
45
|
+
|
|
46
|
+
By default, a failed terminal exit resolves to `ProcessResult`. Use this when a
|
|
47
|
+
supervisor script needs to inspect the outcome:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import process from 'node:process'
|
|
51
|
+
import { supervise } from 'procband'
|
|
52
|
+
|
|
53
|
+
const result = await supervise({
|
|
54
|
+
name: 'job',
|
|
55
|
+
command: process.execPath,
|
|
56
|
+
args: ['-e', 'process.exit(1)'],
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
if (result.exitCode !== 0) {
|
|
60
|
+
console.error(`job failed with ${result.exitCode}`)
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Use `expectSuccess()` for foreground command-runner steps where a failed exit
|
|
65
|
+
should reject:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { ProcessExitError, supervise } from 'procband'
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
await supervise({
|
|
72
|
+
name: 'db',
|
|
73
|
+
command: 'pnpm',
|
|
74
|
+
args: ['db', 'ensure'],
|
|
75
|
+
}).expectSuccess()
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (error instanceof ProcessExitError) {
|
|
78
|
+
console.error(error.command, error.args, error.exitCode)
|
|
79
|
+
}
|
|
80
|
+
throw error
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
If your script never awaits the process or calls `wait()`, an unobserved failed
|
|
85
|
+
terminal exit sets the parent `process.exitCode` and starts stopping other live
|
|
86
|
+
`procband` processes in the same parent script.
|
|
87
|
+
|
|
88
|
+
## Prefix an Existing Stream
|
|
89
|
+
|
|
90
|
+
Use `createPrefixStream()` when another API already owns a process lifecycle
|
|
91
|
+
but its output should use procband's label and color formatting:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { createPrefixStream } from 'procband'
|
|
95
|
+
|
|
96
|
+
const output = createPrefixStream({ label: 'postgres' })
|
|
97
|
+
output.pipe(process.stdout)
|
|
98
|
+
postgres.stdout.pipe(output)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The transform preserves stream backpressure, buffers partial lines and UTF-8
|
|
102
|
+
characters across chunks, and flushes a final unterminated line when input ends.
|
|
103
|
+
Pass `color: [red, green, blue]` to select a prefix color; otherwise procband
|
|
104
|
+
assigns the next color from its shared palette.
|
|
105
|
+
|
|
106
|
+
## Documentation
|
|
107
|
+
|
|
108
|
+
Start with the page that matches the decision in front of you:
|
|
109
|
+
|
|
110
|
+
| Page | Use it when |
|
|
111
|
+
| --------------------------------------------------------- | ---------------------------------------------------------------- |
|
|
112
|
+
| [Docs overview](docs/index.md) | You need the moving parts, boundaries, and next page to read. |
|
|
113
|
+
| [Readiness and matching](docs/guides/readiness.md) | A script must wait for output or react to repeated log lines. |
|
|
114
|
+
| [Restart failed processes](docs/guides/restarts.md) | A child can fail transiently and should be retried with limits. |
|
|
115
|
+
| [Foreground commands](docs/guides/foreground-commands.md) | A script step should reject when a command exits unsuccessfully. |
|
|
116
|
+
| [Concepts and lifecycle](docs/context.md) | You need lifecycle, shutdown, error, and invariant details. |
|
|
33
117
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
- Sequencing example: [examples/sequenced-processes.ts](examples/sequenced-processes.ts)
|
|
38
|
-
- Exact exported signatures: [dist/index.d.mts](dist/index.d.mts)
|
|
118
|
+
Runnable examples live in [`examples/`](examples/). Exact public signatures are
|
|
119
|
+
generated from the TypeScript source and published through
|
|
120
|
+
[`dist/index.d.mts`](dist/index.d.mts).
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { Readable, Transform, Writable } from "node:stream";
|
|
1
2
|
import { ChildProcess } from "node:child_process";
|
|
2
|
-
import { Readable, Writable } from "node:stream";
|
|
3
3
|
|
|
4
4
|
//#region src/types.d.ts
|
|
5
5
|
/**
|
|
@@ -62,6 +62,12 @@ interface ProcessConfig {
|
|
|
62
62
|
* Defaults to `name`.
|
|
63
63
|
*/
|
|
64
64
|
label?: string;
|
|
65
|
+
/**
|
|
66
|
+
* Whether child output should be prefixed with the process label.
|
|
67
|
+
*
|
|
68
|
+
* Defaults to `true`. Set to `false` for raw child output.
|
|
69
|
+
*/
|
|
70
|
+
prefix?: boolean;
|
|
65
71
|
/**
|
|
66
72
|
* RGB color for this process's `stdout` prefix.
|
|
67
73
|
*
|
|
@@ -104,6 +110,20 @@ interface ProcessConfig {
|
|
|
104
110
|
* A 24-bit RGB color tuple.
|
|
105
111
|
*/
|
|
106
112
|
type RgbColor = readonly [red: number, green: number, blue: number];
|
|
113
|
+
/**
|
|
114
|
+
* Options for `createPrefixStream()`.
|
|
115
|
+
*/
|
|
116
|
+
interface PrefixStreamOptions {
|
|
117
|
+
/** Human-facing label added to each output line. */
|
|
118
|
+
label: string;
|
|
119
|
+
/**
|
|
120
|
+
* RGB color for the prefix.
|
|
121
|
+
*
|
|
122
|
+
* When omitted, `procband` assigns the next color from its default palette.
|
|
123
|
+
* The reserved `stderr` red cannot be used here.
|
|
124
|
+
*/
|
|
125
|
+
color?: RgbColor;
|
|
126
|
+
}
|
|
107
127
|
/**
|
|
108
128
|
* Controls whether and how a supervised process restarts after exit.
|
|
109
129
|
*/
|
|
@@ -221,6 +241,18 @@ interface ProcessResult {
|
|
|
221
241
|
*/
|
|
222
242
|
restartSuppressed: boolean;
|
|
223
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Options for `wait()`.
|
|
246
|
+
*/
|
|
247
|
+
interface WaitOptions {
|
|
248
|
+
/**
|
|
249
|
+
* Reject with `ProcessExitError` when the process exits unsuccessfully.
|
|
250
|
+
*
|
|
251
|
+
* Defaults to `false`, preserving procband's supervision-oriented behavior
|
|
252
|
+
* where terminal failures resolve to `ProcessResult`.
|
|
253
|
+
*/
|
|
254
|
+
rejectOnFailure?: boolean;
|
|
255
|
+
}
|
|
224
256
|
/**
|
|
225
257
|
* A supervised child-process handle.
|
|
226
258
|
*
|
|
@@ -257,11 +289,16 @@ interface ProcbandProcess extends ChildProcess, PromiseLike<ProcessResult> {
|
|
|
257
289
|
/**
|
|
258
290
|
* Wait for the process to become terminal with no further restart pending.
|
|
259
291
|
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
* rejection for non-zero exits.
|
|
292
|
+
* By default this resolves for both success and failure. Pass
|
|
293
|
+
* `rejectOnFailure: true` to reject failed exits with `ProcessExitError`.
|
|
263
294
|
*/
|
|
264
|
-
wait(): Promise<ProcessResult>;
|
|
295
|
+
wait(options?: WaitOptions): Promise<ProcessResult>;
|
|
296
|
+
/**
|
|
297
|
+
* Wait for the process to become terminal and reject if it fails.
|
|
298
|
+
*
|
|
299
|
+
* This is equivalent to `wait({ rejectOnFailure: true })`.
|
|
300
|
+
*/
|
|
301
|
+
expectSuccess(): Promise<ProcessResult>;
|
|
265
302
|
/**
|
|
266
303
|
* Disable future restarts and terminate the active process tree.
|
|
267
304
|
*
|
|
@@ -273,6 +310,44 @@ interface ProcbandProcess extends ChildProcess, PromiseLike<ProcessResult> {
|
|
|
273
310
|
kill(signal?: KillSignal): boolean;
|
|
274
311
|
}
|
|
275
312
|
//#endregion
|
|
313
|
+
//#region src/errors.d.ts
|
|
314
|
+
/**
|
|
315
|
+
* Error thrown when a process was expected to exit successfully but did not.
|
|
316
|
+
*/
|
|
317
|
+
declare class ProcessExitError extends Error {
|
|
318
|
+
readonly config: ProcessConfig;
|
|
319
|
+
readonly result: ProcessResult;
|
|
320
|
+
readonly command: string;
|
|
321
|
+
readonly args: readonly string[];
|
|
322
|
+
readonly code: number | null;
|
|
323
|
+
readonly exitCode: number;
|
|
324
|
+
readonly signal: NodeJS.Signals | null;
|
|
325
|
+
constructor(config: ProcessConfig, result: ProcessResult);
|
|
326
|
+
}
|
|
327
|
+
//#endregion
|
|
328
|
+
//#region src/prefix-stream.d.ts
|
|
329
|
+
/**
|
|
330
|
+
* Create a transform that prefixes arbitrary output with a procband label.
|
|
331
|
+
*
|
|
332
|
+
* The transform buffers incomplete lines and UTF-8 sequences across chunks.
|
|
333
|
+
* CRLF input is normalized to LF, matching supervised process output, and a
|
|
334
|
+
* final unterminated line is emitted when the writable side ends.
|
|
335
|
+
*
|
|
336
|
+
* @param options Label and optional prefix color.
|
|
337
|
+
* @returns A backpressure-aware Node.js transform stream.
|
|
338
|
+
* @throws When `options.color` is invalid or uses procband's reserved stderr
|
|
339
|
+
* color.
|
|
340
|
+
* @example
|
|
341
|
+
* ```ts
|
|
342
|
+
* import { createPrefixStream } from 'procband'
|
|
343
|
+
*
|
|
344
|
+
* const output = createPrefixStream({ label: 'postgres' })
|
|
345
|
+
* output.pipe(process.stdout)
|
|
346
|
+
* postgres.stdout.pipe(output)
|
|
347
|
+
* ```
|
|
348
|
+
*/
|
|
349
|
+
declare function createPrefixStream(options: PrefixStreamOptions): Transform;
|
|
350
|
+
//#endregion
|
|
276
351
|
//#region src/process.d.ts
|
|
277
352
|
/**
|
|
278
353
|
* Start supervising a single subprocess.
|
|
@@ -303,4 +378,4 @@ interface ProcbandProcess extends ChildProcess, PromiseLike<ProcessResult> {
|
|
|
303
378
|
*/
|
|
304
379
|
declare function supervise(config: ProcessConfig): ProcbandProcess;
|
|
305
380
|
//#endregion
|
|
306
|
-
export { type MatchCallback, type MatchEvent, type MatchOptions, type ProcbandProcess, type ProcessConfig, type ProcessResult, type RestartPolicy, type RgbColor, type Unsubscribe, type WaitForOptions, supervise };
|
|
381
|
+
export { type MatchCallback, type MatchEvent, type MatchOptions, type PrefixStreamOptions, type ProcbandProcess, type ProcessConfig, ProcessExitError, type ProcessResult, type RestartPolicy, type RgbColor, type Unsubscribe, type WaitForOptions, type WaitOptions, createPrefixStream, supervise };
|
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,51 @@
|
|
|
1
|
+
import { StringDecoder } from "node:string_decoder";
|
|
2
|
+
import { Transform } from "node:stream";
|
|
3
|
+
import ansiStyles from "ansi-styles";
|
|
1
4
|
import { spawn } from "node:child_process";
|
|
2
5
|
import { EventEmitter } from "node:events";
|
|
3
6
|
import { constants } from "node:os";
|
|
4
7
|
import process from "node:process";
|
|
5
|
-
import ansiStyles from "ansi-styles";
|
|
6
8
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
7
9
|
import treeKill, { treeKillSync } from "@alloc/tree-kill";
|
|
10
|
+
//#region src/errors.ts
|
|
11
|
+
/**
|
|
12
|
+
* Error thrown when a process was expected to exit successfully but did not.
|
|
13
|
+
*/
|
|
14
|
+
var ProcessExitError = class extends Error {
|
|
15
|
+
config;
|
|
16
|
+
result;
|
|
17
|
+
command;
|
|
18
|
+
args;
|
|
19
|
+
code;
|
|
20
|
+
exitCode;
|
|
21
|
+
signal;
|
|
22
|
+
constructor(config, result) {
|
|
23
|
+
super(formatProcessExitMessage(config, result));
|
|
24
|
+
this.name = "ProcessExitError";
|
|
25
|
+
this.config = config;
|
|
26
|
+
this.result = result;
|
|
27
|
+
this.command = config.command;
|
|
28
|
+
this.args = [...config.args ?? []];
|
|
29
|
+
this.code = result.code;
|
|
30
|
+
this.exitCode = result.exitCode;
|
|
31
|
+
this.signal = result.signal;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
function formatProcessExitMessage(config, result) {
|
|
35
|
+
const command = formatCommand(config);
|
|
36
|
+
const prefix = `Process "${result.name}" failed: ${command}`;
|
|
37
|
+
if (result.signal) return `${prefix} exited by signal ${result.signal} (exit code ${result.exitCode})`;
|
|
38
|
+
if (result.code != null) return `${prefix} exited with code ${result.code}`;
|
|
39
|
+
return `${prefix} exited unsuccessfully (exit code ${result.exitCode})`;
|
|
40
|
+
}
|
|
41
|
+
function formatCommand(config) {
|
|
42
|
+
return [config.command, ...config.args ?? []].map(formatCommandPart).join(" ");
|
|
43
|
+
}
|
|
44
|
+
function formatCommandPart(value) {
|
|
45
|
+
if (/^[\w./:=@%+,-]+$/.test(value)) return value;
|
|
46
|
+
return JSON.stringify(value);
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
8
49
|
//#region src/colors.ts
|
|
9
50
|
const prefixPalette = [
|
|
10
51
|
[
|
|
@@ -70,23 +111,87 @@ function resolveProcessColor(color) {
|
|
|
70
111
|
nextPaletteIndex += 1;
|
|
71
112
|
return [...paletteColor];
|
|
72
113
|
}
|
|
73
|
-
function validateProcessColor(color) {
|
|
114
|
+
function validateProcessColor(color, optionName = "ProcessConfig.color") {
|
|
74
115
|
if (!color) return;
|
|
75
116
|
const [red, green, blue] = color;
|
|
76
117
|
for (const value of [
|
|
77
118
|
red,
|
|
78
119
|
green,
|
|
79
120
|
blue
|
|
80
|
-
]) if (!Number.isInteger(value) || value < 0 || value > 255) throw new Error(
|
|
81
|
-
if (red === stderrColor[0] && green === stderrColor[1] && blue === stderrColor[2]) throw new Error(
|
|
121
|
+
]) if (!Number.isInteger(value) || value < 0 || value > 255) throw new Error(`${optionName} must contain integer RGB values between 0 and 255`);
|
|
122
|
+
if (red === stderrColor[0] && green === stderrColor[1] && blue === stderrColor[2]) throw new Error(`${optionName} cannot use the reserved stderr color`);
|
|
82
123
|
}
|
|
83
124
|
function writePrefixedLine(target, label, color, line, appendNewline) {
|
|
84
|
-
target.write(
|
|
125
|
+
target.write(formatPrefixedLine(label, color, line, appendNewline));
|
|
126
|
+
}
|
|
127
|
+
function formatPrefixedLine(label, color, line, appendNewline) {
|
|
128
|
+
return (label == null ? "" : formatPrefix(label, color)) + line + (appendNewline ? "\n" : "");
|
|
85
129
|
}
|
|
86
130
|
function formatPrefix(label, color) {
|
|
87
131
|
return `${ansiStyles.color.ansi16m(...color)}[${label}]${ansiStyles.color.close} `;
|
|
88
132
|
}
|
|
89
133
|
//#endregion
|
|
134
|
+
//#region src/prefix-stream.ts
|
|
135
|
+
/**
|
|
136
|
+
* Create a transform that prefixes arbitrary output with a procband label.
|
|
137
|
+
*
|
|
138
|
+
* The transform buffers incomplete lines and UTF-8 sequences across chunks.
|
|
139
|
+
* CRLF input is normalized to LF, matching supervised process output, and a
|
|
140
|
+
* final unterminated line is emitted when the writable side ends.
|
|
141
|
+
*
|
|
142
|
+
* @param options Label and optional prefix color.
|
|
143
|
+
* @returns A backpressure-aware Node.js transform stream.
|
|
144
|
+
* @throws When `options.color` is invalid or uses procband's reserved stderr
|
|
145
|
+
* color.
|
|
146
|
+
* @example
|
|
147
|
+
* ```ts
|
|
148
|
+
* import { createPrefixStream } from 'procband'
|
|
149
|
+
*
|
|
150
|
+
* const output = createPrefixStream({ label: 'postgres' })
|
|
151
|
+
* output.pipe(process.stdout)
|
|
152
|
+
* postgres.stdout.pipe(output)
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
function createPrefixStream(options) {
|
|
156
|
+
validateProcessColor(options.color, "PrefixStreamOptions.color");
|
|
157
|
+
return new PrefixTransform(options.label, resolveProcessColor(options.color));
|
|
158
|
+
}
|
|
159
|
+
var PrefixTransform = class extends Transform {
|
|
160
|
+
decoder = new StringDecoder("utf8");
|
|
161
|
+
label;
|
|
162
|
+
color;
|
|
163
|
+
bufferedText = "";
|
|
164
|
+
constructor(label, color) {
|
|
165
|
+
super();
|
|
166
|
+
this.label = label;
|
|
167
|
+
this.color = color;
|
|
168
|
+
}
|
|
169
|
+
_transform(chunk, _encoding, callback) {
|
|
170
|
+
this.consume(this.decoder.write(chunk));
|
|
171
|
+
callback();
|
|
172
|
+
}
|
|
173
|
+
_flush(callback) {
|
|
174
|
+
this.consume(this.decoder.end());
|
|
175
|
+
if (this.bufferedText.length > 0) {
|
|
176
|
+
this.push(formatPrefixedLine(this.label, this.color, this.bufferedText, false));
|
|
177
|
+
this.bufferedText = "";
|
|
178
|
+
}
|
|
179
|
+
callback();
|
|
180
|
+
}
|
|
181
|
+
consume(decodedText) {
|
|
182
|
+
let text = this.bufferedText + decodedText;
|
|
183
|
+
let newlineIndex = text.indexOf("\n");
|
|
184
|
+
while (newlineIndex >= 0) {
|
|
185
|
+
const rawLine = text.slice(0, newlineIndex);
|
|
186
|
+
const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
|
|
187
|
+
this.push(formatPrefixedLine(this.label, this.color, line, true));
|
|
188
|
+
text = text.slice(newlineIndex + 1);
|
|
189
|
+
newlineIndex = text.indexOf("\n");
|
|
190
|
+
}
|
|
191
|
+
this.bufferedText = text;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
//#endregion
|
|
90
195
|
//#region src/matching.ts
|
|
91
196
|
var MatchRegistry = class {
|
|
92
197
|
subscriptions = /* @__PURE__ */ new Set();
|
|
@@ -400,6 +505,7 @@ var ProcbandProcessImpl = class extends EventEmitter {
|
|
|
400
505
|
config;
|
|
401
506
|
name;
|
|
402
507
|
label;
|
|
508
|
+
prefix;
|
|
403
509
|
color;
|
|
404
510
|
matches;
|
|
405
511
|
restart;
|
|
@@ -422,6 +528,7 @@ var ProcbandProcessImpl = class extends EventEmitter {
|
|
|
422
528
|
this.config = config;
|
|
423
529
|
this.name = name;
|
|
424
530
|
this.label = config.label ?? name;
|
|
531
|
+
this.prefix = config.name === "" ? false : config.prefix ?? true;
|
|
425
532
|
this.color = resolveProcessColor(config.color);
|
|
426
533
|
this.matches = new MatchRegistry(this.name);
|
|
427
534
|
this.restart = new RestartController(config.restart);
|
|
@@ -480,10 +587,14 @@ var ProcbandProcessImpl = class extends EventEmitter {
|
|
|
480
587
|
waitFor(pattern, options) {
|
|
481
588
|
return this.matches.waitFor(pattern, options);
|
|
482
589
|
}
|
|
483
|
-
wait() {
|
|
590
|
+
wait(options) {
|
|
484
591
|
this.markTerminalResultObserved();
|
|
592
|
+
if (options?.rejectOnFailure) return this.finalPromise.then((result) => this.expectSuccessfulResult(result));
|
|
485
593
|
return this.finalPromise;
|
|
486
594
|
}
|
|
595
|
+
expectSuccess() {
|
|
596
|
+
return this.wait({ rejectOnFailure: true });
|
|
597
|
+
}
|
|
487
598
|
then(onfulfilled, onrejected) {
|
|
488
599
|
this.markTerminalResultObserved();
|
|
489
600
|
return this.finalPromise.then(onfulfilled, onrejected);
|
|
@@ -626,7 +737,7 @@ var ProcbandProcessImpl = class extends EventEmitter {
|
|
|
626
737
|
buffer.text = text;
|
|
627
738
|
}
|
|
628
739
|
handleLine(stream, line, appendNewline) {
|
|
629
|
-
writePrefixedLine(stream === "stdout" ? process.stdout : process.stderr, this.label, stream === "stdout" ? this.color : stderrColor, line, appendNewline);
|
|
740
|
+
writePrefixedLine(stream === "stdout" ? process.stdout : process.stderr, this.prefix ? this.label : null, stream === "stdout" ? this.color : stderrColor, line, appendNewline);
|
|
630
741
|
this.matches.emit(stream, line);
|
|
631
742
|
}
|
|
632
743
|
async handleAttemptClose(code, signal) {
|
|
@@ -669,12 +780,16 @@ var ProcbandProcessImpl = class extends EventEmitter {
|
|
|
669
780
|
}
|
|
670
781
|
closeMatches() {
|
|
671
782
|
const error = /* @__PURE__ */ new Error(`Process "${this.name}" exited before a matching line was observed`);
|
|
672
|
-
if (this.matches.hasPendingWait()) writePrefixedLine(process.stderr, this.label, stderrColor, error.message, true);
|
|
783
|
+
if (this.matches.hasPendingWait()) writePrefixedLine(process.stderr, this.prefix ? this.label : null, stderrColor, error.message, true);
|
|
673
784
|
this.matches.close(error);
|
|
674
785
|
}
|
|
675
786
|
markTerminalResultObserved() {
|
|
676
787
|
this.terminalResultObserved = true;
|
|
677
788
|
}
|
|
789
|
+
expectSuccessfulResult(result) {
|
|
790
|
+
if (isFailedResult(result)) throw new ProcessExitError(this.config, result);
|
|
791
|
+
return result;
|
|
792
|
+
}
|
|
678
793
|
shouldPropagateFailure(result) {
|
|
679
794
|
return result.exitCode !== 0 && !this.shutdownRequested && !this.terminalResultObserved;
|
|
680
795
|
}
|
|
@@ -734,7 +849,7 @@ let propagatedFailure = false;
|
|
|
734
849
|
function validateProcessConfig(config) {
|
|
735
850
|
if (!config.command) throw new Error("ProcessConfig.command is required");
|
|
736
851
|
validateProcessColor(config.color);
|
|
737
|
-
const name = config.name || inferProcessName(config.command);
|
|
852
|
+
const name = config.name === void 0 || config.name === "" ? inferProcessName(config.command) : config.name;
|
|
738
853
|
if (!name) throw new Error("ProcessConfig.name is required when command does not match /[-\\w]+$/");
|
|
739
854
|
return name;
|
|
740
855
|
}
|
|
@@ -746,6 +861,9 @@ function getResultExitCode(code, signal) {
|
|
|
746
861
|
if (signal) return 128 + (constants.signals[signal] ?? 0);
|
|
747
862
|
return 1;
|
|
748
863
|
}
|
|
864
|
+
function isFailedResult(result) {
|
|
865
|
+
return result.exitCode !== 0 || result.signal != null;
|
|
866
|
+
}
|
|
749
867
|
function propagateFailure(exitCode) {
|
|
750
868
|
if (propagatedFailure) return;
|
|
751
869
|
propagatedFailure = true;
|
|
@@ -756,4 +874,4 @@ function propagateFailure(exitCode) {
|
|
|
756
874
|
});
|
|
757
875
|
}
|
|
758
876
|
//#endregion
|
|
759
|
-
export { supervise };
|
|
877
|
+
export { ProcessExitError, createPrefixStream, supervise };
|