UNPKG

prisma-migrator

Version:

A Node.js library that extends Prisma ORM's migration with automatic rollback capabilities when migrations fail

183 lines 7.86 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.PrismaMigrator = void 0; const child_process_1 = require("child_process"); const client_1 = require("@prisma/client"); const utils_1 = require("./utils"); class PrismaMigrator { constructor(options = {}) { this.logger = new utils_1.Logger(); this.prisma = new client_1.PrismaClient(); this.options = options; } async initialize() { this.migrationsDir = await (0, utils_1.findMigrationsDir)(this.options.migrationsDir); this.logger.debug(`Using migrations directory: ${this.migrationsDir}`); } async migrate() { try { await this.initialize(); this.logger.info('Starting migration with prisma migrate deploy...'); const migrateCommand = `npx prisma migrate deploy`; this.logger.debug(`Executing: ${migrateCommand}`); let stdout; try { stdout = (0, child_process_1.execSync)(migrateCommand, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }); } catch (execError) { this.logger.error('Migration command failed'); const errorOutput = execError.stderr ? execError.stderr.toString() : execError.message; const rollbackResult = await this.attemptRollbackFromError(errorOutput); return { success: false, error: errorOutput, rolledBack: rollbackResult.success, rollbackError: rollbackResult.error }; } this.logger.success('Prisma migrate deploy completed'); this.logger.debug(`Migration output: ${stdout}`); return { success: true }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.logger.error(`Migration process failed: ${errorMessage}`); const failedMigration = await this.checkForFailedMigrations(); if (failedMigration) { this.logger.warn(`Failed migration detected: ${failedMigration.migration_name}`); const rollbackResult = await this.attemptRollback(failedMigration.migration_name); return { success: false, error: failedMigration.logs || errorMessage, rolledBack: rollbackResult.success, rollbackError: rollbackResult.error }; } return { success: false, error: errorMessage }; } } async checkForFailedMigrations() { try { this.logger.debug('Checking _prisma_migrations table for failures...'); const failedMigrations = await this.prisma.$queryRaw ` SELECT * FROM "_prisma_migrations" WHERE "finished_at" IS NULL ORDER BY "started_at" DESC LIMIT 1 `; if (failedMigrations.length > 0) { this.logger.debug(`Found failed migration: ${failedMigrations[0].migration_name}`); return failedMigrations[0]; } return null; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.logger.debug(`Error checking for failed migrations: ${errorMessage}`); return null; } } async attemptRollback(migrationName) { try { this.logger.warn('Attempting rollback...'); const rollbackFile = await (0, utils_1.getRollbackFile)(migrationName, this.migrationsDir); if (!rollbackFile) { this.logger.warn(`No rollback.sql file found for migration: ${migrationName}`); return { success: false, error: 'No rollback.sql file found' }; } this.logger.info(`Found rollback file: ${rollbackFile}`); const rollbackSql = await (0, utils_1.readFile)(rollbackFile); await (0, utils_1.executeSql)(rollbackSql, this.prisma, this.logger); this.logger.success('Rollback executed successfully'); return { success: true }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.logger.error(`Rollback failed: ${errorMessage}`); return { success: false, error: errorMessage }; } } async attemptRollbackFromError(errorOutput) { try { this.logger.warn('Attempting rollback from error output...'); const migrationDirs = await this.findRecentMigrationDirs(); for (const migrationName of migrationDirs) { const rollbackFile = await (0, utils_1.getRollbackFile)(migrationName, this.migrationsDir); if (rollbackFile) { this.logger.info(`Found rollback file for migration: ${migrationName}`); const rollbackSql = await (0, utils_1.readFile)(rollbackFile); await (0, utils_1.executeSql)(rollbackSql, this.prisma, this.logger); this.logger.success('Rollback executed successfully'); return { success: true }; } } this.logger.warn('No rollback files found for recent migrations'); return { success: false, error: 'No rollback files found' }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.logger.error(`Rollback failed: ${errorMessage}`); return { success: false, error: errorMessage }; } } async findRecentMigrationDirs() { try { const fs = await Promise.resolve().then(() => __importStar(require('fs/promises'))); const migrations = await fs.readdir(this.migrationsDir); return migrations .filter(name => name.match(/^\d{14}_/)) .sort() .reverse() .slice(0, 3); } catch { return []; } } async disconnect() { await this.prisma.$disconnect(); } } exports.PrismaMigrator = PrismaMigrator; //# sourceMappingURL=migrator.js.map