import type { CandidateIdentity, SessionEvent } from "@a4anthony/proctorkit-types"; import type { EventQueue } from "../queue/event-queue.js"; export interface UploaderOptions { queue: EventQueue; ingestUrl: string; sessionId: string; appId?: string; candidate?: () => CandidateIdentity | undefined; batchSize?: number; batchIntervalMs?: number; maxRetries?: number; fetchImpl?: typeof fetch; newBatchId?: () => string; } export interface UploaderEvents { onUploaded?: (batchId: string, count: number, accepted: string[], rejected: string[]) => void; onUploadFailed?: (batchId: string, attempt: number, error: Error) => void; } /** * Drains an {@link EventQueue} to an HTTP ingest endpoint. * * Each batch carries a stable `batchId` in both the body and the * `idempotency-key` header so the server can dedupe retries. Events * are acked locally only after the server confirms them in `accepted` * (or rejects them as permanently bad in `rejected`). * * Retries 5xx, 408 and 429 with exponential backoff and jitter, up to * `maxRetries`. Request-level 4xx responses leave the queue intact; only * event IDs explicitly returned in `accepted` or `rejected` are acknowledged. * Aborts in-flight requests on `stop()`. * * `flush({ keepalive: true })` is the one-shot path used on `pagehide`: * it uses `fetch keepalive` so the request survives navigation, and * does not retry — the page is about to be gone. */ export declare class Uploader { private readonly opts; private events; private timer; private draining; private stopped; private controller; constructor(options: UploaderOptions); /** Replaces the lifecycle handlers. The Uploader keeps a single set; pass `{}` to clear. */ on(events: UploaderEvents): void; /** Starts the periodic drain timer. No-op if already started or stopped. */ start(): void; /** * Drains the queue in batches until empty, no progress was made, or * a send fails. Re-entrant calls are guarded — only one drain runs * at a time even under concurrent invocations. */ drain(): Promise; /** * Sends one batch. With `keepalive: true` the request survives page * navigation but is capped by the browser to ~64KB total in-flight; * we therefore send at most one batch and do not retry on failure. */ flush(options: { keepalive: boolean; }): Promise; /** * Prove that the real ingest route accepts an event before SDK readiness is * announced. This uses the same request builder, auth headers, response * validation, and retry path as normal queue delivery. It is intentionally * not configurable by SDK consumers. */ verifyDelivery(event: SessionEvent, timeoutMs: number): Promise; /** Stops the drain timer and aborts any in-flight non-keepalive request. */ stop(): Promise; private sendBatch; } //# sourceMappingURL=uploader.d.ts.map