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