Implement checked workspace integration contracts (WI-01)

This commit is contained in:
Timothy J. Aveni
2026-09-21 11:53:38 -07:00
parent 3c0378358f
commit 884b12f759
22 changed files with 4349 additions and 3089 deletions
+56
View File
@@ -0,0 +1,56 @@
import type { WorkspaceRevision } from "./types.js";
import type { CapabilityValidationIssue } from "./validation.js";
/** Presentation metadata has no graph authority and cannot name arbitrary module URLs. */
export function validatePresentations(workspace: WorkspaceRevision): CapabilityValidationIssue[] {
const issues: CapabilityValidationIssue[] = [];
const fail = (path: string, message: string) => issues.push({ code: "invalid-presentation", path, message });
const interfaces = new Map(workspace.interfaceImports.map((entry) => [entry.revisionId, entry]));
const packages = new Map(workspace.packageImports.map((entry) => [entry.revisionId, entry]));
const text = (value: string, path: string) => {
if (!value.trim()) fail(path, "Presentation identities and names must be nonempty");
};
const subject = (id: string, path: string) => {
const contract = interfaces.get(id as WorkspaceRevision["interfaceImports"][number]["revisionId"]);
if (!contract || contract.template) fail(path, `Expected an imported closed subject interface: ${id}`);
};
for (const [index, pkg] of workspace.packageImports.entries()) {
const ids = new Set(pkg.exports.map((entry) => entry.id as string));
const names = new Set([
...pkg.exports.map((entry) => entry.displayName),
...(pkg.queries ?? []).map((entry) => entry.displayName),
...(pkg.queryTemplates ?? []).map((entry) => entry.declaration.displayName),
...(pkg.genericExports ?? []).map((entry) => entry.displayName),
]);
for (const [componentIndex, component] of (pkg.components ?? []).entries()) {
const path = `packageImports[${index}].components[${componentIndex}]`;
text(component.id, `${path}.id`);
text(component.displayName, `${path}.displayName`);
if (ids.has(component.id) || names.has(component.displayName))
fail(path, "Duplicate component identity or package member name");
ids.add(component.id);
names.add(component.displayName);
subject(component.subject, `${path}.subject`);
if (component.runtime !== "react") fail(`${path}.runtime`, "Unsupported component runtime");
}
}
const ids = new Set<string>(),
names = new Set<string>();
for (const [index, presentation] of (workspace.presentations ?? []).entries()) {
const path = `presentations[${index}]`;
text(presentation.id, `${path}.id`);
text(presentation.displayName, `${path}.displayName`);
if (ids.has(presentation.id) || names.has(presentation.displayName))
fail(path, "Duplicate presentation identity or name");
ids.add(presentation.id);
names.add(presentation.displayName);
subject(presentation.subject, `${path}.subject`);
const component = packages
.get(presentation.packageRevisionId)
?.components?.find((entry) => entry.id === presentation.componentId);
if (!component) fail(`${path}.componentId`, "Presentation names no installed package component");
else if (component.subject !== presentation.subject)
fail(`${path}.subject`, `Component requires ${component.subject}, not ${presentation.subject}`);
}
return issues;
}