fix: serve design studio through the component app production route

This commit is contained in:
Clintchiz committed 2026-10-19 13:34:08 +05:30
1 parent 3987ddbcfe
commit 9a6a2e0a8d
11 files changed
+174 -47

No files matched your search

+4 -18
View File
@@ -1,18 +1,4 @@
import { startStudio } from "./studio.ts";
const studio = await startStudio();
const site = Bun.spawn([process.execPath, "dist/server.js"], {
stdout: "inherit",
stderr: "inherit",
stdin: "inherit",
env: process.env,
});
const stop = () => {
studio.stop(true);
site.kill("SIGTERM");
};
process.on("SIGINT", stop);
process.on("SIGTERM", stop);
const code = await site.exited;
studio.stop(true);
process.exit(code);
// Compatibility entry point: Studio is mounted in the app middleware now.
const entry = "../dist/server.js";
await import(entry);
export {};
+78
View File
@@ -0,0 +1,78 @@
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);
}