From d7cc562bf6104c1edb7a8650b307ed54be04ce0a Mon Sep 17 00:00:00 2001 From: "Timothy J. Aveni" Date: Sun, 12 Jul 2026 20:19:44 -0700 Subject: [PATCH] Add Automerge CRDT field APIs to package runtime --- .gitignore | 1 + .yarn/install-state.gz | Bin 7399 -> 8191 bytes package.json | 4 +- src/index.ts | 86 +++++++++++++++++++++++++++++++++++++---- yarn-project.nix | 2 + yarn.lock | 18 +++++++++ 6 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..deb6b0a --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.yarn/install-state.gz diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz index 6af1922f0bb4278d43d0901fe394a270f99bad54..8c515728cb65e20cacbaabc6144a15644793784b 100644 GIT binary patch literal 8191 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 ZUzo6c;D@K>bjbKW{XghyX^eTa0040 = { set(value: T): Promise; }; +export type CrdtField = Field> & { + change(fn: (doc: { value: T }) => void): Promise>; + replaceFromJson(value: T): Promise; +}; + export type LiveFieldValue = { $caminoValue: T; $caminoSource: { @@ -475,6 +486,11 @@ const bytesToBase64 = (value: Uint8Array) => const base64ToBytes = (value: string) => Buffer.from(value, "base64"); +type FieldCodec = { + fieldType?: string; + conflictStrategy?: string; +}; + const sourceToProto = (value: unknown) => { if (!isRecord(value) || !isRecord(value.field)) { return undefined; @@ -507,12 +523,30 @@ const sourceToProto = (value: unknown) => { }); }; +const codecFromSource = (value: unknown): FieldCodec | undefined => { + if (!isRecord(value) || !isRecord(value.field)) { + return undefined; + } + const field = value.field; + return { + ...(typeof field.fieldType === "string" ? { fieldType: field.fieldType } : {}), + ...(typeof field.conflictStrategy === "string" + ? { conflictStrategy: field.conflictStrategy } + : {}), + }; +}; + const withSource = (value: Value, source: unknown): Value => { const sourceValue = sourceToProto(source); return sourceValue ? create(ValueSchema, { ...value, source: sourceValue }) : value; }; -export const jsToProtoValue = (value: unknown): Value => { +export const jsToProtoValue = (value: unknown, codec?: FieldCodec): Value => { + if (codec?.conflictStrategy === "crdt" && codec.fieldType) { + return jsToProtoValue( + encodeAutomergeFieldWrite(value, codec.fieldType), + ); + } if (value === null || value === undefined) { return create(ValueSchema, { kind: { @@ -551,14 +585,17 @@ export const jsToProtoValue = (value: unknown): Value => { kind: { case: "listValue", value: create(ListValueSchema, { - values: value.map(jsToProtoValue), + values: value.map((entry) => jsToProtoValue(entry)), }), }, }); } if (isRecord(value)) { if (Object.hasOwn(value, "$caminoValue") && isRecord(value.$caminoSource)) { - return withSource(jsToProtoValue(value.$caminoValue), value.$caminoSource); + return withSource( + jsToProtoValue(value.$caminoValue, codecFromSource(value.$caminoSource)), + value.$caminoSource, + ); } if (typeof value.$caminoRef === "string") { return create(ValueSchema, { @@ -631,11 +668,12 @@ export const protoValueToJs = (value: Value | undefined): unknown => { case "refValue": return value.kind.value.objectId; case "crdtValue": - return { + return loadAutomergeFieldDoc({ $caminoCrdtType: value.kind.value.type, - $caminoCrdtEncoding: value.kind.value.encoding, + $caminoCrdtEncoding: + value.kind.value.encoding === "base64" ? "base64" : "base64", $caminoCrdtPayload: bytesToBase64(value.kind.value.payload), - }; + }, value.kind.value.type); } }; @@ -761,6 +799,7 @@ export const createField = ( camino: CaminoClient, objectId: string, fieldName: string, + codec?: FieldCodec, ): Field => ({ async get() { recordDerivedDependency({ kind: "field", objectId, fieldName }); @@ -771,11 +810,44 @@ export const createField = ( await camino.setField({ objectId, fieldName, - value: jsToProtoValue(value), + value: jsToProtoValue(value, codec), }); }, }); +export const createCrdtField = ( + camino: CaminoClient, + objectId: string, + fieldName: string, + fieldType: string, +): CrdtField => { + const field = createField>(camino, objectId, fieldName, { + fieldType, + conflictStrategy: "crdt", + }); + return { + ...field, + async change(fn) { + const current = await field.get(); + if (!current) { + throw new Error(`Cannot change missing CRDT field ${fieldName}`); + } + const next = Automerge.change(current, (doc) => { + fn(doc as { value: T }); + }) as CaminoAutomergeDoc; + await field.set(next); + return next; + }, + async replaceFromJson(value) { + await camino.setField({ + objectId, + fieldName, + value: jsToProtoValue(value, { fieldType, conflictStrategy: "crdt" }), + }); + }, + }; +}; + const createDependencyTrackingCaminoClient = ( camino: CaminoClient, ): CaminoClient => diff --git a/yarn-project.nix b/yarn-project.nix index f09f13d..ec9f010 100644 --- a/yarn-project.nix +++ b/yarn-project.nix @@ -156,11 +156,13 @@ let overriddenProject = optionalOverride overrideAttrs project; cacheEntries = { +"@automerge/automerge@npm:3.3.0" = { filename = "@automerge-automerge-npm-3.3.0-3f6661ca17-74b3a8ef9e.zip"; hash = "sha512-dLOo7548v0B2DomHJ+za1RKOUv0rBTgJRUrnsrhbTsZHw8uipjXNv4MfIxVzGtQl2jenhLeb8TvIbsKKfpKv8g=="; }; "@bufbuild/protobuf@npm:2.12.1" = { filename = "@bufbuild-protobuf-npm-2.12.1-d8afb467b3-2d40cfe4ec.zip"; hash = "sha512-LUDP5Ow7sOPwarLih3HcXcVm6qywVfvok9dWIbBAfQJy1I9ApJCtR97YOe1UFRThr3G7E68CSzrjbvDFbVbdqQ=="; }; "@bufbuild/protoc-gen-es@npm:2.12.1" = { filename = "@bufbuild-protoc-gen-es-npm-2.12.1-6e9387fcb8-0e2805376d.zip"; hash = "sha512-DigFN22Lmj/OD9OdkPZa4dH/vqMSvi2tk2f3xyEqvFHcKleC531iqPhh7De9xtG9MM+sVRDRJCeCzXA/kH4f9Q=="; }; "@bufbuild/protoplugin@npm:2.12.1" = { filename = "@bufbuild-protoplugin-npm-2.12.1-e1aebb80bf-c7b401ab31.zip"; hash = "sha512-x7QBqzHZSsaCXLm75c/3XQXSjuINRnskt6XUKZuaygZmhuo5t6OoRTuLWgGD+qd4HoP8LliAZhgHudMVX3Hr6Q=="; }; "@connectrpc/connect-node@npm:2.1.2" = { filename = "@connectrpc-connect-node-npm-2.1.2-021434d5d5-ba8b0c9384.zip"; hash = "sha512-uosMk4TDgps3eqeoJtwM1Qpgk1J9LscBRgSRKDYzbqlgSitazqQrEEd/i+RWnknd7WbKD8d/X1sCcDXgKeE+qQ=="; }; "@connectrpc/connect@npm:2.1.2" = { filename = "@connectrpc-connect-npm-2.1.2-ab34825de3-4e15000890.zip"; hash = "sha512-ThUACJAjt1Lv1E+pQKWuHh+gDhvrxLoM7ScruTWSDYw7hbd0UOZErUQruMqaJ1sMFxNviWuo7/A2F4Ex2wHWFQ=="; }; +"@quixos/camino-datatypes@https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-datatypes.git#commit=fa50782559944b88c410e264acadf79266b70b43" = { filename = "@quixos-camino-datatypes-https-08035afa77-0223fd49ac.zip"; hash = "sha512-AiP9SawAVbzD/srTymZRhXZH01djfCteKuHWkkk2wBr66OPF0AaUy3stZyxwEgJrRh18cFcpTc4zcjWsu7mGAg=="; }; "@types/node@npm:24.13.3" = { filename = "@types-node-npm-24.13.3-b512a0bbeb-a5bc08f49b.zip"; hash = "sha512-pbwI9JuVgdzcqQ4CzXcZejx5mECAdmSULlzVFhpVPU0q6AZPfjKUQQ10jX0Ji1wYSL5/pQwG8pxBk6sWvk5Z6w=="; }; "@typescript/vfs@npm:1.6.4" = { filename = "@typescript-vfs-npm-1.6.4-78935c9d3e-acb9de42f2.zip"; hash = "sha512-rLneQvI/2j9149eQC6EG7zI6L0588OSpTbtFfibTU8pjuzUZPtGjL8hzPzrlkJlhKykLBr1iIWlIYb65v7YvYg=="; }; "debug@npm:4.4.3" = { filename = "debug-npm-4.4.3-0105c6123a-d79136ec6c.zip"; hash = "sha512-15E27GyD7L79D2pVk9pqnJHsTX3cS1TIg9bnHsmsy19noaXpbQCjKBlrW1yG02XpjYo6cIVqrxa057GYXmf1pg=="; }; diff --git a/yarn.lock b/yarn.lock index 855060c..13bc8a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,6 +5,13 @@ __metadata: version: 9 cacheKey: 10c0 +"@automerge/automerge@npm:^3.3.0": + version: 3.3.0 + resolution: "@automerge/automerge@npm:3.3.0" + checksum: 10c0/74b3a8ef9e3cbf40760e898727ecdad5128e52fd2b053809454ae7b2b85b4ec647c3cba2a635cdbf831f2315731ad425da37a784b79bf13bc86ec28a7e92aff2 + languageName: node + linkType: hard + "@bufbuild/protobuf@npm:2.12.1, @bufbuild/protobuf@npm:^2.12.1": version: 2.12.1 resolution: "@bufbuild/protobuf@npm:2.12.1" @@ -59,14 +66,25 @@ __metadata: languageName: node linkType: hard +"@quixos/camino-datatypes@https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-datatypes.git#commit=fa50782559944b88c410e264acadf79266b70b43": + version: 0.1.0 + resolution: "@quixos/camino-datatypes@https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-datatypes.git#commit=fa50782559944b88c410e264acadf79266b70b43" + dependencies: + "@automerge/automerge": "npm:^3.3.0" + checksum: 10c0/0223fd49ac0055bcc3fecad3ca6651857647d357637c2b5e2ae1d6924936c01afae8e3c5d00694cb7b2d672c7012026b461d7c7057294dce337235acbbb98602 + languageName: node + linkType: hard + "@quixos/camino-package-runtime@workspace:.": version: 0.0.0-use.local resolution: "@quixos/camino-package-runtime@workspace:." dependencies: + "@automerge/automerge": "npm:^3.3.0" "@bufbuild/protobuf": "npm:^2.12.1" "@bufbuild/protoc-gen-es": "npm:^2.12.1" "@connectrpc/connect": "npm:^2.1.2" "@connectrpc/connect-node": "npm:^2.1.2" + "@quixos/camino-datatypes": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-datatypes.git#commit=fa50782559944b88c410e264acadf79266b70b43" "@types/node": "npm:^24" typescript: "npm:^5.9.3" languageName: unknown