posipaki 0.49.2 → 0.50.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-DBJeP47R.js} +110 -2
- package/dist/fetch-DBJeP47R.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/port/index.js +2 -2
- package/dist/port/node.d.ts +5 -1
- package/dist/port/node.d.ts.map +1 -1
- package/dist/port/node.js +101 -80
- package/dist/port/node.js.map +1 -1
- package/dist/{port-DLpNfoyX.js → port-ByBSeVPm.js} +2 -111
- package/dist/port-ByBSeVPm.js.map +1 -0
- package/dist/process-eCAv2tIS.d.ts.map +1 -1
- package/dist/remote/index.d.ts +26 -1
- 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 +1 -1
- package/dist/remote/node.js +3 -3
- package/dist/types-U3NvKgpg.d.ts.map +1 -1
- package/dist/{version-De-M910s.js → version-_BGyK0ZQ.js} +2 -2
- package/dist/{version-De-M910s.js.map → version-_BGyK0ZQ.js.map} +1 -1
- package/dist/{websocket-Dm2eqDOA.js → websocket-Dqv9GfDy.js} +2 -2
- package/dist/{websocket-Dm2eqDOA.js.map → websocket-Dqv9GfDy.js.map} +1 -1
- package/package.json +1 -1
- package/dist/actor-B5EJRlY8.js.map +0 -1
- package/dist/port-DLpNfoyX.js.map +0 -1
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { r as held } from "./actor-B5EJRlY8.js";
|
|
2
1
|
//#region src/port/channel.ts
|
|
3
2
|
/** What is thrown when a frame is sent on a channel that is over. */
|
|
4
3
|
var ChannelClosed = class extends Error {
|
|
@@ -342,114 +341,6 @@ function settled(sources) {
|
|
|
342
341
|
};
|
|
343
342
|
}
|
|
344
343
|
//#endregion
|
|
345
|
-
//#region src/port/fetch.ts
|
|
346
|
-
/**
|
|
347
|
-
* A URL as a channel of the body it answers with.
|
|
348
|
-
*
|
|
349
|
-
* Frames are the chunks of the body as they arrive, so a caller reads a stream rather than waiting for a whole
|
|
350
|
-
* response. `close()` aborts the request, and the far side hears it.
|
|
351
|
-
*/
|
|
352
|
-
async function fetchPort(url, options = {}) {
|
|
353
|
-
const controller = new AbortController();
|
|
354
|
-
const response = await answered(url, {
|
|
355
|
-
...options.init,
|
|
356
|
-
signal: controller.signal
|
|
357
|
-
}, options.connectMs);
|
|
358
|
-
const readers = /* @__PURE__ */ new Set();
|
|
359
|
-
const closers = /* @__PURE__ */ new Set();
|
|
360
|
-
const failers = /* @__PURE__ */ new Set();
|
|
361
|
-
const ending = { status: response.status };
|
|
362
|
-
let ended = false;
|
|
363
|
-
const end = () => {
|
|
364
|
-
if (ended) return;
|
|
365
|
-
ended = true;
|
|
366
|
-
for (const closer of [...closers]) closer(ending);
|
|
367
|
-
};
|
|
368
|
-
const frames = {
|
|
369
|
-
send: () => Promise.reject(/* @__PURE__ */ new Error("a response body has no input; send on the request instead")),
|
|
370
|
-
onFrame(handler) {
|
|
371
|
-
readers.add(handler);
|
|
372
|
-
return () => {
|
|
373
|
-
readers.delete(handler);
|
|
374
|
-
};
|
|
375
|
-
},
|
|
376
|
-
onClose(handler) {
|
|
377
|
-
if (ended) {
|
|
378
|
-
handler(ending);
|
|
379
|
-
return () => {};
|
|
380
|
-
}
|
|
381
|
-
closers.add(handler);
|
|
382
|
-
return () => {
|
|
383
|
-
closers.delete(handler);
|
|
384
|
-
};
|
|
385
|
-
},
|
|
386
|
-
onError(handler) {
|
|
387
|
-
failers.add(handler);
|
|
388
|
-
return () => {
|
|
389
|
-
failers.delete(handler);
|
|
390
|
-
};
|
|
391
|
-
},
|
|
392
|
-
async close() {
|
|
393
|
-
controller.abort();
|
|
394
|
-
await reader?.cancel().catch(() => void 0);
|
|
395
|
-
},
|
|
396
|
-
info: {
|
|
397
|
-
url,
|
|
398
|
-
status: response.status
|
|
399
|
-
},
|
|
400
|
-
control: () => {}
|
|
401
|
-
};
|
|
402
|
-
const reader = response.body?.getReader();
|
|
403
|
-
const port = held(frames);
|
|
404
|
-
const pump = async () => {
|
|
405
|
-
if (reader === void 0) {
|
|
406
|
-
end();
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
try {
|
|
410
|
-
for (;;) {
|
|
411
|
-
const chunk = await reader.read();
|
|
412
|
-
if (chunk.done) {
|
|
413
|
-
end();
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
for (const target of [...readers]) target(chunk.value);
|
|
417
|
-
}
|
|
418
|
-
} catch (error) {
|
|
419
|
-
for (const failer of [...failers]) failer(error);
|
|
420
|
-
end();
|
|
421
|
-
}
|
|
422
|
-
};
|
|
423
|
-
pump();
|
|
424
|
-
return port;
|
|
425
|
-
}
|
|
426
|
-
/** The response head, or a rejection that says why there is none. */
|
|
427
|
-
/**
|
|
428
|
-
* A response that is not a stream: the far end answered, and the answer was a refusal.
|
|
429
|
-
*
|
|
430
|
-
* The status is here as a fact and in the sentence, because a caller that decides what to do next wants the
|
|
431
|
-
* number and a caller reading a log wants the words.
|
|
432
|
-
*/
|
|
433
|
-
var HttpRefusal = class extends Error {
|
|
434
|
-
constructor(status, message) {
|
|
435
|
-
super(message);
|
|
436
|
-
this.status = status;
|
|
437
|
-
this.name = "HttpRefusal";
|
|
438
|
-
}
|
|
439
|
-
};
|
|
440
|
-
async function answered(url, init, connectMs) {
|
|
441
|
-
const attempt = fetch(url, init).catch((error) => Promise.reject(named(url, error)));
|
|
442
|
-
const response = connectMs === void 0 ? await attempt : await Promise.race([attempt, new Promise((_resolve, reject) => setTimeout(() => reject(/* @__PURE__ */ new Error(`fetchPort: ${url} answered nothing within ${connectMs}ms`)), connectMs))]);
|
|
443
|
-
if (!response.ok) {
|
|
444
|
-
const body = await response.text().catch(() => "");
|
|
445
|
-
throw new HttpRefusal(response.status, `fetchPort: ${url} answered ${response.status}${body === "" ? "" : `: ${body.slice(0, 200)}`}`);
|
|
446
|
-
}
|
|
447
|
-
return response;
|
|
448
|
-
}
|
|
449
|
-
function named(url, error) {
|
|
450
|
-
return /* @__PURE__ */ new Error(`fetchPort: ${url}: ${error instanceof Error ? error.message : String(error)}`);
|
|
451
|
-
}
|
|
452
|
-
//#endregion
|
|
453
344
|
//#region src/port/reactor.ts
|
|
454
345
|
/**
|
|
455
346
|
* One handler per accepted pair, in arrival order.
|
|
@@ -519,6 +410,6 @@ function duplexChannel(pair) {
|
|
|
519
410
|
};
|
|
520
411
|
}
|
|
521
412
|
//#endregion
|
|
522
|
-
export {
|
|
413
|
+
export { streamChannel as a, settled as i, portReactor as n, memoryChannelPair as o, all as r, ChannelClosed as s, duplexChannel as t };
|
|
523
414
|
|
|
524
|
-
//# sourceMappingURL=port-
|
|
415
|
+
//# sourceMappingURL=port-ByBSeVPm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"port-ByBSeVPm.js","names":[],"sources":["../src/port/channel.ts","../src/port/memory.ts","../src/port/stream.ts","../src/port/combine.ts","../src/port/reactor.ts","../src/port/duplex.ts"],"sourcesContent":["// ── A channel ───────────────────────────────────────────────────────────────\n//\n// Carries values of one type between two ends. The type parameter is what crosses: bytes, a line and the\n// octets it announced, a decoded frame. A medium produces one; a protocol adapter wraps one into another.\n//\n// It ends one of three ways, and each has its own callback:\n//\n// close() this side ends it; the far end hears a close, this side's callers hear nothing\n// onClose the far end is gone, with the reason it gave\n// onError the wire failed; once, and always before onClose\n//\n// `close()` is idempotent. `send()` rejects once the channel is over, from either side.\n\n/**\n * A line of a channel that carries lines, and the octets it announced.\n *\n * A line may announce octets after it, as a stream chunk does on the remote seam, so a part is the line and\n * what it announced. A medium that moves whole messages, a websocket for instance, produces parts with no\n * octets. A byte medium produces none until the splitter has run.\n */\nexport interface Part {\n /** One line, without its terminator. */\n line: string;\n /** The octets the line announced, when it announced any. */\n bytes: Uint8Array | null;\n}\n\n/**\n * Values of one type, between two ends.\n *\n * `C` is what the far end says when it closes — a sentence for a medium, or whatever the medium has to say for\n * itself: a spawned process ends with an exit status, and that is the fact worth having. `close()` takes a\n * sentence from this side, because the close we ask for is ours.\n */\nexport interface Channel<F, C = string> {\n /**\n * Put a frame on it.\n *\n * Rejects when the channel is over, from either side: there is no reader left, and a resolved promise\n * would say the frame had been taken.\n */\n send(frame: F): Promise<void>;\n /** A frame arrived. The returned function stops this reader and no other. */\n onFrame(handler: (frame: F) => void): () => void;\n /** The far end closed, and this is what it said. Once, after any frames already read. */\n onClose(handler: (reason: C) => void): () => void;\n /** The wire failed. Once, and always followed by a close. */\n onError(handler: (error: unknown) => void): () => void;\n /**\n * Close it from this side.\n *\n * Idempotent: a second call does nothing. The far end hears a close; the callers on this side do not,\n * because they are the ones who asked.\n */\n close(reason?: string): Promise<void>;\n /** What the medium knows about itself: a process its pid, a socket its peer. Empty when it knows nothing. */\n readonly info: Record<string, unknown>;\n /**\n * Ask the medium to act on what is behind the channel — signal a process, resize a terminal.\n *\n * A medium with nothing behind it does nothing. The command is the medium's own vocabulary; the channel\n * only carries it.\n */\n control(command: Record<string, unknown>): void;\n}\n\n/** What is thrown when a frame is sent on a channel that is over. */\nexport class ChannelClosed extends Error {\n constructor(reason: string) {\n super(`the channel is closed: ${reason}`);\n this.name = \"ChannelClosed\";\n }\n}\n","// ── A channel with no medium ────────────────────────────────────────────────\n//\n// Two ends over nothing: what one sends, the other reads. For testing an adapter or a protocol without a\n// socket, a child or a file, and for exercising the endings without faking one.\n//\n// No buffer: a frame reaches the other end before `send` settles.\n\nimport { ChannelClosed, type Channel } from \"./channel.js\";\n\n/** One end of a pair. `fail` is the hook this double adds: it breaks the wire the way a socket would. */\nexport interface MemoryEnd<F> extends Channel<F> {\n /** Break the wire: the far end hears an error, and then a close. */\n fail(error: unknown): void;\n}\n\nexport interface ChannelPair<F> {\n near: MemoryEnd<F>;\n far: MemoryEnd<F>;\n}\n\ntype Side = \"near\" | \"far\";\n\ninterface End<F> {\n frames: Set<(frame: F) => void>;\n closes: Set<(reason: string) => void>;\n errors: Set<(error: unknown) => void>;\n}\n\nfunction end<F>(): End<F> {\n return { frames: new Set(), closes: new Set(), errors: new Set() };\n}\n\n/** The two halves of one channel. */\nexport function memoryChannelPair<F>(): ChannelPair<F> {\n const near = end<F>();\n const far = end<F>();\n const closed: Record<Side, string | null> = { near: null, far: null };\n const other = (side: Side): Side => (side === \"near\" ? \"far\" : \"near\");\n\n /** One side's own state and the other side's, which is all a channel is. */\n const make = (side: Side): MemoryEnd<F> => {\n const mine = side === \"near\" ? near : far;\n const theirs = side === \"near\" ? far : near;\n return {\n send: async (frame: F) => {\n const ended = closed[side] ?? closed[other(side)];\n if (ended !== null) throw new ChannelClosed(ended);\n for (const handler of [...theirs.frames]) handler(frame);\n },\n onFrame: (handler) => {\n mine.frames.add(handler);\n return () => {\n mine.frames.delete(handler);\n };\n },\n onClose: (handler) => {\n mine.closes.add(handler);\n return () => {\n mine.closes.delete(handler);\n };\n },\n onError: (handler) => {\n mine.errors.add(handler);\n return () => {\n mine.errors.delete(handler);\n };\n },\n close: async (reason = \"closed here\") => {\n if (closed[side] !== null) return;\n const peer = other(side);\n closed[side] = reason;\n if (closed[peer] !== null) return;\n closed[peer] = reason;\n for (const handler of [...theirs.closes]) handler(reason);\n },\n info: {},\n control: () => {},\n fail: (error: unknown) => {\n if (closed[side] !== null) return;\n const peer = other(side);\n if (closed[peer] !== null) return;\n for (const handler of [...theirs.errors]) handler(error);\n for (const handler of [...theirs.closes]) handler(\"the wire failed\");\n closed[side] = \"the wire failed\";\n closed[peer] = \"the wire failed\";\n },\n };\n };\n\n return { near: make(\"near\"), far: make(\"far\") };\n}\n","// ── a stream pair as a channel of bytes ────────────────────────────────\n//\n// Chunks out as they arrive, chunks in as they are written, and an ending. Framing, splitting and the meaning\n// of a frame compose on top of this.\n//\n// A send settles when the stream has flushed the chunk, not when it has accepted it into a buffer, so a writer\n// cannot run ahead of the pipe without waiting.\n//\n// Two of the three endings are discovered here rather than declared: a read side that ends is a peer that\n// closed, and a broken stream is a wire that failed.\n\nimport type { Readable, Writable } from \"node:stream\";\n\nimport { ChannelClosed, type Channel } from \"./channel.js\";\n\n/** What the channel talks over: something to read from, something to write to. */\nexport interface StreamPair {\n read: Readable;\n write: Writable;\n}\n\n/** A write error that means the peer has already hung up. */\nfunction peerGone(code: unknown): boolean {\n return (\n code === \"EPIPE\" ||\n code === \"ERR_STREAM_DESTROYED\" ||\n code === \"ERR_STREAM_WRITE_AFTER_END\" ||\n code === \"ERR_STREAM_ALREADY_FINISHED\"\n );\n}\n\nfunction asBytes(chunk: Buffer | string): Uint8Array {\n if (typeof chunk === \"string\") return new Uint8Array(Buffer.from(chunk, \"utf8\"));\n return new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);\n}\n\n/**\n * A channel of bytes over a stream pair.\n *\n * `close()` ends the write side, so the far end hears an end of stream, and destroys the read side, so no\n * more chunks arrive after this side has said it is done. A peer that goes away is a close of this side's\n * own readers, and the reason says which of the two it was.\n */\nexport function streamChannel(pair: StreamPair): Channel<Uint8Array> {\n const frames = new Set<(frame: Uint8Array) => void>();\n const closers = new Set<(reason: string) => void>();\n const failers = new Set<(error: unknown) => void>();\n /** What this side's readers were told, and null while they have been told nothing. */\n let reported: string | null = null;\n let ended = false;\n\n const release = (): void => {\n // Best effort: a stream whose peer is gone throws from `destroy` and from `end`, and teardown is not\n // the place to find out.\n try {\n pair.read.destroy();\n } catch {\n /* already gone */\n }\n try {\n if (!pair.write.writableEnded) pair.write.end();\n } catch {\n /* already gone */\n }\n };\n\n /** End it from this side: the far end hears it, and this side's readers do not — they asked. */\n const shut = (): void => {\n if (ended) return;\n ended = true;\n release();\n };\n\n /** The far end is gone, and this side's readers hear it. */\n const finish = (reason: string): void => {\n if (ended) return;\n ended = true;\n reported = reason;\n release();\n for (const closer of [...closers]) closer(reason);\n };\n\n /** The wire failed: this side hears the error once, and then a close. */\n const fail = (error: unknown): void => {\n if (ended) return;\n ended = true;\n reported = \"the wire failed\";\n release();\n for (const failer of [...failers]) failer(error);\n for (const closer of [...closers]) closer(\"the wire failed\");\n };\n\n pair.read.on(\"data\", (chunk: Buffer | string) => {\n if (ended) return;\n const bytes = asBytes(chunk);\n for (const reader of [...frames]) reader(bytes);\n });\n pair.read.on(\"end\", () => finish(\"the far end closed\"));\n // A stream destroyed without an end — a socket whose peer vanished — says so only here.\n pair.read.on(\"close\", () => finish(\"the far end closed\"));\n pair.read.on(\"error\", (error: Error) => fail(error));\n pair.write.on(\"error\", (error: Error) => fail(error));\n\n const send = (frame: Uint8Array): Promise<void> =>\n new Promise<void>((resolve, reject) => {\n if (ended) {\n reject(new ChannelClosed(reported ?? \"closed here\"));\n return;\n }\n const done = (err?: Error | null): void => {\n if (err === undefined || err === null) {\n resolve();\n return;\n }\n if (peerGone((err as { code?: unknown }).code)) {\n finish(\"the peer is gone\");\n reject(new ChannelClosed(\"the peer is gone\"));\n return;\n }\n fail(err);\n reject(err);\n };\n try {\n pair.write.write(frame, done);\n } catch (err) {\n fail(err);\n reject(err);\n }\n });\n\n return {\n send,\n onFrame: (handler) => {\n frames.add(handler);\n return () => {\n frames.delete(handler);\n };\n },\n onClose: (handler) => {\n if (reported !== null) {\n handler(reported);\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: () => {\n shut();\n return Promise.resolve();\n },\n info: {},\n control: () => {},\n };\n}\n","// ── several channels as one ─────────────────────────────────────────────────\n//\n// Two combinators, named after the promise pair because the choice is the same one: do you stop at the first\n// ending, or do you wait for every channel to say how it ended?\n//\n// `all` is the first: a frame from any source arrives tagged with the source, and the first ending ends the\n// combined channel with that source's reason. `settled` is the second: every ending arrives as a frame of its\n// own, tagged the same way, and the combined channel closes once all of them have.\n//\n// Neither sends. A combined channel is made of others, so a frame put on it has no destination; `send`\n// rejects and says so, and `close` closes every source.\n\nimport type { Channel } from \"./channel.js\";\n\n/** A frame, and which of the combined channels it arrived on. */\nexport interface Sourced<T, F> {\n readonly source: T;\n readonly frame: F;\n}\n\n/** An ending, and which of the combined channels ended. */\nexport interface Ended<T, C> {\n readonly source: T;\n readonly ending: C;\n}\n\nfunction sendRefusal<T>(sources: T[]): Error {\n return new Error(`this channel is made of ${sources.length}: send on one of them, not on the combination`);\n}\n\n/**\n * Every frame from every source, tagged; the first ending ends it.\n *\n * The ending is the first one to arrive, and the others are ignored afterwards. This is what a process wants\n * by default: its stdout ending means the process is gone, and that is the news.\n */\nexport function all<T extends string, F, C>(sources: Record<T, Channel<F, C>>): Channel<Sourced<T, F>, C> {\n const names = Object.keys(sources) as T[];\n const readers = new Set<(frame: Sourced<T, F>) => void>();\n const closers = new Set<(reason: C) => void>();\n const failers = new Set<(error: unknown) => void>();\n let ended = false;\n /** What the first ending said, for a reader that subscribes after it. */\n let ending: C | undefined;\n\n const end = (reason: C): void => {\n if (ended) return;\n ended = true;\n ending = reason;\n for (const closer of [...closers]) closer(reason);\n };\n\n for (const name of names) {\n const source = sources[name];\n source.onFrame((frame) => {\n for (const reader of [...readers]) reader({ source: name, frame });\n });\n source.onClose(end);\n source.onError((error) => {\n for (const failer of [...failers]) failer(error);\n });\n }\n\n return {\n send: () => Promise.reject(sendRefusal(names)),\n onFrame(handler) {\n readers.add(handler);\n return () => {\n readers.delete(handler);\n };\n },\n onClose(handler) {\n if (ending !== undefined) {\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(reason?: string) {\n ended = true;\n await Promise.all(names.map((name) => sources[name].close(reason)));\n },\n info: {},\n control: () => {},\n };\n}\n\n/**\n * Every frame from every source, tagged, and every ending as a frame of its own.\n *\n * It closes once all of them have ended, and that close carries nothing: the endings already arrived as frames,\n * each one saying which source it belongs to.\n */\nexport function settled<T extends string, F, C>(\n sources: Record<T, Channel<F, C>>,\n): Channel<Sourced<T, F> | Ended<T, C>, void> {\n const names = Object.keys(sources) as T[];\n const readers = new Set<(frame: Sourced<T, F> | Ended<T, C>) => void>();\n const closers = new Set<(reason: void) => void>();\n const failers = new Set<(error: unknown) => void>();\n let remaining = names.length;\n let ended = false;\n\n const emit = (frame: Sourced<T, F> | Ended<T, C>): void => {\n for (const reader of [...readers]) reader(frame);\n };\n\n const end = (): void => {\n if (ended) return;\n ended = true;\n for (const closer of [...closers]) closer(undefined);\n };\n\n for (const name of names) {\n const source = sources[name];\n source.onFrame((frame) => emit({ source: name, frame }));\n source.onClose((ending) => {\n emit({ source: name, ending });\n remaining -= 1;\n if (remaining === 0) end();\n });\n source.onError((error) => {\n for (const failer of [...failers]) failer(error);\n });\n }\n\n return {\n send: () => Promise.reject(sendRefusal(names)),\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(handler) {\n failers.add(handler);\n return () => {\n failers.delete(handler);\n };\n },\n async close(reason?: string) {\n ended = true;\n await Promise.all(names.map((name) => sources[name].close(reason)));\n },\n info: {},\n control: () => {},\n };\n}\n","// ── the loop, at the port level ─────────────────────────────────────────────\n//\n// A listener yields a pair of ports per accept, and one handler runs on that pair. The same loop the connection\n// reactor runs, one level down: there the unit is a connection that speaks a protocol, here it is a pair of\n// channels and nothing above them.\n\nimport type { Channel } from \"./channel.js\";\n\n/** One accepted exchange: what the far side sends, and what we send back. */\nexport interface PortPair<In, Out, Meta = unknown, C = void> {\n readonly inbound: Channel<In, C>;\n readonly outbound: Channel<Out, C>;\n /** Whatever the listener resolved about the caller: admission, a path, a token's subject. */\n readonly meta: Meta;\n}\n\n/** Where pairs arrive. The listener owns the medium and its admission. */\nexport interface PortListener<In, Out, Meta = unknown, C = void> {\n accept(): Promise<PortPair<In, Out, Meta, C>>;\n close(): Promise<void>;\n}\n\nexport interface PortReactorOptions<In, Out, Meta, C = void> {\n /** Called once a pair has been accepted and its handler has been given it. */\n onSession?(pair: PortPair<In, Out, Meta, C>): void;\n /** Called when a session is over, with whatever the handler threw. */\n onClosed?(pair: PortPair<In, Out, Meta, C>, error: unknown): void;\n /** A handler that throws is reported here, and the loop goes on. */\n onError?(error: unknown): void;\n}\n\nexport interface PortReactor {\n /** How many handlers are running now. */\n readonly live: number;\n /** Close the listener, stop accepting, and wait for the handlers to finish. */\n stop(): Promise<void>;\n}\n\n/**\n * One handler per accepted pair, in arrival order.\n *\n * The loop ends when the listener's `accept()` rejects, which a closed listener does. A handler that throws is\n * reported and its session ends; the loop carries on with the next pair.\n */\nexport function portReactor<In, Out, Meta, C = void>(\n listener: PortListener<In, Out, Meta, C>,\n handle: (pair: PortPair<In, Out, Meta, C>) => void | Promise<void>,\n options: PortReactorOptions<In, Out, Meta, C> = {},\n): PortReactor {\n const running = new Set<Promise<void>>();\n let stopped = false;\n\n const serve = (pair: PortPair<In, Out, Meta, C>): void => {\n const session = (async () => {\n options.onSession?.(pair);\n try {\n await handle(pair);\n } catch (error) {\n options.onError?.(error);\n options.onClosed?.(pair, error);\n return;\n }\n options.onClosed?.(pair, undefined);\n })();\n running.add(session);\n void session.finally(() => running.delete(session));\n };\n\n const loop = (async () => {\n for (;;) {\n let pair: PortPair<In, Out, Meta, C>;\n try {\n pair = await listener.accept();\n } catch {\n return;\n }\n if (stopped) return;\n serve(pair);\n }\n })();\n\n return {\n get live() {\n return running.size;\n },\n async stop() {\n stopped = true;\n await listener.close();\n await loop;\n await Promise.all([...running]);\n },\n };\n}\n","// ── a pair of channels as one ───────────────────────────────────────────────\n//\n// A medium that has an inbound side and an outbound side — a request and its answer, a socket's reader and its\n// writer — arrives as two channels. A channel has one frame type, so this is only for a pair whose two sides\n// carry the same one: frames arrive from the inbound side, `send` writes to the outbound side, `close()` ends it.\n//\n// The ending is the inbound side's: when the far side has sent everything, this channel closes with whatever that\n// side said. Closing it here closes the outbound side, which is how an answer is finished.\n\nimport type { Channel } from \"./channel.js\";\n\n/** A pair whose two sides carry the same frame type, read as one channel. */\nexport function duplexChannel<F, C = void>(pair: {\n readonly inbound: Channel<F, C>;\n readonly outbound: Channel<F, C>;\n}): Channel<F, C> {\n return {\n send: (frame) => pair.outbound.send(frame),\n onFrame: (handler) => pair.inbound.onFrame(handler),\n onClose: (handler) => pair.inbound.onClose(handler),\n onError: (handler) => pair.inbound.onError(handler),\n close: async (reason) => {\n await pair.outbound.close(reason);\n },\n info: { ...pair.inbound.info, ...pair.outbound.info },\n control: (command) => pair.inbound.control(command),\n };\n}\n"],"mappings":";;AAmEA,IAAa,gBAAb,cAAmC,MAAM;CACvC,YAAY,QAAgB;EAC1B,MAAM,0BAA0B,QAAQ;EACxC,KAAK,OAAO;CACd;AACF;;;AC5CA,SAAS,MAAiB;CACxB,OAAO;EAAE,wBAAQ,IAAI,IAAI;EAAG,wBAAQ,IAAI,IAAI;EAAG,wBAAQ,IAAI,IAAI;CAAE;AACnE;;AAGA,SAAgB,oBAAuC;CACrD,MAAM,OAAO,IAAO;CACpB,MAAM,MAAM,IAAO;CACnB,MAAM,SAAsC;EAAE,MAAM;EAAM,KAAK;CAAK;CACpE,MAAM,SAAS,SAAsB,SAAS,SAAS,QAAQ;;CAG/D,MAAM,QAAQ,SAA6B;EACzC,MAAM,OAAO,SAAS,SAAS,OAAO;EACtC,MAAM,SAAS,SAAS,SAAS,MAAM;EACvC,OAAO;GACL,MAAM,OAAO,UAAa;IACxB,MAAM,QAAQ,OAAO,SAAS,OAAO,MAAM,IAAI;IAC/C,IAAI,UAAU,MAAM,MAAM,IAAI,cAAc,KAAK;IACjD,KAAK,MAAM,WAAW,CAAC,GAAG,OAAO,MAAM,GAAG,QAAQ,KAAK;GACzD;GACA,UAAU,YAAY;IACpB,KAAK,OAAO,IAAI,OAAO;IACvB,aAAa;KACX,KAAK,OAAO,OAAO,OAAO;IAC5B;GACF;GACA,UAAU,YAAY;IACpB,KAAK,OAAO,IAAI,OAAO;IACvB,aAAa;KACX,KAAK,OAAO,OAAO,OAAO;IAC5B;GACF;GACA,UAAU,YAAY;IACpB,KAAK,OAAO,IAAI,OAAO;IACvB,aAAa;KACX,KAAK,OAAO,OAAO,OAAO;IAC5B;GACF;GACA,OAAO,OAAO,SAAS,kBAAkB;IACvC,IAAI,OAAO,UAAU,MAAM;IAC3B,MAAM,OAAO,MAAM,IAAI;IACvB,OAAO,QAAQ;IACf,IAAI,OAAO,UAAU,MAAM;IAC3B,OAAO,QAAQ;IACf,KAAK,MAAM,WAAW,CAAC,GAAG,OAAO,MAAM,GAAG,QAAQ,MAAM;GAC1D;GACA,MAAM,CAAC;GACP,eAAe,CAAC;GAChB,OAAO,UAAmB;IACxB,IAAI,OAAO,UAAU,MAAM;IAC3B,MAAM,OAAO,MAAM,IAAI;IACvB,IAAI,OAAO,UAAU,MAAM;IAC3B,KAAK,MAAM,WAAW,CAAC,GAAG,OAAO,MAAM,GAAG,QAAQ,KAAK;IACvD,KAAK,MAAM,WAAW,CAAC,GAAG,OAAO,MAAM,GAAG,QAAQ,iBAAiB;IACnE,OAAO,QAAQ;IACf,OAAO,QAAQ;GACjB;EACF;CACF;CAEA,OAAO;EAAE,MAAM,KAAK,MAAM;EAAG,KAAK,KAAK,KAAK;CAAE;AAChD;;;;ACpEA,SAAS,SAAS,MAAwB;CACxC,OACE,SAAS,WACT,SAAS,0BACT,SAAS,gCACT,SAAS;AAEb;AAEA,SAAS,QAAQ,OAAoC;CACnD,IAAI,OAAO,UAAU,UAAU,OAAO,IAAI,WAAW,OAAO,KAAK,OAAO,MAAM,CAAC;CAC/E,OAAO,IAAI,WAAW,MAAM,QAAQ,MAAM,YAAY,MAAM,UAAU;AACxE;;;;;;;;AASA,SAAgB,cAAc,MAAuC;CACnE,MAAM,yBAAS,IAAI,IAAiC;CACpD,MAAM,0BAAU,IAAI,IAA8B;CAClD,MAAM,0BAAU,IAAI,IAA8B;;CAElD,IAAI,WAA0B;CAC9B,IAAI,QAAQ;CAEZ,MAAM,gBAAsB;EAG1B,IAAI;GACF,KAAK,KAAK,QAAQ;EACpB,QAAQ,CAER;EACA,IAAI;GACF,IAAI,CAAC,KAAK,MAAM,eAAe,KAAK,MAAM,IAAI;EAChD,QAAQ,CAER;CACF;;CAGA,MAAM,aAAmB;EACvB,IAAI,OAAO;EACX,QAAQ;EACR,QAAQ;CACV;;CAGA,MAAM,UAAU,WAAyB;EACvC,IAAI,OAAO;EACX,QAAQ;EACR,WAAW;EACX,QAAQ;EACR,KAAK,MAAM,UAAU,CAAC,GAAG,OAAO,GAAG,OAAO,MAAM;CAClD;;CAGA,MAAM,QAAQ,UAAyB;EACrC,IAAI,OAAO;EACX,QAAQ;EACR,WAAW;EACX,QAAQ;EACR,KAAK,MAAM,UAAU,CAAC,GAAG,OAAO,GAAG,OAAO,KAAK;EAC/C,KAAK,MAAM,UAAU,CAAC,GAAG,OAAO,GAAG,OAAO,iBAAiB;CAC7D;CAEA,KAAK,KAAK,GAAG,SAAS,UAA2B;EAC/C,IAAI,OAAO;EACX,MAAM,QAAQ,QAAQ,KAAK;EAC3B,KAAK,MAAM,UAAU,CAAC,GAAG,MAAM,GAAG,OAAO,KAAK;CAChD,CAAC;CACD,KAAK,KAAK,GAAG,aAAa,OAAO,oBAAoB,CAAC;CAEtD,KAAK,KAAK,GAAG,eAAe,OAAO,oBAAoB,CAAC;CACxD,KAAK,KAAK,GAAG,UAAU,UAAiB,KAAK,KAAK,CAAC;CACnD,KAAK,MAAM,GAAG,UAAU,UAAiB,KAAK,KAAK,CAAC;CAEpD,MAAM,QAAQ,UACZ,IAAI,SAAe,SAAS,WAAW;EACrC,IAAI,OAAO;GACT,OAAO,IAAI,cAAc,YAAY,aAAa,CAAC;GACnD;EACF;EACA,MAAM,QAAQ,QAA6B;GACzC,IAAI,QAAQ,KAAA,KAAa,QAAQ,MAAM;IACrC,QAAQ;IACR;GACF;GACA,IAAI,SAAU,IAA2B,IAAI,GAAG;IAC9C,OAAO,kBAAkB;IACzB,OAAO,IAAI,cAAc,kBAAkB,CAAC;IAC5C;GACF;GACA,KAAK,GAAG;GACR,OAAO,GAAG;EACZ;EACA,IAAI;GACF,KAAK,MAAM,MAAM,OAAO,IAAI;EAC9B,SAAS,KAAK;GACZ,KAAK,GAAG;GACR,OAAO,GAAG;EACZ;CACF,CAAC;CAEH,OAAO;EACL;EACA,UAAU,YAAY;GACpB,OAAO,IAAI,OAAO;GAClB,aAAa;IACX,OAAO,OAAO,OAAO;GACvB;EACF;EACA,UAAU,YAAY;GACpB,IAAI,aAAa,MAAM;IACrB,QAAQ,QAAQ;IAChB,aAAa,CAAC;GAChB;GACA,QAAQ,IAAI,OAAO;GACnB,aAAa;IACX,QAAQ,OAAO,OAAO;GACxB;EACF;EACA,UAAU,YAAY;GACpB,QAAQ,IAAI,OAAO;GACnB,aAAa;IACX,QAAQ,OAAO,OAAO;GACxB;EACF;EACA,aAAa;GACX,KAAK;GACL,OAAO,QAAQ,QAAQ;EACzB;EACA,MAAM,CAAC;EACP,eAAe,CAAC;CAClB;AACF;;;ACvIA,SAAS,YAAe,SAAqB;CAC3C,uBAAO,IAAI,MAAM,2BAA2B,QAAQ,OAAO,8CAA8C;AAC3G;;;;;;;AAQA,SAAgB,IAA4B,SAA8D;CACxG,MAAM,QAAQ,OAAO,KAAK,OAAO;CACjC,MAAM,0BAAU,IAAI,IAAoC;CACxD,MAAM,0BAAU,IAAI,IAAyB;CAC7C,MAAM,0BAAU,IAAI,IAA8B;CAClD,IAAI,QAAQ;;CAEZ,IAAI;CAEJ,MAAM,OAAO,WAAoB;EAC/B,IAAI,OAAO;EACX,QAAQ;EACR,SAAS;EACT,KAAK,MAAM,UAAU,CAAC,GAAG,OAAO,GAAG,OAAO,MAAM;CAClD;CAEA,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,SAAS,QAAQ;EACvB,OAAO,SAAS,UAAU;GACxB,KAAK,MAAM,UAAU,CAAC,GAAG,OAAO,GAAG,OAAO;IAAE,QAAQ;IAAM;GAAM,CAAC;EACnE,CAAC;EACD,OAAO,QAAQ,GAAG;EAClB,OAAO,SAAS,UAAU;GACxB,KAAK,MAAM,UAAU,CAAC,GAAG,OAAO,GAAG,OAAO,KAAK;EACjD,CAAC;CACH;CAEA,OAAO;EACL,YAAY,QAAQ,OAAO,YAAY,KAAK,CAAC;EAC7C,QAAQ,SAAS;GACf,QAAQ,IAAI,OAAO;GACnB,aAAa;IACX,QAAQ,OAAO,OAAO;GACxB;EACF;EACA,QAAQ,SAAS;GACf,IAAI,WAAW,KAAA,GAAW;IACxB,QAAQ,MAAM;IACd,aAAa,CAAC;GAChB;GACA,QAAQ,IAAI,OAAO;GACnB,aAAa;IACX,QAAQ,OAAO,OAAO;GACxB;EACF;EACA,QAAQ,SAAS;GACf,QAAQ,IAAI,OAAO;GACnB,aAAa;IACX,QAAQ,OAAO,OAAO;GACxB;EACF;EACA,MAAM,MAAM,QAAiB;GAC3B,QAAQ;GACR,MAAM,QAAQ,IAAI,MAAM,KAAK,SAAS,QAAQ,KAAK,CAAC,MAAM,MAAM,CAAC,CAAC;EACpE;EACA,MAAM,CAAC;EACP,eAAe,CAAC;CAClB;AACF;;;;;;;AAQA,SAAgB,QACd,SAC4C;CAC5C,MAAM,QAAQ,OAAO,KAAK,OAAO;CACjC,MAAM,0BAAU,IAAI,IAAkD;CACtE,MAAM,0BAAU,IAAI,IAA4B;CAChD,MAAM,0BAAU,IAAI,IAA8B;CAClD,IAAI,YAAY,MAAM;CACtB,IAAI,QAAQ;CAEZ,MAAM,QAAQ,UAA6C;EACzD,KAAK,MAAM,UAAU,CAAC,GAAG,OAAO,GAAG,OAAO,KAAK;CACjD;CAEA,MAAM,YAAkB;EACtB,IAAI,OAAO;EACX,QAAQ;EACR,KAAK,MAAM,UAAU,CAAC,GAAG,OAAO,GAAG,OAAO,KAAA,CAAS;CACrD;CAEA,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,SAAS,QAAQ;EACvB,OAAO,SAAS,UAAU,KAAK;GAAE,QAAQ;GAAM;EAAM,CAAC,CAAC;EACvD,OAAO,SAAS,WAAW;GACzB,KAAK;IAAE,QAAQ;IAAM;GAAO,CAAC;GAC7B,aAAa;GACb,IAAI,cAAc,GAAG,IAAI;EAC3B,CAAC;EACD,OAAO,SAAS,UAAU;GACxB,KAAK,MAAM,UAAU,CAAC,GAAG,OAAO,GAAG,OAAO,KAAK;EACjD,CAAC;CACH;CAEA,OAAO;EACL,YAAY,QAAQ,OAAO,YAAY,KAAK,CAAC;EAC7C,QAAQ,SAAS;GACf,QAAQ,IAAI,OAAO;GACnB,aAAa;IACX,QAAQ,OAAO,OAAO;GACxB;EACF;EACA,QAAQ,SAAS;GACf,IAAI,OAAO;IACT,QAAQ,KAAA,CAAS;IACjB,aAAa,CAAC;GAChB;GACA,QAAQ,IAAI,OAAO;GACnB,aAAa;IACX,QAAQ,OAAO,OAAO;GACxB;EACF;EACA,QAAQ,SAAS;GACf,QAAQ,IAAI,OAAO;GACnB,aAAa;IACX,QAAQ,OAAO,OAAO;GACxB;EACF;EACA,MAAM,MAAM,QAAiB;GAC3B,QAAQ;GACR,MAAM,QAAQ,IAAI,MAAM,KAAK,SAAS,QAAQ,KAAK,CAAC,MAAM,MAAM,CAAC,CAAC;EACpE;EACA,MAAM,CAAC;EACP,eAAe,CAAC;CAClB;AACF;;;;;;;;;AC1HA,SAAgB,YACd,UACA,QACA,UAAgD,CAAC,GACpC;CACb,MAAM,0BAAU,IAAI,IAAmB;CACvC,IAAI,UAAU;CAEd,MAAM,SAAS,SAA2C;EACxD,MAAM,WAAW,YAAY;GAC3B,QAAQ,YAAY,IAAI;GACxB,IAAI;IACF,MAAM,OAAO,IAAI;GACnB,SAAS,OAAO;IACd,QAAQ,UAAU,KAAK;IACvB,QAAQ,WAAW,MAAM,KAAK;IAC9B;GACF;GACA,QAAQ,WAAW,MAAM,KAAA,CAAS;EACpC,EAAA,CAAG;EACH,QAAQ,IAAI,OAAO;EACnB,QAAa,cAAc,QAAQ,OAAO,OAAO,CAAC;CACpD;CAEA,MAAM,QAAQ,YAAY;EACxB,SAAS;GACP,IAAI;GACJ,IAAI;IACF,OAAO,MAAM,SAAS,OAAO;GAC/B,QAAQ;IACN;GACF;GACA,IAAI,SAAS;GACb,MAAM,IAAI;EACZ;CACF,EAAA,CAAG;CAEH,OAAO;EACL,IAAI,OAAO;GACT,OAAO,QAAQ;EACjB;EACA,MAAM,OAAO;GACX,UAAU;GACV,MAAM,SAAS,MAAM;GACrB,MAAM;GACN,MAAM,QAAQ,IAAI,CAAC,GAAG,OAAO,CAAC;EAChC;CACF;AACF;;;;AChFA,SAAgB,cAA2B,MAGzB;CAChB,OAAO;EACL,OAAO,UAAU,KAAK,SAAS,KAAK,KAAK;EACzC,UAAU,YAAY,KAAK,QAAQ,QAAQ,OAAO;EAClD,UAAU,YAAY,KAAK,QAAQ,QAAQ,OAAO;EAClD,UAAU,YAAY,KAAK,QAAQ,QAAQ,OAAO;EAClD,OAAO,OAAO,WAAW;GACvB,MAAM,KAAK,SAAS,MAAM,MAAM;EAClC;EACA,MAAM;GAAE,GAAG,KAAK,QAAQ;GAAM,GAAG,KAAK,SAAS;EAAK;EACpD,UAAU,YAAY,KAAK,QAAQ,QAAQ,OAAO;CACpD;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"process-eCAv2tIS.d.ts","names":[],"sources":["../src/process.
|
|
1
|
+
{"version":3,"file":"process-eCAv2tIS.d.ts","names":[],"sources":["../src/process.ts"],"mappings":";;;iBAQgB,MAAM,GAAG,GAAG,WAAW,UAAU,SAAS,WAAW,UAAU,aAC7E,IAAI,UAAU,GAAG,GAAG,IAAI,KACxB,eACA,MAAM,GAAG,IAAI,MAAM,uBACjB,GAAG,MAAM,QAAQ,GAAG,GAAG,IAAI;;;cAMzB,QAAQ,GAAG,GAAG,WAAW,SAAS,WAAW,iBAAiB,aAAa,GAAG,GAAG,IAAI;EACzF,YAAY,IAAI,UAAU,GAAG,GAAG,IAAI,KAAK,eAAe,MAAM,GAAG,IAAI,MAAM;EAI3E,MAAM,GAAG;EAIT,QAAQ"}
|
package/dist/remote/index.d.ts
CHANGED
|
@@ -164,6 +164,31 @@ interface BunServerWebSocketLike {
|
|
|
164
164
|
* ```
|
|
165
165
|
*/
|
|
166
166
|
declare function bunServerWebSocket(ws: BunServerWebSocketLike): WebSocketLike;
|
|
167
|
+
/** The `ws` package's server-side socket, as little of it as is used here. */
|
|
168
|
+
interface NodeServerWebSocketLike {
|
|
169
|
+
send(data: string | Uint8Array): void;
|
|
170
|
+
close(code?: number, reason?: string): void;
|
|
171
|
+
readonly readyState: number;
|
|
172
|
+
on(event: "message", handler: (data: unknown, isBinary: boolean) => void): unknown;
|
|
173
|
+
on(event: "close", handler: () => void): unknown;
|
|
174
|
+
on(event: "error", handler: (error: Error) => void): unknown;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Adapt a node `ws` socket to the `WebSocketLike` the transport consumes.
|
|
178
|
+
*
|
|
179
|
+
* The difference from Bun's is who delivers the events: `ws` hands them to listeners on the socket itself, so the
|
|
180
|
+
* adapter subscribes once and routes them onto the settable slots. An error ends the connection the way a close
|
|
181
|
+
* does — the transport hears one ending, not two.
|
|
182
|
+
*
|
|
183
|
+
* ```ts
|
|
184
|
+
* server.on("upgrade", (request, socket, head) => {
|
|
185
|
+
* wss.handleUpgrade(request, socket, head, (ws) => {
|
|
186
|
+
* serveRemoteActor(actor, wsServerSpawner(nodeServerWebSocket(ws)));
|
|
187
|
+
* });
|
|
188
|
+
* });
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
declare function nodeServerWebSocket(ws: NodeServerWebSocketLike): WebSocketLike;
|
|
167
192
|
//#endregion
|
|
168
193
|
//#region src/remote/spawners/worker-client.d.ts
|
|
169
194
|
type WorkerCtor = new (url: string) => WorkerLike;
|
|
@@ -172,5 +197,5 @@ declare function workerClientSpawner<Args = unknown>(url: string | ((args: Args)
|
|
|
172
197
|
//#region src/remote/spawners/worker-self.d.ts
|
|
173
198
|
declare function workerSelfSpawner(selfScope?: WorkerLike): Spawner;
|
|
174
199
|
//#endregion
|
|
175
|
-
export { type AnyActorDefinition, BAD_PREFIX_LIMIT, type BadFrame, type Blob, type BlobFacts, type BlobRef, type BlobSource, type BunServerWebSocketLike, type BytesFacts, type BytesRef, CHANNEL_LOST, type Channel, type ClientContext, type ClientSpawner, type Connection, type Delivered, type Frame, type FrameSink, HANDSHAKE_TIMEOUT_MS, HandshakeError, type HandshakeOptions, type Init, type JsonSeamOptions, type Listener, type OpenedWebSocket, type OutputSink, PROCESS_REF, PROTO_KEY, type ProcessRef, type Reactor, type ReactorOptions, type RemoteArray, RemoteBlob, type RemoteClientOptions, type RemoteMessage, RemoteProcess, RemoteStream, type Served, type ServedConnection, type ServedProcess, type Session, type SessionFactory, type Spawner, type Stream, type StreamRef, StreamStore, type StreamStoreOptions, type StringTransport, type TypedArrayName, VERSION, WORKER_VERSION, WS_OPEN, type WebSocketClientOptions, type WebSocketCtor, type WebSocketLike, type WebSocketOpener, WebSocketTransport, type WorkerCtor, type WorkerLike, WorkerTransport, badFrame, badReason, bunServerWebSocket, decode, encode, errorFrame, errorReason, frameChannel, handshake, isBad, isBlobRef, isBytesRef, isError, isExit, isInit, isMsg, isPause, isProto, isRelease, isRemoteProcess, isResume, isState, isStop, isStreamRef, json1, json1Channel, jsonSeam, jsonSeamParts, makeSender, oneConnection, protoFrame, protoVersion, pumpOutput, reactor, readFrame, remoteClient, rootIdFor, seamAnnounced, serveRemoteActor, stderrSink, workerClientSpawner, workerSelfSpawner, wsClientSpawner, wsServerSpawner };
|
|
200
|
+
export { type AnyActorDefinition, BAD_PREFIX_LIMIT, type BadFrame, type Blob, type BlobFacts, type BlobRef, type BlobSource, type BunServerWebSocketLike, type BytesFacts, type BytesRef, CHANNEL_LOST, type Channel, type ClientContext, type ClientSpawner, type Connection, type Delivered, type Frame, type FrameSink, HANDSHAKE_TIMEOUT_MS, HandshakeError, type HandshakeOptions, type Init, type JsonSeamOptions, type Listener, type NodeServerWebSocketLike, type OpenedWebSocket, type OutputSink, PROCESS_REF, PROTO_KEY, type ProcessRef, type Reactor, type ReactorOptions, type RemoteArray, RemoteBlob, type RemoteClientOptions, type RemoteMessage, RemoteProcess, RemoteStream, type Served, type ServedConnection, type ServedProcess, type Session, type SessionFactory, type Spawner, type Stream, type StreamRef, StreamStore, type StreamStoreOptions, type StringTransport, type TypedArrayName, VERSION, WORKER_VERSION, WS_OPEN, type WebSocketClientOptions, type WebSocketCtor, type WebSocketLike, type WebSocketOpener, WebSocketTransport, type WorkerCtor, type WorkerLike, WorkerTransport, badFrame, badReason, bunServerWebSocket, decode, encode, errorFrame, errorReason, frameChannel, handshake, isBad, isBlobRef, isBytesRef, isError, isExit, isInit, isMsg, isPause, isProto, isRelease, isRemoteProcess, isResume, isState, isStop, isStreamRef, json1, json1Channel, jsonSeam, jsonSeamParts, makeSender, nodeServerWebSocket, oneConnection, protoFrame, protoVersion, pumpOutput, reactor, readFrame, remoteClient, rootIdFor, seamAnnounced, serveRemoteActor, stderrSink, workerClientSpawner, workerSelfSpawner, wsClientSpawner, wsServerSpawner };
|
|
176
201
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/remote/sender.ts","../../src/remote/handshake.ts","../../src/remote/seam.ts","../../src/remote/transports/worker.ts","../../src/remote/spawners/ws-client.ts","../../src/remote/spawners/ws-server.ts","../../src/remote/spawners/worker-client.ts","../../src/remote/spawners/worker-self.ts"],"mappings":";;;;iBAUgB,WACd,kBACA,2BACA,0BACC;;;;cCIU;;cAGA;;cAGA,uBAAuB;EAClC,YAAY;;;iBAOE,aAAa,OAAO;;iBAMpB,WAAW,kBAAkB;UAI5B;;EAEf;;EAEA;;;EAGA,SAAS,OAAO;;;EAGhB,QAAQ;;;;;;;;;iBAeM,UAAU,YACxB,OAAO,UAAQ,WAAW,IAC1B,SAAS,mBACR,QAAQ,UAAQ,WAAW;;;;iBClDd,cAAc;;UAKb;;EAEf;;EAEA,QAAQ;;;;;;;;;;;;;;;iBAgBM,aAAa,QAAQ,UAAQ,aAAa;;;;;;;iBAwF1C,SAAS,gBACvB,UAAU,MAAM,SAAS,QAAQ,UAAQ,cACzC,UAAS,kBACR,cAAc;;iBAKD,cAAc,gBAC5B,UAAU,MAAM,SAAS,QAAQ,UAAQ,QACzC,UAAS,kBACR,cAAc;;;;cCrIJ;;;;;;UAOI;EACf,YAAY;EACZ,aAAa;IAAS;;EACtB,YAAY;EACZ;;cAGW,2BAA2B;UAC9B;UACA;UACA;UACA;UACA;EAER,YAAY,QAAQ;MAgBhB;EAIJ,UAAU,UAAU,OAAO;EAU3B,QAAQ;MAIJ;EAIE,KAAK,OAAO,0BAA0B;EAKtC,SAAS;;;;;KC5DL;WACD;EACT,KAAK;EACL,MAAM,eAAe;EACrB;EACA,WAAW;EACX,aAAa;IAAS;;EACtB;;KAGU,qBAAqB,gBAAgB;;;;;;;KAQrC,gBAAgB,mBAAmB,aAAa,MAAM,SAAS;UAE1D,uBAAuB;;EAEtC,OAAO,gBAAgB;;iBA6BT,gBAAgB,gBAC9B,gBAAgB,MAAM,kBACtB,gBAAe,eACf,OAAM,uBAAuB,QAC5B,cAAc;;;iBC7DD,gBAAgB,IAAI,gBAAgB;;;;;;;UAUnC;EACf,KAAK,eAAe;EACpB,MAAM,eAAe;WACZ;EACT;;;;;;;;;;;;;;;;;;;;;iBAsBc,mBAAmB,IAAI,yBAAyB;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/remote/sender.ts","../../src/remote/handshake.ts","../../src/remote/seam.ts","../../src/remote/transports/worker.ts","../../src/remote/spawners/ws-client.ts","../../src/remote/spawners/ws-server.ts","../../src/remote/spawners/worker-client.ts","../../src/remote/spawners/worker-self.ts"],"mappings":";;;;iBAUgB,WACd,kBACA,2BACA,0BACC;;;;cCIU;;cAGA;;cAGA,uBAAuB;EAClC,YAAY;;;iBAOE,aAAa,OAAO;;iBAMpB,WAAW,kBAAkB;UAI5B;;EAEf;;EAEA;;;EAGA,SAAS,OAAO;;;EAGhB,QAAQ;;;;;;;;;iBAeM,UAAU,YACxB,OAAO,UAAQ,WAAW,IAC1B,SAAS,mBACR,QAAQ,UAAQ,WAAW;;;;iBClDd,cAAc;;UAKb;;EAEf;;EAEA,QAAQ;;;;;;;;;;;;;;;iBAgBM,aAAa,QAAQ,UAAQ,aAAa;;;;;;;iBAwF1C,SAAS,gBACvB,UAAU,MAAM,SAAS,QAAQ,UAAQ,cACzC,UAAS,kBACR,cAAc;;iBAKD,cAAc,gBAC5B,UAAU,MAAM,SAAS,QAAQ,UAAQ,QACzC,UAAS,kBACR,cAAc;;;;cCrIJ;;;;;;UAOI;EACf,YAAY;EACZ,aAAa;IAAS;;EACtB,YAAY;EACZ;;cAGW,2BAA2B;UAC9B;UACA;UACA;UACA;UACA;EAER,YAAY,QAAQ;MAgBhB;EAIJ,UAAU,UAAU,OAAO;EAU3B,QAAQ;MAIJ;EAIE,KAAK,OAAO,0BAA0B;EAKtC,SAAS;;;;;KC5DL;WACD;EACT,KAAK;EACL,MAAM,eAAe;EACrB;EACA,WAAW;EACX,aAAa;IAAS;;EACtB;;KAGU,qBAAqB,gBAAgB;;;;;;;KAQrC,gBAAgB,mBAAmB,aAAa,MAAM,SAAS;UAE1D,uBAAuB;;EAEtC,OAAO,gBAAgB;;iBA6BT,gBAAgB,gBAC9B,gBAAgB,MAAM,kBACtB,gBAAe,eACf,OAAM,uBAAuB,QAC5B,cAAc;;;iBC7DD,gBAAgB,IAAI,gBAAgB;;;;;;;UAUnC;EACf,KAAK,eAAe;EACpB,MAAM,eAAe;WACZ;EACT;;;;;;;;;;;;;;;;;;;;;iBAsBc,mBAAmB,IAAI,yBAAyB;;UAe/C;EACf,KAAK,eAAe;EACpB,MAAM,eAAe;WACZ;EACT,GAAG,kBAAkB,UAAU,eAAe;EAC9C,GAAG,gBAAgB;EACnB,GAAG,gBAAgB,UAAU,OAAO;;;;;;;;;;;;;;;;;iBAkBtB,oBAAoB,IAAI,0BAA0B;;;KC1EtD,kBAAkB,gBAAgB;iBAI9B,oBAAoB,gBAClC,gBAAgB,MAAM,kBACtB,aAAY,aACX,cAAc;;;iBCRD,kBAAkB,YAAW,aAAmD"}
|
package/dist/remote/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as readFrame, B as isRelease, C as RemoteStream, D as isStreamRef, E as isBytesRef, F as isExit, G as VERSION, H as isState, I as isInit, J as decode, K as badFrame, L as isMsg, M as errorReason, N as frameReaders, O as makeSender, P as isError, Q as json1Channel, R as isPause, S as RemoteBlob, T as isBlobRef, U as isStop, V as isResume, W as BAD_PREFIX_LIMIT, X as isBad, Y as encode, Z as json1, _ as HandshakeError, a as remoteClient, b as protoFrame, c as oneConnection, d as frameChannel, et as PROCESS_REF, f as jsonSeam, g as HANDSHAKE_TIMEOUT_MS, h as seamOf, i as CHANNEL_LOST, j as errorFrame, k as RemoteProcess, l as reactor, m as seamAnnounced, n as WebSocketTransport, o as pumpOutput, p as jsonSeamParts, q as badReason, rt as rootIdFor, s as stderrSink, t as WS_OPEN, tt as isRemoteProcess, u as serveRemoteActor, v as PROTO_KEY, w as StreamStore, x as protoVersion, y as handshake, z as isProto } from "../websocket-
|
|
1
|
+
import { $ as readFrame, B as isRelease, C as RemoteStream, D as isStreamRef, E as isBytesRef, F as isExit, G as VERSION, H as isState, I as isInit, J as decode, K as badFrame, L as isMsg, M as errorReason, N as frameReaders, O as makeSender, P as isError, Q as json1Channel, R as isPause, S as RemoteBlob, T as isBlobRef, U as isStop, V as isResume, W as BAD_PREFIX_LIMIT, X as isBad, Y as encode, Z as json1, _ as HandshakeError, a as remoteClient, b as protoFrame, c as oneConnection, d as frameChannel, et as PROCESS_REF, f as jsonSeam, g as HANDSHAKE_TIMEOUT_MS, h as seamOf, i as CHANNEL_LOST, j as errorFrame, k as RemoteProcess, l as reactor, m as seamAnnounced, n as WebSocketTransport, o as pumpOutput, p as jsonSeamParts, q as badReason, rt as rootIdFor, s as stderrSink, t as WS_OPEN, tt as isRemoteProcess, u as serveRemoteActor, v as PROTO_KEY, w as StreamStore, x as protoVersion, y as handshake, z as isProto } from "../websocket-Dqv9GfDy.js";
|
|
2
2
|
//#region src/remote/transports/worker.ts
|
|
3
3
|
/** The worker encoding: native structured clone (no JSON). */
|
|
4
4
|
const WORKER_VERSION = "clone.v1";
|
|
@@ -122,6 +122,45 @@ function bunServerWebSocket(ws) {
|
|
|
122
122
|
onclose: null
|
|
123
123
|
};
|
|
124
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Adapt a node `ws` socket to the `WebSocketLike` the transport consumes.
|
|
127
|
+
*
|
|
128
|
+
* The difference from Bun's is who delivers the events: `ws` hands them to listeners on the socket itself, so the
|
|
129
|
+
* adapter subscribes once and routes them onto the settable slots. An error ends the connection the way a close
|
|
130
|
+
* does — the transport hears one ending, not two.
|
|
131
|
+
*
|
|
132
|
+
* ```ts
|
|
133
|
+
* server.on("upgrade", (request, socket, head) => {
|
|
134
|
+
* wss.handleUpgrade(request, socket, head, (ws) => {
|
|
135
|
+
* serveRemoteActor(actor, wsServerSpawner(nodeServerWebSocket(ws)));
|
|
136
|
+
* });
|
|
137
|
+
* });
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
function nodeServerWebSocket(ws) {
|
|
141
|
+
const adapter = {
|
|
142
|
+
send: (data) => ws.send(data),
|
|
143
|
+
close: (code, reason) => ws.close(code, reason),
|
|
144
|
+
get readyState() {
|
|
145
|
+
return ws.readyState;
|
|
146
|
+
},
|
|
147
|
+
onmessage: null,
|
|
148
|
+
onclose: null
|
|
149
|
+
};
|
|
150
|
+
let ended = false;
|
|
151
|
+
const finish = () => {
|
|
152
|
+
if (ended) return;
|
|
153
|
+
ended = true;
|
|
154
|
+
adapter.onclose?.();
|
|
155
|
+
};
|
|
156
|
+
ws.on("message", (data, isBinary) => {
|
|
157
|
+
const bytes = Array.isArray(data) ? Buffer.concat(data) : Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
158
|
+
adapter.onmessage?.({ data: isBinary ? bytes : bytes.toString("utf8") });
|
|
159
|
+
});
|
|
160
|
+
ws.on("close", finish);
|
|
161
|
+
ws.on("error", finish);
|
|
162
|
+
return adapter;
|
|
163
|
+
}
|
|
125
164
|
//#endregion
|
|
126
165
|
//#region src/remote/spawners/worker-client.ts
|
|
127
166
|
const defaultWorker = globalThis.Worker;
|
|
@@ -148,6 +187,6 @@ function workerSelfSpawner(selfScope = globalThis) {
|
|
|
148
187
|
};
|
|
149
188
|
}
|
|
150
189
|
//#endregion
|
|
151
|
-
export { BAD_PREFIX_LIMIT, CHANNEL_LOST, HANDSHAKE_TIMEOUT_MS, HandshakeError, PROCESS_REF, PROTO_KEY, RemoteBlob, RemoteProcess, RemoteStream, StreamStore, VERSION, WORKER_VERSION, WS_OPEN, WebSocketTransport, WorkerTransport, badFrame, badReason, bunServerWebSocket, decode, encode, errorFrame, errorReason, frameChannel, handshake, isBad, isBlobRef, isBytesRef, isError, isExit, isInit, isMsg, isPause, isProto, isRelease, isRemoteProcess, isResume, isState, isStop, isStreamRef, json1, json1Channel, jsonSeam, jsonSeamParts, makeSender, oneConnection, protoFrame, protoVersion, pumpOutput, reactor, readFrame, remoteClient, rootIdFor, seamAnnounced, serveRemoteActor, stderrSink, workerClientSpawner, workerSelfSpawner, wsClientSpawner, wsServerSpawner };
|
|
190
|
+
export { BAD_PREFIX_LIMIT, CHANNEL_LOST, HANDSHAKE_TIMEOUT_MS, HandshakeError, PROCESS_REF, PROTO_KEY, RemoteBlob, RemoteProcess, RemoteStream, StreamStore, VERSION, WORKER_VERSION, WS_OPEN, WebSocketTransport, WorkerTransport, badFrame, badReason, bunServerWebSocket, decode, encode, errorFrame, errorReason, frameChannel, handshake, isBad, isBlobRef, isBytesRef, isError, isExit, isInit, isMsg, isPause, isProto, isRelease, isRemoteProcess, isResume, isState, isStop, isStreamRef, json1, json1Channel, jsonSeam, jsonSeamParts, makeSender, nodeServerWebSocket, oneConnection, protoFrame, protoVersion, pumpOutput, reactor, readFrame, remoteClient, rootIdFor, seamAnnounced, serveRemoteActor, stderrSink, workerClientSpawner, workerSelfSpawner, wsClientSpawner, wsServerSpawner };
|
|
152
191
|
|
|
153
192
|
//# sourceMappingURL=index.js.map
|
package/dist/remote/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/remote/transports/worker.ts","../../src/remote/spawners/ws-client.ts","../../src/remote/spawners/ws-server.ts","../../src/remote/spawners/worker-client.ts","../../src/remote/spawners/worker-self.ts"],"sourcesContent":["/* eslint-disable unicorn/prefer-add-event-listener, unicorn/require-post-message-target-origin */\n// ── Worker transport ────────────────────────────────────────────────────────────\n//\n// Moves frame objects directly over postMessage. postMessage structured-clones\n// the frame object, so there is no JSON encode/decode — this transport implements\n// Channel directly (not StringTransport). Structured clone is the worker's\n// native encoding, named by WORKER_VERSION.\n\nimport { frameReaders, type Channel } from \"../channel.js\";\n\n/** The worker encoding: native structured clone (no JSON). */\nexport const WORKER_VERSION = \"clone.v1\";\n\n/**\n * The subset of a worker the transport needs. The main-thread `Worker` and the\n * worker-global `self` both satisfy it; `terminate` is present only on the\n * main-thread side.\n */\nexport interface WorkerLike {\n postMessage(data: unknown): void;\n onmessage: ((event: { data: unknown }) => void) | null;\n onerror?: ((event: unknown) => void) | null;\n terminate?(): void;\n}\n\nexport class WorkerTransport implements Channel {\n private worker: WorkerLike;\n private pvtOnMessage: ((frame: Record<string, unknown>) => void) | null = null;\n private pvtSubscribe: ((handler: (frame: Record<string, unknown>) => void) => () => void) | null = null;\n private pvtOnClose: (() => void) | null = null;\n private closed = false;\n\n constructor(worker: WorkerLike) {\n this.worker = worker;\n worker.onmessage = (event) => {\n if (this.pvtOnMessage && !this.closed) {\n this.pvtOnMessage(event.data as Record<string, unknown>);\n }\n };\n // An uncaught error in the worker is the closest thing to an abrupt drop.\n if (\"onerror\" in worker) {\n worker.onerror = () => {\n this.closed = true;\n this.pvtOnClose?.();\n };\n }\n }\n\n get canSend(): boolean {\n return !this.closed;\n }\n\n onMessage(handler: (frame: Record<string, unknown>) => void): () => void {\n if (this.closed) throw new Error(\"WorkerTransport: closed\");\n // This transport moves frame objects itself, so it is a channel and not a transport: readers are kept\n // the way any channel keeps them, over the one handler the worker's own event needs.\n this.pvtSubscribe ??= frameReaders((deliver) => {\n this.pvtOnMessage = deliver;\n });\n return this.pvtSubscribe(handler);\n }\n\n onClose(handler: () => void): void {\n this.pvtOnClose = handler;\n }\n\n get hasHandler(): boolean {\n return this.pvtOnMessage !== null;\n }\n\n async send(frame: Record<string, unknown>): Promise<void> {\n if (this.closed) throw new Error(\"WorkerTransport: closed\");\n this.worker.postMessage(frame);\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.worker.terminate?.();\n }\n}\n","/* eslint-disable unicorn/prefer-add-event-listener */\n// ── WebSocket client spawner ────────────────────────────────────────────────────\n//\n// Connects to a `ws://` URL with the WHATWG WebSocket and returns the seam's frames over it. The socket must be\n// open before `$proto` can be sent, so the seam is composed first and its announcement waits for the open.\n//\n// How the socket is opened is the caller's: the second parameter constructs one from a URL, and an opener\n// replaces that with a function of the connection, which is where a header belongs. A token in a URL is a token\n// in every log that URL passes through.\n\nimport { WebSocketTransport } from \"../transports/websocket.js\";\nimport { seamOf } from \"../seam.js\";\nimport type { ClientSpawner } from \"../client.js\";\n\n/** The socket the spawner needs: what the transport reads and writes, and the open/error slots. */\nexport type OpenedWebSocket = {\n readonly readyState: number;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n onopen: (() => void) | null;\n onerror: ((err: unknown) => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n onclose: (() => void) | null;\n};\n\nexport type WebSocketCtor = new (url: string) => OpenedWebSocket;\n\n/**\n * How a socket is opened when a constructor is not enough: the URL to open, and the arguments this\n * spawn was called with. A per-tenant header, a bearer token, an authority the machine does not\n * already know — each of them is a fact about the connection, and each of them is the caller's to\n * express in its own runtime's shape rather than in one posipaki was taught.\n */\nexport type WebSocketOpener<Args = unknown> = (url: string, args: Args) => OpenedWebSocket;\n\nexport interface WebSocketClientOptions<Args = unknown> {\n /** Opens the socket. Given, it replaces the constructor. */\n open?: WebSocketOpener<Args>;\n}\n\nconst defaultWebSocket = (globalThis as { WebSocket?: WebSocketCtor }).WebSocket;\n\n/** What a socket failure says about itself.\n *\n * A WHATWG WebSocket reports an open failure as an ErrorEvent, and `String(event)` is\n * `[object ErrorEvent]`: its type, its message and the underlying error are all in its\n * own fields, so read those rather than stringify the event that carries them.\n */\nfunction describeSocketError(err: unknown): string {\n if (typeof err !== \"object\" || err === null) return String(err);\n const event = err as { type?: unknown; message?: unknown; error?: unknown };\n const cause = event.error instanceof Error ? event.error.message : event.error;\n const parts = [event.type, event.message, cause].filter(\n (part): part is string => typeof part === \"string\" && part.length > 0,\n );\n return parts.length > 0 ? parts.join(\": \") : String(err);\n}\n\nfunction openWebSocket(ws: { readonly readyState: number; onopen: (() => void) | null; onerror: ((err: unknown) => void) | null }): Promise<void> {\n return new Promise((resolve, reject) => {\n if (ws.readyState === 1 /* OPEN */) return resolve();\n ws.onopen = () => resolve();\n ws.onerror = (err) => reject(new Error(`websocket failed to open: ${describeSocketError(err)}`));\n });\n}\n\nexport function wsClientSpawner<Args = unknown>(\n url: string | ((args: Args) => string),\n WebSocketImpl: WebSocketCtor = defaultWebSocket as WebSocketCtor,\n opts: WebSocketClientOptions<Args> = {},\n): ClientSpawner<Args> {\n // The constructor is the default opener, so the socket is opened one way either way.\n const open: WebSocketOpener<Args> = opts.open ?? ((target) => new WebSocketImpl(target));\n return async (args) => {\n const target = typeof url === \"function\" ? url(args) : url;\n const ws = open(target, args);\n const transport = new WebSocketTransport(ws);\n // The seam is composed before the socket opens — its reader is installed synchronously — and the\n // announcement waits for the open, because a socket that is still connecting cannot carry it.\n const opened = openWebSocket(ws);\n const seam = seamOf(transport, { ready: opened });\n try {\n await opened;\n } catch (error) {\n // The seam refuses for the same reason; the open failure is the one worth naming, so it is the one\n // that leaves here and the other is not left unhandled.\n void seam.catch(() => undefined);\n throw error;\n }\n return seam;\n };\n}\n","// ── WebSocket server spawner ────────────────────────────────────────────────────\n//\n// Environment-specific: serves an already-upgraded WebSocket connection. The consumer's HTTP server does the\n// upgrade and hands us the socket; we wrap it in a WebSocketTransport and compose the seam over it.\n\nimport { WebSocketTransport, type WebSocketLike } from \"../transports/websocket.js\";\nimport { seamOf } from \"../seam.js\";\nimport type { Spawner } from \"../server.js\";\n\nexport function wsServerSpawner(ws: WebSocketLike): Spawner {\n return async () => seamOf(new WebSocketTransport(ws));\n}\n\n/**\n * A minimal Bun `ServerWebSocket` — `send`/`close`/`readyState` plus the data\n * slot the server uses to stash per-connection state. Bun delivers message and\n * close events through the `Bun.serve` websocket config callbacks, not through\n * properties on the socket, so the consumer routes them onto the adapter.\n */\nexport interface BunServerWebSocketLike {\n send(data: string | Uint8Array): void;\n close(code?: number, reason?: string): void;\n readonly readyState: number;\n data?: unknown;\n}\n\n/**\n * Adapt a Bun server socket to the `WebSocketLike` the transport consumes. The\n * returned adapter owns settable `onmessage`/`onclose` slots; the consumer\n * routes Bun's `message`/`close` config callbacks into them, e.g.:\n *\n * ```ts\n * Bun.serve({\n * websocket: {\n * open(ws) {\n * const adapter = bunServerWebSocket(ws);\n * ws.data = adapter;\n * serveRemoteActor(actor, wsServerSpawner(adapter));\n * },\n * message(ws, msg) { (ws.data as WebSocketLike).onmessage?.({ data: msg }); },\n * close(ws) { (ws.data as WebSocketLike).onclose?.(); },\n * },\n * });\n * ```\n */\nexport function bunServerWebSocket(ws: BunServerWebSocketLike): WebSocketLike {\n return {\n send: (data) => ws.send(data),\n close: (code, reason) => ws.close(code, reason),\n // Read through on every ask: a socket's state changes, and a snapshot taken at the upgrade would keep\n // saying \"opening\" for the life of the connection — which is a send that refuses forever.\n get readyState() {\n return ws.readyState;\n },\n onmessage: null,\n onclose: null,\n };\n}\n","// ── Worker client spawner (command) ─────────────────────────────────────────────\n//\n// Environment-specific: spawns a Worker with the WHATWG `new Worker(url)` API,\n// wraps it in a WorkerTransport, and returns a frame Channel with the $proto\n// handshake already validated.\n\nimport { WorkerTransport, WORKER_VERSION, type WorkerLike } from \"../transports/worker.js\";\nimport { isProto } from \"../channel.js\";\nimport type { ClientSpawner } from \"../client.js\";\n\nexport type WorkerCtor = new (url: string) => WorkerLike;\n\nconst defaultWorker = (globalThis as { Worker?: WorkerCtor }).Worker;\n\nexport function workerClientSpawner<Args = unknown>(\n url: string | ((args: Args) => string),\n WorkerImpl: WorkerCtor = defaultWorker as WorkerCtor,\n): ClientSpawner<Args> {\n return async (args) => {\n const target = typeof url === \"function\" ? url(args) : url;\n const channel = new WorkerTransport(new WorkerImpl(target));\n\n const protoFrame = await new Promise<Record<string, unknown>>((resolve) => {\n const off = channel.onMessage((frame) => {\n off();\n resolve(frame);\n });\n });\n\n if (!isProto(protoFrame) || protoFrame.$proto !== WORKER_VERSION) {\n throw new Error(`unsupported protocol: ${JSON.stringify(protoFrame).slice(0, 50)}`);\n }\n return channel;\n };\n}\n","// ── Worker server spawner (self) ────────────────────────────────────────────────\n//\n// Environment-specific: serves the worker-global scope (`self`) as a frame\n// Channel with the $proto handshake already sent. The worker entry script calls\n// `serveRemoteActor(actor, workerSelfSpawner())`.\n\nimport { WorkerTransport, WORKER_VERSION, type WorkerLike } from \"../transports/worker.js\";\nimport type { Spawner } from \"../server.js\";\n\nexport function workerSelfSpawner(selfScope: WorkerLike = globalThis as unknown as WorkerLike): Spawner {\n return async () => {\n const channel = new WorkerTransport(selfScope);\n await channel.send({ $proto: WORKER_VERSION });\n return channel;\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/remote/transports/worker.ts","../../src/remote/spawners/ws-client.ts","../../src/remote/spawners/ws-server.ts","../../src/remote/spawners/worker-client.ts","../../src/remote/spawners/worker-self.ts"],"sourcesContent":["/* eslint-disable unicorn/prefer-add-event-listener, unicorn/require-post-message-target-origin */\n// ── Worker transport ────────────────────────────────────────────────────────────\n//\n// Moves frame objects directly over postMessage. postMessage structured-clones\n// the frame object, so there is no JSON encode/decode — this transport implements\n// Channel directly (not StringTransport). Structured clone is the worker's\n// native encoding, named by WORKER_VERSION.\n\nimport { frameReaders, type Channel } from \"../channel.js\";\n\n/** The worker encoding: native structured clone (no JSON). */\nexport const WORKER_VERSION = \"clone.v1\";\n\n/**\n * The subset of a worker the transport needs. The main-thread `Worker` and the\n * worker-global `self` both satisfy it; `terminate` is present only on the\n * main-thread side.\n */\nexport interface WorkerLike {\n postMessage(data: unknown): void;\n onmessage: ((event: { data: unknown }) => void) | null;\n onerror?: ((event: unknown) => void) | null;\n terminate?(): void;\n}\n\nexport class WorkerTransport implements Channel {\n private worker: WorkerLike;\n private pvtOnMessage: ((frame: Record<string, unknown>) => void) | null = null;\n private pvtSubscribe: ((handler: (frame: Record<string, unknown>) => void) => () => void) | null = null;\n private pvtOnClose: (() => void) | null = null;\n private closed = false;\n\n constructor(worker: WorkerLike) {\n this.worker = worker;\n worker.onmessage = (event) => {\n if (this.pvtOnMessage && !this.closed) {\n this.pvtOnMessage(event.data as Record<string, unknown>);\n }\n };\n // An uncaught error in the worker is the closest thing to an abrupt drop.\n if (\"onerror\" in worker) {\n worker.onerror = () => {\n this.closed = true;\n this.pvtOnClose?.();\n };\n }\n }\n\n get canSend(): boolean {\n return !this.closed;\n }\n\n onMessage(handler: (frame: Record<string, unknown>) => void): () => void {\n if (this.closed) throw new Error(\"WorkerTransport: closed\");\n // This transport moves frame objects itself, so it is a channel and not a transport: readers are kept\n // the way any channel keeps them, over the one handler the worker's own event needs.\n this.pvtSubscribe ??= frameReaders((deliver) => {\n this.pvtOnMessage = deliver;\n });\n return this.pvtSubscribe(handler);\n }\n\n onClose(handler: () => void): void {\n this.pvtOnClose = handler;\n }\n\n get hasHandler(): boolean {\n return this.pvtOnMessage !== null;\n }\n\n async send(frame: Record<string, unknown>): Promise<void> {\n if (this.closed) throw new Error(\"WorkerTransport: closed\");\n this.worker.postMessage(frame);\n }\n\n async close(): Promise<void> {\n if (this.closed) return;\n this.closed = true;\n this.worker.terminate?.();\n }\n}\n","/* eslint-disable unicorn/prefer-add-event-listener */\n// ── WebSocket client spawner ────────────────────────────────────────────────────\n//\n// Connects to a `ws://` URL with the WHATWG WebSocket and returns the seam's frames over it. The socket must be\n// open before `$proto` can be sent, so the seam is composed first and its announcement waits for the open.\n//\n// How the socket is opened is the caller's: the second parameter constructs one from a URL, and an opener\n// replaces that with a function of the connection, which is where a header belongs. A token in a URL is a token\n// in every log that URL passes through.\n\nimport { WebSocketTransport } from \"../transports/websocket.js\";\nimport { seamOf } from \"../seam.js\";\nimport type { ClientSpawner } from \"../client.js\";\n\n/** The socket the spawner needs: what the transport reads and writes, and the open/error slots. */\nexport type OpenedWebSocket = {\n readonly readyState: number;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n onopen: (() => void) | null;\n onerror: ((err: unknown) => void) | null;\n onmessage: ((event: { data: unknown }) => void) | null;\n onclose: (() => void) | null;\n};\n\nexport type WebSocketCtor = new (url: string) => OpenedWebSocket;\n\n/**\n * How a socket is opened when a constructor is not enough: the URL to open, and the arguments this\n * spawn was called with. A per-tenant header, a bearer token, an authority the machine does not\n * already know — each of them is a fact about the connection, and each of them is the caller's to\n * express in its own runtime's shape rather than in one posipaki was taught.\n */\nexport type WebSocketOpener<Args = unknown> = (url: string, args: Args) => OpenedWebSocket;\n\nexport interface WebSocketClientOptions<Args = unknown> {\n /** Opens the socket. Given, it replaces the constructor. */\n open?: WebSocketOpener<Args>;\n}\n\nconst defaultWebSocket = (globalThis as { WebSocket?: WebSocketCtor }).WebSocket;\n\n/** What a socket failure says about itself.\n *\n * A WHATWG WebSocket reports an open failure as an ErrorEvent, and `String(event)` is\n * `[object ErrorEvent]`: its type, its message and the underlying error are all in its\n * own fields, so read those rather than stringify the event that carries them.\n */\nfunction describeSocketError(err: unknown): string {\n if (typeof err !== \"object\" || err === null) return String(err);\n const event = err as { type?: unknown; message?: unknown; error?: unknown };\n const cause = event.error instanceof Error ? event.error.message : event.error;\n const parts = [event.type, event.message, cause].filter(\n (part): part is string => typeof part === \"string\" && part.length > 0,\n );\n return parts.length > 0 ? parts.join(\": \") : String(err);\n}\n\nfunction openWebSocket(ws: { readonly readyState: number; onopen: (() => void) | null; onerror: ((err: unknown) => void) | null }): Promise<void> {\n return new Promise((resolve, reject) => {\n if (ws.readyState === 1 /* OPEN */) return resolve();\n ws.onopen = () => resolve();\n ws.onerror = (err) => reject(new Error(`websocket failed to open: ${describeSocketError(err)}`));\n });\n}\n\nexport function wsClientSpawner<Args = unknown>(\n url: string | ((args: Args) => string),\n WebSocketImpl: WebSocketCtor = defaultWebSocket as WebSocketCtor,\n opts: WebSocketClientOptions<Args> = {},\n): ClientSpawner<Args> {\n // The constructor is the default opener, so the socket is opened one way either way.\n const open: WebSocketOpener<Args> = opts.open ?? ((target) => new WebSocketImpl(target));\n return async (args) => {\n const target = typeof url === \"function\" ? url(args) : url;\n const ws = open(target, args);\n const transport = new WebSocketTransport(ws);\n // The seam is composed before the socket opens — its reader is installed synchronously — and the\n // announcement waits for the open, because a socket that is still connecting cannot carry it.\n const opened = openWebSocket(ws);\n const seam = seamOf(transport, { ready: opened });\n try {\n await opened;\n } catch (error) {\n // The seam refuses for the same reason; the open failure is the one worth naming, so it is the one\n // that leaves here and the other is not left unhandled.\n void seam.catch(() => undefined);\n throw error;\n }\n return seam;\n };\n}\n","// ── WebSocket server spawner ────────────────────────────────────────────────────\n//\n// Environment-specific: serves an already-upgraded WebSocket connection. The consumer's HTTP server does the\n// upgrade and hands us the socket; we wrap it in a WebSocketTransport and compose the seam over it.\n\nimport { WebSocketTransport, type WebSocketLike } from \"../transports/websocket.js\";\nimport { seamOf } from \"../seam.js\";\nimport type { Spawner } from \"../server.js\";\n\nexport function wsServerSpawner(ws: WebSocketLike): Spawner {\n return async () => seamOf(new WebSocketTransport(ws));\n}\n\n/**\n * A minimal Bun `ServerWebSocket` — `send`/`close`/`readyState` plus the data\n * slot the server uses to stash per-connection state. Bun delivers message and\n * close events through the `Bun.serve` websocket config callbacks, not through\n * properties on the socket, so the consumer routes them onto the adapter.\n */\nexport interface BunServerWebSocketLike {\n send(data: string | Uint8Array): void;\n close(code?: number, reason?: string): void;\n readonly readyState: number;\n data?: unknown;\n}\n\n/**\n * Adapt a Bun server socket to the `WebSocketLike` the transport consumes. The\n * returned adapter owns settable `onmessage`/`onclose` slots; the consumer\n * routes Bun's `message`/`close` config callbacks into them, e.g.:\n *\n * ```ts\n * Bun.serve({\n * websocket: {\n * open(ws) {\n * const adapter = bunServerWebSocket(ws);\n * ws.data = adapter;\n * serveRemoteActor(actor, wsServerSpawner(adapter));\n * },\n * message(ws, msg) { (ws.data as WebSocketLike).onmessage?.({ data: msg }); },\n * close(ws) { (ws.data as WebSocketLike).onclose?.(); },\n * },\n * });\n * ```\n */\nexport function bunServerWebSocket(ws: BunServerWebSocketLike): WebSocketLike {\n return {\n send: (data) => ws.send(data),\n close: (code, reason) => ws.close(code, reason),\n // Read through on every ask: a socket's state changes, and a snapshot taken at the upgrade would keep\n // saying \"opening\" for the life of the connection — which is a send that refuses forever.\n get readyState() {\n return ws.readyState;\n },\n onmessage: null,\n onclose: null,\n };\n}\n\n/** The `ws` package's server-side socket, as little of it as is used here. */\nexport interface NodeServerWebSocketLike {\n send(data: string | Uint8Array): void;\n close(code?: number, reason?: string): void;\n readonly readyState: number;\n on(event: \"message\", handler: (data: unknown, isBinary: boolean) => void): unknown;\n on(event: \"close\", handler: () => void): unknown;\n on(event: \"error\", handler: (error: Error) => void): unknown;\n}\n\n/**\n * Adapt a node `ws` socket to the `WebSocketLike` the transport consumes.\n *\n * The difference from Bun's is who delivers the events: `ws` hands them to listeners on the socket itself, so the\n * adapter subscribes once and routes them onto the settable slots. An error ends the connection the way a close\n * does — the transport hears one ending, not two.\n *\n * ```ts\n * server.on(\"upgrade\", (request, socket, head) => {\n * wss.handleUpgrade(request, socket, head, (ws) => {\n * serveRemoteActor(actor, wsServerSpawner(nodeServerWebSocket(ws)));\n * });\n * });\n * ```\n */\nexport function nodeServerWebSocket(ws: NodeServerWebSocketLike): WebSocketLike {\n const adapter: WebSocketLike = {\n send: (data) => ws.send(data),\n close: (code, reason) => ws.close(code, reason),\n // Read through on every ask, for the same reason as Bun's: a snapshot keeps saying \"opening\".\n get readyState() {\n return ws.readyState;\n },\n onmessage: null,\n onclose: null,\n };\n let ended = false;\n const finish = (): void => {\n if (ended) return;\n ended = true;\n adapter.onclose?.();\n };\n ws.on(\"message\", (data, isBinary) => {\n const bytes = Array.isArray(data)\n ? Buffer.concat(data as Uint8Array[])\n : Buffer.isBuffer(data)\n ? data\n : Buffer.from(data as ArrayBuffer);\n // A text frame has to come back as text. `ws` hands over a Buffer either way and puts the distinction in\n // its second argument; the transport reads a string as a line and bytes as the body a line announced, so a\n // text frame passed on as bytes is a line nobody ever terminates.\n adapter.onmessage?.({ data: isBinary ? bytes : bytes.toString(\"utf8\") });\n });\n ws.on(\"close\", finish);\n ws.on(\"error\", finish);\n return adapter;\n}\n","// ── Worker client spawner (command) ─────────────────────────────────────────────\n//\n// Environment-specific: spawns a Worker with the WHATWG `new Worker(url)` API,\n// wraps it in a WorkerTransport, and returns a frame Channel with the $proto\n// handshake already validated.\n\nimport { WorkerTransport, WORKER_VERSION, type WorkerLike } from \"../transports/worker.js\";\nimport { isProto } from \"../channel.js\";\nimport type { ClientSpawner } from \"../client.js\";\n\nexport type WorkerCtor = new (url: string) => WorkerLike;\n\nconst defaultWorker = (globalThis as { Worker?: WorkerCtor }).Worker;\n\nexport function workerClientSpawner<Args = unknown>(\n url: string | ((args: Args) => string),\n WorkerImpl: WorkerCtor = defaultWorker as WorkerCtor,\n): ClientSpawner<Args> {\n return async (args) => {\n const target = typeof url === \"function\" ? url(args) : url;\n const channel = new WorkerTransport(new WorkerImpl(target));\n\n const protoFrame = await new Promise<Record<string, unknown>>((resolve) => {\n const off = channel.onMessage((frame) => {\n off();\n resolve(frame);\n });\n });\n\n if (!isProto(protoFrame) || protoFrame.$proto !== WORKER_VERSION) {\n throw new Error(`unsupported protocol: ${JSON.stringify(protoFrame).slice(0, 50)}`);\n }\n return channel;\n };\n}\n","// ── Worker server spawner (self) ────────────────────────────────────────────────\n//\n// Environment-specific: serves the worker-global scope (`self`) as a frame\n// Channel with the $proto handshake already sent. The worker entry script calls\n// `serveRemoteActor(actor, workerSelfSpawner())`.\n\nimport { WorkerTransport, WORKER_VERSION, type WorkerLike } from \"../transports/worker.js\";\nimport type { Spawner } from \"../server.js\";\n\nexport function workerSelfSpawner(selfScope: WorkerLike = globalThis as unknown as WorkerLike): Spawner {\n return async () => {\n const channel = new WorkerTransport(selfScope);\n await channel.send({ $proto: WORKER_VERSION });\n return channel;\n };\n}\n"],"mappings":"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"}
|
package/dist/remote/node.d.ts
CHANGED
|
@@ -2,9 +2,9 @@ import { a as Message } from "../types-U3NvKgpg.js";
|
|
|
2
2
|
import { h as MethodOptions, l as ActorDefinition, p as HandlerOptions, v as ReflectionOptions } from "../hooks-DO-efRM_.js";
|
|
3
3
|
import { K as StringTransport, W as Channel, d as stderrSink, l as OutputSink, m as Listener, n as WebSocketLike, o as ClientSpawner, u as pumpOutput } from "../websocket-DNSNv77Y.js";
|
|
4
4
|
import { ChildProcess, StdioOptions } from "node:child_process";
|
|
5
|
+
import { IncomingMessage } from "node:http";
|
|
5
6
|
import { FileHandle } from "node:fs/promises";
|
|
6
7
|
import { Duplex, Readable, Writable } from "node:stream";
|
|
7
|
-
import { IncomingMessage } from "node:http";
|
|
8
8
|
//#region src/remote/transports/fifo.d.ts
|
|
9
9
|
declare class FifoUtf8NlineTransport implements StringTransport {
|
|
10
10
|
private readFd;
|
package/dist/remote/node.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as LIB_VERSION } from "../version-
|
|
2
|
-
import {
|
|
3
|
-
import { A as channelThrough, G as VERSION, M as errorReason$1, Q as json1Channel, a as remoteClient, f as jsonSeam, h as seamOf, j as errorFrame$1, n as WebSocketTransport, nt as lowBand, o as pumpOutput, r as readParts, s as stderrSink, u as serveRemoteActor, w as StreamStore, z as isProto } from "../websocket-
|
|
1
|
+
import { t as LIB_VERSION } from "../version-_BGyK0ZQ.js";
|
|
2
|
+
import { a as streamChannel } from "../port-ByBSeVPm.js";
|
|
3
|
+
import { A as channelThrough, G as VERSION, M as errorReason$1, Q as json1Channel, a as remoteClient, f as jsonSeam, h as seamOf, j as errorFrame$1, n as WebSocketTransport, nt as lowBand, o as pumpOutput, r as readParts, s as stderrSink, u as serveRemoteActor, w as StreamStore, z as isProto } from "../websocket-Dqv9GfDy.js";
|
|
4
4
|
import { execFile, spawn } from "node:child_process";
|
|
5
5
|
import { chmod, mkdir, mkdtemp, open, readFile, rm, stat, unlink } from "node:fs/promises";
|
|
6
6
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types-U3NvKgpg.d.ts","names":[],"sources":["../src/process.async.
|
|
1
|
+
{"version":3,"file":"types-U3NvKgpg.d.ts","names":[],"sources":["../src/process.async.ts","../src/types.d.ts"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//#region src/version.ts
|
|
2
2
|
/** posipaki's release version. Must equal package.json's `version`. */
|
|
3
|
-
const LIB_VERSION = "0.
|
|
3
|
+
const LIB_VERSION = "0.50.0";
|
|
4
4
|
//#endregion
|
|
5
5
|
export { LIB_VERSION as t };
|
|
6
6
|
|
|
7
|
-
//# sourceMappingURL=version-
|
|
7
|
+
//# sourceMappingURL=version-_BGyK0ZQ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version-
|
|
1
|
+
{"version":3,"file":"version-_BGyK0ZQ.js","names":[],"sources":["../src/version.ts"],"sourcesContent":["// ── Library identity ───────────────────────────────────────────────────────\n//\n// posipaki's own release version, as a plain inlined string. A consumer that\n// stages artifacts onto a host it knows nothing about has to name those\n// artifacts after which posipaki they speak, and the bundle that runs there has\n// no node_modules and no posipaki on disk — it can never read a file to find out.\n//\n// It is not the protocol version: that is `VERSION` in ./remote (`json.v1`), and\n// it does not move between releases. The two answer different questions.\n//\n// version.test.ts pins this to package.json, so a release that forgets to bump it\n// fails the suite rather than shipping a lie.\n\n/** posipaki's release version. Must equal package.json's `version`. */\nexport const LIB_VERSION = \"0.50.0\";\n"],"mappings":";;AAcA,MAAa,cAAc"}
|
|
@@ -2,7 +2,7 @@ import { r as makeWaiter } from "./util-Brtfgv68.js";
|
|
|
2
2
|
import { r as isProcess } from "./process.async-B1FqZyD1.js";
|
|
3
3
|
import { s as stopPropagation } from "./hooks-CgXhmY4r.js";
|
|
4
4
|
import { t as defineActor } from "./define-actor-DR1P1PA5.js";
|
|
5
|
-
import {
|
|
5
|
+
import { a as held, i as splitChannel } from "./fetch-DBJeP47R.js";
|
|
6
6
|
//#region src/remote/process-ref.ts
|
|
7
7
|
/** The key that marks a process reference inside an otherwise JSON value. */
|
|
8
8
|
const PROCESS_REF = "$p";
|
|
@@ -3311,4 +3311,4 @@ var WebSocketTransport = class {
|
|
|
3311
3311
|
//#endregion
|
|
3312
3312
|
export { readFrame as $, channelThrough as A, isRelease as B, RemoteStream as C, isStreamRef as D, isBytesRef as E, isExit as F, VERSION as G, isState as H, isInit as I, decode as J, badFrame as K, isMsg as L, errorReason as M, frameReaders as N, makeSender as O, isError as P, json1Channel as Q, isPause as R, RemoteBlob as S, isBlobRef as T, isStop as U, isResume as V, BAD_PREFIX_LIMIT as W, isBad as X, encode as Y, json1 as Z, HandshakeError as _, remoteClient as a, protoFrame as b, oneConnection as c, frameChannel as d, PROCESS_REF as et, jsonSeam as f, HANDSHAKE_TIMEOUT_MS as g, seamOf as h, CHANNEL_LOST as i, errorFrame as j, RemoteProcess as k, reactor as l, seamAnnounced as m, WebSocketTransport as n, lowBand as nt, pumpOutput as o, jsonSeamParts as p, badReason as q, readParts as r, rootIdFor as rt, stderrSink as s, WS_OPEN as t, isRemoteProcess as tt, serveRemoteActor as u, PROTO_KEY as v, StreamStore as w, protoVersion as x, handshake as y, isProto as z };
|
|
3313
3313
|
|
|
3314
|
-
//# sourceMappingURL=websocket-
|
|
3314
|
+
//# sourceMappingURL=websocket-Dqv9GfDy.js.map
|