@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
JavaScript
#!/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,