// Apply SQL migrations in order. Idempotent — tracks applied files in _migrations. import fs from "node:fs"; import path from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; import { db, run, all } from "./db.mjs"; const HERE = path.dirname(fileURLToPath(import.meta.url)); const DIR = path.join(HERE, "migrations"); export async function migrate() { // Bootstrap the tracking table (the first migration also declares it IF NOT EXISTS). await run(`CREATE TABLE IF NOT EXISTS _migrations (name TEXT PRIMARY KEY, applied_at INTEGER NOT NULL)`); const done = new Set((await all(`SELECT name FROM _migrations`)).map((r) => r.name)); const files = fs.readdirSync(DIR).filter((f) => f.endsWith(".sql")).sort(); for (const file of files) { if (done.has(file)) { console.log(`· ${file} (already applied)`); continue; } const sql = fs.readFileSync(path.join(DIR, file), "utf8"); // libSQL executes one statement per call — split on semicolons at line ends. const statements = sql.split(/;\s*(?:\n|$)/).map((s) => s.trim()).filter(Boolean); for (const stmt of statements) await run(stmt); await run(`INSERT INTO _migrations (name, applied_at) VALUES (?, ?)`, [file, Date.now()]); console.log(`✓ ${file}`); } console.log("migrations up to date"); } // Run directly (npm run migrate) — not when imported by the server. if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) { migrate() .then(() => db.close?.()) .catch((e) => { console.error(e); process.exit(1); }); }