fetchguard 2.1.2 → 2.2.1
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.md +52 -23
- package/dist/index.d.ts +3 -617
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/worker-DBL8XAZJ.d.ts +643 -0
- package/dist/worker.d.ts +2 -2
- package/dist/worker.js +34 -0
- package/dist/worker.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts","../src/messages.ts","../src/constants.ts","../src/errors.ts","../src/error-codes.ts","../src/utils/formdata.ts","../src/utils/registry.ts","../src/provider/create-provider.ts","../src/provider/storage/indexeddb.ts","../src/provider/parser/normalize.ts","../src/provider/parser/body.ts","../src/provider/parser/cookie.ts","../src/provider/strategy/cookie.ts","../src/provider/strategy/body.ts","../src/provider/presets.ts","../src/utils/binary.ts","../src/helpers.ts"],"sourcesContent":["import type {\r\n FetchGuardOptions,\r\n FetchGuardRequestInit,\r\n WorkerConfig,\r\n FetchEnvelope,\r\n ProviderPresetConfig,\r\n AuthResult,\r\n DebugHooks,\r\n RetryConfig,\r\n NetworkErrorDetail,\r\n DedupeConfig,\r\n RequestMetrics\r\n} from './types'\r\nimport type { MainToWorkerMessage } from './messages'\r\nimport { ok, err, type Result } from 'ts-micro-result'\r\nimport { MSG } from './messages'\r\nimport { DEFAULT_REFRESH_EARLY_MS } from './constants'\r\nimport { RequestErrors } from './errors'\r\nimport { serializeFormData, isFormData } from './utils/formdata'\r\n\r\n/**\r\n * Request timing data for metrics calculation\r\n */\r\ninterface RequestTiming {\r\n /** When request was created (before queue) */\r\n createdAt: number\r\n /** When request was sent to worker (after queue) */\r\n sentAt?: number\r\n}\r\n\r\n/**\r\n * Queue item for sequential message processing\r\n */\r\ninterface QueueItem {\r\n id: string\r\n message: MainToWorkerMessage\r\n /** Transferable objects for zero-copy postMessage (e.g., ArrayBuffers from FormData) */\r\n transferables?: Transferable[]\r\n resolve: (response: unknown) => void\r\n reject: (error: Error) => void\r\n timeout: ReturnType<typeof setTimeout>\r\n}\r\n\r\n/** Default max concurrent requests */\r\nconst DEFAULT_MAX_CONCURRENT = 6\r\n\r\n/** Default max queue size */\r\nconst DEFAULT_MAX_QUEUE_SIZE = 1000\r\n\r\n/** Default setup timeout (ms) */\r\nconst DEFAULT_SETUP_TIMEOUT = 10000\r\n\r\n/** Default request timeout (ms) */\r\nconst DEFAULT_REQUEST_TIMEOUT = 30000\r\n\r\n/**\r\n * FetchGuard Client - main interface cho việc gọi API thông qua Web Worker\r\n */\r\nexport class FetchGuardClient {\r\n private worker: Worker\r\n private messageId = 0\r\n // Using unknown because different messages have different response types\r\n // (FetchEnvelope for FETCH, AuthResult for AUTH_CALL, etc.)\r\n private pendingRequests = new Map<string, {\r\n resolve: (value: unknown) => void\r\n reject: (error: Error) => void\r\n }>()\r\n /** Track request URLs for debug hooks */\r\n private requestUrls = new Map<string, string>()\r\n /** Track request timing for metrics */\r\n private requestTimings = new Map<string, RequestTiming>()\r\n private authListeners = new Set<(state: AuthResult) => void>()\r\n private readyListeners = new Set<() => void>()\r\n private isReady = false\r\n\r\n private requestQueue: QueueItem[] = []\r\n private activeRequests = 0\r\n private readonly maxConcurrent: number\r\n private readonly maxQueueSize: number\r\n private readonly setupTimeout: number\r\n private readonly requestTimeout: number\r\n private setupResolve?: () => void\r\n private setupReject?: (error: Error) => void\r\n private readonly debug?: DebugHooks\r\n private readonly retry?: RetryConfig\r\n private readonly dedupe?: DedupeConfig\r\n /** In-flight requests for deduplication */\r\n private readonly inFlightRequests = new Map<string, Promise<Result<FetchEnvelope>>>()\r\n /** Recent completed requests for time-window deduplication */\r\n private readonly recentResults = new Map<string, { result: Result<FetchEnvelope>; timestamp: number }>()\r\n\r\n constructor(options: FetchGuardOptions) {\r\n this.maxConcurrent = options.maxConcurrent ?? DEFAULT_MAX_CONCURRENT\r\n this.maxQueueSize = options.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE\r\n this.setupTimeout = options.setupTimeout ?? DEFAULT_SETUP_TIMEOUT\r\n this.requestTimeout = options.requestTimeout ?? DEFAULT_REQUEST_TIMEOUT\r\n this.debug = options.debug\r\n this.retry = options.retry\r\n this.dedupe = options.dedupe\r\n this.worker = new Worker(new URL('./worker.js', import.meta.url), { \r\n type: 'module' \r\n })\r\n\r\n this.worker.onmessage = this.handleWorkerMessage.bind(this)\r\n this.worker.onerror = this.handleWorkerError.bind(this)\r\n\r\n this.initializeWorker(options)\r\n }\r\n\r\n /**\r\n * Initialize worker with config and provider\r\n */\r\n private async initializeWorker(options: FetchGuardOptions): Promise<void> {\r\n const config: WorkerConfig = {\r\n allowedDomains: options.allowedDomains || [],\r\n refreshEarlyMs: options.refreshEarlyMs ?? DEFAULT_REFRESH_EARLY_MS,\r\n defaultHeaders: options.defaultHeaders || {}\r\n }\r\n\r\n // Serialize provider config based on type\r\n let providerConfig: ProviderPresetConfig | string | null = null\r\n\r\n if (typeof options.provider === 'string') {\r\n // String = registry lookup (advanced usage)\r\n providerConfig = options.provider\r\n } else if ('type' in options.provider && options.provider.type) {\r\n // ProviderPresetConfig object (recommended)\r\n providerConfig = options.provider as ProviderPresetConfig\r\n } else {\r\n // TokenProvider instance - NOT SUPPORTED\r\n throw new Error(\r\n 'Direct TokenProvider instance is not supported. Use ProviderPresetConfig instead:\\n' +\r\n ' { type: \"cookie-auth\", refreshUrl: \"...\", loginUrl: \"...\", logoutUrl: \"...\" }\\n' +\r\n 'Or for custom providers, register in worker code and use string name.'\r\n )\r\n }\r\n\r\n const message = {\r\n id: this.generateMessageId(),\r\n type: MSG.SETUP,\r\n payload: {\r\n config,\r\n providerConfig\r\n }\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n // Setup will respond with READY (no id, so we track separately)\r\n this.setupResolve = resolve\r\n this.setupReject = reject\r\n\r\n this.worker.postMessage(message)\r\n\r\n setTimeout(() => {\r\n if (this.setupReject) {\r\n this.setupReject(new Error('Worker setup timeout'))\r\n this.setupResolve = undefined\r\n this.setupReject = undefined\r\n }\r\n }, this.setupTimeout)\r\n })\r\n }\r\n\r\n\r\n /**\r\n * Handle worker messages\r\n */\r\n private handleWorkerMessage(event: MessageEvent): void {\r\n const { id, type, payload } = event.data\r\n\r\n if (type === MSG.FETCH_RESULT) {\r\n // FETCH_RESULT contains FetchEnvelope (raw HTTP response)\r\n // Worker doesn't judge HTTP status - client receives envelope as-is\r\n const request = this.pendingRequests.get(id)\r\n if (!request) return\r\n\r\n const url = this.requestUrls.get(id)\r\n const timing = this.requestTimings.get(id)\r\n this.pendingRequests.delete(id)\r\n this.requestUrls.delete(id)\r\n this.requestTimings.delete(id)\r\n this.onRequestComplete()\r\n\r\n // Calculate metrics\r\n const metrics = this.calculateMetrics(timing)\r\n\r\n // Debug hook: onResponse\r\n if (this.debug?.onResponse && url) {\r\n this.debug.onResponse(url, payload as FetchEnvelope, metrics)\r\n }\r\n\r\n request.resolve(ok(payload as FetchEnvelope))\r\n return\r\n }\r\n\r\n if (type === MSG.FETCH_ERROR) {\r\n // Network/timeout/cancel errors (no HTTP response)\r\n const request = this.pendingRequests.get(id)\r\n if (!request) return\r\n\r\n const url = this.requestUrls.get(id)\r\n const timing = this.requestTimings.get(id)\r\n this.pendingRequests.delete(id)\r\n this.requestUrls.delete(id)\r\n this.requestTimings.delete(id)\r\n this.onRequestComplete()\r\n\r\n const errorMessage = String(payload?.error || 'Network error')\r\n\r\n // Calculate metrics\r\n const metrics = this.calculateMetrics(timing)\r\n\r\n // Debug hook: onError\r\n if (this.debug?.onError && url) {\r\n this.debug.onError(url, { code: 'NETWORK_ERROR', message: errorMessage }, metrics)\r\n }\r\n\r\n request.resolve(err(\r\n RequestErrors.NetworkError({ message: errorMessage })\r\n ))\r\n return\r\n }\r\n\r\n if (type === MSG.ERROR) {\r\n const request = this.pendingRequests.get(id)\r\n if (!request) return\r\n\r\n this.pendingRequests.delete(id)\r\n this.onRequestComplete()\r\n\r\n request.resolve(err(payload.errors, payload.meta))\r\n return\r\n }\r\n\r\n if (type === MSG.SETUP_ERROR) {\r\n // Setup failed - reject setup promise\r\n if (this.setupReject) {\r\n this.setupReject(new Error(`Worker setup failed: ${payload?.error || 'Unknown error'}`))\r\n this.setupResolve = undefined\r\n this.setupReject = undefined\r\n }\r\n return\r\n }\r\n\r\n if (type === MSG.READY) {\r\n this.isReady = true\r\n\r\n // Debug hook: onWorkerReady\r\n this.debug?.onWorkerReady?.()\r\n\r\n // Notify ready listeners\r\n for (const listener of this.readyListeners) {\r\n listener()\r\n }\r\n\r\n if (this.setupResolve) {\r\n this.setupResolve()\r\n this.setupResolve = undefined\r\n this.setupReject = undefined\r\n }\r\n return\r\n }\r\n\r\n if (type === MSG.PONG) {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n this.onRequestComplete()\r\n request.resolve(ok({ timestamp: payload?.timestamp }))\r\n }\r\n return\r\n }\r\n\r\n if (type === MSG.AUTH_STATE_CHANGED) {\r\n for (const cb of this.authListeners) cb(payload)\r\n return\r\n }\r\n\r\n if (type === MSG.AUTH_CALL_RESULT) {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n this.onRequestComplete()\r\n request.resolve(ok(payload)) // payload is AuthResult\r\n }\r\n return\r\n }\r\n\r\n if (type === MSG.TOKEN_REFRESHED) {\r\n // Debug hook: onRefresh\r\n this.debug?.onRefresh?.(payload?.reason)\r\n return\r\n }\r\n }\r\n\r\n /**\r\n * Handle worker errors\r\n */\r\n private handleWorkerError(error: ErrorEvent): void {\r\n console.error('Worker error:', error)\r\n\r\n // Debug hook: onWorkerError\r\n this.debug?.onWorkerError?.(error)\r\n\r\n // Reject all pending requests\r\n for (const [id, request] of this.pendingRequests) {\r\n request.reject(new Error(`Worker error: ${error.message}`))\r\n }\r\n this.pendingRequests.clear()\r\n this.requestUrls.clear()\r\n this.requestTimings.clear()\r\n }\r\n\r\n /**\r\n * Generate unique message ID\r\n */\r\n private generateMessageId(): string {\r\n return `msg_${++this.messageId}_${Date.now()}`\r\n }\r\n\r\n /**\r\n * Make API request with optional deduplication, retry, and AbortSignal support\r\n *\r\n * @param url - Full URL to fetch\r\n * @param options - Request options including optional AbortSignal\r\n * @returns Result with FetchEnvelope on success, error on failure\r\n *\r\n * @example\r\n * // With AbortSignal\r\n * const controller = new AbortController()\r\n * setTimeout(() => controller.abort(), 5000)\r\n * const result = await api.fetch('/slow', { signal: controller.signal })\r\n */\r\n async fetch(url: string, options: FetchGuardRequestInit = {}): Promise<Result<FetchEnvelope>> {\r\n // Extract signal from options (not passed to worker - handled client-side)\r\n const { signal, ...restOptions } = options\r\n\r\n // Check if already aborted\r\n if (signal?.aborted) {\r\n return err(RequestErrors.Cancelled())\r\n }\r\n\r\n // Check for deduplication\r\n const dedupeKey = this.getDedupeKey(url, restOptions)\r\n if (dedupeKey) {\r\n // Check for in-flight request\r\n const inFlight = this.inFlightRequests.get(dedupeKey)\r\n if (inFlight) {\r\n // If we have a signal, wrap the in-flight promise to support cancellation\r\n if (signal) {\r\n return this.wrapWithAbortSignal(inFlight, signal, null)\r\n }\r\n return inFlight\r\n }\r\n\r\n // Check for recent result within time window\r\n const window = this.dedupe?.window ?? 0\r\n if (window > 0) {\r\n const recent = this.recentResults.get(dedupeKey)\r\n if (recent && Date.now() - recent.timestamp < window) {\r\n return recent.result\r\n }\r\n }\r\n\r\n // Create deduped request\r\n const promise = this.fetchWithRetryAndSignal(url, restOptions, signal ?? undefined)\r\n this.inFlightRequests.set(dedupeKey, promise)\r\n\r\n try {\r\n const result = await promise\r\n // Store result for time-window deduplication\r\n if (window > 0) {\r\n this.recentResults.set(dedupeKey, { result, timestamp: Date.now() })\r\n // Clean up old results after window expires\r\n setTimeout(() => this.recentResults.delete(dedupeKey), window)\r\n }\r\n return result\r\n } finally {\r\n this.inFlightRequests.delete(dedupeKey)\r\n }\r\n }\r\n\r\n // No deduplication - just fetch with retry and signal\r\n return this.fetchWithRetryAndSignal(url, restOptions, signal ?? undefined)\r\n }\r\n\r\n /**\r\n * Wrap a promise with AbortSignal support\r\n */\r\n private wrapWithAbortSignal(\r\n promise: Promise<Result<FetchEnvelope>>,\r\n signal: AbortSignal,\r\n requestId: string | null\r\n ): Promise<Result<FetchEnvelope>> {\r\n return new Promise((resolve) => {\r\n // Handle abort\r\n const abortHandler = () => {\r\n if (requestId) {\r\n this.cancel(requestId)\r\n }\r\n resolve(err(RequestErrors.Cancelled()))\r\n }\r\n\r\n if (signal.aborted) {\r\n abortHandler()\r\n return\r\n }\r\n\r\n signal.addEventListener('abort', abortHandler, { once: true })\r\n\r\n promise.then((result) => {\r\n signal.removeEventListener('abort', abortHandler)\r\n resolve(result)\r\n })\r\n })\r\n }\r\n\r\n /**\r\n * Fetch with retry logic and AbortSignal support (internal)\r\n */\r\n private async fetchWithRetryAndSignal(\r\n url: string,\r\n options: Omit<FetchGuardRequestInit, 'signal'>,\r\n signal?: AbortSignal\r\n ): Promise<Result<FetchEnvelope>> {\r\n const maxAttempts = this.retry?.maxAttempts ?? 0\r\n const delay = this.retry?.delay ?? 1000\r\n const backoff = this.retry?.backoff ?? 1\r\n const maxDelay = this.retry?.maxDelay ?? 30000\r\n const jitter = this.retry?.jitter ?? 0\r\n const shouldRetry = this.retry?.shouldRetry ?? this.defaultShouldRetry\r\n\r\n let lastResult: Result<FetchEnvelope> | null = null\r\n let currentDelay = delay\r\n\r\n // Initial attempt + retries\r\n for (let attempt = 0; attempt <= maxAttempts; attempt++) {\r\n // Check if aborted before each attempt\r\n if (signal?.aborted) {\r\n return err(RequestErrors.Cancelled())\r\n }\r\n\r\n const { id, result } = this.fetchWithId(url, options)\r\n\r\n // If we have a signal, wrap result with abort support\r\n if (signal) {\r\n lastResult = await this.wrapWithAbortSignal(result, signal, id)\r\n } else {\r\n lastResult = await result\r\n }\r\n\r\n // Success or HTTP error (4xx/5xx) - don't retry\r\n if (lastResult.ok) {\r\n return lastResult\r\n }\r\n\r\n // Check if cancelled\r\n if (lastResult.errors[0]?.code === 'REQUEST_CANCELLED') {\r\n return lastResult\r\n }\r\n\r\n // Check if we should retry this error\r\n const error = lastResult.errors[0]\r\n const errorDetail: NetworkErrorDetail = {\r\n code: error?.code as NetworkErrorDetail['code'] ?? 'NETWORK_ERROR',\r\n message: error?.message ?? 'Unknown error'\r\n }\r\n\r\n // Don't retry if:\r\n // - This was the last attempt\r\n // - Error is not retryable (e.g., cancelled)\r\n if (attempt >= maxAttempts || !shouldRetry(errorDetail)) {\r\n return lastResult\r\n }\r\n\r\n // Wait before retry (with exponential backoff and optional jitter)\r\n // Jitter only applies when shouldRetry=true (we're actually retrying)\r\n const cappedDelay = Math.min(currentDelay, maxDelay)\r\n const jitteredDelay = this.applyJitter(cappedDelay, jitter)\r\n\r\n // Check abort during delay\r\n if (signal) {\r\n const aborted = await this.sleepWithAbort(jitteredDelay, signal)\r\n if (aborted) {\r\n return err(RequestErrors.Cancelled())\r\n }\r\n } else {\r\n await this.sleep(jitteredDelay)\r\n }\r\n\r\n currentDelay = currentDelay * backoff\r\n }\r\n\r\n return lastResult!\r\n }\r\n\r\n /**\r\n * Generate deduplication key for request\r\n * Returns null if request should not be deduplicated\r\n */\r\n private getDedupeKey(url: string, options: FetchGuardRequestInit): string | null {\r\n if (!this.dedupe?.enabled) {\r\n return null\r\n }\r\n\r\n // Use custom key generator if provided\r\n if (this.dedupe.keyGenerator) {\r\n return this.dedupe.keyGenerator(url, options)\r\n }\r\n\r\n // Default: only dedupe GET requests by URL\r\n const method = (options.method ?? 'GET').toUpperCase()\r\n if (method !== 'GET') {\r\n return null\r\n }\r\n\r\n return `GET:${url}`\r\n }\r\n\r\n /**\r\n * Apply jitter to a delay value\r\n * Jitter adds ±(jitter * delay) randomness to prevent thundering herd\r\n * @param delay - Base delay in milliseconds\r\n * @param jitter - Jitter factor (0-1)\r\n * @returns Jittered delay\r\n */\r\n private applyJitter(delay: number, jitter: number): number {\r\n if (jitter <= 0) return delay\r\n // Clamp jitter to valid range [0, 1]\r\n const clampedJitter = Math.min(Math.max(jitter, 0), 1)\r\n // Random value between -1 and 1\r\n const randomFactor = (Math.random() * 2) - 1\r\n // Apply jitter: delay ± (delay * jitter * random)\r\n return Math.max(0, delay + (delay * clampedJitter * randomFactor))\r\n }\r\n\r\n /**\r\n * Default retry condition - only retry on NETWORK_ERROR\r\n */\r\n private defaultShouldRetry(error: NetworkErrorDetail): boolean {\r\n // Don't retry cancelled requests or parse errors\r\n return error.code === 'NETWORK_ERROR'\r\n }\r\n\r\n /**\r\n * Calculate request metrics from timing data\r\n */\r\n private calculateMetrics(timing?: RequestTiming): RequestMetrics | undefined {\r\n if (!timing) return undefined\r\n\r\n const endTime = Date.now()\r\n const startTime = timing.createdAt\r\n const sentAt = timing.sentAt ?? startTime\r\n const duration = endTime - startTime\r\n const queueTime = sentAt - startTime\r\n const ipcTime = duration - queueTime // Approximate: total - queue = IPC + server\r\n\r\n return {\r\n startTime,\r\n endTime,\r\n duration,\r\n queueTime,\r\n ipcTime\r\n }\r\n }\r\n\r\n /**\r\n * Sleep helper for retry delay\r\n */\r\n private sleep(ms: number): Promise<void> {\r\n return new Promise(resolve => setTimeout(resolve, ms))\r\n }\r\n\r\n /**\r\n * Sleep with abort signal support\r\n * Returns true if aborted, false if completed normally\r\n */\r\n private sleepWithAbort(ms: number, signal: AbortSignal): Promise<boolean> {\r\n return new Promise((resolve) => {\r\n if (signal.aborted) {\r\n resolve(true)\r\n return\r\n }\r\n\r\n const timer = setTimeout(() => {\r\n signal.removeEventListener('abort', abortHandler)\r\n resolve(false)\r\n }, ms)\r\n\r\n const abortHandler = () => {\r\n clearTimeout(timer)\r\n resolve(true)\r\n }\r\n\r\n signal.addEventListener('abort', abortHandler, { once: true })\r\n })\r\n }\r\n\r\n /**\r\n * Fetch with id for external cancellation\r\n * Returns { id, result, cancel }\r\n * Now uses queue system for sequential processing\r\n */\r\n fetchWithId(url: string, options: FetchGuardRequestInit = {}): {\r\n id: string\r\n result: Promise<Result<FetchEnvelope>>\r\n cancel: () => void\r\n } {\r\n const id = this.generateMessageId()\r\n\r\n // Serialize FormData if present (async operation)\r\n const result = new Promise<Result<FetchEnvelope>>(async (resolve, reject) => {\r\n this.pendingRequests.set(id, {\r\n resolve: (response) => resolve(response as Result<FetchEnvelope>),\r\n reject: (error) => reject(error)\r\n })\r\n // Track URL for debug hooks\r\n this.requestUrls.set(id, url)\r\n // Track timing for metrics\r\n this.requestTimings.set(id, { createdAt: Date.now() })\r\n\r\n // Debug hook: onRequest\r\n this.debug?.onRequest?.(url, options)\r\n\r\n try {\r\n let serializedOptions = { ...options }\r\n let transferables: Transferable[] | undefined\r\n\r\n // Serialize FormData body before sending to worker\r\n if (options.body && isFormData(options.body)) {\r\n const { data, transferables: formDataTransferables } = await serializeFormData(options.body)\r\n // SerializedFormData will be deserialized back to FormData in worker\r\n serializedOptions.body = data as unknown as BodyInit\r\n // ArrayBuffers for zero-copy transfer\r\n if (formDataTransferables.length > 0) {\r\n transferables = formDataTransferables\r\n }\r\n }\r\n\r\n // Serialize Headers object to plain object (Headers cannot be cloned)\r\n if (options.headers) {\r\n if (options.headers instanceof Headers) {\r\n const plainHeaders: Record<string, string> = {}\r\n options.headers.forEach((value, key) => {\r\n plainHeaders[key] = value\r\n })\r\n serializedOptions.headers = plainHeaders\r\n }\r\n }\r\n\r\n const message = { id, type: MSG.FETCH, payload: { url, options: serializedOptions } }\r\n\r\n await this.sendMessageQueued(message, 30000, transferables)\r\n } catch (error) {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n this.requestUrls.delete(id)\r\n this.requestTimings.delete(id)\r\n request.reject(error instanceof Error ? error : new Error(String(error)))\r\n }\r\n }\r\n })\r\n\r\n const cancel = () => this.cancel(id)\r\n\r\n return { id, result, cancel }\r\n }\r\n\r\n /**\r\n * Cancel a pending request by ID\r\n */\r\n cancel(id: string): void {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n const url = this.requestUrls.get(id)\r\n const timing = this.requestTimings.get(id)\r\n this.pendingRequests.delete(id)\r\n this.requestUrls.delete(id)\r\n this.requestTimings.delete(id)\r\n this.worker.postMessage({ id, type: MSG.CANCEL })\r\n\r\n // Calculate metrics\r\n const metrics = this.calculateMetrics(timing)\r\n\r\n // Debug hook: onError for cancelled request\r\n if (this.debug?.onError && url) {\r\n this.debug.onError(url, { code: 'REQUEST_CANCELLED', message: 'Request cancelled' }, metrics)\r\n }\r\n\r\n request.reject(new Error('Request cancelled'))\r\n }\r\n }\r\n\r\n /**\r\n * Convenience methods\r\n */\r\n async get(url: string, options: Omit<FetchGuardRequestInit, 'method' | 'body'> = {}): Promise<Result<FetchEnvelope>> {\r\n return this.fetch(url, { ...options, method: 'GET' })\r\n }\r\n\r\n async post(url: string, body?: unknown, options: Omit<FetchGuardRequestInit, 'method' | 'body'> = {}): Promise<Result<FetchEnvelope>> {\r\n // If body is FormData, use fetch directly (no JSON.stringify)\r\n if (body && isFormData(body)) {\r\n return this.fetch(url, {\r\n ...options,\r\n method: 'POST',\r\n body\r\n })\r\n }\r\n\r\n // For non-FormData body, use JSON\r\n const headers = new Headers(options.headers)\r\n\r\n // Set Content-Type if not already set and body is being stringified\r\n if (body && !headers.has('Content-Type')) {\r\n headers.set('Content-Type', 'application/json')\r\n }\r\n\r\n return this.fetch(url, {\r\n ...options,\r\n headers,\r\n method: 'POST',\r\n body: body ? JSON.stringify(body) : undefined\r\n })\r\n }\r\n\r\n async put(url: string, body?: unknown, options: Omit<FetchGuardRequestInit, 'method' | 'body'> = {}): Promise<Result<FetchEnvelope>> {\r\n // If body is FormData, use fetch directly (no JSON.stringify)\r\n if (body && isFormData(body)) {\r\n return this.fetch(url, {\r\n ...options,\r\n method: 'PUT',\r\n body\r\n })\r\n }\r\n\r\n // For non-FormData body, use JSON\r\n const headers = new Headers(options.headers)\r\n\r\n // Set Content-Type if not already set and body is being stringified\r\n if (body && !headers.has('Content-Type')) {\r\n headers.set('Content-Type', 'application/json')\r\n }\r\n\r\n return this.fetch(url, {\r\n ...options,\r\n headers,\r\n method: 'PUT',\r\n body: body ? JSON.stringify(body) : undefined\r\n })\r\n }\r\n\r\n async delete(url: string, options: Omit<FetchGuardRequestInit, 'method' | 'body'> = {}): Promise<Result<FetchEnvelope>> {\r\n return this.fetch(url, { ...options, method: 'DELETE' })\r\n }\r\n\r\n async patch(url: string, body?: unknown, options: Omit<FetchGuardRequestInit, 'method' | 'body'> = {}): Promise<Result<FetchEnvelope>> {\r\n // If body is FormData, use fetch directly (no JSON.stringify)\r\n if (body && isFormData(body)) {\r\n return this.fetch(url, {\r\n ...options,\r\n method: 'PATCH',\r\n body\r\n })\r\n }\r\n\r\n // For non-FormData body, use JSON\r\n const headers = new Headers(options.headers)\r\n\r\n // Set Content-Type if not already set and body is being stringified\r\n if (body && !headers.has('Content-Type')) {\r\n headers.set('Content-Type', 'application/json')\r\n }\r\n\r\n return this.fetch(url, {\r\n ...options,\r\n headers,\r\n method: 'PATCH',\r\n body: body ? JSON.stringify(body) : undefined\r\n })\r\n }\r\n\r\n /**\r\n * Generic method to call any auth method on provider\r\n * @param method - Method name (login, logout, loginWithPhone, etc.)\r\n * @param emitEvent - Whether to emit AUTH_STATE_CHANGED event (default: true)\r\n * @param args - Arguments to pass to the method\r\n * @returns Promise<Result<AuthResult>> - Always returns AuthResult\r\n */\r\n async call(method: string, emitEvent?: boolean, ...args: unknown[]): Promise<Result<AuthResult>> {\r\n const id = this.generateMessageId()\r\n const message = { id, type: MSG.AUTH_CALL, payload: { method, args, emitEvent } }\r\n\r\n return new Promise<Result<AuthResult>>((resolve, reject) => {\r\n this.pendingRequests.set(id, {\r\n resolve: (r) => resolve(r as Result<AuthResult>),\r\n reject: (e: Error) => reject(e)\r\n })\r\n\r\n this.sendMessageQueued(message, 15000).catch((error) => {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n request.reject(error)\r\n }\r\n })\r\n })\r\n }\r\n\r\n\r\n /**\r\n * Convenience wrapper for login\r\n * @param payload - Login credentials\r\n * @param url - Optional URL override\r\n * @param emitEvent - Whether to emit AUTH_STATE_CHANGED event (default: true)\r\n */\r\n async login(payload?: unknown, url?: string, emitEvent: boolean = true): Promise<Result<AuthResult>> {\r\n const args: unknown[] = []\r\n if (typeof payload !== 'undefined') {\r\n args.push(payload)\r\n }\r\n if (typeof url !== 'undefined') {\r\n // If payload is undefined but url is provided, need to pass undefined explicitly\r\n if (args.length === 0) {\r\n args.push(undefined)\r\n }\r\n args.push(url)\r\n }\r\n return this.call('login', emitEvent, ...args)\r\n }\r\n\r\n /**\r\n * Convenience wrapper for logout\r\n * @param payload - Optional logout payload\r\n * @param emitEvent - Whether to emit AUTH_STATE_CHANGED event (default: true)\r\n */\r\n async logout(payload?: unknown, emitEvent: boolean = true): Promise<Result<AuthResult>> {\r\n const args = typeof payload === 'undefined' ? [] : [payload]\r\n return this.call('logout', emitEvent, ...args)\r\n }\r\n\r\n /**\r\n * Convenience wrapper for refreshToken\r\n * @param emitEvent - Whether to emit AUTH_STATE_CHANGED event (default: true)\r\n */\r\n async refreshToken(emitEvent: boolean = true): Promise<Result<AuthResult>> {\r\n return this.call('refreshToken', emitEvent)\r\n }\r\n\r\n /**\r\n * Exchange current token for a new one with different context\r\n *\r\n * Useful for:\r\n * - Switching tenants in multi-tenant apps\r\n * - Changing authorization scope\r\n * - Impersonating users (admin feature)\r\n *\r\n * @param url - URL to call for token exchange\r\n * @param options - Exchange options (method, payload, headers)\r\n * @param emitEvent - Whether to emit AUTH_STATE_CHANGED event (default: true)\r\n *\r\n * @example\r\n * // Switch tenant\r\n * await api.exchangeToken('https://auth.example.com/auth/select-tenant', {\r\n * payload: { tenantId: 'tenant_123' }\r\n * })\r\n *\r\n * // Change scope with PUT method\r\n * await api.exchangeToken('https://auth.example.com/auth/switch-context', {\r\n * method: 'PUT',\r\n * payload: { scope: 'admin' }\r\n * })\r\n *\r\n * // With custom headers (overrides defaultHeaders)\r\n * await api.exchangeToken('https://auth.example.com/auth/impersonate', {\r\n * payload: { userId: 'user_456' },\r\n * headers: { 'X-Impersonate-Reason': 'support-ticket-123' }\r\n * })\r\n */\r\n async exchangeToken(\r\n url: string,\r\n options?: { method?: 'POST' | 'PUT'; payload?: Record<string, unknown>; headers?: Record<string, string> },\r\n emitEvent: boolean = true\r\n ): Promise<Result<AuthResult>> {\r\n const args: unknown[] = [url]\r\n if (options) {\r\n args.push(options)\r\n }\r\n return this.call('exchangeToken', emitEvent, ...args)\r\n }\r\n\r\n /**\r\n * Check if worker is ready\r\n */\r\n ready(): boolean {\r\n return this.isReady\r\n }\r\n\r\n /**\r\n * Wait for worker to be ready\r\n * Returns immediately if already ready\r\n */\r\n async whenReady(): Promise<void> {\r\n if (this.isReady) return Promise.resolve()\r\n\r\n return new Promise<void>((resolve) => {\r\n this.readyListeners.add(resolve)\r\n })\r\n }\r\n\r\n /**\r\n * Subscribe to ready event\r\n * Callback is called immediately if already ready\r\n */\r\n onReady(callback: () => void): () => void {\r\n if (this.isReady) {\r\n // Already ready - call immediately\r\n callback()\r\n }\r\n\r\n this.readyListeners.add(callback)\r\n\r\n // Return unsubscribe function\r\n return () => {\r\n this.readyListeners.delete(callback)\r\n }\r\n }\r\n\r\n /**\r\n * Subscribe to auth state changes\r\n */\r\n onAuthStateChanged(cb: (state: AuthResult) => void): () => void {\r\n this.authListeners.add(cb)\r\n return () => this.authListeners.delete(cb)\r\n }\r\n\r\n /** Send PING and await PONG */\r\n async ping(): Promise<Result<{ timestamp: number }>> {\r\n const id = this.generateMessageId()\r\n const message = { id, type: MSG.PING, payload: { timestamp: Date.now() } }\r\n\r\n return new Promise<Result<{ timestamp: number }>>((resolve, reject) => {\r\n this.pendingRequests.set(id, {\r\n resolve: (r) => resolve(r as Result<{ timestamp: number }>),\r\n reject: (e: Error) => reject(e)\r\n })\r\n\r\n this.sendMessageQueued(message, 5000).catch((error) => {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n request.reject(error)\r\n }\r\n })\r\n })\r\n }\r\n\r\n\r\n /**\r\n * Send message through queue system\r\n * All messages go through queue for sequential processing\r\n * @param transferables - Optional Transferable objects for zero-copy postMessage\r\n */\r\n private sendMessageQueued<T = unknown>(\r\n message: MainToWorkerMessage,\r\n timeoutMs: number = this.requestTimeout,\r\n transferables?: Transferable[]\r\n ): Promise<T> {\r\n return new Promise((resolve, reject) => {\r\n // Check queue size limit to prevent memory leak\r\n if (this.requestQueue.length >= this.maxQueueSize) {\r\n reject(err(RequestErrors.QueueFull({ size: this.requestQueue.length, maxSize: this.maxQueueSize })))\r\n return\r\n }\r\n\r\n const timeout = setTimeout(() => {\r\n const index = this.requestQueue.findIndex(item => item.id === message.id)\r\n if (index !== -1) {\r\n this.requestQueue.splice(index, 1)\r\n }\r\n this.pendingRequests.delete(message.id)\r\n this.requestUrls.delete(message.id)\r\n this.requestTimings.delete(message.id)\r\n reject(err(RequestErrors.Timeout()))\r\n }, timeoutMs)\r\n\r\n const queueItem: QueueItem = {\r\n id: message.id,\r\n message,\r\n transferables,\r\n resolve: resolve as (response: unknown) => void,\r\n reject,\r\n timeout\r\n }\r\n\r\n this.requestQueue.push(queueItem)\r\n\r\n this.processQueue()\r\n })\r\n }\r\n\r\n /**\r\n * Process message queue with concurrency limit\r\n *\r\n * Uses semaphore pattern to allow N concurrent requests.\r\n * Benefits:\r\n * - Higher throughput than sequential processing\r\n * - Backpressure via maxConcurrent limit\r\n * - Better error isolation (one failure doesn't affect others)\r\n */\r\n private processQueue(): void {\r\n // Process as many items as we can within concurrency limit\r\n while (this.requestQueue.length > 0 && this.activeRequests < this.maxConcurrent) {\r\n const item = this.requestQueue.shift()\r\n if (!item) continue\r\n\r\n this.activeRequests++\r\n\r\n // Update timing: mark when request is actually sent to worker\r\n const timing = this.requestTimings.get(item.id)\r\n if (timing) {\r\n timing.sentAt = Date.now()\r\n }\r\n\r\n try {\r\n // Use transferables for zero-copy transfer when available (e.g., FormData with files)\r\n if (item.transferables && item.transferables.length > 0) {\r\n this.worker.postMessage(item.message, item.transferables)\r\n } else {\r\n this.worker.postMessage(item.message)\r\n }\r\n // Note: activeRequests is decremented when response is received\r\n // in handleWorkerMessage, not here\r\n } catch (error) {\r\n this.activeRequests--\r\n clearTimeout(item.timeout)\r\n item.reject(error instanceof Error ? error : new Error(String(error)))\r\n // Continue processing queue after error\r\n this.processQueue()\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Called when a request completes (success or error)\r\n * Decrements active count and processes next items in queue\r\n */\r\n private onRequestComplete(): void {\r\n this.activeRequests--\r\n this.processQueue()\r\n }\r\n\r\n /**\r\n * Cleanup - terminate worker\r\n */\r\n destroy(): void {\r\n this.worker.terminate()\r\n this.pendingRequests.clear()\r\n this.requestUrls.clear()\r\n this.requestTimings.clear()\r\n\r\n for (const item of this.requestQueue) {\r\n clearTimeout(item.timeout)\r\n item.reject(new Error('Client destroyed'))\r\n }\r\n this.requestQueue = []\r\n }\r\n}\r\n\r\n/**\r\n * Factory function to create FetchGuard client\r\n */\r\nexport function createClient(options: FetchGuardOptions): FetchGuardClient {\r\n return new FetchGuardClient(options)\r\n}\r\n","import type { ErrorDetail, Result, ResultMeta } from 'ts-micro-result'\r\nimport type { WorkerConfig, FetchGuardRequestInit, ProviderPresetConfig, AuthResult, FetchEnvelope, RefreshReason } from './types'\r\n\r\n/**\r\n * MESSAGE PAYLOADS - SINGLE SOURCE OF TRUTH\r\n *\r\n * Define all message payloads here. Type unions and MSG constants are auto-generated.\r\n *\r\n * USAGE:\r\n * - To add a new message: Just add one line to the appropriate interface\r\n * - Payload types are automatically inferred\r\n * - MSG constants are automatically generated\r\n *\r\n * EXAMPLE:\r\n * ```typescript\r\n * interface MainPayloads {\r\n * NEW_MESSAGE: { foo: string } // Add this line\r\n * }\r\n * // => Automatically get MainToWorkerMessage union with NEW_MESSAGE\r\n * // => Automatically get MSG.NEW_MESSAGE = 'NEW_MESSAGE'\r\n * ```\r\n */\r\n\r\n/**\r\n * Payloads for messages sent from Main thread → Worker thread\r\n */\r\nexport interface MainPayloads {\r\n SETUP: { config: WorkerConfig; providerConfig: ProviderPresetConfig | string | null }\r\n FETCH: { url: string; options?: FetchGuardRequestInit }\r\n AUTH_CALL: { method: string; args: unknown[]; emitEvent?: boolean } // Generic auth method call (login, logout, loginWithPhone, etc.)\r\n CANCEL: undefined\r\n PING: { timestamp: number }\r\n}\r\n\r\n/**\r\n * Payloads for messages sent from Worker thread → Main thread\r\n */\r\nexport interface WorkerPayloads {\r\n ERROR: { errors: ErrorDetail[]; meta?: ResultMeta, status?: number }\r\n READY: undefined\r\n SETUP_ERROR: { error: string }\r\n PONG: { timestamp: number }\r\n LOG: { level: 'info' | 'warn' | 'error'; message: string }\r\n AUTH_STATE_CHANGED: AuthResult\r\n AUTH_CALL_RESULT: AuthResult\r\n FETCH_RESULT: FetchEnvelope\r\n FETCH_ERROR: { error: string; status?: number }\r\n TOKEN_REFRESHED: { reason: RefreshReason }\r\n}\r\n\r\n/**\r\n * Generate message type from payload definition\r\n * Handles optional payloads (undefined) gracefully\r\n */\r\ntype MessageFromPayloads<P> = {\r\n [K in keyof P]: { id: string; type: K } & (\r\n P[K] extends undefined ? {} : { payload: P[K] }\r\n )\r\n}[keyof P]\r\n\r\n/**\r\n * Message type unions - auto-generated from payload interfaces\r\n */\r\nexport type MainToWorkerMessage = MessageFromPayloads<MainPayloads>\r\nexport type WorkerToMainMessage = MessageFromPayloads<WorkerPayloads>\r\n\r\n/**\r\n * Message type unions for compile-time type checking\r\n */\r\nexport type MainType = keyof MainPayloads\r\nexport type WorkerType = keyof WorkerPayloads\r\nexport type MessageType = MainType | WorkerType\r\n\r\n/**\r\n * MSG constants object\r\n * Usage: MSG.SETUP, MSG.FETCH, etc.\r\n */\r\nexport const MSG = Object.freeze({\r\n // Main -> Worker messages\r\n SETUP: 'SETUP',\r\n FETCH: 'FETCH',\r\n AUTH_CALL: 'AUTH_CALL',\r\n CANCEL: 'CANCEL',\r\n PING: 'PING',\r\n\r\n // Worker -> Main messages\r\n ERROR: 'ERROR',\r\n READY: 'READY',\r\n SETUP_ERROR: 'SETUP_ERROR',\r\n PONG: 'PONG',\r\n LOG: 'LOG',\r\n AUTH_STATE_CHANGED: 'AUTH_STATE_CHANGED',\r\n AUTH_CALL_RESULT: 'AUTH_CALL_RESULT',\r\n FETCH_RESULT: 'FETCH_RESULT',\r\n FETCH_ERROR: 'FETCH_ERROR',\r\n TOKEN_REFRESHED: 'TOKEN_REFRESHED'\r\n}) as { readonly [K in MessageType]: K }\r\n","/**\n * FetchGuard Default Configuration Values\n */\n\n/**\n * Default time (in milliseconds) to refresh token before expiry\n * @default 60000 (60 seconds)\n */\nexport const DEFAULT_REFRESH_EARLY_MS = 60_000\n","/**\n * Error definitions organized by domain\n * Using ts-micro-result's defineError for consistency\n */\n\nimport { defineError, defineErrorAdvanced } from 'ts-micro-result'\nimport { ERROR_CODES } from './error-codes'\n\n/**\n * General errors\n */\nexport const GeneralErrors = {\n Unexpected: defineError(ERROR_CODES.UNEXPECTED, 'Unexpected error'),\n UnknownMessage: defineError(ERROR_CODES.UNKNOWN_MESSAGE, 'Unknown message type'),\n ResultParse: defineError(ERROR_CODES.RESULT_PARSE_ERROR, 'Failed to parse result'),\n} as const\n\n/**\n * Initialization errors\n */\nexport const InitErrors = {\n NotInitialized: defineError(ERROR_CODES.INIT_ERROR, 'Worker not initialized'),\n ProviderInitFailed: defineError(ERROR_CODES.PROVIDER_INIT_FAILED, 'Failed to initialize provider'),\n InitFailed: defineError(ERROR_CODES.INIT_FAILED, 'Initialization failed'),\n} as const\n\n/**\n * Authentication & Token errors\n */\nexport const AuthErrors = {\n TokenRefreshFailed: defineError(ERROR_CODES.TOKEN_REFRESH_FAILED, 'Token refresh failed'),\n TokenExchangeFailed: defineError(ERROR_CODES.TOKEN_EXCHANGE_FAILED, 'Token exchange failed'),\n LoginFailed: defineError(ERROR_CODES.LOGIN_FAILED, 'Login failed'),\n LogoutFailed: defineError(ERROR_CODES.LOGOUT_FAILED, 'Logout failed'),\n NotAuthenticated: defineError(ERROR_CODES.NOT_AUTHENTICATED, 'User is not authenticated'),\n} as const\n\n/**\n * Domain validation errors\n */\nexport const DomainErrors = {\n NotAllowed: defineErrorAdvanced(ERROR_CODES.DOMAIN_NOT_ALLOWED, 'Domain not allowed: {url}'),\n} as const\n\n/**\n * Request/Response errors (network, HTTP, parsing)\n */\nexport const RequestErrors = {\n // Network errors (connection failed, no response)\n NetworkError: defineError(ERROR_CODES.NETWORK_ERROR, 'Network error'),\n Cancelled: defineError(ERROR_CODES.REQUEST_CANCELLED, 'Request was cancelled'),\n\n // HTTP errors (server responded with error status)\n HttpError: defineErrorAdvanced(ERROR_CODES.HTTP_ERROR, 'HTTP {status} error'),\n\n // Response parsing errors\n ResponseParseFailed: defineError(ERROR_CODES.RESPONSE_PARSE_FAILED, 'Failed to parse response body'),\n\n // Queue errors\n QueueFull: defineErrorAdvanced(ERROR_CODES.QUEUE_FULL, 'Request queue full ({size}/{maxSize})'),\n\n // Timeout errors\n Timeout: defineError(ERROR_CODES.REQUEST_TIMEOUT, 'Request timed out'),\n} as const\n","/**\r\n * Error codes as constants for type-safe error matching\r\n *\r\n * Usage:\r\n * ```typescript\r\n * import { ERROR_CODES } from 'fetchguard'\r\n *\r\n * if (result.errors[0]?.code === ERROR_CODES.NETWORK_ERROR) {\r\n * // Handle network error\r\n * }\r\n * ```\r\n */\r\n\r\nexport const ERROR_CODES = {\r\n // General\r\n UNEXPECTED: 'UNEXPECTED',\r\n UNKNOWN_MESSAGE: 'UNKNOWN_MESSAGE',\r\n RESULT_PARSE_ERROR: 'RESULT_PARSE_ERROR',\r\n\r\n // Init\r\n INIT_ERROR: 'INIT_ERROR',\r\n PROVIDER_INIT_FAILED: 'PROVIDER_INIT_FAILED',\r\n INIT_FAILED: 'INIT_FAILED',\r\n\r\n // Auth\r\n TOKEN_REFRESH_FAILED: 'TOKEN_REFRESH_FAILED',\r\n TOKEN_EXCHANGE_FAILED: 'TOKEN_EXCHANGE_FAILED',\r\n LOGIN_FAILED: 'LOGIN_FAILED',\r\n LOGOUT_FAILED: 'LOGOUT_FAILED',\r\n NOT_AUTHENTICATED: 'NOT_AUTHENTICATED',\r\n\r\n // Domain\r\n DOMAIN_NOT_ALLOWED: 'DOMAIN_NOT_ALLOWED',\r\n\r\n // Request\r\n NETWORK_ERROR: 'NETWORK_ERROR',\r\n REQUEST_CANCELLED: 'REQUEST_CANCELLED',\r\n HTTP_ERROR: 'HTTP_ERROR',\r\n RESPONSE_PARSE_FAILED: 'RESPONSE_PARSE_FAILED',\r\n QUEUE_FULL: 'QUEUE_FULL',\r\n REQUEST_TIMEOUT: 'REQUEST_TIMEOUT'\r\n} as const\r\n\r\n/**\r\n * Union type of all error code values\r\n */\r\nexport type ErrorCode = typeof ERROR_CODES[keyof typeof ERROR_CODES]\r\n\r\n/**\r\n * Union type of all error code keys (useful for telemetry mapping)\r\n */\r\nexport type ErrorCodeKey = keyof typeof ERROR_CODES\r\n","import type { SerializedFormData, SerializedFormDataEntry, SerializedFile, SerializedFormDataResult } from '../types'\r\n\r\n/**\r\n * Serialize FormData for transfer over postMessage\r\n *\r\n * FormData cannot be cloned via postMessage, so we need to serialize it first.\r\n * Files are converted to ArrayBuffer and returned as transferables for zero-copy transfer.\r\n *\r\n * IMPORTANT: Preserves original field order by using single-pass iteration.\r\n *\r\n * @returns SerializedFormDataResult with data and transferables array\r\n */\r\nexport async function serializeFormData(formData: FormData): Promise<SerializedFormDataResult> {\r\n const entries: Array<[string, SerializedFormDataEntry]> = []\r\n const transferables: ArrayBuffer[] = []\r\n\r\n // Single-pass iteration to preserve original field order\r\n // Collect all entries with their index for order preservation\r\n const orderedEntries: Array<{ index: number; key: string; value: FormDataEntryValue }> = []\r\n let index = 0\r\n formData.forEach((value, key) => {\r\n orderedEntries.push({ index, key, value })\r\n index++\r\n })\r\n\r\n // Process all entries in order, handling files async\r\n await Promise.all(\r\n orderedEntries.map(async ({ index: idx, key, value }) => {\r\n if (value instanceof File) {\r\n const buffer = await value.arrayBuffer()\r\n const serializedFile: SerializedFile = {\r\n name: value.name,\r\n type: value.type,\r\n buffer\r\n }\r\n // Store with index for sorting later\r\n entries[idx] = [key, serializedFile]\r\n transferables.push(buffer)\r\n } else {\r\n entries[idx] = [key, String(value)]\r\n }\r\n })\r\n )\r\n\r\n return {\r\n data: {\r\n _type: 'FormData',\r\n entries\r\n },\r\n transferables\r\n }\r\n}\r\n\r\n/**\r\n * Deserialize SerializedFormData back to FormData in worker\r\n * Reconstructs File objects from transferred ArrayBuffers\r\n */\r\nexport function deserializeFormData(serialized: SerializedFormData): FormData {\r\n const formData = new FormData()\r\n\r\n for (const [key, value] of serialized.entries) {\r\n if (typeof value === 'string') {\r\n formData.append(key, value)\r\n } else {\r\n // Reconstruct File from SerializedFile (ArrayBuffer already transferred)\r\n const file = new File([value.buffer], value.name, { type: value.type })\r\n formData.append(key, file)\r\n }\r\n }\r\n\r\n return formData\r\n}\r\n\r\n/**\r\n * Check if body is FormData\r\n */\r\nexport function isFormData(body: unknown): body is FormData {\r\n return body instanceof FormData\r\n}\r\n\r\n/**\r\n * Check if serialized body is SerializedFormData\r\n */\r\nexport function isSerializedFormData(body: unknown): body is SerializedFormData {\r\n return body !== null && typeof body === 'object' && (body as SerializedFormData)._type === 'FormData'\r\n}\r\n","import type { TokenProvider } from '../types'\r\n\r\n/**\r\n * Registry to manage token providers\r\n */\r\nconst registry = new Map<string, TokenProvider>()\r\n\r\n/**\r\n * Register a token provider with name\r\n */\r\nexport function registerProvider(name: string, provider: TokenProvider): void {\r\n if (typeof name !== 'string' || !name.trim()) {\r\n throw new Error('Provider name must be a non-empty string')\r\n }\r\n \r\n if (!provider || typeof provider.refreshToken !== 'function') {\r\n throw new Error('Provider must implement TokenProvider interface')\r\n }\r\n \r\n registry.set(name, provider)\r\n}\r\n\r\n/**\r\n * Get provider by name\r\n */\r\nexport function getProvider(name: string): TokenProvider {\r\n const provider = registry.get(name)\r\n if (!provider) {\r\n throw new Error(`Provider '${name}' not found. Available providers: ${Array.from(registry.keys()).join(', ')}`)\r\n }\r\n return provider\r\n}\r\n\r\n/**\r\n * Check if provider exists\r\n */\r\nexport function hasProvider(name: string): boolean {\r\n return registry.has(name)\r\n}\r\n\r\n/**\r\n * Get list of all provider names\r\n */\r\nexport function listProviders(): string[] {\r\n return Array.from(registry.keys())\r\n}\r\n\r\n/**\r\n * Remove provider\r\n */\r\nexport function unregisterProvider(name: string): boolean {\r\n return registry.delete(name)\r\n}\r\n\r\n/**\r\n * Remove all providers\r\n */\r\nexport function clearProviders(): void {\r\n registry.clear()\r\n}\r\n","import type {\r\n TokenProvider,\r\n RefreshTokenStorage,\r\n TokenParser,\r\n AuthStrategy,\r\n TokenInfo,\r\n ExchangeTokenOptions\r\n} from '../types'\r\nimport { ok, err, type Result } from 'ts-micro-result'\r\nimport { AuthErrors, RequestErrors } from '../errors'\r\n\r\n/**\r\n * Custom auth method type\r\n */\r\ntype CustomAuthMethod = (...args: unknown[]) => Promise<Result<TokenInfo>>\r\n\r\n/**\r\n * Configuration for creating provider\r\n *\r\n * refreshStorage: OPTIONAL - to load refresh token initially when worker starts\r\n * - undefined: cookie-based auth (httpOnly cookie, no need to load)\r\n * - RefreshTokenStorage: body-based auth (load from IndexedDB on startup)\r\n *\r\n * strategy: AuthStrategy with refresh (required), login/logout (required)\r\n *\r\n * customMethods: OPTIONAL - custom auth methods (loginWithPhone, loginWithGoogle, etc.)\r\n */\r\nexport interface ProviderConfig {\r\n refreshStorage?: RefreshTokenStorage\r\n parser: TokenParser\r\n strategy: AuthStrategy\r\n customMethods?: Record<string, CustomAuthMethod>\r\n}\r\n\r\n/**\r\n * Factory function to create TokenProvider from modular components\r\n *\r\n * Provider automatically handles refresh token:\r\n * - If refreshToken is null and storage exists → load from storage initially\r\n * - If refreshToken exists → use token from worker memory\r\n * - Cookie-based (no storage) → always null\r\n *\r\n * Custom methods:\r\n * - User can add custom auth methods (loginWithPhone, loginWithGoogle, etc.)\r\n * - Custom methods will be spread into provider object\r\n */\r\nexport function createProvider(config: ProviderConfig): TokenProvider {\r\n const baseProvider: Pick<TokenProvider, 'refreshToken' | 'login' | 'logout' | 'exchangeToken'> = {\r\n async refreshToken(refreshToken: string | null) {\r\n let currentRefreshToken = refreshToken\r\n if (currentRefreshToken === null && config.refreshStorage) {\r\n currentRefreshToken = await config.refreshStorage.get()\r\n }\r\n\r\n try {\r\n const response = await config.strategy.refresh(currentRefreshToken)\r\n\r\n if (!response.ok) {\r\n // Read response body for error details\r\n const body = await response.text().catch(() => '')\r\n return err(AuthErrors.TokenRefreshFailed(), { params: { body, status: response.status } })\r\n }\r\n\r\n const tokenInfo = await config.parser.parse(response)\r\n if (!tokenInfo.token) {\r\n return err(AuthErrors.TokenRefreshFailed({ message: 'No access token in response' }))\r\n }\r\n\r\n if (config.refreshStorage && tokenInfo.refreshToken) {\r\n await config.refreshStorage.set(tokenInfo.refreshToken)\r\n }\r\n\r\n return ok(tokenInfo)\r\n } catch (error) {\r\n return err(RequestErrors.NetworkError({ message: String(error) }))\r\n }\r\n },\r\n\r\n async login(payload: unknown, url?: string) {\r\n try {\r\n const response = await config.strategy.login(payload, url)\r\n\r\n if (!response.ok) {\r\n // Read response body for error details\r\n const body = await response.text().catch(() => '')\r\n return err(AuthErrors.LoginFailed(), { params: { body, status: response.status } })\r\n }\r\n\r\n const tokenInfo = await config.parser.parse(response)\r\n if (!tokenInfo.token) {\r\n return err(AuthErrors.LoginFailed({ message: 'No access token in response' }))\r\n }\r\n\r\n if (config.refreshStorage && tokenInfo.refreshToken) {\r\n await config.refreshStorage.set(tokenInfo.refreshToken)\r\n }\r\n\r\n return ok(tokenInfo)\r\n } catch (error) {\r\n return err(RequestErrors.NetworkError({ message: String(error) }))\r\n }\r\n },\r\n\r\n async logout(payload?: unknown) {\r\n try {\r\n const response = await config.strategy.logout(payload)\r\n\r\n if (!response.ok) {\r\n // Read response body for error details\r\n const body = await response.text().catch(() => '')\r\n return err(AuthErrors.LogoutFailed(), { params: { body, status: response.status } })\r\n }\r\n\r\n if (config.refreshStorage) {\r\n await config.refreshStorage.set(null)\r\n }\r\n\r\n return ok({\r\n token: '',\r\n refreshToken: undefined,\r\n expiresAt: undefined,\r\n user: null // Explicitly clear user on logout\r\n })\r\n } catch (error) {\r\n return err(RequestErrors.NetworkError({ message: String(error) }))\r\n }\r\n },\r\n\r\n async exchangeToken(accessToken: string, url: string, options: ExchangeTokenOptions = {}) {\r\n if (!accessToken) {\r\n return err(AuthErrors.NotAuthenticated())\r\n }\r\n\r\n try {\r\n const response = await config.strategy.exchangeToken(accessToken, url, options)\r\n\r\n if (!response.ok) {\r\n const body = await response.text().catch(() => '')\r\n return err(AuthErrors.TokenExchangeFailed(), { params: { body, status: response.status } })\r\n }\r\n\r\n const tokenInfo = await config.parser.parse(response)\r\n if (!tokenInfo.token) {\r\n return err(AuthErrors.TokenExchangeFailed({ message: 'No access token in response' }))\r\n }\r\n\r\n if (config.refreshStorage && tokenInfo.refreshToken) {\r\n await config.refreshStorage.set(tokenInfo.refreshToken)\r\n }\r\n\r\n return ok(tokenInfo)\r\n } catch (error) {\r\n return err(RequestErrors.NetworkError({ message: String(error) }))\r\n }\r\n }\r\n }\r\n\r\n // Merge custom methods if provided\r\n if (config.customMethods) {\r\n return {\r\n ...baseProvider,\r\n ...config.customMethods\r\n } as TokenProvider\r\n }\r\n\r\n return baseProvider as TokenProvider\r\n}\r\n","import type { RefreshTokenStorage, StorageErrorCallback } from '../../types'\r\n\r\n/**\r\n * IndexedDB storage options\r\n */\r\nexport interface IndexedDBStorageOptions {\r\n /** Database name (default: 'FetchGuardDB') */\r\n dbName?: string\r\n /** Key for refresh token (default: 'refreshToken') */\r\n refreshTokenKey?: string\r\n /**\r\n * Error callback for debugging storage failures\r\n * Called when IndexedDB operations fail (quota exceeded, permission denied, etc.)\r\n * Storage still fails closed (returns null), but this allows logging/debugging.\r\n */\r\n onError?: StorageErrorCallback\r\n}\r\n\r\n/**\r\n * IndexedDB storage - only stores refresh token in IndexedDB\r\n * Suitable for body-based refresh strategy\r\n * Persists refresh token for reuse after reload\r\n *\r\n * @param options - Storage options or legacy dbName string\r\n * @param legacyRefreshTokenKey - Legacy refreshTokenKey (for backward compatibility)\r\n */\r\nexport function createIndexedDBStorage(\r\n options: IndexedDBStorageOptions | string = 'FetchGuardDB',\r\n legacyRefreshTokenKey?: string\r\n): RefreshTokenStorage {\r\n // Support both new options object and legacy string arguments\r\n const config: Required<Omit<IndexedDBStorageOptions, 'onError'>> & Pick<IndexedDBStorageOptions, 'onError'> =\r\n typeof options === 'string'\r\n ? { dbName: options, refreshTokenKey: legacyRefreshTokenKey ?? 'refreshToken', onError: undefined }\r\n : {\r\n dbName: options.dbName ?? 'FetchGuardDB',\r\n refreshTokenKey: options.refreshTokenKey ?? 'refreshToken',\r\n onError: options.onError\r\n }\r\n\r\n const { dbName, refreshTokenKey, onError } = config\r\n const storeName = 'tokens'\r\n\r\n const openDB = (): Promise<IDBDatabase> => {\r\n return new Promise((resolve, reject) => {\r\n const request = indexedDB.open(dbName, 1)\r\n\r\n request.onerror = () => reject(request.error)\r\n request.onsuccess = () => resolve(request.result)\r\n\r\n request.onupgradeneeded = (event) => {\r\n const db = (event.target as IDBOpenDBRequest).result\r\n if (!db.objectStoreNames.contains(storeName)) {\r\n const store = db.createObjectStore(storeName, { keyPath: 'key' })\r\n store.createIndex('timestamp', 'timestamp', { unique: false })\r\n }\r\n }\r\n })\r\n }\r\n\r\n const promisifyRequest = <T>(request: IDBRequest<T>): Promise<T> => {\r\n return new Promise((resolve, reject) => {\r\n request.onsuccess = () => resolve(request.result)\r\n request.onerror = () => reject(request.error)\r\n })\r\n }\r\n\r\n return {\r\n async get() {\r\n try {\r\n const db = await openDB()\r\n const transaction = db.transaction([storeName], 'readonly')\r\n const store = transaction.objectStore(storeName)\r\n const result = await promisifyRequest(store.get(refreshTokenKey))\r\n return result?.value || null\r\n } catch (error) {\r\n onError?.(error as Error, 'get')\r\n return null\r\n }\r\n },\r\n async set(token) {\r\n try {\r\n const db = await openDB()\r\n const transaction = db.transaction([storeName], 'readwrite')\r\n const store = transaction.objectStore(storeName)\r\n\r\n if (token) {\r\n await promisifyRequest(store.put({ key: refreshTokenKey, value: token, timestamp: Date.now() }))\r\n } else {\r\n await promisifyRequest(store.delete(refreshTokenKey))\r\n }\r\n } catch (error) {\r\n onError?.(error as Error, token ? 'set' : 'delete')\r\n }\r\n }\r\n }\r\n}\r\n","/**\r\n * Normalize expiresAt to milliseconds timestamp\r\n * Supports: milliseconds, seconds, ISO string, null/undefined\r\n */\r\nexport function normalizeExpiresAt(value: unknown): number | undefined {\r\n if (value == null) return undefined\r\n\r\n if (typeof value === 'number') {\r\n // Detect seconds vs milliseconds:\r\n // - Milliseconds: 13+ digits (e.g., 1767860146000)\r\n // - Seconds: 10 digits (e.g., 1767860146)\r\n // Threshold: 10^12 (Sep 2001 in ms, year 33658 in seconds)\r\n return value < 1e12 ? value * 1000 : value\r\n }\r\n\r\n if (typeof value === 'string') {\r\n const ts = Date.parse(value)\r\n return isNaN(ts) ? undefined : ts\r\n }\r\n\r\n return undefined\r\n}\r\n","import type { TokenParser } from '../../types'\r\nimport { normalizeExpiresAt } from './normalize'\r\n\r\n/**\r\n * Body parser - parse token from response body (JSON)\r\n * Expects response format: { data: { accessToken, refreshToken, expiresAt?, user? } }\r\n */\r\nexport const bodyParser: TokenParser = {\r\n async parse(response) {\r\n const json = await response.clone().json()\r\n return {\r\n token: json.data.accessToken,\r\n refreshToken: json.data.refreshToken,\r\n expiresAt: normalizeExpiresAt(json.data.expiresAt),\r\n user: json.data.user\r\n }\r\n }\r\n}\r\n","import type { TokenParser } from '../../types'\r\nimport { normalizeExpiresAt } from './normalize'\r\n\r\n/**\r\n * Cookie parser - parse access token from response body\r\n * Expects response format: { data: { accessToken, expiresAt?, user? } }\r\n * Refresh token is automatically set by backend into httpOnly cookie\r\n */\r\nexport const cookieParser: TokenParser = {\r\n async parse(response) {\r\n const json = await response.clone().json()\r\n return {\r\n token: json.data.accessToken,\r\n expiresAt: normalizeExpiresAt(json.data.expiresAt),\r\n user: json.data.user\r\n }\r\n }\r\n}\r\n","import type { AuthStrategy, ExchangeTokenOptions } from '../../types'\r\n\r\n/**\r\n * Cookie auth strategy - all auth operations via httpOnly cookies\r\n * Suitable for SSR and cross-domain authentication\r\n *\r\n * Refresh token is sent automatically via httpOnly cookie\r\n * Credentials are sent in request body\r\n *\r\n * Login URL can be:\r\n * - Configured once: loginUrl: 'https://api.example.com/auth/login'\r\n * - Passed per call: login(payload, 'https://...')\r\n *\r\n * Header priority (lowest to highest):\r\n * - defaultHeaders (from FetchGuardOptions)\r\n * - headers (from ProviderPresetConfig)\r\n * - Content-Type: application/json\r\n */\r\nexport function createCookieStrategy(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n headers?: Record<string, string>\r\n defaultHeaders?: Record<string, string>\r\n}): AuthStrategy {\r\n const baseHeaders = {\r\n ...config.defaultHeaders,\r\n ...config.headers,\r\n 'Content-Type': 'application/json'\r\n }\r\n\r\n return {\r\n async refresh() {\r\n return fetch(config.refreshUrl, {\r\n method: 'POST',\r\n headers: baseHeaders,\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async login(payload, url) {\r\n return fetch(url || config.loginUrl, {\r\n method: 'POST',\r\n headers: baseHeaders,\r\n body: JSON.stringify(payload),\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async logout(payload) {\r\n return fetch(config.logoutUrl, {\r\n method: 'POST',\r\n headers: baseHeaders,\r\n body: payload ? JSON.stringify(payload) : undefined,\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async exchangeToken(accessToken: string, url: string, options: ExchangeTokenOptions = {}) {\r\n const { method = 'POST', payload, headers } = options\r\n return fetch(url, {\r\n method,\r\n headers: {\r\n ...baseHeaders,\r\n ...headers,\r\n 'Authorization': `Bearer ${accessToken}`\r\n },\r\n body: payload ? JSON.stringify(payload) : undefined,\r\n credentials: 'include'\r\n })\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Standard cookie strategy\r\n */\r\nexport const cookieStrategy = createCookieStrategy({\r\n refreshUrl: '/auth/refresh',\r\n loginUrl: '/auth/login',\r\n logoutUrl: '/auth/logout'\r\n})\r\n","import type { AuthStrategy, ExchangeTokenOptions } from '../../types'\r\n\r\n/**\r\n * Body auth strategy - all auth operations via request body\r\n * Suitable for SPA applications\r\n *\r\n * All tokens/credentials are sent in request body\r\n *\r\n * Login URL can be:\r\n * - Configured once: loginUrl: 'https://api.example.com/auth/login'\r\n * - Passed per call: login(payload, 'https://...')\r\n *\r\n * Header priority (lowest to highest):\r\n * - defaultHeaders (from FetchGuardOptions)\r\n * - headers (from ProviderPresetConfig)\r\n * - Content-Type: application/json\r\n */\r\nexport function createBodyStrategy(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n headers?: Record<string, string>\r\n defaultHeaders?: Record<string, string>\r\n}): AuthStrategy {\r\n const baseHeaders = {\r\n ...config.defaultHeaders,\r\n ...config.headers,\r\n 'Content-Type': 'application/json'\r\n }\r\n\r\n return {\r\n async refresh(refreshToken) {\r\n if (!refreshToken) {\r\n throw new Error('No refresh token available')\r\n }\r\n\r\n return fetch(config.refreshUrl, {\r\n method: 'POST',\r\n headers: baseHeaders,\r\n body: JSON.stringify({ refreshToken }),\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async login(payload, url) {\r\n return fetch(url || config.loginUrl, {\r\n method: 'POST',\r\n headers: baseHeaders,\r\n body: JSON.stringify(payload),\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async logout(payload) {\r\n return fetch(config.logoutUrl, {\r\n method: 'POST',\r\n headers: baseHeaders,\r\n body: payload ? JSON.stringify(payload) : undefined,\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async exchangeToken(accessToken: string, url: string, options: ExchangeTokenOptions = {}) {\r\n const { method = 'POST', payload, headers } = options\r\n return fetch(url, {\r\n method,\r\n headers: {\r\n ...baseHeaders,\r\n ...headers,\r\n 'Authorization': `Bearer ${accessToken}`\r\n },\r\n body: payload ? JSON.stringify(payload) : undefined,\r\n credentials: 'include'\r\n })\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Standard body strategy\r\n */\r\nexport const bodyStrategy = createBodyStrategy({\r\n refreshUrl: '/auth/refresh',\r\n loginUrl: '/auth/login',\r\n logoutUrl: '/auth/logout'\r\n})\r\n","import { createProvider } from './create-provider'\r\nimport { createIndexedDBStorage } from './storage/indexeddb'\r\nimport { bodyParser } from './parser/body'\r\nimport { cookieParser } from './parser/cookie'\r\nimport { createCookieStrategy } from './strategy/cookie'\r\nimport { createBodyStrategy } from './strategy/body'\r\nimport type { TokenProvider } from '../types'\r\n\r\n/**\r\n * Cookie Provider - uses httpOnly cookies\r\n * Suitable for SSR and cross-domain authentication\r\n *\r\n * Access token: Worker memory\r\n * Refresh token: httpOnly cookie (managed by backend)\r\n */\r\nexport function createCookieProvider(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n headers?: Record<string, string>\r\n defaultHeaders?: Record<string, string>\r\n}): TokenProvider {\r\n return createProvider({\r\n refreshStorage: undefined,\r\n parser: cookieParser,\r\n strategy: createCookieStrategy(config)\r\n })\r\n}\r\n\r\n/**\r\n * Body Provider - refresh token in response body, persisted to IndexedDB\r\n * Suitable for SPA applications\r\n *\r\n * Access token: Worker memory\r\n * Refresh token: IndexedDB (persists across reload)\r\n */\r\nexport function createBodyProvider(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n refreshTokenKey?: string\r\n headers?: Record<string, string>\r\n defaultHeaders?: Record<string, string>\r\n}): TokenProvider {\r\n return createProvider({\r\n refreshStorage: createIndexedDBStorage('FetchGuardDB', config.refreshTokenKey || 'refreshToken'),\r\n parser: bodyParser,\r\n strategy: createBodyStrategy(config)\r\n })\r\n}\r\n","/**\r\n * Binary data utilities for Worker\r\n * Handles ArrayBuffer <-> Base64 conversion for binary responses\r\n */\r\n\r\n/**\r\n * Convert ArrayBuffer to base64 string\r\n * Used in worker to encode binary responses for postMessage transfer\r\n */\r\nexport function arrayBufferToBase64(buffer: ArrayBuffer): string {\r\n const bytes = new Uint8Array(buffer)\r\n let binary = ''\r\n for (let i = 0; i < bytes.length; i++) {\r\n binary += String.fromCharCode(bytes[i])\r\n }\r\n return btoa(binary)\r\n}\r\n\r\n/**\r\n * Convert base64 string to ArrayBuffer\r\n * Used in client to decode binary responses\r\n */\r\nexport function base64ToArrayBuffer(base64: string): ArrayBuffer {\r\n const binary = atob(base64)\r\n const bytes = new Uint8Array(binary.length)\r\n for (let i = 0; i < binary.length; i++) {\r\n bytes[i] = binary.charCodeAt(i)\r\n }\r\n return bytes.buffer\r\n}\r\n\r\n/**\r\n * Check if content type is binary (should be base64 encoded)\r\n * Returns true for images, PDFs, videos, etc.\r\n */\r\nexport function isBinaryContentType(contentType: string): boolean {\r\n const normalized = contentType.toLowerCase()\r\n\r\n // Text types - NOT binary\r\n if (normalized.includes('text/')) return false\r\n if (normalized.includes('json')) return false\r\n if (normalized.includes('xml')) return false\r\n if (normalized.includes('javascript')) return false\r\n if (normalized.includes('ecmascript')) return false\r\n if (normalized.includes('html')) return false\r\n\r\n // Everything else is considered binary\r\n return true\r\n}\r\n","/**\r\n * Helper functions for common Result patterns\r\n *\r\n * These utilities simplify working with FetchGuard's Result-based API\r\n * by providing type-safe helpers for common operations.\r\n */\r\n\r\nimport type { Result } from 'ts-micro-result'\r\nimport type { FetchEnvelope } from './types'\r\n\r\n/**\r\n * Check if result is a network/transport error (not an HTTP response)\r\n *\r\n * @example\r\n * const result = await api.fetch('/users')\r\n * if (isNetworkError(result)) {\r\n * console.error('Network failed:', result.errors[0]?.message)\r\n * }\r\n */\r\nexport function isNetworkError(result: Result<FetchEnvelope>): result is Result<FetchEnvelope> & { ok: false } {\r\n return !result.ok\r\n}\r\n\r\n/**\r\n * Check if response is successful (2xx status)\r\n *\r\n * @example\r\n * if (isSuccess(result)) {\r\n * const data = parseJson(result)\r\n * }\r\n */\r\nexport function isSuccess(result: Result<FetchEnvelope>): boolean {\r\n return result.ok && result.data.status >= 200 && result.data.status < 300\r\n}\r\n\r\n/**\r\n * Check if response is a client error (4xx status)\r\n *\r\n * @example\r\n * if (isClientError(result)) {\r\n * console.error('Bad request:', getErrorMessage(result))\r\n * }\r\n */\r\nexport function isClientError(result: Result<FetchEnvelope>): boolean {\r\n return result.ok && result.data.status >= 400 && result.data.status < 500\r\n}\r\n\r\n/**\r\n * Check if response is a server error (5xx status)\r\n *\r\n * @example\r\n * if (isServerError(result)) {\r\n * // Maybe retry?\r\n * }\r\n */\r\nexport function isServerError(result: Result<FetchEnvelope>): boolean {\r\n return result.ok && result.data.status >= 500\r\n}\r\n\r\n/**\r\n * Parse JSON body safely with optional type inference\r\n *\r\n * Returns null if:\r\n * - Result is a network error (no response)\r\n * - Body is not valid JSON\r\n *\r\n * @example\r\n * const users = parseJson<User[]>(result)\r\n * if (users) {\r\n * // Use users array\r\n * }\r\n */\r\nexport function parseJson<T = unknown>(result: Result<FetchEnvelope>): T | null {\r\n if (!result.ok) return null\r\n try {\r\n return JSON.parse(result.data.body) as T\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\n/**\r\n * Get human-readable error message from result\r\n *\r\n * For network errors: returns the error message\r\n * For HTTP errors: tries to parse message from body, falls back to status code\r\n *\r\n * @example\r\n * if (!isSuccess(result)) {\r\n * toast.error(getErrorMessage(result))\r\n * }\r\n */\r\nexport function getErrorMessage(result: Result<FetchEnvelope>): string {\r\n if (result.ok) {\r\n // HTTP error - try to parse message from body\r\n try {\r\n const body = JSON.parse(result.data.body)\r\n return body.message || body.error || `HTTP ${result.data.status}`\r\n } catch {\r\n return `HTTP ${result.data.status}`\r\n }\r\n }\r\n // Network error\r\n return result.errors[0]?.message || 'Unknown error'\r\n}\r\n\r\n/**\r\n * Get error body with type safety (best-effort parsing)\r\n *\r\n * NOTE: This is best-effort parsing. The error body comes from the server\r\n * and may not match the expected shape. Always handle null return and\r\n * validate before using typed properties.\r\n *\r\n * @example\r\n * interface ApiError {\r\n * code: string\r\n * message: string\r\n * errors?: { field: string; message: string }[]\r\n * }\r\n *\r\n * const errorBody = getErrorBody<ApiError>(result)\r\n * if (errorBody?.errors) {\r\n * errorBody.errors.forEach(e => console.error(`${e.field}: ${e.message}`))\r\n * }\r\n */\r\nexport function getErrorBody<T = unknown>(result: Result<FetchEnvelope>): T | null {\r\n if (!result.ok) return null\r\n // For HTTP errors (4xx, 5xx), try to parse the body\r\n if (result.data.status >= 400) {\r\n try {\r\n return JSON.parse(result.data.body) as T\r\n } catch {\r\n return null\r\n }\r\n }\r\n return null\r\n}\r\n\r\n/**\r\n * Get the HTTP status code from result\r\n *\r\n * Returns null if result is a network error (no HTTP response)\r\n *\r\n * @example\r\n * const status = getStatus(result)\r\n * if (status === 401) {\r\n * // Redirect to login\r\n * }\r\n */\r\nexport function getStatus(result: Result<FetchEnvelope>): number | null {\r\n return result.ok ? result.data.status : null\r\n}\r\n\r\n/**\r\n * Check if result has a specific HTTP status\r\n *\r\n * @example\r\n * if (hasStatus(result, 404)) {\r\n * console.log('Not found')\r\n * }\r\n */\r\nexport function hasStatus(result: Result<FetchEnvelope>, status: number): boolean {\r\n return result.ok && result.data.status === status\r\n}\r\n\r\n/**\r\n * Match result against multiple handlers\r\n *\r\n * @example\r\n * matchResult(result, {\r\n * success: (data) => console.log('Success:', data),\r\n * clientError: (data) => console.error('Client error:', data.status),\r\n * serverError: (data) => console.error('Server error:', data.status),\r\n * networkError: (errors) => console.error('Network:', errors[0]?.message)\r\n * })\r\n */\r\nexport function matchResult<T>(\r\n result: Result<FetchEnvelope>,\r\n handlers: {\r\n success?: (data: FetchEnvelope) => T\r\n clientError?: (data: FetchEnvelope) => T\r\n serverError?: (data: FetchEnvelope) => T\r\n networkError?: (errors: readonly { code: string; message: string }[]) => T\r\n }\r\n): T | undefined {\r\n if (!result.ok) {\r\n return handlers.networkError?.(result.errors)\r\n }\r\n\r\n const status = result.data.status\r\n\r\n if (status >= 200 && status < 300) {\r\n return handlers.success?.(result.data)\r\n }\r\n\r\n if (status >= 400 && status < 500) {\r\n return handlers.clientError?.(result.data)\r\n }\r\n\r\n if (status >= 500) {\r\n return handlers.serverError?.(result.data)\r\n }\r\n\r\n return undefined\r\n}\r\n"],"mappings":";AAcA,SAAS,IAAI,WAAwB;;;AC+D9B,IAAM,MAAM,OAAO,OAAO;AAAA;AAAA,EAE/B,OAAO;AAAA,EACP,OAAO;AAAA,EACP,WAAW;AAAA,EACX,QAAQ;AAAA,EACR,MAAM;AAAA;AAAA,EAGN,OAAO;AAAA,EACP,OAAO;AAAA,EACP,aAAa;AAAA,EACb,MAAM;AAAA,EACN,KAAK;AAAA,EACL,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,aAAa;AAAA,EACb,iBAAiB;AACnB,CAAC;;;ACxFM,IAAM,2BAA2B;;;ACHxC,SAAS,aAAa,2BAA2B;;;ACQ1C,IAAM,cAAc;AAAA;AAAA,EAEzB,YAAY;AAAA,EACZ,iBAAiB;AAAA,EACjB,oBAAoB;AAAA;AAAA,EAGpB,YAAY;AAAA,EACZ,sBAAsB;AAAA,EACtB,aAAa;AAAA;AAAA,EAGb,sBAAsB;AAAA,EACtB,uBAAuB;AAAA,EACvB,cAAc;AAAA,EACd,eAAe;AAAA,EACf,mBAAmB;AAAA;AAAA,EAGnB,oBAAoB;AAAA;AAAA,EAGpB,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,YAAY;AAAA,EACZ,uBAAuB;AAAA,EACvB,YAAY;AAAA,EACZ,iBAAiB;AACnB;;;AD9BO,IAAM,gBAAgB;AAAA,EAC3B,YAAY,YAAY,YAAY,YAAY,kBAAkB;AAAA,EAClE,gBAAgB,YAAY,YAAY,iBAAiB,sBAAsB;AAAA,EAC/E,aAAa,YAAY,YAAY,oBAAoB,wBAAwB;AACnF;AAKO,IAAM,aAAa;AAAA,EACxB,gBAAgB,YAAY,YAAY,YAAY,wBAAwB;AAAA,EAC5E,oBAAoB,YAAY,YAAY,sBAAsB,+BAA+B;AAAA,EACjG,YAAY,YAAY,YAAY,aAAa,uBAAuB;AAC1E;AAKO,IAAM,aAAa;AAAA,EACxB,oBAAoB,YAAY,YAAY,sBAAsB,sBAAsB;AAAA,EACxF,qBAAqB,YAAY,YAAY,uBAAuB,uBAAuB;AAAA,EAC3F,aAAa,YAAY,YAAY,cAAc,cAAc;AAAA,EACjE,cAAc,YAAY,YAAY,eAAe,eAAe;AAAA,EACpE,kBAAkB,YAAY,YAAY,mBAAmB,2BAA2B;AAC1F;AAKO,IAAM,eAAe;AAAA,EAC1B,YAAY,oBAAoB,YAAY,oBAAoB,2BAA2B;AAC7F;AAKO,IAAM,gBAAgB;AAAA;AAAA,EAE3B,cAAc,YAAY,YAAY,eAAe,eAAe;AAAA,EACpE,WAAW,YAAY,YAAY,mBAAmB,uBAAuB;AAAA;AAAA,EAG7E,WAAW,oBAAoB,YAAY,YAAY,qBAAqB;AAAA;AAAA,EAG5E,qBAAqB,YAAY,YAAY,uBAAuB,+BAA+B;AAAA;AAAA,EAGnG,WAAW,oBAAoB,YAAY,YAAY,uCAAuC;AAAA;AAAA,EAG9F,SAAS,YAAY,YAAY,iBAAiB,mBAAmB;AACvE;;;AEnDA,eAAsB,kBAAkB,UAAuD;AAC7F,QAAM,UAAoD,CAAC;AAC3D,QAAM,gBAA+B,CAAC;AAItC,QAAM,iBAAmF,CAAC;AAC1F,MAAI,QAAQ;AACZ,WAAS,QAAQ,CAAC,OAAO,QAAQ;AAC/B,mBAAe,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC;AACzC;AAAA,EACF,CAAC;AAGD,QAAM,QAAQ;AAAA,IACZ,eAAe,IAAI,OAAO,EAAE,OAAO,KAAK,KAAK,MAAM,MAAM;AACvD,UAAI,iBAAiB,MAAM;AACzB,cAAM,SAAS,MAAM,MAAM,YAAY;AACvC,cAAM,iBAAiC;AAAA,UACrC,MAAM,MAAM;AAAA,UACZ,MAAM,MAAM;AAAA,UACZ;AAAA,QACF;AAEA,gBAAQ,GAAG,IAAI,CAAC,KAAK,cAAc;AACnC,sBAAc,KAAK,MAAM;AAAA,MAC3B,OAAO;AACL,gBAAQ,GAAG,IAAI,CAAC,KAAK,OAAO,KAAK,CAAC;AAAA,MACpC;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,MACJ,OAAO;AAAA,MACP;AAAA,IACF;AAAA,IACA;AAAA,EACF;AACF;AAMO,SAAS,oBAAoB,YAA0C;AAC5E,QAAM,WAAW,IAAI,SAAS;AAE9B,aAAW,CAAC,KAAK,KAAK,KAAK,WAAW,SAAS;AAC7C,QAAI,OAAO,UAAU,UAAU;AAC7B,eAAS,OAAO,KAAK,KAAK;AAAA,IAC5B,OAAO;AAEL,YAAM,OAAO,IAAI,KAAK,CAAC,MAAM,MAAM,GAAG,MAAM,MAAM,EAAE,MAAM,MAAM,KAAK,CAAC;AACtE,eAAS,OAAO,KAAK,IAAI;AAAA,IAC3B;AAAA,EACF;AAEA,SAAO;AACT;AAKO,SAAS,WAAW,MAAiC;AAC1D,SAAO,gBAAgB;AACzB;AAKO,SAAS,qBAAqB,MAA2C;AAC9E,SAAO,SAAS,QAAQ,OAAO,SAAS,YAAa,KAA4B,UAAU;AAC7F;;;ALzCA,IAAM,yBAAyB;AAG/B,IAAM,yBAAyB;AAG/B,IAAM,wBAAwB;AAG9B,IAAM,0BAA0B;AAKzB,IAAM,mBAAN,MAAuB;AAAA,EACpB;AAAA,EACA,YAAY;AAAA;AAAA;AAAA,EAGZ,kBAAkB,oBAAI,IAG3B;AAAA;AAAA,EAEK,cAAc,oBAAI,IAAoB;AAAA;AAAA,EAEtC,iBAAiB,oBAAI,IAA2B;AAAA,EAChD,gBAAgB,oBAAI,IAAiC;AAAA,EACrD,iBAAiB,oBAAI,IAAgB;AAAA,EACrC,UAAU;AAAA,EAEV,eAA4B,CAAC;AAAA,EAC7B,iBAAiB;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT;AAAA,EACA;AAAA,EACS;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAEA,mBAAmB,oBAAI,IAA4C;AAAA;AAAA,EAEnE,gBAAgB,oBAAI,IAAkE;AAAA,EAEvG,YAAY,SAA4B;AACtC,SAAK,gBAAgB,QAAQ,iBAAiB;AAC9C,SAAK,eAAe,QAAQ,gBAAgB;AAC5C,SAAK,eAAe,QAAQ,gBAAgB;AAC5C,SAAK,iBAAiB,QAAQ,kBAAkB;AAChD,SAAK,QAAQ,QAAQ;AACrB,SAAK,QAAQ,QAAQ;AACrB,SAAK,SAAS,QAAQ;AACtB,SAAK,SAAS,IAAI,OAAO,IAAI,IAAI,eAAe,YAAY,GAAG,GAAG;AAAA,MAChE,MAAM;AAAA,IACR,CAAC;AAED,SAAK,OAAO,YAAY,KAAK,oBAAoB,KAAK,IAAI;AAC1D,SAAK,OAAO,UAAU,KAAK,kBAAkB,KAAK,IAAI;AAEtD,SAAK,iBAAiB,OAAO;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,iBAAiB,SAA2C;AACxE,UAAM,SAAuB;AAAA,MAC3B,gBAAgB,QAAQ,kBAAkB,CAAC;AAAA,MAC3C,gBAAgB,QAAQ,kBAAkB;AAAA,MAC1C,gBAAgB,QAAQ,kBAAkB,CAAC;AAAA,IAC7C;AAGA,QAAI,iBAAuD;AAE3D,QAAI,OAAO,QAAQ,aAAa,UAAU;AAExC,uBAAiB,QAAQ;AAAA,IAC3B,WAAW,UAAU,QAAQ,YAAY,QAAQ,SAAS,MAAM;AAE9D,uBAAiB,QAAQ;AAAA,IAC3B,OAAO;AAEL,YAAM,IAAI;AAAA,QACR;AAAA,MAGF;AAAA,IACF;AAEA,UAAM,UAAU;AAAA,MACd,IAAI,KAAK,kBAAkB;AAAA,MAC3B,MAAM,IAAI;AAAA,MACV,SAAS;AAAA,QACP;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AAEtC,WAAK,eAAe;AACpB,WAAK,cAAc;AAEnB,WAAK,OAAO,YAAY,OAAO;AAE/B,iBAAW,MAAM;AACf,YAAI,KAAK,aAAa;AACpB,eAAK,YAAY,IAAI,MAAM,sBAAsB,CAAC;AAClD,eAAK,eAAe;AACpB,eAAK,cAAc;AAAA,QACrB;AAAA,MACF,GAAG,KAAK,YAAY;AAAA,IACtB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAMQ,oBAAoB,OAA2B;AACrD,UAAM,EAAE,IAAI,MAAM,QAAQ,IAAI,MAAM;AAEpC,QAAI,SAAS,IAAI,cAAc;AAG7B,YAAM,UAAU,KAAK,gBAAgB,IAAI,EAAE;AAC3C,UAAI,CAAC,QAAS;AAEd,YAAM,MAAM,KAAK,YAAY,IAAI,EAAE;AACnC,YAAM,SAAS,KAAK,eAAe,IAAI,EAAE;AACzC,WAAK,gBAAgB,OAAO,EAAE;AAC9B,WAAK,YAAY,OAAO,EAAE;AAC1B,WAAK,eAAe,OAAO,EAAE;AAC7B,WAAK,kBAAkB;AAGvB,YAAM,UAAU,KAAK,iBAAiB,MAAM;AAG5C,UAAI,KAAK,OAAO,cAAc,KAAK;AACjC,aAAK,MAAM,WAAW,KAAK,SAA0B,OAAO;AAAA,MAC9D;AAEA,cAAQ,QAAQ,GAAG,OAAwB,CAAC;AAC5C;AAAA,IACF;AAEA,QAAI,SAAS,IAAI,aAAa;AAE5B,YAAM,UAAU,KAAK,gBAAgB,IAAI,EAAE;AAC3C,UAAI,CAAC,QAAS;AAEd,YAAM,MAAM,KAAK,YAAY,IAAI,EAAE;AACnC,YAAM,SAAS,KAAK,eAAe,IAAI,EAAE;AACzC,WAAK,gBAAgB,OAAO,EAAE;AAC9B,WAAK,YAAY,OAAO,EAAE;AAC1B,WAAK,eAAe,OAAO,EAAE;AAC7B,WAAK,kBAAkB;AAEvB,YAAM,eAAe,OAAO,SAAS,SAAS,eAAe;AAG7D,YAAM,UAAU,KAAK,iBAAiB,MAAM;AAG5C,UAAI,KAAK,OAAO,WAAW,KAAK;AAC9B,aAAK,MAAM,QAAQ,KAAK,EAAE,MAAM,iBAAiB,SAAS,aAAa,GAAG,OAAO;AAAA,MACnF;AAEA,cAAQ,QAAQ;AAAA,QACd,cAAc,aAAa,EAAE,SAAS,aAAa,CAAC;AAAA,MACtD,CAAC;AACD;AAAA,IACF;AAEA,QAAI,SAAS,IAAI,OAAO;AACtB,YAAM,UAAU,KAAK,gBAAgB,IAAI,EAAE;AAC3C,UAAI,CAAC,QAAS;AAEd,WAAK,gBAAgB,OAAO,EAAE;AAC9B,WAAK,kBAAkB;AAEvB,cAAQ,QAAQ,IAAI,QAAQ,QAAQ,QAAQ,IAAI,CAAC;AACjD;AAAA,IACF;AAEA,QAAI,SAAS,IAAI,aAAa;AAE5B,UAAI,KAAK,aAAa;AACpB,aAAK,YAAY,IAAI,MAAM,wBAAwB,SAAS,SAAS,eAAe,EAAE,CAAC;AACvF,aAAK,eAAe;AACpB,aAAK,cAAc;AAAA,MACrB;AACA;AAAA,IACF;AAEA,QAAI,SAAS,IAAI,OAAO;AACtB,WAAK,UAAU;AAGf,WAAK,OAAO,gBAAgB;AAG5B,iBAAW,YAAY,KAAK,gBAAgB;AAC1C,iBAAS;AAAA,MACX;AAEA,UAAI,KAAK,cAAc;AACrB,aAAK,aAAa;AAClB,aAAK,eAAe;AACpB,aAAK,cAAc;AAAA,MACrB;AACA;AAAA,IACF;AAEA,QAAI,SAAS,IAAI,MAAM;AACrB,YAAM,UAAU,KAAK,gBAAgB,IAAI,EAAE;AAC3C,UAAI,SAAS;AACX,aAAK,gBAAgB,OAAO,EAAE;AAC9B,aAAK,kBAAkB;AACvB,gBAAQ,QAAQ,GAAG,EAAE,WAAW,SAAS,UAAU,CAAC,CAAC;AAAA,MACvD;AACA;AAAA,IACF;AAEA,QAAI,SAAS,IAAI,oBAAoB;AACnC,iBAAW,MAAM,KAAK,cAAe,IAAG,OAAO;AAC/C;AAAA,IACF;AAEA,QAAI,SAAS,IAAI,kBAAkB;AACjC,YAAM,UAAU,KAAK,gBAAgB,IAAI,EAAE;AAC3C,UAAI,SAAS;AACX,aAAK,gBAAgB,OAAO,EAAE;AAC9B,aAAK,kBAAkB;AACvB,gBAAQ,QAAQ,GAAG,OAAO,CAAC;AAAA,MAC7B;AACA;AAAA,IACF;AAEA,QAAI,SAAS,IAAI,iBAAiB;AAEhC,WAAK,OAAO,YAAY,SAAS,MAAM;AACvC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,OAAyB;AACjD,YAAQ,MAAM,iBAAiB,KAAK;AAGpC,SAAK,OAAO,gBAAgB,KAAK;AAGjC,eAAW,CAAC,IAAI,OAAO,KAAK,KAAK,iBAAiB;AAChD,cAAQ,OAAO,IAAI,MAAM,iBAAiB,MAAM,OAAO,EAAE,CAAC;AAAA,IAC5D;AACA,SAAK,gBAAgB,MAAM;AAC3B,SAAK,YAAY,MAAM;AACvB,SAAK,eAAe,MAAM;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAA4B;AAClC,WAAO,OAAO,EAAE,KAAK,SAAS,IAAI,KAAK,IAAI,CAAC;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,MAAM,KAAa,UAAiC,CAAC,GAAmC;AAE5F,UAAM,EAAE,QAAQ,GAAG,YAAY,IAAI;AAGnC,QAAI,QAAQ,SAAS;AACnB,aAAO,IAAI,cAAc,UAAU,CAAC;AAAA,IACtC;AAGA,UAAM,YAAY,KAAK,aAAa,KAAK,WAAW;AACpD,QAAI,WAAW;AAEb,YAAM,WAAW,KAAK,iBAAiB,IAAI,SAAS;AACpD,UAAI,UAAU;AAEZ,YAAI,QAAQ;AACV,iBAAO,KAAK,oBAAoB,UAAU,QAAQ,IAAI;AAAA,QACxD;AACA,eAAO;AAAA,MACT;AAGA,YAAM,SAAS,KAAK,QAAQ,UAAU;AACtC,UAAI,SAAS,GAAG;AACd,cAAM,SAAS,KAAK,cAAc,IAAI,SAAS;AAC/C,YAAI,UAAU,KAAK,IAAI,IAAI,OAAO,YAAY,QAAQ;AACpD,iBAAO,OAAO;AAAA,QAChB;AAAA,MACF;AAGA,YAAM,UAAU,KAAK,wBAAwB,KAAK,aAAa,UAAU,MAAS;AAClF,WAAK,iBAAiB,IAAI,WAAW,OAAO;AAE5C,UAAI;AACF,cAAM,SAAS,MAAM;AAErB,YAAI,SAAS,GAAG;AACd,eAAK,cAAc,IAAI,WAAW,EAAE,QAAQ,WAAW,KAAK,IAAI,EAAE,CAAC;AAEnE,qBAAW,MAAM,KAAK,cAAc,OAAO,SAAS,GAAG,MAAM;AAAA,QAC/D;AACA,eAAO;AAAA,MACT,UAAE;AACA,aAAK,iBAAiB,OAAO,SAAS;AAAA,MACxC;AAAA,IACF;AAGA,WAAO,KAAK,wBAAwB,KAAK,aAAa,UAAU,MAAS;AAAA,EAC3E;AAAA;AAAA;AAAA;AAAA,EAKQ,oBACN,SACA,QACA,WACgC;AAChC,WAAO,IAAI,QAAQ,CAAC,YAAY;AAE9B,YAAM,eAAe,MAAM;AACzB,YAAI,WAAW;AACb,eAAK,OAAO,SAAS;AAAA,QACvB;AACA,gBAAQ,IAAI,cAAc,UAAU,CAAC,CAAC;AAAA,MACxC;AAEA,UAAI,OAAO,SAAS;AAClB,qBAAa;AACb;AAAA,MACF;AAEA,aAAO,iBAAiB,SAAS,cAAc,EAAE,MAAM,KAAK,CAAC;AAE7D,cAAQ,KAAK,CAAC,WAAW;AACvB,eAAO,oBAAoB,SAAS,YAAY;AAChD,gBAAQ,MAAM;AAAA,MAChB,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,wBACZ,KACA,SACA,QACgC;AAChC,UAAM,cAAc,KAAK,OAAO,eAAe;AAC/C,UAAM,QAAQ,KAAK,OAAO,SAAS;AACnC,UAAM,UAAU,KAAK,OAAO,WAAW;AACvC,UAAM,WAAW,KAAK,OAAO,YAAY;AACzC,UAAM,SAAS,KAAK,OAAO,UAAU;AACrC,UAAM,cAAc,KAAK,OAAO,eAAe,KAAK;AAEpD,QAAI,aAA2C;AAC/C,QAAI,eAAe;AAGnB,aAAS,UAAU,GAAG,WAAW,aAAa,WAAW;AAEvD,UAAI,QAAQ,SAAS;AACnB,eAAO,IAAI,cAAc,UAAU,CAAC;AAAA,MACtC;AAEA,YAAM,EAAE,IAAI,OAAO,IAAI,KAAK,YAAY,KAAK,OAAO;AAGpD,UAAI,QAAQ;AACV,qBAAa,MAAM,KAAK,oBAAoB,QAAQ,QAAQ,EAAE;AAAA,MAChE,OAAO;AACL,qBAAa,MAAM;AAAA,MACrB;AAGA,UAAI,WAAW,IAAI;AACjB,eAAO;AAAA,MACT;AAGA,UAAI,WAAW,OAAO,CAAC,GAAG,SAAS,qBAAqB;AACtD,eAAO;AAAA,MACT;AAGA,YAAM,QAAQ,WAAW,OAAO,CAAC;AACjC,YAAM,cAAkC;AAAA,QACtC,MAAM,OAAO,QAAsC;AAAA,QACnD,SAAS,OAAO,WAAW;AAAA,MAC7B;AAKA,UAAI,WAAW,eAAe,CAAC,YAAY,WAAW,GAAG;AACvD,eAAO;AAAA,MACT;AAIA,YAAM,cAAc,KAAK,IAAI,cAAc,QAAQ;AACnD,YAAM,gBAAgB,KAAK,YAAY,aAAa,MAAM;AAG1D,UAAI,QAAQ;AACV,cAAM,UAAU,MAAM,KAAK,eAAe,eAAe,MAAM;AAC/D,YAAI,SAAS;AACX,iBAAO,IAAI,cAAc,UAAU,CAAC;AAAA,QACtC;AAAA,MACF,OAAO;AACL,cAAM,KAAK,MAAM,aAAa;AAAA,MAChC;AAEA,qBAAe,eAAe;AAAA,IAChC;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,aAAa,KAAa,SAA+C;AAC/E,QAAI,CAAC,KAAK,QAAQ,SAAS;AACzB,aAAO;AAAA,IACT;AAGA,QAAI,KAAK,OAAO,cAAc;AAC5B,aAAO,KAAK,OAAO,aAAa,KAAK,OAAO;AAAA,IAC9C;AAGA,UAAM,UAAU,QAAQ,UAAU,OAAO,YAAY;AACrD,QAAI,WAAW,OAAO;AACpB,aAAO;AAAA,IACT;AAEA,WAAO,OAAO,GAAG;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,YAAY,OAAe,QAAwB;AACzD,QAAI,UAAU,EAAG,QAAO;AAExB,UAAM,gBAAgB,KAAK,IAAI,KAAK,IAAI,QAAQ,CAAC,GAAG,CAAC;AAErD,UAAM,eAAgB,KAAK,OAAO,IAAI,IAAK;AAE3C,WAAO,KAAK,IAAI,GAAG,QAAS,QAAQ,gBAAgB,YAAa;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAmB,OAAoC;AAE7D,WAAO,MAAM,SAAS;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB,QAAoD;AAC3E,QAAI,CAAC,OAAQ,QAAO;AAEpB,UAAM,UAAU,KAAK,IAAI;AACzB,UAAM,YAAY,OAAO;AACzB,UAAM,SAAS,OAAO,UAAU;AAChC,UAAM,WAAW,UAAU;AAC3B,UAAM,YAAY,SAAS;AAC3B,UAAM,UAAU,WAAW;AAE3B,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,MAAM,IAA2B;AACvC,WAAO,IAAI,QAAQ,aAAW,WAAW,SAAS,EAAE,CAAC;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,eAAe,IAAY,QAAuC;AACxE,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAI,OAAO,SAAS;AAClB,gBAAQ,IAAI;AACZ;AAAA,MACF;AAEA,YAAM,QAAQ,WAAW,MAAM;AAC7B,eAAO,oBAAoB,SAAS,YAAY;AAChD,gBAAQ,KAAK;AAAA,MACf,GAAG,EAAE;AAEL,YAAM,eAAe,MAAM;AACzB,qBAAa,KAAK;AAClB,gBAAQ,IAAI;AAAA,MACd;AAEA,aAAO,iBAAiB,SAAS,cAAc,EAAE,MAAM,KAAK,CAAC;AAAA,IAC/D,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,YAAY,KAAa,UAAiC,CAAC,GAIzD;AACA,UAAM,KAAK,KAAK,kBAAkB;AAGlC,UAAM,SAAS,IAAI,QAA+B,OAAO,SAAS,WAAW;AAC3E,WAAK,gBAAgB,IAAI,IAAI;AAAA,QAC3B,SAAS,CAAC,aAAa,QAAQ,QAAiC;AAAA,QAChE,QAAQ,CAAC,UAAU,OAAO,KAAK;AAAA,MACjC,CAAC;AAED,WAAK,YAAY,IAAI,IAAI,GAAG;AAE5B,WAAK,eAAe,IAAI,IAAI,EAAE,WAAW,KAAK,IAAI,EAAE,CAAC;AAGrD,WAAK,OAAO,YAAY,KAAK,OAAO;AAEpC,UAAI;AACF,YAAI,oBAAoB,EAAE,GAAG,QAAQ;AACrC,YAAI;AAGJ,YAAI,QAAQ,QAAQ,WAAW,QAAQ,IAAI,GAAG;AAC5C,gBAAM,EAAE,MAAM,eAAe,sBAAsB,IAAI,MAAM,kBAAkB,QAAQ,IAAI;AAE3F,4BAAkB,OAAO;AAEzB,cAAI,sBAAsB,SAAS,GAAG;AACpC,4BAAgB;AAAA,UAClB;AAAA,QACF;AAGA,YAAI,QAAQ,SAAS;AACnB,cAAI,QAAQ,mBAAmB,SAAS;AACtC,kBAAM,eAAuC,CAAC;AAC9C,oBAAQ,QAAQ,QAAQ,CAAC,OAAO,QAAQ;AACtC,2BAAa,GAAG,IAAI;AAAA,YACtB,CAAC;AACD,8BAAkB,UAAU;AAAA,UAC9B;AAAA,QACF;AAEA,cAAM,UAAU,EAAE,IAAI,MAAM,IAAI,OAAO,SAAS,EAAE,KAAK,SAAS,kBAAkB,EAAE;AAEpF,cAAM,KAAK,kBAAkB,SAAS,KAAO,aAAa;AAAA,MAC5D,SAAS,OAAO;AACd,cAAM,UAAU,KAAK,gBAAgB,IAAI,EAAE;AAC3C,YAAI,SAAS;AACX,eAAK,gBAAgB,OAAO,EAAE;AAC9B,eAAK,YAAY,OAAO,EAAE;AAC1B,eAAK,eAAe,OAAO,EAAE;AAC7B,kBAAQ,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,QAC1E;AAAA,MACF;AAAA,IACF,CAAC;AAED,UAAM,SAAS,MAAM,KAAK,OAAO,EAAE;AAEnC,WAAO,EAAE,IAAI,QAAQ,OAAO;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,IAAkB;AACvB,UAAM,UAAU,KAAK,gBAAgB,IAAI,EAAE;AAC3C,QAAI,SAAS;AACX,YAAM,MAAM,KAAK,YAAY,IAAI,EAAE;AACnC,YAAM,SAAS,KAAK,eAAe,IAAI,EAAE;AACzC,WAAK,gBAAgB,OAAO,EAAE;AAC9B,WAAK,YAAY,OAAO,EAAE;AAC1B,WAAK,eAAe,OAAO,EAAE;AAC7B,WAAK,OAAO,YAAY,EAAE,IAAI,MAAM,IAAI,OAAO,CAAC;AAGhD,YAAM,UAAU,KAAK,iBAAiB,MAAM;AAG5C,UAAI,KAAK,OAAO,WAAW,KAAK;AAC9B,aAAK,MAAM,QAAQ,KAAK,EAAE,MAAM,qBAAqB,SAAS,oBAAoB,GAAG,OAAO;AAAA,MAC9F;AAEA,cAAQ,OAAO,IAAI,MAAM,mBAAmB,CAAC;AAAA,IAC/C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,IAAI,KAAa,UAA0D,CAAC,GAAmC;AACnH,WAAO,KAAK,MAAM,KAAK,EAAE,GAAG,SAAS,QAAQ,MAAM,CAAC;AAAA,EACtD;AAAA,EAEA,MAAM,KAAK,KAAa,MAAgB,UAA0D,CAAC,GAAmC;AAEpI,QAAI,QAAQ,WAAW,IAAI,GAAG;AAC5B,aAAO,KAAK,MAAM,KAAK;AAAA,QACrB,GAAG;AAAA,QACH,QAAQ;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH;AAGA,UAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;AAG3C,QAAI,QAAQ,CAAC,QAAQ,IAAI,cAAc,GAAG;AACxC,cAAQ,IAAI,gBAAgB,kBAAkB;AAAA,IAChD;AAEA,WAAO,KAAK,MAAM,KAAK;AAAA,MACrB,GAAG;AAAA,MACH;AAAA,MACA,QAAQ;AAAA,MACR,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,IACtC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,IAAI,KAAa,MAAgB,UAA0D,CAAC,GAAmC;AAEnI,QAAI,QAAQ,WAAW,IAAI,GAAG;AAC5B,aAAO,KAAK,MAAM,KAAK;AAAA,QACrB,GAAG;AAAA,QACH,QAAQ;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH;AAGA,UAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;AAG3C,QAAI,QAAQ,CAAC,QAAQ,IAAI,cAAc,GAAG;AACxC,cAAQ,IAAI,gBAAgB,kBAAkB;AAAA,IAChD;AAEA,WAAO,KAAK,MAAM,KAAK;AAAA,MACrB,GAAG;AAAA,MACH;AAAA,MACA,QAAQ;AAAA,MACR,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,IACtC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,OAAO,KAAa,UAA0D,CAAC,GAAmC;AACtH,WAAO,KAAK,MAAM,KAAK,EAAE,GAAG,SAAS,QAAQ,SAAS,CAAC;AAAA,EACzD;AAAA,EAEA,MAAM,MAAM,KAAa,MAAgB,UAA0D,CAAC,GAAmC;AAErI,QAAI,QAAQ,WAAW,IAAI,GAAG;AAC5B,aAAO,KAAK,MAAM,KAAK;AAAA,QACrB,GAAG;AAAA,QACH,QAAQ;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH;AAGA,UAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;AAG3C,QAAI,QAAQ,CAAC,QAAQ,IAAI,cAAc,GAAG;AACxC,cAAQ,IAAI,gBAAgB,kBAAkB;AAAA,IAChD;AAEA,WAAO,KAAK,MAAM,KAAK;AAAA,MACrB,GAAG;AAAA,MACH;AAAA,MACA,QAAQ;AAAA,MACR,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,IACtC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,KAAK,QAAgB,cAAwB,MAA8C;AAC/F,UAAM,KAAK,KAAK,kBAAkB;AAClC,UAAM,UAAU,EAAE,IAAI,MAAM,IAAI,WAAW,SAAS,EAAE,QAAQ,MAAM,UAAU,EAAE;AAEhF,WAAO,IAAI,QAA4B,CAAC,SAAS,WAAW;AAC1D,WAAK,gBAAgB,IAAI,IAAI;AAAA,QAC3B,SAAS,CAAC,MAAM,QAAQ,CAAuB;AAAA,QAC/C,QAAQ,CAAC,MAAa,OAAO,CAAC;AAAA,MAChC,CAAC;AAED,WAAK,kBAAkB,SAAS,IAAK,EAAE,MAAM,CAAC,UAAU;AACtD,cAAM,UAAU,KAAK,gBAAgB,IAAI,EAAE;AAC3C,YAAI,SAAS;AACX,eAAK,gBAAgB,OAAO,EAAE;AAC9B,kBAAQ,OAAO,KAAK;AAAA,QACtB;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,MAAM,SAAmB,KAAc,YAAqB,MAAmC;AACnG,UAAM,OAAkB,CAAC;AACzB,QAAI,OAAO,YAAY,aAAa;AAClC,WAAK,KAAK,OAAO;AAAA,IACnB;AACA,QAAI,OAAO,QAAQ,aAAa;AAE9B,UAAI,KAAK,WAAW,GAAG;AACrB,aAAK,KAAK,MAAS;AAAA,MACrB;AACA,WAAK,KAAK,GAAG;AAAA,IACf;AACA,WAAO,KAAK,KAAK,SAAS,WAAW,GAAG,IAAI;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,OAAO,SAAmB,YAAqB,MAAmC;AACtF,UAAM,OAAO,OAAO,YAAY,cAAc,CAAC,IAAI,CAAC,OAAO;AAC3D,WAAO,KAAK,KAAK,UAAU,WAAW,GAAG,IAAI;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,aAAa,YAAqB,MAAmC;AACzE,WAAO,KAAK,KAAK,gBAAgB,SAAS;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgCA,MAAM,cACJ,KACA,SACA,YAAqB,MACQ;AAC7B,UAAM,OAAkB,CAAC,GAAG;AAC5B,QAAI,SAAS;AACX,WAAK,KAAK,OAAO;AAAA,IACnB;AACA,WAAO,KAAK,KAAK,iBAAiB,WAAW,GAAG,IAAI;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA,EAKA,QAAiB;AACf,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,YAA2B;AAC/B,QAAI,KAAK,QAAS,QAAO,QAAQ,QAAQ;AAEzC,WAAO,IAAI,QAAc,CAAC,YAAY;AACpC,WAAK,eAAe,IAAI,OAAO;AAAA,IACjC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,QAAQ,UAAkC;AACxC,QAAI,KAAK,SAAS;AAEhB,eAAS;AAAA,IACX;AAEA,SAAK,eAAe,IAAI,QAAQ;AAGhC,WAAO,MAAM;AACX,WAAK,eAAe,OAAO,QAAQ;AAAA,IACrC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAmB,IAA6C;AAC9D,SAAK,cAAc,IAAI,EAAE;AACzB,WAAO,MAAM,KAAK,cAAc,OAAO,EAAE;AAAA,EAC3C;AAAA;AAAA,EAGA,MAAM,OAA+C;AACnD,UAAM,KAAK,KAAK,kBAAkB;AAClC,UAAM,UAAU,EAAE,IAAI,MAAM,IAAI,MAAM,SAAS,EAAE,WAAW,KAAK,IAAI,EAAE,EAAE;AAEzE,WAAO,IAAI,QAAuC,CAAC,SAAS,WAAW;AACrE,WAAK,gBAAgB,IAAI,IAAI;AAAA,QAC3B,SAAS,CAAC,MAAM,QAAQ,CAAkC;AAAA,QAC1D,QAAQ,CAAC,MAAa,OAAO,CAAC;AAAA,MAChC,CAAC;AAED,WAAK,kBAAkB,SAAS,GAAI,EAAE,MAAM,CAAC,UAAU;AACrD,cAAM,UAAU,KAAK,gBAAgB,IAAI,EAAE;AAC3C,YAAI,SAAS;AACX,eAAK,gBAAgB,OAAO,EAAE;AAC9B,kBAAQ,OAAO,KAAK;AAAA,QACtB;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,kBACN,SACA,YAAoB,KAAK,gBACzB,eACY;AACZ,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AAEtC,UAAI,KAAK,aAAa,UAAU,KAAK,cAAc;AACjD,eAAO,IAAI,cAAc,UAAU,EAAE,MAAM,KAAK,aAAa,QAAQ,SAAS,KAAK,aAAa,CAAC,CAAC,CAAC;AACnG;AAAA,MACF;AAEA,YAAM,UAAU,WAAW,MAAM;AAC/B,cAAM,QAAQ,KAAK,aAAa,UAAU,UAAQ,KAAK,OAAO,QAAQ,EAAE;AACxE,YAAI,UAAU,IAAI;AAChB,eAAK,aAAa,OAAO,OAAO,CAAC;AAAA,QACnC;AACA,aAAK,gBAAgB,OAAO,QAAQ,EAAE;AACtC,aAAK,YAAY,OAAO,QAAQ,EAAE;AAClC,aAAK,eAAe,OAAO,QAAQ,EAAE;AACrC,eAAO,IAAI,cAAc,QAAQ,CAAC,CAAC;AAAA,MACrC,GAAG,SAAS;AAEZ,YAAM,YAAuB;AAAA,QAC3B,IAAI,QAAQ;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,WAAK,aAAa,KAAK,SAAS;AAEhC,WAAK,aAAa;AAAA,IACpB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,eAAqB;AAE3B,WAAO,KAAK,aAAa,SAAS,KAAK,KAAK,iBAAiB,KAAK,eAAe;AAC/E,YAAM,OAAO,KAAK,aAAa,MAAM;AACrC,UAAI,CAAC,KAAM;AAEX,WAAK;AAGL,YAAM,SAAS,KAAK,eAAe,IAAI,KAAK,EAAE;AAC9C,UAAI,QAAQ;AACV,eAAO,SAAS,KAAK,IAAI;AAAA,MAC3B;AAEA,UAAI;AAEF,YAAI,KAAK,iBAAiB,KAAK,cAAc,SAAS,GAAG;AACvD,eAAK,OAAO,YAAY,KAAK,SAAS,KAAK,aAAa;AAAA,QAC1D,OAAO;AACL,eAAK,OAAO,YAAY,KAAK,OAAO;AAAA,QACtC;AAAA,MAGF,SAAS,OAAO;AACd,aAAK;AACL,qBAAa,KAAK,OAAO;AACzB,aAAK,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;AAErE,aAAK,aAAa;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,oBAA0B;AAChC,SAAK;AACL,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA,EAKA,UAAgB;AACd,SAAK,OAAO,UAAU;AACtB,SAAK,gBAAgB,MAAM;AAC3B,SAAK,YAAY,MAAM;AACvB,SAAK,eAAe,MAAM;AAE1B,eAAW,QAAQ,KAAK,cAAc;AACpC,mBAAa,KAAK,OAAO;AACzB,WAAK,OAAO,IAAI,MAAM,kBAAkB,CAAC;AAAA,IAC3C;AACA,SAAK,eAAe,CAAC;AAAA,EACvB;AACF;AAKO,SAAS,aAAa,SAA8C;AACzE,SAAO,IAAI,iBAAiB,OAAO;AACrC;;;AM9iCA,IAAM,WAAW,oBAAI,IAA2B;AAKzC,SAAS,iBAAiB,MAAc,UAA+B;AAC5E,MAAI,OAAO,SAAS,YAAY,CAAC,KAAK,KAAK,GAAG;AAC5C,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,MAAI,CAAC,YAAY,OAAO,SAAS,iBAAiB,YAAY;AAC5D,UAAM,IAAI,MAAM,iDAAiD;AAAA,EACnE;AAEA,WAAS,IAAI,MAAM,QAAQ;AAC7B;AAKO,SAAS,YAAY,MAA6B;AACvD,QAAM,WAAW,SAAS,IAAI,IAAI;AAClC,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,MAAM,aAAa,IAAI,qCAAqC,MAAM,KAAK,SAAS,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,EAChH;AACA,SAAO;AACT;AAKO,SAAS,YAAY,MAAuB;AACjD,SAAO,SAAS,IAAI,IAAI;AAC1B;AAKO,SAAS,gBAA0B;AACxC,SAAO,MAAM,KAAK,SAAS,KAAK,CAAC;AACnC;AAKO,SAAS,mBAAmB,MAAuB;AACxD,SAAO,SAAS,OAAO,IAAI;AAC7B;AAKO,SAAS,iBAAuB;AACrC,WAAS,MAAM;AACjB;;;ACnDA,SAAS,MAAAA,KAAI,OAAAC,YAAwB;AAsC9B,SAAS,eAAe,QAAuC;AACpE,QAAM,eAA2F;AAAA,IAC/F,MAAM,aAAa,cAA6B;AAC9C,UAAI,sBAAsB;AAC1B,UAAI,wBAAwB,QAAQ,OAAO,gBAAgB;AACzD,8BAAsB,MAAM,OAAO,eAAe,IAAI;AAAA,MACxD;AAEA,UAAI;AACF,cAAM,WAAW,MAAM,OAAO,SAAS,QAAQ,mBAAmB;AAElE,YAAI,CAAC,SAAS,IAAI;AAEhB,gBAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACjD,iBAAOC,KAAI,WAAW,mBAAmB,GAAG,EAAE,QAAQ,EAAE,MAAM,QAAQ,SAAS,OAAO,EAAE,CAAC;AAAA,QAC3F;AAEA,cAAM,YAAY,MAAM,OAAO,OAAO,MAAM,QAAQ;AACpD,YAAI,CAAC,UAAU,OAAO;AACpB,iBAAOA,KAAI,WAAW,mBAAmB,EAAE,SAAS,8BAA8B,CAAC,CAAC;AAAA,QACtF;AAEA,YAAI,OAAO,kBAAkB,UAAU,cAAc;AACnD,gBAAM,OAAO,eAAe,IAAI,UAAU,YAAY;AAAA,QACxD;AAEA,eAAOC,IAAG,SAAS;AAAA,MACrB,SAAS,OAAO;AACd,eAAOD,KAAI,cAAc,aAAa,EAAE,SAAS,OAAO,KAAK,EAAE,CAAC,CAAC;AAAA,MACnE;AAAA,IACF;AAAA,IAEA,MAAM,MAAM,SAAkB,KAAc;AAC1C,UAAI;AACF,cAAM,WAAW,MAAM,OAAO,SAAS,MAAM,SAAS,GAAG;AAEzD,YAAI,CAAC,SAAS,IAAI;AAEhB,gBAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACjD,iBAAOA,KAAI,WAAW,YAAY,GAAG,EAAE,QAAQ,EAAE,MAAM,QAAQ,SAAS,OAAO,EAAE,CAAC;AAAA,QACpF;AAEA,cAAM,YAAY,MAAM,OAAO,OAAO,MAAM,QAAQ;AACpD,YAAI,CAAC,UAAU,OAAO;AACpB,iBAAOA,KAAI,WAAW,YAAY,EAAE,SAAS,8BAA8B,CAAC,CAAC;AAAA,QAC/E;AAEA,YAAI,OAAO,kBAAkB,UAAU,cAAc;AACnD,gBAAM,OAAO,eAAe,IAAI,UAAU,YAAY;AAAA,QACxD;AAEA,eAAOC,IAAG,SAAS;AAAA,MACrB,SAAS,OAAO;AACd,eAAOD,KAAI,cAAc,aAAa,EAAE,SAAS,OAAO,KAAK,EAAE,CAAC,CAAC;AAAA,MACnE;AAAA,IACF;AAAA,IAEA,MAAM,OAAO,SAAmB;AAC9B,UAAI;AACF,cAAM,WAAW,MAAM,OAAO,SAAS,OAAO,OAAO;AAErD,YAAI,CAAC,SAAS,IAAI;AAEhB,gBAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACjD,iBAAOA,KAAI,WAAW,aAAa,GAAG,EAAE,QAAQ,EAAE,MAAM,QAAQ,SAAS,OAAO,EAAE,CAAC;AAAA,QACrF;AAEA,YAAI,OAAO,gBAAgB;AACzB,gBAAM,OAAO,eAAe,IAAI,IAAI;AAAA,QACtC;AAEA,eAAOC,IAAG;AAAA,UACR,OAAO;AAAA,UACP,cAAc;AAAA,UACd,WAAW;AAAA,UACX,MAAM;AAAA;AAAA,QACR,CAAC;AAAA,MACH,SAAS,OAAO;AACd,eAAOD,KAAI,cAAc,aAAa,EAAE,SAAS,OAAO,KAAK,EAAE,CAAC,CAAC;AAAA,MACnE;AAAA,IACF;AAAA,IAEA,MAAM,cAAc,aAAqB,KAAa,UAAgC,CAAC,GAAG;AACxF,UAAI,CAAC,aAAa;AAChB,eAAOA,KAAI,WAAW,iBAAiB,CAAC;AAAA,MAC1C;AAEA,UAAI;AACF,cAAM,WAAW,MAAM,OAAO,SAAS,cAAc,aAAa,KAAK,OAAO;AAE9E,YAAI,CAAC,SAAS,IAAI;AAChB,gBAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACjD,iBAAOA,KAAI,WAAW,oBAAoB,GAAG,EAAE,QAAQ,EAAE,MAAM,QAAQ,SAAS,OAAO,EAAE,CAAC;AAAA,QAC5F;AAEA,cAAM,YAAY,MAAM,OAAO,OAAO,MAAM,QAAQ;AACpD,YAAI,CAAC,UAAU,OAAO;AACpB,iBAAOA,KAAI,WAAW,oBAAoB,EAAE,SAAS,8BAA8B,CAAC,CAAC;AAAA,QACvF;AAEA,YAAI,OAAO,kBAAkB,UAAU,cAAc;AACnD,gBAAM,OAAO,eAAe,IAAI,UAAU,YAAY;AAAA,QACxD;AAEA,eAAOC,IAAG,SAAS;AAAA,MACrB,SAAS,OAAO;AACd,eAAOD,KAAI,cAAc,aAAa,EAAE,SAAS,OAAO,KAAK,EAAE,CAAC,CAAC;AAAA,MACnE;AAAA,IACF;AAAA,EACF;AAGA,MAAI,OAAO,eAAe;AACxB,WAAO;AAAA,MACL,GAAG;AAAA,MACH,GAAG,OAAO;AAAA,IACZ;AAAA,EACF;AAEA,SAAO;AACT;;;AC5IO,SAAS,uBACd,UAA4C,gBAC5C,uBACqB;AAErB,QAAM,SACJ,OAAO,YAAY,WACf,EAAE,QAAQ,SAAS,iBAAiB,yBAAyB,gBAAgB,SAAS,OAAU,IAChG;AAAA,IACE,QAAQ,QAAQ,UAAU;AAAA,IAC1B,iBAAiB,QAAQ,mBAAmB;AAAA,IAC5C,SAAS,QAAQ;AAAA,EACnB;AAEN,QAAM,EAAE,QAAQ,iBAAiB,QAAQ,IAAI;AAC7C,QAAM,YAAY;AAElB,QAAM,SAAS,MAA4B;AACzC,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,UAAU,UAAU,KAAK,QAAQ,CAAC;AAExC,cAAQ,UAAU,MAAM,OAAO,QAAQ,KAAK;AAC5C,cAAQ,YAAY,MAAM,QAAQ,QAAQ,MAAM;AAEhD,cAAQ,kBAAkB,CAAC,UAAU;AACnC,cAAM,KAAM,MAAM,OAA4B;AAC9C,YAAI,CAAC,GAAG,iBAAiB,SAAS,SAAS,GAAG;AAC5C,gBAAM,QAAQ,GAAG,kBAAkB,WAAW,EAAE,SAAS,MAAM,CAAC;AAChE,gBAAM,YAAY,aAAa,aAAa,EAAE,QAAQ,MAAM,CAAC;AAAA,QAC/D;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,mBAAmB,CAAI,YAAuC;AAClE,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,cAAQ,YAAY,MAAM,QAAQ,QAAQ,MAAM;AAChD,cAAQ,UAAU,MAAM,OAAO,QAAQ,KAAK;AAAA,IAC9C,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,MAAM,MAAM;AACV,UAAI;AACF,cAAM,KAAK,MAAM,OAAO;AACxB,cAAM,cAAc,GAAG,YAAY,CAAC,SAAS,GAAG,UAAU;AAC1D,cAAM,QAAQ,YAAY,YAAY,SAAS;AAC/C,cAAM,SAAS,MAAM,iBAAiB,MAAM,IAAI,eAAe,CAAC;AAChE,eAAO,QAAQ,SAAS;AAAA,MAC1B,SAAS,OAAO;AACd,kBAAU,OAAgB,KAAK;AAC/B,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,MAAM,IAAI,OAAO;AACf,UAAI;AACF,cAAM,KAAK,MAAM,OAAO;AACxB,cAAM,cAAc,GAAG,YAAY,CAAC,SAAS,GAAG,WAAW;AAC3D,cAAM,QAAQ,YAAY,YAAY,SAAS;AAE/C,YAAI,OAAO;AACT,gBAAM,iBAAiB,MAAM,IAAI,EAAE,KAAK,iBAAiB,OAAO,OAAO,WAAW,KAAK,IAAI,EAAE,CAAC,CAAC;AAAA,QACjG,OAAO;AACL,gBAAM,iBAAiB,MAAM,OAAO,eAAe,CAAC;AAAA,QACtD;AAAA,MACF,SAAS,OAAO;AACd,kBAAU,OAAgB,QAAQ,QAAQ,QAAQ;AAAA,MACpD;AAAA,IACF;AAAA,EACF;AACF;;;AC5FO,SAAS,mBAAmB,OAAoC;AACrE,MAAI,SAAS,KAAM,QAAO;AAE1B,MAAI,OAAO,UAAU,UAAU;AAK7B,WAAO,QAAQ,OAAO,QAAQ,MAAO;AAAA,EACvC;AAEA,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,KAAK,KAAK,MAAM,KAAK;AAC3B,WAAO,MAAM,EAAE,IAAI,SAAY;AAAA,EACjC;AAEA,SAAO;AACT;;;ACdO,IAAM,aAA0B;AAAA,EACrC,MAAM,MAAM,UAAU;AACpB,UAAM,OAAO,MAAM,SAAS,MAAM,EAAE,KAAK;AACzC,WAAO;AAAA,MACL,OAAO,KAAK,KAAK;AAAA,MACjB,cAAc,KAAK,KAAK;AAAA,MACxB,WAAW,mBAAmB,KAAK,KAAK,SAAS;AAAA,MACjD,MAAM,KAAK,KAAK;AAAA,IAClB;AAAA,EACF;AACF;;;ACTO,IAAM,eAA4B;AAAA,EACvC,MAAM,MAAM,UAAU;AACpB,UAAM,OAAO,MAAM,SAAS,MAAM,EAAE,KAAK;AACzC,WAAO;AAAA,MACL,OAAO,KAAK,KAAK;AAAA,MACjB,WAAW,mBAAmB,KAAK,KAAK,SAAS;AAAA,MACjD,MAAM,KAAK,KAAK;AAAA,IAClB;AAAA,EACF;AACF;;;ACCO,SAAS,qBAAqB,QAMpB;AACf,QAAM,cAAc;AAAA,IAClB,GAAG,OAAO;AAAA,IACV,GAAG,OAAO;AAAA,IACV,gBAAgB;AAAA,EAClB;AAEA,SAAO;AAAA,IACL,MAAM,UAAU;AACd,aAAO,MAAM,OAAO,YAAY;AAAA,QAC9B,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,aAAa;AAAA,MACf,CAAC;AAAA,IACH;AAAA,IAEA,MAAM,MAAM,SAAS,KAAK;AACxB,aAAO,MAAM,OAAO,OAAO,UAAU;AAAA,QACnC,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B,aAAa;AAAA,MACf,CAAC;AAAA,IACH;AAAA,IAEA,MAAM,OAAO,SAAS;AACpB,aAAO,MAAM,OAAO,WAAW;AAAA,QAC7B,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,MAAM,UAAU,KAAK,UAAU,OAAO,IAAI;AAAA,QAC1C,aAAa;AAAA,MACf,CAAC;AAAA,IACH;AAAA,IAEA,MAAM,cAAc,aAAqB,KAAa,UAAgC,CAAC,GAAG;AACxF,YAAM,EAAE,SAAS,QAAQ,SAAS,QAAQ,IAAI;AAC9C,aAAO,MAAM,KAAK;AAAA,QAChB;AAAA,QACA,SAAS;AAAA,UACP,GAAG;AAAA,UACH,GAAG;AAAA,UACH,iBAAiB,UAAU,WAAW;AAAA,QACxC;AAAA,QACA,MAAM,UAAU,KAAK,UAAU,OAAO,IAAI;AAAA,QAC1C,aAAa;AAAA,MACf,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAKO,IAAM,iBAAiB,qBAAqB;AAAA,EACjD,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,WAAW;AACb,CAAC;;;AChEM,SAAS,mBAAmB,QAMlB;AACf,QAAM,cAAc;AAAA,IAClB,GAAG,OAAO;AAAA,IACV,GAAG,OAAO;AAAA,IACV,gBAAgB;AAAA,EAClB;AAEA,SAAO;AAAA,IACL,MAAM,QAAQ,cAAc;AAC1B,UAAI,CAAC,cAAc;AACjB,cAAM,IAAI,MAAM,4BAA4B;AAAA,MAC9C;AAEA,aAAO,MAAM,OAAO,YAAY;AAAA,QAC9B,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,MAAM,KAAK,UAAU,EAAE,aAAa,CAAC;AAAA,QACrC,aAAa;AAAA,MACf,CAAC;AAAA,IACH;AAAA,IAEA,MAAM,MAAM,SAAS,KAAK;AACxB,aAAO,MAAM,OAAO,OAAO,UAAU;AAAA,QACnC,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B,aAAa;AAAA,MACf,CAAC;AAAA,IACH;AAAA,IAEA,MAAM,OAAO,SAAS;AACpB,aAAO,MAAM,OAAO,WAAW;AAAA,QAC7B,QAAQ;AAAA,QACR,SAAS;AAAA,QACT,MAAM,UAAU,KAAK,UAAU,OAAO,IAAI;AAAA,QAC1C,aAAa;AAAA,MACf,CAAC;AAAA,IACH;AAAA,IAEA,MAAM,cAAc,aAAqB,KAAa,UAAgC,CAAC,GAAG;AACxF,YAAM,EAAE,SAAS,QAAQ,SAAS,QAAQ,IAAI;AAC9C,aAAO,MAAM,KAAK;AAAA,QAChB;AAAA,QACA,SAAS;AAAA,UACP,GAAG;AAAA,UACH,GAAG;AAAA,UACH,iBAAiB,UAAU,WAAW;AAAA,QACxC;AAAA,QACA,MAAM,UAAU,KAAK,UAAU,OAAO,IAAI;AAAA,QAC1C,aAAa;AAAA,MACf,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAKO,IAAM,eAAe,mBAAmB;AAAA,EAC7C,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,WAAW;AACb,CAAC;;;ACtEM,SAAS,qBAAqB,QAMnB;AAChB,SAAO,eAAe;AAAA,IACpB,gBAAgB;AAAA,IAChB,QAAQ;AAAA,IACR,UAAU,qBAAqB,MAAM;AAAA,EACvC,CAAC;AACH;AASO,SAAS,mBAAmB,QAOjB;AAChB,SAAO,eAAe;AAAA,IACpB,gBAAgB,uBAAuB,gBAAgB,OAAO,mBAAmB,cAAc;AAAA,IAC/F,QAAQ;AAAA,IACR,UAAU,mBAAmB,MAAM;AAAA,EACrC,CAAC;AACH;;;AC3BO,SAAS,oBAAoB,QAA6B;AAC/D,QAAM,SAAS,KAAK,MAAM;AAC1B,QAAM,QAAQ,IAAI,WAAW,OAAO,MAAM;AAC1C,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,UAAM,CAAC,IAAI,OAAO,WAAW,CAAC;AAAA,EAChC;AACA,SAAO,MAAM;AACf;AAMO,SAAS,oBAAoB,aAA8B;AAChE,QAAM,aAAa,YAAY,YAAY;AAG3C,MAAI,WAAW,SAAS,OAAO,EAAG,QAAO;AACzC,MAAI,WAAW,SAAS,MAAM,EAAG,QAAO;AACxC,MAAI,WAAW,SAAS,KAAK,EAAG,QAAO;AACvC,MAAI,WAAW,SAAS,YAAY,EAAG,QAAO;AAC9C,MAAI,WAAW,SAAS,YAAY,EAAG,QAAO;AAC9C,MAAI,WAAW,SAAS,MAAM,EAAG,QAAO;AAGxC,SAAO;AACT;;;AC7BO,SAAS,eAAe,QAAgF;AAC7G,SAAO,CAAC,OAAO;AACjB;AAUO,SAAS,UAAU,QAAwC;AAChE,SAAO,OAAO,MAAM,OAAO,KAAK,UAAU,OAAO,OAAO,KAAK,SAAS;AACxE;AAUO,SAAS,cAAc,QAAwC;AACpE,SAAO,OAAO,MAAM,OAAO,KAAK,UAAU,OAAO,OAAO,KAAK,SAAS;AACxE;AAUO,SAAS,cAAc,QAAwC;AACpE,SAAO,OAAO,MAAM,OAAO,KAAK,UAAU;AAC5C;AAeO,SAAS,UAAuB,QAAyC;AAC9E,MAAI,CAAC,OAAO,GAAI,QAAO;AACvB,MAAI;AACF,WAAO,KAAK,MAAM,OAAO,KAAK,IAAI;AAAA,EACpC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAaO,SAAS,gBAAgB,QAAuC;AACrE,MAAI,OAAO,IAAI;AAEb,QAAI;AACF,YAAM,OAAO,KAAK,MAAM,OAAO,KAAK,IAAI;AACxC,aAAO,KAAK,WAAW,KAAK,SAAS,QAAQ,OAAO,KAAK,MAAM;AAAA,IACjE,QAAQ;AACN,aAAO,QAAQ,OAAO,KAAK,MAAM;AAAA,IACnC;AAAA,EACF;AAEA,SAAO,OAAO,OAAO,CAAC,GAAG,WAAW;AACtC;AAqBO,SAAS,aAA0B,QAAyC;AACjF,MAAI,CAAC,OAAO,GAAI,QAAO;AAEvB,MAAI,OAAO,KAAK,UAAU,KAAK;AAC7B,QAAI;AACF,aAAO,KAAK,MAAM,OAAO,KAAK,IAAI;AAAA,IACpC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAaO,SAAS,UAAU,QAA8C;AACtE,SAAO,OAAO,KAAK,OAAO,KAAK,SAAS;AAC1C;AAUO,SAAS,UAAU,QAA+B,QAAyB;AAChF,SAAO,OAAO,MAAM,OAAO,KAAK,WAAW;AAC7C;AAaO,SAAS,YACd,QACA,UAMe;AACf,MAAI,CAAC,OAAO,IAAI;AACd,WAAO,SAAS,eAAe,OAAO,MAAM;AAAA,EAC9C;AAEA,QAAM,SAAS,OAAO,KAAK;AAE3B,MAAI,UAAU,OAAO,SAAS,KAAK;AACjC,WAAO,SAAS,UAAU,OAAO,IAAI;AAAA,EACvC;AAEA,MAAI,UAAU,OAAO,SAAS,KAAK;AACjC,WAAO,SAAS,cAAc,OAAO,IAAI;AAAA,EAC3C;AAEA,MAAI,UAAU,KAAK;AACjB,WAAO,SAAS,cAAc,OAAO,IAAI;AAAA,EAC3C;AAEA,SAAO;AACT;","names":["ok","err","err","ok"]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/messages.ts","../src/constants.ts","../src/errors.ts","../src/error-codes.ts","../src/utils/formdata.ts","../src/utils/registry.ts","../src/provider/create-provider.ts","../src/provider/storage/indexeddb.ts","../src/provider/parser/normalize.ts","../src/provider/parser/body.ts","../src/provider/parser/cookie.ts","../src/provider/strategy/cookie.ts","../src/provider/strategy/body.ts","../src/provider/presets.ts","../src/utils/binary.ts","../src/helpers.ts"],"sourcesContent":["import type {\r\n FetchGuardOptions,\r\n FetchGuardRequestInit,\r\n WorkerConfig,\r\n FetchEnvelope,\r\n ProviderPresetConfig,\r\n AuthResult,\r\n DebugHooks,\r\n RetryConfig,\r\n NetworkErrorDetail,\r\n DedupeConfig,\r\n RequestMetrics\r\n} from './types'\r\nimport type { MainToWorkerMessage } from './messages'\r\nimport { ok, err, type Result } from 'ts-micro-result'\r\nimport { MSG } from './messages'\r\nimport { DEFAULT_REFRESH_EARLY_MS } from './constants'\r\nimport { RequestErrors } from './errors'\r\nimport { serializeFormData, isFormData } from './utils/formdata'\r\n\r\n/**\r\n * Request timing data for metrics calculation\r\n */\r\ninterface RequestTiming {\r\n /** When request was created (before queue) */\r\n createdAt: number\r\n /** When request was sent to worker (after queue) */\r\n sentAt?: number\r\n}\r\n\r\n/**\r\n * Queue item for sequential message processing\r\n */\r\ninterface QueueItem {\r\n id: string\r\n message: MainToWorkerMessage\r\n /** Transferable objects for zero-copy postMessage (e.g., ArrayBuffers from FormData) */\r\n transferables?: Transferable[]\r\n resolve: (response: unknown) => void\r\n reject: (error: Error) => void\r\n timeout: ReturnType<typeof setTimeout>\r\n}\r\n\r\n/** Default max concurrent requests */\r\nconst DEFAULT_MAX_CONCURRENT = 6\r\n\r\n/** Default max queue size */\r\nconst DEFAULT_MAX_QUEUE_SIZE = 1000\r\n\r\n/** Default setup timeout (ms) */\r\nconst DEFAULT_SETUP_TIMEOUT = 10000\r\n\r\n/** Default request timeout (ms) */\r\nconst DEFAULT_REQUEST_TIMEOUT = 30000\r\n\r\n/**\r\n * FetchGuard Client - main interface cho việc gọi API thông qua Web Worker\r\n */\r\nexport class FetchGuardClient {\r\n private worker: Worker\r\n private messageId = 0\r\n // Using unknown because different messages have different response types\r\n // (FetchEnvelope for FETCH, AuthResult for AUTH_CALL, etc.)\r\n private pendingRequests = new Map<string, {\r\n resolve: (value: unknown) => void\r\n reject: (error: Error) => void\r\n }>()\r\n /** Track request URLs for debug hooks */\r\n private requestUrls = new Map<string, string>()\r\n /** Track request timing for metrics */\r\n private requestTimings = new Map<string, RequestTiming>()\r\n private authListeners = new Set<(state: AuthResult) => void>()\r\n private readyListeners = new Set<() => void>()\r\n private isReady = false\r\n\r\n private requestQueue: QueueItem[] = []\r\n private activeRequests = 0\r\n private readonly maxConcurrent: number\r\n private readonly maxQueueSize: number\r\n private readonly setupTimeout: number\r\n private readonly requestTimeout: number\r\n private setupResolve?: () => void\r\n private setupReject?: (error: Error) => void\r\n private readonly debug?: DebugHooks\r\n private readonly retry?: RetryConfig\r\n private readonly dedupe?: DedupeConfig\r\n /** In-flight requests for deduplication */\r\n private readonly inFlightRequests = new Map<string, Promise<Result<FetchEnvelope>>>()\r\n /** Recent completed requests for time-window deduplication */\r\n private readonly recentResults = new Map<string, { result: Result<FetchEnvelope>; timestamp: number }>()\r\n\r\n constructor(options: FetchGuardOptions) {\r\n this.maxConcurrent = options.maxConcurrent ?? DEFAULT_MAX_CONCURRENT\r\n this.maxQueueSize = options.maxQueueSize ?? DEFAULT_MAX_QUEUE_SIZE\r\n this.setupTimeout = options.setupTimeout ?? DEFAULT_SETUP_TIMEOUT\r\n this.requestTimeout = options.requestTimeout ?? DEFAULT_REQUEST_TIMEOUT\r\n this.debug = options.debug\r\n this.retry = options.retry\r\n this.dedupe = options.dedupe\r\n\r\n // Use custom worker factory if provided, otherwise use built-in worker\r\n if (options.workerFactory) {\r\n this.worker = options.workerFactory()\r\n } else {\r\n this.worker = new Worker(new URL('./worker.js', import.meta.url), {\r\n type: 'module'\r\n })\r\n }\r\n\r\n this.worker.onmessage = this.handleWorkerMessage.bind(this)\r\n this.worker.onerror = this.handleWorkerError.bind(this)\r\n\r\n this.initializeWorker(options)\r\n }\r\n\r\n /**\r\n * Initialize worker with config and provider\r\n */\r\n private async initializeWorker(options: FetchGuardOptions): Promise<void> {\r\n const config: WorkerConfig = {\r\n allowedDomains: options.allowedDomains || [],\r\n refreshEarlyMs: options.refreshEarlyMs ?? DEFAULT_REFRESH_EARLY_MS,\r\n defaultHeaders: options.defaultHeaders || {}\r\n }\r\n\r\n // Serialize provider config based on type\r\n let providerConfig: ProviderPresetConfig | string | null = null\r\n\r\n if (typeof options.provider === 'string') {\r\n // String = registry lookup (advanced usage)\r\n providerConfig = options.provider\r\n } else if ('type' in options.provider && options.provider.type) {\r\n // ProviderPresetConfig object (recommended)\r\n providerConfig = options.provider as ProviderPresetConfig\r\n } else {\r\n // TokenProvider instance - NOT SUPPORTED\r\n throw new Error(\r\n 'Direct TokenProvider instance is not supported. Use ProviderPresetConfig instead:\\n' +\r\n ' { type: \"cookie-auth\", refreshUrl: \"...\", loginUrl: \"...\", logoutUrl: \"...\" }\\n' +\r\n 'Or for custom providers, register in worker code and use string name.'\r\n )\r\n }\r\n\r\n const message = {\r\n id: this.generateMessageId(),\r\n type: MSG.SETUP,\r\n payload: {\r\n config,\r\n providerConfig\r\n }\r\n }\r\n\r\n return new Promise((resolve, reject) => {\r\n // Setup will respond with READY (no id, so we track separately)\r\n this.setupResolve = resolve\r\n this.setupReject = reject\r\n\r\n this.worker.postMessage(message)\r\n\r\n setTimeout(() => {\r\n if (this.setupReject) {\r\n this.setupReject(new Error('Worker setup timeout'))\r\n this.setupResolve = undefined\r\n this.setupReject = undefined\r\n }\r\n }, this.setupTimeout)\r\n })\r\n }\r\n\r\n\r\n /**\r\n * Handle worker messages\r\n */\r\n private handleWorkerMessage(event: MessageEvent): void {\r\n const { id, type, payload } = event.data\r\n\r\n if (type === MSG.FETCH_RESULT) {\r\n // FETCH_RESULT contains FetchEnvelope (raw HTTP response)\r\n // Worker doesn't judge HTTP status - client receives envelope as-is\r\n const request = this.pendingRequests.get(id)\r\n if (!request) return\r\n\r\n const url = this.requestUrls.get(id)\r\n const timing = this.requestTimings.get(id)\r\n this.pendingRequests.delete(id)\r\n this.requestUrls.delete(id)\r\n this.requestTimings.delete(id)\r\n this.onRequestComplete()\r\n\r\n // Calculate metrics\r\n const metrics = this.calculateMetrics(timing)\r\n\r\n // Debug hook: onResponse\r\n if (this.debug?.onResponse && url) {\r\n this.debug.onResponse(url, payload as FetchEnvelope, metrics)\r\n }\r\n\r\n request.resolve(ok(payload as FetchEnvelope))\r\n return\r\n }\r\n\r\n if (type === MSG.FETCH_ERROR) {\r\n // Network/timeout/cancel errors (no HTTP response)\r\n const request = this.pendingRequests.get(id)\r\n if (!request) return\r\n\r\n const url = this.requestUrls.get(id)\r\n const timing = this.requestTimings.get(id)\r\n this.pendingRequests.delete(id)\r\n this.requestUrls.delete(id)\r\n this.requestTimings.delete(id)\r\n this.onRequestComplete()\r\n\r\n const errorMessage = String(payload?.error || 'Network error')\r\n\r\n // Calculate metrics\r\n const metrics = this.calculateMetrics(timing)\r\n\r\n // Debug hook: onError\r\n if (this.debug?.onError && url) {\r\n this.debug.onError(url, { code: 'NETWORK_ERROR', message: errorMessage }, metrics)\r\n }\r\n\r\n request.resolve(err(\r\n RequestErrors.NetworkError({ message: errorMessage })\r\n ))\r\n return\r\n }\r\n\r\n if (type === MSG.ERROR) {\r\n const request = this.pendingRequests.get(id)\r\n if (!request) return\r\n\r\n this.pendingRequests.delete(id)\r\n this.onRequestComplete()\r\n\r\n request.resolve(err(payload.errors, payload.meta))\r\n return\r\n }\r\n\r\n if (type === MSG.SETUP_ERROR) {\r\n // Setup failed - reject setup promise\r\n if (this.setupReject) {\r\n this.setupReject(new Error(`Worker setup failed: ${payload?.error || 'Unknown error'}`))\r\n this.setupResolve = undefined\r\n this.setupReject = undefined\r\n }\r\n return\r\n }\r\n\r\n if (type === MSG.READY) {\r\n this.isReady = true\r\n\r\n // Debug hook: onWorkerReady\r\n this.debug?.onWorkerReady?.()\r\n\r\n // Notify ready listeners\r\n for (const listener of this.readyListeners) {\r\n listener()\r\n }\r\n\r\n if (this.setupResolve) {\r\n this.setupResolve()\r\n this.setupResolve = undefined\r\n this.setupReject = undefined\r\n }\r\n return\r\n }\r\n\r\n if (type === MSG.PONG) {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n this.onRequestComplete()\r\n request.resolve(ok({ timestamp: payload?.timestamp }))\r\n }\r\n return\r\n }\r\n\r\n if (type === MSG.AUTH_STATE_CHANGED) {\r\n for (const cb of this.authListeners) cb(payload)\r\n return\r\n }\r\n\r\n if (type === MSG.AUTH_CALL_RESULT) {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n this.onRequestComplete()\r\n request.resolve(ok(payload)) // payload is AuthResult\r\n }\r\n return\r\n }\r\n\r\n if (type === MSG.TOKEN_REFRESHED) {\r\n // Debug hook: onRefresh\r\n this.debug?.onRefresh?.(payload?.reason)\r\n return\r\n }\r\n }\r\n\r\n /**\r\n * Handle worker errors\r\n */\r\n private handleWorkerError(error: ErrorEvent): void {\r\n console.error('Worker error:', error)\r\n\r\n // Debug hook: onWorkerError\r\n this.debug?.onWorkerError?.(error)\r\n\r\n // Reject all pending requests\r\n for (const [id, request] of this.pendingRequests) {\r\n request.reject(new Error(`Worker error: ${error.message}`))\r\n }\r\n this.pendingRequests.clear()\r\n this.requestUrls.clear()\r\n this.requestTimings.clear()\r\n }\r\n\r\n /**\r\n * Generate unique message ID\r\n */\r\n private generateMessageId(): string {\r\n return `msg_${++this.messageId}_${Date.now()}`\r\n }\r\n\r\n /**\r\n * Make API request with optional deduplication, retry, and AbortSignal support\r\n *\r\n * @param url - Full URL to fetch\r\n * @param options - Request options including optional AbortSignal\r\n * @returns Result with FetchEnvelope on success, error on failure\r\n *\r\n * @example\r\n * // With AbortSignal\r\n * const controller = new AbortController()\r\n * setTimeout(() => controller.abort(), 5000)\r\n * const result = await api.fetch('/slow', { signal: controller.signal })\r\n */\r\n async fetch(url: string, options: FetchGuardRequestInit = {}): Promise<Result<FetchEnvelope>> {\r\n // Extract signal from options (not passed to worker - handled client-side)\r\n const { signal, ...restOptions } = options\r\n\r\n // Check if already aborted\r\n if (signal?.aborted) {\r\n return err(RequestErrors.Cancelled())\r\n }\r\n\r\n // Check for deduplication\r\n const dedupeKey = this.getDedupeKey(url, restOptions)\r\n if (dedupeKey) {\r\n // Check for in-flight request\r\n const inFlight = this.inFlightRequests.get(dedupeKey)\r\n if (inFlight) {\r\n // If we have a signal, wrap the in-flight promise to support cancellation\r\n if (signal) {\r\n return this.wrapWithAbortSignal(inFlight, signal, null)\r\n }\r\n return inFlight\r\n }\r\n\r\n // Check for recent result within time window\r\n const window = this.dedupe?.window ?? 0\r\n if (window > 0) {\r\n const recent = this.recentResults.get(dedupeKey)\r\n if (recent && Date.now() - recent.timestamp < window) {\r\n return recent.result\r\n }\r\n }\r\n\r\n // Create deduped request\r\n const promise = this.fetchWithRetryAndSignal(url, restOptions, signal ?? undefined)\r\n this.inFlightRequests.set(dedupeKey, promise)\r\n\r\n try {\r\n const result = await promise\r\n // Store result for time-window deduplication\r\n if (window > 0) {\r\n this.recentResults.set(dedupeKey, { result, timestamp: Date.now() })\r\n // Clean up old results after window expires\r\n setTimeout(() => this.recentResults.delete(dedupeKey), window)\r\n }\r\n return result\r\n } finally {\r\n this.inFlightRequests.delete(dedupeKey)\r\n }\r\n }\r\n\r\n // No deduplication - just fetch with retry and signal\r\n return this.fetchWithRetryAndSignal(url, restOptions, signal ?? undefined)\r\n }\r\n\r\n /**\r\n * Wrap a promise with AbortSignal support\r\n */\r\n private wrapWithAbortSignal(\r\n promise: Promise<Result<FetchEnvelope>>,\r\n signal: AbortSignal,\r\n requestId: string | null\r\n ): Promise<Result<FetchEnvelope>> {\r\n return new Promise((resolve) => {\r\n // Handle abort\r\n const abortHandler = () => {\r\n if (requestId) {\r\n this.cancel(requestId)\r\n }\r\n resolve(err(RequestErrors.Cancelled()))\r\n }\r\n\r\n if (signal.aborted) {\r\n abortHandler()\r\n return\r\n }\r\n\r\n signal.addEventListener('abort', abortHandler, { once: true })\r\n\r\n promise.then((result) => {\r\n signal.removeEventListener('abort', abortHandler)\r\n resolve(result)\r\n })\r\n })\r\n }\r\n\r\n /**\r\n * Fetch with retry logic and AbortSignal support (internal)\r\n */\r\n private async fetchWithRetryAndSignal(\r\n url: string,\r\n options: Omit<FetchGuardRequestInit, 'signal'>,\r\n signal?: AbortSignal\r\n ): Promise<Result<FetchEnvelope>> {\r\n const maxAttempts = this.retry?.maxAttempts ?? 0\r\n const delay = this.retry?.delay ?? 1000\r\n const backoff = this.retry?.backoff ?? 1\r\n const maxDelay = this.retry?.maxDelay ?? 30000\r\n const jitter = this.retry?.jitter ?? 0\r\n const shouldRetry = this.retry?.shouldRetry ?? this.defaultShouldRetry\r\n\r\n let lastResult: Result<FetchEnvelope> | null = null\r\n let currentDelay = delay\r\n\r\n // Initial attempt + retries\r\n for (let attempt = 0; attempt <= maxAttempts; attempt++) {\r\n // Check if aborted before each attempt\r\n if (signal?.aborted) {\r\n return err(RequestErrors.Cancelled())\r\n }\r\n\r\n const { id, result } = this.fetchWithId(url, options)\r\n\r\n // If we have a signal, wrap result with abort support\r\n if (signal) {\r\n lastResult = await this.wrapWithAbortSignal(result, signal, id)\r\n } else {\r\n lastResult = await result\r\n }\r\n\r\n // Success or HTTP error (4xx/5xx) - don't retry\r\n if (lastResult.ok) {\r\n return lastResult\r\n }\r\n\r\n // Check if cancelled\r\n if (lastResult.errors[0]?.code === 'REQUEST_CANCELLED') {\r\n return lastResult\r\n }\r\n\r\n // Check if we should retry this error\r\n const error = lastResult.errors[0]\r\n const errorDetail: NetworkErrorDetail = {\r\n code: error?.code as NetworkErrorDetail['code'] ?? 'NETWORK_ERROR',\r\n message: error?.message ?? 'Unknown error'\r\n }\r\n\r\n // Don't retry if:\r\n // - This was the last attempt\r\n // - Error is not retryable (e.g., cancelled)\r\n if (attempt >= maxAttempts || !shouldRetry(errorDetail)) {\r\n return lastResult\r\n }\r\n\r\n // Wait before retry (with exponential backoff and optional jitter)\r\n // Jitter only applies when shouldRetry=true (we're actually retrying)\r\n const cappedDelay = Math.min(currentDelay, maxDelay)\r\n const jitteredDelay = this.applyJitter(cappedDelay, jitter)\r\n\r\n // Check abort during delay\r\n if (signal) {\r\n const aborted = await this.sleepWithAbort(jitteredDelay, signal)\r\n if (aborted) {\r\n return err(RequestErrors.Cancelled())\r\n }\r\n } else {\r\n await this.sleep(jitteredDelay)\r\n }\r\n\r\n currentDelay = currentDelay * backoff\r\n }\r\n\r\n return lastResult!\r\n }\r\n\r\n /**\r\n * Generate deduplication key for request\r\n * Returns null if request should not be deduplicated\r\n */\r\n private getDedupeKey(url: string, options: FetchGuardRequestInit): string | null {\r\n if (!this.dedupe?.enabled) {\r\n return null\r\n }\r\n\r\n // Use custom key generator if provided\r\n if (this.dedupe.keyGenerator) {\r\n return this.dedupe.keyGenerator(url, options)\r\n }\r\n\r\n // Default: only dedupe GET requests by URL\r\n const method = (options.method ?? 'GET').toUpperCase()\r\n if (method !== 'GET') {\r\n return null\r\n }\r\n\r\n return `GET:${url}`\r\n }\r\n\r\n /**\r\n * Apply jitter to a delay value\r\n * Jitter adds ±(jitter * delay) randomness to prevent thundering herd\r\n * @param delay - Base delay in milliseconds\r\n * @param jitter - Jitter factor (0-1)\r\n * @returns Jittered delay\r\n */\r\n private applyJitter(delay: number, jitter: number): number {\r\n if (jitter <= 0) return delay\r\n // Clamp jitter to valid range [0, 1]\r\n const clampedJitter = Math.min(Math.max(jitter, 0), 1)\r\n // Random value between -1 and 1\r\n const randomFactor = (Math.random() * 2) - 1\r\n // Apply jitter: delay ± (delay * jitter * random)\r\n return Math.max(0, delay + (delay * clampedJitter * randomFactor))\r\n }\r\n\r\n /**\r\n * Default retry condition - only retry on NETWORK_ERROR\r\n */\r\n private defaultShouldRetry(error: NetworkErrorDetail): boolean {\r\n // Don't retry cancelled requests or parse errors\r\n return error.code === 'NETWORK_ERROR'\r\n }\r\n\r\n /**\r\n * Calculate request metrics from timing data\r\n */\r\n private calculateMetrics(timing?: RequestTiming): RequestMetrics | undefined {\r\n if (!timing) return undefined\r\n\r\n const endTime = Date.now()\r\n const startTime = timing.createdAt\r\n const sentAt = timing.sentAt ?? startTime\r\n const duration = endTime - startTime\r\n const queueTime = sentAt - startTime\r\n const ipcTime = duration - queueTime // Approximate: total - queue = IPC + server\r\n\r\n return {\r\n startTime,\r\n endTime,\r\n duration,\r\n queueTime,\r\n ipcTime\r\n }\r\n }\r\n\r\n /**\r\n * Sleep helper for retry delay\r\n */\r\n private sleep(ms: number): Promise<void> {\r\n return new Promise(resolve => setTimeout(resolve, ms))\r\n }\r\n\r\n /**\r\n * Sleep with abort signal support\r\n * Returns true if aborted, false if completed normally\r\n */\r\n private sleepWithAbort(ms: number, signal: AbortSignal): Promise<boolean> {\r\n return new Promise((resolve) => {\r\n if (signal.aborted) {\r\n resolve(true)\r\n return\r\n }\r\n\r\n const timer = setTimeout(() => {\r\n signal.removeEventListener('abort', abortHandler)\r\n resolve(false)\r\n }, ms)\r\n\r\n const abortHandler = () => {\r\n clearTimeout(timer)\r\n resolve(true)\r\n }\r\n\r\n signal.addEventListener('abort', abortHandler, { once: true })\r\n })\r\n }\r\n\r\n /**\r\n * Fetch with id for external cancellation\r\n * Returns { id, result, cancel }\r\n * Now uses queue system for sequential processing\r\n */\r\n fetchWithId(url: string, options: FetchGuardRequestInit = {}): {\r\n id: string\r\n result: Promise<Result<FetchEnvelope>>\r\n cancel: () => void\r\n } {\r\n const id = this.generateMessageId()\r\n\r\n // Serialize FormData if present (async operation)\r\n const result = new Promise<Result<FetchEnvelope>>(async (resolve, reject) => {\r\n this.pendingRequests.set(id, {\r\n resolve: (response) => resolve(response as Result<FetchEnvelope>),\r\n reject: (error) => reject(error)\r\n })\r\n // Track URL for debug hooks\r\n this.requestUrls.set(id, url)\r\n // Track timing for metrics\r\n this.requestTimings.set(id, { createdAt: Date.now() })\r\n\r\n // Debug hook: onRequest\r\n this.debug?.onRequest?.(url, options)\r\n\r\n try {\r\n let serializedOptions = { ...options }\r\n let transferables: Transferable[] | undefined\r\n\r\n // Serialize FormData body before sending to worker\r\n if (options.body && isFormData(options.body)) {\r\n const { data, transferables: formDataTransferables } = await serializeFormData(options.body)\r\n // SerializedFormData will be deserialized back to FormData in worker\r\n serializedOptions.body = data as unknown as BodyInit\r\n // ArrayBuffers for zero-copy transfer\r\n if (formDataTransferables.length > 0) {\r\n transferables = formDataTransferables\r\n }\r\n }\r\n\r\n // Serialize Headers object to plain object (Headers cannot be cloned)\r\n if (options.headers) {\r\n if (options.headers instanceof Headers) {\r\n const plainHeaders: Record<string, string> = {}\r\n options.headers.forEach((value, key) => {\r\n plainHeaders[key] = value\r\n })\r\n serializedOptions.headers = plainHeaders\r\n }\r\n }\r\n\r\n const message = { id, type: MSG.FETCH, payload: { url, options: serializedOptions } }\r\n\r\n await this.sendMessageQueued(message, 30000, transferables)\r\n } catch (error) {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n this.requestUrls.delete(id)\r\n this.requestTimings.delete(id)\r\n request.reject(error instanceof Error ? error : new Error(String(error)))\r\n }\r\n }\r\n })\r\n\r\n const cancel = () => this.cancel(id)\r\n\r\n return { id, result, cancel }\r\n }\r\n\r\n /**\r\n * Cancel a pending request by ID\r\n */\r\n cancel(id: string): void {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n const url = this.requestUrls.get(id)\r\n const timing = this.requestTimings.get(id)\r\n this.pendingRequests.delete(id)\r\n this.requestUrls.delete(id)\r\n this.requestTimings.delete(id)\r\n this.worker.postMessage({ id, type: MSG.CANCEL })\r\n\r\n // Calculate metrics\r\n const metrics = this.calculateMetrics(timing)\r\n\r\n // Debug hook: onError for cancelled request\r\n if (this.debug?.onError && url) {\r\n this.debug.onError(url, { code: 'REQUEST_CANCELLED', message: 'Request cancelled' }, metrics)\r\n }\r\n\r\n request.reject(new Error('Request cancelled'))\r\n }\r\n }\r\n\r\n /**\r\n * Convenience methods\r\n */\r\n async get(url: string, options: Omit<FetchGuardRequestInit, 'method' | 'body'> = {}): Promise<Result<FetchEnvelope>> {\r\n return this.fetch(url, { ...options, method: 'GET' })\r\n }\r\n\r\n async post(url: string, body?: unknown, options: Omit<FetchGuardRequestInit, 'method' | 'body'> = {}): Promise<Result<FetchEnvelope>> {\r\n // If body is FormData, use fetch directly (no JSON.stringify)\r\n if (body && isFormData(body)) {\r\n return this.fetch(url, {\r\n ...options,\r\n method: 'POST',\r\n body\r\n })\r\n }\r\n\r\n // For non-FormData body, use JSON\r\n const headers = new Headers(options.headers)\r\n\r\n // Set Content-Type if not already set and body is being stringified\r\n if (body && !headers.has('Content-Type')) {\r\n headers.set('Content-Type', 'application/json')\r\n }\r\n\r\n return this.fetch(url, {\r\n ...options,\r\n headers,\r\n method: 'POST',\r\n body: body ? JSON.stringify(body) : undefined\r\n })\r\n }\r\n\r\n async put(url: string, body?: unknown, options: Omit<FetchGuardRequestInit, 'method' | 'body'> = {}): Promise<Result<FetchEnvelope>> {\r\n // If body is FormData, use fetch directly (no JSON.stringify)\r\n if (body && isFormData(body)) {\r\n return this.fetch(url, {\r\n ...options,\r\n method: 'PUT',\r\n body\r\n })\r\n }\r\n\r\n // For non-FormData body, use JSON\r\n const headers = new Headers(options.headers)\r\n\r\n // Set Content-Type if not already set and body is being stringified\r\n if (body && !headers.has('Content-Type')) {\r\n headers.set('Content-Type', 'application/json')\r\n }\r\n\r\n return this.fetch(url, {\r\n ...options,\r\n headers,\r\n method: 'PUT',\r\n body: body ? JSON.stringify(body) : undefined\r\n })\r\n }\r\n\r\n async delete(url: string, options: Omit<FetchGuardRequestInit, 'method' | 'body'> = {}): Promise<Result<FetchEnvelope>> {\r\n return this.fetch(url, { ...options, method: 'DELETE' })\r\n }\r\n\r\n async patch(url: string, body?: unknown, options: Omit<FetchGuardRequestInit, 'method' | 'body'> = {}): Promise<Result<FetchEnvelope>> {\r\n // If body is FormData, use fetch directly (no JSON.stringify)\r\n if (body && isFormData(body)) {\r\n return this.fetch(url, {\r\n ...options,\r\n method: 'PATCH',\r\n body\r\n })\r\n }\r\n\r\n // For non-FormData body, use JSON\r\n const headers = new Headers(options.headers)\r\n\r\n // Set Content-Type if not already set and body is being stringified\r\n if (body && !headers.has('Content-Type')) {\r\n headers.set('Content-Type', 'application/json')\r\n }\r\n\r\n return this.fetch(url, {\r\n ...options,\r\n headers,\r\n method: 'PATCH',\r\n body: body ? JSON.stringify(body) : undefined\r\n })\r\n }\r\n\r\n /**\r\n * Generic method to call any auth method on provider\r\n * @param method - Method name (login, logout, loginWithPhone, etc.)\r\n * @param emitEvent - Whether to emit AUTH_STATE_CHANGED event (default: true)\r\n * @param args - Arguments to pass to the method\r\n * @returns Promise<Result<AuthResult>> - Always returns AuthResult\r\n */\r\n async call(method: string, emitEvent?: boolean, ...args: unknown[]): Promise<Result<AuthResult>> {\r\n const id = this.generateMessageId()\r\n const message = { id, type: MSG.AUTH_CALL, payload: { method, args, emitEvent } }\r\n\r\n return new Promise<Result<AuthResult>>((resolve, reject) => {\r\n this.pendingRequests.set(id, {\r\n resolve: (r) => resolve(r as Result<AuthResult>),\r\n reject: (e: Error) => reject(e)\r\n })\r\n\r\n this.sendMessageQueued(message, 15000).catch((error) => {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n request.reject(error)\r\n }\r\n })\r\n })\r\n }\r\n\r\n\r\n /**\r\n * Convenience wrapper for login\r\n * @param payload - Login credentials\r\n * @param url - Optional URL override\r\n * @param emitEvent - Whether to emit AUTH_STATE_CHANGED event (default: true)\r\n */\r\n async login(payload?: unknown, url?: string, emitEvent: boolean = true): Promise<Result<AuthResult>> {\r\n const args: unknown[] = []\r\n if (typeof payload !== 'undefined') {\r\n args.push(payload)\r\n }\r\n if (typeof url !== 'undefined') {\r\n // If payload is undefined but url is provided, need to pass undefined explicitly\r\n if (args.length === 0) {\r\n args.push(undefined)\r\n }\r\n args.push(url)\r\n }\r\n return this.call('login', emitEvent, ...args)\r\n }\r\n\r\n /**\r\n * Convenience wrapper for logout\r\n * @param payload - Optional logout payload\r\n * @param emitEvent - Whether to emit AUTH_STATE_CHANGED event (default: true)\r\n */\r\n async logout(payload?: unknown, emitEvent: boolean = true): Promise<Result<AuthResult>> {\r\n const args = typeof payload === 'undefined' ? [] : [payload]\r\n return this.call('logout', emitEvent, ...args)\r\n }\r\n\r\n /**\r\n * Convenience wrapper for refreshToken\r\n * @param emitEvent - Whether to emit AUTH_STATE_CHANGED event (default: true)\r\n */\r\n async refreshToken(emitEvent: boolean = true): Promise<Result<AuthResult>> {\r\n return this.call('refreshToken', emitEvent)\r\n }\r\n\r\n /**\r\n * Exchange current token for a new one with different context\r\n *\r\n * Useful for:\r\n * - Switching tenants in multi-tenant apps\r\n * - Changing authorization scope\r\n * - Impersonating users (admin feature)\r\n *\r\n * @param url - URL to call for token exchange\r\n * @param options - Exchange options (method, payload, headers)\r\n * @param emitEvent - Whether to emit AUTH_STATE_CHANGED event (default: true)\r\n *\r\n * @example\r\n * // Switch tenant\r\n * await api.exchangeToken('https://auth.example.com/auth/select-tenant', {\r\n * payload: { tenantId: 'tenant_123' }\r\n * })\r\n *\r\n * // Change scope with PUT method\r\n * await api.exchangeToken('https://auth.example.com/auth/switch-context', {\r\n * method: 'PUT',\r\n * payload: { scope: 'admin' }\r\n * })\r\n *\r\n * // With custom headers (overrides defaultHeaders)\r\n * await api.exchangeToken('https://auth.example.com/auth/impersonate', {\r\n * payload: { userId: 'user_456' },\r\n * headers: { 'X-Impersonate-Reason': 'support-ticket-123' }\r\n * })\r\n */\r\n async exchangeToken(\r\n url: string,\r\n options?: { method?: 'POST' | 'PUT'; payload?: Record<string, unknown>; headers?: Record<string, string> },\r\n emitEvent: boolean = true\r\n ): Promise<Result<AuthResult>> {\r\n const args: unknown[] = [url]\r\n if (options) {\r\n args.push(options)\r\n }\r\n return this.call('exchangeToken', emitEvent, ...args)\r\n }\r\n\r\n /**\r\n * Check if worker is ready\r\n */\r\n ready(): boolean {\r\n return this.isReady\r\n }\r\n\r\n /**\r\n * Wait for worker to be ready\r\n * Returns immediately if already ready\r\n */\r\n async whenReady(): Promise<void> {\r\n if (this.isReady) return Promise.resolve()\r\n\r\n return new Promise<void>((resolve) => {\r\n this.readyListeners.add(resolve)\r\n })\r\n }\r\n\r\n /**\r\n * Subscribe to ready event\r\n * Callback is called immediately if already ready\r\n */\r\n onReady(callback: () => void): () => void {\r\n if (this.isReady) {\r\n // Already ready - call immediately\r\n callback()\r\n }\r\n\r\n this.readyListeners.add(callback)\r\n\r\n // Return unsubscribe function\r\n return () => {\r\n this.readyListeners.delete(callback)\r\n }\r\n }\r\n\r\n /**\r\n * Subscribe to auth state changes\r\n */\r\n onAuthStateChanged(cb: (state: AuthResult) => void): () => void {\r\n this.authListeners.add(cb)\r\n return () => this.authListeners.delete(cb)\r\n }\r\n\r\n /** Send PING and await PONG */\r\n async ping(): Promise<Result<{ timestamp: number }>> {\r\n const id = this.generateMessageId()\r\n const message = { id, type: MSG.PING, payload: { timestamp: Date.now() } }\r\n\r\n return new Promise<Result<{ timestamp: number }>>((resolve, reject) => {\r\n this.pendingRequests.set(id, {\r\n resolve: (r) => resolve(r as Result<{ timestamp: number }>),\r\n reject: (e: Error) => reject(e)\r\n })\r\n\r\n this.sendMessageQueued(message, 5000).catch((error) => {\r\n const request = this.pendingRequests.get(id)\r\n if (request) {\r\n this.pendingRequests.delete(id)\r\n request.reject(error)\r\n }\r\n })\r\n })\r\n }\r\n\r\n\r\n /**\r\n * Send message through queue system\r\n * All messages go through queue for sequential processing\r\n * @param transferables - Optional Transferable objects for zero-copy postMessage\r\n */\r\n private sendMessageQueued<T = unknown>(\r\n message: MainToWorkerMessage,\r\n timeoutMs: number = this.requestTimeout,\r\n transferables?: Transferable[]\r\n ): Promise<T> {\r\n return new Promise((resolve, reject) => {\r\n // Check queue size limit to prevent memory leak\r\n if (this.requestQueue.length >= this.maxQueueSize) {\r\n reject(err(RequestErrors.QueueFull({ size: this.requestQueue.length, maxSize: this.maxQueueSize })))\r\n return\r\n }\r\n\r\n const timeout = setTimeout(() => {\r\n const index = this.requestQueue.findIndex(item => item.id === message.id)\r\n if (index !== -1) {\r\n this.requestQueue.splice(index, 1)\r\n }\r\n this.pendingRequests.delete(message.id)\r\n this.requestUrls.delete(message.id)\r\n this.requestTimings.delete(message.id)\r\n reject(err(RequestErrors.Timeout()))\r\n }, timeoutMs)\r\n\r\n const queueItem: QueueItem = {\r\n id: message.id,\r\n message,\r\n transferables,\r\n resolve: resolve as (response: unknown) => void,\r\n reject,\r\n timeout\r\n }\r\n\r\n this.requestQueue.push(queueItem)\r\n\r\n this.processQueue()\r\n })\r\n }\r\n\r\n /**\r\n * Process message queue with concurrency limit\r\n *\r\n * Uses semaphore pattern to allow N concurrent requests.\r\n * Benefits:\r\n * - Higher throughput than sequential processing\r\n * - Backpressure via maxConcurrent limit\r\n * - Better error isolation (one failure doesn't affect others)\r\n */\r\n private processQueue(): void {\r\n // Process as many items as we can within concurrency limit\r\n while (this.requestQueue.length > 0 && this.activeRequests < this.maxConcurrent) {\r\n const item = this.requestQueue.shift()\r\n if (!item) continue\r\n\r\n this.activeRequests++\r\n\r\n // Update timing: mark when request is actually sent to worker\r\n const timing = this.requestTimings.get(item.id)\r\n if (timing) {\r\n timing.sentAt = Date.now()\r\n }\r\n\r\n try {\r\n // Use transferables for zero-copy transfer when available (e.g., FormData with files)\r\n if (item.transferables && item.transferables.length > 0) {\r\n this.worker.postMessage(item.message, item.transferables)\r\n } else {\r\n this.worker.postMessage(item.message)\r\n }\r\n // Note: activeRequests is decremented when response is received\r\n // in handleWorkerMessage, not here\r\n } catch (error) {\r\n this.activeRequests--\r\n clearTimeout(item.timeout)\r\n item.reject(error instanceof Error ? error : new Error(String(error)))\r\n // Continue processing queue after error\r\n this.processQueue()\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Called when a request completes (success or error)\r\n * Decrements active count and processes next items in queue\r\n */\r\n private onRequestComplete(): void {\r\n this.activeRequests--\r\n this.processQueue()\r\n }\r\n\r\n /**\r\n * Cleanup - terminate worker\r\n */\r\n destroy(): void {\r\n this.worker.terminate()\r\n this.pendingRequests.clear()\r\n this.requestUrls.clear()\r\n this.requestTimings.clear()\r\n\r\n for (const item of this.requestQueue) {\r\n clearTimeout(item.timeout)\r\n item.reject(new Error('Client destroyed'))\r\n }\r\n this.requestQueue = []\r\n }\r\n}\r\n\r\n/**\r\n * Factory function to create FetchGuard client\r\n */\r\nexport function createClient(options: FetchGuardOptions): FetchGuardClient {\r\n return new FetchGuardClient(options)\r\n}\r\n","import type { ErrorDetail, Result, ResultMeta } from 'ts-micro-result'\r\nimport type { WorkerConfig, FetchGuardRequestInit, ProviderPresetConfig, AuthResult, FetchEnvelope, RefreshReason } from './types'\r\n\r\n/**\r\n * MESSAGE PAYLOADS - SINGLE SOURCE OF TRUTH\r\n *\r\n * Define all message payloads here. Type unions and MSG constants are auto-generated.\r\n *\r\n * USAGE:\r\n * - To add a new message: Just add one line to the appropriate interface\r\n * - Payload types are automatically inferred\r\n * - MSG constants are automatically generated\r\n *\r\n * EXAMPLE:\r\n * ```typescript\r\n * interface MainPayloads {\r\n * NEW_MESSAGE: { foo: string } // Add this line\r\n * }\r\n * // => Automatically get MainToWorkerMessage union with NEW_MESSAGE\r\n * // => Automatically get MSG.NEW_MESSAGE = 'NEW_MESSAGE'\r\n * ```\r\n */\r\n\r\n/**\r\n * Payloads for messages sent from Main thread → Worker thread\r\n */\r\nexport interface MainPayloads {\r\n SETUP: { config: WorkerConfig; providerConfig: ProviderPresetConfig | string | null }\r\n FETCH: { url: string; options?: FetchGuardRequestInit }\r\n AUTH_CALL: { method: string; args: unknown[]; emitEvent?: boolean } // Generic auth method call (login, logout, loginWithPhone, etc.)\r\n CANCEL: undefined\r\n PING: { timestamp: number }\r\n}\r\n\r\n/**\r\n * Payloads for messages sent from Worker thread → Main thread\r\n */\r\nexport interface WorkerPayloads {\r\n ERROR: { errors: ErrorDetail[]; meta?: ResultMeta, status?: number }\r\n READY: undefined\r\n SETUP_ERROR: { error: string }\r\n PONG: { timestamp: number }\r\n LOG: { level: 'info' | 'warn' | 'error'; message: string }\r\n AUTH_STATE_CHANGED: AuthResult\r\n AUTH_CALL_RESULT: AuthResult\r\n FETCH_RESULT: FetchEnvelope\r\n FETCH_ERROR: { error: string; status?: number }\r\n TOKEN_REFRESHED: { reason: RefreshReason }\r\n}\r\n\r\n/**\r\n * Generate message type from payload definition\r\n * Handles optional payloads (undefined) gracefully\r\n */\r\ntype MessageFromPayloads<P> = {\r\n [K in keyof P]: { id: string; type: K } & (\r\n P[K] extends undefined ? {} : { payload: P[K] }\r\n )\r\n}[keyof P]\r\n\r\n/**\r\n * Message type unions - auto-generated from payload interfaces\r\n */\r\nexport type MainToWorkerMessage = MessageFromPayloads<MainPayloads>\r\nexport type WorkerToMainMessage = MessageFromPayloads<WorkerPayloads>\r\n\r\n/**\r\n * Message type unions for compile-time type checking\r\n */\r\nexport type MainType = keyof MainPayloads\r\nexport type WorkerType = keyof WorkerPayloads\r\nexport type MessageType = MainType | WorkerType\r\n\r\n/**\r\n * MSG constants object\r\n * Usage: MSG.SETUP, MSG.FETCH, etc.\r\n */\r\nexport const MSG = Object.freeze({\r\n // Main -> Worker messages\r\n SETUP: 'SETUP',\r\n FETCH: 'FETCH',\r\n AUTH_CALL: 'AUTH_CALL',\r\n CANCEL: 'CANCEL',\r\n PING: 'PING',\r\n\r\n // Worker -> Main messages\r\n ERROR: 'ERROR',\r\n READY: 'READY',\r\n SETUP_ERROR: 'SETUP_ERROR',\r\n PONG: 'PONG',\r\n LOG: 'LOG',\r\n AUTH_STATE_CHANGED: 'AUTH_STATE_CHANGED',\r\n AUTH_CALL_RESULT: 'AUTH_CALL_RESULT',\r\n FETCH_RESULT: 'FETCH_RESULT',\r\n FETCH_ERROR: 'FETCH_ERROR',\r\n TOKEN_REFRESHED: 'TOKEN_REFRESHED'\r\n}) as { readonly [K in MessageType]: K }\r\n","/**\n * FetchGuard Default Configuration Values\n */\n\n/**\n * Default time (in milliseconds) to refresh token before expiry\n * @default 60000 (60 seconds)\n */\nexport const DEFAULT_REFRESH_EARLY_MS = 60_000\n","/**\n * Error definitions organized by domain\n * Using ts-micro-result's defineError for consistency\n */\n\nimport { defineError, defineErrorAdvanced } from 'ts-micro-result'\nimport { ERROR_CODES } from './error-codes'\n\n/**\n * General errors\n */\nexport const GeneralErrors = {\n Unexpected: defineError(ERROR_CODES.UNEXPECTED, 'Unexpected error'),\n UnknownMessage: defineError(ERROR_CODES.UNKNOWN_MESSAGE, 'Unknown message type'),\n ResultParse: defineError(ERROR_CODES.RESULT_PARSE_ERROR, 'Failed to parse result'),\n} as const\n\n/**\n * Initialization errors\n */\nexport const InitErrors = {\n NotInitialized: defineError(ERROR_CODES.INIT_ERROR, 'Worker not initialized'),\n ProviderInitFailed: defineError(ERROR_CODES.PROVIDER_INIT_FAILED, 'Failed to initialize provider'),\n InitFailed: defineError(ERROR_CODES.INIT_FAILED, 'Initialization failed'),\n} as const\n\n/**\n * Authentication & Token errors\n */\nexport const AuthErrors = {\n TokenRefreshFailed: defineError(ERROR_CODES.TOKEN_REFRESH_FAILED, 'Token refresh failed'),\n TokenExchangeFailed: defineError(ERROR_CODES.TOKEN_EXCHANGE_FAILED, 'Token exchange failed'),\n LoginFailed: defineError(ERROR_CODES.LOGIN_FAILED, 'Login failed'),\n LogoutFailed: defineError(ERROR_CODES.LOGOUT_FAILED, 'Logout failed'),\n NotAuthenticated: defineError(ERROR_CODES.NOT_AUTHENTICATED, 'User is not authenticated'),\n} as const\n\n/**\n * Domain validation errors\n */\nexport const DomainErrors = {\n NotAllowed: defineErrorAdvanced(ERROR_CODES.DOMAIN_NOT_ALLOWED, 'Domain not allowed: {url}'),\n} as const\n\n/**\n * Request/Response errors (network, HTTP, parsing)\n */\nexport const RequestErrors = {\n // Network errors (connection failed, no response)\n NetworkError: defineError(ERROR_CODES.NETWORK_ERROR, 'Network error'),\n Cancelled: defineError(ERROR_CODES.REQUEST_CANCELLED, 'Request was cancelled'),\n\n // HTTP errors (server responded with error status)\n HttpError: defineErrorAdvanced(ERROR_CODES.HTTP_ERROR, 'HTTP {status} error'),\n\n // Response parsing errors\n ResponseParseFailed: defineError(ERROR_CODES.RESPONSE_PARSE_FAILED, 'Failed to parse response body'),\n\n // Queue errors\n QueueFull: defineErrorAdvanced(ERROR_CODES.QUEUE_FULL, 'Request queue full ({size}/{maxSize})'),\n\n // Timeout errors\n Timeout: defineError(ERROR_CODES.REQUEST_TIMEOUT, 'Request timed out'),\n} as const\n","/**\r\n * Error codes as constants for type-safe error matching\r\n *\r\n * Usage:\r\n * ```typescript\r\n * import { ERROR_CODES } from 'fetchguard'\r\n *\r\n * if (result.errors[0]?.code === ERROR_CODES.NETWORK_ERROR) {\r\n * // Handle network error\r\n * }\r\n * ```\r\n */\r\n\r\nexport const ERROR_CODES = {\r\n // General\r\n UNEXPECTED: 'UNEXPECTED',\r\n UNKNOWN_MESSAGE: 'UNKNOWN_MESSAGE',\r\n RESULT_PARSE_ERROR: 'RESULT_PARSE_ERROR',\r\n\r\n // Init\r\n INIT_ERROR: 'INIT_ERROR',\r\n PROVIDER_INIT_FAILED: 'PROVIDER_INIT_FAILED',\r\n INIT_FAILED: 'INIT_FAILED',\r\n\r\n // Auth\r\n TOKEN_REFRESH_FAILED: 'TOKEN_REFRESH_FAILED',\r\n TOKEN_EXCHANGE_FAILED: 'TOKEN_EXCHANGE_FAILED',\r\n LOGIN_FAILED: 'LOGIN_FAILED',\r\n LOGOUT_FAILED: 'LOGOUT_FAILED',\r\n NOT_AUTHENTICATED: 'NOT_AUTHENTICATED',\r\n\r\n // Domain\r\n DOMAIN_NOT_ALLOWED: 'DOMAIN_NOT_ALLOWED',\r\n\r\n // Request\r\n NETWORK_ERROR: 'NETWORK_ERROR',\r\n REQUEST_CANCELLED: 'REQUEST_CANCELLED',\r\n HTTP_ERROR: 'HTTP_ERROR',\r\n RESPONSE_PARSE_FAILED: 'RESPONSE_PARSE_FAILED',\r\n QUEUE_FULL: 'QUEUE_FULL',\r\n REQUEST_TIMEOUT: 'REQUEST_TIMEOUT'\r\n} as const\r\n\r\n/**\r\n * Union type of all error code values\r\n */\r\nexport type ErrorCode = typeof ERROR_CODES[keyof typeof ERROR_CODES]\r\n\r\n/**\r\n * Union type of all error code keys (useful for telemetry mapping)\r\n */\r\nexport type ErrorCodeKey = keyof typeof ERROR_CODES\r\n","import type { SerializedFormData, SerializedFormDataEntry, SerializedFile, SerializedFormDataResult } from '../types'\r\n\r\n/**\r\n * Serialize FormData for transfer over postMessage\r\n *\r\n * FormData cannot be cloned via postMessage, so we need to serialize it first.\r\n * Files are converted to ArrayBuffer and returned as transferables for zero-copy transfer.\r\n *\r\n * IMPORTANT: Preserves original field order by using single-pass iteration.\r\n *\r\n * @returns SerializedFormDataResult with data and transferables array\r\n */\r\nexport async function serializeFormData(formData: FormData): Promise<SerializedFormDataResult> {\r\n const entries: Array<[string, SerializedFormDataEntry]> = []\r\n const transferables: ArrayBuffer[] = []\r\n\r\n // Single-pass iteration to preserve original field order\r\n // Collect all entries with their index for order preservation\r\n const orderedEntries: Array<{ index: number; key: string; value: FormDataEntryValue }> = []\r\n let index = 0\r\n formData.forEach((value, key) => {\r\n orderedEntries.push({ index, key, value })\r\n index++\r\n })\r\n\r\n // Process all entries in order, handling files async\r\n await Promise.all(\r\n orderedEntries.map(async ({ index: idx, key, value }) => {\r\n if (value instanceof File) {\r\n const buffer = await value.arrayBuffer()\r\n const serializedFile: SerializedFile = {\r\n name: value.name,\r\n type: value.type,\r\n buffer\r\n }\r\n // Store with index for sorting later\r\n entries[idx] = [key, serializedFile]\r\n transferables.push(buffer)\r\n } else {\r\n entries[idx] = [key, String(value)]\r\n }\r\n })\r\n )\r\n\r\n return {\r\n data: {\r\n _type: 'FormData',\r\n entries\r\n },\r\n transferables\r\n }\r\n}\r\n\r\n/**\r\n * Deserialize SerializedFormData back to FormData in worker\r\n * Reconstructs File objects from transferred ArrayBuffers\r\n */\r\nexport function deserializeFormData(serialized: SerializedFormData): FormData {\r\n const formData = new FormData()\r\n\r\n for (const [key, value] of serialized.entries) {\r\n if (typeof value === 'string') {\r\n formData.append(key, value)\r\n } else {\r\n // Reconstruct File from SerializedFile (ArrayBuffer already transferred)\r\n const file = new File([value.buffer], value.name, { type: value.type })\r\n formData.append(key, file)\r\n }\r\n }\r\n\r\n return formData\r\n}\r\n\r\n/**\r\n * Check if body is FormData\r\n */\r\nexport function isFormData(body: unknown): body is FormData {\r\n return body instanceof FormData\r\n}\r\n\r\n/**\r\n * Check if serialized body is SerializedFormData\r\n */\r\nexport function isSerializedFormData(body: unknown): body is SerializedFormData {\r\n return body !== null && typeof body === 'object' && (body as SerializedFormData)._type === 'FormData'\r\n}\r\n","import type { TokenProvider } from '../types'\r\n\r\n/**\r\n * Registry to manage token providers\r\n */\r\nconst registry = new Map<string, TokenProvider>()\r\n\r\n/**\r\n * Register a token provider with name\r\n */\r\nexport function registerProvider(name: string, provider: TokenProvider): void {\r\n if (typeof name !== 'string' || !name.trim()) {\r\n throw new Error('Provider name must be a non-empty string')\r\n }\r\n \r\n if (!provider || typeof provider.refreshToken !== 'function') {\r\n throw new Error('Provider must implement TokenProvider interface')\r\n }\r\n \r\n registry.set(name, provider)\r\n}\r\n\r\n/**\r\n * Get provider by name\r\n */\r\nexport function getProvider(name: string): TokenProvider {\r\n const provider = registry.get(name)\r\n if (!provider) {\r\n throw new Error(`Provider '${name}' not found. Available providers: ${Array.from(registry.keys()).join(', ')}`)\r\n }\r\n return provider\r\n}\r\n\r\n/**\r\n * Check if provider exists\r\n */\r\nexport function hasProvider(name: string): boolean {\r\n return registry.has(name)\r\n}\r\n\r\n/**\r\n * Get list of all provider names\r\n */\r\nexport function listProviders(): string[] {\r\n return Array.from(registry.keys())\r\n}\r\n\r\n/**\r\n * Remove provider\r\n */\r\nexport function unregisterProvider(name: string): boolean {\r\n return registry.delete(name)\r\n}\r\n\r\n/**\r\n * Remove all providers\r\n */\r\nexport function clearProviders(): void {\r\n registry.clear()\r\n}\r\n","import type {\r\n TokenProvider,\r\n RefreshTokenStorage,\r\n TokenParser,\r\n AuthStrategy,\r\n TokenInfo,\r\n ExchangeTokenOptions\r\n} from '../types'\r\nimport { ok, err, type Result } from 'ts-micro-result'\r\nimport { AuthErrors, RequestErrors } from '../errors'\r\n\r\n/**\r\n * Custom auth method type\r\n */\r\ntype CustomAuthMethod = (...args: unknown[]) => Promise<Result<TokenInfo>>\r\n\r\n/**\r\n * Configuration for creating provider\r\n *\r\n * refreshStorage: OPTIONAL - to load refresh token initially when worker starts\r\n * - undefined: cookie-based auth (httpOnly cookie, no need to load)\r\n * - RefreshTokenStorage: body-based auth (load from IndexedDB on startup)\r\n *\r\n * strategy: AuthStrategy with refresh (required), login/logout (required)\r\n *\r\n * customMethods: OPTIONAL - custom auth methods (loginWithPhone, loginWithGoogle, etc.)\r\n */\r\nexport interface ProviderConfig {\r\n refreshStorage?: RefreshTokenStorage\r\n parser: TokenParser\r\n strategy: AuthStrategy\r\n customMethods?: Record<string, CustomAuthMethod>\r\n}\r\n\r\n/**\r\n * Factory function to create TokenProvider from modular components\r\n *\r\n * Provider automatically handles refresh token:\r\n * - If refreshToken is null and storage exists → load from storage initially\r\n * - If refreshToken exists → use token from worker memory\r\n * - Cookie-based (no storage) → always null\r\n *\r\n * Custom methods:\r\n * - User can add custom auth methods (loginWithPhone, loginWithGoogle, etc.)\r\n * - Custom methods will be spread into provider object\r\n */\r\nexport function createProvider(config: ProviderConfig): TokenProvider {\r\n const baseProvider: Pick<TokenProvider, 'refreshToken' | 'login' | 'logout' | 'exchangeToken'> = {\r\n async refreshToken(refreshToken: string | null) {\r\n let currentRefreshToken = refreshToken\r\n if (currentRefreshToken === null && config.refreshStorage) {\r\n currentRefreshToken = await config.refreshStorage.get()\r\n }\r\n\r\n try {\r\n const response = await config.strategy.refresh(currentRefreshToken)\r\n\r\n if (!response.ok) {\r\n // Read response body for error details\r\n const body = await response.text().catch(() => '')\r\n return err(AuthErrors.TokenRefreshFailed(), { params: { body, status: response.status } })\r\n }\r\n\r\n const tokenInfo = await config.parser.parse(response)\r\n if (!tokenInfo.token) {\r\n return err(AuthErrors.TokenRefreshFailed({ message: 'No access token in response' }))\r\n }\r\n\r\n if (config.refreshStorage && tokenInfo.refreshToken) {\r\n await config.refreshStorage.set(tokenInfo.refreshToken)\r\n }\r\n\r\n return ok(tokenInfo)\r\n } catch (error) {\r\n return err(RequestErrors.NetworkError({ message: String(error) }))\r\n }\r\n },\r\n\r\n async login(payload: unknown, url?: string) {\r\n try {\r\n const response = await config.strategy.login(payload, url)\r\n\r\n if (!response.ok) {\r\n // Read response body for error details\r\n const body = await response.text().catch(() => '')\r\n return err(AuthErrors.LoginFailed(), { params: { body, status: response.status } })\r\n }\r\n\r\n const tokenInfo = await config.parser.parse(response)\r\n if (!tokenInfo.token) {\r\n return err(AuthErrors.LoginFailed({ message: 'No access token in response' }))\r\n }\r\n\r\n if (config.refreshStorage && tokenInfo.refreshToken) {\r\n await config.refreshStorage.set(tokenInfo.refreshToken)\r\n }\r\n\r\n return ok(tokenInfo)\r\n } catch (error) {\r\n return err(RequestErrors.NetworkError({ message: String(error) }))\r\n }\r\n },\r\n\r\n async logout(payload?: unknown) {\r\n try {\r\n const response = await config.strategy.logout(payload)\r\n\r\n if (!response.ok) {\r\n // Read response body for error details\r\n const body = await response.text().catch(() => '')\r\n return err(AuthErrors.LogoutFailed(), { params: { body, status: response.status } })\r\n }\r\n\r\n if (config.refreshStorage) {\r\n await config.refreshStorage.set(null)\r\n }\r\n\r\n return ok({\r\n token: '',\r\n refreshToken: undefined,\r\n expiresAt: undefined,\r\n user: null // Explicitly clear user on logout\r\n })\r\n } catch (error) {\r\n return err(RequestErrors.NetworkError({ message: String(error) }))\r\n }\r\n },\r\n\r\n async exchangeToken(accessToken: string, url: string, options: ExchangeTokenOptions = {}) {\r\n if (!accessToken) {\r\n return err(AuthErrors.NotAuthenticated())\r\n }\r\n\r\n try {\r\n const response = await config.strategy.exchangeToken(accessToken, url, options)\r\n\r\n if (!response.ok) {\r\n const body = await response.text().catch(() => '')\r\n return err(AuthErrors.TokenExchangeFailed(), { params: { body, status: response.status } })\r\n }\r\n\r\n const tokenInfo = await config.parser.parse(response)\r\n if (!tokenInfo.token) {\r\n return err(AuthErrors.TokenExchangeFailed({ message: 'No access token in response' }))\r\n }\r\n\r\n if (config.refreshStorage && tokenInfo.refreshToken) {\r\n await config.refreshStorage.set(tokenInfo.refreshToken)\r\n }\r\n\r\n return ok(tokenInfo)\r\n } catch (error) {\r\n return err(RequestErrors.NetworkError({ message: String(error) }))\r\n }\r\n }\r\n }\r\n\r\n // Merge custom methods if provided\r\n if (config.customMethods) {\r\n return {\r\n ...baseProvider,\r\n ...config.customMethods\r\n } as TokenProvider\r\n }\r\n\r\n return baseProvider as TokenProvider\r\n}\r\n","import type { RefreshTokenStorage, StorageErrorCallback } from '../../types'\r\n\r\n/**\r\n * IndexedDB storage options\r\n */\r\nexport interface IndexedDBStorageOptions {\r\n /** Database name (default: 'FetchGuardDB') */\r\n dbName?: string\r\n /** Key for refresh token (default: 'refreshToken') */\r\n refreshTokenKey?: string\r\n /**\r\n * Error callback for debugging storage failures\r\n * Called when IndexedDB operations fail (quota exceeded, permission denied, etc.)\r\n * Storage still fails closed (returns null), but this allows logging/debugging.\r\n */\r\n onError?: StorageErrorCallback\r\n}\r\n\r\n/**\r\n * IndexedDB storage - only stores refresh token in IndexedDB\r\n * Suitable for body-based refresh strategy\r\n * Persists refresh token for reuse after reload\r\n *\r\n * @param options - Storage options or legacy dbName string\r\n * @param legacyRefreshTokenKey - Legacy refreshTokenKey (for backward compatibility)\r\n */\r\nexport function createIndexedDBStorage(\r\n options: IndexedDBStorageOptions | string = 'FetchGuardDB',\r\n legacyRefreshTokenKey?: string\r\n): RefreshTokenStorage {\r\n // Support both new options object and legacy string arguments\r\n const config: Required<Omit<IndexedDBStorageOptions, 'onError'>> & Pick<IndexedDBStorageOptions, 'onError'> =\r\n typeof options === 'string'\r\n ? { dbName: options, refreshTokenKey: legacyRefreshTokenKey ?? 'refreshToken', onError: undefined }\r\n : {\r\n dbName: options.dbName ?? 'FetchGuardDB',\r\n refreshTokenKey: options.refreshTokenKey ?? 'refreshToken',\r\n onError: options.onError\r\n }\r\n\r\n const { dbName, refreshTokenKey, onError } = config\r\n const storeName = 'tokens'\r\n\r\n const openDB = (): Promise<IDBDatabase> => {\r\n return new Promise((resolve, reject) => {\r\n const request = indexedDB.open(dbName, 1)\r\n\r\n request.onerror = () => reject(request.error)\r\n request.onsuccess = () => resolve(request.result)\r\n\r\n request.onupgradeneeded = (event) => {\r\n const db = (event.target as IDBOpenDBRequest).result\r\n if (!db.objectStoreNames.contains(storeName)) {\r\n const store = db.createObjectStore(storeName, { keyPath: 'key' })\r\n store.createIndex('timestamp', 'timestamp', { unique: false })\r\n }\r\n }\r\n })\r\n }\r\n\r\n const promisifyRequest = <T>(request: IDBRequest<T>): Promise<T> => {\r\n return new Promise((resolve, reject) => {\r\n request.onsuccess = () => resolve(request.result)\r\n request.onerror = () => reject(request.error)\r\n })\r\n }\r\n\r\n return {\r\n async get() {\r\n try {\r\n const db = await openDB()\r\n const transaction = db.transaction([storeName], 'readonly')\r\n const store = transaction.objectStore(storeName)\r\n const result = await promisifyRequest(store.get(refreshTokenKey))\r\n return result?.value || null\r\n } catch (error) {\r\n onError?.(error as Error, 'get')\r\n return null\r\n }\r\n },\r\n async set(token) {\r\n try {\r\n const db = await openDB()\r\n const transaction = db.transaction([storeName], 'readwrite')\r\n const store = transaction.objectStore(storeName)\r\n\r\n if (token) {\r\n await promisifyRequest(store.put({ key: refreshTokenKey, value: token, timestamp: Date.now() }))\r\n } else {\r\n await promisifyRequest(store.delete(refreshTokenKey))\r\n }\r\n } catch (error) {\r\n onError?.(error as Error, token ? 'set' : 'delete')\r\n }\r\n }\r\n }\r\n}\r\n","/**\r\n * Normalize expiresAt to milliseconds timestamp\r\n * Supports: milliseconds, seconds, ISO string, null/undefined\r\n */\r\nexport function normalizeExpiresAt(value: unknown): number | undefined {\r\n if (value == null) return undefined\r\n\r\n if (typeof value === 'number') {\r\n // Detect seconds vs milliseconds:\r\n // - Milliseconds: 13+ digits (e.g., 1767860146000)\r\n // - Seconds: 10 digits (e.g., 1767860146)\r\n // Threshold: 10^12 (Sep 2001 in ms, year 33658 in seconds)\r\n return value < 1e12 ? value * 1000 : value\r\n }\r\n\r\n if (typeof value === 'string') {\r\n const ts = Date.parse(value)\r\n return isNaN(ts) ? undefined : ts\r\n }\r\n\r\n return undefined\r\n}\r\n","import type { TokenParser } from '../../types'\r\nimport { normalizeExpiresAt } from './normalize'\r\n\r\n/**\r\n * Body parser - parse token from response body (JSON)\r\n * Expects response format: { data: { accessToken, refreshToken, expiresAt?, user? } }\r\n */\r\nexport const bodyParser: TokenParser = {\r\n async parse(response) {\r\n const json = await response.clone().json()\r\n return {\r\n token: json.data.accessToken,\r\n refreshToken: json.data.refreshToken,\r\n expiresAt: normalizeExpiresAt(json.data.expiresAt),\r\n user: json.data.user\r\n }\r\n }\r\n}\r\n","import type { TokenParser } from '../../types'\r\nimport { normalizeExpiresAt } from './normalize'\r\n\r\n/**\r\n * Cookie parser - parse access token from response body\r\n * Expects response format: { data: { accessToken, expiresAt?, user? } }\r\n * Refresh token is automatically set by backend into httpOnly cookie\r\n */\r\nexport const cookieParser: TokenParser = {\r\n async parse(response) {\r\n const json = await response.clone().json()\r\n return {\r\n token: json.data.accessToken,\r\n expiresAt: normalizeExpiresAt(json.data.expiresAt),\r\n user: json.data.user\r\n }\r\n }\r\n}\r\n","import type { AuthStrategy, ExchangeTokenOptions } from '../../types'\r\n\r\n/**\r\n * Cookie auth strategy - all auth operations via httpOnly cookies\r\n * Suitable for SSR and cross-domain authentication\r\n *\r\n * Refresh token is sent automatically via httpOnly cookie\r\n * Credentials are sent in request body\r\n *\r\n * Login URL can be:\r\n * - Configured once: loginUrl: 'https://api.example.com/auth/login'\r\n * - Passed per call: login(payload, 'https://...')\r\n *\r\n * Header priority (lowest to highest):\r\n * - defaultHeaders (from FetchGuardOptions)\r\n * - headers (from ProviderPresetConfig)\r\n * - Content-Type: application/json\r\n */\r\nexport function createCookieStrategy(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n headers?: Record<string, string>\r\n defaultHeaders?: Record<string, string>\r\n}): AuthStrategy {\r\n const baseHeaders = {\r\n ...config.defaultHeaders,\r\n ...config.headers,\r\n 'Content-Type': 'application/json'\r\n }\r\n\r\n return {\r\n async refresh() {\r\n return fetch(config.refreshUrl, {\r\n method: 'POST',\r\n headers: baseHeaders,\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async login(payload, url) {\r\n return fetch(url || config.loginUrl, {\r\n method: 'POST',\r\n headers: baseHeaders,\r\n body: JSON.stringify(payload),\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async logout(payload) {\r\n return fetch(config.logoutUrl, {\r\n method: 'POST',\r\n headers: baseHeaders,\r\n body: payload ? JSON.stringify(payload) : undefined,\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async exchangeToken(accessToken: string, url: string, options: ExchangeTokenOptions = {}) {\r\n const { method = 'POST', payload, headers } = options\r\n return fetch(url, {\r\n method,\r\n headers: {\r\n ...baseHeaders,\r\n ...headers,\r\n 'Authorization': `Bearer ${accessToken}`\r\n },\r\n body: payload ? JSON.stringify(payload) : undefined,\r\n credentials: 'include'\r\n })\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Standard cookie strategy\r\n */\r\nexport const cookieStrategy = createCookieStrategy({\r\n refreshUrl: '/auth/refresh',\r\n loginUrl: '/auth/login',\r\n logoutUrl: '/auth/logout'\r\n})\r\n","import type { AuthStrategy, ExchangeTokenOptions } from '../../types'\r\n\r\n/**\r\n * Body auth strategy - all auth operations via request body\r\n * Suitable for SPA applications\r\n *\r\n * All tokens/credentials are sent in request body\r\n *\r\n * Login URL can be:\r\n * - Configured once: loginUrl: 'https://api.example.com/auth/login'\r\n * - Passed per call: login(payload, 'https://...')\r\n *\r\n * Header priority (lowest to highest):\r\n * - defaultHeaders (from FetchGuardOptions)\r\n * - headers (from ProviderPresetConfig)\r\n * - Content-Type: application/json\r\n */\r\nexport function createBodyStrategy(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n headers?: Record<string, string>\r\n defaultHeaders?: Record<string, string>\r\n}): AuthStrategy {\r\n const baseHeaders = {\r\n ...config.defaultHeaders,\r\n ...config.headers,\r\n 'Content-Type': 'application/json'\r\n }\r\n\r\n return {\r\n async refresh(refreshToken) {\r\n if (!refreshToken) {\r\n throw new Error('No refresh token available')\r\n }\r\n\r\n return fetch(config.refreshUrl, {\r\n method: 'POST',\r\n headers: baseHeaders,\r\n body: JSON.stringify({ refreshToken }),\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async login(payload, url) {\r\n return fetch(url || config.loginUrl, {\r\n method: 'POST',\r\n headers: baseHeaders,\r\n body: JSON.stringify(payload),\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async logout(payload) {\r\n return fetch(config.logoutUrl, {\r\n method: 'POST',\r\n headers: baseHeaders,\r\n body: payload ? JSON.stringify(payload) : undefined,\r\n credentials: 'include'\r\n })\r\n },\r\n\r\n async exchangeToken(accessToken: string, url: string, options: ExchangeTokenOptions = {}) {\r\n const { method = 'POST', payload, headers } = options\r\n return fetch(url, {\r\n method,\r\n headers: {\r\n ...baseHeaders,\r\n ...headers,\r\n 'Authorization': `Bearer ${accessToken}`\r\n },\r\n body: payload ? JSON.stringify(payload) : undefined,\r\n credentials: 'include'\r\n })\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Standard body strategy\r\n */\r\nexport const bodyStrategy = createBodyStrategy({\r\n refreshUrl: '/auth/refresh',\r\n loginUrl: '/auth/login',\r\n logoutUrl: '/auth/logout'\r\n})\r\n","import { createProvider } from './create-provider'\r\nimport { createIndexedDBStorage } from './storage/indexeddb'\r\nimport { bodyParser } from './parser/body'\r\nimport { cookieParser } from './parser/cookie'\r\nimport { createCookieStrategy } from './strategy/cookie'\r\nimport { createBodyStrategy } from './strategy/body'\r\nimport type { TokenProvider } from '../types'\r\n\r\n/**\r\n * Cookie Provider - uses httpOnly cookies\r\n * Suitable for SSR and cross-domain authentication\r\n *\r\n * Access token: Worker memory\r\n * Refresh token: httpOnly cookie (managed by backend)\r\n */\r\nexport function createCookieProvider(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n headers?: Record<string, string>\r\n defaultHeaders?: Record<string, string>\r\n}): TokenProvider {\r\n return createProvider({\r\n refreshStorage: undefined,\r\n parser: cookieParser,\r\n strategy: createCookieStrategy(config)\r\n })\r\n}\r\n\r\n/**\r\n * Body Provider - refresh token in response body, persisted to IndexedDB\r\n * Suitable for SPA applications\r\n *\r\n * Access token: Worker memory\r\n * Refresh token: IndexedDB (persists across reload)\r\n */\r\nexport function createBodyProvider(config: {\r\n refreshUrl: string\r\n loginUrl: string\r\n logoutUrl: string\r\n refreshTokenKey?: string\r\n headers?: Record<string, string>\r\n defaultHeaders?: Record<string, string>\r\n}): TokenProvider {\r\n return createProvider({\r\n refreshStorage: createIndexedDBStorage('FetchGuardDB', config.refreshTokenKey || 'refreshToken'),\r\n parser: bodyParser,\r\n strategy: createBodyStrategy(config)\r\n })\r\n}\r\n","/**\r\n * Binary data utilities for Worker\r\n * Handles ArrayBuffer <-> Base64 conversion for binary responses\r\n */\r\n\r\n/**\r\n * Convert ArrayBuffer to base64 string\r\n * Used in worker to encode binary responses for postMessage transfer\r\n */\r\nexport function arrayBufferToBase64(buffer: ArrayBuffer): string {\r\n const bytes = new Uint8Array(buffer)\r\n let binary = ''\r\n for (let i = 0; i < bytes.length; i++) {\r\n binary += String.fromCharCode(bytes[i])\r\n }\r\n return btoa(binary)\r\n}\r\n\r\n/**\r\n * Convert base64 string to ArrayBuffer\r\n * Used in client to decode binary responses\r\n */\r\nexport function base64ToArrayBuffer(base64: string): ArrayBuffer {\r\n const binary = atob(base64)\r\n const bytes = new Uint8Array(binary.length)\r\n for (let i = 0; i < binary.length; i++) {\r\n bytes[i] = binary.charCodeAt(i)\r\n }\r\n return bytes.buffer\r\n}\r\n\r\n/**\r\n * Check if content type is binary (should be base64 encoded)\r\n * Returns true for images, PDFs, videos, etc.\r\n */\r\nexport function isBinaryContentType(contentType: string): boolean {\r\n const normalized = contentType.toLowerCase()\r\n\r\n // Text types - NOT binary\r\n if (normalized.includes('text/')) return false\r\n if (normalized.includes('json')) return false\r\n if (normalized.includes('xml')) return false\r\n if (normalized.includes('javascript')) return false\r\n if (normalized.includes('ecmascript')) return false\r\n if (normalized.includes('html')) return false\r\n\r\n // Everything else is considered binary\r\n return true\r\n}\r\n","/**\r\n * Helper functions for common Result patterns\r\n *\r\n * These utilities simplify working with FetchGuard's Result-based API\r\n * by providing type-safe helpers for common operations.\r\n */\r\n\r\nimport type { Result } from 'ts-micro-result'\r\nimport type { FetchEnvelope } from './types'\r\n\r\n/**\r\n * Check if result is a network/transport error (not an HTTP response)\r\n *\r\n * @example\r\n * const result = await api.fetch('/users')\r\n * if (isNetworkError(result)) {\r\n * console.error('Network failed:', result.errors[0]?.message)\r\n * }\r\n */\r\nexport function isNetworkError(result: Result<FetchEnvelope>): result is Result<FetchEnvelope> & { ok: false } {\r\n return !result.ok\r\n}\r\n\r\n/**\r\n * Check if response is successful (2xx status)\r\n *\r\n * @example\r\n * if (isSuccess(result)) {\r\n * const data = parseJson(result)\r\n * }\r\n */\r\nexport function isSuccess(result: Result<FetchEnvelope>): boolean {\r\n return result.ok && result.data.status >= 200 && result.data.status < 300\r\n}\r\n\r\n/**\r\n * Check if response is a client error (4xx status)\r\n *\r\n * @example\r\n * if (isClientError(result)) {\r\n * console.error('Bad request:', getErrorMessage(result))\r\n * }\r\n */\r\nexport function isClientError(result: Result<FetchEnvelope>): boolean {\r\n return result.ok && result.data.status >= 400 && result.data.status < 500\r\n}\r\n\r\n/**\r\n * Check if response is a server error (5xx status)\r\n *\r\n * @example\r\n * if (isServerError(result)) {\r\n * // Maybe retry?\r\n * }\r\n */\r\nexport function isServerError(result: Result<FetchEnvelope>): boolean {\r\n return result.ok && result.data.status >= 500\r\n}\r\n\r\n/**\r\n * Parse JSON body safely with optional type inference\r\n *\r\n * Returns null if:\r\n * - Result is a network error (no response)\r\n * - Body is not valid JSON\r\n *\r\n * @example\r\n * const users = parseJson<User[]>(result)\r\n * if (users) {\r\n * // Use users array\r\n * }\r\n */\r\nexport function parseJson<T = unknown>(result: Result<FetchEnvelope>): T | null {\r\n if (!result.ok) return null\r\n try {\r\n return JSON.parse(result.data.body) as T\r\n } catch {\r\n return null\r\n }\r\n}\r\n\r\n/**\r\n * Get human-readable error message from result\r\n *\r\n * For network errors: returns the error message\r\n * For HTTP errors: tries to parse message from body, falls back to status code\r\n *\r\n * @example\r\n * if (!isSuccess(result)) {\r\n * toast.error(getErrorMessage(result))\r\n * }\r\n */\r\nexport function getErrorMessage(result: Result<FetchEnvelope>): string {\r\n if (result.ok) {\r\n // HTTP error - try to parse message from body\r\n try {\r\n const body = JSON.parse(result.data.body)\r\n return body.message || body.error || `HTTP ${result.data.status}`\r\n } catch {\r\n return `HTTP ${result.data.status}`\r\n }\r\n }\r\n // Network error\r\n return result.errors[0]?.message || 'Unknown error'\r\n}\r\n\r\n/**\r\n * Get error body with type safety (best-effort parsing)\r\n *\r\n * NOTE: This is best-effort parsing. The error body comes from the server\r\n * and may not match the expected shape. Always handle null return and\r\n * validate before using typed properties.\r\n *\r\n * @example\r\n * interface ApiError {\r\n * code: string\r\n * message: string\r\n * errors?: { field: string; message: string }[]\r\n * }\r\n *\r\n * const errorBody = getErrorBody<ApiError>(result)\r\n * if (errorBody?.errors) {\r\n * errorBody.errors.forEach(e => console.error(`${e.field}: ${e.message}`))\r\n * }\r\n */\r\nexport function getErrorBody<T = unknown>(result: Result<FetchEnvelope>): T | null {\r\n if (!result.ok) return null\r\n // For HTTP errors (4xx, 5xx), try to parse the body\r\n if (result.data.status >= 400) {\r\n try {\r\n return JSON.parse(result.data.body) as T\r\n } catch {\r\n return null\r\n }\r\n }\r\n return null\r\n}\r\n\r\n/**\r\n * Get the HTTP status code from result\r\n *\r\n * Returns null if result is a network error (no HTTP response)\r\n *\r\n * @example\r\n * const status = getStatus(result)\r\n * if (status === 401) {\r\n * // Redirect to login\r\n * }\r\n */\r\nexport function getStatus(result: Result<FetchEnvelope>): number | null {\r\n return result.ok ? result.data.status : null\r\n}\r\n\r\n/**\r\n * Check if result has a specific HTTP status\r\n *\r\n * @example\r\n * if (hasStatus(result, 404)) {\r\n * console.log('Not found')\r\n * }\r\n */\r\nexport function hasStatus(result: Result<FetchEnvelope>, status: number): boolean {\r\n return result.ok && result.data.status === status\r\n}\r\n\r\n/**\r\n * Match result against multiple handlers\r\n *\r\n * @example\r\n * matchResult(result, {\r\n * success: (data) => console.log('Success:', data),\r\n * clientError: (data) => console.error('Client error:', data.status),\r\n * serverError: (data) => console.error('Server error:', data.status),\r\n * networkError: (errors) => console.error('Network:', errors[0]?.message)\r\n * })\r\n */\r\nexport function matchResult<T>(\r\n result: Result<FetchEnvelope>,\r\n handlers: {\r\n success?: (data: FetchEnvelope) => T\r\n clientError?: (data: FetchEnvelope) => T\r\n serverError?: (data: FetchEnvelope) => T\r\n networkError?: (errors: readonly { code: string; message: string }[]) => T\r\n }\r\n): T | undefined {\r\n if (!result.ok) {\r\n return handlers.networkError?.(result.errors)\r\n }\r\n\r\n const status = result.data.status\r\n\r\n if (status >= 200 && status < 300) {\r\n return handlers.success?.(result.data)\r\n }\r\n\r\n if (status >= 400 && status < 500) {\r\n return handlers.clientError?.(result.data)\r\n }\r\n\r\n if (status >= 500) {\r\n return handlers.serverError?.(result.data)\r\n }\r\n\r\n return undefined\r\n}\r\n"],"mappings":"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","names":["ok","err","err","ok"]}
|