posipaki 0.49.2 → 0.51.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/dist/{actor-B5EJRlY8.js → fetch-D72Xvuoc.js} +134 -14
- package/dist/fetch-D72Xvuoc.js.map +1 -0
- package/dist/{index-ChSy_Vdu.d.ts → index-BQH-ok2x.d.ts} +75 -3
- package/dist/index-BQH-ok2x.d.ts.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/port/index.d.ts +3 -3
- package/dist/port/index.js +3 -3
- package/dist/port/node.d.ts +12 -3
- package/dist/port/node.d.ts.map +1 -1
- package/dist/port/node.js +111 -84
- package/dist/port/node.js.map +1 -1
- package/dist/port-CDK2CsXP.js +865 -0
- package/dist/port-CDK2CsXP.js.map +1 -0
- package/dist/{reactor-4MaIKVnw.d.ts → reactor-Cl5_rhzB.d.ts} +12 -1
- package/dist/reactor-Cl5_rhzB.d.ts.map +1 -0
- package/dist/remote/gateway-cli.js.map +1 -1
- package/dist/remote/index.d.ts +28 -3
- package/dist/remote/index.d.ts.map +1 -1
- package/dist/remote/index.js +41 -2
- package/dist/remote/index.js.map +1 -1
- package/dist/remote/node.d.ts +3 -3
- package/dist/remote/node.js +4 -4
- package/dist/types-U3NvKgpg.d.ts.map +1 -1
- package/dist/{version-De-M910s.js → version-C6CYx8YW.js} +2 -2
- package/dist/{version-De-M910s.js.map → version-C6CYx8YW.js.map} +1 -1
- package/dist/{websocket-DNSNv77Y.d.ts → websocket-B4txKA7r.d.ts} +2 -2
- package/dist/{websocket-DNSNv77Y.d.ts.map → websocket-B4txKA7r.d.ts.map} +1 -1
- package/dist/{websocket-Dm2eqDOA.js → websocket-Cj4yT9YK.js} +9 -5
- package/dist/websocket-Cj4yT9YK.js.map +1 -0
- package/package.json +1 -1
- package/dist/actor-B5EJRlY8.js.map +0 -1
- package/dist/index-ChSy_Vdu.d.ts.map +0 -1
- package/dist/port-DLpNfoyX.js +0 -524
- package/dist/port-DLpNfoyX.js.map +0 -1
- package/dist/reactor-4MaIKVnw.d.ts.map +0 -1
- package/dist/websocket-Dm2eqDOA.js.map +0 -1
package/dist/port/node.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as held, i as splitChannel, r as portActor, t as HttpRefusal } from "../fetch-D72Xvuoc.js";
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
|
+
import { createServer } from "node:http";
|
|
3
4
|
//#region src/port/node/spawn.ts
|
|
4
5
|
const DEFAULT_KILL_AFTER_MS = 5e3;
|
|
5
6
|
/** A command as a channel of bytes: stdout in, stdin out, and its exit when it closes. */
|
|
@@ -62,7 +63,8 @@ async function spawnPorts(command, options = {}) {
|
|
|
62
63
|
control: (command) => {
|
|
63
64
|
const signal = command.signal;
|
|
64
65
|
if (typeof signal === "string") signalGroup(child, signal);
|
|
65
|
-
}
|
|
66
|
+
},
|
|
67
|
+
hasFrames: async () => false
|
|
66
68
|
};
|
|
67
69
|
const stderr = {
|
|
68
70
|
send: () => Promise.reject(/* @__PURE__ */ new Error("stderr carries the process's output; it has no input")),
|
|
@@ -106,7 +108,8 @@ function bytesOf(stream, label) {
|
|
|
106
108
|
onError: () => () => {},
|
|
107
109
|
close: () => Promise.resolve(),
|
|
108
110
|
info: {},
|
|
109
|
-
control: () => {}
|
|
111
|
+
control: () => {},
|
|
112
|
+
hasFrames: async () => false
|
|
110
113
|
};
|
|
111
114
|
stream?.on("data", (chunk) => {
|
|
112
115
|
const bytes = new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);
|
|
@@ -154,15 +157,13 @@ async function shutdown(child, killAfterMs) {
|
|
|
154
157
|
* A server whose requests arrive as pairs of ports.
|
|
155
158
|
*
|
|
156
159
|
* `identify` runs before a pair exists, so a refused request costs no stream and the caller reads a status with
|
|
157
|
-
* the reason in it. The request body is the inbound port; the response body is the outbound one
|
|
160
|
+
* the reason in it. The request body is the inbound port; the response body is the outbound one, and the head
|
|
161
|
+
* is one command on it.
|
|
158
162
|
*/
|
|
159
163
|
function httpListener(options = {}) {
|
|
160
|
-
const runtime = globalThis.Bun;
|
|
161
|
-
if (runtime === void 0) throw new Error("httpListener: this needs Bun's server");
|
|
162
164
|
const waiting = [];
|
|
163
165
|
const buffered = [];
|
|
164
166
|
let closed = false;
|
|
165
|
-
let server = null;
|
|
166
167
|
const take = (pair) => {
|
|
167
168
|
const next = waiting.shift();
|
|
168
169
|
if (next === void 0) {
|
|
@@ -171,29 +172,36 @@ function httpListener(options = {}) {
|
|
|
171
172
|
}
|
|
172
173
|
next.accept(pair);
|
|
173
174
|
};
|
|
174
|
-
server =
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
175
|
+
const server = createServer((request, response) => {
|
|
176
|
+
(async () => {
|
|
177
|
+
if (closed) {
|
|
178
|
+
response.writeHead(503).end("the listener is closed");
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
179
181
|
let meta;
|
|
180
182
|
try {
|
|
181
|
-
meta = options.identify === void 0 ? void 0 : await options.identify(request);
|
|
183
|
+
meta = options.identify === void 0 ? void 0 : await options.identify(asRequest(request));
|
|
182
184
|
} catch (error) {
|
|
183
|
-
|
|
185
|
+
const status = error instanceof HttpRefusal ? error.status : 401;
|
|
186
|
+
response.writeHead(status).end(error instanceof Error ? error.message : String(error));
|
|
187
|
+
return;
|
|
184
188
|
}
|
|
185
|
-
const outbound = responsePort();
|
|
189
|
+
const outbound = responsePort(response);
|
|
186
190
|
take({
|
|
187
191
|
inbound: requestPort(request),
|
|
188
|
-
outbound
|
|
192
|
+
outbound,
|
|
189
193
|
meta
|
|
190
194
|
});
|
|
191
|
-
|
|
192
|
-
}
|
|
195
|
+
})();
|
|
193
196
|
});
|
|
197
|
+
server.listen(options.port ?? 0, options.hostname);
|
|
194
198
|
return {
|
|
195
199
|
get port() {
|
|
196
|
-
|
|
200
|
+
const address = server.address();
|
|
201
|
+
return typeof address === "object" && address !== null ? address.port : 0;
|
|
202
|
+
},
|
|
203
|
+
get server() {
|
|
204
|
+
return server;
|
|
197
205
|
},
|
|
198
206
|
accept() {
|
|
199
207
|
const next = buffered.shift();
|
|
@@ -209,11 +217,20 @@ function httpListener(options = {}) {
|
|
|
209
217
|
async close() {
|
|
210
218
|
closed = true;
|
|
211
219
|
for (const next of waiting.splice(0)) next.refuse(/* @__PURE__ */ new Error("httpListener: closed"));
|
|
212
|
-
|
|
213
|
-
server
|
|
220
|
+
server.closeAllConnections?.();
|
|
221
|
+
await new Promise((done) => server.close(() => done()));
|
|
214
222
|
}
|
|
215
223
|
};
|
|
216
224
|
}
|
|
225
|
+
/** The request as `identify` sees it: method, url and headers, and no body — the body is the inbound port. */
|
|
226
|
+
function asRequest(request) {
|
|
227
|
+
const host = request.headers.host ?? "localhost";
|
|
228
|
+
const method = request.method ?? "GET";
|
|
229
|
+
return new Request(`http://${host}${request.url ?? "/"}`, {
|
|
230
|
+
method,
|
|
231
|
+
headers: Object.entries(request.headers).flatMap(([name, value]) => value === void 0 ? [] : [[name, Array.isArray(value) ? value.join(", ") : value]])
|
|
232
|
+
});
|
|
233
|
+
}
|
|
217
234
|
/** The request's body as the inbound port: frames as they arrive, ended when the body is. */
|
|
218
235
|
function requestPort(request) {
|
|
219
236
|
const readers = /* @__PURE__ */ new Set();
|
|
@@ -222,7 +239,7 @@ function requestPort(request) {
|
|
|
222
239
|
const end = () => {
|
|
223
240
|
if (ended) return;
|
|
224
241
|
ended = true;
|
|
225
|
-
for (const closer of
|
|
242
|
+
for (const closer of closers) closer(void 0);
|
|
226
243
|
};
|
|
227
244
|
const inbound = {
|
|
228
245
|
send: () => Promise.reject(/* @__PURE__ */ new Error("a request body has no input; that side is the caller's")),
|
|
@@ -246,84 +263,93 @@ function requestPort(request) {
|
|
|
246
263
|
return () => {};
|
|
247
264
|
},
|
|
248
265
|
close: async () => {
|
|
249
|
-
await request.body?.cancel().catch(() => void 0);
|
|
250
266
|
end();
|
|
267
|
+
request.destroy();
|
|
251
268
|
},
|
|
252
269
|
info: {
|
|
253
|
-
method: request.method,
|
|
254
|
-
url: request.url
|
|
270
|
+
method: request.method ?? "GET",
|
|
271
|
+
url: request.url ?? "/"
|
|
255
272
|
},
|
|
256
|
-
control: () => {}
|
|
273
|
+
control: () => {},
|
|
274
|
+
hasFrames: async () => false
|
|
257
275
|
};
|
|
258
|
-
const reader = request.body?.getReader();
|
|
259
276
|
const port = held(inbound);
|
|
260
|
-
(
|
|
261
|
-
if (
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
end();
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
for (const target of [...readers]) target(chunk.value);
|
|
272
|
-
}
|
|
273
|
-
})();
|
|
277
|
+
request.on("data", (chunk) => {
|
|
278
|
+
if (ended) return;
|
|
279
|
+
const bytes = new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);
|
|
280
|
+
for (const target of readers) target(bytes);
|
|
281
|
+
});
|
|
282
|
+
request.on("end", end);
|
|
283
|
+
request.on("close", end);
|
|
284
|
+
request.on("error", end);
|
|
274
285
|
return port;
|
|
275
286
|
}
|
|
276
|
-
/**
|
|
277
|
-
|
|
287
|
+
/**
|
|
288
|
+
* The response's channel: the head is one command, the body is what `send` writes, and `close` ends it.
|
|
289
|
+
*
|
|
290
|
+
* `node:http` is told the head separately from the body, which is why a handler states it before the first byte,
|
|
291
|
+
* and why a handler that writes without stating one gets the 200 the wire would have given anyway.
|
|
292
|
+
*/
|
|
293
|
+
function responsePort(response) {
|
|
278
294
|
const readers = /* @__PURE__ */ new Set();
|
|
279
|
-
|
|
295
|
+
let stated = false;
|
|
280
296
|
let ended = false;
|
|
297
|
+
const state = (head) => {
|
|
298
|
+
if (stated) return;
|
|
299
|
+
stated = true;
|
|
300
|
+
response.writeHead(head.status, head.headers);
|
|
301
|
+
};
|
|
281
302
|
const end = () => {
|
|
282
303
|
if (ended) return;
|
|
283
304
|
ended = true;
|
|
284
|
-
for (const
|
|
305
|
+
for (const reader of readers) reader(void 0);
|
|
285
306
|
};
|
|
307
|
+
response.on("close", end);
|
|
286
308
|
return {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
return () => {};
|
|
294
|
-
},
|
|
295
|
-
onClose(handler) {
|
|
296
|
-
if (ended) {
|
|
297
|
-
handler(void 0);
|
|
298
|
-
return () => {};
|
|
299
|
-
}
|
|
300
|
-
closers.add(handler);
|
|
301
|
-
return () => {
|
|
302
|
-
closers.delete(handler);
|
|
303
|
-
};
|
|
304
|
-
},
|
|
305
|
-
onError() {
|
|
306
|
-
return () => {};
|
|
307
|
-
},
|
|
308
|
-
async close() {
|
|
309
|
-
end();
|
|
310
|
-
},
|
|
311
|
-
info: {},
|
|
312
|
-
control: () => {}
|
|
309
|
+
async send(frame) {
|
|
310
|
+
if (ended) throw new Error("this response is over");
|
|
311
|
+
if (!stated) state({ status: 200 });
|
|
312
|
+
await new Promise((done) => {
|
|
313
|
+
response.write(frame, () => done());
|
|
314
|
+
});
|
|
313
315
|
},
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
cancel() {
|
|
324
|
-
end();
|
|
316
|
+
onFrame() {
|
|
317
|
+
return () => {};
|
|
318
|
+
},
|
|
319
|
+
/** A response carries what is written to it and nothing else: there is never a frame standing. */
|
|
320
|
+
hasFrames: async () => false,
|
|
321
|
+
onClose(handler) {
|
|
322
|
+
if (ended) {
|
|
323
|
+
handler(void 0);
|
|
324
|
+
return () => {};
|
|
325
325
|
}
|
|
326
|
-
|
|
326
|
+
readers.add(handler);
|
|
327
|
+
return () => {
|
|
328
|
+
readers.delete(handler);
|
|
329
|
+
};
|
|
330
|
+
},
|
|
331
|
+
onError() {
|
|
332
|
+
return () => {};
|
|
333
|
+
},
|
|
334
|
+
async close() {
|
|
335
|
+
if (ended) return;
|
|
336
|
+
if (!stated) state({ status: 200 });
|
|
337
|
+
ended = true;
|
|
338
|
+
await new Promise((done) => {
|
|
339
|
+
response.end(() => done());
|
|
340
|
+
});
|
|
341
|
+
},
|
|
342
|
+
info: {},
|
|
343
|
+
control(command) {
|
|
344
|
+
const head = command.head;
|
|
345
|
+
if (typeof head !== "object" || head === null) return;
|
|
346
|
+
const status = head.status;
|
|
347
|
+
const headers = head.headers;
|
|
348
|
+
state({
|
|
349
|
+
status: typeof status === "number" ? status : 200,
|
|
350
|
+
...typeof headers === "object" && headers !== null ? { headers } : {}
|
|
351
|
+
});
|
|
352
|
+
}
|
|
327
353
|
};
|
|
328
354
|
}
|
|
329
355
|
//#endregion
|
|
@@ -367,7 +393,8 @@ function lines(parts) {
|
|
|
367
393
|
},
|
|
368
394
|
close: (reason) => parts.close(reason),
|
|
369
395
|
info: parts.info,
|
|
370
|
-
control: (command) => parts.control(command)
|
|
396
|
+
control: (command) => parts.control(command),
|
|
397
|
+
hasFrames: async () => false
|
|
371
398
|
};
|
|
372
399
|
}
|
|
373
400
|
/** The child's octets, frame for frame. */
|
package/dist/port/node.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.js","names":[],"sources":["../../src/port/node/spawn.ts","../../src/port/node/listener.ts","../../src/port/node/process.ts"],"sourcesContent":["// ── a spawned command as a port ─────────────────────────────────────────────\n//\n// The child's stdout is the channel's frames, its stdin is what `send` writes, and the way it ended is the\n// channel's close reason. Nothing else crosses: a child's stderr is discarded, because stderr is a stream the\n// child writes while it runs and not a fact about how it ended. `spawnPorts` is the same process with both of\n// its output streams.\n//\n// Closing is stdin's end of stream first. A program that reads its input exits on EOF, which is the polite\n// half; the process is killed `killAfterMs` later if it is still there, which is the other half.\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport type { Readable } from \"node:stream\";\nimport { held } from \"../held.js\";\nimport type { Channel } from \"../channel.js\";\n/** How a process ended: it exited with a code, or a signal terminated it. */\nexport type ProcessEnd =\n | { readonly kind: \"exit\"; readonly code: number }\n | { readonly kind: \"signal\"; readonly signal: string };\nexport interface SpawnPortOptions {\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n /** How long the process may live after its stdin is closed. Default 5000 ms, then SIGKILL. */\n killAfterMs?: number;\n}\nexport interface ProcessPorts {\n readonly stdout: Channel<Uint8Array, ProcessEnd>;\n readonly stderr: Channel<Uint8Array, ProcessEnd>;\n}\nconst DEFAULT_KILL_AFTER_MS = 5_000;\n/** A command as a channel of bytes: stdout in, stdin out, and its exit when it closes. */\nexport function spawnPort(\n command: string[],\n options: SpawnPortOptions = {},\n): Promise<Channel<Uint8Array, ProcessEnd>> {\n return spawnPorts(command, options).then((ports) => ports.stdout);\n}\n/** One process, its stdout and its stderr, each a channel that ends with the same exit. */\nexport async function spawnPorts(\n command: string[],\n options: SpawnPortOptions = {},\n): Promise<ProcessPorts> {\n const [file, ...args] = command;\n if (file === undefined) throw new Error(\"spawnPort: there is no command to run\");\n const child = spawn(file, args, { cwd: options.cwd, env: options.env, detached: true });\n await started(child, file);\n const out = bytesOf(child.stdout, \"stdout\");\n const err = bytesOf(child.stderr, \"stderr\");\n let exit: ProcessEnd | null = null;\n let ended = false;\n const closers = new Set<(reason: ProcessEnd) => void>();\n const failers = new Set<(error: unknown) => void>();\n const end = (why: ProcessEnd): void => {\n if (ended) return;\n ended = true;\n exit = why;\n for (const closer of [...closers]) closer(why);\n };\n child.on(\"error\", (error) => {\n for (const failer of [...failers]) failer(error);\n });\n // `close` rather than `exit`: it follows the end of both streams, so no frame is lost behind the ending.\n // Node gives a code or a signal and never both; that is the OS's one status word, split in two.\n child.on(\"close\", (code, signal) =>\n end(code !== null ? { kind: \"exit\", code } : { kind: \"signal\", signal: signal ?? \"unknown\" }),\n );\n const close = (reason?: string): Promise<void> => {\n return shutdown(child, options.killAfterMs ?? DEFAULT_KILL_AFTER_MS).catch(() => undefined);\n };\n const onClose = (handler: (reason: ProcessEnd) => void): (() => void) => {\n if (exit !== null) {\n handler(exit);\n return () => {};\n }\n closers.add(handler);\n return () => {\n closers.delete(handler);\n };\n };\n const onError = (handler: (error: unknown) => void): (() => void) => {\n failers.add(handler);\n return () => {\n failers.delete(handler);\n };\n };\n // What only a process knows, and the one command it takes: a signal for the process and its children.\n const facts = {\n info: { pid: child.pid },\n control: (command: Record<string, unknown>): void => {\n const signal = command.signal;\n if (typeof signal === \"string\") signalGroup(child, signal as NodeJS.Signals);\n },\n };\n const stderr: Channel<Uint8Array, ProcessEnd> = {\n send: () => Promise.reject(new Error(\"stderr carries the process's output; it has no input\")),\n onFrame: err.onFrame,\n onClose,\n onError,\n close,\n ...facts,\n };\n const stdout: Channel<Uint8Array, ProcessEnd> = {\n send: (frame: Uint8Array) => write(child, frame),\n onFrame: out.onFrame,\n onClose,\n onError,\n close,\n ...facts,\n };\n return { stdout, stderr };\n}\n/** Resolves once the child exists, rejects when the command could not be started at all. */\nfunction started(child: ChildProcess, file: string): Promise<void> {\n return new Promise((resolve, reject) => {\n child.once(\"spawn\", () => resolve());\n child.once(\"error\", (error: Error) =>\n reject(new Error(`spawnPort: could not start ${file}: ${error.message}`)),\n );\n });\n}\n/** One output stream as the frames of a channel, kept until somebody reads them. */\nfunction bytesOf(stream: Readable | null, label: string): Channel<Uint8Array, ProcessEnd> {\n const readers = new Set<(frame: Uint8Array) => void>();\n const frames: Channel<Uint8Array, ProcessEnd> = {\n send: () => Promise.reject(new Error(`${label} carries the process's output; it has no input`)),\n onFrame(handler) {\n readers.add(handler);\n return () => {\n readers.delete(handler);\n };\n },\n // The stream ending is not the process ending: the port reports that, from the process's own exit.\n onClose: () => () => {},\n onError: () => () => {},\n close: () => Promise.resolve(),\n info: {},\n control: () => {},\n };\n stream?.on(\"data\", (chunk: Buffer) => {\n const bytes = new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);\n for (const reader of [...readers]) reader(bytes);\n });\n return held(frames);\n}\n/** Writes to the child's stdin, and settles when the write is out. */\nfunction write(child: ChildProcess, frame: Uint8Array): Promise<void> {\n const stdin = child.stdin;\n if (stdin === null || stdin.destroyed) return Promise.reject(new Error(\"the process's stdin is closed\"));\n return new Promise((resolve, reject) => {\n stdin.write(frame, (error) => (error === null || error === undefined ? resolve() : reject(error)));\n });\n}\n/** Signal the child's process group, or the child alone when it has none. */\nfunction signalGroup(child: ChildProcess, signal: NodeJS.Signals): void {\n const pid = child.pid;\n if (pid === undefined) return;\n try {\n // The negative id is the group a detached child leads: its own children are in it too.\n process.kill(-pid, signal);\n return;\n } catch {\n /* no group of that number: this is one process after all */\n }\n try {\n child.kill(signal);\n } catch {\n /* gone */\n }\n}\n\n/** Ends stdin, and kills what is still there after the grace. A caller in a hurry sends `signal` first. */\nasync function shutdown(child: ChildProcess, killAfterMs: number): Promise<void> {\n if (child.exitCode !== null || child.signalCode !== null) return;\n const closed = new Promise<void>((resolve) => {\n child.once(\"close\", () => resolve());\n });\n child.stdin?.end();\n const timer = setTimeout(() => {\n if (child.exitCode === null && child.signalCode === null) signalGroup(child, \"SIGKILL\");\n }, killAfterMs);\n timer.unref?.();\n await closed;\n clearTimeout(timer);\n}\n","// ── an HTTP server as a port listener ───────────────────────────────────────\n//\n// One request is one pair of ports: the request's body is what the far side sends, the response's body is what we\n// send back, and the `Response` is built here because this is the only place that knows HTTP. A handler above\n// sees a pair and nothing about the protocol.\n//\n// The body is built before the handler runs, because a server must answer with a `Response` in the same turn it is\n// asked: writes go into the response's stream, and closing the outbound port ends it.\n\nimport type { Channel } from \"../channel.js\";\nimport { held } from \"../held.js\";\nimport type { PortListener, PortPair } from \"../reactor.js\";\n\nexport interface HttpListenerOptions<Meta> {\n port?: number;\n hostname?: string;\n /** Admission before a body is made: what it returns is the pair's meta; throwing refuses the request. */\n identify?(request: Request): Meta | Promise<Meta>;\n}\n\n/** A port listener that is also an HTTP server: the port it bound, and the pairs it hands out. */\nexport interface HttpListener<Meta> extends PortListener<Uint8Array, Uint8Array, Meta> {\n readonly port: number;\n}\n\n/** The Bun server this wraps, as little of it as is used here. */\ninterface BunServer {\n readonly port: number;\n stop(force?: boolean): void | Promise<void>;\n}\n\ninterface BunRuntime {\n serve(options: {\n port: number;\n hostname?: string;\n fetch(request: Request): Response | Promise<Response>;\n }): BunServer;\n}\n\n/**\n * A server whose requests arrive as pairs of ports.\n *\n * `identify` runs before a pair exists, so a refused request costs no stream and the caller reads a status with\n * the reason in it. The request body is the inbound port; the response body is the outbound one.\n */\nexport function httpListener<Meta = unknown>(options: HttpListenerOptions<Meta> = {}): HttpListener<Meta> {\n const runtime = (globalThis as { Bun?: BunRuntime }).Bun;\n if (runtime === undefined) throw new Error(\"httpListener: this needs Bun's server\");\n\n const waiting: Array<{ accept: (pair: PortPair<Uint8Array, Uint8Array, Meta, void>) => void; refuse: (error: Error) => void }> = [];\n const buffered: Array<PortPair<Uint8Array, Uint8Array, Meta, void>> = [];\n let closed = false;\n let server: BunServer | null = null;\n\n const take = (pair: PortPair<Uint8Array, Uint8Array, Meta, void>): void => {\n const next = waiting.shift();\n if (next === undefined) {\n buffered.push(pair);\n return;\n }\n next.accept(pair);\n };\n\n const bound = runtime.serve({\n port: options.port ?? 0,\n hostname: options.hostname,\n async fetch(request: Request): Promise<Response> {\n if (closed) return new Response(\"listener is closed\", { status: 503 });\n let meta: Meta;\n try {\n meta = options.identify === undefined ? (undefined as Meta) : await options.identify(request);\n } catch (error) {\n return new Response(error instanceof Error ? error.message : String(error), { status: 401 });\n }\n const outbound = responsePort();\n take({ inbound: requestPort(request), outbound: outbound.channel, meta });\n return new Response(outbound.body);\n },\n });\n server = bound;\n\n return {\n get port() {\n return server === null ? 0 : server.port;\n },\n accept(): Promise<PortPair<Uint8Array, Uint8Array, Meta, void>> {\n const next = buffered.shift();\n if (next !== undefined) return Promise.resolve(next);\n if (closed) return Promise.reject(new Error(\"httpListener: closed\"));\n return new Promise((accept, refuse) => {\n waiting.push({ accept, refuse });\n });\n },\n async close(): Promise<void> {\n closed = true;\n for (const next of waiting.splice(0)) next.refuse(new Error(\"httpListener: closed\"));\n await server?.stop(true);\n server = null;\n },\n };\n}\n\n/** The request's body as the inbound port: frames as they arrive, ended when the body is. */\nfunction requestPort(request: Request): Channel<Uint8Array, void> {\n const readers = new Set<(frame: Uint8Array) => void>();\n const closers = new Set<(reason: void) => void>();\n let ended = false;\n\n const end = (): void => {\n if (ended) return;\n ended = true;\n for (const closer of [...closers]) closer(undefined);\n };\n\n const inbound: Channel<Uint8Array, void> = {\n send: () => Promise.reject(new Error(\"a request body has no input; that side is the caller's\")),\n onFrame(handler) {\n readers.add(handler);\n return () => {\n readers.delete(handler);\n };\n },\n onClose(handler) {\n if (ended) {\n handler(undefined);\n return () => {};\n }\n closers.add(handler);\n return () => {\n closers.delete(handler);\n };\n },\n onError() {\n return () => {};\n },\n close: async () => {\n await request.body?.cancel().catch(() => undefined);\n end();\n },\n info: { method: request.method, url: request.url },\n control: () => {},\n };\n\n const reader = request.body?.getReader();\n // Held before the first read: a body can arrive in the same turn the pair is handed out.\n const port = held(inbound);\n void (async () => {\n if (reader === undefined) {\n end();\n return;\n }\n for (;;) {\n const chunk = await reader.read();\n if (chunk.done) {\n end();\n return;\n }\n for (const target of [...readers]) target(chunk.value);\n }\n })();\n\n return port;\n}\n\n/** The response's channel, and the body a `Response` is built from. One stream, written by the channel. */\nfunction responsePort(): { channel: Channel<Uint8Array, void>; body: ReadableStream<Uint8Array> } {\n const readers = new Set<(frame: Uint8Array) => void>();\n const closers = new Set<(reason: void) => void>();\n let ended = false;\n\n const end = (): void => {\n if (ended) return;\n ended = true;\n for (const closer of [...closers]) closer(undefined);\n };\n\n const body = new ReadableStream<Uint8Array>({\n start(controller) {\n readers.add((frame) => controller.enqueue(frame));\n closers.add(() => {\n try {\n controller.close();\n } catch {\n // Already closed by the platform: nothing here has an opinion about it.\n }\n });\n },\n cancel() {\n // The caller stopped reading: the channel is over from this side too.\n end();\n },\n });\n\n const channel: Channel<Uint8Array, void> = {\n async send(frame) {\n if (ended) throw new Error(\"this response is over\");\n for (const reader of [...readers]) reader(frame);\n },\n onFrame() {\n return () => {};\n },\n onClose(handler) {\n if (ended) {\n handler(undefined);\n return () => {};\n }\n closers.add(handler);\n return () => {\n closers.delete(handler);\n };\n },\n onError() {\n return () => {};\n },\n async close() {\n end();\n },\n info: {},\n control: () => {},\n };\n\n return { channel, body };\n}\n","// ── the two ports that wrap a process ───────────────────────────────────────\n//\n// Both are `portActor` over `spawnPorts`: one carries the child's octets as they are, the other puts the splitter\n// underneath and carries lines. The names are the contract — an actor definition is spawned by name.\n\nimport type { Message } from \"../../types.js\";\nimport { portActor } from \"../actor.js\";\nimport type { Channel, Part } from \"../channel.js\";\nimport { splitChannel } from \"../split.js\";\nimport { spawnPort, type ProcessEnd } from \"./spawn.js\";\n\n/** How a process port is started. */\nexport interface ProcessPortArgs {\n command: string[];\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n /** How long the process may live after its stdin is closed. Default 5000 ms, then SIGKILL. */\n killAfterMs?: number;\n}\n\n\n\n/** Parts as their text: what a line port carries. Octets that came with a line are not text and are dropped. */\nfunction lines(parts: Channel<Part, ProcessEnd>): Channel<string, ProcessEnd> {\n const readers = new Set<(line: string) => void>();\n const closers = new Set<(reason: ProcessEnd) => void>();\n const failers = new Set<(error: unknown) => void>();\n\n parts.onFrame(({ line }) => {\n for (const reader of [...readers]) reader(line);\n });\n parts.onClose((reason) => {\n for (const closer of [...closers]) closer(reason);\n });\n parts.onError((error) => {\n for (const failer of [...failers]) failer(error);\n });\n\n return {\n send: (line) => parts.send({ line, bytes: null }),\n onFrame(handler) {\n readers.add(handler);\n return () => {\n readers.delete(handler);\n };\n },\n onClose(handler) {\n closers.add(handler);\n return () => {\n closers.delete(handler);\n };\n },\n onError(handler) {\n failers.add(handler);\n return () => {\n failers.delete(handler);\n };\n },\n close: (reason) => parts.close(reason),\n info: parts.info,\n control: (command) => parts.control(command),\n };\n}\n\n\n/** The child's octets, frame for frame. */\nexport const BinaryPort = portActor<ProcessPortArgs, Uint8Array, ProcessEnd>({\n name: \"binary-port\",\n open: (args) => spawnPort(args.command, args),\n});\n\n/** The same child cut into lines: the facts come with it, because the medium is the same one. */\nexport const Utf8Port = portActor<ProcessPortArgs, string, ProcessEnd>({\n name: \"utf8-port\",\n open: async (args) => lines(splitChannel(await spawnPort(args.command, args))),\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"node.js","names":[],"sources":["../../src/port/node/spawn.ts","../../src/port/node/listener.ts","../../src/port/node/process.ts"],"sourcesContent":["// ── a spawned command as a port ─────────────────────────────────────────────\n//\n// The child's stdout is the channel's frames, its stdin is what `send` writes, and the way it ended is the\n// channel's close reason. Nothing else crosses: a child's stderr is discarded, because stderr is a stream the\n// child writes while it runs and not a fact about how it ended. `spawnPorts` is the same process with both of\n// its output streams.\n//\n// Closing is stdin's end of stream first. A program that reads its input exits on EOF, which is the polite\n// half; the process is killed `killAfterMs` later if it is still there, which is the other half.\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport type { Readable } from \"node:stream\";\nimport { held } from \"../held.js\";\nimport type { Channel } from \"../channel.js\";\n/** How a process ended: it exited with a code, or a signal terminated it. */\nexport type ProcessEnd =\n | { readonly kind: \"exit\"; readonly code: number }\n | { readonly kind: \"signal\"; readonly signal: string };\nexport interface SpawnPortOptions {\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n /** How long the process may live after its stdin is closed. Default 5000 ms, then SIGKILL. */\n killAfterMs?: number;\n}\nexport interface ProcessPorts {\n readonly stdout: Channel<Uint8Array, ProcessEnd>;\n readonly stderr: Channel<Uint8Array, ProcessEnd>;\n}\nconst DEFAULT_KILL_AFTER_MS = 5_000;\n/** A command as a channel of bytes: stdout in, stdin out, and its exit when it closes. */\nexport function spawnPort(\n command: string[],\n options: SpawnPortOptions = {},\n): Promise<Channel<Uint8Array, ProcessEnd>> {\n return spawnPorts(command, options).then((ports) => ports.stdout);\n}\n/** One process, its stdout and its stderr, each a channel that ends with the same exit. */\nexport async function spawnPorts(\n command: string[],\n options: SpawnPortOptions = {},\n): Promise<ProcessPorts> {\n const [file, ...args] = command;\n if (file === undefined) throw new Error(\"spawnPort: there is no command to run\");\n const child = spawn(file, args, { cwd: options.cwd, env: options.env, detached: true });\n await started(child, file);\n const out = bytesOf(child.stdout, \"stdout\");\n const err = bytesOf(child.stderr, \"stderr\");\n let exit: ProcessEnd | null = null;\n let ended = false;\n const closers = new Set<(reason: ProcessEnd) => void>();\n const failers = new Set<(error: unknown) => void>();\n const end = (why: ProcessEnd): void => {\n if (ended) return;\n ended = true;\n exit = why;\n for (const closer of [...closers]) closer(why);\n };\n child.on(\"error\", (error) => {\n for (const failer of [...failers]) failer(error);\n });\n // `close` rather than `exit`: it follows the end of both streams, so no frame is lost behind the ending.\n // Node gives a code or a signal and never both; that is the OS's one status word, split in two.\n child.on(\"close\", (code, signal) =>\n end(code !== null ? { kind: \"exit\", code } : { kind: \"signal\", signal: signal ?? \"unknown\" }),\n );\n const close = (reason?: string): Promise<void> => {\n return shutdown(child, options.killAfterMs ?? DEFAULT_KILL_AFTER_MS).catch(() => undefined);\n };\n const onClose = (handler: (reason: ProcessEnd) => void): (() => void) => {\n if (exit !== null) {\n handler(exit);\n return () => {};\n }\n closers.add(handler);\n return () => {\n closers.delete(handler);\n };\n };\n const onError = (handler: (error: unknown) => void): (() => void) => {\n failers.add(handler);\n return () => {\n failers.delete(handler);\n };\n };\n // What only a process knows, and the one command it takes: a signal for the process and its children.\n const facts = {\n info: { pid: child.pid },\n control: (command: Record<string, unknown>): void => {\n const signal = command.signal;\n if (typeof signal === \"string\") signalGroup(child, signal as NodeJS.Signals);\n },\n hasFrames: async () => false,\n };\n const stderr: Channel<Uint8Array, ProcessEnd> = {\n send: () => Promise.reject(new Error(\"stderr carries the process's output; it has no input\")),\n onFrame: err.onFrame,\n onClose,\n onError,\n close,\n ...facts,\n };\n const stdout: Channel<Uint8Array, ProcessEnd> = {\n send: (frame: Uint8Array) => write(child, frame),\n onFrame: out.onFrame,\n onClose,\n onError,\n close,\n ...facts,\n };\n return { stdout, stderr };\n}\n/** Resolves once the child exists, rejects when the command could not be started at all. */\nfunction started(child: ChildProcess, file: string): Promise<void> {\n return new Promise((resolve, reject) => {\n child.once(\"spawn\", () => resolve());\n child.once(\"error\", (error: Error) =>\n reject(new Error(`spawnPort: could not start ${file}: ${error.message}`)),\n );\n });\n}\n/** One output stream as the frames of a channel, kept until somebody reads them. */\nfunction bytesOf(stream: Readable | null, label: string): Channel<Uint8Array, ProcessEnd> {\n const readers = new Set<(frame: Uint8Array) => void>();\n const frames: Channel<Uint8Array, ProcessEnd> = {\n send: () => Promise.reject(new Error(`${label} carries the process's output; it has no input`)),\n onFrame(handler) {\n readers.add(handler);\n return () => {\n readers.delete(handler);\n };\n },\n // The stream ending is not the process ending: the port reports that, from the process's own exit.\n onClose: () => () => {},\n onError: () => () => {},\n close: () => Promise.resolve(),\n info: {},\n control: () => {},\n hasFrames: async () => false,\n };\n stream?.on(\"data\", (chunk: Buffer) => {\n const bytes = new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);\n for (const reader of [...readers]) reader(bytes);\n });\n return held(frames);\n}\n/** Writes to the child's stdin, and settles when the write is out. */\nfunction write(child: ChildProcess, frame: Uint8Array): Promise<void> {\n const stdin = child.stdin;\n if (stdin === null || stdin.destroyed) return Promise.reject(new Error(\"the process's stdin is closed\"));\n return new Promise((resolve, reject) => {\n stdin.write(frame, (error) => (error === null || error === undefined ? resolve() : reject(error)));\n });\n}\n/** Signal the child's process group, or the child alone when it has none. */\nfunction signalGroup(child: ChildProcess, signal: NodeJS.Signals): void {\n const pid = child.pid;\n if (pid === undefined) return;\n try {\n // The negative id is the group a detached child leads: its own children are in it too.\n process.kill(-pid, signal);\n return;\n } catch {\n /* no group of that number: this is one process after all */\n }\n try {\n child.kill(signal);\n } catch {\n /* gone */\n }\n}\n\n/** Ends stdin, and kills what is still there after the grace. A caller in a hurry sends `signal` first. */\nasync function shutdown(child: ChildProcess, killAfterMs: number): Promise<void> {\n if (child.exitCode !== null || child.signalCode !== null) return;\n const closed = new Promise<void>((resolve) => {\n child.once(\"close\", () => resolve());\n });\n child.stdin?.end();\n const timer = setTimeout(() => {\n if (child.exitCode === null && child.signalCode === null) signalGroup(child, \"SIGKILL\");\n }, killAfterMs);\n timer.unref?.();\n await closed;\n clearTimeout(timer);\n}\n","// ── an HTTP server as a port listener ───────────────────────────────────────\n//\n// One request is one pair of ports: the request's body is what the far side sends, the response's body is what we\n// send back. A handler above sees a pair and nothing about the protocol; this is the only place that knows HTTP.\n//\n// The runtime is `node:http`, which is the whole of what a listener needs: a request is an async iterable of\n// chunks, a response takes a head and then a body, and an upgrade arrives on the same server. Nothing here asks\n// what runs it.\n\nimport { createServer, type IncomingMessage, type Server, type ServerResponse } from \"node:http\";\nimport type { Channel } from \"../channel.js\";\nimport { HttpRefusal } from \"../fetch.js\";\nimport { held } from \"../held.js\";\nimport type { PortListener, PortPair } from \"../reactor.js\";\n\nexport interface HttpListenerOptions<Meta> {\n port?: number;\n hostname?: string;\n /** Admission before a body is made: what it returns is the pair's meta; throwing refuses the request. */\n identify?(request: Request): Meta | Promise<Meta>;\n}\n\n/** A port listener that is also an HTTP server: the port it bound, and the pairs it hands out. */\nexport interface HttpListener<Meta> extends PortListener<Uint8Array, Uint8Array, Meta> {\n readonly port: number;\n /** The server itself, for what only a consumer of it can do — a WebSocket upgrade, a second route. */\n readonly server: Server;\n}\n\n/** What a response says before its body: the status and the headers a handler chose. */\nexport interface ResponseHead {\n readonly status: number;\n readonly headers?: HeadersInit;\n}\n\n/**\n * A server whose requests arrive as pairs of ports.\n *\n * `identify` runs before a pair exists, so a refused request costs no stream and the caller reads a status with\n * the reason in it. The request body is the inbound port; the response body is the outbound one, and the head\n * is one command on it.\n */\nexport function httpListener<Meta = unknown>(options: HttpListenerOptions<Meta> = {}): HttpListener<Meta> {\n const waiting: Array<{\n accept: (pair: PortPair<Uint8Array, Uint8Array, Meta, void>) => void;\n refuse: (error: Error) => void;\n }> = [];\n const buffered: Array<PortPair<Uint8Array, Uint8Array, Meta, void>> = [];\n let closed = false;\n\n const take = (pair: PortPair<Uint8Array, Uint8Array, Meta, void>): void => {\n const next = waiting.shift();\n if (next === undefined) {\n buffered.push(pair);\n return;\n }\n next.accept(pair);\n };\n\n const server = createServer((request: IncomingMessage, response: ServerResponse) => {\n void (async () => {\n if (closed) {\n response.writeHead(503).end(\"the listener is closed\");\n return;\n }\n let meta: Meta;\n try {\n meta = options.identify === undefined ? (undefined as Meta) : await options.identify(asRequest(request));\n } catch (error) {\n // A refusal is an answer: a `HttpRefusal` says which status, anything else is the listener's own 401.\n const status = error instanceof HttpRefusal ? error.status : 401;\n response.writeHead(status).end(error instanceof Error ? error.message : String(error));\n return;\n }\n const outbound = responsePort(response);\n take({ inbound: requestPort(request), outbound, meta });\n })();\n });\n server.listen(options.port ?? 0, options.hostname);\n\n return {\n get port() {\n const address = server.address();\n return typeof address === \"object\" && address !== null ? address.port : 0;\n },\n get server() {\n return server;\n },\n accept(): Promise<PortPair<Uint8Array, Uint8Array, Meta, void>> {\n const next = buffered.shift();\n if (next !== undefined) return Promise.resolve(next);\n if (closed) return Promise.reject(new Error(\"httpListener: closed\"));\n return new Promise((accept, refuse) => {\n waiting.push({ accept, refuse });\n });\n },\n async close(): Promise<void> {\n closed = true;\n for (const next of waiting.splice(0)) next.refuse(new Error(\"httpListener: closed\"));\n server.closeAllConnections?.();\n await new Promise<void>((done) => server.close(() => done()));\n },\n };\n}\n\n/** The request as `identify` sees it: method, url and headers, and no body — the body is the inbound port. */\nfunction asRequest(request: IncomingMessage): Request {\n const host = request.headers.host ?? \"localhost\";\n const method = request.method ?? \"GET\";\n return new Request(`http://${host}${request.url ?? \"/\"}`, {\n method,\n headers: Object.entries(request.headers).flatMap(([name, value]) =>\n value === undefined ? [] : [[name, Array.isArray(value) ? value.join(\", \") : value] as [string, string]],\n ),\n });\n}\n\n/** The request's body as the inbound port: frames as they arrive, ended when the body is. */\nfunction requestPort(request: IncomingMessage): Channel<Uint8Array, void> {\n const readers = new Set<(frame: Uint8Array) => void>();\n const closers = new Set<(reason: void) => void>();\n let ended = false;\n\n const end = (): void => {\n if (ended) return;\n ended = true;\n for (const closer of closers) closer(undefined);\n };\n\n const inbound: Channel<Uint8Array, void> = {\n send: () => Promise.reject(new Error(\"a request body has no input; that side is the caller's\")),\n onFrame(handler) {\n readers.add(handler);\n return () => {\n readers.delete(handler);\n };\n },\n onClose(handler) {\n if (ended) {\n handler(undefined);\n return () => {};\n }\n closers.add(handler);\n return () => {\n closers.delete(handler);\n };\n },\n onError() {\n return () => {};\n },\n close: async () => {\n end();\n request.destroy();\n },\n info: { method: request.method ?? \"GET\", url: request.url ?? \"/\" },\n control: () => {},\n hasFrames: async () => false,\n };\n\n // Held before the first read: a body can arrive in the same turn the pair is handed out.\n const port = held(inbound);\n request.on(\"data\", (chunk: Buffer) => {\n if (ended) return;\n const bytes = new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);\n for (const target of readers) target(bytes);\n });\n request.on(\"end\", end);\n request.on(\"close\", end);\n request.on(\"error\", end);\n return port;\n}\n\n/**\n * The response's channel: the head is one command, the body is what `send` writes, and `close` ends it.\n *\n * `node:http` is told the head separately from the body, which is why a handler states it before the first byte,\n * and why a handler that writes without stating one gets the 200 the wire would have given anyway.\n */\nfunction responsePort(response: ServerResponse): Channel<Uint8Array, void> {\n const readers = new Set<(reason: void) => void>();\n let stated = false;\n let ended = false;\n\n const state = (head: ResponseHead): void => {\n if (stated) return;\n stated = true;\n response.writeHead(head.status, head.headers as Record<string, string> | undefined);\n };\n\n const end = (): void => {\n if (ended) return;\n ended = true;\n for (const reader of readers) reader(undefined);\n };\n // The client leaving is an ending of this channel too: a handler writing into it learns at once.\n response.on(\"close\", end);\n\n return {\n async send(frame) {\n if (ended) throw new Error(\"this response is over\");\n if (!stated) state({ status: 200 });\n await new Promise<void>((done) => {\n response.write(frame, () => done());\n });\n },\n onFrame() {\n return () => {};\n },\n /** A response carries what is written to it and nothing else: there is never a frame standing. */\n hasFrames: async () => false,\n onClose(handler) {\n if (ended) {\n handler(undefined);\n return () => {};\n }\n readers.add(handler);\n return () => {\n readers.delete(handler);\n };\n },\n onError() {\n return () => {};\n },\n async close() {\n if (ended) return;\n if (!stated) state({ status: 200 });\n ended = true;\n await new Promise<void>((done) => {\n response.end(() => done());\n });\n },\n info: {},\n control(command) {\n const head = command.head;\n if (typeof head !== \"object\" || head === null) return;\n const status = (head as { status?: unknown }).status;\n const headers = (head as { headers?: unknown }).headers;\n state({\n status: typeof status === \"number\" ? status : 200,\n ...(typeof headers === \"object\" && headers !== null ? { headers: headers as HeadersInit } : {}),\n });\n },\n };\n}\n","// ── the two ports that wrap a process ───────────────────────────────────────\n//\n// Both are `portActor` over `spawnPorts`: one carries the child's octets as they are, the other puts the splitter\n// underneath and carries lines. The names are the contract — an actor definition is spawned by name.\n\nimport type { Message } from \"../../types.js\";\nimport { portActor } from \"../actor.js\";\nimport type { Channel, Part } from \"../channel.js\";\nimport { splitChannel } from \"../split.js\";\nimport { spawnPort, type ProcessEnd } from \"./spawn.js\";\n\n/** How a process port is started. */\nexport interface ProcessPortArgs {\n command: string[];\n cwd?: string;\n env?: NodeJS.ProcessEnv;\n /** How long the process may live after its stdin is closed. Default 5000 ms, then SIGKILL. */\n killAfterMs?: number;\n}\n\n\n\n/** Parts as their text: what a line port carries. Octets that came with a line are not text and are dropped. */\nfunction lines(parts: Channel<Part, ProcessEnd>): Channel<string, ProcessEnd> {\n const readers = new Set<(line: string) => void>();\n const closers = new Set<(reason: ProcessEnd) => void>();\n const failers = new Set<(error: unknown) => void>();\n\n parts.onFrame(({ line }) => {\n for (const reader of [...readers]) reader(line);\n });\n parts.onClose((reason) => {\n for (const closer of [...closers]) closer(reason);\n });\n parts.onError((error) => {\n for (const failer of [...failers]) failer(error);\n });\n\n return {\n send: (line) => parts.send({ line, bytes: null }),\n onFrame(handler) {\n readers.add(handler);\n return () => {\n readers.delete(handler);\n };\n },\n onClose(handler) {\n closers.add(handler);\n return () => {\n closers.delete(handler);\n };\n },\n onError(handler) {\n failers.add(handler);\n return () => {\n failers.delete(handler);\n };\n },\n close: (reason) => parts.close(reason),\n info: parts.info,\n control: (command) => parts.control(command),\n hasFrames: async () => false,\n };\n}\n\n\n/** The child's octets, frame for frame. */\nexport const BinaryPort = portActor<ProcessPortArgs, Uint8Array, ProcessEnd>({\n name: \"binary-port\",\n open: (args) => spawnPort(args.command, args),\n});\n\n/** The same child cut into lines: the facts come with it, because the medium is the same one. */\nexport const Utf8Port = portActor<ProcessPortArgs, string, ProcessEnd>({\n name: \"utf8-port\",\n open: async (args) => lines(splitChannel(await spawnPort(args.command, args))),\n});\n"],"mappings":"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"}
|