import type { ImageEdgeSample } from "./asset-integrity.js"; export type { ImageEdgeSample } from "./asset-integrity.js"; type CDPSessionLike = { send: (...args: any[]) => Promise; }; type DeviceEmulationPageLike = { setViewportSize: (viewport: { width: number; height: number; }) => Promise; context?: () => { newCDPSession: (page: any) => Promise; }; }; type HydrationPageLike = { evaluate: (fn: (arg: A) => T | Promise, arg: A) => Promise; }; /** * Applies phone input/device characteristics to narrow viewport captures while * preserving browser.newPage() so the remote runtime's URL guard stays attached. * CDP is Chromium-only, so any failure degrades to the historical width-only * viewport and records a warning instead of failing the audit. */ export declare function applyDeviceEmulation(page: DeviceEmulationPageLike, viewport: { w: number; h: number; }, warnings?: string[]): Promise; /** * Waits for custom elements present in the document to upgrade, then waits for * two stable animation frames based on body height, scroll height, and open * shadow-root structure. The bounded wait is advisory: timeouts warn and * measurement continues. */ export declare function waitForCustomElementsHydration(page: HydrationPageLike, warnings: string[], timeoutMs?: number): Promise; export type Theme = "light" | "dark"; export type PageTraits = { source: "live" | "static"; scheme: "light" | "dark" | "mixed" | "unknown"; bg_luminance: number | null; text_density: number | null; section_count: number; image_count: number; video_count: number; canvas_count: number; webgl: boolean | null; backdrop_filter: boolean; animation_count: number | null; scroll_effects: boolean | null; font_families: string[]; max_heading_px: number | null; gradient_count: number; loader_hint: boolean; viewport_fill: number | null; }; export declare class CaptureUnavailableError extends Error { constructor(message?: string); } /** * Wall-clock ceiling for one capture. `timeoutMs` bounds individual Playwright * calls, but the steps after goto — hydration, scroll settle, animation settle, * trait collection — could each wait on a signal a permanently-animating page * never sends, so a capture could block forever with no error. Observed as a * 30-minute silent hang binding apple.com/airpods-pro as a taste reference. */ export declare const CAPTURE_DEADLINE_MS = 90000; export declare class CaptureTimeoutError extends Error { constructor(url: string, deadlineMs: number); } export type VideoArtifactReason = "preload-none" | "autoplay-blocked" | "empty-src" | "decode-error" | "unknown"; export type VideoArtifact = { selector: string; /** The `preload` attribute value on the element (e.g. "none", "auto", "metadata"). */ preload: string; renderedBlank: boolean; /** * Discriminated reason for the blank-video detection. * * - `"preload-none"`: element has preload=none and never buffered (lazy-load pattern — likely not a real defect) * - `"autoplay-blocked"`: play() was rejected with NotAllowedError (browser autoplay policy — likely not a real defect) * - `"empty-src"`: currentSrc is empty or networkState is NETWORK_NO_SOURCE — genuine missing/broken source * - `"decode-error"`: video.error.code is set (MEDIA_ERR_*) — genuine decode/network failure * - `"unknown"`: did not match any specific classification * * @deprecated The legacy value `"unloaded-video-artifact"` is no longer emitted by this library but * may appear in data produced by older versions. Callers should treat it as `"unknown"`. */ reason: VideoArtifactReason | "unloaded-video-artifact"; /** Raw HTMLMediaElement.error.code value (1–4, MEDIA_ERR_* constants), if an error is set. */ errorCode?: number; /** Raw HTMLMediaElement.networkState value at probe time. */ networkState?: number; }; export type CaptureResult = { url: string; renderedHtml: string; screenshotBase64: string; viewport: { w: number; h: number; }; mobile_emulation?: boolean; theme?: Theme; scrolledToBottom: boolean; /** * True when all finite (non-looping) CSS animations/transitions on the page reached * quiescence before capture; false when the settle wait timed out or the browser has * no `document.getAnimations` support (older engines / the file:// no-browser fallback). */ animationsSettled: boolean; /** * Wall-clock milliseconds spent inside the VIEWPORT animation-settle wait, and * nothing else — not launch, not navigation, not the screenshot. It exists so a * test can assert "this page did not consume the settle cap" by measuring the * settle itself; timing the whole `capturePage` call measures the runner's speed * and reports a slow CI box as a settle-cap defect (it did, 2026-07-24: 3967ms * total against a 2800ms bound with the settle wait almost certainly near zero). * * Deliberately NOT the total settle time: the scroll-phase settle inside * `settleScrollReveals` is bounded by the scroll cap and already announces itself * through `scroll-animation-settle-cap-reached`. Summing the two would produce a * number no single cap explains. * * Absent on the file:// fallback, which never runs a settle wait at all. */ viewportAnimationSettleMs?: number; /** Vertical scroll position at the moment the capture was taken. */ captureScrollY: number; /** Capture-integrity warnings that can make downstream audit findings untrustworthy. */ capture_warnings: string[]; videoArtifacts: VideoArtifact[]; imageEdges?: ImageEdgeSample[]; traits?: PageTraits; warnings: string[]; }; export type Interaction = { selector: string; event: "hover" | "click" | "focus"; delay_ms: number; }; export type CaptureOptions = { interactions?: Interaction[]; scroll_settle?: boolean; viewport?: { w: number; h: number; }; theme?: Theme; collectImageEdges?: boolean; collectTraits?: boolean; timeoutMs?: number; /** Maximum time to wait for finite entrance animations. Default 3000ms; hard-capped at 10000ms. */ animation_settle_timeout_ms?: number; /** Wall-clock ceiling for the whole capture. Default {@link CAPTURE_DEADLINE_MS}. */ overall_timeout_ms?: number; }; export declare function capturePage(url: string, opts?: CaptureOptions): Promise; export declare function annotateVideoArtifacts(elements: any[]): VideoArtifact[]; /** Input shape consumed by {@link classifyVideoArtifact}. All fields are optional so that * partial probe data (e.g. from older code paths or static-HTML fallbacks) is still classifiable. */ export type VideoProbeData = { /** `HTMLMediaElement.currentSrc` — empty string when no source is resolved. */ currentSrc?: string; /** `HTMLMediaElement.networkState` — 0=EMPTY, 1=IDLE, 2=LOADING, 3=NO_SOURCE. */ networkState?: number; /** `HTMLMediaElement.error?.code` — set when a media error occurred (MEDIA_ERR_* 1–4). */ errorCode?: number; /** * Name of the DOMException thrown by the play() promise (if any). * "NotAllowedError" → autoplay policy; "NotSupportedError" → unsupported source. */ playRejection?: string; /** Value of the `preload` attribute. */ preload?: string; /** `HTMLMediaElement.readyState` — 0=HAVE_NOTHING … 4=HAVE_ENOUGH_DATA. */ readyState?: number; }; /** * Pure, synchronous classifier that maps raw video probe data to a {@link VideoArtifactReason}. * * Priority (first match wins): * 1. `empty-src` — currentSrc is empty OR networkState === NETWORK_NO_SOURCE (3) * 2. `decode-error` — errorCode is set (1–4) * 3. `autoplay-blocked` — play() rejected with NotAllowedError * 4. `preload-none` — preload attribute is "none" and readyState < 2 * 5. `unknown` — none of the above * * This is exported so it can be unit-tested in isolation without a browser. */ export declare function classifyVideoArtifact(probe: VideoProbeData): VideoArtifactReason; export declare function extractStaticTraits(html: string): PageTraits; export type Verdict = "confirmed" | "likely-artifact" | "inconclusive"; export type VerifiableFinding = { key: string; rule: string; message: string; kind: "issue" | "video-artifact"; selector?: string; /** * When `kind` is `"video-artifact"`, the discriminated reason from the probe. * Used by `verifyFinding` to decide `confirmed` vs `likely-artifact`. */ videoReason?: VideoArtifactReason | "unloaded-video-artifact"; }; export type FindingVerdict = { key: string; verdict: Verdict; evidence: string; warnings?: string[]; }; export type VerifyTarget = { url?: string; html?: string; }; export declare function verifyFindings(target: VerifyTarget, findings: VerifiableFinding[], opts?: { viewport?: { w: number; h: number; }; timeoutMs?: number; }): Promise;