import { Database } from "bun:sqlite"; import { chmodSync } from "node:fs"; import { Effect } from "effect"; const OBSOLETE_TABLES = [ "jobs", "chat_sessions", "chat_messages", "chat_runs", "chat_usage", "sessions", "messages", "runs", "usage", ] as const; const sweptPaths = new Set(); const dropObsoleteTables = (db: Database, dbPath: string): void => { if (sweptPaths.has(dbPath)) return; for (const table of OBSOLETE_TABLES) { db.run(`DROP TABLE IF EXISTS ${table}`); } sweptPaths.add(dbPath); }; export const toFiniteNumber = (value: unknown): number => { const parsed = Number(value ?? 0); return Number.isFinite(parsed) ? parsed : 0; }; export class RepositoryError extends Error { readonly _tag = "RepositoryError"; public constructor( readonly operation: string, override readonly cause: unknown, ) { super(`Repository operation failed: ${operation}`, { cause }); this.name = "RepositoryError"; } } export const repositoryEffect = ( operation: string, execute: () => A, ): Effect.Effect => Effect.try({ try: execute, catch: (cause) => new RepositoryError(operation, cause), }); export const makeDatabaseCloser = ( db: Database, operation: string, ): (() => Effect.Effect) => { let closed = false; return () => repositoryEffect(operation, () => { if (closed) return; db.close(); closed = true; }); }; export const toNullableNumber = (value: unknown): number | null => { if (value === null || value === undefined) return null; const parsed = Number(value); return Number.isFinite(parsed) ? parsed : null; }; export const openSqliteDatabase = (dbPath: string): Database => { const db = new Database(dbPath); try { db.run("PRAGMA busy_timeout = 5000"); if (dbPath !== ":memory:") { try { chmodSync(dbPath, 0o600); } catch {} } dropObsoleteTables(db, dbPath); return db; } catch (cause) { try { db.close(); } catch {} throw cause; } }; export const openInitializedDatabase = ( dbPath: string, initialize: (db: Database) => void, ): Database => { const db = openSqliteDatabase(dbPath); try { initialize(db); return db; } catch (cause) { try { db.close(); } catch {} throw cause; } };