@dxos/edge-client 0.8.4-main.f9ba587 → 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 (46) hide show
  1. package/dist/lib/{browser/chunk-LMP5TVOP.mjs → neutral/chunk-VESGVCLQ.mjs} +8 -4
  2. package/dist/lib/{browser/chunk-LMP5TVOP.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 +667 -324
  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/{node-esm → neutral}/testing/index.mjs.map +2 -2
  9. package/dist/types/src/edge-client.d.ts +18 -15
  10. package/dist/types/src/edge-client.d.ts.map +1 -1
  11. package/dist/types/src/edge-http-client.d.ts +62 -21
  12. package/dist/types/src/edge-http-client.d.ts.map +1 -1
  13. package/dist/types/src/edge-ws-connection.d.ts +21 -0
  14. package/dist/types/src/edge-ws-connection.d.ts.map +1 -1
  15. package/dist/types/src/edge-ws-muxer.d.ts.map +1 -1
  16. package/dist/types/src/http-client.d.ts +10 -7
  17. package/dist/types/src/http-client.d.ts.map +1 -1
  18. package/dist/types/src/index.d.ts +4 -3
  19. package/dist/types/src/index.d.ts.map +1 -1
  20. package/dist/types/src/testing/test-utils.d.ts +3 -3
  21. package/dist/types/src/testing/test-utils.d.ts.map +1 -1
  22. package/dist/types/tsconfig.tsbuildinfo +1 -1
  23. package/package.json +31 -24
  24. package/src/edge-client.test.ts +20 -15
  25. package/src/edge-client.ts +90 -43
  26. package/src/edge-http-client.test.ts +3 -2
  27. package/src/edge-http-client.ts +325 -70
  28. package/src/edge-ws-connection.ts +131 -9
  29. package/src/edge-ws-muxer.ts +1 -1
  30. package/src/http-client.test.ts +11 -8
  31. package/src/http-client.ts +18 -8
  32. package/src/index.ts +4 -3
  33. package/src/testing/test-utils.ts +8 -8
  34. package/src/websocket.test.ts +1 -1
  35. package/dist/lib/browser/index.mjs.map +0 -7
  36. package/dist/lib/browser/meta.json +0 -1
  37. package/dist/lib/browser/testing/index.mjs.map +0 -7
  38. package/dist/lib/node-esm/chunk-X7J46ISZ.mjs +0 -299
  39. package/dist/lib/node-esm/chunk-X7J46ISZ.mjs.map +0 -7
  40. package/dist/lib/node-esm/edge-ws-muxer.mjs +0 -12
  41. package/dist/lib/node-esm/edge-ws-muxer.mjs.map +0 -7
  42. package/dist/lib/node-esm/index.mjs +0 -1090
  43. package/dist/lib/node-esm/index.mjs.map +0 -7
  44. package/dist/lib/node-esm/meta.json +0 -1
  45. package/dist/lib/node-esm/testing/index.mjs +0 -186
  46. /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-LMP5TVOP.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 { Trigger, scheduleMicroTask, TriggerState, PersistentLifecycle, Event } 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,10 +184,29 @@ 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 {
191
+ _identity;
192
+ _connectionInfo;
193
+ _callbacks;
194
+ _inactivityTimeoutCtx;
195
+ _ws;
196
+ _wsMuxer;
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;
79
210
  constructor(_identity, _connectionInfo, _callbacks) {
80
211
  super(), this._identity = _identity, this._connectionInfo = _connectionInfo, this._callbacks = _callbacks;
81
212
  }
@@ -86,19 +217,37 @@ var EdgeWsConnection = class extends Resource {
86
217
  device: this._identity.peerKey
87
218
  };
88
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
+ }
89
238
  send(message) {
90
- invariant2(this._ws, void 0, {
91
- F: __dxlog_file2,
92
- L: 52,
239
+ invariant3(this._ws, void 0, {
240
+ F: __dxlog_file3,
241
+ L: 93,
93
242
  S: this,
94
243
  A: [
95
244
  "this._ws",
96
245
  ""
97
246
  ]
98
247
  });
99
- invariant2(this._wsMuxer, void 0, {
100
- F: __dxlog_file2,
101
- L: 53,
248
+ invariant3(this._wsMuxer, void 0, {
249
+ F: __dxlog_file3,
250
+ L: 94,
102
251
  S: this,
103
252
  A: [
104
253
  "this._wsMuxer",
@@ -109,11 +258,12 @@ var EdgeWsConnection = class extends Resource {
109
258
  peerKey: this._identity.peerKey,
110
259
  payload: protocol.getPayloadType(message)
111
260
  }, {
112
- F: __dxlog_file2,
113
- L: 54,
261
+ F: __dxlog_file3,
262
+ L: 95,
114
263
  S: this,
115
264
  C: (f, a) => f(...a)
116
265
  });
266
+ this._messagesSent++;
117
267
  if (this._ws?.protocol.includes(EdgeWebsocketProtocol.V0)) {
118
268
  const binary = buf.toBinary(MessageSchema, message);
119
269
  if (binary.length > CLOUDFLARE_MESSAGE_MAX_BYTES) {
@@ -122,18 +272,21 @@ var EdgeWsConnection = class extends Resource {
122
272
  serviceId: message.serviceId,
123
273
  payload: protocol.getPayloadType(message)
124
274
  }, {
125
- F: __dxlog_file2,
126
- L: 58,
275
+ F: __dxlog_file3,
276
+ L: 100,
127
277
  S: this,
128
278
  C: (f, a) => f(...a)
129
279
  });
130
280
  return;
131
281
  }
282
+ this._recordBytes(binary.byteLength, 0);
132
283
  this._ws.send(binary);
133
284
  } else {
285
+ const binary = buf.toBinary(MessageSchema, message);
286
+ this._recordBytes(binary.byteLength, 0);
134
287
  this._wsMuxer.send(message).catch((e) => log.catch(e, void 0, {
135
- F: __dxlog_file2,
136
- L: 67,
288
+ F: __dxlog_file3,
289
+ L: 113,
137
290
  S: this,
138
291
  C: (f, a) => f(...a)
139
292
  }));
@@ -148,25 +301,29 @@ var EdgeWsConnection = class extends Resource {
148
301
  this._connectionInfo.protocolHeader
149
302
  ] : [
150
303
  ...baseProtocols
151
- ]);
304
+ ], this._connectionInfo.headers ? {
305
+ headers: this._connectionInfo.headers
306
+ } : void 0);
152
307
  const muxer = new WebSocketMuxer(this._ws);
153
308
  this._wsMuxer = muxer;
154
309
  this._ws.onopen = () => {
155
310
  if (this.isOpen) {
156
311
  log("connected", void 0, {
157
- F: __dxlog_file2,
158
- L: 84,
312
+ F: __dxlog_file3,
313
+ L: 131,
159
314
  S: this,
160
315
  C: (f, a) => f(...a)
161
316
  });
317
+ this._openTimestamp = Date.now();
162
318
  this._callbacks.onConnected();
163
319
  this._scheduleHeartbeats();
320
+ this._scheduleRateCalculation();
164
321
  } else {
165
322
  log.verbose("connected after becoming inactive", {
166
323
  currentIdentity: this._identity
167
324
  }, {
168
- F: __dxlog_file2,
169
- L: 88,
325
+ F: __dxlog_file3,
326
+ L: 137,
170
327
  S: this,
171
328
  C: (f, a) => f(...a)
172
329
  });
@@ -174,12 +331,12 @@ var EdgeWsConnection = class extends Resource {
174
331
  };
175
332
  this._ws.onclose = (event) => {
176
333
  if (this.isOpen) {
177
- log.warn("disconnected while being open", {
334
+ log.warn("server disconnected", {
178
335
  code: event.code,
179
336
  reason: event.reason
180
337
  }, {
181
- F: __dxlog_file2,
182
- L: 93,
338
+ F: __dxlog_file3,
339
+ L: 142,
183
340
  S: this,
184
341
  C: (f, a) => f(...a)
185
342
  });
@@ -193,8 +350,8 @@ var EdgeWsConnection = class extends Resource {
193
350
  error: event.error,
194
351
  info: event.message
195
352
  }, {
196
- F: __dxlog_file2,
197
- L: 100,
353
+ F: __dxlog_file3,
354
+ L: 149,
198
355
  S: this,
199
356
  C: (f, a) => f(...a)
200
357
  });
@@ -203,8 +360,8 @@ var EdgeWsConnection = class extends Resource {
203
360
  log.verbose("error ignored on closed connection", {
204
361
  error: event.error
205
362
  }, {
206
- F: __dxlog_file2,
207
- L: 103,
363
+ F: __dxlog_file3,
364
+ L: 152,
208
365
  S: this,
209
366
  C: (f, a) => f(...a)
210
367
  });
@@ -215,29 +372,36 @@ var EdgeWsConnection = class extends Resource {
215
372
  log.verbose("message ignored on closed connection", {
216
373
  event: event.type
217
374
  }, {
218
- F: __dxlog_file2,
219
- L: 111,
375
+ F: __dxlog_file3,
376
+ L: 160,
220
377
  S: this,
221
378
  C: (f, a) => f(...a)
222
379
  });
223
380
  return;
224
381
  }
382
+ this._lastReceivedMessageTimestamp = Date.now();
225
383
  if (event.data === "__pong__") {
384
+ if (this._pingTimestamp) {
385
+ this._rtt = Date.now() - this._pingTimestamp;
386
+ this._pingTimestamp = void 0;
387
+ }
226
388
  this._rescheduleHeartbeatTimeout();
227
389
  return;
228
390
  }
229
391
  const bytes = await toUint8Array(event.data);
392
+ this._recordBytes(0, bytes.byteLength);
230
393
  if (!this.isOpen) {
231
394
  return;
232
395
  }
396
+ this._messagesReceived++;
233
397
  const message = this._ws?.protocol?.includes(EdgeWebsocketProtocol.V0) ? buf.fromBinary(MessageSchema, bytes) : muxer.receiveData(bytes);
234
398
  if (message) {
235
399
  log("received", {
236
400
  from: message.source,
237
401
  payload: protocol.getPayloadType(message)
238
402
  }, {
239
- F: __dxlog_file2,
240
- L: 128,
403
+ F: __dxlog_file3,
404
+ L: 186,
241
405
  S: this,
242
406
  C: (f, a) => f(...a)
243
407
  });
@@ -257,20 +421,20 @@ var EdgeWsConnection = class extends Resource {
257
421
  if (err instanceof Error && err.message.includes("WebSocket is closed before the connection is established.")) {
258
422
  return;
259
423
  }
260
- log.warn("Error closing websocket", {
424
+ log.warn("error closing websocket", {
261
425
  err
262
426
  }, {
263
- F: __dxlog_file2,
264
- L: 146,
427
+ F: __dxlog_file3,
428
+ L: 204,
265
429
  S: this,
266
430
  C: (f, a) => f(...a)
267
431
  });
268
432
  }
269
433
  }
270
434
  _scheduleHeartbeats() {
271
- invariant2(this._ws, void 0, {
272
- F: __dxlog_file2,
273
- L: 151,
435
+ invariant3(this._ws, void 0, {
436
+ F: __dxlog_file3,
437
+ L: 209,
274
438
  S: this,
275
439
  A: [
276
440
  "this._ws",
@@ -278,8 +442,10 @@ var EdgeWsConnection = class extends Resource {
278
442
  ]
279
443
  });
280
444
  scheduleTaskInterval(this._ctx, async () => {
445
+ this._pingTimestamp = Date.now();
281
446
  this._ws?.send("__ping__");
282
447
  }, SIGNAL_KEEPALIVE_INTERVAL);
448
+ this._pingTimestamp = Date.now();
283
449
  this._ws.send("__ping__");
284
450
  this._rescheduleHeartbeatTimeout();
285
451
  }
@@ -289,21 +455,68 @@ var EdgeWsConnection = class extends Resource {
289
455
  }
290
456
  void this._inactivityTimeoutCtx?.dispose();
291
457
  this._inactivityTimeoutCtx = new Context(void 0, {
292
- F: __dxlog_file2,
293
- L: 170
458
+ F: __dxlog_file3,
459
+ L: 230
294
460
  });
295
461
  scheduleTask(this._inactivityTimeoutCtx, () => {
296
462
  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();
463
+ if (Date.now() - this._lastReceivedMessageTimestamp > SIGNAL_KEEPALIVE_TIMEOUT) {
464
+ log.warn("restart due to inactivity timeout", {
465
+ lastReceivedMessageTimestamp: this._lastReceivedMessageTimestamp
466
+ }, {
467
+ F: __dxlog_file3,
468
+ L: 236,
469
+ S: this,
470
+ C: (f, a) => f(...a)
471
+ });
472
+ this._callbacks.onRestartRequired();
473
+ } else {
474
+ this._rescheduleHeartbeatTimeout();
475
+ }
304
476
  }
305
477
  }, SIGNAL_KEEPALIVE_TIMEOUT);
306
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
+ }
307
520
  };
308
521
  _ts_decorate([
309
522
  logInfo
@@ -336,14 +549,25 @@ function _ts_decorate2(decorators, target, key, desc) {
336
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;
337
550
  return c > 3 && r && Object.defineProperty(target, key, r), r;
338
551
  }
339
- 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";
340
553
  var DEFAULT_TIMEOUT = 1e4;
554
+ var STATUS_REFRESH_INTERVAL = 1e3;
341
555
  var EdgeClient = class extends Resource2 {
556
+ _identity;
557
+ _config;
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();
565
+ _baseWsUrl;
566
+ _baseHttpUrl;
567
+ _currentConnection = void 0;
568
+ _ready = new Trigger();
342
569
  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;
570
+ super(), this._identity = _identity, this._config = _config;
347
571
  this._baseWsUrl = getEdgeUrlWithProtocol(_config.socketEndpoint, "ws");
348
572
  this._baseHttpUrl = getEdgeUrlWithProtocol(_config.socketEndpoint, "http");
349
573
  }
@@ -356,7 +580,15 @@ var EdgeClient = class extends Resource2 {
356
580
  };
357
581
  }
358
582
  get status() {
359
- 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
+ };
360
592
  }
361
593
  get identityKey() {
362
594
  return this._identity.identityKey;
@@ -370,8 +602,8 @@ var EdgeClient = class extends Resource2 {
370
602
  identity,
371
603
  oldIdentity: this._identity
372
604
  }, {
373
- F: __dxlog_file3,
374
- L: 99,
605
+ F: __dxlog_file4,
606
+ L: 121,
375
607
  S: this,
376
608
  C: (f, a) => f(...a)
377
609
  });
@@ -380,6 +612,38 @@ var EdgeClient = class extends Resource2 {
380
612
  void this._persistentLifecycle.scheduleRestart();
381
613
  }
382
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
+ }
383
647
  onMessage(listener) {
384
648
  this._messageListeners.add(listener);
385
649
  return () => this._messageListeners.delete(listener);
@@ -393,8 +657,8 @@ var EdgeClient = class extends Resource2 {
393
657
  listener();
394
658
  } catch (error) {
395
659
  log2.catch(error, void 0, {
396
- F: __dxlog_file3,
397
- L: 121,
660
+ F: __dxlog_file4,
661
+ L: 176,
398
662
  S: this,
399
663
  C: (f, a) => f(...a)
400
664
  });
@@ -411,8 +675,8 @@ var EdgeClient = class extends Resource2 {
411
675
  log2("opening...", {
412
676
  info: this.info
413
677
  }, {
414
- F: __dxlog_file3,
415
- L: 133,
678
+ F: __dxlog_file4,
679
+ L: 189,
416
680
  S: this,
417
681
  C: (f, a) => f(...a)
418
682
  });
@@ -420,12 +684,18 @@ var EdgeClient = class extends Resource2 {
420
684
  log2.warn("Error while opening connection", {
421
685
  err
422
686
  }, {
423
- F: __dxlog_file3,
424
- L: 135,
687
+ F: __dxlog_file4,
688
+ L: 191,
425
689
  S: this,
426
690
  C: (f, a) => f(...a)
427
691
  });
428
692
  });
693
+ scheduleTaskInterval2(this._ctx, async () => {
694
+ if (!this._currentConnection) {
695
+ return;
696
+ }
697
+ this.statusChanged.emit(this.status);
698
+ }, STATUS_REFRESH_INTERVAL);
429
699
  }
430
700
  /**
431
701
  * Close connection and free resources.
@@ -434,8 +704,8 @@ var EdgeClient = class extends Resource2 {
434
704
  log2("closing...", {
435
705
  peerKey: this._identity.peerKey
436
706
  }, {
437
- F: __dxlog_file3,
438
- L: 143,
707
+ F: __dxlog_file4,
708
+ L: 211,
439
709
  S: this,
440
710
  C: (f, a) => f(...a)
441
711
  });
@@ -451,8 +721,8 @@ var EdgeClient = class extends Resource2 {
451
721
  const protocolHeader = this._config.disableAuth ? void 0 : await this._createAuthHeader(path);
452
722
  if (this._identity !== identity) {
453
723
  log2("identity changed during auth header request", void 0, {
454
- F: __dxlog_file3,
455
- L: 157,
724
+ F: __dxlog_file4,
725
+ L: 225,
456
726
  S: this,
457
727
  C: (f, a) => f(...a)
458
728
  });
@@ -464,14 +734,17 @@ var EdgeClient = class extends Resource2 {
464
734
  url: url.toString(),
465
735
  protocolHeader
466
736
  }, {
467
- F: __dxlog_file3,
468
- L: 163,
737
+ F: __dxlog_file4,
738
+ L: 231,
469
739
  S: this,
470
740
  C: (f, a) => f(...a)
471
741
  });
472
742
  const connection = new EdgeWsConnection(identity, {
473
743
  url,
474
- protocolHeader
744
+ protocolHeader,
745
+ headers: this._config.clientTag ? {
746
+ "X-DXOS-Client-Tag": this._config.clientTag
747
+ } : void 0
475
748
  }, {
476
749
  onConnected: () => {
477
750
  if (this._isActive(connection)) {
@@ -479,8 +752,8 @@ var EdgeClient = class extends Resource2 {
479
752
  this._notifyReconnected();
480
753
  } else {
481
754
  log2.verbose("connected callback ignored, because connection is not active", void 0, {
482
- F: __dxlog_file3,
483
- L: 173,
755
+ F: __dxlog_file4,
756
+ L: 245,
484
757
  S: this,
485
758
  C: (f, a) => f(...a)
486
759
  });
@@ -492,8 +765,8 @@ var EdgeClient = class extends Resource2 {
492
765
  void this._persistentLifecycle.scheduleRestart();
493
766
  } else {
494
767
  log2.verbose("restart requested by inactive connection", void 0, {
495
- F: __dxlog_file3,
496
- L: 181,
768
+ F: __dxlog_file4,
769
+ L: 253,
497
770
  S: this,
498
771
  C: (f, a) => f(...a)
499
772
  });
@@ -508,8 +781,8 @@ var EdgeClient = class extends Resource2 {
508
781
  from: message.source,
509
782
  type: message.payload?.typeUrl
510
783
  }, {
511
- F: __dxlog_file3,
512
- L: 189,
784
+ F: __dxlog_file4,
785
+ L: 261,
513
786
  S: this,
514
787
  C: (f, a) => f(...a)
515
788
  });
@@ -545,8 +818,8 @@ var EdgeClient = class extends Resource2 {
545
818
  log2.error("ws reconnect listener failed", {
546
819
  err
547
820
  }, {
548
- F: __dxlog_file3,
549
- L: 225,
821
+ F: __dxlog_file4,
822
+ L: 296,
550
823
  S: this,
551
824
  C: (f, a) => f(...a)
552
825
  });
@@ -562,38 +835,14 @@ var EdgeClient = class extends Resource2 {
562
835
  err,
563
836
  payload: protocol.getPayloadType(message)
564
837
  }, {
565
- F: __dxlog_file3,
566
- L: 235,
838
+ F: __dxlog_file4,
839
+ L: 306,
567
840
  S: this,
568
841
  C: (f, a) => f(...a)
569
842
  });
570
843
  }
571
844
  }
572
845
  }
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
846
  async _createAuthHeader(path) {
598
847
  const httpUrl = new URL(path, this._baseHttpUrl);
599
848
  httpUrl.protocol = getEdgeUrlWithProtocol(this._baseWsUrl.toString(), "http");
@@ -607,14 +856,15 @@ var EdgeClient = class extends Resource2 {
607
856
  status: response.status,
608
857
  statusText: response.statusText
609
858
  }, {
610
- F: __dxlog_file3,
611
- L: 271,
859
+ F: __dxlog_file4,
860
+ L: 318,
612
861
  S: this,
613
862
  C: (f, a) => f(...a)
614
863
  });
615
864
  return void 0;
616
865
  }
617
866
  }
867
+ _isActive = (connection) => connection === this._currentConnection;
618
868
  };
619
869
  _ts_decorate2([
620
870
  logInfo2
@@ -624,144 +874,39 @@ var encodePresentationWsAuthHeader = (encodedPresentation) => {
624
874
  return `base64url.bearer.authorization.dxos.org.${encodedToken}`;
625
875
  };
626
876
 
627
- // 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 = "/__w/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
- };
696
- };
697
- var createEphemeralEdgeIdentity = async () => {
698
- const keyring = new Keyring();
699
- const key = await keyring.createKey();
700
- return createDeviceEdgeIdentity(keyring, key);
701
- };
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
712
- });
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
- };
736
- };
737
-
738
877
  // src/edge-http-client.ts
739
- import { FetchHttpClient, HttpClient } from "@effect/platform";
740
- 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";
741
882
  import { sleep } from "@dxos/async";
742
- 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";
743
886
  import { log as log4 } from "@dxos/log";
744
- import { EdgeAuthChallengeError, EdgeCallFailedError } from "@dxos/protocols";
887
+ import { EDGE_CLIENT_TAG_HEADER, EdgeAuthChallengeError, EdgeCallFailedError } from "@dxos/protocols";
745
888
  import { createUrl } from "@dxos/util";
746
889
 
747
890
  // src/http-client.ts
748
- 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";
749
896
  import { log as log3 } from "@dxos/log";
750
897
  var __dxlog_file5 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/http-client.ts";
751
898
  var HttpConfig = class _HttpConfig extends Context2.Tag("HttpConfig")() {
752
- static {
753
- this.default = Layer.succeed(_HttpConfig, {
754
- timeout: Duration.millis(1e3),
755
- retryTimes: 3,
756
- retryBaseDelay: Duration.millis(1e3)
757
- });
758
- }
899
+ static default = Layer.succeed(_HttpConfig, {
900
+ timeout: Duration.millis(1e3),
901
+ retryTimes: 3,
902
+ retryBaseDelay: Duration.millis(1e3)
903
+ });
759
904
  };
760
- 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 } = {}) => {
761
906
  return effect.pipe(Effect.flatMap((res) => (
762
907
  // Treat 500 errors as retryable?
763
908
  res.status === 500 ? Effect.fail(new Error(res.status.toString())) : res.json
764
- )), Effect.timeout(timeout), Effect.retry({
909
+ )), Effect.timeout(timeout2), Effect.retry({
765
910
  schedule: Schedule.exponential(retryBaseDelay).pipe(Schedule.jittered),
766
911
  times: retryTimes
767
912
  }));
@@ -770,14 +915,16 @@ var withRetryConfig = (effect) => Effect.gen(function* () {
770
915
  const config = yield* HttpConfig;
771
916
  return yield* withRetry(effect, config);
772
917
  });
773
- var withLogging = (effect) => effect.pipe(Effect.tap((res) => log3.info("response", {
774
- status: res.status
775
- }, {
776
- F: __dxlog_file5,
777
- L: 58,
778
- S: void 0,
779
- C: (f, a) => f(...a)
780
- })));
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
+ }));
781
928
  var encodeAuthHeader = (challenge) => {
782
929
  const encodedChallenge = Buffer.from(challenge).toString("base64");
783
930
  return `VerifiablePresentation pb;base64,${encodedChallenge}`;
@@ -788,14 +935,23 @@ var __dxlog_file6 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/edge-http
788
935
  var DEFAULT_RETRY_TIMEOUT = 1500;
789
936
  var DEFAULT_RETRY_JITTER = 500;
790
937
  var DEFAULT_MAX_RETRIES_COUNT = 3;
938
+ var WARNING_BODY_SIZE = 10 * 1024 * 1024;
791
939
  var EdgeHttpClient = class {
792
- constructor(baseUrl) {
940
+ _baseUrl;
941
+ _clientTag;
942
+ _edgeIdentity;
943
+ /**
944
+ * Auth header is cached until receiving the next 401 from EDGE, at which point it gets refreshed.
945
+ */
946
+ _authHeader;
947
+ constructor(baseUrl, options) {
793
948
  this._baseUrl = getEdgeUrlWithProtocol(baseUrl, "http");
949
+ this._clientTag = options?.clientTag;
794
950
  log4("created", {
795
951
  url: this._baseUrl
796
952
  }, {
797
953
  F: __dxlog_file6,
798
- L: 84,
954
+ L: 120,
799
955
  S: this,
800
956
  C: (f, a) => f(...a)
801
957
  });
@@ -812,24 +968,25 @@ var EdgeHttpClient = class {
812
968
  //
813
969
  // Status
814
970
  //
815
- async getStatus(args) {
816
- return this._call(new URL("/status", this.baseUrl), {
971
+ async getStatus(ctx, args) {
972
+ return this._call(ctx, new URL("/status", this.baseUrl), {
817
973
  ...args,
818
- method: "GET"
974
+ method: "GET",
975
+ auth: true
819
976
  });
820
977
  }
821
978
  //
822
979
  // Agents
823
980
  //
824
- createAgent(body, args) {
825
- 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), {
826
983
  ...args,
827
984
  method: "POST",
828
985
  body
829
986
  });
830
987
  }
831
- getAgentStatus(request, args) {
832
- 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), {
833
990
  ...args,
834
991
  method: "GET"
835
992
  });
@@ -837,14 +994,14 @@ var EdgeHttpClient = class {
837
994
  //
838
995
  // Credentials
839
996
  //
840
- getCredentialsForNotarization(spaceId, args) {
841
- 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), {
842
999
  ...args,
843
1000
  method: "GET"
844
1001
  });
845
1002
  }
846
- async notarizeCredentials(spaceId, body, args) {
847
- 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), {
848
1005
  ...args,
849
1006
  body,
850
1007
  method: "POST"
@@ -853,8 +1010,8 @@ var EdgeHttpClient = class {
853
1010
  //
854
1011
  // Identity
855
1012
  //
856
- async recoverIdentity(body, args) {
857
- 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), {
858
1015
  ...args,
859
1016
  body,
860
1017
  method: "POST"
@@ -863,8 +1020,8 @@ var EdgeHttpClient = class {
863
1020
  //
864
1021
  // Invitations
865
1022
  //
866
- async joinSpaceByInvitation(spaceId, body, args) {
867
- 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), {
868
1025
  ...args,
869
1026
  body,
870
1027
  method: "POST"
@@ -873,8 +1030,8 @@ var EdgeHttpClient = class {
873
1030
  //
874
1031
  // OAuth and credentials
875
1032
  //
876
- async initiateOAuthFlow(body, args) {
877
- 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), {
878
1035
  ...args,
879
1036
  body,
880
1037
  method: "POST"
@@ -883,8 +1040,8 @@ var EdgeHttpClient = class {
883
1040
  //
884
1041
  // Spaces
885
1042
  //
886
- async createSpace(body, args) {
887
- 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), {
888
1045
  ...args,
889
1046
  body,
890
1047
  method: "POST"
@@ -893,9 +1050,18 @@ var EdgeHttpClient = class {
893
1050
  //
894
1051
  // Queues
895
1052
  //
896
- async queryQueue(subspaceTag, spaceId, query, args) {
897
- const { queueId } = query;
898
- 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), {
899
1065
  after: query.after,
900
1066
  before: query.before,
901
1067
  limit: query.limit,
@@ -906,8 +1072,8 @@ var EdgeHttpClient = class {
906
1072
  method: "GET"
907
1073
  });
908
1074
  }
909
- async insertIntoQueue(subspaceTag, spaceId, queueId, objects, args) {
910
- 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), {
911
1077
  ...args,
912
1078
  body: {
913
1079
  objects
@@ -915,8 +1081,8 @@ var EdgeHttpClient = class {
915
1081
  method: "POST"
916
1082
  });
917
1083
  }
918
- async deleteFromQueue(subspaceTag, spaceId, queueId, objectIds, args) {
919
- 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), {
920
1086
  ids: objectIds.join(",")
921
1087
  }), {
922
1088
  ...args,
@@ -926,95 +1092,225 @@ var EdgeHttpClient = class {
926
1092
  //
927
1093
  // Functions
928
1094
  //
929
- 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
+ }
930
1109
  const path = [
931
1110
  "functions",
932
1111
  ...pathParts.functionId ? [
933
1112
  pathParts.functionId
934
1113
  ] : []
935
1114
  ].join("/");
936
- return this._call(new URL(path, this.baseUrl), {
1115
+ return this._call(ctx, new URL(path, this.baseUrl), {
937
1116
  ...args,
938
- body,
939
- 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"
940
1146
  });
941
1147
  }
942
1148
  //
943
1149
  // Workflows
944
1150
  //
945
- async executeWorkflow(spaceId, graphId, input, args) {
946
- 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), {
947
1153
  ...args,
948
1154
  body: input,
949
1155
  method: "POST"
950
1156
  });
951
1157
  }
952
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
+ //
953
1215
  // Internal
954
1216
  //
955
- async _fetch(url, args) {
956
- 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);
957
1219
  }
958
1220
  // TODO(burdon): Refactor with effect (see edge-http-client.test.ts).
959
- async _call(url, args) {
1221
+ async _call(ctx, url, args) {
960
1222
  const shouldRetry = createRetryHandler(args);
961
- const requestContext = args.context ?? new Context3(void 0, {
962
- F: __dxlog_file6,
963
- L: 289
964
- });
965
1223
  log4("fetch", {
966
1224
  url,
967
1225
  request: args.body
968
1226
  }, {
969
1227
  F: __dxlog_file6,
970
- L: 290,
1228
+ L: 472,
971
1229
  S: this,
972
1230
  C: (f, a) => f(...a)
973
1231
  });
1232
+ const traceHeaders = getTraceHeaders(ctx);
974
1233
  let handledAuth = false;
1234
+ const tryCount = 1;
975
1235
  while (true) {
976
- let processingError;
977
- let retryAfterHeaderValue = Number.NaN;
1236
+ let processingError = void 0;
978
1237
  try {
979
- 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
+ });
980
1255
  const response = await fetch(url, request);
981
- retryAfterHeaderValue = Number(response.headers.get("Retry-After"));
982
1256
  if (response.ok) {
983
- const body = await response.json();
984
- if (body.success) {
985
- return body.data;
986
- }
987
- log4.warn("unsuccessful edge response", {
988
- url,
989
- body
990
- }, {
1257
+ const body2 = await response.clone().json();
1258
+ invariant4(body2, "Expected body to be present", {
991
1259
  F: __dxlog_file6,
992
- L: 306,
1260
+ L: 494,
993
1261
  S: this,
994
- C: (f, a) => f(...a)
1262
+ A: [
1263
+ "body",
1264
+ "'Expected body to be present'"
1265
+ ]
995
1266
  });
996
- if (body.errorData?.type === "auth_challenge" && typeof body.errorData?.challenge === "string") {
997
- processingError = new EdgeAuthChallengeError(body.errorData.challenge, body.errorData);
998
- } else {
999
- processingError = EdgeCallFailedError.fromUnsuccessfulResponse(response, body);
1267
+ if (!("success" in body2)) {
1268
+ return body2;
1269
+ }
1270
+ if (body2.success) {
1271
+ return body2.data;
1000
1272
  }
1001
1273
  } else if (response.status === 401 && !handledAuth) {
1002
1274
  this._authHeader = await this._handleUnauthorized(response);
1003
1275
  handledAuth = true;
1004
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);
1005
1292
  } else {
1006
- 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);
1007
1303
  }
1008
1304
  } catch (error) {
1009
1305
  processingError = EdgeCallFailedError.fromProcessingFailureCause(error);
1010
1306
  }
1011
- if (processingError.isRetryable && await shouldRetry(requestContext, retryAfterHeaderValue)) {
1012
- log4("retrying edge request", {
1307
+ if (processingError?.isRetryable && await shouldRetry(ctx, processingError.retryAfterMs)) {
1308
+ log4.verbose("retrying edge request", {
1013
1309
  url,
1014
1310
  processingError
1015
1311
  }, {
1016
1312
  F: __dxlog_file6,
1017
- L: 324,
1313
+ L: 525,
1018
1314
  S: this,
1019
1315
  C: (f, a) => f(...a)
1020
1316
  });
@@ -1027,33 +1323,71 @@ var EdgeHttpClient = class {
1027
1323
  if (!this._edgeIdentity) {
1028
1324
  log4.warn("unauthorized response received before identity was set", void 0, {
1029
1325
  F: __dxlog_file6,
1030
- L: 333,
1326
+ L: 534,
1031
1327
  S: this,
1032
1328
  C: (f, a) => f(...a)
1033
1329
  });
1034
- throw EdgeCallFailedError.fromHttpFailure(response);
1330
+ throw await EdgeCallFailedError.fromHttpFailure(response);
1035
1331
  }
1036
1332
  const challenge = await handleAuthChallenge(response, this._edgeIdentity);
1037
1333
  return encodeAuthHeader(challenge);
1038
1334
  }
1039
1335
  };
1040
- 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
+ }
1041
1364
  return {
1042
1365
  method,
1043
- body: body && JSON.stringify(body),
1044
- headers: authHeader ? {
1045
- Authorization: authHeader
1046
- } : 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
1047
1377
  };
1378
+ if (traceCtx.tracestate) {
1379
+ headers.tracestate = traceCtx.tracestate;
1380
+ }
1381
+ return headers;
1048
1382
  };
1049
- var createRetryHandler = ({ retry }) => {
1050
- if (!retry || retry.count < 1) {
1383
+ var createRetryHandler = ({ retry: retry2 }) => {
1384
+ if (!retry2 || retry2.count < 1) {
1051
1385
  return async () => false;
1052
1386
  }
1053
1387
  let retries = 0;
1054
- const maxRetries = retry.count ?? DEFAULT_MAX_RETRIES_COUNT;
1055
- const baseTimeout = retry.timeout ?? DEFAULT_RETRY_TIMEOUT;
1056
- 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;
1057
1391
  return async (ctx, retryAfter) => {
1058
1392
  if (++retries > maxRetries || ctx.disposed) {
1059
1393
  return false;
@@ -1061,12 +1395,16 @@ var createRetryHandler = ({ retry }) => {
1061
1395
  if (retryAfter) {
1062
1396
  await sleep(retryAfter);
1063
1397
  } else {
1064
- const timeout = baseTimeout + Math.random() * jitter;
1065
- await sleep(timeout);
1398
+ const timeout2 = baseTimeout + Math.random() * jitter;
1399
+ await sleep(timeout2);
1066
1400
  }
1067
1401
  return true;
1068
1402
  };
1069
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";
1070
1408
  export {
1071
1409
  CLOUDFLARE_MESSAGE_MAX_BYTES,
1072
1410
  CLOUDFLARE_RPC_MAX_BYTES,
@@ -1074,6 +1412,7 @@ export {
1074
1412
  EdgeConnectionClosedError,
1075
1413
  EdgeHttpClient,
1076
1414
  EdgeIdentityChangedError,
1415
+ HttpConfig,
1077
1416
  Protocol,
1078
1417
  WebSocketMuxer,
1079
1418
  createChainEdgeIdentity,
@@ -1081,9 +1420,13 @@ export {
1081
1420
  createEphemeralEdgeIdentity,
1082
1421
  createStubEdgeIdentity,
1083
1422
  createTestHaloEdgeIdentity,
1423
+ encodeAuthHeader,
1084
1424
  getTypename,
1085
1425
  handleAuthChallenge,
1086
1426
  protocol,
1087
- toUint8Array
1427
+ toUint8Array,
1428
+ withLogging,
1429
+ withRetry,
1430
+ withRetryConfig
1088
1431
  };
1089
1432
  //# sourceMappingURL=index.mjs.map