Files
quixos-protocol/src/execution-world/queries.ts
T

107 lines
4.4 KiB
TypeScript
Raw Normal View History

import { compileKernelProgram } from "./kernel.js";
import { compileInspectionTemplates } from "./inspection.js";
import { portableValueSchema } from "../bindings/portable.js";
import { ExecutionPlanError, hexDigest, type ExecutionPlan } from "./plan.js";
import type { ValueType } from "../capability-model/types.js";
import { compileQueryComposition } from "../query/composition.js";
const lowerTypes = (value: unknown): unknown =>
JSON.parse(
JSON.stringify(value, (key, value: unknown) => {
if (
key === "type" &&
value &&
typeof value === "object" &&
"kind" in value &&
["scalar", "builtin", "optional", "list", "record", "object-ref"].includes(String(value.kind))
)
return portableValueSchema(value as ValueType);
return value;
}),
);
/** Checked query IR and installed getter identities, without GraphQL schemas/parsers or SQL adapters.
* Keep authored selection names only for output paths; field access uses resolved interface/member IDs. */
export function compileQueryProgram(plan: ExecutionPlan): unknown {
if (hexDigest(plan.definition) !== plan.digest)
throw new ExecutionPlanError("INVALID_PLAN", "queries", "Plan digest mismatch");
const interfaces = plan.definition.workspace.interfaceImports;
const allQueries = new Map(plan.definition.queries.map(({ id, query }) => [id, query.checked]));
const compositions = plan.definition.workspace.packageImports.flatMap((pkg) =>
(pkg.queryCompositions ?? []).map((composition) => {
const scoped = new Map(allQueries);
for (const query of pkg.checkedQueries ?? []) scoped.set(query.declaration.id, query);
const checked = compileQueryComposition(
{
id: composition.id,
input: composition.input,
steps: composition.steps,
maxQueries: composition.maxQueries,
},
scoped,
);
if (checked.definitionDigest !== composition.definitionDigest)
throw new ExecutionPlanError("INVALID_QUERY_COMPOSITION", composition.id, "Checked dependencies changed");
return {
id: JSON.stringify([pkg.revisionId, composition.id]),
input: portableValueSchema(checked.input),
output: portableValueSchema(checked.output),
maxQueries: checked.maxQueries,
steps: checked.steps.map((step) => ({
...step,
query: (pkg.checkedQueries ?? []).some((query) => query.declaration.id === step.query)
? JSON.stringify([pkg.revisionId, step.query])
: step.query,
})),
};
}),
);
return {
codec: "camino-query-program-v1",
plan: (compileKernelProgram(plan) as { plan: string }).plan,
...(compositions.length ? { compositions } : {}),
queries: [
...compileInspectionTemplates(plan).queries,
...plan.definition.queries.map(({ id, query, admittedOperations }) => {
const checked = query.checked;
return {
id,
...(checked.declaration.fragment && !checked.declaration.fragment.admission
? {
fragment: {
package: query.packageRevisionId,
id: checked.declaration.id,
digest: checked.definitionDigest,
},
}
: {}),
placement: checked.declaration.placement?.kind ?? "remote",
source:
checked.declaration.placement?.kind === "local"
? JSON.stringify([query.packageRevisionId, checked.declaration.placement.sourceQueryId])
: null,
root: checked.declaration.root,
atoms: query.rootAtoms,
variables: portableValueSchema(checked.variables),
defaults: checked.variableDefaults,
selection: lowerTypes(checked.selection),
budgets: checked.declaration.budgets,
fields: query.fields.map((field) => {
const member = interfaces
.find((i) => i.revisionId === field.interfaceRevisionId)!
.members.find((m) => m.id === field.memberId)!;
return {
atom: field.atomId,
interface: field.interfaceRevisionId,
member: field.memberId,
name: member.displayName,
operation: JSON.stringify([field.atomId, field.interfaceRevisionId, field.getter]),
};
}),
admittedOperations,
};
}),
],
};
}