/** * The built page, off disk. Vite compiles src/tools/control-room/ui into * ui/dist; this serves it, and says so plainly when the build has not been run * rather than answering a blank page. */ import { spawnSync } from "node:child_process"; import { existsSync } from "node:fs"; import { isAbsolute, relative, resolve } from "node:path"; const UI_DIST = resolve(import.meta.dir, "ui", "dist"); export const BUILD_COMMAND = "bun run build:ui"; export function isBuilt(): boolean { return existsSync(resolve(UI_DIST, "index.html")); } /** * A clone has vite; a published install has the prepacked dist and never needs * it. Either way the page is there by the time the server answers. */ export function buildPage(): boolean { const repoRoot = resolve(import.meta.dir, "..", "..", ".."); // node_modules/.bin/vite is a shell shim that Windows cannot spawn directly; // the package's own JS entry runs the same build under any runtime. const vite = resolve(repoRoot, "node_modules", "vite", "bin", "vite.js"); if (!existsSync(vite)) return false; const built = spawnSync(process.execPath, [vite, "build"], { cwd: resolve(import.meta.dir, "ui"), encoding: "utf-8", }); return built.status === 0 && isBuilt(); } function unbuiltPage(): Response { return new Response( `PAL control room` + `` + `

The page has not been built

` + `

The control room's assets are a build artifact rather than source. Run:

` + `
${BUILD_COMMAND}
`, { status: 503, headers: { "content-type": "text/html; charset=utf-8" } } ); } export function indexHtml(): Response { if (!isBuilt()) return unbuiltPage(); return new Response(Bun.file(resolve(UI_DIST, "index.html")), { headers: { "content-type": "text/html; charset=utf-8" }, }); } /** * Only what the build emitted, resolved inside dist — a request that climbs out * of it with `..` is a miss, not a file read. */ export function staticAsset(pathname: string): Response | null { const target = resolve(UI_DIST, `.${pathname}`); if (!isInsideDist(target)) return null; return existsSync(target) ? new Response(Bun.file(target)) : null; } /** Compared as paths, not strings — a separator differs by platform. */ function isInsideDist(target: string): boolean { const rel = relative(UI_DIST, target); return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel); }