node-or-bun 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 (52) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/LICENSE +21 -0
  3. package/README.md +110 -2
  4. package/dist/adapters/bun.d.ts +12 -0
  5. package/dist/adapters/bun.d.ts.map +1 -0
  6. package/dist/adapters/bun.js +263 -0
  7. package/dist/adapters/bun.js.map +1 -0
  8. package/dist/adapters/node.d.ts +11 -0
  9. package/dist/adapters/node.d.ts.map +1 -0
  10. package/dist/adapters/node.js +233 -0
  11. package/dist/adapters/node.js.map +1 -0
  12. package/dist/contract.d.ts +39 -0
  13. package/dist/contract.d.ts.map +1 -0
  14. package/dist/contract.js +4 -0
  15. package/dist/contract.js.map +1 -0
  16. package/dist/errors.d.ts +27 -0
  17. package/dist/errors.d.ts.map +1 -0
  18. package/dist/errors.js +25 -0
  19. package/dist/errors.js.map +1 -0
  20. package/dist/executor.d.ts +3 -0
  21. package/dist/executor.d.ts.map +1 -0
  22. package/dist/executor.js +281 -0
  23. package/dist/executor.js.map +1 -0
  24. package/dist/index.d.ts +6 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +4 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/internal/capture.d.ts +9 -0
  29. package/dist/internal/capture.d.ts.map +1 -0
  30. package/dist/internal/capture.js +58 -0
  31. package/dist/internal/capture.js.map +1 -0
  32. package/dist/internal/classification.d.ts +31 -0
  33. package/dist/internal/classification.d.ts.map +1 -0
  34. package/dist/internal/classification.js +61 -0
  35. package/dist/internal/classification.js.map +1 -0
  36. package/dist/internal/input.d.ts +9 -0
  37. package/dist/internal/input.d.ts.map +1 -0
  38. package/dist/internal/input.js +62 -0
  39. package/dist/internal/input.js.map +1 -0
  40. package/dist/internal/settlement.d.ts +15 -0
  41. package/dist/internal/settlement.d.ts.map +1 -0
  42. package/dist/internal/settlement.js +39 -0
  43. package/dist/internal/settlement.js.map +1 -0
  44. package/dist/internal/termination.d.ts +8 -0
  45. package/dist/internal/termination.d.ts.map +1 -0
  46. package/dist/internal/termination.js +22 -0
  47. package/dist/internal/termination.js.map +1 -0
  48. package/dist/testing/conformance.d.ts +12 -0
  49. package/dist/testing/conformance.d.ts.map +1 -0
  50. package/dist/testing/conformance.js +439 -0
  51. package/dist/testing/conformance.js.map +1 -0
  52. package/package.json +63 -4
@@ -0,0 +1,233 @@
1
+ import { spawn } from "node:child_process";
2
+ import { PROCESS_CLEANUP_STEP_MS } from "../contract.js";
3
+ import { ProcessRuntimeError, processError } from "../errors.js";
4
+ import { rejectedReasons, settleOperation, withCause, } from "../internal/settlement.js";
5
+ import { cleanupDirectChild } from "../internal/termination.js";
6
+ const streamOrThrow = (stream, name) => {
7
+ if (stream === null) {
8
+ throw processError("launch", `${name} capture stream was unavailable`);
9
+ }
10
+ return stream;
11
+ };
12
+ const observeExit = (child) => {
13
+ let terminal = false;
14
+ let terminalResolve;
15
+ const terminalProof = new Promise((resolve) => {
16
+ terminalResolve = resolve;
17
+ });
18
+ let settle;
19
+ let reject;
20
+ const exited = new Promise((resolve, rejectPromise) => {
21
+ settle = resolve;
22
+ reject = rejectPromise;
23
+ });
24
+ void exited.catch(() => undefined);
25
+ const onExit = (exitCode, signal) => {
26
+ if (terminal)
27
+ return;
28
+ terminal = true;
29
+ terminalResolve();
30
+ settle({ exitCode, signal: signal === null ? null : String(signal) });
31
+ };
32
+ const onError = (error) => {
33
+ // ChildProcess errors may be emitted while a live child remains. They are
34
+ // observation failures, not proof that the process reached a terminal state.
35
+ if (terminal)
36
+ return;
37
+ reject(processError("launch", "child process observation failed", error));
38
+ };
39
+ child.once("exit", onExit);
40
+ child.on("error", onError);
41
+ return {
42
+ exited,
43
+ isTerminal: () => terminal,
44
+ hasPid: () => child.pid !== undefined,
45
+ waitForTerminal: (timeoutMs) => {
46
+ let timer;
47
+ const timeout = new Promise((resolve) => {
48
+ timer = setTimeout(() => resolve(false), timeoutMs);
49
+ });
50
+ const settled = terminalProof.then(() => true);
51
+ return Promise.race([settled, timeout]).finally(() => {
52
+ if (timer !== undefined)
53
+ clearTimeout(timer);
54
+ });
55
+ },
56
+ };
57
+ };
58
+ const defaultCleanup = (child, cause, observation) => {
59
+ if (!observation.hasPid()) {
60
+ return Promise.reject(processError("cleanup", "child process was never spawned"));
61
+ }
62
+ return cleanupDirectChild({
63
+ cause,
64
+ isTerminal: observation.isTerminal,
65
+ waitForTerminal: observation.waitForTerminal,
66
+ terminate: (signal) => {
67
+ if (!child.kill(signal) && !observation.isTerminal()) {
68
+ throw processError("cleanup", `child process rejected ${signal}`);
69
+ }
70
+ },
71
+ });
72
+ };
73
+ const closeNodeCapture = async (child) => {
74
+ const streams = [child.stdout, child.stderr].filter((stream) => stream !== null);
75
+ await Promise.all(streams.map((stream) => new Promise((resolve, reject) => {
76
+ if (stream.closed) {
77
+ resolve();
78
+ return;
79
+ }
80
+ let settled = false;
81
+ const onClose = () => {
82
+ if (settled)
83
+ return;
84
+ settled = true;
85
+ resolve();
86
+ };
87
+ const onError = (error) => {
88
+ if (settled)
89
+ return;
90
+ settled = true;
91
+ reject(error);
92
+ };
93
+ stream.once("close", onClose);
94
+ // Keep the listener attached after the first failure so repeated
95
+ // native stream errors cannot become unhandled EventEmitter errors.
96
+ stream.on("error", onError);
97
+ stream.destroy();
98
+ })));
99
+ };
100
+ const closeNodeCaptureWithin = async (child) => {
101
+ const close = closeNodeCapture(child);
102
+ close.catch(() => undefined);
103
+ let timer;
104
+ const timeout = new Promise((_, reject) => {
105
+ timer = setTimeout(() => reject(processError("cleanup", "native capture closure timed out")), PROCESS_CLEANUP_STEP_MS);
106
+ });
107
+ try {
108
+ await Promise.race([close, timeout]);
109
+ }
110
+ finally {
111
+ if (timer !== undefined)
112
+ clearTimeout(timer);
113
+ }
114
+ };
115
+ const createRunning = (child, observation, cleanupHook) => {
116
+ const stdout = streamOrThrow(child.stdout, "stdout");
117
+ const stderr = streamOrThrow(child.stderr, "stderr");
118
+ let cleanupPromise;
119
+ let closePromise;
120
+ return {
121
+ stdout,
122
+ stderr,
123
+ exited: observation.exited,
124
+ cleanup: (cause) => {
125
+ cleanupPromise ??= (async () => {
126
+ await cleanupHook(child, cause);
127
+ })();
128
+ return cleanupPromise;
129
+ },
130
+ closeCapture: () => {
131
+ closePromise ??= closeNodeCapture(child);
132
+ return closePromise;
133
+ },
134
+ };
135
+ };
136
+ const defaultAcquire = (request, retain) => {
137
+ const environment = request.environment === undefined
138
+ ? Object.fromEntries(Object.entries(process.env).filter((entry) => entry[1] !== undefined))
139
+ : { ...request.environment };
140
+ const child = spawn(request.command, [...request.args], {
141
+ cwd: request.cwd,
142
+ env: environment,
143
+ stdio: ["ignore", "pipe", "pipe"],
144
+ windowsHide: true,
145
+ });
146
+ retain(child);
147
+ };
148
+ export const createNodeProcessAdapter = (options = {}) => {
149
+ const hasAcquire = options.acquire !== undefined;
150
+ const hasCleanup = options.cleanup !== undefined;
151
+ if (hasAcquire !== hasCleanup) {
152
+ throw processError("invalid-input", "acquire and cleanup hooks must be provided together");
153
+ }
154
+ const acquire = options.acquire ?? defaultAcquire;
155
+ const customCleanup = options.cleanup;
156
+ return {
157
+ launch: async (request) => {
158
+ let retainedChild;
159
+ let retainCount = 0;
160
+ let observation;
161
+ const retain = (child) => {
162
+ retainCount += 1;
163
+ if (retainCount !== 1 || !(child instanceof Object)) {
164
+ throw processError("invalid-input", "retain must be called exactly once with one child");
165
+ }
166
+ retainedChild = child;
167
+ observation = observeExit(child);
168
+ };
169
+ const rollback = async (cause) => {
170
+ const child = retainedChild;
171
+ if (child === undefined) {
172
+ throw processError("launch", "process setup failed", cause);
173
+ }
174
+ const observed = observation ?? observeExit(child);
175
+ const cleanupSettlement = await settleOperation(async () => {
176
+ if (customCleanup !== undefined) {
177
+ await customCleanup(child, "failed");
178
+ }
179
+ else if (observed.hasPid()) {
180
+ await defaultCleanup(child, "failed", observed);
181
+ }
182
+ });
183
+ const closeSettlement = await settleOperation(() => closeNodeCaptureWithin(child));
184
+ const failures = rejectedReasons([
185
+ cleanupSettlement,
186
+ closeSettlement,
187
+ ]);
188
+ if (failures.length > 0) {
189
+ const cleanupCause = failures.length === 1
190
+ ? failures[0]
191
+ : new AggregateError(failures, "cleanup and capture closure both failed");
192
+ throw withCause(new ProcessRuntimeError("cleanup", "process setup cleanup failed", {
193
+ cause: cleanupCause,
194
+ initiatingError: cause,
195
+ incompleteCustody: true,
196
+ }), cleanupCause);
197
+ }
198
+ throw processError("launch", "process setup failed", cause);
199
+ };
200
+ try {
201
+ acquire(request, retain);
202
+ }
203
+ catch (cause) {
204
+ return rollback(cause);
205
+ }
206
+ if (retainedChild === undefined || observation === undefined) {
207
+ return rollback(new Error("acquire did not retain one child"));
208
+ }
209
+ let running;
210
+ try {
211
+ const observed = observation ?? observeExit(retainedChild);
212
+ running = createRunning(retainedChild, observed, customCleanup ?? ((child, cause) => defaultCleanup(child, cause, observed)));
213
+ await new Promise((resolve, reject) => {
214
+ const onSpawn = () => {
215
+ retainedChild?.off("error", onError);
216
+ resolve();
217
+ };
218
+ const onError = (error) => {
219
+ retainedChild?.off("spawn", onSpawn);
220
+ reject(error);
221
+ };
222
+ retainedChild?.once("spawn", onSpawn);
223
+ retainedChild?.once("error", onError);
224
+ });
225
+ return running;
226
+ }
227
+ catch (cause) {
228
+ return rollback(cause);
229
+ }
230
+ },
231
+ };
232
+ };
233
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../../src/adapters/node.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import type { ProcessRuntimeFacts } from "./errors.js";
2
+ export type Exit = {
3
+ readonly exitCode: number | null;
4
+ readonly signal: string | null;
5
+ };
6
+ export type Launch = {
7
+ readonly command: string;
8
+ readonly args: readonly string[];
9
+ readonly cwd: string;
10
+ readonly environment?: Readonly<Record<string, string>>;
11
+ };
12
+ export type Request = Launch & {
13
+ readonly signal?: AbortSignal;
14
+ readonly timeoutMs?: number;
15
+ readonly strictUtf8?: boolean;
16
+ };
17
+ export type CompletionCause = "exited" | "cancelled" | "timeout" | "failed";
18
+ export type RunningProcess = {
19
+ readonly stdout: AsyncIterable<Uint8Array>;
20
+ readonly stderr: AsyncIterable<Uint8Array>;
21
+ readonly exited: Promise<Exit>;
22
+ readonly cleanup: (cause: CompletionCause) => Promise<void>;
23
+ readonly closeCapture: () => Promise<void>;
24
+ };
25
+ export type ProcessAdapter = {
26
+ readonly launch: (request: Launch) => Promise<RunningProcess>;
27
+ };
28
+ export type ProcessRunResult = Exit & {
29
+ readonly stdout: string;
30
+ readonly stderr: string;
31
+ readonly facts: ProcessRuntimeFacts;
32
+ };
33
+ export type ProcessExecutor = {
34
+ readonly run: (request: Request) => Promise<ProcessRunResult>;
35
+ };
36
+ export declare const PROCESS_CAPTURE_LIMIT_BYTES: number;
37
+ export declare const PROCESS_CLEANUP_STEP_MS = 1000;
38
+ export declare const PROCESS_TIMEOUT_MAX_MS = 2147483647;
39
+ //# sourceMappingURL=contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvD,MAAM,MAAM,IAAI,GAAG;IACjB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACzD,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE5E,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IAC3C,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IAC3C,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,QAAQ,CAAC,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;CAC/D,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,gBAAgB,CAAC,CAAC;CAC/D,CAAC;AAEF,eAAO,MAAM,2BAA2B,QAAkB,CAAC;AAC3D,eAAO,MAAM,uBAAuB,OAAQ,CAAC;AAC7C,eAAO,MAAM,sBAAsB,aAAgB,CAAC"}
@@ -0,0 +1,4 @@
1
+ export const PROCESS_CAPTURE_LIMIT_BYTES = 4 * 1024 * 1024;
2
+ export const PROCESS_CLEANUP_STEP_MS = 1_000;
3
+ export const PROCESS_TIMEOUT_MAX_MS = 2_147_483_647;
4
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AA4CA,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAC3D,MAAM,CAAC,MAAM,uBAAuB,GAAG,KAAK,CAAC;AAC7C,MAAM,CAAC,MAAM,sBAAsB,GAAG,aAAa,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { Exit } from "./contract.js";
2
+ export type ProcessErrorCode = "invalid-input" | "launch" | "output-limit" | "invalid-output" | "stream" | "timeout" | "cancelled" | "cleanup";
3
+ export type ProcessInitiatingCause = "launch" | "timeout" | "cancellation" | "capture" | "observation" | "decode" | "exit" | null;
4
+ export type ProcessRuntimeFacts = {
5
+ readonly initiatingCause: ProcessInitiatingCause;
6
+ readonly completionRequestedBeforeExitObservation: boolean;
7
+ readonly observedExit: Exit | null;
8
+ readonly lateFailureCodes: readonly ProcessErrorCode[];
9
+ readonly outputBounded: boolean;
10
+ readonly incompleteCustody: boolean;
11
+ };
12
+ export type ProcessRuntimeErrorOptions = {
13
+ readonly cause?: unknown;
14
+ readonly initiatingError?: unknown;
15
+ readonly incompleteCustody?: boolean;
16
+ readonly facts?: Partial<ProcessRuntimeFacts>;
17
+ };
18
+ export declare class ProcessRuntimeError extends Error {
19
+ readonly code: ProcessErrorCode;
20
+ readonly initiatingError: unknown;
21
+ readonly facts: ProcessRuntimeFacts;
22
+ constructor(code: ProcessErrorCode, message: string, options?: ProcessRuntimeErrorOptions);
23
+ get incompleteCustody(): boolean;
24
+ }
25
+ export declare const isProcessRuntimeError: (value: unknown) => value is ProcessRuntimeError;
26
+ export declare const processError: (code: ProcessErrorCode, message: string, cause?: unknown) => ProcessRuntimeError;
27
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAE1C,MAAM,MAAM,gBAAgB,GACxB,eAAe,GACf,QAAQ,GACR,cAAc,GACd,gBAAgB,GAChB,QAAQ,GACR,SAAS,GACT,WAAW,GACX,SAAS,CAAC;AAEd,MAAM,MAAM,sBAAsB,GAC9B,QAAQ,GACR,SAAS,GACT,cAAc,GACd,SAAS,GACT,aAAa,GACb,QAAQ,GACR,MAAM,GACN,IAAI,CAAC;AAET,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,CAAC,eAAe,EAAE,sBAAsB,CAAC;IACjD,QAAQ,CAAC,wCAAwC,EAAE,OAAO,CAAC;IAC3D,QAAQ,CAAC,YAAY,EAAE,IAAI,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,gBAAgB,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACvD,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IACrC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;CAC/C,CAAC;AAEF,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAC;IAEpC,YACE,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,0BAA+B,EAgBzC;IAED,IAAI,iBAAiB,IAAI,OAAO,CAE/B;CACF;AAED,eAAO,MAAM,qBAAqB,UACzB,OAAO,KACb,KAAK,IAAI,mBAC0B,CAAC;AAEvC,eAAO,MAAM,YAAY,SACjB,gBAAgB,WACb,MAAM,UACP,OAAO,KACd,mBAC2E,CAAC"}
package/dist/errors.js ADDED
@@ -0,0 +1,25 @@
1
+ export class ProcessRuntimeError extends Error {
2
+ code;
3
+ initiatingError;
4
+ facts;
5
+ constructor(code, message, options = {}) {
6
+ super(message, options.cause === undefined ? undefined : { cause: options.cause });
7
+ this.name = "ProcessRuntimeError";
8
+ this.code = code;
9
+ this.initiatingError = options.initiatingError ?? null;
10
+ this.facts = Object.freeze({
11
+ initiatingCause: options.facts?.initiatingCause ?? null,
12
+ completionRequestedBeforeExitObservation: options.facts?.completionRequestedBeforeExitObservation ?? false,
13
+ observedExit: options.facts?.observedExit ?? null,
14
+ lateFailureCodes: Object.freeze([...(options.facts?.lateFailureCodes ?? [])]),
15
+ outputBounded: options.facts?.outputBounded ?? false,
16
+ incompleteCustody: options.facts?.incompleteCustody ?? options.incompleteCustody ?? false,
17
+ });
18
+ }
19
+ get incompleteCustody() {
20
+ return this.facts.incompleteCustody;
21
+ }
22
+ }
23
+ export const isProcessRuntimeError = (value) => value instanceof ProcessRuntimeError;
24
+ export const processError = (code, message, cause) => new ProcessRuntimeError(code, message, cause === undefined ? {} : { cause });
25
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAsCA,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IACnC,IAAI,CAAmB;IACvB,eAAe,CAAU;IACzB,KAAK,CAAsB;IAEpC,YACE,IAAsB,EACtB,OAAe,EACf,OAAO,GAA+B,EAAE;QAExC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QACnF,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;QAClC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,IAAI,CAAC;QACvD,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC;YACzB,eAAe,EAAE,OAAO,CAAC,KAAK,EAAE,eAAe,IAAI,IAAI;YACvD,wCAAwC,EACtC,OAAO,CAAC,KAAK,EAAE,wCAAwC,IAAI,KAAK;YAClE,YAAY,EAAE,OAAO,CAAC,KAAK,EAAE,YAAY,IAAI,IAAI;YACjD,gBAAgB,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,gBAAgB,IAAI,EAAE,CAAC,CAAC,CAAC;YAC7E,aAAa,EAAE,OAAO,CAAC,KAAK,EAAE,aAAa,IAAI,KAAK;YACpD,iBAAiB,EACf,OAAO,CAAC,KAAK,EAAE,iBAAiB,IAAI,OAAO,CAAC,iBAAiB,IAAI,KAAK;SACzE,CAAC,CAAC;IACL,CAAC;IAED,IAAI,iBAAiB;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC;IACtC,CAAC;CACF;AAED,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,KAAc,EACgB,EAAE,CAChC,KAAK,YAAY,mBAAmB,CAAC;AAEvC,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,IAAsB,EACtB,OAAe,EACf,KAAe,EACM,EAAE,CACvB,IAAI,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ProcessAdapter, ProcessExecutor } from "./contract.js";
2
+ export declare const createProcessExecutor: (adapter: ProcessAdapter) => ProcessExecutor;
3
+ //# sourceMappingURL=executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,cAAc,EACd,eAAe,EAIhB,MAAM,eAAe,CAAC;AAmFvB,eAAO,MAAM,qBAAqB,YAAa,cAAc,KAAG,eAwQ/D,CAAC"}
@@ -0,0 +1,281 @@
1
+ import { PROCESS_CLEANUP_STEP_MS } from "./contract.js";
2
+ import { ProcessRuntimeError, isProcessRuntimeError, processError, } from "./errors.js";
3
+ import { classifyProcessOutcome, } from "./internal/classification.js";
4
+ import { decodeCapture, drainCapture } from "./internal/capture.js";
5
+ import { snapshotRequest } from "./internal/input.js";
6
+ import { rejectedReasons, settleOperation, withCause, } from "./internal/settlement.js";
7
+ const deferred = () => {
8
+ let resolve;
9
+ const promise = new Promise((settle) => {
10
+ resolve = settle;
11
+ });
12
+ return { promise, resolve };
13
+ };
14
+ const wait = async (promise, timeoutMs) => {
15
+ let timer;
16
+ const timeout = new Promise((resolve) => {
17
+ timer = setTimeout(() => resolve({ status: "timeout" }), timeoutMs);
18
+ });
19
+ try {
20
+ return await Promise.race([
21
+ promise.then((value) => ({ status: "settled", value })),
22
+ timeout,
23
+ ]);
24
+ }
25
+ finally {
26
+ if (timer !== undefined) {
27
+ clearTimeout(timer);
28
+ }
29
+ }
30
+ };
31
+ const captureError = (value, streamName) => {
32
+ const code = isProcessRuntimeError(value) && value.code === "output-limit"
33
+ ? "output-limit"
34
+ : "stream";
35
+ return new ProcessRuntimeError(code, code === "output-limit" ? "captured output exceeded 4 MiB" : `${streamName} capture failed`, { cause: value });
36
+ };
37
+ const initiatingCause = (cause) => {
38
+ switch (cause) {
39
+ case "timeout":
40
+ return "timeout";
41
+ case "cancelled":
42
+ return "cancellation";
43
+ case "capture":
44
+ return "capture";
45
+ case "observation":
46
+ case "failed":
47
+ return "observation";
48
+ case "exited":
49
+ return "exit";
50
+ }
51
+ };
52
+ export const createProcessExecutor = (adapter) => {
53
+ if (adapter === null || typeof adapter !== "object" || typeof adapter.launch !== "function") {
54
+ throw processError("invalid-input", "adapter must expose launch(request)");
55
+ }
56
+ return {
57
+ run: async (request) => {
58
+ const snapshot = snapshotRequest(request);
59
+ const failure = deferred();
60
+ let initiatingFailure;
61
+ let observedExit = null;
62
+ let completionRequestedBeforeExitObservation = false;
63
+ let lateFailureCodes = [];
64
+ let captureReads;
65
+ let outputBounded = false;
66
+ const addLateFailureCode = (code) => {
67
+ if (!lateFailureCodes.includes(code)) {
68
+ lateFailureCodes = [...lateFailureCodes, code];
69
+ }
70
+ };
71
+ const beginFailure = (error, cause) => {
72
+ if (initiatingFailure !== undefined) {
73
+ if (cause === "capture")
74
+ addLateFailureCode(error.code);
75
+ return;
76
+ }
77
+ const state = {
78
+ cause: initiatingCause(cause),
79
+ code: error.code,
80
+ error,
81
+ };
82
+ initiatingFailure = state;
83
+ completionRequestedBeforeExitObservation = observedExit === null;
84
+ failure.resolve(state);
85
+ };
86
+ if (snapshot.signal !== undefined && Boolean(snapshot.signal.aborted)) {
87
+ throw new ProcessRuntimeError("cancelled", "process request was already cancelled", {
88
+ initiatingError: snapshot.signal.reason,
89
+ facts: {
90
+ initiatingCause: "cancellation",
91
+ completionRequestedBeforeExitObservation: true,
92
+ },
93
+ });
94
+ }
95
+ const onAbort = () => {
96
+ if (observedExit === null && initiatingFailure === undefined) {
97
+ beginFailure(processError("cancelled", "process request was cancelled", snapshot.signal?.reason), "cancelled");
98
+ }
99
+ };
100
+ snapshot.signal?.addEventListener("abort", onAbort, { once: true });
101
+ const timer = snapshot.timeoutMs === undefined
102
+ ? undefined
103
+ : setTimeout(() => {
104
+ if (observedExit === null && initiatingFailure === undefined) {
105
+ beginFailure(processError("timeout", "process deadline elapsed"), "timeout");
106
+ }
107
+ }, snapshot.timeoutMs);
108
+ if (snapshot.signal !== undefined && Boolean(snapshot.signal.aborted)) {
109
+ if (timer !== undefined)
110
+ clearTimeout(timer);
111
+ snapshot.signal.removeEventListener("abort", onAbort);
112
+ throw new ProcessRuntimeError("cancelled", "process request was already cancelled", {
113
+ initiatingError: snapshot.signal.reason,
114
+ facts: {
115
+ initiatingCause: "cancellation",
116
+ completionRequestedBeforeExitObservation: true,
117
+ },
118
+ });
119
+ }
120
+ let running;
121
+ try {
122
+ running = await adapter.launch(snapshot.launch);
123
+ }
124
+ catch (cause) {
125
+ if (timer !== undefined)
126
+ clearTimeout(timer);
127
+ snapshot.signal?.removeEventListener("abort", onAbort);
128
+ if (isProcessRuntimeError(cause) && cause.code === "cleanup") {
129
+ throw new ProcessRuntimeError("cleanup", "process cleanup did not complete", {
130
+ cause: cause.cause,
131
+ initiatingError: cause.initiatingError,
132
+ facts: {
133
+ ...cause.facts,
134
+ initiatingCause: cause.facts.initiatingCause ?? "launch",
135
+ incompleteCustody: true,
136
+ },
137
+ });
138
+ }
139
+ if (initiatingFailure !== undefined) {
140
+ throw new ProcessRuntimeError(initiatingFailure.code, initiatingFailure.error.message, {
141
+ cause: initiatingFailure.error.cause,
142
+ initiatingError: initiatingFailure.error,
143
+ facts: {
144
+ initiatingCause: initiatingFailure.cause,
145
+ completionRequestedBeforeExitObservation,
146
+ lateFailureCodes,
147
+ },
148
+ });
149
+ }
150
+ if (isProcessRuntimeError(cause)) {
151
+ throw cause;
152
+ }
153
+ throw new ProcessRuntimeError("launch", "process launch failed", {
154
+ cause,
155
+ initiatingError: cause,
156
+ facts: { initiatingCause: "launch" },
157
+ });
158
+ }
159
+ const stdoutRead = drainCapture(running.stdout, (cause) => {
160
+ beginFailure(captureError(cause, "stdout"), "capture");
161
+ });
162
+ const stderrRead = drainCapture(running.stderr, (cause) => {
163
+ beginFailure(captureError(cause, "stderr"), "capture");
164
+ });
165
+ outputBounded = true;
166
+ // Invariant assertion: the 4 MiB capture limits are in force for this
167
+ // run; the fact does not report whether truncation occurred (review-v2
168
+ // R11 disposition, documented invariant).
169
+ // Contract: drainCapture reports failures through beginFailure and its
170
+ // promise never rejects, so this chain cannot reject (review-v2 R17
171
+ // disposition, documented non-rejecting drain contract).
172
+ const captures = Promise.all([stdoutRead, stderrRead]).then((value) => {
173
+ captureReads = value;
174
+ return value;
175
+ });
176
+ const exitObservation = running.exited.then((exit) => {
177
+ observedExit = exit;
178
+ return { kind: "exit" };
179
+ }, (cause) => {
180
+ beginFailure(new ProcessRuntimeError("stream", "process observation failed", { cause }), "observation");
181
+ return { kind: "failure" };
182
+ });
183
+ const failureObservation = failure.promise.then(() => ({ kind: "failure" }));
184
+ const firstObservation = await Promise.race([exitObservation, failureObservation]);
185
+ if (firstObservation.kind === "exit" && initiatingFailure === undefined) {
186
+ if (timer !== undefined)
187
+ clearTimeout(timer);
188
+ snapshot.signal?.removeEventListener("abort", onAbort);
189
+ }
190
+ const closeAndSettleCapture = async () => {
191
+ const initiallySettled = await wait(captures, PROCESS_CLEANUP_STEP_MS);
192
+ if (initiallySettled.status === "settled" &&
193
+ initiallySettled.value.every((capture) => capture.normalEof)) {
194
+ return;
195
+ }
196
+ const forcedAfterEofDeadline = initiallySettled.status === "timeout";
197
+ let closeSettlement;
198
+ const close = settleOperation(() => running.closeCapture()).then((settlement) => {
199
+ closeSettlement = settlement;
200
+ return settlement;
201
+ });
202
+ const settled = await wait(Promise.all([close, settleOperation(() => captures)]).then(() => true), PROCESS_CLEANUP_STEP_MS);
203
+ const closeFailure = closeSettlement !== undefined && closeSettlement.status === "rejected"
204
+ ? closeSettlement
205
+ : undefined;
206
+ if (settled.status === "timeout") {
207
+ throw closeFailure === undefined
208
+ ? new Error("native capture close or reader settlement remained open")
209
+ : closeFailure.reason;
210
+ }
211
+ if (closeFailure !== undefined) {
212
+ throw closeFailure.reason === undefined
213
+ ? withCause(new Error("native capture closure failed"), undefined)
214
+ : closeFailure.reason;
215
+ }
216
+ if (forcedAfterEofDeadline) {
217
+ throw new Error("capture EOF deadline elapsed");
218
+ }
219
+ };
220
+ let cleanupFailure;
221
+ const cleanupSettlement = await settleOperation(() => running.cleanup(initiatingFailure?.cause === "timeout"
222
+ ? "timeout"
223
+ : initiatingFailure?.cause === "cancellation"
224
+ ? "cancelled"
225
+ : firstObservation.kind === "exit"
226
+ ? "exited"
227
+ : "failed"));
228
+ const closeSettlement = await settleOperation(() => closeAndSettleCapture());
229
+ const failures = rejectedReasons([cleanupSettlement, closeSettlement]);
230
+ if (failures.length > 0) {
231
+ cleanupFailure = {
232
+ reason: failures.length === 1
233
+ ? failures[0]
234
+ : new AggregateError(failures, "cleanup and capture closure both failed"),
235
+ };
236
+ }
237
+ if (timer !== undefined)
238
+ clearTimeout(timer);
239
+ snapshot.signal?.removeEventListener("abort", onAbort);
240
+ await wait(exitObservation, PROCESS_CLEANUP_STEP_MS);
241
+ let decodeFailure;
242
+ let stdout = "";
243
+ let stderr = "";
244
+ if (captureReads !== undefined) {
245
+ try {
246
+ stdout = decodeCapture(captureReads[0].bytes, snapshot.strictUtf8, "stdout");
247
+ stderr = decodeCapture(captureReads[1].bytes, snapshot.strictUtf8, "stderr");
248
+ }
249
+ catch (cause) {
250
+ decodeFailure = new ProcessRuntimeError("invalid-output", "captured output was not valid UTF-8", { cause });
251
+ addLateFailureCode("invalid-output");
252
+ }
253
+ }
254
+ const outcome = classifyProcessOutcome({
255
+ initiatingFailure,
256
+ cleanupFailure,
257
+ decodeFailure,
258
+ lateFailureCodes,
259
+ observedExit,
260
+ completionRequestedBeforeExitObservation,
261
+ outputBounded,
262
+ });
263
+ if (outcome.kind === "error") {
264
+ throw outcome.error;
265
+ }
266
+ const finalExit = observedExit;
267
+ if (finalExit === null) {
268
+ throw new ProcessRuntimeError("stream", "process exited without an observed result", {
269
+ facts: outcome.facts,
270
+ });
271
+ }
272
+ return {
273
+ ...finalExit,
274
+ stdout,
275
+ stderr,
276
+ facts: outcome.facts,
277
+ };
278
+ },
279
+ };
280
+ };
281
+ //# sourceMappingURL=executor.js.map