orbit/dev/cdp-probe.mjs

69 lines
2.7 KiB
JavaScript

// Dev: evaluate one expression in a page (brave CDP on :9333) and print
// the full result. Usage: node dev/cdp-probe.mjs <url> <expr> [timeoutMs]
const [url, expr, timeoutMs = '20000'] = process.argv.slice(2);
if (!url || !expr) {
console.error('usage: node dev/cdp-probe.mjs <url> <expr> [timeoutMs]');
process.exit(2);
}
const CDP = '127.0.0.1:9333';
const deadline = Date.now() + Number(timeoutMs);
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
let target = await (await fetch(`http://${CDP}/json/list`)).json()
.then((ts) => ts.find((t) => t.type === 'page'));
if (!target) {
target = await (await fetch(`http://${CDP}/json/new?${encodeURIComponent(url)}`, { method: 'PUT' })).json();
}
const ws = new WebSocket(target.webSocketDebuggerUrl);
let id = 0;
const pending = new Map();
const send = (method, params = {}) => new Promise((res, rej) => {
const mid = ++id;
pending.set(mid, { res, rej });
ws.send(JSON.stringify({ id: mid, method, params }));
});
await new Promise((res, rej) => {
ws.onopen = res;
ws.onerror = (e) => rej(new Error('ws error: ' + (e?.message ?? '?')));
});
ws.onmessage = (m) => {
const msg = JSON.parse(m.data);
if (msg.id && pending.has(msg.id)) {
const { res, rej } = pending.get(msg.id);
pending.delete(msg.id);
msg.error ? rej(new Error(msg.error.message)) : res(msg.result);
}
};
await send('Page.enable');
await send('Runtime.enable');
// The dev static server sends no cache headers, and the browser heuristically
// caches ES modules — a stale module shadows the file on disk (bit the comms
// self-check: a cached ActionBar.js without the new method). Disable the
// cache so the harness always sees the current source.
await send('Network.enable');
await send('Network.setCacheDisabled', { cacheDisabled: true });
// A same-URL navigation can restore the tab from bfcache (the frozen old
// page) — bounce through about:blank so the load is always a real one.
await send('Page.navigate', { url: 'about:blank' });
await new Promise((res) => setTimeout(res, 300));
await send('Page.navigate', { url });
const loaded = new Promise((res) => setTimeout(res, 3000)); // let it settle
await loaded;
const evaluate = async (e) => {
const r = await send('Runtime.evaluate', { expression: e, returnByValue: true, awaitPromise: true });
if (r.exceptionDetails) throw new Error('page: ' + (r.exceptionDetails.exception?.description ?? r.exceptionDetails.text));
return r.result?.value;
};
let out = null;
while (Date.now() < deadline) {
out = await evaluate(expr).catch(() => null);
if (out !== null && out !== undefined) break;
await sleep(300);
}
console.log(JSON.stringify(out, null, 2));
ws.close();
process.exit(0);