/** * Scaffold a new migration file under `scripts/migrations/`. * * Usage: npm run migrate:create -- * e.g.: npm run migrate:create -- add_finance_kyc_column * * The file is named `_.ts` (UTC timestamp). * Author then edits `up()` to implement the schema change. */ import { promises as fs } from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; const SCRIPTS_DIR = path.dirname(fileURLToPath(import.meta.url)); const MIGRATIONS_DIR = path.join(SCRIPTS_DIR, 'migrations'); function pad(n: number, width = 2): string { return String(n).padStart(width, '0'); } function utcTimestamp(): string { const d = new Date(); return ( d.getUTCFullYear().toString() + pad(d.getUTCMonth() + 1) + pad(d.getUTCDate()) + pad(d.getUTCHours()) + pad(d.getUTCMinutes()) + pad(d.getUTCSeconds()) ); } function sanitizeName(input: string): string { const cleaned = input .trim() .toLowerCase() .replace(/[^a-z0-9]+/g, '_') .replace(/^_+|_+$/g, ''); if (!cleaned) { throw new Error('Migration name is empty after sanitisation.'); } if (cleaned.length > 80) { throw new Error( `Migration name "${cleaned}" is too long (${cleaned.length}). Keep it under 80 chars.` ); } return cleaned; } async function main(): Promise { const rawName = process.argv.slice(2).join('_'); if (!rawName) { console.error('Missing migration name.'); console.error('Usage: npm run migrate:create -- '); process.exit(1); } const name = sanitizeName(rawName); const ts = utcTimestamp(); const filename = `${ts}_${name}.ts`; const target = path.join(MIGRATIONS_DIR, filename); const body = `/** * Migration: ${name.replace(/_/g, ' ')} * * Generated at ${new Date().toISOString()}. * Implement up() with idempotent DDL where possible. */ import type { QueryInterface, Sequelize, Transaction } from 'sequelize'; export interface MigrationContext { queryInterface: QueryInterface; sequelize: Sequelize; transaction: Transaction; } const migration = { async up({ sequelize, transaction }: MigrationContext): Promise { // TODO: implement the schema change. // Example: // await sequelize.query(\` // ALTER TABLE "applications" // ADD COLUMN IF NOT EXISTS "kycReviewedAt" TIMESTAMPTZ NULL; // \`, { transaction }); throw new Error('Migration ${filename} has no up() implementation yet.'); } }; export default migration; `; await fs.mkdir(MIGRATIONS_DIR, { recursive: true }); await fs.writeFile(target, body, { flag: 'wx' }); console.log(`Created ${path.relative(process.cwd(), target)}`); console.log('Next:'); console.log(' 1. Edit the file and implement up().'); console.log(' 2. Update the matching Sequelize model.'); console.log(' 3. Run: npm run migrate:up'); } main().catch((err) => { console.error('create-migration failed:', err?.message || err); process.exit(1); });