feat(console-ui): add TanStack management console

- provide Runtime overview, service lifecycle, invocation, and event views\n- add immutable WIT package listing, publication, and download workflows\n- connect all state to the Rust management API without direct database access\n- use TanStack Start with Node serving, Oxlint, Oxfmt, and SSR checks\n- include responsive operational layouts and empty Actor placeholders
This commit is contained in:
Maofeng
2026-07-27 05:02:35 +08:00
parent b7fffd0d0f
commit d32895f5ca
16 changed files with 7390 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
import assert from "node:assert/strict";
import { spawn } from "node:child_process";
import { access, readFile } from "node:fs/promises";
import { createServer } from "node:net";
import { fileURLToPath } from "node:url";
import test, { after, before } from "node:test";
let serverProcess;
let serverOrigin;
async function reservePort() {
const server = createServer();
await new Promise((resolve, reject) => {
server.once("error", reject);
server.listen(0, "127.0.0.1", resolve);
});
const address = server.address();
assert(address && typeof address === "object");
await new Promise((resolve, reject) => {
server.close((error) => (error ? reject(error) : resolve()));
});
return address.port;
}
before(async () => {
const port = await reservePort();
serverOrigin = `http://127.0.0.1:${port}`;
serverProcess = spawn(
process.execPath,
[
fileURLToPath(new URL("../node_modules/srvx/bin/srvx.mjs", import.meta.url)),
"serve",
"--prod",
"--entry",
"dist/server/server.js",
"--static",
"../client",
],
{
env: {
...process.env,
HOST: "127.0.0.1",
PORT: String(port),
},
stdio: ["ignore", "pipe", "pipe"],
},
);
for (let attempt = 0; attempt < 50; attempt += 1) {
if (serverProcess.exitCode !== null) {
throw new Error(`Node server exited with code ${serverProcess.exitCode}`);
}
try {
const response = await fetch(serverOrigin);
if (response.ok) return;
} catch {
// The server is still starting.
}
await new Promise((resolve) => setTimeout(resolve, 100));
}
throw new Error("Node server did not become ready");
});
after(() => {
serverProcess?.kill("SIGTERM");
});
test("server-renders the Wasm management console", async () => {
const response = await fetch(serverOrigin, {
headers: { accept: "text/html" },
});
assert.equal(response.status, 200);
assert.match(response.headers.get("content-type") ?? "", /^text\/html\b/i);
const html = await response.text();
assert.match(html, /<title>Wasmeld Console<\/title>/i);
assert.match(html, /运行概览/);
assert.match(html, /WIT 包/);
assert.match(html, /连接中/);
assert.match(html, /来自 Wasmeld/);
assert.match(html, /Component Model/);
assert.doesNotMatch(html, /本地演示|codex-preview|vinext|next\.js/i);
});
test("uses TanStack Start routing and produces Node artifacts", async () => {
const [rootRoute, indexRoute, router, packageJson, viteConfig] = await Promise.all([
readFile(new URL("../src/routes/__root.tsx", import.meta.url), "utf8"),
readFile(new URL("../src/routes/index.tsx", import.meta.url), "utf8"),
readFile(new URL("../src/router.tsx", import.meta.url), "utf8"),
readFile(new URL("../package.json", import.meta.url), "utf8"),
readFile(new URL("../vite.config.ts", import.meta.url), "utf8"),
]);
assert.match(rootRoute, /createRootRoute/);
assert.match(rootRoute, /<HeadContent \/>/);
assert.match(rootRoute, /<Scripts \/>/);
assert.match(indexRoute, /createFileRoute\("\/"\)/);
assert.match(router, /createRouter/);
assert.match(packageJson, /"@tanstack\/react-start"/);
assert.match(packageJson, /"srvx"/);
assert.doesNotMatch(packageJson, /"next"|"vinext"|"drizzle-orm"/);
assert.match(viteConfig, /tanstackStart\(\{/);
await access(new URL("../dist/server/server.js", import.meta.url));
await access(new URL("../dist/client/og.png", import.meta.url));
});