This commit is contained in:
Andras Schmelczer 2026-09-27 08:29:17 +01:00
commit 3dab48f58b
3 changed files with 1141 additions and 1376 deletions

View file

@ -211,8 +211,9 @@ docker build -f local-client-cli/Dockerfile -t vault-link-cli:test .
The complete client record (database, settings, queued logical notifications and
server history checkpoint) is saved atomically in `.vaultlink/sync-data.json`.
Existing database-only files migrate on the next save. Invalid or unreadable
metadata is reported instead of silently starting with empty state.
Only this complete-record format is supported; database-only files are not
converted. Invalid or unreadable JSON is reported instead of silently starting
with empty state.
File moves use an exclusive copy followed by deletion of the source. An
interruption may leave both files; the next scan reconciles the visible files.

View file

@ -6,7 +6,7 @@ import * as path from "node:path";
import { ClientPersistence } from "./client-persistence";
import type { StoredClient } from "sync-client";
test("CLI persistence retains the whole record and migrates database-only saves", async (t) => {
test("CLI persistence retains the whole record and reports unreadable metadata", async (t) => {
const directory = await fs.mkdtemp(
path.join(tmpdir(), "vaultlink-metadata-")
);
@ -18,7 +18,13 @@ test("CLI persistence retains the whole record and migrates database-only saves"
});
const record: StoredClient = {
settings: { token: "old-token", vaultName: "vault" },
database: { lastSeenUpdateId: 12 },
database: {
vaultKey: "vault",
fileManifest: { fileManifestId: 0, entries: {} },
local: {},
documents: {},
lastSeenUpdateId: 12
},
localChanges: [
{ type: "delete", path: "removed.md", changeId: "queued" }
],
@ -30,12 +36,6 @@ test("CLI persistence retains the whole record and migrates database-only saves"
...record,
settings: { ...record.settings, token: "current-token" }
});
const legacy = {
fileManifest: { fileManifestId: 0, entries: {} },
lastSeenUpdateId: 0
};
await fs.writeFile(file, JSON.stringify(legacy));
assert.deepEqual((await store.load()).database, legacy);
await fs.writeFile(file, "{truncated");
await assert.rejects(store.load(), SyntaxError);
await fs.rm(file);

View file

@ -4,7 +4,6 @@ import { randomUUID } from "node:crypto";
import type {
PersistenceProvider,
StoredClient,
StoredDatabase,
SyncSettings
} from "sync-client";
@ -35,11 +34,7 @@ export class ClientPersistence implements PersistenceProvider<StoredClient> {
Array.isArray(parsed)
)
throw new Error("Invalid client metadata");
// The sync engine validates and migrates the persisted fields.
const data = parsed as StoredClient & Partial<StoredDatabase>;
// Earlier CLI versions saved the database alone.
const stored: StoredClient =
"fileManifest" in data ? { database: data } : data;
const stored = parsed as StoredClient;
return {
...stored,
settings: { ...stored.settings, ...this.settings }