WFDASH-WEBAPP/scripts/_dump-browser-csv.ts

82 lines
2.4 KiB
TypeScript
Raw Normal View History

#!/usr/bin/env node
/**
* Pull the CSV from the headless browser via CDP and assemble it on disk.
*
* Usage:
* tsx scripts/_dump-browser-csv.ts <viewId> <expression> <out-path>
*
* Example:
* tsx scripts/_dump-browser-csv.ts stable-browser-session/xxx 'window.__hbCsv' scripts/nss-hex-bolts.csv
*/
import { writeFileSync } from "node:fs";
const [, , viewIdExpr, expression, outPath] = process.argv;
if (!viewIdExpr || !expression || !outPath) {
console.error("Usage: tsx _dump-browser-csv.ts <viewId> <expr> <out>");
process.exit(1);
}
const port = 9222;
const host = "127.0.0.1";
// Discover the page WebSocket URL.
async function getWsUrl(): Promise<string> {
const r = await fetch(`http://${host}:${port}/json`);
const tabs = (await r.json()) as Array<{ id: string; webSocketDebuggerUrl: string }>;
const target = tabs.find((t) => t.id.startsWith(viewIdExpr));
if (!target) throw new Error(`tab not found for viewId prefix ${viewIdExpr}`);
return target.webSocketDebuggerUrl;
}
async function evalChunked(ws: WebSocket, expression: string): Promise<string> {
const CHUNK = 6000;
const full = await evalOnce(ws, expression);
const out: string[] = [];
for (let i = 0; i < full.length; i += CHUNK) {
out.push(full.substring(i, i + CHUNK));
}
return out.join("");
}
function evalOnce(
ws: WebSocket,
expression: string,
): Promise<string> {
return new Promise((resolve, reject) => {
const id = Math.floor(Math.random() * 1e9);
const onMsg = (ev: MessageEvent) => {
const msg = JSON.parse(ev.data);
if (msg.id !== id) return;
ws.removeEventListener("message", onMsg);
if (msg.error) reject(new Error(msg.error.message));
else resolve(msg.result?.value ?? "");
};
ws.addEventListener("message", onMsg);
ws.send(
JSON.stringify({
id,
method: "Runtime.evaluate",
params: { expression, returnByValue: true },
}),
);
});
}
async function main() {
const wsUrl = await getWsUrl();
const ws = new WebSocket(wsUrl);
await new Promise<void>((resolve, reject) => {
ws.addEventListener("open", () => resolve());
ws.addEventListener("error", (e) => reject(new Error(String(e))));
});
const csv = await evalChunked(ws, expression);
writeFileSync(outPath, csv);
console.log(`Wrote ${csv.length} bytes to ${outPath}`);
ws.close();
}
main().catch((e) => {
console.error("FATAL:", e);
process.exit(1);
});