transport-io 0.0.1

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 (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +28 -0
  3. package/dist/adapter.d.ts +69 -0
  4. package/dist/adapter.d.ts.map +1 -0
  5. package/dist/adapter.js +51 -0
  6. package/dist/adapter.js.map +1 -0
  7. package/dist/client.d.ts +60 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/client.js +162 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/codec.d.ts +5 -0
  12. package/dist/codec.d.ts.map +1 -0
  13. package/dist/codec.js +47 -0
  14. package/dist/codec.js.map +1 -0
  15. package/dist/contract.d.ts +79 -0
  16. package/dist/contract.d.ts.map +1 -0
  17. package/dist/contract.js +54 -0
  18. package/dist/contract.js.map +1 -0
  19. package/dist/datagram.d.ts +29 -0
  20. package/dist/datagram.d.ts.map +1 -0
  21. package/dist/datagram.js +92 -0
  22. package/dist/datagram.js.map +1 -0
  23. package/dist/errors.d.ts +8 -0
  24. package/dist/errors.d.ts.map +1 -0
  25. package/dist/errors.js +11 -0
  26. package/dist/errors.js.map +1 -0
  27. package/dist/framer.d.ts +21 -0
  28. package/dist/framer.d.ts.map +1 -0
  29. package/dist/framer.js +110 -0
  30. package/dist/framer.js.map +1 -0
  31. package/dist/handshake.d.ts +34 -0
  32. package/dist/handshake.d.ts.map +1 -0
  33. package/dist/handshake.js +79 -0
  34. package/dist/handshake.js.map +1 -0
  35. package/dist/hub.d.ts +43 -0
  36. package/dist/hub.d.ts.map +1 -0
  37. package/dist/hub.js +149 -0
  38. package/dist/hub.js.map +1 -0
  39. package/dist/index.d.ts +19 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +18 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/origin.d.ts +18 -0
  44. package/dist/origin.d.ts.map +1 -0
  45. package/dist/origin.js +72 -0
  46. package/dist/origin.js.map +1 -0
  47. package/dist/protocol.d.ts +79 -0
  48. package/dist/protocol.d.ts.map +1 -0
  49. package/dist/protocol.js +76 -0
  50. package/dist/protocol.js.map +1 -0
  51. package/dist/queue.d.ts +45 -0
  52. package/dist/queue.d.ts.map +1 -0
  53. package/dist/queue.js +94 -0
  54. package/dist/queue.js.map +1 -0
  55. package/dist/server.d.ts +45 -0
  56. package/dist/server.d.ts.map +1 -0
  57. package/dist/server.js +127 -0
  58. package/dist/server.js.map +1 -0
  59. package/dist/session.d.ts +94 -0
  60. package/dist/session.d.ts.map +1 -0
  61. package/dist/session.js +645 -0
  62. package/dist/session.js.map +1 -0
  63. package/dist/testing/hostile-adapter.d.ts +44 -0
  64. package/dist/testing/hostile-adapter.d.ts.map +1 -0
  65. package/dist/testing/hostile-adapter.js +116 -0
  66. package/dist/testing/hostile-adapter.js.map +1 -0
  67. package/dist/testing/index.d.ts +8 -0
  68. package/dist/testing/index.d.ts.map +1 -0
  69. package/dist/testing/index.js +8 -0
  70. package/dist/testing/index.js.map +1 -0
  71. package/dist/transport/browser.d.ts +8 -0
  72. package/dist/transport/browser.d.ts.map +1 -0
  73. package/dist/transport/browser.js +89 -0
  74. package/dist/transport/browser.js.map +1 -0
  75. package/dist/transport/fails.node.d.ts +33 -0
  76. package/dist/transport/fails.node.d.ts.map +1 -0
  77. package/dist/transport/fails.node.js +184 -0
  78. package/dist/transport/fails.node.js.map +1 -0
  79. package/dist/transport/loopback.d.ts +11 -0
  80. package/dist/transport/loopback.d.ts.map +1 -0
  81. package/dist/transport/loopback.js +98 -0
  82. package/dist/transport/loopback.js.map +1 -0
  83. package/dist/transport/moq.node.d.ts +19 -0
  84. package/dist/transport/moq.node.d.ts.map +1 -0
  85. package/dist/transport/moq.node.js +211 -0
  86. package/dist/transport/moq.node.js.map +1 -0
  87. package/dist/transport/parity-suite.d.ts +40 -0
  88. package/dist/transport/parity-suite.d.ts.map +1 -0
  89. package/dist/transport/parity-suite.js +133 -0
  90. package/dist/transport/parity-suite.js.map +1 -0
  91. package/dist/transport/types.d.ts +36 -0
  92. package/dist/transport/types.d.ts.map +1 -0
  93. package/dist/transport/types.js +2 -0
  94. package/dist/transport/types.js.map +1 -0
  95. package/dist/transport/unreliable.d.ts +37 -0
  96. package/dist/transport/unreliable.d.ts.map +1 -0
  97. package/dist/transport/unreliable.js +59 -0
  98. package/dist/transport/unreliable.js.map +1 -0
  99. package/package.json +55 -0
@@ -0,0 +1,98 @@
1
+ class Side {
2
+ peer;
3
+ #emitStreamCb;
4
+ #bidiCb;
5
+ #datagramCb;
6
+ // A real transport queues an incoming stream or datagram until the application reads
7
+ // it. Dispatching immediately would drop anything that arrives before the peer has
8
+ // registered its handler, which is a race the real thing does not have.
9
+ #pendingEmit = [];
10
+ #pendingBidi = [];
11
+ #pendingDatagrams = [];
12
+ #resolveClosed;
13
+ #closedFlag = false;
14
+ closed;
15
+ #maxDatagram;
16
+ constructor(maxDatagram) {
17
+ this.#maxDatagram = maxDatagram;
18
+ this.closed = new Promise((res) => {
19
+ this.#resolveClosed = res;
20
+ });
21
+ }
22
+ async openEmitStream() {
23
+ const { readable, writable } = new TransformStream();
24
+ queueMicrotask(() => this.peer.#acceptEmit(readable));
25
+ return writable;
26
+ }
27
+ onEmitStream(cb) {
28
+ this.#emitStreamCb = cb;
29
+ while (this.#pendingEmit.length > 0)
30
+ cb(this.#pendingEmit.shift());
31
+ }
32
+ #acceptEmit(r) {
33
+ if (this.#emitStreamCb === undefined)
34
+ this.#pendingEmit.push(r);
35
+ else
36
+ this.#emitStreamCb(r);
37
+ }
38
+ async openBidi() {
39
+ const up = new TransformStream();
40
+ const down = new TransformStream();
41
+ queueMicrotask(() => this.peer.#acceptBidi({ readable: up.readable, writable: down.writable }));
42
+ return { readable: down.readable, writable: up.writable };
43
+ }
44
+ onBidi(cb) {
45
+ this.#bidiCb = cb;
46
+ while (this.#pendingBidi.length > 0)
47
+ cb(this.#pendingBidi.shift());
48
+ }
49
+ #acceptBidi(s) {
50
+ if (this.#bidiCb === undefined)
51
+ this.#pendingBidi.push(s);
52
+ else
53
+ this.#bidiCb(s);
54
+ }
55
+ sendDatagram(bytes) {
56
+ if (this.#closedFlag)
57
+ return;
58
+ // Mirrors the real transport: an oversized datagram is accepted and discarded with no
59
+ // error. Our layer checks the size before ever reaching here.
60
+ if (bytes.byteLength > this.#maxDatagram)
61
+ return;
62
+ const copy = bytes.slice();
63
+ queueMicrotask(() => this.peer.#acceptDatagram(copy));
64
+ }
65
+ onDatagram(cb) {
66
+ this.#datagramCb = cb;
67
+ while (this.#pendingDatagrams.length > 0)
68
+ cb(this.#pendingDatagrams.shift());
69
+ }
70
+ #acceptDatagram(b) {
71
+ if (this.#datagramCb === undefined)
72
+ this.#pendingDatagrams.push(b);
73
+ else
74
+ this.#datagramCb(b);
75
+ }
76
+ maxDatagramSize() {
77
+ return this.#maxDatagram;
78
+ }
79
+ reliability() {
80
+ return 'supports-unreliable';
81
+ }
82
+ close(code, reason) {
83
+ if (this.#closedFlag)
84
+ return;
85
+ this.#closedFlag = true;
86
+ const info = { code, reason };
87
+ this.#resolveClosed(info);
88
+ queueMicrotask(() => this.peer.close(code, reason));
89
+ }
90
+ }
91
+ export function loopbackPair(maxDatagram = 1024) {
92
+ const a = new Side(maxDatagram);
93
+ const b = new Side(maxDatagram);
94
+ a.peer = b;
95
+ b.peer = a;
96
+ return [a, b];
97
+ }
98
+ //# sourceMappingURL=loopback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loopback.js","sourceRoot":"","sources":["../../src/transport/loopback.ts"],"names":[],"mappings":"AAUA,MAAM,IAAI;IACR,IAAI,CAAO;IACX,aAAa,CAAuD;IACpE,OAAO,CAAuC;IAC9C,WAAW,CAAuC;IAClD,qFAAqF;IACrF,mFAAmF;IACnF,wEAAwE;IAC/D,YAAY,GAAiC,EAAE,CAAA;IAC/C,YAAY,GAAiB,EAAE,CAAA;IAC/B,iBAAiB,GAAiB,EAAE,CAAA;IAC7C,cAAc,CAA4B;IAC1C,WAAW,GAAG,KAAK,CAAA;IACV,MAAM,CAAoB;IACnC,YAAY,CAAQ;IAEpB,YAAY,WAAmB;QAC7B,IAAI,CAAC,YAAY,GAAG,WAAW,CAAA;QAC/B,IAAI,CAAC,MAAM,GAAG,IAAI,OAAO,CAAY,CAAC,GAAG,EAAE,EAAE;YAC3C,IAAI,CAAC,cAAc,GAAG,GAAG,CAAA;QAC3B,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,IAAI,eAAe,EAA0B,CAAA;QAC5E,cAAc,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAA;QACrD,OAAO,QAAQ,CAAA;IACjB,CAAC;IACD,YAAY,CAAC,EAA2C;QACtD,IAAI,CAAC,aAAa,GAAG,EAAE,CAAA;QACvB,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC;YACjC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,EAAgC,CAAC,CAAA;IAC/D,CAAC;IACD,WAAW,CAAC,CAA6B;QACvC,IAAI,IAAI,CAAC,aAAa,KAAK,SAAS;YAAE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;;YAC1D,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,CAAA;IAC5B,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,EAAE,GAAG,IAAI,eAAe,EAA0B,CAAA;QACxD,MAAM,IAAI,GAAG,IAAI,eAAe,EAA0B,CAAA;QAC1D,cAAc,CAAC,GAAG,EAAE,CAClB,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAC1E,CAAA;QACD,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAA;IAC3D,CAAC;IACD,MAAM,CAAC,EAA2B;QAChC,IAAI,CAAC,OAAO,GAAG,EAAE,CAAA;QACjB,OAAO,IAAI,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC;YAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,KAAK,EAAgB,CAAC,CAAA;IAClF,CAAC;IACD,WAAW,CAAC,CAAa;QACvB,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS;YAAE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;;YACpD,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;IACtB,CAAC;IAED,YAAY,CAAC,KAAiB;QAC5B,IAAI,IAAI,CAAC,WAAW;YAAE,OAAM;QAC5B,sFAAsF;QACtF,8DAA8D;QAC9D,IAAI,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC,YAAY;YAAE,OAAM;QAChD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,EAAE,CAAA;QAC1B,cAAc,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAA;IACvD,CAAC;IACD,UAAU,CAAC,EAA2B;QACpC,IAAI,CAAC,WAAW,GAAG,EAAE,CAAA;QACrB,OAAO,IAAI,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC;YAAE,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAgB,CAAC,CAAA;IAC5F,CAAC;IACD,eAAe,CAAC,CAAa;QAC3B,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS;YAAE,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;;YAC7D,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAA;IAC1B,CAAC;IACD,eAAe;QACb,OAAO,IAAI,CAAC,YAAY,CAAA;IAC1B,CAAC;IAED,WAAW;QACT,OAAO,qBAAqB,CAAA;IAC9B,CAAC;IAED,KAAK,CAAC,IAAY,EAAE,MAAc;QAChC,IAAI,IAAI,CAAC,WAAW;YAAE,OAAM;QAC5B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAA;QACvB,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;QAC7B,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAA;QACzB,cAAc,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAA;IACrD,CAAC;CACF;AAED,MAAM,UAAU,YAAY,CAAC,WAAW,GAAG,IAAI;IAC7C,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,WAAW,CAAC,CAAA;IAC/B,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,WAAW,CAAC,CAAA;IAC/B,CAAC,CAAC,IAAI,GAAG,CAAC,CAAA;IACV,CAAC,CAAC,IAAI,GAAG,CAAC,CAAA;IACV,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;AACf,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { Connection } from './types.ts';
2
+ export interface MoqServerOptions {
3
+ readonly port: number;
4
+ readonly host?: string;
5
+ readonly cert: string;
6
+ readonly privKey: string;
7
+ }
8
+ export interface MoqListener {
9
+ readonly port: number;
10
+ sessions(): AsyncIterable<Connection>;
11
+ stop(): void;
12
+ }
13
+ export declare function listenMoq(opts: MoqServerOptions): Promise<MoqListener>;
14
+ export interface MoqClientOptions {
15
+ readonly url: string;
16
+ readonly certificateHash: Uint8Array;
17
+ }
18
+ export declare function connectMoq(opts: MoqClientOptions): Promise<Connection>;
19
+ //# sourceMappingURL=moq.node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"moq.node.d.ts","sourceRoot":"","sources":["../../src/transport/moq.node.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAyB,UAAU,EAAE,MAAM,YAAY,CAAA;AA0NnE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,IAAI,aAAa,CAAC,UAAU,CAAC,CAAA;IACrC,IAAI,IAAI,IAAI,CAAA;CACb;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAmB5E;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,eAAe,EAAE,UAAU,CAAA;CACrC;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CAY5E"}
@@ -0,0 +1,211 @@
1
+ /**
2
+ * The alternative transport, behind the same seam.
3
+ *
4
+ * `@moq/web-transport` is a NAPI-RS binding over a Rust QUIC stack. It is measured flat
5
+ * on the per-stream churn that costs the reference binding 11.6 KB (D66), but a byte
6
+ * count establishes exactly one property. This adapter exists so the existing suite can
7
+ * run against it unchanged and establish the rest.
8
+ *
9
+ * Its surface is promise-and-method based rather than WHATWG streams, so the wrapping
10
+ * here is thicker than in `fails.node.ts`. That is the seam doing its job.
11
+ *
12
+ * It depends on the per-platform NAPI packages directly rather than on
13
+ * `@moq/web-transport`, whose only entry point is raw TypeScript that Node refuses to
14
+ * strip inside `node_modules`. Those platform packages are published independently, each
15
+ * declaring `main` at its own `.node` binary, so this is an ordinary dependency and needs
16
+ * no cooperation from upstream. See D70.
17
+ */
18
+ import { createRequire } from 'node:module';
19
+ import { TransportError } from '../errors.js';
20
+ import { DATAGRAM_CONSERVATIVE_FLOOR } from '../protocol.js';
21
+ /**
22
+ * The per-platform NAPI packages, each of which declares `main` pointing at its own
23
+ * `.node` binary and carries matching `os`/`cpu` fields.
24
+ *
25
+ * This is a normal dependency on a published package's declared entry point. It is NOT
26
+ * the file-path import that made this transport unadoptable: that reached past
27
+ * `@moq/web-transport`'s `exports` map into its internal layout, which a patch release
28
+ * could move without a semver signal. These packages are the interface.
29
+ */
30
+ const PLATFORM_PACKAGES = {
31
+ 'darwin-arm64': '@moq/web-transport-darwin-arm64',
32
+ 'darwin-x64': '@moq/web-transport-darwin-x64',
33
+ 'linux-x64': '@moq/web-transport-linux-x64-gnu',
34
+ 'linux-arm64': '@moq/web-transport-linux-arm64-gnu',
35
+ 'win32-x64': '@moq/web-transport-win32-x64-msvc',
36
+ };
37
+ let cached;
38
+ function native() {
39
+ if (cached !== undefined)
40
+ return cached;
41
+ const key = `${process.platform}-${process.arch}`;
42
+ const pkg = PLATFORM_PACKAGES[key];
43
+ if (pkg === undefined) {
44
+ throw new TransportError('WT_NO_SUPPORT', `no prebuilt QUIC binding for ${key}`, `Supported: ${Object.keys(PLATFORM_PACKAGES).join(', ')}.`);
45
+ }
46
+ try {
47
+ cached = createRequire(import.meta.url)(pkg);
48
+ return cached;
49
+ }
50
+ catch (cause) {
51
+ throw new TransportError('WT_NO_SUPPORT', `could not load ${pkg}: ${cause.message}`, `Install it: npm install ${pkg}`);
52
+ }
53
+ }
54
+ const CHUNK = 65536;
55
+ function toReadable(recv) {
56
+ return new ReadableStream({
57
+ async pull(controller) {
58
+ try {
59
+ const chunk = await recv.read(CHUNK);
60
+ if (chunk === null) {
61
+ controller.close(); // FIN
62
+ return;
63
+ }
64
+ controller.enqueue(new Uint8Array(chunk));
65
+ }
66
+ catch (e) {
67
+ controller.error(e);
68
+ }
69
+ },
70
+ cancel(reason) {
71
+ // STOP_SENDING with an explicit code — no message-string parsing needed here.
72
+ void recv.stop(codeOf(reason)).catch(() => undefined);
73
+ },
74
+ });
75
+ }
76
+ function toWritable(send) {
77
+ return new WritableStream({
78
+ async write(chunk) {
79
+ await send.write(Buffer.from(chunk));
80
+ },
81
+ async close() {
82
+ await send.finish(); // FIN, leaving the read side open
83
+ },
84
+ abort(reason) {
85
+ // RESET_STREAM with an explicit code, likewise.
86
+ void send.reset(codeOf(reason)).catch(() => undefined);
87
+ },
88
+ });
89
+ }
90
+ /**
91
+ * Both `reset` and `stop` take a numeric code directly, so this transport needs no
92
+ * equivalent of `resetCodeFromError`. That function exists only because the reference
93
+ * binding drops the specification's `streamErrorCode` and leaves the message string as
94
+ * the sole carrier.
95
+ */
96
+ function codeOf(reason) {
97
+ if (typeof reason === 'number')
98
+ return reason & 0xff;
99
+ const m = /code:\s*(\d+)/i.exec(reason instanceof Error ? reason.message : String(reason));
100
+ return m?.[1] === undefined ? 0 : Number(m[1]) & 0xff;
101
+ }
102
+ class MoqConnection {
103
+ #s;
104
+ closed;
105
+ constructor(session) {
106
+ this.#s = session;
107
+ this.closed = session
108
+ .closed()
109
+ .then((i) => ({ code: i.closeCode, reason: i.reason }))
110
+ .catch(() => ({ code: 0, reason: 'closed' }));
111
+ }
112
+ async openEmitStream() {
113
+ return toWritable(await this.#s.openUni());
114
+ }
115
+ onEmitStream(cb) {
116
+ void (async () => {
117
+ try {
118
+ for (;;)
119
+ cb(toReadable(await this.#s.acceptUni()));
120
+ }
121
+ catch {
122
+ // Session gone; `closed` reports it.
123
+ }
124
+ })();
125
+ }
126
+ async openBidi() {
127
+ const bi = await this.#s.openBi();
128
+ return { readable: toReadable(bi.takeRecv()), writable: toWritable(bi.takeSend()) };
129
+ }
130
+ onBidi(cb) {
131
+ void (async () => {
132
+ try {
133
+ for (;;) {
134
+ const bi = await this.#s.acceptBi();
135
+ cb({ readable: toReadable(bi.takeRecv()), writable: toWritable(bi.takeSend()) });
136
+ }
137
+ }
138
+ catch {
139
+ // As above.
140
+ }
141
+ })();
142
+ }
143
+ sendDatagram(bytes) {
144
+ try {
145
+ this.#s.sendDatagram(Buffer.from(bytes));
146
+ }
147
+ catch {
148
+ // Whether this throws on oversize is a property of the binding, not something to
149
+ // rely on: our layer checks the size before ever getting here.
150
+ }
151
+ }
152
+ onDatagram(cb) {
153
+ void (async () => {
154
+ try {
155
+ for (;;)
156
+ cb(new Uint8Array(await this.#s.recvDatagram()));
157
+ }
158
+ catch {
159
+ // As above.
160
+ }
161
+ })();
162
+ }
163
+ maxDatagramSize() {
164
+ const r = this.#s.maxDatagramSize();
165
+ return typeof r === 'number' && r > 0 ? r : DATAGRAM_CONSERVATIVE_FLOOR;
166
+ }
167
+ /**
168
+ * This binding speaks HTTP/3 only and exposes no reliability attribute. `undefined` is
169
+ * the correct answer and is safe: D10 refuses only an explicit `reliable-only`, and
170
+ * there is no HTTP/2 mapping here to be negotiated into.
171
+ */
172
+ reliability() {
173
+ return undefined;
174
+ }
175
+ close(code, reason) {
176
+ try {
177
+ this.#s.close(code, reason.slice(0, 1024));
178
+ }
179
+ catch {
180
+ // Already closed.
181
+ }
182
+ }
183
+ }
184
+ export async function listenMoq(opts) {
185
+ const { NapiServer } = native();
186
+ const host = opts.host ?? '127.0.0.1';
187
+ const server = NapiServer.bind(`${host}:${opts.port}`, Buffer.from(opts.cert), Buffer.from(opts.privKey));
188
+ return {
189
+ port: opts.port,
190
+ stop: () => server.close(),
191
+ async *sessions() {
192
+ for (;;) {
193
+ const request = await server.accept();
194
+ if (request === null)
195
+ return;
196
+ yield new MoqConnection(await request.ok());
197
+ }
198
+ },
199
+ };
200
+ }
201
+ export async function connectMoq(opts) {
202
+ const { NapiClient } = native();
203
+ const client = NapiClient.withCertificateHashes([Buffer.from(opts.certificateHash)]);
204
+ try {
205
+ return new MoqConnection(await client.connect(opts.url));
206
+ }
207
+ catch (cause) {
208
+ throw new TransportError('WT_SESSION_CLOSED', `could not open a session to ${opts.url}: ${cause.message}`, 'Check the server is listening, that UDP reaches it, and that the certificate hash matches.');
209
+ }
210
+ }
211
+ //# sourceMappingURL=moq.node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"moq.node.js","sourceRoot":"","sources":["../../src/transport/moq.node.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,2BAA2B,EAAE,MAAM,gBAAgB,CAAA;AA0C5D;;;;;;;;GAQG;AACH,MAAM,iBAAiB,GAAqC;IAC1D,cAAc,EAAE,iCAAiC;IACjD,YAAY,EAAE,+BAA+B;IAC7C,WAAW,EAAE,kCAAkC;IAC/C,aAAa,EAAE,oCAAoC;IACnD,WAAW,EAAE,mCAAmC;CACjD,CAAA;AAED,IAAI,MAA6B,CAAA;AACjC,SAAS,MAAM;IACb,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAA;IACvC,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,IAAI,EAAE,CAAA;IACjD,MAAM,GAAG,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAA;IAClC,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,MAAM,IAAI,cAAc,CACtB,eAAe,EACf,gCAAgC,GAAG,EAAE,EACrC,cAAc,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAC3D,CAAA;IACH,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,CAAc,CAAA;QACzD,OAAO,MAAM,CAAA;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,cAAc,CACtB,eAAe,EACf,kBAAkB,GAAG,KAAM,KAAe,CAAC,OAAO,EAAE,EACpD,2BAA2B,GAAG,EAAE,CACjC,CAAA;IACH,CAAC;AACH,CAAC;AAED,MAAM,KAAK,GAAG,KAAK,CAAA;AAEnB,SAAS,UAAU,CAAC,IAAa;IAC/B,OAAO,IAAI,cAAc,CAAa;QACpC,KAAK,CAAC,IAAI,CAAC,UAAU;YACnB,IAAI,CAAC;gBACH,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;gBACpC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;oBACnB,UAAU,CAAC,KAAK,EAAE,CAAA,CAAC,MAAM;oBACzB,OAAM;gBACR,CAAC;gBACD,UAAU,CAAC,OAAO,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC,CAAA;YAC3C,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;YACrB,CAAC;QACH,CAAC;QACD,MAAM,CAAC,MAAM;YACX,8EAA8E;YAC9E,KAAK,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QACvD,CAAC;KACF,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,IAAa;IAC/B,OAAO,IAAI,cAAc,CAAa;QACpC,KAAK,CAAC,KAAK,CAAC,KAAK;YACf,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;QACtC,CAAC;QACD,KAAK,CAAC,KAAK;YACT,MAAM,IAAI,CAAC,MAAM,EAAE,CAAA,CAAC,kCAAkC;QACxD,CAAC;QACD,KAAK,CAAC,MAAM;YACV,gDAAgD;YAChD,KAAK,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QACxD,CAAC;KACF,CAAC,CAAA;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,MAAM,CAAC,MAAe;IAC7B,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,MAAM,GAAG,IAAI,CAAA;IACpD,MAAM,CAAC,GAAG,gBAAgB,CAAC,IAAI,CAAC,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;IAC1F,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAA;AACvD,CAAC;AAED,MAAM,aAAa;IACR,EAAE,CAAY;IACd,MAAM,CAAoB;IAEnC,YAAY,OAAmB;QAC7B,IAAI,CAAC,EAAE,GAAG,OAAO,CAAA;QACjB,IAAI,CAAC,MAAM,GAAG,OAAO;aAClB,MAAM,EAAE;aACR,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;aACtD,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAA;IACjD,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,OAAO,UAAU,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAA;IAC5C,CAAC;IAED,YAAY,CAAC,EAAkD;QAC7D,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,IAAI,CAAC;gBACH;oBAAS,EAAE,CAAC,UAAU,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC,CAAA;YACpD,CAAC;YAAC,MAAM,CAAC;gBACP,qCAAqC;YACvC,CAAC;QACH,CAAC,CAAC,EAAE,CAAA;IACN,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,CAAA;QACjC,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAA;IACrF,CAAC;IAED,MAAM,CAAC,EAAgC;QACrC,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,IAAI,CAAC;gBACH,SAAS,CAAC;oBACR,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAA;oBACnC,EAAE,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAA;gBAClF,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,YAAY;YACd,CAAC;QACH,CAAC,CAAC,EAAE,CAAA;IACN,CAAC;IAED,YAAY,CAAC,KAAiB;QAC5B,IAAI,CAAC;YACH,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,iFAAiF;YACjF,+DAA+D;QACjE,CAAC;IACH,CAAC;IAED,UAAU,CAAC,EAA+B;QACxC,KAAK,CAAC,KAAK,IAAI,EAAE;YACf,IAAI,CAAC;gBACH;oBAAS,EAAE,CAAC,IAAI,UAAU,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC,YAAY,EAAE,CAAC,CAAC,CAAA;YAC3D,CAAC;YAAC,MAAM,CAAC;gBACP,YAAY;YACd,CAAC;QACH,CAAC,CAAC,EAAE,CAAA;IACN,CAAC;IAED,eAAe;QACb,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,CAAC,eAAe,EAAE,CAAA;QACnC,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,2BAA2B,CAAA;IACzE,CAAC;IAED;;;;OAIG;IACH,WAAW;QACT,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,KAAK,CAAC,IAAY,EAAE,MAAc;QAChC,IAAI,CAAC;YACH,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAA;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,kBAAkB;QACpB,CAAC;IACH,CAAC;CACF;AAeD,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAsB;IACpD,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,EAAE,CAAA;IAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,WAAW,CAAA;IACrC,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAC5B,GAAG,IAAI,IAAI,IAAI,CAAC,IAAI,EAAE,EACtB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EACtB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAC1B,CAAA;IACD,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE;QAC1B,KAAK,CAAC,CAAC,QAAQ;YACb,SAAS,CAAC;gBACR,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,MAAM,EAAE,CAAA;gBACrC,IAAI,OAAO,KAAK,IAAI;oBAAE,OAAM;gBAC5B,MAAM,IAAI,aAAa,CAAC,MAAM,OAAO,CAAC,EAAE,EAAE,CAAC,CAAA;YAC7C,CAAC;QACH,CAAC;KACF,CAAA;AACH,CAAC;AAOD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAsB;IACrD,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,EAAE,CAAA;IAC/B,MAAM,MAAM,GAAG,UAAU,CAAC,qBAAqB,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,CAAA;IACpF,IAAI,CAAC;QACH,OAAO,IAAI,aAAa,CAAC,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;IAC1D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,cAAc,CACtB,mBAAmB,EACnB,+BAA+B,IAAI,CAAC,GAAG,KAAM,KAAe,CAAC,OAAO,EAAE,EACtE,4FAA4F,CAC7F,CAAA;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,40 @@
1
+ declare const randomPort: () => number;
2
+ import type { Connection } from './types.ts';
3
+ interface Listener {
4
+ port: number;
5
+ sessions: () => AsyncIterable<Connection>;
6
+ stop: () => void;
7
+ }
8
+ export interface UnderTest {
9
+ /**
10
+ * Whether a peer's stream reset reaches the responder's `ctx.signal`.
11
+ *
12
+ * `false` is a real capability gap, not a preference. moq surfaces STOP_SENDING only on
13
+ * the next write, and a long-running handler never makes one, so the handler is not
14
+ * told to stop. The caller still rejects either way — the work just keeps running.
15
+ */
16
+ readonly propagatesAbortToHandler: boolean;
17
+ readonly name: string;
18
+ readonly port: number;
19
+ listen: (o: {
20
+ port: number;
21
+ host: string;
22
+ cert: string;
23
+ privKey: string;
24
+ }) => Promise<Listener>;
25
+ connect: (o: {
26
+ url: string;
27
+ certificateHash: Uint8Array;
28
+ }) => Promise<Connection>;
29
+ }
30
+ /**
31
+ * One transport per process, deliberately.
32
+ *
33
+ * Running both bindings' servers concurrently in a single process hangs — verified: each
34
+ * works alone and both can bind, but sessions on both at once deadlock. That is a
35
+ * property of running two native QUIC stacks side by side, not of transport-io, and no
36
+ * deployment would do it. Splitting by process is also better isolation.
37
+ */
38
+ export declare function runParity(t: UnderTest): Promise<void>;
39
+ export { randomPort };
40
+ //# sourceMappingURL=parity-suite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parity-suite.d.ts","sourceRoot":"","sources":["../../src/transport/parity-suite.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,UAAU,QAAO,MAAmD,CAAA;AAqB1E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAU5C,UAAU,QAAQ;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,aAAa,CAAC,UAAU,CAAC,CAAA;IACzC,IAAI,EAAE,MAAM,IAAI,CAAA;CACjB;AACD,MAAM,WAAW,SAAS;IACxB;;;;;;OAMG;IACH,QAAQ,CAAC,wBAAwB,EAAE,OAAO,CAAA;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,CAAC,CAAC,EAAE;QACV,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;QACZ,IAAI,EAAE,MAAM,CAAA;QACZ,OAAO,EAAE,MAAM,CAAA;KAChB,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IACvB,OAAO,EAAE,CAAC,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,UAAU,CAAA;KAAE,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;CAClF;AAuCD;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAqF3D;AAED,OAAO,EAAE,UAAU,EAAE,CAAA"}
@@ -0,0 +1,133 @@
1
+ const randomPort = () => 40000 + Math.floor(Math.random() * 20000);
2
+ /**
3
+ * The parity suite body, shared by one test file per transport.
4
+ *
5
+ * A byte count established that `@moq/web-transport` does not leak (D66). It established
6
+ * nothing else. This establishes the rest: half-close for `call()`, reset for
7
+ * `AbortSignal`, `maxDatagramSize`, oversized-datagram behaviour, and both lanes end to
8
+ * end. The reference binding got each of those wrong in specific ways; this is how we
9
+ * find out which ways the alternative gets them wrong.
10
+ */
11
+ import assert from 'node:assert/strict';
12
+ import { execFileSync } from 'node:child_process';
13
+ import { createHash, X509Certificate } from 'node:crypto';
14
+ import { mkdtempSync, readFileSync, rmSync } from 'node:fs';
15
+ import { tmpdir } from 'node:os';
16
+ import { join } from 'node:path';
17
+ import { Client } from '../client.js';
18
+ import { defineContract, type$ } from '../contract.js';
19
+ const contract = defineContract({
20
+ chat: { lane: 'stream', payload: type$() },
21
+ cursor: { lane: 'datagram', payload: type$() },
22
+ echo: { lane: 'stream', payload: type$(), returns: type$() },
23
+ slow: { lane: 'stream', payload: type$(), returns: type$() },
24
+ });
25
+ // Random high ports. A fixed port makes this suite fail for a reason that has nothing to
26
+ // do with the code — an orphan from a previous killed run still holding the socket, which
27
+ // cost an hour to diagnose once already.
28
+ const dir = mkdtempSync(join(tmpdir(), 'parity-'));
29
+ execFileSync('openssl', [
30
+ 'ecparam',
31
+ '-name',
32
+ 'prime256v1',
33
+ '-genkey',
34
+ '-noout',
35
+ '-out',
36
+ join(dir, 'k.pem'),
37
+ ]);
38
+ execFileSync('openssl', [
39
+ 'req',
40
+ '-new',
41
+ '-x509',
42
+ '-key',
43
+ join(dir, 'k.pem'),
44
+ '-out',
45
+ join(dir, 'c.pem'),
46
+ '-days',
47
+ '14',
48
+ '-subj',
49
+ '/CN=localhost',
50
+ '-addext',
51
+ 'subjectAltName=DNS:localhost,IP:127.0.0.1',
52
+ ]);
53
+ const cert = readFileSync(join(dir, 'c.pem'), 'utf8');
54
+ const privKey = readFileSync(join(dir, 'k.pem'), 'utf8');
55
+ const certificateHash = createHash('sha256').update(new X509Certificate(cert).raw).digest();
56
+ const settle = async (ms = 400) => {
57
+ await new Promise((r) => setTimeout(r, ms));
58
+ };
59
+ /**
60
+ * One transport per process, deliberately.
61
+ *
62
+ * Running both bindings' servers concurrently in a single process hangs — verified: each
63
+ * works alone and both can bind, but sessions on both at once deadlock. That is a
64
+ * property of running two native QUIC stacks side by side, not of transport-io, and no
65
+ * deployment would do it. Splitting by process is also better isolation.
66
+ */
67
+ export async function runParity(t) {
68
+ const { createServer } = await import('../server.js');
69
+ const server = createServer({ contract });
70
+ await server.listen();
71
+ server.handle('echo', async ({ n }) => ({ n: n * 2 }));
72
+ let handlerSawAbort = false;
73
+ server.handle('slow', async (_p, ctx) => {
74
+ await new Promise((resolve) => {
75
+ ctx.signal.addEventListener('abort', () => {
76
+ handlerSawAbort = true;
77
+ resolve();
78
+ }, { once: true });
79
+ // Bounded, so a transport that never delivers the reset fails an assertion instead
80
+ // of hanging the suite. That distinction cost an afternoon to find.
81
+ setTimeout(resolve, 3000);
82
+ });
83
+ return null;
84
+ });
85
+ server.onSession((peer) => {
86
+ void peer.join('lobby');
87
+ peer.on('chat', (p) => void server.to('lobby').emit('chat', p));
88
+ peer.on('cursor', (p) => void server.to('lobby').emit('cursor', p));
89
+ });
90
+ const listener = await t.listen({ port: t.port, host: '127.0.0.1', cert, privKey });
91
+ void (async () => {
92
+ for await (const conn of listener.sessions())
93
+ void server.accept(conn).catch(() => undefined);
94
+ })().catch(() => undefined);
95
+ const url = `https://127.0.0.1:${t.port}/`;
96
+ const client = new Client({
97
+ contract,
98
+ origin: 0xf0000001,
99
+ connect: () => t.connect({ url, certificateHash }),
100
+ });
101
+ await client.connect();
102
+ assert.equal(client.getSnapshot().status, 'connected', `${t.name}: connected`);
103
+ const chat = [];
104
+ const cursor = [];
105
+ client.on('chat', (p) => chat.push(p.body));
106
+ client.on('cursor', (p) => cursor.push(p.n));
107
+ await settle(600);
108
+ client.emit('chat', { body: 'reliable' });
109
+ await settle();
110
+ assert.deepEqual(chat, ['reliable'], `${t.name}: stream lane`);
111
+ client.emit('cursor', { n: 7 });
112
+ await settle();
113
+ assert.deepEqual(cursor, [7], `${t.name}: datagram lane`);
114
+ // Half-close for the request, response read to stream close.
115
+ assert.deepEqual(await client.call('echo', { n: 21 }), { n: 42 }, `${t.name}: call`);
116
+ // AbortSignal maps to a stream reset. The caller always rejects; whether the reset
117
+ // reaches the responder is a property of the transport, asserted either way so a
118
+ // regression in the supported direction is caught.
119
+ const ac = new AbortController();
120
+ const pending = client.call('slow', null, { signal: ac.signal });
121
+ await settle(150);
122
+ ac.abort();
123
+ await assert.rejects(pending, `${t.name}: abort rejects the caller`);
124
+ await settle(900);
125
+ assert.equal(handlerSawAbort, t.propagatesAbortToHandler, `${t.name}: expected ctx.signal to ${t.propagatesAbortToHandler ? '' : 'NOT '}fire`);
126
+ // Our layer refuses oversize before the transport can silently discard it.
127
+ assert.throws(() => client.emit('cursor', { n: 1, pad: 'x'.repeat(4000) }), (e) => e.code === 'WT_DATAGRAM_TOO_LARGE', `${t.name}: oversized datagram refused locally`);
128
+ client.disconnect();
129
+ listener.stop();
130
+ rmSync(dir, { recursive: true, force: true });
131
+ }
132
+ export { randomPort };
133
+ //# sourceMappingURL=parity-suite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parity-suite.js","sourceRoot":"","sources":["../../src/transport/parity-suite.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The transport seam. Internal — not exported from the package (D21).
3
+ *
4
+ * It exists so the reference transport's defects stay in one place: it silently swallows
5
+ * oversized and blocked datagrams, its error type omits the specification's
6
+ * `streamErrorCode` so reset codes are only recoverable by parsing a message string, and
7
+ * it ships a reliability fallback that must be actively disabled.
8
+ */
9
+ export interface CloseInfo {
10
+ readonly code: number;
11
+ readonly reason: string;
12
+ }
13
+ export interface BidiStream {
14
+ readonly readable: ReadableStream<Uint8Array>;
15
+ readonly writable: WritableStream<Uint8Array>;
16
+ }
17
+ export interface Connection {
18
+ /** One long-lived unidirectional stream per direction carries the whole emit lane. */
19
+ openEmitStream(): Promise<WritableStream<Uint8Array>>;
20
+ onEmitStream(cb: (readable: ReadableStream<Uint8Array>) => void): void;
21
+ openBidi(): Promise<BidiStream>;
22
+ onBidi(cb: (stream: BidiStream) => void): void;
23
+ sendDatagram(bytes: Uint8Array): void;
24
+ onDatagram(cb: (bytes: Uint8Array) => void): void;
25
+ /** A property of the path, not a constant. Queried at send time, never cached. */
26
+ maxDatagramSize(): number;
27
+ /**
28
+ * `undefined` where the runtime does not implement the attribute — which includes the
29
+ * dominant browser. Treated as "unknown, allowed"; only an explicit 'reliable-only' is
30
+ * refused. See D10.
31
+ */
32
+ reliability(): 'pending' | 'reliable-only' | 'supports-unreliable' | undefined;
33
+ close(code: number, reason: string): void;
34
+ readonly closed: Promise<CloseInfo>;
35
+ }
36
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/transport/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,CAAA;IAC7C,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,CAAA;CAC9C;AAED,MAAM,WAAW,UAAU;IACzB,sFAAsF;IACtF,cAAc,IAAI,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAA;IACrD,YAAY,CAAC,EAAE,EAAE,CAAC,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,KAAK,IAAI,GAAG,IAAI,CAAA;IAEtE,QAAQ,IAAI,OAAO,CAAC,UAAU,CAAC,CAAA;IAC/B,MAAM,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAAA;IAE9C,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IACrC,UAAU,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAAA;IACjD,kFAAkF;IAClF,eAAe,IAAI,MAAM,CAAA;IAEzB;;;;OAIG;IACH,WAAW,IAAI,SAAS,GAAG,eAAe,GAAG,qBAAqB,GAAG,SAAS,CAAA;IAE9E,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACzC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,CAAA;CACpC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/transport/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Wraps a Connection and makes its datagram path behave like a real one: lossy,
3
+ * reordering, duplicating and delayed, on command.
4
+ *
5
+ * Test-only. The plain loopback delivers datagrams reliably and in order, which is
6
+ * convenient and a lie — every guarantee the datagram lane deliberately does not make
7
+ * goes unexercised against it. This exists so loss, reordering and duplication are forced
8
+ * deliberately rather than waited for.
9
+ *
10
+ * Streams are passed through untouched: they are reliable and ordered, and pretending
11
+ * otherwise would test the wrong thing.
12
+ */
13
+ import type { BidiStream, CloseInfo, Connection } from './types.ts';
14
+ export interface UnreliableOptions {
15
+ /** Drop the nth datagram, 1-based, for every n in this set. */
16
+ readonly dropAt?: ReadonlySet<number>;
17
+ /** Deliver the nth datagram twice. */
18
+ readonly duplicateAt?: ReadonlySet<number>;
19
+ /** Hold the nth datagram and release it after the following one. */
20
+ readonly delayAt?: ReadonlySet<number>;
21
+ }
22
+ export declare class UnreliableConnection implements Connection {
23
+ #private;
24
+ constructor(inner: Connection, opts?: UnreliableOptions);
25
+ get sentCount(): number;
26
+ sendDatagram(bytes: Uint8Array): void;
27
+ openEmitStream(): Promise<WritableStream<Uint8Array>>;
28
+ onEmitStream(cb: (readable: ReadableStream<Uint8Array>) => void): void;
29
+ openBidi(): Promise<BidiStream>;
30
+ onBidi(cb: (stream: BidiStream) => void): void;
31
+ onDatagram(cb: (bytes: Uint8Array) => void): void;
32
+ maxDatagramSize(): number;
33
+ reliability(): 'pending' | 'reliable-only' | 'supports-unreliable' | undefined;
34
+ close(code: number, reason: string): void;
35
+ get closed(): Promise<CloseInfo>;
36
+ }
37
+ //# sourceMappingURL=unreliable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unreliable.d.ts","sourceRoot":"","sources":["../../src/transport/unreliable.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEnE,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACrC,sCAAsC;IACtC,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IAC1C,oEAAoE;IACpE,QAAQ,CAAC,OAAO,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CACvC;AAED,qBAAa,oBAAqB,YAAW,UAAU;;IAMrD,YAAY,KAAK,EAAE,UAAU,EAAE,IAAI,GAAE,iBAAsB,EAG1D;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAmBpC;IAED,cAAc,IAAI,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAEpD;IACD,YAAY,CAAC,EAAE,EAAE,CAAC,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,KAAK,IAAI,GAAG,IAAI,CAErE;IACD,QAAQ,IAAI,OAAO,CAAC,UAAU,CAAC,CAE9B;IACD,MAAM,CAAC,EAAE,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAE7C;IACD,UAAU,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAEhD;IACD,eAAe,IAAI,MAAM,CAExB;IACD,WAAW,IAAI,SAAS,GAAG,eAAe,GAAG,qBAAqB,GAAG,SAAS,CAE7E;IACD,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAExC;IACD,IAAI,MAAM,IAAI,OAAO,CAAC,SAAS,CAAC,CAE/B;CACF"}