type MetricEnvironment = 'development' | 'preview' | 'production'; /** * Options for stream connection behavior */ type StreamOptions = { /** Timeout in ms to wait for initial stream connection before falling back */ initTimeoutMs: number; }; /** * Options for polling behavior */ type PollingOptions = { /** Interval in ms between polling requests */ intervalMs: number; /** Timeout in ms to wait for initial poll before falling back */ initTimeoutMs: number; }; /** Input type for creating a datafile (without metrics) */ type DatafileInput = Packed.Data & { /** * If a data source is used with a specific sdk key then * the sdk key or data source might contain information * about the environment to be evaluated */ environment: string; /** Vercel project id of the source of these flags */ projectId: string; /** * Some older responses might return a string instead of a number. Both will be timestamps. */ configUpdatedAt?: number | string; /** Version number of the data */ revision?: number; }; /** Datafile with metrics attached (returned by the client) */ type Datafile = DatafileInput & { /** Metrics about how the data was retrieved */ metrics: Metrics; }; /** Flag Definitions of a Vercel project */ type BundledDefinitions = DatafileInput & { /** when the data was last updated */ configUpdatedAt: number; /** hash of the data */ digest: string; /** version number of the data */ revision: number; }; /** * Metrics about how data was retrieved and evaluated */ type Metrics = { /** Time in ms to read the datafile */ readMs: number; /** Where the data came from */ source: 'in-memory' | 'embedded' | 'remote'; /** Whether data was already cached, or stale (fallback used) */ cacheStatus: 'HIT' | 'MISS' | 'STALE'; /** Whether the stream is currently connected */ connectionState: 'connected' | 'disconnected'; /** The current operating mode of the client */ mode: 'streaming' | 'polling' | 'build' | 'offline'; /** Time in ms for the pure flag evaluation logic (only present on EvaluationResult) */ evaluationMs?: number; }; /** * DataSource interface for the Vercel Flags client */ interface ControllerInterface { /** * Initialize the data source by fetching the initial file or setting up polling or * subscriptions. * * @see https://openfeature.dev/specification/sections/providers#requirement-241 */ initialize: () => Promise; /** * Returns the in-memory data file, which was loaded from initialize and maybe updated from streams. */ read(): Promise; /** * End polling or subscriptions. Flush any remaining data. */ shutdown(): void; /** * Return the actual datafile containing flag definitions. */ getDatafile(): Promise; /** * Returns the bundled fallback definitions. * Throws FallbackNotFoundError if the fallback file doesn't exist. * Throws FallbackEntryNotFoundError if the file exists but has no entry for the SDK key. */ getFallbackDatafile?(): Promise; } /** * Input for a single flag in a bulk evaluation call. */ type BulkEvaluateInput = { key: string; defaultValue?: T; }; /** * A client for Vercel Flags */ type FlagsClient> = { /** * Origin information for this client. * sdkKey is only present when the client was explicitly created with one. */ origin?: { provider: string; sdkKey?: string; }; /** * Evaluate a feature flag * * Requires initialize() to have been called and awaited first. * * @param flagKey * @param defaultValue * @param entities * @returns */ evaluate: (flagKey: string, defaultValue?: T, entities?: E) => Promise>; /** * Evaluate multiple feature flags against the same entities in a single call. * * Avoids the per-flag overhead of separate `evaluate()` invocations (in particular, * the parallel promises and repeated datafile reads they would entail). * * Requires initialize() to have been called and awaited first. * * @param flags Array of `{ key, defaultValue? }` entries to evaluate. * @param entities Shared entities used for every flag in the bulk call. * @returns Object mapping each key to its EvaluationResult. */ bulkEvaluate: (flags: BulkEvaluateInput[], entities?: E) => Promise>>; /** * Retrieve the latest datafile during startup, and set up subscriptions if needed. */ initialize(): void | Promise; /** * Facilitates a clean shutdown process which may include flushing telemetry information, or closing remote connections. */ shutdown(): void | Promise; /** * Returns the actual datafile containing flag definitions */ getDatafile(): Promise; /** * Returns the bundled fallback definitions. * Throws FallbackNotFoundError if the fallback file doesn't exist. * Throws FallbackEntryNotFoundError if the file exists but has no entry for the SDK key. */ getFallbackDatafile(): Promise; }; type EvaluationParams = { entities?: Record; environment: string; segments?: Record; definition: Packed.FlagDefinition; defaultValue?: T; }; /** * ErrorCodes that can happen during evaluation */ declare enum ErrorCode { /** * The value was resolved before the provider was ready. */ /** * The provider has entered an irrecoverable error state. */ /** * The flag could not be found. */ FLAG_NOT_FOUND = "FLAG_NOT_FOUND" } /** * The detailed result of a flag evaluation as returned by the client's `evaluate` function. */ type EvaluationResult = { /** * In case of successful evaluations this holds the evaluated value */ value: T; /** * Indicates whether the outcome was a single variant or a split */ outcomeType?: OutcomeType; /** * The variant we want to report for o11y */ variantId: VariantId | null; /** * Indicates why the flag evaluated to a certain value */ reason: Exclude; errorMessage?: never; errorCode?: never; /** Metrics about the evaluation (optional) */ metrics?: Metrics; } | { reason: ResolutionReason.ERROR; errorMessage: string; errorCode?: ErrorCode; outcomeType?: never; /** * The variant we want to report for o11y */ variantId: VariantId | null; /** * In cases of errors this is the defaultValue if one was provided */ value?: T; /** Metrics about the evaluation (optional) */ metrics?: Metrics; }; type FlagKey = string; type VariantId = string; type EnvironmentKey = string; type SegmentId = string; /** * The value of a feature flag variant */ type Value = string | number | boolean | null | { [key: string]: Value; } | Value[]; declare enum ResolutionReason { PAUSED = "paused", TARGET_MATCH = "target_match", RULE_MATCH = "rule_match", FALLTHROUGH = "fallthrough", ERROR = "error" } declare enum OutcomeType { /** When the outcome type was a single variant */ VALUE = "value", /** When the outcome type was a split */ SPLIT = "split", /** When the outcome type was a progressive rollout */ ROLLOUT = "rollout" } /** * Vercel Flags * - is equal to (eq) * - is not equal to (!eq) * - is one of (oneOf) * - is not one of (!oneOf) * - contains (contains) * - does not contain (!contains) * - starts with (startsWith) * - does not start with (!startsWith) * - ends with (endsWith) * - does not end with (!endsWith) * - exists (ex) * - does not exist (!ex) * - is greater than (gt) * - is greater than or equal to (gte) * - is lower than (lt) * - is lower than or equal to (lte) * - matches regex (regex) * - does not match regex (!regex) * - is before (before) * - is after (after) */ declare enum Comparator { /** * lhs must be string | number * rhs must be string | number * does a strict equality check */ EQ = "eq", /** * lhs must be string | number * rhs must be string | number * does a strict equality check */ NOT_EQ = "!eq", /** * lhs must be string * rhs must be string[] */ ONE_OF = "oneOf", /** * lhs must be string * rhs must be string[] */ NOT_ONE_OF = "!oneOf", /** * lhs must be string[] * rhs must be string[] */ CONTAINS_ALL_OF = "containsAllOf", /** * lhs must be string[] * rhs must be string[] */ CONTAINS_ANY_OF = "containsAnyOf", /** * lhs must be string[] * rhs must be string[] */ CONTAINS_NONE_OF = "containsNoneOf", /** * lhs must be string * rhs must be string * * other comparisons have to be handled with a regex */ STARTS_WITH = "startsWith", /** * lhs must be string * rhs must be string * * other comparisons have to be handled with a regex */ NOT_STARTS_WITH = "!startsWith", /** * lhs must be string * rhs must be string * * other comparisons have to be handled with a regex */ ENDS_WITH = "endsWith", /** * lhs must be string * rhs must be string * * other comparisons have to be handled with a regex */ NOT_ENDS_WITH = "!endsWith", /** * lhs must be string * rhs must be string * * checks if lhs contains rhs as a substring */ CONTAINS = "contains", /** * lhs must be string * rhs must be string * * checks if lhs does not contain rhs as a substring */ NOT_CONTAINS = "!contains", /** * lhs must be string * rhs must be never */ EXISTS = "ex", /** * lhs must be string * rhs must be never */ NOT_EXISTS = "!ex", /** * lhs must be string | number * rhs must be string | number */ GT = "gt", /** * lhs must be string | number * rhs must be string | number */ GTE = "gte", /** */ /** * lhs must be string | number * rhs must be string | number */ LT = "lt", /** * lhs must be string | number * rhs must be string | number */ LTE = "lte", /** * lhs must be string * rhs must be string */ REGEX = "regex", /** * lhs must be string * rhs must be string */ NOT_REGEX = "!regex", /** * lhs must be date string * rhs must be date string * * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#date_time_string_format */ BEFORE = "before", /** * lhs must be date string * rhs must be date string * * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#date_time_string_format */ AFTER = "after" } declare namespace Packed { /** * Idenitifies a variant based on its index in the variants array. */ type VariantIndex = number; type Data = { /** map of flag keys to definitions */ definitions: Record; /** segments keyed by id */ segments?: Record; }; enum AccessorType { SEGMENT = "segment", ENTITY = "entity" } type SplitOutcome = { type: 'split'; /** * Based on which attribute the traffic should be split. */ base: EntityAccessor; /** * The distribution of the individual groups. * * We use a single number array as the numbers will be placed in the * same order as the variant list. * * So index 0 here is the distribution for variant 0, and so on. */ weights: number[]; /** * This variant will be used when the lhs does not exist */ defaultVariant: VariantIndex; }; type RolloutOutcome = { type: 'rollout'; /** * Based on which attribute the traffic should be split. */ base: EntityAccessor; /** * Epoch ms when the rollout begins. */ startTimestamp: number; /** * Variant index to roll away from. */ rollFromVariant: VariantIndex; /** * Variant index to roll towards. */ rollToVariant: VariantIndex; /** * This variant will be used when the base attribute does not exist. */ defaultVariant: VariantIndex; /** * Progressive rollout slots. * Each tuple: [promille 0-100_000 for rollToVariant, durationMs (how long this slot is served)] * 1 = 0.001% * 1_000 = 1% * 100_000 = 100% * * Once all slots are exhausted, the rollout is complete (100% rollToVariant). */ slots: [number, number][]; }; type SegmentAllOutcome = 1; type SegmentSplitOutcome = { type: 'split'; /** * Based on which attribute the traffic should be split. * * When the attribute does not exist the segment will not match. */ base: EntityAccessor; /** * The promille that should pass the segment (1 = 0.001%; 1000 = 1%) */ passPromille: number; }; type SegmentOutcome = SegmentAllOutcome | SegmentSplitOutcome; type Outcome = VariantIndex | SplitOutcome | RolloutOutcome; type EntityAccessor = (string | number)[]; type SegmentAccessor = 'segment'; /** * An array means an entity */ type LHS = EntityAccessor | SegmentAccessor; /** * undefined when the rhs is not used by the comparator * string[] when the rhs is a list of segments * { type: 'regex'; pattern: string; flags: string } when the rhs is a regex */ type RHS = undefined | string | number | boolean | (string | number)[] | { type: 'regex'; pattern: string; flags: string; }; type ConditionOptions = { /** When true, string comparisons are case-insensitive. */ i?: boolean; }; type Condition = [LHS, Comparator, RHS] | [LHS, Comparator, RHS, ConditionOptions | 'i'] | [LHS, Comparator.EXISTS] | [LHS, Comparator.NOT_EXISTS]; type Rule = { conditions: Condition[]; outcome: Outcome; }; type SegmentRule = { conditions: Condition[]; outcome: SegmentAllOutcome | SegmentSplitOutcome; }; type EnvironmentConfig = /** * Paused flags contain the pausedOutcome only. */ number /** Allows reusing the configuration of another environment */ | { reuse: EnvironmentKey; } /** * Active flags don't contain an explicit "active" state. * The fact that they have a config means they are active. */ | { /** * Each array item represents a variant. * * Each slot holds the targets for that variant. * * So the target list at index 0 is the targets for variant 0, and so on. */ targets?: TargetList[]; rules?: Rule[]; fallthrough: Outcome; }; /** * A list of targets * * @example * { * user: { id: string[] } * } */ type TargetList = Record>; /** * reusable conditions, with no outcome attached */ type Segment = { rules?: SegmentRule[]; /** * Explicitly include targets. Included targets will bypass conditions and exclusion. * * @example * include: { * user: { id: string[] } * } */ include?: TargetList; /** * Explicitly exclude targets. Excluded targets will not be included in the segment, and bypass conditions. * * @example * exclude: { * user: { id: string[] } * } */ exclude?: TargetList; }; type FlagDefinition = { /** for backwards compatibility with HappyKit */ variantIds?: string[]; /** variants, packed down to just their values */ variants: Value[]; /** environments */ environments: Record; /** * A random seed to prevent split points in different flags * from having the same targets. Otherwise the same set of ids would be * opted into all flags for every rollout. By using a different seed for * each flag the distribution is different for every flag. * * We don't use the slug as it might change, but we don't want the distribution * to change when the slug changes. * * We don't use the id or createdAt etc as we want to be able to redistirbute * by changing the seed. */ seed?: number; }; } export { type BundledDefinitions as B, type ControllerInterface as C, type DatafileInput as D, type EvaluationParams as E, type FlagsClient as F, type MetricEnvironment as M, type PollingOptions as P, ResolutionReason as R, type StreamOptions as S, type VariantId as V, type Datafile as a, type EvaluationResult as b, Packed as c, type Value as d };