import assert from "node:assert/strict"; import fs from "node:fs"; const protocol = fs.readFileSync(new URL("../../web/protocol/web-engine.ts", import.meta.url), "utf8"); const registry = fs.readFileSync(new URL("../../web/protocol/web-engine-capabilities.ts", import.meta.url), "utf8"); const app = fs.readFileSync(new URL("../../web/app/src/app/App.tsx", import.meta.url), "utf8"); const catalog = JSON.parse(fs.readFileSync(new URL("../../web/app/src/capabilities/blender-feature-route-catalog.json", import.meta.url), "utf8")); const commandUnion = protocol.split("export type WebEngineEditCommand =", 2)[1]?.split("/** JSON values", 1)[0] ?? ""; const commands = new Set([...commandUnion.matchAll(/type:\s*"([A-Za-z0-9]+)"/gu)].map((match) => match[1])); commands.add("moveNLAStrip"); const registered = new Set([...registry.matchAll(/^ ([A-Za-z0-9]+): \{/gmu)].map((match) => match[1])); registered.delete("listBlenderOperators"); registered.delete("invokeBlenderOperator"); const missing = [...commands].filter((command) => !registered.has(command)).sort(); const stale = [...registered].filter((command) => !commands.has(command)).sort(); assert.deepEqual(missing, [], `WebEngine edit commands missing capabilities: ${missing.join(",")}`); assert.deepEqual(stale, [], `WebEngine capabilities without edit commands: ${stale.join(",")}`); const normalizeCommandIdentifier = (identifier) => String(identifier ?? "").replace(/^(?:web[-_]?command|command):/iu, ""); const webMainEntries = catalog.entries.filter((entry) => entry.route === "WEB_MAIN" && entry.kind === "web-command"); const missingWebMainRoutes = webMainEntries.map((entry) => normalizeCommandIdentifier(entry.identifier)).filter((identifier) => identifier && !registered.has(identifier)); const serverEntries = catalog.entries.filter((entry) => entry.route === "SERVER_NATIVE"); const missingServerRoutes = serverEntries.filter((entry) => entry.httpRoute !== "/api/server-operator-job").map((entry) => entry.identifier); const browserEntries = catalog.entries.filter((entry) => entry.route === "BROWSER_EQUIVALENT"); const browserGroups = new Set(Object.keys(catalog.browserActionGroups ?? {})); const missingBrowserGroups = browserEntries.filter((entry) => !browserGroups.has(entry.browserActionGroup)).map((entry) => entry.identifier); const browserCommands = [...new Set(browserEntries.map((entry) => catalog.browserActionGroups?.[entry.browserActionGroup]?.command).filter(Boolean))]; const missingBrowserCommands = browserCommands.filter((command) => !app.includes(`id: "${command}"`)); const browserActionableEntries = browserEntries.filter((entry) => { const command = catalog.browserActionGroups?.[entry.browserActionGroup]?.command; return browserGroups.has(entry.browserActionGroup) && Boolean(command) && app.includes(`id: "${command}"`); }); const runtimeAudit = { schemaVersion: 1, command: "featureRouteRuntimeAudit", status: missingWebMainRoutes.length || missingServerRoutes.length || missingBrowserGroups.length ? "FAILED" : "PASS", webMain: { entries: webMainEntries.length, registered: webMainEntries.length - missingWebMainRoutes.length, missing: missingWebMainRoutes }, serverNative: { entries: serverEntries.length, httpRoute: "/api/server-operator-job", missing: missingServerRoutes }, browserEquivalent: { entries: browserEntries.length, actionable: browserActionableEntries.length, groups: [...browserGroups].sort(), missing: missingBrowserGroups, missingCommands: missingBrowserCommands }, behavioralEvidence: { separate: true, status: catalog.behavioralEvidence?.status, verifiedUnits: catalog.behavioralEvidence?.verifiedUnits, unverifiedUnits: catalog.behavioralEvidence?.unverifiedUnits }, }; assert.deepEqual(missingWebMainRoutes, [], `WEB_MAIN commands missing capabilities: ${missingWebMainRoutes.join(",")}`); assert.deepEqual(missingServerRoutes, [], `SERVER_NATIVE entries missing HTTP route: ${missingServerRoutes.join(",")}`); assert.deepEqual(missingBrowserGroups, [], `BROWSER_EQUIVALENT entries missing browser action group: ${missingBrowserGroups.join(",")}`); assert.deepEqual(missingBrowserCommands, [], `BROWSER_EQUIVALENT browser action adapters missing from App: ${missingBrowserCommands.join(",")}`); process.stdout.write(`web-engine-command-registry-ok commands=${commands.size} featureRouteRuntimeAudit=${JSON.stringify(runtimeAudit)}\n`);