@dxos/edge-client 0.13.0 → 0.14.0
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/chunk-edge-http-client.mjs +47 -24
- package/dist/lib/chunk-edge-http-client.mjs.map +1 -1
- package/dist/lib/index.mjs +60 -39
- package/dist/lib/index.mjs.map +1 -1
- package/dist/types/src/auth-challenge.d.ts +2 -1
- package/dist/types/src/auth-challenge.d.ts.map +1 -1
- package/dist/types/src/base-http-client.d.ts +1 -1
- package/dist/types/src/base-http-client.d.ts.map +1 -1
- package/dist/types/src/client-version.d.ts +15 -0
- package/dist/types/src/client-version.d.ts.map +1 -0
- package/dist/types/src/edge-client.d.ts.map +1 -1
- package/dist/types/src/edge-http-client.d.ts.map +1 -1
- package/dist/types/src/edge-ws-connection.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +16 -16
- package/src/auth-challenge.test.ts +17 -2
- package/src/auth-challenge.ts +9 -2
- package/src/base-http-client.ts +15 -2
- package/src/client-version.ts +44 -0
- package/src/edge-client.test.ts +28 -2
- package/src/edge-client.ts +25 -3
- package/src/edge-http-client.test.ts +43 -0
- package/src/edge-http-client.ts +1 -4
- package/src/edge-ws-connection.ts +3 -4
|
@@ -10,7 +10,7 @@ import * as Schema from "effect/Schema";
|
|
|
10
10
|
import * as EffectEx from "@dxos/effect/EffectEx";
|
|
11
11
|
import { invariant } from "@dxos/invariant";
|
|
12
12
|
import { log } from "@dxos/log";
|
|
13
|
-
import { EDGE_CLIENT_TAG_HEADER, EdgeAuthChallengeError, EdgeCallFailedError, EdgeCredentialsHeaderCodec, InboxListResponseSchema, InboxSendResponseSchema } from "@dxos/protocols";
|
|
13
|
+
import { EDGE_CLIENT_TAG_HEADER, EDGE_CLIENT_TOO_OLD, EDGE_CLIENT_VERSION_HEADER, EDGE_CLIENT_VERSION_PROTOCOL_PREFIX, EdgeAuthChallengeError, EdgeCallFailedError, EdgeClientTooOldError, EdgeCredentialsHeaderCodec, InboxListResponseSchema, InboxSendResponseSchema } from "@dxos/protocols";
|
|
14
14
|
import { createUrl } from "@dxos/util";
|
|
15
15
|
import { sleep } from "@dxos/async";
|
|
16
16
|
import { TRACE_SPAN_ATTRIBUTE } from "@dxos/context";
|
|
@@ -20,6 +20,27 @@ import { PresentationSchema } from "@dxos/protocols/buf/dxos/halo/credentials_pb
|
|
|
20
20
|
import * as Duration from "effect/Duration";
|
|
21
21
|
import * as Schedule from "effect/Schedule";
|
|
22
22
|
import { BaseError } from "@dxos/errors";
|
|
23
|
+
//#endregion
|
|
24
|
+
//#region src/client-version.ts
|
|
25
|
+
/**
|
|
26
|
+
* This SDK's version, which EDGE checks against the oldest it serves. It releases in lockstep with the rest of the
|
|
27
|
+
* SDK, and Vite tree-shakes the import to this one field.
|
|
28
|
+
*/
|
|
29
|
+
var CLIENT_SDK_VERSION = "0.14.0";
|
|
30
|
+
/** Headers advertising {@link CLIENT_SDK_VERSION} on a request of the EDGE connect flow (`/auth` and its fallback). */
|
|
31
|
+
var clientVersionHeaders = () => ({ [EDGE_CLIENT_VERSION_HEADER]: CLIENT_SDK_VERSION });
|
|
32
|
+
/** The same advertisement for the WebSocket upgrade, where a browser can set no header but the subprotocol list. */
|
|
33
|
+
var clientVersionProtocol = () => `${EDGE_CLIENT_VERSION_PROTOCOL_PREFIX}${CLIENT_SDK_VERSION}`;
|
|
34
|
+
/**
|
|
35
|
+
* Throws {@link EdgeClientTooOldError} when `response` is EDGE's failure envelope refusing this SDK. Matched on the
|
|
36
|
+
* envelope's `data.type`, as any WebSocket endpoint answers a plain GET with a bare 426 "Upgrade Required".
|
|
37
|
+
*/
|
|
38
|
+
var assertClientSupported = async (response) => {
|
|
39
|
+
if (response.ok || !response.headers.get("Content-Type")?.startsWith("application/json")) return;
|
|
40
|
+
const body = await response.clone().json().catch(() => void 0);
|
|
41
|
+
if (body?.success === false && body.data?.type === EDGE_CLIENT_TOO_OLD) throw new EdgeClientTooOldError(body, response.status);
|
|
42
|
+
};
|
|
43
|
+
//#endregion
|
|
23
44
|
//#region src/auth-challenge.ts
|
|
24
45
|
var __dxlog_file$3 = "/__w/dxos/dxos/packages/core/mesh/edge-client/src/auth-challenge.ts";
|
|
25
46
|
/**
|
|
@@ -124,7 +145,7 @@ var readAuthChallenge = async (response) => {
|
|
|
124
145
|
log.verbose("auth challenge response was not JSON", { error }, {
|
|
125
146
|
"~LogMeta": "~LogMeta",
|
|
126
147
|
F: __dxlog_file$3,
|
|
127
|
-
L:
|
|
148
|
+
L: 147,
|
|
128
149
|
S: void 0
|
|
129
150
|
});
|
|
130
151
|
return;
|
|
@@ -138,17 +159,19 @@ var readAuthChallenge = async (response) => {
|
|
|
138
159
|
* console and records a routine auth failure server-side on each client boot.
|
|
139
160
|
*
|
|
140
161
|
* Returns undefined if the endpoint is unreachable or answers in neither known shape; callers fall
|
|
141
|
-
* back to the 401 path, which still works against every server.
|
|
162
|
+
* back to the 401 path, which still works against every server. Throws {@link EdgeClientTooOldError} when EDGE refuses
|
|
163
|
+
* this SDK, which no fallback can get past.
|
|
142
164
|
*/
|
|
143
165
|
var fetchAuthChallengeInfo = async (baseHttpUrl) => {
|
|
144
166
|
try {
|
|
145
|
-
const response = await fetch(new URL("/auth", baseHttpUrl));
|
|
167
|
+
const response = await fetch(new URL("/auth", baseHttpUrl), { headers: clientVersionHeaders() });
|
|
168
|
+
await assertClientSupported(response);
|
|
146
169
|
const challenge = await readAuthChallenge(response);
|
|
147
170
|
if (!challenge) {
|
|
148
171
|
log.verbose("no challenge in /auth response", { status: response.status }, {
|
|
149
172
|
"~LogMeta": "~LogMeta",
|
|
150
173
|
F: __dxlog_file$3,
|
|
151
|
-
L:
|
|
174
|
+
L: 179,
|
|
152
175
|
S: void 0
|
|
153
176
|
});
|
|
154
177
|
return;
|
|
@@ -163,10 +186,11 @@ var fetchAuthChallengeInfo = async (baseHttpUrl) => {
|
|
|
163
186
|
expiresInMs
|
|
164
187
|
};
|
|
165
188
|
} catch (error) {
|
|
189
|
+
if (error instanceof EdgeClientTooOldError) throw error;
|
|
166
190
|
log.verbose("failed to fetch auth challenge", { error }, {
|
|
167
191
|
"~LogMeta": "~LogMeta",
|
|
168
192
|
F: __dxlog_file$3,
|
|
169
|
-
L:
|
|
193
|
+
L: 196,
|
|
170
194
|
S: void 0
|
|
171
195
|
});
|
|
172
196
|
return;
|
|
@@ -206,7 +230,7 @@ var handleAuthChallenge = async (failedResponse, identity) => {
|
|
|
206
230
|
invariant(challenge !== void 0, "No auth challenge in response.", {
|
|
207
231
|
"~LogMeta": "~LogMeta",
|
|
208
232
|
F: __dxlog_file$3,
|
|
209
|
-
L:
|
|
233
|
+
L: 251,
|
|
210
234
|
S: void 0,
|
|
211
235
|
A: ["challenge !== undefined", "'No auth challenge in response.'"]
|
|
212
236
|
});
|
|
@@ -295,7 +319,7 @@ var BaseHttpClient = class {
|
|
|
295
319
|
log("created", { url: this._baseUrl }, {
|
|
296
320
|
"~LogMeta": "~LogMeta",
|
|
297
321
|
F: __dxlog_file$1,
|
|
298
|
-
L:
|
|
322
|
+
L: 97,
|
|
299
323
|
S: this
|
|
300
324
|
});
|
|
301
325
|
}
|
|
@@ -335,7 +359,7 @@ var BaseHttpClient = class {
|
|
|
335
359
|
}, {
|
|
336
360
|
"~LogMeta": "~LogMeta",
|
|
337
361
|
F: __dxlog_file$1,
|
|
338
|
-
L:
|
|
362
|
+
L: 144,
|
|
339
363
|
S: this
|
|
340
364
|
});
|
|
341
365
|
const traceHeaders = getTraceHeaders(ctx);
|
|
@@ -353,7 +377,7 @@ var BaseHttpClient = class {
|
|
|
353
377
|
}, {
|
|
354
378
|
"~LogMeta": "~LogMeta",
|
|
355
379
|
F: __dxlog_file$1,
|
|
356
|
-
L:
|
|
380
|
+
L: 162,
|
|
357
381
|
S: this
|
|
358
382
|
});
|
|
359
383
|
const response = await fetch(url, request);
|
|
@@ -373,17 +397,18 @@ var BaseHttpClient = class {
|
|
|
373
397
|
invariant(!body?.success, "Expected body to not be a failure response or undefined.", {
|
|
374
398
|
"~LogMeta": "~LogMeta",
|
|
375
399
|
F: __dxlog_file$1,
|
|
376
|
-
L:
|
|
400
|
+
L: 199,
|
|
377
401
|
S: this,
|
|
378
402
|
A: ["!body?.success", "'Expected body to not be a failure response or undefined.'"]
|
|
379
403
|
});
|
|
380
404
|
if (body?.data?.type === "auth_challenge" && typeof body?.data?.challenge === "string") processingError = new EdgeAuthChallengeError(body.data.challenge, body.data);
|
|
405
|
+
else if (body?.success === false && body.data?.type === EDGE_CLIENT_TOO_OLD) processingError = new EdgeClientTooOldError(body, response.status);
|
|
381
406
|
else if (body?.success === false) processingError = EdgeCallFailedError.fromUnsuccessfulResponse(response, body);
|
|
382
407
|
else {
|
|
383
408
|
invariant(!response.ok, "Expected response to not be ok.", {
|
|
384
409
|
"~LogMeta": "~LogMeta",
|
|
385
410
|
F: __dxlog_file$1,
|
|
386
|
-
L:
|
|
411
|
+
L: 208,
|
|
387
412
|
S: this,
|
|
388
413
|
A: ["!response.ok", "'Expected response to not be ok.'"]
|
|
389
414
|
});
|
|
@@ -398,7 +423,7 @@ var BaseHttpClient = class {
|
|
|
398
423
|
}, {
|
|
399
424
|
"~LogMeta": "~LogMeta",
|
|
400
425
|
F: __dxlog_file$1,
|
|
401
|
-
L:
|
|
426
|
+
L: 219,
|
|
402
427
|
S: this
|
|
403
428
|
});
|
|
404
429
|
else throw processingError;
|
|
@@ -422,7 +447,7 @@ var BaseHttpClient = class {
|
|
|
422
447
|
}, {
|
|
423
448
|
"~LogMeta": "~LogMeta",
|
|
424
449
|
F: __dxlog_file$1,
|
|
425
|
-
L:
|
|
450
|
+
L: 238,
|
|
426
451
|
S: this
|
|
427
452
|
});
|
|
428
453
|
const traceHeaders = getTraceHeaders(ctx);
|
|
@@ -457,7 +482,7 @@ var BaseHttpClient = class {
|
|
|
457
482
|
}, {
|
|
458
483
|
"~LogMeta": "~LogMeta",
|
|
459
484
|
F: __dxlog_file$1,
|
|
460
|
-
L:
|
|
485
|
+
L: 286,
|
|
461
486
|
S: this
|
|
462
487
|
});
|
|
463
488
|
else throw processingError;
|
|
@@ -478,10 +503,11 @@ var BaseHttpClient = class {
|
|
|
478
503
|
_prefetchAuthHeader() {
|
|
479
504
|
if (this._authPrefetch) return this._authPrefetch;
|
|
480
505
|
const prefetch = this._prefetchAuthHeaderOnce().catch((err) => {
|
|
506
|
+
if (err instanceof EdgeClientTooOldError) throw err;
|
|
481
507
|
log.verbose("auth prefetch failed; proceeding unauthenticated", { err }, {
|
|
482
508
|
"~LogMeta": "~LogMeta",
|
|
483
509
|
F: __dxlog_file$1,
|
|
484
|
-
L:
|
|
510
|
+
L: 315,
|
|
485
511
|
S: this
|
|
486
512
|
});
|
|
487
513
|
}).finally(() => {
|
|
@@ -496,7 +522,7 @@ var BaseHttpClient = class {
|
|
|
496
522
|
log.verbose("auth prefetch skipped: no identity set", void 0, {
|
|
497
523
|
"~LogMeta": "~LogMeta",
|
|
498
524
|
F: __dxlog_file$1,
|
|
499
|
-
L:
|
|
525
|
+
L: 332,
|
|
500
526
|
S: this
|
|
501
527
|
});
|
|
502
528
|
return;
|
|
@@ -529,7 +555,7 @@ var BaseHttpClient = class {
|
|
|
529
555
|
log.warn("unauthorized response received before identity was set", void 0, {
|
|
530
556
|
"~LogMeta": "~LogMeta",
|
|
531
557
|
F: __dxlog_file$1,
|
|
532
|
-
L:
|
|
558
|
+
L: 371,
|
|
533
559
|
S: this
|
|
534
560
|
});
|
|
535
561
|
throw await EdgeCallFailedError.fromHttpFailure(response);
|
|
@@ -560,7 +586,7 @@ var createRequest = ({ method, body, json = true }, authHeader, traceHeaders, cl
|
|
|
560
586
|
if (typeof requestBody === "string" && requestBody.length > WARNING_BODY_SIZE) log.warn("Request with large body", { bodySize: requestBody.length }, {
|
|
561
587
|
"~LogMeta": "~LogMeta",
|
|
562
588
|
F: __dxlog_file$1,
|
|
563
|
-
L:
|
|
589
|
+
L: 413,
|
|
564
590
|
S: void 0
|
|
565
591
|
});
|
|
566
592
|
if (authHeader) headers["Authorization"] = authHeader;
|
|
@@ -1060,10 +1086,7 @@ var EdgeHttpClient = class extends BaseHttpClient {
|
|
|
1060
1086
|
const body = method === "GET" || method === "HEAD" ? void 0 : await request.arrayBuffer();
|
|
1061
1087
|
let handledAuth = false;
|
|
1062
1088
|
while (true) {
|
|
1063
|
-
if (!this._authHeader)
|
|
1064
|
-
const authResponse = await fetch(new URL("/auth", this.baseUrl));
|
|
1065
|
-
if (authResponse.status === 401) this._authHeader = await this._handleUnauthorized(authResponse);
|
|
1066
|
-
}
|
|
1089
|
+
if (!this._authHeader) await this._prefetchAuthHeader();
|
|
1067
1090
|
const headers = new Headers(request.headers);
|
|
1068
1091
|
if (this._authHeader) headers.set("Authorization", this._authHeader);
|
|
1069
1092
|
if (this._clientTag) headers.set(EDGE_CLIENT_TAG_HEADER, this._clientTag);
|
|
@@ -1155,6 +1178,6 @@ var EdgeHttpClient = class extends BaseHttpClient {
|
|
|
1155
1178
|
};
|
|
1156
1179
|
var getFileMimeType = (filename) => [".js", ".mjs"].some((ext) => filename.endsWith(ext)) ? "application/javascript+module" : filename.endsWith(".wasm") ? "application/wasm" : "application/octet-stream";
|
|
1157
1180
|
//#endregion
|
|
1158
|
-
export { parseChallengeHeader as _, HttpConfig as a, withRetry as c, EdgeConnectionClosedError as d, EdgeIdentityChangedError as f, handleAuthChallenge as g, fetchAuthChallengeInfo as h, getEdgeUrlWithProtocol as i, withRetryConfig as l, fetchAuthChallenge as m, EdgeHttpClientService as n, encodeAuthHeader as o, authenticateViaChallengeEndpoint as p, BaseHttpClient as r, withLogging as s, EdgeHttpClient as t, EdgeClientError as u, presentCredentialsForChallenge as v, readAuthChallenge as y };
|
|
1181
|
+
export { clientVersionProtocol as S, parseChallengeHeader as _, HttpConfig as a, assertClientSupported as b, withRetry as c, EdgeConnectionClosedError as d, EdgeIdentityChangedError as f, handleAuthChallenge as g, fetchAuthChallengeInfo as h, getEdgeUrlWithProtocol as i, withRetryConfig as l, fetchAuthChallenge as m, EdgeHttpClientService as n, encodeAuthHeader as o, authenticateViaChallengeEndpoint as p, BaseHttpClient as r, withLogging as s, EdgeHttpClient as t, EdgeClientError as u, presentCredentialsForChallenge as v, clientVersionHeaders as x, readAuthChallenge as y };
|
|
1159
1182
|
|
|
1160
1183
|
//# sourceMappingURL=chunk-edge-http-client.mjs.map
|