power loss
This commit is contained in:
parent
0f6b618c0b
commit
d56084e4bc
1 changed files with 18667 additions and 3902 deletions
82
frontend/sync-client/src/sync-operations/local-changes.ts
Normal file
82
frontend/sync-client/src/sync-operations/local-changes.ts
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
import { v4 as uuid } from "uuid";
|
||||
import type { EngineState } from "../persistence/database";
|
||||
import type { VaultSnapshot } from "../services/types/VaultSnapshot";
|
||||
import { arePathAliases } from "../utils/portable-path";
|
||||
|
||||
export type LocalChange = {
|
||||
changeId?: string;
|
||||
/** Identities at notification time, before a remote application can reuse paths. */
|
||||
identities?: Record<string, string>;
|
||||
} & (
|
||||
| { type: "move"; oldPath: string; relativePath: string }
|
||||
| { type: "create"; path: string; documentId?: string }
|
||||
| { type: "delete"; path: string }
|
||||
| { type: "update"; path: string }
|
||||
);
|
||||
|
||||
export function unappliedChanges(
|
||||
state: EngineState,
|
||||
changes: readonly LocalChange[]
|
||||
): LocalChange[] {
|
||||
for (const change of changes) change.changeId ??= uuid();
|
||||
const applied = changes.findIndex(
|
||||
(change) => change.changeId === state.lastAppliedLocalChangeId
|
||||
);
|
||||
return changes.slice(applied + 1);
|
||||
}
|
||||
|
||||
export function applyLocalChange(
|
||||
next: EngineState,
|
||||
change: LocalChange,
|
||||
ignored: (path: string) => boolean,
|
||||
initial?: VaultSnapshot
|
||||
): void {
|
||||
if (change.type === "create") {
|
||||
if (
|
||||
ignored(change.path) ||
|
||||
Object.values(next.local).includes(change.path)
|
||||
)
|
||||
return;
|
||||
const id = (change.documentId ??=
|
||||
(initial &&
|
||||
Object.keys(initial.fileManifest.entries).find(
|
||||
(id) =>
|
||||
!next.local[id] &&
|
||||
arePathAliases(
|
||||
initial.fileManifest.entries[id],
|
||||
change.path
|
||||
)
|
||||
)) ||
|
||||
uuid());
|
||||
next.local[id] = change.path;
|
||||
next.documents[id] ??= {
|
||||
materialized: true,
|
||||
bootstrap: initial?.fileManifest.entries[id] !== undefined
|
||||
};
|
||||
return;
|
||||
}
|
||||
const from = change.type === "move" ? change.oldPath : change.path;
|
||||
change.identities ??= Object.fromEntries(
|
||||
Object.entries(next.local).filter(
|
||||
([id, path]) =>
|
||||
next.documents[id]?.materialized &&
|
||||
(path === from || path.startsWith(from + "/"))
|
||||
)
|
||||
);
|
||||
if (change.type === "update") return;
|
||||
for (const [id, path] of Object.entries(change.identities)) {
|
||||
if (change.type === "delete") {
|
||||
if (ignored(path)) {
|
||||
next.excluded ??= {};
|
||||
if (next.excluded[id] === undefined)
|
||||
next.excluded[id] = next.fileManifest.entries[id] ?? null;
|
||||
}
|
||||
delete next.local[id];
|
||||
} else {
|
||||
const to = change.relativePath + path.slice(from.length);
|
||||
for (const [other, otherPath] of Object.entries(next.local))
|
||||
if (other !== id && otherPath === to) delete next.local[other];
|
||||
next.local[id] = to;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue