Implement implicit object references and capability query foundations

This commit is contained in:
Timothy J. Aveni
2026-09-26 11:44:32 -07:00
parent 58a75da83f
commit 3728612e8c
13 changed files with 401 additions and 35 deletions
+88 -1
View File
@@ -182,11 +182,36 @@ test("ordinary selections preserve aliases, fragment conditions and wire identit
const node = checked.selection[0]!.selection[0]!.selection[0]!.selection[0]!;
assert.deepEqual(
node.selection.map((s) => s.key),
["plain", "label", "due"],
["plain", "label", "due", "_camino"],
);
assert.equal(node.selection[1]!.conditions.length, 1);
assert.ok(JSON.stringify(checked.selection.map(querySelectionToWire)).includes("label"));
});
test("object references are unconditional, canonical and idempotent through fragments", async () => {
const checked = await compile(
`query Upcoming($show: Boolean!) {root {items(first: 3) {entries {node {...Row @include(if: $show)}}}}}`,
`fragment Row on TaskFacts {label: title identity: _camino {alias: ref} _camino @include(if: $show) {ref}}`,
);
const root = checked.selection[0]!;
const node = root.selection.find((s) => s.name === "items")!.selection[0]!.selection[0]!;
for (const object of [root, node]) {
const metadata = object.selection.filter((s) => s.key === "_camino");
assert.equal(metadata.length, 1);
assert.deepEqual(metadata[0]!.conditions, []);
assert.deepEqual(
metadata[0]!.selection.filter((s) => s.key === "ref").map((s) => s.conditions),
[[]],
);
}
assert.equal(node.selection.find((s) => s.key === "identity")!.conditions.length, 1);
const recompiled = await compileQuery(
{ ...checked.declaration, fragments: [] },
[collection, facts],
async () => checked.document,
);
assert.deepEqual(recompiled.selection, checked.selection);
assert.deepEqual(recompiled.output, checked.output);
});
test("retired live syntax fails with migration guidance", async () => {
for (const selected of ["score @live", "_camino @live {ref}", "title @live title", "title @live(unchecked: true)"])
await assert.rejects(
@@ -199,6 +224,68 @@ test("retired live syntax fails with migration guidance", async () => {
);
});
test("implicit references reject aliases which occupy the canonical metadata path", async () => {
for (const invalid of ["_camino: title", "_camino {ref: __typename}"])
await assert.rejects(compile(`query Upcoming {root {items(first: 3) {entries {node {...Row}}}}}`, `fragment Row on TaskFacts {${invalid}}`), /QUERY_VALIDATION|Introspection/);
});
test("implicit reference merges with conditional aggregate metadata", async () => {
const checked = await compileQuery({ ...fixture(""), fragments: [] }, [collection, facts], async () =>
`query Upcoming($show: Boolean!) {root {_camino @include(if: $show) {relations {items {aggregate {count}}}}}}`);
const metadata = checked.selection[0]!.selection.filter((field) => field.key === "_camino");
assert.equal(metadata.length, 1);
assert.deepEqual(metadata[0]!.conditions, []);
assert.equal(metadata[0]!.selection.find((field) => field.name === "relations")!.conditions.length, 1);
assert.deepEqual(metadata[0]!.selection.find((field) => field.name === "ref")!.conditions, []);
assert.equal(metadata[0]!.selection.find((field) => field.name === "relations")!.selection[0]!.selection[0]!.selection.some((field) => field.name === "_camino"), false);
});
test("qualified interface predicates and guaranteed inline projections retain nominal reads", async () => {
const owner = iface(
`interface Owner id "owner" revision "owner@1" { queryable value name id "name" : string {get id "name:get";} }`,
);
const owned = iface(
`import interface Owner;
interface Owned id "owned" revision "owned@1" {
queryable relation owner id "owner" : optional-one interface Owner {resolve id "owner:resolve";}
queryable value label id "owned:label" : string {get id "owned:label:get";}
}`,
[owner],
);
const item = iface(
`import interface Owned;
interface Item id "item" revision "item@1" requires Owned {
queryable value label id "item:label" : string {get id "item:label:get";}
}`,
[owned, owner],
);
const declaration = {
...fixture(""),
root: { kind: "workspace-interface" as const, interface: item.revisionId },
fragments: [],
};
const document = `query Upcoming($owner: QxRef_Owner!) {root {objects(all: 10, where: {_camino: {as: {Owned: {owner: {eq: $owner}}}}}) {entries {node {label ... on Owned {owner {name} ownerLabel: label}}}}}}`;
const checked = await compileQuery(declaration, [item, owned, owner], async () => document);
assert.ok(
checked.effects.some(
(effect) =>
effect.interfaceRevisionId === owned.revisionId &&
effect.memberId === "owner" &&
effect.uses.includes("predicate"),
),
);
const objects = checked.selection[0]!.selection[0]!;
assert.equal(objects.predicate?.kind, "and");
assert.ok(JSON.stringify(objects.predicate).includes('"kind":"as"'));
const node = objects.selection[0]!.selection[0]!;
assert.equal(node.selection.find((s) => s.key === "ownerLabel")!.interfaceRevisionId, owned.revisionId);
assert.equal(node.selection.filter((s) => s.key === "_camino").length, 1);
await assert.rejects(
compileQuery(declaration, [item, owned, owner], async () => document.replace("ownerLabel: label", "label")),
/QUERY_PROJECTION_CONFLICT|QUERY_VALIDATION/,
);
});
test("query dependency ports resolve exact exports without declaration ordering constraints", () => {
const result = compileCapabilityResourceSource(
`import interface Tasks;