Implement checked aggregation plans, native SQL, and bounded RPC capture

This commit is contained in:
Timothy J. Aveni
2026-09-17 18:23:16 -07:00
parent cd13120937
commit e61b0a36ac
17 changed files with 2968 additions and 100 deletions
+68
View File
@@ -0,0 +1,68 @@
import assert from "node:assert/strict";
import { compileCapabilityResourceSource } from "../../src/capability-language/index.js";
import type { InterfaceRevision } from "../../src/capability-model/types.js";
import { compileQuery } from "../../src/query/compile.js";
const source = { repository: "https://example.test/aggregation.git", commit: "a".repeat(40) };
const interfaces: InterfaceRevision[] = [];
function resource(text: string) {
const result = compileCapabilityResourceSource(`external atom TaskObject id "task";\n${text}`, {
source,
environment: {
interfaces: new Map(interfaces.map((i) => [i.displayName, i])),
interfaceClosure: interfaces,
},
});
assert.ok(result.ok, JSON.stringify(result.diagnostics));
if (result.resource.kind === "interface") interfaces.push(result.resource.revision);
return result.resource;
}
resource(`interface Person id "person" revision "person@1" {
queryable value name id "name" : string { get id "name:get"; }
}`);
resource(`interface Tag id "tag" revision "tag@1" {
queryable value color id "color" : string { get id "color:get"; }
queryable relation tasks id "tag-tasks" : many atom TaskObject { resolve id "tag-tasks:resolve"; }
}`);
resource(`import interface Tag; import interface Person;
interface Task id "task" revision "task@1" {
queryable value estimatedHours id "hours" : optional<double> { get id "hours:get"; }
queryable value cost id "cost" : int64 { get id "cost:get"; }
queryable rpc value score id "score" : optional<double> { get id "score:get"; }
queryable relation tags id "tags" : many-unique interface Tag { resolve id "tags:resolve"; }
queryable relation assignee id "assignee" : optional-one interface Person { resolve id "assignee:resolve"; }
}`);
resource(`import interface Task;
interface Tasks id "tasks" revision "tasks@1" {
queryable relation tasks id "tasks" : many-unique interface Task { resolve id "tasks:resolve"; }
queryable relation copies id "copies" : many interface Task ordered { resolve id "copies:resolve"; }
queryable relation slots id "slots" : many interface Task keyed "int64" { resolve id "slots:resolve"; }
}`);
const pkg = resource(`import interface Tasks; import interface Task;
package Reports id "reports" revision "reports@1" {
query Report id "report" root Tasks document "report.graphql" operation "Report" {
max rows 50;
view TaskObject as Task;
allow Task.score aggregate "Bounded aggregation fixture";
allow Task.score predicate "Bounded aggregation fixture";
allow Task.score order "Bounded aggregation fixture";
allow Task.score group "Bounded aggregation fixture";
allow Task.score distinct "Bounded aggregation fixture";
}
}`);
if (pkg.kind !== "package") throw new Error("package expected");
const declaration = pkg.revision.queries![0]!;
export const compileAggregationDocument = (document: string) =>
compileQuery(declaration, interfaces, async () => document);
export const aggregationBindingSchema = (checked: Awaited<ReturnType<typeof compileQuery>>) => ({
format: "quixos-bindings" as const,
version: 1 as const,
interfaces,
packages: [{ ...pkg.revision, checkedQueries: [checked] }],
});
export const compileAggregation = (body: string, variables = "") =>
compileQuery(
declaration,
interfaces,
async () => `query Report${variables} { root { _qx { relations { tasks { ${body} } } } } }`,
);
+9
View File
@@ -55,6 +55,13 @@ export async function queryWorkspaceFixture() {
allow TaskFacts.score predicate "Bounded local collection";
allow TaskFacts.score order "Bounded local collection";
}
query Totals id "totals" root Collection<interface TaskFacts> document "totals.graphql" operation "Totals" {
max rows 30; watch;
}
query ScoreTotals id "score-totals" root Collection<interface TaskFacts> document "score-totals.graphql" operation "ScoreTotals" {
max rows 30; max candidates 60;
allow TaskFacts.score aggregate "Complete bounded collection report";
}
}`;
sources.Queries = packageSource;
const pkgResult = compileCapabilityResourceSource(packageSource, {
@@ -70,6 +77,8 @@ export async function queryWorkspaceFixture() {
"row.graphql": `fragment TaskRow on TaskFacts {title rank done assignee {name}}`,
"enriched.graphql": `query Enriched {root {items(first: 3) {entries {key node {_qx {ref} title score}}}}}`,
"ranked.graphql": `query Ranked {root {items(first: 3, where: {score: {gt: 0}}, orderBy: [{score: DESC}]) {entries {key node {_qx {ref} title}}}}}`,
"totals.graphql": `query Totals {root {_qx {relations {items {aggregate {count sum {rank}} groups(by: {done: true}, first: 10) {entries {group {done} aggregate {count sum {rank}}}}}}}}}`,
"score-totals.graphql": `query ScoreTotals {root {_qx {relations {items {aggregate {count sum {score}}}}}}}`,
};
pkg.checkedQueries = await Promise.all(
pkg.queries!.map((query) => compileQuery(query, allInterfaces, async (name) => documents[name]!)),