@bereasoftware/nexa 1.4.1 → 1.6.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.
- package/README.en.md +130 -112
- package/README.md +142 -111
- package/dist/bereasoftware-nexa-1.6.0.tgz +0 -0
- package/dist/nexa.cjs.js +311 -85
- package/dist/nexa.cjs.js.map +1 -1
- package/dist/nexa.es.js +614 -218
- package/dist/nexa.es.js.map +1 -1
- package/dist/nexa.iife.js +311 -85
- package/dist/nexa.iife.js.map +1 -1
- package/dist/nexa.umd.js +311 -85
- package/dist/nexa.umd.js.map +1 -1
- package/dist/types/dev-overlay/index.d.ts +7 -0
- package/dist/types/dev-overlay/overlay.d.ts +11 -0
- package/dist/types/dev-overlay/tracker.d.ts +2 -0
- package/dist/types/dev-overlay/types.d.ts +15 -0
- package/package.json +1 -1
- package/dist/bereasoftware-nexa-1.4.1.tgz +0 -0
package/dist/nexa.umd.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nexa.umd.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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"}
|
|
1
|
+
{"version":3,"file":"nexa.umd.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\nconst STORAGE_KEY = 'nexa.devOverlay.config'\n\nexport function loadPersistedConfig(): Partial<DevOverlayConfig> {\n try {\n if (typeof localStorage === 'undefined') {\n return {}\n }\n const raw = localStorage.getItem(STORAGE_KEY)\n if (!raw) {\n return {}\n }\n return JSON.parse(raw) as Partial<DevOverlayConfig>\n } catch {\n return {}\n }\n}\n\nfunction savePersistedConfig(cfg: Partial<DevOverlayConfig>): void {\n try {\n if (typeof localStorage === 'undefined') {\n return\n }\n localStorage.setItem(STORAGE_KEY, JSON.stringify(cfg))\n } catch {}\n}\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 devOnly: config.devOnly ?? true,\n floatingButtonSize: config.floatingButtonSize ?? 48,\n floatingButtonOffset: config.floatingButtonOffset ?? 24,\n floatingButtonTheme: config.floatingButtonTheme ?? 'inherit',\n branding: config.branding ?? 'Nexa DevTools',\n icon:\n config.icon ??\n 'https://raw.githubusercontent.com/Berea-Soft/nexa/refs/heads/main/src/assets/faviconNew.png',\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 updateConfig(partial: Partial<DevOverlayConfig>): Required<DevOverlayConfig> {\n this.config = {\n ...this.config,\n ...partial,\n }\n // Keep internal maxHistory in sync\n this.maxHistory = this.config.maxHistory\n // persist updated config\n try {\n savePersistedConfig(this.config)\n } catch {}\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 gear: `<svg width=\"16\" height=\"16\" viewBox=\"0 0 48 48\" fill=\"currentColor\" xmlns=\"http://www.w3.org/2000/svg\">\n<path d=\"M 24 4 C 22.423103 4 20.902664 4.1994284 19.451172 4.5371094 A 1.50015 1.50015 0 0 0 18.300781 5.8359375 L 17.982422 8.7382812 C 17.878304 9.6893592 17.328913 10.530853 16.5 11.009766 C 15.672739 11.487724 14.66862 11.540667 13.792969 11.15625 L 13.791016 11.15625 L 11.125 9.9824219 A 1.50015 1.50015 0 0 0 9.4257812 10.330078 C 7.3532865 12.539588 5.7626807 15.215064 4.859375 18.201172 A 1.50015 1.50015 0 0 0 5.4082031 19.845703 L 7.7734375 21.580078 C 8.5457929 22.147918 9 23.042801 9 24 C 9 24.95771 8.5458041 25.853342 7.7734375 26.419922 L 5.4082031 28.152344 A 1.50015 1.50015 0 0 0 4.859375 29.796875 C 5.7625845 32.782665 7.3519262 35.460112 9.4257812 37.669922 A 1.50015 1.50015 0 0 0 11.125 38.015625 L 13.791016 36.841797 C 14.667094 36.456509 15.672169 36.511947 16.5 36.990234 C 17.328913 37.469147 17.878304 38.310641 17.982422 39.261719 L 18.300781 42.164062 A 1.50015 1.50015 0 0 0 19.449219 43.460938 C 20.901371 43.799844 22.423103 44 24 44 C 25.576897 44 27.097336 43.800572 28.548828 43.462891 A 1.50015 1.50015 0 0 0 29.699219 42.164062 L 30.017578 39.261719 C 30.121696 38.310641 30.671087 37.469147 31.5 36.990234 C 32.327261 36.512276 33.33138 36.45738 34.207031 36.841797 L 36.875 38.015625 A 1.50015 1.50015 0 0 0 38.574219 37.669922 C 40.646713 35.460412 42.237319 32.782983 43.140625 29.796875 A 1.50015 1.50015 0 0 0 42.591797 28.152344 L 40.226562 26.419922 C 39.454197 25.853342 39 24.95771 39 24 C 39 23.04229 39.454197 22.146658 40.226562 21.580078 L 42.591797 19.847656 A 1.50015 1.50015 0 0 0 43.140625 18.203125 C 42.237319 15.217017 40.646713 12.539588 38.574219 10.330078 A 1.50015 1.50015 0 0 0 36.875 9.984375 L 34.207031 11.158203 C 33.33138 11.54262 32.327261 11.487724 31.5 11.009766 C 30.671087 10.530853 30.121696 9.6893592 30.017578 8.7382812 L 29.699219 5.8359375 A 1.50015 1.50015 0 0 0 28.550781 4.5390625 C 27.098629 4.2001555 25.576897 4 24 4 z M 24 7 C 24.974302 7 25.90992 7.1748796 26.847656 7.3398438 L 27.035156 9.0644531 C 27.243038 10.963375 28.346913 12.652335 30 13.607422 C 31.654169 14.563134 33.668094 14.673009 35.416016 13.904297 L 37.001953 13.207031 C 38.219788 14.669402 39.183985 16.321182 39.857422 18.130859 L 38.451172 19.162109 C 36.911538 20.291529 36 22.08971 36 24 C 36 25.91029 36.911538 27.708471 38.451172 28.837891 L 39.857422 29.869141 C 39.183985 31.678818 38.219788 33.330598 37.001953 34.792969 L 35.416016 34.095703 C 33.668094 33.326991 31.654169 33.436866 30 34.392578 C 28.346913 35.347665 27.243038 37.036625 27.035156 38.935547 L 26.847656 40.660156 C 25.910002 40.82466 24.973817 41 24 41 C 23.025698 41 22.09008 40.82512 21.152344 40.660156 L 20.964844 38.935547 C 20.756962 37.036625 19.653087 35.347665 18 34.392578 C 16.345831 33.436866 14.331906 33.326991 12.583984 34.095703 L 10.998047 34.792969 C 9.7799772 33.330806 8.8159425 31.678964 8.1425781 29.869141 L 9.5488281 28.837891 C 11.088462 27.708471 12 25.91029 12 24 C 12 22.08971 11.087719 20.290363 9.5488281 19.160156 L 8.1425781 18.128906 C 8.8163325 16.318532 9.7814501 14.667839 11 13.205078 L 12.583984 13.902344 C 14.331906 14.671056 16.345831 14.563134 18 13.607422 C 19.653087 12.652335 20.756962 10.963375 20.964844 9.0644531 L 21.152344 7.3398438 C 22.089998 7.1753403 23.026183 7 24 7 z M 24 16 C 19.599487 16 16 19.59949 16 24 C 16 28.40051 19.599487 32 24 32 C 28.400513 32 32 28.40051 32 24 C 32 19.59949 28.400513 16 24 16 z M 24 19 C 26.779194 19 29 21.220808 29 24 C 29 26.779192 26.779194 29 24 29 C 21.220806 29 19 26.779192 19 24 C 19 21.220808 21.220806 19 24 19 z\"></path>\n</svg>`,\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 download: `<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=\"M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v4\"/><polyline points=\"7 10 12 15 17 10\"/><line x1=\"12\" y1=\"15\" x2=\"12\" y2=\"3\"/></svg>`,\n copy: `<svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"/><path d=\"M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1\"/></svg>`,\n}\n\nconst COLORS = {\n bg: '#0b1120',\n bgElevated: '#111827',\n bgSurface: '#172033',\n border: '#24324a',\n borderFocus: '#35507a',\n text: '#e5eefb',\n textMuted: '#b4c4dd',\n textDim: '#7f93b3',\n accent: '#38bdf8',\n accentHover: '#0ea5e9',\n accentSoft: 'rgba(56, 189, 248, 0.18)',\n success: '#34d399',\n successBg: 'rgba(52, 211, 153, 0.16)',\n error: '#fb7185',\n errorBg: 'rgba(251, 113, 133, 0.16)',\n warning: '#fbbf24',\n warningBg: 'rgba(251, 191, 36, 0.16)',\n info: '#a78bfa',\n infoBg: 'rgba(167, 139, 250, 0.16)',\n get: '#34d399',\n post: '#38bdf8',\n put: '#fbbf24',\n patch: '#a78bfa',\n delete: '#fb7185',\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 28px 60px -24px rgba(2, 6, 23, 0.78), 0 0 0 1px rgba(148, 163, 184, 0.08);\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: linear-gradient(135deg, ${COLORS.accentSoft}, rgba(255,255,255,0.02));\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.bgSurface};\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.bgSurface};\n border-radius: 10px;\n transition: all 0.2s;\n }\n #nexa-dev-overlay .nexa-metric:hover {\n background: rgba(53, 80, 122, 0.3);\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 ${COLORS.accentSoft};\n }\n #nexa-dev-overlay .nexa-search-input::placeholder { color: ${COLORS.textDim}; }\n #nexa-dev-overlay .nexa-filters {\n display: flex;\n gap: 6px;\n padding: 12px 16px 12px 16px;\n border-bottom: 1px solid ${COLORS.border};\n overflow-x: auto;\n scrollbar-width: none;\n min-height: 50px;\n }\n #nexa-dev-overlay .nexa-filters::-webkit-scrollbar { display: none; }\n #nexa-dev-overlay .nexa-filter-chip {\n padding: 4px 10px;\n background: ${COLORS.bgSurface};\n border: 1px solid ${COLORS.border};\n border-radius: 20px;\n color: ${COLORS.textDim};\n font-size: 11px;\n font-weight: 600;\n white-space: nowrap;\n cursor: pointer;\n transition: all 0.15s;\n }\n #nexa-dev-overlay .nexa-filter-chip:hover { border-color: ${COLORS.borderFocus}; color: ${COLORS.textMuted}; }\n #nexa-dev-overlay .nexa-filter-chip-active {\n background: ${COLORS.accentSoft};\n border-color: ${COLORS.accent};\n color: ${COLORS.accent};\n }\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(148, 163, 184, 0.14);\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.bgSurface};\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: rgba(23, 32, 51, 0.92);\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: ${COLORS.accentSoft}; color: ${COLORS.post}; }\n #nexa-dev-overlay .nexa-method-put { background: ${COLORS.warningBg}; color: ${COLORS.put}; }\n #nexa-dev-overlay .nexa-method-patch { background: ${COLORS.infoBg}; 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: ${COLORS.infoBg}; color: ${COLORS.info}; }\n #nexa-dev-overlay .nexa-badge-retry { background: ${COLORS.warningBg}; 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-group {\n display: flex;\n gap: 8px;\n }\n #nexa-dev-overlay .nexa-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 14px;\n background: ${COLORS.bgSurface};\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: rgba(53, 80, 122, 0.24); 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: #052e26; }\n #nexa-dev-overlay .nexa-btn-copy { background: ${COLORS.accentSoft}; border-color: transparent; color: ${COLORS.accent}; }\n #nexa-dev-overlay .nexa-btn-copy:hover { background: ${COLORS.accent}; color: #ffffff; }\n #nexa-dev-overlay .nexa-card {\n background: ${COLORS.bgSurface};\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-notification {\n position: absolute;\n bottom: 24px;\n left: 50%;\n transform: translateX(-50%) translateY(20px);\n background: ${COLORS.bgElevated};\n color: ${COLORS.text};\n padding: 10px 18px;\n border-radius: 12px;\n border: 1px solid ${COLORS.borderFocus};\n font-size: 13px;\n font-weight: 500;\n box-shadow: 0 12px 32px rgba(0,0,0,0.4);\n z-index: 2147483651;\n opacity: 0;\n pointer-events: none;\n transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);\n }\n #nexa-dev-overlay .nexa-notification-show {\n opacity: 1;\n transform: translateX(-50%) translateY(0);\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-settings-panel {\n position: absolute;\n top: 72px;\n left: 16px;\n right: 16px;\n bottom: 16px;\n z-index: 2147483650;\n display: none;\n flex-direction: column;\n gap: 12px;\n padding: 16px;\n border-radius: 12px;\n background: ${COLORS.bgElevated};\n border: 1px solid ${COLORS.border};\n box-shadow: 0 28px 60px -24px rgba(2, 6, 23, 0.76);\n overflow: auto;\n }\n #nexa-dev-overlay .nexa-settings-row { display:flex;align-items:center;gap:8px;margin-bottom:8px }\n #nexa-dev-overlay .nexa-settings-row label{font-size:13px;color:${COLORS.textDim};min-width:70px}\n #nexa-dev-overlay .nexa-settings-row select{padding:6px 8px;border-radius:8px;border:1px solid ${COLORS.border};background:${COLORS.bg};color:${COLORS.text}}\n\n #nexa-dev-overlay.nexa-theme-light {\n background: #f8fbff;\n color: #0f172a;\n border-color: #d8e4f2;\n box-shadow: 0 28px 60px -24px rgba(15, 23, 42, 0.18), 0 0 0 1px rgba(148, 163, 184, 0.16);\n }\n #nexa-dev-overlay.nexa-theme-light .nexa-header,\n #nexa-dev-overlay.nexa-theme-light .nexa-metrics-bar,\n #nexa-dev-overlay.nexa-theme-light .nexa-search,\n #nexa-dev-overlay.nexa-theme-light .nexa-tabs {\n background: #f8fbff;\n border-color: #d8e4f2;\n }\n #nexa-dev-overlay.nexa-theme-light .nexa-logo,\n #nexa-dev-overlay.nexa-theme-light .nexa-metric,\n #nexa-dev-overlay.nexa-theme-light .nexa-request-item,\n #nexa-dev-overlay.nexa-theme-light .nexa-btn,\n #nexa-dev-overlay.nexa-theme-light .nexa-card,\n #nexa-dev-overlay.nexa-theme-light .nexa-settings-panel,\n #nexa-dev-overlay.nexa-theme-light .nexa-notification {\n background: #ffffff;\n border-color: #d8e4f2;\n }\n #nexa-dev-overlay.nexa-theme-light .nexa-metric:hover,\n #nexa-dev-overlay.nexa-theme-light .nexa-icon-btn:hover,\n #nexa-dev-overlay.nexa-theme-light .nexa-btn:hover,\n #nexa-dev-overlay.nexa-theme-light .nexa-tab:hover,\n #nexa-dev-overlay.nexa-theme-light .nexa-request-item:hover {\n background: #eef6ff;\n }\n #nexa-dev-overlay.nexa-theme-light .nexa-request-item:hover {\n border-color: #93c5fd;\n }\n #nexa-dev-overlay.nexa-theme-light .nexa-search-icon,\n #nexa-dev-overlay.nexa-theme-light .nexa-metric-label,\n #nexa-dev-overlay.nexa-theme-light .nexa-duration,\n #nexa-dev-overlay.nexa-theme-light .nexa-empty,\n #nexa-dev-overlay.nexa-theme-light .nexa-empty span,\n #nexa-dev-overlay.nexa-theme-light .nexa-row span,\n #nexa-dev-overlay.nexa-theme-light .nexa-settings-row label,\n #nexa-dev-overlay.nexa-theme-light .nexa-notification {\n color: #64748b;\n }\n #nexa-dev-overlay.nexa-theme-light .nexa-notification {\n box-shadow: 0 12px 32px rgba(15,23,42,0.12);\n border-color: #e2e8f0;\n }\n #nexa-dev-overlay.nexa-theme-light .nexa-url,\n #nexa-dev-overlay.nexa-theme-light .nexa-empty p,\n #nexa-dev-overlay.nexa-theme-light .nexa-icon-btn,\n #nexa-dev-overlay.nexa-theme-light .nexa-tab,\n #nexa-dev-overlay.nexa-theme-light .nexa-btn,\n #nexa-dev-overlay.nexa-theme-light .nexa-code {\n color: #334155;\n }\n #nexa-dev-overlay.nexa-theme-light .nexa-metric-value,\n #nexa-dev-overlay.nexa-theme-light .nexa-title,\n #nexa-dev-overlay.nexa-theme-light .nexa-row strong {\n color: #0f172a;\n }\n #nexa-dev-overlay.nexa-theme-light .nexa-search-input,\n #nexa-dev-overlay.nexa-theme-light .nexa-settings-row select,\n #nexa-dev-overlay.nexa-theme-light .nexa-code {\n background: #ffffff;\n color: #0f172a;\n border-color: #d8e4f2;\n }\n #nexa-dev-overlay.nexa-theme-light .nexa-search-input:focus {\n border-color: #38bdf8;\n box-shadow: 0 0 0 3px rgba(56, 189, 248, 0.16);\n }\n #nexa-dev-overlay.nexa-theme-light .nexa-tab-active {\n color: #ffffff;\n background: #0ea5e9 !important;\n }\n #nexa-dev-overlay.nexa-theme-light .nexa-tab-count {\n background: rgba(14, 165, 233, 0.12);\n color: #075985;\n }\n #nexa-dev-overlay.nexa-theme-light .nexa-code,\n #nexa-dev-overlay.nexa-theme-light .nexa-detail,\n #nexa-dev-overlay.nexa-theme-light .nexa-row {\n border-color: #d8e4f2;\n }\n #nexa-dev-overlay .nexa-slow-req {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n #nexa-dev-overlay.nexa-view-detail .nexa-metrics-bar,\n #nexa-dev-overlay.nexa-view-detail .nexa-search,\n #nexa-dev-overlay.nexa-view-detail .nexa-filters,\n #nexa-dev-overlay.nexa-view-detail .nexa-tabs,\n #nexa-dev-overlay.nexa-view-detail .nexa-body {\n display: none !important;\n }\n`\n\nfunction isDevelopmentEnv(): boolean {\n try {\n if (typeof process !== 'undefined') {\n const proc = process as unknown as { env?: { NODE_ENV?: string } }\n if (proc.env && typeof proc.env.NODE_ENV === 'string') {\n return proc.env.NODE_ENV === 'development'\n }\n }\n } catch {\n // ignore\n }\n try {\n if (typeof location !== 'undefined' && location.hostname) {\n const host = location.hostname\n if (host === 'localhost' || host === '127.0.0.1' || host === '0.0.0.0') {\n return true\n }\n }\n } catch {\n // ignore\n }\n return false\n}\n\nexport class DevOverlayUI {\n private panel: HTMLElement | null = null\n private floatingIcon: 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 filterType: 'all' | 'xhr' | 'fetch' | 'err' | 'slow' = 'all'\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 // hide floating icon when panel is visible\n this.hideFloatingIcon()\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 // show floating icon when panel is hidden (if enabled and dev-only policy allows)\n if (this.config.enabled && (!this.config.devOnly || isDevelopmentEnv())) {\n this.showFloatingIcon()\n }\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 if (this.floatingIcon) {\n this.floatingIcon.remove()\n this.floatingIcon = null\n }\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 // Prevent duplicate overlays from being mounted (HMR, multiple inits)\n try {\n const existing = document.getElementById('nexa-dev-overlay')\n if (existing) {\n existing.remove()\n }\n } catch {\n // ignore\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 const offsetPx = `${this.config.floatingButtonOffset ?? 24}px`\n\n const branding = this.config.branding || 'Nexa DevTools'\n const icon =\n this.config.icon ||\n 'https://raw.githubusercontent.com/Berea-Soft/nexa/refs/heads/main/src/assets/faviconNew.png'\n\n this.panel.style.cssText = `\n position: fixed;\n ${isBottom ? `bottom: ${offsetPx};` : `top: ${offsetPx};`}\n ${isRight ? `right: ${offsetPx};` : `left: ${offsetPx};`}\n width: 420px;\n max-height: 70vh;\n z-index: 2147483649;\n display: flex;\n flex-direction: column;\n overflow: hidden;\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 <img src=\"${icon}\" alt=\"${branding}\" style=\"width:100%;height:auto;object-fit:cover;border-radius:8px;display:block;\" />\n </div>\n <span class=\"nexa-title\">${branding}</span>\n </div>\n <div class=\"nexa-header-actions\">\n <button class=\"nexa-icon-btn nexa-btn-export\" title=\"Export history (JSON)\">${ICONS.download}</button>\n <button class=\"nexa-icon-btn nexa-btn-settings\" title=\"Settings\">${ICONS.gear}</button>\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-notification\"></div>\n\n <div class=\"nexa-settings-panel\" style=\"display:none\">\n <div class=\"nexa-settings-row\">\n <label for=\"nexa-pos\">Position</label>\n <select id=\"nexa-pos\" data-setting=\"position\">\n <option value=\"top-right\">Top Right</option>\n <option value=\"top-left\">Top Left</option>\n <option value=\"bottom-right\">Bottom Right</option>\n <option value=\"bottom-left\">Bottom Left</option>\n </select>\n </div>\n <div class=\"nexa-settings-row\">\n <label for=\"nexa-theme\">Theme</label>\n <select id=\"nexa-theme\" data-setting=\"theme\">\n <option value=\"dark\">Dark</option>\n <option value=\"light\">Light</option>\n </select>\n </div>\n <div style=\"display:flex;gap:8px;margin-top:8px\">\n <button class=\"nexa-btn nexa-btn-save\">Save</button>\n <button class=\"nexa-btn nexa-btn-cancel\">Cancel</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-filters\">\n <div class=\"nexa-filter-chip nexa-filter-chip-active\" data-filter=\"all\">All</div>\n <div class=\"nexa-filter-chip\" data-filter=\"err\">Errors</div>\n <div class=\"nexa-filter-chip\" data-filter=\"xhr\">JSON</div>\n <div class=\"nexa-filter-chip\" data-filter=\"slow\">Slow</div>\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 <div class=\"nexa-btn-group\">\n <button class=\"nexa-btn nexa-btn-copy\">${ICONS.copy} Copy as fetch</button>\n <button class=\"nexa-btn nexa-btn-retry\">${ICONS.retry} Retry</button>\n </div>\n </div>\n <div class=\"nexa-detail-body\"></div>\n </div>\n `\n\n document.body.appendChild(this.panel)\n // Apply theme class immediately so initial render reflects persisted theme\n if (this.config.theme === 'light') {\n this.panel.classList.add('nexa-theme-light')\n } else {\n this.panel.classList.remove('nexa-theme-light')\n }\n this.bindEvents()\n this.removeTrackerListener = this.tracker.onChange(() => this.render())\n const canShowFloating = !this.config.devOnly || isDevelopmentEnv()\n if (this.config.enabled && canShowFloating) {\n this.createFloatingIcon()\n }\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-export')\n ?.addEventListener('click', () => this.exportHistory())\n this.panel\n .querySelector('.nexa-btn-copy')\n ?.addEventListener('click', () => this.copyAsFetch())\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-filter-chip').forEach((chip) => {\n chip.addEventListener('click', () => {\n this.panel!.querySelectorAll('.nexa-filter-chip').forEach((c) =>\n c.classList.remove('nexa-filter-chip-active'),\n )\n ;(chip as HTMLElement).classList.add('nexa-filter-chip-active')\n this.filterType = (chip as HTMLElement).dataset.filter as\n | 'all'\n | 'xhr'\n | 'fetch'\n | 'err'\n | 'slow'\n this.renderRequestList()\n })\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 // Settings button\n this.panel\n .querySelector('.nexa-btn-settings')\n ?.addEventListener('click', () => {\n const sp = this.panel!.querySelector(\n '.nexa-settings-panel',\n ) as HTMLElement | null\n if (!sp) {\n return\n }\n // toggle as modal (use flex so layout inside works)\n const posSel = this.panel!.querySelector(\n '[data-setting=\"position\"]',\n ) as HTMLSelectElement | null\n const themeSel = this.panel!.querySelector(\n '[data-setting=\"theme\"]',\n ) as HTMLSelectElement | null\n const isOpen = sp.style.display === 'flex'\n sp.style.display = isOpen ? 'none' : 'flex'\n if (!isOpen) {\n // opening: hide the floating button so modal is not obstructed\n this.hideFloatingIcon()\n if (posSel) {\n posSel.value = this.config.position\n }\n if (themeSel) {\n themeSel.value = this.config.theme\n }\n posSel?.focus()\n }\n })\n\n this.panel\n .querySelector('.nexa-btn-save')\n ?.addEventListener('click', () => {\n const posSel = this.panel!.querySelector(\n '[data-setting=\"position\"]',\n ) as HTMLSelectElement | null\n const themeSel = this.panel!.querySelector(\n '[data-setting=\"theme\"]',\n ) as HTMLSelectElement | null\n const newPos = posSel?.value as DevOverlayConfig['position'] | undefined\n const newTheme = themeSel?.value as\n | DevOverlayConfig['theme']\n | undefined\n const partial: Partial<DevOverlayConfig> = {}\n if (newPos !== undefined) {\n partial.position = newPos\n }\n if (newTheme !== undefined) {\n partial.theme = newTheme\n }\n const newConfig = this.tracker.updateConfig(partial)\n this.applyConfigToUI(newConfig)\n const sp = this.panel!.querySelector(\n '.nexa-settings-panel',\n ) as HTMLElement | null\n if (sp) {\n sp.style.display = 'none'\n }\n })\n\n this.panel\n .querySelector('.nexa-btn-cancel')\n ?.addEventListener('click', () => {\n const sp = this.panel!.querySelector(\n '.nexa-settings-panel',\n ) as HTMLElement | null\n if (sp) {\n sp.style.display = 'none'\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\n if (this.filterType === 'err') {\n requests = requests.filter((r) => !r.ok)\n } else if (this.filterType === 'xhr') {\n // JSON requests\n requests = requests.filter(\n (r) =>\n (r.headers['content-type'] &&\n r.headers['content-type'].includes('json')) ||\n (r.responseHeaders &&\n r.responseHeaders['content-type'] &&\n r.responseHeaders['content-type'].includes('json')),\n )\n } else if (this.filterType === 'slow') {\n requests = requests.filter((r) => r.duration > 500)\n }\n\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 if (!this.panel) {\n return\n }\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 this.panel.classList.add('nexa-view-detail')\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 if (this.panel) {\n this.panel.classList.remove('nexa-view-detail')\n }\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 exportHistory(): void {\n const history = this.tracker.getHistory()\n const data = JSON.stringify(history, null, 2)\n const blob = new Blob([data], { type: 'application/json' })\n const url = URL.createObjectURL(blob)\n const a = document.createElement('a')\n a.href = url\n a.download = `nexa-history-${new Date().toISOString().slice(0, 19).replace(/[:]/g, '-')}.json`\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n URL.revokeObjectURL(url)\n this.showNotification('History exported as JSON')\n }\n\n private copyAsFetch(): void {\n if (!this.selectedRequest) {\n return\n }\n const r = this.selectedRequest\n const headers = { ...r.headers }\n delete headers['host'] // often not needed/allowed in manual fetch\n\n let code = `fetch(\"${r.url}\", {\\n`\n code += ` \"method\": \"${r.method}\",\\n`\n if (Object.keys(headers).length > 0) {\n code += ` \"headers\": ${JSON.stringify(headers, null, 4).replace(/\\n/g, '\\n ')},\\n`\n }\n if (r.body) {\n code += ` \"body\": ${JSON.stringify(r.body, null, 4).replace(/\\n/g, '\\n ')},\\n`\n }\n code += `});`\n\n navigator.clipboard\n .writeText(code)\n .then(() => {\n this.showNotification('Copied as fetch to clipboard')\n })\n .catch(() => {\n this.showNotification('Failed to copy to clipboard')\n })\n }\n\n private showNotification(message: string): void {\n const el = this.panel?.querySelector('.nexa-notification') as HTMLElement\n if (!el) {\n return\n }\n el.textContent = message\n el.classList.add('nexa-notification-show')\n setTimeout(() => {\n el.classList.remove('nexa-notification-show')\n }, 2500)\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 // Floating icon helpers\n private createFloatingIcon(): void {\n if (!this.canUseDOM()) {\n return\n }\n\n // Remove any existing floating element to avoid duplicates (HMR or multiple inits)\n try {\n const existingBtn = document.getElementById(\n 'nexa-dev-overlay-floating',\n ) as HTMLElement | null\n if (existingBtn) {\n existingBtn.remove()\n }\n } catch {\n // ignore\n }\n if (this.floatingIcon) {\n this.floatingIcon.remove()\n this.floatingIcon = null\n }\n\n const btn = document.createElement('button')\n btn.id = 'nexa-dev-overlay-floating'\n btn.title = 'Toggle Nexa DevTools'\n\n const size = this.config.floatingButtonSize ?? 48\n const offset = this.config.floatingButtonOffset ?? 24\n const pos = this.config.position || 'bottom-right'\n const isBottom = pos.includes('bottom')\n const isRight = pos.includes('right')\n const posStyles = `${isBottom ? `bottom: ${offset}px;` : `top: ${offset}px;`} ${isRight ? `right: ${offset}px;` : `left: ${offset}px;`}`\n\n const floatingTheme =\n this.config.floatingButtonTheme === 'inherit'\n ? this.config.theme\n : this.config.floatingButtonTheme\n\n let bg = 'linear-gradient(135deg,#0ea5e9,#8b5cf6)'\n let color = '#ffffff'\n let boxShadow = '0 16px 36px rgba(2,6,23,0.34)'\n let border = 'none'\n\n if (floatingTheme === 'light') {\n bg = 'linear-gradient(135deg,#f8fbff,#e0f2fe)'\n color = '#0f172a'\n boxShadow = '0 14px 30px rgba(15,23,42,0.16)'\n border = '1px solid #d8e4f2'\n }\n\n const branding = this.config.branding || 'Nexa DevTools'\n const icon =\n this.config.icon ||\n 'https://raw.githubusercontent.com/Berea-Soft/nexa/refs/heads/main/src/assets/faviconNew.png'\n\n btn.style.cssText = `\n position: fixed;\n ${posStyles}\n width: ${size}px;\n height: ${size}px;\n border-radius: 50%;\n border: ${border};\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 2147483648;\n cursor: pointer;\n box-shadow: ${boxShadow};\n background: ${bg};\n color: ${color};\n font-weight: 700;\n font-size: ${Math.max(12, Math.floor(size / 3))}px;\n `\n\n btn.innerHTML = `<img src=\"${icon}\" alt=\"${branding}\" style=\"width:${size - 10}px;height:auto;object-fit:cover;border-radius:999px;display:block;\" />`\n\n btn.addEventListener('click', (e) => {\n e.stopPropagation()\n this.toggle()\n })\n\n btn.addEventListener('mousedown', (ev) => ev.preventDefault())\n\n document.body.appendChild(btn)\n // If the panel is currently visible, keep the floating button hidden\n btn.style.display = this.visible ? 'none' : 'flex'\n this.floatingIcon = btn\n }\n\n private showFloatingIcon(): void {\n if (!this.floatingIcon) {\n if (this.config.enabled) {\n this.createFloatingIcon()\n }\n return\n }\n this.floatingIcon.style.display = 'flex'\n }\n\n private hideFloatingIcon(): void {\n if (!this.floatingIcon) {\n return\n }\n this.floatingIcon.style.display = 'none'\n }\n\n private applyConfigToUI(newConfig?: Required<DevOverlayConfig>): void {\n this.config = newConfig ?? this.tracker.getConfig()\n const pos = this.config.position\n const isBottom = pos.includes('bottom')\n const isRight = pos.includes('right')\n const offsetPx = `${this.config.floatingButtonOffset ?? 24}px`\n\n if (this.panel) {\n this.panel.style.bottom = isBottom ? offsetPx : ''\n this.panel.style.top = isBottom ? '' : offsetPx\n this.panel.style.right = isRight ? offsetPx : ''\n this.panel.style.left = isRight ? '' : offsetPx\n if (this.config.theme === 'light') {\n this.panel.classList.add('nexa-theme-light')\n } else {\n this.panel.classList.remove('nexa-theme-light')\n }\n\n // Update branding and icon if changed\n const logoImg = this.panel.querySelector(\n '.nexa-logo img',\n ) as HTMLImageElement\n const titleSpan = this.panel.querySelector('.nexa-title') as HTMLElement\n const branding = this.config.branding || 'Nexa DevTools'\n const icon =\n this.config.icon ||\n 'https://raw.githubusercontent.com/Berea-Soft/nexa/refs/heads/main/src/assets/faviconNew.png'\n\n if (logoImg) {\n logoImg.src = icon\n logoImg.alt = branding\n }\n if (titleSpan) {\n titleSpan.textContent = branding\n }\n }\n\n // Recreate floating icon to apply new size/position/theme\n if (this.floatingIcon) {\n this.floatingIcon.remove()\n this.floatingIcon = null\n }\n if (this.config.enabled && (!this.config.devOnly || isDevelopmentEnv())) {\n this.createFloatingIcon()\n }\n }\n\n // Public helper to allow external callers (e.g. HMR or createDevOverlay)\n // to request the UI to refresh according to the tracker config.\n public refreshConfig(newConfig?: Required<DevOverlayConfig>): void {\n this.applyConfigToUI(newConfig)\n }\n}\n","export { RequestTracker } from './tracker'\nexport { DevOverlayUI } from './overlay'\nexport type { TrackedRequest, DevMetrics, DevOverlayConfig } from './types'\n\nimport { RequestTracker, loadPersistedConfig } from './tracker'\nimport { DevOverlayUI } from './overlay'\nimport type { DevOverlayConfig } from './types'\n\nlet overlayInstance: DevOverlayUI | null = null\nlet trackerInstance: RequestTracker | null = null\n\nexport const defaultDevOverlayConfig: DevOverlayConfig = {\n enabled: true,\n maxHistory: 500,\n keyboardShortcut: 'ctrl+shift+n',\n position: 'bottom-right',\n theme: 'dark',\n devOnly: true,\n floatingButtonSize: 48,\n floatingButtonOffset: 24,\n floatingButtonTheme: 'inherit',\n branding: 'Nexa DevTools',\n icon: 'https://raw.githubusercontent.com/Berea-Soft/nexa/refs/heads/main/src/assets/faviconNew.png',\n}\n\n/**\n * Create (or return) the singleton Dev Overlay instances.\n *\n * The `config` object overrides defaults exposed via `defaultDevOverlayConfig`.\n */\nexport function createDevOverlay(config: DevOverlayConfig = {}): {\n tracker: RequestTracker\n ui: DevOverlayUI\n config: Required<DevOverlayConfig>\n} {\n if (overlayInstance && trackerInstance) {\n // If overlay already exists (HMR or repeated init), re-load persisted config\n // and apply it to the existing tracker/UI so settings persist across reloads.\n const persisted = loadPersistedConfig()\n const merged = { ...defaultDevOverlayConfig, ...config, ...persisted }\n const newCfg = trackerInstance.updateConfig(merged)\n // Ask UI to refresh to apply position/theme/size changes\n try {\n // `refreshConfig` is a public helper on DevOverlayUI\n overlayInstance.refreshConfig(newCfg)\n } catch {\n // ignore if UI cannot be refreshed\n }\n\n return {\n tracker: trackerInstance,\n ui: overlayInstance,\n config: newCfg,\n }\n }\n\n // Load persisted settings from localStorage and merge so persisted values take precedence:\n // defaults <- provided <- persisted (persisted wins)\n const persisted = loadPersistedConfig()\n const merged = { ...defaultDevOverlayConfig, ...config, ...persisted }\n trackerInstance = new RequestTracker(merged)\n overlayInstance = new DevOverlayUI(trackerInstance)\n // Do not open the panel by default; the UI will expose a floating icon when enabled\n\n return {\n tracker: trackerInstance,\n ui: overlayInstance,\n config: trackerInstance.getConfig(),\n }\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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"}
|