@vercel/flags-core 1.5.0-dde2adb-20260526050633 → 1.5.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.
@@ -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.0";
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 import(
@@ -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 {
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
- promise;
1012
- options;
1013
1022
  constructor(options) {
1014
1023
  this.options = options;
1015
1024
  }
1025
+ promise;
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;
@@ -1296,8 +1308,11 @@ async function connectStream(config, callbacks) {
1296
1308
  };
1297
1309
  try {
1298
1310
  lastAttemptTime = Date.now();
1311
+ const token = await config.resolveToken().catch((error) => {
1312
+ throw new TokenResolutionError(error);
1313
+ });
1299
1314
  const headers = {
1300
- Authorization: `Bearer ${sdkKey}`,
1315
+ Authorization: `Bearer ${token}`,
1301
1316
  "User-Agent": `VercelFlagsCore/${version}`,
1302
1317
  "X-Retry-Attempt": String(retryCount)
1303
1318
  };
@@ -1403,6 +1418,11 @@ async function connectStream(config, callbacks) {
1403
1418
  if (abortController.signal.aborted) {
1404
1419
  break;
1405
1420
  }
1421
+ if (error instanceof TokenResolutionError && !initialDataReceived) {
1422
+ rejectInit(error.cause);
1423
+ abortController.abort();
1424
+ break;
1425
+ }
1406
1426
  if (!connectionAbort.signal.aborted) {
1407
1427
  console.error("@vercel/flags-core: Stream error", error);
1408
1428
  }
@@ -1454,7 +1474,7 @@ var StreamSource = class extends TypedEmitter {
1454
1474
  const promise = connectStream(
1455
1475
  {
1456
1476
  host: this.options.host,
1457
- sdkKey: this.options.sdkKey,
1477
+ resolveToken: () => this.options.auth.resolveToken(),
1458
1478
  abortController,
1459
1479
  fetch: this.options.fetch,
1460
1480
  revision: this.revision
@@ -1541,11 +1561,6 @@ var Controller = class {
1541
1561
  // Suppresses usage tracking when the SDK key is unauthorized
1542
1562
  unauthorized = false;
1543
1563
  constructor(options) {
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
1564
  this.options = normalizeOptions(options);
1550
1565
  this.streamSource = new StreamSource(
1551
1566
  this.options,
@@ -1553,7 +1568,7 @@ var Controller = class {
1553
1568
  );
1554
1569
  this.pollingSource = new PollingSource(this.options);
1555
1570
  this.bundledSource = new BundledSource({
1556
- sdkKey: this.options.sdkKey,
1571
+ auth: this.options.auth,
1557
1572
  readBundledDefinitions
1558
1573
  });
1559
1574
  this.wireSourceEvents();
@@ -1753,7 +1768,7 @@ var Controller = class {
1753
1768
  try {
1754
1769
  const fetched = await fetchDatafile({
1755
1770
  host: this.options.host,
1756
- sdkKey: this.options.sdkKey,
1771
+ auth: this.options.auth,
1757
1772
  fetch: this.options.fetch
1758
1773
  });
1759
1774
  this.data = tagData(fetched, "fetched");
@@ -1949,7 +1964,7 @@ var Controller = class {
1949
1964
  try {
1950
1965
  const fetched = await fetchDatafile({
1951
1966
  host: this.options.host,
1952
- sdkKey: this.options.sdkKey,
1967
+ auth: this.options.auth,
1953
1968
  fetch: this.options.fetch
1954
1969
  });
1955
1970
  return tagData(fetched, "fetched");
@@ -1981,7 +1996,7 @@ var Controller = class {
1981
1996
  try {
1982
1997
  const fetched = await fetchDatafile({
1983
1998
  host: this.options.host,
1984
- sdkKey: this.options.sdkKey,
1999
+ auth: this.options.auth,
1985
2000
  fetch: this.options.fetch
1986
2001
  });
1987
2002
  this.data = tagData(fetched, "fetched");
@@ -2033,7 +2048,7 @@ var Controller = class {
2033
2048
  try {
2034
2049
  const fetched = await fetchDatafile({
2035
2050
  host: this.options.host,
2036
- sdkKey: this.options.sdkKey,
2051
+ auth: this.options.auth,
2037
2052
  fetch: this.options.fetch
2038
2053
  });
2039
2054
  this.data = tagData(fetched, "fetched");
@@ -2106,6 +2121,9 @@ var Controller = class {
2106
2121
  }
2107
2122
  };
2108
2123
 
2124
+ // src/controller/auth.ts
2125
+ import { getVercelOidcToken } from "@vercel/oidc";
2126
+
2109
2127
  // src/utils/sdk-keys.ts
2110
2128
  var SDK_KEY_REGEX = /^vf_(?:server|client)_/;
2111
2129
  function parseSdkKeyFromFlagsConnectionString(text) {
@@ -2120,28 +2138,80 @@ function parseSdkKeyFromFlagsConnectionString(text) {
2120
2138
  return null;
2121
2139
  }
2122
2140
 
2141
+ // src/controller/auth.ts
2142
+ async function getOidcToken() {
2143
+ try {
2144
+ return await getVercelOidcToken();
2145
+ } catch {
2146
+ throw new Error(
2147
+ [
2148
+ "@vercel/flags-core: Failed to get OIDC token.",
2149
+ "Are you running in a Vercel Environment where OIDC tokens are available?",
2150
+ "Did you mean to use an SDK Key instead? Use the environment variable FLAGS or pass it directly to the client."
2151
+ ].join(" ")
2152
+ );
2153
+ }
2154
+ }
2155
+ function getProjectIdFromOidcToken(oidcToken) {
2156
+ const tokenParts = oidcToken.split(".");
2157
+ if (tokenParts.length !== 3 || !tokenParts[1]) {
2158
+ throw new Error("@vercel/flags-core: Invalid OIDC token");
2159
+ }
2160
+ const payload = JSON.parse(
2161
+ Buffer.from(tokenParts[1], "base64url").toString("utf8")
2162
+ );
2163
+ if (typeof payload.project_id !== "string" || !payload.project_id) {
2164
+ throw new Error(
2165
+ "@vercel/flags-core: Missing project_id claim in OIDC token"
2166
+ );
2167
+ }
2168
+ return payload.project_id;
2169
+ }
2170
+ var Authentication = class {
2171
+ sdkKey;
2172
+ constructor(sdkKeyOrConnectionString) {
2173
+ if (sdkKeyOrConnectionString !== void 0) {
2174
+ if (typeof sdkKeyOrConnectionString !== "string") {
2175
+ throw new Error(
2176
+ `@vercel/flags-core: Invalid sdkKey. Expected string, got ${typeof sdkKeyOrConnectionString}`
2177
+ );
2178
+ }
2179
+ const parsed = parseSdkKeyFromFlagsConnectionString(
2180
+ sdkKeyOrConnectionString
2181
+ );
2182
+ if (!parsed) {
2183
+ throw new Error("@vercel/flags-core: Missing sdkKey");
2184
+ }
2185
+ this.sdkKey = parsed;
2186
+ }
2187
+ }
2188
+ async resolveToken() {
2189
+ if (this.sdkKey) {
2190
+ return this.sdkKey;
2191
+ }
2192
+ return await getOidcToken();
2193
+ }
2194
+ async resolveBundledDefinitionsLookup() {
2195
+ if (this.sdkKey) {
2196
+ return { type: "sdk-key", sdkKey: this.sdkKey };
2197
+ }
2198
+ const oidcToken = await this.resolveToken();
2199
+ return {
2200
+ type: "project-id",
2201
+ projectId: getProjectIdFromOidcToken(oidcToken)
2202
+ };
2203
+ }
2204
+ };
2205
+
2123
2206
  // src/index.make.ts
2124
2207
  function make(createRawClient) {
2125
2208
  let _defaultFlagsClient = null;
2126
2209
  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 });
2210
+ const auth = new Authentication(sdkKeyOrConnectionString);
2211
+ const controller = new Controller({ auth, ...options });
2142
2212
  return createRawClient({
2143
2213
  controller,
2144
- origin: { provider: "vercel", sdkKey }
2214
+ origin: { provider: "vercel", sdkKey: auth.sdkKey }
2145
2215
  });
2146
2216
  }
2147
2217
  function resetDefaultFlagsClient2() {
@@ -2150,14 +2220,7 @@ function make(createRawClient) {
2150
2220
  const flagsClient2 = new Proxy({}, {
2151
2221
  get(_, prop) {
2152
2222
  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);
2223
+ _defaultFlagsClient = createClient2(process.env.FLAGS);
2161
2224
  }
2162
2225
  return _defaultFlagsClient[prop];
2163
2226
  }
@@ -2198,4 +2261,4 @@ export {
2198
2261
  resetDefaultFlagsClient,
2199
2262
  createClient
2200
2263
  };
2201
- //# sourceMappingURL=chunk-CX2U3ZTN.js.map
2264
+ //# sourceMappingURL=chunk-WTGXBSWR.js.map