posipaki 0.50.0 → 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/{fetch-DBJeP47R.js → fetch-D72Xvuoc.js} +26 -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 +7 -2
- package/dist/port/node.d.ts.map +1 -1
- package/dist/port/node.js +11 -5
- 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/process-eCAv2tIS.d.ts.map +1 -1
- 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 +2 -2
- package/dist/remote/index.js +1 -1
- package/dist/remote/node.d.ts +2 -2
- package/dist/remote/node.js +4 -4
- package/dist/{version-_BGyK0ZQ.js → version-C6CYx8YW.js} +2 -2
- package/dist/{version-_BGyK0ZQ.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-Dqv9GfDy.js → websocket-Cj4yT9YK.js} +9 -5
- package/dist/websocket-Cj4yT9YK.js.map +1 -0
- package/package.json +1 -1
- package/dist/fetch-DBJeP47R.js.map +0 -1
- package/dist/index-ChSy_Vdu.d.ts.map +0 -1
- package/dist/port-ByBSeVPm.js +0 -415
- package/dist/port-ByBSeVPm.js.map +0 -1
- package/dist/reactor-4MaIKVnw.d.ts.map +0 -1
- package/dist/websocket-Dqv9GfDy.js.map +0 -1
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { n as defineMessages, t as defineActor } from "./define-actor-DR1P1PA5.js";
|
|
2
|
-
import { StringDecoder } from "node:string_decoder";
|
|
3
2
|
//#region src/port/held.ts
|
|
4
3
|
/**
|
|
5
4
|
* `inner`, with the frames that arrive before anyone is subscribed kept for the readers that follow.
|
|
@@ -49,6 +48,8 @@ function held(inner) {
|
|
|
49
48
|
readers.delete(handler);
|
|
50
49
|
};
|
|
51
50
|
},
|
|
51
|
+
/** What it kept is handed to whoever subscribes next; what it will *push* is the inner channel's business. */
|
|
52
|
+
hasFrames: () => inner.hasFrames(),
|
|
52
53
|
onClose(handler) {
|
|
53
54
|
if (ending !== null) {
|
|
54
55
|
handler(ending.reason);
|
|
@@ -78,6 +79,17 @@ function held(inner) {
|
|
|
78
79
|
//#region src/port/split.ts
|
|
79
80
|
const NL = 10;
|
|
80
81
|
const CR = 13;
|
|
82
|
+
const EMPTY = /* @__PURE__ */ new Uint8Array(0);
|
|
83
|
+
const decoder = new TextDecoder();
|
|
84
|
+
/** Two octet strings as one: the one place this file would otherwise reach for `Buffer`. */
|
|
85
|
+
function joined(first, second) {
|
|
86
|
+
if (first.length === 0) return second;
|
|
87
|
+
if (second.length === 0) return first;
|
|
88
|
+
const whole = new Uint8Array(first.length + second.length);
|
|
89
|
+
whole.set(first, 0);
|
|
90
|
+
whole.set(second, first.length);
|
|
91
|
+
return whole;
|
|
92
|
+
}
|
|
81
93
|
const NOTHING = () => null;
|
|
82
94
|
/**
|
|
83
95
|
* One byte channel in, one channel of parts out.
|
|
@@ -89,9 +101,8 @@ function splitChannel(inner, announced = NOTHING) {
|
|
|
89
101
|
const readers = /* @__PURE__ */ new Set();
|
|
90
102
|
const closers = /* @__PURE__ */ new Set();
|
|
91
103
|
const failers = /* @__PURE__ */ new Set();
|
|
92
|
-
const decoder = new StringDecoder("utf8");
|
|
93
104
|
/** Bytes that are not yet a frame: a line, or the octets a line announced. */
|
|
94
|
-
let buffered =
|
|
105
|
+
let buffered = EMPTY;
|
|
95
106
|
/** The line whose announced octets are still arriving. */
|
|
96
107
|
let pending = null;
|
|
97
108
|
let ended = false;
|
|
@@ -134,7 +145,7 @@ function splitChannel(inner, announced = NOTHING) {
|
|
|
134
145
|
const raw = buffered.subarray(0, cut);
|
|
135
146
|
buffered = buffered.subarray(cut + 1);
|
|
136
147
|
const line = raw.length > 0 && raw[raw.length - 1] === CR ? raw.subarray(0, raw.length - 1) : raw;
|
|
137
|
-
const text = decoder.
|
|
148
|
+
const text = decoder.decode(line);
|
|
138
149
|
const count = announced(text);
|
|
139
150
|
if (count === null) {
|
|
140
151
|
emit({
|
|
@@ -158,9 +169,8 @@ function splitChannel(inner, announced = NOTHING) {
|
|
|
158
169
|
};
|
|
159
170
|
const parts = held({
|
|
160
171
|
send: async (part) => {
|
|
161
|
-
const head =
|
|
162
|
-
|
|
163
|
-
await inner.send(new Uint8Array(body));
|
|
172
|
+
const head = new TextEncoder().encode(`${part.line}\n`);
|
|
173
|
+
await inner.send(part.bytes === null ? head : joined(head, part.bytes));
|
|
164
174
|
},
|
|
165
175
|
onFrame: (handler) => {
|
|
166
176
|
readers.add(handler);
|
|
@@ -189,11 +199,12 @@ function splitChannel(inner, announced = NOTHING) {
|
|
|
189
199
|
await inner.close(why);
|
|
190
200
|
},
|
|
191
201
|
info: inner.info,
|
|
192
|
-
control: (command) => inner.control(command)
|
|
202
|
+
control: (command) => inner.control(command),
|
|
203
|
+
hasFrames: async () => false
|
|
193
204
|
});
|
|
194
205
|
inner.onFrame((chunk) => {
|
|
195
206
|
if (ended) return;
|
|
196
|
-
buffered =
|
|
207
|
+
buffered = joined(buffered, chunk);
|
|
197
208
|
drain();
|
|
198
209
|
});
|
|
199
210
|
inner.onError((error) => {
|
|
@@ -203,12 +214,12 @@ function splitChannel(inner, announced = NOTHING) {
|
|
|
203
214
|
if (ended) return;
|
|
204
215
|
if (pending === null && buffered.length > 0) {
|
|
205
216
|
const rest = buffered;
|
|
206
|
-
buffered =
|
|
217
|
+
buffered = EMPTY;
|
|
207
218
|
emit({
|
|
208
|
-
line: decoder.
|
|
219
|
+
line: decoder.decode(rest),
|
|
209
220
|
bytes: null
|
|
210
221
|
});
|
|
211
|
-
}
|
|
222
|
+
}
|
|
212
223
|
close(why);
|
|
213
224
|
});
|
|
214
225
|
return parts;
|
|
@@ -326,7 +337,8 @@ async function fetchPort(url, options = {}) {
|
|
|
326
337
|
url,
|
|
327
338
|
status: response.status
|
|
328
339
|
},
|
|
329
|
-
control: () => {}
|
|
340
|
+
control: () => {},
|
|
341
|
+
hasFrames: async () => false
|
|
330
342
|
};
|
|
331
343
|
const reader = response.body?.getReader();
|
|
332
344
|
const port = held(frames);
|
|
@@ -381,4 +393,4 @@ function named(url, error) {
|
|
|
381
393
|
//#endregion
|
|
382
394
|
export { held as a, splitChannel as i, fetchPort as n, portActor as r, HttpRefusal as t };
|
|
383
395
|
|
|
384
|
-
//# sourceMappingURL=fetch-
|
|
396
|
+
//# sourceMappingURL=fetch-D72Xvuoc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-D72Xvuoc.js","names":[],"sources":["../src/port/held.ts","../src/port/split.ts","../src/port/actor.ts","../src/port/fetch.ts"],"sourcesContent":["// ── a channel that keeps what nobody was reading yet ────────────────────────\n//\n// A medium can deliver a frame in the same turn a reader is being installed, and a channel with no subscriber\n// drops it. This wraps a channel so nothing is dropped for want of a listener: what arrives while nobody is\n// subscribed is kept, and every subscriber that arrives before the first frame is delivered is given it.\n//\n// The window ends at the first delivery. After that the channel is live, and a subscriber that comes later has\n// missed what went before it — which is what any live channel means, and the alternative would be an unbounded\n// copy of everything ever said.\n\nimport type { Channel } from \"./channel.js\";\n\n/**\n * `inner`, with the frames that arrive before anyone is subscribed kept for the readers that follow.\n *\n * The kept frames are not bounded, because a bound depends on what the far end may say before anyone listens,\n * and that is not something this adapter can know.\n */\nexport function held<F, C>(inner: Channel<F, C>): Channel<F, C> {\n const readers = new Set<(frame: F) => void>();\n const closers = new Set<(reason: C) => void>();\n const failers = new Set<(error: unknown) => void>();\n const kept: F[] = [];\n let live = false;\n /** The ending, kept for a reader that arrives after it: a channel that is over is not one to wait on. */\n let ending: { reason: C } | null = null;\n let failed: { error: unknown } | null = null;\n\n const emit = (frame: F): void => {\n for (const reader of [...readers]) reader(frame);\n };\n\n inner.onFrame((frame) => {\n if (live) {\n emit(frame);\n return;\n }\n if (readers.size === 0) {\n kept.push(frame);\n return;\n }\n // Somebody is reading: from here the channel is live, and what was kept has been taken.\n live = true;\n kept.length = 0;\n emit(frame);\n });\n inner.onClose((reason) => {\n ending = { reason };\n for (const closer of [...closers]) closer(reason);\n });\n inner.onError((error) => {\n failed = { error };\n for (const failer of [...failers]) failer(error);\n });\n\n return {\n send: (frame) => inner.send(frame),\n onFrame(handler) {\n readers.add(handler);\n // Handed to every subscriber that arrives before the first delivery, not only to the first.\n for (const frame of kept) handler(frame);\n return () => {\n readers.delete(handler);\n };\n },\n /** What it kept is handed to whoever subscribes next; what it will *push* is the inner channel's business. */\n hasFrames: () => inner.hasFrames(),\n onClose(handler) {\n if (ending !== null) {\n handler(ending.reason);\n return () => {};\n }\n closers.add(handler);\n return () => {\n closers.delete(handler);\n };\n },\n onError(handler) {\n if (failed !== null) {\n handler(failed.error);\n return () => {};\n }\n failers.add(handler);\n return () => {\n failers.delete(handler);\n };\n },\n close: (reason) => inner.close(reason),\n info: inner.info,\n control: (command) => inner.control(command),\n };\n}\n","// ── bytes into parts: the splitter ──────────────────────────────────────────\n//\n// Cuts a stream of bytes on 0x0a; what lies between two cuts is a line. Every byte medium does this: a stdio pair,\n// a socket, an HTTP body.\n//\n// Splitting on the byte is safe for UTF-8: a continuation byte is never 0x0a, so a character cannot be cut in\n// half, and a line is decoded only when all of its octets are in hand. A character that straddles two chunks\n// waits in the buffer, which is why this file needs no decoder of its own — and no `node:` import, because\n// `posipaki/port` is the entry a browser takes.\n\nimport type { Channel, Part } from \"./channel.js\";\nimport { held } from \"./held.js\";\n\nconst NL = 0x0a;\nconst CR = 0x0d;\n\nconst EMPTY = new Uint8Array(0);\nconst decoder = new TextDecoder();\n\n/** Two octet strings as one: the one place this file would otherwise reach for `Buffer`. */\nfunction joined(first: Uint8Array, second: Uint8Array): Uint8Array {\n if (first.length === 0) return second;\n if (second.length === 0) return first;\n const whole = new Uint8Array(first.length + second.length);\n whole.set(first, 0);\n whole.set(second, first.length);\n return whole;\n}\n/** Given a line, how many raw octets follow it. Default: none, this is a plain line. */\nexport type Announcement = (line: string) => number | null;\n\nconst NOTHING: Announcement = () => null;\n\n/**\n * One byte channel in, one channel of parts out.\n *\n * The returned channel's `send` is the same rule backwards: a part becomes its line, a terminator, and the\n * octets it carried.\n */\nexport function splitChannel<C = string>(\n inner: Channel<Uint8Array, C>,\n announced: Announcement = NOTHING,\n): Channel<Part, C> {\n const readers = new Set<(part: Part) => void>();\n const closers = new Set<(reason: C) => void>();\n const failers = new Set<(error: unknown) => void>();\n\n /** Bytes that are not yet a frame: a line, or the octets a line announced. */\n let buffered: Uint8Array = EMPTY;\n /** The line whose announced octets are still arriving. */\n let pending: { line: string; bytes: number } | null = null;\n let ended = false;\n let reason: C | null = null;\n\n const emit = (part: Part): void => {\n for (const reader of [...readers]) reader(part);\n };\n\n /** The far end is gone: our own readers hear it. */\n const close = (why: C): void => {\n if (ended) return;\n ended = true;\n reason = why;\n for (const closer of [...closers]) closer(why);\n };\n\n /**\n * End it here: the far end hears a close, our own readers do not, because we are the one who asked. The\n * reason stays null: a sentence from this side is not `C`, which is what the other end had to say.\n */\n const shut = (): void => {\n if (ended) return;\n ended = true;\n };\n\n /** Hand over what the buffer holds, as far as the framing allows. */\n const drain = (): void => {\n while (!ended) {\n if (pending !== null) {\n if (buffered.length < pending.bytes) return;\n const bytes = buffered.subarray(0, pending.bytes);\n buffered = buffered.subarray(pending.bytes);\n const line = pending.line;\n pending = null;\n emit({ line, bytes: new Uint8Array(bytes) });\n continue;\n }\n const cut = buffered.indexOf(NL);\n if (cut === -1) return;\n const raw = buffered.subarray(0, cut);\n buffered = buffered.subarray(cut + 1);\n const line = raw.length > 0 && raw[raw.length - 1] === CR ? raw.subarray(0, raw.length - 1) : raw;\n const text = decoder.decode(line);\n const count = announced(text);\n if (count === null) {\n emit({ line: text, bytes: null });\n continue;\n }\n pending = { line: text, bytes: count };\n if (count === 0) {\n pending = null;\n emit({ line: text, bytes: new Uint8Array(0) });\n }\n }\n };\n\n // Built and wrapped before the inner channel is read: the medium may have kept frames for its first\n // reader, and subscribing drains them at once — a part produced then must be a reader's to find, not the\n // wire's to lose.\n const parts: Channel<Part, C> = held<Part, C>({\n send: async (part: Part) => {\n const head = new TextEncoder().encode(`${part.line}\\n`);\n await inner.send(part.bytes === null ? head : joined(head, part.bytes));\n },\n onFrame: (handler) => {\n readers.add(handler);\n return () => {\n readers.delete(handler);\n };\n },\n onClose: (handler) => {\n if (ended && reason !== null) {\n handler(reason);\n return () => {};\n }\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: async (why = \"closed here\") => {\n shut();\n await inner.close(why);\n },\n info: inner.info,\n control: (command) => inner.control(command),\n hasFrames: async () => false,\n });\n\n inner.onFrame((chunk) => {\n if (ended) return;\n buffered = joined(buffered, chunk);\n drain();\n });\n inner.onError((error) => {\n for (const failer of [...failers]) failer(error);\n });\n inner.onClose((why) => {\n if (ended) return;\n // Whatever is left without a terminator is still a line: a peer that hangs up after writing has said\n // something, and dropping it would be the framing deciding to lose the last word.\n if (pending === null && buffered.length > 0) {\n const rest = buffered;\n buffered = EMPTY;\n emit({ line: decoder.decode(rest), bytes: null });\n }\n close(why);\n });\n\n return parts;\n}\n","// ── a port as an actor ──────────────────────────────────────────────────────\n//\n// One actor that owns one channel and speaks for it: what it is told to send goes in, what the channel delivers\n// comes out as DATA, and the way the channel ended is left in the actor's state, where a parent reads anyway.\n//\n// The channel is opened in `setup`, which the runtime awaits, so an actor that is ready has a channel: a send is\n// never asked to wait for one, and a port that cannot open fails its start rather than looking alive. What a\n// channel is — a process, a socket, a pair in memory — is `open`'s business; the actor knows only that it has one.\n\nimport type { ActorDefinition } from \"../actor-types.js\";\nimport { defineActor, defineMessages } from \"../define-actor.js\";\nimport type { Message } from \"../types.js\";\nimport type { Channel } from \"./channel.js\";\n\n/** Write this to the channel. */\nexport interface SendMessage<F> extends Message {\n type: \"SEND\";\n data: F;\n}\n\n/** Ask the channel's own world to act on what is behind it; a medium without one ignores it. */\nexport interface SignalMessage extends Message {\n type: \"SIGNAL\";\n signal: string;\n}\n\n/** A frame the channel delivered. */\nexport interface DataMessage<F> extends Message {\n type: \"DATA\";\n data: F;\n}\n\nexport type PortIn<F> = SendMessage<F> | SignalMessage;\nexport type PortOut<F> = DataMessage<F>;\n\n/** What a parent may read of a port. The ending is here because it is state, not news. */\nexport interface PortPublic<C> {\n /** The channel is open. False before it opens and after it has ended. */\n alive: boolean;\n /** How it ended, or null while it has not. */\n end: C | null;\n /** What the medium knows about itself — a process its pid — as the channel reported it. */\n info: Record<string, unknown>;\n}\n\n/** What a medium's actor is handed: a name, and a way to open the channel it will own. */\nexport interface PortActorSpec<Args, F, C = string> {\n name: string;\n /** Opens the channel this actor owns. Called once, when the actor starts. */\n open(args: Args): Promise<Channel<F, C>>;\n}\n\ntype PortState<F, C> = {\n public: PortPublic<C>;\n private: { channel: Channel<F, C>; args: unknown };\n};\n\n/**\n * An actor over one channel.\n *\n * In `SEND {data}` writes to it and `SIGNAL {signal}` asks the medium to act on what is behind it, through\n * the channel's own `control`; posipaki's `STOP` ends the actor, which closes the channel. Out `DATA {data}`\n * for every frame. The channel's ending is left in `state.end`, what the medium knows about itself in\n * `state.info`, and then the actor stops.\n */\nexport function portActor<Args, F, C = string>(\n spec: PortActorSpec<Args, F, C>,\n): ActorDefinition<Args, PortState<F, C>, PortIn<F>, PortOut<F>, {}> {\n return defineActor({\n name: spec.name,\n inMessages: defineMessages<PortIn<F>>(),\n outMessages: defineMessages<PortOut<F>>(),\n setup: async (args: Args): Promise<PortState<F, C>> => {\n const channel = await spec.open(args);\n return {\n public: { alive: true, end: null, info: channel.info },\n private: { channel, args },\n };\n },\n afterStart() {\n const { channel } = this.state.private;\n channel.onFrame((data) => {\n this.emit({ type: \"DATA\", data });\n });\n channel.onClose((reason) => {\n this.state.public.alive = false;\n this.state.public.end = reason;\n this.exit();\n });\n },\n async beforeEnd() {\n this.state.public.alive = false;\n await this.state.private.channel.close();\n },\n handlers: {\n async SEND(msg: SendMessage<F>) {\n await this.state.private.channel.send(msg.data);\n },\n SIGNAL(msg: SignalMessage) {\n this.state.private.channel.control({ signal: msg.signal });\n },\n },\n });\n}\n","// ── an HTTP request as a port ───────────────────────────────────────────────\n//\n// The response body is the channel's frames, `close()` aborts the request, and the ending is what the response\n// said about itself. A request that never got a response is a rejection, the way a spawner that cannot connect\n// is: a non-2xx, a refused connection, a promise that never settles.\n\nimport type { Channel } from \"./channel.js\";\nimport { held } from \"./held.js\";\n\n/** What a response said when its body was over. */\nexport interface HttpEnd {\n /** The status the far end answered with. The frames that came before it are its body. */\n readonly status: number;\n}\n\nexport interface FetchPortOptions {\n /** Passed to `fetch`: method, headers, body. The request's own signal is not read; `close()` aborts. */\n init?: RequestInit;\n /** How long to wait for the response head. No bound by default: a stream may be long. */\n connectMs?: number;\n}\n\n/**\n * A URL as a channel of the body it answers with.\n *\n * Frames are the chunks of the body as they arrive, so a caller reads a stream rather than waiting for a whole\n * response. `close()` aborts the request, and the far side hears it.\n */\nexport async function fetchPort(url: string, options: FetchPortOptions = {}): Promise<Channel<Uint8Array, HttpEnd>> {\n const controller = new AbortController();\n const response = await answered(url, { ...options.init, signal: controller.signal }, options.connectMs);\n\n const readers = new Set<(frame: Uint8Array) => void>();\n const closers = new Set<(reason: HttpEnd) => void>();\n const failers = new Set<(error: unknown) => void>();\n const ending: HttpEnd = { status: response.status };\n let ended = false;\n\n const end = (): void => {\n if (ended) return;\n ended = true;\n for (const closer of [...closers]) closer(ending);\n };\n\n const frames: Channel<Uint8Array, HttpEnd> = {\n send: () => Promise.reject(new Error(\"a response body has no input; send on the request instead\")),\n onFrame(handler) {\n readers.add(handler);\n return () => {\n readers.delete(handler);\n };\n },\n onClose(handler) {\n if (ended) {\n handler(ending);\n return () => {};\n }\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 async close() {\n controller.abort();\n await reader?.cancel().catch(() => undefined);\n },\n info: { url, status: response.status },\n control: () => {},\n hasFrames: async () => false,\n };\n\n const reader = response.body?.getReader();\n // Wrapped before the first read: a body can deliver a chunk in the same turn the port is handed over.\n const port = held(frames);\n\n const pump = async (): Promise<void> => {\n if (reader === undefined) {\n end();\n return;\n }\n try {\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 } catch (error) {\n for (const failer of [...failers]) failer(error);\n end();\n }\n };\n void pump();\n\n return port;\n}\n\n/** The response head, or a rejection that says why there is none. */\n/**\n * A response that is not a stream: the far end answered, and the answer was a refusal.\n *\n * The status is here as a fact and in the sentence, because a caller that decides what to do next wants the\n * number and a caller reading a log wants the words.\n */\nexport class HttpRefusal extends Error {\n constructor(\n readonly status: number,\n message: string,\n ) {\n super(message);\n this.name = \"HttpRefusal\";\n }\n}\n\nasync function answered(url: string, init: RequestInit, connectMs: number | undefined): Promise<Response> {\n const attempt = fetch(url, init).catch((error: unknown) => Promise.reject(named(url, error)));\n const response =\n connectMs === undefined\n ? await attempt\n : await Promise.race([\n attempt,\n new Promise<never>((_resolve, reject) =>\n setTimeout(() => reject(new Error(`fetchPort: ${url} answered nothing within ${connectMs}ms`)), connectMs),\n ),\n ]);\n\n if (!response.ok) {\n const body = await response.text().catch(() => \"\");\n throw new HttpRefusal(\n response.status,\n `fetchPort: ${url} answered ${response.status}${body === \"\" ? \"\" : `: ${body.slice(0, 200)}`}`,\n );\n }\n return response;\n}\n\nfunction named(url: string, error: unknown): Error {\n return new Error(`fetchPort: ${url}: ${error instanceof Error ? error.message : String(error)}`);\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { g as Part, m as Channel } from "./reactor-
|
|
1
|
+
import { g as Part, m as Channel } from "./reactor-Cl5_rhzB.js";
|
|
2
2
|
import { Readable, Writable } from "node:stream";
|
|
3
3
|
//#region src/port/memory.d.ts
|
|
4
4
|
/** One end of a pair. `fail` is the hook this double adds: it breaks the wire the way a socket would. */
|
|
@@ -112,5 +112,77 @@ declare function duplexChannel<F, C = void>(pair: {
|
|
|
112
112
|
readonly outbound: Channel<F, C>;
|
|
113
113
|
}): Channel<F, C>;
|
|
114
114
|
//#endregion
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
//#region src/port/map.d.ts
|
|
116
|
+
/** What a mapper returns when a value has nothing to say in the other vocabulary. */
|
|
117
|
+
declare const SKIP: unique symbol;
|
|
118
|
+
/**
|
|
119
|
+
* A channel of `I` over a channel of `O`.
|
|
120
|
+
*
|
|
121
|
+
* `down` maps what is sent and `up` maps what arrives; either may answer `SKIP`, and then nothing is written
|
|
122
|
+
* or delivered. Everything else — close, error, info, control — is the channel below's own, forwarded as it
|
|
123
|
+
* is, because this layer carries a vocabulary and not a medium.
|
|
124
|
+
*/
|
|
125
|
+
declare function mapChannel<I, O, C>(inner: Channel<O, C>, down: (value: I) => O | typeof SKIP, up: (value: O) => I | typeof SKIP): Channel<I, C>;
|
|
126
|
+
//#endregion
|
|
127
|
+
//#region src/port/lines.d.ts
|
|
128
|
+
/** A channel of octets, read and written as lines. */
|
|
129
|
+
declare function linesChannel<C = string>(inner: Channel<Uint8Array, C>): Channel<string, C>;
|
|
130
|
+
//#endregion
|
|
131
|
+
//#region src/port/testing.d.ts
|
|
132
|
+
declare const encode: (text: string) => Uint8Array;
|
|
133
|
+
declare const decode: (bytes: Uint8Array) => string;
|
|
134
|
+
/** What was written to an outbound port: the head it was told, the frames, and whether it ended. */
|
|
135
|
+
interface Written {
|
|
136
|
+
head: Record<string, unknown> | null;
|
|
137
|
+
frames: string[];
|
|
138
|
+
ended: boolean;
|
|
139
|
+
}
|
|
140
|
+
/** An outbound port that records instead of writing, so a head is asserted as the command it is. */
|
|
141
|
+
declare function recording(): {
|
|
142
|
+
out: Channel<Uint8Array, void>;
|
|
143
|
+
written: Written;
|
|
144
|
+
};
|
|
145
|
+
/** An inbound port the test writes into, and then ends. */
|
|
146
|
+
declare function incoming(): {
|
|
147
|
+
inbound: Channel<Uint8Array, void>;
|
|
148
|
+
write: (text: string) => void;
|
|
149
|
+
bytes: (chunk: Uint8Array) => void;
|
|
150
|
+
end: () => void;
|
|
151
|
+
};
|
|
152
|
+
//#endregion
|
|
153
|
+
//#region src/port/read.d.ts
|
|
154
|
+
/**
|
|
155
|
+
* The next message on a channel, and no further.
|
|
156
|
+
*
|
|
157
|
+
* Reads of one channel are served in order: what arrived while a caller was waiting goes to the next caller
|
|
158
|
+
* rather than being dropped. What arrived before the first read is not kept here — a channel whose early
|
|
159
|
+
* frames matter is wrapped in `held` where it is made.
|
|
160
|
+
*/
|
|
161
|
+
declare function readMessage<T, C>(channel: Channel<T, C>): Promise<T>;
|
|
162
|
+
/**
|
|
163
|
+
* The next message, or `nothing` when this channel has none and nothing is coming.
|
|
164
|
+
*
|
|
165
|
+
* `nothing` is answered once the channel says it is quiet — `hasFrames()` answering `false` — and a channel that
|
|
166
|
+
* cannot say waits instead, because "no frame now" is not something it can promise. What is already standing on
|
|
167
|
+
* this channel is taken first, so a frame that arrived while nobody was waiting is not lost to the question.
|
|
168
|
+
*/
|
|
169
|
+
declare function readMessageMaybe<T, C>(channel: Channel<T, C>): Promise<T | null>;
|
|
170
|
+
declare function readMessageMaybe<T, C, N>(channel: Channel<T, C>, nothing: N): Promise<T | N>;
|
|
171
|
+
//#endregion
|
|
172
|
+
//#region src/port/sse.d.ts
|
|
173
|
+
/** A channel of lines, read as SSE events: one value per event, the empty ones skipped. */
|
|
174
|
+
declare function sseChannel<C>(parts: Channel<Part, C>): Channel<string, C>;
|
|
175
|
+
/**
|
|
176
|
+
* The other direction: events into a body.
|
|
177
|
+
*
|
|
178
|
+
* An event's lines each become a `data:` field and a blank line ends it — the inverse of `sseChannel`, so a
|
|
179
|
+
* server's answer and a client's reading are one vocabulary over the same bytes.
|
|
180
|
+
*/
|
|
181
|
+
declare function sseWriter<C>(out: Channel<Uint8Array, C>): Channel<string, C>;
|
|
182
|
+
//#endregion
|
|
183
|
+
//#region src/port/node/file/file.d.ts
|
|
184
|
+
/** Where this channel has read up to: every frame it has handed over ends at this offset. */
|
|
185
|
+
declare function fileChannel(path: string): Channel<Uint8Array, string>;
|
|
186
|
+
//#endregion
|
|
187
|
+
export { memoryChannelPair as A, settled as C, splitChannel as D, Announcement as E, ChannelPair as O, all as S, streamChannel as T, HttpRefusal as _, readMessageMaybe as a, Ended as b, encode as c, linesChannel as d, SKIP as f, HttpEnd as g, FetchPortOptions as h, readMessage as i, MemoryEnd as k, incoming as l, duplexChannel as m, sseChannel as n, Written as o, mapChannel as p, sseWriter as r, decode as s, fileChannel as t, recording as u, fetchPort as v, StreamPair as w, Sourced as x, held as y };
|
|
188
|
+
//# sourceMappingURL=index-BQH-ok2x.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-BQH-ok2x.d.ts","names":[],"sources":["../src/port/memory.ts","../src/port/split.ts","../src/port/stream.ts","../src/port/combine.ts","../src/port/held.ts","../src/port/fetch.ts","../src/port/duplex.ts","../src/port/map.ts","../src/port/lines.ts","../src/port/testing.ts","../src/port/read.ts","../src/port/sse.ts","../src/port/node/file/file.ts"],"mappings":";;;;UAUiB,UAAU,WAAW,QAAQ;;EAE5C,KAAK;;UAGU,YAAY;EAC3B,MAAM,UAAU;EAChB,KAAK,UAAU;;;iBAgBD,kBAAkB,MAAM,YAAY;;;;KCJxC,gBAAgB;;;;;;;iBAUZ,aAAa,YAC3B,OAAO,QAAQ,YAAY,IAC3B,YAAW,eACV,QAAQ,MAAM;;;;UC1BA;EACf,MAAM;EACN,OAAO;;;;;;;;;iBAyBO,cAAc,MAAM,aAAa,QAAQ;;;;UC5BxC,QAAQ,GAAG;WACjB,QAAQ;WACR,OAAO;;;UAID,MAAM,GAAG;WACf,QAAQ;WACR,QAAQ;;;;;;;;iBAaH,IAAI,kBAAkB,GAAG,GAAG,SAAS,OAAO,GAAG,QAAQ,GAAG,MAAM,QAAQ,QAAQ,GAAG,IAAI;;;;;;;iBAmEvF,QAAQ,kBAAkB,GAAG,GAC3C,SAAS,OAAO,GAAG,QAAQ,GAAG,MAC7B,QAAQ,QAAQ,GAAG,KAAK,MAAM,GAAG;;;;;;;;;iBCvFpB,KAAK,GAAG,GAAG,OAAO,QAAQ,GAAG,KAAK,QAAQ,GAAG;;;;UCR5C;;WAEN;;UAGM;;EAEf,OAAO;;EAEP;;;;;;;;iBASoB,UAAU,aAAa,UAAS,mBAAwB,QAAQ,QAAQ,YAAY;;;;;;;;cAoF7F,oBAAoB;WAEpB;EADX,YACW,gBACT;;;;;iBCvGY,cAAc,GAAG,UAAU;WAChC,SAAS,QAAQ,GAAG;WACpB,UAAU,QAAQ,GAAG;IAC5B,QAAQ,GAAG;;;;cCNF;;;;;;;;iBASG,WAAW,GAAG,GAAG,GAC/B,OAAO,QAAQ,GAAG,IAClB,OAAO,OAAO,MAAM,WAAW,MAC/B,KAAK,OAAO,MAAM,WAAW,OAC5B,QAAQ,GAAG;;;;iBCTE,aAAa,YAAY,OAAO,QAAQ,YAAY,KAAK,gBAAgB;;;cCN5E,SAAM,iBAAmB;cACzB,SAAM,OAAW;;UAGb;EACf,MAAM;EACN;EACA;;;iBAIc;EAAe,KAAK,QAAQ;EAAmB,SAAS;;;iBAsBxD;EACd,SAAS,QAAQ;EACjB,QAAQ;EACR,QAAQ,OAAO;EACf;;;;;;;;;;;iBCDoB,YAAY,GAAG,GAAG,SAAS,QAAQ,GAAG,KAAK,QAAQ;;;;;;;;iBAcnD,iBAAiB,GAAG,GAAG,SAAS,QAAQ,GAAG,KAAK,QAAQ;iBACxD,iBAAiB,GAAG,GAAG,GAC3C,SAAS,QAAQ,GAAG,IACpB,SAAS,IACR,QAAQ,IAAI;;;;iBClCC,WAAW,GAAG,OAAO,QAAQ,MAAM,KAAK,gBAAgB;;;;;;;iBAwFxD,UAAU,GAAG,KAAK,QAAQ,YAAY,KAAK,gBAAgB;;;;iBCjG3D,YAAY,eAAe,QAAQ"}
|
package/dist/index.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ declare function defineActor<Args, InternalState, InMsg extends Message, OutMsg
|
|
|
20
20
|
//#endregion
|
|
21
21
|
//#region src/version.d.ts
|
|
22
22
|
/** posipaki's release version. Must equal package.json's `version`. */
|
|
23
|
-
declare const LIB_VERSION = "0.
|
|
23
|
+
declare const LIB_VERSION = "0.51.0";
|
|
24
24
|
//#endregion
|
|
25
25
|
export { type ActorConfig, type ActorContext, type ActorDecorated, type ActorDefinition, type ActorPlugin, type ActorReflection, type AnyProcess, AsyncProcess, type AsyncProcessFn, type ExitMessage, type HandlerFn, type HandlerOptions, type HookResult, LIB_VERSION, type Message, type MethodOptions, type PluginTransform, Process, type ProcessCtx, type ProcessFn, type SenderInfo, type SenderOrigin, type SpawnedFrom, type StopMessage, type WithSender, type WithoutSender, asyncify, callHook, chainHook, defineActor, defineMessages, mergeConfigs, propagateError, runDispatch, runDispatchAsync, spawn, spawnAsync, stopPropagation };
|
|
26
26
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { i as runDispatch } from "./util-Brtfgv68.js";
|
|
|
2
2
|
import { a as spawnAsync, i as runDispatchAsync, o as asyncify, t as AsyncProcess } from "./process.async-B1FqZyD1.js";
|
|
3
3
|
import { n as chainHook, o as propagateError, r as mergeConfigs, s as stopPropagation, t as callHook } from "./hooks-CgXhmY4r.js";
|
|
4
4
|
import { n as defineMessages, t as defineActor } from "./define-actor-DR1P1PA5.js";
|
|
5
|
-
import { t as LIB_VERSION } from "./version-
|
|
5
|
+
import { t as LIB_VERSION } from "./version-C6CYx8YW.js";
|
|
6
6
|
//#region src/process.ts
|
|
7
7
|
/** @deprecated Use {@link spawnAsync} instead. */
|
|
8
8
|
function spawn(fn, pname, tp) {
|
package/dist/port/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as portReactor, c as PortIn, d as SendMessage, f as SignalMessage, g as Part, h as ChannelClosed, i as PortReactorOptions, l as PortOut, m as Channel, n as PortPair, o as DataMessage, p as portActor, r as PortReactor, s as PortActorSpec, t as PortListener, u as PortPublic } from "../reactor-
|
|
2
|
-
import {
|
|
3
|
-
export { type Announcement, type Channel, ChannelClosed, type ChannelPair, type DataMessage, type Ended, type FetchPortOptions, type HttpEnd, HttpRefusal, type MemoryEnd, type Part, type PortActorSpec, type PortIn, type PortListener, type PortOut, type PortPair, type PortPublic, type PortReactor, type PortReactorOptions, type SendMessage, type SignalMessage, type Sourced, type StreamPair, all, duplexChannel, fetchPort, held, memoryChannelPair, portActor, portReactor, settled, splitChannel, streamChannel };
|
|
1
|
+
import { a as portReactor, c as PortIn, d as SendMessage, f as SignalMessage, g as Part, h as ChannelClosed, i as PortReactorOptions, l as PortOut, m as Channel, n as PortPair, o as DataMessage, p as portActor, r as PortReactor, s as PortActorSpec, t as PortListener, u as PortPublic } from "../reactor-Cl5_rhzB.js";
|
|
2
|
+
import { A as memoryChannelPair, C as settled, D as splitChannel, E as Announcement, O as ChannelPair, S as all, T as streamChannel, _ as HttpRefusal, a as readMessageMaybe, b as Ended, c as encode, d as linesChannel, f as SKIP, g as HttpEnd, h as FetchPortOptions, i as readMessage, k as MemoryEnd, l as incoming, m as duplexChannel, n as sseChannel, o as Written, p as mapChannel, r as sseWriter, s as decode, t as fileChannel, u as recording, v as fetchPort, w as StreamPair, x as Sourced, y as held } from "../index-BQH-ok2x.js";
|
|
3
|
+
export { type Announcement, type Channel, ChannelClosed, type ChannelPair, type DataMessage, type Ended, type FetchPortOptions, type HttpEnd, HttpRefusal, type MemoryEnd, type Part, type PortActorSpec, type PortIn, type PortListener, type PortOut, type PortPair, type PortPublic, type PortReactor, type PortReactorOptions, SKIP, type SendMessage, type SignalMessage, type Sourced, type StreamPair, type Written, all, decode, duplexChannel, encode, fetchPort, fileChannel, held, incoming, linesChannel, mapChannel, memoryChannelPair, portActor, portReactor, readMessage, readMessageMaybe, recording, settled, splitChannel, sseChannel, sseWriter, streamChannel };
|
package/dist/port/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as held, i as splitChannel, n as fetchPort, r as portActor, t as HttpRefusal } from "../fetch-
|
|
3
|
-
export { ChannelClosed, HttpRefusal, all, duplexChannel, fetchPort, held, memoryChannelPair, portActor, portReactor, settled, splitChannel, streamChannel };
|
|
1
|
+
import { _ as streamChannel, a as readMessageMaybe, c as incoming, d as SKIP, f as mapChannel, g as settled, h as all, i as readMessage, l as recording, m as portReactor, n as sseChannel, o as decode, p as duplexChannel, r as sseWriter, s as encode, t as fileChannel, u as linesChannel, v as memoryChannelPair, y as ChannelClosed } from "../port-CDK2CsXP.js";
|
|
2
|
+
import { a as held, i as splitChannel, n as fetchPort, r as portActor, t as HttpRefusal } from "../fetch-D72Xvuoc.js";
|
|
3
|
+
export { ChannelClosed, HttpRefusal, SKIP, all, decode, duplexChannel, encode, fetchPort, fileChannel, held, incoming, linesChannel, mapChannel, memoryChannelPair, portActor, portReactor, readMessage, readMessageMaybe, recording, settled, splitChannel, sseChannel, sseWriter, streamChannel };
|
package/dist/port/node.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { l as ActorDefinition } from "../hooks-DO-efRM_.js";
|
|
2
|
-
import { c as PortIn, l as PortOut, m as Channel, t as PortListener, u as PortPublic } from "../reactor-
|
|
2
|
+
import { c as PortIn, l as PortOut, m as Channel, t as PortListener, u as PortPublic } from "../reactor-Cl5_rhzB.js";
|
|
3
3
|
import { Server } from "node:http";
|
|
4
4
|
//#region src/port/node/spawn.d.ts
|
|
5
5
|
/** How a process ended: it exited with a code, or a signal terminated it. */
|
|
@@ -38,6 +38,11 @@ interface HttpListener<Meta> extends PortListener<Uint8Array, Uint8Array, Meta>
|
|
|
38
38
|
/** The server itself, for what only a consumer of it can do — a WebSocket upgrade, a second route. */
|
|
39
39
|
readonly server: Server;
|
|
40
40
|
}
|
|
41
|
+
/** What a response says before its body: the status and the headers a handler chose. */
|
|
42
|
+
interface ResponseHead {
|
|
43
|
+
readonly status: number;
|
|
44
|
+
readonly headers?: HeadersInit;
|
|
45
|
+
}
|
|
41
46
|
/**
|
|
42
47
|
* A server whose requests arrive as pairs of ports.
|
|
43
48
|
*
|
|
@@ -73,5 +78,5 @@ declare const Utf8Port: ActorDefinition<ProcessPortArgs, {
|
|
|
73
78
|
};
|
|
74
79
|
}, PortIn<string>, PortOut<string>, {}>;
|
|
75
80
|
//#endregion
|
|
76
|
-
export { BinaryPort, type HttpListener, type HttpListenerOptions, type ProcessEnd, type ProcessPortArgs, type ProcessPorts, type SpawnPortOptions, Utf8Port, httpListener, spawnPort, spawnPorts };
|
|
81
|
+
export { BinaryPort, type HttpListener, type HttpListenerOptions, type ProcessEnd, type ProcessPortArgs, type ProcessPorts, type ResponseHead, type SpawnPortOptions, Utf8Port, httpListener, spawnPort, spawnPorts };
|
|
77
82
|
//# sourceMappingURL=node.d.ts.map
|
package/dist/port/node.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.d.ts","names":[],"sources":["../../src/port/node/spawn.ts","../../src/port/node/listener.ts","../../src/port/node/process.ts"],"mappings":";;;;;KAcY;WACG;WAAuB;;WACvB;WAAyB;;UACvB;EACf;EACA,MAAM,OAAO;;EAEb;;UAEe;WACN,QAAQ,QAAQ,YAAY;WAC5B,QAAQ,QAAQ,YAAY;;;iBAIvB,UACd,mBACA,UAAS,mBACR,QAAQ,QAAQ,YAAY;;iBAIT,WACpB,mBACA,UAAS,mBACR,QAAQ;;;UCxBM,oBAAoB;EACnC;EACA;;EAEA,UAAU,SAAS,UAAU,OAAO,QAAQ;;;UAI7B,aAAa,cAAc,aAAa,YAAY,YAAY;WACtE;;WAEA,QAAQ;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"node.d.ts","names":[],"sources":["../../src/port/node/spawn.ts","../../src/port/node/listener.ts","../../src/port/node/process.ts"],"mappings":";;;;;KAcY;WACG;WAAuB;;WACvB;WAAyB;;UACvB;EACf;EACA,MAAM,OAAO;;EAEb;;UAEe;WACN,QAAQ,QAAQ,YAAY;WAC5B,QAAQ,QAAQ,YAAY;;;iBAIvB,UACd,mBACA,UAAS,mBACR,QAAQ,QAAQ,YAAY;;iBAIT,WACpB,mBACA,UAAS,mBACR,QAAQ;;;UCxBM,oBAAoB;EACnC;EACA;;EAEA,UAAU,SAAS,UAAU,OAAO,QAAQ;;;UAI7B,aAAa,cAAc,aAAa,YAAY,YAAY;WACtE;;WAEA,QAAQ;;;UAIF;WACN;WACA,UAAU;;;;;;;;;iBAUL,aAAa,gBAAgB,UAAS,oBAAoB,QAAa,aAAa;;;;UC9BnF;EACf;EACA;EACA,MAAM,OAAO;;EAEb;;;cAkDW,YAGX,gBAHqB;;;;;;GAGrB,OAAA,WAAA,mBAAA,QAAA,WAAA;;cAGW,UAGX,gBAHmB;;;;;;mBAGnB"}
|
package/dist/port/node.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as held, i as splitChannel, r as portActor, t as HttpRefusal } from "../fetch-
|
|
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
3
|
import { createServer } from "node:http";
|
|
4
4
|
//#region src/port/node/spawn.ts
|
|
@@ -63,7 +63,8 @@ async function spawnPorts(command, options = {}) {
|
|
|
63
63
|
control: (command) => {
|
|
64
64
|
const signal = command.signal;
|
|
65
65
|
if (typeof signal === "string") signalGroup(child, signal);
|
|
66
|
-
}
|
|
66
|
+
},
|
|
67
|
+
hasFrames: async () => false
|
|
67
68
|
};
|
|
68
69
|
const stderr = {
|
|
69
70
|
send: () => Promise.reject(/* @__PURE__ */ new Error("stderr carries the process's output; it has no input")),
|
|
@@ -107,7 +108,8 @@ function bytesOf(stream, label) {
|
|
|
107
108
|
onError: () => () => {},
|
|
108
109
|
close: () => Promise.resolve(),
|
|
109
110
|
info: {},
|
|
110
|
-
control: () => {}
|
|
111
|
+
control: () => {},
|
|
112
|
+
hasFrames: async () => false
|
|
111
113
|
};
|
|
112
114
|
stream?.on("data", (chunk) => {
|
|
113
115
|
const bytes = new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);
|
|
@@ -268,7 +270,8 @@ function requestPort(request) {
|
|
|
268
270
|
method: request.method ?? "GET",
|
|
269
271
|
url: request.url ?? "/"
|
|
270
272
|
},
|
|
271
|
-
control: () => {}
|
|
273
|
+
control: () => {},
|
|
274
|
+
hasFrames: async () => false
|
|
272
275
|
};
|
|
273
276
|
const port = held(inbound);
|
|
274
277
|
request.on("data", (chunk) => {
|
|
@@ -313,6 +316,8 @@ function responsePort(response) {
|
|
|
313
316
|
onFrame() {
|
|
314
317
|
return () => {};
|
|
315
318
|
},
|
|
319
|
+
/** A response carries what is written to it and nothing else: there is never a frame standing. */
|
|
320
|
+
hasFrames: async () => false,
|
|
316
321
|
onClose(handler) {
|
|
317
322
|
if (ended) {
|
|
318
323
|
handler(void 0);
|
|
@@ -388,7 +393,8 @@ function lines(parts) {
|
|
|
388
393
|
},
|
|
389
394
|
close: (reason) => parts.close(reason),
|
|
390
395
|
info: parts.info,
|
|
391
|
-
control: (command) => parts.control(command)
|
|
396
|
+
control: (command) => parts.control(command),
|
|
397
|
+
hasFrames: async () => false
|
|
392
398
|
};
|
|
393
399
|
}
|
|
394
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. 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 };\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 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 };\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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"}
|