feat: add display kiosk host
CI / verify (pull_request) Successful in 38s
CI / infrastructure-smoke (pull_request) Successful in 20s
CI / migration-smoke (pull_request) Successful in 59s

This commit is contained in:
ekklesia
2026-08-23 20:50:56 -05:00
parent be5ed7853d
commit 2e0fe059c3
80 changed files with 4868 additions and 397 deletions
+136 -1
View File
@@ -34,7 +34,7 @@ async function createModulesDirectory(): Promise<string> {
function manifestFor(id: string): Record<string, unknown> {
return {
manifestVersion: 5,
manifestVersion: 6,
id,
name: `${id} module`,
version: "0.1.0",
@@ -748,7 +748,9 @@ describe("module discovery", () => {
});
expect(discovered?.entrypoints.ui).toBeUndefined();
expect(discovered?.entrypoints.display).toBeUndefined();
expect(Object.hasOwn(discovered?.entrypoints ?? {}, "ui")).toBe(false);
expect(Object.hasOwn(discovered?.entrypoints ?? {}, "display")).toBe(false);
});
it("rejects a source TypeScript UI entrypoint where compiled output is required", async () => {
@@ -821,4 +823,137 @@ describe("module discovery", () => {
expect(discovered[0]?.entrypoints.ui).toContain("dist/ui/index.js");
});
});
describe("module-owned display UI entrypoint", () => {
function manifestWithDisplay(
id: string,
artifact: string,
ui?: string,
): Record<string, unknown> {
const manifest = manifestFor(id);
manifest.entrypoints = {
server: "./dist/server/index.js",
...(ui === undefined ? {} : { ui }),
display: { artifact, permission: `${id}.records.read` },
};
return manifest;
}
it("resolves a display-only artifact and preserves its module-owned permission", async () => {
const modulesDirectory = await createModulesDirectory();
await createModule(modulesDirectory, "announcements", {
manifest: manifestWithDisplay("announcements", "./dist/display/index.mjs"),
extraFiles: {
"dist/display/index.mjs": "throw new Error('discovery must stay inert');",
},
});
const [discovered] = await discoverModules({
moduleSdkVersion: "0.1.0",
modulesDirectory,
platformVersion: "0.1.0",
});
expect(discovered?.entrypoints.ui).toBeUndefined();
expect(discovered?.entrypoints.display).toEqual({
artifactPath: path.join(discovered?.directory ?? "", "dist/display/index.mjs"),
permission: "announcements.records.read",
});
expect(Object.isFrozen(discovered?.entrypoints.display)).toBe(true);
});
it("resolves independent primary and display artifacts without executing either", async () => {
const modulesDirectory = await createModulesDirectory();
await createModule(modulesDirectory, "signage", {
manifest: manifestWithDisplay("signage", "./dist/display/index.js", "./dist/ui/index.js"),
extraFiles: {
"dist/display/index.js": "throw new Error('display must stay inert');",
"dist/ui/index.js": "throw new Error('primary UI must stay inert');",
},
});
const [discovered] = await discoverModules({
moduleSdkVersion: "0.1.0",
modulesDirectory,
platformVersion: "0.1.0",
});
expect(discovered?.entrypoints.ui).toContain("dist/ui/index.js");
expect(discovered?.entrypoints.display?.artifactPath).toContain("dist/display/index.js");
});
it("rejects source TypeScript and reports the nested display artifact path", async () => {
const modulesDirectory = await createModulesDirectory();
await createModule(modulesDirectory, "signage", {
manifest: manifestWithDisplay("signage", "./src/display/index.tsx"),
extraFiles: { "src/display/index.tsx": "export default {};" },
});
const error = await discoveryError(modulesDirectory);
expect(issueCodes(error)).toContain("MODULE_ENTRYPOINT_INVALID");
expect(error.issues).toContainEqual(
expect.objectContaining({ path: "entrypoints.display.artifact" }),
);
});
it("rejects a declared display whose compiled artifact is missing", async () => {
const modulesDirectory = await createModulesDirectory();
await createModule(modulesDirectory, "signage", {
manifest: manifestWithDisplay("signage", "./dist/display/index.js"),
});
const error = await discoveryError(modulesDirectory);
expect(issueCodes(error)).toContain("MODULE_ENTRYPOINT_MISSING");
expect(error.issues).toContainEqual(
expect.objectContaining({ path: "entrypoints.display.artifact" }),
);
});
it("rejects distinct configured UI paths that resolve through links to one artifact", async () => {
const modulesDirectory = await createModulesDirectory();
await createModule(modulesDirectory, "signage", {
manifest: manifestWithDisplay("signage", "./dist/display/index.js", "./dist/ui/index.js"),
extraFiles: { "dist/shared.js": "export default {};" },
});
const moduleDirectory = path.join(modulesDirectory, "signage");
await Promise.all([
mkdir(path.join(moduleDirectory, "dist/ui"), { recursive: true }),
mkdir(path.join(moduleDirectory, "dist/display"), { recursive: true }),
]);
await Promise.all([
symlink("../shared.js", path.join(moduleDirectory, "dist/ui/index.js")),
symlink("../shared.js", path.join(moduleDirectory, "dist/display/index.js")),
]);
const error = await discoveryError(modulesDirectory);
expect(error.issues).toContainEqual(
expect.objectContaining({
code: "MODULE_ENTRYPOINT_INVALID",
path: "entrypoints.display.artifact",
}),
);
});
it("rejects a display artifact that resolves through a link to the server artifact", async () => {
const modulesDirectory = await createModulesDirectory();
await createModule(modulesDirectory, "signage", {
manifest: manifestWithDisplay("signage", "./dist/display/index.js"),
});
const moduleDirectory = path.join(modulesDirectory, "signage");
await mkdir(path.join(moduleDirectory, "dist/display"), { recursive: true });
await symlink("../server/index.js", path.join(moduleDirectory, "dist/display/index.js"));
const error = await discoveryError(modulesDirectory);
expect(error.issues).toContainEqual(
expect.objectContaining({
code: "MODULE_ENTRYPOINT_INVALID",
path: "entrypoints.display.artifact",
}),
);
});
});
});