import { readdir, readFile } from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { build } from "esbuild"; import { beforeAll, describe, expect, it } from "vitest"; /** * The proof that this shell is a *generic* host rather than an application that happens to render * some known modules. * * Two levels, because they fail differently. At source level, a static import of a module would be * an architectural mistake a reviewer should catch. At bundle level, a dynamic import that a bundler * managed to resolve statically would be an invisible one: the code would look correct, the tests * would pass, and installing a new module would silently do nothing until the shell was rebuilt. * * `happy-dom` replaces the global `URL` with the DOM implementation, which `node:fs` rejects, so * everything here works in plain filesystem paths. */ const packageDirectory = path.dirname(path.dirname(fileURLToPath(import.meta.url))); const sourceDirectory = path.join(packageDirectory, "src"); /** The complete set of packages this shell may reach for. `@ekklesia/module-ui-host` is the generic * loader and `@ekklesia/module-ui-sdk` is the browser-side contract; no other `@ekklesia/*` package * — and no module — may appear. */ /** * The complete set of Ekklesia packages the shell may reach: the two browser contracts it mounts * modules with, and the two design packages every Ekklesia screen is built from (ADR 0039). All * four are browser-safe by construction and none of them names a module. */ const ALLOWED_EKKLESIA_IMPORTS = new Set([ "@ekklesia/design-tokens", "@ekklesia/module-ui-host", "@ekklesia/module-ui-sdk", "@ekklesia/ui", ]); async function sourceFiles(directory: string): Promise { const entries = await readdir(directory, { withFileTypes: true }); const files: string[] = []; for (const entry of entries) { const entryPath = path.join(directory, entry.name); if (entry.isDirectory()) { files.push(...(await sourceFiles(entryPath))); } else if (/\.tsx?$/u.test(entry.name)) { files.push(entryPath); } } return files.sort(); } function importSpecifiers(source: string): string[] { const specifiers = new Set(); const patterns = [ /\b(?:import|export)\s+(?:type\s+)?[^;]*?\bfrom\s*["']([^"'\n]+)["']/gu, /\bimport\s*["']([^"'\n]+)["']/gu, /\bimport\s*\(\s*["']([^"'\n]+)["']\s*\)/gu, ]; for (const pattern of patterns) { for (const match of source.matchAll(pattern)) { if (match[1]) { specifiers.add(match[1]); } } } return [...specifiers]; } let files: string[]; let sources: Map; beforeAll(async () => { files = await sourceFiles(sourceDirectory); sources = new Map( await Promise.all(files.map(async (file) => [file, await readFile(file, "utf8")] as const)), ); }); describe("the shell is coupled to no module", () => { it("has source files to check", () => { expect(files.length).toBeGreaterThan(3); }); it("imports no module, template, or workspace package beyond the two browser contracts", () => { for (const [file, source] of sources) { for (const specifier of importSpecifiers(source)) { const label = `${path.relative(packageDirectory, file)} -> ${specifier}`; expect(specifier.includes("modules/"), label).toBe(false); expect(specifier.includes("templates/"), label).toBe(false); // Nothing may climb out of this package at all. expect(specifier.startsWith("../../"), label).toBe(false); if (specifier.startsWith("@ekklesia/")) { expect(ALLOWED_EKKLESIA_IMPORTS.has(specifier), label).toBe(true); } } } }); it("performs no dynamic import of its own: all module loading goes through the generic loader", () => { for (const [file, source] of sources) { // Comment lines are dropped so a doc comment may still *describe* the loader's own // `(url) => import(url)`. The authoritative check is the bundle-level one below, which counts // dynamic imports in the emitted output where no comment survives. const code = source .split("\n") .filter((line) => !/^\s*(\*|\/\/)/u.test(line)) .join("\n"); const dynamicImports = [...code.matchAll(/\bimport\s*\(/gu)]; expect(dynamicImports, path.relative(packageDirectory, file)).toEqual([]); } }); }); describe("the shell builds no module knowledge into its bundle", () => { let bundle: string; beforeAll(async () => { // The real production bundle configuration, produced in memory so this test never depends on a // previous `pnpm build` having run. const result = await build({ absWorkingDir: packageDirectory, bundle: true, define: { __EKKLESIA_API_BASE_URL__: '""', "process.env.NODE_ENV": '"production"', }, entryPoints: [path.join(sourceDirectory, "browser", "main.tsx")], format: "esm", jsx: "automatic", logLevel: "silent", minify: false, platform: "browser", target: "es2022", write: false, }); bundle = result.outputFiles.map((file) => file.text).join("\n"); }, 60_000); it("leaves exactly one dynamic import — the loader's — unresolved for run time", () => { const dynamicImports = [...bundle.matchAll(/\bimport\s*\(/gu)]; // Exactly one: `@ekklesia/module-ui-host`'s `(url) => import(url)`. More would mean the shell // acquired its own loader; fewer would mean the bundler resolved it away and installed modules // could never be fetched at run time. expect(dynamicImports).toHaveLength(1); expect(bundle).toContain("(url) => import(url)"); }); it("contains no module identity and no module API prefix", () => { // The shell learns every module id, artifact URL, and API root from the inventory at run time. // Any of these appearing in the bundle would mean it had been baked in instead. expect(bundle).not.toContain("/api/v1/modules"); expect(bundle).not.toContain("/api/v1/module-ui/"); expect(bundle).not.toContain("ekklesia.module.json"); expect(bundle).not.toContain("entrypoints"); }); it("spells only the platform paths it genuinely owns", () => { // The platform's own endpoints — never a module's. Everything module-specific arrives at run // time from the navigation the server resolved. expect(bundle).toContain('"/api/v1/principal"'); expect(bundle).toContain('"/api/v1/dashboard/navigation"'); expect(bundle).toContain('"/api/auth/sign-in/email"'); expect(bundle).toContain('"/api/auth/sign-out"'); // The shell reads the navigation view, which already contains the inventory; it never calls // the host-neutral inventory endpoint itself. expect(bundle).not.toContain('"/api/v1/module-ui"'); }); it("contains only the browser HTTP auth adapter, never Better Auth or server identity internals", () => { expect(bundle.toLowerCase()).not.toContain("better-auth"); expect(bundle.toLowerCase()).not.toContain("drizzle"); expect(bundle).not.toContain("EKKLESIA_IDENTITY_SECRET"); expect(bundle).not.toContain("bootstrapPlatformOwner"); expect(bundle).not.toContain("session_token"); }); it("bakes in no group, label, or placement", () => { // Grouping is server-resolved configuration. A group id or label appearing in the bundle would // mean the shell had an opinion about an arrangement it must only render. // Deliberately distinctive terms: a generic word like "media" appears inside React's own // bundled source and would make this assertion meaningless rather than strict. for (const term of ["planning", "sunday-production", "livestream", "calendar", "ungrouped-"]) { expect(bundle.toLowerCase(), term).not.toContain(term); } }); it("uses neither eval nor a blob URL to load module code", () => { expect(bundle).not.toContain("new Function("); expect(bundle).not.toMatch(/\beval\s*\(/u); expect(bundle).not.toContain("createObjectURL"); }); }); describe("the display host is a separate generic browser bundle", () => { let bundle: string; beforeAll(async () => { const result = await build({ absWorkingDir: packageDirectory, bundle: true, define: { __EKKLESIA_API_BASE_URL__: '""', "process.env.NODE_ENV": '"production"', }, entryPoints: [path.join(sourceDirectory, "display", "main.tsx")], format: "esm", jsx: "automatic", logLevel: "silent", minify: false, platform: "browser", target: "es2022", write: false, }); bundle = result.outputFiles.map((file) => file.text).join("\n"); }, 60_000); it("contains the generic loader's one dynamic import and no statically known module", () => { expect([...bundle.matchAll(/\bimport\s*\(/gu)]).toHaveLength(1); expect(bundle).toContain("(url) => import(url)"); expect(bundle).not.toContain("ekklesia.module.json"); expect(bundle).not.toContain("/api/v1/modules"); for (const moduleId of ["announcements", "calendar", "livestream", "signage"]) { expect(bundle.toLowerCase()).not.toContain(moduleId); } }); it("knows only its host route resources, with no Dashboard application or transport coupling", () => { expect(bundle).toContain('"/api/v1/module-displays"'); // Pairing is a display concern and lives here; the Dashboard bundle has its own management UI. expect(bundle).toContain('"/api/v1/device-pairing/requests"'); expect(bundle).toContain('"/api/v1/device-pairing/claims"'); // And the display deliberately never asks the *human* principal endpoint: a paired device is // anonymous to it, so consulting it would report every display in the building as signed out // (ADR 0036). The descriptor reports the viewer instead. expect(bundle).not.toContain('"/api/v1/principal"'); // A display never approves, lists, or revokes a device — that is the administrator's surface. expect(bundle).not.toContain('"/api/v1/device-pairing/approvals"'); expect(bundle).not.toContain('"/api/v1/devices"'); expect(bundle).not.toContain('"/api/v1/dashboard/navigation"'); expect(bundle).not.toContain('"/api/auth/sign-in/email"'); expect(bundle).not.toContain('"/api/auth/sign-out"'); expect(bundle).not.toContain("Loading Ekklesia"); expect(bundle).not.toContain("Sign in to Ekklesia"); expect(bundle.toLowerCase()).not.toContain("socket.io"); expect(bundle.toLowerCase()).not.toContain("mqtt"); }); it("uses neither eval nor a blob URL to load display code", () => { expect(bundle).not.toContain("new Function("); expect(bundle).not.toMatch(/\beval\s*\(/u); expect(bundle).not.toContain("createObjectURL"); }); });