@dxos/network-manager 0.10.0 → 0.11.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 (56) hide show
  1. package/dist/lib/chunk-wire-protocol.mjs +3554 -0
  2. package/dist/lib/chunk-wire-protocol.mjs.map +1 -0
  3. package/dist/lib/index.mjs +294 -0
  4. package/dist/lib/index.mjs.map +1 -0
  5. package/dist/lib/testing.mjs +237 -0
  6. package/dist/lib/testing.mjs.map +1 -0
  7. package/dist/lib/transport/tcp/tcp-transport.browser.mjs +34 -0
  8. package/dist/lib/transport/tcp/tcp-transport.browser.mjs.map +1 -0
  9. package/dist/lib/transport/tcp/tcp-transport.mjs +140 -0
  10. package/dist/lib/transport/tcp/tcp-transport.mjs.map +1 -0
  11. package/dist/lib/transport/tcp.mjs +1 -0
  12. package/dist/types/src/network-manager.d.ts +9 -0
  13. package/dist/types/src/network-manager.d.ts.map +1 -1
  14. package/dist/types/src/signal/swarm-messenger.d.ts +2 -7
  15. package/dist/types/src/signal/swarm-messenger.d.ts.map +1 -1
  16. package/dist/types/src/testing/test-builder.d.ts +2 -6
  17. package/dist/types/src/testing/test-builder.d.ts.map +1 -1
  18. package/dist/types/src/transport/webrtc/rtc-peer-connection.d.ts.map +1 -1
  19. package/dist/types/tsconfig.tsbuildinfo +1 -1
  20. package/package.json +28 -31
  21. package/src/network-manager.ts +17 -4
  22. package/src/signal/integration.node.test.ts +7 -11
  23. package/src/signal/swarm-messenger.node.test.ts +7 -29
  24. package/src/signal/swarm-messenger.ts +3 -13
  25. package/src/swarm/swarm.test.ts +2 -2
  26. package/src/testing/test-builder.ts +48 -75
  27. package/src/tests/webrtc-transport.test.ts +4 -15
  28. package/src/transport/webrtc/rtc-peer-connection.ts +0 -3
  29. package/src/transport/webrtc/rtc-transport-proxy.test.ts +2 -1
  30. package/src/transport/webrtc/rtc-transport.test.ts +2 -1
  31. package/dist/lib/browser/chunk-ERB3AQAF.mjs +0 -3095
  32. package/dist/lib/browser/chunk-ERB3AQAF.mjs.map +0 -7
  33. package/dist/lib/browser/index.mjs +0 -244
  34. package/dist/lib/browser/index.mjs.map +0 -7
  35. package/dist/lib/browser/meta.json +0 -1
  36. package/dist/lib/browser/testing/index.mjs +0 -280
  37. package/dist/lib/browser/testing/index.mjs.map +0 -7
  38. package/dist/lib/browser/transport/tcp/index.mjs +0 -5
  39. package/dist/lib/browser/transport/tcp/index.mjs.map +0 -7
  40. package/dist/lib/browser/transport/tcp/tcp-transport.browser.mjs +0 -36
  41. package/dist/lib/browser/transport/tcp/tcp-transport.browser.mjs.map +0 -7
  42. package/dist/lib/browser/transport/tcp/tcp-transport.mjs +0 -125
  43. package/dist/lib/browser/transport/tcp/tcp-transport.mjs.map +0 -7
  44. package/dist/lib/node-esm/chunk-KYLPLEZQ.mjs +0 -3095
  45. package/dist/lib/node-esm/chunk-KYLPLEZQ.mjs.map +0 -7
  46. package/dist/lib/node-esm/index.mjs +0 -244
  47. package/dist/lib/node-esm/index.mjs.map +0 -7
  48. package/dist/lib/node-esm/meta.json +0 -1
  49. package/dist/lib/node-esm/testing/index.mjs +0 -280
  50. package/dist/lib/node-esm/testing/index.mjs.map +0 -7
  51. package/dist/lib/node-esm/transport/tcp/index.mjs +0 -5
  52. package/dist/lib/node-esm/transport/tcp/index.mjs.map +0 -7
  53. package/dist/lib/node-esm/transport/tcp/tcp-transport.browser.mjs +0 -36
  54. package/dist/lib/node-esm/transport/tcp/tcp-transport.browser.mjs.map +0 -7
  55. package/dist/lib/node-esm/transport/tcp/tcp-transport.mjs +0 -125
  56. package/dist/lib/node-esm/transport/tcp/tcp-transport.mjs.map +0 -7
@@ -0,0 +1,294 @@
1
+ import { _ as Swarm, a as createRtcTransportFactory, b as ConnectionState, c as MemoryTransportFactory, d as SwarmNetworkManagerService, f as ConnectionLog, g as SwarmMapper, h as MAX_CONCURRENT_INITIATING_CONNECTIONS, i as RtcTransportProxyFactory, l as FullyConnectedTopology, m as ConnectionLimiter, n as RtcTransportService, o as TransportKind, p as EventType, r as RtcTransportProxy, s as MemoryTransport, t as createTeleportProtocolFactory, u as SwarmNetworkManager, v as SwarmMessenger, y as Connection } from "./chunk-wire-protocol.mjs";
2
+ import { asyncTimeout } from "@dxos/async";
3
+ import { isNonNullable } from "@dxos/util";
4
+ import { invariant } from "@dxos/invariant";
5
+ import { log } from "@dxos/log";
6
+ //#region src/signal/ice.ts
7
+ var __dxlog_file$2 = "/__w/dxos/dxos/packages/core/mesh/network-manager/src/signal/ice.ts";
8
+ var createIceProvider = (iceProviders) => {
9
+ let cachedIceServers;
10
+ return { getIceServers: async () => {
11
+ if (cachedIceServers) return cachedIceServers;
12
+ cachedIceServers = (await Promise.all(iceProviders.map(({ urls }) => asyncTimeout(fetch(urls, { method: "GET" }), 1e4).then((response) => response.json()).catch((err) => {
13
+ if (!(typeof window !== "undefined" && window.location.href.includes("localhost"))) log.error("Failed to fetch ICE servers from provider", {
14
+ urls,
15
+ err
16
+ }, {
17
+ "~LogMeta": "~LogMeta",
18
+ F: __dxlog_file$2,
19
+ L: 30,
20
+ S: void 0
21
+ });
22
+ })))).filter(isNonNullable).map(({ iceServers }) => iceServers).flat();
23
+ return cachedIceServers;
24
+ } };
25
+ };
26
+ //#endregion
27
+ //#region src/topology/mmst-topology.ts
28
+ var __dxlog_file$1 = "/__w/dxos/dxos/packages/core/mesh/network-manager/src/topology/mmst-topology.ts";
29
+ var MIN_UPDATE_INTERVAL = 1e3 * 10;
30
+ var MAX_CHANGES_PER_UPDATE = 1;
31
+ var MMSTTopology = class {
32
+ _originateConnections;
33
+ _maxPeers;
34
+ _sampleSize;
35
+ _controller;
36
+ _sampleCollected = false;
37
+ _lastAction = /* @__PURE__ */ new Date(0);
38
+ constructor({ originateConnections = 2, maxPeers = 4, sampleSize = 10 } = {}) {
39
+ this._originateConnections = originateConnections;
40
+ this._maxPeers = maxPeers;
41
+ this._sampleSize = sampleSize;
42
+ }
43
+ init(controller) {
44
+ invariant(!this._controller, "Already initialized", {
45
+ "~LogMeta": "~LogMeta",
46
+ F: __dxlog_file$1,
47
+ L: 49,
48
+ S: this,
49
+ A: ["!this._controller", "'Already initialized'"]
50
+ });
51
+ this._controller = controller;
52
+ }
53
+ update() {
54
+ invariant(this._controller, "Not initialized", {
55
+ "~LogMeta": "~LogMeta",
56
+ F: __dxlog_file$1,
57
+ L: 54,
58
+ S: this,
59
+ A: ["this._controller", "'Not initialized'"]
60
+ });
61
+ const { connected, candidates } = this._controller.getState();
62
+ if (this._sampleCollected || connected.length > this._maxPeers || candidates.length > 0) {
63
+ log("Running the algorithm.", void 0, {
64
+ "~LogMeta": "~LogMeta",
65
+ F: __dxlog_file$1,
66
+ L: 58,
67
+ S: this
68
+ });
69
+ this._sampleCollected = true;
70
+ this._runAlgorithm();
71
+ }
72
+ }
73
+ forceUpdate() {
74
+ this._lastAction = /* @__PURE__ */ new Date(0);
75
+ this.update();
76
+ }
77
+ async onOffer(peer) {
78
+ invariant(this._controller, "Not initialized", {
79
+ "~LogMeta": "~LogMeta",
80
+ F: __dxlog_file$1,
81
+ L: 70,
82
+ S: this,
83
+ A: ["this._controller", "'Not initialized'"]
84
+ });
85
+ const { connected } = this._controller.getState();
86
+ const accept = connected.length < this._maxPeers;
87
+ log(`Offer ${peer} accept=${accept}`, void 0, {
88
+ "~LogMeta": "~LogMeta",
89
+ F: __dxlog_file$1,
90
+ L: 73,
91
+ S: this
92
+ });
93
+ return accept;
94
+ }
95
+ async destroy() {}
96
+ _runAlgorithm() {
97
+ invariant(this._controller, "Not initialized", {
98
+ "~LogMeta": "~LogMeta",
99
+ F: __dxlog_file$1,
100
+ L: 82,
101
+ S: this,
102
+ A: ["this._controller", "'Not initialized'"]
103
+ });
104
+ const { connected, candidates, ownPeerId } = this._controller.getState();
105
+ if (connected.length > this._maxPeers) {
106
+ log(`disconnect ${connected.length - this._maxPeers} peers.`, void 0, {
107
+ "~LogMeta": "~LogMeta",
108
+ F: __dxlog_file$1,
109
+ L: 88,
110
+ S: this
111
+ });
112
+ const sorted = sortByXorDistance(connected, ownPeerId).reverse().slice(0, this._maxPeers - connected.length);
113
+ invariant(sorted.length === 0, void 0, {
114
+ "~LogMeta": "~LogMeta",
115
+ F: __dxlog_file$1,
116
+ L: 92,
117
+ S: this,
118
+ A: ["sorted.length === 0", ""]
119
+ });
120
+ if (sorted.length > MAX_CHANGES_PER_UPDATE) log(`want to disconnect ${sorted.length} peers but limited to ${MAX_CHANGES_PER_UPDATE}`, void 0, {
121
+ "~LogMeta": "~LogMeta",
122
+ F: __dxlog_file$1,
123
+ L: 95,
124
+ S: this
125
+ });
126
+ if (Date.now() - this._lastAction.getTime() > MIN_UPDATE_INTERVAL) {
127
+ for (const peer of sorted.slice(0, MAX_CHANGES_PER_UPDATE)) {
128
+ log(`Disconnect ${peer}.`, void 0, {
129
+ "~LogMeta": "~LogMeta",
130
+ F: __dxlog_file$1,
131
+ L: 100,
132
+ S: this
133
+ });
134
+ this._controller.disconnect(peer);
135
+ }
136
+ this._lastAction = /* @__PURE__ */ new Date();
137
+ } else log("rate limited disconnect", void 0, {
138
+ "~LogMeta": "~LogMeta",
139
+ F: __dxlog_file$1,
140
+ L: 105,
141
+ S: this
142
+ });
143
+ } else if (connected.length < this._originateConnections) {
144
+ log(`connect ${this._originateConnections - connected.length} peers.`, void 0, {
145
+ "~LogMeta": "~LogMeta",
146
+ F: __dxlog_file$1,
147
+ L: 109,
148
+ S: this
149
+ });
150
+ const sorted = sortByXorDistance(candidates.sort(() => Math.random() - .5).slice(0, this._sampleSize), ownPeerId).slice(0, this._originateConnections - connected.length);
151
+ if (sorted.length > MAX_CHANGES_PER_UPDATE) log(`want to connect ${sorted.length} peers but limited to ${MAX_CHANGES_PER_UPDATE}`, void 0, {
152
+ "~LogMeta": "~LogMeta",
153
+ F: __dxlog_file$1,
154
+ L: 114,
155
+ S: this
156
+ });
157
+ if (Date.now() - this._lastAction.getTime() > MIN_UPDATE_INTERVAL) {
158
+ for (const peer of sorted.slice(0, MAX_CHANGES_PER_UPDATE)) {
159
+ log(`Connect ${peer}.`, void 0, {
160
+ "~LogMeta": "~LogMeta",
161
+ F: __dxlog_file$1,
162
+ L: 118,
163
+ S: this
164
+ });
165
+ this._controller.connect(peer);
166
+ }
167
+ this._lastAction = /* @__PURE__ */ new Date();
168
+ } else log("rate limited connect", void 0, {
169
+ "~LogMeta": "~LogMeta",
170
+ F: __dxlog_file$1,
171
+ L: 123,
172
+ S: this
173
+ });
174
+ }
175
+ }
176
+ toString() {
177
+ return "MMSTTopology";
178
+ }
179
+ };
180
+ var sortByXorDistance = (keys, reference) => {
181
+ const sorted = keys.sort((a, b) => {
182
+ return compareXor(distXor(a.asBuffer(), reference.asBuffer()), distXor(b.asBuffer(), reference.asBuffer()));
183
+ });
184
+ log("Sorted keys", {
185
+ keys,
186
+ reference,
187
+ sorted
188
+ }, {
189
+ "~LogMeta": "~LogMeta",
190
+ F: __dxlog_file$1,
191
+ L: 137,
192
+ S: void 0
193
+ });
194
+ return sorted;
195
+ };
196
+ var distXor = (a, b) => {
197
+ const maxLength = Math.max(a.length, b.length);
198
+ const result = Buffer.allocUnsafe(maxLength);
199
+ for (let i = 0; i < maxLength; i++) result[i] = (a[i] || 0) ^ (b[i] || 0);
200
+ return result;
201
+ };
202
+ var compareXor = (a, b) => {
203
+ const maxLength = Math.max(a.length, b.length);
204
+ for (let i = 0; i < maxLength; i++) {
205
+ if ((a[i] || 0) === (b[i] || 0)) continue;
206
+ return (a[i] || 0) < (b[i] || 0) ? -1 : 1;
207
+ }
208
+ return 0;
209
+ };
210
+ //#endregion
211
+ //#region src/topology/star-topology.ts
212
+ var __dxlog_file = "/__w/dxos/dxos/packages/core/mesh/network-manager/src/topology/star-topology.ts";
213
+ var StarTopology = class {
214
+ _centralPeer;
215
+ _controller;
216
+ constructor(_centralPeer) {
217
+ this._centralPeer = _centralPeer;
218
+ }
219
+ toString() {
220
+ return `StarTopology(${this._centralPeer.truncate()})`;
221
+ }
222
+ init(controller) {
223
+ invariant(!this._controller, "Already initialized.", {
224
+ "~LogMeta": "~LogMeta",
225
+ F: __dxlog_file,
226
+ L: 21,
227
+ S: this,
228
+ A: ["!this._controller", "'Already initialized.'"]
229
+ });
230
+ this._controller = controller;
231
+ }
232
+ update() {
233
+ invariant(this._controller, "Not initialized.", {
234
+ "~LogMeta": "~LogMeta",
235
+ F: __dxlog_file,
236
+ L: 26,
237
+ S: this,
238
+ A: ["this._controller", "'Not initialized.'"]
239
+ });
240
+ const { candidates, connected, ownPeerId } = this._controller.getState();
241
+ if (!ownPeerId.equals(this._centralPeer)) {
242
+ log("leaf peer dropping all connections apart from central peer.", void 0, {
243
+ "~LogMeta": "~LogMeta",
244
+ F: __dxlog_file,
245
+ L: 29,
246
+ S: this
247
+ });
248
+ for (const peer of connected) if (!peer.equals(this._centralPeer)) {
249
+ log("dropping connection", { peer }, {
250
+ "~LogMeta": "~LogMeta",
251
+ F: __dxlog_file,
252
+ L: 34,
253
+ S: this
254
+ });
255
+ this._controller.disconnect(peer);
256
+ }
257
+ }
258
+ for (const peer of candidates) if (peer.equals(this._centralPeer) || ownPeerId.equals(this._centralPeer)) {
259
+ log("connecting to peer", { peer }, {
260
+ "~LogMeta": "~LogMeta",
261
+ F: __dxlog_file,
262
+ L: 43,
263
+ S: this
264
+ });
265
+ this._controller.connect(peer);
266
+ }
267
+ }
268
+ async onOffer(peer) {
269
+ invariant(this._controller, "Not initialized.", {
270
+ "~LogMeta": "~LogMeta",
271
+ F: __dxlog_file,
272
+ L: 50,
273
+ S: this,
274
+ A: ["this._controller", "'Not initialized.'"]
275
+ });
276
+ const { ownPeerId } = this._controller.getState();
277
+ log("offer", {
278
+ peer,
279
+ isCentral: peer.equals(this._centralPeer),
280
+ isSelfCentral: ownPeerId.equals(this._centralPeer)
281
+ }, {
282
+ "~LogMeta": "~LogMeta",
283
+ F: __dxlog_file,
284
+ L: 52,
285
+ S: this
286
+ });
287
+ return ownPeerId.equals(this._centralPeer) || peer.equals(this._centralPeer);
288
+ }
289
+ async destroy() {}
290
+ };
291
+ //#endregion
292
+ export { Connection, ConnectionLimiter, ConnectionLog, ConnectionState, EventType, FullyConnectedTopology, MAX_CONCURRENT_INITIATING_CONNECTIONS, MMSTTopology, MemoryTransport, MemoryTransportFactory, RtcTransportProxy, RtcTransportProxyFactory, RtcTransportService, StarTopology, Swarm, SwarmMapper, SwarmMessenger, SwarmNetworkManager, SwarmNetworkManagerService, TransportKind, createIceProvider, createRtcTransportFactory, createTeleportProtocolFactory };
293
+
294
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/signal/ice.ts","../../src/topology/mmst-topology.ts","../../src/topology/star-topology.ts"],"sourcesContent":["//\n// Copyright 2024 DXOS.org\n//\n\nimport { asyncTimeout } from '@dxos/async';\nimport { log } from '@dxos/log';\nimport { type Runtime } from '@dxos/protocols/proto/dxos/config';\nimport { isNonNullable } from '@dxos/util';\n\nexport interface IceProvider {\n getIceServers: () => Promise<RTCIceServer[]>;\n}\n\nexport const createIceProvider = (iceProviders: Runtime.Services.IceProvider[]): IceProvider => {\n let cachedIceServers: RTCIceServer[];\n return {\n getIceServers: async () => {\n if (cachedIceServers) {\n return cachedIceServers;\n }\n\n cachedIceServers = (\n await Promise.all(\n iceProviders.map(({ urls }) =>\n asyncTimeout(fetch(urls, { method: 'GET' }), 10_000)\n .then((response) => response.json())\n .catch((err) => {\n const isDev = typeof window !== 'undefined' && window.location.href.includes('localhost');\n if (!isDev) {\n log.error('Failed to fetch ICE servers from provider', { urls, err });\n }\n }),\n ),\n )\n )\n .filter(isNonNullable)\n .map(({ iceServers }: { iceServers: RTCIceServer[] }) => iceServers)\n .flat();\n\n return cachedIceServers;\n },\n };\n};\n","//\n// Copyright 2020 DXOS.org\n//\n\nimport { invariant } from '@dxos/invariant';\nimport { type PublicKey } from '@dxos/keys';\nimport { log } from '@dxos/log';\n\nimport { type SwarmController, type Topology } from './topology';\n\nconst MIN_UPDATE_INTERVAL = 1000 * 10;\nconst MAX_CHANGES_PER_UPDATE = 1;\n\nexport interface MMSTTopologyOptions {\n /**\n * Number of connections the peer will originate by itself.\n */\n originateConnections?: number;\n\n /**\n * Maximum number of connections allowed, all other connections will be dropped.\n */\n maxPeers?: number;\n\n /**\n * Size of random sample from which peer candidates are selected.\n */\n sampleSize?: number;\n}\n\nexport class MMSTTopology implements Topology {\n private readonly _originateConnections: number;\n private readonly _maxPeers: number;\n private readonly _sampleSize: number;\n\n private _controller?: SwarmController;\n\n private _sampleCollected = false;\n\n private _lastAction = new Date(0);\n\n constructor({ originateConnections = 2, maxPeers = 4, sampleSize = 10 }: MMSTTopologyOptions = {}) {\n this._originateConnections = originateConnections;\n this._maxPeers = maxPeers;\n this._sampleSize = sampleSize;\n }\n\n init(controller: SwarmController): void {\n invariant(!this._controller, 'Already initialized');\n this._controller = controller;\n }\n\n update(): void {\n invariant(this._controller, 'Not initialized');\n const { connected, candidates } = this._controller.getState();\n // Run the algorithms if we have first candidates, ran it before, or have more connections than needed.\n if (this._sampleCollected || connected.length > this._maxPeers || candidates.length > 0) {\n log('Running the algorithm.');\n this._sampleCollected = true;\n this._runAlgorithm();\n }\n }\n\n forceUpdate(): void {\n this._lastAction = new Date(0);\n this.update();\n }\n\n async onOffer(peer: PublicKey): Promise<boolean> {\n invariant(this._controller, 'Not initialized');\n const { connected } = this._controller.getState();\n const accept = connected.length < this._maxPeers;\n log(`Offer ${peer} accept=${accept}`);\n return accept;\n }\n\n async destroy(): Promise<void> {\n // Nothing to do.\n }\n\n private _runAlgorithm(): void {\n invariant(this._controller, 'Not initialized');\n const { connected, candidates, ownPeerId } = this._controller.getState();\n\n // TODO(nf): does this rate limiting/flap dampening logic belong here or in the SwarmController?\n if (connected.length > this._maxPeers) {\n // Disconnect extra peers.\n log(`disconnect ${connected.length - this._maxPeers} peers.`);\n const sorted = sortByXorDistance(connected, ownPeerId)\n .reverse()\n .slice(0, this._maxPeers - connected.length);\n invariant(sorted.length === 0);\n\n if (sorted.length > MAX_CHANGES_PER_UPDATE) {\n log(`want to disconnect ${sorted.length} peers but limited to ${MAX_CHANGES_PER_UPDATE}`);\n }\n\n if (Date.now() - this._lastAction.getTime() > MIN_UPDATE_INTERVAL) {\n for (const peer of sorted.slice(0, MAX_CHANGES_PER_UPDATE)) {\n log(`Disconnect ${peer}.`);\n this._controller.disconnect(peer);\n }\n this._lastAction = new Date();\n } else {\n log('rate limited disconnect');\n }\n } else if (connected.length < this._originateConnections) {\n // Connect new peers to reach desired quota.\n log(`connect ${this._originateConnections - connected.length} peers.`);\n const sample = candidates.sort(() => Math.random() - 0.5).slice(0, this._sampleSize);\n const sorted = sortByXorDistance(sample, ownPeerId).slice(0, this._originateConnections - connected.length);\n\n if (sorted.length > MAX_CHANGES_PER_UPDATE) {\n log(`want to connect ${sorted.length} peers but limited to ${MAX_CHANGES_PER_UPDATE}`);\n }\n if (Date.now() - this._lastAction.getTime() > MIN_UPDATE_INTERVAL) {\n for (const peer of sorted.slice(0, MAX_CHANGES_PER_UPDATE)) {\n log(`Connect ${peer}.`);\n this._controller.connect(peer);\n }\n this._lastAction = new Date();\n } else {\n log('rate limited connect');\n }\n }\n }\n\n toString(): string {\n return 'MMSTTopology';\n }\n}\n\nconst sortByXorDistance = (keys: PublicKey[], reference: PublicKey): PublicKey[] => {\n const sorted = keys.sort((a, b) => {\n return compareXor(distXor(a.asBuffer(), reference.asBuffer()), distXor(b.asBuffer(), reference.asBuffer()));\n });\n log('Sorted keys', { keys, reference, sorted });\n return sorted;\n};\n\nconst distXor = (a: Buffer, b: Buffer) => {\n const maxLength = Math.max(a.length, b.length);\n const result = Buffer.allocUnsafe(maxLength);\n for (let i = 0; i < maxLength; i++) {\n result[i] = (a[i] || 0) ^ (b[i] || 0);\n }\n return result;\n};\n\nconst compareXor = (a: Buffer, b: Buffer) => {\n const maxLength = Math.max(a.length, b.length);\n for (let i = 0; i < maxLength; i++) {\n if ((a[i] || 0) === (b[i] || 0)) {\n continue;\n }\n return (a[i] || 0) < (b[i] || 0) ? -1 : 1;\n }\n return 0;\n};\n","//\n// Copyright 2020 DXOS.org\n//\n\nimport { invariant } from '@dxos/invariant';\nimport { type PublicKey } from '@dxos/keys';\nimport { log } from '@dxos/log';\n\nimport { type SwarmController, type Topology } from './topology';\n\nexport class StarTopology implements Topology {\n private _controller?: SwarmController;\n\n constructor(private readonly _centralPeer: PublicKey) {}\n\n toString(): string {\n return `StarTopology(${this._centralPeer.truncate()})`;\n }\n\n init(controller: SwarmController): void {\n invariant(!this._controller, 'Already initialized.');\n this._controller = controller;\n }\n\n update(): void {\n invariant(this._controller, 'Not initialized.');\n const { candidates, connected, ownPeerId } = this._controller.getState();\n if (!ownPeerId.equals(this._centralPeer)) {\n log('leaf peer dropping all connections apart from central peer.');\n\n // Drop all connections other than central peer.\n for (const peer of connected) {\n if (!peer.equals(this._centralPeer)) {\n log('dropping connection', { peer });\n this._controller.disconnect(peer);\n }\n }\n }\n\n for (const peer of candidates) {\n // Connect to central peer.\n if (peer.equals(this._centralPeer) || ownPeerId.equals(this._centralPeer)) {\n log('connecting to peer', { peer });\n this._controller.connect(peer);\n }\n }\n }\n\n async onOffer(peer: PublicKey): Promise<boolean> {\n invariant(this._controller, 'Not initialized.');\n const { ownPeerId } = this._controller.getState();\n log('offer', {\n peer,\n isCentral: peer.equals(this._centralPeer),\n isSelfCentral: ownPeerId.equals(this._centralPeer),\n });\n return ownPeerId.equals(this._centralPeer) || peer.equals(this._centralPeer);\n }\n\n async destroy(): Promise<void> {\n // Nothing to do.\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,237 @@
1
+ import { a as createRtcTransportFactory, c as MemoryTransportFactory, i as RtcTransportProxyFactory, l as FullyConnectedTopology, n as RtcTransportService, o as TransportKind, t as createTeleportProtocolFactory, u as SwarmNetworkManager } from "./chunk-wire-protocol.mjs";
2
+ import { Event, asyncTimeout } from "@dxos/async";
3
+ import { PublicKey } from "@dxos/keys";
4
+ import { ComplexMap } from "@dxos/util";
5
+ import { Context } from "@dxos/context";
6
+ import { log } from "@dxos/log";
7
+ import { MemorySignalManager, MemorySignalManagerContext } from "@dxos/messaging";
8
+ import { schema } from "@dxos/protocols/proto";
9
+ import { ConnectionState } from "@dxos/protocols/proto/dxos/client/services";
10
+ import { TestExtension, TestExtensionWithStreams } from "@dxos/teleport";
11
+ import { createLinkedPorts, createProtoRpcPeer } from "@dxos/rpc";
12
+ import { TcpTransportFactory } from "#tcp-transport";
13
+ //#region src/testing/test-wire-protocol.ts
14
+ var __dxlog_file$1 = "/__w/dxos/dxos/packages/core/mesh/network-manager/src/testing/test-wire-protocol.ts";
15
+ var TestWireProtocol = class {
16
+ _extensionFactory;
17
+ connections = new ComplexMap(PublicKey.hash);
18
+ streamConnections = new ComplexMap(PublicKey.hash);
19
+ connected = new Event();
20
+ disconnected = new Event();
21
+ otherConnections = new ComplexMap(({ remotePeerId, extension }) => remotePeerId.toHex() + extension);
22
+ constructor(_extensionFactory = () => []) {
23
+ this._extensionFactory = _extensionFactory;
24
+ }
25
+ factory = createTeleportProtocolFactory(async (teleport) => {
26
+ log("create", { remotePeerId: teleport.remotePeerId }, {
27
+ "~LogMeta": "~LogMeta",
28
+ F: __dxlog_file$1,
29
+ L: 35,
30
+ S: this
31
+ });
32
+ const handleDisconnect = () => {
33
+ this.connections.delete(teleport.remotePeerId);
34
+ this.disconnected.emit(teleport.remotePeerId);
35
+ };
36
+ const extension = new TestExtension({
37
+ onClose: async () => handleDisconnect(),
38
+ onAbort: async () => handleDisconnect()
39
+ });
40
+ this.connections.set(teleport.remotePeerId, extension);
41
+ teleport.addExtension("test", extension);
42
+ this.connected.emit(teleport.remotePeerId);
43
+ const streamExtension = new TestExtensionWithStreams({ onClose: async () => {
44
+ this.streamConnections.delete(teleport.remotePeerId);
45
+ } });
46
+ this.streamConnections.set(teleport.remotePeerId, streamExtension);
47
+ teleport.addExtension("test-stream", streamExtension);
48
+ for (const { name, extension } of this._extensionFactory()) {
49
+ this.otherConnections.set({
50
+ remotePeerId: teleport.remotePeerId,
51
+ extension: name
52
+ }, extension);
53
+ teleport.addExtension(name, extension);
54
+ }
55
+ });
56
+ async waitForConnection(peerId) {
57
+ if (this.connections.has(peerId)) return this.connections.get(peerId);
58
+ log("waitForConnection", { peerId }, {
59
+ "~LogMeta": "~LogMeta",
60
+ F: __dxlog_file$1,
61
+ L: 66,
62
+ S: this
63
+ });
64
+ await asyncTimeout(this.connected.waitFor((connectedId) => connectedId.equals(peerId)), 1e4);
65
+ return this.connections.get(peerId);
66
+ }
67
+ async testConnection(peerId, message) {
68
+ await (await this.waitForConnection(peerId)).test(message);
69
+ }
70
+ async openStream(peerId, streamTag, streamLoadInterval, streamLoadChunkSize) {
71
+ if (!this.streamConnections.has(peerId)) throw new Error("Connection does not exist.");
72
+ return this.streamConnections.get(peerId).addNewStream(streamLoadInterval, streamLoadChunkSize, streamTag);
73
+ }
74
+ async closeStream(peerId, streamTag) {
75
+ if (!this.streamConnections.has(peerId)) throw new Error("Connection does not exist.");
76
+ return this.streamConnections.get(peerId).closeStream(streamTag);
77
+ }
78
+ };
79
+ //#endregion
80
+ //#region src/testing/test-builder.ts
81
+ var __dxlog_file = "/__w/dxos/dxos/packages/core/mesh/network-manager/src/testing/test-builder.ts";
82
+ /**
83
+ * Builder used to construct networks and peers.
84
+ */
85
+ var TestBuilder = class {
86
+ options;
87
+ _signalContext = new MemorySignalManagerContext();
88
+ constructor(options = {}) {
89
+ this.options = options;
90
+ }
91
+ createSignalManager() {
92
+ return new MemorySignalManager(this._signalContext);
93
+ }
94
+ createPeer(peerId = PublicKey.random()) {
95
+ return new TestPeer(this, peerId, this.options.transport);
96
+ }
97
+ };
98
+ /**
99
+ * Testing network peer.
100
+ */
101
+ var TestPeer = class {
102
+ testBuilder;
103
+ peerId;
104
+ transport;
105
+ _swarms = new ComplexMap(PublicKey.hash);
106
+ /**
107
+ * @internal
108
+ */
109
+ _signalManager;
110
+ /**
111
+ * @internal
112
+ */
113
+ _networkManager;
114
+ _proxy;
115
+ _service;
116
+ constructor(testBuilder, peerId, transport = TransportKind.MEMORY) {
117
+ this.testBuilder = testBuilder;
118
+ this.peerId = peerId;
119
+ this.transport = transport;
120
+ this._signalManager = this.testBuilder.createSignalManager();
121
+ this._networkManager = this.createNetworkManager(this.transport);
122
+ this._networkManager.setPeerInfo({
123
+ identityDid: `did:halo:${peerId.toHex()}`,
124
+ peerKey: peerId.toHex()
125
+ });
126
+ }
127
+ createNetworkManager(transport) {
128
+ let transportFactory;
129
+ switch (transport) {
130
+ case TransportKind.MEMORY:
131
+ transportFactory = MemoryTransportFactory;
132
+ break;
133
+ case TransportKind.TCP:
134
+ transportFactory = TcpTransportFactory;
135
+ break;
136
+ case TransportKind.WEB_RTC:
137
+ transportFactory = createRtcTransportFactory();
138
+ break;
139
+ case TransportKind.WEB_RTC_PROXY:
140
+ {
141
+ const [proxyPort, servicePort] = createLinkedPorts();
142
+ this._proxy = createProtoRpcPeer({
143
+ port: proxyPort,
144
+ requested: { BridgeService: schema.getService("dxos.mesh.bridge.BridgeService") },
145
+ noHandshake: true,
146
+ encodingOptions: { preserveAny: true }
147
+ });
148
+ this._service = createProtoRpcPeer({
149
+ port: servicePort,
150
+ exposed: { BridgeService: schema.getService("dxos.mesh.bridge.BridgeService") },
151
+ handlers: { BridgeService: new RtcTransportService() },
152
+ noHandshake: true,
153
+ encodingOptions: { preserveAny: true }
154
+ });
155
+ transportFactory = new RtcTransportProxyFactory().setBridgeService(this._proxy.rpc.BridgeService);
156
+ }
157
+ break;
158
+ default: throw new Error(`Unsupported transport: ${transport}`);
159
+ }
160
+ return new SwarmNetworkManager({
161
+ signalManager: this._signalManager,
162
+ transportFactory
163
+ });
164
+ }
165
+ async open() {
166
+ await this._networkManager.open();
167
+ await this._proxy?.open();
168
+ await this._service?.open();
169
+ }
170
+ async close() {
171
+ await Promise.all(Array.from(this._swarms.values()).map((swarm) => swarm.leave()));
172
+ this._swarms.clear();
173
+ await this._proxy?.close();
174
+ await this._service?.close();
175
+ await this._networkManager.close(Context.default(void 0, {
176
+ "~LogMeta": "~LogMeta",
177
+ F: __dxlog_file,
178
+ L: 145
179
+ }));
180
+ }
181
+ getSwarm(topic) {
182
+ const swarm = this._swarms.get(topic);
183
+ if (!swarm) throw new Error(`Swarm not found for topic: ${topic}`);
184
+ return swarm;
185
+ }
186
+ createSwarm(topic, extensionFactory = () => []) {
187
+ const swarm = new TestSwarmConnection(this, topic, extensionFactory);
188
+ this._swarms.set(topic, swarm);
189
+ return swarm;
190
+ }
191
+ async goOffline() {
192
+ await this._networkManager.setConnectionState(ConnectionState.OFFLINE);
193
+ }
194
+ async goOnline() {
195
+ await this._networkManager.setConnectionState(ConnectionState.ONLINE);
196
+ }
197
+ };
198
+ var TestSwarmConnection = class {
199
+ peer;
200
+ topic;
201
+ extensionFactory;
202
+ protocol;
203
+ constructor(peer, topic, extensionFactory) {
204
+ this.peer = peer;
205
+ this.topic = topic;
206
+ this.extensionFactory = extensionFactory;
207
+ this.protocol = new TestWireProtocol(this.extensionFactory);
208
+ }
209
+ async join(topology = new FullyConnectedTopology()) {
210
+ await this.peer._networkManager.joinSwarm(Context.default(void 0, {
211
+ "~LogMeta": "~LogMeta",
212
+ F: __dxlog_file,
213
+ L: 194
214
+ }), {
215
+ topic: this.topic,
216
+ peerInfo: {
217
+ peerKey: this.peer.peerId.toHex(),
218
+ identityDid: `did:halo:${this.peer.peerId.toHex()}`
219
+ },
220
+ protocolProvider: this.protocol.factory,
221
+ topology
222
+ });
223
+ return this;
224
+ }
225
+ async leave() {
226
+ await this.peer._networkManager.leaveSwarm(Context.default(void 0, {
227
+ "~LogMeta": "~LogMeta",
228
+ F: __dxlog_file,
229
+ L: 205
230
+ }), this.topic);
231
+ return this;
232
+ }
233
+ };
234
+ //#endregion
235
+ export { TestBuilder, TestPeer, TestSwarmConnection };
236
+
237
+ //# sourceMappingURL=testing.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.mjs","names":[],"sources":["../../src/testing/test-wire-protocol.ts","../../src/testing/test-builder.ts"],"sourcesContent":["//\n// Copyright 2022 DXOS.org\n//\n\nimport { Event, asyncTimeout } from '@dxos/async';\nimport { PublicKey } from '@dxos/keys';\nimport { log } from '@dxos/log';\nimport { TestExtension, TestExtensionWithStreams } from '@dxos/teleport';\nimport type { TeleportExtension, TestStreamStats } from '@dxos/teleport';\nimport { ComplexMap } from '@dxos/util';\n\nimport { createTeleportProtocolFactory } from '../wire-protocol';\n\nexport type TestTeleportExtension = {\n name: string;\n extension: TeleportExtension;\n};\n\nexport type TestTeleportExtensionFactory = () => TestTeleportExtension[];\n\nexport class TestWireProtocol {\n public readonly connections = new ComplexMap<PublicKey, TestExtension>(PublicKey.hash);\n public readonly streamConnections = new ComplexMap<PublicKey, TestExtensionWithStreams>(PublicKey.hash);\n\n public readonly connected = new Event<PublicKey>();\n public readonly disconnected = new Event<PublicKey>();\n\n public readonly otherConnections = new ComplexMap<{ remotePeerId: PublicKey; extension: string }, TeleportExtension>(\n ({ remotePeerId, extension }) => remotePeerId.toHex() + extension,\n );\n\n constructor(private readonly _extensionFactory: TestTeleportExtensionFactory = () => []) {}\n\n readonly factory = createTeleportProtocolFactory(async (teleport) => {\n log('create', { remotePeerId: teleport.remotePeerId });\n const handleDisconnect = () => {\n this.connections.delete(teleport.remotePeerId);\n this.disconnected.emit(teleport.remotePeerId);\n };\n const extension = new TestExtension({\n onClose: async () => handleDisconnect(),\n onAbort: async () => handleDisconnect(),\n });\n this.connections.set(teleport.remotePeerId, extension);\n teleport.addExtension('test', extension);\n this.connected.emit(teleport.remotePeerId);\n\n const streamExtension = new TestExtensionWithStreams({\n onClose: async () => {\n this.streamConnections.delete(teleport.remotePeerId);\n },\n });\n this.streamConnections.set(teleport.remotePeerId, streamExtension);\n teleport.addExtension('test-stream', streamExtension);\n\n for (const { name, extension } of this._extensionFactory()) {\n this.otherConnections.set({ remotePeerId: teleport.remotePeerId, extension: name }, extension);\n teleport.addExtension(name, extension);\n }\n });\n\n async waitForConnection(peerId: PublicKey): Promise<TestExtension> {\n if (this.connections.has(peerId)) {\n return this.connections.get(peerId)!;\n }\n log('waitForConnection', { peerId });\n await asyncTimeout(\n this.connected.waitFor((connectedId) => connectedId.equals(peerId)),\n // TODO(nf): Make this configurable.\n 10_000,\n );\n return this.connections.get(peerId)!;\n }\n\n async testConnection(peerId: PublicKey, message?: string): Promise<void> {\n const connection = await this.waitForConnection(peerId);\n await connection.test(message);\n }\n\n async openStream(\n peerId: PublicKey,\n streamTag: string,\n streamLoadInterval: number,\n streamLoadChunkSize: number,\n ): Promise<string> {\n if (!this.streamConnections.has(peerId)) {\n throw new Error('Connection does not exist.');\n }\n const connection = this.streamConnections.get(peerId)!;\n return connection.addNewStream(streamLoadInterval, streamLoadChunkSize, streamTag);\n }\n\n async closeStream(peerId: PublicKey, streamTag: string): Promise<TestStreamStats> {\n if (!this.streamConnections.has(peerId)) {\n throw new Error('Connection does not exist.');\n }\n const connection = this.streamConnections.get(peerId)!;\n return connection.closeStream(streamTag);\n }\n}\n","//\n// Copyright 2022 DXOS.org\n//\n\nimport { Context } from '@dxos/context';\nimport { PublicKey } from '@dxos/keys';\nimport { MemorySignalManager, MemorySignalManagerContext, type SignalManager } from '@dxos/messaging';\nimport { schema } from '@dxos/protocols/proto';\nimport { ConnectionState } from '@dxos/protocols/proto/dxos/client/services';\nimport { type ProtoRpcPeer, createLinkedPorts, createProtoRpcPeer } from '@dxos/rpc';\nimport { ComplexMap } from '@dxos/util';\n\nimport { TcpTransportFactory } from '#tcp-transport';\n\nimport { SwarmNetworkManager } from '../network-manager';\nimport { FullyConnectedTopology } from '../topology';\nimport {\n MemoryTransportFactory,\n RtcTransportProxyFactory,\n RtcTransportService,\n type TransportFactory,\n TransportKind,\n createRtcTransportFactory,\n} from '../transport';\nimport { type TestTeleportExtensionFactory, TestWireProtocol } from './test-wire-protocol';\n\nexport type TestBuilderOptions = {\n transport?: TransportKind;\n};\n\n/**\n * Builder used to construct networks and peers.\n */\nexport class TestBuilder {\n private _signalContext = new MemorySignalManagerContext();\n\n constructor(public readonly options: TestBuilderOptions = {}) {}\n\n createSignalManager(): MemorySignalManager {\n // KUBE `WebsocketSignalManager` was removed; signaling is always in-memory (the shared context\n // connects peers in-process). The transport is chosen independently via `options.transport`.\n return new MemorySignalManager(this._signalContext);\n }\n\n createPeer(peerId: PublicKey = PublicKey.random()): TestPeer {\n return new TestPeer(this, peerId, this.options.transport);\n }\n}\n\n/**\n * Testing network peer.\n */\nexport class TestPeer {\n private readonly _swarms = new ComplexMap<PublicKey, TestSwarmConnection>(PublicKey.hash);\n\n /**\n * @internal\n */\n readonly _signalManager: SignalManager;\n\n /**\n * @internal\n */\n readonly _networkManager: SwarmNetworkManager;\n\n private _proxy?: ProtoRpcPeer<any>;\n private _service?: ProtoRpcPeer<any>;\n\n constructor(\n private readonly testBuilder: TestBuilder,\n public readonly peerId: PublicKey,\n public readonly transport: TransportKind = TransportKind.MEMORY,\n ) {\n this._signalManager = this.testBuilder.createSignalManager();\n this._networkManager = this.createNetworkManager(this.transport);\n this._networkManager.setPeerInfo({ identityDid: `did:halo:${peerId.toHex()}`, peerKey: peerId.toHex() });\n }\n\n // TODO(burdon): Move to TestBuilder.\n createNetworkManager(transport: TransportKind): SwarmNetworkManager {\n let transportFactory: TransportFactory;\n switch (transport) {\n case TransportKind.MEMORY:\n transportFactory = MemoryTransportFactory;\n break;\n case TransportKind.TCP:\n transportFactory = TcpTransportFactory;\n break;\n case TransportKind.WEB_RTC:\n transportFactory = createRtcTransportFactory();\n break;\n case TransportKind.WEB_RTC_PROXY:\n {\n // Simulates bridge to shared worker.\n const [proxyPort, servicePort] = createLinkedPorts();\n\n this._proxy = createProtoRpcPeer({\n port: proxyPort,\n requested: {\n BridgeService: schema.getService('dxos.mesh.bridge.BridgeService'),\n },\n noHandshake: true,\n encodingOptions: {\n preserveAny: true,\n },\n });\n\n this._service = createProtoRpcPeer({\n port: servicePort,\n exposed: {\n BridgeService: schema.getService('dxos.mesh.bridge.BridgeService'),\n },\n handlers: { BridgeService: new RtcTransportService() },\n noHandshake: true,\n encodingOptions: {\n preserveAny: true,\n },\n });\n\n transportFactory = new RtcTransportProxyFactory().setBridgeService(this._proxy.rpc.BridgeService);\n }\n break;\n default:\n throw new Error(`Unsupported transport: ${transport}`);\n }\n\n return new SwarmNetworkManager({\n signalManager: this._signalManager,\n transportFactory,\n });\n }\n\n async open(): Promise<void> {\n await this._networkManager.open();\n await this._proxy?.open();\n await this._service?.open();\n }\n\n async close(): Promise<void> {\n await Promise.all(Array.from(this._swarms.values()).map((swarm) => swarm.leave()));\n this._swarms.clear();\n\n await this._proxy?.close();\n await this._service?.close();\n await this._networkManager.close(Context.default());\n }\n\n getSwarm(topic: PublicKey): TestSwarmConnection {\n const swarm = this._swarms.get(topic);\n if (!swarm) {\n throw new Error(`Swarm not found for topic: ${topic}`);\n }\n\n return swarm;\n }\n\n createSwarm(topic: PublicKey, extensionFactory: TestTeleportExtensionFactory = () => []): TestSwarmConnection {\n // TODO(burdon): Multiple.\n // if (this._swarms.get(topic)) {\n // throw new Error(`Swarm already exists for topic: ${topic.truncate()}`);\n // }\n\n const swarm = new TestSwarmConnection(this, topic, extensionFactory);\n this._swarms.set(topic, swarm);\n return swarm;\n }\n\n async goOffline(): Promise<void> {\n await this._networkManager.setConnectionState(ConnectionState.OFFLINE);\n }\n\n async goOnline(): Promise<void> {\n await this._networkManager.setConnectionState(ConnectionState.ONLINE);\n }\n}\n\n// TODO(burdon): Reconcile with new Swarm concept.\nexport class TestSwarmConnection {\n protocol: TestWireProtocol;\n\n constructor(\n readonly peer: TestPeer,\n readonly topic: PublicKey,\n readonly extensionFactory: TestTeleportExtensionFactory,\n ) {\n // TODO(burdon): Configure plugins.\n // TODO(burdon): Prevent reuse?\n this.protocol = new TestWireProtocol(this.extensionFactory);\n }\n\n // TODO(burdon): Need to create new plugin instance per swarm?\n // If so, then perhaps joinSwarm should return swarm object with access to plugins.\n async join(topology = new FullyConnectedTopology()): Promise<this> {\n await this.peer._networkManager.joinSwarm(Context.default(), {\n topic: this.topic,\n peerInfo: { peerKey: this.peer.peerId.toHex(), identityDid: `did:halo:${this.peer.peerId.toHex()}` },\n protocolProvider: this.protocol.factory,\n topology,\n });\n\n return this;\n }\n\n async leave(): Promise<this> {\n await this.peer._networkManager.leaveSwarm(Context.default(), this.topic);\n return this;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import { Event } from "@dxos/async";
2
+ import { ErrorStream } from "@dxos/debug";
3
+ //#region src/transport/tcp/tcp-transport.browser.ts
4
+ var TcpTransportFactory = { createTransport: () => new TcpTransport() };
5
+ /**
6
+ * NOTE: Browser stub.
7
+ */
8
+ var TcpTransport = class {
9
+ closed = new Event();
10
+ connected = new Event();
11
+ errors = new ErrorStream();
12
+ get isOpen() {
13
+ return true;
14
+ }
15
+ async open() {
16
+ return this;
17
+ }
18
+ async close() {
19
+ return this;
20
+ }
21
+ async onSignal() {
22
+ throw new Error("Method not implemented.");
23
+ }
24
+ async getStats() {
25
+ throw new Error("Method not implemented.");
26
+ }
27
+ async getDetails() {
28
+ throw new Error("Method not implemented.");
29
+ }
30
+ };
31
+ //#endregion
32
+ export { TcpTransport, TcpTransportFactory };
33
+
34
+ //# sourceMappingURL=tcp-transport.browser.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tcp-transport.browser.mjs","names":[],"sources":["../../../../src/transport/tcp/tcp-transport.browser.ts"],"sourcesContent":["//\n// Copyright 2020 DXOS.org\n//\n\nimport { Event } from '@dxos/async';\nimport { ErrorStream } from '@dxos/debug';\n\nimport { type Transport, type TransportFactory, type TransportStats } from '../transport';\n\nexport const TcpTransportFactory: TransportFactory = {\n createTransport: () => new TcpTransport(),\n};\n\n/**\n * NOTE: Browser stub.\n */\nexport class TcpTransport implements Transport {\n public readonly closed = new Event<void>();\n public readonly connected = new Event<void>();\n public readonly errors = new ErrorStream();\n\n get isOpen() {\n return true;\n }\n\n async open(): Promise<this> {\n return this;\n }\n\n async close(): Promise<this> {\n return this;\n }\n\n async onSignal(): Promise<void> {\n throw new Error('Method not implemented.');\n }\n\n async getStats(): Promise<TransportStats> {\n throw new Error('Method not implemented.');\n }\n\n async getDetails(): Promise<string> {\n throw new Error('Method not implemented.');\n }\n}\n"],"mappings":";;;AASA,IAAa,sBAAwC,EACnD,uBAAuB,IAAI,aAAa,EAC1C;;;;AAKA,IAAa,eAAb,MAA+C;CAC7C,SAAyB,IAAI,MAAY;CACzC,YAA4B,IAAI,MAAY;CAC5C,SAAyB,IAAI,YAAY;CAEzC,IAAI,SAAS;EACX,OAAO;CACT;CAEA,MAAM,OAAsB;EAC1B,OAAO;CACT;CAEA,MAAM,QAAuB;EAC3B,OAAO;CACT;CAEA,MAAM,WAA0B;EAC9B,MAAM,IAAI,MAAM,yBAAyB;CAC3C;CAEA,MAAM,WAAoC;EACxC,MAAM,IAAI,MAAM,yBAAyB;CAC3C;CAEA,MAAM,aAA8B;EAClC,MAAM,IAAI,MAAM,yBAAyB;CAC3C;AACF"}