import { spawnSync } from 'node:child_process'; import { mkdirSync } from 'node:fs'; import { dirname } from 'node:path'; import { Database } from 'bun:sqlite'; import { drizzle } from 'drizzle-orm/bun-sqlite'; import { migrate } from 'drizzle-orm/bun-sqlite/migrator'; import { resolveSqlitePath } from './dev-env'; function trim(value: string | undefined) { const candidate = value?.trim(); return candidate ? candidate : undefined; } function shouldRun(value: string | undefined) { return trim(value) !== 'false'; } function log(message: string) { console.info(`[bootstrap ${new Date().toISOString()}] ${message}`); } function formatDuration(startedAt: number) { return `${(performance.now() - startedAt).toFixed(1)}ms`; } function getDatabasePath() { const raw = trim(process.env.DATABASE_URL) || 'file:data/fiscal.sqlite'; let databasePath = raw.startsWith('file:') ? raw.slice(5) : raw; if (databasePath.startsWith('///')) { databasePath = databasePath.slice(2); } if (!databasePath) { throw new Error('DATABASE_URL must point to a SQLite file path.'); } if (databasePath.includes('://')) { throw new Error(`DATABASE_URL must resolve to a SQLite file path. Received: ${raw}`); } return databasePath; } function runWorkflowSetup() { const startedAt = performance.now(); const result = spawnSync('./node_modules/.bin/workflow-postgres-setup', [], { env: process.env, stdio: 'inherit' }); if (result.error) { throw result.error; } if (result.status !== 0) { throw new Error(`workflow-postgres-setup failed with exit code ${result.status ?? 'unknown'}`); } log(`workflow-postgres-setup completed in ${formatDuration(startedAt)}`); } function runDatabaseMigrations() { const startedAt = performance.now(); const databasePath = getDatabasePath(); if (databasePath !== ':memory:') { const normalizedPath = resolveSqlitePath(databasePath); mkdirSync(dirname(normalizedPath), { recursive: true }); } const client = new Database(databasePath, { create: true }); try { client.exec('PRAGMA foreign_keys = ON;'); migrate(drizzle(client), { migrationsFolder: './drizzle' }); } finally { client.close(); } log(`database migrations completed in ${formatDuration(startedAt)} (${databasePath})`); } const totalStartedAt = performance.now(); try { const shouldRunWorkflowSetup = shouldRun(process.env.RUN_WORKFLOW_SETUP_ON_START) && trim(process.env.WORKFLOW_TARGET_WORLD) === '@workflow/world-postgres'; const shouldRunMigrations = shouldRun(process.env.RUN_DB_MIGRATIONS_ON_START); log('starting production bootstrap'); if (shouldRunWorkflowSetup) { runWorkflowSetup(); } else { log('workflow-postgres-setup skipped'); } if (shouldRunMigrations) { runDatabaseMigrations(); } else { log('database migrations skipped'); } log(`production bootstrap completed in ${formatDuration(totalStartedAt)}`); } catch (error) { const reason = error instanceof Error ? error.message : String(error); log(`production bootstrap failed after ${formatDuration(totalStartedAt)}: ${reason}`); process.exit(1); }