import { dirname, join } from 'node:path' import { fileURLToPath } from 'node:url' import { readFileSync, readdirSync, mkdirSync } from 'node:fs' import Database from 'better-sqlite3' import { drizzle } from 'drizzle-orm/better-sqlite3' import { schema } from './schema.js' export type Sqlite = Database.Database export type Db = ReturnType> const __dirname = dirname(fileURLToPath(import.meta.url)) export function resolveDatabasePath(databaseUrl: string): string { const url = databaseUrl.startsWith('sqlite:') ? databaseUrl.slice('sqlite:'.length) : databaseUrl return url } export function createDb(databaseUrl: string): { db: Db; sqlite: Sqlite } { const path = resolveDatabasePath(databaseUrl) const dir = dirname(path) if (dir && dir !== '.') { try { mkdirSync(dir, { recursive: true }) } catch { /* exists */ } } const sqlite = new Database(path) sqlite.pragma('journal_mode = WAL') sqlite.pragma('synchronous = NORMAL') sqlite.pragma('foreign_keys = ON') const db = drizzle(sqlite, { schema }) return { db, sqlite } } export function createMemoryDb(): { db: Db; sqlite: Sqlite } { const sqlite = new Database(':memory:') sqlite.pragma('foreign_keys = ON') const db = drizzle(sqlite, { schema }) return { db, sqlite } } export function runMigrations(sqlite: Sqlite): void { const migrationsDir = join(__dirname, '..', 'migrations') const files = readdirSync(migrationsDir) .filter((f) => f.endsWith('.sql')) .sort() sqlite.exec( `CREATE TABLE IF NOT EXISTS _migrations ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, applied_at TEXT NOT NULL DEFAULT (datetime('now')) )`, ) for (const file of files) { const applied = sqlite .prepare('SELECT 1 FROM _migrations WHERE name = ?') .get(file) if (applied) continue const sql = readFileSync(join(migrationsDir, file), 'utf-8') sqlite.exec(sql) sqlite.prepare('INSERT INTO _migrations (name) VALUES (?)').run(file) } } export function healthCheck(sqlite: Sqlite): void { sqlite.prepare('SELECT 1').get() }