/* eslint-disable @typescript-eslint/no-explicit-any */ import type { GrowthBook, GrowthBookClient, StickyBucketService, UserScopedGrowthBook, } from ".."; import { ConditionInterface, ParentConditionInterface } from "./mongrule"; declare global { interface Window { _growthbook?: GrowthBook; } } export type VariationMeta = { passthrough?: boolean; key?: string; name?: string; }; export type FeatureRule = { id?: string; condition?: ConditionInterface; parentConditions?: ParentConditionInterface[]; force?: T; variations?: T[]; weights?: number[]; key?: string; hashAttribute?: string; fallbackAttribute?: string; hashVersion?: number; disableStickyBucketing?: boolean; bucketVersion?: number; minBucketVersion?: number; range?: VariationRange; coverage?: number; /** @deprecated */ namespace?: [string, number, number]; ranges?: VariationRange[]; meta?: VariationMeta[]; filters?: Filter[]; seed?: string; name?: string; phase?: string; tracks?: Array<{ experiment: Experiment; result: Result; }>; contextualBanditRef?: string; contextualVariations?: T[]; }; export type ContextualBanditDefinition = { banditVersion?: number; contexts: { leafId: number; condition: Record; weights: number[]; }[]; }; export type ContextualBanditDefinitions = Record< string, ContextualBanditDefinition >; export interface FeatureDefinition { defaultValue?: T; rules?: FeatureRule[]; } export type FeatureResultSource = | "unknownFeature" | "defaultValue" | "force" | "override" | "experiment" | "prerequisite" | "cyclicPrerequisite"; export interface FeatureResult { value: T | null; source: FeatureResultSource; on: boolean; off: boolean; ruleId: string; experiment?: Experiment; experimentResult?: Result; } /** @deprecated */ export type ExperimentStatus = "draft" | "running" | "stopped"; export type UrlTargetType = "regex" | "simple"; export type UrlTarget = { include: boolean; type: UrlTargetType; pattern: string; }; export type Experiment = { key: string; variations: [T, T, ...T[]]; ranges?: VariationRange[]; meta?: VariationMeta[]; filters?: Filter[]; seed?: string; name?: string; phase?: string; urlPatterns?: UrlTarget[]; weights?: number[]; condition?: ConditionInterface; parentConditions?: ParentConditionInterface[]; coverage?: number; include?: () => boolean; /** @deprecated */ namespace?: [string, number, number]; force?: number; hashAttribute?: string; fallbackAttribute?: string; hashVersion?: number; disableStickyBucketing?: boolean; bucketVersion?: number; minBucketVersion?: number; active?: boolean; persistQueryString?: boolean; contextualBandit?: CBContext; /** @deprecated */ status?: ExperimentStatus; /** @deprecated */ url?: RegExp; /** @deprecated */ groups?: string[]; }; export type AutoExperimentChangeType = "redirect" | "visual" | "unknown"; export type AutoExperiment = Experiment & { changeId?: string; // If true, require the experiment to be manually triggered manual?: boolean; }; export type ExperimentOverride = { condition?: ConditionInterface; weights?: number[]; active?: boolean; status?: ExperimentStatus; force?: number; coverage?: number; groups?: string[]; namespace?: [string, number, number]; url?: RegExp | string; }; export interface Result { value: T; variationId: number; key: string; name?: string; bucket?: number; passthrough?: boolean; inExperiment: boolean; hashUsed?: boolean; hashAttribute: string; hashValue: string; featureId: string | null; stickyBucketUsed?: boolean; leafId?: number; variationWeights?: number[]; banditVersion?: number; } export type CBContext = { leafId: number; variationWeights: number[]; banditVersion?: number; }; export type Attributes = Record; export type TrackingUserContext = Pick; export interface TrackingData { experiment: Experiment; result: Result; user?: TrackingUserContext; } export interface TrackingDataWithUser { experiment: Experiment; result: Result; user: TrackingUserContext; } export type TrackingCallback = ( experiment: Experiment, result: Result, user?: TrackingUserContext, ) => Promise | void; export type TrackingCallbackWithUser = ( experiment: Experiment, result: Result, user: TrackingUserContext, ) => Promise | void; export type FeatureUsageCallback = ( key: string, result: FeatureResult, ) => void; export type FeatureUsageCallbackWithUser = ( key: string, result: FeatureResult, user: TrackingUserContext, ) => void; // Callback types for internal plugin subscriptions (e.g. session replay). // Must be synchronous — async callbacks are not awaited and rejected promises won't be caught. export type FeatureUsageSubCallback = ( key: string, result: Readonly>, ) => void; export type CustomEventSubCallback = ( eventName: string, properties: Readonly>, ) => void; export type Plugin = ( gb: GrowthBook | UserScopedGrowthBook | GrowthBookClient, ) => void; export type EventProperties = Record; export type EventLogger = ( eventName: string, properties: EventProperties, userContext: TrackingUserContext, ) => void | Promise; export type NavigateCallback = (url: string) => void | Promise; export type ApplyDomChangesCallback = ( changes: AutoExperimentVariation, ) => () => void; export type RenderFunction = () => void; // Constructor Options export type Options = { enabled?: boolean; attributes?: Attributes; url?: string; features?: Record; experiments?: AutoExperiment[]; forcedVariations?: Record; forcedFeatureValues?: Map; attributeOverrides?: Attributes; blockedChangeIds?: string[]; disableVisualExperiments?: boolean; disableJsInjection?: boolean; jsInjectionNonce?: string; disableUrlRedirectExperiments?: boolean; disableCrossOriginUrlRedirectExperiments?: boolean; disableExperimentsOnLoad?: boolean; stickyBucketAssignmentDocs?: Record< StickyAttributeKey, StickyAssignmentsDocument >; stickyBucketService?: StickyBucketService; debug?: boolean; log?: (msg: string, ctx: any) => void; qaMode?: boolean; /** @deprecated */ backgroundSync?: boolean; /** @deprecated */ subscribeToChanges?: boolean; enableDevMode?: boolean; disableCache?: boolean; /** @deprecated */ disableDevTools?: boolean; trackingCallback?: TrackingCallback; onFeatureUsage?: FeatureUsageCallback; eventLogger?: EventLogger; cacheKeyAttributes?: (keyof Attributes)[]; /** @deprecated */ user?: { id?: string; anonId?: string; [key: string]: string | undefined; }; /** @deprecated */ overrides?: Record; /** @deprecated */ groups?: Record; apiHost?: string; streamingHost?: string; apiHostRequestHeaders?: Record; streamingHostRequestHeaders?: Record; clientKey?: string; renderer?: null | RenderFunction; decryptionKey?: string; remoteEval?: boolean; navigate?: NavigateCallback; navigateDelay?: number; maxNavigateDelay?: number; /** @deprecated */ antiFlicker?: boolean; /** @deprecated */ antiFlickerTimeout?: number; applyDomChangesCallback?: ApplyDomChangesCallback; savedGroups?: SavedGroupsValues; contextualBandits?: ContextualBanditDefinitions; plugins?: Plugin[]; }; export type ClientOptions = { enabled?: boolean; debug?: boolean; globalAttributes?: Attributes; forcedVariations?: Record; forcedFeatureValues?: Map; log?: (msg: string, ctx: any) => void; qaMode?: boolean; disableCache?: boolean; trackingCallback?: TrackingCallbackWithUser; onFeatureUsage?: ( key: string, result: FeatureResult, user: TrackingUserContext, ) => void; eventLogger?: EventLogger; apiHost?: string; streamingHost?: string; apiHostRequestHeaders?: Record; streamingHostRequestHeaders?: Record; clientKey?: string; decryptionKey?: string; savedGroups?: SavedGroupsValues; contextualBandits?: ContextualBanditDefinitions; plugins?: Plugin[]; }; // Contexts export type GlobalContext = { log: (msg: string, ctx: any) => void; features?: FeatureDefinitions; experiments?: AutoExperiment[]; enabled?: boolean; qaMode?: boolean; savedGroups?: SavedGroupsValues; contextualBandits?: ContextualBanditDefinitions; forcedVariations?: Record; forcedFeatureValues?: Map; trackingCallback?: TrackingCallbackWithUser; onFeatureUsage?: FeatureUsageCallbackWithUser; onExperimentEval?: (experiment: Experiment, result: Result) => void; saveDeferredTrack?: (data: TrackingData) => void; recordChangeId?: (changeId: string) => void; eventLogger?: EventLogger; /** @deprecated */ overrides?: Record; /** @deprecated */ groups?: Record; /** @deprecated */ user?: { id?: string; anonId?: string; [key: string]: string | undefined; }; }; // Some global fields can be overridden by the user, others are always user-level export type UserContext = { enabled?: boolean; qaMode?: boolean; enableDevMode?: boolean; attributes?: Attributes; url?: string; blockedChangeIds?: string[]; stickyBucketAssignmentDocs?: Record< StickyAttributeKey, StickyAssignmentsDocument >; saveStickyBucketAssignmentDoc?: ( doc: StickyAssignmentsDocument, ) => Promise; forcedVariations?: Record; forcedFeatureValues?: Map; attributeOverrides?: Attributes; trackingCallback?: TrackingCallback; onFeatureUsage?: FeatureUsageCallback; trackedExperiments?: Set; trackedFeatureUsage?: Record; devLogs?: LogUnion[]; featureUsageSubs?: Set; }; export type StackContext = { id?: string; evaluatedFeatures: Set; }; export type EvalContext = { global: GlobalContext; user: UserContext; stack: StackContext; }; export type PrefetchOptions = Pick< Options, | "decryptionKey" | "apiHost" | "apiHostRequestHeaders" | "streamingHost" | "streamingHostRequestHeaders" > & { clientKey: string; streaming?: boolean; skipCache?: boolean; }; export type SubscriptionFunction = ( experiment: Experiment, result: Result, ) => void; export type VariationRange = [number, number]; export interface InitResponse { // If a payload was set success: boolean; // Where the payload came from, if set source: "init" | "cache" | "network" | "error" | "timeout"; // If the payload could not be set (success = false), this will hold the fetch error error?: Error; } export interface FetchResponse { data: FeatureApiResponse | null; success: boolean; source: "cache" | "network" | "error" | "timeout"; error?: Error; } export type JSONValue = | null | number | string | boolean | Array | Record | { [key: string]: JSONValue }; export type WidenPrimitives = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T; export type DOMMutation = { selector: string; action: string; attribute: string; value?: string; parentSelector?: string; insertBeforeSelector?: string; }; export type AutoExperimentVariation = { domMutations?: DOMMutation[]; css?: string; js?: string; urlRedirect?: string; }; export type FeatureDefinitions = Record; export type FeatureApiResponse = { features?: FeatureDefinitions; dateUpdated?: string; encryptedFeatures?: string; experiments?: AutoExperiment[]; encryptedExperiments?: string; savedGroups?: SavedGroupsValues; encryptedSavedGroups?: string; contextualBandits?: ContextualBanditDefinitions; encryptedContextualBandits?: string; }; // Alias export type GrowthBookPayload = FeatureApiResponse; // Polyfills required for non-standard browser environments (ReactNative, Node, etc.) // These are typed as `any` since polyfills like `node-fetch` are not 100% compatible with native types export type Polyfills = { fetch: any; SubtleCrypto: any; EventSource: any; localStorage?: LocalStorageCompat; }; export type Helpers = { fetchFeaturesCall: ({ host, clientKey, headers, }: { host: string; clientKey: string; headers?: Record; }) => Promise; fetchRemoteEvalCall: ({ host, clientKey, payload, headers, }: { host: string; clientKey: string; payload: any; headers?: Record; }) => Promise; eventSourceCall: ({ host, clientKey, headers, }: { host: string; clientKey: string; headers?: Record; }) => EventSource; startIdleListener: () => (() => void) | void; stopIdleListener: () => void; }; export interface LocalStorageCompat { getItem(key: string): string | null | Promise; setItem(key: string, value: string): void | Promise; } export type CacheSettings = { backgroundSync: boolean; cacheKey: string; staleTTL: number; maxAge: number; maxEntries: number; disableIdleStreams: boolean; idleStreamInterval: number; disableCache: boolean; }; export type ApiHost = string; export type ClientKey = string; export type InitOptions = { timeout?: number; skipCache?: boolean; payload?: FeatureApiResponse; streaming?: boolean; cacheSettings?: CacheSettings; }; export type InitSyncOptions = { payload: FeatureApiResponse; streaming?: boolean; }; export type LoadFeaturesOptions = { /** @deprecated */ autoRefresh?: boolean; timeout?: number; skipCache?: boolean; }; export type RefreshFeaturesOptions = { timeout?: number; skipCache?: boolean; }; export type DestroyOptions = { destroyAllStreams?: boolean; }; export interface Filter { // Override the hashAttribute used for this filter attribute?: string; // The hash seed seed: string; // The hashing version to use hashVersion: number; // Only include these resulting ranges ranges: VariationRange[]; } export type StickyAttributeKey = string; // `${attributeName}||${attributeValue}` export type StickyExperimentKey = string; // `${experimentId}__{version}` export type StickyAssignments = Record; export interface StickyAssignmentsDocument { attributeName: string; attributeValue: string; assignments: StickyAssignments; } export type SavedGroupsValues = Record; export type BaseLog = { timestamp: string; }; export type EventLog = BaseLog & { logType: "event"; eventName: string; properties?: Record; }; export type ExperimentLog = BaseLog & { logType: "experiment"; experiment: Experiment; result: Result; }; export type FeatureLog = BaseLog & { logType: "feature"; featureKey: string; result: FeatureResult; }; export type LogUnion = EventLog | ExperimentLog | FeatureLog;