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