Compile and execute bounded GraphQL replica admission
This commit is contained in:
@@ -9,4 +9,5 @@ export { generatePortableTypeScriptBindings } from "../bindings/portable.js";
|
|||||||
|
|
||||||
export * from "./kernel.js";
|
export * from "./kernel.js";
|
||||||
export * from "./projections.js";
|
export * from "./projections.js";
|
||||||
|
export * from "./queries.js";
|
||||||
export { generatePortableClientBindings } from "../bindings/portable-client.js";
|
export { generatePortableClientBindings } from "../bindings/portable-client.js";
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import { portableValueSchema } from "../bindings/portable.js";
|
||||||
|
import { ExecutionPlanError, hexDigest, type ExecutionPlan } from "./plan.js";
|
||||||
|
import type { ValueType } from "../capability-model/types.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;
|
||||||
|
return {
|
||||||
|
codec: "camino-query-program-v1",
|
||||||
|
plan: plan.digest,
|
||||||
|
queries: plan.definition.queries.map(({ id, query, admittedOperations }) => {
|
||||||
|
const checked = query.checked;
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
import { mkdir, writeFile } from "node:fs/promises";
|
import { mkdir, writeFile } from "node:fs/promises";
|
||||||
import { loadReaderWorkspace } from "./reader-fixture.js";
|
import { loadReaderWorkspace } from "./reader-fixture.js";
|
||||||
import { compileKernelProgram } from "../src/execution-world/kernel.js";
|
import { compileKernelProgram } from "../src/execution-world/kernel.js";
|
||||||
|
import { compileQueryProgram } from "../src/execution-world/queries.js";
|
||||||
import { compileExecutionPlan } from "../src/execution-world/plan.js";
|
import { compileExecutionPlan } from "../src/execution-world/plan.js";
|
||||||
import { generatePortableTypeScriptBindings } from "../src/bindings/portable.js";
|
import { generatePortableTypeScriptBindings } from "../src/bindings/portable.js";
|
||||||
import { generatePortableClientBindings } from "../src/bindings/portable-client.js";
|
import { generatePortableClientBindings } from "../src/bindings/portable-client.js";
|
||||||
@@ -15,6 +16,7 @@ for (const target of ["browser", "server"])
|
|||||||
);
|
);
|
||||||
await writeFile("dist/portable-reader/plan.json", JSON.stringify(plan));
|
await writeFile("dist/portable-reader/plan.json", JSON.stringify(plan));
|
||||||
await writeFile("dist/portable-reader/kernel.json", JSON.stringify(compileKernelProgram(plan)));
|
await writeFile("dist/portable-reader/kernel.json", JSON.stringify(compileKernelProgram(plan)));
|
||||||
|
await writeFile("dist/portable-reader/queries.json", JSON.stringify(compileQueryProgram(plan)));
|
||||||
await writeFile("dist/portable-reader/client.ts", generatePortableClientBindings(plan, { target: "browser" }));
|
await writeFile("dist/portable-reader/client.ts", generatePortableClientBindings(plan, { target: "browser" }));
|
||||||
const serviceWorkspace = structuredClone(plan.definition.workspace);
|
const serviceWorkspace = structuredClone(plan.definition.workspace);
|
||||||
serviceWorkspace.atoms = [];
|
serviceWorkspace.atoms = [];
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ revision "package:reader-runtime@1" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
query HighlightScope id "reader:highlight-scope" root workspace HighlightData document "queries/highlight-scope.graphql" operation "HighlightScope" { view Comment as CommentData; replica; max rows 200; }
|
query HighlightScope id "reader:highlight-scope" root workspace HighlightData document "queries/highlight-scope.graphql" operation "HighlightScope" { view Comment as CommentData; replica; max rows 200; }
|
||||||
|
query HighlightWindow id "reader:highlight-window" root workspace HighlightData document "queries/highlight-window.graphql" operation "HighlightWindow" { view Comment as CommentData; replica; max rows 200; }
|
||||||
query HighlightList id "reader:highlight-list" root workspace HighlightData document "queries/highlight-list.graphql" operation "HighlightList" { view Comment as CommentData; local from HighlightScope; max rows 200; watch; }
|
query HighlightList id "reader:highlight-list" root workspace HighlightData document "queries/highlight-list.graphql" operation "HighlightList" { view Comment as CommentData; local from HighlightScope; max rows 200; watch; }
|
||||||
query HighlightEditorInputs id "reader:editor-inputs" root HighlightData document "queries/editor-inputs.graphql" operation "HighlightEditorInputs" { view Comment as CommentData; replica; max rows 1; }
|
query HighlightEditorInputs id "reader:editor-inputs" root HighlightData document "queries/editor-inputs.graphql" operation "HighlightEditorInputs" { view Comment as CommentData; replica; max rows 1; }
|
||||||
query HighlightEditorView id "reader:editor-view" root HighlightData document "queries/editor-view.graphql" operation "HighlightEditorView" { view Comment as CommentData; local from HighlightEditorInputs; max rows 1; watch; }
|
query HighlightEditorView id "reader:editor-view" root HighlightData document "queries/editor-view.graphql" operation "HighlightEditorView" { view Comment as CommentData; local from HighlightEditorInputs; max rows 1; watch; }
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
query HighlightWindow($color: String!) {
|
||||||
|
root {
|
||||||
|
objects(first: 2, where: {color: {eq: $color}}, orderBy: {range: {start: ASC}}) {
|
||||||
|
entries { key node { _qx {ref} color range {sourceRevision start end} } }
|
||||||
|
pageInfo { hasNextPage endCursor }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ test("complete reader contracts compose factories, portable selectors, CRDT edit
|
|||||||
assert.equal(workspace.constructors.length, 0);
|
assert.equal(workspace.constructors.length, 0);
|
||||||
assert.equal(workspace.atoms.length, 3);
|
assert.equal(workspace.atoms.length, 3);
|
||||||
const linked = linkQueries(workspace);
|
const linked = linkQueries(workspace);
|
||||||
assert.equal(linked.length, 6);
|
assert.equal(linked.length, 7);
|
||||||
assert.equal(linked.filter((q) => q.checked.declaration.placement?.kind === "local").length, 3);
|
assert.equal(linked.filter((q) => q.checked.declaration.placement?.kind === "local").length, 3);
|
||||||
const screen = linked.find((q) => q.checked.declaration.displayName === "ReaderScreen")!;
|
const screen = linked.find((q) => q.checked.declaration.displayName === "ReaderScreen")!;
|
||||||
assert.ok(screen.fields.some((f) => f.memberId === "highlight:excerpt" && f.binding.kind === "package"));
|
assert.ok(screen.fields.some((f) => f.memberId === "highlight:excerpt" && f.binding.kind === "package"));
|
||||||
|
|||||||
Reference in New Issue
Block a user