@dxos/network-manager 0.6.5 → 0.6.6-staging.23d123d
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.
- package/dist/lib/browser/{chunk-3UBXH53L.mjs → chunk-ZT4NXID2.mjs} +519 -452
- package/dist/lib/browser/chunk-ZT4NXID2.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +3 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +1 -1
- package/dist/lib/node/{chunk-VXLEPDWN.cjs → chunk-DZJ3BJOK.cjs} +603 -537
- package/dist/lib/node/chunk-DZJ3BJOK.cjs.map +7 -0
- package/dist/lib/node/index.cjs +29 -27
- package/dist/lib/node/index.cjs.map +2 -2
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +18 -18
- package/dist/types/src/signal/ice.d.ts +6 -0
- package/dist/types/src/signal/ice.d.ts.map +1 -0
- package/dist/types/src/signal/ice.test.d.ts +2 -0
- package/dist/types/src/signal/ice.test.d.ts.map +1 -0
- package/dist/types/src/signal/index.d.ts +1 -0
- package/dist/types/src/signal/index.d.ts.map +1 -1
- package/dist/types/src/swarm/connection.d.ts.map +1 -1
- package/dist/types/src/transport/libdatachannel-transport.d.ts +3 -1
- package/dist/types/src/transport/libdatachannel-transport.d.ts.map +1 -1
- package/dist/types/src/transport/simplepeer-transport-service.d.ts +3 -1
- package/dist/types/src/transport/simplepeer-transport-service.d.ts.map +1 -1
- package/dist/types/src/transport/simplepeer-transport.d.ts +5 -3
- package/dist/types/src/transport/simplepeer-transport.d.ts.map +1 -1
- package/package.json +19 -17
- package/src/signal/ice.test.ts +42 -0
- package/src/signal/ice.ts +38 -0
- package/src/signal/index.ts +1 -0
- package/src/swarm/connection.ts +6 -2
- package/src/transport/libdatachannel-transport.ts +23 -7
- package/src/transport/simplepeer-transport-service.ts +6 -1
- package/src/transport/simplepeer-transport.ts +94 -70
- package/dist/lib/browser/chunk-3UBXH53L.mjs.map +0 -7
- package/dist/lib/node/chunk-VXLEPDWN.cjs.map +0 -7
package/src/swarm/connection.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { DeferredTask, Event, sleep, scheduleTask, scheduleTaskInterval, synchronized, Trigger } from '@dxos/async';
|
|
6
|
-
import { Context, cancelWithContext } from '@dxos/context';
|
|
6
|
+
import { Context, cancelWithContext, ContextDisposedError } from '@dxos/context';
|
|
7
7
|
import { ErrorStream } from '@dxos/debug';
|
|
8
8
|
import { invariant } from '@dxos/invariant';
|
|
9
9
|
import { PublicKey } from '@dxos/keys';
|
|
@@ -381,7 +381,11 @@ export class Connection {
|
|
|
381
381
|
});
|
|
382
382
|
} catch (err) {
|
|
383
383
|
// TODO(nf): determine why instanceof doesn't work here
|
|
384
|
-
if (
|
|
384
|
+
if (
|
|
385
|
+
err instanceof CancelledError ||
|
|
386
|
+
err instanceof ContextDisposedError ||
|
|
387
|
+
(err instanceof Error && err.message?.includes('CANCELLED'))
|
|
388
|
+
) {
|
|
385
389
|
return;
|
|
386
390
|
}
|
|
387
391
|
|
|
@@ -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 = (
|
|
26
|
-
|
|
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
|
|
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(
|
|
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?:
|
|
21
|
+
webrtcConfig?: RTCConfiguration;
|
|
22
|
+
iceProvider?: IceProvider;
|
|
21
23
|
};
|
|
22
24
|
|
|
23
|
-
export const createSimplePeerTransportFactory = (
|
|
24
|
-
|
|
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
|
|
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
|
-
|
|
51
|
-
|
|
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.
|
|
217
|
+
log.trace('dxos.mesh.webrtc-transport.open', trace.end({ id: this._instanceId }));
|
|
136
218
|
}
|
|
137
219
|
|
|
138
|
-
|
|
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
|
|