{"version":3,"sources":["../../src/nestjs/circuit-breaker.service.ts","../../src/nestjs/circuit-breaker.guard.ts","../../src/nestjs/circuit-breaker.interceptor.ts","../../src/nestjs/circuit-breaker.module.ts","../../src/nestjs/circuit-breaker.decorator.ts"],"names":["CircuitBreakerService","Logger","__decorateClass","Injectable","__decorateParam","CircuitBreakerRegistry","Reflector","CircuitBreakerGuard","isHttpServerError","ServiceUnavailableException","Inject","CircuitBreakerInterceptor","firstValueFrom","timeout","Observable","CircuitBreakerOpenError","TimeoutError","RequestTimeoutException","CircuitBreakerModule","Module"],"mappings":";;;;;;;AAOA,SAAS,SAAS,KAAA,EAAuB;AACvC,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,SAAA,EAAW,GAAG,CAAA;AACrC;AAGaA,gCAAN,2BAAA,CAAuD;AAAA,EAI5D,YAA6D,QAAA,EAAkC;AAAlC,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AAC3D,IAAA,IAAA,CAAK,eAAA,EAAgB;AAAA,EACvB;AAAA,EAF6D,QAAA;AAAA,EAH5C,MAAA,GAAS,IAAIC,aAAA,CAAOD,6BAAA,CAAsB,IAAI,CAAA;AAAA,EACvD,eAAA,GAAyD,IAAA;AAAA,EAMjE,eAAA,GAAwB;AACtB,IAAA,IAAI,KAAK,eAAA,EAAiB;AACxB,MAAA,aAAA,CAAc,KAAK,eAAe,CAAA;AAClC,MAAA,IAAA,CAAK,eAAA,GAAkB,IAAA;AAAA,IACzB;AAAA,EACF;AAAA,EAEA,aAAA,GAAuD;AACrD,IAAA,OAAO,IAAA,CAAK,SAAS,aAAA,EAAc;AAAA,EACrC;AAAA,EAEA,eAAA,GAA2C;AACzC,IAAA,OAAO,IAAA,CAAK,SAAS,eAAA,EAAgB;AAAA,EACvC;AAAA,EAEA,MAAM,IAAA,EAAoB;AACxB,IAAA,IAAA,CAAK,QAAA,CAAS,MAAM,IAAI,CAAA;AAAA,EAC1B;AAAA,EAEA,QAAA,GAAiB;AACf,IAAA,IAAA,CAAK,SAAS,QAAA,EAAS;AAAA,EACzB;AAAA,EAEQ,eAAA,GAAwB;AAC9B,IAAA,IAAA,CAAK,eAAA,GAAkB,YAAY,MAAM;AACvC,MAAA,MAAM,IAAA,GAAO,KAAK,eAAA,EAAgB;AAClC,MAAA,IAAI,IAAA,CAAK,WAAW,CAAA,EAAG;AAEvB,MAAA,MAAM,YAAA,GAAgB,IAAA,CAAK,MAAA,CAAO,CAAA,CAAA,KAAK,EAAE,KAAA,KAAA,MAAA,YAAkC;AAC3E,MAAA,MAAM,gBAAA,GAAmB,IAAA,CAAK,MAAA,CAAO,CAAA,CAAA,KAAK,EAAE,KAAA,KAAA,WAAA,iBAAuC;AAEnF,MAAA,IAAI,YAAA,CAAa,SAAS,CAAA,EAAG;AAC3B,QAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,UACV,CAAA,uBAAA,EAA0B,aAAa,MAAM,CAAA,CAAA;AAAA,UAC7C,YAAA,CAAa,IAAI,CAAA,CAAA,MAAM;AAAA,YACrB,IAAA,EAAa,QAAA,CAAS,CAAA,CAAE,IAAI,CAAA;AAAA,YAC5B,WAAA,EAAa,CAAA,EAAG,CAAA,CAAE,WAAW,CAAA,CAAA,CAAA;AAAA,YAC7B,cAAc,CAAA,CAAE;AAAA,WAClB,CAAE;AAAA,SACJ;AAAA,MACF;AACA,MAAA,IAAI,gBAAA,CAAiB,SAAS,CAAA,EAAG;AAC/B,QAAA,IAAA,CAAK,MAAA,CAAO,GAAA;AAAA,UACV,CAAA,oCAAA,EAAuC,gBAAA,CAAiB,GAAA,CAAI,CAAA,CAAA,KAAK,QAAA,CAAS,CAAA,CAAE,IAAI,CAAC,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,SAC/F;AAAA,MACF;AAAA,IACF,GAAG,GAAM,CAAA;AAAA,EACX;AACF;AAxDaA,6BAAA,GAANE,iCAAA,CAAA;AAAA,EADNC,iBAAA,EAAW;AAAA,EAKGC,mDAAOC,wCAAsB,CAAA;AAAA,CAAA,EAJ/BL,6BAAA,CAAA;ACsBN,IAAM,iBAAA,GAAoB,CAAC,OAAA,KAChCM,cAAA,CAAU,eAAA,CAA4C;AAAA,EACpD,GAAA,EAAK,iBAAA;AAAA,EACL,WAAW,MAAM;AACnB,CAAC;AAGUC,8BAAN,yBAAA,CAAiD;AAAA,EACtD,WAAA,CACsC,WACa,QAAA,EACjD;AAFoC,IAAA,IAAA,CAAA,SAAA,GAAA,SAAA;AACa,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AAAA,EAChD;AAAA,EAFmC,SAAA;AAAA,EACa,QAAA;AAAA,EAGnD,YAAY,OAAA,EAAoC;AAC9C,IAAA,MAAM,OAAA,GAAU,KAAK,SAAA,CAAU,GAAA;AAAA,MAC7B,iBAAA;AAAA,MACA,QAAQ,UAAA;AAAW,KACrB;AACA,IAAA,IAAI,CAAC,SAAS,OAAO,IAAA;AAErB,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,QAAA,CAAS,WAAA,CAAY;AAAA,MACnC,MAAkB,OAAA,CAAQ,IAAA;AAAA,MAC1B,gBAAA,EAAkB,QAAQ,gBAAA,IAAoB,EAAA;AAAA,MAC9C,SAAA,EAAkB,QAAQ,SAAA,IAAaC;AAAA,KACxC,CAAA;AAED,IAAA,IAAI,CAAC,EAAA,CAAG,UAAA,EAAW,EAAG;AACpB,MAAA,MAAM,IAAIC,kCAAA;AAAA,QACR,CAAA,iBAAA,EAAoB,QAAQ,IAAI,CAAA,gCAAA;AAAA,OAClC;AAAA,IACF;AAGA,IAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,YAAA,EAAa,CAAE,UAAA,EAAW;AAClD,IAAA,MAAM,IAAA,GAAkC;AAAA,MACtC,IAAA,EAAM,EAAA,CAAG,UAAA,EAAW,CAAE,IAAA;AAAA,MACtB,KAAA,EAAO,GAAG,QAAA,EAAS;AAAA,MACnB,UAAA,EAAY;AAAA,KACd;AACA,IAAA,OAAA,CAAQ,gBAAgB,CAAA,GAAI,IAAA;AAC5B,IAAA,OAAO,IAAA;AAAA,EACT;AACF;AAnCaF,2BAAA,GAANL,iCAAA,CAAA;AAAA,EADNC,iBAAAA,EAAW;AAAA,EAGPC,iCAAA,CAAA,CAAA,EAAAM,cAAOJ,cAAS,CAAA,CAAA;AAAA,EAChBF,iCAAA,CAAA,CAAA,EAAAM,cAAOL,wCAAsB,CAAA;AAAA,CAAA,EAHrBE,2BAAA,CAAA;AC5Bb,IAAM,kBAAA,GAAqB,GAAA;AAGdI,oCAAN,+BAAA,CAA2D;AAAA,EAChE,YAA6D,QAAA,EAAkC;AAAlC,IAAA,IAAA,CAAA,QAAA,GAAA,QAAA;AAAA,EAAmC;AAAA,EAAnC,QAAA;AAAA,EAE7D,MAAM,SAAA,CAAU,OAAA,EAA2B,IAAA,EAAiD;AAC1F,IAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,UAAA,EAAW,CAAE,IAAA;AACzC,IAAA,MAAM,SAAA,GAAc,OAAA,CAAQ,QAAA,EAAS,CAAE,IAAA;AAEvC,IAAA,MAAM,EAAA,GAAK,IAAA,CAAK,QAAA,CAAS,WAAA,CAAY;AAAA,MACnC,IAAA,EAAW,CAAA,QAAA,EAAW,SAAS,CAAA,CAAA,EAAI,WAAW,CAAA,CAAA;AAAA,MAC9C,SAAA,EAAWH;AAAA,KACZ,CAAA;AAED,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,EAAA,CAAG,OAAA;AAAA,QAAQ,MAC9BI,mBAAA;AAAA,UACE,KAAK,MAAA,EAAO,CAAE,IAAA,CAAKC,YAAA,CAAQ,kBAAkB,CAAC;AAAA;AAChD,OACF;AACA,MAAA,OAAO,IAAIC,gBAAW,CAAA,GAAA,KAAO;AAAE,QAAA,GAAA,CAAI,KAAK,MAAM,CAAA;AAAG,QAAA,GAAA,CAAI,QAAA,EAAS;AAAA,MAAG,CAAC,CAAA;AAAA,IACpE,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,iBAAiBC,yCAAA,EAAyB;AAC5C,QAAA,MAAM,IAAIN,kCAAAA;AAAA,UACR;AAAA,SACF;AAAA,MACF;AACA,MAAA,IAAI,iBAAiBO,iBAAA,EAAc;AACjC,QAAA,MAAM,IAAIC,+BAAwB,mBAAmB,CAAA;AAAA,MACvD;AACA,MAAA,MAAM,KAAA;AAAA,IACR;AAAA,EACF;AACF;AA/BaN,iCAAA,GAANT,iCAAA,CAAA;AAAA,EADNC,iBAAAA,EAAW;AAAA,EAEGC,iCAAA,CAAA,CAAA,EAAAM,cAAOL,wCAAsB,CAAA;AAAA,CAAA,EAD/BM,iCAAA,CAAA;;;ACIAO,+BAAN,0BAAA,CAA2B;AAAC;AAAtBA,4BAAA,GAANhB,iCAAA,CAAA;AAAA,EAdNiB,aAAA,CAAO;AAAA,IACN,SAAA,EAAW;AAAA,MACTd,wCAAA;AAAA,MACAL,6BAAA;AAAA,MACAO,2BAAA;AAAA,MACAI;AAAA,KACF;AAAA,IACA,OAAA,EAAS;AAAA,MACPN,wCAAA;AAAA,MACAL,6BAAA;AAAA,MACAO,2BAAA;AAAA,MACAI;AAAA;AACF,GACD;AAAA,CAAA,EACYO,4BAAA,CAAA;;;ACVN,SAAS,mBAAmB,OAAA,EAMhC;AACD,EAAA,OAAO,SACL,OAAA,EACA,YAAA,EACA,UAAA,EACoB;AACpB,IAAA,MAAM,iBAAiB,UAAA,CAAW,KAAA;AAElC,IAAA,UAAA,CAAW,KAAA,GAAQ,kBAAkC,IAAA,EAAiB;AACpE,MAAA,MAAM,WAAY,IAAA,CACf,sBAAA;AACH,MAAA,IAAI,CAAC,QAAA,EAAU;AACb,QAAA,MAAM,IAAI,KAAA;AAAA,UACR,CAAA,qBAAA,EAAwB,QAAQ,IAAI,CAAA,qJAAA;AAAA,SAGtC;AAAA,MACF;AAEA,MAAA,MAAM,EAAA,GAAK,SAAS,WAAA,CAAY;AAAA,QAC9B,MAAkB,OAAA,CAAQ,IAAA;AAAA,QAC1B,gBAAA,EAAkB,QAAQ,gBAAA,IAAoB,EAAA;AAAA,QAC9C,aAAA,EAAkB,QAAQ,aAAA,IAAoB,GAAA;AAAA,QAC9C,SAAA,EAAkB,QAAQ,SAAA,IAAoBV;AAAA,OAC/C,CAAA;AAED,MAAA,OAAO,EAAA,CAAG,OAAA;AAAA,QACR,MAAM,cAAA,CAAe,KAAA,CAAM,IAAA,EAAM,IAAI,CAAA;AAAA,QACrC,OAAA,CAAQ;AAAA,OACV;AAAA,IACF,CAAA;AAEA,IAAA,OAAO,UAAA;AAAA,EACT,CAAA;AACF","file":"index.cjs","sourcesContent":["import { Injectable, Inject, Logger, OnModuleDestroy } from '@nestjs/common';\nimport { CircuitBreakerRegistry } from '../circuit-breaker/circuit-breaker.registry.js';\nimport { CircuitBreakerMetrics, CircuitBreakerState } from '../circuit-breaker/circuit-breaker.js';\n\n/**\n * Sanitizes a string for safe logging -- prevents log injection via newlines.\n */\nfunction sanitize(value: string): string {\n  return value.replace(/[\\n\\r]/g, '_');\n}\n\n@Injectable()\nexport class CircuitBreakerService implements OnModuleDestroy {\n  private readonly logger = new Logger(CircuitBreakerService.name);\n  private monitorInterval: ReturnType<typeof setInterval> | null = null;\n\n  constructor(@Inject(CircuitBreakerRegistry) private readonly registry: CircuitBreakerRegistry) {\n    this.startMonitoring();\n  }\n\n  onModuleDestroy(): void {\n    if (this.monitorInterval) {\n      clearInterval(this.monitorInterval);\n      this.monitorInterval = null;\n    }\n  }\n\n  getAllMetrics(): Record<string, CircuitBreakerMetrics> {\n    return this.registry.getAllMetrics();\n  }\n\n  getOpenBreakers(): CircuitBreakerMetrics[] {\n    return this.registry.getOpenBreakers();\n  }\n\n  reset(name: string): void {\n    this.registry.reset(name);\n  }\n\n  resetAll(): void {\n    this.registry.resetAll();\n  }\n\n  private startMonitoring(): void {\n    this.monitorInterval = setInterval(() => {\n      const open = this.getOpenBreakers();\n      if (open.length === 0) return;\n\n      const openCircuits  = open.filter(m => m.state === CircuitBreakerState.OPEN);\n      const halfOpenCircuits = open.filter(m => m.state === CircuitBreakerState.HALF_OPEN);\n\n      if (openCircuits.length > 0) {\n        this.logger.warn(\n          `OPEN circuit breakers: ${openCircuits.length}`,\n          openCircuits.map(m => ({\n            name:        sanitize(m.name),\n            failureRate: `${m.failureRate}%`,\n            notPermitted: m.notPermittedCalls,\n          })),\n        );\n      }\n      if (halfOpenCircuits.length > 0) {\n        this.logger.log(\n          `HALF_OPEN circuit breakers probing: ${halfOpenCircuits.map(m => sanitize(m.name)).join(', ')}`,\n        );\n      }\n    }, 60_000);\n  }\n}\n","import {\n  Injectable,\n  Inject,\n  CanActivate,\n  ExecutionContext,\n  ServiceUnavailableException,\n} from '@nestjs/common';\nimport { Reflector } from '@nestjs/core';\nimport { CircuitBreakerRegistry } from '../circuit-breaker/circuit-breaker.registry.js';\nimport { isHttpServerError } from '../circuit-breaker/circuit-breaker.registry.js';\nimport { CircuitBreakerState } from '../circuit-breaker/circuit-breaker.js';\n\nexport interface CircuitBreakerGuardOptions {\n  /** Named circuit breaker to use */\n  name: string;\n  /** Failure threshold % to open the circuit. Default: 50 */\n  failureThreshold?: number;\n  /**\n   * Custom error classifier. Defaults to `isHttpServerError`:\n   * only HTTP 5xx and non-HTTP errors open the circuit.\n   */\n  isFailure?: (error: unknown) => boolean;\n}\n\n/**\n * Immutable DTO exposed on the request object.\n * Contains only read-only state -- never the full CircuitBreaker instance.\n */\nexport interface CircuitBreakerRequestInfo {\n  readonly name: string;\n  readonly state: CircuitBreakerState;\n  readonly canAttempt: boolean;\n}\n\nexport const UseCircuitBreaker = (options: CircuitBreakerGuardOptions) =>\n  Reflector.createDecorator<CircuitBreakerGuardOptions>({\n    key: 'circuit-breaker',\n    transform: () => options,\n  });\n\n@Injectable()\nexport class CircuitBreakerGuard implements CanActivate {\n  constructor(\n    @Inject(Reflector) private readonly reflector: Reflector,\n    @Inject(CircuitBreakerRegistry) private readonly registry: CircuitBreakerRegistry,\n  ) {}\n\n  canActivate(context: ExecutionContext): boolean {\n    const options = this.reflector.get<CircuitBreakerGuardOptions>(\n      'circuit-breaker',\n      context.getHandler(),\n    );\n    if (!options) return true;\n\n    const cb = this.registry.getOrCreate({\n      name:             options.name,\n      failureThreshold: options.failureThreshold ?? 50,\n      isFailure:        options.isFailure ?? isHttpServerError,\n    });\n\n    if (!cb.canAttempt()) {\n      throw new ServiceUnavailableException(\n        `Circuit breaker '${options.name}' is open -- service unavailable`,\n      );\n    }\n\n    // Expose only an immutable DTO, never the full CircuitBreaker instance\n    const request = context.switchToHttp().getRequest() as Record<string, unknown>;\n    const info: CircuitBreakerRequestInfo = {\n      name: cb.getMetrics().name,\n      state: cb.getState(),\n      canAttempt: true,\n    };\n    request['circuitBreaker'] = info;\n    return true;\n  }\n}\n","import {\n  Injectable,\n  Inject,\n  NestInterceptor,\n  ExecutionContext,\n  CallHandler,\n  ServiceUnavailableException,\n  RequestTimeoutException,\n} from '@nestjs/common';\nimport { Observable, firstValueFrom, TimeoutError, timeout } from 'rxjs';\nimport { CircuitBreakerRegistry, isHttpServerError } from '../circuit-breaker/circuit-breaker.registry.js';\nimport { CircuitBreakerOpenError } from '../circuit-breaker/circuit-breaker.js';\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n@Injectable()\nexport class CircuitBreakerInterceptor implements NestInterceptor {\n  constructor(@Inject(CircuitBreakerRegistry) private readonly registry: CircuitBreakerRegistry) {}\n\n  async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<unknown>> {\n    const handlerName = context.getHandler().name;\n    const className   = context.getClass().name;\n\n    const cb = this.registry.getOrCreate({\n      name:      `handler:${className}.${handlerName}`,\n      isFailure: isHttpServerError,\n    });\n\n    try {\n      const result = await cb.execute(() =>\n        firstValueFrom(\n          next.handle().pipe(timeout(DEFAULT_TIMEOUT_MS)),\n        ),\n      );\n      return new Observable(sub => { sub.next(result); sub.complete(); });\n    } catch (error) {\n      if (error instanceof CircuitBreakerOpenError) {\n        throw new ServiceUnavailableException(\n          'Service temporarily unavailable -- circuit breaker is open',\n        );\n      }\n      if (error instanceof TimeoutError) {\n        throw new RequestTimeoutException('Handler timed out');\n      }\n      throw error;\n    }\n  }\n}\n","import { Module } from '@nestjs/common';\nimport { CircuitBreakerRegistry } from '../circuit-breaker/circuit-breaker.registry.js';\nimport { CircuitBreakerService } from './circuit-breaker.service.js';\nimport { CircuitBreakerGuard } from './circuit-breaker.guard.js';\nimport { CircuitBreakerInterceptor } from './circuit-breaker.interceptor.js';\n\n@Module({\n  providers: [\n    CircuitBreakerRegistry,\n    CircuitBreakerService,\n    CircuitBreakerGuard,\n    CircuitBreakerInterceptor,\n  ],\n  exports: [\n    CircuitBreakerRegistry,\n    CircuitBreakerService,\n    CircuitBreakerGuard,\n    CircuitBreakerInterceptor,\n  ],\n})\nexport class CircuitBreakerModule {}\n","import { CircuitBreakerRegistry, isHttpServerError } from '../circuit-breaker/circuit-breaker.registry.js';\n\n/**\n * Method decorator that wraps execution inside a named circuit breaker.\n * The class must have `circuitBreakerRegistry: CircuitBreakerRegistry` injected.\n *\n * Business errors (HTTP 4xx by default) pass through without affecting the circuit state.\n *\n * @throws Error at decoration time if the registry is not injected in the class.\n */\nexport function WithCircuitBreaker(options: {\n  name: string;\n  failureThreshold?: number;\n  openTimeoutMs?: number;\n  isFailure?: (error: unknown) => boolean;\n  fallback?: (error: unknown) => unknown | Promise<unknown>;\n}) {\n  return function (\n    _target: unknown,\n    _propertyKey: string,\n    descriptor: PropertyDescriptor,\n  ): PropertyDescriptor {\n    const originalMethod = descriptor.value as (...args: unknown[]) => Promise<unknown>;\n\n    descriptor.value = async function (this: unknown, ...args: unknown[]) {\n      const registry = (this as { circuitBreakerRegistry?: CircuitBreakerRegistry })\n        .circuitBreakerRegistry;\n      if (!registry) {\n        throw new Error(\n          `@WithCircuitBreaker('${options.name}'): CircuitBreakerRegistry not injected. ` +\n          'Ensure the class has `private readonly circuitBreakerRegistry: CircuitBreakerRegistry` ' +\n          'in its constructor.',\n        );\n      }\n\n      const cb = registry.getOrCreate({\n        name:             options.name,\n        failureThreshold: options.failureThreshold ?? 50,\n        openTimeoutMs:    options.openTimeoutMs    ?? 60_000,\n        isFailure:        options.isFailure        ?? isHttpServerError,\n      });\n\n      return cb.execute(\n        () => originalMethod.apply(this, args),\n        options.fallback as ((error: unknown) => unknown | Promise<unknown>) | undefined,\n      );\n    };\n\n    return descriptor;\n  };\n}\n"]}