// The one Vite preview every verify-* fixture mounts: an isolated page for a // single entry module whose /api calls reach only the disposable fake-engine // server. vite.config.ts is inherited on purpose (react, tailwind, the @ alias) // so the page renders exactly what the app renders; only the host, the port // and the /api target are pinned here. import { realpathSync } from "node:fs"; import type { IncomingMessage, ServerResponse } from "node:http"; import { join } from "node:path"; import { fileURLToPath } from "node:url"; import { createServer } from "vite"; export const REPO_ROOT = fileURLToPath(new URL("../..", import.meta.url)); export interface PreviewRoute { /** Pathname to answer (the query string is ignored); a RegExp hands its match to the handler. */ path: string | RegExp; /** Restrict to one HTTP method; any method when omitted. */ method?: string; handler: (req: IncomingMessage, res: ServerResponse, match: RegExpExecArray | null) => void | Promise; } export interface PreviewOptions { /** Root-relative URL of the module the page loads, e.g. "/scripts/testing/x.tsx". */ entry: string; /** Pathname the isolated page is served at, e.g. "/__x.html". */ route: string; title: string; /** Fixture-only endpoints beside the page: test pages, drift triggers, captured requests. */ extraRoutes?: PreviewRoute[]; /** Content of the viewport meta tag. */ viewport?: string; /** Vite's log level. Its default prints port and dependency notes on stdout, * ahead of anything the fixture prints there. */ logLevel?: "info" | "warn" | "error" | "silent"; /** Set on every request the page sends the fixture, e.g. a Cloud home's edge headers and a paired device's token. */ proxyHeaders?: Record; } export interface MountedPreview { previewUrl: string; close(): Promise; } const escapeHtml = (text: string) => text.replace(/[&<>"]/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[char] ?? char); const matchRoute = (route: PreviewRoute, pathname: string, method: string | undefined) => { if (route.method !== undefined && route.method !== method) return undefined; if (typeof route.path === "string") return route.path === pathname ? { match: null } : undefined; const match = route.path.exec(pathname); return match ? { match } : undefined; }; /** Serve `entry` in an isolated page whose /api requests are proxied to `fixture`. */ export async function mountPreview( fixture: { info: { url: string } }, options: PreviewOptions, ): Promise { const { entry, route, title, extraRoutes = [], viewport = "width=device-width, initial-scale=1", logLevel, proxyHeaders } = options; const page = `${escapeHtml(title)}
`; const ui = await createServer({ root: REPO_ROOT, ...(logLevel ? { logLevel } : {}), // A worktree's node_modules may be a link to another checkout's: its files (the fonts) are served from there. server: { host: "127.0.0.1", port: 0, fs: { allow: [REPO_ROOT, realpathSync(join(REPO_ROOT, "node_modules"))] }, proxy: { "/api": { target: fixture.info.url, ...(proxyHeaders ? { headers: proxyHeaders } : {}) }, "/.well-known/openmausbot/environment": { target: fixture.info.url, ...(proxyHeaders ? { headers: proxyHeaders } : {}) }, } }, plugins: [{ name: "isolated-preview", configureServer(server) { server.middlewares.use((req, res, next) => { const pathname = (req.url ?? "").split("?")[0]!; if (pathname === route) { void server.transformIndexHtml(route, page) .then((html) => { res.setHeader("content-type", "text/html"); res.end(html); }) .catch(next); return; } for (const extra of extraRoutes) { const hit = matchRoute(extra, pathname, req.method); if (!hit) continue; void Promise.resolve().then(() => extra.handler(req, res, hit.match)).catch(next); return; } next(); }); }, }], }); await ui.listen(); const base = ui.resolvedUrls?.local[0]; if (!base) { await ui.close(); throw new Error("the preview server did not report a local URL"); } return { previewUrl: new URL(route, base).href, close: () => ui.close(), }; } /** Resolve on the first SIGINT or SIGTERM: the "print the URL and wait" ending. */ export function parkUntilSignal(): Promise { return new Promise((resolve) => { const settle = () => { process.off("SIGINT", settle); process.off("SIGTERM", settle); resolve(); }; process.once("SIGINT", settle); process.once("SIGTERM", settle); }); } export interface FixtureApiOptions { /** Sent with every call; `origin: fixture.info.url` marks a request as the app's own. */ headers?: Record; /** Sees every call that succeeded, e.g. to record mutation evidence. */ observe?: (call: { method: string; path: string; status: number }) => void; } /** JSON fetch against the fixture server; a non-2xx status throws with the body. */ export function fixtureApi(baseUrl: string, options: FixtureApiOptions = {}) { return async (method: string, path: string, body?: unknown): Promise => { const response = await fetch(`${baseUrl}${path}`, { method, headers: { "content-type": "application/json", ...options.headers }, ...(body === undefined ? {} : { body: JSON.stringify(body) }), }); const result = await response.json(); if (!response.ok) throw new Error(`${method} ${path}: ${JSON.stringify(result)}`); options.observe?.({ method, path, status: response.status }); return result; }; }