@dxos/network-manager 0.6.12 → 0.6.13-main.09887cd

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 (163) hide show
  1. package/dist/lib/browser/chunk-GW3YM55A.mjs +14 -0
  2. package/dist/lib/browser/chunk-GW3YM55A.mjs.map +7 -0
  3. package/dist/lib/browser/{chunk-XYSYUN63.mjs → chunk-MKIVP7G3.mjs} +1249 -1065
  4. package/dist/lib/browser/chunk-MKIVP7G3.mjs.map +7 -0
  5. package/dist/lib/browser/index.mjs +10 -19
  6. package/dist/lib/browser/meta.json +1 -1
  7. package/dist/lib/browser/testing/index.mjs +22 -32
  8. package/dist/lib/browser/testing/index.mjs.map +3 -3
  9. package/dist/lib/browser/transport/tcp/index.mjs +39 -0
  10. package/dist/lib/browser/transport/tcp/index.mjs.map +7 -0
  11. package/dist/lib/node/{chunk-4YAYC7WN.cjs → chunk-D6P7ACEM.cjs} +1262 -1205
  12. package/dist/lib/node/chunk-D6P7ACEM.cjs.map +7 -0
  13. package/dist/lib/node/index.cjs +27 -37
  14. package/dist/lib/node/index.cjs.map +2 -2
  15. package/dist/lib/node/meta.json +1 -1
  16. package/dist/lib/node/testing/index.cjs +24 -34
  17. package/dist/lib/node/testing/index.cjs.map +3 -3
  18. package/dist/lib/node/transport/tcp/index.cjs +191 -0
  19. package/dist/lib/node/transport/tcp/index.cjs.map +7 -0
  20. package/dist/lib/node-esm/chunk-22DA2US6.mjs +4373 -0
  21. package/dist/lib/node-esm/chunk-22DA2US6.mjs.map +7 -0
  22. package/dist/lib/node-esm/index.mjs +50 -0
  23. package/dist/lib/node-esm/index.mjs.map +7 -0
  24. package/dist/lib/node-esm/meta.json +1 -0
  25. package/dist/lib/node-esm/testing/index.mjs +279 -0
  26. package/dist/lib/node-esm/testing/index.mjs.map +7 -0
  27. package/dist/lib/node-esm/transport/tcp/index.mjs +159 -0
  28. package/dist/lib/node-esm/transport/tcp/index.mjs.map +7 -0
  29. package/dist/types/src/network-manager.d.ts +2 -1
  30. package/dist/types/src/network-manager.d.ts.map +1 -1
  31. package/dist/types/src/signal/ice.d.ts.map +1 -1
  32. package/dist/types/src/signal/integration.node.test.d.ts +2 -0
  33. package/dist/types/src/signal/integration.node.test.d.ts.map +1 -0
  34. package/dist/types/src/signal/swarm-messenger.node.test.d.ts +2 -0
  35. package/dist/types/src/signal/swarm-messenger.node.test.d.ts.map +1 -0
  36. package/dist/types/src/swarm/connection.d.ts.map +1 -1
  37. package/dist/types/src/swarm/swarm.d.ts +1 -1
  38. package/dist/types/src/testing/test-builder.d.ts +2 -2
  39. package/dist/types/src/testing/test-builder.d.ts.map +1 -1
  40. package/dist/types/src/testing/test-wire-protocol.d.ts +1 -2
  41. package/dist/types/src/testing/test-wire-protocol.d.ts.map +1 -1
  42. package/dist/types/src/tests/basic-test-suite.d.ts.map +1 -1
  43. package/dist/types/src/tests/property-test-suite.d.ts.map +1 -1
  44. package/dist/types/src/tests/tcp-transport.node.test.d.ts +2 -0
  45. package/dist/types/src/tests/tcp-transport.node.test.d.ts.map +1 -0
  46. package/dist/types/src/tests/utils.d.ts.map +1 -1
  47. package/dist/types/src/transport/index.d.ts +1 -5
  48. package/dist/types/src/transport/index.d.ts.map +1 -1
  49. package/dist/types/src/transport/memory-transport.d.ts +2 -2
  50. package/dist/types/src/transport/memory-transport.d.ts.map +1 -1
  51. package/dist/types/src/transport/tcp/index.d.ts +2 -0
  52. package/dist/types/src/transport/tcp/index.d.ts.map +1 -0
  53. package/dist/types/src/transport/{tcp-transport.browser.d.ts → tcp/tcp-transport.browser.d.ts} +3 -3
  54. package/dist/types/src/transport/tcp/tcp-transport.browser.d.ts.map +1 -0
  55. package/dist/types/src/transport/{tcp-transport.d.ts → tcp/tcp-transport.d.ts} +3 -3
  56. package/dist/types/src/transport/tcp/tcp-transport.d.ts.map +1 -0
  57. package/dist/types/src/transport/transport.d.ts +7 -6
  58. package/dist/types/src/transport/transport.d.ts.map +1 -1
  59. package/dist/types/src/transport/webrtc/index.d.ts +4 -0
  60. package/dist/types/src/transport/webrtc/index.d.ts.map +1 -0
  61. package/dist/types/src/transport/webrtc/rtc-connection-factory.d.ts +14 -0
  62. package/dist/types/src/transport/webrtc/rtc-connection-factory.d.ts.map +1 -0
  63. package/dist/types/src/transport/webrtc/rtc-peer-connection.d.ts +68 -0
  64. package/dist/types/src/transport/webrtc/rtc-peer-connection.d.ts.map +1 -0
  65. package/dist/types/src/transport/webrtc/rtc-transport-channel.d.ts +33 -0
  66. package/dist/types/src/transport/webrtc/rtc-transport-channel.d.ts.map +1 -0
  67. package/dist/types/src/transport/webrtc/rtc-transport-channel.test.d.ts +2 -0
  68. package/dist/types/src/transport/webrtc/rtc-transport-channel.test.d.ts.map +1 -0
  69. package/dist/types/src/transport/webrtc/rtc-transport-factory.d.ts +4 -0
  70. package/dist/types/src/transport/webrtc/rtc-transport-factory.d.ts.map +1 -0
  71. package/dist/types/src/transport/{simplepeer-transport-proxy.d.ts → webrtc/rtc-transport-proxy.d.ts} +10 -12
  72. package/dist/types/src/transport/webrtc/rtc-transport-proxy.d.ts.map +1 -0
  73. package/dist/types/src/transport/webrtc/rtc-transport-proxy.test.d.ts +2 -0
  74. package/dist/types/src/transport/webrtc/rtc-transport-proxy.test.d.ts.map +1 -0
  75. package/dist/types/src/transport/{simplepeer-transport-service.d.ts → webrtc/rtc-transport-service.d.ts} +9 -7
  76. package/dist/types/src/transport/webrtc/rtc-transport-service.d.ts.map +1 -0
  77. package/dist/types/src/transport/webrtc/rtc-transport-stats.d.ts +4 -0
  78. package/dist/types/src/transport/webrtc/rtc-transport-stats.d.ts.map +1 -0
  79. package/dist/types/src/transport/webrtc/rtc-transport.test.d.ts +2 -0
  80. package/dist/types/src/transport/webrtc/rtc-transport.test.d.ts.map +1 -0
  81. package/dist/types/src/transport/webrtc/test-utils.d.ts +5 -0
  82. package/dist/types/src/transport/webrtc/test-utils.d.ts.map +1 -0
  83. package/dist/types/src/transport/webrtc/utils.d.ts +3 -0
  84. package/dist/types/src/transport/webrtc/utils.d.ts.map +1 -0
  85. package/package.json +55 -30
  86. package/src/network-manager.ts +5 -13
  87. package/src/signal/ice.test.ts +1 -3
  88. package/src/signal/ice.ts +6 -1
  89. package/src/signal/{integration.test.ts → integration.node.test.ts} +9 -15
  90. package/src/signal/{swarm-messenger.test.ts → swarm-messenger.node.test.ts} +13 -23
  91. package/src/swarm/connection-limiter.test.ts +3 -6
  92. package/src/swarm/connection.test.ts +63 -38
  93. package/src/swarm/connection.ts +5 -5
  94. package/src/swarm/swarm.test.ts +10 -12
  95. package/src/swarm/swarm.ts +1 -1
  96. package/src/testing/test-builder.ts +13 -29
  97. package/src/testing/test-wire-protocol.ts +1 -4
  98. package/src/tests/basic-test-suite.ts +34 -33
  99. package/src/tests/memory-transport.test.ts +40 -42
  100. package/src/tests/property-test-suite.ts +21 -22
  101. package/src/tests/tcp-transport.node.test.ts +65 -0
  102. package/src/tests/utils.ts +3 -2
  103. package/src/tests/webrtc-transport.test.ts +9 -9
  104. package/src/transport/index.ts +1 -5
  105. package/src/transport/memory-transport.ts +2 -0
  106. package/src/transport/tcp/index.ts +5 -0
  107. package/src/transport/{tcp-transport.browser.ts → tcp/tcp-transport.browser.ts} +7 -3
  108. package/src/transport/{tcp-transport.ts → tcp/tcp-transport.ts} +3 -1
  109. package/src/transport/transport.ts +8 -7
  110. package/src/transport/webrtc/index.ts +7 -0
  111. package/src/transport/webrtc/rtc-connection-factory.ts +82 -0
  112. package/src/transport/webrtc/rtc-peer-connection.ts +472 -0
  113. package/src/transport/webrtc/rtc-transport-channel.test.ts +176 -0
  114. package/src/transport/webrtc/rtc-transport-channel.ts +195 -0
  115. package/src/transport/webrtc/rtc-transport-factory.ts +28 -0
  116. package/src/transport/webrtc/rtc-transport-proxy.test.ts +413 -0
  117. package/src/transport/webrtc/rtc-transport-proxy.ts +264 -0
  118. package/src/transport/webrtc/rtc-transport-service.ts +192 -0
  119. package/src/transport/webrtc/rtc-transport-stats.ts +67 -0
  120. package/src/transport/webrtc/rtc-transport.test.ts +198 -0
  121. package/src/transport/webrtc/test-utils.ts +22 -0
  122. package/src/transport/webrtc/utils.ts +36 -0
  123. package/src/typings.d.ts +8 -2
  124. package/dist/lib/browser/chunk-XYSYUN63.mjs.map +0 -7
  125. package/dist/lib/node/chunk-4YAYC7WN.cjs.map +0 -7
  126. package/dist/types/src/signal/integration.test.d.ts +0 -2
  127. package/dist/types/src/signal/integration.test.d.ts.map +0 -1
  128. package/dist/types/src/signal/swarm-messenger.test.d.ts +0 -2
  129. package/dist/types/src/signal/swarm-messenger.test.d.ts.map +0 -1
  130. package/dist/types/src/tests/tcp-transport.test.d.ts +0 -2
  131. package/dist/types/src/tests/tcp-transport.test.d.ts.map +0 -1
  132. package/dist/types/src/transport/libdatachannel-transport.d.ts +0 -42
  133. package/dist/types/src/transport/libdatachannel-transport.d.ts.map +0 -1
  134. package/dist/types/src/transport/libdatachannel-transport.test.d.ts +0 -2
  135. package/dist/types/src/transport/libdatachannel-transport.test.d.ts.map +0 -1
  136. package/dist/types/src/transport/memory-transport.test.d.ts +0 -2
  137. package/dist/types/src/transport/memory-transport.test.d.ts.map +0 -1
  138. package/dist/types/src/transport/simplepeer-simple-peer.d.ts +0 -2
  139. package/dist/types/src/transport/simplepeer-simple-peer.d.ts.map +0 -1
  140. package/dist/types/src/transport/simplepeer-transport-proxy-test.d.ts +0 -2
  141. package/dist/types/src/transport/simplepeer-transport-proxy-test.d.ts.map +0 -1
  142. package/dist/types/src/transport/simplepeer-transport-proxy.d.ts.map +0 -1
  143. package/dist/types/src/transport/simplepeer-transport-service.d.ts.map +0 -1
  144. package/dist/types/src/transport/simplepeer-transport.d.ts +0 -36
  145. package/dist/types/src/transport/simplepeer-transport.d.ts.map +0 -1
  146. package/dist/types/src/transport/simplepeer-transport.test.d.ts +0 -2
  147. package/dist/types/src/transport/simplepeer-transport.test.d.ts.map +0 -1
  148. package/dist/types/src/transport/tcp-transport.browser.d.ts.map +0 -1
  149. package/dist/types/src/transport/tcp-transport.d.ts.map +0 -1
  150. package/dist/types/src/transport/webrtc.d.ts +0 -6
  151. package/dist/types/src/transport/webrtc.d.ts.map +0 -1
  152. package/src/globals.d.ts +0 -7
  153. package/src/tests/tcp-transport.test.ts +0 -67
  154. package/src/transport/libdatachannel-transport.test.ts +0 -100
  155. package/src/transport/libdatachannel-transport.ts +0 -376
  156. package/src/transport/memory-transport.test.ts +0 -74
  157. package/src/transport/simplepeer-simple-peer.ts +0 -26
  158. package/src/transport/simplepeer-transport-proxy-test.ts +0 -181
  159. package/src/transport/simplepeer-transport-proxy.ts +0 -246
  160. package/src/transport/simplepeer-transport-service.ts +0 -160
  161. package/src/transport/simplepeer-transport.test.ts +0 -61
  162. package/src/transport/simplepeer-transport.ts +0 -250
  163. package/src/transport/webrtc.ts +0 -15
@@ -0,0 +1,4373 @@
1
+ import { createRequire } from 'node:module';const require = createRequire(import.meta.url);
2
+
3
+ // packages/core/mesh/network-manager/src/swarm/connection.ts
4
+ import { DeferredTask, Event, sleep, scheduleTask, scheduleTaskInterval, synchronized, Trigger } from "@dxos/async";
5
+ import { Context, cancelWithContext, ContextDisposedError } from "@dxos/context";
6
+ import { ErrorStream } from "@dxos/debug";
7
+ import { invariant } from "@dxos/invariant";
8
+ import { PublicKey } from "@dxos/keys";
9
+ import { log, logInfo } from "@dxos/log";
10
+ import { CancelledError, ProtocolError, ConnectionResetError, ConnectivityError, TimeoutError, trace } from "@dxos/protocols";
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 __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/swarm/connection.ts";
18
+ var STARTING_SIGNALLING_DELAY = 10;
19
+ var TRANSPORT_CONNECTION_TIMEOUT = 1e4;
20
+ var TRANSPORT_STATS_INTERVAL = 5e3;
21
+ var MAX_SIGNALLING_DELAY = 300;
22
+ var ConnectionState;
23
+ (function(ConnectionState5) {
24
+ ConnectionState5["CREATED"] = "CREATED";
25
+ ConnectionState5["INITIAL"] = "INITIAL";
26
+ ConnectionState5["CONNECTING"] = "CONNECTING";
27
+ ConnectionState5["CONNECTED"] = "CONNECTED";
28
+ ConnectionState5["CLOSING"] = "CLOSING";
29
+ ConnectionState5["CLOSED"] = "CLOSED";
30
+ ConnectionState5["ABORTING"] = "ABORTING";
31
+ ConnectionState5["ABORTED"] = "ABORTED";
32
+ })(ConnectionState || (ConnectionState = {}));
33
+ var Connection = class {
34
+ constructor(topic, localInfo, remoteInfo, sessionId, initiator, _signalMessaging, _protocol, _transportFactory, _callbacks) {
35
+ this.topic = topic;
36
+ this.localInfo = localInfo;
37
+ this.remoteInfo = remoteInfo;
38
+ this.sessionId = sessionId;
39
+ this.initiator = initiator;
40
+ this._signalMessaging = _signalMessaging;
41
+ this._protocol = _protocol;
42
+ this._transportFactory = _transportFactory;
43
+ this._callbacks = _callbacks;
44
+ this._ctx = new Context(void 0, {
45
+ F: __dxlog_file,
46
+ L: 100
47
+ });
48
+ this.connectedTimeoutContext = new Context(void 0, {
49
+ F: __dxlog_file,
50
+ L: 101
51
+ });
52
+ this._protocolClosed = new Trigger();
53
+ this._transportClosed = new Trigger();
54
+ this._state = "CREATED";
55
+ this._incomingSignalBuffer = [];
56
+ this._outgoingSignalBuffer = [];
57
+ this.stateChanged = new Event();
58
+ this.errors = new ErrorStream();
59
+ this._instanceId = PublicKey.random().toHex();
60
+ this.transportStats = new Event();
61
+ this._signalSendTask = new DeferredTask(this._ctx, async () => {
62
+ await this._flushSignalBuffer();
63
+ });
64
+ this._signallingDelay = STARTING_SIGNALLING_DELAY;
65
+ log.trace("dxos.mesh.connection.construct", {
66
+ sessionId: this.sessionId,
67
+ topic: this.topic,
68
+ localPeer: this.localInfo,
69
+ remotePeer: this.remoteInfo,
70
+ initiator: this.initiator
71
+ }, {
72
+ F: __dxlog_file,
73
+ L: 137,
74
+ S: this,
75
+ C: (f, a) => f(...a)
76
+ });
77
+ }
78
+ get sessionIdString() {
79
+ return this.sessionId.truncate();
80
+ }
81
+ get state() {
82
+ return this._state;
83
+ }
84
+ get transport() {
85
+ return this._transport;
86
+ }
87
+ get protocol() {
88
+ return this._protocol;
89
+ }
90
+ /**
91
+ * Create an underlying transport and prepares it for the connection.
92
+ */
93
+ async openConnection() {
94
+ invariant(this._state === "INITIAL", "Invalid state.", {
95
+ F: __dxlog_file,
96
+ L: 167,
97
+ S: this,
98
+ A: [
99
+ "this._state === ConnectionState.INITIAL",
100
+ "'Invalid state.'"
101
+ ]
102
+ });
103
+ log.trace("dxos.mesh.connection.open-connection", trace.begin({
104
+ id: this._instanceId
105
+ }), {
106
+ F: __dxlog_file,
107
+ L: 168,
108
+ S: this,
109
+ C: (f, a) => f(...a)
110
+ });
111
+ log.trace("dxos.mesh.connection.open", {
112
+ sessionId: this.sessionId,
113
+ topic: this.topic,
114
+ localPeerId: this.localInfo,
115
+ remotePeerId: this.remoteInfo,
116
+ initiator: this.initiator
117
+ }, {
118
+ F: __dxlog_file,
119
+ L: 169,
120
+ S: this,
121
+ C: (f, a) => f(...a)
122
+ });
123
+ this._changeState("CONNECTING");
124
+ this._protocol.open(this.sessionId).catch((err) => {
125
+ this.errors.raise(err);
126
+ });
127
+ this._protocol.stream.on("close", () => {
128
+ log("protocol stream closed", void 0, {
129
+ F: __dxlog_file,
130
+ L: 186,
131
+ S: this,
132
+ C: (f, a) => f(...a)
133
+ });
134
+ this._protocolClosed.wake();
135
+ this.close(new ProtocolError("protocol stream closed")).catch((err) => this.errors.raise(err));
136
+ });
137
+ scheduleTask(this.connectedTimeoutContext, async () => {
138
+ log.info(`timeout waiting ${TRANSPORT_CONNECTION_TIMEOUT / 1e3}s for transport to connect, aborting`, void 0, {
139
+ F: __dxlog_file,
140
+ L: 194,
141
+ S: this,
142
+ C: (f, a) => f(...a)
143
+ });
144
+ await this.abort(new TimeoutError(`${TRANSPORT_CONNECTION_TIMEOUT / 1e3}s for transport to connect`)).catch((err) => this.errors.raise(err));
145
+ }, TRANSPORT_CONNECTION_TIMEOUT);
146
+ invariant(!this._transport, void 0, {
147
+ F: __dxlog_file,
148
+ L: 202,
149
+ S: this,
150
+ A: [
151
+ "!this._transport",
152
+ ""
153
+ ]
154
+ });
155
+ this._transport = this._transportFactory.createTransport({
156
+ ownPeerKey: this.localInfo.peerKey,
157
+ remotePeerKey: this.remoteInfo.peerKey,
158
+ topic: this.topic.toHex(),
159
+ initiator: this.initiator,
160
+ stream: this._protocol.stream,
161
+ sendSignal: async (signal) => this._sendSignal(signal),
162
+ sessionId: this.sessionId
163
+ });
164
+ this._transport.connected.once(async () => {
165
+ this._changeState("CONNECTED");
166
+ await this.connectedTimeoutContext.dispose();
167
+ this._callbacks?.onConnected?.();
168
+ scheduleTaskInterval(this._ctx, async () => this._emitTransportStats(), TRANSPORT_STATS_INTERVAL);
169
+ });
170
+ this._transport.closed.once(() => {
171
+ this._transport = void 0;
172
+ this._transportClosed.wake();
173
+ log("abort triggered by transport close", void 0, {
174
+ F: __dxlog_file,
175
+ L: 224,
176
+ S: this,
177
+ C: (f, a) => f(...a)
178
+ });
179
+ this.abort().catch((err) => this.errors.raise(err));
180
+ });
181
+ this._transport.errors.handle(async (err) => {
182
+ log("transport error:", {
183
+ err
184
+ }, {
185
+ F: __dxlog_file,
186
+ L: 229,
187
+ S: this,
188
+ C: (f, a) => f(...a)
189
+ });
190
+ if (!this.closeReason) {
191
+ this.closeReason = err?.message;
192
+ }
193
+ if (err instanceof ConnectionResetError) {
194
+ log.info("aborting due to transport ConnectionResetError", void 0, {
195
+ F: __dxlog_file,
196
+ L: 236,
197
+ S: this,
198
+ C: (f, a) => f(...a)
199
+ });
200
+ this.abort().catch((err2) => this.errors.raise(err2));
201
+ } else if (err instanceof ConnectivityError) {
202
+ log.info("aborting due to transport ConnectivityError", void 0, {
203
+ F: __dxlog_file,
204
+ L: 239,
205
+ S: this,
206
+ C: (f, a) => f(...a)
207
+ });
208
+ this.abort().catch((err2) => this.errors.raise(err2));
209
+ }
210
+ if (this._state !== "CLOSED" && this._state !== "CLOSING") {
211
+ await this.connectedTimeoutContext.dispose();
212
+ this.errors.raise(err);
213
+ }
214
+ });
215
+ await this._transport.open();
216
+ for (const signal of this._incomingSignalBuffer) {
217
+ void this._transport.onSignal(signal);
218
+ }
219
+ this._incomingSignalBuffer = [];
220
+ log.trace("dxos.mesh.connection.open-connection", trace.end({
221
+ id: this._instanceId
222
+ }), {
223
+ F: __dxlog_file,
224
+ L: 258,
225
+ S: this,
226
+ C: (f, a) => f(...a)
227
+ });
228
+ }
229
+ async abort(err) {
230
+ log("aborting...", {
231
+ err
232
+ }, {
233
+ F: __dxlog_file,
234
+ L: 265,
235
+ S: this,
236
+ C: (f, a) => f(...a)
237
+ });
238
+ if (this._state === "CLOSED" || this._state === "ABORTED") {
239
+ log(`abort ignored: already ${this._state}`, this.closeReason, {
240
+ F: __dxlog_file,
241
+ L: 267,
242
+ S: this,
243
+ C: (f, a) => f(...a)
244
+ });
245
+ return;
246
+ }
247
+ await this.connectedTimeoutContext.dispose();
248
+ this._changeState("ABORTING");
249
+ if (!this.closeReason) {
250
+ this.closeReason = err?.message;
251
+ }
252
+ await this._ctx.dispose();
253
+ try {
254
+ await this._closeProtocol({
255
+ abort: true
256
+ });
257
+ } catch (err2) {
258
+ log.catch(err2, void 0, {
259
+ F: __dxlog_file,
260
+ L: 283,
261
+ S: this,
262
+ C: (f, a) => f(...a)
263
+ });
264
+ }
265
+ try {
266
+ await this._closeTransport();
267
+ } catch (err2) {
268
+ log.catch(err2, void 0, {
269
+ F: __dxlog_file,
270
+ L: 290,
271
+ S: this,
272
+ C: (f, a) => f(...a)
273
+ });
274
+ }
275
+ try {
276
+ this._callbacks?.onClosed?.(err);
277
+ } catch (err2) {
278
+ log.catch(err2, void 0, {
279
+ F: __dxlog_file,
280
+ L: 296,
281
+ S: this,
282
+ C: (f, a) => f(...a)
283
+ });
284
+ }
285
+ this._changeState("ABORTED");
286
+ }
287
+ async close(err) {
288
+ if (!this.closeReason) {
289
+ this.closeReason = err?.message;
290
+ } else {
291
+ this.closeReason += `; ${err?.message}`;
292
+ }
293
+ if (this._state === "CLOSED" || this._state === "ABORTING" || this._state === "ABORTED") {
294
+ return;
295
+ }
296
+ const lastState = this._state;
297
+ this._changeState("CLOSING");
298
+ await this.connectedTimeoutContext.dispose();
299
+ await this._ctx.dispose();
300
+ log("closing...", {
301
+ peerId: this.localInfo
302
+ }, {
303
+ F: __dxlog_file,
304
+ L: 321,
305
+ S: this,
306
+ C: (f, a) => f(...a)
307
+ });
308
+ let abortProtocol = false;
309
+ if (lastState !== "CONNECTED") {
310
+ log(`graceful close requested when we were in ${lastState} state? aborting`, void 0, {
311
+ F: __dxlog_file,
312
+ L: 325,
313
+ S: this,
314
+ C: (f, a) => f(...a)
315
+ });
316
+ abortProtocol = true;
317
+ }
318
+ try {
319
+ await this._closeProtocol({
320
+ abort: abortProtocol
321
+ });
322
+ } catch (err2) {
323
+ log.catch(err2, void 0, {
324
+ F: __dxlog_file,
325
+ L: 331,
326
+ S: this,
327
+ C: (f, a) => f(...a)
328
+ });
329
+ }
330
+ try {
331
+ await this._closeTransport();
332
+ } catch (err2) {
333
+ log.catch(err2, void 0, {
334
+ F: __dxlog_file,
335
+ L: 337,
336
+ S: this,
337
+ C: (f, a) => f(...a)
338
+ });
339
+ }
340
+ log("closed", {
341
+ peerId: this.localInfo
342
+ }, {
343
+ F: __dxlog_file,
344
+ L: 340,
345
+ S: this,
346
+ C: (f, a) => f(...a)
347
+ });
348
+ this._changeState("CLOSED");
349
+ this._callbacks?.onClosed?.(err);
350
+ }
351
+ async _closeProtocol(options) {
352
+ log("closing protocol", options, {
353
+ F: __dxlog_file,
354
+ L: 346,
355
+ S: this,
356
+ C: (f, a) => f(...a)
357
+ });
358
+ await Promise.race([
359
+ options?.abort ? this._protocol.abort() : this._protocol.close(),
360
+ this._protocolClosed.wait()
361
+ ]);
362
+ log("protocol closed", options, {
363
+ F: __dxlog_file,
364
+ L: 348,
365
+ S: this,
366
+ C: (f, a) => f(...a)
367
+ });
368
+ }
369
+ async _closeTransport() {
370
+ log("closing transport", void 0, {
371
+ F: __dxlog_file,
372
+ L: 352,
373
+ S: this,
374
+ C: (f, a) => f(...a)
375
+ });
376
+ await Promise.race([
377
+ this._transport?.close(),
378
+ this._transportClosed.wait()
379
+ ]);
380
+ log("transport closed", void 0, {
381
+ F: __dxlog_file,
382
+ L: 354,
383
+ S: this,
384
+ C: (f, a) => f(...a)
385
+ });
386
+ }
387
+ _sendSignal(signal) {
388
+ this._outgoingSignalBuffer.push(signal);
389
+ this._signalSendTask.schedule();
390
+ }
391
+ async _flushSignalBuffer() {
392
+ if (this._outgoingSignalBuffer.length === 0) {
393
+ return;
394
+ }
395
+ try {
396
+ if (process.env.NODE_ENV !== "test") {
397
+ await cancelWithContext(this._ctx, sleep(this._signallingDelay));
398
+ this._signallingDelay = Math.min(this._signallingDelay * 2, MAX_SIGNALLING_DELAY);
399
+ }
400
+ const signals = [
401
+ ...this._outgoingSignalBuffer
402
+ ];
403
+ this._outgoingSignalBuffer.length = 0;
404
+ await this._signalMessaging.signal({
405
+ author: this.localInfo,
406
+ recipient: this.remoteInfo,
407
+ sessionId: this.sessionId,
408
+ topic: this.topic,
409
+ data: {
410
+ signalBatch: {
411
+ signals
412
+ }
413
+ }
414
+ });
415
+ } catch (err) {
416
+ if (err instanceof CancelledError || err instanceof ContextDisposedError || err instanceof Error && err.message?.includes("CANCELLED")) {
417
+ return;
418
+ }
419
+ log.info("signal message failed to deliver", {
420
+ err
421
+ }, {
422
+ F: __dxlog_file,
423
+ L: 394,
424
+ S: this,
425
+ C: (f, a) => f(...a)
426
+ });
427
+ await this.close(new ConnectivityError("signal message failed to deliver", err));
428
+ }
429
+ }
430
+ /**
431
+ * Receive a signal from the remote peer.
432
+ */
433
+ async signal(msg) {
434
+ invariant(msg.sessionId, void 0, {
435
+ F: __dxlog_file,
436
+ L: 403,
437
+ S: this,
438
+ A: [
439
+ "msg.sessionId",
440
+ ""
441
+ ]
442
+ });
443
+ if (!msg.sessionId.equals(this.sessionId)) {
444
+ log("dropping signal for incorrect session id", void 0, {
445
+ F: __dxlog_file,
446
+ L: 405,
447
+ S: this,
448
+ C: (f, a) => f(...a)
449
+ });
450
+ return;
451
+ }
452
+ invariant(msg.data.signal || msg.data.signalBatch, void 0, {
453
+ F: __dxlog_file,
454
+ L: 408,
455
+ S: this,
456
+ A: [
457
+ "msg.data.signal || msg.data.signalBatch",
458
+ ""
459
+ ]
460
+ });
461
+ invariant(msg.author.peerKey === this.remoteInfo.peerKey, void 0, {
462
+ F: __dxlog_file,
463
+ L: 409,
464
+ S: this,
465
+ A: [
466
+ "msg.author.peerKey === this.remoteInfo.peerKey",
467
+ ""
468
+ ]
469
+ });
470
+ invariant(msg.recipient.peerKey === this.localInfo.peerKey, void 0, {
471
+ F: __dxlog_file,
472
+ L: 410,
473
+ S: this,
474
+ A: [
475
+ "msg.recipient.peerKey === this.localInfo.peerKey",
476
+ ""
477
+ ]
478
+ });
479
+ const signals = msg.data.signalBatch ? msg.data.signalBatch.signals ?? [] : [
480
+ msg.data.signal
481
+ ];
482
+ for (const signal of signals) {
483
+ if (!signal) {
484
+ continue;
485
+ }
486
+ if ([
487
+ "CREATED",
488
+ "INITIAL"
489
+ ].includes(this.state)) {
490
+ log("buffered signal", {
491
+ peerId: this.localInfo,
492
+ remoteId: this.remoteInfo,
493
+ msg: msg.data
494
+ }, {
495
+ F: __dxlog_file,
496
+ L: 419,
497
+ S: this,
498
+ C: (f, a) => f(...a)
499
+ });
500
+ this._incomingSignalBuffer.push(signal);
501
+ } else {
502
+ invariant(this._transport, "Connection not ready to accept signals.", {
503
+ F: __dxlog_file,
504
+ L: 422,
505
+ S: this,
506
+ A: [
507
+ "this._transport",
508
+ "'Connection not ready to accept signals.'"
509
+ ]
510
+ });
511
+ log("received signal", {
512
+ peerId: this.localInfo,
513
+ remoteId: this.remoteInfo,
514
+ msg: msg.data
515
+ }, {
516
+ F: __dxlog_file,
517
+ L: 423,
518
+ S: this,
519
+ C: (f, a) => f(...a)
520
+ });
521
+ await this._transport.onSignal(signal);
522
+ }
523
+ }
524
+ }
525
+ initiate() {
526
+ this._changeState("INITIAL");
527
+ }
528
+ _changeState(state) {
529
+ log("stateChanged", {
530
+ from: this._state,
531
+ to: state,
532
+ peerId: this.localInfo
533
+ }, {
534
+ F: __dxlog_file,
535
+ L: 434,
536
+ S: this,
537
+ C: (f, a) => f(...a)
538
+ });
539
+ invariant(state !== this._state, "Already in this state.", {
540
+ F: __dxlog_file,
541
+ L: 435,
542
+ S: this,
543
+ A: [
544
+ "state !== this._state",
545
+ "'Already in this state.'"
546
+ ]
547
+ });
548
+ this._state = state;
549
+ this.stateChanged.emit(state);
550
+ }
551
+ async _emitTransportStats() {
552
+ const stats = await this.transport?.getStats();
553
+ if (stats) {
554
+ this.transportStats.emit(stats);
555
+ }
556
+ }
557
+ };
558
+ _ts_decorate([
559
+ logInfo
560
+ ], Connection.prototype, "sessionIdString", null);
561
+ _ts_decorate([
562
+ synchronized
563
+ ], Connection.prototype, "abort", null);
564
+ _ts_decorate([
565
+ synchronized
566
+ ], Connection.prototype, "close", null);
567
+
568
+ // packages/core/mesh/network-manager/src/signal/ice.ts
569
+ import { asyncTimeout } from "@dxos/async";
570
+ import { log as log2 } from "@dxos/log";
571
+ import { isNotNullOrUndefined } from "@dxos/util";
572
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/signal/ice.ts";
573
+ var createIceProvider = (iceProviders) => {
574
+ let cachedIceServers;
575
+ return {
576
+ getIceServers: async () => {
577
+ if (cachedIceServers) {
578
+ return cachedIceServers;
579
+ }
580
+ cachedIceServers = (await Promise.all(iceProviders.map(({ urls }) => asyncTimeout(fetch(urls, {
581
+ method: "GET"
582
+ }), 1e4).then((response) => response.json()).catch((err) => {
583
+ const isDev = typeof window !== "undefined" && window.location.href.includes("localhost");
584
+ if (!isDev) {
585
+ log2.error("Failed to fetch ICE servers from provider", {
586
+ urls,
587
+ err
588
+ }, {
589
+ F: __dxlog_file2,
590
+ L: 30,
591
+ S: void 0,
592
+ C: (f, a) => f(...a)
593
+ });
594
+ }
595
+ })))).filter(isNotNullOrUndefined).map(({ iceServers }) => iceServers).flat();
596
+ return cachedIceServers;
597
+ }
598
+ };
599
+ };
600
+
601
+ // packages/core/mesh/network-manager/src/signal/swarm-messenger.ts
602
+ import { Context as Context2 } from "@dxos/context";
603
+ import { invariant as invariant2 } from "@dxos/invariant";
604
+ import { PublicKey as PublicKey2 } from "@dxos/keys";
605
+ import { log as log3 } from "@dxos/log";
606
+ import { TimeoutError as TimeoutError2 } from "@dxos/protocols";
607
+ import { schema } from "@dxos/protocols/proto";
608
+ import { ComplexMap } from "@dxos/util";
609
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/signal/swarm-messenger.ts";
610
+ var SwarmMessage = schema.getCodecForType("dxos.mesh.swarm.SwarmMessage");
611
+ var SwarmMessenger = class {
612
+ constructor({ sendMessage, onSignal, onOffer, topic }) {
613
+ this._ctx = new Context2(void 0, {
614
+ F: __dxlog_file3,
615
+ L: 35
616
+ });
617
+ this._offerRecords = new ComplexMap((key) => key.toHex());
618
+ this._sendMessage = sendMessage;
619
+ this._onSignal = onSignal;
620
+ this._onOffer = onOffer;
621
+ this._topic = topic;
622
+ }
623
+ async receiveMessage({ author, recipient, payload }) {
624
+ if (payload.type_url !== "dxos.mesh.swarm.SwarmMessage") {
625
+ return;
626
+ }
627
+ const message = SwarmMessage.decode(payload.value);
628
+ if (!this._topic.equals(message.topic)) {
629
+ return;
630
+ }
631
+ log3("received", {
632
+ from: author,
633
+ to: recipient,
634
+ msg: message
635
+ }, {
636
+ F: __dxlog_file3,
637
+ L: 71,
638
+ S: this,
639
+ C: (f, a) => f(...a)
640
+ });
641
+ if (message.data?.offer) {
642
+ await this._handleOffer({
643
+ author,
644
+ recipient,
645
+ message
646
+ });
647
+ } else if (message.data?.answer) {
648
+ await this._resolveAnswers(message);
649
+ } else if (message.data?.signal) {
650
+ await this._handleSignal({
651
+ author,
652
+ recipient,
653
+ message
654
+ });
655
+ } else if (message.data?.signalBatch) {
656
+ await this._handleSignal({
657
+ author,
658
+ recipient,
659
+ message
660
+ });
661
+ } else {
662
+ log3.warn("unknown message", {
663
+ message
664
+ }, {
665
+ F: __dxlog_file3,
666
+ L: 82,
667
+ S: this,
668
+ C: (f, a) => f(...a)
669
+ });
670
+ }
671
+ }
672
+ async signal(message) {
673
+ invariant2(message.data?.signal || message.data?.signalBatch, "Invalid message", {
674
+ F: __dxlog_file3,
675
+ L: 87,
676
+ S: this,
677
+ A: [
678
+ "message.data?.signal || message.data?.signalBatch",
679
+ "'Invalid message'"
680
+ ]
681
+ });
682
+ await this._sendReliableMessage({
683
+ author: message.author,
684
+ recipient: message.recipient,
685
+ message
686
+ });
687
+ }
688
+ async offer(message) {
689
+ const networkMessage = {
690
+ ...message,
691
+ messageId: PublicKey2.random()
692
+ };
693
+ return new Promise((resolve, reject) => {
694
+ this._offerRecords.set(networkMessage.messageId, {
695
+ resolve
696
+ });
697
+ this._sendReliableMessage({
698
+ author: message.author,
699
+ recipient: message.recipient,
700
+ message: networkMessage
701
+ }).catch((err) => reject(err));
702
+ });
703
+ }
704
+ async _sendReliableMessage({ author, recipient, message }) {
705
+ const networkMessage = {
706
+ ...message,
707
+ // Setting unique message_id if it not specified yet.
708
+ messageId: message.messageId ?? PublicKey2.random()
709
+ };
710
+ log3("sending", {
711
+ from: author,
712
+ to: recipient,
713
+ msg: networkMessage
714
+ }, {
715
+ F: __dxlog_file3,
716
+ L: 125,
717
+ S: this,
718
+ C: (f, a) => f(...a)
719
+ });
720
+ await this._sendMessage({
721
+ author,
722
+ recipient,
723
+ payload: {
724
+ type_url: "dxos.mesh.swarm.SwarmMessage",
725
+ value: SwarmMessage.encode(networkMessage)
726
+ }
727
+ });
728
+ }
729
+ async _resolveAnswers(message) {
730
+ invariant2(message.data?.answer?.offerMessageId, "No offerMessageId", {
731
+ F: __dxlog_file3,
732
+ L: 137,
733
+ S: this,
734
+ A: [
735
+ "message.data?.answer?.offerMessageId",
736
+ "'No offerMessageId'"
737
+ ]
738
+ });
739
+ const offerRecord = this._offerRecords.get(message.data.answer.offerMessageId);
740
+ if (offerRecord) {
741
+ this._offerRecords.delete(message.data.answer.offerMessageId);
742
+ invariant2(message.data?.answer, "No answer", {
743
+ F: __dxlog_file3,
744
+ L: 141,
745
+ S: this,
746
+ A: [
747
+ "message.data?.answer",
748
+ "'No answer'"
749
+ ]
750
+ });
751
+ log3("resolving", {
752
+ answer: message.data.answer
753
+ }, {
754
+ F: __dxlog_file3,
755
+ L: 142,
756
+ S: this,
757
+ C: (f, a) => f(...a)
758
+ });
759
+ offerRecord.resolve(message.data.answer);
760
+ }
761
+ }
762
+ async _handleOffer({ author, recipient, message }) {
763
+ invariant2(message.data.offer, "No offer", {
764
+ F: __dxlog_file3,
765
+ L: 156,
766
+ S: this,
767
+ A: [
768
+ "message.data.offer",
769
+ "'No offer'"
770
+ ]
771
+ });
772
+ const offerMessage = {
773
+ author,
774
+ recipient,
775
+ ...message,
776
+ data: {
777
+ offer: message.data.offer
778
+ }
779
+ };
780
+ const answer = await this._onOffer(offerMessage);
781
+ answer.offerMessageId = message.messageId;
782
+ try {
783
+ await this._sendReliableMessage({
784
+ author: recipient,
785
+ recipient: author,
786
+ message: {
787
+ topic: message.topic,
788
+ sessionId: message.sessionId,
789
+ data: {
790
+ answer
791
+ }
792
+ }
793
+ });
794
+ } catch (err) {
795
+ if (err instanceof TimeoutError2) {
796
+ log3.info("timeout sending answer to offer", {
797
+ err
798
+ }, {
799
+ F: __dxlog_file3,
800
+ L: 177,
801
+ S: this,
802
+ C: (f, a) => f(...a)
803
+ });
804
+ } else {
805
+ log3.info("error sending answer to offer", {
806
+ err
807
+ }, {
808
+ F: __dxlog_file3,
809
+ L: 179,
810
+ S: this,
811
+ C: (f, a) => f(...a)
812
+ });
813
+ }
814
+ }
815
+ }
816
+ async _handleSignal({ author, recipient, message }) {
817
+ invariant2(message.messageId, void 0, {
818
+ F: __dxlog_file3,
819
+ L: 193,
820
+ S: this,
821
+ A: [
822
+ "message.messageId",
823
+ ""
824
+ ]
825
+ });
826
+ invariant2(message.data.signal || message.data.signalBatch, "Invalid message", {
827
+ F: __dxlog_file3,
828
+ L: 194,
829
+ S: this,
830
+ A: [
831
+ "message.data.signal || message.data.signalBatch",
832
+ "'Invalid message'"
833
+ ]
834
+ });
835
+ const signalMessage = {
836
+ author,
837
+ recipient,
838
+ ...message,
839
+ data: {
840
+ signal: message.data.signal,
841
+ signalBatch: message.data.signalBatch
842
+ }
843
+ };
844
+ await this._onSignal(signalMessage);
845
+ }
846
+ };
847
+
848
+ // packages/core/mesh/network-manager/src/swarm/swarm.ts
849
+ import { Event as Event3, scheduleTask as scheduleTask3, sleep as sleep2, synchronized as synchronized3 } from "@dxos/async";
850
+ import { Context as Context4 } from "@dxos/context";
851
+ import { ErrorStream as ErrorStream2 } from "@dxos/debug";
852
+ import { invariant as invariant4 } from "@dxos/invariant";
853
+ import { PublicKey as PublicKey4 } from "@dxos/keys";
854
+ import { log as log5, logInfo as logInfo2 } from "@dxos/log";
855
+ import { PeerInfoHash } from "@dxos/messaging";
856
+ import { trace as trace2 } from "@dxos/protocols";
857
+ import { ComplexMap as ComplexMap2, isNotNullOrUndefined as isNotNullOrUndefined2 } from "@dxos/util";
858
+
859
+ // packages/core/mesh/network-manager/src/swarm/peer.ts
860
+ import { Event as Event2, scheduleTask as scheduleTask2, synchronized as synchronized2 } from "@dxos/async";
861
+ import { Context as Context3 } from "@dxos/context";
862
+ import { invariant as invariant3 } from "@dxos/invariant";
863
+ import { PublicKey as PublicKey3 } from "@dxos/keys";
864
+ import { log as log4 } from "@dxos/log";
865
+ import { CancelledError as CancelledError2, SystemError } from "@dxos/protocols";
866
+ function _ts_decorate2(decorators, target, key, desc) {
867
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
868
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
869
+ 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;
870
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
871
+ }
872
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/swarm/peer.ts";
873
+ var ConnectionDisplacedError = class extends SystemError {
874
+ constructor() {
875
+ super("Connection displaced by remote initiator.");
876
+ }
877
+ };
878
+ var CONNECTION_COUNTS_STABLE_AFTER = 5e3;
879
+ var Peer = class {
880
+ constructor(remoteInfo, topic, localInfo, _signalMessaging, _protocolProvider, _transportFactory, _connectionLimiter, _callbacks) {
881
+ this.remoteInfo = remoteInfo;
882
+ this.topic = topic;
883
+ this.localInfo = localInfo;
884
+ this._signalMessaging = _signalMessaging;
885
+ this._protocolProvider = _protocolProvider;
886
+ this._transportFactory = _transportFactory;
887
+ this._connectionLimiter = _connectionLimiter;
888
+ this._callbacks = _callbacks;
889
+ this._availableAfter = 0;
890
+ this.availableToConnect = true;
891
+ this._ctx = new Context3(void 0, {
892
+ F: __dxlog_file4,
893
+ L: 80
894
+ });
895
+ this.advertizing = false;
896
+ this.initiating = false;
897
+ this.connectionDisplaced = new Event2();
898
+ }
899
+ /**
900
+ * Respond to remote offer.
901
+ */
902
+ async onOffer(message) {
903
+ const remote = message.author;
904
+ if (this.connection && ![
905
+ ConnectionState.CREATED,
906
+ ConnectionState.INITIAL,
907
+ ConnectionState.CONNECTING
908
+ ].includes(this.connection.state)) {
909
+ log4.info(`received offer when connection already in ${this.connection.state} state`, void 0, {
910
+ F: __dxlog_file4,
911
+ L: 115,
912
+ S: this,
913
+ C: (f, a) => f(...a)
914
+ });
915
+ return {
916
+ accept: false
917
+ };
918
+ }
919
+ if (this.connection || this.initiating) {
920
+ if (remote.peerKey < this.localInfo.peerKey) {
921
+ log4("close local connection", {
922
+ localPeer: this.localInfo,
923
+ topic: this.topic,
924
+ remotePeer: this.remoteInfo,
925
+ sessionId: this.connection?.sessionId
926
+ }, {
927
+ F: __dxlog_file4,
928
+ L: 124,
929
+ S: this,
930
+ C: (f, a) => f(...a)
931
+ });
932
+ if (this.connection) {
933
+ await this.closeConnection(new ConnectionDisplacedError());
934
+ }
935
+ } else {
936
+ return {
937
+ accept: false
938
+ };
939
+ }
940
+ }
941
+ if (await this._callbacks.onOffer(remote)) {
942
+ if (!this.connection) {
943
+ invariant3(message.sessionId, void 0, {
944
+ F: __dxlog_file4,
945
+ L: 144,
946
+ S: this,
947
+ A: [
948
+ "message.sessionId",
949
+ ""
950
+ ]
951
+ });
952
+ const connection = this._createConnection(false, message.sessionId);
953
+ try {
954
+ await this._connectionLimiter.connecting(message.sessionId);
955
+ connection.initiate();
956
+ await connection.openConnection();
957
+ } catch (err) {
958
+ if (!(err instanceof CancelledError2)) {
959
+ log4.info("connection error", {
960
+ topic: this.topic,
961
+ peerId: this.localInfo,
962
+ remoteId: this.remoteInfo,
963
+ err
964
+ }, {
965
+ F: __dxlog_file4,
966
+ L: 154,
967
+ S: this,
968
+ C: (f, a) => f(...a)
969
+ });
970
+ }
971
+ await this.closeConnection(err);
972
+ }
973
+ return {
974
+ accept: true
975
+ };
976
+ }
977
+ }
978
+ return {
979
+ accept: false
980
+ };
981
+ }
982
+ /**
983
+ * Initiate a connection to the remote peer.
984
+ */
985
+ async initiateConnection() {
986
+ invariant3(!this.initiating, "Initiation in progress.", {
987
+ F: __dxlog_file4,
988
+ L: 171,
989
+ S: this,
990
+ A: [
991
+ "!this.initiating",
992
+ "'Initiation in progress.'"
993
+ ]
994
+ });
995
+ invariant3(!this.connection, "Already connected.", {
996
+ F: __dxlog_file4,
997
+ L: 172,
998
+ S: this,
999
+ A: [
1000
+ "!this.connection",
1001
+ "'Already connected.'"
1002
+ ]
1003
+ });
1004
+ const sessionId = PublicKey3.random();
1005
+ log4("initiating...", {
1006
+ local: this.localInfo,
1007
+ topic: this.topic,
1008
+ remote: this.remoteInfo,
1009
+ sessionId
1010
+ }, {
1011
+ F: __dxlog_file4,
1012
+ L: 174,
1013
+ S: this,
1014
+ C: (f, a) => f(...a)
1015
+ });
1016
+ const connection = this._createConnection(true, sessionId);
1017
+ this.initiating = true;
1018
+ let answer;
1019
+ try {
1020
+ await this._connectionLimiter.connecting(sessionId);
1021
+ connection.initiate();
1022
+ answer = await this._signalMessaging.offer({
1023
+ author: this.localInfo,
1024
+ recipient: this.remoteInfo,
1025
+ sessionId,
1026
+ topic: this.topic,
1027
+ data: {
1028
+ offer: {}
1029
+ }
1030
+ });
1031
+ log4("received", {
1032
+ answer,
1033
+ topic: this.topic,
1034
+ local: this.localInfo,
1035
+ remote: this.remoteInfo
1036
+ }, {
1037
+ F: __dxlog_file4,
1038
+ L: 191,
1039
+ S: this,
1040
+ C: (f, a) => f(...a)
1041
+ });
1042
+ if (connection.state !== ConnectionState.INITIAL) {
1043
+ log4("ignoring response", void 0, {
1044
+ F: __dxlog_file4,
1045
+ L: 193,
1046
+ S: this,
1047
+ C: (f, a) => f(...a)
1048
+ });
1049
+ return;
1050
+ }
1051
+ } catch (err) {
1052
+ log4("initiation error: send offer", {
1053
+ err,
1054
+ topic: this.topic,
1055
+ local: this.localInfo,
1056
+ remote: this.remoteInfo
1057
+ }, {
1058
+ F: __dxlog_file4,
1059
+ L: 197,
1060
+ S: this,
1061
+ C: (f, a) => f(...a)
1062
+ });
1063
+ await connection.abort(err);
1064
+ throw err;
1065
+ } finally {
1066
+ this.initiating = false;
1067
+ }
1068
+ try {
1069
+ if (!answer.accept) {
1070
+ this._callbacks.onRejected();
1071
+ return;
1072
+ }
1073
+ } catch (err) {
1074
+ log4("initiation error: accept answer", {
1075
+ err,
1076
+ topic: this.topic,
1077
+ local: this.localInfo,
1078
+ remote: this.remoteInfo
1079
+ }, {
1080
+ F: __dxlog_file4,
1081
+ L: 210,
1082
+ S: this,
1083
+ C: (f, a) => f(...a)
1084
+ });
1085
+ await connection.abort(err);
1086
+ throw err;
1087
+ } finally {
1088
+ this.initiating = false;
1089
+ }
1090
+ try {
1091
+ log4("opening connection as initiator", void 0, {
1092
+ F: __dxlog_file4,
1093
+ L: 223,
1094
+ S: this,
1095
+ C: (f, a) => f(...a)
1096
+ });
1097
+ await connection.openConnection();
1098
+ this._callbacks.onAccepted();
1099
+ } catch (err) {
1100
+ log4("initiation error: open connection", {
1101
+ err,
1102
+ topic: this.topic,
1103
+ local: this.localInfo,
1104
+ remote: this.remoteInfo
1105
+ }, {
1106
+ F: __dxlog_file4,
1107
+ L: 227,
1108
+ S: this,
1109
+ C: (f, a) => f(...a)
1110
+ });
1111
+ log4.warn("closing connection due to unhandled error on openConnection", {
1112
+ err
1113
+ }, {
1114
+ F: __dxlog_file4,
1115
+ L: 234,
1116
+ S: this,
1117
+ C: (f, a) => f(...a)
1118
+ });
1119
+ await this.closeConnection(err);
1120
+ throw err;
1121
+ } finally {
1122
+ this.initiating = false;
1123
+ }
1124
+ }
1125
+ /**
1126
+ * Create new connection.
1127
+ * Either we're initiating a connection or creating one in response to an offer from the other peer.
1128
+ */
1129
+ _createConnection(initiator, sessionId) {
1130
+ log4("creating connection", {
1131
+ topic: this.topic,
1132
+ peerId: this.localInfo,
1133
+ remoteId: this.remoteInfo,
1134
+ initiator,
1135
+ sessionId
1136
+ }, {
1137
+ F: __dxlog_file4,
1138
+ L: 248,
1139
+ S: this,
1140
+ C: (f, a) => f(...a)
1141
+ });
1142
+ invariant3(!this.connection, "Already connected.", {
1143
+ F: __dxlog_file4,
1144
+ L: 255,
1145
+ S: this,
1146
+ A: [
1147
+ "!this.connection",
1148
+ "'Already connected.'"
1149
+ ]
1150
+ });
1151
+ const connection = new Connection(
1152
+ this.topic,
1153
+ this.localInfo,
1154
+ this.remoteInfo,
1155
+ sessionId,
1156
+ initiator,
1157
+ this._signalMessaging,
1158
+ // TODO(dmaretskyi): Init only when connection is established.
1159
+ this._protocolProvider({
1160
+ initiator,
1161
+ localPeerId: PublicKey3.from(this.localInfo.peerKey),
1162
+ remotePeerId: PublicKey3.from(this.remoteInfo.peerKey),
1163
+ topic: this.topic
1164
+ }),
1165
+ this._transportFactory,
1166
+ {
1167
+ onConnected: () => {
1168
+ this.availableToConnect = true;
1169
+ this._lastConnectionTime = Date.now();
1170
+ this._callbacks.onConnected();
1171
+ this._connectionLimiter.doneConnecting(sessionId);
1172
+ log4.trace("dxos.mesh.connection.connected", {
1173
+ topic: this.topic,
1174
+ localPeerId: this.localInfo,
1175
+ remotePeerId: this.remoteInfo,
1176
+ sessionId,
1177
+ initiator
1178
+ }, {
1179
+ F: __dxlog_file4,
1180
+ L: 279,
1181
+ S: this,
1182
+ C: (f, a) => f(...a)
1183
+ });
1184
+ },
1185
+ onClosed: (err) => {
1186
+ log4("connection closed", {
1187
+ topic: this.topic,
1188
+ peerId: this.localInfo,
1189
+ remoteId: this.remoteInfo,
1190
+ initiator
1191
+ }, {
1192
+ F: __dxlog_file4,
1193
+ L: 288,
1194
+ S: this,
1195
+ C: (f, a) => f(...a)
1196
+ });
1197
+ this._connectionLimiter.doneConnecting(sessionId);
1198
+ invariant3(this.connection === connection, "Connection mismatch (race condition).", {
1199
+ F: __dxlog_file4,
1200
+ L: 293,
1201
+ S: this,
1202
+ A: [
1203
+ "this.connection === connection",
1204
+ "'Connection mismatch (race condition).'"
1205
+ ]
1206
+ });
1207
+ log4.trace("dxos.mesh.connection.closed", {
1208
+ topic: this.topic,
1209
+ localPeerId: this.localInfo,
1210
+ remotePeerId: this.remoteInfo,
1211
+ sessionId,
1212
+ initiator
1213
+ }, {
1214
+ F: __dxlog_file4,
1215
+ L: 295,
1216
+ S: this,
1217
+ C: (f, a) => f(...a)
1218
+ });
1219
+ if (err instanceof ConnectionDisplacedError) {
1220
+ this.connectionDisplaced.emit(this.connection);
1221
+ } else {
1222
+ if (this._lastConnectionTime && this._lastConnectionTime + CONNECTION_COUNTS_STABLE_AFTER < Date.now()) {
1223
+ this._availableAfter = 0;
1224
+ } else {
1225
+ this.availableToConnect = false;
1226
+ this._availableAfter = increaseInterval(this._availableAfter);
1227
+ }
1228
+ this._callbacks.onDisconnected();
1229
+ scheduleTask2(this._connectionCtx, () => {
1230
+ this.availableToConnect = true;
1231
+ this._callbacks.onPeerAvailable();
1232
+ }, this._availableAfter);
1233
+ }
1234
+ this.connection = void 0;
1235
+ }
1236
+ }
1237
+ );
1238
+ this._callbacks.onInitiated(connection);
1239
+ void this._connectionCtx?.dispose();
1240
+ this._connectionCtx = this._ctx.derive();
1241
+ connection.errors.handle((err) => {
1242
+ log4.info("connection error, closing", {
1243
+ topic: this.topic,
1244
+ peerId: this.localInfo,
1245
+ remoteId: this.remoteInfo,
1246
+ initiator,
1247
+ err
1248
+ }, {
1249
+ F: __dxlog_file4,
1250
+ L: 335,
1251
+ S: this,
1252
+ C: (f, a) => f(...a)
1253
+ });
1254
+ log4.trace("dxos.mesh.connection.error", {
1255
+ topic: this.topic,
1256
+ localPeerId: this.localInfo,
1257
+ remotePeerId: this.remoteInfo,
1258
+ sessionId,
1259
+ initiator,
1260
+ err
1261
+ }, {
1262
+ F: __dxlog_file4,
1263
+ L: 342,
1264
+ S: this,
1265
+ C: (f, a) => f(...a)
1266
+ });
1267
+ void this.closeConnection(err);
1268
+ });
1269
+ this.connection = connection;
1270
+ return connection;
1271
+ }
1272
+ async closeConnection(err) {
1273
+ if (!this.connection) {
1274
+ return;
1275
+ }
1276
+ const connection = this.connection;
1277
+ log4("closing...", {
1278
+ peerId: this.remoteInfo,
1279
+ sessionId: connection.sessionId
1280
+ }, {
1281
+ F: __dxlog_file4,
1282
+ L: 367,
1283
+ S: this,
1284
+ C: (f, a) => f(...a)
1285
+ });
1286
+ await connection.close(err);
1287
+ log4("closed", {
1288
+ peerId: this.remoteInfo,
1289
+ sessionId: connection.sessionId
1290
+ }, {
1291
+ F: __dxlog_file4,
1292
+ L: 373,
1293
+ S: this,
1294
+ C: (f, a) => f(...a)
1295
+ });
1296
+ }
1297
+ async onSignal(message) {
1298
+ if (!this.connection) {
1299
+ log4("dropping signal message for non-existent connection", {
1300
+ message
1301
+ }, {
1302
+ F: __dxlog_file4,
1303
+ L: 378,
1304
+ S: this,
1305
+ C: (f, a) => f(...a)
1306
+ });
1307
+ return;
1308
+ }
1309
+ await this.connection.signal(message);
1310
+ }
1311
+ async safeDestroy(reason) {
1312
+ await this._ctx.dispose();
1313
+ log4("Destroying peer", {
1314
+ peerId: this.remoteInfo,
1315
+ topic: this.topic
1316
+ }, {
1317
+ F: __dxlog_file4,
1318
+ L: 388,
1319
+ S: this,
1320
+ C: (f, a) => f(...a)
1321
+ });
1322
+ await this?.connection?.close(reason);
1323
+ }
1324
+ };
1325
+ _ts_decorate2([
1326
+ synchronized2
1327
+ ], Peer.prototype, "safeDestroy", null);
1328
+ var increaseInterval = (interval) => {
1329
+ if (interval === 0) {
1330
+ return 50;
1331
+ } else if (interval < 500) {
1332
+ return 500;
1333
+ } else if (interval < 1e3) {
1334
+ return 1e3;
1335
+ } else if (interval < 5e3) {
1336
+ return 5e3;
1337
+ }
1338
+ return 1e4;
1339
+ };
1340
+
1341
+ // packages/core/mesh/network-manager/src/swarm/swarm.ts
1342
+ function _ts_decorate3(decorators, target, key, desc) {
1343
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1344
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1345
+ 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;
1346
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1347
+ }
1348
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/swarm/swarm.ts";
1349
+ var INITIATION_DELAY = 100;
1350
+ var getClassName = (obj) => Object.getPrototypeOf(obj).constructor.name;
1351
+ var Swarm = class {
1352
+ // TODO(burdon): Swarm => Peer.create/destroy =< Connection.open/close
1353
+ // TODO(burdon): Pass in object.
1354
+ constructor(_topic, _ownPeer, _topology, _protocolProvider, _messenger, _transportFactory, _label, _connectionLimiter, _initiationDelay = INITIATION_DELAY) {
1355
+ this._topic = _topic;
1356
+ this._ownPeer = _ownPeer;
1357
+ this._topology = _topology;
1358
+ this._protocolProvider = _protocolProvider;
1359
+ this._messenger = _messenger;
1360
+ this._transportFactory = _transportFactory;
1361
+ this._label = _label;
1362
+ this._connectionLimiter = _connectionLimiter;
1363
+ this._initiationDelay = _initiationDelay;
1364
+ this._ctx = new Context4(void 0, {
1365
+ F: __dxlog_file5,
1366
+ L: 38
1367
+ });
1368
+ this._listeningHandle = void 0;
1369
+ this._peers = new ComplexMap2(PeerInfoHash);
1370
+ this._instanceId = PublicKey4.random().toHex();
1371
+ this.connectionAdded = new Event3();
1372
+ this.disconnected = new Event3();
1373
+ this.connected = new Event3();
1374
+ this.errors = new ErrorStream2();
1375
+ log5.trace("dxos.mesh.swarm.constructor", trace2.begin({
1376
+ id: this._instanceId,
1377
+ data: {
1378
+ topic: this._topic.toHex(),
1379
+ peer: this._ownPeer
1380
+ }
1381
+ }), {
1382
+ F: __dxlog_file5,
1383
+ L: 88,
1384
+ S: this,
1385
+ C: (f, a) => f(...a)
1386
+ });
1387
+ log5("creating swarm", {
1388
+ peerId: _ownPeer
1389
+ }, {
1390
+ F: __dxlog_file5,
1391
+ L: 92,
1392
+ S: this,
1393
+ C: (f, a) => f(...a)
1394
+ });
1395
+ _topology.init(this._getSwarmController());
1396
+ this._swarmMessenger = new SwarmMessenger({
1397
+ sendMessage: async (msg) => await this._messenger.sendMessage(msg),
1398
+ onSignal: async (msg) => await this.onSignal(msg),
1399
+ onOffer: async (msg) => await this.onOffer(msg),
1400
+ topic: this._topic
1401
+ });
1402
+ log5.trace("dxos.mesh.swarm.constructor", trace2.end({
1403
+ id: this._instanceId
1404
+ }), {
1405
+ F: __dxlog_file5,
1406
+ L: 101,
1407
+ S: this,
1408
+ C: (f, a) => f(...a)
1409
+ });
1410
+ }
1411
+ get connections() {
1412
+ return Array.from(this._peers.values()).map((peer) => peer.connection).filter(isNotNullOrUndefined2);
1413
+ }
1414
+ get ownPeerId() {
1415
+ return PublicKey4.from(this._ownPeer.peerKey);
1416
+ }
1417
+ get ownPeer() {
1418
+ return this._ownPeer;
1419
+ }
1420
+ /**
1421
+ * Custom label assigned to this swarm. Used in devtools to display human-readable names for swarms.
1422
+ */
1423
+ get label() {
1424
+ return this._label;
1425
+ }
1426
+ get topic() {
1427
+ return this._topic;
1428
+ }
1429
+ async open() {
1430
+ invariant4(!this._listeningHandle, void 0, {
1431
+ F: __dxlog_file5,
1432
+ L: 132,
1433
+ S: this,
1434
+ A: [
1435
+ "!this._listeningHandle",
1436
+ ""
1437
+ ]
1438
+ });
1439
+ this._listeningHandle = await this._messenger.listen({
1440
+ peer: this._ownPeer,
1441
+ payloadType: "dxos.mesh.swarm.SwarmMessage",
1442
+ onMessage: async (message) => {
1443
+ await this._swarmMessenger.receiveMessage(message).catch((err) => log5.info("Error while receiving message", {
1444
+ err
1445
+ }, {
1446
+ F: __dxlog_file5,
1447
+ L: 140,
1448
+ S: this,
1449
+ C: (f, a) => f(...a)
1450
+ }));
1451
+ }
1452
+ });
1453
+ }
1454
+ async destroy() {
1455
+ log5("destroying...", void 0, {
1456
+ F: __dxlog_file5,
1457
+ L: 146,
1458
+ S: this,
1459
+ C: (f, a) => f(...a)
1460
+ });
1461
+ await this._listeningHandle?.unsubscribe();
1462
+ this._listeningHandle = void 0;
1463
+ await this._ctx.dispose();
1464
+ await this._topology.destroy();
1465
+ await Promise.all(Array.from(this._peers.keys()).map((key) => this._destroyPeer(key, "swarm destroyed")));
1466
+ log5("destroyed", void 0, {
1467
+ F: __dxlog_file5,
1468
+ L: 153,
1469
+ S: this,
1470
+ C: (f, a) => f(...a)
1471
+ });
1472
+ }
1473
+ async setTopology(topology) {
1474
+ invariant4(!this._ctx.disposed, "Swarm is offline", {
1475
+ F: __dxlog_file5,
1476
+ L: 157,
1477
+ S: this,
1478
+ A: [
1479
+ "!this._ctx.disposed",
1480
+ "'Swarm is offline'"
1481
+ ]
1482
+ });
1483
+ if (topology === this._topology) {
1484
+ return;
1485
+ }
1486
+ log5("setting topology", {
1487
+ previous: getClassName(this._topology),
1488
+ topology: getClassName(topology)
1489
+ }, {
1490
+ F: __dxlog_file5,
1491
+ L: 161,
1492
+ S: this,
1493
+ C: (f, a) => f(...a)
1494
+ });
1495
+ await this._topology.destroy();
1496
+ this._topology = topology;
1497
+ this._topology.init(this._getSwarmController());
1498
+ this._topology.update();
1499
+ }
1500
+ onSwarmEvent(swarmEvent) {
1501
+ log5("swarm event", {
1502
+ swarmEvent
1503
+ }, {
1504
+ F: __dxlog_file5,
1505
+ L: 174,
1506
+ S: this,
1507
+ C: (f, a) => f(...a)
1508
+ });
1509
+ if (this._ctx.disposed) {
1510
+ log5("swarm event ignored for disposed swarm", void 0, {
1511
+ F: __dxlog_file5,
1512
+ L: 177,
1513
+ S: this,
1514
+ C: (f, a) => f(...a)
1515
+ });
1516
+ return;
1517
+ }
1518
+ if (swarmEvent.peerAvailable) {
1519
+ const peerId = swarmEvent.peerAvailable.peer.peerKey;
1520
+ if (peerId !== this._ownPeer.peerKey) {
1521
+ log5("new peer", {
1522
+ peerId
1523
+ }, {
1524
+ F: __dxlog_file5,
1525
+ L: 184,
1526
+ S: this,
1527
+ C: (f, a) => f(...a)
1528
+ });
1529
+ const peer = this._getOrCreatePeer(swarmEvent.peerAvailable.peer);
1530
+ peer.advertizing = true;
1531
+ }
1532
+ } else if (swarmEvent.peerLeft) {
1533
+ const peer = this._peers.get(swarmEvent.peerLeft.peer);
1534
+ if (peer) {
1535
+ peer.advertizing = false;
1536
+ if (peer.connection?.state !== ConnectionState.CONNECTED) {
1537
+ void this._destroyPeer(swarmEvent.peerLeft.peer, "peer left").catch((err) => log5.catch(err, void 0, {
1538
+ F: __dxlog_file5,
1539
+ L: 194,
1540
+ S: this,
1541
+ C: (f, a) => f(...a)
1542
+ }));
1543
+ }
1544
+ } else {
1545
+ log5("received peerLeft but no peer found", {
1546
+ peer: swarmEvent.peerLeft.peer.peerKey
1547
+ }, {
1548
+ F: __dxlog_file5,
1549
+ L: 197,
1550
+ S: this,
1551
+ C: (f, a) => f(...a)
1552
+ });
1553
+ }
1554
+ }
1555
+ this._topology.update();
1556
+ }
1557
+ async onOffer(message) {
1558
+ log5("offer", {
1559
+ message
1560
+ }, {
1561
+ F: __dxlog_file5,
1562
+ L: 206,
1563
+ S: this,
1564
+ C: (f, a) => f(...a)
1565
+ });
1566
+ if (this._ctx.disposed) {
1567
+ log5("ignored for disposed swarm", void 0, {
1568
+ F: __dxlog_file5,
1569
+ L: 208,
1570
+ S: this,
1571
+ C: (f, a) => f(...a)
1572
+ });
1573
+ return {
1574
+ accept: false
1575
+ };
1576
+ }
1577
+ invariant4(message.author, void 0, {
1578
+ F: __dxlog_file5,
1579
+ L: 213,
1580
+ S: this,
1581
+ A: [
1582
+ "message.author",
1583
+ ""
1584
+ ]
1585
+ });
1586
+ if (message.recipient.peerKey !== this._ownPeer.peerKey) {
1587
+ log5("rejecting offer with incorrect peerId", {
1588
+ message
1589
+ }, {
1590
+ F: __dxlog_file5,
1591
+ L: 215,
1592
+ S: this,
1593
+ C: (f, a) => f(...a)
1594
+ });
1595
+ return {
1596
+ accept: false
1597
+ };
1598
+ }
1599
+ if (!message.topic?.equals(this._topic)) {
1600
+ log5("rejecting offer with incorrect topic", {
1601
+ message
1602
+ }, {
1603
+ F: __dxlog_file5,
1604
+ L: 219,
1605
+ S: this,
1606
+ C: (f, a) => f(...a)
1607
+ });
1608
+ return {
1609
+ accept: false
1610
+ };
1611
+ }
1612
+ const peer = this._getOrCreatePeer(message.author);
1613
+ const answer = await peer.onOffer(message);
1614
+ this._topology.update();
1615
+ return answer;
1616
+ }
1617
+ async onSignal(message) {
1618
+ log5("signal", {
1619
+ message
1620
+ }, {
1621
+ F: __dxlog_file5,
1622
+ L: 230,
1623
+ S: this,
1624
+ C: (f, a) => f(...a)
1625
+ });
1626
+ if (this._ctx.disposed) {
1627
+ log5.info("ignored for offline swarm", void 0, {
1628
+ F: __dxlog_file5,
1629
+ L: 232,
1630
+ S: this,
1631
+ C: (f, a) => f(...a)
1632
+ });
1633
+ return;
1634
+ }
1635
+ invariant4(message.recipient.peerKey === this._ownPeer.peerKey, `Invalid signal peer id expected=${this.ownPeerId}, actual=${message.recipient}`, {
1636
+ F: __dxlog_file5,
1637
+ L: 235,
1638
+ S: this,
1639
+ A: [
1640
+ "message.recipient.peerKey === this._ownPeer.peerKey",
1641
+ "`Invalid signal peer id expected=${this.ownPeerId}, actual=${message.recipient}`"
1642
+ ]
1643
+ });
1644
+ invariant4(message.topic?.equals(this._topic), void 0, {
1645
+ F: __dxlog_file5,
1646
+ L: 239,
1647
+ S: this,
1648
+ A: [
1649
+ "message.topic?.equals(this._topic)",
1650
+ ""
1651
+ ]
1652
+ });
1653
+ invariant4(message.author, void 0, {
1654
+ F: __dxlog_file5,
1655
+ L: 240,
1656
+ S: this,
1657
+ A: [
1658
+ "message.author",
1659
+ ""
1660
+ ]
1661
+ });
1662
+ const peer = this._getOrCreatePeer(message.author);
1663
+ await peer.onSignal(message);
1664
+ }
1665
+ // For debug purposes
1666
+ async goOffline() {
1667
+ await this._ctx.dispose();
1668
+ await Promise.all([
1669
+ ...this._peers.keys()
1670
+ ].map((peerId) => this._destroyPeer(peerId, "goOffline")));
1671
+ }
1672
+ // For debug purposes
1673
+ async goOnline() {
1674
+ this._ctx = new Context4(void 0, {
1675
+ F: __dxlog_file5,
1676
+ L: 256
1677
+ });
1678
+ }
1679
+ _getOrCreatePeer(peerInfo) {
1680
+ invariant4(peerInfo.peerKey, "PeerInfo.peerKey is required", {
1681
+ F: __dxlog_file5,
1682
+ L: 260,
1683
+ S: this,
1684
+ A: [
1685
+ "peerInfo.peerKey",
1686
+ "'PeerInfo.peerKey is required'"
1687
+ ]
1688
+ });
1689
+ let peer = this._peers.get(peerInfo);
1690
+ if (!peer) {
1691
+ peer = new Peer(peerInfo, this._topic, this._ownPeer, this._swarmMessenger, this._protocolProvider, this._transportFactory, this._connectionLimiter, {
1692
+ onInitiated: (connection) => {
1693
+ this.connectionAdded.emit(connection);
1694
+ },
1695
+ onConnected: () => {
1696
+ this.connected.emit(peerInfo);
1697
+ },
1698
+ onDisconnected: async () => {
1699
+ if (this._isUnregistered(peer)) {
1700
+ return;
1701
+ }
1702
+ if (!peer.advertizing) {
1703
+ await this._destroyPeer(peerInfo, "peer disconnected");
1704
+ }
1705
+ this.disconnected.emit(peerInfo);
1706
+ this._topology.update();
1707
+ },
1708
+ onRejected: () => {
1709
+ if (!this._isUnregistered(peer)) {
1710
+ log5("peer rejected connection", {
1711
+ peerInfo
1712
+ }, {
1713
+ F: __dxlog_file5,
1714
+ L: 293,
1715
+ S: this,
1716
+ C: (f, a) => f(...a)
1717
+ });
1718
+ void this._destroyPeer(peerInfo, "peer rejected connection");
1719
+ }
1720
+ },
1721
+ onAccepted: () => {
1722
+ this._topology.update();
1723
+ },
1724
+ onOffer: (remoteId) => {
1725
+ return this._topology.onOffer(PublicKey4.from(remoteId.peerKey));
1726
+ },
1727
+ onPeerAvailable: () => {
1728
+ this._topology.update();
1729
+ }
1730
+ });
1731
+ this._peers.set(peerInfo, peer);
1732
+ }
1733
+ return peer;
1734
+ }
1735
+ async _destroyPeer(peerInfo, reason) {
1736
+ const peer = this._peers.get(peerInfo);
1737
+ invariant4(peer, void 0, {
1738
+ F: __dxlog_file5,
1739
+ L: 316,
1740
+ S: this,
1741
+ A: [
1742
+ "peer",
1743
+ ""
1744
+ ]
1745
+ });
1746
+ this._peers.delete(peerInfo);
1747
+ await peer.safeDestroy(new Error(reason));
1748
+ }
1749
+ _getSwarmController() {
1750
+ return {
1751
+ getState: () => ({
1752
+ ownPeerId: PublicKey4.from(this._ownPeer.peerKey),
1753
+ connected: Array.from(this._peers.entries()).filter(([_, peer]) => peer.connection).map(([info]) => PublicKey4.from(info.peerKey)),
1754
+ candidates: Array.from(this._peers.entries()).filter(([_, peer]) => !peer.connection && peer.advertizing && peer.availableToConnect).map(([info]) => PublicKey4.from(info.peerKey)),
1755
+ allPeers: Array.from(this._peers.keys()).map((info) => PublicKey4.from(info.peerKey))
1756
+ }),
1757
+ connect: (peer) => {
1758
+ if (this._ctx.disposed) {
1759
+ return;
1760
+ }
1761
+ scheduleTask3(this._ctx, async () => {
1762
+ try {
1763
+ await this._initiateConnection({
1764
+ peerKey: peer.toHex()
1765
+ });
1766
+ } catch (err) {
1767
+ log5("initiation error", err, {
1768
+ F: __dxlog_file5,
1769
+ L: 343,
1770
+ S: this,
1771
+ C: (f, a) => f(...a)
1772
+ });
1773
+ }
1774
+ });
1775
+ },
1776
+ disconnect: async (peer) => {
1777
+ if (this._ctx.disposed) {
1778
+ return;
1779
+ }
1780
+ scheduleTask3(this._ctx, async () => {
1781
+ await this._closeConnection({
1782
+ peerKey: peer.toHex()
1783
+ });
1784
+ this._topology.update();
1785
+ });
1786
+ }
1787
+ };
1788
+ }
1789
+ /**
1790
+ * Creates a connection then sends message over signal network.
1791
+ */
1792
+ async _initiateConnection(remotePeer) {
1793
+ const ctx = this._ctx;
1794
+ const peer = this._getOrCreatePeer(remotePeer);
1795
+ if (remotePeer.peerKey < this._ownPeer.peerKey) {
1796
+ log5("initiation delay", {
1797
+ remotePeer
1798
+ }, {
1799
+ F: __dxlog_file5,
1800
+ L: 371,
1801
+ S: this,
1802
+ C: (f, a) => f(...a)
1803
+ });
1804
+ await sleep2(this._initiationDelay);
1805
+ }
1806
+ if (ctx.disposed) {
1807
+ return;
1808
+ }
1809
+ if (this._isUnregistered(peer)) {
1810
+ throw new Error("Peer left during initiation delay");
1811
+ }
1812
+ if (peer.connection) {
1813
+ return;
1814
+ }
1815
+ log5("initiating connection...", {
1816
+ remotePeer
1817
+ }, {
1818
+ F: __dxlog_file5,
1819
+ L: 387,
1820
+ S: this,
1821
+ C: (f, a) => f(...a)
1822
+ });
1823
+ await peer.initiateConnection();
1824
+ this._topology.update();
1825
+ log5("initiated", {
1826
+ remotePeer
1827
+ }, {
1828
+ F: __dxlog_file5,
1829
+ L: 390,
1830
+ S: this,
1831
+ C: (f, a) => f(...a)
1832
+ });
1833
+ }
1834
+ async _closeConnection(peerInfo) {
1835
+ const peer = this._peers.get(peerInfo);
1836
+ if (!peer) {
1837
+ return;
1838
+ }
1839
+ await peer.closeConnection();
1840
+ }
1841
+ _isUnregistered(peer) {
1842
+ return !peer || this._peers.get(peer.remoteInfo) !== peer;
1843
+ }
1844
+ };
1845
+ _ts_decorate3([
1846
+ logInfo2
1847
+ ], Swarm.prototype, "_instanceId", void 0);
1848
+ _ts_decorate3([
1849
+ logInfo2
1850
+ ], Swarm.prototype, "ownPeer", null);
1851
+ _ts_decorate3([
1852
+ logInfo2
1853
+ ], Swarm.prototype, "topic", null);
1854
+ _ts_decorate3([
1855
+ synchronized3
1856
+ ], Swarm.prototype, "onSwarmEvent", null);
1857
+ _ts_decorate3([
1858
+ synchronized3
1859
+ ], Swarm.prototype, "onOffer", null);
1860
+ _ts_decorate3([
1861
+ synchronized3
1862
+ ], Swarm.prototype, "goOffline", null);
1863
+ _ts_decorate3([
1864
+ synchronized3
1865
+ ], Swarm.prototype, "goOnline", null);
1866
+
1867
+ // packages/core/mesh/network-manager/src/swarm/swarm-mapper.ts
1868
+ import { Event as Event4, EventSubscriptions } from "@dxos/async";
1869
+ import { PublicKey as PublicKey5 } from "@dxos/keys";
1870
+ import { log as log6 } from "@dxos/log";
1871
+ import { PeerInfoHash as PeerInfoHash2 } from "@dxos/messaging";
1872
+ import { ComplexMap as ComplexMap3 } from "@dxos/util";
1873
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/swarm/swarm-mapper.ts";
1874
+ var SwarmMapper = class {
1875
+ get peers() {
1876
+ return Array.from(this._peers.values());
1877
+ }
1878
+ constructor(_swarm) {
1879
+ this._swarm = _swarm;
1880
+ this._subscriptions = new EventSubscriptions();
1881
+ this._connectionSubscriptions = new ComplexMap3(PeerInfoHash2);
1882
+ this._peers = new ComplexMap3(PeerInfoHash2);
1883
+ this.mapUpdated = new Event4();
1884
+ this._subscriptions.add(_swarm.connectionAdded.on((connection) => {
1885
+ this._update();
1886
+ this._connectionSubscriptions.set(connection.remoteInfo, connection.stateChanged.on(() => {
1887
+ this._update();
1888
+ }));
1889
+ }));
1890
+ this._subscriptions.add(_swarm.disconnected.on((peerId) => {
1891
+ this._connectionSubscriptions.get(peerId)?.();
1892
+ this._connectionSubscriptions.delete(peerId);
1893
+ this._update();
1894
+ }));
1895
+ this._update();
1896
+ }
1897
+ _update() {
1898
+ log6("updating swarm", void 0, {
1899
+ F: __dxlog_file6,
1900
+ L: 73,
1901
+ S: this,
1902
+ C: (f, a) => f(...a)
1903
+ });
1904
+ this._peers.clear();
1905
+ this._peers.set(this._swarm.ownPeer, {
1906
+ id: this._swarm.ownPeerId,
1907
+ state: "ME",
1908
+ connections: []
1909
+ });
1910
+ for (const connection of this._swarm.connections) {
1911
+ this._peers.set(connection.remoteInfo, {
1912
+ id: PublicKey5.from(connection.remoteInfo.peerKey),
1913
+ state: connection.state,
1914
+ connections: [
1915
+ this._swarm.ownPeerId
1916
+ ]
1917
+ });
1918
+ }
1919
+ log6("graph changed", {
1920
+ directConnections: this._swarm.connections.length,
1921
+ totalPeersInSwarm: this._peers.size
1922
+ }, {
1923
+ F: __dxlog_file6,
1924
+ L: 114,
1925
+ S: this,
1926
+ C: (f, a) => f(...a)
1927
+ });
1928
+ this.mapUpdated.emit(Array.from(this._peers.values()));
1929
+ }
1930
+ // TODO(burdon): Async open/close.
1931
+ destroy() {
1932
+ Array.from(this._connectionSubscriptions.values()).forEach((cb) => cb());
1933
+ this._subscriptions.clear();
1934
+ }
1935
+ };
1936
+
1937
+ // packages/core/mesh/network-manager/src/swarm/connection-limiter.ts
1938
+ import { DeferredTask as DeferredTask2 } from "@dxos/async";
1939
+ import { Context as Context5 } from "@dxos/context";
1940
+ import { invariant as invariant5 } from "@dxos/invariant";
1941
+ import { PublicKey as PublicKey6 } from "@dxos/keys";
1942
+ import { log as log7 } from "@dxos/log";
1943
+ import { CancelledError as CancelledError3 } from "@dxos/protocols";
1944
+ import { ComplexMap as ComplexMap4 } from "@dxos/util";
1945
+ var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/swarm/connection-limiter.ts";
1946
+ var MAX_CONCURRENT_INITIATING_CONNECTIONS = 50;
1947
+ var ConnectionLimiter = class {
1948
+ constructor({ maxConcurrentInitConnections = MAX_CONCURRENT_INITIATING_CONNECTIONS } = {}) {
1949
+ this._ctx = new Context5(void 0, {
1950
+ F: __dxlog_file7,
1951
+ L: 23
1952
+ });
1953
+ /**
1954
+ * Queue of promises to resolve when initiating connections amount is below the limit.
1955
+ */
1956
+ this._waitingPromises = new ComplexMap4(PublicKey6.hash);
1957
+ this.resolveWaitingPromises = new DeferredTask2(this._ctx, async () => {
1958
+ Array.from(this._waitingPromises.values()).slice(0, this._maxConcurrentInitConnections).forEach(({ resolve }) => {
1959
+ resolve();
1960
+ });
1961
+ });
1962
+ this._maxConcurrentInitConnections = maxConcurrentInitConnections;
1963
+ }
1964
+ /**
1965
+ * @returns Promise that resolves in queue when connections amount with 'CONNECTING' state is below the limit.
1966
+ */
1967
+ async connecting(sessionId) {
1968
+ invariant5(!this._waitingPromises.has(sessionId), "Peer is already waiting for connection", {
1969
+ F: __dxlog_file7,
1970
+ L: 48,
1971
+ S: this,
1972
+ A: [
1973
+ "!this._waitingPromises.has(sessionId)",
1974
+ "'Peer is already waiting for connection'"
1975
+ ]
1976
+ });
1977
+ log7("waiting", {
1978
+ sessionId
1979
+ }, {
1980
+ F: __dxlog_file7,
1981
+ L: 49,
1982
+ S: this,
1983
+ C: (f, a) => f(...a)
1984
+ });
1985
+ await new Promise((resolve, reject) => {
1986
+ this._waitingPromises.set(sessionId, {
1987
+ resolve,
1988
+ reject
1989
+ });
1990
+ this.resolveWaitingPromises.schedule();
1991
+ });
1992
+ log7("allow", {
1993
+ sessionId
1994
+ }, {
1995
+ F: __dxlog_file7,
1996
+ L: 57,
1997
+ S: this,
1998
+ C: (f, a) => f(...a)
1999
+ });
2000
+ }
2001
+ /**
2002
+ * Rejects promise returned by `connecting` method.
2003
+ */
2004
+ doneConnecting(sessionId) {
2005
+ log7("done", {
2006
+ sessionId
2007
+ }, {
2008
+ F: __dxlog_file7,
2009
+ L: 64,
2010
+ S: this,
2011
+ C: (f, a) => f(...a)
2012
+ });
2013
+ if (!this._waitingPromises.has(sessionId)) {
2014
+ return;
2015
+ }
2016
+ this._waitingPromises.get(sessionId).reject(new CancelledError3());
2017
+ this._waitingPromises.delete(sessionId);
2018
+ this.resolveWaitingPromises.schedule();
2019
+ }
2020
+ };
2021
+
2022
+ // packages/core/mesh/network-manager/src/connection-log.ts
2023
+ import { Event as Event5 } from "@dxos/async";
2024
+ import { raise } from "@dxos/debug";
2025
+ import { PublicKey as PublicKey7 } from "@dxos/keys";
2026
+ import { ComplexMap as ComplexMap5 } from "@dxos/util";
2027
+ var CONNECTION_GC_THRESHOLD = 1e3 * 60 * 15;
2028
+ var EventType;
2029
+ (function(EventType2) {
2030
+ EventType2["CONNECTION_STATE_CHANGED"] = "CONNECTION_STATE_CHANGED";
2031
+ EventType2["PROTOCOL_ERROR"] = "PROTOCOL_ERROR";
2032
+ EventType2["PROTOCOL_EXTENSIONS_INITIALIZED"] = "PROTOCOL_EXTENSIONS_INITIALIZED";
2033
+ EventType2["PROTOCOL_EXTENSIONS_HANDSHAKE"] = "PROTOCOL_EXTENSIONS_HANDSHAKE";
2034
+ EventType2["PROTOCOL_HANDSHAKE"] = "PROTOCOL_HANDSHAKE";
2035
+ })(EventType || (EventType = {}));
2036
+ var ConnectionLog = class {
2037
+ constructor() {
2038
+ /**
2039
+ * SwarmId => info
2040
+ */
2041
+ this._swarms = new ComplexMap5(PublicKey7.hash);
2042
+ this.update = new Event5();
2043
+ }
2044
+ getSwarmInfo(swarmId) {
2045
+ return this._swarms.get(swarmId) ?? raise(new Error(`Swarm not found: ${swarmId}`));
2046
+ }
2047
+ get swarms() {
2048
+ return Array.from(this._swarms.values());
2049
+ }
2050
+ joinedSwarm(swarm) {
2051
+ const info = {
2052
+ id: PublicKey7.from(swarm._instanceId),
2053
+ topic: swarm.topic,
2054
+ isActive: true,
2055
+ label: swarm.label,
2056
+ connections: []
2057
+ };
2058
+ this._swarms.set(PublicKey7.from(swarm._instanceId), info);
2059
+ this.update.emit();
2060
+ swarm.connectionAdded.on((connection) => {
2061
+ const connectionInfo = {
2062
+ state: ConnectionState.CREATED,
2063
+ closeReason: connection.closeReason,
2064
+ remotePeerId: PublicKey7.from(connection.remoteInfo.peerKey),
2065
+ sessionId: connection.sessionId,
2066
+ transport: connection.transport && Object.getPrototypeOf(connection.transport).constructor.name,
2067
+ protocolExtensions: [],
2068
+ events: [],
2069
+ lastUpdate: /* @__PURE__ */ new Date()
2070
+ };
2071
+ info.connections.push(connectionInfo);
2072
+ this.update.emit();
2073
+ connection.stateChanged.on(async (state) => {
2074
+ connectionInfo.state = state;
2075
+ connectionInfo.closeReason = connection.closeReason;
2076
+ connectionInfo.lastUpdate = /* @__PURE__ */ new Date();
2077
+ connectionInfo.events.push({
2078
+ type: "CONNECTION_STATE_CHANGED",
2079
+ newState: state
2080
+ });
2081
+ if (state === ConnectionState.CONNECTED) {
2082
+ const details = await connection.transport?.getDetails();
2083
+ connectionInfo.transportDetails = details;
2084
+ }
2085
+ this.update.emit();
2086
+ });
2087
+ connection.protocol?.stats?.on((stats) => {
2088
+ connectionInfo.readBufferSize = stats.readBufferSize;
2089
+ connectionInfo.writeBufferSize = stats.writeBufferSize;
2090
+ connectionInfo.streams = stats.channels;
2091
+ connectionInfo.lastUpdate = /* @__PURE__ */ new Date();
2092
+ this.update.emit();
2093
+ });
2094
+ connection.transportStats?.on((stats) => {
2095
+ connectionInfo.transportBytesSent = stats.bytesSent;
2096
+ connectionInfo.transportBytesReceived = stats.bytesReceived;
2097
+ connectionInfo.transportPacketsSent = stats.packetsSent;
2098
+ connectionInfo.transportPacketsReceived = stats.packetsReceived;
2099
+ });
2100
+ gcSwarm(info);
2101
+ });
2102
+ }
2103
+ leftSwarm(swarm) {
2104
+ this.getSwarmInfo(PublicKey7.from(swarm._instanceId)).isActive = false;
2105
+ this.update.emit();
2106
+ }
2107
+ };
2108
+ var gcSwarm = (swarm) => {
2109
+ swarm.connections = swarm.connections?.filter((connection) => {
2110
+ return connection.lastUpdate ? Date.now() - connection.lastUpdate.getTime() < CONNECTION_GC_THRESHOLD : true;
2111
+ });
2112
+ };
2113
+
2114
+ // packages/core/mesh/network-manager/src/network-manager.ts
2115
+ import { Event as Event6, synchronized as synchronized4 } from "@dxos/async";
2116
+ import { invariant as invariant6 } from "@dxos/invariant";
2117
+ import { PublicKey as PublicKey8 } from "@dxos/keys";
2118
+ import { log as log8 } from "@dxos/log";
2119
+ import { Messenger } from "@dxos/messaging";
2120
+ import { trace as trace3 } from "@dxos/protocols";
2121
+ import { ConnectionState as ConnectionState2 } from "@dxos/protocols/proto/dxos/client/services";
2122
+ import { ComplexMap as ComplexMap6 } from "@dxos/util";
2123
+ function _ts_decorate4(decorators, target, key, desc) {
2124
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2125
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2126
+ 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;
2127
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2128
+ }
2129
+ var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/network-manager.ts";
2130
+ var SwarmNetworkManager = class {
2131
+ constructor({ transportFactory, signalManager, enableDevtoolsLogging, peerInfo }) {
2132
+ /**
2133
+ * @internal
2134
+ */
2135
+ this._swarms = new ComplexMap6(PublicKey8.hash);
2136
+ this._mappers = new ComplexMap6(PublicKey8.hash);
2137
+ this._instanceId = PublicKey8.random().toHex();
2138
+ this._peerInfo = void 0;
2139
+ this._connectionState = ConnectionState2.ONLINE;
2140
+ this.connectionStateChanged = new Event6();
2141
+ this.topicsUpdated = new Event6();
2142
+ this._transportFactory = transportFactory;
2143
+ this._signalManager = signalManager;
2144
+ this._signalManager.swarmEvent.on((event) => this._swarms.get(event.topic)?.onSwarmEvent(event));
2145
+ this._messenger = new Messenger({
2146
+ signalManager: this._signalManager
2147
+ });
2148
+ this._signalConnection = {
2149
+ join: (opts) => this._signalManager.join(opts),
2150
+ leave: (opts) => this._signalManager.leave(opts)
2151
+ };
2152
+ this._peerInfo = peerInfo;
2153
+ this._connectionLimiter = new ConnectionLimiter();
2154
+ if (enableDevtoolsLogging) {
2155
+ this._connectionLog = new ConnectionLog();
2156
+ }
2157
+ }
2158
+ // TODO(burdon): Remove access (Devtools only).
2159
+ get connectionLog() {
2160
+ return this._connectionLog;
2161
+ }
2162
+ get connectionState() {
2163
+ return this._connectionState;
2164
+ }
2165
+ // TODO(burdon): Reconcile with "discovery_key".
2166
+ get topics() {
2167
+ return Array.from(this._swarms.keys());
2168
+ }
2169
+ getSwarmMap(topic) {
2170
+ return this._mappers.get(topic);
2171
+ }
2172
+ getSwarm(topic) {
2173
+ return this._swarms.get(topic);
2174
+ }
2175
+ setPeerInfo(peerInfo) {
2176
+ this._peerInfo = peerInfo;
2177
+ }
2178
+ async open() {
2179
+ log8.trace("dxos.mesh.network-manager.open", trace3.begin({
2180
+ id: this._instanceId
2181
+ }), {
2182
+ F: __dxlog_file8,
2183
+ L: 133,
2184
+ S: this,
2185
+ C: (f, a) => f(...a)
2186
+ });
2187
+ await this._messenger.open();
2188
+ await this._signalManager.open();
2189
+ log8.trace("dxos.mesh.network-manager.open", trace3.end({
2190
+ id: this._instanceId
2191
+ }), {
2192
+ F: __dxlog_file8,
2193
+ L: 136,
2194
+ S: this,
2195
+ C: (f, a) => f(...a)
2196
+ });
2197
+ }
2198
+ async close() {
2199
+ for (const topic of this._swarms.keys()) {
2200
+ await this.leaveSwarm(topic).catch((err) => {
2201
+ log8(err, void 0, {
2202
+ F: __dxlog_file8,
2203
+ L: 142,
2204
+ S: this,
2205
+ C: (f, a) => f(...a)
2206
+ });
2207
+ });
2208
+ }
2209
+ await this._messenger.close();
2210
+ await this._signalManager.close();
2211
+ }
2212
+ /**
2213
+ * Join the swarm.
2214
+ */
2215
+ async joinSwarm({ topic, topology, protocolProvider: protocol, label }) {
2216
+ invariant6(PublicKey8.isPublicKey(topic), void 0, {
2217
+ F: __dxlog_file8,
2218
+ L: 160,
2219
+ S: this,
2220
+ A: [
2221
+ "PublicKey.isPublicKey(topic)",
2222
+ ""
2223
+ ]
2224
+ });
2225
+ invariant6(topology, void 0, {
2226
+ F: __dxlog_file8,
2227
+ L: 161,
2228
+ S: this,
2229
+ A: [
2230
+ "topology",
2231
+ ""
2232
+ ]
2233
+ });
2234
+ invariant6(this._peerInfo, void 0, {
2235
+ F: __dxlog_file8,
2236
+ L: 162,
2237
+ S: this,
2238
+ A: [
2239
+ "this._peerInfo",
2240
+ ""
2241
+ ]
2242
+ });
2243
+ invariant6(typeof protocol === "function", void 0, {
2244
+ F: __dxlog_file8,
2245
+ L: 163,
2246
+ S: this,
2247
+ A: [
2248
+ "typeof protocol === 'function'",
2249
+ ""
2250
+ ]
2251
+ });
2252
+ if (this._swarms.has(topic)) {
2253
+ throw new Error(`Already connected to swarm: ${PublicKey8.from(topic)}`);
2254
+ }
2255
+ log8("joining", {
2256
+ topic: PublicKey8.from(topic),
2257
+ peerInfo: this._peerInfo,
2258
+ topology: topology.toString()
2259
+ }, {
2260
+ F: __dxlog_file8,
2261
+ L: 168,
2262
+ S: this,
2263
+ C: (f, a) => f(...a)
2264
+ });
2265
+ const swarm = new Swarm(topic, this._peerInfo, topology, protocol, this._messenger, this._transportFactory, label, this._connectionLimiter);
2266
+ swarm.errors.handle((error) => {
2267
+ log8("swarm error", {
2268
+ error
2269
+ }, {
2270
+ F: __dxlog_file8,
2271
+ L: 181,
2272
+ S: this,
2273
+ C: (f, a) => f(...a)
2274
+ });
2275
+ });
2276
+ this._swarms.set(topic, swarm);
2277
+ this._mappers.set(topic, new SwarmMapper(swarm));
2278
+ await swarm.open();
2279
+ this._signalConnection.join({
2280
+ topic,
2281
+ peer: this._peerInfo
2282
+ }).catch((error) => log8.catch(error, void 0, {
2283
+ F: __dxlog_file8,
2284
+ L: 190,
2285
+ S: this,
2286
+ C: (f, a) => f(...a)
2287
+ }));
2288
+ this.topicsUpdated.emit();
2289
+ this._connectionLog?.joinedSwarm(swarm);
2290
+ log8("joined", {
2291
+ topic: PublicKey8.from(topic),
2292
+ count: this._swarms.size
2293
+ }, {
2294
+ F: __dxlog_file8,
2295
+ L: 194,
2296
+ S: this,
2297
+ C: (f, a) => f(...a)
2298
+ });
2299
+ return {
2300
+ close: () => this.leaveSwarm(topic)
2301
+ };
2302
+ }
2303
+ /**
2304
+ * Close the connection.
2305
+ */
2306
+ async leaveSwarm(topic) {
2307
+ if (!this._swarms.has(topic)) {
2308
+ return;
2309
+ }
2310
+ log8("leaving", {
2311
+ topic: PublicKey8.from(topic)
2312
+ }, {
2313
+ F: __dxlog_file8,
2314
+ L: 211,
2315
+ S: this,
2316
+ C: (f, a) => f(...a)
2317
+ });
2318
+ const swarm = this._swarms.get(topic);
2319
+ await this._signalConnection.leave({
2320
+ topic,
2321
+ peer: swarm.ownPeer
2322
+ });
2323
+ const map = this._mappers.get(topic);
2324
+ map.destroy();
2325
+ this._mappers.delete(topic);
2326
+ this._connectionLog?.leftSwarm(swarm);
2327
+ await swarm.destroy();
2328
+ this._swarms.delete(topic);
2329
+ this.topicsUpdated.emit();
2330
+ log8("left", {
2331
+ topic: PublicKey8.from(topic),
2332
+ count: this._swarms.size
2333
+ }, {
2334
+ F: __dxlog_file8,
2335
+ L: 225,
2336
+ S: this,
2337
+ C: (f, a) => f(...a)
2338
+ });
2339
+ }
2340
+ async setConnectionState(state) {
2341
+ if (state === this._connectionState) {
2342
+ return;
2343
+ }
2344
+ switch (state) {
2345
+ case ConnectionState2.OFFLINE: {
2346
+ this._connectionState = state;
2347
+ await Promise.all([
2348
+ ...this._swarms.values()
2349
+ ].map((swarm) => swarm.goOffline()));
2350
+ await this._messenger.close();
2351
+ await this._signalManager.close();
2352
+ break;
2353
+ }
2354
+ case ConnectionState2.ONLINE: {
2355
+ this._connectionState = state;
2356
+ this._messenger.open();
2357
+ await Promise.all([
2358
+ ...this._swarms.values()
2359
+ ].map((swarm) => swarm.goOnline()));
2360
+ await this._signalManager.open();
2361
+ break;
2362
+ }
2363
+ }
2364
+ this.connectionStateChanged.emit(this._connectionState);
2365
+ }
2366
+ };
2367
+ _ts_decorate4([
2368
+ synchronized4
2369
+ ], SwarmNetworkManager.prototype, "joinSwarm", null);
2370
+ _ts_decorate4([
2371
+ synchronized4
2372
+ ], SwarmNetworkManager.prototype, "leaveSwarm", null);
2373
+
2374
+ // packages/core/mesh/network-manager/src/topology/fully-connected-topology.ts
2375
+ import { invariant as invariant7 } from "@dxos/invariant";
2376
+ var __dxlog_file9 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/topology/fully-connected-topology.ts";
2377
+ var FullyConnectedTopology = class {
2378
+ toString() {
2379
+ return "FullyConnectedTopology";
2380
+ }
2381
+ init(controller) {
2382
+ invariant7(!this._controller, "Already initialized", {
2383
+ F: __dxlog_file9,
2384
+ L: 18,
2385
+ S: this,
2386
+ A: [
2387
+ "!this._controller",
2388
+ "'Already initialized'"
2389
+ ]
2390
+ });
2391
+ this._controller = controller;
2392
+ }
2393
+ update() {
2394
+ invariant7(this._controller, "Not initialized", {
2395
+ F: __dxlog_file9,
2396
+ L: 23,
2397
+ S: this,
2398
+ A: [
2399
+ "this._controller",
2400
+ "'Not initialized'"
2401
+ ]
2402
+ });
2403
+ const { candidates: discovered } = this._controller.getState();
2404
+ for (const peer of discovered) {
2405
+ this._controller.connect(peer);
2406
+ }
2407
+ }
2408
+ async onOffer(peer) {
2409
+ return true;
2410
+ }
2411
+ async destroy() {
2412
+ }
2413
+ };
2414
+
2415
+ // packages/core/mesh/network-manager/src/topology/mmst-topology.ts
2416
+ import { invariant as invariant8 } from "@dxos/invariant";
2417
+ import { log as log9 } from "@dxos/log";
2418
+ var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/topology/mmst-topology.ts";
2419
+ var MIN_UPDATE_INTERVAL = 1e3 * 10;
2420
+ var MAX_CHANGES_PER_UPDATE = 1;
2421
+ var MMSTTopology = class {
2422
+ constructor({ originateConnections = 2, maxPeers = 4, sampleSize = 10 } = {}) {
2423
+ this._sampleCollected = false;
2424
+ this._lastAction = /* @__PURE__ */ new Date(0);
2425
+ this._originateConnections = originateConnections;
2426
+ this._maxPeers = maxPeers;
2427
+ this._sampleSize = sampleSize;
2428
+ }
2429
+ init(controller) {
2430
+ invariant8(!this._controller, "Already initialized", {
2431
+ F: __dxlog_file10,
2432
+ L: 49,
2433
+ S: this,
2434
+ A: [
2435
+ "!this._controller",
2436
+ "'Already initialized'"
2437
+ ]
2438
+ });
2439
+ this._controller = controller;
2440
+ }
2441
+ update() {
2442
+ invariant8(this._controller, "Not initialized", {
2443
+ F: __dxlog_file10,
2444
+ L: 54,
2445
+ S: this,
2446
+ A: [
2447
+ "this._controller",
2448
+ "'Not initialized'"
2449
+ ]
2450
+ });
2451
+ const { connected, candidates } = this._controller.getState();
2452
+ if (this._sampleCollected || connected.length > this._maxPeers || candidates.length > 0) {
2453
+ log9("Running the algorithm.", void 0, {
2454
+ F: __dxlog_file10,
2455
+ L: 58,
2456
+ S: this,
2457
+ C: (f, a) => f(...a)
2458
+ });
2459
+ this._sampleCollected = true;
2460
+ this._runAlgorithm();
2461
+ }
2462
+ }
2463
+ forceUpdate() {
2464
+ this._lastAction = /* @__PURE__ */ new Date(0);
2465
+ this.update();
2466
+ }
2467
+ async onOffer(peer) {
2468
+ invariant8(this._controller, "Not initialized", {
2469
+ F: __dxlog_file10,
2470
+ L: 70,
2471
+ S: this,
2472
+ A: [
2473
+ "this._controller",
2474
+ "'Not initialized'"
2475
+ ]
2476
+ });
2477
+ const { connected } = this._controller.getState();
2478
+ const accept = connected.length < this._maxPeers;
2479
+ log9(`Offer ${peer} accept=${accept}`, void 0, {
2480
+ F: __dxlog_file10,
2481
+ L: 73,
2482
+ S: this,
2483
+ C: (f, a) => f(...a)
2484
+ });
2485
+ return accept;
2486
+ }
2487
+ async destroy() {
2488
+ }
2489
+ _runAlgorithm() {
2490
+ invariant8(this._controller, "Not initialized", {
2491
+ F: __dxlog_file10,
2492
+ L: 82,
2493
+ S: this,
2494
+ A: [
2495
+ "this._controller",
2496
+ "'Not initialized'"
2497
+ ]
2498
+ });
2499
+ const { connected, candidates, ownPeerId } = this._controller.getState();
2500
+ if (connected.length > this._maxPeers) {
2501
+ log9(`disconnect ${connected.length - this._maxPeers} peers.`, void 0, {
2502
+ F: __dxlog_file10,
2503
+ L: 88,
2504
+ S: this,
2505
+ C: (f, a) => f(...a)
2506
+ });
2507
+ const sorted = sortByXorDistance(connected, ownPeerId).reverse().slice(0, this._maxPeers - connected.length);
2508
+ invariant8(sorted.length === 0, void 0, {
2509
+ F: __dxlog_file10,
2510
+ L: 92,
2511
+ S: this,
2512
+ A: [
2513
+ "sorted.length === 0",
2514
+ ""
2515
+ ]
2516
+ });
2517
+ if (sorted.length > MAX_CHANGES_PER_UPDATE) {
2518
+ log9(`want to disconnect ${sorted.length} peers but limited to ${MAX_CHANGES_PER_UPDATE}`, void 0, {
2519
+ F: __dxlog_file10,
2520
+ L: 95,
2521
+ S: this,
2522
+ C: (f, a) => f(...a)
2523
+ });
2524
+ }
2525
+ if (Date.now() - this._lastAction.getTime() > MIN_UPDATE_INTERVAL) {
2526
+ for (const peer of sorted.slice(0, MAX_CHANGES_PER_UPDATE)) {
2527
+ log9(`Disconnect ${peer}.`, void 0, {
2528
+ F: __dxlog_file10,
2529
+ L: 100,
2530
+ S: this,
2531
+ C: (f, a) => f(...a)
2532
+ });
2533
+ this._controller.disconnect(peer);
2534
+ }
2535
+ this._lastAction = /* @__PURE__ */ new Date();
2536
+ } else {
2537
+ log9("rate limited disconnect", void 0, {
2538
+ F: __dxlog_file10,
2539
+ L: 105,
2540
+ S: this,
2541
+ C: (f, a) => f(...a)
2542
+ });
2543
+ }
2544
+ } else if (connected.length < this._originateConnections) {
2545
+ log9(`connect ${this._originateConnections - connected.length} peers.`, void 0, {
2546
+ F: __dxlog_file10,
2547
+ L: 109,
2548
+ S: this,
2549
+ C: (f, a) => f(...a)
2550
+ });
2551
+ const sample = candidates.sort(() => Math.random() - 0.5).slice(0, this._sampleSize);
2552
+ const sorted = sortByXorDistance(sample, ownPeerId).slice(0, this._originateConnections - connected.length);
2553
+ if (sorted.length > MAX_CHANGES_PER_UPDATE) {
2554
+ log9(`want to connect ${sorted.length} peers but limited to ${MAX_CHANGES_PER_UPDATE}`, void 0, {
2555
+ F: __dxlog_file10,
2556
+ L: 114,
2557
+ S: this,
2558
+ C: (f, a) => f(...a)
2559
+ });
2560
+ }
2561
+ if (Date.now() - this._lastAction.getTime() > MIN_UPDATE_INTERVAL) {
2562
+ for (const peer of sorted.slice(0, MAX_CHANGES_PER_UPDATE)) {
2563
+ log9(`Connect ${peer}.`, void 0, {
2564
+ F: __dxlog_file10,
2565
+ L: 118,
2566
+ S: this,
2567
+ C: (f, a) => f(...a)
2568
+ });
2569
+ this._controller.connect(peer);
2570
+ }
2571
+ this._lastAction = /* @__PURE__ */ new Date();
2572
+ } else {
2573
+ log9("rate limited connect", void 0, {
2574
+ F: __dxlog_file10,
2575
+ L: 123,
2576
+ S: this,
2577
+ C: (f, a) => f(...a)
2578
+ });
2579
+ }
2580
+ }
2581
+ }
2582
+ toString() {
2583
+ return "MMSTTopology";
2584
+ }
2585
+ };
2586
+ var sortByXorDistance = (keys, reference) => {
2587
+ const sorted = keys.sort((a, b) => {
2588
+ return compareXor(distXor(a.asBuffer(), reference.asBuffer()), distXor(b.asBuffer(), reference.asBuffer()));
2589
+ });
2590
+ log9("Sorted keys", {
2591
+ keys,
2592
+ reference,
2593
+ sorted
2594
+ }, {
2595
+ F: __dxlog_file10,
2596
+ L: 137,
2597
+ S: void 0,
2598
+ C: (f, a) => f(...a)
2599
+ });
2600
+ return sorted;
2601
+ };
2602
+ var distXor = (a, b) => {
2603
+ const maxLength = Math.max(a.length, b.length);
2604
+ const result = Buffer.allocUnsafe(maxLength);
2605
+ for (let i = 0; i < maxLength; i++) {
2606
+ result[i] = (a[i] || 0) ^ (b[i] || 0);
2607
+ }
2608
+ return result;
2609
+ };
2610
+ var compareXor = (a, b) => {
2611
+ const maxLength = Math.max(a.length, b.length);
2612
+ for (let i = 0; i < maxLength; i++) {
2613
+ if ((a[i] || 0) === (b[i] || 0)) {
2614
+ continue;
2615
+ }
2616
+ return (a[i] || 0) < (b[i] || 0) ? -1 : 1;
2617
+ }
2618
+ return 0;
2619
+ };
2620
+
2621
+ // packages/core/mesh/network-manager/src/topology/star-topology.ts
2622
+ import { invariant as invariant9 } from "@dxos/invariant";
2623
+ import { log as log10 } from "@dxos/log";
2624
+ var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/topology/star-topology.ts";
2625
+ var StarTopology = class {
2626
+ constructor(_centralPeer) {
2627
+ this._centralPeer = _centralPeer;
2628
+ }
2629
+ toString() {
2630
+ return `StarTopology(${this._centralPeer.truncate()})`;
2631
+ }
2632
+ init(controller) {
2633
+ invariant9(!this._controller, "Already initialized.", {
2634
+ F: __dxlog_file11,
2635
+ L: 21,
2636
+ S: this,
2637
+ A: [
2638
+ "!this._controller",
2639
+ "'Already initialized.'"
2640
+ ]
2641
+ });
2642
+ this._controller = controller;
2643
+ }
2644
+ update() {
2645
+ invariant9(this._controller, "Not initialized.", {
2646
+ F: __dxlog_file11,
2647
+ L: 26,
2648
+ S: this,
2649
+ A: [
2650
+ "this._controller",
2651
+ "'Not initialized.'"
2652
+ ]
2653
+ });
2654
+ const { candidates, connected, ownPeerId } = this._controller.getState();
2655
+ if (!ownPeerId.equals(this._centralPeer)) {
2656
+ log10("leaf peer dropping all connections apart from central peer.", void 0, {
2657
+ F: __dxlog_file11,
2658
+ L: 29,
2659
+ S: this,
2660
+ C: (f, a) => f(...a)
2661
+ });
2662
+ for (const peer of connected) {
2663
+ if (!peer.equals(this._centralPeer)) {
2664
+ log10("dropping connection", {
2665
+ peer
2666
+ }, {
2667
+ F: __dxlog_file11,
2668
+ L: 34,
2669
+ S: this,
2670
+ C: (f, a) => f(...a)
2671
+ });
2672
+ this._controller.disconnect(peer);
2673
+ }
2674
+ }
2675
+ }
2676
+ for (const peer of candidates) {
2677
+ if (peer.equals(this._centralPeer) || ownPeerId.equals(this._centralPeer)) {
2678
+ log10("connecting to peer", {
2679
+ peer
2680
+ }, {
2681
+ F: __dxlog_file11,
2682
+ L: 43,
2683
+ S: this,
2684
+ C: (f, a) => f(...a)
2685
+ });
2686
+ this._controller.connect(peer);
2687
+ }
2688
+ }
2689
+ }
2690
+ async onOffer(peer) {
2691
+ invariant9(this._controller, "Not initialized.", {
2692
+ F: __dxlog_file11,
2693
+ L: 50,
2694
+ S: this,
2695
+ A: [
2696
+ "this._controller",
2697
+ "'Not initialized.'"
2698
+ ]
2699
+ });
2700
+ const { ownPeerId } = this._controller.getState();
2701
+ log10("offer", {
2702
+ peer,
2703
+ isCentral: peer.equals(this._centralPeer),
2704
+ isSelfCentral: ownPeerId.equals(this._centralPeer)
2705
+ }, {
2706
+ F: __dxlog_file11,
2707
+ L: 52,
2708
+ S: this,
2709
+ C: (f, a) => f(...a)
2710
+ });
2711
+ return ownPeerId.equals(this._centralPeer) || peer.equals(this._centralPeer);
2712
+ }
2713
+ async destroy() {
2714
+ }
2715
+ };
2716
+
2717
+ // packages/core/mesh/network-manager/src/transport/memory-transport.ts
2718
+ import { Transform } from "node:stream";
2719
+ import { Event as Event7, Trigger as Trigger2 } from "@dxos/async";
2720
+ import { ErrorStream as ErrorStream3 } from "@dxos/debug";
2721
+ import { invariant as invariant10 } from "@dxos/invariant";
2722
+ import { PublicKey as PublicKey9 } from "@dxos/keys";
2723
+ import { log as log11, logInfo as logInfo3 } from "@dxos/log";
2724
+ import { ComplexMap as ComplexMap7 } from "@dxos/util";
2725
+ function _ts_decorate5(decorators, target, key, desc) {
2726
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2727
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2728
+ 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;
2729
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2730
+ }
2731
+ var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/transport/memory-transport.ts";
2732
+ var MEMORY_TRANSPORT_DELAY = 1;
2733
+ var createStreamDelay = (delay) => {
2734
+ return new Transform({
2735
+ objectMode: true,
2736
+ transform: (chunk, _, cb) => {
2737
+ setTimeout(() => cb(null, chunk), delay);
2738
+ }
2739
+ });
2740
+ };
2741
+ var MemoryTransportFactory = {
2742
+ createTransport: (options) => new MemoryTransport(options)
2743
+ };
2744
+ var MemoryTransport = class _MemoryTransport {
2745
+ static {
2746
+ // TODO(burdon): Remove static properties (inject context into constructor).
2747
+ this._connections = new ComplexMap7(PublicKey9.hash);
2748
+ }
2749
+ constructor(_options) {
2750
+ this._options = _options;
2751
+ this._instanceId = PublicKey9.random();
2752
+ this._remote = new Trigger2();
2753
+ this._outgoingDelay = createStreamDelay(MEMORY_TRANSPORT_DELAY);
2754
+ this._incomingDelay = createStreamDelay(MEMORY_TRANSPORT_DELAY);
2755
+ this._closed = false;
2756
+ this.closed = new Event7();
2757
+ this.connected = new Event7();
2758
+ this.errors = new ErrorStream3();
2759
+ invariant10(!_MemoryTransport._connections.has(this._instanceId), "Duplicate memory connection", {
2760
+ F: __dxlog_file12,
2761
+ L: 64,
2762
+ S: this,
2763
+ A: [
2764
+ "!MemoryTransport._connections.has(this._instanceId)",
2765
+ "'Duplicate memory connection'"
2766
+ ]
2767
+ });
2768
+ _MemoryTransport._connections.set(this._instanceId, this);
2769
+ }
2770
+ get isOpen() {
2771
+ return !this._closed;
2772
+ }
2773
+ async open() {
2774
+ log11("opening...", void 0, {
2775
+ F: __dxlog_file12,
2776
+ L: 74,
2777
+ S: this,
2778
+ C: (f, a) => f(...a)
2779
+ });
2780
+ if (this._options.initiator) {
2781
+ log11("sending signal", void 0, {
2782
+ F: __dxlog_file12,
2783
+ L: 78,
2784
+ S: this,
2785
+ C: (f, a) => f(...a)
2786
+ });
2787
+ try {
2788
+ await this._options.sendSignal({
2789
+ payload: {
2790
+ transportId: this._instanceId.toHex()
2791
+ }
2792
+ });
2793
+ } catch (err) {
2794
+ if (!this._closed) {
2795
+ this.errors.raise(toError(err));
2796
+ }
2797
+ }
2798
+ } else {
2799
+ this._remote.wait({
2800
+ timeout: this._options.timeout ?? 1e3
2801
+ }).then((remoteId) => {
2802
+ if (this._closed) {
2803
+ return;
2804
+ }
2805
+ this._remoteInstanceId = remoteId;
2806
+ this._remoteConnection = _MemoryTransport._connections.get(this._remoteInstanceId);
2807
+ if (!this._remoteConnection) {
2808
+ this._closed = true;
2809
+ this.closed.emit();
2810
+ return;
2811
+ }
2812
+ invariant10(!this._remoteConnection._remoteConnection, `Remote already connected: ${this._remoteInstanceId}`, {
2813
+ F: __dxlog_file12,
2814
+ L: 104,
2815
+ S: this,
2816
+ A: [
2817
+ "!this._remoteConnection._remoteConnection",
2818
+ "`Remote already connected: ${this._remoteInstanceId}`"
2819
+ ]
2820
+ });
2821
+ this._remoteConnection._remoteConnection = this;
2822
+ this._remoteConnection._remoteInstanceId = this._instanceId;
2823
+ log11("connected", void 0, {
2824
+ F: __dxlog_file12,
2825
+ L: 108,
2826
+ S: this,
2827
+ C: (f, a) => f(...a)
2828
+ });
2829
+ this._options.stream.pipe(this._outgoingDelay).pipe(this._remoteConnection._options.stream).pipe(this._incomingDelay).pipe(this._options.stream);
2830
+ this.connected.emit();
2831
+ this._remoteConnection.connected.emit();
2832
+ }).catch((err) => {
2833
+ if (this._closed) {
2834
+ return;
2835
+ }
2836
+ this.errors.raise(err);
2837
+ });
2838
+ }
2839
+ return this;
2840
+ }
2841
+ async close() {
2842
+ log11("closing...", void 0, {
2843
+ F: __dxlog_file12,
2844
+ L: 130,
2845
+ S: this,
2846
+ C: (f, a) => f(...a)
2847
+ });
2848
+ this._closed = true;
2849
+ _MemoryTransport._connections.delete(this._instanceId);
2850
+ if (this._remoteConnection) {
2851
+ this._remoteConnection._closed = true;
2852
+ _MemoryTransport._connections.delete(this._remoteInstanceId);
2853
+ this._options.stream.unpipe(this._incomingDelay);
2854
+ this._incomingDelay.unpipe(this._remoteConnection._options.stream);
2855
+ this._remoteConnection._options.stream.unpipe(this._outgoingDelay);
2856
+ this._outgoingDelay.unpipe(this._options.stream);
2857
+ this._options.stream.unpipe(this._outgoingDelay);
2858
+ this._remoteConnection.closed.emit();
2859
+ this._remoteConnection._remoteConnection = void 0;
2860
+ this._remoteConnection = void 0;
2861
+ }
2862
+ this.closed.emit();
2863
+ log11("closed", void 0, {
2864
+ F: __dxlog_file12,
2865
+ L: 158,
2866
+ S: this,
2867
+ C: (f, a) => f(...a)
2868
+ });
2869
+ return this;
2870
+ }
2871
+ async onSignal({ payload }) {
2872
+ log11("received signal", {
2873
+ payload
2874
+ }, {
2875
+ F: __dxlog_file12,
2876
+ L: 163,
2877
+ S: this,
2878
+ C: (f, a) => f(...a)
2879
+ });
2880
+ if (!payload?.transportId) {
2881
+ return;
2882
+ }
2883
+ const transportId = payload.transportId;
2884
+ if (transportId) {
2885
+ const remoteId = PublicKey9.fromHex(transportId);
2886
+ this._remote.wake(remoteId);
2887
+ }
2888
+ }
2889
+ async getDetails() {
2890
+ return this._instanceId.toHex();
2891
+ }
2892
+ async getStats() {
2893
+ return {
2894
+ bytesSent: 0,
2895
+ bytesReceived: 0,
2896
+ packetsSent: 0,
2897
+ packetsReceived: 0
2898
+ };
2899
+ }
2900
+ };
2901
+ _ts_decorate5([
2902
+ logInfo3
2903
+ ], MemoryTransport.prototype, "_instanceId", void 0);
2904
+ _ts_decorate5([
2905
+ logInfo3
2906
+ ], MemoryTransport.prototype, "_remoteInstanceId", void 0);
2907
+ var toError = (err) => err instanceof Error ? err : new Error(String(err));
2908
+
2909
+ // packages/core/mesh/network-manager/src/transport/transport.ts
2910
+ var TransportKind;
2911
+ (function(TransportKind2) {
2912
+ TransportKind2["WEB_RTC"] = "WEB-RTC";
2913
+ TransportKind2["WEB_RTC_PROXY"] = "WEB-RTC_PROXY";
2914
+ TransportKind2["MEMORY"] = "MEMORY";
2915
+ TransportKind2["TCP"] = "TCP";
2916
+ })(TransportKind || (TransportKind = {}));
2917
+
2918
+ // packages/core/mesh/network-manager/src/transport/webrtc/rtc-connection-factory.ts
2919
+ import { Mutex } from "@dxos/async";
2920
+ var BrowserRtcConnectionFactory = class {
2921
+ async initialize() {
2922
+ }
2923
+ async onConnectionDestroyed() {
2924
+ }
2925
+ async createConnection(config) {
2926
+ return new RTCPeerConnection(config);
2927
+ }
2928
+ async initConnection(connection, info) {
2929
+ }
2930
+ };
2931
+ var NodeRtcConnectionFactory = class _NodeRtcConnectionFactory {
2932
+ static {
2933
+ this._createdConnections = 0;
2934
+ }
2935
+ static {
2936
+ this._cleanupMutex = new Mutex();
2937
+ }
2938
+ // This should be inside the function to avoid triggering `eval` in the global scope.
2939
+ // eslint-disable-next-line no-new-func
2940
+ // TODO(burdon): Do imports here?
2941
+ async initialize() {
2942
+ }
2943
+ async onConnectionDestroyed() {
2944
+ return _NodeRtcConnectionFactory._cleanupMutex.executeSynchronized(async () => {
2945
+ if (--_NodeRtcConnectionFactory._createdConnections === 0) {
2946
+ (await import("#node-datachannel")).cleanup();
2947
+ }
2948
+ });
2949
+ }
2950
+ async createConnection(config) {
2951
+ return _NodeRtcConnectionFactory._cleanupMutex.executeSynchronized(async () => {
2952
+ const { RTCPeerConnection: RTCPeerConnection1 } = await import("#node-datachannel/polyfill");
2953
+ _NodeRtcConnectionFactory._createdConnections++;
2954
+ return new RTCPeerConnection1(config);
2955
+ });
2956
+ }
2957
+ async initConnection(connection, info) {
2958
+ if (info.initiator) {
2959
+ connection.onnegotiationneeded?.(null);
2960
+ }
2961
+ }
2962
+ };
2963
+ var getRtcConnectionFactory = () => {
2964
+ return typeof globalThis.RTCPeerConnection === "undefined" ? new NodeRtcConnectionFactory() : new BrowserRtcConnectionFactory();
2965
+ };
2966
+
2967
+ // packages/core/mesh/network-manager/src/transport/webrtc/rtc-peer-connection.ts
2968
+ import { synchronized as synchronized5, Trigger as Trigger3, Mutex as Mutex2 } from "@dxos/async";
2969
+ import { invariant as invariant12 } from "@dxos/invariant";
2970
+ import { log as log13, logInfo as logInfo4 } from "@dxos/log";
2971
+ import { ConnectivityError as ConnectivityError3 } from "@dxos/protocols";
2972
+ import { trace as trace4 } from "@dxos/tracing";
2973
+
2974
+ // packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-channel.ts
2975
+ import { Duplex } from "node:stream";
2976
+ import { Event as AsyncEvent } from "@dxos/async";
2977
+ import { Resource } from "@dxos/context";
2978
+ import { ErrorStream as ErrorStream4 } from "@dxos/debug";
2979
+ import { invariant as invariant11 } from "@dxos/invariant";
2980
+ import { log as log12 } from "@dxos/log";
2981
+ import { ConnectivityError as ConnectivityError2 } from "@dxos/protocols";
2982
+
2983
+ // packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-stats.ts
2984
+ var describeSelectedRemoteCandidate = async (connection) => {
2985
+ const stats = connection && await getRtcConnectionStats(connection);
2986
+ const rc = stats?.remoteCandidate;
2987
+ if (!rc) {
2988
+ return "unavailable";
2989
+ }
2990
+ if (rc.candidateType === "relay") {
2991
+ return `${rc.ip}:${rc.port} relay for ${rc.relatedAddress}:${rc.relatedPort}`;
2992
+ }
2993
+ return `${rc.ip}:${rc.port} ${rc.candidateType}`;
2994
+ };
2995
+ var createRtcTransportStats = async (connection, topic) => {
2996
+ const stats = connection && await getRtcConnectionStats(connection, topic);
2997
+ if (!stats) {
2998
+ return {
2999
+ bytesSent: 0,
3000
+ bytesReceived: 0,
3001
+ packetsSent: 0,
3002
+ packetsReceived: 0,
3003
+ rawStats: {}
3004
+ };
3005
+ }
3006
+ return {
3007
+ bytesSent: stats.dataChannel?.bytesSent,
3008
+ bytesReceived: stats.dataChannel?.bytesReceived,
3009
+ packetsSent: 0,
3010
+ packetsReceived: 0,
3011
+ rawStats: stats.raw
3012
+ };
3013
+ };
3014
+ var getRtcConnectionStats = async (connection, channelTopic) => {
3015
+ const stats = await connection.getStats();
3016
+ const statsEntries = Array.from(stats.entries());
3017
+ const transport = statsEntries.find(([_, entry]) => entry.type === "transport")?.[1];
3018
+ const selectedCandidatePair = transport && statsEntries.find(([entryId]) => entryId === transport.selectedCandidatePairId)?.[1];
3019
+ const remoteCandidate = selectedCandidatePair && statsEntries.find(([entryId]) => entryId === selectedCandidatePair.remoteCandidateId)?.[1];
3020
+ const dataChannel = channelTopic && statsEntries.find(([_, entry]) => entry.type === "data-channel" && entry.label === channelTopic)?.[1];
3021
+ return {
3022
+ transport,
3023
+ selectedCandidatePair,
3024
+ dataChannel,
3025
+ remoteCandidate,
3026
+ raw: Object.fromEntries(stats)
3027
+ };
3028
+ };
3029
+
3030
+ // packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-channel.ts
3031
+ var __dxlog_file13 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-channel.ts";
3032
+ var MAX_MESSAGE_SIZE = 64 * 1024;
3033
+ var MAX_BUFFERED_AMOUNT = 64 * 1024;
3034
+ var RtcTransportChannel = class extends Resource {
3035
+ constructor(_connection, _options) {
3036
+ super();
3037
+ this._connection = _connection;
3038
+ this._options = _options;
3039
+ this.closed = new AsyncEvent();
3040
+ this.connected = new AsyncEvent();
3041
+ this.errors = new ErrorStream4();
3042
+ this._streamDataFlushedCallback = null;
3043
+ this._isChannelCreationInProgress = false;
3044
+ }
3045
+ get isRtcChannelCreationInProgress() {
3046
+ return this._isChannelCreationInProgress;
3047
+ }
3048
+ onConnectionError(error) {
3049
+ if (this.isOpen) {
3050
+ this.errors.raise(error);
3051
+ }
3052
+ }
3053
+ async _open() {
3054
+ invariant11(!this._isChannelCreationInProgress, void 0, {
3055
+ F: __dxlog_file13,
3056
+ L: 56,
3057
+ S: this,
3058
+ A: [
3059
+ "!this._isChannelCreationInProgress",
3060
+ ""
3061
+ ]
3062
+ });
3063
+ this._isChannelCreationInProgress = true;
3064
+ this._connection.createDataChannel(this._options.topic).then((channel) => {
3065
+ if (this.isOpen) {
3066
+ this._channel = channel;
3067
+ this._initChannel(this._channel);
3068
+ } else {
3069
+ this._safeCloseChannel(channel);
3070
+ }
3071
+ }).catch((err) => {
3072
+ if (this.isOpen) {
3073
+ this.errors.raise(new ConnectivityError2(`Failed to create a channel: ${err?.message ?? "unknown reason."}`));
3074
+ }
3075
+ }).finally(() => {
3076
+ this._isChannelCreationInProgress = false;
3077
+ });
3078
+ }
3079
+ async _close() {
3080
+ if (this._channel) {
3081
+ this._safeCloseChannel(this._channel);
3082
+ this._channel = void 0;
3083
+ this._stream = void 0;
3084
+ }
3085
+ this.closed.emit();
3086
+ log12("closed", void 0, {
3087
+ F: __dxlog_file13,
3088
+ L: 86,
3089
+ S: this,
3090
+ C: (f, a) => f(...a)
3091
+ });
3092
+ }
3093
+ _initChannel(channel) {
3094
+ Object.assign(channel, {
3095
+ onopen: () => {
3096
+ if (!this.isOpen) {
3097
+ log12.warn("channel opened in a closed transport", {
3098
+ topic: this._options.topic
3099
+ }, {
3100
+ F: __dxlog_file13,
3101
+ L: 93,
3102
+ S: this,
3103
+ C: (f, a) => f(...a)
3104
+ });
3105
+ this._safeCloseChannel(channel);
3106
+ return;
3107
+ }
3108
+ log12("onopen", void 0, {
3109
+ F: __dxlog_file13,
3110
+ L: 98,
3111
+ S: this,
3112
+ C: (f, a) => f(...a)
3113
+ });
3114
+ const duplex = new Duplex({
3115
+ read: () => {
3116
+ },
3117
+ write: (chunk, encoding, callback) => {
3118
+ return this._handleChannelWrite(chunk, callback);
3119
+ }
3120
+ });
3121
+ duplex.pipe(this._options.stream).pipe(duplex);
3122
+ this._stream = duplex;
3123
+ this.connected.emit();
3124
+ },
3125
+ onclose: async () => {
3126
+ log12("onclose", void 0, {
3127
+ F: __dxlog_file13,
3128
+ L: 111,
3129
+ S: this,
3130
+ C: (f, a) => f(...a)
3131
+ });
3132
+ await this.close();
3133
+ },
3134
+ onmessage: (event) => {
3135
+ if (!this._stream) {
3136
+ log12.warn("ignoring message on a closed channel", void 0, {
3137
+ F: __dxlog_file13,
3138
+ L: 117,
3139
+ S: this,
3140
+ C: (f, a) => f(...a)
3141
+ });
3142
+ return;
3143
+ }
3144
+ let data = event.data;
3145
+ if (data instanceof ArrayBuffer) {
3146
+ data = Buffer.from(data);
3147
+ }
3148
+ this._stream.push(data);
3149
+ },
3150
+ onerror: (event) => {
3151
+ if (this.isOpen) {
3152
+ const err = event.error instanceof Error ? event.error : new Error(`Datachannel error: ${event.type}.`);
3153
+ this.errors.raise(err);
3154
+ }
3155
+ },
3156
+ onbufferedamountlow: () => {
3157
+ const cb = this._streamDataFlushedCallback;
3158
+ this._streamDataFlushedCallback = null;
3159
+ cb?.();
3160
+ }
3161
+ });
3162
+ }
3163
+ async _handleChannelWrite(chunk, callback) {
3164
+ if (!this._channel) {
3165
+ log12.warn("writing to a channel after a connection was closed", void 0, {
3166
+ F: __dxlog_file13,
3167
+ L: 145,
3168
+ S: this,
3169
+ C: (f, a) => f(...a)
3170
+ });
3171
+ return;
3172
+ }
3173
+ if (chunk.length > MAX_MESSAGE_SIZE) {
3174
+ const error = new Error(`Message too large: ${chunk.length} > ${MAX_MESSAGE_SIZE}.`);
3175
+ this.errors.raise(error);
3176
+ callback();
3177
+ return;
3178
+ }
3179
+ try {
3180
+ this._channel.send(chunk);
3181
+ } catch (err) {
3182
+ this.errors.raise(err);
3183
+ callback();
3184
+ return;
3185
+ }
3186
+ if (this._channel.bufferedAmount > MAX_BUFFERED_AMOUNT) {
3187
+ if (this._streamDataFlushedCallback !== null) {
3188
+ log12.error("consumer trying to write before we are ready for more data", void 0, {
3189
+ F: __dxlog_file13,
3190
+ L: 166,
3191
+ S: this,
3192
+ C: (f, a) => f(...a)
3193
+ });
3194
+ }
3195
+ this._streamDataFlushedCallback = callback;
3196
+ } else {
3197
+ callback();
3198
+ }
3199
+ }
3200
+ _safeCloseChannel(channel) {
3201
+ try {
3202
+ channel.close();
3203
+ } catch (error) {
3204
+ log12.catch(error, void 0, {
3205
+ F: __dxlog_file13,
3206
+ L: 178,
3207
+ S: this,
3208
+ C: (f, a) => f(...a)
3209
+ });
3210
+ }
3211
+ }
3212
+ onSignal(signal) {
3213
+ return this._connection.onSignal(signal);
3214
+ }
3215
+ async getDetails() {
3216
+ return describeSelectedRemoteCandidate(this._connection.currentConnection);
3217
+ }
3218
+ async getStats() {
3219
+ return createRtcTransportStats(this._connection.currentConnection, this._options.topic);
3220
+ }
3221
+ };
3222
+
3223
+ // packages/core/mesh/network-manager/src/transport/webrtc/utils.ts
3224
+ var chooseInitiatorPeer = (peer1Key, peer2Key) => peer1Key < peer2Key ? peer1Key : peer2Key;
3225
+ var areSdpEqual = (sdp1, sdp2) => {
3226
+ const sdp1Lines = deduplicatedSdpLines(sdp1);
3227
+ const sdp2Lines = deduplicatedSdpLines(sdp2);
3228
+ if (sdp1Lines.length !== sdp2Lines.length) {
3229
+ return false;
3230
+ }
3231
+ return sdp1Lines.every((line, idx) => line === sdp2Lines[idx]);
3232
+ };
3233
+ var deduplicatedSdpLines = (sdp) => {
3234
+ const deduplicatedLines = [];
3235
+ const seenLines = [];
3236
+ for (const line of sdp.split("\r\n")) {
3237
+ if (line.startsWith("m")) {
3238
+ seenLines.length = 0;
3239
+ }
3240
+ if (seenLines.includes(line)) {
3241
+ continue;
3242
+ }
3243
+ seenLines.push(line);
3244
+ deduplicatedLines.push(line);
3245
+ }
3246
+ return deduplicatedLines;
3247
+ };
3248
+
3249
+ // packages/core/mesh/network-manager/src/transport/webrtc/rtc-peer-connection.ts
3250
+ function _ts_decorate6(decorators, target, key, desc) {
3251
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3252
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3253
+ 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;
3254
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
3255
+ }
3256
+ var __dxlog_file14 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/transport/webrtc/rtc-peer-connection.ts";
3257
+ var RtcPeerConnection = class {
3258
+ constructor(_factory, _options) {
3259
+ this._factory = _factory;
3260
+ this._options = _options;
3261
+ this._channelCreatedCallbacks = /* @__PURE__ */ new Map();
3262
+ this._transportChannels = /* @__PURE__ */ new Map();
3263
+ this._dataChannels = /* @__PURE__ */ new Map();
3264
+ this._readyForCandidates = new Trigger3();
3265
+ this._offerProcessingMutex = new Mutex2();
3266
+ this._initiator = chooseInitiatorPeer(_options.ownPeerKey, _options.remotePeerKey) === _options.ownPeerKey;
3267
+ }
3268
+ get transportChannelCount() {
3269
+ return this._transportChannels.size;
3270
+ }
3271
+ get currentConnection() {
3272
+ return this._connection;
3273
+ }
3274
+ async createDataChannel(topic) {
3275
+ const connection = await this._openConnection();
3276
+ if (!this._transportChannels.has(topic)) {
3277
+ if (!this._transportChannels.size) {
3278
+ this._lockAndCloseConnection();
3279
+ }
3280
+ throw new Error("Transport closed while connection was being open");
3281
+ }
3282
+ if (this._initiator) {
3283
+ const channel = connection.createDataChannel(topic);
3284
+ this._dataChannels.set(topic, channel);
3285
+ return channel;
3286
+ } else {
3287
+ const existingChannel = this._dataChannels.get(topic);
3288
+ if (existingChannel) {
3289
+ return existingChannel;
3290
+ }
3291
+ log13("waiting for initiator-peer to open a data channel", void 0, {
3292
+ F: __dxlog_file14,
3293
+ L: 90,
3294
+ S: this,
3295
+ C: (f, a) => f(...a)
3296
+ });
3297
+ return new Promise((resolve, reject) => {
3298
+ this._channelCreatedCallbacks.set(topic, {
3299
+ resolve,
3300
+ reject
3301
+ });
3302
+ });
3303
+ }
3304
+ }
3305
+ createTransportChannel(options) {
3306
+ const channel = new RtcTransportChannel(this, options);
3307
+ this._transportChannels.set(options.topic, channel);
3308
+ channel.closed.on(() => {
3309
+ this._transportChannels.delete(options.topic);
3310
+ if (this._transportChannels.size === 0) {
3311
+ this._lockAndCloseConnection();
3312
+ }
3313
+ });
3314
+ return channel;
3315
+ }
3316
+ async _openConnection() {
3317
+ if (this._connection) {
3318
+ return this._connection;
3319
+ }
3320
+ log13("initializing connection...", () => ({
3321
+ remotePeer: this._options.remotePeerKey
3322
+ }), {
3323
+ F: __dxlog_file14,
3324
+ L: 115,
3325
+ S: this,
3326
+ C: (f, a) => f(...a)
3327
+ });
3328
+ const config = await this._loadConnectionConfig();
3329
+ const connection = await this._factory.createConnection(config);
3330
+ const iceCandidateErrors = [];
3331
+ Object.assign(connection, {
3332
+ onnegotiationneeded: async () => {
3333
+ invariant12(this._initiator, void 0, {
3334
+ F: __dxlog_file14,
3335
+ L: 130,
3336
+ S: this,
3337
+ A: [
3338
+ "this._initiator",
3339
+ ""
3340
+ ]
3341
+ });
3342
+ if (connection !== this._connection) {
3343
+ this._onConnectionCallbackAfterClose("onnegotiationneeded", connection);
3344
+ return;
3345
+ }
3346
+ log13("onnegotiationneeded", void 0, {
3347
+ F: __dxlog_file14,
3348
+ L: 137,
3349
+ S: this,
3350
+ C: (f, a) => f(...a)
3351
+ });
3352
+ try {
3353
+ const offer = await connection.createOffer();
3354
+ await connection.setLocalDescription(offer);
3355
+ await this._sendDescription(connection, offer);
3356
+ } catch (err) {
3357
+ this._lockAndAbort(connection, err);
3358
+ }
3359
+ },
3360
+ // When ICE candidate identified (should be sent to remote peer) and when ICE gathering finalized.
3361
+ // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/icecandidate_event
3362
+ onicecandidate: async (event) => {
3363
+ if (connection !== this._connection) {
3364
+ this._onConnectionCallbackAfterClose("onicecandidate", connection);
3365
+ return;
3366
+ }
3367
+ if (event.candidate) {
3368
+ log13("onicecandidate", {
3369
+ candidate: event.candidate.candidate
3370
+ }, {
3371
+ F: __dxlog_file14,
3372
+ L: 156,
3373
+ S: this,
3374
+ C: (f, a) => f(...a)
3375
+ });
3376
+ await this._sendIceCandidate(event.candidate);
3377
+ } else {
3378
+ log13("onicecandidate gathering complete", void 0, {
3379
+ F: __dxlog_file14,
3380
+ L: 159,
3381
+ S: this,
3382
+ C: (f, a) => f(...a)
3383
+ });
3384
+ }
3385
+ },
3386
+ // When error occurs while performing ICE negotiations through a STUN or TURN server.
3387
+ // It's ok for some candidates to fail if a working pair is eventually found.
3388
+ // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/icecandidateerror_event
3389
+ onicecandidateerror: (event) => {
3390
+ const { url, errorCode, errorText } = event;
3391
+ iceCandidateErrors.push({
3392
+ url,
3393
+ errorCode,
3394
+ errorText
3395
+ });
3396
+ },
3397
+ // When possible error during ICE gathering.
3398
+ // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceconnectionstatechange_event
3399
+ oniceconnectionstatechange: () => {
3400
+ if (connection !== this._connection) {
3401
+ this._onConnectionCallbackAfterClose("oniceconnectionstatechange", connection);
3402
+ return;
3403
+ }
3404
+ log13("oniceconnectionstatechange", {
3405
+ state: connection.iceConnectionState
3406
+ }, {
3407
+ F: __dxlog_file14,
3408
+ L: 179,
3409
+ S: this,
3410
+ C: (f, a) => f(...a)
3411
+ });
3412
+ if (connection.iceConnectionState === "failed") {
3413
+ this._lockAndAbort(connection, createIceFailureError(iceCandidateErrors));
3414
+ }
3415
+ },
3416
+ // When new track (or channel) is added.
3417
+ // State: { new, connecting, connected, disconnected, failed, closed }
3418
+ // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionstatechange_event
3419
+ onconnectionstatechange: () => {
3420
+ if (connection !== this._connection) {
3421
+ if (connection.connectionState !== "closed" && connection.connectionState !== "failed") {
3422
+ this._onConnectionCallbackAfterClose("onconnectionstatechange", connection);
3423
+ }
3424
+ return;
3425
+ }
3426
+ log13("onconnectionstatechange", {
3427
+ state: connection.connectionState
3428
+ }, {
3429
+ F: __dxlog_file14,
3430
+ L: 196,
3431
+ S: this,
3432
+ C: (f, a) => f(...a)
3433
+ });
3434
+ if (connection.connectionState === "failed") {
3435
+ this._lockAndAbort(connection, new Error("Connection failed."));
3436
+ }
3437
+ },
3438
+ onsignalingstatechange: () => {
3439
+ log13("onsignalingstatechange", {
3440
+ state: connection.signalingState
3441
+ }, {
3442
+ F: __dxlog_file14,
3443
+ L: 203,
3444
+ S: this,
3445
+ C: (f, a) => f(...a)
3446
+ });
3447
+ },
3448
+ // When channel is added to connection.
3449
+ // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/datachannel_event
3450
+ ondatachannel: (event) => {
3451
+ invariant12(!this._initiator, "Initiator is expected to create data channels.", {
3452
+ F: __dxlog_file14,
3453
+ L: 209,
3454
+ S: this,
3455
+ A: [
3456
+ "!this._initiator",
3457
+ "'Initiator is expected to create data channels.'"
3458
+ ]
3459
+ });
3460
+ if (connection !== this._connection) {
3461
+ this._onConnectionCallbackAfterClose("ondatachannel", connection);
3462
+ return;
3463
+ }
3464
+ log13("ondatachannel", {
3465
+ label: event.channel.label
3466
+ }, {
3467
+ F: __dxlog_file14,
3468
+ L: 216,
3469
+ S: this,
3470
+ C: (f, a) => f(...a)
3471
+ });
3472
+ this._dataChannels.set(event.channel.label, event.channel);
3473
+ const pendingCallback = this._channelCreatedCallbacks.get(event.channel.label);
3474
+ if (pendingCallback) {
3475
+ this._channelCreatedCallbacks.delete(event.channel.label);
3476
+ pendingCallback.resolve(event.channel);
3477
+ }
3478
+ }
3479
+ });
3480
+ this._connection = connection;
3481
+ this._readyForCandidates.reset();
3482
+ await this._factory.initConnection(connection, {
3483
+ initiator: this._initiator
3484
+ });
3485
+ return this._connection;
3486
+ }
3487
+ _lockAndAbort(connection, error) {
3488
+ this._abortConnection(connection, error);
3489
+ }
3490
+ _abortConnection(connection, error) {
3491
+ if (connection !== this._connection) {
3492
+ log13.error("attempted to abort an inactive connection", {
3493
+ error
3494
+ }, {
3495
+ F: __dxlog_file14,
3496
+ L: 241,
3497
+ S: this,
3498
+ C: (f, a) => f(...a)
3499
+ });
3500
+ this._safeCloseConnection(connection);
3501
+ return;
3502
+ }
3503
+ for (const [topic, pendingCallback] of this._channelCreatedCallbacks.entries()) {
3504
+ pendingCallback.reject(error);
3505
+ this._transportChannels.delete(topic);
3506
+ }
3507
+ this._channelCreatedCallbacks.clear();
3508
+ for (const channel of this._transportChannels.values()) {
3509
+ channel.onConnectionError(error);
3510
+ }
3511
+ this._transportChannels.clear();
3512
+ this._safeCloseConnection();
3513
+ log13("connection aborted", {
3514
+ reason: error.message
3515
+ }, {
3516
+ F: __dxlog_file14,
3517
+ L: 255,
3518
+ S: this,
3519
+ C: (f, a) => f(...a)
3520
+ });
3521
+ }
3522
+ _lockAndCloseConnection() {
3523
+ invariant12(this._transportChannels.size === 0, void 0, {
3524
+ F: __dxlog_file14,
3525
+ L: 260,
3526
+ S: this,
3527
+ A: [
3528
+ "this._transportChannels.size === 0",
3529
+ ""
3530
+ ]
3531
+ });
3532
+ if (this._connection) {
3533
+ this._safeCloseConnection();
3534
+ log13("connection closed", void 0, {
3535
+ F: __dxlog_file14,
3536
+ L: 263,
3537
+ S: this,
3538
+ C: (f, a) => f(...a)
3539
+ });
3540
+ }
3541
+ }
3542
+ async onSignal(signal) {
3543
+ const connection = this._connection;
3544
+ if (!connection) {
3545
+ log13.warn("a signal ignored because the connection was closed", {
3546
+ type: signal.payload.data.type
3547
+ }, {
3548
+ F: __dxlog_file14,
3549
+ L: 271,
3550
+ S: this,
3551
+ C: (f, a) => f(...a)
3552
+ });
3553
+ return;
3554
+ }
3555
+ const data = signal.payload.data;
3556
+ switch (data.type) {
3557
+ case "offer": {
3558
+ await this._offerProcessingMutex.executeSynchronized(async () => {
3559
+ if (isRemoteDescriptionSet(connection, data)) {
3560
+ return;
3561
+ }
3562
+ if (connection.connectionState !== "new") {
3563
+ this._abortConnection(connection, new Error(`Received an offer in ${connection.connectionState}.`));
3564
+ return;
3565
+ }
3566
+ try {
3567
+ await connection.setRemoteDescription({
3568
+ type: data.type,
3569
+ sdp: data.sdp
3570
+ });
3571
+ const answer = await connection.createAnswer();
3572
+ await connection.setLocalDescription(answer);
3573
+ await this._sendDescription(connection, answer);
3574
+ this._onSessionNegotiated(connection);
3575
+ } catch (err) {
3576
+ this._abortConnection(connection, new Error("Error handling a remote offer.", {
3577
+ cause: err
3578
+ }));
3579
+ }
3580
+ });
3581
+ break;
3582
+ }
3583
+ case "answer":
3584
+ await this._offerProcessingMutex.executeSynchronized(async () => {
3585
+ try {
3586
+ if (isRemoteDescriptionSet(connection, data)) {
3587
+ return;
3588
+ }
3589
+ if (connection.signalingState !== "have-local-offer") {
3590
+ this._abortConnection(connection, new Error(`Unexpected answer from remote peer, signalingState was ${connection.signalingState}.`));
3591
+ return;
3592
+ }
3593
+ await connection.setRemoteDescription({
3594
+ type: data.type,
3595
+ sdp: data.sdp
3596
+ });
3597
+ this._onSessionNegotiated(connection);
3598
+ } catch (err) {
3599
+ this._abortConnection(connection, new Error("Error handling a remote answer.", {
3600
+ cause: err
3601
+ }));
3602
+ }
3603
+ });
3604
+ break;
3605
+ case "candidate":
3606
+ void this._processIceCandidate(connection, data.candidate);
3607
+ break;
3608
+ default:
3609
+ this._abortConnection(connection, new Error(`Unknown signal type ${data.type}.`));
3610
+ break;
3611
+ }
3612
+ log13("signal processed", void 0, {
3613
+ F: __dxlog_file14,
3614
+ L: 330,
3615
+ S: this,
3616
+ C: (f, a) => f(...a)
3617
+ });
3618
+ }
3619
+ async _processIceCandidate(connection, candidate) {
3620
+ try {
3621
+ await this._readyForCandidates.wait();
3622
+ if (connection === this._connection) {
3623
+ log13("adding ice candidate", {
3624
+ candidate
3625
+ }, {
3626
+ F: __dxlog_file14,
3627
+ L: 338,
3628
+ S: this,
3629
+ C: (f, a) => f(...a)
3630
+ });
3631
+ await connection.addIceCandidate(candidate);
3632
+ }
3633
+ } catch (err) {
3634
+ log13.catch(err, void 0, {
3635
+ F: __dxlog_file14,
3636
+ L: 342,
3637
+ S: this,
3638
+ C: (f, a) => f(...a)
3639
+ });
3640
+ }
3641
+ }
3642
+ _onSessionNegotiated(connection) {
3643
+ if (connection === this._connection) {
3644
+ log13("ready to process ice candidates", void 0, {
3645
+ F: __dxlog_file14,
3646
+ L: 348,
3647
+ S: this,
3648
+ C: (f, a) => f(...a)
3649
+ });
3650
+ this._readyForCandidates.wake();
3651
+ } else {
3652
+ log13.warn("session was negotiated after connection became inactive", void 0, {
3653
+ F: __dxlog_file14,
3654
+ L: 351,
3655
+ S: this,
3656
+ C: (f, a) => f(...a)
3657
+ });
3658
+ }
3659
+ }
3660
+ _onConnectionCallbackAfterClose(callback, connection) {
3661
+ log13.warn("callback invoked after a connection was destroyed, this is probably a bug", {
3662
+ callback,
3663
+ state: connection.connectionState
3664
+ }, {
3665
+ F: __dxlog_file14,
3666
+ L: 356,
3667
+ S: this,
3668
+ C: (f, a) => f(...a)
3669
+ });
3670
+ this._safeCloseConnection(connection);
3671
+ }
3672
+ _safeCloseConnection(connection = this._connection) {
3673
+ const resetFields = this._connection && connection === this._connection;
3674
+ try {
3675
+ connection?.close();
3676
+ } catch (err) {
3677
+ log13.catch(err, void 0, {
3678
+ F: __dxlog_file14,
3679
+ L: 368,
3680
+ S: this,
3681
+ C: (f, a) => f(...a)
3682
+ });
3683
+ }
3684
+ if (resetFields) {
3685
+ this._connection = void 0;
3686
+ this._dataChannels.clear();
3687
+ this._readyForCandidates.wake();
3688
+ void this._factory.onConnectionDestroyed().catch((err) => log13.catch(err, void 0, {
3689
+ F: __dxlog_file14,
3690
+ L: 374,
3691
+ S: this,
3692
+ C: (f, a) => f(...a)
3693
+ }));
3694
+ for (const [_, pendingCallback] of this._channelCreatedCallbacks.entries()) {
3695
+ pendingCallback.reject("Connection closed.");
3696
+ }
3697
+ this._channelCreatedCallbacks.clear();
3698
+ }
3699
+ }
3700
+ async _loadConnectionConfig() {
3701
+ const config = {
3702
+ ...this._options.webrtcConfig
3703
+ };
3704
+ try {
3705
+ const providedIceServers = await this._options.iceProvider?.getIceServers() ?? [];
3706
+ if (providedIceServers.length > 0) {
3707
+ config.iceServers = [
3708
+ ...config.iceServers ?? [],
3709
+ ...providedIceServers
3710
+ ];
3711
+ }
3712
+ } catch (error) {
3713
+ log13.catch(error, void 0, {
3714
+ F: __dxlog_file14,
3715
+ L: 390,
3716
+ S: this,
3717
+ C: (f, a) => f(...a)
3718
+ });
3719
+ }
3720
+ return config;
3721
+ }
3722
+ async _sendIceCandidate(candidate) {
3723
+ try {
3724
+ await this._options.sendSignal({
3725
+ payload: {
3726
+ data: {
3727
+ type: "candidate",
3728
+ candidate: {
3729
+ candidate: candidate.candidate,
3730
+ // These fields never seem to be not null, but connecting to Chrome doesn't work if they are.
3731
+ sdpMLineIndex: candidate.sdpMLineIndex ?? "0",
3732
+ sdpMid: candidate.sdpMid ?? "0"
3733
+ }
3734
+ }
3735
+ }
3736
+ });
3737
+ } catch (err) {
3738
+ log13.warn("signaling error", {
3739
+ err
3740
+ }, {
3741
+ F: __dxlog_file14,
3742
+ L: 411,
3743
+ S: this,
3744
+ C: (f, a) => f(...a)
3745
+ });
3746
+ }
3747
+ }
3748
+ async _sendDescription(connection, description) {
3749
+ if (connection !== this._connection) {
3750
+ return;
3751
+ }
3752
+ const data = {
3753
+ type: description.type,
3754
+ sdp: description.sdp
3755
+ };
3756
+ await this._options.sendSignal({
3757
+ payload: {
3758
+ data
3759
+ }
3760
+ });
3761
+ }
3762
+ get _connectionInfo() {
3763
+ const connectionInfo = this._connection && {
3764
+ connectionState: this._connection.connectionState,
3765
+ iceConnectionState: this._connection.iceConnectionState,
3766
+ iceGatheringState: this._connection.iceGatheringState,
3767
+ signalingState: this._connection.signalingState,
3768
+ remoteDescription: this._connection.remoteDescription,
3769
+ localDescription: this._connection.localDescription
3770
+ };
3771
+ return {
3772
+ ...connectionInfo,
3773
+ ts: Date.now(),
3774
+ remotePeerKey: this._options.remotePeerKey,
3775
+ channels: [
3776
+ ...this._transportChannels.keys()
3777
+ ].map((topic) => topic),
3778
+ config: this._connection?.getConfiguration()
3779
+ };
3780
+ }
3781
+ get _loggerContext() {
3782
+ return {
3783
+ ownPeerKey: this._options.ownPeerKey,
3784
+ remotePeerKey: this._options.remotePeerKey,
3785
+ initiator: this._initiator,
3786
+ channels: this._transportChannels.size
3787
+ };
3788
+ }
3789
+ };
3790
+ _ts_decorate6([
3791
+ synchronized5
3792
+ ], RtcPeerConnection.prototype, "_openConnection", null);
3793
+ _ts_decorate6([
3794
+ synchronized5
3795
+ ], RtcPeerConnection.prototype, "_lockAndAbort", null);
3796
+ _ts_decorate6([
3797
+ synchronized5
3798
+ ], RtcPeerConnection.prototype, "_lockAndCloseConnection", null);
3799
+ _ts_decorate6([
3800
+ synchronized5
3801
+ ], RtcPeerConnection.prototype, "onSignal", null);
3802
+ _ts_decorate6([
3803
+ trace4.info()
3804
+ ], RtcPeerConnection.prototype, "_connectionInfo", null);
3805
+ _ts_decorate6([
3806
+ logInfo4
3807
+ ], RtcPeerConnection.prototype, "_loggerContext", null);
3808
+ RtcPeerConnection = _ts_decorate6([
3809
+ trace4.resource()
3810
+ ], RtcPeerConnection);
3811
+ var isRemoteDescriptionSet = (connection, data) => {
3812
+ if (!connection.remoteDescription?.type || connection.remoteDescription?.type !== data.type) {
3813
+ return false;
3814
+ }
3815
+ return areSdpEqual(connection.remoteDescription.sdp, data.sdp);
3816
+ };
3817
+ var createIceFailureError = (details) => {
3818
+ const candidateErrors = details.map(({ url, errorCode, errorText }) => `${errorCode} ${url}: ${errorText}`);
3819
+ return new ConnectivityError3(`ICE failed:
3820
+ ${candidateErrors.join("\n")}`);
3821
+ };
3822
+
3823
+ // packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-factory.ts
3824
+ var createRtcTransportFactory = (webrtcConfig, iceProvider) => {
3825
+ const connectionFactory = getRtcConnectionFactory();
3826
+ return {
3827
+ createTransport: (options) => {
3828
+ const connection = new RtcPeerConnection(connectionFactory, {
3829
+ ownPeerKey: options.ownPeerKey,
3830
+ remotePeerKey: options.remotePeerKey,
3831
+ sendSignal: options.sendSignal,
3832
+ webrtcConfig,
3833
+ iceProvider
3834
+ });
3835
+ return connection.createTransportChannel(options);
3836
+ }
3837
+ };
3838
+ };
3839
+
3840
+ // packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-proxy.ts
3841
+ import { Writable } from "node:stream";
3842
+ import { Event as Event8, scheduleTask as scheduleTask4 } from "@dxos/async";
3843
+ import { Resource as Resource2 } from "@dxos/context";
3844
+ import { ErrorStream as ErrorStream5 } from "@dxos/debug";
3845
+ import { invariant as invariant13 } from "@dxos/invariant";
3846
+ import { PublicKey as PublicKey10 } from "@dxos/keys";
3847
+ import { log as log14 } from "@dxos/log";
3848
+ import { ConnectionResetError as ConnectionResetError2, ConnectivityError as ConnectivityError4, TimeoutError as TimeoutError3 } from "@dxos/protocols";
3849
+ import { ConnectionState as ConnectionState3 } from "@dxos/protocols/proto/dxos/mesh/bridge";
3850
+ import { arrayToBuffer } from "@dxos/util";
3851
+ var __dxlog_file15 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-proxy.ts";
3852
+ var RPC_TIMEOUT = 1e4;
3853
+ var CLOSE_RPC_TIMEOUT = 3e3;
3854
+ var RESP_MIN_THRESHOLD = 500;
3855
+ var RtcTransportProxy = class extends Resource2 {
3856
+ constructor(_options) {
3857
+ super();
3858
+ this._options = _options;
3859
+ this._proxyId = PublicKey10.random();
3860
+ this.closed = new Event8();
3861
+ this.connected = new Event8();
3862
+ this.errors = new ErrorStream5();
3863
+ }
3864
+ async _open() {
3865
+ let stream;
3866
+ try {
3867
+ stream = this._options.bridgeService.open({
3868
+ proxyId: this._proxyId,
3869
+ remotePeerKey: this._options.remotePeerKey,
3870
+ ownPeerKey: this._options.ownPeerKey,
3871
+ topic: this._options.topic,
3872
+ initiator: this._options.initiator ?? false
3873
+ }, {
3874
+ timeout: RPC_TIMEOUT
3875
+ });
3876
+ } catch (error) {
3877
+ this.errors.raise(error);
3878
+ return;
3879
+ }
3880
+ this._serviceStream = stream;
3881
+ stream.waitUntilReady().then(() => {
3882
+ stream.subscribe(async (event) => {
3883
+ log14("rtc transport proxy event", event, {
3884
+ F: __dxlog_file15,
3885
+ L: 66,
3886
+ S: this,
3887
+ C: (f, a) => f(...a)
3888
+ });
3889
+ if (event.connection) {
3890
+ await this._handleConnection(event.connection);
3891
+ } else if (event.data) {
3892
+ this._handleData(event.data);
3893
+ } else if (event.signal) {
3894
+ await this._handleSignal(event.signal);
3895
+ }
3896
+ }, (err) => {
3897
+ log14("rtc bridge stream closed", {
3898
+ err
3899
+ }, {
3900
+ F: __dxlog_file15,
3901
+ L: 76,
3902
+ S: this,
3903
+ C: (f, a) => f(...a)
3904
+ });
3905
+ if (err) {
3906
+ this._raiseIfOpen(err);
3907
+ } else {
3908
+ void this.close();
3909
+ }
3910
+ });
3911
+ const connectorStream = new Writable({
3912
+ write: (chunk, _, callback) => {
3913
+ const sendStartMs = Date.now();
3914
+ this._options.bridgeService.sendData({
3915
+ proxyId: this._proxyId,
3916
+ payload: chunk
3917
+ }, {
3918
+ timeout: RPC_TIMEOUT
3919
+ }).then(() => {
3920
+ if (Date.now() - sendStartMs > RESP_MIN_THRESHOLD) {
3921
+ log14("slow response, delaying callback", void 0, {
3922
+ F: __dxlog_file15,
3923
+ L: 93,
3924
+ S: this,
3925
+ C: (f, a) => f(...a)
3926
+ });
3927
+ scheduleTask4(this._ctx, () => callback(), RESP_MIN_THRESHOLD);
3928
+ } else {
3929
+ callback();
3930
+ }
3931
+ }, (err) => {
3932
+ callback();
3933
+ this._raiseIfOpen(err);
3934
+ });
3935
+ }
3936
+ });
3937
+ connectorStream.on("error", (err) => {
3938
+ this._raiseIfOpen(err);
3939
+ });
3940
+ this._options.stream.pipe(connectorStream);
3941
+ }, (error) => {
3942
+ if (error) {
3943
+ this._raiseIfOpen(error);
3944
+ } else {
3945
+ void this.close();
3946
+ }
3947
+ });
3948
+ }
3949
+ async _close() {
3950
+ try {
3951
+ await this._serviceStream?.close();
3952
+ this._serviceStream = void 0;
3953
+ } catch (err) {
3954
+ log14.catch(err, void 0, {
3955
+ F: __dxlog_file15,
3956
+ L: 128,
3957
+ S: this,
3958
+ C: (f, a) => f(...a)
3959
+ });
3960
+ }
3961
+ try {
3962
+ await this._options.bridgeService.close({
3963
+ proxyId: this._proxyId
3964
+ }, {
3965
+ timeout: CLOSE_RPC_TIMEOUT
3966
+ });
3967
+ } catch (err) {
3968
+ log14.catch(err, void 0, {
3969
+ F: __dxlog_file15,
3970
+ L: 134,
3971
+ S: this,
3972
+ C: (f, a) => f(...a)
3973
+ });
3974
+ }
3975
+ this.closed.emit();
3976
+ }
3977
+ async onSignal(signal) {
3978
+ this._options.bridgeService.sendSignal({
3979
+ proxyId: this._proxyId,
3980
+ signal
3981
+ }, {
3982
+ timeout: RPC_TIMEOUT
3983
+ }).catch((err) => this._raiseIfOpen(decodeError(err)));
3984
+ }
3985
+ async _handleConnection(connectionEvent) {
3986
+ if (connectionEvent.error) {
3987
+ this.errors.raise(decodeError(connectionEvent.error));
3988
+ return;
3989
+ }
3990
+ switch (connectionEvent.state) {
3991
+ case ConnectionState3.CONNECTED: {
3992
+ this.connected.emit();
3993
+ break;
3994
+ }
3995
+ case ConnectionState3.CLOSED: {
3996
+ await this.close();
3997
+ break;
3998
+ }
3999
+ }
4000
+ }
4001
+ _handleData(dataEvent) {
4002
+ try {
4003
+ this._options.stream.write(arrayToBuffer(dataEvent.payload));
4004
+ } catch (error) {
4005
+ this._raiseIfOpen(error);
4006
+ }
4007
+ }
4008
+ async _handleSignal(signalEvent) {
4009
+ try {
4010
+ await this._options.sendSignal(signalEvent.payload);
4011
+ } catch (error) {
4012
+ const type = signalEvent.payload.payload.data?.type;
4013
+ if (type === "offer" || type === "answer") {
4014
+ this._raiseIfOpen(new ConnectivityError4(`Session establishment failed: ${type} couldn't be sent.`));
4015
+ }
4016
+ }
4017
+ }
4018
+ async getDetails() {
4019
+ try {
4020
+ const response = await this._options.bridgeService.getDetails({
4021
+ proxyId: this._proxyId
4022
+ }, {
4023
+ timeout: RPC_TIMEOUT
4024
+ });
4025
+ return response.details;
4026
+ } catch (err) {
4027
+ return "bridge-svc unreachable";
4028
+ }
4029
+ }
4030
+ async getStats() {
4031
+ try {
4032
+ const response = await this._options.bridgeService.getStats({
4033
+ proxyId: this._proxyId
4034
+ }, {
4035
+ timeout: RPC_TIMEOUT
4036
+ });
4037
+ return response.stats;
4038
+ } catch (err) {
4039
+ return {
4040
+ bytesSent: 0,
4041
+ bytesReceived: 0,
4042
+ packetsSent: 0,
4043
+ packetsReceived: 0,
4044
+ rawStats: "bridge-svc unreachable"
4045
+ };
4046
+ }
4047
+ }
4048
+ _raiseIfOpen(error) {
4049
+ if (this.isOpen) {
4050
+ this.errors.raise(error);
4051
+ } else {
4052
+ log14.info("error swallowed because transport was closed", {
4053
+ message: error.message
4054
+ }, {
4055
+ F: __dxlog_file15,
4056
+ L: 215,
4057
+ S: this,
4058
+ C: (f, a) => f(...a)
4059
+ });
4060
+ }
4061
+ }
4062
+ /**
4063
+ * Called when underlying proxy service becomes unavailable.
4064
+ */
4065
+ forceClose() {
4066
+ void this._serviceStream?.close();
4067
+ this.closed.emit();
4068
+ }
4069
+ };
4070
+ var RtcTransportProxyFactory = class {
4071
+ constructor() {
4072
+ this._connections = /* @__PURE__ */ new Set();
4073
+ }
4074
+ /**
4075
+ * Sets the current BridgeService to be used to open connections.
4076
+ * Calling this method will close any existing connections.
4077
+ */
4078
+ setBridgeService(bridgeService) {
4079
+ this._bridgeService = bridgeService;
4080
+ for (const connection of this._connections) {
4081
+ connection.forceClose();
4082
+ }
4083
+ return this;
4084
+ }
4085
+ createTransport(options) {
4086
+ invariant13(this._bridgeService, "RtcTransportProxyFactory is not ready to open connections", {
4087
+ F: __dxlog_file15,
4088
+ L: 245,
4089
+ S: this,
4090
+ A: [
4091
+ "this._bridgeService",
4092
+ "'RtcTransportProxyFactory is not ready to open connections'"
4093
+ ]
4094
+ });
4095
+ const transport = new RtcTransportProxy({
4096
+ ...options,
4097
+ bridgeService: this._bridgeService
4098
+ });
4099
+ this._connections.add(transport);
4100
+ transport.closed.on(() => this._connections.delete(transport));
4101
+ return transport;
4102
+ }
4103
+ };
4104
+ var decodeError = (err) => {
4105
+ const message = typeof err === "string" ? err : err.message;
4106
+ if (message.includes("CONNECTION_RESET")) {
4107
+ return new ConnectionResetError2(message);
4108
+ } else if (message.includes("TIMEOUT")) {
4109
+ return new TimeoutError3(message);
4110
+ } else if (message.includes("CONNECTIVITY_ERROR")) {
4111
+ return new ConnectivityError4(message);
4112
+ } else {
4113
+ return typeof err === "string" ? new Error(err) : err;
4114
+ }
4115
+ };
4116
+
4117
+ // packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-service.ts
4118
+ import { Duplex as Duplex2 } from "node:stream";
4119
+ import { Stream } from "@dxos/codec-protobuf";
4120
+ import { invariant as invariant14 } from "@dxos/invariant";
4121
+ import { PublicKey as PublicKey11 } from "@dxos/keys";
4122
+ import { log as log15 } from "@dxos/log";
4123
+ import { ConnectionState as ConnectionState4 } from "@dxos/protocols/proto/dxos/mesh/bridge";
4124
+ import { ComplexMap as ComplexMap8 } from "@dxos/util";
4125
+ var __dxlog_file16 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-service.ts";
4126
+ var RtcTransportService = class {
4127
+ constructor(webrtcConfig, iceProvider, _transportFactory = createRtcTransportFactory(webrtcConfig, iceProvider)) {
4128
+ this._transportFactory = _transportFactory;
4129
+ this._openTransports = new ComplexMap8(PublicKey11.hash);
4130
+ }
4131
+ hasOpenTransports() {
4132
+ return this._openTransports.size > 0;
4133
+ }
4134
+ open(request) {
4135
+ const existingTransport = this._openTransports.get(request.proxyId);
4136
+ if (existingTransport) {
4137
+ log15.error("requesting a new transport bridge for an existing proxy", void 0, {
4138
+ F: __dxlog_file16,
4139
+ L: 53,
4140
+ S: this,
4141
+ C: (f, a) => f(...a)
4142
+ });
4143
+ void this._safeCloseTransport(existingTransport);
4144
+ this._openTransports.delete(request.proxyId);
4145
+ }
4146
+ return new Stream(({ ready, next, close }) => {
4147
+ const pushNewState = createStateUpdater(next);
4148
+ const transportStream = new Duplex2({
4149
+ read: () => {
4150
+ const callbacks = [
4151
+ ...transportState.writeProcessedCallbacks
4152
+ ];
4153
+ transportState.writeProcessedCallbacks.length = 0;
4154
+ callbacks.forEach((cb) => cb());
4155
+ },
4156
+ write: function(chunk, _, callback) {
4157
+ next({
4158
+ data: {
4159
+ payload: chunk
4160
+ }
4161
+ });
4162
+ callback();
4163
+ }
4164
+ });
4165
+ const transport = this._transportFactory.createTransport({
4166
+ initiator: request.initiator,
4167
+ topic: request.topic,
4168
+ ownPeerKey: request.ownPeerKey,
4169
+ remotePeerKey: request.remotePeerKey,
4170
+ stream: transportStream,
4171
+ sendSignal: async (signal) => {
4172
+ next({
4173
+ signal: {
4174
+ payload: signal
4175
+ }
4176
+ });
4177
+ }
4178
+ });
4179
+ const transportState = {
4180
+ proxyId: request.proxyId,
4181
+ transport,
4182
+ connectorStream: transportStream,
4183
+ writeProcessedCallbacks: []
4184
+ };
4185
+ pushNewState(ConnectionState4.CONNECTING);
4186
+ transport.connected.on(() => pushNewState(ConnectionState4.CONNECTED));
4187
+ transport.errors.handle(async (err) => {
4188
+ pushNewState(ConnectionState4.CLOSED, err);
4189
+ void this._safeCloseTransport(transportState);
4190
+ close(err);
4191
+ });
4192
+ transport.closed.on(async () => {
4193
+ pushNewState(ConnectionState4.CLOSED);
4194
+ void this._safeCloseTransport(transportState);
4195
+ close();
4196
+ });
4197
+ this._openTransports.set(request.proxyId, transportState);
4198
+ transport.open().catch(async (err) => {
4199
+ pushNewState(ConnectionState4.CLOSED, err);
4200
+ void this._safeCloseTransport(transportState);
4201
+ close(err);
4202
+ });
4203
+ ready();
4204
+ });
4205
+ }
4206
+ async sendSignal({ proxyId, signal }) {
4207
+ const transport = this._openTransports.get(proxyId);
4208
+ invariant14(transport, void 0, {
4209
+ F: __dxlog_file16,
4210
+ L: 121,
4211
+ S: this,
4212
+ A: [
4213
+ "transport",
4214
+ ""
4215
+ ]
4216
+ });
4217
+ await transport.transport.onSignal(signal);
4218
+ }
4219
+ async getDetails({ proxyId }) {
4220
+ const transport = this._openTransports.get(proxyId);
4221
+ invariant14(transport, void 0, {
4222
+ F: __dxlog_file16,
4223
+ L: 128,
4224
+ S: this,
4225
+ A: [
4226
+ "transport",
4227
+ ""
4228
+ ]
4229
+ });
4230
+ return {
4231
+ details: await transport.transport.getDetails()
4232
+ };
4233
+ }
4234
+ async getStats({ proxyId }) {
4235
+ const transport = this._openTransports.get(proxyId);
4236
+ invariant14(transport, void 0, {
4237
+ F: __dxlog_file16,
4238
+ L: 135,
4239
+ S: this,
4240
+ A: [
4241
+ "transport",
4242
+ ""
4243
+ ]
4244
+ });
4245
+ return {
4246
+ stats: await transport.transport.getStats()
4247
+ };
4248
+ }
4249
+ async sendData({ proxyId, payload }) {
4250
+ const transport = this._openTransports.get(proxyId);
4251
+ invariant14(transport, void 0, {
4252
+ F: __dxlog_file16,
4253
+ L: 142,
4254
+ S: this,
4255
+ A: [
4256
+ "transport",
4257
+ ""
4258
+ ]
4259
+ });
4260
+ const bufferHasSpace = transport.connectorStream.push(payload);
4261
+ if (!bufferHasSpace) {
4262
+ await new Promise((resolve) => {
4263
+ transport.writeProcessedCallbacks.push(resolve);
4264
+ });
4265
+ }
4266
+ }
4267
+ async close({ proxyId }) {
4268
+ const transport = this._openTransports.get(proxyId);
4269
+ if (!transport) {
4270
+ return;
4271
+ }
4272
+ this._openTransports.delete(proxyId);
4273
+ await this._safeCloseTransport(transport);
4274
+ }
4275
+ async _safeCloseTransport(transport) {
4276
+ if (this._openTransports.get(transport.proxyId) === transport) {
4277
+ this._openTransports.delete(transport.proxyId);
4278
+ }
4279
+ transport.writeProcessedCallbacks.forEach((cb) => cb());
4280
+ try {
4281
+ await transport.transport.close();
4282
+ } catch (error) {
4283
+ log15.warn("transport close error", {
4284
+ message: error?.message
4285
+ }, {
4286
+ F: __dxlog_file16,
4287
+ L: 172,
4288
+ S: this,
4289
+ C: (f, a) => f(...a)
4290
+ });
4291
+ }
4292
+ try {
4293
+ transport.connectorStream.end();
4294
+ } catch (error) {
4295
+ log15.warn("connectorStream close error", {
4296
+ message: error?.message
4297
+ }, {
4298
+ F: __dxlog_file16,
4299
+ L: 177,
4300
+ S: this,
4301
+ C: (f, a) => f(...a)
4302
+ });
4303
+ }
4304
+ log15("closed", void 0, {
4305
+ F: __dxlog_file16,
4306
+ L: 179,
4307
+ S: this,
4308
+ C: (f, a) => f(...a)
4309
+ });
4310
+ }
4311
+ };
4312
+ var createStateUpdater = (next) => {
4313
+ return (state, err) => {
4314
+ next({
4315
+ connection: {
4316
+ state,
4317
+ ...err ? {
4318
+ error: err.message
4319
+ } : void 0
4320
+ }
4321
+ });
4322
+ };
4323
+ };
4324
+
4325
+ // packages/core/mesh/network-manager/src/wire-protocol.ts
4326
+ import { Teleport } from "@dxos/teleport";
4327
+ var createTeleportProtocolFactory = (onConnection, defaultParams) => {
4328
+ return (params) => {
4329
+ const teleport = new Teleport({
4330
+ ...defaultParams,
4331
+ ...params
4332
+ });
4333
+ return {
4334
+ stream: teleport.stream,
4335
+ open: async (sessionId) => {
4336
+ await teleport.open(sessionId);
4337
+ await onConnection(teleport);
4338
+ },
4339
+ close: async () => {
4340
+ await teleport.close();
4341
+ },
4342
+ abort: async () => {
4343
+ await teleport.abort();
4344
+ }
4345
+ };
4346
+ };
4347
+ };
4348
+
4349
+ export {
4350
+ ConnectionState,
4351
+ Connection,
4352
+ createIceProvider,
4353
+ SwarmMessenger,
4354
+ Swarm,
4355
+ SwarmMapper,
4356
+ MAX_CONCURRENT_INITIATING_CONNECTIONS,
4357
+ ConnectionLimiter,
4358
+ EventType,
4359
+ ConnectionLog,
4360
+ SwarmNetworkManager,
4361
+ FullyConnectedTopology,
4362
+ MMSTTopology,
4363
+ StarTopology,
4364
+ MemoryTransportFactory,
4365
+ MemoryTransport,
4366
+ TransportKind,
4367
+ createRtcTransportFactory,
4368
+ RtcTransportProxy,
4369
+ RtcTransportProxyFactory,
4370
+ RtcTransportService,
4371
+ createTeleportProtocolFactory
4372
+ };
4373
+ //# sourceMappingURL=chunk-22DA2US6.mjs.map