@vercel/flags-core 1.9.0 → 1.10.0-0037f35-20261006132750

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.
Files changed (33) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +184 -0
  3. package/dist/{chunk-TDZBUVBG.js → chunk-D4522FGO.js} +1295 -367
  4. package/dist/chunk-D4522FGO.js.map +1 -0
  5. package/dist/{chunk-PGKH4XQ7.cjs → chunk-ITB5XQD3.cjs} +1335 -407
  6. package/dist/chunk-ITB5XQD3.cjs.map +1 -0
  7. package/dist/{chunk-SLNLPFKU.js → chunk-SYXANNZ4.js} +1295 -367
  8. package/dist/chunk-SYXANNZ4.js.map +1 -0
  9. package/dist/{chunk-W742GGZU.cjs → chunk-W6776MKF.cjs} +1336 -408
  10. package/dist/chunk-W6776MKF.cjs.map +1 -0
  11. package/dist/index.default.cjs +2 -2
  12. package/dist/index.default.d.cts +104 -39
  13. package/dist/index.default.d.ts +104 -39
  14. package/dist/index.default.js +1 -1
  15. package/dist/index.next-js.cjs +2 -2
  16. package/dist/index.next-js.d.cts +104 -39
  17. package/dist/index.next-js.d.ts +104 -39
  18. package/dist/index.next-js.js +1 -1
  19. package/dist/openfeature.default.cjs +2 -2
  20. package/dist/openfeature.default.d.cts +1 -1
  21. package/dist/openfeature.default.d.ts +1 -1
  22. package/dist/openfeature.default.js +1 -1
  23. package/dist/openfeature.next-js.cjs +2 -2
  24. package/dist/openfeature.next-js.d.cts +1 -1
  25. package/dist/openfeature.next-js.d.ts +1 -1
  26. package/dist/openfeature.next-js.js +1 -1
  27. package/dist/{types-1OK52NNi.d.cts → types-DpVlT3fI.d.cts} +7 -1
  28. package/dist/{types-1OK52NNi.d.ts → types-DpVlT3fI.d.ts} +7 -1
  29. package/package.json +1 -1
  30. package/dist/chunk-PGKH4XQ7.cjs.map +0 -1
  31. package/dist/chunk-SLNLPFKU.js.map +0 -1
  32. package/dist/chunk-TDZBUVBG.js.map +0 -1
  33. package/dist/chunk-W742GGZU.cjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/flags/flags/packages/vercel-flags-core/dist/chunk-ITB5XQD3.cjs","../src/index.default.ts","../src/controller-fns.ts","../src/evaluate.ts","../src/types.ts","../package.json","../src/lib/report-value.ts","../src/create-raw-client.ts","../src/utils/read-bundled-definitions.ts","../src/utils/ingest.ts","../src/controller/auth.ts","../src/utils/sdk-keys.ts","../src/utils/backoff.ts","../src/utils/debug-enabled.ts","../src/utils/runtime-ingest.ts","../src/utils/request-context.ts","../src/utils/scheduler.ts","../src/utils/usage/flags-config-read.ts","../src/utils/usage/flags-evaluation.ts","../src/utils/usage-tracker.ts","../src/errors.ts","../src/controller/bundled-source.ts","../src/controller/debug.ts","../src/controller/tagged-data.ts","../src/controller/datafile-cache.ts","../src/controller/fetch-datafile.ts","../src/controller/header-source.ts","../src/controller/normalized-options.ts","../src/controller/typed-emitter.ts","../src/controller/polling-source.ts","../src/utils/runtime.ts","../src/utils/sleep.ts","../src/controller/stream-connection.ts","../src/controller/stream-source.ts","../src/controller/index.ts","../src/index.make.ts"],"names":["bulkEvaluate","evaluate","ResolutionReason","Original","AccessorType","Packed"],"mappings":"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","file":"/home/runner/work/flags/flags/packages/vercel-flags-core/dist/chunk-ITB5XQD3.cjs","sourcesContent":[null,"/**\n * Exports for default runtimes\n *\n * There is also index.next-js.ts which targets Next.js specifically.\n * If you update this file, please update index.next-js.ts as well.\n *\n * Try keeping this file small. Export through index.common and index.make.\n *\n * This file has JSDoc on its exports which will be used by the editor.\n * We do not need to repeat the JSDoc on the next-js export.\n */\n\nimport { waitUntil } from '@vercel/functions';\nimport * as fns from './controller-fns';\nimport { createCreateRawClient } from './create-raw-client';\nimport { make } from './index.make';\n\nexport * from './index.common';\n\nexport const {\n /**\n * A lazily-initialized default flags client.\n *\n * - relies on process.env.FLAGS\n * - does not use process.env.GLOBAL_CONFIG\n */\n flagsClient,\n /**\n * For testing purposes\n */\n resetDefaultFlagsClient,\n /**\n * Create a flags client using an SDK key, connection string, or Vercel OIDC.\n */\n createClient,\n} = make(createCreateRawClient(fns), { waitUntil });\n","import {\n type BulkEvaluationInput,\n bulkEvaluate as bulkEvalFlags,\n evaluate as evalFlag,\n} from './evaluate';\nimport { internalReportValue } from './lib/report-value';\nimport type {\n BulkEvaluateInput,\n BundledDefinitions,\n ControllerInterface,\n Datafile,\n EvaluationResult,\n Metrics,\n Packed,\n} from './types';\nimport { ErrorCode, ResolutionReason } from './types';\nimport type { TrackEvaluationOptions } from './utils/usage/flags-evaluation';\n\nexport type ControllerInstance = {\n controller: ControllerInterface;\n initialized: boolean;\n initPromise: Promise<void> | null;\n};\n\nexport const controllerInstanceMap = new Map<number, ControllerInstance>();\n\ntype EvaluationTrackingController = ControllerInterface & {\n trackEvaluation?: (options: TrackEvaluationOptions) => void;\n};\n\nfunction getInstance(id: number): ControllerInstance {\n const instance = controllerInstanceMap.get(id);\n if (!instance) {\n throw new Error(\n `@vercel/flags-core: Client instance ${id} not found. It may have been shut down.`,\n );\n }\n return instance;\n}\n\nexport function initialize(id: number): Promise<void> {\n return getInstance(id).controller.initialize();\n}\n\nexport function shutdown(id: number): void | Promise<void> {\n return getInstance(id).controller.shutdown();\n}\n\nexport function getDatafile(id: number) {\n return getInstance(id).controller.getDatafile();\n}\n\nexport function getFallbackDatafile(id: number): Promise<BundledDefinitions> {\n const ds = getInstance(id).controller;\n if (ds.getFallbackDatafile) return ds.getFallbackDatafile();\n throw new Error('flags: This data source does not support fallbacks');\n}\n\nfunction trackEvaluation(\n controller: EvaluationTrackingController,\n options: TrackEvaluationOptions,\n): void {\n controller.trackEvaluation?.(options);\n}\n\nexport async function evaluate<T, E = Record<string, unknown>>(\n id: number,\n flagKey: string,\n defaultValue?: T,\n entities?: E,\n): Promise<EvaluationResult<T>> {\n const controller = getInstance(id).controller as EvaluationTrackingController;\n\n let datafile: Datafile;\n try {\n datafile = await controller.read();\n } catch (error) {\n // All data sources failed. Fall back to defaultValue if provided.\n if (defaultValue !== undefined) {\n const result: EvaluationResult<T> = {\n value: defaultValue,\n variantId: null,\n reason: ResolutionReason.ERROR,\n errorMessage:\n error instanceof Error ? error.message : 'Failed to read datafile',\n };\n\n trackEvaluation(controller, {\n flagKey,\n variant: null,\n reason: result.reason,\n });\n\n return result;\n }\n throw error;\n }\n\n const flagDefinition = datafile.definitions[flagKey] as Packed.FlagDefinition;\n\n if (flagDefinition === undefined) {\n if (datafile.projectId) {\n internalReportValue(flagKey, defaultValue, {\n originProjectId: datafile.projectId,\n originProvider: 'vercel',\n reason: ResolutionReason.ERROR,\n });\n }\n\n const result: EvaluationResult<T> = {\n value: defaultValue,\n variantId: null,\n reason: ResolutionReason.ERROR,\n errorCode: ErrorCode.FLAG_NOT_FOUND,\n errorMessage: `@vercel/flags-core: Definition not found for flag \"${flagKey}\"`,\n metrics: {\n evaluationMs: 0,\n readMs: datafile.metrics.readMs,\n source: datafile.metrics.source,\n cacheStatus: datafile.metrics.cacheStatus,\n connectionState: datafile.metrics.connectionState,\n mode: datafile.metrics.mode,\n },\n };\n trackEvaluation(controller, {\n flagKey,\n variant: null,\n reason: result.reason,\n });\n return result;\n }\n\n const evalStartTime = Date.now();\n const result = evalFlag<T>({\n defaultValue,\n definition: flagDefinition,\n environment: datafile.environment,\n entities: entities ?? {},\n segments: datafile.segments,\n });\n const evaluationDurationMs = Date.now() - evalStartTime;\n\n if (datafile.projectId) {\n internalReportValue(flagKey, result.value, {\n originProjectId: datafile.projectId,\n originProvider: 'vercel',\n reason: result.reason,\n outcomeType:\n result.reason !== ResolutionReason.ERROR\n ? result.outcomeType\n : undefined,\n });\n }\n\n trackEvaluation(controller, {\n flagKey,\n variant: result.variantId,\n reason: result.reason,\n });\n\n return Object.assign(result, {\n metrics: {\n evaluationMs: evaluationDurationMs,\n readMs: datafile.metrics.readMs,\n source: datafile.metrics.source,\n cacheStatus: datafile.metrics.cacheStatus,\n connectionState: datafile.metrics.connectionState,\n mode: datafile.metrics.mode,\n },\n });\n}\n\nexport async function bulkEvaluate<T, E = Record<string, unknown>>(\n id: number,\n flags: BulkEvaluateInput<T>[],\n entities?: E,\n): Promise<Record<string, EvaluationResult<T>>> {\n const controller = getInstance(id).controller as EvaluationTrackingController;\n\n let datafile: Datafile;\n try {\n datafile = await controller.read();\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : 'Failed to read datafile';\n\n const results: Record<string, EvaluationResult<T>> = {};\n for (const flag of flags) {\n results[flag.key] = {\n value: flag.defaultValue,\n reason: ResolutionReason.ERROR,\n errorMessage,\n variantId: null,\n };\n trackEvaluation(controller, {\n flagKey: flag.key,\n variant: null,\n reason: ResolutionReason.ERROR,\n });\n }\n return results;\n }\n\n const baseMetrics: Metrics = {\n readMs: datafile.metrics.readMs,\n source: datafile.metrics.source,\n cacheStatus: datafile.metrics.cacheStatus,\n connectionState: datafile.metrics.connectionState,\n mode: datafile.metrics.mode,\n };\n\n const projectId = datafile.projectId;\n const results: Record<string, EvaluationResult<T>> = {};\n const toEvaluate: Record<string, BulkEvaluationInput<T>> = {};\n\n for (const flag of flags) {\n const { key, defaultValue } = flag;\n const flagDefinition = datafile.definitions[key] as Packed.FlagDefinition;\n\n if (flagDefinition === undefined) {\n if (projectId) {\n internalReportValue(key, defaultValue, {\n originProjectId: projectId,\n originProvider: 'vercel',\n reason: ResolutionReason.ERROR,\n });\n }\n results[key] = {\n value: defaultValue,\n reason: ResolutionReason.ERROR,\n errorCode: ErrorCode.FLAG_NOT_FOUND,\n errorMessage: `@vercel/flags-core: Definition not found for flag \"${key}\"`,\n metrics: { evaluationMs: 0, ...baseMetrics },\n variantId: null,\n };\n trackEvaluation(controller, {\n flagKey: key,\n variant: null,\n reason: ResolutionReason.ERROR,\n });\n continue;\n }\n\n toEvaluate[key] = { definition: flagDefinition, defaultValue };\n }\n\n const evalStartTime = Date.now();\n const evaluated = bulkEvalFlags<T>(toEvaluate, {\n entities: (entities ?? {}) as Record<string, unknown>,\n environment: datafile.environment,\n segments: datafile.segments,\n });\n const evaluationDurationMs = Date.now() - evalStartTime;\n\n for (const key in toEvaluate) {\n const result = evaluated[key]!;\n if (projectId) {\n internalReportValue(key, result.value, {\n originProjectId: projectId,\n originProvider: 'vercel',\n reason: result.reason,\n outcomeType:\n result.reason !== ResolutionReason.ERROR\n ? result.outcomeType\n : undefined,\n });\n }\n trackEvaluation(controller, {\n flagKey: key,\n variant: result.variantId,\n reason: result.reason,\n });\n results[key] = Object.assign(result, {\n metrics: { evaluationMs: evaluationDurationMs, ...baseMetrics },\n });\n }\n\n return results;\n}\n","import { xxHash32 as hashInput } from 'js-xxhash';\nimport {\n Comparator,\n type EvaluationParams,\n type EvaluationResult,\n type experimental_ExperimentAssignment,\n type experimental_ExperimentAssignmentReason,\n OutcomeType,\n Packed,\n ResolutionReason,\n type VariantId,\n} from './types';\n\ntype PathArray = (string | number)[];\n\nconst MAX_REGEX_INPUT_LENGTH = 10_000;\n\n/**\n * Bucket space for all traffic splitting. `hashInput` (xxHash32) returns exactly\n * `2**32` distinct values, so we use `2**32` as the space and compare the raw\n * hash against boundaries in it — no modulo (which would bias low buckets), and\n * the final variant's boundary is exactly `2**32` so nothing falls through.\n */\nconst HASH_SPACE = 2 ** 32;\n\n/** Denominator for promille values (rollout slots and segment `passPromille`). */\nconst PROMILLE_SCALE = 100_000;\n\n/**\n * Maps the fraction `numerator / denominator` to a cut point in `[0, HASH_SPACE]`;\n * a hash is \"below\" the fraction iff `hash < boundaryFor(…)`. Splits, rollouts,\n * and segments all cut with this one function, so a rollout at promille `p` is\n * bit-for-bit identical to the split `{ rollFrom: PROMILLE_SCALE - p, rollTo: p }`\n * — which is what lets a flag switch between split and rollout without reassigning\n * anyone. `denominator === 0` yields `NaN`, so evaluation falls through to default.\n */\nfunction boundaryFor(numerator: number, denominator: number): number {\n return Math.floor((numerator / denominator) * HASH_SPACE);\n}\n\n// WeakMaps keyed by datafile objects, so those objects stay pristine (no\n// symbol-keyed props) and serialize cleanly across the RSC boundary; entries\n// are GC'd with the datafile. Split boundaries are static per outcome, so the\n// cumulative cut points are computed once and reused across evaluations.\nconst splitBoundariesCache = new WeakMap<object, number[]>();\nconst compiledRegexCache = new WeakMap<object, RegExp>();\n\n/**\n * Cumulative hash boundaries for a split, one per variant in index order.\n * Variant `i` is served for hashes in `[boundaries[i-1], boundaries[i])`.\n */\nfunction getSplitBoundaries(outcome: { weights: number[] }): number[] {\n const cached = splitBoundariesCache.get(outcome);\n if (cached) return cached;\n const total = sum(outcome.weights);\n const boundaries: number[] = [];\n let cumulative = 0;\n for (const weight of outcome.weights) {\n cumulative += weight;\n boundaries.push(boundaryFor(cumulative, total));\n }\n splitBoundariesCache.set(outcome, boundaries);\n return boundaries;\n}\n\nfunction getCompiledRegex(rhs: { pattern: string; flags: string }): RegExp {\n const cached = compiledRegexCache.get(rhs);\n if (cached) return cached;\n // Boolean predicates must not retain lastIndex across cached evaluations.\n const compiled = new RegExp(rhs.pattern, rhs.flags.replace(/[gy]/g, ''));\n compiledRegexCache.set(rhs, compiled);\n return compiled;\n}\n\nfunction exhaustivenessCheck(_: never): never {\n throw new Error('Exhaustiveness check failed');\n}\n\nfunction getProperty(obj: any, pathArray: PathArray): any {\n return pathArray.reduce((acc: any, key: string | number) => {\n if (acc && key in acc) {\n return acc[key];\n }\n return undefined; // Return undefined if the property is not found\n }, obj);\n}\n\n/**\n * Accesses the value of the given lhs on the provided entities.\n *\n * This must return unknown as we don't know what the library users will pass.\n */\n\nfunction access<T>(lhs: Packed.LHS, params: EvaluationParams<T>): any {\n // we're dealing with an entity\n if (Array.isArray(lhs)) return getProperty(params.entities, lhs);\n\n // Code should never end up here as the segment accessor is handled\n // earlier in the matchConditions() function.\n if (lhs === Packed.AccessorType.SEGMENT)\n throw new Error('Unexpected segment');\n\n throw new Error('Accessor not implemented');\n}\n\nfunction isString(input: unknown): input is string {\n return typeof input === 'string';\n}\n\nfunction isNumber(input: unknown): input is number {\n return typeof input === 'number';\n}\n\nfunction isArray(input: unknown): input is unknown[] {\n return Array.isArray(input);\n}\n\nfunction lower<T>(input: T): T {\n if (typeof input === 'string') return input.toLowerCase() as T;\n if (Array.isArray(input)) return input.map(lower) as T;\n return input;\n}\n\nconst IGNORE_CASE_COMPARATORS: ReadonlySet<Comparator> = new Set([\n Comparator.EQ,\n Comparator.NOT_EQ,\n Comparator.ONE_OF,\n Comparator.NOT_ONE_OF,\n Comparator.CONTAINS_ALL_OF,\n Comparator.CONTAINS_ANY_OF,\n Comparator.CONTAINS_NONE_OF,\n Comparator.STARTS_WITH,\n Comparator.NOT_STARTS_WITH,\n Comparator.ENDS_WITH,\n Comparator.NOT_ENDS_WITH,\n Comparator.CONTAINS,\n Comparator.NOT_CONTAINS,\n]);\n\nfunction matchTargetList<T>(\n targets: Packed.TargetList,\n params: EvaluationParams<T>,\n): boolean {\n for (const kind in targets) {\n const attributes = targets[kind]!;\n for (const attribute in attributes) {\n const entity = access([kind, attribute], params);\n if (isString(entity) && attributes[attribute]!.includes(entity))\n return true;\n }\n }\n return false;\n}\n\nfunction matchSegment<T>(segment: Packed.Segment, params: EvaluationParams<T>) {\n if (segment.include && matchTargetList(segment.include, params)) return true;\n if (segment.exclude && matchTargetList(segment.exclude, params)) return false;\n if (!segment.rules?.length) return false;\n\n const firstMatchingRule = segment.rules.find((rule) =>\n matchConditions(rule.conditions, params),\n );\n\n if (!firstMatchingRule) return false;\n\n return handleSegmentOutcome(params, firstMatchingRule.outcome);\n}\n\nfunction matchSegmentCondition<T>(\n cmp: Comparator,\n rhs: Packed.RHS,\n params: EvaluationParams<T>,\n) {\n switch (cmp) {\n case Comparator.EQ: {\n const segment = params.segments?.[rhs as string];\n if (!segment) return false;\n return matchSegment<T>(segment, params);\n }\n case Comparator.NOT_EQ: {\n const segment = params.segments?.[rhs as string];\n if (!segment) return false;\n return !matchSegment<T>(segment, params);\n }\n case Comparator.ONE_OF: {\n if (!isArray(rhs)) return false;\n const segmentIds = rhs;\n return segmentIds.some((segmentId) => {\n const segment = params.segments?.[segmentId];\n if (!segment) return false;\n return matchSegment<T>(segment, params);\n });\n }\n case Comparator.NOT_ONE_OF: {\n const segmentIds = rhs as string[];\n return segmentIds.every((segmentId) => {\n const segment = params.segments?.[segmentId];\n if (!segment) return false;\n return !matchSegment<T>(segment, params);\n });\n }\n default:\n throw new Error(`Comparator ${cmp} not implemented for segment`);\n }\n}\n\nfunction matchConditions<T>(\n conditions: Packed.Condition[],\n params: EvaluationParams<T>,\n): boolean {\n return conditions.every((condition) => {\n const [lhsAccessor, cmpKey, rawRhs, options] = condition;\n const hasIgnoreCaseFlag =\n typeof options === 'string' && options.includes('i');\n const hasIgnoreCaseOption =\n typeof options === 'object' && options !== null && options.i === true;\n const ignoreCase =\n IGNORE_CASE_COMPARATORS.has(cmpKey) &&\n (hasIgnoreCaseFlag || hasIgnoreCaseOption);\n\n // ignoreCase is not applicable to segment conditions (segments are internal IDs)\n if (lhsAccessor === Packed.AccessorType.SEGMENT) {\n return rawRhs && matchSegmentCondition(cmpKey, rawRhs, params);\n }\n\n const lhs = ignoreCase\n ? lower(access(lhsAccessor, params))\n : access(lhsAccessor, params);\n const rhs = ignoreCase ? lower(rawRhs) : rawRhs;\n\n try {\n switch (cmpKey) {\n case Comparator.EQ:\n return lhs === rhs;\n case Comparator.NOT_EQ:\n return lhs !== rhs;\n case Comparator.ONE_OF:\n return isArray(rhs) && rhs.includes(lhs);\n case Comparator.NOT_ONE_OF:\n // lhs would be undefined when the value was not provided, in which\n // case we should not match the rule\n return (\n isArray(rhs) && typeof lhs !== 'undefined' && !rhs.includes(lhs)\n );\n case Comparator.CONTAINS_ALL_OF: {\n if (!Array.isArray(rhs) || !Array.isArray(lhs)) return false;\n\n const lhsSet = new Set(lhs.filter(isString));\n\n // try to use a set if the lhs is a list of strings - O(1)\n // otherwise we need to iterate over the values - O(n)\n if (lhsSet.size === lhs.length) {\n return rhs.filter(isString).every((item) => lhsSet.has(item));\n }\n\n // this shouldn't happen since we only allow string[] on the lhs\n return rhs.every((item) => lhs.includes(item));\n }\n case Comparator.CONTAINS_ANY_OF: {\n if (!Array.isArray(rhs) || !Array.isArray(lhs)) return false;\n\n const rhsSet = new Set(rhs.filter(isString));\n return lhs.some(\n rhsSet.size === rhs.length\n ? // try to use a set if the rhs is a list of strings - O(1)\n (item) => rhsSet.has(item)\n : // otherwise we need to iterate over the values - O(n)\n (item) => rhs.includes(item),\n );\n }\n case Comparator.CONTAINS_NONE_OF: {\n // if the rhs is not an array something went wrong and we should not match\n if (!Array.isArray(rhs)) return false;\n\n // if it's not an array it doesn't contain any of the values\n if (!Array.isArray(lhs)) return true;\n\n const rhsSet = new Set(rhs.filter(isString));\n return lhs.every(\n rhsSet.size === rhs.length\n ? // try to use a set if the rhs is a list of strings - O(1)\n (item) => !rhsSet.has(item)\n : // otherwise we need to iterate over the values - O(n)\n (item) => !rhs.includes(item),\n );\n }\n case Comparator.STARTS_WITH:\n return isString(lhs) && isString(rhs) && lhs.startsWith(rhs);\n case Comparator.NOT_STARTS_WITH:\n return isString(lhs) && isString(rhs) && !lhs.startsWith(rhs);\n case Comparator.ENDS_WITH:\n return isString(lhs) && isString(rhs) && lhs.endsWith(rhs);\n case Comparator.NOT_ENDS_WITH:\n return isString(lhs) && isString(rhs) && !lhs.endsWith(rhs);\n case Comparator.CONTAINS:\n return isString(lhs) && isString(rhs) && lhs.includes(rhs);\n case Comparator.NOT_CONTAINS:\n return isString(lhs) && isString(rhs) && !lhs.includes(rhs);\n case Comparator.EXISTS:\n return lhs !== undefined && lhs !== null;\n case Comparator.NOT_EXISTS:\n return lhs === undefined || lhs === null;\n case Comparator.GT:\n // NaN will return false for any comparisons\n if (lhs === null || lhs === undefined) return false;\n return (isNumber(rhs) || isString(rhs)) && lhs > rhs;\n case Comparator.GTE:\n if (lhs === null || lhs === undefined) return false;\n return (isNumber(rhs) || isString(rhs)) && lhs >= rhs;\n case Comparator.LT:\n if (lhs === null || lhs === undefined) return false;\n return (isNumber(rhs) || isString(rhs)) && lhs < rhs;\n case Comparator.LTE:\n if (lhs === null || lhs === undefined) return false;\n return (isNumber(rhs) || isString(rhs)) && lhs <= rhs;\n case Comparator.REGEX:\n if (\n isString(lhs) &&\n lhs.length <= MAX_REGEX_INPUT_LENGTH &&\n typeof rhs === 'object' &&\n !Array.isArray(rhs) &&\n rhs?.type === 'regex'\n ) {\n return getCompiledRegex(rhs).test(lhs);\n }\n return false;\n\n case Comparator.NOT_REGEX:\n if (\n isString(lhs) &&\n lhs.length <= MAX_REGEX_INPUT_LENGTH &&\n typeof rhs === 'object' &&\n !Array.isArray(rhs) &&\n rhs?.type === 'regex'\n ) {\n return !getCompiledRegex(rhs).test(lhs);\n }\n return false;\n case Comparator.BEFORE: {\n if (!isString(lhs) || !isString(rhs)) return false;\n const a = new Date(lhs);\n const b = new Date(rhs);\n // if any date fails to parse getTime will return NaN, which will cause\n // comparisons to fail.\n return a.getTime() < b.getTime();\n }\n case Comparator.AFTER: {\n if (!isString(lhs) || !isString(rhs)) return false;\n const a = new Date(lhs);\n const b = new Date(rhs);\n return a.getTime() > b.getTime();\n }\n default: {\n const _x: never = cmpKey; // exhaustive check\n return false;\n }\n }\n } catch (error) {\n console.error('flags: Error matching conditions', error);\n return false;\n }\n });\n}\n\nfunction sum(list: number[]) {\n return list.reduce((acc, n) => acc + n, 0);\n}\n\nfunction handleSegmentOutcome<T>(\n params: EvaluationParams<T>,\n outcome: Packed.SegmentOutcome,\n) {\n // when everyone is flagged in the segment we can return true immediately\n if (outcome === 1) return true;\n\n switch (outcome.type) {\n case 'split': {\n const lhs = access(outcome.base, params);\n\n // exclude from segment if the lhs is not a string\n if (typeof lhs !== 'string') return false;\n\n // bypass hashing for common values and edges\n if (outcome.passPromille <= 0) return false;\n if (outcome.passPromille >= PROMILLE_SCALE) return true;\n\n const bucket = hashInput(lhs, params.definition.seed);\n return bucket < boundaryFor(outcome.passPromille, PROMILLE_SCALE);\n }\n default: {\n const { type } = outcome;\n exhaustivenessCheck(type);\n }\n }\n}\n\nfunction getVariant<T>(\n definition: Packed.FlagDefinition,\n index: number,\n): { value: T; variantId: VariantId | null } {\n const { variants, variantIds } = definition;\n\n if (index < 0 || index >= variants.length) {\n throw new Error(\n `@vercel/flags-core: Invalid variant index ${index}, variants length is ${variants.length}`,\n );\n }\n\n let variantId: VariantId | null = null;\n if (variantIds && index < variantIds.length) {\n variantId = variantIds[index] ?? null;\n }\n\n return {\n value: variants[index] as T,\n variantId,\n };\n}\n\ntype WeightedAssignment = {\n base: Packed.EntityAccessor;\n weights: number[];\n defaultVariant: Packed.VariantIndex;\n};\n\nfunction getWeightedVariantIndex<T>(\n params: EvaluationParams<T>,\n assignment: WeightedAssignment,\n seed: number | undefined,\n): Packed.VariantIndex {\n const lhs = access(assignment.base, params);\n\n if (typeof lhs !== 'string') return assignment.defaultVariant;\n\n const bucket = hashInput(lhs, seed);\n const boundaries = getSplitBoundaries(assignment);\n for (let index = 0; index < boundaries.length; index++) {\n if (bucket < (boundaries[index] as number)) return index;\n }\n\n // Only reached when the weights sum to 0 (every boundary is NaN).\n return assignment.defaultVariant;\n}\n\nfunction experimentAssignment(\n experiment: Packed.experimental_ExperimentDefinition,\n variantId: VariantId | null,\n assignmentReason: experimental_ExperimentAssignmentReason,\n): experimental_ExperimentAssignment | undefined {\n if (variantId === null) return undefined;\n return {\n id: experiment.id,\n variantId,\n base: experiment.base,\n rampId: experiment.rampId,\n rampPercentage: experiment.rampPercentage,\n assignmentReason,\n };\n}\n\nfunction outcomeAssignmentReason(\n outcome: Packed.Outcome,\n): experimental_ExperimentAssignmentReason {\n if (typeof outcome === 'number') return 'variant';\n switch (outcome.type) {\n case 'experiment':\n return 'experiment';\n case 'split':\n return 'split';\n case 'rollout':\n return 'rollout';\n default: {\n const { type } = outcome;\n return exhaustivenessCheck(type);\n }\n }\n}\n\nfunction resolveOutcome<T>(\n params: EvaluationParams<T>,\n outcome: Packed.Outcome,\n): {\n value: T;\n outcomeType: OutcomeType;\n variantId: VariantId | null;\n experiment?: experimental_ExperimentAssignment;\n} {\n if (typeof outcome === 'number') {\n const variant = getVariant<T>(params.definition, outcome);\n return {\n ...variant,\n outcomeType: OutcomeType.VALUE,\n };\n }\n switch (outcome.type) {\n case 'split': {\n const index = getWeightedVariantIndex(\n params,\n outcome,\n params.definition.seed,\n );\n return {\n ...getVariant<T>(params.definition, index),\n outcomeType: OutcomeType.SPLIT,\n };\n }\n case 'experiment': {\n const experiment = params.definition.experiment;\n if (!experiment) {\n throw new Error('@vercel/flags-core: Experiment not found');\n }\n\n const unitValue = access(experiment.base, params);\n const defaultVariant = getVariant<T>(\n params.definition,\n experiment.defaultVariant,\n );\n const assignment = (\n variant: typeof defaultVariant,\n assignmentReason: experimental_ExperimentAssignmentReason,\n ) => ({\n ...variant,\n outcomeType: OutcomeType.EXPERIMENT,\n experiment: experimentAssignment(\n experiment,\n variant.variantId,\n assignmentReason,\n ),\n });\n\n if (typeof unitValue !== 'string') {\n return assignment(defaultVariant, 'not-enrolled');\n }\n\n const rampPercentage = experiment.rampPercentage ?? 100;\n const enrolled =\n rampPercentage >= 100 ||\n (rampPercentage > 0 &&\n hashInput(unitValue, experiment.enrollmentSeed) <\n boundaryFor(rampPercentage, 100));\n if (!enrolled) return assignment(defaultVariant, 'not-enrolled');\n\n const index = getWeightedVariantIndex(\n params,\n experiment,\n params.definition.seed,\n );\n return assignment(getVariant<T>(params.definition, index), 'experiment');\n }\n case 'rollout': {\n const lhs = access(outcome.base, params);\n const defaultOutcome = getVariant<T>(\n params.definition,\n outcome.defaultVariant,\n );\n\n // serve the default variant if the lhs is not a string\n if (typeof lhs !== 'string') {\n return { ...defaultOutcome, outcomeType: OutcomeType.ROLLOUT };\n }\n\n // Determine active slot based on elapsed time\n const now = Date.now();\n const elapsed = now - outcome.startTimestamp;\n\n const rollFromVariant = getVariant<T>(\n params.definition,\n outcome.rollFromVariant,\n );\n\n // Before rollout starts or no slots, serve rollFromVariant\n if (elapsed < 0 || outcome.slots.length === 0) {\n return {\n ...rollFromVariant,\n outcomeType: OutcomeType.ROLLOUT,\n };\n }\n\n // Walk slots to find current promille.\n // Each slot's durationMs is how long that slot is served before\n // moving to the next one. Once all slots are exhausted, serve\n // finalPromille (100% to rollToVariant by default).\n let cumulativeDuration = 0;\n let currentPromille = 0;\n let exhausted = true;\n for (const [promille, durationMs] of outcome.slots) {\n currentPromille = promille;\n cumulativeDuration += durationMs;\n if (cumulativeDuration > elapsed) {\n exhausted = false;\n break;\n }\n }\n if (exhausted) currentPromille = outcome.finalPromille ?? PROMILLE_SCALE;\n\n // short-circuit common edges\n if (currentPromille <= 0) {\n return {\n ...rollFromVariant,\n outcomeType: OutcomeType.ROLLOUT,\n };\n }\n const rollToVariant = getVariant<T>(\n params.definition,\n outcome.rollToVariant,\n );\n if (currentPromille >= PROMILLE_SCALE) {\n return {\n ...rollToVariant,\n outcomeType: OutcomeType.ROLLOUT,\n };\n }\n\n const bucket = hashInput(lhs, params.definition.seed);\n\n // Equivalent to the split { rollFrom: PROMILLE_SCALE - p, rollTo: p }: the\n // lower-index variant takes the low buckets, matching where the split\n // places it (see boundaryFor). So rollTo holds `[0, boundary(p))` when it\n // has the lower index, otherwise rollFrom holds the low buckets.\n if (outcome.rollToVariant < outcome.rollFromVariant) {\n const rollToBoundary = boundaryFor(currentPromille, PROMILLE_SCALE);\n return {\n ...(bucket < rollToBoundary ? rollToVariant : rollFromVariant),\n outcomeType: OutcomeType.ROLLOUT,\n };\n }\n const rollFromBoundary = boundaryFor(\n PROMILLE_SCALE - currentPromille,\n PROMILLE_SCALE,\n );\n return {\n ...(bucket < rollFromBoundary ? rollFromVariant : rollToVariant),\n outcomeType: OutcomeType.ROLLOUT,\n };\n }\n default: {\n const { type } = outcome;\n exhaustivenessCheck(type);\n }\n }\n}\n\nfunction handleOutcome<T>(\n params: EvaluationParams<T>,\n outcome: Packed.Outcome,\n assignmentReason?: experimental_ExperimentAssignmentReason,\n): {\n value: T;\n outcomeType: OutcomeType;\n variantId: VariantId | null;\n experiment?: experimental_ExperimentAssignment;\n} {\n const result = resolveOutcome(params, outcome);\n const experiment = params.definition.experiment;\n if (!experiment || result.experiment) return result;\n\n return {\n ...result,\n experiment: experimentAssignment(\n experiment,\n result.variantId,\n assignmentReason ?? outcomeAssignmentReason(outcome),\n ),\n };\n}\n\n/**\n * Evaluates a single feature flag.\n *\n * This function should never throw for expected errors, instead it returns\n * { reason: Reason.ERROR, errorMessage: ... }.\n *\n * The function can however throw for situations which should not happen under\n * normal circumstances, for example if the environment config is not found.\n */\nexport function evaluate<T>(\n /**\n * The params used for the evaluation\n */\n params: EvaluationParams<T>,\n /** Tracks visited environments to detect circular reuse. */\n _visited?: Set<string>,\n): EvaluationResult<T> {\n const envConfig = params.definition.environments[params.environment];\n\n // handle shortcut where a value is a number directly\n if (typeof envConfig === 'number') {\n return Object.assign(handleOutcome<T>(params, envConfig), {\n reason: ResolutionReason.PAUSED as const,\n }) satisfies EvaluationResult<T>;\n }\n\n if (!envConfig) {\n return {\n reason: ResolutionReason.ERROR,\n errorMessage: `Could not find envConfig for \"${params.environment}\"`,\n value: params.defaultValue,\n variantId: null,\n };\n }\n\n if ('reuse' in envConfig) {\n const reuseEnvConfig = params.definition.environments[envConfig.reuse];\n\n if (reuseEnvConfig === undefined) {\n // this is an unexpected error as this should have never been saved in\n // the first place\n throw new Error(\n `Could not find envConfig for \"${envConfig.reuse}\" when reusing`,\n );\n }\n\n const visited = _visited ?? new Set<string>();\n if (visited.has(envConfig.reuse)) {\n return {\n reason: ResolutionReason.ERROR,\n errorMessage: `Circular environment reuse detected: \"${envConfig.reuse}\"`,\n value: params.defaultValue,\n variantId: null,\n };\n }\n visited.add(params.environment);\n\n return evaluate<T>({ ...params, environment: envConfig.reuse }, visited);\n }\n\n if (envConfig.targets) {\n const matchedIndex = envConfig.targets.findIndex((targetList) =>\n matchTargetList(targetList, params),\n );\n\n if (matchedIndex > -1) {\n return Object.assign(handleOutcome<T>(params, matchedIndex, 'targeted'), {\n reason: ResolutionReason.TARGET_MATCH as const,\n }) satisfies EvaluationResult<T>;\n }\n }\n\n const firstMatchingRule = envConfig.rules\n ? envConfig.rules.find((rule) => matchConditions(rule.conditions, params))\n : undefined;\n\n if (firstMatchingRule) {\n return Object.assign(handleOutcome<T>(params, firstMatchingRule.outcome), {\n reason: ResolutionReason.RULE_MATCH as const,\n }) satisfies EvaluationResult<T>;\n }\n\n return Object.assign(handleOutcome<T>(params, envConfig.fallthrough), {\n reason: ResolutionReason.FALLTHROUGH as const,\n }) satisfies EvaluationResult<T>;\n}\n\nexport type BulkEvaluationInput<T> = {\n definition: Packed.FlagDefinition;\n defaultValue?: T;\n};\n\n/**\n * Evaluates multiple feature flags against the same entities, segments, and\n * environment.\n *\n * Reuses a single shared `EvaluationParams` object across flags so callers\n * avoid the overhead of constructing one per call (and don't need to spawn\n * parallel promises just to fan out independent sync evaluations).\n */\nexport function bulkEvaluate<T = unknown>(\n flags: Record<string, BulkEvaluationInput<T>>,\n shared: {\n entities?: Record<string, unknown>;\n environment: string;\n segments?: EvaluationParams<T>['segments'];\n },\n): Record<string, EvaluationResult<T>> {\n const params: EvaluationParams<T> = {\n entities: shared.entities,\n environment: shared.environment,\n segments: shared.segments,\n definition: undefined as unknown as Packed.FlagDefinition,\n defaultValue: undefined,\n };\n\n const results: Record<string, EvaluationResult<T>> = {};\n for (const key in flags) {\n const flag = flags[key]!;\n params.definition = flag.definition;\n params.defaultValue = flag.defaultValue;\n results[key] = evaluate<T>(params);\n }\n return results;\n}\n","export type MetricEnvironment = 'development' | 'preview' | 'production';\n\n/**\n * Options for stream connection behavior\n */\nexport type StreamOptions = {\n /** Timeout in ms to wait for initial stream connection before falling back */\n initTimeoutMs: number;\n};\n\n/**\n * Options for polling behavior\n */\nexport type PollingOptions = {\n /** Interval in ms between polling requests */\n intervalMs: number;\n /** Timeout in ms to wait for initial poll before falling back */\n initTimeoutMs: number;\n};\n\n/** Keeps an asynchronous task alive after a response has been sent */\nexport type WaitUntil = (promise: Promise<unknown>) => void;\n\n/** Input type for creating a datafile (without metrics) */\nexport type DatafileInput = Packed.Data & {\n /**\n * If a data source is used with a specific sdk key then\n * the sdk key or data source might contain information\n * about the environment to be evaluated\n */\n environment: string;\n /** Vercel project id of the source of these flags */\n projectId: string;\n /**\n * Some older responses might return a string instead of a number. Both will be timestamps.\n */\n configUpdatedAt?: number | string;\n /**\n * When this datafile was successfully fetched, as Unix epoch milliseconds.\n * Preserved when bundled, serialized, or supplied to another client.\n * Omit when the original fetch time is unknown; loading data does not reset it.\n */\n fetchedAt?: number;\n /** Version number of the data */\n revision?: number;\n};\n\n/** Datafile with metrics attached (returned by the client) */\nexport type Datafile = DatafileInput & {\n /** Metrics about how the data was retrieved */\n metrics: Metrics;\n};\n\n/** Flag Definitions of a Vercel project */\nexport type BundledDefinitions = DatafileInput & {\n /** when the data was last updated */\n configUpdatedAt: number;\n /** hash of the data */\n digest: string;\n /** version number of the data */\n revision: number;\n};\n\nexport type BundledDefinitionsResult =\n | { definitions: BundledDefinitions; state: 'ok' }\n | { definitions: null; state: 'missing-file' | 'missing-entry' }\n | { definitions: null; state: 'unexpected-error'; error: unknown };\n\n/**\n * Metrics about how data was retrieved and evaluated\n */\nexport type Metrics = {\n /** Time in ms to read the datafile */\n readMs: number;\n /** Where the data came from */\n source: 'in-memory' | 'embedded' | 'remote';\n /** Whether data was already cached, or stale (fallback used) */\n cacheStatus: 'HIT' | 'MISS' | 'STALE';\n /** Whether the stream is currently connected */\n connectionState: 'connected' | 'disconnected';\n /** The current operating mode of the client */\n mode: 'streaming' | 'polling' | 'build' | 'vercel' | 'offline';\n /** Time in ms for the pure flag evaluation logic (only present on EvaluationResult) */\n evaluationMs?: number;\n};\n\n/**\n * DataSource interface for the Vercel Flags client\n */\nexport interface ControllerInterface {\n /**\n * Initialize the data source by fetching the initial file or setting up polling or\n * subscriptions.\n *\n * @see https://openfeature.dev/specification/sections/providers#requirement-241\n */\n initialize: () => Promise<void>;\n\n /**\n * Returns the in-memory data file, which was loaded from initialize and maybe updated from streams.\n */\n read(): Promise<Datafile>;\n\n /**\n * End polling or subscriptions. Flush any remaining data.\n */\n shutdown(): void;\n\n /**\n * Return the actual datafile containing flag definitions.\n */\n getDatafile(): Promise<Datafile>;\n\n /**\n * Returns the bundled fallback definitions.\n * Throws FallbackNotFoundError if the fallback file doesn't exist.\n * Throws FallbackEntryNotFoundError if the file exists but has no entry for the SDK key.\n */\n getFallbackDatafile?(): Promise<BundledDefinitions>;\n}\n\nexport type Source = {\n orgId: string;\n orgSlug: string;\n projectId: string;\n projectSlug: string;\n};\n\n/**\n * Input for a single flag in a bulk evaluation call.\n */\nexport type BulkEvaluateInput<T = Value> = {\n key: string;\n defaultValue?: T;\n};\n\n/** Options that control side effects of an evaluation call. */\nexport type experimental_EvaluationOptions = {\n /**\n * Whether experiment exposures should be reported for this evaluation.\n * @default true\n */\n experimental_exposureLogging?: boolean;\n};\n\nexport type experimental_ExperimentAssignmentReason =\n | 'experiment'\n | 'not-enrolled'\n | 'targeted'\n | 'split'\n | 'variant'\n | 'rollout'\n | 'override';\n\n/** Information about the experiment linked to an evaluated flag value. */\nexport type experimental_ExperimentAssignment = {\n /** Experiment identifier. */\n id: string;\n /** Identifier of the selected experiment variant. */\n variantId: string;\n /** Entity path on which the experiment assignment is based. */\n base: Packed.EntityAccessor;\n /** Identifier of the ramp active for this assignment. */\n rampId?: string;\n /** Percentage of eligible units included in the ramp, from 0 through 100. */\n rampPercentage?: number;\n /** How this evaluation received its value. */\n assignmentReason: experimental_ExperimentAssignmentReason;\n};\n\n/** An experiment exposure passed to a client's exposure reporter. */\nexport type experimental_Exposure = {\n /** Flag whose evaluation produced the exposure. */\n flagKey: FlagKey;\n /** Experiment identifier. */\n experimentId: string;\n /** Identifier of the selected experiment variant. */\n variantId: string | null;\n /** Entity path on which the experiment assignment is based. */\n base: Packed.EntityAccessor;\n /** Identifier of the ramp active for this assignment. */\n rampId?: string;\n /** Percentage of eligible units included in the ramp, from 0 through 100. */\n rampPercentage?: number;\n /** How this evaluation received its value. */\n assignmentReason: experimental_ExperimentAssignmentReason;\n};\n\n/** Reports experiment exposures produced by one evaluation call. */\nexport type experimental_ReportExposures<Entity = Record<string, unknown>> = (\n exposures: readonly experimental_Exposure[],\n entity: Readonly<Entity>,\n) => void | Promise<void>;\n\n/**\n * A client for Vercel Flags\n */\nexport type FlagsClient<Entities = Record<string, unknown>> = {\n /**\n * Origin information for this client.\n * sdkKey is only present when the client was explicitly created with one.\n * projectId is only present when the client reads another project's flags.\n */\n origin?: {\n provider: string;\n sdkKey?: string;\n projectId?: string;\n };\n /**\n * Evaluate a feature flag.\n *\n * Initializes the client automatically on first use. Initialization failures\n * are caught so evaluation can try its fallback sources and defaultValue.\n * With request-scoped Vercel OIDC, call this inside a request handler.\n *\n * @param flagKey\n * @param defaultValue\n * @param entities\n * @param options Evaluation side-effect options.\n * @returns\n */\n evaluate: <T = Value, E = Entities>(\n flagKey: string,\n defaultValue?: T,\n entities?: E,\n options?: experimental_EvaluationOptions,\n ) => Promise<EvaluationResult<T>>;\n /**\n * Evaluate multiple feature flags against the same entities in a single call.\n *\n * Avoids the per-flag overhead of separate `evaluate()` invocations (in particular,\n * the parallel promises and repeated datafile reads they would entail).\n *\n * Initializes the client automatically on first use. Initialization failures\n * are caught so evaluation can try its fallback sources and default values.\n * With request-scoped Vercel OIDC, call this inside a request handler.\n *\n * @param flags Array of `{ key, defaultValue? }` entries to evaluate.\n * @param entities Shared entities used for every flag in the bulk call.\n * @param options Evaluation side-effect options.\n * @returns Object mapping each key to its EvaluationResult.\n */\n bulkEvaluate: <T = Value, E = Entities>(\n flags: BulkEvaluateInput<T>[],\n entities?: E,\n options?: experimental_EvaluationOptions,\n ) => Promise<Record<string, EvaluationResult<T>>>;\n /**\n * Report a Flags SDK override without evaluating the provider value.\n *\n * @remarks This API is not supported for general use yet. Do not use it\n * unless Vercel has explicitly enabled it for you.\n */\n experimental_reportOverride?: <T = Value, E = Entities>(params: {\n key: string;\n value: T;\n entities?: E;\n }) => Promise<void>;\n /**\n * Load flag definitions and set up subscriptions if needed.\n *\n * Optional: evaluate() and bulkEvaluate() initialize automatically. This method\n * rejects if initialization fails, while evaluation can still try fallbacks.\n * With request-scoped Vercel OIDC, call this inside a request handler, not at\n * module scope. Creating the client at module scope is safe; starting and\n * caching an initialization promise there can run before a token is available.\n */\n initialize(): void | Promise<void>;\n /**\n * Facilitates a clean shutdown process which may include flushing telemetry information, or closing remote connections.\n */\n shutdown(): void | Promise<void>;\n /**\n * Returns the actual datafile containing flag definitions\n */\n getDatafile(): Promise<Datafile>;\n /**\n * Returns the bundled fallback definitions.\n * Throws FallbackNotFoundError if the fallback file doesn't exist.\n * Throws FallbackEntryNotFoundError if the file exists but has no entry for the SDK key.\n */\n getFallbackDatafile(): Promise<BundledDefinitions>;\n};\n\nexport type EvaluationParams<T> = {\n entities?: Record<string, unknown>;\n environment: string;\n segments?: Record<SegmentId, Packed.Segment>;\n definition: Packed.FlagDefinition;\n defaultValue?: T;\n};\n\n// Copied from the OpenFeature ErrorCode and commented out unused types\n/**\n * ErrorCodes that can happen during evaluation\n */\nexport enum ErrorCode {\n /**\n * The value was resolved before the provider was ready.\n */\n // PROVIDER_NOT_READY = 'PROVIDER_NOT_READY',\n /**\n * The provider has entered an irrecoverable error state.\n */\n // PROVIDER_FATAL = 'PROVIDER_FATAL',\n /**\n * The flag could not be found.\n */\n FLAG_NOT_FOUND = 'FLAG_NOT_FOUND',\n /**\n * An error was encountered parsing data, such as a flag configuration.\n */\n // PARSE_ERROR = 'PARSE_ERROR',\n /**\n * The type of the flag value does not match the expected type.\n */\n // TYPE_MISMATCH = 'TYPE_MISMATCH',\n /**\n * The provider requires a targeting key and one was not provided in the evaluation context.\n */\n // TARGETING_KEY_MISSING = 'TARGETING_KEY_MISSING',\n /**\n * The evaluation context does not meet provider requirements.\n */\n // INVALID_CONTEXT = 'INVALID_CONTEXT',\n /**\n * An error with an unspecified code.\n */\n // GENERAL = 'GENERAL',\n}\n\n/**\n * The detailed result of a flag evaluation as returned by the client's `evaluate` function.\n */\nexport type EvaluationResult<T> =\n | {\n /**\n * In case of successful evaluations this holds the evaluated value\n */\n value: T;\n /**\n * Indicates whether the outcome was a single variant or a split\n */\n outcomeType?: OutcomeType;\n /**\n * The variant we want to report for o11y\n */\n variantId: VariantId | null;\n /** Experiment metadata when the flag is linked to an experiment. */\n experiment?: experimental_ExperimentAssignment;\n /**\n * Indicates why the flag evaluated to a certain value\n */\n reason: Exclude<ResolutionReason, ResolutionReason.ERROR>;\n errorMessage?: never;\n errorCode?: never;\n /** Metrics about the evaluation (optional) */\n metrics?: Metrics;\n }\n | {\n reason: ResolutionReason.ERROR;\n errorMessage: string;\n errorCode?: ErrorCode;\n outcomeType?: never;\n experiment?: never;\n /**\n * The variant we want to report for o11y\n */\n variantId: VariantId | null;\n /**\n * In cases of errors this is the defaultValue if one was provided\n */\n value?: T;\n /** Metrics about the evaluation (optional) */\n metrics?: Metrics;\n };\n\nexport type FlagKey = string;\nexport type VariantId = string;\nexport type EnvironmentKey = string;\nexport type SegmentId = string;\n\n/**\n * The value of a feature flag variant\n */\nexport type Value =\n | string\n | number\n | boolean\n | null\n | { [key: string]: Value }\n | Value[];\n\nexport enum ResolutionReason {\n PAUSED = 'paused',\n TARGET_MATCH = 'target_match',\n RULE_MATCH = 'rule_match',\n FALLTHROUGH = 'fallthrough',\n ERROR = 'error',\n}\n\nexport enum OutcomeType {\n /** When the outcome type was a single variant */\n VALUE = 'value',\n /** When the outcome type was a split */\n SPLIT = 'split',\n /** When the outcome type was a progressive rollout */\n ROLLOUT = 'rollout',\n /** When the experiment assignment mechanism produced the value */\n EXPERIMENT = 'experiment',\n}\n\n/**\n * Vercel Flags\n * - is equal to (eq)\n * - is not equal to (!eq)\n * - is one of (oneOf)\n * - is not one of (!oneOf)\n * - contains (contains)\n * - does not contain (!contains)\n * - starts with (startsWith)\n * - does not start with (!startsWith)\n * - ends with (endsWith)\n * - does not end with (!endsWith)\n * - exists (ex)\n * - does not exist (!ex)\n * - is greater than (gt)\n * - is greater than or equal to (gte)\n * - is lower than (lt)\n * - is lower than or equal to (lte)\n * - matches regex (regex)\n * - does not match regex (!regex)\n * - is before (before)\n * - is after (after)\n */\n\nexport enum Comparator {\n /**\n * lhs must be string | number\n * rhs must be string | number\n * does a strict equality check\n */\n EQ = 'eq',\n /**\n * lhs must be string | number\n * rhs must be string | number\n * does a strict equality check\n */\n NOT_EQ = '!eq',\n /**\n * lhs must be string\n * rhs must be string[]\n */\n ONE_OF = 'oneOf',\n /**\n * lhs must be string\n * rhs must be string[]\n */\n NOT_ONE_OF = '!oneOf',\n /**\n * lhs must be string[]\n * rhs must be string[]\n */\n CONTAINS_ALL_OF = 'containsAllOf',\n /**\n * lhs must be string[]\n * rhs must be string[]\n */\n CONTAINS_ANY_OF = 'containsAnyOf',\n /**\n * lhs must be string[]\n * rhs must be string[]\n */\n CONTAINS_NONE_OF = 'containsNoneOf',\n /**\n * lhs must be string\n * rhs must be string\n *\n * other comparisons have to be handled with a regex\n */\n STARTS_WITH = 'startsWith',\n /**\n * lhs must be string\n * rhs must be string\n *\n * other comparisons have to be handled with a regex\n */\n NOT_STARTS_WITH = '!startsWith',\n /**\n * lhs must be string\n * rhs must be string\n *\n * other comparisons have to be handled with a regex\n */\n ENDS_WITH = 'endsWith',\n /**\n * lhs must be string\n * rhs must be string\n *\n * other comparisons have to be handled with a regex\n */\n NOT_ENDS_WITH = '!endsWith',\n /**\n * lhs must be string\n * rhs must be string\n *\n * checks if lhs contains rhs as a substring\n */\n CONTAINS = 'contains',\n /**\n * lhs must be string\n * rhs must be string\n *\n * checks if lhs does not contain rhs as a substring\n */\n NOT_CONTAINS = '!contains',\n /**\n * lhs must be string\n * rhs must be never\n */\n EXISTS = 'ex',\n /**\n * lhs must be string\n * rhs must be never\n */\n NOT_EXISTS = '!ex',\n /**\n * lhs must be string | number\n * rhs must be string | number\n */\n GT = 'gt',\n /**\n * lhs must be string | number\n * rhs must be string | number\n */\n GTE = 'gte',\n /** */\n /**\n * lhs must be string | number\n * rhs must be string | number\n */\n LT = 'lt',\n /**\n * lhs must be string | number\n * rhs must be string | number\n */\n LTE = 'lte',\n /**\n * lhs must be string\n * rhs must be string\n */\n REGEX = 'regex',\n /**\n * lhs must be string\n * rhs must be string\n */\n NOT_REGEX = '!regex',\n /**\n * lhs must be date string\n * rhs must be date string\n *\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#date_time_string_format\n */\n BEFORE = 'before',\n /**\n * lhs must be date string\n * rhs must be date string\n *\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#date_time_string_format\n */\n AFTER = 'after',\n}\n\n// -----------------------------------------------------------------------------\n// Original data\n// -----------------------------------------------------------------------------\n\nexport namespace Original {\n export type Data = {\n definitions: Record<string, FlagDefinition>;\n segments?: Original.Segment[];\n };\n\n export enum AccessorType {\n SEGMENT = 'segment',\n ENTITY = 'entity',\n }\n\n export type SegmentOutcome = SegmentAllOutcome | SegmentSplitOutcome;\n\n export type Outcome =\n | {\n type: 'variant';\n variantId: VariantId;\n }\n | {\n type: 'split';\n /**\n * Based on which attribute the traffic should be split\n */\n base: EntityAccessor;\n /**\n * The distribution for each variant\n */\n weights: Record<VariantId, number>;\n /**\n * This variant will be used when the base attribute does not exist\n */\n defaultVariantId: VariantId;\n }\n | {\n type: 'rollout';\n /**\n * Based on which attribute the traffic should be split\n */\n base: EntityAccessor;\n /**\n * Epoch ms when the rollout begins\n */\n startTimestamp: number;\n /**\n * The variant to roll away from\n */\n rollFromVariantId: VariantId;\n /**\n * The variant to roll towards\n */\n rollToVariantId: VariantId;\n /**\n * This variant will be used when the base attribute does not exist\n */\n defaultVariantId: VariantId;\n /**\n * Progressive rollout slots. Each slot has a promille value (0-100_000)\n * representing the traffic for rollToVariant and a duration (how long\n * it is served before moving to the next slot).\n * 1 = 0.001%\n * 1_000 = 1%\n * 100_000 = 100%\n *\n * Once all slots are exhausted, serve finalPromille (100% by default).\n */\n slots: { promille: number; durationMs: number }[];\n /**\n * Traffic for rollToVariant after all slots are exhausted (0-100_000).\n * Defaults to 100_000 (100%). For example, 50_000 ends at 50%.\n * Before the start or when slots is empty, serve rollFromVariant.\n */\n finalPromille?: number;\n }\n | {\n type: 'experiment';\n };\n\n export type experimental_ExperimentDefinition = {\n id: string;\n /** Entity attribute used as the experiment unit. */\n base: EntityAccessor;\n /** Distribution keyed by flag variant ID. */\n weights: Record<VariantId, number>;\n /** Flag variant used when the base attribute does not exist. */\n defaultVariantId: VariantId;\n /** Stable seed used only for experiment enrollment. */\n enrollmentSeed: number;\n rampId?: string;\n /** Percentage from 0 through 100. */\n rampPercentage?: number;\n };\n\n export type SegmentAllOutcome = {\n type: 'all';\n };\n\n export type SegmentSplitOutcome = {\n type: 'split';\n /**\n * Based on which attribute the passing percentage should be split.\n *\n * When the attribute does not exist the segment will not match.\n */\n base: EntityAccessor;\n /**\n * The promille that should pass the segment\n * 1 = 0.001%\n * 1_000 = 1%\n * 100_000 = 100%\n */\n passPromille: number;\n };\n\n export type EntityAccessor = {\n type: AccessorType.ENTITY;\n kind: string;\n attribute: string;\n };\n export type SegmentAccessor = { type: AccessorType.SEGMENT };\n\n export type List = {\n // backwards compatibility, we should only use \"list\" going forward\n type: 'list/inline' | 'list';\n items: { note?: string; value: string | number }[];\n id?: never;\n };\n\n export type LHS = SegmentAccessor | EntityAccessor;\n export type RHS =\n | string\n | number\n | boolean\n | List\n | { type: 'regex'; pattern: string; flags: string };\n\n export type Condition = {\n lhs: LHS;\n cmp: Comparator;\n rhs: RHS;\n /** When true, string comparisons are case-insensitive. */\n i?: boolean;\n };\n\n export type Rule = {\n conditions: Condition[];\n outcome: Outcome;\n };\n\n export type SegmentRule = {\n conditions: Condition[];\n outcome: SegmentAllOutcome | SegmentSplitOutcome;\n };\n\n export type FlagVariant = {\n id: string;\n label?: string;\n description?: string;\n value: Value;\n };\n\n export type EnvironmentConfig = {\n active: boolean;\n pausedOutcome: Outcome;\n /**\n * If enabled, the flag will be reused from the given environment.\n *\n * The flag will not be evaluated, and the outcome will be the same as the given environment.\n * This environment must be active and the flag must be active in this environment.\n */\n reuse: {\n active: boolean;\n environment: EnvironmentKey;\n };\n targets: Record<VariantId, TargetList>;\n rules: Rule[];\n fallthrough: Outcome;\n };\n\n /**\n * A list of targets\n *\n * @example\n * {\n * user: { id: { note?: string; value: string }[] }\n * }\n */\n export type TargetList = Record<\n string,\n Record<string, { note?: string; value: string }[]>\n >;\n\n /**\n * reusable conditions, with no outcome attached\n */\n export type Segment = {\n id: string;\n rules: SegmentRule[];\n /**\n * Explicitly include targets. Included targets will bypass conditions and exclusion.\n *\n * @example\n * include: {\n * user: { id: { note?: string, value: string }[] }\n * }\n */\n include: TargetList;\n /**\n * Explicitly exclude targets. Excluded targets will not be included in the segment, and bypass conditions.\n *\n * @example\n * exclude: {\n * user: { id: { note?: string, value: string }[] }\n * }\n */\n exclude: TargetList;\n };\n\n export type FlagDefinition = {\n variants: FlagVariant[];\n /** Experiment linked to this flag. */\n experiment?: experimental_ExperimentDefinition;\n environments: Record<EnvironmentKey, EnvironmentConfig>;\n\n /**\n * A random seed to prevent split points in different flags\n * from having the same targets. Otherwise the same set of ids would be\n * opted into all flags for every rollout. By using a different seed for\n * each flag the distribution is different for every flag.\n *\n * We don't use the slug as it might change, but we don't want the distribution\n * to change when the slug changes.\n *\n * We don't use the id or createdAt etc as we want to be able to redistirbute\n * by changing the seed.\n */\n seed: number;\n };\n}\n\n// -----------------------------------------------------------------------------\n// Packed data\n// -----------------------------------------------------------------------------\n\nexport namespace Packed {\n /**\n * Idenitifies a variant based on its index in the variants array.\n */\n export type VariantIndex = number;\n\n export type Data = {\n /** map of flag keys to definitions */\n definitions: Record<FlagKey, FlagDefinition>;\n /** segments keyed by id */\n segments?: Record<SegmentId, Segment>;\n };\n\n export enum AccessorType {\n SEGMENT = 'segment',\n ENTITY = 'entity',\n }\n\n export type SplitOutcome = {\n type: 'split';\n /**\n * Based on which attribute the traffic should be split.\n */\n base: EntityAccessor;\n /**\n * The distribution of the individual groups.\n *\n * We use a single number array as the numbers will be placed in the\n * same order as the variant list.\n *\n * So index 0 here is the distribution for variant 0, and so on.\n */\n weights: number[];\n /**\n * This variant will be used when the lhs does not exist\n */\n defaultVariant: VariantIndex;\n };\n\n export type RolloutOutcome = {\n type: 'rollout';\n /**\n * Based on which attribute the traffic should be split.\n */\n base: EntityAccessor;\n /**\n * Epoch ms when the rollout begins.\n */\n startTimestamp: number;\n /**\n * Variant index to roll away from.\n */\n rollFromVariant: VariantIndex;\n /**\n * Variant index to roll towards.\n */\n rollToVariant: VariantIndex;\n /**\n * This variant will be used when the base attribute does not exist.\n */\n defaultVariant: VariantIndex;\n /**\n * Progressive rollout slots.\n * Each tuple: [promille 0-100_000 for rollToVariant, durationMs (how long this slot is served)]\n * 1 = 0.001%\n * 1_000 = 1%\n * 100_000 = 100%\n *\n * Once all slots are exhausted, serve finalPromille (100% by default).\n */\n slots: [number, number][];\n /**\n * Traffic for rollToVariant after all slots are exhausted (0-100_000).\n * Defaults to 100_000 (100%). For example, 50_000 ends at 50%.\n * Before the start or when slots is empty, serve rollFromVariant.\n */\n finalPromille?: number;\n };\n\n export type experimental_ExperimentDefinition = {\n /** Experiment identifier. */\n id: string;\n /** Entity path used as the experiment unit. */\n base: EntityAccessor;\n /** Distribution indexed by the corresponding flag variant. */\n weights: number[];\n /** Flag variant used when the experiment base is unavailable. */\n defaultVariant: VariantIndex;\n /** Stable seed used only for experiment enrollment. */\n enrollmentSeed: number;\n /** Identifier of the ramp active for this experiment. */\n rampId?: string;\n /** Percentage of eligible units included in the ramp, from 0 through 100. */\n rampPercentage?: number;\n };\n\n export type SegmentAllOutcome = 1;\n\n export type SegmentSplitOutcome = {\n type: 'split';\n /**\n * Based on which attribute the traffic should be split.\n *\n * When the attribute does not exist the segment will not match.\n */\n base: EntityAccessor;\n /**\n * The promille that should pass the segment (1 = 0.001%; 1000 = 1%)\n */\n passPromille: number;\n };\n\n export type SegmentOutcome = SegmentAllOutcome | SegmentSplitOutcome;\n\n export type experimental_ExperimentOutcome = { type: 'experiment' };\n\n export type Outcome =\n | VariantIndex\n | SplitOutcome\n | RolloutOutcome\n | experimental_ExperimentOutcome;\n\n // an array means it's an entity, the string \"segment\" means a segment\n export type EntityAccessor = (string | number)[];\n export type SegmentAccessor = 'segment';\n\n /**\n * An array means an entity\n */\n export type LHS = EntityAccessor | SegmentAccessor;\n\n /**\n * undefined when the rhs is not used by the comparator\n * string[] when the rhs is a list of segments\n * { type: 'regex'; pattern: string; flags: string } when the rhs is a regex\n */\n export type RHS =\n | undefined\n | string\n | number\n | boolean\n | (string | number)[]\n | { type: 'regex'; pattern: string; flags: string };\n\n export type ConditionOptions = {\n /** When true, string comparisons are case-insensitive. */\n i?: boolean;\n };\n\n export type Condition =\n | [LHS, Comparator, RHS]\n | [LHS, Comparator, RHS, ConditionOptions | 'i']\n | [LHS, Comparator.EXISTS]\n | [LHS, Comparator.NOT_EXISTS];\n\n export type Rule = {\n conditions: Condition[];\n outcome: Outcome;\n };\n\n export type SegmentRule = {\n conditions: Condition[];\n outcome: SegmentAllOutcome | SegmentSplitOutcome;\n };\n\n export type EnvironmentConfig =\n /**\n * Paused flags contain the pausedOutcome only.\n */\n | number\n /** Allows reusing the configuration of another environment */\n | { reuse: EnvironmentKey }\n /**\n * Active flags don't contain an explicit \"active\" state.\n * The fact that they have a config means they are active.\n */\n | {\n /**\n * Each array item represents a variant.\n *\n * Each slot holds the targets for that variant.\n *\n * So the target list at index 0 is the targets for variant 0, and so on.\n */\n targets?: TargetList[];\n rules?: Rule[];\n fallthrough: Outcome;\n };\n\n /**\n * A list of targets\n *\n * @example\n * {\n * user: { id: string[] }\n * }\n */\n export type TargetList = Record<string, Record<string, string[]>>;\n\n /**\n * reusable conditions, with no outcome attached\n */\n export type Segment = {\n rules?: SegmentRule[];\n /**\n * Explicitly include targets. Included targets will bypass conditions and exclusion.\n *\n * @example\n * include: {\n * user: { id: string[] }\n * }\n */\n include?: TargetList;\n /**\n * Explicitly exclude targets. Excluded targets will not be included in the segment, and bypass conditions.\n *\n * @example\n * exclude: {\n * user: { id: string[] }\n * }\n */\n exclude?: TargetList;\n };\n\n export type FlagDefinition = {\n /** for backwards compatibility with HappyKit */\n variantIds?: string[];\n /** variants, packed down to just their values */\n variants: Value[];\n /** Experiment linked to this flag. */\n experiment?: experimental_ExperimentDefinition;\n /** environments */\n environments: Record<EnvironmentKey, EnvironmentConfig>;\n /**\n * A random seed to prevent split points in different flags\n * from having the same targets. Otherwise the same set of ids would be\n * opted into all flags for every rollout. By using a different seed for\n * each flag the distribution is different for every flag.\n *\n * We don't use the slug as it might change, but we don't want the distribution\n * to change when the slug changes.\n *\n * We don't use the id or createdAt etc as we want to be able to redistirbute\n * by changing the seed.\n */\n seed?: number;\n };\n}\n","{\n \"name\": \"@vercel/flags-core\",\n \"version\": \"1.10.0-0037f35-20261006132750\",\n \"description\": \"A server-side client for Vercel Flags\",\n \"keywords\": [\n \"vercel\",\n \"flags\",\n \"vercel flags\",\n \"feature flags\",\n \"flags sdk\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/vercel/flags.git\"\n },\n \"license\": \"MIT\",\n \"author\": \"Dominik Ferber <dominik@vercel.com>\",\n \"sideEffects\": false,\n \"type\": \"module\",\n \"exports\": {\n \".\": {\n \"next-js\": {\n \"types\": \"./dist/index.next-js.d.ts\",\n \"default\": \"./dist/index.next-js.js\"\n },\n \"import\": {\n \"types\": \"./dist/index.default.d.ts\",\n \"default\": \"./dist/index.default.js\"\n },\n \"require\": {\n \"types\": \"./dist/index.default.d.cts\",\n \"default\": \"./dist/index.default.cjs\"\n }\n },\n \"./openfeature\": {\n \"next-js\": {\n \"types\": \"./dist/openfeature.next-js.d.ts\",\n \"default\": \"./dist/openfeature.next-js.js\"\n },\n \"import\": {\n \"types\": \"./dist/openfeature.default.d.ts\",\n \"default\": \"./dist/openfeature.default.js\"\n },\n \"require\": {\n \"types\": \"./dist/openfeature.default.d.cts\",\n \"default\": \"./dist/openfeature.default.cjs\"\n }\n }\n },\n \"main\": \"./dist/index.default.js\",\n \"typesVersions\": {\n \"*\": {\n \".\": [\n \"dist/index.default.d.ts\",\n \"dist/index.default.d.cts\"\n ],\n \"openfeature\": [\n \"dist/openfeature.default.d.ts\",\n \"dist/openfeature.default.d.cts\"\n ]\n }\n },\n \"files\": [\n \"dist\",\n \"CHANGELOG.md\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"check\": \"biome check\",\n \"test\": \"vitest --run\",\n \"test:watch\": \"vitest\",\n \"type-check\": \"tsc --noEmit\"\n },\n \"dependencies\": {\n \"@vercel/functions\": \"^3.4.3\",\n \"@vercel/oidc\": \"3.5.0\",\n \"dequal\": \"2.0.3\",\n \"jose\": \"^5.10.0\",\n \"js-xxhash\": \"4.0.0\"\n },\n \"devDependencies\": {\n \"@arethetypeswrong/cli\": \"0.18.2\",\n \"@types/node\": \"22.14.0\",\n \"next\": \"16.3.6\",\n \"tsup\": \"8.5.1\",\n \"typescript\": \"^5.9.3\",\n \"vite\": \"8.1.5\",\n \"vitest\": \"4.1.10\"\n },\n \"peerDependencies\": {\n \"@openfeature/server-sdk\": \"1.18.0\",\n \"next\": \"*\"\n },\n \"peerDependenciesMeta\": {\n \"@openfeature/server-sdk\": {\n \"optional\": true\n },\n \"next\": {\n \"optional\": true\n }\n },\n \"publishConfig\": {\n \"access\": \"public\"\n }\n}\n","import { version } from '../../package.json';\nimport type { OutcomeType, ResolutionReason } from '../types';\n\n/**\n * Only used internally for now.\n */\nexport function internalReportValue(\n key: string,\n value: unknown,\n data: {\n originProjectId?: string;\n originProvider?: 'vercel';\n outcomeType?: OutcomeType;\n reason?: ResolutionReason | 'override';\n },\n) {\n const symbol = Symbol.for('@vercel/request-context');\n const ctx = Reflect.get(globalThis, symbol)?.get();\n const reportFlagValue = ctx?.flags?.reportValue;\n if (typeof reportFlagValue !== 'function') return;\n\n reportFlagValue.call(ctx.flags, key, value, {\n sdkVersion: version,\n ...data,\n });\n}\n","import { waitUntil as defaultWaitUntil } from '@vercel/functions';\nimport { dequal } from 'dequal/lite';\nimport type {\n bulkEvaluate,\n evaluate,\n getDatafile,\n getFallbackDatafile,\n initialize,\n shutdown,\n} from './controller-fns';\nimport {\n type ControllerInstance,\n controllerInstanceMap,\n} from './controller-fns';\nimport type {\n BulkEvaluateInput,\n BundledDefinitions,\n ControllerInterface,\n EvaluationResult,\n experimental_EvaluationOptions,\n experimental_Exposure,\n experimental_ReportExposures,\n FlagsClient,\n Packed,\n Value,\n WaitUntil,\n} from './types';\n\nlet idCount = 0;\n\nasync function performInitialize(\n instance: ControllerInstance,\n initFn: () => Promise<void>,\n): Promise<void> {\n try {\n await initFn();\n instance.initialized = true;\n } catch (error) {\n // Clear so next call can retry\n instance.initPromise = null;\n throw error;\n }\n}\n\nexport function createCreateRawClient(fns: {\n initialize: typeof initialize;\n shutdown: typeof shutdown;\n getFallbackDatafile: typeof getFallbackDatafile;\n evaluate: typeof evaluate;\n bulkEvaluate: typeof bulkEvaluate;\n getDatafile: typeof getDatafile;\n}) {\n return function createRawClient<Entities = Record<string, unknown>>({\n controller,\n origin,\n experimental_reportExposures,\n waitUntil = defaultWaitUntil,\n }: {\n controller: ControllerInterface;\n origin?: { provider: string; sdkKey?: string; projectId?: string };\n experimental_reportExposures?: experimental_ReportExposures<Entities>;\n waitUntil?: WaitUntil;\n }): FlagsClient<Entities> {\n const id = idCount++;\n const pendingExposureReports = new Set<Promise<void>>();\n controllerInstanceMap.set(id, {\n controller,\n initialized: false,\n initPromise: null,\n });\n\n function report(\n exposures: readonly experimental_Exposure[],\n entity: Readonly<Entities>,\n ): void {\n if (!experimental_reportExposures || exposures.length === 0) return;\n\n const pending = (async () => {\n try {\n await experimental_reportExposures(exposures, entity);\n } catch (error) {\n console.error(\n '@vercel/flags-core: Failed to report experiment exposures',\n error,\n );\n }\n })();\n pendingExposureReports.add(pending);\n void pending.finally(() => pendingExposureReports.delete(pending));\n\n try {\n waitUntil(pending);\n } catch {\n // waitUntil is best-effort; the reporter can still finish on its own.\n }\n }\n\n function getExposure<T>(\n flagKey: string,\n result: EvaluationResult<T>,\n ): experimental_Exposure | null {\n if (!result.experiment) return null;\n return {\n flagKey,\n experimentId: result.experiment.id,\n variantId: result.experiment.variantId,\n base: result.experiment.base,\n ...(result.experiment.rampId === undefined\n ? {}\n : { rampId: result.experiment.rampId }),\n ...(result.experiment.rampPercentage === undefined\n ? {}\n : { rampPercentage: result.experiment.rampPercentage }),\n assignmentReason: result.experiment.assignmentReason,\n };\n }\n\n const api = {\n origin,\n initialize: async () => {\n let instance = controllerInstanceMap.get(id);\n if (!instance) {\n instance = { controller, initialized: false, initPromise: null };\n controllerInstanceMap.set(id, instance);\n }\n\n // skip if already initialized\n if (instance.initialized) return;\n\n if (!instance.initPromise) {\n instance.initPromise = performInitialize(instance, () =>\n fns.initialize(id),\n );\n }\n\n return instance.initPromise;\n },\n shutdown: async () => {\n await fns.shutdown(id);\n while (pendingExposureReports.size > 0) {\n await Promise.all(pendingExposureReports);\n }\n controllerInstanceMap.delete(id);\n },\n getDatafile: async () => {\n const instance = controllerInstanceMap.get(id);\n // A snapshot shares initialization already in flight but never starts it.\n if (instance?.initPromise) {\n try {\n await instance.initPromise;\n } catch {\n // Initialization failed — let getDatafile handle its own fallbacks\n }\n }\n return fns.getDatafile(id);\n },\n getFallbackDatafile: (): Promise<BundledDefinitions> => {\n return fns.getFallbackDatafile(id);\n },\n evaluate: async <T = Value, E = Entities>(\n flagKey: string,\n defaultValue?: T,\n entities?: E,\n options?: experimental_EvaluationOptions,\n ): Promise<EvaluationResult<T>> => {\n const instance = controllerInstanceMap.get(id);\n if (!instance?.initialized) {\n try {\n await api.initialize();\n } catch {\n // Initialization failed — let evaluate() handle the fallback\n // chain (last known value → datafile → bundled → defaultValue → throw)\n }\n }\n const entity = entities ?? ({} as E);\n const result = await fns.evaluate<T, E>(\n id,\n flagKey,\n defaultValue,\n entity,\n );\n if (\n experimental_reportExposures &&\n options?.experimental_exposureLogging !== false\n ) {\n const exposure = getExposure(flagKey, result);\n if (exposure) {\n report([exposure], entity as unknown as Readonly<Entities>);\n }\n }\n return result;\n },\n bulkEvaluate: async <T = Value, E = Entities>(\n flags: BulkEvaluateInput<T>[],\n entities?: E,\n options?: experimental_EvaluationOptions,\n ): Promise<Record<string, EvaluationResult<T>>> => {\n const instance = controllerInstanceMap.get(id);\n if (!instance?.initialized) {\n try {\n await api.initialize();\n } catch {\n // Initialization failed — let bulkEvaluate() handle the fallback\n // chain (last known value → datafile → bundled → defaultValue → throw)\n }\n }\n const entity = entities ?? ({} as E);\n const results = await fns.bulkEvaluate<T, E>(id, flags, entity);\n if (\n experimental_reportExposures &&\n options?.experimental_exposureLogging !== false\n ) {\n const exposures: experimental_Exposure[] = [];\n const seen = new Set<string>();\n for (const flag of flags) {\n if (seen.has(flag.key)) continue;\n seen.add(flag.key);\n const result = results[flag.key];\n if (!result) continue;\n const exposure = getExposure(flag.key, result);\n if (exposure) exposures.push(exposure);\n }\n report(exposures, entity as unknown as Readonly<Entities>);\n }\n return results;\n },\n experimental_reportOverride: async <T = Value, E = Entities>({\n key,\n value,\n entities,\n }: {\n key: string;\n value: T;\n entities?: E;\n }): Promise<void> => {\n if (!experimental_reportExposures) return;\n\n try {\n const instance = controllerInstanceMap.get(id);\n if (!instance?.initialized) await api.initialize();\n const datafile = await fns.getDatafile(id);\n const definition = datafile.definitions[key] as Packed.FlagDefinition;\n const experiment = definition?.experiment;\n if (!experiment) return;\n\n const variantIndex = definition.variants.findIndex((variant) =>\n dequal(variant, value),\n );\n const variantId =\n variantIndex < 0\n ? null\n : (definition.variantIds?.[variantIndex] ?? null);\n const entity = entities ?? ({} as E);\n report(\n [\n {\n flagKey: key,\n experimentId: experiment.id,\n variantId,\n base: experiment.base,\n rampId: experiment.rampId,\n rampPercentage: experiment.rampPercentage,\n assignmentReason: 'override',\n },\n ],\n entity as unknown as Readonly<Entities>,\n );\n } catch (error) {\n console.error(\n '@vercel/flags-core: Failed to report experiment override',\n error,\n );\n }\n },\n };\n return api;\n };\n}\n","// The @vercel/flags-definitions module is created at build time by Vercel CLI\n// in node_modules/. This is a fallback mechanism used so the app can always\n// fall back to a bundled version of the definitions, even if the flags network\n// is degraded or unavailable.\n//\n\nimport type { Auth, BundledDefinitionsLookup } from '../controller/auth';\nimport type { BundledDefinitions, BundledDefinitionsResult } from '../types';\n\n/** In-memory cache of SDK key to its hashed value, so we don't re-hash repeatedly. */\nconst sdkKeyHashCache = new Map<string, Promise<string>>();\n\n/** Computes a SHA-256 hex digest of the given SDK key. */\nasync function computeHash(sdkKey: string): Promise<string> {\n const hashBuffer = await crypto.subtle.digest(\n 'SHA-256',\n new TextEncoder().encode(sdkKey),\n );\n const hashArray = new Uint8Array(hashBuffer);\n return Array.from(hashArray)\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/** Returns a cached promise for the SHA-256 hash of the given SDK key. */\nfunction hashSdkKey(sdkKey: string): Promise<string> {\n const cached = sdkKeyHashCache.get(sdkKey);\n if (cached) return cached;\n\n const promise = computeHash(sdkKey);\n sdkKeyHashCache.set(sdkKey, promise);\n return promise;\n}\n\n/**\n * Reads the local definitions that get bundled at build time.\n */\nexport async function readBundledDefinitions(\n auth: Auth,\n): Promise<BundledDefinitionsResult> {\n let get: (sdkKey: string) => BundledDefinitions | null;\n try {\n const module = await import(\n /* turbopackOptional: true */\n '@vercel/flags-definitions'\n );\n get = module.get as (sdkKey: string) => BundledDefinitions | null;\n } catch (error) {\n // If the module doesn't exist, the prepare script didn't run\n if (\n error &&\n typeof error === 'object' &&\n 'code' in error &&\n error.code === 'MODULE_NOT_FOUND'\n ) {\n return { definitions: null, state: 'missing-file' };\n }\n\n return { definitions: null, state: 'unexpected-error', error };\n }\n\n if (typeof get !== 'function') {\n return { definitions: null, state: 'missing-file' };\n }\n\n let lookup: BundledDefinitionsLookup;\n try {\n lookup = await auth.resolveBundledDefinitionsLookup();\n } catch (error) {\n return { definitions: null, state: 'unexpected-error', error };\n }\n\n if (lookup.type === 'project-id') {\n const entry = get(lookup.projectId);\n return entry\n ? { definitions: entry, state: 'ok' }\n : { definitions: null, state: 'missing-entry' };\n }\n\n // Try plain sdk key first for bundles created by older CLI versions.\n const entry = get(lookup.sdkKey);\n if (entry) return { definitions: entry, state: 'ok' };\n\n // try hashed key but catch any errors\n try {\n const hashedKey = await hashSdkKey(lookup.sdkKey);\n // try original key (older cli versions) and hashed key (newer cli versions)\n const hashedEntry = get(hashedKey);\n if (hashedEntry) return { definitions: hashedEntry, state: 'ok' };\n } catch (error) {\n return { definitions: null, state: 'unexpected-error', error };\n }\n\n return { definitions: null, state: 'missing-entry' };\n}\n","import { getVercelOidcToken } from '@vercel/oidc';\nimport { version } from '../../package.json';\nimport { type Auth, authHeaders } from '../controller/auth';\nimport type { MetricEnvironment } from '../types';\nimport { getRetryDelayMs } from './backoff';\nimport { isDebugEnabled } from './debug-enabled';\nimport { getRuntimeIngestFor } from './runtime-ingest';\nimport type { FlushReason } from './scheduler';\nimport type { IngestEvent, UsageEvent } from './usage/events';\n\nconst MAX_RETRIES = 3;\n\n/**\n * Maximum number of events sent in a single POST to the ingest endpoint.\n *\n * Flushes are cut purely by time (see Scheduler), so a single flush can carry\n * arbitrarily many events. Chunking each send is what bounds the request size:\n * the server rejects request bodies with more events than its `maxItems` cap, so this\n * constant must stay below that cap.\n */\nexport const MAX_EVENTS_PER_REQUEST = 2000;\n\nexport const EVALUATING_OIDC_TOKEN_HEADER = 'X-Vercel-Flags-OIDC-Token';\nexport const FLUSH_REASON_HEADER = 'X-Vercel-Flags-Flush-Reason';\n\nconst debugLog = (...args: any[]) => {\n if (!isDebugEnabled()) return;\n console.log(...args);\n};\n\nexport interface IngestOptions {\n auth: Auth;\n host: string;\n fetch: typeof fetch;\n metricEnvironment?: MetricEnvironment;\n}\n\nasync function getEvaluatingOidcToken(auth: Auth): Promise<string | undefined> {\n if (!auth.sdkKey) return undefined;\n\n try {\n return await getVercelOidcToken();\n } catch {\n return undefined;\n }\n}\n\nasync function getIngestHeaders(\n options: IngestOptions,\n flushReason: FlushReason,\n): Promise<Record<string, string>> {\n const token = await options.auth.resolveToken();\n const evaluatingOidcToken = await getEvaluatingOidcToken(options.auth);\n\n return {\n 'Content-Type': 'application/json',\n ...authHeaders(token, options.auth.sourceProjectId),\n 'User-Agent': `VercelFlagsCore/${version}`,\n [FLUSH_REASON_HEADER]: flushReason,\n ...((options.metricEnvironment ?? process.env.VERCEL_ENV)\n ? {\n 'X-Vercel-Env':\n options.metricEnvironment ?? (process.env.VERCEL_ENV as string),\n }\n : null),\n ...(evaluatingOidcToken\n ? { [EVALUATING_OIDC_TOKEN_HEADER]: evaluatingOidcToken }\n : null),\n ...(isDebugEnabled() ? { 'x-vercel-debug-ingest': '1' } : null),\n };\n}\n\n/**\n * Headers for the runtime-provided ingest transport. The runtime attributes\n * the caller itself, so OIDC tokens are omitted; only an SDK key is included\n * when configured.\n */\nfunction getRuntimeIngestHeaders(\n options: IngestOptions,\n flushReason: FlushReason,\n): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n ...(options.auth.sdkKey\n ? { Authorization: `Bearer ${options.auth.sdkKey}` }\n : null),\n 'User-Agent': `VercelFlagsCore/${version}`,\n [FLUSH_REASON_HEADER]: flushReason,\n ...((options.metricEnvironment ?? process.env.VERCEL_ENV)\n ? {\n 'X-Vercel-Env':\n options.metricEnvironment ?? (process.env.VERCEL_ENV as string),\n }\n : null),\n ...(isDebugEnabled() ? { 'x-vercel-debug-ingest': '1' } : null),\n };\n}\n\nexport async function sendIngestEvents(\n options: IngestOptions,\n events: UsageEvent[],\n flushId: number,\n flushReason: FlushReason,\n): Promise<void> {\n let eventsToSend = events.map((event) => event.ingestEvent());\n\n const runtimeIngest = getRuntimeIngestFor(options.auth);\n if (runtimeIngest) {\n const headers = getRuntimeIngestHeaders(options, flushReason);\n // Events the runtime does not accept fall through to the HTTP transport.\n eventsToSend = eventsToSend.filter(\n (event) => !runtimeIngest({ headers, body: [event] }),\n );\n if (eventsToSend.length === 0) return;\n }\n\n for (let i = 0; i < eventsToSend.length; i += MAX_EVENTS_PER_REQUEST) {\n await sendIngestChunk(\n options,\n eventsToSend.slice(i, i + MAX_EVENTS_PER_REQUEST),\n flushId,\n flushReason,\n );\n }\n}\n\nasync function sendIngestChunk(\n options: IngestOptions,\n eventsToSend: IngestEvent[],\n flushId: number,\n flushReason: FlushReason,\n): Promise<void> {\n for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {\n try {\n const response = await options.fetch(`${options.host}/v1/ingest`, {\n method: 'POST',\n headers: await getIngestHeaders(options, flushReason),\n body: JSON.stringify(eventsToSend),\n });\n\n debugLog(\n `@vercel/flags-core: Ingest response ${response.status} for ${eventsToSend.length} events on ${response.headers.get('x-vercel-id')}`,\n );\n\n if (response.ok) {\n break;\n }\n\n throw new Error(\n `Ingest endpoint responded with status ${response.status} for ${eventsToSend.length} events on request ${response.headers.get('x-vercel-id')}.\\n` +\n `Response body: ${await response.text().catch(() => null)}`,\n );\n } catch (error) {\n console.error(\n `@vercel/flags-core: Error sending events (attempt=${attempt}/${MAX_RETRIES} flushId=${flushId}):`,\n error,\n );\n if (attempt < MAX_RETRIES) {\n const delayMs = getRetryDelayMs(attempt);\n await new Promise((res) => setTimeout(res, delayMs));\n } else {\n // All retries exhausted - surface a structured warning so consumers\n // can alert on dropped batches. The events are not persisted anywhere.\n console.error(\n `@vercel/flags-core: Dropped ${eventsToSend.length} events after ${MAX_RETRIES} attempts (flushId=${flushId})`,\n );\n }\n }\n }\n}\n","import { getVercelOidcToken } from '@vercel/oidc';\nimport { parseSdkKeyFromFlagsConnectionString } from '../utils/sdk-keys';\n\nexport type BundledDefinitionsLookup =\n | { type: 'sdk-key'; sdkKey: string }\n | { type: 'project-id'; projectId: string };\n\n/**\n * Names the project whose flags are read when it is not the project the OIDC\n * token belongs to. Only sent together with an OIDC token.\n */\nexport const SOURCE_PROJECT_HEADER = 'X-Vercel-Flags-Project-Id';\n\nexport interface Auth {\n sdkKey?: string;\n /** Set when reading another project's flags with this deployment's OIDC token. */\n sourceProjectId?: string;\n resolveToken(): Promise<string>;\n resolveBundledDefinitionsLookup(): Promise<BundledDefinitionsLookup>;\n}\n\nexport function authHeaders(\n token: string,\n sourceProjectId?: string,\n): Record<string, string> {\n return {\n Authorization: `Bearer ${token}`,\n ...(sourceProjectId ? { [SOURCE_PROJECT_HEADER]: sourceProjectId } : null),\n };\n}\n\nexport function unauthorizedMessage(sourceProjectId?: string): string {\n if (!sourceProjectId) {\n return 'unauthorized (401)';\n }\n return `unauthorized (401): this deployment is not allowed to read the flags of project \"${sourceProjectId}\". Check that the project is connected.`;\n}\n\nasync function getOidcToken(): Promise<string> {\n try {\n return await getVercelOidcToken();\n } catch {\n throw new Error(\n [\n '@vercel/flags-core: Failed to get OIDC token.',\n 'Are you running in a Vercel Environment where OIDC tokens are available?',\n 'Did you mean to use an SDK Key instead? Use the environment variable FLAGS or pass it directly to the client.',\n ].join(' '),\n );\n }\n}\n\nfunction getProjectIdFromOidcToken(oidcToken: string): string {\n const tokenParts = oidcToken.split('.');\n if (tokenParts.length !== 3 || !tokenParts[1]) {\n throw new Error('@vercel/flags-core: Invalid OIDC token');\n }\n\n const payload = JSON.parse(\n Buffer.from(tokenParts[1], 'base64url').toString('utf8'),\n ) as { project_id?: unknown };\n\n if (typeof payload.project_id !== 'string' || !payload.project_id) {\n throw new Error(\n '@vercel/flags-core: Missing project_id claim in OIDC token',\n );\n }\n\n return payload.project_id;\n}\n\nexport class Authentication implements Auth {\n public readonly sdkKey?: string;\n public readonly sourceProjectId?: string;\n\n constructor(sdkKeyOrConnectionString: string | undefined) {\n // validate sdk key format\n if (sdkKeyOrConnectionString !== undefined) {\n if (typeof sdkKeyOrConnectionString !== 'string') {\n throw new Error(\n `@vercel/flags-core: Invalid sdkKey. Expected string, got ${typeof sdkKeyOrConnectionString}`,\n );\n }\n\n // Parse connection string if needed (e.g., \"flags:edgeConfigId=...&sdkKey=vf_xxx\")\n const parsed = parseSdkKeyFromFlagsConnectionString(\n sdkKeyOrConnectionString,\n );\n if (!parsed) {\n throw new Error('@vercel/flags-core: Missing sdkKey');\n }\n\n this.sdkKey = parsed;\n }\n }\n\n public async resolveToken() {\n if (this.sdkKey) {\n return this.sdkKey;\n }\n\n return await getOidcToken();\n }\n\n public async resolveBundledDefinitionsLookup(): Promise<BundledDefinitionsLookup> {\n if (this.sdkKey) {\n return { type: 'sdk-key', sdkKey: this.sdkKey };\n }\n\n if (this.sourceProjectId) {\n return { type: 'project-id', projectId: this.sourceProjectId };\n }\n\n const oidcToken = await this.resolveToken();\n return {\n type: 'project-id',\n projectId: getProjectIdFromOidcToken(oidcToken),\n };\n }\n}\n","/**\n * Regex to match valid Vercel Flags SDK keys.\n * SDK keys must follow the format: vf_server_* or vf_client_*\n * This avoids false positives with third-party identifiers that happen\n * to start with 'vf_' (e.g., Stripe identity flow IDs like 'vf_1PyH...').\n */\nconst SDK_KEY_REGEX = /^vf_(?:server|client)_/;\n\n/**\n * Checks if a string is a valid Vercel Flags SDK key.\n */\nexport function isValidSdkKey(value: string): boolean {\n return SDK_KEY_REGEX.test(value);\n}\n\n/**\n * Parses sdk keys from connection strings with the following format:\n * `flags:edgeConfigId=ecfg_abcd&edgeConfigToken=xxx&sdkKey=xxx`\n */\nexport function parseSdkKeyFromFlagsConnectionString(\n text: string,\n): string | null {\n if (SDK_KEY_REGEX.test(text)) return text;\n\n try {\n if (!text.startsWith('flags:')) return null;\n const params = new URLSearchParams(text.slice(6));\n const sdkKey = params.get('sdkKey');\n if (sdkKey && SDK_KEY_REGEX.test(sdkKey)) return sdkKey;\n } catch {\n // no-op\n }\n\n return null;\n}\n","/**\n * Backoff and jitter utilities for retry loops.\n *\n * The functions here are intentionally generic so they can be used wherever\n * the SDK retries an operation. They use `Math.random` directly; mock it from\n * tests if you need deterministic behaviour.\n */\n\nconst DEFAULT_BASE_MS = 250;\nconst DEFAULT_CAP_MS = 5000;\n\nexport interface RetryDelayOptions {\n /**\n * The base delay in milliseconds. With Full Jitter, the first failed attempt\n * sleeps in `[0, baseMs)`, the second in `[0, baseMs * 2)`, etc.\n * Defaults to 250ms.\n */\n baseMs?: number;\n /**\n * Hard ceiling on the computed delay. Defaults to 5000ms.\n */\n capMs?: number;\n}\n\n/**\n * Returns the sleep duration before the next retry attempt using AWS-style\n * \"Full Jitter\" exponential backoff.\n *\n * @param attempt The 1-indexed attempt number that just failed.\n * @see https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter\n */\nexport function getRetryDelayMs(\n attempt: number,\n options: RetryDelayOptions = {},\n): number {\n const baseMs = options.baseMs ?? DEFAULT_BASE_MS;\n const capMs = options.capMs ?? DEFAULT_CAP_MS;\n\n const ceiling = Math.min(capMs, baseMs * 2 ** Math.max(0, attempt - 1));\n return Math.floor(Math.random() * ceiling);\n}\n\n/**\n * Returns `baseMs` perturbed by `±ratio` of itself, drawn uniformly from\n * `[baseMs * (1 - ratio), baseMs * (1 + ratio))`. Useful for desynchronizing\n * fixed-interval timers across independent processes.\n *\n * @param baseMs The target mean wait, in milliseconds.\n * @param ratio The fractional jitter to apply on each side, in `[0, 1)`.\n */\nexport function getJitteredWaitMs(baseMs: number, ratio: number): number {\n const min = baseMs * (1 - ratio);\n const span = baseMs * 2 * ratio;\n return Math.floor(min + Math.random() * span);\n}\n","const NAMESPACE = '@vercel/flags-core';\n\nlet lastValue: string | undefined;\nlet lastEnabled = false;\n\n/** Matches one `DEBUG` pattern, where `*` stands for any sequence of characters. */\nfunction matches(pattern: string, namespace: string): boolean {\n if (!pattern.includes('*')) {\n return pattern === namespace;\n }\n const expression = pattern\n .split('*')\n .map((part) => part.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'))\n .join('.*');\n return new RegExp(`^${expression}$`).test(namespace);\n}\n\n/**\n * Whether `DEBUG` selects the `@vercel/flags-core` namespace, following the\n * `debug` package conventions: patterns are separated by commas or spaces,\n * `*` is a wildcard, and a leading `-` excludes a pattern.\n *\n * The variable is read on every call so toggling it at runtime takes effect,\n * but the parse is cached by raw value to keep disabled calls cheap.\n */\nexport function isDebugEnabled(): boolean {\n const value = process.env.DEBUG;\n if (value === lastValue) {\n return lastEnabled;\n }\n\n let enabled = false;\n if (value) {\n for (const entry of value.split(/[\\s,]+/)) {\n if (!entry) {\n continue;\n }\n if (entry.startsWith('-')) {\n if (matches(entry.slice(1), NAMESPACE)) {\n enabled = false;\n break;\n }\n } else if (matches(entry, NAMESPACE)) {\n enabled = true;\n }\n }\n }\n\n lastValue = value;\n lastEnabled = enabled;\n return enabled;\n}\n","import type { Auth } from '../controller/auth';\nimport type { IngestEvent } from './usage/events';\n\nexport type RuntimeIngest = (payload: {\n headers: Record<string, string>;\n body: IngestEvent[];\n}) => boolean;\n\nconst FLAGS_CONTEXT_SYMBOL = Symbol.for('@vercel/flags-context');\n\n/**\n * Returns the runtime ingest transport when it can carry events for this\n * auth. The runtime attributes events to the calling deployment's own\n * project, so events for another project's flags always go over HTTP with\n * the source header.\n */\nexport function getRuntimeIngestFor(auth: Auth): RuntimeIngest | undefined {\n return auth.sourceProjectId ? undefined : getRuntimeIngest();\n}\n\n/**\n * Returns the ingest transport provided by the runtime, if available.\n */\nexport function getRuntimeIngest(): RuntimeIngest | undefined {\n try {\n const context = (\n globalThis as typeof globalThis & {\n [key: symbol]: { ingest?: unknown } | undefined;\n }\n )[FLAGS_CONTEXT_SYMBOL];\n\n return typeof context?.ingest === 'function'\n ? (context.ingest as RuntimeIngest)\n : undefined;\n } catch {\n return undefined;\n }\n}\n","interface RequestContext {\n ctx: object | undefined;\n headers: Record<string, string> | undefined;\n}\n\nconst SYMBOL_FOR_REQ_CONTEXT = Symbol.for('@vercel/request-context');\nconst fromSymbol = globalThis as typeof globalThis & {\n [key: symbol]:\n | { get?: () => { headers?: Record<string, string> } }\n | undefined;\n};\n\n/**\n * Gets the Vercel request context and headers from the global symbol.\n */\nexport function getRequestContext(): RequestContext {\n try {\n const ctx = fromSymbol[SYMBOL_FOR_REQ_CONTEXT]?.get?.();\n if (ctx && Object.hasOwn(ctx, 'headers')) {\n return {\n ctx,\n headers: ctx.headers as Record<string, string>,\n };\n }\n return { ctx, headers: undefined };\n } catch {\n return { ctx: undefined, headers: undefined };\n }\n}\n","import { waitUntil } from '@vercel/functions';\nimport type { WaitUntil } from '../types';\nimport { getJitteredWaitMs } from './backoff';\n\nconst IDLE_FLUSH_WAIT_MS = 5000;\nconst IDLE_FLUSH_JITTER_RATIO = 0.2;\nconst MAX_FLUSH_WAIT_MS = 60000;\n\nexport type FlushReason =\n | 'idle_timeout'\n | 'max_timeout'\n | 'shutdown'\n | 'immediate';\n\n/**\n * Schedule helper that flushes when any of the following occur:\n * - the idle window elapses ({@link IDLE_FLUSH_WAIT_MS}, reset on every event), or\n * - the max window elapses ({@link MAX_FLUSH_WAIT_MS}, starts with the batch and is\n * never reset, so a batch always flushes under continuous traffic).\n *\n * Flushing is purely time-based; batches are only cut down to size when the\n * ingest events are sent (see MAX_EVENTS_PER_REQUEST in ingest.ts).\n *\n * The scheduled flush awaits {@link onFlush} (including its ingest send + retries),\n * so the promise handed to `waitUntil` does not resolve until ingest completes.\n */\nexport class Scheduler {\n private resolveWait: ((reason: FlushReason) => void) | null = null;\n private pending: null | Promise<void> = null;\n private idleTimeout: null | ReturnType<typeof setTimeout> = null;\n private maxTimeout: null | ReturnType<typeof setTimeout> = null;\n\n constructor(\n private readonly onFlush: (reason: FlushReason) => void | Promise<void>,\n private readonly scheduleTask: WaitUntil = waitUntil,\n ) {}\n\n scheduleFlush(): void {\n if (!this.pending) {\n this.pending = (async () => {\n // wait for a timeout or the event count to reach the batch size\n const reason = await new Promise<FlushReason>((res) => {\n this.resolveWait = res;\n });\n\n // free state so new events start a fresh batch while ingest runs\n this.reset();\n\n // genuinely await ingest (incl. retries) so waitUntil covers the send\n await this.onFlush(reason);\n })();\n\n try {\n this.scheduleTask(this.pending);\n } catch {\n // waitUntil is best-effort; falling through leaves a floating promise\n }\n\n // max window: starts with the batch and is never reset\n this.maxTimeout = setTimeout(\n () => this.resolveScheduledFlush('max_timeout'),\n MAX_FLUSH_WAIT_MS,\n );\n }\n\n // idle window: reset on every event\n this.resetIdleTimeout();\n }\n\n /**\n * Resolves any in-flight scheduled flush and waits for its ingest to finish.\n */\n async shutdown(): Promise<void> {\n this.clearTimeouts();\n const pending = this.pending;\n this.resolveScheduledFlush('shutdown');\n if (pending) await pending;\n }\n\n private resetIdleTimeout(): void {\n if (this.idleTimeout) clearTimeout(this.idleTimeout);\n this.idleTimeout = setTimeout(\n () => this.resolveScheduledFlush('idle_timeout'),\n getJitteredWaitMs(IDLE_FLUSH_WAIT_MS, IDLE_FLUSH_JITTER_RATIO),\n );\n }\n\n private resolveScheduledFlush(reason: FlushReason): void {\n this.clearTimeouts();\n this.resolveWait?.(reason);\n }\n\n private reset(): void {\n this.pending = null;\n this.resolveWait = null;\n this.clearTimeouts();\n }\n\n private clearTimeouts(): void {\n if (this.idleTimeout) {\n clearTimeout(this.idleTimeout);\n this.idleTimeout = null;\n }\n if (this.maxTimeout) {\n clearTimeout(this.maxTimeout);\n this.maxTimeout = null;\n }\n }\n}\n","import type { UsageEvent } from './events';\n\nexport interface TrackReadOptions {\n /** Whether the config was read from in-memory cache or embedded bundle */\n configOrigin: 'in-memory' | 'embedded';\n /** HIT when definitions exist in memory, MISS when not, BYPASS when using fallback as primary source */\n cacheStatus?: 'HIT' | 'MISS' | 'BYPASS';\n /** FOLLOWING when streaming, REFRESHING when polling, NONE otherwise */\n cacheAction?: 'REFRESHING' | 'FOLLOWING' | 'NONE';\n /** True for the very first getData call */\n cacheIsFirstRead?: boolean;\n /** Whether the cache read was blocking */\n cacheIsBlocking?: boolean;\n /** Duration in milliseconds from start of getData until trackRead */\n duration?: number;\n /** Timestamp when the config was last updated */\n configUpdatedAt?: number;\n /** The mode the SDK is operating in */\n mode?: 'poll' | 'stream' | 'build' | 'vercel' | 'offline';\n /** Revision of the config */\n revision?: number;\n}\n\nexport class FlagsConfigReadEvent implements UsageEvent {\n private readonly ts = Date.now();\n\n private payload: {\n deploymentId?: string;\n region?: string;\n invocationHost?: string;\n vercelRequestId?: string;\n cacheStatus?: 'HIT' | 'MISS' | 'BYPASS' | 'STALE';\n cacheAction?: 'REFRESHING' | 'FOLLOWING' | 'NONE';\n cacheIsBlocking?: boolean;\n cacheIsFirstRead?: boolean;\n duration?: number;\n configUpdatedAt?: number;\n configOrigin?: 'in-memory' | 'embedded' | 'poll' | 'stream' | 'constructor';\n mode?: TrackReadOptions['mode'];\n revision?: string;\n environment?: string;\n };\n\n constructor(\n headers: Record<string, string> | undefined,\n options?: TrackReadOptions,\n ) {\n this.payload = {\n deploymentId: process.env.VERCEL_DEPLOYMENT_ID,\n region: process.env.VERCEL_REGION,\n };\n\n if (headers) {\n this.payload.vercelRequestId = headers['x-vercel-id'] ?? undefined;\n this.payload.invocationHost = headers.host ?? undefined;\n }\n\n if (options) {\n this.payload.configOrigin = options.configOrigin;\n if (options.cacheStatus !== undefined) {\n this.payload.cacheStatus = options.cacheStatus;\n }\n if (options.cacheAction !== undefined) {\n this.payload.cacheAction = options.cacheAction;\n }\n if (options.cacheIsFirstRead !== undefined) {\n this.payload.cacheIsFirstRead = options.cacheIsFirstRead;\n }\n if (options.cacheIsBlocking !== undefined) {\n this.payload.cacheIsBlocking = options.cacheIsBlocking;\n }\n if (options.duration !== undefined) {\n this.payload.duration = options.duration;\n }\n if (options.configUpdatedAt !== undefined) {\n this.payload.configUpdatedAt = options.configUpdatedAt;\n }\n if (options.mode !== undefined) {\n this.payload.mode = options.mode;\n }\n if (options.revision !== undefined) {\n this.payload.revision = String(options.revision);\n }\n }\n\n const environment =\n process.env.VERCEL_ENV || process.env.NODE_ENV || undefined;\n if (environment) {\n this.payload.environment = environment;\n }\n }\n\n ingestEvent() {\n return {\n type: 'FLAGS_CONFIG_READ' as const,\n ts: this.ts,\n payload: this.payload,\n };\n }\n}\n","import type { ResolutionReason, VariantId } from '../../types';\nimport type { UsageEvent } from './events';\n\nexport interface TrackEvaluationOptions {\n flagKey: string;\n variant: VariantId | null;\n reason: ResolutionReason;\n clientName?: string;\n}\n\nconst MINUTE_MS = 60_000;\n\nexport function minuteBucketTs(ts = Date.now()): number {\n return Math.floor(ts / MINUTE_MS) * MINUTE_MS;\n}\n\nexport type BucketedTrackEvaluationOptions = TrackEvaluationOptions & {\n bucketTs: number;\n};\n\nexport function evaluationBatchKey(\n options: BucketedTrackEvaluationOptions,\n): string {\n return JSON.stringify([\n options.flagKey,\n options.variant,\n options.reason,\n options.clientName ?? null,\n options.bucketTs,\n ]);\n}\n\nexport class FlagsEvaluationEvent implements UsageEvent {\n private readonly ts = Date.now();\n\n payload: {\n flagKey: string;\n variant?: string;\n reason: ResolutionReason;\n clientName?: string;\n evaluationCount: number;\n periodStartedAt: number;\n };\n\n constructor(eventOptions: BucketedTrackEvaluationOptions) {\n this.payload = {\n flagKey: eventOptions.flagKey,\n variant: eventOptions.variant ?? undefined,\n reason: eventOptions.reason,\n evaluationCount: 1,\n periodStartedAt: eventOptions.bucketTs,\n };\n\n if (eventOptions.clientName) {\n this.payload.clientName = eventOptions.clientName;\n }\n }\n\n increment(): void {\n this.payload.evaluationCount += 1;\n }\n\n ingestEvent() {\n return {\n type: 'FLAG_EVALUATION' as const,\n ts: this.ts,\n payload: this.payload,\n };\n }\n}\n","import type { WaitUntil } from '../types';\nimport { type IngestOptions, sendIngestEvents } from './ingest';\nimport { getRequestContext } from './request-context';\nimport { getRuntimeIngestFor } from './runtime-ingest';\nimport { type FlushReason, Scheduler } from './scheduler';\nimport {\n FlagsConfigReadEvent,\n type TrackReadOptions,\n} from './usage/flags-config-read';\nimport {\n evaluationBatchKey,\n FlagsEvaluationEvent,\n minuteBucketTs,\n type TrackEvaluationOptions,\n} from './usage/flags-evaluation';\n\n/**\n * Tracks usage events and batches them for submission to the ingest endpoint.\n */\nexport class UsageTracker {\n private flushCount: number = 0;\n\n private options: IngestOptions;\n private scheduler: Scheduler;\n private waitUntil: WaitUntil;\n private inflightFlushes = new Set<Promise<void>>();\n\n private trackedRequests = new WeakSet<object>();\n\n private readEvents: FlagsConfigReadEvent[] = [];\n private evaluationEvents = new Map<string, FlagsEvaluationEvent>();\n\n constructor(options: IngestOptions & { waitUntil: WaitUntil }) {\n this.options = options;\n this.waitUntil = options.waitUntil;\n this.scheduler = new Scheduler(\n (reason) => this.flushEvents(reason),\n options.waitUntil,\n );\n }\n\n /**\n * Triggers an immediate flush of any pending events.\n * Returns a promise that resolves when the flush completes.\n */\n async shutdown() {\n // Drain any in-flight scheduled batch (incl. its ingest send).\n await this.scheduler.shutdown();\n\n // Safety net for events tracked after the drained batch reset; if the\n // drained flush already sent everything this returns early (maps cleared).\n await this.flushEvents('shutdown');\n\n // Drain immediate flushes whose events fell back to the HTTP transport;\n // their events left the maps before the async send started, so the flush\n // above cannot cover them.\n await Promise.all([...this.inflightFlushes]);\n }\n\n /**\n * Tracks a config read event. Deduplicates by request context.\n */\n trackRead(options?: TrackReadOptions): void {\n try {\n const { ctx, headers } = getRequestContext();\n\n // Skip if request context can't be inferred\n if (!ctx) return;\n\n // Skip if we've already tracked this request\n if (this.trackedRequests.has(ctx)) return;\n this.trackedRequests.add(ctx);\n\n this.readEvents.push(new FlagsConfigReadEvent(headers, options));\n\n this.requestFlush();\n } catch (error) {\n // trackRead should never throw, but log the error\n console.error('@vercel/flags-core: Failed to record event:', error);\n }\n }\n\n /**\n * Tracks a flag evaluation event.\n */\n trackEvaluation(options: TrackEvaluationOptions): void {\n try {\n const bucketedOptions = {\n ...options,\n bucketTs: minuteBucketTs(),\n };\n const batchKey = evaluationBatchKey(bucketedOptions);\n\n const existingEvent = this.evaluationEvents.get(batchKey);\n // increment if we already have an event for this batch key\n if (existingEvent) {\n existingEvent.increment();\n } else {\n this.evaluationEvents.set(\n batchKey,\n new FlagsEvaluationEvent(bucketedOptions),\n );\n }\n\n // always schedule to reset the timer\n this.requestFlush();\n } catch (error) {\n console.error(\n '@vercel/flags-core: Failed to record evaluation event:',\n error,\n );\n }\n }\n\n /**\n * Flushes immediately when the runtime provides an ingest transport,\n * otherwise falls back to the time-based scheduler.\n */\n private requestFlush(): void {\n if (getRuntimeIngestFor(this.options.auth)) {\n // Track the flush so shutdown() can drain it: events the runtime does\n // not accept fall back to the async HTTP transport, which outlives this\n // synchronous call. When the runtime accepts everything the promise is\n // already settled, so neither waitUntil nor shutdown() waits on it.\n const flush = this.flushEvents('immediate').catch((error) => {\n console.error('@vercel/flags-core: Failed to flush events:', error);\n });\n this.inflightFlushes.add(flush);\n void flush.finally(() => this.inflightFlushes.delete(flush));\n try {\n this.waitUntil?.(flush);\n } catch {\n // waitUntil is best-effort; shutdown() still drains the flush.\n }\n } else {\n this.scheduler.scheduleFlush();\n }\n }\n\n /**\n * Send all events to the ingest service\n */\n private async flushEvents(flushReason: FlushReason) {\n const events = [...this.readEvents, ...this.evaluationEvents.values()];\n if (events.length === 0) return;\n\n this.flushCount += 1;\n const flushId = this.flushCount;\n\n this.readEvents = [];\n this.evaluationEvents.clear();\n\n await sendIngestEvents(this.options, events, flushId, flushReason);\n }\n}\n","/**\n * Error thrown when the fallback definitions file does not exist.\n * This typically means the \"vercel-flags prepare\" command was not run before building.\n */\nexport class FallbackNotFoundError extends Error {\n constructor() {\n super('@vercel/flags-core: Bundled definitions file not found.');\n this.name = 'FallbackNotFoundError';\n }\n}\n\n/**\n * Error thrown when the fallback definitions file exists but has no entry for the SDK key.\n * This means the SDK key was not included when running \"vercel-flags prepare\".\n */\nexport class FallbackEntryNotFoundError extends Error {\n constructor() {\n super('@vercel/flags-core: No bundled definitions found for SDK key.');\n this.name = 'FallbackEntryNotFoundError';\n }\n}\n","import { FallbackEntryNotFoundError, FallbackNotFoundError } from '../errors';\nimport type {\n BundledDefinitions,\n BundledDefinitionsResult,\n DatafileInput,\n} from '../types';\nimport type { readBundledDefinitions } from '../utils/read-bundled-definitions';\nimport type { Auth } from './auth';\n\n/**\n * Manages loading of bundled flag definitions.\n * Wraps readBundledDefinitions() with caching.\n */\nexport class BundledSource {\n private promise: Promise<BundledDefinitionsResult> | undefined;\n\n constructor(\n private options: {\n auth: Auth;\n readBundledDefinitions: typeof readBundledDefinitions;\n },\n ) {}\n\n /**\n * Load bundled definitions.\n * Throws if bundled definitions are not available.\n */\n async load(): Promise<DatafileInput> {\n const result = await this.getResult();\n\n if (result.state === 'ok' && result.definitions) {\n return result.definitions;\n }\n\n throw new Error(\n '@vercel/flags-core: No flag definitions available. ' +\n 'Bundled definitions not found.',\n );\n }\n\n /**\n * Get the raw BundledDefinitions (for getFallbackDatafile).\n * Throws typed errors if not available.\n */\n async getRaw(): Promise<BundledDefinitions> {\n const result = await this.getResult();\n\n switch (result.state) {\n case 'ok':\n return result.definitions;\n case 'missing-file':\n throw new FallbackNotFoundError();\n case 'missing-entry':\n throw new FallbackEntryNotFoundError();\n case 'unexpected-error':\n throw new Error(\n '@vercel/flags-core: Failed to read bundled definitions: ' +\n String(result.error),\n );\n }\n }\n\n /**\n * Check if bundled definitions loaded successfully (without throwing).\n */\n async tryLoad(): Promise<DatafileInput | undefined> {\n const result = await this.getResult();\n if (result?.state === 'ok' && result.definitions) {\n return result.definitions;\n }\n return undefined;\n }\n\n private getResult(): Promise<BundledDefinitionsResult> {\n if (!this.promise) {\n this.promise = this.options.readBundledDefinitions(this.options.auth);\n }\n return this.promise;\n }\n}\n","import { isDebugEnabled } from '../utils/debug-enabled';\n\n/** Internal diagnostics only: never pass credentials, payloads, or raw errors. */\nexport function debug(\n clientName: string | undefined,\n event: string,\n details?: () => Record<string, string | number | boolean | undefined>,\n): void {\n if (!isDebugEnabled()) {\n return;\n }\n\n // Diagnostics must not affect initialization, reads, or background work.\n try {\n console.debug('@vercel/flags-core', { event, clientName, ...details?.() });\n } catch {\n // Console implementations may throw (for example, a closed output stream).\n }\n}\n","import type { DatafileInput, Metrics } from '../types';\n\n/**\n * Internal origin tracking for how data was obtained.\n * This flows with the data from point of origin through to metrics.\n */\nexport type DataOrigin = 'stream' | 'poll' | 'bundled' | 'provided' | 'fetched';\n\n/**\n * DatafileInput with origin metadata attached at the point of arrival.\n * Internal only — stripped before returning to consumers.\n */\nexport type TaggedData = DatafileInput & {\n _origin: DataOrigin;\n};\n\n/**\n * Tags a DatafileInput with its origin.\n */\nexport function tagData(data: DatafileInput, origin: DataOrigin): TaggedData {\n return Object.assign(data, { _origin: origin }) as TaggedData;\n}\n\n/**\n * Maps internal DataOrigin to the public Metrics.source value.\n */\nexport function originToMetricsSource(origin: DataOrigin): Metrics['source'] {\n switch (origin) {\n case 'stream':\n case 'poll':\n case 'provided':\n return 'in-memory';\n case 'fetched':\n return 'remote';\n case 'bundled':\n return 'embedded';\n }\n}\n","import type { DatafileInput, Metrics, WaitUntil } from '../types';\nimport { debug } from './debug';\nimport { type DataOrigin, type TaggedData, tagData } from './tagged-data';\n\ntype Confirmation = Pick<\n DatafileInput,\n 'configUpdatedAt' | 'revision' | 'projectId' | 'environment'\n>;\ntype ConfirmationSource = DataOrigin | 'header';\n\nexport type CacheMetadata = Confirmation & { ageMs: number };\n\nexport type Freshness = 'fresh' | 'stale' | 'expired' | 'unknown';\n\nexport type CacheAssessment = {\n /** Error means the source cannot assess this entry; the controller must fall back. */\n status: Freshness | 'error';\n /** Positive evidence that renews age and clears the current failure. */\n confirmed?: boolean;\n};\n\nexport type CacheFetch = (signal: AbortSignal) => Promise<DatafileInput>;\nexport type CacheResult = {\n data: TaggedData | undefined;\n status: Metrics['cacheStatus'];\n hasError?: boolean;\n};\n\nconst SOURCE_CONFIRMED = new Error(\n 'Refresh superseded by a source confirmation',\n);\n\nexport type CacheReadPolicy = {\n /** Unknown adds no freshness evidence and keeps cached-read behavior. */\n assess: (data: CacheMetadata) => CacheAssessment;\n /** Header reads can provide new recovery evidence before a source update. */\n retryOnFailure?: boolean;\n /**\n * A live source that owns refreshes. Stale reads then serve the cache while\n * the source recovers, and expired reads wait for its confirmation instead\n * of fetching over HTTP.\n */\n revalidate?: () => Promise<void>;\n};\n\n/**\n * Parses a configUpdatedAt value (number or string) into a numeric timestamp.\n * Returns undefined if the value is missing or cannot be parsed.\n */\nfunction parseConfigUpdatedAt(value: unknown): number | undefined {\n if (typeof value === 'number') return value;\n if (typeof value === 'string') {\n const parsed = Number(value);\n return Number.isNaN(parsed) ? undefined : parsed;\n }\n return undefined;\n}\n\n/** Storage, serving policy, and fetching driven by source callbacks. */\nexport class DatafileCache {\n private data: TaggedData | undefined;\n // Confirmations refresh age without rewriting the datafile's persisted fetch time.\n private freshAt: number | undefined;\n private failure: { error: Error; startedAt: number } | undefined;\n\n private abortController = new AbortController();\n private fetching: Promise<void> | undefined;\n\n private debugState = () => ({\n projectId: this.data?.projectId,\n environment: this.data?.environment,\n hasData: this.hasData,\n origin: this.data?._origin,\n revision: this.revision,\n configUpdatedAt: parseConfigUpdatedAt(this.data?.configUpdatedAt),\n ageMs: this.ageMs,\n failed: this.failure !== undefined,\n failureAgeMs: this.failure\n ? Date.now() - this.failure.startedAt\n : undefined,\n staleIfErrorMs: this.staleIfErrorMs,\n canServe: this.hasData && this.canServe(),\n fetching: this.fetching !== undefined,\n });\n\n constructor(\n private readonly fetch: CacheFetch,\n private readonly staleIfErrorMs = Infinity,\n private readonly waitUntil: WaitUntil = () => {},\n private readonly clientName?: string,\n ) {}\n\n /** Expired data still exists; fallback loading must not bypass its failure policy. */\n get hasData(): boolean {\n return this.data !== undefined;\n }\n\n /** Retained revisions remain available for reconnecting after serving expires. */\n get revision(): number | undefined {\n return this.data?.revision;\n }\n\n /** Time since the latest freshness evidence, if known. */\n get ageMs(): number {\n return this.freshAt === undefined\n ? Infinity\n : Math.max(0, Date.now() - this.freshAt);\n }\n\n /** Records freshness evidence without confirming recovery from a failure. */\n resetAge(): void {\n if (this.data) this.freshAt = Date.now();\n }\n\n /** Freshness checks can inspect retained metadata even after serving expires. */\n public get metadata(): CacheMetadata | undefined {\n if (!this.data) return undefined;\n const { projectId, environment, configUpdatedAt, revision } = this.data;\n return {\n projectId,\n environment,\n configUpdatedAt,\n revision,\n ageMs: this.ageMs,\n };\n }\n\n /** Stores initial or fallback data without confirming recovery from a failure. */\n seed(data: TaggedData): void {\n this.data = data;\n this.freshAt =\n typeof data.fetchedAt === 'number' &&\n Number.isFinite(data.fetchedAt) &&\n data.fetchedAt >= 0\n ? data.fetchedAt\n : undefined;\n debug(this.clientName, 'cache.seed', this.debugState);\n }\n\n /** Stores data and returns it through the serving boundary. */\n seedAndRead(data: TaggedData): TaggedData {\n this.seed(data);\n const seeded = this.read();\n if (!seeded) {\n throw new Error('@vercel/flags-core: Seeded definitions unavailable');\n }\n return seeded;\n }\n\n /**\n * Accepts a newer source response, or keeps the stored data when the version\n * guard rejects it. Every successful response proves the source is reachable,\n * so it renews cache age and clears the failure either way.\n */\n updateFromSource(incoming: DatafileInput, origin: DataOrigin): void {\n if (this.isNewerData(incoming)) {\n this.data = tagData({ ...incoming, fetchedAt: Date.now() }, origin);\n this.confirm(origin);\n debug(this.clientName, 'cache.update.accepted', this.debugState);\n return;\n }\n this.confirm(origin);\n debug(this.clientName, 'cache.update.ignored', () => ({\n ...this.debugState(),\n incomingOrigin: origin,\n incomingRevision: incoming.revision,\n incomingConfigUpdatedAt: parseConfigUpdatedAt(incoming.configUpdatedAt),\n }));\n }\n\n /** Confirms a same-version message without replacing stored data. */\n tryConfirm(\n incoming: Confirmation,\n version: 'configUpdatedAt' | 'revision' = 'configUpdatedAt',\n source: ConfirmationSource = 'header',\n ): boolean {\n if (!this.matchesStored(incoming, version)) {\n return false;\n }\n\n this.confirm(source);\n debug(this.clientName, 'cache.confirmed', () => ({\n version,\n source,\n ...this.debugState(),\n }));\n return true;\n }\n\n /** Whether a message refers to the stored entry: finite equal version and same identity. */\n private matchesStored(\n incoming: Confirmation,\n version: 'configUpdatedAt' | 'revision',\n ): boolean {\n if (!this.data) {\n return false;\n }\n\n const currentTs =\n version === 'revision'\n ? this.data.revision\n : parseConfigUpdatedAt(this.data.configUpdatedAt);\n const incomingTs =\n version === 'revision'\n ? incoming.revision\n : parseConfigUpdatedAt(incoming.configUpdatedAt);\n return (\n Number.isFinite(currentTs) &&\n Number.isFinite(incomingTs) &&\n currentTs === incomingTs &&\n this.data.projectId === incoming.projectId &&\n this.data.environment === incoming.environment\n );\n }\n\n /** Renews freshness and retires HTTP work superseded by stream evidence. */\n confirm(source: ConfirmationSource = 'header'): void {\n const recovered = this.failure !== undefined;\n this.resetAge();\n this.failure = undefined;\n if (recovered) {\n debug(this.clientName, 'cache.recovered', this.debugState);\n }\n // HTTP responses must finish their own refresh; headers may be older requests.\n if (source === 'stream' && this.fetching) {\n debug(this.clientName, 'cache.fetch.cancel', () => ({\n reason: 'source-confirmed',\n ...this.debugState(),\n }));\n this.abortController.abort(SOURCE_CONFIRMED);\n this.abortController = new AbortController();\n this.fetching = undefined;\n }\n }\n\n /** Preserves existing acceptance, including missing or unparseable versions. */\n private isNewerData(incoming: DatafileInput): boolean {\n if (!this.data) return true;\n\n const currentTs = parseConfigUpdatedAt(this.data.configUpdatedAt);\n const incomingTs = parseConfigUpdatedAt(incoming.configUpdatedAt);\n\n if (currentTs === undefined || incomingTs === undefined) {\n return true;\n }\n\n return incomingTs > currentTs;\n }\n\n fail(error: Error): void {\n const repeated = this.failure !== undefined;\n // Repeated failures must not keep extending the stale-if-error allowance.\n this.failure ??= { error, startedAt: Date.now() };\n debug(this.clientName, 'cache.failure', () => ({\n repeated,\n ...this.debugState(),\n }));\n }\n\n private canServe(): boolean {\n if (!this.failure || this.staleIfErrorMs === Infinity) return true;\n return (\n this.staleIfErrorMs > 0 &&\n Date.now() - this.failure.startedAt <= this.staleIfErrorMs\n );\n }\n\n /** The serving boundary for both snapshot and policy-driven reads. */\n read(): TaggedData | undefined {\n if (!this.data) return undefined;\n if (!this.canServe()) {\n debug(this.clientName, 'cache.stale-if-error.expired', this.debugState);\n throw this.failure!.error;\n }\n return this.data;\n }\n\n async resolve(policy: CacheReadPolicy): Promise<CacheResult> {\n const metadata = this.metadata;\n if (metadata) {\n const { status, confirmed } = policy.assess(metadata);\n if (status === 'error') {\n debug(this.clientName, 'cache.source-error', this.debugState);\n // Source availability is not a fetch failure. Preserve the failure deadline\n // and report the source error even when retained data can no longer be served.\n return {\n data: this.canServe() ? this.read() : undefined,\n status: 'STALE',\n hasError: true,\n };\n }\n // Apply recovery evidence before read() enforces the failure deadline.\n if (confirmed) this.confirm();\n debug(this.clientName, 'cache.freshness', () => ({\n status,\n confirmed: confirmed === true,\n ...this.debugState(),\n }));\n if (status === 'fresh' || status === 'unknown') {\n // read() still enforces stale-if-error, even for a fresh assessment.\n return {\n data: this.read(),\n status: status === 'fresh' && !this.failure ? 'HIT' : 'STALE',\n };\n }\n\n if (this.failure) {\n if (!policy.retryOnFailure) {\n debug(this.clientName, 'cache.stale-if-error', () => ({\n ...this.debugState(),\n recovery: 'scheduled-source-update',\n }));\n return { data: this.read(), status: 'STALE' };\n }\n if (this.canServe()) {\n const stale = this.read();\n debug(this.clientName, 'cache.refresh.background', () => ({\n ...this.debugState(),\n reason: 'retry-after-failure',\n }));\n this.fetchInBackground();\n return { data: stale, status: 'STALE' };\n }\n }\n\n // If stale-if-error has expired, fall through to a blocking recovery fetch.\n // Calling read() here would throw before a background fetch could start.\n if (status === 'stale' && this.canServe()) {\n const stale = this.read();\n if (!policy.revalidate) {\n debug(this.clientName, 'cache.refresh.background', () => ({\n ...this.debugState(),\n reason: 'stale',\n }));\n this.fetchInBackground();\n }\n return { data: stale, status: 'STALE' };\n }\n\n if (policy.revalidate) {\n return this.resolveThroughSource(policy);\n }\n }\n\n debug(this.clientName, 'cache.refresh.blocking', () => ({\n ...this.debugState(),\n reason: !metadata ? 'empty-cache' : 'expired',\n }));\n const { promise, signal } = this.startFetch('fetched');\n try {\n await promise;\n signal.throwIfAborted();\n } catch (error) {\n if (signal.aborted) {\n // A live source supplied current data while this read awaited HTTP.\n // Shutdown uses a different reason and must still reject the read.\n if (signal.reason === SOURCE_CONFIRMED && this.data) {\n debug(this.clientName, 'cache.refresh.superseded', this.debugState);\n return { data: this.read(), status: 'HIT' };\n }\n throw error;\n }\n const stale = this.read();\n if (!stale) {\n throw error;\n }\n debug(this.clientName, 'cache.stale-if-error', () => ({\n ...this.debugState(),\n recovery: 'refresh-failed',\n }));\n return { data: stale, status: 'STALE' };\n }\n\n // Record the original request's evidence once a cold fetch discovers metadata.\n // The fetched data serves this read; source availability is checked on the next.\n if (!metadata && this.metadata) {\n const { confirmed } = policy.assess(this.metadata);\n if (confirmed) this.confirm();\n }\n // Serve the accepted cache entry; the response may have contained older data.\n const data = this.read();\n if (!data) {\n throw new Error('@vercel/flags-core: Fetch returned no definitions');\n }\n return { data, status: 'MISS' };\n }\n\n /**\n * Waits for the live source to confirm or replace the cache, then serves\n * whatever it left behind. A failed or silent source leaves the failure\n * policy to decide; the source's own fallback path takes over from there.\n */\n private async resolveThroughSource(\n policy: CacheReadPolicy,\n ): Promise<CacheResult> {\n await policy.revalidate?.().catch(() => {});\n const data = this.read();\n const metadata = this.metadata;\n if (!data || !metadata) {\n throw new Error('@vercel/flags-core: Definitions unavailable');\n }\n const { status } = policy.assess(metadata);\n return {\n data,\n status: status === 'fresh' && !this.failure ? 'HIT' : 'STALE',\n };\n }\n\n /** Runs the one shared datafile refresh used by reads and polling. */\n refresh(origin: DataOrigin = 'fetched'): Promise<void> {\n return this.startFetch(origin).promise;\n }\n\n private startFetch(origin: DataOrigin) {\n const { signal } = this.abortController;\n // Share the fetch, but let each caller assess its own request's headers.\n if (this.fetching) {\n debug(this.clientName, 'cache.fetch.shared', () => ({\n requestedOrigin: origin,\n ...this.debugState(),\n }));\n return { promise: this.fetching, signal };\n }\n debug(this.clientName, 'cache.fetch.start', () => ({\n requestedOrigin: origin,\n ...this.debugState(),\n }));\n\n const promise = Promise.resolve()\n .then(() => {\n signal.throwIfAborted();\n return this.fetch(signal);\n })\n .then((data) => {\n signal.throwIfAborted();\n this.updateFromSource(data, origin);\n debug(this.clientName, 'cache.fetch.applied', this.debugState);\n })\n .catch((error) => {\n if (signal.aborted) {\n debug(this.clientName, 'cache.fetch.aborted', () => ({\n reason:\n signal.reason === SOURCE_CONFIRMED\n ? 'source-confirmed'\n : 'cache-cleared',\n }));\n }\n signal.throwIfAborted();\n const err =\n error instanceof Error ? error : new Error('Unknown fetch error');\n this.fail(err);\n debug(this.clientName, 'cache.fetch.failed', this.debugState);\n throw err;\n })\n .finally(() => {\n // An old, aborted operation must not clear a newer one.\n if (this.abortController.signal === signal) {\n this.fetching = undefined;\n }\n });\n this.fetching = promise;\n return { promise, signal };\n }\n\n private fetchInBackground(): void {\n const { promise, signal } = this.startFetch('fetched');\n const background = promise.catch((error) => {\n if (!signal.aborted) {\n console.error('@vercel/flags-core: Revalidation failed:', error);\n }\n });\n try {\n this.waitUntil(background);\n } catch {\n // Registration is best-effort; the handled refresh continues regardless.\n }\n }\n\n /** Resets storage, age, and the failure deadline so a restarted client begins clean. */\n clear(): void {\n debug(this.clientName, 'cache.clear', this.debugState);\n this.abortController.abort();\n this.abortController = new AbortController();\n this.fetching = undefined;\n this.data = undefined;\n this.freshAt = undefined;\n this.failure = undefined;\n }\n}\n","import { version } from '../../package.json';\nimport type { BundledDefinitions } from '../types';\nimport { type Auth, authHeaders, unauthorizedMessage } from './auth';\nimport { debug } from './debug';\n\nexport const DEFAULT_FETCH_TIMEOUT_MS = 10_000;\nconst DEFAULT_MAX_ATTEMPTS = 3;\n\nclass DatafileHttpError extends Error {\n constructor(\n readonly status: number,\n statusText: string,\n ) {\n super(`Failed to fetch data: ${statusText}`);\n }\n\n get retryable(): boolean {\n return this.status === 408 || this.status === 429 || this.status >= 500;\n }\n}\n\n/**\n * Fetches the datafile with one deadline covering authentication, retries and\n * body parsing. Cancellation also settles transports that ignore the signal.\n */\nexport async function fetchDatafile(options: {\n clientName?: string;\n host: string;\n auth: Auth;\n fetch: typeof globalThis.fetch;\n signal?: AbortSignal;\n /** Total attempts, including the initial request. Defaults to three. */\n maxAttempts?: number;\n}): Promise<BundledDefinitions> {\n options.signal?.throwIfAborted();\n const maxAttempts = options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;\n if (!Number.isInteger(maxAttempts) || maxAttempts < 1) {\n throw new Error('maxAttempts must be a positive integer');\n }\n debug(options.clientName, 'datafile.fetch.start', () => ({\n maxAttempts,\n timeoutMs: DEFAULT_FETCH_TIMEOUT_MS,\n }));\n\n const controller = new AbortController();\n const { signal } = controller;\n let rejectAbort!: (reason: unknown) => void;\n const aborted = new Promise<never>((_, reject) => {\n rejectAbort = reject;\n });\n const onAbort = () => rejectAbort(signal.reason);\n signal.addEventListener('abort', onAbort, { once: true });\n const onExternalAbort = () => controller.abort(options.signal?.reason);\n options.signal?.addEventListener('abort', onExternalAbort, { once: true });\n const timeoutId = setTimeout(() => {\n debug(options.clientName, 'datafile.fetch.timeout', () => ({\n timeoutMs: DEFAULT_FETCH_TIMEOUT_MS,\n }));\n controller.abort(\n new Error('@vercel/flags-core: Datafile fetch deadline exceeded'),\n );\n }, DEFAULT_FETCH_TIMEOUT_MS);\n let delay: ReturnType<typeof setTimeout> | undefined;\n\n const fetchAttempt = async (attempt: number): Promise<BundledDefinitions> => {\n debug(options.clientName, 'datafile.fetch.attempt', () => ({\n attempt,\n maxAttempts,\n }));\n const token = await options.auth.resolveToken().catch((error) => {\n debug(options.clientName, 'datafile.auth.failed', () => ({ attempt }));\n throw error;\n });\n signal.throwIfAborted();\n const res = await options.fetch(`${options.host}/v1/datafile`, {\n headers: {\n ...authHeaders(token, options.auth.sourceProjectId),\n 'User-Agent': `VercelFlagsCore/${version}`,\n ...(process.env.VERCEL_ENV\n ? { 'X-Vercel-Env': process.env.VERCEL_ENV }\n : null),\n },\n signal,\n });\n signal.throwIfAborted();\n debug(options.clientName, 'datafile.fetch.response', () => ({\n attempt,\n status: res.status,\n }));\n if (!res.ok) {\n void res.body?.cancel().catch(() => {});\n throw new DatafileHttpError(\n res.status,\n res.status === 401 && options.auth.sourceProjectId\n ? unauthorizedMessage(options.auth.sourceProjectId)\n : res.statusText,\n );\n }\n\n const data = (await res.json()) as BundledDefinitions;\n signal.throwIfAborted();\n debug(options.clientName, 'datafile.fetch.complete', () => ({\n attempt,\n projectId: data.projectId,\n revision: data.revision,\n configUpdatedAt: Number(data.configUpdatedAt),\n }));\n return data;\n };\n\n try {\n for (let attempt = 0; ; attempt++) {\n if (attempt > 0) {\n await Promise.race([\n new Promise<void>((resolve) => {\n delay = setTimeout(resolve, 100 * 2 ** (attempt - 1));\n }),\n aborted,\n ]);\n }\n signal.throwIfAborted();\n try {\n return await Promise.race([fetchAttempt(attempt + 1), aborted]);\n } catch (error) {\n signal.throwIfAborted();\n if (\n attempt === maxAttempts - 1 ||\n (error instanceof DatafileHttpError && !error.retryable)\n ) {\n throw error instanceof Error\n ? error\n : new Error('Unknown fetch error');\n }\n debug(options.clientName, 'datafile.fetch.retry', () => ({\n nextAttempt: attempt + 2,\n delayMs: 100 * 2 ** attempt,\n status: error instanceof DatafileHttpError ? error.status : undefined,\n }));\n }\n }\n } catch (error) {\n if (signal.aborted) {\n debug(options.clientName, 'datafile.fetch.aborted', () => ({\n // The internal deadline aborts too; only the caller's signal is external.\n reason: options.signal?.aborted ? 'external' : 'timeout',\n }));\n } else {\n debug(options.clientName, 'datafile.fetch.failed', () => ({\n status: error instanceof DatafileHttpError ? error.status : undefined,\n }));\n }\n throw error;\n } finally {\n clearTimeout(timeoutId);\n clearTimeout(delay);\n signal.removeEventListener('abort', onAbort);\n options.signal?.removeEventListener('abort', onExternalAbort);\n }\n}\n","import { getRequestContext } from '../utils/request-context';\nimport type { CacheReadPolicy } from './datafile-cache';\nimport { debug } from './debug';\nimport type { NormalizedOptions } from './normalized-options';\n\n/** Request version evidence; the cache decides how to serve reads. */\nexport class HeaderSource {\n private highestObserved = 0;\n\n constructor(private readonly options: NormalizedOptions) {}\n\n private getVersionHeader(): string | undefined {\n const { headers } = getRequestContext();\n return (\n headers?.['x-vercel-flags-config-versions'] ??\n headers?.['flags-config-versions']\n );\n }\n\n /** Capture the header now so a shared fetch cannot switch the request being assessed. */\n getAssessment(): CacheReadPolicy['assess'] {\n const header = this.getVersionHeader();\n\n return (data) => {\n const headerTs = this.getUpdatedAtHeader(data.projectId, header);\n debug(this.options.clientName, 'header.observed', () => ({\n projectId: data.projectId,\n hasHeader: Boolean(header),\n headerTimestamp: headerTs,\n configUpdatedAt: Number(data.configUpdatedAt),\n previousHighestObserved: this.highestObserved,\n staleWhileRevalidateMs: this.options.staleWhileRevalidateMs,\n }));\n if (headerTs === undefined) {\n return { status: 'error' };\n }\n\n const currentTs = Number(data.configUpdatedAt);\n this.highestObserved = Math.max(this.highestObserved, headerTs);\n\n if (!Number.isFinite(currentTs) || currentTs <= 0) {\n return { status: 'error' };\n }\n\n if (headerTs <= currentTs) {\n // Older requests are satisfied without undoing a newer request's invalidation.\n return {\n status: 'fresh',\n confirmed:\n headerTs === currentTs && headerTs === this.highestObserved,\n };\n }\n\n const { staleWhileRevalidateMs } = this.options;\n return {\n status:\n staleWhileRevalidateMs > 0 && data.ageMs <= staleWhileRevalidateMs\n ? 'stale'\n : 'expired',\n };\n };\n }\n\n private getUpdatedAtHeader(projectId: string, header: string | undefined) {\n if (!header) return;\n\n const prefix = `flags_${projectId}=`;\n const value = header\n .split(';')\n .map((part) => part.trim())\n .find((part) => part.startsWith(prefix))\n ?.slice(prefix.length);\n const timestamp = Number(value);\n return Number.isFinite(timestamp) && timestamp > 0 ? timestamp : undefined;\n }\n\n isEnabled(): boolean {\n // Explicit offline mode disables header-driven refreshes too.\n return (\n this.options.vercel &&\n (this.options.stream.enabled || this.options.polling.enabled)\n );\n }\n\n stop(): void {\n this.highestObserved = 0;\n }\n}\n","import { waitUntil as defaultWaitUntil } from '@vercel/functions';\nimport type {\n DatafileInput,\n MetricEnvironment,\n PollingOptions,\n StreamOptions,\n WaitUntil,\n} from '../types';\nimport type { Auth } from './auth';\n\nconst DEFAULT_STREAM_INIT_TIMEOUT_MS = 3000;\nconst DEFAULT_POLLING_INTERVAL_MS = 30_000;\nconst MIN_POLLING_INTERVAL_MS = 30_000;\nconst DEFAULT_POLLING_INIT_TIMEOUT_MS = 3_000;\nconst DEFAULT_STALE_WHILE_REVALIDATE = 10;\n\n/**\n * Configuration options for Controller\n */\nexport type ControllerOptions = {\n /** Authentication which resolves the token for requests */\n auth: Auth;\n\n /**\n * Initial datafile to use immediately\n * - At runtime: used while waiting for stream/poll, then updated in background\n * - At build step: used as primary source (skips network)\n */\n datafile?: DatafileInput;\n\n /**\n * Configure streaming connection (runtime only, ignored during build step)\n * - `true`: Enable with default options (initTimeoutMs: 3000)\n * - `false`: Disable streaming\n * - `{ initTimeoutMs: number }`: Enable with custom timeout\n * @default true\n */\n stream?: boolean | StreamOptions;\n\n /**\n * Configure polling fallback (runtime only, ignored during build step)\n * - `true`: Enable with default options (intervalMs: 30000, initTimeoutMs: 3000)\n * - `false`: Disable polling\n * - `{ intervalMs: number, initTimeoutMs: number }`: Enable with custom options\n * @default true\n */\n polling?: boolean | PollingOptions;\n\n /**\n * Use request version headers instead of streaming or polling at runtime.\n * Initialization starts no network activity; reads fetch only when needed.\n * An evaluation without a version header permanently falls back to stream/poll.\n * Disabling both stream and polling still selects offline mode.\n * @default process.env.VERCEL === '1'\n */\n vercel?: boolean;\n\n /**\n * How long reads may serve stale cached data while refreshing in the background.\n * Header-driven reads measure from the last fetch or matching version header.\n * Streaming data is fresh for 60 seconds after the last message and polling data\n * for its interval plus the 10-second fetch deadline; this window follows, after\n * which reads wait for the refresh. Accepts finite, non-negative seconds,\n * including fractional seconds. Set to 0 to block as soon as data is no longer fresh.\n * @default 10\n */\n staleWhileRevalidate?: number;\n\n /**\n * How long runtime reads may use cached data after the first consecutive\n * stream/poll/header failure or stream disconnect. Accepts nonnegative seconds or Infinity.\n * Fractional seconds are supported.\n * Zero disables fallback immediately; positive windows include the deadline.\n * Any successful source response, or a matching stream primed revision, resets the\n * allowance. A routine stream reconnect takes about a second, so keep this above\n * the reconnect delay. Applies to evaluations and getDatafile(). Without a live\n * source, data older than staleWhileRevalidate plus this window waits for a refresh.\n * Build/offline behavior is unchanged.\n * @default Infinity\n */\n staleIfError?: number;\n\n /**\n * Override build step detection\n * - `true`: Treat as build step (use datafile/bundled only, no network)\n * - `false`: Treat as runtime (try stream/poll first)\n * @default auto-detected via CI=1 or NEXT_PHASE=phase-production-build\n */\n buildStep?: boolean;\n\n /**\n * Custom fetch function for making HTTP requests.\n * Useful for testing (e.g. resolving to a different IP).\n * @default globalThis.fetch\n */\n fetch?: typeof globalThis.fetch;\n\n /**\n * Custom function for keeping background work alive after a response has\n * been sent.\n * @default waitUntil from `@vercel/functions`\n */\n waitUntil?: WaitUntil;\n\n /**\n * Environment included with evaluation metrics sent to the ingest endpoint.\n * Falls back to the `VERCEL_ENV` environment variable when not set.\n * This does not select the environment used for flag evaluation.\n */\n metricEnvironment?: MetricEnvironment;\n\n /**\n * Custom client name included in evaluation telemetry.\n */\n clientName?: string;\n\n /**\n * Disable evaluation metrics for this client.\n * @default false\n */\n disableMetrics?: boolean;\n};\n\nexport type NormalizedOptions = {\n auth: Auth;\n datafile: DatafileInput | undefined;\n stream: { enabled: boolean; initTimeoutMs: number };\n polling: { enabled: boolean; intervalMs: number; initTimeoutMs: number };\n vercel: boolean;\n staleWhileRevalidateMs: number;\n staleIfErrorMs: number;\n buildStep: boolean;\n fetch: typeof globalThis.fetch;\n waitUntil: WaitUntil;\n host: string;\n metricEnvironment: MetricEnvironment | undefined;\n clientName: string | undefined;\n disableMetrics: boolean;\n};\n\nexport function normalizeOptions(\n options: ControllerOptions,\n): NormalizedOptions {\n const staleIfError = options.staleIfError ?? Infinity;\n if (typeof staleIfError !== 'number' || !(staleIfError >= 0)) {\n throw new Error(\n '@vercel/flags-core: staleIfError must be a nonnegative number of seconds or Infinity.',\n );\n }\n\n const autoDetectedBuildStep =\n process.env.CI === '1' ||\n process.env.NEXT_PHASE === 'phase-production-build';\n const buildStep = options.buildStep ?? autoDetectedBuildStep;\n\n let stream: NormalizedOptions['stream'];\n if (options.stream === undefined || options.stream === true) {\n stream = { enabled: true, initTimeoutMs: DEFAULT_STREAM_INIT_TIMEOUT_MS };\n } else if (options.stream === false) {\n stream = { enabled: false, initTimeoutMs: 0 };\n } else {\n stream = { enabled: true, initTimeoutMs: options.stream.initTimeoutMs };\n }\n\n let polling: NormalizedOptions['polling'];\n if (options.polling === undefined || options.polling === true) {\n polling = {\n enabled: true,\n intervalMs: DEFAULT_POLLING_INTERVAL_MS,\n initTimeoutMs: DEFAULT_POLLING_INIT_TIMEOUT_MS,\n };\n } else if (options.polling === false) {\n polling = { enabled: false, intervalMs: 0, initTimeoutMs: 0 };\n } else {\n if (options.polling.intervalMs < MIN_POLLING_INTERVAL_MS) {\n throw new Error(\n `@vercel/flags-core: Polling interval must be at least ${MIN_POLLING_INTERVAL_MS}ms, got ${options.polling.intervalMs}ms.`,\n );\n }\n polling = {\n enabled: true,\n intervalMs: options.polling.intervalMs,\n initTimeoutMs: options.polling.initTimeoutMs,\n };\n }\n\n const staleWhileRevalidate =\n options.staleWhileRevalidate ?? DEFAULT_STALE_WHILE_REVALIDATE;\n if (!Number.isFinite(staleWhileRevalidate) || staleWhileRevalidate < 0) {\n throw new Error(\n '@vercel/flags-core: staleWhileRevalidate must be a finite, non-negative number of seconds.',\n );\n }\n\n return {\n auth: options.auth,\n datafile: options.datafile,\n stream,\n polling,\n vercel: options.vercel ?? process.env.VERCEL === '1',\n staleWhileRevalidateMs: staleWhileRevalidate * 1000,\n staleIfErrorMs: staleIfError * 1000,\n buildStep,\n fetch: options.fetch ?? globalThis.fetch,\n waitUntil: options.waitUntil ?? defaultWaitUntil,\n host: 'https://flags.vercel.com',\n metricEnvironment: options.metricEnvironment,\n clientName: options.clientName,\n disableMetrics: options.disableMetrics ?? false,\n };\n}\n","/**\n * Lightweight typed event emitter base class.\n * Each source module extends this to emit typed events.\n */\nexport class TypedEmitter<\n Events extends Record<string, (...args: any[]) => void>,\n> {\n private handlers = new Map<keyof Events, Set<Events[keyof Events]>>();\n\n on<E extends keyof Events>(event: E, handler: Events[E]): void {\n let set = this.handlers.get(event);\n if (!set) {\n set = new Set();\n this.handlers.set(event, set);\n }\n set.add(handler as Events[keyof Events]);\n }\n\n off<E extends keyof Events>(event: E, handler: Events[E]): void {\n const set = this.handlers.get(event);\n if (!set) return;\n set.delete(handler as Events[keyof Events]);\n if (set.size === 0) {\n this.handlers.delete(event);\n }\n }\n\n protected emit<E extends keyof Events>(\n event: E,\n ...args: Parameters<Events[E]>\n ): void {\n const set = this.handlers.get(event);\n if (set) {\n for (const handler of set) {\n (handler as (...a: any[]) => void)(...args);\n }\n }\n }\n}\n","import type { CacheAssessment, CacheMetadata } from './datafile-cache';\nimport { debug } from './debug';\nimport { DEFAULT_FETCH_TIMEOUT_MS } from './fetch-datafile';\nimport { TypedEmitter } from './typed-emitter';\n\nexport type PollingSourceConfig = {\n clientName?: string;\n polling: {\n intervalMs: number;\n };\n staleWhileRevalidateMs: number;\n refresh: () => Promise<void>;\n};\n\nexport type PollingSourceEvents = {\n error: (error: Error) => void;\n};\n\n/**\n * Manages interval-based polling for flag data.\n * Shares the cache's HTTP refresh and emits errors.\n */\nexport class PollingSource extends TypedEmitter<PollingSourceEvents> {\n private config: PollingSourceConfig;\n private intervalId: ReturnType<typeof setInterval> | undefined;\n private abortController: AbortController | undefined;\n private polling: Promise<void> | undefined;\n\n constructor(config: PollingSourceConfig) {\n super();\n this.config = config;\n }\n\n /** Allow the scheduled poll its entire fetch deadline before revalidating. */\n get staleAfterMs(): number {\n return this.config.polling.intervalMs + DEFAULT_FETCH_TIMEOUT_MS;\n }\n\n /** Age after which reads block on a refresh. */\n get expiresAfterMs(): number {\n return this.staleAfterMs + this.config.staleWhileRevalidateMs;\n }\n\n assess = ({ ageMs }: Pick<CacheMetadata, 'ageMs'>): CacheAssessment => {\n if (ageMs === Infinity) {\n // Nothing has confirmed this entry yet; keep serving it until a poll does.\n return { status: 'unknown' };\n }\n if (ageMs <= this.staleAfterMs) {\n return { status: 'fresh' };\n }\n if (ageMs <= this.expiresAfterMs) {\n return { status: 'stale' };\n }\n return { status: 'expired' };\n };\n\n /**\n * Perform a single poll request.\n * Updates the cache on success; emits 'error' and rejects on failure.\n */\n async poll(): Promise<void> {\n if (this.polling) {\n debug(this.config.clientName, 'poll.shared');\n return this.polling;\n }\n if (this.abortController?.signal.aborted) return;\n this.abortController ??= new AbortController();\n const controller = this.abortController;\n\n debug(this.config.clientName, 'poll.start');\n this.polling = (async () => {\n try {\n await this.config.refresh();\n controller.signal.throwIfAborted();\n debug(this.config.clientName, 'poll.complete');\n } catch (error) {\n debug(\n this.config.clientName,\n controller.signal.aborted ? 'poll.aborted' : 'poll.failed',\n );\n controller.signal.throwIfAborted();\n const err =\n error instanceof Error ? error : new Error('Unknown poll error');\n this.emit('error', err);\n throw err;\n }\n })().finally(() => {\n if (this.abortController === controller) this.polling = undefined;\n });\n return this.polling;\n }\n\n /**\n * Start interval-based polling.\n * Polls at the configured interval. Does not perform an initial poll —\n * callers should call poll() first if an immediate poll is needed.\n */\n startInterval(): void {\n if (this.intervalId) return;\n\n debug(this.config.clientName, 'poll.interval.start', () => ({\n intervalMs: this.config.polling.intervalMs,\n staleAfterMs: this.config.polling.intervalMs + DEFAULT_FETCH_TIMEOUT_MS,\n expiresAfterMs:\n 2 * this.config.polling.intervalMs + DEFAULT_FETCH_TIMEOUT_MS,\n }));\n // Start interval\n this.intervalId = setInterval(\n () => void this.poll().catch(() => {}),\n this.config.polling.intervalMs,\n );\n }\n\n /**\n * Stop interval-based polling.\n */\n stop(): void {\n if (this.intervalId || this.polling) {\n debug(this.config.clientName, 'poll.stop', () => ({\n pendingPoll: this.polling !== undefined,\n }));\n }\n if (this.intervalId) {\n clearInterval(this.intervalId);\n this.intervalId = undefined;\n }\n this.abortController?.abort();\n this.abortController = undefined;\n this.polling = undefined;\n }\n}\n","/**\n * Detects the Bun runtime. Bun exposes a `Bun` global and `process.versions.bun`;\n * checking the global avoids depending on `process` being present.\n */\nexport function isBun(): boolean {\n return typeof (globalThis as { Bun?: unknown }).Bun !== 'undefined';\n}\n","export function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import { version } from '../../package.json';\nimport type { BundledDefinitions } from '../types';\nimport { isBun } from '../utils/runtime';\nimport { sleep } from '../utils/sleep';\nimport { authHeaders, unauthorizedMessage } from './auth';\nimport { debug } from './debug';\n\nexport type PrimedMessage = {\n type: 'primed';\n revision: number;\n projectId: string;\n environment: string;\n};\n\nexport type StreamMessage =\n | { type: 'datafile'; data: BundledDefinitions }\n | PrimedMessage\n | { type: 'ping' };\n\nconst MAX_RETRY_COUNT = 15;\nconst BASE_RETRY_DELAY_MS = 1000;\nconst MAX_RETRY_DELAY_MS = 60_000;\nexport const PING_MS = 30_000;\nexport const PING_TIMEOUT_MS = PING_MS * 3;\nconst PING_TIMEOUT = new Error('stream: ping timeout');\n\nfunction backoff(retryCount: number): number {\n if (retryCount === 1) return 0;\n const delay = Math.min(\n BASE_RETRY_DELAY_MS * 2 ** (retryCount - 2),\n MAX_RETRY_DELAY_MS,\n );\n return delay + Math.random() * 1000;\n}\n\nexport class UnauthorizedError extends Error {\n constructor(sourceProjectId?: string) {\n super(`stream: ${unauthorizedMessage(sourceProjectId)}`);\n this.name = 'UnauthorizedError';\n }\n}\n\nclass TokenResolutionError extends Error {\n constructor(error: unknown) {\n super('stream: token resolution failed', { cause: error });\n this.name = 'TokenResolutionError';\n }\n}\n\nexport type StreamCallbacks = {\n onDatafile: (data: BundledDefinitions) => void;\n onPrimed?: (message: PrimedMessage) => void;\n onPing?: () => void;\n onDisconnect?: () => void;\n onError?: (error: Error) => void;\n /**\n * Receives, per connection attempt, a handle that drops that connection and\n * reconnects silently, exactly like a ping timeout.\n */\n onConnection?: (reconnect: () => void) => void;\n};\n\nexport type StreamConfig = {\n clientName?: string;\n host: string;\n abortController: AbortController;\n fetch?: typeof globalThis.fetch;\n /** Returns the current revision number to send as X-Revision header */\n revision?: () => number | undefined;\n resolveToken: () => Promise<string>;\n /** Sent as X-Vercel-Flags-Project-Id when reading another project's flags */\n sourceProjectId?: string;\n};\n\n/**\n * Connects to the flags stream endpoint and handles reconnection with backoff.\n * Resolves when the first datafile message is received.\n * Rejects if the connection fails before receiving any data.\n */\nexport async function connectStream(\n config: StreamConfig,\n callbacks: StreamCallbacks,\n): Promise<void> {\n const { host, abortController, fetch: fetchFn = globalThis.fetch } = config;\n const { onDatafile, onPrimed, onPing, onDisconnect, onError, onConnection } =\n callbacks;\n let retryCount = 0;\n let lastAttemptTime = 0;\n\n const reportError = (error: unknown): void => {\n // Deliberate shutdown must not start a stale-if-error deadline.\n if (abortController.signal.aborted) return;\n onError?.(\n error instanceof Error\n ? error\n : new Error('Unknown stream error', { cause: error }),\n );\n };\n\n let resolveInit: () => void;\n let rejectInit: (error: unknown) => void;\n const initPromise = new Promise<void>((resolve, reject) => {\n resolveInit = resolve;\n rejectInit = reject;\n });\n\n void (async () => {\n let initialDataReceived = false;\n let lastError: unknown;\n\n while (!abortController.signal.aborted) {\n if (retryCount > MAX_RETRY_COUNT) {\n debug(config.clientName, 'stream.retries.exhausted', () => ({\n retryCount,\n }));\n console.error(\n '@vercel/flags-core: Max retry count exceeded',\n lastError ?? 'stream closed repeatedly without an error',\n );\n if (!initialDataReceived) {\n rejectInit!(\n new Error('stream: max retry count exceeded before receiving data'),\n );\n }\n // Silent reconnects may exhaust retries without an earlier disconnect.\n reportError(new Error('stream: max retry count exceeded'));\n onDisconnect?.();\n abortController.abort();\n break;\n }\n\n // Per-connection abort controller — allows ping timeout to abort a single\n // connection without stopping the entire retry loop.\n const connectionAbort = new AbortController();\n const onMainAbort = (): void => connectionAbort.abort();\n abortController.signal.addEventListener('abort', onMainAbort, {\n once: true,\n });\n onConnection?.(() => connectionAbort.abort(PING_TIMEOUT));\n\n let pingTimeoutId: ReturnType<typeof setTimeout> | undefined;\n const resetPingTimeout = (): void => {\n if (pingTimeoutId !== undefined) clearTimeout(pingTimeoutId);\n if (!initialDataReceived) return;\n pingTimeoutId = setTimeout(() => {\n debug(config.clientName, 'stream.ping.timeout', () => ({\n timeoutMs: PING_TIMEOUT_MS,\n }));\n lastError = PING_TIMEOUT;\n connectionAbort.abort(PING_TIMEOUT);\n }, PING_TIMEOUT_MS);\n };\n\n try {\n debug(config.clientName, 'stream.connect', () => ({ retryCount }));\n lastAttemptTime = Date.now();\n const token = await config.resolveToken().catch((error) => {\n debug(config.clientName, 'stream.auth.failed');\n throw new TokenResolutionError(error);\n });\n const headers: Record<string, string> = {\n ...authHeaders(token, config.sourceProjectId),\n 'User-Agent': `VercelFlagsCore/${version}`,\n 'X-Retry-Attempt': String(retryCount),\n };\n // The stream is long-lived NDJSON; the server flushes the compressor\n // after every message. Bun's fetch negotiates br/gzip by default but\n // its streaming decoder withholds small decoded output until more\n // compressed input arrives, so a small first datafile never surfaces\n // and init times out. Request an uncompressed body on Bun; the stream\n // carries one datafile plus tiny pings, so compression gains little.\n // See https://github.com/oven-sh/bun/issues/41439\n if (isBun()) {\n headers['Accept-Encoding'] = 'identity';\n }\n const vercelEnv = process.env.VERCEL_ENV;\n if (vercelEnv) {\n headers['X-Vercel-Env'] = vercelEnv;\n }\n const revision = config.revision?.();\n if (revision !== undefined) {\n headers['X-Revision'] = String(revision);\n }\n const response = await fetchFn(`${host}/v1/stream`, {\n headers,\n signal: connectionAbort.signal,\n });\n\n debug(config.clientName, 'stream.response', () => ({\n status: response.status,\n revision,\n }));\n if (!response.ok && response.status === 401) {\n const error = new UnauthorizedError(config.sourceProjectId);\n reportError(error);\n if (!initialDataReceived) {\n rejectInit!(error);\n }\n onDisconnect?.();\n abortController.abort();\n break;\n }\n\n if (!response.ok) {\n throw new Error(`stream was not ok: ${response.status}`);\n }\n\n if (!response.body) {\n throw new Error('stream body was not present');\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n const bufferChunks: string[] = [];\n\n // Allow the ping timeout (or main abort) to cancel the reader,\n // which breaks the read loop immediately even on a zombie connection.\n const onConnectionAbort = (): void => {\n reader.cancel().catch(() => {});\n };\n connectionAbort.signal.addEventListener('abort', onConnectionAbort, {\n once: true,\n });\n // Replacement connections also need a watchdog before their first message.\n resetPingTimeout();\n\n try {\n while (true) {\n const { done, value: chunk } = await reader.read();\n if (done || connectionAbort.signal.aborted) {\n break;\n }\n\n bufferChunks.push(decoder.decode(chunk, { stream: true }));\n const combined = bufferChunks.join('');\n bufferChunks.length = 0;\n const lines = combined.split('\\n');\n bufferChunks.push(lines.pop()!);\n\n for (const line of lines) {\n if (line === '') continue;\n\n let message: StreamMessage;\n try {\n message = JSON.parse(line) as StreamMessage;\n } catch {\n console.warn(\n '@vercel/flags-core: Failed to parse stream message, skipping',\n );\n continue;\n }\n\n if (message.type === 'datafile') {\n onDatafile(message.data);\n retryCount = 0;\n if (!initialDataReceived) {\n initialDataReceived = true;\n resolveInit!();\n }\n resetPingTimeout();\n }\n\n // Primed means the server confirmed our revision is current,\n // so no full datafile is needed. Treat it like initial data\n // for init resolution purposes.\n if (message.type === 'primed') {\n onPrimed?.(message);\n retryCount = 0;\n if (!initialDataReceived) {\n initialDataReceived = true;\n resolveInit!();\n }\n resetPingTimeout();\n }\n\n // Pings prove the connection is alive — reset retry count\n // once initial data has been received\n if (message.type === 'ping' && initialDataReceived) {\n onPing?.();\n retryCount = 0;\n resetPingTimeout();\n }\n }\n }\n } finally {\n connectionAbort.signal.removeEventListener(\n 'abort',\n onConnectionAbort,\n );\n }\n\n // Stream ended normally (server closed connection) - reconnect\n clearTimeout(pingTimeoutId);\n abortController.signal.removeEventListener('abort', onMainAbort);\n if (!abortController.signal.aborted) {\n // A suspended runtime can resume with an overdue heartbeat timer.\n // Renew the transport internally, without triggering source fallback.\n if (connectionAbort.signal.reason !== PING_TIMEOUT) {\n onDisconnect?.();\n }\n retryCount++;\n const elapsed = Date.now() - lastAttemptTime;\n const minGap = Math.max(0, BASE_RETRY_DELAY_MS - elapsed);\n const delayMs = Math.max(backoff(retryCount), minGap);\n debug(config.clientName, 'stream.reconnect', () => ({\n retryCount,\n delayMs,\n reason:\n connectionAbort.signal.reason === PING_TIMEOUT\n ? 'ping-timeout'\n : 'connection-closed',\n }));\n await sleep(delayMs);\n continue;\n }\n } catch (error) {\n clearTimeout(pingTimeoutId);\n abortController.signal.removeEventListener('abort', onMainAbort);\n if (abortController.signal.aborted) {\n break;\n }\n // A heartbeat timeout renews the transport; it is not service failure evidence.\n if (connectionAbort.signal.reason !== PING_TIMEOUT) {\n reportError(error);\n }\n if (error instanceof TokenResolutionError && !initialDataReceived) {\n rejectInit!(error.cause);\n abortController.abort();\n break;\n }\n // Ping timeout aborts only the per-connection controller; this is\n // an expected reconnect, not a real error. Stay silent on retryable\n // failures too — the error is only logged once retries are exhausted.\n if (connectionAbort.signal.reason !== PING_TIMEOUT) {\n lastError = error;\n onDisconnect?.();\n }\n retryCount++;\n const elapsed = Date.now() - lastAttemptTime;\n const minGap = Math.max(0, BASE_RETRY_DELAY_MS - elapsed);\n const delayMs = Math.max(backoff(retryCount), minGap);\n debug(config.clientName, 'stream.reconnect', () => ({\n retryCount,\n delayMs,\n reason:\n connectionAbort.signal.reason === PING_TIMEOUT\n ? 'ping-timeout'\n : 'connection-error',\n }));\n await sleep(delayMs);\n }\n }\n\n // Reject the init promise if the loop exited without receiving data\n // (e.g. aborted externally before any data arrived)\n if (!initialDataReceived) {\n rejectInit!(new Error('stream: aborted before receiving data'));\n }\n })();\n\n return initPromise;\n}\n","import type { DatafileInput } from '../types';\nimport type { CacheAssessment, CacheMetadata } from './datafile-cache';\nimport { debug } from './debug';\nimport { DEFAULT_FETCH_TIMEOUT_MS } from './fetch-datafile';\nimport type { NormalizedOptions } from './normalized-options';\nimport {\n connectStream,\n PING_MS,\n type PrimedMessage,\n} from './stream-connection';\nimport { TypedEmitter } from './typed-emitter';\n\n/** Pings arrive every 30s; tolerate one missed ping before revalidating. */\nexport const STREAM_FRESH_MS = PING_MS * 2;\n\nexport type StreamSourceEvents = {\n data: (data: DatafileInput) => void;\n primed: (message: PrimedMessage) => void;\n ping: () => void;\n connected: () => void;\n disconnected: () => void;\n error: (error: Error) => void;\n /** The connection loop gave up (retries exhausted, 401, or token failure). */\n exhausted: () => void;\n};\n\n/**\n * Manages a streaming connection to the flags service.\n * Wraps connectStream() and emits typed events.\n */\nexport class StreamSource extends TypedEmitter<StreamSourceEvents> {\n private options: NormalizedOptions;\n private revision: () => number | undefined;\n private abortController: AbortController | undefined;\n private promise: Promise<void> | undefined;\n private reconnect: (() => void) | undefined;\n private connectionStartedAt = 0;\n private revalidation:\n | { promise: Promise<void>; fail: (error: Error) => void }\n | undefined;\n\n constructor(options: NormalizedOptions, revision: () => number | undefined) {\n super();\n this.options = options;\n this.revision = revision;\n }\n\n /** Age after which reads revalidate in the background. */\n get staleAfterMs(): number {\n return STREAM_FRESH_MS;\n }\n\n /** Age after which reads block on a refresh. */\n get expiresAfterMs(): number {\n return STREAM_FRESH_MS + this.options.staleWhileRevalidateMs;\n }\n\n /** The connection loop is connecting or connected; false once it gave up or was stopped. */\n get active(): boolean {\n return this.abortController !== undefined;\n }\n\n /**\n * Drops a connection that has been silent beyond the fresh window and\n * resolves once the stream delivers any message, which confirms or replaces\n * the cache through the usual events. A younger connection, such as the\n * replacement a ping timeout just opened, is kept and awaited instead.\n * Rejects when the stream disconnects, gives up, is stopped, or stays silent\n * for the fetch deadline. Concurrent callers share one wait.\n */\n revalidate(): Promise<void> {\n if (this.revalidation) {\n return this.revalidation.promise;\n }\n if (!this.active) {\n return Promise.reject(new Error('stream: not active'));\n }\n let cleanup = (): void => {};\n let fail = (_error: Error): void => {};\n const promise = new Promise<void>((resolve, reject) => {\n const confirmed = (): void => {\n cleanup();\n resolve();\n };\n fail = (error: Error): void => {\n cleanup();\n reject(error);\n };\n const interrupted = (): void => {\n fail(new Error('stream: revalidation interrupted'));\n };\n const timer = setTimeout(() => {\n fail(new Error('stream: revalidation timed out'));\n }, DEFAULT_FETCH_TIMEOUT_MS);\n cleanup = (): void => {\n clearTimeout(timer);\n this.off('data', confirmed);\n this.off('primed', confirmed);\n this.off('ping', confirmed);\n this.off('disconnected', interrupted);\n this.off('exhausted', interrupted);\n };\n this.on('data', confirmed);\n this.on('primed', confirmed);\n this.on('ping', confirmed);\n this.on('disconnected', interrupted);\n this.on('exhausted', interrupted);\n });\n const revalidation = { promise, fail };\n this.revalidation = revalidation;\n void promise\n .catch(() => {})\n .finally(() => {\n if (this.revalidation === revalidation) {\n this.revalidation = undefined;\n }\n });\n if (Date.now() - this.connectionStartedAt >= STREAM_FRESH_MS) {\n this.reconnect?.();\n }\n return promise;\n }\n\n assess = ({ ageMs }: Pick<CacheMetadata, 'ageMs'>): CacheAssessment => {\n if (ageMs === Infinity) {\n // Nothing has confirmed this entry yet; keep serving it until the stream does.\n return { status: 'unknown' };\n }\n if (ageMs <= this.staleAfterMs) {\n return { status: 'fresh' };\n }\n if (ageMs <= this.expiresAfterMs) {\n return { status: 'stale' };\n }\n return { status: 'expired' };\n };\n\n /**\n * Start the stream connection.\n * Returns a promise that resolves when the first datafile or primed message arrives.\n * If already started, returns the existing promise.\n */\n start(): Promise<void> {\n if (this.promise) return this.promise;\n\n debug(this.options.clientName, 'stream.start', () => ({\n revision: this.revision(),\n staleAfterMs: this.staleAfterMs,\n expiresAfterMs: this.expiresAfterMs,\n }));\n const abortController = new AbortController();\n this.abortController = abortController;\n\n // Clear cached state when the stream terminates so that a subsequent\n // start() call creates a fresh connection instead of returning a stale\n // resolved promise. stop() clears the fields first, so reaching this\n // listener with them still set means the connection loop gave up itself.\n abortController.signal.addEventListener(\n 'abort',\n () => {\n if (this.abortController === abortController) {\n this.promise = undefined;\n this.abortController = undefined;\n this.emit('exhausted');\n }\n },\n { once: true },\n );\n\n try {\n const promise = connectStream(\n {\n host: this.options.host,\n clientName: this.options.clientName,\n resolveToken: () => this.options.auth.resolveToken(),\n sourceProjectId: this.options.auth.sourceProjectId,\n abortController,\n fetch: this.options.fetch,\n revision: this.revision,\n },\n {\n onDatafile: (newData) => {\n this.emit('data', newData);\n this.emit('connected');\n },\n onPrimed: (message) => {\n this.emit('primed', message);\n this.emit('connected');\n },\n onPing: () => this.emit('ping'),\n onDisconnect: () => {\n this.emit('disconnected');\n },\n onError: (error) => {\n this.emit('error', error);\n },\n onConnection: (reconnect) => {\n this.reconnect = reconnect;\n this.connectionStartedAt = Date.now();\n },\n },\n );\n\n this.promise = promise;\n return promise;\n } catch (error) {\n this.promise = undefined;\n this.abortController = undefined;\n throw error;\n }\n }\n\n /**\n * Stop the stream connection.\n */\n stop(): void {\n if (this.abortController) {\n debug(this.options.clientName, 'stream.stop');\n }\n const abortController = this.abortController;\n this.abortController = undefined;\n this.promise = undefined;\n this.reconnect = undefined;\n this.revalidation?.fail(new Error('stream: stopped'));\n abortController?.abort();\n }\n}\n","import type {\n BundledDefinitions,\n ControllerInterface,\n Datafile,\n DatafileInput,\n Metrics,\n} from '../types';\nimport { readBundledDefinitions } from '../utils/read-bundled-definitions';\nimport type { TrackReadOptions } from '../utils/usage/flags-config-read';\nimport type { TrackEvaluationOptions } from '../utils/usage/flags-evaluation';\nimport { UsageTracker } from '../utils/usage-tracker';\nimport { unauthorizedMessage } from './auth';\nimport { BundledSource } from './bundled-source';\nimport { type CacheReadPolicy, DatafileCache } from './datafile-cache';\nimport { debug } from './debug';\nimport { fetchDatafile } from './fetch-datafile';\nimport { HeaderSource } from './header-source';\nimport {\n type ControllerOptions,\n type NormalizedOptions,\n normalizeOptions,\n} from './normalized-options';\nimport { PollingSource } from './polling-source';\nimport { type PrimedMessage, UnauthorizedError } from './stream-connection';\nimport { StreamSource } from './stream-source';\nimport { originToMetricsSource, type TaggedData, tagData } from './tagged-data';\n\nexport { BundledSource } from './bundled-source';\nexport type { ControllerOptions } from './normalized-options';\nexport { PollingSource } from './polling-source';\nexport { StreamSource } from './stream-source';\n\nconst UNKNOWN_FRESHNESS: CacheReadPolicy = {\n assess: () => ({ status: 'unknown' }),\n};\n\nfunction isUnauthorizedError(error: unknown): boolean {\n if (error instanceof UnauthorizedError) {\n return true;\n }\n if (!(error instanceof Error)) {\n return false;\n }\n return (\n error.message.includes('401') || ('status' in error && error.status === 401)\n );\n}\n\n// ---------------------------------------------------------------------------\n// Internal types\n// ---------------------------------------------------------------------------\n\n/** How a stream startup attempt ended; `connecting` keeps trying in the background. */\ntype StreamStartup = 'connected' | 'connecting' | 'failed';\n\n/**\n * Explicit states for the controller state machine.\n */\ntype State =\n | 'idle'\n | 'initializing:stream'\n | 'initializing:fallback'\n | 'streaming'\n | 'polling'\n | 'vercel'\n | 'degraded'\n | 'build:loading'\n | 'build:ready'\n | 'shutdown';\n\n// ---------------------------------------------------------------------------\n// Controller\n// ---------------------------------------------------------------------------\n\n/**\n * Connects to flags.vercel.com and manages flag definitions.\n *\n * Implemented as a state machine controller that delegates all I/O to\n * source modules (StreamSource, PollingSource, BundledSource).\n *\n * **Build step** (CI=1 or Next.js build, or buildStep: true):\n * - Uses datafile (if provided), bundled definitions, or one-time fetch as fallback\n * - No streaming or polling\n *\n * **Runtime — streaming mode** (stream enabled):\n * - Streams exclusively; a startup timeout keeps connecting in the background\n * - Retains provided/bundled data during startup; fetches if the cache remains empty\n * - Stale reads serve the cache; expired reads wait for the stream to confirm, never HTTP\n * - Polling starts only once the stream gives up (retries exhausted, 401, or token failure)\n * - Without a live source (`degraded`), reads apply stale-while-revalidate over HTTP\n *\n * **Runtime — polling mode** (polling enabled, stream disabled):\n * - Uses polling exclusively; a 401 stops polling and reads revalidate over HTTP\n * - Same fallback chains as streaming mode\n *\n * **Runtime — Vercel mode** (vercel enabled, with stream or polling enabled):\n * - Loads provided/bundled data before selecting the mode; no startup network\n * - HeaderSource checks request versions and refreshes when needed\n * - An evaluation without a valid project version header permanently starts stream/poll\n * - Cache applies version acceptance and stale-if-error to all served data\n *\n * **Runtime — offline mode** (neither stream nor polling):\n * - Init fallback: constructor datafile → bundled → one-time fetch → throw\n * - Read fallback: in-memory value → constructor datafile → bundled → one-time fetch → throw\n */\nexport class Controller implements ControllerInterface {\n private options: NormalizedOptions;\n\n // State machine\n private state: State = 'idle';\n\n // Data state — tagged with origin\n private readonly cache: DatafileCache;\n\n // Memoized data spread for read() / getDatafile().\n // Rebuilt only when the cached data reference changes (e.g. on stream/poll update).\n // Holds the result of stripping `_origin`; metrics are appended per-call.\n private dataViewSource: TaggedData | undefined = undefined;\n private dataViewBase: DatafileInput | undefined = undefined;\n\n // Sources (I/O delegates)\n private streamSource: StreamSource;\n private pollingSource: PollingSource;\n private bundledSource: BundledSource;\n private headerSource: HeaderSource;\n private sourceStartup: Promise<void> | undefined;\n // Read policy while no live source confirms the cache; derived once from the options.\n private readonly degradedReadPolicy: CacheReadPolicy;\n\n // Usage tracking\n private usageTracker: UsageTracker;\n private isFirstGetData: boolean = true;\n\n // Build-step deduplication\n private buildDataPromise: Promise<TaggedData> | null = null;\n private buildReadTracked = false;\n\n // Suppresses usage tracking while the credential is rejected. Set on a 401\n // from any source, cleared as soon as a source delivers data again.\n private unauthorized = false;\n\n constructor(options: ControllerOptions) {\n this.options = normalizeOptions(options);\n this.cache = new DatafileCache(\n async (signal) => {\n try {\n const data = await fetchDatafile({ ...this.options, signal });\n signal.throwIfAborted();\n this.unauthorized = false;\n return data;\n } catch (error) {\n signal.throwIfAborted();\n this.noteUnauthorized(error);\n throw error;\n }\n },\n this.options.staleIfErrorMs,\n this.options.waitUntil,\n this.options.clientName,\n );\n\n // Create source modules\n this.streamSource = new StreamSource(\n this.options,\n () => this.cache.revision,\n );\n\n this.pollingSource = new PollingSource({\n clientName: this.options.clientName,\n polling: this.options.polling,\n staleWhileRevalidateMs: this.options.staleWhileRevalidateMs,\n refresh: () => this.cache.refresh('poll'),\n });\n this.headerSource = new HeaderSource(this.options);\n this.degradedReadPolicy = this.createDegradedReadPolicy();\n\n this.bundledSource = new BundledSource({\n auth: this.options.auth,\n readBundledDefinitions,\n });\n\n // Wire source events to state machine\n this.wireSourceEvents();\n\n // If datafile provided, use it immediately\n if (this.options.datafile) {\n this.cache.seed(tagData({ ...this.options.datafile }, 'provided'));\n }\n\n this.usageTracker = new UsageTracker(this.options);\n debug(this.options.clientName, 'client.created', () => ({\n ...this.debugState(),\n buildStep: this.options.buildStep,\n vercel: this.options.vercel,\n stream: this.options.stream.enabled,\n streamInitTimeoutMs: this.options.stream.initTimeoutMs,\n polling: this.options.polling.enabled,\n pollingIntervalMs: this.options.polling.intervalMs,\n pollingInitTimeoutMs: this.options.polling.initTimeoutMs,\n staleWhileRevalidateMs: this.options.staleWhileRevalidateMs,\n staleIfErrorMs: this.options.staleIfErrorMs,\n }));\n }\n\n // Source event handlers (stored for cleanup)\n private onStreamData = (data: DatafileInput) => {\n debug(this.options.clientName, 'stream.data', () => ({\n projectId: data.projectId,\n revision: data.revision,\n configUpdatedAt: Number(data.configUpdatedAt),\n }));\n this.unauthorized = false;\n this.cache.updateFromSource(data, 'stream');\n };\n private onStreamPrimed = (message: PrimedMessage) => {\n this.unauthorized = false;\n const confirmed = this.cache.tryConfirm(message, 'revision', 'stream');\n debug(this.options.clientName, 'stream.primed', () => ({\n ...this.debugState(),\n incomingRevision: message.revision,\n confirmed,\n }));\n // The stream is connected even if its revision no longer matches the cache.\n if (this.state === 'degraded' || this.state === 'initializing:stream') {\n this.transition('streaming', 'stream-primed');\n }\n };\n private onStreamPing = () => {\n debug(this.options.clientName, 'stream.ping', this.debugState);\n // Each connection sends primed/datafile before pings, so a ping confirms recovery.\n this.cache.confirm('stream');\n };\n private onStreamConnected = () => {\n if (this.state === 'degraded' || this.state === 'initializing:stream') {\n this.transition('streaming', 'stream-connected');\n }\n };\n private onStreamDisconnected = () => {\n debug(this.options.clientName, 'stream.disconnected', this.debugState);\n this.cache.fail(new Error('stream: disconnected'));\n // The stream reconnects on its own; polling waits until it gives up.\n if (this.state === 'streaming') {\n this.transition('degraded', 'stream-disconnected');\n }\n };\n private onStreamExhausted = () => {\n // Startup handles a stream that gives up before it connects. Later the\n // client is streaming (silent reconnects) or degraded (after a disconnect),\n // and the source chain continues with polling or HTTP revalidation.\n if (this.state !== 'streaming' && this.state !== 'degraded') {\n return;\n }\n // Reads can await this shared startup, but the event handler has no caller.\n // Handle its rejection too, including cancellation during shutdown.\n void this.activateFallbackSource().catch(() => {});\n };\n private onSourceError = (error: Error) => {\n this.noteUnauthorized(error);\n this.cache.fail(error);\n };\n private onPollingError = (error: Error) => {\n this.onSourceError(error);\n // Polling again will not fix a rejected credential. Give up like the\n // stream does; degraded reads revalidate over HTTP once it is fixed.\n if (this.state === 'polling' && isUnauthorizedError(error)) {\n this.pollingSource.stop();\n this.transition('degraded', 'polling-unauthorized');\n }\n };\n\n // ---------------------------------------------------------------------------\n // Source event wiring\n // ---------------------------------------------------------------------------\n\n private wireSourceEvents(): void {\n this.streamSource.on('data', this.onStreamData);\n this.streamSource.on('primed', this.onStreamPrimed);\n this.streamSource.on('ping', this.onStreamPing);\n this.streamSource.on('connected', this.onStreamConnected);\n this.streamSource.on('disconnected', this.onStreamDisconnected);\n this.streamSource.on('exhausted', this.onStreamExhausted);\n this.streamSource.on('error', this.onSourceError);\n this.pollingSource.on('error', this.onPollingError);\n }\n\n private unwireSourceEvents(): void {\n this.streamSource.off('data', this.onStreamData);\n this.streamSource.off('primed', this.onStreamPrimed);\n this.streamSource.off('ping', this.onStreamPing);\n this.streamSource.off('connected', this.onStreamConnected);\n this.streamSource.off('disconnected', this.onStreamDisconnected);\n this.streamSource.off('exhausted', this.onStreamExhausted);\n this.streamSource.off('error', this.onSourceError);\n this.pollingSource.off('error', this.onPollingError);\n }\n\n // ---------------------------------------------------------------------------\n // State machine\n // ---------------------------------------------------------------------------\n\n private debugState = () => ({\n state: this.state,\n mode: this.mode,\n projectId: this.cache.metadata?.projectId,\n hasData: this.cache.hasData,\n revision: this.cache.revision,\n ageMs: this.cache.ageMs,\n unauthorized: this.unauthorized,\n });\n\n private transition(to: State, reason: string): void {\n const from = this.state;\n this.state = to;\n if (from !== to) {\n debug(this.options.clientName, 'client.state', () => ({\n ...this.debugState(),\n from,\n to,\n reason,\n }));\n }\n }\n\n private get isShutdown(): boolean {\n return this.state === 'shutdown';\n }\n\n private get isConnected(): boolean {\n return this.state === 'streaming';\n }\n\n private get mode(): Metrics['mode'] {\n if (this.options.buildStep) return 'build';\n switch (this.state) {\n case 'streaming':\n return 'streaming';\n case 'polling':\n return 'polling';\n case 'vercel':\n return 'vercel';\n default:\n return 'offline';\n }\n }\n\n // ---------------------------------------------------------------------------\n // Public API (DataSource interface)\n // ---------------------------------------------------------------------------\n\n /**\n * Initializes the data source.\n *\n * Build step: datafile → bundled → one-time fetch\n * Streaming mode: stream → datafile → bundled\n * Polling mode (no stream): poll → datafile → bundled\n * Vercel mode: datafile → bundled; fetch only on a read\n * Offline mode (neither): datafile → bundled → one-time fetch\n */\n async initialize(): Promise<void> {\n debug(this.options.clientName, 'client.initialize', this.debugState);\n if (this.options.buildStep) {\n this.transition('build:loading', 'build-initialize');\n await this.initializeForBuildStep();\n this.transition('build:ready', 'build-data-ready');\n return;\n }\n\n if (this.isShutdown) {\n // Reinitialization after shutdown rewires the sources it stopped.\n this.wireSourceEvents();\n this.transition('idle', 'restart-after-shutdown');\n }\n\n // Hydrate from provided datafile if not already set (e.g., after shutdown)\n if (!this.cache.hasData && this.options.datafile) {\n this.cache.seed(tagData({ ...this.options.datafile }, 'provided'));\n }\n\n // If no data yet, try loading bundled definitions eagerly so we can\n // send the revision to the stream and potentially get a lightweight\n // \"primed\" response instead of a full datafile.\n if (!this.cache.hasData) {\n try {\n const bundled = await this.bundledSource.tryLoad();\n if (bundled) this.cache.seed(tagData({ ...bundled }, 'bundled'));\n } catch {\n // Bundled definitions not available — proceed without revision\n }\n }\n\n // Select header mode after hydration so provided/bundled data avoids a cold fetch.\n if (this.headerSource.isEnabled()) {\n this.transition('vercel', 'header-mode-enabled');\n return;\n }\n\n if (!this.options.stream.enabled && !this.options.polling.enabled) {\n await this.initializeFromFallbacks();\n return;\n }\n\n await this.activateFallbackSource();\n if (this.cache.hasData) return;\n if (this.unauthorized) {\n throw this.noDefinitionsError(\n '. Provide a datafile or bundled definitions.',\n );\n }\n\n // All update sources share the same final blocking datafile fetch.\n const fetched = await this.cache.resolve(this.cacheReadPolicy);\n if (!fetched.data) {\n await this.initializeFromFallbacks();\n }\n }\n\n /**\n * Reads the current datafile with metrics.\n */\n async read(): Promise<Datafile> {\n const startTime = Date.now();\n const cacheHadDefinitions = this.cache.hasData;\n const isFirstRead = this.isFirstGetData;\n this.isFirstGetData = false;\n\n debug(this.options.clientName, 'client.read.start', this.debugState);\n const [result, cacheStatus] = await this.resolveData().catch((error) => {\n debug(this.options.clientName, 'client.read.failed', this.debugState);\n throw error;\n });\n\n const datafile = this.toDatafile(result, cacheStatus, startTime);\n debug(this.options.clientName, 'client.read', () => ({\n ...this.debugState(),\n ...datafile.metrics,\n }));\n this.trackRead(startTime, cacheHadDefinitions, isFirstRead, datafile);\n return datafile;\n }\n\n /**\n * Shuts down the data source and releases resources.\n */\n async shutdown(): Promise<void> {\n debug(this.options.clientName, 'client.shutdown.start', this.debugState);\n this.unwireSourceEvents();\n this.streamSource.stop();\n this.pollingSource.stop();\n this.headerSource.stop();\n this.cache.clear();\n if (this.options.datafile) {\n this.cache.seed(tagData({ ...this.options.datafile }, 'provided'));\n }\n this.transition('shutdown', 'shutdown');\n await this.usageTracker.shutdown();\n debug(this.options.clientName, 'client.shutdown.complete', this.debugState);\n }\n\n /**\n * Returns the datafile with metrics as a snapshot: it never starts streaming\n * or polling. Cached data is served through the active source policy\n * (header checks and HTTP revalidation); an empty cache loads bundled\n * definitions, then performs a one-time fetch.\n */\n async getDatafile(): Promise<Datafile> {\n debug(this.options.clientName, 'client.getDatafile.start', this.debugState);\n const startTime = Date.now();\n this.isFirstGetData = false;\n\n const [result, cacheStatus] = await this.resolveSnapshot().catch(\n (error) => {\n debug(\n this.options.clientName,\n 'client.getDatafile.failed',\n this.debugState,\n );\n throw error;\n },\n );\n\n const datafile = this.toDatafile(result, cacheStatus, startTime);\n debug(this.options.clientName, 'client.getDatafile', () => ({\n ...this.debugState(),\n cacheStatus,\n origin: result._origin,\n }));\n return datafile;\n }\n\n /**\n * Returns the bundled fallback datafile.\n */\n async getFallbackDatafile(): Promise<BundledDefinitions> {\n return this.bundledSource.getRaw();\n }\n\n // ---------------------------------------------------------------------------\n // Data resolution\n // ---------------------------------------------------------------------------\n\n private toDatafile(\n result: TaggedData,\n cacheStatus: Metrics['cacheStatus'],\n startTime: number,\n ): Datafile {\n if (this.dataViewSource !== result) {\n const { _origin, ...rest } = result;\n this.dataViewBase = rest;\n this.dataViewSource = result;\n }\n\n debug(this.options.clientName, 'client.getDatafile', () => ({\n ...this.debugState(),\n cacheStatus,\n origin: result._origin,\n }));\n return {\n ...(this.dataViewBase as DatafileInput),\n metrics: {\n readMs: Date.now() - startTime,\n source: originToMetricsSource(result._origin),\n cacheStatus,\n connectionState: this.isConnected\n ? ('connected' as const)\n : ('disconnected' as const),\n mode: this.mode,\n },\n } satisfies Datafile;\n }\n\n /**\n * Resolves the current data, using the appropriate strategy for the\n * current mode. Returns tagged data and cache status.\n *\n * Build step: cached → bundled → one-time fetch\n * Runtime: source policy chooses cached data or refresh; fall back if empty.\n */\n private async resolveData(): Promise<[TaggedData, Metrics['cacheStatus']]> {\n if (this.options.buildStep) {\n return this.resolveDataForBuildStep();\n }\n\n return this.resolveRuntimeData();\n }\n\n /** Initializes the active runtime source, then resolves through its policy. */\n private async resolveRuntimeData(): Promise<\n [TaggedData, Metrics['cacheStatus']]\n > {\n if (this.sourceStartup) {\n debug(this.options.clientName, 'source.startup.shared', this.debugState);\n await this.sourceStartup;\n }\n\n if (\n !this.cache.hasData &&\n !this.options.stream.enabled &&\n !this.options.polling.enabled\n ) {\n return this.resolveStaticFallbackData();\n }\n\n const result = await this.cache.resolve(this.cacheReadPolicy);\n\n if (result.hasError || !result.data) {\n debug(this.options.clientName, 'header.fallback', () => ({\n ...this.debugState(),\n reason: 'source-assessment-error',\n }));\n await this.activateFallbackSource();\n return this.resolveRuntimeData();\n }\n\n return [result.data, result.status];\n }\n\n /**\n * Resolves data for getDatafile() without starting a runtime source.\n * Build step: cached → bundled → one-time fetch\n * Runtime: cached data through the source policy; otherwise bundled → one-time fetch → throw\n */\n private async resolveSnapshot(): Promise<\n [TaggedData, Metrics['cacheStatus']]\n > {\n if (this.options.buildStep) {\n return this.resolveDataForBuildStep();\n }\n\n if (this.sourceStartup) {\n // Share a startup already in flight, but fall back on its failure.\n await this.sourceStartup.catch(() => {});\n }\n\n // Header mode needs no network to select, so snapshots can use it too.\n if (this.state === 'idle' && this.headerSource.isEnabled()) {\n this.transition('vercel', 'header-mode-enabled');\n }\n\n if (!this.cache.hasData) {\n const bundled = await this.bundledSource.tryLoad();\n if (bundled) {\n this.cache.seed(tagData({ ...bundled }, 'bundled'));\n }\n }\n\n if (this.cache.hasData || this.state === 'vercel') {\n const result = await this.cache.resolve(this.cacheReadPolicy);\n // A source assessment error does not switch sources here; the cache's\n // failure policy still decides whether the retained entry can be served.\n const cached = result.data ?? this.cache.read();\n if (cached) {\n return [cached, result.status];\n }\n }\n\n // One-time fetch as last resort\n try {\n await this.cache.refresh();\n } catch {\n throw this.noDefinitionsError(\n '. Initialize the client or provide a datafile.',\n );\n }\n const fetched = this.cache.read();\n if (!fetched) {\n throw this.noDefinitionsError(\n '. Initialize the client or provide a datafile.',\n );\n }\n return [fetched, 'MISS'];\n }\n\n private get cacheReadPolicy(): CacheReadPolicy {\n if (this.state === 'vercel') {\n return {\n assess: this.headerSource.getAssessment(),\n retryOnFailure: true,\n };\n }\n\n if (this.state === 'streaming') {\n // The stream owns refreshes: no HTTP work competes with a live connection.\n return {\n assess: this.streamSource.assess,\n revalidate: () => this.streamSource.revalidate(),\n };\n }\n\n if (this.state === 'polling') {\n return { assess: this.pollingSource.assess };\n }\n\n if (this.state === 'degraded') {\n return this.degradedReadPolicy;\n }\n\n // Startup in progress: serve cached data until the source confirms it.\n return UNKNOWN_FRESHNESS;\n }\n\n /**\n * Without a live source nothing confirms the cache, so reads apply plain\n * stale-while-revalidate over HTTP from the two public windows: data\n * refreshed within `staleWhileRevalidateMs` is served as is, older data is\n * served while refreshing in the background, and data older than that\n * window plus `staleIfErrorMs` is expired and waits for the refresh. An\n * unknown age is not known to be old, so it refreshes in the background.\n * Offline clients never refresh.\n */\n private createDegradedReadPolicy(): CacheReadPolicy {\n if (!this.options.stream.enabled && !this.options.polling.enabled) {\n return UNKNOWN_FRESHNESS;\n }\n const { staleWhileRevalidateMs, staleIfErrorMs } = this.options;\n const expiresAfterMs = staleWhileRevalidateMs + staleIfErrorMs;\n return {\n assess: ({ ageMs }) => {\n if (ageMs <= staleWhileRevalidateMs) {\n return { status: 'fresh' };\n }\n if (ageMs === Infinity || ageMs <= expiresAfterMs) {\n return { status: 'stale' };\n }\n return { status: 'expired' };\n },\n retryOnFailure: true,\n };\n }\n\n /**\n * Advances through the runtime source chain. Every caller uses the same path:\n * request headers → stream → polling → direct cache refresh.\n */\n private activateFallbackSource(): Promise<void> {\n if (this.sourceStartup) {\n debug(this.options.clientName, 'source.startup.shared', this.debugState);\n return this.sourceStartup;\n }\n debug(this.options.clientName, 'source.startup', this.debugState);\n const startup = this.startFallbackSource().finally(() => {\n if (this.sourceStartup === startup) {\n this.sourceStartup = undefined;\n }\n debug(this.options.clientName, 'source.startup.settled', this.debugState);\n });\n this.sourceStartup = startup;\n return startup;\n }\n\n private async startFallbackSource(): Promise<void> {\n if (this.isShutdown) {\n throw new Error('@vercel/flags-core: Client is shut down');\n }\n\n if (\n (this.state === 'idle' || this.state === 'vercel') &&\n this.options.stream.enabled\n ) {\n this.transition('initializing:stream', 'start-stream');\n const outcome = await this.tryInitializeStream();\n if (this.isShutdown) {\n throw new Error('@vercel/flags-core: Client is shut down');\n }\n if (outcome === 'connected' || this.isConnected) {\n this.transition('streaming', 'stream-initialized');\n return;\n }\n if (outcome === 'connecting' && this.streamSource.active) {\n // The stream keeps connecting in the background; polling waits until it gives up.\n this.transition('degraded', 'stream-still-connecting');\n return;\n }\n }\n\n if (this.options.polling.enabled) {\n // A retried initialization keeps the active polling source and waits for its poll again.\n if (this.state !== 'polling') {\n this.transition('polling', 'start-polling');\n this.pollingSource.startInterval();\n }\n await this.initializePolling();\n if (this.isShutdown) {\n throw new Error('@vercel/flags-core: Client is shut down');\n }\n return;\n }\n\n this.transition('degraded', 'no-active-update-source');\n }\n\n // ---------------------------------------------------------------------------\n // Stream initialization\n // ---------------------------------------------------------------------------\n\n /**\n * Attempts to initialize via stream with timeout. A timeout leaves the\n * stream connecting in the background; a rejection means it gave up.\n */\n private async tryInitializeStream(): Promise<StreamStartup> {\n debug(this.options.clientName, 'stream.initialize', () => ({\n timeoutMs: this.options.stream.initTimeoutMs,\n ...this.debugState(),\n }));\n if (this.options.stream.initTimeoutMs <= 0) {\n try {\n await this.streamSource.start();\n return 'connected';\n } catch (error) {\n this.noteUnauthorized(error);\n return 'failed';\n }\n }\n\n // Race against timeout\n let timeoutId: ReturnType<typeof setTimeout>;\n const timeoutPromise = new Promise<'timeout'>((resolve) => {\n timeoutId = setTimeout(\n () => resolve('timeout'),\n this.options.stream.initTimeoutMs,\n );\n });\n\n try {\n const result = await Promise.race([\n this.streamSource.start(),\n timeoutPromise,\n ]);\n clearTimeout(timeoutId!);\n\n if (result === 'timeout') {\n debug(this.options.clientName, 'stream.initialize.timeout', () => ({\n timeoutMs: this.options.stream.initTimeoutMs,\n ...this.debugState(),\n }));\n console.warn(\n '@vercel/flags-core: Stream initialization timeout, falling back while continuing to connect in the background',\n );\n // Don't stop stream - let it continue trying in background.\n // Swallow the rejection from the background stream promise to\n // avoid unhandled promise rejections when it is eventually aborted.\n if (this.streamSource.active) {\n void this.streamSource.start().catch(() => {});\n }\n return 'connecting';\n }\n\n return 'connected';\n } catch (error) {\n clearTimeout(timeoutId!);\n this.noteUnauthorized(error);\n return 'failed';\n }\n }\n\n // ---------------------------------------------------------------------------\n // Polling initialization\n // ---------------------------------------------------------------------------\n\n /**\n * Waits for the first poll whenever polling becomes the active runtime source.\n * On timeout, initialization falls back while the pending poll and interval\n * continue in the background. Poll errors propagate if no data is cached or\n * the client is shutting down.\n */\n private async initializePolling(): Promise<void> {\n debug(this.options.clientName, 'poll.initialize', () => ({\n timeoutMs: this.options.polling.initTimeoutMs,\n ...this.debugState(),\n }));\n const poll = this.pollingSource.poll().catch((error) => {\n // Initialization can finish with retained data; serving still enforces SIE.\n if (!this.cache.hasData || this.isShutdown) {\n throw error;\n }\n debug(\n this.options.clientName,\n 'poll.initialize.fallback',\n this.debugState,\n );\n });\n const timeoutMs = this.options.polling.initTimeoutMs;\n if (timeoutMs <= 0) {\n await poll;\n return;\n }\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n try {\n const outcome = await Promise.race([\n poll,\n new Promise<'timeout'>((resolve) => {\n timeoutId = setTimeout(() => resolve('timeout'), timeoutMs);\n }),\n ]);\n if (outcome === 'timeout') {\n debug(this.options.clientName, 'poll.initialize.timeout', () => ({\n timeoutMs,\n ...this.debugState(),\n }));\n console.warn(\n '@vercel/flags-core: Polling initialization timeout, falling back while continuing to poll in the background',\n );\n }\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n private noteUnauthorized(error: unknown): void {\n if (isUnauthorizedError(error)) {\n this.unauthorized = true;\n debug(this.options.clientName, 'client.unauthorized', this.debugState);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Build step helpers\n // ---------------------------------------------------------------------------\n\n /**\n * Initializes data for build step environments.\n */\n private async initializeForBuildStep(): Promise<void> {\n if (this.cache.hasData) return;\n\n if (!this.buildDataPromise) {\n this.buildDataPromise = this.loadBuildData();\n }\n this.cache.seed(await this.buildDataPromise);\n }\n\n /**\n * Retrieves data during build steps.\n * Concurrent callers share a single load promise. The first caller to\n * populate the cache gets cacheStatus MISS; subsequent callers get HIT.\n */\n private async resolveDataForBuildStep(): Promise<\n [TaggedData, Metrics['cacheStatus']]\n > {\n const cached = this.cache.read();\n if (cached) {\n return [cached, 'HIT'];\n }\n\n if (!this.buildDataPromise) {\n this.buildDataPromise = this.loadBuildData();\n }\n\n const data = await this.buildDataPromise;\n const loaded = this.cache.read();\n if (loaded) {\n return [loaded, 'HIT'];\n }\n return [this.cache.seedAndRead(data), 'MISS'];\n }\n\n /**\n * Loads data for a build step: bundled → one-time fetch.\n */\n private async loadBuildData(): Promise<TaggedData> {\n const bundled = await this.bundledSource.tryLoad();\n if (bundled) return tagData({ ...bundled }, 'bundled');\n\n // Fallback: one-time fetch\n try {\n const fetched = await this.fetchOnce();\n return tagData({ ...fetched, fetchedAt: Date.now() }, 'fetched');\n } catch (error) {\n this.noteUnauthorized(error);\n }\n\n throw this.noDefinitionsError(\n ' during build. Provide a datafile or bundled definitions.',\n );\n }\n\n // ---------------------------------------------------------------------------\n // Fallback helpers\n // ---------------------------------------------------------------------------\n\n private fetchOnce(): Promise<DatafileInput> {\n return fetchDatafile({\n host: this.options.host,\n clientName: this.options.clientName,\n auth: this.options.auth,\n fetch: this.options.fetch,\n });\n }\n\n /**\n * Stores a last-resort fetch as a source response, which clears any failure,\n * and serves it through the cache boundary.\n */\n private acceptLastResortFetch(fetched: DatafileInput): TaggedData {\n this.cache.updateFromSource(fetched, 'fetched');\n const remote = this.cache.read();\n if (!remote) {\n throw new Error('@vercel/flags-core: Fetch returned no definitions');\n }\n return remote;\n }\n\n /**\n * Shared fallback chain used by both initialize() and resolveData().\n */\n private async initializeFromFallbacks(): Promise<void> {\n this.transition('initializing:fallback', 'initialize-static-fallback');\n\n if (this.cache.hasData) {\n this.transition('degraded', 'cached-fallback');\n return;\n }\n\n const bundled = await this.bundledSource.tryLoad();\n if (bundled) {\n this.cache.seed(tagData({ ...bundled }, 'bundled'));\n this.transition('degraded', 'bundled-fallback');\n return;\n }\n\n // Last resort: one-time fetch (only when no stream/poll configured)\n if (!this.options.stream.enabled && !this.options.polling.enabled) {\n try {\n this.acceptLastResortFetch(await this.fetchOnce());\n this.transition('degraded', 'fetched-fallback');\n return;\n } catch {\n // fetch failed — fall through to throw\n }\n }\n\n throw this.noDefinitionsError('. Bundled definitions not found.');\n }\n\n /**\n * `detail` continues the sentence \"No flag definitions available\", so it\n * starts with either \".\" or \" during build.\".\n */\n private noDefinitionsError(detail: string): Error {\n debug(this.options.clientName, 'client.no-definitions', this.debugState);\n const { sourceProjectId } = this.options.auth;\n const reason =\n this.unauthorized && sourceProjectId\n ? ` Request was ${unauthorizedMessage(sourceProjectId)}`\n : '';\n return new Error(\n `@vercel/flags-core: No flag definitions available${detail}${reason}`,\n );\n }\n\n /**\n * Retrieves data when the cache is empty in offline mode:\n * datafile → bundled → one-time fetch.\n */\n private async resolveStaticFallbackData(): Promise<\n [TaggedData, Metrics['cacheStatus']]\n > {\n // Handover can start with newer cached data; do not replace it with a seed.\n const cached = this.cache.read();\n if (cached) return [cached, 'STALE'];\n\n // Fallback chain: datafile → bundled → one-time fetch\n this.transition('initializing:fallback', 'read-static-fallback');\n\n if (this.options.datafile) {\n const provided = this.cache.seedAndRead(\n tagData({ ...this.options.datafile }, 'provided'),\n );\n this.transition('degraded', 'provided-fallback');\n return [provided, 'STALE'];\n }\n\n const bundled = await this.bundledSource.tryLoad();\n if (bundled) {\n console.warn('@vercel/flags-core: Using bundled definitions as fallback');\n const embedded = this.cache.seedAndRead(\n tagData({ ...bundled }, 'bundled'),\n );\n this.transition('degraded', 'bundled-fallback');\n return [embedded, 'STALE'];\n }\n\n // Last resort: one-time fetch (only when no stream/poll configured)\n if (!this.options.stream.enabled && !this.options.polling.enabled) {\n let fetched: DatafileInput | undefined;\n try {\n fetched = await this.fetchOnce();\n } catch {\n // fetch failed — fall through to throw\n }\n if (fetched) {\n const remote = this.acceptLastResortFetch(fetched);\n this.transition('degraded', 'fetched-fallback');\n return [remote, 'MISS'];\n }\n }\n\n throw this.noDefinitionsError(\n '. Provide a datafile or bundled definitions.',\n );\n }\n\n // ---------------------------------------------------------------------------\n // Usage tracking\n // ---------------------------------------------------------------------------\n\n /**\n * Tracks a read operation for usage analytics.\n * During build steps, only the first read is tracked.\n */\n private trackRead(\n startTime: number,\n cacheHadDefinitions: boolean,\n isFirstRead: boolean,\n datafile: Datafile,\n ): void {\n if (this.unauthorized) return;\n if (this.options.buildStep && this.buildReadTracked) return;\n if (this.options.buildStep) this.buildReadTracked = true;\n\n const configOrigin: 'in-memory' | 'embedded' =\n datafile.metrics.source === 'embedded' ? 'embedded' : 'in-memory';\n const cacheAction: 'FOLLOWING' | 'REFRESHING' | 'NONE' =\n this.state === 'streaming'\n ? 'FOLLOWING'\n : this.state === 'polling'\n ? 'REFRESHING'\n : 'NONE';\n const mode = this.mode;\n const trackOptions: TrackReadOptions = {\n configOrigin,\n cacheStatus: cacheHadDefinitions ? 'HIT' : 'MISS',\n cacheAction,\n cacheIsBlocking: !cacheHadDefinitions,\n duration: Date.now() - startTime,\n mode:\n mode === 'streaming' ? 'stream' : mode === 'polling' ? 'poll' : mode,\n };\n const configUpdatedAt = datafile.configUpdatedAt;\n if (typeof configUpdatedAt === 'number') {\n trackOptions.configUpdatedAt = configUpdatedAt;\n }\n const revision = datafile.revision;\n if (typeof revision === 'number') {\n trackOptions.revision = revision;\n }\n if (isFirstRead) {\n trackOptions.cacheIsFirstRead = true;\n }\n this.usageTracker.trackRead(trackOptions);\n }\n\n /**\n * Tracks a flag evaluation for usage analytics.\n */\n trackEvaluation(options: TrackEvaluationOptions): void {\n if (this.unauthorized || this.options.disableMetrics) return;\n\n this.usageTracker.trackEvaluation({\n ...options,\n clientName: options.clientName ?? this.options.clientName,\n });\n }\n}\n","/**\n * Factory functions for exports of index.default.ts and index.next-js.ts\n */\n\nimport { Controller, type ControllerOptions } from './controller';\nimport { Authentication } from './controller/auth';\nimport type { createCreateRawClient } from './create-raw-client';\nimport type {\n experimental_ReportExposures,\n FlagsClient,\n WaitUntil,\n} from './types';\n\n/**\n * Options for createClient\n */\nexport type CreateClientOptions<Entity = Record<string, unknown>> = Omit<\n ControllerOptions,\n 'auth'\n> & {\n /**\n * Reports experiment exposures produced by evaluation calls.\n *\n * @remarks This API is not supported for general use yet. Do not use it\n * unless Vercel has explicitly enabled it for you.\n */\n experimental_reportExposures?: experimental_ReportExposures<Entity>;\n};\n\ntype CreateClient = {\n <Entities = Record<string, unknown>>(\n options: CreateClientOptions<Entities>,\n ): FlagsClient<Entities>;\n <Entities = Record<string, unknown>>(\n sdkKeyOrConnectionString?: string,\n options?: CreateClientOptions<Entities>,\n ): FlagsClient<Entities>;\n};\n\nexport function make(\n createRawClient: ReturnType<typeof createCreateRawClient>,\n defaults: { waitUntil: WaitUntil },\n): {\n flagsClient: FlagsClient;\n resetDefaultFlagsClient: () => void;\n createClient: CreateClient;\n} {\n let _defaultFlagsClient: FlagsClient | null = null;\n\n // Insights\n // - data source must specify the environment & projectId as sdkKey has that info\n // - \"reuse\" functionality relies on the data source having the data for all envs\n function createClient<Entities = Record<string, unknown>>(\n options: CreateClientOptions<Entities>,\n ): FlagsClient<Entities>;\n function createClient<Entities = Record<string, unknown>>(\n sdkKeyOrConnectionString?: string,\n options?: CreateClientOptions<Entities>,\n ): FlagsClient<Entities>;\n function createClient<Entities = Record<string, unknown>>(\n sdkKeyOrConnectionStringOrOptions?: string | CreateClientOptions<Entities>,\n options?: CreateClientOptions<Entities>,\n ): FlagsClient<Entities> {\n const optionsOnly =\n typeof sdkKeyOrConnectionStringOrOptions === 'object' &&\n sdkKeyOrConnectionStringOrOptions !== null;\n const sdkKeyOrConnectionString = optionsOnly\n ? undefined\n : sdkKeyOrConnectionStringOrOptions;\n const createClientOptions = optionsOnly\n ? sdkKeyOrConnectionStringOrOptions\n : options;\n\n const { experimental_reportExposures, ...controllerOptions } =\n createClientOptions ?? {};\n const auth = new Authentication(sdkKeyOrConnectionString);\n const waitUntil = controllerOptions.waitUntil ?? defaults.waitUntil;\n\n // sdk key contains the environment\n const controller = new Controller({\n auth,\n ...controllerOptions,\n waitUntil,\n });\n return createRawClient<Entities>({\n controller,\n origin: {\n provider: 'vercel',\n sdkKey: auth.sdkKey,\n ...(auth.sourceProjectId ? { projectId: auth.sourceProjectId } : null),\n },\n waitUntil,\n ...(experimental_reportExposures ? { experimental_reportExposures } : {}),\n });\n }\n\n function resetDefaultFlagsClient() {\n _defaultFlagsClient = null;\n }\n\n const flagsClient: FlagsClient = new Proxy({} as FlagsClient, {\n get(_, prop) {\n if (!_defaultFlagsClient) {\n _defaultFlagsClient = createClient(process.env.FLAGS);\n }\n return _defaultFlagsClient[prop as keyof FlagsClient];\n },\n });\n\n return {\n flagsClient,\n resetDefaultFlagsClient,\n createClient,\n };\n}\n"]}