/** * Serializable HTTP request — POJO version of Angular's HttpRequest. * Structured-clone safe: no classes, no functions, no prototype chains. */ interface SerializableRequest { method: string; url: string; headers: Record; params: Record; body: unknown; responseType: 'json' | 'text' | 'blob' | 'arraybuffer'; withCredentials: boolean; context: Record; } /** * Serializable HTTP response — POJO version of Angular's HttpResponse. */ interface SerializableResponse { status: number; statusText: string; headers: Record; body: unknown; url: string; } /** * Pure-function interceptor that runs inside a web worker. * * Constraints: * - No `inject()` — Angular DI does not exist in the worker * - No DOM access — workers have no `document` or `window` * - No closures over external mutable state * - Must be serialization-safe (bundled at build time, not transferred at runtime) */ type WorkerInterceptorFn = (req: SerializableRequest, next: (req: SerializableRequest) => Promise) => Promise; /** * Configuration for the retry interceptor. */ interface RetryConfig { /** Maximum number of retries (default: 3) */ maxRetries?: number; /** Initial delay in ms (default: 1000) */ initialDelay?: number; /** Backoff multiplier (default: 2) */ backoffMultiplier?: number; /** HTTP status codes that should trigger a retry (default: [408, 429, 500, 502, 503, 504]) */ retryStatusCodes?: number[]; /** Whether to retry on network errors (fetch throws) (default: true) */ retryOnNetworkError?: boolean; } /** * Configuration for the cache interceptor. */ interface CacheConfig { /** TTL in ms (default: 60000 = 1 min) */ ttl?: number; /** Maximum number of cached entries (default: 100) */ maxEntries?: number; /** HTTP methods to cache (default: ['GET']) */ methods?: string[]; } /** * Configuration for the HMAC signing interceptor. */ interface HmacInterceptorConfig { /** Raw key material for HMAC signing */ keyMaterial: ArrayBuffer | Uint8Array; /** Hash algorithm (default: 'SHA-256') */ algorithm?: 'SHA-256' | 'SHA-384' | 'SHA-512'; /** Header name for the signature (default: 'X-HMAC-Signature') */ headerName?: string; /** Function to build the signing payload from the request (default: `${method}:${url}:${body}`) */ payloadBuilder?: (req: SerializableRequest) => string; } /** * Configuration for the rate limit interceptor. */ interface RateLimitConfig { /** Maximum requests per window (default: 100) */ maxRequests?: number; /** Window size in ms (default: 60000 = 1 min) */ windowMs?: number; } /** * Configuration for the logging interceptor. */ interface LoggingConfig { /** Custom logger function (default: console.log) */ logger?: (message: string, data?: unknown) => void; /** Whether to include request/response headers in logs (default: false) */ includeHeaders?: boolean; } /** * Configuration for the content integrity interceptor. */ interface ContentIntegrityConfig { /** Hash algorithm to use (default: 'SHA-256') */ algorithm?: 'SHA-256' | 'SHA-384' | 'SHA-512'; /** Response header containing the expected hash (default: 'X-Content-Hash') */ headerName?: string; /** Throw if the integrity header is absent (default: false) */ requireHash?: boolean; } /** * Creates and registers a worker-side HTTP pipeline. * * Call this inside a worker file to set up the interceptor chain. * The pipeline listens for incoming requests via `postMessage`, * runs them through the interceptor chain, executes `fetch()`, * and sends the response back. * * For a runtime-configurable variant whose chain is built from specs sent * from the main thread, use `createConfigurableWorkerPipeline()` instead. * * @example * ```typescript * // workers/secure.worker.ts * import { createWorkerPipeline, hmacSigningInterceptor } from '@angular-helpers/worker-http/interceptors'; * * createWorkerPipeline([hmacSigningInterceptor({ keyMaterial })]); * ``` */ declare function createWorkerPipeline(interceptors: WorkerInterceptorFn[]): void; /** * Serializable subset of LoggingConfig — `logger` is a function and cannot * cross the worker boundary, so it is dropped here. The configurable pipeline * uses `console.log` inside the worker for built-in logging. */ type SerializableLoggingConfig = Omit; /** * Serializable subset of HmacInterceptorConfig — `payloadBuilder` is a function * and cannot cross the worker boundary. The default payload builder is used. * * `keyMaterial` is `ArrayBuffer | Uint8Array`, both of which ARE supported by * the structured clone algorithm. */ type SerializableHmacConfig = Omit; /** * Discriminated union of interceptor specifications that can be configured * from Angular DI via `withWorkerInterceptors([...])` and forwarded to the * worker over `postMessage`. * * For interceptors with custom function fields (loggers, payload builders), * register the interceptor in the worker file via `registerInterceptor()` and * reference it here with `kind: 'custom'`. */ type WorkerInterceptorSpec = { readonly kind: 'logging'; readonly config?: SerializableLoggingConfig; } | { readonly kind: 'retry'; readonly config?: RetryConfig; } | { readonly kind: 'cache'; readonly config?: CacheConfig; } | { readonly kind: 'hmac-signing'; readonly config: SerializableHmacConfig; } | { readonly kind: 'rate-limit'; readonly config?: RateLimitConfig; } | { readonly kind: 'content-integrity'; readonly config?: ContentIntegrityConfig; } | { readonly kind: 'custom'; readonly name: string; readonly config?: unknown; }; /** * Wire format for the init handshake message sent from the main thread to the * worker. Posted exactly once per worker, before any HTTP request. */ interface WorkerInterceptorInitMessage { readonly type: 'init-interceptors'; readonly specs: readonly WorkerInterceptorSpec[]; } type CustomFactory = (config?: unknown) => WorkerInterceptorFn; /** * Registers a custom interceptor factory that can be referenced from * `withWorkerInterceptors([workerCustom('my-name', config)])`. * * Must be called inside the worker file BEFORE `createConfigurableWorkerPipeline()`. * * @example * ```typescript * // app.worker.ts * import { createConfigurableWorkerPipeline, registerInterceptor } from '@angular-helpers/worker-http/interceptors'; * * registerInterceptor('auth-token', (config: { token: string }) => async (req, next) => { * const headers = { ...req.headers, authorization: [`Bearer ${config.token}`] }; * return next({ ...req, headers }); * }); * * createConfigurableWorkerPipeline(); * ``` */ declare function registerInterceptor(name: string, factory: CustomFactory): void; /** * Resolves a single spec to its concrete `WorkerInterceptorFn`. * * Exported for test use. Throws on unknown `kind` or unregistered custom name * so misconfiguration fails loudly at init time, not on the first request. */ declare function resolveSpec(spec: WorkerInterceptorSpec): WorkerInterceptorFn; /** * Creates a worker-side pipeline whose interceptor chain is supplied at * runtime via the init handshake message sent by `WorkerHttpBackend`. * * Behavior: * - Listens for the first `init-interceptors` message and builds the chain * from the received specs. * - Until init arrives, incoming `request` messages are buffered (they will * be flushed once the chain is ready). * - If no init arrives (e.g. worker used standalone without * `withWorkerInterceptors`), the pipeline runs with an empty chain so * every request goes straight to `fetch()`. * * Custom interceptor factories must be registered with * `registerInterceptor(name, factory)` before this call. */ declare function createConfigurableWorkerPipeline(): void; type RequestHandler = (req: SerializableRequest, signal?: AbortSignal) => Promise; /** * Wires up the worker's request handler around a built request chain. * * Automatically detects if running in a Dedicated Worker or Shared Worker context * and attaches the appropriate listeners. */ declare function attachRequestLoop(chain: RequestHandler): () => void; /** * Common loop logic for handling requests/cancellation on a port. */ declare function attachPortLoop(port: MessagePort | any, chain: RequestHandler): () => void; /** * Creates a retry interceptor with exponential backoff. * * Retries requests that fail with specific HTTP status codes. * Respects the `Retry-After` response header when present. * * @example * ```typescript * createWorkerPipeline([ * retryInterceptor({ maxRetries: 3, initialDelay: 500 }), * ]); * ``` */ declare function retryInterceptor(config?: RetryConfig): WorkerInterceptorFn; /** * Creates a cache interceptor that stores responses in worker memory. * * Caches GET responses by default (configurable). Evicts oldest entry * when `maxEntries` is reached (insertion-order eviction). * Each factory call creates an independent cache instance. * * @example * ```typescript * createWorkerPipeline([ * cacheInterceptor({ ttl: 30000, maxEntries: 50 }), * ]); * ``` */ declare function cacheInterceptor(config?: CacheConfig): WorkerInterceptorFn; /** * Creates an HMAC signing interceptor that adds a signature header to outgoing requests. * * Uses WebCrypto `SubtleCrypto` (native in web workers). The `CryptoKey` is * imported lazily on the first request and reused for all subsequent requests * in the same factory instance. * * @example * ```typescript * createWorkerPipeline([ * hmacSigningInterceptor({ * keyMaterial: new TextEncoder().encode('my-secret-key'), * algorithm: 'SHA-256', * headerName: 'X-HMAC-Signature', * }), * ]); * ``` */ declare function hmacSigningInterceptor(config: HmacInterceptorConfig): WorkerInterceptorFn; /** * Creates a logging interceptor that logs request and response details. * * Uses `console.log` by default. A custom logger can be provided via config. * Logger exceptions are swallowed — a logging failure never interrupts the pipeline. * * @example * ```typescript * createWorkerPipeline([ * loggingInterceptor({ includeHeaders: true }), * ]); * ``` */ declare function loggingInterceptor(config?: LoggingConfig): WorkerInterceptorFn; /** * Creates a client-side rate limiting interceptor using a sliding window algorithm. * * Tracks request timestamps within the configured window. When the limit is * exceeded, throws an error with status 429. State is per-factory-instance * (resets when the worker is terminated). * * @example * ```typescript * createWorkerPipeline([ * rateLimitInterceptor({ maxRequests: 10, windowMs: 5000 }), * ]); * ``` */ declare function rateLimitInterceptor(config?: RateLimitConfig): WorkerInterceptorFn; /** * Creates a content integrity interceptor that verifies response body integrity * against a hash provided in a response header. * * Uses WebCrypto `SubtleCrypto` (native in web workers). * * @example * ```typescript * createWorkerPipeline([ * contentIntegrityInterceptor({ * algorithm: 'SHA-256', * headerName: 'X-Content-Hash', * requireHash: true, * }), * ]); * ``` */ declare function contentIntegrityInterceptor(config?: ContentIntegrityConfig): WorkerInterceptorFn; /** * Composes multiple worker interceptors into a single `WorkerInterceptorFn`. * * Interceptors are executed left-to-right, matching Angular's interceptor chain convention. * Each interceptor calls `next()` to pass control to the next one. * * @example * ```typescript * const pipeline = composeInterceptors( * loggingInterceptor(), * retryInterceptor({ maxRetries: 2 }), * hmacSigningInterceptor({ keyMaterial: key }), * ); * * createWorkerPipeline([pipeline]); * ``` */ declare function composeInterceptors(...fns: WorkerInterceptorFn[]): WorkerInterceptorFn; /** * Spec builders — pure factories that return POJO `WorkerInterceptorSpec` * objects suitable for `withWorkerInterceptors([...])`. * * Each builder mirrors the corresponding worker-side interceptor factory but * accepts only serializable config (no function fields). */ declare function workerLogging(config?: SerializableLoggingConfig): WorkerInterceptorSpec; declare function workerRetry(config?: RetryConfig): WorkerInterceptorSpec; declare function workerCache(config?: CacheConfig): WorkerInterceptorSpec; declare function workerHmacSigning(config: SerializableHmacConfig): WorkerInterceptorSpec; declare function workerRateLimit(config?: RateLimitConfig): WorkerInterceptorSpec; declare function workerContentIntegrity(config?: ContentIntegrityConfig): WorkerInterceptorSpec; /** * Reference a custom interceptor that has been registered on the worker side * via `registerInterceptor(name, factory)`. */ declare function workerCustom(name: string, config?: unknown): WorkerInterceptorSpec; interface OfflineCacheConfig { /** Cache strategy: 'network-first' (default) or 'cache-first' (stale-while-revalidate) */ strategy?: 'network-first' | 'cache-first'; /** Partition name in Cache API (default: 'ah-http-offline-cache') */ cacheName?: string; /** Cache Time to Live (TTL) in milliseconds (default: 24 hours) */ ttl?: number; /** Custom header to completely bypass this offline cache */ bypassHeader?: string; } /** * Creates an offline cache interceptor that leverages native Cache API in the worker thread. * Only caches GET requests. * * @param config Optional configuration for the offline cache. */ declare function offlineCacheInterceptor(config?: OfflineCacheConfig): WorkerInterceptorFn; interface OfflineSyncConfig { /** Database name for IndexedDB (default: 'ah_offline_sync') */ dbName?: string; /** Custom header to completely bypass the offline sync queue */ bypassHeader?: string; } /** * Creates an offline synchronization queue interceptor that persists mutating requests * (POST, PUT, PATCH, DELETE) to IndexedDB while offline, and replays them in FIFO order * when online. * * @param config Optional configuration for the offline sync queue. */ declare function offlineSyncQueueInterceptor(config?: OfflineSyncConfig): WorkerInterceptorFn; export { attachPortLoop, attachRequestLoop, cacheInterceptor, composeInterceptors, contentIntegrityInterceptor, createConfigurableWorkerPipeline, createWorkerPipeline, hmacSigningInterceptor, loggingInterceptor, offlineCacheInterceptor, offlineSyncQueueInterceptor, rateLimitInterceptor, registerInterceptor, resolveSpec, retryInterceptor, workerCache, workerContentIntegrity, workerCustom, workerHmacSigning, workerLogging, workerRateLimit, workerRetry }; export type { CacheConfig, ContentIntegrityConfig, HmacInterceptorConfig, LoggingConfig, OfflineCacheConfig, OfflineSyncConfig, RateLimitConfig, RetryConfig, SerializableHmacConfig, SerializableLoggingConfig, SerializableRequest, SerializableResponse, WorkerInterceptorFn, WorkerInterceptorInitMessage, WorkerInterceptorSpec };