diff --git a/src/bindings/portable-client.ts b/src/bindings/portable-client.ts index a9d1aa3..8d9a4f5 100644 --- a/src/bindings/portable-client.ts +++ b/src/bindings/portable-client.ts @@ -73,5 +73,5 @@ export function generatePortableClientBindings( } const groups = (entries: Map) => [...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`; } diff --git a/src/bindings/portable.ts b/src/bindings/portable.ts index be5a90d..bb62c06 100644 --- a/src/bindings/portable.ts +++ b/src/bindings/portable.ts @@ -29,6 +29,16 @@ const expectation = (e: ObjectExpectation) => ({ /** The protocol IR and replica wire schema intentionally share structure, not reference identity. */ export function portableValueSchema(type: ValueType): unknown { 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": return type; case "builtin": @@ -55,6 +65,8 @@ export function portableValueSchema(type: ValueType): unknown { export function tsType(type: ValueType): string { portableValueSchema(type); switch (type.kind) { + case "change": + return `PreparedChange<${tsType(type.value)}>`; case "scalar": return type.name === "bool" ? "boolean" @@ -125,6 +137,10 @@ function methods(schema: BindingSchema, entry: PackageExport, port: DependencyPo writable(); 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`); }); case "edge": { @@ -248,5 +264,5 @@ export function generatePortableTypeScriptBindings( 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`; }