{"version":3,"sources":["../../src/repository/index.ts","../../src/repository/types.ts","../../src/repository/utils.ts","../../src/config/index.ts","../../src/config/provider.ts","../../src/repository/memory/index.ts"],"sourcesContent":["/**\r\n * Credits repository - barrel exports\r\n *\r\n * Provides database-agnostic interface for credits storage\r\n */\r\n\r\n// Types and interfaces\r\nexport type {\r\n  ICreditRepository,\r\n  CreditRepositoryFactory,\r\n  CreateReservationInput,\r\n  CreateTransactionInput,\r\n  CreateUsageLogInput,\r\n  CreateJournalEntryInput,\r\n  UsageLogQuery,\r\n  JournalEntryQuery,\r\n  CreditBalanceUpdate,\r\n  TierUpdateInput,\r\n} from \"./types.js\";\r\n\r\nexport { toClientUserCredits } from \"./types.js\";\r\n\r\n// Shared utilities\r\nexport { generateId, toDate, getNextMonthlyReset } from \"./utils.js\";\r\n\r\n// In-memory implementation (for testing and prototyping)\r\nexport {\r\n  InMemoryCreditRepository,\r\n  createInMemoryCreditRepository,\r\n} from \"./memory/index.js\";\r\n","import type {\r\n  PortableUserCredits,\r\n  PortableReservation,\r\n  PortableTransaction,\r\n  PortableUsageLog,\r\n  PortableJournalEntry,\r\n  CreditOperationType,\r\n  SubscriptionTier,\r\n  ReservationStatus,\r\n  AIProviderType,\r\n  MonthlyResetResult,\r\n  SubscriptionExpiryResult,\r\n  CreditSource,\r\n  JournalReferenceType,\r\n} from \"../core/types.js\";\r\n\r\n/**\r\n * Input for creating a credit reservation\r\n */\r\nexport interface CreateReservationInput {\r\n  userId: string;\r\n  amount: number;\r\n  operationType: CreditOperationType;\r\n  expiresAt: Date;\r\n}\r\n\r\n/**\r\n * Input for creating a credit transaction\r\n */\r\nexport interface CreateTransactionInput {\r\n  userId: string;\r\n  type: PortableTransaction[\"type\"];\r\n  amount: number;\r\n  description: string;\r\n  paymentRef?: string;\r\n  previousBalance: number;\r\n  newBalance: number;\r\n}\r\n\r\n/**\r\n * Input for logging usage\r\n */\r\nexport interface CreateUsageLogInput {\r\n  userId: string;\r\n  operationType: CreditOperationType;\r\n  provider: AIProviderType;\r\n  creditsUsed: number;\r\n  success: boolean;\r\n  errorMessage?: string;\r\n  resourceId?: string;\r\n  resourceType?: string;\r\n  requestId?: string;\r\n  metadata?: Record<string, unknown>;\r\n}\r\n\r\n/**\r\n * Query options for usage logs\r\n */\r\nexport interface UsageLogQuery {\r\n  userId?: string;\r\n  operationType?: CreditOperationType;\r\n  success?: boolean;\r\n  limit?: number;\r\n  offset?: number;\r\n  startDate?: Date;\r\n  endDate?: Date;\r\n}\r\n\r\n/**\r\n * Input for creating a journal entry\r\n */\r\nexport interface CreateJournalEntryInput {\r\n  userId: string;\r\n  entryType: \"debit\" | \"credit\";\r\n  amount: number;\r\n  balanceAfter: number;\r\n  source: CreditSource;\r\n  referenceId: string;\r\n  referenceType: JournalReferenceType;\r\n  description: string;\r\n  metadata?: Record<string, unknown>;\r\n}\r\n\r\n/**\r\n * Query options for journal entries\r\n */\r\nexport interface JournalEntryQuery {\r\n  userId: string;\r\n  source?: CreditSource;\r\n  referenceType?: JournalReferenceType;\r\n  limit?: number;\r\n  offset?: number;\r\n  startDate?: Date;\r\n  endDate?: Date;\r\n}\r\n\r\n/**\r\n * Partial update for user credits balance\r\n */\r\nexport interface CreditBalanceUpdate {\r\n  balance?: number;\r\n  bonusCredits?: number;\r\n  reserved?: number;\r\n  tier?: SubscriptionTier;\r\n  monthlyLimit?: number;\r\n  monthlyUsed?: number;\r\n  monthlyResetAt?: Date | string;\r\n  subscriptionExpiresAt?: Date | string | null;\r\n  /** Increment balance by this amount (alternative to absolute value) */\r\n  balanceIncrement?: number;\r\n  /** Increment bonusCredits by this amount (alternative to absolute value) */\r\n  bonusCreditsIncrement?: number;\r\n  /** Increment reserved by this amount (alternative to absolute value) */\r\n  reservedIncrement?: number;\r\n  /** Increment monthlyUsed by this amount */\r\n  monthlyUsedIncrement?: number;\r\n}\r\n\r\n/**\r\n * Input for tier update\r\n */\r\nexport interface TierUpdateInput {\r\n  tier: SubscriptionTier;\r\n  monthlyLimit: number;\r\n  balance?: number;\r\n  monthlyUsed?: number;\r\n  subscriptionExpiresAt?: Date | string | null;\r\n}\r\n\r\n/**\r\n * Repository interface for credits database operations\r\n *\r\n * Implementations can use any database (Firestore, PostgreSQL, etc.)\r\n * All methods should handle their own error handling and transactions\r\n */\r\nexport interface ICreditRepository {\r\n  // ==================== User Credits ====================\r\n\r\n  /**\r\n   * Get user credits balance\r\n   * @param userId - User ID\r\n   * @returns User credits or null if not found\r\n   */\r\n  getUserCredits(userId: string): Promise<PortableUserCredits | null>;\r\n\r\n  /**\r\n   * Initialize credits for a new user\r\n   * @param userId - User ID\r\n   * @param tier - Initial subscription tier\r\n   * @param initialBalance - Initial credit balance\r\n   * @returns Initialized user credits\r\n   */\r\n  initializeUserCredits(\r\n    userId: string,\r\n    tier: SubscriptionTier,\r\n    initialBalance: number\r\n  ): Promise<PortableUserCredits>;\r\n\r\n  /**\r\n   * Update user credits balance\r\n   * @param userId - User ID\r\n   * @param updates - Partial updates to apply\r\n   */\r\n  updateUserCredits(userId: string, updates: CreditBalanceUpdate): Promise<void>;\r\n\r\n  /**\r\n   * Update user subscription tier\r\n   * @param userId - User ID\r\n   * @param input - Tier update data\r\n   */\r\n  updateUserTier(userId: string, input: TierUpdateInput): Promise<void>;\r\n\r\n  // ==================== Reservations ====================\r\n\r\n  /**\r\n   * Create a credit reservation (phase 1 of two-phase commit)\r\n   * @param input - Reservation data\r\n   * @returns Created reservation\r\n   */\r\n  createReservation(input: CreateReservationInput): Promise<PortableReservation>;\r\n\r\n  /**\r\n   * Get a reservation by ID\r\n   * @param userId - User ID\r\n   * @param reservationId - Reservation ID\r\n   * @returns Reservation or null\r\n   */\r\n  getReservation(userId: string, reservationId: string): Promise<PortableReservation | null>;\r\n\r\n  /**\r\n   * Update reservation status\r\n   * @param userId - User ID\r\n   * @param reservationId - Reservation ID\r\n   * @param status - New status\r\n   * @param completedAt - Completion timestamp\r\n   */\r\n  updateReservationStatus(\r\n    userId: string,\r\n    reservationId: string,\r\n    status: ReservationStatus,\r\n    completedAt?: Date\r\n  ): Promise<void>;\r\n\r\n  // ==================== Atomic Operations ====================\r\n\r\n  /**\r\n   * Reserve credits atomically (creates reservation + updates balance in transaction)\r\n   * @param userId - User ID\r\n   * @param amount - Credits to reserve\r\n   * @param operationType - Operation type for tracking\r\n   * @param expiresAt - Reservation expiry time\r\n   * @returns Created reservation\r\n   * @throws Error if insufficient credits\r\n   */\r\n  reserveCreditsAtomic(\r\n    userId: string,\r\n    amount: number,\r\n    operationType: CreditOperationType,\r\n    expiresAt: Date\r\n  ): Promise<PortableReservation>;\r\n\r\n  /**\r\n   * Commit a reservation atomically (deducts credits + marks reservation committed)\r\n   * @param userId - User ID\r\n   * @param reservationId - Reservation ID\r\n   * @throws Error if reservation not found or not in reserved state\r\n   */\r\n  commitReservationAtomic(userId: string, reservationId: string): Promise<void>;\r\n\r\n  /**\r\n   * Release a reservation atomically (releases reserved credits + marks reservation released)\r\n   * @param userId - User ID\r\n   * @param reservationId - Reservation ID\r\n   */\r\n  releaseReservationAtomic(userId: string, reservationId: string): Promise<void>;\r\n\r\n  /**\r\n   * Add credits atomically (creates transaction + updates balance)\r\n   * @param userId - User ID\r\n   * @param amount - Credits to add\r\n   * @param description - Transaction description\r\n   * @param paymentRef - Optional payment reference\r\n   */\r\n  addCreditsAtomic(\r\n    userId: string,\r\n    amount: number,\r\n    description: string,\r\n    paymentRef?: string\r\n  ): Promise<void>;\r\n\r\n  /**\r\n   * Deduct credits from a user's balance atomically.\r\n   *\r\n   * Splits the deduction across `balance` and `bonusCredits`, draining\r\n   * `balance` (monthly, resets each cycle) first so persistent bonus\r\n   * credits survive longer. Runs in a single atomic transaction so\r\n   * concurrent deducts cannot drive either field negative.\r\n   *\r\n   * Callers are responsible for writing any audit record (journal /\r\n   * transaction) — this method only moves credits.\r\n   *\r\n   * @param userId - User ID\r\n   * @param amount - Credits to deduct (positive)\r\n   * @returns Combined totals (balance + bonusCredits) before and after.\r\n   * @throws If user has no credits doc or insufficient credits\r\n   */\r\n  deductCreditsAtomic(\r\n    userId: string,\r\n    amount: number\r\n  ): Promise<{ previousBalance: number; newBalance: number }>;\r\n\r\n  // ==================== Transactions ====================\r\n\r\n  /**\r\n   * Create a credit transaction record\r\n   * @param input - Transaction data\r\n   * @returns Created transaction\r\n   */\r\n  createTransaction(input: CreateTransactionInput): Promise<PortableTransaction>;\r\n\r\n  /**\r\n   * Get user's transaction history\r\n   * @param userId - User ID\r\n   * @param limit - Max results\r\n   * @param offset - Skip results\r\n   * @returns List of transactions\r\n   */\r\n  getTransactions(\r\n    userId: string,\r\n    limit?: number,\r\n    offset?: number\r\n  ): Promise<PortableTransaction[]>;\r\n\r\n  // ==================== Usage Logs ====================\r\n\r\n  /**\r\n   * Log a usage event\r\n   * @param input - Usage log data\r\n   * @returns Created usage log\r\n   */\r\n  logUsage(input: CreateUsageLogInput): Promise<PortableUsageLog>;\r\n\r\n  /**\r\n   * Query usage logs\r\n   * @param query - Query parameters\r\n   * @returns List of usage logs\r\n   */\r\n  getUsageLogs(query: UsageLogQuery): Promise<PortableUsageLog[]>;\r\n\r\n  /**\r\n   * Get usage log count (for pagination)\r\n   * @param query - Query parameters (without limit/offset)\r\n   * @returns Count of matching logs\r\n   */\r\n  getUsageLogsCount(query: Omit<UsageLogQuery, \"limit\" | \"offset\">): Promise<number>;\r\n\r\n  // ==================== Cleanup Operations ====================\r\n\r\n  /**\r\n   * Find and expire reservations past their expiration time\r\n   * Used by cron job to clean up stale reservations\r\n   * @param batchSize - Maximum number of reservations to process per batch (default: 100)\r\n   * @param maxIterations - Maximum number of pagination iterations to prevent infinite loops (default: 100)\r\n   * @returns Cleanup results with counts and errors\r\n   */\r\n  findAndExpireReservations(batchSize?: number, maxIterations?: number): Promise<{\r\n    expiredCount: number;\r\n    creditsReleased: number;\r\n    errors: string[];\r\n  }>;\r\n\r\n  // ==================== Atomic Monthly Reset ====================\r\n\r\n  /**\r\n   * Atomically perform monthly reset if needed\r\n   * Uses optimistic locking to prevent race conditions\r\n   *\r\n   * @param userId - User ID\r\n   * @param tier - User's current subscription tier (for determining new balance)\r\n   * @param expectedResetAt - The expected monthlyResetAt value (for optimistic locking)\r\n   * @returns Result indicating whether reset was performed and updated credits\r\n   */\r\n  atomicMonthlyReset(\r\n    userId: string,\r\n    tier: SubscriptionTier,\r\n    expectedResetAt: Date | string\r\n  ): Promise<MonthlyResetResult>;\r\n\r\n  // ==================== Subscription Expiry ====================\r\n\r\n  /**\r\n   * Check and handle subscription expiry with grace period\r\n   * Auto-downgrades expired subscriptions after grace period\r\n   *\r\n   * @param userId - User ID\r\n   * @param gracePeriodDays - Days to allow after expiry before downgrade (default: 3)\r\n   * @returns Result indicating whether downgrade occurred\r\n   */\r\n  checkAndHandleSubscriptionExpiry(\r\n    userId: string,\r\n    gracePeriodDays?: number\r\n  ): Promise<SubscriptionExpiryResult>;\r\n\r\n  // ==================== Journal Entries ====================\r\n\r\n  /**\r\n   * Create a journal entry for audit trail\r\n   * @param input - Journal entry data\r\n   * @returns Created journal entry\r\n   */\r\n  createJournalEntry(input: CreateJournalEntryInput): Promise<PortableJournalEntry>;\r\n\r\n  /**\r\n   * Get journal entries for a user\r\n   * @param query - Query parameters\r\n   * @returns List of journal entries\r\n   */\r\n  getJournalEntries(query: JournalEntryQuery): Promise<PortableJournalEntry[]>;\r\n\r\n  /**\r\n   * Get journal entry count for pagination\r\n   * @param query - Query parameters (without limit/offset)\r\n   * @returns Count of matching entries\r\n   */\r\n  getJournalEntriesCount(query: Omit<JournalEntryQuery, \"limit\" | \"offset\">): Promise<number>;\r\n}\r\n\r\n/**\r\n * Factory type for creating repository instances\r\n */\r\nexport type CreditRepositoryFactory = () => ICreditRepository;\r\n\r\n/**\r\n * Convert PortableUserCredits to client-safe format\r\n * Utility function that implementations can use\r\n */\r\nexport function toClientUserCredits(credits: PortableUserCredits): PortableUserCredits {\r\n  // Already in portable format, just ensure all timestamps are ISO strings\r\n  return {\r\n    userId: credits.userId,\r\n    balance: credits.balance,\r\n    bonusCredits: credits.bonusCredits ?? 0,\r\n    reserved: credits.reserved,\r\n    tier: credits.tier,\r\n    monthlyLimit: credits.monthlyLimit,\r\n    monthlyUsed: credits.monthlyUsed,\r\n    monthlyResetAt: toISOString(credits.monthlyResetAt),\r\n    subscriptionExpiresAt: credits.subscriptionExpiresAt\r\n      ? toISOString(credits.subscriptionExpiresAt)\r\n      : null,\r\n    createdAt: toISOString(credits.createdAt),\r\n    updatedAt: toISOString(credits.updatedAt),\r\n  };\r\n}\r\n\r\n/**\r\n * Convert any timestamp-like value to ISO string\r\n */\r\nfunction toISOString(value: unknown): string {\r\n  if (!value) return new Date().toISOString();\r\n  if (typeof value === \"string\") return value;\r\n  if (value instanceof Date) return value.toISOString();\r\n  // Handle Firestore Timestamp-like objects\r\n  if (typeof value === \"object\" && \"toDate\" in value && typeof (value as { toDate: () => Date }).toDate === \"function\") {\r\n    return (value as { toDate: () => Date }).toDate().toISOString();\r\n  }\r\n  return new Date().toISOString();\r\n}\r\n","/**\r\n * Shared utilities for credit repository implementations\r\n *\r\n * These are pure functions with no server-only dependencies,\r\n * making them safe to use in any repository implementation.\r\n */\r\n\r\n/**\r\n * Generate a unique ID\r\n * Uses timestamp + random string for uniqueness\r\n */\r\nexport function generateId(): string {\r\n  return `${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;\r\n}\r\n\r\n/**\r\n * Convert date-like value to Date object\r\n * Handles: Date, string, Firestore Timestamp\r\n */\r\nexport function toDate(value: Date | string | unknown): Date {\r\n  if (value instanceof Date) return value;\r\n  if (typeof value === \"string\") return new Date(value);\r\n  // Handle Firestore Timestamp\r\n  if (typeof value === \"object\" && value !== null && \"toDate\" in value) {\r\n    return (value as { toDate: () => Date }).toDate();\r\n  }\r\n  return new Date();\r\n}\r\n\r\n/**\r\n * Get the first day of next month\r\n * Used for calculating monthly reset dates\r\n */\r\nexport function getNextMonthlyReset(from: Date = new Date()): Date {\r\n  const next = new Date(from);\r\n  next.setMonth(next.getMonth() + 1);\r\n  next.setDate(1);\r\n  next.setHours(0, 0, 0, 0);\r\n  return next;\r\n}\r\n","import { z } from \"zod\";\r\nimport type { SubscriptionTier, TierConfig } from \"../core/types.js\";\r\nimport { getConfigProvider } from \"./provider.js\";\r\n\r\n/**\r\n * Schema for tier configuration\r\n */\r\nconst tierConfigSchema = z.object({\r\n  tier: z.string().min(1),\r\n  monthlyCredits: z.number().min(0),\r\n  priceUsd: z.number().min(0),\r\n  features: z.array(z.string()),\r\n  isFree: z.boolean().optional(),\r\n  unlimited: z.boolean().optional(),\r\n  isDefault: z.boolean().optional(),\r\n});\r\n\r\n/**\r\n * Schema for credit system configuration\r\n */\r\nconst creditSystemConfigSchema = z.object({\r\n  /**\r\n   * Operation costs in credits\r\n   * Keys are operation type names (any non-empty string)\r\n   * Values are positive numbers representing credit cost\r\n   */\r\n  operationCosts: z.record(\r\n    z.string().min(1),\r\n    z.number().positive()\r\n  ),\r\n\r\n  /**\r\n   * Display labels for operation types (English canonical labels).\r\n   * Falls back to auto-generating from snake_case keys if not provided.\r\n   */\r\n  operationLabels: z.record(\r\n    z.string().min(1),\r\n    z.string().min(1)\r\n  ).optional().default({}),\r\n\r\n  /**\r\n   * Tier configurations\r\n   */\r\n  tierConfigs: z.record(\r\n    z.string().min(1),\r\n    tierConfigSchema\r\n  ),\r\n\r\n  /**\r\n   * Reservation expiry time in milliseconds\r\n   */\r\n  reservationExpiryMs: z.number().positive().default(5 * 60 * 1000),\r\n\r\n  /**\r\n   * Default credits for new users (free tier)\r\n   */\r\n  defaultFreeCredits: z.number().positive().default(25),\r\n\r\n  /**\r\n   * Grace period in days after subscription expires before downgrade\r\n   */\r\n  subscriptionGracePeriodDays: z.number().min(0).default(3),\r\n\r\n  /**\r\n   * Low balance notification threshold\r\n   */\r\n  lowBalanceThreshold: z.number().min(0).default(10),\r\n\r\n  /**\r\n   * Cooldown in hours between low balance notifications\r\n   */\r\n  lowBalanceNotificationCooldownHours: z.number().positive().default(24),\r\n\r\n  /**\r\n   * Feature flags for optional features\r\n   */\r\n  features: z.object({\r\n    journalEntries: z.boolean().default(true),\r\n    notifications: z.boolean().default(true),\r\n    subscriptionExpiry: z.boolean().default(true),\r\n    usageHistory: z.boolean().default(true),\r\n  }).default({\r\n    journalEntries: true,\r\n    notifications: true,\r\n    subscriptionExpiry: true,\r\n    usageHistory: true,\r\n  }),\r\n});\r\n\r\n/**\r\n * Credit system configuration type\r\n */\r\nexport type CreditSystemConfig = z.infer<typeof creditSystemConfigSchema>;\r\n\r\n/**\r\n * Default configuration\r\n */\r\nconst DEFAULT_CONFIG: CreditSystemConfig = {\r\n  operationCosts: {\r\n    story_generation: 5,\r\n    conversation: 2,\r\n    image_generation: 10,\r\n    template_generation: 10,\r\n  },\r\n  operationLabels: {\r\n    story_generation: \"Story generation\",\r\n    conversation: \"Conversation\",\r\n    image_generation: \"Image generation\",\r\n    template_generation: \"Template generation\",\r\n  },\r\n  tierConfigs: {\r\n    free: {\r\n      tier: \"free\",\r\n      monthlyCredits: 25,\r\n      priceUsd: 0,\r\n      isFree: true,\r\n      isDefault: true,\r\n      features: [\r\n        \"25 credits per month\",\r\n        \"Basic story generation\",\r\n        \"Standard image quality\",\r\n      ],\r\n    },\r\n    basic: {\r\n      tier: \"basic\",\r\n      monthlyCredits: 200,\r\n      priceUsd: 9.99,\r\n      features: [\r\n        \"200 credits per month\",\r\n        \"All story features\",\r\n        \"High quality images\",\r\n        \"Priority support\",\r\n      ],\r\n    },\r\n    premium: {\r\n      tier: \"premium\",\r\n      monthlyCredits: 500,\r\n      priceUsd: 19.99,\r\n      features: [\r\n        \"500 credits per month\",\r\n        \"All features\",\r\n        \"Highest quality images\",\r\n        \"Early access to new features\",\r\n        \"Priority support\",\r\n      ],\r\n    },\r\n    unlimited: {\r\n      tier: \"unlimited\",\r\n      monthlyCredits: 0, // 0 = unlimited\r\n      unlimited: true,\r\n      priceUsd: 49.99,\r\n      features: [\r\n        \"Unlimited credits\",\r\n        \"All features\",\r\n        \"Highest quality images\",\r\n        \"Early access to new features\",\r\n        \"Dedicated support\",\r\n      ],\r\n    },\r\n  },\r\n  reservationExpiryMs: 5 * 60 * 1000, // 5 minutes\r\n  defaultFreeCredits: 25,\r\n  subscriptionGracePeriodDays: 3,\r\n  lowBalanceThreshold: 10,\r\n  lowBalanceNotificationCooldownHours: 24,\r\n  features: {\r\n    journalEntries: true,\r\n    notifications: true,\r\n    subscriptionExpiry: true,\r\n    usageHistory: true,\r\n  },\r\n};\r\n\r\n/**\r\n * Current configuration (can be overridden)\r\n */\r\nlet currentConfig: CreditSystemConfig = DEFAULT_CONFIG;\r\n\r\n/**\r\n * Load configuration from environment variables\r\n * Can be extended to load from other sources (file, remote config, etc.)\r\n *\r\n * Operation costs are loaded from CREDITS_OPERATION_COSTS as JSON:\r\n * CREDITS_OPERATION_COSTS={\"story_generation\":5,\"image_generation\":10}\r\n */\r\nexport function loadConfigFromEnv(): Partial<CreditSystemConfig> {\r\n  const envConfig: Partial<CreditSystemConfig> = {};\r\n\r\n  // Load operation costs from env as JSON\r\n  const operationCostsEnv = process.env.CREDITS_OPERATION_COSTS;\r\n  if (operationCostsEnv) {\r\n    try {\r\n      const parsed = JSON.parse(operationCostsEnv);\r\n      if (typeof parsed === \"object\" && parsed !== null) {\r\n        envConfig.operationCosts = {\r\n          ...DEFAULT_CONFIG.operationCosts,\r\n          ...parsed,\r\n        };\r\n      }\r\n    } catch {\r\n      console.warn(\"[Credits Config] Failed to parse CREDITS_OPERATION_COSTS env var as JSON\");\r\n    }\r\n  }\r\n\r\n  // Load other settings from env\r\n  const reservationExpiry = process.env.CREDITS_RESERVATION_EXPIRY_MS;\r\n  if (reservationExpiry) {\r\n    envConfig.reservationExpiryMs = parseInt(reservationExpiry, 10);\r\n  }\r\n\r\n  const defaultCredits = process.env.CREDITS_DEFAULT_FREE;\r\n  if (defaultCredits) {\r\n    envConfig.defaultFreeCredits = parseInt(defaultCredits, 10);\r\n  }\r\n\r\n  const gracePeriod = process.env.CREDITS_GRACE_PERIOD_DAYS;\r\n  if (gracePeriod) {\r\n    envConfig.subscriptionGracePeriodDays = parseInt(gracePeriod, 10);\r\n  }\r\n\r\n  const lowBalanceThreshold = process.env.CREDITS_LOW_BALANCE_THRESHOLD;\r\n  if (lowBalanceThreshold) {\r\n    envConfig.lowBalanceThreshold = parseInt(lowBalanceThreshold, 10);\r\n  }\r\n\r\n  // Load feature flags\r\n  const featuresEnv = process.env.CREDITS_FEATURES;\r\n  if (featuresEnv) {\r\n    try {\r\n      const features = JSON.parse(featuresEnv);\r\n      envConfig.features = {\r\n        ...DEFAULT_CONFIG.features,\r\n        ...features,\r\n      };\r\n    } catch {\r\n      console.warn(\"[Credits Config] Failed to parse CREDITS_FEATURES env var\");\r\n    }\r\n  }\r\n\r\n  return envConfig;\r\n}\r\n\r\n/**\r\n * Initialize configuration\r\n * Merges default config with environment overrides\r\n */\r\nexport function initializeConfig(overrides?: Partial<CreditSystemConfig>): CreditSystemConfig {\r\n  const envConfig = loadConfigFromEnv();\r\n\r\n  const mergedConfig = {\r\n    ...DEFAULT_CONFIG,\r\n    ...envConfig,\r\n    ...overrides,\r\n    operationCosts: {\r\n      ...DEFAULT_CONFIG.operationCosts,\r\n      ...envConfig.operationCosts,\r\n      ...overrides?.operationCosts,\r\n    },\r\n    operationLabels: {\r\n      ...DEFAULT_CONFIG.operationLabels,\r\n      ...envConfig.operationLabels,\r\n      ...overrides?.operationLabels,\r\n    },\r\n    tierConfigs: {\r\n      ...DEFAULT_CONFIG.tierConfigs,\r\n      ...envConfig.tierConfigs,\r\n      ...overrides?.tierConfigs,\r\n    },\r\n    features: {\r\n      ...DEFAULT_CONFIG.features,\r\n      ...envConfig.features,\r\n      ...overrides?.features,\r\n    },\r\n  };\r\n\r\n  // Validate configuration\r\n  const result = creditSystemConfigSchema.safeParse(mergedConfig);\r\n  if (!result.success) {\r\n    console.error(\"[Credits Config] Invalid configuration:\", result.error.issues);\r\n    throw new Error(\"Invalid credit system configuration\");\r\n  }\r\n\r\n  currentConfig = result.data;\r\n  return currentConfig;\r\n}\r\n\r\n/**\r\n * Get the current configuration.\r\n * If an external provider is registered, delegates to it.\r\n * Otherwise falls back to the local currentConfig.\r\n */\r\nexport function getConfig(): CreditSystemConfig {\r\n  const provider = getConfigProvider();\r\n  if (provider) {\r\n    return provider.getConfig();\r\n  }\r\n  return currentConfig;\r\n}\r\n\r\n/**\r\n * Get all valid operation types from configuration\r\n * Returns the keys of the operationCosts object\r\n */\r\nexport function getValidOperationTypes(): string[] {\r\n  return Object.keys(getConfig().operationCosts);\r\n}\r\n\r\n/**\r\n * Check if an operation type is valid (configured in the system)\r\n * @param type - The operation type to check\r\n * @returns true if the operation type is configured\r\n */\r\nexport function isValidOperationType(type: string): boolean {\r\n  return type in getConfig().operationCosts;\r\n}\r\n\r\n/**\r\n * Get operation cost from configuration\r\n * @throws Error if the operation type is not configured\r\n */\r\nexport function getConfigOperationCost(operationType: string): number {\r\n  const cost = getConfig().operationCosts[operationType];\r\n  if (cost === undefined) {\r\n    throw new Error(\r\n      `Unknown operation type: ${operationType}. Valid types: ${getValidOperationTypes().join(\", \")}`\r\n    );\r\n  }\r\n  return cost;\r\n}\r\n\r\n/**\r\n * Get all valid tier ids from configuration\r\n * Returns the keys of the tierConfigs object\r\n */\r\nexport function getValidTiers(): string[] {\r\n  return Object.keys(getConfig().tierConfigs);\r\n}\r\n\r\n/**\r\n * Check if a tier id is valid (configured in the system)\r\n */\r\nexport function isValidTier(tier: string): boolean {\r\n  return tier in getConfig().tierConfigs;\r\n}\r\n\r\n/**\r\n * Check if a tier is the free/default tier.\r\n * Falls back to priceUsd === 0 when the isFree flag is not set.\r\n */\r\nexport function isFreeTier(tier: SubscriptionTier): boolean {\r\n  const c = getConfig().tierConfigs[tier];\r\n  if (!c) return false;\r\n  return c.isFree ?? c.priceUsd === 0;\r\n}\r\n\r\n/**\r\n * Check if a tier is unlimited.\r\n * Falls back to monthlyCredits === 0 when the unlimited flag is not set.\r\n */\r\nexport function isUnlimitedTier(tier: SubscriptionTier): boolean {\r\n  const c = getConfig().tierConfigs[tier];\r\n  if (!c) return false;\r\n  return c.unlimited ?? c.monthlyCredits === 0;\r\n}\r\n\r\n/**\r\n * Get the tier assigned to brand-new users.\r\n * Prefers the tier flagged isDefault, then the first isFree tier, then \"free\".\r\n */\r\nexport function getDefaultTier(): SubscriptionTier {\r\n  const entries = Object.entries(getConfig().tierConfigs);\r\n  const def =\r\n    entries.find(([, c]) => c.isDefault)?.[0] ??\r\n    entries.find(([, c]) => c.isFree ?? c.priceUsd === 0)?.[0];\r\n  return def ?? \"free\";\r\n}\r\n\r\n/**\r\n * Magic balance assigned to unlimited tiers on upgrade.\r\n * Kept at 999999 for back-compat with stored/displayed balances.\r\n */\r\nexport const UNLIMITED_BALANCE_SENTINEL = 999999;\r\n\r\n/**\r\n * Get the magic balance assigned to unlimited tiers on upgrade.\r\n */\r\nexport function getUnlimitedSentinelBalance(): number {\r\n  return UNLIMITED_BALANCE_SENTINEL;\r\n}\r\n\r\n/**\r\n * Get tier configuration from configuration.\r\n * Falls back to the default tier (with a warning) if the tier is unknown.\r\n */\r\nexport function getConfigTierConfig(tier: SubscriptionTier): TierConfig {\r\n  const cfg = getConfig().tierConfigs[tier];\r\n  if (cfg) return cfg;\r\n  const fallback = getDefaultTier();\r\n  console.warn(\r\n    `[Credits Config] Unknown tier \"${tier}\". Falling back to \"${fallback}\". Valid: ${getValidTiers().join(\", \")}`\r\n  );\r\n  return getConfig().tierConfigs[fallback]!;\r\n}\r\n\r\n/**\r\n * Get monthly limit for a tier from configuration\r\n * Returns Infinity for unlimited tier\r\n */\r\nexport function getConfigMonthlyLimit(tier: SubscriptionTier): number {\r\n  return isUnlimitedTier(tier) ? Infinity : getConfigTierConfig(tier).monthlyCredits;\r\n}\r\n\r\n/**\r\n * Runtime zod validator for tier ids. Validates against the LIVE config keys\r\n * at parse time, so apps that add tiers via config get correct validation.\r\n */\r\nexport const tierSchema = z\r\n  .string()\r\n  .refine(isValidTier, { message: \"Unknown subscription tier\" }) as unknown as z.ZodType<SubscriptionTier>;\r\n\r\n/**\r\n * Parse/validate a tier id against the live config keys.\r\n * @throws ZodError if the tier is not configured\r\n */\r\nexport function parseTier(value: unknown): SubscriptionTier {\r\n  return tierSchema.parse(value);\r\n}\r\n\r\n/**\r\n * Check if a feature is enabled\r\n */\r\nexport function isFeatureEnabled(feature: keyof CreditSystemConfig[\"features\"]): boolean {\r\n  return getConfig().features[feature];\r\n}\r\n\r\n/**\r\n * Convert a snake_case string to Title Case.\r\n * E.g., \"story_generation\" -> \"Story Generation\"\r\n */\r\nfunction snakeCaseToTitleCase(str: string): string {\r\n  return str\r\n    .replace(/_/g, \" \")\r\n    .replace(/\\b\\w/g, (c) => c.toUpperCase());\r\n}\r\n\r\n/**\r\n * Get the display label for an operation type.\r\n * Falls back to converting snake_case to Title Case if no label is configured.\r\n */\r\nexport function getOperationLabel(operationType: string): string {\r\n  const labels = getConfig().operationLabels;\r\n  if (labels && operationType in labels) {\r\n    return labels[operationType];\r\n  }\r\n  return snakeCaseToTitleCase(operationType);\r\n}\r\n\r\n/**\r\n * Get all operation labels as a record.\r\n * For any operation in operationCosts that lacks a label, generates one from the key.\r\n */\r\nexport function getOperationLabels(): Record<string, string> {\r\n  const config = getConfig();\r\n  const result: Record<string, string> = {};\r\n  for (const key of Object.keys(config.operationCosts)) {\r\n    result[key] = config.operationLabels?.[key]\r\n      ?? snakeCaseToTitleCase(key);\r\n  }\r\n  return result;\r\n}\r\n\r\n/**\r\n * Reset configuration to defaults (for testing)\r\n */\r\nexport function resetConfig(): void {\r\n  currentConfig = DEFAULT_CONFIG;\r\n}\r\n\r\n// Initialize configuration on module load\r\ninitializeConfig();\r\n","import type { CreditSystemConfig } from \"./index.js\";\r\n\r\n/** Interface for external config providers (e.g. Firestore-backed) */\r\nexport interface ICreditConfigProvider {\r\n  getConfig(): CreditSystemConfig;\r\n}\r\n\r\nlet configProvider: ICreditConfigProvider | null = null;\r\n\r\n/** Register an external config provider. Called once at app startup. */\r\nexport function registerConfigProvider(provider: ICreditConfigProvider): void {\r\n  configProvider = provider;\r\n}\r\n\r\n/** Get the registered provider, or null if none registered. */\r\nexport function getConfigProvider(): ICreditConfigProvider | null {\r\n  return configProvider;\r\n}\r\n\r\n/** Clear registered provider (for testing). */\r\nexport function clearConfigProvider(): void {\r\n  configProvider = null;\r\n}\r\n","/**\r\n * In-Memory Credit Repository Implementation\r\n *\r\n * A database-agnostic implementation of ICreditRepository for testing and prototyping.\r\n * All data is stored in memory and lost when the process restarts.\r\n *\r\n * Usage:\r\n * - Unit tests without database dependency\r\n * - Local development and prototyping\r\n * - Reference implementation for custom repositories\r\n */\r\n\r\nimport type {\r\n  PortableUserCredits,\r\n  PortableReservation,\r\n  PortableTransaction,\r\n  PortableJournalEntry,\r\n  PortableUsageLog,\r\n  SubscriptionTier,\r\n  ReservationStatus,\r\n  MonthlyResetResult,\r\n  SubscriptionExpiryResult,\r\n} from \"../../core/types.js\";\r\nimport type {\r\n  ICreditRepository,\r\n  CreateReservationInput,\r\n  CreateTransactionInput,\r\n  CreateUsageLogInput,\r\n  CreateJournalEntryInput,\r\n  UsageLogQuery,\r\n  JournalEntryQuery,\r\n  CreditBalanceUpdate,\r\n  TierUpdateInput,\r\n} from \"../types.js\";\r\nimport { generateId, toDate, getNextMonthlyReset } from \"../utils.js\";\r\nimport {\r\n  getConfigMonthlyLimit,\r\n  getConfigTierConfig,\r\n  getDefaultTier,\r\n  isFreeTier,\r\n} from \"../../config/index.js\";\r\n\r\n/**\r\n * In-Memory implementation of ICreditRepository\r\n *\r\n * Implements all repository methods using Map-based storage.\r\n * Useful for testing and as a reference implementation.\r\n */\r\nexport class InMemoryCreditRepository implements ICreditRepository {\r\n  private users = new Map<string, PortableUserCredits>();\r\n  private reservations = new Map<string, Map<string, PortableReservation>>();\r\n  private transactions = new Map<string, PortableTransaction[]>();\r\n  private usageLogs: PortableUsageLog[] = [];\r\n  private journalEntries = new Map<string, PortableJournalEntry[]>();\r\n\r\n  // ==================== User Credits ====================\r\n\r\n  async getUserCredits(userId: string): Promise<PortableUserCredits | null> {\r\n    return this.users.get(userId) ?? null;\r\n  }\r\n\r\n  async initializeUserCredits(\r\n    userId: string,\r\n    tier: SubscriptionTier,\r\n    initialBalance: number\r\n  ): Promise<PortableUserCredits> {\r\n    const now = new Date().toISOString();\r\n    const credits: PortableUserCredits = {\r\n      userId,\r\n      balance: initialBalance,\r\n      bonusCredits: 0,\r\n      reserved: 0,\r\n      tier,\r\n      monthlyLimit: getConfigMonthlyLimit(tier),\r\n      monthlyUsed: 0,\r\n      monthlyResetAt: getNextMonthlyReset().toISOString(),\r\n      subscriptionExpiresAt: null,\r\n      createdAt: now,\r\n      updatedAt: now,\r\n    };\r\n    this.users.set(userId, credits);\r\n    return credits;\r\n  }\r\n\r\n  async updateUserCredits(userId: string, updates: CreditBalanceUpdate): Promise<void> {\r\n    const credits = this.users.get(userId);\r\n    if (!credits) {\r\n      throw new Error(`User ${userId} not found`);\r\n    }\r\n\r\n    const now = new Date().toISOString();\r\n\r\n    // Apply absolute updates\r\n    if (updates.balance !== undefined) credits.balance = updates.balance;\r\n    if (updates.bonusCredits !== undefined) credits.bonusCredits = updates.bonusCredits;\r\n    if (updates.reserved !== undefined) credits.reserved = updates.reserved;\r\n    if (updates.tier !== undefined) credits.tier = updates.tier;\r\n    if (updates.monthlyLimit !== undefined) credits.monthlyLimit = updates.monthlyLimit;\r\n    if (updates.monthlyUsed !== undefined) credits.monthlyUsed = updates.monthlyUsed;\r\n    if (updates.monthlyResetAt !== undefined) {\r\n      credits.monthlyResetAt = updates.monthlyResetAt instanceof Date\r\n        ? updates.monthlyResetAt.toISOString()\r\n        : updates.monthlyResetAt;\r\n    }\r\n    if (updates.subscriptionExpiresAt !== undefined) {\r\n      if (updates.subscriptionExpiresAt === null) {\r\n        credits.subscriptionExpiresAt = null;\r\n      } else {\r\n        credits.subscriptionExpiresAt = updates.subscriptionExpiresAt instanceof Date\r\n          ? updates.subscriptionExpiresAt.toISOString()\r\n          : updates.subscriptionExpiresAt;\r\n      }\r\n    }\r\n\r\n    // Apply increments\r\n    if (updates.balanceIncrement !== undefined) {\r\n      credits.balance += updates.balanceIncrement;\r\n    }\r\n    if (updates.bonusCreditsIncrement !== undefined) {\r\n      credits.bonusCredits += updates.bonusCreditsIncrement;\r\n    }\r\n    if (updates.reservedIncrement !== undefined) {\r\n      credits.reserved += updates.reservedIncrement;\r\n    }\r\n    if (updates.monthlyUsedIncrement !== undefined) {\r\n      credits.monthlyUsed += updates.monthlyUsedIncrement;\r\n    }\r\n\r\n    credits.updatedAt = now;\r\n    this.users.set(userId, credits);\r\n  }\r\n\r\n  async updateUserTier(userId: string, input: TierUpdateInput): Promise<void> {\r\n    const credits = this.users.get(userId);\r\n    if (!credits) {\r\n      throw new Error(`User ${userId} not found`);\r\n    }\r\n\r\n    credits.tier = input.tier;\r\n    credits.monthlyLimit = input.monthlyLimit;\r\n    if (input.balance !== undefined) credits.balance = input.balance;\r\n    if (input.monthlyUsed !== undefined) credits.monthlyUsed = input.monthlyUsed;\r\n    if (input.subscriptionExpiresAt !== undefined) {\r\n      if (input.subscriptionExpiresAt === null) {\r\n        credits.subscriptionExpiresAt = null;\r\n      } else {\r\n        credits.subscriptionExpiresAt = input.subscriptionExpiresAt instanceof Date\r\n          ? input.subscriptionExpiresAt.toISOString()\r\n          : input.subscriptionExpiresAt;\r\n      }\r\n    }\r\n    credits.updatedAt = new Date().toISOString();\r\n\r\n    this.users.set(userId, credits);\r\n  }\r\n\r\n  // ==================== Reservations ====================\r\n\r\n  async createReservation(input: CreateReservationInput): Promise<PortableReservation> {\r\n    const now = new Date().toISOString();\r\n    const reservation: PortableReservation = {\r\n      id: generateId(),\r\n      userId: input.userId,\r\n      amount: input.amount,\r\n      operationType: input.operationType,\r\n      status: \"reserved\",\r\n      createdAt: now,\r\n      expiresAt: input.expiresAt.toISOString(),\r\n    };\r\n\r\n    if (!this.reservations.has(input.userId)) {\r\n      this.reservations.set(input.userId, new Map());\r\n    }\r\n    this.reservations.get(input.userId)!.set(reservation.id, reservation);\r\n\r\n    return reservation;\r\n  }\r\n\r\n  async getReservation(\r\n    userId: string,\r\n    reservationId: string\r\n  ): Promise<PortableReservation | null> {\r\n    const userReservations = this.reservations.get(userId);\r\n    if (!userReservations) return null;\r\n    return userReservations.get(reservationId) ?? null;\r\n  }\r\n\r\n  async updateReservationStatus(\r\n    userId: string,\r\n    reservationId: string,\r\n    status: ReservationStatus,\r\n    completedAt?: Date\r\n  ): Promise<void> {\r\n    const userReservations = this.reservations.get(userId);\r\n    if (!userReservations) {\r\n      throw new Error(`No reservations found for user ${userId}`);\r\n    }\r\n\r\n    const reservation = userReservations.get(reservationId);\r\n    if (!reservation) {\r\n      throw new Error(`Reservation ${reservationId} not found`);\r\n    }\r\n\r\n    reservation.status = status;\r\n    if (completedAt) reservation.completedAt = completedAt.toISOString();\r\n\r\n    userReservations.set(reservationId, reservation);\r\n  }\r\n\r\n  // ==================== Atomic Operations ====================\r\n\r\n  async reserveCreditsAtomic(\r\n    userId: string,\r\n    amount: number,\r\n    operationType: string,\r\n    expiresAt: Date\r\n  ): Promise<PortableReservation> {\r\n    const credits = this.users.get(userId);\r\n    if (!credits) {\r\n      throw new Error(`User ${userId} not found`);\r\n    }\r\n\r\n    // Calculate available credits (balance + bonusCredits - reserved)\r\n    const available = credits.balance + credits.bonusCredits - credits.reserved;\r\n    if (available < amount) {\r\n      throw new Error(\r\n        `Insufficient credits. Available: ${available}, Required: ${amount}`\r\n      );\r\n    }\r\n\r\n    // Create reservation\r\n    const reservation = await this.createReservation({\r\n      userId,\r\n      amount,\r\n      operationType,\r\n      expiresAt,\r\n    });\r\n\r\n    // Update reserved amount\r\n    credits.reserved += amount;\r\n    credits.updatedAt = new Date().toISOString();\r\n    this.users.set(userId, credits);\r\n\r\n    return reservation;\r\n  }\r\n\r\n  async commitReservationAtomic(userId: string, reservationId: string): Promise<void> {\r\n    const credits = this.users.get(userId);\r\n    if (!credits) {\r\n      throw new Error(`User ${userId} not found`);\r\n    }\r\n\r\n    const reservation = await this.getReservation(userId, reservationId);\r\n    if (!reservation) {\r\n      throw new Error(`Reservation ${reservationId} not found`);\r\n    }\r\n\r\n    // Idempotent: re-committing an already-committed reservation is a no-op\r\n    // (retry-safe). Committing a released/expired one is a genuine conflict.\r\n    if (reservation.status === \"committed\") {\r\n      return;\r\n    }\r\n\r\n    if (reservation.status !== \"reserved\") {\r\n      throw new Error(\r\n        `Cannot commit reservation in ${reservation.status} state`\r\n      );\r\n    }\r\n\r\n    const amount = reservation.amount;\r\n\r\n    // Deduct from balance (bonus credits first logic can be added if needed)\r\n    credits.balance -= amount;\r\n    credits.reserved -= amount;\r\n    credits.monthlyUsed += amount;\r\n    credits.updatedAt = new Date().toISOString();\r\n    this.users.set(userId, credits);\r\n\r\n    // Update reservation status\r\n    await this.updateReservationStatus(userId, reservationId, \"committed\", new Date());\r\n  }\r\n\r\n  async releaseReservationAtomic(userId: string, reservationId: string): Promise<void> {\r\n    const credits = this.users.get(userId);\r\n    if (!credits) {\r\n      throw new Error(`User ${userId} not found`);\r\n    }\r\n\r\n    const reservation = await this.getReservation(userId, reservationId);\r\n    if (!reservation) {\r\n      throw new Error(`Reservation ${reservationId} not found`);\r\n    }\r\n\r\n    if (reservation.status !== \"reserved\") {\r\n      // Already processed, no-op\r\n      return;\r\n    }\r\n\r\n    // Release reserved credits\r\n    credits.reserved -= reservation.amount;\r\n    credits.updatedAt = new Date().toISOString();\r\n    this.users.set(userId, credits);\r\n\r\n    // Update reservation status\r\n    await this.updateReservationStatus(userId, reservationId, \"released\", new Date());\r\n  }\r\n\r\n  async addCreditsAtomic(\r\n    userId: string,\r\n    amount: number,\r\n    description: string,\r\n    paymentRef?: string\r\n  ): Promise<void> {\r\n    const credits = this.users.get(userId);\r\n    if (!credits) {\r\n      throw new Error(`User ${userId} not found`);\r\n    }\r\n\r\n    const previousBalance = credits.bonusCredits;\r\n    credits.bonusCredits += amount;\r\n    credits.updatedAt = new Date().toISOString();\r\n    this.users.set(userId, credits);\r\n\r\n    // Create transaction\r\n    await this.createTransaction({\r\n      userId,\r\n      type: \"purchase\",\r\n      amount,\r\n      description,\r\n      paymentRef,\r\n      previousBalance,\r\n      newBalance: credits.bonusCredits,\r\n    });\r\n  }\r\n\r\n  async deductCreditsAtomic(\r\n    userId: string,\r\n    amount: number\r\n  ): Promise<{ previousBalance: number; newBalance: number }> {\r\n    if (amount <= 0) {\r\n      throw new Error(`deductCreditsAtomic amount must be positive (got ${amount})`);\r\n    }\r\n\r\n    const credits = this.users.get(userId);\r\n    if (!credits) {\r\n      throw new Error(`User credits not found for userId: ${userId}`);\r\n    }\r\n\r\n    const available = credits.balance + credits.bonusCredits - credits.reserved;\r\n    if (available < amount) {\r\n      throw new Error(\r\n        `Insufficient credits. Available: ${available}, requested: ${amount}`\r\n      );\r\n    }\r\n\r\n    // Drain balance first, then bonusCredits — same policy as commit.\r\n    const balanceDeduction = Math.min(credits.balance, amount);\r\n    const bonusDeduction = amount - balanceDeduction;\r\n\r\n    const previousBalance = credits.balance + credits.bonusCredits;\r\n    credits.balance -= balanceDeduction;\r\n    credits.bonusCredits -= bonusDeduction;\r\n    credits.updatedAt = new Date().toISOString();\r\n    this.users.set(userId, credits);\r\n\r\n    return { previousBalance, newBalance: previousBalance - amount };\r\n  }\r\n\r\n  // ==================== Transactions ====================\r\n\r\n  async createTransaction(input: CreateTransactionInput): Promise<PortableTransaction> {\r\n    const transaction: PortableTransaction = {\r\n      id: generateId(),\r\n      userId: input.userId,\r\n      type: input.type,\r\n      amount: input.amount,\r\n      description: input.description,\r\n      paymentRef: input.paymentRef,\r\n      previousBalance: input.previousBalance,\r\n      newBalance: input.newBalance,\r\n      createdAt: new Date().toISOString(),\r\n    };\r\n\r\n    if (!this.transactions.has(input.userId)) {\r\n      this.transactions.set(input.userId, []);\r\n    }\r\n    this.transactions.get(input.userId)!.push(transaction);\r\n\r\n    return transaction;\r\n  }\r\n\r\n  async getTransactions(\r\n    userId: string,\r\n    limit = 50,\r\n    offset = 0\r\n  ): Promise<PortableTransaction[]> {\r\n    const userTransactions = this.transactions.get(userId) ?? [];\r\n    // Sort by createdAt descending (most recent first)\r\n    const sorted = [...userTransactions].sort((a, b) => {\r\n      const aDate = toDate(a.createdAt).getTime();\r\n      const bDate = toDate(b.createdAt).getTime();\r\n      return bDate - aDate;\r\n    });\r\n    return sorted.slice(offset, offset + limit);\r\n  }\r\n\r\n  // ==================== Usage Logs ====================\r\n\r\n  async logUsage(input: CreateUsageLogInput): Promise<PortableUsageLog> {\r\n    const log: PortableUsageLog = {\r\n      id: generateId(),\r\n      userId: input.userId,\r\n      operationType: input.operationType,\r\n      provider: input.provider,\r\n      creditsUsed: input.creditsUsed,\r\n      success: input.success,\r\n      errorMessage: input.errorMessage,\r\n      resourceId: input.resourceId,\r\n      resourceType: input.resourceType,\r\n      requestId: input.requestId,\r\n      metadata: input.metadata,\r\n      createdAt: new Date().toISOString(),\r\n    };\r\n\r\n    this.usageLogs.push(log);\r\n    return log;\r\n  }\r\n\r\n  async getUsageLogs(query: UsageLogQuery): Promise<PortableUsageLog[]> {\r\n    let results = [...this.usageLogs];\r\n\r\n    // Apply filters\r\n    if (query.userId) {\r\n      results = results.filter((log) => log.userId === query.userId);\r\n    }\r\n    if (query.operationType) {\r\n      results = results.filter((log) => log.operationType === query.operationType);\r\n    }\r\n    if (query.success !== undefined) {\r\n      results = results.filter((log) => log.success === query.success);\r\n    }\r\n    if (query.startDate) {\r\n      const startTime = query.startDate.getTime();\r\n      results = results.filter(\r\n        (log) => toDate(log.createdAt).getTime() >= startTime\r\n      );\r\n    }\r\n    if (query.endDate) {\r\n      const endTime = query.endDate.getTime();\r\n      results = results.filter(\r\n        (log) => toDate(log.createdAt).getTime() <= endTime\r\n      );\r\n    }\r\n\r\n    // Sort by createdAt descending\r\n    results.sort((a, b) => {\r\n      return toDate(b.createdAt).getTime() - toDate(a.createdAt).getTime();\r\n    });\r\n\r\n    // Apply pagination\r\n    const offset = query.offset ?? 0;\r\n    const limit = query.limit ?? 50;\r\n    return results.slice(offset, offset + limit);\r\n  }\r\n\r\n  async getUsageLogsCount(\r\n    query: Omit<UsageLogQuery, \"limit\" | \"offset\">\r\n  ): Promise<number> {\r\n    const results = await this.getUsageLogs({ ...query, limit: Infinity, offset: 0 });\r\n    return results.length;\r\n  }\r\n\r\n  // ==================== Journal Entries ====================\r\n\r\n  async createJournalEntry(input: CreateJournalEntryInput): Promise<PortableJournalEntry> {\r\n    const entry: PortableJournalEntry = {\r\n      id: generateId(),\r\n      userId: input.userId,\r\n      entryType: input.entryType,\r\n      amount: input.amount,\r\n      balanceAfter: input.balanceAfter,\r\n      source: input.source,\r\n      referenceId: input.referenceId,\r\n      referenceType: input.referenceType,\r\n      description: input.description,\r\n      metadata: input.metadata,\r\n      createdAt: new Date().toISOString(),\r\n    };\r\n\r\n    if (!this.journalEntries.has(input.userId)) {\r\n      this.journalEntries.set(input.userId, []);\r\n    }\r\n    this.journalEntries.get(input.userId)!.push(entry);\r\n\r\n    return entry;\r\n  }\r\n\r\n  async getJournalEntries(query: JournalEntryQuery): Promise<PortableJournalEntry[]> {\r\n    let results = this.journalEntries.get(query.userId) ?? [];\r\n\r\n    // Apply filters\r\n    if (query.source) {\r\n      results = results.filter((entry) => entry.source === query.source);\r\n    }\r\n    if (query.referenceType) {\r\n      results = results.filter((entry) => entry.referenceType === query.referenceType);\r\n    }\r\n    if (query.startDate) {\r\n      const startTime = query.startDate.getTime();\r\n      results = results.filter(\r\n        (entry) => toDate(entry.createdAt).getTime() >= startTime\r\n      );\r\n    }\r\n    if (query.endDate) {\r\n      const endTime = query.endDate.getTime();\r\n      results = results.filter(\r\n        (entry) => toDate(entry.createdAt).getTime() <= endTime\r\n      );\r\n    }\r\n\r\n    // Sort by createdAt descending\r\n    results = [...results].sort((a, b) => {\r\n      return toDate(b.createdAt).getTime() - toDate(a.createdAt).getTime();\r\n    });\r\n\r\n    // Apply pagination\r\n    const offset = query.offset ?? 0;\r\n    const limit = query.limit ?? 50;\r\n    return results.slice(offset, offset + limit);\r\n  }\r\n\r\n  async getJournalEntriesCount(\r\n    query: Omit<JournalEntryQuery, \"limit\" | \"offset\">\r\n  ): Promise<number> {\r\n    const results = await this.getJournalEntries({\r\n      ...query,\r\n      limit: Infinity,\r\n      offset: 0,\r\n    });\r\n    return results.length;\r\n  }\r\n\r\n  // ==================== Cleanup Operations ====================\r\n\r\n  async findAndExpireReservations(\r\n    _batchSize = 100,\r\n    _maxIterations = 100\r\n  ): Promise<{\r\n    expiredCount: number;\r\n    creditsReleased: number;\r\n    errors: string[];\r\n  }> {\r\n    const now = new Date();\r\n    let expiredCount = 0;\r\n    let creditsReleased = 0;\r\n    const errors: string[] = [];\r\n\r\n    for (const [userId, userReservations] of this.reservations) {\r\n      for (const [reservationId, reservation] of userReservations) {\r\n        if (\r\n          reservation.status === \"reserved\" &&\r\n          toDate(reservation.expiresAt).getTime() < now.getTime()\r\n        ) {\r\n          try {\r\n            // Release the reservation\r\n            await this.releaseReservationAtomic(userId, reservationId);\r\n            // Mark as expired instead of released\r\n            reservation.status = \"expired\";\r\n            userReservations.set(reservationId, reservation);\r\n\r\n            expiredCount++;\r\n            creditsReleased += reservation.amount;\r\n          } catch (error) {\r\n            errors.push(\r\n              `Failed to expire reservation ${reservationId}: ${error}`\r\n            );\r\n          }\r\n        }\r\n      }\r\n    }\r\n\r\n    return { expiredCount, creditsReleased, errors };\r\n  }\r\n\r\n  // ==================== Atomic Monthly Reset ====================\r\n\r\n  async atomicMonthlyReset(\r\n    userId: string,\r\n    tier: SubscriptionTier,\r\n    expectedResetAt: Date | string\r\n  ): Promise<MonthlyResetResult> {\r\n    const credits = this.users.get(userId);\r\n    if (!credits) {\r\n      throw new Error(`User ${userId} not found`);\r\n    }\r\n\r\n    // Optimistic locking: check if expectedResetAt matches current value\r\n    const currentResetAt = toDate(credits.monthlyResetAt).getTime();\r\n    const expected = toDate(expectedResetAt).getTime();\r\n\r\n    if (currentResetAt !== expected) {\r\n      // Another request already performed the reset\r\n      return { wasReset: false, credits };\r\n    }\r\n\r\n    // Perform the reset\r\n    const newBalance = getConfigMonthlyLimit(tier);\r\n    const nextReset = getNextMonthlyReset();\r\n\r\n    credits.balance = newBalance === Infinity ? credits.balance : newBalance;\r\n    credits.monthlyUsed = 0;\r\n    credits.monthlyResetAt = nextReset.toISOString();\r\n    credits.updatedAt = new Date().toISOString();\r\n\r\n    this.users.set(userId, credits);\r\n\r\n    return { wasReset: true, credits };\r\n  }\r\n\r\n  // ==================== Subscription Expiry ====================\r\n\r\n  async checkAndHandleSubscriptionExpiry(\r\n    userId: string,\r\n    gracePeriodDays = 3\r\n  ): Promise<SubscriptionExpiryResult> {\r\n    const credits = this.users.get(userId);\r\n    if (!credits) {\r\n      throw new Error(`User ${userId} not found`);\r\n    }\r\n\r\n    // Free tier doesn't expire\r\n    if (isFreeTier(credits.tier) || !credits.subscriptionExpiresAt) {\r\n      return {\r\n        wasDowngraded: false,\r\n        inGracePeriod: false,\r\n        graceDaysRemaining: 0,\r\n        credits,\r\n      };\r\n    }\r\n\r\n    const now = new Date();\r\n    const expiresAt = toDate(credits.subscriptionExpiresAt);\r\n    const daysSinceExpiry =\r\n      (now.getTime() - expiresAt.getTime()) / (1000 * 60 * 60 * 24);\r\n\r\n    if (daysSinceExpiry <= 0) {\r\n      // Not expired yet\r\n      return {\r\n        wasDowngraded: false,\r\n        inGracePeriod: false,\r\n        graceDaysRemaining: 0,\r\n        credits,\r\n      };\r\n    }\r\n\r\n    if (daysSinceExpiry <= gracePeriodDays) {\r\n      // In grace period\r\n      return {\r\n        wasDowngraded: false,\r\n        inGracePeriod: true,\r\n        graceDaysRemaining: Math.ceil(gracePeriodDays - daysSinceExpiry),\r\n        credits,\r\n      };\r\n    }\r\n\r\n    // Grace period expired - downgrade to the default (free) tier\r\n    const defaultTier = getDefaultTier();\r\n    const defaultTierConfig = getConfigTierConfig(defaultTier);\r\n\r\n    credits.tier = defaultTier;\r\n    credits.monthlyLimit = defaultTierConfig.monthlyCredits;\r\n    credits.balance = Math.min(credits.balance, defaultTierConfig.monthlyCredits);\r\n    credits.subscriptionExpiresAt = null;\r\n    credits.updatedAt = new Date().toISOString();\r\n\r\n    this.users.set(userId, credits);\r\n\r\n    return {\r\n      wasDowngraded: true,\r\n      inGracePeriod: false,\r\n      graceDaysRemaining: 0,\r\n      credits,\r\n    };\r\n  }\r\n\r\n  // ==================== Testing Utilities ====================\r\n\r\n  /**\r\n   * Clear all data (useful for testing)\r\n   */\r\n  clear(): void {\r\n    this.users.clear();\r\n    this.reservations.clear();\r\n    this.transactions.clear();\r\n    this.usageLogs = [];\r\n    this.journalEntries.clear();\r\n  }\r\n\r\n  /**\r\n   * Get all users (useful for testing/debugging)\r\n   */\r\n  getAllUsers(): PortableUserCredits[] {\r\n    return Array.from(this.users.values());\r\n  }\r\n\r\n  /**\r\n   * Get all reservations for a user (useful for testing)\r\n   */\r\n  getAllReservations(userId: string): PortableReservation[] {\r\n    const userReservations = this.reservations.get(userId);\r\n    if (!userReservations) return [];\r\n    return Array.from(userReservations.values());\r\n  }\r\n}\r\n\r\n/**\r\n * Create a new in-memory repository instance\r\n * Each call creates a fresh, isolated instance\r\n */\r\nexport function createInMemoryCreditRepository(): InMemoryCreditRepository {\r\n  return new InMemoryCreditRepository();\r\n}\r\n"],"mappings":"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","names":[]}