Introduce persisted Web Studio component capabilities

This commit is contained in:
2026-09-04 21:03:09 -07:00
parent f4987093f6
commit fd039f095b
17 changed files with 2459 additions and 1609 deletions
+97 -2
View File
@@ -22,6 +22,7 @@ import {
type EdgeEndpoint,
type EdgeEndpointConstraint,
type EdgePrimitive,
type EdgeTraversal,
type InterfaceMember,
type InterfaceOperation,
type InterfaceOperationMode,
@@ -56,6 +57,7 @@ import { QuixosCapabilityParser,
type ValueMemberContext,
type ValueTypeContext,
type RelationshipMemberContext,
type RelationshipMaterializationDeclContext,
type WorkspaceDeclContext,
} from "./generated/QuixosCapabilityParser.js";
@@ -91,6 +93,7 @@ interface InterfaceSymbol {
members: Map<
string,
{
memberId: InterfaceMember["id"];
operations: Map<string, InterfaceOperation["id"]>;
}
>;
@@ -556,6 +559,7 @@ const lowerInterface = (
symbol.members,
member.displayName,
{
memberId: member.id,
operations: new Map(
member.operations.map((operation) => [operation.displayName, operation.id]),
),
@@ -1003,6 +1007,34 @@ const lowerBoundDependencies = (
if (!portId) {
return [];
}
const traversal = (
edgeName: string,
projectionName: string,
): EdgeTraversal | undefined => {
const attachment = requireSymbol(
state,
state.attachments,
edgeName,
entry,
"attachment",
);
if (!attachment || attachment.attachment.kind !== "edge") {
if (attachment) {
loweringIssue(state, entry, "wrong-attachment-kind", `${edgeName} is state, not an edge`);
}
return undefined;
}
const projectionId = requireSymbol(
state,
attachment.projections,
projectionName,
entry,
`projection on ${edgeName}`,
);
return projectionId
? { edgeTypeId: attachment.attachment.id, projectionId }
: undefined;
};
if (entry.STATE()) {
const attachmentName = identifier(entry.identifier(1));
const attachment = requireSymbol(
@@ -1023,9 +1055,16 @@ const lowerBoundDependencies = (
}
return [];
}
return [{ portId, binding: { kind: "state", slotId: attachment.attachment.id } }];
const via = entry.VIA()
? traversal(identifier(entry.identifier(2)), identifier(entry.identifier(3)))
: undefined;
if (entry.VIA() && !via) return [];
return [{
portId,
binding: { kind: "state", slotId: attachment.attachment.id, ...(via ? { via } : {}) },
}];
}
if (entry.EDGE()) {
if (entry.EDGE(0)) {
const attachmentName = identifier(entry.identifier(1));
const projectionName = identifier(entry.identifier(2));
const attachment = requireSymbol(
@@ -1053,6 +1092,10 @@ const lowerBoundDependencies = (
entry,
`projection on ${attachmentName}`,
);
const via = entry.VIA()
? traversal(identifier(entry.identifier(3)), identifier(entry.identifier(4)))
: undefined;
if (entry.VIA() && !via) return [];
return projectionId
? [
{
@@ -1061,6 +1104,7 @@ const lowerBoundDependencies = (
kind: "edge",
edgeTypeId: attachment.attachment.id,
projectionId,
...(via ? { via } : {}),
},
},
]
@@ -1095,6 +1139,50 @@ const lowerBoundDependencies = (
});
};
const lowerRelationshipMaterialization = (
state: LoweringState,
interfaceName: string,
context: RelationshipMaterializationDeclContext,
): WorkspaceRevision["conformances"][number]["relationshipMaterializations"][number] | undefined => {
const memberName = identifier(context.identifier(0));
const member = requireSymbol(
state,
state.interfaces.get(interfaceName)?.members ?? new Map(),
memberName,
context,
`member on ${interfaceName}`,
);
const constructorAtom = requireSymbol(
state,
state.atoms,
identifier(context.identifier(1)),
context,
"constructor atom",
);
const edgeName = identifier(context.identifier(2));
const edge = requireSymbol(state, state.attachments, edgeName, context, "attachment");
if (edge && edge.attachment.kind !== "edge") {
loweringIssue(state, context, "wrong-attachment-kind", `${edgeName} is state, not an edge`);
}
const projectionId = edge?.attachment.kind === "edge"
? requireSymbol(
state,
edge.projections,
identifier(context.identifier(3)),
context,
`projection on ${edgeName}`,
)
: undefined;
return member && constructorAtom && edge?.attachment.kind === "edge" && projectionId
? {
memberId: member.memberId,
constructorAtomId: constructorAtom,
edgeTypeId: edge.attachment.id,
constructedProjectionId: projectionId,
}
: undefined;
};
const lowerConformance = (
state: LoweringState,
context: ConformanceDeclContext,
@@ -1246,11 +1334,18 @@ const lowerConformance = (
]
: [];
});
const relationshipMaterializations = context.conformanceItem().flatMap((item) => {
const materialization = item.relationshipMaterializationDecl();
if (!materialization) return [];
const lowered = lowerRelationshipMaterialization(state, interfaceName, materialization);
return lowered ? [lowered] : [];
});
return {
atomId,
interfaceRevisionId: interfaceSymbol.revisionId,
privateAttachments,
operationBindings,
relationshipMaterializations,
};
};