@dxos/network-manager 0.6.12-main.5cc132e → 0.6.12-main.78ddbdf

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