69 lines
2.7 KiB
JavaScript
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);
|