@dxos/edge-client 0.8.4-main.84f28bd → 0.8.4-main.ae835ea
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 +527 -275
- 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 +527 -275
- 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 +22 -1
- 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.ts +172 -31
- 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 +13 -7
- 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
|
}));
|
|
@@ -774,7 +904,7 @@ var withLogging = (effect) => effect.pipe(Effect.tap((res) => log3.info("respons
|
|
|
774
904
|
status: res.status
|
|
775
905
|
}, {
|
|
776
906
|
F: __dxlog_file5,
|
|
777
|
-
L:
|
|
907
|
+
L: 64,
|
|
778
908
|
S: void 0,
|
|
779
909
|
C: (f, a) => f(...a)
|
|
780
910
|
})));
|
|
@@ -788,14 +918,21 @@ var __dxlog_file6 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/edge-http
|
|
|
788
918
|
var DEFAULT_RETRY_TIMEOUT = 1500;
|
|
789
919
|
var DEFAULT_RETRY_JITTER = 500;
|
|
790
920
|
var DEFAULT_MAX_RETRIES_COUNT = 3;
|
|
921
|
+
var WARNING_BODY_SIZE = 10 * 1024 * 1024;
|
|
791
922
|
var EdgeHttpClient = class {
|
|
923
|
+
_baseUrl;
|
|
924
|
+
_edgeIdentity;
|
|
925
|
+
/**
|
|
926
|
+
* Auth header is cached until receiving the next 401 from EDGE, at which point it gets refreshed.
|
|
927
|
+
*/
|
|
928
|
+
_authHeader;
|
|
792
929
|
constructor(baseUrl) {
|
|
793
930
|
this._baseUrl = getEdgeUrlWithProtocol(baseUrl, "http");
|
|
794
931
|
log4("created", {
|
|
795
932
|
url: this._baseUrl
|
|
796
933
|
}, {
|
|
797
934
|
F: __dxlog_file6,
|
|
798
|
-
L:
|
|
935
|
+
L: 101,
|
|
799
936
|
S: this,
|
|
800
937
|
C: (f, a) => f(...a)
|
|
801
938
|
});
|
|
@@ -927,6 +1064,18 @@ var EdgeHttpClient = class {
|
|
|
927
1064
|
// Functions
|
|
928
1065
|
//
|
|
929
1066
|
async uploadFunction(pathParts, body, args) {
|
|
1067
|
+
const formData = new FormData();
|
|
1068
|
+
formData.append("name", body.name ?? "");
|
|
1069
|
+
formData.append("version", body.version);
|
|
1070
|
+
formData.append("ownerPublicKey", body.ownerPublicKey);
|
|
1071
|
+
formData.append("entryPoint", body.entryPoint);
|
|
1072
|
+
for (const [filename, content] of Object.entries(body.assets)) {
|
|
1073
|
+
formData.append("assets", new Blob([
|
|
1074
|
+
content
|
|
1075
|
+
], {
|
|
1076
|
+
type: getFileMimeType(filename)
|
|
1077
|
+
}), filename);
|
|
1078
|
+
}
|
|
930
1079
|
const path = [
|
|
931
1080
|
"functions",
|
|
932
1081
|
...pathParts.functionId ? [
|
|
@@ -935,8 +1084,36 @@ var EdgeHttpClient = class {
|
|
|
935
1084
|
].join("/");
|
|
936
1085
|
return this._call(new URL(path, this.baseUrl), {
|
|
937
1086
|
...args,
|
|
938
|
-
body,
|
|
939
|
-
method: "PUT"
|
|
1087
|
+
body: formData,
|
|
1088
|
+
method: "PUT",
|
|
1089
|
+
json: false
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1092
|
+
async listFunctions(args) {
|
|
1093
|
+
return this._call(new URL("/functions", this.baseUrl), {
|
|
1094
|
+
...args,
|
|
1095
|
+
method: "GET"
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
async invokeFunction(params, input, args) {
|
|
1099
|
+
const url = new URL(`/functions/${params.functionId}`, this.baseUrl);
|
|
1100
|
+
if (params.version) {
|
|
1101
|
+
url.searchParams.set("version", params.version);
|
|
1102
|
+
}
|
|
1103
|
+
if (params.spaceId) {
|
|
1104
|
+
url.searchParams.set("spaceId", params.spaceId.toString());
|
|
1105
|
+
}
|
|
1106
|
+
if (params.cpuTimeLimit) {
|
|
1107
|
+
url.searchParams.set("cpuTimeLimit", params.cpuTimeLimit.toString());
|
|
1108
|
+
}
|
|
1109
|
+
if (params.subrequestsLimit) {
|
|
1110
|
+
url.searchParams.set("subrequestsLimit", params.subrequestsLimit.toString());
|
|
1111
|
+
}
|
|
1112
|
+
return this._call(url, {
|
|
1113
|
+
...args,
|
|
1114
|
+
body: input,
|
|
1115
|
+
method: "POST",
|
|
1116
|
+
rawResponse: true
|
|
940
1117
|
});
|
|
941
1118
|
}
|
|
942
1119
|
//
|
|
@@ -950,71 +1127,118 @@ var EdgeHttpClient = class {
|
|
|
950
1127
|
});
|
|
951
1128
|
}
|
|
952
1129
|
//
|
|
1130
|
+
// Triggers
|
|
1131
|
+
//
|
|
1132
|
+
async getCronTriggers(spaceId) {
|
|
1133
|
+
return this._call(new URL(`/test/functions/${spaceId}/triggers/crons`, this.baseUrl), {
|
|
1134
|
+
method: "GET"
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
//
|
|
1138
|
+
// Import/Export space.
|
|
1139
|
+
//
|
|
1140
|
+
async importBundle(spaceId, body, args) {
|
|
1141
|
+
return this._call(new URL(`/spaces/${spaceId}/import`, this.baseUrl), {
|
|
1142
|
+
...args,
|
|
1143
|
+
body,
|
|
1144
|
+
method: "PUT"
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
async exportBundle(spaceId, body, args) {
|
|
1148
|
+
return this._call(new URL(`/spaces/${spaceId}/export`, this.baseUrl), {
|
|
1149
|
+
...args,
|
|
1150
|
+
body,
|
|
1151
|
+
method: "POST"
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
//
|
|
953
1155
|
// Internal
|
|
954
1156
|
//
|
|
955
1157
|
async _fetch(url, args) {
|
|
956
|
-
return pipe(HttpClient.get(url), withLogging, withRetryConfig, Effect2.provide(FetchHttpClient.layer), Effect2.provide(HttpConfig.default), Effect2.withSpan("EdgeHttpClient"), Effect2.runPromise);
|
|
1158
|
+
return Function.pipe(HttpClient.get(url), withLogging, withRetryConfig, Effect2.provide(FetchHttpClient.layer), Effect2.provide(HttpConfig.default), Effect2.withSpan("EdgeHttpClient"), Effect2.runPromise);
|
|
957
1159
|
}
|
|
958
1160
|
// TODO(burdon): Refactor with effect (see edge-http-client.test.ts).
|
|
959
1161
|
async _call(url, args) {
|
|
960
1162
|
const shouldRetry = createRetryHandler(args);
|
|
961
|
-
const requestContext = args.context ??
|
|
1163
|
+
const requestContext = args.context ?? Context3.default(void 0, {
|
|
962
1164
|
F: __dxlog_file6,
|
|
963
|
-
L:
|
|
1165
|
+
L: 393
|
|
964
1166
|
});
|
|
965
1167
|
log4("fetch", {
|
|
966
1168
|
url,
|
|
967
1169
|
request: args.body
|
|
968
1170
|
}, {
|
|
969
1171
|
F: __dxlog_file6,
|
|
970
|
-
L:
|
|
1172
|
+
L: 394,
|
|
971
1173
|
S: this,
|
|
972
1174
|
C: (f, a) => f(...a)
|
|
973
1175
|
});
|
|
974
1176
|
let handledAuth = false;
|
|
975
1177
|
while (true) {
|
|
976
|
-
let processingError;
|
|
977
|
-
let retryAfterHeaderValue = Number.NaN;
|
|
1178
|
+
let processingError = void 0;
|
|
978
1179
|
try {
|
|
979
1180
|
const request = createRequest(args, this._authHeader);
|
|
980
1181
|
const response = await fetch(url, request);
|
|
981
|
-
|
|
1182
|
+
const body = response.headers.get("Content-Type") === "application/json" ? await response.clone().json() : void 0;
|
|
982
1183
|
if (response.ok) {
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
return body.data;
|
|
1184
|
+
if (args.rawResponse) {
|
|
1185
|
+
return body;
|
|
986
1186
|
}
|
|
987
|
-
|
|
988
|
-
url,
|
|
989
|
-
body
|
|
990
|
-
}, {
|
|
1187
|
+
invariant4(body, "Expected body to be present", {
|
|
991
1188
|
F: __dxlog_file6,
|
|
992
|
-
L:
|
|
1189
|
+
L: 409,
|
|
993
1190
|
S: this,
|
|
994
|
-
|
|
1191
|
+
A: [
|
|
1192
|
+
"body",
|
|
1193
|
+
"'Expected body to be present'"
|
|
1194
|
+
]
|
|
995
1195
|
});
|
|
996
|
-
if (
|
|
997
|
-
|
|
998
|
-
}
|
|
999
|
-
|
|
1196
|
+
if (!("success" in body)) {
|
|
1197
|
+
return body;
|
|
1198
|
+
}
|
|
1199
|
+
if (body.success) {
|
|
1200
|
+
return body.data;
|
|
1000
1201
|
}
|
|
1001
1202
|
} else if (response.status === 401 && !handledAuth) {
|
|
1002
1203
|
this._authHeader = await this._handleUnauthorized(response);
|
|
1003
1204
|
handledAuth = true;
|
|
1004
1205
|
continue;
|
|
1206
|
+
}
|
|
1207
|
+
invariant4(!body?.success, "Expected body to not be a failure response or undefined.", {
|
|
1208
|
+
F: __dxlog_file6,
|
|
1209
|
+
L: 422,
|
|
1210
|
+
S: this,
|
|
1211
|
+
A: [
|
|
1212
|
+
"!body?.success",
|
|
1213
|
+
"'Expected body to not be a failure response or undefined.'"
|
|
1214
|
+
]
|
|
1215
|
+
});
|
|
1216
|
+
if (body?.errorData?.type === "auth_challenge" && typeof body?.errorData?.challenge === "string") {
|
|
1217
|
+
processingError = new EdgeAuthChallengeError(body.errorData.challenge, body.errorData);
|
|
1218
|
+
} else if (body?.success === false) {
|
|
1219
|
+
processingError = EdgeCallFailedError.fromUnsuccessfulResponse(response, body);
|
|
1005
1220
|
} else {
|
|
1006
|
-
|
|
1221
|
+
invariant4(!response.ok, "Expected response to not be ok.", {
|
|
1222
|
+
F: __dxlog_file6,
|
|
1223
|
+
L: 429,
|
|
1224
|
+
S: this,
|
|
1225
|
+
A: [
|
|
1226
|
+
"!response.ok",
|
|
1227
|
+
"'Expected response to not be ok.'"
|
|
1228
|
+
]
|
|
1229
|
+
});
|
|
1230
|
+
processingError = await EdgeCallFailedError.fromHttpFailure(response);
|
|
1007
1231
|
}
|
|
1008
1232
|
} catch (error) {
|
|
1009
1233
|
processingError = EdgeCallFailedError.fromProcessingFailureCause(error);
|
|
1010
1234
|
}
|
|
1011
|
-
if (processingError
|
|
1235
|
+
if (processingError?.isRetryable && await shouldRetry(requestContext, processingError.retryAfterMs)) {
|
|
1012
1236
|
log4("retrying edge request", {
|
|
1013
1237
|
url,
|
|
1014
1238
|
processingError
|
|
1015
1239
|
}, {
|
|
1016
1240
|
F: __dxlog_file6,
|
|
1017
|
-
L:
|
|
1241
|
+
L: 437,
|
|
1018
1242
|
S: this,
|
|
1019
1243
|
C: (f, a) => f(...a)
|
|
1020
1244
|
});
|
|
@@ -1027,33 +1251,52 @@ var EdgeHttpClient = class {
|
|
|
1027
1251
|
if (!this._edgeIdentity) {
|
|
1028
1252
|
log4.warn("unauthorized response received before identity was set", void 0, {
|
|
1029
1253
|
F: __dxlog_file6,
|
|
1030
|
-
L:
|
|
1254
|
+
L: 446,
|
|
1031
1255
|
S: this,
|
|
1032
1256
|
C: (f, a) => f(...a)
|
|
1033
1257
|
});
|
|
1034
|
-
throw EdgeCallFailedError.fromHttpFailure(response);
|
|
1258
|
+
throw await EdgeCallFailedError.fromHttpFailure(response);
|
|
1035
1259
|
}
|
|
1036
1260
|
const challenge = await handleAuthChallenge(response, this._edgeIdentity);
|
|
1037
1261
|
return encodeAuthHeader(challenge);
|
|
1038
1262
|
}
|
|
1039
1263
|
};
|
|
1040
|
-
var createRequest = ({ method, body }, authHeader) => {
|
|
1264
|
+
var createRequest = ({ method, body, json = true }, authHeader) => {
|
|
1265
|
+
let requestBody;
|
|
1266
|
+
const headers = {};
|
|
1267
|
+
if (json) {
|
|
1268
|
+
requestBody = body && JSON.stringify(body);
|
|
1269
|
+
headers["Content-Type"] = "application/json";
|
|
1270
|
+
} else {
|
|
1271
|
+
requestBody = body;
|
|
1272
|
+
}
|
|
1273
|
+
if (typeof requestBody === "string" && requestBody.length > WARNING_BODY_SIZE) {
|
|
1274
|
+
log4.warn("Request with large body", {
|
|
1275
|
+
bodySize: requestBody.length
|
|
1276
|
+
}, {
|
|
1277
|
+
F: __dxlog_file6,
|
|
1278
|
+
L: 470,
|
|
1279
|
+
S: void 0,
|
|
1280
|
+
C: (f, a) => f(...a)
|
|
1281
|
+
});
|
|
1282
|
+
}
|
|
1283
|
+
if (authHeader) {
|
|
1284
|
+
headers["Authorization"] = authHeader;
|
|
1285
|
+
}
|
|
1041
1286
|
return {
|
|
1042
1287
|
method,
|
|
1043
|
-
body:
|
|
1044
|
-
headers
|
|
1045
|
-
Authorization: authHeader
|
|
1046
|
-
} : void 0
|
|
1288
|
+
body: requestBody,
|
|
1289
|
+
headers
|
|
1047
1290
|
};
|
|
1048
1291
|
};
|
|
1049
|
-
var createRetryHandler = ({ retry }) => {
|
|
1050
|
-
if (!
|
|
1292
|
+
var createRetryHandler = ({ retry: retry2 }) => {
|
|
1293
|
+
if (!retry2 || retry2.count < 1) {
|
|
1051
1294
|
return async () => false;
|
|
1052
1295
|
}
|
|
1053
1296
|
let retries = 0;
|
|
1054
|
-
const maxRetries =
|
|
1055
|
-
const baseTimeout =
|
|
1056
|
-
const jitter =
|
|
1297
|
+
const maxRetries = retry2.count ?? DEFAULT_MAX_RETRIES_COUNT;
|
|
1298
|
+
const baseTimeout = retry2.timeout ?? DEFAULT_RETRY_TIMEOUT;
|
|
1299
|
+
const jitter = retry2.jitter ?? DEFAULT_RETRY_JITTER;
|
|
1057
1300
|
return async (ctx, retryAfter) => {
|
|
1058
1301
|
if (++retries > maxRetries || ctx.disposed) {
|
|
1059
1302
|
return false;
|
|
@@ -1061,12 +1304,16 @@ var createRetryHandler = ({ retry }) => {
|
|
|
1061
1304
|
if (retryAfter) {
|
|
1062
1305
|
await sleep(retryAfter);
|
|
1063
1306
|
} else {
|
|
1064
|
-
const
|
|
1065
|
-
await sleep(
|
|
1307
|
+
const timeout2 = baseTimeout + Math.random() * jitter;
|
|
1308
|
+
await sleep(timeout2);
|
|
1066
1309
|
}
|
|
1067
1310
|
return true;
|
|
1068
1311
|
};
|
|
1069
1312
|
};
|
|
1313
|
+
var getFileMimeType = (filename) => [
|
|
1314
|
+
".js",
|
|
1315
|
+
".mjs"
|
|
1316
|
+
].some((codeExtension) => filename.endsWith(codeExtension)) ? "application/javascript+module" : filename.endsWith(".wasm") ? "application/wasm" : "application/octet-stream";
|
|
1070
1317
|
export {
|
|
1071
1318
|
CLOUDFLARE_MESSAGE_MAX_BYTES,
|
|
1072
1319
|
CLOUDFLARE_RPC_MAX_BYTES,
|
|
@@ -1074,6 +1321,7 @@ export {
|
|
|
1074
1321
|
EdgeConnectionClosedError,
|
|
1075
1322
|
EdgeHttpClient,
|
|
1076
1323
|
EdgeIdentityChangedError,
|
|
1324
|
+
HttpConfig,
|
|
1077
1325
|
Protocol,
|
|
1078
1326
|
WebSocketMuxer,
|
|
1079
1327
|
createChainEdgeIdentity,
|
|
@@ -1081,9 +1329,13 @@ export {
|
|
|
1081
1329
|
createEphemeralEdgeIdentity,
|
|
1082
1330
|
createStubEdgeIdentity,
|
|
1083
1331
|
createTestHaloEdgeIdentity,
|
|
1332
|
+
encodeAuthHeader,
|
|
1084
1333
|
getTypename,
|
|
1085
1334
|
handleAuthChallenge,
|
|
1086
1335
|
protocol,
|
|
1087
|
-
toUint8Array
|
|
1336
|
+
toUint8Array,
|
|
1337
|
+
withLogging,
|
|
1338
|
+
withRetry,
|
|
1339
|
+
withRetryConfig
|
|
1088
1340
|
};
|
|
1089
1341
|
//# sourceMappingURL=index.mjs.map
|