simplify 3
This commit is contained in:
parent
82929d7479
commit
96d720eed9
1 changed files with 1318 additions and 995 deletions
67
frontend/local-client-cli/src/client-persistence.ts
Normal file
67
frontend/local-client-cli/src/client-persistence.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import * as fs from "node:fs/promises";
|
||||
import * as path from "node:path";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import type {
|
||||
PersistenceProvider,
|
||||
StoredClient,
|
||||
StoredDatabase,
|
||||
SyncSettings
|
||||
} from "sync-client";
|
||||
|
||||
/** Only client metadata needs an atomic replacement; user files do not. */
|
||||
export class ClientPersistence implements PersistenceProvider<StoredClient> {
|
||||
public constructor(
|
||||
private readonly file: string,
|
||||
private readonly settings: Partial<SyncSettings>
|
||||
) {}
|
||||
|
||||
public async load(): Promise<StoredClient> {
|
||||
const content = await fs
|
||||
.readFile(this.file, "utf8")
|
||||
.catch((error: unknown) => {
|
||||
if (
|
||||
error instanceof Error &&
|
||||
"code" in error &&
|
||||
error.code === "ENOENT"
|
||||
)
|
||||
return undefined;
|
||||
throw error;
|
||||
});
|
||||
if (content === undefined) return { settings: this.settings };
|
||||
const parsed: unknown = JSON.parse(content);
|
||||
if (
|
||||
parsed === null ||
|
||||
typeof parsed !== "object" ||
|
||||
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;
|
||||
return {
|
||||
...stored,
|
||||
settings: { ...stored.settings, ...this.settings }
|
||||
};
|
||||
}
|
||||
|
||||
public async save(data: StoredClient): Promise<void> {
|
||||
await fs.mkdir(path.dirname(this.file), { recursive: true });
|
||||
const temporary = `${this.file}.${randomUUID()}.tmp`;
|
||||
try {
|
||||
const handle = await fs.open(temporary, "wx", 0o600);
|
||||
try {
|
||||
await handle.writeFile(JSON.stringify(data));
|
||||
// Flush the complete value before exposing its name. A crash can
|
||||
// keep the previous value, but must not expose truncated JSON.
|
||||
await handle.sync();
|
||||
} finally {
|
||||
await handle.close();
|
||||
}
|
||||
await fs.rename(temporary, this.file);
|
||||
} finally {
|
||||
await fs.rm(temporary, { force: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue