@dxos/edge-client 0.8.3 → 0.8.4-main.1068cf700f

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 (66) hide show
  1. package/dist/lib/{browser/chunk-VHS3XEIX.mjs → neutral/chunk-VESGVCLQ.mjs} +15 -11
  2. package/dist/lib/{browser/chunk-VHS3XEIX.mjs.map → neutral/chunk-VESGVCLQ.mjs.map} +3 -3
  3. package/dist/lib/{browser → neutral}/edge-ws-muxer.mjs +1 -1
  4. package/dist/lib/{browser → neutral}/index.mjs +694 -354
  5. package/dist/lib/neutral/index.mjs.map +7 -0
  6. package/dist/lib/neutral/meta.json +1 -0
  7. package/dist/lib/{browser → neutral}/testing/index.mjs +61 -16
  8. package/dist/lib/neutral/testing/index.mjs.map +7 -0
  9. package/dist/types/src/edge-client.d.ts +15 -15
  10. package/dist/types/src/edge-client.d.ts.map +1 -1
  11. package/dist/types/src/edge-http-client.d.ts +59 -31
  12. package/dist/types/src/edge-http-client.d.ts.map +1 -1
  13. package/dist/types/src/edge-http-client.test.d.ts +2 -0
  14. package/dist/types/src/edge-http-client.test.d.ts.map +1 -0
  15. package/dist/types/src/edge-ws-connection.d.ts +20 -0
  16. package/dist/types/src/edge-ws-connection.d.ts.map +1 -1
  17. package/dist/types/src/edge-ws-muxer.d.ts.map +1 -1
  18. package/dist/types/src/http-client.d.ts +25 -0
  19. package/dist/types/src/http-client.d.ts.map +1 -0
  20. package/dist/types/src/http-client.test.d.ts +2 -0
  21. package/dist/types/src/http-client.test.d.ts.map +1 -0
  22. package/dist/types/src/index.d.ts +4 -3
  23. package/dist/types/src/index.d.ts.map +1 -1
  24. package/dist/types/src/testing/index.d.ts +1 -0
  25. package/dist/types/src/testing/index.d.ts.map +1 -1
  26. package/dist/types/src/testing/test-server.d.ts +9 -0
  27. package/dist/types/src/testing/test-server.d.ts.map +1 -0
  28. package/dist/types/src/testing/test-utils.d.ts +3 -3
  29. package/dist/types/src/testing/test-utils.d.ts.map +1 -1
  30. package/dist/types/tsconfig.tsbuildinfo +1 -1
  31. package/package.json +30 -21
  32. package/src/edge-client.test.ts +4 -4
  33. package/src/edge-client.ts +73 -42
  34. package/src/edge-http-client.test.ts +22 -0
  35. package/src/edge-http-client.ts +368 -152
  36. package/src/edge-ws-connection.ts +129 -8
  37. package/src/edge-ws-muxer.ts +1 -1
  38. package/src/http-client.test.ts +58 -0
  39. package/src/http-client.ts +77 -0
  40. package/src/index.ts +4 -3
  41. package/src/testing/index.ts +1 -0
  42. package/src/testing/test-server.ts +45 -0
  43. package/src/testing/test-utils.ts +9 -9
  44. package/src/websocket.test.ts +1 -1
  45. package/dist/lib/browser/index.mjs.map +0 -7
  46. package/dist/lib/browser/meta.json +0 -1
  47. package/dist/lib/browser/testing/index.mjs.map +0 -7
  48. package/dist/lib/node/chunk-XNHBUTNB.cjs +0 -317
  49. package/dist/lib/node/chunk-XNHBUTNB.cjs.map +0 -7
  50. package/dist/lib/node/edge-ws-muxer.cjs +0 -33
  51. package/dist/lib/node/edge-ws-muxer.cjs.map +0 -7
  52. package/dist/lib/node/index.cjs +0 -1060
  53. package/dist/lib/node/index.cjs.map +0 -7
  54. package/dist/lib/node/meta.json +0 -1
  55. package/dist/lib/node/testing/index.cjs +0 -169
  56. package/dist/lib/node/testing/index.cjs.map +0 -7
  57. package/dist/lib/node-esm/chunk-HGQUUFIJ.mjs +0 -299
  58. package/dist/lib/node-esm/chunk-HGQUUFIJ.mjs.map +0 -7
  59. package/dist/lib/node-esm/edge-ws-muxer.mjs +0 -12
  60. package/dist/lib/node-esm/edge-ws-muxer.mjs.map +0 -7
  61. package/dist/lib/node-esm/index.mjs +0 -1035
  62. package/dist/lib/node-esm/index.mjs.map +0 -7
  63. package/dist/lib/node-esm/meta.json +0 -1
  64. package/dist/lib/node-esm/testing/index.mjs +0 -141
  65. package/dist/lib/node-esm/testing/index.mjs.map +0 -7
  66. /package/dist/lib/{browser → neutral}/edge-ws-muxer.mjs.map +0 -0
@@ -6,24 +6,135 @@ import {
6
6
  getTypename,
7
7
  protocol,
8
8
  toUint8Array
9
- } from "./chunk-VHS3XEIX.mjs";
9
+ } from "./chunk-VESGVCLQ.mjs";
10
10
 
11
- // packages/core/mesh/edge-client/src/index.ts
11
+ // src/index.ts
12
12
  export * from "@dxos/protocols/buf/dxos/edge/messenger_pb";
13
13
 
14
- // packages/core/mesh/edge-client/src/edge-client.ts
15
- import { Trigger, scheduleMicroTask, TriggerState, PersistentLifecycle, Event } from "@dxos/async";
14
+ // src/auth.ts
15
+ import { createCredential, signPresentation } from "@dxos/credentials";
16
+ import { invariant } from "@dxos/invariant";
17
+ import { Keyring } from "@dxos/keyring";
18
+ import { PublicKey } from "@dxos/keys";
19
+ var __dxlog_file = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/auth.ts";
20
+ var createDeviceEdgeIdentity = async (signer, key) => {
21
+ return {
22
+ identityKey: key.toHex(),
23
+ peerKey: key.toHex(),
24
+ presentCredentials: async ({ challenge }) => {
25
+ return signPresentation({
26
+ presentation: {
27
+ credentials: [
28
+ // Verifier requires at least one credential in the presentation to establish the subject.
29
+ await createCredential({
30
+ assertion: {
31
+ "@type": "dxos.halo.credentials.Auth"
32
+ },
33
+ issuer: key,
34
+ subject: key,
35
+ signer
36
+ })
37
+ ]
38
+ },
39
+ signer,
40
+ signerKey: key,
41
+ nonce: challenge
42
+ });
43
+ }
44
+ };
45
+ };
46
+ var createChainEdgeIdentity = async (signer, identityKey, peerKey, chain, credentials) => {
47
+ const credentialsToSign = credentials.length > 0 ? credentials : [
48
+ await createCredential({
49
+ assertion: {
50
+ "@type": "dxos.halo.credentials.Auth"
51
+ },
52
+ issuer: identityKey,
53
+ subject: identityKey,
54
+ signer,
55
+ chain,
56
+ signingKey: peerKey
57
+ })
58
+ ];
59
+ return {
60
+ identityKey: identityKey.toHex(),
61
+ peerKey: peerKey.toHex(),
62
+ presentCredentials: async ({ challenge }) => {
63
+ invariant(chain, void 0, {
64
+ F: __dxlog_file,
65
+ L: 75,
66
+ S: void 0,
67
+ A: [
68
+ "chain",
69
+ ""
70
+ ]
71
+ });
72
+ return signPresentation({
73
+ presentation: {
74
+ credentials: credentialsToSign
75
+ },
76
+ signer,
77
+ nonce: challenge,
78
+ signerKey: peerKey,
79
+ chain
80
+ });
81
+ }
82
+ };
83
+ };
84
+ var createEphemeralEdgeIdentity = async () => {
85
+ const keyring = new Keyring();
86
+ const key = await keyring.createKey();
87
+ return createDeviceEdgeIdentity(keyring, key);
88
+ };
89
+ var createTestHaloEdgeIdentity = async (signer, identityKey, deviceKey) => {
90
+ const deviceAdmission = await createCredential({
91
+ assertion: {
92
+ "@type": "dxos.halo.credentials.AuthorizedDevice",
93
+ deviceKey,
94
+ identityKey
95
+ },
96
+ issuer: identityKey,
97
+ subject: deviceKey,
98
+ signer
99
+ });
100
+ return createChainEdgeIdentity(signer, identityKey, deviceKey, {
101
+ credential: deviceAdmission
102
+ }, [
103
+ await createCredential({
104
+ assertion: {
105
+ "@type": "dxos.halo.credentials.Auth"
106
+ },
107
+ issuer: identityKey,
108
+ subject: identityKey,
109
+ signer
110
+ })
111
+ ]);
112
+ };
113
+ var createStubEdgeIdentity = () => {
114
+ const identityKey = PublicKey.random();
115
+ const deviceKey = PublicKey.random();
116
+ return {
117
+ identityKey: identityKey.toHex(),
118
+ peerKey: deviceKey.toHex(),
119
+ presentCredentials: async () => {
120
+ throw new Error("Stub identity does not support authentication.");
121
+ }
122
+ };
123
+ };
124
+
125
+ // src/edge-client.ts
126
+ import { Event, PersistentLifecycle, Trigger, TriggerState, scheduleMicroTask, scheduleTaskInterval as scheduleTaskInterval2 } from "@dxos/async";
16
127
  import { Resource as Resource2 } from "@dxos/context";
17
128
  import { log as log2, logInfo as logInfo2 } from "@dxos/log";
18
129
  import { EdgeStatus } from "@dxos/protocols/proto/dxos/client/services";
19
130
 
20
- // packages/core/mesh/edge-client/src/edge-identity.ts
21
- import { invariant } from "@dxos/invariant";
131
+ // src/edge-identity.ts
132
+ import { invariant as invariant2 } from "@dxos/invariant";
22
133
  import { schema } from "@dxos/protocols/proto";
23
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-identity.ts";
134
+ var __dxlog_file2 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/edge-identity.ts";
24
135
  var handleAuthChallenge = async (failedResponse, identity) => {
25
- invariant(failedResponse.status === 401, void 0, {
26
- F: __dxlog_file,
136
+ invariant2(failedResponse.status === 401, void 0, {
137
+ F: __dxlog_file2,
27
138
  L: 21,
28
139
  S: void 0,
29
140
  A: [
@@ -32,8 +143,8 @@ var handleAuthChallenge = async (failedResponse, identity) => {
32
143
  ]
33
144
  });
34
145
  const headerValue = failedResponse.headers.get("Www-Authenticate");
35
- invariant(headerValue?.startsWith("VerifiablePresentation challenge="), void 0, {
36
- F: __dxlog_file,
146
+ invariant2(headerValue?.startsWith("VerifiablePresentation challenge="), void 0, {
147
+ F: __dxlog_file2,
37
148
  L: 24,
38
149
  S: void 0,
39
150
  A: [
@@ -42,8 +153,8 @@ var handleAuthChallenge = async (failedResponse, identity) => {
42
153
  ]
43
154
  });
44
155
  const challenge = headerValue?.slice("VerifiablePresentation challenge=".length);
45
- invariant(challenge, void 0, {
46
- F: __dxlog_file,
156
+ invariant2(challenge, void 0, {
157
+ F: __dxlog_file2,
47
158
  L: 27,
48
159
  S: void 0,
49
160
  A: [
@@ -57,11 +168,11 @@ var handleAuthChallenge = async (failedResponse, identity) => {
57
168
  return schema.getCodecForType("dxos.halo.credentials.Presentation").encode(presentation);
58
169
  };
59
170
 
60
- // packages/core/mesh/edge-client/src/edge-ws-connection.ts
171
+ // src/edge-ws-connection.ts
61
172
  import WebSocket from "isomorphic-ws";
62
173
  import { scheduleTask, scheduleTaskInterval } from "@dxos/async";
63
174
  import { Context, Resource } from "@dxos/context";
64
- import { invariant as invariant2 } from "@dxos/invariant";
175
+ import { invariant as invariant3 } from "@dxos/invariant";
65
176
  import { log, logInfo } from "@dxos/log";
66
177
  import { EdgeWebsocketProtocol } from "@dxos/protocols";
67
178
  import { buf } from "@dxos/protocols/buf";
@@ -72,10 +183,29 @@ function _ts_decorate(decorators, target, key, desc) {
72
183
  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;
73
184
  return c > 3 && r && Object.defineProperty(target, key, r), r;
74
185
  }
75
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-ws-connection.ts";
186
+ var __dxlog_file3 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/edge-ws-connection.ts";
76
187
  var SIGNAL_KEEPALIVE_INTERVAL = 4e3;
77
188
  var SIGNAL_KEEPALIVE_TIMEOUT = 12e3;
78
189
  var EdgeWsConnection = class extends Resource {
190
+ _identity;
191
+ _connectionInfo;
192
+ _callbacks;
193
+ _inactivityTimeoutCtx;
194
+ _ws;
195
+ _wsMuxer;
196
+ _lastReceivedMessageTimestamp = Date.now();
197
+ _openTimestamp;
198
+ // Latency tracking.
199
+ _pingTimestamp;
200
+ _rtt = 0;
201
+ // Rate tracking with sliding window.
202
+ _uploadRate = 0;
203
+ _downloadRate = 0;
204
+ _rateWindow = 1e4;
205
+ _rateUpdateInterval = 1e3;
206
+ _bytesSamples = [];
207
+ _messagesSent = 0;
208
+ _messagesReceived = 0;
79
209
  constructor(_identity, _connectionInfo, _callbacks) {
80
210
  super(), this._identity = _identity, this._connectionInfo = _connectionInfo, this._callbacks = _callbacks;
81
211
  }
@@ -86,19 +216,37 @@ var EdgeWsConnection = class extends Resource {
86
216
  device: this._identity.peerKey
87
217
  };
88
218
  }
219
+ get rtt() {
220
+ return this._rtt;
221
+ }
222
+ get uptime() {
223
+ return this._openTimestamp ? (Date.now() - this._openTimestamp) / 1e3 : 0;
224
+ }
225
+ get uploadRate() {
226
+ return this._uploadRate;
227
+ }
228
+ get downloadRate() {
229
+ return this._downloadRate;
230
+ }
231
+ get messagesSent() {
232
+ return this._messagesSent;
233
+ }
234
+ get messagesReceived() {
235
+ return this._messagesReceived;
236
+ }
89
237
  send(message) {
90
- invariant2(this._ws, void 0, {
91
- F: __dxlog_file2,
92
- L: 52,
238
+ invariant3(this._ws, void 0, {
239
+ F: __dxlog_file3,
240
+ L: 93,
93
241
  S: this,
94
242
  A: [
95
243
  "this._ws",
96
244
  ""
97
245
  ]
98
246
  });
99
- invariant2(this._wsMuxer, void 0, {
100
- F: __dxlog_file2,
101
- L: 53,
247
+ invariant3(this._wsMuxer, void 0, {
248
+ F: __dxlog_file3,
249
+ L: 94,
102
250
  S: this,
103
251
  A: [
104
252
  "this._wsMuxer",
@@ -109,11 +257,12 @@ var EdgeWsConnection = class extends Resource {
109
257
  peerKey: this._identity.peerKey,
110
258
  payload: protocol.getPayloadType(message)
111
259
  }, {
112
- F: __dxlog_file2,
113
- L: 54,
260
+ F: __dxlog_file3,
261
+ L: 95,
114
262
  S: this,
115
263
  C: (f, a) => f(...a)
116
264
  });
265
+ this._messagesSent++;
117
266
  if (this._ws?.protocol.includes(EdgeWebsocketProtocol.V0)) {
118
267
  const binary = buf.toBinary(MessageSchema, message);
119
268
  if (binary.length > CLOUDFLARE_MESSAGE_MAX_BYTES) {
@@ -122,18 +271,21 @@ var EdgeWsConnection = class extends Resource {
122
271
  serviceId: message.serviceId,
123
272
  payload: protocol.getPayloadType(message)
124
273
  }, {
125
- F: __dxlog_file2,
126
- L: 58,
274
+ F: __dxlog_file3,
275
+ L: 100,
127
276
  S: this,
128
277
  C: (f, a) => f(...a)
129
278
  });
130
279
  return;
131
280
  }
281
+ this._recordBytes(binary.byteLength, 0);
132
282
  this._ws.send(binary);
133
283
  } else {
284
+ const binary = buf.toBinary(MessageSchema, message);
285
+ this._recordBytes(binary.byteLength, 0);
134
286
  this._wsMuxer.send(message).catch((e) => log.catch(e, void 0, {
135
- F: __dxlog_file2,
136
- L: 67,
287
+ F: __dxlog_file3,
288
+ L: 113,
137
289
  S: this,
138
290
  C: (f, a) => f(...a)
139
291
  }));
@@ -154,19 +306,21 @@ var EdgeWsConnection = class extends Resource {
154
306
  this._ws.onopen = () => {
155
307
  if (this.isOpen) {
156
308
  log("connected", void 0, {
157
- F: __dxlog_file2,
158
- L: 84,
309
+ F: __dxlog_file3,
310
+ L: 130,
159
311
  S: this,
160
312
  C: (f, a) => f(...a)
161
313
  });
314
+ this._openTimestamp = Date.now();
162
315
  this._callbacks.onConnected();
163
316
  this._scheduleHeartbeats();
317
+ this._scheduleRateCalculation();
164
318
  } else {
165
319
  log.verbose("connected after becoming inactive", {
166
320
  currentIdentity: this._identity
167
321
  }, {
168
- F: __dxlog_file2,
169
- L: 88,
322
+ F: __dxlog_file3,
323
+ L: 136,
170
324
  S: this,
171
325
  C: (f, a) => f(...a)
172
326
  });
@@ -174,12 +328,12 @@ var EdgeWsConnection = class extends Resource {
174
328
  };
175
329
  this._ws.onclose = (event) => {
176
330
  if (this.isOpen) {
177
- log.warn("disconnected while being open", {
331
+ log.warn("server disconnected", {
178
332
  code: event.code,
179
333
  reason: event.reason
180
334
  }, {
181
- F: __dxlog_file2,
182
- L: 93,
335
+ F: __dxlog_file3,
336
+ L: 141,
183
337
  S: this,
184
338
  C: (f, a) => f(...a)
185
339
  });
@@ -193,8 +347,8 @@ var EdgeWsConnection = class extends Resource {
193
347
  error: event.error,
194
348
  info: event.message
195
349
  }, {
196
- F: __dxlog_file2,
197
- L: 100,
350
+ F: __dxlog_file3,
351
+ L: 148,
198
352
  S: this,
199
353
  C: (f, a) => f(...a)
200
354
  });
@@ -203,8 +357,8 @@ var EdgeWsConnection = class extends Resource {
203
357
  log.verbose("error ignored on closed connection", {
204
358
  error: event.error
205
359
  }, {
206
- F: __dxlog_file2,
207
- L: 103,
360
+ F: __dxlog_file3,
361
+ L: 151,
208
362
  S: this,
209
363
  C: (f, a) => f(...a)
210
364
  });
@@ -215,29 +369,36 @@ var EdgeWsConnection = class extends Resource {
215
369
  log.verbose("message ignored on closed connection", {
216
370
  event: event.type
217
371
  }, {
218
- F: __dxlog_file2,
219
- L: 111,
372
+ F: __dxlog_file3,
373
+ L: 159,
220
374
  S: this,
221
375
  C: (f, a) => f(...a)
222
376
  });
223
377
  return;
224
378
  }
379
+ this._lastReceivedMessageTimestamp = Date.now();
225
380
  if (event.data === "__pong__") {
381
+ if (this._pingTimestamp) {
382
+ this._rtt = Date.now() - this._pingTimestamp;
383
+ this._pingTimestamp = void 0;
384
+ }
226
385
  this._rescheduleHeartbeatTimeout();
227
386
  return;
228
387
  }
229
388
  const bytes = await toUint8Array(event.data);
389
+ this._recordBytes(0, bytes.byteLength);
230
390
  if (!this.isOpen) {
231
391
  return;
232
392
  }
393
+ this._messagesReceived++;
233
394
  const message = this._ws?.protocol?.includes(EdgeWebsocketProtocol.V0) ? buf.fromBinary(MessageSchema, bytes) : muxer.receiveData(bytes);
234
395
  if (message) {
235
396
  log("received", {
236
397
  from: message.source,
237
398
  payload: protocol.getPayloadType(message)
238
399
  }, {
239
- F: __dxlog_file2,
240
- L: 128,
400
+ F: __dxlog_file3,
401
+ L: 185,
241
402
  S: this,
242
403
  C: (f, a) => f(...a)
243
404
  });
@@ -257,20 +418,20 @@ var EdgeWsConnection = class extends Resource {
257
418
  if (err instanceof Error && err.message.includes("WebSocket is closed before the connection is established.")) {
258
419
  return;
259
420
  }
260
- log.warn("Error closing websocket", {
421
+ log.warn("error closing websocket", {
261
422
  err
262
423
  }, {
263
- F: __dxlog_file2,
264
- L: 146,
424
+ F: __dxlog_file3,
425
+ L: 203,
265
426
  S: this,
266
427
  C: (f, a) => f(...a)
267
428
  });
268
429
  }
269
430
  }
270
431
  _scheduleHeartbeats() {
271
- invariant2(this._ws, void 0, {
272
- F: __dxlog_file2,
273
- L: 151,
432
+ invariant3(this._ws, void 0, {
433
+ F: __dxlog_file3,
434
+ L: 208,
274
435
  S: this,
275
436
  A: [
276
437
  "this._ws",
@@ -278,8 +439,10 @@ var EdgeWsConnection = class extends Resource {
278
439
  ]
279
440
  });
280
441
  scheduleTaskInterval(this._ctx, async () => {
442
+ this._pingTimestamp = Date.now();
281
443
  this._ws?.send("__ping__");
282
444
  }, SIGNAL_KEEPALIVE_INTERVAL);
445
+ this._pingTimestamp = Date.now();
283
446
  this._ws.send("__ping__");
284
447
  this._rescheduleHeartbeatTimeout();
285
448
  }
@@ -289,27 +452,74 @@ var EdgeWsConnection = class extends Resource {
289
452
  }
290
453
  void this._inactivityTimeoutCtx?.dispose();
291
454
  this._inactivityTimeoutCtx = new Context(void 0, {
292
- F: __dxlog_file2,
293
- L: 170
455
+ F: __dxlog_file3,
456
+ L: 229
294
457
  });
295
458
  scheduleTask(this._inactivityTimeoutCtx, () => {
296
459
  if (this.isOpen) {
297
- log.warn("restart due to inactivity timeout", void 0, {
298
- F: __dxlog_file2,
299
- L: 175,
300
- S: this,
301
- C: (f, a) => f(...a)
302
- });
303
- this._callbacks.onRestartRequired();
460
+ if (Date.now() - this._lastReceivedMessageTimestamp > SIGNAL_KEEPALIVE_TIMEOUT) {
461
+ log.warn("restart due to inactivity timeout", {
462
+ lastReceivedMessageTimestamp: this._lastReceivedMessageTimestamp
463
+ }, {
464
+ F: __dxlog_file3,
465
+ L: 235,
466
+ S: this,
467
+ C: (f, a) => f(...a)
468
+ });
469
+ this._callbacks.onRestartRequired();
470
+ } else {
471
+ this._rescheduleHeartbeatTimeout();
472
+ }
304
473
  }
305
474
  }, SIGNAL_KEEPALIVE_TIMEOUT);
306
475
  }
476
+ _recordBytes(sent, received) {
477
+ const now = Date.now();
478
+ const currentSecond = Math.floor(now / 1e3) * 1e3;
479
+ const existingSample = this._bytesSamples.find((s) => Math.floor(s.timestamp / 1e3) * 1e3 === currentSecond);
480
+ if (existingSample) {
481
+ existingSample.sent += sent;
482
+ existingSample.received += received;
483
+ } else {
484
+ this._bytesSamples.push({
485
+ timestamp: now,
486
+ sent,
487
+ received
488
+ });
489
+ }
490
+ }
491
+ _scheduleRateCalculation() {
492
+ scheduleTaskInterval(this._ctx, async () => {
493
+ this._calculateRates();
494
+ }, this._rateUpdateInterval);
495
+ this._calculateRates();
496
+ }
497
+ _calculateRates() {
498
+ const now = Date.now();
499
+ const cutoff = now - this._rateWindow;
500
+ this._bytesSamples = this._bytesSamples.filter((s) => s.timestamp > cutoff);
501
+ if (this._bytesSamples.length === 0) {
502
+ this._uploadRate = 0;
503
+ this._downloadRate = 0;
504
+ return;
505
+ }
506
+ let totalSent = 0;
507
+ let totalReceived = 0;
508
+ const oldestTimestamp = Math.min(...this._bytesSamples.map((s) => s.timestamp));
509
+ const timeSpan = (now - oldestTimestamp) / 1e3;
510
+ for (const sample of this._bytesSamples) {
511
+ totalSent += sample.sent;
512
+ totalReceived += sample.received;
513
+ }
514
+ this._uploadRate = timeSpan > 0 ? Math.round(totalSent / timeSpan) : 0;
515
+ this._downloadRate = timeSpan > 0 ? Math.round(totalReceived / timeSpan) : 0;
516
+ }
307
517
  };
308
518
  _ts_decorate([
309
519
  logInfo
310
520
  ], EdgeWsConnection.prototype, "info", null);
311
521
 
312
- // packages/core/mesh/edge-client/src/errors.ts
522
+ // src/errors.ts
313
523
  var EdgeConnectionClosedError = class extends Error {
314
524
  constructor() {
315
525
  super("Edge connection closed.");
@@ -321,7 +531,7 @@ var EdgeIdentityChangedError = class extends Error {
321
531
  }
322
532
  };
323
533
 
324
- // packages/core/mesh/edge-client/src/utils.ts
534
+ // src/utils.ts
325
535
  var getEdgeUrlWithProtocol = (baseUrl, protocol2) => {
326
536
  const isSecure = baseUrl.startsWith("https") || baseUrl.startsWith("wss");
327
537
  const url = new URL(baseUrl);
@@ -329,21 +539,32 @@ var getEdgeUrlWithProtocol = (baseUrl, protocol2) => {
329
539
  return url.toString();
330
540
  };
331
541
 
332
- // packages/core/mesh/edge-client/src/edge-client.ts
542
+ // src/edge-client.ts
333
543
  function _ts_decorate2(decorators, target, key, desc) {
334
544
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
335
545
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
336
546
  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;
337
547
  return c > 3 && r && Object.defineProperty(target, key, r), r;
338
548
  }
339
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-client.ts";
549
+ var __dxlog_file4 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/edge-client.ts";
340
550
  var DEFAULT_TIMEOUT = 1e4;
551
+ var STATUS_REFRESH_INTERVAL = 1e3;
341
552
  var EdgeClient = class extends Resource2 {
553
+ _identity;
554
+ _config;
555
+ statusChanged = new Event();
556
+ _persistentLifecycle = new PersistentLifecycle({
557
+ start: async () => this._connect(),
558
+ stop: async (state) => this._disconnect(state)
559
+ });
560
+ _messageListeners = /* @__PURE__ */ new Set();
561
+ _reconnectListeners = /* @__PURE__ */ new Set();
562
+ _baseWsUrl;
563
+ _baseHttpUrl;
564
+ _currentConnection = void 0;
565
+ _ready = new Trigger();
342
566
  constructor(_identity, _config) {
343
- super(), this._identity = _identity, this._config = _config, this.statusChanged = new Event(), this._persistentLifecycle = new PersistentLifecycle({
344
- start: async () => this._connect(),
345
- stop: async (state) => this._disconnect(state)
346
- }), this._messageListeners = /* @__PURE__ */ new Set(), this._reconnectListeners = /* @__PURE__ */ new Set(), this._currentConnection = void 0, this._ready = new Trigger(), this._isActive = (connection) => connection === this._currentConnection;
567
+ super(), this._identity = _identity, this._config = _config;
347
568
  this._baseWsUrl = getEdgeUrlWithProtocol(_config.socketEndpoint, "ws");
348
569
  this._baseHttpUrl = getEdgeUrlWithProtocol(_config.socketEndpoint, "http");
349
570
  }
@@ -356,7 +577,15 @@ var EdgeClient = class extends Resource2 {
356
577
  };
357
578
  }
358
579
  get status() {
359
- return Boolean(this._currentConnection) && this._ready.state === TriggerState.RESOLVED ? EdgeStatus.CONNECTED : EdgeStatus.NOT_CONNECTED;
580
+ return {
581
+ state: Boolean(this._currentConnection) && this._ready.state === TriggerState.RESOLVED ? EdgeStatus.ConnectionState.CONNECTED : EdgeStatus.ConnectionState.NOT_CONNECTED,
582
+ uptime: this._currentConnection?.uptime ?? 0,
583
+ rtt: this._currentConnection?.rtt ?? 0,
584
+ rateBytesUp: this._currentConnection?.uploadRate ?? 0,
585
+ rateBytesDown: this._currentConnection?.downloadRate ?? 0,
586
+ messagesSent: this._currentConnection?.messagesSent ?? 0,
587
+ messagesReceived: this._currentConnection?.messagesReceived ?? 0
588
+ };
360
589
  }
361
590
  get identityKey() {
362
591
  return this._identity.identityKey;
@@ -370,8 +599,8 @@ var EdgeClient = class extends Resource2 {
370
599
  identity,
371
600
  oldIdentity: this._identity
372
601
  }, {
373
- F: __dxlog_file3,
374
- L: 99,
602
+ F: __dxlog_file4,
603
+ L: 118,
375
604
  S: this,
376
605
  C: (f, a) => f(...a)
377
606
  });
@@ -380,6 +609,30 @@ var EdgeClient = class extends Resource2 {
380
609
  void this._persistentLifecycle.scheduleRestart();
381
610
  }
382
611
  }
612
+ /**
613
+ * Send message.
614
+ * NOTE: The message is guaranteed to be delivered but the service must respond with a message to confirm processing.
615
+ */
616
+ async send(message) {
617
+ if (this._ready.state !== TriggerState.RESOLVED) {
618
+ log2("waiting for websocket", void 0, {
619
+ F: __dxlog_file4,
620
+ L: 131,
621
+ S: this,
622
+ C: (f, a) => f(...a)
623
+ });
624
+ await this._ready.wait({
625
+ timeout: this._config.timeout ?? DEFAULT_TIMEOUT
626
+ });
627
+ }
628
+ if (!this._currentConnection) {
629
+ throw new EdgeConnectionClosedError();
630
+ }
631
+ if (message.source && (message.source.peerKey !== this._identity.peerKey || message.source.identityKey !== this.identityKey)) {
632
+ throw new EdgeIdentityChangedError();
633
+ }
634
+ this._currentConnection.send(message);
635
+ }
383
636
  onMessage(listener) {
384
637
  this._messageListeners.add(listener);
385
638
  return () => this._messageListeners.delete(listener);
@@ -393,8 +646,8 @@ var EdgeClient = class extends Resource2 {
393
646
  listener();
394
647
  } catch (error) {
395
648
  log2.catch(error, void 0, {
396
- F: __dxlog_file3,
397
- L: 121,
649
+ F: __dxlog_file4,
650
+ L: 164,
398
651
  S: this,
399
652
  C: (f, a) => f(...a)
400
653
  });
@@ -411,8 +664,8 @@ var EdgeClient = class extends Resource2 {
411
664
  log2("opening...", {
412
665
  info: this.info
413
666
  }, {
414
- F: __dxlog_file3,
415
- L: 133,
667
+ F: __dxlog_file4,
668
+ L: 177,
416
669
  S: this,
417
670
  C: (f, a) => f(...a)
418
671
  });
@@ -420,12 +673,18 @@ var EdgeClient = class extends Resource2 {
420
673
  log2.warn("Error while opening connection", {
421
674
  err
422
675
  }, {
423
- F: __dxlog_file3,
424
- L: 135,
676
+ F: __dxlog_file4,
677
+ L: 179,
425
678
  S: this,
426
679
  C: (f, a) => f(...a)
427
680
  });
428
681
  });
682
+ scheduleTaskInterval2(this._ctx, async () => {
683
+ if (!this._currentConnection) {
684
+ return;
685
+ }
686
+ this.statusChanged.emit(this.status);
687
+ }, STATUS_REFRESH_INTERVAL);
429
688
  }
430
689
  /**
431
690
  * Close connection and free resources.
@@ -434,8 +693,8 @@ var EdgeClient = class extends Resource2 {
434
693
  log2("closing...", {
435
694
  peerKey: this._identity.peerKey
436
695
  }, {
437
- F: __dxlog_file3,
438
- L: 143,
696
+ F: __dxlog_file4,
697
+ L: 199,
439
698
  S: this,
440
699
  C: (f, a) => f(...a)
441
700
  });
@@ -451,8 +710,8 @@ var EdgeClient = class extends Resource2 {
451
710
  const protocolHeader = this._config.disableAuth ? void 0 : await this._createAuthHeader(path);
452
711
  if (this._identity !== identity) {
453
712
  log2("identity changed during auth header request", void 0, {
454
- F: __dxlog_file3,
455
- L: 157,
713
+ F: __dxlog_file4,
714
+ L: 213,
456
715
  S: this,
457
716
  C: (f, a) => f(...a)
458
717
  });
@@ -464,8 +723,8 @@ var EdgeClient = class extends Resource2 {
464
723
  url: url.toString(),
465
724
  protocolHeader
466
725
  }, {
467
- F: __dxlog_file3,
468
- L: 163,
726
+ F: __dxlog_file4,
727
+ L: 219,
469
728
  S: this,
470
729
  C: (f, a) => f(...a)
471
730
  });
@@ -479,8 +738,8 @@ var EdgeClient = class extends Resource2 {
479
738
  this._notifyReconnected();
480
739
  } else {
481
740
  log2.verbose("connected callback ignored, because connection is not active", void 0, {
482
- F: __dxlog_file3,
483
- L: 173,
741
+ F: __dxlog_file4,
742
+ L: 229,
484
743
  S: this,
485
744
  C: (f, a) => f(...a)
486
745
  });
@@ -492,8 +751,8 @@ var EdgeClient = class extends Resource2 {
492
751
  void this._persistentLifecycle.scheduleRestart();
493
752
  } else {
494
753
  log2.verbose("restart requested by inactive connection", void 0, {
495
- F: __dxlog_file3,
496
- L: 181,
754
+ F: __dxlog_file4,
755
+ L: 237,
497
756
  S: this,
498
757
  C: (f, a) => f(...a)
499
758
  });
@@ -508,8 +767,8 @@ var EdgeClient = class extends Resource2 {
508
767
  from: message.source,
509
768
  type: message.payload?.typeUrl
510
769
  }, {
511
- F: __dxlog_file3,
512
- L: 189,
770
+ F: __dxlog_file4,
771
+ L: 245,
513
772
  S: this,
514
773
  C: (f, a) => f(...a)
515
774
  });
@@ -545,8 +804,8 @@ var EdgeClient = class extends Resource2 {
545
804
  log2.error("ws reconnect listener failed", {
546
805
  err
547
806
  }, {
548
- F: __dxlog_file3,
549
- L: 225,
807
+ F: __dxlog_file4,
808
+ L: 280,
550
809
  S: this,
551
810
  C: (f, a) => f(...a)
552
811
  });
@@ -562,38 +821,14 @@ var EdgeClient = class extends Resource2 {
562
821
  err,
563
822
  payload: protocol.getPayloadType(message)
564
823
  }, {
565
- F: __dxlog_file3,
566
- L: 235,
824
+ F: __dxlog_file4,
825
+ L: 290,
567
826
  S: this,
568
827
  C: (f, a) => f(...a)
569
828
  });
570
829
  }
571
830
  }
572
831
  }
573
- /**
574
- * Send message.
575
- * NOTE: The message is guaranteed to be delivered but the service must respond with a message to confirm processing.
576
- */
577
- async send(message) {
578
- if (this._ready.state !== TriggerState.RESOLVED) {
579
- log2("waiting for websocket to become ready", void 0, {
580
- F: __dxlog_file3,
581
- L: 246,
582
- S: this,
583
- C: (f, a) => f(...a)
584
- });
585
- await this._ready.wait({
586
- timeout: this._config.timeout ?? DEFAULT_TIMEOUT
587
- });
588
- }
589
- if (!this._currentConnection) {
590
- throw new EdgeConnectionClosedError();
591
- }
592
- if (message.source && (message.source.peerKey !== this._identity.peerKey || message.source.identityKey !== this.identityKey)) {
593
- throw new EdgeIdentityChangedError();
594
- }
595
- this._currentConnection.send(message);
596
- }
597
832
  async _createAuthHeader(path) {
598
833
  const httpUrl = new URL(path, this._baseHttpUrl);
599
834
  httpUrl.protocol = getEdgeUrlWithProtocol(this._baseWsUrl.toString(), "http");
@@ -607,14 +842,15 @@ var EdgeClient = class extends Resource2 {
607
842
  status: response.status,
608
843
  statusText: response.statusText
609
844
  }, {
610
- F: __dxlog_file3,
611
- L: 271,
845
+ F: __dxlog_file4,
846
+ L: 302,
612
847
  S: this,
613
848
  C: (f, a) => f(...a)
614
849
  });
615
850
  return void 0;
616
851
  }
617
852
  }
853
+ _isActive = (connection) => connection === this._currentConnection;
618
854
  };
619
855
  _ts_decorate2([
620
856
  logInfo2
@@ -624,134 +860,82 @@ var encodePresentationWsAuthHeader = (encodedPresentation) => {
624
860
  return `base64url.bearer.authorization.dxos.org.${encodedToken}`;
625
861
  };
626
862
 
627
- // packages/core/mesh/edge-client/src/auth.ts
628
- import { createCredential, signPresentation } from "@dxos/credentials";
629
- import { invariant as invariant3 } from "@dxos/invariant";
630
- import { Keyring } from "@dxos/keyring";
631
- import { PublicKey } from "@dxos/keys";
632
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/auth.ts";
633
- var createDeviceEdgeIdentity = async (signer, key) => {
634
- return {
635
- identityKey: key.toHex(),
636
- peerKey: key.toHex(),
637
- presentCredentials: async ({ challenge }) => {
638
- return signPresentation({
639
- presentation: {
640
- credentials: [
641
- // Verifier requires at least one credential in the presentation to establish the subject.
642
- await createCredential({
643
- assertion: {
644
- "@type": "dxos.halo.credentials.Auth"
645
- },
646
- issuer: key,
647
- subject: key,
648
- signer
649
- })
650
- ]
651
- },
652
- signer,
653
- signerKey: key,
654
- nonce: challenge
655
- });
656
- }
657
- };
658
- };
659
- var createChainEdgeIdentity = async (signer, identityKey, peerKey, chain, credentials) => {
660
- const credentialsToSign = credentials.length > 0 ? credentials : [
661
- await createCredential({
662
- assertion: {
663
- "@type": "dxos.halo.credentials.Auth"
664
- },
665
- issuer: identityKey,
666
- subject: identityKey,
667
- signer,
668
- chain,
669
- signingKey: peerKey
670
- })
671
- ];
672
- return {
673
- identityKey: identityKey.toHex(),
674
- peerKey: peerKey.toHex(),
675
- presentCredentials: async ({ challenge }) => {
676
- invariant3(chain, void 0, {
677
- F: __dxlog_file4,
678
- L: 75,
679
- S: void 0,
680
- A: [
681
- "chain",
682
- ""
683
- ]
684
- });
685
- return signPresentation({
686
- presentation: {
687
- credentials: credentialsToSign
688
- },
689
- signer,
690
- nonce: challenge,
691
- signerKey: peerKey,
692
- chain
693
- });
694
- }
695
- };
863
+ // src/edge-http-client.ts
864
+ import * as FetchHttpClient from "@effect/platform/FetchHttpClient";
865
+ import * as HttpClient from "@effect/platform/HttpClient";
866
+ import * as Effect2 from "effect/Effect";
867
+ import * as Function from "effect/Function";
868
+ import { sleep } from "@dxos/async";
869
+ import { Context as Context3 } from "@dxos/context";
870
+ import { runAndForwardErrors } from "@dxos/effect";
871
+ import { invariant as invariant4 } from "@dxos/invariant";
872
+ import { log as log4 } from "@dxos/log";
873
+ import { EdgeAuthChallengeError, EdgeCallFailedError } from "@dxos/protocols";
874
+ import { createUrl } from "@dxos/util";
875
+
876
+ // src/http-client.ts
877
+ import * as Context2 from "effect/Context";
878
+ import * as Duration from "effect/Duration";
879
+ import * as Effect from "effect/Effect";
880
+ import * as Layer from "effect/Layer";
881
+ import * as Schedule from "effect/Schedule";
882
+ import { log as log3 } from "@dxos/log";
883
+ var __dxlog_file5 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/http-client.ts";
884
+ var HttpConfig = class _HttpConfig extends Context2.Tag("HttpConfig")() {
885
+ static default = Layer.succeed(_HttpConfig, {
886
+ timeout: Duration.millis(1e3),
887
+ retryTimes: 3,
888
+ retryBaseDelay: Duration.millis(1e3)
889
+ });
696
890
  };
697
- var createEphemeralEdgeIdentity = async () => {
698
- const keyring = new Keyring();
699
- const key = await keyring.createKey();
700
- return createDeviceEdgeIdentity(keyring, key);
891
+ var withRetry = (effect, { timeout: timeout2 = Duration.millis(1e3), retryBaseDelay = Duration.millis(1e3), retryTimes = 3 } = {}) => {
892
+ return effect.pipe(Effect.flatMap((res) => (
893
+ // Treat 500 errors as retryable?
894
+ res.status === 500 ? Effect.fail(new Error(res.status.toString())) : res.json
895
+ )), Effect.timeout(timeout2), Effect.retry({
896
+ schedule: Schedule.exponential(retryBaseDelay).pipe(Schedule.jittered),
897
+ times: retryTimes
898
+ }));
701
899
  };
702
- var createTestHaloEdgeIdentity = async (signer, identityKey, deviceKey) => {
703
- const deviceAdmission = await createCredential({
704
- assertion: {
705
- "@type": "dxos.halo.credentials.AuthorizedDevice",
706
- deviceKey,
707
- identityKey
708
- },
709
- issuer: identityKey,
710
- subject: deviceKey,
711
- signer
900
+ var withRetryConfig = (effect) => Effect.gen(function* () {
901
+ const config = yield* HttpConfig;
902
+ return yield* withRetry(effect, config);
903
+ });
904
+ var withLogging = (effect) => effect.pipe(Effect.tap((res) => {
905
+ log3.info("response", {
906
+ status: res.status
907
+ }, {
908
+ F: __dxlog_file5,
909
+ L: 66,
910
+ S: void 0,
911
+ C: (f, a) => f(...a)
712
912
  });
713
- return createChainEdgeIdentity(signer, identityKey, deviceKey, {
714
- credential: deviceAdmission
715
- }, [
716
- await createCredential({
717
- assertion: {
718
- "@type": "dxos.halo.credentials.Auth"
719
- },
720
- issuer: identityKey,
721
- subject: identityKey,
722
- signer
723
- })
724
- ]);
725
- };
726
- var createStubEdgeIdentity = () => {
727
- const identityKey = PublicKey.random();
728
- const deviceKey = PublicKey.random();
729
- return {
730
- identityKey: identityKey.toHex(),
731
- peerKey: deviceKey.toHex(),
732
- presentCredentials: async () => {
733
- throw new Error("Stub identity does not support authentication.");
734
- }
735
- };
913
+ }));
914
+ var encodeAuthHeader = (challenge) => {
915
+ const encodedChallenge = Buffer.from(challenge).toString("base64");
916
+ return `VerifiablePresentation pb;base64,${encodedChallenge}`;
736
917
  };
737
918
 
738
- // packages/core/mesh/edge-client/src/edge-http-client.ts
739
- import { sleep } from "@dxos/async";
740
- import { Context as Context2 } from "@dxos/context";
741
- import { log as log3 } from "@dxos/log";
742
- import { EdgeCallFailedError, EdgeAuthChallengeError } from "@dxos/protocols";
743
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-http-client.ts";
919
+ // src/edge-http-client.ts
920
+ var __dxlog_file6 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/edge-http-client.ts";
744
921
  var DEFAULT_RETRY_TIMEOUT = 1500;
745
922
  var DEFAULT_RETRY_JITTER = 500;
746
923
  var DEFAULT_MAX_RETRIES_COUNT = 3;
924
+ var WARNING_BODY_SIZE = 10 * 1024 * 1024;
747
925
  var EdgeHttpClient = class {
926
+ _baseUrl;
927
+ _edgeIdentity;
928
+ /**
929
+ * Auth header is cached until receiving the next 401 from EDGE, at which point it gets refreshed.
930
+ */
931
+ _authHeader;
748
932
  constructor(baseUrl) {
749
933
  this._baseUrl = getEdgeUrlWithProtocol(baseUrl, "http");
750
- log3("created", {
934
+ log4("created", {
751
935
  url: this._baseUrl
752
936
  }, {
753
- F: __dxlog_file5,
754
- L: 53,
937
+ F: __dxlog_file6,
938
+ L: 106,
755
939
  S: this,
756
940
  C: (f, a) => f(...a)
757
941
  });
@@ -765,190 +949,329 @@ var EdgeHttpClient = class {
765
949
  this._authHeader = void 0;
766
950
  }
767
951
  }
952
+ //
953
+ // Status
954
+ //
955
+ async getStatus(args) {
956
+ return this._call(new URL("/status", this.baseUrl), {
957
+ ...args,
958
+ method: "GET",
959
+ auth: true
960
+ });
961
+ }
962
+ //
963
+ // Agents
964
+ //
768
965
  createAgent(body, args) {
769
- return this._call("/agents/create", {
966
+ return this._call(new URL("/agents/create", this.baseUrl), {
770
967
  ...args,
771
968
  method: "POST",
772
969
  body
773
970
  });
774
971
  }
775
972
  getAgentStatus(request, args) {
776
- return this._call(`/users/${request.ownerIdentityKey.toHex()}/agent/status`, {
973
+ return this._call(new URL(`/users/${request.ownerIdentityKey.toHex()}/agent/status`, this.baseUrl), {
777
974
  ...args,
778
975
  method: "GET"
779
976
  });
780
977
  }
978
+ //
979
+ // Credentials
980
+ //
781
981
  getCredentialsForNotarization(spaceId, args) {
782
- return this._call(`/spaces/${spaceId}/notarization`, {
982
+ return this._call(new URL(`/spaces/${spaceId}/notarization`, this.baseUrl), {
783
983
  ...args,
784
984
  method: "GET"
785
985
  });
786
986
  }
787
987
  async notarizeCredentials(spaceId, body, args) {
788
- await this._call(`/spaces/${spaceId}/notarization`, {
988
+ await this._call(new URL(`/spaces/${spaceId}/notarization`, this.baseUrl), {
989
+ ...args,
990
+ body,
991
+ method: "POST"
992
+ });
993
+ }
994
+ //
995
+ // Identity
996
+ //
997
+ async recoverIdentity(body, args) {
998
+ return this._call(new URL("/identity/recover", this.baseUrl), {
789
999
  ...args,
790
1000
  body,
791
1001
  method: "POST"
792
1002
  });
793
1003
  }
1004
+ //
1005
+ // Invitations
1006
+ //
794
1007
  async joinSpaceByInvitation(spaceId, body, args) {
795
- return this._call(`/spaces/${spaceId}/join`, {
1008
+ return this._call(new URL(`/spaces/${spaceId}/join`, this.baseUrl), {
796
1009
  ...args,
797
1010
  body,
798
1011
  method: "POST"
799
1012
  });
800
1013
  }
801
- async recoverIdentity(body, args) {
802
- return this._call("/identity/recover", {
1014
+ //
1015
+ // OAuth and credentials
1016
+ //
1017
+ async initiateOAuthFlow(body, args) {
1018
+ return this._call(new URL("/oauth/initiate", this.baseUrl), {
803
1019
  ...args,
804
1020
  body,
805
1021
  method: "POST"
806
1022
  });
807
1023
  }
808
- async executeWorkflow(spaceId, graphId, input, args) {
809
- return this._call(`/workflows/${spaceId}/${graphId}`, {
1024
+ //
1025
+ // Spaces
1026
+ //
1027
+ async createSpace(body, args) {
1028
+ return this._call(new URL("/spaces/create", this.baseUrl), {
810
1029
  ...args,
811
- body: input,
1030
+ body,
812
1031
  method: "POST"
813
1032
  });
814
1033
  }
1034
+ //
1035
+ // Queues
1036
+ //
1037
+ async queryQueue(subspaceTag, spaceId, query, args) {
1038
+ const queueId = query.queueIds?.[0];
1039
+ invariant4(queueId, "queueId required", {
1040
+ F: __dxlog_file6,
1041
+ L: 215,
1042
+ S: this,
1043
+ A: [
1044
+ "queueId",
1045
+ "'queueId required'"
1046
+ ]
1047
+ });
1048
+ return this._call(createUrl(new URL(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}/query`, this.baseUrl), {
1049
+ after: query.after,
1050
+ before: query.before,
1051
+ limit: query.limit,
1052
+ reverse: query.reverse,
1053
+ objectIds: query.objectIds?.join(",")
1054
+ }), {
1055
+ ...args,
1056
+ method: "GET"
1057
+ });
1058
+ }
1059
+ async insertIntoQueue(subspaceTag, spaceId, queueId, objects, args) {
1060
+ return this._call(new URL(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}`, this.baseUrl), {
1061
+ ...args,
1062
+ body: {
1063
+ objects
1064
+ },
1065
+ method: "POST"
1066
+ });
1067
+ }
1068
+ async deleteFromQueue(subspaceTag, spaceId, queueId, objectIds, args) {
1069
+ return this._call(createUrl(new URL(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}`, this.baseUrl), {
1070
+ ids: objectIds.join(",")
1071
+ }), {
1072
+ ...args,
1073
+ method: "DELETE"
1074
+ });
1075
+ }
1076
+ //
1077
+ // Functions
1078
+ //
815
1079
  async uploadFunction(pathParts, body, args) {
1080
+ const formData = new FormData();
1081
+ formData.append("name", body.name ?? "");
1082
+ formData.append("version", body.version);
1083
+ formData.append("ownerPublicKey", body.ownerPublicKey);
1084
+ formData.append("entryPoint", body.entryPoint);
1085
+ body.runtime && formData.append("runtime", body.runtime);
1086
+ for (const [filename, content] of Object.entries(body.assets)) {
1087
+ formData.append("assets", new Blob([
1088
+ content
1089
+ ], {
1090
+ type: getFileMimeType(filename)
1091
+ }), filename);
1092
+ }
816
1093
  const path = [
817
1094
  "functions",
818
1095
  ...pathParts.functionId ? [
819
1096
  pathParts.functionId
820
1097
  ] : []
821
1098
  ].join("/");
822
- return this._call(path, {
1099
+ return this._call(new URL(path, this.baseUrl), {
823
1100
  ...args,
824
- body,
825
- method: "PUT"
1101
+ body: formData,
1102
+ method: "PUT",
1103
+ json: false
826
1104
  });
827
1105
  }
828
- async initiateOAuthFlow(body, args) {
829
- return this._call("/oauth/initiate", {
1106
+ async listFunctions(args) {
1107
+ return this._call(new URL("/functions", this.baseUrl), {
830
1108
  ...args,
831
- body,
832
- method: "POST"
1109
+ method: "GET"
833
1110
  });
834
1111
  }
835
- async queryQueue(subspaceTag, spaceId, query, args) {
836
- const { queueId } = query;
837
- const queryParams = new URLSearchParams();
838
- if (query.after != null) {
839
- queryParams.set("after", query.after);
840
- }
841
- if (query.before != null) {
842
- queryParams.set("before", query.before);
1112
+ async invokeFunction(params, input, args) {
1113
+ const url = new URL(`/functions/${params.functionId}`, this.baseUrl);
1114
+ if (params.version) {
1115
+ url.searchParams.set("version", params.version);
843
1116
  }
844
- if (query.limit != null) {
845
- queryParams.set("limit", query.limit.toString());
1117
+ if (params.spaceId) {
1118
+ url.searchParams.set("spaceId", params.spaceId.toString());
846
1119
  }
847
- if (query.reverse != null) {
848
- queryParams.set("reverse", query.reverse.toString());
1120
+ if (params.cpuTimeLimit) {
1121
+ url.searchParams.set("cpuTimeLimit", params.cpuTimeLimit.toString());
849
1122
  }
850
- if (query.objectIds != null) {
851
- queryParams.set("objectIds", query.objectIds.join(","));
1123
+ if (params.subrequestsLimit) {
1124
+ url.searchParams.set("subrequestsLimit", params.subrequestsLimit.toString());
852
1125
  }
853
- return this._call(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}/query?${queryParams.toString()}`, {
1126
+ return this._call(url, {
854
1127
  ...args,
855
- method: "GET"
1128
+ body: input,
1129
+ method: "POST"
856
1130
  });
857
1131
  }
858
- async insertIntoQueue(subspaceTag, spaceId, queueId, objects, args) {
859
- return this._call(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}`, {
1132
+ //
1133
+ // Workflows
1134
+ //
1135
+ async executeWorkflow(spaceId, graphId, input, args) {
1136
+ return this._call(new URL(`/workflows/${spaceId}/${graphId}`, this.baseUrl), {
860
1137
  ...args,
861
- body: {
862
- objects
863
- },
1138
+ body: input,
864
1139
  method: "POST"
865
1140
  });
866
1141
  }
867
- async deleteFromQueue(subspaceTag, spaceId, queueId, objectIds, args) {
868
- return this._call(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}`, {
1142
+ //
1143
+ // Triggers
1144
+ //
1145
+ async getCronTriggers(spaceId) {
1146
+ return this._call(new URL(`/test/functions/${spaceId}/triggers/crons`, this.baseUrl), {
1147
+ method: "GET"
1148
+ });
1149
+ }
1150
+ async forceRunCronTrigger(spaceId, triggerId) {
1151
+ return this._call(new URL(`/test/functions/${spaceId}/triggers/crons/${triggerId}/run`, this.baseUrl), {
1152
+ method: "POST"
1153
+ });
1154
+ }
1155
+ //
1156
+ // Import/Export space.
1157
+ //
1158
+ async importBundle(spaceId, body, args) {
1159
+ return this._call(new URL(`/spaces/${spaceId}/import`, this.baseUrl), {
869
1160
  ...args,
870
- query: {
871
- ids: objectIds.join(",")
872
- },
873
- method: "DELETE"
1161
+ body,
1162
+ method: "PUT"
874
1163
  });
875
1164
  }
876
- async createSpace(body, args) {
877
- return this._call("/spaces/create", {
1165
+ async exportBundle(spaceId, body, args) {
1166
+ return this._call(new URL(`/spaces/${spaceId}/export`, this.baseUrl), {
878
1167
  ...args,
879
1168
  body,
880
1169
  method: "POST"
881
1170
  });
882
1171
  }
883
- async _call(path, args) {
884
- const requestContext = args.context ?? new Context2(void 0, {
885
- F: __dxlog_file5,
886
- L: 192
887
- });
1172
+ //
1173
+ // Internal
1174
+ //
1175
+ async _fetch(url, _args) {
1176
+ return Function.pipe(HttpClient.get(url), withLogging, withRetryConfig, Effect2.provide(FetchHttpClient.layer), Effect2.provide(HttpConfig.default), Effect2.withSpan("EdgeHttpClient"), runAndForwardErrors);
1177
+ }
1178
+ // TODO(burdon): Refactor with effect (see edge-http-client.test.ts).
1179
+ async _call(url, args) {
888
1180
  const shouldRetry = createRetryHandler(args);
889
- let url = `${this._baseUrl}${path.startsWith("/") ? path.slice(1) : path}`;
890
- if (args.query) {
891
- const queryParams = new URLSearchParams();
892
- for (const [key, value] of Object.entries(args.query)) {
893
- queryParams.set(key, value.toString());
894
- }
895
- url += `?${queryParams.toString()}`;
896
- }
897
- log3("call", {
898
- method: args.method,
899
- path,
1181
+ const requestContext = args.context ?? Context3.default(void 0, {
1182
+ F: __dxlog_file6,
1183
+ L: 407
1184
+ });
1185
+ log4("fetch", {
1186
+ url,
900
1187
  request: args.body
901
1188
  }, {
902
- F: __dxlog_file5,
903
- L: 204,
1189
+ F: __dxlog_file6,
1190
+ L: 408,
904
1191
  S: this,
905
1192
  C: (f, a) => f(...a)
906
1193
  });
907
1194
  let handledAuth = false;
908
- let authHeader = this._authHeader;
1195
+ const tryCount = 1;
909
1196
  while (true) {
910
- let processingError;
911
- let retryAfterHeaderValue = Number.NaN;
1197
+ let processingError = void 0;
912
1198
  try {
913
- const request = createRequest(args, authHeader);
1199
+ if (!this._authHeader && args.auth) {
1200
+ const response2 = await fetch(new URL(`/auth`, this.baseUrl));
1201
+ if (response2.status === 401) {
1202
+ this._authHeader = await this._handleUnauthorized(response2);
1203
+ }
1204
+ }
1205
+ const request = createRequest(args, this._authHeader);
1206
+ log4("call edge", {
1207
+ url,
1208
+ tryCount,
1209
+ authHeader: !!this._authHeader
1210
+ }, {
1211
+ F: __dxlog_file6,
1212
+ L: 423,
1213
+ S: this,
1214
+ C: (f, a) => f(...a)
1215
+ });
914
1216
  const response = await fetch(url, request);
915
- retryAfterHeaderValue = Number(response.headers.get("Retry-After"));
916
1217
  if (response.ok) {
917
- const body = await response.json();
918
- if (body.success) {
919
- return body.data;
920
- }
921
- log3("unsuccessful edge response", {
922
- path,
923
- body
924
- }, {
925
- F: __dxlog_file5,
926
- L: 223,
1218
+ const body2 = await response.clone().json();
1219
+ invariant4(body2, "Expected body to be present", {
1220
+ F: __dxlog_file6,
1221
+ L: 428,
927
1222
  S: this,
928
- C: (f, a) => f(...a)
1223
+ A: [
1224
+ "body",
1225
+ "'Expected body to be present'"
1226
+ ]
929
1227
  });
930
- if (body.errorData?.type === "auth_challenge" && typeof body.errorData?.challenge === "string") {
931
- processingError = new EdgeAuthChallengeError(body.errorData.challenge, body.errorData);
932
- } else {
933
- processingError = EdgeCallFailedError.fromUnsuccessfulResponse(response, body);
1228
+ if (!("success" in body2)) {
1229
+ return body2;
1230
+ }
1231
+ if (body2.success) {
1232
+ return body2.data;
934
1233
  }
935
1234
  } else if (response.status === 401 && !handledAuth) {
936
- authHeader = await this._handleUnauthorized(response);
1235
+ this._authHeader = await this._handleUnauthorized(response);
937
1236
  handledAuth = true;
938
1237
  continue;
1238
+ }
1239
+ const body = response.headers.get("Content-Type") === "application/json" ? await response.clone().json() : void 0;
1240
+ invariant4(!body?.success, "Expected body to not be a failure response or undefined.", {
1241
+ F: __dxlog_file6,
1242
+ L: 444,
1243
+ S: this,
1244
+ A: [
1245
+ "!body?.success",
1246
+ "'Expected body to not be a failure response or undefined.'"
1247
+ ]
1248
+ });
1249
+ if (body?.data?.type === "auth_challenge" && typeof body?.data?.challenge === "string") {
1250
+ processingError = new EdgeAuthChallengeError(body.data.challenge, body.data);
1251
+ } else if (body?.success === false) {
1252
+ processingError = EdgeCallFailedError.fromUnsuccessfulResponse(response, body);
939
1253
  } else {
940
- processingError = EdgeCallFailedError.fromHttpFailure(response);
1254
+ invariant4(!response.ok, "Expected response to not be ok.", {
1255
+ F: __dxlog_file6,
1256
+ L: 451,
1257
+ S: this,
1258
+ A: [
1259
+ "!response.ok",
1260
+ "'Expected response to not be ok.'"
1261
+ ]
1262
+ });
1263
+ processingError = await EdgeCallFailedError.fromHttpFailure(response);
941
1264
  }
942
1265
  } catch (error) {
943
1266
  processingError = EdgeCallFailedError.fromProcessingFailureCause(error);
944
1267
  }
945
- if (processingError.isRetryable && await shouldRetry(requestContext, retryAfterHeaderValue)) {
946
- log3("retrying edge request", {
947
- path,
1268
+ if (processingError?.isRetryable && await shouldRetry(requestContext, processingError.retryAfterMs)) {
1269
+ log4.verbose("retrying edge request", {
1270
+ url,
948
1271
  processingError
949
1272
  }, {
950
- F: __dxlog_file5,
951
- L: 242,
1273
+ F: __dxlog_file6,
1274
+ L: 459,
952
1275
  S: this,
953
1276
  C: (f, a) => f(...a)
954
1277
  });
@@ -959,33 +1282,54 @@ var EdgeHttpClient = class {
959
1282
  }
960
1283
  async _handleUnauthorized(response) {
961
1284
  if (!this._edgeIdentity) {
962
- log3.warn("edge unauthorized response received before identity was set", void 0, {
963
- F: __dxlog_file5,
964
- L: 251,
1285
+ log4.warn("unauthorized response received before identity was set", void 0, {
1286
+ F: __dxlog_file6,
1287
+ L: 468,
965
1288
  S: this,
966
1289
  C: (f, a) => f(...a)
967
1290
  });
968
- throw EdgeCallFailedError.fromHttpFailure(response);
1291
+ throw await EdgeCallFailedError.fromHttpFailure(response);
969
1292
  }
970
1293
  const challenge = await handleAuthChallenge(response, this._edgeIdentity);
971
- this._authHeader = encodeAuthHeader(challenge);
972
- log3("auth header updated", void 0, {
973
- F: __dxlog_file5,
974
- L: 256,
975
- S: this,
1294
+ return encodeAuthHeader(challenge);
1295
+ }
1296
+ };
1297
+ var createRequest = ({ method, body, json = true }, authHeader) => {
1298
+ let requestBody;
1299
+ const headers = {};
1300
+ if (json) {
1301
+ requestBody = body && JSON.stringify(body);
1302
+ headers["Content-Type"] = "application/json";
1303
+ } else {
1304
+ requestBody = body;
1305
+ }
1306
+ if (typeof requestBody === "string" && requestBody.length > WARNING_BODY_SIZE) {
1307
+ log4.warn("Request with large body", {
1308
+ bodySize: requestBody.length
1309
+ }, {
1310
+ F: __dxlog_file6,
1311
+ L: 492,
1312
+ S: void 0,
976
1313
  C: (f, a) => f(...a)
977
1314
  });
978
- return this._authHeader;
979
1315
  }
1316
+ if (authHeader) {
1317
+ headers["Authorization"] = authHeader;
1318
+ }
1319
+ return {
1320
+ method,
1321
+ body: requestBody,
1322
+ headers
1323
+ };
980
1324
  };
981
- var createRetryHandler = (args) => {
982
- if (!args.retry || args.retry.count < 1) {
1325
+ var createRetryHandler = ({ retry: retry2 }) => {
1326
+ if (!retry2 || retry2.count < 1) {
983
1327
  return async () => false;
984
1328
  }
985
1329
  let retries = 0;
986
- const maxRetries = args.retry.count ?? DEFAULT_MAX_RETRIES_COUNT;
987
- const baseTimeout = args.retry.timeout ?? DEFAULT_RETRY_TIMEOUT;
988
- const jitter = args.retry.jitter ?? DEFAULT_RETRY_JITTER;
1330
+ const maxRetries = retry2.count ?? DEFAULT_MAX_RETRIES_COUNT;
1331
+ const baseTimeout = retry2.timeout ?? DEFAULT_RETRY_TIMEOUT;
1332
+ const jitter = retry2.jitter ?? DEFAULT_RETRY_JITTER;
989
1333
  return async (ctx, retryAfter) => {
990
1334
  if (++retries > maxRetries || ctx.disposed) {
991
1335
  return false;
@@ -993,25 +1337,16 @@ var createRetryHandler = (args) => {
993
1337
  if (retryAfter) {
994
1338
  await sleep(retryAfter);
995
1339
  } else {
996
- const timeout = baseTimeout + Math.random() * jitter;
997
- await sleep(timeout);
1340
+ const timeout2 = baseTimeout + Math.random() * jitter;
1341
+ await sleep(timeout2);
998
1342
  }
999
1343
  return true;
1000
1344
  };
1001
1345
  };
1002
- var createRequest = (args, authHeader) => {
1003
- return {
1004
- method: args.method,
1005
- body: args.body && JSON.stringify(args.body),
1006
- headers: authHeader ? {
1007
- Authorization: authHeader
1008
- } : void 0
1009
- };
1010
- };
1011
- var encodeAuthHeader = (challenge) => {
1012
- const encodedChallenge = Buffer.from(challenge).toString("base64");
1013
- return `VerifiablePresentation pb;base64,${encodedChallenge}`;
1014
- };
1346
+ var getFileMimeType = (filename) => [
1347
+ ".js",
1348
+ ".mjs"
1349
+ ].some((codeExtension) => filename.endsWith(codeExtension)) ? "application/javascript+module" : filename.endsWith(".wasm") ? "application/wasm" : "application/octet-stream";
1015
1350
  export {
1016
1351
  CLOUDFLARE_MESSAGE_MAX_BYTES,
1017
1352
  CLOUDFLARE_RPC_MAX_BYTES,
@@ -1019,6 +1354,7 @@ export {
1019
1354
  EdgeConnectionClosedError,
1020
1355
  EdgeHttpClient,
1021
1356
  EdgeIdentityChangedError,
1357
+ HttpConfig,
1022
1358
  Protocol,
1023
1359
  WebSocketMuxer,
1024
1360
  createChainEdgeIdentity,
@@ -1026,9 +1362,13 @@ export {
1026
1362
  createEphemeralEdgeIdentity,
1027
1363
  createStubEdgeIdentity,
1028
1364
  createTestHaloEdgeIdentity,
1365
+ encodeAuthHeader,
1029
1366
  getTypename,
1030
1367
  handleAuthChallenge,
1031
1368
  protocol,
1032
- toUint8Array
1369
+ toUint8Array,
1370
+ withLogging,
1371
+ withRetry,
1372
+ withRetryConfig
1033
1373
  };
1034
1374
  //# sourceMappingURL=index.mjs.map