import { describe, expect, it } from "vitest"; import { isModuleConfigurationBlockedReason, isVersionCompatible, MODULE_BLOCKED_REASONS, MODULE_CONFIGURATION_BLOCKED_REASONS, MODULE_DEPENDENCY_KINDS, MODULE_MANIFEST_VERSION, MODULE_RUNTIME_STATES, MODULE_STORAGE_BLOCKED_REASON, packageRelativePathSchema, parseModuleManifest, parsePermissionId, safeParseModuleManifest, } from "../src/index.js"; function validManifest(): Record { return { manifestVersion: 8, id: "calendar", name: "Calendar", version: "0.1.0", compatibility: { platform: "^0.1.0", moduleSdk: "^0.1.0", }, capabilities: [{ id: "calendar.events", version: "2.3.0" }], permissions: [ { id: "calendar.events.read", baseline: "restricted", description: "Read calendar events.", }, ], storage: { buckets: [{ id: "assets", description: "Calendar-owned event assets." }], }, entrypoints: { server: "./dist/server/index.js", }, }; } describe("module manifest contract", () => { it("accepts both manifest-v8 database modes", () => { expect(parseModuleManifest(validManifest()).database).toBeUndefined(); expect(parseModuleManifest({ ...validManifest(), database: { abi: 1 } }).database).toEqual({ abi: 1, }); }); it("rejects manifest v7, unsupported ABIs, malformed declarations, and unknown database fields", () => { expect(safeParseModuleManifest({ ...validManifest(), manifestVersion: 7 }).success).toBe(false); expect(safeParseModuleManifest({ ...validManifest(), database: { abi: 2 } }).success).toBe( false, ); expect(safeParseModuleManifest({ ...validManifest(), database: "abi-v1" }).success).toBe(false); expect( safeParseModuleManifest({ ...validManifest(), database: { abi: 1, driver: "drizzle" } }) .success, ).toBe(false); }); it("normalizes a valid manifest with safe defaults", () => { const manifest = parseModuleManifest(validManifest()); expect(manifest.id).toBe("calendar"); expect(manifest.dependencies).toEqual({ capabilities: { optional: {}, required: {} }, modules: { optional: {}, required: {} }, }); expect(manifest.storage.buckets).toEqual([ { id: "assets", description: "Calendar-owned event assets." }, ]); }); it("creates isolated default collections for every parsed manifest", () => { const input = validManifest(); delete input.storage; delete input.permissions; const first = parseModuleManifest(input); const second = parseModuleManifest(input); first.dependencies.modules.required.people = "^0.1.0"; first.dependencies.capabilities.required["people.records"] = "^1.0.0"; first.storage.buckets.push({ id: "exports" }); first.permissions.push({ id: parsePermissionId("calendar.events.write"), baseline: "restricted", description: "Write calendar events.", }); expect(second.dependencies.modules.required).toEqual({}); expect(second.dependencies.capabilities.required).toEqual({}); expect(second.storage.buckets).toEqual([]); expect(second.permissions).toEqual([]); }); it("rejects unknown and presentation-oriented manifest properties", () => { const manifest = { ...validManifest(), renderers: ["calendar-month"] }; const result = safeParseModuleManifest(manifest); expect(result.success).toBe(false); }); it("requires module-owned capability and permission namespaces", () => { const manifest = { ...validManifest(), capabilities: [{ id: "media.items", version: "1.0.0" }], permissions: [ { id: "media.items.read", baseline: "restricted", description: "Read media items." }, ], }; const result = safeParseModuleManifest(manifest); expect(result.success).toBe(false); if (!result.success) { expect(result.error.issues.map((issue) => issue.path.join("."))).toEqual( expect.arrayContaining(["capabilities.0.id", "permissions.0.id"]), ); } }); it("rejects duplicate declarations and dependency overlap", () => { const manifest = { ...validManifest(), dependencies: { modules: { required: { people: "^1.0.0" }, optional: { people: "^1.0.0" }, }, capabilities: { required: { "people.records": "^1.0.0" }, optional: { "people.records": "^1.0.0" }, }, }, capabilities: [ { id: "calendar.events", version: "2.3.0" }, { id: "calendar.events", version: "2.4.0" }, ], storage: { buckets: [{ id: "assets" }, { id: "assets" }] }, }; const result = safeParseModuleManifest(manifest); expect(result.success).toBe(false); if (!result.success) { expect(result.error.issues).toHaveLength(4); } }); it("rejects a duplicate permission id at its exact issue path", () => { const manifest = { ...validManifest(), permissions: [ { id: "calendar.events.read", baseline: "restricted", description: "Read calendar events.", }, { id: "calendar.events.read", baseline: "authenticated", description: "Read calendar events, again.", }, ], }; const result = safeParseModuleManifest(manifest); expect(result.success).toBe(false); if (!result.success) { expect(result.error.issues.map((issue) => issue.path.join("."))).toContain( "permissions.1.id", ); } }); it("rejects module and capability self-dependencies", () => { const manifest = { ...validManifest(), dependencies: { modules: { required: { calendar: "^0.1.0" } }, capabilities: { required: { "calendar.events": "^2.0.0" } }, }, }; expect(safeParseModuleManifest(manifest).success).toBe(false); }); it("versions capabilities independently from the module release", () => { const manifest = parseModuleManifest(validManifest()); expect(manifest.version).toBe("0.1.0"); expect(manifest.capabilities).toEqual([{ id: "calendar.events", version: "2.3.0" }]); }); it("requires exact capability versions and dependency ranges", () => { const manifest = { ...validManifest(), capabilities: [{ id: "calendar.events", version: "^2.0.0" }], dependencies: { capabilities: { required: { "people.records": "not-a-range" } }, }, }; const result = safeParseModuleManifest(manifest); expect(result.success).toBe(false); if (!result.success) { expect(result.error.issues.map((issue) => issue.path.join("."))).toEqual( expect.arrayContaining([ "capabilities.0.version", "dependencies.capabilities.required.people.records", ]), ); } }); it("rejects obsolete manifest versions instead of guessing a migration", () => { expect(safeParseModuleManifest({ ...validManifest(), manifestVersion: 1 }).success).toBe(false); }); it("rejects a version 2 manifest rather than silently interpreting it as a later version", () => { // manifestVersion 2's own permissions shape (a bare string array) is also what every later // version must reject — this manifest is invalid twice over, deliberately, since a real // version 2 manifest is exactly this shape. const version2Manifest = { ...validManifest(), manifestVersion: 2, permissions: ["calendar.events.read"], }; expect(safeParseModuleManifest(version2Manifest).success).toBe(false); }); it("rejects a version 3 manifest rather than silently interpreting it as a later version", () => { const version3Manifest = { ...validManifest(), manifestVersion: 3 }; expect(safeParseModuleManifest(version3Manifest).success).toBe(false); }); it("rejects a version 4 manifest rather than silently interpreting it as version 5", () => { // A manifest declaring no configuration is otherwise byte-identical between version 4 and // version 5: only the version literal separates them, and it is not reinterpreted (ADR 0029). const version4Manifest = { ...validManifest(), manifestVersion: 4 }; expect(safeParseModuleManifest(version4Manifest).success).toBe(false); }); it("rejects a version 5 manifest rather than silently interpreting it as a later version", () => { const version5Manifest = { ...validManifest(), manifestVersion: 5 }; expect(safeParseModuleManifest(version5Manifest).success).toBe(false); }); it("rejects a version 6 manifest rather than silently interpreting it as version 8", () => { const version6Manifest = { ...validManifest(), manifestVersion: 6 }; expect(safeParseModuleManifest(version6Manifest).success).toBe(false); }); it("accepts exactly one manifest version, so no document can be read as two versions", () => { expect(MODULE_MANIFEST_VERSION).toBe(8); for (const manifestVersion of [1, 2, 3, 4, 5, 6, 7, 9, "8", 8.5, null]) { expect(safeParseModuleManifest({ ...validManifest(), manifestVersion }).success).toBe(false); } expect( safeParseModuleManifest({ ...validManifest(), manifestVersion: MODULE_MANIFEST_VERSION }) .success, ).toBe(true); }); it("defaults contributions to an empty list for a module that contributes nothing", () => { const parsed = safeParseModuleManifest(validManifest()); expect(parsed.success).toBe(true); expect(parsed.success && parsed.data.contributions).toEqual([]); }); it("accepts a well-formed browser contribution", () => { const parsed = safeParseModuleManifest({ ...validManifest(), contributions: [ { id: "calendar.signage-slide", version: "1.0.0", capability: "signage.slide-renderer", capabilityRange: "^1.0.0", artifact: "./dist/contributions/signage-slide/index.js", description: "Renders an upcoming-events slide.", }, ], }); expect(parsed.success).toBe(true); expect(parsed.success && parsed.data.contributions[0]?.capability).toBe( "signage.slide-renderer", ); }); it.each<[string, Record]>([ ["an id outside this module's namespace", { id: "signage.slide" }], ["an id with no local segment", { id: "calendar" }], ["an id with more than one local segment", { id: "calendar.signage.slide" }], ["an unknown property", { slot: "primary" }], ["a non-semver version", { version: "1" }], ["an invalid capability range", { capabilityRange: "not a range" }], ["an artifact escaping the package", { artifact: "../elsewhere/index.js" }], ["an absolute artifact path", { artifact: "/etc/passwd" }], ])("rejects a contribution with %s", (_label, override) => { const contribution = { id: "calendar.signage-slide", version: "1.0.0", capability: "signage.slide-renderer", capabilityRange: "^1.0.0", artifact: "./dist/contributions/signage-slide/index.js", ...override, }; expect( safeParseModuleManifest({ ...validManifest(), contributions: [contribution] }).success, ).toBe(false); }); it("rejects a contribution targeting a capability this module already provides", () => { // A module contributing to itself needs no platform mechanism, and would make the compatibility // check meaningless: it would always be comparing a module against itself. expect( safeParseModuleManifest({ ...validManifest(), contributions: [ { id: "calendar.self", version: "1.0.0", capability: "calendar.events", capabilityRange: "^2.0.0", artifact: "./dist/contributions/self/index.js", }, ], }).success, ).toBe(false); }); it("rejects two contributions sharing an id or an artifact path", () => { const base = { version: "1.0.0", capability: "signage.slide-renderer", capabilityRange: "^1.0.0", }; expect( safeParseModuleManifest({ ...validManifest(), contributions: [ { ...base, id: "calendar.a", artifact: "./dist/contributions/a/index.js" }, { ...base, id: "calendar.a", artifact: "./dist/contributions/b/index.js" }, ], }).success, ).toBe(false); expect( safeParseModuleManifest({ ...validManifest(), contributions: [ { ...base, id: "calendar.a", artifact: "./dist/contributions/a/index.js" }, { ...base, id: "calendar.b", artifact: "./dist/contributions/a/index.js" }, ], }).success, ).toBe(false); }); it("rejects a contribution artifact that collides with a declared entrypoint", () => { const manifest = validManifest(); const entrypoints = manifest.entrypoints as Record; expect( safeParseModuleManifest({ ...manifest, contributions: [ { id: "calendar.signage-slide", version: "1.0.0", capability: "signage.slide-renderer", capabilityRange: "^1.0.0", artifact: entrypoints.server, }, ], }).success, ).toBe(false); }); it("rejects a bare permission-id string array instead of complete declarations", () => { const manifest = { ...validManifest(), permissions: ["calendar.events.read"] }; expect(safeParseModuleManifest(manifest).success).toBe(false); }); it("defaults to an empty permission list when omitted", () => { const input = validManifest(); delete input.permissions; const manifest = parseModuleManifest(input); expect(manifest.permissions).toEqual([]); }); it.each([ "public", "authenticated", "restricted", ] as const)("accepts a %s permission declaration and preserves its exact baseline", (baseline) => { const manifest = parseModuleManifest({ ...validManifest(), permissions: [{ id: "calendar.events.read", baseline, description: "Read events." }], }); expect(manifest.permissions).toEqual([ { id: "calendar.events.read", baseline, description: "Read events." }, ]); }); it("rejects a permission declaration with a missing baseline", () => { const manifest = { ...validManifest(), permissions: [{ id: "calendar.events.read", description: "Read events." }], }; expect(safeParseModuleManifest(manifest).success).toBe(false); }); it("rejects a permission declaration with an invalid baseline", () => { const manifest = { ...validManifest(), permissions: [ { id: "calendar.events.read", baseline: "superuser", description: "Read events." }, ], }; expect(safeParseModuleManifest(manifest).success).toBe(false); }); it("rejects a permission declaration with a missing or blank description", () => { const missingDescription = { ...validManifest(), permissions: [{ id: "calendar.events.read", baseline: "restricted" }], }; const blankDescription = { ...validManifest(), permissions: [{ id: "calendar.events.read", baseline: "restricted", description: " " }], }; expect(safeParseModuleManifest(missingDescription).success).toBe(false); expect(safeParseModuleManifest(blankDescription).success).toBe(false); }); it("rejects a permission declaration with an oversized description", () => { const manifest = { ...validManifest(), permissions: [ { id: "calendar.events.read", baseline: "restricted", description: "x".repeat(241) }, ], }; expect(safeParseModuleManifest(manifest).success).toBe(false); }); it("rejects a permission declaration with an unknown property", () => { const manifest = { ...validManifest(), permissions: [ { id: "calendar.events.read", baseline: "restricted", description: "Read events.", scope: "calendar-event:123", }, ], }; expect(safeParseModuleManifest(manifest).success).toBe(false); }); it("rejects a malformed permission id", () => { const manifest = { ...validManifest(), permissions: [ { id: "Calendar.Events.Read", baseline: "restricted", description: "Read events." }, ], }; expect(safeParseModuleManifest(manifest).success).toBe(false); }); it.each([ "../outside.ts", "/absolute.ts", "file:///server.ts", "./src/../server.ts", "./src\\server.ts", "./src/server.ts?raw", ])("rejects unsafe package entrypoint %s", (entrypoint) => { expect(packageRelativePathSchema.safeParse(entrypoint).success).toBe(false); }); it("allows package directories whose names merely begin with two dots", () => { expect(packageRelativePathSchema.safeParse("./..data/server.ts").success).toBe(true); }); it("uses full semantic-version compatibility ranges", () => { expect(isVersionCompatible("0.1.5", "^0.1.0")).toBe(true); expect(isVersionCompatible("0.2.0", "^0.1.0")).toBe(false); expect(isVersionCompatible("not-a-version", "*")).toBe(false); }); it("publishes exactly the operational lifecycle vocabulary the platform implements", () => { // Every name here is produced by real code and observable through the lifecycle read model // (ADR 0028). The nine-stage `MODULE_LIFECYCLE_STAGES` vocabulary this replaced named states // nothing ever entered, including a `failed` state that startup atomicity makes unobservable — // asserting the exact sets is what keeps this contract from drifting back into aspiration. expect(MODULE_RUNTIME_STATES).toEqual(["running", "disabled", "blocked"]); expect(MODULE_RUNTIME_STATES).not.toContain("failed"); expect(MODULE_BLOCKED_REASONS).toEqual([ "required_dependency_disabled", "required_dependency_blocked", "configuration_missing", "configuration_invalid", "secret_missing", "storage_unavailable", ]); // Storage adds exactly one reason, and only one: a *binding* that cannot be supplied. A // configured provider that is temporarily unreachable is deliberately not a lifecycle state // (ADR 0030), so there is no `storage_unreachable` here and there must never be. expect(MODULE_BLOCKED_REASONS).not.toContain("storage_unreachable"); expect(MODULE_STORAGE_BLOCKED_REASON).toBe("storage_unavailable"); // The three configuration reasons are a subset, and each corresponds to a real implemented // check with a distinct operator fix (ADR 0029) — the only justification for a separate code. expect(MODULE_CONFIGURATION_BLOCKED_REASONS).toEqual([ "configuration_missing", "configuration_invalid", "secret_missing", ]); for (const reason of MODULE_CONFIGURATION_BLOCKED_REASONS) { expect(isModuleConfigurationBlockedReason(reason)).toBe(true); } expect(isModuleConfigurationBlockedReason("required_dependency_disabled")).toBe(false); expect(MODULE_DEPENDENCY_KINDS).toEqual(["module", "capability"]); }); describe("module-owned UI entrypoint (ADR 0021)", () => { it("accepts a backend-only module that declares no UI at all", () => { const manifest = parseModuleManifest(validManifest()); expect(manifest.entrypoints.ui).toBeUndefined(); expect(manifest.entrypoints.display).toBeUndefined(); expect(manifest.entrypoints.server).toBe("./dist/server/index.js"); }); it("accepts a declared compiled UI entrypoint alongside the server entrypoint", () => { const manifest = parseModuleManifest({ ...validManifest(), entrypoints: { server: "./dist/server/index.js", ui: "./dist/ui/index.js" }, }); expect(manifest.entrypoints.ui).toBe("./dist/ui/index.js"); // Declaring UI never alters the backend contract: a product module is one module, not two. expect(manifest.entrypoints.server).toBe("./dist/server/index.js"); }); it("accepts an independently declared display UI protected by a module-owned permission", () => { const manifest = parseModuleManifest({ ...validManifest(), entrypoints: { server: "./dist/server/index.js", display: { artifact: "./dist/display/index.js", permission: "calendar.events.read", }, }, }); expect(manifest.entrypoints.ui).toBeUndefined(); expect(manifest.entrypoints.display).toEqual({ artifact: "./dist/display/index.js", permission: "calendar.events.read", }); }); it("rejects a display permission that this module did not declare", () => { const result = safeParseModuleManifest({ ...validManifest(), entrypoints: { server: "./dist/server/index.js", display: { artifact: "./dist/display/index.js", permission: "calendar.display.view", }, }, }); expect(result.success).toBe(false); if (!result.success) { expect(result.error.issues.map((issue) => issue.path.join("."))).toContain( "entrypoints.display.permission", ); } }); it("rejects a display permission whose baseline would admit an anonymous principal", () => { const result = safeParseModuleManifest({ ...validManifest(), permissions: [ { id: "calendar.display.view", baseline: "public", description: "View the calendar display.", }, ], entrypoints: { server: "./dist/server/index.js", display: { artifact: "./dist/display/index.js", permission: "calendar.display.view", }, }, }); expect(result.success).toBe(false); if (!result.success) { expect(result.error.issues.map((issue) => issue.path.join("."))).toContain( "entrypoints.display.permission", ); } }); for (const entrypointName of ["server", "schema", "migrations", "jobs", "ui"] as const) { it(`rejects a display artifact that is also configured as the ${entrypointName} entrypoint`, () => { const sharedPath = "./dist/shared.js"; const result = safeParseModuleManifest({ ...validManifest(), entrypoints: { server: entrypointName === "server" ? sharedPath : "./dist/server/index.js", ...(entrypointName === "server" ? {} : { [entrypointName]: sharedPath }), display: { artifact: sharedPath, permission: "calendar.events.read", }, }, }); expect(result.success).toBe(false); if (!result.success) { expect(result.error.issues.map((issue) => issue.path.join("."))).toContain( "entrypoints.display.artifact", ); } }); } it("rejects primary and display UI declarations that name the same artifact", () => { const result = safeParseModuleManifest({ ...validManifest(), entrypoints: { server: "./dist/server/index.js", ui: "./dist/ui/index.js", display: { artifact: "./dist/ui/index.js", permission: "calendar.events.read", }, }, }); expect(result.success).toBe(false); if (!result.success) { expect(result.error.issues.map((issue) => issue.path.join("."))).toContain( "entrypoints.display.artifact", ); } }); it.each([ "../outside/display.js", "/absolute/display.js", "./dist/../../display.js", "file:///dist/display.js", ".\\dist\\display.js", "./dist/display.js?raw", "", ])("rejects the unsafe display artifact path %s", (artifact) => { expect( safeParseModuleManifest({ ...validManifest(), entrypoints: { server: "./dist/server/index.js", display: { artifact, permission: "calendar.events.read" }, }, }).success, ).toBe(false); }); it("rejects unknown properties in the static display declaration", () => { expect( safeParseModuleManifest({ ...validManifest(), entrypoints: { server: "./dist/server/index.js", display: { artifact: "./dist/display/index.js", permission: "calendar.events.read", public: true, }, }, }).success, ).toBe(false); }); it.each([ ["../outside/ui.js", "escapes the package boundary"], ["/absolute/ui.js", "is an absolute path"], ["./dist/../../ui.js", "traverses out through a parent segment"], ["file:///dist/ui/index.js", "is a URL rather than a package-relative path"], ["./dist\\ui\\index.js", "uses backslash separators"], ["./dist/ui/index.js?raw", "carries a query string"], ["", "is empty"], ])("rejects a UI entrypoint that %s", (ui) => { const manifest = { ...validManifest(), entrypoints: { server: "./dist/server/index.js", ui }, }; expect(safeParseModuleManifest(manifest).success).toBe(false); }); it.each([ 42, null, true, ["./dist/ui/index.js"], { entry: "./dist/ui/index.js" }, ])("rejects a non-string UI entrypoint %s", (ui) => { const manifest = { ...validManifest(), entrypoints: { server: "./dist/server/index.js", ui }, }; expect(safeParseModuleManifest(manifest).success).toBe(false); }); it("still rejects unknown entrypoint keys, so UI is the only new one", () => { for (const unknownEntrypoint of ["dashboard", "surface", "client", "web", "renderers"]) { const manifest = { ...validManifest(), entrypoints: { server: "./dist/server/index.js", [unknownEntrypoint]: "./dist/ui/index.js", }, }; expect(safeParseModuleManifest(manifest).success).toBe(false); } }); it("declares no grouping, folder, parent, or menu field for a module to own", () => { // Dashboard organization is presentation configuration, never module architecture // (ADR 0021). The manifest must offer no place to encode a hierarchy. for (const presentationField of [ "group", "folder", "parent", "menu", "navigation", "icon", "order", ]) { const manifest = { ...validManifest(), [presentationField]: "media" }; expect(safeParseModuleManifest(manifest).success).toBe(false); } }); it("keeps the module dependency graph flat: UI adds no parent/child relationship", () => { const manifest = parseModuleManifest({ ...validManifest(), entrypoints: { server: "./dist/server/index.js", ui: "./dist/ui/index.js" }, }); // A module that ships UI still expresses relationships only through genuine module and // capability dependencies — the same two mechanisms a backend-only module uses. expect(Object.keys(manifest.dependencies.modules.required)).toEqual([]); expect(Object.keys(manifest.dependencies.modules.optional)).toEqual([]); expect(Object.keys(manifest)).not.toContain("children"); }); it("still supports a genuine module dependency for a module that ships UI", () => { const manifest = parseModuleManifest({ ...validManifest(), dependencies: { modules: { required: { people: "^0.1.0" } }, capabilities: { required: { "people.records": "^2.0.0" } }, }, entrypoints: { server: "./dist/server/index.js", ui: "./dist/ui/index.js" }, }); expect(manifest.dependencies.modules.required).toEqual({ people: "^0.1.0" }); expect(manifest.dependencies.capabilities.required).toEqual({ "people.records": "^2.0.0" }); }); }); });