TypeScript 55.4%
Python 43.2%
SQL 1.2%
1import { readdirSync, readFileSync } from "node:fs";2import { dirname, join } from "node:path";3import { fileURLToPath } from "node:url";4import pg from "pg";56/** Plain-SQL forward migrations tracked in `schema_migrations`. Idempotent. */7export async function migrate(databaseUrl = process.env.DATABASE_URL ?? "postgres://localhost:5432/websensor"): Promise<string[]> {8 const here = dirname(fileURLToPath(import.meta.url));9 const dir = join(here, "..", "migrations");10 const files = readdirSync(dir)11 .filter((f) => f.endsWith(".sql"))12 .sort();13 const client = new pg.Client({ connectionString: databaseUrl });14 await client.connect();15 const applied: string[] = [];16 try {17 await client.query("create table if not exists schema_migrations (name text primary key, applied_at timestamptz not null default now())");18 await client.query("select pg_advisory_lock(7245)");19 const done = new Set((await client.query<{ name: string }>("select name from schema_migrations")).rows.map((r) => r.name));20 for (const f of files) {21 if (done.has(f)) continue;22 const sql = readFileSync(join(dir, f), "utf8");23 await client.query("begin");24 try {25 await client.query(sql);26 await client.query("insert into schema_migrations (name) values ($1)", [f]);27 await client.query("commit");28 applied.push(f);29 } catch (e) {30 await client.query("rollback");31 throw e;32 }33 }34 await client.query("select pg_advisory_unlock(7245)");35 } finally {36 await client.end();37 }38 return applied;39}4041if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {42 migrate()43 .then((a) => {44 console.log(a.length ? `applied: ${a.join(", ")}` : "schema up to date");45 })46 .catch((e) => {47 console.error(e);48 process.exit(1);49 });50}51