@procedures/shell 0.0.0-stage → 0.2.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.
Files changed (85) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/LICENSE +21 -0
  3. package/README.md +421 -2
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/command/index.d.ts +18 -0
  6. package/dist/command/index.d.ts.map +1 -0
  7. package/dist/command/index.js +17 -0
  8. package/dist/command/index.js.map +1 -0
  9. package/dist/command/output.d.ts +22 -0
  10. package/dist/command/output.d.ts.map +1 -0
  11. package/dist/command/output.js +67 -0
  12. package/dist/command/output.js.map +1 -0
  13. package/dist/command/processes.d.ts +89 -0
  14. package/dist/command/processes.d.ts.map +1 -0
  15. package/dist/command/processes.js +223 -0
  16. package/dist/command/processes.js.map +1 -0
  17. package/dist/command/run.d.ts +48 -0
  18. package/dist/command/run.d.ts.map +1 -0
  19. package/dist/command/run.js +89 -0
  20. package/dist/command/run.js.map +1 -0
  21. package/dist/command/spawn.d.ts +26 -0
  22. package/dist/command/spawn.d.ts.map +1 -0
  23. package/dist/command/spawn.js +28 -0
  24. package/dist/command/spawn.js.map +1 -0
  25. package/dist/command/stream.d.ts +42 -0
  26. package/dist/command/stream.d.ts.map +1 -0
  27. package/dist/command/stream.js +158 -0
  28. package/dist/command/stream.js.map +1 -0
  29. package/dist/command/tree-kill.d.ts +26 -0
  30. package/dist/command/tree-kill.d.ts.map +1 -0
  31. package/dist/command/tree-kill.js +122 -0
  32. package/dist/command/tree-kill.js.map +1 -0
  33. package/dist/index.d.ts +31 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +33 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/module.d.ts +19 -0
  38. package/dist/module.d.ts.map +1 -0
  39. package/dist/module.js +98 -0
  40. package/dist/module.js.map +1 -0
  41. package/dist/procedures/shell/exec.d.ts +21 -0
  42. package/dist/procedures/shell/exec.d.ts.map +1 -0
  43. package/dist/procedures/shell/exec.js +45 -0
  44. package/dist/procedures/shell/exec.js.map +1 -0
  45. package/dist/procedures/shell/index.d.ts +5 -0
  46. package/dist/procedures/shell/index.d.ts.map +1 -0
  47. package/dist/procedures/shell/index.js +5 -0
  48. package/dist/procedures/shell/index.js.map +1 -0
  49. package/dist/procedures/shell/run.d.ts +14 -0
  50. package/dist/procedures/shell/run.d.ts.map +1 -0
  51. package/dist/procedures/shell/run.js +40 -0
  52. package/dist/procedures/shell/run.js.map +1 -0
  53. package/dist/procedures/shell/stream.d.ts +20 -0
  54. package/dist/procedures/shell/stream.d.ts.map +1 -0
  55. package/dist/procedures/shell/stream.js +39 -0
  56. package/dist/procedures/shell/stream.js.map +1 -0
  57. package/dist/procedures/shell/which.d.ts +10 -0
  58. package/dist/procedures/shell/which.d.ts.map +1 -0
  59. package/dist/procedures/shell/which.js +21 -0
  60. package/dist/procedures/shell/which.js.map +1 -0
  61. package/dist/register.d.ts +7 -0
  62. package/dist/register.d.ts.map +1 -0
  63. package/dist/register.js +9 -0
  64. package/dist/register.js.map +1 -0
  65. package/dist/types.d.ts +79 -0
  66. package/dist/types.d.ts.map +1 -0
  67. package/dist/types.js +89 -0
  68. package/dist/types.js.map +1 -0
  69. package/package.json +81 -3
  70. package/src/command/index.ts +24 -0
  71. package/src/command/output.ts +67 -0
  72. package/src/command/processes.ts +271 -0
  73. package/src/command/run.ts +127 -0
  74. package/src/command/spawn.ts +44 -0
  75. package/src/command/stream.ts +186 -0
  76. package/src/command/tree-kill.ts +121 -0
  77. package/src/index.ts +53 -0
  78. package/src/module.ts +123 -0
  79. package/src/procedures/shell/exec.ts +55 -0
  80. package/src/procedures/shell/index.ts +4 -0
  81. package/src/procedures/shell/run.ts +47 -0
  82. package/src/procedures/shell/stream.ts +43 -0
  83. package/src/procedures/shell/which.ts +26 -0
  84. package/src/register.ts +11 -0
  85. package/src/types.ts +172 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processes.d.ts","sourceRoot":"","sources":["../../src/command/processes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,EAA0B,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAEvE,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,wFAAwF;IACxF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yBAAyB;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAoB,SAAQ,YAAY;IACvD,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,UAAU;IACzB,mDAAmD;IACnD,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAUD,mEAAmE;AACnE,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAWD,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,OAAO,CAAK;IAEpB,YAAY,OAAO,GAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAO,EAEhD;IAED,oFAAoF;IACpF,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,kBAAkB,CA+D5E;IAED,6DAA6D;IAC7D,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAG9C;IAED,+DAA+D;IAC/D,IAAI,CAAC,MAAM,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAO,GAAG,kBAAkB,EAAE,CAOtE;IAED,2FAA2F;IAC3F,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAEzB;IAED,yEAAyE;IACnE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAKhE;IAED;;;;;OAKG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,SAAS,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA0BhF;IAED;;;OAGG;IACG,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,OAAmB,GAAG,OAAO,CAAC,UAAU,CAAC,CAW9E;IAED,mFAAmF;IAC7E,OAAO,CAAC,MAAM,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAI1E;IAED,kGAAkG;IAClG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAI1B;IAED,qFAAqF;IACrF,IAAI,IAAI,MAAM,CAKb;IAED,OAAO,CAAC,UAAU;YAOJ,UAAU;CAWzB;AAED,uFAAuF;AACvF,eAAO,MAAM,SAAS,EAAE,eAAuC,CAAC"}
@@ -0,0 +1,223 @@
1
+ /**
2
+ * The registry of long-running processes (deep dive WRP-4, WRP-10).
3
+ *
4
+ * Some procedures start a process and return while it runs: `vitest.watch`, `node.spawn`,
5
+ * `vite.dev`. Before, they started it detached and forgot it, so nothing could stop it, and it
6
+ * kept running after the host ended. The registry gives each process:
7
+ * - a record (id, pid, status, exit code), which `list` and `get` give;
8
+ * - a `stop` that kills the whole process tree;
9
+ * - the end of its output (the last 64 KiB), which also keeps its pipes from filling;
10
+ * - an end with the host: the host does not wait for the process, and kills it when it ends.
11
+ *
12
+ * The registry keeps the records of ended processes, up to `keepExited` of them.
13
+ */
14
+ import { killTree } from "./tree-kill.js";
15
+ import { OutputCollector } from "./output.js";
16
+ import { startChild, startError } from "./spawn.js";
17
+ /** The end of the output that the registry keeps for each process. */
18
+ const OUTPUT_TAIL_BYTES = 64 * 1024;
19
+ /** How long `stop` waits after the first kill, before it kills again with SIGKILL. */
20
+ const STOP_GRACE_MS = 5000;
21
+ // The CSI and OSC escape sequences of terminals (colors, styles, links)
22
+ const ANSI = /\x1b\[[0-?]*[ -/]*[@-~]|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g;
23
+ /** This function removes the ANSI escape sequences from a text. */
24
+ export function stripAnsi(text) {
25
+ return text.replace(ANSI, "");
26
+ }
27
+ export class ProcessRegistry {
28
+ entries = new Map();
29
+ keepExited;
30
+ counter = 0;
31
+ constructor(options = {}) {
32
+ this.keepExited = options.keepExited ?? 50;
33
+ }
34
+ /** This function starts a program and records it. The program inherits no stdin. */
35
+ start(command, options = {}) {
36
+ const child = startChild(command, options, "ignore");
37
+ const id = `${options.group ?? "process"}-${Date.now().toString(36)}-${++this.counter}`;
38
+ const info = {
39
+ id,
40
+ pid: child.pid ?? 0,
41
+ command,
42
+ args: [...(options.args ?? [])],
43
+ status: "running",
44
+ startedAt: new Date().toISOString(),
45
+ };
46
+ if (options.cwd !== undefined)
47
+ info.cwd = options.cwd;
48
+ if (options.group !== undefined)
49
+ info.group = options.group;
50
+ if (options.label !== undefined)
51
+ info.label = options.label;
52
+ const output = new OutputCollector(OUTPUT_TAIL_BYTES, "tail");
53
+ const watchers = new Set();
54
+ const changed = () => {
55
+ for (const watcher of [...watchers])
56
+ watcher();
57
+ };
58
+ child.stdout.on("data", (chunk) => {
59
+ output.add(chunk);
60
+ changed();
61
+ });
62
+ child.stderr.on("data", (chunk) => {
63
+ output.add(chunk);
64
+ changed();
65
+ });
66
+ const done = new Promise((resolve) => {
67
+ let finished = false;
68
+ const finish = () => {
69
+ if (finished)
70
+ return;
71
+ finished = true;
72
+ info.exitedAt = new Date().toISOString();
73
+ resolve();
74
+ changed();
75
+ this.reap();
76
+ };
77
+ child.on("error", (error) => {
78
+ info.error = startError(error);
79
+ if (child.pid === undefined) {
80
+ info.status = "error";
81
+ finish();
82
+ }
83
+ });
84
+ child.on("close", (code, signal) => {
85
+ info.status = info.error ? "error" : "exited";
86
+ if (code !== null)
87
+ info.exitCode = code;
88
+ if (signal)
89
+ info.signal = signal;
90
+ finish();
91
+ });
92
+ });
93
+ // The host does not wait for the process: it ends when the host ends (see tree-kill.ts)
94
+ child.unref();
95
+ for (const stream of [child.stdout, child.stderr]) {
96
+ stream?.unref?.();
97
+ }
98
+ this.entries.set(id, { info, child, output, watchers, done });
99
+ this.reap();
100
+ return { ...info };
101
+ }
102
+ /** This function gives a copy of the record of a process. */
103
+ get(id) {
104
+ const entry = this.entries.get(id);
105
+ return entry ? { ...entry.info } : undefined;
106
+ }
107
+ /** This function gives copies of the records, oldest first. */
108
+ list(filter = {}) {
109
+ const result = [];
110
+ for (const entry of this.entries.values()) {
111
+ if (filter.group !== undefined && entry.info.group !== filter.group)
112
+ continue;
113
+ result.push({ ...entry.info });
114
+ }
115
+ return result;
116
+ }
117
+ /** This function gives the end of the output of a process (stdout and stderr together). */
118
+ output(id) {
119
+ return this.entries.get(id)?.output.text() ?? "";
120
+ }
121
+ /** This function waits until the process ends, then gives its record. */
122
+ async exited(id) {
123
+ const entry = this.entries.get(id);
124
+ if (!entry)
125
+ return undefined;
126
+ await entry.done;
127
+ return { ...entry.info };
128
+ }
129
+ /**
130
+ * This function waits until the output of the process matches `pattern`, and gives the match.
131
+ * The pattern sees the output without ANSI escape codes: a program can color its output when
132
+ * the environment has `FORCE_COLOR`. It rejects when the process ends first, or after
133
+ * `timeout` milliseconds.
134
+ */
135
+ waitFor(id, pattern, timeout = 30_000) {
136
+ const entry = this.entries.get(id);
137
+ if (!entry)
138
+ return Promise.reject(new Error(`No process ${id}`));
139
+ return new Promise((resolve, reject) => {
140
+ const check = () => {
141
+ const match = stripAnsi(entry.output.text()).match(pattern);
142
+ if (match) {
143
+ stop();
144
+ resolve(match);
145
+ }
146
+ else if (entry.info.status !== "running" || entry.info.exitedAt) {
147
+ stop();
148
+ const code = entry.info.exitCode ?? entry.info.signal ?? entry.info.error;
149
+ reject(new Error(`Process ${id} exited (${code}) before its output matched ${pattern}`));
150
+ }
151
+ };
152
+ const timer = setTimeout(() => {
153
+ stop();
154
+ reject(new Error(`Timeout after ${timeout} ms: the output of process ${id} did not match ${pattern}`));
155
+ }, timeout);
156
+ const stop = () => {
157
+ clearTimeout(timer);
158
+ entry.watchers.delete(check);
159
+ };
160
+ entry.watchers.add(check);
161
+ check();
162
+ });
163
+ }
164
+ /**
165
+ * This function kills the process tree and waits for the end. When the process does not end
166
+ * within the grace time, it kills again with SIGKILL.
167
+ */
168
+ async stop(id, signal = "SIGTERM") {
169
+ const entry = this.entries.get(id);
170
+ if (!entry || entry.info.exitedAt) {
171
+ return entry ? this.stopResult(entry, false) : { stopped: false };
172
+ }
173
+ killTree(entry.child, signal);
174
+ if (!(await this.endsWithin(entry, STOP_GRACE_MS))) {
175
+ killTree(entry.child, "SIGKILL");
176
+ await this.endsWithin(entry, STOP_GRACE_MS);
177
+ }
178
+ return this.stopResult(entry, true);
179
+ }
180
+ /** This function stops every running process of the registry (or of one group). */
181
+ async stopAll(filter = {}) {
182
+ const running = this.list(filter).filter((info) => info.status === "running");
183
+ await Promise.all(running.map((info) => this.stop(info.id)));
184
+ return running.length;
185
+ }
186
+ /** This function removes the record of an ended process. It keeps the record of a running one. */
187
+ forget(id) {
188
+ const entry = this.entries.get(id);
189
+ if (!entry || !entry.info.exitedAt)
190
+ return false;
191
+ return this.entries.delete(id);
192
+ }
193
+ /** This function removes the oldest records of ended processes past `keepExited`. */
194
+ reap() {
195
+ const ended = [...this.entries.values()].filter((entry) => entry.info.exitedAt);
196
+ const excess = ended.length - this.keepExited;
197
+ for (let i = 0; i < excess; i++)
198
+ this.entries.delete(ended[i].info.id);
199
+ return Math.max(0, excess);
200
+ }
201
+ stopResult(entry, stopped) {
202
+ const result = { stopped };
203
+ if (entry.info.exitCode !== undefined)
204
+ result.exitCode = entry.info.exitCode;
205
+ if (entry.info.signal !== undefined)
206
+ result.signal = entry.info.signal;
207
+ return result;
208
+ }
209
+ async endsWithin(entry, ms) {
210
+ let timer;
211
+ const ended = await Promise.race([
212
+ entry.done.then(() => true),
213
+ new Promise((resolve) => {
214
+ timer = setTimeout(() => resolve(false), ms);
215
+ }),
216
+ ]);
217
+ clearTimeout(timer);
218
+ return ended;
219
+ }
220
+ }
221
+ /** The registry of the host process. The procedures of the wrapper packages use it. */
222
+ export const processes = new ProcessRegistry();
223
+ //# sourceMappingURL=processes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processes.js","sourceRoot":"","sources":["../../src/command/processes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * runCommand: run a program to its end and give its output (roadmap 2.2).
3
+ *
4
+ * This is the one way the wrapper packages run a program. Each wrapper had its own way: shell
5
+ * strings (`docker`, `pnpm`), synchronous calls that blocked the event loop and ignored the
6
+ * signal (`git`, `cue`), and `spawn` calls that killed only the direct child.
7
+ */
8
+ import { type SpawnOptions } from "./spawn.js";
9
+ export interface CommandOptions extends SpawnOptions {
10
+ /** Text or bytes to write to stdin. Without it, stdin is empty (the program reads its end at once). */
11
+ input?: string | Buffer | undefined;
12
+ /** After this many milliseconds, the process tree is killed. */
13
+ timeout?: number | undefined;
14
+ /** When the signal aborts, the process tree is killed. An aborted signal starts nothing. */
15
+ signal?: AbortSignal | undefined;
16
+ /** The most bytes kept of each output stream (default: 64 MiB). */
17
+ maxOutputBytes?: number | undefined;
18
+ /** Past the limit, keep the first bytes ("head", the default) or the last bytes ("tail"). */
19
+ keep?: "head" | "tail" | undefined;
20
+ /** The encoding of the output (default: utf8). */
21
+ encoding?: BufferEncoding | undefined;
22
+ }
23
+ export interface CommandResult {
24
+ /** The exit code. It is 1 when the process ended on a signal or did not start. */
25
+ exitCode: number;
26
+ stdout: string;
27
+ stderr: string;
28
+ /** True when the exit code is 0. */
29
+ success: boolean;
30
+ /** The time from the start to the end, in milliseconds. */
31
+ duration: number;
32
+ /** The signal that ended the process. When the helper killed the tree, it is "SIGTERM". */
33
+ signal?: string;
34
+ /** True when the timeout killed the process. */
35
+ timedOut?: boolean;
36
+ /** True when the signal aborted the command. */
37
+ aborted?: boolean;
38
+ /** Present when an output stream was longer than `maxOutputBytes`: which one lost bytes. */
39
+ truncated?: {
40
+ stdout: boolean;
41
+ stderr: boolean;
42
+ };
43
+ /** The error that kept the program from starting, for example `spawn x ENOENT`. */
44
+ error?: string;
45
+ }
46
+ /** This function runs `command` with `options.args`. It does not throw: a failure is in the result. */
47
+ export declare function runCommand(command: string, options?: CommandOptions): Promise<CommandResult>;
48
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/command/run.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAA0B,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAEvE,MAAM,WAAW,cAAe,SAAQ,YAAY;IAClD,uGAAuG;IACvG,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACpC,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,4FAA4F;IAC5F,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IACjC,mEAAmE;IACnE,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,6FAA6F;IAC7F,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACnC,kDAAkD;IAClD,QAAQ,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;CACvC;AAED,MAAM,WAAW,aAAa;IAC5B,kFAAkF;IAClF,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,2FAA2F;IAC3F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gDAAgD;IAChD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,4FAA4F;IAC5F,SAAS,CAAC,EAAE;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC;IACjD,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,uGAAuG;AACvG,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,CA6EhG"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * runCommand: run a program to its end and give its output (roadmap 2.2).
3
+ *
4
+ * This is the one way the wrapper packages run a program. Each wrapper had its own way: shell
5
+ * strings (`docker`, `pnpm`), synchronous calls that blocked the event loop and ignored the
6
+ * signal (`git`, `cue`), and `spawn` calls that killed only the direct child.
7
+ */
8
+ import { killTree } from "./tree-kill.js";
9
+ import { DEFAULT_MAX_OUTPUT_BYTES, OutputCollector } from "./output.js";
10
+ import { startChild, startError } from "./spawn.js";
11
+ /** This function runs `command` with `options.args`. It does not throw: a failure is in the result. */
12
+ export function runCommand(command, options = {}) {
13
+ const started = Date.now();
14
+ if (options.signal?.aborted) {
15
+ return Promise.resolve({ exitCode: 1, stdout: "", stderr: "", success: false, duration: 0, aborted: true });
16
+ }
17
+ const encoding = options.encoding ?? "utf8";
18
+ const limit = options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
19
+ const stdout = new OutputCollector(limit, options.keep);
20
+ const stderr = new OutputCollector(limit, options.keep);
21
+ return new Promise((resolve) => {
22
+ const child = startChild(command, options, options.input !== undefined ? "pipe" : "ignore");
23
+ let timedOut = false;
24
+ let aborted = false;
25
+ let settled = false;
26
+ const timer = options.timeout !== undefined
27
+ ? setTimeout(() => {
28
+ timedOut = true;
29
+ killTree(child);
30
+ }, options.timeout)
31
+ : undefined;
32
+ const onAbort = () => {
33
+ aborted = true;
34
+ killTree(child);
35
+ };
36
+ options.signal?.addEventListener("abort", onAbort, { once: true });
37
+ const finish = (result) => {
38
+ if (settled)
39
+ return;
40
+ settled = true;
41
+ if (timer)
42
+ clearTimeout(timer);
43
+ options.signal?.removeEventListener("abort", onAbort);
44
+ resolve(result);
45
+ };
46
+ child.stdout.on("data", (chunk) => stdout.add(chunk));
47
+ child.stderr.on("data", (chunk) => stderr.add(chunk));
48
+ if (options.input !== undefined) {
49
+ // A program that ends before it reads its input closes the pipe: that is not an error here
50
+ child.stdin.on("error", () => { });
51
+ child.stdin.end(options.input);
52
+ }
53
+ child.on("error", (error) => {
54
+ // The program did not start: no "close" follows
55
+ if (child.pid === undefined) {
56
+ finish({
57
+ exitCode: 1,
58
+ stdout: "",
59
+ stderr: startError(error),
60
+ success: false,
61
+ duration: Date.now() - started,
62
+ error: startError(error),
63
+ });
64
+ }
65
+ });
66
+ child.on("close", (code, signal) => {
67
+ const result = {
68
+ exitCode: code ?? 1,
69
+ stdout: stdout.text(encoding),
70
+ stderr: stderr.text(encoding),
71
+ success: code === 0 && !timedOut && !aborted,
72
+ duration: Date.now() - started,
73
+ };
74
+ // On Windows, taskkill ends the process with an exit code and no signal
75
+ const endSignal = signal ?? (timedOut || aborted ? "SIGTERM" : undefined);
76
+ if (endSignal)
77
+ result.signal = endSignal;
78
+ if (timedOut)
79
+ result.timedOut = true;
80
+ if (aborted)
81
+ result.aborted = true;
82
+ if (stdout.truncated || stderr.truncated) {
83
+ result.truncated = { stdout: stdout.truncated, stderr: stderr.truncated };
84
+ }
85
+ finish(result);
86
+ });
87
+ });
88
+ }
89
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../src/command/run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Start a child process the one way the helpers allow: an argument list, no shell, no window.
3
+ */
4
+ import { type ChildProcess } from "node:child_process";
5
+ export interface SpawnOptions {
6
+ /** The arguments. Each one goes to the program as it is: no shell reads it. */
7
+ args?: readonly string[] | undefined;
8
+ /** The working directory (default: the current directory of the host). */
9
+ cwd?: string | undefined;
10
+ /** Environment variables to add to the environment of the host. */
11
+ env?: Record<string, string | undefined> | undefined;
12
+ /**
13
+ * On Windows, pass the arguments without quotes. Only `shell.exec` sets it, for the command
14
+ * line of `cmd.exe`.
15
+ */
16
+ windowsVerbatimArguments?: boolean | undefined;
17
+ }
18
+ /**
19
+ * This function starts `command` with `shell: false` and `windowsHide`. On POSIX the child gets
20
+ * its own process group, so `killTree` reaches its descendants. The child is recorded until it
21
+ * ends, so the end of the host kills it.
22
+ */
23
+ export declare function startChild(command: string, options: SpawnOptions, stdin: "pipe" | "ignore"): ChildProcess;
24
+ /** The text of an error that kept a program from starting, for example `spawn x ENOENT`. */
25
+ export declare function startError(error: unknown): string;
26
+ //# sourceMappingURL=spawn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn.d.ts","sourceRoot":"","sources":["../../src/command/spawn.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAS,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAG9D,MAAM,WAAW,YAAY;IAC3B,+EAA+E;IAC/E,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACrC,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,mEAAmE;IACnE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;IACrD;;;OAGG;IACH,wBAAwB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAChD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,YAAY,CAYzG;AAED,4FAA4F;AAC5F,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEjD"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Start a child process the one way the helpers allow: an argument list, no shell, no window.
3
+ */
4
+ import { spawn } from "node:child_process";
5
+ import { OWN_PROCESS_GROUP, track } from "./tree-kill.js";
6
+ /**
7
+ * This function starts `command` with `shell: false` and `windowsHide`. On POSIX the child gets
8
+ * its own process group, so `killTree` reaches its descendants. The child is recorded until it
9
+ * ends, so the end of the host kills it.
10
+ */
11
+ export function startChild(command, options, stdin) {
12
+ const child = spawn(command, [...(options.args ?? [])], {
13
+ cwd: options.cwd,
14
+ env: options.env ? { ...process.env, ...options.env } : process.env,
15
+ shell: false,
16
+ windowsHide: true,
17
+ detached: OWN_PROCESS_GROUP,
18
+ stdio: [stdin, "pipe", "pipe"],
19
+ ...(options.windowsVerbatimArguments ? { windowsVerbatimArguments: true } : {}),
20
+ });
21
+ track(child);
22
+ return child;
23
+ }
24
+ /** The text of an error that kept a program from starting, for example `spawn x ENOENT`. */
25
+ export function startError(error) {
26
+ return error instanceof Error ? error.message : String(error);
27
+ }
28
+ //# sourceMappingURL=spawn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn.js","sourceRoot":"","sources":["../../src/command/spawn.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAgB1D;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,OAAe,EAAE,OAAqB,EAAE,KAAwB;IACzF,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,EAAE;QACtD,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG;QACnE,KAAK,EAAE,KAAK;QACZ,WAAW,EAAE,IAAI;QACjB,QAAQ,EAAE,iBAAiB;QAC3B,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC;QAC9B,GAAG,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC,CAAC,EAAE,wBAAwB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChF,CAAC,CAAC;IACH,KAAK,CAAC,KAAK,CAAC,CAAC;IACb,OAAO,KAAK,CAAC;AACf,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * streamCommand: run a program and give each output line while it runs.
3
+ *
4
+ * The queue between the program and the reader has a limit (deep dive WRP-9). When the queue
5
+ * is full, the helper stops reading the pipes of the program. The pipes fill, and the program
6
+ * waits in its next write. When the reader takes items again, the helper reads again. Before,
7
+ * the queue had no limit: a program with a large output filled the memory of the host.
8
+ */
9
+ import { type SpawnOptions } from "./spawn.js";
10
+ /** One item of a stream: an output line, or the exit of the program (the last item). */
11
+ export type CommandStreamItem = {
12
+ type: "stdout" | "stderr";
13
+ line: string;
14
+ } | {
15
+ type: "exit";
16
+ exitCode: number;
17
+ duration: number;
18
+ signal?: string;
19
+ timedOut?: boolean;
20
+ aborted?: boolean;
21
+ };
22
+ export interface StreamCommandOptions extends SpawnOptions {
23
+ /** Text or bytes to write to stdin. Without it, stdin is empty. */
24
+ input?: string | Buffer | undefined;
25
+ /** After this many milliseconds, the process tree is killed. */
26
+ timeout?: number | undefined;
27
+ /** When the signal aborts, the process tree is killed. */
28
+ signal?: AbortSignal | undefined;
29
+ /** The most items that wait for the reader before the helper stops reading (default: 1000). */
30
+ highWaterMark?: number | undefined;
31
+ /** A longer line arrives in parts of this many characters (default: 1 MiB). */
32
+ maxLineLength?: number | undefined;
33
+ /** The encoding of the output (default: utf8). */
34
+ encoding?: BufferEncoding | undefined;
35
+ }
36
+ /**
37
+ * This function runs `command` and yields each line of stdout and stderr, then one exit item.
38
+ * When the reader stops early, or the signal aborts, the process tree is killed. A program that
39
+ * cannot start makes the generator throw.
40
+ */
41
+ export declare function streamCommand(command: string, options?: StreamCommandOptions): AsyncGenerator<CommandStreamItem, void, undefined>;
42
+ //# sourceMappingURL=stream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../src/command/stream.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAE3D,wFAAwF;AACxF,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC3C;IACE,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEN,MAAM,WAAW,oBAAqB,SAAQ,YAAY;IACxD,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACpC,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,0DAA0D;IAC1D,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IACjC,+FAA+F;IAC/F,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,+EAA+E;IAC/E,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,kDAAkD;IAClD,QAAQ,CAAC,EAAE,cAAc,GAAG,SAAS,CAAC;CACvC;AAKD;;;;GAIG;AACH,wBAAuB,aAAa,CAClC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,oBAAyB,GACjC,cAAc,CAAC,iBAAiB,EAAE,IAAI,EAAE,SAAS,CAAC,CAsIpD"}