Author and apply bounded structured CRDT edits through the mutation log

This commit is contained in:
Timothy J. Aveni
2026-09-20 04:41:35 -07:00
parent c75888833b
commit 824caddbf2
2 changed files with 18 additions and 2 deletions
+1 -1
View File
@@ -73,5 +73,5 @@ export function generatePortableClientBindings(
} }
const groups = (entries: Map<string, string[]>) => const groups = (entries: Map<string, string[]>) =>
[...entries].map(([name, values]) => `${JSON.stringify(name)}: {${values.join(",\n")}}`).join(",\n"); [...entries].map(([name, values]) => `${JSON.stringify(name)}: {${values.join(",\n")}}`).join(",\n");
return `// Generated application descriptors. Regenerate; do not edit.\nimport { defineOperationDescriptor, type ManagedReference, type ReplicaProjection, type LocalQueryDescriptor } from ${JSON.stringify(options.runtimeModule ?? "@quixos/camino-replica-core")};\nexport const planDigest = ${JSON.stringify(plan.digest)};\nexport const operations = {${groups(atoms)}} as const;\nexport const queries = {${groups(queries)}} as const;\nexport const views = {${groups(views)}} as const;\nexport const unavailable = ${JSON.stringify(unavailable)} as const;\n`; return `// Generated application descriptors. Regenerate; do not edit.\nimport { defineOperationDescriptor, type PreparedChange, type ManagedReference, type ReplicaProjection, type LocalQueryDescriptor } from ${JSON.stringify(options.runtimeModule ?? "@quixos/camino-replica-core")};\nexport const planDigest = ${JSON.stringify(plan.digest)};\nexport const operations = {${groups(atoms)}} as const;\nexport const queries = {${groups(queries)}} as const;\nexport const views = {${groups(views)}} as const;\nexport const unavailable = ${JSON.stringify(unavailable)} as const;\n`;
} }
+17 -1
View File
@@ -29,6 +29,16 @@ const expectation = (e: ObjectExpectation) => ({
/** The protocol IR and replica wire schema intentionally share structure, not reference identity. */ /** The protocol IR and replica wire schema intentionally share structure, not reference identity. */
export function portableValueSchema(type: ValueType): unknown { export function portableValueSchema(type: ValueType): unknown {
switch (type.kind) { switch (type.kind) {
case "change":
return {
kind: "record",
fields: {
codec: { kind: "scalar", name: "string" },
profile: { kind: "scalar", name: "string" },
generation: { kind: "scalar", name: "string" },
changes: { kind: "list", value: { kind: "scalar", name: "bytes" } },
},
};
case "scalar": case "scalar":
return type; return type;
case "builtin": case "builtin":
@@ -55,6 +65,8 @@ export function portableValueSchema(type: ValueType): unknown {
export function tsType(type: ValueType): string { export function tsType(type: ValueType): string {
portableValueSchema(type); portableValueSchema(type);
switch (type.kind) { switch (type.kind) {
case "change":
return `PreparedChange<${tsType(type.value)}>`;
case "scalar": case "scalar":
return type.name === "bool" return type.name === "bool"
? "boolean" ? "boolean"
@@ -125,6 +137,10 @@ function methods(schema: BindingSchema, entry: PackageExport, port: DependencyPo
writable(); writable();
return method("set", { kind: "state", primitive }, req.valueType, unit); return method("set", { kind: "state", primitive }, req.valueType, unit);
} }
if (primitive === "edit") {
writable();
return method("edit", { kind: "state", primitive }, { kind: "change", value: req.valueType }, unit);
}
throw new PortableBindingError(path, `State ${primitive} is not supported by this portable adapter yet`); throw new PortableBindingError(path, `State ${primitive} is not supported by this portable adapter yet`);
}); });
case "edge": { case "edge": {
@@ -248,5 +264,5 @@ export function generatePortableTypeScriptBindings(
methods: all.map((m) => ({ ...m, input: portableValueSchema(m.input), output: portableValueSchema(m.output) })), methods: all.map((m) => ({ ...m, input: portableValueSchema(m.input), output: portableValueSchema(m.output) })),
}); });
} }
return `// Generated portable bindings. Regenerate from the checked capability schema; do not edit.\nimport { definePortableRegistry, type PortableExport, type ManagedReference, type ExactReference, type MutationKey } from ${q(options.runtimeModule ?? "@quixos/camino-replica-core")};\nexport const bindingDigest = ${q(hexDigest(schema))};\nexport const packageRevision = ${q(revision)};\nexport const target = ${q(options.target)};\nexport type Contexts = { ${contexts.join(";\n")} };\nexport type PortableImplementation = { ${implementations.join(";\n")} };\nexport const declarations = ${JSON.stringify(descriptors, null, 2)} as const satisfies readonly PortableExport[];\nexport function createRegistry(handlers: PortableImplementation) { const installed = { ...handlers }; return definePortableRegistry(declarations, { ${adapters.join(",\n")} }); }\n`; return `// Generated portable bindings. Regenerate from the checked capability schema; do not edit.\nimport { definePortableRegistry, type PortableExport, type PreparedChange, type ManagedReference, type ExactReference, type MutationKey } from ${q(options.runtimeModule ?? "@quixos/camino-replica-core")};\nexport const bindingDigest = ${q(hexDigest(schema))};\nexport const packageRevision = ${q(revision)};\nexport const target = ${q(options.target)};\nexport type Contexts = { ${contexts.join(";\n")} };\nexport type PortableImplementation = { ${implementations.join(";\n")} };\nexport const declarations = ${JSON.stringify(descriptors, null, 2)} as const satisfies readonly PortableExport[];\nexport function createRegistry(handlers: PortableImplementation) { const installed = { ...handlers }; return definePortableRegistry(declarations, { ${adapters.join(",\n")} }); }\n`;
} }