@fanalis/render
v0.1.0
Published
Playwright-based multi-viewport renderer + axe injector + public-file fetcher for Fanalis.
Downloads
95
Readme
@fanalis/render
playwright + chromium driver. fetches a url, runs axe-core, captures screenshots, probes 5 viewports.
entries
renderUrl(url, opts)→AuditContext— the main one. used by@fanalis/orchestrator.closeBrowser()— kills the shared chromium. caller invokes at end of run.
what one call does
- shared chromium singleton (
getBrowser()), heap ceiling =512 * FANALIS_CONCURRENCYMB - SSRF-gated
page.route()blocks any subrequest to RFC 1918 / loopback / metadata IPs - desktop pass at 1440×900: navigate, settle, full-page screenshot, axe-core injected via
addScriptTag({content})(avoidsrequire.resolvebrittleness in next runtime) - rendered snapshot (~50 fields): title/meta/og/twitter, headings, links, images, forms, buttons, json-ld, palette colours, style aggregate, layout, focusables (for the a11y keyboard graph)
- mobile pass at 390×844 (iPhone 14 Pro CSS) — fresh context, isMobile, hasTouch, captures cls via Layout-Instability buffered PerformanceObserver
- inside the mobile context: multi-viewport probe at 320/390/768/1280/1920 — slim probe per viewport (
documentWidth,overflowCount,smallTapTargets,headingOrder+headingPositions,focusableCount) - axe-core findings get jsx-traceback: walk
__reactFiber$*→_debugSource = {fileName, lineNumber, columnNumber}to attach source coordinates to violations (dev builds only)
caveats
- the browser is a process-wide singleton.
closeBrowser()is required between runs that share a node process; the orchestrator calls it after each route viakeepBrowser. --max-old-space-sizeper concurrency is set viaFANALIS_CONCURRENCYenv. the CLI sets it before anyrenderUrlcall.- SSRF gate aborts with
addressunreachable. logged vialog.warn('audit.playwright.ssrf-blocked', {host, url, resourceType}); debug builds setFANALIS_DEBUG=1to surface those. - mobile capture closes its own context. don't reuse the
pageobject after. - viewport-probe positions are absolute (
y + scrollY), not viewport-relative. the responsiveness pillar uses them to compute kendall-τ between visual (sorted by(y, x)) and dom order.
what we don't render
- urls that aren't localhost (per v1 promise). the CLI rejects non-localhost URLs upstream;
renderUrlitself doesn't enforce that.
perf
cold start ~3 s (chromium spawn). per-route ~5–8 s with axe-core + mobile pass.
