@procedures/shell 0.3.0 → 0.5.0
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/CHANGELOG.md +34 -0
- package/README.md +24 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/command/index.d.ts +1 -1
- package/dist/command/index.d.ts.map +1 -1
- package/dist/command/index.js +1 -1
- package/dist/command/index.js.map +1 -1
- package/dist/command/processes.d.ts +69 -0
- package/dist/command/processes.d.ts.map +1 -1
- package/dist/command/processes.js +129 -7
- package/dist/command/processes.js.map +1 -1
- package/package.json +2 -2
- package/src/command/index.ts +5 -0
- package/src/command/processes.ts +167 -7
package/dist/command/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export { runCommand, type CommandOptions, type CommandResult } from "./run.js";
|
|
13
13
|
export { streamCommand, type StreamCommandOptions, type CommandStreamItem } from "./stream.js";
|
|
14
|
-
export { ProcessRegistry, processes, type ManagedProcessInfo, type StartProcessOptions, type StopResult, } from "./processes.js";
|
|
14
|
+
export { ProcessRegistry, processes, FOLLOW_BUFFER_BYTES, type ManagedProcessInfo, type StartProcessOptions, type StopResult, type ProcessOutputChunk, type ProcessOutputListener, type ProcessFollowItem, type FollowOptions, } from "./processes.js";
|
|
15
15
|
export { killTree } from "./tree-kill.js";
|
|
16
16
|
export type { SpawnOptions } from "./spawn.js";
|
|
17
17
|
export { DEFAULT_MAX_OUTPUT_BYTES } from "./output.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/command/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC/F,OAAO,EACL,eAAe,EACf,SAAS,EACT,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,UAAU,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/command/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC/F,OAAO,EACL,eAAe,EACf,SAAS,EACT,mBAAmB,EACnB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,aAAa,GACnB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,YAAY,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/command/index.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export { runCommand } from "./run.js";
|
|
13
13
|
export { streamCommand } from "./stream.js";
|
|
14
|
-
export { ProcessRegistry, processes, } from "./processes.js";
|
|
14
|
+
export { ProcessRegistry, processes, FOLLOW_BUFFER_BYTES, } from "./processes.js";
|
|
15
15
|
export { killTree } from "./tree-kill.js";
|
|
16
16
|
export { DEFAULT_MAX_OUTPUT_BYTES } from "./output.js";
|
|
17
17
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/command/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAA2C,MAAM,UAAU,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAqD,MAAM,aAAa,CAAC;AAC/F,OAAO,EACL,eAAe,EACf,SAAS,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/command/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAA2C,MAAM,UAAU,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAqD,MAAM,aAAa,CAAC;AAC/F,OAAO,EACL,eAAe,EACf,SAAS,EACT,mBAAmB,GAQpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* - a record (id, pid, status, exit code), which `list` and `get` give;
|
|
8
8
|
* - a `stop` that kills the whole process tree;
|
|
9
9
|
* - the end of its output (the last 64 KiB), which also keeps its pipes from filling;
|
|
10
|
+
* - the output after a time, for a follower: `subscribe` and `follow`;
|
|
10
11
|
* - an end with the host: the host does not wait for the process, and kills it when it ends.
|
|
11
12
|
*
|
|
12
13
|
* The registry keeps the records of ended processes, up to `keepExited` of them.
|
|
@@ -40,6 +41,58 @@ export interface StopResult {
|
|
|
40
41
|
exitCode?: number;
|
|
41
42
|
signal?: string;
|
|
42
43
|
}
|
|
44
|
+
/** One piece of the output of a process, as `subscribe` gives it. */
|
|
45
|
+
export interface ProcessOutputChunk {
|
|
46
|
+
/** The stream of the piece. */
|
|
47
|
+
stream: "stdout" | "stderr";
|
|
48
|
+
/** The bytes of the piece. A piece can end in a line or in a UTF-8 character. */
|
|
49
|
+
data: Buffer;
|
|
50
|
+
}
|
|
51
|
+
/** The functions that `subscribe` calls for one subscriber. */
|
|
52
|
+
export interface ProcessOutputListener {
|
|
53
|
+
/** This function gets each piece of output that the process writes after the subscription. */
|
|
54
|
+
output?(chunk: ProcessOutputChunk): void;
|
|
55
|
+
/** This function gets a copy of the record when the process ends. After it, no call comes. */
|
|
56
|
+
end?(info: ManagedProcessInfo): void;
|
|
57
|
+
}
|
|
58
|
+
/** One item of `follow`. */
|
|
59
|
+
export type ProcessFollowItem =
|
|
60
|
+
/** The output that the registry kept before the subscription: the first item with `backlog`. */
|
|
61
|
+
{
|
|
62
|
+
type: "backlog";
|
|
63
|
+
text: string;
|
|
64
|
+
}
|
|
65
|
+
/** A piece of new output, decoded as UTF-8. A piece can end in the middle of a line. */
|
|
66
|
+
| {
|
|
67
|
+
type: "output";
|
|
68
|
+
stream: "stdout" | "stderr";
|
|
69
|
+
text: string;
|
|
70
|
+
}
|
|
71
|
+
/** The reader was slow, so the follower dropped the oldest pieces: this many bytes. */
|
|
72
|
+
| {
|
|
73
|
+
type: "dropped";
|
|
74
|
+
bytes: number;
|
|
75
|
+
}
|
|
76
|
+
/** The end of the process: the last item. */
|
|
77
|
+
| {
|
|
78
|
+
type: "end";
|
|
79
|
+
info: ManagedProcessInfo;
|
|
80
|
+
};
|
|
81
|
+
/** The options of `follow`. */
|
|
82
|
+
export interface FollowOptions {
|
|
83
|
+
/** True: the first item is the output that the registry kept before the subscription. */
|
|
84
|
+
backlog?: boolean | undefined;
|
|
85
|
+
/** An abort ends the stream. The process continues. */
|
|
86
|
+
signal?: AbortSignal | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* The most bytes of output that wait for the reader. Above it, the follower drops the oldest
|
|
89
|
+
* pieces and gives a `dropped` item, because the registry cannot slow the process.
|
|
90
|
+
* @default 1 MiB
|
|
91
|
+
*/
|
|
92
|
+
maxBufferedBytes?: number | undefined;
|
|
93
|
+
}
|
|
94
|
+
/** The default of `maxBufferedBytes` of `follow`. */
|
|
95
|
+
export declare const FOLLOW_BUFFER_BYTES: number;
|
|
43
96
|
/** This function removes the ANSI escape sequences from a text. */
|
|
44
97
|
export declare function stripAnsi(text: string): string;
|
|
45
98
|
export declare class ProcessRegistry {
|
|
@@ -59,6 +112,22 @@ export declare class ProcessRegistry {
|
|
|
59
112
|
}): ManagedProcessInfo[];
|
|
60
113
|
/** This function gives the end of the output of a process (stdout and stderr together). */
|
|
61
114
|
output(id: string): string;
|
|
115
|
+
/**
|
|
116
|
+
* This function subscribes to the output of a process. `listener.output` gets each piece of
|
|
117
|
+
* output after this call, with its stream. `listener.end` gets the record at the end of the
|
|
118
|
+
* process, and then the registry forgets the subscriber. For a process that ended already,
|
|
119
|
+
* `listener.end` runs at once. The result removes the subscriber. It is undefined when the
|
|
120
|
+
* registry has no process of this id.
|
|
121
|
+
*/
|
|
122
|
+
subscribe(id: string, listener: ProcessOutputListener): (() => void) | undefined;
|
|
123
|
+
/**
|
|
124
|
+
* This function gives the output of a process as a stream, until the process ends or the
|
|
125
|
+
* signal aborts. The subscription starts when the reader asks for the first item, and the
|
|
126
|
+
* end of the stream removes it: an end of the process, an abort, or a reader that stops. With
|
|
127
|
+
* `backlog`, the first item is the output that the registry kept until then, without a gap.
|
|
128
|
+
* The last item of an ended process is `end`. An unknown id throws at the first item.
|
|
129
|
+
*/
|
|
130
|
+
follow(id: string, options?: FollowOptions): AsyncGenerator<ProcessFollowItem, void, undefined>;
|
|
62
131
|
/** This function waits until the process ends, then gives its record. */
|
|
63
132
|
exited(id: string): Promise<ManagedProcessInfo | undefined>;
|
|
64
133
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"processes.d.ts","sourceRoot":"","sources":["../../src/command/processes.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"processes.d.ts","sourceRoot":"","sources":["../../src/command/processes.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,13 +7,17 @@
|
|
|
7
7
|
* - a record (id, pid, status, exit code), which `list` and `get` give;
|
|
8
8
|
* - a `stop` that kills the whole process tree;
|
|
9
9
|
* - the end of its output (the last 64 KiB), which also keeps its pipes from filling;
|
|
10
|
+
* - the output after a time, for a follower: `subscribe` and `follow`;
|
|
10
11
|
* - an end with the host: the host does not wait for the process, and kills it when it ends.
|
|
11
12
|
*
|
|
12
13
|
* The registry keeps the records of ended processes, up to `keepExited` of them.
|
|
13
14
|
*/
|
|
15
|
+
import { StringDecoder } from "node:string_decoder";
|
|
14
16
|
import { killTree } from "./tree-kill.js";
|
|
15
17
|
import { OutputCollector } from "./output.js";
|
|
16
18
|
import { startChild, startError } from "./spawn.js";
|
|
19
|
+
/** The default of `maxBufferedBytes` of `follow`. */
|
|
20
|
+
export const FOLLOW_BUFFER_BYTES = 1024 * 1024;
|
|
17
21
|
/** The end of the output that the registry keeps for each process. */
|
|
18
22
|
const OUTPUT_TAIL_BYTES = 64 * 1024;
|
|
19
23
|
/** How long `stop` waits after the first kill, before it kills again with SIGKILL. */
|
|
@@ -24,6 +28,15 @@ const ANSI = /\x1b\[[0-?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g;
|
|
|
24
28
|
export function stripAnsi(text) {
|
|
25
29
|
return text.replace(ANSI, "");
|
|
26
30
|
}
|
|
31
|
+
/** This function calls a function of a subscriber. An error of a subscriber does not reach the registry. */
|
|
32
|
+
function notify(run) {
|
|
33
|
+
try {
|
|
34
|
+
run();
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
console.error("A subscriber of the process registry failed:", error);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
27
40
|
export class ProcessRegistry {
|
|
28
41
|
entries = new Map();
|
|
29
42
|
keepExited;
|
|
@@ -51,18 +64,19 @@ export class ProcessRegistry {
|
|
|
51
64
|
info.label = options.label;
|
|
52
65
|
const output = new OutputCollector(OUTPUT_TAIL_BYTES, "tail");
|
|
53
66
|
const watchers = new Set();
|
|
67
|
+
const listeners = new Set();
|
|
54
68
|
const changed = () => {
|
|
55
69
|
for (const watcher of [...watchers])
|
|
56
70
|
watcher();
|
|
57
71
|
};
|
|
58
|
-
|
|
59
|
-
output.add(chunk);
|
|
60
|
-
changed();
|
|
61
|
-
});
|
|
62
|
-
child.stderr.on("data", (chunk) => {
|
|
72
|
+
const received = (stream, chunk) => {
|
|
63
73
|
output.add(chunk);
|
|
74
|
+
for (const listener of [...listeners])
|
|
75
|
+
notify(() => listener.output?.({ stream, data: chunk }));
|
|
64
76
|
changed();
|
|
65
|
-
}
|
|
77
|
+
};
|
|
78
|
+
child.stdout.on("data", (chunk) => received("stdout", chunk));
|
|
79
|
+
child.stderr.on("data", (chunk) => received("stderr", chunk));
|
|
66
80
|
const done = new Promise((resolve) => {
|
|
67
81
|
let finished = false;
|
|
68
82
|
const finish = () => {
|
|
@@ -72,6 +86,11 @@ export class ProcessRegistry {
|
|
|
72
86
|
info.exitedAt = new Date().toISOString();
|
|
73
87
|
resolve();
|
|
74
88
|
changed();
|
|
89
|
+
// The end is the last call to a subscriber: the registry then forgets each subscriber
|
|
90
|
+
const ending = [...listeners];
|
|
91
|
+
listeners.clear();
|
|
92
|
+
for (const listener of ending)
|
|
93
|
+
notify(() => listener.end?.({ ...info, args: [...info.args] }));
|
|
75
94
|
this.reap();
|
|
76
95
|
};
|
|
77
96
|
child.on("error", (error) => {
|
|
@@ -95,7 +114,7 @@ export class ProcessRegistry {
|
|
|
95
114
|
for (const stream of [child.stdout, child.stderr]) {
|
|
96
115
|
stream?.unref?.();
|
|
97
116
|
}
|
|
98
|
-
this.entries.set(id, { info, child, output, watchers, done });
|
|
117
|
+
this.entries.set(id, { info, child, output, watchers, listeners, done });
|
|
99
118
|
this.reap();
|
|
100
119
|
return { ...info };
|
|
101
120
|
}
|
|
@@ -118,6 +137,109 @@ export class ProcessRegistry {
|
|
|
118
137
|
output(id) {
|
|
119
138
|
return this.entries.get(id)?.output.text() ?? "";
|
|
120
139
|
}
|
|
140
|
+
/**
|
|
141
|
+
* This function subscribes to the output of a process. `listener.output` gets each piece of
|
|
142
|
+
* output after this call, with its stream. `listener.end` gets the record at the end of the
|
|
143
|
+
* process, and then the registry forgets the subscriber. For a process that ended already,
|
|
144
|
+
* `listener.end` runs at once. The result removes the subscriber. It is undefined when the
|
|
145
|
+
* registry has no process of this id.
|
|
146
|
+
*/
|
|
147
|
+
subscribe(id, listener) {
|
|
148
|
+
const entry = this.entries.get(id);
|
|
149
|
+
if (!entry)
|
|
150
|
+
return undefined;
|
|
151
|
+
if (entry.info.exitedAt) {
|
|
152
|
+
notify(() => listener.end?.({ ...entry.info, args: [...entry.info.args] }));
|
|
153
|
+
return () => undefined;
|
|
154
|
+
}
|
|
155
|
+
entry.listeners.add(listener);
|
|
156
|
+
return () => {
|
|
157
|
+
entry.listeners.delete(listener);
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* This function gives the output of a process as a stream, until the process ends or the
|
|
162
|
+
* signal aborts. The subscription starts when the reader asks for the first item, and the
|
|
163
|
+
* end of the stream removes it: an end of the process, an abort, or a reader that stops. With
|
|
164
|
+
* `backlog`, the first item is the output that the registry kept until then, without a gap.
|
|
165
|
+
* The last item of an ended process is `end`. An unknown id throws at the first item.
|
|
166
|
+
*/
|
|
167
|
+
async *follow(id, options = {}) {
|
|
168
|
+
const limit = Math.max(1, options.maxBufferedBytes ?? FOLLOW_BUFFER_BYTES);
|
|
169
|
+
const signal = options.signal;
|
|
170
|
+
const queue = [];
|
|
171
|
+
let queued = 0;
|
|
172
|
+
let dropped = 0;
|
|
173
|
+
let ended;
|
|
174
|
+
let wake;
|
|
175
|
+
const wakeUp = () => {
|
|
176
|
+
const resolve = wake;
|
|
177
|
+
wake = undefined;
|
|
178
|
+
resolve?.();
|
|
179
|
+
};
|
|
180
|
+
const decoders = { stdout: new StringDecoder("utf8"), stderr: new StringDecoder("utf8") };
|
|
181
|
+
const add = (stream, text, bytes) => {
|
|
182
|
+
if (text === "")
|
|
183
|
+
return;
|
|
184
|
+
queue.push({ item: { type: "output", stream, text }, bytes });
|
|
185
|
+
queued += bytes;
|
|
186
|
+
// The registry cannot slow the process: above the limit, the oldest pieces go
|
|
187
|
+
while (queued > limit && queue.length > 1) {
|
|
188
|
+
const oldest = queue.shift();
|
|
189
|
+
queued -= oldest.bytes;
|
|
190
|
+
dropped += oldest.bytes;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
if (signal?.aborted)
|
|
194
|
+
return;
|
|
195
|
+
const backlog = options.backlog ? this.output(id) : "";
|
|
196
|
+
const unsubscribe = this.subscribe(id, {
|
|
197
|
+
output: ({ stream, data }) => {
|
|
198
|
+
add(stream, decoders[stream].write(data), data.length);
|
|
199
|
+
wakeUp();
|
|
200
|
+
},
|
|
201
|
+
end: (info) => {
|
|
202
|
+
for (const stream of ["stdout", "stderr"]) {
|
|
203
|
+
const rest = decoders[stream].end();
|
|
204
|
+
add(stream, rest, Buffer.byteLength(rest));
|
|
205
|
+
}
|
|
206
|
+
ended = info;
|
|
207
|
+
wakeUp();
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
if (!unsubscribe)
|
|
211
|
+
throw new Error(`No process ${id}`);
|
|
212
|
+
signal?.addEventListener("abort", wakeUp, { once: true });
|
|
213
|
+
try {
|
|
214
|
+
if (options.backlog)
|
|
215
|
+
yield { type: "backlog", text: backlog };
|
|
216
|
+
for (;;) {
|
|
217
|
+
if (signal?.aborted)
|
|
218
|
+
return;
|
|
219
|
+
if (dropped > 0) {
|
|
220
|
+
const bytes = dropped;
|
|
221
|
+
dropped = 0;
|
|
222
|
+
yield { type: "dropped", bytes };
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
const next = queue.shift();
|
|
226
|
+
if (next) {
|
|
227
|
+
queued -= next.bytes;
|
|
228
|
+
yield next.item;
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (ended) {
|
|
232
|
+
yield { type: "end", info: ended };
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
await new Promise((resolve) => (wake = resolve));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
finally {
|
|
239
|
+
unsubscribe();
|
|
240
|
+
signal?.removeEventListener("abort", wakeUp);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
121
243
|
/** This function waits until the process ends, then gives its record. */
|
|
122
244
|
async exited(id) {
|
|
123
245
|
const entry = this.entries.get(id);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"processes.js","sourceRoot":"","sources":["../../src/command/processes.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"processes.js","sourceRoot":"","sources":["../../src/command/processes.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package",
|
|
3
3
|
"name": "@procedures/shell",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.0",
|
|
5
5
|
"description": "Safe command execution for TypeScript: shell.run with argv and no shell, shell.stream line streaming with backpressure, process-tree kill, timeouts and output caps.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"shell",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"vitest": "^5.0.3"
|
|
69
69
|
},
|
|
70
70
|
"dependencies": {
|
|
71
|
-
"@procedures/client": "0.
|
|
71
|
+
"@procedures/client": "0.5.0",
|
|
72
72
|
"zod": "^4.6.5"
|
|
73
73
|
},
|
|
74
74
|
"client": {
|
package/src/command/index.ts
CHANGED
|
@@ -15,9 +15,14 @@ export { streamCommand, type StreamCommandOptions, type CommandStreamItem } from
|
|
|
15
15
|
export {
|
|
16
16
|
ProcessRegistry,
|
|
17
17
|
processes,
|
|
18
|
+
FOLLOW_BUFFER_BYTES,
|
|
18
19
|
type ManagedProcessInfo,
|
|
19
20
|
type StartProcessOptions,
|
|
20
21
|
type StopResult,
|
|
22
|
+
type ProcessOutputChunk,
|
|
23
|
+
type ProcessOutputListener,
|
|
24
|
+
type ProcessFollowItem,
|
|
25
|
+
type FollowOptions,
|
|
21
26
|
} from "./processes.js";
|
|
22
27
|
export { killTree } from "./tree-kill.js";
|
|
23
28
|
export type { SpawnOptions } from "./spawn.js";
|
package/src/command/processes.ts
CHANGED
|
@@ -7,12 +7,14 @@
|
|
|
7
7
|
* - a record (id, pid, status, exit code), which `list` and `get` give;
|
|
8
8
|
* - a `stop` that kills the whole process tree;
|
|
9
9
|
* - the end of its output (the last 64 KiB), which also keeps its pipes from filling;
|
|
10
|
+
* - the output after a time, for a follower: `subscribe` and `follow`;
|
|
10
11
|
* - an end with the host: the host does not wait for the process, and kills it when it ends.
|
|
11
12
|
*
|
|
12
13
|
* The registry keeps the records of ended processes, up to `keepExited` of them.
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
16
|
import type { ChildProcess } from "node:child_process";
|
|
17
|
+
import { StringDecoder } from "node:string_decoder";
|
|
16
18
|
import { killTree } from "./tree-kill.js";
|
|
17
19
|
import { OutputCollector } from "./output.js";
|
|
18
20
|
import { startChild, startError, type SpawnOptions } from "./spawn.js";
|
|
@@ -48,6 +50,50 @@ export interface StopResult {
|
|
|
48
50
|
signal?: string;
|
|
49
51
|
}
|
|
50
52
|
|
|
53
|
+
/** One piece of the output of a process, as `subscribe` gives it. */
|
|
54
|
+
export interface ProcessOutputChunk {
|
|
55
|
+
/** The stream of the piece. */
|
|
56
|
+
stream: "stdout" | "stderr";
|
|
57
|
+
/** The bytes of the piece. A piece can end in a line or in a UTF-8 character. */
|
|
58
|
+
data: Buffer;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The functions that `subscribe` calls for one subscriber. */
|
|
62
|
+
export interface ProcessOutputListener {
|
|
63
|
+
/** This function gets each piece of output that the process writes after the subscription. */
|
|
64
|
+
output?(chunk: ProcessOutputChunk): void;
|
|
65
|
+
/** This function gets a copy of the record when the process ends. After it, no call comes. */
|
|
66
|
+
end?(info: ManagedProcessInfo): void;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** One item of `follow`. */
|
|
70
|
+
export type ProcessFollowItem =
|
|
71
|
+
/** The output that the registry kept before the subscription: the first item with `backlog`. */
|
|
72
|
+
| { type: "backlog"; text: string }
|
|
73
|
+
/** A piece of new output, decoded as UTF-8. A piece can end in the middle of a line. */
|
|
74
|
+
| { type: "output"; stream: "stdout" | "stderr"; text: string }
|
|
75
|
+
/** The reader was slow, so the follower dropped the oldest pieces: this many bytes. */
|
|
76
|
+
| { type: "dropped"; bytes: number }
|
|
77
|
+
/** The end of the process: the last item. */
|
|
78
|
+
| { type: "end"; info: ManagedProcessInfo };
|
|
79
|
+
|
|
80
|
+
/** The options of `follow`. */
|
|
81
|
+
export interface FollowOptions {
|
|
82
|
+
/** True: the first item is the output that the registry kept before the subscription. */
|
|
83
|
+
backlog?: boolean | undefined;
|
|
84
|
+
/** An abort ends the stream. The process continues. */
|
|
85
|
+
signal?: AbortSignal | undefined;
|
|
86
|
+
/**
|
|
87
|
+
* The most bytes of output that wait for the reader. Above it, the follower drops the oldest
|
|
88
|
+
* pieces and gives a `dropped` item, because the registry cannot slow the process.
|
|
89
|
+
* @default 1 MiB
|
|
90
|
+
*/
|
|
91
|
+
maxBufferedBytes?: number | undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** The default of `maxBufferedBytes` of `follow`. */
|
|
95
|
+
export const FOLLOW_BUFFER_BYTES: number = 1024 * 1024;
|
|
96
|
+
|
|
51
97
|
/** The end of the output that the registry keeps for each process. */
|
|
52
98
|
const OUTPUT_TAIL_BYTES = 64 * 1024;
|
|
53
99
|
/** How long `stop` waits after the first kill, before it kills again with SIGKILL. */
|
|
@@ -67,9 +113,20 @@ interface Entry {
|
|
|
67
113
|
output: OutputCollector;
|
|
68
114
|
/** Called after each output chunk and at the end. */
|
|
69
115
|
watchers: Set<() => void>;
|
|
116
|
+
/** The subscribers of `subscribe`. The end of the process removes them. */
|
|
117
|
+
listeners: Set<ProcessOutputListener>;
|
|
70
118
|
done: Promise<void>;
|
|
71
119
|
}
|
|
72
120
|
|
|
121
|
+
/** This function calls a function of a subscriber. An error of a subscriber does not reach the registry. */
|
|
122
|
+
function notify(run: () => void): void {
|
|
123
|
+
try {
|
|
124
|
+
run();
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error("A subscriber of the process registry failed:", error);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
73
130
|
export class ProcessRegistry {
|
|
74
131
|
private readonly entries = new Map<string, Entry>();
|
|
75
132
|
private readonly keepExited: number;
|
|
@@ -97,17 +154,17 @@ export class ProcessRegistry {
|
|
|
97
154
|
|
|
98
155
|
const output = new OutputCollector(OUTPUT_TAIL_BYTES, "tail");
|
|
99
156
|
const watchers = new Set<() => void>();
|
|
157
|
+
const listeners = new Set<ProcessOutputListener>();
|
|
100
158
|
const changed = (): void => {
|
|
101
159
|
for (const watcher of [...watchers]) watcher();
|
|
102
160
|
};
|
|
103
|
-
|
|
104
|
-
output.add(chunk);
|
|
105
|
-
changed();
|
|
106
|
-
});
|
|
107
|
-
child.stderr!.on("data", (chunk: Buffer) => {
|
|
161
|
+
const received = (stream: "stdout" | "stderr", chunk: Buffer): void => {
|
|
108
162
|
output.add(chunk);
|
|
163
|
+
for (const listener of [...listeners]) notify(() => listener.output?.({ stream, data: chunk }));
|
|
109
164
|
changed();
|
|
110
|
-
}
|
|
165
|
+
};
|
|
166
|
+
child.stdout!.on("data", (chunk: Buffer) => received("stdout", chunk));
|
|
167
|
+
child.stderr!.on("data", (chunk: Buffer) => received("stderr", chunk));
|
|
111
168
|
|
|
112
169
|
const done = new Promise<void>((resolve) => {
|
|
113
170
|
let finished = false;
|
|
@@ -117,6 +174,10 @@ export class ProcessRegistry {
|
|
|
117
174
|
info.exitedAt = new Date().toISOString();
|
|
118
175
|
resolve();
|
|
119
176
|
changed();
|
|
177
|
+
// The end is the last call to a subscriber: the registry then forgets each subscriber
|
|
178
|
+
const ending = [...listeners];
|
|
179
|
+
listeners.clear();
|
|
180
|
+
for (const listener of ending) notify(() => listener.end?.({ ...info, args: [...info.args] }));
|
|
120
181
|
this.reap();
|
|
121
182
|
};
|
|
122
183
|
child.on("error", (error) => {
|
|
@@ -140,7 +201,7 @@ export class ProcessRegistry {
|
|
|
140
201
|
(stream as unknown as { unref?: () => void } | null)?.unref?.();
|
|
141
202
|
}
|
|
142
203
|
|
|
143
|
-
this.entries.set(id, { info, child, output, watchers, done });
|
|
204
|
+
this.entries.set(id, { info, child, output, watchers, listeners, done });
|
|
144
205
|
this.reap();
|
|
145
206
|
return { ...info };
|
|
146
207
|
}
|
|
@@ -166,6 +227,105 @@ export class ProcessRegistry {
|
|
|
166
227
|
return this.entries.get(id)?.output.text() ?? "";
|
|
167
228
|
}
|
|
168
229
|
|
|
230
|
+
/**
|
|
231
|
+
* This function subscribes to the output of a process. `listener.output` gets each piece of
|
|
232
|
+
* output after this call, with its stream. `listener.end` gets the record at the end of the
|
|
233
|
+
* process, and then the registry forgets the subscriber. For a process that ended already,
|
|
234
|
+
* `listener.end` runs at once. The result removes the subscriber. It is undefined when the
|
|
235
|
+
* registry has no process of this id.
|
|
236
|
+
*/
|
|
237
|
+
subscribe(id: string, listener: ProcessOutputListener): (() => void) | undefined {
|
|
238
|
+
const entry = this.entries.get(id);
|
|
239
|
+
if (!entry) return undefined;
|
|
240
|
+
if (entry.info.exitedAt) {
|
|
241
|
+
notify(() => listener.end?.({ ...entry.info, args: [...entry.info.args] }));
|
|
242
|
+
return () => undefined;
|
|
243
|
+
}
|
|
244
|
+
entry.listeners.add(listener);
|
|
245
|
+
return () => {
|
|
246
|
+
entry.listeners.delete(listener);
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* This function gives the output of a process as a stream, until the process ends or the
|
|
252
|
+
* signal aborts. The subscription starts when the reader asks for the first item, and the
|
|
253
|
+
* end of the stream removes it: an end of the process, an abort, or a reader that stops. With
|
|
254
|
+
* `backlog`, the first item is the output that the registry kept until then, without a gap.
|
|
255
|
+
* The last item of an ended process is `end`. An unknown id throws at the first item.
|
|
256
|
+
*/
|
|
257
|
+
async *follow(id: string, options: FollowOptions = {}): AsyncGenerator<ProcessFollowItem, void, undefined> {
|
|
258
|
+
const limit = Math.max(1, options.maxBufferedBytes ?? FOLLOW_BUFFER_BYTES);
|
|
259
|
+
const signal = options.signal;
|
|
260
|
+
const queue: Array<{ item: ProcessFollowItem; bytes: number }> = [];
|
|
261
|
+
let queued = 0;
|
|
262
|
+
let dropped = 0;
|
|
263
|
+
let ended: ManagedProcessInfo | undefined;
|
|
264
|
+
let wake: (() => void) | undefined;
|
|
265
|
+
const wakeUp = (): void => {
|
|
266
|
+
const resolve = wake;
|
|
267
|
+
wake = undefined;
|
|
268
|
+
resolve?.();
|
|
269
|
+
};
|
|
270
|
+
const decoders = { stdout: new StringDecoder("utf8"), stderr: new StringDecoder("utf8") };
|
|
271
|
+
const add = (stream: "stdout" | "stderr", text: string, bytes: number): void => {
|
|
272
|
+
if (text === "") return;
|
|
273
|
+
queue.push({ item: { type: "output", stream, text }, bytes });
|
|
274
|
+
queued += bytes;
|
|
275
|
+
// The registry cannot slow the process: above the limit, the oldest pieces go
|
|
276
|
+
while (queued > limit && queue.length > 1) {
|
|
277
|
+
const oldest = queue.shift()!;
|
|
278
|
+
queued -= oldest.bytes;
|
|
279
|
+
dropped += oldest.bytes;
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
if (signal?.aborted) return;
|
|
284
|
+
const backlog = options.backlog ? this.output(id) : "";
|
|
285
|
+
const unsubscribe = this.subscribe(id, {
|
|
286
|
+
output: ({ stream, data }) => {
|
|
287
|
+
add(stream, decoders[stream].write(data), data.length);
|
|
288
|
+
wakeUp();
|
|
289
|
+
},
|
|
290
|
+
end: (info) => {
|
|
291
|
+
for (const stream of ["stdout", "stderr"] as const) {
|
|
292
|
+
const rest = decoders[stream].end();
|
|
293
|
+
add(stream, rest, Buffer.byteLength(rest));
|
|
294
|
+
}
|
|
295
|
+
ended = info;
|
|
296
|
+
wakeUp();
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
if (!unsubscribe) throw new Error(`No process ${id}`);
|
|
300
|
+
signal?.addEventListener("abort", wakeUp, { once: true });
|
|
301
|
+
try {
|
|
302
|
+
if (options.backlog) yield { type: "backlog", text: backlog };
|
|
303
|
+
for (;;) {
|
|
304
|
+
if (signal?.aborted) return;
|
|
305
|
+
if (dropped > 0) {
|
|
306
|
+
const bytes = dropped;
|
|
307
|
+
dropped = 0;
|
|
308
|
+
yield { type: "dropped", bytes };
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
const next = queue.shift();
|
|
312
|
+
if (next) {
|
|
313
|
+
queued -= next.bytes;
|
|
314
|
+
yield next.item;
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
if (ended) {
|
|
318
|
+
yield { type: "end", info: ended };
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
await new Promise<void>((resolve) => (wake = resolve));
|
|
322
|
+
}
|
|
323
|
+
} finally {
|
|
324
|
+
unsubscribe();
|
|
325
|
+
signal?.removeEventListener("abort", wakeUp);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
169
329
|
/** This function waits until the process ends, then gives its record. */
|
|
170
330
|
async exited(id: string): Promise<ManagedProcessInfo | undefined> {
|
|
171
331
|
const entry = this.entries.get(id);
|