Build reusable React component hosting and typed fragment composition

This commit is contained in:
Timothy J. Aveni
2026-09-22 16:46:33 -07:00
parent a8869e4545
commit b8571d054b
4 changed files with 65 additions and 4 deletions
+35
View File
@@ -29,6 +29,13 @@ export interface ClientContractCatalog {
readonly queries: readonly PublicQueryContract[];
readonly components: readonly InstalledComponent[];
readonly presentations: readonly { id: string; subject: string; packageRevision: string; component: string }[];
readonly moduleBindings: readonly {
atom: string;
interface: string;
member: string;
packageRevision: string;
id: string;
}[];
}
/** Explicit projection prevents accidental spreading of filesystem roots or private bindings. */
export function clientContractCatalog(
@@ -68,6 +75,17 @@ export function clientContractCatalog(
(pkg.checkedQueries ?? []).map((q) => publicQueryContract(pkg, q.declaration.id)),
),
components: installation.components.filter((c) => requestedPackages.includes(c.packageRevision)),
moduleBindings: plan.definition.workspace.conformances.flatMap((c) =>
(c.moduleBindings ?? [])
.filter((b) => requestedPackages.includes(b.packageRevisionId))
.map((b) => ({
atom: c.atomId,
interface: c.interfaceRevisionId,
member: b.memberId,
packageRevision: b.packageRevisionId,
id: b.moduleId,
})),
),
presentations: (plan.definition.presentations ?? [])
.filter((p) => requestedPackages.includes(p.packageRevisionId))
.map((p) => ({ id: p.id, subject: p.subject, packageRevision: p.packageRevisionId, component: p.componentId })),
@@ -99,6 +117,12 @@ export interface ExecutionInstallation {
}
/** Safe to expose in a presentation catalog: no source paths, state slots, or host configuration. */
export interface InstalledComponent {
readonly role: {
readonly interface: string;
readonly member: string;
readonly subject: string;
readonly contract: string;
};
readonly packageRevision: string;
readonly id: string;
readonly subject: string;
@@ -176,6 +200,17 @@ function assembleExecutionInstallation(
routes[url] ??= { path: path.join(root, file.path), mediaType: file.mediaType };
}
components.push({
role: (() => {
const module = plan.definition.packages
.find((p) => p.revision === revision)!
.modules!.find((m) => m.id === component.id)!;
return {
interface: module.interfaceRevisionId,
member: module.memberId,
subject: component.subject,
contract: module.contract,
};
})(),
packageRevision: revision,
id: component.id,
subject: component.subject,