{"version":3,"file":"flag-exposure-tracker.mjs","sources":["../../../../src/lib/flags/analytics/flag-exposure-tracker.ts"],"sourcesContent":["/**\n * @fileoverview Feature flag exposure tracking for experiments and A/B tests.\n *\n * Provides comprehensive exposure tracking:\n * - User exposure recording\n * - Deduplication\n * - Cohort assignment\n * - Experiment integration\n *\n * @module flags/analytics/flag-exposure-tracker\n *\n * @example\n * ```typescript\n * const tracker = new ExposureTracker({\n *   deduplicationWindow: 24 * 60 * 60 * 1000, // 24 hours\n *   onExposure: (exposure) => analytics.track('flag_exposure', exposure),\n * });\n *\n * // Track exposure\n * tracker.trackExposure({\n *   flagKey: 'new-checkout',\n *   variantId: 'variant-b',\n *   userId: 'user-123',\n * });\n *\n * // Get exposure data\n * const exposures = tracker.getExposures('user-123');\n * ```\n */\n\nimport type {\n  // FlagId,\n  VariantId,\n  UserId,\n  EvaluationReason,\n  JsonValue,\n} from '../advanced/types';\n// import type { Mutable } from '../../utils/types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Exposure event data.\n */\nexport interface ExposureEvent {\n  /** Flag key */\n  readonly flagKey: string;\n  /** Assigned variant */\n  readonly variantId: VariantId;\n  /** User ID */\n  readonly userId: UserId;\n  /** Session ID */\n  readonly sessionId?: string;\n  /** Experiment ID if part of experiment */\n  readonly experimentId?: string;\n  /** Cohort assignment */\n  readonly cohort?: string;\n  /** Evaluation reason */\n  readonly reason?: EvaluationReason;\n  /** Whether this is first exposure */\n  readonly isFirstExposure: boolean;\n  /** Timestamp */\n  readonly timestamp: Date;\n  /** Page/screen where exposure occurred */\n  readonly page?: string;\n  /** Additional properties */\n  readonly properties?: Record<string, JsonValue>;\n}\n\n/**\n * Stored exposure record.\n */\nexport interface ExposureRecord {\n  /** Flag key */\n  readonly flagKey: string;\n  /** Variant ID */\n  readonly variantId: VariantId;\n  /** First exposure timestamp */\n  readonly firstExposedAt: Date;\n  /** Last exposure timestamp */\n  readonly lastExposedAt: Date;\n  /** Total exposure count */\n  readonly exposureCount: number;\n  /** Experiment ID */\n  readonly experimentId?: string;\n  /** Cohort */\n  readonly cohort?: string;\n}\n\n/**\n * User exposure summary.\n */\nexport interface UserExposureSummary {\n  /** User ID */\n  readonly userId: UserId;\n  /** Total flags exposed to */\n  readonly flagCount: number;\n  /** Total exposure events */\n  readonly totalExposures: number;\n  /** Experiments participating in */\n  readonly experiments: string[];\n  /** Flag exposures */\n  readonly exposures: ExposureRecord[];\n  /** First activity */\n  readonly firstSeenAt: Date;\n  /** Last activity */\n  readonly lastSeenAt: Date;\n}\n\n/**\n * Exposure tracker configuration.\n */\nexport interface ExposureTrackerConfig {\n  /** Enable tracking */\n  readonly enabled?: boolean;\n  /** Deduplication window in ms (default: 24h) */\n  readonly deduplicationWindow?: number;\n  /** Storage key prefix */\n  readonly storagePrefix?: string;\n  /** Use persistent storage */\n  readonly persistent?: boolean;\n  /** Callback on exposure */\n  readonly onExposure?: (exposure: ExposureEvent) => void;\n  /** Batch exposures before callback */\n  readonly batchSize?: number;\n  /** Batch flush interval */\n  readonly flushInterval?: number;\n  /** Enable debug logging */\n  readonly debug?: boolean;\n  /** Maximum stored exposures per user */\n  readonly maxExposuresPerUser?: number;\n}\n\n/**\n * Input for tracking exposure.\n */\nexport interface TrackExposureInput {\n  /** Flag key */\n  readonly flagKey: string;\n  /** Variant ID */\n  readonly variantId: VariantId;\n  /** User ID */\n  readonly userId: UserId;\n  /** Session ID */\n  readonly sessionId?: string;\n  /** Experiment ID */\n  readonly experimentId?: string;\n  /** Cohort */\n  readonly cohort?: string;\n  /** Evaluation reason */\n  readonly reason?: EvaluationReason;\n  /** Current page/screen */\n  readonly page?: string;\n  /** Additional properties */\n  readonly properties?: Record<string, JsonValue>;\n}\n\n// ============================================================================\n// Exposure Tracker\n// ============================================================================\n\n/**\n * Tracks feature flag exposures for analytics and experiments.\n */\nexport class ExposureTracker {\n  private config: Required<ExposureTrackerConfig>;\n  private userExposures = new Map<UserId, Map<string, ExposureRecord>>();\n  private pendingExposures: ExposureEvent[] = [];\n  private flushTimer: ReturnType<typeof setInterval> | null = null;\n  private listeners = new Set<(exposure: ExposureEvent) => void>();\n\n  constructor(config: ExposureTrackerConfig = {}) {\n    this.config = {\n      enabled: config.enabled ?? true,\n      deduplicationWindow: config.deduplicationWindow ?? 24 * 60 * 60 * 1000,\n      storagePrefix: config.storagePrefix ?? 'flag_exposure_',\n      persistent: config.persistent ?? true,\n      onExposure: config.onExposure ?? (() => {}),\n      batchSize: config.batchSize ?? 10,\n      flushInterval: config.flushInterval ?? 5000,\n      debug: config.debug ?? false,\n      maxExposuresPerUser: config.maxExposuresPerUser ?? 100,\n    };\n\n    this.loadFromStorage();\n    this.startFlushTimer();\n  }\n\n  // ==========================================================================\n  // Exposure Tracking\n  // ==========================================================================\n\n  /**\n   * Track a flag exposure.\n   */\n  trackExposure(input: TrackExposureInput): ExposureEvent | null {\n    if (!this.config.enabled) {\n      return null;\n    }\n\n    const { flagKey, variantId, userId, experimentId, cohort } = input;\n    const exposureKey = this.getExposureKey(flagKey, experimentId);\n    const now = new Date();\n\n    // Get or create user exposures\n    if (!this.userExposures.has(userId)) {\n      this.userExposures.set(userId, new Map());\n    }\n    const userMap = this.userExposures.get(userId);\n\n    if (!userMap) {\n      return null;\n    }\n\n    // Check for existing exposure\n    const existing = userMap.get(exposureKey);\n    const isFirstExposure = !existing;\n    const isDuplicate = this.isDuplicateExposure(existing, now);\n\n    // Update or create exposure record\n    const record: ExposureRecord = existing\n      ? {\n          ...existing,\n          lastExposedAt: now,\n          exposureCount: existing.exposureCount + 1,\n        }\n      : {\n          flagKey,\n          variantId,\n          firstExposedAt: now,\n          lastExposedAt: now,\n          exposureCount: 1,\n          experimentId,\n          cohort,\n        };\n\n    userMap.set(exposureKey, record);\n    this.enforceMaxExposures(userId);\n\n    // Only emit event if not a duplicate within window\n    if (isDuplicate) {\n      this.log(`Duplicate exposure suppressed: ${flagKey} for ${userId}`);\n      return null;\n    }\n\n    const exposure: ExposureEvent = {\n      flagKey,\n      variantId,\n      userId,\n      sessionId: input.sessionId,\n      experimentId,\n      cohort,\n      reason: input.reason,\n      isFirstExposure,\n      timestamp: now,\n      page: input.page,\n      properties: input.properties,\n    };\n\n    this.pendingExposures.push(exposure);\n    this.notifyListeners(exposure);\n    this.saveToStorage();\n\n    // Flush if batch size reached\n    if (this.pendingExposures.length >= this.config.batchSize) {\n      void this.flush();\n    }\n\n    this.log(`Tracked exposure: ${flagKey}/${variantId} for ${userId}`);\n    return exposure;\n  }\n\n  /**\n   * Track multiple exposures at once.\n   */\n  trackExposures(inputs: TrackExposureInput[]): ExposureEvent[] {\n    const events: ExposureEvent[] = [];\n    for (const input of inputs) {\n      const event = this.trackExposure(input);\n      if (event) {\n        events.push(event);\n      }\n    }\n    return events;\n  }\n\n  /**\n   * Get exposure record for a user and flag.\n   */\n  getExposure(userId: UserId, flagKey: string, experimentId?: string): ExposureRecord | null {\n    const userMap = this.userExposures.get(userId);\n    if (!userMap) {\n      return null;\n    }\n\n    const key = this.getExposureKey(flagKey, experimentId);\n    return userMap.get(key) ?? null;\n  }\n\n  /**\n   * Get all exposures for a user.\n   */\n  getExposures(userId: UserId): ExposureRecord[] {\n    const userMap = this.userExposures.get(userId);\n    if (!userMap) {\n      return [];\n    }\n    return Array.from(userMap.values());\n  }\n\n  /**\n   * Get user exposure summary.\n   */\n  getUserSummary(userId: UserId): UserExposureSummary | null {\n    const userMap = this.userExposures.get(userId);\n    if (!userMap || userMap.size === 0) {\n      return null;\n    }\n\n    const exposures = Array.from(userMap.values());\n    const experiments = new Set<string>();\n    let totalExposures = 0;\n    let firstSeenAt = new Date();\n    let lastSeenAt = new Date(0);\n\n    for (const exposure of exposures) {\n      totalExposures += exposure.exposureCount;\n\n      if (exposure.experimentId != null && exposure.experimentId !== '') {\n        experiments.add(exposure.experimentId);\n      }\n\n      if (exposure.firstExposedAt < firstSeenAt) {\n        firstSeenAt = exposure.firstExposedAt;\n      }\n\n      if (exposure.lastExposedAt > lastSeenAt) {\n        lastSeenAt = exposure.lastExposedAt;\n      }\n    }\n\n    return {\n      userId,\n      flagCount: exposures.length,\n      totalExposures,\n      experiments: Array.from(experiments),\n      exposures,\n      firstSeenAt,\n      lastSeenAt,\n    };\n  }\n\n  // ==========================================================================\n  // Exposure Queries\n  // ==========================================================================\n\n  /**\n   * Check if user was exposed to a flag.\n   */\n  wasExposed(userId: UserId, flagKey: string, experimentId?: string): boolean {\n    return this.getExposure(userId, flagKey, experimentId) !== null;\n  }\n\n  /**\n   * Get the variant a user was exposed to.\n   */\n  getExposedVariant(userId: UserId, flagKey: string, experimentId?: string): VariantId | null {\n    const exposure = this.getExposure(userId, flagKey, experimentId);\n    return exposure?.variantId ?? null;\n  }\n\n  /**\n   * Get all users exposed to a flag.\n   */\n  getExposedUsers(flagKey: string, experimentId?: string): UserId[] {\n    const users: UserId[] = [];\n    const key = this.getExposureKey(flagKey, experimentId);\n\n    for (const [userId, userMap] of this.userExposures.entries()) {\n      if (userMap.has(key)) {\n        users.push(userId);\n      }\n    }\n\n    return users;\n  }\n\n  /**\n   * Get exposure counts by variant.\n   */\n  getVariantExposures(flagKey: string, experimentId?: string): Record<VariantId, number> {\n    const counts: Record<VariantId, number> = {};\n    const key = this.getExposureKey(flagKey, experimentId);\n\n    for (const userMap of this.userExposures.values()) {\n      const exposure = userMap.get(key);\n      if (exposure) {\n        counts[exposure.variantId] = (counts[exposure.variantId] ?? 0) + 1;\n      }\n    }\n\n    return counts;\n  }\n\n  /**\n   * Get all users in an experiment.\n   */\n  getExperimentUsers(experimentId: string): UserId[] {\n    const users: UserId[] = [];\n\n    for (const [userId, userMap] of this.userExposures.entries()) {\n      for (const exposure of userMap.values()) {\n        if (exposure.experimentId === experimentId) {\n          users.push(userId);\n          break;\n        }\n      }\n    }\n\n    return users;\n  }\n\n  /**\n   * Get experiment cohort assignments.\n   */\n  getExperimentCohorts(experimentId: string): Record<string, UserId[]> {\n    const cohorts: Record<string, UserId[]> = {};\n\n    for (const [userId, userMap] of this.userExposures.entries()) {\n      for (const exposure of userMap.values()) {\n        if (exposure.experimentId === experimentId && exposure.cohort != null && exposure.cohort !== '') {\n          if (!cohorts[exposure.cohort]) {\n            cohorts[exposure.cohort] = [];\n          }\n          cohorts[exposure.cohort]!.push(userId);\n        }\n      }\n    }\n\n    return cohorts;\n  }\n\n  /**\n   * Get variant distribution for an experiment.\n   */\n  getExperimentDistribution(experimentId: string): Record<VariantId, number> {\n    const distribution: Record<VariantId, number> = {};\n\n    for (const userMap of this.userExposures.values()) {\n      for (const exposure of userMap.values()) {\n        if (exposure.experimentId === experimentId) {\n          distribution[exposure.variantId] =\n            (distribution[exposure.variantId] ?? 0) + 1;\n        }\n      }\n    }\n\n    return distribution;\n  }\n\n  // ==========================================================================\n  // Experiment Support\n  // ==========================================================================\n\n  /**\n   * Subscribe to exposure events.\n   */\n  subscribe(listener: (exposure: ExposureEvent) => void): () => void {\n    this.listeners.add(listener);\n    return () => {\n      this.listeners.delete(listener);\n    };\n  }\n\n  /**\n   * Flush pending exposures.\n   */\n  async flush(): Promise<void> {\n    if (this.pendingExposures.length === 0) {\n      return;\n    }\n\n    const exposures = [...this.pendingExposures];\n    this.pendingExposures = [];\n\n    try {\n      for (const exposure of exposures) {\n        const result = this.config.onExposure(exposure) as unknown;\n        if (result && typeof result === 'object' && 'then' in result && typeof result.then === 'function') {\n          await (result as Promise<void>);\n        }\n      }\n      this.log(`Flushed ${exposures.length} exposures`);\n    } catch (error) {\n      // Put exposures back on failure\n      this.pendingExposures = [...exposures, ...this.pendingExposures];\n      this.log('Error flushing exposures:', error);\n    }\n  }\n\n  /**\n   * Clear all exposure data.\n   */\n  clear(): void {\n    this.userExposures.clear();\n    this.pendingExposures = [];\n\n    if (this.config.persistent && typeof localStorage !== 'undefined') {\n      localStorage.removeItem(`${this.config.storagePrefix}data`);\n    }\n\n    this.log('Exposure data cleared');\n  }\n\n  // ==========================================================================\n  // Subscriptions\n  // ==========================================================================\n\n  /**\n   * Clear exposures for a specific user.\n   */\n  clearUser(userId: UserId): void {\n    this.userExposures.delete(userId);\n    this.saveToStorage();\n    this.log(`Cleared exposures for user: ${userId}`);\n  }\n\n  /**\n   * Shutdown the tracker.\n   */\n  async shutdown(): Promise<void> {\n    this.stopFlushTimer();\n    await this.flush();\n    this.saveToStorage();\n    this.listeners.clear();\n    this.log('Exposure tracker shutdown');\n  }\n\n  // ==========================================================================\n  // Flushing\n  // ==========================================================================\n\n  /**\n   * Enable or disable tracking.\n   */\n  setEnabled(enabled: boolean): void {\n    (this.config as { enabled: boolean }).enabled = enabled;\n    if (enabled) {\n      this.startFlushTimer();\n    } else {\n      this.stopFlushTimer();\n    }\n  }\n\n  private isDuplicateExposure(\n    existing: ExposureRecord | undefined,\n    now: Date\n  ): boolean {\n    if (!existing) {\n      return false;\n    }\n\n    const timeSinceLastExposure =\n      now.getTime() - existing.lastExposedAt.getTime();\n    return timeSinceLastExposure < this.config.deduplicationWindow;\n  }\n\n  private getExposureKey(flagKey: string, experimentId?: string): string {\n    return experimentId != null && experimentId !== '' ? `${flagKey}:${experimentId}` : flagKey;\n  }\n\n  // ==========================================================================\n  // Storage\n  // ==========================================================================\n\n  private enforceMaxExposures(userId: UserId): void {\n    const userMap = this.userExposures.get(userId);\n    if (!userMap) {\n      return;\n    }\n\n    if (userMap.size > this.config.maxExposuresPerUser) {\n      // Remove oldest exposures\n      const entries = Array.from(userMap.entries()).sort(\n        (a, b) => a[1].lastExposedAt.getTime() - b[1].lastExposedAt.getTime()\n      );\n\n      const toRemove = entries.slice(\n        0,\n        userMap.size - this.config.maxExposuresPerUser\n      );\n      for (const [key] of toRemove) {\n        userMap.delete(key);\n      }\n    }\n  }\n\n  private notifyListeners(exposure: ExposureEvent): void {\n    for (const listener of this.listeners) {\n      try {\n        listener(exposure);\n      } catch (error) {\n        this.log('Error in exposure listener:', error);\n      }\n    }\n  }\n\n  // ==========================================================================\n  // Lifecycle\n  // ==========================================================================\n\n  private startFlushTimer(): void {\n    if (this.flushTimer) {\n      return;\n    }\n\n    this.flushTimer = setInterval(() => {\n      void this.flush();\n    }, this.config.flushInterval);\n  }\n\n  private stopFlushTimer(): void {\n    if (this.flushTimer) {\n      clearInterval(this.flushTimer);\n      this.flushTimer = null;\n    }\n  }\n\n  private loadFromStorage(): void {\n    if (!this.config.persistent || typeof localStorage === 'undefined') {\n      return;\n    }\n\n    try {\n      const stored = localStorage.getItem(`${this.config.storagePrefix}data`);\n      if (stored != null && stored !== '') {\n        const data = JSON.parse(stored) as Record<string, unknown>;\n\n        for (const [userId, exposures] of Object.entries(data)) {\n          const userMap = new Map<string, ExposureRecord>();\n\n          for (const [key, record] of Object.entries(exposures as Record<string, unknown>)) {\n            const typedRecord = record as ExposureRecord;\n            userMap.set(key, {\n              ...typedRecord,\n              firstExposedAt: new Date(typedRecord.firstExposedAt),\n              lastExposedAt: new Date(typedRecord.lastExposedAt),\n            });\n          }\n\n          this.userExposures.set(userId, userMap);\n        }\n\n        this.log(`Loaded ${this.userExposures.size} users from storage`);\n      }\n    } catch (error) {\n      this.log('Error loading from storage:', error);\n    }\n  }\n\n  private saveToStorage(): void {\n    if (!this.config.persistent || typeof localStorage === 'undefined') {\n      return;\n    }\n\n    try {\n      const data: Record<string, Record<string, ExposureRecord>> = {};\n\n      for (const [userId, userMap] of this.userExposures.entries()) {\n        data[userId] = Object.fromEntries(userMap.entries());\n      }\n\n      localStorage.setItem(\n        `${this.config.storagePrefix}data`,\n        JSON.stringify(data)\n      );\n    } catch (error) {\n      this.log('Error saving to storage:', error);\n    }\n  }\n\n  // ==========================================================================\n  // Utilities\n  // ==========================================================================\n\n  private log(message: string, ...args: unknown[]): void {\n    if (this.config.debug) {\n      // eslint-disable-next-line no-console\n      console.log(`[ExposureTracker] ${message}`, ...args);\n    }\n  }\n}\n\n// ============================================================================\n// Singleton Instance\n// ============================================================================\n\nlet instance: ExposureTracker | null = null;\n\n/**\n * Get the singleton exposure tracker instance.\n */\nexport function getExposureTracker(): ExposureTracker {\n  instance ??= new ExposureTracker();\n  return instance;\n}\n\n/**\n * Initialize the singleton with configuration.\n */\nexport function initExposureTracker(config: ExposureTrackerConfig): ExposureTracker {\n  instance = new ExposureTracker(config);\n  return instance;\n}\n\n/**\n * Reset the singleton instance.\n */\nexport function resetExposureTracker(): void {\n  if (instance) {\n    void instance.shutdown();\n  }\n  instance = null;\n}\n\n// ============================================================================\n// React Hook Support\n// ============================================================================\n\n/**\n * Create exposure tracking context for React.\n */\nexport function createExposureContext(config?: ExposureTrackerConfig): {\n  tracker: ExposureTracker;\n  trackExposure: (input: TrackExposureInput) => ExposureEvent | null;\n  getExposure: (userId: UserId, flagKey: string, experimentId?: string) => ExposureRecord | null;\n  wasExposed: (userId: UserId, flagKey: string, experimentId?: string) => boolean;\n  subscribe: (callback: (exposure: ExposureEvent) => void) => () => void;\n} {\n  const tracker = new ExposureTracker(config);\n\n  return {\n    tracker,\n    trackExposure: tracker.trackExposure.bind(tracker),\n    getExposure: tracker.getExposure.bind(tracker),\n    wasExposed: tracker.wasExposed.bind(tracker),\n    subscribe: tracker.subscribe.bind(tracker),\n  };\n}\n"],"names":["ExposureTracker","config","input","flagKey","variantId","userId","experimentId","cohort","exposureKey","now","userMap","existing","isFirstExposure","isDuplicate","record","exposure","inputs","events","event","key","exposures","experiments","totalExposures","firstSeenAt","lastSeenAt","users","counts","cohorts","distribution","listener","result","error","enabled","toRemove","a","b","stored","data","typedRecord","message","args","instance","getExposureTracker","initExposureTracker","resetExposureTracker","createExposureContext","tracker"],"mappings":"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"}