@fortify-ts/core
Advanced tools
+119
-0
@@ -375,2 +375,117 @@ 'use strict'; | ||
| }); | ||
| var MAX_TOKENS = 1e9; | ||
| var MAX_TIMESTAMP = 41024448e5; | ||
| var bucketStateSchema = zod.z.object({ | ||
| tokens: zod.z.number().nonnegative().max(MAX_TOKENS), | ||
| lastRefill: zod.z.number().int().nonnegative().max(MAX_TIMESTAMP) | ||
| }); | ||
| function validateBucketState(data) { | ||
| const result = bucketStateSchema.safeParse(data); | ||
| return result.success ? result.data : null; | ||
| } | ||
| var MAX_KEY_LENGTH = 256; | ||
| var CONTROL_CHARS_REGEX = /[\x00-\x1f\x7f]/g; | ||
| var PATH_SEPARATORS_REGEX = /[/\\]/g; | ||
| function sanitizeStorageKey(key) { | ||
| const truncated = key.length > MAX_KEY_LENGTH ? key.slice(0, MAX_KEY_LENGTH) : key; | ||
| return truncated.replace(CONTROL_CHARS_REGEX, "").replace(PATH_SEPARATORS_REGEX, "_"); | ||
| } | ||
| var _MemoryStorage = class _MemoryStorage { | ||
| /** | ||
| * Create a new in-memory storage. | ||
| * | ||
| * @param options - Storage options | ||
| * @param options.maxEntries - Maximum entries before LRU eviction (0 = unlimited, default: 10000) | ||
| */ | ||
| constructor(options) { | ||
| __publicField(this, "entries", /* @__PURE__ */ new Map()); | ||
| __publicField(this, "maxEntries"); | ||
| __publicField(this, "evictionCount", 0); | ||
| this.maxEntries = options?.maxEntries ?? 1e4; | ||
| } | ||
| get(key) { | ||
| const state = this.getSync(key); | ||
| return state === null ? _MemoryStorage.RESOLVED_NULL : Promise.resolve(state); | ||
| } | ||
| set(key, state) { | ||
| this.setSync(key, state); | ||
| return _MemoryStorage.RESOLVED_VOID; | ||
| } | ||
| compareAndSet(key, expected, newState) { | ||
| const current = this.entries.get(key) ?? null; | ||
| const matches = current === expected || current !== null && expected !== null && current.tokens === expected.tokens && current.lastRefill === expected.lastRefill; | ||
| if (matches) { | ||
| this.setSync(key, newState); | ||
| return Promise.resolve({ success: true, currentState: newState }); | ||
| } | ||
| return Promise.resolve({ success: false, currentState: current }); | ||
| } | ||
| delete(key) { | ||
| this.deleteSync(key); | ||
| return _MemoryStorage.RESOLVED_VOID; | ||
| } | ||
| clear() { | ||
| this.clearSync(); | ||
| return _MemoryStorage.RESOLVED_VOID; | ||
| } | ||
| /** | ||
| * Get the current number of entries. | ||
| */ | ||
| size() { | ||
| return this.entries.size; | ||
| } | ||
| /** | ||
| * Get the total number of evictions since creation. | ||
| */ | ||
| getEvictionCount() { | ||
| return this.evictionCount; | ||
| } | ||
| /** | ||
| * Synchronous get for backward compatibility with sync rate limiter methods. | ||
| * @internal | ||
| */ | ||
| getSync(key) { | ||
| const state = this.entries.get(key); | ||
| if (state) { | ||
| this.entries.delete(key); | ||
| this.entries.set(key, state); | ||
| } | ||
| return state ?? null; | ||
| } | ||
| /** | ||
| * Synchronous set for backward compatibility with sync rate limiter methods. | ||
| * @internal | ||
| */ | ||
| setSync(key, state) { | ||
| if (this.entries.has(key)) { | ||
| this.entries.delete(key); | ||
| } else if (this.maxEntries > 0 && this.entries.size >= this.maxEntries) { | ||
| const firstKey = this.entries.keys().next().value; | ||
| if (firstKey !== void 0) { | ||
| this.entries.delete(firstKey); | ||
| this.evictionCount++; | ||
| } | ||
| } | ||
| this.entries.set(key, state); | ||
| } | ||
| /** | ||
| * Synchronous delete for backward compatibility. | ||
| * @internal | ||
| */ | ||
| deleteSync(key) { | ||
| this.entries.delete(key); | ||
| } | ||
| /** | ||
| * Synchronous clear for backward compatibility. | ||
| * @internal | ||
| */ | ||
| clearSync() { | ||
| this.entries.clear(); | ||
| } | ||
| }; | ||
| /** Cached resolved promise for void returns to avoid allocation */ | ||
| __publicField(_MemoryStorage, "RESOLVED_VOID", Promise.resolve()); | ||
| /** Cached resolved promise for null returns to avoid allocation */ | ||
| __publicField(_MemoryStorage, "RESOLVED_NULL", Promise.resolve(null)); | ||
| var MemoryStorage = _MemoryStorage; | ||
@@ -382,2 +497,3 @@ exports.BulkheadClosedError = BulkheadClosedError; | ||
| exports.MaxAttemptsReachedError = MaxAttemptsReachedError; | ||
| exports.MemoryStorage = MemoryStorage; | ||
| exports.RateLimitExceededError = RateLimitExceededError; | ||
@@ -390,2 +506,3 @@ exports.RetryableErrorWrapper = RetryableErrorWrapper; | ||
| exports.backoffPolicySchema = backoffPolicySchema; | ||
| exports.bucketStateSchema = bucketStateSchema; | ||
| exports.bulkheadConfigSchema = bulkheadConfigSchema; | ||
@@ -409,7 +526,9 @@ exports.circuitBreakerConfigSchema = circuitBreakerConfigSchema; | ||
| exports.safeCallback = safeCallback; | ||
| exports.sanitizeStorageKey = sanitizeStorageKey; | ||
| exports.sleep = sleep; | ||
| exports.throwIfAborted = throwIfAborted; | ||
| exports.timeoutConfigSchema = timeoutConfigSchema; | ||
| exports.validateBucketState = validateBucketState; | ||
| exports.withTimeout = withTimeout; | ||
| //# sourceMappingURL=index.cjs.map | ||
| //# sourceMappingURL=index.cjs.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/errors.ts","../src/types.ts","../src/utils.ts","../src/schemas.ts"],"names":["z"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Base error class for all Fortify errors.\n */\nexport class FortifyError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'FortifyError';\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n // Use typeof check to avoid unnecessary-condition lint error\n if (typeof Error.captureStackTrace === 'function') {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Error thrown when circuit breaker is in open state and rejecting requests.\n */\nexport class CircuitOpenError extends FortifyError {\n constructor(message = 'Circuit breaker is open') {\n super(message);\n this.name = 'CircuitOpenError';\n }\n}\n\n/**\n * Error thrown when rate limit is exceeded.\n */\nexport class RateLimitExceededError extends FortifyError {\n public readonly key: string | undefined;\n\n constructor(message = 'Rate limit exceeded', key?: string) {\n super(message);\n this.name = 'RateLimitExceededError';\n this.key = key;\n }\n}\n\n/**\n * Error thrown when bulkhead is at capacity.\n */\nexport class BulkheadFullError extends FortifyError {\n public readonly activeCount: number;\n public readonly queuedCount: number;\n\n constructor(\n message = 'Bulkhead is full',\n activeCount = 0,\n queuedCount = 0\n ) {\n super(message);\n this.name = 'BulkheadFullError';\n this.activeCount = activeCount;\n this.queuedCount = queuedCount;\n }\n}\n\n/**\n * Error thrown when operation times out.\n */\nexport class TimeoutError extends FortifyError {\n public readonly timeoutMs: number;\n\n constructor(message = 'Operation timed out', timeoutMs = 0) {\n super(message);\n this.name = 'TimeoutError';\n this.timeoutMs = timeoutMs;\n }\n}\n\n/**\n * Error thrown when maximum retry attempts are reached.\n */\nexport class MaxAttemptsReachedError extends FortifyError {\n public readonly attempts: number;\n public readonly lastError: Error | undefined;\n\n constructor(\n message = 'Maximum retry attempts reached',\n attempts = 0,\n lastError?: Error\n ) {\n super(message);\n this.name = 'MaxAttemptsReachedError';\n this.attempts = attempts;\n this.lastError = lastError;\n }\n}\n\n/**\n * Error thrown when bulkhead is closed and no new operations accepted.\n */\nexport class BulkheadClosedError extends FortifyError {\n constructor(message = 'Bulkhead is closed') {\n super(message);\n this.name = 'BulkheadClosedError';\n }\n}\n\n/**\n * Interface for errors that can indicate whether they are retryable.\n */\nexport interface RetryableError {\n retryable: boolean;\n}\n\n/**\n * Type guard to check if an error implements RetryableError interface.\n */\nexport function isRetryableError(error: unknown): error is Error & RetryableError {\n return (\n error instanceof Error &&\n 'retryable' in error &&\n typeof (error as RetryableError).retryable === 'boolean'\n );\n}\n\n/**\n * Wrapper class to mark an error as retryable.\n */\nexport class RetryableErrorWrapper extends Error implements RetryableError {\n public readonly retryable: boolean;\n public override readonly cause: Error;\n\n constructor(error: Error, retryable: boolean) {\n super(error.message, { cause: error });\n this.name = 'RetryableErrorWrapper';\n this.retryable = retryable;\n this.cause = error;\n if (error.stack) {\n this.stack = error.stack;\n }\n }\n}\n\n/**\n * Wrap an error as retryable.\n */\nexport function asRetryable(error: Error, retryable = true): RetryableErrorWrapper {\n return new RetryableErrorWrapper(error, retryable);\n}\n\n/**\n * Wrap an error as non-retryable.\n */\nexport function asNonRetryable(error: Error): RetryableErrorWrapper {\n return new RetryableErrorWrapper(error, false);\n}\n\n/**\n * Check if an error is retryable.\n * Returns true if the error implements RetryableError and retryable is true.\n * Returns undefined if the error doesn't implement RetryableError (let caller decide).\n */\nexport function isRetryable(error: unknown): boolean | undefined {\n if (isRetryableError(error)) {\n return error.retryable;\n }\n return undefined;\n}\n","/**\n * Represents an async operation that can be executed with cancellation support.\n * The signal parameter allows the operation to be cancelled via AbortController.\n *\n * @template T - The return type of the operation\n */\nexport type Operation<T> = (signal: AbortSignal) => Promise<T>;\n\n/**\n * Callback for state changes in stateful patterns (e.g., circuit breaker).\n *\n * @template S - The state type\n */\nexport type StateChangeCallback<S> = (from: S, to: S) => void;\n\n/**\n * Callback for error events.\n */\nexport type ErrorCallback = (error: Error) => void;\n\n/**\n * Simple void callback.\n */\nexport type VoidCallback = () => void;\n\n/**\n * Callback for retry events.\n */\nexport type RetryCallback = (attempt: number, error: Error) => void;\n\n/**\n * Callback for rate limit events.\n */\nexport type RateLimitCallback = (key: string) => void;\n\n/**\n * Logger interface for structured logging across all patterns.\n * Compatible with pino, winston, console, and custom implementations.\n */\nexport interface FortifyLogger {\n debug(msg: string, context?: Record<string, unknown>): void;\n info(msg: string, context?: Record<string, unknown>): void;\n warn(msg: string, context?: Record<string, unknown>): void;\n error(msg: string, context?: Record<string, unknown>): void;\n}\n\n/**\n * No-operation logger that discards all log messages.\n * Useful for testing or when logging is not needed.\n */\nexport const noopLogger: FortifyLogger = {\n debug: () => undefined,\n info: () => undefined,\n warn: () => undefined,\n error: () => undefined,\n};\n\n/**\n * Console-based logger implementation.\n * Browser-friendly and works in all environments.\n */\nexport const consoleLogger: FortifyLogger = {\n debug: (msg, context) => {\n if (context) {\n console.debug(`[fortify] ${msg}`, context);\n } else {\n console.debug(`[fortify] ${msg}`);\n }\n },\n info: (msg, context) => {\n if (context) {\n console.info(`[fortify] ${msg}`, context);\n } else {\n console.info(`[fortify] ${msg}`);\n }\n },\n warn: (msg, context) => {\n if (context) {\n console.warn(`[fortify] ${msg}`, context);\n } else {\n console.warn(`[fortify] ${msg}`);\n }\n },\n error: (msg, context) => {\n if (context) {\n console.error(`[fortify] ${msg}`, context);\n } else {\n console.error(`[fortify] ${msg}`);\n }\n },\n};\n\n/**\n * Generic pattern interface that all resilience patterns implement.\n *\n * @template T - The return type of the operation\n */\nexport interface Pattern<T> {\n /**\n * Execute an operation with the pattern's resilience logic.\n *\n * @param operation - The async operation to execute\n * @param signal - Optional AbortSignal for cancellation\n * @returns Promise resolving to the operation result\n */\n execute(operation: Operation<T>, signal?: AbortSignal): Promise<T>;\n}\n\n/**\n * Interface for patterns that support closing/cleanup.\n */\nexport interface Closeable {\n /**\n * Close the pattern and release resources.\n * May wait for in-flight operations to complete.\n */\n close(): Promise<void>;\n}\n\n/**\n * Interface for patterns that support resetting state.\n */\nexport interface Resettable {\n /**\n * Reset the pattern to its initial state.\n */\n reset(): void;\n}\n","import { TimeoutError } from './errors.js';\n\n/**\n * Sleep for a specified duration with optional cancellation support.\n *\n * @param ms - Duration in milliseconds\n * @param signal - Optional AbortSignal for cancellation\n * @returns Promise that resolves after the delay or rejects if cancelled\n */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(ensureError(signal.reason));\n return;\n }\n\n const timeoutId = setTimeout(resolve, ms);\n\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timeoutId);\n reject(ensureError(signal.reason));\n },\n { once: true }\n );\n });\n}\n\n/**\n * Ensure a value is an Error instance.\n * @param reason - The reason to convert to an Error\n * @returns An Error instance\n */\nfunction ensureError(reason: unknown): Error {\n if (reason instanceof Error) {\n return reason;\n }\n if (typeof reason === 'string') {\n return new Error(reason);\n }\n if (reason !== undefined && reason !== null) {\n // Handle objects and other types safely\n try {\n return new Error(JSON.stringify(reason));\n } catch {\n return new Error('Unknown error');\n }\n }\n return new DOMException('Aborted', 'AbortError');\n}\n\n/**\n * Wrap a promise with a timeout.\n *\n * @template T - The promise return type\n * @param promise - The promise to wrap\n * @param ms - Timeout duration in milliseconds\n * @param signal - Optional external AbortSignal for cancellation\n * @returns Promise that resolves with the result or rejects with TimeoutError\n */\nexport async function withTimeout<T>(\n promise: Promise<T>,\n ms: number,\n signal?: AbortSignal\n): Promise<T> {\n // Check if already aborted\n if (signal?.aborted) {\n throw ensureError(signal.reason);\n }\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new TimeoutError(`Operation timed out after ${String(ms)}ms`, ms));\n }, ms);\n });\n\n // Handle external signal\n const abortPromise = signal\n ? new Promise<never>((_, reject) => {\n signal.addEventListener(\n 'abort',\n () => reject(ensureError(signal.reason)),\n { once: true }\n );\n })\n : null;\n\n try {\n const racers: Promise<T>[] = [promise, timeoutPromise];\n if (abortPromise) {\n racers.push(abortPromise);\n }\n return await Promise.race(racers);\n } finally {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n }\n}\n\n/**\n * Execute an operation with a timeout, passing the combined signal to the operation.\n *\n * @template T - The operation return type\n * @param operation - The operation to execute\n * @param ms - Timeout duration in milliseconds\n * @param signal - Optional external AbortSignal for cancellation\n * @returns Promise that resolves with the result or rejects with TimeoutError\n */\nexport async function executeWithTimeout<T>(\n operation: (signal: AbortSignal) => Promise<T>,\n ms: number,\n signal?: AbortSignal\n): Promise<T> {\n const controller = new AbortController();\n\n // Combine signals\n const combinedSignal = combineSignals(signal, controller.signal);\n\n const timeoutId = setTimeout(() => {\n controller.abort(new TimeoutError(`Operation timed out after ${String(ms)}ms`, ms));\n }, ms);\n\n try {\n return await operation(combinedSignal);\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Combine multiple AbortSignals into one.\n * Returns a signal that aborts when any of the input signals abort.\n *\n * Uses AbortSignal.any() when available (Node 20+, modern browsers).\n * Falls back to a manual implementation that properly cleans up event listeners.\n *\n * @param signals - AbortSignals to combine (undefined values are filtered out)\n * @returns Combined AbortSignal\n */\nexport function combineSignals(...signals: (AbortSignal | undefined)[]): AbortSignal {\n const validSignals = signals.filter((s): s is AbortSignal => s !== undefined);\n\n if (validSignals.length === 0) {\n return new AbortController().signal;\n }\n\n const firstSignal = validSignals[0];\n if (validSignals.length === 1 && firstSignal) {\n return firstSignal;\n }\n\n // Use AbortSignal.any if available (Node 20+, modern browsers)\n if ('any' in AbortSignal) {\n return AbortSignal.any(validSignals);\n }\n\n // Fallback: create a new controller and link it to all signals\n // Track listeners so we can clean them up to prevent memory leaks\n const controller = new AbortController();\n const listeners: { signal: AbortSignal; listener: () => void }[] = [];\n\n // Cleanup function to remove all listeners\n const cleanup = () => {\n for (const { signal, listener } of listeners) {\n signal.removeEventListener('abort', listener);\n }\n listeners.length = 0;\n };\n\n for (const signal of validSignals) {\n if (signal.aborted) {\n controller.abort(signal.reason);\n return controller.signal;\n }\n\n const listener = () => {\n cleanup(); // Clean up all listeners when any signal aborts\n controller.abort(signal.reason);\n };\n\n listeners.push({ signal, listener });\n signal.addEventListener('abort', listener, { once: true });\n }\n\n return controller.signal;\n}\n\n/**\n * Check if an error is an abort error (from AbortController).\n *\n * @param error - Error to check\n * @returns True if the error is an AbortError\n */\nexport function isAbortError(error: unknown): boolean {\n return (\n error instanceof DOMException &&\n error.name === 'AbortError'\n );\n}\n\n/**\n * Throw if the given signal is aborted.\n * Useful for checking signal state early in operations to avoid unnecessary work.\n *\n * @param signal - AbortSignal to check\n * @throws {DOMException} If signal is aborted (AbortError)\n */\nexport function throwIfAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n}\n\n/**\n * Safely execute a callback, catching and logging any errors.\n * Used for user-provided callbacks to prevent them from breaking the pattern.\n *\n * @param callback - Callback to execute\n * @param onError - Optional error handler\n */\nexport function safeCallback<T extends (...args: unknown[]) => unknown>(\n callback: T | undefined,\n onError?: (error: Error) => void\n): T | undefined {\n if (!callback) return undefined;\n\n return ((...args: Parameters<T>): ReturnType<T> | undefined => {\n try {\n return callback(...args) as ReturnType<T>;\n } catch (error) {\n if (onError && error instanceof Error) {\n onError(error);\n }\n return undefined;\n }\n }) as T;\n}\n\n/**\n * Calculate jittered delay.\n * Adds 0-10% random variance to prevent thundering herd.\n *\n * @param delay - Base delay in milliseconds\n * @returns Jittered delay\n */\nexport function addJitter(delay: number): number {\n const jitterFactor = 1 + Math.random() * 0.1; // 0-10% jitter\n return Math.floor(delay * jitterFactor);\n}\n\n/**\n * Clamp a number between min and max values.\n *\n * @param value - Value to clamp\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Clamped value\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\n/**\n * Get current timestamp in milliseconds.\n * Uses performance.now() if available for higher precision.\n *\n * @returns Current timestamp in milliseconds\n */\nexport function now(): number {\n if (typeof performance !== 'undefined' && typeof performance.now === 'function') {\n return performance.now();\n }\n return Date.now();\n}\n","import { z } from 'zod';\n\n/**\n * Schema for a function type (Zod 4 compatible).\n * Uses z.function() which validates that the value is a function.\n */\nconst functionSchema = z.function();\n\n/**\n * Base schema for logger configuration.\n * Validates that the logger has the required methods.\n */\nexport const loggerSchema = z.object({\n debug: functionSchema,\n info: functionSchema,\n warn: functionSchema,\n error: functionSchema,\n}).optional();\n\n/**\n * 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: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type TimeoutConfig = z.infer<typeof timeoutConfigSchema>;\n\n/**\n * Schema for retry backoff policy.\n */\nexport const backoffPolicySchema = z.enum(['exponential', 'linear', 'constant']);\n\nexport type BackoffPolicy = z.infer<typeof backoffPolicySchema>;\n\n/**\n * Schema for retry configuration.\n */\nexport const retryConfigSchema = z.object({\n /** Maximum number of attempts including the first (default: 3) */\n maxAttempts: z.number().int().positive().default(3),\n /** Initial delay before first retry in milliseconds (default: 100) */\n initialDelay: z.number().int().positive().default(100),\n /** Maximum delay between retries in milliseconds */\n maxDelay: z.number().int().positive().optional(),\n /** Backoff strategy (default: 'exponential') */\n backoffPolicy: backoffPolicySchema.default('exponential'),\n /** Multiplier for exponential backoff (default: 2.0) */\n multiplier: z.number().positive().default(2.0),\n /** Add random jitter to delays (default: false) */\n jitter: z.boolean().default(false),\n /** Custom function to determine if error is retryable */\n isRetryable: functionSchema.optional(),\n /** Callback on each retry attempt */\n onRetry: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type RetryConfig = z.infer<typeof retryConfigSchema>;\n\n/**\n * Schema for circuit breaker state.\n */\nexport const circuitBreakerStateSchema = z.enum(['closed', 'open', 'half-open']);\n\nexport type CircuitBreakerState = z.infer<typeof circuitBreakerStateSchema>;\n\n/**\n * Schema for circuit breaker counts/metrics.\n */\nexport const circuitBreakerCountsSchema = z.object({\n requests: z.number().int().nonnegative(),\n totalSuccesses: z.number().int().nonnegative(),\n totalFailures: z.number().int().nonnegative(),\n consecutiveSuccesses: z.number().int().nonnegative(),\n consecutiveFailures: z.number().int().nonnegative(),\n});\n\nexport type CircuitBreakerCounts = z.infer<typeof circuitBreakerCountsSchema>;\n\n/**\n * Schema for circuit breaker configuration.\n */\nexport const circuitBreakerConfigSchema = z.object({\n /** Maximum consecutive failures before opening (default: 5) */\n maxFailures: z.number().int().positive().default(5),\n /** Duration in open state before transitioning to half-open in milliseconds (default: 60000) */\n timeout: z.number().int().positive().default(60000),\n /** Maximum requests allowed in half-open state (default: 1) */\n halfOpenMaxRequests: z.number().int().positive().default(1),\n /** Period to clear counts when closed, 0 means never (default: 0) */\n interval: z.number().int().nonnegative().default(0),\n /** Custom function to determine when to trip the breaker */\n readyToTrip: functionSchema.optional(),\n /** Custom function to determine if result is successful */\n isSuccessful: functionSchema.optional(),\n /** Callback on state change */\n onStateChange: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type CircuitBreakerConfig = z.infer<typeof circuitBreakerConfigSchema>;\n\n/**\n * Schema for rate limiter configuration.\n */\nexport const rateLimitConfigSchema = z.object({\n /** Number of tokens added per interval (default: 100) */\n rate: z.number().int().positive().default(100),\n /** Maximum bucket capacity (defaults to rate) */\n burst: z.number().int().positive().optional(),\n /** Interval for token refill in milliseconds (default: 1000) */\n interval: z.number().int().positive().default(1000),\n /** Callback when rate limit is hit */\n onLimit: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type RateLimitConfig = z.infer<typeof rateLimitConfigSchema>;\n\n/**\n * Schema for bulkhead configuration.\n */\nexport const bulkheadConfigSchema = z.object({\n /** Maximum concurrent executions (default: 10) */\n maxConcurrent: z.number().int().positive().default(10),\n /** Maximum queued requests, 0 means no queueing (default: 0) */\n maxQueue: z.number().int().nonnegative().default(0),\n /** Maximum time to wait in queue in milliseconds */\n queueTimeout: z.number().int().positive().optional(),\n /** Callback when request is rejected */\n onRejected: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type BulkheadConfig = z.infer<typeof bulkheadConfigSchema>;\n\n/**\n * Schema for fallback configuration.\n */\nexport const fallbackConfigSchema = z.object({\n /** Fallback function to execute when primary fails */\n fallback: functionSchema,\n /** Custom function to determine if fallback should be used */\n shouldFallback: functionSchema.optional(),\n /** Callback when fallback is triggered */\n onFallback: functionSchema.optional(),\n /** Callback when primary succeeds */\n onSuccess: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type FallbackConfig = z.infer<typeof fallbackConfigSchema>;\n"]} | ||
| {"version":3,"sources":["../src/errors.ts","../src/types.ts","../src/utils.ts","../src/schemas.ts","../src/storage.ts"],"names":["z"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Base error class for all Fortify errors.\n */\nexport class FortifyError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'FortifyError';\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n // Use typeof check to avoid unnecessary-condition lint error\n if (typeof Error.captureStackTrace === 'function') {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Error thrown when circuit breaker is in open state and rejecting requests.\n */\nexport class CircuitOpenError extends FortifyError {\n constructor(message = 'Circuit breaker is open') {\n super(message);\n this.name = 'CircuitOpenError';\n }\n}\n\n/**\n * Error thrown when rate limit is exceeded.\n */\nexport class RateLimitExceededError extends FortifyError {\n public readonly key: string | undefined;\n\n constructor(message = 'Rate limit exceeded', key?: string) {\n super(message);\n this.name = 'RateLimitExceededError';\n this.key = key;\n }\n}\n\n/**\n * Error thrown when bulkhead is at capacity.\n */\nexport class BulkheadFullError extends FortifyError {\n public readonly activeCount: number;\n public readonly queuedCount: number;\n\n constructor(\n message = 'Bulkhead is full',\n activeCount = 0,\n queuedCount = 0\n ) {\n super(message);\n this.name = 'BulkheadFullError';\n this.activeCount = activeCount;\n this.queuedCount = queuedCount;\n }\n}\n\n/**\n * Error thrown when operation times out.\n */\nexport class TimeoutError extends FortifyError {\n public readonly timeoutMs: number;\n\n constructor(message = 'Operation timed out', timeoutMs = 0) {\n super(message);\n this.name = 'TimeoutError';\n this.timeoutMs = timeoutMs;\n }\n}\n\n/**\n * Error thrown when maximum retry attempts are reached.\n */\nexport class MaxAttemptsReachedError extends FortifyError {\n public readonly attempts: number;\n public readonly lastError: Error | undefined;\n\n constructor(\n message = 'Maximum retry attempts reached',\n attempts = 0,\n lastError?: Error\n ) {\n super(message);\n this.name = 'MaxAttemptsReachedError';\n this.attempts = attempts;\n this.lastError = lastError;\n }\n}\n\n/**\n * Error thrown when bulkhead is closed and no new operations accepted.\n */\nexport class BulkheadClosedError extends FortifyError {\n constructor(message = 'Bulkhead is closed') {\n super(message);\n this.name = 'BulkheadClosedError';\n }\n}\n\n/**\n * Interface for errors that can indicate whether they are retryable.\n */\nexport interface RetryableError {\n retryable: boolean;\n}\n\n/**\n * Type guard to check if an error implements RetryableError interface.\n */\nexport function isRetryableError(error: unknown): error is Error & RetryableError {\n return (\n error instanceof Error &&\n 'retryable' in error &&\n typeof (error as RetryableError).retryable === 'boolean'\n );\n}\n\n/**\n * Wrapper class to mark an error as retryable.\n */\nexport class RetryableErrorWrapper extends Error implements RetryableError {\n public readonly retryable: boolean;\n public override readonly cause: Error;\n\n constructor(error: Error, retryable: boolean) {\n super(error.message, { cause: error });\n this.name = 'RetryableErrorWrapper';\n this.retryable = retryable;\n this.cause = error;\n if (error.stack) {\n this.stack = error.stack;\n }\n }\n}\n\n/**\n * Wrap an error as retryable.\n */\nexport function asRetryable(error: Error, retryable = true): RetryableErrorWrapper {\n return new RetryableErrorWrapper(error, retryable);\n}\n\n/**\n * Wrap an error as non-retryable.\n */\nexport function asNonRetryable(error: Error): RetryableErrorWrapper {\n return new RetryableErrorWrapper(error, false);\n}\n\n/**\n * Check if an error is retryable.\n * Returns true if the error implements RetryableError and retryable is true.\n * Returns undefined if the error doesn't implement RetryableError (let caller decide).\n */\nexport function isRetryable(error: unknown): boolean | undefined {\n if (isRetryableError(error)) {\n return error.retryable;\n }\n return undefined;\n}\n","/**\n * Represents an async operation that can be executed with cancellation support.\n * The signal parameter allows the operation to be cancelled via AbortController.\n *\n * @template T - The return type of the operation\n */\nexport type Operation<T> = (signal: AbortSignal) => Promise<T>;\n\n/**\n * Callback for state changes in stateful patterns (e.g., circuit breaker).\n *\n * @template S - The state type\n */\nexport type StateChangeCallback<S> = (from: S, to: S) => void;\n\n/**\n * Callback for error events.\n */\nexport type ErrorCallback = (error: Error) => void;\n\n/**\n * Simple void callback.\n */\nexport type VoidCallback = () => void;\n\n/**\n * Callback for retry events.\n */\nexport type RetryCallback = (attempt: number, error: Error) => void;\n\n/**\n * Callback for rate limit events.\n */\nexport type RateLimitCallback = (key: string) => void;\n\n/**\n * Logger interface for structured logging across all patterns.\n * Compatible with pino, winston, console, and custom implementations.\n */\nexport interface FortifyLogger {\n debug(msg: string, context?: Record<string, unknown>): void;\n info(msg: string, context?: Record<string, unknown>): void;\n warn(msg: string, context?: Record<string, unknown>): void;\n error(msg: string, context?: Record<string, unknown>): void;\n}\n\n/**\n * No-operation logger that discards all log messages.\n * Useful for testing or when logging is not needed.\n */\nexport const noopLogger: FortifyLogger = {\n debug: () => undefined,\n info: () => undefined,\n warn: () => undefined,\n error: () => undefined,\n};\n\n/**\n * Console-based logger implementation.\n * Browser-friendly and works in all environments.\n */\nexport const consoleLogger: FortifyLogger = {\n debug: (msg, context) => {\n if (context) {\n console.debug(`[fortify] ${msg}`, context);\n } else {\n console.debug(`[fortify] ${msg}`);\n }\n },\n info: (msg, context) => {\n if (context) {\n console.info(`[fortify] ${msg}`, context);\n } else {\n console.info(`[fortify] ${msg}`);\n }\n },\n warn: (msg, context) => {\n if (context) {\n console.warn(`[fortify] ${msg}`, context);\n } else {\n console.warn(`[fortify] ${msg}`);\n }\n },\n error: (msg, context) => {\n if (context) {\n console.error(`[fortify] ${msg}`, context);\n } else {\n console.error(`[fortify] ${msg}`);\n }\n },\n};\n\n/**\n * Generic pattern interface that all resilience patterns implement.\n *\n * @template T - The return type of the operation\n */\nexport interface Pattern<T> {\n /**\n * Execute an operation with the pattern's resilience logic.\n *\n * @param operation - The async operation to execute\n * @param signal - Optional AbortSignal for cancellation\n * @returns Promise resolving to the operation result\n */\n execute(operation: Operation<T>, signal?: AbortSignal): Promise<T>;\n}\n\n/**\n * Interface for patterns that support closing/cleanup.\n */\nexport interface Closeable {\n /**\n * Close the pattern and release resources.\n * May wait for in-flight operations to complete.\n */\n close(): Promise<void>;\n}\n\n/**\n * Interface for patterns that support resetting state.\n */\nexport interface Resettable {\n /**\n * Reset the pattern to its initial state.\n */\n reset(): void;\n}\n","import { TimeoutError } from './errors.js';\n\n/**\n * Sleep for a specified duration with optional cancellation support.\n *\n * @param ms - Duration in milliseconds\n * @param signal - Optional AbortSignal for cancellation\n * @returns Promise that resolves after the delay or rejects if cancelled\n */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(ensureError(signal.reason));\n return;\n }\n\n const timeoutId = setTimeout(resolve, ms);\n\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timeoutId);\n reject(ensureError(signal.reason));\n },\n { once: true }\n );\n });\n}\n\n/**\n * Ensure a value is an Error instance.\n * @param reason - The reason to convert to an Error\n * @returns An Error instance\n */\nfunction ensureError(reason: unknown): Error {\n if (reason instanceof Error) {\n return reason;\n }\n if (typeof reason === 'string') {\n return new Error(reason);\n }\n if (reason !== undefined && reason !== null) {\n // Handle objects and other types safely\n try {\n return new Error(JSON.stringify(reason));\n } catch {\n return new Error('Unknown error');\n }\n }\n return new DOMException('Aborted', 'AbortError');\n}\n\n/**\n * Wrap a promise with a timeout.\n *\n * @template T - The promise return type\n * @param promise - The promise to wrap\n * @param ms - Timeout duration in milliseconds\n * @param signal - Optional external AbortSignal for cancellation\n * @returns Promise that resolves with the result or rejects with TimeoutError\n */\nexport async function withTimeout<T>(\n promise: Promise<T>,\n ms: number,\n signal?: AbortSignal\n): Promise<T> {\n // Check if already aborted\n if (signal?.aborted) {\n throw ensureError(signal.reason);\n }\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new TimeoutError(`Operation timed out after ${String(ms)}ms`, ms));\n }, ms);\n });\n\n // Handle external signal\n const abortPromise = signal\n ? new Promise<never>((_, reject) => {\n signal.addEventListener(\n 'abort',\n () => reject(ensureError(signal.reason)),\n { once: true }\n );\n })\n : null;\n\n try {\n const racers: Promise<T>[] = [promise, timeoutPromise];\n if (abortPromise) {\n racers.push(abortPromise);\n }\n return await Promise.race(racers);\n } finally {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n }\n}\n\n/**\n * Execute an operation with a timeout, passing the combined signal to the operation.\n *\n * @template T - The operation return type\n * @param operation - The operation to execute\n * @param ms - Timeout duration in milliseconds\n * @param signal - Optional external AbortSignal for cancellation\n * @returns Promise that resolves with the result or rejects with TimeoutError\n */\nexport async function executeWithTimeout<T>(\n operation: (signal: AbortSignal) => Promise<T>,\n ms: number,\n signal?: AbortSignal\n): Promise<T> {\n const controller = new AbortController();\n\n // Combine signals\n const combinedSignal = combineSignals(signal, controller.signal);\n\n const timeoutId = setTimeout(() => {\n controller.abort(new TimeoutError(`Operation timed out after ${String(ms)}ms`, ms));\n }, ms);\n\n try {\n return await operation(combinedSignal);\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Combine multiple AbortSignals into one.\n * Returns a signal that aborts when any of the input signals abort.\n *\n * Uses AbortSignal.any() when available (Node 20+, modern browsers).\n * Falls back to a manual implementation that properly cleans up event listeners.\n *\n * @param signals - AbortSignals to combine (undefined values are filtered out)\n * @returns Combined AbortSignal\n */\nexport function combineSignals(...signals: (AbortSignal | undefined)[]): AbortSignal {\n const validSignals = signals.filter((s): s is AbortSignal => s !== undefined);\n\n if (validSignals.length === 0) {\n return new AbortController().signal;\n }\n\n const firstSignal = validSignals[0];\n if (validSignals.length === 1 && firstSignal) {\n return firstSignal;\n }\n\n // Use AbortSignal.any if available (Node 20+, modern browsers)\n if ('any' in AbortSignal) {\n return AbortSignal.any(validSignals);\n }\n\n // Fallback: create a new controller and link it to all signals\n // Track listeners so we can clean them up to prevent memory leaks\n const controller = new AbortController();\n const listeners: { signal: AbortSignal; listener: () => void }[] = [];\n\n // Cleanup function to remove all listeners\n const cleanup = () => {\n for (const { signal, listener } of listeners) {\n signal.removeEventListener('abort', listener);\n }\n listeners.length = 0;\n };\n\n for (const signal of validSignals) {\n if (signal.aborted) {\n controller.abort(signal.reason);\n return controller.signal;\n }\n\n const listener = () => {\n cleanup(); // Clean up all listeners when any signal aborts\n controller.abort(signal.reason);\n };\n\n listeners.push({ signal, listener });\n signal.addEventListener('abort', listener, { once: true });\n }\n\n return controller.signal;\n}\n\n/**\n * Check if an error is an abort error (from AbortController).\n *\n * @param error - Error to check\n * @returns True if the error is an AbortError\n */\nexport function isAbortError(error: unknown): boolean {\n return (\n error instanceof DOMException &&\n error.name === 'AbortError'\n );\n}\n\n/**\n * Throw if the given signal is aborted.\n * Useful for checking signal state early in operations to avoid unnecessary work.\n *\n * @param signal - AbortSignal to check\n * @throws {DOMException} If signal is aborted (AbortError)\n */\nexport function throwIfAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n}\n\n/**\n * Safely execute a callback, catching and logging any errors.\n * Used for user-provided callbacks to prevent them from breaking the pattern.\n *\n * @param callback - Callback to execute\n * @param onError - Optional error handler\n */\nexport function safeCallback<T extends (...args: unknown[]) => unknown>(\n callback: T | undefined,\n onError?: (error: Error) => void\n): T | undefined {\n if (!callback) return undefined;\n\n return ((...args: Parameters<T>): ReturnType<T> | undefined => {\n try {\n return callback(...args) as ReturnType<T>;\n } catch (error) {\n if (onError && error instanceof Error) {\n onError(error);\n }\n return undefined;\n }\n }) as T;\n}\n\n/**\n * Calculate jittered delay.\n * Adds 0-10% random variance to prevent thundering herd.\n *\n * @param delay - Base delay in milliseconds\n * @returns Jittered delay\n */\nexport function addJitter(delay: number): number {\n const jitterFactor = 1 + Math.random() * 0.1; // 0-10% jitter\n return Math.floor(delay * jitterFactor);\n}\n\n/**\n * Clamp a number between min and max values.\n *\n * @param value - Value to clamp\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Clamped value\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\n/**\n * Get current timestamp in milliseconds.\n * Uses performance.now() if available for higher precision.\n *\n * @returns Current timestamp in milliseconds\n */\nexport function now(): number {\n if (typeof performance !== 'undefined' && typeof performance.now === 'function') {\n return performance.now();\n }\n return Date.now();\n}\n","import { z } from 'zod';\n\n/**\n * Schema for a function type (Zod 4 compatible).\n * Uses z.function() which validates that the value is a function.\n */\nconst functionSchema = z.function();\n\n/**\n * Base schema for logger configuration.\n * Validates that the logger has the required methods.\n */\nexport const loggerSchema = z.object({\n debug: functionSchema,\n info: functionSchema,\n warn: functionSchema,\n error: functionSchema,\n}).optional();\n\n/**\n * 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: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type TimeoutConfig = z.infer<typeof timeoutConfigSchema>;\n\n/**\n * Schema for retry backoff policy.\n */\nexport const backoffPolicySchema = z.enum(['exponential', 'linear', 'constant']);\n\nexport type BackoffPolicy = z.infer<typeof backoffPolicySchema>;\n\n/**\n * Schema for retry configuration.\n */\nexport const retryConfigSchema = z.object({\n /** Maximum number of attempts including the first (default: 3) */\n maxAttempts: z.number().int().positive().default(3),\n /** Initial delay before first retry in milliseconds (default: 100) */\n initialDelay: z.number().int().positive().default(100),\n /** Maximum delay between retries in milliseconds */\n maxDelay: z.number().int().positive().optional(),\n /** Backoff strategy (default: 'exponential') */\n backoffPolicy: backoffPolicySchema.default('exponential'),\n /** Multiplier for exponential backoff (default: 2.0) */\n multiplier: z.number().positive().default(2.0),\n /** Add random jitter to delays (default: false) */\n jitter: z.boolean().default(false),\n /** Custom function to determine if error is retryable */\n isRetryable: functionSchema.optional(),\n /** Callback on each retry attempt */\n onRetry: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type RetryConfig = z.infer<typeof retryConfigSchema>;\n\n/**\n * Schema for circuit breaker state.\n */\nexport const circuitBreakerStateSchema = z.enum(['closed', 'open', 'half-open']);\n\nexport type CircuitBreakerState = z.infer<typeof circuitBreakerStateSchema>;\n\n/**\n * Schema for circuit breaker counts/metrics.\n */\nexport const circuitBreakerCountsSchema = z.object({\n requests: z.number().int().nonnegative(),\n totalSuccesses: z.number().int().nonnegative(),\n totalFailures: z.number().int().nonnegative(),\n consecutiveSuccesses: z.number().int().nonnegative(),\n consecutiveFailures: z.number().int().nonnegative(),\n});\n\nexport type CircuitBreakerCounts = z.infer<typeof circuitBreakerCountsSchema>;\n\n/**\n * Schema for circuit breaker configuration.\n */\nexport const circuitBreakerConfigSchema = z.object({\n /** Maximum consecutive failures before opening (default: 5) */\n maxFailures: z.number().int().positive().default(5),\n /** Duration in open state before transitioning to half-open in milliseconds (default: 60000) */\n timeout: z.number().int().positive().default(60000),\n /** Maximum requests allowed in half-open state (default: 1) */\n halfOpenMaxRequests: z.number().int().positive().default(1),\n /** Period to clear counts when closed, 0 means never (default: 0) */\n interval: z.number().int().nonnegative().default(0),\n /** Custom function to determine when to trip the breaker */\n readyToTrip: functionSchema.optional(),\n /** Custom function to determine if result is successful */\n isSuccessful: functionSchema.optional(),\n /** Callback on state change */\n onStateChange: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type CircuitBreakerConfig = z.infer<typeof circuitBreakerConfigSchema>;\n\n/**\n * Schema for rate limiter configuration.\n */\nexport const rateLimitConfigSchema = z.object({\n /** Number of tokens added per interval (default: 100) */\n rate: z.number().int().positive().default(100),\n /** Maximum bucket capacity (defaults to rate) */\n burst: z.number().int().positive().optional(),\n /** Interval for token refill in milliseconds (default: 1000) */\n interval: z.number().int().positive().default(1000),\n /** Callback when rate limit is hit */\n onLimit: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type RateLimitConfig = z.infer<typeof rateLimitConfigSchema>;\n\n/**\n * Schema for bulkhead configuration.\n */\nexport const bulkheadConfigSchema = z.object({\n /** Maximum concurrent executions (default: 10) */\n maxConcurrent: z.number().int().positive().default(10),\n /** Maximum queued requests, 0 means no queueing (default: 0) */\n maxQueue: z.number().int().nonnegative().default(0),\n /** Maximum time to wait in queue in milliseconds */\n queueTimeout: z.number().int().positive().optional(),\n /** Callback when request is rejected */\n onRejected: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type BulkheadConfig = z.infer<typeof bulkheadConfigSchema>;\n\n/**\n * Schema for fallback configuration.\n */\nexport const fallbackConfigSchema = z.object({\n /** Fallback function to execute when primary fails */\n fallback: functionSchema,\n /** Custom function to determine if fallback should be used */\n shouldFallback: functionSchema.optional(),\n /** Callback when fallback is triggered */\n onFallback: functionSchema.optional(),\n /** Callback when primary succeeds */\n onSuccess: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type FallbackConfig = z.infer<typeof fallbackConfigSchema>;\n","import { z } from 'zod';\n\n/** Maximum reasonable token count (1 billion) */\nconst MAX_TOKENS = 1_000_000_000;\n\n/** Maximum reasonable timestamp (year 2100 in ms) */\nconst MAX_TIMESTAMP = 4_102_444_800_000;\n\n/**\n * Zod schema for validating bucket state from external storage.\n * Use this to validate data retrieved from untrusted storage sources.\n * Includes bounds checking to prevent malicious or corrupted data.\n */\nexport const bucketStateSchema = z.object({\n tokens: z.number().nonnegative().max(MAX_TOKENS),\n lastRefill: z.number().int().nonnegative().max(MAX_TIMESTAMP),\n});\n\n/**\n * State of a rate limit bucket for external storage.\n * Contains all data needed to reconstruct bucket state across invocations.\n */\nexport interface BucketState {\n /** Current number of tokens in the bucket */\n readonly tokens: number;\n /** Timestamp (ms since epoch) of last token refill */\n readonly lastRefill: number;\n}\n\n/**\n * Validates bucket state from external storage.\n * Returns the validated state or null if invalid.\n *\n * @param data - Raw data from storage\n * @returns Validated BucketState or null if invalid\n *\n * @example\n * ```typescript\n * const data = await redis.get(key);\n * const state = validateBucketState(JSON.parse(data));\n * if (state === null) {\n * // Invalid data, treat as new bucket\n * }\n * ```\n */\nexport function validateBucketState(data: unknown): BucketState | null {\n const result = bucketStateSchema.safeParse(data);\n return result.success ? result.data : null;\n}\n\n/**\n * Result of a compare-and-set operation.\n */\nexport interface CompareAndSetResult {\n /** Whether the update was successful */\n readonly success: boolean;\n /** The current state (after operation) */\n readonly currentState: BucketState | null;\n}\n\n/** Maximum key length after sanitization */\nconst MAX_KEY_LENGTH = 256;\n\n/** Pre-compiled regex for control characters (for performance) */\n// eslint-disable-next-line no-control-regex\nconst CONTROL_CHARS_REGEX = /[\\x00-\\x1f\\x7f]/g;\n\n/** Pre-compiled regex for path separators (for performance) */\nconst PATH_SEPARATORS_REGEX = /[/\\\\]/g;\n\n/**\n * Sanitizes a storage key to prevent injection attacks.\n * Removes or encodes potentially dangerous characters.\n *\n * Optimization: Truncates before regex processing to limit work on long strings.\n *\n * @param key - Raw key from user input\n * @returns Sanitized key safe for storage operations\n *\n * @example\n * ```typescript\n * const safeKey = sanitizeStorageKey(userProvidedKey);\n * await storage.get(safeKey);\n * ```\n */\nexport function sanitizeStorageKey(key: string): string {\n // Truncate first to limit regex work on long strings\n const truncated = key.length > MAX_KEY_LENGTH ? key.slice(0, MAX_KEY_LENGTH) : key;\n\n // Replace control characters, null bytes, and path separators\n // Keep alphanumeric, dash, underscore, dot, colon, at\n return truncated\n .replace(CONTROL_CHARS_REGEX, '') // Remove control characters\n .replace(PATH_SEPARATORS_REGEX, '_'); // Replace path separators\n}\n\n/**\n * Storage adapter interface for rate limiting.\n *\n * Implement this interface to persist rate limit state across serverless\n * invocations or distributed systems. The storage adapter enables rate\n * limiting in environments where in-memory state doesn't persist.\n *\n * ## Concurrency Warning\n *\n * **Important:** The basic get/set interface has inherent TOCTOU (time-of-check\n * to time-of-use) race conditions in distributed systems. Between reading the\n * bucket state and writing the updated state, another process may have modified\n * the bucket. This can result in rate limits being slightly over or under the\n * configured limit.\n *\n * For precise rate limiting in high-concurrency distributed environments,\n * implement the optional `compareAndSet` method with atomic operations:\n *\n * - **Redis**: Use Lua scripts with WATCH/MULTI/EXEC\n * - **DynamoDB**: Use conditional writes with version checks\n * - **Forge Storage**: Accepts eventual consistency\n *\n * For most use cases, the slight inaccuracy from race conditions is acceptable.\n * The rate limiter still provides effective protection against abuse.\n *\n * @example Redis implementation with atomic operations\n * ```typescript\n * const redisStorage: RateLimitStorage = {\n * async get(key) {\n * const data = await redis.get(`ratelimit:${key}`);\n * if (!data) return null;\n * return validateBucketState(JSON.parse(data));\n * },\n * async set(key, state, ttlMs) {\n * const value = JSON.stringify(state);\n * if (ttlMs) {\n * await redis.set(`ratelimit:${key}`, value, 'PX', ttlMs);\n * } else {\n * await redis.set(`ratelimit:${key}`, value);\n * }\n * },\n * async compareAndSet(key, expected, newState, ttlMs) {\n * // Use Lua script for atomic compare-and-set\n * const script = `\n * local current = redis.call('GET', KEYS[1])\n * if current == ARGV[1] or (current == false and ARGV[1] == 'null') then\n * if ARGV[3] then\n * redis.call('SET', KEYS[1], ARGV[2], 'PX', ARGV[3])\n * else\n * redis.call('SET', KEYS[1], ARGV[2])\n * end\n * return {1, ARGV[2]}\n * end\n * return {0, current or 'null'}\n * `;\n * const expectedStr = expected ? JSON.stringify(expected) : 'null';\n * const [success, currentStr] = await redis.eval(script, 1,\n * `ratelimit:${key}`, expectedStr, JSON.stringify(newState), ttlMs?.toString()\n * );\n * return {\n * success: success === 1,\n * currentState: currentStr === 'null' ? null : validateBucketState(JSON.parse(currentStr))\n * };\n * },\n * async delete(key) {\n * await redis.del(`ratelimit:${key}`);\n * },\n * async clear() {\n * const keys = await redis.keys('ratelimit:*');\n * if (keys.length > 0) {\n * await redis.del(...keys);\n * }\n * }\n * };\n * ```\n *\n * @example Forge Storage implementation (accepts eventual consistency)\n * ```typescript\n * import { storage } from '@forge/api';\n *\n * const forgeStorage: RateLimitStorage = {\n * async get(key) {\n * const data = await storage.get(`ratelimit:${key}`);\n * return validateBucketState(data);\n * },\n * async set(key, state) {\n * await storage.set(`ratelimit:${key}`, state);\n * },\n * async delete(key) {\n * await storage.delete(`ratelimit:${key}`);\n * }\n * };\n * ```\n */\nexport interface RateLimitStorage {\n /**\n * Retrieve bucket state for a key.\n *\n * @param key - The rate limiting key (already sanitized)\n * @returns The bucket state, or null if not found\n */\n get(key: string): Promise<BucketState | null>;\n\n /**\n * Store bucket state for a key.\n *\n * @param key - The rate limiting key (already sanitized)\n * @param state - The bucket state to store\n * @param ttlMs - Optional TTL in milliseconds for automatic cleanup.\n * Recommended: set to interval * (burst / rate) * 2 to auto-expire\n * stale buckets while keeping active ones alive.\n */\n set(key: string, state: BucketState, ttlMs?: number): Promise<void>;\n\n /**\n * Atomically compare and set bucket state.\n * Optional - implement for precise rate limiting in distributed systems.\n *\n * This method should atomically:\n * 1. Check if the current state matches `expected`\n * 2. If it matches, update to `newState` and return success\n * 3. If it doesn't match, return failure with the current state\n *\n * @param key - The rate limiting key (already sanitized)\n * @param expected - The expected current state (null if expecting no entry)\n * @param newState - The new state to set if expected matches\n * @param ttlMs - Optional TTL in milliseconds\n * @returns Result indicating success and current state\n */\n compareAndSet?(\n key: string,\n expected: BucketState | null,\n newState: BucketState,\n ttlMs?: number\n ): Promise<CompareAndSetResult>;\n\n /**\n * Delete bucket state for a key.\n * Optional - if not implemented, reset operations on individual keys\n * will not be supported.\n *\n * @param key - The rate limiting key (already sanitized)\n */\n delete?(key: string): Promise<void>;\n\n /**\n * Clear all bucket states.\n * Optional - if not implemented, full reset operations will not clear storage.\n */\n clear?(): Promise<void>;\n}\n\n/**\n * In-memory storage implementation using Map.\n * This is the default storage used when no external storage is provided.\n *\n * Features:\n * - LRU eviction when maxEntries is exceeded\n * - Synchronous operations (async interface for compatibility)\n * - No persistence across process restarts\n * - No race conditions (single-threaded JavaScript)\n *\n * @example\n * ```typescript\n * const storage = new MemoryStorage({ maxEntries: 10000 });\n * const limiter = new RateLimiter({ rate: 100, storage });\n * ```\n */\nexport class MemoryStorage implements RateLimitStorage {\n /** Cached resolved promise for void returns to avoid allocation */\n private static readonly RESOLVED_VOID = Promise.resolve();\n\n /** Cached resolved promise for null returns to avoid allocation */\n private static readonly RESOLVED_NULL: Promise<BucketState | null> = Promise.resolve(null);\n\n private readonly entries = new Map<string, BucketState>();\n private readonly maxEntries: number;\n private evictionCount = 0;\n\n /**\n * Create a new in-memory storage.\n *\n * @param options - Storage options\n * @param options.maxEntries - Maximum entries before LRU eviction (0 = unlimited, default: 10000)\n */\n constructor(options?: { readonly maxEntries?: number }) {\n this.maxEntries = options?.maxEntries ?? 10000;\n }\n\n get(key: string): Promise<BucketState | null> {\n const state = this.getSync(key);\n return state === null ? MemoryStorage.RESOLVED_NULL : Promise.resolve(state);\n }\n\n set(key: string, state: BucketState): Promise<void> {\n this.setSync(key, state);\n return MemoryStorage.RESOLVED_VOID;\n }\n\n compareAndSet(\n key: string,\n expected: BucketState | null,\n newState: BucketState\n ): Promise<CompareAndSetResult> {\n const current = this.entries.get(key) ?? null;\n\n // Check if current matches expected\n const matches =\n current === expected ||\n (current !== null &&\n expected !== null &&\n current.tokens === expected.tokens &&\n current.lastRefill === expected.lastRefill);\n\n if (matches) {\n this.setSync(key, newState);\n return Promise.resolve({ success: true, currentState: newState });\n }\n\n return Promise.resolve({ success: false, currentState: current });\n }\n\n delete(key: string): Promise<void> {\n this.deleteSync(key);\n return MemoryStorage.RESOLVED_VOID;\n }\n\n clear(): Promise<void> {\n this.clearSync();\n return MemoryStorage.RESOLVED_VOID;\n }\n\n /**\n * Get the current number of entries.\n */\n size(): number {\n return this.entries.size;\n }\n\n /**\n * Get the total number of evictions since creation.\n */\n getEvictionCount(): number {\n return this.evictionCount;\n }\n\n /**\n * Synchronous get for backward compatibility with sync rate limiter methods.\n * @internal\n */\n getSync(key: string): BucketState | null {\n const state = this.entries.get(key);\n if (state) {\n // LRU touch: move to end\n this.entries.delete(key);\n this.entries.set(key, state);\n }\n return state ?? null;\n }\n\n /**\n * Synchronous set for backward compatibility with sync rate limiter methods.\n * @internal\n */\n setSync(key: string, state: BucketState): void {\n if (this.entries.has(key)) {\n this.entries.delete(key);\n } else if (this.maxEntries > 0 && this.entries.size >= this.maxEntries) {\n const firstKey = this.entries.keys().next().value;\n if (firstKey !== undefined) {\n this.entries.delete(firstKey);\n this.evictionCount++;\n }\n }\n this.entries.set(key, state);\n }\n\n /**\n * Synchronous delete for backward compatibility.\n * @internal\n */\n deleteSync(key: string): void {\n this.entries.delete(key);\n }\n\n /**\n * Synchronous clear for backward compatibility.\n * @internal\n */\n clearSync(): void {\n this.entries.clear();\n }\n}\n"]} |
+272
-1
@@ -400,2 +400,273 @@ import { z } from 'zod'; | ||
| export { type BackoffPolicy, BulkheadClosedError, type BulkheadConfig, BulkheadFullError, type CircuitBreakerConfig, type CircuitBreakerCounts, type CircuitBreakerState, CircuitOpenError, type Closeable, type ErrorCallback, type FallbackConfig, FortifyError, type FortifyLogger, MaxAttemptsReachedError, type Operation, type Pattern, type RateLimitCallback, type RateLimitConfig, RateLimitExceededError, type Resettable, type RetryCallback, type RetryConfig, type RetryableError, RetryableErrorWrapper, type StateChangeCallback, type TimeoutConfig, TimeoutError, type VoidCallback, addJitter, asNonRetryable, asRetryable, backoffPolicySchema, bulkheadConfigSchema, circuitBreakerConfigSchema, circuitBreakerCountsSchema, circuitBreakerStateSchema, clamp, combineSignals, consoleLogger, executeWithTimeout, fallbackConfigSchema, isAbortError, isRetryable, isRetryableError, loggerSchema, noopLogger, now, rateLimitConfigSchema, retryConfigSchema, safeCallback, sleep, throwIfAborted, timeoutConfigSchema, withTimeout }; | ||
| /** | ||
| * Zod schema for validating bucket state from external storage. | ||
| * Use this to validate data retrieved from untrusted storage sources. | ||
| * Includes bounds checking to prevent malicious or corrupted data. | ||
| */ | ||
| declare const bucketStateSchema: z.ZodObject<{ | ||
| tokens: z.ZodNumber; | ||
| lastRefill: z.ZodNumber; | ||
| }, z.core.$strip>; | ||
| /** | ||
| * State of a rate limit bucket for external storage. | ||
| * Contains all data needed to reconstruct bucket state across invocations. | ||
| */ | ||
| interface BucketState { | ||
| /** Current number of tokens in the bucket */ | ||
| readonly tokens: number; | ||
| /** Timestamp (ms since epoch) of last token refill */ | ||
| readonly lastRefill: number; | ||
| } | ||
| /** | ||
| * Validates bucket state from external storage. | ||
| * Returns the validated state or null if invalid. | ||
| * | ||
| * @param data - Raw data from storage | ||
| * @returns Validated BucketState or null if invalid | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const data = await redis.get(key); | ||
| * const state = validateBucketState(JSON.parse(data)); | ||
| * if (state === null) { | ||
| * // Invalid data, treat as new bucket | ||
| * } | ||
| * ``` | ||
| */ | ||
| declare function validateBucketState(data: unknown): BucketState | null; | ||
| /** | ||
| * Result of a compare-and-set operation. | ||
| */ | ||
| interface CompareAndSetResult { | ||
| /** Whether the update was successful */ | ||
| readonly success: boolean; | ||
| /** The current state (after operation) */ | ||
| readonly currentState: BucketState | null; | ||
| } | ||
| /** | ||
| * Sanitizes a storage key to prevent injection attacks. | ||
| * Removes or encodes potentially dangerous characters. | ||
| * | ||
| * Optimization: Truncates before regex processing to limit work on long strings. | ||
| * | ||
| * @param key - Raw key from user input | ||
| * @returns Sanitized key safe for storage operations | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const safeKey = sanitizeStorageKey(userProvidedKey); | ||
| * await storage.get(safeKey); | ||
| * ``` | ||
| */ | ||
| declare function sanitizeStorageKey(key: string): string; | ||
| /** | ||
| * Storage adapter interface for rate limiting. | ||
| * | ||
| * Implement this interface to persist rate limit state across serverless | ||
| * invocations or distributed systems. The storage adapter enables rate | ||
| * limiting in environments where in-memory state doesn't persist. | ||
| * | ||
| * ## Concurrency Warning | ||
| * | ||
| * **Important:** The basic get/set interface has inherent TOCTOU (time-of-check | ||
| * to time-of-use) race conditions in distributed systems. Between reading the | ||
| * bucket state and writing the updated state, another process may have modified | ||
| * the bucket. This can result in rate limits being slightly over or under the | ||
| * configured limit. | ||
| * | ||
| * For precise rate limiting in high-concurrency distributed environments, | ||
| * implement the optional `compareAndSet` method with atomic operations: | ||
| * | ||
| * - **Redis**: Use Lua scripts with WATCH/MULTI/EXEC | ||
| * - **DynamoDB**: Use conditional writes with version checks | ||
| * - **Forge Storage**: Accepts eventual consistency | ||
| * | ||
| * For most use cases, the slight inaccuracy from race conditions is acceptable. | ||
| * The rate limiter still provides effective protection against abuse. | ||
| * | ||
| * @example Redis implementation with atomic operations | ||
| * ```typescript | ||
| * const redisStorage: RateLimitStorage = { | ||
| * async get(key) { | ||
| * const data = await redis.get(`ratelimit:${key}`); | ||
| * if (!data) return null; | ||
| * return validateBucketState(JSON.parse(data)); | ||
| * }, | ||
| * async set(key, state, ttlMs) { | ||
| * const value = JSON.stringify(state); | ||
| * if (ttlMs) { | ||
| * await redis.set(`ratelimit:${key}`, value, 'PX', ttlMs); | ||
| * } else { | ||
| * await redis.set(`ratelimit:${key}`, value); | ||
| * } | ||
| * }, | ||
| * async compareAndSet(key, expected, newState, ttlMs) { | ||
| * // Use Lua script for atomic compare-and-set | ||
| * const script = ` | ||
| * local current = redis.call('GET', KEYS[1]) | ||
| * if current == ARGV[1] or (current == false and ARGV[1] == 'null') then | ||
| * if ARGV[3] then | ||
| * redis.call('SET', KEYS[1], ARGV[2], 'PX', ARGV[3]) | ||
| * else | ||
| * redis.call('SET', KEYS[1], ARGV[2]) | ||
| * end | ||
| * return {1, ARGV[2]} | ||
| * end | ||
| * return {0, current or 'null'} | ||
| * `; | ||
| * const expectedStr = expected ? JSON.stringify(expected) : 'null'; | ||
| * const [success, currentStr] = await redis.eval(script, 1, | ||
| * `ratelimit:${key}`, expectedStr, JSON.stringify(newState), ttlMs?.toString() | ||
| * ); | ||
| * return { | ||
| * success: success === 1, | ||
| * currentState: currentStr === 'null' ? null : validateBucketState(JSON.parse(currentStr)) | ||
| * }; | ||
| * }, | ||
| * async delete(key) { | ||
| * await redis.del(`ratelimit:${key}`); | ||
| * }, | ||
| * async clear() { | ||
| * const keys = await redis.keys('ratelimit:*'); | ||
| * if (keys.length > 0) { | ||
| * await redis.del(...keys); | ||
| * } | ||
| * } | ||
| * }; | ||
| * ``` | ||
| * | ||
| * @example Forge Storage implementation (accepts eventual consistency) | ||
| * ```typescript | ||
| * import { storage } from '@forge/api'; | ||
| * | ||
| * const forgeStorage: RateLimitStorage = { | ||
| * async get(key) { | ||
| * const data = await storage.get(`ratelimit:${key}`); | ||
| * return validateBucketState(data); | ||
| * }, | ||
| * async set(key, state) { | ||
| * await storage.set(`ratelimit:${key}`, state); | ||
| * }, | ||
| * async delete(key) { | ||
| * await storage.delete(`ratelimit:${key}`); | ||
| * } | ||
| * }; | ||
| * ``` | ||
| */ | ||
| interface RateLimitStorage { | ||
| /** | ||
| * Retrieve bucket state for a key. | ||
| * | ||
| * @param key - The rate limiting key (already sanitized) | ||
| * @returns The bucket state, or null if not found | ||
| */ | ||
| get(key: string): Promise<BucketState | null>; | ||
| /** | ||
| * Store bucket state for a key. | ||
| * | ||
| * @param key - The rate limiting key (already sanitized) | ||
| * @param state - The bucket state to store | ||
| * @param ttlMs - Optional TTL in milliseconds for automatic cleanup. | ||
| * Recommended: set to interval * (burst / rate) * 2 to auto-expire | ||
| * stale buckets while keeping active ones alive. | ||
| */ | ||
| set(key: string, state: BucketState, ttlMs?: number): Promise<void>; | ||
| /** | ||
| * Atomically compare and set bucket state. | ||
| * Optional - implement for precise rate limiting in distributed systems. | ||
| * | ||
| * This method should atomically: | ||
| * 1. Check if the current state matches `expected` | ||
| * 2. If it matches, update to `newState` and return success | ||
| * 3. If it doesn't match, return failure with the current state | ||
| * | ||
| * @param key - The rate limiting key (already sanitized) | ||
| * @param expected - The expected current state (null if expecting no entry) | ||
| * @param newState - The new state to set if expected matches | ||
| * @param ttlMs - Optional TTL in milliseconds | ||
| * @returns Result indicating success and current state | ||
| */ | ||
| compareAndSet?(key: string, expected: BucketState | null, newState: BucketState, ttlMs?: number): Promise<CompareAndSetResult>; | ||
| /** | ||
| * Delete bucket state for a key. | ||
| * Optional - if not implemented, reset operations on individual keys | ||
| * will not be supported. | ||
| * | ||
| * @param key - The rate limiting key (already sanitized) | ||
| */ | ||
| delete?(key: string): Promise<void>; | ||
| /** | ||
| * Clear all bucket states. | ||
| * Optional - if not implemented, full reset operations will not clear storage. | ||
| */ | ||
| clear?(): Promise<void>; | ||
| } | ||
| /** | ||
| * In-memory storage implementation using Map. | ||
| * This is the default storage used when no external storage is provided. | ||
| * | ||
| * Features: | ||
| * - LRU eviction when maxEntries is exceeded | ||
| * - Synchronous operations (async interface for compatibility) | ||
| * - No persistence across process restarts | ||
| * - No race conditions (single-threaded JavaScript) | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const storage = new MemoryStorage({ maxEntries: 10000 }); | ||
| * const limiter = new RateLimiter({ rate: 100, storage }); | ||
| * ``` | ||
| */ | ||
| declare class MemoryStorage implements RateLimitStorage { | ||
| /** Cached resolved promise for void returns to avoid allocation */ | ||
| private static readonly RESOLVED_VOID; | ||
| /** Cached resolved promise for null returns to avoid allocation */ | ||
| private static readonly RESOLVED_NULL; | ||
| private readonly entries; | ||
| private readonly maxEntries; | ||
| private evictionCount; | ||
| /** | ||
| * Create a new in-memory storage. | ||
| * | ||
| * @param options - Storage options | ||
| * @param options.maxEntries - Maximum entries before LRU eviction (0 = unlimited, default: 10000) | ||
| */ | ||
| constructor(options?: { | ||
| readonly maxEntries?: number; | ||
| }); | ||
| get(key: string): Promise<BucketState | null>; | ||
| set(key: string, state: BucketState): Promise<void>; | ||
| compareAndSet(key: string, expected: BucketState | null, newState: BucketState): Promise<CompareAndSetResult>; | ||
| delete(key: string): Promise<void>; | ||
| clear(): Promise<void>; | ||
| /** | ||
| * Get the current number of entries. | ||
| */ | ||
| size(): number; | ||
| /** | ||
| * Get the total number of evictions since creation. | ||
| */ | ||
| getEvictionCount(): number; | ||
| /** | ||
| * Synchronous get for backward compatibility with sync rate limiter methods. | ||
| * @internal | ||
| */ | ||
| getSync(key: string): BucketState | null; | ||
| /** | ||
| * Synchronous set for backward compatibility with sync rate limiter methods. | ||
| * @internal | ||
| */ | ||
| setSync(key: string, state: BucketState): void; | ||
| /** | ||
| * Synchronous delete for backward compatibility. | ||
| * @internal | ||
| */ | ||
| deleteSync(key: string): void; | ||
| /** | ||
| * Synchronous clear for backward compatibility. | ||
| * @internal | ||
| */ | ||
| clearSync(): void; | ||
| } | ||
| export { type BackoffPolicy, type BucketState, BulkheadClosedError, type BulkheadConfig, BulkheadFullError, type CircuitBreakerConfig, type CircuitBreakerCounts, type CircuitBreakerState, CircuitOpenError, type Closeable, type CompareAndSetResult, type ErrorCallback, type FallbackConfig, FortifyError, type FortifyLogger, MaxAttemptsReachedError, MemoryStorage, type Operation, type Pattern, type RateLimitCallback, type RateLimitConfig, RateLimitExceededError, type RateLimitStorage, type Resettable, type RetryCallback, type RetryConfig, type RetryableError, RetryableErrorWrapper, type StateChangeCallback, type TimeoutConfig, TimeoutError, type VoidCallback, addJitter, asNonRetryable, asRetryable, backoffPolicySchema, bucketStateSchema, bulkheadConfigSchema, circuitBreakerConfigSchema, circuitBreakerCountsSchema, circuitBreakerStateSchema, clamp, combineSignals, consoleLogger, executeWithTimeout, fallbackConfigSchema, isAbortError, isRetryable, isRetryableError, loggerSchema, noopLogger, now, rateLimitConfigSchema, retryConfigSchema, safeCallback, sanitizeStorageKey, sleep, throwIfAborted, timeoutConfigSchema, validateBucketState, withTimeout }; |
+272
-1
@@ -400,2 +400,273 @@ import { z } from 'zod'; | ||
| export { type BackoffPolicy, BulkheadClosedError, type BulkheadConfig, BulkheadFullError, type CircuitBreakerConfig, type CircuitBreakerCounts, type CircuitBreakerState, CircuitOpenError, type Closeable, type ErrorCallback, type FallbackConfig, FortifyError, type FortifyLogger, MaxAttemptsReachedError, type Operation, type Pattern, type RateLimitCallback, type RateLimitConfig, RateLimitExceededError, type Resettable, type RetryCallback, type RetryConfig, type RetryableError, RetryableErrorWrapper, type StateChangeCallback, type TimeoutConfig, TimeoutError, type VoidCallback, addJitter, asNonRetryable, asRetryable, backoffPolicySchema, bulkheadConfigSchema, circuitBreakerConfigSchema, circuitBreakerCountsSchema, circuitBreakerStateSchema, clamp, combineSignals, consoleLogger, executeWithTimeout, fallbackConfigSchema, isAbortError, isRetryable, isRetryableError, loggerSchema, noopLogger, now, rateLimitConfigSchema, retryConfigSchema, safeCallback, sleep, throwIfAborted, timeoutConfigSchema, withTimeout }; | ||
| /** | ||
| * Zod schema for validating bucket state from external storage. | ||
| * Use this to validate data retrieved from untrusted storage sources. | ||
| * Includes bounds checking to prevent malicious or corrupted data. | ||
| */ | ||
| declare const bucketStateSchema: z.ZodObject<{ | ||
| tokens: z.ZodNumber; | ||
| lastRefill: z.ZodNumber; | ||
| }, z.core.$strip>; | ||
| /** | ||
| * State of a rate limit bucket for external storage. | ||
| * Contains all data needed to reconstruct bucket state across invocations. | ||
| */ | ||
| interface BucketState { | ||
| /** Current number of tokens in the bucket */ | ||
| readonly tokens: number; | ||
| /** Timestamp (ms since epoch) of last token refill */ | ||
| readonly lastRefill: number; | ||
| } | ||
| /** | ||
| * Validates bucket state from external storage. | ||
| * Returns the validated state or null if invalid. | ||
| * | ||
| * @param data - Raw data from storage | ||
| * @returns Validated BucketState or null if invalid | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const data = await redis.get(key); | ||
| * const state = validateBucketState(JSON.parse(data)); | ||
| * if (state === null) { | ||
| * // Invalid data, treat as new bucket | ||
| * } | ||
| * ``` | ||
| */ | ||
| declare function validateBucketState(data: unknown): BucketState | null; | ||
| /** | ||
| * Result of a compare-and-set operation. | ||
| */ | ||
| interface CompareAndSetResult { | ||
| /** Whether the update was successful */ | ||
| readonly success: boolean; | ||
| /** The current state (after operation) */ | ||
| readonly currentState: BucketState | null; | ||
| } | ||
| /** | ||
| * Sanitizes a storage key to prevent injection attacks. | ||
| * Removes or encodes potentially dangerous characters. | ||
| * | ||
| * Optimization: Truncates before regex processing to limit work on long strings. | ||
| * | ||
| * @param key - Raw key from user input | ||
| * @returns Sanitized key safe for storage operations | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const safeKey = sanitizeStorageKey(userProvidedKey); | ||
| * await storage.get(safeKey); | ||
| * ``` | ||
| */ | ||
| declare function sanitizeStorageKey(key: string): string; | ||
| /** | ||
| * Storage adapter interface for rate limiting. | ||
| * | ||
| * Implement this interface to persist rate limit state across serverless | ||
| * invocations or distributed systems. The storage adapter enables rate | ||
| * limiting in environments where in-memory state doesn't persist. | ||
| * | ||
| * ## Concurrency Warning | ||
| * | ||
| * **Important:** The basic get/set interface has inherent TOCTOU (time-of-check | ||
| * to time-of-use) race conditions in distributed systems. Between reading the | ||
| * bucket state and writing the updated state, another process may have modified | ||
| * the bucket. This can result in rate limits being slightly over or under the | ||
| * configured limit. | ||
| * | ||
| * For precise rate limiting in high-concurrency distributed environments, | ||
| * implement the optional `compareAndSet` method with atomic operations: | ||
| * | ||
| * - **Redis**: Use Lua scripts with WATCH/MULTI/EXEC | ||
| * - **DynamoDB**: Use conditional writes with version checks | ||
| * - **Forge Storage**: Accepts eventual consistency | ||
| * | ||
| * For most use cases, the slight inaccuracy from race conditions is acceptable. | ||
| * The rate limiter still provides effective protection against abuse. | ||
| * | ||
| * @example Redis implementation with atomic operations | ||
| * ```typescript | ||
| * const redisStorage: RateLimitStorage = { | ||
| * async get(key) { | ||
| * const data = await redis.get(`ratelimit:${key}`); | ||
| * if (!data) return null; | ||
| * return validateBucketState(JSON.parse(data)); | ||
| * }, | ||
| * async set(key, state, ttlMs) { | ||
| * const value = JSON.stringify(state); | ||
| * if (ttlMs) { | ||
| * await redis.set(`ratelimit:${key}`, value, 'PX', ttlMs); | ||
| * } else { | ||
| * await redis.set(`ratelimit:${key}`, value); | ||
| * } | ||
| * }, | ||
| * async compareAndSet(key, expected, newState, ttlMs) { | ||
| * // Use Lua script for atomic compare-and-set | ||
| * const script = ` | ||
| * local current = redis.call('GET', KEYS[1]) | ||
| * if current == ARGV[1] or (current == false and ARGV[1] == 'null') then | ||
| * if ARGV[3] then | ||
| * redis.call('SET', KEYS[1], ARGV[2], 'PX', ARGV[3]) | ||
| * else | ||
| * redis.call('SET', KEYS[1], ARGV[2]) | ||
| * end | ||
| * return {1, ARGV[2]} | ||
| * end | ||
| * return {0, current or 'null'} | ||
| * `; | ||
| * const expectedStr = expected ? JSON.stringify(expected) : 'null'; | ||
| * const [success, currentStr] = await redis.eval(script, 1, | ||
| * `ratelimit:${key}`, expectedStr, JSON.stringify(newState), ttlMs?.toString() | ||
| * ); | ||
| * return { | ||
| * success: success === 1, | ||
| * currentState: currentStr === 'null' ? null : validateBucketState(JSON.parse(currentStr)) | ||
| * }; | ||
| * }, | ||
| * async delete(key) { | ||
| * await redis.del(`ratelimit:${key}`); | ||
| * }, | ||
| * async clear() { | ||
| * const keys = await redis.keys('ratelimit:*'); | ||
| * if (keys.length > 0) { | ||
| * await redis.del(...keys); | ||
| * } | ||
| * } | ||
| * }; | ||
| * ``` | ||
| * | ||
| * @example Forge Storage implementation (accepts eventual consistency) | ||
| * ```typescript | ||
| * import { storage } from '@forge/api'; | ||
| * | ||
| * const forgeStorage: RateLimitStorage = { | ||
| * async get(key) { | ||
| * const data = await storage.get(`ratelimit:${key}`); | ||
| * return validateBucketState(data); | ||
| * }, | ||
| * async set(key, state) { | ||
| * await storage.set(`ratelimit:${key}`, state); | ||
| * }, | ||
| * async delete(key) { | ||
| * await storage.delete(`ratelimit:${key}`); | ||
| * } | ||
| * }; | ||
| * ``` | ||
| */ | ||
| interface RateLimitStorage { | ||
| /** | ||
| * Retrieve bucket state for a key. | ||
| * | ||
| * @param key - The rate limiting key (already sanitized) | ||
| * @returns The bucket state, or null if not found | ||
| */ | ||
| get(key: string): Promise<BucketState | null>; | ||
| /** | ||
| * Store bucket state for a key. | ||
| * | ||
| * @param key - The rate limiting key (already sanitized) | ||
| * @param state - The bucket state to store | ||
| * @param ttlMs - Optional TTL in milliseconds for automatic cleanup. | ||
| * Recommended: set to interval * (burst / rate) * 2 to auto-expire | ||
| * stale buckets while keeping active ones alive. | ||
| */ | ||
| set(key: string, state: BucketState, ttlMs?: number): Promise<void>; | ||
| /** | ||
| * Atomically compare and set bucket state. | ||
| * Optional - implement for precise rate limiting in distributed systems. | ||
| * | ||
| * This method should atomically: | ||
| * 1. Check if the current state matches `expected` | ||
| * 2. If it matches, update to `newState` and return success | ||
| * 3. If it doesn't match, return failure with the current state | ||
| * | ||
| * @param key - The rate limiting key (already sanitized) | ||
| * @param expected - The expected current state (null if expecting no entry) | ||
| * @param newState - The new state to set if expected matches | ||
| * @param ttlMs - Optional TTL in milliseconds | ||
| * @returns Result indicating success and current state | ||
| */ | ||
| compareAndSet?(key: string, expected: BucketState | null, newState: BucketState, ttlMs?: number): Promise<CompareAndSetResult>; | ||
| /** | ||
| * Delete bucket state for a key. | ||
| * Optional - if not implemented, reset operations on individual keys | ||
| * will not be supported. | ||
| * | ||
| * @param key - The rate limiting key (already sanitized) | ||
| */ | ||
| delete?(key: string): Promise<void>; | ||
| /** | ||
| * Clear all bucket states. | ||
| * Optional - if not implemented, full reset operations will not clear storage. | ||
| */ | ||
| clear?(): Promise<void>; | ||
| } | ||
| /** | ||
| * In-memory storage implementation using Map. | ||
| * This is the default storage used when no external storage is provided. | ||
| * | ||
| * Features: | ||
| * - LRU eviction when maxEntries is exceeded | ||
| * - Synchronous operations (async interface for compatibility) | ||
| * - No persistence across process restarts | ||
| * - No race conditions (single-threaded JavaScript) | ||
| * | ||
| * @example | ||
| * ```typescript | ||
| * const storage = new MemoryStorage({ maxEntries: 10000 }); | ||
| * const limiter = new RateLimiter({ rate: 100, storage }); | ||
| * ``` | ||
| */ | ||
| declare class MemoryStorage implements RateLimitStorage { | ||
| /** Cached resolved promise for void returns to avoid allocation */ | ||
| private static readonly RESOLVED_VOID; | ||
| /** Cached resolved promise for null returns to avoid allocation */ | ||
| private static readonly RESOLVED_NULL; | ||
| private readonly entries; | ||
| private readonly maxEntries; | ||
| private evictionCount; | ||
| /** | ||
| * Create a new in-memory storage. | ||
| * | ||
| * @param options - Storage options | ||
| * @param options.maxEntries - Maximum entries before LRU eviction (0 = unlimited, default: 10000) | ||
| */ | ||
| constructor(options?: { | ||
| readonly maxEntries?: number; | ||
| }); | ||
| get(key: string): Promise<BucketState | null>; | ||
| set(key: string, state: BucketState): Promise<void>; | ||
| compareAndSet(key: string, expected: BucketState | null, newState: BucketState): Promise<CompareAndSetResult>; | ||
| delete(key: string): Promise<void>; | ||
| clear(): Promise<void>; | ||
| /** | ||
| * Get the current number of entries. | ||
| */ | ||
| size(): number; | ||
| /** | ||
| * Get the total number of evictions since creation. | ||
| */ | ||
| getEvictionCount(): number; | ||
| /** | ||
| * Synchronous get for backward compatibility with sync rate limiter methods. | ||
| * @internal | ||
| */ | ||
| getSync(key: string): BucketState | null; | ||
| /** | ||
| * Synchronous set for backward compatibility with sync rate limiter methods. | ||
| * @internal | ||
| */ | ||
| setSync(key: string, state: BucketState): void; | ||
| /** | ||
| * Synchronous delete for backward compatibility. | ||
| * @internal | ||
| */ | ||
| deleteSync(key: string): void; | ||
| /** | ||
| * Synchronous clear for backward compatibility. | ||
| * @internal | ||
| */ | ||
| clearSync(): void; | ||
| } | ||
| export { type BackoffPolicy, type BucketState, BulkheadClosedError, type BulkheadConfig, BulkheadFullError, type CircuitBreakerConfig, type CircuitBreakerCounts, type CircuitBreakerState, CircuitOpenError, type Closeable, type CompareAndSetResult, type ErrorCallback, type FallbackConfig, FortifyError, type FortifyLogger, MaxAttemptsReachedError, MemoryStorage, type Operation, type Pattern, type RateLimitCallback, type RateLimitConfig, RateLimitExceededError, type RateLimitStorage, type Resettable, type RetryCallback, type RetryConfig, type RetryableError, RetryableErrorWrapper, type StateChangeCallback, type TimeoutConfig, TimeoutError, type VoidCallback, addJitter, asNonRetryable, asRetryable, backoffPolicySchema, bucketStateSchema, bulkheadConfigSchema, circuitBreakerConfigSchema, circuitBreakerCountsSchema, circuitBreakerStateSchema, clamp, combineSignals, consoleLogger, executeWithTimeout, fallbackConfigSchema, isAbortError, isRetryable, isRetryableError, loggerSchema, noopLogger, now, rateLimitConfigSchema, retryConfigSchema, safeCallback, sanitizeStorageKey, sleep, throwIfAborted, timeoutConfigSchema, validateBucketState, withTimeout }; |
+116
-1
@@ -373,5 +373,120 @@ import { z } from 'zod'; | ||
| }); | ||
| var MAX_TOKENS = 1e9; | ||
| var MAX_TIMESTAMP = 41024448e5; | ||
| var bucketStateSchema = z.object({ | ||
| tokens: z.number().nonnegative().max(MAX_TOKENS), | ||
| lastRefill: z.number().int().nonnegative().max(MAX_TIMESTAMP) | ||
| }); | ||
| function validateBucketState(data) { | ||
| const result = bucketStateSchema.safeParse(data); | ||
| return result.success ? result.data : null; | ||
| } | ||
| var MAX_KEY_LENGTH = 256; | ||
| var CONTROL_CHARS_REGEX = /[\x00-\x1f\x7f]/g; | ||
| var PATH_SEPARATORS_REGEX = /[/\\]/g; | ||
| function sanitizeStorageKey(key) { | ||
| const truncated = key.length > MAX_KEY_LENGTH ? key.slice(0, MAX_KEY_LENGTH) : key; | ||
| return truncated.replace(CONTROL_CHARS_REGEX, "").replace(PATH_SEPARATORS_REGEX, "_"); | ||
| } | ||
| var _MemoryStorage = class _MemoryStorage { | ||
| /** | ||
| * Create a new in-memory storage. | ||
| * | ||
| * @param options - Storage options | ||
| * @param options.maxEntries - Maximum entries before LRU eviction (0 = unlimited, default: 10000) | ||
| */ | ||
| constructor(options) { | ||
| __publicField(this, "entries", /* @__PURE__ */ new Map()); | ||
| __publicField(this, "maxEntries"); | ||
| __publicField(this, "evictionCount", 0); | ||
| this.maxEntries = options?.maxEntries ?? 1e4; | ||
| } | ||
| get(key) { | ||
| const state = this.getSync(key); | ||
| return state === null ? _MemoryStorage.RESOLVED_NULL : Promise.resolve(state); | ||
| } | ||
| set(key, state) { | ||
| this.setSync(key, state); | ||
| return _MemoryStorage.RESOLVED_VOID; | ||
| } | ||
| compareAndSet(key, expected, newState) { | ||
| const current = this.entries.get(key) ?? null; | ||
| const matches = current === expected || current !== null && expected !== null && current.tokens === expected.tokens && current.lastRefill === expected.lastRefill; | ||
| if (matches) { | ||
| this.setSync(key, newState); | ||
| return Promise.resolve({ success: true, currentState: newState }); | ||
| } | ||
| return Promise.resolve({ success: false, currentState: current }); | ||
| } | ||
| delete(key) { | ||
| this.deleteSync(key); | ||
| return _MemoryStorage.RESOLVED_VOID; | ||
| } | ||
| clear() { | ||
| this.clearSync(); | ||
| return _MemoryStorage.RESOLVED_VOID; | ||
| } | ||
| /** | ||
| * Get the current number of entries. | ||
| */ | ||
| size() { | ||
| return this.entries.size; | ||
| } | ||
| /** | ||
| * Get the total number of evictions since creation. | ||
| */ | ||
| getEvictionCount() { | ||
| return this.evictionCount; | ||
| } | ||
| /** | ||
| * Synchronous get for backward compatibility with sync rate limiter methods. | ||
| * @internal | ||
| */ | ||
| getSync(key) { | ||
| const state = this.entries.get(key); | ||
| if (state) { | ||
| this.entries.delete(key); | ||
| this.entries.set(key, state); | ||
| } | ||
| return state ?? null; | ||
| } | ||
| /** | ||
| * Synchronous set for backward compatibility with sync rate limiter methods. | ||
| * @internal | ||
| */ | ||
| setSync(key, state) { | ||
| if (this.entries.has(key)) { | ||
| this.entries.delete(key); | ||
| } else if (this.maxEntries > 0 && this.entries.size >= this.maxEntries) { | ||
| const firstKey = this.entries.keys().next().value; | ||
| if (firstKey !== void 0) { | ||
| this.entries.delete(firstKey); | ||
| this.evictionCount++; | ||
| } | ||
| } | ||
| this.entries.set(key, state); | ||
| } | ||
| /** | ||
| * Synchronous delete for backward compatibility. | ||
| * @internal | ||
| */ | ||
| deleteSync(key) { | ||
| this.entries.delete(key); | ||
| } | ||
| /** | ||
| * Synchronous clear for backward compatibility. | ||
| * @internal | ||
| */ | ||
| clearSync() { | ||
| this.entries.clear(); | ||
| } | ||
| }; | ||
| /** Cached resolved promise for void returns to avoid allocation */ | ||
| __publicField(_MemoryStorage, "RESOLVED_VOID", Promise.resolve()); | ||
| /** Cached resolved promise for null returns to avoid allocation */ | ||
| __publicField(_MemoryStorage, "RESOLVED_NULL", Promise.resolve(null)); | ||
| var MemoryStorage = _MemoryStorage; | ||
| export { BulkheadClosedError, BulkheadFullError, CircuitOpenError, FortifyError, MaxAttemptsReachedError, RateLimitExceededError, RetryableErrorWrapper, TimeoutError, addJitter, asNonRetryable, asRetryable, backoffPolicySchema, bulkheadConfigSchema, circuitBreakerConfigSchema, circuitBreakerCountsSchema, circuitBreakerStateSchema, clamp, combineSignals, consoleLogger, executeWithTimeout, fallbackConfigSchema, isAbortError, isRetryable, isRetryableError, loggerSchema, noopLogger, now, rateLimitConfigSchema, retryConfigSchema, safeCallback, sleep, throwIfAborted, timeoutConfigSchema, withTimeout }; | ||
| export { BulkheadClosedError, BulkheadFullError, CircuitOpenError, FortifyError, MaxAttemptsReachedError, MemoryStorage, RateLimitExceededError, RetryableErrorWrapper, TimeoutError, addJitter, asNonRetryable, asRetryable, backoffPolicySchema, bucketStateSchema, bulkheadConfigSchema, circuitBreakerConfigSchema, circuitBreakerCountsSchema, circuitBreakerStateSchema, clamp, combineSignals, consoleLogger, executeWithTimeout, fallbackConfigSchema, isAbortError, isRetryable, isRetryableError, loggerSchema, noopLogger, now, rateLimitConfigSchema, retryConfigSchema, safeCallback, sanitizeStorageKey, sleep, throwIfAborted, timeoutConfigSchema, validateBucketState, withTimeout }; | ||
| //# sourceMappingURL=index.js.map | ||
| //# sourceMappingURL=index.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/errors.ts","../src/types.ts","../src/utils.ts","../src/schemas.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Base error class for all Fortify errors.\n */\nexport class FortifyError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'FortifyError';\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n // Use typeof check to avoid unnecessary-condition lint error\n if (typeof Error.captureStackTrace === 'function') {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Error thrown when circuit breaker is in open state and rejecting requests.\n */\nexport class CircuitOpenError extends FortifyError {\n constructor(message = 'Circuit breaker is open') {\n super(message);\n this.name = 'CircuitOpenError';\n }\n}\n\n/**\n * Error thrown when rate limit is exceeded.\n */\nexport class RateLimitExceededError extends FortifyError {\n public readonly key: string | undefined;\n\n constructor(message = 'Rate limit exceeded', key?: string) {\n super(message);\n this.name = 'RateLimitExceededError';\n this.key = key;\n }\n}\n\n/**\n * Error thrown when bulkhead is at capacity.\n */\nexport class BulkheadFullError extends FortifyError {\n public readonly activeCount: number;\n public readonly queuedCount: number;\n\n constructor(\n message = 'Bulkhead is full',\n activeCount = 0,\n queuedCount = 0\n ) {\n super(message);\n this.name = 'BulkheadFullError';\n this.activeCount = activeCount;\n this.queuedCount = queuedCount;\n }\n}\n\n/**\n * Error thrown when operation times out.\n */\nexport class TimeoutError extends FortifyError {\n public readonly timeoutMs: number;\n\n constructor(message = 'Operation timed out', timeoutMs = 0) {\n super(message);\n this.name = 'TimeoutError';\n this.timeoutMs = timeoutMs;\n }\n}\n\n/**\n * Error thrown when maximum retry attempts are reached.\n */\nexport class MaxAttemptsReachedError extends FortifyError {\n public readonly attempts: number;\n public readonly lastError: Error | undefined;\n\n constructor(\n message = 'Maximum retry attempts reached',\n attempts = 0,\n lastError?: Error\n ) {\n super(message);\n this.name = 'MaxAttemptsReachedError';\n this.attempts = attempts;\n this.lastError = lastError;\n }\n}\n\n/**\n * Error thrown when bulkhead is closed and no new operations accepted.\n */\nexport class BulkheadClosedError extends FortifyError {\n constructor(message = 'Bulkhead is closed') {\n super(message);\n this.name = 'BulkheadClosedError';\n }\n}\n\n/**\n * Interface for errors that can indicate whether they are retryable.\n */\nexport interface RetryableError {\n retryable: boolean;\n}\n\n/**\n * Type guard to check if an error implements RetryableError interface.\n */\nexport function isRetryableError(error: unknown): error is Error & RetryableError {\n return (\n error instanceof Error &&\n 'retryable' in error &&\n typeof (error as RetryableError).retryable === 'boolean'\n );\n}\n\n/**\n * Wrapper class to mark an error as retryable.\n */\nexport class RetryableErrorWrapper extends Error implements RetryableError {\n public readonly retryable: boolean;\n public override readonly cause: Error;\n\n constructor(error: Error, retryable: boolean) {\n super(error.message, { cause: error });\n this.name = 'RetryableErrorWrapper';\n this.retryable = retryable;\n this.cause = error;\n if (error.stack) {\n this.stack = error.stack;\n }\n }\n}\n\n/**\n * Wrap an error as retryable.\n */\nexport function asRetryable(error: Error, retryable = true): RetryableErrorWrapper {\n return new RetryableErrorWrapper(error, retryable);\n}\n\n/**\n * Wrap an error as non-retryable.\n */\nexport function asNonRetryable(error: Error): RetryableErrorWrapper {\n return new RetryableErrorWrapper(error, false);\n}\n\n/**\n * Check if an error is retryable.\n * Returns true if the error implements RetryableError and retryable is true.\n * Returns undefined if the error doesn't implement RetryableError (let caller decide).\n */\nexport function isRetryable(error: unknown): boolean | undefined {\n if (isRetryableError(error)) {\n return error.retryable;\n }\n return undefined;\n}\n","/**\n * Represents an async operation that can be executed with cancellation support.\n * The signal parameter allows the operation to be cancelled via AbortController.\n *\n * @template T - The return type of the operation\n */\nexport type Operation<T> = (signal: AbortSignal) => Promise<T>;\n\n/**\n * Callback for state changes in stateful patterns (e.g., circuit breaker).\n *\n * @template S - The state type\n */\nexport type StateChangeCallback<S> = (from: S, to: S) => void;\n\n/**\n * Callback for error events.\n */\nexport type ErrorCallback = (error: Error) => void;\n\n/**\n * Simple void callback.\n */\nexport type VoidCallback = () => void;\n\n/**\n * Callback for retry events.\n */\nexport type RetryCallback = (attempt: number, error: Error) => void;\n\n/**\n * Callback for rate limit events.\n */\nexport type RateLimitCallback = (key: string) => void;\n\n/**\n * Logger interface for structured logging across all patterns.\n * Compatible with pino, winston, console, and custom implementations.\n */\nexport interface FortifyLogger {\n debug(msg: string, context?: Record<string, unknown>): void;\n info(msg: string, context?: Record<string, unknown>): void;\n warn(msg: string, context?: Record<string, unknown>): void;\n error(msg: string, context?: Record<string, unknown>): void;\n}\n\n/**\n * No-operation logger that discards all log messages.\n * Useful for testing or when logging is not needed.\n */\nexport const noopLogger: FortifyLogger = {\n debug: () => undefined,\n info: () => undefined,\n warn: () => undefined,\n error: () => undefined,\n};\n\n/**\n * Console-based logger implementation.\n * Browser-friendly and works in all environments.\n */\nexport const consoleLogger: FortifyLogger = {\n debug: (msg, context) => {\n if (context) {\n console.debug(`[fortify] ${msg}`, context);\n } else {\n console.debug(`[fortify] ${msg}`);\n }\n },\n info: (msg, context) => {\n if (context) {\n console.info(`[fortify] ${msg}`, context);\n } else {\n console.info(`[fortify] ${msg}`);\n }\n },\n warn: (msg, context) => {\n if (context) {\n console.warn(`[fortify] ${msg}`, context);\n } else {\n console.warn(`[fortify] ${msg}`);\n }\n },\n error: (msg, context) => {\n if (context) {\n console.error(`[fortify] ${msg}`, context);\n } else {\n console.error(`[fortify] ${msg}`);\n }\n },\n};\n\n/**\n * Generic pattern interface that all resilience patterns implement.\n *\n * @template T - The return type of the operation\n */\nexport interface Pattern<T> {\n /**\n * Execute an operation with the pattern's resilience logic.\n *\n * @param operation - The async operation to execute\n * @param signal - Optional AbortSignal for cancellation\n * @returns Promise resolving to the operation result\n */\n execute(operation: Operation<T>, signal?: AbortSignal): Promise<T>;\n}\n\n/**\n * Interface for patterns that support closing/cleanup.\n */\nexport interface Closeable {\n /**\n * Close the pattern and release resources.\n * May wait for in-flight operations to complete.\n */\n close(): Promise<void>;\n}\n\n/**\n * Interface for patterns that support resetting state.\n */\nexport interface Resettable {\n /**\n * Reset the pattern to its initial state.\n */\n reset(): void;\n}\n","import { TimeoutError } from './errors.js';\n\n/**\n * Sleep for a specified duration with optional cancellation support.\n *\n * @param ms - Duration in milliseconds\n * @param signal - Optional AbortSignal for cancellation\n * @returns Promise that resolves after the delay or rejects if cancelled\n */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(ensureError(signal.reason));\n return;\n }\n\n const timeoutId = setTimeout(resolve, ms);\n\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timeoutId);\n reject(ensureError(signal.reason));\n },\n { once: true }\n );\n });\n}\n\n/**\n * Ensure a value is an Error instance.\n * @param reason - The reason to convert to an Error\n * @returns An Error instance\n */\nfunction ensureError(reason: unknown): Error {\n if (reason instanceof Error) {\n return reason;\n }\n if (typeof reason === 'string') {\n return new Error(reason);\n }\n if (reason !== undefined && reason !== null) {\n // Handle objects and other types safely\n try {\n return new Error(JSON.stringify(reason));\n } catch {\n return new Error('Unknown error');\n }\n }\n return new DOMException('Aborted', 'AbortError');\n}\n\n/**\n * Wrap a promise with a timeout.\n *\n * @template T - The promise return type\n * @param promise - The promise to wrap\n * @param ms - Timeout duration in milliseconds\n * @param signal - Optional external AbortSignal for cancellation\n * @returns Promise that resolves with the result or rejects with TimeoutError\n */\nexport async function withTimeout<T>(\n promise: Promise<T>,\n ms: number,\n signal?: AbortSignal\n): Promise<T> {\n // Check if already aborted\n if (signal?.aborted) {\n throw ensureError(signal.reason);\n }\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new TimeoutError(`Operation timed out after ${String(ms)}ms`, ms));\n }, ms);\n });\n\n // Handle external signal\n const abortPromise = signal\n ? new Promise<never>((_, reject) => {\n signal.addEventListener(\n 'abort',\n () => reject(ensureError(signal.reason)),\n { once: true }\n );\n })\n : null;\n\n try {\n const racers: Promise<T>[] = [promise, timeoutPromise];\n if (abortPromise) {\n racers.push(abortPromise);\n }\n return await Promise.race(racers);\n } finally {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n }\n}\n\n/**\n * Execute an operation with a timeout, passing the combined signal to the operation.\n *\n * @template T - The operation return type\n * @param operation - The operation to execute\n * @param ms - Timeout duration in milliseconds\n * @param signal - Optional external AbortSignal for cancellation\n * @returns Promise that resolves with the result or rejects with TimeoutError\n */\nexport async function executeWithTimeout<T>(\n operation: (signal: AbortSignal) => Promise<T>,\n ms: number,\n signal?: AbortSignal\n): Promise<T> {\n const controller = new AbortController();\n\n // Combine signals\n const combinedSignal = combineSignals(signal, controller.signal);\n\n const timeoutId = setTimeout(() => {\n controller.abort(new TimeoutError(`Operation timed out after ${String(ms)}ms`, ms));\n }, ms);\n\n try {\n return await operation(combinedSignal);\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Combine multiple AbortSignals into one.\n * Returns a signal that aborts when any of the input signals abort.\n *\n * Uses AbortSignal.any() when available (Node 20+, modern browsers).\n * Falls back to a manual implementation that properly cleans up event listeners.\n *\n * @param signals - AbortSignals to combine (undefined values are filtered out)\n * @returns Combined AbortSignal\n */\nexport function combineSignals(...signals: (AbortSignal | undefined)[]): AbortSignal {\n const validSignals = signals.filter((s): s is AbortSignal => s !== undefined);\n\n if (validSignals.length === 0) {\n return new AbortController().signal;\n }\n\n const firstSignal = validSignals[0];\n if (validSignals.length === 1 && firstSignal) {\n return firstSignal;\n }\n\n // Use AbortSignal.any if available (Node 20+, modern browsers)\n if ('any' in AbortSignal) {\n return AbortSignal.any(validSignals);\n }\n\n // Fallback: create a new controller and link it to all signals\n // Track listeners so we can clean them up to prevent memory leaks\n const controller = new AbortController();\n const listeners: { signal: AbortSignal; listener: () => void }[] = [];\n\n // Cleanup function to remove all listeners\n const cleanup = () => {\n for (const { signal, listener } of listeners) {\n signal.removeEventListener('abort', listener);\n }\n listeners.length = 0;\n };\n\n for (const signal of validSignals) {\n if (signal.aborted) {\n controller.abort(signal.reason);\n return controller.signal;\n }\n\n const listener = () => {\n cleanup(); // Clean up all listeners when any signal aborts\n controller.abort(signal.reason);\n };\n\n listeners.push({ signal, listener });\n signal.addEventListener('abort', listener, { once: true });\n }\n\n return controller.signal;\n}\n\n/**\n * Check if an error is an abort error (from AbortController).\n *\n * @param error - Error to check\n * @returns True if the error is an AbortError\n */\nexport function isAbortError(error: unknown): boolean {\n return (\n error instanceof DOMException &&\n error.name === 'AbortError'\n );\n}\n\n/**\n * Throw if the given signal is aborted.\n * Useful for checking signal state early in operations to avoid unnecessary work.\n *\n * @param signal - AbortSignal to check\n * @throws {DOMException} If signal is aborted (AbortError)\n */\nexport function throwIfAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n}\n\n/**\n * Safely execute a callback, catching and logging any errors.\n * Used for user-provided callbacks to prevent them from breaking the pattern.\n *\n * @param callback - Callback to execute\n * @param onError - Optional error handler\n */\nexport function safeCallback<T extends (...args: unknown[]) => unknown>(\n callback: T | undefined,\n onError?: (error: Error) => void\n): T | undefined {\n if (!callback) return undefined;\n\n return ((...args: Parameters<T>): ReturnType<T> | undefined => {\n try {\n return callback(...args) as ReturnType<T>;\n } catch (error) {\n if (onError && error instanceof Error) {\n onError(error);\n }\n return undefined;\n }\n }) as T;\n}\n\n/**\n * Calculate jittered delay.\n * Adds 0-10% random variance to prevent thundering herd.\n *\n * @param delay - Base delay in milliseconds\n * @returns Jittered delay\n */\nexport function addJitter(delay: number): number {\n const jitterFactor = 1 + Math.random() * 0.1; // 0-10% jitter\n return Math.floor(delay * jitterFactor);\n}\n\n/**\n * Clamp a number between min and max values.\n *\n * @param value - Value to clamp\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Clamped value\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\n/**\n * Get current timestamp in milliseconds.\n * Uses performance.now() if available for higher precision.\n *\n * @returns Current timestamp in milliseconds\n */\nexport function now(): number {\n if (typeof performance !== 'undefined' && typeof performance.now === 'function') {\n return performance.now();\n }\n return Date.now();\n}\n","import { z } from 'zod';\n\n/**\n * Schema for a function type (Zod 4 compatible).\n * Uses z.function() which validates that the value is a function.\n */\nconst functionSchema = z.function();\n\n/**\n * Base schema for logger configuration.\n * Validates that the logger has the required methods.\n */\nexport const loggerSchema = z.object({\n debug: functionSchema,\n info: functionSchema,\n warn: functionSchema,\n error: functionSchema,\n}).optional();\n\n/**\n * 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: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type TimeoutConfig = z.infer<typeof timeoutConfigSchema>;\n\n/**\n * Schema for retry backoff policy.\n */\nexport const backoffPolicySchema = z.enum(['exponential', 'linear', 'constant']);\n\nexport type BackoffPolicy = z.infer<typeof backoffPolicySchema>;\n\n/**\n * Schema for retry configuration.\n */\nexport const retryConfigSchema = z.object({\n /** Maximum number of attempts including the first (default: 3) */\n maxAttempts: z.number().int().positive().default(3),\n /** Initial delay before first retry in milliseconds (default: 100) */\n initialDelay: z.number().int().positive().default(100),\n /** Maximum delay between retries in milliseconds */\n maxDelay: z.number().int().positive().optional(),\n /** Backoff strategy (default: 'exponential') */\n backoffPolicy: backoffPolicySchema.default('exponential'),\n /** Multiplier for exponential backoff (default: 2.0) */\n multiplier: z.number().positive().default(2.0),\n /** Add random jitter to delays (default: false) */\n jitter: z.boolean().default(false),\n /** Custom function to determine if error is retryable */\n isRetryable: functionSchema.optional(),\n /** Callback on each retry attempt */\n onRetry: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type RetryConfig = z.infer<typeof retryConfigSchema>;\n\n/**\n * Schema for circuit breaker state.\n */\nexport const circuitBreakerStateSchema = z.enum(['closed', 'open', 'half-open']);\n\nexport type CircuitBreakerState = z.infer<typeof circuitBreakerStateSchema>;\n\n/**\n * Schema for circuit breaker counts/metrics.\n */\nexport const circuitBreakerCountsSchema = z.object({\n requests: z.number().int().nonnegative(),\n totalSuccesses: z.number().int().nonnegative(),\n totalFailures: z.number().int().nonnegative(),\n consecutiveSuccesses: z.number().int().nonnegative(),\n consecutiveFailures: z.number().int().nonnegative(),\n});\n\nexport type CircuitBreakerCounts = z.infer<typeof circuitBreakerCountsSchema>;\n\n/**\n * Schema for circuit breaker configuration.\n */\nexport const circuitBreakerConfigSchema = z.object({\n /** Maximum consecutive failures before opening (default: 5) */\n maxFailures: z.number().int().positive().default(5),\n /** Duration in open state before transitioning to half-open in milliseconds (default: 60000) */\n timeout: z.number().int().positive().default(60000),\n /** Maximum requests allowed in half-open state (default: 1) */\n halfOpenMaxRequests: z.number().int().positive().default(1),\n /** Period to clear counts when closed, 0 means never (default: 0) */\n interval: z.number().int().nonnegative().default(0),\n /** Custom function to determine when to trip the breaker */\n readyToTrip: functionSchema.optional(),\n /** Custom function to determine if result is successful */\n isSuccessful: functionSchema.optional(),\n /** Callback on state change */\n onStateChange: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type CircuitBreakerConfig = z.infer<typeof circuitBreakerConfigSchema>;\n\n/**\n * Schema for rate limiter configuration.\n */\nexport const rateLimitConfigSchema = z.object({\n /** Number of tokens added per interval (default: 100) */\n rate: z.number().int().positive().default(100),\n /** Maximum bucket capacity (defaults to rate) */\n burst: z.number().int().positive().optional(),\n /** Interval for token refill in milliseconds (default: 1000) */\n interval: z.number().int().positive().default(1000),\n /** Callback when rate limit is hit */\n onLimit: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type RateLimitConfig = z.infer<typeof rateLimitConfigSchema>;\n\n/**\n * Schema for bulkhead configuration.\n */\nexport const bulkheadConfigSchema = z.object({\n /** Maximum concurrent executions (default: 10) */\n maxConcurrent: z.number().int().positive().default(10),\n /** Maximum queued requests, 0 means no queueing (default: 0) */\n maxQueue: z.number().int().nonnegative().default(0),\n /** Maximum time to wait in queue in milliseconds */\n queueTimeout: z.number().int().positive().optional(),\n /** Callback when request is rejected */\n onRejected: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type BulkheadConfig = z.infer<typeof bulkheadConfigSchema>;\n\n/**\n * Schema for fallback configuration.\n */\nexport const fallbackConfigSchema = z.object({\n /** Fallback function to execute when primary fails */\n fallback: functionSchema,\n /** Custom function to determine if fallback should be used */\n shouldFallback: functionSchema.optional(),\n /** Callback when fallback is triggered */\n onFallback: functionSchema.optional(),\n /** Callback when primary succeeds */\n onSuccess: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type FallbackConfig = z.infer<typeof fallbackConfigSchema>;\n"]} | ||
| {"version":3,"sources":["../src/errors.ts","../src/types.ts","../src/utils.ts","../src/schemas.ts","../src/storage.ts"],"names":["z"],"mappings":";;;;;;;AAGO,IAAM,YAAA,GAAN,cAA2B,KAAA,CAAM;AAAA,EACtC,YAAY,OAAA,EAAiB;AAC3B,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,cAAA;AAGZ,IAAA,IAAI,OAAO,KAAA,CAAM,iBAAA,KAAsB,UAAA,EAAY;AACjD,MAAA,KAAA,CAAM,iBAAA,CAAkB,IAAA,EAAM,IAAA,CAAK,WAAW,CAAA;AAAA,IAChD;AAAA,EACF;AACF;AAKO,IAAM,gBAAA,GAAN,cAA+B,YAAA,CAAa;AAAA,EACjD,WAAA,CAAY,UAAU,yBAAA,EAA2B;AAC/C,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,kBAAA;AAAA,EACd;AACF;AAKO,IAAM,sBAAA,GAAN,cAAqC,YAAA,CAAa;AAAA,EAGvD,WAAA,CAAY,OAAA,GAAU,qBAAA,EAAuB,GAAA,EAAc;AACzD,IAAA,KAAA,CAAM,OAAO,CAAA;AAHf,IAAA,aAAA,CAAA,IAAA,EAAgB,KAAA,CAAA;AAId,IAAA,IAAA,CAAK,IAAA,GAAO,wBAAA;AACZ,IAAA,IAAA,CAAK,GAAA,GAAM,GAAA;AAAA,EACb;AACF;AAKO,IAAM,iBAAA,GAAN,cAAgC,YAAA,CAAa;AAAA,EAIlD,YACE,OAAA,GAAU,kBAAA,EACV,WAAA,GAAc,CAAA,EACd,cAAc,CAAA,EACd;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AARf,IAAA,aAAA,CAAA,IAAA,EAAgB,aAAA,CAAA;AAChB,IAAA,aAAA,CAAA,IAAA,EAAgB,aAAA,CAAA;AAQd,IAAA,IAAA,CAAK,IAAA,GAAO,mBAAA;AACZ,IAAA,IAAA,CAAK,WAAA,GAAc,WAAA;AACnB,IAAA,IAAA,CAAK,WAAA,GAAc,WAAA;AAAA,EACrB;AACF;AAKO,IAAM,YAAA,GAAN,cAA2B,YAAA,CAAa;AAAA,EAG7C,WAAA,CAAY,OAAA,GAAU,qBAAA,EAAuB,SAAA,GAAY,CAAA,EAAG;AAC1D,IAAA,KAAA,CAAM,OAAO,CAAA;AAHf,IAAA,aAAA,CAAA,IAAA,EAAgB,WAAA,CAAA;AAId,IAAA,IAAA,CAAK,IAAA,GAAO,cAAA;AACZ,IAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AAAA,EACnB;AACF;AAKO,IAAM,uBAAA,GAAN,cAAsC,YAAA,CAAa;AAAA,EAIxD,WAAA,CACE,OAAA,GAAU,gCAAA,EACV,QAAA,GAAW,GACX,SAAA,EACA;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AARf,IAAA,aAAA,CAAA,IAAA,EAAgB,UAAA,CAAA;AAChB,IAAA,aAAA,CAAA,IAAA,EAAgB,WAAA,CAAA;AAQd,IAAA,IAAA,CAAK,IAAA,GAAO,yBAAA;AACZ,IAAA,IAAA,CAAK,QAAA,GAAW,QAAA;AAChB,IAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AAAA,EACnB;AACF;AAKO,IAAM,mBAAA,GAAN,cAAkC,YAAA,CAAa;AAAA,EACpD,WAAA,CAAY,UAAU,oBAAA,EAAsB;AAC1C,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,qBAAA;AAAA,EACd;AACF;AAYO,SAAS,iBAAiB,KAAA,EAAiD;AAChF,EAAA,OACE,iBAAiB,KAAA,IACjB,WAAA,IAAe,KAAA,IACf,OAAQ,MAAyB,SAAA,KAAc,SAAA;AAEnD;AAKO,IAAM,qBAAA,GAAN,cAAoC,KAAA,CAAgC;AAAA,EAIzE,WAAA,CAAY,OAAc,SAAA,EAAoB;AAC5C,IAAA,KAAA,CAAM,KAAA,CAAM,OAAA,EAAS,EAAE,KAAA,EAAO,OAAO,CAAA;AAJvC,IAAA,aAAA,CAAA,IAAA,EAAgB,WAAA,CAAA;AAChB,IAAA,aAAA,CAAA,IAAA,EAAyB,OAAA,CAAA;AAIvB,IAAA,IAAA,CAAK,IAAA,GAAO,uBAAA;AACZ,IAAA,IAAA,CAAK,SAAA,GAAY,SAAA;AACjB,IAAA,IAAA,CAAK,KAAA,GAAQ,KAAA;AACb,IAAA,IAAI,MAAM,KAAA,EAAO;AACf,MAAA,IAAA,CAAK,QAAQ,KAAA,CAAM,KAAA;AAAA,IACrB;AAAA,EACF;AACF;AAKO,SAAS,WAAA,CAAY,KAAA,EAAc,SAAA,GAAY,IAAA,EAA6B;AACjF,EAAA,OAAO,IAAI,qBAAA,CAAsB,KAAA,EAAO,SAAS,CAAA;AACnD;AAKO,SAAS,eAAe,KAAA,EAAqC;AAClE,EAAA,OAAO,IAAI,qBAAA,CAAsB,KAAA,EAAO,KAAK,CAAA;AAC/C;AAOO,SAAS,YAAY,KAAA,EAAqC;AAC/D,EAAA,IAAI,gBAAA,CAAiB,KAAK,CAAA,EAAG;AAC3B,IAAA,OAAO,KAAA,CAAM,SAAA;AAAA,EACf;AACA,EAAA,OAAO,MAAA;AACT;;;AC7GO,IAAM,UAAA,GAA4B;AAAA,EACvC,OAAO,MAAM,MAAA;AAAA,EACb,MAAM,MAAM,MAAA;AAAA,EACZ,MAAM,MAAM,MAAA;AAAA,EACZ,OAAO,MAAM;AACf;AAMO,IAAM,aAAA,GAA+B;AAAA,EAC1C,KAAA,EAAO,CAAC,GAAA,EAAK,OAAA,KAAY;AACvB,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,UAAA,EAAa,GAAG,CAAA,CAAA,EAAI,OAAO,CAAA;AAAA,IAC3C,CAAA,MAAO;AACL,MAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,UAAA,EAAa,GAAG,CAAA,CAAE,CAAA;AAAA,IAClC;AAAA,EACF,CAAA;AAAA,EACA,IAAA,EAAM,CAAC,GAAA,EAAK,OAAA,KAAY;AACtB,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,UAAA,EAAa,GAAG,CAAA,CAAA,EAAI,OAAO,CAAA;AAAA,IAC1C,CAAA,MAAO;AACL,MAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,UAAA,EAAa,GAAG,CAAA,CAAE,CAAA;AAAA,IACjC;AAAA,EACF,CAAA;AAAA,EACA,IAAA,EAAM,CAAC,GAAA,EAAK,OAAA,KAAY;AACtB,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,UAAA,EAAa,GAAG,CAAA,CAAA,EAAI,OAAO,CAAA;AAAA,IAC1C,CAAA,MAAO;AACL,MAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,UAAA,EAAa,GAAG,CAAA,CAAE,CAAA;AAAA,IACjC;AAAA,EACF,CAAA;AAAA,EACA,KAAA,EAAO,CAAC,GAAA,EAAK,OAAA,KAAY;AACvB,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,UAAA,EAAa,GAAG,CAAA,CAAA,EAAI,OAAO,CAAA;AAAA,IAC3C,CAAA,MAAO;AACL,MAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,UAAA,EAAa,GAAG,CAAA,CAAE,CAAA;AAAA,IAClC;AAAA,EACF;AACF;;;ACjFO,SAAS,KAAA,CAAM,IAAY,MAAA,EAAqC;AACrE,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,IAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,MAAA,MAAA,CAAO,WAAA,CAAY,MAAA,CAAO,MAAM,CAAC,CAAA;AACjC,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,SAAA,GAAY,UAAA,CAAW,OAAA,EAAS,EAAE,CAAA;AAExC,IAAA,MAAA,EAAQ,gBAAA;AAAA,MACN,OAAA;AAAA,MACA,MAAM;AACJ,QAAA,YAAA,CAAa,SAAS,CAAA;AACtB,QAAA,MAAA,CAAO,WAAA,CAAY,MAAA,CAAO,MAAM,CAAC,CAAA;AAAA,MACnC,CAAA;AAAA,MACA,EAAE,MAAM,IAAA;AAAK,KACf;AAAA,EACF,CAAC,CAAA;AACH;AAOA,SAAS,YAAY,MAAA,EAAwB;AAC3C,EAAA,IAAI,kBAAkB,KAAA,EAAO;AAC3B,IAAA,OAAO,MAAA;AAAA,EACT;AACA,EAAA,IAAI,OAAO,WAAW,QAAA,EAAU;AAC9B,IAAA,OAAO,IAAI,MAAM,MAAM,CAAA;AAAA,EACzB;AACA,EAAA,IAAI,MAAA,KAAW,MAAA,IAAa,MAAA,KAAW,IAAA,EAAM;AAE3C,IAAA,IAAI;AACF,MAAA,OAAO,IAAI,KAAA,CAAM,IAAA,CAAK,SAAA,CAAU,MAAM,CAAC,CAAA;AAAA,IACzC,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,IAAI,MAAM,eAAe,CAAA;AAAA,IAClC;AAAA,EACF;AACA,EAAA,OAAO,IAAI,YAAA,CAAa,SAAA,EAAW,YAAY,CAAA;AACjD;AAWA,eAAsB,WAAA,CACpB,OAAA,EACA,EAAA,EACA,MAAA,EACY;AAEZ,EAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,IAAA,MAAM,WAAA,CAAY,OAAO,MAAM,CAAA;AAAA,EACjC;AAEA,EAAA,IAAI,SAAA;AAEJ,EAAA,MAAM,cAAA,GAAiB,IAAI,OAAA,CAAe,CAAC,GAAG,MAAA,KAAW;AACvD,IAAA,SAAA,GAAY,WAAW,MAAM;AAC3B,MAAA,MAAA,CAAO,IAAI,aAAa,CAAA,0BAAA,EAA6B,MAAA,CAAO,EAAE,CAAC,CAAA,EAAA,CAAA,EAAM,EAAE,CAAC,CAAA;AAAA,IAC1E,GAAG,EAAE,CAAA;AAAA,EACP,CAAC,CAAA;AAGD,EAAA,MAAM,eAAe,MAAA,GACjB,IAAI,OAAA,CAAe,CAAC,GAAG,MAAA,KAAW;AAChC,IAAA,MAAA,CAAO,gBAAA;AAAA,MACL,OAAA;AAAA,MACA,MAAM,MAAA,CAAO,WAAA,CAAY,MAAA,CAAO,MAAM,CAAC,CAAA;AAAA,MACvC,EAAE,MAAM,IAAA;AAAK,KACf;AAAA,EACF,CAAC,CAAA,GACD,IAAA;AAEJ,EAAA,IAAI;AACF,IAAA,MAAM,MAAA,GAAuB,CAAC,OAAA,EAAS,cAAc,CAAA;AACrD,IAAA,IAAI,YAAA,EAAc;AAChB,MAAA,MAAA,CAAO,KAAK,YAAY,CAAA;AAAA,IAC1B;AACA,IAAA,OAAO,MAAM,OAAA,CAAQ,IAAA,CAAK,MAAM,CAAA;AAAA,EAClC,CAAA,SAAE;AACA,IAAA,IAAI,cAAc,MAAA,EAAW;AAC3B,MAAA,YAAA,CAAa,SAAS,CAAA;AAAA,IACxB;AAAA,EACF;AACF;AAWA,eAAsB,kBAAA,CACpB,SAAA,EACA,EAAA,EACA,MAAA,EACY;AACZ,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AAGvC,EAAA,MAAM,cAAA,GAAiB,cAAA,CAAe,MAAA,EAAQ,UAAA,CAAW,MAAM,CAAA;AAE/D,EAAA,MAAM,SAAA,GAAY,WAAW,MAAM;AACjC,IAAA,UAAA,CAAW,KAAA,CAAM,IAAI,YAAA,CAAa,CAAA,0BAAA,EAA6B,OAAO,EAAE,CAAC,CAAA,EAAA,CAAA,EAAM,EAAE,CAAC,CAAA;AAAA,EACpF,GAAG,EAAE,CAAA;AAEL,EAAA,IAAI;AACF,IAAA,OAAO,MAAM,UAAU,cAAc,CAAA;AAAA,EACvC,CAAA,SAAE;AACA,IAAA,YAAA,CAAa,SAAS,CAAA;AAAA,EACxB;AACF;AAYO,SAAS,kBAAkB,OAAA,EAAmD;AACnF,EAAA,MAAM,eAAe,OAAA,CAAQ,MAAA,CAAO,CAAC,CAAA,KAAwB,MAAM,MAAS,CAAA;AAE5E,EAAA,IAAI,YAAA,CAAa,WAAW,CAAA,EAAG;AAC7B,IAAA,OAAO,IAAI,iBAAgB,CAAE,MAAA;AAAA,EAC/B;AAEA,EAAA,MAAM,WAAA,GAAc,aAAa,CAAC,CAAA;AAClC,EAAA,IAAI,YAAA,CAAa,MAAA,KAAW,CAAA,IAAK,WAAA,EAAa;AAC5C,IAAA,OAAO,WAAA;AAAA,EACT;AAGA,EAAA,IAAI,SAAS,WAAA,EAAa;AACxB,IAAA,OAAO,WAAA,CAAY,IAAI,YAAY,CAAA;AAAA,EACrC;AAIA,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,MAAM,YAA6D,EAAC;AAGpE,EAAA,MAAM,UAAU,MAAM;AACpB,IAAA,KAAA,MAAW,EAAE,MAAA,EAAQ,QAAA,EAAS,IAAK,SAAA,EAAW;AAC5C,MAAA,MAAA,CAAO,mBAAA,CAAoB,SAAS,QAAQ,CAAA;AAAA,IAC9C;AACA,IAAA,SAAA,CAAU,MAAA,GAAS,CAAA;AAAA,EACrB,CAAA;AAEA,EAAA,KAAA,MAAW,UAAU,YAAA,EAAc;AACjC,IAAA,IAAI,OAAO,OAAA,EAAS;AAClB,MAAA,UAAA,CAAW,KAAA,CAAM,OAAO,MAAM,CAAA;AAC9B,MAAA,OAAO,UAAA,CAAW,MAAA;AAAA,IACpB;AAEA,IAAA,MAAM,WAAW,MAAM;AACrB,MAAA,OAAA,EAAQ;AACR,MAAA,UAAA,CAAW,KAAA,CAAM,OAAO,MAAM,CAAA;AAAA,IAChC,CAAA;AAEA,IAAA,SAAA,CAAU,IAAA,CAAK,EAAE,MAAA,EAAQ,QAAA,EAAU,CAAA;AACnC,IAAA,MAAA,CAAO,iBAAiB,OAAA,EAAS,QAAA,EAAU,EAAE,IAAA,EAAM,MAAM,CAAA;AAAA,EAC3D;AAEA,EAAA,OAAO,UAAA,CAAW,MAAA;AACpB;AAQO,SAAS,aAAa,KAAA,EAAyB;AACpD,EAAA,OACE,KAAA,YAAiB,YAAA,IACjB,KAAA,CAAM,IAAA,KAAS,YAAA;AAEnB;AASO,SAAS,eAAe,MAAA,EAAuC;AACpE,EAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,IAAA,MAAM,MAAA,CAAO,MAAA,IAAU,IAAI,YAAA,CAAa,WAAW,YAAY,CAAA;AAAA,EACjE;AACF;AASO,SAAS,YAAA,CACd,UACA,OAAA,EACe;AACf,EAAA,IAAI,CAAC,UAAU,OAAO,MAAA;AAEtB,EAAA,QAAQ,IAAI,IAAA,KAAmD;AAC7D,IAAA,IAAI;AACF,MAAA,OAAO,QAAA,CAAS,GAAG,IAAI,CAAA;AAAA,IACzB,SAAS,KAAA,EAAO;AACd,MAAA,IAAI,OAAA,IAAW,iBAAiB,KAAA,EAAO;AACrC,QAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,MACf;AACA,MAAA,OAAO,MAAA;AAAA,IACT;AAAA,EACF,CAAA;AACF;AASO,SAAS,UAAU,KAAA,EAAuB;AAC/C,EAAA,MAAM,YAAA,GAAe,CAAA,GAAI,IAAA,CAAK,MAAA,EAAO,GAAI,GAAA;AACzC,EAAA,OAAO,IAAA,CAAK,KAAA,CAAM,KAAA,GAAQ,YAAY,CAAA;AACxC;AAUO,SAAS,KAAA,CAAM,KAAA,EAAe,GAAA,EAAa,GAAA,EAAqB;AACrE,EAAA,OAAO,KAAK,GAAA,CAAI,IAAA,CAAK,IAAI,KAAA,EAAO,GAAG,GAAG,GAAG,CAAA;AAC3C;AAQO,SAAS,GAAA,GAAc;AAC5B,EAAA,IAAI,OAAO,WAAA,KAAgB,WAAA,IAAe,OAAO,WAAA,CAAY,QAAQ,UAAA,EAAY;AAC/E,IAAA,OAAO,YAAY,GAAA,EAAI;AAAA,EACzB;AACA,EAAA,OAAO,KAAK,GAAA,EAAI;AAClB;AC/QA,IAAM,cAAA,GAAiB,EAAE,QAAA,EAAS;AAM3B,IAAM,YAAA,GAAe,EAAE,MAAA,CAAO;AAAA,EACnC,KAAA,EAAO,cAAA;AAAA,EACP,IAAA,EAAM,cAAA;AAAA,EACN,IAAA,EAAM,cAAA;AAAA,EACN,KAAA,EAAO;AACT,CAAC,EAAE,QAAA;AAKI,IAAM,mBAAA,GAAsB,EAAE,MAAA,CAAO;AAAA;AAAA,EAE1C,cAAA,EAAgB,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,GAAK,CAAA;AAAA;AAAA,EAEzD,SAAA,EAAW,eAAe,QAAA,EAAS;AAAA;AAAA,EAEnC,MAAA,EAAQ;AACV,CAAC;AAOM,IAAM,sBAAsB,CAAA,CAAE,IAAA,CAAK,CAAC,aAAA,EAAe,QAAA,EAAU,UAAU,CAAC;AAOxE,IAAM,iBAAA,GAAoB,EAAE,MAAA,CAAO;AAAA;AAAA,EAExC,WAAA,EAAa,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,CAAC,CAAA;AAAA;AAAA,EAElD,YAAA,EAAc,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,GAAG,CAAA;AAAA;AAAA,EAErD,QAAA,EAAU,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,GAAW,QAAA,EAAS;AAAA;AAAA,EAE/C,aAAA,EAAe,mBAAA,CAAoB,OAAA,CAAQ,aAAa,CAAA;AAAA;AAAA,EAExD,YAAY,CAAA,CAAE,MAAA,GAAS,QAAA,EAAS,CAAE,QAAQ,CAAG,CAAA;AAAA;AAAA,EAE7C,MAAA,EAAQ,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,KAAK,CAAA;AAAA;AAAA,EAEjC,WAAA,EAAa,eAAe,QAAA,EAAS;AAAA;AAAA,EAErC,OAAA,EAAS,eAAe,QAAA,EAAS;AAAA;AAAA,EAEjC,MAAA,EAAQ;AACV,CAAC;AAOM,IAAM,4BAA4B,CAAA,CAAE,IAAA,CAAK,CAAC,QAAA,EAAU,MAAA,EAAQ,WAAW,CAAC;AAOxE,IAAM,0BAAA,GAA6B,EAAE,MAAA,CAAO;AAAA,EACjD,UAAU,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA,EAAY;AAAA,EACvC,gBAAgB,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA,EAAY;AAAA,EAC7C,eAAe,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA,EAAY;AAAA,EAC5C,sBAAsB,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA,EAAY;AAAA,EACnD,qBAAqB,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,WAAA;AACxC,CAAC;AAOM,IAAM,0BAAA,GAA6B,EAAE,MAAA,CAAO;AAAA;AAAA,EAEjD,WAAA,EAAa,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,CAAC,CAAA;AAAA;AAAA,EAElD,OAAA,EAAS,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,GAAK,CAAA;AAAA;AAAA,EAElD,mBAAA,EAAqB,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,CAAC,CAAA;AAAA;AAAA,EAE1D,QAAA,EAAU,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,WAAA,EAAY,CAAE,OAAA,CAAQ,CAAC,CAAA;AAAA;AAAA,EAElD,WAAA,EAAa,eAAe,QAAA,EAAS;AAAA;AAAA,EAErC,YAAA,EAAc,eAAe,QAAA,EAAS;AAAA;AAAA,EAEtC,aAAA,EAAe,eAAe,QAAA,EAAS;AAAA;AAAA,EAEvC,MAAA,EAAQ;AACV,CAAC;AAOM,IAAM,qBAAA,GAAwB,EAAE,MAAA,CAAO;AAAA;AAAA,EAE5C,IAAA,EAAM,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,GAAG,CAAA;AAAA;AAAA,EAE7C,KAAA,EAAO,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,GAAW,QAAA,EAAS;AAAA;AAAA,EAE5C,QAAA,EAAU,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,GAAI,CAAA;AAAA;AAAA,EAElD,OAAA,EAAS,eAAe,QAAA,EAAS;AAAA;AAAA,EAEjC,MAAA,EAAQ;AACV,CAAC;AAOM,IAAM,oBAAA,GAAuB,EAAE,MAAA,CAAO;AAAA;AAAA,EAE3C,aAAA,EAAe,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,EAAS,CAAE,OAAA,CAAQ,EAAE,CAAA;AAAA;AAAA,EAErD,QAAA,EAAU,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,WAAA,EAAY,CAAE,OAAA,CAAQ,CAAC,CAAA;AAAA;AAAA,EAElD,YAAA,EAAc,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,QAAA,GAAW,QAAA,EAAS;AAAA;AAAA,EAEnD,UAAA,EAAY,eAAe,QAAA,EAAS;AAAA;AAAA,EAEpC,MAAA,EAAQ;AACV,CAAC;AAOM,IAAM,oBAAA,GAAuB,EAAE,MAAA,CAAO;AAAA;AAAA,EAE3C,QAAA,EAAU,cAAA;AAAA;AAAA,EAEV,cAAA,EAAgB,eAAe,QAAA,EAAS;AAAA;AAAA,EAExC,UAAA,EAAY,eAAe,QAAA,EAAS;AAAA;AAAA,EAEpC,SAAA,EAAW,eAAe,QAAA,EAAS;AAAA;AAAA,EAEnC,MAAA,EAAQ;AACV,CAAC;AC7JD,IAAM,UAAA,GAAa,GAAA;AAGnB,IAAM,aAAA,GAAgB,UAAA;AAOf,IAAM,iBAAA,GAAoBA,EAAE,MAAA,CAAO;AAAA,EACxC,QAAQA,CAAAA,CAAE,MAAA,GAAS,WAAA,EAAY,CAAE,IAAI,UAAU,CAAA;AAAA,EAC/C,UAAA,EAAYA,EAAE,MAAA,EAAO,CAAE,KAAI,CAAE,WAAA,EAAY,CAAE,GAAA,CAAI,aAAa;AAC9D,CAAC;AA6BM,SAAS,oBAAoB,IAAA,EAAmC;AACrE,EAAA,MAAM,MAAA,GAAS,iBAAA,CAAkB,SAAA,CAAU,IAAI,CAAA;AAC/C,EAAA,OAAO,MAAA,CAAO,OAAA,GAAU,MAAA,CAAO,IAAA,GAAO,IAAA;AACxC;AAaA,IAAM,cAAA,GAAiB,GAAA;AAIvB,IAAM,mBAAA,GAAsB,kBAAA;AAG5B,IAAM,qBAAA,GAAwB,QAAA;AAiBvB,SAAS,mBAAmB,GAAA,EAAqB;AAEtD,EAAA,MAAM,SAAA,GAAY,IAAI,MAAA,GAAS,cAAA,GAAiB,IAAI,KAAA,CAAM,CAAA,EAAG,cAAc,CAAA,GAAI,GAAA;AAI/E,EAAA,OAAO,UACJ,OAAA,CAAQ,mBAAA,EAAqB,EAAE,CAAA,CAC/B,OAAA,CAAQ,uBAAuB,GAAG,CAAA;AACvC;AA0KO,IAAM,cAAA,GAAN,MAAM,cAAA,CAA0C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBrD,YAAY,OAAA,EAA4C;AAVxD,IAAA,aAAA,CAAA,IAAA,EAAiB,SAAA,sBAAc,GAAA,EAAyB,CAAA;AACxD,IAAA,aAAA,CAAA,IAAA,EAAiB,YAAA,CAAA;AACjB,IAAA,aAAA,CAAA,IAAA,EAAQ,eAAA,EAAgB,CAAA,CAAA;AAStB,IAAA,IAAA,CAAK,UAAA,GAAa,SAAS,UAAA,IAAc,GAAA;AAAA,EAC3C;AAAA,EAEA,IAAI,GAAA,EAA0C;AAC5C,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,OAAA,CAAQ,GAAG,CAAA;AAC9B,IAAA,OAAO,UAAU,IAAA,GAAO,cAAA,CAAc,aAAA,GAAgB,OAAA,CAAQ,QAAQ,KAAK,CAAA;AAAA,EAC7E;AAAA,EAEA,GAAA,CAAI,KAAa,KAAA,EAAmC;AAClD,IAAA,IAAA,CAAK,OAAA,CAAQ,KAAK,KAAK,CAAA;AACvB,IAAA,OAAO,cAAA,CAAc,aAAA;AAAA,EACvB;AAAA,EAEA,aAAA,CACE,GAAA,EACA,QAAA,EACA,QAAA,EAC8B;AAC9B,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA,IAAK,IAAA;AAGzC,IAAA,MAAM,OAAA,GACJ,OAAA,KAAY,QAAA,IACX,OAAA,KAAY,IAAA,IACX,QAAA,KAAa,IAAA,IACb,OAAA,CAAQ,MAAA,KAAW,QAAA,CAAS,MAAA,IAC5B,OAAA,CAAQ,eAAe,QAAA,CAAS,UAAA;AAEpC,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,IAAA,CAAK,OAAA,CAAQ,KAAK,QAAQ,CAAA;AAC1B,MAAA,OAAO,QAAQ,OAAA,CAAQ,EAAE,SAAS,IAAA,EAAM,YAAA,EAAc,UAAU,CAAA;AAAA,IAClE;AAEA,IAAA,OAAO,QAAQ,OAAA,CAAQ,EAAE,SAAS,KAAA,EAAO,YAAA,EAAc,SAAS,CAAA;AAAA,EAClE;AAAA,EAEA,OAAO,GAAA,EAA4B;AACjC,IAAA,IAAA,CAAK,WAAW,GAAG,CAAA;AACnB,IAAA,OAAO,cAAA,CAAc,aAAA;AAAA,EACvB;AAAA,EAEA,KAAA,GAAuB;AACrB,IAAA,IAAA,CAAK,SAAA,EAAU;AACf,IAAA,OAAO,cAAA,CAAc,aAAA;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKA,IAAA,GAAe;AACb,IAAA,OAAO,KAAK,OAAA,CAAQ,IAAA;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAA,GAA2B;AACzB,IAAA,OAAO,IAAA,CAAK,aAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAAQ,GAAA,EAAiC;AACvC,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA;AAClC,IAAA,IAAI,KAAA,EAAO;AAET,MAAA,IAAA,CAAK,OAAA,CAAQ,OAAO,GAAG,CAAA;AACvB,MAAA,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AAAA,IAC7B;AACA,IAAA,OAAO,KAAA,IAAS,IAAA;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAA,CAAQ,KAAa,KAAA,EAA0B;AAC7C,IAAA,IAAI,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA,EAAG;AACzB,MAAA,IAAA,CAAK,OAAA,CAAQ,OAAO,GAAG,CAAA;AAAA,IACzB,CAAA,MAAA,IAAW,KAAK,UAAA,GAAa,CAAA,IAAK,KAAK,OAAA,CAAQ,IAAA,IAAQ,KAAK,UAAA,EAAY;AACtE,MAAA,MAAM,WAAW,IAAA,CAAK,OAAA,CAAQ,IAAA,EAAK,CAAE,MAAK,CAAE,KAAA;AAC5C,MAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,QAAA,IAAA,CAAK,OAAA,CAAQ,OAAO,QAAQ,CAAA;AAC5B,QAAA,IAAA,CAAK,aAAA,EAAA;AAAA,MACP;AAAA,IACF;AACA,IAAA,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,WAAW,GAAA,EAAmB;AAC5B,IAAA,IAAA,CAAK,OAAA,CAAQ,OAAO,GAAG,CAAA;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAA,GAAkB;AAChB,IAAA,IAAA,CAAK,QAAQ,KAAA,EAAM;AAAA,EACrB;AACF,CAAA;AAAA;AA1HE,aAAA,CAFW,cAAA,EAEa,eAAA,EAAgB,OAAA,CAAQ,OAAA,EAAQ,CAAA;AAAA;AAGxD,aAAA,CALW,cAAA,EAKa,eAAA,EAA6C,OAAA,CAAQ,OAAA,CAAQ,IAAI,CAAA,CAAA;AALpF,IAAM,aAAA,GAAN","file":"index.js","sourcesContent":["/**\n * Base error class for all Fortify errors.\n */\nexport class FortifyError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'FortifyError';\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n // Use typeof check to avoid unnecessary-condition lint error\n if (typeof Error.captureStackTrace === 'function') {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Error thrown when circuit breaker is in open state and rejecting requests.\n */\nexport class CircuitOpenError extends FortifyError {\n constructor(message = 'Circuit breaker is open') {\n super(message);\n this.name = 'CircuitOpenError';\n }\n}\n\n/**\n * Error thrown when rate limit is exceeded.\n */\nexport class RateLimitExceededError extends FortifyError {\n public readonly key: string | undefined;\n\n constructor(message = 'Rate limit exceeded', key?: string) {\n super(message);\n this.name = 'RateLimitExceededError';\n this.key = key;\n }\n}\n\n/**\n * Error thrown when bulkhead is at capacity.\n */\nexport class BulkheadFullError extends FortifyError {\n public readonly activeCount: number;\n public readonly queuedCount: number;\n\n constructor(\n message = 'Bulkhead is full',\n activeCount = 0,\n queuedCount = 0\n ) {\n super(message);\n this.name = 'BulkheadFullError';\n this.activeCount = activeCount;\n this.queuedCount = queuedCount;\n }\n}\n\n/**\n * Error thrown when operation times out.\n */\nexport class TimeoutError extends FortifyError {\n public readonly timeoutMs: number;\n\n constructor(message = 'Operation timed out', timeoutMs = 0) {\n super(message);\n this.name = 'TimeoutError';\n this.timeoutMs = timeoutMs;\n }\n}\n\n/**\n * Error thrown when maximum retry attempts are reached.\n */\nexport class MaxAttemptsReachedError extends FortifyError {\n public readonly attempts: number;\n public readonly lastError: Error | undefined;\n\n constructor(\n message = 'Maximum retry attempts reached',\n attempts = 0,\n lastError?: Error\n ) {\n super(message);\n this.name = 'MaxAttemptsReachedError';\n this.attempts = attempts;\n this.lastError = lastError;\n }\n}\n\n/**\n * Error thrown when bulkhead is closed and no new operations accepted.\n */\nexport class BulkheadClosedError extends FortifyError {\n constructor(message = 'Bulkhead is closed') {\n super(message);\n this.name = 'BulkheadClosedError';\n }\n}\n\n/**\n * Interface for errors that can indicate whether they are retryable.\n */\nexport interface RetryableError {\n retryable: boolean;\n}\n\n/**\n * Type guard to check if an error implements RetryableError interface.\n */\nexport function isRetryableError(error: unknown): error is Error & RetryableError {\n return (\n error instanceof Error &&\n 'retryable' in error &&\n typeof (error as RetryableError).retryable === 'boolean'\n );\n}\n\n/**\n * Wrapper class to mark an error as retryable.\n */\nexport class RetryableErrorWrapper extends Error implements RetryableError {\n public readonly retryable: boolean;\n public override readonly cause: Error;\n\n constructor(error: Error, retryable: boolean) {\n super(error.message, { cause: error });\n this.name = 'RetryableErrorWrapper';\n this.retryable = retryable;\n this.cause = error;\n if (error.stack) {\n this.stack = error.stack;\n }\n }\n}\n\n/**\n * Wrap an error as retryable.\n */\nexport function asRetryable(error: Error, retryable = true): RetryableErrorWrapper {\n return new RetryableErrorWrapper(error, retryable);\n}\n\n/**\n * Wrap an error as non-retryable.\n */\nexport function asNonRetryable(error: Error): RetryableErrorWrapper {\n return new RetryableErrorWrapper(error, false);\n}\n\n/**\n * Check if an error is retryable.\n * Returns true if the error implements RetryableError and retryable is true.\n * Returns undefined if the error doesn't implement RetryableError (let caller decide).\n */\nexport function isRetryable(error: unknown): boolean | undefined {\n if (isRetryableError(error)) {\n return error.retryable;\n }\n return undefined;\n}\n","/**\n * Represents an async operation that can be executed with cancellation support.\n * The signal parameter allows the operation to be cancelled via AbortController.\n *\n * @template T - The return type of the operation\n */\nexport type Operation<T> = (signal: AbortSignal) => Promise<T>;\n\n/**\n * Callback for state changes in stateful patterns (e.g., circuit breaker).\n *\n * @template S - The state type\n */\nexport type StateChangeCallback<S> = (from: S, to: S) => void;\n\n/**\n * Callback for error events.\n */\nexport type ErrorCallback = (error: Error) => void;\n\n/**\n * Simple void callback.\n */\nexport type VoidCallback = () => void;\n\n/**\n * Callback for retry events.\n */\nexport type RetryCallback = (attempt: number, error: Error) => void;\n\n/**\n * Callback for rate limit events.\n */\nexport type RateLimitCallback = (key: string) => void;\n\n/**\n * Logger interface for structured logging across all patterns.\n * Compatible with pino, winston, console, and custom implementations.\n */\nexport interface FortifyLogger {\n debug(msg: string, context?: Record<string, unknown>): void;\n info(msg: string, context?: Record<string, unknown>): void;\n warn(msg: string, context?: Record<string, unknown>): void;\n error(msg: string, context?: Record<string, unknown>): void;\n}\n\n/**\n * No-operation logger that discards all log messages.\n * Useful for testing or when logging is not needed.\n */\nexport const noopLogger: FortifyLogger = {\n debug: () => undefined,\n info: () => undefined,\n warn: () => undefined,\n error: () => undefined,\n};\n\n/**\n * Console-based logger implementation.\n * Browser-friendly and works in all environments.\n */\nexport const consoleLogger: FortifyLogger = {\n debug: (msg, context) => {\n if (context) {\n console.debug(`[fortify] ${msg}`, context);\n } else {\n console.debug(`[fortify] ${msg}`);\n }\n },\n info: (msg, context) => {\n if (context) {\n console.info(`[fortify] ${msg}`, context);\n } else {\n console.info(`[fortify] ${msg}`);\n }\n },\n warn: (msg, context) => {\n if (context) {\n console.warn(`[fortify] ${msg}`, context);\n } else {\n console.warn(`[fortify] ${msg}`);\n }\n },\n error: (msg, context) => {\n if (context) {\n console.error(`[fortify] ${msg}`, context);\n } else {\n console.error(`[fortify] ${msg}`);\n }\n },\n};\n\n/**\n * Generic pattern interface that all resilience patterns implement.\n *\n * @template T - The return type of the operation\n */\nexport interface Pattern<T> {\n /**\n * Execute an operation with the pattern's resilience logic.\n *\n * @param operation - The async operation to execute\n * @param signal - Optional AbortSignal for cancellation\n * @returns Promise resolving to the operation result\n */\n execute(operation: Operation<T>, signal?: AbortSignal): Promise<T>;\n}\n\n/**\n * Interface for patterns that support closing/cleanup.\n */\nexport interface Closeable {\n /**\n * Close the pattern and release resources.\n * May wait for in-flight operations to complete.\n */\n close(): Promise<void>;\n}\n\n/**\n * Interface for patterns that support resetting state.\n */\nexport interface Resettable {\n /**\n * Reset the pattern to its initial state.\n */\n reset(): void;\n}\n","import { TimeoutError } from './errors.js';\n\n/**\n * Sleep for a specified duration with optional cancellation support.\n *\n * @param ms - Duration in milliseconds\n * @param signal - Optional AbortSignal for cancellation\n * @returns Promise that resolves after the delay or rejects if cancelled\n */\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(ensureError(signal.reason));\n return;\n }\n\n const timeoutId = setTimeout(resolve, ms);\n\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timeoutId);\n reject(ensureError(signal.reason));\n },\n { once: true }\n );\n });\n}\n\n/**\n * Ensure a value is an Error instance.\n * @param reason - The reason to convert to an Error\n * @returns An Error instance\n */\nfunction ensureError(reason: unknown): Error {\n if (reason instanceof Error) {\n return reason;\n }\n if (typeof reason === 'string') {\n return new Error(reason);\n }\n if (reason !== undefined && reason !== null) {\n // Handle objects and other types safely\n try {\n return new Error(JSON.stringify(reason));\n } catch {\n return new Error('Unknown error');\n }\n }\n return new DOMException('Aborted', 'AbortError');\n}\n\n/**\n * Wrap a promise with a timeout.\n *\n * @template T - The promise return type\n * @param promise - The promise to wrap\n * @param ms - Timeout duration in milliseconds\n * @param signal - Optional external AbortSignal for cancellation\n * @returns Promise that resolves with the result or rejects with TimeoutError\n */\nexport async function withTimeout<T>(\n promise: Promise<T>,\n ms: number,\n signal?: AbortSignal\n): Promise<T> {\n // Check if already aborted\n if (signal?.aborted) {\n throw ensureError(signal.reason);\n }\n\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(new TimeoutError(`Operation timed out after ${String(ms)}ms`, ms));\n }, ms);\n });\n\n // Handle external signal\n const abortPromise = signal\n ? new Promise<never>((_, reject) => {\n signal.addEventListener(\n 'abort',\n () => reject(ensureError(signal.reason)),\n { once: true }\n );\n })\n : null;\n\n try {\n const racers: Promise<T>[] = [promise, timeoutPromise];\n if (abortPromise) {\n racers.push(abortPromise);\n }\n return await Promise.race(racers);\n } finally {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n }\n}\n\n/**\n * Execute an operation with a timeout, passing the combined signal to the operation.\n *\n * @template T - The operation return type\n * @param operation - The operation to execute\n * @param ms - Timeout duration in milliseconds\n * @param signal - Optional external AbortSignal for cancellation\n * @returns Promise that resolves with the result or rejects with TimeoutError\n */\nexport async function executeWithTimeout<T>(\n operation: (signal: AbortSignal) => Promise<T>,\n ms: number,\n signal?: AbortSignal\n): Promise<T> {\n const controller = new AbortController();\n\n // Combine signals\n const combinedSignal = combineSignals(signal, controller.signal);\n\n const timeoutId = setTimeout(() => {\n controller.abort(new TimeoutError(`Operation timed out after ${String(ms)}ms`, ms));\n }, ms);\n\n try {\n return await operation(combinedSignal);\n } finally {\n clearTimeout(timeoutId);\n }\n}\n\n/**\n * Combine multiple AbortSignals into one.\n * Returns a signal that aborts when any of the input signals abort.\n *\n * Uses AbortSignal.any() when available (Node 20+, modern browsers).\n * Falls back to a manual implementation that properly cleans up event listeners.\n *\n * @param signals - AbortSignals to combine (undefined values are filtered out)\n * @returns Combined AbortSignal\n */\nexport function combineSignals(...signals: (AbortSignal | undefined)[]): AbortSignal {\n const validSignals = signals.filter((s): s is AbortSignal => s !== undefined);\n\n if (validSignals.length === 0) {\n return new AbortController().signal;\n }\n\n const firstSignal = validSignals[0];\n if (validSignals.length === 1 && firstSignal) {\n return firstSignal;\n }\n\n // Use AbortSignal.any if available (Node 20+, modern browsers)\n if ('any' in AbortSignal) {\n return AbortSignal.any(validSignals);\n }\n\n // Fallback: create a new controller and link it to all signals\n // Track listeners so we can clean them up to prevent memory leaks\n const controller = new AbortController();\n const listeners: { signal: AbortSignal; listener: () => void }[] = [];\n\n // Cleanup function to remove all listeners\n const cleanup = () => {\n for (const { signal, listener } of listeners) {\n signal.removeEventListener('abort', listener);\n }\n listeners.length = 0;\n };\n\n for (const signal of validSignals) {\n if (signal.aborted) {\n controller.abort(signal.reason);\n return controller.signal;\n }\n\n const listener = () => {\n cleanup(); // Clean up all listeners when any signal aborts\n controller.abort(signal.reason);\n };\n\n listeners.push({ signal, listener });\n signal.addEventListener('abort', listener, { once: true });\n }\n\n return controller.signal;\n}\n\n/**\n * Check if an error is an abort error (from AbortController).\n *\n * @param error - Error to check\n * @returns True if the error is an AbortError\n */\nexport function isAbortError(error: unknown): boolean {\n return (\n error instanceof DOMException &&\n error.name === 'AbortError'\n );\n}\n\n/**\n * Throw if the given signal is aborted.\n * Useful for checking signal state early in operations to avoid unnecessary work.\n *\n * @param signal - AbortSignal to check\n * @throws {DOMException} If signal is aborted (AbortError)\n */\nexport function throwIfAborted(signal: AbortSignal | undefined): void {\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n}\n\n/**\n * Safely execute a callback, catching and logging any errors.\n * Used for user-provided callbacks to prevent them from breaking the pattern.\n *\n * @param callback - Callback to execute\n * @param onError - Optional error handler\n */\nexport function safeCallback<T extends (...args: unknown[]) => unknown>(\n callback: T | undefined,\n onError?: (error: Error) => void\n): T | undefined {\n if (!callback) return undefined;\n\n return ((...args: Parameters<T>): ReturnType<T> | undefined => {\n try {\n return callback(...args) as ReturnType<T>;\n } catch (error) {\n if (onError && error instanceof Error) {\n onError(error);\n }\n return undefined;\n }\n }) as T;\n}\n\n/**\n * Calculate jittered delay.\n * Adds 0-10% random variance to prevent thundering herd.\n *\n * @param delay - Base delay in milliseconds\n * @returns Jittered delay\n */\nexport function addJitter(delay: number): number {\n const jitterFactor = 1 + Math.random() * 0.1; // 0-10% jitter\n return Math.floor(delay * jitterFactor);\n}\n\n/**\n * Clamp a number between min and max values.\n *\n * @param value - Value to clamp\n * @param min - Minimum value\n * @param max - Maximum value\n * @returns Clamped value\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\n/**\n * Get current timestamp in milliseconds.\n * Uses performance.now() if available for higher precision.\n *\n * @returns Current timestamp in milliseconds\n */\nexport function now(): number {\n if (typeof performance !== 'undefined' && typeof performance.now === 'function') {\n return performance.now();\n }\n return Date.now();\n}\n","import { z } from 'zod';\n\n/**\n * Schema for a function type (Zod 4 compatible).\n * Uses z.function() which validates that the value is a function.\n */\nconst functionSchema = z.function();\n\n/**\n * Base schema for logger configuration.\n * Validates that the logger has the required methods.\n */\nexport const loggerSchema = z.object({\n debug: functionSchema,\n info: functionSchema,\n warn: functionSchema,\n error: functionSchema,\n}).optional();\n\n/**\n * 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: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type TimeoutConfig = z.infer<typeof timeoutConfigSchema>;\n\n/**\n * Schema for retry backoff policy.\n */\nexport const backoffPolicySchema = z.enum(['exponential', 'linear', 'constant']);\n\nexport type BackoffPolicy = z.infer<typeof backoffPolicySchema>;\n\n/**\n * Schema for retry configuration.\n */\nexport const retryConfigSchema = z.object({\n /** Maximum number of attempts including the first (default: 3) */\n maxAttempts: z.number().int().positive().default(3),\n /** Initial delay before first retry in milliseconds (default: 100) */\n initialDelay: z.number().int().positive().default(100),\n /** Maximum delay between retries in milliseconds */\n maxDelay: z.number().int().positive().optional(),\n /** Backoff strategy (default: 'exponential') */\n backoffPolicy: backoffPolicySchema.default('exponential'),\n /** Multiplier for exponential backoff (default: 2.0) */\n multiplier: z.number().positive().default(2.0),\n /** Add random jitter to delays (default: false) */\n jitter: z.boolean().default(false),\n /** Custom function to determine if error is retryable */\n isRetryable: functionSchema.optional(),\n /** Callback on each retry attempt */\n onRetry: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type RetryConfig = z.infer<typeof retryConfigSchema>;\n\n/**\n * Schema for circuit breaker state.\n */\nexport const circuitBreakerStateSchema = z.enum(['closed', 'open', 'half-open']);\n\nexport type CircuitBreakerState = z.infer<typeof circuitBreakerStateSchema>;\n\n/**\n * Schema for circuit breaker counts/metrics.\n */\nexport const circuitBreakerCountsSchema = z.object({\n requests: z.number().int().nonnegative(),\n totalSuccesses: z.number().int().nonnegative(),\n totalFailures: z.number().int().nonnegative(),\n consecutiveSuccesses: z.number().int().nonnegative(),\n consecutiveFailures: z.number().int().nonnegative(),\n});\n\nexport type CircuitBreakerCounts = z.infer<typeof circuitBreakerCountsSchema>;\n\n/**\n * Schema for circuit breaker configuration.\n */\nexport const circuitBreakerConfigSchema = z.object({\n /** Maximum consecutive failures before opening (default: 5) */\n maxFailures: z.number().int().positive().default(5),\n /** Duration in open state before transitioning to half-open in milliseconds (default: 60000) */\n timeout: z.number().int().positive().default(60000),\n /** Maximum requests allowed in half-open state (default: 1) */\n halfOpenMaxRequests: z.number().int().positive().default(1),\n /** Period to clear counts when closed, 0 means never (default: 0) */\n interval: z.number().int().nonnegative().default(0),\n /** Custom function to determine when to trip the breaker */\n readyToTrip: functionSchema.optional(),\n /** Custom function to determine if result is successful */\n isSuccessful: functionSchema.optional(),\n /** Callback on state change */\n onStateChange: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type CircuitBreakerConfig = z.infer<typeof circuitBreakerConfigSchema>;\n\n/**\n * Schema for rate limiter configuration.\n */\nexport const rateLimitConfigSchema = z.object({\n /** Number of tokens added per interval (default: 100) */\n rate: z.number().int().positive().default(100),\n /** Maximum bucket capacity (defaults to rate) */\n burst: z.number().int().positive().optional(),\n /** Interval for token refill in milliseconds (default: 1000) */\n interval: z.number().int().positive().default(1000),\n /** Callback when rate limit is hit */\n onLimit: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type RateLimitConfig = z.infer<typeof rateLimitConfigSchema>;\n\n/**\n * Schema for bulkhead configuration.\n */\nexport const bulkheadConfigSchema = z.object({\n /** Maximum concurrent executions (default: 10) */\n maxConcurrent: z.number().int().positive().default(10),\n /** Maximum queued requests, 0 means no queueing (default: 0) */\n maxQueue: z.number().int().nonnegative().default(0),\n /** Maximum time to wait in queue in milliseconds */\n queueTimeout: z.number().int().positive().optional(),\n /** Callback when request is rejected */\n onRejected: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type BulkheadConfig = z.infer<typeof bulkheadConfigSchema>;\n\n/**\n * Schema for fallback configuration.\n */\nexport const fallbackConfigSchema = z.object({\n /** Fallback function to execute when primary fails */\n fallback: functionSchema,\n /** Custom function to determine if fallback should be used */\n shouldFallback: functionSchema.optional(),\n /** Callback when fallback is triggered */\n onFallback: functionSchema.optional(),\n /** Callback when primary succeeds */\n onSuccess: functionSchema.optional(),\n /** Logger instance */\n logger: loggerSchema,\n});\n\nexport type FallbackConfig = z.infer<typeof fallbackConfigSchema>;\n","import { z } from 'zod';\n\n/** Maximum reasonable token count (1 billion) */\nconst MAX_TOKENS = 1_000_000_000;\n\n/** Maximum reasonable timestamp (year 2100 in ms) */\nconst MAX_TIMESTAMP = 4_102_444_800_000;\n\n/**\n * Zod schema for validating bucket state from external storage.\n * Use this to validate data retrieved from untrusted storage sources.\n * Includes bounds checking to prevent malicious or corrupted data.\n */\nexport const bucketStateSchema = z.object({\n tokens: z.number().nonnegative().max(MAX_TOKENS),\n lastRefill: z.number().int().nonnegative().max(MAX_TIMESTAMP),\n});\n\n/**\n * State of a rate limit bucket for external storage.\n * Contains all data needed to reconstruct bucket state across invocations.\n */\nexport interface BucketState {\n /** Current number of tokens in the bucket */\n readonly tokens: number;\n /** Timestamp (ms since epoch) of last token refill */\n readonly lastRefill: number;\n}\n\n/**\n * Validates bucket state from external storage.\n * Returns the validated state or null if invalid.\n *\n * @param data - Raw data from storage\n * @returns Validated BucketState or null if invalid\n *\n * @example\n * ```typescript\n * const data = await redis.get(key);\n * const state = validateBucketState(JSON.parse(data));\n * if (state === null) {\n * // Invalid data, treat as new bucket\n * }\n * ```\n */\nexport function validateBucketState(data: unknown): BucketState | null {\n const result = bucketStateSchema.safeParse(data);\n return result.success ? result.data : null;\n}\n\n/**\n * Result of a compare-and-set operation.\n */\nexport interface CompareAndSetResult {\n /** Whether the update was successful */\n readonly success: boolean;\n /** The current state (after operation) */\n readonly currentState: BucketState | null;\n}\n\n/** Maximum key length after sanitization */\nconst MAX_KEY_LENGTH = 256;\n\n/** Pre-compiled regex for control characters (for performance) */\n// eslint-disable-next-line no-control-regex\nconst CONTROL_CHARS_REGEX = /[\\x00-\\x1f\\x7f]/g;\n\n/** Pre-compiled regex for path separators (for performance) */\nconst PATH_SEPARATORS_REGEX = /[/\\\\]/g;\n\n/**\n * Sanitizes a storage key to prevent injection attacks.\n * Removes or encodes potentially dangerous characters.\n *\n * Optimization: Truncates before regex processing to limit work on long strings.\n *\n * @param key - Raw key from user input\n * @returns Sanitized key safe for storage operations\n *\n * @example\n * ```typescript\n * const safeKey = sanitizeStorageKey(userProvidedKey);\n * await storage.get(safeKey);\n * ```\n */\nexport function sanitizeStorageKey(key: string): string {\n // Truncate first to limit regex work on long strings\n const truncated = key.length > MAX_KEY_LENGTH ? key.slice(0, MAX_KEY_LENGTH) : key;\n\n // Replace control characters, null bytes, and path separators\n // Keep alphanumeric, dash, underscore, dot, colon, at\n return truncated\n .replace(CONTROL_CHARS_REGEX, '') // Remove control characters\n .replace(PATH_SEPARATORS_REGEX, '_'); // Replace path separators\n}\n\n/**\n * Storage adapter interface for rate limiting.\n *\n * Implement this interface to persist rate limit state across serverless\n * invocations or distributed systems. The storage adapter enables rate\n * limiting in environments where in-memory state doesn't persist.\n *\n * ## Concurrency Warning\n *\n * **Important:** The basic get/set interface has inherent TOCTOU (time-of-check\n * to time-of-use) race conditions in distributed systems. Between reading the\n * bucket state and writing the updated state, another process may have modified\n * the bucket. This can result in rate limits being slightly over or under the\n * configured limit.\n *\n * For precise rate limiting in high-concurrency distributed environments,\n * implement the optional `compareAndSet` method with atomic operations:\n *\n * - **Redis**: Use Lua scripts with WATCH/MULTI/EXEC\n * - **DynamoDB**: Use conditional writes with version checks\n * - **Forge Storage**: Accepts eventual consistency\n *\n * For most use cases, the slight inaccuracy from race conditions is acceptable.\n * The rate limiter still provides effective protection against abuse.\n *\n * @example Redis implementation with atomic operations\n * ```typescript\n * const redisStorage: RateLimitStorage = {\n * async get(key) {\n * const data = await redis.get(`ratelimit:${key}`);\n * if (!data) return null;\n * return validateBucketState(JSON.parse(data));\n * },\n * async set(key, state, ttlMs) {\n * const value = JSON.stringify(state);\n * if (ttlMs) {\n * await redis.set(`ratelimit:${key}`, value, 'PX', ttlMs);\n * } else {\n * await redis.set(`ratelimit:${key}`, value);\n * }\n * },\n * async compareAndSet(key, expected, newState, ttlMs) {\n * // Use Lua script for atomic compare-and-set\n * const script = `\n * local current = redis.call('GET', KEYS[1])\n * if current == ARGV[1] or (current == false and ARGV[1] == 'null') then\n * if ARGV[3] then\n * redis.call('SET', KEYS[1], ARGV[2], 'PX', ARGV[3])\n * else\n * redis.call('SET', KEYS[1], ARGV[2])\n * end\n * return {1, ARGV[2]}\n * end\n * return {0, current or 'null'}\n * `;\n * const expectedStr = expected ? JSON.stringify(expected) : 'null';\n * const [success, currentStr] = await redis.eval(script, 1,\n * `ratelimit:${key}`, expectedStr, JSON.stringify(newState), ttlMs?.toString()\n * );\n * return {\n * success: success === 1,\n * currentState: currentStr === 'null' ? null : validateBucketState(JSON.parse(currentStr))\n * };\n * },\n * async delete(key) {\n * await redis.del(`ratelimit:${key}`);\n * },\n * async clear() {\n * const keys = await redis.keys('ratelimit:*');\n * if (keys.length > 0) {\n * await redis.del(...keys);\n * }\n * }\n * };\n * ```\n *\n * @example Forge Storage implementation (accepts eventual consistency)\n * ```typescript\n * import { storage } from '@forge/api';\n *\n * const forgeStorage: RateLimitStorage = {\n * async get(key) {\n * const data = await storage.get(`ratelimit:${key}`);\n * return validateBucketState(data);\n * },\n * async set(key, state) {\n * await storage.set(`ratelimit:${key}`, state);\n * },\n * async delete(key) {\n * await storage.delete(`ratelimit:${key}`);\n * }\n * };\n * ```\n */\nexport interface RateLimitStorage {\n /**\n * Retrieve bucket state for a key.\n *\n * @param key - The rate limiting key (already sanitized)\n * @returns The bucket state, or null if not found\n */\n get(key: string): Promise<BucketState | null>;\n\n /**\n * Store bucket state for a key.\n *\n * @param key - The rate limiting key (already sanitized)\n * @param state - The bucket state to store\n * @param ttlMs - Optional TTL in milliseconds for automatic cleanup.\n * Recommended: set to interval * (burst / rate) * 2 to auto-expire\n * stale buckets while keeping active ones alive.\n */\n set(key: string, state: BucketState, ttlMs?: number): Promise<void>;\n\n /**\n * Atomically compare and set bucket state.\n * Optional - implement for precise rate limiting in distributed systems.\n *\n * This method should atomically:\n * 1. Check if the current state matches `expected`\n * 2. If it matches, update to `newState` and return success\n * 3. If it doesn't match, return failure with the current state\n *\n * @param key - The rate limiting key (already sanitized)\n * @param expected - The expected current state (null if expecting no entry)\n * @param newState - The new state to set if expected matches\n * @param ttlMs - Optional TTL in milliseconds\n * @returns Result indicating success and current state\n */\n compareAndSet?(\n key: string,\n expected: BucketState | null,\n newState: BucketState,\n ttlMs?: number\n ): Promise<CompareAndSetResult>;\n\n /**\n * Delete bucket state for a key.\n * Optional - if not implemented, reset operations on individual keys\n * will not be supported.\n *\n * @param key - The rate limiting key (already sanitized)\n */\n delete?(key: string): Promise<void>;\n\n /**\n * Clear all bucket states.\n * Optional - if not implemented, full reset operations will not clear storage.\n */\n clear?(): Promise<void>;\n}\n\n/**\n * In-memory storage implementation using Map.\n * This is the default storage used when no external storage is provided.\n *\n * Features:\n * - LRU eviction when maxEntries is exceeded\n * - Synchronous operations (async interface for compatibility)\n * - No persistence across process restarts\n * - No race conditions (single-threaded JavaScript)\n *\n * @example\n * ```typescript\n * const storage = new MemoryStorage({ maxEntries: 10000 });\n * const limiter = new RateLimiter({ rate: 100, storage });\n * ```\n */\nexport class MemoryStorage implements RateLimitStorage {\n /** Cached resolved promise for void returns to avoid allocation */\n private static readonly RESOLVED_VOID = Promise.resolve();\n\n /** Cached resolved promise for null returns to avoid allocation */\n private static readonly RESOLVED_NULL: Promise<BucketState | null> = Promise.resolve(null);\n\n private readonly entries = new Map<string, BucketState>();\n private readonly maxEntries: number;\n private evictionCount = 0;\n\n /**\n * Create a new in-memory storage.\n *\n * @param options - Storage options\n * @param options.maxEntries - Maximum entries before LRU eviction (0 = unlimited, default: 10000)\n */\n constructor(options?: { readonly maxEntries?: number }) {\n this.maxEntries = options?.maxEntries ?? 10000;\n }\n\n get(key: string): Promise<BucketState | null> {\n const state = this.getSync(key);\n return state === null ? MemoryStorage.RESOLVED_NULL : Promise.resolve(state);\n }\n\n set(key: string, state: BucketState): Promise<void> {\n this.setSync(key, state);\n return MemoryStorage.RESOLVED_VOID;\n }\n\n compareAndSet(\n key: string,\n expected: BucketState | null,\n newState: BucketState\n ): Promise<CompareAndSetResult> {\n const current = this.entries.get(key) ?? null;\n\n // Check if current matches expected\n const matches =\n current === expected ||\n (current !== null &&\n expected !== null &&\n current.tokens === expected.tokens &&\n current.lastRefill === expected.lastRefill);\n\n if (matches) {\n this.setSync(key, newState);\n return Promise.resolve({ success: true, currentState: newState });\n }\n\n return Promise.resolve({ success: false, currentState: current });\n }\n\n delete(key: string): Promise<void> {\n this.deleteSync(key);\n return MemoryStorage.RESOLVED_VOID;\n }\n\n clear(): Promise<void> {\n this.clearSync();\n return MemoryStorage.RESOLVED_VOID;\n }\n\n /**\n * Get the current number of entries.\n */\n size(): number {\n return this.entries.size;\n }\n\n /**\n * Get the total number of evictions since creation.\n */\n getEvictionCount(): number {\n return this.evictionCount;\n }\n\n /**\n * Synchronous get for backward compatibility with sync rate limiter methods.\n * @internal\n */\n getSync(key: string): BucketState | null {\n const state = this.entries.get(key);\n if (state) {\n // LRU touch: move to end\n this.entries.delete(key);\n this.entries.set(key, state);\n }\n return state ?? null;\n }\n\n /**\n * Synchronous set for backward compatibility with sync rate limiter methods.\n * @internal\n */\n setSync(key: string, state: BucketState): void {\n if (this.entries.has(key)) {\n this.entries.delete(key);\n } else if (this.maxEntries > 0 && this.entries.size >= this.maxEntries) {\n const firstKey = this.entries.keys().next().value;\n if (firstKey !== undefined) {\n this.entries.delete(firstKey);\n this.evictionCount++;\n }\n }\n this.entries.set(key, state);\n }\n\n /**\n * Synchronous delete for backward compatibility.\n * @internal\n */\n deleteSync(key: string): void {\n this.entries.delete(key);\n }\n\n /**\n * Synchronous clear for backward compatibility.\n * @internal\n */\n clearSync(): void {\n this.entries.clear();\n }\n}\n"]} |
+4
-1
| { | ||
| "name": "@fortify-ts/core", | ||
| "version": "0.1.4", | ||
| "version": "0.2.0", | ||
| "description": "Core types, errors, and utilities for Fortify TS resilience library", | ||
@@ -59,2 +59,5 @@ "type": "module", | ||
| "sideEffects": false, | ||
| "publishConfig": { | ||
| "access": "public" | ||
| }, | ||
| "scripts": { | ||
@@ -61,0 +64,0 @@ "build": "tsup", |
184289
46.39%1679
43.02%