Support checked keyed relationships across portable operations and queries

This commit is contained in:
Timothy J. Aveni
2026-09-25 12:02:05 -07:00
parent e0e8563224
commit 1bc3c69de3
23 changed files with 1203 additions and 949 deletions
+1
View File
@@ -32,6 +32,7 @@ export type GenericDependencyPort = Omit<DependencyPort, "requirement"> & {
kind: "edge";
target: ObjectTypeExpression;
cardinality: Extract<DependencyPort["requirement"], { kind: "edge" }>["cardinality"];
keyType?: "string" | "boolean" | "int64";
primitives: Extract<DependencyPort["requirement"], { kind: "edge" }>["primitives"];
}
| {
+7
View File
@@ -33,6 +33,13 @@ export function validateInitialization(workspace: WorkspaceRevision): Capability
fail(ownerKey(owner), `Initialization input ${field.input} must name an edge projection on the newborn atom`);
return;
}
if (target.keyType) {
fail(
ownerKey(owner),
"Initialize a keyed relationship through its keyed projection; inverse seeds cannot supply a map key",
);
return;
}
if (endpoint.cardinality === "many" || endpoint.cardinality === "many-unique") {
const fields: Record<string, ValueType> = {
key: { kind: "scalar", name: "string" },
+1
View File
@@ -250,6 +250,7 @@ export type DependencyPortRequirement =
kind: "edge";
target: EdgeEndpointConstraint;
cardinality: EdgeCardinality;
keyType?: "string" | "boolean" | "int64";
primitives: EdgePrimitive[];
}
| {
+30 -3
View File
@@ -439,7 +439,19 @@ export const edgePrimitiveSignature = (
case "disconnect":
return {
mode: "call",
inputType: targetType,
inputType:
primitive === "connect" && projection.endpoint.keyType
? {
kind: "record",
fields: {
mapKey: {
kind: "scalar",
name: projection.endpoint.keyType === "boolean" ? "bool" : projection.endpoint.keyType,
},
target: targetType,
},
}
: targetType,
outputType: valueType.unit,
};
case "watch-start":
@@ -1398,6 +1410,13 @@ const validateBoundDependencies = (
);
break;
}
if (projection.target.keyType && requirement.primitives.includes("connect"))
issue(
issues,
"invalid-dependency-binding",
path,
"Connect through the keyed projection so the caller supplies a map key",
);
const traversal = edgeBinding.via
? validateTraversal(
issues,
@@ -1434,13 +1453,14 @@ const validateBoundDependencies = (
requirement.target,
params.indexes.conformances,
) ||
requirement.cardinality !== projection.endpoint.cardinality
requirement.cardinality !== projection.endpoint.cardinality ||
requirement.keyType !== projection.endpoint.keyType
) {
issue(
issues,
"invalid-dependency-binding",
`${path}.binding`,
`Edge port target or cardinality does not match projection ${edgeBinding.projectionId}`,
`Edge port target, cardinality or key type does not match projection ${edgeBinding.projectionId}`,
);
}
break;
@@ -1822,6 +1842,13 @@ const validateConformances = (
continue;
}
const projection = edgeProjection(attachment.attachment, binding.projectionId);
if (projection?.target.keyType && binding.primitive === "connect")
issue(
issues,
"invalid-edge-binding",
bindingPath,
"Connect through the keyed projection so the caller supplies a map key",
);
// An owned endpoint may explicitly export read-only traversal, including
// a native inverse relationship view. This never exports mutation rights.
if (!(binding.primitive === "resolve" && projection?.endpoint.publicTraversal)) {