{"version":3,"file":"service-circuit-breaker.mjs","sources":["../../../src/lib/services/service-circuit-breaker.ts"],"sourcesContent":["/**\n * @file Service-Level Circuit Breaker\n * @description Per-service circuit breakers with health monitoring\n *\n * This module provides circuit breaker patterns for external service calls,\n * preventing cascading failures and providing graceful degradation when\n * services are unhealthy.\n */\n\nimport {\n  CircuitBreaker,\n  type CircuitState,\n  type CircuitBreakerConfig,\n} from '../utils/resilience';\nimport { ErrorReporter } from '../monitoring/ErrorReporter';\nimport { globalEventBus } from '../shared/event-utils';\nimport {\n  DEFAULT_TIMEOUT,\n  MINUTE,\n} from '@/lib/core/config/constants';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Service health status\n */\nexport type ServiceHealth = 'healthy' | 'degraded' | 'unhealthy' | 'unknown';\n\n/**\n * Service configuration extending circuit breaker config\n */\nexport interface ServiceConfig extends CircuitBreakerConfig {\n  /** Unique service name */\n  name: string;\n  /** Service base URL */\n  baseUrl?: string;\n  /** Health check endpoint (relative to baseUrl) */\n  healthCheckEndpoint?: string;\n  /** Health check interval in ms */\n  healthCheckInterval?: number;\n  /** Fallback function when circuit is open */\n  fallback?: <T>() => T | Promise<T>;\n  /** Whether to start health checks automatically */\n  autoStartHealthChecks?: boolean;\n  /** Custom health check function */\n  customHealthCheck?: () => Promise<boolean>;\n}\n\n/**\n * Service status for monitoring\n */\nexport interface ServiceStatus {\n  /** Service name */\n  name: string;\n  /** Current health status */\n  health: ServiceHealth;\n  /** Circuit breaker state */\n  circuitState: CircuitState;\n  /** Last health check time */\n  lastCheck: Date | null;\n  /** Last error message if any */\n  lastError: string | null;\n  /** Success rate (0-1) */\n  successRate: number;\n  /** Average response time in ms */\n  avgResponseTime: number;\n  /** Total request count */\n  requestCount: number;\n}\n\n/**\n * Service state change event\n */\nexport interface ServiceStateChangeEvent {\n  service: string;\n  from: CircuitState;\n  to: CircuitState;\n  health: ServiceHealth;\n}\n\n// ============================================================================\n// Extend Global Events\n// ============================================================================\n\n// declare module '../utils/eventEmitter' {\n//   interface GlobalEvents {\n//     'service:stateChange': ServiceStateChangeEvent;\n//     'service:healthCheck': { service: string; healthy: boolean };\n//     'service:error': { service: string; error: string };\n//   }\n// }\n\n// ============================================================================\n// Service Circuit Breaker\n// ============================================================================\n\n/**\n * Circuit breaker for a specific service with health monitoring\n *\n * @example\n * ```tsx\n * const apiBreaker = new ServiceCircuitBreaker({\n *   name: 'api',\n *   baseUrl: 'https://api.example.com',\n *   healthCheckEndpoint: '/health',\n *   failureThreshold: 5,\n *   resetTimeout: 30000,\n * });\n *\n * // Execute requests through the circuit breaker\n * const result = await apiBreaker.execute(() => fetch('/api/users'));\n * ```\n */\nexport class ServiceCircuitBreaker {\n  private breaker: CircuitBreaker;\n  private config: Required<\n    Omit<ServiceConfig, 'onStateChange' | 'fallback' | 'customHealthCheck'>\n  > & {\n    fallback?: ServiceConfig['fallback'];\n    customHealthCheck?: ServiceConfig['customHealthCheck'];\n  };\n  private healthCheckTimer: ReturnType<typeof setInterval> | null = null;\n  private metrics = {\n    successCount: 0,\n    failureCount: 0,\n    totalResponseTime: 0,\n    requestCount: 0,\n    lastError: null as string | null,\n    lastCheck: null as Date | null,\n  };\n\n  constructor(config: ServiceConfig) {\n    this.config = {\n      failureThreshold: config.failureThreshold ?? 5,\n      successThreshold: config.successThreshold ?? 2,\n      resetTimeout: config.resetTimeout ?? DEFAULT_TIMEOUT,\n      failureWindow: config.failureWindow ?? MINUTE,\n      healthCheckInterval: config.healthCheckInterval ?? MINUTE,\n      autoStartHealthChecks: config.autoStartHealthChecks ?? false,\n      name: config.name,\n      baseUrl: config.baseUrl ?? '',\n      healthCheckEndpoint: config.healthCheckEndpoint ?? '',\n      fallback: config.fallback,\n      customHealthCheck: config.customHealthCheck,\n    };\n\n    this.breaker = new CircuitBreaker({\n      failureThreshold: this.config.failureThreshold,\n      successThreshold: this.config.successThreshold,\n      resetTimeout: this.config.resetTimeout,\n      failureWindow: this.config.failureWindow,\n      onStateChange: this.handleStateChange.bind(this),\n    });\n\n    if (this.config.autoStartHealthChecks) {\n      this.startHealthChecks();\n    }\n  }\n\n  /**\n   * Execute a request through the circuit breaker\n   *\n   * @param fn - Async function to execute\n   * @returns Result of the function\n   * @throws Error if circuit is open and no fallback available\n   */\n  async execute<T>(fn: () => Promise<T>): Promise<T> {\n    const startTime = performance.now();\n    this.metrics.requestCount++;\n\n    try {\n      const result = await this.breaker.execute(fn);\n\n      // Record success\n      const responseTime = performance.now() - startTime;\n      this.metrics.successCount++;\n      this.metrics.totalResponseTime += responseTime;\n\n      return result;\n    } catch (error) {\n      // Record failure\n      this.metrics.failureCount++;\n      this.metrics.lastError = error instanceof Error ? error.message : String(error);\n\n      globalEventBus.emitSync('service:error', {\n        service: this.config.name,\n        error: new Error(this.metrics.lastError),\n      });\n\n      // Try fallback if circuit is open and fallback exists\n      if (this.breaker.getState() === 'open' && this.config.fallback) {\n        return this.config.fallback<T>();\n      }\n\n      throw error;\n    }\n  }\n\n  /**\n   * Execute with automatic retry on failure\n   *\n   * @param fn - Async function to execute\n   * @param maxRetries - Maximum retry attempts\n   * @param delay - Base delay between retries in ms\n   */\n  async executeWithRetry<T>(\n    fn: () => Promise<T>,\n    maxRetries: number = 3,\n    delay: number = 1000\n  ): Promise<T> {\n    let lastError: Error | null = null;\n\n    for (let attempt = 0; attempt <= maxRetries; attempt++) {\n      try {\n        return await this.execute(fn);\n      } catch (error) {\n        lastError = error instanceof Error ? error : new Error(String(error));\n\n        // Don't retry if circuit is open\n        if (this.breaker.getState() === 'open') {\n          throw lastError;\n        }\n\n        // Wait before retry with exponential backoff\n        if (attempt < maxRetries) {\n          await new Promise((resolve) =>\n            setTimeout(resolve, delay * Math.pow(2, attempt))\n          );\n        }\n      }\n    }\n\n    throw lastError ?? new Error('Max retries exceeded');\n  }\n\n  /**\n   * Get service health based on circuit state and metrics\n   */\n  getHealth(): ServiceHealth {\n    const state = this.breaker.getState();\n\n    if (state === 'open') {\n      return 'unhealthy';\n    }\n\n    if (state === 'half-open') {\n      return 'degraded';\n    }\n\n    const successRate = this.getSuccessRate();\n    if (successRate < 0.9 && this.metrics.requestCount > 10) {\n      return 'degraded';\n    }\n\n    if (this.metrics.requestCount === 0) {\n      return 'unknown';\n    }\n\n    return 'healthy';\n  }\n\n  /**\n   * Get success rate (0-1)\n   */\n  getSuccessRate(): number {\n    const total = this.metrics.successCount + this.metrics.failureCount;\n    return total > 0 ? this.metrics.successCount / total : 1;\n  }\n\n  /**\n   * Get average response time in ms\n   */\n  getAvgResponseTime(): number {\n    return this.metrics.requestCount > 0\n      ? this.metrics.totalResponseTime / this.metrics.requestCount\n      : 0;\n  }\n\n  /**\n   * Get complete service status\n   */\n  getStatus(): ServiceStatus {\n    return {\n      name: this.config.name,\n      health: this.getHealth(),\n      circuitState: this.breaker.getState(),\n      lastCheck: this.metrics.lastCheck,\n      lastError: this.metrics.lastError,\n      successRate: this.getSuccessRate(),\n      avgResponseTime: this.getAvgResponseTime(),\n      requestCount: this.metrics.requestCount,\n    };\n  }\n\n  /**\n   * Get circuit breaker state\n   */\n  getState(): CircuitState {\n    return this.breaker.getState();\n  }\n\n  /**\n   * Check if circuit is allowing requests\n   */\n  isAvailable(): boolean {\n    return this.breaker.getState() !== 'open';\n  }\n\n  /**\n   * Start periodic health checks\n   */\n  startHealthChecks(): void {\n    if (this.healthCheckTimer) {\n      return; // Already running\n    }\n\n    if (!this.config.healthCheckEndpoint && !this.config.customHealthCheck) {\n      return; // No health check configured\n    }\n\n    this.healthCheckTimer = setInterval(\n      () => void this.performHealthCheck(),\n      this.config.healthCheckInterval\n    );\n\n    // Perform initial check\n    void this.performHealthCheck();\n  }\n\n  /**\n   * Stop periodic health checks\n   */\n  stopHealthChecks(): void {\n    if (this.healthCheckTimer) {\n      clearInterval(this.healthCheckTimer);\n      this.healthCheckTimer = null;\n    }\n  }\n\n  /**\n   * Perform a single health check\n   *\n   * Note: This method intentionally uses raw fetch() rather than apiClient because:\n   * 1. Health checks are infrastructure-level, not application API calls\n   * 2. The circuit breaker monitors services that apiClient itself depends on\n   * 3. Using apiClient would create circular dependency/infinite loops\n   *\n   * @see {@link @/lib/api/api-client} for application API calls\n   */\n  async performHealthCheck(): Promise<boolean> {\n    this.metrics.lastCheck = new Date();\n\n    try {\n      let isHealthy: boolean;\n\n      if (this.config.customHealthCheck) {\n        isHealthy = await this.config.customHealthCheck();\n      } else if (this.config.healthCheckEndpoint) {\n        const url = this.config.baseUrl\n          ? `${this.config.baseUrl}${this.config.healthCheckEndpoint}`\n          : this.config.healthCheckEndpoint;\n\n        // Raw fetch is intentional - health checks must be independent of apiClient\n        const response = await fetch(url, {\n          method: 'GET',\n          headers: { Accept: 'application/json' },\n          cache: 'no-store',\n        });\n\n        isHealthy = response.ok;\n      } else {\n        return true; // No health check configured\n      }\n\n      if (isHealthy) {\n        this.breaker.success();\n      } else {\n        this.breaker.failure();\n      }\n\n      globalEventBus.emitSync('service:healthCheck', {\n        service: this.config.name,\n        health: isHealthy,\n      });\n\n      return isHealthy;\n    } catch (error) {\n      this.metrics.lastError = error instanceof Error ? error.message : String(error);\n      this.breaker.failure();\n\n      globalEventBus.emitSync('service:healthCheck', {\n        service: this.config.name,\n        health: false,\n      });\n\n      return false;\n    }\n  }\n\n  /**\n   * Manually record a success (for external success tracking)\n   */\n  recordSuccess(): void {\n    this.metrics.successCount++;\n    this.breaker.success();\n  }\n\n  /**\n   * Manually record a failure (for external failure tracking)\n   */\n  recordFailure(error?: string): void {\n    this.metrics.failureCount++;\n    if (error !== undefined) {\n      this.metrics.lastError = error;\n    }\n    this.breaker.failure();\n  }\n\n  /**\n   * Reset circuit breaker and metrics\n   */\n  reset(): void {\n    this.breaker.reset();\n    this.metrics = {\n      successCount: 0,\n      failureCount: 0,\n      totalResponseTime: 0,\n      requestCount: 0,\n      lastError: null,\n      lastCheck: null,\n    };\n  }\n\n  /**\n   * Dispose and cleanup resources\n   */\n  dispose(): void {\n    this.stopHealthChecks();\n  }\n\n  /**\n   * Handle circuit state change\n   */\n  private handleStateChange(from: CircuitState, to: CircuitState): void {\n    const health = this.getHealth();\n\n    globalEventBus.emitSync('service:stateChange', {\n      service: this.config.name,\n      from,\n      to,\n      health,\n    });\n\n    // Report significant state changes\n    if (to === 'open') {\n      ErrorReporter.reportWarning(`Service ${this.config.name} circuit opened`, {\n        action: 'circuit_open',\n        metadata: {\n          service: this.config.name,\n          successRate: this.getSuccessRate(),\n          lastError: this.metrics.lastError,\n        },\n      });\n    } else if (from === 'open' && to === 'closed') {\n      ErrorReporter.reportInfo(`Service ${this.config.name} recovered`);\n    }\n  }\n}\n\n// ============================================================================\n// Service Registry\n// ============================================================================\n\n/**\n * Registry for managing multiple service circuit breakers\n */\nclass ServiceRegistry {\n  private services: Map<string, ServiceCircuitBreaker> = new Map();\n\n  /**\n   * Get registry size\n   */\n  get size(): number {\n    return this.services.size;\n  }\n\n  /**\n   * Register a new service\n   *\n   * @param config - Service configuration\n   * @returns The created circuit breaker\n   */\n  register(config: ServiceConfig): ServiceCircuitBreaker {\n    if (this.services.has(config.name)) {\n      console.warn(\n        `[ServiceRegistry] Service \"${config.name}\" already registered, replacing`\n      );\n      this.services.get(config.name)?.dispose();\n    }\n\n    const breaker = new ServiceCircuitBreaker(config);\n    this.services.set(config.name, breaker);\n    return breaker;\n  }\n\n  /**\n   * Get a service circuit breaker by name\n   */\n  get(name: string): ServiceCircuitBreaker | undefined {\n    return this.services.get(name);\n  }\n\n  /**\n   * Get or create a service circuit breaker\n   */\n  getOrCreate(config: ServiceConfig): ServiceCircuitBreaker {\n    const existing = this.services.get(config.name);\n    if (existing) {\n      return existing;\n    }\n    return this.register(config);\n  }\n\n  /**\n   * Check if a service is registered\n   */\n  has(name: string): boolean {\n    return this.services.has(name);\n  }\n\n  /**\n   * Unregister a service\n   */\n  unregister(name: string): void {\n    const service = this.services.get(name);\n    if (service) {\n      service.dispose();\n      this.services.delete(name);\n    }\n  }\n\n  /**\n   * Get status for all registered services\n   */\n  getAllStatuses(): ServiceStatus[] {\n    return Array.from(this.services.values()).map((s) => s.getStatus());\n  }\n\n  /**\n   * Get overall system health based on all services\n   */\n  getOverallHealth(): ServiceHealth {\n    const statuses = this.getAllStatuses();\n\n    if (statuses.length === 0) {\n      return 'unknown';\n    }\n\n    const unhealthyCount = statuses.filter((s) => s.health === 'unhealthy').length;\n    const degradedCount = statuses.filter((s) => s.health === 'degraded').length;\n\n    if (unhealthyCount > 0) {\n      return 'unhealthy';\n    }\n    if (degradedCount > 0) {\n      return 'degraded';\n    }\n\n    return 'healthy';\n  }\n\n  /**\n   * Get names of all unhealthy services\n   */\n  getUnhealthyServices(): string[] {\n    return this.getAllStatuses()\n      .filter((s) => s.health === 'unhealthy' || s.health === 'degraded')\n      .map((s) => s.name);\n  }\n\n  /**\n   * Start health checks for all services\n   */\n  startAllHealthChecks(): void {\n    this.services.forEach((s) => s.startHealthChecks());\n  }\n\n  /**\n   * Stop health checks for all services\n   */\n  stopAllHealthChecks(): void {\n    this.services.forEach((s) => s.stopHealthChecks());\n  }\n\n  /**\n   * Reset all circuit breakers\n   */\n  resetAll(): void {\n    this.services.forEach((s) => s.reset());\n  }\n\n  /**\n   * Dispose all services and cleanup\n   */\n  dispose(): void {\n    this.services.forEach((s) => s.dispose());\n    this.services.clear();\n  }\n\n  /**\n   * Get all service names\n   */\n  getServiceNames(): string[] {\n    return Array.from(this.services.keys());\n  }\n}\n\n/**\n * Global service registry instance\n */\nexport const serviceRegistry = new ServiceRegistry();\n\n// ============================================================================\n// Setup Helpers\n// ============================================================================\n\n/**\n * Setup default services with common configuration\n *\n * @param apiBaseUrl - Base URL for API services\n * @param options - Additional configuration options\n */\nexport function setupDefaultServices(\n  apiBaseUrl: string,\n  options?: {\n    authBaseUrl?: string;\n    healthCheckInterval?: number;\n    autoStart?: boolean;\n  }\n): void {\n  const { authBaseUrl, healthCheckInterval = MINUTE, autoStart = true } = options ?? {};\n\n  // Main API service\n  serviceRegistry.register({\n    name: 'api',\n    baseUrl: apiBaseUrl,\n    healthCheckEndpoint: '/health',\n    healthCheckInterval,\n    failureThreshold: 5,\n    resetTimeout: DEFAULT_TIMEOUT,\n    autoStartHealthChecks: autoStart,\n  });\n\n  // Auth service (may be separate)\n  serviceRegistry.register({\n    name: 'auth',\n    baseUrl: authBaseUrl ?? apiBaseUrl,\n    healthCheckEndpoint: '/auth/health',\n    healthCheckInterval,\n    failureThreshold: 3,\n    resetTimeout: MINUTE,\n    autoStartHealthChecks: autoStart,\n  });\n}\n\n/**\n * Create a wrapped fetch function that uses the service circuit breaker\n *\n * @param serviceName - Name of the service to use\n * @returns Wrapped fetch function\n */\nexport function createServiceFetch(\n  serviceName: string\n): (input: RequestInfo | URL, init?: RequestInit) => Promise<Response> {\n  return async (input: RequestInfo | URL, init?: RequestInit): Promise<Response> => {\n    const service = serviceRegistry.get(serviceName);\n\n    if (!service) {\n      // If service not registered, just use regular fetch\n      return fetch(input, init);\n    }\n\n    return service.execute(async () => fetch(input, init));\n  };\n}\n"],"names":["ServiceCircuitBreaker","config","DEFAULT_TIMEOUT","MINUTE","CircuitBreaker","fn","startTime","result","responseTime","error","globalEventBus","maxRetries","delay","lastError","attempt","resolve","state","total","isHealthy","url","from","to","health","ErrorReporter","ServiceRegistry","breaker","name","existing","service","s","statuses","unhealthyCount","degradedCount","serviceRegistry","setupDefaultServices","apiBaseUrl","options","authBaseUrl","healthCheckInterval","autoStart","createServiceFetch","serviceName","input","init"],"mappings":"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"}