import { mkdirSync, readdirSync, readFileSync } from 'node:fs'; import { dirname, join, resolve } from 'node:path'; import Database from 'better-sqlite3'; import { config as loadDotenv } from 'dotenv'; loadDotenv({ path: [resolve(process.cwd(), '.env'), resolve(process.cwd(), '../../.env')], }); const databaseUrl = process.env.DATABASE_URL; if (!databaseUrl) { throw new Error('DATABASE_URL is required.'); } const dbPath = resolve(process.cwd(), databaseUrl); mkdirSync(dirname(dbPath), { recursive: true }); const db = new Database(dbPath); db.pragma('foreign_keys = ON'); db.exec(` CREATE TABLE IF NOT EXISTS _migrations ( name TEXT PRIMARY KEY, applied_at INTEGER NOT NULL ); `); const applied = new Set( db .prepare('SELECT name FROM _migrations') .all() .map((row) => String((row as { name: string }).name)), ); const migrationsDir = resolve(process.cwd(), 'migrations'); const files = readdirSync(migrationsDir) .filter((file) => file.endsWith('.sql')) .sort(); for (const file of files) { if (applied.has(file)) continue; const sql = readFileSync(join(migrationsDir, file), 'utf8'); db.exec('BEGIN'); try { db.exec(sql); db.prepare('INSERT INTO _migrations (name, applied_at) VALUES (?, ?)').run(file, Date.now()); db.exec('COMMIT'); console.log(`Applied migration ${file}`); } catch (error) { db.exec('ROLLBACK'); throw error; } } db.close();