// SQLite persistence for games (one row per game id; the full GameState as JSON). import { mkdirSync } from 'node:fs'; import path from 'node:path'; import Database from 'better-sqlite3'; import type { GameState } from '../shared/types'; export interface Db { save(state: GameState): void; load(id: string): GameState | null; listIds(): string[]; close(): void; } export function openDb(filePath: string): Db { const dir = path.dirname(filePath); if (dir) mkdirSync(dir, { recursive: true }); const db = new Database(filePath); db.pragma('journal_mode = WAL'); db.exec( `CREATE TABLE IF NOT EXISTS games ( id TEXT PRIMARY KEY, data TEXT NOT NULL, updated_at TEXT NOT NULL )`, ); const upsert = db.prepare( `INSERT INTO games (id, data, updated_at) VALUES (?, ?, ?) ON CONFLICT(id) DO UPDATE SET data = excluded.data, updated_at = excluded.updated_at`, ); const byId = db.prepare('SELECT data FROM games WHERE id = ?'); const all = db.prepare('SELECT id FROM games ORDER BY updated_at DESC'); return { save(state) { upsert.run(state.id, JSON.stringify(state), new Date().toISOString()); }, load(id) { const row = byId.get(id) as { data: string } | undefined; if (!row) return null; try { return JSON.parse(row.data) as GameState; } catch { return null; } }, listIds() { return (all.all() as { id: string }[]).map((r) => r.id); }, close() { db.close(); }, }; }