@bereasoftware/nexa 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"nexa.iife.js","names":[],"sources":["../src/types/index.ts","../src/utils/index.ts","../src/http-client/node-http-adapter.ts","../src/http-client/http-client.ts","../src/realtime/websocket-client.ts","../src/realtime/sse-client.ts","../src/realtime/plugin.ts","../src/testing/mock-client.ts"],"sourcesContent":["/**\r\n * HTTP Client Plugin - Type Definitions\r\n * Combines fetch power + axios convenience with SOLID principles\r\n */\r\n\r\n// ============= Result Type (Either monad) =============\r\n/**\r\n * Represents a successful or failed result\r\n * Allows for type-safe error handling without exceptions\r\n */\r\nexport type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };\r\n\r\nexport const Ok = <T,>(value: T): Result<T> => ({ ok: true, value });\r\nexport const Err = <E,>(error: E): Result<never, E> => ({ ok: false, error });\r\n\r\n// ============= HTTP Request/Response =============\r\nexport interface HttpRequest {\r\n url: string;\r\n method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';\r\n headers?: Record<string, string>;\r\n body?: unknown;\r\n query?: Record<string, string | number | boolean>;\r\n params?: Record<string, string | number>;\r\n timeout?: HttpTimeout;\r\n signal?: AbortSignal;\r\n /**\r\n * Controls cookie/credential policy for CORS requests. Same as fetch API.\r\n * 'omit' | 'same-origin' | 'include'\r\n */\r\n credentials?: RequestCredentials;\r\n /**\r\n * Adapter personalizado para esta request (firma igual a fetch).\r\n */\r\n adapter?: (input: RequestInfo, init?: RequestInit) => Promise<Response>;\r\n /**\r\n * Si es true (default), convierte automáticamente el body a FormData si detecta archivos.\r\n */\r\n autoFormData?: boolean;\r\n /**\r\n * Transport layer to use for this request.\r\n * Overrides global transport setting.\r\n */\r\n transport?: 'fetch' | 'node' | 'http2' | 'deno' | 'bun' | 'cloudflare';\r\n /**\r\n * Node.js specific transport options for this request.\r\n * Overrides global nodeOptions.\r\n */\r\n nodeOptions?: NodeTransportOptions;\r\n}\r\n\r\nexport interface HttpResponse<T = unknown> {\r\n status: number;\r\n statusText: string;\r\n headers: Headers;\r\n data: T;\r\n request: HttpRequest;\r\n duration: number;\r\n}\r\n\r\nexport interface HttpErrorDetails {\r\n message: string;\r\n status?: number;\r\n statusText?: string;\r\n code?: string;\r\n originalError?: unknown;\r\n /**\r\n * The HTTP request that caused the error (available for both network and HTTP errors)\r\n */\r\n request?: HttpRequest;\r\n /**\r\n * The HTTP response if the request reached the server and received a response (HTTP errors only)\r\n */\r\n response?: HttpResponse<unknown>;\r\n /**\r\n * The configuration used for the request\r\n */\r\n config?: HttpRequestConfig;\r\n}\r\n\r\n// ============= Progress =============\r\nexport interface ProgressEvent {\r\n loaded: number;\r\n total: number;\r\n percent: number;\r\n}\r\n\r\n// ============= Lifecycle Hooks =============\r\nexport interface RequestHooks<T = unknown> {\r\n onStart?: (request: HttpRequest) => void;\r\n onSuccess?: (response: HttpResponse<T>) => void;\r\n onError?: (error: HttpErrorDetails) => void;\r\n onFinally?: () => void;\r\n onRetry?: (attempt: number, error: HttpErrorDetails) => void;\r\n}\r\n\r\n// ============= Interceptor Pattern (Open/Closed) =============\r\nexport interface RequestInterceptor {\r\n onRequest(request: HttpRequest): HttpRequest | Promise<HttpRequest>;\r\n onError?(error: HttpErrorDetails): HttpErrorDetails | Promise<HttpErrorDetails>;\r\n}\r\n\r\nexport interface ResponseInterceptor {\r\n onResponse<T = unknown>(response: HttpResponse<T>): HttpResponse<T> | Promise<HttpResponse<T>>;\r\n onError?(error: HttpErrorDetails): HttpErrorDetails | Promise<HttpErrorDetails>;\r\n}\r\n\r\n// ============= Retry Strategy (Strategy Pattern) =============\r\nexport type RetryCondition = (error: HttpErrorDetails, attempt: number) => boolean;\r\n\r\nexport interface RetryStrategy {\r\n shouldRetry(attempt: number, error: HttpErrorDetails): boolean;\r\n delayMs(attempt: number): number;\r\n}\r\n\r\nexport interface InlineRetryConfig {\r\n maxAttempts?: number;\r\n backoffMs?: number;\r\n on?: RetryCondition;\r\n}\r\n\r\n// ============= Node.js Transport Options =============\r\n/**\r\n * Node.js specific transport configuration for HTTP/1.1 and HTTP/2.\r\n */\r\nexport interface NodeTransportOptions {\r\n /**\r\n * Enable keep-alive connections. Default: true\r\n */\r\n keepAlive?: boolean;\r\n /**\r\n * Maximum number of sockets to allow per host. Default: 50\r\n */\r\n maxSockets?: number;\r\n /**\r\n * Maximum number of sockets to leave open in a free state. Default: 10\r\n */\r\n maxFreeSockets?: number;\r\n /**\r\n * Maximum number of requests per socket. Default: 0 (unlimited)\r\n */\r\n maxRequestsPerSocket?: number;\r\n /**\r\n * Socket timeout in milliseconds. Default: 60000 (60 seconds)\r\n */\r\n timeout?: number;\r\n /**\r\n * Enable HTTP/2 protocol (only when transport is 'http2').\r\n */\r\n http2?: boolean;\r\n /**\r\n * HTTP/2 specific settings.\r\n */\r\n http2Settings?: Record<string, unknown>;\r\n}\r\n\r\n// ============= Cache Strategy (Strategy Pattern) =============\r\nexport interface CacheStrategy {\r\n get(key: string): unknown | null;\r\n set(key: string, value: unknown, ttlMs?: number): void;\r\n clear(): void;\r\n has(key: string): boolean;\r\n}\r\n\r\n// ============= Validation & Transform (Processing Pipeline) =============\r\nexport interface Validator {\r\n validate(data: unknown): Result<unknown, HttpErrorDetails>;\r\n}\r\n\r\nexport interface Transformer {\r\n transform(data: unknown): unknown;\r\n}\r\n\r\n// ============= Response Type =============\r\nexport type ResponseType = 'json' | 'text' | 'blob' | 'arrayBuffer' | 'formData' | 'stream' | 'auto';\r\n\r\n// ============= Timeout Configuration =============\r\n/**\r\n * Timeout configuration for HTTP requests.\r\n * - number: total timeout for the entire request (connection + response)\r\n * - object: differentiated timeouts for connection and response phases\r\n */\r\nexport type HttpTimeout = number | {\r\n /**\r\n * Maximum time to establish connection (TCP/TLS handshake) in milliseconds.\r\n * If not specified, no connection timeout is applied.\r\n */\r\n connection?: number;\r\n /**\r\n * Maximum time to receive complete response (after connection is established) in milliseconds.\r\n * If not specified, no response timeout is applied.\r\n */\r\n response?: number;\r\n /**\r\n * Total timeout for the entire request (connection + response) in milliseconds.\r\n * If specified, overrides both connection and response timeouts.\r\n * Provided for backward compatibility and convenience.\r\n */\r\n total?: number;\r\n};\r\n\r\n// ============= Request Configuration =============\r\n/**\r\n * Configuración extendida para solicitudes HTTP.\r\n * - credentials: controla el envío de cookies/credenciales ('omit' | 'same-origin' | 'include').\r\n */\r\nexport interface HttpRequestConfig extends HttpRequest {\r\n /**\r\n * Si es true (default), convierte automáticamente el body a FormData si detecta archivos.\r\n */\r\n autoFormData?: boolean;\r\n /**\r\n * Compatibilidad con axios: si es true, establece credentials: 'include'; si es false, credentials: 'same-origin'.\r\n * Si también se especifica credentials, este campo es ignorado.\r\n */\r\n withCredentials?: boolean;\r\n /**\r\n * Permite usar un adapter personalizado para la request (firma igual a fetch).\r\n * Útil para mocks, tests, o entornos especiales.\r\n */\r\n adapter?: (input: RequestInfo, init?: RequestInit) => Promise<Response>;\r\n /**\r\n * Permite transformar el body antes de serializarlo y enviarlo. Similar a axios.transformRequest.\r\n * Puede ser una función o un array de funciones.\r\n */\r\n transformRequest?: ((data: unknown, headers: Record<string, string>) => unknown) | Array<(data: unknown, headers: Record<string, string>) => unknown>;\r\n retry?: RetryStrategy | InlineRetryConfig;\r\n validate?: Validator;\r\n transform?: Transformer;\r\n cache?: { enabled: boolean; ttlMs?: number };\r\n responseType?: ResponseType;\r\n hooks?: RequestHooks;\r\n onUploadProgress?: (event: ProgressEvent) => void;\r\n onDownloadProgress?: (event: ProgressEvent) => void;\r\n /**\r\n * Enable debug logging for this request. Overrides global debug setting.\r\n */\r\n debug?: boolean | 'verbose';\r\n /**\r\n * Custom logger function for this request. Overrides global logger.\r\n */\r\n logger?: (message: string, data?: unknown) => void;\r\n /**\r\n * Transport layer to use for this request.\r\n * Overrides global transport setting.\r\n */\r\n transport?: 'fetch' | 'node' | 'http2' | 'deno' | 'bun' | 'cloudflare';\r\n /**\r\n * Node.js specific transport options for this request.\r\n * Overrides global nodeOptions.\r\n */\r\n nodeOptions?: NodeTransportOptions;\r\n}\r\n\r\n// ============= Pagination =============\r\nexport interface PaginateOptions<T> {\r\n /** Extract items from a response page */\r\n getItems: (data: T) => unknown[];\r\n /** Return the config for the next page, or null to stop */\r\n getNextPage: (data: T, currentConfig: Omit<HttpRequestConfig, 'url' | 'method'>) => Omit<HttpRequestConfig, 'url' | 'method'> | null;\r\n}\r\n\r\n// ============= Polling =============\r\nexport interface PollOptions<T> {\r\n /** Interval between polls in ms */\r\n intervalMs: number;\r\n /** Max number of polls (0 = unlimited) */\r\n maxAttempts?: number;\r\n /** Stop polling when this returns true */\r\n until: (data: T) => boolean;\r\n /** Called on each successful poll */\r\n onPoll?: (data: T, attempt: number) => void;\r\n}\r\n\r\n// ============= HTTP Client Interface (Dependency Inversion) =============\r\nexport interface IHttpClient {\r\n request<T = unknown>(config: HttpRequestConfig): Promise<Result<HttpResponse<T>, HttpErrorDetails>>;\r\n get<T = unknown>(url: string, config?: Omit<HttpRequestConfig, 'url' | 'method'>): Promise<Result<HttpResponse<T>, HttpErrorDetails>>;\r\n post<T = unknown>(url: string, body?: unknown, config?: Omit<HttpRequestConfig, 'url' | 'method' | 'body'>): Promise<Result<HttpResponse<T>, HttpErrorDetails>>;\r\n put<T = unknown>(url: string, body?: unknown, config?: Omit<HttpRequestConfig, 'url' | 'method' | 'body'>): Promise<Result<HttpResponse<T>, HttpErrorDetails>>;\r\n patch<T = unknown>(url: string, body?: unknown, config?: Omit<HttpRequestConfig, 'url' | 'method' | 'body'>): Promise<Result<HttpResponse<T>, HttpErrorDetails>>;\r\n delete<T = unknown>(url: string, config?: Omit<HttpRequestConfig, 'url' | 'method'>): Promise<Result<HttpResponse<T>, HttpErrorDetails>>;\r\n head(url: string, config?: Omit<HttpRequestConfig, 'url' | 'method'>): Promise<Result<HttpResponse<void>, HttpErrorDetails>>;\r\n options(url: string, config?: Omit<HttpRequestConfig, 'url' | 'method'>): Promise<Result<HttpResponse<void>, HttpErrorDetails>>;\r\n addRequestInterceptor(interceptor: RequestInterceptor): Disposer;\r\n addResponseInterceptor(interceptor: ResponseInterceptor): Disposer;\r\n clearInterceptors(): void;\r\n extend(config?: HttpClientConfig): IHttpClient;\r\n paginate<T = unknown>(url: string, options: PaginateOptions<T>, config?: Omit<HttpRequestConfig, 'url' | 'method'>): AsyncIterable<T[]>;\r\n poll<T = unknown>(url: string, options: PollOptions<T>, config?: Omit<HttpRequestConfig, 'url' | 'method'>): Promise<Result<HttpResponse<T>, HttpErrorDetails>>;\r\n cancelAll(): void;\r\n clearCache(): void;\r\n}\r\n\r\n/** Function that removes a previously added interceptor */\r\nexport type Disposer = () => void;\r\n\r\n// ============= Create Client Config =============\r\nexport interface HttpClientConfig {\r\n /**\r\n * Adapter global para todas las requests (firma igual a fetch).\r\n */\r\n adapter?: (input: RequestInfo, init?: RequestInit) => Promise<Response>;\r\n baseURL?: string;\r\n defaultHeaders?: Record<string, string>;\r\n /**\r\n * Controls cookie/credential policy for CORS requests. Same as fetch API.\r\n * 'omit' | 'same-origin' | 'include'\r\n */\r\n credentials?: RequestCredentials;\r\n /**\r\n * Compatibilidad con axios: si es true, establece credentials: 'include'; si es false, credentials: 'same-origin'.\r\n * Si también se especifica credentials, este campo es ignorado.\r\n */\r\n withCredentials?: boolean;\r\n defaultTimeout?: HttpTimeout;\r\n cacheStrategy?: CacheStrategy;\r\n validateStatus?: (status: number) => boolean;\r\n maxConcurrent?: number;\r\n defaultResponseType?: ResponseType;\r\n defaultHooks?: RequestHooks;\r\n /**\r\n * Permite transformar el body antes de serializarlo y enviarlo por defecto en todas las requests.\r\n */\r\n transformRequest?: ((data: unknown, headers: Record<string, string>) => unknown) | Array<(data: unknown, headers: Record<string, string>) => unknown>;\r\n /**\r\n * Si es true (default), convierte automáticamente el body a FormData si detecta archivos.\r\n */\r\n autoFormData?: boolean;\r\n /**\r\n * Enable debug logging for requests/responses. true for basic logs, 'verbose' for detailed logs.\r\n */\r\n debug?: boolean | 'verbose';\r\n /**\r\n * Custom logger function. If provided, replaces the default console.log with custom logging.\r\n */\r\n logger?: (message: string, data?: unknown) => void;\r\n /**\r\n * Transport layer to use for HTTP requests.\r\n * - 'fetch': Uses global fetch API (default)\r\n * - 'node': Uses Node.js http/https modules with HTTP/1.1\r\n * - 'http2': Uses Node.js http2 module (HTTP/2)\r\n * - 'deno': Uses Deno's fetch API (Deno environment)\r\n * - 'bun': Uses Bun's fetch API (Bun environment)\r\n * - 'cloudflare': Uses Cloudflare Workers fetch API\r\n * \r\n * When 'node' or 'http2' is specified, nodeOptions can be used to configure\r\n * keep-alive, connection pooling, and other Node-specific settings.\r\n * \r\n * Note: Node transports are only available in Node.js environments.\r\n * Deno, Bun, and Cloudflare transports use their respective fetch implementations.\r\n */\r\n transport?: 'fetch' | 'node' | 'http2' | 'deno' | 'bun' | 'cloudflare';\r\n /**\r\n * Node.js specific transport options.\r\n * Only applies when transport is 'node' or 'http2'.\r\n */\r\n nodeOptions?: NodeTransportOptions;\r\n}\r\n\r\n// ============= Real-Time Communication =============\r\n\r\n/**\r\n * WebSocket connection options\r\n */\r\nexport interface WebSocketOptions {\r\n /** WebSocket protocols (subprotocols) */\r\n protocols?: string | string[];\r\n /** Headers to send during handshake */\r\n headers?: Record<string, string>;\r\n /** Automatic reconnection settings */\r\n reconnect?: {\r\n /** Enable automatic reconnection (default: true) */\r\n enabled?: boolean;\r\n /** Base delay in ms for exponential backoff (default: 1000) */\r\n baseDelay?: number;\r\n /** Maximum delay in ms (default: 30000) */\r\n maxDelay?: number;\r\n /** Maximum number of reconnect attempts (default: Infinity) */\r\n maxAttempts?: number;\r\n /** Called before each reconnection attempt */\r\n onReconnecting?: (attempt: number, delay: number) => void;\r\n };\r\n /** Timeout for connection establishment in ms (default: 10000) */\r\n timeout?: number;\r\n /** Callback for connection open */\r\n onOpen?: (event: Event) => void;\r\n /** Callback for connection close */\r\n onClose?: (event: CloseEvent) => void;\r\n /** Callback for connection error */\r\n onError?: (event: Event) => void;\r\n /** Enable heartbeat/ping-pong to keep connection alive */\r\n heartbeat?: {\r\n /** Interval in ms to send ping (default: 30000) */\r\n interval?: number;\r\n /** Timeout in ms to wait for pong before closing (default: 5000) */\r\n timeout?: number;\r\n /** Custom ping message (default: 'ping') */\r\n pingMessage?: string | ArrayBuffer | Blob;\r\n /** Custom pong message (default: 'pong') */\r\n pongMessage?: string | ArrayBuffer | Blob;\r\n };\r\n}\r\n\r\n/**\r\n * Server-Sent Events (SSE) connection options\r\n */\r\nexport interface SSEOptions {\r\n /** Headers to send with the request */\r\n headers?: Record<string, string>;\r\n /** Request method (default: GET) */\r\n method?: string;\r\n /** Request body (for POST requests) */\r\n body?: unknown;\r\n /** Whether to send credentials (cookies) (default: same-origin) */\r\n credentials?: RequestCredentials;\r\n /** Timeout for connection establishment in ms (default: 10000) */\r\n timeout?: number;\r\n /** Automatic reconnection settings */\r\n reconnect?: {\r\n /** Enable automatic reconnection (default: true) */\r\n enabled?: boolean;\r\n /** Base delay in ms for exponential backoff (default: 1000) */\r\n baseDelay?: number;\r\n /** Maximum delay in ms (default: 30000) */\r\n maxDelay?: number;\r\n /** Maximum number of reconnect attempts (default: Infinity) */\r\n maxAttempts?: number;\r\n /** Called before each reconnection attempt */\r\n onReconnecting?: (attempt: number, delay: number) => void;\r\n };\r\n /** Callback for connection open */\r\n onOpen?: (event: Event) => void;\r\n /** Callback for connection error */\r\n onError?: (event: Event) => void;\r\n /** Callback for connection close */\r\n onClose?: () => void;\r\n}\r\n\r\n/**\r\n * Real-time message event\r\n */\r\nexport interface RealtimeMessageEvent<T = unknown> {\r\n /** Message data (parsed if possible) */\r\n data: T;\r\n /** Raw message data */\r\n raw: string | ArrayBuffer | Blob;\r\n /** Message type (for WebSocket: 'message', for SSE: event type) */\r\n type: string;\r\n /** Timestamp when message was received */\r\n timestamp: number;\r\n}\r\n\r\n/**\r\n * Real-time client interface\r\n */\r\nexport interface IRealtimeClient {\r\n /** Connect to the server */\r\n connect(): Promise<void>;\r\n /** Disconnect from the server */\r\n disconnect(): void;\r\n /** Send a message */\r\n send(data: string | ArrayBuffer | Blob): void;\r\n /** Subscribe to messages */\r\n onMessage<T = unknown>(callback: (event: RealtimeMessageEvent<T>) => void): () => void;\r\n /** Subscribe to connection open events */\r\n onOpen(callback: (event: Event) => void): () => void;\r\n /** Subscribe to connection close events */\r\n onClose(callback: (event?: CloseEvent) => void): () => void;\r\n /** Subscribe to connection error events */\r\n onError(callback: (event: Event) => void): () => void;\r\n /** Get connection status */\r\n getStatus(): 'connecting' | 'open' | 'closing' | 'closed';\r\n /** Get connection statistics */\r\n getStats(): {\r\n messagesSent: number;\r\n messagesReceived: number;\r\n connectionTime: number;\r\n reconnectAttempts: number;\r\n };\r\n}\r\n\r\n/**\r\n * WebSocket client interface (extends IRealtimeClient)\r\n */\r\nexport interface IWebSocketClient extends IRealtimeClient {\r\n /** WebSocket instance */\r\n readonly socket: WebSocket | null;\r\n /** Send JSON data (automatically serialized) */\r\n sendJson(data: unknown): void;\r\n /** Subscribe to specific message types */\r\n onMessageType<T = unknown>(type: string, callback: (data: T) => void): () => void;\r\n}\r\n\r\n/**\r\n * SSE client interface (extends IRealtimeClient)\r\n */\r\nexport interface ISSEClient extends IRealtimeClient {\r\n /** EventSource instance */\r\n readonly source: EventSource | null;\r\n /** Subscribe to specific event types */\r\n onEvent<T = unknown>(event: string, callback: (data: T) => void): () => void;\r\n /** Last event ID */\r\n readonly lastEventId: string | null;\r\n}\r\n\r\n// ============= Global Environment Declarations =============\r\ndeclare global {\r\n // Deno runtime\r\n interface Deno {\r\n readonly version: {\r\n deno: string;\r\n };\r\n }\r\n const Deno: Deno | undefined;\r\n \r\n // Bun runtime\r\n interface Bun {\r\n readonly version: string;\r\n }\r\n const Bun: Bun | undefined;\r\n \r\n // Cloudflare Workers WebSocketPair\r\n const WebSocketPair: {\r\n new(): { 0: WebSocket; 1: WebSocket };\r\n } | undefined;\r\n}\r\n","/**\r\n * HTTP Client Utilities\r\n * Common validators, transformers, and helpers\r\n */\r\n\r\nimport type { Validator, Transformer, RetryStrategy, HttpErrorDetails, IHttpClient } from '../types';\r\nimport { Ok, Err } from '../types';\r\n\r\n// ============= Validators =============\r\n\r\n/**\r\n * Schema validator using simple checks (can be replaced with Zod, Yup, etc)\r\n */\r\nexport function createSchemaValidator<T>(schema: Record<keyof T, (value: unknown) => boolean>): Validator {\r\n return {\r\n validate(data) {\r\n const obj = data as Record<string, unknown>;\r\n for (const [key, check] of Object.entries(schema)) {\r\n const checkFn = check as (value: unknown) => boolean;\r\n if (!checkFn(obj[key])) {\r\n return Err({\r\n message: `Validation failed: field \"${key}\" is invalid`,\r\n code: 'VALIDATION_ERROR',\r\n });\r\n }\r\n }\r\n return Ok(data);\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Validator that ensures response has required fields\r\n */\r\nexport function createRequiredFieldsValidator(fields: string[]): Validator {\r\n return {\r\n validate(data) {\r\n const obj = data as any;\r\n const missing = fields.filter((field) => !(field in obj));\r\n if (missing.length > 0) {\r\n return Err({\r\n message: `Validation failed: missing fields: ${missing.join(', ')}`,\r\n code: 'VALIDATION_ERROR',\r\n });\r\n }\r\n return Ok(data);\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Validator that ensures response is an array\r\n */\r\nexport const validatorIsArray: Validator = {\r\n validate(data) {\r\n return Array.isArray(data) ? Ok(data) : Err({ message: 'Expected array response', code: 'VALIDATION_ERROR' });\r\n },\r\n};\r\n\r\n/**\r\n * Validator that ensures response is an object\r\n */\r\nexport const validatorIsObject: Validator = {\r\n validate(data) {\r\n return data && typeof data === 'object' && !Array.isArray(data)\r\n ? Ok(data)\r\n : Err({ message: 'Expected object response', code: 'VALIDATION_ERROR' });\r\n },\r\n};\r\n\r\n// ============= Transformers =============\r\n\r\n/**\r\n * Transform that converts snake_case to camelCase (common API pattern)\r\n */\r\nexport const transformSnakeToCamel: Transformer = {\r\n transform(data) {\r\n return transformObject(data, snakeToCamel);\r\n },\r\n};\r\n\r\n/**\r\n * Transform that converts camelCase to snake_case\r\n */\r\nexport const transformCamelToSnake: Transformer = {\r\n transform(data) {\r\n return transformObject(data, camelToSnake);\r\n },\r\n};\r\n\r\n/**\r\n * Transform that flattens nested data\r\n */\r\nexport const transformFlatten: Transformer = {\r\n transform(data) {\r\n return flatten(data);\r\n },\r\n};\r\n\r\n/**\r\n * Transform that picks specific fields (projection)\r\n */\r\nexport function createProjectionTransformer(fields: string[]): Transformer {\r\n return {\r\n transform(data) {\r\n if (Array.isArray(data)) {\r\n return data.map((item) => pickFields(item, fields));\r\n }\r\n return pickFields(data, fields);\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Transform that wraps data in a container\r\n */\r\nexport function createWrapperTransformer(wrapper: string): Transformer {\r\n return {\r\n transform(data) {\r\n return { [wrapper]: data };\r\n },\r\n };\r\n}\r\n\r\n// ============= Retry Strategies =============\r\n\r\n/**\r\n * Aggressive retry: retry all errors up to max attempts\r\n */\r\nexport class AggressiveRetry implements RetryStrategy {\r\n private maxAttempts: number;\r\n\r\n constructor(maxAttempts: number = 5) {\r\n this.maxAttempts = maxAttempts;\r\n }\r\n\r\n shouldRetry(attempt: number): boolean {\r\n return attempt < this.maxAttempts;\r\n }\r\n\r\n delayMs(attempt: number): number {\r\n // Quick retries with minimal backoff\r\n return attempt * 50;\r\n }\r\n}\r\n\r\n/**\r\n * Conservative retry: only retry on specific status codes\r\n */\r\nexport class ConservativeRetry implements RetryStrategy {\r\n private retryableStatuses = [408, 429, 500, 502, 503, 504];\r\n private maxAttempts: number;\r\n\r\n constructor(maxAttempts: number = 3) {\r\n this.maxAttempts = maxAttempts;\r\n }\r\n\r\n shouldRetry(attempt: number, error: HttpErrorDetails): boolean {\r\n if (attempt >= this.maxAttempts) return false;\r\n return this.retryableStatuses.includes(error.status ?? 0) || error.code === 'TIMEOUT';\r\n }\r\n\r\n delayMs(attempt: number): number {\r\n return Math.min(1000 * Math.pow(2, attempt - 1), 10000); // capped at 10s\r\n }\r\n}\r\n\r\n/**\r\n * Circuit breaker pattern: fail fast after threshold\r\n */\r\nexport class CircuitBreakerRetry implements RetryStrategy {\r\n private failureCount = 0;\r\n private lastFailureTime = 0;\r\n private maxAttempts: number;\r\n private failureThreshold: number;\r\n private resetTimeMs: number;\r\n\r\n constructor(maxAttempts: number = 3, failureThreshold: number = 5, resetTimeMs: number = 60000) {\r\n this.maxAttempts = maxAttempts;\r\n this.failureThreshold = failureThreshold;\r\n this.resetTimeMs = resetTimeMs;\r\n }\r\n\r\n shouldRetry(attempt: number): boolean {\r\n if (attempt >= this.maxAttempts) return false;\r\n\r\n // Check if circuit should reset\r\n if (Date.now() - this.lastFailureTime > this.resetTimeMs) {\r\n this.failureCount = 0;\r\n }\r\n\r\n // Open circuit after threshold\r\n if (this.failureCount >= this.failureThreshold) {\r\n return false;\r\n }\r\n\r\n this.failureCount++;\r\n this.lastFailureTime = Date.now();\r\n return true;\r\n }\r\n\r\n delayMs(attempt: number): number {\r\n return 100 * Math.pow(2, attempt - 1);\r\n }\r\n\r\n reset(): void {\r\n this.failureCount = 0;\r\n this.lastFailureTime = 0;\r\n }\r\n}\r\n\r\n// ============= Helper Functions =============\r\n\r\nfunction snakeToCamel(str: string): string {\r\n return str.replace(/_([a-z])/g, (_, char) => char.toUpperCase());\r\n}\r\n\r\nfunction camelToSnake(str: string): string {\r\n return str.replace(/[A-Z]/g, (char) => `_${char.toLowerCase()}`);\r\n}\r\n\r\nfunction transformObject(data: unknown, keyTransform: (key: string) => string): unknown {\r\n if (!data || typeof data !== 'object') return data;\r\n\r\n if (Array.isArray(data)) {\r\n return data.map((item) => transformObject(item, keyTransform));\r\n }\r\n\r\n const transformed: Record<string, unknown> = {};\r\n for (const [key, value] of Object.entries(data as Record<string, unknown>)) {\r\n transformed[keyTransform(key)] = transformObject(value, keyTransform);\r\n }\r\n return transformed;\r\n}\r\n\r\nfunction flatten(data: unknown, prefix = ''): Record<string, unknown> {\r\n const result: Record<string, unknown> = {};\r\n\r\n if (Array.isArray(data)) {\r\n data.forEach((item, index) => {\r\n const key = prefix ? `${prefix}[${index}]` : `[${index}]`;\r\n Object.assign(result, flatten(item, key));\r\n });\r\n } else if (data && typeof data === 'object') {\r\n for (const [key, value] of Object.entries(data as Record<string, unknown>)) {\r\n const flatKey = prefix ? `${prefix}.${key}` : key;\r\n if (value && typeof value === 'object' && !Array.isArray(value)) {\r\n Object.assign(result, flatten(value, flatKey));\r\n } else {\r\n result[flatKey] = value;\r\n }\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\nfunction pickFields(data: unknown, fields: string[]): Record<string, unknown> {\r\n if (!data || typeof data !== 'object') return {};\r\n\r\n const obj = data as Record<string, unknown>;\r\n const result: Record<string, unknown> = {};\r\n\r\n for (const field of fields) {\r\n if (field in obj) {\r\n result[field] = obj[field];\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n// ============= Timeout Utilities =============\r\n\r\n/**\r\n * Creates an AbortController with a timeout.\r\n * Automatically aborts the operation after the specified milliseconds.\r\n * The timer is cleaned up when the signal is aborted (either by timeout or externally).\r\n * @param ms - Timeout in milliseconds\r\n * @returns AbortController that will abort after timeout\r\n */\r\nexport function withTimeout(ms: number): AbortController {\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), ms);\r\n controller.signal.addEventListener('abort', () => clearTimeout(timeoutId), { once: true });\r\n return controller;\r\n}\r\n\r\n/**\r\n * Retry helper function that retries an async operation\r\n * @param fn - Async function to retry\r\n * @param retries - Number of retries (default: 3)\r\n * @returns Result of the function call\r\n */\r\nexport async function retry<T>(fn: () => Promise<T>, retries = 3): Promise<T> {\r\n try {\r\n return await fn();\r\n } catch (err) {\r\n if (retries <= 0) throw err;\r\n return retry(fn, retries - 1);\r\n }\r\n}\r\n\r\n// ============= Advanced Features =============\r\n\r\n// ===== 1. Automatic Cache (React Query Lite) =====\r\n\r\ninterface CacheEntry<T> {\r\n data: T;\r\n timestamp: number;\r\n ttlMs: number;\r\n}\r\n\r\n/**\r\n * Simple cache store with TTL support\r\n */\r\nexport class CacheStore {\r\n private cache: Map<string, CacheEntry<unknown>> = new Map();\r\n\r\n get<T>(key: string): T | null {\r\n const entry = this.cache.get(key) as CacheEntry<T> | undefined;\r\n if (!entry) return null;\r\n\r\n const isExpired = Date.now() - entry.timestamp > entry.ttlMs;\r\n if (isExpired) {\r\n this.cache.delete(key);\r\n return null;\r\n }\r\n\r\n return entry.data;\r\n }\r\n\r\n set<T>(key: string, data: T, ttlMs: number = 60000): void {\r\n this.cache.set(key, { data, timestamp: Date.now(), ttlMs });\r\n }\r\n\r\n clear(): void {\r\n this.cache.clear();\r\n }\r\n\r\n has(key: string): boolean {\r\n const entry = this.cache.get(key) as CacheEntry<unknown> | undefined;\r\n if (!entry) return false;\r\n const isExpired = Date.now() - entry.timestamp > entry.ttlMs;\r\n if (isExpired) {\r\n this.cache.delete(key);\r\n return false;\r\n }\r\n return true;\r\n }\r\n\r\n delete(key: string): void {\r\n this.cache.delete(key);\r\n }\r\n}\r\n\r\n/**\r\n * Cache middleware factory - caches HTTP responses with TTL support\r\n * Automatically skips caching for non-GET requests\r\n */\r\nexport function createCacheMiddleware(options: { cache?: CacheStore; ttlMs?: number; cacheableStatuses?: number[] } = {}): Middleware<HttpContext> {\r\n const cache = options.cache || new CacheStore();\r\n const ttlMs = options.ttlMs || 60000; // Default 1 minute\r\n const cacheableStatuses = options.cacheableStatuses || [200, 304]; // Only cache successful responses\r\n\r\n return async (ctx, next) => {\r\n const method = (ctx.request.method || 'GET').toUpperCase();\r\n const isCacheable = method === 'GET'; // Only cache GET requests\r\n const cacheKey = `${method}:${ctx.request.url}`;\r\n\r\n // Try to serve from cache\r\n if (isCacheable && cache.has(cacheKey)) {\r\n const cachedResponse = cache.get<typeof ctx.response>(cacheKey);\r\n if (cachedResponse) {\r\n ctx.response = cachedResponse;\r\n ctx.state.cacheHit = true;\r\n return;\r\n }\r\n }\r\n\r\n // Proceed to next middleware\r\n await next();\r\n\r\n // Cache successful responses\r\n if (isCacheable && ctx.response && cacheableStatuses.includes(ctx.response.status)) {\r\n cache.set(cacheKey, ctx.response, ttlMs);\r\n ctx.state.cacheMiss = true;\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Pre-configured cache middleware with default 60s TTL\r\n */\r\nexport const cacheMiddleware: Middleware<HttpContext> = createCacheMiddleware();\r\n\r\n// ===== 2. Request Deduplication =====\r\n\r\n/**\r\n * Prevents duplicate requests to the same endpoint\r\n * Shares pending request promises\r\n */\r\nexport class RequestDeduplicator {\r\n private pending: Map<string, Promise<unknown>> = new Map();\r\n\r\n async execute<T>(key: string, fn: () => Promise<T>): Promise<T> {\r\n // If request is already pending, return the existing promise\r\n if (this.pending.has(key)) {\r\n return this.pending.get(key) as Promise<T>;\r\n }\r\n\r\n // Create new request and track it\r\n const promise = fn().finally(() => {\r\n this.pending.delete(key);\r\n });\r\n\r\n this.pending.set(key, promise);\r\n return promise as Promise<T>;\r\n }\r\n\r\n clear(): void {\r\n this.pending.clear();\r\n }\r\n}\r\n\r\n/**\r\n * Deduplication middleware factory - shares pending requests to the same URL\r\n * Prevents duplicate network requests by sharing the same Promise\r\n */\r\nexport function createDedupeMiddleware(options: { deduplicator?: RequestDeduplicator; includeBody?: boolean; methods?: string[] } = {}): Middleware<HttpContext> {\r\n const deduplicator = options.deduplicator || new RequestDeduplicator();\r\n const includeBody = options.includeBody ?? false; // Include body in dedup key for POST/PUT/PATCH\r\n const methods = options.methods || ['GET']; // Methods to deduplicate\r\n\r\n return async (ctx, next) => {\r\n const method = (ctx.request.method || 'GET').toUpperCase();\r\n const shouldDedupe = methods.includes(method);\r\n\r\n if (!shouldDedupe) {\r\n await next();\r\n return;\r\n }\r\n\r\n // Build dedup key\r\n let dedupeKey = `${method}:${ctx.request.url}`;\r\n if (includeBody && ctx.request.body) {\r\n dedupeKey += `:${JSON.stringify(ctx.request.body)}`;\r\n }\r\n\r\n try {\r\n // Use deduplicator to share pending requests\r\n const response = await deduplicator.execute(dedupeKey, async () => {\r\n await next();\r\n return ctx.response;\r\n });\r\n\r\n ctx.response = response;\r\n ctx.state.deduped = true;\r\n } catch (error) {\r\n ctx.error = error;\r\n throw error;\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Pre-configured deduplication middleware for GET requests\r\n */\r\nexport const dedupeMiddleware: Middleware<HttpContext> = createDedupeMiddleware();\r\n\r\n/**\r\n * Rate limiting middleware factory - limits requests per time window\r\n */\r\nexport function createRateLimitMiddleware(options: {\r\n /** Maximum number of requests per window */\r\n maxRequests: number;\r\n /** Time window in milliseconds (default: 60000 = 1 minute) */\r\n windowMs?: number;\r\n /** Function to generate rate limit key (default: uses request URL) */\r\n keyGenerator?: (ctx: HttpContext) => string;\r\n /** Custom error response when limit is exceeded (default: 429 Too Many Requests) */\r\n errorResponse?: { status: number; body: unknown };\r\n} = { maxRequests: 100, windowMs: 60000 }): Middleware<HttpContext> {\r\n const windowMs = options.windowMs ?? 60000;\r\n const maxRequests = options.maxRequests;\r\n const keyGenerator = options.keyGenerator ?? ((ctx) => `${ctx.request.method}:${ctx.request.url}`);\r\n \r\n // Store request counts per key\r\n const requestCounts = new Map<string, { count: number; resetTime: number }>();\r\n \r\n return async (ctx, next) => {\r\n const key = keyGenerator(ctx);\r\n const now = Date.now();\r\n \r\n // Clean up old entries\r\n if (requestCounts.size > 1000) {\r\n for (const [k, entry] of requestCounts.entries()) {\r\n if (now > entry.resetTime) {\r\n requestCounts.delete(k);\r\n }\r\n }\r\n }\r\n \r\n // Get or create entry\r\n let entry = requestCounts.get(key);\r\n if (!entry || now > entry.resetTime) {\r\n entry = { count: 0, resetTime: now + windowMs };\r\n requestCounts.set(key, entry);\r\n }\r\n \r\n // Check limit\r\n if (entry.count >= maxRequests) {\r\n ctx.response = {\r\n status: options.errorResponse?.status ?? 429,\r\n headers: { 'Content-Type': 'application/json' },\r\n body: options.errorResponse?.body ?? { error: 'Too Many Requests', message: 'Rate limit exceeded' },\r\n };\r\n ctx.state.rateLimited = true;\r\n return; // Stop middleware chain\r\n }\r\n \r\n // Increment count and continue\r\n entry.count++;\r\n ctx.state.rateLimit = {\r\n limit: maxRequests,\r\n remaining: maxRequests - entry.count,\r\n reset: entry.resetTime,\r\n };\r\n \r\n await next();\r\n };\r\n}\r\n\r\n/**\r\n * Pre-configured rate limit middleware: 100 requests per minute per endpoint\r\n */\r\nexport const rateLimitMiddleware: Middleware<HttpContext> = createRateLimitMiddleware();\r\n\r\n/**\r\n * Circuit breaker middleware factory - prevents cascading failures\r\n */\r\nexport function createCircuitBreakerMiddleware(options: {\r\n /** Failure threshold to open circuit (default: 5) */\r\n failureThreshold?: number;\r\n /** Time in ms to wait before attempting again (default: 30000) */\r\n resetTimeout?: number;\r\n /** Function to determine if a response is a failure (default: status >= 500) */\r\n isFailure?: (ctx: HttpContext) => boolean;\r\n /** Function to generate circuit key (default: uses request URL) */\r\n keyGenerator?: (ctx: HttpContext) => string;\r\n} = {}): Middleware<HttpContext> {\r\n const failureThreshold = options.failureThreshold ?? 5;\r\n const resetTimeout = options.resetTimeout ?? 30000;\r\n const isFailure = options.isFailure ?? ((ctx) => ctx.response.status >= 500);\r\n const keyGenerator = options.keyGenerator ?? ((ctx) => `${ctx.request.method}:${ctx.request.url}`);\r\n \r\n // Circuit states: 'closed', 'open', 'half-open'\r\n const circuits = new Map<string, {\r\n state: 'closed' | 'open' | 'half-open';\r\n failures: number;\r\n lastFailure: number;\r\n successCount: number;\r\n }>();\r\n \r\n return async (ctx, next) => {\r\n const key = keyGenerator(ctx);\r\n let circuit = circuits.get(key);\r\n \r\n if (!circuit) {\r\n circuit = {\r\n state: 'closed',\r\n failures: 0,\r\n lastFailure: 0,\r\n successCount: 0,\r\n };\r\n circuits.set(key, circuit);\r\n }\r\n \r\n // Check if circuit is open\r\n if (circuit.state === 'open') {\r\n const timeSinceFailure = Date.now() - circuit.lastFailure;\r\n if (timeSinceFailure > resetTimeout) {\r\n // Move to half-open state\r\n circuit.state = 'half-open';\r\n circuit.successCount = 0;\r\n } else {\r\n // Circuit is still open - fail fast\r\n ctx.response = {\r\n status: 503,\r\n headers: { 'Content-Type': 'application/json' },\r\n body: { error: 'Service Unavailable', message: 'Circuit breaker is open' },\r\n };\r\n ctx.state.circuitOpen = true;\r\n return;\r\n }\r\n }\r\n \r\n try {\r\n await next();\r\n \r\n // Check if response is a failure\r\n if (isFailure(ctx)) {\r\n circuit.failures++;\r\n circuit.lastFailure = Date.now();\r\n \r\n if (circuit.failures >= failureThreshold) {\r\n circuit.state = 'open';\r\n } else if (circuit.state === 'half-open') {\r\n // Half-open circuit failed, reopen\r\n circuit.state = 'open';\r\n }\r\n } else {\r\n // Request succeeded\r\n circuit.failures = 0;\r\n if (circuit.state === 'half-open') {\r\n circuit.successCount++;\r\n if (circuit.successCount >= 3) {\r\n // Enough successes, close the circuit\r\n circuit.state = 'closed';\r\n circuit.successCount = 0;\r\n }\r\n }\r\n }\r\n } catch (error) {\r\n // Request error (network, timeout, etc.)\r\n circuit.failures++;\r\n circuit.lastFailure = Date.now();\r\n \r\n if (circuit.failures >= failureThreshold) {\r\n circuit.state = 'open';\r\n } else if (circuit.state === 'half-open') {\r\n circuit.state = 'open';\r\n }\r\n \r\n throw error; // Re-throw for other middleware to handle\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Pre-configured circuit breaker middleware\r\n */\r\nexport const circuitBreakerMiddleware: Middleware<HttpContext> = createCircuitBreakerMiddleware();\r\n\r\n// ===== 3. Middleware Pipeline =====\r\n\r\n/**\r\n * HTTP Context passed through middleware chain\r\n */\r\nexport interface HttpContext {\r\n request: {\r\n method: string;\r\n url: string;\r\n headers: Record<string, string>;\r\n body?: unknown;\r\n };\r\n response: {\r\n status: number;\r\n headers: Record<string, string>;\r\n body?: unknown;\r\n };\r\n state: Record<string, unknown>;\r\n error?: unknown;\r\n}\r\n\r\n/**\r\n * Middleware function type with Express/Koa-like pattern\r\n * Receives context and next() function to proceed through pipeline\r\n */\r\nexport type Middleware<T extends HttpContext = HttpContext> = (\r\n ctx: T,\r\n next: () => Promise<void>,\r\n) => Promise<void>;\r\n\r\n/**\r\n * Create a middleware pipeline executor with proper sequencing\r\n * Prevents multiple next() calls and ensures proper error propagation\r\n */\r\nexport function createPipeline<T extends HttpContext = HttpContext>(middlewares: Middleware<T>[]) {\r\n return async (ctx: T): Promise<void> => {\r\n let index = -1;\r\n\r\n async function dispatch(i: number): Promise<void> {\r\n if (i <= index) {\r\n throw new Error('next() called multiple times');\r\n }\r\n index = i;\r\n\r\n const fn = middlewares[i];\r\n if (fn) {\r\n await fn(ctx, () => dispatch(i + 1));\r\n }\r\n }\r\n\r\n await dispatch(0);\r\n };\r\n}\r\n\r\n/**\r\n * Legacy: MiddlewarePipeline class (backwards compatible)\r\n * For simpler use cases, kept for compatibility\r\n */\r\nexport class MiddlewarePipeline<T = unknown> {\r\n private middlewares: Array<\r\n | Middleware<T extends HttpContext ? T : HttpContext>\r\n | ((data: T) => T | Promise<T>)\r\n > = [];\r\n\r\n use(\r\n middleware:\r\n | Middleware<T extends HttpContext ? T : HttpContext>\r\n | ((data: T) => T | Promise<T>),\r\n ): this {\r\n this.middlewares.push(middleware);\r\n return this;\r\n }\r\n\r\n async execute(data: T): Promise<T> {\r\n // If data looks like HttpContext, use pipeline pattern\r\n if (data && typeof data === 'object' && 'request' in data && 'response' in data) {\r\n const ctx = data as unknown as HttpContext;\r\n const pipeline = createPipeline(\r\n this.middlewares.map((mw) => {\r\n if (typeof mw === 'function' && mw.length === 2) {\r\n return mw as Middleware;\r\n }\r\n // Convert simple transformer to middleware\r\n return async (ctx: HttpContext, next: () => Promise<void>) => {\r\n const transform = mw as (data: T) => T | Promise<T>;\r\n ctx.response.body = await transform(ctx.response.body as T);\r\n await next();\r\n };\r\n }),\r\n );\r\n await pipeline(ctx);\r\n return ctx.response.body as T;\r\n }\r\n\r\n // Fallback: simple data transformation pipeline\r\n let result = data;\r\n for (const mw of this.middlewares) {\r\n const transform = mw as (data: T) => T | Promise<T>;\r\n result = await transform(result);\r\n }\r\n return result;\r\n }\r\n\r\n clear(): void {\r\n this.middlewares = [];\r\n }\r\n}\r\n\r\n// ===== 4. Advanced Typed Generics =====\r\n\r\n/**\r\n * Type-safe response wrapper with automatic type inference\r\n */\r\nexport interface TypedResponse<T, U = unknown> {\r\n ok: boolean;\r\n data?: T;\r\n error?: U;\r\n status: number;\r\n headers: Record<string, string>;\r\n}\r\n\r\n/**\r\n * Create a typed response builder\r\n */\r\nexport function createTypedResponse<T, U = unknown>(\r\n status: number,\r\n data?: T,\r\n error?: U,\r\n headers: Record<string, string> = {},\r\n): TypedResponse<T, U> {\r\n return {\r\n ok: status >= 200 && status < 300,\r\n data,\r\n error,\r\n status,\r\n headers,\r\n };\r\n}\r\n\r\n/**\r\n * API Endpoint definition with typed request/response\r\n */\r\nexport interface ApiEndpoint<TRequest = unknown, TResponse = unknown> {\r\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\r\n path: string;\r\n request?: TRequest;\r\n response: TResponse;\r\n}\r\n\r\n/**\r\n * Create a strongly-typed API client method\r\n */\r\nexport function createTypedRequest<TRequest, TResponse>(\r\n endpoint: ApiEndpoint<TRequest, TResponse>,\r\n): (client: IHttpClient, req?: TRequest) => Promise<TResponse> {\r\n return async (client, req?) => {\r\n const url = endpoint.path;\r\n let response: unknown;\r\n\r\n switch (endpoint.method) {\r\n case 'GET':\r\n response = await client.get(url);\r\n break;\r\n case 'POST':\r\n response = await client.post(url, req);\r\n break;\r\n case 'PUT':\r\n response = await client.put(url, req);\r\n break;\r\n case 'PATCH':\r\n response = await client.patch(url, req);\r\n break;\r\n case 'DELETE':\r\n response = await client.delete(url);\r\n break;\r\n default:\r\n throw new Error(`Unsupported method: ${endpoint.method}`);\r\n }\r\n\r\n return response as TResponse;\r\n };\r\n}\r\n\r\n/**\r\n * API Schema - maps multiple endpoints with automatic type inference\r\n */\r\nexport type ApiSchema = Record<string, ApiEndpoint>;\r\n\r\n/**\r\n * Create a typed API client from schema\r\n */\r\nexport function createTypedApiClient<T extends ApiSchema>(schema: T) {\r\n return {\r\n request: async <K extends keyof T>(\r\n client: IHttpClient,\r\n endpoint: K,\r\n data?: T[K] extends ApiEndpoint<infer TReq, any> ? TReq : never,\r\n ): Promise<T[K] extends ApiEndpoint<any, infer TRes> ? TRes : never> => {\r\n const ep = schema[endpoint] as ApiEndpoint;\r\n const requester = createTypedRequest(ep);\r\n return (await requester(client, data)) as T[K] extends ApiEndpoint<any, infer TRes> ? TRes : never;\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Observable-like response wrapper for reactive patterns\r\n */\r\nexport class TypedObservable<T> {\r\n private subscribers: Array<(value: T) => void> = [];\r\n private errorSubscribers: Array<(error: unknown) => void> = [];\r\n private completeSubscribers: Array<() => void> = [];\r\n\r\n subscribe(\r\n next?: (value: T) => void,\r\n error?: (err: unknown) => void,\r\n complete?: () => void,\r\n ): { unsubscribe: () => void } {\r\n if (next) this.subscribers.push(next);\r\n if (error) this.errorSubscribers.push(error);\r\n if (complete) this.completeSubscribers.push(complete);\r\n\r\n return {\r\n unsubscribe: () => {\r\n this.subscribers = this.subscribers.filter((s) => s !== next);\r\n this.errorSubscribers = this.errorSubscribers.filter((e) => e !== error);\r\n this.completeSubscribers = this.completeSubscribers.filter((c) => c !== complete);\r\n },\r\n };\r\n }\r\n\r\n next(value: T): void {\r\n this.subscribers.forEach((s) => s(value));\r\n }\r\n\r\n error(err: unknown): void {\r\n this.errorSubscribers.forEach((e) => e(err));\r\n }\r\n\r\n complete(): void {\r\n this.completeSubscribers.forEach((c) => c());\r\n }\r\n\r\n map<U>(fn: (value: T) => U): TypedObservable<U> {\r\n const obs = new TypedObservable<U>();\r\n this.subscribe(\r\n (value) => obs.next(fn(value)),\r\n (err) => obs.error(err),\r\n () => obs.complete(),\r\n );\r\n return obs;\r\n }\r\n\r\n filter(predicate: (value: T) => boolean): TypedObservable<T> {\r\n const obs = new TypedObservable<T>();\r\n this.subscribe(\r\n (value) => {\r\n if (predicate(value)) obs.next(value);\r\n },\r\n (err) => obs.error(err),\r\n () => obs.complete(),\r\n );\r\n return obs;\r\n }\r\n}\r\n\r\n/**\r\n * Union type helper - extracts success/error types\r\n */\r\nexport type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;\r\n\r\n/**\r\n * Result type extractor for discriminated unions\r\n */\r\nexport type ResultOf<T extends { ok: boolean }> = T extends { ok: true } ? Omit<T, 'ok'> : T extends { ok: false } ? Omit<T, 'ok'> : never;\r\n\r\n/**\r\n * Guard function for typing - validates data is T at runtime\r\n */\r\nexport function createTypeGuard<T>(\r\n check: (value: unknown) => value is T,\r\n): (value: unknown) => T {\r\n return (value) => {\r\n if (!check(value)) {\r\n throw new TypeError(`Value does not match expected type`);\r\n }\r\n return value;\r\n };\r\n}\r\n\r\n/**\r\n * Branded types for URL safety\r\n */\r\nexport type Url<T extends string = string> = string & { readonly __url: unique symbol; readonly __type: T };\r\nexport type ApiUrl = Url<'api'>;\r\nexport type FileUrl = Url<'file'>;\r\n\r\nexport function createUrl<T extends string = string>(url: string): Url<T> {\r\n return url as Url<T>;\r\n}\r\n\r\nexport function createApiUrl(path: string): ApiUrl {\r\n return createUrl<'api'>(path);\r\n}\r\n\r\n/**\r\n * Defer pattern for lazy evaluation\r\n */\r\nexport class Defer<T> {\r\n private _promise: Promise<T>;\r\n private resolveFunc!: (value: T) => void;\r\n private rejectFunc!: (reason?: unknown) => void;\r\n\r\n constructor() {\r\n this._promise = new Promise((resolve, reject) => {\r\n this.resolveFunc = resolve;\r\n this.rejectFunc = reject;\r\n });\r\n }\r\n\r\n resolve(value: T): void {\r\n this.resolveFunc(value);\r\n }\r\n\r\n reject(reason?: unknown): void {\r\n this.rejectFunc(reason);\r\n }\r\n\r\n get promise(): Promise<T> {\r\n return this._promise;\r\n }\r\n\r\n /**\r\n * @deprecated Use `defer.promise` getter instead\r\n */\r\n promise_(): Promise<T> {\r\n return this._promise;\r\n }\r\n}\r\n\r\n// ===== 5. Streaming Support =====\r\n\r\n/**\r\n * Streaming response handler for large files/streams\r\n */\r\nexport interface StreamOptions {\r\n chunkSize?: number;\r\n onChunk?: (chunk: Uint8Array) => void | Promise<void>;\r\n onProgress?: (loaded: number, total: number) => void;\r\n}\r\n\r\n/**\r\n * Handle streaming responses\r\n */\r\nexport async function handleStream(\r\n response: Response,\r\n options: StreamOptions = {},\r\n): Promise<Uint8Array> {\r\n const reader = response.body?.getReader();\r\n if (!reader) throw new Error('Response body is not readable');\r\n\r\n const chunks: Uint8Array[] = [];\r\n let loaded = 0;\r\n const total = parseInt(response.headers.get('content-length') || '0', 10);\r\n\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n\r\n chunks.push(value);\r\n loaded += value.length;\r\n\r\n if (options.onChunk) {\r\n await options.onChunk(value);\r\n }\r\n\r\n if (options.onProgress && total > 0) {\r\n options.onProgress(loaded, total);\r\n }\r\n }\r\n\r\n return concatUint8Arrays(chunks, loaded);\r\n}\r\n\r\n/**\r\n * Efficiently concatenate Uint8Array chunks into a single array\r\n */\r\nfunction concatUint8Arrays(chunks: Uint8Array[], totalLength: number): Uint8Array {\r\n const result = new Uint8Array(totalLength);\r\n let offset = 0;\r\n for (const chunk of chunks) {\r\n result.set(chunk, offset);\r\n offset += chunk.byteLength;\r\n }\r\n return result;\r\n}\r\n\r\n/**\r\n * Stream to file (Node.js compatible)\r\n */\r\nexport async function streamToFile(\r\n response: Response,\r\n filePath: string,\r\n): Promise<void> {\r\n const data = await handleStream(response);\r\n\r\n // For browser, you'd use Blob\r\n // For Node.js, you'd use fs.writeFile\r\n if (typeof window === 'undefined') {\r\n // Node.js environment\r\n // Security: Dynamically import Node.js built-in 'fs' module for file operations\r\n const fs = await import('fs').then((m) => m.promises);\r\n await fs.writeFile(filePath, data);\r\n } else {\r\n // Browser: create blob and download\r\n const blob = new Blob([data.buffer as BlobPart], { type: 'application/octet-stream' });\r\n const url = URL.createObjectURL(blob);\r\n const a = document.createElement('a');\r\n a.href = url;\r\n a.download = filePath;\r\n a.click();\r\n URL.revokeObjectURL(url);\r\n }\r\n}\r\n\r\n/**\r\n * Streaming middleware factory - handles streaming responses with progress tracking\r\n * Useful for large file downloads, real-time data streaming, etc.\r\n */\r\nexport function createStreamingMiddleware(options: { onChunk?: (chunk: Uint8Array) => void | Promise<void>; onProgress?: (loaded: number, total: number) => void } = {}): Middleware<HttpContext> {\r\n return async (ctx, next) => {\r\n await next();\r\n\r\n // Check if response has a body to stream\r\n if (ctx.response && ctx.response.body && typeof ctx.response.body === 'object' && 'getReader' in ctx.response.body) {\r\n const reader = (ctx.response.body as ReadableStream<Uint8Array>).getReader();\r\n const chunks: Uint8Array[] = [];\r\n let loaded = 0;\r\n const total = parseInt((ctx.response.headers?.['content-length'] as string) || '0', 10);\r\n\r\n try {\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n\r\n chunks.push(value);\r\n loaded += value.length;\r\n\r\n if (options.onChunk) {\r\n await options.onChunk(value);\r\n }\r\n\r\n if (options.onProgress && total > 0) {\r\n options.onProgress(loaded, total);\r\n }\r\n\r\n ctx.state.streamedChunks = (ctx.state.streamedChunks as Uint8Array[]) || [];\r\n (ctx.state.streamedChunks as Uint8Array[]).push(value);\r\n }\r\n\r\n // Combine all chunks into response body\r\n const resultData = concatUint8Arrays(chunks, loaded);\r\n ctx.response.body = resultData;\r\n ctx.state.streaming = true;\r\n ctx.state.streamedBytes = loaded;\r\n } finally {\r\n reader.releaseLock();\r\n }\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Pre-configured streaming middleware with default progress tracking\r\n */\r\nexport const streamingMiddleware: Middleware<HttpContext> = createStreamingMiddleware({\r\n onProgress: (loaded, total) => {\r\n if (total > 0) {\r\n const percent = Math.round((loaded / total) * 100);\r\n console.log(`⬇️ Streaming: ${percent}% (${loaded}/${total} bytes)`);\r\n }\r\n },\r\n});\r\n\r\n// ===== 6. Plugins System =====\r\n\r\n/**\r\n * Plugin interface - plugins can extend HttpClient functionality\r\n */\r\nexport interface Plugin {\r\n name: string;\r\n setup(manager: PluginManager): void | Promise<void>;\r\n}\r\n\r\n/**\r\n * Plugin manager for extensible architecture\r\n * Integrates cache, deduplication, and middleware\r\n */\r\nexport class PluginManager {\r\n private plugins: Plugin[] = [];\r\n private cache: CacheStore = new CacheStore();\r\n private deduplicator: RequestDeduplicator = new RequestDeduplicator();\r\n private middlewares: Middleware<HttpContext>[] = [];\r\n private listeners: Map<string, Set<(...args: unknown[]) => void>> = new Map();\r\n\r\n /**\r\n * Register a plugin and call its setup method\r\n */\r\n register(plugin: Plugin): this {\r\n this.plugins.push(plugin);\r\n void plugin.setup(this);\r\n this.emit('plugin:registered', plugin.name);\r\n return this;\r\n }\r\n\r\n /**\r\n * Add middleware to the pipeline\r\n */\r\n addMiddleware(middleware: Middleware<HttpContext>): this {\r\n this.middlewares.push(middleware);\r\n return this;\r\n }\r\n\r\n /**\r\n * Get cache store\r\n */\r\n getCache(): CacheStore {\r\n return this.cache;\r\n }\r\n\r\n /**\r\n * Get request deduplicator\r\n */\r\n getDeduplicator(): RequestDeduplicator {\r\n return this.deduplicator;\r\n }\r\n\r\n /**\r\n * Get middleware pipeline executor\r\n */\r\n getPipeline(): (ctx: HttpContext) => Promise<void> {\r\n return createPipeline(this.middlewares);\r\n }\r\n\r\n /**\r\n * Execute middleware pipeline for a context\r\n */\r\n async executePipeline(ctx: HttpContext): Promise<void> {\r\n const pipeline = this.getPipeline();\r\n await pipeline(ctx);\r\n }\r\n\r\n /**\r\n * Register event listener\r\n */\r\n on(event: string, handler: (...args: unknown[]) => void | Promise<void>): this {\r\n if (!this.listeners.has(event)) {\r\n this.listeners.set(event, new Set());\r\n }\r\n this.listeners.get(event)?.add(handler);\r\n return this;\r\n }\r\n\r\n /**\r\n * Emit event to all listeners\r\n */\r\n emit(event: string, ...args: unknown[]): void {\r\n const handlers = this.listeners.get(event);\r\n if (handlers) {\r\n for (const handler of handlers) {\r\n void handler(...args);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Unregister event listener\r\n */\r\n off(event: string, handler: (...args: unknown[]) => void): this {\r\n this.listeners.get(event)?.delete(handler);\r\n return this;\r\n }\r\n\r\n /**\r\n * Get all registered plugins\r\n */\r\n getPlugins(): Plugin[] {\r\n return [...this.plugins];\r\n }\r\n\r\n /**\r\n * Clear all plugins, middlewares, and listeners\r\n */\r\n clear(): void {\r\n this.plugins = [];\r\n this.middlewares = [];\r\n this.listeners.clear();\r\n this.cache.clear();\r\n this.deduplicator.clear();\r\n }\r\n}\r\n\r\n// ===== Example Plugins =====\r\n\r\n/**\r\n * Example: Logger plugin - logs HTTP requests and responses\r\n */\r\nexport const LoggerPlugin: Plugin = {\r\n name: 'logger',\r\n setup(manager: PluginManager) {\r\n manager.on('request:start', (...args: unknown[]) => {\r\n const url = args[0] as string;\r\n console.log(`📤 Request started: ${url}`);\r\n });\r\n manager.on('request:success', (...args: unknown[]) => {\r\n const url = args[0] as string;\r\n const status = args[1] as number;\r\n console.log(`✅ Request succeeded: ${url} (${status})`);\r\n });\r\n manager.on('request:error', (...args: unknown[]) => {\r\n const url = args[0] as string;\r\n const error = args[1] as unknown;\r\n console.error(`❌ Request failed: ${url}`, error);\r\n });\r\n },\r\n};\r\n\r\n/**\r\n * Example: Metrics plugin - collects request metrics\r\n */\r\nexport class MetricsPlugin implements Plugin {\r\n name = 'metrics';\r\n private metrics = {\r\n requests: 0,\r\n errors: 0,\r\n totalTime: 0,\r\n avgTime: 0,\r\n };\r\n\r\n setup(manager: PluginManager): void {\r\n manager.on('request:complete', (...args: unknown[]) => {\r\n const duration = args[0] as number;\r\n const success = args[1] as boolean;\r\n this.metrics.requests++;\r\n this.metrics.totalTime += duration;\r\n this.metrics.avgTime = this.metrics.totalTime / this.metrics.requests;\r\n if (!success) {\r\n this.metrics.errors++;\r\n }\r\n });\r\n }\r\n\r\n getMetrics() {\r\n return { ...this.metrics };\r\n }\r\n}\r\n\r\n/**\r\n * Example: Cache plugin - automatically adds caching middleware\r\n */\r\nexport class CachePlugin implements Plugin {\r\n name = 'cache';\r\n ttlMs: number;\r\n\r\n constructor(ttlMs: number = 60000) {\r\n this.ttlMs = ttlMs;\r\n }\r\n\r\n setup(manager: PluginManager): void {\r\n manager.addMiddleware(createCacheMiddleware({ ttlMs: this.ttlMs }));\r\n }\r\n}\r\n\r\n/**\r\n * Example: Deduplication plugin - prevents duplicate requests\r\n */\r\nexport class DedupePlugin implements Plugin {\r\n name = 'dedupe';\r\n\r\n setup(manager: PluginManager): void {\r\n manager.addMiddleware(createDedupeMiddleware());\r\n }\r\n}\r\n\r\n/**\r\n * Rate limiting plugin - adds rate limiting middleware\r\n */\r\nexport class RateLimitPlugin implements Plugin {\r\n name = 'rate-limit';\r\n private options: {\r\n maxRequests?: number;\r\n windowMs?: number;\r\n keyGenerator?: (ctx: HttpContext) => string;\r\n errorResponse?: { status: number; body: unknown };\r\n };\r\n \r\n constructor(options: {\r\n maxRequests?: number;\r\n windowMs?: number;\r\n keyGenerator?: (ctx: HttpContext) => string;\r\n errorResponse?: { status: number; body: unknown };\r\n } = {}) {\r\n this.options = options;\r\n }\r\n \r\n setup(manager: PluginManager): void {\r\n manager.addMiddleware(createRateLimitMiddleware({\r\n maxRequests: this.options.maxRequests ?? 100,\r\n windowMs: this.options.windowMs ?? 60000,\r\n keyGenerator: this.options.keyGenerator,\r\n errorResponse: this.options.errorResponse,\r\n }));\r\n }\r\n}\r\n\r\n/**\r\n * Circuit breaker plugin - adds circuit breaker middleware\r\n */\r\nexport class CircuitBreakerPlugin implements Plugin {\r\n name = 'circuit-breaker';\r\n private options: {\r\n failureThreshold?: number;\r\n resetTimeout?: number;\r\n isFailure?: (ctx: HttpContext) => boolean;\r\n keyGenerator?: (ctx: HttpContext) => string;\r\n };\r\n \r\n constructor(options: {\r\n failureThreshold?: number;\r\n resetTimeout?: number;\r\n isFailure?: (ctx: HttpContext) => boolean;\r\n keyGenerator?: (ctx: HttpContext) => string;\r\n } = {}) {\r\n this.options = options;\r\n }\r\n \r\n setup(manager: PluginManager): void {\r\n manager.addMiddleware(createCircuitBreakerMiddleware({\r\n failureThreshold: this.options.failureThreshold ?? 5,\r\n resetTimeout: this.options.resetTimeout ?? 30000,\r\n isFailure: this.options.isFailure,\r\n keyGenerator: this.options.keyGenerator,\r\n }));\r\n }\r\n}\r\n\r\n// Re-export type for convenience\r\nexport type { HttpErrorDetails };\r\n","/**\n * Node.js HTTP/1.1 adapter using native http/https modules.\n * Supports keep-alive, connection pooling, and other Node-specific options.\n */\n\nimport type { NodeTransportOptions } from '../types/index.js';\n\n// Lazy load Node.js modules\nlet http: typeof import('http') | undefined;\nlet https: typeof import('https') | undefined;\nlet http2: typeof import('http2') | undefined;\n\n// HTTP/2 Session Pool\ninterface Http2SessionInfo {\n session: import('http2').ClientHttp2Session;\n lastUsed: number;\n requestCount: number;\n origin: string;\n closing?: boolean;\n}\n\nclass Http2SessionPool {\n private sessions = new Map<string, Http2SessionInfo>();\n private cleanupInterval: NodeJS.Timeout | null = null;\n private readonly maxIdleTime = 30000; // 30 seconds idle timeout\n private readonly maxRequestsPerSession = 1000; // Max requests per session before recycling\n\n constructor() {\n this.startCleanup();\n }\n\n private startCleanup() {\n if (this.cleanupInterval) return;\n this.cleanupInterval = setInterval(() => this.cleanup(), 10000); // Check every 10 seconds\n }\n\n private cleanup() {\n const now = Date.now();\n for (const info of this.sessions.values()) {\n // Close idle sessions\n if (!info.closing && now - info.lastUsed > this.maxIdleTime) {\n this.closeSession(info, 'idle timeout');\n }\n // Close sessions that have exceeded request count\n if (!info.closing && info.requestCount >= this.maxRequestsPerSession) {\n this.closeSession(info, 'max requests exceeded');\n }\n }\n }\n\n private closeSession(info: Http2SessionInfo, _reason: string) {\n info.closing = true;\n info.session.close();\n this.sessions.delete(info.origin);\n }\n\n async getSession(origin: string, options?: NodeTransportOptions): Promise<import('http2').ClientHttp2Session> {\n // Check for existing session\n let info = this.sessions.get(origin);\n \n if (info && !info.session.closed && !info.session.destroyed) {\n info.lastUsed = Date.now();\n info.requestCount++;\n return info.session;\n }\n\n // Create new session\n const http2Module = await getHttp2();\n const session = http2Module.connect(origin, {\n settings: options?.http2Settings,\n });\n\n // Set up error handling for the session\n session.on('error', (_err) => {\n // Remove broken session from pool\n this.sessions.delete(origin);\n });\n\n session.on('close', () => {\n this.sessions.delete(origin);\n });\n\n info = {\n session,\n lastUsed: Date.now(),\n requestCount: 1,\n origin,\n closing: false,\n };\n\n this.sessions.set(origin, info);\n return session;\n }\n\n releaseSession(origin: string) {\n const info = this.sessions.get(origin);\n if (info) {\n info.lastUsed = Date.now();\n }\n }\n\n getStats() {\n return {\n sessionCount: this.sessions.size,\n origins: Array.from(this.sessions.keys()),\n sessions: Array.from(this.sessions.values()).map(info => ({\n origin: info.origin,\n requestCount: info.requestCount,\n lastUsed: info.lastUsed,\n closing: info.closing,\n sessionAlive: !info.session.closed && !info.session.destroyed,\n })),\n };\n }\n\n closeAll() {\n for (const info of this.sessions.values()) {\n if (!info.closing) {\n info.session.close();\n }\n }\n this.sessions.clear();\n if (this.cleanupInterval) {\n clearInterval(this.cleanupInterval);\n this.cleanupInterval = null;\n }\n }\n}\n\n// Global session pool instance\nconst http2SessionPool = new Http2SessionPool();\n\n/**\n * Dynamically import Node.js built-in modules for HTTP/HTTPS.\n * Security: These are core Node.js modules, not third-party dependencies.\n */\nasync function getHttp() {\n if (!http) {\n http = await import('http');\n https = await import('https');\n }\n return { http: http!, https: https! };\n}\n\n/**\n * Dynamically import Node.js built-in module for HTTP/2.\n * Security: This is a core Node.js module, not a third-party dependency.\n */\nasync function getHttp2() {\n if (!http2) {\n http2 = await import('http2');\n }\n return http2!;\n}\n\n/**\n * Extract request information from RequestInfo and RequestInit\n */\nfunction extractRequestInfo(input: RequestInfo, init?: RequestInit): {\n url: string;\n method: string;\n headers: Record<string, string>;\n body?: unknown;\n signal?: AbortSignal;\n} {\n let url: string;\n let method: string;\n let headers: Record<string, string>;\n let body: unknown | undefined;\n let signal: AbortSignal | undefined;\n\n if (typeof input === 'string') {\n url = input;\n method = init?.method || 'GET';\n headers = (init?.headers as Record<string, string>) || {};\n body = init?.body;\n signal = init?.signal !== null ? init?.signal : undefined;\n } else {\n // input is a Request object\n url = input.url;\n method = init?.method || input.method || 'GET';\n // Merge headers: Request headers take precedence? Actually, init overrides request.\n const inputHeaders = new Headers(input.headers);\n const initHeaders = new Headers(init?.headers);\n const mergedHeaders = new Headers(inputHeaders);\n initHeaders.forEach((value, key) => mergedHeaders.set(key, value));\n headers = Object.fromEntries(mergedHeaders.entries());\n body = init?.body ?? input.body ?? undefined;\n signal = init?.signal !== null ? init?.signal : input.signal;\n }\n\n return { url, method, headers, body, signal };\n}\n\n/**\n * Create agent with keep-alive configuration\n */\nfunction createAgent(\n url: string,\n http: typeof import('http'),\n https: typeof import('https'),\n options?: NodeTransportOptions,\n isHttp2: boolean = false\n): any {\n if (isHttp2) {\n // HTTP/2 session management is more complex - for simplicity we don't pool here\n return null;\n }\n \n // For HTTP/1.1, create a custom agent\n const Agent = url.startsWith('https:') ? https.Agent : http.Agent;\n const agentOptions: any = {\n keepAlive: options?.keepAlive ?? true,\n maxSockets: options?.maxSockets ?? 50,\n maxFreeSockets: options?.maxFreeSockets ?? 10,\n timeout: options?.timeout ?? 60000,\n };\n if (options?.maxRequestsPerSocket !== undefined) {\n agentOptions.maxRequestsPerSocket = options.maxRequestsPerSocket;\n }\n return new Agent(agentOptions);\n}\n\n/**\n * Convert Node.js IncomingMessage to Fetch Response\n */\nasync function createResponse(nodeRes: import('http').IncomingMessage): Promise<Response> {\n const headers = new Headers();\n for (const [key, value] of Object.entries(nodeRes.headers)) {\n if (Array.isArray(value)) {\n value.forEach(v => headers.append(key, v));\n } else if (value !== undefined) {\n headers.set(key, String(value));\n }\n }\n\n // Collect body chunks\n const chunks: Buffer[] = [];\n nodeRes.on('data', (chunk) => chunks.push(chunk));\n \n return new Promise<Response>((resolve) => {\n nodeRes.on('end', () => {\n const body = Buffer.concat(chunks);\n resolve(new Response(body, {\n status: nodeRes.statusCode || 200,\n statusText: nodeRes.statusMessage || 'OK',\n headers,\n }));\n });\n });\n}\n\n/**\n * Node.js HTTP/1.1 adapter\n */\nexport async function nodeHttpAdapter(\n input: RequestInfo,\n init?: RequestInit,\n options?: NodeTransportOptions\n): Promise<Response> {\n const { http, https } = await getHttp();\n const { url, method, headers, body, signal } = extractRequestInfo(input, init);\n const parsedUrl = new URL(url);\n const isHttps = parsedUrl.protocol === 'https:';\n const module = isHttps ? https : http;\n \n const agent = createAgent(url, http, https, options, false);\n \n return new Promise((resolve, reject) => {\n const req = module.request({\n hostname: parsedUrl.hostname,\n port: parsedUrl.port || (isHttps ? 443 : 80),\n path: parsedUrl.pathname + parsedUrl.search,\n method,\n headers,\n agent,\n });\n\n // Handle abort signal\n if (signal) {\n if (signal.aborted) {\n req.destroy();\n reject(new Error('Request aborted'));\n return;\n }\n const onAbort = () => {\n req.destroy();\n reject(new Error('Request aborted'));\n };\n signal.addEventListener('abort', onAbort);\n // Clean up listener when request completes or errors\n const cleanup = () => signal.removeEventListener('abort', onAbort);\n req.on('close', cleanup);\n req.on('error', cleanup);\n }\n \n req.setTimeout(options?.timeout ?? 60000, () => {\n req.destroy();\n reject(new Error('Request timed out'));\n });\n \n req.on('response', async (res) => {\n try {\n const response = await createResponse(res);\n resolve(response);\n } catch (error) {\n reject(error);\n }\n });\n \n req.on('error', reject);\n \n // Write body if present\n if (body) {\n if (typeof body === 'string') {\n req.write(body);\n } else if (body instanceof Uint8Array) {\n req.write(Buffer.from(body));\n } else if (Buffer.isBuffer(body)) {\n req.write(body);\n } else if (typeof body === 'object') {\n req.write(JSON.stringify(body));\n }\n // Note: ReadableStream not supported in this simple adapter\n }\n \n req.end();\n });\n}\n\n/**\n * Node.js HTTP/2 adapter (basic implementation)\n */\nexport async function nodeHttp2Adapter(\n input: RequestInfo,\n init?: RequestInit,\n options?: NodeTransportOptions\n): Promise<Response> {\n const { url, method, headers, body, signal } = extractRequestInfo(input, init);\n const parsedUrl = new URL(url);\n const origin = parsedUrl.origin;\n \n return new Promise(async (resolve, reject) => {\n let session: import('http2').ClientHttp2Session;\n let req: import('http2').ClientHttp2Stream;\n let released = false;\n\n const releaseSession = () => {\n if (!released) {\n released = true;\n http2SessionPool.releaseSession(origin);\n }\n };\n\n const cleanupAndReject = (error: Error) => {\n releaseSession();\n reject(error);\n };\n\n const cleanupAndResolve = (response: Response) => {\n releaseSession();\n resolve(response);\n };\n\n try {\n // Get session from pool\n session = await http2SessionPool.getSession(origin, options);\n \n // Handle abort signal before making request\n if (signal?.aborted) {\n cleanupAndReject(new Error('Request aborted'));\n return;\n }\n\n req = session.request({\n ':path': parsedUrl.pathname + parsedUrl.search,\n ':method': method,\n ...headers,\n });\n\n // Set timeout for the request\n if (options?.timeout) {\n req.setTimeout(options.timeout, () => {\n req.close();\n cleanupAndReject(new Error('Request timed out'));\n });\n }\n\n // Handle abort signal\n if (signal) {\n const onAbort = () => {\n req.close();\n cleanupAndReject(new Error('Request aborted'));\n };\n signal.addEventListener('abort', onAbort);\n // Clean up listener when request completes or errors\n const cleanup = () => signal.removeEventListener('abort', onAbort);\n req.on('close', cleanup);\n req.on('error', cleanup);\n }\n \n const chunks: Buffer[] = [];\n req.on('data', (chunk) => chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)));\n \n req.on('response', (headers) => {\n const status = Number(headers[':status']) || 200;\n const responseHeaders = new Headers();\n \n for (const [key, value] of Object.entries(headers)) {\n if (key.startsWith(':')) continue;\n if (Array.isArray(value)) {\n value.forEach(v => responseHeaders.append(key, v));\n } else if (value !== undefined) {\n responseHeaders.set(key, String(value));\n }\n }\n \n req.on('end', () => {\n const body = Buffer.concat(chunks);\n cleanupAndResolve(new Response(body, {\n status,\n statusText: 'OK',\n headers: responseHeaders,\n }));\n });\n });\n \n req.on('error', (err) => {\n // Stream error - reject the request but keep session alive (might be reusable)\n cleanupAndReject(err);\n });\n\n if (body) {\n if (typeof body === 'string') {\n req.write(body);\n } else if (body instanceof Uint8Array || Buffer.isBuffer(body)) {\n req.write(body);\n } else if (typeof body === 'object') {\n req.write(JSON.stringify(body));\n }\n }\n \n req.end();\n } catch (error) {\n cleanupAndReject(error instanceof Error ? error : new Error(String(error)));\n }\n });\n}\n\n/**\n * Close all HTTP/2 sessions in the pool.\n * Useful for cleanup in tests or when shutting down the application.\n */\nexport function closeHttp2SessionPool(): void {\n http2SessionPool.closeAll();\n}\n\n/**\n * Get statistics about the HTTP/2 session pool.\n */\nexport function getHttp2SessionPoolStats(): {\n sessionCount: number;\n origins: string[];\n} {\n const stats = http2SessionPool.getStats();\n return {\n sessionCount: stats.sessionCount,\n origins: stats.origins,\n };\n}\n\nexport { Http2SessionPool };","/**\r\n * HTTP Client Implementation\r\n * Superior to fetch + axios:\r\n *\r\n * vs fetch:\r\n * ✓ Automatic JSON parsing + error handling via Result<T,E>\r\n * ✓ Interceptors, retry, cache, timeout built-in\r\n * ✓ Progress tracking for uploads/downloads\r\n * ✓ Path parameter interpolation (/users/:id)\r\n * ✓ Request lifecycle hooks (onStart, onSuccess, onError, onFinally, onRetry)\r\n * ✓ Auto content-type detection (FormData, Blob, URLSearchParams, etc.)\r\n * ✓ Concurrent request limiting (rate limiter)\r\n *\r\n * vs axios:\r\n * ✓ Zero dependencies (~3KB gzipped vs axios ~13KB)\r\n * ✓ Result<T,E> monad — no try/catch needed, type-safe error handling\r\n * ✓ Built-in request deduplication\r\n * ✓ Streaming support with chunk callbacks\r\n * ✓ Plugin architecture (SOLID)\r\n * ✓ Middleware pipeline (Express/Koa-like)\r\n * ✓ Circuit breaker retry strategy\r\n * ✓ Response duration tracking\r\n * ✓ Modern ESM-first with tree-shaking\r\n *\r\n * SOLID Principles:\r\n * - S: HttpClient → HTTP communication\r\n * - O: Extensible via interceptors, strategies, plugins\r\n * - L: Liskov — implementations interchange without breaking\r\n * - I: Interface Segregation — small focused interfaces\r\n * - D: Dependency Inversion — depends on abstractions (IHttpClient)\r\n */\r\n\r\nimport type {\r\n IHttpClient,\r\n HttpRequest,\r\n HttpRequestConfig,\r\n HttpResponse,\r\n HttpErrorDetails,\r\n RequestInterceptor,\r\n ResponseInterceptor,\r\n RetryStrategy,\r\n RetryCondition,\r\n InlineRetryConfig,\r\n HttpClientConfig,\r\n CacheStrategy,\r\n ResponseType,\r\n RequestHooks,\r\n PaginateOptions,\r\n PollOptions,\r\n Disposer,\r\n Result,\r\n ProgressEvent as NexaProgressEvent,\r\n HttpTimeout,\r\n NodeTransportOptions,\r\n} from \"../types\";\r\nimport { Ok, Err } from \"../types\";\r\nimport { CacheStore } from \"../utils\";\r\n\r\n// ============= Internal Helpers =============\r\n\r\nfunction isNode(): boolean {\r\n return (\r\n typeof window === \"undefined\" &&\r\n typeof process !== \"undefined\" &&\r\n process.versions?.node !== undefined\r\n );\r\n}\r\n\r\nfunction isDeno(): boolean {\r\n const global = globalThis as any;\r\n return (\r\n typeof window === \"undefined\" &&\r\n typeof global.Deno !== \"undefined\" &&\r\n global.Deno.version?.deno !== undefined\r\n );\r\n}\r\n\r\nfunction isBun(): boolean {\r\n const global = globalThis as any;\r\n return (\r\n typeof global.Bun !== \"undefined\" &&\r\n global.Bun.version !== undefined\r\n );\r\n}\r\n\r\nfunction isCloudflare(): boolean {\r\n // Cloudflare Workers environment detection\r\n const global = globalThis as any;\r\n return (\r\n typeof global.caches !== \"undefined\" &&\r\n typeof global.WebSocketPair !== \"undefined\"\r\n );\r\n}\r\n\r\n/**\r\n * Get default adapter based on transport and environment\r\n */\r\nfunction getDefaultAdapter(\r\n transport?: \"fetch\" | \"node\" | \"http2\" | \"deno\" | \"bun\" | \"cloudflare\",\r\n nodeOptions?: NodeTransportOptions,\r\n): (input: RequestInfo, init?: RequestInit) => Promise<Response> {\r\n // If no transport specified or transport is 'fetch', use global fetch\r\n if (!transport || transport === \"fetch\") {\r\n return fetch;\r\n }\r\n\r\n // Node transports require Node.js environment\r\n if (transport === \"node\" || transport === \"http2\") {\r\n if (!isNode()) {\r\n throw new Error(\r\n `Transport '${transport}' is only available in Node.js environment`,\r\n );\r\n }\r\n\r\n // For Node transports, we'll return a function that lazily imports the adapter\r\n // to avoid bundling Node.js modules in browser builds\r\n return (input: RequestInfo, init?: RequestInit) => {\r\n // Dynamic import based on transport\r\n if (transport === \"http2\") {\r\n return import(\"./node-http-adapter.js\").then((module) =>\r\n module.nodeHttp2Adapter(input, init, nodeOptions),\r\n );\r\n } else {\r\n // transport === 'node'\r\n return import(\"./node-http-adapter.js\").then((module) =>\r\n module.nodeHttpAdapter(input, init, nodeOptions),\r\n );\r\n }\r\n };\r\n }\r\n\r\n // Environment-specific transports\r\n switch (transport) {\r\n case \"deno\":\r\n if (!isDeno()) {\r\n throw new Error(\r\n \"Transport 'deno' is only available in Deno environment\",\r\n );\r\n }\r\n return fetch; // Deno has global fetch\r\n \r\n case \"bun\":\r\n if (!isBun()) {\r\n throw new Error(\r\n \"Transport 'bun' is only available in Bun environment\",\r\n );\r\n }\r\n return fetch; // Bun has global fetch\r\n \r\n case \"cloudflare\":\r\n if (!isCloudflare()) {\r\n throw new Error(\r\n \"Transport 'cloudflare' is only available in Cloudflare Workers environment\",\r\n );\r\n }\r\n return fetch; // Cloudflare Workers has global fetch\r\n \r\n default:\r\n // Fallback to fetch for unknown transports\r\n return fetch;\r\n }\r\n}\r\n\r\n/**\r\n * In-memory cache adapter (delegates to CacheStore)\r\n */\r\nclass MemoryCache implements CacheStrategy {\r\n private store = new CacheStore();\r\n\r\n get(key: string): unknown | null {\r\n return this.store.get(key);\r\n }\r\n\r\n set(key: string, value: unknown, ttlMs = 60000): void {\r\n this.store.set(key, value, ttlMs);\r\n }\r\n\r\n has(key: string): boolean {\r\n return this.store.has(key);\r\n }\r\n\r\n clear(): void {\r\n this.store.clear();\r\n }\r\n}\r\n\r\n/**\r\n * Default retry strategy: exponential backoff with jitter\r\n */\r\nclass ExponentialBackoffRetry implements RetryStrategy {\r\n private maxAttempts: number;\r\n private baseDelayMs: number;\r\n\r\n constructor(maxAttempts: number = 3, baseDelayMs: number = 100) {\r\n this.maxAttempts = maxAttempts;\r\n this.baseDelayMs = baseDelayMs;\r\n }\r\n\r\n shouldRetry(attempt: number, error: HttpErrorDetails): boolean {\r\n const retryableStatus = error.status !== undefined && error.status >= 500;\r\n const networkError = error.code === \"NETWORK_ERROR\";\r\n return (\r\n attempt < this.maxAttempts &&\r\n (retryableStatus || networkError || error.code === \"TIMEOUT\")\r\n );\r\n }\r\n\r\n delayMs(attempt: number): number {\r\n const base = this.baseDelayMs * Math.pow(2, attempt - 1);\r\n const jitter = Math.random() * base * 0.1;\r\n return Math.min(base + jitter, 30000);\r\n }\r\n}\r\n\r\n/**\r\n * Retry strategy with custom condition function\r\n */\r\nclass ConditionalRetry implements RetryStrategy {\r\n private maxAttempts: number;\r\n private baseDelayMs: number;\r\n private condition?: RetryCondition;\r\n\r\n constructor(\r\n maxAttempts: number = 3,\r\n baseDelayMs: number = 100,\r\n condition?: RetryCondition,\r\n ) {\r\n this.maxAttempts = maxAttempts;\r\n this.baseDelayMs = baseDelayMs;\r\n this.condition = condition;\r\n }\r\n\r\n shouldRetry(attempt: number, error: HttpErrorDetails): boolean {\r\n if (attempt >= this.maxAttempts) {\r\n return false;\r\n }\r\n if (this.condition) {\r\n return this.condition(error, attempt);\r\n }\r\n // Default condition similar to ExponentialBackoffRetry\r\n const retryableStatus = error.status !== undefined && error.status >= 500;\r\n const networkError = error.code === \"NETWORK_ERROR\";\r\n return retryableStatus || networkError || error.code === \"TIMEOUT\";\r\n }\r\n\r\n delayMs(attempt: number): number {\r\n const base = this.baseDelayMs * Math.pow(2, attempt - 1);\r\n const jitter = Math.random() * base * 0.1;\r\n return Math.min(base + jitter, 30000);\r\n }\r\n}\r\n\r\n/**\r\n * Concurrent request limiter — controls max parallel requests\r\n */\r\nclass RequestQueue {\r\n private running = 0;\r\n private queue: Array<() => void> = [];\r\n private maxConcurrent: number;\r\n\r\n constructor(maxConcurrent: number) {\r\n this.maxConcurrent = maxConcurrent;\r\n }\r\n\r\n async acquire(): Promise<void> {\r\n if (this.running < this.maxConcurrent) {\r\n this.running++;\r\n return;\r\n }\r\n return new Promise<void>((resolve) => {\r\n this.queue.push(() => {\r\n this.running++;\r\n resolve();\r\n });\r\n });\r\n }\r\n\r\n release(): void {\r\n this.running--;\r\n const next = this.queue.shift();\r\n if (next) next();\r\n }\r\n\r\n get pending(): number {\r\n return this.queue.length;\r\n }\r\n\r\n get active(): number {\r\n return this.running;\r\n }\r\n}\r\n\r\n/**\r\n * Detect if an object contains File/Blob instances\r\n */\r\nfunction containsFiles(obj: unknown): boolean {\r\n if (obj === null || obj === undefined) return false;\r\n\r\n // Check if it's a File or Blob\r\n if (typeof Blob !== \"undefined\" && obj instanceof Blob) {\r\n return true;\r\n }\r\n\r\n // Check if it's a File (File extends Blob)\r\n if (typeof File !== \"undefined\" && obj instanceof File) {\r\n return true;\r\n }\r\n\r\n // Check arrays\r\n if (Array.isArray(obj)) {\r\n return obj.some((item) => containsFiles(item));\r\n }\r\n\r\n // Check plain objects\r\n if (typeof obj === \"object\") {\r\n return Object.values(obj).some((value) => containsFiles(value));\r\n }\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Convert an object to FormData, handling nested structures and arrays\r\n */\r\nfunction objectToFormData(\r\n obj: Record<string, any>,\r\n formData?: FormData,\r\n parentKey?: string,\r\n): FormData {\r\n const fd = formData || new FormData();\r\n\r\n for (const [key, value] of Object.entries(obj)) {\r\n const formKey = parentKey ? `${parentKey}[${key}]` : key;\r\n\r\n if (value === null || value === undefined) {\r\n continue;\r\n }\r\n\r\n if (typeof Blob !== \"undefined\" && value instanceof Blob) {\r\n fd.append(formKey, value);\r\n } else if (Array.isArray(value)) {\r\n // Handle arrays: append each item with same key for multiple files\r\n // or recursively process non-file arrays\r\n const hasFiles = value.some((item) => containsFiles(item));\r\n if (hasFiles) {\r\n // For arrays containing files, append each file with same key\r\n value.forEach((item) => {\r\n if (containsFiles(item)) {\r\n fd.append(formKey, item);\r\n } else {\r\n // Non-file items in array with files: convert to JSON string\r\n fd.append(formKey, JSON.stringify(item));\r\n }\r\n });\r\n } else {\r\n // Array without files: convert to JSON string\r\n fd.append(formKey, JSON.stringify(value));\r\n }\r\n } else if (typeof value === \"object\" && !(value instanceof Blob)) {\r\n // Recursively process nested objects\r\n objectToFormData(value, fd, formKey);\r\n } else {\r\n // Primitive values\r\n fd.append(formKey, String(value));\r\n }\r\n }\r\n\r\n return fd;\r\n}\r\n\r\n/**\r\n * Auto-convert body to FormData if it contains files and autoFormData is enabled\r\n */\r\nfunction autoConvertToFormData(body: unknown, autoFormData: boolean): unknown {\r\n if (!autoFormData) return body;\r\n if (body === null || body === undefined) return body;\r\n\r\n // Already FormData, Blob, etc. - don't convert\r\n if (typeof FormData !== \"undefined\" && body instanceof FormData) {\r\n return body;\r\n }\r\n if (typeof Blob !== \"undefined\" && body instanceof Blob) {\r\n return body;\r\n }\r\n if (\r\n typeof URLSearchParams !== \"undefined\" &&\r\n body instanceof URLSearchParams\r\n ) {\r\n return body;\r\n }\r\n if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {\r\n return body;\r\n }\r\n if (typeof ReadableStream !== \"undefined\" && body instanceof ReadableStream) {\r\n return body;\r\n }\r\n if (typeof body === \"string\") {\r\n return body;\r\n }\r\n\r\n // Check if object contains files\r\n if (typeof body === \"object\" && containsFiles(body)) {\r\n return objectToFormData(body as Record<string, any>);\r\n }\r\n\r\n return body;\r\n}\r\n\r\n/**\r\n * Detect body type and return appropriate fetch body + content-type\r\n */\r\nfunction serializeBody(\r\n body: unknown,\r\n autoFormData: boolean = true,\r\n): { serialized: BodyInit | undefined; contentType: string | null } {\r\n const processedBody = autoConvertToFormData(body, autoFormData);\r\n\r\n if (processedBody === undefined || processedBody === null) {\r\n return { serialized: undefined, contentType: null };\r\n }\r\n // FormData — browser sets multipart boundary automatically\r\n if (typeof FormData !== \"undefined\" && processedBody instanceof FormData) {\r\n return { serialized: processedBody, contentType: null }; // Let browser set Content-Type with boundary\r\n }\r\n // URLSearchParams\r\n if (\r\n typeof URLSearchParams !== \"undefined\" &&\r\n processedBody instanceof URLSearchParams\r\n ) {\r\n return {\r\n serialized: processedBody,\r\n contentType: \"application/x-www-form-urlencoded\",\r\n };\r\n }\r\n // Blob / File\r\n if (typeof Blob !== \"undefined\" && processedBody instanceof Blob) {\r\n return {\r\n serialized: processedBody,\r\n contentType: processedBody.type || \"application/octet-stream\",\r\n };\r\n }\r\n // ArrayBuffer / TypedArray\r\n if (\r\n processedBody instanceof ArrayBuffer ||\r\n ArrayBuffer.isView(processedBody)\r\n ) {\r\n return {\r\n serialized: processedBody as BodyInit,\r\n contentType: \"application/octet-stream\",\r\n };\r\n }\r\n // ReadableStream\r\n if (\r\n typeof ReadableStream !== \"undefined\" &&\r\n processedBody instanceof ReadableStream\r\n ) {\r\n return {\r\n serialized: processedBody,\r\n contentType: \"application/octet-stream\",\r\n };\r\n }\r\n // String\r\n if (typeof processedBody === \"string\") {\r\n return { serialized: processedBody, contentType: \"text/plain\" };\r\n }\r\n // Object / Array → JSON\r\n return {\r\n serialized: JSON.stringify(processedBody),\r\n contentType: \"application/json\",\r\n };\r\n}\r\n\r\n/**\r\n * Interpolate path parameters: /users/:id → /users/123\r\n */\r\nfunction interpolatePath(\r\n path: string,\r\n params?: Record<string, string | number>,\r\n): string {\r\n if (!params) return path;\r\n return path.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, (_, key) => {\r\n const value = params[key];\r\n if (value === undefined) {\r\n throw new Error(`Missing path parameter: :${key}`);\r\n }\r\n return encodeURIComponent(String(value));\r\n });\r\n}\r\n\r\n/**\r\n * Normalize credentials: if credentials is provided, use it; otherwise convert withCredentials boolean.\r\n */\r\nfunction normalizeCredentials(\r\n credentials?: RequestCredentials,\r\n withCredentials?: boolean,\r\n): RequestCredentials | undefined {\r\n if (credentials !== undefined) {\r\n return credentials;\r\n }\r\n if (withCredentials !== undefined) {\r\n return withCredentials ? \"include\" : \"same-origin\";\r\n }\r\n return undefined;\r\n}\r\n\r\n/**\r\n * Normalize timeout configuration to an object with connection, response, and total timeouts.\r\n * If a number is provided, it's treated as total timeout.\r\n * If an object is provided, missing fields are undefined.\r\n */\r\nfunction normalizeTimeout(timeout?: HttpTimeout): {\r\n connection?: number;\r\n response?: number;\r\n total?: number;\r\n} {\r\n if (typeof timeout === \"number\") {\r\n return { total: timeout };\r\n }\r\n if (typeof timeout === \"object\" && timeout !== null) {\r\n return {\r\n connection: timeout.connection,\r\n response: timeout.response,\r\n total: timeout.total,\r\n };\r\n }\r\n return {};\r\n}\r\n\r\n// ============= Main HTTP Client =============\r\n\r\n/**\r\n * Main HTTP Client Implementation\r\n * Combines fetch API with axios-like convenience + modern features\r\n */\r\nexport class HttpClient implements IHttpClient {\r\n private requestInterceptors: RequestInterceptor[] = [];\r\n private responseInterceptors: ResponseInterceptor[] = [];\r\n private cache: CacheStrategy;\r\n private config: Required<\r\n Pick<\r\n HttpClientConfig,\r\n \"baseURL\" | \"defaultHeaders\" | \"defaultTimeout\" | \"validateStatus\"\r\n >\r\n > & {\r\n cacheStrategy: CacheStrategy;\r\n maxConcurrent: number;\r\n defaultResponseType: ResponseType;\r\n defaultHooks: RequestHooks;\r\n transformRequest?: HttpClientConfig[\"transformRequest\"];\r\n credentials?: RequestCredentials;\r\n adapter?: HttpClientConfig[\"adapter\"];\r\n autoFormData?: boolean;\r\n debug?: boolean | \"verbose\";\r\n logger?: (message: string, data?: unknown) => void;\r\n transport?: \"fetch\" | \"node\" | \"http2\" | \"deno\" | \"bun\" | \"cloudflare\";\r\n nodeOptions?: NodeTransportOptions;\r\n };\r\n private requestQueue: RequestQueue | null;\r\n private pendingRequests = new Map<symbol, AbortController>();\r\n\r\n constructor(config: HttpClientConfig = {}) {\r\n this.config = {\r\n baseURL: config.baseURL ?? \"\",\r\n defaultHeaders: config.defaultHeaders ?? {\r\n \"Content-Type\": \"application/json\",\r\n },\r\n defaultTimeout: config.defaultTimeout ?? 30000,\r\n validateStatus:\r\n config.validateStatus ?? ((status) => status >= 200 && status < 300),\r\n cacheStrategy: config.cacheStrategy ?? new MemoryCache(),\r\n maxConcurrent: config.maxConcurrent ?? 0,\r\n defaultResponseType: config.defaultResponseType ?? \"auto\",\r\n defaultHooks: config.defaultHooks ?? {},\r\n transformRequest: config.transformRequest,\r\n credentials: normalizeCredentials(\r\n config.credentials,\r\n config.withCredentials,\r\n ),\r\n adapter: config.adapter,\r\n autoFormData: config.autoFormData ?? true,\r\n debug: config.debug,\r\n logger: config.logger,\r\n transport: config.transport,\r\n nodeOptions: config.nodeOptions,\r\n };\r\n this.cache = this.config.cacheStrategy;\r\n this.requestQueue =\r\n this.config.maxConcurrent > 0\r\n ? new RequestQueue(this.config.maxConcurrent)\r\n : null;\r\n }\r\n\r\n private logDebug(\r\n debug: boolean | \"verbose\" | undefined,\r\n level: \"info\" | \"verbose\",\r\n message: string,\r\n data?: unknown,\r\n logger?: (message: string, data?: unknown) => void,\r\n ): void {\r\n const effectiveDebug = debug ?? this.config.debug;\r\n if (!effectiveDebug) return;\r\n if (effectiveDebug === true && level === \"verbose\") return;\r\n\r\n const prefix = `[Nexa HTTP] `;\r\n const fullMessage = prefix + message;\r\n const finalLogger = logger ?? this.config.logger ?? console.log;\r\n if (data !== undefined) {\r\n finalLogger(fullMessage, data);\r\n } else {\r\n finalLogger(fullMessage);\r\n }\r\n }\r\n\r\n /**\r\n * Core request method — all others delegate to this\r\n * Pipeline: hooks → cache → interceptors → transformRequest → fetch → parse → validate → transformResponse → interceptors → cache → hooks\r\n */\r\n async request<T = unknown>(\r\n config: HttpRequestConfig,\r\n ): Promise<Result<HttpResponse<T>, HttpErrorDetails>> {\r\n const hooks = { ...this.config.defaultHooks, ...config.hooks };\r\n const debug = config.debug ?? this.config.debug;\r\n const logger = config.logger ?? this.config.logger;\r\n const maxAttempts = this.getMaxAttempts(config.retry);\r\n const retryStrategy = this.getRetryStrategy(config.retry);\r\n const requestId = Symbol(\"request\");\r\n\r\n // Lifecycle: onStart\r\n hooks.onStart?.(this.buildRequest(config));\r\n\r\n // Acquire queue slot if rate limiting is enabled\r\n if (this.requestQueue) {\r\n await this.requestQueue.acquire();\r\n }\r\n\r\n try {\r\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\r\n let finalRequest: HttpRequest | undefined = undefined;\r\n try {\r\n // Step 1: Check cache for GET requests\r\n if (config.method === \"GET\" || !config.method) {\r\n if (config.cache?.enabled) {\r\n const cacheKey = this.getCacheKey(config);\r\n const cached = this.cache.get(cacheKey);\r\n if (cached) {\r\n const cachedResponse = cached as HttpResponse<T>;\r\n this.logDebug(\r\n debug,\r\n \"info\",\r\n `Cache hit for ${config.url}`,\r\n cachedResponse,\r\n logger,\r\n );\r\n hooks.onSuccess?.(cachedResponse);\r\n hooks.onFinally?.();\r\n return Ok(cachedResponse);\r\n }\r\n }\r\n }\r\n\r\n // Step 2: Build final request (with path param interpolation)\r\n finalRequest = this.buildRequest(config);\r\n this.logDebug(\r\n debug,\r\n \"info\",\r\n `${finalRequest.method || \"GET\"} ${finalRequest.url}`,\r\n finalRequest,\r\n logger,\r\n );\r\n\r\n // Step 3: Run request interceptors\r\n for (const interceptor of this.requestInterceptors) {\r\n finalRequest = await interceptor.onRequest(finalRequest);\r\n }\r\n this.logDebug(\r\n debug,\r\n \"verbose\",\r\n \"Request after interceptors\",\r\n finalRequest,\r\n logger,\r\n );\r\n\r\n // Step 4: Apply transformRequest (global + request-specific)\r\n finalRequest = this.applyTransformRequestToRequest(\r\n finalRequest,\r\n config,\r\n );\r\n this.logDebug(\r\n debug,\r\n \"verbose\",\r\n \"Request after transformRequest\",\r\n finalRequest,\r\n logger,\r\n );\r\n\r\n // Normalize timeout configuration\r\n const timeouts = normalizeTimeout(\r\n config.timeout ?? this.config.defaultTimeout,\r\n );\r\n\r\n // Step 5: Create AbortController for cancellation\r\n const controller = new AbortController();\r\n this.pendingRequests.set(requestId, controller);\r\n\r\n // Merge external signal if provided\r\n if (config.signal) {\r\n config.signal.addEventListener(\"abort\", () => controller.abort(), {\r\n once: true,\r\n });\r\n }\r\n\r\n this.logDebug(\r\n debug,\r\n \"info\",\r\n `Fetching (attempt ${attempt}/${maxAttempts})`,\r\n { url: finalRequest.url, method: finalRequest.method },\r\n logger,\r\n );\r\n\r\n // Step 6: Fetch with timeout + progress tracking\r\n const startTime = performance.now();\r\n const response = await this.fetchWithTimeout(\r\n finalRequest,\r\n timeouts,\r\n controller,\r\n );\r\n const duration = performance.now() - startTime;\r\n this.logDebug(\r\n debug,\r\n \"info\",\r\n `Response ${response.status} ${response.statusText}`,\r\n { duration, status: response.status, attempt },\r\n logger,\r\n );\r\n\r\n // Step 7: Track download progress if callback provided\r\n let responseForParsing = response;\r\n if (config.onDownloadProgress && response.body) {\r\n responseForParsing = this.trackDownloadProgress(\r\n response,\r\n config.onDownloadProgress,\r\n );\r\n }\r\n\r\n // Step 8: Parse response based on responseType\r\n const responseType =\r\n config.responseType ?? this.config.defaultResponseType;\r\n const httpResponse = await this.parseResponse<T>(\r\n responseForParsing,\r\n finalRequest,\r\n duration,\r\n responseType,\r\n timeouts.response,\r\n );\r\n\r\n // Step 9: Validate status\r\n if (!this.config.validateStatus(httpResponse.status)) {\r\n const errorDetails: HttpErrorDetails = {\r\n message: `Request failed with status ${httpResponse.status}`,\r\n status: httpResponse.status,\r\n statusText: httpResponse.statusText,\r\n code: \"HTTP_ERROR\",\r\n request: finalRequest,\r\n response: httpResponse as HttpResponse<unknown>,\r\n config,\r\n };\r\n throw errorDetails;\r\n }\r\n\r\n // Step 10: Validate response data\r\n if (config.validate) {\r\n const validation = config.validate.validate(httpResponse.data);\r\n if (!validation.ok) {\r\n return validation;\r\n }\r\n }\r\n\r\n // Step 11: Transform response data\r\n if (config.transform) {\r\n httpResponse.data = config.transform.transform(\r\n httpResponse.data,\r\n ) as T;\r\n }\r\n\r\n // Step 12: Run response interceptors\r\n let finalResponse = httpResponse;\r\n for (const interceptor of this.responseInterceptors) {\r\n finalResponse = await interceptor.onResponse(finalResponse);\r\n }\r\n\r\n // Step 13: Cache successful GET responses\r\n if (\r\n (config.method === \"GET\" || !config.method) &&\r\n config.cache?.enabled\r\n ) {\r\n const cacheKey = this.getCacheKey(config);\r\n this.cache.set(cacheKey, finalResponse, config.cache.ttlMs);\r\n }\r\n\r\n // Lifecycle: onSuccess\r\n hooks.onSuccess?.(finalResponse);\r\n this.logDebug(\r\n debug,\r\n \"verbose\",\r\n \"Response data\",\r\n finalResponse.data,\r\n logger,\r\n );\r\n\r\n // Cleanup\r\n this.pendingRequests.delete(requestId);\r\n\r\n return Ok(finalResponse);\r\n } catch (error) {\r\n const errorDetails = this.normalizeError(error, finalRequest, config);\r\n this.logDebug(\r\n debug,\r\n \"info\",\r\n `Error: ${errorDetails.message}`,\r\n { error: errorDetails, attempt },\r\n logger,\r\n );\r\n\r\n // Lifecycle: onRetry\r\n if (\r\n attempt < maxAttempts &&\r\n retryStrategy.shouldRetry(attempt, errorDetails)\r\n ) {\r\n hooks.onRetry?.(attempt, errorDetails);\r\n const delayMs = retryStrategy.delayMs(attempt);\r\n this.logDebug(\r\n debug,\r\n \"info\",\r\n `Retrying after ${delayMs}ms (attempt ${attempt + 1}/${maxAttempts})`,\r\n { error: errorDetails },\r\n logger,\r\n );\r\n await this.delay(delayMs);\r\n continue;\r\n }\r\n\r\n // Run error interceptors\r\n let finalErrorDetails = errorDetails;\r\n for (const interceptor of this.responseInterceptors) {\r\n if (interceptor.onError) {\r\n finalErrorDetails = await interceptor.onError(finalErrorDetails);\r\n }\r\n }\r\n\r\n // Lifecycle: onError\r\n hooks.onError?.(finalErrorDetails);\r\n\r\n // Cleanup\r\n this.pendingRequests.delete(requestId);\r\n\r\n return Err(finalErrorDetails);\r\n }\r\n }\r\n\r\n const exhaustedError: HttpErrorDetails = {\r\n message: \"Max retries exceeded\",\r\n code: \"MAX_RETRIES\",\r\n };\r\n this.logDebug(\r\n debug,\r\n \"info\",\r\n \"Max retries exceeded\",\r\n exhaustedError,\r\n logger,\r\n );\r\n hooks.onError?.(exhaustedError);\r\n return Err(exhaustedError);\r\n } finally {\r\n // Lifecycle: onFinally (always runs)\r\n hooks.onFinally?.();\r\n\r\n // Release queue slot\r\n if (this.requestQueue) {\r\n this.requestQueue.release();\r\n }\r\n }\r\n }\r\n\r\n // ============= HTTP Method Shortcuts =============\r\n\r\n get<T = unknown>(\r\n url: string,\r\n config?: Omit<HttpRequestConfig, \"url\" | \"method\">,\r\n ) {\r\n return this.request<T>({ ...config, url, method: \"GET\" });\r\n }\r\n\r\n post<T = unknown>(\r\n url: string,\r\n body?: unknown,\r\n config?: Omit<HttpRequestConfig, \"url\" | \"method\" | \"body\">,\r\n ) {\r\n return this.request<T>({ ...config, url, method: \"POST\", body });\r\n }\r\n\r\n put<T = unknown>(\r\n url: string,\r\n body?: unknown,\r\n config?: Omit<HttpRequestConfig, \"url\" | \"method\" | \"body\">,\r\n ) {\r\n return this.request<T>({ ...config, url, method: \"PUT\", body });\r\n }\r\n\r\n patch<T = unknown>(\r\n url: string,\r\n body?: unknown,\r\n config?: Omit<HttpRequestConfig, \"url\" | \"method\" | \"body\">,\r\n ) {\r\n return this.request<T>({ ...config, url, method: \"PATCH\", body });\r\n }\r\n\r\n delete<T = unknown>(\r\n url: string,\r\n config?: Omit<HttpRequestConfig, \"url\" | \"method\">,\r\n ) {\r\n return this.request<T>({ ...config, url, method: \"DELETE\" });\r\n }\r\n\r\n head(url: string, config?: Omit<HttpRequestConfig, \"url\" | \"method\">) {\r\n return this.request<void>({ ...config, url, method: \"HEAD\" });\r\n }\r\n\r\n options(url: string, config?: Omit<HttpRequestConfig, \"url\" | \"method\">) {\r\n return this.request<void>({ ...config, url, method: \"OPTIONS\" });\r\n }\r\n\r\n // ============= Interceptor Management =============\r\n\r\n addRequestInterceptor(interceptor: RequestInterceptor): Disposer {\r\n this.requestInterceptors.push(interceptor);\r\n return () => {\r\n const idx = this.requestInterceptors.indexOf(interceptor);\r\n if (idx !== -1) this.requestInterceptors.splice(idx, 1);\r\n };\r\n }\r\n\r\n addResponseInterceptor(interceptor: ResponseInterceptor): Disposer {\r\n this.responseInterceptors.push(interceptor);\r\n return () => {\r\n const idx = this.responseInterceptors.indexOf(interceptor);\r\n if (idx !== -1) this.responseInterceptors.splice(idx, 1);\r\n };\r\n }\r\n\r\n clearInterceptors(): void {\r\n this.requestInterceptors = [];\r\n this.responseInterceptors = [];\r\n }\r\n\r\n /**\r\n * Clear all cached responses\r\n */\r\n clearCache(): void {\r\n this.cache.clear();\r\n }\r\n\r\n // ============= Cancellation =============\r\n\r\n /**\r\n * Cancel all pending requests\r\n */\r\n cancelAll(): void {\r\n for (const controller of this.pendingRequests.values()) {\r\n controller.abort();\r\n }\r\n this.pendingRequests.clear();\r\n }\r\n\r\n /**\r\n * Number of currently active requests\r\n */\r\n get activeRequests(): number {\r\n return this.pendingRequests.size;\r\n }\r\n\r\n /**\r\n * Queue stats (only relevant when maxConcurrent > 0)\r\n */\r\n get queueStats(): { active: number; pending: number } {\r\n return {\r\n active: this.requestQueue?.active ?? this.pendingRequests.size,\r\n pending: this.requestQueue?.pending ?? 0,\r\n };\r\n }\r\n\r\n // ============= Extended Features =============\r\n\r\n /**\r\n * Create a child client that inherits config + interceptors.\r\n * Overrides are merged (headers are shallow-merged, rest overwrites).\r\n */\r\n extend(overrides: HttpClientConfig = {}): HttpClient {\r\n const child = new HttpClient({\r\n baseURL: overrides.baseURL ?? this.config.baseURL,\r\n defaultHeaders: {\r\n ...this.config.defaultHeaders,\r\n ...overrides.defaultHeaders,\r\n },\r\n credentials:\r\n normalizeCredentials(\r\n overrides.credentials,\r\n overrides.withCredentials,\r\n ) ?? this.config.credentials,\r\n defaultTimeout: overrides.defaultTimeout ?? this.config.defaultTimeout,\r\n validateStatus: overrides.validateStatus ?? this.config.validateStatus,\r\n cacheStrategy: overrides.cacheStrategy ?? this.cache,\r\n maxConcurrent: overrides.maxConcurrent ?? this.config.maxConcurrent,\r\n defaultResponseType:\r\n overrides.defaultResponseType ?? this.config.defaultResponseType,\r\n defaultHooks: { ...this.config.defaultHooks, ...overrides.defaultHooks },\r\n transformRequest:\r\n overrides.transformRequest ?? this.config.transformRequest,\r\n adapter: overrides.adapter ?? this.config.adapter,\r\n autoFormData: overrides.autoFormData ?? this.config.autoFormData,\r\n });\r\n // Inherit interceptors\r\n for (const interceptor of this.requestInterceptors) {\r\n child.addRequestInterceptor(interceptor);\r\n }\r\n for (const interceptor of this.responseInterceptors) {\r\n child.addResponseInterceptor(interceptor);\r\n }\r\n return child;\r\n }\r\n\r\n /**\r\n * Auto-paginate a GET endpoint. Yields arrays of items per page.\r\n *\r\n * Usage:\r\n * ```ts\r\n * for await (const users of client.paginate<UserListResponse>('/users', {\r\n * getItems: (data) => data.items,\r\n * getNextPage: (data, cfg) =>\r\n * data.nextCursor ? { ...cfg, query: { ...cfg.query, cursor: data.nextCursor } } : null,\r\n * })) {\r\n * console.log(users); // items from this page\r\n * }\r\n * ```\r\n */\r\n async *paginate<T = unknown>(\r\n url: string,\r\n options: PaginateOptions<T>,\r\n config: Omit<HttpRequestConfig, \"url\" | \"method\"> = {},\r\n ): AsyncGenerator<T[]> {\r\n let currentConfig: Omit<HttpRequestConfig, \"url\" | \"method\"> = {\r\n ...config,\r\n };\r\n\r\n while (true) {\r\n const result = await this.get<T>(url, currentConfig);\r\n if (!result.ok) break;\r\n\r\n const items = options.getItems(result.value.data) as T[];\r\n yield items;\r\n\r\n const nextConfig = options.getNextPage(result.value.data, currentConfig);\r\n if (!nextConfig) break;\r\n currentConfig = nextConfig;\r\n }\r\n }\r\n\r\n /**\r\n * Poll an endpoint until a condition is met.\r\n * Returns the final response that satisfied `until()`.\r\n *\r\n * Usage:\r\n * ```ts\r\n * const result = await client.poll<Job>('/jobs/123', {\r\n * intervalMs: 2000,\r\n * maxAttempts: 30,\r\n * until: (job) => job.status === 'completed',\r\n * onPoll: (job, attempt) => console.log(`Attempt ${attempt}: ${job.status}`),\r\n * });\r\n * ```\r\n */\r\n async poll<T = unknown>(\r\n url: string,\r\n options: PollOptions<T>,\r\n config: Omit<HttpRequestConfig, \"url\" | \"method\"> = {},\r\n ): Promise<Result<HttpResponse<T>, HttpErrorDetails>> {\r\n const maxAttempts = options.maxAttempts ?? 0;\r\n\r\n for (\r\n let attempt = 1;\r\n maxAttempts === 0 || attempt <= maxAttempts;\r\n attempt++\r\n ) {\r\n const result = await this.get<T>(url, config);\r\n\r\n if (!result.ok) return result;\r\n\r\n options.onPoll?.(result.value.data, attempt);\r\n\r\n if (options.until(result.value.data)) {\r\n return result;\r\n }\r\n\r\n if (maxAttempts > 0 && attempt >= maxAttempts) break;\r\n\r\n await this.delay(options.intervalMs);\r\n }\r\n\r\n return Err({\r\n message: `Polling exhausted after ${maxAttempts} attempts`,\r\n code: \"POLL_EXHAUSTED\",\r\n });\r\n }\r\n\r\n // ============= Private Helpers =============\r\n\r\n private buildRequest(config: HttpRequestConfig): HttpRequest {\r\n const path = interpolatePath(config.url, config.params);\r\n const url = this.buildUrl(path, config.query);\r\n const credentials =\r\n normalizeCredentials(config.credentials, config.withCredentials) ??\r\n this.config.credentials;\r\n const transport = config.transport ?? this.config.transport;\r\n const nodeOptions = config.nodeOptions ?? this.config.nodeOptions;\r\n\r\n return {\r\n url,\r\n method: config.method ?? \"GET\",\r\n headers: {\r\n ...this.config.defaultHeaders,\r\n ...config.headers,\r\n },\r\n body: config.body,\r\n query: config.query,\r\n params: config.params,\r\n timeout: config.timeout,\r\n signal: config.signal,\r\n credentials,\r\n adapter: config.adapter,\r\n autoFormData: config.autoFormData ?? this.config.autoFormData,\r\n transport,\r\n nodeOptions,\r\n };\r\n }\r\n\r\n private buildUrl(\r\n path: string,\r\n query?: Record<string, string | number | boolean>,\r\n ): string {\r\n let url = this.config.baseURL + path;\r\n\r\n if (query && Object.keys(query).length > 0) {\r\n const params = new URLSearchParams();\r\n Object.entries(query).forEach(([key, value]) => {\r\n params.append(key, String(value));\r\n });\r\n url += `?${params.toString()}`;\r\n }\r\n\r\n return url;\r\n }\r\n\r\n /**\r\n * Apply transformRequest functions to the request body and headers.\r\n * Combines global transformRequest (from client config) and request-specific transformRequest.\r\n * Mutates headers object in place (axios-style).\r\n */\r\n private applyTransformRequestToRequest(\r\n finalRequest: HttpRequest,\r\n requestConfig: HttpRequestConfig,\r\n ): HttpRequest {\r\n const globalTransform = this.config.transformRequest;\r\n const requestTransform = requestConfig.transformRequest;\r\n\r\n // Collect all transformers: global first, then request-specific\r\n const transformers: Array<\r\n (data: unknown, headers: Record<string, string>) => unknown\r\n > = [];\r\n\r\n if (globalTransform) {\r\n if (Array.isArray(globalTransform)) {\r\n transformers.push(...globalTransform);\r\n } else {\r\n transformers.push(globalTransform);\r\n }\r\n }\r\n\r\n if (requestTransform) {\r\n if (Array.isArray(requestTransform)) {\r\n transformers.push(...requestTransform);\r\n } else {\r\n transformers.push(requestTransform);\r\n }\r\n }\r\n\r\n if (transformers.length === 0) {\r\n return finalRequest;\r\n }\r\n\r\n // Apply transformations sequentially\r\n let transformedBody = finalRequest.body;\r\n const headers = finalRequest.headers ?? {};\r\n\r\n for (const transformer of transformers) {\r\n transformedBody = transformer(transformedBody, headers);\r\n }\r\n\r\n return {\r\n ...finalRequest,\r\n body: transformedBody,\r\n headers,\r\n };\r\n }\r\n\r\n private getCacheKey(config: HttpRequestConfig): string {\r\n const path = interpolatePath(config.url, config.params);\r\n const queryStr = config.query ? JSON.stringify(config.query) : \"\";\r\n return `${config.method ?? \"GET\"}:${path}${queryStr ? \":\" + queryStr : \"\"}`;\r\n }\r\n\r\n private fetchWithTimeout(\r\n request: HttpRequest,\r\n timeouts: { connection?: number; response?: number; total?: number },\r\n controller: AbortController,\r\n ): Promise<Response> {\r\n const { serialized, contentType } = serializeBody(\r\n request.body,\r\n request.autoFormData,\r\n );\r\n\r\n // Build headers — auto-detect content-type if body determines it\r\n const headers = { ...request.headers };\r\n if (contentType) {\r\n headers[\"Content-Type\"] = contentType;\r\n } else if (contentType === null && serialized instanceof FormData) {\r\n // Remove Content-Type so browser sets multipart boundary\r\n delete headers[\"Content-Type\"];\r\n }\r\n\r\n // Determine connection timeout: total has priority, then connection\r\n const connectionTimeoutMs = timeouts.total ?? timeouts.connection;\r\n\r\n // Promise.race ensures timeout works even when fetch mock doesn't respect AbortSignal\r\n return new Promise<Response>((resolve, reject) => {\r\n let timeoutId: NodeJS.Timeout | null = null;\r\n if (connectionTimeoutMs !== undefined) {\r\n timeoutId = setTimeout(() => {\r\n controller.abort();\r\n const err = new Error(\"Request timed out\");\r\n err.name = \"TimeoutError\";\r\n reject(err);\r\n }, connectionTimeoutMs);\r\n }\r\n\r\n const init: RequestInit = {\r\n method: request.method,\r\n headers,\r\n body: serialized,\r\n signal: controller.signal,\r\n };\r\n\r\n if (request.credentials !== undefined) {\r\n init.credentials = request.credentials;\r\n }\r\n\r\n const transport = request.transport ?? this.config.transport ?? \"fetch\";\r\n const nodeOptions = request.nodeOptions ?? this.config.nodeOptions;\r\n\r\n let adapter = request.adapter ?? this.config.adapter;\r\n if (!adapter) {\r\n adapter = getDefaultAdapter(transport, nodeOptions);\r\n }\r\n adapter(request.url, init).then(\r\n (response) => {\r\n if (timeoutId) clearTimeout(timeoutId);\r\n resolve(response);\r\n },\r\n (error) => {\r\n if (timeoutId) clearTimeout(timeoutId);\r\n reject(error);\r\n },\r\n );\r\n });\r\n }\r\n\r\n /**\r\n * Wraps response body with a progress-tracking ReadableStream\r\n */\r\n private trackDownloadProgress(\r\n response: Response,\r\n onProgress: (event: NexaProgressEvent) => void,\r\n ): Response {\r\n const total = parseInt(response.headers.get(\"content-length\") || \"0\", 10);\r\n const reader = response.body?.getReader();\r\n if (!reader) return response;\r\n\r\n let loaded = 0;\r\n const stream = new ReadableStream({\r\n async pull(controller) {\r\n const { done, value } = await reader.read();\r\n if (done) {\r\n controller.close();\r\n return;\r\n }\r\n loaded += value.byteLength;\r\n onProgress({\r\n loaded,\r\n total,\r\n percent: total > 0 ? Math.round((loaded / total) * 100) : 0,\r\n });\r\n controller.enqueue(value);\r\n },\r\n });\r\n\r\n return new Response(stream, {\r\n headers: response.headers,\r\n status: response.status,\r\n statusText: response.statusText,\r\n });\r\n }\r\n\r\n /**\r\n * Wraps a promise with a timeout. If the timeout elapses before the promise resolves,\r\n * rejects with a TimeoutError.\r\n */\r\n private withTimeout<T>(\r\n promise: Promise<T>,\r\n timeoutMs: number,\r\n errorMessage: string = \"Operation timed out\",\r\n ): Promise<T> {\r\n if (timeoutMs <= 0) return promise;\r\n\r\n return new Promise<T>((resolve, reject) => {\r\n const timeoutId = setTimeout(() => {\r\n const err = new Error(errorMessage);\r\n err.name = \"TimeoutError\";\r\n reject(err);\r\n }, timeoutMs);\r\n\r\n promise.then(\r\n (result) => {\r\n clearTimeout(timeoutId);\r\n resolve(result);\r\n },\r\n (error) => {\r\n clearTimeout(timeoutId);\r\n reject(error);\r\n },\r\n );\r\n });\r\n }\r\n\r\n private async parseResponse<T>(\r\n response: Response,\r\n request: HttpRequest,\r\n duration: number,\r\n responseType: ResponseType,\r\n responseTimeout?: number,\r\n ): Promise<HttpResponse<T>> {\r\n const data = await this.parseBody<T>(\r\n response,\r\n responseType,\r\n responseTimeout,\r\n );\r\n\r\n return {\r\n status: response.status,\r\n statusText: response.statusText,\r\n headers: response.headers,\r\n data,\r\n request,\r\n duration,\r\n };\r\n }\r\n\r\n private async parseBody<T>(\r\n response: Response,\r\n responseType: ResponseType,\r\n responseTimeout?: number,\r\n ): Promise<T> {\r\n // Helper to wrap promise with timeout if responseTimeout is provided\r\n const read = async <R>(promise: Promise<R>): Promise<R> => {\r\n if (responseTimeout !== undefined && responseTimeout > 0) {\r\n return this.withTimeout(promise, responseTimeout, \"Response timeout\");\r\n }\r\n return promise;\r\n };\r\n\r\n switch (responseType) {\r\n case \"json\":\r\n return await read(response.json() as Promise<T>);\r\n case \"text\":\r\n return await read(response.text() as Promise<T>);\r\n case \"blob\":\r\n return await read(response.blob() as Promise<T>);\r\n case \"arrayBuffer\":\r\n return await read(response.arrayBuffer() as Promise<T>);\r\n case \"formData\":\r\n return await read(response.formData() as Promise<T>);\r\n case \"stream\":\r\n // Stream response cannot have a timeout applied to reading\r\n return response.body as T;\r\n case \"auto\":\r\n default: {\r\n const contentType = response.headers.get(\"content-type\") ?? \"\";\r\n if (contentType.includes(\"application/json\")) {\r\n return await read(response.json() as Promise<T>);\r\n }\r\n if (contentType.includes(\"text/\")) {\r\n return await read(response.text() as Promise<T>);\r\n }\r\n if (contentType.includes(\"multipart/form-data\")) {\r\n return await read(response.formData() as Promise<T>);\r\n }\r\n if (\r\n contentType.includes(\"application/octet-stream\") ||\r\n contentType.includes(\"image/\") ||\r\n contentType.includes(\"audio/\") ||\r\n contentType.includes(\"video/\")\r\n ) {\r\n return await read(response.blob() as Promise<T>);\r\n }\r\n // Fallback: try JSON, then text\r\n try {\r\n return await read(response.json() as Promise<T>);\r\n } catch {\r\n return await read(response.text() as Promise<T>);\r\n }\r\n }\r\n }\r\n }\r\n\r\n private normalizeError(\r\n error: unknown,\r\n request?: HttpRequest,\r\n config?: HttpRequestConfig,\r\n ): HttpErrorDetails {\r\n const baseError = (code: string, message: string): HttpErrorDetails => ({\r\n message,\r\n code,\r\n originalError: error,\r\n request,\r\n config,\r\n });\r\n\r\n if (error instanceof Error && error.name === \"TimeoutError\") {\r\n // Differentiate between connection/timeout total and response timeout\r\n if (error.message.includes(\"Response timeout\")) {\r\n return baseError(\"RESPONSE_TIMEOUT\", error.message);\r\n }\r\n return baseError(\"TIMEOUT\", \"Request timed out\");\r\n }\r\n if (error instanceof DOMException && error.name === \"AbortError\") {\r\n return baseError(\"ABORTED\", \"Request aborted\");\r\n }\r\n if (error instanceof Error) {\r\n if (error.name === \"AbortError\" || error.message.includes(\"abort\")) {\r\n return baseError(\"ABORTED\", \"Request aborted\");\r\n }\r\n return {\r\n message: error.message,\r\n code: error.name === \"TypeError\" ? \"NETWORK_ERROR\" : \"UNKNOWN_ERROR\",\r\n originalError: error,\r\n request,\r\n config,\r\n };\r\n }\r\n\r\n // If error is already HttpErrorDetails, enrich with request/config if missing\r\n if (this.isHttpErrorDetails(error)) {\r\n return {\r\n ...error,\r\n request: error.request ?? request,\r\n config: error.config ?? config,\r\n };\r\n }\r\n\r\n return {\r\n message: String(error),\r\n code: \"UNKNOWN_ERROR\",\r\n originalError: error,\r\n request,\r\n config,\r\n };\r\n }\r\n\r\n private isHttpErrorDetails(error: unknown): error is HttpErrorDetails {\r\n return (\r\n typeof error === \"object\" &&\r\n error !== null &&\r\n \"message\" in error &&\r\n \"code\" in error\r\n );\r\n }\r\n\r\n private getMaxAttempts(retry?: HttpRequestConfig[\"retry\"]): number {\r\n if (!retry) return 1;\r\n if (\"shouldRetry\" in retry) {\r\n // RetryStrategy controls retries via shouldRetry; use safe upper bound\r\n return 100;\r\n }\r\n // InlineRetryConfig\r\n const config = retry as InlineRetryConfig;\r\n return config.maxAttempts ?? 3;\r\n }\r\n\r\n private getRetryStrategy(retry?: HttpRequestConfig[\"retry\"]): RetryStrategy {\r\n if (!retry) return { shouldRetry: () => false, delayMs: () => 0 };\r\n if (\"shouldRetry\" in retry) return retry;\r\n // InlineRetryConfig\r\n const config = retry as InlineRetryConfig;\r\n if (config.on) {\r\n return new ConditionalRetry(\r\n config.maxAttempts ?? 3,\r\n config.backoffMs ?? 100,\r\n config.on,\r\n );\r\n }\r\n return new ExponentialBackoffRetry(\r\n config.maxAttempts ?? 3,\r\n config.backoffMs ?? 100,\r\n );\r\n }\r\n\r\n private delay(ms: number): Promise<void> {\r\n return new Promise((resolve) => setTimeout(resolve, ms));\r\n }\r\n}\r\n\r\n// ============= Errors =============\r\nexport class HttpError extends Error {\r\n status: number;\r\n code: string;\r\n response?: unknown;\r\n\r\n constructor(\r\n message: string,\r\n status: number,\r\n code: string,\r\n response?: unknown,\r\n ) {\r\n super(message);\r\n this.name = \"HttpError\";\r\n this.status = status;\r\n this.code = code;\r\n this.response = response;\r\n }\r\n}\r\n\r\nexport function isHttpError(error: unknown): error is HttpError {\r\n return error instanceof HttpError;\r\n}\r\n\r\n// ============= Factory =============\r\n\r\n/**\r\n * Factory function (Dependency Inversion — easier testing)\r\n */\r\nexport function createHttpClient(config?: HttpClientConfig): HttpClient {\r\n return new HttpClient(config);\r\n}\r\n","/**\n * WebSocket client with automatic reconnection, heartbeat, and plugin support.\n */\n\nimport type { WebSocketOptions, IWebSocketClient, RealtimeMessageEvent } from '../types/index.js';\nimport { PluginManager } from '../utils/index.js';\n\n/**\n * Check if running in Node.js environment\n */\nfunction isNode(): boolean {\n return (\n typeof window === 'undefined' &&\n typeof process !== 'undefined' &&\n process.versions?.node !== undefined\n );\n}\n\n/**\n * Base realtime client with common functionality\n */\nabstract class BaseRealtimeClient {\n protected url: string;\n protected options: WebSocketOptions;\n protected pluginManager: PluginManager;\n protected status: 'connecting' | 'open' | 'closing' | 'closed' = 'closed';\n protected reconnectAttempt = 0;\n protected reconnectTimer: any = null;\n protected heartbeatTimer: any = null;\n protected stats = {\n messagesSent: 0,\n messagesReceived: 0,\n connectionTime: 0,\n reconnectAttempts: 0,\n };\n private connectionStartTime = 0;\n private listeners = {\n open: new Set<(event: Event) => void>(),\n close: new Set<(event?: CloseEvent) => void>(),\n error: new Set<(event: Event) => void>(),\n message: new Set<(event: RealtimeMessageEvent) => void>(),\n };\n\n constructor(url: string, options: WebSocketOptions = {}) {\n this.url = url;\n this.options = options;\n this.pluginManager = new PluginManager();\n }\n\n protected updateStatus(status: 'connecting' | 'open' | 'closing' | 'closed'): void {\n this.status = status;\n if (status === 'open') {\n this.connectionStartTime = Date.now();\n } else if (status === 'closed' && this.connectionStartTime > 0) {\n this.stats.connectionTime += Date.now() - this.connectionStartTime;\n this.connectionStartTime = 0;\n }\n }\n\n protected emitOpen(event: Event): void {\n this.pluginManager.emit('websocket:open', this.url, event);\n for (const listener of this.listeners.open) {\n listener(event);\n }\n }\n\n protected emitClose(event?: CloseEvent): void {\n this.pluginManager.emit('websocket:close', this.url, event);\n for (const listener of this.listeners.close) {\n listener(event);\n }\n }\n\n protected emitError(event: Event | Error): void {\n let errorEvent: Event;\n if (event instanceof Error) {\n // Create a synthetic error event\n errorEvent = new Event('error');\n (errorEvent as any).error = event;\n } else {\n errorEvent = event;\n }\n this.pluginManager.emit('websocket:error', this.url, errorEvent);\n for (const listener of this.listeners.error) {\n listener(errorEvent);\n }\n }\n\n protected emitMessage(event: RealtimeMessageEvent): void {\n this.pluginManager.emit('websocket:message', this.url, event);\n for (const listener of this.listeners.message) {\n listener(event);\n }\n }\n\n onOpen(callback: (event: Event) => void): () => void {\n this.listeners.open.add(callback);\n return () => this.listeners.open.delete(callback);\n }\n\n onClose(callback: (event?: CloseEvent) => void): () => void {\n this.listeners.close.add(callback);\n return () => this.listeners.close.delete(callback);\n }\n\n onError(callback: (event: Event) => void): () => void {\n this.listeners.error.add(callback);\n return () => this.listeners.error.delete(callback);\n }\n\n onMessage<T = unknown>(callback: (event: RealtimeMessageEvent<T>) => void): () => void {\n // Type assertion needed because Set doesn't support generic type parameters\n this.listeners.message.add(callback as (event: RealtimeMessageEvent) => void);\n return () => this.listeners.message.delete(callback as (event: RealtimeMessageEvent) => void);\n }\n\n getStatus(): 'connecting' | 'open' | 'closing' | 'closed' {\n return this.status;\n }\n\n getStats() {\n return {\n ...this.stats,\n connectionTime: this.stats.connectionTime + \n (this.connectionStartTime > 0 ? Date.now() - this.connectionStartTime : 0),\n };\n }\n\n protected scheduleReconnect(): void {\n if (this.options.reconnect?.enabled === false) {\n return;\n }\n\n const maxAttempts = this.options.reconnect?.maxAttempts ?? Infinity;\n if (this.reconnectAttempt >= maxAttempts) {\n this.pluginManager.emit('websocket:reconnect:failed', this.url, this.reconnectAttempt);\n return;\n }\n\n const baseDelay = this.options.reconnect?.baseDelay ?? 1000;\n const maxDelay = this.options.reconnect?.maxDelay ?? 30000;\n const delay = Math.min(maxDelay, baseDelay * Math.pow(2, this.reconnectAttempt));\n \n this.reconnectAttempt++;\n this.stats.reconnectAttempts = this.reconnectAttempt;\n\n this.options.reconnect?.onReconnecting?.(this.reconnectAttempt, delay);\n this.pluginManager.emit('websocket:reconnecting', this.url, this.reconnectAttempt, delay);\n\n this.reconnectTimer = setTimeout(() => {\n this.connect().catch((err) => {\n this.emitError(err instanceof Error ? err : new Error(String(err)));\n });\n }, delay);\n }\n\n protected startHeartbeat(): void {\n if (!this.options.heartbeat) return;\n\n const interval = this.options.heartbeat.interval ?? 30000;\n const pingMessage = this.options.heartbeat.pingMessage ?? 'ping';\n const pongMessage = this.options.heartbeat.pongMessage ?? 'pong';\n\n let pongReceived = true;\n\n const checkPong = () => {\n if (!pongReceived) {\n this.pluginManager.emit('websocket:heartbeat:timeout', this.url);\n this.disconnect();\n return;\n }\n pongReceived = false;\n this.send(pingMessage);\n this.heartbeatTimer = setTimeout(checkPong, interval);\n };\n\n // Listen for pong messages\n const messageListener = (event: RealtimeMessageEvent) => {\n const data = event.raw;\n if (typeof data === 'string' && data === pongMessage) {\n pongReceived = true;\n }\n };\n\n this.onMessage(messageListener);\n this.heartbeatTimer = setTimeout(checkPong, interval);\n }\n\n protected stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearTimeout(this.heartbeatTimer);\n this.heartbeatTimer = null;\n }\n }\n\n protected cleanup(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n this.stopHeartbeat();\n }\n\n abstract connect(): Promise<void>;\n abstract disconnect(): void;\n abstract send(data: string | ArrayBuffer | Blob): void;\n}\n\n/**\n * Browser WebSocket client implementation\n */\nclass BrowserWebSocketClient extends BaseRealtimeClient implements IWebSocketClient {\n socket: WebSocket | null = null;\n private messageTypeListeners = new Map<string, Set<(data: unknown) => void>>();\n\n constructor(url: string, options: WebSocketOptions = {}) {\n super(url, options);\n }\n\n async connect(): Promise<void> {\n if (this.status === 'connecting' || this.status === 'open') {\n return;\n }\n\n this.updateStatus('connecting');\n this.pluginManager.emit('websocket:connect:start', this.url);\n\n return new Promise((resolve, reject) => {\n const timeout = this.options.timeout ?? 10000;\n const timeoutTimer = setTimeout(() => {\n this.updateStatus('closed');\n this.socket?.close();\n this.socket = null;\n const error = new Error(`WebSocket connection timeout after ${timeout}ms`);\n this.emitError(error);\n reject(error);\n }, timeout);\n\n try {\n this.socket = new WebSocket(this.url, this.options.protocols);\n\n this.socket.onopen = (event) => {\n clearTimeout(timeoutTimer);\n this.updateStatus('open');\n this.reconnectAttempt = 0;\n this.emitOpen(event);\n this.options.onOpen?.(event);\n this.startHeartbeat();\n this.pluginManager.emit('websocket:connect:success', this.url);\n resolve();\n };\n\n this.socket.onclose = (event) => {\n clearTimeout(timeoutTimer);\n this.updateStatus('closed');\n this.emitClose(event);\n this.options.onClose?.(event);\n this.stopHeartbeat();\n this.pluginManager.emit('websocket:disconnected', this.url, event.code, event.reason);\n \n // Schedule reconnect if not explicitly closed by user\n if (event.code !== 1000 && !event.wasClean) {\n this.scheduleReconnect();\n }\n };\n\n this.socket.onerror = (event) => {\n clearTimeout(timeoutTimer);\n this.updateStatus('closed');\n this.emitError(event);\n this.options.onError?.(event);\n this.pluginManager.emit('websocket:connect:error', this.url, event);\n reject(event);\n };\n\n this.socket.onmessage = (event) => {\n this.stats.messagesReceived++;\n const messageEvent: RealtimeMessageEvent = {\n data: this.tryParseData(event.data),\n raw: event.data,\n type: 'message',\n timestamp: Date.now(),\n };\n this.emitMessage(messageEvent);\n this.pluginManager.emit('websocket:message:received', this.url, messageEvent);\n };\n } catch (error) {\n clearTimeout(timeoutTimer);\n this.updateStatus('closed');\n this.pluginManager.emit('websocket:connect:error', this.url, error);\n reject(error);\n }\n });\n }\n\n disconnect(): void {\n this.cleanup();\n if (this.socket) {\n this.updateStatus('closing');\n this.socket.close(1000, 'Client disconnected');\n this.socket = null;\n this.updateStatus('closed');\n }\n }\n\n send(data: string | ArrayBuffer | Blob): void {\n if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {\n throw new Error('WebSocket is not connected');\n }\n this.socket.send(data);\n this.stats.messagesSent++;\n this.pluginManager.emit('websocket:message:sent', this.url, data);\n }\n\n sendJson(data: unknown): void {\n this.send(JSON.stringify(data));\n }\n\n onMessageType<T = unknown>(type: string, callback: (data: T) => void): () => void {\n if (!this.messageTypeListeners.has(type)) {\n this.messageTypeListeners.set(type, new Set());\n }\n this.messageTypeListeners.get(type)!.add(callback as (data: unknown) => void);\n \n return () => {\n const listeners = this.messageTypeListeners.get(type);\n if (listeners) {\n listeners.delete(callback as (data: unknown) => void);\n if (listeners.size === 0) {\n this.messageTypeListeners.delete(type);\n }\n }\n };\n }\n\n private tryParseData(data: string | ArrayBuffer | Blob): unknown {\n if (typeof data === 'string') {\n try {\n return JSON.parse(data);\n } catch {\n return data;\n }\n }\n return data;\n }\n}\n\n/**\n * Node.js WebSocket client implementation (requires 'ws' package)\n */\nclass NodeWebSocketClient extends BrowserWebSocketClient {\n async connect(): Promise<void> {\n if (isNode()) {\n try {\n // Security: Optional dependency 'ws' for Node.js WebSocket support\n // This is a dynamic import that only loads if the package is installed\n // @ts-ignore - optional dependency\n const { default: _WebSocket } = await import('ws');\n // Override socket creation\n // Note: This is a simplified implementation\n // In a full implementation, we'd need to handle the WebSocket constructor differently\n return super.connect();\n } catch (error) {\n throw new Error(\n 'WebSocket client for Node.js requires the \"ws\" package. Please install it: npm install ws'\n );\n }\n }\n return super.connect();\n }\n}\n\n/**\n * Create a WebSocket client appropriate for the current environment\n */\nexport function createWebSocketClient(url: string, options: WebSocketOptions = {}): IWebSocketClient {\n if (isNode()) {\n return new NodeWebSocketClient(url, options);\n }\n return new BrowserWebSocketClient(url, options);\n}","/**\n * Server-Sent Events (SSE) client with automatic reconnection and plugin support.\n */\n\nimport type { SSEOptions, ISSEClient, RealtimeMessageEvent } from '../types/index.js';\nimport { PluginManager } from '../utils/index.js';\n\n/**\n * Check if running in Node.js environment\n */\nfunction isNode(): boolean {\n return (\n typeof window === 'undefined' &&\n typeof process !== 'undefined' &&\n process.versions?.node !== undefined\n );\n}\n\n/**\n * Browser SSE client implementation using EventSource\n */\nclass BrowserSSEClient implements ISSEClient {\n private url: string;\n private options: SSEOptions;\n private _source: EventSource | null = null;\n private pluginManager: PluginManager;\n private status: 'connecting' | 'open' | 'closing' | 'closed' = 'closed';\n private reconnectAttempt = 0;\n private reconnectTimer: any = null;\n private stats = {\n messagesSent: 0, // SSE is receive-only\n messagesReceived: 0,\n connectionTime: 0,\n reconnectAttempts: 0,\n };\n private connectionStartTime = 0;\n private listeners = {\n open: new Set<(event: Event) => void>(),\n close: new Set<() => void>(),\n error: new Set<(event: Event) => void>(),\n message: new Set<(event: RealtimeMessageEvent) => void>(),\n event: new Map<string, Set<(data: unknown) => void>>(),\n };\n private _lastEventId: string | null = null;\n\n constructor(url: string, options: SSEOptions = {}) {\n this.url = url;\n this.options = options;\n this.pluginManager = new PluginManager();\n }\n\n private updateStatus(status: 'connecting' | 'open' | 'closing' | 'closed'): void {\n this.status = status;\n if (status === 'open') {\n this.connectionStartTime = Date.now();\n } else if (status === 'closed' && this.connectionStartTime > 0) {\n this.stats.connectionTime += Date.now() - this.connectionStartTime;\n this.connectionStartTime = 0;\n }\n }\n\n async connect(): Promise<void> {\n if (this.status === 'connecting' || this.status === 'open') {\n return;\n }\n\n this.updateStatus('connecting');\n this.pluginManager.emit('sse:connect:start', this.url);\n\n return new Promise((resolve, reject) => {\n const timeout = this.options.timeout ?? 10000;\n const timeoutTimer = setTimeout(() => {\n this.updateStatus('closed');\n this._source?.close();\n this._source = null;\n const error = new Error(`SSE connection timeout after ${timeout}ms`);\n this.emitError(error);\n reject(error);\n }, timeout);\n\n try {\n // EventSource doesn't support custom headers or POST requests in standard API\n // For advanced features, we'd need to use fetch with streaming\n this._source = new EventSource(this.url);\n\n this._source.onopen = (event) => {\n clearTimeout(timeoutTimer);\n this.updateStatus('open');\n this.reconnectAttempt = 0;\n this.emitOpen(event);\n this.options.onOpen?.(event);\n this.pluginManager.emit('sse:connect:success', this.url);\n resolve();\n };\n\n this._source.onerror = (event) => {\n // EventSource doesn't provide close event, only error\n // We'll treat certain error states as disconnections\n clearTimeout(timeoutTimer);\n this.updateStatus('closed');\n this.emitError(event);\n this.options.onError?.(event);\n this.pluginManager.emit('sse:connect:error', this.url, event);\n \n if (this._source?.readyState === EventSource.CLOSED) {\n this.emitClose();\n this.options.onClose?.();\n this.scheduleReconnect();\n }\n reject(event);\n };\n\n // Listen for messages\n this._source.onmessage = (event) => {\n this.stats.messagesReceived++;\n this._lastEventId = event.lastEventId || this._lastEventId;\n \n const messageEvent: RealtimeMessageEvent = {\n data: this.tryParseData(event.data),\n raw: event.data,\n type: event.type || 'message',\n timestamp: Date.now(),\n };\n \n this.emitMessage(messageEvent);\n this.pluginManager.emit('sse:message:received', this.url, messageEvent);\n \n // Also emit to event-specific listeners\n const eventType = event.type || 'message';\n const eventListeners = this.listeners.event.get(eventType);\n if (eventListeners) {\n for (const listener of eventListeners) {\n listener(messageEvent.data);\n }\n }\n };\n\n // Listen for named events\n this._source.addEventListener = this._source.addEventListener.bind(this._source);\n } catch (error) {\n clearTimeout(timeoutTimer);\n this.updateStatus('closed');\n this.pluginManager.emit('sse:connect:error', this.url, error);\n reject(error);\n }\n });\n }\n\n disconnect(): void {\n this.cleanup();\n if (this._source) {\n this.updateStatus('closing');\n this._source.close();\n this._source = null;\n this.updateStatus('closed');\n this.emitClose();\n this.options.onClose?.();\n }\n }\n\n send(_data: string | ArrayBuffer | Blob): void {\n throw new Error('SSE is a receive-only protocol. Use HTTP requests to send data to server.');\n }\n\n onMessage<T = unknown>(callback: (event: RealtimeMessageEvent<T>) => void): () => void {\n this.listeners.message.add(callback as (event: RealtimeMessageEvent) => void);\n return () => this.listeners.message.delete(callback as (event: RealtimeMessageEvent) => void);\n }\n\n onOpen(callback: (event: Event) => void): () => void {\n this.listeners.open.add(callback);\n return () => this.listeners.open.delete(callback);\n }\n\n onClose(callback: () => void): () => void {\n this.listeners.close.add(callback);\n return () => this.listeners.close.delete(callback);\n }\n\n onError(callback: (event: Event) => void): () => void {\n this.listeners.error.add(callback);\n return () => this.listeners.error.delete(callback);\n }\n\n onEvent<T = unknown>(event: string, callback: (data: T) => void): () => void {\n if (!this.listeners.event.has(event)) {\n this.listeners.event.set(event, new Set());\n }\n this.listeners.event.get(event)!.add(callback as (data: unknown) => void);\n \n // Also set up EventSource listener if connected\n if (this._source && !(this._source as any)[`on${event}`]) {\n this._source.addEventListener(event, (e: any) => {\n const messageEvent: RealtimeMessageEvent = {\n data: this.tryParseData(e.data),\n raw: e.data,\n type: event,\n timestamp: Date.now(),\n };\n callback(messageEvent.data as T);\n });\n }\n \n return () => {\n const listeners = this.listeners.event.get(event);\n if (listeners) {\n listeners.delete(callback as (data: unknown) => void);\n if (listeners.size === 0) {\n this.listeners.event.delete(event);\n }\n }\n };\n }\n\n getStatus(): 'connecting' | 'open' | 'closing' | 'closed' {\n return this.status;\n }\n\n getStats() {\n return {\n ...this.stats,\n connectionTime: this.stats.connectionTime + \n (this.connectionStartTime > 0 ? Date.now() - this.connectionStartTime : 0),\n };\n }\n\n get lastEventId(): string | null {\n return this._lastEventId;\n }\n\n get source(): EventSource | null {\n return this._source;\n }\n\n private emitOpen(event: Event): void {\n this.pluginManager.emit('sse:open', this.url, event);\n for (const listener of this.listeners.open) {\n listener(event);\n }\n }\n\n private emitClose(): void {\n this.pluginManager.emit('sse:close', this.url);\n for (const listener of this.listeners.close) {\n listener();\n }\n }\n\n private emitError(event: Event | Error): void {\n let errorEvent: Event;\n if (event instanceof Error) {\n // Create a synthetic error event\n errorEvent = new Event('error');\n (errorEvent as any).error = event;\n } else {\n errorEvent = event;\n }\n this.pluginManager.emit('sse:error', this.url, errorEvent);\n for (const listener of this.listeners.error) {\n listener(errorEvent);\n }\n }\n\n private emitMessage(event: RealtimeMessageEvent): void {\n this.pluginManager.emit('sse:message', this.url, event);\n for (const listener of this.listeners.message) {\n listener(event);\n }\n }\n\n private scheduleReconnect(): void {\n if (this.options.reconnect?.enabled === false) {\n return;\n }\n\n const maxAttempts = this.options.reconnect?.maxAttempts ?? Infinity;\n if (this.reconnectAttempt >= maxAttempts) {\n this.pluginManager.emit('sse:reconnect:failed', this.url, this.reconnectAttempt);\n return;\n }\n\n const baseDelay = this.options.reconnect?.baseDelay ?? 1000;\n const maxDelay = this.options.reconnect?.maxDelay ?? 30000;\n const delay = Math.min(maxDelay, baseDelay * Math.pow(2, this.reconnectAttempt));\n \n this.reconnectAttempt++;\n this.stats.reconnectAttempts = this.reconnectAttempt;\n\n this.options.reconnect?.onReconnecting?.(this.reconnectAttempt, delay);\n this.pluginManager.emit('sse:reconnecting', this.url, this.reconnectAttempt, delay);\n\n this.reconnectTimer = setTimeout(() => {\n this.connect().catch((err) => {\n this.emitError(err instanceof Error ? err : new Error(String(err)));\n });\n }, delay);\n }\n\n private cleanup(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n\n private tryParseData(data: string): unknown {\n try {\n return JSON.parse(data);\n } catch {\n return data;\n }\n }\n}\n\n/**\n * Node.js SSE client implementation using fetch with streaming\n * Note: This is a basic implementation - EventSource is not available in Node.js\n */\nclass NodeSSEClient extends BrowserSSEClient {\n async connect(): Promise<void> {\n if (isNode()) {\n throw new Error(\n 'SSE client for Node.js requires a polyfill or custom implementation. ' +\n 'Consider using a library like \"eventsource\" or implement using fetch with streaming.'\n );\n }\n return super.connect();\n }\n}\n\n/**\n * Create an SSE client appropriate for the current environment\n */\nexport function createSSEClient(url: string, options: SSEOptions = {}): ISSEClient {\n if (isNode()) {\n return new NodeSSEClient(url, options);\n }\n return new BrowserSSEClient(url, options);\n}","/**\n * Real-time plugin for Nexa\n * Provides WebSocket and SSE integration with the plugin system\n */\n\nimport type { Plugin, PluginManager } from '../utils/index.js';\n\n/**\n * Real-time plugin that adds WebSocket and SSE event listeners to the plugin manager\n */\nexport class RealtimePlugin implements Plugin {\n name = 'realtime';\n\n setup(manager: PluginManager): void {\n // Add event listeners for real-time communication\n manager.on('websocket:connect:start', (...args: unknown[]) => {\n const url = args[0] as string;\n manager.emit('realtime:connect:start', 'websocket', url);\n });\n\n manager.on('websocket:connect:success', (...args: unknown[]) => {\n const url = args[0] as string;\n manager.emit('realtime:connect:success', 'websocket', url);\n });\n\n manager.on('websocket:message:received', (...args: unknown[]) => {\n const url = args[0] as string;\n const message = args[1] as unknown;\n manager.emit('realtime:message', 'websocket', url, message);\n });\n\n manager.on('sse:connect:start', (...args: unknown[]) => {\n const url = args[0] as string;\n manager.emit('realtime:connect:start', 'sse', url);\n });\n\n manager.on('sse:connect:success', (...args: unknown[]) => {\n const url = args[0] as string;\n manager.emit('realtime:connect:success', 'sse', url);\n });\n\n manager.on('sse:message:received', (...args: unknown[]) => {\n const url = args[0] as string;\n const message = args[1] as unknown;\n manager.emit('realtime:message', 'sse', url, message);\n });\n }\n}\n\n/**\n * Create a realtime plugin instance\n */\nexport function createRealtimePlugin(): RealtimePlugin {\n return new RealtimePlugin();\n}","/**\n * Mocking utilities for Nexa HTTP Client.\n * Provides axios-mock-adapter-like functionality for testing.\n */\n\nimport type { IHttpClient, HttpRequestConfig } from '../types/index.js';\n\n/**\n * Configuration for a mocked response\n */\nexport interface MockResponse {\n /** HTTP status code (default: 200) */\n status?: number;\n /** HTTP status text (default: 'OK') */\n statusText?: string;\n /** Response headers (default: { 'content-type': 'application/json' }) */\n headers?: Record<string, string>;\n /** Response body (will be JSON.stringified if object/array) */\n data?: any;\n /** Optional delay in milliseconds before responding */\n delay?: number;\n /** Throw a network error instead of returning a response */\n networkError?: boolean;\n /** Error message for network error */\n errorMessage?: string;\n}\n\n/**\n * Mock route configuration\n */\ninterface MockRoute {\n /** HTTP method (uppercase) */\n method: string;\n /** URL pattern (string or RegExp) */\n urlPattern: string | RegExp;\n /** Response handler */\n response: MockResponse | ((config: HttpRequestConfig) => MockResponse | Promise<MockResponse>);\n /** Number of times this route has been called */\n timesCalled: number;\n /** Maximum number of times this route should match (0 = unlimited) */\n times?: number;\n}\n\n/**\n * Options for creating a mock client\n */\nexport interface MockClientOptions {\n /** Base URL to match against (optional) */\n baseURL?: string;\n /** Default delay for all responses (optional) */\n delay?: number;\n /** Whether to pass through unmatched requests to original adapter (default: false) */\n passthrough?: boolean;\n}\n\n/**\n * Route builder returned by onGet(), onPost(), etc.\n * Allows fluent API like mockAdapter.onGet('/users').reply(200, users)\n */\nclass RouteBuilder {\n private adapter: MockAdapter;\n private method: string;\n private urlPattern: string | RegExp;\n\n constructor(adapter: MockAdapter, method: string, urlPattern: string | RegExp) {\n this.adapter = adapter;\n this.method = method;\n this.urlPattern = urlPattern;\n }\n\n /**\n * Configure a response for this route\n */\n reply(status: number, data?: any, headers?: Record<string, string>): MockAdapter;\n reply(response: MockResponse): MockAdapter;\n reply(arg1: number | MockResponse, data?: any, headers?: Record<string, string>): MockAdapter {\n let response: MockResponse;\n if (typeof arg1 === 'number') {\n response = { status: arg1, data, headers };\n } else {\n response = arg1;\n }\n this.adapter.addRoute(this.method, this.urlPattern, response);\n return this.adapter;\n }\n\n /**\n * Configure a response that will only be used once\n */\n replyOnce(status: number, data?: any, headers?: Record<string, string>): MockAdapter;\n replyOnce(response: MockResponse): MockAdapter;\n replyOnce(arg1: number | MockResponse, data?: any, headers?: Record<string, string>): MockAdapter {\n let response: MockResponse;\n if (typeof arg1 === 'number') {\n response = { status: arg1, data, headers };\n } else {\n response = arg1;\n }\n this.adapter.addRoute(this.method, this.urlPattern, response, { times: 1 });\n return this.adapter;\n }\n\n /**\n * Configure a network error for this route\n */\n networkError(errorMessage: string = 'Network Error'): MockAdapter {\n this.adapter.addRoute(this.method, this.urlPattern, {\n networkError: true,\n errorMessage,\n });\n return this.adapter;\n }\n\n /**\n * Configure a timeout error for this route\n */\n timeout(): MockAdapter {\n this.adapter.addRoute(this.method, this.urlPattern, {\n status: 408,\n statusText: 'Request Timeout',\n });\n return this.adapter;\n }\n}\n\n/**\n * Mock adapter that intercepts HTTP requests and returns configured responses.\n * Similar to axios-mock-adapter.\n */\nexport class MockAdapter {\n private routes: MockRoute[] = [];\n private originalAdapter?: (input: RequestInfo, init?: RequestInit) => Promise<Response>;\n private mockClient: IHttpClient;\n private options: MockClientOptions;\n private defaultResponse: MockResponse = {\n status: 200,\n statusText: 'OK',\n headers: { 'content-type': 'application/json' },\n };\n\n /**\n * Create a new MockAdapter and attach it to a client.\n * The original client is not modified. Instead, a new client with the mock adapter is created.\n * Use the `client` property to access the mock-enabled client.\n */\n constructor(client: IHttpClient, options: MockClientOptions = {}) {\n this.options = options;\n \n // Create mock adapter function\n const adapter = this.createAdapter();\n \n // Try to extend the client with the mock adapter\n // We assume the client has an `extend` method (HttpClient does)\n if (typeof (client as any).extend === 'function') {\n this.mockClient = (client as any).extend({ adapter });\n } else {\n // Fallback: create a new HttpClient with mock adapter\n // This requires importing HttpClient, but we want to avoid circular dependencies\n // For now, throw an error suggesting to use HttpClient instance\n throw new Error('MockAdapter requires an HttpClient instance with extend() method');\n }\n }\n\n /**\n * Get the mock-enabled client\n */\n get client(): IHttpClient {\n return this.mockClient;\n }\n\n /**\n * Create a mock adapter that can be used as a standalone adapter.\n * This adapter can be set on any HttpClient via config.adapter.\n */\n createAdapter(): (input: RequestInfo, init?: RequestInit) => Promise<Response> {\n return async (input: RequestInfo, init?: RequestInit): Promise<Response> => {\n // Convert RequestInfo to URL string\n const url = typeof input === 'string' ? input : input.url;\n \n // Convert RequestInit to HttpRequestConfig-like object\n const method = init?.method || 'GET';\n const config: HttpRequestConfig = {\n url,\n method: method as any,\n headers: init?.headers as Record<string, string>,\n body: init?.body,\n signal: init?.signal ?? undefined,\n };\n\n // Find matching route\n const route = this.findMatchingRoute(method, url);\n \n if (!route) {\n if (this.options.passthrough) {\n // Use original adapter or global fetch\n const adapter = this.originalAdapter || fetch;\n return adapter(input, init);\n }\n // No route matched and no passthrough - return 404\n return new Response(JSON.stringify({ error: 'No mock route matched' }), {\n status: 404,\n statusText: 'Not Found',\n headers: { 'content-type': 'application/json' },\n });\n }\n\n route.timesCalled++;\n\n // Get response configuration\n let responseConfig: MockResponse;\n if (typeof route.response === 'function') {\n responseConfig = await route.response(config);\n } else {\n responseConfig = route.response;\n }\n\n // Handle network error\n if (responseConfig.networkError) {\n throw new TypeError(responseConfig.errorMessage || 'Network Error');\n }\n\n // Apply default values - filter out undefined properties from responseConfig\n const filteredConfig = Object.fromEntries(\n Object.entries(responseConfig).filter(([_, v]) => v !== undefined)\n );\n const finalResponse: MockResponse = {\n ...this.defaultResponse,\n ...filteredConfig,\n };\n // Determine if user explicitly provided content-type\n const userProvidedContentType = responseConfig.headers && 'content-type' in responseConfig.headers;\n if (!userProvidedContentType) {\n // Set appropriate content-type based on data type\n if (finalResponse.data instanceof Uint8Array || ArrayBuffer.isView(finalResponse.data)) {\n finalResponse.headers = { ...finalResponse.headers, 'content-type': 'application/octet-stream' };\n } else if (finalResponse.data && typeof finalResponse.data === 'object') {\n finalResponse.headers = { ...finalResponse.headers, 'content-type': 'application/json' };\n }\n }\n\n // Apply delay if specified, with abort support\n if (finalResponse.delay || this.options.delay) {\n const delay = finalResponse.delay ?? this.options.delay;\n if (delay && delay > 0) {\n const signal = init?.signal;\n if (signal) {\n // Check if already aborted\n signal.throwIfAborted();\n // Wait for delay or abort\n await new Promise<void>((resolve, reject) => {\n const timeoutId = setTimeout(resolve, delay);\n const onAbort = () => {\n clearTimeout(timeoutId);\n reject(new DOMException('Aborted', 'AbortError'));\n };\n signal.addEventListener('abort', onAbort, { once: true });\n // Cleanup on timeout completion\n setTimeout(() => {\n signal.removeEventListener('abort', onAbort);\n }, delay);\n });\n } else {\n // No signal, just delay\n await new Promise(resolve => setTimeout(resolve, delay));\n }\n }\n }\n\n // Create and adjust headers\n const headers = new Headers(finalResponse.headers);\n const data = finalResponse.data;\n \n\n \n // Adjust for status codes that shouldn't have a body\n if (finalResponse.status === 204 || finalResponse.status === 205) {\n // 204 No Content and 205 Reset Content must not have a body\n // Remove content-type header for these statuses\n headers.delete('content-type');\n }\n\n // Prepare body\n let body: string;\n if (data === undefined || data === null) {\n body = '';\n } else if (typeof data === 'string') {\n body = data;\n } else if (data instanceof Uint8Array || ArrayBuffer.isView(data)) {\n // Convert to ArrayBuffer for Response constructor\n let buffer: ArrayBuffer;\n if (data.buffer instanceof ArrayBuffer) {\n buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);\n } else {\n // SharedArrayBuffer - copy to a new ArrayBuffer\n const uint8 = new Uint8Array(data.byteLength);\n uint8.set(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));\n buffer = uint8.buffer;\n }\n return new Response(buffer, {\n status: finalResponse.status,\n statusText: finalResponse.statusText,\n headers,\n });\n } else {\n body = JSON.stringify(data);\n }\n\n // Adjust body for status codes that shouldn't have a body\n let responseBody: string | null = body;\n if (finalResponse.status === 204 || finalResponse.status === 205) {\n // 204 No Content and 205 Reset Content must not have a body\n responseBody = null;\n } else if (responseBody === '') {\n // Empty string body can be set to null to avoid issues\n responseBody = null;\n }\n\n // Remove content-type header if there's no body\n if (responseBody === null) {\n headers.delete('content-type');\n }\n\n return new Response(responseBody, {\n status: finalResponse.status,\n statusText: finalResponse.statusText,\n headers,\n });\n };\n }\n\n /**\n * Add a route for GET requests\n */\n onGet(urlPattern: string | RegExp): RouteBuilder {\n return new RouteBuilder(this, 'GET', urlPattern);\n }\n\n /**\n * Add a route for POST requests\n */\n onPost(urlPattern: string | RegExp): RouteBuilder {\n return new RouteBuilder(this, 'POST', urlPattern);\n }\n\n /**\n * Add a route for PUT requests\n */\n onPut(urlPattern: string | RegExp): RouteBuilder {\n return new RouteBuilder(this, 'PUT', urlPattern);\n }\n\n /**\n * Add a route for PATCH requests\n */\n onPatch(urlPattern: string | RegExp): RouteBuilder {\n return new RouteBuilder(this, 'PATCH', urlPattern);\n }\n\n /**\n * Add a route for DELETE requests\n */\n onDelete(urlPattern: string | RegExp): RouteBuilder {\n return new RouteBuilder(this, 'DELETE', urlPattern);\n }\n\n /**\n * Add a route for any HTTP method\n */\n onAny(urlPattern: string | RegExp): RouteBuilder {\n return new RouteBuilder(this, 'ANY', urlPattern);\n }\n\n /**\n * Add a route with a specific HTTP method and response\n */\n addRoute(\n method: string,\n urlPattern: string | RegExp,\n response: MockResponse | ((config: HttpRequestConfig) => MockResponse | Promise<MockResponse>),\n options?: { times?: number }\n ): this {\n this.routes.push({\n method: method.toUpperCase(),\n urlPattern,\n response,\n timesCalled: 0,\n times: options?.times,\n });\n return this;\n }\n\n /**\n * Reset all mock routes\n */\n reset(): void {\n this.routes = [];\n }\n\n /**\n * Restore original adapter (if any)\n */\n restore(): void {\n // Currently, we don't modify the original client, so nothing to restore\n }\n\n /**\n * Helper to create a response configuration\n */\n static reply(status: number, data?: any, headers?: Record<string, string>): MockResponse {\n return { status, data, headers };\n }\n\n /**\n * Helper to create a network error response\n */\n static networkError(message: string = 'Network Error'): MockResponse {\n return { networkError: true, errorMessage: message };\n }\n\n private findMatchingRoute(method: string, url: string): MockRoute | null {\n // Normalize URL by removing baseURL if specified\n let normalizedUrl = url;\n if (this.options.baseURL && url.startsWith(this.options.baseURL)) {\n normalizedUrl = url.slice(this.options.baseURL.length);\n } else {\n // If URL is absolute (contains ://), extract pathname + search\n // This allows matching patterns like '/users' against 'https://api.example.com/users'\n try {\n if (normalizedUrl.includes('://')) {\n const urlObj = new URL(normalizedUrl);\n normalizedUrl = urlObj.pathname + urlObj.search;\n }\n } catch {\n // URL parsing failed, use as-is\n }\n }\n\n for (const route of this.routes) {\n // Skip routes that have reached their call limit\n if (route.times && route.timesCalled >= route.times) {\n continue;\n }\n\n // Check method\n if (route.method !== 'ANY' && route.method !== method.toUpperCase()) {\n continue;\n }\n\n // Check URL pattern\n let matches = false;\n if (typeof route.urlPattern === 'string') {\n // Simple string match (exact or startsWith)\n matches = normalizedUrl === route.urlPattern || normalizedUrl.startsWith(route.urlPattern);\n } else if (route.urlPattern instanceof RegExp) {\n matches = route.urlPattern.test(normalizedUrl);\n }\n\n if (matches) {\n return route;\n }\n }\n\n return null;\n }\n}\n\n/**\n * Create a mock client with axios-mock-adapter-like API\n * \n * @example\n * ```typescript\n * import { createHttpClient } from '@bereasoftware/nexa';\n * import { createMockClient } from '@bereasoftware/nexa/testing';\n * \n * const client = createHttpClient({ baseURL: 'https://api.example.com' });\n * const mockClient = createMockClient(client);\n * \n * mockClient.onGet('/users').reply(200, [{ id: 1 }]);\n * mockClient.onPost('/users').reply(201, { id: 2 });\n * \n * // Use mockClient.client for making requests\n * const result = await mockClient.client.get('/users');\n * ```\n */\nexport function createMockClient(client: IHttpClient, options: MockClientOptions = {}): MockAdapter {\n return new MockAdapter(client, options);\n}"],"mappings":"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"}
1
+ {"version":3,"file":"nexa.iife.js","names":[],"sources":["../src/types/index.ts","../src/utils/index.ts","../src/http-client/http-client.ts","../src/realtime/websocket-client.ts","../src/realtime/sse-client.ts","../src/realtime/plugin.ts","../src/dev-overlay/tracker.ts","../src/dev-overlay/overlay.ts","../src/dev-overlay/index.ts"],"sourcesContent":["/**\n * HTTP Client Plugin - Type Definitions\n * Combines fetch power + axios convenience with SOLID principles\n */\n\n// ============= Result Type (Either monad) =============\n/**\n * Represents a successful or failed result\n * Allows for type-safe error handling without exceptions\n */\nexport type Result<T, E = Error> =\n | { ok: true; value: T }\n | { ok: false; error: E }\n\nexport const Ok = <T>(value: T): Result<T> => ({ ok: true, value })\nexport const Err = <E>(error: E): Result<never, E> => ({ ok: false, error })\n\n// ============= HTTP Request/Response =============\nexport interface HttpRequest {\n url: string\n method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS'\n headers?: Record<string, string>\n body?: unknown\n query?: Record<string, string | number | boolean>\n params?: Record<string, string | number>\n timeout?: HttpTimeout\n signal?: AbortSignal\n /**\n * Controls cookie/credential policy for CORS requests. Same as fetch API.\n * 'omit' | 'same-origin' | 'include'\n */\n credentials?: RequestCredentials\n /**\n * Custom adapter for this request (same signature as fetch).\n */\n adapter?: (input: RequestInfo, init?: RequestInit) => Promise<Response>\n /**\n * If true (default), automatically converts body to FormData if files are detected.\n */\n autoFormData?: boolean\n /**\n * Transport layer to use for this request.\n * Overrides global transport setting.\n */\n transport?: 'fetch' | 'node' | 'http2' | 'deno' | 'bun' | 'cloudflare'\n /**\n * Node.js specific transport options for this request.\n * Overrides global nodeOptions.\n */\n nodeOptions?: NodeTransportOptions\n}\n\nexport interface HttpResponse<T = unknown> {\n status: number\n statusText: string\n headers: Headers\n data: T\n request: HttpRequest\n duration: number\n}\n\nexport interface HttpErrorDetails {\n message: string\n status?: number\n statusText?: string\n code?: string\n originalError?: unknown\n /**\n * The HTTP request that caused the error (available for both network and HTTP errors)\n */\n request?: HttpRequest\n /**\n * The HTTP response if the request reached the server and received a response (HTTP errors only)\n */\n response?: HttpResponse<unknown>\n /**\n * The configuration used for the request\n */\n config?: HttpRequestConfig\n}\n\n// ============= Progress =============\nexport interface ProgressEvent {\n loaded: number\n total: number\n percent: number\n}\n\n// ============= Lifecycle Hooks =============\nexport interface RequestHooks<T = unknown> {\n onStart?: (request: HttpRequest) => void\n onSuccess?: (response: HttpResponse<T>) => void\n onError?: (error: HttpErrorDetails) => void\n onFinally?: () => void\n onRetry?: (attempt: number, error: HttpErrorDetails) => void\n}\n\n// ============= Interceptor Pattern (Open/Closed) =============\nexport interface RequestInterceptor {\n onRequest(request: HttpRequest): HttpRequest | Promise<HttpRequest>\n onError?(\n error: HttpErrorDetails,\n ): HttpErrorDetails | Promise<HttpErrorDetails>\n}\n\nexport interface ResponseInterceptor {\n onResponse<T = unknown>(\n response: HttpResponse<T>,\n ): HttpResponse<T> | Promise<HttpResponse<T>>\n onError?(\n error: HttpErrorDetails,\n ): HttpErrorDetails | Promise<HttpErrorDetails>\n}\n\n// ============= Retry Strategy (Strategy Pattern) =============\nexport type RetryCondition = (\n error: HttpErrorDetails,\n attempt: number,\n) => boolean\n\nexport interface RetryStrategy {\n shouldRetry(attempt: number, error: HttpErrorDetails): boolean\n delayMs(attempt: number): number\n}\n\nexport interface InlineRetryConfig {\n maxAttempts?: number\n backoffMs?: number\n on?: RetryCondition\n}\n\n// ============= Node.js Transport Options =============\n/**\n * Node.js specific transport configuration for HTTP/1.1 and HTTP/2.\n */\nexport interface NodeTransportOptions {\n /**\n * Enable keep-alive connections. Default: true\n */\n keepAlive?: boolean\n /**\n * Maximum number of sockets to allow per host. Default: 50\n */\n maxSockets?: number\n /**\n * Maximum number of sockets to leave open in a free state. Default: 10\n */\n maxFreeSockets?: number\n /**\n * Maximum number of requests per socket. Default: 0 (unlimited)\n */\n maxRequestsPerSocket?: number\n /**\n * Socket timeout in milliseconds. Default: 60000 (60 seconds)\n */\n timeout?: number\n /**\n * Enable HTTP/2 protocol (only when transport is 'http2').\n */\n http2?: boolean\n /**\n * HTTP/2 specific settings.\n */\n http2Settings?: Record<string, unknown>\n}\n\n// ============= Cache Strategy (Strategy Pattern) =============\nexport interface CacheStrategy {\n get(key: string): unknown | null\n set(key: string, value: unknown, ttlMs?: number): void\n clear(): void\n has(key: string): boolean\n}\n\n// ============= Validation & Transform (Processing Pipeline) =============\nexport interface Validator {\n validate(data: unknown): Result<unknown, HttpErrorDetails>\n}\n\nexport interface Transformer {\n transform(data: unknown): unknown\n}\n\n// ============= Response Type =============\nexport type ResponseType =\n | 'json'\n | 'text'\n | 'blob'\n | 'arrayBuffer'\n | 'formData'\n | 'stream'\n | 'auto'\n\n// ============= Timeout Configuration =============\n/**\n * Timeout configuration for HTTP requests.\n * - number: total timeout for the entire request (connection + response)\n * - object: differentiated timeouts for connection and response phases\n */\nexport type HttpTimeout =\n | number\n | {\n /**\n * Maximum time to establish connection (TCP/TLS handshake) in milliseconds.\n * If not specified, no connection timeout is applied.\n */\n connection?: number\n /**\n * Maximum time to receive complete response (after connection is established) in milliseconds.\n * If not specified, no response timeout is applied.\n */\n response?: number\n /**\n * Total timeout for the entire request (connection + response) in milliseconds.\n * If specified, overrides both connection and response timeouts.\n * Provided for backward compatibility and convenience.\n */\n total?: number\n }\n\n// ============= Request Configuration =============\nexport interface HttpRequestConfig extends HttpRequest {\n retry?: RetryStrategy | InlineRetryConfig\n timeout?: HttpTimeout\n validate?: Validator\n transform?: Transformer\n cache?: { enabled: boolean; ttlMs?: number }\n responseType?: ResponseType\n hooks?: RequestHooks\n onUploadProgress?: (event: ProgressEvent) => void\n onDownloadProgress?: (event: ProgressEvent) => void\n /**\n * Axios compatibility: if true, sets credentials: 'include'; if false, credentials: 'same-origin'.\n * If credentials is also specified, this field is ignored.\n */\n withCredentials?: boolean\n /**\n * Allows using a custom adapter for the request (same signature as fetch).\n * Useful for mocks, tests, or special environments.\n */\n adapter?: (input: RequestInfo, init?: RequestInit) => Promise<Response>\n /**\n * Allows transforming the body before serializing and sending it. Similar to axios.transformRequest.\n * Can be a function or an array of functions.\n */\n transformRequest?:\n | ((data: unknown, headers: Record<string, string>) => unknown)\n | Array<(data: unknown, headers: Record<string, string>) => unknown>\n /**\n * Enable debug logging for this request. Overrides global debug setting.\n */\n debug?: boolean | 'verbose'\n /**\n * Custom logger function for this request. Overrides global logger.\n */\n logger?: (message: string, data?: unknown) => void\n /**\n * Transport layer to use for this request.\n * Overrides global transport setting.\n */\n transport?: 'fetch' | 'node' | 'http2' | 'deno' | 'bun' | 'cloudflare'\n /**\n * Node.js specific transport options for this request.\n * Overrides global nodeOptions.\n */\n nodeOptions?: NodeTransportOptions\n /**\n * If true (default), automatically converts body to FormData if files are detected.\n */\n autoFormData?: boolean\n}\n\n// ============= Pagination =============\nexport interface PaginateOptions<T> {\n /** Extract items from a response page */\n getItems: (data: T) => unknown[]\n /** Return the config for the next page, or null to stop */\n getNextPage: (\n data: T,\n currentConfig: Omit<HttpRequestConfig, 'url' | 'method'>,\n ) => Omit<HttpRequestConfig, 'url' | 'method'> | null\n}\n\n// ============= Polling =============\nexport interface PollOptions<T> {\n /** Interval between polls in ms */\n intervalMs: number\n /** Max number of polls (0 = unlimited) */\n maxAttempts?: number\n /** Stop polling when this returns true */\n until: (data: T) => boolean\n /** Called on each successful poll */\n onPoll?: (data: T, attempt: number) => void\n}\n\n// ============= HTTP Client Interface (Dependency Inversion) =============\nexport interface IHttpClient {\n request<T = unknown>(\n config: HttpRequestConfig,\n ): Promise<Result<HttpResponse<T>, HttpErrorDetails>>\n get<T = unknown>(\n url: string,\n config?: Omit<HttpRequestConfig, 'url' | 'method'>,\n ): Promise<Result<HttpResponse<T>, HttpErrorDetails>>\n post<T = unknown>(\n url: string,\n body?: unknown,\n config?: Omit<HttpRequestConfig, 'url' | 'method' | 'body'>,\n ): Promise<Result<HttpResponse<T>, HttpErrorDetails>>\n put<T = unknown>(\n url: string,\n body?: unknown,\n config?: Omit<HttpRequestConfig, 'url' | 'method' | 'body'>,\n ): Promise<Result<HttpResponse<T>, HttpErrorDetails>>\n patch<T = unknown>(\n url: string,\n body?: unknown,\n config?: Omit<HttpRequestConfig, 'url' | 'method' | 'body'>,\n ): Promise<Result<HttpResponse<T>, HttpErrorDetails>>\n delete<T = unknown>(\n url: string,\n config?: Omit<HttpRequestConfig, 'url' | 'method'>,\n ): Promise<Result<HttpResponse<T>, HttpErrorDetails>>\n head(\n url: string,\n config?: Omit<HttpRequestConfig, 'url' | 'method'>,\n ): Promise<Result<HttpResponse<void>, HttpErrorDetails>>\n options(\n url: string,\n config?: Omit<HttpRequestConfig, 'url' | 'method'>,\n ): Promise<Result<HttpResponse<void>, HttpErrorDetails>>\n addRequestInterceptor(interceptor: RequestInterceptor): Disposer\n addResponseInterceptor(interceptor: ResponseInterceptor): Disposer\n clearInterceptors(): void\n extend(config?: HttpClientConfig): IHttpClient\n paginate<T = unknown>(\n url: string,\n options: PaginateOptions<T>,\n config?: Omit<HttpRequestConfig, 'url' | 'method'>,\n ): AsyncIterable<T[]>\n poll<T = unknown>(\n url: string,\n options: PollOptions<T>,\n config?: Omit<HttpRequestConfig, 'url' | 'method'>,\n ): Promise<Result<HttpResponse<T>, HttpErrorDetails>>\n cancelAll(): void\n clearCache(): void\n}\n\n/** Function that removes a previously added interceptor */\nexport type Disposer = () => void\n\n// ============= Create Client Config =============\nexport interface HttpClientConfig {\n baseURL?: string\n defaultHeaders?: Record<string, string>\n defaultTimeout?: HttpTimeout\n cacheStrategy?: CacheStrategy\n validateStatus?: (status: number) => boolean\n maxConcurrent?: number\n defaultResponseType?: ResponseType\n defaultHooks?: RequestHooks\n devTracker?: DevTracker\n /**\n * Global adapter for all requests (same signature as fetch).\n */\n adapter?: (input: RequestInfo, init?: RequestInit) => Promise<Response>\n /**\n * Controls cookie/credential policy for CORS requests. Same as fetch API.\n * 'omit' | 'same-origin' | 'include'\n */\n credentials?: RequestCredentials\n /**\n * Axios compatibility: if true, sets credentials: 'include'; if false, credentials: 'same-origin'.\n * If credentials is also specified, this field is ignored.\n */\n withCredentials?: boolean\n /**\n * Allows transforming the body before serializing and sending it by default in all requests.\n */\n transformRequest?:\n | ((data: unknown, headers: Record<string, string>) => unknown)\n | Array<(data: unknown, headers: Record<string, string>) => unknown>\n /**\n * If true (default), automatically converts body to FormData if files are detected.\n */\n autoFormData?: boolean\n /**\n * Enable debug logging for requests/responses. true for basic logs, 'verbose' for detailed logs.\n */\n debug?: boolean | 'verbose'\n /**\n * Custom logger function. If provided, replaces the default console.log with custom logging.\n */\n logger?: (message: string, data?: unknown) => void\n /**\n * Transport layer to use for HTTP requests.\n * - 'fetch': Uses global fetch API (default)\n * - 'node': Uses Node.js http/https modules with HTTP/1.1\n * - 'http2': Uses Node.js http2 module (HTTP/2)\n * - 'deno': Uses Deno's fetch API (Deno environment)\n * - 'bun': Uses Bun's fetch API (Bun environment)\n * - 'cloudflare': Uses Cloudflare Workers fetch API\n */\n transport?: 'fetch' | 'node' | 'http2' | 'deno' | 'bun' | 'cloudflare'\n /**\n * Node.js specific transport options.\n * Only applies when transport is 'node' or 'http2'.\n */\n nodeOptions?: NodeTransportOptions\n}\n\n// ============= Dev Tracker Interface =============\nexport interface DevTracker {\n track(request: {\n method: string\n url: string\n status?: number\n duration: number\n cached: boolean\n ok: boolean\n code?: string\n headers: Record<string, string>\n body?: unknown\n retryCount: number\n }): void\n}\n\n// ============= Real-time Communication =============\n\n/**\n * WebSocket connection options\n */\nexport interface WebSocketOptions {\n /** WebSocket protocols (subprotocols) */\n protocols?: string | string[]\n /** Headers to send during handshake */\n headers?: Record<string, string>\n /** Automatic reconnection settings */\n reconnect?: {\n /** Enable automatic reconnection (default: true) */\n enabled?: boolean\n /** Base delay in ms for exponential backoff (default: 1000) */\n baseDelay?: number\n /** Maximum delay in ms (default: 30000) */\n maxDelay?: number\n /** Maximum number of reconnect attempts (default: Infinity) */\n maxAttempts?: number\n /** Called before each reconnection attempt */\n onReconnecting?: (attempt: number, delay: number) => void\n }\n /** Timeout for connection establishment in ms (default: 10000) */\n timeout?: number\n /** Callback for connection open */\n onOpen?: (event: Event) => void\n /** Callback for connection close */\n onClose?: (event: CloseEvent) => void\n /** Callback for connection error */\n onError?: (event: Event) => void\n /** Enable heartbeat/ping-pong to keep connection alive */\n heartbeat?: {\n /** Interval in ms to send ping (default: 30000) */\n interval?: number\n /** Timeout in ms to wait for pong before closing (default: 5000) */\n timeout?: number\n /** Custom ping message (default: 'ping') */\n pingMessage?: string | ArrayBuffer | Blob\n /** Custom pong message (default: 'pong') */\n pongMessage?: string | ArrayBuffer | Blob\n }\n}\n\n/**\n * Server-Sent Events (SSE) connection options\n */\nexport interface SSEOptions {\n /** Headers to send with the request */\n headers?: Record<string, string>\n /** Request method (default: GET) */\n method?: string\n /** Request body (for POST requests) */\n body?: unknown\n /** Whether to send credentials (cookies) (default: same-origin) */\n credentials?: RequestCredentials\n /** Timeout for connection establishment in ms (default: 10000) */\n timeout?: number\n /** Automatic reconnection settings */\n reconnect?: {\n /** Enable automatic reconnection (default: true) */\n enabled?: boolean\n /** Base delay in ms for exponential backoff (default: 1000) */\n baseDelay?: number\n /** Maximum delay in ms (default: 30000) */\n maxDelay?: number\n /** Maximum number of reconnect attempts (default: Infinity) */\n maxAttempts?: number\n /** Called before each reconnection attempt */\n onReconnecting?: (attempt: number, delay: number) => void\n }\n /** Callback for connection open */\n onOpen?: (event: Event) => void\n /** Callback for connection error */\n onError?: (event: Event) => void\n /** Callback for connection close */\n onClose?: () => void\n}\n\n/**\n * Real-time message event\n */\nexport interface RealtimeMessageEvent<T = unknown> {\n /** Message data (parsed if possible) */\n data: T\n /** Raw message data */\n raw: string | ArrayBuffer | Blob\n /** Message type (for WebSocket: 'message', for SSE: event type) */\n type: string\n /** Timestamp when message was received */\n timestamp: number\n}\n\n/**\n * Real-time client interface\n */\nexport interface IRealtimeClient {\n /** Connect to the server */\n connect(): Promise<void>\n /** Disconnect from the server */\n disconnect(): void\n /** Send a message */\n send(data: string | ArrayBuffer | Blob): void\n /** Subscribe to messages */\n onMessage<T = unknown>(\n callback: (event: RealtimeMessageEvent<T>) => void,\n ): () => void\n /** Subscribe to connection open events */\n onOpen(callback: (event: Event) => void): () => void\n /** Subscribe to connection close events */\n onClose(callback: (event?: CloseEvent) => void): () => void\n /** Subscribe to connection error events */\n onError(callback: (event: Event) => void): () => void\n /** Get connection status */\n getStatus(): 'connecting' | 'open' | 'closing' | 'closed'\n /** Get connection statistics */\n getStats(): {\n messagesSent: number\n messagesReceived: number\n connectionTime: number\n reconnectAttempts: number\n }\n}\n\n/**\n * WebSocket client interface (extends IRealtimeClient)\n */\nexport interface IWebSocketClient extends IRealtimeClient {\n /** WebSocket instance */\n readonly socket: WebSocket | null\n /** Send JSON data (automatically serialized) */\n sendJson(data: unknown): void\n /** Subscribe to specific message types */\n onMessageType<T = unknown>(\n type: string,\n callback: (data: T) => void,\n ): () => void\n}\n\n/**\n * SSE client interface (extends IRealtimeClient)\n */\nexport interface ISSEClient extends IRealtimeClient {\n /** EventSource instance */\n readonly source: EventSource | null\n /** Subscribe to specific event types */\n onEvent<T = unknown>(event: string, callback: (data: T) => void): () => void\n /** Last event ID */\n readonly lastEventId: string | null\n}\n\n// ============= Global Environment Declarations =============\ndeclare global {\n // Deno runtime\n interface Deno {\n readonly version: {\n deno: string\n }\n }\n const Deno: Deno | undefined\n\n // Bun runtime\n interface Bun {\n readonly version: string\n }\n const Bun: Bun | undefined\n\n // Cloudflare Workers WebSocketPair\n const WebSocketPair:\n | {\n new (): { 0: WebSocket; 1: WebSocket }\n }\n | undefined\n}\n","/**\n * HTTP Client Utilities\n * Common validators, transformers, and helpers\n */\n\nimport type {\n Validator,\n Transformer,\n RetryStrategy,\n HttpErrorDetails,\n IHttpClient,\n} from '../types'\nimport { Ok, Err } from '../types'\n\n// ============= Validators =============\n\n/**\n * Schema validator using simple checks (can be replaced with Zod, Yup, etc)\n */\nexport function createSchemaValidator<T>(\n schema: Record<keyof T, (value: unknown) => boolean>,\n): Validator {\n return {\n validate(data) {\n const obj = data as Record<string, unknown>\n for (const [key, check] of Object.entries(schema)) {\n const checkFn = check as (value: unknown) => boolean\n if (!checkFn(obj[key])) {\n return Err({\n message: `Validation failed: field \"${key}\" is invalid`,\n code: 'VALIDATION_ERROR',\n })\n }\n }\n return Ok(data)\n },\n }\n}\n\n/**\n * Validator that ensures response has required fields\n */\nexport function createRequiredFieldsValidator(fields: string[]): Validator {\n return {\n validate(data) {\n const obj = data as Record<string, unknown>\n const missing = fields.filter((field) => !(field in obj))\n if (missing.length > 0) {\n return Err({\n message: `Validation failed: missing fields: ${missing.join(', ')}`,\n code: 'VALIDATION_ERROR',\n })\n }\n return Ok(data)\n },\n }\n}\n\n/**\n * Validator that ensures response is an array\n */\nexport const validatorIsArray: Validator = {\n validate(data) {\n return Array.isArray(data)\n ? Ok(data)\n : Err({ message: 'Expected array response', code: 'VALIDATION_ERROR' })\n },\n}\n\n/**\n * Validator that ensures response is an object\n */\nexport const validatorIsObject: Validator = {\n validate(data) {\n return data && typeof data === 'object' && !Array.isArray(data)\n ? Ok(data)\n : Err({ message: 'Expected object response', code: 'VALIDATION_ERROR' })\n },\n}\n\n// ============= Transformers =============\n\n/**\n * Transform that converts snake_case to camelCase (common API pattern)\n */\nexport const transformSnakeToCamel: Transformer = {\n transform(data) {\n return transformObject(data, snakeToCamel)\n },\n}\n\n/**\n * Transform that converts camelCase to snake_case\n */\nexport const transformCamelToSnake: Transformer = {\n transform(data) {\n return transformObject(data, camelToSnake)\n },\n}\n\n/**\n * Transform that flattens nested data\n */\nexport const transformFlatten: Transformer = {\n transform(data) {\n return flatten(data)\n },\n}\n\n/**\n * Transform that picks specific fields (projection)\n */\nexport function createProjectionTransformer(fields: string[]): Transformer {\n return {\n transform(data) {\n if (Array.isArray(data)) {\n return data.map((item) => pickFields(item, fields))\n }\n return pickFields(data, fields)\n },\n }\n}\n\n/**\n * Transform that wraps data in a container\n */\nexport function createWrapperTransformer(wrapper: string): Transformer {\n return {\n transform(data) {\n return { [wrapper]: data }\n },\n }\n}\n\n// ============= Retry Strategies =============\n\n/**\n * Aggressive retry: retry all errors up to max attempts\n */\nexport class AggressiveRetry implements RetryStrategy {\n private maxAttempts: number\n\n constructor(maxAttempts: number = 5) {\n this.maxAttempts = maxAttempts\n }\n\n shouldRetry(attempt: number): boolean {\n return attempt < this.maxAttempts\n }\n\n delayMs(attempt: number): number {\n return attempt * 50\n }\n}\n\n/**\n * Conservative retry: only retry on specific status codes\n */\nexport class ConservativeRetry implements RetryStrategy {\n private retryableStatuses = [408, 429, 500, 502, 503, 504]\n private maxAttempts: number\n\n constructor(maxAttempts: number = 3) {\n this.maxAttempts = maxAttempts\n }\n\n shouldRetry(attempt: number, error: HttpErrorDetails): boolean {\n if (attempt >= this.maxAttempts) {\n return false\n }\n return (\n this.retryableStatuses.includes(error.status ?? 0) ||\n error.code === 'TIMEOUT'\n )\n }\n\n delayMs(attempt: number): number {\n return Math.min(1000 * Math.pow(2, attempt - 1), 10000) // capped at 10s\n }\n}\n\n/**\n * Circuit breaker pattern: fail fast after threshold\n */\nexport class CircuitBreakerRetry implements RetryStrategy {\n private failureCount = 0\n private lastFailureTime = 0\n private maxAttempts: number\n private failureThreshold: number\n private resetTimeMs: number\n\n constructor(\n maxAttempts: number = 3,\n failureThreshold: number = 5,\n resetTimeMs: number = 60000,\n ) {\n this.maxAttempts = maxAttempts\n this.failureThreshold = failureThreshold\n this.resetTimeMs = resetTimeMs\n }\n\n shouldRetry(attempt: number): boolean {\n if (attempt >= this.maxAttempts) {\n return false\n }\n\n // Check if circuit should reset\n if (Date.now() - this.lastFailureTime > this.resetTimeMs) {\n this.failureCount = 0\n }\n\n // Open circuit after threshold\n if (this.failureCount >= this.failureThreshold) {\n return false\n }\n\n this.failureCount++\n this.lastFailureTime = Date.now()\n return true\n }\n\n delayMs(attempt: number): number {\n return 100 * Math.pow(2, attempt - 1)\n }\n\n reset(): void {\n this.failureCount = 0\n this.lastFailureTime = 0\n }\n}\n\n// ============= Helper Functions =============\n\nfunction snakeToCamel(str: string): string {\n return str.replace(/_([a-z])/g, (_, char) => char.toUpperCase())\n}\n\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, (char) => `_${char.toLowerCase()}`)\n}\n\nfunction transformObject(\n data: unknown,\n keyTransform: (key: string) => string,\n): unknown {\n if (!data || typeof data !== 'object') {\n return data\n }\n\n if (Array.isArray(data)) {\n return data.map((item) => transformObject(item, keyTransform))\n }\n\n const transformed: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(data as Record<string, unknown>)) {\n transformed[keyTransform(key)] = transformObject(value, keyTransform)\n }\n return transformed\n}\n\nfunction flatten(data: unknown, prefix = ''): Record<string, unknown> {\n const result: Record<string, unknown> = {}\n\n if (Array.isArray(data)) {\n data.forEach((item, index) => {\n const key = prefix ? `${prefix}[${index}]` : `[${index}]`\n Object.assign(result, flatten(item, key))\n })\n } else if (data && typeof data === 'object') {\n for (const [key, value] of Object.entries(\n data as Record<string, unknown>,\n )) {\n const flatKey = prefix ? `${prefix}.${key}` : key\n if (value && typeof value === 'object' && !Array.isArray(value)) {\n Object.assign(result, flatten(value, flatKey))\n } else {\n result[flatKey] = value\n }\n }\n }\n\n return result\n}\n\nfunction pickFields(data: unknown, fields: string[]): Record<string, unknown> {\n if (!data || typeof data !== 'object') {\n return {}\n }\n\n const obj = data as Record<string, unknown>\n const result: Record<string, unknown> = {}\n\n for (const field of fields) {\n if (field in obj) {\n result[field] = obj[field]\n }\n }\n\n return result\n}\n\n// ============= Timeout Utilities =============\n\n/**\n * Creates an AbortController with a timeout.\n * Automatically aborts the operation after the specified milliseconds.\n * The timer is cleaned up when the signal is aborted (either by timeout or externally).\n * @param ms - Timeout in milliseconds\n * @returns AbortController that will abort after timeout\n */\nexport function withTimeout(ms: number): AbortController {\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), ms)\n controller.signal.addEventListener('abort', () => clearTimeout(timeoutId), {\n once: true,\n })\n return controller\n}\n\n/**\n * Retry helper function that retries an async operation\n * @param fn - Async function to retry\n * @param retries - Number of retries (default: 3)\n * @returns Result of the function call\n */\nexport async function retry<T>(fn: () => Promise<T>, retries = 3): Promise<T> {\n try {\n return await fn()\n } catch (err) {\n if (retries <= 0) {\n throw err\n }\n return retry(fn, retries - 1)\n }\n}\n\n// ============= Advanced Features =============\n\n// ===== 1. Automatic Cache (React Query Lite) =====\n\ninterface CacheEntry<T> {\n data: T\n timestamp: number\n ttlMs: number\n}\n\n/**\n * Simple cache store with TTL support\n */\nexport class CacheStore {\n private cache: Map<string, CacheEntry<unknown>> = new Map()\n\n get<T>(key: string): T | null {\n const entry = this.cache.get(key) as CacheEntry<T> | undefined\n if (!entry) {\n return null\n }\n\n const isExpired = Date.now() - entry.timestamp > entry.ttlMs\n if (isExpired) {\n this.cache.delete(key)\n return null\n }\n\n return entry.data\n }\n\n set<T>(key: string, data: T, ttlMs: number = 60000): void {\n this.cache.set(key, { data, timestamp: Date.now(), ttlMs })\n }\n\n clear(): void {\n this.cache.clear()\n }\n\n has(key: string): boolean {\n const entry = this.cache.get(key) as CacheEntry<unknown> | undefined\n if (!entry) {\n return false\n }\n const isExpired = Date.now() - entry.timestamp > entry.ttlMs\n if (isExpired) {\n this.cache.delete(key)\n return false\n }\n return true\n }\n\n delete(key: string): void {\n this.cache.delete(key)\n }\n}\n\n/**\n * Cache middleware factory - caches HTTP responses with TTL support\n * Automatically skips caching for non-GET requests\n */\nexport function createCacheMiddleware(\n options: {\n cache?: CacheStore\n ttlMs?: number\n cacheableStatuses?: number[]\n } = {},\n): Middleware<HttpContext> {\n const cache = options.cache || new CacheStore()\n const ttlMs = options.ttlMs || 60000 // Default 1 minute\n const cacheableStatuses = options.cacheableStatuses || [200, 304] // Only cache successful responses\n\n return async (ctx, next) => {\n const method = (ctx.request.method || 'GET').toUpperCase()\n const isCacheable = method === 'GET' // Only cache GET requests\n const cacheKey = `${method}:${ctx.request.url}`\n\n // Try to serve from cache\n if (isCacheable && cache.has(cacheKey)) {\n const cachedResponse = cache.get<typeof ctx.response>(cacheKey)\n if (cachedResponse) {\n ctx.response = cachedResponse\n ctx.state.cacheHit = true\n return\n }\n }\n\n // Proceed to next middleware\n await next()\n\n // Cache successful responses\n if (\n isCacheable &&\n ctx.response &&\n cacheableStatuses.includes(ctx.response.status)\n ) {\n cache.set(cacheKey, ctx.response, ttlMs)\n ctx.state.cacheMiss = true\n }\n }\n}\n\n/**\n * Pre-configured cache middleware with default 60s TTL\n */\nexport const cacheMiddleware: Middleware<HttpContext> = createCacheMiddleware()\n\n// ===== 2. Request Deduplication =====\n\n/**\n * Prevents duplicate requests to the same endpoint\n * Shares pending request promises\n */\nexport class RequestDeduplicator {\n private pending: Map<string, Promise<unknown>> = new Map()\n\n async execute<T>(key: string, fn: () => Promise<T>): Promise<T> {\n // If request is already pending, return the existing promise\n if (this.pending.has(key)) {\n return this.pending.get(key) as Promise<T>\n }\n\n // Create new request and track it\n const promise = fn().finally(() => {\n this.pending.delete(key)\n })\n\n this.pending.set(key, promise)\n return promise as Promise<T>\n }\n\n clear(): void {\n this.pending.clear()\n }\n}\n\n/**\n * Deduplication middleware factory - shares pending requests to the same URL\n * Prevents duplicate network requests by sharing the same Promise\n */\nexport function createDedupeMiddleware(\n options: {\n deduplicator?: RequestDeduplicator\n includeBody?: boolean\n methods?: string[]\n } = {},\n): Middleware<HttpContext> {\n const deduplicator = options.deduplicator || new RequestDeduplicator()\n const includeBody = options.includeBody ?? false // Include body in dedup key for POST/PUT/PATCH\n const methods = options.methods || ['GET'] // Methods to deduplicate\n\n return async (ctx, next) => {\n const method = (ctx.request.method || 'GET').toUpperCase()\n const shouldDedupe = methods.includes(method)\n\n if (!shouldDedupe) {\n await next()\n return\n }\n\n // Build dedup key\n let dedupeKey = `${method}:${ctx.request.url}`\n if (includeBody && ctx.request.body) {\n dedupeKey += `:${JSON.stringify(ctx.request.body)}`\n }\n\n try {\n // Use deduplicator to share pending requests\n const response = await deduplicator.execute(dedupeKey, async () => {\n await next()\n return ctx.response\n })\n\n ctx.response = response\n ctx.state.deduped = true\n } catch (error) {\n ctx.error = error\n throw error\n }\n }\n}\n\n/**\n * Pre-configured deduplication middleware for GET requests\n */\nexport const dedupeMiddleware: Middleware<HttpContext> =\n createDedupeMiddleware()\n\n// ===== 3. Middleware Pipeline =====\n\n/**\n * HTTP Context passed through middleware chain\n */\nexport interface HttpContext {\n request: {\n method: string\n url: string\n headers: Record<string, string>\n body?: unknown\n }\n response: {\n status: number\n headers: Record<string, string>\n body?: unknown\n }\n state: Record<string, unknown>\n error?: unknown\n}\n\n/**\n * Middleware function type with Express/Koa-like pattern\n * Receives context and next() function to proceed through pipeline\n */\nexport type Middleware<T extends HttpContext = HttpContext> = (\n ctx: T,\n next: () => Promise<void>,\n) => Promise<void>\n\n/**\n * Create a middleware pipeline executor with proper sequencing\n * Prevents multiple next() calls and ensures proper error propagation\n */\nexport function createPipeline<T extends HttpContext = HttpContext>(\n middlewares: Middleware<T>[],\n) {\n return async (ctx: T): Promise<void> => {\n let index = -1\n\n async function dispatch(i: number): Promise<void> {\n if (i <= index) {\n throw new Error('next() called multiple times')\n }\n index = i\n\n const fn = middlewares[i]\n if (fn) {\n await fn(ctx, () => dispatch(i + 1))\n }\n }\n\n await dispatch(0)\n }\n}\n\n/**\n * Legacy: MiddlewarePipeline class (backwards compatible)\n * For simpler use cases, kept for compatibility\n */\nexport class MiddlewarePipeline<T = unknown> {\n private middlewares: Array<\n | Middleware<T extends HttpContext ? T : HttpContext>\n | ((data: T) => T | Promise<T>)\n > = []\n\n use(\n middleware:\n | Middleware<T extends HttpContext ? T : HttpContext>\n | ((data: T) => T | Promise<T>),\n ): this {\n this.middlewares.push(middleware)\n return this\n }\n\n async execute(data: T): Promise<T> {\n // If data looks like HttpContext, use pipeline pattern\n if (\n data &&\n typeof data === 'object' &&\n 'request' in data &&\n 'response' in data\n ) {\n const ctx = data as unknown as HttpContext\n const pipeline = createPipeline(\n this.middlewares.map((mw) => {\n if (typeof mw === 'function' && mw.length === 2) {\n return mw as Middleware\n }\n // Convert simple transformer to middleware\n return async (ctx: HttpContext, next: () => Promise<void>) => {\n const transform = mw as (data: T) => T | Promise<T>\n ctx.response.body = await transform(ctx.response.body as T)\n await next()\n }\n }),\n )\n await pipeline(ctx)\n return ctx.response.body as T\n }\n\n // Fallback: simple data transformation pipeline\n let result = data\n for (const mw of this.middlewares) {\n const transform = mw as (data: T) => T | Promise<T>\n result = await transform(result)\n }\n return result\n }\n\n clear(): void {\n this.middlewares = []\n }\n}\n\n// ===== 4. Advanced Typed Generics =====\n\n/**\n * Type-safe response wrapper with automatic type inference\n */\nexport interface TypedResponse<T, U = unknown> {\n ok: boolean\n data?: T\n error?: U\n status: number\n headers: Record<string, string>\n}\n\n/**\n * Create a typed response builder\n */\nexport function createTypedResponse<T, U = unknown>(\n status: number,\n data?: T,\n error?: U,\n headers: Record<string, string> = {},\n): TypedResponse<T, U> {\n return {\n ok: status >= 200 && status < 300,\n data,\n error,\n status,\n headers,\n }\n}\n\n/**\n * API Endpoint definition with typed request/response\n */\nexport interface ApiEndpoint<TRequest = unknown, TResponse = unknown> {\n method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'\n path: string\n request?: TRequest\n response: TResponse\n}\n\n/**\n * Create a strongly-typed API client method\n */\nexport function createTypedRequest<TRequest, TResponse>(\n endpoint: ApiEndpoint<TRequest, TResponse>,\n): (client: IHttpClient, req?: TRequest) => Promise<TResponse> {\n return async (client, req?) => {\n const url = endpoint.path\n let response: unknown\n\n switch (endpoint.method) {\n case 'GET':\n response = await client.get(url)\n break\n case 'POST':\n response = await client.post(url, req)\n break\n case 'PUT':\n response = await client.put(url, req)\n break\n case 'PATCH':\n response = await client.patch(url, req)\n break\n case 'DELETE':\n response = await client.delete(url)\n break\n default:\n throw new Error(`Unsupported method: ${endpoint.method}`)\n }\n\n return response as TResponse\n }\n}\n\n/**\n * API Schema - maps multiple endpoints with automatic type inference\n */\nexport type ApiSchema = Record<string, ApiEndpoint>\n\n/**\n * Create a typed API client from schema\n */\nexport function createTypedApiClient<T extends ApiSchema>(schema: T) {\n return {\n request: async <K extends keyof T>(\n client: IHttpClient,\n endpoint: K,\n data?: T[K] extends ApiEndpoint<infer TReq, unknown> ? TReq : never,\n ): Promise<\n T[K] extends ApiEndpoint<unknown, infer TRes> ? TRes : never\n > => {\n const ep = schema[endpoint] as ApiEndpoint\n const requester = createTypedRequest(ep)\n return (await requester(client, data)) as T[K] extends ApiEndpoint<\n unknown,\n infer TRes\n >\n ? TRes\n : never\n },\n }\n}\n\n/**\n * Observable-like response wrapper for reactive patterns\n */\nexport class TypedObservable<T> {\n private subscribers: Array<(value: T) => void> = []\n private errorSubscribers: Array<(error: unknown) => void> = []\n private completeSubscribers: Array<() => void> = []\n\n subscribe(\n next?: (value: T) => void,\n error?: (err: unknown) => void,\n complete?: () => void,\n ): { unsubscribe: () => void } {\n if (next) {\n this.subscribers.push(next)\n }\n if (error) {\n this.errorSubscribers.push(error)\n }\n if (complete) {\n this.completeSubscribers.push(complete)\n }\n\n return {\n unsubscribe: () => {\n this.subscribers = this.subscribers.filter((s) => s !== next)\n this.errorSubscribers = this.errorSubscribers.filter((e) => e !== error)\n this.completeSubscribers = this.completeSubscribers.filter(\n (c) => c !== complete,\n )\n },\n }\n }\n\n next(value: T): void {\n this.subscribers.forEach((s) => s(value))\n }\n\n error(err: unknown): void {\n this.errorSubscribers.forEach((e) => e(err))\n }\n\n complete(): void {\n this.completeSubscribers.forEach((c) => c())\n }\n\n map<U>(fn: (value: T) => U): TypedObservable<U> {\n const obs = new TypedObservable<U>()\n this.subscribe(\n (value) => obs.next(fn(value)),\n (err) => obs.error(err),\n () => obs.complete(),\n )\n return obs\n }\n\n filter(predicate: (value: T) => boolean): TypedObservable<T> {\n const obs = new TypedObservable<T>()\n this.subscribe(\n (value) => {\n if (predicate(value)) {\n obs.next(value)\n }\n },\n (err) => obs.error(err),\n () => obs.complete(),\n )\n return obs\n }\n}\n\n/**\n * Union type helper - extracts success/error types\n */\nexport type UnionToIntersection<U> = (\n U extends unknown ? (k: U) => void : never\n) extends (k: infer I) => void\n ? I\n : never\n\n/**\n * Result type extractor for discriminated unions\n */\nexport type ResultOf<T extends { ok: boolean }> = T extends { ok: true }\n ? Omit<T, 'ok'>\n : T extends { ok: false }\n ? Omit<T, 'ok'>\n : never\n\n/**\n * Guard function for typing - validates data is T at runtime\n */\nexport function createTypeGuard<T>(\n check: (value: unknown) => value is T,\n): (value: unknown) => T {\n return (value) => {\n if (!check(value)) {\n throw new TypeError(`Value does not match expected type`)\n }\n return value\n }\n}\n\n/**\n * Branded types for URL safety\n */\nexport type Url<T extends string = string> = string & {\n readonly __url: unique symbol\n readonly __type: T\n}\nexport type ApiUrl = Url<'api'>\nexport type FileUrl = Url<'file'>\n\nexport function createUrl<T extends string = string>(url: string): Url<T> {\n return url as Url<T>\n}\n\nexport function createApiUrl(path: string): ApiUrl {\n return createUrl<'api'>(path)\n}\n\n/**\n * Defer pattern for lazy evaluation\n */\nexport class Defer<T> {\n private _promise: Promise<T>\n private resolveFunc!: (value: T) => void\n private rejectFunc!: (reason?: unknown) => void\n\n constructor() {\n this._promise = new Promise((resolve, reject) => {\n this.resolveFunc = resolve\n this.rejectFunc = reject\n })\n }\n\n resolve(value: T): void {\n this.resolveFunc(value)\n }\n\n reject(reason?: unknown): void {\n this.rejectFunc(reason)\n }\n\n get promise(): Promise<T> {\n return this._promise\n }\n\n /**\n * @deprecated Use `defer.promise` getter instead\n */\n promise_(): Promise<T> {\n return this._promise\n }\n}\n\n// ===== 5. Streaming Support =====\n\n/**\n * Streaming response handler for large files/streams\n */\nexport interface StreamOptions {\n chunkSize?: number\n onChunk?: (chunk: Uint8Array) => void | Promise<void>\n onProgress?: (loaded: number, total: number) => void\n}\n\n/**\n * Handle streaming responses\n */\nexport async function handleStream(\n response: Response,\n options: StreamOptions = {},\n): Promise<Uint8Array> {\n const reader = response.body?.getReader()\n if (!reader) {\n throw new Error('Response body is not readable')\n }\n\n const chunks: Uint8Array[] = []\n let loaded = 0\n const total = parseInt(response.headers.get('content-length') || '0', 10)\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) {\n break\n }\n\n chunks.push(value)\n loaded += value.length\n\n if (options.onChunk) {\n await options.onChunk(value)\n }\n\n if (options.onProgress && total > 0) {\n options.onProgress(loaded, total)\n }\n }\n\n return concatUint8Arrays(chunks, loaded)\n}\n\n/**\n * Efficiently concatenate Uint8Array chunks into a single array\n */\nfunction concatUint8Arrays(\n chunks: Uint8Array[],\n totalLength: number,\n): Uint8Array {\n const result = new Uint8Array(totalLength)\n let offset = 0\n for (const chunk of chunks) {\n result.set(chunk, offset)\n offset += chunk.byteLength\n }\n return result\n}\n\n/**\n * Stream to file (Node.js compatible)\n */\nexport async function streamToFile(\n response: Response,\n filePath: string,\n): Promise<void> {\n const data = await handleStream(response)\n\n // For browser, you'd use Blob\n // For Node.js, you'd use fs.writeFile\n if (typeof window === 'undefined') {\n // Node.js environment\n const fs = await import('fs').then((m) => m.promises)\n await fs.writeFile(filePath, data)\n } else {\n // Browser: create blob and download\n const blob = new Blob([data.buffer as BlobPart], {\n type: 'application/octet-stream',\n })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = filePath\n a.click()\n URL.revokeObjectURL(url)\n }\n}\n\n/**\n * Streaming middleware factory - handles streaming responses with progress tracking\n * Useful for large file downloads, real-time data streaming, etc.\n */\nexport function createStreamingMiddleware(\n options: {\n onChunk?: (chunk: Uint8Array) => void | Promise<void>\n onProgress?: (loaded: number, total: number) => void\n } = {},\n): Middleware<HttpContext> {\n return async (ctx, next) => {\n await next()\n\n // Check if response has a body to stream\n if (\n ctx.response &&\n ctx.response.body &&\n typeof ctx.response.body === 'object' &&\n 'getReader' in ctx.response.body\n ) {\n const reader = (\n ctx.response.body as ReadableStream<Uint8Array>\n ).getReader()\n const chunks: Uint8Array[] = []\n let loaded = 0\n const total = parseInt(\n (ctx.response.headers?.['content-length'] as string) || '0',\n 10,\n )\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done) {\n break\n }\n\n chunks.push(value)\n loaded += value.length\n\n if (options.onChunk) {\n await options.onChunk(value)\n }\n\n if (options.onProgress && total > 0) {\n options.onProgress(loaded, total)\n }\n\n ctx.state.streamedChunks =\n (ctx.state.streamedChunks as Uint8Array[]) || []\n ;(ctx.state.streamedChunks as Uint8Array[]).push(value)\n }\n\n // Combine all chunks into response body\n const resultData = concatUint8Arrays(chunks, loaded)\n ctx.response.body = resultData\n ctx.state.streaming = true\n ctx.state.streamedBytes = loaded\n } finally {\n reader.releaseLock()\n }\n }\n }\n}\n\n/**\n * Pre-configured streaming middleware with default progress tracking\n */\nexport const streamingMiddleware: Middleware<HttpContext> =\n createStreamingMiddleware({\n onProgress: (loaded, total) => {\n if (total > 0) {\n const percent = Math.round((loaded / total) * 100)\n console.warn(`Streaming: ${percent}% (${loaded}/${total} bytes)`)\n }\n },\n })\n\n// ===== 6. Plugins System =====\n\n/**\n * Plugin interface - plugins can extend HttpClient functionality\n */\nexport interface Plugin {\n name: string\n setup(manager: PluginManager): void | Promise<void>\n}\n\n/**\n * Plugin manager for extensible architecture\n * Integrates cache, deduplication, and middleware\n */\nexport class PluginManager {\n private plugins: Plugin[] = []\n private cache: CacheStore = new CacheStore()\n private deduplicator: RequestDeduplicator = new RequestDeduplicator()\n private middlewares: Middleware<HttpContext>[] = []\n private listeners: Map<string, Set<(...args: unknown[]) => void>> = new Map()\n\n /**\n * Register a plugin and call its setup method\n */\n register(plugin: Plugin): this {\n this.plugins.push(plugin)\n void plugin.setup(this)\n this.emit('plugin:registered', plugin.name)\n return this\n }\n\n /**\n * Add middleware to the pipeline\n */\n addMiddleware(middleware: Middleware<HttpContext>): this {\n this.middlewares.push(middleware)\n return this\n }\n\n /**\n * Get cache store\n */\n getCache(): CacheStore {\n return this.cache\n }\n\n /**\n * Get request deduplicator\n */\n getDeduplicator(): RequestDeduplicator {\n return this.deduplicator\n }\n\n /**\n * Get middleware pipeline executor\n */\n getPipeline(): (ctx: HttpContext) => Promise<void> {\n return createPipeline(this.middlewares)\n }\n\n /**\n * Execute middleware pipeline for a context\n */\n async executePipeline(ctx: HttpContext): Promise<void> {\n const pipeline = this.getPipeline()\n await pipeline(ctx)\n }\n\n /**\n * Register event listener\n */\n on(\n event: string,\n handler: (...args: unknown[]) => void | Promise<void>,\n ): this {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set())\n }\n const set = this.listeners.get(event)\n const h = handler\n if (set) {\n set.add(h)\n }\n return this\n }\n\n /**\n * Emit event to all listeners\n */\n emit(event: string, ...args: unknown[]): void {\n const handlers = this.listeners.get(event)\n if (handlers) {\n for (const handler of handlers) {\n void handler(...args)\n }\n }\n }\n\n /**\n * Unregister event listener\n */\n off(event: string, handler: (...args: unknown[]) => void): this {\n this.listeners.get(event)?.delete(handler)\n return this\n }\n\n /**\n * Get all registered plugins\n */\n getPlugins(): Plugin[] {\n return [...this.plugins]\n }\n\n /**\n * Clear all plugins, middlewares, and listeners\n */\n clear(): void {\n this.plugins = []\n this.middlewares = []\n this.listeners.clear()\n this.cache.clear()\n this.deduplicator.clear()\n }\n}\n\n// ===== Example Plugins =====\n\n/**\n * Example: Logger plugin - logs HTTP requests and responses\n */\nexport const LoggerPlugin: Plugin = {\n name: 'logger',\n setup(manager: PluginManager) {\n manager.on('request:start', (...args: unknown[]) => {\n const url = args[0] as string\n console.warn(`Request started: ${url}`)\n })\n manager.on('request:success', (...args: unknown[]) => {\n const url = args[0] as string\n const status = args[1] as number\n console.warn(`Request succeeded: ${url} (${status})`)\n })\n manager.on('request:error', (...args: unknown[]) => {\n const url = args[0] as string\n const error = args[1] as unknown\n console.error(`❌ Request failed: ${url}`, error)\n })\n },\n}\n\n/**\n * Example: Metrics plugin - collects request metrics\n */\nexport class MetricsPlugin implements Plugin {\n name = 'metrics'\n private metrics = {\n requests: 0,\n errors: 0,\n totalTime: 0,\n avgTime: 0,\n }\n\n setup(manager: PluginManager): void {\n manager.on('request:complete', (...args: unknown[]) => {\n const duration = args[0] as number\n const success = args[1] as boolean\n this.metrics.requests++\n this.metrics.totalTime += duration\n this.metrics.avgTime = this.metrics.totalTime / this.metrics.requests\n if (!success) {\n this.metrics.errors++\n }\n })\n }\n\n getMetrics() {\n return { ...this.metrics }\n }\n}\n\n/**\n * Example: Cache plugin - automatically adds caching middleware\n */\nexport class CachePlugin implements Plugin {\n name = 'cache'\n ttlMs: number\n\n constructor(ttlMs: number = 60000) {\n this.ttlMs = ttlMs\n }\n\n setup(manager: PluginManager): void {\n manager.addMiddleware(createCacheMiddleware({ ttlMs: this.ttlMs }))\n }\n}\n\n/**\n * Example: Deduplication plugin - prevents duplicate requests\n */\nexport class DedupePlugin implements Plugin {\n name = 'dedupe'\n\n setup(manager: PluginManager): void {\n manager.addMiddleware(createDedupeMiddleware())\n }\n}\n\n// Re-export type for convenience\nexport type { HttpErrorDetails }\n","/**\n * HTTP Client Implementation\n * Superior to fetch + axios:\n *\n * vs fetch:\n * ✓ Automatic JSON parsing + error handling via Result<T,E>\n * ✓ Interceptors, retry, cache, timeout built-in\n * ✓ Progress tracking for uploads/downloads\n * ✓ Path parameter interpolation (/users/:id)\n * ✓ Request lifecycle hooks (onStart, onSuccess, onError, onFinally, onRetry)\n * ✓ Auto content-type detection (FormData, Blob, URLSearchParams, etc.)\n * ✓ Concurrent request limiting (rate limiter)\n *\n * vs axios:\n * ✓ Zero dependencies (~3KB gzipped vs axios ~13KB)\n * ✓ Result<T,E> monad — no try/catch needed, type-safe error handling\n * ✓ Built-in request deduplication\n * ✓ Streaming support with chunk callbacks\n * ✓ Plugin architecture (SOLID)\n * ✓ Middleware pipeline (Express/Koa-like)\n * ✓ Circuit breaker retry strategy\n * ✓ Response duration tracking\n * ✓ Modern ESM-first with tree-shaking\n *\n * SOLID Principles:\n * - S: HttpClient → HTTP communication\n * - O: Extensible via interceptors, strategies, plugins\n * - L: Liskov — implementations interchange without breaking\n * - I: Interface Segregation — small focused interfaces\n * - D: Dependency Inversion — depends on abstractions (IHttpClient)\n */\n\nimport type {\n IHttpClient,\n HttpRequest,\n HttpRequestConfig,\n HttpResponse,\n HttpErrorDetails,\n RequestInterceptor,\n ResponseInterceptor,\n RetryStrategy,\n HttpClientConfig,\n CacheStrategy,\n ResponseType,\n RequestHooks,\n PaginateOptions,\n PollOptions,\n Disposer,\n Result,\n HttpTimeout,\n ProgressEvent as NexaProgressEvent,\n DevTracker,\n} from '../types'\nimport { Ok, Err } from '../types'\nimport { CacheStore } from '../utils'\n\n// ============= Internal Helpers =============\n\n/**\n * In-memory cache adapter (delegates to CacheStore)\n */\nclass MemoryCache implements CacheStrategy {\n private store = new CacheStore()\n\n get(key: string): unknown | null {\n return this.store.get(key)\n }\n\n set(key: string, value: unknown, ttlMs = 60000): void {\n this.store.set(key, value, ttlMs)\n }\n\n has(key: string): boolean {\n return this.store.has(key)\n }\n\n clear(): void {\n this.store.clear()\n }\n}\n\n/**\n * Default retry strategy: exponential backoff with jitter\n */\nclass ExponentialBackoffRetry implements RetryStrategy {\n private maxAttempts: number\n private baseDelayMs: number\n\n constructor(maxAttempts: number = 3, baseDelayMs: number = 100) {\n this.maxAttempts = maxAttempts\n this.baseDelayMs = baseDelayMs\n }\n\n shouldRetry(attempt: number, error: HttpErrorDetails): boolean {\n const retryableStatus = error.status !== undefined && error.status >= 500\n const networkError = error.code === 'NETWORK_ERROR'\n return (\n attempt < this.maxAttempts &&\n (retryableStatus || networkError || error.code === 'TIMEOUT')\n )\n }\n\n delayMs(attempt: number): number {\n const base = this.baseDelayMs * Math.pow(2, attempt - 1)\n const jitter = Math.random() * base * 0.1\n return Math.min(base + jitter, 30000)\n }\n}\n\n/**\n * Concurrent request limiter — controls max parallel requests\n */\nclass RequestQueue {\n private running = 0\n private queue: Array<() => void> = []\n private maxConcurrent: number\n\n constructor(maxConcurrent: number) {\n this.maxConcurrent = maxConcurrent\n }\n\n async acquire(): Promise<void> {\n if (this.running < this.maxConcurrent) {\n this.running++\n return\n }\n return new Promise<void>((resolve) => {\n this.queue.push(() => {\n this.running++\n resolve()\n })\n })\n }\n\n release(): void {\n this.running--\n const next = this.queue.shift()\n if (next) {\n next()\n }\n }\n\n get pending(): number {\n return this.queue.length\n }\n\n get active(): number {\n return this.running\n }\n}\n\n/**\n * Detect body type and return appropriate fetch body + content-type\n */\nfunction serializeBody(body: unknown): {\n serialized: BodyInit | undefined\n contentType: string | null\n} {\n if (body === undefined || body === null) {\n return { serialized: undefined, contentType: null }\n }\n if (typeof body === 'string') {\n return { serialized: body, contentType: 'text/plain' }\n }\n if (typeof FormData !== 'undefined' && body instanceof FormData) {\n return { serialized: body, contentType: null }\n }\n if (\n typeof URLSearchParams !== 'undefined' &&\n body instanceof URLSearchParams\n ) {\n return {\n serialized: body,\n contentType: 'application/x-www-form-urlencoded',\n }\n }\n if (typeof Blob !== 'undefined' && body instanceof Blob) {\n return {\n serialized: body,\n contentType: body.type || 'application/octet-stream',\n }\n }\n if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {\n return {\n serialized: body as BodyInit,\n contentType: 'application/octet-stream',\n }\n }\n if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) {\n return { serialized: body, contentType: 'application/octet-stream' }\n }\n return { serialized: JSON.stringify(body), contentType: 'application/json' }\n}\n\n/**\n * Interpolate path parameters: /users/:id → /users/123\n */\nfunction interpolatePath(\n path: string,\n params?: Record<string, string | number>,\n): string {\n if (!params) {\n return path\n }\n return path.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, (_, key) => {\n const value = params[key]\n if (value === undefined) {\n throw new Error(`Missing path parameter: :${key}`)\n }\n return encodeURIComponent(String(value))\n })\n}\n\n// ============= Main HTTP Client =============\n\n/**\n * Main HTTP Client Implementation\n * Combines fetch API with axios-like convenience + modern features\n */\nexport class HttpClient implements IHttpClient {\n private requestInterceptors: RequestInterceptor[] = []\n private responseInterceptors: ResponseInterceptor[] = []\n private cache: CacheStrategy\n private devTracker: DevTracker | null\n private config: Required<\n Pick<\n HttpClientConfig,\n 'baseURL' | 'defaultHeaders' | 'defaultTimeout' | 'validateStatus'\n >\n > & {\n cacheStrategy: CacheStrategy\n maxConcurrent: number\n defaultResponseType: ResponseType\n defaultHooks: RequestHooks\n adapter?: (input: RequestInfo, init?: RequestInit) => Promise<Response>\n }\n private requestQueue: RequestQueue | null\n private pendingRequests = new Set<AbortController>()\n\n constructor(config: HttpClientConfig = {}) {\n this.config = {\n baseURL: config.baseURL ?? '',\n defaultHeaders: config.defaultHeaders ?? {\n 'Content-Type': 'application/json',\n },\n defaultTimeout: config.defaultTimeout ?? 30000,\n validateStatus:\n config.validateStatus ?? ((status) => status >= 200 && status < 300),\n cacheStrategy: config.cacheStrategy ?? new MemoryCache(),\n maxConcurrent: config.maxConcurrent ?? 0,\n defaultResponseType: config.defaultResponseType ?? 'auto',\n defaultHooks: config.defaultHooks ?? {},\n adapter: config.adapter,\n }\n this.cache = this.config.cacheStrategy\n this.requestQueue =\n this.config.maxConcurrent > 0\n ? new RequestQueue(this.config.maxConcurrent)\n : null\n this.devTracker = config.devTracker ?? null\n }\n\n /**\n * Core request method — all others delegate to this\n * Pipeline: hooks → cache → interceptors → fetch → parse → validate → transform → interceptors → cache → hooks\n * Fast path: when no features are used, goes directly to fetch\n */\n async request<T = unknown>(\n config: HttpRequestConfig,\n ): Promise<Result<HttpResponse<T>, HttpErrorDetails>> {\n // Fast path: no interceptors, cache, hooks, retry, validate, transform, or queue\n if (\n !this.requestInterceptors.length &&\n !this.responseInterceptors.length &&\n !config.cache?.enabled &&\n !config.hooks &&\n !Object.keys(this.config.defaultHooks).length &&\n !config.retry &&\n !config.validate &&\n !config.transform &&\n !this.requestQueue &&\n !config.onDownloadProgress &&\n !config.signal\n ) {\n return this.fastPath<T>(config)\n }\n\n const hooks = config.hooks\n ? { ...this.config.defaultHooks, ...config.hooks }\n : this.config.defaultHooks\n const maxAttempts = this.getMaxAttempts(config.retry)\n const retryStrategy = this.getRetryStrategy(config.retry)\n\n // Build request once — reused for cache key and execution\n const builtRequest = this.buildRequest(config)\n\n // Lifecycle: onStart\n hooks.onStart?.(builtRequest)\n\n // Acquire queue slot if rate limiting is enabled\n if (this.requestQueue) {\n await this.requestQueue.acquire()\n }\n\n try {\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n let controller: AbortController | undefined\n try {\n // Step 1: Check cache for GET requests\n if (config.method === 'GET' || !config.method) {\n if (config.cache?.enabled) {\n const cacheKey = this.getCacheKey(config)\n const cached = this.cache.get(cacheKey)\n if (cached) {\n const cachedResponse = cached as HttpResponse<T>\n this.trackDev({\n method: config.method ?? 'GET',\n url: builtRequest.url,\n status: cachedResponse.status,\n duration: cachedResponse.duration,\n cached: true,\n ok: true,\n headers: { ...builtRequest.headers },\n body: config.body,\n retryCount: 0,\n })\n hooks.onSuccess?.(cachedResponse)\n hooks.onFinally?.()\n return Ok(cachedResponse)\n }\n }\n }\n\n // Step 2: Run request interceptors on pre-built request\n let finalRequest = builtRequest\n for (const interceptor of this.requestInterceptors) {\n finalRequest = await interceptor.onRequest(finalRequest)\n }\n\n // Step 3: Create AbortController for cancellation\n controller = new AbortController()\n this.pendingRequests.add(controller)\n\n // Merge external signal if provided\n if (config.signal) {\n config.signal.addEventListener('abort', () => controller!.abort(), {\n once: true,\n })\n }\n\n // Step 4: Fetch with timeout + progress tracking\n const startTime = performance.now()\n const response = await this.fetchWithTimeout(\n finalRequest,\n this.resolveTimeoutMs(config.timeout ?? this.config.defaultTimeout),\n controller,\n )\n const duration = performance.now() - startTime\n\n // Step 5: Track download progress if callback provided\n let responseForParsing = response\n if (config.onDownloadProgress && response.body) {\n responseForParsing = this.trackDownloadProgress(\n response,\n config.onDownloadProgress,\n )\n }\n\n // Step 6: Parse response based on responseType\n const responseType =\n config.responseType ?? this.config.defaultResponseType\n const httpResponse = await this.parseResponse<T>(\n responseForParsing,\n finalRequest,\n duration,\n responseType,\n )\n\n // Step 7: Validate status\n if (!this.config.validateStatus(httpResponse.status)) {\n const errorDetails: HttpErrorDetails = {\n message: `Request failed with status ${httpResponse.status}`,\n status: httpResponse.status,\n statusText: httpResponse.statusText,\n code: 'HTTP_ERROR',\n }\n throw errorDetails\n }\n\n // Step 8: Validate response data\n if (config.validate) {\n const validation = config.validate.validate(httpResponse.data)\n if (!validation.ok) {\n return validation\n }\n }\n\n // Step 9: Transform response data\n if (config.transform) {\n httpResponse.data = config.transform.transform(\n httpResponse.data,\n ) as T\n }\n\n // Step 10: Run response interceptors\n let finalResponse = httpResponse\n for (const interceptor of this.responseInterceptors) {\n finalResponse = await interceptor.onResponse(finalResponse)\n }\n\n // Step 11: Cache successful GET responses\n if (\n (config.method === 'GET' || !config.method) &&\n config.cache?.enabled\n ) {\n const cacheKey = this.getCacheKey(config)\n this.cache.set(cacheKey, finalResponse, config.cache.ttlMs)\n }\n\n // Lifecycle: onSuccess\n hooks.onSuccess?.(finalResponse)\n\n // Cleanup\n this.pendingRequests.delete(controller!)\n this.trackDev({\n method: config.method ?? 'GET',\n url: builtRequest.url,\n status: finalResponse.status,\n duration: finalResponse.duration,\n cached: false,\n ok: true,\n headers: { ...builtRequest.headers },\n body: config.body,\n retryCount: attempt - 1,\n })\n\n return Ok(finalResponse)\n } catch (error) {\n let errorDetails: HttpErrorDetails\n if (error instanceof DOMException) {\n errorDetails =\n error.name === 'TimeoutError'\n ? { message: 'Request timed out', code: 'TIMEOUT' }\n : error.name === 'AbortError'\n ? { message: 'Request aborted', code: 'ABORTED' }\n : {\n message: error.message,\n code: 'UNKNOWN_ERROR',\n originalError: error,\n }\n } else {\n errorDetails = this.isHttpErrorDetails(error)\n ? error\n : this.normalizeError(error)\n }\n\n // Lifecycle: onRetry\n if (\n attempt < maxAttempts &&\n retryStrategy.shouldRetry(attempt, errorDetails)\n ) {\n hooks.onRetry?.(attempt, errorDetails)\n const delayMs = retryStrategy.delayMs(attempt)\n await this.delay(delayMs)\n continue\n }\n\n // Run error interceptors\n let finalErrorDetails = errorDetails\n for (const interceptor of this.responseInterceptors) {\n if (interceptor.onError) {\n finalErrorDetails = await interceptor.onError(finalErrorDetails)\n }\n }\n\n // Lifecycle: onError\n hooks.onError?.(finalErrorDetails)\n\n // Cleanup\n if (controller) {\n this.pendingRequests.delete(controller)\n }\n this.trackDev({\n method: config.method ?? 'GET',\n url: builtRequest.url,\n status: finalErrorDetails.status,\n duration: 0,\n cached: false,\n ok: false,\n code: finalErrorDetails.code,\n headers: { ...builtRequest.headers },\n body: config.body,\n retryCount: attempt - 1,\n })\n\n return Err(finalErrorDetails)\n }\n }\n\n const exhaustedError: HttpErrorDetails = {\n message: 'Max retries exceeded',\n code: 'MAX_RETRIES',\n }\n hooks.onError?.(exhaustedError)\n this.trackDev({\n method: config.method ?? 'GET',\n url: builtRequest.url,\n duration: 0,\n cached: false,\n ok: false,\n code: 'MAX_RETRIES',\n headers: { ...builtRequest.headers },\n body: config.body,\n retryCount: maxAttempts,\n })\n return Err(exhaustedError)\n } finally {\n // Lifecycle: onFinally (always runs)\n hooks.onFinally?.()\n\n // Release queue slot\n if (this.requestQueue) {\n this.requestQueue.release()\n }\n }\n }\n\n /**\n * Fast path — minimal overhead for simple requests without features\n * No interceptors, cache, hooks, retry, validate, transform, queue, progress, or signal\n */\n private async fastPath<T = unknown>(\n config: HttpRequestConfig,\n ): Promise<Result<HttpResponse<T>, HttpErrorDetails>> {\n const path = interpolatePath(config.url, config.params)\n const url = this.buildUrl(path, config.query)\n const { serialized, contentType } = serializeBody(config.body)\n\n const headers = { ...this.config.defaultHeaders, ...config.headers }\n if (contentType) {\n headers['Content-Type'] = contentType\n } else if (serialized instanceof FormData) {\n delete headers['Content-Type']\n }\n\n const controller = new AbortController()\n const timeoutMs = this.resolveTimeoutMs(\n config.timeout ?? this.config.defaultTimeout,\n )\n\n this.pendingRequests.add(controller)\n\n try {\n const startTime = performance.now()\n const response = await this.fetchWithTimeout(\n {\n url,\n method: config.method ?? 'GET',\n headers,\n body: config.body,\n params: config.params,\n },\n timeoutMs,\n controller,\n )\n const duration = performance.now() - startTime\n\n const data = await this.parseBody<T>(\n response,\n config.responseType ?? this.config.defaultResponseType,\n )\n\n if (!this.config.validateStatus(response.status)) {\n const result = Err({\n message: `Request failed with status ${response.status}`,\n status: response.status,\n statusText: response.statusText,\n code: 'HTTP_ERROR',\n })\n this.trackDev({\n method: config.method ?? 'GET',\n url,\n status: response.status,\n duration,\n cached: false,\n ok: false,\n code: 'HTTP_ERROR',\n headers,\n body: config.body,\n retryCount: 0,\n })\n this.pendingRequests.delete(controller)\n return result\n }\n\n this.pendingRequests.delete(controller)\n this.trackDev({\n method: config.method ?? 'GET',\n url,\n status: response.status,\n duration,\n cached: false,\n ok: true,\n headers,\n body: config.body,\n retryCount: 0,\n })\n\n return Ok({\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n data,\n request: {\n url,\n method: config.method ?? 'GET',\n headers,\n body: config.body,\n params: config.params,\n },\n duration,\n })\n } catch (error) {\n this.pendingRequests.delete(controller)\n let code = 'UNKNOWN_ERROR'\n if (error instanceof DOMException) {\n if (error.name === 'TimeoutError') {\n code = 'TIMEOUT'\n } else if (error.name === 'AbortError') {\n code = 'ABORTED'\n }\n } else if (error instanceof Error) {\n if (error.name === 'TimeoutError') {\n code = 'TIMEOUT'\n } else if (\n error.name === 'AbortError' ||\n error.message.includes('abort')\n ) {\n code = 'ABORTED'\n } else if (error.name === 'TypeError') {\n code = 'NETWORK_ERROR'\n }\n }\n this.trackDev({\n method: config.method ?? 'GET',\n url,\n duration: performance.now() - (performance.now() - 0),\n cached: false,\n ok: false,\n code,\n headers,\n body: config.body,\n retryCount: 0,\n })\n if (error instanceof DOMException) {\n if (error.name === 'TimeoutError') {\n return Err({ message: 'Request timed out', code: 'TIMEOUT' })\n }\n if (error.name === 'AbortError') {\n return Err({ message: 'Request aborted', code: 'ABORTED' })\n }\n }\n if (error instanceof Error) {\n if (error.name === 'TimeoutError') {\n return Err({ message: 'Request timed out', code: 'TIMEOUT' })\n }\n if (error.name === 'AbortError' || error.message.includes('abort')) {\n return Err({ message: 'Request aborted', code: 'ABORTED' })\n }\n return Err({\n message: error.message,\n code: error.name === 'TypeError' ? 'NETWORK_ERROR' : 'UNKNOWN_ERROR',\n })\n }\n return Err({ message: String(error), code: 'UNKNOWN_ERROR' })\n }\n }\n\n // ============= HTTP Method Shortcuts =============\n\n get<T = unknown>(\n url: string,\n config?: Omit<HttpRequestConfig, 'url' | 'method'>,\n ) {\n return this.request<T>({ ...config, url, method: 'GET' })\n }\n\n post<T = unknown>(\n url: string,\n body?: unknown,\n config?: Omit<HttpRequestConfig, 'url' | 'method' | 'body'>,\n ) {\n return this.request<T>({ ...config, url, method: 'POST', body })\n }\n\n put<T = unknown>(\n url: string,\n body?: unknown,\n config?: Omit<HttpRequestConfig, 'url' | 'method' | 'body'>,\n ) {\n return this.request<T>({ ...config, url, method: 'PUT', body })\n }\n\n patch<T = unknown>(\n url: string,\n body?: unknown,\n config?: Omit<HttpRequestConfig, 'url' | 'method' | 'body'>,\n ) {\n return this.request<T>({ ...config, url, method: 'PATCH', body })\n }\n\n delete<T = unknown>(\n url: string,\n config?: Omit<HttpRequestConfig, 'url' | 'method'>,\n ) {\n return this.request<T>({ ...config, url, method: 'DELETE' })\n }\n\n head(url: string, config?: Omit<HttpRequestConfig, 'url' | 'method'>) {\n return this.request<void>({ ...config, url, method: 'HEAD' })\n }\n\n options(url: string, config?: Omit<HttpRequestConfig, 'url' | 'method'>) {\n return this.request<void>({ ...config, url, method: 'OPTIONS' })\n }\n\n // ============= Interceptor Management =============\n\n addRequestInterceptor(interceptor: RequestInterceptor): Disposer {\n this.requestInterceptors.push(interceptor)\n return () => {\n const idx = this.requestInterceptors.indexOf(interceptor)\n if (idx !== -1) {\n this.requestInterceptors.splice(idx, 1)\n }\n }\n }\n\n addResponseInterceptor(interceptor: ResponseInterceptor): Disposer {\n this.responseInterceptors.push(interceptor)\n return () => {\n const idx = this.responseInterceptors.indexOf(interceptor)\n if (idx !== -1) {\n this.responseInterceptors.splice(idx, 1)\n }\n }\n }\n\n clearInterceptors(): void {\n this.requestInterceptors = []\n this.responseInterceptors = []\n }\n\n /**\n * Clear all cached responses\n */\n clearCache(): void {\n this.cache.clear()\n }\n\n // ============= Cancellation =============\n\n /**\n * Cancel all pending requests\n */\n cancelAll(): void {\n for (const controller of this.pendingRequests) {\n controller.abort()\n }\n this.pendingRequests.clear()\n }\n\n /**\n * Number of currently active requests\n */\n get activeRequests(): number {\n return this.pendingRequests.size\n }\n\n /**\n * Queue stats (only relevant when maxConcurrent > 0)\n */\n get queueStats(): { active: number; pending: number } {\n return {\n active: this.requestQueue?.active ?? this.pendingRequests.size,\n pending: this.requestQueue?.pending ?? 0,\n }\n }\n\n // ============= Extended Features =============\n\n /**\n * Create a child client that inherits config + interceptors.\n * Overrides are merged (headers are shallow-merged, rest overwrites).\n */\n extend(overrides: HttpClientConfig = {}): HttpClient {\n const child = new HttpClient({\n baseURL: overrides.baseURL ?? this.config.baseURL,\n defaultHeaders: {\n ...this.config.defaultHeaders,\n ...overrides.defaultHeaders,\n },\n defaultTimeout: overrides.defaultTimeout ?? this.config.defaultTimeout,\n validateStatus: overrides.validateStatus ?? this.config.validateStatus,\n cacheStrategy: overrides.cacheStrategy ?? this.cache,\n maxConcurrent: overrides.maxConcurrent ?? this.config.maxConcurrent,\n defaultResponseType:\n overrides.defaultResponseType ?? this.config.defaultResponseType,\n defaultHooks: { ...this.config.defaultHooks, ...overrides.defaultHooks },\n adapter: overrides.adapter,\n })\n // Inherit interceptors\n for (const interceptor of this.requestInterceptors) {\n child.addRequestInterceptor(interceptor)\n }\n for (const interceptor of this.responseInterceptors) {\n child.addResponseInterceptor(interceptor)\n }\n return child\n }\n\n /**\n * Auto-paginate a GET endpoint. Yields arrays of items per page.\n *\n * Usage:\n * ```ts\n * for await (const users of client.paginate<UserListResponse>('/users', {\n * getItems: (data) => data.items,\n * getNextPage: (data, cfg) =>\n * data.nextCursor ? { ...cfg, query: { ...cfg.query, cursor: data.nextCursor } } : null,\n * })) {\n * console.log(users); // items from this page\n * }\n * ```\n */\n async *paginate<T = unknown>(\n url: string,\n options: PaginateOptions<T>,\n config: Omit<HttpRequestConfig, 'url' | 'method'> = {},\n ): AsyncGenerator<T[]> {\n let currentConfig: Omit<HttpRequestConfig, 'url' | 'method'> = { ...config }\n\n while (true) {\n const result = await this.get<T>(url, currentConfig)\n if (!result.ok) {\n break\n }\n\n const items = options.getItems(result.value.data) as T[]\n yield items\n\n const nextConfig = options.getNextPage(result.value.data, currentConfig)\n if (!nextConfig) {\n break\n }\n currentConfig = nextConfig\n }\n }\n\n /**\n * Poll an endpoint until a condition is met.\n * Returns the final response that satisfied `until()`.\n *\n * Usage:\n * ```ts\n * const result = await client.poll<Job>('/jobs/123', {\n * intervalMs: 2000,\n * maxAttempts: 30,\n * until: (job) => job.status === 'completed',\n * onPoll: (job, attempt) => console.log(`Attempt ${attempt}: ${job.status}`),\n * });\n * ```\n */\n async poll<T = unknown>(\n url: string,\n options: PollOptions<T>,\n config: Omit<HttpRequestConfig, 'url' | 'method'> = {},\n ): Promise<Result<HttpResponse<T>, HttpErrorDetails>> {\n const maxAttempts = options.maxAttempts ?? 0\n\n for (\n let attempt = 1;\n maxAttempts === 0 || attempt <= maxAttempts;\n attempt++\n ) {\n const result = await this.get<T>(url, config)\n\n if (!result.ok) {\n return result\n }\n\n options.onPoll?.(result.value.data, attempt)\n\n if (options.until(result.value.data)) {\n return result\n }\n\n if (maxAttempts > 0 && attempt >= maxAttempts) {\n break\n }\n\n await this.delay(options.intervalMs)\n }\n\n return Err({\n message: `Polling exhausted after ${maxAttempts} attempts`,\n code: 'POLL_EXHAUSTED',\n })\n }\n\n // ============= Private Helpers =============\n\n private buildRequest(config: HttpRequestConfig): HttpRequest {\n const path = interpolatePath(config.url, config.params)\n const url = this.buildUrl(path, config.query)\n\n return {\n url,\n method: config.method ?? 'GET',\n headers: {\n ...this.config.defaultHeaders,\n ...config.headers,\n },\n body: config.body,\n params: config.params,\n }\n }\n\n private buildUrl(\n path: string,\n query?: Record<string, string | number | boolean>,\n ): string {\n let url = this.config.baseURL + path\n\n if (query) {\n const keys = Object.keys(query)\n if (keys.length > 0) {\n const params = new URLSearchParams()\n for (let i = 0; i < keys.length; i++) {\n params.append(keys[i], String(query[keys[i]]))\n }\n url += `?${params.toString()}`\n }\n }\n\n return url\n }\n\n private getCacheKey(config: HttpRequestConfig): string {\n const path = interpolatePath(config.url, config.params)\n const queryStr = config.query ? JSON.stringify(config.query) : ''\n return `${config.method ?? 'GET'}:${path}${queryStr ? ':' + queryStr : ''}`\n }\n\n private fetchWithTimeout(\n request: HttpRequest,\n timeoutMs: number,\n controller: AbortController,\n ): Promise<Response> {\n const { serialized, contentType } = serializeBody(request.body)\n\n const headers = { ...request.headers }\n if (contentType) {\n headers['Content-Type'] = contentType\n } else if (serialized instanceof FormData) {\n delete headers['Content-Type']\n }\n\n return new Promise<Response>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n controller.abort()\n reject(new DOMException('Request timed out', 'TimeoutError'))\n }, timeoutMs)\n\n const executeFetch = this.config.adapter ?? fetch\n\n executeFetch(request.url, {\n method: request.method,\n headers,\n body: serialized,\n signal: controller.signal,\n }).then(\n (response) => {\n clearTimeout(timeoutId)\n resolve(response)\n },\n (error) => {\n clearTimeout(timeoutId)\n reject(error)\n },\n )\n })\n }\n\n /**\n * Wraps response body with a progress-tracking ReadableStream\n */\n private trackDownloadProgress(\n response: Response,\n onProgress: (event: NexaProgressEvent) => void,\n ): Response {\n const total = parseInt(response.headers.get('content-length') || '0', 10)\n const reader = response.body?.getReader()\n if (!reader) {\n return response\n }\n\n let loaded = 0\n const stream = new ReadableStream({\n async pull(controller) {\n const { done, value } = await reader.read()\n if (done) {\n controller.close()\n return\n }\n loaded += value.byteLength\n onProgress({\n loaded,\n total,\n percent: total > 0 ? Math.round((loaded / total) * 100) : 0,\n })\n controller.enqueue(value)\n },\n })\n\n return new Response(stream, {\n headers: response.headers,\n status: response.status,\n statusText: response.statusText,\n })\n }\n\n private async parseResponse<T>(\n response: Response,\n request: HttpRequest,\n duration: number,\n responseType: ResponseType,\n ): Promise<HttpResponse<T>> {\n const data = await this.parseBody<T>(response, responseType)\n\n return {\n status: response.status,\n statusText: response.statusText,\n headers: response.headers,\n data,\n request,\n duration,\n }\n }\n\n private async parseBody<T>(\n response: Response,\n responseType: ResponseType,\n ): Promise<T> {\n switch (responseType) {\n case 'json':\n return (await response.json()) as T\n case 'text':\n return (await response.text()) as T\n case 'blob':\n return (await response.blob()) as T\n case 'arrayBuffer':\n return (await response.arrayBuffer()) as T\n case 'formData':\n return (await response.formData()) as T\n case 'stream':\n return response.body as T\n case 'auto':\n default: {\n const contentType = response.headers.get('content-type') ?? ''\n if (contentType.includes('application/json')) {\n return (await response.json()) as T\n }\n if (contentType.includes('text/')) {\n return (await response.text()) as T\n }\n if (contentType.includes('multipart/form-data')) {\n return (await response.formData()) as T\n }\n if (\n contentType.includes('application/octet-stream') ||\n contentType.includes('image/') ||\n contentType.includes('audio/') ||\n contentType.includes('video/')\n ) {\n return (await response.blob()) as T\n }\n // Fallback: try JSON, then text\n try {\n return (await response.json()) as T\n } catch {\n return (await response.text()) as T\n }\n }\n }\n }\n\n private normalizeError(error: unknown): HttpErrorDetails {\n if (error instanceof DOMException) {\n if (error.name === 'TimeoutError') {\n return { message: 'Request timed out', code: 'TIMEOUT' }\n }\n if (error.name === 'AbortError') {\n return { message: 'Request aborted', code: 'ABORTED' }\n }\n }\n if (error instanceof Error) {\n if (error.name === 'TimeoutError') {\n return { message: 'Request timed out', code: 'TIMEOUT' }\n }\n if (error.name === 'AbortError' || error.message.includes('abort')) {\n return { message: 'Request aborted', code: 'ABORTED' }\n }\n return {\n message: error.message,\n code: error.name === 'TypeError' ? 'NETWORK_ERROR' : 'UNKNOWN_ERROR',\n originalError: error,\n }\n }\n return {\n message: String(error),\n code: 'UNKNOWN_ERROR',\n originalError: error,\n }\n }\n\n private isHttpErrorDetails(error: unknown): error is HttpErrorDetails {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'message' in error &&\n 'code' in error\n )\n }\n\n private getMaxAttempts(retry?: HttpRequestConfig['retry']): number {\n if (!retry) {\n return 1\n }\n if ('maxAttempts' in retry) {\n return retry.maxAttempts ?? 1\n }\n // RetryStrategy controls retries via shouldRetry; use safe upper bound\n return 100\n }\n\n private getRetryStrategy(retry?: HttpRequestConfig['retry']): RetryStrategy {\n if (!retry) {\n return { shouldRetry: () => false, delayMs: () => 0 }\n }\n if ('shouldRetry' in retry) {\n return retry\n }\n return new ExponentialBackoffRetry(retry.maxAttempts, retry.backoffMs)\n }\n\n private resolveTimeoutMs(timeout: HttpTimeout): number {\n if (typeof timeout === 'number') {\n return timeout\n }\n return timeout.total ?? timeout.response ?? timeout.connection ?? 30000\n }\n\n private delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n }\n\n private trackDev(data: {\n method: string\n url: string\n status?: number\n duration: number\n cached: boolean\n ok: boolean\n code?: string\n headers: Record<string, string>\n body?: unknown\n retryCount: number\n }): void {\n if (this.devTracker) {\n this.devTracker.track(data)\n }\n }\n}\n\n// ============= Errors =============\nexport class HttpError extends Error {\n status: number\n code: string\n response?: unknown\n\n constructor(\n message: string,\n status: number,\n code: string,\n response?: unknown,\n ) {\n super(message)\n this.name = 'HttpError'\n this.status = status\n this.code = code\n this.response = response\n }\n}\n\nexport function isHttpError(error: unknown): error is HttpError {\n return error instanceof HttpError\n}\n\n// ============= Factory =============\n\n/**\n * Factory function (Dependency Inversion — easier testing)\n */\nexport function createHttpClient(config?: HttpClientConfig): HttpClient {\n return new HttpClient(config)\n}\n","/**\n * WebSocket client with automatic reconnection, heartbeat, and plugin support.\n */\n\nimport type {\n WebSocketOptions,\n IWebSocketClient,\n RealtimeMessageEvent,\n} from '../types/index.js'\nimport { PluginManager } from '../utils/index.js'\n\n/**\n * Check if running in Node.js environment\n */\nfunction isNode(): boolean {\n return (\n typeof window === 'undefined' &&\n typeof process !== 'undefined' &&\n process.versions?.node !== undefined\n )\n}\n\n/**\n * Base realtime client with common functionality\n */\nabstract class BaseRealtimeClient {\n protected url: string\n protected options: WebSocketOptions\n protected pluginManager: PluginManager\n protected status: 'connecting' | 'open' | 'closing' | 'closed' = 'closed'\n protected reconnectAttempt = 0\n protected reconnectTimer: ReturnType<typeof setTimeout> | null = null\n protected heartbeatTimer: ReturnType<typeof setInterval> | null = null\n protected stats = {\n messagesSent: 0,\n messagesReceived: 0,\n connectionTime: 0,\n reconnectAttempts: 0,\n }\n private connectionStartTime = 0\n private listeners = {\n open: new Set<(event: Event) => void>(),\n close: new Set<(event?: CloseEvent) => void>(),\n error: new Set<(event: Event) => void>(),\n message: new Set<(event: RealtimeMessageEvent) => void>(),\n }\n\n constructor(url: string, options: WebSocketOptions = {}) {\n this.url = url\n this.options = options\n this.pluginManager = new PluginManager()\n }\n\n protected updateStatus(\n status: 'connecting' | 'open' | 'closing' | 'closed',\n ): void {\n this.status = status\n if (status === 'open') {\n this.connectionStartTime = Date.now()\n } else if (status === 'closed' && this.connectionStartTime > 0) {\n this.stats.connectionTime += Date.now() - this.connectionStartTime\n this.connectionStartTime = 0\n }\n }\n\n protected emitOpen(event: Event): void {\n this.pluginManager.emit('websocket:open', this.url, event)\n for (const listener of this.listeners.open) {\n listener(event)\n }\n }\n\n protected emitClose(event?: CloseEvent): void {\n this.pluginManager.emit('websocket:close', this.url, event)\n for (const listener of this.listeners.close) {\n listener(event)\n }\n }\n\n protected emitError(event: Event | Error): void {\n let errorEvent: Event\n if (event instanceof Error) {\n // Create a synthetic error event\n errorEvent = new Event('error')\n ;(errorEvent as unknown as Record<string, unknown>).error = event\n } else {\n errorEvent = event\n }\n this.pluginManager.emit('websocket:error', this.url, errorEvent)\n for (const listener of this.listeners.error) {\n listener(errorEvent)\n }\n }\n\n protected emitMessage(event: RealtimeMessageEvent): void {\n this.pluginManager.emit('websocket:message', this.url, event)\n for (const listener of this.listeners.message) {\n listener(event)\n }\n }\n\n onOpen(callback: (event: Event) => void): () => void {\n this.listeners.open.add(callback)\n return () => this.listeners.open.delete(callback)\n }\n\n onClose(callback: (event?: CloseEvent) => void): () => void {\n this.listeners.close.add(callback)\n return () => this.listeners.close.delete(callback)\n }\n\n onError(callback: (event: Event) => void): () => void {\n this.listeners.error.add(callback)\n return () => this.listeners.error.delete(callback)\n }\n\n onMessage<T = unknown>(\n callback: (event: RealtimeMessageEvent<T>) => void,\n ): () => void {\n // Type assertion needed because Set doesn't support generic type parameters\n this.listeners.message.add(\n callback as (event: RealtimeMessageEvent) => void,\n )\n return () =>\n this.listeners.message.delete(\n callback as (event: RealtimeMessageEvent) => void,\n )\n }\n\n getStatus(): 'connecting' | 'open' | 'closing' | 'closed' {\n return this.status\n }\n\n getStats() {\n return {\n ...this.stats,\n connectionTime:\n this.stats.connectionTime +\n (this.connectionStartTime > 0\n ? Date.now() - this.connectionStartTime\n : 0),\n }\n }\n\n protected scheduleReconnect(): void {\n if (this.options.reconnect?.enabled === false) {\n return\n }\n\n const maxAttempts = this.options.reconnect?.maxAttempts ?? Infinity\n if (this.reconnectAttempt >= maxAttempts) {\n this.pluginManager.emit(\n 'websocket:reconnect:failed',\n this.url,\n this.reconnectAttempt,\n )\n return\n }\n\n const baseDelay = this.options.reconnect?.baseDelay ?? 1000\n const maxDelay = this.options.reconnect?.maxDelay ?? 30000\n const delay = Math.min(\n maxDelay,\n baseDelay * Math.pow(2, this.reconnectAttempt),\n )\n\n this.reconnectAttempt++\n this.stats.reconnectAttempts = this.reconnectAttempt\n\n this.options.reconnect?.onReconnecting?.(this.reconnectAttempt, delay)\n this.pluginManager.emit(\n 'websocket:reconnecting',\n this.url,\n this.reconnectAttempt,\n delay,\n )\n\n this.reconnectTimer = setTimeout(() => {\n this.connect().catch((err) => {\n this.emitError(err instanceof Error ? err : new Error(String(err)))\n })\n }, delay)\n }\n\n protected startHeartbeat(): void {\n if (!this.options.heartbeat) {\n return\n }\n\n const interval = this.options.heartbeat.interval ?? 30000\n const pingMessage = this.options.heartbeat.pingMessage ?? 'ping'\n const pongMessage = this.options.heartbeat.pongMessage ?? 'pong'\n\n let pongReceived = true\n\n const checkPong = () => {\n if (!pongReceived) {\n this.pluginManager.emit('websocket:heartbeat:timeout', this.url)\n this.disconnect()\n return\n }\n pongReceived = false\n this.send(pingMessage)\n this.heartbeatTimer = setTimeout(checkPong, interval)\n }\n\n // Listen for pong messages\n const messageListener = (event: RealtimeMessageEvent) => {\n const data = event.raw\n if (typeof data === 'string' && data === pongMessage) {\n pongReceived = true\n }\n }\n\n this.onMessage(messageListener)\n this.heartbeatTimer = setTimeout(checkPong, interval)\n }\n\n protected stopHeartbeat(): void {\n if (this.heartbeatTimer) {\n clearTimeout(this.heartbeatTimer)\n this.heartbeatTimer = null\n }\n }\n\n protected cleanup(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer)\n this.reconnectTimer = null\n }\n this.stopHeartbeat()\n }\n\n abstract connect(): Promise<void>\n abstract disconnect(): void\n abstract send(data: string | ArrayBuffer | Blob): void\n}\n\n/**\n * Browser WebSocket client implementation\n */\nclass BrowserWebSocketClient\n extends BaseRealtimeClient\n implements IWebSocketClient\n{\n socket: WebSocket | null = null\n private messageTypeListeners = new Map<string, Set<(data: unknown) => void>>()\n\n constructor(url: string, options: WebSocketOptions = {}) {\n super(url, options)\n }\n\n async connect(): Promise<void> {\n if (this.status === 'connecting' || this.status === 'open') {\n return\n }\n\n this.updateStatus('connecting')\n this.pluginManager.emit('websocket:connect:start', this.url)\n\n return new Promise((resolve, reject) => {\n const timeout = this.options.timeout ?? 10000\n const timeoutTimer = setTimeout(() => {\n this.updateStatus('closed')\n this.socket?.close()\n this.socket = null\n const error = new Error(\n `WebSocket connection timeout after ${timeout}ms`,\n )\n this.emitError(error)\n reject(error)\n }, timeout)\n\n try {\n this.socket = new WebSocket(this.url, this.options.protocols)\n\n this.socket.onopen = (event) => {\n clearTimeout(timeoutTimer)\n this.updateStatus('open')\n this.reconnectAttempt = 0\n this.emitOpen(event)\n this.options.onOpen?.(event)\n this.startHeartbeat()\n this.pluginManager.emit('websocket:connect:success', this.url)\n resolve()\n }\n\n this.socket.onclose = (event) => {\n clearTimeout(timeoutTimer)\n this.updateStatus('closed')\n this.emitClose(event)\n this.options.onClose?.(event)\n this.stopHeartbeat()\n this.pluginManager.emit(\n 'websocket:disconnected',\n this.url,\n event.code,\n event.reason,\n )\n\n // Schedule reconnect if not explicitly closed by user\n if (event.code !== 1000 && !event.wasClean) {\n this.scheduleReconnect()\n }\n }\n\n this.socket.onerror = (event) => {\n clearTimeout(timeoutTimer)\n this.updateStatus('closed')\n this.emitError(event)\n this.options.onError?.(event)\n this.pluginManager.emit('websocket:connect:error', this.url, event)\n reject(event)\n }\n\n this.socket.onmessage = (event) => {\n this.stats.messagesReceived++\n const messageEvent: RealtimeMessageEvent = {\n data: this.tryParseData(event.data),\n raw: event.data,\n type: 'message',\n timestamp: Date.now(),\n }\n this.emitMessage(messageEvent)\n this.pluginManager.emit(\n 'websocket:message:received',\n this.url,\n messageEvent,\n )\n }\n } catch (error) {\n clearTimeout(timeoutTimer)\n this.updateStatus('closed')\n this.pluginManager.emit('websocket:connect:error', this.url, error)\n reject(error)\n }\n })\n }\n\n disconnect(): void {\n this.cleanup()\n if (this.socket) {\n this.updateStatus('closing')\n this.socket.close(1000, 'Client disconnected')\n this.socket = null\n this.updateStatus('closed')\n }\n }\n\n send(data: string | ArrayBuffer | Blob): void {\n if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {\n throw new Error('WebSocket is not connected')\n }\n this.socket.send(data)\n this.stats.messagesSent++\n this.pluginManager.emit('websocket:message:sent', this.url, data)\n }\n\n sendJson(data: unknown): void {\n this.send(JSON.stringify(data))\n }\n\n onMessageType<T = unknown>(\n type: string,\n callback: (data: T) => void,\n ): () => void {\n if (!this.messageTypeListeners.has(type)) {\n this.messageTypeListeners.set(type, new Set())\n }\n this.messageTypeListeners\n .get(type)!\n .add(callback as (data: unknown) => void)\n\n return () => {\n const listeners = this.messageTypeListeners.get(type)\n if (listeners) {\n listeners.delete(callback as (data: unknown) => void)\n if (listeners.size === 0) {\n this.messageTypeListeners.delete(type)\n }\n }\n }\n }\n\n private tryParseData(data: string | ArrayBuffer | Blob): unknown {\n if (typeof data === 'string') {\n try {\n return JSON.parse(data)\n } catch {\n return data\n }\n }\n return data\n }\n}\n\n/**\n * Node.js WebSocket client implementation (requires 'ws' package)\n */\nclass NodeWebSocketClient extends BrowserWebSocketClient {\n async connect(): Promise<void> {\n if (isNode()) {\n try {\n // Security: Optional dependency 'ws' for Node.js WebSocket support\n // This is a dynamic import that only loads if the package is installed\n // @ts-ignore - optional dependency\n const { default: _ws } = await import('ws')\n void _ws\n // Override socket creation\n // Note: This is a simplified implementation\n // In a full implementation, we'd need to handle the WebSocket constructor differently\n return super.connect()\n } catch {\n throw new Error(\n 'WebSocket client for Node.js requires the \"ws\" package. Please install it: npm install ws',\n )\n }\n }\n return super.connect()\n }\n}\n\n/**\n * Create a WebSocket client appropriate for the current environment\n */\nexport function createWebSocketClient(\n url: string,\n options: WebSocketOptions = {},\n): IWebSocketClient {\n if (isNode()) {\n return new NodeWebSocketClient(url, options)\n }\n return new BrowserWebSocketClient(url, options)\n}\n","/**\n * Server-Sent Events (SSE) client with automatic reconnection and plugin support.\n */\n\nimport type {\n SSEOptions,\n ISSEClient,\n RealtimeMessageEvent,\n} from '../types/index.js'\nimport { PluginManager } from '../utils/index.js'\n\n/**\n * Check if running in Node.js environment\n */\nfunction isNode(): boolean {\n return (\n typeof window === 'undefined' &&\n typeof process !== 'undefined' &&\n process.versions?.node !== undefined\n )\n}\n\n/**\n * Browser SSE client implementation using EventSource\n */\nclass BrowserSSEClient implements ISSEClient {\n private url: string\n private options: SSEOptions\n private _source: EventSource | null = null\n private pluginManager: PluginManager\n private status: 'connecting' | 'open' | 'closing' | 'closed' = 'closed'\n private reconnectAttempt = 0\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null\n private stats = {\n messagesSent: 0, // SSE is receive-only\n messagesReceived: 0,\n connectionTime: 0,\n reconnectAttempts: 0,\n }\n private connectionStartTime = 0\n private listeners = {\n open: new Set<(event: Event) => void>(),\n close: new Set<() => void>(),\n error: new Set<(event: Event) => void>(),\n message: new Set<(event: RealtimeMessageEvent) => void>(),\n event: new Map<string, Set<(data: unknown) => void>>(),\n }\n private _lastEventId: string | null = null\n\n constructor(url: string, options: SSEOptions = {}) {\n this.url = url\n this.options = options\n this.pluginManager = new PluginManager()\n }\n\n private updateStatus(\n status: 'connecting' | 'open' | 'closing' | 'closed',\n ): void {\n this.status = status\n if (status === 'open') {\n this.connectionStartTime = Date.now()\n } else if (status === 'closed' && this.connectionStartTime > 0) {\n this.stats.connectionTime += Date.now() - this.connectionStartTime\n this.connectionStartTime = 0\n }\n }\n\n async connect(): Promise<void> {\n if (this.status === 'connecting' || this.status === 'open') {\n return\n }\n\n this.updateStatus('connecting')\n this.pluginManager.emit('sse:connect:start', this.url)\n\n return new Promise((resolve, reject) => {\n const timeout = this.options.timeout ?? 10000\n const timeoutTimer = setTimeout(() => {\n this.updateStatus('closed')\n this._source?.close()\n this._source = null\n const error = new Error(`SSE connection timeout after ${timeout}ms`)\n this.emitError(error)\n reject(error)\n }, timeout)\n\n try {\n // EventSource doesn't support custom headers or POST requests in standard API\n // For advanced features, we'd need to use fetch with streaming\n this._source = new EventSource(this.url)\n\n this._source.onopen = (event) => {\n clearTimeout(timeoutTimer)\n this.updateStatus('open')\n this.reconnectAttempt = 0\n this.emitOpen(event)\n this.options.onOpen?.(event)\n this.pluginManager.emit('sse:connect:success', this.url)\n resolve()\n }\n\n this._source.onerror = (event) => {\n // EventSource doesn't provide close event, only error\n // We'll treat certain error states as disconnections\n clearTimeout(timeoutTimer)\n this.updateStatus('closed')\n this.emitError(event)\n this.options.onError?.(event)\n this.pluginManager.emit('sse:connect:error', this.url, event)\n\n if (this._source?.readyState === EventSource.CLOSED) {\n this.emitClose()\n this.options.onClose?.()\n this.scheduleReconnect()\n }\n reject(event)\n }\n\n // Listen for messages\n this._source.onmessage = (event) => {\n this.stats.messagesReceived++\n this._lastEventId = event.lastEventId || this._lastEventId\n\n const messageEvent: RealtimeMessageEvent = {\n data: this.tryParseData(event.data),\n raw: event.data,\n type: event.type || 'message',\n timestamp: Date.now(),\n }\n\n this.emitMessage(messageEvent)\n this.pluginManager.emit(\n 'sse:message:received',\n this.url,\n messageEvent,\n )\n\n // Also emit to event-specific listeners\n const eventType = event.type || 'message'\n const eventListeners = this.listeners.event.get(eventType)\n if (eventListeners) {\n for (const listener of eventListeners) {\n listener(messageEvent.data)\n }\n }\n }\n\n // Listen for named events\n this._source.addEventListener = this._source.addEventListener.bind(\n this._source,\n )\n } catch (error) {\n clearTimeout(timeoutTimer)\n this.updateStatus('closed')\n this.pluginManager.emit('sse:connect:error', this.url, error)\n reject(error)\n }\n })\n }\n\n disconnect(): void {\n this.cleanup()\n if (this._source) {\n this.updateStatus('closing')\n this._source.close()\n this._source = null\n this.updateStatus('closed')\n this.emitClose()\n this.options.onClose?.()\n }\n }\n\n send(_data: string | ArrayBuffer | Blob): void {\n throw new Error(\n 'SSE is a receive-only protocol. Use HTTP requests to send data to server.',\n )\n }\n\n onMessage<T = unknown>(\n callback: (event: RealtimeMessageEvent<T>) => void,\n ): () => void {\n this.listeners.message.add(\n callback as (event: RealtimeMessageEvent) => void,\n )\n return () =>\n this.listeners.message.delete(\n callback as (event: RealtimeMessageEvent) => void,\n )\n }\n\n onOpen(callback: (event: Event) => void): () => void {\n this.listeners.open.add(callback)\n return () => this.listeners.open.delete(callback)\n }\n\n onClose(callback: () => void): () => void {\n this.listeners.close.add(callback)\n return () => this.listeners.close.delete(callback)\n }\n\n onError(callback: (event: Event) => void): () => void {\n this.listeners.error.add(callback)\n return () => this.listeners.error.delete(callback)\n }\n\n onEvent<T = unknown>(event: string, callback: (data: T) => void): () => void {\n if (!this.listeners.event.has(event)) {\n this.listeners.event.set(event, new Set())\n }\n this.listeners.event.get(event)!.add(callback as (data: unknown) => void)\n\n // Also set up EventSource listener if connected\n if (\n this._source &&\n !(this._source as unknown as Record<string, unknown>)[`on${event}`]\n ) {\n this._source.addEventListener(event, (e: MessageEvent) => {\n const messageEvent: RealtimeMessageEvent = {\n data: this.tryParseData(e.data),\n raw: e.data,\n type: event,\n timestamp: Date.now(),\n }\n callback(messageEvent.data as T)\n })\n }\n\n return () => {\n const listeners = this.listeners.event.get(event)\n if (listeners) {\n listeners.delete(callback as (data: unknown) => void)\n if (listeners.size === 0) {\n this.listeners.event.delete(event)\n }\n }\n }\n }\n\n getStatus(): 'connecting' | 'open' | 'closing' | 'closed' {\n return this.status\n }\n\n getStats() {\n return {\n ...this.stats,\n connectionTime:\n this.stats.connectionTime +\n (this.connectionStartTime > 0\n ? Date.now() - this.connectionStartTime\n : 0),\n }\n }\n\n get lastEventId(): string | null {\n return this._lastEventId\n }\n\n get source(): EventSource | null {\n return this._source\n }\n\n private emitOpen(event: Event): void {\n this.pluginManager.emit('sse:open', this.url, event)\n for (const listener of this.listeners.open) {\n listener(event)\n }\n }\n\n private emitClose(): void {\n this.pluginManager.emit('sse:close', this.url)\n for (const listener of this.listeners.close) {\n listener()\n }\n }\n\n private emitError(event: Event | Error): void {\n let errorEvent: Event\n if (event instanceof Error) {\n // Create a synthetic error event\n errorEvent = new Event('error')\n ;(errorEvent as unknown as Record<string, unknown>).error = event\n } else {\n errorEvent = event\n }\n this.pluginManager.emit('sse:error', this.url, errorEvent)\n for (const listener of this.listeners.error) {\n listener(errorEvent)\n }\n }\n\n private emitMessage(event: RealtimeMessageEvent): void {\n this.pluginManager.emit('sse:message', this.url, event)\n for (const listener of this.listeners.message) {\n listener(event)\n }\n }\n\n private scheduleReconnect(): void {\n if (this.options.reconnect?.enabled === false) {\n return\n }\n\n const maxAttempts = this.options.reconnect?.maxAttempts ?? Infinity\n if (this.reconnectAttempt >= maxAttempts) {\n this.pluginManager.emit(\n 'sse:reconnect:failed',\n this.url,\n this.reconnectAttempt,\n )\n return\n }\n\n const baseDelay = this.options.reconnect?.baseDelay ?? 1000\n const maxDelay = this.options.reconnect?.maxDelay ?? 30000\n const delay = Math.min(\n maxDelay,\n baseDelay * Math.pow(2, this.reconnectAttempt),\n )\n\n this.reconnectAttempt++\n this.stats.reconnectAttempts = this.reconnectAttempt\n\n this.options.reconnect?.onReconnecting?.(this.reconnectAttempt, delay)\n this.pluginManager.emit(\n 'sse:reconnecting',\n this.url,\n this.reconnectAttempt,\n delay,\n )\n\n this.reconnectTimer = setTimeout(() => {\n this.connect().catch((err) => {\n this.emitError(err instanceof Error ? err : new Error(String(err)))\n })\n }, delay)\n }\n\n private cleanup(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer)\n this.reconnectTimer = null\n }\n }\n\n private tryParseData(data: string): unknown {\n try {\n return JSON.parse(data)\n } catch {\n return data\n }\n }\n}\n\n/**\n * Node.js SSE client implementation using fetch with streaming\n * Note: This is a basic implementation - EventSource is not available in Node.js\n */\nclass NodeSSEClient extends BrowserSSEClient {\n async connect(): Promise<void> {\n if (isNode()) {\n throw new Error(\n 'SSE client for Node.js requires a polyfill or custom implementation. ' +\n 'Consider using a library like \"eventsource\" or implement using fetch with streaming.',\n )\n }\n return super.connect()\n }\n}\n\n/**\n * Create an SSE client appropriate for the current environment\n */\nexport function createSSEClient(\n url: string,\n options: SSEOptions = {},\n): ISSEClient {\n if (isNode()) {\n return new NodeSSEClient(url, options)\n }\n return new BrowserSSEClient(url, options)\n}\n","/**\n * Real-time plugin for Nexa\n * Provides WebSocket and SSE integration with the plugin system\n */\n\nimport type { Plugin, PluginManager } from '../utils/index.js'\n\n/**\n * Real-time plugin that adds WebSocket and SSE event listeners to the plugin manager\n */\nexport class RealtimePlugin implements Plugin {\n name = 'realtime'\n\n setup(manager: PluginManager): void {\n // Add event listeners for real-time communication\n manager.on('websocket:connect:start', (...args: unknown[]) => {\n const url = args[0] as string\n manager.emit('realtime:connect:start', 'websocket', url)\n })\n\n manager.on('websocket:connect:success', (...args: unknown[]) => {\n const url = args[0] as string\n manager.emit('realtime:connect:success', 'websocket', url)\n })\n\n manager.on('websocket:message:received', (...args: unknown[]) => {\n const url = args[0] as string\n const message = args[1] as unknown\n manager.emit('realtime:message', 'websocket', url, message)\n })\n\n manager.on('sse:connect:start', (...args: unknown[]) => {\n const url = args[0] as string\n manager.emit('realtime:connect:start', 'sse', url)\n })\n\n manager.on('sse:connect:success', (...args: unknown[]) => {\n const url = args[0] as string\n manager.emit('realtime:connect:success', 'sse', url)\n })\n\n manager.on('sse:message:received', (...args: unknown[]) => {\n const url = args[0] as string\n const message = args[1] as unknown\n manager.emit('realtime:message', 'sse', url, message)\n })\n }\n}\n\n/**\n * Create a realtime plugin instance\n */\nexport function createRealtimePlugin(): RealtimePlugin {\n return new RealtimePlugin()\n}\n","import type { TrackedRequest, DevMetrics, DevOverlayConfig } from './types'\n\nexport class RequestTracker {\n private history: TrackedRequest[] = []\n private maxHistory: number\n private listeners: Set<(request: TrackedRequest) => void> = new Set()\n private startTime = Date.now()\n private config: Required<DevOverlayConfig>\n\n constructor(config: DevOverlayConfig = {}) {\n this.maxHistory = config.maxHistory ?? 500\n this.config = {\n enabled: config.enabled ?? true,\n maxHistory: this.maxHistory,\n keyboardShortcut: config.keyboardShortcut ?? 'ctrl+shift+n',\n position: config.position ?? 'bottom-right',\n theme: config.theme ?? 'dark',\n }\n }\n\n track(request: Omit<TrackedRequest, 'id' | 'timestamp'>): TrackedRequest {\n const tracked: TrackedRequest = {\n ...request,\n id: this.generateId(),\n timestamp: Date.now(),\n }\n\n this.history.unshift(tracked)\n if (this.history.length > this.maxHistory) {\n this.history.pop()\n }\n\n for (const listener of this.listeners) {\n listener(tracked)\n }\n\n return tracked\n }\n\n getHistory(): TrackedRequest[] {\n return this.history\n }\n\n getMetrics(): DevMetrics {\n const durations = this.history.map((r) => r.duration)\n const elapsed = (Date.now() - this.startTime) / 1000\n\n return {\n totalRequests: this.history.length,\n successfulRequests: this.history.filter((r) => r.ok).length,\n failedRequests: this.history.filter((r) => !r.ok).length,\n cachedRequests: this.history.filter((r) => r.cached).length,\n avgDuration: durations.length\n ? durations.reduce((a, b) => a + b, 0) / durations.length\n : 0,\n maxDuration: durations.length ? Math.max(...durations) : 0,\n minDuration: durations.length ? Math.min(...durations) : 0,\n requestsPerSecond: elapsed > 0 ? this.history.length / elapsed : 0,\n slowestRequests: [...this.history]\n .sort((a, b) => b.duration - a.duration)\n .slice(0, 5),\n }\n }\n\n clear(): void {\n this.history = []\n this.startTime = Date.now()\n }\n\n onChange(listener: (request: TrackedRequest) => void): () => void {\n this.listeners.add(listener)\n return () => this.listeners.delete(listener)\n }\n\n getConfig(): Required<DevOverlayConfig> {\n return this.config\n }\n\n private generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`\n }\n}\n","import type { TrackedRequest, DevOverlayConfig } from './types'\nimport type { RequestTracker } from './tracker'\n\nconst ICONS = {\n close: `<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 6L6 18M6 6l12 12\"/></svg>`,\n chevron: `<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M9 18l6-6-6-6\"/></svg>`,\n back: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M19 12H5M12 19l-7-7 7-7\"/></svg>`,\n retry: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M1 4v6h6M23 20v-6h-6\"/><path d=\"M20.49 9A9 9 0 005.64 5.64L1 10m22 4l-4.64 4.36A9 9 0 013.51 15\"/></svg>`,\n clear: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2\"/></svg>`,\n search: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><circle cx=\"11\" cy=\"11\" r=\"8\"/><path d=\"M21 21l-4.35-4.35\"/></svg>`,\n clock: `<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M12 6v6l4 2\"/></svg>`,\n zap: `<svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polygon points=\"13 2 3 14 12 14 11 22 21 10 12 10 13 2\"/></svg>`,\n}\n\nconst COLORS = {\n bg: '#09090b',\n bgElevated: '#18181b',\n border: '#27272a',\n borderFocus: '#3f3f46',\n text: '#fafafa',\n textMuted: '#a1a1aa',\n textDim: '#71717a',\n accent: '#3b82f6',\n accentHover: '#2563eb',\n success: '#22c55e',\n successBg: 'rgba(34, 197, 94, 0.1)',\n error: '#ef4444',\n errorBg: 'rgba(239, 68, 68, 0.1)',\n warning: '#f59e0b',\n get: '#22c55e',\n post: '#3b82f6',\n put: '#f59e0b',\n patch: '#a855f7',\n delete: '#ef4444',\n}\n\nconst STYLES = `\n #nexa-dev-overlay * { margin: 0; padding: 0; box-sizing: border-box; }\n #nexa-dev-overlay {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n background: ${COLORS.bg};\n color: ${COLORS.text};\n border: 1px solid ${COLORS.border};\n border-radius: 16px;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255,255,255,0.03);\n overflow: hidden;\n }\n #nexa-dev-overlay .nexa-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid ${COLORS.border};\n background: ${COLORS.bgElevated};\n }\n #nexa-dev-overlay .nexa-header-left {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n #nexa-dev-overlay .nexa-logo {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: ${COLORS.bgElevated};\n border-radius: 8px;\n overflow: hidden;\n }\n #nexa-dev-overlay .nexa-title {\n font-size: 15px;\n font-weight: 600;\n letter-spacing: -0.02em;\n }\n #nexa-dev-overlay .nexa-header-actions {\n display: flex;\n gap: 4px;\n }\n #nexa-dev-overlay .nexa-icon-btn {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n border-radius: 8px;\n color: ${COLORS.textMuted};\n cursor: pointer;\n transition: all 0.15s;\n }\n #nexa-dev-overlay .nexa-icon-btn:hover {\n background: ${COLORS.border};\n color: ${COLORS.text};\n }\n #nexa-dev-overlay .nexa-metrics-bar {\n display: flex;\n gap: 4px;\n padding: 12px 16px;\n background: ${COLORS.bg};\n border-bottom: 1px solid ${COLORS.border};\n }\n #nexa-dev-overlay .nexa-metric {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 2px;\n padding: 8px 12px;\n background: ${COLORS.bgElevated};\n border-radius: 10px;\n transition: all 0.2s;\n }\n #nexa-dev-overlay .nexa-metric:hover {\n background: ${COLORS.border};\n }\n #nexa-dev-overlay .nexa-metric-value {\n font-size: 18px;\n font-weight: 700;\n letter-spacing: -0.03em;\n color: ${COLORS.text};\n }\n #nexa-dev-overlay .nexa-metric-ok .nexa-metric-value { color: ${COLORS.success}; }\n #nexa-dev-overlay .nexa-metric-err .nexa-metric-value { color: ${COLORS.error}; }\n #nexa-dev-overlay .nexa-metric-label {\n font-size: 11px;\n color: ${COLORS.textDim};\n text-transform: uppercase;\n letter-spacing: 0.05em;\n }\n #nexa-dev-overlay .nexa-search {\n padding: 12px 16px;\n border-bottom: 1px solid ${COLORS.border};\n position: relative;\n }\n #nexa-dev-overlay .nexa-search-icon {\n position: absolute;\n left: 28px;\n top: 50%;\n transform: translateY(-50%);\n color: ${COLORS.textDim};\n }\n #nexa-dev-overlay .nexa-search-input {\n width: 100%;\n padding: 10px 12px 10px 38px;\n background: ${COLORS.bg};\n border: 1px solid ${COLORS.border};\n border-radius: 10px;\n color: ${COLORS.text};\n font-size: 13px;\n outline: none;\n transition: all 0.15s;\n }\n #nexa-dev-overlay .nexa-search-input:focus {\n border-color: ${COLORS.accent};\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);\n }\n #nexa-dev-overlay .nexa-search-input::placeholder { color: ${COLORS.textDim}; }\n #nexa-dev-overlay .nexa-tabs {\n display: flex;\n gap: 4px;\n padding: 8px 16px;\n border-bottom: 1px solid ${COLORS.border};\n }\n #nexa-dev-overlay .nexa-tab {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n background: transparent;\n border: none;\n border-radius: 8px;\n color: ${COLORS.textMuted};\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s;\n }\n #nexa-dev-overlay .nexa-tab:hover { color: ${COLORS.text}; background: ${COLORS.bgElevated}; }\n #nexa-dev-overlay .nexa-tab-active { color: ${COLORS.text}; background: ${COLORS.accent} !important; }\n #nexa-dev-overlay .nexa-tab-count {\n font-size: 11px;\n padding: 2px 6px;\n background: rgba(255,255,255,0.1);\n border-radius: 10px;\n }\n #nexa-dev-overlay .nexa-body { flex: 1; overflow: hidden; display: flex; }\n #nexa-dev-overlay .nexa-panel { display: none; width: 100%; overflow-y: auto; }\n #nexa-dev-overlay .nexa-panel-active { display: block; }\n #nexa-dev-overlay .nexa-request-list {\n padding: 8px;\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n #nexa-dev-overlay .nexa-request-item {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 14px;\n background: ${COLORS.bgElevated};\n border: 1px solid transparent;\n border-radius: 12px;\n cursor: pointer;\n transition: all 0.15s;\n animation: nexaFadeIn 0.2s ease forwards;\n opacity: 0;\n }\n @keyframes nexaFadeIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: translateY(0); } }\n #nexa-dev-overlay .nexa-request-item:hover {\n background: ${COLORS.border};\n border-color: ${COLORS.borderFocus};\n transform: translateX(2px);\n }\n #nexa-dev-overlay .nexa-req-left {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n #nexa-dev-overlay .nexa-method {\n font-size: 11px;\n font-weight: 700;\n padding: 4px 8px;\n border-radius: 6px;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n }\n #nexa-dev-overlay .nexa-method-get { background: ${COLORS.successBg}; color: ${COLORS.get}; }\n #nexa-dev-overlay .nexa-method-post { background: rgba(59, 130, 246, 0.15); color: ${COLORS.post}; }\n #nexa-dev-overlay .nexa-method-put { background: rgba(245, 158, 11, 0.15); color: ${COLORS.put}; }\n #nexa-dev-overlay .nexa-method-patch { background: rgba(168, 85, 247, 0.15); color: ${COLORS.patch}; }\n #nexa-dev-overlay .nexa-method-delete { background: ${COLORS.errorBg}; color: ${COLORS.delete}; }\n #nexa-dev-overlay .nexa-status {\n font-size: 12px;\n font-weight: 600;\n padding: 4px 8px;\n border-radius: 6px;\n min-width: 36px;\n text-align: center;\n }\n #nexa-dev-overlay .nexa-ok { background: ${COLORS.successBg}; color: ${COLORS.success}; }\n #nexa-dev-overlay .nexa-err { background: ${COLORS.errorBg}; color: ${COLORS.error}; }\n #nexa-dev-overlay .nexa-url {\n font-size: 13px;\n color: ${COLORS.textMuted};\n max-width: 180px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n #nexa-dev-overlay .nexa-req-right {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n #nexa-dev-overlay .nexa-badge {\n font-size: 10px;\n font-weight: 600;\n padding: 3px 6px;\n border-radius: 6px;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n }\n #nexa-dev-overlay .nexa-badge-cache { background: rgba(168, 85, 247, 0.15); color: #a855f7; }\n #nexa-dev-overlay .nexa-badge-retry { background: rgba(245, 158, 11, 0.15); color: ${COLORS.warning}; }\n #nexa-dev-overlay .nexa-duration {\n font-size: 12px;\n font-weight: 600;\n color: ${COLORS.textDim};\n font-variant-numeric: tabular-nums;\n }\n #nexa-dev-overlay .nexa-slow { color: ${COLORS.warning}; }\n #nexa-dev-overlay .nexa-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 48px 24px;\n text-align: center;\n color: ${COLORS.textDim};\n }\n #nexa-dev-overlay .nexa-empty svg { margin-bottom: 16px; opacity: 0.4; }\n #nexa-dev-overlay .nexa-empty p { font-size: 14px; color: ${COLORS.textMuted}; margin-bottom: 4px; }\n #nexa-dev-overlay .nexa-empty span { font-size: 12px; color: ${COLORS.textDim}; }\n #nexa-dev-overlay .nexa-detail {\n flex-direction: column;\n padding: 16px;\n display: none;\n overflow-y: auto;\n max-height: 100%;\n }\n #nexa-dev-overlay .nexa-detail-active { display: flex; }\n #nexa-dev-overlay .nexa-detail-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 16px;\n }\n #nexa-dev-overlay .nexa-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 14px;\n background: ${COLORS.bgElevated};\n border: 1px solid ${COLORS.border};\n border-radius: 8px;\n color: ${COLORS.textMuted};\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s;\n }\n #nexa-dev-overlay .nexa-btn:hover { background: ${COLORS.border}; color: ${COLORS.text}; }\n #nexa-dev-overlay .nexa-btn-retry { background: ${COLORS.successBg}; border-color: transparent; color: ${COLORS.success}; }\n #nexa-dev-overlay .nexa-btn-retry:hover { background: ${COLORS.success}; color: white; }\n #nexa-dev-overlay .nexa-card {\n background: ${COLORS.bgElevated};\n border: 1px solid ${COLORS.border};\n border-radius: 12px;\n padding: 16px;\n margin-bottom: 12px;\n }\n #nexa-dev-overlay .nexa-card h3 {\n font-size: 12px;\n font-weight: 600;\n color: ${COLORS.textDim};\n text-transform: uppercase;\n letter-spacing: 0.05em;\n margin-bottom: 12px;\n }\n #nexa-dev-overlay .nexa-row {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 6px 0;\n font-size: 13px;\n border-bottom: 1px solid ${COLORS.border};\n }\n #nexa-dev-overlay .nexa-row:last-child { border-bottom: none; }\n #nexa-dev-overlay .nexa-row span { color: ${COLORS.textMuted}; }\n #nexa-dev-overlay .nexa-row strong { color: ${COLORS.text}; font-weight: 500; font-variant-numeric: tabular-nums; }\n #nexa-dev-overlay .nexa-row .nexa-ok { color: ${COLORS.success}; }\n #nexa-dev-overlay .nexa-row .nexa-err { color: ${COLORS.error}; }\n #nexa-dev-overlay .nexa-code {\n background: ${COLORS.bg};\n border-radius: 8px;\n padding: 12px;\n font-size: 11px;\n font-family: 'JetBrains Mono', 'Fira Code', monospace;\n color: ${COLORS.textMuted};\n overflow-x: auto;\n white-space: pre;\n max-height: 200px;\n line-height: 1.6;\n }\n #nexa-dev-overlay .nexa-url-full {\n font-size: 12px;\n word-break: break-all;\n color: ${COLORS.accent};\n }\n #nexa-dev-overlay .nexa-metrics-content {\n padding: 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n #nexa-dev-overlay .nexa-slow-req {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n`\n\nexport class DevOverlayUI {\n private panel: HTMLElement | null = null\n private tracker: RequestTracker\n private visible = false\n private selectedRequest: TrackedRequest | null = null\n private config: Required<DevOverlayConfig>\n private searchQuery = ''\n private removeTrackerListener: (() => void) | null = null\n private keyboardShortcutHandler: ((e: KeyboardEvent) => void) | null = null\n private globalKeyboardHandler: ((e: KeyboardEvent) => void) | null = null\n\n constructor(tracker: RequestTracker) {\n this.tracker = tracker\n this.config = tracker.getConfig()\n if (!this.canUseDOM()) {\n return\n }\n this.setupKeyboardShortcut()\n this.createPanel()\n }\n\n show(): void {\n if (!this.panel) {\n return\n }\n this.panel.style.display = 'flex'\n this.panel.style.opacity = '0'\n this.panel.style.transform = 'scale(0.96) translateY(8px)'\n const animate =\n typeof requestAnimationFrame === 'function'\n ? requestAnimationFrame\n : (callback: FrameRequestCallback) => setTimeout(callback, 0)\n animate(() => {\n this.panel!.style.transition = 'all 0.25s cubic-bezier(0.16, 1, 0.3, 1)'\n this.panel!.style.opacity = '1'\n this.panel!.style.transform = 'scale(1) translateY(0)'\n })\n this.visible = true\n }\n\n hide(): void {\n if (!this.panel) {\n return\n }\n this.panel.style.transition = 'all 0.15s ease-out'\n this.panel.style.opacity = '0'\n this.panel.style.transform = 'scale(0.96) translateY(8px)'\n setTimeout(() => {\n if (this.panel) {\n this.panel.style.display = 'none'\n }\n }, 150)\n this.visible = false\n }\n\n toggle(): void {\n this.visible ? this.hide() : this.show()\n }\n\n destroy(): void {\n if (this.keyboardShortcutHandler) {\n document.removeEventListener('keydown', this.keyboardShortcutHandler)\n this.keyboardShortcutHandler = null\n }\n if (this.globalKeyboardHandler) {\n document.removeEventListener('keydown', this.globalKeyboardHandler)\n this.globalKeyboardHandler = null\n }\n this.removeTrackerListener?.()\n this.removeTrackerListener = null\n this.panel?.remove()\n this.panel = null\n this.visible = false\n this.selectedRequest = null\n }\n\n private setupKeyboardShortcut(): void {\n const keys = this.config.keyboardShortcut.split('+')\n const requiredKeys = new Set(keys.map((k) => k.toLowerCase()))\n this.keyboardShortcutHandler = (e: KeyboardEvent) => {\n const pressed = new Set<string>()\n if (e.ctrlKey) {\n pressed.add('ctrl')\n }\n if (e.metaKey) {\n pressed.add('meta')\n pressed.add('cmd')\n pressed.add('ctrl')\n }\n if (e.shiftKey) {\n pressed.add('shift')\n }\n if (e.altKey) {\n pressed.add('alt')\n }\n if (e.key && e.key.length === 1) {\n pressed.add(e.key.toLowerCase())\n } else if (e.key.length > 1) {\n pressed.add(e.key.toLowerCase())\n }\n let match = true\n for (const k of requiredKeys) {\n if (!pressed.has(k)) {\n match = false\n break\n }\n }\n if (match && pressed.size === requiredKeys.size) {\n e.preventDefault()\n this.toggle()\n }\n }\n document.addEventListener('keydown', this.keyboardShortcutHandler)\n }\n\n private createPanel(): void {\n if (!this.canUseDOM()) {\n return\n }\n this.panel = document.createElement('div')\n this.panel.id = 'nexa-dev-overlay'\n\n const pos = this.config.position\n const isBottom = pos.includes('bottom')\n const isRight = pos.includes('right')\n\n this.panel.style.cssText = `\n position: fixed;\n ${isBottom ? 'bottom: 24px;' : 'top: 24px;'}\n ${isRight ? 'right: 24px;' : 'left: 24px;'}\n width: 420px;\n max-height: 70vh;\n z-index: 2147483647;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n `\n\n this.panel.innerHTML = `<style>${STYLES}</style>\n\n <div class=\"nexa-header\">\n<div class=\"nexa-header-left\">\n <div class=\"nexa-logo\">\n <span style=\"display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;color:#fff;background:linear-gradient(135deg,#3b82f6,#238636);width:18px;height:18px;border-radius:4px;\">N</span>\n </div>\n <span class=\"nexa-title\">Nexa DevTools</span>\n </div>\n <div class=\"nexa-header-actions\">\n <button class=\"nexa-icon-btn nexa-btn-clear\" title=\"Clear history\">${ICONS.clear}</button>\n <button class=\"nexa-icon-btn nexa-btn-close\" title=\"Close (Esc)\">${ICONS.close}</button>\n </div>\n </div>\n\n <div class=\"nexa-metrics-bar\">\n <div class=\"nexa-metric\"><span class=\"nexa-metric-value\" data-metric=\"total\">0</span><span class=\"nexa-metric-label\">Requests</span></div>\n <div class=\"nexa-metric\"><span class=\"nexa-metric-value\" data-metric=\"avg\">0ms</span><span class=\"nexa-metric-label\">Avg</span></div>\n <div class=\"nexa-metric\"><span class=\"nexa-metric-value\" data-metric=\"rate\">0/s</span><span class=\"nexa-metric-label\">Throughput</span></div>\n <div class=\"nexa-metric nexa-metric-ok\"><span class=\"nexa-metric-value\" data-metric=\"success\">0</span><span class=\"nexa-metric-label\">Success</span></div>\n <div class=\"nexa-metric nexa-metric-err\"><span class=\"nexa-metric-value\" data-metric=\"fail\">0</span><span class=\"nexa-metric-label\">Failed</span></div>\n </div>\n\n <div class=\"nexa-search\">\n <span class=\"nexa-search-icon\">${ICONS.search}</span>\n <input type=\"text\" class=\"nexa-search-input\" placeholder=\"Filter by URL, method, or status...\" />\n </div>\n\n <div class=\"nexa-tabs\">\n <button class=\"nexa-tab nexa-tab-active\" data-tab=\"requests\"><span>Requests</span><span class=\"nexa-tab-count\" data-count=\"requests\">0</span></button>\n <button class=\"nexa-tab\" data-tab=\"metrics\"><span>Metrics</span></button>\n </div>\n\n <div class=\"nexa-body\">\n <div class=\"nexa-panel nexa-panel-active\" data-panel=\"requests\"><div class=\"nexa-request-list\"></div></div>\n <div class=\"nexa-panel\" data-panel=\"metrics\"><div class=\"nexa-metrics-content\"></div></div>\n </div>\n\n <div class=\"nexa-detail\" style=\"display:none\">\n <div class=\"nexa-detail-header\">\n <button class=\"nexa-btn nexa-btn-back\">${ICONS.back} Back</button>\n <button class=\"nexa-btn nexa-btn-retry\">${ICONS.retry} Retry</button>\n </div>\n <div class=\"nexa-detail-body\"></div>\n </div>\n `\n\n document.body.appendChild(this.panel)\n this.bindEvents()\n this.removeTrackerListener = this.tracker.onChange(() => this.render())\n this.hide()\n\n this.globalKeyboardHandler = (e: KeyboardEvent) => {\n if (e.key === 'Escape' && this.visible) {\n this.hide()\n return\n }\n if (\n this.visible &&\n (e.ctrlKey || e.metaKey) &&\n e.key.toLowerCase() === 'f'\n ) {\n e.preventDefault()\n const searchInput = this.panel?.querySelector(\n '.nexa-search-input',\n ) as HTMLInputElement | null\n searchInput?.focus()\n searchInput?.select()\n }\n }\n document.addEventListener('keydown', this.globalKeyboardHandler)\n }\n\n private bindEvents(): void {\n if (!this.panel) {\n return\n }\n this.panel\n .querySelector('.nexa-btn-close')\n ?.addEventListener('click', () => this.hide())\n this.panel\n .querySelector('.nexa-btn-clear')\n ?.addEventListener('click', () => {\n this.tracker.clear()\n this.render()\n })\n this.panel\n .querySelector('.nexa-btn-back')\n ?.addEventListener('click', () => this.showMainView())\n this.panel\n .querySelector('.nexa-btn-retry')\n ?.addEventListener('click', () => this.retrySelected())\n\n const searchInput = this.panel.querySelector(\n '.nexa-search-input',\n ) as HTMLInputElement\n searchInput?.addEventListener('input', (e) => {\n this.searchQuery = (e.target as HTMLInputElement).value.toLowerCase()\n this.renderRequestList()\n })\n\n this.panel.querySelectorAll('.nexa-tab').forEach((tab) => {\n tab.addEventListener('click', () => {\n this.panel!.querySelectorAll('.nexa-tab').forEach((t) =>\n t.classList.remove('nexa-tab-active'),\n )\n this.panel!.querySelectorAll('.nexa-panel').forEach((p) =>\n p.classList.remove('nexa-panel-active'),\n )\n ;(tab as HTMLElement).classList.add('nexa-tab-active')\n const panel = this.panel!.querySelector(\n `[data-panel=\"${(tab as HTMLElement).dataset.tab}\"]`,\n )\n panel?.classList.add('nexa-panel-active')\n if ((tab as HTMLElement).dataset.tab === 'metrics') {\n this.renderMetrics()\n }\n })\n })\n }\n\n private render(): void {\n if (!this.panel || !this.visible) {\n return\n }\n this.renderMetricsBar()\n this.renderRequestList()\n }\n\n private renderMetricsBar(): void {\n const m = this.tracker.getMetrics()\n const el = this.panel\n if (!el) {\n return\n }\n el.querySelector('[data-metric=\"total\"]')!.textContent = String(\n m.totalRequests,\n )\n el.querySelector('[data-metric=\"avg\"]')!.textContent =\n `${m.avgDuration.toFixed(0)}ms`\n el.querySelector('[data-metric=\"rate\"]')!.textContent =\n `${m.requestsPerSecond.toFixed(1)}`\n el.querySelector('[data-metric=\"success\"]')!.textContent = String(\n m.successfulRequests,\n )\n el.querySelector('[data-metric=\"fail\"]')!.textContent = String(\n m.failedRequests,\n )\n el.querySelector('[data-count=\"requests\"]')!.textContent = String(\n m.totalRequests,\n )\n }\n\n private renderRequestList(): void {\n const list = this.panel?.querySelector('.nexa-request-list')\n if (!list) {\n return\n }\n\n let requests = this.tracker.getHistory()\n if (this.searchQuery) {\n requests = requests.filter(\n (r) =>\n r.url.toLowerCase().includes(this.searchQuery) ||\n r.method.toLowerCase().includes(this.searchQuery) ||\n String(r.status).includes(this.searchQuery),\n )\n }\n\n if (requests.length === 0) {\n list.innerHTML = `\n <div class=\"nexa-empty\">\n <svg width=\"48\" height=\"48\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><path d=\"M8 12h8M12 8v8\"/>\n </svg>\n <p>${this.searchQuery ? 'No matching requests' : 'No requests yet'}</p>\n <span>${this.searchQuery ? 'Try a different search term' : 'Make a request to see it here'}</span>\n </div>`\n return\n }\n\n list.innerHTML = requests\n .map(\n (r, i) => `\n <div class=\"nexa-request-item\" data-id=\"${r.id}\" style=\"animation-delay: ${Math.min(i * 20, 300)}ms\">\n <div class=\"nexa-req-left\">\n <span class=\"nexa-method nexa-method-${r.method.toLowerCase()}\">${r.method}</span>\n <span class=\"nexa-status ${r.ok ? 'nexa-ok' : 'nexa-err'}\">${r.status || 'ERR'}</span>\n <span class=\"nexa-url\" title=\"${r.url}\">${this.truncateUrl(r.url)}</span>\n </div>\n <div class=\"nexa-req-right\">\n ${r.cached ? '<span class=\"nexa-badge nexa-badge-cache\">Cache</span>' : ''}\n ${r.retryCount > 0 ? `<span class=\"nexa-badge nexa-badge-retry\">${r.retryCount}R</span>` : ''}\n <span class=\"nexa-duration ${r.duration > 500 ? 'nexa-slow' : ''}\">${r.duration.toFixed(0)}ms</span>\n ${ICONS.chevron}\n </div>\n </div>\n `,\n )\n .join('')\n\n list.querySelectorAll('.nexa-request-item').forEach((item) => {\n item.addEventListener('click', () => {\n const id = (item as HTMLElement).dataset.id\n const request = requests.find((r) => r.id === id)\n if (request) {\n this.showDetail(request)\n }\n })\n })\n }\n\n private renderMetrics(): void {\n const m = this.tracker.getMetrics()\n const el = this.panel?.querySelector('.nexa-metrics-content')\n if (!el) {\n return\n }\n\n const successRate =\n m.totalRequests > 0\n ? ((m.successfulRequests / m.totalRequests) * 100).toFixed(1)\n : '0'\n\n el.innerHTML = `\n <div class=\"nexa-card\">\n <h3>Overview</h3>\n <div class=\"nexa-row\"><span>Total Requests</span><strong>${m.totalRequests}</strong></div>\n <div class=\"nexa-row\"><span>Successful</span><strong class=\"nexa-ok\">${m.successfulRequests}</strong></div>\n <div class=\"nexa-row\"><span>Failed</span><strong class=\"nexa-err\">${m.failedRequests}</strong></div>\n <div class=\"nexa-row\"><span>Cached</span><strong>${m.cachedRequests}</strong></div>\n <div class=\"nexa-row\"><span>Success Rate</span><strong>${successRate}%</strong></div>\n </div>\n <div class=\"nexa-card\">\n <h3>Performance</h3>\n <div class=\"nexa-row\"><span>Average</span><strong>${m.avgDuration.toFixed(1)}ms</strong></div>\n <div class=\"nexa-row\"><span>Fastest</span><strong class=\"nexa-ok\">${m.minDuration.toFixed(1)}ms</strong></div>\n <div class=\"nexa-row\"><span>Slowest</span><strong class=\"nexa-err\">${m.maxDuration.toFixed(1)}ms</strong></div>\n <div class=\"nexa-row\"><span>Throughput</span><strong>${m.requestsPerSecond.toFixed(2)} req/s</strong></div>\n </div>\n ${\n m.slowestRequests.length > 0\n ? `\n <div class=\"nexa-card\">\n <h3>Slowest Requests</h3>\n ${m.slowestRequests\n .map(\n (r) => `\n <div class=\"nexa-row nexa-slow-req\">\n <span><span class=\"nexa-method nexa-method-${r.method.toLowerCase()}\" style=\"font-size:10px;padding:2px 5px;\">${r.method}</span> ${this.truncateUrl(r.url, 25)}</span>\n <strong class=\"nexa-err\">${r.duration.toFixed(0)}ms</strong>\n </div>\n `,\n )\n .join('')}\n </div>\n `\n : ''\n }`\n }\n\n private showDetail(request: TrackedRequest): void {\n this.selectedRequest = request\n const body = this.panel?.querySelector('.nexa-body') as HTMLElement | null\n const detail = this.panel?.querySelector(\n '.nexa-detail',\n ) as HTMLElement | null\n const content = this.panel?.querySelector('.nexa-detail-body')\n if (!body || !detail || !content) {\n return\n }\n\n body.style.display = 'none'\n detail.style.display = 'flex'\n\n content.innerHTML = `\n <div class=\"nexa-card\">\n <h3>Request</h3>\n <div class=\"nexa-row\"><span>Method</span><strong style=\"color:${request.method === 'GET' ? COLORS.get : request.method === 'POST' ? COLORS.post : request.method === 'DELETE' ? COLORS.delete : COLORS.warning}\">${request.method}</strong></div>\n <div class=\"nexa-row\"><span>URL</span><span class=\"nexa-url-full\">${request.url}</span></div>\n <div class=\"nexa-row\"><span>Status</span><strong class=\"${request.ok ? 'nexa-ok' : 'nexa-err'}\">${request.status || 'N/A'}</strong></div>\n <div class=\"nexa-row\"><span>Duration</span><strong>${request.duration.toFixed(1)}ms</strong></div>\n <div class=\"nexa-row\"><span>Cached</span><strong>${request.cached ? 'Yes' : 'No'}</strong></div>\n <div class=\"nexa-row\"><span>Retries</span><strong>${request.retryCount}</strong></div>\n <div class=\"nexa-row\"><span>Timestamp</span><strong>${new Date(request.timestamp).toLocaleTimeString()}</strong></div>\n </div>\n ${\n request.body !== undefined\n ? `\n <div class=\"nexa-card\">\n <h3>Request Body</h3>\n <pre class=\"nexa-code\">${this.formatJson(request.body)}</pre>\n </div>\n `\n : ''\n }\n ${\n Object.keys(request.headers).length > 0\n ? `\n <div class=\"nexa-card\">\n <h3>Headers</h3>\n <pre class=\"nexa-code\">${this.formatJson(request.headers)}</pre>\n </div>\n `\n : ''\n }`\n }\n\n private showMainView(): void {\n const body = this.panel?.querySelector('.nexa-body') as HTMLElement | null\n const detail = this.panel?.querySelector(\n '.nexa-detail',\n ) as HTMLElement | null\n if (body) {\n body.style.display = 'flex'\n }\n if (detail) {\n detail.style.display = 'none'\n }\n this.selectedRequest = null\n }\n\n private retrySelected(): void {\n if (!this.selectedRequest) {\n return\n }\n const { method, url, body, headers } = this.selectedRequest\n fetch(url, {\n method,\n headers: headers as Record<string, string>,\n body: body ? JSON.stringify(body) : undefined,\n })\n .then(async (res) => {\n if (this.selectedRequest) {\n this.selectedRequest = {\n ...this.selectedRequest,\n status: res.status,\n ok: res.ok,\n duration: this.selectedRequest.duration,\n timestamp: Date.now(),\n }\n this.showDetail(this.selectedRequest)\n }\n })\n .catch(() => {})\n }\n\n private truncateUrl(url: string, max = 35): string {\n try {\n const parsed = new URL(url)\n return parsed.pathname + (parsed.search || '')\n } catch {\n return url.length > max ? url.slice(0, max) + '...' : url\n }\n }\n\n private formatJson(data: unknown): string {\n try {\n return JSON.stringify(data, null, 2)\n } catch {\n return String(data)\n }\n }\n\n private canUseDOM(): boolean {\n return (\n typeof document !== 'undefined' &&\n typeof document.createElement === 'function' &&\n !!document.body\n )\n }\n}\n","export { RequestTracker } from './tracker'\nexport { DevOverlayUI } from './overlay'\nexport type { TrackedRequest, DevMetrics, DevOverlayConfig } from './types'\n\nimport { RequestTracker } from './tracker'\nimport { DevOverlayUI } from './overlay'\nimport type { DevOverlayConfig } from './types'\n\nlet overlayInstance: DevOverlayUI | null = null\nlet trackerInstance: RequestTracker | null = null\n\nexport function createDevOverlay(config: DevOverlayConfig = {}): {\n tracker: RequestTracker\n ui: DevOverlayUI\n} {\n if (overlayInstance) {\n return { tracker: trackerInstance!, ui: overlayInstance }\n }\n\n trackerInstance = new RequestTracker(config)\n overlayInstance = new DevOverlayUI(trackerInstance)\n overlayInstance.show()\n\n return { tracker: trackerInstance, ui: overlayInstance }\n}\n\nexport function getDevOverlay(): {\n tracker: RequestTracker | null\n ui: DevOverlayUI | null\n} {\n return { tracker: trackerInstance, ui: overlayInstance }\n}\n\nexport function destroyDevOverlay(): void {\n overlayInstance?.destroy()\n overlayInstance = null\n trackerInstance = null\n}\n"],"mappings":"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"}