@dxos/edge-client 0.6.13 → 0.6.14-main.7bd9c89
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.
- package/dist/lib/browser/chunk-ZWJXA37R.mjs +113 -0
- package/dist/lib/browser/chunk-ZWJXA37R.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +469 -160
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +125 -0
- package/dist/lib/browser/testing/index.mjs.map +7 -0
- package/dist/lib/node/chunk-ANV2HBEH.cjs +136 -0
- package/dist/lib/node/chunk-ANV2HBEH.cjs.map +7 -0
- package/dist/lib/node/index.cjs +468 -158
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +155 -0
- package/dist/lib/node/testing/index.cjs.map +7 -0
- package/dist/lib/node-esm/chunk-HNVT57AU.mjs +115 -0
- package/dist/lib/node-esm/chunk-HNVT57AU.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +787 -0
- package/dist/lib/node-esm/index.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -0
- package/dist/lib/node-esm/testing/index.mjs +126 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/types/src/auth.d.ts +22 -0
- package/dist/types/src/auth.d.ts.map +1 -0
- package/dist/types/src/defs.d.ts.map +1 -1
- package/dist/types/src/edge-client.d.ts +14 -13
- package/dist/types/src/edge-client.d.ts.map +1 -1
- package/dist/types/src/edge-http-client.d.ts +48 -0
- package/dist/types/src/edge-http-client.d.ts.map +1 -0
- package/dist/types/src/edge-identity.d.ts +15 -0
- package/dist/types/src/edge-identity.d.ts.map +1 -0
- package/dist/types/src/errors.d.ts +4 -1
- package/dist/types/src/errors.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +4 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/protocol.d.ts +2 -2
- package/dist/types/src/protocol.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/test-utils.d.ts +21 -0
- package/dist/types/src/testing/test-utils.d.ts.map +1 -0
- package/dist/types/src/utils.d.ts +2 -0
- package/dist/types/src/utils.d.ts.map +1 -0
- package/package.json +29 -14
- package/src/auth.ts +135 -0
- package/src/defs.ts +2 -3
- package/src/edge-client.test.ts +50 -18
- package/src/edge-client.ts +76 -24
- package/src/edge-http-client.ts +210 -0
- package/src/edge-identity.ts +31 -0
- package/src/errors.ts +8 -2
- package/src/index.ts +4 -0
- package/src/persistent-lifecycle.test.ts +2 -2
- package/src/protocol.test.ts +1 -2
- package/src/protocol.ts +2 -2
- package/src/testing/index.ts +5 -0
- package/src/testing/test-utils.ts +114 -0
- package/src/utils.ts +10 -0
- package/src/websocket.test.ts +5 -4
- package/dist/types/src/test-utils.d.ts +0 -11
- package/dist/types/src/test-utils.d.ts.map +0 -1
- package/src/test-utils.ts +0 -49
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Protocol,
|
|
3
|
+
getTypename,
|
|
4
|
+
protocol,
|
|
5
|
+
toUint8Array
|
|
6
|
+
} from "./chunk-ZWJXA37R.mjs";
|
|
7
|
+
|
|
1
8
|
// packages/core/mesh/edge-client/src/index.ts
|
|
2
9
|
export * from "@dxos/protocols/buf/dxos/edge/messenger_pb";
|
|
3
10
|
|
|
@@ -5,123 +12,59 @@ export * from "@dxos/protocols/buf/dxos/edge/messenger_pb";
|
|
|
5
12
|
import WebSocket from "isomorphic-ws";
|
|
6
13
|
import { Trigger, Event, scheduleTaskInterval, scheduleTask, TriggerState } from "@dxos/async";
|
|
7
14
|
import { Context, LifecycleState as LifecycleState2, Resource as Resource2 } from "@dxos/context";
|
|
8
|
-
import { invariant as invariant2 } from "@dxos/invariant";
|
|
9
15
|
import { log as log2 } from "@dxos/log";
|
|
10
|
-
import { buf
|
|
11
|
-
import { MessageSchema
|
|
12
|
-
|
|
13
|
-
// packages/core/mesh/edge-client/src/defs.ts
|
|
14
|
-
import { AnySchema } from "@bufbuild/protobuf/wkt";
|
|
15
|
-
import { SwarmRequestSchema, SwarmResponseSchema, TextMessageSchema } from "@dxos/protocols/buf/dxos/edge/messenger_pb";
|
|
16
|
+
import { buf } from "@dxos/protocols/buf";
|
|
17
|
+
import { MessageSchema } from "@dxos/protocols/buf/dxos/edge/messenger_pb";
|
|
16
18
|
|
|
17
|
-
// packages/core/mesh/edge-client/src/
|
|
19
|
+
// packages/core/mesh/edge-client/src/edge-identity.ts
|
|
18
20
|
import { invariant } from "@dxos/invariant";
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const payloadTypename = this.getPayloadType(message);
|
|
56
|
-
if (type && type.typeName !== payloadTypename) {
|
|
57
|
-
throw new Error(`Unexpected payload type: ${payloadTypename}; expected ${type.typeName}`);
|
|
58
|
-
}
|
|
59
|
-
invariant(bufWkt.anyIs(message.payload, type), `Unexpected payload type: ${payloadTypename}}`, {
|
|
60
|
-
F: __dxlog_file,
|
|
61
|
-
L: 46,
|
|
62
|
-
S: this,
|
|
63
|
-
A: [
|
|
64
|
-
"bufWkt.anyIs(message.payload, type)",
|
|
65
|
-
"`Unexpected payload type: ${payloadTypename}}`"
|
|
66
|
-
]
|
|
67
|
-
});
|
|
68
|
-
const payload = bufWkt.anyUnpack(message.payload, this.typeRegistry);
|
|
69
|
-
invariant(payload, `Empty payload: ${payloadTypename}}`, {
|
|
70
|
-
F: __dxlog_file,
|
|
71
|
-
L: 48,
|
|
72
|
-
S: this,
|
|
73
|
-
A: [
|
|
74
|
-
"payload",
|
|
75
|
-
"`Empty payload: ${payloadTypename}}`"
|
|
76
|
-
]
|
|
77
|
-
});
|
|
78
|
-
return payload;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Get the payload type.
|
|
82
|
-
*/
|
|
83
|
-
getPayloadType(message) {
|
|
84
|
-
if (!message.payload) {
|
|
85
|
-
return void 0;
|
|
86
|
-
}
|
|
87
|
-
const [, type] = message.payload.typeUrl.split("/");
|
|
88
|
-
return type;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Create a packed message.
|
|
92
|
-
*/
|
|
93
|
-
createMessage(type, { source, target, payload, serviceId }) {
|
|
94
|
-
return buf.create(MessageSchema, {
|
|
95
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
96
|
-
source,
|
|
97
|
-
target,
|
|
98
|
-
serviceId,
|
|
99
|
-
payload: payload ? bufWkt.anyPack(type, buf.create(type, payload)) : void 0
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
var toUint8Array = async (data) => {
|
|
104
|
-
if (data instanceof Buffer) {
|
|
105
|
-
return bufferToArray(data);
|
|
106
|
-
}
|
|
107
|
-
if (data instanceof Blob) {
|
|
108
|
-
return new Uint8Array(await data.arrayBuffer());
|
|
109
|
-
}
|
|
110
|
-
throw new Error(`Unexpected datatype: ${data}`);
|
|
21
|
+
import { schema } from "@dxos/protocols/proto";
|
|
22
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-identity.ts";
|
|
23
|
+
var handleAuthChallenge = async (failedResponse, identity) => {
|
|
24
|
+
invariant(failedResponse.status === 401, void 0, {
|
|
25
|
+
F: __dxlog_file,
|
|
26
|
+
L: 21,
|
|
27
|
+
S: void 0,
|
|
28
|
+
A: [
|
|
29
|
+
"failedResponse.status === 401",
|
|
30
|
+
""
|
|
31
|
+
]
|
|
32
|
+
});
|
|
33
|
+
const headerValue = failedResponse.headers.get("Www-Authenticate");
|
|
34
|
+
invariant(headerValue?.startsWith("VerifiablePresentation challenge="), void 0, {
|
|
35
|
+
F: __dxlog_file,
|
|
36
|
+
L: 24,
|
|
37
|
+
S: void 0,
|
|
38
|
+
A: [
|
|
39
|
+
"headerValue?.startsWith('VerifiablePresentation challenge=')",
|
|
40
|
+
""
|
|
41
|
+
]
|
|
42
|
+
});
|
|
43
|
+
const challenge = headerValue?.slice("VerifiablePresentation challenge=".length);
|
|
44
|
+
invariant(challenge, void 0, {
|
|
45
|
+
F: __dxlog_file,
|
|
46
|
+
L: 27,
|
|
47
|
+
S: void 0,
|
|
48
|
+
A: [
|
|
49
|
+
"challenge",
|
|
50
|
+
""
|
|
51
|
+
]
|
|
52
|
+
});
|
|
53
|
+
const presentation = await identity.presentCredentials({
|
|
54
|
+
challenge: Buffer.from(challenge, "base64")
|
|
55
|
+
});
|
|
56
|
+
return schema.getCodecForType("dxos.halo.credentials.Presentation").encode(presentation);
|
|
111
57
|
};
|
|
112
58
|
|
|
113
|
-
// packages/core/mesh/edge-client/src/defs.ts
|
|
114
|
-
var protocol = new Protocol([
|
|
115
|
-
SwarmRequestSchema,
|
|
116
|
-
SwarmResponseSchema,
|
|
117
|
-
TextMessageSchema,
|
|
118
|
-
AnySchema
|
|
119
|
-
]);
|
|
120
|
-
|
|
121
59
|
// packages/core/mesh/edge-client/src/errors.ts
|
|
122
|
-
var
|
|
60
|
+
var EdgeConnectionClosedError = class extends Error {
|
|
123
61
|
constructor() {
|
|
124
|
-
super("
|
|
62
|
+
super("Edge connection closed.");
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
var EdgeIdentityChangedError = class extends Error {
|
|
66
|
+
constructor() {
|
|
67
|
+
super("Edge identity changed.");
|
|
125
68
|
}
|
|
126
69
|
};
|
|
127
70
|
|
|
@@ -218,17 +161,25 @@ _ts_decorate([
|
|
|
218
161
|
synchronized
|
|
219
162
|
], PersistentLifecycle.prototype, "scheduleRestart", null);
|
|
220
163
|
|
|
164
|
+
// packages/core/mesh/edge-client/src/utils.ts
|
|
165
|
+
var getEdgeUrlWithProtocol = (baseUrl, protocol2) => {
|
|
166
|
+
const isSecure = baseUrl.startsWith("https") || baseUrl.startsWith("wss");
|
|
167
|
+
const url = new URL(baseUrl);
|
|
168
|
+
url.protocol = protocol2 + (isSecure ? "s" : "");
|
|
169
|
+
return url.toString();
|
|
170
|
+
};
|
|
171
|
+
|
|
221
172
|
// packages/core/mesh/edge-client/src/edge-client.ts
|
|
222
173
|
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-client.ts";
|
|
223
174
|
var DEFAULT_TIMEOUT = 1e4;
|
|
224
175
|
var SIGNAL_KEEPALIVE_INTERVAL = 5e3;
|
|
225
176
|
var EdgeClient = class extends Resource2 {
|
|
226
|
-
constructor(
|
|
177
|
+
constructor(_identity, _config) {
|
|
227
178
|
super();
|
|
228
|
-
this.
|
|
229
|
-
this._peerKey = _peerKey;
|
|
179
|
+
this._identity = _identity;
|
|
230
180
|
this._config = _config;
|
|
231
181
|
this.reconnect = new Event();
|
|
182
|
+
this.connected = new Event();
|
|
232
183
|
this._persistentLifecycle = new PersistentLifecycle({
|
|
233
184
|
start: async () => this._openWebSocket(),
|
|
234
185
|
stop: async () => this._closeWebSocket(),
|
|
@@ -239,26 +190,40 @@ var EdgeClient = class extends Resource2 {
|
|
|
239
190
|
this._ws = void 0;
|
|
240
191
|
this._keepaliveCtx = void 0;
|
|
241
192
|
this._heartBeatContext = void 0;
|
|
242
|
-
this.
|
|
193
|
+
this._baseWsUrl = getEdgeUrlWithProtocol(_config.socketEndpoint, "ws");
|
|
194
|
+
this._baseHttpUrl = getEdgeUrlWithProtocol(_config.socketEndpoint, "http");
|
|
243
195
|
}
|
|
244
196
|
// TODO(burdon): Attach logging.
|
|
245
197
|
get info() {
|
|
246
198
|
return {
|
|
247
199
|
open: this.isOpen,
|
|
248
|
-
identity: this.
|
|
249
|
-
device: this.
|
|
200
|
+
identity: this._identity.identityKey,
|
|
201
|
+
device: this._identity.peerKey
|
|
250
202
|
};
|
|
251
203
|
}
|
|
204
|
+
get isConnected() {
|
|
205
|
+
return Boolean(this._ws) && this._ready.state === TriggerState.RESOLVED;
|
|
206
|
+
}
|
|
252
207
|
get identityKey() {
|
|
253
|
-
return this.
|
|
208
|
+
return this._identity.identityKey;
|
|
254
209
|
}
|
|
255
210
|
get peerKey() {
|
|
256
|
-
return this.
|
|
211
|
+
return this._identity.peerKey;
|
|
257
212
|
}
|
|
258
|
-
setIdentity(
|
|
259
|
-
this.
|
|
260
|
-
|
|
261
|
-
|
|
213
|
+
setIdentity(identity) {
|
|
214
|
+
if (identity.identityKey !== this._identity.identityKey || identity.peerKey !== this._identity.peerKey) {
|
|
215
|
+
log2("Edge identity changed", {
|
|
216
|
+
identity,
|
|
217
|
+
oldIdentity: this._identity
|
|
218
|
+
}, {
|
|
219
|
+
F: __dxlog_file3,
|
|
220
|
+
L: 99,
|
|
221
|
+
S: this,
|
|
222
|
+
C: (f, a) => f(...a)
|
|
223
|
+
});
|
|
224
|
+
this._identity = identity;
|
|
225
|
+
this._persistentLifecycle.scheduleRestart();
|
|
226
|
+
}
|
|
262
227
|
}
|
|
263
228
|
addListener(listener) {
|
|
264
229
|
this._listeners.add(listener);
|
|
@@ -272,7 +237,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
272
237
|
info: this.info
|
|
273
238
|
}, {
|
|
274
239
|
F: __dxlog_file3,
|
|
275
|
-
L:
|
|
240
|
+
L: 114,
|
|
276
241
|
S: this,
|
|
277
242
|
C: (f, a) => f(...a)
|
|
278
243
|
});
|
|
@@ -281,7 +246,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
281
246
|
err
|
|
282
247
|
}, {
|
|
283
248
|
F: __dxlog_file3,
|
|
284
|
-
L:
|
|
249
|
+
L: 116,
|
|
285
250
|
S: this,
|
|
286
251
|
C: (f, a) => f(...a)
|
|
287
252
|
});
|
|
@@ -292,31 +257,48 @@ var EdgeClient = class extends Resource2 {
|
|
|
292
257
|
*/
|
|
293
258
|
async _close() {
|
|
294
259
|
log2("closing...", {
|
|
295
|
-
peerKey: this.
|
|
260
|
+
peerKey: this._identity.peerKey
|
|
296
261
|
}, {
|
|
297
262
|
F: __dxlog_file3,
|
|
298
|
-
L:
|
|
263
|
+
L: 124,
|
|
299
264
|
S: this,
|
|
300
265
|
C: (f, a) => f(...a)
|
|
301
266
|
});
|
|
302
267
|
await this._persistentLifecycle.close();
|
|
303
268
|
}
|
|
304
269
|
async _openWebSocket() {
|
|
305
|
-
|
|
306
|
-
|
|
270
|
+
if (this._ctx.disposed) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const path = `/ws/${this._identity.identityKey}/${this._identity.peerKey}`;
|
|
274
|
+
const protocolHeader = this._config.disableAuth ? void 0 : await this._createAuthHeader(path);
|
|
275
|
+
const url = new URL(path, this._baseWsUrl);
|
|
276
|
+
log2("Opening websocket", {
|
|
277
|
+
url: url.toString(),
|
|
278
|
+
protocolHeader
|
|
279
|
+
}, {
|
|
280
|
+
F: __dxlog_file3,
|
|
281
|
+
L: 136,
|
|
282
|
+
S: this,
|
|
283
|
+
C: (f, a) => f(...a)
|
|
284
|
+
});
|
|
285
|
+
this._ws = new WebSocket(url, protocolHeader ? [
|
|
286
|
+
protocolHeader
|
|
287
|
+
] : []);
|
|
307
288
|
this._ws.onopen = () => {
|
|
308
289
|
log2("opened", this.info, {
|
|
309
290
|
F: __dxlog_file3,
|
|
310
|
-
L:
|
|
291
|
+
L: 140,
|
|
311
292
|
S: this,
|
|
312
293
|
C: (f, a) => f(...a)
|
|
313
294
|
});
|
|
314
295
|
this._ready.wake();
|
|
296
|
+
this.connected.emit();
|
|
315
297
|
};
|
|
316
298
|
this._ws.onclose = () => {
|
|
317
299
|
log2("closed", this.info, {
|
|
318
300
|
F: __dxlog_file3,
|
|
319
|
-
L:
|
|
301
|
+
L: 145,
|
|
320
302
|
S: this,
|
|
321
303
|
C: (f, a) => f(...a)
|
|
322
304
|
});
|
|
@@ -328,7 +310,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
328
310
|
info: event.message
|
|
329
311
|
}, {
|
|
330
312
|
F: __dxlog_file3,
|
|
331
|
-
L:
|
|
313
|
+
L: 149,
|
|
332
314
|
S: this,
|
|
333
315
|
C: (f, a) => f(...a)
|
|
334
316
|
});
|
|
@@ -340,13 +322,13 @@ var EdgeClient = class extends Resource2 {
|
|
|
340
322
|
return;
|
|
341
323
|
}
|
|
342
324
|
const data = await toUint8Array(event.data);
|
|
343
|
-
const message =
|
|
325
|
+
const message = buf.fromBinary(MessageSchema, data);
|
|
344
326
|
log2("received", {
|
|
345
|
-
peerKey: this.
|
|
327
|
+
peerKey: this._identity.peerKey,
|
|
346
328
|
payload: protocol.getPayloadType(message)
|
|
347
329
|
}, {
|
|
348
330
|
F: __dxlog_file3,
|
|
349
|
-
L:
|
|
331
|
+
L: 162,
|
|
350
332
|
S: this,
|
|
351
333
|
C: (f, a) => f(...a)
|
|
352
334
|
});
|
|
@@ -360,7 +342,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
360
342
|
payload: protocol.getPayloadType(message)
|
|
361
343
|
}, {
|
|
362
344
|
F: __dxlog_file3,
|
|
363
|
-
L:
|
|
345
|
+
L: 168,
|
|
364
346
|
S: this,
|
|
365
347
|
C: (f, a) => f(...a)
|
|
366
348
|
});
|
|
@@ -371,9 +353,18 @@ var EdgeClient = class extends Resource2 {
|
|
|
371
353
|
await this._ready.wait({
|
|
372
354
|
timeout: this._config.timeout ?? DEFAULT_TIMEOUT
|
|
373
355
|
});
|
|
356
|
+
log2("Websocket is ready", {
|
|
357
|
+
identity: this._identity.identityKey,
|
|
358
|
+
peer: this._identity.peerKey
|
|
359
|
+
}, {
|
|
360
|
+
F: __dxlog_file3,
|
|
361
|
+
L: 176,
|
|
362
|
+
S: this,
|
|
363
|
+
C: (f, a) => f(...a)
|
|
364
|
+
});
|
|
374
365
|
this._keepaliveCtx = new Context(void 0, {
|
|
375
366
|
F: __dxlog_file3,
|
|
376
|
-
L:
|
|
367
|
+
L: 179
|
|
377
368
|
});
|
|
378
369
|
scheduleTaskInterval(this._keepaliveCtx, async () => {
|
|
379
370
|
this._ws?.send("__ping__");
|
|
@@ -386,7 +377,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
386
377
|
return;
|
|
387
378
|
}
|
|
388
379
|
try {
|
|
389
|
-
this._ready.throw(new
|
|
380
|
+
this._ready.throw(this.isOpen ? new EdgeIdentityChangedError() : new EdgeConnectionClosedError());
|
|
390
381
|
this._ready.reset();
|
|
391
382
|
void this._keepaliveCtx?.dispose();
|
|
392
383
|
this._keepaliveCtx = void 0;
|
|
@@ -408,7 +399,7 @@ var EdgeClient = class extends Resource2 {
|
|
|
408
399
|
err
|
|
409
400
|
}, {
|
|
410
401
|
F: __dxlog_file3,
|
|
411
|
-
L:
|
|
402
|
+
L: 215,
|
|
412
403
|
S: this,
|
|
413
404
|
C: (f, a) => f(...a)
|
|
414
405
|
});
|
|
@@ -420,38 +411,32 @@ var EdgeClient = class extends Resource2 {
|
|
|
420
411
|
*/
|
|
421
412
|
async send(message) {
|
|
422
413
|
if (this._ready.state !== TriggerState.RESOLVED) {
|
|
414
|
+
log2("waiting for websocket to become ready", void 0, {
|
|
415
|
+
F: __dxlog_file3,
|
|
416
|
+
L: 225,
|
|
417
|
+
S: this,
|
|
418
|
+
C: (f, a) => f(...a)
|
|
419
|
+
});
|
|
423
420
|
await this._ready.wait({
|
|
424
421
|
timeout: this._config.timeout ?? DEFAULT_TIMEOUT
|
|
425
422
|
});
|
|
426
423
|
}
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
""
|
|
434
|
-
]
|
|
435
|
-
});
|
|
436
|
-
invariant2(!message.source || message.source.peerKey === this._peerKey, void 0, {
|
|
437
|
-
F: __dxlog_file3,
|
|
438
|
-
L: 203,
|
|
439
|
-
S: this,
|
|
440
|
-
A: [
|
|
441
|
-
"!message.source || message.source.peerKey === this._peerKey",
|
|
442
|
-
""
|
|
443
|
-
]
|
|
444
|
-
});
|
|
424
|
+
if (!this._ws) {
|
|
425
|
+
throw new EdgeConnectionClosedError();
|
|
426
|
+
}
|
|
427
|
+
if (message.source && (message.source.peerKey !== this._identity.peerKey || message.source.identityKey !== this.identityKey)) {
|
|
428
|
+
throw new EdgeIdentityChangedError();
|
|
429
|
+
}
|
|
445
430
|
log2("sending...", {
|
|
446
|
-
peerKey: this.
|
|
431
|
+
peerKey: this._identity.peerKey,
|
|
447
432
|
payload: protocol.getPayloadType(message)
|
|
448
433
|
}, {
|
|
449
434
|
F: __dxlog_file3,
|
|
450
|
-
L:
|
|
435
|
+
L: 238,
|
|
451
436
|
S: this,
|
|
452
437
|
C: (f, a) => f(...a)
|
|
453
438
|
});
|
|
454
|
-
this._ws.send(
|
|
439
|
+
this._ws.send(buf.toBinary(MessageSchema, message));
|
|
455
440
|
}
|
|
456
441
|
_onHeartbeat() {
|
|
457
442
|
if (this._lifecycleState !== LifecycleState2.OPEN) {
|
|
@@ -460,17 +445,341 @@ var EdgeClient = class extends Resource2 {
|
|
|
460
445
|
void this._heartBeatContext?.dispose();
|
|
461
446
|
this._heartBeatContext = new Context(void 0, {
|
|
462
447
|
F: __dxlog_file3,
|
|
463
|
-
L:
|
|
448
|
+
L: 247
|
|
464
449
|
});
|
|
465
450
|
scheduleTask(this._heartBeatContext, () => {
|
|
466
451
|
this._persistentLifecycle.scheduleRestart();
|
|
467
452
|
}, 2 * SIGNAL_KEEPALIVE_INTERVAL);
|
|
468
453
|
}
|
|
454
|
+
async _createAuthHeader(path) {
|
|
455
|
+
const httpUrl = new URL(path, this._baseHttpUrl);
|
|
456
|
+
httpUrl.protocol = getEdgeUrlWithProtocol(this._baseWsUrl.toString(), "http");
|
|
457
|
+
const response = await fetch(httpUrl, {
|
|
458
|
+
method: "GET"
|
|
459
|
+
});
|
|
460
|
+
if (response.status === 401) {
|
|
461
|
+
return encodePresentationWsAuthHeader(await handleAuthChallenge(response, this._identity));
|
|
462
|
+
} else {
|
|
463
|
+
log2.warn("no auth challenge from edge", {
|
|
464
|
+
status: response.status,
|
|
465
|
+
statusText: response.statusText
|
|
466
|
+
}, {
|
|
467
|
+
F: __dxlog_file3,
|
|
468
|
+
L: 264,
|
|
469
|
+
S: this,
|
|
470
|
+
C: (f, a) => f(...a)
|
|
471
|
+
});
|
|
472
|
+
return void 0;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
var encodePresentationWsAuthHeader = (encodedPresentation) => {
|
|
477
|
+
const encodedToken = Buffer.from(encodedPresentation).toString("base64").replace(/=*$/, "").replaceAll("/", "|");
|
|
478
|
+
return `base64url.bearer.authorization.dxos.org.${encodedToken}`;
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
// packages/core/mesh/edge-client/src/auth.ts
|
|
482
|
+
import { createCredential, signPresentation } from "@dxos/credentials";
|
|
483
|
+
import { Keyring } from "@dxos/keyring";
|
|
484
|
+
import { PublicKey } from "@dxos/keys";
|
|
485
|
+
var createDeviceEdgeIdentity = async (signer, key) => {
|
|
486
|
+
return {
|
|
487
|
+
identityKey: key.toHex(),
|
|
488
|
+
peerKey: key.toHex(),
|
|
489
|
+
presentCredentials: async ({ challenge }) => {
|
|
490
|
+
return signPresentation({
|
|
491
|
+
presentation: {
|
|
492
|
+
credentials: [
|
|
493
|
+
// Verifier requires at least one credential in the presentation to establish the subject.
|
|
494
|
+
await createCredential({
|
|
495
|
+
assertion: {
|
|
496
|
+
"@type": "dxos.halo.credentials.Auth"
|
|
497
|
+
},
|
|
498
|
+
issuer: key,
|
|
499
|
+
subject: key,
|
|
500
|
+
signer
|
|
501
|
+
})
|
|
502
|
+
]
|
|
503
|
+
},
|
|
504
|
+
signer,
|
|
505
|
+
signerKey: key,
|
|
506
|
+
nonce: challenge
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
};
|
|
511
|
+
var createChainEdgeIdentity = async (signer, identityKey, peerKey, chain, credentials) => {
|
|
512
|
+
const credentialsToSign = credentials.length > 0 ? credentials : [
|
|
513
|
+
await createCredential({
|
|
514
|
+
assertion: {
|
|
515
|
+
"@type": "dxos.halo.credentials.Auth"
|
|
516
|
+
},
|
|
517
|
+
issuer: identityKey,
|
|
518
|
+
subject: identityKey,
|
|
519
|
+
signer,
|
|
520
|
+
chain,
|
|
521
|
+
signingKey: peerKey
|
|
522
|
+
})
|
|
523
|
+
];
|
|
524
|
+
return {
|
|
525
|
+
identityKey: identityKey.toHex(),
|
|
526
|
+
peerKey: peerKey.toHex(),
|
|
527
|
+
presentCredentials: async ({ challenge }) => {
|
|
528
|
+
return signPresentation({
|
|
529
|
+
presentation: {
|
|
530
|
+
credentials: credentialsToSign
|
|
531
|
+
},
|
|
532
|
+
signer,
|
|
533
|
+
nonce: challenge,
|
|
534
|
+
signerKey: peerKey,
|
|
535
|
+
chain
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
};
|
|
540
|
+
var createEphemeralEdgeIdentity = async () => {
|
|
541
|
+
const keyring = new Keyring();
|
|
542
|
+
const key = await keyring.createKey();
|
|
543
|
+
return createDeviceEdgeIdentity(keyring, key);
|
|
544
|
+
};
|
|
545
|
+
var createTestHaloEdgeIdentity = async (signer, identityKey, deviceKey) => {
|
|
546
|
+
const deviceAdmission = await createCredential({
|
|
547
|
+
assertion: {
|
|
548
|
+
"@type": "dxos.halo.credentials.AuthorizedDevice",
|
|
549
|
+
deviceKey,
|
|
550
|
+
identityKey
|
|
551
|
+
},
|
|
552
|
+
issuer: identityKey,
|
|
553
|
+
subject: deviceKey,
|
|
554
|
+
signer
|
|
555
|
+
});
|
|
556
|
+
return createChainEdgeIdentity(signer, identityKey, deviceKey, {
|
|
557
|
+
credential: deviceAdmission
|
|
558
|
+
}, [
|
|
559
|
+
await createCredential({
|
|
560
|
+
assertion: {
|
|
561
|
+
"@type": "dxos.halo.credentials.Auth"
|
|
562
|
+
},
|
|
563
|
+
issuer: identityKey,
|
|
564
|
+
subject: identityKey,
|
|
565
|
+
signer
|
|
566
|
+
})
|
|
567
|
+
]);
|
|
568
|
+
};
|
|
569
|
+
var createStubEdgeIdentity = () => {
|
|
570
|
+
const identityKey = PublicKey.random();
|
|
571
|
+
const deviceKey = PublicKey.random();
|
|
572
|
+
return {
|
|
573
|
+
identityKey: identityKey.toHex(),
|
|
574
|
+
peerKey: deviceKey.toHex(),
|
|
575
|
+
presentCredentials: async () => {
|
|
576
|
+
throw new Error("Stub identity does not support authentication.");
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
// packages/core/mesh/edge-client/src/edge-http-client.ts
|
|
582
|
+
import { sleep as sleep2 } from "@dxos/async";
|
|
583
|
+
import { Context as Context2 } from "@dxos/context";
|
|
584
|
+
import { log as log3 } from "@dxos/log";
|
|
585
|
+
import { EdgeCallFailedError, EdgeAuthChallengeError } from "@dxos/protocols";
|
|
586
|
+
var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/mesh/edge-client/src/edge-http-client.ts";
|
|
587
|
+
var DEFAULT_RETRY_TIMEOUT = 1500;
|
|
588
|
+
var DEFAULT_RETRY_JITTER = 500;
|
|
589
|
+
var DEFAULT_MAX_RETRIES_COUNT = 3;
|
|
590
|
+
var EdgeHttpClient = class {
|
|
591
|
+
constructor(baseUrl) {
|
|
592
|
+
this._baseUrl = getEdgeUrlWithProtocol(baseUrl, "http");
|
|
593
|
+
log3("created", {
|
|
594
|
+
url: this._baseUrl
|
|
595
|
+
}, {
|
|
596
|
+
F: __dxlog_file4,
|
|
597
|
+
L: 42,
|
|
598
|
+
S: this,
|
|
599
|
+
C: (f, a) => f(...a)
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
setIdentity(identity) {
|
|
603
|
+
this._edgeIdentity = identity;
|
|
604
|
+
}
|
|
605
|
+
createAgent(body, args) {
|
|
606
|
+
return this._call("/agents/create", {
|
|
607
|
+
...args,
|
|
608
|
+
method: "POST",
|
|
609
|
+
body
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
getAgentStatus(request, args) {
|
|
613
|
+
return this._call(`/users/${request.ownerIdentityKey.toHex()}/agent/status`, {
|
|
614
|
+
...args,
|
|
615
|
+
method: "GET"
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
getCredentialsForNotarization(spaceId, args) {
|
|
619
|
+
return this._call(`/spaces/${spaceId}/notarization`, {
|
|
620
|
+
...args,
|
|
621
|
+
method: "GET"
|
|
622
|
+
});
|
|
623
|
+
}
|
|
624
|
+
async notarizeCredentials(spaceId, body, args) {
|
|
625
|
+
await this._call(`/spaces/${spaceId}/notarization`, {
|
|
626
|
+
...args,
|
|
627
|
+
body,
|
|
628
|
+
method: "POST"
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
async joinSpaceByInvitation(spaceId, body, args) {
|
|
632
|
+
return this._call(`/spaces/${spaceId}/join`, {
|
|
633
|
+
...args,
|
|
634
|
+
body,
|
|
635
|
+
method: "POST"
|
|
636
|
+
});
|
|
637
|
+
}
|
|
638
|
+
async recoverIdentity(body, args) {
|
|
639
|
+
return this._call("/identity/recover", {
|
|
640
|
+
...args,
|
|
641
|
+
body,
|
|
642
|
+
method: "POST"
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
async _call(path, args) {
|
|
646
|
+
const requestContext = args.context ?? new Context2(void 0, {
|
|
647
|
+
F: __dxlog_file4,
|
|
648
|
+
L: 88
|
|
649
|
+
});
|
|
650
|
+
const shouldRetry = createRetryHandler(args);
|
|
651
|
+
const url = `${this._baseUrl}${path.startsWith("/") ? path.slice(1) : path}`;
|
|
652
|
+
log3.info("call", {
|
|
653
|
+
method: args.method,
|
|
654
|
+
path,
|
|
655
|
+
request: args.body
|
|
656
|
+
}, {
|
|
657
|
+
F: __dxlog_file4,
|
|
658
|
+
L: 92,
|
|
659
|
+
S: this,
|
|
660
|
+
C: (f, a) => f(...a)
|
|
661
|
+
});
|
|
662
|
+
let handledAuth = false;
|
|
663
|
+
let authHeader = this._authHeader;
|
|
664
|
+
while (true) {
|
|
665
|
+
let processingError;
|
|
666
|
+
let retryAfterHeaderValue = Number.NaN;
|
|
667
|
+
try {
|
|
668
|
+
const request = createRequest(args, authHeader);
|
|
669
|
+
const response = await fetch(url, request);
|
|
670
|
+
retryAfterHeaderValue = Number(response.headers.get("Retry-After"));
|
|
671
|
+
if (response.ok) {
|
|
672
|
+
const body = await response.json();
|
|
673
|
+
if (body.success) {
|
|
674
|
+
return body.data;
|
|
675
|
+
}
|
|
676
|
+
log3.info("unsuccessful edge response", {
|
|
677
|
+
path,
|
|
678
|
+
body
|
|
679
|
+
}, {
|
|
680
|
+
F: __dxlog_file4,
|
|
681
|
+
L: 111,
|
|
682
|
+
S: this,
|
|
683
|
+
C: (f, a) => f(...a)
|
|
684
|
+
});
|
|
685
|
+
if (body.errorData?.type === "auth_challenge" && typeof body.errorData?.challenge === "string") {
|
|
686
|
+
processingError = new EdgeAuthChallengeError(body.errorData.challenge, body.errorData);
|
|
687
|
+
} else {
|
|
688
|
+
processingError = EdgeCallFailedError.fromUnsuccessfulResponse(response, body);
|
|
689
|
+
}
|
|
690
|
+
} else if (response.status === 401 && !handledAuth) {
|
|
691
|
+
authHeader = await this._handleUnauthorized(response);
|
|
692
|
+
handledAuth = true;
|
|
693
|
+
continue;
|
|
694
|
+
} else {
|
|
695
|
+
processingError = EdgeCallFailedError.fromHttpFailure(response);
|
|
696
|
+
}
|
|
697
|
+
} catch (error) {
|
|
698
|
+
processingError = EdgeCallFailedError.fromProcessingFailureCause(error);
|
|
699
|
+
}
|
|
700
|
+
if (processingError.isRetryable && await shouldRetry(requestContext, retryAfterHeaderValue)) {
|
|
701
|
+
log3.info("retrying edge request", {
|
|
702
|
+
path,
|
|
703
|
+
processingError
|
|
704
|
+
}, {
|
|
705
|
+
F: __dxlog_file4,
|
|
706
|
+
L: 130,
|
|
707
|
+
S: this,
|
|
708
|
+
C: (f, a) => f(...a)
|
|
709
|
+
});
|
|
710
|
+
} else {
|
|
711
|
+
throw processingError;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
async _handleUnauthorized(response) {
|
|
716
|
+
if (!this._edgeIdentity) {
|
|
717
|
+
log3.warn("edge unauthorized response received before identity was set", void 0, {
|
|
718
|
+
F: __dxlog_file4,
|
|
719
|
+
L: 139,
|
|
720
|
+
S: this,
|
|
721
|
+
C: (f, a) => f(...a)
|
|
722
|
+
});
|
|
723
|
+
throw EdgeCallFailedError.fromHttpFailure(response);
|
|
724
|
+
}
|
|
725
|
+
const challenge = await handleAuthChallenge(response, this._edgeIdentity);
|
|
726
|
+
this._authHeader = encodeAuthHeader(challenge);
|
|
727
|
+
log3("auth header updated", void 0, {
|
|
728
|
+
F: __dxlog_file4,
|
|
729
|
+
L: 144,
|
|
730
|
+
S: this,
|
|
731
|
+
C: (f, a) => f(...a)
|
|
732
|
+
});
|
|
733
|
+
return this._authHeader;
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
var createRetryHandler = (args) => {
|
|
737
|
+
if (!args.retry || args.retry.count < 1) {
|
|
738
|
+
return async () => false;
|
|
739
|
+
}
|
|
740
|
+
let retries = 0;
|
|
741
|
+
const maxRetries = args.retry.count ?? DEFAULT_MAX_RETRIES_COUNT;
|
|
742
|
+
const baseTimeout = args.retry.timeout ?? DEFAULT_RETRY_TIMEOUT;
|
|
743
|
+
const jitter = args.retry.jitter ?? DEFAULT_RETRY_JITTER;
|
|
744
|
+
return async (ctx, retryAfter) => {
|
|
745
|
+
if (++retries > maxRetries || ctx.disposed) {
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
if (retryAfter) {
|
|
749
|
+
await sleep2(retryAfter);
|
|
750
|
+
} else {
|
|
751
|
+
const timeout = baseTimeout + Math.random() * jitter;
|
|
752
|
+
await sleep2(timeout);
|
|
753
|
+
}
|
|
754
|
+
return true;
|
|
755
|
+
};
|
|
756
|
+
};
|
|
757
|
+
var createRequest = (args, authHeader) => {
|
|
758
|
+
return {
|
|
759
|
+
method: args.method,
|
|
760
|
+
body: args.body && JSON.stringify(args.body),
|
|
761
|
+
headers: authHeader ? {
|
|
762
|
+
Authorization: authHeader
|
|
763
|
+
} : void 0
|
|
764
|
+
};
|
|
765
|
+
};
|
|
766
|
+
var encodeAuthHeader = (challenge) => {
|
|
767
|
+
const encodedChallenge = Buffer.from(challenge).toString("base64");
|
|
768
|
+
return `VerifiablePresentation pb;base64,${encodedChallenge}`;
|
|
469
769
|
};
|
|
470
770
|
export {
|
|
471
771
|
EdgeClient,
|
|
772
|
+
EdgeConnectionClosedError,
|
|
773
|
+
EdgeHttpClient,
|
|
774
|
+
EdgeIdentityChangedError,
|
|
472
775
|
Protocol,
|
|
776
|
+
createChainEdgeIdentity,
|
|
777
|
+
createDeviceEdgeIdentity,
|
|
778
|
+
createEphemeralEdgeIdentity,
|
|
779
|
+
createStubEdgeIdentity,
|
|
780
|
+
createTestHaloEdgeIdentity,
|
|
473
781
|
getTypename,
|
|
782
|
+
handleAuthChallenge,
|
|
474
783
|
protocol,
|
|
475
784
|
toUint8Array
|
|
476
785
|
};
|