@vercel/flags-core 1.5.0-dde2adb-20260526050633 → 1.5.1

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.
@@ -482,7 +482,7 @@ function findWeightedIndex(weights, value, maxValue) {
482
482
  }
483
483
 
484
484
  // package.json
485
- var version = "1.5.0-dde2adb-20260526050633";
485
+ var version = "1.5.1";
486
486
 
487
487
  // src/lib/report-value.ts
488
488
  function internalReportValue(key, value, data) {
@@ -759,7 +759,7 @@ function hashSdkKey(sdkKey) {
759
759
  sdkKeyHashCache.set(sdkKey, promise);
760
760
  return promise;
761
761
  }
762
- async function readBundledDefinitions(sdkKey) {
762
+ async function readBundledDefinitions(auth) {
763
763
  let get;
764
764
  try {
765
765
  const module = await Promise.resolve().then(() => _interopRequireWildcard(require(
@@ -776,10 +776,20 @@ async function readBundledDefinitions(sdkKey) {
776
776
  if (typeof get !== "function") {
777
777
  return { definitions: null, state: "missing-file" };
778
778
  }
779
- const entry = get(sdkKey);
779
+ let lookup;
780
+ try {
781
+ lookup = await auth.resolveBundledDefinitionsLookup();
782
+ } catch (error) {
783
+ return { definitions: null, state: "unexpected-error", error };
784
+ }
785
+ if (lookup.type === "project-id") {
786
+ const entry2 = get(lookup.projectId);
787
+ return entry2 ? { definitions: entry2, state: "ok" } : { definitions: null, state: "missing-entry" };
788
+ }
789
+ const entry = get(lookup.sdkKey);
780
790
  if (entry) return { definitions: entry, state: "ok" };
781
791
  try {
782
- const hashedKey = await hashSdkKey(sdkKey);
792
+ const hashedKey = await hashSdkKey(lookup.sdkKey);
783
793
  const hashedEntry = get(hashedKey);
784
794
  if (hashedEntry) return { definitions: hashedEntry, state: "ok" };
785
795
  } catch (error) {
@@ -950,13 +960,14 @@ var UsageTracker = (_class = class {
950
960
  const flushId = ++this.flushCounter;
951
961
  for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
952
962
  try {
963
+ const token = await this.options.auth.resolveToken();
953
964
  const response = await this.options.fetch(
954
965
  `${this.options.host}/v1/ingest`,
955
966
  {
956
967
  method: "POST",
957
968
  headers: {
958
969
  "Content-Type": "application/json",
959
- Authorization: `Bearer ${this.options.sdkKey}`,
970
+ Authorization: `Bearer ${token}`,
960
971
  "User-Agent": `VercelFlagsCore/${version}`,
961
972
  ...process.env.VERCEL_ENV ? { "X-Vercel-Env": process.env.VERCEL_ENV } : null,
962
973
  ...isDebugMode ? { "x-vercel-debug-ingest": "1" } : null
@@ -1008,11 +1019,10 @@ var FallbackEntryNotFoundError = class extends Error {
1008
1019
 
1009
1020
  // src/controller/bundled-source.ts
1010
1021
  var BundledSource = class {
1011
-
1012
-
1013
1022
  constructor(options) {
1014
1023
  this.options = options;
1015
1024
  }
1025
+
1016
1026
  /**
1017
1027
  * Load bundled definitions.
1018
1028
  * Throws if bundled definitions are not available.
@@ -1057,7 +1067,7 @@ var BundledSource = class {
1057
1067
  }
1058
1068
  getResult() {
1059
1069
  if (!this.promise) {
1060
- this.promise = this.options.readBundledDefinitions(this.options.sdkKey);
1070
+ this.promise = this.options.readBundledDefinitions(this.options.auth);
1061
1071
  }
1062
1072
  return this.promise;
1063
1073
  }
@@ -1066,6 +1076,7 @@ var BundledSource = class {
1066
1076
  // src/controller/fetch-datafile.ts
1067
1077
  var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
1068
1078
  async function fetchDatafile(options) {
1079
+ const token = await options.auth.resolveToken();
1069
1080
  const controller = new AbortController();
1070
1081
  const timeoutId = setTimeout(
1071
1082
  () => controller.abort(),
@@ -1082,7 +1093,7 @@ async function fetchDatafile(options) {
1082
1093
  try {
1083
1094
  const res = await options.fetch(`${options.host}/v1/datafile`, {
1084
1095
  headers: {
1085
- Authorization: `Bearer ${options.sdkKey}`,
1096
+ Authorization: `Bearer ${token}`,
1086
1097
  "User-Agent": `VercelFlagsCore/${version}`,
1087
1098
  ...process.env.VERCEL_ENV ? { "X-Vercel-Env": process.env.VERCEL_ENV } : null
1088
1099
  },
@@ -1139,7 +1150,7 @@ function normalizeOptions(options) {
1139
1150
  };
1140
1151
  }
1141
1152
  return {
1142
- sdkKey: options.sdkKey,
1153
+ auth: options.auth,
1143
1154
  datafile: options.datafile,
1144
1155
  stream,
1145
1156
  polling,
@@ -1249,13 +1260,14 @@ var UnauthorizedError = class extends Error {
1249
1260
  this.name = "UnauthorizedError";
1250
1261
  }
1251
1262
  };
1263
+ var TokenResolutionError = class extends Error {
1264
+ constructor(error) {
1265
+ super("stream: token resolution failed", { cause: error });
1266
+ this.name = "TokenResolutionError";
1267
+ }
1268
+ };
1252
1269
  async function connectStream(config, callbacks) {
1253
- const {
1254
- host,
1255
- sdkKey,
1256
- abortController,
1257
- fetch: fetchFn = globalThis.fetch
1258
- } = config;
1270
+ const { host, abortController, fetch: fetchFn = globalThis.fetch } = config;
1259
1271
  const { onDatafile, onPrimed, onDisconnect } = callbacks;
1260
1272
  let retryCount = 0;
1261
1273
  let lastAttemptTime = 0;
@@ -1267,9 +1279,13 @@ async function connectStream(config, callbacks) {
1267
1279
  });
1268
1280
  void (async () => {
1269
1281
  let initialDataReceived = false;
1282
+ let lastError;
1270
1283
  while (!abortController.signal.aborted) {
1271
1284
  if (retryCount > MAX_RETRY_COUNT) {
1272
- console.error("@vercel/flags-core: Max retry count exceeded");
1285
+ console.error(
1286
+ "@vercel/flags-core: Max retry count exceeded",
1287
+ _nullishCoalesce(lastError, () => ( "stream closed repeatedly without an error"))
1288
+ );
1273
1289
  if (!initialDataReceived) {
1274
1290
  rejectInit(
1275
1291
  new Error("stream: max retry count exceeded before receiving data")
@@ -1296,8 +1312,11 @@ async function connectStream(config, callbacks) {
1296
1312
  };
1297
1313
  try {
1298
1314
  lastAttemptTime = Date.now();
1315
+ const token = await config.resolveToken().catch((error) => {
1316
+ throw new TokenResolutionError(error);
1317
+ });
1299
1318
  const headers = {
1300
- Authorization: `Bearer ${sdkKey}`,
1319
+ Authorization: `Bearer ${token}`,
1301
1320
  "User-Agent": `VercelFlagsCore/${version}`,
1302
1321
  "X-Retry-Attempt": String(retryCount)
1303
1322
  };
@@ -1403,8 +1422,13 @@ async function connectStream(config, callbacks) {
1403
1422
  if (abortController.signal.aborted) {
1404
1423
  break;
1405
1424
  }
1425
+ if (error instanceof TokenResolutionError && !initialDataReceived) {
1426
+ rejectInit(error.cause);
1427
+ abortController.abort();
1428
+ break;
1429
+ }
1406
1430
  if (!connectionAbort.signal.aborted) {
1407
- console.error("@vercel/flags-core: Stream error", error);
1431
+ lastError = error;
1408
1432
  }
1409
1433
  _optionalChain([onDisconnect, 'optionalCall', _64 => _64()]);
1410
1434
  retryCount++;
@@ -1454,7 +1478,7 @@ var StreamSource = class extends TypedEmitter {
1454
1478
  const promise = connectStream(
1455
1479
  {
1456
1480
  host: this.options.host,
1457
- sdkKey: this.options.sdkKey,
1481
+ resolveToken: () => this.options.auth.resolveToken(),
1458
1482
  abortController,
1459
1483
  fetch: this.options.fetch,
1460
1484
  revision: this.revision
@@ -1541,11 +1565,6 @@ var Controller = (_class3 = class {
1541
1565
  // Suppresses usage tracking when the SDK key is unauthorized
1542
1566
  __init11() {this.unauthorized = false}
1543
1567
  constructor(options) {;_class3.prototype.__init5.call(this);_class3.prototype.__init6.call(this);_class3.prototype.__init7.call(this);_class3.prototype.__init8.call(this);_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);_class3.prototype.__init11.call(this);_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);_class3.prototype.__init14.call(this);_class3.prototype.__init15.call(this);_class3.prototype.__init16.call(this);_class3.prototype.__init17.call(this);
1544
- if (!options.sdkKey || typeof options.sdkKey !== "string" || !options.sdkKey.startsWith("vf_")) {
1545
- throw new Error(
1546
- '@vercel/flags-core: SDK key must be a string starting with "vf_"'
1547
- );
1548
- }
1549
1568
  this.options = normalizeOptions(options);
1550
1569
  this.streamSource = new StreamSource(
1551
1570
  this.options,
@@ -1553,7 +1572,7 @@ var Controller = (_class3 = class {
1553
1572
  );
1554
1573
  this.pollingSource = new PollingSource(this.options);
1555
1574
  this.bundledSource = new BundledSource({
1556
- sdkKey: this.options.sdkKey,
1575
+ auth: this.options.auth,
1557
1576
  readBundledDefinitions
1558
1577
  });
1559
1578
  this.wireSourceEvents();
@@ -1753,7 +1772,7 @@ var Controller = (_class3 = class {
1753
1772
  try {
1754
1773
  const fetched = await fetchDatafile({
1755
1774
  host: this.options.host,
1756
- sdkKey: this.options.sdkKey,
1775
+ auth: this.options.auth,
1757
1776
  fetch: this.options.fetch
1758
1777
  });
1759
1778
  this.data = tagData(fetched, "fetched");
@@ -1844,7 +1863,7 @@ var Controller = (_class3 = class {
1844
1863
  clearTimeout(timeoutId);
1845
1864
  if (result === "timeout") {
1846
1865
  console.warn(
1847
- "@vercel/flags-core: Stream initialization timeout, falling back"
1866
+ "@vercel/flags-core: Stream initialization timeout, falling back while continuing to connect in the background"
1848
1867
  );
1849
1868
  void this.streamSource.start().catch(() => {
1850
1869
  });
@@ -1894,7 +1913,7 @@ var Controller = (_class3 = class {
1894
1913
  clearTimeout(timeoutId);
1895
1914
  if (result === "timeout") {
1896
1915
  console.warn(
1897
- "@vercel/flags-core: Polling initialization timeout, falling back"
1916
+ "@vercel/flags-core: Polling initialization timeout, falling back while continuing to poll in the background"
1898
1917
  );
1899
1918
  return false;
1900
1919
  }
@@ -1949,7 +1968,7 @@ var Controller = (_class3 = class {
1949
1968
  try {
1950
1969
  const fetched = await fetchDatafile({
1951
1970
  host: this.options.host,
1952
- sdkKey: this.options.sdkKey,
1971
+ auth: this.options.auth,
1953
1972
  fetch: this.options.fetch
1954
1973
  });
1955
1974
  return tagData(fetched, "fetched");
@@ -1981,7 +2000,7 @@ var Controller = (_class3 = class {
1981
2000
  try {
1982
2001
  const fetched = await fetchDatafile({
1983
2002
  host: this.options.host,
1984
- sdkKey: this.options.sdkKey,
2003
+ auth: this.options.auth,
1985
2004
  fetch: this.options.fetch
1986
2005
  });
1987
2006
  this.data = tagData(fetched, "fetched");
@@ -2033,7 +2052,7 @@ var Controller = (_class3 = class {
2033
2052
  try {
2034
2053
  const fetched = await fetchDatafile({
2035
2054
  host: this.options.host,
2036
- sdkKey: this.options.sdkKey,
2055
+ auth: this.options.auth,
2037
2056
  fetch: this.options.fetch
2038
2057
  });
2039
2058
  this.data = tagData(fetched, "fetched");
@@ -2106,6 +2125,9 @@ var Controller = (_class3 = class {
2106
2125
  }
2107
2126
  }, _class3);
2108
2127
 
2128
+ // src/controller/auth.ts
2129
+ var _oidc = require('@vercel/oidc');
2130
+
2109
2131
  // src/utils/sdk-keys.ts
2110
2132
  var SDK_KEY_REGEX = /^vf_(?:server|client)_/;
2111
2133
  function parseSdkKeyFromFlagsConnectionString(text) {
@@ -2120,28 +2142,80 @@ function parseSdkKeyFromFlagsConnectionString(text) {
2120
2142
  return null;
2121
2143
  }
2122
2144
 
2145
+ // src/controller/auth.ts
2146
+ async function getOidcToken() {
2147
+ try {
2148
+ return await _oidc.getVercelOidcToken.call(void 0, );
2149
+ } catch (e15) {
2150
+ throw new Error(
2151
+ [
2152
+ "@vercel/flags-core: Failed to get OIDC token.",
2153
+ "Are you running in a Vercel Environment where OIDC tokens are available?",
2154
+ "Did you mean to use an SDK Key instead? Use the environment variable FLAGS or pass it directly to the client."
2155
+ ].join(" ")
2156
+ );
2157
+ }
2158
+ }
2159
+ function getProjectIdFromOidcToken(oidcToken) {
2160
+ const tokenParts = oidcToken.split(".");
2161
+ if (tokenParts.length !== 3 || !tokenParts[1]) {
2162
+ throw new Error("@vercel/flags-core: Invalid OIDC token");
2163
+ }
2164
+ const payload = JSON.parse(
2165
+ Buffer.from(tokenParts[1], "base64url").toString("utf8")
2166
+ );
2167
+ if (typeof payload.project_id !== "string" || !payload.project_id) {
2168
+ throw new Error(
2169
+ "@vercel/flags-core: Missing project_id claim in OIDC token"
2170
+ );
2171
+ }
2172
+ return payload.project_id;
2173
+ }
2174
+ var Authentication = class {
2175
+
2176
+ constructor(sdkKeyOrConnectionString) {
2177
+ if (sdkKeyOrConnectionString !== void 0) {
2178
+ if (typeof sdkKeyOrConnectionString !== "string") {
2179
+ throw new Error(
2180
+ `@vercel/flags-core: Invalid sdkKey. Expected string, got ${typeof sdkKeyOrConnectionString}`
2181
+ );
2182
+ }
2183
+ const parsed = parseSdkKeyFromFlagsConnectionString(
2184
+ sdkKeyOrConnectionString
2185
+ );
2186
+ if (!parsed) {
2187
+ throw new Error("@vercel/flags-core: Missing sdkKey");
2188
+ }
2189
+ this.sdkKey = parsed;
2190
+ }
2191
+ }
2192
+ async resolveToken() {
2193
+ if (this.sdkKey) {
2194
+ return this.sdkKey;
2195
+ }
2196
+ return await getOidcToken();
2197
+ }
2198
+ async resolveBundledDefinitionsLookup() {
2199
+ if (this.sdkKey) {
2200
+ return { type: "sdk-key", sdkKey: this.sdkKey };
2201
+ }
2202
+ const oidcToken = await this.resolveToken();
2203
+ return {
2204
+ type: "project-id",
2205
+ projectId: getProjectIdFromOidcToken(oidcToken)
2206
+ };
2207
+ }
2208
+ };
2209
+
2123
2210
  // src/index.make.ts
2124
2211
  function make(createRawClient) {
2125
2212
  let _defaultFlagsClient = null;
2126
2213
  function createClient2(sdkKeyOrConnectionString, options) {
2127
- if (!sdkKeyOrConnectionString)
2128
- throw new Error("@vercel/flags-core: Missing sdkKey");
2129
- if (typeof sdkKeyOrConnectionString !== "string")
2130
- throw new Error(
2131
- `@vercel/flags-core: Invalid sdkKey. Expected string, got ${typeof sdkKeyOrConnectionString}`
2132
- );
2133
- const sdkKey = parseSdkKeyFromFlagsConnectionString(
2134
- sdkKeyOrConnectionString
2135
- );
2136
- if (!sdkKey) {
2137
- throw new Error(
2138
- "@vercel/flags-core: Missing sdkKey in connection string"
2139
- );
2140
- }
2141
- const controller = new Controller({ sdkKey, ...options });
2214
+ const auth = new Authentication(sdkKeyOrConnectionString);
2215
+ const controller = new Controller({ auth, ...options });
2142
2216
  return createRawClient({
2143
2217
  controller,
2144
- origin: { provider: "vercel", sdkKey }
2218
+ origin: { provider: "vercel", sdkKey: auth.sdkKey }
2145
2219
  });
2146
2220
  }
2147
2221
  function resetDefaultFlagsClient2() {
@@ -2150,14 +2224,7 @@ function make(createRawClient) {
2150
2224
  const flagsClient2 = new Proxy({}, {
2151
2225
  get(_, prop) {
2152
2226
  if (!_defaultFlagsClient) {
2153
- if (!process.env.FLAGS) {
2154
- throw new Error("flags: Missing environment variable FLAGS");
2155
- }
2156
- const sdkKey = parseSdkKeyFromFlagsConnectionString(process.env.FLAGS);
2157
- if (!sdkKey) {
2158
- throw new Error("@vercel/flags-core: Missing sdkKey");
2159
- }
2160
- _defaultFlagsClient = createClient2(sdkKey);
2227
+ _defaultFlagsClient = createClient2(process.env.FLAGS);
2161
2228
  }
2162
2229
  return _defaultFlagsClient[prop];
2163
2230
  }
@@ -2198,4 +2265,4 @@ var {
2198
2265
 
2199
2266
 
2200
2267
  exports.ResolutionReason = ResolutionReason; exports.evaluate = evaluate; exports.FallbackNotFoundError = FallbackNotFoundError; exports.FallbackEntryNotFoundError = FallbackEntryNotFoundError; exports.Controller = Controller; exports.flagsClient = flagsClient; exports.resetDefaultFlagsClient = resetDefaultFlagsClient; exports.createClient = createClient;
2201
- //# sourceMappingURL=chunk-ZHIY5NVJ.cjs.map
2268
+ //# sourceMappingURL=chunk-AXRUQULC.cjs.map