/** * Main-thread uploader for screen-recording chunks. Separate from the * event Uploader (which runs in the worker) because chunks are large * binary blobs that we don't want to structured-clone across the * worker boundary on every timeslice. * * Architecture: * - Single in-flight POST at a time per chunk number (chunks upload * in order). If a chunk's POST is in flight when the next chunk * arrives, the new one queues. * - On 5xx / network error: exponential backoff retry up to maxRetries. * - On 4xx: drop and emit chunk-dropped (the chunk isn't valid; * no point retrying). * - Byte cap on the queue: when queued bytes + the new chunk would * exceed maxQueueBytes, drop the oldest queued chunk (FIFO) and * emit chunk-dropped("overflow") until we're under budget. * - On stop(): abort the in-flight request and clear the queue. */ export interface ChunkUploaderConfig { sessionId: string; /** * The events ingest URL (eg. `http://localhost:3001/ingest`). The * uploader derives the origin from this and joins it with * `pathTemplate`. */ ingestUrl: string; /** * URL path template under the ingest origin. Supports `{sessionId}` * and `{n}` placeholders (n is the chunk number). Default: * `/recordings/sessions/{sessionId}/chunks/{n}` — used for screen * share. Webcam photos use a different path; the WebcamObserver * passes its own template. */ pathTemplate?: string; /** Public app key (`pk_live_…`) for org routing. */ appId?: string; /** * Per-device fingerprint stamped by the SDK at construction * time. Sent on every chunk upload via `x-fingerprint-id` so * the server can attribute chunks to the device that * uploaded them (per-device counters live further down the * road; today this is just attribution). */ fingerprintId?: string; /** * Max bytes of queued + in-flight chunks held in memory. When * exceeded, the oldest queued chunk is dropped. Default: 50 MB. */ maxQueueBytes?: number; /** Per-chunk retry budget for 5xx / network errors. Default: 5. */ maxRetries?: number; /** Called when a chunk POST returns 2xx. */ onUploaded?: (chunkNumber: number, byteSize: number) => void; /** * Called when a chunk is dropped without being uploaded. `reason`: * - "max-retries": ran out of retries (network or 5xx). * - "rejected": server returned 4xx. * - "overflow": queue hit maxQueueBytes; oldest evicted. * - "aborted": stop() called while this chunk was queued. */ onDropped?: (chunkNumber: number, reason: ChunkDropReason, detail?: ChunkDropDetail) => void; } export type ChunkDropReason = "max-retries" | "rejected" | "overflow" | "aborted"; export interface ChunkDropDetail { httpStatus?: number; serverError?: string; statusText?: string; retryCount?: number; message?: string; timedOut?: boolean; timeoutMs?: number; } export declare class ChunkUploader { private readonly config; private readonly queue; private queuedBytes; private inFlight; private currentAbort; private stopped; constructor(config: ChunkUploaderConfig); /** * Enqueue a chunk for upload. Returns immediately. If the queue is * over budget the oldest queued chunks are dropped to make room. * * `extraHeaders` are merged into the POST request alongside the * mandatory content-type and x-app-id headers. Used by callers that * want to ship per-upload metadata without round-tripping through a * database. */ upload(chunkNumber: number, blob: Blob, extraHeaders?: Record): void; /** * Abort the in-flight request, clear the queue, and emit a dropped * event for everything we couldn't send. Idempotent. */ stop(): void; /** * Wait for the queue to drain — every queued chunk gets uploaded * (or explicitly dropped via the existing retry/4xx/overflow paths) * and no upload is in flight. Does NOT cancel anything; the * complement to `stop()`. Use this on graceful session end so the * tail chunk has a chance to reach the server before we tear down. * * Resolves when `inFlight` is false and `queue.length` is 0. * Rejects if `timeoutMs` elapses first — the caller can then choose * to call `stop()` to give up, accepting that whatever's still * queued will be dropped with reason "aborted". * * Default timeout: 30s. A flaky network can stretch a single chunk * through several backoff attempts (1s + 2s + 4s + 8s + 16s = 31s * worst-case for 5 retries), so the default sits just above that — * one full retry budget before the caller bails. */ drain(timeoutMs?: number): Promise; /** * Internal worker loop: pull the next chunk and POST it. Re-runs * itself after each completion until the queue is empty. */ private processNext; private send; } //# sourceMappingURL=chunk-uploader.d.ts.map