Complete XYZAC LinuxCNC WASM parity and smooth 3D preview
This commit is contained in:
@@ -4,6 +4,7 @@ import { mkdtemp, rm } from "node:fs/promises";
|
||||
import { normalize, join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { spawn } from "node:child_process";
|
||||
|
||||
const root = new URL("../web/", import.meta.url).pathname.replace(/\/$/, "");
|
||||
const modulePath = process.env.PLAYWRIGHT_MODULE;
|
||||
@@ -16,9 +17,10 @@ if (!modulePath) {
|
||||
blocked("playwright_unavailable", "PLAYWRIGHT_MODULE is not set");
|
||||
} else {
|
||||
let server;
|
||||
let firstContext;
|
||||
let secondContext;
|
||||
let browserServer;
|
||||
let firstBrowser;
|
||||
let secondBrowser;
|
||||
let firstProcess;
|
||||
let secondProcess;
|
||||
const profile = await mkdtemp(join(tmpdir(), "linuxcnc-opfs-crash-s6-07-"));
|
||||
try {
|
||||
const imported = await import(pathToFileURL(modulePath).href);
|
||||
@@ -32,32 +34,59 @@ if (!modulePath) {
|
||||
});
|
||||
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));
|
||||
const port = server.address().port;
|
||||
firstContext = await playwright.chromium.launchPersistentContext(profile, { headless: false });
|
||||
browserServer = await firstContext.browser()._launchServer();
|
||||
const writer = await firstContext.newPage();
|
||||
const executable = process.env.BROWSER_EXECUTABLE_PATH || "google-chrome";
|
||||
async function launchBrowser() {
|
||||
const debugPort = 39000 + Math.floor(Math.random() * 1000);
|
||||
const child = spawn(executable, [
|
||||
"--headless=new", "--no-sandbox", "--disable-gpu", "--disable-dev-shm-usage", "--disable-background-networking",
|
||||
`--user-data-dir=${profile}`, `--remote-debugging-port=${debugPort}`, "--remote-allow-origins=*", "about:blank",
|
||||
], { stdio: ["ignore", "ignore", "ignore"] });
|
||||
let version = null;
|
||||
for (let attempt = 0; attempt < 100; attempt += 1) {
|
||||
try {
|
||||
const response = await fetch(`http://127.0.0.1:${debugPort}/json/version`);
|
||||
if (response.ok) { version = await response.json(); break; }
|
||||
} catch {}
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
}
|
||||
if (!version) { child.kill("SIGKILL"); throw new Error("browser_devtools_unavailable"); }
|
||||
const browser = await playwright.chromium.connectOverCDP(`http://127.0.0.1:${debugPort}`);
|
||||
return { child, browser, version: version.Browser ?? "unknown" };
|
||||
}
|
||||
({ child: firstProcess, browser: firstBrowser } = await launchBrowser());
|
||||
const firstContext = firstBrowser.contexts()[0];
|
||||
const writer = firstContext.pages()[0] ?? await firstContext.newPage();
|
||||
await writer.goto(`http://127.0.0.1:${port}/app/browser-opfs-probe.html?mode=write&cleanup=false`);
|
||||
await writer.waitForFunction(() => document.querySelector("#result")?.textContent !== "pending", null, { timeout: 10000 });
|
||||
const written = JSON.parse(await writer.locator("#result").textContent());
|
||||
if (written.status !== "verified") throw new Error(written.code ?? "opfs_write_blocked");
|
||||
const processId = browserServer.process()?.pid ?? null;
|
||||
const processId = firstProcess.pid ?? null;
|
||||
if (!processId) throw new Error("browser_process_missing");
|
||||
browserServer.kill();
|
||||
await new Promise((resolve) => browserServer.once("close", resolve));
|
||||
firstContext = null;
|
||||
secondContext = await playwright.chromium.launchPersistentContext(profile, { headless: false });
|
||||
const reader = await secondContext.newPage();
|
||||
firstProcess.kill("SIGKILL");
|
||||
await new Promise((resolve) => firstProcess.once("close", resolve));
|
||||
await firstBrowser.close().catch(() => {});
|
||||
firstBrowser = null;
|
||||
({ child: secondProcess, browser: secondBrowser } = await launchBrowser());
|
||||
const secondContext = secondBrowser.contexts()[0];
|
||||
const reader = secondContext.pages()[0] ?? await secondContext.newPage();
|
||||
await reader.goto(`http://127.0.0.1:${port}/app/browser-opfs-probe.html?mode=read&cleanup=true`);
|
||||
await reader.waitForFunction(() => document.querySelector("#result")?.textContent !== "pending", null, { timeout: 10000 });
|
||||
const restored = JSON.parse(await reader.locator("#result").textContent());
|
||||
if (restored.status !== "verified" || restored.content_hash !== written.content_hash || restored.cleanup !== true) throw new Error("opfs_crash_record_mismatch");
|
||||
console.log(JSON.stringify({ schema: "browser-opfs-s6-07-crash-sample.v1", status: "verified", browser: "chromium", browser_version: (await secondContext.browser()).version(), mode: "process_kill_profile_reopen", process_killed: true, commit_seq: restored.commit_seq, content_hash: restored.content_hash, cleanup: restored.cleanup }));
|
||||
console.log(JSON.stringify({ schema: "browser-opfs-s6-07-crash-sample.v1", status: "verified", browser: "chromium", browser_version: secondProcess ? (await secondBrowser.version()) : "unknown", mode: "process_kill_profile_reopen", process_killed: true, commit_seq: restored.commit_seq, content_hash: restored.content_hash, cleanup: restored.cleanup }));
|
||||
} catch (error) {
|
||||
blocked("opfs_crash_probe_failed", error?.message ?? error?.name ?? String(error));
|
||||
} finally {
|
||||
try { browserServer?.kill(); } catch {}
|
||||
await secondContext?.close().catch(() => {});
|
||||
await firstContext?.close().catch(() => {});
|
||||
try { firstProcess?.kill("SIGKILL"); } catch {}
|
||||
try { secondProcess?.kill("SIGKILL"); } catch {}
|
||||
await secondBrowser?.close().catch(() => {});
|
||||
await firstBrowser?.close().catch(() => {});
|
||||
await Promise.all([firstProcess, secondProcess].filter(Boolean).map((child) => new Promise((resolve) => {
|
||||
if (child.exitCode !== null || child.signalCode !== null) { resolve(); return; }
|
||||
child.once("close", resolve);
|
||||
setTimeout(resolve, 2000);
|
||||
})));
|
||||
server?.close();
|
||||
await rm(profile, { recursive: true, force: true });
|
||||
await rm(profile, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user