@dxos/network-manager 0.10.0 → 0.11.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 (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
@@ -1,3095 +0,0 @@
1
- import "@dxos/node-std/globals";
2
-
3
- // src/swarm/connection.ts
4
- import { DeferredTask, Event, Trigger, scheduleTask, scheduleTaskInterval, sleep, synchronized } from "@dxos/async";
5
- import { Context, ContextDisposedError, cancelWithContext } from "@dxos/context";
6
- import { ErrorStream } from "@dxos/debug";
7
- import { invariant } from "@dxos/invariant";
8
- import { log, logInfo } from "@dxos/log";
9
- import { CancelledError, ConnectionResetError, ConnectivityError, ProtocolError, TimeoutError } from "@dxos/protocols";
10
- var __dxlog_file = "/__w/dxos/dxos/packages/core/mesh/network-manager/src/swarm/connection.ts";
11
- function _ts_decorate(decorators, target, key, desc) {
12
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
13
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
14
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
15
- return c > 3 && r && Object.defineProperty(target, key, r), r;
16
- }
17
- var STARTING_SIGNALLING_DELAY = 10;
18
- var TRANSPORT_CONNECTION_TIMEOUT = 1e4;
19
- var TRANSPORT_STATS_INTERVAL = 5e3;
20
- var MAX_SIGNALLING_DELAY = 300;
21
- var ConnectionState = /* @__PURE__ */ (function(ConnectionState5) {
22
- ConnectionState5["CREATED"] = "CREATED";
23
- ConnectionState5["INITIAL"] = "INITIAL";
24
- ConnectionState5["CONNECTING"] = "CONNECTING";
25
- ConnectionState5["CONNECTED"] = "CONNECTED";
26
- ConnectionState5["CLOSING"] = "CLOSING";
27
- ConnectionState5["CLOSED"] = "CLOSED";
28
- ConnectionState5["ABORTING"] = "ABORTING";
29
- ConnectionState5["ABORTED"] = "ABORTED";
30
- return ConnectionState5;
31
- })({});
32
- var Connection = class {
33
- topic;
34
- localInfo;
35
- remoteInfo;
36
- sessionId;
37
- initiator;
38
- _signalMessaging;
39
- _protocol;
40
- _transportFactory;
41
- _callbacks;
42
- _ctx = new Context(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 66 });
43
- connectedTimeoutContext = new Context(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 67 });
44
- _protocolClosed = new Trigger();
45
- _transportClosed = new Trigger();
46
- _state = "CREATED";
47
- _transport;
48
- closeReason;
49
- _incomingSignalBuffer = [];
50
- _outgoingSignalBuffer = [];
51
- stateChanged = new Event();
52
- errors = new ErrorStream();
53
- transportStats = new Event();
54
- _signalSendTask = new DeferredTask(this._ctx, async () => {
55
- await this._flushSignalBuffer(this._ctx);
56
- });
57
- _signallingDelay = STARTING_SIGNALLING_DELAY;
58
- constructor(topic, localInfo, remoteInfo, sessionId, initiator, _signalMessaging, _protocol, _transportFactory, _callbacks) {
59
- this.topic = topic;
60
- this.localInfo = localInfo;
61
- this.remoteInfo = remoteInfo;
62
- this.sessionId = sessionId;
63
- this.initiator = initiator;
64
- this._signalMessaging = _signalMessaging;
65
- this._protocol = _protocol;
66
- this._transportFactory = _transportFactory;
67
- this._callbacks = _callbacks;
68
- log.trace("dxos.mesh.connection.construct", {
69
- sessionId: this.sessionId,
70
- topic: this.topic,
71
- localPeer: this.localInfo,
72
- remotePeer: this.remoteInfo,
73
- initiator: this.initiator
74
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 92, S: this });
75
- }
76
- get sessionIdString() {
77
- return this.sessionId.truncate();
78
- }
79
- get state() {
80
- return this._state;
81
- }
82
- get transport() {
83
- return this._transport;
84
- }
85
- get protocol() {
86
- return this._protocol;
87
- }
88
- /**
89
- * Create an underlying transport and prepares it for the connection.
90
- */
91
- async openConnection() {
92
- invariant(this._state === "INITIAL", "Invalid state.", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 115, S: this, A: ['this._state === "INITIAL"', "'Invalid state.'"] });
93
- log("opening connection", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 116, S: this });
94
- log.trace("dxos.mesh.connection.open", {
95
- sessionId: this.sessionId,
96
- topic: this.topic,
97
- localPeerId: this.localInfo,
98
- remotePeerId: this.remoteInfo,
99
- initiator: this.initiator
100
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 117, S: this });
101
- this._changeState("CONNECTING");
102
- this._protocol.open(this.sessionId).catch((err) => {
103
- this.errors.raise(err);
104
- });
105
- this._protocol.stream.on("close", () => {
106
- log("protocol stream closed", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 131, S: this });
107
- this._protocolClosed.wake();
108
- this.close({
109
- error: new ProtocolError({
110
- message: "protocol stream closed"
111
- })
112
- }).catch((err) => this.errors.raise(err));
113
- });
114
- scheduleTask(this.connectedTimeoutContext, async () => {
115
- log.info(`timeout waiting ${TRANSPORT_CONNECTION_TIMEOUT / 1e3}s for transport to connect, aborting`, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 140, S: this });
116
- await this.abort(new TimeoutError({
117
- message: `${TRANSPORT_CONNECTION_TIMEOUT / 1e3}s for transport to connect`
118
- })).catch((err) => this.errors.raise(err));
119
- }, TRANSPORT_CONNECTION_TIMEOUT);
120
- invariant(!this._transport, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 145, S: this, A: ["!this._transport", ""] });
121
- this._transport = this._transportFactory.createTransport({
122
- ownPeerKey: this.localInfo.peerKey,
123
- remotePeerKey: this.remoteInfo.peerKey,
124
- topic: this.topic.toHex(),
125
- initiator: this.initiator,
126
- stream: this._protocol.stream,
127
- sendSignal: async (signal) => this._sendSignal(signal),
128
- sessionId: this.sessionId
129
- });
130
- this._transport.connected.once(async () => {
131
- this._changeState("CONNECTED");
132
- await this.connectedTimeoutContext.dispose();
133
- this._callbacks?.onConnected?.();
134
- scheduleTaskInterval(this._ctx, async () => this._emitTransportStats(), TRANSPORT_STATS_INTERVAL);
135
- });
136
- this._transport.closed.once(() => {
137
- this._transport = void 0;
138
- this._transportClosed.wake();
139
- log("abort triggered by transport close", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 164, S: this });
140
- this.abort().catch((err) => this.errors.raise(err));
141
- });
142
- this._transport.errors.handle(async (err) => {
143
- log("transport error:", {
144
- err
145
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 168, S: this });
146
- if (!this.closeReason) {
147
- this.closeReason = err?.message;
148
- }
149
- if (err instanceof ConnectionResetError) {
150
- log.info("aborting due to transport ConnectionResetError", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 176, S: this });
151
- this.abort(err).catch((err2) => this.errors.raise(err2));
152
- } else if (err instanceof ConnectivityError) {
153
- log.info("aborting due to transport ConnectivityError", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 179, S: this });
154
- this.abort(err).catch((err2) => this.errors.raise(err2));
155
- }
156
- if (this._state !== "CLOSED" && this._state !== "CLOSING") {
157
- await this.connectedTimeoutContext.dispose();
158
- this.errors.raise(err);
159
- }
160
- });
161
- await this._transport.open();
162
- for (const signal of this._incomingSignalBuffer) {
163
- void this._transport.onSignal(signal);
164
- }
165
- this._incomingSignalBuffer = [];
166
- log("opened connection", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 193, S: this });
167
- }
168
- async abort(err) {
169
- log("abort", {
170
- err
171
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 196, S: this });
172
- if (this._state === "CLOSED" || this._state === "ABORTED") {
173
- log(`abort ignored: already ${this._state}`, this.closeReason, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 200, S: this });
174
- return;
175
- }
176
- await this.connectedTimeoutContext.dispose();
177
- this._changeState("ABORTING");
178
- if (!this.closeReason) {
179
- this.closeReason = err?.message;
180
- }
181
- await this._ctx.dispose();
182
- log("aborting...", {
183
- peerId: this.localInfo,
184
- err
185
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 209, S: this });
186
- try {
187
- await this._closeProtocol({
188
- abort: true
189
- });
190
- } catch (err2) {
191
- log.catch(err2, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 219, S: this });
192
- }
193
- try {
194
- await this._closeTransport();
195
- } catch (err2) {
196
- log.catch(err2, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 225, S: this });
197
- }
198
- try {
199
- this._callbacks?.onClosed?.(err);
200
- } catch (err2) {
201
- log.catch(err2, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 230, S: this });
202
- }
203
- this._changeState("ABORTED");
204
- }
205
- async close({ error, reason } = {}) {
206
- log("close", {
207
- error
208
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 235, S: this });
209
- if (!this.closeReason) {
210
- this.closeReason = reason ?? error?.message;
211
- } else {
212
- this.closeReason += `; ${reason ?? error?.message}`;
213
- }
214
- if (this._state === "CLOSED" || this._state === "ABORTING" || this._state === "ABORTED") {
215
- log("close ignored: already in progress", {
216
- state: this._state,
217
- error
218
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 244, S: this });
219
- return;
220
- }
221
- const lastState = this._state;
222
- this._changeState("CLOSING");
223
- await this.connectedTimeoutContext.dispose();
224
- await this._ctx.dispose();
225
- let abortProtocol = false;
226
- if (lastState !== "CONNECTED" || error != null) {
227
- log(`graceful close requested when we were in ${lastState} state? aborting`, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 256, S: this });
228
- abortProtocol = true;
229
- }
230
- log("closing...", {
231
- peerId: this.localInfo,
232
- abortProtocol,
233
- error
234
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 259, S: this });
235
- try {
236
- await this._closeProtocol({
237
- abort: abortProtocol
238
- });
239
- } catch (err) {
240
- log.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 269, S: this });
241
- }
242
- try {
243
- await this._closeTransport();
244
- } catch (err) {
245
- log.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 275, S: this });
246
- }
247
- log("closed", {
248
- peerId: this.localInfo
249
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 277, S: this });
250
- this._changeState("CLOSED");
251
- this._callbacks?.onClosed?.(error);
252
- }
253
- async _closeProtocol(options) {
254
- log("closing protocol", options, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 284, S: this });
255
- await Promise.race([
256
- options?.abort ? this._protocol.abort() : this._protocol.close(),
257
- this._protocolClosed.wait()
258
- ]);
259
- log("protocol closed", options, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 289, S: this });
260
- }
261
- async _closeTransport() {
262
- log("closing transport", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 292, S: this });
263
- await Promise.race([
264
- this._transport?.close(),
265
- this._transportClosed.wait()
266
- ]);
267
- log("transport closed", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 297, S: this });
268
- }
269
- _sendSignal(signal) {
270
- this._outgoingSignalBuffer.push(signal);
271
- this._signalSendTask.schedule();
272
- }
273
- async _flushSignalBuffer(ctx) {
274
- if (this._outgoingSignalBuffer.length === 0) {
275
- return;
276
- }
277
- try {
278
- if (true) {
279
- await cancelWithContext(ctx, sleep(this._signallingDelay));
280
- this._signallingDelay = Math.min(this._signallingDelay * 2, MAX_SIGNALLING_DELAY);
281
- }
282
- const signals = [
283
- ...this._outgoingSignalBuffer
284
- ];
285
- this._outgoingSignalBuffer.length = 0;
286
- await this._signalMessaging.signal(ctx, {
287
- author: this.localInfo,
288
- recipient: this.remoteInfo,
289
- sessionId: this.sessionId,
290
- topic: this.topic,
291
- data: {
292
- signalBatch: {
293
- signals
294
- }
295
- }
296
- });
297
- } catch (err) {
298
- if (err instanceof CancelledError || err instanceof ContextDisposedError || err instanceof Error && err.message?.includes("CANCELLED")) {
299
- return;
300
- }
301
- log.info("signal message failed to deliver", {
302
- err
303
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 333, S: this });
304
- await this.close({
305
- error: new ConnectivityError({
306
- message: "signal message failed to deliver",
307
- cause: err
308
- })
309
- });
310
- }
311
- }
312
- /**
313
- * Receive a signal from the remote peer.
314
- */
315
- async signal(_ctx, msg) {
316
- invariant(msg.sessionId, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 347, S: this, A: ["msg.sessionId", ""] });
317
- if (!msg.sessionId.equals(this.sessionId)) {
318
- log("dropping signal for incorrect session id", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 349, S: this });
319
- return;
320
- }
321
- invariant(msg.data.signal || msg.data.signalBatch, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 352, S: this, A: ["msg.data.signal || msg.data.signalBatch", ""] });
322
- invariant(msg.author.peerKey === this.remoteInfo.peerKey, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 353, S: this, A: ["msg.author.peerKey === this.remoteInfo.peerKey", ""] });
323
- invariant(msg.recipient.peerKey === this.localInfo.peerKey, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 354, S: this, A: ["msg.recipient.peerKey === this.localInfo.peerKey", ""] });
324
- const signals = msg.data.signalBatch ? msg.data.signalBatch.signals ?? [] : [
325
- msg.data.signal
326
- ];
327
- for (const signal of signals) {
328
- if (!signal) {
329
- continue;
330
- }
331
- if ([
332
- "CREATED",
333
- "INITIAL"
334
- ].includes(this.state)) {
335
- log("buffered signal", {
336
- peerId: this.localInfo,
337
- remoteId: this.remoteInfo,
338
- msg: msg.data
339
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 366, S: this });
340
- this._incomingSignalBuffer.push(signal);
341
- } else {
342
- invariant(this._transport, "Connection not ready to accept signals.", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 373, S: this, A: ["this._transport", "'Connection not ready to accept signals.'"] });
343
- log("received signal", {
344
- peerId: this.localInfo,
345
- remoteId: this.remoteInfo,
346
- msg: msg.data
347
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 374, S: this });
348
- await this._transport.onSignal(signal);
349
- }
350
- }
351
- }
352
- initiate() {
353
- this._changeState("INITIAL");
354
- }
355
- _changeState(state) {
356
- log("stateChanged", {
357
- from: this._state,
358
- to: state,
359
- peerId: this.localInfo
360
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 387, S: this });
361
- invariant(state !== this._state, "Already in this state.", { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 392, S: this, A: ["state !== this._state", "'Already in this state.'"] });
362
- this._state = state;
363
- this.stateChanged.emit(state);
364
- }
365
- async _emitTransportStats() {
366
- const stats = await this.transport?.getStats();
367
- if (stats) {
368
- this.transportStats.emit(stats);
369
- }
370
- }
371
- };
372
- _ts_decorate([
373
- logInfo
374
- ], Connection.prototype, "sessionIdString", null);
375
- _ts_decorate([
376
- synchronized
377
- ], Connection.prototype, "abort", null);
378
- _ts_decorate([
379
- synchronized
380
- ], Connection.prototype, "close", null);
381
-
382
- // src/signal/swarm-messenger.ts
383
- import { invariant as invariant2 } from "@dxos/invariant";
384
- import { PublicKey } from "@dxos/keys";
385
- import { log as log2 } from "@dxos/log";
386
- import { TimeoutError as TimeoutError2 } from "@dxos/protocols";
387
- import { schema } from "@dxos/protocols/proto";
388
- import { ComplexMap } from "@dxos/util";
389
- var __dxlog_file2 = "/__w/dxos/dxos/packages/core/mesh/network-manager/src/signal/swarm-messenger.ts";
390
- var SwarmMessage = schema.getCodecForType("dxos.mesh.swarm.SwarmMessage");
391
- var SwarmMessenger = class {
392
- _sendMessage;
393
- _onSignal;
394
- _onOffer;
395
- _topic;
396
- _offerRecords = new ComplexMap((key) => key.toHex());
397
- constructor({ sendMessage, onSignal, onOffer, topic }) {
398
- this._sendMessage = sendMessage;
399
- this._onSignal = onSignal;
400
- this._onOffer = onOffer;
401
- this._topic = topic;
402
- }
403
- async receiveMessage(ctx, { author, recipient, payload }) {
404
- if (payload.type_url !== "dxos.mesh.swarm.SwarmMessage") {
405
- return;
406
- }
407
- const message = SwarmMessage.decode(payload.value);
408
- if (!this._topic.equals(message.topic)) {
409
- return;
410
- }
411
- log2("received", {
412
- from: author,
413
- to: recipient,
414
- msg: message
415
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 35, S: this });
416
- if (message.data?.offer) {
417
- await this._handleOffer(ctx, {
418
- author,
419
- recipient,
420
- message
421
- });
422
- } else if (message.data?.answer) {
423
- await this._resolveAnswers(message);
424
- } else if (message.data?.signal) {
425
- await this._handleSignal(ctx, {
426
- author,
427
- recipient,
428
- message
429
- });
430
- } else if (message.data?.signalBatch) {
431
- await this._handleSignal(ctx, {
432
- author,
433
- recipient,
434
- message
435
- });
436
- } else {
437
- log2.warn("unknown message", {
438
- message
439
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 61, S: this });
440
- }
441
- }
442
- async signal(ctx, message) {
443
- invariant2(message.data?.signal || message.data?.signalBatch, "Invalid message", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 67, S: this, A: ["message.data?.signal || message.data?.signalBatch", "'Invalid message'"] });
444
- await this._sendReliableMessage(ctx, {
445
- author: message.author,
446
- recipient: message.recipient,
447
- message
448
- });
449
- }
450
- async offer(ctx, message) {
451
- const networkMessage = {
452
- ...message,
453
- messageId: PublicKey.random()
454
- };
455
- return new Promise((resolve, reject) => {
456
- this._offerRecords.set(networkMessage.messageId, {
457
- resolve
458
- });
459
- this._sendReliableMessage(ctx, {
460
- author: message.author,
461
- recipient: message.recipient,
462
- message: networkMessage
463
- }).catch((err) => reject(err));
464
- });
465
- }
466
- async _sendReliableMessage(ctx, { author, recipient, message }) {
467
- const networkMessage = {
468
- ...message,
469
- // Setting unique message_id if it not specified yet.
470
- messageId: message.messageId ?? PublicKey.random()
471
- };
472
- log2("sending", {
473
- from: author,
474
- to: recipient,
475
- msg: networkMessage
476
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 96, S: this });
477
- await this._sendMessage(ctx, {
478
- author,
479
- recipient,
480
- payload: {
481
- type_url: "dxos.mesh.swarm.SwarmMessage",
482
- value: SwarmMessage.encode(networkMessage)
483
- }
484
- });
485
- }
486
- async _resolveAnswers(message) {
487
- invariant2(message.data?.answer?.offerMessageId, "No offerMessageId", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 111, S: this, A: ["message.data?.answer?.offerMessageId", "'No offerMessageId'"] });
488
- const offerRecord = this._offerRecords.get(message.data.answer.offerMessageId);
489
- if (offerRecord) {
490
- this._offerRecords.delete(message.data.answer.offerMessageId);
491
- invariant2(message.data?.answer, "No answer", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 115, S: this, A: ["message.data?.answer", "'No answer'"] });
492
- log2("resolving", {
493
- answer: message.data.answer
494
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 116, S: this });
495
- offerRecord.resolve(message.data.answer);
496
- }
497
- }
498
- async _handleOffer(ctx, { author, recipient, message }) {
499
- invariant2(message.data.offer, "No offer", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 123, S: this, A: ["message.data.offer", "'No offer'"] });
500
- const offerMessage = {
501
- author,
502
- recipient,
503
- ...message,
504
- data: {
505
- offer: message.data.offer
506
- }
507
- };
508
- const answer = await this._onOffer(ctx, offerMessage);
509
- answer.offerMessageId = message.messageId;
510
- try {
511
- await this._sendReliableMessage(ctx, {
512
- author: recipient,
513
- recipient: author,
514
- message: {
515
- topic: message.topic,
516
- sessionId: message.sessionId,
517
- data: {
518
- answer
519
- }
520
- }
521
- });
522
- } catch (err) {
523
- if (err instanceof TimeoutError2) {
524
- log2.info("timeout sending answer to offer", {
525
- err
526
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 148, S: this });
527
- } else {
528
- log2.info("error sending answer to offer", {
529
- err
530
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 152, S: this });
531
- }
532
- }
533
- }
534
- async _handleSignal(ctx, { author, recipient, message }) {
535
- invariant2(message.messageId, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 159, S: this, A: ["message.messageId", ""] });
536
- invariant2(message.data.signal || message.data.signalBatch, "Invalid message", { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 160, S: this, A: ["message.data.signal || message.data.signalBatch", "'Invalid message'"] });
537
- const signalMessage = {
538
- author,
539
- recipient,
540
- ...message,
541
- data: {
542
- signal: message.data.signal,
543
- signalBatch: message.data.signalBatch
544
- }
545
- };
546
- await this._onSignal(ctx, signalMessage);
547
- }
548
- };
549
-
550
- // src/swarm/swarm.ts
551
- import { Event as Event3, scheduleTask as scheduleTask3, sleep as sleep2, synchronized as synchronized3 } from "@dxos/async";
552
- import { Context as Context3 } from "@dxos/context";
553
- import { ErrorStream as ErrorStream2 } from "@dxos/debug";
554
- import { invariant as invariant4 } from "@dxos/invariant";
555
- import { PublicKey as PublicKey3 } from "@dxos/keys";
556
- import { log as log4, logInfo as logInfo2 } from "@dxos/log";
557
- import { PeerInfoHash } from "@dxos/messaging";
558
- import { ComplexMap as ComplexMap2, isNonNullable } from "@dxos/util";
559
-
560
- // src/swarm/peer.ts
561
- import { Event as Event2, scheduleTask as scheduleTask2, synchronized as synchronized2 } from "@dxos/async";
562
- import { Context as Context2 } from "@dxos/context";
563
- import { invariant as invariant3 } from "@dxos/invariant";
564
- import { PublicKey as PublicKey2 } from "@dxos/keys";
565
- import { log as log3 } from "@dxos/log";
566
- import { CancelledError as CancelledError2, SystemError } from "@dxos/protocols";
567
- var __dxlog_file3 = "/__w/dxos/dxos/packages/core/mesh/network-manager/src/swarm/peer.ts";
568
- function _ts_decorate2(decorators, target, key, desc) {
569
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
570
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
571
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
572
- return c > 3 && r && Object.defineProperty(target, key, r), r;
573
- }
574
- var ConnectionDisplacedError = class extends SystemError {
575
- constructor() {
576
- super({
577
- message: "Connection displaced by remote initiator."
578
- });
579
- }
580
- };
581
- var CONNECTION_COUNTS_STABLE_AFTER = 5e3;
582
- var Peer = class {
583
- remoteInfo;
584
- topic;
585
- localInfo;
586
- _signalMessaging;
587
- _protocolProvider;
588
- _transportFactory;
589
- _connectionLimiter;
590
- _callbacks;
591
- /**
592
- * Will be available to connect after this time.
593
- */
594
- _availableAfter = 0;
595
- availableToConnect = true;
596
- _lastConnectionTime;
597
- _ctx = new Context2(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 44 });
598
- _connectionCtx;
599
- connection;
600
- /**
601
- * Whether the peer is currently advertizing itself on the signal-network.
602
- */
603
- advertizing = false;
604
- initiating = false;
605
- connectionDisplaced = new Event2();
606
- constructor(remoteInfo, topic, localInfo, _signalMessaging, _protocolProvider, _transportFactory, _connectionLimiter, _callbacks) {
607
- this.remoteInfo = remoteInfo;
608
- this.topic = topic;
609
- this.localInfo = localInfo;
610
- this._signalMessaging = _signalMessaging;
611
- this._protocolProvider = _protocolProvider;
612
- this._transportFactory = _transportFactory;
613
- this._connectionLimiter = _connectionLimiter;
614
- this._callbacks = _callbacks;
615
- }
616
- /**
617
- * Respond to remote offer.
618
- */
619
- async onOffer(_ctx, message) {
620
- const remote = message.author;
621
- if (this.connection && ![
622
- ConnectionState.CREATED,
623
- ConnectionState.INITIAL,
624
- ConnectionState.CONNECTING
625
- ].includes(this.connection.state)) {
626
- log3.info(`received offer when connection already in ${this.connection.state} state`, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 71, S: this });
627
- return {
628
- accept: false
629
- };
630
- }
631
- if (this.connection || this.initiating) {
632
- if (remote.peerKey < this.localInfo.peerKey) {
633
- log3("close local connection", {
634
- localPeer: this.localInfo,
635
- topic: this.topic,
636
- remotePeer: this.remoteInfo,
637
- sessionId: this.connection?.sessionId
638
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 82, S: this });
639
- if (this.connection) {
640
- await this.closeConnection(new ConnectionDisplacedError());
641
- }
642
- } else {
643
- return {
644
- accept: false
645
- };
646
- }
647
- }
648
- if (await this._callbacks.onOffer(remote)) {
649
- if (!this.connection) {
650
- invariant3(message.sessionId, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 102, S: this, A: ["message.sessionId", ""] });
651
- const connection = this._createConnection(false, message.sessionId);
652
- try {
653
- await this._connectionLimiter.connecting(message.sessionId);
654
- connection.initiate();
655
- await connection.openConnection();
656
- } catch (err) {
657
- if (!(err instanceof CancelledError2)) {
658
- log3.info("connection error", {
659
- topic: this.topic,
660
- peerId: this.localInfo,
661
- remoteId: this.remoteInfo,
662
- err
663
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 110, S: this });
664
- }
665
- await this.closeConnection(err);
666
- }
667
- return {
668
- accept: true
669
- };
670
- }
671
- }
672
- return {
673
- accept: false
674
- };
675
- }
676
- /**
677
- * Initiate a connection to the remote peer.
678
- */
679
- async initiateConnection(ctx) {
680
- invariant3(!this.initiating, "Initiation in progress.", { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 132, S: this, A: ["!this.initiating", "'Initiation in progress.'"] });
681
- invariant3(!this.connection, "Already connected.", { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 133, S: this, A: ["!this.connection", "'Already connected.'"] });
682
- const sessionId = PublicKey2.random();
683
- log3("initiating...", {
684
- local: this.localInfo,
685
- topic: this.topic,
686
- remote: this.remoteInfo,
687
- sessionId
688
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 135, S: this });
689
- const connection = this._createConnection(true, sessionId);
690
- this.initiating = true;
691
- let answer;
692
- try {
693
- await this._connectionLimiter.connecting(sessionId);
694
- connection.initiate();
695
- answer = await this._signalMessaging.offer(ctx, {
696
- author: this.localInfo,
697
- recipient: this.remoteInfo,
698
- sessionId,
699
- topic: this.topic,
700
- data: {
701
- offer: {}
702
- }
703
- });
704
- log3("received", {
705
- answer,
706
- topic: this.topic,
707
- local: this.localInfo,
708
- remote: this.remoteInfo
709
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 156, S: this });
710
- if (connection.state !== ConnectionState.INITIAL) {
711
- log3("ignoring response", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 163, S: this });
712
- return;
713
- }
714
- } catch (err) {
715
- log3("initiation error: send offer", {
716
- err,
717
- topic: this.topic,
718
- local: this.localInfo,
719
- remote: this.remoteInfo
720
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 167, S: this });
721
- await connection.abort(err);
722
- throw err;
723
- } finally {
724
- this.initiating = false;
725
- }
726
- try {
727
- if (!answer.accept) {
728
- this._callbacks.onRejected();
729
- return;
730
- }
731
- } catch (err) {
732
- log3("initiation error: accept answer", {
733
- err,
734
- topic: this.topic,
735
- local: this.localInfo,
736
- remote: this.remoteInfo
737
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 184, S: this });
738
- await connection.abort(err);
739
- throw err;
740
- } finally {
741
- this.initiating = false;
742
- }
743
- try {
744
- log3("opening connection as initiator", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 196, S: this });
745
- await connection.openConnection();
746
- this._callbacks.onAccepted();
747
- } catch (err) {
748
- log3("initiation error: open connection", {
749
- err,
750
- topic: this.topic,
751
- local: this.localInfo,
752
- remote: this.remoteInfo
753
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 200, S: this });
754
- log3.warn("closing connection due to unhandled error on openConnection", {
755
- err
756
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 207, S: this });
757
- await this.closeConnection(err);
758
- throw err;
759
- } finally {
760
- this.initiating = false;
761
- }
762
- }
763
- /**
764
- * Create new connection.
765
- * Either we're initiating a connection or creating one in response to an offer from the other peer.
766
- */
767
- _createConnection(initiator, sessionId) {
768
- log3("creating connection", {
769
- topic: this.topic,
770
- peerId: this.localInfo,
771
- remoteId: this.remoteInfo,
772
- initiator,
773
- sessionId
774
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 221, S: this });
775
- invariant3(!this.connection, "Already connected.", { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 228, S: this, A: ["!this.connection", "'Already connected.'"] });
776
- const connection = new Connection(
777
- this.topic,
778
- this.localInfo,
779
- this.remoteInfo,
780
- sessionId,
781
- initiator,
782
- this._signalMessaging,
783
- // TODO(dmaretskyi): Init only when connection is established.
784
- this._protocolProvider({
785
- initiator,
786
- localPeerId: PublicKey2.from(this.localInfo.peerKey),
787
- remotePeerId: PublicKey2.from(this.remoteInfo.peerKey),
788
- topic: this.topic
789
- }),
790
- this._transportFactory,
791
- {
792
- onConnected: () => {
793
- this.availableToConnect = true;
794
- this._lastConnectionTime = Date.now();
795
- this._callbacks.onConnected();
796
- this._connectionLimiter.doneConnecting(sessionId);
797
- log3.trace("dxos.mesh.connection.connected", {
798
- topic: this.topic,
799
- localPeerId: this.localInfo,
800
- remotePeerId: this.remoteInfo,
801
- sessionId,
802
- initiator
803
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 241, S: this });
804
- },
805
- onClosed: (err) => {
806
- const logMeta = {
807
- topic: this.topic,
808
- peerId: this.localInfo,
809
- remoteId: this.remoteInfo,
810
- initiator
811
- };
812
- log3("connection closed", logMeta, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 256, S: this });
813
- this._connectionLimiter.doneConnecting(sessionId);
814
- invariant3(this.connection === connection, "Connection mismatch (race condition).", { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 259, S: this, A: ["this.connection === connection", "'Connection mismatch (race condition).'"] });
815
- log3.trace("dxos.mesh.connection.closed", {
816
- topic: this.topic,
817
- localPeerId: this.localInfo,
818
- remotePeerId: this.remoteInfo,
819
- sessionId,
820
- initiator
821
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 260, S: this });
822
- if (err instanceof ConnectionDisplacedError) {
823
- this.connectionDisplaced.emit(this.connection);
824
- } else {
825
- if (this._lastConnectionTime && this._lastConnectionTime + CONNECTION_COUNTS_STABLE_AFTER < Date.now()) {
826
- this._availableAfter = 0;
827
- } else {
828
- this.availableToConnect = false;
829
- this._availableAfter = increaseInterval(this._availableAfter);
830
- }
831
- this._callbacks.onDisconnected();
832
- scheduleTask2(this._connectionCtx, () => {
833
- log3("peer became available", logMeta, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 279, S: this });
834
- this.availableToConnect = true;
835
- this._callbacks.onPeerAvailable();
836
- }, this._availableAfter);
837
- }
838
- this.connection = void 0;
839
- }
840
- }
841
- );
842
- this._callbacks.onInitiated(connection);
843
- void this._connectionCtx?.dispose();
844
- this._connectionCtx = this._ctx.derive();
845
- connection.errors.handle((err) => {
846
- log3.info("connection error, closing", {
847
- topic: this.topic,
848
- peerId: this.localInfo,
849
- remoteId: this.remoteInfo,
850
- initiator,
851
- err
852
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 291, S: this });
853
- log3.trace("dxos.mesh.connection.error", {
854
- topic: this.topic,
855
- localPeerId: this.localInfo,
856
- remotePeerId: this.remoteInfo,
857
- sessionId,
858
- initiator,
859
- err
860
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 298, S: this });
861
- void this.closeConnection(err);
862
- });
863
- this.connection = connection;
864
- return connection;
865
- }
866
- async closeConnection(err) {
867
- if (!this.connection) {
868
- return;
869
- }
870
- const connection = this.connection;
871
- log3("closing...", {
872
- peerId: this.remoteInfo,
873
- sessionId: connection.sessionId
874
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 317, S: this });
875
- await connection.close({
876
- error: err
877
- });
878
- log3("closed", {
879
- peerId: this.remoteInfo,
880
- sessionId: connection.sessionId
881
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 326, S: this });
882
- }
883
- async onSignal(ctx, message) {
884
- if (!this.connection) {
885
- log3("dropping signal message for non-existent connection", {
886
- message
887
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 333, S: this });
888
- return;
889
- }
890
- await this.connection.signal(ctx, message);
891
- }
892
- async safeDestroy(reason) {
893
- await this._ctx.dispose();
894
- log3("Destroying peer", {
895
- peerId: this.remoteInfo,
896
- topic: this.topic
897
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 342, S: this });
898
- await this?.connection?.close({
899
- reason
900
- });
901
- }
902
- };
903
- _ts_decorate2([
904
- synchronized2
905
- ], Peer.prototype, "safeDestroy", null);
906
- var increaseInterval = (interval) => {
907
- if (interval === 0) {
908
- return 50;
909
- } else if (interval < 500) {
910
- return 500;
911
- } else if (interval < 1e3) {
912
- return 1e3;
913
- } else if (interval < 5e3) {
914
- return 5e3;
915
- }
916
- return 1e4;
917
- };
918
-
919
- // src/swarm/swarm.ts
920
- var __dxlog_file4 = "/__w/dxos/dxos/packages/core/mesh/network-manager/src/swarm/swarm.ts";
921
- function _ts_decorate3(decorators, target, key, desc) {
922
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
923
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
924
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
925
- return c > 3 && r && Object.defineProperty(target, key, r), r;
926
- }
927
- var INITIATION_DELAY = 100;
928
- var getClassName = (obj) => Object.getPrototypeOf(obj).constructor.name;
929
- var Swarm = class {
930
- _topic;
931
- _ownPeer;
932
- _topology;
933
- _protocolProvider;
934
- _messenger;
935
- _transportFactory;
936
- _label;
937
- _connectionLimiter;
938
- _initiationDelay;
939
- _swarmMessenger;
940
- _ctx = new Context3(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 39 });
941
- _listeningHandle = void 0;
942
- /**
943
- * PeerInfo -> Peer.
944
- * @internal
945
- */
946
- _peers = new ComplexMap2(PeerInfoHash);
947
- /**
948
- * Unique id of the swarm, local to the current peer, generated when swarm is joined.
949
- */
950
- _instanceId = PublicKey3.random().toHex();
951
- /**
952
- * New connection to a peer is started.
953
- * @internal
954
- */
955
- connectionAdded = new Event3();
956
- /**
957
- * Connection to a peer is dropped.
958
- * @internal
959
- */
960
- disconnected = new Event3();
961
- /**
962
- * Connection is established to a new peer.
963
- * @internal
964
- */
965
- connected = new Event3();
966
- errors = new ErrorStream2();
967
- // TODO(burdon): Swarm => Peer.create/destroy =< Connection.open/close
968
- // TODO(burdon): Pass in object.
969
- constructor(_topic, _ownPeer, _topology, _protocolProvider, _messenger, _transportFactory, _label, _connectionLimiter, _initiationDelay = INITIATION_DELAY) {
970
- this._topic = _topic;
971
- this._ownPeer = _ownPeer;
972
- this._topology = _topology;
973
- this._protocolProvider = _protocolProvider;
974
- this._messenger = _messenger;
975
- this._transportFactory = _transportFactory;
976
- this._label = _label;
977
- this._connectionLimiter = _connectionLimiter;
978
- this._initiationDelay = _initiationDelay;
979
- log4("creating swarm", {
980
- topic: this._topic.toHex(),
981
- peer: this._ownPeer
982
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 73, S: this });
983
- _topology.init(this._getSwarmController());
984
- this._swarmMessenger = new SwarmMessenger({
985
- sendMessage: async (ctx, msg) => await this._messenger.sendMessage(ctx, msg),
986
- onSignal: async (ctx, msg) => await this.onSignal(ctx, msg),
987
- onOffer: async (ctx, msg) => await this.onOffer(ctx, msg),
988
- topic: this._topic
989
- });
990
- }
991
- get connections() {
992
- return Array.from(this._peers.values()).map((peer) => peer.connection).filter(isNonNullable);
993
- }
994
- get ownPeerId() {
995
- return PublicKey3.from(this._ownPeer.peerKey);
996
- }
997
- get ownPeer() {
998
- return this._ownPeer;
999
- }
1000
- /**
1001
- * Custom label assigned to this swarm. Used in devtools to display human-readable names for swarms.
1002
- */
1003
- get label() {
1004
- return this._label;
1005
- }
1006
- get topic() {
1007
- return this._topic;
1008
- }
1009
- async open() {
1010
- invariant4(!this._listeningHandle, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 103, S: this, A: ["!this._listeningHandle", ""] });
1011
- this._listeningHandle = await this._messenger.listen({
1012
- peer: this._ownPeer,
1013
- payloadType: "dxos.mesh.swarm.SwarmMessage",
1014
- onMessage: async (message) => {
1015
- await this._swarmMessenger.receiveMessage(this._ctx, message).catch((err) => log4.info("Error while receiving message", {
1016
- err
1017
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 109, S: this }));
1018
- }
1019
- });
1020
- }
1021
- async destroy() {
1022
- log4("destroying...", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 116, S: this });
1023
- await this._listeningHandle?.unsubscribe();
1024
- this._listeningHandle = void 0;
1025
- await this._ctx.dispose();
1026
- await this._topology.destroy();
1027
- await Promise.all(Array.from(this._peers.keys()).map((key) => this._destroyPeer(key, "swarm destroyed")));
1028
- log4("destroyed", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 122, S: this });
1029
- }
1030
- async setTopology(topology) {
1031
- invariant4(!this._ctx.disposed, "Swarm is offline", { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 125, S: this, A: ["!this._ctx.disposed", "'Swarm is offline'"] });
1032
- if (topology === this._topology) {
1033
- return;
1034
- }
1035
- log4("setting topology", {
1036
- previous: getClassName(this._topology),
1037
- topology: getClassName(topology)
1038
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 129, S: this });
1039
- await this._topology.destroy();
1040
- this._topology = topology;
1041
- this._topology.init(this._getSwarmController());
1042
- this._topology.update();
1043
- }
1044
- async onSwarmEvent(swarmEvent) {
1045
- log4("swarm event", {
1046
- swarmEvent
1047
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 139, S: this });
1048
- if (this._ctx.disposed) {
1049
- log4("swarm event ignored for disposed swarm", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 143, S: this });
1050
- return;
1051
- }
1052
- if (swarmEvent.peerAvailable) {
1053
- const peerId = swarmEvent.peerAvailable.peer.peerKey;
1054
- if (peerId !== this._ownPeer.peerKey) {
1055
- log4("new peer", {
1056
- peerId
1057
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 149, S: this });
1058
- const peer = this._getOrCreatePeer(swarmEvent.peerAvailable.peer);
1059
- peer.advertizing = true;
1060
- }
1061
- } else if (swarmEvent.peerLeft) {
1062
- const peer = this._peers.get(swarmEvent.peerLeft.peer);
1063
- if (peer) {
1064
- peer.advertizing = false;
1065
- if (this._isConnectionEstablishmentInProgress(peer)) {
1066
- log4(`destroying peer, state: ${peer.connection?.state}`, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 162, S: this });
1067
- void this._destroyPeer(swarmEvent.peerLeft.peer, "peer left").catch((err) => log4.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 163, S: this }));
1068
- }
1069
- } else {
1070
- log4("received peerLeft but no peer found", {
1071
- peer: swarmEvent.peerLeft.peer.peerKey
1072
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 166, S: this });
1073
- }
1074
- }
1075
- this._topology.update();
1076
- }
1077
- async onOffer(ctx, message) {
1078
- log4("offer", {
1079
- message
1080
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 174, S: this });
1081
- if (this._ctx.disposed) {
1082
- log4("ignored for disposed swarm", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 178, S: this });
1083
- return {
1084
- accept: false
1085
- };
1086
- }
1087
- invariant4(message.author, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 184, S: this, A: ["message.author", ""] });
1088
- if (message.recipient.peerKey !== this._ownPeer.peerKey) {
1089
- log4("rejecting offer with incorrect peerId", {
1090
- message
1091
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 186, S: this });
1092
- return {
1093
- accept: false
1094
- };
1095
- }
1096
- if (!message.topic?.equals(this._topic)) {
1097
- log4("rejecting offer with incorrect topic", {
1098
- message
1099
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 194, S: this });
1100
- return {
1101
- accept: false
1102
- };
1103
- }
1104
- const peer = this._getOfferSenderPeer(message.author);
1105
- const answer = await peer.onOffer(ctx, message);
1106
- this._topology.update();
1107
- return answer;
1108
- }
1109
- _getOfferSenderPeer(senderInfo) {
1110
- const peer = this._getOrCreatePeer(senderInfo);
1111
- const connectionState = peer.connection?.state;
1112
- if (connectionState === ConnectionState.CLOSING || connectionState === ConnectionState.ABORTING) {
1113
- this._peers.delete(peer.remoteInfo);
1114
- this.disconnected.emit(peer.remoteInfo);
1115
- return this._getOrCreatePeer(peer.remoteInfo);
1116
- }
1117
- return peer;
1118
- }
1119
- async onSignal(ctx, message) {
1120
- log4("signal", {
1121
- message
1122
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 218, S: this });
1123
- if (this._ctx.disposed) {
1124
- log4.info("ignored for offline swarm", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 222, S: this });
1125
- return;
1126
- }
1127
- invariant4(message.recipient.peerKey === this._ownPeer.peerKey, `Invalid signal peer id expected=${this.ownPeerId}, actual=${message.recipient}`, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 225, S: this, A: ["message.recipient.peerKey === this._ownPeer.peerKey", "`Invalid signal peer id expected=${this.ownPeerId}, actual=${message.recipient}`"] });
1128
- invariant4(message.topic?.equals(this._topic), void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 226, S: this, A: ["message.topic?.equals(this._topic)", ""] });
1129
- invariant4(message.author, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 227, S: this, A: ["message.author", ""] });
1130
- const peer = this._getOrCreatePeer(message.author);
1131
- await peer.onSignal(ctx, message);
1132
- }
1133
- // For debug purposes
1134
- async goOffline() {
1135
- await this._ctx.dispose();
1136
- await Promise.all([
1137
- ...this._peers.keys()
1138
- ].map((peerId) => this._destroyPeer(peerId, "goOffline")));
1139
- }
1140
- // For debug purposes
1141
- async goOnline() {
1142
- this._ctx = new Context3(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 240 });
1143
- }
1144
- _getOrCreatePeer(peerInfo) {
1145
- invariant4(peerInfo.peerKey, "PeerInfo.peerKey is required", { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 243, S: this, A: ["peerInfo.peerKey", "'PeerInfo.peerKey is required'"] });
1146
- let peer = this._peers.get(peerInfo);
1147
- if (!peer) {
1148
- peer = new Peer(peerInfo, this._topic, this._ownPeer, this._swarmMessenger, this._protocolProvider, this._transportFactory, this._connectionLimiter, {
1149
- onInitiated: (connection) => {
1150
- this.connectionAdded.emit(connection);
1151
- },
1152
- onConnected: () => {
1153
- this.connected.emit(peerInfo);
1154
- },
1155
- onDisconnected: async () => {
1156
- if (this._isUnregistered(peer)) {
1157
- log4.verbose("ignored onDisconnected for unregistered peer", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 255, S: this });
1158
- return;
1159
- }
1160
- if (!peer.advertizing) {
1161
- await this._destroyPeer(peerInfo, "peer disconnected");
1162
- }
1163
- this.disconnected.emit(peerInfo);
1164
- this._topology.update();
1165
- },
1166
- onRejected: () => {
1167
- if (!this._isUnregistered(peer)) {
1168
- log4("peer rejected connection", {
1169
- peerInfo
1170
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 268, S: this });
1171
- void this._destroyPeer(peerInfo, "peer rejected connection");
1172
- }
1173
- },
1174
- onAccepted: () => {
1175
- this._topology.update();
1176
- },
1177
- onOffer: (remoteId) => {
1178
- return this._topology.onOffer(PublicKey3.from(remoteId.peerKey));
1179
- },
1180
- onPeerAvailable: () => {
1181
- this._topology.update();
1182
- }
1183
- });
1184
- this._peers.set(peerInfo, peer);
1185
- }
1186
- return peer;
1187
- }
1188
- async _destroyPeer(peerInfo, reason) {
1189
- log4("destroy peer", {
1190
- peerKey: peerInfo.peerKey,
1191
- reason
1192
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 289, S: this });
1193
- const peer = this._peers.get(peerInfo);
1194
- invariant4(peer, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 294, S: this, A: ["peer", ""] });
1195
- this._peers.delete(peerInfo);
1196
- await peer.safeDestroy(reason);
1197
- }
1198
- _getSwarmController() {
1199
- return {
1200
- getState: () => ({
1201
- ownPeerId: PublicKey3.from(this._ownPeer.peerKey),
1202
- connected: Array.from(this._peers.entries()).filter(([_, peer]) => peer.connection).map(([info]) => PublicKey3.from(info.peerKey)),
1203
- candidates: Array.from(this._peers.entries()).filter(([_, peer]) => !peer.connection && peer.advertizing && peer.availableToConnect).map(([info]) => PublicKey3.from(info.peerKey)),
1204
- allPeers: Array.from(this._peers.keys()).map((info) => PublicKey3.from(info.peerKey))
1205
- }),
1206
- connect: (peer) => {
1207
- if (this._ctx.disposed) {
1208
- return;
1209
- }
1210
- scheduleTask3(this._ctx, async () => {
1211
- try {
1212
- await this._initiateConnection({
1213
- peerKey: peer.toHex()
1214
- });
1215
- } catch (err) {
1216
- log4("initiation error", err, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 317, S: this });
1217
- }
1218
- });
1219
- },
1220
- disconnect: async (peer) => {
1221
- if (this._ctx.disposed) {
1222
- return;
1223
- }
1224
- scheduleTask3(this._ctx, async () => {
1225
- await this._closeConnection({
1226
- peerKey: peer.toHex()
1227
- });
1228
- this._topology.update();
1229
- });
1230
- }
1231
- };
1232
- }
1233
- /**
1234
- * Creates a connection then sends message over signal network.
1235
- */
1236
- async _initiateConnection(remotePeer) {
1237
- const ctx = this._ctx;
1238
- const peer = this._getOrCreatePeer(remotePeer);
1239
- if (remotePeer.peerKey < this._ownPeer.peerKey) {
1240
- log4("initiation delay", {
1241
- remotePeer
1242
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 343, S: this });
1243
- await sleep2(this._initiationDelay);
1244
- }
1245
- if (ctx.disposed) {
1246
- return;
1247
- }
1248
- if (this._isUnregistered(peer)) {
1249
- throw new Error("Peer left during initiation delay");
1250
- }
1251
- if (peer.connection) {
1252
- return;
1253
- }
1254
- log4("initiating connection...", {
1255
- remotePeer
1256
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 358, S: this });
1257
- await peer.initiateConnection(ctx);
1258
- this._topology.update();
1259
- log4("initiated", {
1260
- remotePeer
1261
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 363, S: this });
1262
- }
1263
- async _closeConnection(peerInfo) {
1264
- const peer = this._peers.get(peerInfo);
1265
- if (!peer) {
1266
- return;
1267
- }
1268
- await peer.closeConnection();
1269
- }
1270
- _isConnectionEstablishmentInProgress(peer) {
1271
- if (!peer.connection) {
1272
- return true;
1273
- }
1274
- return [
1275
- ConnectionState.INITIAL,
1276
- ConnectionState.CREATED,
1277
- ConnectionState.CONNECTING
1278
- ].includes(peer.connection.state);
1279
- }
1280
- _isUnregistered(peer) {
1281
- return !peer || this._peers.get(peer.remoteInfo) !== peer;
1282
- }
1283
- };
1284
- _ts_decorate3([
1285
- logInfo2
1286
- ], Swarm.prototype, "_instanceId", void 0);
1287
- _ts_decorate3([
1288
- logInfo2
1289
- ], Swarm.prototype, "ownPeer", null);
1290
- _ts_decorate3([
1291
- logInfo2
1292
- ], Swarm.prototype, "topic", null);
1293
- _ts_decorate3([
1294
- synchronized3
1295
- ], Swarm.prototype, "onSwarmEvent", null);
1296
- _ts_decorate3([
1297
- synchronized3
1298
- ], Swarm.prototype, "onOffer", null);
1299
- _ts_decorate3([
1300
- synchronized3
1301
- ], Swarm.prototype, "goOffline", null);
1302
- _ts_decorate3([
1303
- synchronized3
1304
- ], Swarm.prototype, "goOnline", null);
1305
-
1306
- // src/swarm/swarm-mapper.ts
1307
- import { Event as Event4, SubscriptionList } from "@dxos/async";
1308
- import { PublicKey as PublicKey4 } from "@dxos/keys";
1309
- import { log as log5 } from "@dxos/log";
1310
- import { PeerInfoHash as PeerInfoHash2 } from "@dxos/messaging";
1311
- import { ComplexMap as ComplexMap3 } from "@dxos/util";
1312
- var __dxlog_file5 = "/__w/dxos/dxos/packages/core/mesh/network-manager/src/swarm/swarm-mapper.ts";
1313
- var SwarmMapper = class {
1314
- _swarm;
1315
- _subscriptions = new SubscriptionList();
1316
- _connectionSubscriptions = new ComplexMap3(PeerInfoHash2);
1317
- _peers = new ComplexMap3(PeerInfoHash2);
1318
- mapUpdated = new Event4();
1319
- get peers() {
1320
- return Array.from(this._peers.values());
1321
- }
1322
- constructor(_swarm) {
1323
- this._swarm = _swarm;
1324
- this._subscriptions.add(_swarm.connectionAdded.on((connection) => {
1325
- this._update();
1326
- this._connectionSubscriptions.set(connection.remoteInfo, connection.stateChanged.on(() => {
1327
- this._update();
1328
- }));
1329
- }));
1330
- this._subscriptions.add(_swarm.disconnected.on((peerId) => {
1331
- this._connectionSubscriptions.get(peerId)?.();
1332
- this._connectionSubscriptions.delete(peerId);
1333
- this._update();
1334
- }));
1335
- this._update();
1336
- }
1337
- _update() {
1338
- log5("updating swarm", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 40, S: this });
1339
- this._peers.clear();
1340
- this._peers.set(this._swarm.ownPeer, {
1341
- id: this._swarm.ownPeerId,
1342
- state: "ME",
1343
- connections: []
1344
- });
1345
- for (const connection of this._swarm.connections) {
1346
- this._peers.set(connection.remoteInfo, {
1347
- id: PublicKey4.from(connection.remoteInfo.peerKey),
1348
- state: connection.state,
1349
- connections: [
1350
- this._swarm.ownPeerId
1351
- ]
1352
- });
1353
- }
1354
- log5("graph changed", {
1355
- directConnections: this._swarm.connections.length,
1356
- totalPeersInSwarm: this._peers.size
1357
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 77, S: this });
1358
- this.mapUpdated.emit(Array.from(this._peers.values()));
1359
- }
1360
- // TODO(burdon): Async open/close.
1361
- destroy() {
1362
- Array.from(this._connectionSubscriptions.values()).forEach((cb) => cb());
1363
- this._connectionSubscriptions.clear();
1364
- this._subscriptions.clear();
1365
- }
1366
- };
1367
-
1368
- // src/swarm/connection-limiter.ts
1369
- import { DeferredTask as DeferredTask2 } from "@dxos/async";
1370
- import { Context as Context4 } from "@dxos/context";
1371
- import { invariant as invariant5 } from "@dxos/invariant";
1372
- import { PublicKey as PublicKey5 } from "@dxos/keys";
1373
- import { log as log6 } from "@dxos/log";
1374
- import { CancelledError as CancelledError3 } from "@dxos/protocols";
1375
- import { ComplexMap as ComplexMap4 } from "@dxos/util";
1376
- var __dxlog_file6 = "/__w/dxos/dxos/packages/core/mesh/network-manager/src/swarm/connection-limiter.ts";
1377
- var MAX_CONCURRENT_INITIATING_CONNECTIONS = 50;
1378
- var ConnectionLimiter = class {
1379
- _ctx = new Context4(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 15 });
1380
- _maxConcurrentInitConnections;
1381
- /**
1382
- * Queue of promises to resolve when initiating connections amount is below the limit.
1383
- */
1384
- _waitingPromises = new ComplexMap4(PublicKey5.hash);
1385
- resolveWaitingPromises = new DeferredTask2(this._ctx, async () => {
1386
- Array.from(this._waitingPromises.values()).slice(0, this._maxConcurrentInitConnections).forEach(({ resolve }) => {
1387
- resolve();
1388
- });
1389
- });
1390
- constructor({ maxConcurrentInitConnections = MAX_CONCURRENT_INITIATING_CONNECTIONS } = {}) {
1391
- this._maxConcurrentInitConnections = maxConcurrentInitConnections;
1392
- }
1393
- /**
1394
- * @returns Promise that resolves in queue when connections amount with 'CONNECTING' state is below the limit.
1395
- */
1396
- async connecting(sessionId) {
1397
- invariant5(!this._waitingPromises.has(sessionId), "Peer is already waiting for connection", { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 31, S: this, A: ["!this._waitingPromises.has(sessionId)", "'Peer is already waiting for connection'"] });
1398
- log6("waiting", {
1399
- sessionId
1400
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 32, S: this });
1401
- await new Promise((resolve, reject) => {
1402
- this._waitingPromises.set(sessionId, {
1403
- resolve,
1404
- reject
1405
- });
1406
- this.resolveWaitingPromises.schedule();
1407
- });
1408
- log6("allow", {
1409
- sessionId
1410
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 42, S: this });
1411
- }
1412
- /**
1413
- * Rejects promise returned by `connecting` method.
1414
- */
1415
- doneConnecting(sessionId) {
1416
- log6("done", {
1417
- sessionId
1418
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 49, S: this });
1419
- if (!this._waitingPromises.has(sessionId)) {
1420
- return;
1421
- }
1422
- this._waitingPromises.get(sessionId).reject(new CancelledError3());
1423
- this._waitingPromises.delete(sessionId);
1424
- this.resolveWaitingPromises.schedule();
1425
- }
1426
- };
1427
-
1428
- // src/connection-log.ts
1429
- import { Event as Event5 } from "@dxos/async";
1430
- import { raise } from "@dxos/debug";
1431
- import { PublicKey as PublicKey6 } from "@dxos/keys";
1432
- import { ComplexMap as ComplexMap5 } from "@dxos/util";
1433
- var CONNECTION_GC_THRESHOLD = 1e3 * 60 * 15;
1434
- var EventType = /* @__PURE__ */ (function(EventType2) {
1435
- EventType2["CONNECTION_STATE_CHANGED"] = "CONNECTION_STATE_CHANGED";
1436
- EventType2["PROTOCOL_ERROR"] = "PROTOCOL_ERROR";
1437
- EventType2["PROTOCOL_EXTENSIONS_INITIALIZED"] = "PROTOCOL_EXTENSIONS_INITIALIZED";
1438
- EventType2["PROTOCOL_EXTENSIONS_HANDSHAKE"] = "PROTOCOL_EXTENSIONS_HANDSHAKE";
1439
- EventType2["PROTOCOL_HANDSHAKE"] = "PROTOCOL_HANDSHAKE";
1440
- return EventType2;
1441
- })({});
1442
- var ConnectionLog = class {
1443
- /**
1444
- * SwarmId => info
1445
- */
1446
- _swarms = new ComplexMap5(PublicKey6.hash);
1447
- update = new Event5();
1448
- getSwarmInfo(swarmId) {
1449
- return this._swarms.get(swarmId) ?? raise(new Error(`Swarm not found: ${swarmId}`));
1450
- }
1451
- get swarms() {
1452
- return Array.from(this._swarms.values());
1453
- }
1454
- joinedSwarm(swarm) {
1455
- const info = {
1456
- id: PublicKey6.from(swarm._instanceId),
1457
- topic: swarm.topic,
1458
- isActive: true,
1459
- label: swarm.label,
1460
- connections: []
1461
- };
1462
- this._swarms.set(PublicKey6.from(swarm._instanceId), info);
1463
- this.update.emit();
1464
- swarm.connectionAdded.on((connection) => {
1465
- const connectionInfo = {
1466
- state: ConnectionState.CREATED,
1467
- closeReason: connection.closeReason,
1468
- remotePeerId: PublicKey6.from(connection.remoteInfo.peerKey),
1469
- sessionId: connection.sessionId,
1470
- transport: connection.transport && Object.getPrototypeOf(connection.transport).constructor.name,
1471
- protocolExtensions: [],
1472
- events: [],
1473
- lastUpdate: /* @__PURE__ */ new Date()
1474
- };
1475
- info.connections.push(connectionInfo);
1476
- this.update.emit();
1477
- connection.stateChanged.on(async (state) => {
1478
- connectionInfo.state = state;
1479
- connectionInfo.closeReason = connection.closeReason;
1480
- connectionInfo.lastUpdate = /* @__PURE__ */ new Date();
1481
- connectionInfo.events.push({
1482
- type: "CONNECTION_STATE_CHANGED",
1483
- newState: state
1484
- });
1485
- if (state === ConnectionState.CONNECTED) {
1486
- const details = await connection.transport?.getDetails();
1487
- connectionInfo.transportDetails = details;
1488
- }
1489
- this.update.emit();
1490
- });
1491
- connection.protocol?.stats?.on((stats) => {
1492
- connectionInfo.readBufferSize = stats.readBufferSize;
1493
- connectionInfo.writeBufferSize = stats.writeBufferSize;
1494
- connectionInfo.streams = stats.channels;
1495
- connectionInfo.lastUpdate = /* @__PURE__ */ new Date();
1496
- this.update.emit();
1497
- });
1498
- connection.transportStats?.on((stats) => {
1499
- connectionInfo.transportBytesSent = stats.bytesSent;
1500
- connectionInfo.transportBytesReceived = stats.bytesReceived;
1501
- connectionInfo.transportPacketsSent = stats.packetsSent;
1502
- connectionInfo.transportPacketsReceived = stats.packetsReceived;
1503
- });
1504
- gcSwarm(info);
1505
- });
1506
- }
1507
- leftSwarm(swarm) {
1508
- this.getSwarmInfo(PublicKey6.from(swarm._instanceId)).isActive = false;
1509
- this.update.emit();
1510
- }
1511
- };
1512
- var gcSwarm = (swarm) => {
1513
- swarm.connections = swarm.connections?.filter((connection) => {
1514
- return connection.lastUpdate ? Date.now() - connection.lastUpdate.getTime() < CONNECTION_GC_THRESHOLD : true;
1515
- });
1516
- };
1517
-
1518
- // src/network-manager.ts
1519
- import { Event as Event6, synchronized as synchronized4 } from "@dxos/async";
1520
- import { assertArgument, invariant as invariant6 } from "@dxos/invariant";
1521
- import { PublicKey as PublicKey7 } from "@dxos/keys";
1522
- import { log as log7 } from "@dxos/log";
1523
- import { Messenger } from "@dxos/messaging";
1524
- import { ConnectionState as ConnectionState2 } from "@dxos/protocols/proto/dxos/client/services";
1525
- import { ComplexMap as ComplexMap6 } from "@dxos/util";
1526
- var __dxlog_file7 = "/__w/dxos/dxos/packages/core/mesh/network-manager/src/network-manager.ts";
1527
- function _ts_decorate4(decorators, target, key, desc) {
1528
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1529
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1530
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1531
- return c > 3 && r && Object.defineProperty(target, key, r), r;
1532
- }
1533
- var SwarmNetworkManager = class {
1534
- /**
1535
- * @internal
1536
- */
1537
- _swarms = new ComplexMap6(PublicKey7.hash);
1538
- _mappers = new ComplexMap6(PublicKey7.hash);
1539
- _transportFactory;
1540
- _signalManager;
1541
- _messenger;
1542
- _signalConnection;
1543
- _connectionLimiter;
1544
- _connectionLog;
1545
- _peerInfo = void 0;
1546
- _connectionState = ConnectionState2.ONLINE;
1547
- connectionStateChanged = new Event6();
1548
- topicsUpdated = new Event6();
1549
- constructor({ transportFactory, signalManager, enableDevtoolsLogging, peerInfo }) {
1550
- this._transportFactory = transportFactory;
1551
- this._signalManager = signalManager;
1552
- this._signalManager.swarmEvent.on((event) => this._swarms.get(event.topic)?.onSwarmEvent(event));
1553
- this._messenger = new Messenger({
1554
- signalManager: this._signalManager
1555
- });
1556
- this._signalConnection = {
1557
- join: (ctx, opts) => this._signalManager.join(ctx, opts),
1558
- leave: (ctx, opts) => this._signalManager.leave(ctx, opts)
1559
- };
1560
- this._peerInfo = peerInfo;
1561
- this._connectionLimiter = new ConnectionLimiter();
1562
- if (enableDevtoolsLogging) {
1563
- this._connectionLog = new ConnectionLog();
1564
- }
1565
- }
1566
- // TODO(burdon): Remove access (Devtools only).
1567
- get connectionLog() {
1568
- return this._connectionLog;
1569
- }
1570
- get connectionState() {
1571
- return this._connectionState;
1572
- }
1573
- // TODO(burdon): Reconcile with "discovery_key".
1574
- get topics() {
1575
- return Array.from(this._swarms.keys());
1576
- }
1577
- getSwarmMap(topic) {
1578
- return this._mappers.get(topic);
1579
- }
1580
- getSwarm(topic) {
1581
- return this._swarms.get(topic);
1582
- }
1583
- setPeerInfo(peerInfo) {
1584
- this._peerInfo = peerInfo;
1585
- }
1586
- async open() {
1587
- log7("opening network manager", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 76, S: this });
1588
- await this._messenger.open();
1589
- await this._signalManager.open();
1590
- log7("opened network manager", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 79, S: this });
1591
- }
1592
- async close(ctx) {
1593
- for (const topic of this._swarms.keys()) {
1594
- await this.leaveSwarm(ctx, topic).catch((err) => {
1595
- log7(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 84, S: this });
1596
- });
1597
- }
1598
- await this._messenger.close();
1599
- await this._signalManager.close();
1600
- }
1601
- /**
1602
- * Join the swarm.
1603
- */
1604
- async joinSwarm(ctx, { topic, topology, protocolProvider: protocol, label }) {
1605
- assertArgument(PublicKey7.isPublicKey(topic), "topic");
1606
- invariant6(topology, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 94, S: this, A: ["topology", ""] });
1607
- invariant6(this._peerInfo, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 95, S: this, A: ["this._peerInfo", ""] });
1608
- assertArgument(typeof protocol === "function", "protocolProvider");
1609
- if (this._swarms.has(topic)) {
1610
- throw new Error(`Already connected to swarm: ${PublicKey7.from(topic)}`);
1611
- }
1612
- log7("joining", {
1613
- topic: PublicKey7.from(topic),
1614
- peerInfo: this._peerInfo,
1615
- topology: topology.toString()
1616
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 100, S: this });
1617
- const swarm = new Swarm(topic, this._peerInfo, topology, protocol, this._messenger, this._transportFactory, label, this._connectionLimiter);
1618
- swarm.errors.handle((error) => {
1619
- log7("swarm error", {
1620
- error
1621
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 107, S: this });
1622
- });
1623
- this._swarms.set(topic, swarm);
1624
- this._mappers.set(topic, new SwarmMapper(swarm));
1625
- await swarm.open();
1626
- this._signalConnection.join(ctx, {
1627
- topic,
1628
- peer: this._peerInfo
1629
- }).catch((error) => log7.catch(error, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 118, S: this }));
1630
- this.topicsUpdated.emit();
1631
- this._connectionLog?.joinedSwarm(swarm);
1632
- log7("joined", {
1633
- topic: PublicKey7.from(topic),
1634
- count: this._swarms.size
1635
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 121, S: this });
1636
- return {
1637
- close: (ctx2) => this.leaveSwarm(ctx2, topic)
1638
- };
1639
- }
1640
- /**
1641
- * Close the connection.
1642
- */
1643
- async leaveSwarm(ctx, topic) {
1644
- if (!this._swarms.has(topic)) {
1645
- return;
1646
- }
1647
- log7("leaving", {
1648
- topic: PublicKey7.from(topic)
1649
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 136, S: this });
1650
- const swarm = this._swarms.get(topic);
1651
- await this._signalConnection.leave(ctx, {
1652
- topic,
1653
- peer: swarm.ownPeer
1654
- });
1655
- const map = this._mappers.get(topic);
1656
- map.destroy();
1657
- this._mappers.delete(topic);
1658
- this._connectionLog?.leftSwarm(swarm);
1659
- await swarm.destroy();
1660
- this._swarms.delete(topic);
1661
- this.topicsUpdated.emit();
1662
- log7("left", {
1663
- topic: PublicKey7.from(topic),
1664
- count: this._swarms.size
1665
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 151, S: this });
1666
- }
1667
- async setConnectionState(state) {
1668
- if (state === this._connectionState) {
1669
- return;
1670
- }
1671
- switch (state) {
1672
- case ConnectionState2.OFFLINE: {
1673
- this._connectionState = state;
1674
- await Promise.all([
1675
- ...this._swarms.values()
1676
- ].map((swarm) => swarm.goOffline()));
1677
- await this._messenger.close();
1678
- await this._signalManager.close();
1679
- break;
1680
- }
1681
- case ConnectionState2.ONLINE: {
1682
- this._connectionState = state;
1683
- this._messenger.open();
1684
- await Promise.all([
1685
- ...this._swarms.values()
1686
- ].map((swarm) => swarm.goOnline()));
1687
- await this._signalManager.open();
1688
- break;
1689
- }
1690
- }
1691
- this.connectionStateChanged.emit(this._connectionState);
1692
- }
1693
- };
1694
- _ts_decorate4([
1695
- synchronized4
1696
- ], SwarmNetworkManager.prototype, "joinSwarm", null);
1697
- _ts_decorate4([
1698
- synchronized4
1699
- ], SwarmNetworkManager.prototype, "leaveSwarm", null);
1700
-
1701
- // src/topology/fully-connected-topology.ts
1702
- import { invariant as invariant7 } from "@dxos/invariant";
1703
- var __dxlog_file8 = "/__w/dxos/dxos/packages/core/mesh/network-manager/src/topology/fully-connected-topology.ts";
1704
- var FullyConnectedTopology = class {
1705
- _controller;
1706
- toString() {
1707
- return "FullyConnectedTopology";
1708
- }
1709
- init(controller) {
1710
- invariant7(!this._controller, "Already initialized", { "~LogMeta": "~LogMeta", F: __dxlog_file8, L: 11, S: this, A: ["!this._controller", "'Already initialized'"] });
1711
- this._controller = controller;
1712
- }
1713
- update() {
1714
- invariant7(this._controller, "Not initialized", { "~LogMeta": "~LogMeta", F: __dxlog_file8, L: 15, S: this, A: ["this._controller", "'Not initialized'"] });
1715
- const { candidates: discovered } = this._controller.getState();
1716
- for (const peer of discovered) {
1717
- this._controller.connect(peer);
1718
- }
1719
- }
1720
- async onOffer(peer) {
1721
- return true;
1722
- }
1723
- async destroy() {
1724
- }
1725
- };
1726
-
1727
- // src/transport/memory-transport.ts
1728
- import { Transform } from "@dxos/node-std/stream";
1729
- import { Event as Event7, Trigger as Trigger2 } from "@dxos/async";
1730
- import { ErrorStream as ErrorStream3 } from "@dxos/debug";
1731
- import { invariant as invariant8 } from "@dxos/invariant";
1732
- import { PublicKey as PublicKey8 } from "@dxos/keys";
1733
- import { log as log8, logInfo as logInfo3 } from "@dxos/log";
1734
- import { ComplexMap as ComplexMap7 } from "@dxos/util";
1735
- var __dxlog_file9 = "/__w/dxos/dxos/packages/core/mesh/network-manager/src/transport/memory-transport.ts";
1736
- function _ts_decorate5(decorators, target, key, desc) {
1737
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1738
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1739
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1740
- return c > 3 && r && Object.defineProperty(target, key, r), r;
1741
- }
1742
- var MEMORY_TRANSPORT_DELAY = 1;
1743
- var createStreamDelay = (delay) => {
1744
- return new Transform({
1745
- objectMode: true,
1746
- transform: (chunk, _, cb) => {
1747
- setTimeout(() => cb(null, chunk), delay);
1748
- }
1749
- });
1750
- };
1751
- var MemoryTransportFactory = {
1752
- createTransport: (options) => new MemoryTransport(options)
1753
- };
1754
- var MemoryTransport = class _MemoryTransport {
1755
- _options;
1756
- // TODO(burdon): Remove static properties (inject context into constructor).
1757
- static _connections = new ComplexMap7(PublicKey8.hash);
1758
- _instanceId = PublicKey8.random();
1759
- _remote = new Trigger2();
1760
- _outgoingDelay = createStreamDelay(MEMORY_TRANSPORT_DELAY);
1761
- _incomingDelay = createStreamDelay(MEMORY_TRANSPORT_DELAY);
1762
- _closed = false;
1763
- _remoteInstanceId;
1764
- _remoteConnection;
1765
- closed = new Event7();
1766
- connected = new Event7();
1767
- errors = new ErrorStream3();
1768
- constructor(_options) {
1769
- this._options = _options;
1770
- invariant8(!_MemoryTransport._connections.has(this._instanceId), "Duplicate memory connection", { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 51, S: this, A: ["!MemoryTransport._connections.has(this._instanceId)", "'Duplicate memory connection'"] });
1771
- _MemoryTransport._connections.set(this._instanceId, this);
1772
- }
1773
- get isOpen() {
1774
- return !this._closed;
1775
- }
1776
- async open() {
1777
- log8("opening...", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 59, S: this });
1778
- if (this._options.initiator) {
1779
- log8("sending signal", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 62, S: this });
1780
- try {
1781
- await this._options.sendSignal({
1782
- payload: {
1783
- transportId: this._instanceId.toHex()
1784
- }
1785
- });
1786
- } catch (err) {
1787
- if (!this._closed) {
1788
- this.errors.raise(toError(err));
1789
- }
1790
- }
1791
- } else {
1792
- this._remote.wait({
1793
- timeout: this._options.timeout ?? 1e3
1794
- }).then((remoteId) => {
1795
- if (this._closed) {
1796
- return;
1797
- }
1798
- this._remoteInstanceId = remoteId;
1799
- this._remoteConnection = _MemoryTransport._connections.get(this._remoteInstanceId);
1800
- if (!this._remoteConnection) {
1801
- this._closed = true;
1802
- this.closed.emit();
1803
- return;
1804
- }
1805
- invariant8(!this._remoteConnection._remoteConnection, `Remote already connected: ${this._remoteInstanceId}`, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 90, S: this, A: ["!this._remoteConnection._remoteConnection", "`Remote already connected: ${this._remoteInstanceId}`"] });
1806
- this._remoteConnection._remoteConnection = this;
1807
- this._remoteConnection._remoteInstanceId = this._instanceId;
1808
- log8("connected", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 93, S: this });
1809
- this._options.stream.pipe(this._outgoingDelay).pipe(this._remoteConnection._options.stream).pipe(this._incomingDelay).pipe(this._options.stream);
1810
- this.connected.emit();
1811
- this._remoteConnection.connected.emit();
1812
- }).catch((err) => {
1813
- if (this._closed) {
1814
- return;
1815
- }
1816
- this.errors.raise(err);
1817
- });
1818
- }
1819
- return this;
1820
- }
1821
- async close() {
1822
- log8("closing...", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 107, S: this });
1823
- this._closed = true;
1824
- _MemoryTransport._connections.delete(this._instanceId);
1825
- if (this._remoteConnection) {
1826
- this._remoteConnection._closed = true;
1827
- _MemoryTransport._connections.delete(this._remoteInstanceId);
1828
- this._options.stream.unpipe(this._incomingDelay);
1829
- this._incomingDelay.unpipe(this._remoteConnection._options.stream);
1830
- this._remoteConnection._options.stream.unpipe(this._outgoingDelay);
1831
- this._outgoingDelay.unpipe(this._options.stream);
1832
- this._options.stream.unpipe(this._outgoingDelay);
1833
- this._remoteConnection.closed.emit();
1834
- this._remoteConnection._remoteConnection = void 0;
1835
- this._remoteConnection = void 0;
1836
- }
1837
- this.closed.emit();
1838
- log8("closed", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 130, S: this });
1839
- return this;
1840
- }
1841
- async onSignal({ payload }) {
1842
- log8("received signal", {
1843
- payload
1844
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 134, S: this });
1845
- if (!payload?.transportId) {
1846
- return;
1847
- }
1848
- const transportId = payload.transportId;
1849
- if (transportId) {
1850
- const remoteId = PublicKey8.fromHex(transportId);
1851
- this._remote.wake(remoteId);
1852
- }
1853
- }
1854
- async getDetails() {
1855
- return this._instanceId.toHex();
1856
- }
1857
- async getStats() {
1858
- return {
1859
- bytesSent: 0,
1860
- bytesReceived: 0,
1861
- packetsSent: 0,
1862
- packetsReceived: 0
1863
- };
1864
- }
1865
- };
1866
- _ts_decorate5([
1867
- logInfo3
1868
- ], MemoryTransport.prototype, "_instanceId", void 0);
1869
- _ts_decorate5([
1870
- logInfo3
1871
- ], MemoryTransport.prototype, "_remoteInstanceId", void 0);
1872
- var toError = (err) => err instanceof Error ? err : new Error(String(err));
1873
-
1874
- // src/transport/transport.ts
1875
- var TransportKind = /* @__PURE__ */ (function(TransportKind2) {
1876
- TransportKind2["WEB_RTC"] = "WEB-RTC";
1877
- TransportKind2["WEB_RTC_PROXY"] = "WEB-RTC_PROXY";
1878
- TransportKind2["MEMORY"] = "MEMORY";
1879
- TransportKind2["TCP"] = "TCP";
1880
- return TransportKind2;
1881
- })({});
1882
-
1883
- // src/transport/webrtc/rtc-connection-factory.ts
1884
- import { Mutex } from "@dxos/async";
1885
- var BrowserRtcConnectionFactory = class {
1886
- async initialize() {
1887
- }
1888
- async onConnectionDestroyed() {
1889
- }
1890
- async createConnection(config) {
1891
- return new RTCPeerConnection(config);
1892
- }
1893
- async initConnection(connection, info) {
1894
- }
1895
- };
1896
- var NodeRtcConnectionFactory = class _NodeRtcConnectionFactory {
1897
- static _createdConnections = 0;
1898
- static _cleanupMutex = new Mutex();
1899
- // This should be inside the function to avoid triggering `eval` in the global scope.
1900
- // eslint-disable-next-line @typescript-eslint/no-implied-eval
1901
- // TODO(burdon): Do imports here?
1902
- async initialize() {
1903
- }
1904
- async onConnectionDestroyed() {
1905
- return _NodeRtcConnectionFactory._cleanupMutex.executeSynchronized(async () => {
1906
- if (--_NodeRtcConnectionFactory._createdConnections === 0) {
1907
- (await import("#node-datachannel")).cleanup();
1908
- }
1909
- });
1910
- }
1911
- async createConnection(config) {
1912
- return _NodeRtcConnectionFactory._cleanupMutex.executeSynchronized(async () => {
1913
- const { RTCPeerConnection: RTCPeerConnection1 } = await import("#node-datachannel/polyfill");
1914
- _NodeRtcConnectionFactory._createdConnections++;
1915
- return new RTCPeerConnection1(config);
1916
- });
1917
- }
1918
- async initConnection(connection, info) {
1919
- if (info.initiator) {
1920
- connection.onnegotiationneeded?.(null);
1921
- }
1922
- }
1923
- };
1924
- var getRtcConnectionFactory = () => {
1925
- return typeof globalThis.RTCPeerConnection === "undefined" ? new NodeRtcConnectionFactory() : new BrowserRtcConnectionFactory();
1926
- };
1927
-
1928
- // src/transport/webrtc/rtc-peer-connection.ts
1929
- import { Mutex as Mutex2, Trigger as Trigger3, synchronized as synchronized5 } from "@dxos/async";
1930
- import { invariant as invariant10 } from "@dxos/invariant";
1931
- import { log as log10, logInfo as logInfo4 } from "@dxos/log";
1932
- import { ConnectivityError as ConnectivityError3 } from "@dxos/protocols";
1933
- import { trace } from "@dxos/tracing";
1934
-
1935
- // src/transport/webrtc/rtc-transport-channel.ts
1936
- import { Duplex } from "@dxos/node-std/stream";
1937
- import { Event as AsyncEvent } from "@dxos/async";
1938
- import { Resource } from "@dxos/context";
1939
- import { ErrorStream as ErrorStream4 } from "@dxos/debug";
1940
- import { invariant as invariant9 } from "@dxos/invariant";
1941
- import { log as log9 } from "@dxos/log";
1942
- import { ConnectivityError as ConnectivityError2 } from "@dxos/protocols";
1943
-
1944
- // src/transport/webrtc/rtc-transport-stats.ts
1945
- var describeSelectedRemoteCandidate = async (connection) => {
1946
- const stats = connection && await getRtcConnectionStats(connection);
1947
- const rc = stats?.remoteCandidate;
1948
- if (!rc) {
1949
- return "unavailable";
1950
- }
1951
- if (rc.candidateType === "relay") {
1952
- return `${rc.ip}:${rc.port} relay for ${rc.relatedAddress}:${rc.relatedPort}`;
1953
- }
1954
- return `${rc.ip}:${rc.port} ${rc.candidateType}`;
1955
- };
1956
- var createRtcTransportStats = async (connection, topic) => {
1957
- const stats = connection && await getRtcConnectionStats(connection, topic);
1958
- if (!stats) {
1959
- return {
1960
- bytesSent: 0,
1961
- bytesReceived: 0,
1962
- packetsSent: 0,
1963
- packetsReceived: 0,
1964
- rawStats: {}
1965
- };
1966
- }
1967
- return {
1968
- bytesSent: stats.dataChannel?.bytesSent,
1969
- bytesReceived: stats.dataChannel?.bytesReceived,
1970
- packetsSent: 0,
1971
- packetsReceived: 0,
1972
- rawStats: stats.raw
1973
- };
1974
- };
1975
- var getRtcConnectionStats = async (connection, channelTopic) => {
1976
- const stats = await connection.getStats();
1977
- const statsEntries = Array.from(stats.entries());
1978
- const transport = statsEntries.find(([_, entry]) => entry.type === "transport")?.[1];
1979
- const selectedCandidatePair = transport && statsEntries.find(([entryId]) => entryId === transport.selectedCandidatePairId)?.[1];
1980
- const remoteCandidate = selectedCandidatePair && statsEntries.find(([entryId]) => entryId === selectedCandidatePair.remoteCandidateId)?.[1];
1981
- const dataChannel = channelTopic && statsEntries.find(([_, entry]) => entry.type === "data-channel" && entry.label === channelTopic)?.[1];
1982
- return {
1983
- transport,
1984
- selectedCandidatePair,
1985
- dataChannel,
1986
- remoteCandidate,
1987
- raw: Object.fromEntries(stats)
1988
- };
1989
- };
1990
-
1991
- // src/transport/webrtc/rtc-transport-channel.ts
1992
- var __dxlog_file10 = "/__w/dxos/dxos/packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-channel.ts";
1993
- var MAX_MESSAGE_SIZE = 64 * 1024;
1994
- var MAX_BUFFERED_AMOUNT = 64 * 1024;
1995
- var RtcTransportChannel = class extends Resource {
1996
- _connection;
1997
- _options;
1998
- closed = new AsyncEvent();
1999
- connected = new AsyncEvent();
2000
- errors = new ErrorStream4();
2001
- _channel;
2002
- _stream;
2003
- _streamDataFlushedCallback = null;
2004
- _isChannelCreationInProgress = false;
2005
- constructor(_connection, _options) {
2006
- super(), this._connection = _connection, this._options = _options;
2007
- }
2008
- get isRtcChannelCreationInProgress() {
2009
- return this._isChannelCreationInProgress;
2010
- }
2011
- onConnectionError(error) {
2012
- if (this.isOpen) {
2013
- this.errors.raise(error);
2014
- }
2015
- }
2016
- async _open() {
2017
- invariant9(!this._isChannelCreationInProgress, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file10, L: 41, S: this, A: ["!this._isChannelCreationInProgress", ""] });
2018
- this._isChannelCreationInProgress = true;
2019
- this._connection.createDataChannel(this._options.topic).then((channel) => {
2020
- if (this.isOpen) {
2021
- this._channel = channel;
2022
- this._initChannel(this._channel);
2023
- } else {
2024
- this._safeCloseChannel(channel);
2025
- }
2026
- }).catch((err) => {
2027
- if (this.isOpen) {
2028
- const error = err instanceof Error ? err : new ConnectivityError2({
2029
- message: `Failed to create a channel: ${JSON.stringify(err?.message)}`
2030
- });
2031
- this.errors.raise(error);
2032
- } else {
2033
- log9.verbose("connection establishment failed after transport was closed", {
2034
- err
2035
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file10, L: 57, S: this });
2036
- }
2037
- }).finally(() => {
2038
- this._isChannelCreationInProgress = false;
2039
- });
2040
- }
2041
- async _close() {
2042
- if (this._channel) {
2043
- this._safeCloseChannel(this._channel);
2044
- this._channel = void 0;
2045
- this._stream = void 0;
2046
- }
2047
- this.closed.emit();
2048
- log9("closed", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file10, L: 72, S: this });
2049
- }
2050
- _initChannel(channel) {
2051
- Object.assign(channel, {
2052
- onopen: () => {
2053
- if (!this.isOpen) {
2054
- log9.warn("channel opened in a closed transport", {
2055
- topic: this._options.topic
2056
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file10, L: 78, S: this });
2057
- this._safeCloseChannel(channel);
2058
- return;
2059
- }
2060
- log9("onopen", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file10, L: 84, S: this });
2061
- const duplex = new Duplex({
2062
- read: () => {
2063
- },
2064
- write: (chunk, encoding, callback) => {
2065
- return this._handleChannelWrite(chunk, callback);
2066
- }
2067
- });
2068
- duplex.pipe(this._options.stream).pipe(duplex);
2069
- this._stream = duplex;
2070
- this.connected.emit();
2071
- },
2072
- onclose: async () => {
2073
- log9("onclose", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file10, L: 96, S: this });
2074
- await this.close();
2075
- },
2076
- onmessage: async (event) => {
2077
- if (!this._stream) {
2078
- log9.warn("ignoring message on a closed channel", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file10, L: 101, S: this });
2079
- return;
2080
- }
2081
- let data = event.data;
2082
- if (data instanceof ArrayBuffer) {
2083
- data = Buffer.from(data);
2084
- } else if (data instanceof Blob) {
2085
- data = Buffer.from(await data.arrayBuffer());
2086
- }
2087
- this._stream.push(data);
2088
- },
2089
- onerror: (event) => {
2090
- if (this.isOpen) {
2091
- const err = event.error instanceof Error ? event.error : new Error(`Datachannel error: ${event.type}.`);
2092
- this.errors.raise(err);
2093
- }
2094
- },
2095
- onbufferedamountlow: () => {
2096
- const cb = this._streamDataFlushedCallback;
2097
- this._streamDataFlushedCallback = null;
2098
- cb?.();
2099
- }
2100
- });
2101
- }
2102
- async _handleChannelWrite(chunk, callback) {
2103
- if (!this._channel) {
2104
- log9.warn("writing to a channel after a connection was closed", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file10, L: 127, S: this });
2105
- return;
2106
- }
2107
- if (chunk.length > MAX_MESSAGE_SIZE) {
2108
- const error = new Error(`Message too large: ${chunk.length} > ${MAX_MESSAGE_SIZE}.`);
2109
- this.errors.raise(error);
2110
- callback();
2111
- return;
2112
- }
2113
- try {
2114
- this._channel.send(chunk);
2115
- } catch (err) {
2116
- this.errors.raise(err);
2117
- callback();
2118
- return;
2119
- }
2120
- if (this._channel.bufferedAmount > MAX_BUFFERED_AMOUNT) {
2121
- if (this._streamDataFlushedCallback !== null) {
2122
- log9.error("consumer trying to write before we are ready for more data", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file10, L: 145, S: this });
2123
- }
2124
- this._streamDataFlushedCallback = callback;
2125
- } else {
2126
- callback();
2127
- }
2128
- }
2129
- _safeCloseChannel(channel) {
2130
- try {
2131
- channel.close();
2132
- } catch (error) {
2133
- log9.catch(error, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file10, L: 156, S: this });
2134
- }
2135
- }
2136
- onSignal(signal) {
2137
- return this._connection.onSignal(signal);
2138
- }
2139
- async getDetails() {
2140
- return describeSelectedRemoteCandidate(this._connection.currentConnection);
2141
- }
2142
- async getStats() {
2143
- return createRtcTransportStats(this._connection.currentConnection, this._options.topic);
2144
- }
2145
- };
2146
-
2147
- // src/transport/webrtc/utils.ts
2148
- var chooseInitiatorPeer = (peer1Key, peer2Key) => peer1Key < peer2Key ? peer1Key : peer2Key;
2149
- var areSdpEqual = (sdp1, sdp2) => {
2150
- const sdp1Lines = deduplicatedSdpLines(sdp1);
2151
- const sdp2Lines = deduplicatedSdpLines(sdp2);
2152
- if (sdp1Lines.length !== sdp2Lines.length) {
2153
- return false;
2154
- }
2155
- return sdp1Lines.every((line, idx) => line === sdp2Lines[idx]);
2156
- };
2157
- var deduplicatedSdpLines = (sdp) => {
2158
- const deduplicatedLines = [];
2159
- const seenLines = [];
2160
- for (const line of sdp.split("\r\n")) {
2161
- if (line.startsWith("m")) {
2162
- seenLines.length = 0;
2163
- }
2164
- if (seenLines.includes(line)) {
2165
- continue;
2166
- }
2167
- seenLines.push(line);
2168
- deduplicatedLines.push(line);
2169
- }
2170
- return deduplicatedLines;
2171
- };
2172
-
2173
- // src/transport/webrtc/rtc-peer-connection.ts
2174
- var __dxlog_file11 = "/__w/dxos/dxos/packages/core/mesh/network-manager/src/transport/webrtc/rtc-peer-connection.ts";
2175
- function _ts_decorate6(decorators, target, key, desc) {
2176
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2177
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2178
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2179
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2180
- }
2181
- var RtcPeerConnection = class {
2182
- _factory;
2183
- _options;
2184
- // A peer who is not the initiator waits for another party to open a channel.
2185
- _channelCreatedCallbacks = /* @__PURE__ */ new Map();
2186
- // Channels indexed by topic.
2187
- _transportChannels = /* @__PURE__ */ new Map();
2188
- _dataChannels = /* @__PURE__ */ new Map();
2189
- // A peer is ready to receive ICE candidates when local and remote description were set.
2190
- _readyForCandidates = new Trigger3();
2191
- _offerProcessingMutex = new Mutex2();
2192
- /**
2193
- * Can't use peer.connection.initiator, because if two connections to the same peer are created in
2194
- * different swarms, we might be the initiator of the first one, but not of the other one.
2195
- * Use a stable peer keypair property (key ordering) to decide who's acting as the initiator of
2196
- * transport connection establishment and data channel creation.
2197
- */
2198
- _initiator;
2199
- _connection;
2200
- constructor(_factory, _options) {
2201
- this._factory = _factory;
2202
- this._options = _options;
2203
- this._initiator = chooseInitiatorPeer(_options.ownPeerKey, _options.remotePeerKey) === _options.ownPeerKey;
2204
- }
2205
- get transportChannelCount() {
2206
- return this._transportChannels.size;
2207
- }
2208
- get currentConnection() {
2209
- return this._connection;
2210
- }
2211
- async createDataChannel(topic) {
2212
- const connection = await this._openConnection();
2213
- if (!this._transportChannels.has(topic)) {
2214
- if (!this._transportChannels.size) {
2215
- void this._lockAndCloseConnection();
2216
- }
2217
- throw new Error("Transport closed while connection was being open");
2218
- }
2219
- if (this._initiator) {
2220
- const channel = connection.createDataChannel(topic);
2221
- this._dataChannels.set(topic, channel);
2222
- return channel;
2223
- } else {
2224
- const existingChannel = this._dataChannels.get(topic);
2225
- if (existingChannel) {
2226
- return existingChannel;
2227
- }
2228
- log10("waiting for initiator-peer to open a data channel", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 63, S: this });
2229
- return new Promise((resolve, reject) => {
2230
- this._channelCreatedCallbacks.set(topic, {
2231
- resolve,
2232
- reject
2233
- });
2234
- });
2235
- }
2236
- }
2237
- createTransportChannel(options) {
2238
- const channel = new RtcTransportChannel(this, options);
2239
- this._transportChannels.set(options.topic, channel);
2240
- channel.closed.on(() => {
2241
- this._transportChannels.delete(options.topic);
2242
- if (this._transportChannels.size === 0) {
2243
- void this._lockAndCloseConnection();
2244
- }
2245
- });
2246
- return channel;
2247
- }
2248
- async _openConnection() {
2249
- if (this._connection) {
2250
- return this._connection;
2251
- }
2252
- log10("initializing connection...", () => ({
2253
- remotePeer: this._options.remotePeerKey
2254
- }), { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 87, S: this });
2255
- const config = await this._loadConnectionConfig();
2256
- const connection = await this._factory.createConnection(config);
2257
- const iceCandidateErrors = [];
2258
- Object.assign(connection, {
2259
- onnegotiationneeded: async () => {
2260
- invariant10(this._initiator, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 100, S: this, A: ["this._initiator", ""] });
2261
- if (connection !== this._connection) {
2262
- this._onConnectionCallbackAfterClose("onnegotiationneeded", connection);
2263
- return;
2264
- }
2265
- log10("onnegotiationneeded", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 105, S: this });
2266
- try {
2267
- const offer = await connection.createOffer();
2268
- await connection.setLocalDescription(offer);
2269
- await this._sendDescription(connection, offer);
2270
- } catch (err) {
2271
- void this._lockAndAbort(connection, err);
2272
- }
2273
- },
2274
- // When ICE candidate identified (should be sent to remote peer) and when ICE gathering finalized.
2275
- // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/icecandidate_event
2276
- onicecandidate: async (event) => {
2277
- if (connection !== this._connection) {
2278
- this._onConnectionCallbackAfterClose("onicecandidate", connection);
2279
- return;
2280
- }
2281
- if (event.candidate) {
2282
- log10("onicecandidate", {
2283
- candidate: event.candidate.candidate
2284
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 122, S: this });
2285
- await this._sendIceCandidate(event.candidate);
2286
- } else {
2287
- log10("onicecandidate gathering complete", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 127, S: this });
2288
- }
2289
- },
2290
- // When error occurs while performing ICE negotiations through a STUN or TURN server.
2291
- // It's ok for some candidates to fail if a working pair is eventually found.
2292
- // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/icecandidateerror_event
2293
- onicecandidateerror: (event) => {
2294
- const { url, errorCode, errorText } = event;
2295
- iceCandidateErrors.push({
2296
- url,
2297
- errorCode,
2298
- errorText
2299
- });
2300
- },
2301
- // When possible error during ICE gathering.
2302
- // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceconnectionstatechange_event
2303
- oniceconnectionstatechange: () => {
2304
- if (connection !== this._connection) {
2305
- this._onConnectionCallbackAfterClose("oniceconnectionstatechange", connection);
2306
- return;
2307
- }
2308
- log10("oniceconnectionstatechange", {
2309
- state: connection.iceConnectionState
2310
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 148, S: this });
2311
- if (connection.iceConnectionState === "failed") {
2312
- void this._lockAndAbort(connection, createIceFailureError(iceCandidateErrors));
2313
- }
2314
- },
2315
- // When new track (or channel) is added.
2316
- // State: { new, connecting, connected, disconnected, failed, closed }
2317
- // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionstatechange_event
2318
- onconnectionstatechange: () => {
2319
- if (connection !== this._connection) {
2320
- if (connection.connectionState !== "closed" && connection.connectionState !== "failed") {
2321
- this._onConnectionCallbackAfterClose("onconnectionstatechange", connection);
2322
- }
2323
- return;
2324
- }
2325
- log10("onconnectionstatechange", {
2326
- state: connection.connectionState
2327
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 165, S: this });
2328
- if (connection.connectionState === "failed") {
2329
- void this._lockAndAbort(connection, new Error("Connection failed."));
2330
- }
2331
- },
2332
- onsignalingstatechange: () => {
2333
- log10("onsignalingstatechange", {
2334
- state: connection.signalingState
2335
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 173, S: this });
2336
- },
2337
- // When channel is added to connection.
2338
- // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/datachannel_event
2339
- ondatachannel: (event) => {
2340
- invariant10(!this._initiator, "Initiator is expected to create data channels.", { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 180, S: this, A: ["!this._initiator", "'Initiator is expected to create data channels.'"] });
2341
- if (connection !== this._connection) {
2342
- this._onConnectionCallbackAfterClose("ondatachannel", connection);
2343
- return;
2344
- }
2345
- log10("ondatachannel", {
2346
- label: event.channel.label
2347
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 185, S: this });
2348
- this._dataChannels.set(event.channel.label, event.channel);
2349
- const pendingCallback = this._channelCreatedCallbacks.get(event.channel.label);
2350
- if (pendingCallback) {
2351
- this._channelCreatedCallbacks.delete(event.channel.label);
2352
- pendingCallback.resolve(event.channel);
2353
- }
2354
- }
2355
- });
2356
- this._connection = connection;
2357
- this._readyForCandidates.reset();
2358
- await this._factory.initConnection(connection, {
2359
- initiator: this._initiator
2360
- });
2361
- return this._connection;
2362
- }
2363
- async _lockAndAbort(connection, error) {
2364
- this._abortConnection(connection, error);
2365
- }
2366
- _abortConnection(connection, error) {
2367
- if (connection !== this._connection) {
2368
- log10.error("attempted to abort an inactive connection", {
2369
- error
2370
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 208, S: this });
2371
- this._safeCloseConnection(connection);
2372
- return;
2373
- }
2374
- for (const [topic, pendingCallback] of this._channelCreatedCallbacks.entries()) {
2375
- pendingCallback.reject(error);
2376
- this._transportChannels.delete(topic);
2377
- }
2378
- this._channelCreatedCallbacks.clear();
2379
- for (const channel of this._transportChannels.values()) {
2380
- channel.onConnectionError(error);
2381
- }
2382
- this._transportChannels.clear();
2383
- this._safeCloseConnection();
2384
- log10("connection aborted", {
2385
- reason: error.message
2386
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 224, S: this });
2387
- }
2388
- async _lockAndCloseConnection() {
2389
- invariant10(this._transportChannels.size === 0, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 229, S: this, A: ["this._transportChannels.size === 0", ""] });
2390
- if (this._connection) {
2391
- this._safeCloseConnection();
2392
- log10("connection closed", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 232, S: this });
2393
- }
2394
- }
2395
- async onSignal(signal) {
2396
- const connection = this._connection;
2397
- if (!connection) {
2398
- log10.warn("a signal ignored because the connection was closed", {
2399
- type: signal.payload.data.type
2400
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 238, S: this });
2401
- return;
2402
- }
2403
- const data = signal.payload.data;
2404
- switch (data.type) {
2405
- case "offer": {
2406
- await this._offerProcessingMutex.executeSynchronized(async () => {
2407
- if (isRemoteDescriptionSet(connection, data)) {
2408
- return;
2409
- }
2410
- if (connection.connectionState !== "new") {
2411
- this._abortConnection(connection, new Error(`Received an offer in ${connection.connectionState}.`));
2412
- return;
2413
- }
2414
- try {
2415
- await connection.setRemoteDescription({
2416
- type: data.type,
2417
- sdp: data.sdp
2418
- });
2419
- const answer = await connection.createAnswer();
2420
- await connection.setLocalDescription(answer);
2421
- await this._sendDescription(connection, answer);
2422
- this._onSessionNegotiated(connection);
2423
- } catch (err) {
2424
- this._abortConnection(connection, new Error("Error handling a remote offer.", {
2425
- cause: err
2426
- }));
2427
- }
2428
- });
2429
- break;
2430
- }
2431
- case "answer":
2432
- await this._offerProcessingMutex.executeSynchronized(async () => {
2433
- try {
2434
- if (isRemoteDescriptionSet(connection, data)) {
2435
- return;
2436
- }
2437
- if (connection.signalingState !== "have-local-offer") {
2438
- this._abortConnection(connection, new Error(`Unexpected answer from remote peer, signalingState was ${connection.signalingState}.`));
2439
- return;
2440
- }
2441
- await connection.setRemoteDescription({
2442
- type: data.type,
2443
- sdp: data.sdp
2444
- });
2445
- this._onSessionNegotiated(connection);
2446
- } catch (err) {
2447
- this._abortConnection(connection, new Error("Error handling a remote answer.", {
2448
- cause: err
2449
- }));
2450
- }
2451
- });
2452
- break;
2453
- case "candidate":
2454
- void this._processIceCandidate(connection, data.candidate);
2455
- break;
2456
- default:
2457
- this._abortConnection(connection, new Error(`Unknown signal type ${data.type}.`));
2458
- break;
2459
- }
2460
- log10("signal processed", {
2461
- type: data.type
2462
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 301, S: this });
2463
- }
2464
- async _processIceCandidate(connection, candidate) {
2465
- try {
2466
- await this._readyForCandidates.wait();
2467
- if (connection === this._connection) {
2468
- log10("adding ice candidate", {
2469
- candidate
2470
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 310, S: this });
2471
- await connection.addIceCandidate(candidate);
2472
- }
2473
- } catch (err) {
2474
- log10.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 316, S: this });
2475
- }
2476
- }
2477
- _onSessionNegotiated(connection) {
2478
- if (connection === this._connection) {
2479
- log10("ready to process ice candidates", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 321, S: this });
2480
- this._readyForCandidates.wake();
2481
- } else {
2482
- log10.warn("session was negotiated after connection became inactive", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 324, S: this });
2483
- }
2484
- }
2485
- _onConnectionCallbackAfterClose(callback, connection) {
2486
- log10.warn("callback invoked after a connection was destroyed, this is probably a bug", {
2487
- callback,
2488
- state: connection.connectionState
2489
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 328, S: this });
2490
- this._safeCloseConnection(connection);
2491
- }
2492
- _safeCloseConnection(connection = this._connection) {
2493
- const resetFields = this._connection && connection === this._connection;
2494
- try {
2495
- connection?.close();
2496
- } catch (err) {
2497
- log10.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 339, S: this });
2498
- }
2499
- if (resetFields) {
2500
- this._connection = void 0;
2501
- this._dataChannels.clear();
2502
- this._readyForCandidates.wake();
2503
- void this._factory.onConnectionDestroyed().catch((err) => log10.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 345, S: this }));
2504
- for (const [_, pendingCallback] of this._channelCreatedCallbacks.entries()) {
2505
- pendingCallback.reject("Connection closed.");
2506
- }
2507
- this._channelCreatedCallbacks.clear();
2508
- }
2509
- }
2510
- async _loadConnectionConfig() {
2511
- const config = {
2512
- ...this._options.webrtcConfig
2513
- };
2514
- try {
2515
- const providedIceServers = await this._options.iceProvider?.getIceServers() ?? [];
2516
- if (providedIceServers.length > 0) {
2517
- config.iceServers = [
2518
- ...config.iceServers ?? [],
2519
- ...providedIceServers
2520
- ];
2521
- }
2522
- } catch (error) {
2523
- log10.catch(error, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 365, S: this });
2524
- }
2525
- return config;
2526
- }
2527
- async _sendIceCandidate(candidate) {
2528
- try {
2529
- await this._options.sendSignal({
2530
- payload: {
2531
- data: {
2532
- type: "candidate",
2533
- candidate: {
2534
- candidate: candidate.candidate,
2535
- // These fields never seem to be not null, but connecting to Chrome doesn't work if they are.
2536
- sdpMLineIndex: candidate.sdpMLineIndex ?? "0",
2537
- sdpMid: candidate.sdpMid ?? "0"
2538
- }
2539
- }
2540
- }
2541
- });
2542
- } catch (err) {
2543
- log10.warn("signaling error", {
2544
- err
2545
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 385, S: this });
2546
- }
2547
- }
2548
- async _sendDescription(connection, description) {
2549
- if (connection !== this._connection) {
2550
- return;
2551
- }
2552
- const data = {
2553
- type: description.type,
2554
- sdp: description.sdp
2555
- };
2556
- await this._options.sendSignal({
2557
- payload: {
2558
- data
2559
- }
2560
- });
2561
- }
2562
- get _connectionInfo() {
2563
- const connectionInfo = this._connection && {
2564
- connectionState: this._connection.connectionState,
2565
- iceConnectionState: this._connection.iceConnectionState,
2566
- iceGatheringState: this._connection.iceGatheringState,
2567
- signalingState: this._connection.signalingState,
2568
- remoteDescription: this._connection.remoteDescription,
2569
- localDescription: this._connection.localDescription
2570
- };
2571
- return {
2572
- ...connectionInfo,
2573
- ts: Date.now(),
2574
- remotePeerKey: this._options.remotePeerKey,
2575
- channels: [
2576
- ...this._transportChannels.keys()
2577
- ].map((topic) => topic),
2578
- config: this._connection?.getConfiguration()
2579
- };
2580
- }
2581
- get _loggerContext() {
2582
- return {
2583
- ownPeerKey: this._options.ownPeerKey,
2584
- remotePeerKey: this._options.remotePeerKey,
2585
- initiator: this._initiator,
2586
- channels: this._transportChannels.size
2587
- };
2588
- }
2589
- };
2590
- _ts_decorate6([
2591
- synchronized5
2592
- ], RtcPeerConnection.prototype, "_openConnection", null);
2593
- _ts_decorate6([
2594
- synchronized5
2595
- ], RtcPeerConnection.prototype, "_lockAndAbort", null);
2596
- _ts_decorate6([
2597
- synchronized5
2598
- ], RtcPeerConnection.prototype, "_lockAndCloseConnection", null);
2599
- _ts_decorate6([
2600
- synchronized5
2601
- ], RtcPeerConnection.prototype, "onSignal", null);
2602
- _ts_decorate6([
2603
- trace.info()
2604
- ], RtcPeerConnection.prototype, "_connectionInfo", null);
2605
- _ts_decorate6([
2606
- logInfo4
2607
- ], RtcPeerConnection.prototype, "_loggerContext", null);
2608
- RtcPeerConnection = _ts_decorate6([
2609
- trace.resource()
2610
- ], RtcPeerConnection);
2611
- var isRemoteDescriptionSet = (connection, data) => {
2612
- if (!connection.remoteDescription?.type || connection.remoteDescription?.type !== data.type) {
2613
- return false;
2614
- }
2615
- return areSdpEqual(connection.remoteDescription.sdp, data.sdp);
2616
- };
2617
- var createIceFailureError = (details) => {
2618
- const candidateErrors = details.map(({ url, errorCode, errorText }) => `${errorCode} ${url}: ${errorText}`);
2619
- return new ConnectivityError3({
2620
- message: `ICE failed:
2621
- ${candidateErrors.join("\n")}`
2622
- });
2623
- };
2624
-
2625
- // src/transport/webrtc/rtc-transport-factory.ts
2626
- var createRtcTransportFactory = (webrtcConfig, iceProvider) => {
2627
- const connectionFactory = getRtcConnectionFactory();
2628
- return {
2629
- createTransport: (options) => {
2630
- const connection = new RtcPeerConnection(connectionFactory, {
2631
- ownPeerKey: options.ownPeerKey,
2632
- remotePeerKey: options.remotePeerKey,
2633
- sendSignal: options.sendSignal,
2634
- legacyInitiator: options.initiator,
2635
- webrtcConfig,
2636
- iceProvider
2637
- });
2638
- return connection.createTransportChannel(options);
2639
- }
2640
- };
2641
- };
2642
-
2643
- // src/transport/webrtc/rtc-transport-proxy.ts
2644
- import { Writable } from "@dxos/node-std/stream";
2645
- import { Event as Event8, scheduleTask as scheduleTask4 } from "@dxos/async";
2646
- import { Resource as Resource2 } from "@dxos/context";
2647
- import { ErrorStream as ErrorStream5 } from "@dxos/debug";
2648
- import { invariant as invariant11 } from "@dxos/invariant";
2649
- import { PublicKey as PublicKey9 } from "@dxos/keys";
2650
- import { log as log11 } from "@dxos/log";
2651
- import { ConnectionResetError as ConnectionResetError2, ConnectivityError as ConnectivityError4, TimeoutError as TimeoutError3 } from "@dxos/protocols";
2652
- import { ConnectionState as ConnectionState3 } from "@dxos/protocols/proto/dxos/mesh/bridge";
2653
- import { arrayToBuffer } from "@dxos/util";
2654
- var __dxlog_file12 = "/__w/dxos/dxos/packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-proxy.ts";
2655
- var RPC_TIMEOUT = 1e4;
2656
- var CLOSE_RPC_TIMEOUT = 3e3;
2657
- var RESP_MIN_THRESHOLD = 500;
2658
- var RtcTransportProxy = class extends Resource2 {
2659
- _options;
2660
- _proxyId = PublicKey9.random();
2661
- closed = new Event8();
2662
- connected = new Event8();
2663
- errors = new ErrorStream5();
2664
- _serviceStream;
2665
- constructor(_options) {
2666
- super(), this._options = _options;
2667
- }
2668
- async _open() {
2669
- let stream;
2670
- try {
2671
- stream = this._options.bridgeService.open({
2672
- proxyId: this._proxyId,
2673
- remotePeerKey: this._options.remotePeerKey,
2674
- ownPeerKey: this._options.ownPeerKey,
2675
- topic: this._options.topic,
2676
- initiator: this._options.initiator ?? false
2677
- }, {
2678
- timeout: RPC_TIMEOUT
2679
- });
2680
- } catch (error) {
2681
- this.errors.raise(error);
2682
- return;
2683
- }
2684
- this._serviceStream = stream;
2685
- stream.waitUntilReady().then(() => {
2686
- stream.subscribe(async (event) => {
2687
- log11("rtc transport proxy event", event, { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 46, S: this });
2688
- if (event.connection) {
2689
- await this._handleConnection(event.connection);
2690
- } else if (event.data) {
2691
- this._handleData(event.data);
2692
- } else if (event.signal) {
2693
- await this._handleSignal(event.signal);
2694
- }
2695
- }, (err) => {
2696
- log11("rtc bridge stream closed", {
2697
- err
2698
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 55, S: this });
2699
- if (err) {
2700
- this._raiseIfOpen(err);
2701
- } else {
2702
- void this.close();
2703
- }
2704
- });
2705
- const connectorStream = new Writable({
2706
- write: (chunk, _, callback) => {
2707
- const sendStartMs = Date.now();
2708
- this._options.bridgeService.sendData({
2709
- proxyId: this._proxyId,
2710
- payload: chunk
2711
- }, {
2712
- timeout: RPC_TIMEOUT
2713
- }).then(() => {
2714
- if (Date.now() - sendStartMs > RESP_MIN_THRESHOLD) {
2715
- log11("slow response, delaying callback", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 74, S: this });
2716
- scheduleTask4(this._ctx, () => callback(), RESP_MIN_THRESHOLD);
2717
- } else {
2718
- callback();
2719
- }
2720
- }, (err) => {
2721
- callback();
2722
- this._raiseIfOpen(err);
2723
- });
2724
- }
2725
- });
2726
- connectorStream.on("error", (err) => {
2727
- this._raiseIfOpen(err);
2728
- });
2729
- this._options.stream.pipe(connectorStream);
2730
- }, (error) => {
2731
- if (error) {
2732
- this._raiseIfOpen(error);
2733
- } else {
2734
- void this.close();
2735
- }
2736
- });
2737
- }
2738
- async _close() {
2739
- try {
2740
- await this._serviceStream?.close();
2741
- this._serviceStream = void 0;
2742
- } catch (err) {
2743
- log11.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 102, S: this });
2744
- }
2745
- try {
2746
- await this._options.bridgeService.close({
2747
- proxyId: this._proxyId
2748
- }, {
2749
- timeout: CLOSE_RPC_TIMEOUT
2750
- });
2751
- } catch (err) {
2752
- log11.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 111, S: this });
2753
- }
2754
- this.closed.emit();
2755
- }
2756
- async onSignal(signal) {
2757
- this._options.bridgeService.sendSignal({
2758
- proxyId: this._proxyId,
2759
- signal
2760
- }, {
2761
- timeout: RPC_TIMEOUT
2762
- }).catch((err) => this._raiseIfOpen(decodeError(err)));
2763
- }
2764
- async _handleConnection(connectionEvent) {
2765
- if (connectionEvent.error) {
2766
- this.errors.raise(decodeError(connectionEvent.error));
2767
- return;
2768
- }
2769
- switch (connectionEvent.state) {
2770
- case ConnectionState3.CONNECTED: {
2771
- this.connected.emit();
2772
- break;
2773
- }
2774
- case ConnectionState3.CLOSED: {
2775
- await this.close();
2776
- break;
2777
- }
2778
- }
2779
- }
2780
- _handleData(dataEvent) {
2781
- try {
2782
- this._options.stream.write(arrayToBuffer(dataEvent.payload));
2783
- } catch (error) {
2784
- this._raiseIfOpen(error);
2785
- }
2786
- }
2787
- async _handleSignal(signalEvent) {
2788
- try {
2789
- await this._options.sendSignal(signalEvent.payload);
2790
- } catch (error) {
2791
- const type = signalEvent.payload.payload.data?.type;
2792
- if (type === "offer" || type === "answer") {
2793
- this._raiseIfOpen(new ConnectivityError4({
2794
- message: `Session establishment failed: ${type} couldn't be sent.`
2795
- }));
2796
- }
2797
- }
2798
- }
2799
- async getDetails() {
2800
- try {
2801
- const response = await this._options.bridgeService.getDetails({
2802
- proxyId: this._proxyId
2803
- }, {
2804
- timeout: RPC_TIMEOUT
2805
- });
2806
- return response.details;
2807
- } catch (err) {
2808
- return "bridge-svc unreachable";
2809
- }
2810
- }
2811
- async getStats() {
2812
- try {
2813
- const response = await this._options.bridgeService.getStats({
2814
- proxyId: this._proxyId
2815
- }, {
2816
- timeout: RPC_TIMEOUT
2817
- });
2818
- return response.stats;
2819
- } catch (err) {
2820
- return {
2821
- bytesSent: 0,
2822
- bytesReceived: 0,
2823
- packetsSent: 0,
2824
- packetsReceived: 0,
2825
- rawStats: "bridge-svc unreachable"
2826
- };
2827
- }
2828
- }
2829
- _raiseIfOpen(error) {
2830
- if (this.isOpen) {
2831
- this.errors.raise(error);
2832
- } else {
2833
- log11.info("error swallowed because transport was closed", {
2834
- message: error.message
2835
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 195, S: this });
2836
- }
2837
- }
2838
- /**
2839
- * Called when underlying proxy service becomes unavailable.
2840
- */
2841
- forceClose() {
2842
- void this._serviceStream?.close();
2843
- this.closed.emit();
2844
- }
2845
- };
2846
- var RtcTransportProxyFactory = class {
2847
- _bridgeService;
2848
- _connections = /* @__PURE__ */ new Set();
2849
- /**
2850
- * Sets the current BridgeService to be used to open connections.
2851
- * Calling this method will close any existing connections.
2852
- */
2853
- setBridgeService(bridgeService) {
2854
- this._bridgeService = bridgeService;
2855
- for (const connection of this._connections) {
2856
- connection.forceClose();
2857
- }
2858
- return this;
2859
- }
2860
- createTransport(options) {
2861
- invariant11(this._bridgeService, "RtcTransportProxyFactory is not ready to open connections", { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 221, S: this, A: ["this._bridgeService", "'RtcTransportProxyFactory is not ready to open connections'"] });
2862
- const transport = new RtcTransportProxy({
2863
- ...options,
2864
- bridgeService: this._bridgeService
2865
- });
2866
- this._connections.add(transport);
2867
- transport.closed.on(() => {
2868
- this._connections.delete(transport);
2869
- });
2870
- return transport;
2871
- }
2872
- };
2873
- var decodeError = (err) => {
2874
- const message = typeof err === "string" ? err : err.message;
2875
- if (message.includes("CONNECTION_RESET")) {
2876
- return new ConnectionResetError2({
2877
- message
2878
- });
2879
- } else if (message.includes("TIMEOUT")) {
2880
- return new TimeoutError3({
2881
- message
2882
- });
2883
- } else if (message.includes("CONNECTIVITY_ERROR")) {
2884
- return new ConnectivityError4({
2885
- message
2886
- });
2887
- } else {
2888
- return typeof err === "string" ? new Error(err) : err;
2889
- }
2890
- };
2891
-
2892
- // src/transport/webrtc/rtc-transport-service.ts
2893
- import { Duplex as Duplex2 } from "@dxos/node-std/stream";
2894
- import { Stream } from "@dxos/codec-protobuf/stream";
2895
- import { invariant as invariant12 } from "@dxos/invariant";
2896
- import { PublicKey as PublicKey10 } from "@dxos/keys";
2897
- import { log as log12 } from "@dxos/log";
2898
- import { ConnectionState as ConnectionState4 } from "@dxos/protocols/proto/dxos/mesh/bridge";
2899
- import { ComplexMap as ComplexMap8 } from "@dxos/util";
2900
- var __dxlog_file13 = "/__w/dxos/dxos/packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-service.ts";
2901
- var RtcTransportService = class {
2902
- _transportFactory;
2903
- _openTransports = new ComplexMap8(PublicKey10.hash);
2904
- constructor(webrtcConfig, iceProvider, _transportFactory = createRtcTransportFactory(webrtcConfig, iceProvider)) {
2905
- this._transportFactory = _transportFactory;
2906
- }
2907
- hasOpenTransports() {
2908
- return this._openTransports.size > 0;
2909
- }
2910
- open(request) {
2911
- const existingTransport = this._openTransports.get(request.proxyId);
2912
- if (existingTransport) {
2913
- log12.error("requesting a new transport bridge for an existing proxy", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file13, L: 24, S: this });
2914
- void this._safeCloseTransport(existingTransport);
2915
- this._openTransports.delete(request.proxyId);
2916
- }
2917
- return new Stream(({ ready, next, close }) => {
2918
- const pushNewState = createStateUpdater(next);
2919
- const transportStream = new Duplex2({
2920
- read: () => {
2921
- const callbacks = [
2922
- ...transportState.writeProcessedCallbacks
2923
- ];
2924
- transportState.writeProcessedCallbacks.length = 0;
2925
- callbacks.forEach((cb) => cb());
2926
- },
2927
- write: function(chunk, _, callback) {
2928
- next({
2929
- data: {
2930
- payload: chunk
2931
- }
2932
- });
2933
- callback();
2934
- }
2935
- });
2936
- const transport = this._transportFactory.createTransport({
2937
- initiator: request.initiator,
2938
- topic: request.topic,
2939
- ownPeerKey: request.ownPeerKey,
2940
- remotePeerKey: request.remotePeerKey,
2941
- stream: transportStream,
2942
- sendSignal: async (signal) => {
2943
- next({
2944
- signal: {
2945
- payload: signal
2946
- }
2947
- });
2948
- }
2949
- });
2950
- const transportState = {
2951
- proxyId: request.proxyId,
2952
- transport,
2953
- connectorStream: transportStream,
2954
- writeProcessedCallbacks: []
2955
- };
2956
- transport.connected.on(() => pushNewState(ConnectionState4.CONNECTED));
2957
- transport.errors.handle(async (err) => {
2958
- pushNewState(ConnectionState4.CLOSED, err);
2959
- void this._safeCloseTransport(transportState);
2960
- close(err);
2961
- });
2962
- transport.closed.on(async () => {
2963
- pushNewState(ConnectionState4.CLOSED);
2964
- void this._safeCloseTransport(transportState);
2965
- close();
2966
- });
2967
- this._openTransports.set(request.proxyId, transportState);
2968
- transport.open().catch(async (err) => {
2969
- pushNewState(ConnectionState4.CLOSED, err);
2970
- void this._safeCloseTransport(transportState);
2971
- close(err);
2972
- });
2973
- ready();
2974
- log12("stream ready", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file13, L: 85, S: this });
2975
- pushNewState(ConnectionState4.CONNECTING);
2976
- });
2977
- }
2978
- async sendSignal({ proxyId, signal }) {
2979
- const transport = this._openTransports.get(proxyId);
2980
- invariant12(transport, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file13, L: 91, S: this, A: ["transport", ""] });
2981
- await transport.transport.onSignal(signal);
2982
- }
2983
- async getDetails({ proxyId }) {
2984
- const transport = this._openTransports.get(proxyId);
2985
- invariant12(transport, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file13, L: 96, S: this, A: ["transport", ""] });
2986
- return {
2987
- details: await transport.transport.getDetails()
2988
- };
2989
- }
2990
- async getStats({ proxyId }) {
2991
- const transport = this._openTransports.get(proxyId);
2992
- invariant12(transport, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file13, L: 103, S: this, A: ["transport", ""] });
2993
- return {
2994
- stats: await transport.transport.getStats()
2995
- };
2996
- }
2997
- async sendData({ proxyId, payload }) {
2998
- const transport = this._openTransports.get(proxyId);
2999
- invariant12(transport, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file13, L: 110, S: this, A: ["transport", ""] });
3000
- const bufferHasSpace = transport.connectorStream.push(payload);
3001
- if (!bufferHasSpace) {
3002
- await new Promise((resolve) => {
3003
- transport.writeProcessedCallbacks.push(resolve);
3004
- });
3005
- }
3006
- }
3007
- async close({ proxyId }) {
3008
- const transport = this._openTransports.get(proxyId);
3009
- if (!transport) {
3010
- return;
3011
- }
3012
- this._openTransports.delete(proxyId);
3013
- await this._safeCloseTransport(transport);
3014
- }
3015
- async _safeCloseTransport(transport) {
3016
- if (this._openTransports.get(transport.proxyId) === transport) {
3017
- this._openTransports.delete(transport.proxyId);
3018
- }
3019
- transport.writeProcessedCallbacks.forEach((cb) => cb());
3020
- try {
3021
- await transport.transport.close();
3022
- } catch (error) {
3023
- log12.warn("transport close error", {
3024
- message: error?.message
3025
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file13, L: 134, S: this });
3026
- }
3027
- try {
3028
- transport.connectorStream.end();
3029
- } catch (error) {
3030
- log12.warn("connectorStream close error", {
3031
- message: error?.message
3032
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file13, L: 141, S: this });
3033
- }
3034
- log12("closed", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file13, L: 145, S: this });
3035
- }
3036
- };
3037
- var createStateUpdater = (next) => {
3038
- return (state, err) => {
3039
- next({
3040
- connection: {
3041
- state,
3042
- ...err ? {
3043
- error: err.message
3044
- } : void 0
3045
- }
3046
- });
3047
- };
3048
- };
3049
-
3050
- // src/wire-protocol.ts
3051
- import { Teleport } from "@dxos/teleport";
3052
- var createTeleportProtocolFactory = (onConnection, defaultProps) => {
3053
- return (params) => {
3054
- const teleport = new Teleport({
3055
- ...defaultProps,
3056
- ...params
3057
- });
3058
- return {
3059
- stream: teleport.stream,
3060
- open: async (sessionId) => {
3061
- await teleport.open(sessionId);
3062
- await onConnection(teleport);
3063
- },
3064
- close: async () => {
3065
- await teleport.close();
3066
- },
3067
- abort: async () => {
3068
- await teleport.abort();
3069
- }
3070
- };
3071
- };
3072
- };
3073
-
3074
- export {
3075
- ConnectionState,
3076
- Connection,
3077
- SwarmMessenger,
3078
- Swarm,
3079
- SwarmMapper,
3080
- MAX_CONCURRENT_INITIATING_CONNECTIONS,
3081
- ConnectionLimiter,
3082
- EventType,
3083
- ConnectionLog,
3084
- SwarmNetworkManager,
3085
- FullyConnectedTopology,
3086
- MemoryTransportFactory,
3087
- MemoryTransport,
3088
- TransportKind,
3089
- createRtcTransportFactory,
3090
- RtcTransportProxy,
3091
- RtcTransportProxyFactory,
3092
- RtcTransportService,
3093
- createTeleportProtocolFactory
3094
- };
3095
- //# sourceMappingURL=chunk-ERB3AQAF.mjs.map