♻️ refactor: extract jsonFile helper for file-backed JSON stores

This commit is contained in:
2026-06-29 15:29:09 +07:00
parent a18d9612ee
commit d54972c84c
5 changed files with 47 additions and 49 deletions
+5 -13
View File
@@ -1,7 +1,7 @@
import { app } from "electron";
import { join } from "node:path";
import { copyFileSync, existsSync, readFileSync, rmSync } from "node:fs";
import { writeFileAtomicSync } from "../lib/atomicFile";
import { copyFileSync, existsSync, rmSync } from "node:fs";
import { jsonFile } from "../lib/jsonFile";
import type { Account, AccountsState } from "../../shared/types/auth";
const dir = () => join(app.getPath("userData"), "sessions");
@@ -15,17 +15,9 @@ interface Registry {
accounts: Account[];
}
function read(): Registry {
try {
return JSON.parse(readFileSync(registryPath(), "utf8")) as Registry;
} catch {
return { active: null, accounts: [] };
}
}
function write(reg: Registry): void {
writeFileAtomicSync(registryPath(), JSON.stringify(reg, null, 2));
}
const registry = jsonFile<Registry>(registryPath, () => ({ active: null, accounts: [] }));
const read = registry.read;
const write = registry.write;
export function listAccounts(): AccountsState {
const reg = read();
+13 -10
View File
@@ -1,7 +1,7 @@
import { app } from "electron";
import { join } from "node:path";
import { existsSync, readFileSync } from "node:fs";
import { writeFileAtomicSync } from "../lib/atomicFile";
import { existsSync } from "node:fs";
import { jsonFile } from "../lib/jsonFile";
import type { AppConfig, PreferredRegion } from "../../shared/types/appConfig";
import { detectedGamePath } from "../game/steam";
@@ -10,12 +10,15 @@ const path = () => join(app.getPath("userData"), "app-config.json");
type StoredConfig = Omit<AppConfig, "version" | "detectedGamePath">;
const DEFAULTS: StoredConfig = { gamePath: null, preferredRegion: "auto" };
function readStored(): StoredConfig {
try {
return { ...DEFAULTS, ...(JSON.parse(readFileSync(path(), "utf8")) as Partial<StoredConfig>) };
} catch {
return { ...DEFAULTS };
}
const configFile = jsonFile<StoredConfig>(
path,
() => ({ ...DEFAULTS }),
(raw) => ({ ...DEFAULTS, ...(raw as Partial<StoredConfig>) }),
);
const readStored = configFile.read;
function writeStored(next: StoredConfig): void {
configFile.write(next);
}
function withDerived(stored: StoredConfig): AppConfig {
@@ -32,7 +35,7 @@ export function preferredRegion(): PreferredRegion {
export function setPreferredRegion(region: PreferredRegion): AppConfig {
const next: StoredConfig = { ...readStored(), preferredRegion: region };
writeFileAtomicSync(path(), JSON.stringify(next, null, 2));
writeStored(next);
return withDerived(next);
}
@@ -46,6 +49,6 @@ export function setGamePath(gamePath: string | null): AppConfig {
throw new Error("That path doesn't exist on disk.");
}
const next: StoredConfig = { ...readStored(), gamePath: trimmed };
writeFileAtomicSync(path(), JSON.stringify(next, null, 2));
writeStored(next);
return withDerived(next);
}
+4 -13
View File
@@ -1,7 +1,6 @@
import { app } from "electron";
import { join } from "node:path";
import { readFileSync } from "node:fs";
import { writeFileAtomicSync } from "../lib/atomicFile";
import { jsonFile } from "../lib/jsonFile";
import { logger } from "../debug/logger";
import type {
EnhancementId,
@@ -17,17 +16,9 @@ const storePath = () => join(app.getPath("userData"), "enhancements.json");
type Prefs = Partial<Record<EnhancementId, boolean>>;
function readPrefs(): Prefs {
try {
return JSON.parse(readFileSync(storePath(), "utf8")) as Prefs;
} catch {
return {};
}
}
function writePrefs(prefs: Prefs): void {
writeFileAtomicSync(storePath(), JSON.stringify(prefs, null, 2));
}
const prefsFile = jsonFile<Prefs>(storePath, () => ({}));
const readPrefs = prefsFile.read;
const writePrefs = prefsFile.write;
function screenshotState(): EnhancementState {
const s = screenshot.status();
+21
View File
@@ -0,0 +1,21 @@
import { readFileSync } from "node:fs";
import { writeFileAtomicSync } from "./atomicFile";
export function jsonFile<T>(
path: () => string,
fallback: () => T,
parse: (raw: unknown) => T = (raw) => raw as T,
) {
return {
read(): T {
try {
return parse(JSON.parse(readFileSync(path(), "utf8")));
} catch {
return fallback();
}
},
write(value: T): void {
writeFileAtomicSync(path(), JSON.stringify(value, null, 2));
},
};
}
+4 -13
View File
@@ -1,19 +1,13 @@
import { app, screen, type BrowserWindow, type Rectangle } from "electron";
import { join } from "node:path";
import { readFileSync } from "node:fs";
import { writeFileAtomicSync } from "./lib/atomicFile";
import { jsonFile } from "./lib/jsonFile";
type Bounds = Rectangle & { maximized: boolean };
const path = () => join(app.getPath("userData"), "window-state.json");
function read(): Partial<Bounds> | null {
try {
return JSON.parse(readFileSync(path(), "utf8")) as Partial<Bounds>;
} catch {
return null;
}
}
const stateFile = jsonFile<Partial<Bounds> | null>(path, () => null);
const read = stateFile.read;
function onScreen(b: Rectangle): boolean {
return screen.getAllDisplays().some((d) => {
@@ -47,10 +41,7 @@ export function trackBounds(win: BrowserWindow): void {
const save = (): void => {
if (win.isDestroyed()) return;
writeFileAtomicSync(
path(),
JSON.stringify({ ...normalBounds, maximized: win.isMaximized() }, null, 2),
);
stateFile.write({ ...normalBounds, maximized: win.isMaximized() });
};
const remember = (): void => {