{"version":3,"sources":["../src/index.ts","../src/core/types.ts","../src/core/errors.ts","../src/core/deferred.ts","../src/config/index.ts","../src/config/provider.ts","../src/core/operations.ts","../src/config/costs.ts","../src/repository/types.ts","../src/repository/utils.ts","../src/repository/memory/index.ts","../src/auth/api-key.ts","../src/service/credits-service.ts","../src/adapters/generic.ts","../src/notifications/index.ts","../src/sdk/types.ts","../src/sdk/errors.ts","../src/sdk/client.ts","../src/sdk/admin-client.ts","../src/utils/error-utils.ts","../src/utils/index.ts"],"sourcesContent":["/**\r\n * @nehorai/credits - Framework-agnostic credits system\r\n *\r\n * Provides a complete credits/billing system with:\r\n * - Two-phase commit for safe credit deduction\r\n * - Journal-based audit trail\r\n * - Subscription tier management\r\n * - Notification hooks\r\n * - REST API SDK\r\n *\r\n * @example\r\n * ```typescript\r\n * import {\r\n *   CreditsService,\r\n *   createInMemoryCreditRepository,\r\n *   CreditError,\r\n * } from '@nehorai/credits';\r\n *\r\n * const repository = createInMemoryCreditRepository();\r\n * const service = new CreditsService(repository);\r\n *\r\n * // Check credits\r\n * const check = await service.checkCredits(userId, 10);\r\n * if (!check.hasCredits) {\r\n *   throw new Error(`Need ${check.shortfall} more credits`);\r\n * }\r\n *\r\n * // Reserve and execute\r\n * const reservation = await service.reserveCredits(userId, 10, 'story_generation');\r\n * try {\r\n *   await doWork();\r\n *   await service.commitCredits(userId, reservation.id);\r\n * } catch (error) {\r\n *   await service.releaseCredits(userId, reservation.id);\r\n *   throw error;\r\n * }\r\n * ```\r\n */\r\n\r\n// ==================== Core ====================\r\nexport * from \"./core/index.js\";\r\n\r\n// ==================== Config ====================\r\nexport {\r\n  getConfig,\r\n  initializeConfig,\r\n  loadConfigFromEnv,\r\n  getValidOperationTypes,\r\n  isValidOperationType,\r\n  getConfigOperationCost,\r\n  getConfigTierConfig,\r\n  getConfigMonthlyLimit,\r\n  getValidTiers,\r\n  isValidTier,\r\n  isFreeTier,\r\n  isUnlimitedTier,\r\n  getDefaultTier,\r\n  getUnlimitedSentinelBalance,\r\n  UNLIMITED_BALANCE_SENTINEL,\r\n  tierSchema,\r\n  parseTier,\r\n  isFeatureEnabled,\r\n  resetConfig,\r\n  getOperationLabel,\r\n  getOperationLabels,\r\n  type CreditSystemConfig,\r\n} from \"./config/index.js\";\r\n\r\nexport {\r\n  registerConfigProvider,\r\n  getConfigProvider,\r\n  clearConfigProvider,\r\n  type ICreditConfigProvider,\r\n} from \"./config/provider.js\";\r\n\r\nexport {\r\n  getOperationCosts,\r\n  getOperationCost,\r\n  getTierConfig,\r\n  getMonthlyLimit,\r\n  getDefaultFreeCredits,\r\n  getReservationExpiryMs,\r\n  getOperationCostsWithLabels,\r\n  type OperationCostInfo,\r\n} from \"./config/costs.js\";\r\n\r\n// ==================== Repository ====================\r\n// Export types and classes (excluding toDate which is already exported from core)\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 \"./repository/types.js\";\r\nexport { toClientUserCredits } from \"./repository/types.js\";\r\nexport { generateId, getNextMonthlyReset } from \"./repository/utils.js\";\r\nexport {\r\n  InMemoryCreditRepository,\r\n  createInMemoryCreditRepository,\r\n} from \"./repository/memory/index.js\";\r\n\r\n// ==================== Auth ====================\r\nexport * from \"./auth/index.js\";\r\n\r\n// ==================== Service ====================\r\nexport * from \"./service/index.js\";\r\n\r\n// ==================== Adapters ====================\r\nexport * from \"./adapters/index.js\";\r\n\r\n// ==================== Notifications ====================\r\nexport {\r\n  registerNotificationHandler,\r\n  clearNotificationHandlers,\r\n  clearCooldownState,\r\n  checkAndNotifyLowBalance,\r\n  notifySubscriptionExpiring,\r\n  notifySubscriptionExpired,\r\n  ConsoleNotificationHandler,\r\n  type CreditNotificationEvent,\r\n  type ICreditNotificationHandler,\r\n} from \"./notifications/index.js\";\r\n\r\n// ==================== SDK ====================\r\n// Export SDK (excluding types that conflict with core)\r\nexport { CreditsClient } from \"./sdk/client.js\";\r\nexport { AdminCreditsClient } from \"./sdk/admin-client.js\";\r\nexport type {\r\n  CreditsClientConfig,\r\n  AdminCreditsClientConfig,\r\n  UserCredits,\r\n  CreditReservation,\r\n  CreditCheckResult as SDKCreditCheckResult,\r\n  ReservationResult,\r\n  PaginationOptions,\r\n  UsageHistoryEntry as SDKUsageHistoryEntry,\r\n  UsageHistoryResponse as SDKUsageHistoryResponse,\r\n  ApiResponse,\r\n  SDKErrorCodeType,\r\n  SubscriptionTier as SDKSubscriptionTier,\r\n} from \"./sdk/types.js\";\r\nexport type {\r\n  CreditsConfig,\r\n  ListUsersResponse,\r\n  ListUsersOptions,\r\n} from \"./sdk/admin-client.js\";\r\nexport { SDKErrorCode } from \"./sdk/types.js\";\r\nexport {\r\n  CreditsSDKError,\r\n  NetworkError,\r\n  AuthenticationError,\r\n  AuthorizationError,\r\n  InsufficientCreditsError as SDKInsufficientCreditsError,\r\n  ReservationNotFoundError as SDKReservationNotFoundError,\r\n  ReservationExpiredError as SDKReservationExpiredError,\r\n  ValidationError,\r\n  ServerError,\r\n  parseApiError,\r\n} from \"./sdk/errors.js\";\r\n\r\n// ==================== Utils ====================\r\nexport {\r\n  getTotalCredits,\r\n  generateRequestId,\r\n  parseCreditError,\r\n  isCreditErrorMessage,\r\n  isPreviewMode,\r\n  createDummyReservation,\r\n  type CreditErrorInfo,\r\n} from \"./utils/index.js\";\r\n","/**\r\n * Portable credits types - framework and database agnostic\r\n *\r\n * These types use standard JavaScript types (string, Date) instead of\r\n * Firebase-specific types (Timestamp). This makes the credits system\r\n * portable to other environments without Firebase dependencies.\r\n *\r\n * Note: Firestore Timestamp is handled by the credits-firestore adapter.\r\n */\r\n\r\n// ==================== Utility Functions ====================\r\n\r\n/**\r\n * Calculate available credits from balance, bonus, and reserved amounts.\r\n *\r\n * Available = balance + bonusCredits - reserved\r\n *\r\n * @param balance - Regular monthly credits\r\n * @param bonusCredits - Purchased/admin credits\r\n * @param reserved - Credits locked for in-flight operations\r\n * @returns Available credits for new operations\r\n */\r\nexport function calculateAvailableCredits(\r\n  balance: number,\r\n  bonusCredits: number,\r\n  reserved: number\r\n): number {\r\n  return balance + bonusCredits - reserved;\r\n}\r\n\r\n/**\r\n * Convert various timestamp formats to ISO string.\r\n *\r\n * Handles:\r\n * - Date objects\r\n * - ISO string (returned as-is)\r\n * - Timestamp-like objects with toDate() method (Firebase)\r\n * - undefined/null (returns current time)\r\n *\r\n * @param value - Value to convert\r\n * @returns ISO 8601 string\r\n */\r\nexport function toPortableTimestamp(value: unknown): string {\r\n  if (!value) {\r\n    return new Date().toISOString();\r\n  }\r\n\r\n  if (typeof value === \"string\") {\r\n    return value;\r\n  }\r\n\r\n  if (value instanceof Date) {\r\n    return value.toISOString();\r\n  }\r\n\r\n  if (\r\n    typeof value === \"object\" &&\r\n    value !== null &&\r\n    \"toDate\" in value &&\r\n    typeof (value as { toDate: () => Date }).toDate === \"function\"\r\n  ) {\r\n    return (value as { toDate: () => Date }).toDate().toISOString();\r\n  }\r\n\r\n  return new Date().toISOString();\r\n}\r\n\r\n/**\r\n * Convert various timestamp formats to Date object.\r\n *\r\n * Handles:\r\n * - Date objects (returned as-is)\r\n * - ISO string\r\n * - Timestamp-like objects with toDate() method (Firebase)\r\n * - Invalid values (returns current date)\r\n *\r\n * @param value - Value to convert\r\n * @returns Date object\r\n */\r\nexport function toDate(value: unknown): Date {\r\n  if (value instanceof Date) {\r\n    return value;\r\n  }\r\n\r\n  if (typeof value === \"string\") {\r\n    return new Date(value);\r\n  }\r\n\r\n  if (\r\n    typeof value === \"object\" &&\r\n    value !== null &&\r\n    \"toDate\" in value &&\r\n    typeof (value as { toDate: () => Date }).toDate === \"function\"\r\n  ) {\r\n    return (value as { toDate: () => Date }).toDate();\r\n  }\r\n\r\n  return new Date();\r\n}\r\n\r\n// ==================== Subscription Types ====================\r\n\r\n/**\r\n * Built-in tier ids (autocomplete) — apps may add more via config.\r\n */\r\nexport type BuiltinTier = \"free\" | \"basic\" | \"premium\" | \"unlimited\";\r\n\r\n/**\r\n * Subscription tiers for the credits system.\r\n *\r\n * Any built-in OR any config-defined tier id. The `(string & {})` keeps builtin\r\n * autocomplete while accepting arbitrary configured tiers.\r\n */\r\nexport type SubscriptionTier = BuiltinTier | (string & {});\r\n\r\n// ==================== Operation Types ====================\r\n\r\n/**\r\n * Operation types for cost tracking (provider-agnostic)\r\n *\r\n * Dynamic string type - any operation configured in the system is valid.\r\n */\r\nexport type CreditOperationType = string;\r\n\r\n/**\r\n * AI providers\r\n */\r\nexport type AIProviderType = \"gemini\";\r\n\r\n/**\r\n * Resource type for usage logging\r\n */\r\nexport type ResourceType = string;\r\n\r\n// ==================== User Credits ====================\r\n\r\n/**\r\n * Portable user credits balance\r\n *\r\n * Uses ISO string timestamps instead of Firebase Timestamp.\r\n * This type is safe for JSON serialization and works across frameworks.\r\n */\r\nexport interface PortableUserCredits {\r\n  userId: string;\r\n  /** Current available balance (monthly credits, reset each month) */\r\n  balance: number;\r\n  /** Bonus credits from purchases/admin (never reset, persist until used) */\r\n  bonusCredits: number;\r\n  /** Credits currently reserved for in-flight operations */\r\n  reserved: number;\r\n  /** User's subscription tier */\r\n  tier: SubscriptionTier;\r\n  /** Monthly credit limit based on tier (0 = unlimited) */\r\n  monthlyLimit: number;\r\n  /** Credits used this month (resets monthly) */\r\n  monthlyUsed: number;\r\n  /** When monthly credits reset (start of next month) - ISO 8601 */\r\n  monthlyResetAt: string;\r\n  /** Subscription expiry (null/undefined for free tier) - ISO 8601 or null */\r\n  subscriptionExpiresAt?: string | null;\r\n  /** Creation timestamp - ISO 8601 */\r\n  createdAt: string;\r\n  /** Last update timestamp - ISO 8601 */\r\n  updatedAt: string;\r\n}\r\n\r\n// ==================== Reservation Types ====================\r\n\r\n/**\r\n * Credit reservation status for two-phase commit\r\n */\r\nexport type ReservationStatus = \"reserved\" | \"committed\" | \"released\" | \"expired\";\r\n\r\n/**\r\n * Portable credit reservation\r\n *\r\n * Used for two-phase commit to prevent double-spending.\r\n */\r\nexport interface PortableReservation {\r\n  id: string;\r\n  userId: string;\r\n  /** Amount of credits reserved */\r\n  amount: number;\r\n  /** Operation type for tracking */\r\n  operationType: CreditOperationType;\r\n  /** Current status of reservation */\r\n  status: ReservationStatus;\r\n  /** When reservation was created - ISO 8601 */\r\n  createdAt: string;\r\n  /** When reservation expires (for cleanup) - ISO 8601 */\r\n  expiresAt: string;\r\n  /** When reservation was committed/released - ISO 8601 or undefined */\r\n  completedAt?: string;\r\n}\r\n\r\n// ==================== Result Types ====================\r\n\r\n/**\r\n * Result of a credit check operation\r\n */\r\nexport interface CreditCheckResult {\r\n  /** Whether user has sufficient credits */\r\n  hasCredits: boolean;\r\n  /** Current balance (balance + bonusCredits) */\r\n  balance: number;\r\n  /** Required credits for operation */\r\n  required: number;\r\n  /** Shortfall if insufficient */\r\n  shortfall: number;\r\n}\r\n\r\n/**\r\n * Result of monthly reset operation\r\n */\r\nexport interface MonthlyResetResult {\r\n  /** Whether the reset was performed */\r\n  wasReset: boolean;\r\n  /** The user credits after the operation */\r\n  credits: PortableUserCredits;\r\n}\r\n\r\n/**\r\n * Result of subscription expiry check\r\n */\r\nexport interface SubscriptionExpiryResult {\r\n  /** Whether the subscription was downgraded */\r\n  wasDowngraded: boolean;\r\n  /** Whether user is in grace period */\r\n  inGracePeriod: boolean;\r\n  /** Days remaining in grace period (0 if not in grace) */\r\n  graceDaysRemaining: number;\r\n  /** The user credits after the operation */\r\n  credits: PortableUserCredits;\r\n}\r\n\r\n// ==================== Transaction Types ====================\r\n\r\n/**\r\n * Credit transaction type\r\n */\r\nexport type TransactionType = \"purchase\" | \"subscription\" | \"bonus\" | \"refund\" | \"adjustment\";\r\n\r\n/**\r\n * Portable credit transaction\r\n */\r\nexport interface PortableTransaction {\r\n  id: string;\r\n  userId: string;\r\n  type: TransactionType;\r\n  /** Amount of credits (positive for additions, negative for deductions) */\r\n  amount: number;\r\n  description: string;\r\n  /** Reference to payment provider */\r\n  paymentRef?: string;\r\n  previousBalance: number;\r\n  newBalance: number;\r\n  createdAt: string;\r\n}\r\n\r\n// ==================== Journal Types ====================\r\n\r\n/**\r\n * Source of a credit journal entry\r\n */\r\nexport type CreditSource =\r\n  | \"operation_commit\"\r\n  | \"operation_release\"\r\n  | \"purchase\"\r\n  | \"subscription_grant\"\r\n  | \"subscription_upgrade\"\r\n  | \"subscription_downgrade\"\r\n  | \"monthly_reset\"\r\n  | \"bonus\"\r\n  | \"refund\"\r\n  | \"admin_adjustment\"\r\n  | \"expiry\"\r\n  | \"reservation_expired\";\r\n\r\n/**\r\n * Type of reference for a journal entry\r\n */\r\nexport type JournalReferenceType =\r\n  | \"reservation\"\r\n  | \"transaction\"\r\n  | \"adjustment\"\r\n  | \"reset\"\r\n  | \"subscription\";\r\n\r\n/**\r\n * Portable credit journal entry for audit trail\r\n */\r\nexport interface PortableJournalEntry {\r\n  id: string;\r\n  userId: string;\r\n  /** Type of entry - debit decreases balance, credit increases balance */\r\n  entryType: \"debit\" | \"credit\";\r\n  /** Amount of credits involved */\r\n  amount: number;\r\n  /** Balance after this entry */\r\n  balanceAfter: number;\r\n  /** Source of the credit change */\r\n  source: CreditSource;\r\n  /** Reference ID (e.g., reservationId, transactionId) */\r\n  referenceId: string;\r\n  /** Type of reference */\r\n  referenceType: JournalReferenceType;\r\n  /** Human-readable description */\r\n  description: string;\r\n  /** Additional metadata */\r\n  metadata?: Record<string, unknown>;\r\n  /** When this entry was created - ISO 8601 */\r\n  createdAt: string;\r\n}\r\n\r\n// ==================== Usage Types ====================\r\n\r\n/**\r\n * Portable usage log entry\r\n */\r\nexport interface PortableUsageLog {\r\n  id: string;\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?: ResourceType;\r\n  requestId?: string;\r\n  metadata?: Record<string, unknown>;\r\n  createdAt: string;\r\n}\r\n\r\n/**\r\n * User-friendly usage history entry\r\n */\r\nexport interface UsageHistoryEntry {\r\n  id: string;\r\n  /** Type of entry for display */\r\n  type: \"usage\" | \"purchase\" | \"bonus\" | \"reset\" | \"refund\" | \"adjustment\";\r\n  /** Positive = earned, Negative = spent */\r\n  creditsChange: number;\r\n  /** Balance after this entry */\r\n  balanceAfter: number;\r\n  /** Human-readable description */\r\n  description: string;\r\n  /** When this occurred - ISO 8601 */\r\n  createdAt: string;\r\n}\r\n\r\n/**\r\n * Paginated usage history response\r\n */\r\nexport interface UsageHistoryResponse {\r\n  entries: UsageHistoryEntry[];\r\n  pagination: {\r\n    total: number;\r\n    limit: number;\r\n    offset: number;\r\n    hasMore: boolean;\r\n  };\r\n}\r\n\r\n// ==================== Tier Configuration ====================\r\n\r\n/**\r\n * Tier configuration for credit limits and pricing\r\n */\r\nexport interface TierConfig {\r\n  tier: SubscriptionTier;\r\n  monthlyCredits: number;\r\n  priceUsd: number;\r\n  features: string[];\r\n  /** Free/default tier marker — no subscription expiry, balance untouched on tier change. Defaults to priceUsd === 0. */\r\n  isFree?: boolean;\r\n  /** Unlimited tier marker. Defaults to monthlyCredits === 0. */\r\n  unlimited?: boolean;\r\n  /** Tier assigned to brand-new users. Defaults to the tier flagged isFree. */\r\n  isDefault?: boolean;\r\n}\r\n\r\n// ==================== Options Types ====================\r\n\r\n/**\r\n * Options for withCredits HOF\r\n */\r\nexport interface WithCreditsOptions {\r\n  /** Operation type for cost lookup */\r\n  operationType: CreditOperationType;\r\n  /** AI provider (defaults to \"gemini\") */\r\n  provider?: AIProviderType;\r\n  /** Custom cost override (use with caution) */\r\n  customCost?: number;\r\n  /** Resource ID for usage logging */\r\n  resourceId?: string;\r\n  /** Resource type for usage logging */\r\n  resourceType?: ResourceType;\r\n}\r\n\r\n// ==================== Constants ====================\r\n","/**\r\n * Credit system error classes - framework agnostic\r\n *\r\n * Provides typed error handling for credit operations.\r\n * These errors can be used across different environments.\r\n */\r\n\r\n/**\r\n * Error codes for credit operations\r\n */\r\nexport const CreditErrorCode = {\r\n  INSUFFICIENT_CREDITS: \"INSUFFICIENT_CREDITS\",\r\n  RESERVATION_NOT_FOUND: \"RESERVATION_NOT_FOUND\",\r\n  RESERVATION_EXPIRED: \"RESERVATION_EXPIRED\",\r\n  RESERVATION_ALREADY_PROCESSED: \"RESERVATION_ALREADY_PROCESSED\",\r\n  USER_NOT_FOUND: \"USER_NOT_FOUND\",\r\n  INVALID_OPERATION_TYPE: \"INVALID_OPERATION_TYPE\",\r\n  CONFIGURATION_ERROR: \"CONFIGURATION_ERROR\",\r\n  DATABASE_ERROR: \"DATABASE_ERROR\",\r\n} as const;\r\n\r\nexport type CreditErrorCodeType = (typeof CreditErrorCode)[keyof typeof CreditErrorCode];\r\n\r\n/**\r\n * Custom error class for credit operations\r\n *\r\n * Provides structured error information including:\r\n * - Error code for programmatic handling\r\n * - Human-readable message\r\n * - Optional details for debugging\r\n */\r\nexport class CreditError extends Error {\r\n  public readonly code: CreditErrorCodeType;\r\n  public readonly details?: Record<string, unknown>;\r\n\r\n  constructor(\r\n    message: string,\r\n    code: CreditErrorCodeType,\r\n    details?: Record<string, unknown>\r\n  ) {\r\n    super(message);\r\n    this.name = \"CreditError\";\r\n    this.code = code;\r\n    this.details = details;\r\n\r\n    // Maintains proper stack trace for where error was thrown\r\n    if (Error.captureStackTrace) {\r\n      Error.captureStackTrace(this, CreditError);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Convert to JSON-serializable object\r\n   */\r\n  toJSON(): { name: string; code: string; message: string; details?: Record<string, unknown> } {\r\n    return {\r\n      name: this.name,\r\n      code: this.code,\r\n      message: this.message,\r\n      ...(this.details && { details: this.details }),\r\n    };\r\n  }\r\n}\r\n\r\n/**\r\n * Type guard to check if an error is a CreditError\r\n */\r\nexport function isCreditError(error: unknown): error is CreditError {\r\n  return error instanceof CreditError;\r\n}\r\n\r\n/**\r\n * Check if an error represents insufficient credits\r\n *\r\n * Works with both CreditError and regular Error instances.\r\n * Useful for handling errors from different sources.\r\n */\r\nexport function isInsufficientCreditsError(error: unknown): boolean {\r\n  if (isCreditError(error)) {\r\n    return error.code === CreditErrorCode.INSUFFICIENT_CREDITS;\r\n  }\r\n\r\n  if (error instanceof Error) {\r\n    return error.message.toLowerCase().includes(\"insufficient credits\");\r\n  }\r\n\r\n  return false;\r\n}\r\n\r\n/**\r\n * Create an insufficient credits error\r\n */\r\nexport function createInsufficientCreditsError(\r\n  required: number,\r\n  available: number\r\n): CreditError {\r\n  return new CreditError(\r\n    `Insufficient credits: available ${available}, required ${required}`,\r\n    CreditErrorCode.INSUFFICIENT_CREDITS,\r\n    { required, available, shortfall: required - available }\r\n  );\r\n}\r\n\r\n/**\r\n * Create a reservation not found error\r\n */\r\nexport function createReservationNotFoundError(reservationId: string): CreditError {\r\n  return new CreditError(\r\n    `Reservation ${reservationId} not found`,\r\n    CreditErrorCode.RESERVATION_NOT_FOUND,\r\n    { reservationId }\r\n  );\r\n}\r\n\r\n/**\r\n * Create a reservation expired error\r\n */\r\nexport function createReservationExpiredError(reservationId: string): CreditError {\r\n  return new CreditError(\r\n    `Reservation ${reservationId} has expired`,\r\n    CreditErrorCode.RESERVATION_EXPIRED,\r\n    { reservationId }\r\n  );\r\n}\r\n\r\n/**\r\n * Create a reservation already processed error\r\n */\r\nexport function createReservationAlreadyProcessedError(\r\n  reservationId: string,\r\n  status: string\r\n): CreditError {\r\n  return new CreditError(\r\n    `Reservation ${reservationId} has already been ${status}`,\r\n    CreditErrorCode.RESERVATION_ALREADY_PROCESSED,\r\n    { reservationId, status }\r\n  );\r\n}\r\n\r\n/**\r\n * Create a user not found error\r\n */\r\nexport function createUserNotFoundError(userId: string): CreditError {\r\n  return new CreditError(\r\n    `User ${userId} not found`,\r\n    CreditErrorCode.USER_NOT_FOUND,\r\n    { userId }\r\n  );\r\n}\r\n\r\n/**\r\n * Create an invalid operation type error\r\n */\r\nexport function createInvalidOperationTypeError(\r\n  operationType: string,\r\n  validTypes: string[]\r\n): CreditError {\r\n  return new CreditError(\r\n    `Invalid operation type: ${operationType}. Valid types: ${validTypes.join(\", \")}`,\r\n    CreditErrorCode.INVALID_OPERATION_TYPE,\r\n    { operationType, validTypes }\r\n  );\r\n}\r\n","/**\r\n * Deferred execution abstraction - framework agnostic\r\n *\r\n * Provides a way to schedule work to run after the current operation completes.\r\n * Different frameworks can provide their own implementations:\r\n * - Next.js: uses `after()` from `next/server`\r\n * - Generic: uses `setImmediate()` or `setTimeout()`\r\n * - Testing: can use synchronous execution\r\n */\r\n\r\n/**\r\n * Interface for deferred execution\r\n *\r\n * Implementations should schedule the function to run asynchronously\r\n * without blocking the current operation.\r\n */\r\nexport interface DeferredExecutor {\r\n  /**\r\n   * Schedule a function to run asynchronously\r\n   *\r\n   * The function should be executed after the current operation completes.\r\n   * Errors should be caught and logged, not propagated.\r\n   *\r\n   * @param fn - Async function to execute\r\n   */\r\n  defer(fn: () => Promise<void>): void;\r\n}\r\n\r\n/**\r\n * Generic deferred executor using setImmediate/setTimeout\r\n *\r\n * Works in Node.js and browser environments.\r\n * Suitable for non-framework environments or testing.\r\n */\r\nexport const genericDeferred: DeferredExecutor = {\r\n  defer(fn: () => Promise<void>): void {\r\n    // Use setImmediate in Node.js, setTimeout as fallback\r\n    const scheduler = typeof setImmediate !== \"undefined\" ? setImmediate : setTimeout;\r\n    scheduler(() => {\r\n      fn().catch((error) => {\r\n        console.error(\"[Credits] Deferred task error:\", error);\r\n      });\r\n    });\r\n  },\r\n};\r\n\r\n/**\r\n * Synchronous deferred executor\r\n *\r\n * Executes the function immediately (fire-and-forget).\r\n * Useful for testing or when deferred execution isn't needed.\r\n */\r\nexport const synchronousDeferred: DeferredExecutor = {\r\n  defer(fn: () => Promise<void>): void {\r\n    fn().catch((error) => {\r\n      console.error(\"[Credits] Deferred task error:\", error);\r\n    });\r\n  },\r\n};\r\n\r\n/**\r\n * Create a custom deferred executor\r\n *\r\n * Allows frameworks to provide their own scheduling mechanism.\r\n *\r\n * @example\r\n * ```typescript\r\n * // Next.js adapter\r\n * import { after } from 'next/server';\r\n *\r\n * const nextJsDeferred = createDeferredExecutor((fn) => after(fn));\r\n * ```\r\n *\r\n * @param schedule - Function that schedules async work\r\n * @returns DeferredExecutor instance\r\n */\r\nexport function createDeferredExecutor(\r\n  schedule: (fn: () => Promise<void>) => void\r\n): DeferredExecutor {\r\n  return {\r\n    defer: schedule,\r\n  };\r\n}\r\n\r\n/**\r\n * No-op deferred executor\r\n *\r\n * Discards deferred work. Useful when you want to disable\r\n * background tasks (e.g., in certain test scenarios).\r\n */\r\nexport const noopDeferred: DeferredExecutor = {\r\n  defer(_fn: () => Promise<void>): void {\r\n    // Intentionally do nothing\r\n  },\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 * Core credit operations - framework agnostic\r\n *\r\n * Contains the business logic for credit operations that can be\r\n * used by any adapter or service implementation.\r\n */\r\n\r\nimport type { ICreditRepository } from \"../repository/types.js\";\r\nimport type { PortableReservation } from \"./types.js\";\r\nimport { getOperationLabel } from \"../config/index.js\";\r\n\r\n/**\r\n * Commit a reservation with journal entry\r\n *\r\n * @param repository - The credit repository\r\n * @param userId - User ID\r\n * @param reservationId - Reservation to commit\r\n */\r\nexport async function commitReservationWithJournal(\r\n  repository: ICreditRepository,\r\n  userId: string,\r\n  reservationId: string\r\n): Promise<void> {\r\n  // Get the reservation to know the amount\r\n  const reservation = await repository.getReservation(userId, reservationId);\r\n  if (!reservation) {\r\n    throw new Error(`Reservation ${reservationId} not found`);\r\n  }\r\n\r\n  // Commit the reservation atomically\r\n  await repository.commitReservationAtomic(userId, reservationId);\r\n\r\n  // Create journal entry\r\n  const credits = await repository.getUserCredits(userId);\r\n  if (credits) {\r\n    await repository.createJournalEntry({\r\n      userId,\r\n      entryType: \"debit\",\r\n      amount: reservation.amount,\r\n      balanceAfter: credits.balance,\r\n      source: \"operation_commit\",\r\n      referenceId: reservationId,\r\n      referenceType: \"reservation\",\r\n      description: `Committed ${reservation.amount} credits for ${getOperationLabel(reservation.operationType)}`,\r\n      metadata: {\r\n        operationType: reservation.operationType,\r\n      },\r\n    });\r\n  }\r\n}\r\n\r\n/**\r\n * Release a reservation with journal entry\r\n *\r\n * @param repository - The credit repository\r\n * @param userId - User ID\r\n * @param reservationId - Reservation to release\r\n */\r\nexport async function releaseReservationWithJournal(\r\n  repository: ICreditRepository,\r\n  userId: string,\r\n  reservationId: string\r\n): Promise<void> {\r\n  // Get the reservation to check its state\r\n  const reservation = await repository.getReservation(userId, reservationId);\r\n\r\n  // Release the reservation atomically\r\n  await repository.releaseReservationAtomic(userId, reservationId);\r\n\r\n  // Create journal entry only if reservation was in reserved state\r\n  if (reservation?.status === \"reserved\") {\r\n    const credits = await repository.getUserCredits(userId);\r\n    if (credits) {\r\n      await repository.createJournalEntry({\r\n        userId,\r\n        entryType: \"credit\",\r\n        amount: 0, // No actual credits returned (they were reserved, not spent)\r\n        balanceAfter: credits.balance,\r\n        source: \"operation_release\",\r\n        referenceId: reservationId,\r\n        referenceType: \"reservation\",\r\n        description: `Released ${reservation.amount} reserved credits for ${getOperationLabel(reservation.operationType)}`,\r\n        metadata: {\r\n          operationType: reservation.operationType,\r\n          amount: reservation.amount,\r\n        },\r\n      });\r\n    }\r\n  }\r\n}\r\n\r\n/**\r\n * Reserve credits for an operation\r\n *\r\n * @param repository - The credit repository\r\n * @param userId - User ID\r\n * @param amount - Credits to reserve\r\n * @param operationType - Type of operation\r\n * @param expiryMs - Reservation expiry time in milliseconds\r\n * @returns The reservation\r\n */\r\nexport async function reserveCreditsForOperation(\r\n  repository: ICreditRepository,\r\n  userId: string,\r\n  amount: number,\r\n  operationType: string,\r\n  expiryMs: number = 5 * 60 * 1000\r\n): Promise<PortableReservation> {\r\n  const expiresAt = new Date(Date.now() + expiryMs);\r\n  return repository.reserveCreditsAtomic(userId, amount, operationType, expiresAt);\r\n}\r\n","import type { SubscriptionTier, TierConfig } from \"../core/types.js\";\r\nimport {\r\n  getConfig,\r\n  getConfigOperationCost,\r\n  getConfigTierConfig,\r\n  getConfigMonthlyLimit,\r\n  getValidOperationTypes,\r\n  isValidOperationType,\r\n  getOperationLabel,\r\n} from \"./index.js\";\r\n\r\n/**\r\n * Get operation costs as a record\r\n * Returns all configured operation costs from the current config\r\n */\r\nexport function getOperationCosts(): Record<string, number> {\r\n  return { ...getConfig().operationCosts };\r\n}\r\n\r\n/**\r\n * Get the credit cost for an operation type\r\n * Uses the configuration system for dynamic costs\r\n *\r\n * @param type - Operation type (must be configured in the system)\r\n * @returns Credit cost for the operation\r\n * @throws Error if the operation type is not configured\r\n */\r\nexport function getOperationCost(type: string): number {\r\n  return getConfigOperationCost(type);\r\n}\r\n\r\n// Re-export validation helpers for convenience\r\nexport { getValidOperationTypes, isValidOperationType };\r\n\r\n/**\r\n * Get tier configuration\r\n * Uses the configuration system for dynamic configs\r\n *\r\n * @param tier - Subscription tier\r\n * @returns Tier configuration\r\n */\r\nexport function getTierConfig(tier: SubscriptionTier): TierConfig {\r\n  return getConfigTierConfig(tier);\r\n}\r\n\r\n/**\r\n * Get monthly credit limit for a tier\r\n * Returns Infinity for unlimited tier\r\n * Uses the configuration system for dynamic limits\r\n *\r\n * @param tier - Subscription tier\r\n * @returns Monthly credit limit\r\n */\r\nexport function getMonthlyLimit(tier: SubscriptionTier): number {\r\n  return getConfigMonthlyLimit(tier);\r\n}\r\n\r\n/**\r\n * Get default free credits from configuration\r\n */\r\nexport function getDefaultFreeCredits(): number {\r\n  return getConfig().defaultFreeCredits;\r\n}\r\n\r\n/**\r\n * Get reservation expiry time from configuration\r\n */\r\nexport function getReservationExpiryMs(): number {\r\n  return getConfig().reservationExpiryMs;\r\n}\r\n\r\n/**\r\n * Information about an operation's cost and display label\r\n */\r\nexport interface OperationCostInfo {\r\n  key: string;\r\n  cost: number;\r\n  label: string;\r\n}\r\n\r\n/**\r\n * Get all operation costs with their display labels.\r\n * Returns a record keyed by operation type with cost + label.\r\n */\r\nexport function getOperationCostsWithLabels(): Record<string, OperationCostInfo> {\r\n  const config = getConfig();\r\n  const result: Record<string, OperationCostInfo> = {};\r\n  for (const [key, cost] of Object.entries(config.operationCosts)) {\r\n    result[key] = {\r\n      key,\r\n      cost,\r\n      label: getOperationLabel(key),\r\n    };\r\n  }\r\n  return result;\r\n}\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","/**\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","import type { ICreditsAuthProvider, CreditsUser } from \"./types.js\";\r\n\r\n/**\r\n * Timing-safe string comparison to prevent timing attacks\r\n */\r\nfunction timingSafeEqual(a: string, b: string): boolean {\r\n  if (!a || !b) {\r\n    return false;\r\n  }\r\n\r\n  // Pad strings to same length to prevent length-based timing attacks\r\n  const maxLength = Math.max(a.length, b.length);\r\n  const paddedA = a.padEnd(maxLength);\r\n  const paddedB = b.padEnd(maxLength);\r\n\r\n  let result = 0;\r\n  for (let i = 0; i < maxLength; i++) {\r\n    result |= paddedA.charCodeAt(i) ^ paddedB.charCodeAt(i);\r\n  }\r\n\r\n  // Also check that lengths were originally equal\r\n  return result === 0 && a.length === b.length;\r\n}\r\n\r\n/**\r\n * Verify a Bearer token from an Authorization header\r\n *\r\n * @param header - The Authorization header value (e.g., \"Bearer secret123\")\r\n * @param secret - The expected secret value\r\n * @returns True if token is valid\r\n */\r\nexport function verifyBearerToken(header: string | null, secret: string): boolean {\r\n  if (!header || !secret) {\r\n    return false;\r\n  }\r\n\r\n  // Extract token from \"Bearer {token}\" format\r\n  const token = header.replace(/^Bearer\\s+/i, \"\");\r\n\r\n  // Use timing-safe comparison\r\n  return timingSafeEqual(token, secret);\r\n}\r\n\r\n/**\r\n * API Key auth provider for admin routes\r\n *\r\n * Uses Bearer token authentication for external API access\r\n * The API key is stored in CREDITS_ADMIN_API_KEY environment variable\r\n */\r\nexport class ApiKeyCreditsProvider implements ICreditsAuthProvider {\r\n  private readonly apiKey: string | undefined;\r\n  private isValidated = false;\r\n\r\n  constructor(authHeader?: string | null, apiKey?: string) {\r\n    this.apiKey = apiKey ?? process.env.CREDITS_ADMIN_API_KEY;\r\n\r\n    // Validate the provided auth header using timing-safe comparison\r\n    if (authHeader && this.apiKey) {\r\n      this.isValidated = verifyBearerToken(authHeader, this.apiKey);\r\n    }\r\n  }\r\n\r\n  async getCurrentUser(): Promise<CreditsUser | null> {\r\n    if (!this.isValidated) {\r\n      return null;\r\n    }\r\n\r\n    // API key auth returns a special \"admin\" user\r\n    return {\r\n      id: \"api-admin\",\r\n      email: \"api@admin.internal\",\r\n      name: \"API Admin\",\r\n    };\r\n  }\r\n\r\n  async verifyAdminAccess(): Promise<boolean> {\r\n    return this.isValidated;\r\n  }\r\n}\r\n\r\n/**\r\n * Verify an API key from a request header\r\n * @param authHeader - Authorization header value\r\n * @param apiKey - Optional API key to use (defaults to CREDITS_ADMIN_API_KEY env var)\r\n * @returns True if API key is valid\r\n */\r\nexport function verifyAdminApiKey(authHeader: string | null, apiKey?: string): boolean {\r\n  if (!authHeader) {\r\n    return false;\r\n  }\r\n\r\n  const expectedKey = apiKey ?? process.env.CREDITS_ADMIN_API_KEY;\r\n  if (!expectedKey) {\r\n    console.warn(\"CREDITS_ADMIN_API_KEY not configured\");\r\n    return false;\r\n  }\r\n\r\n  // Use timing-safe comparison to prevent timing attacks\r\n  return verifyBearerToken(authHeader, expectedKey);\r\n}\r\n\r\n/**\r\n * Create an API key auth provider from a request\r\n * @param request - Request object with headers\r\n * @param apiKey - Optional API key to use\r\n * @returns API key auth provider\r\n */\r\nexport function createApiKeyProvider(request: Request, apiKey?: string): ApiKeyCreditsProvider {\r\n  const authHeader = request.headers.get(\"Authorization\");\r\n  return new ApiKeyCreditsProvider(authHeader, apiKey);\r\n}\r\n","import type {\r\n  PortableUserCredits,\r\n  PortableReservation,\r\n  CreditCheckResult,\r\n  CreditOperationType,\r\n  SubscriptionTier,\r\n  PortableUsageLog,\r\n  PortableJournalEntry,\r\n  UsageHistoryEntry,\r\n  UsageHistoryResponse,\r\n} from \"../core/types.js\";\r\nimport { toDate } from \"../core/types.js\";\r\nimport type { ICreditRepository, CreateUsageLogInput, JournalEntryQuery } from \"../repository/types.js\";\r\nimport { toClientUserCredits } from \"../repository/types.js\";\r\nimport {\r\n  getConfig,\r\n  getConfigMonthlyLimit,\r\n  getOperationLabel,\r\n  isFreeTier,\r\n  isUnlimitedTier,\r\n  getDefaultTier,\r\n  getUnlimitedSentinelBalance,\r\n} from \"../config/index.js\";\r\n\r\n/**\r\n * Check if a date is past the monthly reset date\r\n */\r\nfunction isPastMonthlyReset(resetAt: unknown): boolean {\r\n  if (!resetAt) return false;\r\n  const resetDate = toDate(resetAt);\r\n  return new Date() >= resetDate;\r\n}\r\n\r\n/**\r\n * Notification callback type for low balance notifications\r\n */\r\nexport type LowBalanceNotificationCallback = (userId: string, balance: number) => Promise<void>;\r\n\r\n/**\r\n * Notification callback type for subscription expired notifications\r\n */\r\nexport type SubscriptionExpiredNotificationCallback = (userId: string, wasDowngraded: boolean) => Promise<void>;\r\n\r\n/**\r\n * Options for reserving credits.\r\n */\r\nexport interface ReserveCreditsOptions {\r\n  /**\r\n   * Time-to-live for the reservation, in milliseconds, before the cleanup\r\n   * sweep may expire it. Defaults to `config.reservationExpiryMs` (suitable\r\n   * for synchronous, in-request operations). Long-running async jobs\r\n   * (e.g. background media generation) should pass a TTL matching their\r\n   * own lifecycle so the reservation is not expired while the job is still\r\n   * legitimately in flight.\r\n   */\r\n  ttlMs?: number;\r\n}\r\n\r\n/**\r\n * Credits service with dependency injection for repository\r\n *\r\n * Provides business logic for credit operations, delegating\r\n * database operations to the injected repository.\r\n */\r\nexport class CreditsService {\r\n  private lowBalanceCallback?: LowBalanceNotificationCallback;\r\n  private subscriptionExpiredCallback?: SubscriptionExpiredNotificationCallback;\r\n\r\n  constructor(private readonly repository: ICreditRepository) {}\r\n\r\n  /**\r\n   * Set callback for low balance notifications\r\n   */\r\n  setLowBalanceCallback(callback: LowBalanceNotificationCallback): void {\r\n    this.lowBalanceCallback = callback;\r\n  }\r\n\r\n  /**\r\n   * Set callback for subscription expired notifications\r\n   */\r\n  setSubscriptionExpiredCallback(callback: SubscriptionExpiredNotificationCallback): void {\r\n    this.subscriptionExpiredCallback = callback;\r\n  }\r\n\r\n  /**\r\n   * Get user credits, performing monthly reset and subscription expiry checks if needed\r\n   *\r\n   * This method uses atomic operations to prevent race conditions:\r\n   * 1. Checks subscription expiry with grace period\r\n   * 2. Atomically performs monthly reset if needed (with optimistic locking)\r\n   *\r\n   * @param userId - User ID\r\n   * @returns User credits or null if not found\r\n   */\r\n  async getUserCredits(userId: string): Promise<PortableUserCredits | null> {\r\n    let data = await this.repository.getUserCredits(userId);\r\n\r\n    if (!data) {\r\n      return null;\r\n    }\r\n\r\n    // Step 1: Check subscription expiry (for non-free tiers)\r\n    if (!isFreeTier(data.tier) && data.subscriptionExpiresAt) {\r\n      const expiryResult = await this.repository.checkAndHandleSubscriptionExpiry(\r\n        userId,\r\n        getConfig().subscriptionGracePeriodDays\r\n      );\r\n\r\n      if (expiryResult.wasDowngraded) {\r\n        // Create journal entry for downgrade\r\n        await this.repository.createJournalEntry({\r\n          userId,\r\n          entryType: \"debit\",\r\n          amount: 0, // No credits deducted, just tier change\r\n          balanceAfter: expiryResult.credits.balance,\r\n          source: \"subscription_downgrade\",\r\n          referenceId: `downgrade-${Date.now()}`,\r\n          referenceType: \"subscription\",\r\n          description: `Subscription expired. Downgraded from ${data.tier} to free tier.`,\r\n          metadata: {\r\n            previousTier: data.tier,\r\n            previousBalance: data.balance,\r\n            newBalance: expiryResult.credits.balance,\r\n          },\r\n        });\r\n\r\n        // Trigger subscription expired notification (non-blocking)\r\n        if (this.subscriptionExpiredCallback) {\r\n          this.subscriptionExpiredCallback(userId, true).catch((error) => {\r\n            console.error(\"[Credits] Failed to send subscription expired notification:\", error);\r\n          });\r\n        }\r\n      }\r\n\r\n      // Use the potentially updated credits\r\n      data = expiryResult.credits;\r\n    }\r\n\r\n    // Step 2: Check if monthly reset is needed (use atomic operation)\r\n    if (isPastMonthlyReset(data.monthlyResetAt)) {\r\n      // Convert monthlyResetAt to a compatible type (Date or string)\r\n      const expectedResetAt = toDate(data.monthlyResetAt);\r\n      const resetResult = await this.repository.atomicMonthlyReset(\r\n        userId,\r\n        data.tier,\r\n        expectedResetAt\r\n      );\r\n\r\n      if (resetResult.wasReset) {\r\n        // Create journal entry for monthly reset\r\n        const balanceChange = resetResult.credits.balance - data.balance;\r\n        if (balanceChange !== 0) {\r\n          await this.repository.createJournalEntry({\r\n            userId,\r\n            entryType: balanceChange > 0 ? \"credit\" : \"debit\",\r\n            amount: Math.abs(balanceChange),\r\n            balanceAfter: resetResult.credits.balance,\r\n            source: \"monthly_reset\",\r\n            referenceId: `reset-${Date.now()}`,\r\n            referenceType: \"reset\",\r\n            description: `Monthly credit reset for ${data.tier} tier.`,\r\n            metadata: {\r\n              tier: data.tier,\r\n              previousBalance: data.balance,\r\n              newBalance: resetResult.credits.balance,\r\n            },\r\n          });\r\n        }\r\n      }\r\n\r\n      // Use the potentially updated credits\r\n      data = resetResult.credits;\r\n    }\r\n\r\n    return toClientUserCredits(data);\r\n  }\r\n\r\n  /**\r\n   * Initialize credits for a new user with free tier\r\n   * @param userId - User ID\r\n   * @returns Initialized user credits\r\n   */\r\n  async initializeUserCredits(userId: string): Promise<PortableUserCredits> {\r\n    const credits = await this.repository.initializeUserCredits(\r\n      userId,\r\n      getDefaultTier(),\r\n      getConfig().defaultFreeCredits\r\n    );\r\n    return toClientUserCredits(credits);\r\n  }\r\n\r\n  /**\r\n   * Get or create user credits\r\n   * Initializes with free tier if not exists\r\n   * @param userId - User ID\r\n   * @returns User credits\r\n   */\r\n  async getOrCreateUserCredits(userId: string): Promise<PortableUserCredits> {\r\n    const existing = await this.getUserCredits(userId);\r\n    if (existing) {\r\n      return existing;\r\n    }\r\n    return this.initializeUserCredits(userId);\r\n  }\r\n\r\n  /**\r\n   * Check if user has sufficient credits for an operation\r\n   * @param userId - User ID\r\n   * @param requiredCredits - Credits required\r\n   * @returns Credit check result\r\n   */\r\n  async checkCredits(userId: string, requiredCredits: number): Promise<CreditCheckResult> {\r\n    const credits = await this.getOrCreateUserCredits(userId);\r\n\r\n    // Available = balance + bonusCredits - reserved\r\n    const totalBalance = credits.balance + credits.bonusCredits;\r\n    const available = totalBalance - credits.reserved;\r\n    const hasCredits = available >= requiredCredits;\r\n\r\n    return {\r\n      hasCredits,\r\n      balance: totalBalance,\r\n      required: requiredCredits,\r\n      shortfall: hasCredits ? 0 : requiredCredits - available,\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Reserve credits for an operation (phase 1 of two-phase commit)\r\n   * Creates a reservation and locks the credits\r\n   * @param userId - User ID\r\n   * @param amount - Credits to reserve\r\n   * @param operationType - Operation type for tracking\r\n   * @param options - Optional settings (e.g. a custom `ttlMs` for long-running async jobs)\r\n   * @returns Reservation object\r\n   * @throws Error if insufficient credits\r\n   */\r\n  async reserveCredits(\r\n    userId: string,\r\n    amount: number,\r\n    operationType: CreditOperationType,\r\n    options?: ReserveCreditsOptions\r\n  ): Promise<PortableReservation> {\r\n    const ttlMs = options?.ttlMs ?? getConfig().reservationExpiryMs;\r\n    const expiresAt = new Date(Date.now() + ttlMs);\r\n    return this.repository.reserveCreditsAtomic(userId, amount, operationType, expiresAt);\r\n  }\r\n\r\n  /**\r\n   * Commit a reservation (phase 2 of two-phase commit - success)\r\n   * Deducts credits and marks reservation as committed\r\n   * Also triggers low balance notifications if balance drops below threshold\r\n   */\r\n  async commitCredits(userId: string, reservationId: string): Promise<void> {\r\n    // Get the reservation to know the amount\r\n    const reservation = await this.repository.getReservation(userId, reservationId);\r\n    if (!reservation) {\r\n      throw new Error(`Reservation ${reservationId} not found`);\r\n    }\r\n\r\n    // Idempotent: a re-delivered commit for an already-committed reservation\r\n    // is a no-op. The balance + journal were applied by the first commit; the\r\n    // atomic layer also guards the balance, this just avoids a duplicate\r\n    // journal entry on the happy retry path.\r\n    if (reservation.status === \"committed\") {\r\n      return;\r\n    }\r\n\r\n    // Commit the reservation atomically\r\n    await this.repository.commitReservationAtomic(userId, reservationId);\r\n\r\n    // Create journal entry\r\n    const credits = await this.repository.getUserCredits(userId);\r\n    if (credits) {\r\n      await this.repository.createJournalEntry({\r\n        userId,\r\n        entryType: \"debit\",\r\n        amount: reservation.amount,\r\n        balanceAfter: credits.balance,\r\n        source: \"operation_commit\",\r\n        referenceId: reservationId,\r\n        referenceType: \"reservation\",\r\n        description: `Committed ${reservation.amount} credits for ${getOperationLabel(reservation.operationType)}`,\r\n        metadata: {\r\n          operationType: reservation.operationType,\r\n        },\r\n      });\r\n\r\n      // Trigger low balance notification (non-blocking)\r\n      if (this.lowBalanceCallback) {\r\n        this.lowBalanceCallback(userId, credits.balance).catch((error) => {\r\n          console.error(\"[Credits] Failed to send low balance notification:\", error);\r\n        });\r\n      }\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Release a reservation (phase 2 of two-phase commit - failure)\r\n   * Returns reserved credits and marks reservation as released\r\n   */\r\n  async releaseCredits(userId: string, reservationId: string): Promise<void> {\r\n    // Get the reservation to check its state\r\n    const reservation = await this.repository.getReservation(userId, reservationId);\r\n\r\n    // Release the reservation atomically\r\n    await this.repository.releaseReservationAtomic(userId, reservationId);\r\n\r\n    // Create journal entry only if reservation was in reserved state\r\n    if (reservation?.status === \"reserved\") {\r\n      const credits = await this.repository.getUserCredits(userId);\r\n      if (credits) {\r\n        await this.repository.createJournalEntry({\r\n          userId,\r\n          entryType: \"credit\",\r\n          amount: 0, // No actual credits returned (they were reserved, not spent)\r\n          balanceAfter: credits.balance,\r\n          source: \"operation_release\",\r\n          referenceId: reservationId,\r\n          referenceType: \"reservation\",\r\n          description: `Released ${reservation.amount} reserved credits for ${getOperationLabel(reservation.operationType)}`,\r\n          metadata: {\r\n            operationType: reservation.operationType,\r\n            amount: reservation.amount,\r\n          },\r\n        });\r\n      }\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Log usage for audit trail\r\n   * @param log - Usage log data\r\n   */\r\n  async logUsage(log: Omit<PortableUsageLog, \"id\" | \"createdAt\">): Promise<void> {\r\n    await this.repository.logUsage(log as CreateUsageLogInput);\r\n  }\r\n\r\n  /**\r\n   * Add credits to user account (for purchases, bonuses, etc.)\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  async addCredits(\r\n    userId: string,\r\n    amount: number,\r\n    description: string,\r\n    paymentRef?: string\r\n  ): Promise<void> {\r\n    return this.repository.addCreditsAtomic(userId, amount, description, paymentRef);\r\n  }\r\n\r\n  /**\r\n   * Update user subscription tier\r\n   * @param userId - User ID\r\n   * @param tier - New subscription tier\r\n   * @param expiresAt - Subscription expiry date (optional)\r\n   */\r\n  async updateTier(\r\n    userId: string,\r\n    tier: SubscriptionTier,\r\n    expiresAt?: Date\r\n  ): Promise<void> {\r\n    const monthlyLimit = getConfigMonthlyLimit(tier);\r\n    const unlimited = isUnlimitedTier(tier);\r\n    const free = isFreeTier(tier);\r\n\r\n    await this.repository.updateUserTier(userId, {\r\n      tier,\r\n      // Stored monthlyLimit uses the 0-means-unlimited convention.\r\n      monthlyLimit: unlimited ? 0 : monthlyLimit,\r\n      // Reset balance to new tier limit if upgrading\r\n      balance: !free\r\n        ? (unlimited ? getUnlimitedSentinelBalance() : monthlyLimit)\r\n        : undefined,\r\n      monthlyUsed: !free ? 0 : undefined,\r\n      subscriptionExpiresAt: expiresAt ? expiresAt.toISOString() : null,\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Get usage logs with optional filtering\r\n   * @param userId - Optional user ID filter\r\n   * @param limit - Max results\r\n   * @param offset - Skip results\r\n   * @returns List of usage logs\r\n   */\r\n  async getUsageLogs(\r\n    userId?: string,\r\n    limit = 50,\r\n    offset = 0\r\n  ): Promise<PortableUsageLog[]> {\r\n    return this.repository.getUsageLogs({ userId, limit, offset });\r\n  }\r\n\r\n  /**\r\n   * Get user-friendly usage history\r\n   * Combines journal entries into a user-facing format\r\n   *\r\n   * @param userId - User ID\r\n   * @param limit - Max results per page\r\n   * @param offset - Skip results for pagination\r\n   * @returns Paginated usage history response\r\n   */\r\n  async getUsageHistory(\r\n    userId: string,\r\n    limit = 20,\r\n    offset = 0\r\n  ): Promise<UsageHistoryResponse> {\r\n    // Get journal entries\r\n    const [entries, total] = await Promise.all([\r\n      this.repository.getJournalEntries({ userId, limit, offset }),\r\n      this.repository.getJournalEntriesCount({ userId }),\r\n    ]);\r\n\r\n    // Convert journal entries to user-friendly format\r\n    const historyEntries: UsageHistoryEntry[] = entries.map((entry) => {\r\n      const type = this.mapSourceToHistoryType(entry.source);\r\n      const creditsChange = entry.entryType === \"credit\" ? entry.amount : -entry.amount;\r\n\r\n      return {\r\n        id: entry.id,\r\n        type,\r\n        creditsChange,\r\n        balanceAfter: entry.balanceAfter,\r\n        description: entry.description,\r\n        createdAt: typeof entry.createdAt === \"string\"\r\n          ? entry.createdAt\r\n          : toDate(entry.createdAt).toISOString(),\r\n      };\r\n    });\r\n\r\n    return {\r\n      entries: historyEntries,\r\n      pagination: {\r\n        total,\r\n        limit,\r\n        offset,\r\n        hasMore: offset + entries.length < total,\r\n      },\r\n    };\r\n  }\r\n\r\n  /**\r\n   * Map journal entry source to user-friendly history type\r\n   */\r\n  private mapSourceToHistoryType(\r\n    source: PortableJournalEntry[\"source\"]\r\n  ): UsageHistoryEntry[\"type\"] {\r\n    switch (source) {\r\n      case \"operation_commit\":\r\n      case \"reservation_expired\":\r\n        return \"usage\";\r\n      case \"purchase\":\r\n        return \"purchase\";\r\n      case \"subscription_grant\":\r\n      case \"subscription_upgrade\":\r\n      case \"bonus\":\r\n        return \"bonus\";\r\n      case \"monthly_reset\":\r\n        return \"reset\";\r\n      case \"refund\":\r\n      case \"operation_release\":\r\n        return \"refund\";\r\n      case \"admin_adjustment\":\r\n      case \"subscription_downgrade\":\r\n      case \"expiry\":\r\n      default:\r\n        return \"adjustment\";\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Get journal entries directly (for admin or debugging)\r\n   * @param query - Journal entry query parameters\r\n   * @returns List of journal entries\r\n   */\r\n  async getJournalEntries(query: JournalEntryQuery): Promise<PortableJournalEntry[]> {\r\n    return this.repository.getJournalEntries(query);\r\n  }\r\n\r\n  /**\r\n   * Get the underlying repository (for advanced use cases)\r\n   */\r\n  getRepository(): ICreditRepository {\r\n    return this.repository;\r\n  }\r\n}\r\n\r\n/**\r\n * Create a credits service with a repository\r\n */\r\nexport function createCreditsService(repository: ICreditRepository): CreditsService {\r\n  return new CreditsService(repository);\r\n}\r\n","/**\r\n * Generic credits adapter - framework agnostic\r\n *\r\n * This adapter uses standard JavaScript APIs and works in any\r\n * JavaScript environment (Node.js, browser, etc.).\r\n */\r\n\r\nimport type { PortableReservation, WithCreditsOptions } from \"../core/types.js\";\r\nimport type { ActionResult, CreditsAdapter, CreditsAdapterConfig, CreditActionHandler } from \"./types.js\";\r\nimport {\r\n  commitReservationWithJournal,\r\n  releaseReservationWithJournal,\r\n  reserveCreditsForOperation,\r\n} from \"../core/operations.js\";\r\n\r\n/**\r\n * Generate a unique request ID\r\n */\r\nfunction generateRequestId(): string {\r\n  const timestamp = Date.now();\r\n  const randomPart = Math.random().toString(36).substring(2, 14);\r\n  return `req_${timestamp}_${randomPart}`;\r\n}\r\n\r\n/**\r\n * Create a generic credits adapter\r\n *\r\n * This adapter:\r\n * - Authenticates users via the provided auth provider\r\n * - Reserves credits before action execution\r\n * - Commits credits on success, releases on failure\r\n * - Logs usage asynchronously via the deferred executor\r\n */\r\nexport function createGenericAdapter(config: CreditsAdapterConfig): CreditsAdapter {\r\n  const { repository, authProvider, deferred, operationCosts } = config;\r\n\r\n  function getOperationCost(operationType: string): number {\r\n    const cost = operationCosts[operationType];\r\n    if (cost === undefined) {\r\n      throw new Error(\r\n        `Unknown operation type: \"${operationType}\". ` +\r\n        `Configured types: ${Object.keys(operationCosts).join(\", \")}`\r\n      );\r\n    }\r\n    return cost;\r\n  }\r\n\r\n  function logUsage(params: {\r\n    userId: string;\r\n    operationType: string;\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  }): void {\r\n    deferred.defer(async () => {\r\n      await repository.logUsage({\r\n        userId: params.userId,\r\n        operationType: params.operationType,\r\n        provider: \"gemini\",\r\n        creditsUsed: params.creditsUsed,\r\n        success: params.success,\r\n        errorMessage: params.errorMessage,\r\n        resourceId: params.resourceId,\r\n        resourceType: params.resourceType,\r\n        requestId: params.requestId,\r\n      });\r\n    });\r\n  }\r\n\r\n  return {\r\n    withCredits<TInput, TOutput>(\r\n      options: WithCreditsOptions,\r\n      handler: CreditActionHandler<TInput, TOutput>\r\n    ): (data: TInput) => Promise<ActionResult<TOutput>> {\r\n      return async (data: TInput): Promise<ActionResult<TOutput>> => {\r\n        const requestId = generateRequestId();\r\n\r\n        // Authenticate\r\n        const user = await authProvider.getCurrentUser();\r\n        if (!user?.id) {\r\n          return { success: false, error: \"Authentication required\" };\r\n        }\r\n\r\n        // Handle preview mode - skip credit handling entirely\r\n        if (isPreviewMode(data)) {\r\n          const dummyReservation = createDummyReservation(user.id, options.operationType);\r\n          return handler(user, data, dummyReservation);\r\n        }\r\n\r\n        // Calculate cost and reserve credits\r\n        let reservation: PortableReservation;\r\n        try {\r\n          const cost = options.customCost ?? getOperationCost(options.operationType);\r\n          reservation = await reserveCreditsForOperation(\r\n            repository,\r\n            user.id,\r\n            cost,\r\n            options.operationType\r\n          );\r\n        } catch (error) {\r\n          const message = error instanceof Error ? error.message : \"Failed to reserve credits\";\r\n          logUsage({\r\n            userId: user.id,\r\n            operationType: options.operationType,\r\n            creditsUsed: 0,\r\n            success: false,\r\n            errorMessage: message,\r\n            resourceId: options.resourceId,\r\n            resourceType: options.resourceType,\r\n            requestId,\r\n          });\r\n          return { success: false, error: message };\r\n        }\r\n\r\n        // Execute action\r\n        try {\r\n          const result = await handler(user, data, reservation);\r\n\r\n          if (result.success) {\r\n            await commitReservationWithJournal(repository, user.id, reservation.id);\r\n            logUsage({\r\n              userId: user.id,\r\n              operationType: options.operationType,\r\n              creditsUsed: reservation.amount,\r\n              success: true,\r\n              resourceId: options.resourceId,\r\n              resourceType: options.resourceType,\r\n              requestId,\r\n            });\r\n          } else {\r\n            await releaseReservationWithJournal(repository, user.id, reservation.id);\r\n            logUsage({\r\n              userId: user.id,\r\n              operationType: options.operationType,\r\n              creditsUsed: 0,\r\n              success: false,\r\n              errorMessage: result.error,\r\n              resourceId: options.resourceId,\r\n              resourceType: options.resourceType,\r\n              requestId,\r\n            });\r\n          }\r\n\r\n          return result;\r\n        } catch (error) {\r\n          const message = error instanceof Error ? error.message : \"An unexpected error occurred\";\r\n          await releaseReservationWithJournal(repository, user.id, reservation.id);\r\n          logUsage({\r\n            userId: user.id,\r\n            operationType: options.operationType,\r\n            creditsUsed: 0,\r\n            success: false,\r\n            errorMessage: message,\r\n            resourceId: options.resourceId,\r\n            resourceType: options.resourceType,\r\n            requestId,\r\n          });\r\n          console.error(\"Action error:\", error);\r\n          return { success: false, error: message };\r\n        }\r\n      };\r\n    },\r\n  };\r\n}\r\n\r\nfunction isPreviewMode(data: unknown): boolean {\r\n  return (\r\n    typeof data === \"object\" &&\r\n    data !== null &&\r\n    \"preview\" in data &&\r\n    (data as Record<string, unknown>).preview === true\r\n  );\r\n}\r\n\r\nfunction createDummyReservation(userId: string, operationType: string): PortableReservation {\r\n  return {\r\n    id: \"preview-mode\",\r\n    userId,\r\n    amount: 0,\r\n    operationType,\r\n    status: \"released\",\r\n    createdAt: new Date().toISOString(),\r\n    expiresAt: new Date().toISOString(),\r\n  };\r\n}\r\n","import { getConfig } from \"../config/index.js\";\r\n\r\n/**\r\n * Notification event types\r\n */\r\nexport type CreditNotificationEvent =\r\n  | {\r\n      type: \"low_balance\";\r\n      userId: string;\r\n      balance: number;\r\n      threshold: number;\r\n    }\r\n  | {\r\n      type: \"balance_depleted\";\r\n      userId: string;\r\n    }\r\n  | {\r\n      type: \"subscription_expiring\";\r\n      userId: string;\r\n      expiresAt: Date;\r\n      daysRemaining: number;\r\n    }\r\n  | {\r\n      type: \"subscription_expired\";\r\n      userId: string;\r\n      wasDowngraded: boolean;\r\n    };\r\n\r\n/**\r\n * Notification handler interface\r\n * Implementations can send emails, push notifications, etc.\r\n */\r\nexport interface ICreditNotificationHandler {\r\n  /**\r\n   * Handle a notification event\r\n   * @param event - The notification event\r\n   */\r\n  handleNotification(event: CreditNotificationEvent): Promise<void>;\r\n}\r\n\r\n/**\r\n * Notification cooldown tracker\r\n * Prevents spam by tracking last notification time per user per event type\r\n */\r\ninterface NotificationCooldownState {\r\n  lastLowBalanceNotification?: Date;\r\n  lastDepletedNotification?: Date;\r\n  lastExpiringNotification?: Date;\r\n}\r\n\r\n/**\r\n * In-memory cooldown tracker\r\n * In production, this should be stored in a persistent store\r\n */\r\nconst cooldownState = new Map<string, NotificationCooldownState>();\r\n\r\n/**\r\n * Check if a notification is allowed (not in cooldown)\r\n */\r\nfunction isNotificationAllowed(\r\n  userId: string,\r\n  eventType: \"lowBalance\" | \"depleted\" | \"expiring\"\r\n): boolean {\r\n  const state = cooldownState.get(userId);\r\n  if (!state) return true;\r\n\r\n  const now = Date.now();\r\n  let lastNotification: Date | undefined;\r\n\r\n  switch (eventType) {\r\n    case \"lowBalance\":\r\n      lastNotification = state.lastLowBalanceNotification;\r\n      break;\r\n    case \"depleted\":\r\n      lastNotification = state.lastDepletedNotification;\r\n      break;\r\n    case \"expiring\":\r\n      lastNotification = state.lastExpiringNotification;\r\n      break;\r\n  }\r\n\r\n  if (!lastNotification) return true;\r\n  return now - lastNotification.getTime() > getConfig().lowBalanceNotificationCooldownHours * 60 * 60 * 1000;\r\n}\r\n\r\n/**\r\n * Record that a notification was sent\r\n */\r\nfunction recordNotification(\r\n  userId: string,\r\n  eventType: \"lowBalance\" | \"depleted\" | \"expiring\"\r\n): void {\r\n  const state = cooldownState.get(userId) || {};\r\n  const now = new Date();\r\n\r\n  switch (eventType) {\r\n    case \"lowBalance\":\r\n      state.lastLowBalanceNotification = now;\r\n      break;\r\n    case \"depleted\":\r\n      state.lastDepletedNotification = now;\r\n      break;\r\n    case \"expiring\":\r\n      state.lastExpiringNotification = now;\r\n      break;\r\n  }\r\n\r\n  cooldownState.set(userId, state);\r\n}\r\n\r\n/**\r\n * Default notification handlers registry\r\n * Handlers can be registered at application startup\r\n */\r\nconst notificationHandlers: ICreditNotificationHandler[] = [];\r\n\r\n/**\r\n * Register a notification handler\r\n */\r\nexport function registerNotificationHandler(\r\n  handler: ICreditNotificationHandler\r\n): void {\r\n  notificationHandlers.push(handler);\r\n}\r\n\r\n/**\r\n * Clear all notification handlers (for testing)\r\n */\r\nexport function clearNotificationHandlers(): void {\r\n  notificationHandlers.length = 0;\r\n}\r\n\r\n/**\r\n * Clear cooldown state (for testing)\r\n */\r\nexport function clearCooldownState(): void {\r\n  cooldownState.clear();\r\n}\r\n\r\n/**\r\n * Dispatch a notification event to all handlers\r\n * Runs asynchronously without blocking\r\n */\r\nasync function dispatchNotification(\r\n  event: CreditNotificationEvent\r\n): Promise<void> {\r\n  if (notificationHandlers.length === 0) {\r\n    // No handlers registered - log for debugging\r\n    console.debug(`[Credits] No notification handlers registered for: ${event.type}`);\r\n    return;\r\n  }\r\n\r\n  // Run handlers in parallel\r\n  await Promise.allSettled(\r\n    notificationHandlers.map((handler) =>\r\n      handler.handleNotification(event).catch((error) => {\r\n        console.error(`[Credits] Notification handler error:`, error);\r\n      })\r\n    )\r\n  );\r\n}\r\n\r\n/**\r\n * Check and trigger low balance notification if needed\r\n * Call this after commit operations\r\n *\r\n * @param userId - User ID\r\n * @param newBalance - Balance after the operation\r\n * @param threshold - Custom threshold (defaults to 10)\r\n */\r\nexport async function checkAndNotifyLowBalance(\r\n  userId: string,\r\n  newBalance: number,\r\n  threshold = getConfig().lowBalanceThreshold\r\n): Promise<void> {\r\n  // Check if balance is depleted\r\n  if (newBalance <= 0 && isNotificationAllowed(userId, \"depleted\")) {\r\n    recordNotification(userId, \"depleted\");\r\n    await dispatchNotification({\r\n      type: \"balance_depleted\",\r\n      userId,\r\n    });\r\n    return; // Don't also send low balance notification\r\n  }\r\n\r\n  // Check if balance is below threshold\r\n  if (newBalance > 0 && newBalance <= threshold && isNotificationAllowed(userId, \"lowBalance\")) {\r\n    recordNotification(userId, \"lowBalance\");\r\n    await dispatchNotification({\r\n      type: \"low_balance\",\r\n      userId,\r\n      balance: newBalance,\r\n      threshold,\r\n    });\r\n  }\r\n}\r\n\r\n/**\r\n * Trigger subscription expiring notification\r\n *\r\n * @param userId - User ID\r\n * @param expiresAt - When subscription expires\r\n * @param daysRemaining - Days until expiry\r\n */\r\nexport async function notifySubscriptionExpiring(\r\n  userId: string,\r\n  expiresAt: Date,\r\n  daysRemaining: number\r\n): Promise<void> {\r\n  if (!isNotificationAllowed(userId, \"expiring\")) {\r\n    return;\r\n  }\r\n\r\n  recordNotification(userId, \"expiring\");\r\n  await dispatchNotification({\r\n    type: \"subscription_expiring\",\r\n    userId,\r\n    expiresAt,\r\n    daysRemaining,\r\n  });\r\n}\r\n\r\n/**\r\n * Trigger subscription expired notification\r\n *\r\n * @param userId - User ID\r\n * @param wasDowngraded - Whether the user was downgraded to free tier\r\n */\r\nexport async function notifySubscriptionExpired(\r\n  userId: string,\r\n  wasDowngraded: boolean\r\n): Promise<void> {\r\n  await dispatchNotification({\r\n    type: \"subscription_expired\",\r\n    userId,\r\n    wasDowngraded,\r\n  });\r\n}\r\n\r\n/**\r\n * Console notification handler for development/testing\r\n * Logs notifications to console\r\n */\r\nexport class ConsoleNotificationHandler implements ICreditNotificationHandler {\r\n  async handleNotification(event: CreditNotificationEvent): Promise<void> {\r\n    switch (event.type) {\r\n      case \"low_balance\":\r\n        console.log(\r\n          `[Credits Notification] Low balance alert for user ${event.userId}: ` +\r\n          `${event.balance} credits remaining (threshold: ${event.threshold})`\r\n        );\r\n        break;\r\n      case \"balance_depleted\":\r\n        console.log(\r\n          `[Credits Notification] Balance depleted for user ${event.userId}`\r\n        );\r\n        break;\r\n      case \"subscription_expiring\":\r\n        console.log(\r\n          `[Credits Notification] Subscription expiring for user ${event.userId}: ` +\r\n          `${event.daysRemaining} days remaining`\r\n        );\r\n        break;\r\n      case \"subscription_expired\":\r\n        console.log(\r\n          `[Credits Notification] Subscription expired for user ${event.userId}: ` +\r\n          `downgraded=${event.wasDowngraded}`\r\n        );\r\n        break;\r\n    }\r\n  }\r\n}\r\n","/**\r\n * SDK types for the credits system\r\n *\r\n * These types are used by the client SDKs for REST API consumption.\r\n */\r\n\r\n// Re-export core types for SDK consumers\r\nexport type {\r\n  PortableUserCredits as UserCredits,\r\n  PortableReservation as CreditReservation,\r\n  CreditCheckResult,\r\n  SubscriptionTier,\r\n} from \"../core/types.js\";\r\n\r\n/**\r\n * Configuration for CreditsClient\r\n */\r\nexport interface CreditsClientConfig {\r\n  /** Base URL of the credits API (e.g., \"https://api.example.com\") */\r\n  baseUrl: string;\r\n  /** Function to get the auth token for session-based auth */\r\n  getAuthToken?: () => Promise<string>;\r\n  /** Optional custom fetch function for testing */\r\n  fetch?: typeof fetch;\r\n}\r\n\r\n/**\r\n * Configuration for AdminCreditsClient\r\n */\r\nexport interface AdminCreditsClientConfig {\r\n  /** Base URL of the credits API */\r\n  baseUrl: string;\r\n  /** Admin API key */\r\n  apiKey: string;\r\n  /** Optional custom fetch function for testing */\r\n  fetch?: typeof fetch;\r\n}\r\n\r\n/**\r\n * Result of a credit reservation\r\n */\r\nexport interface ReservationResult {\r\n  /** The reservation ID */\r\n  reservationId: string;\r\n  /** Amount of credits reserved */\r\n  amount: number;\r\n  /** When the reservation expires (ISO 8601) */\r\n  expiresAt: string;\r\n}\r\n\r\n/**\r\n * Pagination options for API requests\r\n */\r\nexport interface PaginationOptions {\r\n  /** Page number (1-indexed) */\r\n  page?: number;\r\n  /** Number of items per page */\r\n  limit?: number;\r\n}\r\n\r\n/**\r\n * Usage history entry\r\n */\r\nexport interface UsageHistoryEntry {\r\n  /** Unique ID of the usage log */\r\n  id: string;\r\n  /** Type of operation */\r\n  operationType: string;\r\n  /** AI provider used */\r\n  provider: string;\r\n  /** Credits consumed */\r\n  creditsUsed: number;\r\n  /** Whether the operation was successful */\r\n  success: boolean;\r\n  /** Error message if unsuccessful */\r\n  errorMessage?: string;\r\n  /** ID of the related resource */\r\n  resourceId?: string;\r\n  /** Type of the related resource */\r\n  resourceType?: string;\r\n  /** When the operation occurred (ISO 8601) */\r\n  createdAt: string;\r\n}\r\n\r\n/**\r\n * Response from usage history endpoint\r\n */\r\nexport interface UsageHistoryResponse {\r\n  /** List of usage entries */\r\n  entries: UsageHistoryEntry[];\r\n  /** Whether there are more results */\r\n  hasMore: boolean;\r\n  /** Total count (if available) */\r\n  total?: number;\r\n}\r\n\r\n/**\r\n * Generic API response wrapper\r\n */\r\nexport interface ApiResponse<T> {\r\n  success: boolean;\r\n  data?: T;\r\n  error?: {\r\n    code: string;\r\n    message: string;\r\n    details?: unknown;\r\n  };\r\n}\r\n\r\n/**\r\n * SDK error codes\r\n */\r\nexport const SDKErrorCode = {\r\n  NETWORK_ERROR: \"NETWORK_ERROR\",\r\n  AUTHENTICATION_ERROR: \"AUTHENTICATION_ERROR\",\r\n  AUTHORIZATION_ERROR: \"AUTHORIZATION_ERROR\",\r\n  INSUFFICIENT_CREDITS: \"INSUFFICIENT_CREDITS\",\r\n  RESERVATION_NOT_FOUND: \"RESERVATION_NOT_FOUND\",\r\n  RESERVATION_EXPIRED: \"RESERVATION_EXPIRED\",\r\n  VALIDATION_ERROR: \"VALIDATION_ERROR\",\r\n  SERVER_ERROR: \"SERVER_ERROR\",\r\n  UNKNOWN_ERROR: \"UNKNOWN_ERROR\",\r\n} as const;\r\n\r\nexport type SDKErrorCodeType = (typeof SDKErrorCode)[keyof typeof SDKErrorCode];\r\n","/**\r\n * SDK error classes\r\n *\r\n * Provides typed error handling for SDK operations.\r\n */\r\n\r\nimport { SDKErrorCode, type SDKErrorCodeType } from \"./types.js\";\r\n\r\n/**\r\n * Base error class for SDK errors\r\n */\r\nexport class CreditsSDKError extends Error {\r\n  public readonly code: SDKErrorCodeType;\r\n  public readonly details?: Record<string, unknown>;\r\n  public readonly statusCode?: number;\r\n\r\n  constructor(\r\n    message: string,\r\n    code: SDKErrorCodeType,\r\n    options?: {\r\n      details?: Record<string, unknown>;\r\n      statusCode?: number;\r\n      cause?: Error;\r\n    }\r\n  ) {\r\n    super(message, { cause: options?.cause });\r\n    this.name = \"CreditsSDKError\";\r\n    this.code = code;\r\n    this.details = options?.details;\r\n    this.statusCode = options?.statusCode;\r\n  }\r\n\r\n  /**\r\n   * Check if this is a specific error type\r\n   */\r\n  is(code: SDKErrorCodeType): boolean {\r\n    return this.code === code;\r\n  }\r\n\r\n  /**\r\n   * Create a JSON representation for logging\r\n   */\r\n  toJSON(): Record<string, unknown> {\r\n    return {\r\n      name: this.name,\r\n      message: this.message,\r\n      code: this.code,\r\n      details: this.details,\r\n      statusCode: this.statusCode,\r\n    };\r\n  }\r\n}\r\n\r\n/**\r\n * Error thrown when a network request fails\r\n */\r\nexport class NetworkError extends CreditsSDKError {\r\n  constructor(message: string, cause?: Error) {\r\n    super(message, SDKErrorCode.NETWORK_ERROR, { cause });\r\n    this.name = \"NetworkError\";\r\n  }\r\n}\r\n\r\n/**\r\n * Error thrown when authentication fails\r\n */\r\nexport class AuthenticationError extends CreditsSDKError {\r\n  constructor(message: string = \"Authentication required\") {\r\n    super(message, SDKErrorCode.AUTHENTICATION_ERROR, { statusCode: 401 });\r\n    this.name = \"AuthenticationError\";\r\n  }\r\n}\r\n\r\n/**\r\n * Error thrown when authorization fails\r\n */\r\nexport class AuthorizationError extends CreditsSDKError {\r\n  constructor(message: string = \"Access denied\") {\r\n    super(message, SDKErrorCode.AUTHORIZATION_ERROR, { statusCode: 403 });\r\n    this.name = \"AuthorizationError\";\r\n  }\r\n}\r\n\r\n/**\r\n * Error thrown when credits are insufficient\r\n */\r\nexport class InsufficientCreditsError extends CreditsSDKError {\r\n  public readonly available: number;\r\n  public readonly required: number;\r\n\r\n  constructor(available: number, required: number) {\r\n    super(\r\n      `Insufficient credits: available ${available}, required ${required}`,\r\n      SDKErrorCode.INSUFFICIENT_CREDITS,\r\n      {\r\n        statusCode: 402,\r\n        details: { available, required },\r\n      }\r\n    );\r\n    this.name = \"InsufficientCreditsError\";\r\n    this.available = available;\r\n    this.required = required;\r\n  }\r\n}\r\n\r\n/**\r\n * Error thrown when a reservation is not found\r\n */\r\nexport class ReservationNotFoundError extends CreditsSDKError {\r\n  public readonly reservationId: string;\r\n\r\n  constructor(reservationId: string) {\r\n    super(\r\n      `Reservation not found: ${reservationId}`,\r\n      SDKErrorCode.RESERVATION_NOT_FOUND,\r\n      {\r\n        statusCode: 404,\r\n        details: { reservationId },\r\n      }\r\n    );\r\n    this.name = \"ReservationNotFoundError\";\r\n    this.reservationId = reservationId;\r\n  }\r\n}\r\n\r\n/**\r\n * Error thrown when a reservation has expired\r\n */\r\nexport class ReservationExpiredError extends CreditsSDKError {\r\n  public readonly reservationId: string;\r\n\r\n  constructor(reservationId: string) {\r\n    super(\r\n      `Reservation expired: ${reservationId}`,\r\n      SDKErrorCode.RESERVATION_EXPIRED,\r\n      {\r\n        statusCode: 410,\r\n        details: { reservationId },\r\n      }\r\n    );\r\n    this.name = \"ReservationExpiredError\";\r\n    this.reservationId = reservationId;\r\n  }\r\n}\r\n\r\n/**\r\n * Error thrown for validation failures\r\n */\r\nexport class ValidationError extends CreditsSDKError {\r\n  public readonly field?: string;\r\n\r\n  constructor(message: string, field?: string) {\r\n    super(message, SDKErrorCode.VALIDATION_ERROR, {\r\n      statusCode: 400,\r\n      details: field ? { field } : undefined,\r\n    });\r\n    this.name = \"ValidationError\";\r\n    this.field = field;\r\n  }\r\n}\r\n\r\n/**\r\n * Error thrown for server errors\r\n */\r\nexport class ServerError extends CreditsSDKError {\r\n  constructor(message: string = \"Internal server error\") {\r\n    super(message, SDKErrorCode.SERVER_ERROR, { statusCode: 500 });\r\n    this.name = \"ServerError\";\r\n  }\r\n}\r\n\r\n/**\r\n * Parse an API error response into the appropriate SDK error\r\n */\r\nexport function parseApiError(\r\n  statusCode: number,\r\n  body: { error?: { code?: string; message?: string; details?: unknown } }\r\n): CreditsSDKError {\r\n  const errorCode = body.error?.code || \"\";\r\n  const errorMessage = body.error?.message || \"An error occurred\";\r\n  const details = body.error?.details as Record<string, unknown> | undefined;\r\n\r\n  switch (statusCode) {\r\n    case 401:\r\n      return new AuthenticationError(errorMessage);\r\n    case 403:\r\n      return new AuthorizationError(errorMessage);\r\n    case 402:\r\n      // Parse insufficient credits details\r\n      if (details?.available !== undefined && details?.required !== undefined) {\r\n        return new InsufficientCreditsError(\r\n          details.available as number,\r\n          details.required as number\r\n        );\r\n      }\r\n      return new CreditsSDKError(errorMessage, SDKErrorCode.INSUFFICIENT_CREDITS, {\r\n        statusCode,\r\n        details,\r\n      });\r\n    case 404:\r\n      if (errorCode === \"RESERVATION_NOT_FOUND\" && details?.reservationId) {\r\n        return new ReservationNotFoundError(details.reservationId as string);\r\n      }\r\n      return new CreditsSDKError(errorMessage, SDKErrorCode.RESERVATION_NOT_FOUND, {\r\n        statusCode,\r\n        details,\r\n      });\r\n    case 410:\r\n      if (errorCode === \"RESERVATION_EXPIRED\" && details?.reservationId) {\r\n        return new ReservationExpiredError(details.reservationId as string);\r\n      }\r\n      return new CreditsSDKError(errorMessage, SDKErrorCode.RESERVATION_EXPIRED, {\r\n        statusCode,\r\n        details,\r\n      });\r\n    case 400:\r\n    case 422:\r\n      return new ValidationError(errorMessage, details?.field as string | undefined);\r\n    case 500:\r\n    default:\r\n      if (statusCode >= 500) {\r\n        return new ServerError(errorMessage);\r\n      }\r\n      return new CreditsSDKError(errorMessage, SDKErrorCode.UNKNOWN_ERROR, {\r\n        statusCode,\r\n        details,\r\n      });\r\n  }\r\n}\r\n","/**\r\n * CreditsClient - REST API client for the credits system\r\n *\r\n * Provides a type-safe interface for consuming credits API endpoints\r\n * from external services or other applications.\r\n *\r\n * @example\r\n * ```typescript\r\n * const client = new CreditsClient({\r\n *   baseUrl: process.env.CREDITS_API_URL,\r\n *   getAuthToken: async () => session.accessToken,\r\n * });\r\n *\r\n * // Check if user has sufficient credits\r\n * const check = await client.checkAvailability(\"story_generation\");\r\n * if (!check.hasCredits) {\r\n *   console.log(\"Insufficient credits\");\r\n *   return;\r\n * }\r\n *\r\n * // Two-phase commit pattern\r\n * const reservation = await client.reserve(\"story_generation\");\r\n * try {\r\n *   const result = await doExpensiveWork();\r\n *   await client.commit(reservation.reservationId);\r\n *   return result;\r\n * } catch (error) {\r\n *   await client.release(reservation.reservationId);\r\n *   throw error;\r\n * }\r\n * ```\r\n */\r\n\r\nimport type {\r\n  CreditsClientConfig,\r\n  UserCredits,\r\n  CreditCheckResult,\r\n  ReservationResult,\r\n  UsageHistoryResponse,\r\n  PaginationOptions,\r\n} from \"./types.js\";\r\nimport { parseApiError, NetworkError } from \"./errors.js\";\r\n\r\nexport class CreditsClient {\r\n  private readonly baseUrl: string;\r\n  private readonly getAuthToken?: () => Promise<string>;\r\n  private readonly fetchFn: typeof fetch;\r\n\r\n  constructor(config: CreditsClientConfig) {\r\n    // Ensure baseUrl doesn't have trailing slash\r\n    this.baseUrl = config.baseUrl.replace(/\\/$/, \"\");\r\n    this.getAuthToken = config.getAuthToken;\r\n    this.fetchFn = config.fetch ?? fetch;\r\n  }\r\n\r\n  /**\r\n   * Make an authenticated request to the API\r\n   */\r\n  private async request<T>(\r\n    path: string,\r\n    options: {\r\n      method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\r\n      body?: unknown;\r\n      params?: Record<string, string | number | undefined>;\r\n    } = {}\r\n  ): Promise<T> {\r\n    const { method = \"GET\", body, params } = options;\r\n\r\n    // Build URL with query params\r\n    let url = `${this.baseUrl}${path}`;\r\n    if (params) {\r\n      const searchParams = new URLSearchParams();\r\n      for (const [key, value] of Object.entries(params)) {\r\n        if (value !== undefined) {\r\n          searchParams.set(key, String(value));\r\n        }\r\n      }\r\n      const queryString = searchParams.toString();\r\n      if (queryString) {\r\n        url += `?${queryString}`;\r\n      }\r\n    }\r\n\r\n    // Build headers\r\n    const headers: Record<string, string> = {\r\n      \"Content-Type\": \"application/json\",\r\n    };\r\n\r\n    // Add auth token if available\r\n    if (this.getAuthToken) {\r\n      try {\r\n        const token = await this.getAuthToken();\r\n        if (token) {\r\n          headers[\"Authorization\"] = `Bearer ${token}`;\r\n        }\r\n      } catch {\r\n        // Auth token not available, proceed without it\r\n      }\r\n    }\r\n\r\n    // Make the request\r\n    let response: Response;\r\n    try {\r\n      response = await this.fetchFn(url, {\r\n        method,\r\n        headers,\r\n        body: body ? JSON.stringify(body) : undefined,\r\n      });\r\n    } catch (error) {\r\n      throw new NetworkError(\r\n        error instanceof Error ? error.message : \"Network request failed\",\r\n        error instanceof Error ? error : undefined\r\n      );\r\n    }\r\n\r\n    // Parse response\r\n    const responseBody = (await response.json()) as {\r\n      data?: T;\r\n      error?: { code?: string; message?: string; details?: unknown };\r\n    };\r\n\r\n    // Handle errors\r\n    if (!response.ok) {\r\n      throw parseApiError(response.status, responseBody);\r\n    }\r\n\r\n    // Return data\r\n    return responseBody.data as T;\r\n  }\r\n\r\n  /**\r\n   * Get the current user's credit balance\r\n   *\r\n   * @returns User's credit information\r\n   * @throws AuthenticationError if not authenticated\r\n   */\r\n  async getBalance(): Promise<UserCredits> {\r\n    return this.request<UserCredits>(\"/api/v1/credits/balance\");\r\n  }\r\n\r\n  /**\r\n   * Check if the user has sufficient credits for an operation\r\n   *\r\n   * @param operationType - Type of operation to check\r\n   * @param customCost - Optional custom cost to check against\r\n   * @returns Credit availability status\r\n   * @throws AuthenticationError if not authenticated\r\n   */\r\n  async checkAvailability(\r\n    operationType: string,\r\n    customCost?: number\r\n  ): Promise<CreditCheckResult> {\r\n    return this.request<CreditCheckResult>(\"/api/v1/credits/availability\", {\r\n      params: {\r\n        operationType,\r\n        customCost: customCost?.toString(),\r\n      },\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Reserve credits for an operation\r\n   *\r\n   * This is the first step in the two-phase commit pattern.\r\n   * Reserved credits are held for 5 minutes before expiring.\r\n   *\r\n   * @param operationType - Type of operation\r\n   * @param options - Optional reservation options\r\n   * @returns Reservation details including ID and expiry\r\n   * @throws InsufficientCreditsError if not enough credits\r\n   * @throws AuthenticationError if not authenticated\r\n   */\r\n  async reserve(\r\n    operationType: string,\r\n    options?: {\r\n      customCost?: number;\r\n      resourceId?: string;\r\n      resourceType?: string;\r\n    }\r\n  ): Promise<ReservationResult> {\r\n    return this.request<ReservationResult>(\"/api/v1/credits/reserve\", {\r\n      method: \"POST\",\r\n      body: {\r\n        operationType,\r\n        ...options,\r\n      },\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Commit a reservation (deduct credits)\r\n   *\r\n   * This is the second step in the two-phase commit pattern.\r\n   * Call this after your operation succeeds.\r\n   *\r\n   * @param reservationId - ID of the reservation to commit\r\n   * @throws ReservationNotFoundError if reservation doesn't exist\r\n   * @throws ReservationExpiredError if reservation has expired\r\n   * @throws AuthenticationError if not authenticated\r\n   */\r\n  async commit(reservationId: string): Promise<void> {\r\n    await this.request<{ message: string }>(\"/api/v1/credits/commit\", {\r\n      method: \"POST\",\r\n      body: { reservationId },\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Release a reservation (refund credits)\r\n   *\r\n   * Call this if your operation fails after reserving credits.\r\n   *\r\n   * @param reservationId - ID of the reservation to release\r\n   * @throws ReservationNotFoundError if reservation doesn't exist\r\n   * @throws AuthenticationError if not authenticated\r\n   */\r\n  async release(reservationId: string): Promise<void> {\r\n    await this.request<{ message: string }>(\"/api/v1/credits/release\", {\r\n      method: \"POST\",\r\n      body: { reservationId },\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Get usage history for the current user\r\n   *\r\n   * @param options - Optional pagination options\r\n   * @returns Paginated list of usage entries\r\n   * @throws AuthenticationError if not authenticated\r\n   */\r\n  async getHistory(options?: PaginationOptions): Promise<UsageHistoryResponse> {\r\n    return this.request<UsageHistoryResponse>(\"/api/v1/credits/history\", {\r\n      params: {\r\n        page: options?.page,\r\n        limit: options?.limit,\r\n      },\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Get the available credits (balance + bonus - reserved)\r\n   *\r\n   * Convenience method that calculates available credits from balance.\r\n   *\r\n   * @returns Number of available credits\r\n   * @throws AuthenticationError if not authenticated\r\n   */\r\n  async getAvailableCredits(): Promise<number> {\r\n    const balance = await this.getBalance();\r\n    return balance.balance + balance.bonusCredits - balance.reserved;\r\n  }\r\n\r\n  /**\r\n   * Execute an operation with automatic credit handling\r\n   *\r\n   * This is a convenience method that handles the full two-phase commit\r\n   * pattern automatically.\r\n   *\r\n   * @param operationType - Type of operation\r\n   * @param operation - The operation to execute\r\n   * @param options - Optional reservation options\r\n   * @returns Result of the operation\r\n   * @throws InsufficientCreditsError if not enough credits\r\n   * @throws AuthenticationError if not authenticated\r\n   *\r\n   * @example\r\n   * ```typescript\r\n   * const result = await client.withCredits(\r\n   *   \"story_generation\",\r\n   *   async () => {\r\n   *     return generateStory(data);\r\n   *   }\r\n   * );\r\n   * ```\r\n   */\r\n  async withCredits<T>(\r\n    operationType: string,\r\n    operation: () => Promise<T>,\r\n    options?: {\r\n      customCost?: number;\r\n      resourceId?: string;\r\n      resourceType?: string;\r\n    }\r\n  ): Promise<T> {\r\n    const reservation = await this.reserve(operationType, options);\r\n\r\n    try {\r\n      const result = await operation();\r\n      await this.commit(reservation.reservationId);\r\n      return result;\r\n    } catch (error) {\r\n      // Always attempt to release on failure\r\n      try {\r\n        await this.release(reservation.reservationId);\r\n      } catch {\r\n        // Ignore release errors, original error is more important\r\n      }\r\n      throw error;\r\n    }\r\n  }\r\n}\r\n","/**\r\n * AdminCreditsClient - Admin REST API client for the credits system\r\n *\r\n * Provides administrative access to the credits system for managing\r\n * user credits, configurations, and subscriptions.\r\n *\r\n * @example\r\n * ```typescript\r\n * const adminClient = new AdminCreditsClient({\r\n *   baseUrl: process.env.CREDITS_API_URL,\r\n *   apiKey: process.env.CREDITS_ADMIN_API_KEY,\r\n * });\r\n *\r\n * // Add credits to a user\r\n * await adminClient.addCredits(\"user-123\", 100, \"Support credit grant\");\r\n *\r\n * // Update subscription tier\r\n * await adminClient.updateSubscription(\"user-123\", \"premium\", \"2027-01-01T00:00:00Z\");\r\n *\r\n * // Update operation costs\r\n * await adminClient.updateCosts({\r\n *   story_generation: 8,\r\n *   image_generation: 12,\r\n * });\r\n * ```\r\n */\r\n\r\nimport type { AdminCreditsClientConfig, UserCredits } from \"./types.js\";\r\nimport { parseApiError, NetworkError } from \"./errors.js\";\r\n\r\n/**\r\n * Credits configuration returned by the API\r\n */\r\nexport interface CreditsConfig {\r\n  operationCosts: Record<string, number>;\r\n  tierConfigs: Record<\r\n    string,\r\n    {\r\n      monthlyLimit: number;\r\n      features: string[];\r\n    }\r\n  >;\r\n}\r\n\r\n/**\r\n * Response from listing users\r\n */\r\nexport interface ListUsersResponse {\r\n  users: Array<{\r\n    userId: string;\r\n    balance: number;\r\n    bonusCredits?: number;\r\n    tier: string;\r\n    monthlyUsed?: number;\r\n  }>;\r\n  hasMore: boolean;\r\n  total?: number;\r\n}\r\n\r\n/**\r\n * Options for listing users\r\n */\r\nexport interface ListUsersOptions {\r\n  page?: number;\r\n  limit?: number;\r\n  tier?: string;\r\n  search?: string;\r\n}\r\n\r\nexport class AdminCreditsClient {\r\n  private readonly baseUrl: string;\r\n  private readonly apiKey: string;\r\n  private readonly fetchFn: typeof fetch;\r\n\r\n  constructor(config: AdminCreditsClientConfig) {\r\n    // Ensure baseUrl doesn't have trailing slash\r\n    this.baseUrl = config.baseUrl.replace(/\\/$/, \"\");\r\n    this.apiKey = config.apiKey;\r\n    this.fetchFn = config.fetch ?? fetch;\r\n  }\r\n\r\n  /**\r\n   * Make an authenticated admin request to the API\r\n   */\r\n  private async request<T>(\r\n    path: string,\r\n    options: {\r\n      method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\r\n      body?: unknown;\r\n      params?: Record<string, string | number | undefined>;\r\n    } = {}\r\n  ): Promise<T> {\r\n    const { method = \"GET\", body, params } = options;\r\n\r\n    // Build URL with query params\r\n    let url = `${this.baseUrl}${path}`;\r\n    if (params) {\r\n      const searchParams = new URLSearchParams();\r\n      for (const [key, value] of Object.entries(params)) {\r\n        if (value !== undefined) {\r\n          searchParams.set(key, String(value));\r\n        }\r\n      }\r\n      const queryString = searchParams.toString();\r\n      if (queryString) {\r\n        url += `?${queryString}`;\r\n      }\r\n    }\r\n\r\n    // Build headers with admin API key\r\n    const headers: Record<string, string> = {\r\n      \"Content-Type\": \"application/json\",\r\n      \"x-api-key\": this.apiKey,\r\n    };\r\n\r\n    // Make the request\r\n    let response: Response;\r\n    try {\r\n      response = await this.fetchFn(url, {\r\n        method,\r\n        headers,\r\n        body: body ? JSON.stringify(body) : undefined,\r\n      });\r\n    } catch (error) {\r\n      throw new NetworkError(\r\n        error instanceof Error ? error.message : \"Network request failed\",\r\n        error instanceof Error ? error : undefined\r\n      );\r\n    }\r\n\r\n    // Parse response\r\n    const responseBody = (await response.json()) as {\r\n      data?: T;\r\n      error?: { code?: string; message?: string; details?: unknown };\r\n    };\r\n\r\n    // Handle errors\r\n    if (!response.ok) {\r\n      throw parseApiError(response.status, responseBody);\r\n    }\r\n\r\n    // Return data\r\n    return responseBody.data as T;\r\n  }\r\n\r\n  /**\r\n   * Get a specific user's credits\r\n   *\r\n   * @param userId - ID of the user\r\n   * @returns User's credit information\r\n   * @throws AuthorizationError if API key is invalid\r\n   */\r\n  async getUserCredits(userId: string): Promise<UserCredits> {\r\n    return this.request<UserCredits>(`/api/admin/credits/users/${userId}`);\r\n  }\r\n\r\n  /**\r\n   * Add credits to a user's account\r\n   *\r\n   * Credits are added to bonusCredits, which don't expire on monthly reset.\r\n   *\r\n   * @param userId - ID of the user\r\n   * @param amount - Amount of credits to add\r\n   * @param description - Reason for adding credits\r\n   * @throws AuthorizationError if API key is invalid\r\n   */\r\n  async addCredits(\r\n    userId: string,\r\n    amount: number,\r\n    description: string\r\n  ): Promise<void> {\r\n    await this.request<{ message: string; newBalance: number }>(\r\n      `/api/admin/credits/users/${userId}/add`,\r\n      {\r\n        method: \"POST\",\r\n        body: { amount, description },\r\n      }\r\n    );\r\n  }\r\n\r\n  /**\r\n   * Update a user's subscription tier\r\n   *\r\n   * @param userId - ID of the user\r\n   * @param tier - New subscription tier\r\n   * @param expiresAt - Optional expiration date (ISO 8601)\r\n   * @throws AuthorizationError if API key is invalid\r\n   */\r\n  async updateSubscription(\r\n    userId: string,\r\n    tier: string,\r\n    expiresAt?: string\r\n  ): Promise<void> {\r\n    const body: { tier: string; expiresAt?: string } = { tier };\r\n    if (expiresAt) {\r\n      body.expiresAt = expiresAt;\r\n    }\r\n\r\n    await this.request<{ message: string }>(\r\n      `/api/admin/credits/users/${userId}/subscription`,\r\n      {\r\n        method: \"PUT\",\r\n        body,\r\n      }\r\n    );\r\n  }\r\n\r\n  /**\r\n   * Get the current credits configuration\r\n   *\r\n   * @returns Credits system configuration\r\n   * @throws AuthorizationError if API key is invalid\r\n   */\r\n  async getConfig(): Promise<CreditsConfig> {\r\n    return this.request<CreditsConfig>(\"/api/admin/credits/config\");\r\n  }\r\n\r\n  /**\r\n   * Update operation costs\r\n   *\r\n   * @param costs - New operation costs (operation type -> cost)\r\n   * @throws AuthorizationError if API key is invalid\r\n   */\r\n  async updateCosts(costs: Record<string, number>): Promise<void> {\r\n    await this.request<{ message: string }>(\"/api/admin/credits/config/costs\", {\r\n      method: \"PUT\",\r\n      body: { operationCosts: costs },\r\n    });\r\n  }\r\n\r\n  /**\r\n   * List all users with their credits\r\n   *\r\n   * @param options - Pagination and filtering options\r\n   * @returns Paginated list of users\r\n   * @throws AuthorizationError if API key is invalid\r\n   */\r\n  async listUsers(options?: ListUsersOptions): Promise<ListUsersResponse> {\r\n    return this.request<ListUsersResponse>(\"/api/admin/credits/users\", {\r\n      params: {\r\n        page: options?.page,\r\n        limit: options?.limit,\r\n        tier: options?.tier,\r\n        search: options?.search,\r\n      },\r\n    });\r\n  }\r\n\r\n  /**\r\n   * Deduct credits from a user's account\r\n   *\r\n   * @param userId - ID of the user\r\n   * @param amount - Amount of credits to deduct\r\n   * @param description - Reason for deducting credits\r\n   * @throws AuthorizationError if API key is invalid\r\n   */\r\n  async deductCredits(\r\n    userId: string,\r\n    amount: number,\r\n    description: string\r\n  ): Promise<void> {\r\n    await this.request<{ message: string; newBalance: number }>(\r\n      `/api/admin/credits/users/${userId}/deduct`,\r\n      {\r\n        method: \"POST\",\r\n        body: { amount, description },\r\n      }\r\n    );\r\n  }\r\n\r\n  /**\r\n   * Get usage statistics for a user\r\n   *\r\n   * @param userId - ID of the user\r\n   * @param options - Optional date range\r\n   * @returns Usage statistics\r\n   * @throws AuthorizationError if API key is invalid\r\n   */\r\n  async getUserStats(\r\n    userId: string,\r\n    options?: { startDate?: string; endDate?: string }\r\n  ): Promise<{\r\n    totalCreditsUsed: number;\r\n    operationBreakdown: Record<string, number>;\r\n    successRate: number;\r\n  }> {\r\n    return this.request<{\r\n      totalCreditsUsed: number;\r\n      operationBreakdown: Record<string, number>;\r\n      successRate: number;\r\n    }>(`/api/admin/credits/users/${userId}/stats`, {\r\n      params: {\r\n        startDate: options?.startDate,\r\n        endDate: options?.endDate,\r\n      },\r\n    });\r\n  }\r\n}\r\n","/**\r\n * Credit error utilities\r\n *\r\n * Provides functions to parse and identify credit-related errors\r\n * for consistent error handling across the application.\r\n */\r\n\r\n/**\r\n * Parsed information from a credit error message\r\n */\r\nexport interface CreditErrorInfo {\r\n  /** Credits currently available */\r\n  available: number;\r\n  /** Credits required for the operation */\r\n  required: number;\r\n  /** Difference between required and available */\r\n  shortfall: number;\r\n  /** Original error message */\r\n  rawMessage: string;\r\n}\r\n\r\n/**\r\n * Parse a credit error message to extract available/required credits\r\n *\r\n * @param error - Error message string\r\n * @returns Parsed credit info or null if not a valid credit error format\r\n *\r\n * @example\r\n * ```ts\r\n * const error = \"Insufficient credits. Available: 3, Required: 10\";\r\n * const info = parseCreditError(error);\r\n * // { available: 3, required: 10, shortfall: 7, rawMessage: \"...\" }\r\n * ```\r\n */\r\nexport function parseCreditError(error: string): CreditErrorInfo | null {\r\n  // Pattern: Available: X, Required: Y (case insensitive, flexible whitespace)\r\n  const match = error.match(/Available:\\s*(\\d+)\\s*,\\s*Required:\\s*(\\d+)/i);\r\n\r\n  if (!match) {\r\n    return null;\r\n  }\r\n\r\n  const available = parseInt(match[1], 10);\r\n  const required = parseInt(match[2], 10);\r\n\r\n  // Validate parsed numbers\r\n  if (isNaN(available) || isNaN(required)) {\r\n    return null;\r\n  }\r\n\r\n  return {\r\n    available,\r\n    required,\r\n    shortfall: required - available,\r\n    rawMessage: error,\r\n  };\r\n}\r\n\r\n/**\r\n * Check if an error message is a credit-related error\r\n *\r\n * @param error - Error message string\r\n * @returns true if the error is credit-related\r\n *\r\n * @example\r\n * ```ts\r\n * if (isCreditError(error)) {\r\n *   showCreditErrorDialog(parseCreditError(error));\r\n * }\r\n * ```\r\n */\r\nexport function isCreditErrorMessage(error: string): boolean {\r\n  const lowerError = error.toLowerCase();\r\n  return (\r\n    lowerError.includes(\"insufficient credits\") ||\r\n    lowerError.includes(\"not enough credits\")\r\n  );\r\n}\r\n","/**\r\n * Credit utility functions\r\n *\r\n * Common helper functions for credit calculations\r\n */\r\n\r\nimport type { PortableReservation } from \"../core/index.js\";\r\n\r\nexport {\r\n  parseCreditError,\r\n  isCreditErrorMessage,\r\n  type CreditErrorInfo,\r\n} from \"./error-utils.js\";\r\n\r\n/**\r\n * Calculate total available credits from balance and bonus credits\r\n *\r\n * @param credits - Object containing balance and bonusCredits properties\r\n * @returns Total credits (balance + bonusCredits)\r\n *\r\n * @example\r\n * ```ts\r\n * const total = getTotalCredits({ balance: 100, bonusCredits: 50 });\r\n * // Returns 150\r\n * ```\r\n */\r\nexport function getTotalCredits(credits: {\r\n  balance: number;\r\n  bonusCredits: number;\r\n}): number {\r\n  return credits.balance + credits.bonusCredits;\r\n}\r\n\r\n/**\r\n * Generate a unique request ID\r\n *\r\n * Format: req_{timestamp}_{randomPart}\r\n * Example: req_1706234567890_a1b2c3d4e5f6\r\n *\r\n * @returns URL-safe request ID string\r\n */\r\nexport function generateRequestId(): string {\r\n  const timestamp = Date.now();\r\n  const randomPart = Math.random().toString(36).substring(2, 14);\r\n  return `req_${timestamp}_${randomPart}`;\r\n}\r\n\r\n/**\r\n * Check if the input data indicates preview mode\r\n *\r\n * Preview mode skips actual credit deduction - used for dry runs\r\n * and UI previews where the user wants to see results without committing.\r\n *\r\n * @param data - The input data to check\r\n * @returns true if data has preview: true\r\n */\r\nexport function isPreviewMode(data: unknown): boolean {\r\n  return (\r\n    typeof data === \"object\" &&\r\n    data !== null &&\r\n    \"preview\" in data &&\r\n    (data as Record<string, unknown>).preview === true\r\n  );\r\n}\r\n\r\n/**\r\n * Create a dummy reservation for preview mode\r\n *\r\n * Used when `isPreviewMode(data)` returns true to provide\r\n * a valid reservation object without actual credit operations.\r\n *\r\n * @param userId - The user ID\r\n * @param operationType - The operation type\r\n * @returns A dummy PortableReservation with id \"preview-mode\"\r\n */\r\nexport function createDummyReservation(\r\n  userId: string,\r\n  operationType: string\r\n): PortableReservation {\r\n  const now = new Date().toISOString();\r\n  return {\r\n    id: \"preview-mode\",\r\n    userId,\r\n    amount: 0,\r\n    operationType,\r\n    status: \"released\",\r\n    createdAt: now,\r\n    expiresAt: now,\r\n  };\r\n}\r\n"],"mappings":"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","names":["createDummyReservation","generateRequestId","isPreviewMode","toDate","toDate","getOperationCost","generateRequestId","isPreviewMode","createDummyReservation"]}