@dxos/network-manager 0.4.10-main.fd4f2a3 → 0.4.10-next.71cec10

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 (45) hide show
  1. package/dist/lib/browser/{chunk-OIR45T43.mjs → chunk-SOVZDG44.mjs} +464 -425
  2. package/dist/lib/browser/chunk-SOVZDG44.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +1 -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-HUPX2JGP.cjs → chunk-BMZFS4HT.cjs} +480 -440
  7. package/dist/lib/node/chunk-BMZFS4HT.cjs.map +7 -0
  8. package/dist/lib/node/index.cjs +27 -27
  9. package/dist/lib/node/index.cjs.map +1 -1
  10. package/dist/lib/node/meta.json +1 -1
  11. package/dist/lib/node/testing/index.cjs +18 -18
  12. package/dist/types/src/swarm/connection.d.ts.map +1 -1
  13. package/dist/types/src/swarm/peer.d.ts.map +1 -1
  14. package/dist/types/src/transport/libdatachannel-transport.d.ts +20 -19
  15. package/dist/types/src/transport/libdatachannel-transport.d.ts.map +1 -1
  16. package/dist/types/src/transport/memory-transport.d.ts +17 -10
  17. package/dist/types/src/transport/memory-transport.d.ts.map +1 -1
  18. package/dist/types/src/transport/simplepeer-transport-proxy.d.ts +7 -9
  19. package/dist/types/src/transport/simplepeer-transport-proxy.d.ts.map +1 -1
  20. package/dist/types/src/transport/simplepeer-transport-service.d.ts.map +1 -1
  21. package/dist/types/src/transport/simplepeer-transport.d.ts +9 -11
  22. package/dist/types/src/transport/simplepeer-transport.d.ts.map +1 -1
  23. package/dist/types/src/transport/tcp-transport.browser.d.ts +7 -2
  24. package/dist/types/src/transport/tcp-transport.browser.d.ts.map +1 -1
  25. package/dist/types/src/transport/tcp-transport.d.ts +6 -4
  26. package/dist/types/src/transport/tcp-transport.d.ts.map +1 -1
  27. package/dist/types/src/transport/transport.d.ts +18 -12
  28. package/dist/types/src/transport/transport.d.ts.map +1 -1
  29. package/package.json +17 -17
  30. package/src/swarm/connection.ts +7 -5
  31. package/src/swarm/peer.ts +3 -4
  32. package/src/transport/libdatachannel-transport.test.ts +14 -9
  33. package/src/transport/libdatachannel-transport.ts +226 -204
  34. package/src/transport/memory-transport.test.ts +10 -7
  35. package/src/transport/memory-transport.ts +49 -42
  36. package/src/transport/simplepeer-transport-proxy-test.ts +13 -8
  37. package/src/transport/simplepeer-transport-proxy.ts +48 -49
  38. package/src/transport/simplepeer-transport-service.ts +5 -2
  39. package/src/transport/simplepeer-transport.test.ts +9 -5
  40. package/src/transport/simplepeer-transport.ts +25 -25
  41. package/src/transport/tcp-transport.browser.ts +10 -5
  42. package/src/transport/tcp-transport.ts +31 -22
  43. package/src/transport/transport.ts +25 -14
  44. package/dist/lib/browser/chunk-OIR45T43.mjs.map +0 -7
  45. package/dist/lib/node/chunk-HUPX2JGP.cjs.map +0 -7
@@ -12,7 +12,7 @@ import { log, logInfo } from '@dxos/log';
12
12
  import { type Signal } from '@dxos/protocols/proto/dxos/mesh/swarm';
13
13
  import { ComplexMap } from '@dxos/util';
14
14
 
15
- import { type Transport, type TransportFactory, type TransportOptions, type TransportStats } from './transport';
15
+ import { type Transport, type TransportFactory, type TransportOptions } from './transport';
16
16
 
17
17
  // TODO(burdon): Make configurable.
18
18
  // Delay (in milliseconds) for data being sent through in-memory connections to simulate network latency.
@@ -31,7 +31,7 @@ const createStreamDelay = (delay: number): NodeJS.ReadWriteStream => {
31
31
  };
32
32
 
33
33
  export const MemoryTransportFactory: TransportFactory = {
34
- createTransport: (params) => new MemoryTransport(params),
34
+ createTransport: (options) => new MemoryTransport(options),
35
35
  };
36
36
 
37
37
  /**
@@ -41,10 +41,6 @@ export class MemoryTransport implements Transport {
41
41
  // TODO(burdon): Remove static properties (inject context into constructor).
42
42
  private static readonly _connections = new ComplexMap<PublicKey, MemoryTransport>(PublicKey.hash);
43
43
 
44
- public readonly closed = new Event<void>();
45
- public readonly connected = new Event<void>();
46
- public readonly errors = new ErrorStream();
47
-
48
44
  @logInfo
49
45
  private readonly _instanceId = PublicKey.random(); // TODO(burdon): Rename peerId? (Use local/remote labels in logs).
50
46
 
@@ -53,37 +49,46 @@ export class MemoryTransport implements Transport {
53
49
  private readonly _outgoingDelay = createStreamDelay(MEMORY_TRANSPORT_DELAY);
54
50
  private readonly _incomingDelay = createStreamDelay(MEMORY_TRANSPORT_DELAY);
55
51
 
56
- private _destroyed = false;
52
+ private _closed = false;
57
53
 
58
54
  @logInfo
59
55
  private _remoteInstanceId!: PublicKey;
60
56
 
61
57
  private _remoteConnection?: MemoryTransport;
62
58
 
63
- constructor(private readonly options: TransportOptions) {
64
- log('creating');
59
+ public readonly closed = new Event<void>();
60
+ public readonly connected = new Event<void>();
61
+ public readonly errors = new ErrorStream();
65
62
 
63
+ constructor(private readonly _options: TransportOptions) {
66
64
  invariant(!MemoryTransport._connections.has(this._instanceId), 'Duplicate memory connection');
67
65
  MemoryTransport._connections.set(this._instanceId, this);
66
+ }
67
+
68
+ get isOpen() {
69
+ // TODO(burdon): Open state?
70
+ return !this._closed;
71
+ }
72
+
73
+ async open() {
74
+ log('opening...');
68
75
 
69
76
  // Initiator will send a signal, the receiver will receive the unique ID and connect the streams.
70
- if (this.options.initiator) {
71
- // prettier-ignore
72
- setTimeout(async () => {
73
- log('sending signal');
74
- void this.options.sendSignal({
75
- payload: { transportId: this._instanceId.toHex() }
76
- }).catch(err => {
77
- if (!this._destroyed) {
78
- this.errors.raise(err);
79
- }
80
- });
81
- });
77
+ if (this._options.initiator) {
78
+ log('sending signal');
79
+ try {
80
+ await this._options.sendSignal({ payload: { transportId: this._instanceId.toHex() } });
81
+ } catch (err) {
82
+ if (!this._closed) {
83
+ this.errors.raise(toError(err));
84
+ }
85
+ }
82
86
  } else {
87
+ // Don't block the open method.
83
88
  this._remote
84
- .wait({ timeout: this.options.timeout ?? 1000 })
89
+ .wait({ timeout: this._options.timeout ?? 1_000 })
85
90
  .then((remoteId) => {
86
- if (this._destroyed) {
91
+ if (this._closed) {
87
92
  return;
88
93
  }
89
94
 
@@ -91,7 +96,7 @@ export class MemoryTransport implements Transport {
91
96
  this._remoteConnection = MemoryTransport._connections.get(this._remoteInstanceId);
92
97
  if (!this._remoteConnection) {
93
98
  // Remote connection was destroyed before we could connect.
94
- this._destroyed = true;
99
+ this._closed = true;
95
100
  this.closed.emit();
96
101
  return;
97
102
  }
@@ -101,17 +106,17 @@ export class MemoryTransport implements Transport {
101
106
  this._remoteConnection._remoteInstanceId = this._instanceId;
102
107
 
103
108
  log('connected');
104
- this.options.stream
109
+ this._options.stream
105
110
  .pipe(this._outgoingDelay)
106
- .pipe(this._remoteConnection.options.stream)
111
+ .pipe(this._remoteConnection._options.stream)
107
112
  .pipe(this._incomingDelay)
108
- .pipe(this.options.stream);
113
+ .pipe(this._options.stream);
109
114
 
110
115
  this.connected.emit();
111
116
  this._remoteConnection.connected.emit();
112
117
  })
113
118
  .catch((err) => {
114
- if (this._destroyed) {
119
+ if (this._closed) {
115
120
  return;
116
121
  }
117
122
 
@@ -120,14 +125,13 @@ export class MemoryTransport implements Transport {
120
125
  }
121
126
  }
122
127
 
123
- async destroy(): Promise<void> {
124
- log('closing');
125
- this._destroyed = true;
128
+ async close() {
129
+ log('closing...');
130
+ this._closed = true;
126
131
 
127
132
  MemoryTransport._connections.delete(this._instanceId);
128
133
  if (this._remoteConnection) {
129
- log('closing');
130
- this._remoteConnection._destroyed = true;
134
+ this._remoteConnection._closed = true;
131
135
  MemoryTransport._connections.delete(this._remoteInstanceId);
132
136
 
133
137
  // TODO(dmaretskyi): Hypercore streams do not seem to have the unpipe method.
@@ -138,27 +142,27 @@ export class MemoryTransport implements Transport {
138
142
  // code .unpipe(this._incomingDelay)
139
143
  // code .unpipe(this._stream);
140
144
 
141
- this.options.stream.unpipe(this._incomingDelay);
142
- this._incomingDelay.unpipe(this._remoteConnection.options.stream);
143
- this._remoteConnection.options.stream.unpipe(this._outgoingDelay);
144
- this._outgoingDelay.unpipe(this.options.stream);
145
- this.options.stream.unpipe(this._outgoingDelay);
145
+ this._options.stream.unpipe(this._incomingDelay);
146
+ this._incomingDelay.unpipe(this._remoteConnection._options.stream);
147
+ this._remoteConnection._options.stream.unpipe(this._outgoingDelay);
148
+ this._outgoingDelay.unpipe(this._options.stream);
149
+ this._options.stream.unpipe(this._outgoingDelay);
146
150
 
147
151
  this._remoteConnection.closed.emit();
148
152
  this._remoteConnection._remoteConnection = undefined;
149
153
  this._remoteConnection = undefined;
150
- log('closed');
151
154
  }
152
155
 
153
156
  this.closed.emit();
154
157
  log('closed');
155
158
  }
156
159
 
157
- signal({ payload }: Signal) {
160
+ async onSignal({ payload }: Signal) {
158
161
  log('received signal', { payload });
159
162
  if (!payload?.transportId) {
160
163
  return;
161
164
  }
165
+
162
166
  // TODO(burdon): Check open?
163
167
  const transportId = payload.transportId as string;
164
168
  if (transportId) {
@@ -167,11 +171,11 @@ export class MemoryTransport implements Transport {
167
171
  }
168
172
  }
169
173
 
170
- async getDetails(): Promise<string> {
174
+ async getDetails() {
171
175
  return this._instanceId.toHex();
172
176
  }
173
177
 
174
- async getStats(): Promise<TransportStats> {
178
+ async getStats() {
175
179
  return {
176
180
  bytesSent: 0,
177
181
  bytesReceived: 0,
@@ -180,3 +184,6 @@ export class MemoryTransport implements Transport {
180
184
  };
181
185
  }
182
186
  }
187
+
188
+ // TODO(burdon): Factor out.
189
+ const toError = (err: any): Error => (err instanceof Error ? err : new Error(String(err)));
@@ -65,7 +65,8 @@ describe('SimplePeerTransportProxy', () => {
65
65
  sendSignal,
66
66
  bridgeService: rpcClient.rpc.BridgeService,
67
67
  });
68
- afterTest(async () => await simplePeerTransportProxy.destroy());
68
+ await simplePeerTransportProxy.open();
69
+ afterTest(async () => await simplePeerTransportProxy.close());
69
70
 
70
71
  return { simplePeerService: rpcService, SimplePeerTransportProxy: simplePeerTransportProxy };
71
72
  };
@@ -75,7 +76,7 @@ describe('SimplePeerTransportProxy', () => {
75
76
  const { SimplePeerTransportProxy: connection } = await setupProxy();
76
77
 
77
78
  const wait = connection.closed.waitForCount(1);
78
- await connection.destroy();
79
+ await connection.close();
79
80
  await wait;
80
81
  }).timeout(1_000);
81
82
 
@@ -85,7 +86,7 @@ describe('SimplePeerTransportProxy', () => {
85
86
  initiator: true,
86
87
  stream: stream1,
87
88
  sendSignal: async (signal) => {
88
- connection2.signal(signal);
89
+ await connection2.onSignal(signal);
89
90
  },
90
91
  });
91
92
  afterTest(() => connection1.errors.assertNoUnhandledErrors());
@@ -95,10 +96,11 @@ describe('SimplePeerTransportProxy', () => {
95
96
  initiator: false,
96
97
  stream: stream2,
97
98
  sendSignal: async (signal) => {
98
- connection1.signal(signal);
99
+ await connection1.onSignal(signal);
99
100
  },
100
101
  });
101
102
  afterTest(() => connection2.errors.assertNoUnhandledErrors());
103
+
102
104
  await TestStream.assertConnectivity(stream1, stream2);
103
105
  }).timeout(2_000);
104
106
 
@@ -147,13 +149,13 @@ describe('SimplePeerTransportProxy', () => {
147
149
  initiator: true,
148
150
  stream: stream1,
149
151
  sendSignal: async (signal) => {
150
- proxy2.signal(signal);
152
+ await proxy2.onSignal(signal);
151
153
  },
152
154
  bridgeService: rpcClient.rpc.BridgeService,
153
155
  });
154
156
  afterTest(async () => {
155
157
  proxy1.errors.assertNoUnhandledErrors();
156
- await proxy1.destroy();
158
+ await proxy1.close();
157
159
  });
158
160
 
159
161
  const stream2 = new TestStream();
@@ -161,15 +163,18 @@ describe('SimplePeerTransportProxy', () => {
161
163
  initiator: false,
162
164
  stream: stream2,
163
165
  sendSignal: async (signal) => {
164
- proxy1.signal(signal);
166
+ await proxy1.onSignal(signal);
165
167
  },
166
168
  bridgeService: rpcClient.rpc.BridgeService,
167
169
  });
168
170
  afterTest(async () => {
169
171
  proxy2.errors.assertNoUnhandledErrors();
170
- await proxy2.destroy();
172
+ await proxy2.close();
171
173
  });
172
174
 
175
+ await proxy1.open();
176
+ await proxy2.open();
177
+
173
178
  await TestStream.assertConnectivity(stream1, stream2);
174
179
  }).timeout(3_000);
175
180
  });
@@ -28,12 +28,8 @@ const RPC_TIMEOUT = 10_000;
28
28
  const RESP_MIN_THRESHOLD = 500;
29
29
  const TIMEOUT_THRESHOLD = 10;
30
30
 
31
- export type SimplePeerTransportProxyParams = {
32
- initiator: boolean;
33
- stream: NodeJS.ReadWriteStream;
31
+ export type SimplePeerTransportProxyOptions = TransportOptions & {
34
32
  bridgeService: BridgeService;
35
- // TODO(burdon): Rename onSignal.
36
- sendSignal: (signal: Signal) => Promise<void>;
37
33
  };
38
34
 
39
35
  export class SimplePeerTransportProxy implements Transport {
@@ -48,11 +44,18 @@ export class SimplePeerTransportProxy implements Transport {
48
44
  private _closed = false;
49
45
  private _serviceStream!: Stream<BridgeEvent>;
50
46
 
51
- constructor(private readonly _params: SimplePeerTransportProxyParams) {
52
- this._serviceStream = this._params.bridgeService.open(
47
+ constructor(private readonly _options: SimplePeerTransportProxyOptions) {}
48
+
49
+ get isOpen() {
50
+ // TODO(burdon): Open state?
51
+ return !this._closed;
52
+ }
53
+
54
+ async open() {
55
+ this._serviceStream = this._options.bridgeService.open(
53
56
  {
54
57
  proxyId: this._proxyId,
55
- initiator: this._params.initiator,
58
+ initiator: this._options.initiator,
56
59
  },
57
60
  { timeout: RPC_TIMEOUT },
58
61
  );
@@ -73,7 +76,7 @@ export class SimplePeerTransportProxy implements Transport {
73
76
  const proxyStream = new Writable({
74
77
  write: (chunk, _, callback) => {
75
78
  const then = performance.now();
76
- this._params.bridgeService
79
+ this._options.bridgeService
77
80
  .sendData(
78
81
  {
79
82
  proxyId: this._proxyId,
@@ -111,12 +114,42 @@ export class SimplePeerTransportProxy implements Transport {
111
114
  log('proxystream error', { err });
112
115
  });
113
116
 
114
- this._params.stream.pipe(proxyStream);
117
+ this._options.stream.pipe(proxyStream);
115
118
  },
116
119
  (error) => log.catch(error),
117
120
  );
118
121
  }
119
122
 
123
+ async close() {
124
+ await this._ctx.dispose();
125
+ if (this._closed) {
126
+ return;
127
+ }
128
+
129
+ await this._serviceStream.close();
130
+
131
+ try {
132
+ await this._options.bridgeService.close({ proxyId: this._proxyId }, { timeout: RPC_TIMEOUT });
133
+ } catch (err: any) {
134
+ log.catch(err);
135
+ }
136
+
137
+ this.closed.emit();
138
+ this._closed = true;
139
+ }
140
+
141
+ async onSignal(signal: Signal) {
142
+ this._options.bridgeService
143
+ .sendSignal(
144
+ {
145
+ proxyId: this._proxyId,
146
+ signal,
147
+ },
148
+ { timeout: RPC_TIMEOUT },
149
+ )
150
+ .catch((err) => this.errors.raise(decodeError(err)));
151
+ }
152
+
120
153
  private async _handleConnection(connectionEvent: BridgeEvent.ConnectionEvent): Promise<void> {
121
154
  if (connectionEvent.error) {
122
155
  this.errors.raise(decodeError(connectionEvent.error));
@@ -128,7 +161,7 @@ export class SimplePeerTransportProxy implements Transport {
128
161
  break;
129
162
  }
130
163
  case ConnectionState.CLOSED: {
131
- await this.destroy();
164
+ await this.close();
132
165
  break;
133
166
  }
134
167
  }
@@ -136,53 +169,22 @@ export class SimplePeerTransportProxy implements Transport {
136
169
 
137
170
  private _handleData(dataEvent: BridgeEvent.DataEvent) {
138
171
  // NOTE: This must be a Buffer otherwise hypercore-protocol breaks.
139
- this._params.stream.write(arrayToBuffer(dataEvent.payload));
172
+ this._options.stream.write(arrayToBuffer(dataEvent.payload));
140
173
  }
141
174
 
142
175
  private async _handleSignal(signalEvent: BridgeEvent.SignalEvent) {
143
- await this._params.sendSignal(signalEvent.payload);
144
- }
145
-
146
- signal(signal: Signal): void {
147
- this._params.bridgeService
148
- .sendSignal(
149
- {
150
- proxyId: this._proxyId,
151
- signal,
152
- },
153
- { timeout: RPC_TIMEOUT },
154
- )
155
- .catch((err) => this.errors.raise(decodeError(err)));
176
+ await this._options.sendSignal(signalEvent.payload);
156
177
  }
157
178
 
158
179
  async getDetails(): Promise<string> {
159
- return (await this._params.bridgeService.getDetails({ proxyId: this._proxyId }, { timeout: RPC_TIMEOUT })).details;
180
+ return (await this._options.bridgeService.getDetails({ proxyId: this._proxyId }, { timeout: RPC_TIMEOUT })).details;
160
181
  }
161
182
 
162
183
  async getStats(): Promise<TransportStats> {
163
- return (await this._params.bridgeService.getStats({ proxyId: this._proxyId }, { timeout: RPC_TIMEOUT }))
184
+ return (await this._options.bridgeService.getStats({ proxyId: this._proxyId }, { timeout: RPC_TIMEOUT }))
164
185
  .stats as TransportStats;
165
186
  }
166
187
 
167
- // TODO(burdon): Move open from constructor.
168
- async destroy(): Promise<void> {
169
- await this._ctx.dispose();
170
- if (this._closed) {
171
- return;
172
- }
173
-
174
- await this._serviceStream.close();
175
-
176
- try {
177
- await this._params.bridgeService.close({ proxyId: this._proxyId }, { timeout: RPC_TIMEOUT });
178
- } catch (err: any) {
179
- log.catch(err);
180
- }
181
-
182
- this.closed.emit();
183
- this._closed = true;
184
- }
185
-
186
188
  /**
187
189
  * Called when underlying proxy service becomes unavailable.
188
190
  */
@@ -214,7 +216,6 @@ export class SimplePeerTransportProxyFactory implements TransportFactory {
214
216
 
215
217
  createTransport(options: TransportOptions): Transport {
216
218
  invariant(this._bridgeService, 'SimplePeerTransportProxyFactory is not ready to open connections');
217
-
218
219
  const transport = new SimplePeerTransportProxy({
219
220
  ...options,
220
221
  bridgeService: this._bridgeService,
@@ -222,7 +223,6 @@ export class SimplePeerTransportProxyFactory implements TransportFactory {
222
223
 
223
224
  this._connections.add(transport);
224
225
  transport.closed.on(() => this._connections.delete(transport));
225
-
226
226
  return transport;
227
227
  }
228
228
  }
@@ -230,7 +230,6 @@ export class SimplePeerTransportProxyFactory implements TransportFactory {
230
230
  // TODO(nf): fix so Errors crossing RPC boundary preserve class
231
231
  const decodeError = (err: Error | string) => {
232
232
  const message = typeof err === 'string' ? err : err.message;
233
-
234
233
  if (message.includes('CONNECTION_RESET')) {
235
234
  return new ConnectionResetError(message);
236
235
  } else if (message.includes('TIMEOUT')) {
@@ -64,6 +64,9 @@ export class SimplePeerTransportService implements BridgeService {
64
64
  },
65
65
  });
66
66
 
67
+ // TODO(burdon): Async.
68
+ void transport.open();
69
+
67
70
  next({
68
71
  connection: {
69
72
  state: ConnectionState.CONNECTING,
@@ -114,7 +117,7 @@ export class SimplePeerTransportService implements BridgeService {
114
117
 
115
118
  async sendSignal({ proxyId, signal }: SignalRequest): Promise<void> {
116
119
  invariant(this.transports.has(proxyId));
117
- await this.transports.get(proxyId)!.transport.signal(signal);
120
+ await this.transports.get(proxyId)!.transport.onSignal(signal);
118
121
  }
119
122
 
120
123
  async getDetails({ proxyId }: DetailsRequest): Promise<DetailsResponse> {
@@ -142,7 +145,7 @@ export class SimplePeerTransportService implements BridgeService {
142
145
  }
143
146
 
144
147
  async close({ proxyId }: CloseRequest) {
145
- await this.transports.get(proxyId)?.transport.destroy();
148
+ await this.transports.get(proxyId)?.transport.close();
146
149
  await this.transports.get(proxyId)?.stream.end();
147
150
  if (this.transports.get(proxyId)) {
148
151
  this.transports.get(proxyId)!.state = 'CLOSED';
@@ -18,8 +18,9 @@ describe('SimplePeerTransport', () => {
18
18
  sendSignal: async () => {},
19
19
  });
20
20
 
21
+ await connection.open();
21
22
  const wait = connection.closed.waitForCount(1);
22
- await connection.destroy();
23
+ await connection.close();
23
24
  await wait;
24
25
  })
25
26
  .timeout(1_000)
@@ -32,10 +33,10 @@ describe('SimplePeerTransport', () => {
32
33
  stream: stream1,
33
34
  sendSignal: async (signal) => {
34
35
  await sleep(10);
35
- await connection2.signal(signal);
36
+ await connection2.onSignal(signal);
36
37
  },
37
38
  });
38
- afterTest(() => connection1.destroy());
39
+ afterTest(() => connection1.close());
39
40
  afterTest(() => connection1.errors.assertNoUnhandledErrors());
40
41
 
41
42
  const stream2 = new TestStream();
@@ -44,12 +45,15 @@ describe('SimplePeerTransport', () => {
44
45
  stream: stream2,
45
46
  sendSignal: async (signal) => {
46
47
  await sleep(10);
47
- await connection1.signal(signal);
48
+ await connection1.onSignal(signal);
48
49
  },
49
50
  });
50
- afterTest(() => connection2.destroy());
51
+ afterTest(() => connection2.close());
51
52
  afterTest(() => connection2.errors.assertNoUnhandledErrors());
52
53
 
54
+ await connection1.open();
55
+ await connection2.open();
56
+
53
57
  await TestStream.assertConnectivity(stream1, stream2);
54
58
  })
55
59
  .timeout(2_000)
@@ -13,16 +13,17 @@ import { log } from '@dxos/log';
13
13
  import { ConnectionResetError, ConnectivityError, ProtocolError, UnknownProtocolError, trace } from '@dxos/protocols';
14
14
  import { type Signal } from '@dxos/protocols/proto/dxos/mesh/swarm';
15
15
 
16
- import { type Transport, type TransportFactory, type TransportStats } from './transport';
16
+ import { type Transport, type TransportFactory, type TransportOptions, type TransportStats } from './transport';
17
17
  import { wrtc } from './webrtc';
18
18
 
19
- export type SimplePeerTransportParams = {
20
- initiator: boolean;
21
- stream: NodeJS.ReadWriteStream;
19
+ export type SimplePeerTransportParams = TransportOptions & {
22
20
  webrtcConfig?: any;
23
- sendSignal: (signal: Signal) => Promise<void>;
24
21
  };
25
22
 
23
+ export const createSimplePeerTransportFactory = (webrtcConfig?: any): TransportFactory => ({
24
+ createTransport: (options) => new SimplePeerTransport({ ...options, webrtcConfig }),
25
+ });
26
+
26
27
  /**
27
28
  * Implements Transport for WebRTC. Uses simple-peer under the hood.
28
29
  */
@@ -37,34 +38,39 @@ export class SimplePeerTransport implements Transport {
37
38
 
38
39
  private readonly _instanceId = PublicKey.random().toHex();
39
40
 
41
+ get isOpen() {
42
+ // TODO(burdon): Open state?
43
+ return this._piped && !this._closed;
44
+ }
45
+
40
46
  /**
41
47
  * @params opts.config formatted as per https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection
42
48
  */
43
- constructor(private readonly params: SimplePeerTransportParams) {
49
+ constructor(private readonly _params: SimplePeerTransportParams) {
44
50
  log.trace('dxos.mesh.webrtc-transport.constructor', trace.begin({ id: this._instanceId }));
45
- log('created connection', params);
51
+ log('created connection', _params);
46
52
  this._peer = new SimplePeerConstructor({
47
53
  channelName: 'dxos.mesh.transport',
48
- initiator: this.params.initiator,
54
+ initiator: this._params.initiator,
49
55
  wrtc: SimplePeerConstructor.WEBRTC_SUPPORT ? undefined : wrtc ?? raise(new Error('wrtc not available')),
50
- config: this.params.webrtcConfig,
56
+ config: this._params.webrtcConfig,
51
57
  });
52
58
 
53
59
  this._peer.on('signal', async (data) => {
54
60
  log('signal', data);
55
- await this.params.sendSignal({ payload: { data } });
61
+ await this._params.sendSignal({ payload: { data } });
56
62
  });
57
63
 
58
64
  this._peer.on('connect', () => {
59
65
  log('connected');
60
- this.params.stream.pipe(this._peer!).pipe(this.params.stream);
66
+ this._params.stream.pipe(this._peer!).pipe(this._params.stream);
61
67
  this._piped = true;
62
68
  this.connected.emit();
63
69
  });
64
70
 
65
71
  this._peer.on('close', async () => {
66
72
  log('closed');
67
- await this.destroy();
73
+ await this.close();
68
74
  });
69
75
 
70
76
  this._peer.on('error', async (err) => {
@@ -114,7 +120,7 @@ export class SimplePeerTransport implements Transport {
114
120
  if (typeof (this._peer as any)?._pc?.getStats === 'function') {
115
121
  (this._peer as any)._pc.getStats().then((stats: any) => {
116
122
  log.info('report after webrtc error', {
117
- config: this.params.webrtcConfig,
123
+ config: this._params.webrtcConfig,
118
124
  stats: Object.fromEntries((stats as any).entries()),
119
125
  });
120
126
  });
@@ -123,7 +129,7 @@ export class SimplePeerTransport implements Transport {
123
129
  log.catch(err);
124
130
  }
125
131
 
126
- await this.destroy();
132
+ await this.close();
127
133
  });
128
134
 
129
135
  log.trace('dxos.mesh.webrtc-transport.constructor', trace.end({ id: this._instanceId }));
@@ -188,7 +194,9 @@ export class SimplePeerTransport implements Transport {
188
194
  return `${rc.ip}:${rc.port}/${rc.protocol} ${rc.candidateType}`;
189
195
  }
190
196
 
191
- async destroy() {
197
+ async open() {}
198
+
199
+ async close() {
192
200
  log('closing...');
193
201
  if (this._closed) {
194
202
  return;
@@ -200,7 +208,7 @@ export class SimplePeerTransport implements Transport {
200
208
  log('closed');
201
209
  }
202
210
 
203
- signal(signal: Signal) {
211
+ async onSignal(signal: Signal) {
204
212
  if (this._closed) {
205
213
  return; // Ignore signals after close.
206
214
  }
@@ -212,15 +220,7 @@ export class SimplePeerTransport implements Transport {
212
220
  private _disconnectStreams() {
213
221
  // TODO(rzadp): Find a way of unpiping this?
214
222
  if (this._piped) {
215
- this.params.stream.unpipe?.(this._peer)?.unpipe?.(this.params.stream);
223
+ this._params.stream.unpipe?.(this._peer)?.unpipe?.(this._params.stream);
216
224
  }
217
225
  }
218
226
  }
219
-
220
- export const createSimplePeerTransportFactory = (webrtcConfig?: any): TransportFactory => ({
221
- createTransport: (params) =>
222
- new SimplePeerTransport({
223
- ...params,
224
- webrtcConfig,
225
- }),
226
- });
@@ -7,22 +7,27 @@ import { ErrorStream } from '@dxos/debug';
7
7
 
8
8
  import { type Transport, type TransportFactory, type TransportStats } from './transport';
9
9
 
10
- // NOTE: Browser stub.
11
-
12
10
  export const TcpTransportFactory: TransportFactory = {
13
11
  createTransport: () => new TcpTransport(),
14
12
  };
15
13
 
14
+ /**
15
+ * NOTE: Browser stub.
16
+ */
16
17
  export class TcpTransport implements Transport {
17
18
  public readonly closed = new Event<void>();
18
19
  public readonly connected = new Event<void>();
19
20
  public readonly errors = new ErrorStream();
20
21
 
21
- async destroy() {
22
- throw new Error('Method not implemented.');
22
+ get isOpen() {
23
+ return true;
23
24
  }
24
25
 
25
- signal() {
26
+ async open() {}
27
+
28
+ async close() {}
29
+
30
+ async onSignal() {
26
31
  throw new Error('Method not implemented.');
27
32
  }
28
33