UNPKG

@driftjs/flow

Version:

Production-ready CLI that automatically enhances database migrations with 50+ safety, performance, and monitoring improvements. Supports Prisma, Drizzle, TypeORM with zero-downtime deployments and comprehensive rollback capabilities.

1,354 lines (1,335 loc) 131 kB
#!/usr/bin/env node var __defProp = Object.defineProperty; var __getOwnPropNames = Object.getOwnPropertyNames; var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { get: (a, b) => (typeof require !== "undefined" ? require : a)[b] }) : x)(function(x) { if (typeof require !== "undefined") return require.apply(this, arguments); throw Error('Dynamic require of "' + x + '" is not supported'); }); var __esm = (fn, res) => function __init() { return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; }; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; // src/lib/config.ts import fsExtra from "fs-extra"; import { resolve, dirname } from "path"; async function getFlowConfig(global, projectPath) { const configPath = await findConfigFile(projectPath || process.cwd(), global.config); return JSON.parse(await fsExtra.readFile(configPath, "utf8")); } async function findConfigFile(startDir, explicit) { if (explicit) { const p = resolve(explicit); if (await fsExtra.pathExists(p)) return p; throw new Error(`Config file not found at ${p}`); } let dir = startDir; while (true) { const candidate = resolve(dir, "flow.config.json"); if (await fsExtra.pathExists(candidate)) return candidate; const parent = dirname(dir); if (parent === dir) break; dir = parent; } throw new Error("flow.config.json not found"); } var init_config = __esm({ "src/lib/config.ts"() { "use strict"; } }); // src/lib/prompts.ts import { confirm, select, multiselect, text, spinner, log } from "@clack/prompts"; import colors from "picocolors"; async function confirmAction(message, options = {}) { return await confirm({ message: colors.cyan(message), ...options }); } async function textInput(message, options = {}) { return await text({ message: colors.cyan(message), ...options }); } function createSpinner(message) { const s = spinner(); return { start: (msg) => { s.start(colors.blue(`\u{1F30A} ${msg || message}`)); return { update: (newMessage) => s.message(colors.blue(`\u{1F30A} ${newMessage}`)), succeed: (successMessage) => s.stop(colors.green(`\u2705 ${successMessage || "Complete"}`)), fail: (errorMessage) => s.stop(colors.red(`\u274C ${errorMessage || "Failed"}`)), stop: (finalMessage) => s.stop(finalMessage ? colors.gray(finalMessage) : ""), message: (msg2) => s.message(colors.blue(`\u{1F30A} ${msg2}`)) }; } }; } function createFlowSpinner() { const s = spinner(); let isStarted = false; return { start: (message) => { if (!isStarted) { s.start(colors.blue(`\u{1F30A} ${message}`)); isStarted = true; } return { update: (msg) => s.message(colors.blue(`\u{1F30A} ${msg}`)), succeed: (msg) => { s.stop(colors.green(`\u2705 ${msg || "Complete"}`)); isStarted = false; }, fail: (msg) => { s.stop(colors.red(`\u274C ${msg || "Failed"}`)); isStarted = false; }, stop: (msg) => { s.stop(msg ? colors.gray(msg) : ""); isStarted = false; } }; } }; } function displaySuccess(message, details) { log.success(colors.green(`\u2705 ${message}`)); if (details && details.length > 0) { details.forEach((detail) => { log.info(colors.dim(` \u2022 ${detail}`)); }); } } function displayError(message, details) { log.error(colors.red(`\u274C ${message}`)); if (details && details.length > 0) { details.forEach((detail) => { log.info(colors.dim(` \u2022 ${detail}`)); }); } } function displayWarning(message, details) { log.warn(colors.yellow(`\u26A0\uFE0F ${message}`)); if (details && details.length > 0) { details.forEach((detail) => { log.info(colors.dim(` \u2022 ${detail}`)); }); } } function displayInfo(message, details) { log.info(colors.blue(`\u2139\uFE0F ${message}`)); if (details && details.length > 0) { details.forEach((detail) => { log.info(colors.dim(` \u2022 ${detail}`)); }); } } var init_prompts = __esm({ "src/lib/prompts.ts"() { "use strict"; } }); // src/enhancements/safety/transaction-wrapper.ts var enhancement, TransactionWrapperDetector, TransactionWrapperApplicator, transactionWrapperModule; var init_transaction_wrapper = __esm({ "src/enhancements/safety/transaction-wrapper.ts"() { "use strict"; enhancement = { id: "safety-transaction-wrapper", name: "Transaction Wrapper", description: "Wraps migration operations in database transactions to ensure atomicity and enable rollback on failure", category: "safety", priority: 9, requiresConfirmation: false, tags: ["transaction", "atomicity", "rollback", "critical"] }; TransactionWrapperDetector = class { async detect(migration) { const content = migration.up.toLowerCase(); if (content.includes("begin") || content.includes("start transaction") || content.includes("commit")) { return false; } const riskyOperations = [ "drop table", "drop column", "alter table", "delete from", "update ", "create index", "drop index" ]; return riskyOperations.some((op) => content.includes(op)); } async analyze(migration) { const applicable = await this.detect(migration); if (!applicable) { return { applicable: false, confidence: 0, issues: [], impact: { riskReduction: 0, performanceImprovement: 0, complexityAdded: 0, description: "Transaction wrapper not applicable - migration already has transaction handling or no risky operations" } }; } const issues = []; const content = migration.up.toLowerCase(); if (content.includes("drop table")) { issues.push({ severity: "high", description: "DROP TABLE operation without transaction protection", location: "DROP TABLE statement", line: this.findLineNumber(migration.up, /drop\s+table/i), recommendation: "Wrap in transaction to enable rollback if migration fails" }); } if (content.includes("alter table")) { issues.push({ severity: "medium", description: "ALTER TABLE operation without transaction protection", location: "ALTER TABLE statement", line: this.findLineNumber(migration.up, /alter\s+table/i), recommendation: "Wrap in transaction to ensure consistency" }); } return { applicable: true, confidence: 0.9, issues, impact: { riskReduction: 0.8, performanceImprovement: 0, complexityAdded: 0.1, description: "Significantly reduces risk by enabling rollback on failure with minimal complexity added" } }; } findLineNumber(content, pattern) { const lines = content.split("\n"); for (let i = 0; i < lines.length; i++) { if (pattern.test(lines[i])) { return i + 1; } } return 1; } }; TransactionWrapperApplicator = class { async apply(content, migration) { try { const modifiedContent = `-- Flow Enhancement: Transaction Wrapper -- Wraps migration in transaction for safety and rollback capability BEGIN; ${content.trim()} COMMIT; -- If any statement fails, the entire transaction will be rolled back automatically`; return { enhancement, applied: true, modifiedContent, warnings: [], changes: [ { type: "WRAPPED", original: content.trim(), modified: modifiedContent, line: 1, reason: "Wrapped entire migration in transaction for safety" } ] }; } catch (error) { return { enhancement, applied: false, modifiedContent: content, warnings: [`Failed to apply transaction wrapper: ${error instanceof Error ? error.message : "Unknown error"}`], changes: [] }; } } }; transactionWrapperModule = { enhancement, detector: new TransactionWrapperDetector(), applicator: new TransactionWrapperApplicator() }; } }); // src/enhancements/safety/drop-table-safeguard.ts var enhancement2, DropTableSafeguardDetector, DropTableSafeguardApplicator, dropTableSafeguardModule; var init_drop_table_safeguard = __esm({ "src/enhancements/safety/drop-table-safeguard.ts"() { "use strict"; enhancement2 = { id: "safety-drop-table-safeguard", name: "Drop Table Safeguard", description: "Adds explicit confirmations and backup recommendations for DROP TABLE operations to prevent accidental data loss", category: "safety", priority: 10, requiresConfirmation: true, tags: ["drop-table", "data-protection", "backup", "critical"] }; DropTableSafeguardDetector = class { async detect(migration) { const content = migration.up.toLowerCase(); return content.includes("drop table"); } async analyze(migration) { const applicable = await this.detect(migration); if (!applicable) { return { applicable: false, confidence: 0, issues: [], impact: { riskReduction: 0, performanceImprovement: 0, complexityAdded: 0, description: "No DROP TABLE operations found" } }; } const issues = []; const lines = migration.up.split("\n"); lines.forEach((line, index) => { if (/drop\s+table/i.test(line)) { const tableName = this.extractTableName(line); issues.push({ severity: "critical", description: `DROP TABLE operation on table "${tableName}" - IRREVERSIBLE DATA LOSS`, location: line.trim(), line: index + 1, recommendation: "Ensure you have a backup and consider using a different approach if possible" }); } }); return { applicable: true, confidence: 1, issues, impact: { riskReduction: 0.9, performanceImprovement: 0, complexityAdded: 0.2, description: "Adds explicit warnings and safeguards to prevent accidental data loss from DROP TABLE operations" } }; } extractTableName(line) { const match = line.match(/drop\s+table\s+(?:if\s+exists\s+)?`?([^`\s;]+)`?/i); return match ? match[1] : "unknown"; } }; DropTableSafeguardApplicator = class { async apply(content, migration) { try { const lines = content.split("\n"); const modifiedLines = []; const changes = []; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (/drop\s+table/i.test(line)) { const tableName = this.extractTableName(line); const safeguardComment = `-- \u26A0\uFE0F CRITICAL WARNING: DROP TABLE OPERATION -- Table: ${tableName} -- This operation will PERMANENTLY DELETE ALL DATA in this table -- Ensure you have a backup before proceeding -- Consider using 'DROP TABLE IF EXISTS' for safer execution -- Original command: ${line.trim()}`; modifiedLines.push(safeguardComment); const saferDropCommand = line.replace(/drop\s+table\s+/i, "DROP TABLE IF EXISTS "); modifiedLines.push(saferDropCommand); changes.push({ type: "MODIFIED", original: line, modified: safeguardComment + "\n" + saferDropCommand, line: i + 1, reason: "Added safety warnings and IF EXISTS clause to DROP TABLE operation" }); } else { modifiedLines.push(line); } } return { enhancement: enhancement2, applied: true, modifiedContent: modifiedLines.join("\n"), warnings: [ "DROP TABLE operations detected - please verify you have backups", "Added IF EXISTS clauses to prevent errors if tables don't exist" ], changes }; } catch (error) { return { enhancement: enhancement2, applied: false, modifiedContent: content, warnings: [`Failed to apply drop table safeguard: ${error instanceof Error ? error.message : "Unknown error"}`], changes: [] }; } } extractTableName(line) { const match = line.match(/drop\s+table\s+(?:if\s+exists\s+)?`?([^`\s;]+)`?/i); return match ? match[1] : "unknown"; } }; dropTableSafeguardModule = { enhancement: enhancement2, detector: new DropTableSafeguardDetector(), applicator: new DropTableSafeguardApplicator() }; } }); // src/enhancements/safety/foreign-key-constraint.ts var enhancement3, ForeignKeyConstraintDetector, ForeignKeyConstraintApplicator, foreignKeyConstraintModule; var init_foreign_key_constraint = __esm({ "src/enhancements/safety/foreign-key-constraint.ts"() { "use strict"; enhancement3 = { id: "safety-foreign-key-constraint", name: "Foreign Key Constraint Validation", description: "Validates foreign key constraints and adds proper error handling", category: "safety", priority: 7, requiresConfirmation: false, tags: ["foreign-key", "constraint", "validation"] }; ForeignKeyConstraintDetector = class { async detect(migration) { return migration.up.toLowerCase().includes("foreign key"); } async analyze(migration) { return { applicable: await this.detect(migration), confidence: 0.7, issues: [], impact: { riskReduction: 0.5, performanceImprovement: 0, complexityAdded: 0.2, description: "Validates foreign key constraints" } }; } }; ForeignKeyConstraintApplicator = class { async apply(content, migration) { return { enhancement: enhancement3, applied: false, modifiedContent: content, warnings: ["Foreign key constraint enhancement not yet implemented"], changes: [] }; } }; foreignKeyConstraintModule = { enhancement: enhancement3, detector: new ForeignKeyConstraintDetector(), applicator: new ForeignKeyConstraintApplicator() }; } }); // src/enhancements/safety/nullable-column.ts var enhancement4, NullableColumnDetector, NullableColumnApplicator, nullableColumnModule; var init_nullable_column = __esm({ "src/enhancements/safety/nullable-column.ts"() { "use strict"; enhancement4 = { id: "safety-nullable-column", name: "Nullable Column Safety", description: "Ensures safe handling of nullable column operations by adding proper NULL checks and default values", category: "safety", priority: 6, requiresConfirmation: true, tags: ["nullable", "column", "safety", "null-checks"] }; NullableColumnDetector = class { async detect(migration) { const content = migration.up.toLowerCase(); return content.includes("alter column") && content.includes("null") || content.includes("not null") || content.includes("set null") || content.includes("drop not null"); } async analyze(migration) { const applicable = await this.detect(migration); if (!applicable) { return { applicable: false, confidence: 0, issues: [], impact: { riskReduction: 0, performanceImprovement: 0, complexityAdded: 0, description: "No nullable column operations detected" } }; } const issues = []; const content = migration.up.toLowerCase(); const lines = migration.up.split("\n"); if (content.includes("not null") && !content.includes("default")) { issues.push({ severity: "high", description: "Adding NOT NULL constraint without a default value can fail if table has existing NULL values", location: "ALTER COLUMN ... NOT NULL", line: lines.findIndex((line) => line.toLowerCase().includes("not null")) + 1, recommendation: "Add a default value or update existing NULL values before applying constraint" }); } if (content.includes("drop not null")) { issues.push({ severity: "medium", description: "Removing NOT NULL constraint without data validation can lead to unexpected NULL values", location: "DROP NOT NULL", line: lines.findIndex((line) => line.toLowerCase().includes("drop not null")) + 1, recommendation: "Consider if allowing NULL values is intentional and add application-level validation" }); } return { applicable: true, confidence: 0.8, issues, impact: { riskReduction: 0.7, performanceImprovement: 0, complexityAdded: 0.3, description: "Adds proper NULL handling and validation to column modifications" } }; } }; NullableColumnApplicator = class { async apply(content, migration) { let modifiedContent = content; const changes = []; if (content.toLowerCase().includes("not null") && !content.toLowerCase().includes("default")) { const lines = content.split("\n"); let modified = false; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line.toLowerCase().includes("alter column") && line.toLowerCase().includes("not null")) { const safetyComment = " -- Safety check: Ensure no NULL values exist before adding NOT NULL constraint"; const updateComment = " -- UPDATE table_name SET column_name = 'default_value' WHERE column_name IS NULL;"; lines.splice(i, 0, safetyComment); lines.splice(i + 1, 0, updateComment); changes.push({ type: "ADDED", original: line, modified: `${safetyComment} ${updateComment} ${line}`, line: i + 1, reason: "Added safety check comments for NOT NULL constraint" }); i += 2; modified = true; } } if (modified) { modifiedContent = lines.join("\n"); } } if (content.toLowerCase().includes("drop not null")) { const lines = content.split("\n"); let modified = false; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line.toLowerCase().includes("drop not null")) { const intentionComment = " -- Safety check: Consider if allowing NULL values is intentional"; const validationComment = " -- Add application-level validation if needed"; lines.splice(i, 0, intentionComment); lines.splice(i + 1, 0, validationComment); changes.push({ type: "ADDED", original: line, modified: `${intentionComment} ${validationComment} ${line}`, line: i + 1, reason: "Added safety warnings for DROP NOT NULL constraint" }); i += 2; modified = true; } } if (modified) { modifiedContent = lines.join("\n"); } } return { enhancement: enhancement4, applied: changes.length > 0, modifiedContent, warnings: changes.length === 0 ? ["No nullable column safety improvements were applied"] : [], changes }; } }; nullableColumnModule = { enhancement: enhancement4, detector: new NullableColumnDetector(), applicator: new NullableColumnApplicator() }; } }); // src/enhancements/safety/index-creation.ts var enhancement5, StubDetector, StubApplicator, indexCreationModule; var init_index_creation = __esm({ "src/enhancements/safety/index-creation.ts"() { "use strict"; enhancement5 = { id: "safety-index-creation", name: "Index Creation Safety", description: "Stub", category: "safety", priority: 5, requiresConfirmation: false, tags: ["stub"] }; StubDetector = class { async detect(migration) { return false; } async analyze(migration) { return { applicable: false, confidence: 0, issues: [], impact: { riskReduction: 0, performanceImprovement: 0, complexityAdded: 0, description: "Stub" } }; } }; StubApplicator = class { async apply(content, migration) { return { enhancement: enhancement5, applied: false, modifiedContent: content, warnings: ["Not implemented"], changes: [] }; } }; indexCreationModule = { enhancement: enhancement5, detector: new StubDetector(), applicator: new StubApplicator() }; } }); // src/enhancements/safety/data-type-change.ts var enhancement6, DataTypeChangeDetector, DataTypeChangeApplicator, dataTypeChangeModule; var init_data_type_change = __esm({ "src/enhancements/safety/data-type-change.ts"() { "use strict"; enhancement6 = { id: "safety-data-type-change", name: "Data Type Change Safety", description: "Detects risky data type conversions and suggests safer migration patterns", category: "safety", priority: 8, requiresConfirmation: true, tags: ["data-type", "conversion", "safety", "column-alteration"] }; DataTypeChangeDetector = class { async detect(migration) { const content = migration.up.toLowerCase(); return content.includes("alter column") && content.includes("type") || content.includes("alter table") && content.includes("alter column") || content.includes("change column"); } async analyze(migration) { const applicable = await this.detect(migration); if (!applicable) { return { applicable: false, confidence: 0, issues: [], impact: { riskReduction: 0, performanceImprovement: 0, complexityAdded: 0, description: "No data type changes detected" } }; } const issues = []; const content = migration.up.toLowerCase(); const lines = migration.up.split("\n"); const riskyConversions = [ { from: "varchar", to: "int", risk: "high" }, { from: "text", to: "varchar", risk: "medium" }, { from: "decimal", to: "int", risk: "high" }, { from: "timestamp", to: "date", risk: "medium" } ]; for (const conversion of riskyConversions) { if (content.includes(conversion.to)) { issues.push({ severity: conversion.risk, description: `Converting to ${conversion.to} may cause data loss or conversion errors`, location: "ALTER COLUMN ... TYPE", line: lines.findIndex((line) => line.toLowerCase().includes("type")) + 1 || 1, recommendation: `Verify data compatibility before converting to ${conversion.to} type` }); } } return { applicable: true, confidence: 0.8, issues, impact: { riskReduction: 0.7, performanceImprovement: 0, complexityAdded: 0.4, description: "Adds safety checks and warnings for risky data type conversions" } }; } }; DataTypeChangeApplicator = class { async apply(content, migration) { let modifiedContent = content; const changes = []; if (content.toLowerCase().includes("alter column") && content.toLowerCase().includes("type")) { const lines = content.split("\n"); let modified = false; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line.toLowerCase().includes("alter column") && line.toLowerCase().includes("type")) { lines.splice(i, 0, " -- Safety check: Verify data compatibility before type conversion"); lines.splice(i + 1, 0, " -- Consider backing up data or using a staged migration approach"); changes.push("Added data type conversion safety comments"); i += 2; modified = true; } } if (modified) { modifiedContent = lines.join("\n"); } } return { enhancement: enhancement6, applied: changes.length > 0, modifiedContent, warnings: changes.length === 0 ? ["No data type change safety improvements were applied"] : [], changes: changes.map((change) => ({ type: "ADDED", original: "", modified: change, line: 1, reason: change })) }; } }; dataTypeChangeModule = { enhancement: enhancement6, detector: new DataTypeChangeDetector(), applicator: new DataTypeChangeApplicator() }; } }); // src/enhancements/safety/column-renaming.ts var enhancement7, ColumnRenamingDetector, ColumnRenamingApplicator, columnRenamingModule; var init_column_renaming = __esm({ "src/enhancements/safety/column-renaming.ts"() { "use strict"; enhancement7 = { id: "safety-column-renaming", name: "Column Renaming Safety", description: "Warns about application compatibility risks when renaming database columns", category: "safety", priority: 7, requiresConfirmation: true, tags: ["column", "rename", "compatibility", "breaking-change"] }; ColumnRenamingDetector = class { async detect(migration) { const content = migration.up.toLowerCase(); return content.includes("rename column") || content.includes("alter column") && content.includes("rename to"); } async analyze(migration) { const applicable = await this.detect(migration); if (!applicable) { return { applicable: false, confidence: 0, issues: [], impact: { riskReduction: 0, performanceImprovement: 0, complexityAdded: 0, description: "No column renaming detected" } }; } const issues = []; const content = migration.up.toLowerCase(); const lines = migration.up.split("\n"); if (content.includes("rename column")) { issues.push({ severity: "high", description: "Renaming columns can break existing application code and queries", location: "RENAME COLUMN operation", line: lines.findIndex((line) => line.toLowerCase().includes("rename column")) + 1 || 1, recommendation: "Consider a staged approach: add new column, copy data, update application, then drop old column" }); } return { applicable: true, confidence: 0.8, issues, impact: { riskReduction: 0.7, performanceImprovement: 0, complexityAdded: 0.4, description: "Prevents application breakage from column renames" } }; } }; ColumnRenamingApplicator = class { async apply(content, migration) { let modifiedContent = content; const changes = []; const lines = content.split("\n"); for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line.toLowerCase().includes("rename column")) { const warnings = [ "-- \u26A0\uFE0F COLUMN RENAME WARNING:", "-- This operation can break existing application code.", "-- Recommended staged approach:", "-- 1. Add new column with desired name", "-- 2. Copy data from old to new column", "-- 3. Update application code to use new column", "-- 4. Drop old column in a separate migration" ]; lines.splice(i, 0, ...warnings); changes.push("Added column renaming safety warning"); i += warnings.length; } } if (changes.length > 0) { modifiedContent = lines.join("\n"); } return { enhancement: enhancement7, applied: changes.length > 0, modifiedContent, warnings: changes.length === 0 ? ["No column renaming operations found"] : [], changes: changes.map((change) => ({ type: "ADDED", original: "RENAME COLUMN", modified: change, line: 1, reason: change })) }; } }; columnRenamingModule = { enhancement: enhancement7, detector: new ColumnRenamingDetector(), applicator: new ColumnRenamingApplicator() }; } }); // src/enhancements/safety/cascade-delete.ts var enhancement8, CascadeDeleteDetector, CascadeDeleteApplicator, cascadeDeleteModule; var init_cascade_delete = __esm({ "src/enhancements/safety/cascade-delete.ts"() { "use strict"; enhancement8 = { id: "safety-cascade-delete", name: "Cascade Delete Safety", description: "Warns about CASCADE DELETE operations and suggests safer alternatives", category: "safety", priority: 9, requiresConfirmation: true, tags: ["cascade", "delete", "foreign-key", "safety"] }; CascadeDeleteDetector = class { async detect(migration) { const content = migration.up.toLowerCase(); return content.includes("on delete cascade") || content.includes("cascade delete"); } async analyze(migration) { const applicable = await this.detect(migration); if (!applicable) { return { applicable: false, confidence: 0, issues: [], impact: { riskReduction: 0, performanceImprovement: 0, complexityAdded: 0, description: "No cascade delete operations detected" } }; } const issues = []; const content = migration.up.toLowerCase(); const lines = migration.up.split("\n"); if (content.includes("on delete cascade")) { issues.push({ severity: "high", description: "CASCADE DELETE can cause unintended data loss across related tables", location: "ON DELETE CASCADE constraint", line: lines.findIndex((line) => line.toLowerCase().includes("on delete cascade")) + 1 || 1, recommendation: "Consider using ON DELETE RESTRICT or SET NULL with application-level cleanup logic" }); } return { applicable: true, confidence: 0.9, issues, impact: { riskReduction: 0.8, performanceImprovement: 0, complexityAdded: 0.3, description: "Prevents accidental data loss from cascade deletions" } }; } }; CascadeDeleteApplicator = class { async apply(content, migration) { let modifiedContent = content; const changes = []; const lines = content.split("\n"); for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line.toLowerCase().includes("on delete cascade")) { const warnings = [ "-- \u26A0\uFE0F CASCADE DELETE WARNING:", "-- This will automatically delete related records in child tables.", "-- Consider safer alternatives:", "-- 1. ON DELETE RESTRICT (prevents deletion if child records exist)", "-- 2. ON DELETE SET NULL (sets foreign key to NULL)", "-- 3. Application-level cleanup logic" ]; lines.splice(i, 0, ...warnings); changes.push("Added CASCADE DELETE safety warning"); i += warnings.length; } } if (changes.length > 0) { modifiedContent = lines.join("\n"); } return { enhancement: enhancement8, applied: changes.length > 0, modifiedContent, warnings: changes.length === 0 ? ["No cascade delete operations found"] : [], changes: changes.map((change) => ({ type: "ADDED", original: "ON DELETE CASCADE", modified: change, line: 1, reason: change })) }; } }; cascadeDeleteModule = { enhancement: enhancement8, detector: new CascadeDeleteDetector(), applicator: new CascadeDeleteApplicator() }; } }); // src/enhancements/safety/unique-constraint.ts var enhancement9, UniqueConstraintDetector, UniqueConstraintApplicator, uniqueConstraintModule; var init_unique_constraint = __esm({ "src/enhancements/safety/unique-constraint.ts"() { "use strict"; enhancement9 = { id: "safety-unique-constraint", name: "Unique Constraint Safety", description: "Warns about potential issues when adding unique constraints to existing tables with data", category: "safety", priority: 7, requiresConfirmation: true, tags: ["unique", "constraint", "safety", "data-integrity"] }; UniqueConstraintDetector = class { async detect(migration) { const content = migration.up.toLowerCase(); return content.includes("unique") && (content.includes("constraint") || content.includes("add unique") || content.includes("create unique")); } async analyze(migration) { const applicable = await this.detect(migration); if (!applicable) { return { applicable: false, confidence: 0, issues: [], impact: { riskReduction: 0, performanceImprovement: 0, complexityAdded: 0, description: "No unique constraints detected" } }; } const issues = []; const content = migration.up.toLowerCase(); const lines = migration.up.split("\n"); if (content.includes("add unique") || content.includes("add constraint") && content.includes("unique")) { issues.push({ severity: "medium", description: "Adding unique constraint to existing table may fail if duplicate values exist", location: "UNIQUE constraint", line: lines.findIndex((line) => line.toLowerCase().includes("unique")) + 1 || 1, recommendation: "Verify data uniqueness before adding constraint or clean up duplicates first" }); } return { applicable: true, confidence: 0.8, issues, impact: { riskReduction: 0.6, performanceImprovement: 0, complexityAdded: 0.3, description: "Prevents constraint violation errors during migration" } }; } }; UniqueConstraintApplicator = class { async apply(content, migration) { let modifiedContent = content; const changes = []; const lines = content.split("\n"); for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line.toLowerCase().includes("unique") && (line.toLowerCase().includes("constraint") || line.toLowerCase().includes("add unique"))) { const warnings = [ "-- UNIQUE CONSTRAINT WARNING:", "-- Ensure no duplicate values exist before adding unique constraint:", "-- SELECT column_name, COUNT(*) FROM table_name GROUP BY column_name HAVING COUNT(*) > 1;" ]; lines.splice(i, 0, ...warnings); changes.push("Added unique constraint safety check"); i += warnings.length; } } if (changes.length > 0) { modifiedContent = lines.join("\n"); } return { enhancement: enhancement9, applied: changes.length > 0, modifiedContent, warnings: changes.length === 0 ? ["No unique constraints found"] : [], changes: changes.map((change) => ({ type: "ADDED", original: "UNIQUE constraint", modified: change, line: 1, reason: change })) }; } }; uniqueConstraintModule = { enhancement: enhancement9, detector: new UniqueConstraintDetector(), applicator: new UniqueConstraintApplicator() }; } }); // src/enhancements/safety/check-constraint.ts var enhancement10, CheckConstraintDetector, CheckConstraintApplicator, checkConstraintModule; var init_check_constraint = __esm({ "src/enhancements/safety/check-constraint.ts"() { "use strict"; enhancement10 = { id: "safety-check-constraint", name: "Check Constraint Safety", description: "Warns about potential issues when adding check constraints to existing tables", category: "safety", priority: 6, requiresConfirmation: true, tags: ["check", "constraint", "validation", "safety"] }; CheckConstraintDetector = class { async detect(migration) { const content = migration.up.toLowerCase(); return content.includes("check") && content.includes("constraint"); } async analyze(migration) { const applicable = await this.detect(migration); if (!applicable) { return { applicable: false, confidence: 0, issues: [], impact: { riskReduction: 0, performanceImprovement: 0, complexityAdded: 0, description: "No check constraints detected" } }; } const issues = []; const content = migration.up.toLowerCase(); const lines = migration.up.split("\n"); if (content.includes("add constraint") && content.includes("check")) { issues.push({ severity: "medium", description: "Adding check constraint to existing table may fail if data violates the constraint", location: "CHECK constraint", line: lines.findIndex((line) => line.toLowerCase().includes("check")) + 1 || 1, recommendation: "Verify existing data meets constraint requirements before adding" }); } return { applicable: true, confidence: 0.8, issues, impact: { riskReduction: 0.6, performanceImprovement: 0, complexityAdded: 0.3, description: "Prevents constraint violation errors during migration" } }; } }; CheckConstraintApplicator = class { async apply(content, migration) { let modifiedContent = content; const changes = []; const lines = content.split("\n"); for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line.toLowerCase().includes("check") && line.toLowerCase().includes("constraint")) { const warnings = [ "-- CHECK CONSTRAINT WARNING:", "-- Verify existing data meets constraint requirements:", "-- SELECT * FROM table_name WHERE NOT (constraint_condition);" ]; lines.splice(i, 0, ...warnings); changes.push("Added check constraint validation warning"); i += warnings.length; } } if (changes.length > 0) { modifiedContent = lines.join("\n"); } return { enhancement: enhancement10, applied: changes.length > 0, modifiedContent, warnings: changes.length === 0 ? ["No check constraints found"] : [], changes: changes.map((change) => ({ type: "ADDED", original: "CHECK constraint", modified: change, line: 1, reason: change })) }; } }; checkConstraintModule = { enhancement: enhancement10, detector: new CheckConstraintDetector(), applicator: new CheckConstraintApplicator() }; } }); // src/enhancements/safety/backup-recommendation.ts var enhancement11, BackupRecommendationDetector, BackupRecommendationApplicator, backupRecommendationModule; var init_backup_recommendation = __esm({ "src/enhancements/safety/backup-recommendation.ts"() { "use strict"; enhancement11 = { id: "safety-backup-recommendation", name: "Backup Recommendation", description: "Recommends taking database backups before executing risky migration operations", category: "safety", priority: 9, requiresConfirmation: false, tags: ["backup", "safety", "data-protection", "risk-mitigation"] }; BackupRecommendationDetector = class { async detect(migration) { const content = migration.up.toLowerCase(); const riskyOperations = [ "drop table", "drop column", "alter column", "truncate", "delete from", "update", "drop index", "drop constraint" ]; return riskyOperations.some((op) => content.includes(op)); } async analyze(migration) { const applicable = await this.detect(migration); if (!applicable) { return { applicable: false, confidence: 0, issues: [], impact: { riskReduction: 0, performanceImprovement: 0, complexityAdded: 0, description: "No risky operations detected" } }; } const issues = []; const content = migration.up.toLowerCase(); const lines = migration.up.split("\n"); const riskyOperations = [ { operation: "drop table", severity: "critical", description: "Dropping tables permanently removes all data" }, { operation: "drop column", severity: "high", description: "Dropping columns permanently removes data" }, { operation: "truncate", severity: "critical", description: "Truncating tables removes all data" }, { operation: "delete from", severity: "high", description: "Mass deletion operations can remove critical data" } ]; for (const op of riskyOperations) { if (content.includes(op.operation)) { issues.push({ severity: op.severity, description: op.description, location: op.operation.toUpperCase(), line: lines.findIndex((line) => line.toLowerCase().includes(op.operation)) + 1 || 1, recommendation: "Create a database backup before executing this migration" }); } } return { applicable: true, confidence: 0.9, issues, impact: { riskReduction: 0.9, performanceImprovement: 0, complexityAdded: 0.1, description: "Adds backup recommendations for data safety" } }; } }; BackupRecommendationApplicator = class { async apply(content, migration) { const changes = []; const backupWarning = [ "-- \u26A0\uFE0F IMPORTANT: BACKUP RECOMMENDATION", "-- This migration contains potentially destructive operations.", "-- It is STRONGLY recommended to create a full database backup before proceeding.", "-- Command example: pg_dump database_name > backup_$(date +%Y%m%d_%H%M%S).sql", "--" ].join("\n"); const modifiedContent = backupWarning + "\n" + content; changes.push("Added backup recommendation warning"); return { enhancement: enhancement11, applied: true, modifiedContent, warnings: [], changes: changes.map((change) => ({ type: "ADDED", original: "", modified: change, line: 1, reason: change })) }; } }; backupRecommendationModule = { enhancement: enhancement11, detector: new BackupRecommendationDetector(), applicator: new BackupRecommendationApplicator() }; } }); // src/enhancements/safety/migration-order.ts var enhancement12, MigrationOrderDetector, MigrationOrderApplicator, migrationOrderModule; var init_migration_order = __esm({ "src/enhancements/safety/migration-order.ts"() { "use strict"; enhancement12 = { id: "safety-migration-order", name: "Migration Order Safety", description: "Checks for potential issues with the order of operations in migrations", category: "safety", priority: 8, requiresConfirmation: false, tags: ["order", "sequence", "dependencies", "safety"] }; MigrationOrderDetector = class { async detect(migration) { const content = migration.up.toLowerCase(); return content.includes("drop") && content.includes("create") || content.includes("alter") && content.includes("add") || content.includes("insert") && content.includes("create"); } async analyze(migration) { const applicable = await this.detect(migration); if (!applicable) { return { applicable: false, confidence: 0, issues: [], impact: { riskReduction: 0, performanceImprovement: 0, complexityAdded: 0, description: "No order-sensitive operations detected" } }; } const issues = []; const content = migration.up.toLowerCase(); const lines = migration.up.split("\n"); if (content.includes("insert") && content.includes("create table")) { const insertIndex = lines.findIndex((line) => line.toLowerCase().includes("insert")); const createIndex = lines.findIndex((line) => line.toLowerCase().includes("create table")); if (insertIndex >= 0 && createIndex >= 0 && insertIndex < createIndex) { issues.push({ severity: "high", description: "INSERT statement appears before CREATE TABLE - this will cause an error", location: "INSERT before CREATE TABLE", line: insertIndex + 1, recommendation: "Move CREATE TABLE statements before INSERT statements" }); } } return { applicable: true, confidence: 0.7, issues, impact: { riskReduction: 0.6, performanceImprovement: 0, complexityAdded: 0.2, description: "Prevents migration failures due to incorrect operation ordering" } }; } }; MigrationOrderApplicator = class { async apply(content, migration) { let modifiedContent = content; const changes = []; const orderingGuideline = [ "-- MIGRATION ORDER GUIDELINES:", "-- 1. CREATE statements (tables, indexes) first", "-- 2. ALTER statements (modify existing structures)", "-- 3. INSERT/UPDATE statements (data operations)", "-- 4. DROP statements (remove structures) last", "--" ].join("\n"); modifiedContent = orderingGuideline + "\n" + content; changes.push("Added migration order guidelines"); return { enhancement: enhancement12, applied: true, modifiedContent, warnings: [], changes: changes.map((change) => ({ type: "ADDED", original: "", modified: change, line: 1, reason: change })) }; } }; migrationOrderModule = { enhancement: enhancement12, detector: new MigrationOrderDetector(), applicator: new MigrationOrderApplicator() }; } }); // src/enhancements/safety/index.ts async function loadSafetyEnhancements() { return [ transactionWrapperModule, dropTableSafeguardModule, foreignKeyConstraintModule, nullableColumnModule, indexCreationModule, dataTypeChangeModule, columnRenamingModule, cascadeDeleteModule, uniqueConstraintModule, checkConstraintModule, backupRecommendationModule, migrationOrderModule ]; } var init_safety = __esm({ "src/enhancements/safety/index.ts"() { "use strict"; init_transaction_wrapper(); init_drop_table_safeguard(); init_foreign_key_constraint(); init_nullable_column(); init_index_creation(); init_data_type_change(); init_column_renaming(); init_cascade_delete(); init_unique_constraint(); init_check_constraint(); init_backup_recommendation(); init_migration_order(); } }); // src/enhancements/speed/remaining-stubs.ts var StubDetector2, StubApplicator2, batchInsertEnhancement, batchInsertModule, partialIndexEnhancement, partialIndexModule, indexOptimizationEnhancement, indexOptimizationModule, concurrentIndexEnhancement,