@dxos/network-manager 0.6.12 → 0.6.13-main.548ca8d

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 (157) 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-IDPSMH73.mjs} +1230 -1031
  4. package/dist/lib/browser/chunk-IDPSMH73.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 +18 -27
  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-MCPADZS5.cjs} +1246 -1174
  12. package/dist/lib/node/chunk-MCPADZS5.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 +20 -29
  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-NL5MMIE4.mjs +4388 -0
  21. package/dist/lib/node-esm/chunk-NL5MMIE4.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 +280 -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/signal/ice.d.ts.map +1 -1
  30. package/dist/types/src/signal/integration.node.test.d.ts +2 -0
  31. package/dist/types/src/signal/integration.node.test.d.ts.map +1 -0
  32. package/dist/types/src/signal/swarm-messenger.node.test.d.ts +2 -0
  33. package/dist/types/src/signal/swarm-messenger.node.test.d.ts.map +1 -0
  34. package/dist/types/src/swarm/connection.d.ts.map +1 -1
  35. package/dist/types/src/swarm/swarm.d.ts +1 -1
  36. package/dist/types/src/testing/test-builder.d.ts +2 -2
  37. package/dist/types/src/testing/test-builder.d.ts.map +1 -1
  38. package/dist/types/src/tests/basic-test-suite.d.ts.map +1 -1
  39. package/dist/types/src/tests/property-test-suite.d.ts.map +1 -1
  40. package/dist/types/src/tests/tcp-transport.node.test.d.ts +2 -0
  41. package/dist/types/src/tests/tcp-transport.node.test.d.ts.map +1 -0
  42. package/dist/types/src/tests/utils.d.ts.map +1 -1
  43. package/dist/types/src/transport/index.d.ts +1 -5
  44. package/dist/types/src/transport/index.d.ts.map +1 -1
  45. package/dist/types/src/transport/memory-transport.d.ts +2 -2
  46. package/dist/types/src/transport/memory-transport.d.ts.map +1 -1
  47. package/dist/types/src/transport/tcp/index.d.ts +2 -0
  48. package/dist/types/src/transport/tcp/index.d.ts.map +1 -0
  49. package/dist/types/src/transport/{tcp-transport.browser.d.ts → tcp/tcp-transport.browser.d.ts} +3 -3
  50. package/dist/types/src/transport/tcp/tcp-transport.browser.d.ts.map +1 -0
  51. package/dist/types/src/transport/{tcp-transport.d.ts → tcp/tcp-transport.d.ts} +3 -3
  52. package/dist/types/src/transport/tcp/tcp-transport.d.ts.map +1 -0
  53. package/dist/types/src/transport/transport.d.ts +7 -6
  54. package/dist/types/src/transport/transport.d.ts.map +1 -1
  55. package/dist/types/src/transport/webrtc/index.d.ts +4 -0
  56. package/dist/types/src/transport/webrtc/index.d.ts.map +1 -0
  57. package/dist/types/src/transport/webrtc/rtc-connection-factory.d.ts +14 -0
  58. package/dist/types/src/transport/webrtc/rtc-connection-factory.d.ts.map +1 -0
  59. package/dist/types/src/transport/webrtc/rtc-peer-connection.d.ts +68 -0
  60. package/dist/types/src/transport/webrtc/rtc-peer-connection.d.ts.map +1 -0
  61. package/dist/types/src/transport/webrtc/rtc-transport-channel.d.ts +33 -0
  62. package/dist/types/src/transport/webrtc/rtc-transport-channel.d.ts.map +1 -0
  63. package/dist/types/src/transport/webrtc/rtc-transport-channel.test.d.ts +2 -0
  64. package/dist/types/src/transport/webrtc/rtc-transport-channel.test.d.ts.map +1 -0
  65. package/dist/types/src/transport/webrtc/rtc-transport-factory.d.ts +4 -0
  66. package/dist/types/src/transport/webrtc/rtc-transport-factory.d.ts.map +1 -0
  67. package/dist/types/src/transport/{simplepeer-transport-proxy.d.ts → webrtc/rtc-transport-proxy.d.ts} +10 -12
  68. package/dist/types/src/transport/webrtc/rtc-transport-proxy.d.ts.map +1 -0
  69. package/dist/types/src/transport/webrtc/rtc-transport-proxy.test.d.ts +2 -0
  70. package/dist/types/src/transport/webrtc/rtc-transport-proxy.test.d.ts.map +1 -0
  71. package/dist/types/src/transport/{simplepeer-transport-service.d.ts → webrtc/rtc-transport-service.d.ts} +9 -7
  72. package/dist/types/src/transport/webrtc/rtc-transport-service.d.ts.map +1 -0
  73. package/dist/types/src/transport/webrtc/rtc-transport-stats.d.ts +4 -0
  74. package/dist/types/src/transport/webrtc/rtc-transport-stats.d.ts.map +1 -0
  75. package/dist/types/src/transport/webrtc/rtc-transport.test.d.ts +2 -0
  76. package/dist/types/src/transport/webrtc/rtc-transport.test.d.ts.map +1 -0
  77. package/dist/types/src/transport/webrtc/test-utils.d.ts +5 -0
  78. package/dist/types/src/transport/webrtc/test-utils.d.ts.map +1 -0
  79. package/dist/types/src/transport/webrtc/utils.d.ts +3 -0
  80. package/dist/types/src/transport/webrtc/utils.d.ts.map +1 -0
  81. package/package.json +53 -30
  82. package/src/signal/ice.test.ts +1 -3
  83. package/src/signal/ice.ts +6 -1
  84. package/src/signal/{integration.test.ts → integration.node.test.ts} +9 -15
  85. package/src/signal/{swarm-messenger.test.ts → swarm-messenger.node.test.ts} +13 -23
  86. package/src/swarm/connection-limiter.test.ts +3 -6
  87. package/src/swarm/connection.test.ts +63 -38
  88. package/src/swarm/connection.ts +5 -5
  89. package/src/swarm/swarm.test.ts +9 -11
  90. package/src/swarm/swarm.ts +1 -1
  91. package/src/testing/test-builder.ts +12 -28
  92. package/src/tests/basic-test-suite.ts +34 -33
  93. package/src/tests/memory-transport.test.ts +40 -42
  94. package/src/tests/property-test-suite.ts +21 -22
  95. package/src/tests/tcp-transport.node.test.ts +65 -0
  96. package/src/tests/utils.ts +3 -2
  97. package/src/tests/webrtc-transport.test.ts +9 -9
  98. package/src/transport/index.ts +1 -5
  99. package/src/transport/memory-transport.ts +2 -0
  100. package/src/transport/tcp/index.ts +5 -0
  101. package/src/transport/{tcp-transport.browser.ts → tcp/tcp-transport.browser.ts} +7 -3
  102. package/src/transport/{tcp-transport.ts → tcp/tcp-transport.ts} +3 -1
  103. package/src/transport/transport.ts +8 -7
  104. package/src/transport/webrtc/index.ts +7 -0
  105. package/src/transport/webrtc/rtc-connection-factory.ts +82 -0
  106. package/src/transport/webrtc/rtc-peer-connection.ts +472 -0
  107. package/src/transport/webrtc/rtc-transport-channel.test.ts +176 -0
  108. package/src/transport/webrtc/rtc-transport-channel.ts +195 -0
  109. package/src/transport/webrtc/rtc-transport-factory.ts +28 -0
  110. package/src/transport/webrtc/rtc-transport-proxy.test.ts +413 -0
  111. package/src/transport/webrtc/rtc-transport-proxy.ts +264 -0
  112. package/src/transport/webrtc/rtc-transport-service.ts +192 -0
  113. package/src/transport/webrtc/rtc-transport-stats.ts +67 -0
  114. package/src/transport/webrtc/rtc-transport.test.ts +198 -0
  115. package/src/transport/webrtc/test-utils.ts +22 -0
  116. package/src/transport/webrtc/utils.ts +36 -0
  117. package/src/typings.d.ts +8 -2
  118. package/dist/lib/browser/chunk-XYSYUN63.mjs.map +0 -7
  119. package/dist/lib/node/chunk-4YAYC7WN.cjs.map +0 -7
  120. package/dist/types/src/signal/integration.test.d.ts +0 -2
  121. package/dist/types/src/signal/integration.test.d.ts.map +0 -1
  122. package/dist/types/src/signal/swarm-messenger.test.d.ts +0 -2
  123. package/dist/types/src/signal/swarm-messenger.test.d.ts.map +0 -1
  124. package/dist/types/src/tests/tcp-transport.test.d.ts +0 -2
  125. package/dist/types/src/tests/tcp-transport.test.d.ts.map +0 -1
  126. package/dist/types/src/transport/libdatachannel-transport.d.ts +0 -42
  127. package/dist/types/src/transport/libdatachannel-transport.d.ts.map +0 -1
  128. package/dist/types/src/transport/libdatachannel-transport.test.d.ts +0 -2
  129. package/dist/types/src/transport/libdatachannel-transport.test.d.ts.map +0 -1
  130. package/dist/types/src/transport/memory-transport.test.d.ts +0 -2
  131. package/dist/types/src/transport/memory-transport.test.d.ts.map +0 -1
  132. package/dist/types/src/transport/simplepeer-simple-peer.d.ts +0 -2
  133. package/dist/types/src/transport/simplepeer-simple-peer.d.ts.map +0 -1
  134. package/dist/types/src/transport/simplepeer-transport-proxy-test.d.ts +0 -2
  135. package/dist/types/src/transport/simplepeer-transport-proxy-test.d.ts.map +0 -1
  136. package/dist/types/src/transport/simplepeer-transport-proxy.d.ts.map +0 -1
  137. package/dist/types/src/transport/simplepeer-transport-service.d.ts.map +0 -1
  138. package/dist/types/src/transport/simplepeer-transport.d.ts +0 -36
  139. package/dist/types/src/transport/simplepeer-transport.d.ts.map +0 -1
  140. package/dist/types/src/transport/simplepeer-transport.test.d.ts +0 -2
  141. package/dist/types/src/transport/simplepeer-transport.test.d.ts.map +0 -1
  142. package/dist/types/src/transport/tcp-transport.browser.d.ts.map +0 -1
  143. package/dist/types/src/transport/tcp-transport.d.ts.map +0 -1
  144. package/dist/types/src/transport/webrtc.d.ts +0 -6
  145. package/dist/types/src/transport/webrtc.d.ts.map +0 -1
  146. package/src/globals.d.ts +0 -7
  147. package/src/tests/tcp-transport.test.ts +0 -67
  148. package/src/transport/libdatachannel-transport.test.ts +0 -100
  149. package/src/transport/libdatachannel-transport.ts +0 -376
  150. package/src/transport/memory-transport.test.ts +0 -74
  151. package/src/transport/simplepeer-simple-peer.ts +0 -26
  152. package/src/transport/simplepeer-transport-proxy-test.ts +0 -181
  153. package/src/transport/simplepeer-transport-proxy.ts +0 -246
  154. package/src/transport/simplepeer-transport-service.ts +0 -160
  155. package/src/transport/simplepeer-transport.test.ts +0 -61
  156. package/src/transport/simplepeer-transport.ts +0 -250
  157. package/src/transport/webrtc.ts +0 -15
@@ -1,17 +1,7 @@
1
1
  import "@dxos/node-std/globals";
2
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
- }) : x)(function(x) {
5
- if (typeof require !== "undefined") return require.apply(this, arguments);
6
- throw Error('Dynamic require of "' + x + '" is not supported');
7
- });
8
-
9
- // inject-globals:@inject-globals
10
2
  import {
11
- global,
12
- Buffer as Buffer2,
13
- process
14
- } from "@dxos/node-std/inject-globals";
3
+ Buffer
4
+ } from "./chunk-GW3YM55A.mjs";
15
5
 
16
6
  // packages/core/mesh/network-manager/src/swarm/connection.ts
17
7
  import { DeferredTask, Event, sleep, scheduleTask, scheduleTaskInterval, synchronized, Trigger } from "@dxos/async";
@@ -20,7 +10,7 @@ import { ErrorStream } from "@dxos/debug";
20
10
  import { invariant } from "@dxos/invariant";
21
11
  import { PublicKey } from "@dxos/keys";
22
12
  import { log, logInfo } from "@dxos/log";
23
- import { CancelledError, ProtocolError, ConnectionResetError, ConnectivityError, TimeoutError, UnknownProtocolError, trace } from "@dxos/protocols";
13
+ import { CancelledError, ProtocolError, ConnectionResetError, ConnectivityError, TimeoutError, trace } from "@dxos/protocols";
24
14
  function _ts_decorate(decorators, target, key, desc) {
25
15
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
26
16
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -56,11 +46,11 @@ var Connection = class {
56
46
  this._callbacks = _callbacks;
57
47
  this._ctx = new Context(void 0, {
58
48
  F: __dxlog_file,
59
- L: 101
49
+ L: 100
60
50
  });
61
51
  this.connectedTimeoutContext = new Context(void 0, {
62
52
  F: __dxlog_file,
63
- L: 102
53
+ L: 101
64
54
  });
65
55
  this._protocolClosed = new Trigger();
66
56
  this._transportClosed = new Trigger();
@@ -83,7 +73,7 @@ var Connection = class {
83
73
  initiator: this.initiator
84
74
  }, {
85
75
  F: __dxlog_file,
86
- L: 138,
76
+ L: 137,
87
77
  S: this,
88
78
  C: (f, a) => f(...a)
89
79
  });
@@ -106,7 +96,7 @@ var Connection = class {
106
96
  async openConnection() {
107
97
  invariant(this._state === "INITIAL", "Invalid state.", {
108
98
  F: __dxlog_file,
109
- L: 168,
99
+ L: 167,
110
100
  S: this,
111
101
  A: [
112
102
  "this._state === ConnectionState.INITIAL",
@@ -117,7 +107,7 @@ var Connection = class {
117
107
  id: this._instanceId
118
108
  }), {
119
109
  F: __dxlog_file,
120
- L: 169,
110
+ L: 168,
121
111
  S: this,
122
112
  C: (f, a) => f(...a)
123
113
  });
@@ -129,7 +119,7 @@ var Connection = class {
129
119
  initiator: this.initiator
130
120
  }, {
131
121
  F: __dxlog_file,
132
- L: 170,
122
+ L: 169,
133
123
  S: this,
134
124
  C: (f, a) => f(...a)
135
125
  });
@@ -140,7 +130,7 @@ var Connection = class {
140
130
  this._protocol.stream.on("close", () => {
141
131
  log("protocol stream closed", void 0, {
142
132
  F: __dxlog_file,
143
- L: 187,
133
+ L: 186,
144
134
  S: this,
145
135
  C: (f, a) => f(...a)
146
136
  });
@@ -150,7 +140,7 @@ var Connection = class {
150
140
  scheduleTask(this.connectedTimeoutContext, async () => {
151
141
  log.info(`timeout waiting ${TRANSPORT_CONNECTION_TIMEOUT / 1e3}s for transport to connect, aborting`, void 0, {
152
142
  F: __dxlog_file,
153
- L: 195,
143
+ L: 194,
154
144
  S: this,
155
145
  C: (f, a) => f(...a)
156
146
  });
@@ -158,7 +148,7 @@ var Connection = class {
158
148
  }, TRANSPORT_CONNECTION_TIMEOUT);
159
149
  invariant(!this._transport, void 0, {
160
150
  F: __dxlog_file,
161
- L: 203,
151
+ L: 202,
162
152
  S: this,
163
153
  A: [
164
154
  "!this._transport",
@@ -166,12 +156,14 @@ var Connection = class {
166
156
  ]
167
157
  });
168
158
  this._transport = this._transportFactory.createTransport({
159
+ ownPeerKey: this.localInfo.peerKey,
160
+ remotePeerKey: this.remoteInfo.peerKey,
161
+ topic: this.topic.toHex(),
169
162
  initiator: this.initiator,
170
163
  stream: this._protocol.stream,
171
164
  sendSignal: async (signal) => this._sendSignal(signal),
172
165
  sessionId: this.sessionId
173
166
  });
174
- await this._transport.open();
175
167
  this._transport.connected.once(async () => {
176
168
  this._changeState("CONNECTED");
177
169
  await this.connectedTimeoutContext.dispose();
@@ -217,21 +209,13 @@ var Connection = class {
217
209
  C: (f, a) => f(...a)
218
210
  });
219
211
  this.abort().catch((err2) => this.errors.raise(err2));
220
- } else if (err instanceof UnknownProtocolError) {
221
- log.warn("unsure what to do with UnknownProtocolError, will keep on truckin", {
222
- err
223
- }, {
224
- F: __dxlog_file,
225
- L: 242,
226
- S: this,
227
- C: (f, a) => f(...a)
228
- });
229
212
  }
230
213
  if (this._state !== "CLOSED" && this._state !== "CLOSING") {
231
214
  await this.connectedTimeoutContext.dispose();
232
215
  this.errors.raise(err);
233
216
  }
234
217
  });
218
+ await this._transport.open();
235
219
  for (const signal of this._incomingSignalBuffer) {
236
220
  void this._transport.onSignal(signal);
237
221
  }
@@ -598,15 +582,20 @@ var createIceProvider = (iceProviders) => {
598
582
  }
599
583
  cachedIceServers = (await Promise.all(iceProviders.map(({ urls }) => asyncTimeout(fetch(urls, {
600
584
  method: "GET"
601
- }), 1e4).then((response) => response.json()).catch((err) => log2.error("Failed to fetch ICE servers from provider", {
602
- urls,
603
- err
604
- }, {
605
- F: __dxlog_file2,
606
- L: 27,
607
- S: void 0,
608
- C: (f, a) => f(...a)
609
- }))))).filter(isNotNullOrUndefined).map(({ iceServers }) => iceServers).flat();
585
+ }), 1e4).then((response) => response.json()).catch((err) => {
586
+ const isDev = typeof window !== "undefined" && window.location.href.includes("localhost");
587
+ if (!isDev) {
588
+ log2.error("Failed to fetch ICE servers from provider", {
589
+ urls,
590
+ err
591
+ }, {
592
+ F: __dxlog_file2,
593
+ L: 30,
594
+ S: void 0,
595
+ C: (f, a) => f(...a)
596
+ });
597
+ }
598
+ })))).filter(isNotNullOrUndefined).map(({ iceServers }) => iceServers).flat();
610
599
  return cachedIceServers;
611
600
  }
612
601
  };
@@ -2630,7 +2619,7 @@ var sortByXorDistance = (keys, reference) => {
2630
2619
  };
2631
2620
  var distXor = (a, b) => {
2632
2621
  const maxLength = Math.max(a.length, b.length);
2633
- const result = Buffer2.allocUnsafe(maxLength);
2622
+ const result = Buffer.allocUnsafe(maxLength);
2634
2623
  for (let i = 0; i < maxLength; i++) {
2635
2624
  result[i] = (a[i] || 0) ^ (b[i] || 0);
2636
2625
  }
@@ -2865,11 +2854,12 @@ var MemoryTransport = class _MemoryTransport {
2865
2854
  this.errors.raise(err);
2866
2855
  });
2867
2856
  }
2857
+ return this;
2868
2858
  }
2869
2859
  async close() {
2870
2860
  log11("closing...", void 0, {
2871
2861
  F: __dxlog_file12,
2872
- L: 129,
2862
+ L: 130,
2873
2863
  S: this,
2874
2864
  C: (f, a) => f(...a)
2875
2865
  });
@@ -2890,17 +2880,18 @@ var MemoryTransport = class _MemoryTransport {
2890
2880
  this.closed.emit();
2891
2881
  log11("closed", void 0, {
2892
2882
  F: __dxlog_file12,
2893
- L: 157,
2883
+ L: 158,
2894
2884
  S: this,
2895
2885
  C: (f, a) => f(...a)
2896
2886
  });
2887
+ return this;
2897
2888
  }
2898
2889
  async onSignal({ payload }) {
2899
2890
  log11("received signal", {
2900
2891
  payload
2901
2892
  }, {
2902
2893
  F: __dxlog_file12,
2903
- L: 161,
2894
+ L: 163,
2904
2895
  S: this,
2905
2896
  C: (f, a) => f(...a)
2906
2897
  });
@@ -2936,524 +2927,978 @@ var toError = (err) => err instanceof Error ? err : new Error(String(err));
2936
2927
  // packages/core/mesh/network-manager/src/transport/transport.ts
2937
2928
  var TransportKind;
2938
2929
  (function(TransportKind2) {
2939
- TransportKind2["SIMPLE_PEER"] = "SIMPLE_PEER";
2940
- TransportKind2["SIMPLE_PEER_PROXY"] = "SIMPLE_PEER_PROXY";
2941
- TransportKind2["LIBDATACHANNEL"] = "LIBDATACHANNEL";
2930
+ TransportKind2["WEB_RTC"] = "WEB-RTC";
2931
+ TransportKind2["WEB_RTC_PROXY"] = "WEB-RTC_PROXY";
2942
2932
  TransportKind2["MEMORY"] = "MEMORY";
2943
2933
  TransportKind2["TCP"] = "TCP";
2944
2934
  })(TransportKind || (TransportKind = {}));
2945
2935
 
2946
- // packages/core/mesh/network-manager/src/transport/simplepeer-transport.ts
2947
- import SimplePeerConstructor from "simple-peer";
2948
- import invariant11 from "tiny-invariant";
2949
- import { Event as Event8, synchronized as synchronized5 } from "@dxos/async";
2950
- import { ErrorStream as ErrorStream4, raise as raise2 } from "@dxos/debug";
2951
- import { PublicKey as PublicKey10 } from "@dxos/keys";
2952
- import { log as log12 } from "@dxos/log";
2953
- import { ConnectionResetError as ConnectionResetError2, ConnectivityError as ConnectivityError2, ProtocolError as ProtocolError2, UnknownProtocolError as UnknownProtocolError2, trace as trace4 } from "@dxos/protocols";
2936
+ // packages/core/mesh/network-manager/src/transport/webrtc/rtc-connection-factory.ts
2937
+ import { Mutex } from "@dxos/async";
2938
+ var BrowserRtcConnectionFactory = class {
2939
+ async initialize() {
2940
+ }
2941
+ async onConnectionDestroyed() {
2942
+ }
2943
+ async createConnection(config) {
2944
+ return new RTCPeerConnection(config);
2945
+ }
2946
+ async initConnection(connection, info) {
2947
+ }
2948
+ };
2949
+ var NodeRtcConnectionFactory = class _NodeRtcConnectionFactory {
2950
+ static {
2951
+ this._createdConnections = 0;
2952
+ }
2953
+ static {
2954
+ this._cleanupMutex = new Mutex();
2955
+ }
2956
+ // This should be inside the function to avoid triggering `eval` in the global scope.
2957
+ // eslint-disable-next-line no-new-func
2958
+ // TODO(burdon): Do imports here?
2959
+ async initialize() {
2960
+ }
2961
+ async onConnectionDestroyed() {
2962
+ return _NodeRtcConnectionFactory._cleanupMutex.executeSynchronized(async () => {
2963
+ if (--_NodeRtcConnectionFactory._createdConnections === 0) {
2964
+ (await import("#node-datachannel")).cleanup();
2965
+ }
2966
+ });
2967
+ }
2968
+ async createConnection(config) {
2969
+ return _NodeRtcConnectionFactory._cleanupMutex.executeSynchronized(async () => {
2970
+ const { RTCPeerConnection: RTCPeerConnection1 } = await import("#node-datachannel/polyfill");
2971
+ _NodeRtcConnectionFactory._createdConnections++;
2972
+ return new RTCPeerConnection1(config);
2973
+ });
2974
+ }
2975
+ async initConnection(connection, info) {
2976
+ if (info.initiator) {
2977
+ connection.onnegotiationneeded?.(null);
2978
+ }
2979
+ }
2980
+ };
2981
+ var getRtcConnectionFactory = () => {
2982
+ return typeof globalThis.RTCPeerConnection === "undefined" ? new NodeRtcConnectionFactory() : new BrowserRtcConnectionFactory();
2983
+ };
2954
2984
 
2955
- // packages/core/mesh/network-manager/src/transport/webrtc.ts
2956
- var wrtc = null;
2957
- try {
2958
- wrtc = __require("@koush/wrtc");
2959
- } catch {
2960
- }
2985
+ // packages/core/mesh/network-manager/src/transport/webrtc/rtc-peer-connection.ts
2986
+ import { synchronized as synchronized5, Trigger as Trigger3, Mutex as Mutex2 } from "@dxos/async";
2987
+ import { invariant as invariant12 } from "@dxos/invariant";
2988
+ import { log as log13, logInfo as logInfo4 } from "@dxos/log";
2989
+ import { ConnectivityError as ConnectivityError3 } from "@dxos/protocols";
2990
+ import { trace as trace4 } from "@dxos/tracing";
2961
2991
 
2962
- // packages/core/mesh/network-manager/src/transport/simplepeer-transport.ts
2963
- function _ts_decorate6(decorators, target, key, desc) {
2964
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2965
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2966
- 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;
2967
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2968
- }
2969
- var __dxlog_file13 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/transport/simplepeer-transport.ts";
2970
- var createSimplePeerTransportFactory = (webrtcConfig, iceProvider) => ({
2971
- createTransport: (options) => new SimplePeerTransport({
2972
- ...options,
2973
- webrtcConfig,
2974
- iceProvider
2975
- })
2976
- });
2977
- var SimplePeerTransport = class {
2978
- get isOpen() {
2979
- return this._piped && !this._closed;
2992
+ // packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-channel.ts
2993
+ import { Duplex } from "@dxos/node-std/stream";
2994
+ import { Event as AsyncEvent } from "@dxos/async";
2995
+ import { Resource } from "@dxos/context";
2996
+ import { ErrorStream as ErrorStream4 } from "@dxos/debug";
2997
+ import { invariant as invariant11 } from "@dxos/invariant";
2998
+ import { log as log12 } from "@dxos/log";
2999
+ import { ConnectivityError as ConnectivityError2 } from "@dxos/protocols";
3000
+
3001
+ // packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-stats.ts
3002
+ var describeSelectedRemoteCandidate = async (connection) => {
3003
+ const stats = connection && await getRtcConnectionStats(connection);
3004
+ const rc = stats?.remoteCandidate;
3005
+ if (!rc) {
3006
+ return "unavailable";
2980
3007
  }
2981
- /**
2982
- * @params opts.config formatted as per https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection
2983
- */
2984
- constructor(_params) {
2985
- this._params = _params;
2986
- this._peer = void 0;
2987
- this._closed = false;
2988
- this._piped = false;
2989
- this.closed = new Event8();
2990
- this.connected = new Event8();
2991
- this.errors = new ErrorStream4();
2992
- this._instanceId = PublicKey10.random().toHex();
3008
+ if (rc.candidateType === "relay") {
3009
+ return `${rc.ip}:${rc.port} relay for ${rc.relatedAddress}:${rc.relatedPort}`;
2993
3010
  }
2994
- async getStats() {
2995
- const stats = await this._getStats();
2996
- if (!stats) {
2997
- return {
2998
- bytesSent: 0,
2999
- bytesReceived: 0,
3000
- packetsSent: 0,
3001
- packetsReceived: 0,
3002
- rawStats: {}
3003
- };
3004
- }
3011
+ return `${rc.ip}:${rc.port} ${rc.candidateType}`;
3012
+ };
3013
+ var createRtcTransportStats = async (connection, topic) => {
3014
+ const stats = connection && await getRtcConnectionStats(connection, topic);
3015
+ if (!stats) {
3005
3016
  return {
3006
- bytesSent: stats.transport.bytesSent,
3007
- bytesReceived: stats.transport.bytesReceived,
3008
- packetsSent: stats.transport.packetsSent,
3009
- packetsReceived: stats.transport.packetsReceived,
3010
- rawStats: stats.raw
3017
+ bytesSent: 0,
3018
+ bytesReceived: 0,
3019
+ packetsSent: 0,
3020
+ packetsReceived: 0,
3021
+ rawStats: {}
3011
3022
  };
3012
3023
  }
3013
- async _getStats() {
3014
- if (typeof this._peer?._pc?.getStats !== "function") {
3015
- return null;
3016
- }
3017
- return await this._peer._pc.getStats().then((stats) => {
3018
- const statsEntries = Array.from(stats.entries());
3019
- const transport = statsEntries.filter((s) => s[1].type === "transport")[0][1];
3020
- const candidatePair = statsEntries.filter((s) => s[0] === transport.selectedCandidatePairId);
3021
- let selectedCandidatePair;
3022
- let remoteCandidate;
3023
- if (candidatePair.length > 0) {
3024
- selectedCandidatePair = candidatePair[0][1];
3025
- remoteCandidate = statsEntries.filter((s) => s[0] === selectedCandidatePair.remoteCandidateId)[0][1];
3026
- }
3027
- return {
3028
- datachannel: statsEntries.filter((s) => s[1].type === "data-channel")[0][1],
3029
- transport,
3030
- selectedCandidatePair,
3031
- remoteCandidate,
3032
- raw: Object.fromEntries(stats.entries())
3033
- };
3034
- });
3024
+ return {
3025
+ bytesSent: stats.dataChannel?.bytesSent,
3026
+ bytesReceived: stats.dataChannel?.bytesReceived,
3027
+ packetsSent: 0,
3028
+ packetsReceived: 0,
3029
+ rawStats: stats.raw
3030
+ };
3031
+ };
3032
+ var getRtcConnectionStats = async (connection, channelTopic) => {
3033
+ const stats = await connection.getStats();
3034
+ const statsEntries = Array.from(stats.entries());
3035
+ const transport = statsEntries.find(([_, entry]) => entry.type === "transport")?.[1];
3036
+ const selectedCandidatePair = transport && statsEntries.find(([entryId]) => entryId === transport.selectedCandidatePairId)?.[1];
3037
+ const remoteCandidate = selectedCandidatePair && statsEntries.find(([entryId]) => entryId === selectedCandidatePair.remoteCandidateId)?.[1];
3038
+ const dataChannel = channelTopic && statsEntries.find(([_, entry]) => entry.type === "data-channel" && entry.label === channelTopic)?.[1];
3039
+ return {
3040
+ transport,
3041
+ selectedCandidatePair,
3042
+ dataChannel,
3043
+ remoteCandidate,
3044
+ raw: Object.fromEntries(stats)
3045
+ };
3046
+ };
3047
+
3048
+ // packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-channel.ts
3049
+ var __dxlog_file13 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-channel.ts";
3050
+ var MAX_MESSAGE_SIZE = 64 * 1024;
3051
+ var MAX_BUFFERED_AMOUNT = 64 * 1024;
3052
+ var RtcTransportChannel = class extends Resource {
3053
+ constructor(_connection, _options) {
3054
+ super();
3055
+ this._connection = _connection;
3056
+ this._options = _options;
3057
+ this.closed = new AsyncEvent();
3058
+ this.connected = new AsyncEvent();
3059
+ this.errors = new ErrorStream4();
3060
+ this._streamDataFlushedCallback = null;
3061
+ this._isChannelCreationInProgress = false;
3035
3062
  }
3036
- async getDetails() {
3037
- const stats = await this._getStats();
3038
- const rc = stats?.remoteCandidate;
3039
- if (!rc) {
3040
- return "unavailable";
3041
- }
3042
- if (rc.candidateType === "relay") {
3043
- return `${rc.ip}:${rc.port}/${rc.protocol} relay for ${rc.relatedAddress}:${rc.relatedPort}`;
3063
+ get isRtcChannelCreationInProgress() {
3064
+ return this._isChannelCreationInProgress;
3065
+ }
3066
+ onConnectionError(error) {
3067
+ if (this.isOpen) {
3068
+ this.errors.raise(error);
3044
3069
  }
3045
- return `${rc.ip}:${rc.port}/${rc.protocol} ${rc.candidateType}`;
3046
3070
  }
3047
- async open() {
3048
- log12.trace("dxos.mesh.webrtc-transport.open", trace4.begin({
3049
- id: this._instanceId
3050
- }), {
3071
+ async _open() {
3072
+ invariant11(!this._isChannelCreationInProgress, void 0, {
3051
3073
  F: __dxlog_file13,
3052
- L: 122,
3074
+ L: 56,
3053
3075
  S: this,
3054
- C: (f, a) => f(...a)
3076
+ A: [
3077
+ "!this._isChannelCreationInProgress",
3078
+ ""
3079
+ ]
3055
3080
  });
3056
- log12("created connection", {
3057
- params: this._params
3058
- }, {
3081
+ this._isChannelCreationInProgress = true;
3082
+ this._connection.createDataChannel(this._options.topic).then((channel) => {
3083
+ if (this.isOpen) {
3084
+ this._channel = channel;
3085
+ this._initChannel(this._channel);
3086
+ } else {
3087
+ this._safeCloseChannel(channel);
3088
+ }
3089
+ }).catch((err) => {
3090
+ if (this.isOpen) {
3091
+ this.errors.raise(new ConnectivityError2(`Failed to create a channel: ${err?.message ?? "unknown reason."}`));
3092
+ }
3093
+ }).finally(() => {
3094
+ this._isChannelCreationInProgress = false;
3095
+ });
3096
+ }
3097
+ async _close() {
3098
+ if (this._channel) {
3099
+ this._safeCloseChannel(this._channel);
3100
+ this._channel = void 0;
3101
+ this._stream = void 0;
3102
+ }
3103
+ this.closed.emit();
3104
+ log12("closed", void 0, {
3059
3105
  F: __dxlog_file13,
3060
- L: 123,
3106
+ L: 86,
3061
3107
  S: this,
3062
3108
  C: (f, a) => f(...a)
3063
3109
  });
3064
- const providedIceServers = await this._params.iceProvider?.getIceServers();
3065
- if (!this._params.webrtcConfig) {
3066
- this._params.webrtcConfig = {};
3067
- }
3068
- this._params.webrtcConfig.iceServers = [
3069
- ...this._params.webrtcConfig.iceServers ?? [],
3070
- ...providedIceServers ?? []
3071
- ];
3072
- this._peer = new SimplePeerConstructor({
3073
- channelName: "dxos.mesh.transport",
3074
- initiator: this._params.initiator,
3075
- wrtc: SimplePeerConstructor.WEBRTC_SUPPORT ? void 0 : wrtc ?? raise2(new Error("wrtc not available")),
3076
- config: this._params.webrtcConfig
3077
- });
3078
- this._peer.on("signal", async (data) => {
3079
- log12("signal", data, {
3080
- F: __dxlog_file13,
3081
- L: 142,
3082
- S: this,
3083
- C: (f, a) => f(...a)
3084
- });
3085
- await this._params.sendSignal({
3086
- payload: {
3087
- data
3088
- }
3089
- });
3090
- });
3091
- this._peer.on("connect", () => {
3092
- log12("connected", void 0, {
3093
- F: __dxlog_file13,
3094
- L: 147,
3095
- S: this,
3096
- C: (f, a) => f(...a)
3097
- });
3098
- this._params.stream.pipe(this._peer).pipe(this._params.stream);
3099
- this._piped = true;
3100
- this.connected.emit();
3101
- });
3102
- this._peer.on("close", async () => {
3103
- log12("closed", void 0, {
3104
- F: __dxlog_file13,
3105
- L: 154,
3106
- S: this,
3107
- C: (f, a) => f(...a)
3108
- });
3109
- await this.close();
3110
- });
3111
- this._peer.on("error", async (err) => {
3112
- if (typeof RTCError !== "undefined" && err instanceof RTCError) {
3113
- if (err.errorDetail === "sctp-failure") {
3114
- this.errors.raise(new ConnectionResetError2("sctp-failure from RTCError", err));
3115
- } else {
3116
- log12.info("unknown RTCError", {
3117
- err
3110
+ }
3111
+ _initChannel(channel) {
3112
+ Object.assign(channel, {
3113
+ onopen: () => {
3114
+ if (!this.isOpen) {
3115
+ log12.warn("channel opened in a closed transport", {
3116
+ topic: this._options.topic
3118
3117
  }, {
3119
3118
  F: __dxlog_file13,
3120
- L: 165,
3119
+ L: 93,
3121
3120
  S: this,
3122
3121
  C: (f, a) => f(...a)
3123
3122
  });
3124
- this.errors.raise(new UnknownProtocolError2("unknown RTCError", err));
3123
+ this._safeCloseChannel(channel);
3124
+ return;
3125
3125
  }
3126
- } else if ("code" in err) {
3127
- log12.info("simple-peer error", err, {
3126
+ log12("onopen", void 0, {
3128
3127
  F: __dxlog_file13,
3129
- L: 170,
3128
+ L: 98,
3130
3129
  S: this,
3131
3130
  C: (f, a) => f(...a)
3132
3131
  });
3133
- switch (err.code) {
3134
- case "ERR_WEBRTC_SUPPORT":
3135
- this.errors.raise(new ProtocolError2("WebRTC not supported", err));
3136
- break;
3137
- case "ERR_SIGNALING":
3138
- this.errors.raise(new ConnectivityError2("signaling failure", err));
3139
- break;
3140
- case "ERR_ICE_CONNECTION_FAILURE":
3141
- case "ERR_DATA_CHANNEL":
3142
- case "ERR_CONNECTION_FAILURE":
3143
- this.errors.raise(new ConnectivityError2("unknown communication failure", err));
3144
- break;
3145
- // errors due to library issues or improper API usage
3146
- case "ERR_CREATE_OFFER":
3147
- case "ERR_CREATE_ANSWER":
3148
- case "ERR_SET_LOCAL_DESCRIPTION":
3149
- case "ERR_SET_REMOTE_DESCRIPTION":
3150
- case "ERR_ADD_ICE_CANDIDATE":
3151
- this.errors.raise(new UnknownProtocolError2("unknown simple-peer library failure", err));
3152
- break;
3153
- default:
3154
- this.errors.raise(new Error("unknown simple-peer error"));
3155
- break;
3156
- }
3157
- } else {
3158
- log12.info("unknown peer connection error", err, {
3132
+ const duplex = new Duplex({
3133
+ read: () => {
3134
+ },
3135
+ write: (chunk, encoding, callback) => {
3136
+ return this._handleChannelWrite(chunk, callback);
3137
+ }
3138
+ });
3139
+ duplex.pipe(this._options.stream).pipe(duplex);
3140
+ this._stream = duplex;
3141
+ this.connected.emit();
3142
+ },
3143
+ onclose: async () => {
3144
+ log12("onclose", void 0, {
3159
3145
  F: __dxlog_file13,
3160
- L: 196,
3146
+ L: 111,
3161
3147
  S: this,
3162
3148
  C: (f, a) => f(...a)
3163
3149
  });
3164
- this.errors.raise(err);
3165
- }
3166
- try {
3167
- if (typeof this._peer?._pc?.getStats === "function") {
3168
- this._peer._pc.getStats().then((stats) => {
3169
- log12.info("report after webrtc error", {
3170
- config: this._params.webrtcConfig,
3171
- stats: Object.fromEntries(stats.entries())
3172
- }, {
3173
- F: __dxlog_file13,
3174
- L: 204,
3175
- S: this,
3176
- C: (f, a) => f(...a)
3177
- });
3150
+ await this.close();
3151
+ },
3152
+ onmessage: (event) => {
3153
+ if (!this._stream) {
3154
+ log12.warn("ignoring message on a closed channel", void 0, {
3155
+ F: __dxlog_file13,
3156
+ L: 117,
3157
+ S: this,
3158
+ C: (f, a) => f(...a)
3178
3159
  });
3160
+ return;
3161
+ }
3162
+ let data = event.data;
3163
+ if (data instanceof ArrayBuffer) {
3164
+ data = Buffer.from(data);
3165
+ }
3166
+ this._stream.push(data);
3167
+ },
3168
+ onerror: (event) => {
3169
+ if (this.isOpen) {
3170
+ const err = event.error instanceof Error ? event.error : new Error(`Datachannel error: ${event.type}.`);
3171
+ this.errors.raise(err);
3179
3172
  }
3180
- } catch (err2) {
3181
- log12.catch(err2, void 0, {
3173
+ },
3174
+ onbufferedamountlow: () => {
3175
+ const cb = this._streamDataFlushedCallback;
3176
+ this._streamDataFlushedCallback = null;
3177
+ cb?.();
3178
+ }
3179
+ });
3180
+ }
3181
+ async _handleChannelWrite(chunk, callback) {
3182
+ if (!this._channel) {
3183
+ log12.warn("writing to a channel after a connection was closed", void 0, {
3184
+ F: __dxlog_file13,
3185
+ L: 145,
3186
+ S: this,
3187
+ C: (f, a) => f(...a)
3188
+ });
3189
+ return;
3190
+ }
3191
+ if (chunk.length > MAX_MESSAGE_SIZE) {
3192
+ const error = new Error(`Message too large: ${chunk.length} > ${MAX_MESSAGE_SIZE}.`);
3193
+ this.errors.raise(error);
3194
+ callback();
3195
+ return;
3196
+ }
3197
+ try {
3198
+ this._channel.send(chunk);
3199
+ } catch (err) {
3200
+ this.errors.raise(err);
3201
+ callback();
3202
+ return;
3203
+ }
3204
+ if (this._channel.bufferedAmount > MAX_BUFFERED_AMOUNT) {
3205
+ if (this._streamDataFlushedCallback !== null) {
3206
+ log12.error("consumer trying to write before we are ready for more data", void 0, {
3182
3207
  F: __dxlog_file13,
3183
- L: 211,
3208
+ L: 166,
3184
3209
  S: this,
3185
3210
  C: (f, a) => f(...a)
3186
3211
  });
3187
3212
  }
3188
- await this.close();
3189
- });
3190
- log12.trace("dxos.mesh.webrtc-transport.open", trace4.end({
3191
- id: this._instanceId
3192
- }), {
3193
- F: __dxlog_file13,
3194
- L: 217,
3195
- S: this,
3196
- C: (f, a) => f(...a)
3197
- });
3198
- }
3199
- async close() {
3200
- log12("closing...", void 0, {
3201
- F: __dxlog_file13,
3202
- L: 222,
3203
- S: this,
3204
- C: (f, a) => f(...a)
3205
- });
3206
- if (this._closed) {
3207
- return;
3213
+ this._streamDataFlushedCallback = callback;
3214
+ } else {
3215
+ callback();
3208
3216
  }
3209
- this._disconnectStreams();
3210
- this._peer.destroy();
3211
- this._closed = true;
3212
- this.closed.emit();
3213
- log12("closed", void 0, {
3214
- F: __dxlog_file13,
3215
- L: 230,
3216
- S: this,
3217
- C: (f, a) => f(...a)
3218
- });
3219
3217
  }
3220
- async onSignal(signal) {
3221
- if (this._closed) {
3222
- return;
3218
+ _safeCloseChannel(channel) {
3219
+ try {
3220
+ channel.close();
3221
+ } catch (error) {
3222
+ log12.catch(error, void 0, {
3223
+ F: __dxlog_file13,
3224
+ L: 178,
3225
+ S: this,
3226
+ C: (f, a) => f(...a)
3227
+ });
3223
3228
  }
3224
- invariant11(signal.payload.data, "Signal message must contain signal data.");
3225
- invariant11(this._peer, "Peer must be initialized before receiving signals.");
3226
- this._peer.signal(signal.payload.data);
3227
3229
  }
3228
- _disconnectStreams() {
3229
- if (this._piped) {
3230
- this._params.stream.unpipe?.(this._peer)?.unpipe?.(this._params.stream);
3230
+ onSignal(signal) {
3231
+ return this._connection.onSignal(signal);
3232
+ }
3233
+ async getDetails() {
3234
+ return describeSelectedRemoteCandidate(this._connection.currentConnection);
3235
+ }
3236
+ async getStats() {
3237
+ return createRtcTransportStats(this._connection.currentConnection, this._options.topic);
3238
+ }
3239
+ };
3240
+
3241
+ // packages/core/mesh/network-manager/src/transport/webrtc/utils.ts
3242
+ var chooseInitiatorPeer = (peer1Key, peer2Key) => peer1Key < peer2Key ? peer1Key : peer2Key;
3243
+ var areSdpEqual = (sdp1, sdp2) => {
3244
+ const sdp1Lines = deduplicatedSdpLines(sdp1);
3245
+ const sdp2Lines = deduplicatedSdpLines(sdp2);
3246
+ if (sdp1Lines.length !== sdp2Lines.length) {
3247
+ return false;
3248
+ }
3249
+ return sdp1Lines.every((line, idx) => line === sdp2Lines[idx]);
3250
+ };
3251
+ var deduplicatedSdpLines = (sdp) => {
3252
+ const deduplicatedLines = [];
3253
+ const seenLines = [];
3254
+ for (const line of sdp.split("\r\n")) {
3255
+ if (line.startsWith("m")) {
3256
+ seenLines.length = 0;
3257
+ }
3258
+ if (seenLines.includes(line)) {
3259
+ continue;
3231
3260
  }
3261
+ seenLines.push(line);
3262
+ deduplicatedLines.push(line);
3232
3263
  }
3264
+ return deduplicatedLines;
3233
3265
  };
3234
- _ts_decorate6([
3235
- synchronized5
3236
- ], SimplePeerTransport.prototype, "open", null);
3237
- _ts_decorate6([
3238
- synchronized5
3239
- ], SimplePeerTransport.prototype, "close", null);
3240
- _ts_decorate6([
3241
- synchronized5
3242
- ], SimplePeerTransport.prototype, "onSignal", null);
3243
3266
 
3244
- // packages/core/mesh/network-manager/src/transport/simplepeer-transport-service.ts
3245
- import { Duplex } from "@dxos/node-std/stream";
3246
- import { Stream } from "@dxos/codec-protobuf";
3247
- import { invariant as invariant12 } from "@dxos/invariant";
3248
- import { PublicKey as PublicKey11 } from "@dxos/keys";
3249
- import { log as log13 } from "@dxos/log";
3250
- import { ConnectionState as ConnectionState3 } from "@dxos/protocols/proto/dxos/mesh/bridge";
3251
- import { ComplexMap as ComplexMap8 } from "@dxos/util";
3252
- var __dxlog_file14 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/transport/simplepeer-transport-service.ts";
3253
- var SimplePeerTransportService = class {
3254
- constructor(_webrtcConfig, _iceProvider) {
3255
- this._webrtcConfig = _webrtcConfig;
3256
- this._iceProvider = _iceProvider;
3257
- this.transports = new ComplexMap8(PublicKey11.hash);
3267
+ // packages/core/mesh/network-manager/src/transport/webrtc/rtc-peer-connection.ts
3268
+ function _ts_decorate6(decorators, target, key, desc) {
3269
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3270
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3271
+ 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;
3272
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
3273
+ }
3274
+ var __dxlog_file14 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/transport/webrtc/rtc-peer-connection.ts";
3275
+ var RtcPeerConnection = class {
3276
+ constructor(_factory, _options) {
3277
+ this._factory = _factory;
3278
+ this._options = _options;
3279
+ this._channelCreatedCallbacks = /* @__PURE__ */ new Map();
3280
+ this._transportChannels = /* @__PURE__ */ new Map();
3281
+ this._dataChannels = /* @__PURE__ */ new Map();
3282
+ this._readyForCandidates = new Trigger3();
3283
+ this._offerProcessingMutex = new Mutex2();
3284
+ this._initiator = chooseInitiatorPeer(_options.ownPeerKey, _options.remotePeerKey) === _options.ownPeerKey;
3258
3285
  }
3259
- open(request) {
3260
- const rpcStream = new Stream(({ ready, next, close }) => {
3261
- const duplex = new Duplex({
3262
- read: () => {
3263
- const callbacks = [
3264
- ...transportState.writeCallbacks
3265
- ];
3266
- transportState.writeCallbacks.length = 0;
3267
- for (const cb of callbacks) {
3268
- cb();
3269
- }
3270
- },
3271
- write: function(chunk, _, callback) {
3272
- next({
3273
- data: {
3274
- payload: chunk
3275
- }
3276
- });
3277
- callback();
3278
- }
3286
+ get transportChannelCount() {
3287
+ return this._transportChannels.size;
3288
+ }
3289
+ get currentConnection() {
3290
+ return this._connection;
3291
+ }
3292
+ async createDataChannel(topic) {
3293
+ const connection = await this._openConnection();
3294
+ if (!this._transportChannels.has(topic)) {
3295
+ if (!this._transportChannels.size) {
3296
+ this._lockAndCloseConnection();
3297
+ }
3298
+ throw new Error("Transport closed while connection was being open");
3299
+ }
3300
+ if (this._initiator) {
3301
+ const channel = connection.createDataChannel(topic);
3302
+ this._dataChannels.set(topic, channel);
3303
+ return channel;
3304
+ } else {
3305
+ const existingChannel = this._dataChannels.get(topic);
3306
+ if (existingChannel) {
3307
+ return existingChannel;
3308
+ }
3309
+ log13("waiting for initiator-peer to open a data channel", void 0, {
3310
+ F: __dxlog_file14,
3311
+ L: 90,
3312
+ S: this,
3313
+ C: (f, a) => f(...a)
3279
3314
  });
3280
- const transport = new SimplePeerTransport({
3281
- initiator: request.initiator,
3282
- stream: duplex,
3283
- webrtcConfig: this._webrtcConfig,
3284
- sendSignal: async (signal) => {
3285
- next({
3286
- signal: {
3287
- payload: signal
3288
- }
3289
- });
3290
- },
3291
- iceProvider: this._iceProvider
3315
+ return new Promise((resolve, reject) => {
3316
+ this._channelCreatedCallbacks.set(topic, {
3317
+ resolve,
3318
+ reject
3319
+ });
3292
3320
  });
3293
- void transport.open();
3294
- next({
3295
- connection: {
3296
- state: ConnectionState3.CONNECTING
3321
+ }
3322
+ }
3323
+ createTransportChannel(options) {
3324
+ const channel = new RtcTransportChannel(this, options);
3325
+ this._transportChannels.set(options.topic, channel);
3326
+ channel.closed.on(() => {
3327
+ this._transportChannels.delete(options.topic);
3328
+ if (this._transportChannels.size === 0) {
3329
+ this._lockAndCloseConnection();
3330
+ }
3331
+ });
3332
+ return channel;
3333
+ }
3334
+ async _openConnection() {
3335
+ if (this._connection) {
3336
+ return this._connection;
3337
+ }
3338
+ log13("initializing connection...", () => ({
3339
+ remotePeer: this._options.remotePeerKey
3340
+ }), {
3341
+ F: __dxlog_file14,
3342
+ L: 115,
3343
+ S: this,
3344
+ C: (f, a) => f(...a)
3345
+ });
3346
+ const config = await this._loadConnectionConfig();
3347
+ const connection = await this._factory.createConnection(config);
3348
+ const iceCandidateErrors = [];
3349
+ Object.assign(connection, {
3350
+ onnegotiationneeded: async () => {
3351
+ invariant12(this._initiator, void 0, {
3352
+ F: __dxlog_file14,
3353
+ L: 130,
3354
+ S: this,
3355
+ A: [
3356
+ "this._initiator",
3357
+ ""
3358
+ ]
3359
+ });
3360
+ if (connection !== this._connection) {
3361
+ this._onConnectionCallbackAfterClose("onnegotiationneeded", connection);
3362
+ return;
3297
3363
  }
3298
- });
3299
- transport.connected.on(() => {
3300
- next({
3301
- connection: {
3302
- state: ConnectionState3.CONNECTED
3303
- }
3364
+ log13("onnegotiationneeded", void 0, {
3365
+ F: __dxlog_file14,
3366
+ L: 137,
3367
+ S: this,
3368
+ C: (f, a) => f(...a)
3304
3369
  });
3305
- });
3306
- transport.errors.handle((err) => {
3307
- next({
3308
- connection: {
3309
- state: ConnectionState3.CLOSED,
3310
- error: err.toString()
3311
- }
3370
+ try {
3371
+ const offer = await connection.createOffer();
3372
+ await connection.setLocalDescription(offer);
3373
+ await this._sendDescription(connection, offer);
3374
+ } catch (err) {
3375
+ this._lockAndAbort(connection, err);
3376
+ }
3377
+ },
3378
+ // When ICE candidate identified (should be sent to remote peer) and when ICE gathering finalized.
3379
+ // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/icecandidate_event
3380
+ onicecandidate: async (event) => {
3381
+ if (connection !== this._connection) {
3382
+ this._onConnectionCallbackAfterClose("onicecandidate", connection);
3383
+ return;
3384
+ }
3385
+ if (event.candidate) {
3386
+ log13("onicecandidate", {
3387
+ candidate: event.candidate.candidate
3388
+ }, {
3389
+ F: __dxlog_file14,
3390
+ L: 156,
3391
+ S: this,
3392
+ C: (f, a) => f(...a)
3393
+ });
3394
+ await this._sendIceCandidate(event.candidate);
3395
+ } else {
3396
+ log13("onicecandidate gathering complete", void 0, {
3397
+ F: __dxlog_file14,
3398
+ L: 159,
3399
+ S: this,
3400
+ C: (f, a) => f(...a)
3401
+ });
3402
+ }
3403
+ },
3404
+ // When error occurs while performing ICE negotiations through a STUN or TURN server.
3405
+ // It's ok for some candidates to fail if a working pair is eventually found.
3406
+ // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/icecandidateerror_event
3407
+ onicecandidateerror: (event) => {
3408
+ const { url, errorCode, errorText } = event;
3409
+ iceCandidateErrors.push({
3410
+ url,
3411
+ errorCode,
3412
+ errorText
3312
3413
  });
3313
- close(err);
3314
- });
3315
- transport.closed.on(() => {
3316
- next({
3317
- connection: {
3318
- state: ConnectionState3.CLOSED
3414
+ },
3415
+ // When possible error during ICE gathering.
3416
+ // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/iceconnectionstatechange_event
3417
+ oniceconnectionstatechange: () => {
3418
+ if (connection !== this._connection) {
3419
+ this._onConnectionCallbackAfterClose("oniceconnectionstatechange", connection);
3420
+ return;
3421
+ }
3422
+ log13("oniceconnectionstatechange", {
3423
+ state: connection.iceConnectionState
3424
+ }, {
3425
+ F: __dxlog_file14,
3426
+ L: 179,
3427
+ S: this,
3428
+ C: (f, a) => f(...a)
3429
+ });
3430
+ if (connection.iceConnectionState === "failed") {
3431
+ this._lockAndAbort(connection, createIceFailureError(iceCandidateErrors));
3432
+ }
3433
+ },
3434
+ // When new track (or channel) is added.
3435
+ // State: { new, connecting, connected, disconnected, failed, closed }
3436
+ // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionstatechange_event
3437
+ onconnectionstatechange: () => {
3438
+ if (connection !== this._connection) {
3439
+ if (connection.connectionState !== "closed" && connection.connectionState !== "failed") {
3440
+ this._onConnectionCallbackAfterClose("onconnectionstatechange", connection);
3319
3441
  }
3442
+ return;
3443
+ }
3444
+ log13("onconnectionstatechange", {
3445
+ state: connection.connectionState
3446
+ }, {
3447
+ F: __dxlog_file14,
3448
+ L: 196,
3449
+ S: this,
3450
+ C: (f, a) => f(...a)
3320
3451
  });
3321
- close();
3322
- });
3323
- const transportState = {
3324
- transport,
3325
- stream: duplex,
3326
- writeCallbacks: [],
3327
- state: "OPEN"
3328
- };
3329
- ready();
3330
- this.transports.set(request.proxyId, transportState);
3452
+ if (connection.connectionState === "failed") {
3453
+ this._lockAndAbort(connection, new Error("Connection failed."));
3454
+ }
3455
+ },
3456
+ onsignalingstatechange: () => {
3457
+ log13("onsignalingstatechange", {
3458
+ state: connection.signalingState
3459
+ }, {
3460
+ F: __dxlog_file14,
3461
+ L: 203,
3462
+ S: this,
3463
+ C: (f, a) => f(...a)
3464
+ });
3465
+ },
3466
+ // When channel is added to connection.
3467
+ // https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/datachannel_event
3468
+ ondatachannel: (event) => {
3469
+ invariant12(!this._initiator, "Initiator is expected to create data channels.", {
3470
+ F: __dxlog_file14,
3471
+ L: 209,
3472
+ S: this,
3473
+ A: [
3474
+ "!this._initiator",
3475
+ "'Initiator is expected to create data channels.'"
3476
+ ]
3477
+ });
3478
+ if (connection !== this._connection) {
3479
+ this._onConnectionCallbackAfterClose("ondatachannel", connection);
3480
+ return;
3481
+ }
3482
+ log13("ondatachannel", {
3483
+ label: event.channel.label
3484
+ }, {
3485
+ F: __dxlog_file14,
3486
+ L: 216,
3487
+ S: this,
3488
+ C: (f, a) => f(...a)
3489
+ });
3490
+ this._dataChannels.set(event.channel.label, event.channel);
3491
+ const pendingCallback = this._channelCreatedCallbacks.get(event.channel.label);
3492
+ if (pendingCallback) {
3493
+ this._channelCreatedCallbacks.delete(event.channel.label);
3494
+ pendingCallback.resolve(event.channel);
3495
+ }
3496
+ }
3331
3497
  });
3332
- return rpcStream;
3333
- }
3334
- async sendSignal({ proxyId, signal }) {
3335
- invariant12(this.transports.has(proxyId), void 0, {
3336
- F: __dxlog_file14,
3337
- L: 124,
3338
- S: this,
3339
- A: [
3340
- "this.transports.has(proxyId)",
3341
- ""
3342
- ]
3498
+ this._connection = connection;
3499
+ this._readyForCandidates.reset();
3500
+ await this._factory.initConnection(connection, {
3501
+ initiator: this._initiator
3343
3502
  });
3344
- await this.transports.get(proxyId).transport.onSignal(signal);
3503
+ return this._connection;
3345
3504
  }
3346
- async getDetails({ proxyId }) {
3347
- invariant12(this.transports.has(proxyId), void 0, {
3505
+ _lockAndAbort(connection, error) {
3506
+ this._abortConnection(connection, error);
3507
+ }
3508
+ _abortConnection(connection, error) {
3509
+ if (connection !== this._connection) {
3510
+ log13.error("attempted to abort an inactive connection", {
3511
+ error
3512
+ }, {
3513
+ F: __dxlog_file14,
3514
+ L: 241,
3515
+ S: this,
3516
+ C: (f, a) => f(...a)
3517
+ });
3518
+ this._safeCloseConnection(connection);
3519
+ return;
3520
+ }
3521
+ for (const [topic, pendingCallback] of this._channelCreatedCallbacks.entries()) {
3522
+ pendingCallback.reject(error);
3523
+ this._transportChannels.delete(topic);
3524
+ }
3525
+ this._channelCreatedCallbacks.clear();
3526
+ for (const channel of this._transportChannels.values()) {
3527
+ channel.onConnectionError(error);
3528
+ }
3529
+ this._transportChannels.clear();
3530
+ this._safeCloseConnection();
3531
+ log13("connection aborted", {
3532
+ reason: error.message
3533
+ }, {
3348
3534
  F: __dxlog_file14,
3349
- L: 129,
3535
+ L: 255,
3350
3536
  S: this,
3351
- A: [
3352
- "this.transports.has(proxyId)",
3353
- ""
3354
- ]
3537
+ C: (f, a) => f(...a)
3355
3538
  });
3356
- return {
3357
- details: await this.transports.get(proxyId).transport.getDetails()
3358
- };
3359
3539
  }
3360
- async getStats({ proxyId }) {
3361
- invariant12(this.transports.has(proxyId), void 0, {
3540
+ _lockAndCloseConnection() {
3541
+ invariant12(this._transportChannels.size === 0, void 0, {
3362
3542
  F: __dxlog_file14,
3363
- L: 134,
3543
+ L: 260,
3364
3544
  S: this,
3365
3545
  A: [
3366
- "this.transports.has(proxyId)",
3546
+ "this._transportChannels.size === 0",
3367
3547
  ""
3368
3548
  ]
3369
3549
  });
3370
- return {
3371
- stats: await this.transports.get(proxyId).transport.getStats()
3372
- };
3550
+ if (this._connection) {
3551
+ this._safeCloseConnection();
3552
+ log13("connection closed", void 0, {
3553
+ F: __dxlog_file14,
3554
+ L: 263,
3555
+ S: this,
3556
+ C: (f, a) => f(...a)
3557
+ });
3558
+ }
3373
3559
  }
3374
- async sendData({ proxyId, payload }) {
3375
- if (this.transports.get(proxyId)?.state !== "OPEN") {
3376
- log13.debug("transport is closed", void 0, {
3560
+ async onSignal(signal) {
3561
+ const connection = this._connection;
3562
+ if (!connection) {
3563
+ log13.warn("a signal ignored because the connection was closed", {
3564
+ type: signal.payload.data.type
3565
+ }, {
3377
3566
  F: __dxlog_file14,
3378
- L: 140,
3567
+ L: 271,
3379
3568
  S: this,
3380
3569
  C: (f, a) => f(...a)
3381
3570
  });
3571
+ return;
3382
3572
  }
3383
- invariant12(this.transports.has(proxyId), void 0, {
3573
+ const data = signal.payload.data;
3574
+ switch (data.type) {
3575
+ case "offer": {
3576
+ await this._offerProcessingMutex.executeSynchronized(async () => {
3577
+ if (isRemoteDescriptionSet(connection, data)) {
3578
+ return;
3579
+ }
3580
+ if (connection.connectionState !== "new") {
3581
+ this._abortConnection(connection, new Error(`Received an offer in ${connection.connectionState}.`));
3582
+ return;
3583
+ }
3584
+ try {
3585
+ await connection.setRemoteDescription({
3586
+ type: data.type,
3587
+ sdp: data.sdp
3588
+ });
3589
+ const answer = await connection.createAnswer();
3590
+ await connection.setLocalDescription(answer);
3591
+ await this._sendDescription(connection, answer);
3592
+ this._onSessionNegotiated(connection);
3593
+ } catch (err) {
3594
+ this._abortConnection(connection, new Error("Error handling a remote offer.", {
3595
+ cause: err
3596
+ }));
3597
+ }
3598
+ });
3599
+ break;
3600
+ }
3601
+ case "answer":
3602
+ await this._offerProcessingMutex.executeSynchronized(async () => {
3603
+ try {
3604
+ if (isRemoteDescriptionSet(connection, data)) {
3605
+ return;
3606
+ }
3607
+ if (connection.signalingState !== "have-local-offer") {
3608
+ this._abortConnection(connection, new Error(`Unexpected answer from remote peer, signalingState was ${connection.signalingState}.`));
3609
+ return;
3610
+ }
3611
+ await connection.setRemoteDescription({
3612
+ type: data.type,
3613
+ sdp: data.sdp
3614
+ });
3615
+ this._onSessionNegotiated(connection);
3616
+ } catch (err) {
3617
+ this._abortConnection(connection, new Error("Error handling a remote answer.", {
3618
+ cause: err
3619
+ }));
3620
+ }
3621
+ });
3622
+ break;
3623
+ case "candidate":
3624
+ void this._processIceCandidate(connection, data.candidate);
3625
+ break;
3626
+ default:
3627
+ this._abortConnection(connection, new Error(`Unknown signal type ${data.type}.`));
3628
+ break;
3629
+ }
3630
+ log13("signal processed", void 0, {
3384
3631
  F: __dxlog_file14,
3385
- L: 142,
3632
+ L: 330,
3386
3633
  S: this,
3387
- A: [
3388
- "this.transports.has(proxyId)",
3389
- ""
3390
- ]
3634
+ C: (f, a) => f(...a)
3391
3635
  });
3392
- const state = this.transports.get(proxyId);
3393
- const bufferHasSpace = state.stream.push(payload);
3394
- if (!bufferHasSpace) {
3395
- await new Promise((resolve) => {
3396
- state.writeCallbacks.push(resolve);
3636
+ }
3637
+ async _processIceCandidate(connection, candidate) {
3638
+ try {
3639
+ await this._readyForCandidates.wait();
3640
+ if (connection === this._connection) {
3641
+ log13("adding ice candidate", {
3642
+ candidate
3643
+ }, {
3644
+ F: __dxlog_file14,
3645
+ L: 338,
3646
+ S: this,
3647
+ C: (f, a) => f(...a)
3648
+ });
3649
+ await connection.addIceCandidate(candidate);
3650
+ }
3651
+ } catch (err) {
3652
+ log13.catch(err, void 0, {
3653
+ F: __dxlog_file14,
3654
+ L: 342,
3655
+ S: this,
3656
+ C: (f, a) => f(...a)
3397
3657
  });
3398
3658
  }
3399
3659
  }
3400
- async close({ proxyId }) {
3401
- await this.transports.get(proxyId)?.transport.close();
3402
- await this.transports.get(proxyId)?.stream.end();
3403
- if (this.transports.get(proxyId)) {
3404
- this.transports.get(proxyId).state = "CLOSED";
3660
+ _onSessionNegotiated(connection) {
3661
+ if (connection === this._connection) {
3662
+ log13("ready to process ice candidates", void 0, {
3663
+ F: __dxlog_file14,
3664
+ L: 348,
3665
+ S: this,
3666
+ C: (f, a) => f(...a)
3667
+ });
3668
+ this._readyForCandidates.wake();
3669
+ } else {
3670
+ log13.warn("session was negotiated after connection became inactive", void 0, {
3671
+ F: __dxlog_file14,
3672
+ L: 351,
3673
+ S: this,
3674
+ C: (f, a) => f(...a)
3675
+ });
3405
3676
  }
3406
- log13("Closed.", void 0, {
3677
+ }
3678
+ _onConnectionCallbackAfterClose(callback, connection) {
3679
+ log13.warn("callback invoked after a connection was destroyed, this is probably a bug", {
3680
+ callback,
3681
+ state: connection.connectionState
3682
+ }, {
3407
3683
  F: __dxlog_file14,
3408
- L: 158,
3684
+ L: 356,
3409
3685
  S: this,
3410
3686
  C: (f, a) => f(...a)
3411
3687
  });
3688
+ this._safeCloseConnection(connection);
3689
+ }
3690
+ _safeCloseConnection(connection = this._connection) {
3691
+ const resetFields = this._connection && connection === this._connection;
3692
+ try {
3693
+ connection?.close();
3694
+ } catch (err) {
3695
+ log13.catch(err, void 0, {
3696
+ F: __dxlog_file14,
3697
+ L: 368,
3698
+ S: this,
3699
+ C: (f, a) => f(...a)
3700
+ });
3701
+ }
3702
+ if (resetFields) {
3703
+ this._connection = void 0;
3704
+ this._dataChannels.clear();
3705
+ this._readyForCandidates.wake();
3706
+ void this._factory.onConnectionDestroyed().catch((err) => log13.catch(err, void 0, {
3707
+ F: __dxlog_file14,
3708
+ L: 374,
3709
+ S: this,
3710
+ C: (f, a) => f(...a)
3711
+ }));
3712
+ for (const [_, pendingCallback] of this._channelCreatedCallbacks.entries()) {
3713
+ pendingCallback.reject("Connection closed.");
3714
+ }
3715
+ this._channelCreatedCallbacks.clear();
3716
+ }
3717
+ }
3718
+ async _loadConnectionConfig() {
3719
+ const config = {
3720
+ ...this._options.webrtcConfig
3721
+ };
3722
+ try {
3723
+ const providedIceServers = await this._options.iceProvider?.getIceServers() ?? [];
3724
+ if (providedIceServers.length > 0) {
3725
+ config.iceServers = [
3726
+ ...config.iceServers ?? [],
3727
+ ...providedIceServers
3728
+ ];
3729
+ }
3730
+ } catch (error) {
3731
+ log13.catch(error, void 0, {
3732
+ F: __dxlog_file14,
3733
+ L: 390,
3734
+ S: this,
3735
+ C: (f, a) => f(...a)
3736
+ });
3737
+ }
3738
+ return config;
3739
+ }
3740
+ async _sendIceCandidate(candidate) {
3741
+ try {
3742
+ await this._options.sendSignal({
3743
+ payload: {
3744
+ data: {
3745
+ type: "candidate",
3746
+ candidate: {
3747
+ candidate: candidate.candidate,
3748
+ // These fields never seem to be not null, but connecting to Chrome doesn't work if they are.
3749
+ sdpMLineIndex: candidate.sdpMLineIndex ?? "0",
3750
+ sdpMid: candidate.sdpMid ?? "0"
3751
+ }
3752
+ }
3753
+ }
3754
+ });
3755
+ } catch (err) {
3756
+ log13.warn("signaling error", {
3757
+ err
3758
+ }, {
3759
+ F: __dxlog_file14,
3760
+ L: 411,
3761
+ S: this,
3762
+ C: (f, a) => f(...a)
3763
+ });
3764
+ }
3765
+ }
3766
+ async _sendDescription(connection, description) {
3767
+ if (connection !== this._connection) {
3768
+ return;
3769
+ }
3770
+ const data = {
3771
+ type: description.type,
3772
+ sdp: description.sdp
3773
+ };
3774
+ await this._options.sendSignal({
3775
+ payload: {
3776
+ data
3777
+ }
3778
+ });
3779
+ }
3780
+ get _connectionInfo() {
3781
+ const connectionInfo = this._connection && {
3782
+ connectionState: this._connection.connectionState,
3783
+ iceConnectionState: this._connection.iceConnectionState,
3784
+ iceGatheringState: this._connection.iceGatheringState,
3785
+ signalingState: this._connection.signalingState,
3786
+ remoteDescription: this._connection.remoteDescription,
3787
+ localDescription: this._connection.localDescription
3788
+ };
3789
+ return {
3790
+ ...connectionInfo,
3791
+ ts: Date.now(),
3792
+ remotePeerKey: this._options.remotePeerKey,
3793
+ channels: [
3794
+ ...this._transportChannels.keys()
3795
+ ].map((topic) => topic),
3796
+ config: this._connection?.getConfiguration()
3797
+ };
3798
+ }
3799
+ get _loggerContext() {
3800
+ return {
3801
+ ownPeerKey: this._options.ownPeerKey,
3802
+ remotePeerKey: this._options.remotePeerKey,
3803
+ initiator: this._initiator,
3804
+ channels: this._transportChannels.size
3805
+ };
3412
3806
  }
3413
3807
  };
3808
+ _ts_decorate6([
3809
+ synchronized5
3810
+ ], RtcPeerConnection.prototype, "_openConnection", null);
3811
+ _ts_decorate6([
3812
+ synchronized5
3813
+ ], RtcPeerConnection.prototype, "_lockAndAbort", null);
3814
+ _ts_decorate6([
3815
+ synchronized5
3816
+ ], RtcPeerConnection.prototype, "_lockAndCloseConnection", null);
3817
+ _ts_decorate6([
3818
+ synchronized5
3819
+ ], RtcPeerConnection.prototype, "onSignal", null);
3820
+ _ts_decorate6([
3821
+ trace4.info()
3822
+ ], RtcPeerConnection.prototype, "_connectionInfo", null);
3823
+ _ts_decorate6([
3824
+ logInfo4
3825
+ ], RtcPeerConnection.prototype, "_loggerContext", null);
3826
+ RtcPeerConnection = _ts_decorate6([
3827
+ trace4.resource()
3828
+ ], RtcPeerConnection);
3829
+ var isRemoteDescriptionSet = (connection, data) => {
3830
+ if (!connection.remoteDescription?.type || connection.remoteDescription?.type !== data.type) {
3831
+ return false;
3832
+ }
3833
+ return areSdpEqual(connection.remoteDescription.sdp, data.sdp);
3834
+ };
3835
+ var createIceFailureError = (details) => {
3836
+ const candidateErrors = details.map(({ url, errorCode, errorText }) => `${errorCode} ${url}: ${errorText}`);
3837
+ return new ConnectivityError3(`ICE failed:
3838
+ ${candidateErrors.join("\n")}`);
3839
+ };
3414
3840
 
3415
- // packages/core/mesh/network-manager/src/transport/simplepeer-transport-proxy.ts
3841
+ // packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-factory.ts
3842
+ var createRtcTransportFactory = (webrtcConfig, iceProvider) => {
3843
+ const connectionFactory = getRtcConnectionFactory();
3844
+ return {
3845
+ createTransport: (options) => {
3846
+ const connection = new RtcPeerConnection(connectionFactory, {
3847
+ ownPeerKey: options.ownPeerKey,
3848
+ remotePeerKey: options.remotePeerKey,
3849
+ sendSignal: options.sendSignal,
3850
+ webrtcConfig,
3851
+ iceProvider
3852
+ });
3853
+ return connection.createTransportChannel(options);
3854
+ }
3855
+ };
3856
+ };
3857
+
3858
+ // packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-proxy.ts
3416
3859
  import { Writable } from "@dxos/node-std/stream";
3417
- import { Event as Event9, scheduleTask as scheduleTask4 } from "@dxos/async";
3418
- import { Context as Context6 } from "@dxos/context";
3860
+ import { Event as Event8, scheduleTask as scheduleTask4 } from "@dxos/async";
3861
+ import { Resource as Resource2 } from "@dxos/context";
3419
3862
  import { ErrorStream as ErrorStream5 } from "@dxos/debug";
3420
3863
  import { invariant as invariant13 } from "@dxos/invariant";
3421
- import { PublicKey as PublicKey12 } from "@dxos/keys";
3864
+ import { PublicKey as PublicKey10 } from "@dxos/keys";
3422
3865
  import { log as log14 } from "@dxos/log";
3423
- import { ConnectionResetError as ConnectionResetError3, TimeoutError as TimeoutError3, ProtocolError as ProtocolError3, ConnectivityError as ConnectivityError3, UnknownProtocolError as UnknownProtocolError3 } from "@dxos/protocols";
3424
- import { ConnectionState as ConnectionState4 } from "@dxos/protocols/proto/dxos/mesh/bridge";
3866
+ import { ConnectionResetError as ConnectionResetError2, ConnectivityError as ConnectivityError4, TimeoutError as TimeoutError3 } from "@dxos/protocols";
3867
+ import { ConnectionState as ConnectionState3 } from "@dxos/protocols/proto/dxos/mesh/bridge";
3425
3868
  import { arrayToBuffer } from "@dxos/util";
3426
- var __dxlog_file15 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/transport/simplepeer-transport-proxy.ts";
3869
+ var __dxlog_file15 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-proxy.ts";
3427
3870
  var RPC_TIMEOUT = 1e4;
3871
+ var CLOSE_RPC_TIMEOUT = 3e3;
3428
3872
  var RESP_MIN_THRESHOLD = 500;
3429
- var TIMEOUT_THRESHOLD = 10;
3430
- var SimplePeerTransportProxy = class {
3873
+ var RtcTransportProxy = class extends Resource2 {
3431
3874
  constructor(_options) {
3875
+ super();
3432
3876
  this._options = _options;
3433
- this._proxyId = PublicKey12.random();
3434
- this._ctx = new Context6(void 0, {
3435
- F: __dxlog_file15,
3436
- L: 37
3437
- });
3438
- this._timeoutCount = 0;
3439
- this.closed = new Event9();
3440
- this.connected = new Event9();
3877
+ this._proxyId = PublicKey10.random();
3878
+ this.closed = new Event8();
3879
+ this.connected = new Event8();
3441
3880
  this.errors = new ErrorStream5();
3442
- this._closed = false;
3443
- }
3444
- get isOpen() {
3445
- return !this._closed;
3446
3881
  }
3447
- async open() {
3448
- this._serviceStream = this._options.bridgeService.open({
3449
- proxyId: this._proxyId,
3450
- initiator: this._options.initiator
3451
- }, {
3452
- timeout: RPC_TIMEOUT
3453
- });
3454
- this._serviceStream.waitUntilReady().then(() => {
3455
- this._serviceStream.subscribe(async (event) => {
3456
- log14("SimplePeerTransportProxy: event", event, {
3882
+ async _open() {
3883
+ let stream;
3884
+ try {
3885
+ stream = this._options.bridgeService.open({
3886
+ proxyId: this._proxyId,
3887
+ remotePeerKey: this._options.remotePeerKey,
3888
+ ownPeerKey: this._options.ownPeerKey,
3889
+ topic: this._options.topic,
3890
+ initiator: this._options.initiator ?? false
3891
+ }, {
3892
+ timeout: RPC_TIMEOUT
3893
+ });
3894
+ } catch (error) {
3895
+ this.errors.raise(error);
3896
+ return;
3897
+ }
3898
+ this._serviceStream = stream;
3899
+ stream.waitUntilReady().then(() => {
3900
+ stream.subscribe(async (event) => {
3901
+ log14("rtc transport proxy event", event, {
3457
3902
  F: __dxlog_file15,
3458
3903
  L: 66,
3459
3904
  S: this,
@@ -3466,20 +3911,34 @@ var SimplePeerTransportProxy = class {
3466
3911
  } else if (event.signal) {
3467
3912
  await this._handleSignal(event.signal);
3468
3913
  }
3914
+ }, (err) => {
3915
+ log14("rtc bridge stream closed", {
3916
+ err
3917
+ }, {
3918
+ F: __dxlog_file15,
3919
+ L: 76,
3920
+ S: this,
3921
+ C: (f, a) => f(...a)
3922
+ });
3923
+ if (err) {
3924
+ this._raiseIfOpen(err);
3925
+ } else {
3926
+ void this.close();
3927
+ }
3469
3928
  });
3470
- const proxyStream = new Writable({
3929
+ const connectorStream = new Writable({
3471
3930
  write: (chunk, _, callback) => {
3472
- const then = performance.now();
3931
+ const sendStartMs = Date.now();
3473
3932
  this._options.bridgeService.sendData({
3474
3933
  proxyId: this._proxyId,
3475
3934
  payload: chunk
3476
3935
  }, {
3477
3936
  timeout: RPC_TIMEOUT
3478
3937
  }).then(() => {
3479
- if (performance.now() - then > RESP_MIN_THRESHOLD) {
3938
+ if (Date.now() - sendStartMs > RESP_MIN_THRESHOLD) {
3480
3939
  log14("slow response, delaying callback", void 0, {
3481
3940
  F: __dxlog_file15,
3482
- L: 90,
3941
+ L: 93,
3483
3942
  S: this,
3484
3943
  C: (f, a) => f(...a)
3485
3944
  });
@@ -3487,60 +3946,41 @@ var SimplePeerTransportProxy = class {
3487
3946
  } else {
3488
3947
  callback();
3489
3948
  }
3490
- this._timeoutCount = 0;
3491
3949
  }, (err) => {
3492
- if (err instanceof TimeoutError3 || err.constructor.name === "TimeoutError") {
3493
- if (this._timeoutCount++ > TIMEOUT_THRESHOLD) {
3494
- throw new TimeoutError3(`too many timeouts (${this._timeoutCount} > ${TIMEOUT_THRESHOLD}`);
3495
- } else {
3496
- log14("timeout error, but still invoking callback", void 0, {
3497
- F: __dxlog_file15,
3498
- L: 102,
3499
- S: this,
3500
- C: (f, a) => f(...a)
3501
- });
3502
- callback();
3503
- }
3504
- } else {
3505
- log14.catch(err, void 0, {
3506
- F: __dxlog_file15,
3507
- L: 106,
3508
- S: this,
3509
- C: (f, a) => f(...a)
3510
- });
3511
- }
3950
+ callback();
3951
+ this._raiseIfOpen(err);
3512
3952
  });
3513
3953
  }
3514
3954
  });
3515
- proxyStream.on("error", (err) => {
3516
- log14("proxystream error", {
3517
- err
3518
- }, {
3519
- F: __dxlog_file15,
3520
- L: 114,
3521
- S: this,
3522
- C: (f, a) => f(...a)
3523
- });
3955
+ connectorStream.on("error", (err) => {
3956
+ this._raiseIfOpen(err);
3524
3957
  });
3525
- this._options.stream.pipe(proxyStream);
3526
- }, (error) => log14.catch(error, void 0, {
3527
- F: __dxlog_file15,
3528
- L: 119,
3529
- S: this,
3530
- C: (f, a) => f(...a)
3531
- }));
3958
+ this._options.stream.pipe(connectorStream);
3959
+ }, (error) => {
3960
+ if (error) {
3961
+ this._raiseIfOpen(error);
3962
+ } else {
3963
+ void this.close();
3964
+ }
3965
+ });
3532
3966
  }
3533
- async close() {
3534
- await this._ctx.dispose();
3535
- if (this._closed) {
3536
- return;
3967
+ async _close() {
3968
+ try {
3969
+ await this._serviceStream?.close();
3970
+ this._serviceStream = void 0;
3971
+ } catch (err) {
3972
+ log14.catch(err, void 0, {
3973
+ F: __dxlog_file15,
3974
+ L: 128,
3975
+ S: this,
3976
+ C: (f, a) => f(...a)
3977
+ });
3537
3978
  }
3538
- await this._serviceStream.close();
3539
3979
  try {
3540
3980
  await this._options.bridgeService.close({
3541
3981
  proxyId: this._proxyId
3542
3982
  }, {
3543
- timeout: RPC_TIMEOUT
3983
+ timeout: CLOSE_RPC_TIMEOUT
3544
3984
  });
3545
3985
  } catch (err) {
3546
3986
  log14.catch(err, void 0, {
@@ -3551,7 +3991,6 @@ var SimplePeerTransportProxy = class {
3551
3991
  });
3552
3992
  }
3553
3993
  this.closed.emit();
3554
- this._closed = true;
3555
3994
  }
3556
3995
  async onSignal(signal) {
3557
3996
  this._options.bridgeService.sendSignal({
@@ -3559,581 +3998,347 @@ var SimplePeerTransportProxy = class {
3559
3998
  signal
3560
3999
  }, {
3561
4000
  timeout: RPC_TIMEOUT
3562
- }).catch((err) => this.errors.raise(decodeError(err)));
4001
+ }).catch((err) => this._raiseIfOpen(decodeError(err)));
3563
4002
  }
3564
4003
  async _handleConnection(connectionEvent) {
3565
4004
  if (connectionEvent.error) {
3566
4005
  this.errors.raise(decodeError(connectionEvent.error));
4006
+ return;
3567
4007
  }
3568
4008
  switch (connectionEvent.state) {
3569
- case ConnectionState4.CONNECTED: {
4009
+ case ConnectionState3.CONNECTED: {
3570
4010
  this.connected.emit();
3571
4011
  break;
3572
4012
  }
3573
- case ConnectionState4.CLOSED: {
4013
+ case ConnectionState3.CLOSED: {
3574
4014
  await this.close();
3575
4015
  break;
3576
4016
  }
3577
4017
  }
3578
4018
  }
3579
4019
  _handleData(dataEvent) {
3580
- this._options.stream.write(arrayToBuffer(dataEvent.payload));
4020
+ try {
4021
+ this._options.stream.write(arrayToBuffer(dataEvent.payload));
4022
+ } catch (error) {
4023
+ this._raiseIfOpen(error);
4024
+ }
3581
4025
  }
3582
4026
  async _handleSignal(signalEvent) {
3583
- await this._options.sendSignal(signalEvent.payload);
3584
- }
3585
- async getDetails() {
3586
- return (await this._options.bridgeService.getDetails({
3587
- proxyId: this._proxyId
3588
- }, {
3589
- timeout: RPC_TIMEOUT
3590
- })).details;
3591
- }
3592
- async getStats() {
3593
- return (await this._options.bridgeService.getStats({
3594
- proxyId: this._proxyId
3595
- }, {
3596
- timeout: RPC_TIMEOUT
3597
- })).stats;
3598
- }
3599
- /**
3600
- * Called when underlying proxy service becomes unavailable.
3601
- */
3602
- // TODO(burdon): Option on close method.
3603
- forceClose() {
3604
- void this._serviceStream.close();
3605
- this.closed.emit();
3606
- this._closed = true;
3607
- }
3608
- };
3609
- var SimplePeerTransportProxyFactory = class {
3610
- constructor() {
3611
- this._connections = /* @__PURE__ */ new Set();
3612
- }
3613
- /**
3614
- * Sets the current BridgeService to be used to open connections.
3615
- * Calling this method will close any existing connections.
3616
- */
3617
- setBridgeService(bridgeService) {
3618
- this._bridgeService = bridgeService;
3619
- for (const connection of this._connections) {
3620
- connection.forceClose();
4027
+ try {
4028
+ await this._options.sendSignal(signalEvent.payload);
4029
+ } catch (error) {
4030
+ const type = signalEvent.payload.payload.data?.type;
4031
+ if (type === "offer" || type === "answer") {
4032
+ this._raiseIfOpen(new ConnectivityError4(`Session establishment failed: ${type} couldn't be sent.`));
4033
+ }
3621
4034
  }
3622
- return this;
3623
4035
  }
3624
- createTransport(options) {
3625
- invariant13(this._bridgeService, "SimplePeerTransportProxyFactory is not ready to open connections", {
3626
- F: __dxlog_file15,
3627
- L: 218,
3628
- S: this,
3629
- A: [
3630
- "this._bridgeService",
3631
- "'SimplePeerTransportProxyFactory is not ready to open connections'"
3632
- ]
3633
- });
3634
- const transport = new SimplePeerTransportProxy({
3635
- ...options,
3636
- bridgeService: this._bridgeService
3637
- });
3638
- this._connections.add(transport);
3639
- transport.closed.on(() => this._connections.delete(transport));
3640
- return transport;
3641
- }
3642
- };
3643
- var decodeError = (err) => {
3644
- const message = typeof err === "string" ? err : err.message;
3645
- if (message.includes("CONNECTION_RESET")) {
3646
- return new ConnectionResetError3(message);
3647
- } else if (message.includes("TIMEOUT")) {
3648
- return new TimeoutError3(message);
3649
- } else if (message.includes("PROTOCOL_ERROR")) {
3650
- return new ProtocolError3(message);
3651
- } else if (message.includes("CONNECTIVITY_ERROR")) {
3652
- return new ConnectivityError3(message);
3653
- } else if (message.includes("UNKNOWN_PROTOCOL_ERROR")) {
3654
- return new UnknownProtocolError3(message);
3655
- } else {
3656
- return typeof err === "string" ? new Error(err) : err;
3657
- }
3658
- };
3659
-
3660
- // packages/core/mesh/network-manager/src/transport/libdatachannel-transport.ts
3661
- import { Duplex as Duplex2 } from "stream";
3662
- import { Event as Event10, Trigger as Trigger3, synchronized as synchronized6 } from "@dxos/async";
3663
- import { ErrorStream as ErrorStream6 } from "@dxos/debug";
3664
- import { invariant as invariant14 } from "@dxos/invariant";
3665
- import { log as log15 } from "@dxos/log";
3666
- function _ts_decorate7(decorators, target, key, desc) {
3667
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3668
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
3669
- 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;
3670
- return c > 3 && r && Object.defineProperty(target, key, r), r;
3671
- }
3672
- var __dxlog_file16 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/transport/libdatachannel-transport.ts";
3673
- var DATACHANNEL_LABEL = "dxos.mesh.transport";
3674
- var MAX_BUFFERED_AMOUNT = 64 * 1024;
3675
- var MAX_MESSAGE_SIZE = 64 * 1024;
3676
- var createLibDataChannelTransportFactory = (webrtcConfig, iceProvider) => {
3677
- return {
3678
- createTransport: (options) => new LibDataChannelTransport({
3679
- ...options,
3680
- webrtcConfig,
3681
- iceProvider
3682
- })
3683
- };
3684
- };
3685
- var LibDataChannelTransport = class _LibDataChannelTransport {
3686
- static {
3687
- this._instanceCount = 0;
3688
- }
3689
- constructor(_options) {
3690
- this._options = _options;
3691
- this._closed = false;
3692
- this._connected = false;
3693
- this._writeCallback = null;
3694
- this._readyForCandidates = new Trigger3();
3695
- this.closed = new Event10();
3696
- this.connected = new Event10();
3697
- this.errors = new ErrorStream6();
3698
- }
3699
- get isOpen() {
3700
- return !!this._peer && !this._closed;
3701
- }
3702
- async open() {
3703
- if (this._closed) {
3704
- this.errors.raise(new Error("connection already closed"));
3705
- }
3706
- const { RTCPeerConnection } = (await importESM("node-datachannel/polyfill")).default;
3707
- const providedIceServers = await this._options.iceProvider?.getIceServers();
3708
- if (!this._options.webrtcConfig) {
3709
- this._options.webrtcConfig = {};
3710
- }
3711
- this._options.webrtcConfig.iceServers = [
3712
- ...this._options.webrtcConfig.iceServers ?? [],
3713
- ...providedIceServers ?? []
3714
- ];
3715
- this._peer = new RTCPeerConnection(this._options.webrtcConfig);
3716
- this._peer.onicecandidateerror = (event) => {
3717
- log15.error("peer.onicecandidateerror", {
3718
- event
3719
- }, {
3720
- F: __dxlog_file16,
3721
- L: 93,
3722
- S: this,
3723
- C: (f, a) => f(...a)
3724
- });
3725
- };
3726
- this._peer.onconnectionstatechange = (event) => {
3727
- log15.debug("peer.onconnectionstatechange", {
3728
- event,
3729
- peerConnectionState: this._peer?.connectionState,
3730
- transportConnectionState: this._connected
4036
+ async getDetails() {
4037
+ try {
4038
+ const response = await this._options.bridgeService.getDetails({
4039
+ proxyId: this._proxyId
3731
4040
  }, {
3732
- F: __dxlog_file16,
3733
- L: 97,
3734
- S: this,
3735
- C: (f, a) => f(...a)
4041
+ timeout: RPC_TIMEOUT
3736
4042
  });
3737
- };
3738
- this._peer.onicecandidate = async (event) => {
3739
- log15.debug("peer.onicecandidate", {
3740
- event
4043
+ return response.details;
4044
+ } catch (err) {
4045
+ return "bridge-svc unreachable";
4046
+ }
4047
+ }
4048
+ async getStats() {
4049
+ try {
4050
+ const response = await this._options.bridgeService.getStats({
4051
+ proxyId: this._proxyId
3741
4052
  }, {
3742
- F: __dxlog_file16,
3743
- L: 107,
3744
- S: this,
3745
- C: (f, a) => f(...a)
3746
- });
3747
- if (event.candidate) {
3748
- try {
3749
- await this._options.sendSignal({
3750
- payload: {
3751
- data: {
3752
- type: "candidate",
3753
- candidate: {
3754
- candidate: event.candidate.candidate,
3755
- // These fields never seem to be not null, but connecting to Chrome doesn't work if they are.
3756
- sdpMLineIndex: event.candidate.sdpMLineIndex ?? 0,
3757
- sdpMid: event.candidate.sdpMid ?? 0
3758
- }
3759
- }
3760
- }
3761
- });
3762
- } catch (err) {
3763
- log15.info("signaling error", {
3764
- err
3765
- }, {
3766
- F: __dxlog_file16,
3767
- L: 124,
3768
- S: this,
3769
- C: (f, a) => f(...a)
3770
- });
3771
- }
3772
- }
3773
- };
3774
- if (this._options.initiator) {
3775
- invariant14(this._peer, "not open", {
3776
- F: __dxlog_file16,
3777
- L: 130,
3778
- S: this,
3779
- A: [
3780
- "this._peer",
3781
- "'not open'"
3782
- ]
3783
- });
3784
- this._peer.createOffer().then(async (offer) => {
3785
- if (this._closed) {
3786
- return;
3787
- }
3788
- if (this._peer?.connectionState !== "connecting") {
3789
- log15.error("peer not connecting", {
3790
- peer: this._peer
3791
- }, {
3792
- F: __dxlog_file16,
3793
- L: 141,
3794
- S: this,
3795
- C: (f, a) => f(...a)
3796
- });
3797
- this.errors.raise(new Error("invalid state: peer is initiator, but other peer not in state connecting"));
3798
- }
3799
- log15.debug("creating offer", {
3800
- peer: this._peer,
3801
- offer
3802
- }, {
3803
- F: __dxlog_file16,
3804
- L: 145,
3805
- S: this,
3806
- C: (f, a) => f(...a)
3807
- });
3808
- await this._peer.setLocalDescription(offer);
3809
- await this._options.sendSignal({
3810
- payload: {
3811
- data: {
3812
- type: offer.type,
3813
- sdp: offer.sdp
3814
- }
3815
- }
3816
- });
3817
- }).catch((err) => {
3818
- this.errors.raise(err);
3819
- });
3820
- this._handleChannel(this._peer.createDataChannel(DATACHANNEL_LABEL));
3821
- log15.debug("created data channel", void 0, {
3822
- F: __dxlog_file16,
3823
- L: 155,
3824
- S: this,
3825
- C: (f, a) => f(...a)
4053
+ timeout: RPC_TIMEOUT
3826
4054
  });
3827
- this._peer.ondatachannel = () => {
3828
- this.errors.raise(new Error("unexpected ondatachannel event for initiator"));
3829
- };
3830
- } else {
3831
- this._peer.ondatachannel = (event) => {
3832
- log15.debug("peer.ondatachannel (non-initiator)", {
3833
- event
3834
- }, {
3835
- F: __dxlog_file16,
3836
- L: 161,
3837
- S: this,
3838
- C: (f, a) => f(...a)
3839
- });
3840
- if (event.channel.label !== DATACHANNEL_LABEL) {
3841
- this.errors.raise(new Error(`unexpected channel label ${event.channel.label}`));
3842
- }
3843
- this._handleChannel(event.channel);
4055
+ return response.stats;
4056
+ } catch (err) {
4057
+ return {
4058
+ bytesSent: 0,
4059
+ bytesReceived: 0,
4060
+ packetsSent: 0,
4061
+ packetsReceived: 0,
4062
+ rawStats: "bridge-svc unreachable"
3844
4063
  };
3845
4064
  }
3846
- _LibDataChannelTransport._instanceCount++;
3847
4065
  }
3848
- async close() {
3849
- await this._close();
3850
- if (--_LibDataChannelTransport._instanceCount === 0) {
3851
- (await importESM("node-datachannel")).cleanup();
4066
+ _raiseIfOpen(error) {
4067
+ if (this.isOpen) {
4068
+ this.errors.raise(error);
4069
+ } else {
4070
+ log14.info("error swallowed because transport was closed", {
4071
+ message: error.message
4072
+ }, {
4073
+ F: __dxlog_file15,
4074
+ L: 215,
4075
+ S: this,
4076
+ C: (f, a) => f(...a)
4077
+ });
3852
4078
  }
3853
4079
  }
3854
- async _close() {
3855
- if (this._closed) {
3856
- return;
3857
- }
3858
- await this._disconnectStreams();
3859
- try {
3860
- this._peer?.close();
3861
- } catch (err) {
3862
- this.errors.raise(err);
3863
- }
3864
- this._peer = void 0;
3865
- this._closed = true;
4080
+ /**
4081
+ * Called when underlying proxy service becomes unavailable.
4082
+ */
4083
+ forceClose() {
4084
+ void this._serviceStream?.close();
3866
4085
  this.closed.emit();
3867
4086
  }
4087
+ };
4088
+ var RtcTransportProxyFactory = class {
4089
+ constructor() {
4090
+ this._connections = /* @__PURE__ */ new Set();
4091
+ }
3868
4092
  /**
3869
- * Handle data channel events.
4093
+ * Sets the current BridgeService to be used to open connections.
4094
+ * Calling this method will close any existing connections.
3870
4095
  */
3871
- _handleChannel(dataChannel) {
3872
- this._channel = dataChannel;
3873
- this._channel.onopen = () => {
3874
- log15.debug("channel.onopen", void 0, {
4096
+ setBridgeService(bridgeService) {
4097
+ this._bridgeService = bridgeService;
4098
+ for (const connection of this._connections) {
4099
+ connection.forceClose();
4100
+ }
4101
+ return this;
4102
+ }
4103
+ createTransport(options) {
4104
+ invariant13(this._bridgeService, "RtcTransportProxyFactory is not ready to open connections", {
4105
+ F: __dxlog_file15,
4106
+ L: 245,
4107
+ S: this,
4108
+ A: [
4109
+ "this._bridgeService",
4110
+ "'RtcTransportProxyFactory is not ready to open connections'"
4111
+ ]
4112
+ });
4113
+ const transport = new RtcTransportProxy({
4114
+ ...options,
4115
+ bridgeService: this._bridgeService
4116
+ });
4117
+ this._connections.add(transport);
4118
+ transport.closed.on(() => this._connections.delete(transport));
4119
+ return transport;
4120
+ }
4121
+ };
4122
+ var decodeError = (err) => {
4123
+ const message = typeof err === "string" ? err : err.message;
4124
+ if (message.includes("CONNECTION_RESET")) {
4125
+ return new ConnectionResetError2(message);
4126
+ } else if (message.includes("TIMEOUT")) {
4127
+ return new TimeoutError3(message);
4128
+ } else if (message.includes("CONNECTIVITY_ERROR")) {
4129
+ return new ConnectivityError4(message);
4130
+ } else {
4131
+ return typeof err === "string" ? new Error(err) : err;
4132
+ }
4133
+ };
4134
+
4135
+ // packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-service.ts
4136
+ import { Duplex as Duplex2 } from "@dxos/node-std/stream";
4137
+ import { Stream } from "@dxos/codec-protobuf";
4138
+ import { invariant as invariant14 } from "@dxos/invariant";
4139
+ import { PublicKey as PublicKey11 } from "@dxos/keys";
4140
+ import { log as log15 } from "@dxos/log";
4141
+ import { ConnectionState as ConnectionState4 } from "@dxos/protocols/proto/dxos/mesh/bridge";
4142
+ import { ComplexMap as ComplexMap8 } from "@dxos/util";
4143
+ var __dxlog_file16 = "/home/runner/work/dxos/dxos/packages/core/mesh/network-manager/src/transport/webrtc/rtc-transport-service.ts";
4144
+ var RtcTransportService = class {
4145
+ constructor(webrtcConfig, iceProvider, _transportFactory = createRtcTransportFactory(webrtcConfig, iceProvider)) {
4146
+ this._transportFactory = _transportFactory;
4147
+ this._openTransports = new ComplexMap8(PublicKey11.hash);
4148
+ }
4149
+ hasOpenTransports() {
4150
+ return this._openTransports.size > 0;
4151
+ }
4152
+ open(request) {
4153
+ const existingTransport = this._openTransports.get(request.proxyId);
4154
+ if (existingTransport) {
4155
+ log15.error("requesting a new transport bridge for an existing proxy", void 0, {
3875
4156
  F: __dxlog_file16,
3876
- L: 206,
4157
+ L: 53,
3877
4158
  S: this,
3878
4159
  C: (f, a) => f(...a)
3879
4160
  });
3880
- const duplex = new Duplex2({
4161
+ void this._safeCloseTransport(existingTransport);
4162
+ this._openTransports.delete(request.proxyId);
4163
+ }
4164
+ return new Stream(({ ready, next, close }) => {
4165
+ const pushNewState = createStateUpdater(next);
4166
+ const transportStream = new Duplex2({
3881
4167
  read: () => {
4168
+ const callbacks = [
4169
+ ...transportState.writeProcessedCallbacks
4170
+ ];
4171
+ transportState.writeProcessedCallbacks.length = 0;
4172
+ callbacks.forEach((cb) => cb());
3882
4173
  },
3883
- write: async (chunk, encoding, callback) => {
3884
- if (chunk.length > MAX_MESSAGE_SIZE) {
3885
- this.errors.raise(new Error(`message too large: ${chunk.length} > ${MAX_MESSAGE_SIZE}`));
3886
- }
3887
- try {
3888
- dataChannel.send(chunk);
3889
- } catch (err) {
3890
- this.errors.raise(err);
3891
- await this._close();
3892
- }
3893
- if (this._channel.bufferedAmount > MAX_BUFFERED_AMOUNT) {
3894
- if (this._writeCallback !== null) {
3895
- log15.error("consumer trying to write before we are ready for more data", void 0, {
3896
- F: __dxlog_file16,
3897
- L: 223,
3898
- S: this,
3899
- C: (f, a) => f(...a)
3900
- });
4174
+ write: function(chunk, _, callback) {
4175
+ next({
4176
+ data: {
4177
+ payload: chunk
3901
4178
  }
3902
- this._writeCallback = callback;
3903
- } else {
3904
- callback();
3905
- }
4179
+ });
4180
+ callback();
3906
4181
  }
3907
4182
  });
3908
- duplex.pipe(this._options.stream).pipe(duplex);
3909
- this._stream = duplex;
3910
- this._connected = true;
3911
- this.connected.emit();
3912
- };
3913
- this._channel.onclose = async (err) => {
3914
- log15.info("channel.onclose", {
3915
- err
3916
- }, {
3917
- F: __dxlog_file16,
3918
- L: 239,
3919
- S: this,
3920
- C: (f, a) => f(...a)
4183
+ const transport = this._transportFactory.createTransport({
4184
+ initiator: request.initiator,
4185
+ topic: request.topic,
4186
+ ownPeerKey: request.ownPeerKey,
4187
+ remotePeerKey: request.remotePeerKey,
4188
+ stream: transportStream,
4189
+ sendSignal: async (signal) => {
4190
+ next({
4191
+ signal: {
4192
+ payload: signal
4193
+ }
4194
+ });
4195
+ }
3921
4196
  });
3922
- await this._close();
3923
- };
3924
- this._channel.onerror = async (err) => {
3925
- this.errors.raise(new Error("channel error: " + err.toString()));
3926
- await this._close();
3927
- };
3928
- this._channel.onbufferedamountlow = () => {
3929
- const cb = this._writeCallback;
3930
- this._writeCallback = null;
3931
- cb?.();
3932
- };
3933
- this._channel.onmessage = (event) => {
3934
- let data = event.data;
3935
- if (data instanceof ArrayBuffer) {
3936
- data = Buffer2.from(data);
3937
- }
3938
- this._stream.push(data);
3939
- };
4197
+ const transportState = {
4198
+ proxyId: request.proxyId,
4199
+ transport,
4200
+ connectorStream: transportStream,
4201
+ writeProcessedCallbacks: []
4202
+ };
4203
+ pushNewState(ConnectionState4.CONNECTING);
4204
+ transport.connected.on(() => pushNewState(ConnectionState4.CONNECTED));
4205
+ transport.errors.handle(async (err) => {
4206
+ pushNewState(ConnectionState4.CLOSED, err);
4207
+ void this._safeCloseTransport(transportState);
4208
+ close(err);
4209
+ });
4210
+ transport.closed.on(async () => {
4211
+ pushNewState(ConnectionState4.CLOSED);
4212
+ void this._safeCloseTransport(transportState);
4213
+ close();
4214
+ });
4215
+ this._openTransports.set(request.proxyId, transportState);
4216
+ transport.open().catch(async (err) => {
4217
+ pushNewState(ConnectionState4.CLOSED, err);
4218
+ void this._safeCloseTransport(transportState);
4219
+ close(err);
4220
+ });
4221
+ ready();
4222
+ });
3940
4223
  }
3941
- async onSignal(signal) {
3942
- invariant14(this._peer, "not open", {
4224
+ async sendSignal({ proxyId, signal }) {
4225
+ const transport = this._openTransports.get(proxyId);
4226
+ invariant14(transport, void 0, {
3943
4227
  F: __dxlog_file16,
3944
- L: 265,
4228
+ L: 121,
3945
4229
  S: this,
3946
4230
  A: [
3947
- "this._peer",
3948
- "'not open'"
4231
+ "transport",
4232
+ ""
3949
4233
  ]
3950
4234
  });
3951
- try {
3952
- const data = signal.payload.data;
3953
- switch (data.type) {
3954
- case "offer": {
3955
- if (this._peer.connectionState !== "new") {
3956
- log15.error("received offer but peer not in state new", {
3957
- peer: this._peer
3958
- }, {
3959
- F: __dxlog_file16,
3960
- L: 272,
3961
- S: this,
3962
- C: (f, a) => f(...a)
3963
- });
3964
- this.errors.raise(new Error("invalid signalling state: received offer when peer is not in state new"));
3965
- break;
3966
- }
3967
- try {
3968
- await this._peer.setRemoteDescription({
3969
- type: data.type,
3970
- sdp: data.sdp
3971
- });
3972
- const answer = await this._peer.createAnswer();
3973
- await this._peer.setLocalDescription(answer);
3974
- await this._options.sendSignal({
3975
- payload: {
3976
- data: {
3977
- type: answer.type,
3978
- sdp: answer.sdp
3979
- }
3980
- }
3981
- });
3982
- this._readyForCandidates.wake();
3983
- } catch (err) {
3984
- log15.error("cannot handle offer from signalling server", {
3985
- err
3986
- }, {
3987
- F: __dxlog_file16,
3988
- L: 284,
3989
- S: this,
3990
- C: (f, a) => f(...a)
3991
- });
3992
- this.errors.raise(new Error("error handling offer"));
3993
- }
3994
- break;
3995
- }
3996
- case "answer":
3997
- try {
3998
- await this._peer.setRemoteDescription({
3999
- type: data.type,
4000
- sdp: data.sdp
4001
- });
4002
- this._readyForCandidates.wake();
4003
- } catch (err) {
4004
- log15.error("cannot handle answer from signalling server", {
4005
- err
4006
- }, {
4007
- F: __dxlog_file16,
4008
- L: 295,
4009
- S: this,
4010
- C: (f, a) => f(...a)
4011
- });
4012
- this.errors.raise(new Error("error handling answer"));
4013
- }
4014
- break;
4015
- case "candidate":
4016
- await this._readyForCandidates.wait();
4017
- await this._peer.addIceCandidate({
4018
- candidate: data.candidate.candidate
4019
- });
4020
- break;
4021
- default:
4022
- log15.error("unhandled signal type", {
4023
- type: data.type,
4024
- signal
4025
- }, {
4026
- F: __dxlog_file16,
4027
- L: 306,
4028
- S: this,
4029
- C: (f, a) => f(...a)
4030
- });
4031
- this.errors.raise(new Error(`unhandled signal type ${data.type}`));
4032
- }
4033
- } catch (err) {
4034
- log15.catch(err, void 0, {
4035
- F: __dxlog_file16,
4036
- L: 310,
4037
- S: this,
4038
- C: (f, a) => f(...a)
4039
- });
4040
- }
4041
- }
4042
- async getDetails() {
4043
- const stats = await this._getStats();
4044
- const rc = stats?.remoteCandidate;
4045
- if (!rc) {
4046
- return "unavailable";
4047
- }
4048
- if (rc.candidateType === "relay") {
4049
- return `${rc.ip}:${rc.port} relay for ${rc.relatedAddress}:${rc.relatedPort}`;
4050
- }
4051
- return `${rc.ip}:${rc.port} ${rc.candidateType}`;
4235
+ await transport.transport.onSignal(signal);
4052
4236
  }
4053
- async getStats() {
4054
- const stats = await this._getStats();
4055
- if (!stats) {
4056
- return {
4057
- bytesSent: 0,
4058
- bytesReceived: 0,
4059
- packetsSent: 0,
4060
- packetsReceived: 0,
4061
- rawStats: {}
4062
- };
4063
- }
4237
+ async getDetails({ proxyId }) {
4238
+ const transport = this._openTransports.get(proxyId);
4239
+ invariant14(transport, void 0, {
4240
+ F: __dxlog_file16,
4241
+ L: 128,
4242
+ S: this,
4243
+ A: [
4244
+ "transport",
4245
+ ""
4246
+ ]
4247
+ });
4064
4248
  return {
4065
- bytesSent: stats.transport.bytesSent,
4066
- bytesReceived: stats.transport.bytesReceived,
4067
- packetsSent: 0,
4068
- packetsReceived: 0,
4069
- rawStats: stats.raw
4249
+ details: await transport.transport.getDetails()
4070
4250
  };
4071
4251
  }
4072
- async _getStats() {
4073
- invariant14(this._peer, "not open", {
4252
+ async getStats({ proxyId }) {
4253
+ const transport = this._openTransports.get(proxyId);
4254
+ invariant14(transport, void 0, {
4074
4255
  F: __dxlog_file16,
4075
- L: 350,
4256
+ L: 135,
4076
4257
  S: this,
4077
4258
  A: [
4078
- "this._peer",
4079
- "'not open'"
4259
+ "transport",
4260
+ ""
4080
4261
  ]
4081
4262
  });
4082
- const stats = await this._peer.getStats();
4083
- const statsEntries = Array.from(stats.entries());
4084
- const transport = statsEntries.filter((s) => s[1].type === "transport")[0][1];
4085
- const candidatePair = statsEntries.filter((s) => s[0] === transport.selectedCandidatePairId);
4086
- let selectedCandidatePair;
4087
- let remoteCandidate;
4088
- if (candidatePair.length > 0) {
4089
- selectedCandidatePair = candidatePair[0][1];
4090
- remoteCandidate = statsEntries.filter((s) => s[0] === selectedCandidatePair.remoteCandidateId)[0][1];
4091
- }
4092
4263
  return {
4093
- transport,
4094
- selectedCandidatePair,
4095
- remoteCandidate,
4096
- raw: Object.fromEntries(stats)
4264
+ stats: await transport.transport.getStats()
4097
4265
  };
4098
4266
  }
4099
- async _disconnectStreams() {
4100
- this._options.stream.unpipe?.(this._stream)?.unpipe?.(this._options.stream);
4101
- }
4102
- };
4103
- _ts_decorate7([
4104
- synchronized6
4105
- ], LibDataChannelTransport.prototype, "_close", null);
4106
- var importESM = Function("path", "return import(path)");
4107
-
4108
- // packages/core/mesh/network-manager/src/transport/tcp-transport.browser.ts
4109
- import { Event as Event11 } from "@dxos/async";
4110
- import { ErrorStream as ErrorStream7 } from "@dxos/debug";
4111
- var TcpTransportFactory = {
4112
- createTransport: () => new TcpTransport()
4113
- };
4114
- var TcpTransport = class {
4115
- constructor() {
4116
- this.closed = new Event11();
4117
- this.connected = new Event11();
4118
- this.errors = new ErrorStream7();
4119
- }
4120
- get isOpen() {
4121
- return true;
4122
- }
4123
- async open() {
4124
- }
4125
- async close() {
4126
- }
4127
- async onSignal() {
4128
- throw new Error("Method not implemented.");
4267
+ async sendData({ proxyId, payload }) {
4268
+ const transport = this._openTransports.get(proxyId);
4269
+ invariant14(transport, void 0, {
4270
+ F: __dxlog_file16,
4271
+ L: 142,
4272
+ S: this,
4273
+ A: [
4274
+ "transport",
4275
+ ""
4276
+ ]
4277
+ });
4278
+ const bufferHasSpace = transport.connectorStream.push(payload);
4279
+ if (!bufferHasSpace) {
4280
+ await new Promise((resolve) => {
4281
+ transport.writeProcessedCallbacks.push(resolve);
4282
+ });
4283
+ }
4129
4284
  }
4130
- async getStats() {
4131
- throw new Error("Method not implemented.");
4285
+ async close({ proxyId }) {
4286
+ const transport = this._openTransports.get(proxyId);
4287
+ if (!transport) {
4288
+ return;
4289
+ }
4290
+ this._openTransports.delete(proxyId);
4291
+ await this._safeCloseTransport(transport);
4132
4292
  }
4133
- async getDetails() {
4134
- throw new Error("Method not implemented.");
4293
+ async _safeCloseTransport(transport) {
4294
+ if (this._openTransports.get(transport.proxyId) === transport) {
4295
+ this._openTransports.delete(transport.proxyId);
4296
+ }
4297
+ transport.writeProcessedCallbacks.forEach((cb) => cb());
4298
+ try {
4299
+ await transport.transport.close();
4300
+ } catch (error) {
4301
+ log15.warn("transport close error", {
4302
+ message: error?.message
4303
+ }, {
4304
+ F: __dxlog_file16,
4305
+ L: 172,
4306
+ S: this,
4307
+ C: (f, a) => f(...a)
4308
+ });
4309
+ }
4310
+ try {
4311
+ transport.connectorStream.end();
4312
+ } catch (error) {
4313
+ log15.warn("connectorStream close error", {
4314
+ message: error?.message
4315
+ }, {
4316
+ F: __dxlog_file16,
4317
+ L: 177,
4318
+ S: this,
4319
+ C: (f, a) => f(...a)
4320
+ });
4321
+ }
4322
+ log15("closed", void 0, {
4323
+ F: __dxlog_file16,
4324
+ L: 179,
4325
+ S: this,
4326
+ C: (f, a) => f(...a)
4327
+ });
4135
4328
  }
4136
4329
  };
4330
+ var createStateUpdater = (next) => {
4331
+ return (state, err) => {
4332
+ next({
4333
+ connection: {
4334
+ state,
4335
+ ...err ? {
4336
+ error: err.message
4337
+ } : void 0
4338
+ }
4339
+ });
4340
+ };
4341
+ };
4137
4342
 
4138
4343
  // packages/core/mesh/network-manager/src/wire-protocol.ts
4139
4344
  import { Teleport } from "@dxos/teleport";
@@ -4160,7 +4365,6 @@ var createTeleportProtocolFactory = (onConnection, defaultParams) => {
4160
4365
  };
4161
4366
 
4162
4367
  export {
4163
- process,
4164
4368
  ConnectionState,
4165
4369
  Connection,
4166
4370
  createIceProvider,
@@ -4178,15 +4382,10 @@ export {
4178
4382
  MemoryTransportFactory,
4179
4383
  MemoryTransport,
4180
4384
  TransportKind,
4181
- createSimplePeerTransportFactory,
4182
- SimplePeerTransport,
4183
- SimplePeerTransportService,
4184
- SimplePeerTransportProxy,
4185
- SimplePeerTransportProxyFactory,
4186
- createLibDataChannelTransportFactory,
4187
- LibDataChannelTransport,
4188
- TcpTransportFactory,
4189
- TcpTransport,
4385
+ createRtcTransportFactory,
4386
+ RtcTransportProxy,
4387
+ RtcTransportProxyFactory,
4388
+ RtcTransportService,
4190
4389
  createTeleportProtocolFactory
4191
4390
  };
4192
- //# sourceMappingURL=chunk-XYSYUN63.mjs.map
4391
+ //# sourceMappingURL=chunk-IDPSMH73.mjs.map