@fortify-ts/fallback
Advanced tools
+3
-2
@@ -89,4 +89,5 @@ "use strict"; | ||
| } | ||
| if (this.config.onFallback) { | ||
| this.safeCallback(() => this.config.onFallback(error)); | ||
| const onFallback = this.config.onFallback; | ||
| if (onFallback) { | ||
| this.safeCallback(() => onFallback(error)); | ||
| } | ||
@@ -93,0 +94,0 @@ this.logger.info("Fallback triggered", { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/index.ts","../src/fallback.ts","../src/config.ts"],"sourcesContent":["export { Fallback } from './fallback.js';\nexport { type FallbackConfig, validateFallbackConfig } from './config.js';\n","import {\n type Operation,\n type Pattern,\n type FortifyLogger,\n noopLogger,\n} from '@fortify-ts/core';\nimport { type FallbackConfig, validateFallbackConfig } from './config.js';\n\n/**\n * Fallback pattern implementation for graceful degradation.\n *\n * Executes a primary operation and, if it fails, executes a fallback\n * operation to provide a default value or alternative behavior.\n *\n * @template T - The return type of operations\n *\n * @example\n * ```typescript\n * const fallback = new Fallback<UserData>({\n * fallback: async (signal, error) => {\n * // Return cached data when primary fails\n * return getCachedUserData();\n * },\n * onFallback: (error) => console.log(`Primary failed: ${error.message}`),\n * });\n *\n * const result = await fallback.execute(async (signal) => {\n * return fetchUserDataFromAPI(signal);\n * });\n * ```\n */\nexport class Fallback<T> implements Pattern<T> {\n private readonly config: FallbackConfig<T>;\n private readonly logger: FortifyLogger;\n\n /**\n * Create a new Fallback instance.\n *\n * @param config - Fallback configuration (fallback function is required)\n * @throws {Error} When fallback function is not provided\n */\n constructor(config: FallbackConfig<T>) {\n this.config = validateFallbackConfig(config);\n this.logger = this.config.logger ?? noopLogger;\n }\n\n /**\n * Execute an operation with fallback on failure.\n *\n * @param operation - The primary async operation to execute\n * @param signal - Optional AbortSignal for cancellation\n * @returns Promise resolving to the operation result or fallback result\n * @throws {Error} When both primary and fallback fail\n * @throws {DOMException} When cancelled via signal (AbortError)\n */\n async execute(operation: Operation<T>, signal?: AbortSignal): Promise<T> {\n // Check if cancelled\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n\n const operationSignal = signal ?? new AbortController().signal;\n\n try {\n // Execute primary operation\n const result = await operation(operationSignal);\n\n // Success callback\n if (this.config.onSuccess) {\n this.safeCallback(this.config.onSuccess);\n }\n\n this.logger.debug('Primary operation succeeded');\n return result;\n } catch (error) {\n // Check if cancelled\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n\n // Non-Error throws are re-thrown as-is\n if (!(error instanceof Error)) {\n throw error;\n }\n\n // Check if we should use fallback\n if (this.config.shouldFallback && !this.config.shouldFallback(error)) {\n this.logger.debug('Fallback skipped by shouldFallback', {\n error: error.message,\n });\n throw error;\n }\n\n // Callback before fallback\n if (this.config.onFallback) {\n this.safeCallback(() => this.config.onFallback!(error));\n }\n\n this.logger.info('Fallback triggered', {\n primaryError: error.message,\n });\n\n try {\n // Execute fallback\n const fallbackResult = await this.config.fallback(operationSignal, error);\n this.logger.debug('Fallback succeeded');\n return fallbackResult;\n } catch (fallbackError) {\n this.logger.warn('Fallback failed', {\n fallbackError: fallbackError instanceof Error ? fallbackError.message : String(fallbackError),\n });\n // Return original error, not fallback error\n throw error;\n }\n }\n }\n\n /**\n * Safely execute a callback with error handling.\n */\n private safeCallback(fn: () => void): void {\n try {\n fn();\n } catch (error) {\n this.logger.error('Callback threw an error', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n}\n","import { type FortifyLogger } from '@fortify-ts/core';\n\n/**\n * Configuration for the Fallback pattern.\n *\n * @template T - The return type of operations\n */\nexport interface FallbackConfig<T> {\n /**\n * The fallback function to execute when the primary operation fails.\n * Receives the AbortSignal and the error from the primary operation.\n * Required.\n */\n fallback: (signal: AbortSignal, error: Error) => Promise<T> | T;\n\n /**\n * Determines whether to execute the fallback function for a given error.\n * If not provided or returns true, fallback is always executed on primary failure.\n * Optional.\n */\n shouldFallback?: (error: Error) => boolean;\n\n /**\n * Called when the fallback function is triggered.\n * Receives the error from the primary operation.\n * Optional.\n */\n onFallback?: (error: Error) => void;\n\n /**\n * Called when the primary operation succeeds.\n * Optional.\n */\n onSuccess?: () => void;\n\n /**\n * Logger instance for structured logging.\n * Optional.\n */\n logger?: FortifyLogger;\n}\n\n/**\n * Validate and return the fallback configuration.\n *\n * @param config - Fallback configuration\n * @returns Validated configuration\n * @throws {Error} When fallback function is not provided\n */\nexport function validateFallbackConfig<T>(config: FallbackConfig<T>): FallbackConfig<T> {\n if (!config.fallback) {\n throw new Error('Fallback function is required');\n }\n return config;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAKO;;;AC4CA,SAAS,uBAA0B,QAA8C;AACtF,MAAI,CAAC,OAAO,UAAU;AACpB,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;AACA,SAAO;AACT;;;ADvBO,IAAM,WAAN,MAAwC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAU7C,YAAY,QAA2B;AATvC,wBAAiB;AACjB,wBAAiB;AASf,SAAK,SAAS,uBAAuB,MAAM;AAC3C,SAAK,SAAS,KAAK,OAAO,UAAU;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,QAAQ,WAAyB,QAAkC;AAEvE,QAAI,QAAQ,SAAS;AACnB,YAAM,OAAO,UAAU,IAAI,aAAa,WAAW,YAAY;AAAA,IACjE;AAEA,UAAM,kBAAkB,UAAU,IAAI,gBAAgB,EAAE;AAExD,QAAI;AAEF,YAAM,SAAS,MAAM,UAAU,eAAe;AAG9C,UAAI,KAAK,OAAO,WAAW;AACzB,aAAK,aAAa,KAAK,OAAO,SAAS;AAAA,MACzC;AAEA,WAAK,OAAO,MAAM,6BAA6B;AAC/C,aAAO;AAAA,IACT,SAAS,OAAO;AAEd,UAAI,QAAQ,SAAS;AACnB,cAAM,OAAO,UAAU,IAAI,aAAa,WAAW,YAAY;AAAA,MACjE;AAGA,UAAI,EAAE,iBAAiB,QAAQ;AAC7B,cAAM;AAAA,MACR;AAGA,UAAI,KAAK,OAAO,kBAAkB,CAAC,KAAK,OAAO,eAAe,KAAK,GAAG;AACpE,aAAK,OAAO,MAAM,sCAAsC;AAAA,UACtD,OAAO,MAAM;AAAA,QACf,CAAC;AACD,cAAM;AAAA,MACR;AAGA,UAAI,KAAK,OAAO,YAAY;AAC1B,aAAK,aAAa,MAAM,KAAK,OAAO,WAAY,KAAK,CAAC;AAAA,MACxD;AAEA,WAAK,OAAO,KAAK,sBAAsB;AAAA,QACrC,cAAc,MAAM;AAAA,MACtB,CAAC;AAED,UAAI;AAEF,cAAM,iBAAiB,MAAM,KAAK,OAAO,SAAS,iBAAiB,KAAK;AACxE,aAAK,OAAO,MAAM,oBAAoB;AACtC,eAAO;AAAA,MACT,SAAS,eAAe;AACtB,aAAK,OAAO,KAAK,mBAAmB;AAAA,UAClC,eAAe,yBAAyB,QAAQ,cAAc,UAAU,OAAO,aAAa;AAAA,QAC9F,CAAC;AAED,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,IAAsB;AACzC,QAAI;AACF,SAAG;AAAA,IACL,SAAS,OAAO;AACd,WAAK,OAAO,MAAM,2BAA2B;AAAA,QAC3C,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAC9D,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":[]} | ||
| {"version":3,"sources":["../src/index.ts","../src/fallback.ts","../src/config.ts"],"sourcesContent":["export { Fallback } from './fallback.js';\nexport { type FallbackConfig, validateFallbackConfig } from './config.js';\n","import {\n type Operation,\n type Pattern,\n type FortifyLogger,\n noopLogger,\n} from '@fortify-ts/core';\nimport { type FallbackConfig, validateFallbackConfig } from './config.js';\n\n/**\n * Fallback pattern implementation for graceful degradation.\n *\n * Executes a primary operation and, if it fails, executes a fallback\n * operation to provide a default value or alternative behavior.\n *\n * @template T - The return type of operations\n *\n * @example\n * ```typescript\n * const fallback = new Fallback<UserData>({\n * fallback: async (signal, error) => {\n * // Return cached data when primary fails\n * return getCachedUserData();\n * },\n * onFallback: (error) => console.log(`Primary failed: ${error.message}`),\n * });\n *\n * const result = await fallback.execute(async (signal) => {\n * return fetchUserDataFromAPI(signal);\n * });\n * ```\n */\nexport class Fallback<T> implements Pattern<T> {\n private readonly config: FallbackConfig<T>;\n private readonly logger: FortifyLogger;\n\n /**\n * Create a new Fallback instance.\n *\n * @param config - Fallback configuration (fallback function is required)\n * @throws {Error} When fallback function is not provided\n */\n constructor(config: FallbackConfig<T>) {\n this.config = validateFallbackConfig(config);\n this.logger = this.config.logger ?? noopLogger;\n }\n\n /**\n * Execute an operation with fallback on failure.\n *\n * @param operation - The primary async operation to execute\n * @param signal - Optional AbortSignal for cancellation\n * @returns Promise resolving to the operation result or fallback result\n * @throws {Error} When both primary and fallback fail\n * @throws {DOMException} When cancelled via signal (AbortError)\n */\n async execute(operation: Operation<T>, signal?: AbortSignal): Promise<T> {\n // Check if cancelled\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n\n const operationSignal = signal ?? new AbortController().signal;\n\n try {\n // Execute primary operation\n const result = await operation(operationSignal);\n\n // Success callback\n if (this.config.onSuccess) {\n this.safeCallback(this.config.onSuccess);\n }\n\n this.logger.debug('Primary operation succeeded');\n return result;\n } catch (error) {\n // Check if cancelled\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n\n // Non-Error throws are re-thrown as-is\n if (!(error instanceof Error)) {\n throw error;\n }\n\n // Check if we should use fallback\n if (this.config.shouldFallback && !this.config.shouldFallback(error)) {\n this.logger.debug('Fallback skipped by shouldFallback', {\n error: error.message,\n });\n throw error;\n }\n\n // Callback before fallback\n const onFallback = this.config.onFallback;\n if (onFallback) {\n this.safeCallback(() => onFallback(error));\n }\n\n this.logger.info('Fallback triggered', {\n primaryError: error.message,\n });\n\n try {\n // Execute fallback\n const fallbackResult = await this.config.fallback(operationSignal, error);\n this.logger.debug('Fallback succeeded');\n return fallbackResult;\n } catch (fallbackError) {\n this.logger.warn('Fallback failed', {\n fallbackError: fallbackError instanceof Error ? fallbackError.message : String(fallbackError),\n });\n // Return original error, not fallback error\n throw error;\n }\n }\n }\n\n /**\n * Safely execute a callback with error handling.\n */\n private safeCallback(fn: () => void): void {\n try {\n fn();\n } catch (error) {\n this.logger.error('Callback threw an error', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n}\n","import { type FortifyLogger } from '@fortify-ts/core';\n\n/**\n * Configuration for the Fallback pattern.\n *\n * @template T - The return type of operations\n */\nexport interface FallbackConfig<T> {\n /**\n * The fallback function to execute when the primary operation fails.\n * Receives the AbortSignal and the error from the primary operation.\n * Required.\n */\n fallback: (signal: AbortSignal, error: Error) => Promise<T> | T;\n\n /**\n * Determines whether to execute the fallback function for a given error.\n * If not provided or returns true, fallback is always executed on primary failure.\n * Optional.\n */\n shouldFallback?: (error: Error) => boolean;\n\n /**\n * Called when the fallback function is triggered.\n * Receives the error from the primary operation.\n * Optional.\n */\n onFallback?: (error: Error) => void;\n\n /**\n * Called when the primary operation succeeds.\n * Optional.\n */\n onSuccess?: () => void;\n\n /**\n * Logger instance for structured logging.\n * Optional.\n */\n logger?: FortifyLogger;\n}\n\n/**\n * Validate and return the fallback configuration.\n * Runtime validation for JavaScript users or those bypassing TypeScript.\n *\n * @param config - Fallback configuration\n * @returns Validated configuration\n * @throws {Error} When fallback function is not provided\n */\nexport function validateFallbackConfig<T>(config: FallbackConfig<T>): FallbackConfig<T> {\n // Runtime check for JS users or those using `as any`\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!config.fallback) {\n throw new Error('Fallback function is required');\n }\n return config;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,kBAKO;;;AC6CA,SAAS,uBAA0B,QAA8C;AAGtF,MAAI,CAAC,OAAO,UAAU;AACpB,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;AACA,SAAO;AACT;;;AD1BO,IAAM,WAAN,MAAwC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAU7C,YAAY,QAA2B;AATvC,wBAAiB;AACjB,wBAAiB;AASf,SAAK,SAAS,uBAAuB,MAAM;AAC3C,SAAK,SAAS,KAAK,OAAO,UAAU;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,QAAQ,WAAyB,QAAkC;AAEvE,QAAI,QAAQ,SAAS;AACnB,YAAM,OAAO,UAAU,IAAI,aAAa,WAAW,YAAY;AAAA,IACjE;AAEA,UAAM,kBAAkB,UAAU,IAAI,gBAAgB,EAAE;AAExD,QAAI;AAEF,YAAM,SAAS,MAAM,UAAU,eAAe;AAG9C,UAAI,KAAK,OAAO,WAAW;AACzB,aAAK,aAAa,KAAK,OAAO,SAAS;AAAA,MACzC;AAEA,WAAK,OAAO,MAAM,6BAA6B;AAC/C,aAAO;AAAA,IACT,SAAS,OAAO;AAEd,UAAI,QAAQ,SAAS;AACnB,cAAM,OAAO,UAAU,IAAI,aAAa,WAAW,YAAY;AAAA,MACjE;AAGA,UAAI,EAAE,iBAAiB,QAAQ;AAC7B,cAAM;AAAA,MACR;AAGA,UAAI,KAAK,OAAO,kBAAkB,CAAC,KAAK,OAAO,eAAe,KAAK,GAAG;AACpE,aAAK,OAAO,MAAM,sCAAsC;AAAA,UACtD,OAAO,MAAM;AAAA,QACf,CAAC;AACD,cAAM;AAAA,MACR;AAGA,YAAM,aAAa,KAAK,OAAO;AAC/B,UAAI,YAAY;AACd,aAAK,aAAa,MAAM,WAAW,KAAK,CAAC;AAAA,MAC3C;AAEA,WAAK,OAAO,KAAK,sBAAsB;AAAA,QACrC,cAAc,MAAM;AAAA,MACtB,CAAC;AAED,UAAI;AAEF,cAAM,iBAAiB,MAAM,KAAK,OAAO,SAAS,iBAAiB,KAAK;AACxE,aAAK,OAAO,MAAM,oBAAoB;AACtC,eAAO;AAAA,MACT,SAAS,eAAe;AACtB,aAAK,OAAO,KAAK,mBAAmB;AAAA,UAClC,eAAe,yBAAyB,QAAQ,cAAc,UAAU,OAAO,aAAa;AAAA,QAC9F,CAAC;AAED,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,IAAsB;AACzC,QAAI;AACF,SAAG;AAAA,IACL,SAAS,OAAO;AACd,WAAK,OAAO,MAAM,2BAA2B;AAAA,QAC3C,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAC9D,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":[]} |
+1
-0
@@ -40,2 +40,3 @@ import { FortifyLogger, Pattern, Operation } from '@fortify-ts/core'; | ||
| * Validate and return the fallback configuration. | ||
| * Runtime validation for JavaScript users or those bypassing TypeScript. | ||
| * | ||
@@ -42,0 +43,0 @@ * @param config - Fallback configuration |
+1
-0
@@ -40,2 +40,3 @@ import { FortifyLogger, Pattern, Operation } from '@fortify-ts/core'; | ||
| * Validate and return the fallback configuration. | ||
| * Runtime validation for JavaScript users or those bypassing TypeScript. | ||
| * | ||
@@ -42,0 +43,0 @@ * @param config - Fallback configuration |
+3
-2
@@ -66,4 +66,5 @@ var __defProp = Object.defineProperty; | ||
| } | ||
| if (this.config.onFallback) { | ||
| this.safeCallback(() => this.config.onFallback(error)); | ||
| const onFallback = this.config.onFallback; | ||
| if (onFallback) { | ||
| this.safeCallback(() => onFallback(error)); | ||
| } | ||
@@ -70,0 +71,0 @@ this.logger.info("Fallback triggered", { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/fallback.ts","../src/config.ts"],"sourcesContent":["import {\n type Operation,\n type Pattern,\n type FortifyLogger,\n noopLogger,\n} from '@fortify-ts/core';\nimport { type FallbackConfig, validateFallbackConfig } from './config.js';\n\n/**\n * Fallback pattern implementation for graceful degradation.\n *\n * Executes a primary operation and, if it fails, executes a fallback\n * operation to provide a default value or alternative behavior.\n *\n * @template T - The return type of operations\n *\n * @example\n * ```typescript\n * const fallback = new Fallback<UserData>({\n * fallback: async (signal, error) => {\n * // Return cached data when primary fails\n * return getCachedUserData();\n * },\n * onFallback: (error) => console.log(`Primary failed: ${error.message}`),\n * });\n *\n * const result = await fallback.execute(async (signal) => {\n * return fetchUserDataFromAPI(signal);\n * });\n * ```\n */\nexport class Fallback<T> implements Pattern<T> {\n private readonly config: FallbackConfig<T>;\n private readonly logger: FortifyLogger;\n\n /**\n * Create a new Fallback instance.\n *\n * @param config - Fallback configuration (fallback function is required)\n * @throws {Error} When fallback function is not provided\n */\n constructor(config: FallbackConfig<T>) {\n this.config = validateFallbackConfig(config);\n this.logger = this.config.logger ?? noopLogger;\n }\n\n /**\n * Execute an operation with fallback on failure.\n *\n * @param operation - The primary async operation to execute\n * @param signal - Optional AbortSignal for cancellation\n * @returns Promise resolving to the operation result or fallback result\n * @throws {Error} When both primary and fallback fail\n * @throws {DOMException} When cancelled via signal (AbortError)\n */\n async execute(operation: Operation<T>, signal?: AbortSignal): Promise<T> {\n // Check if cancelled\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n\n const operationSignal = signal ?? new AbortController().signal;\n\n try {\n // Execute primary operation\n const result = await operation(operationSignal);\n\n // Success callback\n if (this.config.onSuccess) {\n this.safeCallback(this.config.onSuccess);\n }\n\n this.logger.debug('Primary operation succeeded');\n return result;\n } catch (error) {\n // Check if cancelled\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n\n // Non-Error throws are re-thrown as-is\n if (!(error instanceof Error)) {\n throw error;\n }\n\n // Check if we should use fallback\n if (this.config.shouldFallback && !this.config.shouldFallback(error)) {\n this.logger.debug('Fallback skipped by shouldFallback', {\n error: error.message,\n });\n throw error;\n }\n\n // Callback before fallback\n if (this.config.onFallback) {\n this.safeCallback(() => this.config.onFallback!(error));\n }\n\n this.logger.info('Fallback triggered', {\n primaryError: error.message,\n });\n\n try {\n // Execute fallback\n const fallbackResult = await this.config.fallback(operationSignal, error);\n this.logger.debug('Fallback succeeded');\n return fallbackResult;\n } catch (fallbackError) {\n this.logger.warn('Fallback failed', {\n fallbackError: fallbackError instanceof Error ? fallbackError.message : String(fallbackError),\n });\n // Return original error, not fallback error\n throw error;\n }\n }\n }\n\n /**\n * Safely execute a callback with error handling.\n */\n private safeCallback(fn: () => void): void {\n try {\n fn();\n } catch (error) {\n this.logger.error('Callback threw an error', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n}\n","import { type FortifyLogger } from '@fortify-ts/core';\n\n/**\n * Configuration for the Fallback pattern.\n *\n * @template T - The return type of operations\n */\nexport interface FallbackConfig<T> {\n /**\n * The fallback function to execute when the primary operation fails.\n * Receives the AbortSignal and the error from the primary operation.\n * Required.\n */\n fallback: (signal: AbortSignal, error: Error) => Promise<T> | T;\n\n /**\n * Determines whether to execute the fallback function for a given error.\n * If not provided or returns true, fallback is always executed on primary failure.\n * Optional.\n */\n shouldFallback?: (error: Error) => boolean;\n\n /**\n * Called when the fallback function is triggered.\n * Receives the error from the primary operation.\n * Optional.\n */\n onFallback?: (error: Error) => void;\n\n /**\n * Called when the primary operation succeeds.\n * Optional.\n */\n onSuccess?: () => void;\n\n /**\n * Logger instance for structured logging.\n * Optional.\n */\n logger?: FortifyLogger;\n}\n\n/**\n * Validate and return the fallback configuration.\n *\n * @param config - Fallback configuration\n * @returns Validated configuration\n * @throws {Error} When fallback function is not provided\n */\nexport function validateFallbackConfig<T>(config: FallbackConfig<T>): FallbackConfig<T> {\n if (!config.fallback) {\n throw new Error('Fallback function is required');\n }\n return config;\n}\n"],"mappings":";;;;;AAAA;AAAA,EAIE;AAAA,OACK;;;AC4CA,SAAS,uBAA0B,QAA8C;AACtF,MAAI,CAAC,OAAO,UAAU;AACpB,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;AACA,SAAO;AACT;;;ADvBO,IAAM,WAAN,MAAwC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAU7C,YAAY,QAA2B;AATvC,wBAAiB;AACjB,wBAAiB;AASf,SAAK,SAAS,uBAAuB,MAAM;AAC3C,SAAK,SAAS,KAAK,OAAO,UAAU;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,QAAQ,WAAyB,QAAkC;AAEvE,QAAI,QAAQ,SAAS;AACnB,YAAM,OAAO,UAAU,IAAI,aAAa,WAAW,YAAY;AAAA,IACjE;AAEA,UAAM,kBAAkB,UAAU,IAAI,gBAAgB,EAAE;AAExD,QAAI;AAEF,YAAM,SAAS,MAAM,UAAU,eAAe;AAG9C,UAAI,KAAK,OAAO,WAAW;AACzB,aAAK,aAAa,KAAK,OAAO,SAAS;AAAA,MACzC;AAEA,WAAK,OAAO,MAAM,6BAA6B;AAC/C,aAAO;AAAA,IACT,SAAS,OAAO;AAEd,UAAI,QAAQ,SAAS;AACnB,cAAM,OAAO,UAAU,IAAI,aAAa,WAAW,YAAY;AAAA,MACjE;AAGA,UAAI,EAAE,iBAAiB,QAAQ;AAC7B,cAAM;AAAA,MACR;AAGA,UAAI,KAAK,OAAO,kBAAkB,CAAC,KAAK,OAAO,eAAe,KAAK,GAAG;AACpE,aAAK,OAAO,MAAM,sCAAsC;AAAA,UACtD,OAAO,MAAM;AAAA,QACf,CAAC;AACD,cAAM;AAAA,MACR;AAGA,UAAI,KAAK,OAAO,YAAY;AAC1B,aAAK,aAAa,MAAM,KAAK,OAAO,WAAY,KAAK,CAAC;AAAA,MACxD;AAEA,WAAK,OAAO,KAAK,sBAAsB;AAAA,QACrC,cAAc,MAAM;AAAA,MACtB,CAAC;AAED,UAAI;AAEF,cAAM,iBAAiB,MAAM,KAAK,OAAO,SAAS,iBAAiB,KAAK;AACxE,aAAK,OAAO,MAAM,oBAAoB;AACtC,eAAO;AAAA,MACT,SAAS,eAAe;AACtB,aAAK,OAAO,KAAK,mBAAmB;AAAA,UAClC,eAAe,yBAAyB,QAAQ,cAAc,UAAU,OAAO,aAAa;AAAA,QAC9F,CAAC;AAED,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,IAAsB;AACzC,QAAI;AACF,SAAG;AAAA,IACL,SAAS,OAAO;AACd,WAAK,OAAO,MAAM,2BAA2B;AAAA,QAC3C,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAC9D,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":[]} | ||
| {"version":3,"sources":["../src/fallback.ts","../src/config.ts"],"sourcesContent":["import {\n type Operation,\n type Pattern,\n type FortifyLogger,\n noopLogger,\n} from '@fortify-ts/core';\nimport { type FallbackConfig, validateFallbackConfig } from './config.js';\n\n/**\n * Fallback pattern implementation for graceful degradation.\n *\n * Executes a primary operation and, if it fails, executes a fallback\n * operation to provide a default value or alternative behavior.\n *\n * @template T - The return type of operations\n *\n * @example\n * ```typescript\n * const fallback = new Fallback<UserData>({\n * fallback: async (signal, error) => {\n * // Return cached data when primary fails\n * return getCachedUserData();\n * },\n * onFallback: (error) => console.log(`Primary failed: ${error.message}`),\n * });\n *\n * const result = await fallback.execute(async (signal) => {\n * return fetchUserDataFromAPI(signal);\n * });\n * ```\n */\nexport class Fallback<T> implements Pattern<T> {\n private readonly config: FallbackConfig<T>;\n private readonly logger: FortifyLogger;\n\n /**\n * Create a new Fallback instance.\n *\n * @param config - Fallback configuration (fallback function is required)\n * @throws {Error} When fallback function is not provided\n */\n constructor(config: FallbackConfig<T>) {\n this.config = validateFallbackConfig(config);\n this.logger = this.config.logger ?? noopLogger;\n }\n\n /**\n * Execute an operation with fallback on failure.\n *\n * @param operation - The primary async operation to execute\n * @param signal - Optional AbortSignal for cancellation\n * @returns Promise resolving to the operation result or fallback result\n * @throws {Error} When both primary and fallback fail\n * @throws {DOMException} When cancelled via signal (AbortError)\n */\n async execute(operation: Operation<T>, signal?: AbortSignal): Promise<T> {\n // Check if cancelled\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n\n const operationSignal = signal ?? new AbortController().signal;\n\n try {\n // Execute primary operation\n const result = await operation(operationSignal);\n\n // Success callback\n if (this.config.onSuccess) {\n this.safeCallback(this.config.onSuccess);\n }\n\n this.logger.debug('Primary operation succeeded');\n return result;\n } catch (error) {\n // Check if cancelled\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n\n // Non-Error throws are re-thrown as-is\n if (!(error instanceof Error)) {\n throw error;\n }\n\n // Check if we should use fallback\n if (this.config.shouldFallback && !this.config.shouldFallback(error)) {\n this.logger.debug('Fallback skipped by shouldFallback', {\n error: error.message,\n });\n throw error;\n }\n\n // Callback before fallback\n const onFallback = this.config.onFallback;\n if (onFallback) {\n this.safeCallback(() => onFallback(error));\n }\n\n this.logger.info('Fallback triggered', {\n primaryError: error.message,\n });\n\n try {\n // Execute fallback\n const fallbackResult = await this.config.fallback(operationSignal, error);\n this.logger.debug('Fallback succeeded');\n return fallbackResult;\n } catch (fallbackError) {\n this.logger.warn('Fallback failed', {\n fallbackError: fallbackError instanceof Error ? fallbackError.message : String(fallbackError),\n });\n // Return original error, not fallback error\n throw error;\n }\n }\n }\n\n /**\n * Safely execute a callback with error handling.\n */\n private safeCallback(fn: () => void): void {\n try {\n fn();\n } catch (error) {\n this.logger.error('Callback threw an error', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n}\n","import { type FortifyLogger } from '@fortify-ts/core';\n\n/**\n * Configuration for the Fallback pattern.\n *\n * @template T - The return type of operations\n */\nexport interface FallbackConfig<T> {\n /**\n * The fallback function to execute when the primary operation fails.\n * Receives the AbortSignal and the error from the primary operation.\n * Required.\n */\n fallback: (signal: AbortSignal, error: Error) => Promise<T> | T;\n\n /**\n * Determines whether to execute the fallback function for a given error.\n * If not provided or returns true, fallback is always executed on primary failure.\n * Optional.\n */\n shouldFallback?: (error: Error) => boolean;\n\n /**\n * Called when the fallback function is triggered.\n * Receives the error from the primary operation.\n * Optional.\n */\n onFallback?: (error: Error) => void;\n\n /**\n * Called when the primary operation succeeds.\n * Optional.\n */\n onSuccess?: () => void;\n\n /**\n * Logger instance for structured logging.\n * Optional.\n */\n logger?: FortifyLogger;\n}\n\n/**\n * Validate and return the fallback configuration.\n * Runtime validation for JavaScript users or those bypassing TypeScript.\n *\n * @param config - Fallback configuration\n * @returns Validated configuration\n * @throws {Error} When fallback function is not provided\n */\nexport function validateFallbackConfig<T>(config: FallbackConfig<T>): FallbackConfig<T> {\n // Runtime check for JS users or those using `as any`\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (!config.fallback) {\n throw new Error('Fallback function is required');\n }\n return config;\n}\n"],"mappings":";;;;;AAAA;AAAA,EAIE;AAAA,OACK;;;AC6CA,SAAS,uBAA0B,QAA8C;AAGtF,MAAI,CAAC,OAAO,UAAU;AACpB,UAAM,IAAI,MAAM,+BAA+B;AAAA,EACjD;AACA,SAAO;AACT;;;AD1BO,IAAM,WAAN,MAAwC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAU7C,YAAY,QAA2B;AATvC,wBAAiB;AACjB,wBAAiB;AASf,SAAK,SAAS,uBAAuB,MAAM;AAC3C,SAAK,SAAS,KAAK,OAAO,UAAU;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,QAAQ,WAAyB,QAAkC;AAEvE,QAAI,QAAQ,SAAS;AACnB,YAAM,OAAO,UAAU,IAAI,aAAa,WAAW,YAAY;AAAA,IACjE;AAEA,UAAM,kBAAkB,UAAU,IAAI,gBAAgB,EAAE;AAExD,QAAI;AAEF,YAAM,SAAS,MAAM,UAAU,eAAe;AAG9C,UAAI,KAAK,OAAO,WAAW;AACzB,aAAK,aAAa,KAAK,OAAO,SAAS;AAAA,MACzC;AAEA,WAAK,OAAO,MAAM,6BAA6B;AAC/C,aAAO;AAAA,IACT,SAAS,OAAO;AAEd,UAAI,QAAQ,SAAS;AACnB,cAAM,OAAO,UAAU,IAAI,aAAa,WAAW,YAAY;AAAA,MACjE;AAGA,UAAI,EAAE,iBAAiB,QAAQ;AAC7B,cAAM;AAAA,MACR;AAGA,UAAI,KAAK,OAAO,kBAAkB,CAAC,KAAK,OAAO,eAAe,KAAK,GAAG;AACpE,aAAK,OAAO,MAAM,sCAAsC;AAAA,UACtD,OAAO,MAAM;AAAA,QACf,CAAC;AACD,cAAM;AAAA,MACR;AAGA,YAAM,aAAa,KAAK,OAAO;AAC/B,UAAI,YAAY;AACd,aAAK,aAAa,MAAM,WAAW,KAAK,CAAC;AAAA,MAC3C;AAEA,WAAK,OAAO,KAAK,sBAAsB;AAAA,QACrC,cAAc,MAAM;AAAA,MACtB,CAAC;AAED,UAAI;AAEF,cAAM,iBAAiB,MAAM,KAAK,OAAO,SAAS,iBAAiB,KAAK;AACxE,aAAK,OAAO,MAAM,oBAAoB;AACtC,eAAO;AAAA,MACT,SAAS,eAAe;AACtB,aAAK,OAAO,KAAK,mBAAmB;AAAA,UAClC,eAAe,yBAAyB,QAAQ,cAAc,UAAU,OAAO,aAAa;AAAA,QAC9F,CAAC;AAED,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAa,IAAsB;AACzC,QAAI;AACF,SAAG;AAAA,IACL,SAAS,OAAO;AACd,WAAK,OAAO,MAAM,2BAA2B;AAAA,QAC3C,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAC9D,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":[]} |
+2
-2
| { | ||
| "name": "@fortify-ts/fallback", | ||
| "version": "0.1.3", | ||
| "version": "0.1.4", | ||
| "description": "Fallback pattern for graceful degradation in @fortify-ts", | ||
@@ -25,3 +25,3 @@ "type": "module", | ||
| "dependencies": { | ||
| "@fortify-ts/core": "0.1.3" | ||
| "@fortify-ts/core": "0.1.4" | ||
| }, | ||
@@ -28,0 +28,0 @@ "devDependencies": { |
31023
2.21%308
0.98%+ Added
- Removed
Updated