@dxos/edge-client 0.6.14-main.f49f251 → 0.6.14-staging.54a8bab

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.
@@ -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}`;
@@ -581,26 +785,29 @@ var createStubEdgeIdentity = () => {
581
785
  // packages/core/mesh/edge-client/src/edge-http-client.ts
582
786
  import { sleep as sleep2 } from "@dxos/async";
583
787
  import { Context as Context2 } from "@dxos/context";
584
- import { log as log3 } from "@dxos/log";
788
+ import { log as log4 } from "@dxos/log";
585
789
  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";
790
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-http-client.ts";
587
791
  var DEFAULT_RETRY_TIMEOUT = 1500;
588
792
  var DEFAULT_RETRY_JITTER = 500;
589
793
  var DEFAULT_MAX_RETRIES_COUNT = 3;
590
794
  var EdgeHttpClient = class {
591
795
  constructor(baseUrl) {
592
796
  this._baseUrl = getEdgeUrlWithProtocol(baseUrl, "http");
593
- log3("created", {
797
+ log4("created", {
594
798
  url: this._baseUrl
595
799
  }, {
596
- F: __dxlog_file4,
800
+ F: __dxlog_file5,
597
801
  L: 42,
598
802
  S: this,
599
803
  C: (f, a) => f(...a)
600
804
  });
601
805
  }
602
806
  setIdentity(identity) {
603
- this._edgeIdentity = identity;
807
+ if (this._edgeIdentity?.identityKey !== identity.identityKey || this._edgeIdentity?.peerKey !== identity.peerKey) {
808
+ this._edgeIdentity = identity;
809
+ this._authHeader = void 0;
810
+ }
604
811
  }
605
812
  createAgent(body, args) {
606
813
  return this._call("/agents/create", {
@@ -644,18 +851,18 @@ var EdgeHttpClient = class {
644
851
  }
645
852
  async _call(path, args) {
646
853
  const requestContext = args.context ?? new Context2(void 0, {
647
- F: __dxlog_file4,
648
- L: 88
854
+ F: __dxlog_file5,
855
+ L: 91
649
856
  });
650
857
  const shouldRetry = createRetryHandler(args);
651
858
  const url = `${this._baseUrl}${path.startsWith("/") ? path.slice(1) : path}`;
652
- log3.info("call", {
859
+ log4.info("call", {
653
860
  method: args.method,
654
861
  path,
655
862
  request: args.body
656
863
  }, {
657
- F: __dxlog_file4,
658
- L: 92,
864
+ F: __dxlog_file5,
865
+ L: 95,
659
866
  S: this,
660
867
  C: (f, a) => f(...a)
661
868
  });
@@ -673,12 +880,12 @@ var EdgeHttpClient = class {
673
880
  if (body.success) {
674
881
  return body.data;
675
882
  }
676
- log3.info("unsuccessful edge response", {
883
+ log4.info("unsuccessful edge response", {
677
884
  path,
678
885
  body
679
886
  }, {
680
- F: __dxlog_file4,
681
- L: 111,
887
+ F: __dxlog_file5,
888
+ L: 114,
682
889
  S: this,
683
890
  C: (f, a) => f(...a)
684
891
  });
@@ -698,12 +905,12 @@ var EdgeHttpClient = class {
698
905
  processingError = EdgeCallFailedError.fromProcessingFailureCause(error);
699
906
  }
700
907
  if (processingError.isRetryable && await shouldRetry(requestContext, retryAfterHeaderValue)) {
701
- log3.info("retrying edge request", {
908
+ log4.info("retrying edge request", {
702
909
  path,
703
910
  processingError
704
911
  }, {
705
- F: __dxlog_file4,
706
- L: 130,
912
+ F: __dxlog_file5,
913
+ L: 133,
707
914
  S: this,
708
915
  C: (f, a) => f(...a)
709
916
  });
@@ -714,9 +921,9 @@ var EdgeHttpClient = class {
714
921
  }
715
922
  async _handleUnauthorized(response) {
716
923
  if (!this._edgeIdentity) {
717
- log3.warn("edge unauthorized response received before identity was set", void 0, {
718
- F: __dxlog_file4,
719
- L: 139,
924
+ log4.warn("edge unauthorized response received before identity was set", void 0, {
925
+ F: __dxlog_file5,
926
+ L: 142,
720
927
  S: this,
721
928
  C: (f, a) => f(...a)
722
929
  });
@@ -724,9 +931,9 @@ var EdgeHttpClient = class {
724
931
  }
725
932
  const challenge = await handleAuthChallenge(response, this._edgeIdentity);
726
933
  this._authHeader = encodeAuthHeader(challenge);
727
- log3("auth header updated", void 0, {
728
- F: __dxlog_file4,
729
- L: 144,
934
+ log4("auth header updated", void 0, {
935
+ F: __dxlog_file5,
936
+ L: 147,
730
937
  S: this,
731
938
  C: (f, a) => f(...a)
732
939
  });