@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.
@@ -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: 145,
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: 175,
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: 189,
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: 244,
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: 90,
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: 137,
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: 155,
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: 192,
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: 199,
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: 210,
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: 229,
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: 277,
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: 302,
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: 319,
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: 358,
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: 400,
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