Skip to content

Instantly share code, notes, and snippets.

@staylor
Created July 28, 2026 03:43
Show Gist options
  • Select an option

  • Save staylor/a0e5980248be4eabab1a1d8ee9ab925b to your computer and use it in GitHub Desktop.

Select an option

Save staylor/a0e5980248be4eabab1a1d8ee9ab925b to your computer and use it in GitHub Desktop.
Lightpanda HTMLCollection CDP close-timeout reproducer
import { spawn } from "node:child_process";
import http from "node:http";
import { setTimeout as sleep } from "node:timers/promises";
import playwright from "playwright-core";
const { chromium } = playwright;
const LIGHTPANDA_BIN = process.env.LIGHTPANDA_BIN ?? "../zig-out/bin/lightpanda";
const COUNT = Number(process.env.COUNT ?? "20000");
const CLOSE_TIMEOUT_MS = Number(process.env.CLOSE_TIMEOUT_MS ?? "5100");
const CDP_PORT = Number(process.env.CDP_PORT ?? "9222");
const CDP = `http://127.0.0.1:${CDP_PORT}`;
const html = `<!doctype html><body>${'<div class="item"></div>'.repeat(COUNT)}</body>`;
const fixture = http.createServer((_request, response) => {
response.writeHead(200, { "content-type": "text/html" });
response.end(html);
});
await new Promise((resolve) => fixture.listen(0, "127.0.0.1", resolve));
const fixturePort = fixture.address().port;
const lightpanda = spawn(
LIGHTPANDA_BIN,
["serve", "--host", "127.0.0.1", "--port", String(CDP_PORT), "--log-level", "error"],
{ stdio: "inherit" },
);
for (let attempt = 0; attempt < 100; attempt++) {
try {
const browser = await chromium.connectOverCDP(CDP, { timeout: 100 });
await browser.close();
break;
} catch {
if (attempt === 99) throw new Error("Lightpanda did not start");
await sleep(100);
}
}
const browser = await chromium.connectOverCDP(CDP);
const context = await browser.newContext();
const page = await context.newPage();
await page.goto(`http://127.0.0.1:${fixturePort}`, { waitUntil: "domcontentloaded" });
await page.evaluate(() => {
setTimeout(() => {
Reflect.apply(() => {}, null, document.getElementsByClassName("item"));
}, 0);
});
await sleep(10);
const startedAt = performance.now();
let timer;
const outcome = await Promise.race([
Promise.all([page.close(), context.close()]).then(() => "closed", (error) => `error:${error.message}`),
new Promise((resolve) => {
timer = setTimeout(() => resolve("timeout"), CLOSE_TIMEOUT_MS);
}),
]);
clearTimeout(timer);
console.log({ COUNT, outcome, durationMs: Math.round(performance.now() - startedAt) });
lightpanda.kill("SIGTERM");
await browser.close().catch(() => {});
await new Promise((resolve) => fixture.close(resolve));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment