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