import { type TelemetryHttpResult } from "./uploader"; /** * Telemetry transport policy, browser subset (qfg-y8je.11; policy P1-P10 in * project/plans/2026-09-24-sdk-telemetry-transport-policy.md, contract tests * in integration-test-data/chaos/telemetry-transport-contract.md). * * Mirrors sdk-node's src/telemetry/transportQueue.ts. This module owns the * retained queue of serialized batches, the send gate (30s floor after a * failure + Retry-After), the drain loop, disable-on-auth and the P7 logging * episodes. It knows nothing about the aggregator or payload shape: it stores * and resends opaque strings. Retention is in memory for the life of the page. */ /** Shipped defaults for the `telemetry*` init options (browser SDK class). */ export declare const TELEMETRY_DEFAULTS: { readonly flushIntervalMs: 30000; readonly timeoutMs: 10000; readonly maxRetainedBatches: 5; readonly maxRetainedBytes: number; readonly maxRetainedAgeMs: 300000; readonly maxEvaluationSummaries: 10000; }; /** No send sooner than this after a failed POST (P4). */ export declare const RESEND_FLOOR_MS = 30000; /** Retry-After is honored up to this (P4). */ export declare const RETRY_AFTER_CAP_MS = 600000; /** At most one drop WARN per this interval while dropping continues (P7). */ export declare const DROP_WARN_INTERVAL_MS = 600000; /** pagehide / close() give the live window one POST with this deadline (P8, browser). */ export declare const SHUTDOWN_FLUSH_DEADLINE_MS = 2000; /** The four levels the transport logs at (P7). */ export interface TelemetryLogger { debug(message: string): void; info(message: string): void; warn(message: string): void; error(message: string): void; } export type StatusClass = "ok" | "retryable" | "auth" | "rejected"; /** * 2xx -> ok; 401, 403, 404 -> auth; 408, 429, 5xx -> retryable; every other * status (other 4xx, 3xx, 1xx) -> rejected (P3). */ export declare function classifyStatus(status: number): StatusClass; /** * Parse a Retry-After header into a wait in ms: delta-seconds, or an HTTP-date * relative to `nowMs` (past dates -> 0). Unparseable -> undefined. Clamped to * {@link RETRY_AFTER_CAP_MS}. */ export declare function parseRetryAfterMs(header: string | undefined, nowMs: number): number | undefined; /** UTF-8 byte length of a serialized batch (what goes on the wire). */ export declare function byteLength(body: string): number; export type TelemetrySend = (body: string, timeoutMs: number, signal: AbortSignal, keepalive?: boolean) => Promise; export declare class TelemetryTransportQueue { private readonly send; private readonly telemetryUrl; private readonly logger; private readonly timeoutMs; private readonly maxRetainedBatches; private readonly maxRetainedBytes; private readonly maxRetainedAgeMs; private readonly onDisabled; private queue; private inFlight; private idle; private lastFailureAt; private retryAfterUntil; private isDisabled; private failuresSinceSuccess; private firstFailureAt; private lastResult; private lastDropWarnAt; private dropsSinceWarn; private dropsThisOutage; private lastRejectErrorAt; private rejectsSinceError; constructor(args: { send: TelemetrySend; telemetryUrl: string; logger: TelemetryLogger; timeoutMs: number; maxRetainedBatches: number; maxRetainedBytes: number; maxRetainedAgeMs: number; onDisabled: () => void; }); /** A POST is in flight. */ get busy(): boolean; get disabled(): boolean; /** Every queued batch, including a not-yet-sent oversize one. */ get retainedCount(): number; get retainedBytes(): number; /** Discard batches older than the max age (strictly greater). Tick step 2. */ expire(): void; /** The 30s floor after a failure and any Retry-After have both elapsed. Tick step 3. */ sendAllowed(): boolean; /** Append a serialized window and enforce the caps (drop oldest). Tick step 4. */ append(body: string): void; /** * POST queued batches oldest-first, one at a time; stop at the first * failure. Tick step 5. Never rejects. */ drain(): Promise; /** Resolves when no drain is running. */ whenIdle(): Promise; /** Abort the in-flight POST, if any (close()). The aborted batch is kept but never resent. */ abortInFlight(): void; /** * pagehide / close(): one POST of the live window bounded by `deadlineMs`, * sent with `keepalive` so it can outlive the page. Never retains, never * touches the outage episode, never throws. Does not take the one-in-flight * slot: on pagehide the page is going away and the regular POST, if any, is * already accounted for. */ sendFinal(body: string, deadlineMs: number): Promise; private drainLoop; private onSuccess; private onRetryableFailure; private disable; private onRejected; private recordDrop; }