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