{"_id":"@beethovn/circuit-breaker","name":"@beethovn/circuit-breaker","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@beethovn/circuit-breaker","version":"1.0.0","description":"Circuit breaker pattern implementation with failure tracking and automatic recovery","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"}},"dependencies":{"prom-client":"^15.1.0","@beethovn/errors":"1.0.0","@beethovn/logging":"1.0.0"},"devDependencies":{"@types/node":"^20.0.0","tsx":"^4.21.0","typescript":"^5.3.3","vitest":"^1.0.0"},"keywords":["circuit-breaker","resilience","fault-tolerance","error-handling"],"author":{"name":"Beethovn Team"},"license":"MIT","scripts":{"build":"tsc","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","type-check":"tsc --noEmit","lint":"eslint src","lint:fix":"eslint src --fix","bench":"tsx scripts/benchmark.ts"},"_id":"@beethovn/circuit-breaker@1.0.0","gitHead":"0e058eb9596d53b68620f504bc3382b9a2dfcfdc","_integrity":"sha512-YBJUl+GMOwxXyoM8hNc/pdiB/wx5VaZLaWQiZlIz6Vcwogy71r21wA/In442i4pA3ocDZbYgmw1JgBGsfqNHHg==","_resolved":"/private/var/folders/6k/7zmzv04n3fvfj530653gr8b80000gn/T/8719b28bd494ceefecf2366537aad823/beethovn-circuit-breaker-1.0.0.tgz","_from":"file:beethovn-circuit-breaker-1.0.0.tgz","_nodeVersion":"22.13.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-YBJUl+GMOwxXyoM8hNc/pdiB/wx5VaZLaWQiZlIz6Vcwogy71r21wA/In442i4pA3ocDZbYgmw1JgBGsfqNHHg==","shasum":"c90b5eb8c6d7f3273e914c1cd3b7050022d35f37","tarball":"https://registry.npmjs.org/@beethovn/circuit-breaker/-/circuit-breaker-1.0.0.tgz","fileCount":76,"unpackedSize":577909,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIF9rAOk92gJwcp1g0imOTpyM9ww7eZEZkCzEHashIq40AiEA9IZmhtC5qSWO22/qK7a7GpotWpsrNJ3HDWuCZmBWhQM="}]},"_npmUser":{"name":"cr17","email":"carsten@beethovn.com"},"directories":{},"maintainers":[{"name":"cr17","email":"carsten@beethovn.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/circuit-breaker_1.0.0_1767469546234_0.8669460692792264"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-03T19:45:46.071Z","1.0.0":"2026-01-03T19:45:46.475Z","modified":"2026-01-03T19:45:46.820Z"},"maintainers":[{"name":"cr17","email":"carsten@beethovn.com"}],"description":"Circuit breaker pattern implementation with failure tracking and automatic recovery","keywords":["circuit-breaker","resilience","fault-tolerance","error-handling"],"author":{"name":"Beethovn Team"},"license":"MIT","readme":"# @beethovn/circuit-breaker\n\nCircuit breaker pattern implementation with retry policies and failure tracking for the Beethovn monorepo.\n\n## Features\n\n- **Circuit Breaker Pattern**: Prevent cascading failures with automatic circuit opening/closing\n- **State Machine**: CLOSED → OPEN → HALF_OPEN state transitions\n- **Failure Tracking**: Rolling window-based failure rate calculation\n- **Retry Policies**: Exponential backoff with multiple jitter strategies\n- **Error Filtering**: Configurable error filtering for fine-grained control\n- **Event Emission**: Listen to state change events\n- **Resilient Executor**: Combine circuit breaker and retry in one wrapper\n\n## Installation\n\n```bash\npnpm add @beethovn/circuit-breaker\n```\n\n## Quick Start\n\n### Basic Circuit Breaker\n\n```typescript\nimport { CircuitBreaker } from '@beethovn/circuit-breaker';\n\nconst breaker = new CircuitBreaker({\n  name: 'payment-api',\n  failureThreshold: 5,      // Open after 5 failures\n  successThreshold: 2,       // Close after 2 successes in half-open\n  timeout: 60000,            // Try half-open after 60s\n  rollingWindowSize: 10000,  // 10s rolling window\n  minimumRequests: 5,        // Minimum requests before evaluation\n});\n\ntry {\n  const result = await breaker.execute(() => paymentApi.charge(amount));\n  console.log('Success:', result);\n} catch (error) {\n  console.error('Failed:', error);\n}\n```\n\n### Retry Policy\n\n```typescript\nimport { RetryPolicy, RetryPolicyPresets } from '@beethovn/circuit-breaker';\n\n//Use preset\nconst policy = RetryPolicyPresets.conservative();\n\n// Or custom configuration\nconst customPolicy = new RetryPolicy({\n  maxAttempts: 3,\n  initialDelay: 1000,\n  maxDelay: 30000,\n  backoffMultiplier: 2,\n  useJitter: true,\n});\n\nconst result = await policy.execute(() => fetchData());\n```\n\n### Resilient Executor (Combined)\n\n```typescript\nimport { ResilientExecutor } from '@beethovn/circuit-breaker';\n\nconst executor = new ResilientExecutor({\n  circuitBreaker: {\n    name: 'external-api',\n    failureThreshold: 5,\n    timeout: 60000,\n  },\n  retry: {\n    maxAttempts: 3,\n    initialDelay: 1000,\n  },\n});\n\nconst result = await executor.execute(() => externalApi.call());\n```\n\n## Circuit Breaker Pattern\n\n### States\n\n```\nCLOSED (Normal operation)\n   ↓ (failures ≥ threshold)\nOPEN (Reject immediately)\n   ↓ (after timeout)\nHALF_OPEN (Test recovery)\n   ↓ (successes ≥ threshold)    ↓ (any failure)\nCLOSED                          OPEN\n```\n\n### State Transitions\n\n| From | To | Trigger |\n|------|------------|---------|\n| CLOSED | OPEN | Failure threshold exceeded |\n| OPEN | HALF_OPEN | Timeout period elapsed |\n| HALF_OPEN | CLOSED | Success threshold met |\n| HALF_OPEN | OPEN | Any failure occurs |\n\n## Usage Examples\n\n### Monitor State Changes\n\n```typescript\nconst breaker = new CircuitBreaker({\n  name: 'database',\n  failureThreshold: 3,\n  timeout: 30000,\n});\n\nbreaker.onStateChange((event) => {\n  console.log(`Circuit ${event.name}: ${event.from} → ${event.to}`);\n  console.log('Stats:', event.stats);\n  \n  // Alert if circuit opened\n  if (event.to === CircuitState.OPEN) {\n    alerting.sendAlert(`Circuit ${event.name} opened!`);\n  }\n});\n```\n\n### Custom Error Filtering\n\n```typescript\nconst breaker = new CircuitBreaker({\n  name: 'api',\n  failureThreshold: 5,\n  errorFilter: (error) => {\n    // Only count 5xx errors, ignore 4xx\n    if (error instanceof HttpError) {\n      return error.status >= 500;\n    }\n    return true;\n  },\n});\n```\n\n### Retry with Custom Filter\n\n```typescript\nconst policy = new RetryPolicy({\n  maxAttempts: 3,\n  retryableErrorFilter: (error) => {\n    // Don't retry on 4xx errors\n    if (error instanceof HttpError) {\n      return error.status >= 500;\n    }\n    return true;\n  },\n});\n```\n\n### Get Circuit Statistics\n\n```typescript\nconst stats = breaker.getStats();\n\nconsole.log({\n  state: stats.state,\n  totalSuccesses: stats.totalSuccesses,\n  totalFailures: stats.totalFailures,\n  totalRejections: stats.totalRejections,\n  failureRate: stats.failureRate,\n  timeUntilRetry: stats.timeUntilRetry,\n});\n```\n\n### Jitter Strategies\n\n```typescript\nimport { JitterStrategy } from '@beethovn/circuit-breaker';\n\nconst policy = new RetryPolicy({ maxAttempts: 3 });\n\n// Full jitter: random(0, delay)\npolicy.setJitterStrategy(JitterStrategy.FULL);\n\n// Equal jitter: delay/2 + random(0, delay/2)\npolicy.setJitterStrategy(JitterStrategy.EQUAL);\n\n// No jitter\npolicy.setJitterStrategy(JitterStrategy.NONE);\n```\n\n### Retry Presets\n\n```typescript\n// Conservative: 3 attempts, 1s initial, 10s max\nconst conservative = RetryPolicyPresets.conservative();\n\n// Aggressive: 5 attempts, 500ms initial, 5s max\nconst aggressive = RetryPolicyPresets.aggressive();\n\n// Patient: 4 attempts, 2s initial, 30s max\nconst patient = RetryPolicyPresets.patient();\n\n// Quick: 2 attempts, 100ms initial, 1s max\nconst quick = RetryPolicyPresets.quick();\n```\n\n### Create Resilient Function\n\n```typescript\nimport { createResilient } from '@beethovn/circuit-breaker';\n\nconst resilientFetch = createResilient(\n  () => fetch(url),\n  {\n    circuitBreaker: {\n      name: 'fetch-api',\n      failureThreshold: 5,\n      timeout: 60000,\n    },\n    retry: {\n      maxAttempts: 3,\n      initialDelay: 1000,\n    },\n  }\n);\n\n// Use like a normal function\nconst data = await resilientFetch();\n```\n\n## API Reference\n\n### CircuitBreaker\n\n```typescript\nclass CircuitBreaker {\n  constructor(config: CircuitBreakerConfig);\n  \n  async execute<T>(fn: () => Promise<T>, options?: ExecuteOptions): Promise<T>;\n  getStats(): CircuitBreakerStats;\n  getState(): CircuitState;\n  isOpen(): boolean;\n  reset(): void;\n  onStateChange(listener: (event: CircuitStateChangeEvent) => void): void;\n  removeStateChangeListener(listener: (event: CircuitStateChangeEvent) => void): void;\n}\n```\n\n### CircuitBreakerConfig\n\n```typescript\ninterface CircuitBreakerConfig {\n  name: string;\n  failureThreshold: number;      // default: 5\n  successThreshold: number;       // default: 2\n  timeout: number;                // default: 60000 (60s)\n  rollingWindowSize: number;      // default: 10000 (10s)\n  minimumRequests: number;        // default: 5\n  errorFilter?: (error: unknown) => boolean;\n  enableLogging?: boolean;        // default: true\n}\n```\n\n### RetryPolicy\n\n```typescript\nclass RetryPolicy {\n  constructor(config?: Partial<RetryPolicyConfig>);\n  \n  async execute<T>(fn: () => Promise<T>): Promise<T>;\n  setJitterStrategy(strategy: JitterStrategy): void;\n  getConfig(): Required<RetryPolicyConfig>;\n}\n```\n\n### RetryPolicyConfig\n\n```typescript\ninterface RetryPolicyConfig {\n  maxAttempts: number;            // default: 3\n  initialDelay: number;           // default: 1000 (1s)\n  maxDelay: number;               // default: 30000 (30s)\n  backoffMultiplier: number;      // default: 2\n  useJitter: boolean;             // default: true\n  retryableErrorFilter?: (error: unknown) => boolean;\n}\n```\n\n### ResilientExecutor\n\n```typescript\nclass ResilientExecutor {\n  constructor(config: ResilienceConfig);\n  \n  async execute<T>(fn: () => Promise<T>): Promise<T>;\n  getCircuitBreaker(): CircuitBreaker;\n  getRetryPolicy(): RetryPolicy | undefined;\n}\n```\n\n## Integration with Other Packages\n\n### With @beethovn/errors\n\n```typescript\nimport { ErrorFactory } from '@beethovn/errors';\nimport { CircuitBreaker } from '@beethovn/circuit-breaker';\n\nconst breaker = new CircuitBreaker({ name: 'api', failureThreshold: 5 });\n\ntry {\n  await breaker.execute(() => apiCall());\n} catch (error: unknown) {\n  const err = ErrorFactory.fromUnknown(error);\n  logger.error('Circuit breaker execution failed', err);\n}\n```\n\n### With @beethovn/logging\n\n```typescript\nimport { Logger } from '@beethovn/logging';\nimport { CircuitBreaker, CircuitState } from '@beethovn/circuit-breaker';\n\nconst logger = new Logger({ service: 'payment-service' });\nconst breaker = new CircuitBreaker({ name: 'payment-api', failureThreshold: 5 });\n\nbreaker.onStateChange((event) => {\n  if (event.to === CircuitState.OPEN) {\n    logger.error('Circuit breaker opened', {\n      circuit: event.name,\n      stats: event.stats,\n    });\n  } else if (event.to === CircuitState.CLOSED) {\n    logger.info('Circuit breaker closed', {\n      circuit: event.name,\n    });\n  }\n});\n```\n\n## Best Practices\n\n### 1. Choose Appropriate Thresholds\n\n```typescript\n// ✅ Good: Balanced thresholds\nconst breaker = new CircuitBreaker({\n  name: 'external-api',\n  failureThreshold: 5,        // Open after 5 failures\n  successThreshold: 2,         // Close after 2 successes\n  timeout: 60000,              // Wait 60s before retry\n  minimumRequests: 5,          // Need 5 requests min\n});\n\n// ❌ Bad: Too sensitive\nconst badBreaker = new CircuitBreaker({\n  name: 'api',\n  failureThreshold: 1,        // Opens after single failure\n  successThreshold: 10,        // Needs 10 successes\n});\n```\n\n### 2. Use Error Filters\n\n```typescript\n// ✅ Good: Only count real failures\nconst breaker = new CircuitBreaker({\n  name: 'api',\n  failureThreshold: 5,\n  errorFilter: (error) => {\n    // Don't count validation errors (4xx)\n    if (error instanceof ValidationError) return false;\n    if (error instanceof NotFoundError) return false;\n    return true;\n  },\n});\n```\n\n### 3. Monitor State Changes\n\n```typescript\n// ✅ Good: Monitor and alert\nbreaker.onStateChange((event) => {\n  metrics.record(`circuit.${event.name}.state`, event.to);\n  \n  if (event.to === CircuitState.OPEN) {\n    alerting.critical(`Circuit ${event.name} opened`);\n  }\n});\n```\n\n### 4. Combine with Retry Wisely\n\n```typescript\n// ✅ Good: Circuit breaker wraps retry\nconst executor = new ResilientExecutor({\n  circuitBreaker: {\n    name: 'api',\n    failureThreshold: 5,      // Circuit level\n  },\n  retry: {\n    maxAttempts: 2,            // Few retries\n    initialDelay: 100,         // Quick retries\n  },\n});\n\n// ❌ Bad: Too many retries can trigger circuit\nconst badExecutor = new ResilientExecutor({\n  circuitBreaker: {\n    failureThreshold: 3,\n  },\n  retry: {\n    maxAttempts: 10,          // Will trigger circuit quickly\n  },\n});\n```\n\n### 5. Use Jitter in Production\n\n```typescript\n// ✅ Good: Jitter prevents thundering herd\nconst policy = new RetryPolicy({\n  maxAttempts: 3,\n  useJitter: true,            // Randomize delays\n});\n\n// ❌ Bad: No jitter in distributed system\nconst badPolicy = new RetryPolicy({\n  maxAttempts: 3,\n  useJitter: false,           // All clients retry at same time\n});\n```\n\n## Exponential Backoff Calculation\n\n### Delay Formula\n\n```\nbaseDelay = initialDelay × (backoffMultiplier ^ (attempt - 1))\nfinalDelay = min(baseDelay, maxDelay)\n```\n\n### Example (initialDelay=1000, multiplier=2, maxDelay=30000)\n\n| Attempt | Base Delay | With Jitter (Full) |\n|---------|------------|-------------------|\n| 1 | 1000ms | random(0, 1000) |\n| 2 | 2000ms | random(0, 2000) |\n| 3 | 4000ms | random(0, 4000) |\n| 4 | 8000ms | random(0, 8000) |\n| 5 | 16000ms | random(0, 16000) |\n| 6 | 30000ms (capped) | random(0, 30000) |\n\n## Error Handling\n\n```typescript\nimport { CircuitBreakerOpenError } from '@beethovn/circuit-breaker';\n\ntry {\n  await breaker.execute(() => operation());\n} catch (error) {\n  if (error instanceof CircuitBreakerOpenError) {\n    // Circuit is open, service is down\n    console.log('Service unavailable, try again in:', error.metadata?.timeUntilRetry);\n  } else {\n    // Operation failed\n    console.error('Operation error:', error);\n  }\n}\n```\n\n## Performance Considerations\n\n- **Rolling Window**: Old requests are cleaned automatically, minimal memory footprint\n- **State Checks**: O(1) complexity for state checks\n- **Failure Tracking**: O(n) where n = requests in window (typically small)\n- **Logging**: Disable with `enableLogging: false` for high-throughput scenarios\n\n## Testing\n\n```typescript\nimport { describe, it, expect, vi } from 'vitest';\nimport { CircuitBreaker, CircuitState } from '@beethovn/circuit-breaker';\n\ndescribe('MyService', () => {\n  it('should handle circuit breaker', async () => {\n    const breaker = new CircuitBreaker({\n      name: 'test',\n      failureThreshold: 2,\n    });\n\n    const fn = vi.fn().mockRejectedValue(new Error('fail'));\n\n    // Trigger failures\n    try { await breaker.execute(fn); } catch {}\n    try { await breaker.execute(fn); } catch {}\n\n    expect(breaker.getState()).toBe(CircuitState.OPEN);\n  });\n});\n```\n\n## Migration Guide\n\n### From Manual Retry Logic\n\n```typescript\n// Before: Manual retry\nasync function fetchWithRetry() {\n  let attempt = 0;\n  while (attempt < 3) {\n    try {\n      return await fetch(url);\n    } catch (error) {\n      attempt++;\n      if (attempt >= 3) throw error;\n      await sleep(1000 * Math.pow(2, attempt));\n    }\n  }\n}\n\n// After: RetryPolicy\nconst policy = new RetryPolicy({ maxAttempts: 3 });\nconst result = await policy.execute(() => fetch(url));\n```\n\n### From Simple Circuit Breaker\n\n```typescript\n// Before: Manual circuit breaker\nlet failures = 0;\nlet isOpen = false;\n\nasync function callApi() {\n  if (isOpen) throw new Error('Circuit open');\n  \n  try {\n    const result = await api.call();\n    failures = 0;\n    return result;\n  } catch (error) {\n    failures++;\n    if (failures >= 5) isOpen = true;\n    throw error;\n  }\n}\n\n// After: CircuitBreaker\nconst breaker = new CircuitBreaker({\n  name: 'api',\n  failureThreshold: 5,\n});\n\nconst result = await breaker.execute(() => api.call());\n```\n\n## TypeScript Support\n\nFull TypeScript support with strict types:\n\n```typescript\nimport type {\n  CircuitBreakerConfig,\n  CircuitBreakerStats,\n  CircuitStateChangeEvent,\n  RetryPolicyConfig,\n  ResilienceConfig,\n} from '@beethovn/circuit-breaker';\n```\n\n## License\n\nMIT\n\n---\n\n**Package Version:** 1.0.0  \n**Dependencies:** @beethovn/errors, @beethovn/logging  \n**Node Version:** >= 18.0.0\n","readmeFilename":"README.md","_rev":"1-0a7c402d223ab973e040f8b075c1b23b"}