@dxos/network-manager 0.6.5 → 0.6.6-main.e1a6e1f

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 (32) hide show
  1. package/dist/lib/browser/{chunk-3UBXH53L.mjs → chunk-2KJH3U3C.mjs} +506 -439
  2. package/dist/lib/browser/chunk-2KJH3U3C.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +3 -1
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/browser/testing/index.mjs +1 -1
  6. package/dist/lib/node/{chunk-VXLEPDWN.cjs → chunk-F5GUHIHT.cjs} +591 -525
  7. package/dist/lib/node/chunk-F5GUHIHT.cjs.map +7 -0
  8. package/dist/lib/node/index.cjs +29 -27
  9. package/dist/lib/node/index.cjs.map +2 -2
  10. package/dist/lib/node/meta.json +1 -1
  11. package/dist/lib/node/testing/index.cjs +18 -18
  12. package/dist/types/src/signal/ice.d.ts +6 -0
  13. package/dist/types/src/signal/ice.d.ts.map +1 -0
  14. package/dist/types/src/signal/ice.test.d.ts +2 -0
  15. package/dist/types/src/signal/ice.test.d.ts.map +1 -0
  16. package/dist/types/src/signal/index.d.ts +1 -0
  17. package/dist/types/src/signal/index.d.ts.map +1 -1
  18. package/dist/types/src/transport/libdatachannel-transport.d.ts +3 -1
  19. package/dist/types/src/transport/libdatachannel-transport.d.ts.map +1 -1
  20. package/dist/types/src/transport/simplepeer-transport-service.d.ts +3 -1
  21. package/dist/types/src/transport/simplepeer-transport-service.d.ts.map +1 -1
  22. package/dist/types/src/transport/simplepeer-transport.d.ts +5 -3
  23. package/dist/types/src/transport/simplepeer-transport.d.ts.map +1 -1
  24. package/package.json +19 -17
  25. package/src/signal/ice.test.ts +42 -0
  26. package/src/signal/ice.ts +38 -0
  27. package/src/signal/index.ts +1 -0
  28. package/src/transport/libdatachannel-transport.ts +23 -7
  29. package/src/transport/simplepeer-transport-service.ts +6 -1
  30. package/src/transport/simplepeer-transport.ts +94 -70
  31. package/dist/lib/browser/chunk-3UBXH53L.mjs.map +0 -7
  32. package/dist/lib/node/chunk-VXLEPDWN.cjs.map +0 -7
@@ -11,6 +11,7 @@ import { log } from '@dxos/log';
11
11
  import { type Signal } from '@dxos/protocols/proto/dxos/mesh/swarm';
12
12
 
13
13
  import { type Transport, type TransportFactory, type TransportOptions, type TransportStats } from './transport';
14
+ import { type IceProvider } from '../signal';
14
15
 
15
16
  const DATACHANNEL_LABEL = 'dxos.mesh.transport';
16
17
  const MAX_BUFFERED_AMOUNT = 64 * 1024;
@@ -20,11 +21,22 @@ const MAX_MESSAGE_SIZE = 64 * 1024;
20
21
 
21
22
  export type LibDataChannelTransportOptions = TransportOptions & {
22
23
  webrtcConfig?: RTCConfiguration;
24
+ iceProvider?: IceProvider;
23
25
  };
24
26
 
25
- export const createLibDataChannelTransportFactory = (webrtcConfig?: any): TransportFactory => ({
26
- createTransport: (options) => new LibDataChannelTransport({ ...options, webrtcConfig }),
27
- });
27
+ export const createLibDataChannelTransportFactory = (
28
+ webrtcConfig?: RTCConfiguration,
29
+ iceProvider?: IceProvider,
30
+ ): TransportFactory => {
31
+ return {
32
+ createTransport: (options) =>
33
+ new LibDataChannelTransport({
34
+ ...options,
35
+ webrtcConfig,
36
+ iceProvider,
37
+ }),
38
+ };
39
+ };
28
40
 
29
41
  /**
30
42
  * Transport
@@ -64,12 +76,16 @@ export class LibDataChannelTransport implements Transport {
64
76
  const { RTCPeerConnection } = (await importESM('node-datachannel/polyfill'))
65
77
  .default as typeof import('node-datachannel/polyfill');
66
78
 
79
+ const providedIceServers = await this._options.iceProvider?.getIceServers();
80
+
67
81
  // workaround https://github.com/murat-dogan/node-datachannel/pull/207
68
- if (this._options.webrtcConfig) {
69
- this._options.webrtcConfig.iceServers = this._options.webrtcConfig.iceServers ?? [];
70
- } else {
71
- this._options.webrtcConfig = { iceServers: [] };
82
+ if (!this._options.webrtcConfig) {
83
+ this._options.webrtcConfig = {};
72
84
  }
85
+ this._options.webrtcConfig.iceServers = [
86
+ ...(this._options.webrtcConfig.iceServers ?? []),
87
+ ...(providedIceServers ?? []),
88
+ ];
73
89
 
74
90
  this._peer = new RTCPeerConnection(this._options.webrtcConfig);
75
91
 
@@ -24,6 +24,7 @@ import {
24
24
  import { ComplexMap } from '@dxos/util';
25
25
 
26
26
  import { SimplePeerTransport } from './simplepeer-transport';
27
+ import { type IceProvider } from '../signal';
27
28
 
28
29
  type TransportState = {
29
30
  transport: SimplePeerTransport;
@@ -35,7 +36,10 @@ type TransportState = {
35
36
  export class SimplePeerTransportService implements BridgeService {
36
37
  private readonly transports = new ComplexMap<PublicKey, TransportState>(PublicKey.hash);
37
38
 
38
- constructor(private readonly _webrtcConfig?: any) {}
39
+ constructor(
40
+ private readonly _webrtcConfig?: RTCConfiguration,
41
+ private readonly _iceProvider?: IceProvider,
42
+ ) {}
39
43
 
40
44
  open(request: ConnectionRequest): Stream<BridgeEvent> {
41
45
  const rpcStream: Stream<BridgeEvent> = new Stream(({ ready, next, close }) => {
@@ -62,6 +66,7 @@ export class SimplePeerTransportService implements BridgeService {
62
66
  signal: { payload: signal },
63
67
  });
64
68
  },
69
+ iceProvider: this._iceProvider,
65
70
  });
66
71
 
67
72
  // TODO(burdon): Async.
@@ -6,7 +6,7 @@
6
6
  import SimplePeerConstructor, { type Instance as SimplePeer } from 'simple-peer';
7
7
  import invariant from 'tiny-invariant';
8
8
 
9
- import { Event } from '@dxos/async';
9
+ import { Event, synchronized } from '@dxos/async';
10
10
  import { ErrorStream, raise } from '@dxos/debug';
11
11
  import { PublicKey } from '@dxos/keys';
12
12
  import { log } from '@dxos/log';
@@ -15,20 +15,30 @@ import { type Signal } from '@dxos/protocols/proto/dxos/mesh/swarm';
15
15
 
16
16
  import { type Transport, type TransportFactory, type TransportOptions, type TransportStats } from './transport';
17
17
  import { wrtc } from './webrtc';
18
+ import { type IceProvider } from '../signal';
18
19
 
19
20
  export type SimplePeerTransportParams = TransportOptions & {
20
- webrtcConfig?: any;
21
+ webrtcConfig?: RTCConfiguration;
22
+ iceProvider?: IceProvider;
21
23
  };
22
24
 
23
- export const createSimplePeerTransportFactory = (webrtcConfig?: any): TransportFactory => ({
24
- createTransport: (options) => new SimplePeerTransport({ ...options, webrtcConfig }),
25
+ export const createSimplePeerTransportFactory = (
26
+ webrtcConfig?: RTCConfiguration,
27
+ iceProvider?: IceProvider,
28
+ ): TransportFactory => ({
29
+ createTransport: (options) =>
30
+ new SimplePeerTransport({
31
+ ...options,
32
+ webrtcConfig,
33
+ iceProvider,
34
+ }),
25
35
  });
26
36
 
27
37
  /**
28
38
  * Implements Transport for WebRTC. Uses simple-peer under the hood.
29
39
  */
30
40
  export class SimplePeerTransport implements Transport {
31
- private readonly _peer: SimplePeer;
41
+ private _peer?: SimplePeer = undefined;
32
42
  private _closed = false;
33
43
  private _piped = false;
34
44
 
@@ -46,9 +56,81 @@ export class SimplePeerTransport implements Transport {
46
56
  /**
47
57
  * @params opts.config formatted as per https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection
48
58
  */
49
- constructor(private readonly _params: SimplePeerTransportParams) {
50
- log.trace('dxos.mesh.webrtc-transport.constructor', trace.begin({ id: this._instanceId }));
51
- log('created connection', _params);
59
+ constructor(private readonly _params: SimplePeerTransportParams) {}
60
+
61
+ async getStats(): Promise<TransportStats> {
62
+ const stats = await this._getStats();
63
+ if (!stats) {
64
+ return {
65
+ bytesSent: 0,
66
+ bytesReceived: 0,
67
+ packetsSent: 0,
68
+ packetsReceived: 0,
69
+ rawStats: {},
70
+ };
71
+ }
72
+
73
+ // TODO(nf): transport or candidatePair?
74
+ return {
75
+ bytesSent: stats.transport.bytesSent,
76
+ bytesReceived: stats.transport.bytesReceived,
77
+ packetsSent: stats.transport.packetsSent,
78
+ packetsReceived: stats.transport.packetsReceived,
79
+ rawStats: stats.raw,
80
+ };
81
+ }
82
+
83
+ async _getStats(): Promise<any> {
84
+ if (typeof (this._peer as any)?._pc?.getStats !== 'function') {
85
+ return null;
86
+ }
87
+ return await (this._peer as any)._pc.getStats().then((stats: any) => {
88
+ const statsEntries = Array.from(stats.entries() as any[]);
89
+ const transport = statsEntries.filter((s: any) => s[1].type === 'transport')[0][1];
90
+ const candidatePair = statsEntries.filter((s: any) => s[0] === transport.selectedCandidatePairId);
91
+ let selectedCandidatePair: any;
92
+ let remoteCandidate: any;
93
+ if (candidatePair.length > 0) {
94
+ selectedCandidatePair = candidatePair[0][1];
95
+ remoteCandidate = statsEntries.filter((s: any) => s[0] === selectedCandidatePair.remoteCandidateId)[0][1];
96
+ }
97
+ return {
98
+ datachannel: statsEntries.filter((s: any) => s[1].type === 'data-channel')[0][1],
99
+ transport,
100
+ selectedCandidatePair,
101
+ remoteCandidate,
102
+ raw: Object.fromEntries(stats.entries()),
103
+ };
104
+ });
105
+ }
106
+
107
+ async getDetails(): Promise<string> {
108
+ const stats = await this._getStats();
109
+ const rc = stats?.remoteCandidate;
110
+ if (!rc) {
111
+ return 'unavailable';
112
+ }
113
+
114
+ if (rc.candidateType === 'relay') {
115
+ return `${rc.ip}:${rc.port}/${rc.protocol} relay for ${rc.relatedAddress}:${rc.relatedPort}`;
116
+ }
117
+ return `${rc.ip}:${rc.port}/${rc.protocol} ${rc.candidateType}`;
118
+ }
119
+
120
+ @synchronized
121
+ async open() {
122
+ log.trace('dxos.mesh.webrtc-transport.open', trace.begin({ id: this._instanceId }));
123
+ log('created connection', { params: this._params });
124
+
125
+ const providedIceServers = await this._params.iceProvider?.getIceServers();
126
+ if (!this._params.webrtcConfig) {
127
+ this._params.webrtcConfig = {};
128
+ }
129
+ this._params.webrtcConfig.iceServers = [
130
+ ...(this._params.webrtcConfig.iceServers ?? []),
131
+ ...(providedIceServers ?? []),
132
+ ];
133
+
52
134
  this._peer = new SimplePeerConstructor({
53
135
  channelName: 'dxos.mesh.transport',
54
136
  initiator: this._params.initiator,
@@ -132,70 +214,10 @@ export class SimplePeerTransport implements Transport {
132
214
  await this.close();
133
215
  });
134
216
 
135
- log.trace('dxos.mesh.webrtc-transport.constructor', trace.end({ id: this._instanceId }));
217
+ log.trace('dxos.mesh.webrtc-transport.open', trace.end({ id: this._instanceId }));
136
218
  }
137
219
 
138
- async getStats(): Promise<TransportStats> {
139
- const stats = await this._getStats();
140
- if (!stats) {
141
- return {
142
- bytesSent: 0,
143
- bytesReceived: 0,
144
- packetsSent: 0,
145
- packetsReceived: 0,
146
- rawStats: {},
147
- };
148
- }
149
-
150
- // TODO(nf): transport or candidatePair?
151
- return {
152
- bytesSent: stats.transport.bytesSent,
153
- bytesReceived: stats.transport.bytesReceived,
154
- packetsSent: stats.transport.packetsSent,
155
- packetsReceived: stats.transport.packetsReceived,
156
- rawStats: stats.raw,
157
- };
158
- }
159
-
160
- async _getStats(): Promise<any> {
161
- if (typeof (this._peer as any)?._pc?.getStats !== 'function') {
162
- return null;
163
- }
164
- return await (this._peer as any)._pc.getStats().then((stats: any) => {
165
- const statsEntries = Array.from(stats.entries() as any[]);
166
- const transport = statsEntries.filter((s: any) => s[1].type === 'transport')[0][1];
167
- const candidatePair = statsEntries.filter((s: any) => s[0] === transport.selectedCandidatePairId);
168
- let selectedCandidatePair: any;
169
- let remoteCandidate: any;
170
- if (candidatePair.length > 0) {
171
- selectedCandidatePair = candidatePair[0][1];
172
- remoteCandidate = statsEntries.filter((s: any) => s[0] === selectedCandidatePair.remoteCandidateId)[0][1];
173
- }
174
- return {
175
- datachannel: statsEntries.filter((s: any) => s[1].type === 'data-channel')[0][1],
176
- transport,
177
- selectedCandidatePair,
178
- remoteCandidate,
179
- raw: Object.fromEntries(stats.entries()),
180
- };
181
- });
182
- }
183
-
184
- async getDetails(): Promise<string> {
185
- const stats = await this._getStats();
186
- const rc = stats?.remoteCandidate;
187
- if (!rc) {
188
- return 'unavailable';
189
- }
190
-
191
- if (rc.candidateType === 'relay') {
192
- return `${rc.ip}:${rc.port}/${rc.protocol} relay for ${rc.relatedAddress}:${rc.relatedPort}`;
193
- }
194
- return `${rc.ip}:${rc.port}/${rc.protocol} ${rc.candidateType}`;
195
- }
196
-
197
- async open() {}
198
-
220
+ @synchronized
199
221
  async close() {
200
222
  log('closing...');
201
223
  if (this._closed) {
@@ -208,12 +230,14 @@ export class SimplePeerTransport implements Transport {
208
230
  log('closed');
209
231
  }
210
232
 
233
+ @synchronized
211
234
  async onSignal(signal: Signal) {
212
235
  if (this._closed) {
213
236
  return; // Ignore signals after close.
214
237
  }
215
238
 
216
239
  invariant(signal.payload.data, 'Signal message must contain signal data.');
240
+ invariant(this._peer, 'Peer must be initialized before receiving signals.');
217
241
  this._peer.signal(signal.payload.data);
218
242
  }
219
243