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
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Restart Failed Processes
|
|
2
|
+
|
|
3
|
+
> Add a restart policy when a child process may fail transiently and the parent
|
|
4
|
+
> script should retry before treating the supervision run as terminal.
|
|
5
|
+
|
|
6
|
+
Restarts are disabled by default. Enable them per process with `restart: true`
|
|
7
|
+
or an explicit `RestartPolicy`.
|
|
8
|
+
|
|
9
|
+
## Use the Built-In Policy
|
|
10
|
+
|
|
11
|
+
Use `restart: true` when the defaults are acceptable:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import process from 'node:process'
|
|
15
|
+
import { supervise } from 'procband'
|
|
16
|
+
|
|
17
|
+
const proc = supervise({
|
|
18
|
+
name: 'worker',
|
|
19
|
+
command: process.execPath,
|
|
20
|
+
args: ['-e', 'process.exit(1)'],
|
|
21
|
+
restart: true,
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const result = await proc
|
|
25
|
+
console.log(result.restarts, result.restartSuppressed)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The default policy restarts failed exits after `1000` milliseconds and suppresses
|
|
29
|
+
restart after more than `3` failed exits inside `30000` milliseconds.
|
|
30
|
+
|
|
31
|
+
## Set an Explicit Policy
|
|
32
|
+
|
|
33
|
+
Use an explicit policy when tests, examples, or short-lived scripts need faster
|
|
34
|
+
feedback:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import process from 'node:process'
|
|
38
|
+
import { supervise } from 'procband'
|
|
39
|
+
|
|
40
|
+
const proc = supervise({
|
|
41
|
+
name: 'job',
|
|
42
|
+
command: process.execPath,
|
|
43
|
+
args: ['-e', 'process.exit(1)'],
|
|
44
|
+
restart: {
|
|
45
|
+
when: 'on-failure',
|
|
46
|
+
delayMs: 25,
|
|
47
|
+
maxFailures: 5,
|
|
48
|
+
windowMs: 1000,
|
|
49
|
+
},
|
|
50
|
+
})
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
With this policy, failed attempts are retried after `25` milliseconds. Once more
|
|
54
|
+
than `5` failures occur inside `1000` milliseconds, `procband` stops retrying
|
|
55
|
+
and resolves the terminal result with `restartSuppressed: true`.
|
|
56
|
+
|
|
57
|
+
## Wait for a Later Attempt
|
|
58
|
+
|
|
59
|
+
`waitFor()` watches future output across restart attempts, so it can wait for a
|
|
60
|
+
line that appears only after earlier attempts fail:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { mkdtempSync, writeFileSync } from 'node:fs'
|
|
64
|
+
import process from 'node:process'
|
|
65
|
+
import { tmpdir } from 'node:os'
|
|
66
|
+
import { join } from 'node:path'
|
|
67
|
+
import { supervise } from 'procband'
|
|
68
|
+
|
|
69
|
+
const stateDir = mkdtempSync(join(tmpdir(), 'procband-example-'))
|
|
70
|
+
const attemptFile = join(stateDir, 'attempt.txt')
|
|
71
|
+
writeFileSync(attemptFile, '0')
|
|
72
|
+
|
|
73
|
+
const script = [
|
|
74
|
+
'const fs = await import("node:fs")',
|
|
75
|
+
'const file = process.argv[1]',
|
|
76
|
+
'let attempt = Number(fs.readFileSync(file, "utf8"))',
|
|
77
|
+
'attempt += 1',
|
|
78
|
+
'fs.writeFileSync(file, String(attempt))',
|
|
79
|
+
'console.log(`attempt ${attempt}`)',
|
|
80
|
+
'if (attempt < 3) process.exit(1)',
|
|
81
|
+
'console.log("ready")',
|
|
82
|
+
].join(';')
|
|
83
|
+
|
|
84
|
+
const proc = supervise({
|
|
85
|
+
name: 'job',
|
|
86
|
+
command: process.execPath,
|
|
87
|
+
args: ['-e', script, attemptFile],
|
|
88
|
+
restart: {
|
|
89
|
+
delayMs: 25,
|
|
90
|
+
maxFailures: 5,
|
|
91
|
+
windowMs: 1000,
|
|
92
|
+
},
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
proc.match(/^attempt \d+$/, (event) => {
|
|
96
|
+
console.log(`observed ${event.line}`)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
await proc.waitFor('ready')
|
|
100
|
+
const result = await proc
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The first two attempts can exit unsuccessfully, the third attempt can print
|
|
104
|
+
`ready`, and the final result includes the number of restart attempts that were
|
|
105
|
+
started.
|
|
106
|
+
|
|
107
|
+
## Pick `when` Deliberately
|
|
108
|
+
|
|
109
|
+
| `when` | Restarts after | Use it when |
|
|
110
|
+
| -------------- | -------------------------------- | ------------------------------------------------------------------------------------ |
|
|
111
|
+
| `'on-failure'` | Non-zero exits and signal exits. | A clean exit means the work is done. |
|
|
112
|
+
| `'on-exit'` | Any exit. | The child is expected to be long-lived and should be relaunched even after code `0`. |
|
|
113
|
+
|
|
114
|
+
`proc.kill()` disables future restarts before stopping the active process tree,
|
|
115
|
+
so deliberate shutdown does not start another attempt.
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Documentation Overview
|
|
2
|
+
|
|
3
|
+
> Choose the right `procband` behavior before reaching for an API: readiness
|
|
4
|
+
> matching, failure observation, restart policy, and shutdown each change how a
|
|
5
|
+
> script should supervise its child process.
|
|
6
|
+
|
|
7
|
+
`procband` is for TypeScript scripts that need to run child processes with a
|
|
8
|
+
small amount of supervision. It is not a daemon, service manager, CLI runner, or
|
|
9
|
+
multi-process orchestrator.
|
|
10
|
+
|
|
11
|
+
## What It Adds
|
|
12
|
+
|
|
13
|
+
Each `supervise()` call wraps one child process and adds five behaviors on top
|
|
14
|
+
of Node.js `spawn()`:
|
|
15
|
+
|
|
16
|
+
| Behavior | Use it when |
|
|
17
|
+
| ------------------------------ | -------------------------------------------------------------------------- |
|
|
18
|
+
| Optional prefixed output | Multiple child processes write to the same parent terminal. |
|
|
19
|
+
| Future line matching | The parent script must wait for a readiness line or react to logs. |
|
|
20
|
+
| Optional restarts | A transient failure should start another child attempt. |
|
|
21
|
+
| Process-tree shutdown | Deliberate cleanup should stop descendants as well as the direct child. |
|
|
22
|
+
| Unobserved failure propagation | A background failure should fail the parent script when nobody awaited it. |
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import process from 'node:process'
|
|
26
|
+
import { supervise } from 'procband'
|
|
27
|
+
|
|
28
|
+
const proc = supervise({
|
|
29
|
+
name: 'api',
|
|
30
|
+
command: process.execPath,
|
|
31
|
+
args: ['-e', 'console.log("ready")'],
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
await proc.waitFor('ready')
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
After `supervise()` runs, the child has already started and future output can be
|
|
38
|
+
matched.
|
|
39
|
+
|
|
40
|
+
## First Decisions
|
|
41
|
+
|
|
42
|
+
| Decision | Default | Change it when |
|
|
43
|
+
| ---------------- | --------------------------------------------- | -------------------------------------------------------------------------- |
|
|
44
|
+
| Process identity | `name` is inferred from `command`. | The command path does not end in `/[-\w]+$/`, or logs need a stable label. |
|
|
45
|
+
| Output prefix | Child output is labeled. | Pass `prefix: false` when the child should write raw output. |
|
|
46
|
+
| Failure handling | Awaiting resolves to `ProcessResult`. | Use `expectSuccess()` for command-runner steps that should reject. |
|
|
47
|
+
| Matching scope | `waitFor()` and `match()` watch both streams. | Pass `{ stream: 'stdout' }` or `{ stream: 'stderr' }` to narrow matching. |
|
|
48
|
+
| Restart behavior | No restart. | Pass `restart: true` or a policy for transient child failures. |
|
|
49
|
+
| Stdin | Disconnected. | Pass `stdin: true` or a readable stream when the child needs input. |
|
|
50
|
+
|
|
51
|
+
## Where to Go Next
|
|
52
|
+
|
|
53
|
+
| Page | Reader job |
|
|
54
|
+
| ---------------------------------------------------- | ------------------------------------------------------------------- |
|
|
55
|
+
| [Readiness and matching](guides/readiness.md) | Wait for future output or subscribe to repeated matching lines. |
|
|
56
|
+
| [Restart failed processes](guides/restarts.md) | Configure retries with a failure-suppression guard. |
|
|
57
|
+
| [Foreground commands](guides/foreground-commands.md) | Make a supervised command reject on failed exit. |
|
|
58
|
+
| [Concepts and lifecycle](context.md) | Understand terminal state, shutdown, config boundaries, and errors. |
|
|
59
|
+
|
|
60
|
+
Runnable scripts in [`../examples/`](../examples/) cover the same workflows with
|
|
61
|
+
project-realistic child processes that can be checked by `pnpm docs:check`.
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "procband",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.7",
|
|
4
4
|
"description": "Supervise subprocesses from TypeScript: prefix logs, wait for output patterns, and manage lifecycle/restarts.",
|
|
5
|
+
"packageManager": "pnpm@11.5.1",
|
|
5
6
|
"license": "MIT",
|
|
6
7
|
"author": "Alec Larson",
|
|
7
8
|
"repository": {
|
|
@@ -18,8 +19,19 @@
|
|
|
18
19
|
"types": "./dist/index.d.mts",
|
|
19
20
|
"default": "./dist/index.mjs"
|
|
20
21
|
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"dev": "tsdown --sourcemap --watch",
|
|
24
|
+
"build": "tsdown",
|
|
25
|
+
"docs:check": "pnpm build && tsc -p examples --noEmit && node examples/basic-usage.ts && node examples/restart-on-failure.ts",
|
|
26
|
+
"format": "oxfmt .",
|
|
27
|
+
"lint": "oxlint src",
|
|
28
|
+
"typecheck": "tsc --noEmit && tsc -p test --noEmit",
|
|
29
|
+
"test": "vitest",
|
|
30
|
+
"prepublishOnly": "pnpm build"
|
|
31
|
+
},
|
|
21
32
|
"devDependencies": {
|
|
22
33
|
"@types/node": "^25.5.2",
|
|
34
|
+
"lildocs": "^0.1.11",
|
|
23
35
|
"oxfmt": "^0.43.0",
|
|
24
36
|
"oxlint": "^1.58.0",
|
|
25
37
|
"tsdown": "^0.21.7",
|
|
@@ -29,14 +41,5 @@
|
|
|
29
41
|
"dependencies": {
|
|
30
42
|
"@alloc/tree-kill": "^1.4.0",
|
|
31
43
|
"ansi-styles": "^6.2.3"
|
|
32
|
-
},
|
|
33
|
-
"scripts": {
|
|
34
|
-
"dev": "tsdown --sourcemap --watch",
|
|
35
|
-
"build": "tsdown",
|
|
36
|
-
"docs:check": "pnpm build && tsc -p examples --noEmit && node examples/basic-usage.ts && node examples/restart-on-failure.ts",
|
|
37
|
-
"format": "oxfmt .",
|
|
38
|
-
"lint": "oxlint src",
|
|
39
|
-
"typecheck": "tsc --noEmit && tsc -p test --noEmit",
|
|
40
|
-
"test": "vitest"
|
|
41
44
|
}
|
|
42
|
-
}
|
|
45
|
+
}
|