@dxos/edge-client 0.8.4-main.84f28bd → 0.8.4-main.937b3ca
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 +572 -287
- 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 +572 -287
- 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 +29 -3
- package/dist/types/src/edge-http-client.d.ts.map +1 -1
- package/dist/types/src/edge-ws-connection.d.ts +20 -0
- package/dist/types/src/edge-ws-connection.d.ts.map +1 -1
- package/dist/types/src/edge-ws-muxer.d.ts.map +1 -1
- package/dist/types/src/http-client.d.ts +10 -7
- package/dist/types/src/http-client.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +4 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/testing/test-utils.d.ts +3 -3
- package/dist/types/src/testing/test-utils.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +25 -17
- package/src/edge-client.test.ts +4 -4
- package/src/edge-client.ts +73 -42
- package/src/edge-http-client.test.ts +1 -1
- package/src/edge-http-client.ts +202 -38
- package/src/edge-ws-connection.ts +129 -8
- package/src/edge-ws-muxer.ts +1 -1
- package/src/http-client.test.ts +11 -8
- package/src/http-client.ts +18 -8
- package/src/index.ts +4 -3
- package/src/testing/test-utils.ts +8 -8
- 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,39 @@ 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 { runAndForwardErrors } from "@dxos/effect";
|
|
872
|
+
import { invariant as invariant4 } from "@dxos/invariant";
|
|
744
873
|
import { log as log4 } from "@dxos/log";
|
|
745
874
|
import { EdgeAuthChallengeError, EdgeCallFailedError } from "@dxos/protocols";
|
|
746
875
|
import { createUrl } from "@dxos/util";
|
|
747
876
|
|
|
748
877
|
// src/http-client.ts
|
|
749
|
-
import
|
|
878
|
+
import * as Context2 from "effect/Context";
|
|
879
|
+
import * as Duration from "effect/Duration";
|
|
880
|
+
import * as Effect from "effect/Effect";
|
|
881
|
+
import * as Layer from "effect/Layer";
|
|
882
|
+
import * as Schedule from "effect/Schedule";
|
|
750
883
|
import { log as log3 } from "@dxos/log";
|
|
751
884
|
var __dxlog_file5 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/http-client.ts";
|
|
752
885
|
var HttpConfig = class _HttpConfig extends Context2.Tag("HttpConfig")() {
|
|
753
|
-
static {
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
});
|
|
759
|
-
}
|
|
886
|
+
static default = Layer.succeed(_HttpConfig, {
|
|
887
|
+
timeout: Duration.millis(1e3),
|
|
888
|
+
retryTimes: 3,
|
|
889
|
+
retryBaseDelay: Duration.millis(1e3)
|
|
890
|
+
});
|
|
760
891
|
};
|
|
761
|
-
var withRetry = (effect, { timeout = Duration.millis(1e3), retryBaseDelay = Duration.millis(1e3), retryTimes = 3 } = {}) => {
|
|
892
|
+
var withRetry = (effect, { timeout: timeout2 = Duration.millis(1e3), retryBaseDelay = Duration.millis(1e3), retryTimes = 3 } = {}) => {
|
|
762
893
|
return effect.pipe(Effect.flatMap((res) => (
|
|
763
894
|
// Treat 500 errors as retryable?
|
|
764
895
|
res.status === 500 ? Effect.fail(new Error(res.status.toString())) : res.json
|
|
765
|
-
)), Effect.timeout(
|
|
896
|
+
)), Effect.timeout(timeout2), Effect.retry({
|
|
766
897
|
schedule: Schedule.exponential(retryBaseDelay).pipe(Schedule.jittered),
|
|
767
898
|
times: retryTimes
|
|
768
899
|
}));
|
|
@@ -771,14 +902,16 @@ var withRetryConfig = (effect) => Effect.gen(function* () {
|
|
|
771
902
|
const config = yield* HttpConfig;
|
|
772
903
|
return yield* withRetry(effect, config);
|
|
773
904
|
});
|
|
774
|
-
var withLogging = (effect) => effect.pipe(Effect.tap((res) =>
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
905
|
+
var withLogging = (effect) => effect.pipe(Effect.tap((res) => {
|
|
906
|
+
log3.info("response", {
|
|
907
|
+
status: res.status
|
|
908
|
+
}, {
|
|
909
|
+
F: __dxlog_file5,
|
|
910
|
+
L: 66,
|
|
911
|
+
S: void 0,
|
|
912
|
+
C: (f, a) => f(...a)
|
|
913
|
+
});
|
|
914
|
+
}));
|
|
782
915
|
var encodeAuthHeader = (challenge) => {
|
|
783
916
|
const encodedChallenge = Buffer.from(challenge).toString("base64");
|
|
784
917
|
return `VerifiablePresentation pb;base64,${encodedChallenge}`;
|
|
@@ -789,14 +922,21 @@ var __dxlog_file6 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/edge-http
|
|
|
789
922
|
var DEFAULT_RETRY_TIMEOUT = 1500;
|
|
790
923
|
var DEFAULT_RETRY_JITTER = 500;
|
|
791
924
|
var DEFAULT_MAX_RETRIES_COUNT = 3;
|
|
925
|
+
var WARNING_BODY_SIZE = 10 * 1024 * 1024;
|
|
792
926
|
var EdgeHttpClient = class {
|
|
927
|
+
_baseUrl;
|
|
928
|
+
_edgeIdentity;
|
|
929
|
+
/**
|
|
930
|
+
* Auth header is cached until receiving the next 401 from EDGE, at which point it gets refreshed.
|
|
931
|
+
*/
|
|
932
|
+
_authHeader;
|
|
793
933
|
constructor(baseUrl) {
|
|
794
934
|
this._baseUrl = getEdgeUrlWithProtocol(baseUrl, "http");
|
|
795
935
|
log4("created", {
|
|
796
936
|
url: this._baseUrl
|
|
797
937
|
}, {
|
|
798
938
|
F: __dxlog_file6,
|
|
799
|
-
L:
|
|
939
|
+
L: 107,
|
|
800
940
|
S: this,
|
|
801
941
|
C: (f, a) => f(...a)
|
|
802
942
|
});
|
|
@@ -816,7 +956,8 @@ var EdgeHttpClient = class {
|
|
|
816
956
|
async getStatus(args) {
|
|
817
957
|
return this._call(new URL("/status", this.baseUrl), {
|
|
818
958
|
...args,
|
|
819
|
-
method: "GET"
|
|
959
|
+
method: "GET",
|
|
960
|
+
auth: true
|
|
820
961
|
});
|
|
821
962
|
}
|
|
822
963
|
//
|
|
@@ -895,7 +1036,16 @@ var EdgeHttpClient = class {
|
|
|
895
1036
|
// Queues
|
|
896
1037
|
//
|
|
897
1038
|
async queryQueue(subspaceTag, spaceId, query, args) {
|
|
898
|
-
const
|
|
1039
|
+
const queueId = query.queueIds?.[0];
|
|
1040
|
+
invariant4(queueId, "queueId required", {
|
|
1041
|
+
F: __dxlog_file6,
|
|
1042
|
+
L: 216,
|
|
1043
|
+
S: this,
|
|
1044
|
+
A: [
|
|
1045
|
+
"queueId",
|
|
1046
|
+
"'queueId required'"
|
|
1047
|
+
]
|
|
1048
|
+
});
|
|
899
1049
|
return this._call(createUrl(new URL(`/spaces/${subspaceTag}/${spaceId}/queue/${queueId}/query`, this.baseUrl), {
|
|
900
1050
|
after: query.after,
|
|
901
1051
|
before: query.before,
|
|
@@ -928,6 +1078,19 @@ var EdgeHttpClient = class {
|
|
|
928
1078
|
// Functions
|
|
929
1079
|
//
|
|
930
1080
|
async uploadFunction(pathParts, body, args) {
|
|
1081
|
+
const formData = new FormData();
|
|
1082
|
+
formData.append("name", body.name ?? "");
|
|
1083
|
+
formData.append("version", body.version);
|
|
1084
|
+
formData.append("ownerPublicKey", body.ownerPublicKey);
|
|
1085
|
+
formData.append("entryPoint", body.entryPoint);
|
|
1086
|
+
body.runtime && formData.append("runtime", body.runtime);
|
|
1087
|
+
for (const [filename, content] of Object.entries(body.assets)) {
|
|
1088
|
+
formData.append("assets", new Blob([
|
|
1089
|
+
content
|
|
1090
|
+
], {
|
|
1091
|
+
type: getFileMimeType(filename)
|
|
1092
|
+
}), filename);
|
|
1093
|
+
}
|
|
931
1094
|
const path = [
|
|
932
1095
|
"functions",
|
|
933
1096
|
...pathParts.functionId ? [
|
|
@@ -936,8 +1099,35 @@ var EdgeHttpClient = class {
|
|
|
936
1099
|
].join("/");
|
|
937
1100
|
return this._call(new URL(path, this.baseUrl), {
|
|
938
1101
|
...args,
|
|
939
|
-
body,
|
|
940
|
-
method: "PUT"
|
|
1102
|
+
body: formData,
|
|
1103
|
+
method: "PUT",
|
|
1104
|
+
json: false
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
async listFunctions(args) {
|
|
1108
|
+
return this._call(new URL("/functions", this.baseUrl), {
|
|
1109
|
+
...args,
|
|
1110
|
+
method: "GET"
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
async invokeFunction(params, input, args) {
|
|
1114
|
+
const url = new URL(`/functions/${params.functionId}`, this.baseUrl);
|
|
1115
|
+
if (params.version) {
|
|
1116
|
+
url.searchParams.set("version", params.version);
|
|
1117
|
+
}
|
|
1118
|
+
if (params.spaceId) {
|
|
1119
|
+
url.searchParams.set("spaceId", params.spaceId.toString());
|
|
1120
|
+
}
|
|
1121
|
+
if (params.cpuTimeLimit) {
|
|
1122
|
+
url.searchParams.set("cpuTimeLimit", params.cpuTimeLimit.toString());
|
|
1123
|
+
}
|
|
1124
|
+
if (params.subrequestsLimit) {
|
|
1125
|
+
url.searchParams.set("subrequestsLimit", params.subrequestsLimit.toString());
|
|
1126
|
+
}
|
|
1127
|
+
return this._call(url, {
|
|
1128
|
+
...args,
|
|
1129
|
+
body: input,
|
|
1130
|
+
method: "POST"
|
|
941
1131
|
});
|
|
942
1132
|
}
|
|
943
1133
|
//
|
|
@@ -951,71 +1141,138 @@ var EdgeHttpClient = class {
|
|
|
951
1141
|
});
|
|
952
1142
|
}
|
|
953
1143
|
//
|
|
1144
|
+
// Triggers
|
|
1145
|
+
//
|
|
1146
|
+
async getCronTriggers(spaceId) {
|
|
1147
|
+
return this._call(new URL(`/test/functions/${spaceId}/triggers/crons`, this.baseUrl), {
|
|
1148
|
+
method: "GET"
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
async forceRunCronTrigger(spaceId, triggerId) {
|
|
1152
|
+
return this._call(new URL(`/test/functions/${spaceId}/triggers/crons/${triggerId}/run`, this.baseUrl), {
|
|
1153
|
+
method: "POST"
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
//
|
|
1157
|
+
// Import/Export space.
|
|
1158
|
+
//
|
|
1159
|
+
async importBundle(spaceId, body, args) {
|
|
1160
|
+
return this._call(new URL(`/spaces/${spaceId}/import`, this.baseUrl), {
|
|
1161
|
+
...args,
|
|
1162
|
+
body,
|
|
1163
|
+
method: "PUT"
|
|
1164
|
+
});
|
|
1165
|
+
}
|
|
1166
|
+
async exportBundle(spaceId, body, args) {
|
|
1167
|
+
return this._call(new URL(`/spaces/${spaceId}/export`, this.baseUrl), {
|
|
1168
|
+
...args,
|
|
1169
|
+
body,
|
|
1170
|
+
method: "POST"
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
//
|
|
954
1174
|
// Internal
|
|
955
1175
|
//
|
|
956
|
-
async _fetch(url,
|
|
957
|
-
return pipe(HttpClient.get(url), withLogging, withRetryConfig, Effect2.provide(FetchHttpClient.layer), Effect2.provide(HttpConfig.default), Effect2.withSpan("EdgeHttpClient"),
|
|
1176
|
+
async _fetch(url, _args) {
|
|
1177
|
+
return Function.pipe(HttpClient.get(url), withLogging, withRetryConfig, Effect2.provide(FetchHttpClient.layer), Effect2.provide(HttpConfig.default), Effect2.withSpan("EdgeHttpClient"), runAndForwardErrors);
|
|
958
1178
|
}
|
|
959
1179
|
// TODO(burdon): Refactor with effect (see edge-http-client.test.ts).
|
|
960
1180
|
async _call(url, args) {
|
|
961
1181
|
const shouldRetry = createRetryHandler(args);
|
|
962
|
-
const requestContext = args.context ??
|
|
1182
|
+
const requestContext = args.context ?? Context3.default(void 0, {
|
|
963
1183
|
F: __dxlog_file6,
|
|
964
|
-
L:
|
|
1184
|
+
L: 408
|
|
965
1185
|
});
|
|
966
1186
|
log4("fetch", {
|
|
967
1187
|
url,
|
|
968
1188
|
request: args.body
|
|
969
1189
|
}, {
|
|
970
1190
|
F: __dxlog_file6,
|
|
971
|
-
L:
|
|
1191
|
+
L: 409,
|
|
972
1192
|
S: this,
|
|
973
1193
|
C: (f, a) => f(...a)
|
|
974
1194
|
});
|
|
975
1195
|
let handledAuth = false;
|
|
1196
|
+
const tryCount = 1;
|
|
976
1197
|
while (true) {
|
|
977
|
-
let processingError;
|
|
978
|
-
let retryAfterHeaderValue = Number.NaN;
|
|
1198
|
+
let processingError = void 0;
|
|
979
1199
|
try {
|
|
1200
|
+
if (!this._authHeader && args.auth) {
|
|
1201
|
+
const response2 = await fetch(new URL(`/auth`, this.baseUrl));
|
|
1202
|
+
if (response2.status === 401) {
|
|
1203
|
+
this._authHeader = await this._handleUnauthorized(response2);
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
980
1206
|
const request = createRequest(args, this._authHeader);
|
|
1207
|
+
log4("call edge", {
|
|
1208
|
+
url,
|
|
1209
|
+
tryCount,
|
|
1210
|
+
authHeader: !!this._authHeader
|
|
1211
|
+
}, {
|
|
1212
|
+
F: __dxlog_file6,
|
|
1213
|
+
L: 424,
|
|
1214
|
+
S: this,
|
|
1215
|
+
C: (f, a) => f(...a)
|
|
1216
|
+
});
|
|
981
1217
|
const response = await fetch(url, request);
|
|
982
|
-
retryAfterHeaderValue = Number(response.headers.get("Retry-After"));
|
|
983
1218
|
if (response.ok) {
|
|
984
|
-
const
|
|
985
|
-
|
|
986
|
-
return body.data;
|
|
987
|
-
}
|
|
988
|
-
log4.warn("unsuccessful edge response", {
|
|
989
|
-
url,
|
|
990
|
-
body
|
|
991
|
-
}, {
|
|
1219
|
+
const body2 = await response.clone().json();
|
|
1220
|
+
invariant4(body2, "Expected body to be present", {
|
|
992
1221
|
F: __dxlog_file6,
|
|
993
|
-
L:
|
|
1222
|
+
L: 429,
|
|
994
1223
|
S: this,
|
|
995
|
-
|
|
1224
|
+
A: [
|
|
1225
|
+
"body",
|
|
1226
|
+
"'Expected body to be present'"
|
|
1227
|
+
]
|
|
996
1228
|
});
|
|
997
|
-
if (
|
|
998
|
-
|
|
999
|
-
}
|
|
1000
|
-
|
|
1229
|
+
if (!("success" in body2)) {
|
|
1230
|
+
return body2;
|
|
1231
|
+
}
|
|
1232
|
+
if (body2.success) {
|
|
1233
|
+
return body2.data;
|
|
1001
1234
|
}
|
|
1002
1235
|
} else if (response.status === 401 && !handledAuth) {
|
|
1003
1236
|
this._authHeader = await this._handleUnauthorized(response);
|
|
1004
1237
|
handledAuth = true;
|
|
1005
1238
|
continue;
|
|
1239
|
+
}
|
|
1240
|
+
const body = response.headers.get("Content-Type") === "application/json" ? await response.clone().json() : void 0;
|
|
1241
|
+
invariant4(!body?.success, "Expected body to not be a failure response or undefined.", {
|
|
1242
|
+
F: __dxlog_file6,
|
|
1243
|
+
L: 445,
|
|
1244
|
+
S: this,
|
|
1245
|
+
A: [
|
|
1246
|
+
"!body?.success",
|
|
1247
|
+
"'Expected body to not be a failure response or undefined.'"
|
|
1248
|
+
]
|
|
1249
|
+
});
|
|
1250
|
+
if (body?.data?.type === "auth_challenge" && typeof body?.data?.challenge === "string") {
|
|
1251
|
+
processingError = new EdgeAuthChallengeError(body.data.challenge, body.data);
|
|
1252
|
+
} else if (body?.success === false) {
|
|
1253
|
+
processingError = EdgeCallFailedError.fromUnsuccessfulResponse(response, body);
|
|
1006
1254
|
} else {
|
|
1007
|
-
|
|
1255
|
+
invariant4(!response.ok, "Expected response to not be ok.", {
|
|
1256
|
+
F: __dxlog_file6,
|
|
1257
|
+
L: 452,
|
|
1258
|
+
S: this,
|
|
1259
|
+
A: [
|
|
1260
|
+
"!response.ok",
|
|
1261
|
+
"'Expected response to not be ok.'"
|
|
1262
|
+
]
|
|
1263
|
+
});
|
|
1264
|
+
processingError = await EdgeCallFailedError.fromHttpFailure(response);
|
|
1008
1265
|
}
|
|
1009
1266
|
} catch (error) {
|
|
1010
1267
|
processingError = EdgeCallFailedError.fromProcessingFailureCause(error);
|
|
1011
1268
|
}
|
|
1012
|
-
if (processingError
|
|
1013
|
-
log4("retrying edge request", {
|
|
1269
|
+
if (processingError?.isRetryable && await shouldRetry(requestContext, processingError.retryAfterMs)) {
|
|
1270
|
+
log4.verbose("retrying edge request", {
|
|
1014
1271
|
url,
|
|
1015
1272
|
processingError
|
|
1016
1273
|
}, {
|
|
1017
1274
|
F: __dxlog_file6,
|
|
1018
|
-
L:
|
|
1275
|
+
L: 460,
|
|
1019
1276
|
S: this,
|
|
1020
1277
|
C: (f, a) => f(...a)
|
|
1021
1278
|
});
|
|
@@ -1028,33 +1285,52 @@ var EdgeHttpClient = class {
|
|
|
1028
1285
|
if (!this._edgeIdentity) {
|
|
1029
1286
|
log4.warn("unauthorized response received before identity was set", void 0, {
|
|
1030
1287
|
F: __dxlog_file6,
|
|
1031
|
-
L:
|
|
1288
|
+
L: 469,
|
|
1032
1289
|
S: this,
|
|
1033
1290
|
C: (f, a) => f(...a)
|
|
1034
1291
|
});
|
|
1035
|
-
throw EdgeCallFailedError.fromHttpFailure(response);
|
|
1292
|
+
throw await EdgeCallFailedError.fromHttpFailure(response);
|
|
1036
1293
|
}
|
|
1037
1294
|
const challenge = await handleAuthChallenge(response, this._edgeIdentity);
|
|
1038
1295
|
return encodeAuthHeader(challenge);
|
|
1039
1296
|
}
|
|
1040
1297
|
};
|
|
1041
|
-
var createRequest = ({ method, body }, authHeader) => {
|
|
1298
|
+
var createRequest = ({ method, body, json = true }, authHeader) => {
|
|
1299
|
+
let requestBody;
|
|
1300
|
+
const headers = {};
|
|
1301
|
+
if (json) {
|
|
1302
|
+
requestBody = body && JSON.stringify(body);
|
|
1303
|
+
headers["Content-Type"] = "application/json";
|
|
1304
|
+
} else {
|
|
1305
|
+
requestBody = body;
|
|
1306
|
+
}
|
|
1307
|
+
if (typeof requestBody === "string" && requestBody.length > WARNING_BODY_SIZE) {
|
|
1308
|
+
log4.warn("Request with large body", {
|
|
1309
|
+
bodySize: requestBody.length
|
|
1310
|
+
}, {
|
|
1311
|
+
F: __dxlog_file6,
|
|
1312
|
+
L: 493,
|
|
1313
|
+
S: void 0,
|
|
1314
|
+
C: (f, a) => f(...a)
|
|
1315
|
+
});
|
|
1316
|
+
}
|
|
1317
|
+
if (authHeader) {
|
|
1318
|
+
headers["Authorization"] = authHeader;
|
|
1319
|
+
}
|
|
1042
1320
|
return {
|
|
1043
1321
|
method,
|
|
1044
|
-
body:
|
|
1045
|
-
headers
|
|
1046
|
-
Authorization: authHeader
|
|
1047
|
-
} : void 0
|
|
1322
|
+
body: requestBody,
|
|
1323
|
+
headers
|
|
1048
1324
|
};
|
|
1049
1325
|
};
|
|
1050
|
-
var createRetryHandler = ({ retry }) => {
|
|
1051
|
-
if (!
|
|
1326
|
+
var createRetryHandler = ({ retry: retry2 }) => {
|
|
1327
|
+
if (!retry2 || retry2.count < 1) {
|
|
1052
1328
|
return async () => false;
|
|
1053
1329
|
}
|
|
1054
1330
|
let retries = 0;
|
|
1055
|
-
const maxRetries =
|
|
1056
|
-
const baseTimeout =
|
|
1057
|
-
const jitter =
|
|
1331
|
+
const maxRetries = retry2.count ?? DEFAULT_MAX_RETRIES_COUNT;
|
|
1332
|
+
const baseTimeout = retry2.timeout ?? DEFAULT_RETRY_TIMEOUT;
|
|
1333
|
+
const jitter = retry2.jitter ?? DEFAULT_RETRY_JITTER;
|
|
1058
1334
|
return async (ctx, retryAfter) => {
|
|
1059
1335
|
if (++retries > maxRetries || ctx.disposed) {
|
|
1060
1336
|
return false;
|
|
@@ -1062,12 +1338,16 @@ var createRetryHandler = ({ retry }) => {
|
|
|
1062
1338
|
if (retryAfter) {
|
|
1063
1339
|
await sleep(retryAfter);
|
|
1064
1340
|
} else {
|
|
1065
|
-
const
|
|
1066
|
-
await sleep(
|
|
1341
|
+
const timeout2 = baseTimeout + Math.random() * jitter;
|
|
1342
|
+
await sleep(timeout2);
|
|
1067
1343
|
}
|
|
1068
1344
|
return true;
|
|
1069
1345
|
};
|
|
1070
1346
|
};
|
|
1347
|
+
var getFileMimeType = (filename) => [
|
|
1348
|
+
".js",
|
|
1349
|
+
".mjs"
|
|
1350
|
+
].some((codeExtension) => filename.endsWith(codeExtension)) ? "application/javascript+module" : filename.endsWith(".wasm") ? "application/wasm" : "application/octet-stream";
|
|
1071
1351
|
export {
|
|
1072
1352
|
CLOUDFLARE_MESSAGE_MAX_BYTES,
|
|
1073
1353
|
CLOUDFLARE_RPC_MAX_BYTES,
|
|
@@ -1075,6 +1355,7 @@ export {
|
|
|
1075
1355
|
EdgeConnectionClosedError,
|
|
1076
1356
|
EdgeHttpClient,
|
|
1077
1357
|
EdgeIdentityChangedError,
|
|
1358
|
+
HttpConfig,
|
|
1078
1359
|
Protocol,
|
|
1079
1360
|
WebSocketMuxer,
|
|
1080
1361
|
createChainEdgeIdentity,
|
|
@@ -1082,9 +1363,13 @@ export {
|
|
|
1082
1363
|
createEphemeralEdgeIdentity,
|
|
1083
1364
|
createStubEdgeIdentity,
|
|
1084
1365
|
createTestHaloEdgeIdentity,
|
|
1366
|
+
encodeAuthHeader,
|
|
1085
1367
|
getTypename,
|
|
1086
1368
|
handleAuthChallenge,
|
|
1087
1369
|
protocol,
|
|
1088
|
-
toUint8Array
|
|
1370
|
+
toUint8Array,
|
|
1371
|
+
withLogging,
|
|
1372
|
+
withRetry,
|
|
1373
|
+
withRetryConfig
|
|
1089
1374
|
};
|
|
1090
1375
|
//# sourceMappingURL=index.mjs.map
|