/** * ChaosCrawler - Playwright-based chaos testing crawler */ import type { Page } from "playwright"; import type { CrawlerOptions, CrawlerEvents, PageResult, ActionResult, CrawlReport, FaultInjectionStats } from "./types.js"; import { Logger } from "./logger.js"; export declare const COMMON_IGNORE_PATTERNS: string[]; /** * Named bundles of regex patterns for `--exclude` / `excludePatterns`. * * Real-world crawls hit the same families of third-party noise (analytics * pixels, embedded map tiles, video players) again and again. Each preset * names one such family so wrapper scripts can write `--ignore-preset * analytics,maps` instead of pasting 8 regexes. Presets compose with * `--exclude` — both are unioned before the pattern test runs. * * Keys are intentionally short and stable. Adding a new pattern to an * existing preset is backward-compatible; renaming a preset is not. */ export declare const IGNORE_PRESETS: Record; /** * Resolve a comma-separated preset spec into a flat list of regex * strings. Unknown preset names throw with the available keys listed — * silent fall-through would let typos drop the user's noise filter and * spam the report. */ export declare function resolveIgnorePresets(spec: string): string[]; /** * Parse a W3C traceparent header. Returns `null` if the value is malformed. * Used by the route handler when honouring an incoming traceparent so we * can still pass `traceId` / `spanId` to the consumer hook. * * Format: `00-{trace-id-32hex}-{span-id-16hex}-{flags-2hex}`. * * Exported for testing — consumers should not parse traceparents * themselves. */ export declare function parseTraceparent(value: string): { traceId: string; spanId: string; } | null; export declare class ChaosCrawler { private options; private actionWeights; private events; private logger; private browser; private context; private visited; private queue; private results; private actions; private blockedExternalCount; private startTime; private baseOrigin; /** Actions performed on the page currently being crawled. Reset on * every new crawlPage call so the recovery dump only reports actions * from the page that actually failed. */ private currentPageActions; /** Last successfully loaded URL for recovery */ private lastSuccessfulUrl; /** Recovery count for reporting */ private recoveryCount; /** How many failure-artifact bundles have been written. Used as a sequence + cap. */ private failureArtifactCount; /** Discovery metrics */ private discoveryMetrics; /** Current page being crawled (for source tracking) */ private currentEntry; /** JSONL trace entries collected when `traceOut` is set. */ private trace; /** * Actions to replay on the current page. Non-null only while a replay run * is mid-flight; the action dispatcher branches on this to decide between * random-weighted and playback. */ private currentReplayActions; /** Deterministic RNG for reproducible action selection. */ private rng; /** Fault injection rules compiled once at construction time. */ private compiledFaultRules; /** Page-lifecycle faults compiled once at construction time. */ private compiledLifecycleFaults; /** Compiled runtime faults — installed once at context level via init script. */ private compiledRuntimeFaults; /** * Per-page lifecycle executor — created on `applyLifecycleStage` first * call for each page and dropped when the page is closed. */ private lifecycleExecutor; /** * Run-scoped key/value bag shared with every invariant via * `InvariantContext.state`. Reset on `start()` so reusing a crawler for * multiple runs doesn't leak stale state. */ private invariantState; /** Resolved coverage-feedback config (null when disabled). */ private coverageFeedback; /** Per-page coverage collector — recreated each new page when enabled. */ private coverageCollector; /** All V8 function fingerprints seen across the run so far. */ private globalCoverage; /** Per-page coverage delta count, in BFS visit order. */ private pageCoverageDeltas; /** Historical novelty score per `targetKey(url, selector)`. */ private targetNovelty; /** * Most recent V8 coverage snapshot taken for the current page. Per-action * deltas are computed as `take() − lastCoverageSnapshot`. Reset to an * empty set whenever a fresh page is opened. */ private lastCoverageSnapshot; /** Resolved advisor wiring (null when the option is unset). */ private advisorRuntime; /** Per-run replay drift counters. Populated only when `traceReplay` is set. */ private replayFidelity; /** * Buffers server-side fault events parsed from response headers when * `options.server.mode === "remote"`. Null otherwise so the per-page * `page.on("response")` listener can be skipped in the common case. */ private readonly serverFaultCollector; /** * Set by the action loop immediately before each `performActionOnTarget` * call; cleared at the start of the next iteration. The traceparent * injection site uses `recordTraceId` to append captured trace-ids onto * this action's `traceIds[]`. */ private currentAction; /** Resolved driver (null when caller did not pass `options.driver`). */ private readonly driver; /** * Buffer of invariant violations observed since the driver's previous * step. Drained at the top of every driver-loop iteration. Empty when * no driver is configured. */ private driverPendingViolations; constructor(options: CrawlerOptions, events?: CrawlerEvents); /** Seed used for this run (useful for reproducing failures). */ getSeed(): number; /** * Match drained rejections against already-captured `pageerror` entries and * reclassify them as unhandled-rejection. Rejections in Chromium fire both * the DOM event and Playwright's CDP-level pageerror, so we dedupe here. */ private reclassifyRejections; /** * Evaluate all invariants declared for the given phase on the current page. * Any invariant that returns false/throws/returns a string is recorded as * a PageError with type "invariant-violation". */ private runInvariants; /** * Pop and return SPA navigations recorded by the in-page hook since the * previous drain. Used to surface History-API routing as discovered links * the BFS queue can pick up. */ private drainSpaNavigations; /** Pop and return any unhandled promise rejections captured since last call. */ private drainRejections; /** Get the logger instance for external use */ getLogger(): Logger; /** Record an action for the current page's recovery dump. */ private addToHistory; /** Actions performed on the current page (for recovery diagnostics). */ getRecentActions(): ActionResult[]; /** Create recovery info from current state */ private createRecoveryInfo; start(): Promise; /** * Run chaos testing on a single page (for Playwright Test integration) */ testPage(page: Page, url: string): Promise; private shouldExclude; /** * Shard ownership gate. Returns true when this shard should enqueue `url`. * Single-shard configs always return true. Multi-shard configs drop every * URL whose hash doesn't match this shard's index — except `baseUrl`, which * every shard must process so it has a seed for BFS. */ /** * True when trace entries should be recorded in memory. `traceOut` * obviously needs them; `failureArtifacts` also needs an in-memory trace * so it can serialize the prefix-up-to-failure into each bundle — but * only when `saveTrace` isn't explicitly disabled. Recording for a * caller that has opted out wastes memory on long crawls. */ private isRecordingTrace; /** * If failure artefacts are enabled and the page result qualifies, capture * a screenshot + HTML + trace snapshot and dump a bundle directory. * Errors here are intentionally swallowed: the bundle is diagnostic, not * load-bearing — losing one bundle shouldn't take the crawler down. */ /** * Read `window.__chaosbringerRuntimeStats` and accumulate into the * compiled runtime-fault counters. Errors are swallowed: the in-page * counter is best-effort diagnostics, not load-bearing. */ private collectRuntimeFaultStats; private maybeWriteFailureBundle; private ownsUrl; /** Check if URL matches SPA patterns */ private matchesSpaPattern; private shouldIgnoreError; private isExternalUrl; /** * Pull URLs out of a sitemap (index-aware) and prepend them to the queue. * URLs outside the baseUrl origin are dropped — the crawler's * blockExternalNavigation would block them anyway, and queueing them * wastes visit budget. */ private seedQueueFromSitemap; /** * Take a coverage snapshot right after `page.goto` finishes. Functions * executed during page load are credited to the URL itself (no specific * action) and folded into `globalCoverage`. The snapshot is also saved as * the per-action delta baseline. */ private recordPageLoadCoverage; /** * Take a coverage snapshot after a chaos action and credit any * never-before-seen functions to `(url, selector)` in `targetNovelty`. * Updates `globalCoverage` and `lastCoverageSnapshot` so subsequent * actions see the right baseline. */ private attributeActionCoverage; /** * Compute the coverage-feedback weight multiplier for a target on a given * URL. Returns 1 when feedback is off or when the target has no history. */ private coverageWeightFor; /** * Append a trace-id to the currently-executing action, if any. Called * from the per-request traceparent injection in `setupNavigationBlocking`. * No-op when no action is in flight (e.g. during initial page load). */ private recordTraceId; private consultAdvisorIfStalled; /** * Pop the just-pending advisor pick if (and only if) it matches the * selector that was actually performed. Mismatch can happen when the * advisor's chosen target was rejected as not-visible and the loop * fell back to a heuristic pick — those should NOT be tagged advisor. */ private consumeAdvisorStamp; /** * Run every lifecycle fault that targets `stage` and matches `url`. Errors * are caught and recorded in the fault's stats counter — a misbehaving * fault should not abort the rest of the crawl. */ private applyLifecycleStage; /** * Attach a CDP session to the page and apply a throttling preset. Called * per-page because `Network.emulateNetworkConditions` is a Page-level * setting in Playwright — there's no context-wide equivalent. */ private applyNetworkProfile; private setupNavigationBlocking; private crawlPage; private crawlPageWithExistingPage; /** * Compare measured metrics against the configured budget and push one * invariant-violation per breached metric. Delegates to a pure helper * (`checkPerformanceBudget`) so the check is unit-testable without a * running browser. */ private enforcePerformanceBudget; private collectMetrics; private extractLinks; /** * Get action targets from DOM with accessibility-based weighting */ private getWeightedActionTargets; private escapeSelector; /** * Perform actions based on weighted random selection */ private performWeightedActions; /** * Driver-based action loop. Replaces the weighted-random path when the * caller supplied `options.driver`. The crawler still owns target * discovery, action execution, history, lifecycle hooks, and coverage * attribution — the driver only decides *which* candidate to act on * each step. A `kind: "skip"` pick or a `null` return short-circuits * the step; the loop's attempt counter still ticks so a misbehaving * driver cannot loop forever. */ private performDriverActions; /** * Play back a sequence of recorded actions on the current page. Actions * whose selectors no longer resolve are recorded as failed — the run * continues so downstream errors can still surface. Scroll actions * reconstruct the Y offset from the recorded `target` string. */ private performReplayActions; private recordReplayOutcome; private performActionOnTarget; private getScreenshotFilename; private generateReport; /** Build a shell command that reruns this crawl with the same seed / limits. */ private buildReproCommand; /** Per-rule fault injection stats (for reporting). */ getFaultStats(): FaultInjectionStats[]; private calculateSummary; } /** * Validate user-supplied options up front so downstream code can assume * well-formed inputs. Every error starts with `chaosbringer:` and names * the field, so users don't get an anonymous `TypeError: Invalid URL`. */ export declare function validateOptions(options: CrawlerOptions): void; //# sourceMappingURL=crawler.d.ts.map