@dxos/edge-client 0.8.4-main.f9ba587 → 0.8.4-main.fffef41
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-LMP5TVOP.mjs → chunk-VESGVCLQ.mjs} +8 -4
- package/dist/lib/browser/{chunk-LMP5TVOP.mjs.map → chunk-VESGVCLQ.mjs.map} +3 -3
- package/dist/lib/browser/edge-ws-muxer.mjs +1 -1
- package/dist/lib/browser/index.mjs +557 -284
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +1 -1
- package/dist/lib/browser/testing/index.mjs.map +2 -2
- package/dist/lib/node-esm/{chunk-X7J46ISZ.mjs → chunk-JTBFRYNM.mjs} +8 -4
- package/dist/lib/node-esm/{chunk-X7J46ISZ.mjs.map → chunk-JTBFRYNM.mjs.map} +3 -3
- package/dist/lib/node-esm/edge-ws-muxer.mjs +1 -1
- package/dist/lib/node-esm/index.mjs +557 -284
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +1 -1
- package/dist/lib/node-esm/testing/index.mjs.map +2 -2
- package/dist/types/src/edge-client.d.ts +15 -15
- package/dist/types/src/edge-client.d.ts.map +1 -1
- package/dist/types/src/edge-http-client.d.ts +30 -3
- package/dist/types/src/edge-http-client.d.ts.map +1 -1
- package/dist/types/src/edge-ws-connection.d.ts +20 -0
- package/dist/types/src/edge-ws-connection.d.ts.map +1 -1
- package/dist/types/src/edge-ws-muxer.d.ts.map +1 -1
- package/dist/types/src/http-client.d.ts +10 -7
- package/dist/types/src/http-client.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +4 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/testing/test-utils.d.ts +1 -1
- package/dist/types/src/testing/test-utils.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +18 -15
- package/src/edge-client.test.ts +4 -4
- package/src/edge-client.ts +73 -42
- package/src/edge-http-client.test.ts +1 -1
- package/src/edge-http-client.ts +194 -35
- package/src/edge-ws-connection.ts +129 -8
- package/src/edge-ws-muxer.ts +1 -1
- package/src/http-client.test.ts +8 -6
- package/src/http-client.ts +18 -8
- package/src/index.ts +4 -3
- package/src/testing/test-utils.ts +4 -4
- package/src/websocket.test.ts +1 -1
|
@@ -6,24 +6,135 @@ import {
|
|
|
6
6
|
getTypename,
|
|
7
7
|
protocol,
|
|
8
8
|
toUint8Array
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-VESGVCLQ.mjs";
|
|
10
10
|
|
|
11
11
|
// src/index.ts
|
|
12
12
|
export * from "@dxos/protocols/buf/dxos/edge/messenger_pb";
|
|
13
13
|
|
|
14
|
+
// src/auth.ts
|
|
15
|
+
import { createCredential, signPresentation } from "@dxos/credentials";
|
|
16
|
+
import { invariant } from "@dxos/invariant";
|
|
17
|
+
import { Keyring } from "@dxos/keyring";
|
|
18
|
+
import { PublicKey } from "@dxos/keys";
|
|
19
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/auth.ts";
|
|
20
|
+
var createDeviceEdgeIdentity = async (signer, key) => {
|
|
21
|
+
return {
|
|
22
|
+
identityKey: key.toHex(),
|
|
23
|
+
peerKey: key.toHex(),
|
|
24
|
+
presentCredentials: async ({ challenge }) => {
|
|
25
|
+
return signPresentation({
|
|
26
|
+
presentation: {
|
|
27
|
+
credentials: [
|
|
28
|
+
// Verifier requires at least one credential in the presentation to establish the subject.
|
|
29
|
+
await createCredential({
|
|
30
|
+
assertion: {
|
|
31
|
+
"@type": "dxos.halo.credentials.Auth"
|
|
32
|
+
},
|
|
33
|
+
issuer: key,
|
|
34
|
+
subject: key,
|
|
35
|
+
signer
|
|
36
|
+
})
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
signer,
|
|
40
|
+
signerKey: key,
|
|
41
|
+
nonce: challenge
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
var createChainEdgeIdentity = async (signer, identityKey, peerKey, chain, credentials) => {
|
|
47
|
+
const credentialsToSign = credentials.length > 0 ? credentials : [
|
|
48
|
+
await createCredential({
|
|
49
|
+
assertion: {
|
|
50
|
+
"@type": "dxos.halo.credentials.Auth"
|
|
51
|
+
},
|
|
52
|
+
issuer: identityKey,
|
|
53
|
+
subject: identityKey,
|
|
54
|
+
signer,
|
|
55
|
+
chain,
|
|
56
|
+
signingKey: peerKey
|
|
57
|
+
})
|
|
58
|
+
];
|
|
59
|
+
return {
|
|
60
|
+
identityKey: identityKey.toHex(),
|
|
61
|
+
peerKey: peerKey.toHex(),
|
|
62
|
+
presentCredentials: async ({ challenge }) => {
|
|
63
|
+
invariant(chain, void 0, {
|
|
64
|
+
F: __dxlog_file,
|
|
65
|
+
L: 75,
|
|
66
|
+
S: void 0,
|
|
67
|
+
A: [
|
|
68
|
+
"chain",
|
|
69
|
+
""
|
|
70
|
+
]
|
|
71
|
+
});
|
|
72
|
+
return signPresentation({
|
|
73
|
+
presentation: {
|
|
74
|
+
credentials: credentialsToSign
|
|
75
|
+
},
|
|
76
|
+
signer,
|
|
77
|
+
nonce: challenge,
|
|
78
|
+
signerKey: peerKey,
|
|
79
|
+
chain
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
var createEphemeralEdgeIdentity = async () => {
|
|
85
|
+
const keyring = new Keyring();
|
|
86
|
+
const key = await keyring.createKey();
|
|
87
|
+
return createDeviceEdgeIdentity(keyring, key);
|
|
88
|
+
};
|
|
89
|
+
var createTestHaloEdgeIdentity = async (signer, identityKey, deviceKey) => {
|
|
90
|
+
const deviceAdmission = await createCredential({
|
|
91
|
+
assertion: {
|
|
92
|
+
"@type": "dxos.halo.credentials.AuthorizedDevice",
|
|
93
|
+
deviceKey,
|
|
94
|
+
identityKey
|
|
95
|
+
},
|
|
96
|
+
issuer: identityKey,
|
|
97
|
+
subject: deviceKey,
|
|
98
|
+
signer
|
|
99
|
+
});
|
|
100
|
+
return createChainEdgeIdentity(signer, identityKey, deviceKey, {
|
|
101
|
+
credential: deviceAdmission
|
|
102
|
+
}, [
|
|
103
|
+
await createCredential({
|
|
104
|
+
assertion: {
|
|
105
|
+
"@type": "dxos.halo.credentials.Auth"
|
|
106
|
+
},
|
|
107
|
+
issuer: identityKey,
|
|
108
|
+
subject: identityKey,
|
|
109
|
+
signer
|
|
110
|
+
})
|
|
111
|
+
]);
|
|
112
|
+
};
|
|
113
|
+
var createStubEdgeIdentity = () => {
|
|
114
|
+
const identityKey = PublicKey.random();
|
|
115
|
+
const deviceKey = PublicKey.random();
|
|
116
|
+
return {
|
|
117
|
+
identityKey: identityKey.toHex(),
|
|
118
|
+
peerKey: deviceKey.toHex(),
|
|
119
|
+
presentCredentials: async () => {
|
|
120
|
+
throw new Error("Stub identity does not support authentication.");
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
|
|
14
125
|
// src/edge-client.ts
|
|
15
|
-
import {
|
|
126
|
+
import { Event, PersistentLifecycle, Trigger, TriggerState, scheduleMicroTask, scheduleTaskInterval as scheduleTaskInterval2 } from "@dxos/async";
|
|
16
127
|
import { Resource as Resource2 } from "@dxos/context";
|
|
17
128
|
import { log as log2, logInfo as logInfo2 } from "@dxos/log";
|
|
18
129
|
import { EdgeStatus } from "@dxos/protocols/proto/dxos/client/services";
|
|
19
130
|
|
|
20
131
|
// src/edge-identity.ts
|
|
21
|
-
import { invariant } from "@dxos/invariant";
|
|
132
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
22
133
|
import { schema } from "@dxos/protocols/proto";
|
|
23
|
-
var
|
|
134
|
+
var __dxlog_file2 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/edge-identity.ts";
|
|
24
135
|
var handleAuthChallenge = async (failedResponse, identity) => {
|
|
25
|
-
|
|
26
|
-
F:
|
|
136
|
+
invariant2(failedResponse.status === 401, void 0, {
|
|
137
|
+
F: __dxlog_file2,
|
|
27
138
|
L: 21,
|
|
28
139
|
S: void 0,
|
|
29
140
|
A: [
|
|
@@ -32,8 +143,8 @@ var handleAuthChallenge = async (failedResponse, identity) => {
|
|
|
32
143
|
]
|
|
33
144
|
});
|
|
34
145
|
const headerValue = failedResponse.headers.get("Www-Authenticate");
|
|
35
|
-
|
|
36
|
-
F:
|
|
146
|
+
invariant2(headerValue?.startsWith("VerifiablePresentation challenge="), void 0, {
|
|
147
|
+
F: __dxlog_file2,
|
|
37
148
|
L: 24,
|
|
38
149
|
S: void 0,
|
|
39
150
|
A: [
|
|
@@ -42,8 +153,8 @@ var handleAuthChallenge = async (failedResponse, identity) => {
|
|
|
42
153
|
]
|
|
43
154
|
});
|
|
44
155
|
const challenge = headerValue?.slice("VerifiablePresentation challenge=".length);
|
|
45
|
-
|
|
46
|
-
F:
|
|
156
|
+
invariant2(challenge, void 0, {
|
|
157
|
+
F: __dxlog_file2,
|
|
47
158
|
L: 27,
|
|
48
159
|
S: void 0,
|
|
49
160
|
A: [
|
|
@@ -61,7 +172,7 @@ var handleAuthChallenge = async (failedResponse, identity) => {
|
|
|
61
172
|
import WebSocket from "isomorphic-ws";
|
|
62
173
|
import { scheduleTask, scheduleTaskInterval } from "@dxos/async";
|
|
63
174
|
import { Context, Resource } from "@dxos/context";
|
|
64
|
-
import { invariant as
|
|
175
|
+
import { invariant as invariant3 } from "@dxos/invariant";
|
|
65
176
|
import { log, logInfo } from "@dxos/log";
|
|
66
177
|
import { EdgeWebsocketProtocol } from "@dxos/protocols";
|
|
67
178
|
import { buf } from "@dxos/protocols/buf";
|
|
@@ -72,10 +183,29 @@ function _ts_decorate(decorators, target, key, desc) {
|
|
|
72
183
|
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
184
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
74
185
|
}
|
|
75
|
-
var
|
|
186
|
+
var __dxlog_file3 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/edge-ws-connection.ts";
|
|
76
187
|
var SIGNAL_KEEPALIVE_INTERVAL = 4e3;
|
|
77
188
|
var SIGNAL_KEEPALIVE_TIMEOUT = 12e3;
|
|
78
189
|
var EdgeWsConnection = class extends Resource {
|
|
190
|
+
_identity;
|
|
191
|
+
_connectionInfo;
|
|
192
|
+
_callbacks;
|
|
193
|
+
_inactivityTimeoutCtx;
|
|
194
|
+
_ws;
|
|
195
|
+
_wsMuxer;
|
|
196
|
+
_lastReceivedMessageTimestamp = Date.now();
|
|
197
|
+
_openTimestamp;
|
|
198
|
+
// Latency tracking.
|
|
199
|
+
_pingTimestamp;
|
|
200
|
+
_rtt = 0;
|
|
201
|
+
// Rate tracking with sliding window.
|
|
202
|
+
_uploadRate = 0;
|
|
203
|
+
_downloadRate = 0;
|
|
204
|
+
_rateWindow = 1e4;
|
|
205
|
+
_rateUpdateInterval = 1e3;
|
|
206
|
+
_bytesSamples = [];
|
|
207
|
+
_messagesSent = 0;
|
|
208
|
+
_messagesReceived = 0;
|
|
79
209
|
constructor(_identity, _connectionInfo, _callbacks) {
|
|
80
210
|
super(), this._identity = _identity, this._connectionInfo = _connectionInfo, this._callbacks = _callbacks;
|
|
81
211
|
}
|
|
@@ -86,19 +216,37 @@ var EdgeWsConnection = class extends Resource {
|
|
|
86
216
|
device: this._identity.peerKey
|
|
87
217
|
};
|
|
88
218
|
}
|
|
219
|
+
get rtt() {
|
|
220
|
+
return this._rtt;
|
|
221
|
+
}
|
|
222
|
+
get uptime() {
|
|
223
|
+
return this._openTimestamp ? (Date.now() - this._openTimestamp) / 1e3 : 0;
|
|
224
|
+
}
|
|
225
|
+
get uploadRate() {
|
|
226
|
+
return this._uploadRate;
|
|
227
|
+
}
|
|
228
|
+
get downloadRate() {
|
|
229
|
+
return this._downloadRate;
|
|
230
|
+
}
|
|
231
|
+
get messagesSent() {
|
|
232
|
+
return this._messagesSent;
|
|
233
|
+
}
|
|
234
|
+
get messagesReceived() {
|
|
235
|
+
return this._messagesReceived;
|
|
236
|
+
}
|
|
89
237
|
send(message) {
|
|
90
|
-
|
|
91
|
-
F:
|
|
92
|
-
L:
|
|
238
|
+
invariant3(this._ws, void 0, {
|
|
239
|
+
F: __dxlog_file3,
|
|
240
|
+
L: 93,
|
|
93
241
|
S: this,
|
|
94
242
|
A: [
|
|
95
243
|
"this._ws",
|
|
96
244
|
""
|
|
97
245
|
]
|
|
98
246
|
});
|
|
99
|
-
|
|
100
|
-
F:
|
|
101
|
-
L:
|
|
247
|
+
invariant3(this._wsMuxer, void 0, {
|
|
248
|
+
F: __dxlog_file3,
|
|
249
|
+
L: 94,
|
|
102
250
|
S: this,
|
|
103
251
|
A: [
|
|
104
252
|
"this._wsMuxer",
|
|
@@ -109,11 +257,12 @@ var EdgeWsConnection = class extends Resource {
|
|
|
109
257
|
peerKey: this._identity.peerKey,
|
|
110
258
|
payload: protocol.getPayloadType(message)
|
|
111
259
|
}, {
|
|
112
|
-
F:
|
|
113
|
-
L:
|
|
260
|
+
F: __dxlog_file3,
|
|
261
|
+
L: 95,
|
|
114
262
|
S: this,
|
|
115
263
|
C: (f, a) => f(...a)
|
|
116
264
|
});
|
|
265
|
+
this._messagesSent++;
|
|
117
266
|
if (this._ws?.protocol.includes(EdgeWebsocketProtocol.V0)) {
|
|
118
267
|
const binary = buf.toBinary(MessageSchema, message);
|
|
119
268
|
if (binary.length > CLOUDFLARE_MESSAGE_MAX_BYTES) {
|
|
@@ -122,18 +271,21 @@ var EdgeWsConnection = class extends Resource {
|
|
|
122
271
|
serviceId: message.serviceId,
|
|
123
272
|
payload: protocol.getPayloadType(message)
|
|
124
273
|
}, {
|
|
125
|
-
F:
|
|
126
|
-
L:
|
|
274
|
+
F: __dxlog_file3,
|
|
275
|
+
L: 100,
|
|
127
276
|
S: this,
|
|
128
277
|
C: (f, a) => f(...a)
|
|
129
278
|
});
|
|
130
279
|
return;
|
|
131
280
|
}
|
|
281
|
+
this._recordBytes(binary.byteLength, 0);
|
|
132
282
|
this._ws.send(binary);
|
|
133
283
|
} else {
|
|
284
|
+
const binary = buf.toBinary(MessageSchema, message);
|
|
285
|
+
this._recordBytes(binary.byteLength, 0);
|
|
134
286
|
this._wsMuxer.send(message).catch((e) => log.catch(e, void 0, {
|
|
135
|
-
F:
|
|
136
|
-
L:
|
|
287
|
+
F: __dxlog_file3,
|
|
288
|
+
L: 113,
|
|
137
289
|
S: this,
|
|
138
290
|
C: (f, a) => f(...a)
|
|
139
291
|
}));
|
|
@@ -154,19 +306,21 @@ var EdgeWsConnection = class extends Resource {
|
|
|
154
306
|
this._ws.onopen = () => {
|
|
155
307
|
if (this.isOpen) {
|
|
156
308
|
log("connected", void 0, {
|
|
157
|
-
F:
|
|
158
|
-
L:
|
|
309
|
+
F: __dxlog_file3,
|
|
310
|
+
L: 130,
|
|
159
311
|
S: this,
|
|
160
312
|
C: (f, a) => f(...a)
|
|
161
313
|
});
|
|
314
|
+
this._openTimestamp = Date.now();
|
|
162
315
|
this._callbacks.onConnected();
|
|
163
316
|
this._scheduleHeartbeats();
|
|
317
|
+
this._scheduleRateCalculation();
|
|
164
318
|
} else {
|
|
165
319
|
log.verbose("connected after becoming inactive", {
|
|
166
320
|
currentIdentity: this._identity
|
|
167
321
|
}, {
|
|
168
|
-
F:
|
|
169
|
-
L:
|
|
322
|
+
F: __dxlog_file3,
|
|
323
|
+
L: 136,
|
|
170
324
|
S: this,
|
|
171
325
|
C: (f, a) => f(...a)
|
|
172
326
|
});
|
|
@@ -174,12 +328,12 @@ var EdgeWsConnection = class extends Resource {
|
|
|
174
328
|
};
|
|
175
329
|
this._ws.onclose = (event) => {
|
|
176
330
|
if (this.isOpen) {
|
|
177
|
-
log.warn("disconnected
|
|
331
|
+
log.warn("server disconnected", {
|
|
178
332
|
code: event.code,
|
|
179
333
|
reason: event.reason
|
|
180
334
|
}, {
|
|
181
|
-
F:
|
|
182
|
-
L:
|
|
335
|
+
F: __dxlog_file3,
|
|
336
|
+
L: 141,
|
|
183
337
|
S: this,
|
|
184
338
|
C: (f, a) => f(...a)
|
|
185
339
|
});
|
|
@@ -193,8 +347,8 @@ var EdgeWsConnection = class extends Resource {
|
|
|
193
347
|
error: event.error,
|
|
194
348
|
info: event.message
|
|
195
349
|
}, {
|
|
196
|
-
F:
|
|
197
|
-
L:
|
|
350
|
+
F: __dxlog_file3,
|
|
351
|
+
L: 148,
|
|
198
352
|
S: this,
|
|
199
353
|
C: (f, a) => f(...a)
|
|
200
354
|
});
|
|
@@ -203,8 +357,8 @@ var EdgeWsConnection = class extends Resource {
|
|
|
203
357
|
log.verbose("error ignored on closed connection", {
|
|
204
358
|
error: event.error
|
|
205
359
|
}, {
|
|
206
|
-
F:
|
|
207
|
-
L:
|
|
360
|
+
F: __dxlog_file3,
|
|
361
|
+
L: 151,
|
|
208
362
|
S: this,
|
|
209
363
|
C: (f, a) => f(...a)
|
|
210
364
|
});
|
|
@@ -215,29 +369,36 @@ var EdgeWsConnection = class extends Resource {
|
|
|
215
369
|
log.verbose("message ignored on closed connection", {
|
|
216
370
|
event: event.type
|
|
217
371
|
}, {
|
|
218
|
-
F:
|
|
219
|
-
L:
|
|
372
|
+
F: __dxlog_file3,
|
|
373
|
+
L: 159,
|
|
220
374
|
S: this,
|
|
221
375
|
C: (f, a) => f(...a)
|
|
222
376
|
});
|
|
223
377
|
return;
|
|
224
378
|
}
|
|
379
|
+
this._lastReceivedMessageTimestamp = Date.now();
|
|
225
380
|
if (event.data === "__pong__") {
|
|
381
|
+
if (this._pingTimestamp) {
|
|
382
|
+
this._rtt = Date.now() - this._pingTimestamp;
|
|
383
|
+
this._pingTimestamp = void 0;
|
|
384
|
+
}
|
|
226
385
|
this._rescheduleHeartbeatTimeout();
|
|
227
386
|
return;
|
|
228
387
|
}
|
|
229
388
|
const bytes = await toUint8Array(event.data);
|
|
389
|
+
this._recordBytes(0, bytes.byteLength);
|
|
230
390
|
if (!this.isOpen) {
|
|
231
391
|
return;
|
|
232
392
|
}
|
|
393
|
+
this._messagesReceived++;
|
|
233
394
|
const message = this._ws?.protocol?.includes(EdgeWebsocketProtocol.V0) ? buf.fromBinary(MessageSchema, bytes) : muxer.receiveData(bytes);
|
|
234
395
|
if (message) {
|
|
235
396
|
log("received", {
|
|
236
397
|
from: message.source,
|
|
237
398
|
payload: protocol.getPayloadType(message)
|
|
238
399
|
}, {
|
|
239
|
-
F:
|
|
240
|
-
L:
|
|
400
|
+
F: __dxlog_file3,
|
|
401
|
+
L: 185,
|
|
241
402
|
S: this,
|
|
242
403
|
C: (f, a) => f(...a)
|
|
243
404
|
});
|
|
@@ -257,20 +418,20 @@ var EdgeWsConnection = class extends Resource {
|
|
|
257
418
|
if (err instanceof Error && err.message.includes("WebSocket is closed before the connection is established.")) {
|
|
258
419
|
return;
|
|
259
420
|
}
|
|
260
|
-
log.warn("
|
|
421
|
+
log.warn("error closing websocket", {
|
|
261
422
|
err
|
|
262
423
|
}, {
|
|
263
|
-
F:
|
|
264
|
-
L:
|
|
424
|
+
F: __dxlog_file3,
|
|
425
|
+
L: 203,
|
|
265
426
|
S: this,
|
|
266
427
|
C: (f, a) => f(...a)
|
|
267
428
|
});
|
|
268
429
|
}
|
|
269
430
|
}
|
|
270
431
|
_scheduleHeartbeats() {
|
|
271
|
-
|
|
272
|
-
F:
|
|
273
|
-
L:
|
|
432
|
+
invariant3(this._ws, void 0, {
|
|
433
|
+
F: __dxlog_file3,
|
|
434
|
+
L: 208,
|
|
274
435
|
S: this,
|
|
275
436
|
A: [
|
|
276
437
|
"this._ws",
|
|
@@ -278,8 +439,10 @@ var EdgeWsConnection = class extends Resource {
|
|
|
278
439
|
]
|
|
279
440
|
});
|
|
280
441
|
scheduleTaskInterval(this._ctx, async () => {
|
|
442
|
+
this._pingTimestamp = Date.now();
|
|
281
443
|
this._ws?.send("__ping__");
|
|
282
444
|
}, SIGNAL_KEEPALIVE_INTERVAL);
|
|
445
|
+
this._pingTimestamp = Date.now();
|
|
283
446
|
this._ws.send("__ping__");
|
|
284
447
|
this._rescheduleHeartbeatTimeout();
|
|
285
448
|
}
|
|
@@ -289,21 +452,68 @@ var EdgeWsConnection = class extends Resource {
|
|
|
289
452
|
}
|
|
290
453
|
void this._inactivityTimeoutCtx?.dispose();
|
|
291
454
|
this._inactivityTimeoutCtx = new Context(void 0, {
|
|
292
|
-
F:
|
|
293
|
-
L:
|
|
455
|
+
F: __dxlog_file3,
|
|
456
|
+
L: 229
|
|
294
457
|
});
|
|
295
458
|
scheduleTask(this._inactivityTimeoutCtx, () => {
|
|
296
459
|
if (this.isOpen) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
460
|
+
if (Date.now() - this._lastReceivedMessageTimestamp > SIGNAL_KEEPALIVE_TIMEOUT) {
|
|
461
|
+
log.warn("restart due to inactivity timeout", {
|
|
462
|
+
lastReceivedMessageTimestamp: this._lastReceivedMessageTimestamp
|
|
463
|
+
}, {
|
|
464
|
+
F: __dxlog_file3,
|
|
465
|
+
L: 235,
|
|
466
|
+
S: this,
|
|
467
|
+
C: (f, a) => f(...a)
|
|
468
|
+
});
|
|
469
|
+
this._callbacks.onRestartRequired();
|
|
470
|
+
} else {
|
|
471
|
+
this._rescheduleHeartbeatTimeout();
|
|
472
|
+
}
|
|
304
473
|
}
|
|
305
474
|
}, SIGNAL_KEEPALIVE_TIMEOUT);
|
|
306
475
|
}
|
|
476
|
+
_recordBytes(sent, received) {
|
|
477
|
+
const now = Date.now();
|
|
478
|
+
const currentSecond = Math.floor(now / 1e3) * 1e3;
|
|
479
|
+
const existingSample = this._bytesSamples.find((s) => Math.floor(s.timestamp / 1e3) * 1e3 === currentSecond);
|
|
480
|
+
if (existingSample) {
|
|
481
|
+
existingSample.sent += sent;
|
|
482
|
+
existingSample.received += received;
|
|
483
|
+
} else {
|
|
484
|
+
this._bytesSamples.push({
|
|
485
|
+
timestamp: now,
|
|
486
|
+
sent,
|
|
487
|
+
received
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
_scheduleRateCalculation() {
|
|
492
|
+
scheduleTaskInterval(this._ctx, async () => {
|
|
493
|
+
this._calculateRates();
|
|
494
|
+
}, this._rateUpdateInterval);
|
|
495
|
+
this._calculateRates();
|
|
496
|
+
}
|
|
497
|
+
_calculateRates() {
|
|
498
|
+
const now = Date.now();
|
|
499
|
+
const cutoff = now - this._rateWindow;
|
|
500
|
+
this._bytesSamples = this._bytesSamples.filter((s) => s.timestamp > cutoff);
|
|
501
|
+
if (this._bytesSamples.length === 0) {
|
|
502
|
+
this._uploadRate = 0;
|
|
503
|
+
this._downloadRate = 0;
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
let totalSent = 0;
|
|
507
|
+
let totalReceived = 0;
|
|
508
|
+
const oldestTimestamp = Math.min(...this._bytesSamples.map((s) => s.timestamp));
|
|
509
|
+
const timeSpan = (now - oldestTimestamp) / 1e3;
|
|
510
|
+
for (const sample of this._bytesSamples) {
|
|
511
|
+
totalSent += sample.sent;
|
|
512
|
+
totalReceived += sample.received;
|
|
513
|
+
}
|
|
514
|
+
this._uploadRate = timeSpan > 0 ? Math.round(totalSent / timeSpan) : 0;
|
|
515
|
+
this._downloadRate = timeSpan > 0 ? Math.round(totalReceived / timeSpan) : 0;
|
|
516
|
+
}
|
|
307
517
|
};
|
|
308
518
|
_ts_decorate([
|
|
309
519
|
logInfo
|
|
@@ -336,14 +546,25 @@ function _ts_decorate2(decorators, target, key, desc) {
|
|
|
336
546
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
337
547
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
338
548
|
}
|
|
339
|
-
var
|
|
549
|
+
var __dxlog_file4 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/edge-client.ts";
|
|
340
550
|
var DEFAULT_TIMEOUT = 1e4;
|
|
551
|
+
var STATUS_REFRESH_INTERVAL = 1e3;
|
|
341
552
|
var EdgeClient = class extends Resource2 {
|
|
553
|
+
_identity;
|
|
554
|
+
_config;
|
|
555
|
+
statusChanged = new Event();
|
|
556
|
+
_persistentLifecycle = new PersistentLifecycle({
|
|
557
|
+
start: async () => this._connect(),
|
|
558
|
+
stop: async (state) => this._disconnect(state)
|
|
559
|
+
});
|
|
560
|
+
_messageListeners = /* @__PURE__ */ new Set();
|
|
561
|
+
_reconnectListeners = /* @__PURE__ */ new Set();
|
|
562
|
+
_baseWsUrl;
|
|
563
|
+
_baseHttpUrl;
|
|
564
|
+
_currentConnection = void 0;
|
|
565
|
+
_ready = new Trigger();
|
|
342
566
|
constructor(_identity, _config) {
|
|
343
|
-
super(), this._identity = _identity, this._config = _config
|
|
344
|
-
start: async () => this._connect(),
|
|
345
|
-
stop: async (state) => this._disconnect(state)
|
|
346
|
-
}), this._messageListeners = /* @__PURE__ */ new Set(), this._reconnectListeners = /* @__PURE__ */ new Set(), this._currentConnection = void 0, this._ready = new Trigger(), this._isActive = (connection) => connection === this._currentConnection;
|
|
567
|
+
super(), this._identity = _identity, this._config = _config;
|
|
347
568
|
this._baseWsUrl = getEdgeUrlWithProtocol(_config.socketEndpoint, "ws");
|
|
348
569
|
this._baseHttpUrl = getEdgeUrlWithProtocol(_config.socketEndpoint, "http");
|
|
349
570
|
}
|
|
@@ -356,7 +577,15 @@ var EdgeClient = class extends Resource2 {
|
|
|
356
577
|
};
|
|
357
578
|
}
|
|
358
579
|
get status() {
|
|
359
|
-
return
|
|
580
|
+
return {
|
|
581
|
+
state: Boolean(this._currentConnection) && this._ready.state === TriggerState.RESOLVED ? EdgeStatus.ConnectionState.CONNECTED : EdgeStatus.ConnectionState.NOT_CONNECTED,
|
|
582
|
+
uptime: this._currentConnection?.uptime ?? 0,
|
|
583
|
+
rtt: this._currentConnection?.rtt ?? 0,
|
|
584
|
+
rateBytesUp: this._currentConnection?.uploadRate ?? 0,
|
|
585
|
+
rateBytesDown: this._currentConnection?.downloadRate ?? 0,
|
|
586
|
+
messagesSent: this._currentConnection?.messagesSent ?? 0,
|
|
587
|
+
messagesReceived: this._currentConnection?.messagesReceived ?? 0
|
|
588
|
+
};
|
|
360
589
|
}
|
|
361
590
|
get identityKey() {
|
|
362
591
|
return this._identity.identityKey;
|
|
@@ -370,8 +599,8 @@ var EdgeClient = class extends Resource2 {
|
|
|
370
599
|
identity,
|
|
371
600
|
oldIdentity: this._identity
|
|
372
601
|
}, {
|
|
373
|
-
F:
|
|
374
|
-
L:
|
|
602
|
+
F: __dxlog_file4,
|
|
603
|
+
L: 118,
|
|
375
604
|
S: this,
|
|
376
605
|
C: (f, a) => f(...a)
|
|
377
606
|
});
|
|
@@ -380,6 +609,30 @@ var EdgeClient = class extends Resource2 {
|
|
|
380
609
|
void this._persistentLifecycle.scheduleRestart();
|
|
381
610
|
}
|
|
382
611
|
}
|
|
612
|
+
/**
|
|
613
|
+
* Send message.
|
|
614
|
+
* NOTE: The message is guaranteed to be delivered but the service must respond with a message to confirm processing.
|
|
615
|
+
*/
|
|
616
|
+
async send(message) {
|
|
617
|
+
if (this._ready.state !== TriggerState.RESOLVED) {
|
|
618
|
+
log2("waiting for websocket", void 0, {
|
|
619
|
+
F: __dxlog_file4,
|
|
620
|
+
L: 131,
|
|
621
|
+
S: this,
|
|
622
|
+
C: (f, a) => f(...a)
|
|
623
|
+
});
|
|
624
|
+
await this._ready.wait({
|
|
625
|
+
timeout: this._config.timeout ?? DEFAULT_TIMEOUT
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
if (!this._currentConnection) {
|
|
629
|
+
throw new EdgeConnectionClosedError();
|
|
630
|
+
}
|
|
631
|
+
if (message.source && (message.source.peerKey !== this._identity.peerKey || message.source.identityKey !== this.identityKey)) {
|
|
632
|
+
throw new EdgeIdentityChangedError();
|
|
633
|
+
}
|
|
634
|
+
this._currentConnection.send(message);
|
|
635
|
+
}
|
|
383
636
|
onMessage(listener) {
|
|
384
637
|
this._messageListeners.add(listener);
|
|
385
638
|
return () => this._messageListeners.delete(listener);
|
|
@@ -393,8 +646,8 @@ var EdgeClient = class extends Resource2 {
|
|
|
393
646
|
listener();
|
|
394
647
|
} catch (error) {
|
|
395
648
|
log2.catch(error, void 0, {
|
|
396
|
-
F:
|
|
397
|
-
L:
|
|
649
|
+
F: __dxlog_file4,
|
|
650
|
+
L: 164,
|
|
398
651
|
S: this,
|
|
399
652
|
C: (f, a) => f(...a)
|
|
400
653
|
});
|
|
@@ -411,8 +664,8 @@ var EdgeClient = class extends Resource2 {
|
|
|
411
664
|
log2("opening...", {
|
|
412
665
|
info: this.info
|
|
413
666
|
}, {
|
|
414
|
-
F:
|
|
415
|
-
L:
|
|
667
|
+
F: __dxlog_file4,
|
|
668
|
+
L: 177,
|
|
416
669
|
S: this,
|
|
417
670
|
C: (f, a) => f(...a)
|
|
418
671
|
});
|
|
@@ -420,12 +673,18 @@ var EdgeClient = class extends Resource2 {
|
|
|
420
673
|
log2.warn("Error while opening connection", {
|
|
421
674
|
err
|
|
422
675
|
}, {
|
|
423
|
-
F:
|
|
424
|
-
L:
|
|
676
|
+
F: __dxlog_file4,
|
|
677
|
+
L: 179,
|
|
425
678
|
S: this,
|
|
426
679
|
C: (f, a) => f(...a)
|
|
427
680
|
});
|
|
428
681
|
});
|
|
682
|
+
scheduleTaskInterval2(this._ctx, async () => {
|
|
683
|
+
if (!this._currentConnection) {
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
this.statusChanged.emit(this.status);
|
|
687
|
+
}, STATUS_REFRESH_INTERVAL);
|
|
429
688
|
}
|
|
430
689
|
/**
|
|
431
690
|
* Close connection and free resources.
|
|
@@ -434,8 +693,8 @@ var EdgeClient = class extends Resource2 {
|
|
|
434
693
|
log2("closing...", {
|
|
435
694
|
peerKey: this._identity.peerKey
|
|
436
695
|
}, {
|
|
437
|
-
F:
|
|
438
|
-
L:
|
|
696
|
+
F: __dxlog_file4,
|
|
697
|
+
L: 199,
|
|
439
698
|
S: this,
|
|
440
699
|
C: (f, a) => f(...a)
|
|
441
700
|
});
|
|
@@ -451,8 +710,8 @@ var EdgeClient = class extends Resource2 {
|
|
|
451
710
|
const protocolHeader = this._config.disableAuth ? void 0 : await this._createAuthHeader(path);
|
|
452
711
|
if (this._identity !== identity) {
|
|
453
712
|
log2("identity changed during auth header request", void 0, {
|
|
454
|
-
F:
|
|
455
|
-
L:
|
|
713
|
+
F: __dxlog_file4,
|
|
714
|
+
L: 213,
|
|
456
715
|
S: this,
|
|
457
716
|
C: (f, a) => f(...a)
|
|
458
717
|
});
|
|
@@ -464,8 +723,8 @@ var EdgeClient = class extends Resource2 {
|
|
|
464
723
|
url: url.toString(),
|
|
465
724
|
protocolHeader
|
|
466
725
|
}, {
|
|
467
|
-
F:
|
|
468
|
-
L:
|
|
726
|
+
F: __dxlog_file4,
|
|
727
|
+
L: 219,
|
|
469
728
|
S: this,
|
|
470
729
|
C: (f, a) => f(...a)
|
|
471
730
|
});
|
|
@@ -479,8 +738,8 @@ var EdgeClient = class extends Resource2 {
|
|
|
479
738
|
this._notifyReconnected();
|
|
480
739
|
} else {
|
|
481
740
|
log2.verbose("connected callback ignored, because connection is not active", void 0, {
|
|
482
|
-
F:
|
|
483
|
-
L:
|
|
741
|
+
F: __dxlog_file4,
|
|
742
|
+
L: 229,
|
|
484
743
|
S: this,
|
|
485
744
|
C: (f, a) => f(...a)
|
|
486
745
|
});
|
|
@@ -492,8 +751,8 @@ var EdgeClient = class extends Resource2 {
|
|
|
492
751
|
void this._persistentLifecycle.scheduleRestart();
|
|
493
752
|
} else {
|
|
494
753
|
log2.verbose("restart requested by inactive connection", void 0, {
|
|
495
|
-
F:
|
|
496
|
-
L:
|
|
754
|
+
F: __dxlog_file4,
|
|
755
|
+
L: 237,
|
|
497
756
|
S: this,
|
|
498
757
|
C: (f, a) => f(...a)
|
|
499
758
|
});
|
|
@@ -508,8 +767,8 @@ var EdgeClient = class extends Resource2 {
|
|
|
508
767
|
from: message.source,
|
|
509
768
|
type: message.payload?.typeUrl
|
|
510
769
|
}, {
|
|
511
|
-
F:
|
|
512
|
-
L:
|
|
770
|
+
F: __dxlog_file4,
|
|
771
|
+
L: 245,
|
|
513
772
|
S: this,
|
|
514
773
|
C: (f, a) => f(...a)
|
|
515
774
|
});
|
|
@@ -545,8 +804,8 @@ var EdgeClient = class extends Resource2 {
|
|
|
545
804
|
log2.error("ws reconnect listener failed", {
|
|
546
805
|
err
|
|
547
806
|
}, {
|
|
548
|
-
F:
|
|
549
|
-
L:
|
|
807
|
+
F: __dxlog_file4,
|
|
808
|
+
L: 280,
|
|
550
809
|
S: this,
|
|
551
810
|
C: (f, a) => f(...a)
|
|
552
811
|
});
|
|
@@ -562,38 +821,14 @@ var EdgeClient = class extends Resource2 {
|
|
|
562
821
|
err,
|
|
563
822
|
payload: protocol.getPayloadType(message)
|
|
564
823
|
}, {
|
|
565
|
-
F:
|
|
566
|
-
L:
|
|
824
|
+
F: __dxlog_file4,
|
|
825
|
+
L: 290,
|
|
567
826
|
S: this,
|
|
568
827
|
C: (f, a) => f(...a)
|
|
569
828
|
});
|
|
570
829
|
}
|
|
571
830
|
}
|
|
572
831
|
}
|
|
573
|
-
/**
|
|
574
|
-
* Send message.
|
|
575
|
-
* NOTE: The message is guaranteed to be delivered but the service must respond with a message to confirm processing.
|
|
576
|
-
*/
|
|
577
|
-
async send(message) {
|
|
578
|
-
if (this._ready.state !== TriggerState.RESOLVED) {
|
|
579
|
-
log2("waiting for websocket to become ready", void 0, {
|
|
580
|
-
F: __dxlog_file3,
|
|
581
|
-
L: 246,
|
|
582
|
-
S: this,
|
|
583
|
-
C: (f, a) => f(...a)
|
|
584
|
-
});
|
|
585
|
-
await this._ready.wait({
|
|
586
|
-
timeout: this._config.timeout ?? DEFAULT_TIMEOUT
|
|
587
|
-
});
|
|
588
|
-
}
|
|
589
|
-
if (!this._currentConnection) {
|
|
590
|
-
throw new EdgeConnectionClosedError();
|
|
591
|
-
}
|
|
592
|
-
if (message.source && (message.source.peerKey !== this._identity.peerKey || message.source.identityKey !== this.identityKey)) {
|
|
593
|
-
throw new EdgeIdentityChangedError();
|
|
594
|
-
}
|
|
595
|
-
this._currentConnection.send(message);
|
|
596
|
-
}
|
|
597
832
|
async _createAuthHeader(path) {
|
|
598
833
|
const httpUrl = new URL(path, this._baseHttpUrl);
|
|
599
834
|
httpUrl.protocol = getEdgeUrlWithProtocol(this._baseWsUrl.toString(), "http");
|
|
@@ -607,14 +842,15 @@ var EdgeClient = class extends Resource2 {
|
|
|
607
842
|
status: response.status,
|
|
608
843
|
statusText: response.statusText
|
|
609
844
|
}, {
|
|
610
|
-
F:
|
|
611
|
-
L:
|
|
845
|
+
F: __dxlog_file4,
|
|
846
|
+
L: 302,
|
|
612
847
|
S: this,
|
|
613
848
|
C: (f, a) => f(...a)
|
|
614
849
|
});
|
|
615
850
|
return void 0;
|
|
616
851
|
}
|
|
617
852
|
}
|
|
853
|
+
_isActive = (connection) => connection === this._currentConnection;
|
|
618
854
|
};
|
|
619
855
|
_ts_decorate2([
|
|
620
856
|
logInfo2
|
|
@@ -624,144 +860,38 @@ var encodePresentationWsAuthHeader = (encodedPresentation) => {
|
|
|
624
860
|
return `base64url.bearer.authorization.dxos.org.${encodedToken}`;
|
|
625
861
|
};
|
|
626
862
|
|
|
627
|
-
// src/auth.ts
|
|
628
|
-
import { createCredential, signPresentation } from "@dxos/credentials";
|
|
629
|
-
import { invariant as invariant3 } from "@dxos/invariant";
|
|
630
|
-
import { Keyring } from "@dxos/keyring";
|
|
631
|
-
import { PublicKey } from "@dxos/keys";
|
|
632
|
-
var __dxlog_file4 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/auth.ts";
|
|
633
|
-
var createDeviceEdgeIdentity = async (signer, key) => {
|
|
634
|
-
return {
|
|
635
|
-
identityKey: key.toHex(),
|
|
636
|
-
peerKey: key.toHex(),
|
|
637
|
-
presentCredentials: async ({ challenge }) => {
|
|
638
|
-
return signPresentation({
|
|
639
|
-
presentation: {
|
|
640
|
-
credentials: [
|
|
641
|
-
// Verifier requires at least one credential in the presentation to establish the subject.
|
|
642
|
-
await createCredential({
|
|
643
|
-
assertion: {
|
|
644
|
-
"@type": "dxos.halo.credentials.Auth"
|
|
645
|
-
},
|
|
646
|
-
issuer: key,
|
|
647
|
-
subject: key,
|
|
648
|
-
signer
|
|
649
|
-
})
|
|
650
|
-
]
|
|
651
|
-
},
|
|
652
|
-
signer,
|
|
653
|
-
signerKey: key,
|
|
654
|
-
nonce: challenge
|
|
655
|
-
});
|
|
656
|
-
}
|
|
657
|
-
};
|
|
658
|
-
};
|
|
659
|
-
var createChainEdgeIdentity = async (signer, identityKey, peerKey, chain, credentials) => {
|
|
660
|
-
const credentialsToSign = credentials.length > 0 ? credentials : [
|
|
661
|
-
await createCredential({
|
|
662
|
-
assertion: {
|
|
663
|
-
"@type": "dxos.halo.credentials.Auth"
|
|
664
|
-
},
|
|
665
|
-
issuer: identityKey,
|
|
666
|
-
subject: identityKey,
|
|
667
|
-
signer,
|
|
668
|
-
chain,
|
|
669
|
-
signingKey: peerKey
|
|
670
|
-
})
|
|
671
|
-
];
|
|
672
|
-
return {
|
|
673
|
-
identityKey: identityKey.toHex(),
|
|
674
|
-
peerKey: peerKey.toHex(),
|
|
675
|
-
presentCredentials: async ({ challenge }) => {
|
|
676
|
-
invariant3(chain, void 0, {
|
|
677
|
-
F: __dxlog_file4,
|
|
678
|
-
L: 75,
|
|
679
|
-
S: void 0,
|
|
680
|
-
A: [
|
|
681
|
-
"chain",
|
|
682
|
-
""
|
|
683
|
-
]
|
|
684
|
-
});
|
|
685
|
-
return signPresentation({
|
|
686
|
-
presentation: {
|
|
687
|
-
credentials: credentialsToSign
|
|
688
|
-
},
|
|
689
|
-
signer,
|
|
690
|
-
nonce: challenge,
|
|
691
|
-
signerKey: peerKey,
|
|
692
|
-
chain
|
|
693
|
-
});
|
|
694
|
-
}
|
|
695
|
-
};
|
|
696
|
-
};
|
|
697
|
-
var createEphemeralEdgeIdentity = async () => {
|
|
698
|
-
const keyring = new Keyring();
|
|
699
|
-
const key = await keyring.createKey();
|
|
700
|
-
return createDeviceEdgeIdentity(keyring, key);
|
|
701
|
-
};
|
|
702
|
-
var createTestHaloEdgeIdentity = async (signer, identityKey, deviceKey) => {
|
|
703
|
-
const deviceAdmission = await createCredential({
|
|
704
|
-
assertion: {
|
|
705
|
-
"@type": "dxos.halo.credentials.AuthorizedDevice",
|
|
706
|
-
deviceKey,
|
|
707
|
-
identityKey
|
|
708
|
-
},
|
|
709
|
-
issuer: identityKey,
|
|
710
|
-
subject: deviceKey,
|
|
711
|
-
signer
|
|
712
|
-
});
|
|
713
|
-
return createChainEdgeIdentity(signer, identityKey, deviceKey, {
|
|
714
|
-
credential: deviceAdmission
|
|
715
|
-
}, [
|
|
716
|
-
await createCredential({
|
|
717
|
-
assertion: {
|
|
718
|
-
"@type": "dxos.halo.credentials.Auth"
|
|
719
|
-
},
|
|
720
|
-
issuer: identityKey,
|
|
721
|
-
subject: identityKey,
|
|
722
|
-
signer
|
|
723
|
-
})
|
|
724
|
-
]);
|
|
725
|
-
};
|
|
726
|
-
var createStubEdgeIdentity = () => {
|
|
727
|
-
const identityKey = PublicKey.random();
|
|
728
|
-
const deviceKey = PublicKey.random();
|
|
729
|
-
return {
|
|
730
|
-
identityKey: identityKey.toHex(),
|
|
731
|
-
peerKey: deviceKey.toHex(),
|
|
732
|
-
presentCredentials: async () => {
|
|
733
|
-
throw new Error("Stub identity does not support authentication.");
|
|
734
|
-
}
|
|
735
|
-
};
|
|
736
|
-
};
|
|
737
|
-
|
|
738
863
|
// src/edge-http-client.ts
|
|
739
|
-
import
|
|
740
|
-
import
|
|
864
|
+
import * as FetchHttpClient from "@effect/platform/FetchHttpClient";
|
|
865
|
+
import * as HttpClient from "@effect/platform/HttpClient";
|
|
866
|
+
import * as Effect2 from "effect/Effect";
|
|
867
|
+
import * as Function from "effect/Function";
|
|
741
868
|
import { sleep } from "@dxos/async";
|
|
742
869
|
import { Context as Context3 } from "@dxos/context";
|
|
870
|
+
import { invariant as invariant4 } from "@dxos/invariant";
|
|
743
871
|
import { log as log4 } from "@dxos/log";
|
|
744
872
|
import { EdgeAuthChallengeError, EdgeCallFailedError } from "@dxos/protocols";
|
|
745
873
|
import { createUrl } from "@dxos/util";
|
|
746
874
|
|
|
747
875
|
// src/http-client.ts
|
|
748
|
-
import
|
|
876
|
+
import * as Context2 from "effect/Context";
|
|
877
|
+
import * as Duration from "effect/Duration";
|
|
878
|
+
import * as Effect from "effect/Effect";
|
|
879
|
+
import * as Layer from "effect/Layer";
|
|
880
|
+
import * as Schedule from "effect/Schedule";
|
|
749
881
|
import { log as log3 } from "@dxos/log";
|
|
750
882
|
var __dxlog_file5 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/http-client.ts";
|
|
751
883
|
var HttpConfig = class _HttpConfig extends Context2.Tag("HttpConfig")() {
|
|
752
|
-
static {
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
});
|
|
758
|
-
}
|
|
884
|
+
static default = Layer.succeed(_HttpConfig, {
|
|
885
|
+
timeout: Duration.millis(1e3),
|
|
886
|
+
retryTimes: 3,
|
|
887
|
+
retryBaseDelay: Duration.millis(1e3)
|
|
888
|
+
});
|
|
759
889
|
};
|
|
760
|
-
var withRetry = (effect, { timeout = Duration.millis(1e3), retryBaseDelay = Duration.millis(1e3), retryTimes = 3 } = {}) => {
|
|
890
|
+
var withRetry = (effect, { timeout: timeout2 = Duration.millis(1e3), retryBaseDelay = Duration.millis(1e3), retryTimes = 3 } = {}) => {
|
|
761
891
|
return effect.pipe(Effect.flatMap((res) => (
|
|
762
892
|
// Treat 500 errors as retryable?
|
|
763
893
|
res.status === 500 ? Effect.fail(new Error(res.status.toString())) : res.json
|
|
764
|
-
)), Effect.timeout(
|
|
894
|
+
)), Effect.timeout(timeout2), Effect.retry({
|
|
765
895
|
schedule: Schedule.exponential(retryBaseDelay).pipe(Schedule.jittered),
|
|
766
896
|
times: retryTimes
|
|
767
897
|
}));
|
|
@@ -770,14 +900,16 @@ var withRetryConfig = (effect) => Effect.gen(function* () {
|
|
|
770
900
|
const config = yield* HttpConfig;
|
|
771
901
|
return yield* withRetry(effect, config);
|
|
772
902
|
});
|
|
773
|
-
var withLogging = (effect) => effect.pipe(Effect.tap((res) =>
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
903
|
+
var withLogging = (effect) => effect.pipe(Effect.tap((res) => {
|
|
904
|
+
log3.info("response", {
|
|
905
|
+
status: res.status
|
|
906
|
+
}, {
|
|
907
|
+
F: __dxlog_file5,
|
|
908
|
+
L: 66,
|
|
909
|
+
S: void 0,
|
|
910
|
+
C: (f, a) => f(...a)
|
|
911
|
+
});
|
|
912
|
+
}));
|
|
781
913
|
var encodeAuthHeader = (challenge) => {
|
|
782
914
|
const encodedChallenge = Buffer.from(challenge).toString("base64");
|
|
783
915
|
return `VerifiablePresentation pb;base64,${encodedChallenge}`;
|
|
@@ -788,14 +920,21 @@ var __dxlog_file6 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/edge-http
|
|
|
788
920
|
var DEFAULT_RETRY_TIMEOUT = 1500;
|
|
789
921
|
var DEFAULT_RETRY_JITTER = 500;
|
|
790
922
|
var DEFAULT_MAX_RETRIES_COUNT = 3;
|
|
923
|
+
var WARNING_BODY_SIZE = 10 * 1024 * 1024;
|
|
791
924
|
var EdgeHttpClient = class {
|
|
925
|
+
_baseUrl;
|
|
926
|
+
_edgeIdentity;
|
|
927
|
+
/**
|
|
928
|
+
* Auth header is cached until receiving the next 401 from EDGE, at which point it gets refreshed.
|
|
929
|
+
*/
|
|
930
|
+
_authHeader;
|
|
792
931
|
constructor(baseUrl) {
|
|
793
932
|
this._baseUrl = getEdgeUrlWithProtocol(baseUrl, "http");
|
|
794
933
|
log4("created", {
|
|
795
934
|
url: this._baseUrl
|
|
796
935
|
}, {
|
|
797
936
|
F: __dxlog_file6,
|
|
798
|
-
L:
|
|
937
|
+
L: 107,
|
|
799
938
|
S: this,
|
|
800
939
|
C: (f, a) => f(...a)
|
|
801
940
|
});
|
|
@@ -927,6 +1066,19 @@ var EdgeHttpClient = class {
|
|
|
927
1066
|
// Functions
|
|
928
1067
|
//
|
|
929
1068
|
async uploadFunction(pathParts, body, args) {
|
|
1069
|
+
const formData = new FormData();
|
|
1070
|
+
formData.append("name", body.name ?? "");
|
|
1071
|
+
formData.append("version", body.version);
|
|
1072
|
+
formData.append("ownerPublicKey", body.ownerPublicKey);
|
|
1073
|
+
formData.append("entryPoint", body.entryPoint);
|
|
1074
|
+
body.runtime && formData.append("runtime", body.runtime);
|
|
1075
|
+
for (const [filename, content] of Object.entries(body.assets)) {
|
|
1076
|
+
formData.append("assets", new Blob([
|
|
1077
|
+
content
|
|
1078
|
+
], {
|
|
1079
|
+
type: getFileMimeType(filename)
|
|
1080
|
+
}), filename);
|
|
1081
|
+
}
|
|
930
1082
|
const path = [
|
|
931
1083
|
"functions",
|
|
932
1084
|
...pathParts.functionId ? [
|
|
@@ -935,8 +1087,36 @@ var EdgeHttpClient = class {
|
|
|
935
1087
|
].join("/");
|
|
936
1088
|
return this._call(new URL(path, this.baseUrl), {
|
|
937
1089
|
...args,
|
|
938
|
-
body,
|
|
939
|
-
method: "PUT"
|
|
1090
|
+
body: formData,
|
|
1091
|
+
method: "PUT",
|
|
1092
|
+
json: false
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
async listFunctions(args) {
|
|
1096
|
+
return this._call(new URL("/functions", this.baseUrl), {
|
|
1097
|
+
...args,
|
|
1098
|
+
method: "GET"
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
async invokeFunction(params, input, args) {
|
|
1102
|
+
const url = new URL(`/functions/${params.functionId}`, this.baseUrl);
|
|
1103
|
+
if (params.version) {
|
|
1104
|
+
url.searchParams.set("version", params.version);
|
|
1105
|
+
}
|
|
1106
|
+
if (params.spaceId) {
|
|
1107
|
+
url.searchParams.set("spaceId", params.spaceId.toString());
|
|
1108
|
+
}
|
|
1109
|
+
if (params.cpuTimeLimit) {
|
|
1110
|
+
url.searchParams.set("cpuTimeLimit", params.cpuTimeLimit.toString());
|
|
1111
|
+
}
|
|
1112
|
+
if (params.subrequestsLimit) {
|
|
1113
|
+
url.searchParams.set("subrequestsLimit", params.subrequestsLimit.toString());
|
|
1114
|
+
}
|
|
1115
|
+
return this._call(url, {
|
|
1116
|
+
...args,
|
|
1117
|
+
body: input,
|
|
1118
|
+
method: "POST",
|
|
1119
|
+
rawResponse: true
|
|
940
1120
|
});
|
|
941
1121
|
}
|
|
942
1122
|
//
|
|
@@ -950,71 +1130,136 @@ var EdgeHttpClient = class {
|
|
|
950
1130
|
});
|
|
951
1131
|
}
|
|
952
1132
|
//
|
|
1133
|
+
// Triggers
|
|
1134
|
+
//
|
|
1135
|
+
async getCronTriggers(spaceId) {
|
|
1136
|
+
return this._call(new URL(`/test/functions/${spaceId}/triggers/crons`, this.baseUrl), {
|
|
1137
|
+
method: "GET"
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
//
|
|
1141
|
+
// Import/Export space.
|
|
1142
|
+
//
|
|
1143
|
+
async importBundle(spaceId, body, args) {
|
|
1144
|
+
return this._call(new URL(`/spaces/${spaceId}/import`, this.baseUrl), {
|
|
1145
|
+
...args,
|
|
1146
|
+
body,
|
|
1147
|
+
method: "PUT"
|
|
1148
|
+
});
|
|
1149
|
+
}
|
|
1150
|
+
async exportBundle(spaceId, body, args) {
|
|
1151
|
+
return this._call(new URL(`/spaces/${spaceId}/export`, this.baseUrl), {
|
|
1152
|
+
...args,
|
|
1153
|
+
body,
|
|
1154
|
+
method: "POST"
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
//
|
|
953
1158
|
// Internal
|
|
954
1159
|
//
|
|
955
|
-
async _fetch(url,
|
|
956
|
-
return pipe(HttpClient.get(url), withLogging, withRetryConfig, Effect2.provide(FetchHttpClient.layer), Effect2.provide(HttpConfig.default), Effect2.withSpan("EdgeHttpClient"), Effect2.runPromise);
|
|
1160
|
+
async _fetch(url, _args) {
|
|
1161
|
+
return Function.pipe(HttpClient.get(url), withLogging, withRetryConfig, Effect2.provide(FetchHttpClient.layer), Effect2.provide(HttpConfig.default), Effect2.withSpan("EdgeHttpClient"), Effect2.runPromise);
|
|
957
1162
|
}
|
|
958
1163
|
// TODO(burdon): Refactor with effect (see edge-http-client.test.ts).
|
|
959
1164
|
async _call(url, args) {
|
|
960
1165
|
const shouldRetry = createRetryHandler(args);
|
|
961
|
-
const requestContext = args.context ??
|
|
1166
|
+
const requestContext = args.context ?? Context3.default(void 0, {
|
|
962
1167
|
F: __dxlog_file6,
|
|
963
|
-
L:
|
|
1168
|
+
L: 400
|
|
964
1169
|
});
|
|
965
1170
|
log4("fetch", {
|
|
966
1171
|
url,
|
|
967
1172
|
request: args.body
|
|
968
1173
|
}, {
|
|
969
1174
|
F: __dxlog_file6,
|
|
970
|
-
L:
|
|
1175
|
+
L: 401,
|
|
971
1176
|
S: this,
|
|
972
1177
|
C: (f, a) => f(...a)
|
|
973
1178
|
});
|
|
974
1179
|
let handledAuth = false;
|
|
1180
|
+
const tryCount = 1;
|
|
975
1181
|
while (true) {
|
|
976
|
-
let processingError;
|
|
977
|
-
let retryAfterHeaderValue = Number.NaN;
|
|
1182
|
+
let processingError = void 0;
|
|
978
1183
|
try {
|
|
1184
|
+
if (!this._authHeader && args.auth) {
|
|
1185
|
+
const response2 = await fetch(new URL(`/auth`, this.baseUrl));
|
|
1186
|
+
if (response2.status === 401) {
|
|
1187
|
+
this._authHeader = await this._handleUnauthorized(response2);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
979
1190
|
const request = createRequest(args, this._authHeader);
|
|
1191
|
+
log4("call edge", {
|
|
1192
|
+
url,
|
|
1193
|
+
tryCount,
|
|
1194
|
+
authHeader: !!this._authHeader
|
|
1195
|
+
}, {
|
|
1196
|
+
F: __dxlog_file6,
|
|
1197
|
+
L: 416,
|
|
1198
|
+
S: this,
|
|
1199
|
+
C: (f, a) => f(...a)
|
|
1200
|
+
});
|
|
980
1201
|
const response = await fetch(url, request);
|
|
981
|
-
retryAfterHeaderValue = Number(response.headers.get("Retry-After"));
|
|
982
1202
|
if (response.ok) {
|
|
983
|
-
const
|
|
984
|
-
if (
|
|
985
|
-
return
|
|
1203
|
+
const body2 = await response.clone().json();
|
|
1204
|
+
if (args.rawResponse) {
|
|
1205
|
+
return body2;
|
|
986
1206
|
}
|
|
987
|
-
|
|
988
|
-
url,
|
|
989
|
-
body
|
|
990
|
-
}, {
|
|
1207
|
+
invariant4(body2, "Expected body to be present", {
|
|
991
1208
|
F: __dxlog_file6,
|
|
992
|
-
L:
|
|
1209
|
+
L: 424,
|
|
993
1210
|
S: this,
|
|
994
|
-
|
|
1211
|
+
A: [
|
|
1212
|
+
"body",
|
|
1213
|
+
"'Expected body to be present'"
|
|
1214
|
+
]
|
|
995
1215
|
});
|
|
996
|
-
if (
|
|
997
|
-
|
|
998
|
-
}
|
|
999
|
-
|
|
1216
|
+
if (!("success" in body2)) {
|
|
1217
|
+
return body2;
|
|
1218
|
+
}
|
|
1219
|
+
if (body2.success) {
|
|
1220
|
+
return body2.data;
|
|
1000
1221
|
}
|
|
1001
1222
|
} else if (response.status === 401 && !handledAuth) {
|
|
1002
1223
|
this._authHeader = await this._handleUnauthorized(response);
|
|
1003
1224
|
handledAuth = true;
|
|
1004
1225
|
continue;
|
|
1226
|
+
}
|
|
1227
|
+
const body = response.headers.get("Content-Type") === "application/json" ? await response.clone().json() : void 0;
|
|
1228
|
+
invariant4(!body?.success, "Expected body to not be a failure response or undefined.", {
|
|
1229
|
+
F: __dxlog_file6,
|
|
1230
|
+
L: 440,
|
|
1231
|
+
S: this,
|
|
1232
|
+
A: [
|
|
1233
|
+
"!body?.success",
|
|
1234
|
+
"'Expected body to not be a failure response or undefined.'"
|
|
1235
|
+
]
|
|
1236
|
+
});
|
|
1237
|
+
if (body?.errorData?.type === "auth_challenge" && typeof body?.errorData?.challenge === "string") {
|
|
1238
|
+
processingError = new EdgeAuthChallengeError(body.errorData.challenge, body.errorData);
|
|
1239
|
+
} else if (body?.success === false) {
|
|
1240
|
+
processingError = EdgeCallFailedError.fromUnsuccessfulResponse(response, body);
|
|
1005
1241
|
} else {
|
|
1006
|
-
|
|
1242
|
+
invariant4(!response.ok, "Expected response to not be ok.", {
|
|
1243
|
+
F: __dxlog_file6,
|
|
1244
|
+
L: 447,
|
|
1245
|
+
S: this,
|
|
1246
|
+
A: [
|
|
1247
|
+
"!response.ok",
|
|
1248
|
+
"'Expected response to not be ok.'"
|
|
1249
|
+
]
|
|
1250
|
+
});
|
|
1251
|
+
processingError = await EdgeCallFailedError.fromHttpFailure(response);
|
|
1007
1252
|
}
|
|
1008
1253
|
} catch (error) {
|
|
1009
1254
|
processingError = EdgeCallFailedError.fromProcessingFailureCause(error);
|
|
1010
1255
|
}
|
|
1011
|
-
if (processingError
|
|
1012
|
-
log4("retrying edge request", {
|
|
1256
|
+
if (processingError?.isRetryable && await shouldRetry(requestContext, processingError.retryAfterMs)) {
|
|
1257
|
+
log4.verbose("retrying edge request", {
|
|
1013
1258
|
url,
|
|
1014
1259
|
processingError
|
|
1015
1260
|
}, {
|
|
1016
1261
|
F: __dxlog_file6,
|
|
1017
|
-
L:
|
|
1262
|
+
L: 455,
|
|
1018
1263
|
S: this,
|
|
1019
1264
|
C: (f, a) => f(...a)
|
|
1020
1265
|
});
|
|
@@ -1027,33 +1272,52 @@ var EdgeHttpClient = class {
|
|
|
1027
1272
|
if (!this._edgeIdentity) {
|
|
1028
1273
|
log4.warn("unauthorized response received before identity was set", void 0, {
|
|
1029
1274
|
F: __dxlog_file6,
|
|
1030
|
-
L:
|
|
1275
|
+
L: 464,
|
|
1031
1276
|
S: this,
|
|
1032
1277
|
C: (f, a) => f(...a)
|
|
1033
1278
|
});
|
|
1034
|
-
throw EdgeCallFailedError.fromHttpFailure(response);
|
|
1279
|
+
throw await EdgeCallFailedError.fromHttpFailure(response);
|
|
1035
1280
|
}
|
|
1036
1281
|
const challenge = await handleAuthChallenge(response, this._edgeIdentity);
|
|
1037
1282
|
return encodeAuthHeader(challenge);
|
|
1038
1283
|
}
|
|
1039
1284
|
};
|
|
1040
|
-
var createRequest = ({ method, body }, authHeader) => {
|
|
1285
|
+
var createRequest = ({ method, body, json = true }, authHeader) => {
|
|
1286
|
+
let requestBody;
|
|
1287
|
+
const headers = {};
|
|
1288
|
+
if (json) {
|
|
1289
|
+
requestBody = body && JSON.stringify(body);
|
|
1290
|
+
headers["Content-Type"] = "application/json";
|
|
1291
|
+
} else {
|
|
1292
|
+
requestBody = body;
|
|
1293
|
+
}
|
|
1294
|
+
if (typeof requestBody === "string" && requestBody.length > WARNING_BODY_SIZE) {
|
|
1295
|
+
log4.warn("Request with large body", {
|
|
1296
|
+
bodySize: requestBody.length
|
|
1297
|
+
}, {
|
|
1298
|
+
F: __dxlog_file6,
|
|
1299
|
+
L: 488,
|
|
1300
|
+
S: void 0,
|
|
1301
|
+
C: (f, a) => f(...a)
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
if (authHeader) {
|
|
1305
|
+
headers["Authorization"] = authHeader;
|
|
1306
|
+
}
|
|
1041
1307
|
return {
|
|
1042
1308
|
method,
|
|
1043
|
-
body:
|
|
1044
|
-
headers
|
|
1045
|
-
Authorization: authHeader
|
|
1046
|
-
} : void 0
|
|
1309
|
+
body: requestBody,
|
|
1310
|
+
headers
|
|
1047
1311
|
};
|
|
1048
1312
|
};
|
|
1049
|
-
var createRetryHandler = ({ retry }) => {
|
|
1050
|
-
if (!
|
|
1313
|
+
var createRetryHandler = ({ retry: retry2 }) => {
|
|
1314
|
+
if (!retry2 || retry2.count < 1) {
|
|
1051
1315
|
return async () => false;
|
|
1052
1316
|
}
|
|
1053
1317
|
let retries = 0;
|
|
1054
|
-
const maxRetries =
|
|
1055
|
-
const baseTimeout =
|
|
1056
|
-
const jitter =
|
|
1318
|
+
const maxRetries = retry2.count ?? DEFAULT_MAX_RETRIES_COUNT;
|
|
1319
|
+
const baseTimeout = retry2.timeout ?? DEFAULT_RETRY_TIMEOUT;
|
|
1320
|
+
const jitter = retry2.jitter ?? DEFAULT_RETRY_JITTER;
|
|
1057
1321
|
return async (ctx, retryAfter) => {
|
|
1058
1322
|
if (++retries > maxRetries || ctx.disposed) {
|
|
1059
1323
|
return false;
|
|
@@ -1061,12 +1325,16 @@ var createRetryHandler = ({ retry }) => {
|
|
|
1061
1325
|
if (retryAfter) {
|
|
1062
1326
|
await sleep(retryAfter);
|
|
1063
1327
|
} else {
|
|
1064
|
-
const
|
|
1065
|
-
await sleep(
|
|
1328
|
+
const timeout2 = baseTimeout + Math.random() * jitter;
|
|
1329
|
+
await sleep(timeout2);
|
|
1066
1330
|
}
|
|
1067
1331
|
return true;
|
|
1068
1332
|
};
|
|
1069
1333
|
};
|
|
1334
|
+
var getFileMimeType = (filename) => [
|
|
1335
|
+
".js",
|
|
1336
|
+
".mjs"
|
|
1337
|
+
].some((codeExtension) => filename.endsWith(codeExtension)) ? "application/javascript+module" : filename.endsWith(".wasm") ? "application/wasm" : "application/octet-stream";
|
|
1070
1338
|
export {
|
|
1071
1339
|
CLOUDFLARE_MESSAGE_MAX_BYTES,
|
|
1072
1340
|
CLOUDFLARE_RPC_MAX_BYTES,
|
|
@@ -1074,6 +1342,7 @@ export {
|
|
|
1074
1342
|
EdgeConnectionClosedError,
|
|
1075
1343
|
EdgeHttpClient,
|
|
1076
1344
|
EdgeIdentityChangedError,
|
|
1345
|
+
HttpConfig,
|
|
1077
1346
|
Protocol,
|
|
1078
1347
|
WebSocketMuxer,
|
|
1079
1348
|
createChainEdgeIdentity,
|
|
@@ -1081,9 +1350,13 @@ export {
|
|
|
1081
1350
|
createEphemeralEdgeIdentity,
|
|
1082
1351
|
createStubEdgeIdentity,
|
|
1083
1352
|
createTestHaloEdgeIdentity,
|
|
1353
|
+
encodeAuthHeader,
|
|
1084
1354
|
getTypename,
|
|
1085
1355
|
handleAuthChallenge,
|
|
1086
1356
|
protocol,
|
|
1087
|
-
toUint8Array
|
|
1357
|
+
toUint8Array,
|
|
1358
|
+
withLogging,
|
|
1359
|
+
withRetry,
|
|
1360
|
+
withRetryConfig
|
|
1088
1361
|
};
|
|
1089
1362
|
//# sourceMappingURL=index.mjs.map
|