From 1a4f3ab8ccce7f76dc4e3e5f51cf385cca700529 Mon Sep 17 00:00:00 2001 From: "Timothy J. Aveni" Date: Thu, 3 Sep 2026 09:39:22 -0700 Subject: [PATCH] Rebase pending Automerge field edits --- dist/src/crdt-automerge.d.ts | 3 ++- dist/src/crdt-automerge.js | 14 +++++++++----- dist/src/crdt-automerge.js.map | 2 +- src/crdt-automerge.ts | 25 +++++++++++++++++-------- test/crdt-automerge.test.ts | 27 +++++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 15 deletions(-) diff --git a/dist/src/crdt-automerge.d.ts b/dist/src/crdt-automerge.d.ts index 3a030ed..6f615d5 100644 --- a/dist/src/crdt-automerge.d.ts +++ b/dist/src/crdt-automerge.d.ts @@ -18,7 +18,8 @@ export declare const loadAutomergeFieldDoc: (value: CaminoCrdtEnvelope, type: st export declare const saveAutomergeFieldDoc: (doc: Automerge.Doc, type: string) => CaminoCrdtEnvelope; export declare const createAutomergeFieldValue: (value: unknown, type: string) => CaminoCrdtEnvelope; export declare const isAutomergeFieldDoc: (value: unknown) => value is Automerge.Doc; -export declare const createAutomergeFieldChanges: (base: Automerge.Doc, next: Automerge.Doc, type: string) => CaminoCrdtEnvelope; +export declare const reconcileAutomergeFieldDocs: (base: CaminoAutomergeDoc, desired: CaminoAutomergeDoc) => CaminoAutomergeDoc; +export declare const createAutomergeFieldChanges: (base: Automerge.Doc, desired: Automerge.Doc, type: string) => CaminoCrdtEnvelope; export declare const encodeAutomergeFieldWrite: (value: unknown, type: string) => CaminoCrdtEnvelope; export declare const normalizeAutomergeFieldValue: (value: unknown, type: string) => CaminoCrdtEnvelope; export declare const materializeAutomergeFieldValue: (value: CaminoCrdtEnvelope, type: string) => unknown; diff --git a/dist/src/crdt-automerge.js b/dist/src/crdt-automerge.js index 495dde4..477bd3c 100644 --- a/dist/src/crdt-automerge.js +++ b/dist/src/crdt-automerge.js @@ -107,11 +107,15 @@ const decodeChanges = (payload) => { } return changes; }; -export const createAutomergeFieldChanges = (base, next, type) => ({ - $caminoCrdtType: type, - $caminoCrdtEncoding: AUTOMERGE_CHANGES_ENCODING, - $caminoCrdtPayload: bytesToBase64(encodeChanges(Automerge.getChanges(base, next))), -}); +export const reconcileAutomergeFieldDocs = (base, desired) => Automerge.merge(Automerge.clone(base), Automerge.clone(desired)); +export const createAutomergeFieldChanges = (base, desired, type) => { + const next = reconcileAutomergeFieldDocs(base, desired); + return { + $caminoCrdtType: type, + $caminoCrdtEncoding: AUTOMERGE_CHANGES_ENCODING, + $caminoCrdtPayload: bytesToBase64(encodeChanges(Automerge.getChanges(base, next))), + }; +}; // Full snapshots remain an explicit escape hatch for initialization, import, // and recovery when a client cannot establish a shared Automerge history. export const encodeAutomergeFieldWrite = (value, type) => { diff --git a/dist/src/crdt-automerge.js.map b/dist/src/crdt-automerge.js.map index abc7652..ebded4f 100644 --- a/dist/src/crdt-automerge.js.map +++ b/dist/src/crdt-automerge.js.map @@ -1 +1 @@ -{"version":3,"file":"crdt-automerge.js","sourceRoot":"","sources":["../../src/crdt-automerge.ts"],"names":[],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"crdt-automerge.js","sourceRoot":"","sources":["../../src/crdt-automerge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAC;AAElD,MAAM,CAAC,MAAM,2BAA2B,GAAG,uBAAuB,CAAC;AACnE,MAAM,CAAC,MAAM,0BAA0B,GAAG,sBAAsB,CAAC;AAsBjE,MAAM,aAAa,GAAG,CAAC,KAAiB,EAAE,EAAE;IAC1C,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;QAClC,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC/C,CAAC;IACD,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;AACtB,CAAC,CAAC;AAEF,MAAM,aAAa,GAAG,CAAC,KAAa,EAAE,EAAE;IACtC,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;QAClC,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC;IACtD,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IAC3B,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACtD,KAAK,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF,MAAM,cAAc,GAAG,CAAC,KAAc,EAA+B,EAAE,CACrE,KAAK,KAAK,QAAQ;IAClB,KAAK,KAAK,2BAA2B;IACrC,KAAK,KAAK,0BAA0B,CAAC;AAEvC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAClC,KAAc,EACe,EAAE,CAC/B,OAAO,CAAC,KAAK,CAAC;IACd,OAAO,KAAK,KAAK,QAAQ;IACzB,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;IACrB,OAAQ,KAAuC,CAAC,eAAe;QAC7D,QAAQ;IACV,OAAQ,KAA0C,CAAC,kBAAkB;QACnE,QAAQ;IACV,cAAc,CACX,KAA2C,CAAC,mBAAmB;QAC9D,QAAQ,CACX,CAAC;AAEJ,MAAM,kBAAkB,GAAG,CAAC,KAAyB,EAAE,IAAY,EAAE,EAAE;IACrE,IAAI,KAAK,CAAC,eAAe,KAAK,IAAI,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CACb,gCAAgC,IAAI,SAAS,KAAK,CAAC,eAAe,EAAE,CACrE,CAAC;IACJ,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,uBAAuB,GAAG,CAAC,KAAyB,EAAE,IAAY,EAAE,EAAE;IAC1E,kBAAkB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAChC,IAAI,KAAK,CAAC,mBAAmB,KAAK,0BAA0B,EAAE,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,KAAyB,EACzB,IAAY,EACsB,EAAE;IACpC,MAAM,QAAQ,GAAG,uBAAuB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACtD,OAAO,SAAS,CAAC,IAAI,CACnB,aAAa,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAC3C,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACnC,GAAqC,EACrC,IAAY,EACQ,EAAE,CAAC,CAAC;IACxB,eAAe,EAAE,IAAI;IACrB,mBAAmB,EAAE,2BAA2B;IAChD,kBAAkB,EAAE,aAAa,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;CACvD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CACvC,KAAc,EACd,IAAY,EACQ,EAAE,CACtB,qBAAqB,CAAC,SAAS,CAAC,IAAI,CAAoB,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;AAE5E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,KAAc,EAC6B,EAAE;IAC7C,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,CAAC;QACH,SAAS,CAAC,QAAQ,CAAC,KAAyC,CAAC,CAAC;QAC9D,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,aAAa,GAAG,CAAC,OAA2B,EAAc,EAAE;IAChE,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACpF,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC1C,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QACtC,MAAM,IAAI,CAAC,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC5B,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC;IAC1B,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,aAAa,GAAG,CAAC,OAAmB,EAAsB,EAAE;IAChE,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC1E,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;IAClF,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAChC,MAAM,OAAO,GAAuB,EAAE,CAAC;IACvC,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC9C,IAAI,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;QAC3E,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;QACtC,MAAM,IAAI,CAAC,CAAC;QACZ,IAAI,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;QACtE,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC;QACrD,MAAM,IAAI,MAAM,CAAC;IACnB,CAAC;IACD,IAAI,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACzC,IAA2B,EAC3B,OAA8B,EACP,EAAE,CACzB,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;AAEnE,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACzC,IAAsC,EACtC,OAAyC,EACzC,IAAY,EACQ,EAAE;IACtB,MAAM,IAAI,GAAG,2BAA2B,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACxD,OAAO;QACL,eAAe,EAAE,IAAI;QACrB,mBAAmB,EAAE,0BAA0B;QAC/C,kBAAkB,EAAE,aAAa,CAC/B,aAAa,CAAC,SAAS,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAChD;KACF,CAAC;AACJ,CAAC,CAAC;AAEF,6EAA6E;AAC7E,0EAA0E;AAC1E,MAAM,CAAC,MAAM,yBAAyB,GAAG,CACvC,KAAc,EACd,IAAY,EACQ,EAAE;IACtB,IAAI,oBAAoB,CAAC,KAAK,CAAC,EAAE,CAAC;QAChC,OAAO,qBAAqB,CAAC,qBAAqB,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;IACzE,CAAC;IACD,IAAI,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/B,OAAO,qBAAqB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC;IACD,MAAM,IAAI,KAAK,CACb,0EAA0E,CAC3E,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAC1C,KAAc,EACd,IAAY,EACQ,EAAE,CAAC,yBAAyB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;AAEhE,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAC5C,KAAyB,EACzB,IAAY,EACZ,EAAE,CAAC,qBAAqB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC;AAa9C,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,EACxC,QAAQ,EACR,QAAQ,EACR,IAAI,GAC2B,EAAkC,EAAE;IACnE,MAAM,gBAAgB,GAAG,oBAAoB,CAAC,QAAQ,CAAC;QACrD,CAAC,CAAC,QAAQ;QACV,CAAC,CAAC,yBAAyB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC9C,kBAAkB,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;IAE3C,IAAI,SAA2C,CAAC;IAChD,IAAI,gBAAgB,CAAC,mBAAmB,KAAK,0BAA0B,EAAE,CAAC;QACxE,IAAI,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YAChD,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;QAC/E,CAAC;QACD,MAAM,WAAW,GAAG,qBAAqB,CACvC,yBAAyB,CAAC,QAAQ,EAAE,IAAI,CAAC,EACzC,IAAI,CACL,CAAC;QACF,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC,YAAY,CAClC,WAAW,EACX,aAAa,CAAC,aAAa,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,CAAC,CAClE,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,MAAM,WAAW,GAAG,qBAAqB,CAAC,gBAAgB,EAAE,IAAI,CAAC,CAAC;QAClE,SAAS;YACP,QAAQ,KAAK,SAAS,IAAI,QAAQ,KAAK,IAAI;gBACzC,CAAC,CAAC,WAAW;gBACb,CAAC,CAAC,SAAS,CAAC,KAAK,CACb,qBAAqB,CAAC,yBAAyB,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EACtE,WAAW,CACZ,CAAC;IACV,CAAC;IAED,MAAM,MAAM,GAAG,qBAAqB,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IACtD,OAAO;QACL,MAAM;QACN,YAAY,EAAE,SAAS,CAAC,KAAK;KAC9B,CAAC;AACJ,CAAC,CAAC"} \ No newline at end of file diff --git a/src/crdt-automerge.ts b/src/crdt-automerge.ts index bb4124b..30f1bf5 100644 --- a/src/crdt-automerge.ts +++ b/src/crdt-automerge.ts @@ -162,17 +162,26 @@ const decodeChanges = (payload: Uint8Array): Automerge.Change[] => { return changes; }; +export const reconcileAutomergeFieldDocs = ( + base: CaminoAutomergeDoc, + desired: CaminoAutomergeDoc, +): CaminoAutomergeDoc => + Automerge.merge(Automerge.clone(base), Automerge.clone(desired)); + export const createAutomergeFieldChanges = ( base: Automerge.Doc, - next: Automerge.Doc, + desired: Automerge.Doc, type: string, -): CaminoCrdtEnvelope => ({ - $caminoCrdtType: type, - $caminoCrdtEncoding: AUTOMERGE_CHANGES_ENCODING, - $caminoCrdtPayload: bytesToBase64( - encodeChanges(Automerge.getChanges(base, next)), - ), -}); +): CaminoCrdtEnvelope => { + const next = reconcileAutomergeFieldDocs(base, desired); + return { + $caminoCrdtType: type, + $caminoCrdtEncoding: AUTOMERGE_CHANGES_ENCODING, + $caminoCrdtPayload: bytesToBase64( + encodeChanges(Automerge.getChanges(base, next)), + ), + }; +}; // Full snapshots remain an explicit escape hatch for initialization, import, // and recovery when a client cannot establish a shared Automerge history. diff --git a/test/crdt-automerge.test.ts b/test/crdt-automerge.test.ts index 7d09e45..363dac3 100644 --- a/test/crdt-automerge.test.ts +++ b/test/crdt-automerge.test.ts @@ -11,6 +11,7 @@ import { loadAutomergeFieldDoc, materializeAutomergeFieldValue, mergeAutomergeFieldValues, + reconcileAutomergeFieldDocs, saveAutomergeFieldDoc, } from "../src/crdt-automerge.js"; @@ -105,6 +106,32 @@ test("Automerge change application is idempotent", () => { assert.deepEqual(twice.materialized, { count: 1 }); }); +test("Automerge changes retain remote history while local writes are pending", () => { + const stored = createAutomergeFieldValue({ local: false, remote: false }, type); + const base = loadAutomergeFieldDoc(stored, type); + const local = Automerge.change( + Automerge.clone(base, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + (doc) => { + (doc.value as { local: boolean }).local = true; + }, + ); + const remote = Automerge.change( + Automerge.clone(base, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), + (doc) => { + (doc.value as { remote: boolean }).remote = true; + }, + ); + const rebased = reconcileAutomergeFieldDocs(remote, local); + const update = createAutomergeFieldChanges(remote, rebased, type); + const result = mergeAutomergeFieldValues({ + existing: saveAutomergeFieldDoc(remote, type), + incoming: update, + type, + }); + + assert.deepEqual(result.materialized, { local: true, remote: true }); +}); + test("Automerge change batches reject malformed framing", () => { const stored = createAutomergeFieldValue({ count: 0 }, type); assert.throws(