Skip to content

Report per-viewport image-scan outcome from beforePageScan/beholder - #962

Merged
YusukeHirao merged 2 commits into
devfrom
feat/image-scan-outcome
Sep 10, 2026
Merged

Report per-viewport image-scan outcome from beforePageScan/beholder#962
YusukeHirao merged 2 commits into
devfrom
feat/image-scan-outcome

Conversation

@YusukeHirao

Copy link
Copy Markdown
Member

Summary

  • beforePageScan's networkidle0/networkidle2 fallback used to fail outright when both timed out. Add an opt-in continueOnDegradedNetwork option: on a double timeout, check whether the frame is still usable (main frame attached, URL matches, document past loading) and continue as settled: 'degraded' instead of throwing — pages with analytics beacons, chat widgets, or open WebSocket connections never go network-idle even though navigation completed. Defaults to false, so every existing caller keeps its current throw-on-double-timeout behavior unchanged.
  • When the frame genuinely isn't usable, throw the new NavigationUnsettledError instead of the raw Puppeteer timeout error, so callers can classify "navigation never settled" without parsing error message strings.
  • beholder's Scraper#fetchImages opts into the new option and records each device preset's outcome as a small integer (IMAGE_SCAN_CODE: ok/degraded/nav-unsettled/frame-lost/scroll-height-exceeded/unknown) on PageData.imageScan, via the new classifyImageScanError helper — instead of only emitting a free-text changePhase log message that a caller would have had to string-match to know why a viewport's image scan was abandoned.

Breaking change

PageData.imageScan is a new required field, the same category of change as the 4.0.0 addition of mainContents/scrollHeight. Anyone constructing a PageData object directly (rather than only reading scrapeStart()'s result) needs to add it. @d-zero/puppeteer-page-scan's change is additive/opt-in and is not breaking.

Test plan

  • yarn lint / yarn build / yarn test all pass at the repo root
  • puppeteer-page-scan: new before-page-scan.spec.ts cases cover idle / networkidle0→idle2 fallback / degraded-continue / each NavigationUnsettledError trigger (detached frame, URL mismatch, readyState) / non-timeout errors at both layers / the continueOnDegradedNetwork: false backward-compat path
  • puppeteer-page-scan: new is-frame-settled.spec.ts covers the trailing-slash/hash URL normalization
  • beholder: new classify-image-scan-error.spec.ts covers all classification branches
  • Ran the full test suites of the 5 other beforePageScan consumers (print, puppeteer-screenshot, a11y-check-core, replicator, anatomist) to confirm no behavior change for callers that don't opt in

🤖 Generated with Claude Code

YusukeHirao and others added 2 commits September 10, 2026 13:06
…ttles but frame is usable

Add an opt-in continueOnDegradedNetwork option: when both the networkidle0
and networkidle2 waits time out, check whether the frame is nonetheless
usable (main frame attached, URL matches the target, document past
loading) instead of failing outright. Some pages (analytics beacons, chat
widgets, open WebSocket connections) never go network-idle even though
navigation itself completed. Defaults to false, so every existing caller
of beforePageScan keeps throwing on a double timeout exactly as before.

BeforePageScanResult gains a settled: 'idle' | 'degraded' field. A
double timeout with the frame unusable throws the new
NavigationUnsettledError instead of the raw Puppeteer timeout error, so
callers can distinguish "navigation never settled" from frame/session
loss without parsing error message strings.

isFrameSettled's URL comparison normalizes a trailing slash and hash
before comparing, since Chrome normalizes a root navigation's page.url()
to end in '/' even when the requested URL string omits it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
BREAKING CHANGE: PageData.imageScan is a new required field ({ desktop,
mobile }: ImageScanCode | null per viewport), following the same
precedent as the 4.0.0 addition of mainContents/scrollHeight. Anyone
constructing a PageData object directly (rather than only reading
scrapeStart()'s result) must add this field.

Scraper#fetchImages now classifies each device preset's outcome via the
new IMAGE_SCAN_CODE vocabulary (0=ok, 1=degraded, 2=nav-unsettled,
3=frame-lost, 4=scroll-height-exceeded, 255=unknown) instead of only
emitting a free-text changePhase message: success records ok/degraded
based on @d-zero/puppeteer-page-scan's new settled result, a
scrollHeight-limit skip records scroll-height-exceeded, and a caught
exception is classified by the new classifyImageScanError (reusing
puppeteer-scroll's isTransientFrameError, plus "Not attached to an
active page"/"Target closed" for reload/goto-specific failures).

#fetchImages opts into @d-zero/puppeteer-page-scan's new
continueOnDegradedNetwork so a page whose network never settles still
yields images (flagged degraded) instead of losing that viewport
outright.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@YusukeHirao
YusukeHirao merged commit 044cae5 into dev Sep 10, 2026
7 checks passed
@YusukeHirao
YusukeHirao deleted the feat/image-scan-outcome branch September 10, 2026 09:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant