@dxos/edge-client 0.6.13 → 0.6.14-main.7bd9c89

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 (61) 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 +469 -160
  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 +125 -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 +468 -158
  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 +155 -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 +787 -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 +126 -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 +14 -13
  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/errors.d.ts +4 -1
  32. package/dist/types/src/errors.d.ts.map +1 -1
  33. package/dist/types/src/index.d.ts +4 -0
  34. package/dist/types/src/index.d.ts.map +1 -1
  35. package/dist/types/src/protocol.d.ts +2 -2
  36. package/dist/types/src/protocol.d.ts.map +1 -1
  37. package/dist/types/src/testing/index.d.ts +2 -0
  38. package/dist/types/src/testing/index.d.ts.map +1 -0
  39. package/dist/types/src/testing/test-utils.d.ts +21 -0
  40. package/dist/types/src/testing/test-utils.d.ts.map +1 -0
  41. package/dist/types/src/utils.d.ts +2 -0
  42. package/dist/types/src/utils.d.ts.map +1 -0
  43. package/package.json +29 -14
  44. package/src/auth.ts +135 -0
  45. package/src/defs.ts +2 -3
  46. package/src/edge-client.test.ts +50 -18
  47. package/src/edge-client.ts +76 -24
  48. package/src/edge-http-client.ts +210 -0
  49. package/src/edge-identity.ts +31 -0
  50. package/src/errors.ts +8 -2
  51. package/src/index.ts +4 -0
  52. package/src/persistent-lifecycle.test.ts +2 -2
  53. package/src/protocol.test.ts +1 -2
  54. package/src/protocol.ts +2 -2
  55. package/src/testing/index.ts +5 -0
  56. package/src/testing/test-utils.ts +114 -0
  57. package/src/utils.ts +10 -0
  58. package/src/websocket.test.ts +5 -4
  59. package/dist/types/src/test-utils.d.ts +0 -11
  60. package/dist/types/src/test-utils.d.ts.map +0 -1
  61. package/src/test-utils.ts +0 -49
@@ -1,3 +1,10 @@
1
+ import {
2
+ Protocol,
3
+ getTypename,
4
+ protocol,
5
+ toUint8Array
6
+ } from "./chunk-ZWJXA37R.mjs";
7
+
1
8
  // packages/core/mesh/edge-client/src/index.ts
2
9
  export * from "@dxos/protocols/buf/dxos/edge/messenger_pb";
3
10
 
@@ -5,123 +12,59 @@ export * from "@dxos/protocols/buf/dxos/edge/messenger_pb";
5
12
  import WebSocket from "isomorphic-ws";
6
13
  import { Trigger, Event, scheduleTaskInterval, scheduleTask, TriggerState } from "@dxos/async";
7
14
  import { Context, LifecycleState as LifecycleState2, Resource as Resource2 } from "@dxos/context";
8
- import { invariant as invariant2 } from "@dxos/invariant";
9
15
  import { log as log2 } from "@dxos/log";
10
- import { buf as buf2 } from "@dxos/protocols/buf";
11
- import { MessageSchema as MessageSchema2 } from "@dxos/protocols/buf/dxos/edge/messenger_pb";
12
-
13
- // packages/core/mesh/edge-client/src/defs.ts
14
- import { AnySchema } from "@bufbuild/protobuf/wkt";
15
- import { SwarmRequestSchema, SwarmResponseSchema, TextMessageSchema } from "@dxos/protocols/buf/dxos/edge/messenger_pb";
16
+ import { buf } from "@dxos/protocols/buf";
17
+ import { MessageSchema } from "@dxos/protocols/buf/dxos/edge/messenger_pb";
16
18
 
17
- // packages/core/mesh/edge-client/src/protocol.ts
19
+ // packages/core/mesh/edge-client/src/edge-identity.ts
18
20
  import { invariant } from "@dxos/invariant";
19
- import { buf, bufWkt } from "@dxos/protocols/buf";
20
- import { MessageSchema } from "@dxos/protocols/buf/dxos/edge/messenger_pb";
21
- import { bufferToArray } from "@dxos/util";
22
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/protocol.ts";
23
- var getTypename = (typeName) => `type.googleapis.com/${typeName}`;
24
- var Protocol = class {
25
- constructor(types) {
26
- this._typeRegistry = buf.createRegistry(...types);
27
- }
28
- get typeRegistry() {
29
- return this._typeRegistry;
30
- }
31
- toJson(message) {
32
- try {
33
- return buf.toJson(MessageSchema, message, {
34
- registry: this.typeRegistry
35
- });
36
- } catch (err) {
37
- return {
38
- type: this.getPayloadType(message)
39
- };
40
- }
41
- }
42
- /**
43
- * Return the payload with the given type.
44
- */
45
- getPayload(message, type) {
46
- invariant(message.payload, void 0, {
47
- F: __dxlog_file,
48
- L: 40,
49
- S: this,
50
- A: [
51
- "message.payload",
52
- ""
53
- ]
54
- });
55
- const payloadTypename = this.getPayloadType(message);
56
- if (type && type.typeName !== payloadTypename) {
57
- throw new Error(`Unexpected payload type: ${payloadTypename}; expected ${type.typeName}`);
58
- }
59
- invariant(bufWkt.anyIs(message.payload, type), `Unexpected payload type: ${payloadTypename}}`, {
60
- F: __dxlog_file,
61
- L: 46,
62
- S: this,
63
- A: [
64
- "bufWkt.anyIs(message.payload, type)",
65
- "`Unexpected payload type: ${payloadTypename}}`"
66
- ]
67
- });
68
- const payload = bufWkt.anyUnpack(message.payload, this.typeRegistry);
69
- invariant(payload, `Empty payload: ${payloadTypename}}`, {
70
- F: __dxlog_file,
71
- L: 48,
72
- S: this,
73
- A: [
74
- "payload",
75
- "`Empty payload: ${payloadTypename}}`"
76
- ]
77
- });
78
- return payload;
79
- }
80
- /**
81
- * Get the payload type.
82
- */
83
- getPayloadType(message) {
84
- if (!message.payload) {
85
- return void 0;
86
- }
87
- const [, type] = message.payload.typeUrl.split("/");
88
- return type;
89
- }
90
- /**
91
- * Create a packed message.
92
- */
93
- createMessage(type, { source, target, payload, serviceId }) {
94
- return buf.create(MessageSchema, {
95
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
96
- source,
97
- target,
98
- serviceId,
99
- payload: payload ? bufWkt.anyPack(type, buf.create(type, payload)) : void 0
100
- });
101
- }
102
- };
103
- var toUint8Array = async (data) => {
104
- if (data instanceof Buffer) {
105
- return bufferToArray(data);
106
- }
107
- if (data instanceof Blob) {
108
- return new Uint8Array(await data.arrayBuffer());
109
- }
110
- throw new Error(`Unexpected datatype: ${data}`);
21
+ import { schema } from "@dxos/protocols/proto";
22
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-identity.ts";
23
+ var handleAuthChallenge = async (failedResponse, identity) => {
24
+ invariant(failedResponse.status === 401, void 0, {
25
+ F: __dxlog_file,
26
+ L: 21,
27
+ S: void 0,
28
+ A: [
29
+ "failedResponse.status === 401",
30
+ ""
31
+ ]
32
+ });
33
+ const headerValue = failedResponse.headers.get("Www-Authenticate");
34
+ invariant(headerValue?.startsWith("VerifiablePresentation challenge="), void 0, {
35
+ F: __dxlog_file,
36
+ L: 24,
37
+ S: void 0,
38
+ A: [
39
+ "headerValue?.startsWith('VerifiablePresentation challenge=')",
40
+ ""
41
+ ]
42
+ });
43
+ const challenge = headerValue?.slice("VerifiablePresentation challenge=".length);
44
+ invariant(challenge, void 0, {
45
+ F: __dxlog_file,
46
+ L: 27,
47
+ S: void 0,
48
+ A: [
49
+ "challenge",
50
+ ""
51
+ ]
52
+ });
53
+ const presentation = await identity.presentCredentials({
54
+ challenge: Buffer.from(challenge, "base64")
55
+ });
56
+ return schema.getCodecForType("dxos.halo.credentials.Presentation").encode(presentation);
111
57
  };
112
58
 
113
- // packages/core/mesh/edge-client/src/defs.ts
114
- var protocol = new Protocol([
115
- SwarmRequestSchema,
116
- SwarmResponseSchema,
117
- TextMessageSchema,
118
- AnySchema
119
- ]);
120
-
121
59
  // packages/core/mesh/edge-client/src/errors.ts
122
- var WebsocketClosedError = class extends Error {
60
+ var EdgeConnectionClosedError = class extends Error {
123
61
  constructor() {
124
- super("WebSocket connection closed");
62
+ super("Edge connection closed.");
63
+ }
64
+ };
65
+ var EdgeIdentityChangedError = class extends Error {
66
+ constructor() {
67
+ super("Edge identity changed.");
125
68
  }
126
69
  };
127
70
 
@@ -218,17 +161,25 @@ _ts_decorate([
218
161
  synchronized
219
162
  ], PersistentLifecycle.prototype, "scheduleRestart", null);
220
163
 
164
+ // packages/core/mesh/edge-client/src/utils.ts
165
+ var getEdgeUrlWithProtocol = (baseUrl, protocol2) => {
166
+ const isSecure = baseUrl.startsWith("https") || baseUrl.startsWith("wss");
167
+ const url = new URL(baseUrl);
168
+ url.protocol = protocol2 + (isSecure ? "s" : "");
169
+ return url.toString();
170
+ };
171
+
221
172
  // packages/core/mesh/edge-client/src/edge-client.ts
222
173
  var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-client.ts";
223
174
  var DEFAULT_TIMEOUT = 1e4;
224
175
  var SIGNAL_KEEPALIVE_INTERVAL = 5e3;
225
176
  var EdgeClient = class extends Resource2 {
226
- constructor(_identityKey, _peerKey, _config) {
177
+ constructor(_identity, _config) {
227
178
  super();
228
- this._identityKey = _identityKey;
229
- this._peerKey = _peerKey;
179
+ this._identity = _identity;
230
180
  this._config = _config;
231
181
  this.reconnect = new Event();
182
+ this.connected = new Event();
232
183
  this._persistentLifecycle = new PersistentLifecycle({
233
184
  start: async () => this._openWebSocket(),
234
185
  stop: async () => this._closeWebSocket(),
@@ -239,26 +190,40 @@ var EdgeClient = class extends Resource2 {
239
190
  this._ws = void 0;
240
191
  this._keepaliveCtx = void 0;
241
192
  this._heartBeatContext = void 0;
242
- this._protocol = this._config.protocol ?? protocol;
193
+ this._baseWsUrl = getEdgeUrlWithProtocol(_config.socketEndpoint, "ws");
194
+ this._baseHttpUrl = getEdgeUrlWithProtocol(_config.socketEndpoint, "http");
243
195
  }
244
196
  // TODO(burdon): Attach logging.
245
197
  get info() {
246
198
  return {
247
199
  open: this.isOpen,
248
- identity: this._identityKey,
249
- device: this._peerKey
200
+ identity: this._identity.identityKey,
201
+ device: this._identity.peerKey
250
202
  };
251
203
  }
204
+ get isConnected() {
205
+ return Boolean(this._ws) && this._ready.state === TriggerState.RESOLVED;
206
+ }
252
207
  get identityKey() {
253
- return this._identityKey;
208
+ return this._identity.identityKey;
254
209
  }
255
210
  get peerKey() {
256
- return this._peerKey;
211
+ return this._identity.peerKey;
257
212
  }
258
- setIdentity({ peerKey, identityKey }) {
259
- this._peerKey = peerKey;
260
- this._identityKey = identityKey;
261
- this._persistentLifecycle.scheduleRestart();
213
+ setIdentity(identity) {
214
+ if (identity.identityKey !== this._identity.identityKey || identity.peerKey !== this._identity.peerKey) {
215
+ log2("Edge identity changed", {
216
+ identity,
217
+ oldIdentity: this._identity
218
+ }, {
219
+ F: __dxlog_file3,
220
+ L: 99,
221
+ S: this,
222
+ C: (f, a) => f(...a)
223
+ });
224
+ this._identity = identity;
225
+ this._persistentLifecycle.scheduleRestart();
226
+ }
262
227
  }
263
228
  addListener(listener) {
264
229
  this._listeners.add(listener);
@@ -272,7 +237,7 @@ var EdgeClient = class extends Resource2 {
272
237
  info: this.info
273
238
  }, {
274
239
  F: __dxlog_file3,
275
- L: 101,
240
+ L: 114,
276
241
  S: this,
277
242
  C: (f, a) => f(...a)
278
243
  });
@@ -281,7 +246,7 @@ var EdgeClient = class extends Resource2 {
281
246
  err
282
247
  }, {
283
248
  F: __dxlog_file3,
284
- L: 103,
249
+ L: 116,
285
250
  S: this,
286
251
  C: (f, a) => f(...a)
287
252
  });
@@ -292,31 +257,48 @@ var EdgeClient = class extends Resource2 {
292
257
  */
293
258
  async _close() {
294
259
  log2("closing...", {
295
- peerKey: this._peerKey
260
+ peerKey: this._identity.peerKey
296
261
  }, {
297
262
  F: __dxlog_file3,
298
- L: 111,
263
+ L: 124,
299
264
  S: this,
300
265
  C: (f, a) => f(...a)
301
266
  });
302
267
  await this._persistentLifecycle.close();
303
268
  }
304
269
  async _openWebSocket() {
305
- const url = new URL(`/ws/${this._identityKey}/${this._peerKey}`, this._config.socketEndpoint);
306
- this._ws = new WebSocket(url);
270
+ if (this._ctx.disposed) {
271
+ return;
272
+ }
273
+ const path = `/ws/${this._identity.identityKey}/${this._identity.peerKey}`;
274
+ const protocolHeader = this._config.disableAuth ? void 0 : await this._createAuthHeader(path);
275
+ const url = new URL(path, this._baseWsUrl);
276
+ log2("Opening websocket", {
277
+ url: url.toString(),
278
+ protocolHeader
279
+ }, {
280
+ F: __dxlog_file3,
281
+ L: 136,
282
+ S: this,
283
+ C: (f, a) => f(...a)
284
+ });
285
+ this._ws = new WebSocket(url, protocolHeader ? [
286
+ protocolHeader
287
+ ] : []);
307
288
  this._ws.onopen = () => {
308
289
  log2("opened", this.info, {
309
290
  F: __dxlog_file3,
310
- L: 120,
291
+ L: 140,
311
292
  S: this,
312
293
  C: (f, a) => f(...a)
313
294
  });
314
295
  this._ready.wake();
296
+ this.connected.emit();
315
297
  };
316
298
  this._ws.onclose = () => {
317
299
  log2("closed", this.info, {
318
300
  F: __dxlog_file3,
319
- L: 124,
301
+ L: 145,
320
302
  S: this,
321
303
  C: (f, a) => f(...a)
322
304
  });
@@ -328,7 +310,7 @@ var EdgeClient = class extends Resource2 {
328
310
  info: event.message
329
311
  }, {
330
312
  F: __dxlog_file3,
331
- L: 128,
313
+ L: 149,
332
314
  S: this,
333
315
  C: (f, a) => f(...a)
334
316
  });
@@ -340,13 +322,13 @@ var EdgeClient = class extends Resource2 {
340
322
  return;
341
323
  }
342
324
  const data = await toUint8Array(event.data);
343
- const message = buf2.fromBinary(MessageSchema2, data);
325
+ const message = buf.fromBinary(MessageSchema, data);
344
326
  log2("received", {
345
- peerKey: this._peerKey,
327
+ peerKey: this._identity.peerKey,
346
328
  payload: protocol.getPayloadType(message)
347
329
  }, {
348
330
  F: __dxlog_file3,
349
- L: 141,
331
+ L: 162,
350
332
  S: this,
351
333
  C: (f, a) => f(...a)
352
334
  });
@@ -360,7 +342,7 @@ var EdgeClient = class extends Resource2 {
360
342
  payload: protocol.getPayloadType(message)
361
343
  }, {
362
344
  F: __dxlog_file3,
363
- L: 147,
345
+ L: 168,
364
346
  S: this,
365
347
  C: (f, a) => f(...a)
366
348
  });
@@ -371,9 +353,18 @@ var EdgeClient = class extends Resource2 {
371
353
  await this._ready.wait({
372
354
  timeout: this._config.timeout ?? DEFAULT_TIMEOUT
373
355
  });
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
+ });
374
365
  this._keepaliveCtx = new Context(void 0, {
375
366
  F: __dxlog_file3,
376
- L: 154
367
+ L: 179
377
368
  });
378
369
  scheduleTaskInterval(this._keepaliveCtx, async () => {
379
370
  this._ws?.send("__ping__");
@@ -386,7 +377,7 @@ var EdgeClient = class extends Resource2 {
386
377
  return;
387
378
  }
388
379
  try {
389
- this._ready.throw(new WebsocketClosedError());
380
+ this._ready.throw(this.isOpen ? new EdgeIdentityChangedError() : new EdgeConnectionClosedError());
390
381
  this._ready.reset();
391
382
  void this._keepaliveCtx?.dispose();
392
383
  this._keepaliveCtx = void 0;
@@ -408,7 +399,7 @@ var EdgeClient = class extends Resource2 {
408
399
  err
409
400
  }, {
410
401
  F: __dxlog_file3,
411
- L: 190,
402
+ L: 215,
412
403
  S: this,
413
404
  C: (f, a) => f(...a)
414
405
  });
@@ -420,38 +411,32 @@ var EdgeClient = class extends Resource2 {
420
411
  */
421
412
  async send(message) {
422
413
  if (this._ready.state !== TriggerState.RESOLVED) {
414
+ log2("waiting for websocket to become ready", void 0, {
415
+ F: __dxlog_file3,
416
+ L: 225,
417
+ S: this,
418
+ C: (f, a) => f(...a)
419
+ });
423
420
  await this._ready.wait({
424
421
  timeout: this._config.timeout ?? DEFAULT_TIMEOUT
425
422
  });
426
423
  }
427
- invariant2(this._ws, void 0, {
428
- F: __dxlog_file3,
429
- L: 202,
430
- S: this,
431
- A: [
432
- "this._ws",
433
- ""
434
- ]
435
- });
436
- invariant2(!message.source || message.source.peerKey === this._peerKey, void 0, {
437
- F: __dxlog_file3,
438
- L: 203,
439
- S: this,
440
- A: [
441
- "!message.source || message.source.peerKey === this._peerKey",
442
- ""
443
- ]
444
- });
424
+ if (!this._ws) {
425
+ throw new EdgeConnectionClosedError();
426
+ }
427
+ if (message.source && (message.source.peerKey !== this._identity.peerKey || message.source.identityKey !== this.identityKey)) {
428
+ throw new EdgeIdentityChangedError();
429
+ }
445
430
  log2("sending...", {
446
- peerKey: this._peerKey,
431
+ peerKey: this._identity.peerKey,
447
432
  payload: protocol.getPayloadType(message)
448
433
  }, {
449
434
  F: __dxlog_file3,
450
- L: 204,
435
+ L: 238,
451
436
  S: this,
452
437
  C: (f, a) => f(...a)
453
438
  });
454
- this._ws.send(buf2.toBinary(MessageSchema2, message));
439
+ this._ws.send(buf.toBinary(MessageSchema, message));
455
440
  }
456
441
  _onHeartbeat() {
457
442
  if (this._lifecycleState !== LifecycleState2.OPEN) {
@@ -460,17 +445,341 @@ var EdgeClient = class extends Resource2 {
460
445
  void this._heartBeatContext?.dispose();
461
446
  this._heartBeatContext = new Context(void 0, {
462
447
  F: __dxlog_file3,
463
- L: 213
448
+ L: 247
464
449
  });
465
450
  scheduleTask(this._heartBeatContext, () => {
466
451
  this._persistentLifecycle.scheduleRestart();
467
452
  }, 2 * SIGNAL_KEEPALIVE_INTERVAL);
468
453
  }
454
+ async _createAuthHeader(path) {
455
+ const httpUrl = new URL(path, this._baseHttpUrl);
456
+ httpUrl.protocol = getEdgeUrlWithProtocol(this._baseWsUrl.toString(), "http");
457
+ const response = await fetch(httpUrl, {
458
+ method: "GET"
459
+ });
460
+ if (response.status === 401) {
461
+ return encodePresentationWsAuthHeader(await handleAuthChallenge(response, this._identity));
462
+ } else {
463
+ log2.warn("no auth challenge from edge", {
464
+ status: response.status,
465
+ statusText: response.statusText
466
+ }, {
467
+ F: __dxlog_file3,
468
+ L: 264,
469
+ S: this,
470
+ C: (f, a) => f(...a)
471
+ });
472
+ return void 0;
473
+ }
474
+ }
475
+ };
476
+ var encodePresentationWsAuthHeader = (encodedPresentation) => {
477
+ const encodedToken = Buffer.from(encodedPresentation).toString("base64").replace(/=*$/, "").replaceAll("/", "|");
478
+ return `base64url.bearer.authorization.dxos.org.${encodedToken}`;
479
+ };
480
+
481
+ // packages/core/mesh/edge-client/src/auth.ts
482
+ import { createCredential, signPresentation } from "@dxos/credentials";
483
+ import { Keyring } from "@dxos/keyring";
484
+ import { PublicKey } from "@dxos/keys";
485
+ var createDeviceEdgeIdentity = async (signer, key) => {
486
+ return {
487
+ identityKey: key.toHex(),
488
+ peerKey: key.toHex(),
489
+ presentCredentials: async ({ challenge }) => {
490
+ return signPresentation({
491
+ presentation: {
492
+ credentials: [
493
+ // Verifier requires at least one credential in the presentation to establish the subject.
494
+ await createCredential({
495
+ assertion: {
496
+ "@type": "dxos.halo.credentials.Auth"
497
+ },
498
+ issuer: key,
499
+ subject: key,
500
+ signer
501
+ })
502
+ ]
503
+ },
504
+ signer,
505
+ signerKey: key,
506
+ nonce: challenge
507
+ });
508
+ }
509
+ };
510
+ };
511
+ var createChainEdgeIdentity = async (signer, identityKey, peerKey, chain, credentials) => {
512
+ const credentialsToSign = credentials.length > 0 ? credentials : [
513
+ await createCredential({
514
+ assertion: {
515
+ "@type": "dxos.halo.credentials.Auth"
516
+ },
517
+ issuer: identityKey,
518
+ subject: identityKey,
519
+ signer,
520
+ chain,
521
+ signingKey: peerKey
522
+ })
523
+ ];
524
+ return {
525
+ identityKey: identityKey.toHex(),
526
+ peerKey: peerKey.toHex(),
527
+ presentCredentials: async ({ challenge }) => {
528
+ return signPresentation({
529
+ presentation: {
530
+ credentials: credentialsToSign
531
+ },
532
+ signer,
533
+ nonce: challenge,
534
+ signerKey: peerKey,
535
+ chain
536
+ });
537
+ }
538
+ };
539
+ };
540
+ var createEphemeralEdgeIdentity = async () => {
541
+ const keyring = new Keyring();
542
+ const key = await keyring.createKey();
543
+ return createDeviceEdgeIdentity(keyring, key);
544
+ };
545
+ var createTestHaloEdgeIdentity = async (signer, identityKey, deviceKey) => {
546
+ const deviceAdmission = await createCredential({
547
+ assertion: {
548
+ "@type": "dxos.halo.credentials.AuthorizedDevice",
549
+ deviceKey,
550
+ identityKey
551
+ },
552
+ issuer: identityKey,
553
+ subject: deviceKey,
554
+ signer
555
+ });
556
+ return createChainEdgeIdentity(signer, identityKey, deviceKey, {
557
+ credential: deviceAdmission
558
+ }, [
559
+ await createCredential({
560
+ assertion: {
561
+ "@type": "dxos.halo.credentials.Auth"
562
+ },
563
+ issuer: identityKey,
564
+ subject: identityKey,
565
+ signer
566
+ })
567
+ ]);
568
+ };
569
+ var createStubEdgeIdentity = () => {
570
+ const identityKey = PublicKey.random();
571
+ const deviceKey = PublicKey.random();
572
+ return {
573
+ identityKey: identityKey.toHex(),
574
+ peerKey: deviceKey.toHex(),
575
+ presentCredentials: async () => {
576
+ throw new Error("Stub identity does not support authentication.");
577
+ }
578
+ };
579
+ };
580
+
581
+ // packages/core/mesh/edge-client/src/edge-http-client.ts
582
+ import { sleep as sleep2 } from "@dxos/async";
583
+ import { Context as Context2 } from "@dxos/context";
584
+ import { log as log3 } from "@dxos/log";
585
+ 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";
587
+ var DEFAULT_RETRY_TIMEOUT = 1500;
588
+ var DEFAULT_RETRY_JITTER = 500;
589
+ var DEFAULT_MAX_RETRIES_COUNT = 3;
590
+ var EdgeHttpClient = class {
591
+ constructor(baseUrl) {
592
+ this._baseUrl = getEdgeUrlWithProtocol(baseUrl, "http");
593
+ log3("created", {
594
+ url: this._baseUrl
595
+ }, {
596
+ F: __dxlog_file4,
597
+ L: 42,
598
+ S: this,
599
+ C: (f, a) => f(...a)
600
+ });
601
+ }
602
+ setIdentity(identity) {
603
+ this._edgeIdentity = identity;
604
+ }
605
+ createAgent(body, args) {
606
+ return this._call("/agents/create", {
607
+ ...args,
608
+ method: "POST",
609
+ body
610
+ });
611
+ }
612
+ getAgentStatus(request, args) {
613
+ return this._call(`/users/${request.ownerIdentityKey.toHex()}/agent/status`, {
614
+ ...args,
615
+ method: "GET"
616
+ });
617
+ }
618
+ getCredentialsForNotarization(spaceId, args) {
619
+ return this._call(`/spaces/${spaceId}/notarization`, {
620
+ ...args,
621
+ method: "GET"
622
+ });
623
+ }
624
+ async notarizeCredentials(spaceId, body, args) {
625
+ await this._call(`/spaces/${spaceId}/notarization`, {
626
+ ...args,
627
+ body,
628
+ method: "POST"
629
+ });
630
+ }
631
+ async joinSpaceByInvitation(spaceId, body, args) {
632
+ return this._call(`/spaces/${spaceId}/join`, {
633
+ ...args,
634
+ body,
635
+ method: "POST"
636
+ });
637
+ }
638
+ async recoverIdentity(body, args) {
639
+ return this._call("/identity/recover", {
640
+ ...args,
641
+ body,
642
+ method: "POST"
643
+ });
644
+ }
645
+ async _call(path, args) {
646
+ const requestContext = args.context ?? new Context2(void 0, {
647
+ F: __dxlog_file4,
648
+ L: 88
649
+ });
650
+ const shouldRetry = createRetryHandler(args);
651
+ const url = `${this._baseUrl}${path.startsWith("/") ? path.slice(1) : path}`;
652
+ log3.info("call", {
653
+ method: args.method,
654
+ path,
655
+ request: args.body
656
+ }, {
657
+ F: __dxlog_file4,
658
+ L: 92,
659
+ S: this,
660
+ C: (f, a) => f(...a)
661
+ });
662
+ let handledAuth = false;
663
+ let authHeader = this._authHeader;
664
+ while (true) {
665
+ let processingError;
666
+ let retryAfterHeaderValue = Number.NaN;
667
+ try {
668
+ const request = createRequest(args, authHeader);
669
+ const response = await fetch(url, request);
670
+ retryAfterHeaderValue = Number(response.headers.get("Retry-After"));
671
+ if (response.ok) {
672
+ const body = await response.json();
673
+ if (body.success) {
674
+ return body.data;
675
+ }
676
+ log3.info("unsuccessful edge response", {
677
+ path,
678
+ body
679
+ }, {
680
+ F: __dxlog_file4,
681
+ L: 111,
682
+ S: this,
683
+ C: (f, a) => f(...a)
684
+ });
685
+ if (body.errorData?.type === "auth_challenge" && typeof body.errorData?.challenge === "string") {
686
+ processingError = new EdgeAuthChallengeError(body.errorData.challenge, body.errorData);
687
+ } else {
688
+ processingError = EdgeCallFailedError.fromUnsuccessfulResponse(response, body);
689
+ }
690
+ } else if (response.status === 401 && !handledAuth) {
691
+ authHeader = await this._handleUnauthorized(response);
692
+ handledAuth = true;
693
+ continue;
694
+ } else {
695
+ processingError = EdgeCallFailedError.fromHttpFailure(response);
696
+ }
697
+ } catch (error) {
698
+ processingError = EdgeCallFailedError.fromProcessingFailureCause(error);
699
+ }
700
+ if (processingError.isRetryable && await shouldRetry(requestContext, retryAfterHeaderValue)) {
701
+ log3.info("retrying edge request", {
702
+ path,
703
+ processingError
704
+ }, {
705
+ F: __dxlog_file4,
706
+ L: 130,
707
+ S: this,
708
+ C: (f, a) => f(...a)
709
+ });
710
+ } else {
711
+ throw processingError;
712
+ }
713
+ }
714
+ }
715
+ async _handleUnauthorized(response) {
716
+ if (!this._edgeIdentity) {
717
+ log3.warn("edge unauthorized response received before identity was set", void 0, {
718
+ F: __dxlog_file4,
719
+ L: 139,
720
+ S: this,
721
+ C: (f, a) => f(...a)
722
+ });
723
+ throw EdgeCallFailedError.fromHttpFailure(response);
724
+ }
725
+ const challenge = await handleAuthChallenge(response, this._edgeIdentity);
726
+ this._authHeader = encodeAuthHeader(challenge);
727
+ log3("auth header updated", void 0, {
728
+ F: __dxlog_file4,
729
+ L: 144,
730
+ S: this,
731
+ C: (f, a) => f(...a)
732
+ });
733
+ return this._authHeader;
734
+ }
735
+ };
736
+ var createRetryHandler = (args) => {
737
+ if (!args.retry || args.retry.count < 1) {
738
+ return async () => false;
739
+ }
740
+ let retries = 0;
741
+ const maxRetries = args.retry.count ?? DEFAULT_MAX_RETRIES_COUNT;
742
+ const baseTimeout = args.retry.timeout ?? DEFAULT_RETRY_TIMEOUT;
743
+ const jitter = args.retry.jitter ?? DEFAULT_RETRY_JITTER;
744
+ return async (ctx, retryAfter) => {
745
+ if (++retries > maxRetries || ctx.disposed) {
746
+ return false;
747
+ }
748
+ if (retryAfter) {
749
+ await sleep2(retryAfter);
750
+ } else {
751
+ const timeout = baseTimeout + Math.random() * jitter;
752
+ await sleep2(timeout);
753
+ }
754
+ return true;
755
+ };
756
+ };
757
+ var createRequest = (args, authHeader) => {
758
+ return {
759
+ method: args.method,
760
+ body: args.body && JSON.stringify(args.body),
761
+ headers: authHeader ? {
762
+ Authorization: authHeader
763
+ } : void 0
764
+ };
765
+ };
766
+ var encodeAuthHeader = (challenge) => {
767
+ const encodedChallenge = Buffer.from(challenge).toString("base64");
768
+ return `VerifiablePresentation pb;base64,${encodedChallenge}`;
469
769
  };
470
770
  export {
471
771
  EdgeClient,
772
+ EdgeConnectionClosedError,
773
+ EdgeHttpClient,
774
+ EdgeIdentityChangedError,
472
775
  Protocol,
776
+ createChainEdgeIdentity,
777
+ createDeviceEdgeIdentity,
778
+ createEphemeralEdgeIdentity,
779
+ createStubEdgeIdentity,
780
+ createTestHaloEdgeIdentity,
473
781
  getTypename,
782
+ handleAuthChallenge,
474
783
  protocol,
475
784
  toUint8Array
476
785
  };