/** * Run-level crawl confidence: did the crawl hit an authentication boundary, and * is the result still allowed to claim full certification? * * A green visual capture is misleading when the crawler stopped at a sign-in * form or auth redirect. This module turns redacted {@link classifyAuthBoundary} * diagnostics into a run-level status (`complete` | `incomplete-auth` | * `incomplete-unknown`) and an acknowledgement ledger so unacknowledged walls * fail closed while reasoned exclusions mark scope as explicitly limited — * never inventing a coverage percentage for inaccessible surfaces. * `incomplete-unknown` is available on the resolver when callers pass * `unknownIncompleteness`; the crawl itself does not currently emit it. * * Secrets never appear here: observations carry only classifier diagnostics * (selectors already redacted, pathnames only). Values, cookies, tokens, * headers, query strings, fragments, and secret DOM text are out of scope. */ import { type AuthBoundaryDiagnostic } from './auth-boundary.js'; /** Run-level crawl completeness for certification. */ export type CrawlConfidenceStatus = 'complete' | 'incomplete-auth' | 'incomplete-unknown'; /** * One auth boundary observed during a crawl — route + redacted diagnostics only. * Never includes field values, cookies, tokens, headers, or secret text. */ export type AuthBoundaryObservation = { /** * Redacted pathname of the page (or requested route) where the boundary was * seen. Query, fragment, and credentials are never retained. */ route?: string; /** Deterministic diagnostics from {@link classifyAuthBoundary}. */ diagnostics: AuthBoundaryDiagnostic[]; }; /** `observationKey → non-empty reason` — walls deliberately outside certification. */ export type AuthBoundaryExclude = Record; /** * Run-level confidence attached to {@link CrawlReport}. Backward compatible for * consumers that ignore the field: absence of walls yields `status: 'complete'`. */ export type CrawlConfidence = { status: CrawlConfidenceStatus; /** Every distinct auth boundary observed (redacted). */ authBoundaries: AuthBoundaryObservation[]; /** Observed boundaries matched by a reasoned exclusion. */ acknowledged: Array; /** Observed boundaries with no exclusion — fail closed when non-empty. */ unacknowledged: Array; /** * Exclusion keys that matched nothing this run — a rotted opt-out ledger * (mirrors inventory / data-residue stale acknowledgements). */ staleExclusions: string[]; /** * True only when status is `complete` (no auth walls observed). Acknowledged * exclusions keep `status: 'incomplete-auth'` and never claim full certification. */ certifiesFully: boolean; /** * True when unacknowledged auth boundaries remain — CI/crawl must fail closed. */ blocked: boolean; }; /** * Stable identity for one observation, for exclusion matching and CLI output. * Format: `·` (route defaults to `unknown`). */ export declare function authBoundaryKey(observation: AuthBoundaryObservation): string; /** * Validate an exclusion ledger. Every reason must be a non-empty string after * trim — empty reasons are rejected so silence cannot clear a fail-closed wall. */ export declare function normalizeAuthBoundaryExclude(exclude: Record | undefined): AuthBoundaryExclude; /** Redact a URL or path down to pathname only (shared with crawl observation). */ export declare function redactedRoutePath(value: string | null | undefined): string | undefined; export declare function mergeAuthBoundaryObservations(observations: AuthBoundaryObservation[]): AuthBoundaryObservation[]; /** * Whether HTTP auth-redirect diagnostics collected during navigation should be * retained after optional setup. * * Intermediate redirects are dropped only when setup ran, the landed DOM has no * auth wall, AND the final pathname is known and not auth-semantic (successful * unlock away from login/oauth). Unrelated setup on an OAuth-only /login (no * password field) must not drop the redirect. No setup, still-gated DOM, or * auth-semantic landed path all retain (fail closed). * * @param landedPathname Redacted pathname only — never query, fragment, or secrets. */ export declare function shouldRetainAuthRedirects(hadSetup: boolean, landedHasAuthBoundary: boolean, landedPathname?: string | null): boolean; /** * Resolve run-level confidence from observations + optional reasoned exclusions. * * - No walls → `complete` (full certification). * - Any wall → `incomplete-auth` (even when every wall is excluded — exclusions * mark scope limited and never upgrade to full certification). * - `unknownIncompleteness` with no auth walls → `incomplete-unknown`. * Note: `incomplete-unknown` is resolver-only today — callers may pass * `unknownIncompleteness`; the crawl path does not auto-produce it. * - `blocked` iff any wall is unacknowledged (fail closed). */ /** * Collapse text for single-line CLI/log rendering: strip C0 controls, DEL, and * Unicode line/paragraph separators so exclusion reasons cannot inject multiline * log lines. Stored/API reason strings keep their original semantics; only the * terminal rendering path should use this. */ export declare function cliSafeLine(value: string): string; /** * Crawl CLI exit precedence after a successful capture run. * Coverage residue (`--require-full-coverage`) exits 4 and intentionally wins * over unacknowledged auth (exit 5) so a coverage gap is not masked by auth. * Auth blocked → 5; otherwise 0. */ export declare function crawlCaptureExitCode(input: { requireFullCoverage: boolean; hasCoverageResidue: boolean; authBlocked: boolean; }): 0 | 4 | 5; export declare function resolveCrawlConfidence(input: { observations: AuthBoundaryObservation[]; exclude?: Record; /** Other incompleteness with no auth signal (reserved; optional callers). */ unknownIncompleteness?: boolean; }): CrawlConfidence;