@dbsp/cli 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/commands/generate.ts","../src/commands/introspect.ts","../src/commands/migrate.ts","../src/migration-file.ts","../src/commands/push.ts","../src/ddl-executor.ts","../src/commands/repl.ts","../src/commands/verify.ts","../src/verifier.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * ARCH-002 Block 3: CLI Scaffold\n *\n * dbsp CLI - Schema-first code generation for db-semantic-planner.\n */\n\nimport { Command, CommanderError } from 'commander';\nimport { generateCommand } from './commands/generate.js';\nimport { introspectCommand } from './commands/introspect.js';\nimport { migrateCommand } from './commands/migrate.js';\nimport { pushCommand } from './commands/push.js';\nimport { replCommand } from './commands/repl.js';\nimport { verifyCommand } from './commands/verify.js';\n\nconst program = new Command();\n\nprogram\n\t.name('dbsp')\n\t.description('Schema-first code generation for db-semantic-planner')\n\t.version('0.0.1');\n\n// Register commands\nprogram.addCommand(generateCommand);\nprogram.addCommand(introspectCommand);\nprogram.addCommand(migrateCommand);\nprogram.addCommand(pushCommand);\nprogram.addCommand(replCommand);\nprogram.addCommand(verifyCommand);\n\n// CC-15: Intercept Commander parse errors so --json commands receive a JSON\n// error object on stdout instead of a plain-text usage message.\nprogram.exitOverride();\n\ntry {\n\tprogram.parse();\n} catch (err) {\n\t// Commander throws CommanderError for --help, --version, and parse errors.\n\t// Exit 0 for informational outputs (help/version); only exit 1 for real errors.\n\tif (err instanceof CommanderError && err.exitCode === 0) {\n\t\tprocess.exit(0);\n\t}\n\tconst message = err instanceof Error ? err.message : 'Command parse error';\n\tif (process.argv.includes('--json')) {\n\t\tconsole.log(JSON.stringify({ status: 'error', error: message }, null, 2));\n\t} else {\n\t\tconsole.error(`❌ ${message}`);\n\t}\n\tprocess.exit(1);\n}\n","/**\n * ARCH-002 Block 3+4+5: Generate Command\n * CLI-DDL: Added DDL generation target\n * ARCH-005: Migrated to schema() API, removed legacy generators\n *\n * dbsp generate <target> - Generate code from schema.\n *\n * Targets:\n * - ddl: Generate SQL DDL (CREATE TABLE statements)\n *\n * Deprecated targets (removed in ARCH-005):\n * - manifest: Was for legacy defineSchema() format\n * - kysely: Was for legacy defineSchema() format\n */\n\nimport { mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { Command } from 'commander';\nimport { loadSchema, loadSchemaFromCwd } from '../utils/schema-loader.js';\n\nexport const generateCommand = new Command('generate')\n\t.description('Generate code from schema')\n\t.argument('<target>', 'Target to generate: ddl')\n\t.option('-s, --schema <path>', 'Path to schema file (default: auto-detect)')\n\t.option('-o, --out <dir>', 'Output directory (default: ./generated/<target>)')\n\t.option('--output <dir>', 'Output directory (alias for --out)')\n\t.option('--drop', 'Include DROP TABLE IF EXISTS statements (ddl only)')\n\t.option('--schema-name <name>', 'Database schema name (ddl only)')\n\t.option(\n\t\t'--dialect <name>',\n\t\t'Database dialect: postgresql | mysql | sqlite | mssql (default: postgresql)',\n\t)\n\t.option(\n\t\t'--casing <type>',\n\t\t'Column naming: snake | camel | none (default: based on dialect)',\n\t)\n\t.action(\n\t\tasync (\n\t\t\ttarget: string,\n\t\t\toptions: {\n\t\t\t\tschema?: string;\n\t\t\t\tout?: string;\n\t\t\t\toutput?: string;\n\t\t\t\tdrop?: boolean;\n\t\t\t\tschemaName?: string;\n\t\t\t\tdialect?: string;\n\t\t\t\tcasing?: 'snake' | 'camel' | 'none';\n\t\t\t},\n\t\t) => {\n\t\t\ttry {\n\t\t\t\t// Load schema (ARCH-005: only schema() format supported)\n\t\t\t\tlet schema: Awaited<ReturnType<typeof loadSchema>>;\n\t\t\t\tlet schemaPath: string;\n\n\t\t\t\tif (options.schema) {\n\t\t\t\t\tschema = await loadSchema(options.schema);\n\t\t\t\t\tschemaPath = options.schema;\n\t\t\t\t} else {\n\t\t\t\t\tconst result = await loadSchemaFromCwd();\n\t\t\t\t\tschema = result.schema;\n\t\t\t\t\tschemaPath = result.path;\n\t\t\t\t}\n\n\t\t\t\t// For DDL without --output, we output to stdout so use stderr for info\n\t\t\t\tconst outputPath = options.out ?? options.output;\n\t\t\t\tconst useStdout = target === 'ddl' && !outputPath;\n\t\t\t\tconst log = useStdout ? console.error : console.log;\n\n\t\t\t\tlog(`📄 Loaded schema from: ${schemaPath}`);\n\n\t\t\t\t// Validate dialect option\n\t\t\t\tconst dialect = options.dialect ?? 'postgresql';\n\t\t\t\tif (dialect !== 'postgresql') {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`⚠️ Warning: Only 'postgresql' dialect is currently supported. Using postgresql.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Generate based on target\n\t\t\t\tswitch (target) {\n\t\t\t\t\tcase 'ddl': {\n\t\t\t\t\t\t// Determine casing: explicit option > dialect default > 'snake'\n\t\t\t\t\t\tconst casing = options.casing ?? 'snake';\n\n\t\t\t\t\t\t// Import adapter from adapter-pgsql (compile-only, no DB connection needed)\n\t\t\t\t\t\tconst { createPgsqlCompileOnlyAdapter } = await import(\n\t\t\t\t\t\t\t'@dbsp/adapter-pgsql'\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tconst dbCasing =\n\t\t\t\t\t\t\tcasing === 'snake'\n\t\t\t\t\t\t\t\t? ('snake_case' as const)\n\t\t\t\t\t\t\t\t: ('preserve' as const);\n\t\t\t\t\t\tconst adapter = createPgsqlCompileOnlyAdapter({\n\t\t\t\t\t\t\tdbCasing,\n\t\t\t\t\t\t\t...(options.schemaName ? { schemaName: options.schemaName } : {}),\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// ARCH-005: Use schema.model directly (already ModelIR)\n\t\t\t\t\t\t\tconst ddlStatements = adapter.generateDDL(schema.model, {\n\t\t\t\t\t\t\t\t...(options.drop !== undefined && {\n\t\t\t\t\t\t\t\t\tincludeDropStatements: options.drop,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tconst ddlContent = ddlStatements.join('\\n\\n');\n\t\t\t\t\t\t\tconst outputPath = options.out ?? options.output;\n\n\t\t\t\t\t\t\tif (outputPath) {\n\t\t\t\t\t\t\t\t// Write to file if --output is specified\n\t\t\t\t\t\t\t\tconst outPath = outputPath.endsWith('.sql')\n\t\t\t\t\t\t\t\t\t? resolve(process.cwd(), outputPath)\n\t\t\t\t\t\t\t\t\t: resolve(process.cwd(), outputPath, 'schema.sql');\n\n\t\t\t\t\t\t\t\tmkdirSync(dirname(outPath), { recursive: true });\n\t\t\t\t\t\t\t\twriteFileSync(outPath, ddlContent, 'utf-8');\n\n\t\t\t\t\t\t\t\tconsole.log(`✅ Generated DDL: ${outPath}`);\n\t\t\t\t\t\t\t\tconsole.log(` Tables: ${schema.tableNames.length}`);\n\t\t\t\t\t\t\t\tconsole.log(` Statements: ${ddlStatements.length}`);\n\t\t\t\t\t\t\t\tconsole.log(` Casing: ${casing}`);\n\t\t\t\t\t\t\t\tif (options.drop) {\n\t\t\t\t\t\t\t\t\tconsole.log(` Includes DROP statements`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (options.schemaName) {\n\t\t\t\t\t\t\t\t\tconsole.log(` Schema: ${options.schemaName}`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// Output DDL to stdout (for piping)\n\t\t\t\t\t\t\t\t// Info messages (schema loaded) went to stderr\n\t\t\t\t\t\t\t\tconsole.log(ddlContent);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase 'manifest':\n\t\t\t\t\tcase 'kysely':\n\t\t\t\t\t\t// EH-10: Throw instead of process.exit(1) inside try — outer catch handles exit\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Target '${target}' has been removed in ARCH-005. ` +\n\t\t\t\t\t\t\t\t`These generators required the legacy defineSchema() format. ` +\n\t\t\t\t\t\t\t\t`Use 'ddl' target for SQL generation.`,\n\t\t\t\t\t\t);\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\t// EH-10: Throw instead of process.exit(1) inside try — outer catch handles exit\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Unknown target: ${target}. Available targets: ddl`,\n\t\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tconsole.error(`❌ ${message}`);\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n","/**\n * CLI-DDL: Introspect Command\n *\n * dbsp introspect - Generate schema.ts from database introspection.\n */\n\nimport { mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { Command } from 'commander';\nimport {\n\tgenerateSchemaFile,\n\ttype SchemaCodegenOptions,\n} from '../generators/schema-codegen.js';\nimport { createDbConnection, redactDbUrl } from '../utils/db-utils.js';\n\nexport const introspectCommand = new Command('introspect')\n\t.description('Generate schema.ts from database introspection')\n\t.requiredOption('-d, --db <url>', 'Database connection URL (required)')\n\t.option('-o, --out <file>', 'Output schema file', './dbsp.schema.ts')\n\t.option('--schema-name <name>', 'Database schema name', 'public')\n\t.option(\n\t\t'--exclude <patterns>',\n\t\t'Tables to exclude (comma-separated glob patterns)',\n\t\t'_migrations,_prisma*,pg_*',\n\t)\n\t.option('--include <patterns>', 'Tables to include (comma-separated)')\n\t.option('--no-db-type-comments', 'Omit original DB type comments')\n\t.option(\n\t\t'--db-casing <casing>',\n\t\t'Database column casing (snake_case → camelCase in generated code)',\n\t\t'snake_case',\n\t)\n\t.action(\n\t\tasync (options: {\n\t\t\tdb: string;\n\t\t\tout: string;\n\t\t\tschemaName: string;\n\t\t\texclude: string;\n\t\t\tinclude?: string;\n\t\t\tdbTypeComments: boolean;\n\t\t\tdbCasing: 'snake_case' | 'camelCase' | 'preserve';\n\t\t}) => {\n\t\t\tconst redactedUrl = redactDbUrl(options.db);\n\n\t\t\tconsole.log(`🔍 Introspecting database: ${redactedUrl}`);\n\t\t\tconsole.log(` Schema: ${options.schemaName}`);\n\t\t\tif (options.exclude) {\n\t\t\t\tconsole.log(` Excluding: ${options.exclude}`);\n\t\t\t}\n\t\t\tconsole.log('');\n\n\t\t\ttry {\n\t\t\t\t// Connect to database\n\t\t\t\tconst { pool } = await createDbConnection(options.db);\n\n\t\t\t\ttry {\n\t\t\t\t\t// Import introspect from adapter-pgsql\n\t\t\t\t\tconst { introspect } = await import('@dbsp/adapter-pgsql');\n\n\t\t\t\t\t// Build introspection options from CLI flags\n\t\t\t\t\tconst excludePatterns = options.exclude\n\t\t\t\t\t\t? options.exclude.split(',').map((s) => s.trim())\n\t\t\t\t\t\t: undefined;\n\t\t\t\t\tconst includePatterns = options.include\n\t\t\t\t\t\t? options.include.split(',').map((s) => s.trim())\n\t\t\t\t\t\t: undefined;\n\n\t\t\t\t\t// Introspect the database directly (returns IntrospectedModelIR)\n\t\t\t\t\tconst model = await introspect(pool, {\n\t\t\t\t\t\tschema: options.schemaName,\n\t\t\t\t\t\t...(excludePatterns ? { exclude: excludePatterns } : {}),\n\t\t\t\t\t\t...(includePatterns ? { include: includePatterns } : {}),\n\t\t\t\t\t});\n\n\t\t\t\t\t// Report what we found\n\t\t\t\t\tconst tableCount = model.tables.size;\n\t\t\t\t\tconst relationCount = model.relations.size;\n\t\t\t\t\tconst hierarchyCount = model.hierarchies?.length ?? 0;\n\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t`📊 Found ${tableCount} tables, ${relationCount} relations, ${hierarchyCount} hierarchies`,\n\t\t\t\t\t);\n\t\t\t\t\tif (model.warnings?.length) {\n\t\t\t\t\t\tfor (const w of model.warnings) {\n\t\t\t\t\t\t\tconsole.log(` ⚠️ ${w}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tconsole.log('');\n\n\t\t\t\t\t// Generate schema file — pass metadata from introspection\n\t\t\t\t\tconst codegenOptions: SchemaCodegenOptions = {\n\t\t\t\t\t\tsourceUrl: options.db,\n\t\t\t\t\t\tincludeDbTypeComments: options.dbTypeComments,\n\t\t\t\t\t\twarnings: model.warnings,\n\t\t\t\t\t\tintrospectedAt: model.introspectedAt,\n\t\t\t\t\t\tdbCasing: options.dbCasing,\n\t\t\t\t\t};\n\n\t\t\t\t\tconst schemaCode = generateSchemaFile(model, codegenOptions);\n\n\t\t\t\t\t// Write output file\n\t\t\t\t\tconst outPath = resolve(process.cwd(), options.out);\n\t\t\t\t\tmkdirSync(dirname(outPath), { recursive: true });\n\t\t\t\t\twriteFileSync(outPath, schemaCode, 'utf-8');\n\n\t\t\t\t\tconsole.log(`✅ Generated schema: ${outPath}`);\n\t\t\t\t\tconsole.log(` Tables: ${tableCount}`);\n\t\t\t\t\tconsole.log(` Relations: ${relationCount}`);\n\t\t\t\t} finally {\n\t\t\t\t\t// Close database connection\n\t\t\t\t\tawait pool.end();\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tconsole.error(`❌ ${message}`);\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n","/**\n * Migrate Command — Migration Infrastructure\n *\n * dbsp migrate dev - Generate migration from schema diff\n * dbsp migrate apply - Apply pending migrations\n * dbsp migrate rollback - Roll back applied migrations\n * dbsp migrate status - Show migration status\n */\n\nimport {\n\tcompareSchemata,\n\tensureMigrationsTable,\n\tgenerateMigrationFile,\n\tgenerateMigrationSQL,\n\tgetAppliedMigrations,\n\tgetNextSchemaVersion,\n\tintrospect,\n\tisDestructiveDown,\n\tparseMigrationFile,\n\trecordMigration,\n\tremoveMigrationRecord,\n\twithMigrationLock,\n} from '@dbsp/adapter-pgsql';\nimport { Command } from 'commander';\nimport type { Pool } from 'pg';\nimport {\n\tDEFAULT_MIGRATIONS_DIR,\n\tgenerateMigrationFilename,\n\tscanMigrationFiles,\n\twriteMigrationFile,\n} from '../migration-file.js';\nimport { createDbConnection, redactDbUrl } from '../utils/db-utils.js';\nimport { loadSchema } from '../utils/schema-loader.js';\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Typed error for migration failures.\n * Distinguishes user-facing errors (validation, logic) from unexpected errors.\n */\nexport class MigrationError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = 'MigrationError';\n\t}\n}\n\n/**\n * Sanitize a pg error for user-facing output.\n *\n * PostgreSQL errors may carry schema/table/row details in their message text.\n * For pg errors (identifiable by SQLSTATE `.code`), we emit a sanitized message\n * keyed only by the SQLSTATE code. The raw message is available via DEBUG=dbsp.\n *\n * @param err - Any caught value\n * @returns An Error instance safe to display to the user\n */\nexport function sanitizePgError(err: unknown): Error {\n\tif (err instanceof Error) {\n\t\t// pg errors carry a SQLSTATE `.code` property\n\t\tconst code = (err as Error & { code?: string }).code;\n\t\tif (typeof code === 'string' && /^[0-9A-Z]{5}$/.test(code)) {\n\t\t\tif (process.env.DEBUG?.includes('dbsp')) {\n\t\t\t\tconsole.error(`[DEBUG] pg error detail: ${err.message}`);\n\t\t\t}\n\t\t\treturn new MigrationError(`Migration failed: database error ${code}`);\n\t\t}\n\t\treturn err;\n\t}\n\treturn new Error(String(err));\n}\n\n/**\n * DRY lifecycle wrapper: create pool → run fn → end pool on success or error.\n * Replaces the repeated try { ... } finally { pool.end() } pattern across commands.\n *\n * process.exit is called OUTSIDE the pool lifecycle so the lock-holding client\n * (from withMigrationLock) is always released before the process terminates.\n */\nexport async function withMigratePool<T>(\n\tdbUrl: string,\n\tfn: (pool: Pool) => Promise<T>,\n): Promise<T> {\n\tconst { pool } = await createDbConnection(dbUrl);\n\ttry {\n\t\treturn await fn(pool);\n\t} finally {\n\t\tlet endError: unknown;\n\t\ttry {\n\t\t\tawait pool.end();\n\t\t} catch (e) {\n\t\t\tendError = e;\n\t\t}\n\t\tif (endError !== undefined) {\n\t\t\t// Cleanup failure is non-fatal — log it but don't mask the original error\n\t\t\tconsole.error(\n\t\t\t\t`Warning: pool.end() failed: ${endError instanceof Error ? endError.message : String(endError)}`,\n\t\t\t);\n\t\t}\n\t}\n}\n\n/**\n * Top-level error handler for migrate commands.\n * Ensures process.exit is only called AFTER pool cleanup (via withMigratePool).\n */\nasync function runMigrateAction(fn: () => Promise<void>): Promise<void> {\n\ttry {\n\t\tawait fn();\n\t} catch (error) {\n\t\tif (error instanceof Error) {\n\t\t\tconsole.error(`❌ Error: ${error.message}`);\n\t\t} else {\n\t\t\tconsole.error('❌ Unknown error occurred');\n\t\t}\n\t\tprocess.exit(1);\n\t}\n}\n\n// ============================================================================\n// Subcommands\n// ============================================================================\n\nconst devCommand = new Command('dev')\n\t.description('Generate a migration from schema changes')\n\t.option(\n\t\t'-s, --schema <path>',\n\t\t'Path to schema file (default: dbsp.schema.ts)',\n\t)\n\t.requiredOption('-d, --db <url>', 'Database connection URL (required)')\n\t.option('--schema-name <name>', 'Database schema name (default: public)')\n\t.option('--dir <path>', 'Migrations directory', DEFAULT_MIGRATIONS_DIR)\n\t.option('-n, --name <description>', 'Migration description', 'migration')\n\t.option('--allow-destructive', 'Include destructive changes (drops)')\n\t.action(\n\t\t(options: {\n\t\t\tschema?: string;\n\t\t\tdb: string;\n\t\t\tschemaName?: string;\n\t\t\tdir: string;\n\t\t\tname: string;\n\t\t\tallowDestructive?: boolean;\n\t\t}) =>\n\t\t\trunMigrateAction(async () => {\n\t\t\t\tconst schemaPath = options.schema ?? 'dbsp.schema.ts';\n\n\t\t\t\tconsole.log(`📝 Generating migration: ${schemaPath}`);\n\t\t\t\tconsole.log(` Database: ${redactDbUrl(options.db)}`);\n\t\t\t\tconsole.log('');\n\n\t\t\t\tconst loaded = await loadSchema(schemaPath);\n\t\t\t\tconst schemaModel = loaded.model;\n\n\t\t\t\tawait withMigratePool(options.db, async (pool) => {\n\t\t\t\t\tconst dbModel = await introspect(pool, {\n\t\t\t\t\t\t...(options.schemaName ? { schema: options.schemaName } : {}),\n\t\t\t\t\t});\n\n\t\t\t\t\tconst diff = compareSchemata(schemaModel, dbModel);\n\n\t\t\t\t\tif (diff.changes.length === 0) {\n\t\t\t\t\t\tconsole.log('✅ No changes detected — database matches schema.');\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check for destructive changes\n\t\t\t\t\tif (diff.hasDestructive && !options.allowDestructive) {\n\t\t\t\t\t\tconst destructive = diff.changes.filter((c) => c.destructive);\n\t\t\t\t\t\tthrow new MigrationError(\n\t\t\t\t\t\t\t`${destructive.length} destructive change(s) detected:\\n` +\n\t\t\t\t\t\t\t\tdestructive.map((c) => ` - ${c.details}`).join('\\n') +\n\t\t\t\t\t\t\t\t'\\n\\nUse --allow-destructive to include these changes.',\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst sqlOptions = {\n\t\t\t\t\t\tincludeDestructive: options.allowDestructive ?? false,\n\t\t\t\t\t\t...(options.schemaName ? { schemaName: options.schemaName } : {}),\n\t\t\t\t\t};\n\n\t\t\t\t\tconst statements = generateMigrationSQL(diff, sqlOptions);\n\n\t\t\t\t\tif (statements.length === 0) {\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t'✅ No migration needed — all changes are non-actionable.',\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Generate migration file with UP + DOWN sections\n\t\t\t\t\tconst existingFiles = scanMigrationFiles(options.dir).map(\n\t\t\t\t\t\t(f) => f.name,\n\t\t\t\t\t);\n\t\t\t\t\tconst filename = generateMigrationFilename(\n\t\t\t\t\t\texistingFiles,\n\t\t\t\t\t\toptions.name,\n\t\t\t\t\t);\n\t\t\t\t\tconst content = generateMigrationFile(diff, {\n\t\t\t\t\t\t...sqlOptions,\n\t\t\t\t\t\tname: filename,\n\t\t\t\t\t});\n\n\t\t\t\t\tconst file = writeMigrationFile(options.dir, filename, content);\n\n\t\t\t\t\tconsole.log(`✅ Migration created: ${file.path}`);\n\t\t\t\t\tconsole.log(` Statements: ${statements.length}`);\n\t\t\t\t\tconsole.log(` Checksum: ${file.checksum.slice(0, 12)}...`);\n\t\t\t\t});\n\t\t\t}),\n\t);\n\nconst applyCommand = new Command('apply')\n\t.description('Apply pending migrations')\n\t.requiredOption('-d, --db <url>', 'Database connection URL (required)')\n\t.option('--dir <path>', 'Migrations directory', DEFAULT_MIGRATIONS_DIR)\n\t.option('--dry-run', 'Show pending migrations without applying')\n\t.action((options: { db: string; dir: string; dryRun?: boolean }) =>\n\t\trunMigrateAction(async () => {\n\t\t\tconsole.log('🔄 Applying migrations');\n\t\t\tconsole.log(` Database: ${redactDbUrl(options.db)}`);\n\t\t\tconsole.log(` Directory: ${options.dir}`);\n\t\t\tconsole.log('');\n\n\t\t\tawait withMigratePool(options.db, async (pool) => {\n\t\t\t\t// Ensure tracking table exists before acquiring lock\n\t\t\t\tawait ensureMigrationsTable(pool);\n\n\t\t\t\tawait withMigrationLock(pool, async (client) => {\n\t\t\t\t\t// Get applied migrations on the lock-holding client\n\t\t\t\t\tconst applied = await getAppliedMigrations(client as unknown as Pool);\n\t\t\t\t\tconst appliedMap = new Map(applied.map((m) => [m.name, m.checksum]));\n\n\t\t\t\t\t// Scan migration files\n\t\t\t\t\tconst files = scanMigrationFiles(options.dir);\n\n\t\t\t\t\tif (files.length === 0) {\n\t\t\t\t\t\tconsole.log(`No migration files found in ${options.dir}`);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Validate checksums for already-applied migrations\n\t\t\t\t\tfor (const file of files) {\n\t\t\t\t\t\tconst existingChecksum = appliedMap.get(file.name);\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\texistingChecksum !== undefined &&\n\t\t\t\t\t\t\texistingChecksum !== file.checksum\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tthrow new MigrationError(\n\t\t\t\t\t\t\t\t`Checksum mismatch for ${file.name}\\n` +\n\t\t\t\t\t\t\t\t\t` Expected: ${existingChecksum}\\n` +\n\t\t\t\t\t\t\t\t\t` Got: ${file.checksum}\\n` +\n\t\t\t\t\t\t\t\t\t'\\nMigration file has been tampered with after being applied.',\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Find pending migrations\n\t\t\t\t\tconst pending = files.filter((f) => !appliedMap.has(f.name));\n\n\t\t\t\t\tif (pending.length === 0) {\n\t\t\t\t\t\tconsole.log('✅ All migrations already applied.');\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (options.dryRun) {\n\t\t\t\t\t\tconsole.log(`${pending.length} pending migration(s):`);\n\t\t\t\t\t\tfor (const file of pending) {\n\t\t\t\t\t\t\tconsole.log(` - ${file.name}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Apply each pending migration atomically (DDL + record in one transaction)\n\t\t\t\t\tlet appliedCount = 0;\n\t\t\t\t\tfor (const file of pending) {\n\t\t\t\t\t\tconsole.log(` Applying: ${file.name}...`);\n\n\t\t\t\t\t\t// Parse UP section from file\n\t\t\t\t\t\tconst parsed = parseMigrationFile(file.content);\n\t\t\t\t\t\tconst statements = parsed.upStatements.filter(\n\t\t\t\t\t\t\t(s) => s.length > 0 && !s.startsWith('-- '),\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// Determine version and destructive flag before the transaction\n\t\t\t\t\t\t// (read from the lock-held client so we see a consistent view)\n\t\t\t\t\t\tconst version = await getNextSchemaVersion(\n\t\t\t\t\t\t\tclient as unknown as Pool,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst destructive = isDestructiveDown(parsed.downStatements);\n\n\t\t\t\t\t\t// Atomic: DDL + record in ONE transaction on the lock-holding client\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait client.query('BEGIN');\n\n\t\t\t\t\t\t\tfor (const stmt of statements) {\n\t\t\t\t\t\t\t\tawait client.query(stmt);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tawait recordMigration(\n\t\t\t\t\t\t\t\tclient as unknown as Pool,\n\t\t\t\t\t\t\t\tfile.name,\n\t\t\t\t\t\t\t\tfile.checksum,\n\t\t\t\t\t\t\t\tversion,\n\t\t\t\t\t\t\t\tdestructive,\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tawait client.query('COMMIT');\n\t\t\t\t\t\t} catch (applyError) {\n\t\t\t\t\t\t\tlet rollbackError: unknown;\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tawait client.query('ROLLBACK');\n\t\t\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t\t\trollbackError = e;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst primary = sanitizePgError(applyError);\n\t\t\t\t\t\t\tif (rollbackError !== undefined) {\n\t\t\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t\t\t` Note: rollback also failed: ${sanitizePgError(rollbackError).message}`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tthrow primary;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tappliedCount++;\n\t\t\t\t\t\tconsole.log(` ✅ Applied: ${file.name}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t`\\n✅ ${appliedCount} migration(s) applied successfully.`,\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t});\n\t\t}),\n\t);\n\nconst rollbackCommand = new Command('rollback')\n\t.description('Roll back applied migrations')\n\t.argument('<count>', 'Number of migrations to roll back')\n\t.requiredOption('-d, --db <url>', 'Database connection URL (required)')\n\t.option('--dir <path>', 'Migrations directory', DEFAULT_MIGRATIONS_DIR)\n\t.option('--force', 'Force rollback of destructive or empty DOWN migrations')\n\t.action(\n\t\t(\n\t\t\tcountArg: string,\n\t\t\toptions: { db: string; dir: string; force?: boolean },\n\t\t) => {\n\t\t\tconst count = Number.parseInt(countArg, 10);\n\t\t\tif (Number.isNaN(count) || count < 1) {\n\t\t\t\tconsole.error('❌ Count must be a positive integer');\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\n\t\t\treturn runMigrateAction(async () => {\n\t\t\t\tconsole.log(`⏪ Rolling back ${count} migration(s)`);\n\t\t\t\tconsole.log(` Database: ${redactDbUrl(options.db)}`);\n\t\t\t\tconsole.log(` Directory: ${options.dir}`);\n\t\t\t\tconsole.log('');\n\n\t\t\t\tawait withMigratePool(options.db, async (pool) => {\n\t\t\t\t\tawait ensureMigrationsTable(pool);\n\n\t\t\t\t\tawait withMigrationLock(pool, async (client) => {\n\t\t\t\t\t\t// Get applied migrations on the lock-holding client\n\t\t\t\t\t\tconst applied = await getAppliedMigrations(\n\t\t\t\t\t\t\tclient as unknown as Pool,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst sortedDesc = [...applied].sort((a, b) =>\n\t\t\t\t\t\t\tb.name.localeCompare(a.name),\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// SC-18: Validate count\n\t\t\t\t\t\tif (count > sortedDesc.length) {\n\t\t\t\t\t\t\tthrow new MigrationError(\n\t\t\t\t\t\t\t\t`Cannot roll back ${count} migration(s) — only ${sortedDesc.length} applied`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst toRollback = sortedDesc.slice(0, count);\n\n\t\t\t\t\t\t// Load migration files from disk\n\t\t\t\t\t\tconst files = scanMigrationFiles(options.dir);\n\t\t\t\t\t\tconst fileMap = new Map(files.map((f) => [f.name, f]));\n\n\t\t\t\t\t\tlet rolledBack = 0;\n\t\t\t\t\t\tfor (const record of toRollback) {\n\t\t\t\t\t\t\tconst file = fileMap.get(record.name);\n\t\t\t\t\t\t\tif (!file) {\n\t\t\t\t\t\t\t\tthrow new MigrationError(\n\t\t\t\t\t\t\t\t\t`Migration file not found on disk: ${record.name}`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// SC-17: Verify checksum\n\t\t\t\t\t\t\tif (file.checksum !== record.checksum) {\n\t\t\t\t\t\t\t\tthrow new MigrationError(\n\t\t\t\t\t\t\t\t\t`Checksum mismatch for ${record.name}\\n` +\n\t\t\t\t\t\t\t\t\t\t` Expected: ${record.checksum}\\n` +\n\t\t\t\t\t\t\t\t\t\t` Got: ${file.checksum}\\n` +\n\t\t\t\t\t\t\t\t\t\t'\\nMigration file has been modified since it was applied.',\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Parse DOWN section\n\t\t\t\t\t\t\tconst parsed = parseMigrationFile(file.content);\n\n\t\t\t\t\t\t\t// SC-10/ERR-01: No DOWN section\n\t\t\t\t\t\t\tif (!parsed.hasDown) {\n\t\t\t\t\t\t\t\tthrow new MigrationError(\n\t\t\t\t\t\t\t\t\t`Migration ${record.name} has no DOWN section\\n` +\n\t\t\t\t\t\t\t\t\t\t' Cannot roll back a migration without a DOWN section.',\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// SC-11/ERR-04: Empty DOWN section\n\t\t\t\t\t\t\tconst downStmts = parsed.downStatements.filter(\n\t\t\t\t\t\t\t\t(s) => s.length > 0 && !s.startsWith('-- '),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tif (downStmts.length === 0 && !options.force) {\n\t\t\t\t\t\t\t\tthrow new MigrationError(\n\t\t\t\t\t\t\t\t\t`Migration ${record.name} has an empty DOWN section\\n` +\n\t\t\t\t\t\t\t\t\t\t' Use --force to roll back anyway.',\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// SC-19: Destructive DOWN check\n\t\t\t\t\t\t\tif (isDestructiveDown(parsed.downStatements) && !options.force) {\n\t\t\t\t\t\t\t\tthrow new MigrationError(\n\t\t\t\t\t\t\t\t\t`Migration ${record.name} has destructive DOWN operations\\n` +\n\t\t\t\t\t\t\t\t\t\t' Use --force to proceed with destructive rollback.',\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Atomic: DOWN SQL + remove record in ONE transaction on the lock-holding client\n\t\t\t\t\t\t\tif (downStmts.length > 0 || options.force) {\n\t\t\t\t\t\t\t\tconsole.log(` Rolling back: ${record.name}...`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tawait client.query('BEGIN');\n\n\t\t\t\t\t\t\t\tfor (const stmt of downStmts) {\n\t\t\t\t\t\t\t\t\tawait client.query(stmt);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tawait removeMigrationRecord(\n\t\t\t\t\t\t\t\t\tclient as unknown as Pool,\n\t\t\t\t\t\t\t\t\trecord.name,\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\tawait client.query('COMMIT');\n\t\t\t\t\t\t\t} catch (rollbackError) {\n\t\t\t\t\t\t\t\tlet rollbackCleanupError: unknown;\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tawait client.query('ROLLBACK');\n\t\t\t\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t\t\t\trollbackCleanupError = e;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tconst primary = sanitizePgError(rollbackError);\n\t\t\t\t\t\t\t\tif (rollbackCleanupError !== undefined) {\n\t\t\t\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t\t\t\t` Note: rollback also failed: ${sanitizePgError(rollbackCleanupError).message}`,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tthrow primary;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\trolledBack++;\n\t\t\t\t\t\t\tconsole.log(` ✅ Rolled back: ${record.name}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t`\\n✅ ${rolledBack} migration(s) rolled back successfully.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t);\n\nconst statusCommand = new Command('status')\n\t.description('Show migration status')\n\t.requiredOption('-d, --db <url>', 'Database connection URL (required)')\n\t.option('--dir <path>', 'Migrations directory', DEFAULT_MIGRATIONS_DIR)\n\t.option('--json', 'Output as JSON')\n\t.action((options: { db: string; dir: string; json?: boolean }) =>\n\t\trunMigrateAction(async () => {\n\t\t\tawait withMigratePool(options.db, async (pool) => {\n\t\t\t\tawait ensureMigrationsTable(pool);\n\n\t\t\t\tconst applied = await getAppliedMigrations(pool);\n\t\t\t\tconst appliedMap = new Map(applied.map((m) => [m.name, m]));\n\n\t\t\t\tconst files = scanMigrationFiles(options.dir);\n\n\t\t\t\tconst statuses: Array<{\n\t\t\t\t\tname: string;\n\t\t\t\t\tstatus: 'pending' | 'applied' | 'checksum_mismatch' | 'missing_file';\n\t\t\t\t\tappliedAt?: Date;\n\t\t\t\t}> = files.map((file) => {\n\t\t\t\t\tconst record = appliedMap.get(file.name);\n\t\t\t\t\tif (record === undefined) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tname: file.name,\n\t\t\t\t\t\t\tstatus: 'pending' as const,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tif (record.checksum !== file.checksum) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tname: file.name,\n\t\t\t\t\t\t\tstatus: 'checksum_mismatch' as const,\n\t\t\t\t\t\t\tappliedAt: record.appliedAt,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\tname: file.name,\n\t\t\t\t\t\tstatus: 'applied' as const,\n\t\t\t\t\t\tappliedAt: record.appliedAt,\n\t\t\t\t\t};\n\t\t\t\t});\n\n\t\t\t\t// Also show applied migrations that don't have files anymore\n\t\t\t\tfor (const record of applied) {\n\t\t\t\t\tif (!files.some((f) => f.name === record.name)) {\n\t\t\t\t\t\tstatuses.push({\n\t\t\t\t\t\t\tname: record.name,\n\t\t\t\t\t\t\tstatus: 'missing_file' as const,\n\t\t\t\t\t\t\tappliedAt: record.appliedAt,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (options.json) {\n\t\t\t\t\tconsole.log(JSON.stringify(statuses, null, 2));\n\t\t\t\t} else {\n\t\t\t\t\tconsole.log('Migration Status');\n\t\t\t\t\tconsole.log(` Database: ${redactDbUrl(options.db)}`);\n\t\t\t\t\tconsole.log(` Directory: ${options.dir}`);\n\t\t\t\t\tconsole.log('');\n\n\t\t\t\t\tif (statuses.length === 0) {\n\t\t\t\t\t\tconsole.log('No migrations found.');\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfor (const s of statuses) {\n\t\t\t\t\t\t\tconst icon =\n\t\t\t\t\t\t\t\ts.status === 'applied'\n\t\t\t\t\t\t\t\t\t? '✅'\n\t\t\t\t\t\t\t\t\t: s.status === 'pending'\n\t\t\t\t\t\t\t\t\t\t? '⏳'\n\t\t\t\t\t\t\t\t\t\t: s.status === 'checksum_mismatch'\n\t\t\t\t\t\t\t\t\t\t\t? '⚠️'\n\t\t\t\t\t\t\t\t\t\t\t: '❓';\n\t\t\t\t\t\t\tconst detail =\n\t\t\t\t\t\t\t\t'appliedAt' in s && s.appliedAt\n\t\t\t\t\t\t\t\t\t? ` (applied: ${s.appliedAt.toISOString()})`\n\t\t\t\t\t\t\t\t\t: '';\n\t\t\t\t\t\t\tconsole.log(` ${icon} ${s.name} — ${s.status}${detail}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst pendingCount = statuses.filter(\n\t\t\t\t\t\t\t(s) => s.status === 'pending',\n\t\t\t\t\t\t).length;\n\t\t\t\t\t\tconst appliedCount = statuses.filter(\n\t\t\t\t\t\t\t(s) => s.status === 'applied',\n\t\t\t\t\t\t).length;\n\n\t\t\t\t\t\tconsole.log('');\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t`Total: ${statuses.length} | Applied: ${appliedCount} | Pending: ${pendingCount}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}),\n\t);\n\n// ============================================================================\n// Main Command\n// ============================================================================\n\nexport const migrateCommand = new Command('migrate')\n\t.description('Database migration management')\n\t.addCommand(devCommand)\n\t.addCommand(applyCommand)\n\t.addCommand(rollbackCommand)\n\t.addCommand(statusCommand);\n","/**\n * Migration File — File I/O, naming conventions, checksums.\n *\n * Handles reading/writing migration SQL files and computing\n * SHA-256 checksums for tamper detection.\n */\n\nimport { createHash } from 'node:crypto';\nimport {\n\texistsSync,\n\tmkdirSync,\n\treaddirSync,\n\treadFileSync,\n\twriteFileSync,\n} from 'node:fs';\nimport { join, resolve } from 'node:path';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface MigrationFile {\n\t/** Filename (e.g., \"0001_create_users.sql\") */\n\treadonly name: string;\n\t/** Full path to the file */\n\treadonly path: string;\n\t/** SQL content */\n\treadonly content: string;\n\t/** SHA-256 checksum of content */\n\treadonly checksum: string;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Default directory for migration files */\nexport const DEFAULT_MIGRATIONS_DIR = 'migrations';\n\n/** Migration file naming pattern: NNNN_description.sql */\nconst MIGRATION_FILENAME_PATTERN = /^\\d{4}_[\\w-]+\\.sql$/;\n\n// ============================================================================\n// Checksum\n// ============================================================================\n\n/**\n * Compute SHA-256 checksum of content.\n */\nexport function computeChecksum(content: string): string {\n\treturn createHash('sha256').update(content, 'utf-8').digest('hex');\n}\n\n// ============================================================================\n// File Naming\n// ============================================================================\n\n/**\n * Generate the next migration filename.\n *\n * @param existingFiles - List of existing migration filenames\n * @param description - Human-readable description (e.g., \"create_users\")\n * @returns Filename (e.g., \"0001_create_users.sql\")\n */\nexport function generateMigrationFilename(\n\texistingFiles: readonly string[],\n\tdescription: string,\n): string {\n\tconst maxNum = existingFiles.reduce((max, f) => {\n\t\tconst match = f.match(/^(\\d{4})/);\n\t\treturn match?.[1] ? Math.max(max, Number.parseInt(match[1], 10)) : max;\n\t}, 0);\n\n\tconst nextNum = String(maxNum + 1).padStart(4, '0');\n\tconst sanitized = description\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9_-]/g, '_')\n\t\t.replace(/_+/g, '_')\n\t\t.replace(/^_|_$/g, '');\n\n\treturn `${nextNum}_${sanitized || 'migration'}.sql`;\n}\n\n// ============================================================================\n// File I/O\n// ============================================================================\n\n/**\n * Ensure the migrations directory exists.\n */\nexport function ensureMigrationsDir(dir: string): string {\n\tconst fullPath = resolve(dir);\n\tif (!existsSync(fullPath)) {\n\t\tmkdirSync(fullPath, { recursive: true });\n\t}\n\treturn fullPath;\n}\n\n/**\n * Scan migrations directory and return all valid migration files,\n * sorted by name (lexicographic = chronological with zero-padded numbers).\n */\nexport function scanMigrationFiles(dir: string): readonly MigrationFile[] {\n\tconst fullPath = resolve(dir);\n\tif (!existsSync(fullPath)) {\n\t\treturn [];\n\t}\n\n\tconst entries = readdirSync(fullPath)\n\t\t.filter((f) => MIGRATION_FILENAME_PATTERN.test(f))\n\t\t.sort();\n\n\treturn entries.map((name) => {\n\t\tconst filePath = join(fullPath, name);\n\t\tconst content = readFileSync(filePath, 'utf-8');\n\t\treturn {\n\t\t\tname,\n\t\t\tpath: filePath,\n\t\t\tcontent,\n\t\t\tchecksum: computeChecksum(content),\n\t\t};\n\t});\n}\n\n/**\n * Write a migration file to the migrations directory.\n *\n * @returns The created MigrationFile\n */\nexport function writeMigrationFile(\n\tdir: string,\n\tfilename: string,\n\tcontent: string,\n): MigrationFile {\n\tconst fullDir = ensureMigrationsDir(dir);\n\tconst filePath = join(fullDir, filename);\n\twriteFileSync(filePath, content, 'utf-8');\n\n\treturn {\n\t\tname: filename,\n\t\tpath: filePath,\n\t\tcontent,\n\t\tchecksum: computeChecksum(content),\n\t};\n}\n","/**\n * Push Command — Schema Provisioning\n *\n * dbsp push - Push schema to database.\n * Additive by default: only creates missing objects.\n * With --drop: recreates from scratch (preserves _dbsp_migrations).\n */\n\nimport {\n\tcompareSchemata,\n\tgenerateDDL,\n\tgenerateMigrationSQL,\n\tintrospect,\n} from '@dbsp/adapter-pgsql';\nimport { Command } from 'commander';\nimport { executeDdl } from '../ddl-executor.js';\nimport { createDbConnection, redactDbUrl } from '../utils/db-utils.js';\nimport { loadSchema } from '../utils/schema-loader.js';\n\n/** Table name reserved for migration history — never dropped by push. */\nconst MIGRATIONS_TABLE = '_dbsp_migrations';\n\nexport const pushCommand = new Command('push')\n\t.description('Push schema to database (additive by default)')\n\t.option(\n\t\t'-s, --schema <path>',\n\t\t'Path to schema file (default: dbsp.schema.ts)',\n\t)\n\t.requiredOption('-d, --db <url>', 'Database connection URL (required)')\n\t.option('--schema-name <name>', 'Database schema name (default: public)')\n\t.option('--drop', 'Drop and recreate all objects (preserves migrations)')\n\t.option('--dry-run', 'Print SQL without executing')\n\t.option('--json', 'Output as JSON')\n\t.action(\n\t\tasync (options: {\n\t\t\tschema?: string;\n\t\t\tdb: string;\n\t\t\tschemaName?: string;\n\t\t\tdrop?: boolean;\n\t\t\tdryRun?: boolean;\n\t\t\tjson?: boolean;\n\t\t}) => {\n\t\t\tconst schemaPath = options.schema ?? 'dbsp.schema.ts';\n\t\t\tconst redactedUrl = redactDbUrl(options.db);\n\n\t\t\tif (!options.json) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`🚀 Pushing schema: ${schemaPath}${options.drop ? ' (with --drop)' : ''}`,\n\t\t\t\t);\n\t\t\t\tconsole.log(` Database: ${redactedUrl}`);\n\t\t\t\tif (options.schemaName) {\n\t\t\t\t\tconsole.log(` Schema: ${options.schemaName}`);\n\t\t\t\t}\n\t\t\t\tif (options.dryRun) {\n\t\t\t\t\tconsole.log(` Mode: DRY RUN (no changes will be applied)`);\n\t\t\t\t}\n\t\t\t\tconsole.log('');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst loaded = await loadSchema(schemaPath);\n\t\t\t\tconst schemaModel = loaded.model;\n\n\t\t\t\tconst { pool } = await createDbConnection(options.db);\n\n\t\t\t\ttry {\n\t\t\t\t\tif (options.drop) {\n\t\t\t\t\t\t// --drop mode: generate full DDL with drops, excluding _dbsp_migrations\n\t\t\t\t\t\tconst statements = generateDDL(schemaModel, {\n\t\t\t\t\t\t\tincludeDropStatements: true,\n\t\t\t\t\t\t\t...(options.schemaName ? { schemaName: options.schemaName } : {}),\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// SEC-7: Escape MIGRATIONS_TABLE before interpolating into RegExp\n\t\t\t\t\t\tconst escapedTable = MIGRATIONS_TABLE.replace(\n\t\t\t\t\t\t\t/[.*+?^${}()|[\\]\\\\]/g,\n\t\t\t\t\t\t\t'\\\\$&',\n\t\t\t\t\t\t);\n\t\t\t\t\t\t// CC-11: Token-based check — match DROP TABLE ... \"tableName\" (no greedy .*\n\t\t\t\t\t\t// across statement boundaries). The pattern anchors on the quoted table name\n\t\t\t\t\t\t// appearing anywhere in the statement, which is safe for single-statement\n\t\t\t\t\t\t// inputs (generateDDL returns one statement per array entry).\n\t\t\t\t\t\tconst migrationsPattern = new RegExp(\n\t\t\t\t\t\t\t`DROP\\\\s+TABLE(?:\\\\s+IF\\\\s+EXISTS)?(?:\\\\s+\"[^\"]*\"\\\\s*\\\\.)?\\\\s*\"${escapedTable}\"`,\n\t\t\t\t\t\t\t'i',\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst filtered = statements.filter(\n\t\t\t\t\t\t\t(stmt) => !migrationsPattern.test(stmt),\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\toutputResult(filtered, options);\n\n\t\t\t\t\t\tconst result = await executeDdl(pool, filtered, {\n\t\t\t\t\t\t\t...(options.dryRun !== undefined\n\t\t\t\t\t\t\t\t? { dryRun: options.dryRun }\n\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// CC-1: --drop --json must emit JSON to stdout on success\n\t\t\t\t\t\tif (options.json) {\n\t\t\t\t\t\t\tconst droppedTables = statements\n\t\t\t\t\t\t\t\t.filter((s) => /DROP\\s+TABLE/i.test(s))\n\t\t\t\t\t\t\t\t.filter((s) => !migrationsPattern.test(s))\n\t\t\t\t\t\t\t\t.map((s) => {\n\t\t\t\t\t\t\t\t\t// M6: handle CASCADE between last quoted identifier and semicolon\n\t\t\t\t\t\t\t\t\t// e.g. DROP TABLE IF EXISTS \"public\".\"users\" CASCADE;\n\t\t\t\t\t\t\t\t\tconst m = s.match(/\"([^\"]+)\"\\s*(?:CASCADE\\s*)?;?\\s*$/i);\n\t\t\t\t\t\t\t\t\treturn m ? m[1] : s;\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t.filter((t): t is string => t !== undefined);\n\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\tJSON.stringify(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tstatus: options.dryRun ? 'dry-run' : 'dropped',\n\t\t\t\t\t\t\t\t\t\ttables: droppedTables,\n\t\t\t\t\t\t\t\t\t\ttablesDropped: droppedTables.length,\n\t\t\t\t\t\t\t\t\t\tstatementsExecuted: result.statementsExecuted,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\t\t2,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} else if (!options.dryRun) {\n\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t`\\n✅ Push complete: ${result.statementsExecuted} statements executed`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Additive mode: introspect → diff → generate migration SQL (additive only)\n\t\t\t\t\t\tconst dbModel = await introspect(pool, {\n\t\t\t\t\t\t\t...(options.schemaName ? { schema: options.schemaName } : {}),\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tconst diff = compareSchemata(schemaModel, dbModel);\n\n\t\t\t\t\t\t// Generate SQL for additive changes only (no destructive)\n\t\t\t\t\t\tconst statements = generateMigrationSQL(diff, {\n\t\t\t\t\t\t\tincludeDestructive: false,\n\t\t\t\t\t\t\t...(options.schemaName ? { schemaName: options.schemaName } : {}),\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// Collect warnings for skipped non-additive changes\n\t\t\t\t\t\tconst skippedChanges = diff.changes.filter((c) => c.destructive);\n\n\t\t\t\t\t\tif (!options.json && skippedChanges.length > 0) {\n\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t`⚠️ ${skippedChanges.length} non-additive change(s) skipped:`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tfor (const change of skippedChanges) {\n\t\t\t\t\t\t\t\tconsole.log(` - ${change.details}`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconsole.log('');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (statements.length === 0) {\n\t\t\t\t\t\t\tif (options.json) {\n\t\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t\tJSON.stringify(\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tstatus: 'up-to-date',\n\t\t\t\t\t\t\t\t\t\t\tstatementsExecuted: 0,\n\t\t\t\t\t\t\t\t\t\t\tskippedChanges: skippedChanges.length,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\t\t\t2,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tconsole.log('✅ Database is up to date — nothing to push.');\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// EH-14: return instead of process.exit(0) so pool.end() in finally runs\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\toutputResult(statements, options);\n\n\t\t\t\t\t\tconst result = await executeDdl(pool, statements, {\n\t\t\t\t\t\t\t...(options.dryRun !== undefined\n\t\t\t\t\t\t\t\t? { dryRun: options.dryRun }\n\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tif (options.json) {\n\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\tJSON.stringify(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tstatus: options.dryRun ? 'dry-run' : 'applied',\n\t\t\t\t\t\t\t\t\t\tstatementsExecuted: result.statementsExecuted,\n\t\t\t\t\t\t\t\t\t\tskippedChanges: skippedChanges.length,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\t\t2,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} else if (!options.dryRun) {\n\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t`\\n✅ Push complete: ${result.statementsExecuted} statement(s) executed`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// EH-14: return so finally runs pool.end() before the outer success path\n\t\t\t\t} finally {\n\t\t\t\t\tawait pool.end();\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst message =\n\t\t\t\t\terror instanceof Error ? error.message : 'Unknown error occurred';\n\t\t\t\t// CC-2+EH-7: If --json, error goes to stdout as JSON; otherwise stderr\n\t\t\t\tif (options.json) {\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\tJSON.stringify({ status: 'error', error: message }, null, 2),\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error(`❌ Error: ${message}`);\n\t\t\t\t}\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n\n/**\n * Output SQL statements (dry-run or --json mode).\n */\nfunction outputResult(\n\tstatements: readonly string[],\n\toptions: { dryRun?: boolean; json?: boolean },\n): void {\n\tif (options.dryRun && !options.json) {\n\t\tconsole.log(`-- Dry run: ${statements.length} statement(s)\\n`);\n\t\tfor (const stmt of statements) {\n\t\t\tconsole.log(`${stmt};\\n`);\n\t\t}\n\t}\n}\n","/**\n * DDL Executor — Transaction-wrapped DDL execution.\n *\n * Shared by `dbsp push` and `dbsp migrate apply`.\n * Executes an array of SQL statements inside a single transaction.\n */\n\nimport type { Pool, PoolClient } from 'pg';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface DdlExecutionResult {\n\t/** Number of statements executed */\n\tstatementsExecuted: number;\n\t/** Whether execution was a dry-run (no actual changes) */\n\tdryRun: boolean;\n}\n\n// ============================================================================\n// Executor\n// ============================================================================\n\n/**\n * Execute DDL statements in a transaction.\n *\n * @param pool - pg Pool instance\n * @param statements - SQL statements to execute\n * @param options - Execution options\n * @returns Execution result\n */\nexport async function executeDdl(\n\tpool: Pool,\n\tstatements: readonly string[],\n\toptions?: { dryRun?: boolean },\n): Promise<DdlExecutionResult> {\n\tif (statements.length === 0) {\n\t\treturn { statementsExecuted: 0, dryRun: options?.dryRun ?? false };\n\t}\n\n\tif (options?.dryRun) {\n\t\treturn { statementsExecuted: statements.length, dryRun: true };\n\t}\n\n\tlet client: PoolClient | undefined;\n\ttry {\n\t\tclient = await pool.connect();\n\t\tawait client.query('BEGIN');\n\n\t\tfor (const stmt of statements) {\n\t\t\tawait client.query(stmt);\n\t\t}\n\n\t\tawait client.query('COMMIT');\n\t\treturn { statementsExecuted: statements.length, dryRun: false };\n\t} catch (error) {\n\t\tif (client) {\n\t\t\tawait client.query('ROLLBACK');\n\t\t}\n\t\tthrow error;\n\t} finally {\n\t\tif (client) {\n\t\t\tclient.release();\n\t\t}\n\t}\n}\n","/**\n * DX-030 Block 1: REPL Command\n *\n * dbsp repl [--schema <path>] - Launch interactive REPL.\n * CLI-022: Batch mode support with --eval and --input options.\n */\n\nimport { readFileSync } from 'node:fs';\nimport { Command } from 'commander';\nimport { config } from '../config.js';\nimport { loadSchema, loadSchemaFromCwd } from '../utils/schema-loader.js';\n\nexport interface ReplOptions {\n\tschema?: string;\n\t/** CLI-020: Database connection URL for execution mode */\n\tdb?: string;\n\t/** CLI-022: Single query to evaluate (batch mode) */\n\teval?: string;\n\t/** CLI-022: File containing queries to execute (batch mode, one per line) */\n\tinput?: string;\n\t/** CLI-022: Output format for batch mode */\n\tformat?: 'text' | 'json';\n\t/** DEMO-E2E: Assertion file for validating query output */\n\tassert?: string;\n\t/** CLI-IMPORT: SQL files to import before queries (injected as .import commands) */\n\timport?: string[];\n\t/** CLI-USE: PostgreSQL schema to use (injected as .use command) */\n\tuse?: string;\n\t/** CLI-MUT: Start REPL with parse mode enabled */\n\tparse?: boolean;\n\t/** CLI-MUT: Start REPL with exec mode enabled */\n\texec?: boolean;\n\t/** CLI-CONFIG: Custom config file path (default: ~/.dbsp/config.json) */\n\tconfig?: string;\n\t/** CLI-CASING: Column naming convention (describes DB column casing) */\n\tcasing?: 'snake' | 'camel' | 'none';\n}\n\nexport const replCommand = new Command('repl')\n\t.description('Launch interactive REPL for exploring schema and queries')\n\t.option('-s, --schema <path>', 'Path to schema file (default: auto-detect)')\n\t.option(\n\t\t'-d, --db <url>',\n\t\t'PostgreSQL connection URL for execution mode (e.g., postgres://localhost/mydb)',\n\t)\n\t.option('-e, --eval <query>', 'Execute a single query and exit (batch mode)')\n\t.option(\n\t\t'-i, --input <file>',\n\t\t'Execute queries from file, one per line (batch mode)',\n\t)\n\t.option(\n\t\t'-f, --format <format>',\n\t\t'Output format for batch mode: text (default) or json',\n\t\t'text',\n\t)\n\t.option(\n\t\t'-a, --assert <file>',\n\t\t'Assertion file to validate query output (requires --input)',\n\t)\n\t.option(\n\t\t'--import <files...>',\n\t\t'SQL files to import before queries (equivalent to .import commands)',\n\t)\n\t.option(\n\t\t'--use <schema>',\n\t\t'PostgreSQL schema to use (equivalent to .use command)',\n\t)\n\t.option('--parse', 'Start REPL with parse mode enabled (.parse toggle)')\n\t.option('--exec', 'Start REPL with exec mode enabled (.exec toggle)')\n\t.option(\n\t\t'--casing <type>',\n\t\t'Column naming convention: snake (DB uses snake_case), camel (DB uses camelCase), none (preserve as-is)',\n\t)\n\t.option(\n\t\t'-c, --config <path>',\n\t\t'Custom config file path (default: ~/.dbsp/config.json)',\n\t)\n\t.action(async (options: ReplOptions) => {\n\t\t// Set custom config path if provided\n\t\tif (options.config) {\n\t\t\tconfig.setConfigPath(options.config);\n\t\t}\n\t\t// Load config\n\t\tconfig.load();\n\t\ttry {\n\t\t\t// Load schema\n\t\t\tlet schemaPath: string;\n\t\t\tlet schema: Awaited<ReturnType<typeof loadSchema>>;\n\n\t\t\tif (options.schema) {\n\t\t\t\tschema = await loadSchema(options.schema);\n\t\t\t\tschemaPath = options.schema;\n\t\t\t} else {\n\t\t\t\tconst result = await loadSchemaFromCwd();\n\t\t\t\tschema = result.schema;\n\t\t\t\tschemaPath = result.path;\n\t\t\t}\n\n\t\t\t// DEMO-E2E: Validate --assert requires --input\n\t\t\tif (options.assert && !options.input) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'--assert requires --input (assertion files validate query output from input files)',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// CLI-IMPORT: Validate that --import requires batch mode (SQL file execution)\n\t\t\t// Note: --use, --parse, --exec work in interactive mode (REPL state setup)\n\t\t\tif (options.import && !options.eval && !options.input) {\n\t\t\t\tthrow new Error('--import requires batch mode (--eval or --input)');\n\t\t\t}\n\n\t\t\t// CLI-CASING: Map --casing flag to dbCasing (intuitive: describes DB columns)\n\t\t\tconst dbCasing =\n\t\t\t\toptions.casing === 'snake'\n\t\t\t\t\t? ('snake_case' as const)\n\t\t\t\t\t: options.casing === 'camel'\n\t\t\t\t\t\t? ('camelCase' as const)\n\t\t\t\t\t\t: options.casing === 'none'\n\t\t\t\t\t\t\t? ('preserve' as const)\n\t\t\t\t\t\t\t: undefined;\n\n\t\t\t// CLI-022: Batch mode - execute queries without interactive UI\n\t\t\tif (options.eval || options.input) {\n\t\t\t\tconst { runBatchMode } = await import('../repl/batch.js');\n\t\t\t\tconst queries: string[] = [];\n\n\t\t\t\t// CLI-USE: Inject .use command first (schema scoping)\n\t\t\t\tif (options.use) {\n\t\t\t\t\tqueries.push(`.use ${options.use}`);\n\t\t\t\t}\n\n\t\t\t\t// CLI-IMPORT: Inject .import commands for SQL files\n\t\t\t\tif (options.import) {\n\t\t\t\t\tfor (const file of options.import) {\n\t\t\t\t\t\tqueries.push(`.import ${file}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (options.eval) {\n\t\t\t\t\tqueries.push(options.eval);\n\t\t\t\t}\n\n\t\t\t\tif (options.input) {\n\t\t\t\t\t// EH-2: Map ENOENT to a friendly error instead of raw stack trace\n\t\t\t\t\tlet content: string;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcontent = readFileSync(options.input, 'utf-8');\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tconst isNotFound =\n\t\t\t\t\t\t\terr instanceof Error &&\n\t\t\t\t\t\t\t'code' in err &&\n\t\t\t\t\t\t\t(err as NodeJS.ErrnoException).code === 'ENOENT';\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\tisNotFound\n\t\t\t\t\t\t\t\t? `Input file not found: ${options.input}`\n\t\t\t\t\t\t\t\t: `Failed to read input file: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tqueries.push(...content.split('\\n'));\n\t\t\t\t}\n\n\t\t\t\tawait runBatchMode({\n\t\t\t\t\tqueries,\n\t\t\t\t\tschema,\n\t\t\t\t\tschemaPath,\n\t\t\t\t\tformat: options.format ?? 'text',\n\t\t\t\t\t...(options.db && { databaseUrl: options.db }),\n\t\t\t\t\t...(options.assert && { assertFile: options.assert }),\n\t\t\t\t\t...(dbCasing && { dbCasing }),\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Dynamic import to avoid loading React/Ink for other commands\n\t\t\tconst { startRepl } = await import('../repl/index.js');\n\t\t\t// CLI-020: Pass database URL if provided\n\t\t\t// CLI-MUT: Pass initial REPL state options\n\t\t\tawait startRepl({\n\t\t\t\tschema,\n\t\t\t\tschemaPath,\n\t\t\t\t...(options.db && { databaseUrl: options.db }),\n\t\t\t\t...(options.use && { initialSchemaName: options.use }),\n\t\t\t\t...(options.parse && { initialParseMode: true }),\n\t\t\t\t...(options.exec && { initialExecMode: true }),\n\t\t\t\t...(dbCasing && { dbCasing }),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tconsole.error(`❌ ${message}`);\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n","/**\n * Verify Command — Schema Drift Detection\n *\n * dbsp verify - Compare schema vs real database using the comparison engine.\n * Detects drift in tables, columns, types, nullable, defaults, FKs, indexes.\n */\n\nimport { compareSchemata, introspect } from '@dbsp/adapter-pgsql';\nimport { Command } from 'commander';\nimport { createDbConnection, redactDbUrl } from '../utils/db-utils.js';\nimport { loadSchema } from '../utils/schema-loader.js';\nimport { formatVerifyResult, verifyFromDiff } from '../verifier.js';\n\nexport const verifyCommand = new Command('verify')\n\t.description('Compare schema vs real database (drift detection)')\n\t.option(\n\t\t'-s, --schema <path>',\n\t\t'Path to schema file (default: dbsp.schema.ts)',\n\t)\n\t.requiredOption('-d, --db <url>', 'Database connection URL (required)')\n\t.option('--schema-name <name>', 'Database schema name (default: public)')\n\t.option('--json', 'Output as JSON')\n\t.action(\n\t\tasync (options: {\n\t\t\tschema?: string;\n\t\t\tdb: string;\n\t\t\tschemaName?: string;\n\t\t\tjson?: boolean;\n\t\t}) => {\n\t\t\tconst schemaPath = options.schema ?? 'dbsp.schema.ts';\n\n\t\t\tconst redactedUrl = redactDbUrl(options.db);\n\n\t\t\tif (!options.json) {\n\t\t\t\tconsole.log(`🔍 Verifying schema: ${schemaPath}`);\n\t\t\t\tconsole.log(` Database: ${redactedUrl}`);\n\t\t\t\tif (options.schemaName) {\n\t\t\t\t\tconsole.log(` Schema: ${options.schemaName}`);\n\t\t\t\t}\n\t\t\t\tconsole.log('');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// Load schema from file → ModelIR\n\t\t\t\tconst loaded = await loadSchema(schemaPath);\n\t\t\t\tconst schemaModel = loaded.model;\n\n\t\t\t\t// Connect to database\n\t\t\t\tconst { pool } = await createDbConnection(options.db);\n\n\t\t\t\ttry {\n\t\t\t\t\t// Introspect database → ModelIR\n\t\t\t\t\tconst dbModel = await introspect(pool, {\n\t\t\t\t\t\t...(options.schemaName ? { schema: options.schemaName } : {}),\n\t\t\t\t\t});\n\n\t\t\t\t\t// Compare using the comparison engine\n\t\t\t\t\tconst diff = compareSchemata(schemaModel, dbModel);\n\n\t\t\t\t\t// Convert to verify result\n\t\t\t\t\tconst schemaTables = Array.from(schemaModel.tables.keys());\n\t\t\t\t\tconst dbTables = Array.from(dbModel.tables.keys());\n\t\t\t\t\tconst result = verifyFromDiff(diff, schemaTables, dbTables);\n\n\t\t\t\t\t// Output\n\t\t\t\t\tif (options.json) {\n\t\t\t\t\t\t// Exclude the full diff meta from JSON output (too verbose)\n\t\t\t\t\t\tconst { diff: _diff, ...jsonResult } = result;\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\tJSON.stringify(\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t...jsonResult,\n\t\t\t\t\t\t\t\t\tsummary: diff.summary,\n\t\t\t\t\t\t\t\t\thasDestructive: diff.hasDestructive,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\t2,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconsole.log(formatVerifyResult(result));\n\t\t\t\t\t}\n\n\t\t\t\t\t// EH-14: set exit code; let finally run pool.end() before process exits\n\t\t\t\t\tprocess.exitCode = result.valid ? 0 : 1;\n\t\t\t\t\treturn;\n\t\t\t\t} finally {\n\t\t\t\t\t// Close database connection\n\t\t\t\t\tawait pool.end();\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst message =\n\t\t\t\t\terror instanceof Error ? error.message : 'Unknown error occurred';\n\t\t\t\t// CC-2+EH-7: If --json, error goes to stdout as JSON; otherwise stderr\n\t\t\t\tif (options.json) {\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\tJSON.stringify({ status: 'error', error: message }, null, 2),\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error(`❌ Error: ${message}`);\n\t\t\t\t}\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n","/**\n * Schema Verifier — Drift Detection via Comparison Engine\n *\n * Compares schema definition against real database using the\n * adapter's compareSchemata engine for full drift detection.\n *\n * Detects: tables, columns, types, nullable, defaults, FKs, indexes, PKs.\n */\n\nimport type { ChangeKind, SchemaChange, SchemaDiff } from '@dbsp/adapter-pgsql';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type DriftSeverity = 'error' | 'warning' | 'info';\n\nexport type DriftType =\n\t// Tables\n\t| 'missing_table_in_db'\n\t| 'missing_table_in_schema'\n\t// Columns\n\t| 'missing_column_in_db'\n\t| 'missing_column_in_schema'\n\t| 'type_mismatch'\n\t| 'nullable_mismatch'\n\t| 'default_mismatch'\n\t// Constraints\n\t| 'primary_key_mismatch'\n\t| 'missing_fk_in_db'\n\t| 'missing_fk_in_schema'\n\t| 'fk_on_delete_mismatch'\n\t// Indexes\n\t| 'missing_index_in_db'\n\t| 'missing_index_in_schema'\n\t// CHECK constraints\n\t| 'missing_check_in_db'\n\t| 'missing_check_in_schema'\n\t// ENUM types\n\t| 'missing_enum_in_db'\n\t| 'missing_enum_in_schema'\n\t| 'enum_value_mismatch'\n\t// Column enhancements\n\t| 'collation_mismatch'\n\t| 'identity_mismatch'\n\t// Comments\n\t| 'comment_mismatch'\n\t// Extensions & Sequences\n\t| 'missing_extension'\n\t| 'missing_sequence'\n\t| 'sequence_mismatch'\n\t// Constraints\n\t| 'constraint_validation_mismatch'\n\t// Row-Level Security\n\t| 'rls_enabled_mismatch'\n\t| 'missing_policy_in_db'\n\t| 'missing_policy_in_schema';\n\nexport interface DriftIssue {\n\t/** Issue severity */\n\tseverity: DriftSeverity;\n\t/** Human-readable message */\n\tmessage: string;\n\t/** Table affected (if any) */\n\ttable?: string;\n\t/** Column affected (if any) */\n\tcolumn?: string;\n\t/** Issue type for programmatic handling */\n\ttype: DriftType;\n}\n\nexport interface VerifyResult {\n\t/** Whether the schema matches the database */\n\tvalid: boolean;\n\t/** List of drift issues found */\n\tissues: DriftIssue[];\n\t/** Tables in schema */\n\tschemaTables: string[];\n\t/** Tables in database */\n\tdbTables: string[];\n\t/** Structured diff (for programmatic consumers) */\n\tdiff: SchemaDiff;\n}\n\n/**\n * @deprecated Use DbTableInfo from introspection instead.\n * Kept for backward compatibility with existing tests.\n */\nexport interface DbTableInfo {\n\tname: string;\n\tcolumns: DbColumnInfo[];\n}\n\n/**\n * @deprecated Use ColumnIR from introspection instead.\n */\nexport interface DbColumnInfo {\n\tname: string;\n\tdataType: string;\n\tisNullable: boolean;\n\tisPrimaryKey?: boolean;\n\thasDefault?: boolean;\n}\n\n// ============================================================================\n// Change → Drift Mapping\n// ============================================================================\n\nconst CHANGE_TO_DRIFT: Record<\n\tChangeKind,\n\t{ type: DriftType; severity: DriftSeverity }\n> = {\n\tcreate_table: { type: 'missing_table_in_db', severity: 'error' },\n\tdrop_table: { type: 'missing_table_in_schema', severity: 'warning' },\n\tadd_column: { type: 'missing_column_in_db', severity: 'error' },\n\tdrop_column: { type: 'missing_column_in_schema', severity: 'info' },\n\talter_column_type: { type: 'type_mismatch', severity: 'error' },\n\talter_column_nullable: { type: 'nullable_mismatch', severity: 'warning' },\n\talter_column_default: { type: 'default_mismatch', severity: 'warning' },\n\tadd_primary_key: { type: 'primary_key_mismatch', severity: 'error' },\n\tdrop_primary_key: { type: 'primary_key_mismatch', severity: 'error' },\n\tadd_foreign_key: { type: 'missing_fk_in_db', severity: 'error' },\n\tdrop_foreign_key: { type: 'missing_fk_in_schema', severity: 'warning' },\n\talter_foreign_key: { type: 'fk_on_delete_mismatch', severity: 'warning' },\n\tcreate_index: { type: 'missing_index_in_db', severity: 'warning' },\n\tdrop_index: { type: 'missing_index_in_schema', severity: 'info' },\n\t// CHECK constraints\n\tadd_check_constraint: { type: 'missing_check_in_db', severity: 'warning' },\n\tdrop_check_constraint: { type: 'missing_check_in_schema', severity: 'info' },\n\t// ENUM types\n\tcreate_enum: { type: 'missing_enum_in_db', severity: 'error' },\n\talter_enum_add_value: { type: 'enum_value_mismatch', severity: 'warning' },\n\tdrop_enum: { type: 'missing_enum_in_schema', severity: 'warning' },\n\t// Column enhancements\n\talter_column_collation: { type: 'collation_mismatch', severity: 'warning' },\n\talter_column_identity: { type: 'identity_mismatch', severity: 'warning' },\n\t// Comments\n\tadd_comment: { type: 'comment_mismatch', severity: 'info' },\n\tdrop_comment: { type: 'comment_mismatch', severity: 'info' },\n\t// Extensions & Sequences\n\tcreate_extension: { type: 'missing_extension', severity: 'error' },\n\tdrop_extension: { type: 'missing_extension', severity: 'info' },\n\tcreate_sequence: { type: 'missing_sequence', severity: 'warning' },\n\talter_sequence: { type: 'sequence_mismatch', severity: 'warning' },\n\tdrop_sequence: { type: 'missing_sequence', severity: 'info' },\n\t// Constraints\n\tvalidate_constraint: {\n\t\ttype: 'constraint_validation_mismatch',\n\t\tseverity: 'warning',\n\t},\n\t// Row-Level Security\n\tenable_rls: { type: 'rls_enabled_mismatch', severity: 'warning' },\n\tdisable_rls: { type: 'rls_enabled_mismatch', severity: 'warning' },\n\tcreate_policy: { type: 'missing_policy_in_db', severity: 'warning' },\n\tdrop_policy: { type: 'missing_policy_in_schema', severity: 'warning' },\n};\n\nfunction changeToDriftIssue(change: SchemaChange): DriftIssue {\n\tconst mapping = CHANGE_TO_DRIFT[change.kind] ?? {\n\t\ttype: 'type_mismatch' as DriftType,\n\t\tseverity: 'warning' as DriftSeverity,\n\t};\n\treturn {\n\t\tseverity: mapping.severity,\n\t\ttype: mapping.type,\n\t\ttable: change.table,\n\t\t...(change.column !== undefined ? { column: change.column } : {}),\n\t\tmessage: change.details,\n\t};\n}\n\n// ============================================================================\n// Verification (from SchemaDiff)\n// ============================================================================\n\n/**\n * Convert a SchemaDiff into a VerifyResult.\n *\n * @param diff - Structured diff from compareSchemata()\n * @param schemaTables - Table names in schema (for backward compat)\n * @param dbTables - Table names in database (for backward compat)\n */\nexport function verifyFromDiff(\n\tdiff: SchemaDiff,\n\tschemaTables: string[],\n\tdbTables: string[],\n): VerifyResult {\n\tconst issues = diff.changes.map(changeToDriftIssue);\n\n\t// Sort by severity (error > warning > info)\n\tconst severityOrder: Record<DriftSeverity, number> = {\n\t\terror: 0,\n\t\twarning: 1,\n\t\tinfo: 2,\n\t};\n\tissues.sort(\n\t\t(a: DriftIssue, b: DriftIssue) =>\n\t\t\tseverityOrder[a.severity] - severityOrder[b.severity],\n\t);\n\n\tconst hasErrors = issues.some((i: DriftIssue) => i.severity === 'error');\n\n\treturn {\n\t\tvalid: !hasErrors,\n\t\tissues,\n\t\tschemaTables,\n\t\tdbTables,\n\t\tdiff,\n\t};\n}\n\n// ============================================================================\n// Format\n// ============================================================================\n\n/**\n * Format verification result for CLI output.\n */\nexport function formatVerifyResult(result: VerifyResult): string {\n\tconst lines: string[] = [];\n\n\tif (result.valid) {\n\t\tlines.push('✅ Schema matches database');\n\t} else {\n\t\tlines.push('❌ Schema drift detected');\n\t}\n\n\tlines.push('');\n\tlines.push(`Tables in schema: ${result.schemaTables.length}`);\n\tlines.push(`Tables in database: ${result.dbTables.length}`);\n\tlines.push('');\n\n\tif (result.issues.length === 0) {\n\t\tlines.push('No issues found.');\n\t} else {\n\t\tconst errors = result.issues.filter((i) => i.severity === 'error');\n\t\tconst warnings = result.issues.filter((i) => i.severity === 'warning');\n\t\tconst infos = result.issues.filter((i) => i.severity === 'info');\n\n\t\tif (errors.length > 0) {\n\t\t\tlines.push(`❌ ${errors.length} error(s):`);\n\t\t\tfor (const issue of errors) {\n\t\t\t\tlines.push(` ${issue.message}`);\n\t\t\t}\n\t\t\tlines.push('');\n\t\t}\n\n\t\tif (warnings.length > 0) {\n\t\t\tlines.push(`⚠️ ${warnings.length} warning(s):`);\n\t\t\tfor (const issue of warnings) {\n\t\t\t\tlines.push(` ${issue.message}`);\n\t\t\t}\n\t\t\tlines.push('');\n\t\t}\n\n\t\tif (infos.length > 0) {\n\t\t\tlines.push(`ℹ️ ${infos.length} info:`);\n\t\t\tfor (const issue of infos) {\n\t\t\t\tlines.push(` ${issue.message}`);\n\t\t\t}\n\t\t\tlines.push('');\n\t\t}\n\t}\n\n\treturn lines.join('\\n');\n}\n"],"mappings":"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","names":["Command","outputPath","mkdirSync","writeFileSync","dirname","resolve","Command","Command","introspect","resolve","mkdirSync","dirname","writeFileSync","Command","mkdirSync","writeFileSync","resolve","Command","compareSchemata","generateMigrationSQL","introspect","Command","Command","introspect","compareSchemata","generateMigrationSQL","readFileSync","Command","Command","readFileSync","compareSchemata","introspect","Command","Command","introspect","compareSchemata","Command"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/commands/generate.ts","../src/commands/introspect.ts","../src/commands/migrate.ts","../src/migration-file.ts","../src/commands/push.ts","../src/ddl-executor.ts","../src/commands/repl.ts","../src/commands/verify.ts","../src/verifier.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * ARCH-002 Block 3: CLI Scaffold\n *\n * dbsp CLI - Schema-first code generation for db-semantic-planner.\n */\n\nimport { Command, CommanderError } from 'commander';\nimport { generateCommand } from './commands/generate.js';\nimport { introspectCommand } from './commands/introspect.js';\nimport { migrateCommand } from './commands/migrate.js';\nimport { pushCommand } from './commands/push.js';\nimport { replCommand } from './commands/repl.js';\nimport { verifyCommand } from './commands/verify.js';\n\nconst program = new Command();\n\nprogram\n\t.name('dbsp')\n\t.description('Schema-first code generation for db-semantic-planner')\n\t.version('0.0.1');\n\n// Register commands\nprogram.addCommand(generateCommand);\nprogram.addCommand(introspectCommand);\nprogram.addCommand(migrateCommand);\nprogram.addCommand(pushCommand);\nprogram.addCommand(replCommand);\nprogram.addCommand(verifyCommand);\n\n// CC-15: Intercept Commander parse errors so --json commands receive a JSON\n// error object on stdout instead of a plain-text usage message.\nprogram.exitOverride();\n\ntry {\n\tprogram.parse();\n} catch (err) {\n\t// Commander throws CommanderError for --help, --version, and parse errors.\n\t// Exit 0 for informational outputs (help/version); only exit 1 for real errors.\n\tif (err instanceof CommanderError && err.exitCode === 0) {\n\t\tprocess.exit(0);\n\t}\n\tconst message = err instanceof Error ? err.message : 'Command parse error';\n\tif (process.argv.includes('--json')) {\n\t\tconsole.log(JSON.stringify({ status: 'error', error: message }, null, 2));\n\t} else {\n\t\tconsole.error(`❌ ${message}`);\n\t}\n\tprocess.exit(1);\n}\n","/**\n * ARCH-002 Block 3+4+5: Generate Command\n * CLI-DDL: Added DDL generation target\n * ARCH-005: Migrated to schema() API, removed legacy generators\n *\n * dbsp generate <target> - Generate code from schema.\n *\n * Targets:\n * - ddl: Generate SQL DDL (CREATE TABLE statements)\n *\n * Deprecated targets (removed in ARCH-005):\n * - manifest: Was for legacy defineSchema() format\n * - kysely: Was for legacy defineSchema() format\n */\n\nimport { mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { Command } from 'commander';\nimport { loadSchema, loadSchemaFromCwd } from '../utils/schema-loader.js';\n\nexport const generateCommand = new Command('generate')\n\t.description('Generate code from schema')\n\t.argument('<target>', 'Target to generate: ddl')\n\t.option('-s, --schema <path>', 'Path to schema file (default: auto-detect)')\n\t.option('-o, --out <dir>', 'Output directory (default: ./generated/<target>)')\n\t.option('--output <dir>', 'Output directory (alias for --out)')\n\t.option('--drop', 'Include DROP TABLE IF EXISTS statements (ddl only)')\n\t.option('--schema-name <name>', 'Database schema name (ddl only)')\n\t.option(\n\t\t'--dialect <name>',\n\t\t'Database dialect: postgresql | mysql | sqlite | mssql (default: postgresql)',\n\t)\n\t.option(\n\t\t'--casing <type>',\n\t\t'Column naming: snake | camel | none (default: based on dialect)',\n\t)\n\t.action(\n\t\tasync (\n\t\t\ttarget: string,\n\t\t\toptions: {\n\t\t\t\tschema?: string;\n\t\t\t\tout?: string;\n\t\t\t\toutput?: string;\n\t\t\t\tdrop?: boolean;\n\t\t\t\tschemaName?: string;\n\t\t\t\tdialect?: string;\n\t\t\t\tcasing?: 'snake' | 'camel' | 'none';\n\t\t\t},\n\t\t) => {\n\t\t\ttry {\n\t\t\t\t// Load schema (ARCH-005: only schema() format supported)\n\t\t\t\tlet schema: Awaited<ReturnType<typeof loadSchema>>;\n\t\t\t\tlet schemaPath: string;\n\n\t\t\t\tif (options.schema) {\n\t\t\t\t\tschema = await loadSchema(options.schema);\n\t\t\t\t\tschemaPath = options.schema;\n\t\t\t\t} else {\n\t\t\t\t\tconst result = await loadSchemaFromCwd();\n\t\t\t\t\tschema = result.schema;\n\t\t\t\t\tschemaPath = result.path;\n\t\t\t\t}\n\n\t\t\t\t// For DDL without --output, we output to stdout so use stderr for info\n\t\t\t\tconst outputPath = options.out ?? options.output;\n\t\t\t\tconst useStdout = target === 'ddl' && !outputPath;\n\t\t\t\tconst log = useStdout ? console.error : console.log;\n\n\t\t\t\tlog(`📄 Loaded schema from: ${schemaPath}`);\n\n\t\t\t\t// Validate dialect option\n\t\t\t\tconst dialect = options.dialect ?? 'postgresql';\n\t\t\t\tif (dialect !== 'postgresql') {\n\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t`⚠️ Warning: Only 'postgresql' dialect is currently supported. Using postgresql.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Generate based on target\n\t\t\t\tswitch (target) {\n\t\t\t\t\tcase 'ddl': {\n\t\t\t\t\t\t// Determine casing: explicit option > dialect default > 'snake'\n\t\t\t\t\t\tconst casing = options.casing ?? 'snake';\n\n\t\t\t\t\t\t// Import adapter from adapter-pgsql (compile-only, no DB connection needed)\n\t\t\t\t\t\tconst { createPgsqlCompileOnlyAdapter } = await import(\n\t\t\t\t\t\t\t'@dbsp/adapter-pgsql'\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\tconst dbCasing =\n\t\t\t\t\t\t\tcasing === 'snake'\n\t\t\t\t\t\t\t\t? ('snake_case' as const)\n\t\t\t\t\t\t\t\t: ('preserve' as const);\n\t\t\t\t\t\tconst adapter = createPgsqlCompileOnlyAdapter({\n\t\t\t\t\t\t\tdbCasing,\n\t\t\t\t\t\t\t...(options.schemaName ? { schemaName: options.schemaName } : {}),\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// ARCH-005: Use schema.model directly (already ModelIR)\n\t\t\t\t\t\t\tconst ddlStatements = adapter.generateDDL(schema.model, {\n\t\t\t\t\t\t\t\t...(options.drop !== undefined && {\n\t\t\t\t\t\t\t\t\tincludeDropStatements: options.drop,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tconst ddlContent = ddlStatements.join('\\n\\n');\n\t\t\t\t\t\t\tconst outputPath = options.out ?? options.output;\n\n\t\t\t\t\t\t\tif (outputPath) {\n\t\t\t\t\t\t\t\t// Write to file if --output is specified\n\t\t\t\t\t\t\t\tconst outPath = outputPath.endsWith('.sql')\n\t\t\t\t\t\t\t\t\t? resolve(process.cwd(), outputPath)\n\t\t\t\t\t\t\t\t\t: resolve(process.cwd(), outputPath, 'schema.sql');\n\n\t\t\t\t\t\t\t\tmkdirSync(dirname(outPath), { recursive: true });\n\t\t\t\t\t\t\t\twriteFileSync(outPath, ddlContent, 'utf-8');\n\n\t\t\t\t\t\t\t\tconsole.log(`✅ Generated DDL: ${outPath}`);\n\t\t\t\t\t\t\t\tconsole.log(` Tables: ${schema.tableNames.length}`);\n\t\t\t\t\t\t\t\tconsole.log(` Statements: ${ddlStatements.length}`);\n\t\t\t\t\t\t\t\tconsole.log(` Casing: ${casing}`);\n\t\t\t\t\t\t\t\tif (options.drop) {\n\t\t\t\t\t\t\t\t\tconsole.log(` Includes DROP statements`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tif (options.schemaName) {\n\t\t\t\t\t\t\t\t\tconsole.log(` Schema: ${options.schemaName}`);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t// Output DDL to stdout (for piping)\n\t\t\t\t\t\t\t\t// Info messages (schema loaded) went to stderr\n\t\t\t\t\t\t\t\tconsole.log(ddlContent);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tcase 'manifest':\n\t\t\t\t\tcase 'kysely':\n\t\t\t\t\t\t// EH-10: Throw instead of process.exit(1) inside try — outer catch handles exit\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Target '${target}' has been removed in ARCH-005. ` +\n\t\t\t\t\t\t\t\t`These generators required the legacy defineSchema() format. ` +\n\t\t\t\t\t\t\t\t`Use 'ddl' target for SQL generation.`,\n\t\t\t\t\t\t);\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\t// EH-10: Throw instead of process.exit(1) inside try — outer catch handles exit\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t`Unknown target: ${target}. Available targets: ddl`,\n\t\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tconsole.error(`❌ ${message}`);\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n","/**\n * CLI-DDL: Introspect Command\n *\n * dbsp introspect - Generate schema.ts from database introspection.\n */\n\nimport { mkdirSync, writeFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport { Command } from 'commander';\nimport {\n\tgenerateSchemaFile,\n\ttype SchemaCodegenOptions,\n} from '../generators/schema-codegen.js';\nimport { createDbConnection, redactDbUrl } from '../utils/db-utils.js';\n\nexport const introspectCommand = new Command('introspect')\n\t.description('Generate schema.ts from database introspection')\n\t.requiredOption('-d, --db <url>', 'Database connection URL (required)')\n\t.option('-o, --out <file>', 'Output schema file', './dbsp.schema.ts')\n\t.option('--schema-name <name>', 'Database schema name', 'public')\n\t.option(\n\t\t'--exclude <patterns>',\n\t\t'Tables to exclude (comma-separated glob patterns)',\n\t\t'_migrations,_prisma*,pg_*',\n\t)\n\t.option('--include <patterns>', 'Tables to include (comma-separated)')\n\t.option('--no-db-type-comments', 'Omit original DB type comments')\n\t.option(\n\t\t'--db-casing <casing>',\n\t\t'Database column casing (snake_case → camelCase in generated code)',\n\t\t'snake_case',\n\t)\n\t.action(\n\t\tasync (options: {\n\t\t\tdb: string;\n\t\t\tout: string;\n\t\t\tschemaName: string;\n\t\t\texclude: string;\n\t\t\tinclude?: string;\n\t\t\tdbTypeComments: boolean;\n\t\t\tdbCasing: 'snake_case' | 'camelCase' | 'preserve';\n\t\t}) => {\n\t\t\tconst redactedUrl = redactDbUrl(options.db);\n\n\t\t\tconsole.log(`🔍 Introspecting database: ${redactedUrl}`);\n\t\t\tconsole.log(` Schema: ${options.schemaName}`);\n\t\t\tif (options.exclude) {\n\t\t\t\tconsole.log(` Excluding: ${options.exclude}`);\n\t\t\t}\n\t\t\tconsole.log('');\n\n\t\t\ttry {\n\t\t\t\t// Connect to database\n\t\t\t\tconst { pool } = await createDbConnection(options.db);\n\n\t\t\t\ttry {\n\t\t\t\t\t// Import introspect from adapter-pgsql\n\t\t\t\t\tconst { introspect } = await import('@dbsp/adapter-pgsql');\n\n\t\t\t\t\t// Build introspection options from CLI flags\n\t\t\t\t\tconst excludePatterns = options.exclude\n\t\t\t\t\t\t? options.exclude.split(',').map((s) => s.trim())\n\t\t\t\t\t\t: undefined;\n\t\t\t\t\tconst includePatterns = options.include\n\t\t\t\t\t\t? options.include.split(',').map((s) => s.trim())\n\t\t\t\t\t\t: undefined;\n\n\t\t\t\t\t// Introspect the database directly (returns IntrospectedModelIR)\n\t\t\t\t\tconst model = await introspect(pool, {\n\t\t\t\t\t\tschema: options.schemaName,\n\t\t\t\t\t\t...(excludePatterns ? { exclude: excludePatterns } : {}),\n\t\t\t\t\t\t...(includePatterns ? { include: includePatterns } : {}),\n\t\t\t\t\t});\n\n\t\t\t\t\t// Report what we found\n\t\t\t\t\tconst tableCount = model.tables.size;\n\t\t\t\t\tconst relationCount = model.relations.size;\n\t\t\t\t\tconst hierarchyCount = model.hierarchies?.length ?? 0;\n\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t`📊 Found ${tableCount} tables, ${relationCount} relations, ${hierarchyCount} hierarchies`,\n\t\t\t\t\t);\n\t\t\t\t\tif (model.warnings?.length) {\n\t\t\t\t\t\tfor (const w of model.warnings) {\n\t\t\t\t\t\t\tconsole.log(` ⚠️ ${w}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tconsole.log('');\n\n\t\t\t\t\t// Generate schema file — pass metadata from introspection\n\t\t\t\t\tconst codegenOptions: SchemaCodegenOptions = {\n\t\t\t\t\t\tsourceUrl: options.db,\n\t\t\t\t\t\tincludeDbTypeComments: options.dbTypeComments,\n\t\t\t\t\t\twarnings: model.warnings,\n\t\t\t\t\t\tintrospectedAt: model.introspectedAt,\n\t\t\t\t\t\tdbCasing: options.dbCasing,\n\t\t\t\t\t};\n\n\t\t\t\t\tconst schemaCode = generateSchemaFile(model, codegenOptions);\n\n\t\t\t\t\t// Write output file\n\t\t\t\t\tconst outPath = resolve(process.cwd(), options.out);\n\t\t\t\t\tmkdirSync(dirname(outPath), { recursive: true });\n\t\t\t\t\twriteFileSync(outPath, schemaCode, 'utf-8');\n\n\t\t\t\t\tconsole.log(`✅ Generated schema: ${outPath}`);\n\t\t\t\t\tconsole.log(` Tables: ${tableCount}`);\n\t\t\t\t\tconsole.log(` Relations: ${relationCount}`);\n\t\t\t\t} finally {\n\t\t\t\t\t// Close database connection\n\t\t\t\t\tawait pool.end();\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\t\tconsole.error(`❌ ${message}`);\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n","/**\n * Migrate Command — Migration Infrastructure\n *\n * dbsp migrate dev - Generate migration from schema diff\n * dbsp migrate apply - Apply pending migrations\n * dbsp migrate rollback - Roll back applied migrations\n * dbsp migrate status - Show migration status\n */\n\nimport {\n\tcompareSchemata,\n\tensureMigrationsTable,\n\tgenerateMigrationFile,\n\tgenerateMigrationSQL,\n\tgetAppliedMigrations,\n\tgetNextSchemaVersion,\n\tintrospect,\n\tisDestructiveDown,\n\tparseMigrationFile,\n\trecordMigration,\n\tremoveMigrationRecord,\n\twithMigrationLock,\n} from '@dbsp/adapter-pgsql';\nimport { Command } from 'commander';\nimport type { Pool } from 'pg';\nimport {\n\tDEFAULT_MIGRATIONS_DIR,\n\tgenerateMigrationFilename,\n\tscanMigrationFiles,\n\twriteMigrationFile,\n} from '../migration-file.js';\nimport { createDbConnection, redactDbUrl } from '../utils/db-utils.js';\nimport { loadSchema } from '../utils/schema-loader.js';\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Typed error for migration failures.\n * Distinguishes user-facing errors (validation, logic) from unexpected errors.\n */\nexport class MigrationError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = 'MigrationError';\n\t}\n}\n\n/**\n * Sanitize a pg error for user-facing output.\n *\n * PostgreSQL errors may carry schema/table/row details in their message text.\n * For pg errors (identifiable by SQLSTATE `.code`), we emit a sanitized message\n * keyed only by the SQLSTATE code. The raw message is available via DEBUG=dbsp.\n *\n * @param err - Any caught value\n * @returns An Error instance safe to display to the user\n */\nexport function sanitizePgError(err: unknown): Error {\n\tif (err instanceof Error) {\n\t\t// pg errors carry a SQLSTATE `.code` property\n\t\tconst code = (err as Error & { code?: string }).code;\n\t\tif (typeof code === 'string' && /^[0-9A-Z]{5}$/.test(code)) {\n\t\t\tif (process.env.DEBUG?.includes('dbsp')) {\n\t\t\t\tconsole.error(`[DEBUG] pg error detail: ${err.message}`);\n\t\t\t}\n\t\t\treturn new MigrationError(`Migration failed: database error ${code}`);\n\t\t}\n\t\treturn err;\n\t}\n\treturn new Error(String(err));\n}\n\n/**\n * DRY lifecycle wrapper: create pool → run fn → end pool on success or error.\n * Replaces the repeated try { ... } finally { pool.end() } pattern across commands.\n *\n * process.exit is called OUTSIDE the pool lifecycle so the lock-holding client\n * (from withMigrationLock) is always released before the process terminates.\n */\nexport async function withMigratePool<T>(\n\tdbUrl: string,\n\tfn: (pool: Pool) => Promise<T>,\n): Promise<T> {\n\tconst { pool } = await createDbConnection(dbUrl);\n\ttry {\n\t\treturn await fn(pool);\n\t} finally {\n\t\tlet endError: unknown;\n\t\ttry {\n\t\t\tawait pool.end();\n\t\t} catch (e) {\n\t\t\tendError = e;\n\t\t}\n\t\tif (endError !== undefined) {\n\t\t\t// Cleanup failure is non-fatal — log it but don't mask the original error\n\t\t\tconsole.error(\n\t\t\t\t`Warning: pool.end() failed: ${endError instanceof Error ? endError.message : String(endError)}`,\n\t\t\t);\n\t\t}\n\t}\n}\n\n/**\n * Top-level error handler for migrate commands.\n * Ensures process.exit is only called AFTER pool cleanup (via withMigratePool).\n */\nasync function runMigrateAction(fn: () => Promise<void>): Promise<void> {\n\ttry {\n\t\tawait fn();\n\t} catch (error) {\n\t\tif (error instanceof Error) {\n\t\t\tconsole.error(`❌ Error: ${error.message}`);\n\t\t} else {\n\t\t\tconsole.error('❌ Unknown error occurred');\n\t\t}\n\t\tprocess.exit(1);\n\t}\n}\n\n/**\n * Returns true if a migration statement contains at least one executable SQL\n * line (non-empty, not a comment). Statements that consist entirely of blank\n * lines and `--` comment lines are skipped; statements that begin with a\n * comment header but also contain real SQL are kept (PostgreSQL ignores the\n * comment, so the header is harmless and preserves intent).\n */\nexport function hasExecutableSql(stmt: string): boolean {\n\treturn stmt.split('\\n').some((line) => {\n\t\tconst t = line.trim();\n\t\treturn t.length > 0 && !t.startsWith('--');\n\t});\n}\n\n// ============================================================================\n// Subcommands\n// ============================================================================\n\nconst devCommand = new Command('dev')\n\t.description('Generate a migration from schema changes')\n\t.option(\n\t\t'-s, --schema <path>',\n\t\t'Path to schema file (default: dbsp.schema.ts)',\n\t)\n\t.requiredOption('-d, --db <url>', 'Database connection URL (required)')\n\t.option('--schema-name <name>', 'Database schema name (default: public)')\n\t.option('--dir <path>', 'Migrations directory', DEFAULT_MIGRATIONS_DIR)\n\t.option('-n, --name <description>', 'Migration description', 'migration')\n\t.option('--allow-destructive', 'Include destructive changes (drops)')\n\t.action(\n\t\t(options: {\n\t\t\tschema?: string;\n\t\t\tdb: string;\n\t\t\tschemaName?: string;\n\t\t\tdir: string;\n\t\t\tname: string;\n\t\t\tallowDestructive?: boolean;\n\t\t}) =>\n\t\t\trunMigrateAction(async () => {\n\t\t\t\tconst schemaPath = options.schema ?? 'dbsp.schema.ts';\n\n\t\t\t\tconsole.log(`📝 Generating migration: ${schemaPath}`);\n\t\t\t\tconsole.log(` Database: ${redactDbUrl(options.db)}`);\n\t\t\t\tconsole.log('');\n\n\t\t\t\tconst loaded = await loadSchema(schemaPath);\n\t\t\t\tconst schemaModel = loaded.model;\n\n\t\t\t\tawait withMigratePool(options.db, async (pool) => {\n\t\t\t\t\tconst dbModel = await introspect(pool, {\n\t\t\t\t\t\t...(options.schemaName ? { schema: options.schemaName } : {}),\n\t\t\t\t\t});\n\n\t\t\t\t\tconst diff = compareSchemata(schemaModel, dbModel);\n\n\t\t\t\t\tif (diff.changes.length === 0) {\n\t\t\t\t\t\tconsole.log('✅ No changes detected — database matches schema.');\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Check for destructive changes\n\t\t\t\t\tif (diff.hasDestructive && !options.allowDestructive) {\n\t\t\t\t\t\tconst destructive = diff.changes.filter((c) => c.destructive);\n\t\t\t\t\t\tthrow new MigrationError(\n\t\t\t\t\t\t\t`${destructive.length} destructive change(s) detected:\\n` +\n\t\t\t\t\t\t\t\tdestructive.map((c) => ` - ${c.details}`).join('\\n') +\n\t\t\t\t\t\t\t\t'\\n\\nUse --allow-destructive to include these changes.',\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\n\t\t\t\t\tconst sqlOptions = {\n\t\t\t\t\t\tincludeDestructive: options.allowDestructive ?? false,\n\t\t\t\t\t\t...(options.schemaName ? { schemaName: options.schemaName } : {}),\n\t\t\t\t\t};\n\n\t\t\t\t\tconst statements = generateMigrationSQL(diff, sqlOptions);\n\n\t\t\t\t\tif (statements.length === 0) {\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t'✅ No migration needed — all changes are non-actionable.',\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Generate migration file with UP + DOWN sections\n\t\t\t\t\tconst existingFiles = scanMigrationFiles(options.dir).map(\n\t\t\t\t\t\t(f) => f.name,\n\t\t\t\t\t);\n\t\t\t\t\tconst filename = generateMigrationFilename(\n\t\t\t\t\t\texistingFiles,\n\t\t\t\t\t\toptions.name,\n\t\t\t\t\t);\n\t\t\t\t\tconst content = generateMigrationFile(diff, {\n\t\t\t\t\t\t...sqlOptions,\n\t\t\t\t\t\tname: filename,\n\t\t\t\t\t});\n\n\t\t\t\t\tconst file = writeMigrationFile(options.dir, filename, content);\n\n\t\t\t\t\tconsole.log(`✅ Migration created: ${file.path}`);\n\t\t\t\t\tconsole.log(` Statements: ${statements.length}`);\n\t\t\t\t\tconsole.log(` Checksum: ${file.checksum.slice(0, 12)}...`);\n\t\t\t\t});\n\t\t\t}),\n\t);\n\nconst applyCommand = new Command('apply')\n\t.description('Apply pending migrations')\n\t.requiredOption('-d, --db <url>', 'Database connection URL (required)')\n\t.option('--dir <path>', 'Migrations directory', DEFAULT_MIGRATIONS_DIR)\n\t.option('--dry-run', 'Show pending migrations without applying')\n\t.action((options: { db: string; dir: string; dryRun?: boolean }) =>\n\t\trunMigrateAction(async () => {\n\t\t\tconsole.log('🔄 Applying migrations');\n\t\t\tconsole.log(` Database: ${redactDbUrl(options.db)}`);\n\t\t\tconsole.log(` Directory: ${options.dir}`);\n\t\t\tconsole.log('');\n\n\t\t\tawait withMigratePool(options.db, async (pool) => {\n\t\t\t\t// Ensure tracking table exists before acquiring lock\n\t\t\t\tawait ensureMigrationsTable(pool);\n\n\t\t\t\tawait withMigrationLock(pool, async (client) => {\n\t\t\t\t\t// Get applied migrations on the lock-holding client\n\t\t\t\t\tconst applied = await getAppliedMigrations(client as unknown as Pool);\n\t\t\t\t\tconst appliedMap = new Map(applied.map((m) => [m.name, m.checksum]));\n\n\t\t\t\t\t// Scan migration files\n\t\t\t\t\tconst files = scanMigrationFiles(options.dir);\n\n\t\t\t\t\tif (files.length === 0) {\n\t\t\t\t\t\tconsole.log(`No migration files found in ${options.dir}`);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Validate checksums for already-applied migrations\n\t\t\t\t\tfor (const file of files) {\n\t\t\t\t\t\tconst existingChecksum = appliedMap.get(file.name);\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\texistingChecksum !== undefined &&\n\t\t\t\t\t\t\texistingChecksum !== file.checksum\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tthrow new MigrationError(\n\t\t\t\t\t\t\t\t`Checksum mismatch for ${file.name}\\n` +\n\t\t\t\t\t\t\t\t\t` Expected: ${existingChecksum}\\n` +\n\t\t\t\t\t\t\t\t\t` Got: ${file.checksum}\\n` +\n\t\t\t\t\t\t\t\t\t'\\nMigration file has been tampered with after being applied.',\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Find pending migrations\n\t\t\t\t\tconst pending = files.filter((f) => !appliedMap.has(f.name));\n\n\t\t\t\t\tif (pending.length === 0) {\n\t\t\t\t\t\tconsole.log('✅ All migrations already applied.');\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (options.dryRun) {\n\t\t\t\t\t\tconsole.log(`${pending.length} pending migration(s):`);\n\t\t\t\t\t\tfor (const file of pending) {\n\t\t\t\t\t\t\tconsole.log(` - ${file.name}`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Apply each pending migration atomically (DDL + record in one transaction)\n\t\t\t\t\tlet appliedCount = 0;\n\t\t\t\t\tfor (const file of pending) {\n\t\t\t\t\t\tconsole.log(` Applying: ${file.name}...`);\n\n\t\t\t\t\t\t// Parse UP section from file\n\t\t\t\t\t\tconst parsed = parseMigrationFile(file.content);\n\t\t\t\t\t\tconst statements = parsed.upStatements.filter(hasExecutableSql);\n\n\t\t\t\t\t\t// Determine version and destructive flag before the transaction\n\t\t\t\t\t\t// (read from the lock-held client so we see a consistent view)\n\t\t\t\t\t\tconst version = await getNextSchemaVersion(\n\t\t\t\t\t\t\tclient as unknown as Pool,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst destructive = isDestructiveDown(parsed.downStatements);\n\n\t\t\t\t\t\t// Atomic: DDL + record in ONE transaction on the lock-holding client\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tawait client.query('BEGIN');\n\n\t\t\t\t\t\t\tfor (const stmt of statements) {\n\t\t\t\t\t\t\t\tawait client.query(stmt);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tawait recordMigration(\n\t\t\t\t\t\t\t\tclient as unknown as Pool,\n\t\t\t\t\t\t\t\tfile.name,\n\t\t\t\t\t\t\t\tfile.checksum,\n\t\t\t\t\t\t\t\tversion,\n\t\t\t\t\t\t\t\tdestructive,\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\tawait client.query('COMMIT');\n\t\t\t\t\t\t} catch (applyError) {\n\t\t\t\t\t\t\tlet rollbackError: unknown;\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tawait client.query('ROLLBACK');\n\t\t\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t\t\trollbackError = e;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconst primary = sanitizePgError(applyError);\n\t\t\t\t\t\t\tif (rollbackError !== undefined) {\n\t\t\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t\t\t` Note: rollback also failed: ${sanitizePgError(rollbackError).message}`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tthrow primary;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tappliedCount++;\n\t\t\t\t\t\tconsole.log(` ✅ Applied: ${file.name}`);\n\t\t\t\t\t}\n\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t`\\n✅ ${appliedCount} migration(s) applied successfully.`,\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t});\n\t\t}),\n\t);\n\nconst rollbackCommand = new Command('rollback')\n\t.description('Roll back applied migrations')\n\t.argument('<count>', 'Number of migrations to roll back')\n\t.requiredOption('-d, --db <url>', 'Database connection URL (required)')\n\t.option('--dir <path>', 'Migrations directory', DEFAULT_MIGRATIONS_DIR)\n\t.option('--force', 'Force rollback of destructive or empty DOWN migrations')\n\t.action(\n\t\t(\n\t\t\tcountArg: string,\n\t\t\toptions: { db: string; dir: string; force?: boolean },\n\t\t) => {\n\t\t\tconst count = Number.parseInt(countArg, 10);\n\t\t\tif (Number.isNaN(count) || count < 1) {\n\t\t\t\tconsole.error('❌ Count must be a positive integer');\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\n\t\t\treturn runMigrateAction(async () => {\n\t\t\t\tconsole.log(`⏪ Rolling back ${count} migration(s)`);\n\t\t\t\tconsole.log(` Database: ${redactDbUrl(options.db)}`);\n\t\t\t\tconsole.log(` Directory: ${options.dir}`);\n\t\t\t\tconsole.log('');\n\n\t\t\t\tawait withMigratePool(options.db, async (pool) => {\n\t\t\t\t\tawait ensureMigrationsTable(pool);\n\n\t\t\t\t\tawait withMigrationLock(pool, async (client) => {\n\t\t\t\t\t\t// Get applied migrations on the lock-holding client\n\t\t\t\t\t\tconst applied = await getAppliedMigrations(\n\t\t\t\t\t\t\tclient as unknown as Pool,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst sortedDesc = [...applied].sort((a, b) =>\n\t\t\t\t\t\t\tb.name.localeCompare(a.name),\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\t// SC-18: Validate count\n\t\t\t\t\t\tif (count > sortedDesc.length) {\n\t\t\t\t\t\t\tthrow new MigrationError(\n\t\t\t\t\t\t\t\t`Cannot roll back ${count} migration(s) — only ${sortedDesc.length} applied`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst toRollback = sortedDesc.slice(0, count);\n\n\t\t\t\t\t\t// Load migration files from disk\n\t\t\t\t\t\tconst files = scanMigrationFiles(options.dir);\n\t\t\t\t\t\tconst fileMap = new Map(files.map((f) => [f.name, f]));\n\n\t\t\t\t\t\tlet rolledBack = 0;\n\t\t\t\t\t\tfor (const record of toRollback) {\n\t\t\t\t\t\t\tconst file = fileMap.get(record.name);\n\t\t\t\t\t\t\tif (!file) {\n\t\t\t\t\t\t\t\tthrow new MigrationError(\n\t\t\t\t\t\t\t\t\t`Migration file not found on disk: ${record.name}`,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// SC-17: Verify checksum\n\t\t\t\t\t\t\tif (file.checksum !== record.checksum) {\n\t\t\t\t\t\t\t\tthrow new MigrationError(\n\t\t\t\t\t\t\t\t\t`Checksum mismatch for ${record.name}\\n` +\n\t\t\t\t\t\t\t\t\t\t` Expected: ${record.checksum}\\n` +\n\t\t\t\t\t\t\t\t\t\t` Got: ${file.checksum}\\n` +\n\t\t\t\t\t\t\t\t\t\t'\\nMigration file has been modified since it was applied.',\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Parse DOWN section\n\t\t\t\t\t\t\tconst parsed = parseMigrationFile(file.content);\n\n\t\t\t\t\t\t\t// SC-10/ERR-01: No DOWN section\n\t\t\t\t\t\t\tif (!parsed.hasDown) {\n\t\t\t\t\t\t\t\tthrow new MigrationError(\n\t\t\t\t\t\t\t\t\t`Migration ${record.name} has no DOWN section\\n` +\n\t\t\t\t\t\t\t\t\t\t' Cannot roll back a migration without a DOWN section.',\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// SC-11/ERR-04: Empty DOWN section\n\t\t\t\t\t\t\tconst downStmts = parsed.downStatements.filter(hasExecutableSql);\n\t\t\t\t\t\t\tif (downStmts.length === 0 && !options.force) {\n\t\t\t\t\t\t\t\tthrow new MigrationError(\n\t\t\t\t\t\t\t\t\t`Migration ${record.name} has an empty DOWN section\\n` +\n\t\t\t\t\t\t\t\t\t\t' Use --force to roll back anyway.',\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// SC-19: Destructive DOWN check\n\t\t\t\t\t\t\tif (isDestructiveDown(parsed.downStatements) && !options.force) {\n\t\t\t\t\t\t\t\tthrow new MigrationError(\n\t\t\t\t\t\t\t\t\t`Migration ${record.name} has destructive DOWN operations\\n` +\n\t\t\t\t\t\t\t\t\t\t' Use --force to proceed with destructive rollback.',\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Atomic: DOWN SQL + remove record in ONE transaction on the lock-holding client\n\t\t\t\t\t\t\tif (downStmts.length > 0 || options.force) {\n\t\t\t\t\t\t\t\tconsole.log(` Rolling back: ${record.name}...`);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tawait client.query('BEGIN');\n\n\t\t\t\t\t\t\t\tfor (const stmt of downStmts) {\n\t\t\t\t\t\t\t\t\tawait client.query(stmt);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tawait removeMigrationRecord(\n\t\t\t\t\t\t\t\t\tclient as unknown as Pool,\n\t\t\t\t\t\t\t\t\trecord.name,\n\t\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t\tawait client.query('COMMIT');\n\t\t\t\t\t\t\t} catch (rollbackError) {\n\t\t\t\t\t\t\t\tlet rollbackCleanupError: unknown;\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tawait client.query('ROLLBACK');\n\t\t\t\t\t\t\t\t} catch (e) {\n\t\t\t\t\t\t\t\t\trollbackCleanupError = e;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tconst primary = sanitizePgError(rollbackError);\n\t\t\t\t\t\t\t\tif (rollbackCleanupError !== undefined) {\n\t\t\t\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t\t\t\t` Note: rollback also failed: ${sanitizePgError(rollbackCleanupError).message}`,\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tthrow primary;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\trolledBack++;\n\t\t\t\t\t\t\tconsole.log(` ✅ Rolled back: ${record.name}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t`\\n✅ ${rolledBack} migration(s) rolled back successfully.`,\n\t\t\t\t\t\t);\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t);\n\nconst statusCommand = new Command('status')\n\t.description('Show migration status')\n\t.requiredOption('-d, --db <url>', 'Database connection URL (required)')\n\t.option('--dir <path>', 'Migrations directory', DEFAULT_MIGRATIONS_DIR)\n\t.option('--json', 'Output as JSON')\n\t.action((options: { db: string; dir: string; json?: boolean }) =>\n\t\trunMigrateAction(async () => {\n\t\t\tawait withMigratePool(options.db, async (pool) => {\n\t\t\t\tawait ensureMigrationsTable(pool);\n\n\t\t\t\tconst applied = await getAppliedMigrations(pool);\n\t\t\t\tconst appliedMap = new Map(applied.map((m) => [m.name, m]));\n\n\t\t\t\tconst files = scanMigrationFiles(options.dir);\n\n\t\t\t\tconst statuses: Array<{\n\t\t\t\t\tname: string;\n\t\t\t\t\tstatus: 'pending' | 'applied' | 'checksum_mismatch' | 'missing_file';\n\t\t\t\t\tappliedAt?: Date;\n\t\t\t\t}> = files.map((file) => {\n\t\t\t\t\tconst record = appliedMap.get(file.name);\n\t\t\t\t\tif (record === undefined) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tname: file.name,\n\t\t\t\t\t\t\tstatus: 'pending' as const,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tif (record.checksum !== file.checksum) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tname: file.name,\n\t\t\t\t\t\t\tstatus: 'checksum_mismatch' as const,\n\t\t\t\t\t\t\tappliedAt: record.appliedAt,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\tname: file.name,\n\t\t\t\t\t\tstatus: 'applied' as const,\n\t\t\t\t\t\tappliedAt: record.appliedAt,\n\t\t\t\t\t};\n\t\t\t\t});\n\n\t\t\t\t// Also show applied migrations that don't have files anymore\n\t\t\t\tfor (const record of applied) {\n\t\t\t\t\tif (!files.some((f) => f.name === record.name)) {\n\t\t\t\t\t\tstatuses.push({\n\t\t\t\t\t\t\tname: record.name,\n\t\t\t\t\t\t\tstatus: 'missing_file' as const,\n\t\t\t\t\t\t\tappliedAt: record.appliedAt,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (options.json) {\n\t\t\t\t\tconsole.log(JSON.stringify(statuses, null, 2));\n\t\t\t\t} else {\n\t\t\t\t\tconsole.log('Migration Status');\n\t\t\t\t\tconsole.log(` Database: ${redactDbUrl(options.db)}`);\n\t\t\t\t\tconsole.log(` Directory: ${options.dir}`);\n\t\t\t\t\tconsole.log('');\n\n\t\t\t\t\tif (statuses.length === 0) {\n\t\t\t\t\t\tconsole.log('No migrations found.');\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfor (const s of statuses) {\n\t\t\t\t\t\t\tconst icon =\n\t\t\t\t\t\t\t\ts.status === 'applied'\n\t\t\t\t\t\t\t\t\t? '✅'\n\t\t\t\t\t\t\t\t\t: s.status === 'pending'\n\t\t\t\t\t\t\t\t\t\t? '⏳'\n\t\t\t\t\t\t\t\t\t\t: s.status === 'checksum_mismatch'\n\t\t\t\t\t\t\t\t\t\t\t? '⚠️'\n\t\t\t\t\t\t\t\t\t\t\t: '❓';\n\t\t\t\t\t\t\tconst detail =\n\t\t\t\t\t\t\t\t'appliedAt' in s && s.appliedAt\n\t\t\t\t\t\t\t\t\t? ` (applied: ${s.appliedAt.toISOString()})`\n\t\t\t\t\t\t\t\t\t: '';\n\t\t\t\t\t\t\tconsole.log(` ${icon} ${s.name} — ${s.status}${detail}`);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tconst pendingCount = statuses.filter(\n\t\t\t\t\t\t\t(s) => s.status === 'pending',\n\t\t\t\t\t\t).length;\n\t\t\t\t\t\tconst appliedCount = statuses.filter(\n\t\t\t\t\t\t\t(s) => s.status === 'applied',\n\t\t\t\t\t\t).length;\n\n\t\t\t\t\t\tconsole.log('');\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t`Total: ${statuses.length} | Applied: ${appliedCount} | Pending: ${pendingCount}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t});\n\t\t}),\n\t);\n\n// ============================================================================\n// Main Command\n// ============================================================================\n\nexport const migrateCommand = new Command('migrate')\n\t.description('Database migration management')\n\t.addCommand(devCommand)\n\t.addCommand(applyCommand)\n\t.addCommand(rollbackCommand)\n\t.addCommand(statusCommand);\n","/**\n * Migration File — File I/O, naming conventions, checksums.\n *\n * Handles reading/writing migration SQL files and computing\n * SHA-256 checksums for tamper detection.\n */\n\nimport { createHash } from 'node:crypto';\nimport {\n\texistsSync,\n\tmkdirSync,\n\treaddirSync,\n\treadFileSync,\n\twriteFileSync,\n} from 'node:fs';\nimport { join, resolve } from 'node:path';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface MigrationFile {\n\t/** Filename (e.g., \"0001_create_users.sql\") */\n\treadonly name: string;\n\t/** Full path to the file */\n\treadonly path: string;\n\t/** SQL content */\n\treadonly content: string;\n\t/** SHA-256 checksum of content */\n\treadonly checksum: string;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/** Default directory for migration files */\nexport const DEFAULT_MIGRATIONS_DIR = 'migrations';\n\n/** Migration file naming pattern: NNNN_description.sql */\nconst MIGRATION_FILENAME_PATTERN = /^\\d{4}_[\\w-]+\\.sql$/;\n\n// ============================================================================\n// Checksum\n// ============================================================================\n\n/**\n * Compute SHA-256 checksum of content.\n */\nexport function computeChecksum(content: string): string {\n\treturn createHash('sha256').update(content, 'utf-8').digest('hex');\n}\n\n// ============================================================================\n// File Naming\n// ============================================================================\n\n/**\n * Generate the next migration filename.\n *\n * @param existingFiles - List of existing migration filenames\n * @param description - Human-readable description (e.g., \"create_users\")\n * @returns Filename (e.g., \"0001_create_users.sql\")\n */\nexport function generateMigrationFilename(\n\texistingFiles: readonly string[],\n\tdescription: string,\n): string {\n\tconst maxNum = existingFiles.reduce((max, f) => {\n\t\tconst match = f.match(/^(\\d{4})/);\n\t\treturn match?.[1] ? Math.max(max, Number.parseInt(match[1], 10)) : max;\n\t}, 0);\n\n\tconst nextNum = String(maxNum + 1).padStart(4, '0');\n\tconst sanitized = description\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9_-]/g, '_')\n\t\t.replace(/_+/g, '_')\n\t\t.replace(/^_|_$/g, '');\n\n\treturn `${nextNum}_${sanitized || 'migration'}.sql`;\n}\n\n// ============================================================================\n// File I/O\n// ============================================================================\n\n/**\n * Ensure the migrations directory exists.\n */\nexport function ensureMigrationsDir(dir: string): string {\n\tconst fullPath = resolve(dir);\n\tif (!existsSync(fullPath)) {\n\t\tmkdirSync(fullPath, { recursive: true });\n\t}\n\treturn fullPath;\n}\n\n/**\n * Scan migrations directory and return all valid migration files,\n * sorted by name (lexicographic = chronological with zero-padded numbers).\n */\nexport function scanMigrationFiles(dir: string): readonly MigrationFile[] {\n\tconst fullPath = resolve(dir);\n\tif (!existsSync(fullPath)) {\n\t\treturn [];\n\t}\n\n\tconst entries = readdirSync(fullPath)\n\t\t.filter((f) => MIGRATION_FILENAME_PATTERN.test(f))\n\t\t.sort();\n\n\treturn entries.map((name) => {\n\t\tconst filePath = join(fullPath, name);\n\t\tconst content = readFileSync(filePath, 'utf-8');\n\t\treturn {\n\t\t\tname,\n\t\t\tpath: filePath,\n\t\t\tcontent,\n\t\t\tchecksum: computeChecksum(content),\n\t\t};\n\t});\n}\n\n/**\n * Write a migration file to the migrations directory.\n *\n * @returns The created MigrationFile\n */\nexport function writeMigrationFile(\n\tdir: string,\n\tfilename: string,\n\tcontent: string,\n): MigrationFile {\n\tconst fullDir = ensureMigrationsDir(dir);\n\tconst filePath = join(fullDir, filename);\n\twriteFileSync(filePath, content, 'utf-8');\n\n\treturn {\n\t\tname: filename,\n\t\tpath: filePath,\n\t\tcontent,\n\t\tchecksum: computeChecksum(content),\n\t};\n}\n","/**\n * Push Command — Schema Provisioning\n *\n * dbsp push - Push schema to database.\n * Additive by default: only creates missing objects.\n * With --drop: recreates from scratch (preserves _dbsp_migrations).\n */\n\nimport {\n\tcompareSchemata,\n\tgenerateDDL,\n\tgenerateMigrationSQL,\n\tintrospect,\n} from '@dbsp/adapter-pgsql';\nimport { Command } from 'commander';\nimport { executeDdl } from '../ddl-executor.js';\nimport { createDbConnection, redactDbUrl } from '../utils/db-utils.js';\nimport { loadSchema } from '../utils/schema-loader.js';\n\n/** Table name reserved for migration history — never dropped by push. */\nconst MIGRATIONS_TABLE = '_dbsp_migrations';\n\nexport const pushCommand = new Command('push')\n\t.description('Push schema to database (additive by default)')\n\t.option(\n\t\t'-s, --schema <path>',\n\t\t'Path to schema file (default: dbsp.schema.ts)',\n\t)\n\t.requiredOption('-d, --db <url>', 'Database connection URL (required)')\n\t.option('--schema-name <name>', 'Database schema name (default: public)')\n\t.option('--drop', 'Drop and recreate all objects (preserves migrations)')\n\t.option('--dry-run', 'Print SQL without executing')\n\t.option('--json', 'Output as JSON')\n\t.action(\n\t\tasync (options: {\n\t\t\tschema?: string;\n\t\t\tdb: string;\n\t\t\tschemaName?: string;\n\t\t\tdrop?: boolean;\n\t\t\tdryRun?: boolean;\n\t\t\tjson?: boolean;\n\t\t}) => {\n\t\t\tconst schemaPath = options.schema ?? 'dbsp.schema.ts';\n\t\t\tconst redactedUrl = redactDbUrl(options.db);\n\n\t\t\tif (!options.json) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`🚀 Pushing schema: ${schemaPath}${options.drop ? ' (with --drop)' : ''}`,\n\t\t\t\t);\n\t\t\t\tconsole.log(` Database: ${redactedUrl}`);\n\t\t\t\tif (options.schemaName) {\n\t\t\t\t\tconsole.log(` Schema: ${options.schemaName}`);\n\t\t\t\t}\n\t\t\t\tif (options.dryRun) {\n\t\t\t\t\tconsole.log(` Mode: DRY RUN (no changes will be applied)`);\n\t\t\t\t}\n\t\t\t\tconsole.log('');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst loaded = await loadSchema(schemaPath);\n\t\t\t\tconst schemaModel = loaded.model;\n\n\t\t\t\tconst { pool } = await createDbConnection(options.db);\n\n\t\t\t\ttry {\n\t\t\t\t\tif (options.drop) {\n\t\t\t\t\t\t// --drop mode: generate full DDL with drops, excluding _dbsp_migrations\n\t\t\t\t\t\tconst statements = generateDDL(schemaModel, {\n\t\t\t\t\t\t\tincludeDropStatements: true,\n\t\t\t\t\t\t\t...(options.schemaName ? { schemaName: options.schemaName } : {}),\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// SEC-7: Escape MIGRATIONS_TABLE before interpolating into RegExp\n\t\t\t\t\t\tconst escapedTable = MIGRATIONS_TABLE.replace(\n\t\t\t\t\t\t\t/[.*+?^${}()|[\\]\\\\]/g,\n\t\t\t\t\t\t\t'\\\\$&',\n\t\t\t\t\t\t);\n\t\t\t\t\t\t// CC-11: Token-based check — match DROP TABLE ... \"tableName\" (no greedy .*\n\t\t\t\t\t\t// across statement boundaries). The pattern anchors on the quoted table name\n\t\t\t\t\t\t// appearing anywhere in the statement, which is safe for single-statement\n\t\t\t\t\t\t// inputs (generateDDL returns one statement per array entry).\n\t\t\t\t\t\tconst migrationsPattern = new RegExp(\n\t\t\t\t\t\t\t`DROP\\\\s+TABLE(?:\\\\s+IF\\\\s+EXISTS)?(?:\\\\s+\"[^\"]*\"\\\\s*\\\\.)?\\\\s*\"${escapedTable}\"`,\n\t\t\t\t\t\t\t'i',\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst filtered = statements.filter(\n\t\t\t\t\t\t\t(stmt) => !migrationsPattern.test(stmt),\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\toutputResult(filtered, options);\n\n\t\t\t\t\t\tconst result = await executeDdl(pool, filtered, {\n\t\t\t\t\t\t\t...(options.dryRun !== undefined\n\t\t\t\t\t\t\t\t? { dryRun: options.dryRun }\n\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// CC-1: --drop --json must emit JSON to stdout on success\n\t\t\t\t\t\tif (options.json) {\n\t\t\t\t\t\t\tconst droppedTables = statements\n\t\t\t\t\t\t\t\t.filter((s) => /DROP\\s+TABLE/i.test(s))\n\t\t\t\t\t\t\t\t.filter((s) => !migrationsPattern.test(s))\n\t\t\t\t\t\t\t\t.map((s) => {\n\t\t\t\t\t\t\t\t\t// M6: handle CASCADE between last quoted identifier and semicolon\n\t\t\t\t\t\t\t\t\t// e.g. DROP TABLE IF EXISTS \"public\".\"users\" CASCADE;\n\t\t\t\t\t\t\t\t\tconst m = s.match(/\"([^\"]+)\"\\s*(?:CASCADE\\s*)?;?\\s*$/i);\n\t\t\t\t\t\t\t\t\treturn m ? m[1] : s;\n\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t.filter((t): t is string => t !== undefined);\n\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\tJSON.stringify(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tstatus: options.dryRun ? 'dry-run' : 'dropped',\n\t\t\t\t\t\t\t\t\t\ttables: droppedTables,\n\t\t\t\t\t\t\t\t\t\ttablesDropped: droppedTables.length,\n\t\t\t\t\t\t\t\t\t\tstatementsExecuted: result.statementsExecuted,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\t\t2,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} else if (!options.dryRun) {\n\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t`\\n✅ Push complete: ${result.statementsExecuted} statements executed`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Additive mode: introspect → diff → generate migration SQL (additive only)\n\t\t\t\t\t\tconst dbModel = await introspect(pool, {\n\t\t\t\t\t\t\t...(options.schemaName ? { schema: options.schemaName } : {}),\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tconst diff = compareSchemata(schemaModel, dbModel);\n\n\t\t\t\t\t\t// Generate SQL for additive changes only (no destructive)\n\t\t\t\t\t\tconst statements = generateMigrationSQL(diff, {\n\t\t\t\t\t\t\tincludeDestructive: false,\n\t\t\t\t\t\t\t...(options.schemaName ? { schemaName: options.schemaName } : {}),\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\t// Collect warnings for skipped non-additive changes\n\t\t\t\t\t\tconst skippedChanges = diff.changes.filter((c) => c.destructive);\n\n\t\t\t\t\t\tif (!options.json && skippedChanges.length > 0) {\n\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t`⚠️ ${skippedChanges.length} non-additive change(s) skipped:`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tfor (const change of skippedChanges) {\n\t\t\t\t\t\t\t\tconsole.log(` - ${change.details}`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tconsole.log('');\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (statements.length === 0) {\n\t\t\t\t\t\t\tif (options.json) {\n\t\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t\tJSON.stringify(\n\t\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t\tstatus: 'up-to-date',\n\t\t\t\t\t\t\t\t\t\t\tstatementsExecuted: 0,\n\t\t\t\t\t\t\t\t\t\t\tskippedChanges: skippedChanges.length,\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\t\t\t2,\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tconsole.log('✅ Database is up to date — nothing to push.');\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t// EH-14: return instead of process.exit(0) so pool.end() in finally runs\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\toutputResult(statements, options);\n\n\t\t\t\t\t\tconst result = await executeDdl(pool, statements, {\n\t\t\t\t\t\t\t...(options.dryRun !== undefined\n\t\t\t\t\t\t\t\t? { dryRun: options.dryRun }\n\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tif (options.json) {\n\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\tJSON.stringify(\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\tstatus: options.dryRun ? 'dry-run' : 'applied',\n\t\t\t\t\t\t\t\t\t\tstatementsExecuted: result.statementsExecuted,\n\t\t\t\t\t\t\t\t\t\tskippedChanges: skippedChanges.length,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\t\t2,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t} else if (!options.dryRun) {\n\t\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t\t`\\n✅ Push complete: ${result.statementsExecuted} statement(s) executed`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// EH-14: return so finally runs pool.end() before the outer success path\n\t\t\t\t} finally {\n\t\t\t\t\tawait pool.end();\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst message =\n\t\t\t\t\terror instanceof Error ? error.message : 'Unknown error occurred';\n\t\t\t\t// CC-2+EH-7: If --json, error goes to stdout as JSON; otherwise stderr\n\t\t\t\tif (options.json) {\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\tJSON.stringify({ status: 'error', error: message }, null, 2),\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error(`❌ Error: ${message}`);\n\t\t\t\t}\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n\n/**\n * Output SQL statements (dry-run or --json mode).\n */\nfunction outputResult(\n\tstatements: readonly string[],\n\toptions: { dryRun?: boolean; json?: boolean },\n): void {\n\tif (options.dryRun && !options.json) {\n\t\tconsole.log(`-- Dry run: ${statements.length} statement(s)\\n`);\n\t\tfor (const stmt of statements) {\n\t\t\tconsole.log(`${stmt};\\n`);\n\t\t}\n\t}\n}\n","/**\n * DDL Executor — Transaction-wrapped DDL execution.\n *\n * Shared by `dbsp push` and `dbsp migrate apply`.\n * Executes an array of SQL statements inside a single transaction.\n */\n\nimport type { Pool, PoolClient } from 'pg';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface DdlExecutionResult {\n\t/** Number of statements executed */\n\tstatementsExecuted: number;\n\t/** Whether execution was a dry-run (no actual changes) */\n\tdryRun: boolean;\n}\n\n// ============================================================================\n// Executor\n// ============================================================================\n\n/**\n * Execute DDL statements in a transaction.\n *\n * @param pool - pg Pool instance\n * @param statements - SQL statements to execute\n * @param options - Execution options\n * @returns Execution result\n */\nexport async function executeDdl(\n\tpool: Pool,\n\tstatements: readonly string[],\n\toptions?: { dryRun?: boolean },\n): Promise<DdlExecutionResult> {\n\tif (statements.length === 0) {\n\t\treturn { statementsExecuted: 0, dryRun: options?.dryRun ?? false };\n\t}\n\n\tif (options?.dryRun) {\n\t\treturn { statementsExecuted: statements.length, dryRun: true };\n\t}\n\n\tlet client: PoolClient | undefined;\n\ttry {\n\t\tclient = await pool.connect();\n\t\tawait client.query('BEGIN');\n\n\t\tfor (const stmt of statements) {\n\t\t\tawait client.query(stmt);\n\t\t}\n\n\t\tawait client.query('COMMIT');\n\t\treturn { statementsExecuted: statements.length, dryRun: false };\n\t} catch (error) {\n\t\tif (client) {\n\t\t\tawait client.query('ROLLBACK');\n\t\t}\n\t\tthrow error;\n\t} finally {\n\t\tif (client) {\n\t\t\tclient.release();\n\t\t}\n\t}\n}\n","/**\n * DX-030 Block 1: REPL Command\n *\n * dbsp repl [--schema <path>] - Launch interactive REPL.\n * CLI-022: Batch mode support with --eval and --input options.\n */\n\nimport { readFileSync } from 'node:fs';\nimport { Command } from 'commander';\nimport { config } from '../config.js';\nimport { validateIdentifier } from '../utils/identifier-validation.js';\nimport { loadSchema, loadSchemaFromCwd } from '../utils/schema-loader.js';\n\nexport interface ReplOptions {\n\tschema?: string;\n\t/** CLI-020: Database connection URL for execution mode */\n\tdb?: string;\n\t/** CLI-022: Single query to evaluate (batch mode) */\n\teval?: string;\n\t/** CLI-022: File containing queries to execute (batch mode, one per line) */\n\tinput?: string;\n\t/** CLI-022: Output format for batch mode */\n\tformat?: 'text' | 'json';\n\t/** DEMO-E2E: Assertion file for validating query output */\n\tassert?: string;\n\t/** CLI-IMPORT: SQL files to import before queries (injected as .import commands) */\n\timport?: string[];\n\t/** CLI-USE: PostgreSQL schema to use (injected as .use command) */\n\tuse?: string;\n\t/** CLI-MUT: Start REPL with parse mode enabled */\n\tparse?: boolean;\n\t/** CLI-MUT: Start REPL with exec mode enabled */\n\texec?: boolean;\n\t/** CLI-CONFIG: Custom config file path (default: ~/.dbsp/config.json) */\n\tconfig?: string;\n\t/** CLI-CASING: Column naming convention (describes DB column casing) */\n\tcasing?: 'snake' | 'camel' | 'none';\n}\n\nexport const replCommand = new Command('repl')\n\t.description('Launch interactive REPL for exploring schema and queries')\n\t.option('-s, --schema <path>', 'Path to schema file (default: auto-detect)')\n\t.option(\n\t\t'-d, --db <url>',\n\t\t'PostgreSQL connection URL for execution mode (e.g., postgres://localhost/mydb)',\n\t)\n\t.option('-e, --eval <query>', 'Execute a single query and exit (batch mode)')\n\t.option(\n\t\t'-i, --input <file>',\n\t\t'Execute queries from file, one per line (batch mode)',\n\t)\n\t.option(\n\t\t'-f, --format <format>',\n\t\t'Output format for batch mode: text (default) or json',\n\t\t'text',\n\t)\n\t.option(\n\t\t'-a, --assert <file>',\n\t\t'Assertion file to validate query output (requires --input)',\n\t)\n\t.option(\n\t\t'--import <files...>',\n\t\t'SQL files to import before queries (equivalent to .import commands)',\n\t)\n\t.option(\n\t\t'--use <schema>',\n\t\t'PostgreSQL schema to use (equivalent to .use command)',\n\t)\n\t.option('--parse', 'Start REPL with parse mode enabled (.parse toggle)')\n\t.option('--exec', 'Start REPL with exec mode enabled (.exec toggle)')\n\t.option(\n\t\t'--casing <type>',\n\t\t'Column naming convention: snake (DB uses snake_case), camel (DB uses camelCase), none (preserve as-is)',\n\t)\n\t.option(\n\t\t'-c, --config <path>',\n\t\t'Custom config file path (default: ~/.dbsp/config.json)',\n\t)\n\t.action(async (options: ReplOptions) => {\n\t\t// Set custom config path if provided\n\t\tif (options.config) {\n\t\t\tconfig.setConfigPath(options.config);\n\t\t}\n\t\t// Load config\n\t\tconfig.load();\n\t\ttry {\n\t\t\t// Load schema\n\t\t\tlet schemaPath: string;\n\t\t\tlet schema: Awaited<ReturnType<typeof loadSchema>>;\n\n\t\t\tif (options.schema) {\n\t\t\t\tschema = await loadSchema(options.schema);\n\t\t\t\tschemaPath = options.schema;\n\t\t\t} else {\n\t\t\t\tconst result = await loadSchemaFromCwd();\n\t\t\t\tschema = result.schema;\n\t\t\t\tschemaPath = result.path;\n\t\t\t}\n\n\t\t\t// DEMO-E2E: Validate --assert requires --input\n\t\t\tif (options.assert && !options.input) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'--assert requires --input (assertion files validate query output from input files)',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// CLI-IMPORT: Validate that --import requires batch mode (SQL file execution)\n\t\t\t// Note: --use, --parse, --exec work in interactive mode (REPL state setup)\n\t\t\tif (options.import && !options.eval && !options.input) {\n\t\t\t\tthrow new Error('--import requires batch mode (--eval or --input)');\n\t\t\t}\n\n\t\t\t// SEC: Validate --use schema name at the entry point so both the batch path\n\t\t\t// (which injects `.use <schema>`) and the interactive path (which passes\n\t\t\t// initialSchemaName) are protected against SQL injection.\n\t\t\tif (options.use) {\n\t\t\t\tvalidateIdentifier(options.use, 'schema');\n\t\t\t}\n\n\t\t\t// CLI-CASING: Map --casing flag to dbCasing (intuitive: describes DB columns)\n\t\t\tconst dbCasing =\n\t\t\t\toptions.casing === 'snake'\n\t\t\t\t\t? ('snake_case' as const)\n\t\t\t\t\t: options.casing === 'camel'\n\t\t\t\t\t\t? ('camelCase' as const)\n\t\t\t\t\t\t: options.casing === 'none'\n\t\t\t\t\t\t\t? ('preserve' as const)\n\t\t\t\t\t\t\t: undefined;\n\n\t\t\t// CLI-022: Batch mode - execute queries without interactive UI\n\t\t\tif (options.eval || options.input) {\n\t\t\t\tconst { runBatchMode } = await import('../repl/batch.js');\n\t\t\t\tconst queries: string[] = [];\n\n\t\t\t\t// CLI-USE: Inject .use command first (schema scoping)\n\t\t\t\tif (options.use) {\n\t\t\t\t\tqueries.push(`.use ${options.use}`);\n\t\t\t\t}\n\n\t\t\t\t// CLI-IMPORT: Inject .import commands for SQL files\n\t\t\t\tif (options.import) {\n\t\t\t\t\tfor (const file of options.import) {\n\t\t\t\t\t\tqueries.push(`.import ${file}`);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (options.eval) {\n\t\t\t\t\tqueries.push(options.eval);\n\t\t\t\t}\n\n\t\t\t\tif (options.input) {\n\t\t\t\t\t// EH-2: Map ENOENT to a friendly error instead of raw stack trace\n\t\t\t\t\tlet content: string;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcontent = readFileSync(options.input, 'utf-8');\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tconst isNotFound =\n\t\t\t\t\t\t\terr instanceof Error &&\n\t\t\t\t\t\t\t'code' in err &&\n\t\t\t\t\t\t\t(err as NodeJS.ErrnoException).code === 'ENOENT';\n\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\tisNotFound\n\t\t\t\t\t\t\t\t? `Input file not found: ${options.input}`\n\t\t\t\t\t\t\t\t: `Failed to read input file: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tqueries.push(...content.split('\\n'));\n\t\t\t\t}\n\n\t\t\t\tawait runBatchMode({\n\t\t\t\t\tqueries,\n\t\t\t\t\tschema,\n\t\t\t\t\tschemaPath,\n\t\t\t\t\tformat: options.format ?? 'text',\n\t\t\t\t\t...(options.db && { databaseUrl: options.db }),\n\t\t\t\t\t...(options.assert && { assertFile: options.assert }),\n\t\t\t\t\t...(dbCasing && { dbCasing }),\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Dynamic import to avoid loading React/Ink for other commands\n\t\t\tconst { startRepl } = await import('../repl/index.js');\n\t\t\t// CLI-020: Pass database URL if provided\n\t\t\t// CLI-MUT: Pass initial REPL state options\n\t\t\tawait startRepl({\n\t\t\t\tschema,\n\t\t\t\tschemaPath,\n\t\t\t\t...(options.db && { databaseUrl: options.db }),\n\t\t\t\t...(options.use && { initialSchemaName: options.use }),\n\t\t\t\t...(options.parse && { initialParseMode: true }),\n\t\t\t\t...(options.exec && { initialExecMode: true }),\n\t\t\t\t...(dbCasing && { dbCasing }),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tconsole.error(`❌ ${message}`);\n\t\t\tprocess.exit(1);\n\t\t}\n\t});\n","/**\n * Verify Command — Schema Drift Detection\n *\n * dbsp verify - Compare schema vs real database using the comparison engine.\n * Detects drift in tables, columns, types, nullable, defaults, FKs, indexes.\n */\n\nimport { compareSchemata, introspect } from '@dbsp/adapter-pgsql';\nimport { Command } from 'commander';\nimport { createDbConnection, redactDbUrl } from '../utils/db-utils.js';\nimport { loadSchema } from '../utils/schema-loader.js';\nimport { formatVerifyResult, verifyFromDiff } from '../verifier.js';\n\nexport const verifyCommand = new Command('verify')\n\t.description('Compare schema vs real database (drift detection)')\n\t.option(\n\t\t'-s, --schema <path>',\n\t\t'Path to schema file (default: dbsp.schema.ts)',\n\t)\n\t.requiredOption('-d, --db <url>', 'Database connection URL (required)')\n\t.option('--schema-name <name>', 'Database schema name (default: public)')\n\t.option('--json', 'Output as JSON')\n\t.action(\n\t\tasync (options: {\n\t\t\tschema?: string;\n\t\t\tdb: string;\n\t\t\tschemaName?: string;\n\t\t\tjson?: boolean;\n\t\t}) => {\n\t\t\tconst schemaPath = options.schema ?? 'dbsp.schema.ts';\n\n\t\t\tconst redactedUrl = redactDbUrl(options.db);\n\n\t\t\tif (!options.json) {\n\t\t\t\tconsole.log(`🔍 Verifying schema: ${schemaPath}`);\n\t\t\t\tconsole.log(` Database: ${redactedUrl}`);\n\t\t\t\tif (options.schemaName) {\n\t\t\t\t\tconsole.log(` Schema: ${options.schemaName}`);\n\t\t\t\t}\n\t\t\t\tconsole.log('');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// Load schema from file → ModelIR\n\t\t\t\tconst loaded = await loadSchema(schemaPath);\n\t\t\t\tconst schemaModel = loaded.model;\n\n\t\t\t\t// Connect to database\n\t\t\t\tconst { pool } = await createDbConnection(options.db);\n\n\t\t\t\ttry {\n\t\t\t\t\t// Introspect database → ModelIR\n\t\t\t\t\tconst dbModel = await introspect(pool, {\n\t\t\t\t\t\t...(options.schemaName ? { schema: options.schemaName } : {}),\n\t\t\t\t\t});\n\n\t\t\t\t\t// Compare using the comparison engine\n\t\t\t\t\tconst diff = compareSchemata(schemaModel, dbModel);\n\n\t\t\t\t\t// Convert to verify result\n\t\t\t\t\tconst schemaTables = Array.from(schemaModel.tables.keys());\n\t\t\t\t\tconst dbTables = Array.from(dbModel.tables.keys());\n\t\t\t\t\tconst result = verifyFromDiff(diff, schemaTables, dbTables);\n\n\t\t\t\t\t// Output\n\t\t\t\t\tif (options.json) {\n\t\t\t\t\t\t// Exclude the full diff meta from JSON output (too verbose)\n\t\t\t\t\t\tconst { diff: _diff, ...jsonResult } = result;\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\tJSON.stringify(\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t...jsonResult,\n\t\t\t\t\t\t\t\t\tsummary: diff.summary,\n\t\t\t\t\t\t\t\t\thasDestructive: diff.hasDestructive,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\tnull,\n\t\t\t\t\t\t\t\t2,\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconsole.log(formatVerifyResult(result));\n\t\t\t\t\t}\n\n\t\t\t\t\t// EH-14: set exit code; let finally run pool.end() before process exits\n\t\t\t\t\tprocess.exitCode = result.valid ? 0 : 1;\n\t\t\t\t\treturn;\n\t\t\t\t} finally {\n\t\t\t\t\t// Close database connection\n\t\t\t\t\tawait pool.end();\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\tconst message =\n\t\t\t\t\terror instanceof Error ? error.message : 'Unknown error occurred';\n\t\t\t\t// CC-2+EH-7: If --json, error goes to stdout as JSON; otherwise stderr\n\t\t\t\tif (options.json) {\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\tJSON.stringify({ status: 'error', error: message }, null, 2),\n\t\t\t\t\t);\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error(`❌ Error: ${message}`);\n\t\t\t\t}\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\t\t},\n\t);\n","/**\n * Schema Verifier — Drift Detection via Comparison Engine\n *\n * Compares schema definition against real database using the\n * adapter's compareSchemata engine for full drift detection.\n *\n * Detects: tables, columns, types, nullable, defaults, FKs, indexes, PKs.\n */\n\nimport type { ChangeKind, SchemaChange, SchemaDiff } from '@dbsp/adapter-pgsql';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport type DriftSeverity = 'error' | 'warning' | 'info';\n\nexport type DriftType =\n\t// Tables\n\t| 'missing_table_in_db'\n\t| 'missing_table_in_schema'\n\t// Columns\n\t| 'missing_column_in_db'\n\t| 'missing_column_in_schema'\n\t| 'type_mismatch'\n\t| 'nullable_mismatch'\n\t| 'default_mismatch'\n\t// Constraints\n\t| 'primary_key_mismatch'\n\t| 'missing_fk_in_db'\n\t| 'missing_fk_in_schema'\n\t| 'fk_on_delete_mismatch'\n\t// Indexes\n\t| 'missing_index_in_db'\n\t| 'missing_index_in_schema'\n\t// CHECK constraints\n\t| 'missing_check_in_db'\n\t| 'missing_check_in_schema'\n\t// ENUM types\n\t| 'missing_enum_in_db'\n\t| 'missing_enum_in_schema'\n\t| 'enum_value_mismatch'\n\t// Column enhancements\n\t| 'collation_mismatch'\n\t| 'identity_mismatch'\n\t// Comments\n\t| 'comment_mismatch'\n\t// Extensions & Sequences\n\t| 'missing_extension'\n\t| 'missing_sequence'\n\t| 'sequence_mismatch'\n\t// Constraints\n\t| 'constraint_validation_mismatch'\n\t// Row-Level Security\n\t| 'rls_enabled_mismatch'\n\t| 'missing_policy_in_db'\n\t| 'missing_policy_in_schema';\n\nexport interface DriftIssue {\n\t/** Issue severity */\n\tseverity: DriftSeverity;\n\t/** Human-readable message */\n\tmessage: string;\n\t/** Table affected (if any) */\n\ttable?: string;\n\t/** Column affected (if any) */\n\tcolumn?: string;\n\t/** Issue type for programmatic handling */\n\ttype: DriftType;\n}\n\nexport interface VerifyResult {\n\t/** Whether the schema matches the database */\n\tvalid: boolean;\n\t/** List of drift issues found */\n\tissues: DriftIssue[];\n\t/** Tables in schema */\n\tschemaTables: string[];\n\t/** Tables in database */\n\tdbTables: string[];\n\t/** Structured diff (for programmatic consumers) */\n\tdiff: SchemaDiff;\n}\n\n/**\n * @deprecated Use DbTableInfo from introspection instead.\n * Kept for backward compatibility with existing tests.\n */\nexport interface DbTableInfo {\n\tname: string;\n\tcolumns: DbColumnInfo[];\n}\n\n/**\n * @deprecated Use ColumnIR from introspection instead.\n */\nexport interface DbColumnInfo {\n\tname: string;\n\tdataType: string;\n\tisNullable: boolean;\n\tisPrimaryKey?: boolean;\n\thasDefault?: boolean;\n}\n\n// ============================================================================\n// Change → Drift Mapping\n// ============================================================================\n\nconst CHANGE_TO_DRIFT: Record<\n\tChangeKind,\n\t{ type: DriftType; severity: DriftSeverity }\n> = {\n\tcreate_table: { type: 'missing_table_in_db', severity: 'error' },\n\tdrop_table: { type: 'missing_table_in_schema', severity: 'warning' },\n\tadd_column: { type: 'missing_column_in_db', severity: 'error' },\n\tdrop_column: { type: 'missing_column_in_schema', severity: 'info' },\n\talter_column_type: { type: 'type_mismatch', severity: 'error' },\n\talter_column_nullable: { type: 'nullable_mismatch', severity: 'warning' },\n\talter_column_default: { type: 'default_mismatch', severity: 'warning' },\n\tadd_primary_key: { type: 'primary_key_mismatch', severity: 'error' },\n\tdrop_primary_key: { type: 'primary_key_mismatch', severity: 'error' },\n\tadd_foreign_key: { type: 'missing_fk_in_db', severity: 'error' },\n\tdrop_foreign_key: { type: 'missing_fk_in_schema', severity: 'warning' },\n\talter_foreign_key: { type: 'fk_on_delete_mismatch', severity: 'warning' },\n\tcreate_index: { type: 'missing_index_in_db', severity: 'warning' },\n\tdrop_index: { type: 'missing_index_in_schema', severity: 'info' },\n\t// CHECK constraints\n\tadd_check_constraint: { type: 'missing_check_in_db', severity: 'warning' },\n\tdrop_check_constraint: { type: 'missing_check_in_schema', severity: 'info' },\n\t// ENUM types\n\tcreate_enum: { type: 'missing_enum_in_db', severity: 'error' },\n\talter_enum_add_value: { type: 'enum_value_mismatch', severity: 'warning' },\n\tdrop_enum: { type: 'missing_enum_in_schema', severity: 'warning' },\n\t// Column enhancements\n\talter_column_collation: { type: 'collation_mismatch', severity: 'warning' },\n\talter_column_identity: { type: 'identity_mismatch', severity: 'warning' },\n\t// Comments\n\tadd_comment: { type: 'comment_mismatch', severity: 'info' },\n\tdrop_comment: { type: 'comment_mismatch', severity: 'info' },\n\t// Extensions & Sequences\n\tcreate_extension: { type: 'missing_extension', severity: 'error' },\n\tdrop_extension: { type: 'missing_extension', severity: 'info' },\n\tcreate_sequence: { type: 'missing_sequence', severity: 'warning' },\n\talter_sequence: { type: 'sequence_mismatch', severity: 'warning' },\n\tdrop_sequence: { type: 'missing_sequence', severity: 'info' },\n\t// Constraints\n\tvalidate_constraint: {\n\t\ttype: 'constraint_validation_mismatch',\n\t\tseverity: 'warning',\n\t},\n\t// Row-Level Security\n\tenable_rls: { type: 'rls_enabled_mismatch', severity: 'warning' },\n\tdisable_rls: { type: 'rls_enabled_mismatch', severity: 'warning' },\n\tcreate_policy: { type: 'missing_policy_in_db', severity: 'warning' },\n\tdrop_policy: { type: 'missing_policy_in_schema', severity: 'warning' },\n};\n\nfunction changeToDriftIssue(change: SchemaChange): DriftIssue {\n\tconst mapping = CHANGE_TO_DRIFT[change.kind] ?? {\n\t\ttype: 'type_mismatch' as DriftType,\n\t\tseverity: 'warning' as DriftSeverity,\n\t};\n\treturn {\n\t\tseverity: mapping.severity,\n\t\ttype: mapping.type,\n\t\ttable: change.table,\n\t\t...(change.column !== undefined ? { column: change.column } : {}),\n\t\tmessage: change.details,\n\t};\n}\n\n// ============================================================================\n// Verification (from SchemaDiff)\n// ============================================================================\n\n/**\n * Convert a SchemaDiff into a VerifyResult.\n *\n * @param diff - Structured diff from compareSchemata()\n * @param schemaTables - Table names in schema (for backward compat)\n * @param dbTables - Table names in database (for backward compat)\n */\nexport function verifyFromDiff(\n\tdiff: SchemaDiff,\n\tschemaTables: string[],\n\tdbTables: string[],\n): VerifyResult {\n\tconst issues = diff.changes.map(changeToDriftIssue);\n\n\t// Sort by severity (error > warning > info)\n\tconst severityOrder: Record<DriftSeverity, number> = {\n\t\terror: 0,\n\t\twarning: 1,\n\t\tinfo: 2,\n\t};\n\tissues.sort(\n\t\t(a: DriftIssue, b: DriftIssue) =>\n\t\t\tseverityOrder[a.severity] - severityOrder[b.severity],\n\t);\n\n\tconst hasErrors = issues.some((i: DriftIssue) => i.severity === 'error');\n\n\treturn {\n\t\tvalid: !hasErrors,\n\t\tissues,\n\t\tschemaTables,\n\t\tdbTables,\n\t\tdiff,\n\t};\n}\n\n// ============================================================================\n// Format\n// ============================================================================\n\n/**\n * Format verification result for CLI output.\n */\nexport function formatVerifyResult(result: VerifyResult): string {\n\tconst lines: string[] = [];\n\n\tif (result.valid) {\n\t\tlines.push('✅ Schema matches database');\n\t} else {\n\t\tlines.push('❌ Schema drift detected');\n\t}\n\n\tlines.push('');\n\tlines.push(`Tables in schema: ${result.schemaTables.length}`);\n\tlines.push(`Tables in database: ${result.dbTables.length}`);\n\tlines.push('');\n\n\tif (result.issues.length === 0) {\n\t\tlines.push('No issues found.');\n\t} else {\n\t\tconst errors = result.issues.filter((i) => i.severity === 'error');\n\t\tconst warnings = result.issues.filter((i) => i.severity === 'warning');\n\t\tconst infos = result.issues.filter((i) => i.severity === 'info');\n\n\t\tif (errors.length > 0) {\n\t\t\tlines.push(`❌ ${errors.length} error(s):`);\n\t\t\tfor (const issue of errors) {\n\t\t\t\tlines.push(` ${issue.message}`);\n\t\t\t}\n\t\t\tlines.push('');\n\t\t}\n\n\t\tif (warnings.length > 0) {\n\t\t\tlines.push(`⚠️ ${warnings.length} warning(s):`);\n\t\t\tfor (const issue of warnings) {\n\t\t\t\tlines.push(` ${issue.message}`);\n\t\t\t}\n\t\t\tlines.push('');\n\t\t}\n\n\t\tif (infos.length > 0) {\n\t\t\tlines.push(`ℹ️ ${infos.length} info:`);\n\t\t\tfor (const issue of infos) {\n\t\t\t\tlines.push(` ${issue.message}`);\n\t\t\t}\n\t\t\tlines.push('');\n\t\t}\n\t}\n\n\treturn lines.join('\\n');\n}\n"],"mappings":"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","names":["Command","outputPath","mkdirSync","writeFileSync","dirname","resolve","Command","Command","introspect","resolve","mkdirSync","dirname","writeFileSync","Command","mkdirSync","writeFileSync","resolve","Command","compareSchemata","generateMigrationSQL","introspect","Command","Command","introspect","compareSchemata","generateMigrationSQL","readFileSync","Command","Command","readFileSync","compareSchemata","introspect","Command","Command","introspect","compareSchemata","Command"]}
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  ReplEngine,
3
3
  processDotCommand
4
- } from "../chunk-ZSGVJFWG.js";
5
- import "../chunk-U5DSGBS2.js";
4
+ } from "../chunk-O4QLFJIM.js";
5
+ import "../chunk-AKVQI5ZD.js";
6
6
 
7
7
  // src/repl/batch.ts
8
8
  import { readFileSync } from "fs";
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  ReplEngine,
3
3
  getDatabaseName
4
- } from "./chunk-ZSGVJFWG.js";
4
+ } from "./chunk-O4QLFJIM.js";
5
5
  import {
6
6
  config
7
- } from "./chunk-U5DSGBS2.js";
7
+ } from "./chunk-AKVQI5ZD.js";
8
8
 
9
9
  // src/repl/index.tsx
10
10
  import { Box as Box10, render, useApp, useInput as useInput3 } from "ink";
@@ -1451,4 +1451,4 @@ async function startRepl(config2) {
1451
1451
  export {
1452
1452
  startRepl
1453
1453
  };
1454
- //# sourceMappingURL=repl-4OFERLKZ.js.map
1454
+ //# sourceMappingURL=repl-PXJYHDVX.js.map