@dxos/edge-client 0.8.4-main.f9ba587 → 0.8.4-main.fcc0d83b33

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