@fortify-ts/http
Advanced tools
+1
-1
@@ -181,3 +181,3 @@ "use strict"; | ||
| } | ||
| function createFallbackMiddleware(fallback, _config) { | ||
| function createFallbackMiddleware(fallback) { | ||
| return (handler) => { | ||
@@ -184,0 +184,0 @@ return async (request) => { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/index.ts","../src/types.ts","../src/middleware.ts"],"sourcesContent":["export {\n type HttpRequest,\n type HttpResponse,\n type HttpHandler,\n type HttpMiddleware,\n type KeyExtractor,\n keyFromIp,\n keyFromHeader,\n combineKeys,\n createErrorResponse,\n HttpErrors,\n} from './types.js';\n\nexport {\n type CircuitBreakerMiddlewareConfig,\n type RetryMiddlewareConfig,\n type RateLimitMiddlewareConfig,\n type TimeoutMiddlewareConfig,\n type BulkheadMiddlewareConfig,\n type FallbackMiddlewareConfig,\n createCircuitBreakerMiddleware,\n createRetryMiddleware,\n createRateLimitMiddleware,\n createRateLimitGuard,\n createTimeoutMiddleware,\n createBulkheadMiddleware,\n createFallbackMiddleware,\n createChainMiddleware,\n composeMiddleware,\n} from './middleware.js';\n","/**\n * Generic HTTP request interface.\n * Framework-agnostic representation of an HTTP request.\n */\nexport interface HttpRequest {\n /** HTTP method (GET, POST, PUT, DELETE, etc.) */\n method: string;\n /** Request URL or path */\n url: string;\n /** Request headers */\n headers: Record<string, string | string[] | undefined>;\n /** Request body (if any) */\n body?: unknown;\n /** Client IP address (for rate limiting) */\n ip?: string;\n /** Additional context data */\n context?: Record<string, unknown>;\n}\n\n/**\n * Generic HTTP response interface.\n * Framework-agnostic representation of an HTTP response.\n */\nexport interface HttpResponse {\n /** HTTP status code */\n status: number;\n /** Response headers */\n headers: Record<string, string | string[]>;\n /** Response body (if any) */\n body?: unknown;\n}\n\n/**\n * HTTP handler function type.\n * Takes a request and returns a promise of a response.\n */\nexport type HttpHandler = (request: HttpRequest) => Promise<HttpResponse>;\n\n/**\n * HTTP middleware function type.\n * Wraps a handler with additional behavior.\n */\nexport type HttpMiddleware = (handler: HttpHandler) => HttpHandler;\n\n/**\n * Key extractor function type.\n * Extracts a rate limiting key from an HTTP request.\n */\nexport type KeyExtractor = (request: HttpRequest) => string;\n\n/**\n * Extract rate limiting key from client IP address.\n *\n * @param request - HTTP request\n * @returns IP address or 'unknown'\n */\nexport const keyFromIp: KeyExtractor = (request: HttpRequest): string => {\n return request.ip ?? 'unknown';\n};\n\n/**\n * Create a key extractor that extracts from a specific header.\n *\n * @param header - Header name to extract key from\n * @param defaultValue - Default value if header is missing\n * @returns Key extractor function\n */\nexport function keyFromHeader(\n header: string,\n defaultValue = 'unknown'\n): KeyExtractor {\n return (request: HttpRequest): string => {\n const value = request.headers[header.toLowerCase()];\n if (Array.isArray(value)) {\n return value[0] ?? defaultValue;\n }\n return value ?? defaultValue;\n };\n}\n\n/**\n * Create a key extractor that combines multiple extractors.\n *\n * @param extractors - Key extractors to combine\n * @param separator - Separator between keys\n * @returns Combined key extractor\n */\nexport function combineKeys(\n extractors: KeyExtractor[],\n separator = ':'\n): KeyExtractor {\n return (request: HttpRequest): string => {\n return extractors.map((e) => e(request)).join(separator);\n };\n}\n\n/**\n * Standard HTTP error response factory.\n */\nexport function createErrorResponse(\n status: number,\n message: string,\n headers: Record<string, string> = {}\n): HttpResponse {\n return {\n status,\n headers: {\n 'Content-Type': 'application/json',\n ...headers,\n },\n body: { error: message },\n };\n}\n\n/**\n * Pre-built error responses for common scenarios.\n */\nexport const HttpErrors = {\n /** 429 Too Many Requests */\n tooManyRequests: (retryAfter?: number): HttpResponse =>\n createErrorResponse(\n 429,\n 'Too Many Requests',\n retryAfter !== undefined ? { 'Retry-After': String(retryAfter) } : {}\n ),\n\n /** 503 Service Unavailable (circuit open) */\n serviceUnavailable: (): HttpResponse =>\n createErrorResponse(503, 'Service Unavailable'),\n\n /** 504 Gateway Timeout */\n gatewayTimeout: (): HttpResponse =>\n createErrorResponse(504, 'Gateway Timeout'),\n\n /** 503 Service Unavailable (bulkhead full) */\n capacityExceeded: (): HttpResponse =>\n createErrorResponse(503, 'Service at capacity, please retry later'),\n} as const;\n","import {\n CircuitOpenError,\n RateLimitExceededError,\n BulkheadFullError,\n TimeoutError,\n} from '@fortify-ts/core';\nimport { type CircuitBreaker } from '@fortify-ts/circuit-breaker';\nimport { type Retry } from '@fortify-ts/retry';\nimport { type RateLimiter } from '@fortify-ts/rate-limit';\nimport { type Timeout } from '@fortify-ts/timeout';\nimport { type Bulkhead } from '@fortify-ts/bulkhead';\nimport { type Fallback } from '@fortify-ts/fallback';\nimport { type Chain } from '@fortify-ts/middleware';\nimport {\n type HttpHandler,\n type HttpMiddleware,\n type HttpResponse,\n type KeyExtractor,\n HttpErrors,\n keyFromIp,\n} from './types.js';\n\n/**\n * Configuration for circuit breaker HTTP middleware.\n */\nexport interface CircuitBreakerMiddlewareConfig {\n /** Custom error response when circuit is open */\n onOpen?: () => HttpResponse;\n}\n\n/**\n * Create HTTP middleware that wraps handlers with a circuit breaker.\n *\n * @param circuitBreaker - Circuit breaker instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createCircuitBreakerMiddleware(\n circuitBreaker: CircuitBreaker<HttpResponse>,\n config?: CircuitBreakerMiddlewareConfig\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n try {\n return await circuitBreaker.execute(async (signal) => {\n return handler({ ...request, context: { ...request.context, signal } });\n });\n } catch (error) {\n if (error instanceof CircuitOpenError) {\n return config?.onOpen?.() ?? HttpErrors.serviceUnavailable();\n }\n throw error;\n }\n };\n };\n}\n\n/**\n * Configuration for retry HTTP middleware.\n */\nexport interface RetryMiddlewareConfig {\n /** Determine if a response should trigger a retry */\n shouldRetry?: (response: HttpResponse) => boolean;\n}\n\n/**\n * Create HTTP middleware that wraps handlers with retry logic.\n *\n * @param retry - Retry instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createRetryMiddleware(\n retry: Retry<HttpResponse>,\n config?: RetryMiddlewareConfig\n): HttpMiddleware {\n const shouldRetry = config?.shouldRetry ?? ((r) => r.status >= 500);\n\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n return retry.execute(async (signal) => {\n const response = await handler({\n ...request,\n context: { ...request.context, signal },\n });\n if (shouldRetry(response)) {\n throw new Error(`Server error: ${String(response.status)}`);\n }\n return response;\n });\n };\n };\n}\n\n/**\n * Configuration for rate limit HTTP middleware.\n */\nexport interface RateLimitMiddlewareConfig {\n /** Key extractor for rate limiting (defaults to IP) */\n keyExtractor?: KeyExtractor;\n /** Whether to wait for token or reject immediately */\n wait?: boolean;\n /** Custom error response when rate limited */\n onLimit?: (key: string) => HttpResponse;\n}\n\n/**\n * Create HTTP middleware that enforces rate limiting.\n *\n * @param rateLimiter - Rate limiter instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createRateLimitMiddleware(\n rateLimiter: RateLimiter,\n config?: RateLimitMiddlewareConfig\n): HttpMiddleware {\n const keyExtractor = config?.keyExtractor ?? keyFromIp;\n const wait = config?.wait ?? false;\n\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n const key = keyExtractor(request);\n\n if (wait) {\n await rateLimiter.wait(key);\n } else if (!rateLimiter.allow(key)) {\n if (config?.onLimit) {\n return config.onLimit(key);\n }\n throw new RateLimitExceededError(`Rate limit exceeded for key: ${key}`);\n }\n\n return handler(request);\n };\n };\n}\n\n/**\n * Create HTTP middleware that wraps handlers with rate limiting,\n * returning a 429 response when rate limited.\n *\n * @param rateLimiter - Rate limiter instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createRateLimitGuard(\n rateLimiter: RateLimiter,\n config?: Omit<RateLimitMiddlewareConfig, 'wait'>\n): HttpMiddleware {\n const keyExtractor = config?.keyExtractor ?? keyFromIp;\n\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n const key = keyExtractor(request);\n\n if (!rateLimiter.allow(key)) {\n return config?.onLimit?.(key) ?? HttpErrors.tooManyRequests();\n }\n\n return handler(request);\n };\n };\n}\n\n/**\n * Configuration for timeout HTTP middleware.\n */\nexport interface TimeoutMiddlewareConfig {\n /** Timeout duration in milliseconds */\n duration?: number;\n /** Custom error response on timeout */\n onTimeout?: () => HttpResponse;\n}\n\n/**\n * Create HTTP middleware that enforces request timeouts.\n *\n * @param timeout - Timeout instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createTimeoutMiddleware(\n timeout: Timeout<HttpResponse>,\n config?: TimeoutMiddlewareConfig\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n const operation = async (signal: AbortSignal) => {\n return handler({ ...request, context: { ...request.context, signal } });\n };\n\n try {\n // Use executeWithTimeout if custom duration is specified, otherwise use default\n return config?.duration !== undefined\n ? await timeout.executeWithTimeout(operation, config.duration)\n : await timeout.execute(operation);\n } catch (error) {\n if (error instanceof TimeoutError) {\n return config?.onTimeout?.() ?? HttpErrors.gatewayTimeout();\n }\n throw error;\n }\n };\n };\n}\n\n/**\n * Configuration for bulkhead HTTP middleware.\n */\nexport interface BulkheadMiddlewareConfig {\n /** Custom error response when bulkhead is full */\n onFull?: () => HttpResponse;\n}\n\n/**\n * Create HTTP middleware that limits concurrent requests.\n *\n * @param bulkhead - Bulkhead instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createBulkheadMiddleware(\n bulkhead: Bulkhead<HttpResponse>,\n config?: BulkheadMiddlewareConfig\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n try {\n return await bulkhead.execute(async (signal) => {\n return handler({ ...request, context: { ...request.context, signal } });\n });\n } catch (error) {\n if (error instanceof BulkheadFullError) {\n return config?.onFull?.() ?? HttpErrors.capacityExceeded();\n }\n throw error;\n }\n };\n };\n}\n\n/**\n * Configuration for fallback HTTP middleware.\n */\nexport interface FallbackMiddlewareConfig {\n /** Determine if error should trigger fallback */\n shouldFallback?: (error: Error) => boolean;\n}\n\n/**\n * Create HTTP middleware that provides fallback responses.\n *\n * @param fallback - Fallback instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createFallbackMiddleware(\n fallback: Fallback<HttpResponse>,\n _config?: FallbackMiddlewareConfig\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n return fallback.execute(async (signal) => {\n return handler({ ...request, context: { ...request.context, signal } });\n });\n };\n };\n}\n\n/**\n * Create HTTP middleware from a pre-configured middleware chain.\n *\n * @param chain - Middleware chain instance\n * @returns HTTP middleware function\n */\nexport function createChainMiddleware(\n chain: Chain<HttpResponse>\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n return chain.execute(async (signal) => {\n return handler({ ...request, context: { ...request.context, signal } });\n });\n };\n };\n}\n\n/**\n * Compose multiple HTTP middlewares into a single middleware.\n * Middlewares are applied in order (first = outermost).\n *\n * @param middlewares - Array of HTTP middlewares\n * @returns Composed HTTP middleware\n */\nexport function composeMiddleware(\n ...middlewares: HttpMiddleware[]\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return middlewares.reduceRight(\n (next, middleware) => middleware(next),\n handler\n );\n };\n}\n"],"mappings":"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","names":[]} | ||
| {"version":3,"sources":["../src/index.ts","../src/types.ts","../src/middleware.ts"],"sourcesContent":["export {\n type HttpRequest,\n type HttpResponse,\n type HttpHandler,\n type HttpMiddleware,\n type KeyExtractor,\n keyFromIp,\n keyFromHeader,\n combineKeys,\n createErrorResponse,\n HttpErrors,\n} from './types.js';\n\nexport {\n type CircuitBreakerMiddlewareConfig,\n type RetryMiddlewareConfig,\n type RateLimitMiddlewareConfig,\n type TimeoutMiddlewareConfig,\n type BulkheadMiddlewareConfig,\n createCircuitBreakerMiddleware,\n createRetryMiddleware,\n createRateLimitMiddleware,\n createRateLimitGuard,\n createTimeoutMiddleware,\n createBulkheadMiddleware,\n createFallbackMiddleware,\n createChainMiddleware,\n composeMiddleware,\n} from './middleware.js';\n","/**\n * Generic HTTP request interface.\n * Framework-agnostic representation of an HTTP request.\n */\nexport interface HttpRequest {\n /** HTTP method (GET, POST, PUT, DELETE, etc.) */\n method: string;\n /** Request URL or path */\n url: string;\n /** Request headers */\n headers: Record<string, string | string[] | undefined>;\n /** Request body (if any) */\n body?: unknown;\n /** Client IP address (for rate limiting) */\n ip?: string;\n /** Additional context data */\n context?: Record<string, unknown>;\n}\n\n/**\n * Generic HTTP response interface.\n * Framework-agnostic representation of an HTTP response.\n */\nexport interface HttpResponse {\n /** HTTP status code */\n status: number;\n /** Response headers */\n headers: Record<string, string | string[]>;\n /** Response body (if any) */\n body?: unknown;\n}\n\n/**\n * HTTP handler function type.\n * Takes a request and returns a promise of a response.\n */\nexport type HttpHandler = (request: HttpRequest) => Promise<HttpResponse>;\n\n/**\n * HTTP middleware function type.\n * Wraps a handler with additional behavior.\n */\nexport type HttpMiddleware = (handler: HttpHandler) => HttpHandler;\n\n/**\n * Key extractor function type.\n * Extracts a rate limiting key from an HTTP request.\n */\nexport type KeyExtractor = (request: HttpRequest) => string;\n\n/**\n * Extract rate limiting key from client IP address.\n *\n * @param request - HTTP request\n * @returns IP address or 'unknown'\n */\nexport const keyFromIp: KeyExtractor = (request: HttpRequest): string => {\n return request.ip ?? 'unknown';\n};\n\n/**\n * Create a key extractor that extracts from a specific header.\n *\n * @param header - Header name to extract key from\n * @param defaultValue - Default value if header is missing\n * @returns Key extractor function\n */\nexport function keyFromHeader(\n header: string,\n defaultValue = 'unknown'\n): KeyExtractor {\n return (request: HttpRequest): string => {\n const value = request.headers[header.toLowerCase()];\n if (Array.isArray(value)) {\n return value[0] ?? defaultValue;\n }\n return value ?? defaultValue;\n };\n}\n\n/**\n * Create a key extractor that combines multiple extractors.\n *\n * @param extractors - Key extractors to combine\n * @param separator - Separator between keys\n * @returns Combined key extractor\n */\nexport function combineKeys(\n extractors: KeyExtractor[],\n separator = ':'\n): KeyExtractor {\n return (request: HttpRequest): string => {\n return extractors.map((e) => e(request)).join(separator);\n };\n}\n\n/**\n * Standard HTTP error response factory.\n */\nexport function createErrorResponse(\n status: number,\n message: string,\n headers: Record<string, string> = {}\n): HttpResponse {\n return {\n status,\n headers: {\n 'Content-Type': 'application/json',\n ...headers,\n },\n body: { error: message },\n };\n}\n\n/**\n * Pre-built error responses for common scenarios.\n */\nexport const HttpErrors = {\n /** 429 Too Many Requests */\n tooManyRequests: (retryAfter?: number): HttpResponse =>\n createErrorResponse(\n 429,\n 'Too Many Requests',\n retryAfter !== undefined ? { 'Retry-After': String(retryAfter) } : {}\n ),\n\n /** 503 Service Unavailable (circuit open) */\n serviceUnavailable: (): HttpResponse =>\n createErrorResponse(503, 'Service Unavailable'),\n\n /** 504 Gateway Timeout */\n gatewayTimeout: (): HttpResponse =>\n createErrorResponse(504, 'Gateway Timeout'),\n\n /** 503 Service Unavailable (bulkhead full) */\n capacityExceeded: (): HttpResponse =>\n createErrorResponse(503, 'Service at capacity, please retry later'),\n} as const;\n","import {\n CircuitOpenError,\n RateLimitExceededError,\n BulkheadFullError,\n TimeoutError,\n} from '@fortify-ts/core';\nimport { type CircuitBreaker } from '@fortify-ts/circuit-breaker';\nimport { type Retry } from '@fortify-ts/retry';\nimport { type RateLimiter } from '@fortify-ts/rate-limit';\nimport { type Timeout } from '@fortify-ts/timeout';\nimport { type Bulkhead } from '@fortify-ts/bulkhead';\nimport { type Fallback } from '@fortify-ts/fallback';\nimport { type Chain } from '@fortify-ts/middleware';\nimport {\n type HttpHandler,\n type HttpMiddleware,\n type HttpResponse,\n type KeyExtractor,\n HttpErrors,\n keyFromIp,\n} from './types.js';\n\n/**\n * Configuration for circuit breaker HTTP middleware.\n */\nexport interface CircuitBreakerMiddlewareConfig {\n /** Custom error response when circuit is open */\n onOpen?: () => HttpResponse;\n}\n\n/**\n * Create HTTP middleware that wraps handlers with a circuit breaker.\n *\n * @param circuitBreaker - Circuit breaker instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createCircuitBreakerMiddleware(\n circuitBreaker: CircuitBreaker<HttpResponse>,\n config?: CircuitBreakerMiddlewareConfig\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n try {\n return await circuitBreaker.execute(async (signal) => {\n return handler({ ...request, context: { ...request.context, signal } });\n });\n } catch (error) {\n if (error instanceof CircuitOpenError) {\n return config?.onOpen?.() ?? HttpErrors.serviceUnavailable();\n }\n throw error;\n }\n };\n };\n}\n\n/**\n * Configuration for retry HTTP middleware.\n */\nexport interface RetryMiddlewareConfig {\n /** Determine if a response should trigger a retry */\n shouldRetry?: (response: HttpResponse) => boolean;\n}\n\n/**\n * Create HTTP middleware that wraps handlers with retry logic.\n *\n * @param retry - Retry instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createRetryMiddleware(\n retry: Retry<HttpResponse>,\n config?: RetryMiddlewareConfig\n): HttpMiddleware {\n const shouldRetry = config?.shouldRetry ?? ((r) => r.status >= 500);\n\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n return retry.execute(async (signal) => {\n const response = await handler({\n ...request,\n context: { ...request.context, signal },\n });\n if (shouldRetry(response)) {\n throw new Error(`Server error: ${String(response.status)}`);\n }\n return response;\n });\n };\n };\n}\n\n/**\n * Configuration for rate limit HTTP middleware.\n */\nexport interface RateLimitMiddlewareConfig {\n /** Key extractor for rate limiting (defaults to IP) */\n keyExtractor?: KeyExtractor;\n /** Whether to wait for token or reject immediately */\n wait?: boolean;\n /** Custom error response when rate limited */\n onLimit?: (key: string) => HttpResponse;\n}\n\n/**\n * Create HTTP middleware that enforces rate limiting.\n *\n * @param rateLimiter - Rate limiter instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createRateLimitMiddleware(\n rateLimiter: RateLimiter,\n config?: RateLimitMiddlewareConfig\n): HttpMiddleware {\n const keyExtractor = config?.keyExtractor ?? keyFromIp;\n const wait = config?.wait ?? false;\n\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n const key = keyExtractor(request);\n\n if (wait) {\n await rateLimiter.wait(key);\n } else if (!rateLimiter.allow(key)) {\n if (config?.onLimit) {\n return config.onLimit(key);\n }\n throw new RateLimitExceededError(`Rate limit exceeded for key: ${key}`);\n }\n\n return handler(request);\n };\n };\n}\n\n/**\n * Create HTTP middleware that wraps handlers with rate limiting,\n * returning a 429 response when rate limited.\n *\n * @param rateLimiter - Rate limiter instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createRateLimitGuard(\n rateLimiter: RateLimiter,\n config?: Omit<RateLimitMiddlewareConfig, 'wait'>\n): HttpMiddleware {\n const keyExtractor = config?.keyExtractor ?? keyFromIp;\n\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n const key = keyExtractor(request);\n\n if (!rateLimiter.allow(key)) {\n return config?.onLimit?.(key) ?? HttpErrors.tooManyRequests();\n }\n\n return handler(request);\n };\n };\n}\n\n/**\n * Configuration for timeout HTTP middleware.\n */\nexport interface TimeoutMiddlewareConfig {\n /** Timeout duration in milliseconds */\n duration?: number;\n /** Custom error response on timeout */\n onTimeout?: () => HttpResponse;\n}\n\n/**\n * Create HTTP middleware that enforces request timeouts.\n *\n * @param timeout - Timeout instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createTimeoutMiddleware(\n timeout: Timeout<HttpResponse>,\n config?: TimeoutMiddlewareConfig\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n const operation = async (signal: AbortSignal) => {\n return handler({ ...request, context: { ...request.context, signal } });\n };\n\n try {\n // Use executeWithTimeout if custom duration is specified, otherwise use default\n return config?.duration !== undefined\n ? await timeout.executeWithTimeout(operation, config.duration)\n : await timeout.execute(operation);\n } catch (error) {\n if (error instanceof TimeoutError) {\n return config?.onTimeout?.() ?? HttpErrors.gatewayTimeout();\n }\n throw error;\n }\n };\n };\n}\n\n/**\n * Configuration for bulkhead HTTP middleware.\n */\nexport interface BulkheadMiddlewareConfig {\n /** Custom error response when bulkhead is full */\n onFull?: () => HttpResponse;\n}\n\n/**\n * Create HTTP middleware that limits concurrent requests.\n *\n * @param bulkhead - Bulkhead instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createBulkheadMiddleware(\n bulkhead: Bulkhead<HttpResponse>,\n config?: BulkheadMiddlewareConfig\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n try {\n return await bulkhead.execute(async (signal) => {\n return handler({ ...request, context: { ...request.context, signal } });\n });\n } catch (error) {\n if (error instanceof BulkheadFullError) {\n return config?.onFull?.() ?? HttpErrors.capacityExceeded();\n }\n throw error;\n }\n };\n };\n}\n\n/**\n * Create HTTP middleware that provides fallback responses.\n *\n * @param fallback - Fallback instance (configure shouldFallback on the Fallback instance)\n * @returns HTTP middleware function\n */\nexport function createFallbackMiddleware(\n fallback: Fallback<HttpResponse>\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n return fallback.execute(async (signal) => {\n return handler({ ...request, context: { ...request.context, signal } });\n });\n };\n };\n}\n\n/**\n * Create HTTP middleware from a pre-configured middleware chain.\n *\n * @param chain - Middleware chain instance\n * @returns HTTP middleware function\n */\nexport function createChainMiddleware(\n chain: Chain<HttpResponse>\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n return chain.execute(async (signal) => {\n return handler({ ...request, context: { ...request.context, signal } });\n });\n };\n };\n}\n\n/**\n * Compose multiple HTTP middlewares into a single middleware.\n * Middlewares are applied in order (first = outermost).\n *\n * @param middlewares - Array of HTTP middlewares\n * @returns Composed HTTP middleware\n */\nexport function composeMiddleware(\n ...middlewares: HttpMiddleware[]\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return middlewares.reduceRight(\n (next, middleware) => middleware(next),\n handler\n );\n };\n}\n"],"mappings":"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","names":[]} |
+3
-11
@@ -186,16 +186,8 @@ import { CircuitBreaker } from '@fortify-ts/circuit-breaker'; | ||
| /** | ||
| * Configuration for fallback HTTP middleware. | ||
| */ | ||
| interface FallbackMiddlewareConfig { | ||
| /** Determine if error should trigger fallback */ | ||
| shouldFallback?: (error: Error) => boolean; | ||
| } | ||
| /** | ||
| * Create HTTP middleware that provides fallback responses. | ||
| * | ||
| * @param fallback - Fallback instance | ||
| * @param config - Optional middleware configuration | ||
| * @param fallback - Fallback instance (configure shouldFallback on the Fallback instance) | ||
| * @returns HTTP middleware function | ||
| */ | ||
| declare function createFallbackMiddleware(fallback: Fallback<HttpResponse>, _config?: FallbackMiddlewareConfig): HttpMiddleware; | ||
| declare function createFallbackMiddleware(fallback: Fallback<HttpResponse>): HttpMiddleware; | ||
| /** | ||
@@ -217,2 +209,2 @@ * Create HTTP middleware from a pre-configured middleware chain. | ||
| export { type BulkheadMiddlewareConfig, type CircuitBreakerMiddlewareConfig, type FallbackMiddlewareConfig, HttpErrors, type HttpHandler, type HttpMiddleware, type HttpRequest, type HttpResponse, type KeyExtractor, type RateLimitMiddlewareConfig, type RetryMiddlewareConfig, type TimeoutMiddlewareConfig, combineKeys, composeMiddleware, createBulkheadMiddleware, createChainMiddleware, createCircuitBreakerMiddleware, createErrorResponse, createFallbackMiddleware, createRateLimitGuard, createRateLimitMiddleware, createRetryMiddleware, createTimeoutMiddleware, keyFromHeader, keyFromIp }; | ||
| export { type BulkheadMiddlewareConfig, type CircuitBreakerMiddlewareConfig, HttpErrors, type HttpHandler, type HttpMiddleware, type HttpRequest, type HttpResponse, type KeyExtractor, type RateLimitMiddlewareConfig, type RetryMiddlewareConfig, type TimeoutMiddlewareConfig, combineKeys, composeMiddleware, createBulkheadMiddleware, createChainMiddleware, createCircuitBreakerMiddleware, createErrorResponse, createFallbackMiddleware, createRateLimitGuard, createRateLimitMiddleware, createRetryMiddleware, createTimeoutMiddleware, keyFromHeader, keyFromIp }; |
+3
-11
@@ -186,16 +186,8 @@ import { CircuitBreaker } from '@fortify-ts/circuit-breaker'; | ||
| /** | ||
| * Configuration for fallback HTTP middleware. | ||
| */ | ||
| interface FallbackMiddlewareConfig { | ||
| /** Determine if error should trigger fallback */ | ||
| shouldFallback?: (error: Error) => boolean; | ||
| } | ||
| /** | ||
| * Create HTTP middleware that provides fallback responses. | ||
| * | ||
| * @param fallback - Fallback instance | ||
| * @param config - Optional middleware configuration | ||
| * @param fallback - Fallback instance (configure shouldFallback on the Fallback instance) | ||
| * @returns HTTP middleware function | ||
| */ | ||
| declare function createFallbackMiddleware(fallback: Fallback<HttpResponse>, _config?: FallbackMiddlewareConfig): HttpMiddleware; | ||
| declare function createFallbackMiddleware(fallback: Fallback<HttpResponse>): HttpMiddleware; | ||
| /** | ||
@@ -217,2 +209,2 @@ * Create HTTP middleware from a pre-configured middleware chain. | ||
| export { type BulkheadMiddlewareConfig, type CircuitBreakerMiddlewareConfig, type FallbackMiddlewareConfig, HttpErrors, type HttpHandler, type HttpMiddleware, type HttpRequest, type HttpResponse, type KeyExtractor, type RateLimitMiddlewareConfig, type RetryMiddlewareConfig, type TimeoutMiddlewareConfig, combineKeys, composeMiddleware, createBulkheadMiddleware, createChainMiddleware, createCircuitBreakerMiddleware, createErrorResponse, createFallbackMiddleware, createRateLimitGuard, createRateLimitMiddleware, createRetryMiddleware, createTimeoutMiddleware, keyFromHeader, keyFromIp }; | ||
| export { type BulkheadMiddlewareConfig, type CircuitBreakerMiddlewareConfig, HttpErrors, type HttpHandler, type HttpMiddleware, type HttpRequest, type HttpResponse, type KeyExtractor, type RateLimitMiddlewareConfig, type RetryMiddlewareConfig, type TimeoutMiddlewareConfig, combineKeys, composeMiddleware, createBulkheadMiddleware, createChainMiddleware, createCircuitBreakerMiddleware, createErrorResponse, createFallbackMiddleware, createRateLimitGuard, createRateLimitMiddleware, createRetryMiddleware, createTimeoutMiddleware, keyFromHeader, keyFromIp }; |
+1
-1
@@ -147,3 +147,3 @@ // src/types.ts | ||
| } | ||
| function createFallbackMiddleware(fallback, _config) { | ||
| function createFallbackMiddleware(fallback) { | ||
| return (handler) => { | ||
@@ -150,0 +150,0 @@ return async (request) => { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"sources":["../src/types.ts","../src/middleware.ts"],"sourcesContent":["/**\n * Generic HTTP request interface.\n * Framework-agnostic representation of an HTTP request.\n */\nexport interface HttpRequest {\n /** HTTP method (GET, POST, PUT, DELETE, etc.) */\n method: string;\n /** Request URL or path */\n url: string;\n /** Request headers */\n headers: Record<string, string | string[] | undefined>;\n /** Request body (if any) */\n body?: unknown;\n /** Client IP address (for rate limiting) */\n ip?: string;\n /** Additional context data */\n context?: Record<string, unknown>;\n}\n\n/**\n * Generic HTTP response interface.\n * Framework-agnostic representation of an HTTP response.\n */\nexport interface HttpResponse {\n /** HTTP status code */\n status: number;\n /** Response headers */\n headers: Record<string, string | string[]>;\n /** Response body (if any) */\n body?: unknown;\n}\n\n/**\n * HTTP handler function type.\n * Takes a request and returns a promise of a response.\n */\nexport type HttpHandler = (request: HttpRequest) => Promise<HttpResponse>;\n\n/**\n * HTTP middleware function type.\n * Wraps a handler with additional behavior.\n */\nexport type HttpMiddleware = (handler: HttpHandler) => HttpHandler;\n\n/**\n * Key extractor function type.\n * Extracts a rate limiting key from an HTTP request.\n */\nexport type KeyExtractor = (request: HttpRequest) => string;\n\n/**\n * Extract rate limiting key from client IP address.\n *\n * @param request - HTTP request\n * @returns IP address or 'unknown'\n */\nexport const keyFromIp: KeyExtractor = (request: HttpRequest): string => {\n return request.ip ?? 'unknown';\n};\n\n/**\n * Create a key extractor that extracts from a specific header.\n *\n * @param header - Header name to extract key from\n * @param defaultValue - Default value if header is missing\n * @returns Key extractor function\n */\nexport function keyFromHeader(\n header: string,\n defaultValue = 'unknown'\n): KeyExtractor {\n return (request: HttpRequest): string => {\n const value = request.headers[header.toLowerCase()];\n if (Array.isArray(value)) {\n return value[0] ?? defaultValue;\n }\n return value ?? defaultValue;\n };\n}\n\n/**\n * Create a key extractor that combines multiple extractors.\n *\n * @param extractors - Key extractors to combine\n * @param separator - Separator between keys\n * @returns Combined key extractor\n */\nexport function combineKeys(\n extractors: KeyExtractor[],\n separator = ':'\n): KeyExtractor {\n return (request: HttpRequest): string => {\n return extractors.map((e) => e(request)).join(separator);\n };\n}\n\n/**\n * Standard HTTP error response factory.\n */\nexport function createErrorResponse(\n status: number,\n message: string,\n headers: Record<string, string> = {}\n): HttpResponse {\n return {\n status,\n headers: {\n 'Content-Type': 'application/json',\n ...headers,\n },\n body: { error: message },\n };\n}\n\n/**\n * Pre-built error responses for common scenarios.\n */\nexport const HttpErrors = {\n /** 429 Too Many Requests */\n tooManyRequests: (retryAfter?: number): HttpResponse =>\n createErrorResponse(\n 429,\n 'Too Many Requests',\n retryAfter !== undefined ? { 'Retry-After': String(retryAfter) } : {}\n ),\n\n /** 503 Service Unavailable (circuit open) */\n serviceUnavailable: (): HttpResponse =>\n createErrorResponse(503, 'Service Unavailable'),\n\n /** 504 Gateway Timeout */\n gatewayTimeout: (): HttpResponse =>\n createErrorResponse(504, 'Gateway Timeout'),\n\n /** 503 Service Unavailable (bulkhead full) */\n capacityExceeded: (): HttpResponse =>\n createErrorResponse(503, 'Service at capacity, please retry later'),\n} as const;\n","import {\n CircuitOpenError,\n RateLimitExceededError,\n BulkheadFullError,\n TimeoutError,\n} from '@fortify-ts/core';\nimport { type CircuitBreaker } from '@fortify-ts/circuit-breaker';\nimport { type Retry } from '@fortify-ts/retry';\nimport { type RateLimiter } from '@fortify-ts/rate-limit';\nimport { type Timeout } from '@fortify-ts/timeout';\nimport { type Bulkhead } from '@fortify-ts/bulkhead';\nimport { type Fallback } from '@fortify-ts/fallback';\nimport { type Chain } from '@fortify-ts/middleware';\nimport {\n type HttpHandler,\n type HttpMiddleware,\n type HttpResponse,\n type KeyExtractor,\n HttpErrors,\n keyFromIp,\n} from './types.js';\n\n/**\n * Configuration for circuit breaker HTTP middleware.\n */\nexport interface CircuitBreakerMiddlewareConfig {\n /** Custom error response when circuit is open */\n onOpen?: () => HttpResponse;\n}\n\n/**\n * Create HTTP middleware that wraps handlers with a circuit breaker.\n *\n * @param circuitBreaker - Circuit breaker instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createCircuitBreakerMiddleware(\n circuitBreaker: CircuitBreaker<HttpResponse>,\n config?: CircuitBreakerMiddlewareConfig\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n try {\n return await circuitBreaker.execute(async (signal) => {\n return handler({ ...request, context: { ...request.context, signal } });\n });\n } catch (error) {\n if (error instanceof CircuitOpenError) {\n return config?.onOpen?.() ?? HttpErrors.serviceUnavailable();\n }\n throw error;\n }\n };\n };\n}\n\n/**\n * Configuration for retry HTTP middleware.\n */\nexport interface RetryMiddlewareConfig {\n /** Determine if a response should trigger a retry */\n shouldRetry?: (response: HttpResponse) => boolean;\n}\n\n/**\n * Create HTTP middleware that wraps handlers with retry logic.\n *\n * @param retry - Retry instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createRetryMiddleware(\n retry: Retry<HttpResponse>,\n config?: RetryMiddlewareConfig\n): HttpMiddleware {\n const shouldRetry = config?.shouldRetry ?? ((r) => r.status >= 500);\n\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n return retry.execute(async (signal) => {\n const response = await handler({\n ...request,\n context: { ...request.context, signal },\n });\n if (shouldRetry(response)) {\n throw new Error(`Server error: ${String(response.status)}`);\n }\n return response;\n });\n };\n };\n}\n\n/**\n * Configuration for rate limit HTTP middleware.\n */\nexport interface RateLimitMiddlewareConfig {\n /** Key extractor for rate limiting (defaults to IP) */\n keyExtractor?: KeyExtractor;\n /** Whether to wait for token or reject immediately */\n wait?: boolean;\n /** Custom error response when rate limited */\n onLimit?: (key: string) => HttpResponse;\n}\n\n/**\n * Create HTTP middleware that enforces rate limiting.\n *\n * @param rateLimiter - Rate limiter instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createRateLimitMiddleware(\n rateLimiter: RateLimiter,\n config?: RateLimitMiddlewareConfig\n): HttpMiddleware {\n const keyExtractor = config?.keyExtractor ?? keyFromIp;\n const wait = config?.wait ?? false;\n\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n const key = keyExtractor(request);\n\n if (wait) {\n await rateLimiter.wait(key);\n } else if (!rateLimiter.allow(key)) {\n if (config?.onLimit) {\n return config.onLimit(key);\n }\n throw new RateLimitExceededError(`Rate limit exceeded for key: ${key}`);\n }\n\n return handler(request);\n };\n };\n}\n\n/**\n * Create HTTP middleware that wraps handlers with rate limiting,\n * returning a 429 response when rate limited.\n *\n * @param rateLimiter - Rate limiter instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createRateLimitGuard(\n rateLimiter: RateLimiter,\n config?: Omit<RateLimitMiddlewareConfig, 'wait'>\n): HttpMiddleware {\n const keyExtractor = config?.keyExtractor ?? keyFromIp;\n\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n const key = keyExtractor(request);\n\n if (!rateLimiter.allow(key)) {\n return config?.onLimit?.(key) ?? HttpErrors.tooManyRequests();\n }\n\n return handler(request);\n };\n };\n}\n\n/**\n * Configuration for timeout HTTP middleware.\n */\nexport interface TimeoutMiddlewareConfig {\n /** Timeout duration in milliseconds */\n duration?: number;\n /** Custom error response on timeout */\n onTimeout?: () => HttpResponse;\n}\n\n/**\n * Create HTTP middleware that enforces request timeouts.\n *\n * @param timeout - Timeout instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createTimeoutMiddleware(\n timeout: Timeout<HttpResponse>,\n config?: TimeoutMiddlewareConfig\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n const operation = async (signal: AbortSignal) => {\n return handler({ ...request, context: { ...request.context, signal } });\n };\n\n try {\n // Use executeWithTimeout if custom duration is specified, otherwise use default\n return config?.duration !== undefined\n ? await timeout.executeWithTimeout(operation, config.duration)\n : await timeout.execute(operation);\n } catch (error) {\n if (error instanceof TimeoutError) {\n return config?.onTimeout?.() ?? HttpErrors.gatewayTimeout();\n }\n throw error;\n }\n };\n };\n}\n\n/**\n * Configuration for bulkhead HTTP middleware.\n */\nexport interface BulkheadMiddlewareConfig {\n /** Custom error response when bulkhead is full */\n onFull?: () => HttpResponse;\n}\n\n/**\n * Create HTTP middleware that limits concurrent requests.\n *\n * @param bulkhead - Bulkhead instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createBulkheadMiddleware(\n bulkhead: Bulkhead<HttpResponse>,\n config?: BulkheadMiddlewareConfig\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n try {\n return await bulkhead.execute(async (signal) => {\n return handler({ ...request, context: { ...request.context, signal } });\n });\n } catch (error) {\n if (error instanceof BulkheadFullError) {\n return config?.onFull?.() ?? HttpErrors.capacityExceeded();\n }\n throw error;\n }\n };\n };\n}\n\n/**\n * Configuration for fallback HTTP middleware.\n */\nexport interface FallbackMiddlewareConfig {\n /** Determine if error should trigger fallback */\n shouldFallback?: (error: Error) => boolean;\n}\n\n/**\n * Create HTTP middleware that provides fallback responses.\n *\n * @param fallback - Fallback instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createFallbackMiddleware(\n fallback: Fallback<HttpResponse>,\n _config?: FallbackMiddlewareConfig\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n return fallback.execute(async (signal) => {\n return handler({ ...request, context: { ...request.context, signal } });\n });\n };\n };\n}\n\n/**\n * Create HTTP middleware from a pre-configured middleware chain.\n *\n * @param chain - Middleware chain instance\n * @returns HTTP middleware function\n */\nexport function createChainMiddleware(\n chain: Chain<HttpResponse>\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n return chain.execute(async (signal) => {\n return handler({ ...request, context: { ...request.context, signal } });\n });\n };\n };\n}\n\n/**\n * Compose multiple HTTP middlewares into a single middleware.\n * Middlewares are applied in order (first = outermost).\n *\n * @param middlewares - Array of HTTP middlewares\n * @returns Composed HTTP middleware\n */\nexport function composeMiddleware(\n ...middlewares: HttpMiddleware[]\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return middlewares.reduceRight(\n (next, middleware) => middleware(next),\n handler\n );\n };\n}\n"],"mappings":";AAwDO,IAAM,YAA0B,CAAC,YAAiC;AACvE,SAAO,QAAQ,MAAM;AACvB;AASO,SAAS,cACd,QACA,eAAe,WACD;AACd,SAAO,CAAC,YAAiC;AACvC,UAAM,QAAQ,QAAQ,QAAQ,OAAO,YAAY,CAAC;AAClD,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,aAAO,MAAM,CAAC,KAAK;AAAA,IACrB;AACA,WAAO,SAAS;AAAA,EAClB;AACF;AASO,SAAS,YACd,YACA,YAAY,KACE;AACd,SAAO,CAAC,YAAiC;AACvC,WAAO,WAAW,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,SAAS;AAAA,EACzD;AACF;AAKO,SAAS,oBACd,QACA,SACA,UAAkC,CAAC,GACrB;AACd,SAAO;AAAA,IACL;AAAA,IACA,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,GAAG;AAAA,IACL;AAAA,IACA,MAAM,EAAE,OAAO,QAAQ;AAAA,EACzB;AACF;AAKO,IAAM,aAAa;AAAA;AAAA,EAExB,iBAAiB,CAAC,eAChB;AAAA,IACE;AAAA,IACA;AAAA,IACA,eAAe,SAAY,EAAE,eAAe,OAAO,UAAU,EAAE,IAAI,CAAC;AAAA,EACtE;AAAA;AAAA,EAGF,oBAAoB,MAClB,oBAAoB,KAAK,qBAAqB;AAAA;AAAA,EAGhD,gBAAgB,MACd,oBAAoB,KAAK,iBAAiB;AAAA;AAAA,EAG5C,kBAAkB,MAChB,oBAAoB,KAAK,yCAAyC;AACtE;;;ACzIA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAgCA,SAAS,+BACd,gBACA,QACgB;AAChB,SAAO,CAAC,YAAsC;AAC5C,WAAO,OAAO,YAAY;AACxB,UAAI;AACF,eAAO,MAAM,eAAe,QAAQ,OAAO,WAAW;AACpD,iBAAO,QAAQ,EAAE,GAAG,SAAS,SAAS,EAAE,GAAG,QAAQ,SAAS,OAAO,EAAE,CAAC;AAAA,QACxE,CAAC;AAAA,MACH,SAAS,OAAO;AACd,YAAI,iBAAiB,kBAAkB;AACrC,iBAAO,QAAQ,SAAS,KAAK,WAAW,mBAAmB;AAAA,QAC7D;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAiBO,SAAS,sBACd,OACA,QACgB;AAChB,QAAM,cAAc,QAAQ,gBAAgB,CAAC,MAAM,EAAE,UAAU;AAE/D,SAAO,CAAC,YAAsC;AAC5C,WAAO,OAAO,YAAY;AACxB,aAAO,MAAM,QAAQ,OAAO,WAAW;AACrC,cAAM,WAAW,MAAM,QAAQ;AAAA,UAC7B,GAAG;AAAA,UACH,SAAS,EAAE,GAAG,QAAQ,SAAS,OAAO;AAAA,QACxC,CAAC;AACD,YAAI,YAAY,QAAQ,GAAG;AACzB,gBAAM,IAAI,MAAM,iBAAiB,OAAO,SAAS,MAAM,CAAC,EAAE;AAAA,QAC5D;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAqBO,SAAS,0BACd,aACA,QACgB;AAChB,QAAM,eAAe,QAAQ,gBAAgB;AAC7C,QAAM,OAAO,QAAQ,QAAQ;AAE7B,SAAO,CAAC,YAAsC;AAC5C,WAAO,OAAO,YAAY;AACxB,YAAM,MAAM,aAAa,OAAO;AAEhC,UAAI,MAAM;AACR,cAAM,YAAY,KAAK,GAAG;AAAA,MAC5B,WAAW,CAAC,YAAY,MAAM,GAAG,GAAG;AAClC,YAAI,QAAQ,SAAS;AACnB,iBAAO,OAAO,QAAQ,GAAG;AAAA,QAC3B;AACA,cAAM,IAAI,uBAAuB,gCAAgC,GAAG,EAAE;AAAA,MACxE;AAEA,aAAO,QAAQ,OAAO;AAAA,IACxB;AAAA,EACF;AACF;AAUO,SAAS,qBACd,aACA,QACgB;AAChB,QAAM,eAAe,QAAQ,gBAAgB;AAE7C,SAAO,CAAC,YAAsC;AAC5C,WAAO,OAAO,YAAY;AACxB,YAAM,MAAM,aAAa,OAAO;AAEhC,UAAI,CAAC,YAAY,MAAM,GAAG,GAAG;AAC3B,eAAO,QAAQ,UAAU,GAAG,KAAK,WAAW,gBAAgB;AAAA,MAC9D;AAEA,aAAO,QAAQ,OAAO;AAAA,IACxB;AAAA,EACF;AACF;AAmBO,SAAS,wBACd,SACA,QACgB;AAChB,SAAO,CAAC,YAAsC;AAC5C,WAAO,OAAO,YAAY;AACxB,YAAM,YAAY,OAAO,WAAwB;AAC/C,eAAO,QAAQ,EAAE,GAAG,SAAS,SAAS,EAAE,GAAG,QAAQ,SAAS,OAAO,EAAE,CAAC;AAAA,MACxE;AAEA,UAAI;AAEF,eAAO,QAAQ,aAAa,SACxB,MAAM,QAAQ,mBAAmB,WAAW,OAAO,QAAQ,IAC3D,MAAM,QAAQ,QAAQ,SAAS;AAAA,MACrC,SAAS,OAAO;AACd,YAAI,iBAAiB,cAAc;AACjC,iBAAO,QAAQ,YAAY,KAAK,WAAW,eAAe;AAAA,QAC5D;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAiBO,SAAS,yBACd,UACA,QACgB;AAChB,SAAO,CAAC,YAAsC;AAC5C,WAAO,OAAO,YAAY;AACxB,UAAI;AACF,eAAO,MAAM,SAAS,QAAQ,OAAO,WAAW;AAC9C,iBAAO,QAAQ,EAAE,GAAG,SAAS,SAAS,EAAE,GAAG,QAAQ,SAAS,OAAO,EAAE,CAAC;AAAA,QACxE,CAAC;AAAA,MACH,SAAS,OAAO;AACd,YAAI,iBAAiB,mBAAmB;AACtC,iBAAO,QAAQ,SAAS,KAAK,WAAW,iBAAiB;AAAA,QAC3D;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAiBO,SAAS,yBACd,UACA,SACgB;AAChB,SAAO,CAAC,YAAsC;AAC5C,WAAO,OAAO,YAAY;AACxB,aAAO,SAAS,QAAQ,OAAO,WAAW;AACxC,eAAO,QAAQ,EAAE,GAAG,SAAS,SAAS,EAAE,GAAG,QAAQ,SAAS,OAAO,EAAE,CAAC;AAAA,MACxE,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAQO,SAAS,sBACd,OACgB;AAChB,SAAO,CAAC,YAAsC;AAC5C,WAAO,OAAO,YAAY;AACxB,aAAO,MAAM,QAAQ,OAAO,WAAW;AACrC,eAAO,QAAQ,EAAE,GAAG,SAAS,SAAS,EAAE,GAAG,QAAQ,SAAS,OAAO,EAAE,CAAC;AAAA,MACxE,CAAC;AAAA,IACH;AAAA,EACF;AACF;AASO,SAAS,qBACX,aACa;AAChB,SAAO,CAAC,YAAsC;AAC5C,WAAO,YAAY;AAAA,MACjB,CAAC,MAAM,eAAe,WAAW,IAAI;AAAA,MACrC;AAAA,IACF;AAAA,EACF;AACF;","names":[]} | ||
| {"version":3,"sources":["../src/types.ts","../src/middleware.ts"],"sourcesContent":["/**\n * Generic HTTP request interface.\n * Framework-agnostic representation of an HTTP request.\n */\nexport interface HttpRequest {\n /** HTTP method (GET, POST, PUT, DELETE, etc.) */\n method: string;\n /** Request URL or path */\n url: string;\n /** Request headers */\n headers: Record<string, string | string[] | undefined>;\n /** Request body (if any) */\n body?: unknown;\n /** Client IP address (for rate limiting) */\n ip?: string;\n /** Additional context data */\n context?: Record<string, unknown>;\n}\n\n/**\n * Generic HTTP response interface.\n * Framework-agnostic representation of an HTTP response.\n */\nexport interface HttpResponse {\n /** HTTP status code */\n status: number;\n /** Response headers */\n headers: Record<string, string | string[]>;\n /** Response body (if any) */\n body?: unknown;\n}\n\n/**\n * HTTP handler function type.\n * Takes a request and returns a promise of a response.\n */\nexport type HttpHandler = (request: HttpRequest) => Promise<HttpResponse>;\n\n/**\n * HTTP middleware function type.\n * Wraps a handler with additional behavior.\n */\nexport type HttpMiddleware = (handler: HttpHandler) => HttpHandler;\n\n/**\n * Key extractor function type.\n * Extracts a rate limiting key from an HTTP request.\n */\nexport type KeyExtractor = (request: HttpRequest) => string;\n\n/**\n * Extract rate limiting key from client IP address.\n *\n * @param request - HTTP request\n * @returns IP address or 'unknown'\n */\nexport const keyFromIp: KeyExtractor = (request: HttpRequest): string => {\n return request.ip ?? 'unknown';\n};\n\n/**\n * Create a key extractor that extracts from a specific header.\n *\n * @param header - Header name to extract key from\n * @param defaultValue - Default value if header is missing\n * @returns Key extractor function\n */\nexport function keyFromHeader(\n header: string,\n defaultValue = 'unknown'\n): KeyExtractor {\n return (request: HttpRequest): string => {\n const value = request.headers[header.toLowerCase()];\n if (Array.isArray(value)) {\n return value[0] ?? defaultValue;\n }\n return value ?? defaultValue;\n };\n}\n\n/**\n * Create a key extractor that combines multiple extractors.\n *\n * @param extractors - Key extractors to combine\n * @param separator - Separator between keys\n * @returns Combined key extractor\n */\nexport function combineKeys(\n extractors: KeyExtractor[],\n separator = ':'\n): KeyExtractor {\n return (request: HttpRequest): string => {\n return extractors.map((e) => e(request)).join(separator);\n };\n}\n\n/**\n * Standard HTTP error response factory.\n */\nexport function createErrorResponse(\n status: number,\n message: string,\n headers: Record<string, string> = {}\n): HttpResponse {\n return {\n status,\n headers: {\n 'Content-Type': 'application/json',\n ...headers,\n },\n body: { error: message },\n };\n}\n\n/**\n * Pre-built error responses for common scenarios.\n */\nexport const HttpErrors = {\n /** 429 Too Many Requests */\n tooManyRequests: (retryAfter?: number): HttpResponse =>\n createErrorResponse(\n 429,\n 'Too Many Requests',\n retryAfter !== undefined ? { 'Retry-After': String(retryAfter) } : {}\n ),\n\n /** 503 Service Unavailable (circuit open) */\n serviceUnavailable: (): HttpResponse =>\n createErrorResponse(503, 'Service Unavailable'),\n\n /** 504 Gateway Timeout */\n gatewayTimeout: (): HttpResponse =>\n createErrorResponse(504, 'Gateway Timeout'),\n\n /** 503 Service Unavailable (bulkhead full) */\n capacityExceeded: (): HttpResponse =>\n createErrorResponse(503, 'Service at capacity, please retry later'),\n} as const;\n","import {\n CircuitOpenError,\n RateLimitExceededError,\n BulkheadFullError,\n TimeoutError,\n} from '@fortify-ts/core';\nimport { type CircuitBreaker } from '@fortify-ts/circuit-breaker';\nimport { type Retry } from '@fortify-ts/retry';\nimport { type RateLimiter } from '@fortify-ts/rate-limit';\nimport { type Timeout } from '@fortify-ts/timeout';\nimport { type Bulkhead } from '@fortify-ts/bulkhead';\nimport { type Fallback } from '@fortify-ts/fallback';\nimport { type Chain } from '@fortify-ts/middleware';\nimport {\n type HttpHandler,\n type HttpMiddleware,\n type HttpResponse,\n type KeyExtractor,\n HttpErrors,\n keyFromIp,\n} from './types.js';\n\n/**\n * Configuration for circuit breaker HTTP middleware.\n */\nexport interface CircuitBreakerMiddlewareConfig {\n /** Custom error response when circuit is open */\n onOpen?: () => HttpResponse;\n}\n\n/**\n * Create HTTP middleware that wraps handlers with a circuit breaker.\n *\n * @param circuitBreaker - Circuit breaker instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createCircuitBreakerMiddleware(\n circuitBreaker: CircuitBreaker<HttpResponse>,\n config?: CircuitBreakerMiddlewareConfig\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n try {\n return await circuitBreaker.execute(async (signal) => {\n return handler({ ...request, context: { ...request.context, signal } });\n });\n } catch (error) {\n if (error instanceof CircuitOpenError) {\n return config?.onOpen?.() ?? HttpErrors.serviceUnavailable();\n }\n throw error;\n }\n };\n };\n}\n\n/**\n * Configuration for retry HTTP middleware.\n */\nexport interface RetryMiddlewareConfig {\n /** Determine if a response should trigger a retry */\n shouldRetry?: (response: HttpResponse) => boolean;\n}\n\n/**\n * Create HTTP middleware that wraps handlers with retry logic.\n *\n * @param retry - Retry instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createRetryMiddleware(\n retry: Retry<HttpResponse>,\n config?: RetryMiddlewareConfig\n): HttpMiddleware {\n const shouldRetry = config?.shouldRetry ?? ((r) => r.status >= 500);\n\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n return retry.execute(async (signal) => {\n const response = await handler({\n ...request,\n context: { ...request.context, signal },\n });\n if (shouldRetry(response)) {\n throw new Error(`Server error: ${String(response.status)}`);\n }\n return response;\n });\n };\n };\n}\n\n/**\n * Configuration for rate limit HTTP middleware.\n */\nexport interface RateLimitMiddlewareConfig {\n /** Key extractor for rate limiting (defaults to IP) */\n keyExtractor?: KeyExtractor;\n /** Whether to wait for token or reject immediately */\n wait?: boolean;\n /** Custom error response when rate limited */\n onLimit?: (key: string) => HttpResponse;\n}\n\n/**\n * Create HTTP middleware that enforces rate limiting.\n *\n * @param rateLimiter - Rate limiter instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createRateLimitMiddleware(\n rateLimiter: RateLimiter,\n config?: RateLimitMiddlewareConfig\n): HttpMiddleware {\n const keyExtractor = config?.keyExtractor ?? keyFromIp;\n const wait = config?.wait ?? false;\n\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n const key = keyExtractor(request);\n\n if (wait) {\n await rateLimiter.wait(key);\n } else if (!rateLimiter.allow(key)) {\n if (config?.onLimit) {\n return config.onLimit(key);\n }\n throw new RateLimitExceededError(`Rate limit exceeded for key: ${key}`);\n }\n\n return handler(request);\n };\n };\n}\n\n/**\n * Create HTTP middleware that wraps handlers with rate limiting,\n * returning a 429 response when rate limited.\n *\n * @param rateLimiter - Rate limiter instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createRateLimitGuard(\n rateLimiter: RateLimiter,\n config?: Omit<RateLimitMiddlewareConfig, 'wait'>\n): HttpMiddleware {\n const keyExtractor = config?.keyExtractor ?? keyFromIp;\n\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n const key = keyExtractor(request);\n\n if (!rateLimiter.allow(key)) {\n return config?.onLimit?.(key) ?? HttpErrors.tooManyRequests();\n }\n\n return handler(request);\n };\n };\n}\n\n/**\n * Configuration for timeout HTTP middleware.\n */\nexport interface TimeoutMiddlewareConfig {\n /** Timeout duration in milliseconds */\n duration?: number;\n /** Custom error response on timeout */\n onTimeout?: () => HttpResponse;\n}\n\n/**\n * Create HTTP middleware that enforces request timeouts.\n *\n * @param timeout - Timeout instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createTimeoutMiddleware(\n timeout: Timeout<HttpResponse>,\n config?: TimeoutMiddlewareConfig\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n const operation = async (signal: AbortSignal) => {\n return handler({ ...request, context: { ...request.context, signal } });\n };\n\n try {\n // Use executeWithTimeout if custom duration is specified, otherwise use default\n return config?.duration !== undefined\n ? await timeout.executeWithTimeout(operation, config.duration)\n : await timeout.execute(operation);\n } catch (error) {\n if (error instanceof TimeoutError) {\n return config?.onTimeout?.() ?? HttpErrors.gatewayTimeout();\n }\n throw error;\n }\n };\n };\n}\n\n/**\n * Configuration for bulkhead HTTP middleware.\n */\nexport interface BulkheadMiddlewareConfig {\n /** Custom error response when bulkhead is full */\n onFull?: () => HttpResponse;\n}\n\n/**\n * Create HTTP middleware that limits concurrent requests.\n *\n * @param bulkhead - Bulkhead instance\n * @param config - Optional middleware configuration\n * @returns HTTP middleware function\n */\nexport function createBulkheadMiddleware(\n bulkhead: Bulkhead<HttpResponse>,\n config?: BulkheadMiddlewareConfig\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n try {\n return await bulkhead.execute(async (signal) => {\n return handler({ ...request, context: { ...request.context, signal } });\n });\n } catch (error) {\n if (error instanceof BulkheadFullError) {\n return config?.onFull?.() ?? HttpErrors.capacityExceeded();\n }\n throw error;\n }\n };\n };\n}\n\n/**\n * Create HTTP middleware that provides fallback responses.\n *\n * @param fallback - Fallback instance (configure shouldFallback on the Fallback instance)\n * @returns HTTP middleware function\n */\nexport function createFallbackMiddleware(\n fallback: Fallback<HttpResponse>\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n return fallback.execute(async (signal) => {\n return handler({ ...request, context: { ...request.context, signal } });\n });\n };\n };\n}\n\n/**\n * Create HTTP middleware from a pre-configured middleware chain.\n *\n * @param chain - Middleware chain instance\n * @returns HTTP middleware function\n */\nexport function createChainMiddleware(\n chain: Chain<HttpResponse>\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return async (request) => {\n return chain.execute(async (signal) => {\n return handler({ ...request, context: { ...request.context, signal } });\n });\n };\n };\n}\n\n/**\n * Compose multiple HTTP middlewares into a single middleware.\n * Middlewares are applied in order (first = outermost).\n *\n * @param middlewares - Array of HTTP middlewares\n * @returns Composed HTTP middleware\n */\nexport function composeMiddleware(\n ...middlewares: HttpMiddleware[]\n): HttpMiddleware {\n return (handler: HttpHandler): HttpHandler => {\n return middlewares.reduceRight(\n (next, middleware) => middleware(next),\n handler\n );\n };\n}\n"],"mappings":"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","names":[]} |
+10
-9
| { | ||
| "name": "@fortify-ts/http", | ||
| "version": "0.1.1", | ||
| "version": "0.1.2", | ||
| "description": "Framework-agnostic HTTP utilities for fortify-ts resilience patterns", | ||
@@ -25,10 +25,10 @@ "type": "module", | ||
| "dependencies": { | ||
| "@fortify-ts/bulkhead": "0.1.1", | ||
| "@fortify-ts/circuit-breaker": "0.1.1", | ||
| "@fortify-ts/fallback": "0.1.1", | ||
| "@fortify-ts/core": "0.1.1", | ||
| "@fortify-ts/middleware": "0.1.1", | ||
| "@fortify-ts/rate-limit": "0.1.1", | ||
| "@fortify-ts/timeout": "0.1.1", | ||
| "@fortify-ts/retry": "0.1.1" | ||
| "@fortify-ts/bulkhead": "0.1.2", | ||
| "@fortify-ts/circuit-breaker": "0.1.2", | ||
| "@fortify-ts/fallback": "0.1.2", | ||
| "@fortify-ts/core": "0.1.2", | ||
| "@fortify-ts/middleware": "0.1.2", | ||
| "@fortify-ts/rate-limit": "0.1.2", | ||
| "@fortify-ts/retry": "0.1.2", | ||
| "@fortify-ts/timeout": "0.1.2" | ||
| }, | ||
@@ -52,2 +52,3 @@ "devDependencies": { | ||
| "license": "MIT", | ||
| "sideEffects": false, | ||
| "scripts": { | ||
@@ -54,0 +55,0 @@ "build": "tsup", |
62917
-1.67%597
-1.32%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated
Updated
Updated
Updated
Updated
Updated
Updated