.
This commit is contained in:
parent
25f64ec690
commit
ff1f474324
1 changed files with 3271 additions and 2003 deletions
|
|
@ -22,13 +22,17 @@ export interface DocumentState {
|
|||
|
||||
export type { PendingRequest } from "./requests";
|
||||
|
||||
export interface EngineState {
|
||||
export interface StoredDatabase {
|
||||
vaultKey: string;
|
||||
/** Last incorporated server manifest; the base for the next three-way path merge. */
|
||||
fileManifest: FileManifest;
|
||||
/** Current local document IDs and paths, including files awaiting upload or download. */
|
||||
local: FileManifestEntries;
|
||||
/** Per-document merge bases and observations; content bytes are not stored here. */
|
||||
documents: Record<DocumentId, DocumentState>;
|
||||
/** Absent until a vault snapshot is applied; also marks a durable history reset. */
|
||||
lastSeenUpdateId?: VaultUpdateId;
|
||||
/** Exact request to resolve before observing a newer remote base. */
|
||||
pending?: PendingRequest;
|
||||
/** Commits consumption of the saved notification queue with the identity map. */
|
||||
lastAppliedLocalChangeId?: string;
|
||||
|
|
@ -36,9 +40,7 @@ export interface EngineState {
|
|||
excluded?: Record<DocumentId, RelativePath | null>;
|
||||
}
|
||||
|
||||
export type StoredDatabase = EngineState;
|
||||
|
||||
export function emptyState(vaultKey: string): StoredDatabase {
|
||||
export function createEmptyDatabase(vaultKey: string): StoredDatabase {
|
||||
return {
|
||||
vaultKey,
|
||||
fileManifest: { fileManifestId: 0, entries: {} },
|
||||
|
|
@ -47,7 +49,7 @@ export function emptyState(vaultKey: string): StoredDatabase {
|
|||
};
|
||||
}
|
||||
|
||||
function canRebindEmpty(state: StoredDatabase): boolean {
|
||||
function canBindToAnotherVault(state: StoredDatabase): boolean {
|
||||
return (
|
||||
state.lastSeenUpdateId === undefined &&
|
||||
!state.pending &&
|
||||
|
|
@ -66,32 +68,44 @@ export class Database {
|
|||
) {
|
||||
const state =
|
||||
initial === undefined
|
||||
? emptyState(vaultKey)
|
||||
? createEmptyDatabase(vaultKey)
|
||||
: structuredClone(initial);
|
||||
if (state.vaultKey !== vaultKey && !canRebindEmpty(state)) {
|
||||
if (state.vaultKey !== vaultKey && !canBindToAnotherVault(state)) {
|
||||
throw new Error(
|
||||
"Incompatible vault identity. Preserve it for recovery and initialize a separate state store."
|
||||
);
|
||||
}
|
||||
|
||||
this.state = { ...state, vaultKey };
|
||||
}
|
||||
|
||||
public get length(): number {
|
||||
return Object.keys(this.state.local).length;
|
||||
}
|
||||
|
||||
/** Bind an empty database to the configured vault. */
|
||||
public async bindVault(vaultKey: string): Promise<void> {
|
||||
if (this.state.vaultKey === vaultKey) return;
|
||||
if (!canRebindEmpty(this.state))
|
||||
if (this.state.vaultKey === vaultKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!canBindToAnotherVault(this.state)) {
|
||||
throw new Error(
|
||||
"Incompatible vault identity. Preserve it for recovery and initialize a separate state store."
|
||||
);
|
||||
}
|
||||
|
||||
await this.commit({ ...this.state, vaultKey });
|
||||
}
|
||||
|
||||
public async commit(next: StoredDatabase): Promise<void> {
|
||||
const snapshot = structuredClone(next);
|
||||
if (this.needsReload)
|
||||
if (this.needsReload) {
|
||||
throw new Error(
|
||||
"Reload saved state before retrying an uncertain save"
|
||||
);
|
||||
}
|
||||
|
||||
this.needsReload = true;
|
||||
await this.saveData(snapshot);
|
||||
this.state = snapshot;
|
||||
|
|
@ -99,20 +113,26 @@ export class Database {
|
|||
}
|
||||
|
||||
public async recoverPersistence(): Promise<void> {
|
||||
if (!this.needsReload) return;
|
||||
if (!this.needsReload) {
|
||||
return;
|
||||
}
|
||||
|
||||
const saved = await this.loadData();
|
||||
if (saved) this.state = structuredClone(saved);
|
||||
if (saved) {
|
||||
this.state = structuredClone(saved);
|
||||
}
|
||||
|
||||
this.needsReload = false;
|
||||
}
|
||||
|
||||
public cloneState(): StoredDatabase {
|
||||
return structuredClone(this.state);
|
||||
}
|
||||
|
||||
public async save(): Promise<void> {
|
||||
await this.commit(this.state);
|
||||
}
|
||||
|
||||
public get length(): number {
|
||||
return Object.keys(this.state.local).length;
|
||||
}
|
||||
|
||||
public findDocumentId(path: RelativePath): DocumentId | undefined {
|
||||
return Object.keys(this.state.local).find(
|
||||
(id) => this.state.local[id] === path
|
||||
|
|
|
|||
Loading…
Reference in a new issue