Skip to main content

The Challenger Framework

Every scrape and crawl-page run is driven through the same dispatch sequence, whether or not any extension is listening.

dispatcher.beginRun(seed) -> ChallengerRunSession
|
createHardenedContext(opts, session)
-> dispatch('bootstrap-context') <- proxy / fingerprint / launchArgs decided here
|
context.newPage() -> instrumentPage(page, session)
-> dispatch('create-page')
-> page.on('request'|'response') -> dispatch('request'|'response'|'redirect')
-> if any interception handlers exist: context.route('**/*', ...)
|
runDsl / simple scrape
-> dispatch('before-navigation') / ('after-navigation') <- retry/abort/delay honored here
-> per step: dispatch('before-step') / ('after-step')
-> unknown action? session.resolveAction(name) -> runAction()
|
collectArtefacts -> dispatch('artefact-collected')
discoverLinks -> dispatch('discovered-link') <- links can be dropped here
|
finalizeRun: dispatch('run-outcome') -> session.end(outcome)
-> extension.afterRun() / onError()

Listeners are attached only when a handler actually exists for that stage (session.hasHandlers(...)), so an unused stage costs nothing.

Ordering and isolation

Extensions and handlers both carry a priority (lower runs first, default 100). Within a stage, mutating handlers run serially in priority order; observer handlers run concurrently afterward and cannot affect run state. Each extension sees only its own ctx.state — the only sanctioned way for one extension to learn about another's activity is a signal (see Signals and Diagnostics).

Request and response interception

Both flow through one context.route seam, installed at most once per page. Request interception is first-terminal-wins; response interception is an ordered pipeline where each handler sees the previous one's output. Both fail open on a handler exception or an upstream fetch failure — see Intercepting Requests and Responses.

Browser pooling and fingerprint isolation

See Browser Execution and Pooling for how the same lifecycle reconciles sharing one browser process across runs with letting an extension request a genuinely distinct one.

Config, lifecycle, and the admin surface

Extension configuration, the enable/disable toggle, and the archive-then-purge removal lifecycle are covered in the Manage Extensions guide — this page is about what happens during a run; that one is about operating extensions between runs.