SPB Git

spb/search-box Public

Agentic web research engine — hypotheses, verbatim evidence, contradictions, sourced answers streamed live. Claude Opus 5 + Firecrawl + PostgreSQL.

TypeScript 76.9% CSS 18.7% SQL 2.1% JavaScript 1.8% Shell 0.5%
1.6 KB · 54 lines typescript
Raw Blame History
1/**2 * Search-box.ai3 * Author: Simon-Pierre Boucher4 * Contact: contact@spboucher.ai5 * File: packages/db/src/migrate.ts6 * Description: Idempotent migration runner (applies packages/db/migrations in order).7 */89import { readdirSync, readFileSync } from "node:fs";10import { dirname, join } from "node:path";11import { fileURLToPath } from "node:url";12import { getPool } from "./pool.js";1314const here = dirname(fileURLToPath(import.meta.url));15const migrationsDir = join(here, "..", "migrations");1617export async function migrate(): Promise<void> {18  const pool = getPool();19  await pool.query(20    `CREATE TABLE IF NOT EXISTS schema_migrations (21       name text PRIMARY KEY, applied_at timestamptz NOT NULL DEFAULT now()22     )`23  );24  const files = readdirSync(migrationsDir).filter((f) => f.endsWith(".sql")).sort();25  for (const file of files) {26    const { rows } = await pool.query("SELECT 1 FROM schema_migrations WHERE name = $1", [file]);27    if (rows.length > 0) continue;28    const sql = readFileSync(join(migrationsDir, file), "utf8");29    const client = await pool.connect();30    try {31      await client.query("BEGIN");32      await client.query(sql);33      await client.query("INSERT INTO schema_migrations (name) VALUES ($1)", [file]);34      await client.query("COMMIT");35      console.log(`applied ${file}`);36    } catch (err) {37      await client.query("ROLLBACK");38      throw err;39    } finally {40      client.release();41    }42  }43}4445// Run directly: `pnpm migrate`46if (process.argv[1] && process.argv[1].endsWith("migrate.ts")) {47  migrate()48    .then(() => process.exit(0))49    .catch((err) => {50      console.error(err);51      process.exit(1);52    });53}54