Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@fortify-ts/timeout

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@fortify-ts/timeout - npm Package Compare versions

Package was removed
Sorry, it seems this package was removed from the registry
Comparing version
0.1.5
to
0.2.0
+93
README.md
# @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":"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","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":";;;;;;AAIA,IAAM,cAAA,GAAiB,IAAA;AAKhB,IAAM,mBAAA,GAAsBA,MAAE,MAAA,CAAO;AAAA;AAAA,EAE1C,cAAA,EAAgBA,KAAA,CAAE,MAAA,EAAO,CAAE,GAAA,EAAI,CAAE,QAAA,EAAS,CAAE,GAAA,CAAI,cAAc,CAAA,CAAE,OAAA,CAAQ,GAAK,CAAA;AAAA;AAAA,EAE7E,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;;;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,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/** 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"]}
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"]}
{
"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",