feat: add display kiosk host
This commit is contained in:
@@ -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",
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user