page-as-data
Use it inside your own browser tooling
The part that runs in the page is one file, page-as-data.js. Inject it
before the page's own scripts, so it can see router requests from the
start, and call window.__pageAsData.
// Playwright
import { readFileSync } from 'node:fs'
import { createRequire } from 'node:module'
const inPage = readFileSync(createRequire(import.meta.url).resolve('@keenskills/page-as-data/in-page'), 'utf8')
await page.addInitScript(inPage)
await page.goto('http://localhost:3000/orders')
await page.evaluate(() => window.__pageAsData.settle())
const screen = await page.evaluate(() => window.__pageAsData.read())
const issues = await page.evaluate(() => window.__pageAsData.layoutIssues())
With Chrome DevTools MCP, pass the file's contents as initScript to
navigate_page, then call the same functions with evaluate_script. You can
also paste the file into the DevTools console.
| Function | Returns |
|---|---|
await settle({ timeoutMs }) |
{ settled, ms }, or why it did not settle |
read({ maxText, maxRows }) |
url, title, headings, dialogs, alerts, text, tables, form, controls, images |
inspect(query) |
For each match: box, visible, why hidden or cut off, colours, contrast, readable |
layoutIssues() |
page overflow, clipped controls, sticky that cannot stick, small targets |
await click(name) / await fill(label, value) |
What it acted on, then settles |
It reads the page and nothing else. It makes no network calls of its own, and
it only wraps window.fetch to count React Server Component requests. It never
changes them. Password fields are reported as ••••, never their value.
From Node, import { readPage, checkPages } from '@keenskills/page-as-data' returns the
same results as the CLI's --json.