@statewalker/webrun-rpc 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +549 -0
  2. package/dist/byte-channel.d.ts +15 -0
  3. package/dist/byte-channel.d.ts.map +1 -0
  4. package/dist/call-bidi.d.ts +25 -0
  5. package/dist/call-bidi.d.ts.map +1 -0
  6. package/dist/call-port.d.ts +37 -0
  7. package/dist/call-port.d.ts.map +1 -0
  8. package/dist/cancel-channel.d.ts +15 -0
  9. package/dist/cancel-channel.d.ts.map +1 -0
  10. package/dist/close-signal.d.ts +36 -0
  11. package/dist/close-signal.d.ts.map +1 -0
  12. package/dist/connect-serve.d.ts +104 -0
  13. package/dist/connect-serve.d.ts.map +1 -0
  14. package/dist/duplex-over-port.d.ts +49 -0
  15. package/dist/duplex-over-port.d.ts.map +1 -0
  16. package/dist/index.d.ts +19 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +1180 -0
  19. package/dist/io-handle.d.ts +17 -0
  20. package/dist/io-handle.d.ts.map +1 -0
  21. package/dist/io-send.d.ts +26 -0
  22. package/dist/io-send.d.ts.map +1 -0
  23. package/dist/listen-bidi.d.ts +11 -0
  24. package/dist/listen-bidi.d.ts.map +1 -0
  25. package/dist/listen-port.d.ts +16 -0
  26. package/dist/listen-port.d.ts.map +1 -0
  27. package/dist/message-target.d.ts +16 -0
  28. package/dist/message-target.d.ts.map +1 -0
  29. package/dist/multiplex-port.d.ts +12 -0
  30. package/dist/multiplex-port.d.ts.map +1 -0
  31. package/dist/port-types.d.ts +86 -0
  32. package/dist/port-types.d.ts.map +1 -0
  33. package/dist/recieve.d.ts +35 -0
  34. package/dist/recieve.d.ts.map +1 -0
  35. package/dist/send.d.ts +23 -0
  36. package/dist/send.d.ts.map +1 -0
  37. package/dist/structured-codec.d.ts +12 -0
  38. package/dist/structured-codec.d.ts.map +1 -0
  39. package/dist/through-abort.d.ts +8 -0
  40. package/dist/through-abort.d.ts.map +1 -0
  41. package/dist/transfer-port-mux.d.ts +39 -0
  42. package/dist/transfer-port-mux.d.ts.map +1 -0
  43. package/dist/virtual-port.d.ts +19 -0
  44. package/dist/virtual-port.d.ts.map +1 -0
  45. package/package.json +51 -0
  46. package/src/byte-channel.ts +109 -0
  47. package/src/call-bidi.ts +60 -0
  48. package/src/call-port.ts +119 -0
  49. package/src/cancel-channel.ts +42 -0
  50. package/src/close-signal.ts +43 -0
  51. package/src/connect-serve.ts +208 -0
  52. package/src/duplex-over-port.ts +471 -0
  53. package/src/index.ts +29 -0
  54. package/src/io-handle.ts +40 -0
  55. package/src/io-send.ts +70 -0
  56. package/src/listen-bidi.ts +31 -0
  57. package/src/listen-port.ts +47 -0
  58. package/src/message-target.ts +18 -0
  59. package/src/multiplex-port.ts +134 -0
  60. package/src/port-types.ts +80 -0
  61. package/src/recieve.ts +89 -0
  62. package/src/send.ts +60 -0
  63. package/src/structured-codec.ts +30 -0
  64. package/src/through-abort.ts +32 -0
  65. package/src/transfer-port-mux.ts +106 -0
  66. package/src/virtual-port.ts +71 -0
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Per-port AbortSignal that fires when the underlying transport adapter
3
+ * (libp2p stream, LiveKit participant, WebSocket, …) detects closure.
4
+ *
5
+ * Native `MessagePort` does NOT surface close events to either entangled
6
+ * half — neither a `close` event, nor a `messageerror`, nor any signal at
7
+ * all. Closing one half just silently stops message delivery on the other.
8
+ * A WHATWG proposal exists (https://github.com/whatwg/html/issues/1766) but
9
+ * no browser ships it.
10
+ *
11
+ * This module is the work-around: `bindBytesToPort` creates an
12
+ * `AbortController` per port and aborts it when `transport.onClose` fires.
13
+ * Consumers (`callPort`, `recieve`, `send`, application code) look up the
14
+ * signal via `getPortCloseSignal(port)` and combine it into their own
15
+ * abort plumbing — typically with `AbortSignal.any([userSignal, closeSignal])`
16
+ * — so pending awaits reject cleanly instead of hanging until a per-call
17
+ * timeout fires.
18
+ *
19
+ * Ports created via raw `new MessageChannel()` (i.e., without going through
20
+ * `bindBytesToPort`) return `undefined` from `getPortCloseSignal` — there's
21
+ * no underlying transport so there's nothing to track.
22
+ */
23
+ import type { MessageTarget } from "./message-target.js";
24
+ /**
25
+ * Internal: register a port's close signal. Called once by `bindBytesToPort`.
26
+ *
27
+ * @internal
28
+ */
29
+ export declare function setPortCloseSignal(port: MessageTarget, signal: AbortSignal): void;
30
+ /**
31
+ * Return the AbortSignal that fires when `port`'s transport closes, or
32
+ * `undefined` if `port` is not transport-backed (e.g., a raw
33
+ * `MessageChannel().port1`).
34
+ */
35
+ export declare function getPortCloseSignal(port: MessageTarget): AbortSignal | undefined;
36
+ //# sourceMappingURL=close-signal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"close-signal.d.ts","sourceRoot":"","sources":["../src/close-signal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIzD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAEjF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,GAAG,WAAW,GAAG,SAAS,CAE/E"}
@@ -0,0 +1,104 @@
1
+ import type { Connect, Serve } from "@statewalker/webrun-streams";
2
+ import type { MessageTarget } from "./message-target.js";
3
+ import type { PortCodec, PortMux } from "./port-types.js";
4
+ /** What a mux calls when the peer opens a port. `false` rejects it. */
5
+ export type OnPort = (port: MessageTarget, meta?: unknown) => boolean | undefined;
6
+ /**
7
+ * A source of ports, built around the handler that will answer them.
8
+ *
9
+ * WHY A FACTORY AND NOT A `PortMux`. Every mux decides accept-or-reject
10
+ * **synchronously**, inside the `open` envelope, and tells the peer on the
11
+ * spot — `multiplexPort` posts `{type:"close", reason:"rejected"}` before
12
+ * returning. There is no "decide later", and layer 1 refuses to queue what it
13
+ * cannot deliver ("Drop, never queue").
14
+ *
15
+ * So a mux handed over already built has a window between its construction and
16
+ * its handler being attached, and a port arriving in that window is rejected
17
+ * outright — measured: zero messages delivered, and the peer told `rejected`
18
+ * for a call that was merely early. A factory closes the window by
19
+ * construction: the mux cannot exist before the thing that answers it.
20
+ *
21
+ * `connect` passes no handler, which is how a caller declines inbound ports.
22
+ * The same factory therefore serves both sides, and whichever of them called
23
+ * it owns the mux and closes it.
24
+ */
25
+ export type PortMuxFactory = (onPort?: OnPort) => PortMux | Promise<PortMux>;
26
+ export interface PortParams {
27
+ /**
28
+ * Where ports come from. See {@link PortMuxFactory}, and `overPipe` /
29
+ * `overPorts` for the two this package ships.
30
+ *
31
+ * This used to be a `MessageTarget` plus `side` and a credit window, which
32
+ * forced one strategy — an id table — on every transport. A libp2p
33
+ * connection multiplexes already, so that stacked two multiplexers with no
34
+ * way to tell which one stalled; a transferable boundary moves real ports
35
+ * and needs no table at all. The strategy belongs to whoever knows the
36
+ * transport, which is never this file.
37
+ */
38
+ mux: PortMuxFactory;
39
+ /** Per-stream inactivity timeout in ms, reset by any chunk in either direction.
40
+ *
41
+ * Unset — the default — means no timeout at all: a slow consumer is
42
+ * throttled, never failed. Set it on the side that must not hang. */
43
+ timeout?: number;
44
+ }
45
+ /**
46
+ * One port source in, one caller `Duplex` out.
47
+ *
48
+ * A call is a port: the mux allocates one, `duplexOverPort` runs the single
49
+ * invocation on it, and closing the stream closes the port. No stream ids, no
50
+ * framing and no credit accounting live here — the mux owns the first, and
51
+ * `duplexOverPort` owns the one-chunk window that makes memory bounded.
52
+ */
53
+ export declare const connect: Connect<PortParams>;
54
+ /**
55
+ * One port source in, `handler` serving every call that arrives on it.
56
+ *
57
+ * Each inbound port is one invocation, so the factory's `onPort` is the accept
58
+ * loop: it installs `handler` on the new port and nothing else. The returned
59
+ * teardown abandons the streams still running *before* dropping the mux, so
60
+ * the peer's callers reject with "the peer abandoned the stream" instead of
61
+ * parking forever on a port that has silently gone inert — layer 1's close is
62
+ * not observable to layer 2, which is why the notice has to be posted
63
+ * deliberately.
64
+ */
65
+ export declare const serve: Serve<PortParams>;
66
+ export interface OverPipeOptions {
67
+ /** How envelopes are placed on the pipe. `structuredCodec` for a `MessagePort`. */
68
+ codec: PortCodec;
69
+ /**
70
+ * Id parity. The initiator allocates even ids, the responder odd, so both
71
+ * ends may open concurrently with no negotiation. The two ends of one pair
72
+ * must disagree, or their ids collide.
73
+ */
74
+ side?: "initiator" | "responder";
75
+ /** Ceiling on concurrently open ports. Bounds the id table only. */
76
+ maxPorts?: number;
77
+ /**
78
+ * Largest payload one message may carry, if the pipe imposes a limit.
79
+ * Bodies are split to fit. Leave at least 256 bytes of margin below the
80
+ * transport's real ceiling — see `PortMuxOptions.maxMessageSize`.
81
+ */
82
+ maxMessageSize?: number;
83
+ }
84
+ /**
85
+ * Ports over ONE PIPE OF BYTES — a `MessagePort`, a worker, a WebSocket.
86
+ *
87
+ * There is no second port to be had, so `multiplexPort`'s id table invents
88
+ * them. Use this when the transport gives you exactly one channel.
89
+ */
90
+ export declare function overPipe(pipe: MessageTarget, options: OverPipeOptions): PortMuxFactory;
91
+ /**
92
+ * Ports from a source that already has them — a transferable boundary, or a
93
+ * transport that multiplexes on its own (libp2p's yamux, say).
94
+ *
95
+ * A pass-through, so an adapter that builds its own `PortMux` plugs in without
96
+ * this package learning what that transport is. It exists to make the
97
+ * three-way choice legible at the call site rather than to do work:
98
+ *
99
+ * ```ts
100
+ * await serve({ mux: overPorts((onPort) => libp2pPortMux({ node, onPort })) }, handler);
101
+ * ```
102
+ */
103
+ export declare function overPorts(factory: PortMuxFactory): PortMuxFactory;
104
+ //# sourceMappingURL=connect-serve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect-serve.d.ts","sourceRoot":"","sources":["../src/connect-serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAU,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAM1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAU1D,uEAAuE;AACvE,MAAM,MAAM,MAAM,GAAG,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,OAAO,GAAG,SAAS,CAAC;AAElF;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAE7E,MAAM,WAAW,UAAU;IACzB;;;;;;;;;;OAUG;IACH,GAAG,EAAE,cAAc,CAAC;IACpB;;;yEAGqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,OAAO,EAAE,OAAO,CAAC,UAAU,CA0BvC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,UAAU,CAqDnC,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,mFAAmF;IACnF,KAAK,EAAE,SAAS,CAAC;IACjB;;;;OAIG;IACH,IAAI,CAAC,EAAE,WAAW,GAAG,WAAW,CAAC;IACjC,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,eAAe,GAAG,cAAc,CAEtF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,cAAc,CAEjE"}
@@ -0,0 +1,49 @@
1
+ import { type Duplex } from "@statewalker/webrun-streams";
2
+ import type { MessageTarget } from "./message-target.js";
3
+ /**
4
+ * The `type` of the out-of-band notice a side posts when it abandons a stream.
5
+ *
6
+ * Layer 1's `close` is not observable to layer 2 — a closed virtual port drops
7
+ * its listeners silently and is indistinguishable from a working port nobody
8
+ * is answering — so the peer would otherwise wait forever. Exported because
9
+ * tests and adapters assert on it.
10
+ */
11
+ export declare const STREAM_ABORT = "webrun-rpc:stream-abort";
12
+ export interface DuplexOverPortOptions {
13
+ /**
14
+ * Largest payload one chunk may carry, from `PortMux.maxMessageSize` (spec
15
+ * D10). Bodies are split to fit with `toChunks`. Unset means no limit and no
16
+ * splitting.
17
+ */
18
+ maxMessageSize?: number;
19
+ /**
20
+ * Inactivity timeout for the whole stream, in ms: the clock is reset by any
21
+ * chunk in either direction, and elapsing aborts the stream. Unset — the
22
+ * default — means no timeout at all (spec D8): a slow consumer is throttled,
23
+ * never failed. Any finite default would reintroduce F5 at a different
24
+ * threshold.
25
+ */
26
+ timeout?: number;
27
+ /** Logging function; defaults to a no-op. */
28
+ log?: (...args: unknown[]) => void;
29
+ }
30
+ /**
31
+ * One port in, one `Duplex` out (spec D9).
32
+ *
33
+ * The returned `Duplex` runs a single stream on `port`: the caller's `input`
34
+ * is sent chunk by chunk with `callPort`, and the handler's output arrives the
35
+ * same way on the other channel. Within each direction the next chunk is never
36
+ * sent until the previous one has been delivered *and* pulled past by the
37
+ * consumer (spec D11) — the reply to a chunk call *is* the confirmation, and
38
+ * `listenPort` withholds it until then.
39
+ *
40
+ * A stream port carries exactly one invocation. To make several calls, open
41
+ * several ports: `mux.openPort({ kind: "stream" })` per call.
42
+ */
43
+ export declare function duplexOverPort(port: MessageTarget, options?: DuplexOverPortOptions): Duplex;
44
+ /**
45
+ * Installs `handler` as the serving side of one stream on `port`. Returns an
46
+ * idempotent teardown that abandons the stream and notifies the peer.
47
+ */
48
+ export declare function serveDuplexOverPort(port: MessageTarget, handler: Duplex, options?: DuplexOverPortOptions): () => void;
49
+ //# sourceMappingURL=duplex-over-port.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"duplex-over-port.d.ts","sourceRoot":"","sources":["../src/duplex-over-port.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,MAAM,EAOZ,MAAM,6BAA6B,CAAC;AAGrC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,4BAA4B,CAAC;AActD,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CACpC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,GAAE,qBAA0B,GAAG,MAAM,CAE/F;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,qBAA0B,GAClC,MAAM,IAAI,CA6BZ"}
@@ -0,0 +1,19 @@
1
+ export { byteChannelFromMessagePort } from "./byte-channel.js";
2
+ export * from "./call-bidi.js";
3
+ export * from "./call-port.js";
4
+ export * from "./cancel-channel.js";
5
+ export { getPortCloseSignal, setPortCloseSignal } from "./close-signal.js";
6
+ export { connect, type OnPort, type OverPipeOptions, overPipe, overPorts, type PortMuxFactory, type PortParams, serve, } from "./connect-serve.js";
7
+ export * from "./duplex-over-port.js";
8
+ export * from "./io-handle.js";
9
+ export * from "./io-send.js";
10
+ export * from "./listen-bidi.js";
11
+ export * from "./listen-port.js";
12
+ export * from "./message-target.js";
13
+ export { DEFAULT_MAX_PORTS, multiplexPort } from "./multiplex-port.js";
14
+ export type { PortCodec, PortEnvelope, PortMux, PortMuxOptions } from "./port-types.js";
15
+ export * from "./recieve.js";
16
+ export * from "./send.js";
17
+ export { structuredCodec } from "./structured-codec.js";
18
+ export * from "./transfer-port-mux.js";
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EACL,OAAO,EACP,KAAK,MAAM,EACX,KAAK,eAAe,EACpB,QAAQ,EACR,SAAS,EACT,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,GACN,MAAM,oBAAoB,CAAC;AAC5B,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACvE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACxF,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,cAAc,wBAAwB,CAAC"}