Extract reconcile
This commit is contained in:
parent
c9512ab8b1
commit
f5fc71f388
1 changed files with 162 additions and 0 deletions
69
frontend/sync-client/src/sync-operations/content.ts
Normal file
69
frontend/sync-client/src/sync-operations/content.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import { base64ToBytes, bytesToBase64 } from "byte-base64";
|
||||
import { reconcile } from "reconcile-text";
|
||||
import type { FileSnapshot } from "../file-operations/filesystem-operations";
|
||||
import type { StoredSnapshot } from "../persistence/database";
|
||||
import { hash } from "../utils/hash";
|
||||
import { isBinary } from "../utils/is-binary";
|
||||
import { isFileTypeMergable } from "../utils/is-file-type-mergable";
|
||||
|
||||
export async function toStoredSnapshot(
|
||||
snapshot: FileSnapshot
|
||||
): Promise<StoredSnapshot> {
|
||||
return {
|
||||
contentBase64: bytesToBase64(snapshot.content),
|
||||
cursors: snapshot.cursors,
|
||||
hash: await hash(snapshot.content)
|
||||
};
|
||||
}
|
||||
|
||||
export function fromStoredSnapshot(snapshot: StoredSnapshot): FileSnapshot {
|
||||
return {
|
||||
content: base64ToBytes(snapshot.contentBase64),
|
||||
cursors: snapshot.cursors
|
||||
};
|
||||
}
|
||||
|
||||
export async function mergeContent(
|
||||
path: string,
|
||||
base: StoredSnapshot | undefined,
|
||||
local: StoredSnapshot,
|
||||
remote: StoredSnapshot,
|
||||
extensions: string[]
|
||||
): Promise<StoredSnapshot> {
|
||||
if (local.contentBase64 === remote.contentBase64)
|
||||
return {
|
||||
...remote,
|
||||
cursors: local.cursors
|
||||
};
|
||||
|
||||
if (!local.cursors && local.contentBase64 === base?.contentBase64)
|
||||
return remote;
|
||||
|
||||
if (remote.contentBase64 === base?.contentBase64) return local;
|
||||
|
||||
const inputs = [
|
||||
base?.contentBase64 ?? "",
|
||||
local.contentBase64,
|
||||
remote.contentBase64
|
||||
].map(base64ToBytes);
|
||||
|
||||
// Concurrent unmergeable edits keep the server's content. The filesystem
|
||||
// journal retains displaced local bytes for recovery.
|
||||
if (!isFileTypeMergable(path, extensions) || inputs.some(isBinary))
|
||||
return remote;
|
||||
|
||||
const [parentText, localText, remoteText] = inputs.map((bytes) =>
|
||||
new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes)
|
||||
);
|
||||
|
||||
const merged = reconcile(
|
||||
parentText,
|
||||
{ text: localText, cursors: local.cursors },
|
||||
remoteText
|
||||
);
|
||||
|
||||
return toStoredSnapshot({
|
||||
content: new TextEncoder().encode(merged.text),
|
||||
cursors: local.cursors ? merged.cursors : undefined
|
||||
});
|
||||
}
|
||||
Loading…
Reference in a new issue