@vercel/flags-core 1.10.1 → 1.10.3

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.
@@ -559,7 +559,7 @@ function bulkEvaluate(flags, shared) {
559
559
  }
560
560
 
561
561
  // package.json
562
- var version = "1.10.1";
562
+ var version = "1.10.3";
563
563
 
564
564
  // src/lib/report-value.ts
565
565
  function internalReportValue(key, value, data) {
@@ -1087,19 +1087,28 @@ function unauthorizedMessage(sourceProjectId) {
1087
1087
  if (!sourceProjectId) {
1088
1088
  return "unauthorized (401)";
1089
1089
  }
1090
- return `unauthorized (401): this deployment is not allowed to read the flags of project "${sourceProjectId}". Check that the project is connected.`;
1090
+ return `unauthorized (401): this deployment is not allowed to read the flags of project "${sourceProjectId}". Check that the project is connected. See https://vercel.com/docs/flags/vercel-flags/project-connections`;
1091
1091
  }
1092
+ var oidcTokenMessage = [
1093
+ "@vercel/flags-core: Failed to get OIDC token.",
1094
+ "Are you running in a Vercel Environment where OIDC tokens are available?",
1095
+ "Did you mean to use an SDK Key instead? Use the environment variable FLAGS or pass it directly to the client."
1096
+ ].join(" ");
1097
+ var missingCredentials = /`vc link|vercel login|^The 'x-vercel-oidc-token' header is missing from the request\.$/;
1098
+ var OidcTokenError = class extends Error {
1099
+ constructor(cause) {
1100
+ super(oidcTokenMessage, { cause });
1101
+ this.name = "OidcTokenError";
1102
+ }
1103
+ };
1092
1104
  async function getOidcToken() {
1093
1105
  try {
1094
1106
  return await getVercelOidcToken();
1095
- } catch {
1096
- throw new Error(
1097
- [
1098
- "@vercel/flags-core: Failed to get OIDC token.",
1099
- "Are you running in a Vercel Environment where OIDC tokens are available?",
1100
- "Did you mean to use an SDK Key instead? Use the environment variable FLAGS or pass it directly to the client."
1101
- ].join(" ")
1102
- );
1107
+ } catch (error) {
1108
+ if (error instanceof Error && missingCredentials.test(error.message)) {
1109
+ throw new OidcTokenError(error);
1110
+ }
1111
+ throw new Error(oidcTokenMessage, { cause: error });
1103
1112
  }
1104
1113
  }
1105
1114
  function getProjectIdFromOidcToken(oidcToken) {
@@ -1142,13 +1151,24 @@ var Authentication = class {
1142
1151
  }
1143
1152
  return await getOidcToken();
1144
1153
  }
1145
- async resolveBundledDefinitionsLookup() {
1154
+ getStaticBundledDefinitionsLookup() {
1146
1155
  if (this.sdkKey) {
1147
1156
  return { type: "sdk-key", sdkKey: this.sdkKey };
1148
1157
  }
1149
1158
  if (this.sourceProjectId) {
1150
1159
  return { type: "project-id", projectId: this.sourceProjectId };
1151
1160
  }
1161
+ const projectId = process.env.VERCEL_PROJECT_ID;
1162
+ if (projectId) {
1163
+ return { type: "project-id", projectId };
1164
+ }
1165
+ return void 0;
1166
+ }
1167
+ async resolveBundledDefinitionsLookup() {
1168
+ const lookup = this.getStaticBundledDefinitionsLookup();
1169
+ if (lookup) {
1170
+ return lookup;
1171
+ }
1152
1172
  const oidcToken = await this.resolveToken();
1153
1173
  return {
1154
1174
  type: "project-id",
@@ -1658,6 +1678,13 @@ var BundledSource = class {
1658
1678
  }
1659
1679
  return void 0;
1660
1680
  }
1681
+ /**
1682
+ * Starts reading bundled definitions without waiting for the result.
1683
+ */
1684
+ preload() {
1685
+ this.getResult().catch(() => {
1686
+ });
1687
+ }
1661
1688
  getResult() {
1662
1689
  if (!this.promise) {
1663
1690
  this.promise = this.options.readBundledDefinitions(this.options.auth);
@@ -2125,7 +2152,8 @@ async function fetchDatafile(options) {
2125
2152
  return await Promise.race([fetchAttempt(attempt + 1), aborted]);
2126
2153
  } catch (error) {
2127
2154
  signal.throwIfAborted();
2128
- if (attempt === maxAttempts - 1 || error instanceof DatafileHttpError && !error.retryable) {
2155
+ if (attempt === maxAttempts - 1 || error instanceof DatafileHttpError && !error.retryable || // Retrying will not produce a token that is not there.
2156
+ error instanceof OidcTokenError) {
2129
2157
  throw error instanceof Error ? error : new Error("Unknown fetch error");
2130
2158
  }
2131
2159
  debug(options.clientName, "datafile.fetch.retry", () => ({
@@ -2543,7 +2571,7 @@ async function connectStream(config, callbacks) {
2543
2571
  rejectInit(error);
2544
2572
  }
2545
2573
  onDisconnect?.();
2546
- abortController.abort();
2574
+ abortController.abort(error);
2547
2575
  break;
2548
2576
  }
2549
2577
  if (!response.ok) {
@@ -2645,7 +2673,7 @@ async function connectStream(config, callbacks) {
2645
2673
  }
2646
2674
  if (error instanceof TokenResolutionError && !initialDataReceived) {
2647
2675
  rejectInit(error.cause);
2648
- abortController.abort();
2676
+ abortController.abort(error.cause);
2649
2677
  break;
2650
2678
  }
2651
2679
  if (connectionAbort.signal.reason !== PING_TIMEOUT) {
@@ -2719,7 +2747,7 @@ var StreamSource = class extends TypedEmitter {
2719
2747
  if (this.abortController === abortController) {
2720
2748
  this.promise = void 0;
2721
2749
  this.abortController = void 0;
2722
- this.emit("exhausted");
2750
+ this.emit("exhausted", abortController.signal.reason);
2723
2751
  }
2724
2752
  },
2725
2753
  { once: true }
@@ -2780,7 +2808,7 @@ var UNKNOWN_FRESHNESS = {
2780
2808
  assess: () => ({ status: "unknown" })
2781
2809
  };
2782
2810
  function isUnauthorizedError(error) {
2783
- if (error instanceof UnauthorizedError) {
2811
+ if (error instanceof UnauthorizedError || error instanceof OidcTokenError) {
2784
2812
  return true;
2785
2813
  }
2786
2814
  if (!(error instanceof Error)) {
@@ -2815,8 +2843,9 @@ var Controller = class {
2815
2843
  // Build-step deduplication
2816
2844
  buildDataPromise = null;
2817
2845
  buildReadTracked = false;
2818
- // Suppresses usage tracking while the credential is rejected. Set on a 401
2819
- // from any source, cleared as soon as a source delivers data again.
2846
+ // Suppresses usage tracking while the credential is rejected or no OIDC
2847
+ // token is available. Set on an auth failure from any source, cleared as
2848
+ // soon as a source delivers data again.
2820
2849
  unauthorized = false;
2821
2850
  constructor(options) {
2822
2851
  this.options = normalizeOptions(options);
@@ -2853,6 +2882,9 @@ var Controller = class {
2853
2882
  auth: this.options.auth,
2854
2883
  readBundledDefinitions
2855
2884
  });
2885
+ if (!this.options.datafile && this.options.auth.getStaticBundledDefinitionsLookup()) {
2886
+ this.bundledSource.preload();
2887
+ }
2856
2888
  this.wireSourceEvents();
2857
2889
  if (this.options.datafile) {
2858
2890
  this.cache.seed(tagData({ ...this.options.datafile }, "provided"));
@@ -2909,10 +2941,16 @@ var Controller = class {
2909
2941
  this.transition("degraded", "stream-disconnected");
2910
2942
  }
2911
2943
  };
2912
- onStreamExhausted = () => {
2944
+ onStreamExhausted = (reason) => {
2913
2945
  if (this.state !== "streaming" && this.state !== "degraded") {
2914
2946
  return;
2915
2947
  }
2948
+ if (isUnauthorizedError(reason)) {
2949
+ if (this.state === "streaming") {
2950
+ this.transition("degraded", "stream-auth-failed");
2951
+ }
2952
+ return;
2953
+ }
2916
2954
  void this.activateFallbackSource().catch(() => {
2917
2955
  });
2918
2956
  };
@@ -2924,7 +2962,7 @@ var Controller = class {
2924
2962
  this.onSourceError(error);
2925
2963
  if (this.state === "polling" && isUnauthorizedError(error)) {
2926
2964
  this.pollingSource.stop();
2927
- this.transition("degraded", "polling-unauthorized");
2965
+ this.transition("degraded", "polling-auth-failed");
2928
2966
  }
2929
2967
  };
2930
2968
  // ---------------------------------------------------------------------------
@@ -3334,6 +3372,10 @@ var Controller = class {
3334
3372
  this.transition("degraded", "stream-still-connecting");
3335
3373
  return;
3336
3374
  }
3375
+ if (outcome === "auth-failed") {
3376
+ this.transition("degraded", "stream-auth-failed");
3377
+ return;
3378
+ }
3337
3379
  }
3338
3380
  if (this.options.polling.enabled) {
3339
3381
  if (this.state !== "polling") {
@@ -3366,7 +3408,7 @@ var Controller = class {
3366
3408
  return "connected";
3367
3409
  } catch (error) {
3368
3410
  this.noteUnauthorized(error);
3369
- return "failed";
3411
+ return isUnauthorizedError(error) ? "auth-failed" : "failed";
3370
3412
  }
3371
3413
  }
3372
3414
  let timeoutId;
@@ -3400,7 +3442,7 @@ var Controller = class {
3400
3442
  } catch (error) {
3401
3443
  clearTimeout(timeoutId);
3402
3444
  this.noteUnauthorized(error);
3403
- return "failed";
3445
+ return isUnauthorizedError(error) ? "auth-failed" : "failed";
3404
3446
  }
3405
3447
  }
3406
3448
  // ---------------------------------------------------------------------------
@@ -3754,4 +3796,4 @@ export {
3754
3796
  resetDefaultFlagsClient,
3755
3797
  createClient
3756
3798
  };
3757
- //# sourceMappingURL=chunk-ZY7CHPJG.js.map
3799
+ //# sourceMappingURL=chunk-OIOT7N7A.js.map