@dxos/edge-client 0.6.13 → 0.6.14-main.1366248

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 (67) hide show
  1. package/dist/lib/browser/chunk-ZWJXA37R.mjs +113 -0
  2. package/dist/lib/browser/chunk-ZWJXA37R.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +802 -286
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +128 -0
  7. package/dist/lib/browser/testing/index.mjs.map +7 -0
  8. package/dist/lib/node/chunk-ANV2HBEH.cjs +136 -0
  9. package/dist/lib/node/chunk-ANV2HBEH.cjs.map +7 -0
  10. package/dist/lib/node/index.cjs +798 -283
  11. package/dist/lib/node/index.cjs.map +4 -4
  12. package/dist/lib/node/meta.json +1 -1
  13. package/dist/lib/node/testing/index.cjs +158 -0
  14. package/dist/lib/node/testing/index.cjs.map +7 -0
  15. package/dist/lib/node-esm/chunk-HNVT57AU.mjs +115 -0
  16. package/dist/lib/node-esm/chunk-HNVT57AU.mjs.map +7 -0
  17. package/dist/lib/node-esm/index.mjs +994 -0
  18. package/dist/lib/node-esm/index.mjs.map +7 -0
  19. package/dist/lib/node-esm/meta.json +1 -0
  20. package/dist/lib/node-esm/testing/index.mjs +129 -0
  21. package/dist/lib/node-esm/testing/index.mjs.map +7 -0
  22. package/dist/types/src/auth.d.ts +22 -0
  23. package/dist/types/src/auth.d.ts.map +1 -0
  24. package/dist/types/src/defs.d.ts.map +1 -1
  25. package/dist/types/src/edge-client.d.ts +30 -26
  26. package/dist/types/src/edge-client.d.ts.map +1 -1
  27. package/dist/types/src/edge-http-client.d.ts +48 -0
  28. package/dist/types/src/edge-http-client.d.ts.map +1 -0
  29. package/dist/types/src/edge-identity.d.ts +15 -0
  30. package/dist/types/src/edge-identity.d.ts.map +1 -0
  31. package/dist/types/src/edge-ws-connection.d.ts +30 -0
  32. package/dist/types/src/edge-ws-connection.d.ts.map +1 -0
  33. package/dist/types/src/errors.d.ts +4 -1
  34. package/dist/types/src/errors.d.ts.map +1 -1
  35. package/dist/types/src/index.d.ts +4 -0
  36. package/dist/types/src/index.d.ts.map +1 -1
  37. package/dist/types/src/persistent-lifecycle.d.ts +7 -5
  38. package/dist/types/src/persistent-lifecycle.d.ts.map +1 -1
  39. package/dist/types/src/protocol.d.ts +2 -2
  40. package/dist/types/src/protocol.d.ts.map +1 -1
  41. package/dist/types/src/testing/index.d.ts +2 -0
  42. package/dist/types/src/testing/index.d.ts.map +1 -0
  43. package/dist/types/src/testing/test-utils.d.ts +22 -0
  44. package/dist/types/src/testing/test-utils.d.ts.map +1 -0
  45. package/dist/types/src/utils.d.ts +2 -0
  46. package/dist/types/src/utils.d.ts.map +1 -0
  47. package/package.json +27 -17
  48. package/src/auth.ts +135 -0
  49. package/src/defs.ts +2 -3
  50. package/src/edge-client.test.ts +144 -25
  51. package/src/edge-client.ts +181 -127
  52. package/src/edge-http-client.ts +213 -0
  53. package/src/edge-identity.ts +31 -0
  54. package/src/edge-ws-connection.ts +148 -0
  55. package/src/errors.ts +8 -2
  56. package/src/index.ts +4 -0
  57. package/src/persistent-lifecycle.test.ts +2 -2
  58. package/src/persistent-lifecycle.ts +26 -11
  59. package/src/protocol.test.ts +1 -2
  60. package/src/protocol.ts +2 -2
  61. package/src/testing/index.ts +5 -0
  62. package/src/testing/test-utils.ts +117 -0
  63. package/src/utils.ts +10 -0
  64. package/src/websocket.test.ts +5 -4
  65. package/dist/types/src/test-utils.d.ts +0 -11
  66. package/dist/types/src/test-utils.d.ts.map +0 -1
  67. package/src/test-utils.ts +0 -49
@@ -30,143 +30,294 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  var node_exports = {};
31
31
  __export(node_exports, {
32
32
  EdgeClient: () => EdgeClient,
33
- Protocol: () => Protocol,
34
- getTypename: () => getTypename,
35
- protocol: () => protocol,
36
- toUint8Array: () => toUint8Array
33
+ EdgeConnectionClosedError: () => EdgeConnectionClosedError,
34
+ EdgeHttpClient: () => EdgeHttpClient,
35
+ EdgeIdentityChangedError: () => EdgeIdentityChangedError,
36
+ Protocol: () => import_chunk_ANV2HBEH.Protocol,
37
+ createChainEdgeIdentity: () => createChainEdgeIdentity,
38
+ createDeviceEdgeIdentity: () => createDeviceEdgeIdentity,
39
+ createEphemeralEdgeIdentity: () => createEphemeralEdgeIdentity,
40
+ createStubEdgeIdentity: () => createStubEdgeIdentity,
41
+ createTestHaloEdgeIdentity: () => createTestHaloEdgeIdentity,
42
+ getTypename: () => import_chunk_ANV2HBEH.getTypename,
43
+ handleAuthChallenge: () => handleAuthChallenge,
44
+ protocol: () => import_chunk_ANV2HBEH.protocol,
45
+ toUint8Array: () => import_chunk_ANV2HBEH.toUint8Array
37
46
  });
38
47
  module.exports = __toCommonJS(node_exports);
48
+ var import_chunk_ANV2HBEH = require("./chunk-ANV2HBEH.cjs");
39
49
  __reExport(node_exports, require("@dxos/protocols/buf/dxos/edge/messenger_pb"), module.exports);
40
- var import_isomorphic_ws = __toESM(require("isomorphic-ws"));
41
50
  var import_async = require("@dxos/async");
42
51
  var import_context = require("@dxos/context");
43
- var import_invariant = require("@dxos/invariant");
44
52
  var import_log = require("@dxos/log");
45
- var import_buf = require("@dxos/protocols/buf");
46
- var import_messenger_pb = require("@dxos/protocols/buf/dxos/edge/messenger_pb");
47
- var import_wkt = require("@bufbuild/protobuf/wkt");
48
- var import_messenger_pb2 = require("@dxos/protocols/buf/dxos/edge/messenger_pb");
49
- var import_invariant2 = require("@dxos/invariant");
50
- var import_buf2 = require("@dxos/protocols/buf");
51
- var import_messenger_pb3 = require("@dxos/protocols/buf/dxos/edge/messenger_pb");
52
- var import_util = require("@dxos/util");
53
+ var import_invariant = require("@dxos/invariant");
54
+ var import_proto = require("@dxos/protocols/proto");
55
+ var import_isomorphic_ws = __toESM(require("isomorphic-ws"));
53
56
  var import_async2 = require("@dxos/async");
54
57
  var import_context2 = require("@dxos/context");
55
- var import_debug = require("@dxos/debug");
58
+ var import_invariant2 = require("@dxos/invariant");
56
59
  var import_log2 = require("@dxos/log");
57
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/protocol.ts";
58
- var getTypename = (typeName) => `type.googleapis.com/${typeName}`;
59
- var Protocol = class {
60
- constructor(types) {
61
- this._typeRegistry = import_buf2.buf.createRegistry(...types);
62
- }
63
- get typeRegistry() {
64
- return this._typeRegistry;
60
+ var import_buf = require("@dxos/protocols/buf");
61
+ var import_messenger_pb = require("@dxos/protocols/buf/dxos/edge/messenger_pb");
62
+ var import_async3 = require("@dxos/async");
63
+ var import_context3 = require("@dxos/context");
64
+ var import_debug = require("@dxos/debug");
65
+ var import_log3 = require("@dxos/log");
66
+ var import_credentials = require("@dxos/credentials");
67
+ var import_keyring = require("@dxos/keyring");
68
+ var import_keys = require("@dxos/keys");
69
+ var import_async4 = require("@dxos/async");
70
+ var import_context4 = require("@dxos/context");
71
+ var import_log4 = require("@dxos/log");
72
+ var import_protocols = require("@dxos/protocols");
73
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-identity.ts";
74
+ var handleAuthChallenge = async (failedResponse, identity) => {
75
+ (0, import_invariant.invariant)(failedResponse.status === 401, void 0, {
76
+ F: __dxlog_file,
77
+ L: 21,
78
+ S: void 0,
79
+ A: [
80
+ "failedResponse.status === 401",
81
+ ""
82
+ ]
83
+ });
84
+ const headerValue = failedResponse.headers.get("Www-Authenticate");
85
+ (0, import_invariant.invariant)(headerValue?.startsWith("VerifiablePresentation challenge="), void 0, {
86
+ F: __dxlog_file,
87
+ L: 24,
88
+ S: void 0,
89
+ A: [
90
+ "headerValue?.startsWith('VerifiablePresentation challenge=')",
91
+ ""
92
+ ]
93
+ });
94
+ const challenge = headerValue?.slice("VerifiablePresentation challenge=".length);
95
+ (0, import_invariant.invariant)(challenge, void 0, {
96
+ F: __dxlog_file,
97
+ L: 27,
98
+ S: void 0,
99
+ A: [
100
+ "challenge",
101
+ ""
102
+ ]
103
+ });
104
+ const presentation = await identity.presentCredentials({
105
+ challenge: Buffer.from(challenge, "base64")
106
+ });
107
+ return import_proto.schema.getCodecForType("dxos.halo.credentials.Presentation").encode(presentation);
108
+ };
109
+ function _ts_decorate(decorators, target, key, desc) {
110
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
111
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
112
+ 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;
113
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
114
+ }
115
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-ws-connection.ts";
116
+ var SIGNAL_KEEPALIVE_INTERVAL = 5e3;
117
+ var EdgeWsConnection = class extends import_context2.Resource {
118
+ constructor(_identity, _connectionInfo, _callbacks) {
119
+ super();
120
+ this._identity = _identity;
121
+ this._connectionInfo = _connectionInfo;
122
+ this._callbacks = _callbacks;
65
123
  }
66
- toJson(message) {
67
- try {
68
- return import_buf2.buf.toJson(import_messenger_pb3.MessageSchema, message, {
69
- registry: this.typeRegistry
70
- });
71
- } catch (err) {
72
- return {
73
- type: this.getPayloadType(message)
74
- };
75
- }
124
+ get info() {
125
+ return {
126
+ open: this.isOpen,
127
+ identity: this._identity.identityKey,
128
+ device: this._identity.peerKey
129
+ };
76
130
  }
77
- /**
78
- * Return the payload with the given type.
79
- */
80
- getPayload(message, type) {
81
- (0, import_invariant2.invariant)(message.payload, void 0, {
82
- F: __dxlog_file,
83
- L: 40,
131
+ send(message) {
132
+ (0, import_invariant2.invariant)(this._ws, void 0, {
133
+ F: __dxlog_file2,
134
+ L: 48,
84
135
  S: this,
85
136
  A: [
86
- "message.payload",
137
+ "this._ws",
87
138
  ""
88
139
  ]
89
140
  });
90
- const payloadTypename = this.getPayloadType(message);
91
- if (type && type.typeName !== payloadTypename) {
92
- throw new Error(`Unexpected payload type: ${payloadTypename}; expected ${type.typeName}`);
93
- }
94
- (0, import_invariant2.invariant)(import_buf2.bufWkt.anyIs(message.payload, type), `Unexpected payload type: ${payloadTypename}}`, {
95
- F: __dxlog_file,
96
- L: 46,
141
+ (0, import_log2.log)("sending...", {
142
+ peerKey: this._identity.peerKey,
143
+ payload: import_chunk_ANV2HBEH.protocol.getPayloadType(message)
144
+ }, {
145
+ F: __dxlog_file2,
146
+ L: 49,
97
147
  S: this,
98
- A: [
99
- "bufWkt.anyIs(message.payload, type)",
100
- "`Unexpected payload type: ${payloadTypename}}`"
101
- ]
148
+ C: (f, a) => f(...a)
102
149
  });
103
- const payload = import_buf2.bufWkt.anyUnpack(message.payload, this.typeRegistry);
104
- (0, import_invariant2.invariant)(payload, `Empty payload: ${payloadTypename}}`, {
105
- F: __dxlog_file,
106
- L: 48,
150
+ this._ws.send(import_buf.buf.toBinary(import_messenger_pb.MessageSchema, message));
151
+ }
152
+ async _open() {
153
+ this._ws = new import_isomorphic_ws.default(this._connectionInfo.url, this._connectionInfo.protocolHeader ? [
154
+ this._connectionInfo.protocolHeader
155
+ ] : []);
156
+ this._ws.onopen = () => {
157
+ if (this.isOpen) {
158
+ (0, import_log2.log)("connected", void 0, {
159
+ F: __dxlog_file2,
160
+ L: 61,
161
+ S: this,
162
+ C: (f, a) => f(...a)
163
+ });
164
+ this._callbacks.onConnected();
165
+ this._scheduleHeartbeats();
166
+ } else {
167
+ import_log2.log.verbose("connected after becoming inactive", {
168
+ currentIdentity: this._identity
169
+ }, {
170
+ F: __dxlog_file2,
171
+ L: 65,
172
+ S: this,
173
+ C: (f, a) => f(...a)
174
+ });
175
+ }
176
+ };
177
+ this._ws.onclose = () => {
178
+ if (this.isOpen) {
179
+ (0, import_log2.log)("disconnected while being open", void 0, {
180
+ F: __dxlog_file2,
181
+ L: 70,
182
+ S: this,
183
+ C: (f, a) => f(...a)
184
+ });
185
+ this._callbacks.onRestartRequired();
186
+ }
187
+ };
188
+ this._ws.onerror = (event) => {
189
+ if (this.isOpen) {
190
+ import_log2.log.warn("edge connection socket error", {
191
+ error: event.error,
192
+ info: event.message
193
+ }, {
194
+ F: __dxlog_file2,
195
+ L: 76,
196
+ S: this,
197
+ C: (f, a) => f(...a)
198
+ });
199
+ this._callbacks.onRestartRequired();
200
+ } else {
201
+ import_log2.log.verbose("error ignored on closed connection", {
202
+ error: event.error
203
+ }, {
204
+ F: __dxlog_file2,
205
+ L: 79,
206
+ S: this,
207
+ C: (f, a) => f(...a)
208
+ });
209
+ }
210
+ };
211
+ this._ws.onmessage = async (event) => {
212
+ if (!this.isOpen) {
213
+ import_log2.log.verbose("message ignored on closed connection", {
214
+ event: event.type
215
+ }, {
216
+ F: __dxlog_file2,
217
+ L: 87,
218
+ S: this,
219
+ C: (f, a) => f(...a)
220
+ });
221
+ return;
222
+ }
223
+ if (event.data === "__pong__") {
224
+ this._rescheduleHeartbeatTimeout();
225
+ return;
226
+ }
227
+ const data = await (0, import_chunk_ANV2HBEH.toUint8Array)(event.data);
228
+ if (this.isOpen) {
229
+ const message = import_buf.buf.fromBinary(import_messenger_pb.MessageSchema, data);
230
+ (0, import_log2.log)("received", {
231
+ from: message.source,
232
+ payload: import_chunk_ANV2HBEH.protocol.getPayloadType(message)
233
+ }, {
234
+ F: __dxlog_file2,
235
+ L: 97,
236
+ S: this,
237
+ C: (f, a) => f(...a)
238
+ });
239
+ this._callbacks.onMessage(message);
240
+ }
241
+ };
242
+ }
243
+ async _close() {
244
+ void this._inactivityTimeoutCtx?.dispose().catch(() => {
245
+ });
246
+ try {
247
+ this._ws?.close();
248
+ this._ws = void 0;
249
+ } catch (err) {
250
+ if (err instanceof Error && err.message.includes("WebSocket is closed before the connection is established.")) {
251
+ return;
252
+ }
253
+ import_log2.log.warn("Error closing websocket", {
254
+ err
255
+ }, {
256
+ F: __dxlog_file2,
257
+ L: 113,
258
+ S: this,
259
+ C: (f, a) => f(...a)
260
+ });
261
+ }
262
+ }
263
+ _scheduleHeartbeats() {
264
+ (0, import_invariant2.invariant)(this._ws, void 0, {
265
+ F: __dxlog_file2,
266
+ L: 118,
107
267
  S: this,
108
268
  A: [
109
- "payload",
110
- "`Empty payload: ${payloadTypename}}`"
269
+ "this._ws",
270
+ ""
111
271
  ]
112
272
  });
113
- return payload;
273
+ (0, import_async2.scheduleTaskInterval)(this._ctx, async () => {
274
+ this._ws?.send("__ping__");
275
+ }, SIGNAL_KEEPALIVE_INTERVAL);
276
+ this._ws.send("__ping__");
277
+ this._rescheduleHeartbeatTimeout();
114
278
  }
115
- /**
116
- * Get the payload type.
117
- */
118
- getPayloadType(message) {
119
- if (!message.payload) {
120
- return void 0;
279
+ _rescheduleHeartbeatTimeout() {
280
+ if (!this.isOpen) {
281
+ return;
121
282
  }
122
- const [, type] = message.payload.typeUrl.split("/");
123
- return type;
124
- }
125
- /**
126
- * Create a packed message.
127
- */
128
- createMessage(type, { source, target, payload, serviceId }) {
129
- return import_buf2.buf.create(import_messenger_pb3.MessageSchema, {
130
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
131
- source,
132
- target,
133
- serviceId,
134
- payload: payload ? import_buf2.bufWkt.anyPack(type, import_buf2.buf.create(type, payload)) : void 0
283
+ void this._inactivityTimeoutCtx?.dispose();
284
+ this._inactivityTimeoutCtx = new import_context2.Context(void 0, {
285
+ F: __dxlog_file2,
286
+ L: 137
135
287
  });
288
+ (0, import_async2.scheduleTask)(this._inactivityTimeoutCtx, () => {
289
+ if (this.isOpen) {
290
+ this._callbacks.onRestartRequired();
291
+ }
292
+ }, 2 * SIGNAL_KEEPALIVE_INTERVAL);
136
293
  }
137
294
  };
138
- var toUint8Array = async (data) => {
139
- if (data instanceof Buffer) {
140
- return (0, import_util.bufferToArray)(data);
141
- }
142
- if (data instanceof Blob) {
143
- return new Uint8Array(await data.arrayBuffer());
295
+ _ts_decorate([
296
+ import_log2.logInfo
297
+ ], EdgeWsConnection.prototype, "info", null);
298
+ var EdgeConnectionClosedError = class extends Error {
299
+ constructor() {
300
+ super("Edge connection closed.");
144
301
  }
145
- throw new Error(`Unexpected datatype: ${data}`);
146
302
  };
147
- var protocol = new Protocol([
148
- import_messenger_pb2.SwarmRequestSchema,
149
- import_messenger_pb2.SwarmResponseSchema,
150
- import_messenger_pb2.TextMessageSchema,
151
- import_wkt.AnySchema
152
- ]);
153
- var WebsocketClosedError = class extends Error {
303
+ var EdgeIdentityChangedError = class extends Error {
154
304
  constructor() {
155
- super("WebSocket connection closed");
305
+ super("Edge identity changed.");
156
306
  }
157
307
  };
158
- function _ts_decorate(decorators, target, key, desc) {
308
+ function _ts_decorate2(decorators, target, key, desc) {
159
309
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
160
310
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
161
311
  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;
162
312
  return c > 3 && r && Object.defineProperty(target, key, r), r;
163
313
  }
164
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/persistent-lifecycle.ts";
314
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/persistent-lifecycle.ts";
165
315
  var INIT_RESTART_DELAY = 100;
166
316
  var DEFAULT_MAX_RESTART_DELAY = 5e3;
167
- var PersistentLifecycle = class extends import_context2.Resource {
317
+ var PersistentLifecycle = class extends import_context3.Resource {
168
318
  constructor({ start, stop, onRestart, maxRestartDelay = DEFAULT_MAX_RESTART_DELAY }) {
169
319
  super();
320
+ this._currentContext = void 0;
170
321
  this._restartTask = void 0;
171
322
  this._restartAfter = 0;
172
323
  this._start = start;
@@ -175,117 +326,177 @@ var PersistentLifecycle = class extends import_context2.Resource {
175
326
  this._maxRestartDelay = maxRestartDelay;
176
327
  }
177
328
  async _open() {
178
- this._restartTask = new import_async2.DeferredTask(this._ctx, async () => {
329
+ this._restartTask = new import_async3.DeferredTask(this._ctx, async () => {
179
330
  try {
180
331
  await this._restart();
181
332
  } catch (err) {
182
- import_log2.log.warn("Restart failed", {
333
+ import_log3.log.warn("Restart failed", {
183
334
  err
184
335
  }, {
185
- F: __dxlog_file2,
186
- L: 64,
336
+ F: __dxlog_file3,
337
+ L: 65,
187
338
  S: this,
188
339
  C: (f, a) => f(...a)
189
340
  });
190
341
  this._restartTask?.schedule();
191
342
  }
192
343
  });
193
- await this._start().catch((err) => {
194
- import_log2.log.warn("Start failed", {
344
+ this._currentContext = await this._start().catch((err) => {
345
+ import_log3.log.warn("Start failed", {
195
346
  err
196
347
  }, {
197
- F: __dxlog_file2,
198
- L: 69,
348
+ F: __dxlog_file3,
349
+ L: 70,
199
350
  S: this,
200
351
  C: (f, a) => f(...a)
201
352
  });
202
353
  this._restartTask?.schedule();
354
+ return void 0;
203
355
  });
204
356
  }
205
357
  async _close() {
206
358
  await this._restartTask?.join();
207
- await this._stop();
359
+ await this._stopCurrentContext();
208
360
  this._restartTask = void 0;
209
361
  }
210
362
  async _restart() {
211
- (0, import_log2.log)(`restarting in ${this._restartAfter}ms`, {
363
+ (0, import_log3.log)(`restarting in ${this._restartAfter}ms`, {
212
364
  state: this._lifecycleState
213
365
  }, {
214
- F: __dxlog_file2,
215
- L: 81,
366
+ F: __dxlog_file3,
367
+ L: 83,
216
368
  S: this,
217
369
  C: (f, a) => f(...a)
218
370
  });
219
- await this._stop();
220
- if (this._lifecycleState !== import_context2.LifecycleState.OPEN) {
371
+ await this._stopCurrentContext();
372
+ if (this._lifecycleState !== import_context3.LifecycleState.OPEN) {
221
373
  return;
222
374
  }
223
- await (0, import_context2.cancelWithContext)(this._ctx, (0, import_async2.sleep)(this._restartAfter));
375
+ await (0, import_context3.cancelWithContext)(this._ctx, (0, import_async3.sleep)(this._restartAfter));
224
376
  this._restartAfter = Math.min(Math.max(this._restartAfter * 2, INIT_RESTART_DELAY), this._maxRestartDelay);
225
- await (0, import_debug.warnAfterTimeout)(5e3, "Connection establishment takes too long", () => this._start());
377
+ await (0, import_debug.warnAfterTimeout)(5e3, "Connection establishment takes too long", async () => {
378
+ this._currentContext = await this._start();
379
+ });
226
380
  this._restartAfter = 0;
227
381
  await this._onRestart?.();
228
382
  }
383
+ async _stopCurrentContext() {
384
+ if (this._currentContext) {
385
+ try {
386
+ await this._stop(this._currentContext);
387
+ } catch (err) {
388
+ import_log3.log.catch(err, void 0, {
389
+ F: __dxlog_file3,
390
+ L: 105,
391
+ S: this,
392
+ C: (f, a) => f(...a)
393
+ });
394
+ }
395
+ this._currentContext = void 0;
396
+ }
397
+ }
229
398
  /**
230
399
  * Scheduling restart should be done from outside.
231
400
  */
232
401
  scheduleRestart() {
233
- if (this._lifecycleState !== import_context2.LifecycleState.OPEN) {
402
+ if (this._lifecycleState !== import_context3.LifecycleState.OPEN) {
234
403
  return;
235
404
  }
236
405
  this._restartTask.schedule();
237
406
  }
238
407
  };
239
- _ts_decorate([
240
- import_async2.synchronized
408
+ _ts_decorate2([
409
+ import_async3.synchronized
241
410
  ], PersistentLifecycle.prototype, "_open", null);
242
- _ts_decorate([
243
- import_async2.synchronized
411
+ _ts_decorate2([
412
+ import_async3.synchronized
244
413
  ], PersistentLifecycle.prototype, "scheduleRestart", null);
245
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-client.ts";
414
+ var getEdgeUrlWithProtocol = (baseUrl, protocol2) => {
415
+ const isSecure = baseUrl.startsWith("https") || baseUrl.startsWith("wss");
416
+ const url = new URL(baseUrl);
417
+ url.protocol = protocol2 + (isSecure ? "s" : "");
418
+ return url.toString();
419
+ };
420
+ function _ts_decorate3(decorators, target, key, desc) {
421
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
422
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
423
+ 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;
424
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
425
+ }
426
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-client.ts";
246
427
  var DEFAULT_TIMEOUT = 1e4;
247
- var SIGNAL_KEEPALIVE_INTERVAL = 5e3;
248
428
  var EdgeClient = class extends import_context.Resource {
249
- constructor(_identityKey, _peerKey, _config) {
429
+ constructor(_identity, _config) {
250
430
  super();
251
- this._identityKey = _identityKey;
252
- this._peerKey = _peerKey;
431
+ this._identity = _identity;
253
432
  this._config = _config;
254
- this.reconnect = new import_async.Event();
255
433
  this._persistentLifecycle = new PersistentLifecycle({
256
- start: async () => this._openWebSocket(),
257
- stop: async () => this._closeWebSocket(),
258
- onRestart: async () => this.reconnect.emit()
434
+ start: async () => this._connect(),
435
+ stop: async (state) => this._disconnect(state)
259
436
  });
260
- this._listeners = /* @__PURE__ */ new Set();
437
+ this._messageListeners = /* @__PURE__ */ new Set();
438
+ this._reconnectListeners = /* @__PURE__ */ new Set();
439
+ this._currentConnection = void 0;
261
440
  this._ready = new import_async.Trigger();
262
- this._ws = void 0;
263
- this._keepaliveCtx = void 0;
264
- this._heartBeatContext = void 0;
265
- this._protocol = this._config.protocol ?? protocol;
441
+ this._isActive = (connection) => connection === this._currentConnection;
442
+ this._baseWsUrl = getEdgeUrlWithProtocol(_config.socketEndpoint, "ws");
443
+ this._baseHttpUrl = getEdgeUrlWithProtocol(_config.socketEndpoint, "http");
266
444
  }
267
- // TODO(burdon): Attach logging.
268
445
  get info() {
269
446
  return {
270
447
  open: this.isOpen,
271
- identity: this._identityKey,
272
- device: this._peerKey
448
+ identity: this._identity.identityKey,
449
+ device: this._identity.peerKey
273
450
  };
274
451
  }
452
+ get isConnected() {
453
+ return Boolean(this._currentConnection) && this._ready.state === import_async.TriggerState.RESOLVED;
454
+ }
275
455
  get identityKey() {
276
- return this._identityKey;
456
+ return this._identity.identityKey;
277
457
  }
278
458
  get peerKey() {
279
- return this._peerKey;
459
+ return this._identity.peerKey;
280
460
  }
281
- setIdentity({ peerKey, identityKey }) {
282
- this._peerKey = peerKey;
283
- this._identityKey = identityKey;
284
- this._persistentLifecycle.scheduleRestart();
461
+ setIdentity(identity) {
462
+ if (identity.identityKey !== this._identity.identityKey || identity.peerKey !== this._identity.peerKey) {
463
+ (0, import_log.log)("Edge identity changed", {
464
+ identity,
465
+ oldIdentity: this._identity
466
+ }, {
467
+ F: __dxlog_file4,
468
+ L: 95,
469
+ S: this,
470
+ C: (f, a) => f(...a)
471
+ });
472
+ this._identity = identity;
473
+ this._closeCurrentConnection(new EdgeIdentityChangedError());
474
+ this._persistentLifecycle.scheduleRestart();
475
+ }
285
476
  }
286
- addListener(listener) {
287
- this._listeners.add(listener);
288
- return () => this._listeners.delete(listener);
477
+ onMessage(listener) {
478
+ this._messageListeners.add(listener);
479
+ return () => this._messageListeners.delete(listener);
480
+ }
481
+ onReconnected(listener) {
482
+ this._reconnectListeners.add(listener);
483
+ if (this._ready.state === import_async.TriggerState.RESOLVED) {
484
+ (0, import_async.scheduleMicroTask)(this._ctx, () => {
485
+ if (this._reconnectListeners.has(listener)) {
486
+ try {
487
+ listener();
488
+ } catch (error) {
489
+ import_log.log.catch(error, void 0, {
490
+ F: __dxlog_file4,
491
+ L: 117,
492
+ S: this,
493
+ C: (f, a) => f(...a)
494
+ });
495
+ }
496
+ }
497
+ });
498
+ }
499
+ return () => this._reconnectListeners.delete(listener);
289
500
  }
290
501
  /**
291
502
  * Open connection to messaging service.
@@ -294,8 +505,8 @@ var EdgeClient = class extends import_context.Resource {
294
505
  (0, import_log.log)("opening...", {
295
506
  info: this.info
296
507
  }, {
297
- F: __dxlog_file3,
298
- L: 101,
508
+ F: __dxlog_file4,
509
+ L: 129,
299
510
  S: this,
300
511
  C: (f, a) => f(...a)
301
512
  });
@@ -303,8 +514,8 @@ var EdgeClient = class extends import_context.Resource {
303
514
  import_log.log.warn("Error while opening connection", {
304
515
  err
305
516
  }, {
306
- F: __dxlog_file3,
307
- L: 103,
517
+ F: __dxlog_file4,
518
+ L: 131,
308
519
  S: this,
309
520
  C: (f, a) => f(...a)
310
521
  });
@@ -315,126 +526,139 @@ var EdgeClient = class extends import_context.Resource {
315
526
  */
316
527
  async _close() {
317
528
  (0, import_log.log)("closing...", {
318
- peerKey: this._peerKey
529
+ peerKey: this._identity.peerKey
319
530
  }, {
320
- F: __dxlog_file3,
321
- L: 111,
531
+ F: __dxlog_file4,
532
+ L: 139,
322
533
  S: this,
323
534
  C: (f, a) => f(...a)
324
535
  });
536
+ this._closeCurrentConnection();
325
537
  await this._persistentLifecycle.close();
326
538
  }
327
- async _openWebSocket() {
328
- const url = new URL(`/ws/${this._identityKey}/${this._peerKey}`, this._config.socketEndpoint);
329
- this._ws = new import_isomorphic_ws.default(url);
330
- this._ws.onopen = () => {
331
- (0, import_log.log)("opened", this.info, {
332
- F: __dxlog_file3,
333
- L: 120,
334
- S: this,
335
- C: (f, a) => f(...a)
336
- });
337
- this._ready.wake();
338
- };
339
- this._ws.onclose = () => {
340
- (0, import_log.log)("closed", this.info, {
341
- F: __dxlog_file3,
342
- L: 124,
343
- S: this,
344
- C: (f, a) => f(...a)
345
- });
346
- this._persistentLifecycle.scheduleRestart();
347
- };
348
- this._ws.onerror = (event) => {
349
- import_log.log.warn("EdgeClient socket error", {
350
- error: event.error,
351
- info: event.message
352
- }, {
353
- F: __dxlog_file3,
354
- L: 128,
355
- S: this,
356
- C: (f, a) => f(...a)
357
- });
358
- this._persistentLifecycle.scheduleRestart();
359
- };
360
- this._ws.onmessage = async (event) => {
361
- if (event.data === "__pong__") {
362
- this._onHeartbeat();
363
- return;
364
- }
365
- const data = await toUint8Array(event.data);
366
- const message = import_buf.buf.fromBinary(import_messenger_pb.MessageSchema, data);
367
- (0, import_log.log)("received", {
368
- peerKey: this._peerKey,
369
- payload: protocol.getPayloadType(message)
370
- }, {
371
- F: __dxlog_file3,
372
- L: 141,
539
+ async _connect() {
540
+ if (this._ctx.disposed) {
541
+ return void 0;
542
+ }
543
+ const identity = this._identity;
544
+ const path = `/ws/${identity.identityKey}/${identity.peerKey}`;
545
+ const protocolHeader = this._config.disableAuth ? void 0 : await this._createAuthHeader(path);
546
+ if (this._identity !== identity) {
547
+ (0, import_log.log)("identity changed during auth header request", void 0, {
548
+ F: __dxlog_file4,
549
+ L: 153,
373
550
  S: this,
374
551
  C: (f, a) => f(...a)
375
552
  });
376
- if (message) {
377
- for (const listener of this._listeners) {
378
- try {
379
- await listener(message);
380
- } catch (err) {
381
- import_log.log.error("processing", {
382
- err,
383
- payload: protocol.getPayloadType(message)
384
- }, {
385
- F: __dxlog_file3,
386
- L: 147,
387
- S: this,
388
- C: (f, a) => f(...a)
389
- });
390
- }
553
+ return void 0;
554
+ }
555
+ const restartRequired = new import_async.Trigger();
556
+ const url = new URL(path, this._baseWsUrl);
557
+ (0, import_log.log)("Opening websocket", {
558
+ url: url.toString(),
559
+ protocolHeader
560
+ }, {
561
+ F: __dxlog_file4,
562
+ L: 159,
563
+ S: this,
564
+ C: (f, a) => f(...a)
565
+ });
566
+ const connection = new EdgeWsConnection(identity, {
567
+ url,
568
+ protocolHeader
569
+ }, {
570
+ onConnected: () => {
571
+ if (this._isActive(connection)) {
572
+ this._ready.wake();
573
+ this._notifyReconnected();
574
+ } else {
575
+ import_log.log.verbose("connected callback ignored, because connection is not active", void 0, {
576
+ F: __dxlog_file4,
577
+ L: 169,
578
+ S: this,
579
+ C: (f, a) => f(...a)
580
+ });
581
+ }
582
+ },
583
+ onRestartRequired: () => {
584
+ if (this._isActive(connection)) {
585
+ this._closeCurrentConnection();
586
+ this._persistentLifecycle.scheduleRestart();
587
+ } else {
588
+ import_log.log.verbose("restart requested by inactive connection", void 0, {
589
+ F: __dxlog_file4,
590
+ L: 177,
591
+ S: this,
592
+ C: (f, a) => f(...a)
593
+ });
594
+ }
595
+ restartRequired.wake();
596
+ },
597
+ onMessage: (message) => {
598
+ if (this._isActive(connection)) {
599
+ this._notifyMessageReceived(message);
600
+ } else {
601
+ import_log.log.verbose("ignored a message on inactive connection", {
602
+ from: message.source,
603
+ type: message.payload?.typeUrl
604
+ }, {
605
+ F: __dxlog_file4,
606
+ L: 185,
607
+ S: this,
608
+ C: (f, a) => f(...a)
609
+ });
391
610
  }
392
611
  }
393
- };
394
- await this._ready.wait({
395
- timeout: this._config.timeout ?? DEFAULT_TIMEOUT
396
- });
397
- this._keepaliveCtx = new import_context.Context(void 0, {
398
- F: __dxlog_file3,
399
- L: 154
400
612
  });
401
- (0, import_async.scheduleTaskInterval)(this._keepaliveCtx, async () => {
402
- this._ws?.send("__ping__");
403
- }, SIGNAL_KEEPALIVE_INTERVAL);
404
- this._ws.send("__ping__");
405
- this._onHeartbeat();
613
+ this._currentConnection = connection;
614
+ await connection.open();
615
+ await Promise.race([
616
+ this._ready.wait({
617
+ timeout: this._config.timeout ?? DEFAULT_TIMEOUT
618
+ }),
619
+ restartRequired
620
+ ]);
621
+ return connection;
406
622
  }
407
- async _closeWebSocket() {
408
- if (!this._ws) {
409
- return;
623
+ async _disconnect(state) {
624
+ await state.close();
625
+ }
626
+ _closeCurrentConnection(error = new EdgeConnectionClosedError()) {
627
+ this._currentConnection = void 0;
628
+ this._ready.throw(error);
629
+ this._ready.reset();
630
+ }
631
+ _notifyReconnected() {
632
+ for (const listener of this._reconnectListeners) {
633
+ try {
634
+ listener();
635
+ } catch (err) {
636
+ import_log.log.error("ws reconnect listener failed", {
637
+ err
638
+ }, {
639
+ F: __dxlog_file4,
640
+ L: 218,
641
+ S: this,
642
+ C: (f, a) => f(...a)
643
+ });
644
+ }
410
645
  }
411
- try {
412
- this._ready.throw(new WebsocketClosedError());
413
- this._ready.reset();
414
- void this._keepaliveCtx?.dispose();
415
- this._keepaliveCtx = void 0;
416
- void this._heartBeatContext?.dispose();
417
- this._heartBeatContext = void 0;
418
- this._ws.onopen = () => {
419
- };
420
- this._ws.onclose = () => {
421
- };
422
- this._ws.onerror = () => {
423
- };
424
- this._ws.close();
425
- this._ws = void 0;
426
- } catch (err) {
427
- if (err instanceof Error && err.message.includes("WebSocket is closed before the connection is established.")) {
428
- return;
646
+ }
647
+ _notifyMessageReceived(message) {
648
+ for (const listener of this._messageListeners) {
649
+ try {
650
+ listener(message);
651
+ } catch (err) {
652
+ import_log.log.error("ws incoming message processing failed", {
653
+ err,
654
+ payload: import_chunk_ANV2HBEH.protocol.getPayloadType(message)
655
+ }, {
656
+ F: __dxlog_file4,
657
+ L: 228,
658
+ S: this,
659
+ C: (f, a) => f(...a)
660
+ });
429
661
  }
430
- import_log.log.warn("Error closing websocket", {
431
- err
432
- }, {
433
- F: __dxlog_file3,
434
- L: 190,
435
- S: this,
436
- C: (f, a) => f(...a)
437
- });
438
662
  }
439
663
  }
440
664
  /**
@@ -443,58 +667,349 @@ var EdgeClient = class extends import_context.Resource {
443
667
  */
444
668
  async send(message) {
445
669
  if (this._ready.state !== import_async.TriggerState.RESOLVED) {
670
+ (0, import_log.log)("waiting for websocket to become ready", void 0, {
671
+ F: __dxlog_file4,
672
+ L: 239,
673
+ S: this,
674
+ C: (f, a) => f(...a)
675
+ });
446
676
  await this._ready.wait({
447
677
  timeout: this._config.timeout ?? DEFAULT_TIMEOUT
448
678
  });
449
679
  }
450
- (0, import_invariant.invariant)(this._ws, void 0, {
451
- F: __dxlog_file3,
452
- L: 202,
453
- S: this,
454
- A: [
455
- "this._ws",
456
- ""
457
- ]
680
+ if (!this._currentConnection) {
681
+ throw new EdgeConnectionClosedError();
682
+ }
683
+ if (message.source && (message.source.peerKey !== this._identity.peerKey || message.source.identityKey !== this.identityKey)) {
684
+ throw new EdgeIdentityChangedError();
685
+ }
686
+ this._currentConnection.send(message);
687
+ }
688
+ async _createAuthHeader(path) {
689
+ const httpUrl = new URL(path, this._baseHttpUrl);
690
+ httpUrl.protocol = getEdgeUrlWithProtocol(this._baseWsUrl.toString(), "http");
691
+ const response = await fetch(httpUrl, {
692
+ method: "GET"
458
693
  });
459
- (0, import_invariant.invariant)(!message.source || message.source.peerKey === this._peerKey, void 0, {
460
- F: __dxlog_file3,
461
- L: 203,
694
+ if (response.status === 401) {
695
+ return encodePresentationWsAuthHeader(await handleAuthChallenge(response, this._identity));
696
+ } else {
697
+ import_log.log.warn("no auth challenge from edge", {
698
+ status: response.status,
699
+ statusText: response.statusText
700
+ }, {
701
+ F: __dxlog_file4,
702
+ L: 264,
703
+ S: this,
704
+ C: (f, a) => f(...a)
705
+ });
706
+ return void 0;
707
+ }
708
+ }
709
+ };
710
+ _ts_decorate3([
711
+ import_log.logInfo
712
+ ], EdgeClient.prototype, "info", null);
713
+ var encodePresentationWsAuthHeader = (encodedPresentation) => {
714
+ const encodedToken = Buffer.from(encodedPresentation).toString("base64").replace(/=*$/, "").replaceAll("/", "|");
715
+ return `base64url.bearer.authorization.dxos.org.${encodedToken}`;
716
+ };
717
+ var createDeviceEdgeIdentity = async (signer, key) => {
718
+ return {
719
+ identityKey: key.toHex(),
720
+ peerKey: key.toHex(),
721
+ presentCredentials: async ({ challenge }) => {
722
+ return (0, import_credentials.signPresentation)({
723
+ presentation: {
724
+ credentials: [
725
+ // Verifier requires at least one credential in the presentation to establish the subject.
726
+ await (0, import_credentials.createCredential)({
727
+ assertion: {
728
+ "@type": "dxos.halo.credentials.Auth"
729
+ },
730
+ issuer: key,
731
+ subject: key,
732
+ signer
733
+ })
734
+ ]
735
+ },
736
+ signer,
737
+ signerKey: key,
738
+ nonce: challenge
739
+ });
740
+ }
741
+ };
742
+ };
743
+ var createChainEdgeIdentity = async (signer, identityKey, peerKey, chain, credentials) => {
744
+ const credentialsToSign = credentials.length > 0 ? credentials : [
745
+ await (0, import_credentials.createCredential)({
746
+ assertion: {
747
+ "@type": "dxos.halo.credentials.Auth"
748
+ },
749
+ issuer: identityKey,
750
+ subject: identityKey,
751
+ signer,
752
+ chain,
753
+ signingKey: peerKey
754
+ })
755
+ ];
756
+ return {
757
+ identityKey: identityKey.toHex(),
758
+ peerKey: peerKey.toHex(),
759
+ presentCredentials: async ({ challenge }) => {
760
+ return (0, import_credentials.signPresentation)({
761
+ presentation: {
762
+ credentials: credentialsToSign
763
+ },
764
+ signer,
765
+ nonce: challenge,
766
+ signerKey: peerKey,
767
+ chain
768
+ });
769
+ }
770
+ };
771
+ };
772
+ var createEphemeralEdgeIdentity = async () => {
773
+ const keyring = new import_keyring.Keyring();
774
+ const key = await keyring.createKey();
775
+ return createDeviceEdgeIdentity(keyring, key);
776
+ };
777
+ var createTestHaloEdgeIdentity = async (signer, identityKey, deviceKey) => {
778
+ const deviceAdmission = await (0, import_credentials.createCredential)({
779
+ assertion: {
780
+ "@type": "dxos.halo.credentials.AuthorizedDevice",
781
+ deviceKey,
782
+ identityKey
783
+ },
784
+ issuer: identityKey,
785
+ subject: deviceKey,
786
+ signer
787
+ });
788
+ return createChainEdgeIdentity(signer, identityKey, deviceKey, {
789
+ credential: deviceAdmission
790
+ }, [
791
+ await (0, import_credentials.createCredential)({
792
+ assertion: {
793
+ "@type": "dxos.halo.credentials.Auth"
794
+ },
795
+ issuer: identityKey,
796
+ subject: identityKey,
797
+ signer
798
+ })
799
+ ]);
800
+ };
801
+ var createStubEdgeIdentity = () => {
802
+ const identityKey = import_keys.PublicKey.random();
803
+ const deviceKey = import_keys.PublicKey.random();
804
+ return {
805
+ identityKey: identityKey.toHex(),
806
+ peerKey: deviceKey.toHex(),
807
+ presentCredentials: async () => {
808
+ throw new Error("Stub identity does not support authentication.");
809
+ }
810
+ };
811
+ };
812
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-http-client.ts";
813
+ var DEFAULT_RETRY_TIMEOUT = 1500;
814
+ var DEFAULT_RETRY_JITTER = 500;
815
+ var DEFAULT_MAX_RETRIES_COUNT = 3;
816
+ var EdgeHttpClient = class {
817
+ constructor(baseUrl) {
818
+ this._baseUrl = getEdgeUrlWithProtocol(baseUrl, "http");
819
+ (0, import_log4.log)("created", {
820
+ url: this._baseUrl
821
+ }, {
822
+ F: __dxlog_file5,
823
+ L: 42,
462
824
  S: this,
463
- A: [
464
- "!message.source || message.source.peerKey === this._peerKey",
465
- ""
466
- ]
825
+ C: (f, a) => f(...a)
467
826
  });
468
- (0, import_log.log)("sending...", {
469
- peerKey: this._peerKey,
470
- payload: protocol.getPayloadType(message)
827
+ }
828
+ setIdentity(identity) {
829
+ if (this._edgeIdentity?.identityKey !== identity.identityKey || this._edgeIdentity?.peerKey !== identity.peerKey) {
830
+ this._edgeIdentity = identity;
831
+ this._authHeader = void 0;
832
+ }
833
+ }
834
+ createAgent(body, args) {
835
+ return this._call("/agents/create", {
836
+ ...args,
837
+ method: "POST",
838
+ body
839
+ });
840
+ }
841
+ getAgentStatus(request, args) {
842
+ return this._call(`/users/${request.ownerIdentityKey.toHex()}/agent/status`, {
843
+ ...args,
844
+ method: "GET"
845
+ });
846
+ }
847
+ getCredentialsForNotarization(spaceId, args) {
848
+ return this._call(`/spaces/${spaceId}/notarization`, {
849
+ ...args,
850
+ method: "GET"
851
+ });
852
+ }
853
+ async notarizeCredentials(spaceId, body, args) {
854
+ await this._call(`/spaces/${spaceId}/notarization`, {
855
+ ...args,
856
+ body,
857
+ method: "POST"
858
+ });
859
+ }
860
+ async joinSpaceByInvitation(spaceId, body, args) {
861
+ return this._call(`/spaces/${spaceId}/join`, {
862
+ ...args,
863
+ body,
864
+ method: "POST"
865
+ });
866
+ }
867
+ async recoverIdentity(body, args) {
868
+ return this._call("/identity/recover", {
869
+ ...args,
870
+ body,
871
+ method: "POST"
872
+ });
873
+ }
874
+ async _call(path, args) {
875
+ const requestContext = args.context ?? new import_context4.Context(void 0, {
876
+ F: __dxlog_file5,
877
+ L: 91
878
+ });
879
+ const shouldRetry = createRetryHandler(args);
880
+ const url = `${this._baseUrl}${path.startsWith("/") ? path.slice(1) : path}`;
881
+ import_log4.log.info("call", {
882
+ method: args.method,
883
+ path,
884
+ request: args.body
471
885
  }, {
472
- F: __dxlog_file3,
473
- L: 204,
886
+ F: __dxlog_file5,
887
+ L: 95,
474
888
  S: this,
475
889
  C: (f, a) => f(...a)
476
890
  });
477
- this._ws.send(import_buf.buf.toBinary(import_messenger_pb.MessageSchema, message));
891
+ let handledAuth = false;
892
+ let authHeader = this._authHeader;
893
+ while (true) {
894
+ let processingError;
895
+ let retryAfterHeaderValue = Number.NaN;
896
+ try {
897
+ const request = createRequest(args, authHeader);
898
+ const response = await fetch(url, request);
899
+ retryAfterHeaderValue = Number(response.headers.get("Retry-After"));
900
+ if (response.ok) {
901
+ const body = await response.json();
902
+ if (body.success) {
903
+ return body.data;
904
+ }
905
+ import_log4.log.info("unsuccessful edge response", {
906
+ path,
907
+ body
908
+ }, {
909
+ F: __dxlog_file5,
910
+ L: 114,
911
+ S: this,
912
+ C: (f, a) => f(...a)
913
+ });
914
+ if (body.errorData?.type === "auth_challenge" && typeof body.errorData?.challenge === "string") {
915
+ processingError = new import_protocols.EdgeAuthChallengeError(body.errorData.challenge, body.errorData);
916
+ } else {
917
+ processingError = import_protocols.EdgeCallFailedError.fromUnsuccessfulResponse(response, body);
918
+ }
919
+ } else if (response.status === 401 && !handledAuth) {
920
+ authHeader = await this._handleUnauthorized(response);
921
+ handledAuth = true;
922
+ continue;
923
+ } else {
924
+ processingError = import_protocols.EdgeCallFailedError.fromHttpFailure(response);
925
+ }
926
+ } catch (error) {
927
+ processingError = import_protocols.EdgeCallFailedError.fromProcessingFailureCause(error);
928
+ }
929
+ if (processingError.isRetryable && await shouldRetry(requestContext, retryAfterHeaderValue)) {
930
+ import_log4.log.info("retrying edge request", {
931
+ path,
932
+ processingError
933
+ }, {
934
+ F: __dxlog_file5,
935
+ L: 133,
936
+ S: this,
937
+ C: (f, a) => f(...a)
938
+ });
939
+ } else {
940
+ throw processingError;
941
+ }
942
+ }
478
943
  }
479
- _onHeartbeat() {
480
- if (this._lifecycleState !== import_context.LifecycleState.OPEN) {
481
- return;
944
+ async _handleUnauthorized(response) {
945
+ if (!this._edgeIdentity) {
946
+ import_log4.log.warn("edge unauthorized response received before identity was set", void 0, {
947
+ F: __dxlog_file5,
948
+ L: 142,
949
+ S: this,
950
+ C: (f, a) => f(...a)
951
+ });
952
+ throw import_protocols.EdgeCallFailedError.fromHttpFailure(response);
482
953
  }
483
- void this._heartBeatContext?.dispose();
484
- this._heartBeatContext = new import_context.Context(void 0, {
485
- F: __dxlog_file3,
486
- L: 213
954
+ const challenge = await handleAuthChallenge(response, this._edgeIdentity);
955
+ this._authHeader = encodeAuthHeader(challenge);
956
+ (0, import_log4.log)("auth header updated", void 0, {
957
+ F: __dxlog_file5,
958
+ L: 147,
959
+ S: this,
960
+ C: (f, a) => f(...a)
487
961
  });
488
- (0, import_async.scheduleTask)(this._heartBeatContext, () => {
489
- this._persistentLifecycle.scheduleRestart();
490
- }, 2 * SIGNAL_KEEPALIVE_INTERVAL);
962
+ return this._authHeader;
963
+ }
964
+ };
965
+ var createRetryHandler = (args) => {
966
+ if (!args.retry || args.retry.count < 1) {
967
+ return async () => false;
491
968
  }
969
+ let retries = 0;
970
+ const maxRetries = args.retry.count ?? DEFAULT_MAX_RETRIES_COUNT;
971
+ const baseTimeout = args.retry.timeout ?? DEFAULT_RETRY_TIMEOUT;
972
+ const jitter = args.retry.jitter ?? DEFAULT_RETRY_JITTER;
973
+ return async (ctx, retryAfter) => {
974
+ if (++retries > maxRetries || ctx.disposed) {
975
+ return false;
976
+ }
977
+ if (retryAfter) {
978
+ await (0, import_async4.sleep)(retryAfter);
979
+ } else {
980
+ const timeout = baseTimeout + Math.random() * jitter;
981
+ await (0, import_async4.sleep)(timeout);
982
+ }
983
+ return true;
984
+ };
985
+ };
986
+ var createRequest = (args, authHeader) => {
987
+ return {
988
+ method: args.method,
989
+ body: args.body && JSON.stringify(args.body),
990
+ headers: authHeader ? {
991
+ Authorization: authHeader
992
+ } : void 0
993
+ };
994
+ };
995
+ var encodeAuthHeader = (challenge) => {
996
+ const encodedChallenge = Buffer.from(challenge).toString("base64");
997
+ return `VerifiablePresentation pb;base64,${encodedChallenge}`;
492
998
  };
493
999
  // Annotate the CommonJS export names for ESM import in node:
494
1000
  0 && (module.exports = {
495
1001
  EdgeClient,
1002
+ EdgeConnectionClosedError,
1003
+ EdgeHttpClient,
1004
+ EdgeIdentityChangedError,
496
1005
  Protocol,
1006
+ createChainEdgeIdentity,
1007
+ createDeviceEdgeIdentity,
1008
+ createEphemeralEdgeIdentity,
1009
+ createStubEdgeIdentity,
1010
+ createTestHaloEdgeIdentity,
497
1011
  getTypename,
1012
+ handleAuthChallenge,
498
1013
  protocol,
499
1014
  toUint8Array,
500
1015
  ...require("@dxos/protocols/buf/dxos/edge/messenger_pb")