/** * Fetch observation + origin-readiness aggregation. * * Every HTTP request during an audit emits a `FetchObservation` (url, status, * wall-clock duration, whether the cache served or revalidated it). These * observations feed two consumers: * * 1. `computeReadiness` — post-crawl aggregate (median / p95 / 5xx count / * cache-assist ratio) surfaced as the `audit/origin-readiness` finding. * 2. `BackpressureMonitor` (backpressure.ts) — in-flight watchdog that aborts * the audit if the origin degrades under concurrent load. * * Both consumers treat pure cache hits (`fromCache` with no revalidation) * as non-informative about the origin — latency stats should describe the * SERVER, not our local SSD. */ export interface FetchObservation { url: string; status: number; /** Wall-clock duration of the fetch in milliseconds. For revalidated cache * entries this is the 304 round-trip; for uncached fetches it's the full * TTFB + body read. */ durationMs: number; /** True when the cache returned the body without contacting the origin. */ fromCache: boolean; /** True when the cache made a conditional GET that returned 304 Not Modified. * Counts as a cache-assist for the ratio but as a live origin call for * latency purposes. */ revalidated: boolean; /** `Date.now()` when the fetch began. Ordering, not wall-clock accuracy. */ startedAt: number; /** * v0.4: lower-cased response headers. Populated when available so the * auditor can sniff framework markers (`x-powered-by`, `x-vite-*`, etc.) * without a separate probe fetch. Optional to keep memory bounded. */ headers?: Record; } export type ReadinessVerdict = "ready" | "concerning" | "not-ready"; export interface ReadinessReport { /** Count of observations that actually went to the origin (not pure cache). */ liveFetchCount: number; medianMs: number; p95Ms: number; /** 2xx/3xx count / liveFetchCount. Not meaningful — provided for callers. */ successRatio: number; serverErrorCount: number; /** 5xx / liveFetchCount. */ serverErrorRatio: number; /** (revalidated + fromCache) / total observations. How much the cache helped. */ cacheAssistRatio: number; verdict: ReadinessVerdict; /** * v0.4: which dev-server framework was detected on the source response, if any. * When set, readiness thresholds are softened (p95 cap raised to 10s, first * five fetches dropped as warmup grace) so Turbopack/Vite/Astro hot-compile * latency doesn't trip the watchdog when auditing localhost. */ detectedFramework?: DetectedFramework; } /** Frameworks the readiness probe knows to grace. */ export type DetectedFramework = "nextjs" | "vite" | "astro"; /** v0.4: detect dev-server framework from response headers. Returns null when not a known dev server. */ export declare function detectDevServer(headers: Record): DetectedFramework | null; export interface ReadinessThresholds { /** p95 at or above this → verdict 'not-ready' (absolute cap). Default 3000. */ notReadyP95Ms?: number; /** p95 at or above this (but below notReadyP95Ms) → 'concerning'. Default 800. */ concerningP95Ms?: number; /** 5xx/live ratio at or above this → 'not-ready' regardless of latency. Default 0.1. */ notReadyErrorRatio?: number; /** * v0.4: when set to a known framework, the absolute p95 cap is raised to 10s * (Turbopack/Vite hot-compile envelope) and the first 5 live fetches are * dropped from the percentile calculation as warmup grace. */ detectedFramework?: DetectedFramework | null; } /** * Aggregate a run's fetch observations into a readiness report. Returns `null` * when there's no origin data to speak to (zero fetches, or all pure cache * hits). Null is a signal to callers that the finding should be suppressed — * it's not a result worth displaying. */ export declare function computeReadiness(observations: readonly FetchObservation[], thresholds?: ReadinessThresholds): ReadinessReport | null; /** * A small collector so callers can hand the same object to the fetch pipeline * (as an onObservation callback) and to the readiness/backpressure consumers. */ export declare class FetchObserver { private readonly entries; record(obs: FetchObservation): void; getAll(): readonly FetchObservation[]; snapshotLast(n: number): readonly FetchObservation[]; get size(): number; } //# sourceMappingURL=fetch-observer.d.ts.map