@fortify-ts/timeout
Advanced tools
+93
| # @fortify-ts/timeout | ||
| Timeout pattern for the Fortify-TS resilience library. | ||
| ## Installation | ||
| ```bash | ||
| npm install @fortify-ts/timeout | ||
| # or | ||
| pnpm add @fortify-ts/timeout | ||
| ``` | ||
| ## Features | ||
| - **Default and Per-Operation Timeouts**: Configure default with override capability | ||
| - **Abort Signal Integration**: Proper cancellation support | ||
| - **Timeout Notifications**: `onTimeout` callback | ||
| ## Usage | ||
| ### Basic Usage | ||
| ```typescript | ||
| import { Timeout } from '@fortify-ts/timeout'; | ||
| const timeout = new Timeout<Response>({ | ||
| defaultTimeout: 5000, // 5 seconds | ||
| }); | ||
| try { | ||
| const result = await timeout.execute(async (signal) => { | ||
| return fetch('/api/data', { signal }); | ||
| }); | ||
| } catch (error) { | ||
| if (error instanceof TimeoutError) { | ||
| console.log('Request timed out'); | ||
| } | ||
| } | ||
| ``` | ||
| ### Per-Operation Timeout | ||
| ```typescript | ||
| // Use default timeout | ||
| await timeout.execute(operation); | ||
| // Override with specific timeout | ||
| await timeout.executeWithTimeout(operation, 10000); // 10 seconds | ||
| ``` | ||
| ### Configuration Options | ||
| ```typescript | ||
| const timeout = new Timeout<Response>({ | ||
| // Default timeout in milliseconds | ||
| defaultTimeout: 5000, | ||
| // Timeout notification | ||
| onTimeout: (duration) => { | ||
| console.log(`Operation timed out after ${duration}ms`); | ||
| }, | ||
| // Optional logger | ||
| logger: myLogger, | ||
| }); | ||
| ``` | ||
| ### With External Signal | ||
| ```typescript | ||
| const controller = new AbortController(); | ||
| // Both timeout and external signal can cancel | ||
| const result = await timeout.execute( | ||
| async (signal) => fetch('/api/data', { signal }), | ||
| controller.signal | ||
| ); | ||
| // Cancel from external code | ||
| controller.abort(); | ||
| ``` | ||
| ## Configuration Reference | ||
| | Option | Type | Default | Description | | ||
| |--------|------|---------|-------------| | ||
| | `defaultTimeout` | number | 30000 | Default timeout (ms) | | ||
| | `onTimeout` | function | - | Timeout callback | | ||
| | `logger` | FortifyLogger | - | Optional logger | | ||
| ## License | ||
| MIT |
+6
-7
@@ -6,8 +6,7 @@ 'use strict'; | ||
| var __defProp = Object.defineProperty; | ||
| var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
| var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); | ||
| // src/timeout.ts | ||
| var MAX_TIMEOUT_MS = 36e5; | ||
| var timeoutConfigSchema = zod.z.object({ | ||
| /** Default timeout in milliseconds (default: 30000) */ | ||
| defaultTimeout: zod.z.number().int().positive().default(3e4), | ||
| /** Default timeout in milliseconds (default: 30000, max: 1 hour) */ | ||
| defaultTimeout: zod.z.number().int().positive().max(MAX_TIMEOUT_MS).default(3e4), | ||
| /** Callback when timeout occurs */ | ||
@@ -26,2 +25,4 @@ onTimeout: zod.z.function().optional() | ||
| var Timeout = class { | ||
| config; | ||
| logger; | ||
| /** | ||
@@ -33,4 +34,2 @@ * Create a new Timeout instance. | ||
| constructor(config) { | ||
| __publicField(this, "config"); | ||
| __publicField(this, "logger"); | ||
| this.config = parseTimeoutConfig(config); | ||
@@ -37,0 +36,0 @@ this.logger = this.config.logger ?? core.noopLogger; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/config.ts","../src/timeout.ts"],"names":["z","noopLogger","combineSignals","TimeoutError"],"mappings":";;;;;;;;AAMO,IAAM,mBAAA,GAAsBA,MAAE,MAAA,CAAO;AAAA;AAAA,EAE1C,cAAA,EAAgBA,MAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,GAAK,CAAA;AAAA;AAAA,EAEzD,SAAA,EAAWA,KAAA,CAAE,QAAA,EAAS,CAAE,QAAA;AAC1B,CAAC;AA0BM,SAAS,mBAAmB,MAAA,EAAyE;AAC1G,EAAA,MAAM,MAAA,GAAS,mBAAA,CAAoB,KAAA,CAAM,MAAA,IAAU,EAAE,CAAA;AACrD,EAAA,OAAO;AAAA,IACL,GAAG,MAAA;AAAA,IACH,QAAQ,MAAA,EAAQ;AAAA,GAClB;AACF;;;ACfO,IAAM,UAAN,MAAuC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAS5C,YAAY,MAAA,EAA0D;AARtE,IAAA,aAAA,CAAA,IAAA,EAAiB,QAAA,CAAA;AACjB,IAAA,aAAA,CAAA,IAAA,EAAiB,QAAA,CAAA;AAQf,IAAA,IAAA,CAAK,MAAA,GAAS,mBAAmB,MAAM,CAAA;AACvC,IAAA,IAAA,CAAK,MAAA,GAAS,IAAA,CAAK,MAAA,CAAO,MAAA,IAAUC,eAAA;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,OAAA,CACJ,SAAA,EACA,MAAA,EACY;AACZ,IAAA,OAAO,KAAK,kBAAA,CAAmB,SAAA,EAAW,IAAA,CAAK,MAAA,CAAO,gBAAgB,MAAM,CAAA;AAAA,EAC9E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,kBAAA,CACJ,SAAA,EACA,OAAA,EACA,MAAA,EACY;AACZ,IAAA,MAAM,SAAA,GAAY,OAAA;AAGlB,IAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,MAAA,MAAM,MAAA,CAAO,MAAA,IAAU,IAAI,YAAA,CAAa,WAAW,YAAY,CAAA;AAAA,IACjE;AAEA,IAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,IAAA,MAAM,cAAA,GAAiBC,mBAAA,CAAe,MAAA,EAAQ,UAAA,CAAW,MAAM,CAAA;AAE/D,IAAA,IAAI,SAAA;AAEJ,IAAA,MAAM,cAAA,GAAiB,IAAI,OAAA,CAAe,CAAC,GAAG,MAAA,KAAW;AACvD,MAAA,SAAA,GAAY,WAAW,MAAM;AAC3B,QAAA,MAAM,QAAQ,IAAIC,iBAAA;AAAA,UAChB,CAAA,0BAAA,EAA6B,MAAA,CAAO,SAAS,CAAC,CAAA,EAAA,CAAA;AAAA,UAC9C;AAAA,SACF;AACA,QAAA,UAAA,CAAW,MAAM,KAAK,CAAA;AACtB,QAAA,MAAA,CAAO,KAAK,CAAA;AAAA,MACd,GAAG,SAAS,CAAA;AAAA,IACd,CAAC,CAAA;AAED,IAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,kCAAA,EAAoC,EAAE,WAAW,CAAA;AAEnE,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,IAAA,CAAK;AAAA,QAChC,UAAU,cAAc,CAAA;AAAA,QACxB;AAAA,OACD,CAAA;AAED,MAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,kCAAA,EAAoC,EAAE,WAAW,CAAA;AACnE,MAAA,OAAO,MAAA;AAAA,IACT,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,iBAAiBA,iBAAA,EAAc;AACjC,QAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,qBAAA,EAAuB,EAAE,WAAW,CAAA;AACrD,QAAA,IAAA,CAAK,iBAAA,EAAkB;AAAA,MACzB,CAAA,MAAO;AACL,QAAA,IAAA,CAAK,MAAA,CAAO,MAAM,kBAAA,EAAoB;AAAA,UACpC,SAAA;AAAA,UACA,OAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK;AAAA,SAC7D,CAAA;AAAA,MACH;AACA,MAAA,MAAM,KAAA;AAAA,IACR,CAAA,SAAE;AACA,MAAA,IAAI,cAAc,MAAA,EAAW;AAC3B,QAAA,YAAA,CAAa,SAAS,CAAA;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAA,GAA4B;AAC1B,IAAA,OAAO,KAAK,MAAA,CAAO,cAAA;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAA,GAA0B;AAChC,IAAA,IAAI,CAAC,IAAA,CAAK,MAAA,CAAO,SAAA,EAAW;AAE5B,IAAA,IAAI;AACF,MAAA,IAAA,CAAK,OAAO,SAAA,EAAU;AAAA,IACxB,SAAS,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,MAAA,CAAO,MAAM,mCAAA,EAAqC;AAAA,QACrD,OAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK;AAAA,OAC7D,CAAA;AAAA,IACH;AAAA,EACF;AACF","file":"index.cjs","sourcesContent":["import { z } from 'zod';\nimport { type FortifyLogger } from '@fortify-ts/core';\n\n/**\n * Zod schema for Timeout configuration.\n */\nexport const timeoutConfigSchema = z.object({\n /** Default timeout in milliseconds (default: 30000) */\n defaultTimeout: z.number().int().positive().default(30000),\n /** Callback when timeout occurs */\n onTimeout: z.function().optional(),\n});\n\n/**\n * Raw config input type (before defaults are applied).\n */\nexport type TimeoutConfigInput = z.input<typeof timeoutConfigSchema>;\n\n/**\n * Parsed config type (after defaults are applied).\n */\nexport type TimeoutConfigParsed = z.output<typeof timeoutConfigSchema>;\n\n/**\n * Full configuration type including logger.\n */\nexport interface TimeoutConfig extends TimeoutConfigParsed {\n /** Logger instance for structured logging */\n logger: FortifyLogger | undefined;\n}\n\n/**\n * Parse and validate timeout configuration.\n *\n * @param config - Raw configuration input\n * @returns Validated configuration with defaults applied\n */\nexport function parseTimeoutConfig(config?: TimeoutConfigInput & { logger?: FortifyLogger }): TimeoutConfig {\n const parsed = timeoutConfigSchema.parse(config ?? {});\n return {\n ...parsed,\n logger: config?.logger,\n };\n}\n","import {\n type Operation,\n type Pattern,\n TimeoutError,\n combineSignals,\n type FortifyLogger,\n noopLogger,\n} from '@fortify-ts/core';\nimport { type TimeoutConfig, type TimeoutConfigInput, parseTimeoutConfig } from './config.js';\n\n/**\n * Timeout pattern implementation.\n *\n * Wraps operations with a configurable timeout, ensuring they complete\n * within a specified duration or are cancelled.\n *\n * @template T - The return type of operations\n *\n * @example\n * ```typescript\n * const timeout = new Timeout({ defaultTimeout: 5000 });\n *\n * const result = await timeout.execute(async (signal) => {\n * const response = await fetch('/api/data', { signal });\n * return response.json();\n * });\n * ```\n */\nexport class Timeout<T> implements Pattern<T> {\n private readonly config: TimeoutConfig;\n private readonly logger: FortifyLogger;\n\n /**\n * Create a new Timeout instance.\n *\n * @param config - Timeout configuration\n */\n constructor(config?: TimeoutConfigInput & { logger?: FortifyLogger }) {\n this.config = parseTimeoutConfig(config);\n this.logger = this.config.logger ?? noopLogger;\n }\n\n /**\n * Execute an operation with the default timeout.\n *\n * The operation receives an AbortSignal that will be aborted when:\n * - The timeout duration is exceeded\n * - The external signal (if provided) is aborted\n *\n * Operations MUST respect the AbortSignal for proper cancellation.\n *\n * @param operation - The async operation to execute\n * @param signal - Optional external AbortSignal for cancellation\n * @returns Promise resolving to the operation result\n * @throws {TimeoutError} When the operation exceeds the timeout\n * @throws {DOMException} When cancelled via external signal (AbortError)\n */\n async execute(\n operation: Operation<T>,\n signal?: AbortSignal\n ): Promise<T> {\n return this.executeWithTimeout(operation, this.config.defaultTimeout, signal);\n }\n\n /**\n * Execute an operation with a custom timeout duration.\n *\n * Use this method when you need to override the default timeout for a specific call.\n *\n * @param operation - The async operation to execute\n * @param timeout - Timeout duration in milliseconds\n * @param signal - Optional external AbortSignal for cancellation\n * @returns Promise resolving to the operation result\n * @throws {TimeoutError} When the operation exceeds the timeout\n * @throws {DOMException} When cancelled via external signal (AbortError)\n */\n async executeWithTimeout(\n operation: Operation<T>,\n timeout: number,\n signal?: AbortSignal\n ): Promise<T> {\n const timeoutMs = timeout;\n\n // Check if already aborted\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n\n const controller = new AbortController();\n const combinedSignal = combineSignals(signal, controller.signal);\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n const error = new TimeoutError(\n `Operation timed out after ${String(timeoutMs)}ms`,\n timeoutMs\n );\n controller.abort(error);\n reject(error);\n }, timeoutMs);\n });\n\n this.logger.debug('Executing operation with timeout', { timeoutMs });\n\n try {\n const result = await Promise.race([\n operation(combinedSignal),\n timeoutPromise,\n ]);\n\n this.logger.debug('Operation completed successfully', { timeoutMs });\n return result;\n } catch (error) {\n if (error instanceof TimeoutError) {\n this.logger.warn('Operation timed out', { timeoutMs });\n this.safeCallOnTimeout();\n } else {\n this.logger.error('Operation failed', {\n timeoutMs,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n throw error;\n } finally {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n }\n }\n\n /**\n * Get the default timeout duration.\n *\n * @returns Default timeout in milliseconds\n */\n getDefaultTimeout(): number {\n return this.config.defaultTimeout;\n }\n\n /**\n * Safely call the onTimeout callback.\n */\n private safeCallOnTimeout(): void {\n if (!this.config.onTimeout) return;\n\n try {\n this.config.onTimeout();\n } catch (error) {\n this.logger.error('onTimeout callback threw an error', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n}\n"]} | ||
| {"version":3,"sources":["../src/config.ts","../src/timeout.ts"],"names":["z","noopLogger","combineSignals","TimeoutError"],"mappings":"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","file":"index.cjs","sourcesContent":["import { z } from 'zod';\nimport { type FortifyLogger } from '@fortify-ts/core';\n\n/** Maximum allowed timeout in milliseconds (1 hour) */\nconst MAX_TIMEOUT_MS = 3_600_000;\n\n/**\n * Zod schema for Timeout configuration.\n */\nexport const timeoutConfigSchema = z.object({\n /** Default timeout in milliseconds (default: 30000, max: 1 hour) */\n defaultTimeout: z.number().int().positive().max(MAX_TIMEOUT_MS).default(30000),\n /** Callback when timeout occurs */\n onTimeout: z.function().optional(),\n});\n\n/**\n * Raw config input type (before defaults are applied).\n */\nexport type TimeoutConfigInput = z.input<typeof timeoutConfigSchema>;\n\n/**\n * Parsed config type (after defaults are applied).\n */\nexport type TimeoutConfigParsed = z.output<typeof timeoutConfigSchema>;\n\n/**\n * Full configuration type including logger.\n */\nexport interface TimeoutConfig extends TimeoutConfigParsed {\n /** Logger instance for structured logging */\n logger: FortifyLogger | undefined;\n}\n\n/**\n * Parse and validate timeout configuration.\n *\n * @param config - Raw configuration input\n * @returns Validated configuration with defaults applied\n */\nexport function parseTimeoutConfig(config?: TimeoutConfigInput & { logger?: FortifyLogger }): TimeoutConfig {\n const parsed = timeoutConfigSchema.parse(config ?? {});\n return {\n ...parsed,\n logger: config?.logger,\n };\n}\n","import {\n type Operation,\n type Pattern,\n TimeoutError,\n combineSignals,\n type FortifyLogger,\n noopLogger,\n} from '@fortify-ts/core';\nimport { type TimeoutConfig, type TimeoutConfigInput, parseTimeoutConfig } from './config.js';\n\n/**\n * Timeout pattern implementation.\n *\n * Wraps operations with a configurable timeout, ensuring they complete\n * within a specified duration or are cancelled.\n *\n * @template T - The return type of operations\n *\n * @example\n * ```typescript\n * const timeout = new Timeout({ defaultTimeout: 5000 });\n *\n * const result = await timeout.execute(async (signal) => {\n * const response = await fetch('/api/data', { signal });\n * return response.json();\n * });\n * ```\n */\nexport class Timeout<T> implements Pattern<T> {\n private readonly config: TimeoutConfig;\n private readonly logger: FortifyLogger;\n\n /**\n * Create a new Timeout instance.\n *\n * @param config - Timeout configuration\n */\n constructor(config?: TimeoutConfigInput & { logger?: FortifyLogger }) {\n this.config = parseTimeoutConfig(config);\n this.logger = this.config.logger ?? noopLogger;\n }\n\n /**\n * Execute an operation with the default timeout.\n *\n * The operation receives an AbortSignal that will be aborted when:\n * - The timeout duration is exceeded\n * - The external signal (if provided) is aborted\n *\n * Operations MUST respect the AbortSignal for proper cancellation.\n *\n * @param operation - The async operation to execute\n * @param signal - Optional external AbortSignal for cancellation\n * @returns Promise resolving to the operation result\n * @throws {TimeoutError} When the operation exceeds the timeout\n * @throws {DOMException} When cancelled via external signal (AbortError)\n */\n async execute(\n operation: Operation<T>,\n signal?: AbortSignal\n ): Promise<T> {\n return this.executeWithTimeout(operation, this.config.defaultTimeout, signal);\n }\n\n /**\n * Execute an operation with a custom timeout duration.\n *\n * Use this method when you need to override the default timeout for a specific call.\n *\n * @param operation - The async operation to execute\n * @param timeout - Timeout duration in milliseconds\n * @param signal - Optional external AbortSignal for cancellation\n * @returns Promise resolving to the operation result\n * @throws {TimeoutError} When the operation exceeds the timeout\n * @throws {DOMException} When cancelled via external signal (AbortError)\n */\n async executeWithTimeout(\n operation: Operation<T>,\n timeout: number,\n signal?: AbortSignal\n ): Promise<T> {\n const timeoutMs = timeout;\n\n // Check if already aborted\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n\n const controller = new AbortController();\n const combinedSignal = combineSignals(signal, controller.signal);\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n const error = new TimeoutError(\n `Operation timed out after ${String(timeoutMs)}ms`,\n timeoutMs\n );\n controller.abort(error);\n reject(error);\n }, timeoutMs);\n });\n\n this.logger.debug('Executing operation with timeout', { timeoutMs });\n\n try {\n const result = await Promise.race([\n operation(combinedSignal),\n timeoutPromise,\n ]);\n\n this.logger.debug('Operation completed successfully', { timeoutMs });\n return result;\n } catch (error) {\n if (error instanceof TimeoutError) {\n this.logger.warn('Operation timed out', { timeoutMs });\n this.safeCallOnTimeout();\n } else {\n this.logger.error('Operation failed', {\n timeoutMs,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n throw error;\n } finally {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n }\n }\n\n /**\n * Get the default timeout duration.\n *\n * @returns Default timeout in milliseconds\n */\n getDefaultTimeout(): number {\n return this.config.defaultTimeout;\n }\n\n /**\n * Safely call the onTimeout callback.\n */\n private safeCallOnTimeout(): void {\n if (!this.config.onTimeout) return;\n\n try {\n this.config.onTimeout();\n } catch (error) {\n this.logger.error('onTimeout callback threw an error', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n}\n"]} |
+6
-7
| import { noopLogger, combineSignals, TimeoutError } from '@fortify-ts/core'; | ||
| import { z } from 'zod'; | ||
| var __defProp = Object.defineProperty; | ||
| var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; | ||
| var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value); | ||
| // src/timeout.ts | ||
| var MAX_TIMEOUT_MS = 36e5; | ||
| var timeoutConfigSchema = z.object({ | ||
| /** Default timeout in milliseconds (default: 30000) */ | ||
| defaultTimeout: z.number().int().positive().default(3e4), | ||
| /** Default timeout in milliseconds (default: 30000, max: 1 hour) */ | ||
| defaultTimeout: z.number().int().positive().max(MAX_TIMEOUT_MS).default(3e4), | ||
| /** Callback when timeout occurs */ | ||
@@ -23,2 +22,4 @@ onTimeout: z.function().optional() | ||
| var Timeout = class { | ||
| config; | ||
| logger; | ||
| /** | ||
@@ -30,4 +31,2 @@ * Create a new Timeout instance. | ||
| constructor(config) { | ||
| __publicField(this, "config"); | ||
| __publicField(this, "logger"); | ||
| this.config = parseTimeoutConfig(config); | ||
@@ -34,0 +33,0 @@ this.logger = this.config.logger ?? noopLogger; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/config.ts","../src/timeout.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import { z } from 'zod';\nimport { type FortifyLogger } from '@fortify-ts/core';\n\n/**\n * Zod schema for Timeout configuration.\n */\nexport const timeoutConfigSchema = z.object({\n /** Default timeout in milliseconds (default: 30000) */\n defaultTimeout: z.number().int().positive().default(30000),\n /** Callback when timeout occurs */\n onTimeout: z.function().optional(),\n});\n\n/**\n * Raw config input type (before defaults are applied).\n */\nexport type TimeoutConfigInput = z.input<typeof timeoutConfigSchema>;\n\n/**\n * Parsed config type (after defaults are applied).\n */\nexport type TimeoutConfigParsed = z.output<typeof timeoutConfigSchema>;\n\n/**\n * Full configuration type including logger.\n */\nexport interface TimeoutConfig extends TimeoutConfigParsed {\n /** Logger instance for structured logging */\n logger: FortifyLogger | undefined;\n}\n\n/**\n * Parse and validate timeout configuration.\n *\n * @param config - Raw configuration input\n * @returns Validated configuration with defaults applied\n */\nexport function parseTimeoutConfig(config?: TimeoutConfigInput & { logger?: FortifyLogger }): TimeoutConfig {\n const parsed = timeoutConfigSchema.parse(config ?? {});\n return {\n ...parsed,\n logger: config?.logger,\n };\n}\n","import {\n type Operation,\n type Pattern,\n TimeoutError,\n combineSignals,\n type FortifyLogger,\n noopLogger,\n} from '@fortify-ts/core';\nimport { type TimeoutConfig, type TimeoutConfigInput, parseTimeoutConfig } from './config.js';\n\n/**\n * Timeout pattern implementation.\n *\n * Wraps operations with a configurable timeout, ensuring they complete\n * within a specified duration or are cancelled.\n *\n * @template T - The return type of operations\n *\n * @example\n * ```typescript\n * const timeout = new Timeout({ defaultTimeout: 5000 });\n *\n * const result = await timeout.execute(async (signal) => {\n * const response = await fetch('/api/data', { signal });\n * return response.json();\n * });\n * ```\n */\nexport class Timeout<T> implements Pattern<T> {\n private readonly config: TimeoutConfig;\n private readonly logger: FortifyLogger;\n\n /**\n * Create a new Timeout instance.\n *\n * @param config - Timeout configuration\n */\n constructor(config?: TimeoutConfigInput & { logger?: FortifyLogger }) {\n this.config = parseTimeoutConfig(config);\n this.logger = this.config.logger ?? noopLogger;\n }\n\n /**\n * Execute an operation with the default timeout.\n *\n * The operation receives an AbortSignal that will be aborted when:\n * - The timeout duration is exceeded\n * - The external signal (if provided) is aborted\n *\n * Operations MUST respect the AbortSignal for proper cancellation.\n *\n * @param operation - The async operation to execute\n * @param signal - Optional external AbortSignal for cancellation\n * @returns Promise resolving to the operation result\n * @throws {TimeoutError} When the operation exceeds the timeout\n * @throws {DOMException} When cancelled via external signal (AbortError)\n */\n async execute(\n operation: Operation<T>,\n signal?: AbortSignal\n ): Promise<T> {\n return this.executeWithTimeout(operation, this.config.defaultTimeout, signal);\n }\n\n /**\n * Execute an operation with a custom timeout duration.\n *\n * Use this method when you need to override the default timeout for a specific call.\n *\n * @param operation - The async operation to execute\n * @param timeout - Timeout duration in milliseconds\n * @param signal - Optional external AbortSignal for cancellation\n * @returns Promise resolving to the operation result\n * @throws {TimeoutError} When the operation exceeds the timeout\n * @throws {DOMException} When cancelled via external signal (AbortError)\n */\n async executeWithTimeout(\n operation: Operation<T>,\n timeout: number,\n signal?: AbortSignal\n ): Promise<T> {\n const timeoutMs = timeout;\n\n // Check if already aborted\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n\n const controller = new AbortController();\n const combinedSignal = combineSignals(signal, controller.signal);\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n const error = new TimeoutError(\n `Operation timed out after ${String(timeoutMs)}ms`,\n timeoutMs\n );\n controller.abort(error);\n reject(error);\n }, timeoutMs);\n });\n\n this.logger.debug('Executing operation with timeout', { timeoutMs });\n\n try {\n const result = await Promise.race([\n operation(combinedSignal),\n timeoutPromise,\n ]);\n\n this.logger.debug('Operation completed successfully', { timeoutMs });\n return result;\n } catch (error) {\n if (error instanceof TimeoutError) {\n this.logger.warn('Operation timed out', { timeoutMs });\n this.safeCallOnTimeout();\n } else {\n this.logger.error('Operation failed', {\n timeoutMs,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n throw error;\n } finally {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n }\n }\n\n /**\n * Get the default timeout duration.\n *\n * @returns Default timeout in milliseconds\n */\n getDefaultTimeout(): number {\n return this.config.defaultTimeout;\n }\n\n /**\n * Safely call the onTimeout callback.\n */\n private safeCallOnTimeout(): void {\n if (!this.config.onTimeout) return;\n\n try {\n this.config.onTimeout();\n } catch (error) {\n this.logger.error('onTimeout callback threw an error', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n}\n"]} | ||
| {"version":3,"sources":["../src/config.ts","../src/timeout.ts"],"names":[],"mappings":";;;;AAIA,IAAM,cAAA,GAAiB,IAAA;AAKhB,IAAM,mBAAA,GAAsB,EAAE,MAAA,CAAO;AAAA;AAAA,EAE1C,cAAA,EAAgB,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,EAAI,CAAE,QAAA,EAAS,CAAE,GAAA,CAAI,cAAc,CAAA,CAAE,OAAA,CAAQ,GAAK,CAAA;AAAA;AAAA,EAE7E,SAAA,EAAW,CAAA,CAAE,QAAA,EAAS,CAAE,QAAA;AAC1B,CAAC;AA0BM,SAAS,mBAAmB,MAAA,EAAyE;AAC1G,EAAA,MAAM,MAAA,GAAS,mBAAA,CAAoB,KAAA,CAAM,MAAA,IAAU,EAAE,CAAA;AACrD,EAAA,OAAO;AAAA,IACL,GAAG,MAAA;AAAA,IACH,QAAQ,MAAA,EAAQ;AAAA,GAClB;AACF;;;AClBO,IAAM,UAAN,MAAuC;AAAA,EAC3B,MAAA;AAAA,EACA,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOjB,YAAY,MAAA,EAA0D;AACpE,IAAA,IAAA,CAAK,MAAA,GAAS,mBAAmB,MAAM,CAAA;AACvC,IAAA,IAAA,CAAK,MAAA,GAAS,IAAA,CAAK,MAAA,CAAO,MAAA,IAAU,UAAA;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,MAAM,OAAA,CACJ,SAAA,EACA,MAAA,EACY;AACZ,IAAA,OAAO,KAAK,kBAAA,CAAmB,SAAA,EAAW,IAAA,CAAK,MAAA,CAAO,gBAAgB,MAAM,CAAA;AAAA,EAC9E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,kBAAA,CACJ,SAAA,EACA,OAAA,EACA,MAAA,EACY;AACZ,IAAA,MAAM,SAAA,GAAY,OAAA;AAGlB,IAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,MAAA,MAAM,MAAA,CAAO,MAAA,IAAU,IAAI,YAAA,CAAa,WAAW,YAAY,CAAA;AAAA,IACjE;AAEA,IAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,IAAA,MAAM,cAAA,GAAiB,cAAA,CAAe,MAAA,EAAQ,UAAA,CAAW,MAAM,CAAA;AAE/D,IAAA,IAAI,SAAA;AAEJ,IAAA,MAAM,cAAA,GAAiB,IAAI,OAAA,CAAe,CAAC,GAAG,MAAA,KAAW;AACvD,MAAA,SAAA,GAAY,WAAW,MAAM;AAC3B,QAAA,MAAM,QAAQ,IAAI,YAAA;AAAA,UAChB,CAAA,0BAAA,EAA6B,MAAA,CAAO,SAAS,CAAC,CAAA,EAAA,CAAA;AAAA,UAC9C;AAAA,SACF;AACA,QAAA,UAAA,CAAW,MAAM,KAAK,CAAA;AACtB,QAAA,MAAA,CAAO,KAAK,CAAA;AAAA,MACd,GAAG,SAAS,CAAA;AAAA,IACd,CAAC,CAAA;AAED,IAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,kCAAA,EAAoC,EAAE,WAAW,CAAA;AAEnE,IAAA,IAAI;AACF,MAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,IAAA,CAAK;AAAA,QAChC,UAAU,cAAc,CAAA;AAAA,QACxB;AAAA,OACD,CAAA;AAED,MAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,kCAAA,EAAoC,EAAE,WAAW,CAAA;AACnE,MAAA,OAAO,MAAA;AAAA,IACT,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,iBAAiB,YAAA,EAAc;AACjC,QAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,qBAAA,EAAuB,EAAE,WAAW,CAAA;AACrD,QAAA,IAAA,CAAK,iBAAA,EAAkB;AAAA,MACzB,CAAA,MAAO;AACL,QAAA,IAAA,CAAK,MAAA,CAAO,MAAM,kBAAA,EAAoB;AAAA,UACpC,SAAA;AAAA,UACA,OAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK;AAAA,SAC7D,CAAA;AAAA,MACH;AACA,MAAA,MAAM,KAAA;AAAA,IACR,CAAA,SAAE;AACA,MAAA,IAAI,cAAc,MAAA,EAAW;AAC3B,QAAA,YAAA,CAAa,SAAS,CAAA;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAA,GAA4B;AAC1B,IAAA,OAAO,KAAK,MAAA,CAAO,cAAA;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAA,GAA0B;AAChC,IAAA,IAAI,CAAC,IAAA,CAAK,MAAA,CAAO,SAAA,EAAW;AAE5B,IAAA,IAAI;AACF,MAAA,IAAA,CAAK,OAAO,SAAA,EAAU;AAAA,IACxB,SAAS,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,MAAA,CAAO,MAAM,mCAAA,EAAqC;AAAA,QACrD,OAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK;AAAA,OAC7D,CAAA;AAAA,IACH;AAAA,EACF;AACF","file":"index.js","sourcesContent":["import { z } from 'zod';\nimport { type FortifyLogger } from '@fortify-ts/core';\n\n/** Maximum allowed timeout in milliseconds (1 hour) */\nconst MAX_TIMEOUT_MS = 3_600_000;\n\n/**\n * Zod schema for Timeout configuration.\n */\nexport const timeoutConfigSchema = z.object({\n /** Default timeout in milliseconds (default: 30000, max: 1 hour) */\n defaultTimeout: z.number().int().positive().max(MAX_TIMEOUT_MS).default(30000),\n /** Callback when timeout occurs */\n onTimeout: z.function().optional(),\n});\n\n/**\n * Raw config input type (before defaults are applied).\n */\nexport type TimeoutConfigInput = z.input<typeof timeoutConfigSchema>;\n\n/**\n * Parsed config type (after defaults are applied).\n */\nexport type TimeoutConfigParsed = z.output<typeof timeoutConfigSchema>;\n\n/**\n * Full configuration type including logger.\n */\nexport interface TimeoutConfig extends TimeoutConfigParsed {\n /** Logger instance for structured logging */\n logger: FortifyLogger | undefined;\n}\n\n/**\n * Parse and validate timeout configuration.\n *\n * @param config - Raw configuration input\n * @returns Validated configuration with defaults applied\n */\nexport function parseTimeoutConfig(config?: TimeoutConfigInput & { logger?: FortifyLogger }): TimeoutConfig {\n const parsed = timeoutConfigSchema.parse(config ?? {});\n return {\n ...parsed,\n logger: config?.logger,\n };\n}\n","import {\n type Operation,\n type Pattern,\n TimeoutError,\n combineSignals,\n type FortifyLogger,\n noopLogger,\n} from '@fortify-ts/core';\nimport { type TimeoutConfig, type TimeoutConfigInput, parseTimeoutConfig } from './config.js';\n\n/**\n * Timeout pattern implementation.\n *\n * Wraps operations with a configurable timeout, ensuring they complete\n * within a specified duration or are cancelled.\n *\n * @template T - The return type of operations\n *\n * @example\n * ```typescript\n * const timeout = new Timeout({ defaultTimeout: 5000 });\n *\n * const result = await timeout.execute(async (signal) => {\n * const response = await fetch('/api/data', { signal });\n * return response.json();\n * });\n * ```\n */\nexport class Timeout<T> implements Pattern<T> {\n private readonly config: TimeoutConfig;\n private readonly logger: FortifyLogger;\n\n /**\n * Create a new Timeout instance.\n *\n * @param config - Timeout configuration\n */\n constructor(config?: TimeoutConfigInput & { logger?: FortifyLogger }) {\n this.config = parseTimeoutConfig(config);\n this.logger = this.config.logger ?? noopLogger;\n }\n\n /**\n * Execute an operation with the default timeout.\n *\n * The operation receives an AbortSignal that will be aborted when:\n * - The timeout duration is exceeded\n * - The external signal (if provided) is aborted\n *\n * Operations MUST respect the AbortSignal for proper cancellation.\n *\n * @param operation - The async operation to execute\n * @param signal - Optional external AbortSignal for cancellation\n * @returns Promise resolving to the operation result\n * @throws {TimeoutError} When the operation exceeds the timeout\n * @throws {DOMException} When cancelled via external signal (AbortError)\n */\n async execute(\n operation: Operation<T>,\n signal?: AbortSignal\n ): Promise<T> {\n return this.executeWithTimeout(operation, this.config.defaultTimeout, signal);\n }\n\n /**\n * Execute an operation with a custom timeout duration.\n *\n * Use this method when you need to override the default timeout for a specific call.\n *\n * @param operation - The async operation to execute\n * @param timeout - Timeout duration in milliseconds\n * @param signal - Optional external AbortSignal for cancellation\n * @returns Promise resolving to the operation result\n * @throws {TimeoutError} When the operation exceeds the timeout\n * @throws {DOMException} When cancelled via external signal (AbortError)\n */\n async executeWithTimeout(\n operation: Operation<T>,\n timeout: number,\n signal?: AbortSignal\n ): Promise<T> {\n const timeoutMs = timeout;\n\n // Check if already aborted\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n\n const controller = new AbortController();\n const combinedSignal = combineSignals(signal, controller.signal);\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n const error = new TimeoutError(\n `Operation timed out after ${String(timeoutMs)}ms`,\n timeoutMs\n );\n controller.abort(error);\n reject(error);\n }, timeoutMs);\n });\n\n this.logger.debug('Executing operation with timeout', { timeoutMs });\n\n try {\n const result = await Promise.race([\n operation(combinedSignal),\n timeoutPromise,\n ]);\n\n this.logger.debug('Operation completed successfully', { timeoutMs });\n return result;\n } catch (error) {\n if (error instanceof TimeoutError) {\n this.logger.warn('Operation timed out', { timeoutMs });\n this.safeCallOnTimeout();\n } else {\n this.logger.error('Operation failed', {\n timeoutMs,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n throw error;\n } finally {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n }\n }\n\n /**\n * Get the default timeout duration.\n *\n * @returns Default timeout in milliseconds\n */\n getDefaultTimeout(): number {\n return this.config.defaultTimeout;\n }\n\n /**\n * Safely call the onTimeout callback.\n */\n private safeCallOnTimeout(): void {\n if (!this.config.onTimeout) return;\n\n try {\n this.config.onTimeout();\n } catch (error) {\n this.logger.error('onTimeout callback threw an error', {\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n}\n"]} |
+3
-2
| { | ||
| "name": "@fortify-ts/timeout", | ||
| "version": "0.1.5", | ||
| "version": "0.2.0", | ||
| "description": "Timeout pattern for Fortify TS resilience library", | ||
@@ -26,5 +26,6 @@ "type": "module", | ||
| "zod": "^4.1.13", | ||
| "@fortify-ts/core": "0.2.0" | ||
| "@fortify-ts/core": "0.3.0" | ||
| }, | ||
| "devDependencies": { | ||
| "fast-check": "^4.1.1", | ||
| "tsup": "^8.5.1", | ||
@@ -31,0 +32,0 @@ "typescript": "^5.9.3", |
Unpublished package
Supply chain riskPackage version was not found on the registry. It may exist on a different registry and need to be configured to pull from that registry.
Found 1 instance in 1 package
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
Unpublished package
Supply chain riskPackage version was not found on the registry. It may exist on a different registry and need to be configured to pull from that registry.
Found 1 instance in 1 package
38161
4.42%9
12.5%0
-100%94
Infinity%4
33.33%360
-0.55%Updated