Encode Automerge docs for Camino CRDT writes

This commit is contained in:
Timothy J. Aveni
2026-07-12 20:15:51 -07:00
parent 864f8f5de6
commit e72b3c5577
5 changed files with 112 additions and 8 deletions
+38 -3
View File
@@ -1,6 +1,25 @@
import * as Automerge from "@automerge/automerge";
const bytesToBase64 = (value) => Buffer.from(value).toString("base64");
const base64ToBytes = (value) => Buffer.from(value, "base64");
const bytesToBase64 = (value) => {
if (typeof Buffer !== "undefined") {
return Buffer.from(value).toString("base64");
}
let binary = "";
for (const byte of value) {
binary += String.fromCharCode(byte);
}
return btoa(binary);
};
const base64ToBytes = (value) => {
if (typeof Buffer !== "undefined") {
return Buffer.from(value, "base64");
}
const binary = atob(value);
const bytes = new Uint8Array(binary.length);
for (let index = 0; index < binary.length; index += 1) {
bytes[index] = binary.charCodeAt(index);
}
return bytes;
};
export const isCaminoCrdtEnvelope = (value) => Boolean(value) &&
typeof value === "object" &&
!Array.isArray(value) &&
@@ -27,12 +46,28 @@ export const saveAutomergeFieldDoc = (doc, type) => ({
$caminoCrdtPayload: bytesToBase64(Automerge.save(doc)),
});
export const createAutomergeFieldValue = (value, type) => saveAutomergeFieldDoc(Automerge.from({ value }), type);
export const normalizeAutomergeFieldValue = (value, type) => {
export const isAutomergeFieldDoc = (value) => {
if (!value || typeof value !== "object") {
return false;
}
try {
Automerge.getHeads(value);
return true;
}
catch {
return false;
}
};
export const encodeAutomergeFieldWrite = (value, type) => {
if (isCaminoCrdtEnvelope(value)) {
return saveAutomergeFieldDoc(loadAutomergeFieldDoc(value, type), type);
}
if (isAutomergeFieldDoc(value)) {
return saveAutomergeFieldDoc(value, type);
}
return createAutomergeFieldValue(value, type);
};
export const normalizeAutomergeFieldValue = (value, type) => encodeAutomergeFieldWrite(value, type);
export const materializeAutomergeFieldValue = (value, type) => loadAutomergeFieldDoc(value, type).value;
export const mergeAutomergeFieldValues = ({ existing, incoming, type, }) => {
const incomingDoc = loadAutomergeFieldDoc(normalizeAutomergeFieldValue(incoming, type), type);