import { CORE_DESIGN_SYSTEMS } from "@wrnexus/design-system"; // Exercise the compiled entry point, including package-relative Studio assets. const reservation = Bun.serve({ port: 0, hostname: "127.0.0.1", fetch: () => new Response(), }); const port = reservation.port!; reservation.stop(true); const server = Bun.spawn([process.execPath, "dist/server.js"], { env: { ...process.env, PORT: String(port) }, stdout: "pipe", stderr: "pipe", }); const origin = `http://127.0.0.1:${port}`; try { let ready = false; for (let attempt = 0; attempt < 60; attempt++) { try { ready = (await fetch(origin)).ok; } catch { /* Wait for the server to listen. */ } if (ready) break; await Bun.sleep(100); } if (!ready) throw Error("Compiled server did not become ready."); for (const path of [ "/", "/components/button", "/design-studio/", "/design-studio/studio.js", "/design-studio/studio.css", "/design-studio/site-help.js", ]) { const response = await fetch(origin + path); if (!response.ok) throw Error(`${path} returned ${response.status}`); if ( path === "/design-studio/" && !(await response.clone().text()).includes("How to use Design Studio") ) throw Error("Studio's usage guide was not included in the compiled app."); const policy = response.headers.get("content-security-policy") ?? ""; if (path === "/" && !policy.includes("require-trusted-types-for")) throw Error( "Normal component pages lost the production security policy.", ); if ( path === "/design-studio/" && (!policy.includes("frame-ancestors 'none'") || policy.includes("require-trusted-types-for")) ) throw Error("The app replaced Studio's isolated editor security policy."); } const application = await ( await fetch(origin + "/design-studio/api/application") ).json(); // Browser module requests carry Origin; the VM forwards them over plain HTTP. const publicOrigin = process.env.STUDIO_PUBLIC_ORIGIN ?? "https://component.wrnexusjs.dev"; const proxiedHeaders = { origin: publicOrigin, host: new URL(publicOrigin).host, }; for (const path of [ "/design-studio/studio.js", "/design-studio/api/application", ]) { const proxied = await fetch(origin + path, { headers: proxiedHeaders }); if (!proxied.ok) throw Error( `HTTPS-origin request over HTTP failed: ${path} (${proxied.status})`, ); } const rejected = await fetch(origin + "/design-studio/studio.js", { headers: { ...proxiedHeaders, origin: "https://attacker.example" }, }); if (rejected.status !== 403) throw Error("Cross-origin Studio requests were not rejected."); const generation = await ( await fetch(origin + "/design-studio/api/generation") ).json(); if ( process.env.APIMART_API_KEY?.trim() && (generation.provider !== "APIMart" || generation.model !== "gpt-6-luna") ) throw Error( "The compiled app did not pick up its server-side AI configuration.", ); if (application.enabled !== false) throw Error("Hosted Studio must remain export-only."); const preview = await fetch(origin + "/design-studio/api/preview", { method: "POST", headers: { "content-type": "application/json", origin }, body: JSON.stringify({ system: CORE_DESIGN_SYSTEMS.soft }), }); if (!preview.ok) throw Error( `Studio preview returned ${preview.status}: ${await preview.text()}`, ); const { url } = await preview.json(); if ( !url.startsWith("/design-studio/preview/") || !(await fetch(origin + url)).ok ) throw Error("Studio preview was not mounted under the app path."); console.log( "Compiled server serves component pages, Studio assets, export-only APIs and previews on one port.", ); } finally { server.kill("SIGTERM"); await server.exited; const errors = await new Response(server.stderr).text(); if (errors.trim()) console.error(errors); }