{"version":3,"file":"percentage-rollout.mjs","sources":["../../../../src/lib/flags/advanced/percentage-rollout.ts"],"sourcesContent":["/**\n * @fileoverview Percentage-based rollout engine for gradual feature releases.\n *\n * Supports multiple rollout strategies:\n * - Simple percentage rollouts\n * - Scheduled/staged rollouts\n * - Ring-based rollouts (internal -> beta -> GA)\n * - Canary releases\n * - A/B test traffic allocation\n *\n * Uses consistent hashing to ensure users always get the same experience.\n *\n * @module flags/advanced/percentage-rollout\n *\n * @example\n * ```typescript\n * const engine = new PercentageRolloutEngine();\n *\n * const rollout: PercentageRollout = {\n *   strategy: 'percentage',\n *   percentage: 25,\n *   sticky: true,\n * };\n *\n * const result = engine.evaluate(rollout, context, 'feature-key', variants);\n * if (result.included) {\n *   console.log('User is in the rollout');\n * }\n * ```\n */\n\nimport type {\n  RolloutConfig,\n  PercentageRollout,\n  ScheduledRollout,\n  RingRollout,\n  CanaryRollout,\n  ExperimentRollout,\n  EvaluationContext,\n  Variant,\n  VariantId,\n  SegmentId,\n  RolloutStage,\n} from './types';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Result of rollout evaluation.\n */\nexport interface RolloutResult {\n  /** Whether the user is included in the rollout */\n  readonly included: boolean;\n  /** The variant to serve */\n  readonly variantId?: VariantId;\n  /** The percentage bucket the user falls into */\n  readonly bucket?: number;\n  /** Current rollout percentage */\n  readonly percentage?: number;\n  /** Current stage (for staged rollouts) */\n  readonly stage?: string;\n  /** Current ring (for ring rollouts) */\n  readonly ring?: string;\n  /** Whether user is in canary group */\n  readonly isCanary?: boolean;\n  /** Experiment details */\n  readonly experiment?: {\n    readonly experimentId: string;\n    readonly allocationPercentage: number;\n  };\n}\n\n/**\n * Hash function type for consistent user bucketing.\n */\nexport type HashFunction = (key: string, salt?: string) => number;\n\n// ============================================================================\n// Default Hash Function\n// ============================================================================\n\n/**\n * Default hash function using a simple but effective algorithm.\n * Returns a value between 0 and 100.\n */\nfunction defaultHashFunction(key: string, salt: string = ''): number {\n  const input = key + salt;\n  let hash = 0;\n\n  for (let i = 0; i < input.length; i++) {\n    const char = input.charCodeAt(i);\n    hash = ((hash << 5) - hash + char) | 0;\n  }\n\n  // Convert to positive number and normalize to 0-100\n  return Math.abs(hash % 10000) / 100;\n}\n\n// ============================================================================\n// Percentage Rollout Engine\n// ============================================================================\n\n/**\n * Engine for evaluating percentage-based rollouts.\n */\nexport class PercentageRolloutEngine {\n  private readonly hashFunction: HashFunction;\n  private stickyBuckets = new Map<string, number>();\n\n  constructor(hashFunction?: HashFunction) {\n    this.hashFunction = hashFunction ?? defaultHashFunction;\n  }\n\n  /**\n   * Evaluate a rollout configuration.\n   */\n  evaluate(\n    rollout: RolloutConfig,\n    context: EvaluationContext,\n    flagKey: string,\n    variants: readonly Variant[]\n  ): RolloutResult {\n    switch (rollout.strategy) {\n      case 'percentage':\n        return this.evaluatePercentage(rollout, context, flagKey, variants);\n      case 'scheduled':\n        return this.evaluateScheduled(rollout, context, flagKey, variants);\n      case 'ring':\n        return this.evaluateRing(rollout, context, flagKey, variants);\n      case 'canary':\n        return this.evaluateCanary(rollout, context, flagKey, variants);\n      case 'experiment':\n        return this.evaluateExperiment(rollout, context, flagKey, variants);\n      default:\n        return { included: false };\n    }\n  }\n\n  // ==========================================================================\n  // Percentage Rollout\n  // ==========================================================================\n\n  /**\n   * Set a sticky bucket for a user/flag combination.\n   */\n  setStickyBucket(flagKey: string, hashKey: string, bucket: number): void {\n    this.stickyBuckets.set(`${flagKey}:${hashKey}`, bucket);\n  }\n\n  // ==========================================================================\n  // Scheduled Rollout\n  // ==========================================================================\n\n  /**\n   * Clear sticky buckets.\n   */\n  clearStickyBuckets(): void {\n    this.stickyBuckets.clear();\n  }\n\n  /**\n   * Get the bucket for a user/flag combination.\n   */\n  getBucket(flagKey: string, hashKey: string, salt?: string): number {\n    return this.hashFunction(`${flagKey}:${hashKey}`, salt ?? 'default');\n  }\n\n  // ==========================================================================\n  // Ring Rollout\n  // ==========================================================================\n\n  private evaluatePercentage(\n    rollout: PercentageRollout,\n    context: EvaluationContext,\n    flagKey: string,\n    variants: readonly Variant[]\n  ): RolloutResult {\n    const hashKey = rollout.hashKey ?? this.getDefaultHashKey(context);\n    if (hashKey == null || hashKey === '') {\n      return { included: false, percentage: rollout.percentage };\n    }\n\n    let bucket: number;\n\n    // Check for sticky bucket\n    if (rollout.sticky === true) {\n      const stickyKey = `${flagKey}:${hashKey}`;\n      const existingBucket = this.stickyBuckets.get(stickyKey);\n      if (existingBucket != null) {\n        bucket = existingBucket;\n      } else {\n        bucket = this.hashFunction(\n          `${flagKey}:${hashKey}`,\n          rollout.salt ?? 'default'\n        );\n        this.stickyBuckets.set(stickyKey, bucket);\n      }\n    } else {\n      bucket = this.hashFunction(\n        `${flagKey}:${hashKey}`,\n        rollout.salt ?? 'default'\n      );\n    }\n\n    const included = bucket < rollout.percentage;\n\n    return {\n      included,\n      variantId: included ? this.selectVariant(bucket, variants) : undefined,\n      bucket,\n      percentage: rollout.percentage,\n    };\n  }\n\n  private evaluateScheduled(\n    rollout: ScheduledRollout,\n    context: EvaluationContext,\n    flagKey: string,\n    variants: readonly Variant[]\n  ): RolloutResult {\n    const now = context.timestamp ?? new Date();\n    const currentStage = this.getCurrentStage(rollout.stages, now);\n\n    if (!currentStage) {\n      return { included: false };\n    }\n\n    const hashKey = this.getDefaultHashKey(context);\n    if (hashKey == null || hashKey === '') {\n      return { included: false, stage: currentStage.name };\n    }\n\n    const bucket = this.hashFunction(`${flagKey}:${hashKey}`, 'scheduled');\n    const included = bucket < currentStage.percentage;\n\n    return {\n      included,\n      variantId: included ? this.selectVariant(bucket, variants) : undefined,\n      bucket,\n      percentage: currentStage.percentage,\n      stage: currentStage.name,\n    };\n  }\n\n  private getCurrentStage(\n    stages: readonly RolloutStage[],\n    now: Date\n  ): RolloutStage | null {\n    let currentStage: RolloutStage | null = null;\n\n    for (const stage of stages) {\n      if (stage.startTime <= now) {\n        currentStage = stage;\n      } else {\n        break;\n      }\n    }\n\n    return currentStage;\n  }\n\n  // ==========================================================================\n  // Canary Rollout\n  // ==========================================================================\n\n  private evaluateRing(\n    rollout: RingRollout,\n    context: EvaluationContext,\n    flagKey: string,\n    variants: readonly Variant[]\n  ): RolloutResult {\n    const userId = context.user?.id;\n    if (userId == null || userId === '') {\n      return { included: false };\n    }\n\n    // Find which ring the user belongs to\n    for (const ring of rollout.rings) {\n      if (ring.priority > this.getRingPriority(rollout.currentRing, rollout.rings)) {\n        continue; // Skip rings that haven't been reached yet\n      }\n\n      // Check if user is in any of the ring's segments\n      // Note: Segment matching is done externally, here we just check inclusion\n      for (const _ of ring.segments) {\n        // In a real implementation, this would check segment membership\n        // For now, we'll use a hash-based approach\n\n        // Ring-based bucketing\n        if (this.isInRing(ring.id, rollout.currentRing, rollout.rings)) {\n          const bucket = this.hashFunction(`${flagKey}:${userId}`, 'ring');\n          return {\n            included: true,\n            variantId: this.selectVariant(bucket, variants),\n            bucket,\n            ring: ring.name,\n          };\n        }\n      }\n    }\n\n    return { included: false, ring: rollout.currentRing };\n  }\n\n  // ==========================================================================\n  // Experiment Rollout\n  // ==========================================================================\n\n  private getRingPriority(\n    ringId: string,\n    rings: readonly { id: string; priority: number }[]\n  ): number {\n    const ring = rings.find((r) => r.id === ringId);\n    return ring?.priority ?? Infinity;\n  }\n\n  // ==========================================================================\n  // Helpers\n  // ==========================================================================\n\n  private isInRing(\n    ringId: string,\n    currentRing: string,\n    rings: readonly { id: string; priority: number }[]\n  ): boolean {\n    const ringPriority = this.getRingPriority(ringId, rings);\n    const currentPriority = this.getRingPriority(currentRing, rings);\n    return ringPriority <= currentPriority;\n  }\n\n  private evaluateCanary(\n    rollout: CanaryRollout,\n    context: EvaluationContext,\n    flagKey: string,\n    variants: readonly Variant[]\n  ): RolloutResult {\n    const hashKey = this.getDefaultHashKey(context);\n    if (hashKey == null || hashKey === '') {\n      return { included: false };\n    }\n\n    // Check if user is in canary segment\n    if (rollout.canarySegment != null && rollout.canarySegment !== '') {\n      // Segment check would be done externally\n      // Here we use hash-based bucketing\n    }\n\n    const bucket = this.hashFunction(`${flagKey}:${hashKey}`, 'canary');\n    const isCanary = bucket < rollout.canaryPercentage;\n\n    return {\n      included: isCanary,\n      variantId: isCanary ? this.selectVariant(bucket, variants) : undefined,\n      bucket,\n      percentage: rollout.canaryPercentage,\n      isCanary,\n    };\n  }\n\n  private evaluateExperiment(\n    rollout: ExperimentRollout,\n    context: EvaluationContext,\n    flagKey: string,\n    _variants: readonly Variant[]\n  ): RolloutResult {\n    // Check if experiment has ended\n    if (rollout.endDate && new Date() > rollout.endDate) {\n      return { included: false };\n    }\n\n    const hashKey = this.getDefaultHashKey(context);\n    if (hashKey == null || hashKey === '') {\n      return { included: false };\n    }\n\n    const bucket = this.hashFunction(`${flagKey}:${hashKey}`, rollout.experimentId);\n\n    // Allocate to variants based on percentage allocation\n    let cumulativePercentage = 0;\n    for (const allocation of rollout.allocation) {\n      cumulativePercentage += allocation.percentage;\n      if (bucket < cumulativePercentage) {\n        // const variant = variants.find((v) => v.id === allocation.variantId);\n        return {\n          included: true,\n          variantId: allocation.variantId,\n          bucket,\n          percentage: allocation.percentage,\n          experiment: {\n            experimentId: rollout.experimentId,\n            allocationPercentage: allocation.percentage,\n          },\n        };\n      }\n    }\n\n    // User not allocated to any variant\n    return {\n      included: false,\n      bucket,\n      experiment: {\n        experimentId: rollout.experimentId,\n        allocationPercentage: 0,\n      },\n    };\n  }\n\n  private getDefaultHashKey(context: EvaluationContext): string | null {\n    return (\n      context.user?.id ??\n      context.session?.sessionId ??\n      null\n    );\n  }\n\n  private selectVariant(\n    _bucket: number,\n    variants: readonly Variant[]\n  ): VariantId {\n    // Find the first non-control variant, or use the first variant\n    const nonControl = variants.find((v) => v.isControl !== true);\n    return nonControl?.id ?? variants[0]?.id ?? 'default';\n  }\n}\n\n// ============================================================================\n// Rollout Builder\n// ============================================================================\n\n/**\n * Builder for creating rollout configurations.\n */\nexport class RolloutBuilder {\n  /**\n   * Create a simple percentage rollout.\n   */\n  static percentage(\n    percentage: number,\n    options?: Partial<Omit<PercentageRollout, 'strategy' | 'percentage'>>\n  ): PercentageRollout {\n    return {\n      strategy: 'percentage',\n      percentage: Math.max(0, Math.min(100, percentage)),\n      sticky: options?.sticky ?? true,\n      hashKey: options?.hashKey,\n      salt: options?.salt,\n    };\n  }\n\n  /**\n   * Create a scheduled rollout.\n   */\n  static scheduled(\n    stages: RolloutStage[],\n    options?: { autoAdvance?: boolean }\n  ): ScheduledRollout {\n    return {\n      strategy: 'scheduled',\n      stages,\n      currentStage: 0,\n      autoAdvance: options?.autoAdvance ?? false,\n    };\n  }\n\n  /**\n   * Create rollout stages for scheduled rollout.\n   */\n  static stages(): RolloutStageBuilder {\n    return new RolloutStageBuilder();\n  }\n\n  /**\n   * Create a ring-based rollout.\n   */\n  static ring(\n    rings: Array<{\n      id: string;\n      name: string;\n      segments: SegmentId[];\n      priority: number;\n    }>,\n    currentRing: string\n  ): RingRollout {\n    return {\n      strategy: 'ring',\n      rings,\n      currentRing,\n    };\n  }\n\n  /**\n   * Create a canary rollout.\n   */\n  static canary(\n    canaryPercentage: number,\n    options?: {\n      canarySegment?: SegmentId;\n      autoRollback?: boolean;\n    }\n  ): CanaryRollout {\n    return {\n      strategy: 'canary',\n      canaryPercentage,\n      canarySegment: options?.canarySegment,\n      criteria: {\n        maxErrorRate: 0.01, // 1% error rate threshold\n        minSampleSize: 100,\n      },\n      autoRollback: options?.autoRollback ?? true,\n    };\n  }\n\n  /**\n   * Create an experiment rollout.\n   */\n  static experiment(\n    experimentId: string,\n    allocation: Array<{ variantId: VariantId; percentage: number }>,\n    options?: {\n      primaryMetric: string;\n      endDate?: Date;\n    }\n  ): ExperimentRollout {\n    return {\n      strategy: 'experiment',\n      experimentId,\n      allocation,\n      primaryMetric: options?.primaryMetric ?? 'conversion',\n      endDate: options?.endDate,\n    };\n  }\n}\n\n/**\n * Builder for rollout stages.\n */\nclass RolloutStageBuilder {\n  private stages: RolloutStage[] = [];\n\n  /**\n   * Add a stage.\n   */\n  stage(\n    name: string,\n    percentage: number,\n    startTime: Date,\n    options?: { minDuration?: number }\n  ): this {\n    this.stages.push({\n      name,\n      percentage,\n      startTime,\n      minDuration: options?.minDuration,\n    });\n    return this;\n  }\n\n  /**\n   * Add an internal stage (e.g., employees only).\n   */\n  internal(startTime: Date): this {\n    return this.stage('internal', 0, startTime);\n  }\n\n  /**\n   * Add a beta stage.\n   */\n  beta(percentage: number, startTime: Date): this {\n    return this.stage('beta', percentage, startTime);\n  }\n\n  /**\n   * Add a GA (general availability) stage.\n   */\n  ga(startTime: Date): this {\n    return this.stage('ga', 100, startTime);\n  }\n\n  /**\n   * Build the stages.\n   */\n  build(): RolloutStage[] {\n    return [...this.stages].sort(\n      (a, b) => a.startTime.getTime() - b.startTime.getTime()\n    );\n  }\n}\n\n// ============================================================================\n// Utility Functions\n// ============================================================================\n\n/**\n * Calculate what percentage of users would be affected by a rollout change.\n */\nexport function calculateRolloutImpact(\n  currentPercentage: number,\n  newPercentage: number\n): {\n  added: number;\n  removed: number;\n  unchanged: number;\n} {\n  if (newPercentage > currentPercentage) {\n    return {\n      added: newPercentage - currentPercentage,\n      removed: 0,\n      unchanged: currentPercentage,\n    };\n  } else {\n    return {\n      added: 0,\n      removed: currentPercentage - newPercentage,\n      unchanged: newPercentage,\n    };\n  }\n}\n\n/**\n * Generate a suggested rollout schedule.\n */\nexport function generateRolloutSchedule(\n  startDate: Date,\n  targetPercentage: number = 100,\n  durationDays: number = 7\n): RolloutStage[] {\n  const stages: RolloutStage[] = [];\n  const percentages = [1, 5, 10, 25, 50, 75, targetPercentage];\n  const msPerDay = 24 * 60 * 60 * 1000;\n\n  let currentDate = new Date(startDate);\n  const interval = durationDays / percentages.length;\n\n  for (const percentage of percentages) {\n    if (percentage > targetPercentage) break;\n\n    stages.push({\n      name: `${percentage}%`,\n      percentage,\n      startTime: new Date(currentDate),\n      minDuration: interval * msPerDay,\n    });\n\n    currentDate = new Date(currentDate.getTime() + interval * msPerDay);\n  }\n\n  return stages;\n}\n"],"names":["defaultHashFunction","key","salt","input","hash","i","char","PercentageRolloutEngine","hashFunction","rollout","context","flagKey","variants","hashKey","bucket","stickyKey","existingBucket","included","now","currentStage","stages","stage","userId","ring","_","ringId","rings","r","currentRing","ringPriority","currentPriority","isCanary","_variants","cumulativePercentage","allocation","_bucket","v","RolloutBuilder","percentage","options","RolloutStageBuilder","canaryPercentage","experimentId","name","startTime","a","b","calculateRolloutImpact","currentPercentage","newPercentage","generateRolloutSchedule","startDate","targetPercentage","durationDays","percentages","msPerDay","currentDate","interval"],"mappings":"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"}