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