Implement query execution, scoped RPC enrichment, live collections, and scaffold integration

This commit is contained in:
Timothy J. Aveni
2026-09-17 14:34:16 -07:00
parent 61a410f98f
commit cd13120937
37 changed files with 4406 additions and 2647 deletions
+93
View File
@@ -3,6 +3,7 @@ import { test } from "node:test";
import { compileCapabilityResourceSource } from "../src/capability-language/index.js";
import { compileQuery } from "../src/query/compile.js";
import { QueryCompileError } from "../src/query/types.js";
import { checkQueryTemplate } from "../src/query/templates.js";
import type { InterfaceRevision } from "../src/capability-model/types.js";
const source = { repository: "https://example.test/queries.git", commit: "a".repeat(40) };
@@ -33,6 +34,64 @@ interface Tasks id "tasks" revision "tasks@1" {
}`,
[facts],
);
test("generic query templates check declared bounds before closed specialization", async () => {
const generic = iface(
`import interface TaskFacts;
interface Collection<object Item implements TaskFacts> id "generic-collection" revision "generic-collection@1" {
queryable relation items id "items" : many object Item { resolve id "items:resolve"; }
}`,
[facts],
);
const build = (bound: string) =>
compileCapabilityResourceSource(
`import interface Collection; import interface TaskFacts;
package GenericQueries id "generic-queries" revision "generic-queries@1" {
query Rows<object Item ${bound}> id "rows-template" root Collection<Item>
document "rows.graphql" operation "Rows" { view object Item as TaskFacts; max rows 30; }
query TaskRows id "task-rows" specialize Rows<interface TaskFacts>;
}`,
{
source,
environment: {
interfaces: new Map([
["Collection", generic],
["TaskFacts", facts],
]),
interfaceClosure: [generic, facts],
},
},
);
const result = build("implements TaskFacts");
assert.ok(result.ok, JSON.stringify(result.diagnostics));
if (result.resource.kind !== "package") throw new Error("package expected");
const pkg = result.resource.revision;
assert.equal(pkg.queries?.length, 1);
assert.equal(pkg.queryTemplates?.length, 1);
assert.equal(pkg.queries![0]!.application!.templateId, "rows-template");
const read = async () => `query Rows { root { items(first: 3) { entries { node { title } } } } }`;
const universal = await checkQueryTemplate(pkg.queryTemplates![0]!, [generic, facts], read);
assert.equal(
universal.effects.some((effect) => effect.memberId === "title"),
true,
);
const specialized = await compileQuery(
pkg.queries![0]!,
[generic, facts, ...(result.resource.specializations ?? [])],
read,
);
assert.equal(
specialized.effects.some((effect) => effect.memberId === "title"),
true,
);
const invalid = build("");
assert.ok(invalid.ok, JSON.stringify(invalid.diagnostics));
if (invalid.resource.kind !== "package") throw new Error("package expected");
await assert.rejects(
checkQueryTemplate(invalid.resource.revision.queryTemplates![0]!, [generic, facts], read),
/bound|guarantee|implement/i,
);
});
function fixture(clauses = "") {
const result = compileCapabilityResourceSource(
`import interface Tasks; import interface TaskFacts;
@@ -66,6 +125,40 @@ const document = `query Upcoming($first: Int!, $before: Int64!) {
const compile = (query = document, row = "fragment Row on TaskFacts { title due }", clauses = "") =>
compileQuery(fixture(clauses), [collection, facts], async (name) => (name.endsWith("row.graphql") ? row : query));
test("query dependency ports resolve exact exports without declaration ordering constraints", () => {
const result = compileCapabilityResourceSource(
`import interface Tasks;
package Queries id "queries" revision "queries@1" {
operation load id "read" : unit -> unit mode call receiver interfaces [Tasks]
requires { query upcoming id "upcoming-port" : Queries.Upcoming; };
query Upcoming id "upcoming" root Tasks document "upcoming.graphql" operation "Upcoming" { max rows 30; }
}`,
{ source, environment: { interfaces: new Map([["Tasks", collection]]), interfaceClosure: [collection, facts] } },
);
assert.ok(result.ok, JSON.stringify(result.diagnostics));
assert.equal(result.resource.kind, "package");
if (result.resource.kind !== "package") throw new Error("package expected");
assert.deepEqual(result.resource.revision.exports[0]!.dependencyPorts[0]!.requirement, {
kind: "query",
packageRevisionId: "queries@1",
queryId: "upcoming",
});
});
test("result types merge repeated selections and preserve conditional fragment fields", async () => {
const checked = await compile(
`query Upcoming($show: Boolean!) {
root { items(first: 3) { entries { node { title } } } }
root { items(first: 3) { entries { node { ...Row @include(if: $show) } } } }
}`,
"fragment Row on TaskFacts { due done }",
);
const output = JSON.stringify(checked.output);
assert.match(output, /\"title\":\{\"kind\":\"scalar\",\"name\":\"string\"\}/);
assert.match(output, /\"done\":\{\"kind\":\"optional\",\"value\":\{\"kind\":\"scalar\",\"name\":\"bool\"\}\}/);
assert.match(output, /\"due\"/);
});
test("fixed GraphQL yields exact effects, typed references and distinct query artifacts", async () => {
const checked = await compile();
assert.deepEqual(checked.variables, {