/** * Resolving the environment into one immutable rollout snapshot. * * READ ONCE, ASKED MANY TIMES. Every decision is made here and then frozen, so a * feature cannot answer differently at two points in a run because something * mutated process.env in between. That has a cost -- a caller holding a snapshot * will not see a later change -- and it is the point: a request that began with a * feature on must not finish with it off. * * PRECEDENCE, AND WHY IT DIFFERS FROM THE OBVIOUS DESIGN. The tidy rule would be * that the channel decides availability and nothing narrower may override it. That * rule would be wrong here, because every feature in the registry ALREADY SHIPPED * with its own environment variable and users have those set today. Making a * channel veto them would turn "we added a rollout system" into "your flag stopped * working", silently, with the flag still sitting in the settings file. So: * * 1. an off value in the feature's own variable -> Disabled * 2. a name in TOKEN_OPTIMIZER_DISABLE_FEATURES -> Disabled * 3. an on value in the feature's own variable -> Explicit (beats the channel) * 4. a name in TOKEN_OPTIMIZER_FEATURES -> Requested (channel-gated) * 5. the channel enables it by default -> Default * 6. otherwise -> NotRequested * * Disabling always wins, at every level: a kill switch that can be outvoted is not * a kill switch. The generic request in (4) IS channel-gated, because unlike the * per-feature variable it is new -- nobody has it set already, so gating it breaks * nothing and it is the only place the channel can mean anything. * * Unknown names are dropped, not guessed at, and named in the refusals so the * inspector can show a user that the feature they thought they enabled does not * exist. `strict` throws instead, for the inspector and the tests. */ import { RolloutChannel } from './channel.js'; import { FEATURES, FeatureName } from './features.js'; export declare const ROLLOUT_SCHEMA_VERSION = 1; export declare const CHANNEL_ENV = "TOKEN_OPTIMIZER_ROLLOUT_CHANNEL"; export declare const REQUEST_ENV = "TOKEN_OPTIMIZER_FEATURES"; export declare const DISABLE_ENV = "TOKEN_OPTIMIZER_DISABLE_FEATURES"; /** Why a feature ended up the way it did. Reported, never inferred by a caller. */ export declare enum DecisionReason { /** The channel runs it without being asked. */ Default = "default", /** Its own environment variable says on. */ Explicit = "explicit", /** Named in TOKEN_OPTIMIZER_FEATURES, and the channel allows it. */ Requested = "requested", /** Named in TOKEN_OPTIMIZER_FEATURES, but this channel does not carry it. */ BlockedByChannel = "blocked_by_channel", /** Switched off, by its own variable or by name. */ Disabled = "disabled", /** Nobody asked and no channel default applies. */ NotRequested = "not_requested" } export interface FeatureDecision { readonly name: FeatureName; readonly enabled: boolean; readonly reason: DecisionReason; /** The switch that turned it off, when one did, and null when none did. */ readonly by: string | null; readonly availableIn: RolloutChannel; readonly defaultEnabledIn: RolloutChannel | null; readonly env: string; } export interface RolloutSnapshot { readonly schemaVersion: number; readonly channel: RolloutChannel; readonly registryDigest: string; readonly decisions: readonly FeatureDecision[]; /** Every input this run could not use, in the order it was read. */ readonly refusals: readonly string[]; isEnabled(feature: FeatureName): boolean; decisionFor(feature: FeatureName): FeatureDecision; readonly enabled: readonly FeatureName[]; /** A stable identity for the whole resolved state, for comparing two runs. */ digest(): string; } export interface ResolveOptions { /** Extra names to request, as if they had been listed in the environment. */ readonly requested?: readonly string[]; /** Extra names to switch off. */ readonly disabled?: readonly string[]; /** Throw on anything unusable instead of collecting it. */ readonly strict?: boolean; } export declare function resolveRollout(env?: NodeJS.ProcessEnv, options?: ResolveOptions): RolloutSnapshot; /** * The one-flag question, for a call site that has no snapshot to hold. * * Resolving the whole environment to answer about one flag looks wasteful and is * not: it is a dozen string reads with no I/O, and the alternative -- each site * reading its own variable with its own idea of what "on" means -- is the state * this module replaced. Sites that ask repeatedly in one request should hold a * snapshot instead, so their answers cannot drift apart. */ export declare function featureEnabled(feature: FeatureName, env?: NodeJS.ProcessEnv): boolean; export { FeatureName, FEATURES }; //# sourceMappingURL=resolve.d.ts.map