@dxos/edge-client 0.8.4-main.b97322e → 0.8.4-main.bc2380dfbc

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 (56) hide show
  1. package/LICENSE +102 -5
  2. package/dist/lib/{browser/chunk-LMP5TVOP.mjs → neutral/chunk-ZIQ5T3A7.mjs} +13 -43
  3. package/dist/lib/{browser/chunk-LMP5TVOP.mjs.map → neutral/chunk-ZIQ5T3A7.mjs.map} +3 -3
  4. package/dist/lib/{browser → neutral}/edge-ws-muxer.mjs +1 -1
  5. package/dist/lib/{browser → neutral}/index.mjs +576 -489
  6. package/dist/lib/neutral/index.mjs.map +7 -0
  7. package/dist/lib/neutral/meta.json +1 -0
  8. package/dist/lib/{browser → neutral}/testing/index.mjs +6 -31
  9. package/dist/lib/neutral/testing/index.mjs.map +7 -0
  10. package/dist/types/src/auth.d.ts.map +1 -1
  11. package/dist/types/src/edge-client.d.ts +18 -15
  12. package/dist/types/src/edge-client.d.ts.map +1 -1
  13. package/dist/types/src/edge-http-client.d.ts +80 -22
  14. package/dist/types/src/edge-http-client.d.ts.map +1 -1
  15. package/dist/types/src/edge-identity.d.ts.map +1 -1
  16. package/dist/types/src/edge-ws-connection.d.ts +20 -0
  17. package/dist/types/src/edge-ws-connection.d.ts.map +1 -1
  18. package/dist/types/src/edge-ws-muxer.d.ts.map +1 -1
  19. package/dist/types/src/errors.d.ts.map +1 -1
  20. package/dist/types/src/http-client.d.ts +10 -7
  21. package/dist/types/src/http-client.d.ts.map +1 -1
  22. package/dist/types/src/index.d.ts +4 -3
  23. package/dist/types/src/index.d.ts.map +1 -1
  24. package/dist/types/src/protocol.d.ts +1 -1
  25. package/dist/types/src/protocol.d.ts.map +1 -1
  26. package/dist/types/src/testing/test-server.d.ts.map +1 -1
  27. package/dist/types/src/testing/test-utils.d.ts +3 -3
  28. package/dist/types/src/testing/test-utils.d.ts.map +1 -1
  29. package/dist/types/src/utils.d.ts +1 -1
  30. package/dist/types/src/utils.d.ts.map +1 -1
  31. package/dist/types/tsconfig.tsbuildinfo +1 -1
  32. package/package.json +32 -32
  33. package/src/edge-client.test.ts +20 -15
  34. package/src/edge-client.ts +90 -43
  35. package/src/edge-http-client.test.ts +3 -2
  36. package/src/edge-http-client.ts +355 -74
  37. package/src/edge-ws-connection.ts +121 -7
  38. package/src/edge-ws-muxer.ts +1 -1
  39. package/src/http-client.test.ts +11 -7
  40. package/src/http-client.ts +18 -8
  41. package/src/index.ts +4 -3
  42. package/src/testing/test-utils.ts +8 -8
  43. package/src/websocket.test.ts +1 -1
  44. package/dist/lib/browser/index.mjs.map +0 -7
  45. package/dist/lib/browser/meta.json +0 -1
  46. package/dist/lib/browser/testing/index.mjs.map +0 -7
  47. package/dist/lib/node-esm/chunk-X7J46ISZ.mjs +0 -299
  48. package/dist/lib/node-esm/chunk-X7J46ISZ.mjs.map +0 -7
  49. package/dist/lib/node-esm/edge-ws-muxer.mjs +0 -12
  50. package/dist/lib/node-esm/edge-ws-muxer.mjs.map +0 -7
  51. package/dist/lib/node-esm/index.mjs +0 -1103
  52. package/dist/lib/node-esm/index.mjs.map +0 -7
  53. package/dist/lib/node-esm/meta.json +0 -1
  54. package/dist/lib/node-esm/testing/index.mjs +0 -186
  55. package/dist/lib/node-esm/testing/index.mjs.map +0 -7
  56. /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,23 +141,42 @@ 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
- super(), this._identity = _identity, this._connectionInfo = _connectionInfo, this._callbacks = _callbacks, this._lastReceivedMessageTimestamp = Date.now();
179
+ super(), this._identity = _identity, this._connectionInfo = _connectionInfo, this._callbacks = _callbacks;
81
180
  }
82
181
  get info() {
83
182
  return {
@@ -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: 53,
93
- S: this,
94
- A: [
95
- "this._ws",
96
- ""
97
- ]
98
- });
99
- invariant2(this._wsMuxer, void 0, {
100
- F: __dxlog_file2,
101
- L: 54,
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: 55,
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: 59,
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: 68,
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: 85,
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: 89,
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: 94,
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,56 +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: 101,
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: 104,
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: 112,
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
  }
225
289
  this._lastReceivedMessageTimestamp = Date.now();
226
290
  if (event.data === "__pong__") {
291
+ if (this._pingTimestamp) {
292
+ this._rtt = Date.now() - this._pingTimestamp;
293
+ this._pingTimestamp = void 0;
294
+ }
227
295
  this._rescheduleHeartbeatTimeout();
228
296
  return;
229
297
  }
230
298
  const bytes = await toUint8Array(event.data);
299
+ this._recordBytes(0, bytes.byteLength);
231
300
  if (!this.isOpen) {
232
301
  return;
233
302
  }
303
+ this._messagesReceived++;
234
304
  const message = this._ws?.protocol?.includes(EdgeWebsocketProtocol.V0) ? buf.fromBinary(MessageSchema, bytes) : muxer.receiveData(bytes);
235
305
  if (message) {
236
306
  log("received", {
237
307
  from: message.source,
238
308
  payload: protocol.getPayloadType(message)
239
- }, {
240
- F: __dxlog_file2,
241
- L: 130,
242
- S: this,
243
- C: (f, a) => f(...a)
244
- });
309
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 175, S: this });
245
310
  this._callbacks.onMessage(message);
246
311
  }
247
312
  };
@@ -258,29 +323,18 @@ var EdgeWsConnection = class extends Resource {
258
323
  if (err instanceof Error && err.message.includes("WebSocket is closed before the connection is established.")) {
259
324
  return;
260
325
  }
261
- log.warn("Error closing websocket", {
326
+ log.warn("error closing websocket", {
262
327
  err
263
- }, {
264
- F: __dxlog_file2,
265
- L: 148,
266
- S: this,
267
- C: (f, a) => f(...a)
268
- });
328
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 194, S: this });
269
329
  }
270
330
  }
271
331
  _scheduleHeartbeats() {
272
- invariant2(this._ws, void 0, {
273
- F: __dxlog_file2,
274
- L: 153,
275
- S: this,
276
- A: [
277
- "this._ws",
278
- ""
279
- ]
280
- });
332
+ invariant3(this._ws, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 200, S: this, A: ["this._ws", ""] });
281
333
  scheduleTaskInterval(this._ctx, async () => {
334
+ this._pingTimestamp = Date.now();
282
335
  this._ws?.send("__ping__");
283
336
  }, SIGNAL_KEEPALIVE_INTERVAL);
337
+ this._pingTimestamp = Date.now();
284
338
  this._ws.send("__ping__");
285
339
  this._rescheduleHeartbeatTimeout();
286
340
  }
@@ -289,21 +343,13 @@ var EdgeWsConnection = class extends Resource {
289
343
  return;
290
344
  }
291
345
  void this._inactivityTimeoutCtx?.dispose();
292
- this._inactivityTimeoutCtx = new Context(void 0, {
293
- F: __dxlog_file2,
294
- L: 172
295
- });
346
+ this._inactivityTimeoutCtx = new Context(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 216 });
296
347
  scheduleTask(this._inactivityTimeoutCtx, () => {
297
348
  if (this.isOpen) {
298
349
  if (Date.now() - this._lastReceivedMessageTimestamp > SIGNAL_KEEPALIVE_TIMEOUT) {
299
350
  log.warn("restart due to inactivity timeout", {
300
351
  lastReceivedMessageTimestamp: this._lastReceivedMessageTimestamp
301
- }, {
302
- F: __dxlog_file2,
303
- L: 178,
304
- S: this,
305
- C: (f, a) => f(...a)
306
- });
352
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 220, S: this });
307
353
  this._callbacks.onRestartRequired();
308
354
  } else {
309
355
  this._rescheduleHeartbeatTimeout();
@@ -311,6 +357,47 @@ var EdgeWsConnection = class extends Resource {
311
357
  }
312
358
  }, SIGNAL_KEEPALIVE_TIMEOUT);
313
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
+ }
314
401
  };
315
402
  _ts_decorate([
316
403
  logInfo
@@ -337,20 +424,31 @@ var getEdgeUrlWithProtocol = (baseUrl, protocol2) => {
337
424
  };
338
425
 
339
426
  // src/edge-client.ts
427
+ var __dxlog_file4 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/edge-client.ts";
340
428
  function _ts_decorate2(decorators, target, key, desc) {
341
429
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
342
430
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
343
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;
344
432
  return c > 3 && r && Object.defineProperty(target, key, r), r;
345
433
  }
346
- var __dxlog_file3 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/edge-client.ts";
347
434
  var DEFAULT_TIMEOUT = 1e4;
435
+ var STATUS_REFRESH_INTERVAL = 1e3;
348
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();
349
450
  constructor(_identity, _config) {
350
- super(), this._identity = _identity, this._config = _config, this.statusChanged = new Event(), this._persistentLifecycle = new PersistentLifecycle({
351
- start: async () => this._connect(),
352
- stop: async (state) => this._disconnect(state)
353
- }), 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;
354
452
  this._baseWsUrl = getEdgeUrlWithProtocol(_config.socketEndpoint, "ws");
355
453
  this._baseHttpUrl = getEdgeUrlWithProtocol(_config.socketEndpoint, "http");
356
454
  }
@@ -363,7 +461,15 @@ var EdgeClient = class extends Resource2 {
363
461
  };
364
462
  }
365
463
  get status() {
366
- 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
+ };
367
473
  }
368
474
  get identityKey() {
369
475
  return this._identity.identityKey;
@@ -376,17 +482,39 @@ var EdgeClient = class extends Resource2 {
376
482
  log2("Edge identity changed", {
377
483
  identity,
378
484
  oldIdentity: this._identity
379
- }, {
380
- F: __dxlog_file3,
381
- L: 99,
382
- S: this,
383
- C: (f, a) => f(...a)
384
- });
485
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 74, S: this });
385
486
  this._identity = identity;
386
487
  this._closeCurrentConnection(new EdgeIdentityChangedError());
387
488
  void this._persistentLifecycle.scheduleRestart();
388
489
  }
389
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
+ }
390
518
  onMessage(listener) {
391
519
  this._messageListeners.add(listener);
392
520
  return () => this._messageListeners.delete(listener);
@@ -399,12 +527,7 @@ var EdgeClient = class extends Resource2 {
399
527
  try {
400
528
  listener();
401
529
  } catch (error) {
402
- log2.catch(error, void 0, {
403
- F: __dxlog_file3,
404
- L: 121,
405
- S: this,
406
- C: (f, a) => f(...a)
407
- });
530
+ log2.catch(error, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 123, S: this });
408
531
  }
409
532
  }
410
533
  });
@@ -417,22 +540,18 @@ var EdgeClient = class extends Resource2 {
417
540
  async _open() {
418
541
  log2("opening...", {
419
542
  info: this.info
420
- }, {
421
- F: __dxlog_file3,
422
- L: 133,
423
- S: this,
424
- C: (f, a) => f(...a)
425
- });
543
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 133, S: this });
426
544
  this._persistentLifecycle.open().catch((err) => {
427
545
  log2.warn("Error while opening connection", {
428
546
  err
429
- }, {
430
- F: __dxlog_file3,
431
- L: 135,
432
- S: this,
433
- C: (f, a) => f(...a)
434
- });
547
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 137, S: this });
435
548
  });
549
+ scheduleTaskInterval2(this._ctx, async () => {
550
+ if (!this._currentConnection) {
551
+ return;
552
+ }
553
+ this.statusChanged.emit(this.status);
554
+ }, STATUS_REFRESH_INTERVAL);
436
555
  }
437
556
  /**
438
557
  * Close connection and free resources.
@@ -440,12 +559,7 @@ var EdgeClient = class extends Resource2 {
440
559
  async _close() {
441
560
  log2("closing...", {
442
561
  peerKey: this._identity.peerKey
443
- }, {
444
- F: __dxlog_file3,
445
- L: 143,
446
- S: this,
447
- C: (f, a) => f(...a)
448
- });
562
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 152, S: this });
449
563
  this._closeCurrentConnection();
450
564
  await this._persistentLifecycle.close();
451
565
  }
@@ -457,12 +571,7 @@ var EdgeClient = class extends Resource2 {
457
571
  const path = `/ws/${identity.identityKey}/${identity.peerKey}`;
458
572
  const protocolHeader = this._config.disableAuth ? void 0 : await this._createAuthHeader(path);
459
573
  if (this._identity !== identity) {
460
- log2("identity changed during auth header request", void 0, {
461
- F: __dxlog_file3,
462
- L: 157,
463
- S: this,
464
- C: (f, a) => f(...a)
465
- });
574
+ log2("identity changed during auth header request", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 166, S: this });
466
575
  return void 0;
467
576
  }
468
577
  const restartRequired = new Trigger();
@@ -470,27 +579,20 @@ var EdgeClient = class extends Resource2 {
470
579
  log2("Opening websocket", {
471
580
  url: url.toString(),
472
581
  protocolHeader
473
- }, {
474
- F: __dxlog_file3,
475
- L: 163,
476
- S: this,
477
- C: (f, a) => f(...a)
478
- });
582
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 171, S: this });
479
583
  const connection = new EdgeWsConnection(identity, {
480
584
  url,
481
- protocolHeader
585
+ protocolHeader,
586
+ headers: this._config.clientTag ? {
587
+ "X-DXOS-Client-Tag": this._config.clientTag
588
+ } : void 0
482
589
  }, {
483
590
  onConnected: () => {
484
591
  if (this._isActive(connection)) {
485
592
  this._ready.wake();
486
593
  this._notifyReconnected();
487
594
  } else {
488
- log2.verbose("connected callback ignored, because connection is not active", void 0, {
489
- F: __dxlog_file3,
490
- L: 173,
491
- S: this,
492
- C: (f, a) => f(...a)
493
- });
595
+ log2.verbose("connected callback ignored, because connection is not active", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 187, S: this });
494
596
  }
495
597
  },
496
598
  onRestartRequired: () => {
@@ -498,12 +600,7 @@ var EdgeClient = class extends Resource2 {
498
600
  this._closeCurrentConnection();
499
601
  void this._persistentLifecycle.scheduleRestart();
500
602
  } else {
501
- log2.verbose("restart requested by inactive connection", void 0, {
502
- F: __dxlog_file3,
503
- L: 181,
504
- S: this,
505
- C: (f, a) => f(...a)
506
- });
603
+ log2.verbose("restart requested by inactive connection", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 195, S: this });
507
604
  }
508
605
  restartRequired.wake();
509
606
  },
@@ -514,12 +611,7 @@ var EdgeClient = class extends Resource2 {
514
611
  log2.verbose("ignored a message on inactive connection", {
515
612
  from: message.source,
516
613
  type: message.payload?.typeUrl
517
- }, {
518
- F: __dxlog_file3,
519
- L: 189,
520
- S: this,
521
- C: (f, a) => f(...a)
522
- });
614
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 203, S: this });
523
615
  }
524
616
  }
525
617
  });
@@ -551,12 +643,7 @@ var EdgeClient = class extends Resource2 {
551
643
  } catch (err) {
552
644
  log2.error("ws reconnect listener failed", {
553
645
  err
554
- }, {
555
- F: __dxlog_file3,
556
- L: 225,
557
- S: this,
558
- C: (f, a) => f(...a)
559
- });
646
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 238, S: this });
560
647
  }
561
648
  }
562
649
  }
@@ -568,39 +655,10 @@ var EdgeClient = class extends Resource2 {
568
655
  log2.error("ws incoming message processing failed", {
569
656
  err,
570
657
  payload: protocol.getPayloadType(message)
571
- }, {
572
- F: __dxlog_file3,
573
- L: 235,
574
- S: this,
575
- C: (f, a) => f(...a)
576
- });
658
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 249, S: this });
577
659
  }
578
660
  }
579
661
  }
580
- /**
581
- * Send message.
582
- * NOTE: The message is guaranteed to be delivered but the service must respond with a message to confirm processing.
583
- */
584
- async send(message) {
585
- if (this._ready.state !== TriggerState.RESOLVED) {
586
- log2("waiting for websocket to become ready", void 0, {
587
- F: __dxlog_file3,
588
- L: 246,
589
- S: this,
590
- C: (f, a) => f(...a)
591
- });
592
- await this._ready.wait({
593
- timeout: this._config.timeout ?? DEFAULT_TIMEOUT
594
- });
595
- }
596
- if (!this._currentConnection) {
597
- throw new EdgeConnectionClosedError();
598
- }
599
- if (message.source && (message.source.peerKey !== this._identity.peerKey || message.source.identityKey !== this.identityKey)) {
600
- throw new EdgeIdentityChangedError();
601
- }
602
- this._currentConnection.send(message);
603
- }
604
662
  async _createAuthHeader(path) {
605
663
  const httpUrl = new URL(path, this._baseHttpUrl);
606
664
  httpUrl.protocol = getEdgeUrlWithProtocol(this._baseWsUrl.toString(), "http");
@@ -613,15 +671,11 @@ var EdgeClient = class extends Resource2 {
613
671
  log2.warn("no auth challenge from edge", {
614
672
  status: response.status,
615
673
  statusText: response.statusText
616
- }, {
617
- F: __dxlog_file3,
618
- L: 271,
619
- S: this,
620
- C: (f, a) => f(...a)
621
- });
674
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 265, S: this });
622
675
  return void 0;
623
676
  }
624
677
  }
678
+ _isActive = (connection) => connection === this._currentConnection;
625
679
  };
626
680
  _ts_decorate2([
627
681
  logInfo2
@@ -631,144 +685,39 @@ var encodePresentationWsAuthHeader = (encodedPresentation) => {
631
685
  return `base64url.bearer.authorization.dxos.org.${encodedToken}`;
632
686
  };
633
687
 
634
- // src/auth.ts
635
- import { createCredential, signPresentation } from "@dxos/credentials";
636
- import { invariant as invariant3 } from "@dxos/invariant";
637
- import { Keyring } from "@dxos/keyring";
638
- import { PublicKey } from "@dxos/keys";
639
- var __dxlog_file4 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/auth.ts";
640
- var createDeviceEdgeIdentity = async (signer, key) => {
641
- return {
642
- identityKey: key.toHex(),
643
- peerKey: key.toHex(),
644
- presentCredentials: async ({ challenge }) => {
645
- return signPresentation({
646
- presentation: {
647
- credentials: [
648
- // Verifier requires at least one credential in the presentation to establish the subject.
649
- await createCredential({
650
- assertion: {
651
- "@type": "dxos.halo.credentials.Auth"
652
- },
653
- issuer: key,
654
- subject: key,
655
- signer
656
- })
657
- ]
658
- },
659
- signer,
660
- signerKey: key,
661
- nonce: challenge
662
- });
663
- }
664
- };
665
- };
666
- var createChainEdgeIdentity = async (signer, identityKey, peerKey, chain, credentials) => {
667
- const credentialsToSign = credentials.length > 0 ? credentials : [
668
- await createCredential({
669
- assertion: {
670
- "@type": "dxos.halo.credentials.Auth"
671
- },
672
- issuer: identityKey,
673
- subject: identityKey,
674
- signer,
675
- chain,
676
- signingKey: peerKey
677
- })
678
- ];
679
- return {
680
- identityKey: identityKey.toHex(),
681
- peerKey: peerKey.toHex(),
682
- presentCredentials: async ({ challenge }) => {
683
- invariant3(chain, void 0, {
684
- F: __dxlog_file4,
685
- L: 75,
686
- S: void 0,
687
- A: [
688
- "chain",
689
- ""
690
- ]
691
- });
692
- return signPresentation({
693
- presentation: {
694
- credentials: credentialsToSign
695
- },
696
- signer,
697
- nonce: challenge,
698
- signerKey: peerKey,
699
- chain
700
- });
701
- }
702
- };
703
- };
704
- var createEphemeralEdgeIdentity = async () => {
705
- const keyring = new Keyring();
706
- const key = await keyring.createKey();
707
- return createDeviceEdgeIdentity(keyring, key);
708
- };
709
- var createTestHaloEdgeIdentity = async (signer, identityKey, deviceKey) => {
710
- const deviceAdmission = await createCredential({
711
- assertion: {
712
- "@type": "dxos.halo.credentials.AuthorizedDevice",
713
- deviceKey,
714
- identityKey
715
- },
716
- issuer: identityKey,
717
- subject: deviceKey,
718
- signer
719
- });
720
- return createChainEdgeIdentity(signer, identityKey, deviceKey, {
721
- credential: deviceAdmission
722
- }, [
723
- await createCredential({
724
- assertion: {
725
- "@type": "dxos.halo.credentials.Auth"
726
- },
727
- issuer: identityKey,
728
- subject: identityKey,
729
- signer
730
- })
731
- ]);
732
- };
733
- var createStubEdgeIdentity = () => {
734
- const identityKey = PublicKey.random();
735
- const deviceKey = PublicKey.random();
736
- return {
737
- identityKey: identityKey.toHex(),
738
- peerKey: deviceKey.toHex(),
739
- presentCredentials: async () => {
740
- throw new Error("Stub identity does not support authentication.");
741
- }
742
- };
743
- };
744
-
745
688
  // src/edge-http-client.ts
746
- import { FetchHttpClient, HttpClient } from "@effect/platform";
747
- 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";
748
693
  import { sleep } from "@dxos/async";
749
- 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";
750
697
  import { log as log4 } from "@dxos/log";
751
- import { EdgeAuthChallengeError, EdgeCallFailedError } from "@dxos/protocols";
698
+ import { EDGE_CLIENT_TAG_HEADER, EdgeAuthChallengeError, EdgeCallFailedError } from "@dxos/protocols";
752
699
  import { createUrl } from "@dxos/util";
753
700
 
754
701
  // src/http-client.ts
755
- 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";
756
707
  import { log as log3 } from "@dxos/log";
757
708
  var __dxlog_file5 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/http-client.ts";
758
709
  var HttpConfig = class _HttpConfig extends Context2.Tag("HttpConfig")() {
759
- static {
760
- this.default = Layer.succeed(_HttpConfig, {
761
- timeout: Duration.millis(1e3),
762
- retryTimes: 3,
763
- retryBaseDelay: Duration.millis(1e3)
764
- });
765
- }
710
+ static default = Layer.succeed(_HttpConfig, {
711
+ timeout: Duration.millis(1e3),
712
+ retryTimes: 3,
713
+ retryBaseDelay: Duration.millis(1e3)
714
+ });
766
715
  };
767
- 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 } = {}) => {
768
717
  return effect.pipe(Effect.flatMap((res) => (
769
718
  // Treat 500 errors as retryable?
770
719
  res.status === 500 ? Effect.fail(new Error(res.status.toString())) : res.json
771
- )), Effect.timeout(timeout), Effect.retry({
720
+ )), Effect.timeout(timeout2), Effect.retry({
772
721
  schedule: Schedule.exponential(retryBaseDelay).pipe(Schedule.jittered),
773
722
  times: retryTimes
774
723
  }));
@@ -777,14 +726,11 @@ var withRetryConfig = (effect) => Effect.gen(function* () {
777
726
  const config = yield* HttpConfig;
778
727
  return yield* withRetry(effect, config);
779
728
  });
780
- var withLogging = (effect) => effect.pipe(Effect.tap((res) => log3.info("response", {
781
- status: res.status
782
- }, {
783
- F: __dxlog_file5,
784
- L: 58,
785
- S: void 0,
786
- C: (f, a) => f(...a)
787
- })));
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
+ }));
788
734
  var encodeAuthHeader = (challenge) => {
789
735
  const encodedChallenge = Buffer.from(challenge).toString("base64");
790
736
  return `VerifiablePresentation pb;base64,${encodedChallenge}`;
@@ -795,17 +741,21 @@ var __dxlog_file6 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/edge-http
795
741
  var DEFAULT_RETRY_TIMEOUT = 1500;
796
742
  var DEFAULT_RETRY_JITTER = 500;
797
743
  var DEFAULT_MAX_RETRIES_COUNT = 3;
744
+ var WARNING_BODY_SIZE = 10 * 1024 * 1024;
798
745
  var EdgeHttpClient = class {
799
- 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) {
800
754
  this._baseUrl = getEdgeUrlWithProtocol(baseUrl, "http");
755
+ this._clientTag = options?.clientTag;
801
756
  log4("created", {
802
757
  url: this._baseUrl
803
- }, {
804
- F: __dxlog_file6,
805
- L: 84,
806
- S: this,
807
- C: (f, a) => f(...a)
808
- });
758
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 32, S: this });
809
759
  }
810
760
  get baseUrl() {
811
761
  return this._baseUrl;
@@ -819,24 +769,25 @@ var EdgeHttpClient = class {
819
769
  //
820
770
  // Status
821
771
  //
822
- async getStatus(args) {
823
- return this._call(new URL("/status", this.baseUrl), {
772
+ async getStatus(ctx, args) {
773
+ return this._call(ctx, new URL("/status", this.baseUrl), {
824
774
  ...args,
825
- method: "GET"
775
+ method: "GET",
776
+ auth: true
826
777
  });
827
778
  }
828
779
  //
829
780
  // Agents
830
781
  //
831
- createAgent(body, args) {
832
- 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), {
833
784
  ...args,
834
785
  method: "POST",
835
786
  body
836
787
  });
837
788
  }
838
- getAgentStatus(request, args) {
839
- 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), {
840
791
  ...args,
841
792
  method: "GET"
842
793
  });
@@ -844,14 +795,14 @@ var EdgeHttpClient = class {
844
795
  //
845
796
  // Credentials
846
797
  //
847
- getCredentialsForNotarization(spaceId, args) {
848
- 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), {
849
800
  ...args,
850
801
  method: "GET"
851
802
  });
852
803
  }
853
- async notarizeCredentials(spaceId, body, args) {
854
- 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), {
855
806
  ...args,
856
807
  body,
857
808
  method: "POST"
@@ -860,8 +811,8 @@ var EdgeHttpClient = class {
860
811
  //
861
812
  // Identity
862
813
  //
863
- async recoverIdentity(body, args) {
864
- 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), {
865
816
  ...args,
866
817
  body,
867
818
  method: "POST"
@@ -870,8 +821,8 @@ var EdgeHttpClient = class {
870
821
  //
871
822
  // Invitations
872
823
  //
873
- async joinSpaceByInvitation(spaceId, body, args) {
874
- 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), {
875
826
  ...args,
876
827
  body,
877
828
  method: "POST"
@@ -880,14 +831,8 @@ var EdgeHttpClient = class {
880
831
  //
881
832
  // OAuth and credentials
882
833
  //
883
- async listFunctions(args) {
884
- return this._call(new URL("/functions", this.baseUrl), {
885
- ...args,
886
- method: "GET"
887
- });
888
- }
889
- async initiateOAuthFlow(body, args) {
890
- 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), {
891
836
  ...args,
892
837
  body,
893
838
  method: "POST"
@@ -896,8 +841,8 @@ var EdgeHttpClient = class {
896
841
  //
897
842
  // Spaces
898
843
  //
899
- async createSpace(body, args) {
900
- 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), {
901
846
  ...args,
902
847
  body,
903
848
  method: "POST"
@@ -906,9 +851,10 @@ var EdgeHttpClient = class {
906
851
  //
907
852
  // Queues
908
853
  //
909
- async queryQueue(subspaceTag, spaceId, query, args) {
910
- const { queueId } = query;
911
- 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), {
912
858
  after: query.after,
913
859
  before: query.before,
914
860
  limit: query.limit,
@@ -919,8 +865,8 @@ var EdgeHttpClient = class {
919
865
  method: "GET"
920
866
  });
921
867
  }
922
- async insertIntoQueue(subspaceTag, spaceId, queueId, objects, args) {
923
- 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), {
924
870
  ...args,
925
871
  body: {
926
872
  objects
@@ -928,8 +874,8 @@ var EdgeHttpClient = class {
928
874
  method: "POST"
929
875
  });
930
876
  }
931
- async deleteFromQueue(subspaceTag, spaceId, queueId, objectIds, args) {
932
- 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), {
933
879
  ids: objectIds.join(",")
934
880
  }), {
935
881
  ...args,
@@ -939,98 +885,202 @@ var EdgeHttpClient = class {
939
885
  //
940
886
  // Functions
941
887
  //
942
- 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
+ }
943
902
  const path = [
944
903
  "functions",
945
904
  ...pathParts.functionId ? [
946
905
  pathParts.functionId
947
906
  ] : []
948
907
  ].join("/");
949
- return this._call(new URL(path, this.baseUrl), {
908
+ return this._call(ctx, new URL(path, this.baseUrl), {
950
909
  ...args,
951
- body,
952
- 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"
953
939
  });
954
940
  }
955
941
  //
956
942
  // Workflows
957
943
  //
958
- async executeWorkflow(spaceId, graphId, input, args) {
959
- 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), {
960
946
  ...args,
961
947
  body: input,
962
948
  method: "POST"
963
949
  });
964
950
  }
965
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
+ * Fetches the available release versions for a given plugin repo. `repo` is the
992
+ * GitHub `owner/name` form; this method takes care of URL-encoding before issuing
993
+ * the request. Unauthenticated; same surface area as {@link getRegistryPlugins}.
994
+ *
995
+ * Versions are returned newest first, suitable for direct rendering in a picker.
996
+ */
997
+ async getRegistryPluginVersions(ctx, repo, args) {
998
+ return this._call(ctx, new URL(`/registry/plugins/${encodeURIComponent(repo)}/versions`, this.baseUrl), {
999
+ ...args,
1000
+ method: "GET"
1001
+ });
1002
+ }
1003
+ //
1004
+ // Import/Export space.
1005
+ //
1006
+ async importBundle(ctx, spaceId, body, args) {
1007
+ return this._call(ctx, new URL(`/spaces/${spaceId}/import`, this.baseUrl), {
1008
+ ...args,
1009
+ body,
1010
+ method: "PUT"
1011
+ });
1012
+ }
1013
+ async exportBundle(ctx, spaceId, body, args) {
1014
+ return this._call(ctx, new URL(`/spaces/${spaceId}/export`, this.baseUrl), {
1015
+ ...args,
1016
+ body,
1017
+ method: "POST"
1018
+ });
1019
+ }
1020
+ //
966
1021
  // Internal
967
1022
  //
968
- async _fetch(url, args) {
969
- return pipe(HttpClient.get(url), withLogging, withRetryConfig, Effect2.provide(FetchHttpClient.layer), Effect2.provide(HttpConfig.default), Effect2.withSpan("EdgeHttpClient"), Effect2.runPromise);
1023
+ async _fetch(url, _args) {
1024
+ return Function.pipe(HttpClient.get(url), withLogging, withRetryConfig, Effect2.provide(FetchHttpClient.layer), Effect2.provide(HttpConfig.default), Effect2.withSpan("EdgeHttpClient"), runAndForwardErrors);
970
1025
  }
971
1026
  // TODO(burdon): Refactor with effect (see edge-http-client.test.ts).
972
- async _call(url, args) {
1027
+ async _call(ctx, url, args) {
973
1028
  const shouldRetry = createRetryHandler(args);
974
- const requestContext = args.context ?? new Context3(void 0, {
975
- F: __dxlog_file6,
976
- L: 293
977
- });
978
1029
  log4("fetch", {
979
1030
  url,
980
1031
  request: args.body
981
- }, {
982
- F: __dxlog_file6,
983
- L: 294,
984
- S: this,
985
- C: (f, a) => f(...a)
986
- });
1032
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 302, S: this });
1033
+ const traceHeaders = getTraceHeaders(ctx);
987
1034
  let handledAuth = false;
1035
+ const tryCount = 1;
988
1036
  while (true) {
989
- let processingError;
990
- let retryAfterHeaderValue = Number.NaN;
1037
+ let processingError = void 0;
991
1038
  try {
992
- const request = createRequest(args, this._authHeader);
1039
+ if (!this._authHeader && args.auth) {
1040
+ const response2 = await fetch(new URL(`/auth`, this.baseUrl));
1041
+ if (response2.status === 401) {
1042
+ this._authHeader = await this._handleUnauthorized(response2);
1043
+ }
1044
+ }
1045
+ const request = createRequest(args, this._authHeader, traceHeaders, this._clientTag);
1046
+ log4("call edge", {
1047
+ url,
1048
+ tryCount,
1049
+ authHeader: !!this._authHeader
1050
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 319, S: this });
993
1051
  const response = await fetch(url, request);
994
- retryAfterHeaderValue = Number(response.headers.get("Retry-After"));
995
1052
  if (response.ok) {
996
- const body = await response.json();
997
- if (body.success) {
998
- return body.data;
1053
+ const body2 = await response.clone().json();
1054
+ invariant4(body2, "Expected body to be present", { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 327, S: this, A: ["body", "'Expected body to be present'"] });
1055
+ if (!("success" in body2)) {
1056
+ return body2;
999
1057
  }
1000
- log4.warn("unsuccessful edge response", {
1001
- url,
1002
- body
1003
- }, {
1004
- F: __dxlog_file6,
1005
- L: 310,
1006
- S: this,
1007
- C: (f, a) => f(...a)
1008
- });
1009
- if (body.errorData?.type === "auth_challenge" && typeof body.errorData?.challenge === "string") {
1010
- processingError = new EdgeAuthChallengeError(body.errorData.challenge, body.errorData);
1011
- } else {
1012
- processingError = EdgeCallFailedError.fromUnsuccessfulResponse(response, body);
1058
+ if (body2.success) {
1059
+ return body2.data;
1013
1060
  }
1014
1061
  } else if (response.status === 401 && !handledAuth) {
1015
1062
  this._authHeader = await this._handleUnauthorized(response);
1016
1063
  handledAuth = true;
1017
1064
  continue;
1065
+ }
1066
+ const body = response.headers.get("Content-Type") === "application/json" ? await response.clone().json() : void 0;
1067
+ invariant4(!body?.success, "Expected body to not be a failure response or undefined.", { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 340, S: this, A: ["!body?.success", "'Expected body to not be a failure response or undefined.'"] });
1068
+ if (body?.data?.type === "auth_challenge" && typeof body?.data?.challenge === "string") {
1069
+ processingError = new EdgeAuthChallengeError(body.data.challenge, body.data);
1070
+ } else if (body?.success === false) {
1071
+ processingError = EdgeCallFailedError.fromUnsuccessfulResponse(response, body);
1018
1072
  } else {
1019
- processingError = EdgeCallFailedError.fromHttpFailure(response);
1073
+ invariant4(!response.ok, "Expected response to not be ok.", { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 346, S: this, A: ["!response.ok", "'Expected response to not be ok.'"] });
1074
+ processingError = await EdgeCallFailedError.fromHttpFailure(response);
1020
1075
  }
1021
1076
  } catch (error) {
1022
1077
  processingError = EdgeCallFailedError.fromProcessingFailureCause(error);
1023
1078
  }
1024
- if (processingError.isRetryable && await shouldRetry(requestContext, retryAfterHeaderValue)) {
1025
- log4("retrying edge request", {
1079
+ if (processingError?.isRetryable && await shouldRetry(ctx, processingError.retryAfterMs)) {
1080
+ log4.verbose("retrying edge request", {
1026
1081
  url,
1027
1082
  processingError
1028
- }, {
1029
- F: __dxlog_file6,
1030
- L: 328,
1031
- S: this,
1032
- C: (f, a) => f(...a)
1033
- });
1083
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 353, S: this });
1034
1084
  } else {
1035
1085
  throw processingError;
1036
1086
  }
@@ -1038,35 +1088,63 @@ var EdgeHttpClient = class {
1038
1088
  }
1039
1089
  async _handleUnauthorized(response) {
1040
1090
  if (!this._edgeIdentity) {
1041
- log4.warn("unauthorized response received before identity was set", void 0, {
1042
- F: __dxlog_file6,
1043
- L: 337,
1044
- S: this,
1045
- C: (f, a) => f(...a)
1046
- });
1047
- throw EdgeCallFailedError.fromHttpFailure(response);
1091
+ log4.warn("unauthorized response received before identity was set", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 364, S: this });
1092
+ throw await EdgeCallFailedError.fromHttpFailure(response);
1048
1093
  }
1049
1094
  const challenge = await handleAuthChallenge(response, this._edgeIdentity);
1050
1095
  return encodeAuthHeader(challenge);
1051
1096
  }
1052
1097
  };
1053
- var createRequest = ({ method, body }, authHeader) => {
1098
+ var createRequest = ({ method, body, json = true }, authHeader, traceHeaders, clientTag) => {
1099
+ let requestBody;
1100
+ const headers = {};
1101
+ if (json) {
1102
+ requestBody = body && JSON.stringify(body);
1103
+ headers["Content-Type"] = "application/json";
1104
+ } else {
1105
+ requestBody = body;
1106
+ }
1107
+ if (typeof requestBody === "string" && requestBody.length > WARNING_BODY_SIZE) {
1108
+ log4.warn("Request with large body", {
1109
+ bodySize: requestBody.length
1110
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 381, S: void 0 });
1111
+ }
1112
+ if (authHeader) {
1113
+ headers["Authorization"] = authHeader;
1114
+ }
1115
+ if (traceHeaders) {
1116
+ Object.assign(headers, traceHeaders);
1117
+ }
1118
+ if (clientTag) {
1119
+ headers[EDGE_CLIENT_TAG_HEADER] = clientTag;
1120
+ }
1054
1121
  return {
1055
1122
  method,
1056
- body: body && JSON.stringify(body),
1057
- headers: authHeader ? {
1058
- Authorization: authHeader
1059
- } : void 0
1123
+ body: requestBody,
1124
+ headers
1060
1125
  };
1061
1126
  };
1062
- var createRetryHandler = ({ retry }) => {
1063
- if (!retry || retry.count < 1) {
1127
+ var getTraceHeaders = (ctx) => {
1128
+ const traceCtx = ctx.getAttribute(TRACE_SPAN_ATTRIBUTE2);
1129
+ if (!traceCtx) {
1130
+ return void 0;
1131
+ }
1132
+ const headers = {
1133
+ traceparent: traceCtx.traceparent
1134
+ };
1135
+ if (traceCtx.tracestate) {
1136
+ headers.tracestate = traceCtx.tracestate;
1137
+ }
1138
+ return headers;
1139
+ };
1140
+ var createRetryHandler = ({ retry: retry2 }) => {
1141
+ if (!retry2 || retry2.count < 1) {
1064
1142
  return async () => false;
1065
1143
  }
1066
1144
  let retries = 0;
1067
- const maxRetries = retry.count ?? DEFAULT_MAX_RETRIES_COUNT;
1068
- const baseTimeout = retry.timeout ?? DEFAULT_RETRY_TIMEOUT;
1069
- const jitter = retry.jitter ?? DEFAULT_RETRY_JITTER;
1145
+ const maxRetries = retry2.count ?? DEFAULT_MAX_RETRIES_COUNT;
1146
+ const baseTimeout = retry2.timeout ?? DEFAULT_RETRY_TIMEOUT;
1147
+ const jitter = retry2.jitter ?? DEFAULT_RETRY_JITTER;
1070
1148
  return async (ctx, retryAfter) => {
1071
1149
  if (++retries > maxRetries || ctx.disposed) {
1072
1150
  return false;
@@ -1074,12 +1152,16 @@ var createRetryHandler = ({ retry }) => {
1074
1152
  if (retryAfter) {
1075
1153
  await sleep(retryAfter);
1076
1154
  } else {
1077
- const timeout = baseTimeout + Math.random() * jitter;
1078
- await sleep(timeout);
1155
+ const timeout2 = baseTimeout + Math.random() * jitter;
1156
+ await sleep(timeout2);
1079
1157
  }
1080
1158
  return true;
1081
1159
  };
1082
1160
  };
1161
+ var getFileMimeType = (filename) => [
1162
+ ".js",
1163
+ ".mjs"
1164
+ ].some((codeExtension) => filename.endsWith(codeExtension)) ? "application/javascript+module" : filename.endsWith(".wasm") ? "application/wasm" : "application/octet-stream";
1083
1165
  export {
1084
1166
  CLOUDFLARE_MESSAGE_MAX_BYTES,
1085
1167
  CLOUDFLARE_RPC_MAX_BYTES,
@@ -1087,6 +1169,7 @@ export {
1087
1169
  EdgeConnectionClosedError,
1088
1170
  EdgeHttpClient,
1089
1171
  EdgeIdentityChangedError,
1172
+ HttpConfig,
1090
1173
  Protocol,
1091
1174
  WebSocketMuxer,
1092
1175
  createChainEdgeIdentity,
@@ -1094,9 +1177,13 @@ export {
1094
1177
  createEphemeralEdgeIdentity,
1095
1178
  createStubEdgeIdentity,
1096
1179
  createTestHaloEdgeIdentity,
1180
+ encodeAuthHeader,
1097
1181
  getTypename,
1098
1182
  handleAuthChallenge,
1099
1183
  protocol,
1100
- toUint8Array
1184
+ toUint8Array,
1185
+ withLogging,
1186
+ withRetry,
1187
+ withRetryConfig
1101
1188
  };
1102
1189
  //# sourceMappingURL=index.mjs.map