@gkoos/caracal 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/LICENSE +21 -0
- package/README.md +311 -0
- package/dist/chunk-5CXDW7W6.js +202 -0
- package/dist/chunk-5CXDW7W6.js.map +1 -0
- package/dist/circuit-breaker-BSkcV0W_.d.ts +296 -0
- package/dist/fetch.d.ts +58 -0
- package/dist/fetch.js +117 -0
- package/dist/fetch.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +1065 -0
- package/dist/index.js.map +1 -0
- package/dist/postgres.d.ts +28 -0
- package/dist/postgres.js +56 -0
- package/dist/postgres.js.map +1 -0
- package/dist/redis.d.ts +59 -0
- package/dist/redis.js +549 -0
- package/dist/redis.js.map +1 -0
- package/dist/retry-BFP_k3Hg.d.ts +26 -0
- package/dist/testing/index.d.ts +45 -0
- package/dist/testing/index.js +101 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/types-Tf9T76C7.d.ts +187 -0
- package/package.json +127 -0
- package/src/adapters/fetch/adapter.ts +122 -0
- package/src/adapters/fetch/index.ts +15 -0
- package/src/adapters/fetch/retry-after.ts +111 -0
- package/src/adapters/postgres/adapter.ts +102 -0
- package/src/adapters/postgres/index.ts +7 -0
- package/src/coordination/redis/bulkhead.ts +61 -0
- package/src/coordination/redis/circuit-breaker.ts +270 -0
- package/src/coordination/redis/client.ts +78 -0
- package/src/coordination/redis/eval-script.ts +71 -0
- package/src/coordination/redis/keys.ts +32 -0
- package/src/coordination/redis/leases.ts +44 -0
- package/src/coordination/redis/scripts.ts +314 -0
- package/src/core/bulkhead.ts +336 -0
- package/src/core/circuit-breaker.ts +1066 -0
- package/src/core/index.ts +36 -0
- package/src/core/operation.ts +174 -0
- package/src/core/retry.ts +204 -0
- package/src/core/runtime.ts +123 -0
- package/src/core/scope-state-cache.ts +50 -0
- package/src/core/timeout.ts +73 -0
- package/src/core/types.ts +230 -0
- package/src/fetch.ts +17 -0
- package/src/index.ts +49 -0
- package/src/postgres.ts +9 -0
- package/src/redis.ts +8 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/bulkhead.ts","../src/core/scope-state-cache.ts","../src/core/circuit-breaker.ts","../src/core/retry.ts","../src/core/timeout.ts"],"names":["operation","classification","validate","local","distributed","randomUUID"],"mappings":"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","file":"index.js","sourcesContent":["import { randomUUID } from \"node:crypto\"\nimport {\n admissionSignal,\n emitRuntimeEvent,\n withAdmissionSignal,\n withSignal,\n} from \"./runtime.js\"\nimport type { ExecutionContext, Next, Policy } from \"./types.js\"\n\nexport class BulkheadRejectedError extends Error {\n constructor(\n readonly coordination: \"local\" | \"distributed\",\n readonly policyName: string,\n readonly scope: string,\n readonly reason: string,\n ) {\n super(`Bulkhead ${policyName} rejected: ${reason}`)\n this.name = \"BulkheadRejectedError\"\n }\n}\nexport interface LocalBulkheadOptions {\n readonly name: string\n readonly limit: number\n readonly queue?: { readonly limit: number; readonly timeoutMs: number }\n}\n/** Policy-specific capability supplied by caracal/redis. */\nexport interface BulkheadCoordinator {\n command(\n identity: { name: string; operation: string; scope: string },\n action: \"acquire\" | \"renew\" | \"release\",\n token: string,\n leaseMs: number,\n limit: number,\n ): Promise<{ allowed: boolean; occupancy: number }>\n}\nexport interface DistributedBulkheadOptions {\n readonly name: string\n readonly limit: number\n readonly coordinator: BulkheadCoordinator\n readonly scope: (context: ExecutionContext) => string\n readonly leaseMs?: number\n}\nfunction validate(name: string, limit: number) {\n if (!name.trim() || !Number.isSafeInteger(limit) || limit < 1)\n throw new RangeError(\"Bulkhead needs a name and positive integer limit\")\n}\nfunction event(\n context: ExecutionContext,\n coordination: \"local\" | \"distributed\",\n policyName: string,\n scope: string,\n type:\n | \"admitted\"\n | \"rejected\"\n | \"waited\"\n | \"released\"\n | \"lease-lost\"\n | \"degraded\",\n occupancy?: number,\n reason?: string,\n) {\n emitRuntimeEvent(context, {\n type: `bulkhead.${type}`,\n coordination,\n policyName,\n scope,\n ...(occupancy === undefined ? {} : { occupancy }),\n ...(reason === undefined ? {} : { reason }),\n })\n}\nfunction local(options: LocalBulkheadOptions): Policy & {\n readonly coordination: \"local\"\n snapshot(): { coordination: \"local\"; occupancy: number; waiting: number }\n} {\n const { name, limit } = options\n const queue = options.queue && { ...options.queue }\n validate(name, limit)\n if (\n queue &&\n (!Number.isSafeInteger(queue.limit) ||\n queue.limit < 1 ||\n !Number.isSafeInteger(queue.timeoutMs) ||\n queue.timeoutMs < 1 ||\n queue.timeoutMs > 2147483647)\n )\n throw new RangeError(\"Invalid bounded queue\")\n let occupancy = 0\n const waiting: (() => void)[] = []\n return Object.freeze({\n name,\n phase: \"attempt\" as const,\n coordination: \"local\" as const,\n snapshot: () => ({\n coordination: \"local\" as const,\n occupancy,\n waiting: waiting.length,\n }),\n async execute<Result>(\n context: ExecutionContext,\n next: Next<Result>,\n ): Promise<Result> {\n const signal = admissionSignal(context)\n signal?.throwIfAborted()\n const reject = (reason: string) => {\n event(context, \"local\", name, \"process\", \"rejected\", occupancy, reason)\n return new BulkheadRejectedError(\"local\", name, \"process\", reason)\n }\n if (occupancy >= limit) {\n if (!queue || waiting.length >= queue.limit) throw reject(\"capacity\")\n event(context, \"local\", name, \"process\", \"waited\", occupancy)\n await new Promise<void>((resolve, fail) => {\n const cleanup = () => {\n clearTimeout(timer)\n signal?.removeEventListener(\"abort\", abort)\n const i = waiting.indexOf(grant)\n if (i >= 0) waiting.splice(i, 1)\n }\n const grant = () => {\n cleanup()\n occupancy++\n resolve()\n }\n const abort = () => {\n cleanup()\n event(\n context,\n \"local\",\n name,\n \"process\",\n \"rejected\",\n occupancy,\n \"cancelled\",\n )\n fail(signal?.reason)\n }\n const timer = setTimeout(() => {\n cleanup()\n fail(reject(\"wait-timeout\"))\n }, queue.timeoutMs)\n waiting.push(grant)\n signal?.addEventListener(\"abort\", abort, { once: true })\n if (signal?.aborted) abort()\n })\n } else occupancy++\n event(context, \"local\", name, \"process\", \"admitted\", occupancy)\n try {\n signal?.throwIfAborted()\n return await next(context)\n } finally {\n occupancy--\n waiting[0]?.()\n event(context, \"local\", name, \"process\", \"released\", occupancy)\n }\n },\n })\n}\nfunction distributed(\n options: DistributedBulkheadOptions,\n): Policy & { readonly coordination: \"distributed\" } {\n const {\n name,\n limit,\n coordinator,\n scope: resolveScope,\n leaseMs = 30000,\n } = options\n validate(name, limit)\n if (!Number.isSafeInteger(leaseMs) || leaseMs < 100 || leaseMs > 86400000)\n throw new RangeError(\"leaseMs must be 100..86400000\")\n return Object.freeze({\n name,\n phase: \"attempt\" as const,\n coordination: \"distributed\" as const,\n async execute<Result>(\n context: ExecutionContext,\n next: Next<Result>,\n ): Promise<Result> {\n admissionSignal(context)?.throwIfAborted()\n const scope = resolveScope(context)\n if (typeof scope !== \"string\" || !scope.trim())\n throw new TypeError(\"Invalid bulkhead scope\")\n const identity = { name, operation: context.operationName, scope }\n const token = randomUUID()\n const command = (action: \"acquire\" | \"renew\" | \"release\") =>\n coordinator.command(identity, action, token, leaseMs, limit)\n let admitted: { allowed: boolean; occupancy: number }\n const started = performance.now()\n try {\n admitted = await command(\"acquire\")\n } catch (error) {\n event(\n context,\n \"distributed\",\n name,\n scope,\n \"degraded\",\n undefined,\n \"admission-unknown\",\n )\n event(\n context,\n \"distributed\",\n name,\n scope,\n \"rejected\",\n undefined,\n \"coordinator-unavailable\",\n )\n throw error\n }\n if (!admitted.allowed) {\n event(\n context,\n \"distributed\",\n name,\n scope,\n \"rejected\",\n admitted.occupancy,\n \"capacity\",\n )\n throw new BulkheadRejectedError(\"distributed\", name, scope, \"capacity\")\n }\n let stopped = false\n let lost = false\n let timer: ReturnType<typeof setTimeout> | undefined\n let deadlineTimer: ReturnType<typeof setTimeout> | undefined\n let deadline = started + leaseMs\n const controller = new AbortController()\n const lose = () => {\n if (lost || stopped) return\n lost = true\n clearTimeout(timer)\n event(context, \"distributed\", name, scope, \"lease-lost\")\n event(\n context,\n \"distributed\",\n name,\n scope,\n \"degraded\",\n undefined,\n \"lease-uncertain\",\n )\n controller.abort(\n new BulkheadRejectedError(\"distributed\", name, scope, \"lease-lost\"),\n )\n }\n const watch = () => {\n clearTimeout(deadlineTimer)\n deadlineTimer = setTimeout(\n lose,\n Math.max(0, deadline - performance.now()),\n )\n }\n const renew = async () => {\n const sent = performance.now()\n try {\n if (\n !(await command(\"renew\")).allowed ||\n performance.now() >= deadline\n ) {\n lose()\n return\n }\n deadline = sent + leaseMs\n } catch {\n lose()\n return\n }\n if (!stopped && !lost) {\n watch()\n timer = setTimeout(() => void renew(), leaseMs / 3)\n }\n }\n try {\n if (performance.now() >= deadline)\n throw new BulkheadRejectedError(\n \"distributed\",\n name,\n scope,\n \"admission-expired\",\n )\n admissionSignal(context)?.throwIfAborted()\n event(\n context,\n \"distributed\",\n name,\n scope,\n \"admitted\",\n admitted.occupancy,\n )\n watch()\n timer = setTimeout(() => void renew(), leaseMs / 3)\n return await next(\n withAdmissionSignal(\n context.capabilities.abort === \"supported\"\n ? withSignal(\n context,\n context.signal\n ? AbortSignal.any([context.signal, controller.signal])\n : controller.signal,\n )\n : context,\n controller.signal,\n ),\n )\n } finally {\n stopped = true\n clearTimeout(timer)\n clearTimeout(deadlineTimer)\n try {\n const result = await command(\"release\")\n event(\n context,\n \"distributed\",\n name,\n scope,\n \"released\",\n result.occupancy,\n result.allowed ? undefined : \"already-expired-or-released\",\n )\n } catch {\n event(\n context,\n \"distributed\",\n name,\n scope,\n \"degraded\",\n undefined,\n \"release-unknown\",\n )\n }\n }\n },\n })\n}\nexport const bulkhead = Object.freeze({ local, distributed })\n","/**\n * In-process record of the scopes whose last known distributed state was\n * non-closed.\n *\n * It exists for one decision only: when `readState()` fails, a scope last seen\n * OPEN or HALF_OPEN must fail closed, because admitting work to a known-open\n * breaker removes the protection it exists to provide.\n *\n * CLOSED states are deliberately not retained. A missing entry and a CLOSED\n * entry are treated identically by that decision, so storing CLOSED would grow\n * this map with every scope ever observed while changing nothing.\n *\n * Entries are keyed by `(operation, scope)` to match the coordinator identity\n * `(name, operation, scope)`: one policy instance shared by several operations\n * must not read another operation's last known state.\n */\n\n/** Non-closed breaker state retained in process memory. */\nexport type RetainedScopeState = \"open\" | \"half-open\"\n\nexport interface ScopeStateCache {\n remember(operation: string, scope: string, state: RetainedScopeState): void\n forget(operation: string, scope: string): void\n read(operation: string, scope: string): RetainedScopeState | undefined\n /** Number of retained scopes. Internal; used by tests. */\n size(): number\n}\n\nexport function createScopeStateCache(): ScopeStateCache {\n const retained = new Map<string, RetainedScopeState>()\n // JSON encoding keeps the parts unambiguous even if a name contains the\n // separator or other special characters.\n const keyFor = (operation: string, scope: string) =>\n JSON.stringify([operation, scope])\n\n return {\n remember(operation, scope, state) {\n retained.set(keyFor(operation, scope), state)\n },\n forget(operation, scope) {\n retained.delete(keyFor(operation, scope))\n },\n read(operation, scope) {\n return retained.get(keyFor(operation, scope))\n },\n size() {\n return retained.size\n },\n }\n}\n","import { randomUUID } from \"node:crypto\"\nimport { emitRuntimeEvent } from \"./runtime.js\"\nimport { createScopeStateCache } from \"./scope-state-cache.js\"\nimport type { ExecutionContext, Next, Outcome, Policy } from \"./types.js\"\n\n// ---------------------------------------------------------------------------\n// Public types\n// ---------------------------------------------------------------------------\n\nexport type BreakerState = \"closed\" | \"open\" | \"half-open\"\n\n/** Classification of an attempt outcome for circuit-breaker purposes. */\nexport type BreakerOutcome = \"success\" | \"failure\" | \"ignored\"\n\n/**\n * Called after every attempt that the breaker observes. Returning \"ignored\"\n * means the outcome neither counts toward nor clears failures.\n */\nexport type BreakerClassifier = (\n error: unknown,\n isSuccess: boolean,\n) => BreakerOutcome\n\nfunction classifyOutcome(\n context: ExecutionContext,\n classifier: BreakerClassifier | undefined,\n outcome: Outcome<unknown>,\n): BreakerOutcome {\n if (classifier !== undefined) {\n return classifier(\n outcome.status === \"failure\" ? outcome.error : undefined,\n outcome.status === \"success\",\n )\n }\n\n const classification = context.classify(outcome)\n return classification === \"retryable\" ? \"failure\" : classification\n}\n\nexport interface LocalBreakerOptions {\n /** Identifies this policy in events and introspection. */\n readonly name: string\n /**\n * Minimum number of observations in the window before the breaker may open.\n * Default: 5.\n */\n readonly minimumThroughput?: number\n /**\n * Fraction of failures (0–1 exclusive) that triggers opening.\n * Default: 0.5.\n */\n readonly failureThreshold?: number\n /**\n * How long (ms) the breaker stays open before entering half-open.\n * Default: 10 000.\n */\n readonly openMs?: number\n /**\n * Number of consecutive successes needed to close from half-open.\n * Default: 1.\n */\n readonly halfOpenSuccesses?: number\n /**\n * Maximum concurrent probes allowed in half-open state.\n * Default: 1.\n */\n readonly halfOpenProbes?: number\n /**\n * Sliding-window size (number of observations retained).\n * Default: 100.\n */\n readonly windowSize?: number\n /** Custom outcome classifier. Defaults to the adapter classification; retryable counts as failure. */\n readonly classify?: BreakerClassifier\n}\n\nexport interface BreakerSnapshot {\n readonly coordination: \"local\"\n readonly state: BreakerState\n readonly failures: number\n readonly successes: number\n readonly observations: number\n /** Only meaningful in half-open; number of probes currently in-flight. */\n readonly probesInFlight: number\n /** Only meaningful in half-open; consecutive successes so far this epoch. */\n readonly halfOpenSuccesses: number\n}\n\nexport class CircuitOpenError extends Error {\n constructor(\n readonly policyName: string,\n readonly coordination: \"local\" | \"distributed\",\n readonly scope: string,\n ) {\n super(`Circuit ${policyName} is open for scope \"${scope}\"`)\n this.name = \"CircuitOpenError\"\n }\n}\n\n// ---------------------------------------------------------------------------\n// Defaults\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_MINIMUM_THROUGHPUT = 5\nconst DEFAULT_FAILURE_THRESHOLD = 0.5\nconst DEFAULT_OPEN_MS = 10_000\nconst DEFAULT_HALF_OPEN_SUCCESSES = 1\nconst DEFAULT_HALF_OPEN_PROBES = 1\nconst DEFAULT_WINDOW_SIZE = 100\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\n\nfunction validate(opts: LocalBreakerOptions): void {\n if (!opts.name.trim())\n throw new RangeError(\"Circuit breaker name must not be empty\")\n\n const { minimumThroughput = DEFAULT_MINIMUM_THROUGHPUT } = opts\n if (!Number.isInteger(minimumThroughput) || minimumThroughput < 1)\n throw new RangeError(\"minimumThroughput must be a positive integer\")\n\n const { failureThreshold = DEFAULT_FAILURE_THRESHOLD } = opts\n if (\n !Number.isFinite(failureThreshold) ||\n failureThreshold <= 0 ||\n failureThreshold >= 1\n )\n throw new RangeError(\"failureThreshold must be a number in (0, 1)\")\n\n const { openMs = DEFAULT_OPEN_MS } = opts\n if (!Number.isInteger(openMs) || openMs < 1)\n throw new RangeError(\"openMs must be a positive integer\")\n\n const { halfOpenSuccesses = DEFAULT_HALF_OPEN_SUCCESSES } = opts\n if (!Number.isInteger(halfOpenSuccesses) || halfOpenSuccesses < 1)\n throw new RangeError(\"halfOpenSuccesses must be a positive integer\")\n\n const { halfOpenProbes = DEFAULT_HALF_OPEN_PROBES } = opts\n if (!Number.isInteger(halfOpenProbes) || halfOpenProbes < 1)\n throw new RangeError(\"halfOpenProbes must be a positive integer\")\n\n const { windowSize = DEFAULT_WINDOW_SIZE } = opts\n if (!Number.isInteger(windowSize) || windowSize < 1)\n throw new RangeError(\"windowSize must be a positive integer\")\n}\n\n// ---------------------------------------------------------------------------\n// Sliding window (circular buffer of booleans: true = failure)\n// ---------------------------------------------------------------------------\n\nclass SlidingWindow {\n readonly #size: number\n readonly #buf: boolean[]\n #head = 0\n #count = 0\n #failures = 0\n\n constructor(size: number) {\n this.#size = size\n this.#buf = new Array<boolean>(size).fill(false)\n }\n\n record(failure: boolean): void {\n const evicted = this.#buf[this.#head] === true\n if (this.#count === this.#size) {\n if (evicted) this.#failures--\n } else {\n this.#count++\n }\n this.#buf[this.#head] = failure\n if (failure) this.#failures++\n this.#head = (this.#head + 1) % this.#size\n }\n\n get count(): number {\n return this.#count\n }\n\n get failures(): number {\n return this.#failures\n }\n\n get successes(): number {\n return this.#count - this.#failures\n }\n\n reset(): void {\n this.#buf.fill(false)\n this.#head = 0\n this.#count = 0\n this.#failures = 0\n }\n}\n\n// ---------------------------------------------------------------------------\n// Local circuit breaker factory\n// ---------------------------------------------------------------------------\n\ntype LocalBreakerPolicy = Policy & {\n readonly coordination: \"local\"\n snapshot(): BreakerSnapshot\n}\n\nfunction local(options: LocalBreakerOptions): LocalBreakerPolicy {\n validate(options)\n\n const name = options.name\n const minimumThroughput =\n options.minimumThroughput ?? DEFAULT_MINIMUM_THROUGHPUT\n const failureThreshold = options.failureThreshold ?? DEFAULT_FAILURE_THRESHOLD\n const openMs = options.openMs ?? DEFAULT_OPEN_MS\n const halfOpenSuccessTarget =\n options.halfOpenSuccesses ?? DEFAULT_HALF_OPEN_SUCCESSES\n const halfOpenProbeLimit = options.halfOpenProbes ?? DEFAULT_HALF_OPEN_PROBES\n const windowSize = options.windowSize ?? DEFAULT_WINDOW_SIZE\n const classifier = options.classify\n\n let state: BreakerState = \"closed\"\n let generation = 0\n let openedAt = 0\n let halfOpenSuccessCount = 0\n let halfOpenProbesInFlight = 0\n const window = new SlidingWindow(windowSize)\n\n // ---------------------------------------------------------------------------\n // Transition helpers\n // ---------------------------------------------------------------------------\n\n function transitionToOpen(\n context: ExecutionContext,\n previousState: \"closed\" | \"half-open\",\n ): void {\n state = \"open\"\n generation++\n openedAt = Date.now()\n halfOpenSuccessCount = 0\n halfOpenProbesInFlight = 0\n window.reset()\n emitRuntimeEvent(context, {\n type: \"breaker.state-changed\",\n coordination: \"local\",\n policyName: name,\n scope: \"process\",\n state: \"open\",\n previousState,\n })\n }\n\n function transitionToHalfOpen(context: ExecutionContext): void {\n state = \"half-open\"\n generation++\n halfOpenSuccessCount = 0\n halfOpenProbesInFlight = 0\n window.reset()\n emitRuntimeEvent(context, {\n type: \"breaker.state-changed\",\n coordination: \"local\",\n policyName: name,\n scope: \"process\",\n state: \"half-open\",\n previousState: \"open\",\n })\n }\n\n function transitionToClosed(context: ExecutionContext): void {\n state = \"closed\"\n generation++\n halfOpenSuccessCount = 0\n halfOpenProbesInFlight = 0\n window.reset()\n emitRuntimeEvent(context, {\n type: \"breaker.state-changed\",\n coordination: \"local\",\n policyName: name,\n scope: \"process\",\n state: \"closed\",\n previousState: \"half-open\",\n })\n }\n\n function admit(\n context: ExecutionContext,\n ): \"closed\" | \"half-open\" | \"rejected\" {\n if (state === \"closed\") return \"closed\"\n\n if (state === \"open\") {\n if (Date.now() - openedAt >= openMs) {\n transitionToHalfOpen(context)\n // fall through to half-open admission below\n } else {\n emitRuntimeEvent(context, {\n type: \"breaker.rejected\",\n coordination: \"local\",\n policyName: name,\n scope: \"process\",\n state: \"open\",\n })\n return \"rejected\"\n }\n }\n\n // half-open\n if (halfOpenProbesInFlight >= halfOpenProbeLimit) {\n emitRuntimeEvent(context, {\n type: \"breaker.rejected\",\n coordination: \"local\",\n policyName: name,\n scope: \"process\",\n state: \"half-open\",\n })\n return \"rejected\"\n }\n\n halfOpenProbesInFlight++\n emitRuntimeEvent(context, {\n type: \"breaker.probe-started\",\n coordination: \"local\",\n policyName: name,\n scope: \"process\",\n })\n return \"half-open\"\n }\n\n function observe(\n context: ExecutionContext,\n admitted: \"closed\" | \"half-open\",\n admittedGeneration: number,\n settled: Outcome<unknown>,\n ): void {\n // Transitions reset the window and probe accounting. Older work must not\n // contribute observations or release a probe slot in the new generation.\n if (admittedGeneration !== generation || admitted !== state) return\n\n const outcome = classifyOutcome(context, classifier, settled)\n\n if (outcome === \"ignored\") {\n if (admitted === \"half-open\") {\n halfOpenProbesInFlight = Math.max(0, halfOpenProbesInFlight - 1)\n }\n return\n }\n\n const failure = outcome === \"failure\"\n\n emitRuntimeEvent(context, {\n type: \"breaker.observation\",\n coordination: \"local\",\n policyName: name,\n scope: \"process\",\n outcome,\n })\n\n if (admitted === \"half-open\") {\n halfOpenProbesInFlight = Math.max(0, halfOpenProbesInFlight - 1)\n\n if (failure) {\n transitionToOpen(context, \"half-open\")\n return\n }\n\n halfOpenSuccessCount++\n if (halfOpenSuccessCount >= halfOpenSuccessTarget) {\n transitionToClosed(context)\n }\n return\n }\n\n // admitted === \"closed\"\n window.record(failure)\n\n if (\n window.count >= minimumThroughput &&\n window.failures / window.count >= failureThreshold\n ) {\n transitionToOpen(context, \"closed\")\n }\n }\n\n // ---------------------------------------------------------------------------\n // Policy implementation\n // ---------------------------------------------------------------------------\n\n return Object.freeze({\n name,\n coordination: \"local\" as const,\n\n snapshot(): BreakerSnapshot {\n return {\n coordination: \"local\",\n state,\n failures: window.failures,\n successes: window.successes,\n observations: window.count,\n probesInFlight: halfOpenProbesInFlight,\n halfOpenSuccesses: halfOpenSuccessCount,\n }\n },\n\n async execute<Result>(\n context: ExecutionContext,\n next: Next<Result>,\n ): Promise<Result> {\n const admitted = admit(context)\n const admittedGeneration = generation\n\n if (admitted === \"rejected\") {\n throw new CircuitOpenError(name, \"local\", \"process\")\n }\n\n let isSuccess = false\n let value: Result | undefined\n let error: unknown\n\n try {\n value = await next(context)\n isSuccess = true\n return value\n } catch (err) {\n error = err\n throw err\n } finally {\n observe(\n context,\n admitted,\n admittedGeneration,\n isSuccess\n ? { status: \"success\", value }\n : { status: \"failure\", error },\n )\n }\n },\n })\n}\n\nexport interface BreakerIdentity {\n readonly name: string\n readonly operation: string\n readonly scope: string\n}\n\nexport type ObserveResult =\n | { readonly type: \"stale\"; readonly currentGeneration: number }\n | {\n readonly type: \"observed\"\n readonly generation: number\n readonly windowTotal: number\n readonly windowFailures: number\n }\n | {\n readonly type: \"opened\"\n readonly newGeneration: number\n readonly windowTotal: number\n readonly windowFailures: number\n }\n\nexport type AdmitProbeResult =\n | {\n readonly type: \"rejected\"\n readonly reason: \"closed\" | \"open\" | \"probe-limit\"\n readonly generation: number\n }\n | {\n readonly type: \"admitted\"\n readonly generation: number\n readonly probeCount: number\n readonly stateChanged: boolean\n }\n\nexport type SettleProbeResult =\n | { readonly type: \"stale\"; readonly generation: number }\n | {\n readonly type: \"settled\"\n readonly state: BreakerState\n readonly generation: number\n }\n | {\n readonly type: \"transitioned\"\n readonly newState: BreakerState\n readonly newGeneration: number\n readonly previousState: \"half-open\"\n }\n\n/**\n * Policy-specific coordinator capability for `circuitBreaker.distributed()`.\n * The Redis implementation lives in `@gkoos/caracal/redis`; the memory implementation\n * lives in `test/support/memory-coordinator` and must not be a production export.\n */\nexport interface BreakerCoordinator {\n readState(\n identity: BreakerIdentity,\n ): Promise<{ state: BreakerState; generation: number } | null>\n\n observe(\n identity: BreakerIdentity,\n params: {\n readonly generation: number\n readonly outcome: \"success\" | \"failure\"\n readonly uuid: string\n readonly windowTtlMs: number\n readonly minimumThroughput: number\n readonly failureThresholdNumerator: number\n readonly windowSize: number\n readonly openMs: number\n },\n ): Promise<ObserveResult>\n\n admitProbe(\n identity: BreakerIdentity,\n params: {\n readonly probeToken: string\n readonly openMs: number\n readonly halfOpenProbes: number\n readonly probeLeaseTtlMs: number\n },\n ): Promise<AdmitProbeResult>\n\n settleProbe(\n identity: BreakerIdentity,\n params: {\n readonly probeToken: string\n readonly outcome: \"success\" | \"failure\"\n readonly generation: number\n readonly halfOpenSuccesses: number\n readonly openMs: number\n /**\n * Observation retention period, used as a floor for the CLOSED cleanup\n * TTL: the state hash must not expire before the window it governs, or\n * retained members would outlive their epoch. Optional so that custom\n * coordinators keep compiling; the policy always supplies it and the\n * Redis coordinator falls back to `openMs × 2` when it is missing.\n */\n readonly windowTtlMs?: number\n },\n ): Promise<SettleProbeResult>\n}\n\n/**\n * Distributed (Redis-backed) circuit breaker options.\n *\n * The defaults are mutually consistent. Overriding one timer or count usually\n * means revisiting the related ones; see the Redis coordination guide\n * (`docs/redis.md`, \"Keeping the breaker knobs consistent\") for the constraints\n * and the symptoms of getting them wrong.\n */\nexport interface DistributedBreakerOptions {\n /** Identifies this policy in events and introspection. */\n readonly name: string\n /** Redis-backed coordinator. See `redisCircuitBreakerCoordinator` in `@gkoos/caracal/redis`. */\n readonly coordinator: BreakerCoordinator\n /**\n * Maps an execution context to the coordination scope key.\n * `scope: ctx => 'process'` is NOT local mode, it's still Redis-backed\n * with full coordinator-failure semantics.\n */\n readonly scope: (context: ExecutionContext) => string\n /**\n * Minimum observations in the window before the breaker may open. Default: 20.\n *\n * Requires `windowSize >= minimumThroughput`; a smaller window can never\n * reach this count, so the breaker would never open.\n */\n readonly minimumThroughput?: number\n /** Failure fraction (0–1 exclusive) that triggers opening. Default: 0.5. */\n readonly failureThreshold?: number\n /**\n * Sliding-window size (observation count). Default: 100.\n *\n * Must be at least `minimumThroughput`: the count cap trims the window, so a\n * smaller window keeps the observed total below the opening threshold and the\n * breaker never opens.\n */\n readonly windowSize?: number\n /**\n * Observation retention period in ms. Observations older than this are\n * pruned regardless of `windowSize`. Default: max(openMs × 3, 60_000).\n *\n * Must be long enough to accumulate `minimumThroughput` observations at your\n * traffic rate. If pruning fires first the window never fills and the\n * breaker never opens; the default is a proxy for that, not a measurement.\n */\n readonly windowTtlMs?: number\n /** How long (ms) the breaker stays open before half-open. Default: 30_000. */\n readonly openMs?: number\n /** Maximum concurrent half-open probes per scope. Default: 3. */\n readonly halfOpenProbes?: number\n /**\n * Probe successes needed to close from half-open. Default: 2.\n *\n * Any probe failure resets progress back to OPEN, so a value that is large\n * relative to the probe rate keeps traffic throttled long after the\n * downstream recovered.\n */\n readonly halfOpenSuccesses?: number\n /**\n * Probe token TTL in ms. A dead worker's probe expires without blocking\n * recovery. Default: openMs × 2.\n *\n * Must exceed the slowest probe settle time (at least `timeoutMs`): if a live\n * token expires mid-probe the slot is re-issued, more than `halfOpenProbes`\n * probes run concurrently, and the late settle is dropped as stale. It is\n * also the worst case a HALF_OPEN window stalls while crashed workers hold\n * every slot, so do not make it arbitrarily large.\n */\n readonly probeLeaseTtlMs?: number\n /**\n * What to do when the coordinator is unreachable and the last known state\n * for the scope was CLOSED (or no prior successful read has occurred).\n * `\"fail-open\"` allows the attempt through (default).\n * `\"fail-closed\"` rejects it with CircuitOpenError.\n *\n * If the last successfully-read state was OPEN or HALF_OPEN the attempt is\n * always rejected, regardless of this setting. Admitting work into a\n * known-open breaker removes the protection it exists to provide.\n *\n * Coordinator unavailability during probe admission (after a successful\n * readState that returned OPEN/HALF_OPEN) also always fails closed.\n */\n readonly onCoordinatorError?: \"fail-open\" | \"fail-closed\"\n /** Custom outcome classifier. Defaults to the adapter classification; retryable counts as failure. */\n readonly classify?: BreakerClassifier\n}\n\nconst DEFAULT_DIST_MINIMUM_THROUGHPUT = 20\nconst DEFAULT_DIST_FAILURE_THRESHOLD = 0.5\nconst DEFAULT_DIST_WINDOW_SIZE = 100\nconst DEFAULT_DIST_OPEN_MS = 30_000\nconst DEFAULT_DIST_HALF_OPEN_PROBES = 3\nconst DEFAULT_DIST_HALF_OPEN_SUCCESSES = 2\nconst DEFAULT_DIST_ON_COORDINATOR_ERROR = \"fail-open\" as const\n\nfunction validateDistributed(opts: DistributedBreakerOptions): void {\n if (typeof opts.name !== \"string\" || !opts.name.trim())\n throw new RangeError(\"Distributed circuit breaker name must not be empty\")\n if (!opts.coordinator || typeof opts.coordinator !== \"object\")\n throw new TypeError(\"coordinator is required\")\n if (typeof opts.scope !== \"function\")\n throw new TypeError(\"scope must be a function\")\n\n const { minimumThroughput = DEFAULT_DIST_MINIMUM_THROUGHPUT } = opts\n if (!Number.isInteger(minimumThroughput) || minimumThroughput < 1)\n throw new RangeError(\"minimumThroughput must be a positive integer\")\n\n const { failureThreshold = DEFAULT_DIST_FAILURE_THRESHOLD } = opts\n if (\n !Number.isFinite(failureThreshold) ||\n failureThreshold <= 0 ||\n failureThreshold >= 1\n )\n throw new RangeError(\"failureThreshold must be a number in (0, 1)\")\n\n const { openMs = DEFAULT_DIST_OPEN_MS } = opts\n if (!Number.isInteger(openMs) || openMs < 1)\n throw new RangeError(\"openMs must be a positive integer\")\n\n const { halfOpenSuccesses = DEFAULT_DIST_HALF_OPEN_SUCCESSES } = opts\n if (!Number.isInteger(halfOpenSuccesses) || halfOpenSuccesses < 1)\n throw new RangeError(\"halfOpenSuccesses must be a positive integer\")\n\n const { halfOpenProbes = DEFAULT_DIST_HALF_OPEN_PROBES } = opts\n if (!Number.isInteger(halfOpenProbes) || halfOpenProbes < 1)\n throw new RangeError(\"halfOpenProbes must be a positive integer\")\n\n const { windowSize = DEFAULT_DIST_WINDOW_SIZE } = opts\n if (!Number.isInteger(windowSize) || windowSize < 1)\n throw new RangeError(\"windowSize must be a positive integer\")\n\n if (opts.windowTtlMs !== undefined) {\n if (!Number.isInteger(opts.windowTtlMs) || opts.windowTtlMs < 1)\n throw new RangeError(\"windowTtlMs must be a positive integer\")\n }\n\n if (opts.probeLeaseTtlMs !== undefined) {\n if (!Number.isInteger(opts.probeLeaseTtlMs) || opts.probeLeaseTtlMs < 1)\n throw new RangeError(\"probeLeaseTtlMs must be a positive integer\")\n }\n\n if (\n opts.onCoordinatorError !== undefined &&\n opts.onCoordinatorError !== \"fail-open\" &&\n opts.onCoordinatorError !== \"fail-closed\"\n )\n throw new TypeError(\n 'onCoordinatorError must be \"fail-open\" or \"fail-closed\"',\n )\n}\n\ntype DistributedBreakerPolicy = Policy & {\n readonly coordination: \"distributed\"\n}\n\nfunction distributed(\n options: DistributedBreakerOptions,\n): DistributedBreakerPolicy {\n validateDistributed(options)\n\n const name = options.name\n const coordinator = options.coordinator\n const resolveScope = options.scope\n const minimumThroughput =\n options.minimumThroughput ?? DEFAULT_DIST_MINIMUM_THROUGHPUT\n const failureThreshold =\n options.failureThreshold ?? DEFAULT_DIST_FAILURE_THRESHOLD\n const failureThresholdNumerator = Math.round(failureThreshold * 1000)\n const windowSize = options.windowSize ?? DEFAULT_DIST_WINDOW_SIZE\n const openMs = options.openMs ?? DEFAULT_DIST_OPEN_MS\n const windowTtlMs = options.windowTtlMs ?? Math.max(openMs * 3, 60_000)\n const halfOpenProbes = options.halfOpenProbes ?? DEFAULT_DIST_HALF_OPEN_PROBES\n const halfOpenSuccesses =\n options.halfOpenSuccesses ?? DEFAULT_DIST_HALF_OPEN_SUCCESSES\n const probeLeaseTtlMs = options.probeLeaseTtlMs ?? openMs * 2\n const onCoordinatorError =\n options.onCoordinatorError ?? DEFAULT_DIST_ON_COORDINATOR_ERROR\n const classifier = options.classify\n\n // Per-(operation, scope) record of the last known NON-CLOSED state.\n // Used as a fallback when readState() fails: if the last confirmed state was\n // OPEN or HALF_OPEN the breaker must fail-closed regardless of\n // onCoordinatorError, because admitting traffic to a known-open breaker\n // removes the protection the breaker exists to provide.\n // CLOSED is not retained: it is indistinguishable from \"never seen\" for this\n // decision, and storing it would grow without bound with scope cardinality.\n const lastKnownState = createScopeStateCache()\n\n // Admission state tracked per-attempt within a single execute() call.\n type AdmissionState =\n | { readonly kind: \"closed\"; readonly generation: number }\n | {\n readonly kind: \"probe\"\n readonly probeToken: string\n readonly generation: number\n }\n\n return Object.freeze({\n name,\n coordination: \"distributed\" as const,\n\n async execute<Result>(\n context: ExecutionContext,\n next: Next<Result>,\n ): Promise<Result> {\n const scope = resolveScope(context)\n if (typeof scope !== \"string\" || !scope.trim())\n throw new TypeError(\n \"circuitBreaker.distributed scope must be a non-empty string\",\n )\n\n const identity: BreakerIdentity = {\n name,\n operation: context.operationName,\n scope,\n }\n\n // ---- ADMISSION ----\n\n let stateData: { state: BreakerState; generation: number } | null = null\n\n try {\n stateData = await coordinator.readState(identity)\n // Remember only non-closed states: CLOSED is indistinguishable from\n // \"never seen\" for the failure decision below, so storing it would grow\n // this process-local record with every scope ever observed.\n const observed = stateData?.state\n if (observed === \"open\" || observed === \"half-open\") {\n lastKnownState.remember(identity.operation, scope, observed)\n } else {\n lastKnownState.forget(identity.operation, scope)\n }\n } catch (readError) {\n emitRuntimeEvent(context, {\n type: \"breaker.coordinator-error\",\n coordination: \"distributed\",\n policyName: name,\n scope,\n operation: \"admit\",\n error: readError,\n })\n\n // If the last successfully-read state for this scope was OPEN or\n // HALF_OPEN, we know the breaker was non-closed before the outage.\n // Admitting work in that case removes the protection the breaker\n // provides, so we always fail-closed regardless of onCoordinatorError.\n const cached = lastKnownState.read(identity.operation, scope)\n const effectiveBehavior =\n cached === \"open\" || cached === \"half-open\"\n ? \"fail-closed\"\n : onCoordinatorError\n\n emitRuntimeEvent(context, {\n type: \"breaker.degraded\",\n coordination: \"distributed\",\n policyName: name,\n scope,\n reason: \"coordinator-unavailable\",\n behavior: effectiveBehavior,\n })\n\n if (effectiveBehavior === \"fail-closed\") {\n emitRuntimeEvent(context, {\n type: \"breaker.rejected\",\n coordination: \"distributed\",\n policyName: name,\n scope,\n state: \"open\",\n })\n throw new CircuitOpenError(name, \"distributed\", scope)\n }\n // fail-open: last known state was CLOSED (or no prior read succeeded).\n // Treat as CLOSED with generation 0.\n stateData = { state: \"closed\", generation: 0 }\n }\n\n // null from readState means missing hash key → implicit CLOSED / generation 0\n const reportedState: BreakerState = stateData?.state ?? \"closed\"\n const reportedGeneration: number = stateData?.generation ?? 0\n let admission: AdmissionState\n\n if (reportedState === \"closed\") {\n admission = { kind: \"closed\", generation: reportedGeneration }\n } else {\n // OPEN or HALF_OPEN: attempt probe admission\n const probeToken = randomUUID()\n let probeResult: AdmitProbeResult\n\n try {\n probeResult = await coordinator.admitProbe(identity, {\n probeToken,\n openMs,\n halfOpenProbes,\n probeLeaseTtlMs,\n })\n } catch (admitError) {\n // Coordinator unavailable during probe admission → always fail-closed\n emitRuntimeEvent(context, {\n type: \"breaker.coordinator-error\",\n coordination: \"distributed\",\n policyName: name,\n scope,\n operation: \"admit\",\n error: admitError,\n })\n emitRuntimeEvent(context, {\n type: \"breaker.degraded\",\n coordination: \"distributed\",\n policyName: name,\n scope,\n reason: \"coordinator-unavailable\",\n behavior: \"fail-closed\",\n })\n emitRuntimeEvent(context, {\n type: \"breaker.rejected\",\n coordination: \"distributed\",\n policyName: name,\n scope,\n state: reportedState === \"open\" ? \"open\" : \"half-open\",\n })\n throw new CircuitOpenError(name, \"distributed\", scope)\n }\n\n if (probeResult.type === \"rejected\") {\n if (probeResult.reason === \"closed\") {\n // Breaker closed between readState and admitProbe; treat as CLOSED\n lastKnownState.forget(identity.operation, scope)\n admission = { kind: \"closed\", generation: probeResult.generation }\n } else {\n // \"open\" → openMs not yet elapsed; still OPEN\n // \"probe-limit\" → admitProbe atomically transitioned (or was) HALF_OPEN\n const rejState =\n probeResult.reason === \"open\"\n ? (\"open\" as const)\n : (\"half-open\" as const)\n emitRuntimeEvent(context, {\n type: \"breaker.rejected\",\n coordination: \"distributed\",\n policyName: name,\n scope,\n state: rejState,\n generation: probeResult.generation,\n })\n throw new CircuitOpenError(name, \"distributed\", scope)\n }\n } else {\n // Admitted as probe\n if (probeResult.stateChanged) {\n lastKnownState.remember(identity.operation, scope, \"half-open\")\n emitRuntimeEvent(context, {\n type: \"breaker.state-changed\",\n coordination: \"distributed\",\n policyName: name,\n scope,\n state: \"half-open\",\n previousState: \"open\",\n generation: probeResult.generation,\n })\n }\n emitRuntimeEvent(context, {\n type: \"breaker.probe-started\",\n coordination: \"distributed\",\n policyName: name,\n scope,\n generation: probeResult.generation,\n })\n admission = {\n kind: \"probe\",\n probeToken,\n generation: probeResult.generation,\n }\n }\n }\n\n // ---- EXECUTION ----\n\n let isSuccess = false\n let value: Result | undefined\n let thrownError: unknown\n\n try {\n value = await next(context)\n isSuccess = true\n return value\n } catch (err) {\n thrownError = err\n throw err\n } finally {\n // ---- SETTLEMENT ----\n const breakerOutcome = classifyOutcome(\n context,\n classifier,\n isSuccess\n ? { status: \"success\", value }\n : { status: \"failure\", error: thrownError },\n )\n if (breakerOutcome !== \"ignored\") {\n const outcomeStr =\n breakerOutcome === \"success\"\n ? (\"success\" as const)\n : (\"failure\" as const)\n\n if (admission.kind === \"closed\") {\n try {\n const result = await coordinator.observe(identity, {\n generation: admission.generation,\n outcome: outcomeStr,\n uuid: randomUUID(),\n windowTtlMs,\n minimumThroughput,\n failureThresholdNumerator,\n windowSize,\n openMs,\n })\n if (result.type === \"stale\") {\n emitRuntimeEvent(context, {\n type: \"breaker.observation-stale\",\n coordination: \"distributed\",\n policyName: name,\n scope,\n attemptGeneration: admission.generation,\n currentGeneration: result.currentGeneration,\n })\n } else {\n emitRuntimeEvent(context, {\n type: \"breaker.observation\",\n coordination: \"distributed\",\n policyName: name,\n scope,\n outcome: outcomeStr,\n generation: admission.generation,\n })\n if (result.type === \"opened\") {\n lastKnownState.remember(identity.operation, scope, \"open\")\n emitRuntimeEvent(context, {\n type: \"breaker.state-changed\",\n coordination: \"distributed\",\n policyName: name,\n scope,\n state: \"open\",\n previousState: \"closed\",\n generation: result.newGeneration,\n })\n }\n }\n } catch (observeError) {\n // Coordinator error during observation: drop the datapoint — do not\n // fail the caller, one lost observation has low impact. Emit the\n // diagnostic event so operators can detect that the breaker has\n // stopped learning (e.g. sustained coordinator unavailability).\n emitRuntimeEvent(context, {\n type: \"breaker.coordinator-error\",\n coordination: \"distributed\",\n policyName: name,\n scope,\n operation: \"observe\",\n error: observeError,\n })\n }\n } else {\n // Probe settlement\n try {\n const result = await coordinator.settleProbe(identity, {\n probeToken: admission.probeToken,\n outcome: outcomeStr,\n generation: admission.generation,\n halfOpenSuccesses,\n openMs,\n windowTtlMs,\n })\n if (result.type === \"stale\") {\n emitRuntimeEvent(context, {\n type: \"breaker.observation-stale\",\n coordination: \"distributed\",\n policyName: name,\n scope,\n attemptGeneration: admission.generation,\n currentGeneration: result.generation,\n })\n } else {\n emitRuntimeEvent(context, {\n type: \"breaker.observation\",\n coordination: \"distributed\",\n policyName: name,\n scope,\n outcome: outcomeStr,\n generation: admission.generation,\n })\n if (result.type === \"transitioned\") {\n if (result.newState === \"closed\") {\n lastKnownState.forget(identity.operation, scope)\n } else {\n lastKnownState.remember(\n identity.operation,\n scope,\n result.newState,\n )\n }\n emitRuntimeEvent(context, {\n type: \"breaker.state-changed\",\n coordination: \"distributed\",\n policyName: name,\n scope,\n state: result.newState,\n previousState: \"half-open\",\n generation: result.newGeneration,\n })\n }\n }\n } catch (settleError) {\n // Coordinator error during probe settlement: drop the result — the\n // probe token expires via TTL without blocking recovery. Emit the\n // diagnostic event so operators can detect the failure.\n emitRuntimeEvent(context, {\n type: \"breaker.coordinator-error\",\n coordination: \"distributed\",\n policyName: name,\n scope,\n operation: \"settle-probe\",\n error: settleError,\n })\n }\n }\n }\n }\n },\n })\n}\n\nexport const circuitBreaker = Object.freeze({ local, distributed })\n","import { admissionSignal, emitRuntimeEvent, nextAttempt } from \"./runtime.js\"\nimport type {\n Classification,\n ExecutionContext,\n ExecutionMetadata,\n Next,\n OperationCapabilities,\n Outcome,\n Policy,\n} from \"./types.js\"\n\n/**\n * Settled-attempt information passed to a custom `delay` function.\n *\n * `result` and `error` are conveniences over `outcome`; the core never\n * interprets them, so an adapter-specific helper can pace retries from\n * protocol feedback (for example an HTTP `Retry-After` header) without\n * leaking protocol types into the core.\n */\nexport interface RetryContext {\n readonly outcome: Outcome<unknown>\n readonly result: unknown\n readonly error: unknown\n readonly capabilities: OperationCapabilities\n readonly metadata: ExecutionMetadata\n}\n\nexport type RetryDelay =\n | number\n | ((attempt: number, context: RetryContext) => number)\n\nexport interface RetryOptions {\n readonly maxAttempts: number\n readonly delay?: RetryDelay\n}\n\nfunction throwIfAborted(signal: AbortSignal | undefined): void {\n if (!signal?.aborted) {\n return\n }\n\n throw (\n signal.reason ?? new DOMException(\"The operation was aborted\", \"AbortError\")\n )\n}\n\nfunction retryContext(\n context: ExecutionContext,\n outcome: Outcome<unknown>,\n): RetryContext {\n return {\n outcome,\n result: outcome.status === \"success\" ? outcome.value : undefined,\n error: outcome.status === \"failure\" ? outcome.error : undefined,\n capabilities: context.capabilities,\n metadata: context.metadata,\n }\n}\n\nfunction delayFor(\n options: RetryOptions,\n context: ExecutionContext,\n attempt: number,\n outcome: Outcome<unknown>,\n): number {\n const configured = options.delay\n const delay =\n typeof configured === \"function\"\n ? configured(attempt, retryContext(context, outcome))\n : (configured ?? 0)\n if (!Number.isFinite(delay) || delay < 0) {\n throw new RangeError(\"retry delay must be a finite non-negative number\")\n }\n\n return delay\n}\n\nfunction wait(delayMs: number, signal: AbortSignal | undefined): Promise<void> {\n if (delayMs === 0) {\n throwIfAborted(signal)\n return Promise.resolve()\n }\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(done, delayMs)\n\n function done(): void {\n signal?.removeEventListener(\"abort\", aborted)\n resolve()\n }\n\n function aborted(): void {\n clearTimeout(timer)\n reject(\n signal?.reason ??\n new DOMException(\"The operation was aborted\", \"AbortError\"),\n )\n }\n\n if (signal?.aborted) {\n aborted()\n return\n }\n\n signal?.addEventListener(\"abort\", aborted, { once: true })\n })\n}\n\nfunction summarized<Result>(outcome: Outcome<Result>): Outcome<undefined> {\n return outcome.status === \"success\"\n ? { status: \"success\", value: undefined }\n : { status: \"failure\", error: outcome.error }\n}\n\nfunction classification<Result>(\n context: ExecutionContext,\n outcome: Outcome<Result>,\n): Classification {\n return context.classify(outcome)\n}\n\n/** Retries adapter-classified failures within a single logical invocation. */\nexport function retry(options: RetryOptions): Policy {\n if (!Number.isInteger(options.maxAttempts) || options.maxAttempts < 1) {\n throw new RangeError(\"maxAttempts must be a positive integer\")\n }\n\n return Object.freeze({\n name: \"retry\",\n async execute<Result>(\n initialContext: ExecutionContext,\n next: Next<Result>,\n ): Promise<Result> {\n let context = initialContext\n\n for (;;) {\n throwIfAborted(admissionSignal(context))\n\n try {\n const value = await next(context)\n const outcome: Outcome<Result> = { status: \"success\", value }\n const outcomeClassification = classification(context, outcome)\n if (\n outcomeClassification !== \"retryable\" ||\n context.capabilities.replay !== \"safe\"\n ) {\n return value\n }\n\n if (context.attempt >= options.maxAttempts) {\n emitRuntimeEvent(context, {\n type: \"retry.exhausted\",\n outcome: summarized(outcome),\n classification: outcomeClassification,\n })\n return value\n }\n\n const delayMs = delayFor(options, context, context.attempt, outcome)\n emitRuntimeEvent(context, {\n type: \"retry.scheduled\",\n nextAttempt: context.attempt + 1,\n delayMs,\n outcome: summarized(outcome),\n classification: outcomeClassification,\n })\n await wait(delayMs, admissionSignal(context))\n context = nextAttempt(context)\n } catch (error) {\n const outcome: Outcome<Result> = { status: \"failure\", error }\n const outcomeClassification = classification(context, outcome)\n\n if (\n admissionSignal(context)?.aborted ||\n context.capabilities.replay !== \"safe\" ||\n outcomeClassification !== \"retryable\"\n ) {\n throw error\n }\n\n if (context.attempt >= options.maxAttempts) {\n emitRuntimeEvent(context, {\n type: \"retry.exhausted\",\n outcome: summarized(outcome),\n classification: outcomeClassification,\n })\n throw error\n }\n\n const delayMs = delayFor(options, context, context.attempt, outcome)\n emitRuntimeEvent(context, {\n type: \"retry.scheduled\",\n nextAttempt: context.attempt + 1,\n delayMs,\n outcome: summarized(outcome),\n classification: outcomeClassification,\n })\n await wait(delayMs, admissionSignal(context))\n context = nextAttempt(context)\n }\n }\n },\n })\n}\n","import { emitRuntimeEvent, withAdmissionSignal, withSignal } from \"./runtime.js\"\nimport type { ExecutionContext, Next, Policy } from \"./types.js\"\n\nexport class TimeoutError extends Error {\n readonly timeoutMs: number\n\n constructor(timeoutMs: number) {\n super(`Operation timed out after ${timeoutMs}ms`)\n this.name = \"TimeoutError\"\n this.timeoutMs = timeoutMs\n }\n}\n\nexport interface TimeoutOptions {\n readonly ms: number\n}\n\nfunction combineSignals(\n external: AbortSignal | undefined,\n timeoutController: AbortController,\n): AbortSignal {\n if (external === undefined) {\n return timeoutController.signal\n }\n\n return AbortSignal.any([external, timeoutController.signal])\n}\n\n/** Bounds caller wait time and requests cancellation only when the adapter supports it. */\nexport function timeout(options: TimeoutOptions): Policy {\n if (!Number.isFinite(options.ms) || options.ms <= 0) {\n throw new RangeError(\"timeout ms must be a finite positive number\")\n }\n\n return Object.freeze({\n name: \"timeout\",\n async execute<Result>(\n context: ExecutionContext,\n next: Next<Result>,\n ): Promise<Result> {\n const timeoutError = new TimeoutError(options.ms)\n const supportsAbort = context.capabilities.abort === \"supported\"\n const controller = new AbortController()\n const attemptContext = withAdmissionSignal(\n supportsAbort\n ? withSignal(context, combineSignals(context.signal, controller))\n : context,\n controller.signal,\n )\n let timer: ReturnType<typeof setTimeout> | undefined\n\n const timeoutPromise = new Promise<never>((_resolve, reject) => {\n timer = setTimeout(() => {\n controller.abort(timeoutError)\n emitRuntimeEvent(context, {\n type: \"timeout.triggered\",\n timeoutMs: options.ms,\n abortRequested: supportsAbort,\n })\n reject(timeoutError)\n }, options.ms)\n })\n\n try {\n return await Promise.race([next(attemptContext), timeoutPromise])\n } finally {\n if (timer !== undefined) {\n clearTimeout(timer)\n }\n }\n },\n })\n}\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { QueryResult } from 'pg';
|
|
2
|
+
import { O as OperationCapabilities, C as Classification, A as Adapter } from './types-Tf9T76C7.js';
|
|
3
|
+
|
|
4
|
+
type PostgresReplay = OperationCapabilities["replay"] | ((args: PostgresQueryArgs) => OperationCapabilities["replay"]);
|
|
5
|
+
type PostgresQueryArgs = Readonly<{
|
|
6
|
+
sql: string;
|
|
7
|
+
values?: readonly unknown[];
|
|
8
|
+
/** Application-declared per-query replay trait; never inferred from SQL. */
|
|
9
|
+
replay?: OperationCapabilities["replay"];
|
|
10
|
+
}>;
|
|
11
|
+
interface PostgresQueryable {
|
|
12
|
+
query(query: {
|
|
13
|
+
text: string;
|
|
14
|
+
values?: readonly unknown[];
|
|
15
|
+
}): Promise<QueryResult>;
|
|
16
|
+
}
|
|
17
|
+
interface PostgresAdapterOptions {
|
|
18
|
+
readonly replay?: PostgresReplay;
|
|
19
|
+
readonly classifyError?: (error: unknown) => Classification;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Adapter for node-postgres (`pg`) 8.x query clients/pools. The standard
|
|
23
|
+
* `query()` contract does not provide a portable AbortSignal cancellation path,
|
|
24
|
+
* so this adapter accurately declares abort as unsupported.
|
|
25
|
+
*/
|
|
26
|
+
declare function postgresAdapter(client: PostgresQueryable, options?: PostgresAdapterOptions): Adapter<PostgresQueryArgs, QueryResult>;
|
|
27
|
+
|
|
28
|
+
export { type PostgresAdapterOptions, type PostgresQueryArgs, type PostgresQueryable, type PostgresReplay, postgresAdapter };
|
package/dist/postgres.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// src/adapters/postgres/adapter.ts
|
|
2
|
+
var retryableSqlStates = /* @__PURE__ */ new Set([
|
|
3
|
+
"40001",
|
|
4
|
+
// serialization_failure
|
|
5
|
+
"40P01",
|
|
6
|
+
// deadlock_detected
|
|
7
|
+
"55P03",
|
|
8
|
+
// lock_not_available
|
|
9
|
+
"53300",
|
|
10
|
+
// too_many_connections
|
|
11
|
+
"57P01",
|
|
12
|
+
// admin_shutdown
|
|
13
|
+
"57P02",
|
|
14
|
+
// crash_shutdown
|
|
15
|
+
"57P03"
|
|
16
|
+
// cannot_connect_now
|
|
17
|
+
]);
|
|
18
|
+
function sqlState(error) {
|
|
19
|
+
if (typeof error !== "object" || error === null || !("code" in error)) {
|
|
20
|
+
return void 0;
|
|
21
|
+
}
|
|
22
|
+
return typeof error.code === "string" ? error.code : void 0;
|
|
23
|
+
}
|
|
24
|
+
function defaultErrorClassification(error) {
|
|
25
|
+
const state = sqlState(error);
|
|
26
|
+
return state?.startsWith("08") === true || state !== void 0 && retryableSqlStates.has(state) ? "retryable" : "failure";
|
|
27
|
+
}
|
|
28
|
+
function replayFor(args, configured) {
|
|
29
|
+
if (args.replay !== void 0) {
|
|
30
|
+
return args.replay;
|
|
31
|
+
}
|
|
32
|
+
return typeof configured === "function" ? configured(args) : configured ?? "unknown";
|
|
33
|
+
}
|
|
34
|
+
function postgresAdapter(client, options = {}) {
|
|
35
|
+
return Object.freeze({
|
|
36
|
+
capabilities(args) {
|
|
37
|
+
return { abort: "unsupported", replay: replayFor(args, options.replay) };
|
|
38
|
+
},
|
|
39
|
+
execute(args) {
|
|
40
|
+
return client.query({
|
|
41
|
+
text: args.sql,
|
|
42
|
+
values: args.values === void 0 ? void 0 : [...args.values]
|
|
43
|
+
});
|
|
44
|
+
},
|
|
45
|
+
classify(outcome) {
|
|
46
|
+
if (outcome.status === "success") {
|
|
47
|
+
return "success";
|
|
48
|
+
}
|
|
49
|
+
return options.classifyError?.(outcome.error) ?? defaultErrorClassification(outcome.error);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export { postgresAdapter };
|
|
55
|
+
//# sourceMappingURL=postgres.js.map
|
|
56
|
+
//# sourceMappingURL=postgres.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapters/postgres/adapter.ts"],"names":[],"mappings":";AAgCA,IAAM,kBAAA,uBAAyB,GAAA,CAAI;AAAA,EACjC,OAAA;AAAA;AAAA,EACA,OAAA;AAAA;AAAA,EACA,OAAA;AAAA;AAAA,EACA,OAAA;AAAA;AAAA,EACA,OAAA;AAAA;AAAA,EACA,OAAA;AAAA;AAAA,EACA;AAAA;AACF,CAAC,CAAA;AAED,SAAS,SAAS,KAAA,EAAoC;AACpD,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,UAAU,IAAA,IAAQ,EAAE,UAAU,KAAA,CAAA,EAAQ;AACrE,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,OAAO,OAAO,KAAA,CAAM,IAAA,KAAS,QAAA,GAAW,MAAM,IAAA,GAAO,MAAA;AACvD;AAEA,SAAS,2BAA2B,KAAA,EAAgC;AAClE,EAAA,MAAM,KAAA,GAAQ,SAAS,KAAK,CAAA;AAC5B,EAAA,OAAO,KAAA,EAAO,UAAA,CAAW,IAAI,CAAA,KAAM,IAAA,IAChC,KAAA,KAAU,MAAA,IAAa,kBAAA,CAAmB,GAAA,CAAI,KAAK,CAAA,GAClD,WAAA,GACA,SAAA;AACN;AAEA,SAAS,SAAA,CACP,MACA,UAAA,EACiC;AACjC,EAAA,IAAI,IAAA,CAAK,WAAW,MAAA,EAAW;AAC7B,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AAEA,EAAA,OAAO,OAAO,UAAA,KAAe,UAAA,GACzB,UAAA,CAAW,IAAI,IACd,UAAA,IAAc,SAAA;AACrB;AAOO,SAAS,eAAA,CACd,MAAA,EACA,OAAA,GAAkC,EAAC,EACM;AACzC,EAAA,OAAO,OAAO,MAAA,CAAO;AAAA,IACnB,aAAa,IAAA,EAAgD;AAC3D,MAAA,OAAO,EAAE,OAAO,aAAA,EAAe,MAAA,EAAQ,UAAU,IAAA,EAAM,OAAA,CAAQ,MAAM,CAAA,EAAE;AAAA,IACzE,CAAA;AAAA,IACA,QAAQ,IAAA,EAA+C;AACrD,MAAA,OAAO,OAAO,KAAA,CAAM;AAAA,QAClB,MAAM,IAAA,CAAK,GAAA;AAAA,QACX,MAAA,EAAQ,KAAK,MAAA,KAAW,MAAA,GAAY,SAAY,CAAC,GAAG,KAAK,MAAM;AAAA,OAChE,CAAA;AAAA,IACH,CAAA;AAAA,IACA,SAAS,OAAA,EAA+C;AACtD,MAAA,IAAI,OAAA,CAAQ,WAAW,SAAA,EAAW;AAChC,QAAA,OAAO,SAAA;AAAA,MACT;AAEA,MAAA,OACE,QAAQ,aAAA,GAAgB,OAAA,CAAQ,KAAK,CAAA,IACrC,0BAAA,CAA2B,QAAQ,KAAK,CAAA;AAAA,IAE5C;AAAA,GACD,CAAA;AACH","file":"postgres.js","sourcesContent":["import type { QueryResult } from \"pg\"\n\nimport type {\n Adapter,\n Classification,\n OperationCapabilities,\n Outcome,\n} from \"../../core/types.js\"\n\nexport type PostgresReplay =\n | OperationCapabilities[\"replay\"]\n | ((args: PostgresQueryArgs) => OperationCapabilities[\"replay\"])\n\nexport type PostgresQueryArgs = Readonly<{\n sql: string\n values?: readonly unknown[]\n /** Application-declared per-query replay trait; never inferred from SQL. */\n replay?: OperationCapabilities[\"replay\"]\n}>\n\nexport interface PostgresQueryable {\n query(query: {\n text: string\n values?: readonly unknown[]\n }): Promise<QueryResult>\n}\n\nexport interface PostgresAdapterOptions {\n readonly replay?: PostgresReplay\n readonly classifyError?: (error: unknown) => Classification\n}\n\nconst retryableSqlStates = new Set([\n \"40001\", // serialization_failure\n \"40P01\", // deadlock_detected\n \"55P03\", // lock_not_available\n \"53300\", // too_many_connections\n \"57P01\", // admin_shutdown\n \"57P02\", // crash_shutdown\n \"57P03\", // cannot_connect_now\n])\n\nfunction sqlState(error: unknown): string | undefined {\n if (typeof error !== \"object\" || error === null || !(\"code\" in error)) {\n return undefined\n }\n\n return typeof error.code === \"string\" ? error.code : undefined\n}\n\nfunction defaultErrorClassification(error: unknown): Classification {\n const state = sqlState(error)\n return state?.startsWith(\"08\") === true ||\n (state !== undefined && retryableSqlStates.has(state))\n ? \"retryable\"\n : \"failure\"\n}\n\nfunction replayFor(\n args: PostgresQueryArgs,\n configured: PostgresReplay | undefined,\n): OperationCapabilities[\"replay\"] {\n if (args.replay !== undefined) {\n return args.replay\n }\n\n return typeof configured === \"function\"\n ? configured(args)\n : (configured ?? \"unknown\")\n}\n\n/**\n * Adapter for node-postgres (`pg`) 8.x query clients/pools. The standard\n * `query()` contract does not provide a portable AbortSignal cancellation path,\n * so this adapter accurately declares abort as unsupported.\n */\nexport function postgresAdapter(\n client: PostgresQueryable,\n options: PostgresAdapterOptions = {},\n): Adapter<PostgresQueryArgs, QueryResult> {\n return Object.freeze({\n capabilities(args: PostgresQueryArgs): OperationCapabilities {\n return { abort: \"unsupported\", replay: replayFor(args, options.replay) }\n },\n execute(args: PostgresQueryArgs): Promise<QueryResult> {\n return client.query({\n text: args.sql,\n values: args.values === undefined ? undefined : [...args.values],\n })\n },\n classify(outcome: Outcome<QueryResult>): Classification {\n if (outcome.status === \"success\") {\n return \"success\"\n }\n\n return (\n options.classifyError?.(outcome.error) ??\n defaultErrorClassification(outcome.error)\n )\n },\n })\n}\n"]}
|
package/dist/redis.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { f as BulkheadCoordinator, a as BreakerCoordinator } from './circuit-breaker-BSkcV0W_.js';
|
|
2
|
+
import { Redis, Cluster } from 'ioredis';
|
|
3
|
+
import './types-Tf9T76C7.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Minimal Lua-script capability required by the Redis coordinators.
|
|
7
|
+
*
|
|
8
|
+
* `eval` is always required. `evalsha` is optional so structural test doubles
|
|
9
|
+
* and clients that only expose `eval` keep working; when it is missing the
|
|
10
|
+
* script body is sent with `eval`.
|
|
11
|
+
*/
|
|
12
|
+
interface ScriptClient {
|
|
13
|
+
eval(script: string, numberOfKeys: number, ...args: (string | number)[]): Promise<unknown>;
|
|
14
|
+
evalsha?(sha: string, numberOfKeys: number, ...args: (string | number)[]): Promise<unknown>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface RedisScriptClient extends ScriptClient {
|
|
18
|
+
hmget(key: string, ...fields: string[]): Promise<(string | null)[]>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
declare function redisCoordinator(client: RedisScriptClient, options: {
|
|
22
|
+
namespace: string;
|
|
23
|
+
}): BulkheadCoordinator;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Policy-specific Redis coordinator for `circuitBreaker.distributed()`.
|
|
27
|
+
*
|
|
28
|
+
* The caller owns `client` and must connect/disconnect it independently.
|
|
29
|
+
* `namespace` is prepended to every key; use a per-service, per-environment
|
|
30
|
+
* value to avoid cross-deployment state collisions.
|
|
31
|
+
*/
|
|
32
|
+
declare function redisCircuitBreakerCoordinator(client: RedisScriptClient, options: {
|
|
33
|
+
readonly namespace: string;
|
|
34
|
+
}): BreakerCoordinator;
|
|
35
|
+
|
|
36
|
+
declare class CoordinatorUnavailableError extends Error {
|
|
37
|
+
readonly coordination = "distributed";
|
|
38
|
+
constructor(cause: unknown);
|
|
39
|
+
}
|
|
40
|
+
/** Standalone Redis client. Caller owns it and must disconnect at shutdown. */
|
|
41
|
+
declare function createCoordinationClient(url: string, commandTimeout?: number): Redis;
|
|
42
|
+
interface ClusterNode {
|
|
43
|
+
readonly host: string;
|
|
44
|
+
readonly port: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Redis Cluster client. Caller owns it and must disconnect at shutdown.
|
|
48
|
+
*
|
|
49
|
+
* All coordination keys use a hash-tag so that every key for a given
|
|
50
|
+
* policy+operation+scope lands on the same cluster slot. Multi-key Lua
|
|
51
|
+
* scripts (circuit breaker) are therefore cluster-safe without cross-slot
|
|
52
|
+
* concerns.
|
|
53
|
+
*
|
|
54
|
+
* Call `connect()` before use and `disconnect()` at shutdown, matching the
|
|
55
|
+
* standalone client contract.
|
|
56
|
+
*/
|
|
57
|
+
declare function createCoordinationClusterClient(nodes: ReadonlyArray<ClusterNode>, commandTimeout?: number): Cluster;
|
|
58
|
+
|
|
59
|
+
export { type ClusterNode, CoordinatorUnavailableError, createCoordinationClient, createCoordinationClusterClient, redisCircuitBreakerCoordinator, redisCoordinator };
|