From a05555c6e10dbd82d351c53683e4578405d15482 Mon Sep 17 00:00:00 2001 From: "bugbot[bot]" Date: Sat, 10 Oct 2026 09:42:06 +0000 Subject: [PATCH] feat: add onCircuitOpen metrics hook and MetricsHooks interface Co-authored-by: aider (gemini/gemini-3.8-flash) --- packages/smooth-api-ts/src/index.ts | 14 +++++++++++--- packages/smooth-api-ts/src/types.ts | 19 ++++++++++++++----- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/packages/smooth-api-ts/src/index.ts b/packages/smooth-api-ts/src/index.ts index 3f3b743..53102eb 100644 --- a/packages/smooth-api-ts/src/index.ts +++ b/packages/smooth-api-ts/src/index.ts @@ -57,9 +57,17 @@ export function createSmoothFetch(globalConfig: SmoothFetchConfig) { const retryOn = globalConfig.retryOn ?? DEFAULT_RETRY_ON; const breaker = new CircuitBreakerState( globalConfig.circuitBreaker, - globalConfig.onCircuitStateChange - ? (event) => safeInvoke(globalConfig.onCircuitStateChange, event) - : undefined + (event) => { + if (globalConfig.onCircuitStateChange) { + safeInvoke(globalConfig.onCircuitStateChange, event); + } + if (event.to === 'OPEN' && globalConfig.onCircuitOpen) { + safeInvoke(globalConfig.onCircuitOpen, { + domain: event.domain, + failureCount: event.failureCount, + }); + } + } ); const deduplicator = globalConfig.deduplication ? new RequestDeduplicator(globalConfig.deduplication.keyFn) diff --git a/packages/smooth-api-ts/src/types.ts b/packages/smooth-api-ts/src/types.ts index c6e4494..321d156 100644 --- a/packages/smooth-api-ts/src/types.ts +++ b/packages/smooth-api-ts/src/types.ts @@ -55,6 +55,11 @@ export interface CircuitStateChangeEvent { failureCount: number; } +export interface CircuitOpenEvent { + domain: string; + failureCount: number; +} + /** * Custom predicate function to determine if an attempt should be retried. * Receives either (response, undefined) on HTTP completion or (undefined, error) on network/catch errors. @@ -88,8 +93,16 @@ export interface FailureContext { export type RequestSuccessContext = SuccessContext; export type RequestFailureContext = FailureContext; +export interface MetricsHooks { + onRetry?: (context: RetryContext) => void | Promise; + onFailure?: (context: FailureContext) => void | Promise; + onSuccess?: (context: SuccessContext) => void | Promise; + onCircuitOpen?: (event: CircuitOpenEvent) => void | Promise; + onCircuitStateChange?: (event: CircuitStateChangeEvent) => void | Promise; +} + // T types the fallback payload so callers get inference at the use site. -export interface SmoothFetchConfig { +export interface SmoothFetchConfig extends MetricsHooks { backoff?: Partial; circuitBreaker?: Partial; fallback?: T; // returned immediately on an OPEN circuit, no network IO @@ -113,10 +126,6 @@ export interface SmoothFetchConfig { * Must be a positive finite number. */ timeoutMs?: number; - onRetry?: (context: RetryContext) => void | Promise; - onCircuitStateChange?: (event: CircuitStateChangeEvent) => void | Promise; - onSuccess?: (context: SuccessContext) => void | Promise; - onFailure?: (context: FailureContext) => void | Promise; } /** @deprecated use SmoothFetchConfig instead */