Add explicit CRDT edit contracts and structured query values

This commit is contained in:
Timothy J. Aveni
2026-09-19 22:33:20 -07:00
parent 0555c929b1
commit 4c0c404a39
27 changed files with 3272 additions and 2745 deletions
+91
View File
@@ -0,0 +1,91 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import { compileCapabilityResourceSource, compileCapabilitySource } from "../src/capability-language/index.js";
const source = { repository: "https://example.test/edit.git", commit: "a".repeat(40) };
const content = "record { text:string; tags:list<string>; }";
const iface = compileCapabilityResourceSource(
`interface Comment id "comment" revision "comment@1" {
value content id "content" : ${content} {
get id "content:get" execution snapshot targets [browser, server];
edit id "content:edit" execution transaction targets [browser, server];
}
}`,
{ source },
);
assert.ok(iface.ok && iface.resource.kind === "interface", JSON.stringify(iface.diagnostics));
const contract = iface.resource.revision;
const compile = (policy = `crdt(${content})`, binding = "edit") =>
compileCapabilitySource(
`
workspace Comments id "w" revision "w@1" commit "${"a".repeat(40)}" {
import interface Comment;
atom Comment id "comment";
conform Comment as Comment id "owner:comment" {
private state Content id "content" on Comment : ${content} policy ${policy};
initialize { content to state Content; };
bind content.get to state Content.read;
bind content.edit to state Content.${binding};
}
}`,
"comments.qx",
{ interfaces: new Map([["Comment", contract]]), interfaceClosure: [contract] },
);
test("native edit is an explicit typed change capability over structured content", () => {
const result = compile();
assert.ok(result.ok, JSON.stringify(result.diagnostics));
const member = contract.members[0]!;
assert.equal(member.kind, "value");
if (member.kind !== "value") return;
assert.deepEqual(member.operations[1]!.inputType, { kind: "change", value: member.valueType });
assert.equal(result.workspace.conformances[0]!.operationBindings[1]!.binding.kind, "state");
});
test("edit cannot target a register, mismatched profile, or ordinary replacement", () => {
for (const [policy, binding] of [
["register", "edit"],
["crdt(bytes)", "edit"],
[`crdt(${content})`, "write"],
]) {
const result = compile(policy, binding);
assert.equal(result.ok, false);
assert.ok(
result.diagnostics.some((d) => d.code === "invalid-state-binding"),
JSON.stringify(result.diagnostics),
);
}
});
test("authored prepared change transport values fail explicitly until their runtime is implemented", () => {
const result = compileCapabilityResourceSource(
'package P id "p" revision "p@1" { function applyChange id "apply" : change<string> -> unit; }',
{ source },
);
assert.equal(result.ok, false);
assert.ok(
result.diagnostics.some((d) => d.code === "unsupported-prepared-change"),
JSON.stringify(result.diagnostics),
);
});
test("generic edit retains its materialized type through specialization", () => {
const generic = compileCapabilityResourceSource(
`interface Cell<value T : storable> id "cell" revision "cell@1" {
value content id "content" : T { get id "get"; edit id "edit" execution transaction targets [browser, server]; }
}`,
{ source },
);
assert.ok(generic.ok && generic.resource.kind === "interface", JSON.stringify(generic.diagnostics));
const result = compileCapabilitySource(
`workspace W id "w" revision "w@1" commit "${"a".repeat(40)}" {
import interface Cell;
atom Note id "note";
conform Note as Cell<${content}> {
private state Content id "content" on Note : ${content} policy crdt(${content});
bind content to state Content;
}
}`,
"cell.qx",
{ interfaces: new Map([["Cell", generic.resource.revision]]), interfaceClosure: [generic.resource.revision] },
);
assert.ok(result.ok, JSON.stringify(result.diagnostics));
const applied = result.workspace.interfaceImports.find((i) => i.application)!;
assert.equal(applied.members[0]!.operations[1]!.inputType.kind, "change");
});
+2 -2
View File
@@ -455,8 +455,8 @@ test("expanding recursive generic interfaces fail with a bounded diagnostic", ()
assert.equal(result.diagnostics[0].code, "recursive-application");
});
test("storable parameters do not imply support for RPC-only records", () => {
assert.equal(isStorableType({ kind: "record", fields: { name: valueType.string } }), false);
test("storable records recursively require storable leaves", () => {
assert.equal(isStorableType({ kind: "record", fields: { name: valueType.string } }), true);
assert.equal(isStorableType(valueType.list(valueType.optional(valueType.string))), true);
});
+1 -1
View File
@@ -34,7 +34,7 @@ test("queryable fields need one authoritative getter and supported values", () =
assert.equal(compile('queryable value title id "title" : string { get id "title:get"; }').ok, true);
for (const member of [
'queryable value title id "title" : string { set id "title:set"; }',
'queryable value title id "title" : list<string> { get id "title:get"; }',
'queryable value title id "title" : list<unit> { get id "title:get"; }',
'queryable value title id "title" : string { get id "title:get"; get id "other:get"; }',
])
assert.equal(compile(member).ok, false, member);
+86
View File
@@ -0,0 +1,86 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import { compileCapabilityResourceSource } from "../src/capability-language/index.js";
import { compileQuery } from "../src/query/compile.js";
import { querySelectionToWire } from "../src/query/proto.js";
import { defaultQueryBudgets, type QueryDeclaration } from "../src/query/types.js";
const source = { repository: "https://example.test/structured.git", commit: "a".repeat(40) };
const result = compileCapabilityResourceSource(
`interface Highlight id "highlight" revision "highlight@1" {
queryable value range id "range" : record { sourceRevision:string; start:int32; end:int32; } { get id "range:get" execution snapshot targets [browser, server]; }
queryable value content id "content" : record { text:string; tags:list<string>; metadata:optional<record { score:int32; }>; } { get id "content:get" execution snapshot targets [browser, server]; }
}`,
{ source },
);
assert.ok(result.ok && result.resource.kind === "interface", JSON.stringify(result.diagnostics));
const iface = result.resource.revision;
const declaration: QueryDeclaration = {
id: "rows",
displayName: "Rows",
root: { kind: "workspace-interface", interface: iface.revisionId },
document: "rows.graphql",
operation: "Rows",
fragments: [],
views: [],
allowances: [],
budgets: { ...defaultQueryBudgets },
watch: false,
};
const compile = (selection: string) =>
compileQuery(
declaration,
[iface],
async () =>
`query Rows {root {objects(first:10, ${selection}) {entries {node {position:range {begin:start end} content {text tags metadata {score}}}}}}}`,
);
test("record/list query values preserve nested projections and leaf predicates", async () => {
const checked = await compile(
"where:{range:{start:{gte:4}},content:{metadata:{score:{gt:2}}}}, orderBy:{range:{start:ASC}}",
);
const extent = checked.selection[0]!.selection[0]!;
assert.equal(extent.predicate?.kind, "and");
if (extent.predicate?.kind !== "and") return;
const expressions = extent.predicate.children.map((p) => (p.kind === "compare" ? p.expression : undefined));
assert.deepEqual(
expressions.map((e) => e?.leaf),
[
{ kind: "field", interfaceRevisionId: "highlight@1", memberId: "range", valuePath: ["start"] },
{ kind: "field", interfaceRevisionId: "highlight@1", memberId: "content", valuePath: ["metadata", "score"] },
],
);
assert.deepEqual(expressions[1]?.type, { kind: "optional", value: { kind: "scalar", name: "int32" } });
const wire = querySelectionToWire(extent);
assert.equal(wire.predicate?.predicate.case, "and");
if (wire.predicate?.predicate.case === "and") {
const child = wire.predicate.predicate.value.children[0]!;
assert.equal(child.predicate.case, "compare");
if (child.predicate.case === "compare") {
const operand = child.predicate.value.operand?.operand;
assert.ok(operand?.case === "expression" && operand.value.leaf.case === "field");
assert.deepEqual(operand.value.leaf.value.valuePath, ["start"]);
}
}
const raw = JSON.stringify(checked.output);
assert.match(raw, /"begin"/);
assert.match(raw, /"tags":\{"kind":"list"/);
assert.equal(checked.effects.filter((e) => e.memberId === "range").length, 1);
});
test("structured predicates reject whole-record scalar comparisons and implicit list expansion", async () => {
await assert.rejects(compile("where:{range:{eq:4}}"), /not defined/);
await assert.rejects(compile('where:{content:{tags:{eq:"x"}}}'), /not defined/);
});
test("explicit budgets can exceed defaults without exceeding wire bounds", () => {
const compile = (rows: string) =>
compileCapabilityResourceSource(
`import interface Highlight;
package P id "p" revision "p@1" {
query Rows id "rows" root Highlight document "rows.graphql" operation "Rows" { replica; max rows ${rows}; max result-bytes 8388608; }
}`,
{ source, environment: { interfaces: new Map([["Highlight", iface]]), interfaceClosure: [iface] } },
);
const result = compile("5000");
assert.ok(result.ok && result.resource.kind === "package", JSON.stringify(result.diagnostics));
assert.equal(result.resource.revision.queries![0]!.budgets.rows, 5000);
assert.equal(compile("4294967296").ok, false);
});