diff --git a/PROJECT_STATE.md b/PROJECT_STATE.md index 6b913f0..063daef 100644 --- a/PROJECT_STATE.md +++ b/PROJECT_STATE.md @@ -115,7 +115,17 @@ --- -## H. Instructions for the Next AI Agent +## H. Important Decisions + +| Decision | Reason | Alternatives Rejected | +|---|---|---| +| Native JSON store (`JsonStore`) over `electron-store` | `electron-store` v10 is pure ESM causing `ERR_REQUIRE_ESM` in Electron CJS main bundle; native `fs` JSON store has 0 external dependencies and 100% synchronous stability | `electron-store` v10 (rejected: ESM runtime collision) | +| Atomic JSON store over `better-sqlite3` | Eliminates requirement for Visual Studio C++ compilers and node-gyp native compilation on target user PCs | `better-sqlite3` (rejected: requires C++ build tools) | +| electron-vite over vanilla Vite | Direct Electron HMR and optimized dual-bundle build pipeline | Custom webpack, Create React App | + +--- + +## I. Instructions for the Next AI Agent 1. **Read `PROJECT_STATE.md` completely before making changes.** 2. **Read `DESIGN.md` before modifying any UI.** Adhere strictly to the Axiom design system. diff --git a/electron/persistence/database.ts b/electron/persistence/database.ts index cff84aa..9e90d90 100644 --- a/electron/persistence/database.ts +++ b/electron/persistence/database.ts @@ -1,4 +1,4 @@ -import Store from 'electron-store' +import { JsonStore } from './json-store' import { createLogger } from '../logger' import type { Repository } from '../../shared/types/git.types' @@ -10,18 +10,14 @@ interface DatabaseSchema { graphLayout: Record } -let store: Store | null = null +let store: JsonStore | null = null -export function getDb(): Store { +export function getDb(): JsonStore { if (!store) { - store = new Store({ - name: 'flowgit-data', - defaults: { - repositories: [], - uiState: {}, - graphLayout: {}, - }, - clearInvalidConfig: true, + store = new JsonStore('flowgit-data', { + repositories: [], + uiState: {}, + graphLayout: {}, }) } return store @@ -47,7 +43,7 @@ export function getRepository(id: string): Repository | null { export function upsertRepository(repo: Omit): void { const db = getDb() - const repos = db.get('repositories') ?? [] + const repos = [...(db.get('repositories') ?? [])] const existingIndex = repos.findIndex(r => r.id === repo.id || r.path === repo.path) const fullRepo: Repository = { @@ -78,7 +74,7 @@ export function removeRepository(id: string): void { export function pinRepository(id: string, pinned: boolean): void { const db = getDb() - const repos = db.get('repositories') ?? [] + const repos = [...(db.get('repositories') ?? [])] const repo = repos.find(r => r.id === id) if (repo) { repo.isPinned = pinned @@ -95,7 +91,7 @@ export function getUiState(key: string): string | null { export function setUiState(key: string, value: string): void { const db = getDb() - const state = db.get('uiState') ?? {} + const state = { ...(db.get('uiState') ?? {}) } state[key] = value db.set('uiState', state) } diff --git a/electron/persistence/json-store.ts b/electron/persistence/json-store.ts new file mode 100644 index 0000000..9211fb0 --- /dev/null +++ b/electron/persistence/json-store.ts @@ -0,0 +1,52 @@ +import { app } from 'electron' +import { join } from 'path' +import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs' + +export class JsonStore { + private filePath: string + private data: T + + constructor(name: string, defaults: T) { + let dir = '' + try { + dir = app.getPath('userData') + } catch { + dir = join(process.cwd(), '.flowgit-data') + } + mkdirSync(dir, { recursive: true }) + this.filePath = join(dir, `${name}.json`) + + if (existsSync(this.filePath)) { + try { + const raw = readFileSync(this.filePath, 'utf8') + this.data = { ...defaults, ...JSON.parse(raw) } + } catch { + this.data = { ...defaults } + } + } else { + this.data = { ...defaults } + this.save() + } + } + + get(key: K): T[K] { + return this.data[key] + } + + set(key: K, value: T[K]): void { + this.data[key] = value + this.save() + } + + getAll(): T { + return { ...this.data } + } + + private save(): void { + try { + writeFileSync(this.filePath, JSON.stringify(this.data, null, 2), 'utf8') + } catch (err) { + console.error(`Failed to write store to ${this.filePath}:`, err) + } + } +} diff --git a/electron/settings/settings-store.ts b/electron/settings/settings-store.ts index 594586e..a3e2ff9 100644 --- a/electron/settings/settings-store.ts +++ b/electron/settings/settings-store.ts @@ -1,4 +1,4 @@ -import Store from 'electron-store' +import { JsonStore } from '../persistence/json-store' import type { AppSettings } from '../../shared/types/ipc.types' const defaults: AppSettings = { @@ -14,20 +14,23 @@ const defaults: AppSettings = { confirmDestructive: true, } -const store = new Store({ - name: 'settings', - defaults, - clearInvalidConfig: true, -}) +let store: JsonStore | null = null + +function getStore(): JsonStore { + if (!store) { + store = new JsonStore('settings', defaults) + } + return store +} export function getSettings(): AppSettings { - return store.store + return getStore().getAll() } export function getSetting(key: K): AppSettings[K] { - return store.get(key) + return getStore().get(key) } export function setSetting(key: K, value: AppSettings[K]): void { - store.set(key, value) + getStore().set(key, value) } diff --git a/package.json b/package.json index e05b2d7..547c8f6 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,7 @@ "dist": "npm run build && electron-builder", "dist:dir": "npm run build && electron-builder --dir" }, - "dependencies": { - "electron-store": "^10.0.0" - }, + "dependencies": {}, "devDependencies": { "@electron-toolkit/preload": "^3.0.1", "@electron-toolkit/utils": "^3.0.0",