import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; import { ANONYMOUS_PRINCIPAL, type ModuleDatabase, type ModuleRuntimeServices, } from "@ekklesia/contracts"; import { type AuthorizationEvaluator, type AuthorizationStore, CORE_PERMISSION_REGISTRY, } from "@ekklesia/core/authorization"; import type { EkklesiaIdentityRuntime } from "@ekklesia/core/identity"; import { createModuleUiPublicationRegistry, type DiscoveredModule, discoverModules, } from "@ekklesia/core/modules"; import { loadModuleUi } from "@ekklesia/module-ui-host"; import type { Hono } from "hono"; import { afterEach, describe, expect, it, vi } from "vitest"; import { createApiApp } from "../src/app.js"; import { MODULE_UI_ASSET_PREFIX, MODULE_UI_CACHE_CONTROL, MODULE_UI_CONTENT_TYPE, moduleUiArtifactUrl, } from "../src/module-ui-routes.js"; const trustedOrigin = "https://dashboard.example.test"; const temporaryDirectories: string[] = []; afterEach(async () => { await Promise.all( temporaryDirectories .splice(0) .map((directory) => rm(directory, { force: true, recursive: true })), ); }); async function createModulesDirectory(): Promise { const directory = await mkdtemp(path.join(tmpdir(), "ekklesia-api-ui-publication-")); temporaryDirectories.push(directory); return directory; } /** A real, loadable module UI artifact: a valid `ModuleUiDefinition` default export, written as * plain ESM. `@ekklesia/module-ui-build` already proves the esbuild pipeline emits this shape * (`packages/module-ui-host/tests/real-artifact.test.ts`); what these tests need is a byte * sequence whose exact publication and loader-compatibility can be asserted. */ function artifactSource(id: string, version: string): string { return `export default { id: ${JSON.stringify(id)}, version: ${JSON.stringify(version)}, mount(container, context) { container.textContent = context.moduleId + ":mounted"; return () => { container.textContent = ""; }; }, }; `; } interface ModuleFixture { readonly id: string; readonly version?: string; /** Written to `dist/ui/index.js` and declared as `entrypoints.ui`. Omit for a backend-only * module, whose manifest then declares no UI entrypoint at all. */ readonly ui?: string; /** Extra package-relative files written alongside the artifact — a source map, a sibling bundle * chunk, an operator secret — so "only the declared artifact is reachable" is asserted against * files that genuinely exist on disk rather than against hypothetical ones. */ readonly extraFiles?: Readonly>; } async function createModule(modulesDirectory: string, fixture: ModuleFixture): Promise { const version = fixture.version ?? "0.1.0"; const moduleDirectory = path.join(modulesDirectory, fixture.id); await mkdir(path.join(moduleDirectory, "dist/server"), { recursive: true }); await writeFile( path.join(moduleDirectory, "package.json"), JSON.stringify({ ekklesia: { layer: "module" }, name: `@test/module-${fixture.id}`, type: "module", version, }), ); await writeFile( path.join(moduleDirectory, "ekklesia.module.json"), JSON.stringify({ compatibility: { moduleSdk: "^0.1.0", platform: "^0.1.0" }, entrypoints: { server: "./dist/server/index.js", ...(fixture.ui === undefined ? {} : { ui: "./dist/ui/index.js" }), }, id: fixture.id, manifestVersion: 4, name: `${fixture.id} module`, version, }), ); await writeFile( path.join(moduleDirectory, "dist/server/index.js"), `export default { id: ${JSON.stringify(fixture.id)}, version: ${JSON.stringify(version)} };`, ); if (fixture.ui !== undefined) { await mkdir(path.join(moduleDirectory, "dist/ui"), { recursive: true }); await writeFile(path.join(moduleDirectory, "dist/ui/index.js"), fixture.ui); } for (const [relativePath, contents] of Object.entries(fixture.extraFiles ?? {})) { const target = path.join(moduleDirectory, relativePath); await mkdir(path.dirname(target), { recursive: true }); await writeFile(target, contents); } } interface Installation { readonly app: Hono; readonly modules: readonly DiscoveredModule[]; readonly modulesDirectory: string; } /** * A real installation: fixtures on disk, Core's real `discoverModules` (manifest validation, * compiled-artifact validation, package containment, symlink canonicalization), Core's real * publication registry, and the real `createApiApp`. Nothing about the publication policy or the * artifact path is hand-built, so these tests exercise the actual security boundary rather than a * reconstruction of it. */ async function install(fixtures: readonly ModuleFixture[]): Promise { const modulesDirectory = await createModulesDirectory(); for (const fixture of fixtures) { await createModule(modulesDirectory, fixture); } const modules = await discoverModules({ moduleSdkVersion: "0.1.0", modulesDirectory, platformVersion: "0.1.0", }); const database = Object.freeze({}) as unknown as ModuleDatabase; const services: ModuleRuntimeServices = Object.freeze({ database }); const runtime: EkklesiaIdentityRuntime = { handleAuthRequest: vi.fn().mockResolvedValue(new Response(null, { status: 200 })), // Anonymous, and every authorization decision denied: whatever this route does, it does it // without a session and without a single permission. resolvePrincipal: vi.fn().mockResolvedValue(ANONYMOUS_PRINCIPAL), }; const evaluator: AuthorizationEvaluator = { authorize: vi.fn().mockResolvedValue({ allowed: false }), }; const store = { createGrant: vi.fn(), grantPlatformOwner: vi.fn(), hasActiveGrant: vi.fn().mockResolvedValue(false), isPlatformOwner: vi.fn().mockResolvedValue(false), listGrantsForUser: vi.fn().mockResolvedValue({ grants: [] }), revokeGrant: vi.fn(), revokePlatformOwner: vi.fn(), } as unknown as AuthorizationStore; const app = createApiApp({ authorization: { evaluator, registry: CORE_PERMISSION_REGISTRY, store }, moduleRoutes: Object.freeze([]), moduleServices: services, moduleUi: createModuleUiPublicationRegistry(modules), runtime, trustedOrigins: [trustedOrigin], }); return { app, modules, modulesDirectory }; } describe("module UI artifact publication — happy path", () => { it("serves the declared artifact byte-for-byte at the canonical URL", async () => { const source = artifactSource("announcements", "1.4.2"); const { app } = await install([{ id: "announcements", ui: source, version: "1.4.2" }]); const response = await app.request( moduleUiArtifactUrl({ moduleId: "announcements", version: "1.4.2" }), ); expect(response.status).toBe(200); expect(await response.text()).toBe(source); }); it("serves it as ES module JavaScript, with sniffing disabled", async () => { const { app } = await install([ { id: "announcements", ui: artifactSource("announcements", "0.1.0") }, ]); const response = await app.request( moduleUiArtifactUrl({ moduleId: "announcements", version: "0.1.0" }), ); expect(response.headers.get("content-type")).toBe(MODULE_UI_CONTENT_TYPE); expect(response.headers.get("x-content-type-options")).toBe("nosniff"); }); it("revalidates rather than promising immutability the platform cannot enforce", async () => { const { app } = await install([ { id: "announcements", ui: artifactSource("announcements", "0.1.0") }, ]); const response = await app.request( moduleUiArtifactUrl({ moduleId: "announcements", version: "0.1.0" }), ); expect(response.headers.get("cache-control")).toBe(MODULE_UI_CACHE_CONTROL); // A reused version can be rebuilt with different bytes, so neither of these may ever appear. expect(response.headers.get("cache-control")).not.toContain("immutable"); expect(response.headers.get("cache-control")).not.toContain("max-age"); // A strong validator (no `W/` prefix), so the revalidation above is actually decidable. expect(response.headers.get("etag")).toMatch(/^"[A-Za-z0-9_-]+"$/u); }); it("varies on Origin, so a shared cache cannot reuse one origin's CORS response for another", async () => { const { app } = await install([ { id: "announcements", ui: artifactSource("announcements", "0.1.0") }, ]); const response = await app.request( moduleUiArtifactUrl({ moduleId: "announcements", version: "0.1.0" }), { headers: { origin: trustedOrigin } }, ); expect(response.headers.get("access-control-allow-origin")).toBe(trustedOrigin); expect(response.headers.get("vary")?.toLowerCase()).toContain("origin"); }); it("answers a matching conditional request with 304 and no body", async () => { const { app } = await install([ { id: "announcements", ui: artifactSource("announcements", "0.1.0") }, ]); const url = moduleUiArtifactUrl({ moduleId: "announcements", version: "0.1.0" }); const etag = (await app.request(url)).headers.get("etag") as string; const revalidated = await app.request(url, { headers: { "if-none-match": etag } }); expect(revalidated.status).toBe(304); expect(await revalidated.text()).toBe(""); expect(revalidated.headers.get("etag")).toBe(etag); expect(revalidated.headers.get("cache-control")).toBe(MODULE_UI_CACHE_CONTROL); }); it("applies RFC 9110 weak comparison and the wildcard, and refuses a stale validator", async () => { const { app } = await install([ { id: "announcements", ui: artifactSource("announcements", "0.1.0") }, ]); const url = moduleUiArtifactUrl({ moduleId: "announcements", version: "0.1.0" }); const etag = (await app.request(url)).headers.get("etag") as string; const cases: ReadonlyArray = [ [`W/${etag}`, 304], ["*", 304], [`"someone-elses-tag", ${etag}`, 304], ['"stale-tag"', 200], ["not a valid header at all", 200], ]; for (const [header, expected] of cases) { const response = await app.request(url, { headers: { "if-none-match": header } }); expect(response.status, header).toBe(expected); } }); it("recomputes the validator from the bytes actually on disk after a same-version rebuild", async () => { const { app, modulesDirectory } = await install([ { id: "announcements", ui: artifactSource("announcements", "0.1.0") }, ]); const url = moduleUiArtifactUrl({ moduleId: "announcements", version: "0.1.0" }); const first = await app.request(url); const firstEtag = first.headers.get("etag"); // Exactly the develop-in-place case an `immutable` policy would have made unrecoverable. const rebuilt = `${artifactSource("announcements", "0.1.0")}// rebuilt\n`; await writeFile(path.join(modulesDirectory, "announcements/dist/ui/index.js"), rebuilt); const second = await app.request(url, { headers: { "if-none-match": firstEtag as string } }); expect(second.status).toBe(200); expect(await second.text()).toBe(rebuilt); expect(second.headers.get("etag")).not.toBe(firstEtag); }); it("serves the artifact without executing it, even when its top level throws", async () => { const exploding = 'throw new Error("module UI must never be evaluated on the server");\n'; const { app } = await install([{ id: "announcements", ui: exploding }]); const response = await app.request( moduleUiArtifactUrl({ moduleId: "announcements", version: "0.1.0" }), ); expect(response.status).toBe(200); expect(await response.text()).toBe(exploding); }); it("requires no authentication, by deliberate decision", async () => { // The installation's identity runtime resolves an anonymous principal and its evaluator denies // every permission. If this route ever grows an authentication or authorization requirement, // this test is the one that must be consciously changed. const { app } = await install([ { id: "announcements", ui: artifactSource("announcements", "0.1.0") }, ]); const response = await app.request( moduleUiArtifactUrl({ moduleId: "announcements", version: "0.1.0" }), ); expect(response.status).toBe(200); }); it("still refuses a protected platform API to the same anonymous caller", async () => { // Publication grants no authority: the very same caller that just downloaded the bundle is // still denied every permission-protected route. const { app } = await install([ { id: "announcements", ui: artifactSource("announcements", "0.1.0") }, ]); expect( (await app.request(moduleUiArtifactUrl({ moduleId: "announcements", version: "0.1.0" }))) .status, ).toBe(200); const grants = await app.request("/api/v1/grants?userId=someone"); expect(grants.status).toBe(403); }); }); describe("module UI artifact publication — module identity", () => { async function expectNotPublished(app: Hono, url: string): Promise { const response = await app.request(url); expect(response.status).toBe(404); expect(await response.json()).toEqual({ code: "MODULE_UI_NOT_PUBLISHED" }); } it("refuses a module that is not installed", async () => { const { app } = await install([ { id: "announcements", ui: artifactSource("announcements", "0.1.0") }, ]); await expectNotPublished(app, moduleUiArtifactUrl({ moduleId: "calendar", version: "0.1.0" })); }); it("refuses an installed module that declares no UI entrypoint", async () => { const { app } = await install([{ id: "calendar" }]); await expectNotPublished(app, moduleUiArtifactUrl({ moduleId: "calendar", version: "0.1.0" })); }); it("refuses a version other than the installed one rather than serving what exists", async () => { const { app } = await install([ { id: "announcements", ui: artifactSource("announcements", "1.4.2"), version: "1.4.2" }, ]); for (const version of ["1.4.1", "1.4.3", "1.4", "latest", "^1.4.2"]) { await expectNotPublished(app, moduleUiArtifactUrl({ moduleId: "announcements", version })); } expect( (await app.request(moduleUiArtifactUrl({ moduleId: "announcements", version: "1.4.2" }))) .status, ).toBe(200); }); it("makes unknown module, no UI, and wrong version indistinguishable", async () => { const { app } = await install([ { id: "announcements", ui: artifactSource("announcements", "1.4.2"), version: "1.4.2" }, { id: "calendar" }, ]); const responses = await Promise.all( [ moduleUiArtifactUrl({ moduleId: "signage", version: "1.4.2" }), moduleUiArtifactUrl({ moduleId: "calendar", version: "0.1.0" }), moduleUiArtifactUrl({ moduleId: "announcements", version: "9.9.9" }), ].map(async (url) => { const response = await app.request(url); return { body: await response.text(), status: response.status }; }), ); // An anonymous caller must not be able to use the distinction to enumerate an installation. expect(new Set(responses.map((entry) => `${entry.status}:${entry.body}`)).size).toBe(1); }); it("never serves one module's artifact under another module's identity", async () => { const { app } = await install([ { id: "announcements", ui: artifactSource("announcements", "0.1.0") }, { id: "signage", ui: artifactSource("signage", "0.1.0") }, ]); const announcements = await app.request( moduleUiArtifactUrl({ moduleId: "announcements", version: "0.1.0" }), ); const signage = await app.request( moduleUiArtifactUrl({ moduleId: "signage", version: "0.1.0" }), ); expect(await announcements.text()).toContain('"announcements"'); expect(await signage.text()).toContain('"signage"'); }); }); describe("module UI artifact publication — filesystem exposure", () => { const sourceMap = '{"version":3,"sources":["../../src/ui/index.tsx"],"sourcesContent":["secret"]}'; /** One installation carrying every kind of file a wildcard route would have leaked. */ async function installWithNeighbours(): Promise { return install([ { extraFiles: { ".env": "EKKLESIA_IDENTITY_SECRET=super-secret\n", "dist/server/index.js.map": sourceMap, "dist/ui/index.js.map": sourceMap, "dist/ui/internal-chunk.js": "export const internal = 1;\n", }, id: "announcements", ui: artifactSource("announcements", "0.1.0"), }, ]); } it("exposes no path shape beyond the two identity segments", async () => { const { app } = await installWithNeighbours(); const base = `${MODULE_UI_ASSET_PREFIX}/announcements`; const unreachable = [ // Source maps: real files, sitting right next to the published artifact. `${base}/0.1.0.map`, `${base}/0.1.0/index.js.map`, `${base}/0.1.0/dist/ui/index.js.map`, // Sibling bundle output and package-root files. `${base}/0.1.0/internal-chunk.js`, `${base}/0.1.0/package.json`, `${base}/0.1.0/ekklesia.module.json`, `${base}/0.1.0/.env`, // A trailing segment of any kind, including an empty one. `${base}/0.1.0/`, `${base}/0.1.0/anything`, // Fewer segments than the route declares. The collection root itself is deliberately absent // from this list: it is the authenticated inventory (ADR 0026) rather than a 404, and is // asserted separately below. base, ]; for (const url of unreachable) { const response = await app.request(url); expect(response.status, url).toBe(404); // Nothing leaks: not the artifact, not a neighbour, not a path. const body = await response.text(); expect(body, url).not.toContain("secret"); expect(body, url).not.toContain("internal"); expect(body, url).not.toContain("mount"); } }); it("serves no artifact bytes from the collection root, which is the authenticated inventory", async () => { const { app } = await installWithNeighbours(); // `/api/v1/module-ui` lists what a host may mount (ADR 0026) and requires // `core.modules.ui.read`; this installation's caller is anonymous, so it is refused. Either way // it never yields a byte of any artifact or neighbouring file. const response = await app.request(MODULE_UI_ASSET_PREFIX); const body = await response.text(); expect(response.status).toBe(403); expect(body).not.toContain("mount"); expect(body).not.toContain("secret"); expect(body).not.toContain("internal"); }); it("treats a traversal attempt as an ordinary failed lookup", async () => { const { app } = await installWithNeighbours(); const attempts = [ `${MODULE_UI_ASSET_PREFIX}/..%2F..%2F..%2Fetc%2Fpasswd/0.1.0`, `${MODULE_UI_ASSET_PREFIX}/announcements/..%2F..%2Fpackage.json`, `${MODULE_UI_ASSET_PREFIX}/announcements%2Fdist%2Fui%2Findex.js/0.1.0`, `${MODULE_UI_ASSET_PREFIX}/${encodeURIComponent("../announcements")}/0.1.0`, `${MODULE_UI_ASSET_PREFIX}/announcements/${encodeURIComponent("0.1.0/../../.env")}`, `${MODULE_UI_ASSET_PREFIX}/announcements/${encodeURIComponent("0.1.0 .map")}`, ]; for (const url of attempts) { const response = await app.request(url); expect(response.status, url).toBe(404); expect(await response.text(), url).not.toContain("secret"); } }); it("keeps an absolute filesystem path from ever becoming a request parameter that works", async () => { const { app, modules } = await install([ { id: "announcements", ui: artifactSource("announcements", "0.1.0") }, ]); const artifactPath = (modules[0] as DiscoveredModule).entrypoints.ui as string; const response = await app.request( `${MODULE_UI_ASSET_PREFIX}/${encodeURIComponent(artifactPath)}/0.1.0`, ); expect(response.status).toBe(404); }); }); describe("module UI artifact publication — request surface", () => { it("is read-only: no other method is routed at all", async () => { const { app } = await install([ { id: "announcements", ui: artifactSource("announcements", "0.1.0") }, ]); const url = moduleUiArtifactUrl({ moduleId: "announcements", version: "0.1.0" }); for (const method of ["POST", "PUT", "PATCH", "DELETE"]) { const response = await app.request(url, { method }); expect(response.status, method).toBe(404); expect(await response.json(), method).toEqual({ code: "NOT_FOUND" }); } }); it("answers HEAD with the full header contract and no body", async () => { const { app } = await install([ { id: "announcements", ui: artifactSource("announcements", "0.1.0") }, ]); const url = moduleUiArtifactUrl({ moduleId: "announcements", version: "0.1.0" }); const response = await app.request(url, { method: "HEAD" }); expect(response.status).toBe(200); expect(response.headers.get("content-type")).toBe(MODULE_UI_CONTENT_TYPE); expect(response.headers.get("etag")).toBe((await app.request(url)).headers.get("etag")); expect(await response.text()).toBe(""); }); it("fails closed on malformed percent-encoding instead of throwing", async () => { const { app } = await install([ { id: "announcements", ui: artifactSource("announcements", "0.1.0") }, ]); for (const url of [ `${MODULE_UI_ASSET_PREFIX}/%zz/0.1.0`, `${MODULE_UI_ASSET_PREFIX}/announcements/%E0%A4%A`, `${MODULE_UI_ASSET_PREFIX}/a%00b/0.1.0`, ]) { const response = await app.request(url); // An ordinary refused lookup, never a 500 that would reveal a decoding failure. expect(response.status, url).toBe(404); expect(await response.json(), url).toEqual({ code: "MODULE_UI_NOT_PUBLISHED" }); } }); }); describe("module UI artifact publication — artifact changed after discovery", () => { it("reports a sanitized 503 when the validated artifact has been deleted", async () => { const { app, modulesDirectory } = await install([ { id: "announcements", ui: artifactSource("announcements", "0.1.0") }, ]); const url = moduleUiArtifactUrl({ moduleId: "announcements", version: "0.1.0" }); expect((await app.request(url)).status).toBe(200); await rm(path.join(modulesDirectory, "announcements/dist/ui/index.js")); const response = await app.request(url); expect(response.status).toBe(503); expect(await response.json()).toEqual({ code: "MODULE_UI_ARTIFACT_UNAVAILABLE" }); }); it("leaks no filesystem detail when the artifact is unreadable", async () => { const { app, modulesDirectory } = await install([ { id: "announcements", ui: artifactSource("announcements", "0.1.0") }, ]); const artifact = path.join(modulesDirectory, "announcements/dist/ui/index.js"); await rm(artifact); // Replaced by a directory: a different `errno` (EISDIR) on the same validated path. await mkdir(artifact); const response = await app.request( moduleUiArtifactUrl({ moduleId: "announcements", version: "0.1.0" }), ); const body = await response.text(); expect(response.status).toBe(503); expect(body).not.toContain(modulesDirectory); expect(body).not.toContain(tmpdir()); expect(body).not.toContain("dist"); expect(body).not.toContain("EISDIR"); expect(response.headers.get("cache-control")).toBe("no-store"); }); it("never falls back to a reconstructed path when the exact artifact is gone", async () => { const { app, modulesDirectory } = await install([ { id: "announcements", ui: artifactSource("announcements", "0.1.0") }, ]); const moduleDirectory = path.join(modulesDirectory, "announcements"); await rm(path.join(moduleDirectory, "dist/ui/index.js")); // Plausible neighbours a path-guessing implementation might land on instead. await writeFile(path.join(moduleDirectory, "dist/ui/index.mjs"), "export default {};\n"); await writeFile(path.join(moduleDirectory, "dist/index.js"), "export default {};\n"); const response = await app.request( moduleUiArtifactUrl({ moduleId: "announcements", version: "0.1.0" }), ); expect(response.status).toBe(503); }); }); describe("module UI artifact URL generation", () => { it("agrees with the route it is generated for", async () => { const { app } = await install([ { id: "announcements", ui: artifactSource("announcements", "1.4.2"), version: "1.4.2" }, ]); const url = moduleUiArtifactUrl({ moduleId: "announcements", version: "1.4.2" }); expect(url).toBe(`${MODULE_UI_ASSET_PREFIX}/announcements/1.4.2`); expect((await app.request(url)).status).toBe(200); }); it("is root-relative, so a host resolves it against whichever API origin it is configured for", () => { const url = moduleUiArtifactUrl({ moduleId: "announcements", version: "1.4.2" }); expect(url.startsWith("/")).toBe(true); expect(new URL(url, "https://ekklesia.example.test").href).toBe( `https://ekklesia.example.test${MODULE_UI_ASSET_PREFIX}/announcements/1.4.2`, ); // A split-origin host resolves the identical value against its own API base. expect(new URL(url, "http://127.0.0.1:3000").href).toBe( `http://127.0.0.1:3000${MODULE_UI_ASSET_PREFIX}/announcements/1.4.2`, ); }); it("encodes hostile input instead of letting it restructure the URL", () => { const hostile = [ "../../etc/passwd", "announcements/dist/ui/index.js", "announcements?x=1", "announcements#fragment", "announcements ", "a b", ]; for (const value of hostile) { // Still exactly the prefix's own segments plus one for the module and one for the version. const expectedSegments = MODULE_UI_ASSET_PREFIX.split("/").length + 2; const moduleUrl = moduleUiArtifactUrl({ moduleId: value, version: "0.1.0" }); expect(moduleUrl.split("/"), value).toHaveLength(expectedSegments); expect(moduleUrl.includes("?"), value).toBe(false); expect(moduleUrl.includes("#"), value).toBe(false); const versionUrl = moduleUiArtifactUrl({ moduleId: "announcements", version: value }); expect(versionUrl.split("/"), value).toHaveLength(expectedSegments); expect(versionUrl.includes("?"), value).toBe(false); expect(versionUrl.includes("#"), value).toBe(false); } }); }); describe("module UI artifact publication — the generic loader consumes what is published", () => { /** * Node cannot `import()` an `http:` URL, so the two halves of the chain are proven separately and * honestly: the tests above prove the route publishes the exact artifact bytes with ES module * JavaScript semantics over real HTTP routing, and this one takes **those very bytes, as returned * by the route**, and puts them through the real `@ekklesia/module-ui-host` loader as a `data:` * URL — a genuine module URL, the same technique ADR 0024's own loader tests use. Nothing here * reads the artifact from the filesystem. */ async function publishedModuleUrl(app: Hono, moduleId: string, version: string): Promise { const response = await app.request(moduleUiArtifactUrl({ moduleId, version })); expect(response.status).toBe(200); expect(response.headers.get("content-type")).toBe(MODULE_UI_CONTENT_TYPE); const bytes = Buffer.from(await response.arrayBuffer()); return `data:text/javascript;base64,${bytes.toString("base64")}`; } /** * The loader forwards the container untouched to the module's own `mount` and never inspects it, * so a plain sink is enough and no DOM implementation is required. The container's type is read * back off `loadModuleUi` itself rather than written as `Element`, because `apps/api` compiles * against Node libs only — naming a DOM type here would be the same browser/server confusion this * route exists to keep apart. */ type LoaderContainer = Parameters[0]["container"]; function containerSink(): { textContent: string } { return { textContent: "" }; } function hostContext() { return { api: { request: async () => new Response(null, { status: 200 }) }, moduleId: "announcements", permissions: { has: () => false }, principal: { kind: "anonymous" as const }, }; } it("mounts and unmounts an artifact obtained from the publication route", async () => { const { app } = await install([ { id: "announcements", ui: artifactSource("announcements", "1.4.2"), version: "1.4.2" }, ]); const url = await publishedModuleUrl(app, "announcements", "1.4.2"); const container = containerSink(); const mounted = await loadModuleUi({ container: container as unknown as LoaderContainer, context: hostContext(), source: { moduleId: "announcements", url, version: "1.4.2" }, }); expect(container.textContent).toBe("announcements:mounted"); await mounted.unmount(); expect(container.textContent).toBe(""); }); it("still rejects the published artifact when the host expects a different identity", async () => { const { app } = await install([ { id: "announcements", ui: artifactSource("announcements", "1.4.2"), version: "1.4.2" }, ]); const url = await publishedModuleUrl(app, "announcements", "1.4.2"); await expect( loadModuleUi({ container: containerSink() as unknown as LoaderContainer, context: hostContext(), source: { moduleId: "announcements", url, version: "9.9.9" }, }), ).rejects.toMatchObject({ code: "MODULE_UI_IDENTITY_MISMATCH" }); }); });