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
+28 -31
View File
@@ -14,6 +14,7 @@ import {
type GraphQLInputType,
type GraphQLFieldConfigMap,
type GraphQLInputFieldConfigMap,
type DocumentNode,
} from "graphql";
import { createHash } from "node:crypto";
import type {
@@ -23,6 +24,7 @@ import type {
RelationshipInterfaceMember,
} from "../capability-model/types.js";
import { QueryCompileError, type QueryDeclaration } from "./types.js";
import { relationalSchema, objectRow } from "./relational-schema.js";
// Validate losslessly; callers carry decimal strings across JSON transports.
function integerParser(name: string, min: bigint, max: bigint, value: unknown): string {
@@ -67,19 +69,21 @@ export const cursorScalar = new GraphQLScalarType({
return value;
},
});
export const referenceScalar = new GraphQLScalarType({ name: "ManagedReference" });
export function querySchema(declaration: QueryDeclaration, interfaces: readonly InterfaceRevision[]) {
export function querySchema(
declaration: QueryDeclaration,
interfaces: readonly InterfaceRevision[],
document?: DocumentNode,
) {
const contracts = new Map(interfaces.map((entry) => [entry.revisionId, entry]));
const objects = new Map<InterfaceRevisionId, GraphQLObjectType>();
const filters = new Map<InterfaceRevisionId, GraphQLInputObjectType>();
const orders = new Map<InterfaceRevisionId, GraphQLInputObjectType>();
const byName = new Map<string, InterfaceRevision>();
const comparisons = new Map<string, GraphQLInputObjectType>();
const metadata = new GraphQLObjectType({
name: "QxMetadata",
fields: { ref: { type: new GraphQLNonNull(referenceScalar) } },
});
const scalarTypes = new Map<string, ValueType>(
Object.entries(queryScalars).map(([name, type]) => [type.name, { kind: "scalar", name } as ValueType]),
);
scalarTypes.set("Cursor", { kind: "scalar", name: "string" });
const pageInfo = new GraphQLObjectType({
name: "QxPageInfo",
fields: {
@@ -130,6 +134,8 @@ export function querySchema(declaration: QueryDeclaration, interfaces: readonly
let result = comparisons.get(key);
if (!result) {
const fields: GraphQLInputFieldConfigMap = { eq: { type: value }, isNull: { type: GraphQLBoolean } };
if (base.kind === "scalar" && base.name !== "bytes")
fields.in = { type: new GraphQLList(new GraphQLNonNull(value)) };
if (base.kind === "scalar" && base.name !== "bool" && base.name !== "bytes")
for (const op of ["lt", "lte", "gt", "gte"]) fields[op] = { type: value };
result = new GraphQLInputObjectType({ name: `QxCompare${key}`, fields });
@@ -137,29 +143,12 @@ export function querySchema(declaration: QueryDeclaration, interfaces: readonly
}
return result;
};
const filter = (id: InterfaceRevisionId): GraphQLInputObjectType => {
const existing = filters.get(id);
if (existing) return existing;
const result = new GraphQLInputObjectType({
name: `${name(id)}Where`,
fields: () => {
const fields: GraphQLInputFieldConfigMap = {
and: { type: new GraphQLList(new GraphQLNonNull(result)) },
or: { type: new GraphQLList(new GraphQLNonNull(result)) },
not: { type: result },
};
for (const member of contract(id).members)
if (member.kind === "value" && member.queryRead) {
if (member.displayName in fields)
throw new QueryCompileError("QUERY_SCHEMA_NAME", `Reserved predicate name ${member.displayName}`);
fields[member.displayName] = { type: comparison(member.valueType) };
}
return fields;
},
});
filters.set(id, result);
return result;
};
const relational = relationalSchema(
{ contracts, target, name, scalar, comparison, scalarTypes, direction, cursor: cursorScalar, pageInfo },
document,
);
relational.discover(declaration.root);
const filter = (id: InterfaceRevisionId) => relational.where(objectRow(id));
const order = (id: InterfaceRevisionId) => {
let result = orders.get(id);
if (!result) {
@@ -181,6 +170,14 @@ export function querySchema(declaration: QueryDeclaration, interfaces: readonly
const result = new GraphQLObjectType({
name: name(id),
fields: () => {
const helpers = relational.helpers(id);
const metadata = new GraphQLObjectType({
name: `QxMetadata_${name(id)}`,
fields: {
ref: { type: new GraphQLNonNull(relational.refs(id)) },
...(helpers ? { relations: { type: new GraphQLNonNull(helpers) } } : {}),
},
});
const fields: GraphQLFieldConfigMap<unknown, unknown> = { _qx: { type: new GraphQLNonNull(metadata) } };
for (const member of contract(id).members) {
if (member.kind === "operation" || !member.queryRead) continue;
@@ -249,5 +246,5 @@ export function querySchema(declaration: QueryDeclaration, interfaces: readonly
fields: { root: { type: new GraphQLNonNull(object(declaration.root)) } },
}),
});
return { schema, byName, target, contracts };
return { schema, byName, target, contracts, relational, scalarTypes };
}