@dxos/edge-client 0.8.4-main.f5c0578 → 0.8.4-main.fcfe5033a5

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