import type { BrowserContext } from 'playwright'; /** * Privacy-signal request headers attached to every capture context. * * These are standard, widely-sent browser headers (Firefox sends `DNT`, Brave * sends `Sec-GPC`), so they're invisible to origin anti-bot defenses — they * only ever observe a normal first-party page load. Privacy-first analytics * (Plausible, Fathom, …) honor them and suppress the pageview, which * complements the network-level blocking below for the providers that respect * the signal. Free, zero-risk belt-and-suspenders. */ export declare const PRIVACY_HEADERS: Record; /** * Hostnames of DEDICATED web-analytics / product-telemetry / session-replay * endpoints. These domains serve ONLY analytics, so aborting requests to them * during a capture has zero functional impact on the page being screenshotted — * it only prevents AutoKap's automated visit from registering as a phantom * "visitor" in the site owner's analytics (AUT-234). * * Matched by exact host OR sub-domain suffix * (`host === h || host.endsWith('.' + h)`), so regional shards * (`eu.i.posthog.com`, `region1.google-analytics.com`, `*.matomo.cloud`, …) are * covered without enumerating each one. * * Deliberately ABSENT: `googletagmanager.com`. GTM can inject functional tags, * and loading `gtm.js` does NOT itself record a visit — the GA *beacon* to * `google-analytics.com` does, and that host IS blocked. So we neutralize the * GA visit without risking a broken page. * * Self-hosted / first-party-proxied analytics (e.g. Plausible reverse-proxied * on the site's own domain) is intentionally NOT covered: it reads as * first-party (see the `isFirstPartyUrl` guard in {@link installAnalyticsBlock}) * and is impossible to detect universally. That edge case is out of scope by * design — catching it would depend on per-site configuration. */ export declare const ANALYTICS_HOSTS: readonly string[]; /** * True when `url` targets a dedicated web-analytics *ingestion* endpoint (see * {@link ANALYTICS_HOSTS}). Host-suffix aware so regional/sub-domain shards * match their parent. Fail-CLOSED on unparseable or non-http(s) URLs (returns * `false`): we never abort a request we can't confidently classify. * * For hosts that co-serve functional config on the same domain as their * analytics (PostHog), only the event-capture paths count — feature-flag / * config / library paths are preserved so the captured UI never changes * (see {@link POSTHOG_FUNCTIONAL_PATH_RE}). */ export declare function isAnalyticsRequest(url: string): boolean; /** * The per-request block decision, factored out of {@link installAnalyticsBlock} * so the guard composition is unit-testable without a real browser context. * * Blocks ONLY a third-party analytics request. A first-party one — analytics * self-hosted or reverse-proxied on the captured site's OWN domain — is * preserved so we can never break the site's own functionality. * * `pageUrl` is the request's frame URL. When it's empty/unknown (a request in * flight before the first navigation commits, a detached/teardown frame, some * worker-originated requests) `isFirstPartyUrl` fail-OPENS to first-party, so * the beacon is NOT aborted. That's the deliberate safe direction — never break * a page — at the cost of a rare phantom-visit leak in that narrow window; real * analytics beacons fire after navigation commits, so the frame URL is present. */ export declare function shouldBlockAnalyticsRequest(pageUrl: string, requestUrl: string): boolean; /** * Install a context-level route that aborts outgoing requests to dedicated * third-party analytics endpoints, so capturing a site never registers a * phantom "visit" in its analytics (AUT-234). * * Only THIRD-party analytics is blocked (the `!isFirstPartyUrl` guard): a * first-party request is never aborted, so we can never break the captured * site's own functionality. Aborting a third-party beacon is invisible to the * origin's anti-bot (it only ever sees a normal first-party page load) — exactly * what an ad-blocker does — so this carries no risk of tripping bot defenses. * * Registered at the CONTEXT level so it (a) covers every page/frame in the * context, (b) survives `page.unrouteAll()` from `clearRouteInterception()` * (which only clears page-level routes), and (c) composes with the page-level * mock routes from `setupRouteInterception()` — page routes run first; this * catch-all `fallback()`s every non-analytics request back to the network (or * the next handler). Aborting also lowers in-flight count, which only helps * `networkidle` settle. The adaptive-wait progress signal already ignores * third-party traffic (AUT-240), so there's no interaction there. */ export declare function installAnalyticsBlock(context: BrowserContext): Promise;