84 lines
3.0 KiB
JavaScript
84 lines
3.0 KiB
JavaScript
import * as Automerge from "@automerge/automerge";
|
|
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) &&
|
|
typeof value.$caminoCrdtType ===
|
|
"string" &&
|
|
typeof value.$caminoCrdtPayload ===
|
|
"string" &&
|
|
(value.$caminoCrdtEncoding ===
|
|
undefined ||
|
|
value.$caminoCrdtEncoding ===
|
|
"base64");
|
|
const assertEnvelopeType = (value, type) => {
|
|
if (value.$caminoCrdtType !== type) {
|
|
throw new Error(`CRDT type mismatch: expected ${type}, got ${value.$caminoCrdtType}`);
|
|
}
|
|
};
|
|
export const loadAutomergeFieldDoc = (value, type) => {
|
|
assertEnvelopeType(value, type);
|
|
return Automerge.load(base64ToBytes(value.$caminoCrdtPayload));
|
|
};
|
|
export const saveAutomergeFieldDoc = (doc, type) => ({
|
|
$caminoCrdtType: type,
|
|
$caminoCrdtEncoding: "base64",
|
|
$caminoCrdtPayload: bytesToBase64(Automerge.save(doc)),
|
|
});
|
|
export const createAutomergeFieldValue = (value, type) => saveAutomergeFieldDoc(Automerge.from({ 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);
|
|
}
|
|
throw new Error("CRDT field writes must use an Automerge document or Camino CRDT envelope");
|
|
};
|
|
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);
|
|
const mergedDoc = existing === undefined || existing === null
|
|
? incomingDoc
|
|
: Automerge.merge(loadAutomergeFieldDoc(normalizeAutomergeFieldValue(existing, type), type), incomingDoc);
|
|
const stored = saveAutomergeFieldDoc(mergedDoc, type);
|
|
return {
|
|
stored,
|
|
materialized: mergedDoc.value,
|
|
};
|
|
};
|
|
//# sourceMappingURL=crdt-automerge.js.map
|