@dxos/edge-client 0.6.14-staging.e15392e → 0.7.0

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 (34) hide show
  1. package/dist/lib/browser/index.mjs +444 -226
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/browser/testing/index.mjs +7 -4
  5. package/dist/lib/browser/testing/index.mjs.map +3 -3
  6. package/dist/lib/node/index.cjs +441 -225
  7. package/dist/lib/node/index.cjs.map +4 -4
  8. package/dist/lib/node/meta.json +1 -1
  9. package/dist/lib/node/testing/index.cjs +7 -4
  10. package/dist/lib/node/testing/index.cjs.map +3 -3
  11. package/dist/lib/node-esm/index.mjs +444 -226
  12. package/dist/lib/node-esm/index.mjs.map +4 -4
  13. package/dist/lib/node-esm/meta.json +1 -1
  14. package/dist/lib/node-esm/testing/index.mjs +7 -4
  15. package/dist/lib/node-esm/testing/index.mjs.map +3 -3
  16. package/dist/types/src/auth.d.ts +1 -1
  17. package/dist/types/src/auth.d.ts.map +1 -1
  18. package/dist/types/src/edge-client.d.ts +21 -18
  19. package/dist/types/src/edge-client.d.ts.map +1 -1
  20. package/dist/types/src/edge-http-client.d.ts.map +1 -1
  21. package/dist/types/src/edge-ws-connection.d.ts +30 -0
  22. package/dist/types/src/edge-ws-connection.d.ts.map +1 -0
  23. package/dist/types/src/persistent-lifecycle.d.ts +7 -5
  24. package/dist/types/src/persistent-lifecycle.d.ts.map +1 -1
  25. package/dist/types/src/testing/test-utils.d.ts +1 -0
  26. package/dist/types/src/testing/test-utils.d.ts.map +1 -1
  27. package/package.json +14 -14
  28. package/src/auth.ts +4 -1
  29. package/src/edge-client.test.ts +101 -14
  30. package/src/edge-client.ts +128 -126
  31. package/src/edge-http-client.ts +4 -1
  32. package/src/edge-ws-connection.ts +148 -0
  33. package/src/persistent-lifecycle.ts +26 -11
  34. package/src/testing/test-utils.ts +3 -0
@@ -10,12 +10,9 @@ import {
10
10
  export * from "@dxos/protocols/buf/dxos/edge/messenger_pb";
11
11
 
12
12
  // packages/core/mesh/edge-client/src/edge-client.ts
13
- import WebSocket from "isomorphic-ws";
14
- import { Trigger, Event, scheduleTaskInterval, scheduleTask, TriggerState } from "@dxos/async";
15
- import { Context, LifecycleState as LifecycleState2, Resource as Resource2 } from "@dxos/context";
16
- import { log as log2 } from "@dxos/log";
17
- import { buf } from "@dxos/protocols/buf";
18
- import { MessageSchema } from "@dxos/protocols/buf/dxos/edge/messenger_pb";
13
+ import { Trigger, scheduleMicroTask, TriggerState } from "@dxos/async";
14
+ import { Resource as Resource3 } from "@dxos/context";
15
+ import { log as log3, logInfo as logInfo2 } from "@dxos/log";
19
16
 
20
17
  // packages/core/mesh/edge-client/src/edge-identity.ts
21
18
  import { invariant } from "@dxos/invariant";
@@ -57,6 +54,204 @@ var handleAuthChallenge = async (failedResponse, identity) => {
57
54
  return schema.getCodecForType("dxos.halo.credentials.Presentation").encode(presentation);
58
55
  };
59
56
 
57
+ // packages/core/mesh/edge-client/src/edge-ws-connection.ts
58
+ import WebSocket from "isomorphic-ws";
59
+ import { scheduleTask, scheduleTaskInterval } from "@dxos/async";
60
+ import { Context, Resource } from "@dxos/context";
61
+ import { invariant as invariant2 } from "@dxos/invariant";
62
+ import { log, logInfo } from "@dxos/log";
63
+ import { buf } from "@dxos/protocols/buf";
64
+ import { MessageSchema } from "@dxos/protocols/buf/dxos/edge/messenger_pb";
65
+ function _ts_decorate(decorators, target, key, desc) {
66
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
67
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
68
+ 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;
69
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
70
+ }
71
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-ws-connection.ts";
72
+ var SIGNAL_KEEPALIVE_INTERVAL = 5e3;
73
+ var EdgeWsConnection = class extends Resource {
74
+ constructor(_identity, _connectionInfo, _callbacks) {
75
+ super();
76
+ this._identity = _identity;
77
+ this._connectionInfo = _connectionInfo;
78
+ this._callbacks = _callbacks;
79
+ }
80
+ get info() {
81
+ return {
82
+ open: this.isOpen,
83
+ identity: this._identity.identityKey,
84
+ device: this._identity.peerKey
85
+ };
86
+ }
87
+ send(message) {
88
+ invariant2(this._ws, void 0, {
89
+ F: __dxlog_file2,
90
+ L: 48,
91
+ S: this,
92
+ A: [
93
+ "this._ws",
94
+ ""
95
+ ]
96
+ });
97
+ log("sending...", {
98
+ peerKey: this._identity.peerKey,
99
+ payload: protocol.getPayloadType(message)
100
+ }, {
101
+ F: __dxlog_file2,
102
+ L: 49,
103
+ S: this,
104
+ C: (f, a) => f(...a)
105
+ });
106
+ this._ws.send(buf.toBinary(MessageSchema, message));
107
+ }
108
+ async _open() {
109
+ this._ws = new WebSocket(this._connectionInfo.url, this._connectionInfo.protocolHeader ? [
110
+ this._connectionInfo.protocolHeader
111
+ ] : []);
112
+ this._ws.onopen = () => {
113
+ if (this.isOpen) {
114
+ log("connected", void 0, {
115
+ F: __dxlog_file2,
116
+ L: 61,
117
+ S: this,
118
+ C: (f, a) => f(...a)
119
+ });
120
+ this._callbacks.onConnected();
121
+ this._scheduleHeartbeats();
122
+ } else {
123
+ log.verbose("connected after becoming inactive", {
124
+ currentIdentity: this._identity
125
+ }, {
126
+ F: __dxlog_file2,
127
+ L: 65,
128
+ S: this,
129
+ C: (f, a) => f(...a)
130
+ });
131
+ }
132
+ };
133
+ this._ws.onclose = () => {
134
+ if (this.isOpen) {
135
+ log("disconnected while being open", void 0, {
136
+ F: __dxlog_file2,
137
+ L: 70,
138
+ S: this,
139
+ C: (f, a) => f(...a)
140
+ });
141
+ this._callbacks.onRestartRequired();
142
+ }
143
+ };
144
+ this._ws.onerror = (event) => {
145
+ if (this.isOpen) {
146
+ log.warn("edge connection socket error", {
147
+ error: event.error,
148
+ info: event.message
149
+ }, {
150
+ F: __dxlog_file2,
151
+ L: 76,
152
+ S: this,
153
+ C: (f, a) => f(...a)
154
+ });
155
+ this._callbacks.onRestartRequired();
156
+ } else {
157
+ log.verbose("error ignored on closed connection", {
158
+ error: event.error
159
+ }, {
160
+ F: __dxlog_file2,
161
+ L: 79,
162
+ S: this,
163
+ C: (f, a) => f(...a)
164
+ });
165
+ }
166
+ };
167
+ this._ws.onmessage = async (event) => {
168
+ if (!this.isOpen) {
169
+ log.verbose("message ignored on closed connection", {
170
+ event: event.type
171
+ }, {
172
+ F: __dxlog_file2,
173
+ L: 87,
174
+ S: this,
175
+ C: (f, a) => f(...a)
176
+ });
177
+ return;
178
+ }
179
+ if (event.data === "__pong__") {
180
+ this._rescheduleHeartbeatTimeout();
181
+ return;
182
+ }
183
+ const data = await toUint8Array(event.data);
184
+ if (this.isOpen) {
185
+ const message = buf.fromBinary(MessageSchema, data);
186
+ log("received", {
187
+ from: message.source,
188
+ payload: protocol.getPayloadType(message)
189
+ }, {
190
+ F: __dxlog_file2,
191
+ L: 97,
192
+ S: this,
193
+ C: (f, a) => f(...a)
194
+ });
195
+ this._callbacks.onMessage(message);
196
+ }
197
+ };
198
+ }
199
+ async _close() {
200
+ void this._inactivityTimeoutCtx?.dispose().catch(() => {
201
+ });
202
+ try {
203
+ this._ws?.close();
204
+ this._ws = void 0;
205
+ } catch (err) {
206
+ if (err instanceof Error && err.message.includes("WebSocket is closed before the connection is established.")) {
207
+ return;
208
+ }
209
+ log.warn("Error closing websocket", {
210
+ err
211
+ }, {
212
+ F: __dxlog_file2,
213
+ L: 113,
214
+ S: this,
215
+ C: (f, a) => f(...a)
216
+ });
217
+ }
218
+ }
219
+ _scheduleHeartbeats() {
220
+ invariant2(this._ws, void 0, {
221
+ F: __dxlog_file2,
222
+ L: 118,
223
+ S: this,
224
+ A: [
225
+ "this._ws",
226
+ ""
227
+ ]
228
+ });
229
+ scheduleTaskInterval(this._ctx, async () => {
230
+ this._ws?.send("__ping__");
231
+ }, SIGNAL_KEEPALIVE_INTERVAL);
232
+ this._ws.send("__ping__");
233
+ this._rescheduleHeartbeatTimeout();
234
+ }
235
+ _rescheduleHeartbeatTimeout() {
236
+ if (!this.isOpen) {
237
+ return;
238
+ }
239
+ void this._inactivityTimeoutCtx?.dispose();
240
+ this._inactivityTimeoutCtx = new Context(void 0, {
241
+ F: __dxlog_file2,
242
+ L: 137
243
+ });
244
+ scheduleTask(this._inactivityTimeoutCtx, () => {
245
+ if (this.isOpen) {
246
+ this._callbacks.onRestartRequired();
247
+ }
248
+ }, 2 * SIGNAL_KEEPALIVE_INTERVAL);
249
+ }
250
+ };
251
+ _ts_decorate([
252
+ logInfo
253
+ ], EdgeWsConnection.prototype, "info", null);
254
+
60
255
  // packages/core/mesh/edge-client/src/errors.ts
61
256
  var EdgeConnectionClosedError = class extends Error {
62
257
  constructor() {
@@ -71,21 +266,22 @@ var EdgeIdentityChangedError = class extends Error {
71
266
 
72
267
  // packages/core/mesh/edge-client/src/persistent-lifecycle.ts
73
268
  import { DeferredTask, sleep, synchronized } from "@dxos/async";
74
- import { cancelWithContext, LifecycleState, Resource } from "@dxos/context";
269
+ import { cancelWithContext, LifecycleState, Resource as Resource2 } from "@dxos/context";
75
270
  import { warnAfterTimeout } from "@dxos/debug";
76
- import { log } from "@dxos/log";
77
- function _ts_decorate(decorators, target, key, desc) {
271
+ import { log as log2 } from "@dxos/log";
272
+ function _ts_decorate2(decorators, target, key, desc) {
78
273
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
79
274
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
80
275
  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;
81
276
  return c > 3 && r && Object.defineProperty(target, key, r), r;
82
277
  }
83
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/persistent-lifecycle.ts";
278
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/persistent-lifecycle.ts";
84
279
  var INIT_RESTART_DELAY = 100;
85
280
  var DEFAULT_MAX_RESTART_DELAY = 5e3;
86
- var PersistentLifecycle = class extends Resource {
281
+ var PersistentLifecycle = class extends Resource2 {
87
282
  constructor({ start, stop, onRestart, maxRestartDelay = DEFAULT_MAX_RESTART_DELAY }) {
88
283
  super();
284
+ this._currentContext = void 0;
89
285
  this._restartTask = void 0;
90
286
  this._restartAfter = 0;
91
287
  this._start = start;
@@ -98,53 +294,71 @@ var PersistentLifecycle = class extends Resource {
98
294
  try {
99
295
  await this._restart();
100
296
  } catch (err) {
101
- log.warn("Restart failed", {
297
+ log2.warn("Restart failed", {
102
298
  err
103
299
  }, {
104
- F: __dxlog_file2,
105
- L: 64,
300
+ F: __dxlog_file3,
301
+ L: 65,
106
302
  S: this,
107
303
  C: (f, a) => f(...a)
108
304
  });
109
305
  this._restartTask?.schedule();
110
306
  }
111
307
  });
112
- await this._start().catch((err) => {
113
- log.warn("Start failed", {
308
+ this._currentContext = await this._start().catch((err) => {
309
+ log2.warn("Start failed", {
114
310
  err
115
311
  }, {
116
- F: __dxlog_file2,
117
- L: 69,
312
+ F: __dxlog_file3,
313
+ L: 70,
118
314
  S: this,
119
315
  C: (f, a) => f(...a)
120
316
  });
121
317
  this._restartTask?.schedule();
318
+ return void 0;
122
319
  });
123
320
  }
124
321
  async _close() {
125
322
  await this._restartTask?.join();
126
- await this._stop();
323
+ await this._stopCurrentContext();
127
324
  this._restartTask = void 0;
128
325
  }
129
326
  async _restart() {
130
- log(`restarting in ${this._restartAfter}ms`, {
327
+ log2(`restarting in ${this._restartAfter}ms`, {
131
328
  state: this._lifecycleState
132
329
  }, {
133
- F: __dxlog_file2,
134
- L: 81,
330
+ F: __dxlog_file3,
331
+ L: 83,
135
332
  S: this,
136
333
  C: (f, a) => f(...a)
137
334
  });
138
- await this._stop();
335
+ await this._stopCurrentContext();
139
336
  if (this._lifecycleState !== LifecycleState.OPEN) {
140
337
  return;
141
338
  }
142
339
  await cancelWithContext(this._ctx, sleep(this._restartAfter));
143
340
  this._restartAfter = Math.min(Math.max(this._restartAfter * 2, INIT_RESTART_DELAY), this._maxRestartDelay);
144
- await warnAfterTimeout(5e3, "Connection establishment takes too long", () => this._start());
341
+ await warnAfterTimeout(5e3, "Connection establishment takes too long", async () => {
342
+ this._currentContext = await this._start();
343
+ });
145
344
  this._restartAfter = 0;
146
345
  await this._onRestart?.();
147
346
  }
347
+ async _stopCurrentContext() {
348
+ if (this._currentContext) {
349
+ try {
350
+ await this._stop(this._currentContext);
351
+ } catch (err) {
352
+ log2.catch(err, void 0, {
353
+ F: __dxlog_file3,
354
+ L: 105,
355
+ S: this,
356
+ C: (f, a) => f(...a)
357
+ });
358
+ }
359
+ this._currentContext = void 0;
360
+ }
361
+ }
148
362
  /**
149
363
  * Scheduling restart should be done from outside.
150
364
  */
@@ -155,10 +369,10 @@ var PersistentLifecycle = class extends Resource {
155
369
  this._restartTask.schedule();
156
370
  }
157
371
  };
158
- _ts_decorate([
372
+ _ts_decorate2([
159
373
  synchronized
160
374
  ], PersistentLifecycle.prototype, "_open", null);
161
- _ts_decorate([
375
+ _ts_decorate2([
162
376
  synchronized
163
377
  ], PersistentLifecycle.prototype, "scheduleRestart", null);
164
378
 
@@ -171,30 +385,31 @@ var getEdgeUrlWithProtocol = (baseUrl, protocol2) => {
171
385
  };
172
386
 
173
387
  // packages/core/mesh/edge-client/src/edge-client.ts
174
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-client.ts";
388
+ function _ts_decorate3(decorators, target, key, desc) {
389
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
390
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
391
+ 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;
392
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
393
+ }
394
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-client.ts";
175
395
  var DEFAULT_TIMEOUT = 1e4;
176
- var SIGNAL_KEEPALIVE_INTERVAL = 5e3;
177
- var EdgeClient = class extends Resource2 {
396
+ var EdgeClient = class extends Resource3 {
178
397
  constructor(_identity, _config) {
179
398
  super();
180
399
  this._identity = _identity;
181
400
  this._config = _config;
182
- this.reconnect = new Event();
183
- this.connected = new Event();
184
401
  this._persistentLifecycle = new PersistentLifecycle({
185
- start: async () => this._openWebSocket(),
186
- stop: async () => this._closeWebSocket(),
187
- onRestart: async () => this.reconnect.emit()
402
+ start: async () => this._connect(),
403
+ stop: async (state) => this._disconnect(state)
188
404
  });
189
- this._listeners = /* @__PURE__ */ new Set();
405
+ this._messageListeners = /* @__PURE__ */ new Set();
406
+ this._reconnectListeners = /* @__PURE__ */ new Set();
407
+ this._currentConnection = void 0;
190
408
  this._ready = new Trigger();
191
- this._ws = void 0;
192
- this._keepaliveCtx = void 0;
193
- this._heartBeatContext = void 0;
409
+ this._isActive = (connection) => connection === this._currentConnection;
194
410
  this._baseWsUrl = getEdgeUrlWithProtocol(_config.socketEndpoint, "ws");
195
411
  this._baseHttpUrl = getEdgeUrlWithProtocol(_config.socketEndpoint, "http");
196
412
  }
197
- // TODO(burdon): Attach logging.
198
413
  get info() {
199
414
  return {
200
415
  open: this.isOpen,
@@ -203,7 +418,7 @@ var EdgeClient = class extends Resource2 {
203
418
  };
204
419
  }
205
420
  get isConnected() {
206
- return Boolean(this._ws) && this._ready.state === TriggerState.RESOLVED;
421
+ return Boolean(this._currentConnection) && this._ready.state === TriggerState.RESOLVED;
207
422
  }
208
423
  get identityKey() {
209
424
  return this._identity.identityKey;
@@ -213,41 +428,62 @@ var EdgeClient = class extends Resource2 {
213
428
  }
214
429
  setIdentity(identity) {
215
430
  if (identity.identityKey !== this._identity.identityKey || identity.peerKey !== this._identity.peerKey) {
216
- log2("Edge identity changed", {
431
+ log3("Edge identity changed", {
217
432
  identity,
218
433
  oldIdentity: this._identity
219
434
  }, {
220
- F: __dxlog_file3,
221
- L: 99,
435
+ F: __dxlog_file4,
436
+ L: 95,
222
437
  S: this,
223
438
  C: (f, a) => f(...a)
224
439
  });
225
440
  this._identity = identity;
441
+ this._closeCurrentConnection(new EdgeIdentityChangedError());
226
442
  this._persistentLifecycle.scheduleRestart();
227
443
  }
228
444
  }
229
- addListener(listener) {
230
- this._listeners.add(listener);
231
- return () => this._listeners.delete(listener);
445
+ onMessage(listener) {
446
+ this._messageListeners.add(listener);
447
+ return () => this._messageListeners.delete(listener);
448
+ }
449
+ onReconnected(listener) {
450
+ this._reconnectListeners.add(listener);
451
+ if (this._ready.state === TriggerState.RESOLVED) {
452
+ scheduleMicroTask(this._ctx, () => {
453
+ if (this._reconnectListeners.has(listener)) {
454
+ try {
455
+ listener();
456
+ } catch (error) {
457
+ log3.catch(error, void 0, {
458
+ F: __dxlog_file4,
459
+ L: 117,
460
+ S: this,
461
+ C: (f, a) => f(...a)
462
+ });
463
+ }
464
+ }
465
+ });
466
+ }
467
+ return () => this._reconnectListeners.delete(listener);
232
468
  }
233
469
  /**
234
470
  * Open connection to messaging service.
235
471
  */
236
472
  async _open() {
237
- log2("opening...", {
473
+ log3("opening...", {
238
474
  info: this.info
239
475
  }, {
240
- F: __dxlog_file3,
241
- L: 114,
476
+ F: __dxlog_file4,
477
+ L: 129,
242
478
  S: this,
243
479
  C: (f, a) => f(...a)
244
480
  });
245
481
  this._persistentLifecycle.open().catch((err) => {
246
- log2.warn("Error while opening connection", {
482
+ log3.warn("Error while opening connection", {
247
483
  err
248
484
  }, {
249
- F: __dxlog_file3,
250
- L: 116,
485
+ F: __dxlog_file4,
486
+ L: 131,
251
487
  S: this,
252
488
  C: (f, a) => f(...a)
253
489
  });
@@ -257,153 +493,140 @@ var EdgeClient = class extends Resource2 {
257
493
  * Close connection and free resources.
258
494
  */
259
495
  async _close() {
260
- log2("closing...", {
496
+ log3("closing...", {
261
497
  peerKey: this._identity.peerKey
262
498
  }, {
263
- F: __dxlog_file3,
264
- L: 124,
499
+ F: __dxlog_file4,
500
+ L: 139,
265
501
  S: this,
266
502
  C: (f, a) => f(...a)
267
503
  });
504
+ this._closeCurrentConnection();
268
505
  await this._persistentLifecycle.close();
269
506
  }
270
- async _openWebSocket() {
507
+ async _connect() {
271
508
  if (this._ctx.disposed) {
272
- return;
509
+ return void 0;
273
510
  }
274
- const path = `/ws/${this._identity.identityKey}/${this._identity.peerKey}`;
511
+ const identity = this._identity;
512
+ const path = `/ws/${identity.identityKey}/${identity.peerKey}`;
275
513
  const protocolHeader = this._config.disableAuth ? void 0 : await this._createAuthHeader(path);
514
+ if (this._identity !== identity) {
515
+ log3("identity changed during auth header request", void 0, {
516
+ F: __dxlog_file4,
517
+ L: 153,
518
+ S: this,
519
+ C: (f, a) => f(...a)
520
+ });
521
+ return void 0;
522
+ }
523
+ const restartRequired = new Trigger();
276
524
  const url = new URL(path, this._baseWsUrl);
277
- log2("Opening websocket", {
525
+ log3("Opening websocket", {
278
526
  url: url.toString(),
279
527
  protocolHeader
280
528
  }, {
281
- F: __dxlog_file3,
282
- L: 136,
529
+ F: __dxlog_file4,
530
+ L: 159,
283
531
  S: this,
284
532
  C: (f, a) => f(...a)
285
533
  });
286
- this._ws = new WebSocket(url, protocolHeader ? [
534
+ const connection = new EdgeWsConnection(identity, {
535
+ url,
287
536
  protocolHeader
288
- ] : []);
289
- this._ws.onopen = () => {
290
- log2("opened", this.info, {
291
- F: __dxlog_file3,
292
- L: 140,
293
- S: this,
294
- C: (f, a) => f(...a)
295
- });
296
- this._ready.wake();
297
- this.connected.emit();
298
- };
299
- this._ws.onclose = () => {
300
- log2("closed", this.info, {
301
- F: __dxlog_file3,
302
- L: 145,
303
- S: this,
304
- C: (f, a) => f(...a)
305
- });
306
- this._persistentLifecycle.scheduleRestart();
307
- };
308
- this._ws.onerror = (event) => {
309
- log2.warn("EdgeClient socket error", {
310
- error: event.error,
311
- info: event.message
312
- }, {
313
- F: __dxlog_file3,
314
- L: 149,
315
- S: this,
316
- C: (f, a) => f(...a)
317
- });
318
- this._persistentLifecycle.scheduleRestart();
319
- };
320
- this._ws.onmessage = async (event) => {
321
- if (event.data === "__pong__") {
322
- this._onHeartbeat();
323
- return;
324
- }
325
- const data = await toUint8Array(event.data);
326
- const message = buf.fromBinary(MessageSchema, data);
327
- log2("received", {
328
- peerKey: this._identity.peerKey,
329
- payload: protocol.getPayloadType(message)
330
- }, {
331
- F: __dxlog_file3,
332
- L: 162,
333
- S: this,
334
- C: (f, a) => f(...a)
335
- });
336
- if (message) {
337
- for (const listener of this._listeners) {
338
- try {
339
- await listener(message);
340
- } catch (err) {
341
- log2.error("processing", {
342
- err,
343
- payload: protocol.getPayloadType(message)
344
- }, {
345
- F: __dxlog_file3,
346
- L: 168,
347
- S: this,
348
- C: (f, a) => f(...a)
349
- });
350
- }
537
+ }, {
538
+ onConnected: () => {
539
+ if (this._isActive(connection)) {
540
+ this._ready.wake();
541
+ this._notifyReconnected();
542
+ } else {
543
+ log3.verbose("connected callback ignored, because connection is not active", void 0, {
544
+ F: __dxlog_file4,
545
+ L: 169,
546
+ S: this,
547
+ C: (f, a) => f(...a)
548
+ });
549
+ }
550
+ },
551
+ onRestartRequired: () => {
552
+ if (this._isActive(connection)) {
553
+ this._closeCurrentConnection();
554
+ this._persistentLifecycle.scheduleRestart();
555
+ } else {
556
+ log3.verbose("restart requested by inactive connection", void 0, {
557
+ F: __dxlog_file4,
558
+ L: 177,
559
+ S: this,
560
+ C: (f, a) => f(...a)
561
+ });
562
+ }
563
+ restartRequired.wake();
564
+ },
565
+ onMessage: (message) => {
566
+ if (this._isActive(connection)) {
567
+ this._notifyMessageReceived(message);
568
+ } else {
569
+ log3.verbose("ignored a message on inactive connection", {
570
+ from: message.source,
571
+ type: message.payload?.typeUrl
572
+ }, {
573
+ F: __dxlog_file4,
574
+ L: 185,
575
+ S: this,
576
+ C: (f, a) => f(...a)
577
+ });
351
578
  }
352
579
  }
353
- };
354
- await this._ready.wait({
355
- timeout: this._config.timeout ?? DEFAULT_TIMEOUT
356
580
  });
357
- log2("Websocket is ready", {
358
- identity: this._identity.identityKey,
359
- peer: this._identity.peerKey
360
- }, {
361
- F: __dxlog_file3,
362
- L: 176,
363
- S: this,
364
- C: (f, a) => f(...a)
365
- });
366
- this._keepaliveCtx = new Context(void 0, {
367
- F: __dxlog_file3,
368
- L: 179
369
- });
370
- scheduleTaskInterval(this._keepaliveCtx, async () => {
371
- this._ws?.send("__ping__");
372
- }, SIGNAL_KEEPALIVE_INTERVAL);
373
- this._ws.send("__ping__");
374
- this._onHeartbeat();
581
+ this._currentConnection = connection;
582
+ await connection.open();
583
+ await Promise.race([
584
+ this._ready.wait({
585
+ timeout: this._config.timeout ?? DEFAULT_TIMEOUT
586
+ }),
587
+ restartRequired
588
+ ]);
589
+ return connection;
375
590
  }
376
- async _closeWebSocket() {
377
- if (!this._ws) {
378
- return;
591
+ async _disconnect(state) {
592
+ await state.close();
593
+ }
594
+ _closeCurrentConnection(error = new EdgeConnectionClosedError()) {
595
+ this._currentConnection = void 0;
596
+ this._ready.throw(error);
597
+ this._ready.reset();
598
+ }
599
+ _notifyReconnected() {
600
+ for (const listener of this._reconnectListeners) {
601
+ try {
602
+ listener();
603
+ } catch (err) {
604
+ log3.error("ws reconnect listener failed", {
605
+ err
606
+ }, {
607
+ F: __dxlog_file4,
608
+ L: 218,
609
+ S: this,
610
+ C: (f, a) => f(...a)
611
+ });
612
+ }
379
613
  }
380
- try {
381
- this._ready.throw(this.isOpen ? new EdgeIdentityChangedError() : new EdgeConnectionClosedError());
382
- this._ready.reset();
383
- void this._keepaliveCtx?.dispose();
384
- this._keepaliveCtx = void 0;
385
- void this._heartBeatContext?.dispose();
386
- this._heartBeatContext = void 0;
387
- this._ws.onopen = () => {
388
- };
389
- this._ws.onclose = () => {
390
- };
391
- this._ws.onerror = () => {
392
- };
393
- this._ws.close();
394
- this._ws = void 0;
395
- } catch (err) {
396
- if (err instanceof Error && err.message.includes("WebSocket is closed before the connection is established.")) {
397
- return;
614
+ }
615
+ _notifyMessageReceived(message) {
616
+ for (const listener of this._messageListeners) {
617
+ try {
618
+ listener(message);
619
+ } catch (err) {
620
+ log3.error("ws incoming message processing failed", {
621
+ err,
622
+ payload: protocol.getPayloadType(message)
623
+ }, {
624
+ F: __dxlog_file4,
625
+ L: 228,
626
+ S: this,
627
+ C: (f, a) => f(...a)
628
+ });
398
629
  }
399
- log2.warn("Error closing websocket", {
400
- err
401
- }, {
402
- F: __dxlog_file3,
403
- L: 215,
404
- S: this,
405
- C: (f, a) => f(...a)
406
- });
407
630
  }
408
631
  }
409
632
  /**
@@ -412,9 +635,9 @@ var EdgeClient = class extends Resource2 {
412
635
  */
413
636
  async send(message) {
414
637
  if (this._ready.state !== TriggerState.RESOLVED) {
415
- log2("waiting for websocket to become ready", void 0, {
416
- F: __dxlog_file3,
417
- L: 225,
638
+ log3("waiting for websocket to become ready", void 0, {
639
+ F: __dxlog_file4,
640
+ L: 239,
418
641
  S: this,
419
642
  C: (f, a) => f(...a)
420
643
  });
@@ -422,35 +645,13 @@ var EdgeClient = class extends Resource2 {
422
645
  timeout: this._config.timeout ?? DEFAULT_TIMEOUT
423
646
  });
424
647
  }
425
- if (!this._ws) {
648
+ if (!this._currentConnection) {
426
649
  throw new EdgeConnectionClosedError();
427
650
  }
428
651
  if (message.source && (message.source.peerKey !== this._identity.peerKey || message.source.identityKey !== this.identityKey)) {
429
652
  throw new EdgeIdentityChangedError();
430
653
  }
431
- log2("sending...", {
432
- peerKey: this._identity.peerKey,
433
- payload: protocol.getPayloadType(message)
434
- }, {
435
- F: __dxlog_file3,
436
- L: 238,
437
- S: this,
438
- C: (f, a) => f(...a)
439
- });
440
- this._ws.send(buf.toBinary(MessageSchema, message));
441
- }
442
- _onHeartbeat() {
443
- if (this._lifecycleState !== LifecycleState2.OPEN) {
444
- return;
445
- }
446
- void this._heartBeatContext?.dispose();
447
- this._heartBeatContext = new Context(void 0, {
448
- F: __dxlog_file3,
449
- L: 247
450
- });
451
- scheduleTask(this._heartBeatContext, () => {
452
- this._persistentLifecycle.scheduleRestart();
453
- }, 2 * SIGNAL_KEEPALIVE_INTERVAL);
654
+ this._currentConnection.send(message);
454
655
  }
455
656
  async _createAuthHeader(path) {
456
657
  const httpUrl = new URL(path, this._baseHttpUrl);
@@ -461,11 +662,11 @@ var EdgeClient = class extends Resource2 {
461
662
  if (response.status === 401) {
462
663
  return encodePresentationWsAuthHeader(await handleAuthChallenge(response, this._identity));
463
664
  } else {
464
- log2.warn("no auth challenge from edge", {
665
+ log3.warn("no auth challenge from edge", {
465
666
  status: response.status,
466
667
  statusText: response.statusText
467
668
  }, {
468
- F: __dxlog_file3,
669
+ F: __dxlog_file4,
469
670
  L: 264,
470
671
  S: this,
471
672
  C: (f, a) => f(...a)
@@ -474,6 +675,9 @@ var EdgeClient = class extends Resource2 {
474
675
  }
475
676
  }
476
677
  };
678
+ _ts_decorate3([
679
+ logInfo2
680
+ ], EdgeClient.prototype, "info", null);
477
681
  var encodePresentationWsAuthHeader = (encodedPresentation) => {
478
682
  const encodedToken = Buffer.from(encodedPresentation).toString("base64").replace(/=*$/, "").replaceAll("/", "|");
479
683
  return `base64url.bearer.authorization.dxos.org.${encodedToken}`;
@@ -481,8 +685,10 @@ var encodePresentationWsAuthHeader = (encodedPresentation) => {
481
685
 
482
686
  // packages/core/mesh/edge-client/src/auth.ts
483
687
  import { createCredential, signPresentation } from "@dxos/credentials";
688
+ import { invariant as invariant3 } from "@dxos/invariant";
484
689
  import { Keyring } from "@dxos/keyring";
485
690
  import { PublicKey } from "@dxos/keys";
691
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/auth.ts";
486
692
  var createDeviceEdgeIdentity = async (signer, key) => {
487
693
  return {
488
694
  identityKey: key.toHex(),
@@ -526,6 +732,15 @@ var createChainEdgeIdentity = async (signer, identityKey, peerKey, chain, creden
526
732
  identityKey: identityKey.toHex(),
527
733
  peerKey: peerKey.toHex(),
528
734
  presentCredentials: async ({ challenge }) => {
735
+ invariant3(chain, void 0, {
736
+ F: __dxlog_file5,
737
+ L: 75,
738
+ S: void 0,
739
+ A: [
740
+ "chain",
741
+ ""
742
+ ]
743
+ });
529
744
  return signPresentation({
530
745
  presentation: {
531
746
  credentials: credentialsToSign
@@ -582,26 +797,29 @@ var createStubEdgeIdentity = () => {
582
797
  // packages/core/mesh/edge-client/src/edge-http-client.ts
583
798
  import { sleep as sleep2 } from "@dxos/async";
584
799
  import { Context as Context2 } from "@dxos/context";
585
- import { log as log3 } from "@dxos/log";
800
+ import { log as log4 } from "@dxos/log";
586
801
  import { EdgeCallFailedError, EdgeAuthChallengeError } from "@dxos/protocols";
587
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-http-client.ts";
802
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-http-client.ts";
588
803
  var DEFAULT_RETRY_TIMEOUT = 1500;
589
804
  var DEFAULT_RETRY_JITTER = 500;
590
805
  var DEFAULT_MAX_RETRIES_COUNT = 3;
591
806
  var EdgeHttpClient = class {
592
807
  constructor(baseUrl) {
593
808
  this._baseUrl = getEdgeUrlWithProtocol(baseUrl, "http");
594
- log3("created", {
809
+ log4("created", {
595
810
  url: this._baseUrl
596
811
  }, {
597
- F: __dxlog_file4,
812
+ F: __dxlog_file6,
598
813
  L: 42,
599
814
  S: this,
600
815
  C: (f, a) => f(...a)
601
816
  });
602
817
  }
603
818
  setIdentity(identity) {
604
- this._edgeIdentity = identity;
819
+ if (this._edgeIdentity?.identityKey !== identity.identityKey || this._edgeIdentity?.peerKey !== identity.peerKey) {
820
+ this._edgeIdentity = identity;
821
+ this._authHeader = void 0;
822
+ }
605
823
  }
606
824
  createAgent(body, args) {
607
825
  return this._call("/agents/create", {
@@ -645,18 +863,18 @@ var EdgeHttpClient = class {
645
863
  }
646
864
  async _call(path, args) {
647
865
  const requestContext = args.context ?? new Context2(void 0, {
648
- F: __dxlog_file4,
649
- L: 88
866
+ F: __dxlog_file6,
867
+ L: 91
650
868
  });
651
869
  const shouldRetry = createRetryHandler(args);
652
870
  const url = `${this._baseUrl}${path.startsWith("/") ? path.slice(1) : path}`;
653
- log3.info("call", {
871
+ log4.info("call", {
654
872
  method: args.method,
655
873
  path,
656
874
  request: args.body
657
875
  }, {
658
- F: __dxlog_file4,
659
- L: 92,
876
+ F: __dxlog_file6,
877
+ L: 95,
660
878
  S: this,
661
879
  C: (f, a) => f(...a)
662
880
  });
@@ -674,12 +892,12 @@ var EdgeHttpClient = class {
674
892
  if (body.success) {
675
893
  return body.data;
676
894
  }
677
- log3.info("unsuccessful edge response", {
895
+ log4.info("unsuccessful edge response", {
678
896
  path,
679
897
  body
680
898
  }, {
681
- F: __dxlog_file4,
682
- L: 111,
899
+ F: __dxlog_file6,
900
+ L: 114,
683
901
  S: this,
684
902
  C: (f, a) => f(...a)
685
903
  });
@@ -699,12 +917,12 @@ var EdgeHttpClient = class {
699
917
  processingError = EdgeCallFailedError.fromProcessingFailureCause(error);
700
918
  }
701
919
  if (processingError.isRetryable && await shouldRetry(requestContext, retryAfterHeaderValue)) {
702
- log3.info("retrying edge request", {
920
+ log4.info("retrying edge request", {
703
921
  path,
704
922
  processingError
705
923
  }, {
706
- F: __dxlog_file4,
707
- L: 130,
924
+ F: __dxlog_file6,
925
+ L: 133,
708
926
  S: this,
709
927
  C: (f, a) => f(...a)
710
928
  });
@@ -715,9 +933,9 @@ var EdgeHttpClient = class {
715
933
  }
716
934
  async _handleUnauthorized(response) {
717
935
  if (!this._edgeIdentity) {
718
- log3.warn("edge unauthorized response received before identity was set", void 0, {
719
- F: __dxlog_file4,
720
- L: 139,
936
+ log4.warn("edge unauthorized response received before identity was set", void 0, {
937
+ F: __dxlog_file6,
938
+ L: 142,
721
939
  S: this,
722
940
  C: (f, a) => f(...a)
723
941
  });
@@ -725,9 +943,9 @@ var EdgeHttpClient = class {
725
943
  }
726
944
  const challenge = await handleAuthChallenge(response, this._edgeIdentity);
727
945
  this._authHeader = encodeAuthHeader(challenge);
728
- log3("auth header updated", void 0, {
729
- F: __dxlog_file4,
730
- L: 144,
946
+ log4("auth header updated", void 0, {
947
+ F: __dxlog_file6,
948
+ L: 147,
731
949
  S: this,
732
950
  C: (f, a) => f(...a)
733
951
  });