79 lines
2.5 KiB
TypeScript
79 lines
2.5 KiB
TypeScript
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",
|
|
]) {
|
|
const response = await fetch(origin + path);
|
|
if (!response.ok) throw Error(`${path} returned ${response.status}`);
|
|
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();
|
|
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);
|
|
}
|