@md-oss/migrations 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,5 @@
1
+ Copyright 2026 Mirasaki Development
2
+
3
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
4
+
5
+ THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # @md-oss/migrations
2
+
3
+ Lightweight migration framework with registry, runner, progress tracking, validation, and rollback support.
4
+
5
+ ## Features
6
+
7
+ - **Registry & Discovery** – Register migrations manually or auto-discover from files
8
+ - **Runner** – Execute pending migrations with optional dry-run, timeouts, and rollback
9
+ - **Validation** – Pre-flight validation hooks with warnings
10
+ - **Progress Tracking** – Built-in progress logger for long-running jobs
11
+ - **Rich Errors** – Specific error classes for duplicates, timeouts, rollbacks, and validation
12
+ - **Typed Contracts** – Strongly-typed migration context and results
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ pnpm add @md-oss/migrations
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ```typescript
23
+ import { MigrationRegistry, MigrationRunner, createDefaultLogger } from '@md-oss/migrations';
24
+ import type { Migration } from '@md-oss/migrations';
25
+ import crypto from 'node:crypto';
26
+
27
+ // 1) Define a migration
28
+ const addUsersIndex: Migration = {
29
+ name: '2026-01-27-add-users-index',
30
+ description: 'Add index on users(email)',
31
+ async isCompleted() {
32
+ return false; // check DB metadata
33
+ },
34
+ async isFailing() {
35
+ return false;
36
+ },
37
+ async hasFailed() {
38
+ return false;
39
+ },
40
+ async markComplete() {},
41
+ async markFailed(_ctx, _err) {},
42
+ async markRollback() {},
43
+ async up(ctx) {
44
+ ctx.logger.info('Running migration logic');
45
+ // apply changes here
46
+ },
47
+ async down(ctx) {
48
+ ctx.logger.info('Rolling back migration');
49
+ // rollback changes here
50
+ },
51
+ async validate() {
52
+ return { valid: true };
53
+ },
54
+ };
55
+
56
+ // 2) Register migrations
57
+ const registry = new MigrationRegistry();
58
+ registry.register(addUsersIndex);
59
+
60
+ // 3) Run pending migrations
61
+ const runner = new MigrationRunner(registry, {
62
+ logger: createDefaultLogger(),
63
+ metadata: { runId: crypto.randomUUID(), startedAt: new Date() },
64
+ dryRun: false,
65
+ });
66
+
67
+ await runner.runPending({ skipCompleted: true });
68
+ ```
69
+
70
+ ## Discover Migrations from Files
71
+
72
+ ```typescript
73
+ import path from 'node:path';
74
+
75
+ const registry = new MigrationRegistry({ debug: true });
76
+ await registry.discoverFrom(path.join(process.cwd(), 'migrations'), {
77
+ pattern: /\.migration\.(ts|js)$/,
78
+ recursive: true,
79
+ });
80
+ ```
81
+
82
+ ## Runner Options
83
+
84
+ - `skipCompleted` – Skip migrations already marked complete (default: true)
85
+ - `continueOnError` – Keep running remaining migrations when one fails
86
+ - `timeout` – Fail a migration if it exceeds the timeout (ms)
87
+ - `dryRun` – Execute without calling `markComplete` / `markFailed`
88
+
89
+ ## Progress Tracking
90
+
91
+ ```typescript
92
+ import { MigrationProgress, createDefaultLogger } from '@md-oss/migrations';
93
+
94
+ const progress = new MigrationProgress(createDefaultLogger(), 1_000);
95
+ for (let i = 0; i < 1_000; i++) {
96
+ // do work
97
+ progress.increment();
98
+ }
99
+ progress.complete();
100
+ ```
101
+
102
+ ## Error Types
103
+
104
+ - MigrationAlreadyRunningError
105
+ - MigrationAlreadyCompletedError
106
+ - MigrationValidationError
107
+ - MigrationExecutionError
108
+ - MigrationRollbackError
109
+ - MigrationNotRollbackableError
110
+ - MigrationNotFoundError
111
+ - MigrationTimeoutError
112
+ - DuplicateMigrationError
113
+
114
+ ## API Surface
115
+
116
+ - MigrationRegistry – register, discover, get, list migrations
117
+ - MigrationRunner – run/rollback migrations; run pending
118
+ - MigrationProgress – log long-running work
119
+ - createDefaultLogger / ConsoleLogger – colorful console logging
120
+ - Types: Migration, RunOptions, MigrationResult, MigrationStatus, MigrationSystemStatus, MigrationLogger
package/dist/index.cjs ADDED
@@ -0,0 +1,4 @@
1
+ "use strict";var O=Object.defineProperty;var c=(s,t)=>O(s,"name",{value:t,configurable:!0});var x=require("node:fs/promises"),F=require("node:path"),T=require("node:crypto");function M(s){var t=Object.create(null);return s&&Object.keys(s).forEach(function(r){if(r!=="default"){var i=Object.getOwnPropertyDescriptor(s,r);Object.defineProperty(t,r,i.get?i:{enumerable:!0,get:c(function(){return s[r]},"get")})}}),t.default=s,Object.freeze(t)}c(M,"_interopNamespaceDefault");var I=M(x),L=M(F);class d extends Error{static{c(this,"MigrationError")}constructor(t,r,i){super(t),this.migrationName=r,this.code=i,this.name="MigrationError",Error.captureStackTrace(this,this.constructor)}}class p extends d{static{c(this,"MigrationAlreadyRunningError")}constructor(t){super(`Migration "${t}" is already running`,t,"MIGRATION_ALREADY_RUNNING"),this.name="MigrationAlreadyRunningError"}}class m extends d{static{c(this,"MigrationAlreadyCompletedError")}constructor(t){super(`Migration "${t}" has already been completed`,t,"MIGRATION_ALREADY_COMPLETED"),this.name="MigrationAlreadyCompletedError"}}class y extends d{static{c(this,"MigrationValidationError")}constructor(t,r){super(`Migration "${t}" validation failed: ${r}`,t,"MIGRATION_VALIDATION_FAILED"),this.validationMessage=r,this.name="MigrationValidationError"}}class w extends d{static{c(this,"MigrationExecutionError")}constructor(t,r){super(`Migration "${t}" execution failed: ${r.message}`,t,"MIGRATION_EXECUTION_FAILED"),this.cause=r,this.name="MigrationExecutionError",this.stack=r.stack}}class R extends d{static{c(this,"MigrationRollbackError")}constructor(t,r){super(`Migration "${t}" rollback failed: ${r.message}`,t,"MIGRATION_ROLLBACK_FAILED"),this.cause=r,this.name="MigrationRollbackError",this.stack=r.stack}}class E extends d{static{c(this,"MigrationNotRollbackableError")}constructor(t){super(`Migration "${t}" does not support rollback`,t,"MIGRATION_NOT_ROLLBACKABLE"),this.name="MigrationNotRollbackableError"}}class $ extends d{static{c(this,"MigrationNotFoundError")}constructor(t){super(`Migration "${t}" not found in registry`,t,"MIGRATION_NOT_FOUND"),this.name="MigrationNotFoundError"}}class A extends d{static{c(this,"MigrationTimeoutError")}constructor(t,r){super(`Migration "${t}" timed out after ${r}ms`,t,"MIGRATION_TIMEOUT"),this.timeout=r,this.name="MigrationTimeoutError"}}class b extends d{static{c(this,"DuplicateMigrationError")}constructor(t){super(`Migration "${t}" is already registered`,t,"DUPLICATE_MIGRATION"),this.name="DuplicateMigrationError"}}const f={reset:"\x1B[0m",bright:"\x1B[1m",red:"\x1B[31m",yellow:"\x1B[33m",blue:"\x1B[34m",cyan:"\x1B[36m",gray:"\x1B[90m"};function v(s){if(!s||Object.keys(s).length===0)return"";try{return" "+JSON.stringify(s,null,2).split(`
2
+ `).map((t,r)=>r===0?t:` ${t}`).join(`
3
+ `)}catch{return" [Object]"}}c(v,"formatMeta");function D(){return new Date().toISOString()}c(D,"getTimestamp");class k{static{c(this,"ConsoleLogger")}constructor(t=!0){this.useColors=t}log(t,r,i,e){const n=this.useColors?`${f.gray}${D()}${f.reset}`:D(),o=this.useColors?`${r}${t.padEnd(5)}${f.reset}`:t.padEnd(5),l=this.useColors?`${f.bright}${i}${f.reset}`:i,a=v(e);console.log(`${n} ${o} ${l}${a}`)}info(t,r){this.log("INFO",f.blue,t,r)}warn(t,r){this.log("WARN",f.yellow,t,r)}error(t,r){this.log("ERROR",f.red,t,r)}debug(t,r){this.log("DEBUG",f.cyan,t,r)}}function P(){const s=process.stdout.isTTY&&process.env.FORCE_COLOR!=="0"&&process.env.NODE_DISABLE_COLORS===void 0;return new k(s)}c(P,"createDefaultLogger");class _{static{c(this,"MigrationProgress")}constructor(t,r,i=10){this.logger=t,this.total=r,this.logInterval=i,this.logger.info(`Starting processing of ${this.formatNumber(r)} items`)}processed=0;lastLoggedPercent=0;startTime=Date.now();increment(){this.processed++,this.checkAndLog()}add(t){this.processed+=t,this.checkAndLog()}set(t){this.processed=t,this.checkAndLog()}getPercent(){return this.total===0?100:Math.floor(this.processed/this.total*100)}getElapsed(){return Date.now()-this.startTime}getETA(){if(this.processed===0)return 0;const t=this.getElapsed(),r=this.processed/t;return(this.total-this.processed)/r}complete(){this.processed=this.total;const t=this.getElapsed(),r=this.total/(t/1e3);this.logger.info(`Completed processing ${this.formatNumber(this.total)} items in ${this.formatDuration(t)}`,{total:this.total,duration:t,rate:`${r.toFixed(2)} items/sec`})}checkAndLog(){const t=this.getPercent();(t>=this.lastLoggedPercent+this.logInterval||this.processed===this.total)&&(this.logProgress(),this.lastLoggedPercent=t)}logProgress(){const t=this.getPercent(),r=this.getElapsed(),i=this.getETA(),e=this.createProgressBar(t);this.logger.info(`Progress: ${e} ${t}% (${this.formatNumber(this.processed)}/${this.formatNumber(this.total)})`,{elapsed:this.formatDuration(r),eta:this.formatDuration(i)})}createProgressBar(t,r=20){const i=Math.floor(t/100*r),e=r-i;return`[${"\u2588".repeat(i)}${" ".repeat(e)}]`}formatNumber(t){return t.toLocaleString()}formatDuration(t){return t<1e3?`${Math.round(t)}ms`:t<6e4?`${(t/1e3).toFixed(1)}s`:t<36e5?`${(t/6e4).toFixed(1)}m`:`${(t/36e5).toFixed(1)}h`}}class S{static{c(this,"MigrationRegistry")}migrations=new Map;options;constructor(t={}){this.options=t}register(t){if(this.migrations.has(t.name))throw new b(t.name);this.migrations.set(t.name,t),this.options.debug&&console.debug(`[MigrationRegistry] Registered migration: ${t.name}`)}registerMany(t){for(const r of t)this.register(r)}get(t){const r=this.migrations.get(t);if(!r)throw new $(t);return r}has(t){return this.migrations.has(t)}getAll(){return Array.from(this.migrations.values())}getNames(){return Array.from(this.migrations.keys())}count(){return this.migrations.size}clear(){this.migrations.clear()}async discoverFrom(t,r={}){const{pattern:i=/\.migration\.(ts|js)$/,recursive:e=!1}=r,n=[];try{const o=await this.scanDirectory(t,i,e);for(const l of o)try{const a=await this.loadMigrationFile(l);n.push(...a)}catch(a){this.options.debug&&console.error(`[MigrationRegistry] Failed to load migration from ${l}:`,a)}return this.registerMany(n),this.options.debug&&console.debug(`[MigrationRegistry] Discovered ${n.length} migrations from ${t}`),n.length}catch(o){throw this.options.debug&&console.error(`[MigrationRegistry] Failed to discover migrations from ${t}:`,o),o}}async scanDirectory(t,r,i){const e=[];try{const n=await I.readdir(t,{withFileTypes:!0});for(const o of n){const l=L.join(t,o.name);if(o.isDirectory()&&i){const a=await this.scanDirectory(l,r,i);e.push(...a)}else o.isFile()&&r.test(o.name)&&e.push(l)}}catch(n){this.options.debug&&console.debug(`[MigrationRegistry] Cannot scan directory ${t}:`,n)}return e}async loadMigrationFile(t){const r=await import(t),i=[];r.default&&(this.isMigration(r.default)?i.push(r.default):Array.isArray(r.default)&&i.push(...r.default.filter(this.isMigration))),this.isMigration(r.migration)&&i.push(r.migration),Array.isArray(r.migrations)&&i.push(...r.migrations.filter(this.isMigration));for(const[e,n]of Object.entries(r))e!=="default"&&e!=="migration"&&e!=="migrations"&&this.isMigration(n)&&i.push(n);return i}isMigration(t){if(!t||typeof t!="object")return!1;const r=t;return typeof r.name=="string"&&typeof r.description=="string"&&typeof r.isCompleted=="function"&&typeof r.isFailing=="function"&&typeof r.hasFailed=="function"&&typeof r.markComplete=="function"&&typeof r.markFailed=="function"&&typeof r.up=="function"}}class B{static{c(this,"MigrationRunner")}constructor(t,r){this.registry=t,this.context=r}runningMigrations=new Set;async runPending(t={}){const r=this.buildContext(t),{skipCompleted:i=!0,continueOnError:e=!1}=t;r.logger.info("Starting pending migrations...");const n=this.registry.getAll(),o=[];for(const g of n)try{if(i&&await g.isCompleted(r)){r.logger.info(`Skipping completed migration: ${g.name}`),o.push({name:g.name,status:"skipped",duration:0,startedAt:new Date,completedAt:new Date});continue}const u=await this.run(g.name,t);o.push(u)}catch(u){if(r.logger.error(`Migration ${g.name} failed`,{error:u instanceof Error?u.message:String(u)}),o.push({name:g.name,status:"failed",duration:0,startedAt:new Date,completedAt:new Date,error:u instanceof Error?u:new Error(String(u))}),!e)throw u}const l=o.filter(g=>g.status==="completed").length,a=o.filter(g=>g.status==="failed").length,h=o.filter(g=>g.status==="skipped").length;return r.logger.info("All pending migrations processed",{total:o.length,completed:l,failed:a,skipped:h}),o}async run(t,r={}){const i=this.registry.get(t),e=this.buildContext(r),{skipCompleted:n=!0,timeout:o}=r;if(this.runningMigrations.has(t))throw new p(t);const l=await i.isCompleted(e);if(n&&l&&!e.dryRun)throw new m(t);this.runningMigrations.add(t);try{const a=Date.now();if(e.logger.info(e.dryRun?`[DRY RUN] Starting migration: ${t}`:`Starting migration: ${t}`,{description:i.description}),i.validate){const u=await i.validate(e);if(!u.valid)throw new y(t,u.message||"Validation failed");if(u.warnings&&u.warnings.length>0)for(const C of u.warnings)e.logger.warn(C)}o?await this.runWithTimeout(()=>i.up(e),o,t):await i.up(e);const h=Date.now()-a,g={name:t,status:"completed",duration:h,startedAt:new Date(a),completedAt:new Date};return e.dryRun||await i.markComplete(e,g),e.logger.info(e.dryRun?`[DRY RUN] Migration completed: ${t}`:`Migration completed: ${t}`,{duration:`${h}ms`}),g}catch(a){const h=a instanceof Error?a:new Error(String(a));throw e.logger.error(`Migration failed: ${t}`,{error:h.message,stack:h.stack}),e.dryRun||await i.markFailed(e,h),new w(t,h)}finally{this.runningMigrations.delete(t)}}async rollback(t,r={}){const i=this.registry.get(t),e=this.buildContext(r),{timeout:n}=r;if(!i.down)throw new E(t);if(this.runningMigrations.has(t))throw new p(t);this.runningMigrations.add(t);try{const o=Date.now();e.logger.info(e.dryRun?`[DRY RUN] Rolling back migration: ${t}`:`Rolling back migration: ${t}`),n?await this.runWithTimeout(async()=>{if(!i.down)throw new Error("Rollback function not defined");await i.down(e)},n,t):await i.down(e);const l=Date.now()-o,a={name:t,status:"rolled-back",duration:l,startedAt:new Date(o),completedAt:new Date};return e.logger.info(e.dryRun?`[DRY RUN] Migration rolled back: ${t}`:`Migration rolled back: ${t}`,{duration:`${l}ms`}),e.dryRun||await i.markRollback(e,a),a}catch(o){const l=o instanceof Error?o:new Error(String(o));throw e.logger.error(`Rollback failed: ${t}`,{error:l.message,stack:l.stack}),new R(t,l)}finally{this.runningMigrations.delete(t)}}async getStatus(){const t=this.buildContext({}),r=this.registry.getAll(),i=await Promise.all(r.map(async e=>{const[n,o,l,a]=await Promise.all([e.isCompleted(t),e.isFailing(t),e.hasFailed(t),Promise.resolve(this.runningMigrations.has(e.name))]);return{name:e.name,description:e.description,isCompleted:n,isFailing:o,hasFailed:l,isRunning:a,canRollback:!!e.down}}));return{total:i.length,pending:i.filter(e=>!e.isCompleted&&!e.hasFailed&&!e.isRunning),completed:i.filter(e=>e.isCompleted&&!e.isRunning),failed:{currentlyFailing:i.filter(e=>e.isFailing&&!e.isRunning),previouslyFailed:i.filter(e=>e.hasFailed&&!e.isRunning),recovered:i.filter(e=>e.isCompleted&&!e.isFailing&&e.hasFailed&&!e.isRunning)},running:i.filter(e=>e.isRunning)}}buildContext(t){const r={runId:T.randomUUID(),startedAt:new Date,triggeredBy:t.triggeredBy};return{...this.context,...t,metadata:r,dryRun:t.dryRun??!1}}async runWithTimeout(t,r,i){const e=new Promise((n,o)=>{setTimeout(()=>{o(new A(i,r))},r)});await Promise.race([t(),e])}}exports.ConsoleLogger=k,exports.DuplicateMigrationError=b,exports.MigrationAlreadyCompletedError=m,exports.MigrationAlreadyRunningError=p,exports.MigrationError=d,exports.MigrationExecutionError=w,exports.MigrationNotFoundError=$,exports.MigrationNotRollbackableError=E,exports.MigrationProgress=_,exports.MigrationRegistry=S,exports.MigrationRollbackError=R,exports.MigrationRunner=B,exports.MigrationTimeoutError=A,exports.MigrationValidationError=y,exports.createDefaultLogger=P;
4
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/errors.ts","../src/logger.ts","../src/progress.ts","../src/registry.ts","../src/runner.ts"],"sourcesContent":["/**\n * Base error class for all migration errors\n */\nexport class MigrationError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly migrationName?: string,\n\t\tpublic readonly code?: string\n\t) {\n\t\tsuper(message);\n\t\tthis.name = 'MigrationError';\n\t\tError.captureStackTrace(this, this.constructor);\n\t}\n}\n\n/**\n * Thrown when a migration is already running\n */\nexport class MigrationAlreadyRunningError extends MigrationError {\n\tconstructor(migrationName: string) {\n\t\tsuper(\n\t\t\t`Migration \"${migrationName}\" is already running`,\n\t\t\tmigrationName,\n\t\t\t'MIGRATION_ALREADY_RUNNING'\n\t\t);\n\t\tthis.name = 'MigrationAlreadyRunningError';\n\t}\n}\n\n/**\n * Thrown when a migration has already been completed\n */\nexport class MigrationAlreadyCompletedError extends MigrationError {\n\tconstructor(migrationName: string) {\n\t\tsuper(\n\t\t\t`Migration \"${migrationName}\" has already been completed`,\n\t\t\tmigrationName,\n\t\t\t'MIGRATION_ALREADY_COMPLETED'\n\t\t);\n\t\tthis.name = 'MigrationAlreadyCompletedError';\n\t}\n}\n\n/**\n * Thrown when a migration validation fails\n */\nexport class MigrationValidationError extends MigrationError {\n\tconstructor(\n\t\tmigrationName: string,\n\t\tpublic readonly validationMessage: string\n\t) {\n\t\tsuper(\n\t\t\t`Migration \"${migrationName}\" validation failed: ${validationMessage}`,\n\t\t\tmigrationName,\n\t\t\t'MIGRATION_VALIDATION_FAILED'\n\t\t);\n\t\tthis.name = 'MigrationValidationError';\n\t}\n}\n\n/**\n * Thrown when a migration execution fails\n */\nexport class MigrationExecutionError extends MigrationError {\n\tconstructor(\n\t\tmigrationName: string,\n\t\tpublic override readonly cause: Error\n\t) {\n\t\tsuper(\n\t\t\t`Migration \"${migrationName}\" execution failed: ${cause.message}`,\n\t\t\tmigrationName,\n\t\t\t'MIGRATION_EXECUTION_FAILED'\n\t\t);\n\t\tthis.name = 'MigrationExecutionError';\n\t\tthis.stack = cause.stack;\n\t}\n}\n\n/**\n * Thrown when a migration rollback fails\n */\nexport class MigrationRollbackError extends MigrationError {\n\tconstructor(\n\t\tmigrationName: string,\n\t\tpublic override readonly cause: Error\n\t) {\n\t\tsuper(\n\t\t\t`Migration \"${migrationName}\" rollback failed: ${cause.message}`,\n\t\t\tmigrationName,\n\t\t\t'MIGRATION_ROLLBACK_FAILED'\n\t\t);\n\t\tthis.name = 'MigrationRollbackError';\n\t\tthis.stack = cause.stack;\n\t}\n}\n\n/**\n * Thrown when attempting to rollback a migration that doesn't support it\n */\nexport class MigrationNotRollbackableError extends MigrationError {\n\tconstructor(migrationName: string) {\n\t\tsuper(\n\t\t\t`Migration \"${migrationName}\" does not support rollback`,\n\t\t\tmigrationName,\n\t\t\t'MIGRATION_NOT_ROLLBACKABLE'\n\t\t);\n\t\tthis.name = 'MigrationNotRollbackableError';\n\t}\n}\n\n/**\n * Thrown when a migration is not found in the registry\n */\nexport class MigrationNotFoundError extends MigrationError {\n\tconstructor(migrationName: string) {\n\t\tsuper(\n\t\t\t`Migration \"${migrationName}\" not found in registry`,\n\t\t\tmigrationName,\n\t\t\t'MIGRATION_NOT_FOUND'\n\t\t);\n\t\tthis.name = 'MigrationNotFoundError';\n\t}\n}\n\n/**\n * Thrown when a migration times out\n */\nexport class MigrationTimeoutError extends MigrationError {\n\tconstructor(\n\t\tmigrationName: string,\n\t\tpublic readonly timeout: number\n\t) {\n\t\tsuper(\n\t\t\t`Migration \"${migrationName}\" timed out after ${timeout}ms`,\n\t\t\tmigrationName,\n\t\t\t'MIGRATION_TIMEOUT'\n\t\t);\n\t\tthis.name = 'MigrationTimeoutError';\n\t}\n}\n\n/**\n * Thrown when attempting to register a duplicate migration\n */\nexport class DuplicateMigrationError extends MigrationError {\n\tconstructor(migrationName: string) {\n\t\tsuper(\n\t\t\t`Migration \"${migrationName}\" is already registered`,\n\t\t\tmigrationName,\n\t\t\t'DUPLICATE_MIGRATION'\n\t\t);\n\t\tthis.name = 'DuplicateMigrationError';\n\t}\n}\n","import type { MigrationLogger } from './types';\n\n/**\n * ANSI color codes for console output\n */\nconst colors = {\n\treset: '\\x1b[0m',\n\tbright: '\\x1b[1m',\n\tdim: '\\x1b[2m',\n\tred: '\\x1b[31m',\n\tgreen: '\\x1b[32m',\n\tyellow: '\\x1b[33m',\n\tblue: '\\x1b[34m',\n\tcyan: '\\x1b[36m',\n\tgray: '\\x1b[90m',\n} as const;\n\n/**\n * Format metadata for logging\n */\nfunction formatMeta(meta?: Record<string, unknown>): string {\n\tif (!meta || Object.keys(meta).length === 0) {\n\t\treturn '';\n\t}\n\n\ttry {\n\t\treturn (\n\t\t\t' ' +\n\t\t\tJSON.stringify(meta, null, 2)\n\t\t\t\t.split('\\n')\n\t\t\t\t.map((line, i) => (i === 0 ? line : ` ${line}`))\n\t\t\t\t.join('\\n')\n\t\t);\n\t} catch {\n\t\treturn ' [Object]';\n\t}\n}\n\n/**\n * Get current timestamp string\n */\nfunction getTimestamp(): string {\n\treturn new Date().toISOString();\n}\n\n/**\n * Default console logger implementation\n */\nexport class ConsoleLogger implements MigrationLogger {\n\tconstructor(private useColors = true) {}\n\n\tprivate log(\n\t\tlevel: 'INFO' | 'WARN' | 'ERROR' | 'DEBUG',\n\t\tcolor: string,\n\t\tmessage: string,\n\t\tmeta?: Record<string, unknown>\n\t): void {\n\t\tconst timestamp = this.useColors\n\t\t\t? `${colors.gray}${getTimestamp()}${colors.reset}`\n\t\t\t: getTimestamp();\n\n\t\tconst levelStr = this.useColors\n\t\t\t? `${color}${level.padEnd(5)}${colors.reset}`\n\t\t\t: level.padEnd(5);\n\n\t\tconst formattedMessage = this.useColors\n\t\t\t? `${colors.bright}${message}${colors.reset}`\n\t\t\t: message;\n\n\t\tconst metaStr = formatMeta(meta);\n\n\t\tconsole.log(`${timestamp} ${levelStr} ${formattedMessage}${metaStr}`);\n\t}\n\n\tinfo(message: string, meta?: Record<string, unknown>): void {\n\t\tthis.log('INFO', colors.blue, message, meta);\n\t}\n\n\twarn(message: string, meta?: Record<string, unknown>): void {\n\t\tthis.log('WARN', colors.yellow, message, meta);\n\t}\n\n\terror(message: string, meta?: Record<string, unknown>): void {\n\t\tthis.log('ERROR', colors.red, message, meta);\n\t}\n\n\tdebug(message: string, meta?: Record<string, unknown>): void {\n\t\tthis.log('DEBUG', colors.cyan, message, meta);\n\t}\n}\n\n/**\n * Create a default logger instance\n */\nexport function createDefaultLogger(): MigrationLogger {\n\tconst isColorSupported =\n\t\tprocess.stdout.isTTY &&\n\t\tprocess.env.FORCE_COLOR !== '0' &&\n\t\tprocess.env.NODE_DISABLE_COLORS === undefined;\n\n\treturn new ConsoleLogger(isColorSupported);\n}\n","import type { MigrationLogger } from './types';\n\n/**\n * Progress tracker for long-running migrations\n */\nexport class MigrationProgress {\n\tprivate processed = 0;\n\tprivate lastLoggedPercent = 0;\n\tprivate startTime = Date.now();\n\n\tconstructor(\n\t\tprivate logger: MigrationLogger,\n\t\tprivate total: number,\n\t\tprivate logInterval = 10 // Log every 10% by default\n\t) {\n\t\tthis.logger.info(\n\t\t\t`Starting processing of ${this.formatNumber(total)} items`\n\t\t);\n\t}\n\n\t/**\n\t * Increment progress by one\n\t */\n\tincrement(): void {\n\t\tthis.processed++;\n\t\tthis.checkAndLog();\n\t}\n\n\t/**\n\t * Increment progress by a specific amount\n\t */\n\tadd(amount: number): void {\n\t\tthis.processed += amount;\n\t\tthis.checkAndLog();\n\t}\n\n\t/**\n\t * Set current progress to a specific value\n\t */\n\tset(value: number): void {\n\t\tthis.processed = value;\n\t\tthis.checkAndLog();\n\t}\n\n\t/**\n\t * Get current progress percentage\n\t */\n\tgetPercent(): number {\n\t\treturn this.total === 0\n\t\t\t? 100\n\t\t\t: Math.floor((this.processed / this.total) * 100);\n\t}\n\n\t/**\n\t * Get elapsed time in milliseconds\n\t */\n\tgetElapsed(): number {\n\t\treturn Date.now() - this.startTime;\n\t}\n\n\t/**\n\t * Get estimated time remaining in milliseconds\n\t */\n\tgetETA(): number {\n\t\tif (this.processed === 0) return 0;\n\n\t\tconst elapsed = this.getElapsed();\n\t\tconst rate = this.processed / elapsed;\n\t\tconst remaining = this.total - this.processed;\n\n\t\treturn remaining / rate;\n\t}\n\n\t/**\n\t * Complete the progress tracking\n\t */\n\tcomplete(): void {\n\t\tthis.processed = this.total;\n\t\tconst elapsed = this.getElapsed();\n\t\tconst rate = this.total / (elapsed / 1000);\n\n\t\tthis.logger.info(\n\t\t\t`Completed processing ${this.formatNumber(this.total)} items in ${this.formatDuration(elapsed)}`,\n\t\t\t{\n\t\t\t\ttotal: this.total,\n\t\t\t\tduration: elapsed,\n\t\t\t\trate: `${rate.toFixed(2)} items/sec`,\n\t\t\t}\n\t\t);\n\t}\n\n\t/**\n\t * Check if we should log progress and do so\n\t */\n\tprivate checkAndLog(): void {\n\t\tconst currentPercent = this.getPercent();\n\n\t\t// Log at intervals or when complete\n\t\tif (\n\t\t\tcurrentPercent >= this.lastLoggedPercent + this.logInterval ||\n\t\t\tthis.processed === this.total\n\t\t) {\n\t\t\tthis.logProgress();\n\t\t\tthis.lastLoggedPercent = currentPercent;\n\t\t}\n\t}\n\n\t/**\n\t * Log current progress\n\t */\n\tprivate logProgress(): void {\n\t\tconst percent = this.getPercent();\n\t\tconst elapsed = this.getElapsed();\n\t\tconst eta = this.getETA();\n\n\t\tconst progressBar = this.createProgressBar(percent);\n\n\t\tthis.logger.info(\n\t\t\t`Progress: ${progressBar} ${percent}% (${this.formatNumber(this.processed)}/${this.formatNumber(this.total)})`,\n\t\t\t{\n\t\t\t\telapsed: this.formatDuration(elapsed),\n\t\t\t\teta: this.formatDuration(eta),\n\t\t\t}\n\t\t);\n\t}\n\n\t/**\n\t * Create a visual progress bar\n\t */\n\tprivate createProgressBar(percent: number, width = 20): string {\n\t\tconst filled = Math.floor((percent / 100) * width);\n\t\tconst empty = width - filled;\n\n\t\treturn `[${'█'.repeat(filled)}${' '.repeat(empty)}]`;\n\t}\n\n\t/**\n\t * Format a number with thousands separators\n\t */\n\tprivate formatNumber(num: number): string {\n\t\treturn num.toLocaleString();\n\t}\n\n\t/**\n\t * Format duration in milliseconds to human-readable string\n\t */\n\tprivate formatDuration(ms: number): string {\n\t\tif (ms < 1000) return `${Math.round(ms)}ms`;\n\t\tif (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n\t\tif (ms < 3600000) return `${(ms / 60000).toFixed(1)}m`;\n\t\treturn `${(ms / 3600000).toFixed(1)}h`;\n\t}\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { DuplicateMigrationError, MigrationNotFoundError } from './errors';\nimport type { BaseMigrationContext, Migration, RegistryOptions } from './types';\n\n/**\n * Registry for managing migrations\n */\nexport class MigrationRegistry<\n\tTContext extends BaseMigrationContext = BaseMigrationContext,\n> {\n\tprivate migrations = new Map<string, Migration<TContext>>();\n\tprivate options: RegistryOptions;\n\n\tconstructor(options: RegistryOptions = {}) {\n\t\tthis.options = options;\n\t}\n\n\t/**\n\t * Register a migration\n\t */\n\tregister(migration: Migration<TContext>): void {\n\t\tif (this.migrations.has(migration.name)) {\n\t\t\tthrow new DuplicateMigrationError(migration.name);\n\t\t}\n\n\t\tthis.migrations.set(migration.name, migration);\n\n\t\tif (this.options.debug) {\n\t\t\tconsole.debug(\n\t\t\t\t`[MigrationRegistry] Registered migration: ${migration.name}`\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Register multiple migrations at once\n\t */\n\tregisterMany(migrations: Migration<TContext>[]): void {\n\t\tfor (const migration of migrations) {\n\t\t\tthis.register(migration);\n\t\t}\n\t}\n\n\t/**\n\t * Get a migration by name\n\t */\n\tget(name: string): Migration<TContext> {\n\t\tconst migration = this.migrations.get(name);\n\n\t\tif (!migration) {\n\t\t\tthrow new MigrationNotFoundError(name);\n\t\t}\n\n\t\treturn migration;\n\t}\n\n\t/**\n\t * Check if a migration exists\n\t */\n\thas(name: string): boolean {\n\t\treturn this.migrations.has(name);\n\t}\n\n\t/**\n\t * Get all registered migrations\n\t */\n\tgetAll(): Migration<TContext>[] {\n\t\treturn Array.from(this.migrations.values());\n\t}\n\n\t/**\n\t * Get all migration names\n\t */\n\tgetNames(): string[] {\n\t\treturn Array.from(this.migrations.keys());\n\t}\n\n\t/**\n\t * Get count of registered migrations\n\t */\n\tcount(): number {\n\t\treturn this.migrations.size;\n\t}\n\n\t/**\n\t * Clear all migrations from registry\n\t */\n\tclear(): void {\n\t\tthis.migrations.clear();\n\t}\n\n\t/**\n\t * Auto-discover and register migrations from a directory\n\t *\n\t * @param directory - Absolute path to directory containing migration files\n\t * @param options - Discovery options\n\t */\n\tasync discoverFrom(\n\t\tdirectory: string,\n\t\toptions: {\n\t\t\t/** File pattern to match (default: \"*.migration.{ts,js}\") */\n\t\t\tpattern?: RegExp;\n\t\t\t/** Recursively search subdirectories */\n\t\t\trecursive?: boolean;\n\t\t} = {}\n\t): Promise<number> {\n\t\tconst { pattern = /\\.migration\\.(ts|js)$/, recursive = false } = options;\n\n\t\tconst discovered: Migration<TContext>[] = [];\n\n\t\ttry {\n\t\t\tconst files = await this.scanDirectory(directory, pattern, recursive);\n\n\t\t\tfor (const file of files) {\n\t\t\t\ttry {\n\t\t\t\t\tconst migrations = await this.loadMigrationFile(file);\n\t\t\t\t\tdiscovered.push(...migrations);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (this.options.debug) {\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t`[MigrationRegistry] Failed to load migration from ${file}:`,\n\t\t\t\t\t\t\terror\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.registerMany(discovered);\n\n\t\t\tif (this.options.debug) {\n\t\t\t\tconsole.debug(\n\t\t\t\t\t`[MigrationRegistry] Discovered ${discovered.length} migrations from ${directory}`\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn discovered.length;\n\t\t} catch (error) {\n\t\t\tif (this.options.debug) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`[MigrationRegistry] Failed to discover migrations from ${directory}:`,\n\t\t\t\t\terror\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Scan directory for migration files\n\t */\n\tprivate async scanDirectory(\n\t\tdirectory: string,\n\t\tpattern: RegExp,\n\t\trecursive: boolean\n\t): Promise<string[]> {\n\t\tconst files: string[] = [];\n\n\t\ttry {\n\t\t\tconst entries = await fs.readdir(directory, { withFileTypes: true });\n\n\t\t\tfor (const entry of entries) {\n\t\t\t\tconst fullPath = path.join(directory, entry.name);\n\n\t\t\t\tif (entry.isDirectory() && recursive) {\n\t\t\t\t\tconst subFiles = await this.scanDirectory(\n\t\t\t\t\t\tfullPath,\n\t\t\t\t\t\tpattern,\n\t\t\t\t\t\trecursive\n\t\t\t\t\t);\n\t\t\t\t\tfiles.push(...subFiles);\n\t\t\t\t} else if (entry.isFile() && pattern.test(entry.name)) {\n\t\t\t\t\tfiles.push(fullPath);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Directory doesn't exist or not accessible\n\t\t\tif (this.options.debug) {\n\t\t\t\tconsole.debug(\n\t\t\t\t\t`[MigrationRegistry] Cannot scan directory ${directory}:`,\n\t\t\t\t\terror\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn files;\n\t}\n\n\t/**\n\t * Load migration(s) from a file\n\t */\n\tprivate async loadMigrationFile(\n\t\tfilePath: string\n\t): Promise<Migration<TContext>[]> {\n\t\t// Dynamic import works for both .ts (with ts-node/tsx) and .js\n\t\tconst module = await import(filePath);\n\n\t\tconst migrations: Migration<TContext>[] = [];\n\n\t\t// Support different export patterns:\n\t\t// 1. export default migration\n\t\t// 2. export const migration = ...\n\t\t// 3. export const migrations = [...]\n\t\t// 4. Named exports that are migration objects\n\n\t\tif (module.default) {\n\t\t\tif (this.isMigration(module.default)) {\n\t\t\t\tmigrations.push(module.default);\n\t\t\t} else if (Array.isArray(module.default)) {\n\t\t\t\tmigrations.push(...module.default.filter(this.isMigration));\n\t\t\t}\n\t\t}\n\n\t\t// Check for \"migration\" or \"migrations\" exports\n\t\tif (this.isMigration(module.migration)) {\n\t\t\tmigrations.push(module.migration);\n\t\t}\n\n\t\tif (Array.isArray(module.migrations)) {\n\t\t\tmigrations.push(...module.migrations.filter(this.isMigration));\n\t\t}\n\n\t\t// Check all named exports\n\t\tfor (const [key, value] of Object.entries(module)) {\n\t\t\tif (key !== 'default' && key !== 'migration' && key !== 'migrations') {\n\t\t\t\tif (this.isMigration(value)) {\n\t\t\t\t\tmigrations.push(value as Migration<TContext>);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn migrations;\n\t}\n\n\t/**\n\t * Type guard to check if an object is a valid migration\n\t */\n\tprivate isMigration(obj: unknown): obj is Migration<TContext> {\n\t\tif (!obj || typeof obj !== 'object') return false;\n\n\t\tconst migration = obj as Partial<Migration<TContext>>;\n\n\t\treturn (\n\t\t\ttypeof migration.name === 'string' &&\n\t\t\ttypeof migration.description === 'string' &&\n\t\t\ttypeof migration.isCompleted === 'function' &&\n\t\t\ttypeof migration.isFailing === 'function' &&\n\t\t\ttypeof migration.hasFailed === 'function' &&\n\t\t\ttypeof migration.markComplete === 'function' &&\n\t\t\ttypeof migration.markFailed === 'function' &&\n\t\t\ttypeof migration.up === 'function'\n\t\t);\n\t}\n}\n","import { randomUUID } from 'node:crypto';\nimport {\n\tMigrationAlreadyCompletedError,\n\tMigrationAlreadyRunningError,\n\tMigrationExecutionError,\n\tMigrationNotRollbackableError,\n\tMigrationRollbackError,\n\tMigrationTimeoutError,\n\tMigrationValidationError,\n} from './errors';\nimport type { MigrationRegistry } from './registry';\nimport type {\n\tBaseMigrationContext,\n\tMigrationMetadata,\n\tMigrationResult,\n\tMigrationSystemStatus,\n\tRunOptions,\n} from './types';\n\n/**\n * Migration runner - executes migrations from a registry\n */\nexport class MigrationRunner<\n\tTContext extends BaseMigrationContext = BaseMigrationContext,\n> {\n\tprivate runningMigrations = new Set<string>();\n\n\tconstructor(\n\t\tprivate registry: MigrationRegistry<TContext>,\n\t\tprivate context: Omit<TContext, 'dryRun' | 'metadata'>\n\t) {}\n\n\t/**\n\t * Run all pending migrations\n\t */\n\tasync runPending(options: RunOptions = {}): Promise<MigrationResult[]> {\n\t\tconst fullContext = this.buildContext(options);\n\t\tconst { skipCompleted = true, continueOnError = false } = options;\n\n\t\tfullContext.logger.info('Starting pending migrations...');\n\n\t\tconst allMigrations = this.registry.getAll();\n\t\tconst results: MigrationResult[] = [];\n\n\t\tfor (const migration of allMigrations) {\n\t\t\ttry {\n\t\t\t\t// Check if already completed\n\t\t\t\tif (skipCompleted && (await migration.isCompleted(fullContext))) {\n\t\t\t\t\tfullContext.logger.info(\n\t\t\t\t\t\t`Skipping completed migration: ${migration.name}`\n\t\t\t\t\t);\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\tname: migration.name,\n\t\t\t\t\t\tstatus: 'skipped',\n\t\t\t\t\t\tduration: 0,\n\t\t\t\t\t\tstartedAt: new Date(),\n\t\t\t\t\t\tcompletedAt: new Date(),\n\t\t\t\t\t});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst result = await this.run(migration.name, options);\n\t\t\t\tresults.push(result);\n\t\t\t} catch (error) {\n\t\t\t\tfullContext.logger.error(`Migration ${migration.name} failed`, {\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t});\n\n\t\t\t\tresults.push({\n\t\t\t\t\tname: migration.name,\n\t\t\t\t\tstatus: 'failed',\n\t\t\t\t\tduration: 0,\n\t\t\t\t\tstartedAt: new Date(),\n\t\t\t\t\tcompletedAt: new Date(),\n\t\t\t\t\terror: error instanceof Error ? error : new Error(String(error)),\n\t\t\t\t});\n\n\t\t\t\tif (!continueOnError) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst completed = results.filter((r) => r.status === 'completed').length;\n\t\tconst failed = results.filter((r) => r.status === 'failed').length;\n\t\tconst skipped = results.filter((r) => r.status === 'skipped').length;\n\n\t\tfullContext.logger.info('All pending migrations processed', {\n\t\t\ttotal: results.length,\n\t\t\tcompleted,\n\t\t\tfailed,\n\t\t\tskipped,\n\t\t});\n\n\t\treturn results;\n\t}\n\n\t/**\n\t * Run a specific migration by name\n\t */\n\tasync run(\n\t\tmigrationName: string,\n\t\toptions: RunOptions = {}\n\t): Promise<MigrationResult> {\n\t\tconst migration = this.registry.get(migrationName);\n\t\tconst fullContext = this.buildContext(options);\n\t\tconst { skipCompleted = true, timeout } = options;\n\n\t\t// Check if already running\n\t\tif (this.runningMigrations.has(migrationName)) {\n\t\t\tthrow new MigrationAlreadyRunningError(migrationName);\n\t\t}\n\n\t\t// Check if already completed\n\t\tconst isCompleted = await migration.isCompleted(fullContext);\n\t\tif (skipCompleted && isCompleted) {\n\t\t\tif (!fullContext.dryRun) {\n\t\t\t\tthrow new MigrationAlreadyCompletedError(migrationName);\n\t\t\t}\n\t\t}\n\n\t\tthis.runningMigrations.add(migrationName);\n\n\t\ttry {\n\t\t\tconst startTime = Date.now();\n\t\t\tfullContext.logger.info(\n\t\t\t\tfullContext.dryRun\n\t\t\t\t\t? `[DRY RUN] Starting migration: ${migrationName}`\n\t\t\t\t\t: `Starting migration: ${migrationName}`,\n\t\t\t\t{ description: migration.description }\n\t\t\t);\n\n\t\t\t// Validate if validation function exists\n\t\t\tif (migration.validate) {\n\t\t\t\tconst validation = await migration.validate(fullContext);\n\t\t\t\tif (!validation.valid) {\n\t\t\t\t\tthrow new MigrationValidationError(\n\t\t\t\t\t\tmigrationName,\n\t\t\t\t\t\tvalidation.message || 'Validation failed'\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tif (validation.warnings && validation.warnings.length > 0) {\n\t\t\t\t\tfor (const warning of validation.warnings) {\n\t\t\t\t\t\tfullContext.logger.warn(warning);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Execute migration with optional timeout\n\t\t\tif (timeout) {\n\t\t\t\tawait this.runWithTimeout(\n\t\t\t\t\t() => migration.up(fullContext),\n\t\t\t\t\ttimeout,\n\t\t\t\t\tmigrationName\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tawait migration.up(fullContext);\n\t\t\t}\n\n\t\t\tconst duration = Date.now() - startTime;\n\t\t\tconst result: MigrationResult = {\n\t\t\t\tname: migrationName,\n\t\t\t\tstatus: 'completed',\n\t\t\t\tduration,\n\t\t\t\tstartedAt: new Date(startTime),\n\t\t\t\tcompletedAt: new Date(),\n\t\t\t};\n\n\t\t\t// Mark as complete (unless dry run)\n\t\t\tif (!fullContext.dryRun) {\n\t\t\t\tawait migration.markComplete(fullContext, result);\n\t\t\t}\n\n\t\t\tfullContext.logger.info(\n\t\t\t\tfullContext.dryRun\n\t\t\t\t\t? `[DRY RUN] Migration completed: ${migrationName}`\n\t\t\t\t\t: `Migration completed: ${migrationName}`,\n\t\t\t\t{ duration: `${duration}ms` }\n\t\t\t);\n\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tconst err = error instanceof Error ? error : new Error(String(error));\n\n\t\t\tfullContext.logger.error(`Migration failed: ${migrationName}`, {\n\t\t\t\terror: err.message,\n\t\t\t\tstack: err.stack,\n\t\t\t});\n\n\t\t\t// Mark as failed (unless dry run)\n\t\t\tif (!fullContext.dryRun) {\n\t\t\t\tawait migration.markFailed(fullContext, err);\n\t\t\t}\n\n\t\t\tthrow new MigrationExecutionError(migrationName, err);\n\t\t} finally {\n\t\t\tthis.runningMigrations.delete(migrationName);\n\t\t}\n\t}\n\n\t/**\n\t * Rollback a specific migration by name\n\t */\n\tasync rollback(\n\t\tmigrationName: string,\n\t\toptions: RunOptions = {}\n\t): Promise<MigrationResult> {\n\t\tconst migration = this.registry.get(migrationName);\n\t\tconst fullContext = this.buildContext(options);\n\t\tconst { timeout } = options;\n\n\t\t// Check if rollback is supported\n\t\tif (!migration.down) {\n\t\t\tthrow new MigrationNotRollbackableError(migrationName);\n\t\t}\n\n\t\t// Check if migration is running\n\t\tif (this.runningMigrations.has(migrationName)) {\n\t\t\tthrow new MigrationAlreadyRunningError(migrationName);\n\t\t}\n\n\t\tthis.runningMigrations.add(migrationName);\n\n\t\ttry {\n\t\t\tconst startTime = Date.now();\n\t\t\tfullContext.logger.info(\n\t\t\t\tfullContext.dryRun\n\t\t\t\t\t? `[DRY RUN] Rolling back migration: ${migrationName}`\n\t\t\t\t\t: `Rolling back migration: ${migrationName}`\n\t\t\t);\n\n\t\t\t// Execute rollback with optional timeout\n\t\t\tif (timeout) {\n\t\t\t\tawait this.runWithTimeout(\n\t\t\t\t\tasync () => {\n\t\t\t\t\t\tif (!migration.down) {\n\t\t\t\t\t\t\tthrow new Error('Rollback function not defined');\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait migration.down(fullContext);\n\t\t\t\t\t},\n\t\t\t\t\ttimeout,\n\t\t\t\t\tmigrationName\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tawait migration.down(fullContext);\n\t\t\t}\n\n\t\t\tconst duration = Date.now() - startTime;\n\t\t\tconst result: MigrationResult = {\n\t\t\t\tname: migrationName,\n\t\t\t\tstatus: 'rolled-back',\n\t\t\t\tduration,\n\t\t\t\tstartedAt: new Date(startTime),\n\t\t\t\tcompletedAt: new Date(),\n\t\t\t};\n\n\t\t\tfullContext.logger.info(\n\t\t\t\tfullContext.dryRun\n\t\t\t\t\t? `[DRY RUN] Migration rolled back: ${migrationName}`\n\t\t\t\t\t: `Migration rolled back: ${migrationName}`,\n\t\t\t\t{ duration: `${duration}ms` }\n\t\t\t);\n\n\t\t\tif (!fullContext.dryRun) {\n\t\t\t\tawait migration.markRollback(fullContext, result);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tconst err = error instanceof Error ? error : new Error(String(error));\n\n\t\t\tfullContext.logger.error(`Rollback failed: ${migrationName}`, {\n\t\t\t\terror: err.message,\n\t\t\t\tstack: err.stack,\n\t\t\t});\n\n\t\t\tthrow new MigrationRollbackError(migrationName, err);\n\t\t} finally {\n\t\t\tthis.runningMigrations.delete(migrationName);\n\t\t}\n\t}\n\n\t/**\n\t * Get the status of all migrations\n\t */\n\tasync getStatus(): Promise<MigrationSystemStatus> {\n\t\tconst fullContext = this.buildContext({});\n\t\tconst allMigrations = this.registry.getAll();\n\n\t\tconst statuses = await Promise.all(\n\t\t\tallMigrations.map(async (migration) => {\n\t\t\t\tconst [isCompleted, isFailing, hasFailed, isRunning] =\n\t\t\t\t\tawait Promise.all([\n\t\t\t\t\t\tmigration.isCompleted(fullContext),\n\t\t\t\t\t\tmigration.isFailing(fullContext),\n\t\t\t\t\t\tmigration.hasFailed(fullContext),\n\t\t\t\t\t\tPromise.resolve(this.runningMigrations.has(migration.name)),\n\t\t\t\t\t]);\n\n\t\t\t\treturn {\n\t\t\t\t\tname: migration.name,\n\t\t\t\t\tdescription: migration.description,\n\t\t\t\t\tisCompleted,\n\t\t\t\t\tisFailing,\n\t\t\t\t\thasFailed,\n\t\t\t\t\tisRunning,\n\t\t\t\t\tcanRollback: !!migration.down,\n\t\t\t\t};\n\t\t\t})\n\t\t);\n\n\t\treturn {\n\t\t\ttotal: statuses.length,\n\t\t\tpending: statuses.filter(\n\t\t\t\t(s) => !s.isCompleted && !s.hasFailed && !s.isRunning\n\t\t\t),\n\t\t\tcompleted: statuses.filter((s) => s.isCompleted && !s.isRunning),\n\t\t\tfailed: {\n\t\t\t\tcurrentlyFailing: statuses.filter((s) => s.isFailing && !s.isRunning),\n\t\t\t\tpreviouslyFailed: statuses.filter((s) => s.hasFailed && !s.isRunning),\n\t\t\t\trecovered: statuses.filter(\n\t\t\t\t\t(s) => s.isCompleted && !s.isFailing && s.hasFailed && !s.isRunning\n\t\t\t\t),\n\t\t\t},\n\t\t\trunning: statuses.filter((s) => s.isRunning),\n\t\t};\n\t}\n\n\t/**\n\t * Build full context from partial context\n\t */\n\tprivate buildContext(\n\t\tpartial: Partial<Pick<RunOptions, 'dryRun' | 'signal'>>\n\t) {\n\t\tconst metadata: MigrationMetadata = {\n\t\t\trunId: randomUUID(),\n\t\t\tstartedAt: new Date(),\n\t\t\ttriggeredBy: (\n\t\t\t\tpartial as {\n\t\t\t\t\ttriggeredBy?: string;\n\t\t\t\t}\n\t\t\t).triggeredBy,\n\t\t};\n\n\t\treturn {\n\t\t\t...this.context,\n\t\t\t...partial,\n\t\t\tmetadata,\n\t\t\tdryRun: partial.dryRun ?? false,\n\t\t} as TContext & {\n\t\t\tmetadata: MigrationMetadata;\n\t\t};\n\t}\n\n\t/**\n\t * Run a function with a timeout\n\t */\n\tprivate async runWithTimeout(\n\t\tfn: () => Promise<void>,\n\t\ttimeout: number,\n\t\tmigrationName: string\n\t): Promise<void> {\n\t\tconst timeoutPromise = new Promise<never>((_, reject) => {\n\t\t\tsetTimeout(() => {\n\t\t\t\treject(new MigrationTimeoutError(migrationName, timeout));\n\t\t\t}, timeout);\n\t\t});\n\n\t\tawait Promise.race([fn(), timeoutPromise]);\n\t}\n}\n"],"names":["MigrationError","__name","message","migrationName","code","MigrationAlreadyRunningError","MigrationAlreadyCompletedError","MigrationValidationError","validationMessage","MigrationExecutionError","cause","MigrationRollbackError","MigrationNotRollbackableError","MigrationNotFoundError","MigrationTimeoutError","timeout","DuplicateMigrationError","colors","formatMeta","meta","line","i","getTimestamp","ConsoleLogger","useColors","level","color","timestamp","levelStr","formattedMessage","metaStr","createDefaultLogger","isColorSupported","MigrationProgress","logger","total","logInterval","amount","value","elapsed","rate","currentPercent","percent","eta","progressBar","width","filled","empty","num","ms","MigrationRegistry","options","migration","migrations","name","directory","pattern","recursive","discovered","files","file","error","entries","fs","entry","fullPath","path","subFiles","filePath","module","key","obj","MigrationRunner","registry","context","fullContext","skipCompleted","continueOnError","allMigrations","results","result","completed","r","failed","skipped","isCompleted","startTime","validation","warning","duration","err","statuses","isFailing","hasFailed","isRunning","s","partial","metadata","randomUUID","fn","timeoutPromise","_","reject"],"mappings":"0eAGO,MAAMA,UAAuB,KAAM,OAAA,CAAAC,EAAA,uBACzC,YACCC,EACgBC,EACAC,EACf,CACD,MAAMF,CAAO,EAHG,KAAA,cAAAC,EACA,KAAA,KAAAC,EAGhB,KAAK,KAAO,iBACZ,MAAM,kBAAkB,KAAM,KAAK,WAAW,CAC/C,CACD,CAKO,MAAMC,UAAqCL,CAAe,OAAA,CAAAC,EAAA,qCAChE,YAAYE,EAAuB,CAClC,MACC,cAAcA,CAAa,uBAC3BA,EACA,2BAAA,EAED,KAAK,KAAO,8BACb,CACD,CAKO,MAAMG,UAAuCN,CAAe,OAAA,CAAAC,EAAA,uCAClE,YAAYE,EAAuB,CAClC,MACC,cAAcA,CAAa,+BAC3BA,EACA,6BAAA,EAED,KAAK,KAAO,gCACb,CACD,CAKO,MAAMI,UAAiCP,CAAe,OAAA,CAAAC,EAAA,iCAC5D,YACCE,EACgBK,EACf,CACD,MACC,cAAcL,CAAa,wBAAwBK,CAAiB,GACpEL,EACA,6BAAA,EALe,KAAA,kBAAAK,EAOhB,KAAK,KAAO,0BACb,CACD,CAKO,MAAMC,UAAgCT,CAAe,OAAA,CAAAC,EAAA,gCAC3D,YACCE,EACyBO,EACxB,CACD,MACC,cAAcP,CAAa,uBAAuBO,EAAM,OAAO,GAC/DP,EACA,4BAAA,EALwB,KAAA,MAAAO,EAOzB,KAAK,KAAO,0BACZ,KAAK,MAAQA,EAAM,KACpB,CACD,CAKO,MAAMC,UAA+BX,CAAe,OAAA,CAAAC,EAAA,+BAC1D,YACCE,EACyBO,EACxB,CACD,MACC,cAAcP,CAAa,sBAAsBO,EAAM,OAAO,GAC9DP,EACA,2BAAA,EALwB,KAAA,MAAAO,EAOzB,KAAK,KAAO,yBACZ,KAAK,MAAQA,EAAM,KACpB,CACD,CAKO,MAAME,UAAsCZ,CAAe,OAAA,CAAAC,EAAA,sCACjE,YAAYE,EAAuB,CAClC,MACC,cAAcA,CAAa,8BAC3BA,EACA,4BAAA,EAED,KAAK,KAAO,+BACb,CACD,CAKO,MAAMU,UAA+Bb,CAAe,OAAA,CAAAC,EAAA,+BAC1D,YAAYE,EAAuB,CAClC,MACC,cAAcA,CAAa,0BAC3BA,EACA,qBAAA,EAED,KAAK,KAAO,wBACb,CACD,CAKO,MAAMW,UAA8Bd,CAAe,OAAA,CAAAC,EAAA,8BACzD,YACCE,EACgBY,EACf,CACD,MACC,cAAcZ,CAAa,qBAAqBY,CAAO,KACvDZ,EACA,mBAAA,EALe,KAAA,QAAAY,EAOhB,KAAK,KAAO,uBACb,CACD,CAKO,MAAMC,UAAgChB,CAAe,OAAA,CAAAC,EAAA,gCAC3D,YAAYE,EAAuB,CAClC,MACC,cAAcA,CAAa,0BAC3BA,EACA,qBAAA,EAED,KAAK,KAAO,yBACb,CACD,CCpJA,MAAMc,EAAS,CACd,MAAO,UACP,OAAQ,UAER,IAAK,WAEL,OAAQ,WACR,KAAM,WACN,KAAM,WACN,KAAM,UACP,EAKA,SAASC,EAAWC,EAAwC,CAC3D,GAAI,CAACA,GAAQ,OAAO,KAAKA,CAAI,EAAE,SAAW,EACzC,MAAO,GAGR,GAAI,CACH,MACC,IACA,KAAK,UAAUA,EAAM,KAAM,CAAC,EAC1B,MAAM;AAAA,CAAI,EACV,IAAI,CAACC,EAAMC,IAAOA,IAAM,EAAID,EAAO,KAAKA,CAAI,EAAG,EAC/C,KAAK;AAAA,CAAI,CAEb,MAAQ,CACP,MAAO,WACR,CACD,CAhBSnB,EAAAiB,EAAA,cAqBT,SAASI,GAAuB,CAC/B,OAAO,IAAI,KAAA,EAAO,YAAA,CACnB,CAFSrB,EAAAqB,EAAA,gBAOF,MAAMC,CAAyC,OAAA,CAAAtB,EAAA,sBACrD,YAAoBuB,EAAY,GAAM,CAAlB,KAAA,UAAAA,CAAmB,CAE/B,IACPC,EACAC,EACAxB,EACAiB,EACO,CACP,MAAMQ,EAAY,KAAK,UACpB,GAAGV,EAAO,IAAI,GAAGK,EAAA,CAAc,GAAGL,EAAO,KAAK,GAC9CK,EAAA,EAEGM,EAAW,KAAK,UACnB,GAAGF,CAAK,GAAGD,EAAM,OAAO,CAAC,CAAC,GAAGR,EAAO,KAAK,GACzCQ,EAAM,OAAO,CAAC,EAEXI,EAAmB,KAAK,UAC3B,GAAGZ,EAAO,MAAM,GAAGf,CAAO,GAAGe,EAAO,KAAK,GACzCf,EAEG4B,EAAUZ,EAAWC,CAAI,EAE/B,QAAQ,IAAI,GAAGQ,CAAS,IAAIC,CAAQ,IAAIC,CAAgB,GAAGC,CAAO,EAAE,CACrE,CAEA,KAAK5B,EAAiBiB,EAAsC,CAC3D,KAAK,IAAI,OAAQF,EAAO,KAAMf,EAASiB,CAAI,CAC5C,CAEA,KAAKjB,EAAiBiB,EAAsC,CAC3D,KAAK,IAAI,OAAQF,EAAO,OAAQf,EAASiB,CAAI,CAC9C,CAEA,MAAMjB,EAAiBiB,EAAsC,CAC5D,KAAK,IAAI,QAASF,EAAO,IAAKf,EAASiB,CAAI,CAC5C,CAEA,MAAMjB,EAAiBiB,EAAsC,CAC5D,KAAK,IAAI,QAASF,EAAO,KAAMf,EAASiB,CAAI,CAC7C,CACD,CAKO,SAASY,GAAuC,CACtD,MAAMC,EACL,QAAQ,OAAO,OACf,QAAQ,IAAI,cAAgB,KAC5B,QAAQ,IAAI,sBAAwB,OAErC,OAAO,IAAIT,EAAcS,CAAgB,CAC1C,CAPgB/B,EAAA8B,EAAA,uBCzFT,MAAME,CAAkB,OAAA,CAAAhC,EAAA,0BAK9B,YACSiC,EACAC,EACAC,EAAc,GACrB,CAHO,KAAA,OAAAF,EACA,KAAA,MAAAC,EACA,KAAA,YAAAC,EAER,KAAK,OAAO,KACX,0BAA0B,KAAK,aAAaD,CAAK,CAAC,QAAA,CAEpD,CAZQ,UAAY,EACZ,kBAAoB,EACpB,UAAY,KAAK,IAAA,EAezB,WAAkB,CACjB,KAAK,YACL,KAAK,YAAA,CACN,CAKA,IAAIE,EAAsB,CACzB,KAAK,WAAaA,EAClB,KAAK,YAAA,CACN,CAKA,IAAIC,EAAqB,CACxB,KAAK,UAAYA,EACjB,KAAK,YAAA,CACN,CAKA,YAAqB,CACpB,OAAO,KAAK,QAAU,EACnB,IACA,KAAK,MAAO,KAAK,UAAY,KAAK,MAAS,GAAG,CAClD,CAKA,YAAqB,CACpB,OAAO,KAAK,MAAQ,KAAK,SAC1B,CAKA,QAAiB,CAChB,GAAI,KAAK,YAAc,EAAG,MAAO,GAEjC,MAAMC,EAAU,KAAK,WAAA,EACfC,EAAO,KAAK,UAAYD,EAG9B,OAFkB,KAAK,MAAQ,KAAK,WAEjBC,CACpB,CAKA,UAAiB,CAChB,KAAK,UAAY,KAAK,MACtB,MAAMD,EAAU,KAAK,WAAA,EACfC,EAAO,KAAK,OAASD,EAAU,KAErC,KAAK,OAAO,KACX,wBAAwB,KAAK,aAAa,KAAK,KAAK,CAAC,aAAa,KAAK,eAAeA,CAAO,CAAC,GAC9F,CACC,MAAO,KAAK,MACZ,SAAUA,EACV,KAAM,GAAGC,EAAK,QAAQ,CAAC,CAAC,YAAA,CACzB,CAEF,CAKQ,aAAoB,CAC3B,MAAMC,EAAiB,KAAK,WAAA,GAI3BA,GAAkB,KAAK,kBAAoB,KAAK,aAChD,KAAK,YAAc,KAAK,SAExB,KAAK,YAAA,EACL,KAAK,kBAAoBA,EAE3B,CAKQ,aAAoB,CAC3B,MAAMC,EAAU,KAAK,WAAA,EACfH,EAAU,KAAK,WAAA,EACfI,EAAM,KAAK,OAAA,EAEXC,EAAc,KAAK,kBAAkBF,CAAO,EAElD,KAAK,OAAO,KACX,aAAaE,CAAW,IAAIF,CAAO,MAAM,KAAK,aAAa,KAAK,SAAS,CAAC,IAAI,KAAK,aAAa,KAAK,KAAK,CAAC,IAC3G,CACC,QAAS,KAAK,eAAeH,CAAO,EACpC,IAAK,KAAK,eAAeI,CAAG,CAAA,CAC7B,CAEF,CAKQ,kBAAkBD,EAAiBG,EAAQ,GAAY,CAC9D,MAAMC,EAAS,KAAK,MAAOJ,EAAU,IAAOG,CAAK,EAC3CE,EAAQF,EAAQC,EAEtB,MAAO,IAAI,SAAI,OAAOA,CAAM,CAAC,GAAG,IAAI,OAAOC,CAAK,CAAC,GAClD,CAKQ,aAAaC,EAAqB,CACzC,OAAOA,EAAI,eAAA,CACZ,CAKQ,eAAeC,EAAoB,CAC1C,OAAIA,EAAK,IAAa,GAAG,KAAK,MAAMA,CAAE,CAAC,KACnCA,EAAK,IAAc,IAAIA,EAAK,KAAM,QAAQ,CAAC,CAAC,IAC5CA,EAAK,KAAgB,IAAIA,EAAK,KAAO,QAAQ,CAAC,CAAC,IAC5C,IAAIA,EAAK,MAAS,QAAQ,CAAC,CAAC,GACpC,CACD,CChJO,MAAMC,CAEX,OAAA,CAAAjD,EAAA,0BACO,eAAiB,IACjB,QAER,YAAYkD,EAA2B,GAAI,CAC1C,KAAK,QAAUA,CAChB,CAKA,SAASC,EAAsC,CAC9C,GAAI,KAAK,WAAW,IAAIA,EAAU,IAAI,EACrC,MAAM,IAAIpC,EAAwBoC,EAAU,IAAI,EAGjD,KAAK,WAAW,IAAIA,EAAU,KAAMA,CAAS,EAEzC,KAAK,QAAQ,OAChB,QAAQ,MACP,6CAA6CA,EAAU,IAAI,EAAA,CAG9D,CAKA,aAAaC,EAAyC,CACrD,UAAWD,KAAaC,EACvB,KAAK,SAASD,CAAS,CAEzB,CAKA,IAAIE,EAAmC,CACtC,MAAMF,EAAY,KAAK,WAAW,IAAIE,CAAI,EAE1C,GAAI,CAACF,EACJ,MAAM,IAAIvC,EAAuByC,CAAI,EAGtC,OAAOF,CACR,CAKA,IAAIE,EAAuB,CAC1B,OAAO,KAAK,WAAW,IAAIA,CAAI,CAChC,CAKA,QAAgC,CAC/B,OAAO,MAAM,KAAK,KAAK,WAAW,QAAQ,CAC3C,CAKA,UAAqB,CACpB,OAAO,MAAM,KAAK,KAAK,WAAW,MAAM,CACzC,CAKA,OAAgB,CACf,OAAO,KAAK,WAAW,IACxB,CAKA,OAAc,CACb,KAAK,WAAW,MAAA,CACjB,CAQA,MAAM,aACLC,EACAJ,EAKI,GACc,CAClB,KAAM,CAAE,QAAAK,EAAU,wBAAyB,UAAAC,EAAY,IAAUN,EAE3DO,EAAoC,CAAA,EAE1C,GAAI,CACH,MAAMC,EAAQ,MAAM,KAAK,cAAcJ,EAAWC,EAASC,CAAS,EAEpE,UAAWG,KAAQD,EAClB,GAAI,CACH,MAAMN,EAAa,MAAM,KAAK,kBAAkBO,CAAI,EACpDF,EAAW,KAAK,GAAGL,CAAU,CAC9B,OAASQ,EAAO,CACX,KAAK,QAAQ,OAChB,QAAQ,MACP,qDAAqDD,CAAI,IACzDC,CAAA,CAGH,CAGD,YAAK,aAAaH,CAAU,EAExB,KAAK,QAAQ,OAChB,QAAQ,MACP,kCAAkCA,EAAW,MAAM,oBAAoBH,CAAS,EAAA,EAI3EG,EAAW,MACnB,OAASG,EAAO,CACf,MAAI,KAAK,QAAQ,OAChB,QAAQ,MACP,0DAA0DN,CAAS,IACnEM,CAAA,EAGIA,CACP,CACD,CAKA,MAAc,cACbN,EACAC,EACAC,EACoB,CACpB,MAAME,EAAkB,CAAA,EAExB,GAAI,CACH,MAAMG,EAAU,MAAMC,EAAG,QAAQR,EAAW,CAAE,cAAe,GAAM,EAEnE,UAAWS,KAASF,EAAS,CAC5B,MAAMG,EAAWC,EAAK,KAAKX,EAAWS,EAAM,IAAI,EAEhD,GAAIA,EAAM,YAAA,GAAiBP,EAAW,CACrC,MAAMU,EAAW,MAAM,KAAK,cAC3BF,EACAT,EACAC,CAAA,EAEDE,EAAM,KAAK,GAAGQ,CAAQ,CACvB,MAAWH,EAAM,OAAA,GAAYR,EAAQ,KAAKQ,EAAM,IAAI,GACnDL,EAAM,KAAKM,CAAQ,CAErB,CACD,OAASJ,EAAO,CAEX,KAAK,QAAQ,OAChB,QAAQ,MACP,6CAA6CN,CAAS,IACtDM,CAAA,CAGH,CAEA,OAAOF,CACR,CAKA,MAAc,kBACbS,EACiC,CAEjC,MAAMC,EAAS,MAAM,OAAOD,GAEtBf,EAAoC,CAAA,EAQtCgB,EAAO,UACN,KAAK,YAAYA,EAAO,OAAO,EAClChB,EAAW,KAAKgB,EAAO,OAAO,EACpB,MAAM,QAAQA,EAAO,OAAO,GACtChB,EAAW,KAAK,GAAGgB,EAAO,QAAQ,OAAO,KAAK,WAAW,CAAC,GAKxD,KAAK,YAAYA,EAAO,SAAS,GACpChB,EAAW,KAAKgB,EAAO,SAAS,EAG7B,MAAM,QAAQA,EAAO,UAAU,GAClChB,EAAW,KAAK,GAAGgB,EAAO,WAAW,OAAO,KAAK,WAAW,CAAC,EAI9D,SAAW,CAACC,EAAKhC,CAAK,IAAK,OAAO,QAAQ+B,CAAM,EAC3CC,IAAQ,WAAaA,IAAQ,aAAeA,IAAQ,cACnD,KAAK,YAAYhC,CAAK,GACzBe,EAAW,KAAKf,CAA4B,EAK/C,OAAOe,CACR,CAKQ,YAAYkB,EAA0C,CAC7D,GAAI,CAACA,GAAO,OAAOA,GAAQ,SAAU,MAAO,GAE5C,MAAMnB,EAAYmB,EAElB,OACC,OAAOnB,EAAU,MAAS,UAC1B,OAAOA,EAAU,aAAgB,UACjC,OAAOA,EAAU,aAAgB,YACjC,OAAOA,EAAU,WAAc,YAC/B,OAAOA,EAAU,WAAc,YAC/B,OAAOA,EAAU,cAAiB,YAClC,OAAOA,EAAU,YAAe,YAChC,OAAOA,EAAU,IAAO,UAE1B,CACD,CCvOO,MAAMoB,CAEX,OAAA,CAAAvE,EAAA,wBAGD,YACSwE,EACAC,EACP,CAFO,KAAA,SAAAD,EACA,KAAA,QAAAC,CACN,CALK,sBAAwB,IAUhC,MAAM,WAAWvB,EAAsB,GAAgC,CACtE,MAAMwB,EAAc,KAAK,aAAaxB,CAAO,EACvC,CAAE,cAAAyB,EAAgB,GAAM,gBAAAC,EAAkB,IAAU1B,EAE1DwB,EAAY,OAAO,KAAK,gCAAgC,EAExD,MAAMG,EAAgB,KAAK,SAAS,OAAA,EAC9BC,EAA6B,CAAA,EAEnC,UAAW3B,KAAa0B,EACvB,GAAI,CAEH,GAAIF,GAAkB,MAAMxB,EAAU,YAAYuB,CAAW,EAAI,CAChEA,EAAY,OAAO,KAClB,iCAAiCvB,EAAU,IAAI,EAAA,EAEhD2B,EAAQ,KAAK,CACZ,KAAM3B,EAAU,KAChB,OAAQ,UACR,SAAU,EACV,cAAe,KACf,gBAAiB,IAAK,CACtB,EACD,QACD,CAEA,MAAM4B,EAAS,MAAM,KAAK,IAAI5B,EAAU,KAAMD,CAAO,EACrD4B,EAAQ,KAAKC,CAAM,CACpB,OAASnB,EAAO,CAcf,GAbAc,EAAY,OAAO,MAAM,aAAavB,EAAU,IAAI,UAAW,CAC9D,MAAOS,aAAiB,MAAQA,EAAM,QAAU,OAAOA,CAAK,CAAA,CAC5D,EAEDkB,EAAQ,KAAK,CACZ,KAAM3B,EAAU,KAChB,OAAQ,SACR,SAAU,EACV,cAAe,KACf,gBAAiB,KACjB,MAAOS,aAAiB,MAAQA,EAAQ,IAAI,MAAM,OAAOA,CAAK,CAAC,CAAA,CAC/D,EAEG,CAACgB,EACJ,MAAMhB,CAER,CAGD,MAAMoB,EAAYF,EAAQ,OAAQG,GAAMA,EAAE,SAAW,WAAW,EAAE,OAC5DC,EAASJ,EAAQ,OAAQG,GAAMA,EAAE,SAAW,QAAQ,EAAE,OACtDE,EAAUL,EAAQ,OAAQG,GAAMA,EAAE,SAAW,SAAS,EAAE,OAE9D,OAAAP,EAAY,OAAO,KAAK,mCAAoC,CAC3D,MAAOI,EAAQ,OACf,UAAAE,EACA,OAAAE,EACA,QAAAC,CAAA,CACA,EAEML,CACR,CAKA,MAAM,IACL5E,EACAgD,EAAsB,GACK,CAC3B,MAAMC,EAAY,KAAK,SAAS,IAAIjD,CAAa,EAC3CwE,EAAc,KAAK,aAAaxB,CAAO,EACvC,CAAE,cAAAyB,EAAgB,GAAM,QAAA7D,CAAA,EAAYoC,EAG1C,GAAI,KAAK,kBAAkB,IAAIhD,CAAa,EAC3C,MAAM,IAAIE,EAA6BF,CAAa,EAIrD,MAAMkF,EAAc,MAAMjC,EAAU,YAAYuB,CAAW,EAC3D,GAAIC,GAAiBS,GAChB,CAACV,EAAY,OAChB,MAAM,IAAIrE,EAA+BH,CAAa,EAIxD,KAAK,kBAAkB,IAAIA,CAAa,EAExC,GAAI,CACH,MAAMmF,EAAY,KAAK,IAAA,EASvB,GARAX,EAAY,OAAO,KAClBA,EAAY,OACT,iCAAiCxE,CAAa,GAC9C,uBAAuBA,CAAa,GACvC,CAAE,YAAaiD,EAAU,WAAA,CAAY,EAIlCA,EAAU,SAAU,CACvB,MAAMmC,EAAa,MAAMnC,EAAU,SAASuB,CAAW,EACvD,GAAI,CAACY,EAAW,MACf,MAAM,IAAIhF,EACTJ,EACAoF,EAAW,SAAW,mBAAA,EAIxB,GAAIA,EAAW,UAAYA,EAAW,SAAS,OAAS,EACvD,UAAWC,KAAWD,EAAW,SAChCZ,EAAY,OAAO,KAAKa,CAAO,CAGlC,CAGIzE,EACH,MAAM,KAAK,eACV,IAAMqC,EAAU,GAAGuB,CAAW,EAC9B5D,EACAZ,CAAA,EAGD,MAAMiD,EAAU,GAAGuB,CAAW,EAG/B,MAAMc,EAAW,KAAK,IAAA,EAAQH,EACxBN,EAA0B,CAC/B,KAAM7E,EACN,OAAQ,YACR,SAAAsF,EACA,UAAW,IAAI,KAAKH,CAAS,EAC7B,gBAAiB,IAAK,EAIvB,OAAKX,EAAY,QAChB,MAAMvB,EAAU,aAAauB,EAAaK,CAAM,EAGjDL,EAAY,OAAO,KAClBA,EAAY,OACT,kCAAkCxE,CAAa,GAC/C,wBAAwBA,CAAa,GACxC,CAAE,SAAU,GAAGsF,CAAQ,IAAA,CAAK,EAGtBT,CACR,OAASnB,EAAO,CACf,MAAM6B,EAAM7B,aAAiB,MAAQA,EAAQ,IAAI,MAAM,OAAOA,CAAK,CAAC,EAEpE,MAAAc,EAAY,OAAO,MAAM,qBAAqBxE,CAAa,GAAI,CAC9D,MAAOuF,EAAI,QACX,MAAOA,EAAI,KAAA,CACX,EAGIf,EAAY,QAChB,MAAMvB,EAAU,WAAWuB,EAAae,CAAG,EAGtC,IAAIjF,EAAwBN,EAAeuF,CAAG,CACrD,QAAA,CACC,KAAK,kBAAkB,OAAOvF,CAAa,CAC5C,CACD,CAKA,MAAM,SACLA,EACAgD,EAAsB,GACK,CAC3B,MAAMC,EAAY,KAAK,SAAS,IAAIjD,CAAa,EAC3CwE,EAAc,KAAK,aAAaxB,CAAO,EACvC,CAAE,QAAApC,GAAYoC,EAGpB,GAAI,CAACC,EAAU,KACd,MAAM,IAAIxC,EAA8BT,CAAa,EAItD,GAAI,KAAK,kBAAkB,IAAIA,CAAa,EAC3C,MAAM,IAAIE,EAA6BF,CAAa,EAGrD,KAAK,kBAAkB,IAAIA,CAAa,EAExC,GAAI,CACH,MAAMmF,EAAY,KAAK,IAAA,EACvBX,EAAY,OAAO,KAClBA,EAAY,OACT,qCAAqCxE,CAAa,GAClD,2BAA2BA,CAAa,EAAA,EAIxCY,EACH,MAAM,KAAK,eACV,SAAY,CACX,GAAI,CAACqC,EAAU,KACd,MAAM,IAAI,MAAM,+BAA+B,EAEhD,MAAMA,EAAU,KAAKuB,CAAW,CACjC,EACA5D,EACAZ,CAAA,EAGD,MAAMiD,EAAU,KAAKuB,CAAW,EAGjC,MAAMc,EAAW,KAAK,IAAA,EAAQH,EACxBN,EAA0B,CAC/B,KAAM7E,EACN,OAAQ,cACR,SAAAsF,EACA,UAAW,IAAI,KAAKH,CAAS,EAC7B,gBAAiB,IAAK,EAGvB,OAAAX,EAAY,OAAO,KAClBA,EAAY,OACT,oCAAoCxE,CAAa,GACjD,0BAA0BA,CAAa,GAC1C,CAAE,SAAU,GAAGsF,CAAQ,IAAA,CAAK,EAGxBd,EAAY,QAChB,MAAMvB,EAAU,aAAauB,EAAaK,CAAM,EAG1CA,CACR,OAASnB,EAAO,CACf,MAAM6B,EAAM7B,aAAiB,MAAQA,EAAQ,IAAI,MAAM,OAAOA,CAAK,CAAC,EAEpE,MAAAc,EAAY,OAAO,MAAM,oBAAoBxE,CAAa,GAAI,CAC7D,MAAOuF,EAAI,QACX,MAAOA,EAAI,KAAA,CACX,EAEK,IAAI/E,EAAuBR,EAAeuF,CAAG,CACpD,QAAA,CACC,KAAK,kBAAkB,OAAOvF,CAAa,CAC5C,CACD,CAKA,MAAM,WAA4C,CACjD,MAAMwE,EAAc,KAAK,aAAa,EAAE,EAClCG,EAAgB,KAAK,SAAS,OAAA,EAE9Ba,EAAW,MAAM,QAAQ,IAC9Bb,EAAc,IAAI,MAAO1B,GAAc,CACtC,KAAM,CAACiC,EAAaO,EAAWC,EAAWC,CAAS,EAClD,MAAM,QAAQ,IAAI,CACjB1C,EAAU,YAAYuB,CAAW,EACjCvB,EAAU,UAAUuB,CAAW,EAC/BvB,EAAU,UAAUuB,CAAW,EAC/B,QAAQ,QAAQ,KAAK,kBAAkB,IAAIvB,EAAU,IAAI,CAAC,CAAA,CAC1D,EAEF,MAAO,CACN,KAAMA,EAAU,KAChB,YAAaA,EAAU,YACvB,YAAAiC,EACA,UAAAO,EACA,UAAAC,EACA,UAAAC,EACA,YAAa,CAAC,CAAC1C,EAAU,IAAA,CAE3B,CAAC,CAAA,EAGF,MAAO,CACN,MAAOuC,EAAS,OAChB,QAASA,EAAS,OAChBI,GAAM,CAACA,EAAE,aAAe,CAACA,EAAE,WAAa,CAACA,EAAE,SAAA,EAE7C,UAAWJ,EAAS,OAAQI,GAAMA,EAAE,aAAe,CAACA,EAAE,SAAS,EAC/D,OAAQ,CACP,iBAAkBJ,EAAS,OAAQI,GAAMA,EAAE,WAAa,CAACA,EAAE,SAAS,EACpE,iBAAkBJ,EAAS,OAAQI,GAAMA,EAAE,WAAa,CAACA,EAAE,SAAS,EACpE,UAAWJ,EAAS,OAClBI,GAAMA,EAAE,aAAe,CAACA,EAAE,WAAaA,EAAE,WAAa,CAACA,EAAE,SAAA,CAC3D,EAED,QAASJ,EAAS,OAAQI,GAAMA,EAAE,SAAS,CAAA,CAE7C,CAKQ,aACPC,EACC,CACD,MAAMC,EAA8B,CACnC,MAAOC,EAAAA,WAAA,EACP,cAAe,KACf,YACCF,EAGC,WAAA,EAGH,MAAO,CACN,GAAG,KAAK,QACR,GAAGA,EACH,SAAAC,EACA,OAAQD,EAAQ,QAAU,EAAA,CAI5B,CAKA,MAAc,eACbG,EACApF,EACAZ,EACgB,CAChB,MAAMiG,EAAiB,IAAI,QAAe,CAACC,EAAGC,IAAW,CACxD,WAAW,IAAM,CAChBA,EAAO,IAAIxF,EAAsBX,EAAeY,CAAO,CAAC,CACzD,EAAGA,CAAO,CACX,CAAC,EAED,MAAM,QAAQ,KAAK,CAACoF,EAAA,EAAMC,CAAc,CAAC,CAC1C,CACD"}
@@ -0,0 +1,369 @@
1
+ /**
2
+ * Base error class for all migration errors
3
+ */
4
+ declare class MigrationError extends Error {
5
+ readonly migrationName?: string | undefined;
6
+ readonly code?: string | undefined;
7
+ constructor(message: string, migrationName?: string | undefined, code?: string | undefined);
8
+ }
9
+ /**
10
+ * Thrown when a migration is already running
11
+ */
12
+ declare class MigrationAlreadyRunningError extends MigrationError {
13
+ constructor(migrationName: string);
14
+ }
15
+ /**
16
+ * Thrown when a migration has already been completed
17
+ */
18
+ declare class MigrationAlreadyCompletedError extends MigrationError {
19
+ constructor(migrationName: string);
20
+ }
21
+ /**
22
+ * Thrown when a migration validation fails
23
+ */
24
+ declare class MigrationValidationError extends MigrationError {
25
+ readonly validationMessage: string;
26
+ constructor(migrationName: string, validationMessage: string);
27
+ }
28
+ /**
29
+ * Thrown when a migration execution fails
30
+ */
31
+ declare class MigrationExecutionError extends MigrationError {
32
+ readonly cause: Error;
33
+ constructor(migrationName: string, cause: Error);
34
+ }
35
+ /**
36
+ * Thrown when a migration rollback fails
37
+ */
38
+ declare class MigrationRollbackError extends MigrationError {
39
+ readonly cause: Error;
40
+ constructor(migrationName: string, cause: Error);
41
+ }
42
+ /**
43
+ * Thrown when attempting to rollback a migration that doesn't support it
44
+ */
45
+ declare class MigrationNotRollbackableError extends MigrationError {
46
+ constructor(migrationName: string);
47
+ }
48
+ /**
49
+ * Thrown when a migration is not found in the registry
50
+ */
51
+ declare class MigrationNotFoundError extends MigrationError {
52
+ constructor(migrationName: string);
53
+ }
54
+ /**
55
+ * Thrown when a migration times out
56
+ */
57
+ declare class MigrationTimeoutError extends MigrationError {
58
+ readonly timeout: number;
59
+ constructor(migrationName: string, timeout: number);
60
+ }
61
+ /**
62
+ * Thrown when attempting to register a duplicate migration
63
+ */
64
+ declare class DuplicateMigrationError extends MigrationError {
65
+ constructor(migrationName: string);
66
+ }
67
+
68
+ /**
69
+ * Logger interface for migration operations
70
+ */
71
+ interface MigrationLogger {
72
+ info: (message: string, meta?: Record<string, unknown>) => void;
73
+ warn: (message: string, meta?: Record<string, unknown>) => void;
74
+ error: (message: string, meta?: Record<string, unknown>) => void;
75
+ debug: (message: string, meta?: Record<string, unknown>) => void;
76
+ }
77
+ /**
78
+ * Metadata about a migration execution
79
+ */
80
+ interface MigrationMetadata {
81
+ runId: string;
82
+ startedAt: Date;
83
+ triggeredBy?: string;
84
+ }
85
+ /**
86
+ * Base context available to all migrations
87
+ */
88
+ interface BaseMigrationContext {
89
+ logger: MigrationLogger;
90
+ metadata: MigrationMetadata;
91
+ dryRun: boolean;
92
+ }
93
+ /**
94
+ * Result of a migration validation
95
+ */
96
+ interface ValidationResult {
97
+ valid: boolean;
98
+ message?: string;
99
+ warnings?: string[];
100
+ }
101
+ /**
102
+ * Result of a migration execution
103
+ */
104
+ interface MigrationResult {
105
+ name: string;
106
+ status: 'completed' | 'failed' | 'skipped' | 'rolled-back';
107
+ duration: number;
108
+ startedAt: Date;
109
+ completedAt: Date;
110
+ error?: Error;
111
+ recordsProcessed?: number;
112
+ warnings?: string[];
113
+ metadata?: Record<string, unknown>;
114
+ }
115
+ /**
116
+ * Status of a single migration
117
+ */
118
+ interface MigrationStatus {
119
+ name: string;
120
+ description: string;
121
+ isCompleted: boolean;
122
+ canRollback: boolean;
123
+ lastRun?: Date;
124
+ error?: string;
125
+ }
126
+ /**
127
+ * Overall migration system status
128
+ */
129
+ interface MigrationSystemStatus {
130
+ total: number;
131
+ pending: MigrationStatus[];
132
+ completed: MigrationStatus[];
133
+ failed: {
134
+ currentlyFailing: MigrationStatus[];
135
+ previouslyFailed: MigrationStatus[];
136
+ recovered: MigrationStatus[];
137
+ };
138
+ running: MigrationStatus[];
139
+ }
140
+ /**
141
+ * Core migration definition
142
+ * @template TContext - Extended context type including consumer-specific properties
143
+ */
144
+ interface Migration<TContext extends BaseMigrationContext = BaseMigrationContext> {
145
+ /** Unique identifier for this migration */
146
+ name: string;
147
+ /** Human-readable description */
148
+ description: string;
149
+ /** Check if this migration has already been completed */
150
+ isCompleted: (context: TContext) => Promise<boolean>;
151
+ /** Check if this migration is currently failing */
152
+ isFailing: (context: TContext) => Promise<boolean>;
153
+ /** Check if this migration has failed in a previous attempt */
154
+ hasFailed: (context: TContext) => Promise<boolean>;
155
+ /** Mark this migration as completed (called after successful execution) */
156
+ markComplete: (context: TContext, result: MigrationResult) => Promise<void>;
157
+ /** Mark this migration as failed (called after error) */
158
+ markFailed: (context: TContext, error: Error) => Promise<void>;
159
+ /** Mark this migration as rolled back (called after successful rollback) */
160
+ markRollback: (context: TContext, result: MigrationResult) => Promise<void>;
161
+ /** Execute the migration (apply changes) */
162
+ up: (context: TContext) => Promise<void>;
163
+ /** Optional: Rollback the migration (undo changes) */
164
+ down?: (context: TContext) => Promise<void>;
165
+ /** Optional: Validate prerequisites before execution */
166
+ validate?: (context: TContext) => Promise<ValidationResult>;
167
+ }
168
+ /**
169
+ * Options for running migrations
170
+ */
171
+ interface RunOptions {
172
+ /** Skip migrations that are already completed */
173
+ skipCompleted?: boolean;
174
+ /** Continue running remaining migrations if one fails */
175
+ continueOnError?: boolean;
176
+ /** Maximum time to wait for a migration to complete (ms) */
177
+ timeout?: number;
178
+ /** Execute in dry-run mode (no changes applied) */
179
+ dryRun?: boolean;
180
+ /** Abort signal to cancel migration execution */
181
+ signal?: AbortSignal;
182
+ }
183
+ /**
184
+ * Options for migration registry
185
+ */
186
+ interface RegistryOptions {
187
+ /** Enable debug logging */
188
+ debug?: boolean;
189
+ }
190
+
191
+ /**
192
+ * Default console logger implementation
193
+ */
194
+ declare class ConsoleLogger implements MigrationLogger {
195
+ private useColors;
196
+ constructor(useColors?: boolean);
197
+ private log;
198
+ info(message: string, meta?: Record<string, unknown>): void;
199
+ warn(message: string, meta?: Record<string, unknown>): void;
200
+ error(message: string, meta?: Record<string, unknown>): void;
201
+ debug(message: string, meta?: Record<string, unknown>): void;
202
+ }
203
+ /**
204
+ * Create a default logger instance
205
+ */
206
+ declare function createDefaultLogger(): MigrationLogger;
207
+
208
+ /**
209
+ * Progress tracker for long-running migrations
210
+ */
211
+ declare class MigrationProgress {
212
+ private logger;
213
+ private total;
214
+ private logInterval;
215
+ private processed;
216
+ private lastLoggedPercent;
217
+ private startTime;
218
+ constructor(logger: MigrationLogger, total: number, logInterval?: number);
219
+ /**
220
+ * Increment progress by one
221
+ */
222
+ increment(): void;
223
+ /**
224
+ * Increment progress by a specific amount
225
+ */
226
+ add(amount: number): void;
227
+ /**
228
+ * Set current progress to a specific value
229
+ */
230
+ set(value: number): void;
231
+ /**
232
+ * Get current progress percentage
233
+ */
234
+ getPercent(): number;
235
+ /**
236
+ * Get elapsed time in milliseconds
237
+ */
238
+ getElapsed(): number;
239
+ /**
240
+ * Get estimated time remaining in milliseconds
241
+ */
242
+ getETA(): number;
243
+ /**
244
+ * Complete the progress tracking
245
+ */
246
+ complete(): void;
247
+ /**
248
+ * Check if we should log progress and do so
249
+ */
250
+ private checkAndLog;
251
+ /**
252
+ * Log current progress
253
+ */
254
+ private logProgress;
255
+ /**
256
+ * Create a visual progress bar
257
+ */
258
+ private createProgressBar;
259
+ /**
260
+ * Format a number with thousands separators
261
+ */
262
+ private formatNumber;
263
+ /**
264
+ * Format duration in milliseconds to human-readable string
265
+ */
266
+ private formatDuration;
267
+ }
268
+
269
+ /**
270
+ * Registry for managing migrations
271
+ */
272
+ declare class MigrationRegistry<TContext extends BaseMigrationContext = BaseMigrationContext> {
273
+ private migrations;
274
+ private options;
275
+ constructor(options?: RegistryOptions);
276
+ /**
277
+ * Register a migration
278
+ */
279
+ register(migration: Migration<TContext>): void;
280
+ /**
281
+ * Register multiple migrations at once
282
+ */
283
+ registerMany(migrations: Migration<TContext>[]): void;
284
+ /**
285
+ * Get a migration by name
286
+ */
287
+ get(name: string): Migration<TContext>;
288
+ /**
289
+ * Check if a migration exists
290
+ */
291
+ has(name: string): boolean;
292
+ /**
293
+ * Get all registered migrations
294
+ */
295
+ getAll(): Migration<TContext>[];
296
+ /**
297
+ * Get all migration names
298
+ */
299
+ getNames(): string[];
300
+ /**
301
+ * Get count of registered migrations
302
+ */
303
+ count(): number;
304
+ /**
305
+ * Clear all migrations from registry
306
+ */
307
+ clear(): void;
308
+ /**
309
+ * Auto-discover and register migrations from a directory
310
+ *
311
+ * @param directory - Absolute path to directory containing migration files
312
+ * @param options - Discovery options
313
+ */
314
+ discoverFrom(directory: string, options?: {
315
+ /** File pattern to match (default: "*.migration.{ts,js}") */
316
+ pattern?: RegExp;
317
+ /** Recursively search subdirectories */
318
+ recursive?: boolean;
319
+ }): Promise<number>;
320
+ /**
321
+ * Scan directory for migration files
322
+ */
323
+ private scanDirectory;
324
+ /**
325
+ * Load migration(s) from a file
326
+ */
327
+ private loadMigrationFile;
328
+ /**
329
+ * Type guard to check if an object is a valid migration
330
+ */
331
+ private isMigration;
332
+ }
333
+
334
+ /**
335
+ * Migration runner - executes migrations from a registry
336
+ */
337
+ declare class MigrationRunner<TContext extends BaseMigrationContext = BaseMigrationContext> {
338
+ private registry;
339
+ private context;
340
+ private runningMigrations;
341
+ constructor(registry: MigrationRegistry<TContext>, context: Omit<TContext, 'dryRun' | 'metadata'>);
342
+ /**
343
+ * Run all pending migrations
344
+ */
345
+ runPending(options?: RunOptions): Promise<MigrationResult[]>;
346
+ /**
347
+ * Run a specific migration by name
348
+ */
349
+ run(migrationName: string, options?: RunOptions): Promise<MigrationResult>;
350
+ /**
351
+ * Rollback a specific migration by name
352
+ */
353
+ rollback(migrationName: string, options?: RunOptions): Promise<MigrationResult>;
354
+ /**
355
+ * Get the status of all migrations
356
+ */
357
+ getStatus(): Promise<MigrationSystemStatus>;
358
+ /**
359
+ * Build full context from partial context
360
+ */
361
+ private buildContext;
362
+ /**
363
+ * Run a function with a timeout
364
+ */
365
+ private runWithTimeout;
366
+ }
367
+
368
+ export { ConsoleLogger, DuplicateMigrationError, MigrationAlreadyCompletedError, MigrationAlreadyRunningError, MigrationError, MigrationExecutionError, MigrationNotFoundError, MigrationNotRollbackableError, MigrationProgress, MigrationRegistry, MigrationRollbackError, MigrationRunner, MigrationTimeoutError, MigrationValidationError, createDefaultLogger };
369
+ export type { BaseMigrationContext, Migration, MigrationLogger, MigrationMetadata, MigrationResult, MigrationStatus, MigrationSystemStatus, RegistryOptions, RunOptions, ValidationResult };
@@ -0,0 +1,369 @@
1
+ /**
2
+ * Base error class for all migration errors
3
+ */
4
+ declare class MigrationError extends Error {
5
+ readonly migrationName?: string | undefined;
6
+ readonly code?: string | undefined;
7
+ constructor(message: string, migrationName?: string | undefined, code?: string | undefined);
8
+ }
9
+ /**
10
+ * Thrown when a migration is already running
11
+ */
12
+ declare class MigrationAlreadyRunningError extends MigrationError {
13
+ constructor(migrationName: string);
14
+ }
15
+ /**
16
+ * Thrown when a migration has already been completed
17
+ */
18
+ declare class MigrationAlreadyCompletedError extends MigrationError {
19
+ constructor(migrationName: string);
20
+ }
21
+ /**
22
+ * Thrown when a migration validation fails
23
+ */
24
+ declare class MigrationValidationError extends MigrationError {
25
+ readonly validationMessage: string;
26
+ constructor(migrationName: string, validationMessage: string);
27
+ }
28
+ /**
29
+ * Thrown when a migration execution fails
30
+ */
31
+ declare class MigrationExecutionError extends MigrationError {
32
+ readonly cause: Error;
33
+ constructor(migrationName: string, cause: Error);
34
+ }
35
+ /**
36
+ * Thrown when a migration rollback fails
37
+ */
38
+ declare class MigrationRollbackError extends MigrationError {
39
+ readonly cause: Error;
40
+ constructor(migrationName: string, cause: Error);
41
+ }
42
+ /**
43
+ * Thrown when attempting to rollback a migration that doesn't support it
44
+ */
45
+ declare class MigrationNotRollbackableError extends MigrationError {
46
+ constructor(migrationName: string);
47
+ }
48
+ /**
49
+ * Thrown when a migration is not found in the registry
50
+ */
51
+ declare class MigrationNotFoundError extends MigrationError {
52
+ constructor(migrationName: string);
53
+ }
54
+ /**
55
+ * Thrown when a migration times out
56
+ */
57
+ declare class MigrationTimeoutError extends MigrationError {
58
+ readonly timeout: number;
59
+ constructor(migrationName: string, timeout: number);
60
+ }
61
+ /**
62
+ * Thrown when attempting to register a duplicate migration
63
+ */
64
+ declare class DuplicateMigrationError extends MigrationError {
65
+ constructor(migrationName: string);
66
+ }
67
+
68
+ /**
69
+ * Logger interface for migration operations
70
+ */
71
+ interface MigrationLogger {
72
+ info: (message: string, meta?: Record<string, unknown>) => void;
73
+ warn: (message: string, meta?: Record<string, unknown>) => void;
74
+ error: (message: string, meta?: Record<string, unknown>) => void;
75
+ debug: (message: string, meta?: Record<string, unknown>) => void;
76
+ }
77
+ /**
78
+ * Metadata about a migration execution
79
+ */
80
+ interface MigrationMetadata {
81
+ runId: string;
82
+ startedAt: Date;
83
+ triggeredBy?: string;
84
+ }
85
+ /**
86
+ * Base context available to all migrations
87
+ */
88
+ interface BaseMigrationContext {
89
+ logger: MigrationLogger;
90
+ metadata: MigrationMetadata;
91
+ dryRun: boolean;
92
+ }
93
+ /**
94
+ * Result of a migration validation
95
+ */
96
+ interface ValidationResult {
97
+ valid: boolean;
98
+ message?: string;
99
+ warnings?: string[];
100
+ }
101
+ /**
102
+ * Result of a migration execution
103
+ */
104
+ interface MigrationResult {
105
+ name: string;
106
+ status: 'completed' | 'failed' | 'skipped' | 'rolled-back';
107
+ duration: number;
108
+ startedAt: Date;
109
+ completedAt: Date;
110
+ error?: Error;
111
+ recordsProcessed?: number;
112
+ warnings?: string[];
113
+ metadata?: Record<string, unknown>;
114
+ }
115
+ /**
116
+ * Status of a single migration
117
+ */
118
+ interface MigrationStatus {
119
+ name: string;
120
+ description: string;
121
+ isCompleted: boolean;
122
+ canRollback: boolean;
123
+ lastRun?: Date;
124
+ error?: string;
125
+ }
126
+ /**
127
+ * Overall migration system status
128
+ */
129
+ interface MigrationSystemStatus {
130
+ total: number;
131
+ pending: MigrationStatus[];
132
+ completed: MigrationStatus[];
133
+ failed: {
134
+ currentlyFailing: MigrationStatus[];
135
+ previouslyFailed: MigrationStatus[];
136
+ recovered: MigrationStatus[];
137
+ };
138
+ running: MigrationStatus[];
139
+ }
140
+ /**
141
+ * Core migration definition
142
+ * @template TContext - Extended context type including consumer-specific properties
143
+ */
144
+ interface Migration<TContext extends BaseMigrationContext = BaseMigrationContext> {
145
+ /** Unique identifier for this migration */
146
+ name: string;
147
+ /** Human-readable description */
148
+ description: string;
149
+ /** Check if this migration has already been completed */
150
+ isCompleted: (context: TContext) => Promise<boolean>;
151
+ /** Check if this migration is currently failing */
152
+ isFailing: (context: TContext) => Promise<boolean>;
153
+ /** Check if this migration has failed in a previous attempt */
154
+ hasFailed: (context: TContext) => Promise<boolean>;
155
+ /** Mark this migration as completed (called after successful execution) */
156
+ markComplete: (context: TContext, result: MigrationResult) => Promise<void>;
157
+ /** Mark this migration as failed (called after error) */
158
+ markFailed: (context: TContext, error: Error) => Promise<void>;
159
+ /** Mark this migration as rolled back (called after successful rollback) */
160
+ markRollback: (context: TContext, result: MigrationResult) => Promise<void>;
161
+ /** Execute the migration (apply changes) */
162
+ up: (context: TContext) => Promise<void>;
163
+ /** Optional: Rollback the migration (undo changes) */
164
+ down?: (context: TContext) => Promise<void>;
165
+ /** Optional: Validate prerequisites before execution */
166
+ validate?: (context: TContext) => Promise<ValidationResult>;
167
+ }
168
+ /**
169
+ * Options for running migrations
170
+ */
171
+ interface RunOptions {
172
+ /** Skip migrations that are already completed */
173
+ skipCompleted?: boolean;
174
+ /** Continue running remaining migrations if one fails */
175
+ continueOnError?: boolean;
176
+ /** Maximum time to wait for a migration to complete (ms) */
177
+ timeout?: number;
178
+ /** Execute in dry-run mode (no changes applied) */
179
+ dryRun?: boolean;
180
+ /** Abort signal to cancel migration execution */
181
+ signal?: AbortSignal;
182
+ }
183
+ /**
184
+ * Options for migration registry
185
+ */
186
+ interface RegistryOptions {
187
+ /** Enable debug logging */
188
+ debug?: boolean;
189
+ }
190
+
191
+ /**
192
+ * Default console logger implementation
193
+ */
194
+ declare class ConsoleLogger implements MigrationLogger {
195
+ private useColors;
196
+ constructor(useColors?: boolean);
197
+ private log;
198
+ info(message: string, meta?: Record<string, unknown>): void;
199
+ warn(message: string, meta?: Record<string, unknown>): void;
200
+ error(message: string, meta?: Record<string, unknown>): void;
201
+ debug(message: string, meta?: Record<string, unknown>): void;
202
+ }
203
+ /**
204
+ * Create a default logger instance
205
+ */
206
+ declare function createDefaultLogger(): MigrationLogger;
207
+
208
+ /**
209
+ * Progress tracker for long-running migrations
210
+ */
211
+ declare class MigrationProgress {
212
+ private logger;
213
+ private total;
214
+ private logInterval;
215
+ private processed;
216
+ private lastLoggedPercent;
217
+ private startTime;
218
+ constructor(logger: MigrationLogger, total: number, logInterval?: number);
219
+ /**
220
+ * Increment progress by one
221
+ */
222
+ increment(): void;
223
+ /**
224
+ * Increment progress by a specific amount
225
+ */
226
+ add(amount: number): void;
227
+ /**
228
+ * Set current progress to a specific value
229
+ */
230
+ set(value: number): void;
231
+ /**
232
+ * Get current progress percentage
233
+ */
234
+ getPercent(): number;
235
+ /**
236
+ * Get elapsed time in milliseconds
237
+ */
238
+ getElapsed(): number;
239
+ /**
240
+ * Get estimated time remaining in milliseconds
241
+ */
242
+ getETA(): number;
243
+ /**
244
+ * Complete the progress tracking
245
+ */
246
+ complete(): void;
247
+ /**
248
+ * Check if we should log progress and do so
249
+ */
250
+ private checkAndLog;
251
+ /**
252
+ * Log current progress
253
+ */
254
+ private logProgress;
255
+ /**
256
+ * Create a visual progress bar
257
+ */
258
+ private createProgressBar;
259
+ /**
260
+ * Format a number with thousands separators
261
+ */
262
+ private formatNumber;
263
+ /**
264
+ * Format duration in milliseconds to human-readable string
265
+ */
266
+ private formatDuration;
267
+ }
268
+
269
+ /**
270
+ * Registry for managing migrations
271
+ */
272
+ declare class MigrationRegistry<TContext extends BaseMigrationContext = BaseMigrationContext> {
273
+ private migrations;
274
+ private options;
275
+ constructor(options?: RegistryOptions);
276
+ /**
277
+ * Register a migration
278
+ */
279
+ register(migration: Migration<TContext>): void;
280
+ /**
281
+ * Register multiple migrations at once
282
+ */
283
+ registerMany(migrations: Migration<TContext>[]): void;
284
+ /**
285
+ * Get a migration by name
286
+ */
287
+ get(name: string): Migration<TContext>;
288
+ /**
289
+ * Check if a migration exists
290
+ */
291
+ has(name: string): boolean;
292
+ /**
293
+ * Get all registered migrations
294
+ */
295
+ getAll(): Migration<TContext>[];
296
+ /**
297
+ * Get all migration names
298
+ */
299
+ getNames(): string[];
300
+ /**
301
+ * Get count of registered migrations
302
+ */
303
+ count(): number;
304
+ /**
305
+ * Clear all migrations from registry
306
+ */
307
+ clear(): void;
308
+ /**
309
+ * Auto-discover and register migrations from a directory
310
+ *
311
+ * @param directory - Absolute path to directory containing migration files
312
+ * @param options - Discovery options
313
+ */
314
+ discoverFrom(directory: string, options?: {
315
+ /** File pattern to match (default: "*.migration.{ts,js}") */
316
+ pattern?: RegExp;
317
+ /** Recursively search subdirectories */
318
+ recursive?: boolean;
319
+ }): Promise<number>;
320
+ /**
321
+ * Scan directory for migration files
322
+ */
323
+ private scanDirectory;
324
+ /**
325
+ * Load migration(s) from a file
326
+ */
327
+ private loadMigrationFile;
328
+ /**
329
+ * Type guard to check if an object is a valid migration
330
+ */
331
+ private isMigration;
332
+ }
333
+
334
+ /**
335
+ * Migration runner - executes migrations from a registry
336
+ */
337
+ declare class MigrationRunner<TContext extends BaseMigrationContext = BaseMigrationContext> {
338
+ private registry;
339
+ private context;
340
+ private runningMigrations;
341
+ constructor(registry: MigrationRegistry<TContext>, context: Omit<TContext, 'dryRun' | 'metadata'>);
342
+ /**
343
+ * Run all pending migrations
344
+ */
345
+ runPending(options?: RunOptions): Promise<MigrationResult[]>;
346
+ /**
347
+ * Run a specific migration by name
348
+ */
349
+ run(migrationName: string, options?: RunOptions): Promise<MigrationResult>;
350
+ /**
351
+ * Rollback a specific migration by name
352
+ */
353
+ rollback(migrationName: string, options?: RunOptions): Promise<MigrationResult>;
354
+ /**
355
+ * Get the status of all migrations
356
+ */
357
+ getStatus(): Promise<MigrationSystemStatus>;
358
+ /**
359
+ * Build full context from partial context
360
+ */
361
+ private buildContext;
362
+ /**
363
+ * Run a function with a timeout
364
+ */
365
+ private runWithTimeout;
366
+ }
367
+
368
+ export { ConsoleLogger, DuplicateMigrationError, MigrationAlreadyCompletedError, MigrationAlreadyRunningError, MigrationError, MigrationExecutionError, MigrationNotFoundError, MigrationNotRollbackableError, MigrationProgress, MigrationRegistry, MigrationRollbackError, MigrationRunner, MigrationTimeoutError, MigrationValidationError, createDefaultLogger };
369
+ export type { BaseMigrationContext, Migration, MigrationLogger, MigrationMetadata, MigrationResult, MigrationStatus, MigrationSystemStatus, RegistryOptions, RunOptions, ValidationResult };
package/dist/index.mjs ADDED
@@ -0,0 +1,4 @@
1
+ var C=Object.defineProperty;var c=(n,t)=>C(n,"name",{value:t,configurable:!0});import*as x from"node:fs/promises";import*as F from"node:path";import{randomUUID as I}from"node:crypto";class d extends Error{static{c(this,"MigrationError")}constructor(t,r,i){super(t),this.migrationName=r,this.code=i,this.name="MigrationError",Error.captureStackTrace(this,this.constructor)}}class p extends d{static{c(this,"MigrationAlreadyRunningError")}constructor(t){super(`Migration "${t}" is already running`,t,"MIGRATION_ALREADY_RUNNING"),this.name="MigrationAlreadyRunningError"}}class m extends d{static{c(this,"MigrationAlreadyCompletedError")}constructor(t){super(`Migration "${t}" has already been completed`,t,"MIGRATION_ALREADY_COMPLETED"),this.name="MigrationAlreadyCompletedError"}}class y extends d{static{c(this,"MigrationValidationError")}constructor(t,r){super(`Migration "${t}" validation failed: ${r}`,t,"MIGRATION_VALIDATION_FAILED"),this.validationMessage=r,this.name="MigrationValidationError"}}class M extends d{static{c(this,"MigrationExecutionError")}constructor(t,r){super(`Migration "${t}" execution failed: ${r.message}`,t,"MIGRATION_EXECUTION_FAILED"),this.cause=r,this.name="MigrationExecutionError",this.stack=r.stack}}class w extends d{static{c(this,"MigrationRollbackError")}constructor(t,r){super(`Migration "${t}" rollback failed: ${r.message}`,t,"MIGRATION_ROLLBACK_FAILED"),this.cause=r,this.name="MigrationRollbackError",this.stack=r.stack}}class R extends d{static{c(this,"MigrationNotRollbackableError")}constructor(t){super(`Migration "${t}" does not support rollback`,t,"MIGRATION_NOT_ROLLBACKABLE"),this.name="MigrationNotRollbackableError"}}class $ extends d{static{c(this,"MigrationNotFoundError")}constructor(t){super(`Migration "${t}" not found in registry`,t,"MIGRATION_NOT_FOUND"),this.name="MigrationNotFoundError"}}class E extends d{static{c(this,"MigrationTimeoutError")}constructor(t,r){super(`Migration "${t}" timed out after ${r}ms`,t,"MIGRATION_TIMEOUT"),this.timeout=r,this.name="MigrationTimeoutError"}}class A extends d{static{c(this,"DuplicateMigrationError")}constructor(t){super(`Migration "${t}" is already registered`,t,"DUPLICATE_MIGRATION"),this.name="DuplicateMigrationError"}}const f={reset:"\x1B[0m",bright:"\x1B[1m",red:"\x1B[31m",yellow:"\x1B[33m",blue:"\x1B[34m",cyan:"\x1B[36m",gray:"\x1B[90m"};function T(n){if(!n||Object.keys(n).length===0)return"";try{return" "+JSON.stringify(n,null,2).split(`
2
+ `).map((t,r)=>r===0?t:` ${t}`).join(`
3
+ `)}catch{return" [Object]"}}c(T,"formatMeta");function b(){return new Date().toISOString()}c(b,"getTimestamp");class D{static{c(this,"ConsoleLogger")}constructor(t=!0){this.useColors=t}log(t,r,i,e){const o=this.useColors?`${f.gray}${b()}${f.reset}`:b(),s=this.useColors?`${r}${t.padEnd(5)}${f.reset}`:t.padEnd(5),l=this.useColors?`${f.bright}${i}${f.reset}`:i,a=T(e);console.log(`${o} ${s} ${l}${a}`)}info(t,r){this.log("INFO",f.blue,t,r)}warn(t,r){this.log("WARN",f.yellow,t,r)}error(t,r){this.log("ERROR",f.red,t,r)}debug(t,r){this.log("DEBUG",f.cyan,t,r)}}function O(){const n=process.stdout.isTTY&&process.env.FORCE_COLOR!=="0"&&process.env.NODE_DISABLE_COLORS===void 0;return new D(n)}c(O,"createDefaultLogger");class L{static{c(this,"MigrationProgress")}constructor(t,r,i=10){this.logger=t,this.total=r,this.logInterval=i,this.logger.info(`Starting processing of ${this.formatNumber(r)} items`)}processed=0;lastLoggedPercent=0;startTime=Date.now();increment(){this.processed++,this.checkAndLog()}add(t){this.processed+=t,this.checkAndLog()}set(t){this.processed=t,this.checkAndLog()}getPercent(){return this.total===0?100:Math.floor(this.processed/this.total*100)}getElapsed(){return Date.now()-this.startTime}getETA(){if(this.processed===0)return 0;const t=this.getElapsed(),r=this.processed/t;return(this.total-this.processed)/r}complete(){this.processed=this.total;const t=this.getElapsed(),r=this.total/(t/1e3);this.logger.info(`Completed processing ${this.formatNumber(this.total)} items in ${this.formatDuration(t)}`,{total:this.total,duration:t,rate:`${r.toFixed(2)} items/sec`})}checkAndLog(){const t=this.getPercent();(t>=this.lastLoggedPercent+this.logInterval||this.processed===this.total)&&(this.logProgress(),this.lastLoggedPercent=t)}logProgress(){const t=this.getPercent(),r=this.getElapsed(),i=this.getETA(),e=this.createProgressBar(t);this.logger.info(`Progress: ${e} ${t}% (${this.formatNumber(this.processed)}/${this.formatNumber(this.total)})`,{elapsed:this.formatDuration(r),eta:this.formatDuration(i)})}createProgressBar(t,r=20){const i=Math.floor(t/100*r),e=r-i;return`[${"\u2588".repeat(i)}${" ".repeat(e)}]`}formatNumber(t){return t.toLocaleString()}formatDuration(t){return t<1e3?`${Math.round(t)}ms`:t<6e4?`${(t/1e3).toFixed(1)}s`:t<36e5?`${(t/6e4).toFixed(1)}m`:`${(t/36e5).toFixed(1)}h`}}class P{static{c(this,"MigrationRegistry")}migrations=new Map;options;constructor(t={}){this.options=t}register(t){if(this.migrations.has(t.name))throw new A(t.name);this.migrations.set(t.name,t),this.options.debug&&console.debug(`[MigrationRegistry] Registered migration: ${t.name}`)}registerMany(t){for(const r of t)this.register(r)}get(t){const r=this.migrations.get(t);if(!r)throw new $(t);return r}has(t){return this.migrations.has(t)}getAll(){return Array.from(this.migrations.values())}getNames(){return Array.from(this.migrations.keys())}count(){return this.migrations.size}clear(){this.migrations.clear()}async discoverFrom(t,r={}){const{pattern:i=/\.migration\.(ts|js)$/,recursive:e=!1}=r,o=[];try{const s=await this.scanDirectory(t,i,e);for(const l of s)try{const a=await this.loadMigrationFile(l);o.push(...a)}catch(a){this.options.debug&&console.error(`[MigrationRegistry] Failed to load migration from ${l}:`,a)}return this.registerMany(o),this.options.debug&&console.debug(`[MigrationRegistry] Discovered ${o.length} migrations from ${t}`),o.length}catch(s){throw this.options.debug&&console.error(`[MigrationRegistry] Failed to discover migrations from ${t}:`,s),s}}async scanDirectory(t,r,i){const e=[];try{const o=await x.readdir(t,{withFileTypes:!0});for(const s of o){const l=F.join(t,s.name);if(s.isDirectory()&&i){const a=await this.scanDirectory(l,r,i);e.push(...a)}else s.isFile()&&r.test(s.name)&&e.push(l)}}catch(o){this.options.debug&&console.debug(`[MigrationRegistry] Cannot scan directory ${t}:`,o)}return e}async loadMigrationFile(t){const r=await import(t),i=[];r.default&&(this.isMigration(r.default)?i.push(r.default):Array.isArray(r.default)&&i.push(...r.default.filter(this.isMigration))),this.isMigration(r.migration)&&i.push(r.migration),Array.isArray(r.migrations)&&i.push(...r.migrations.filter(this.isMigration));for(const[e,o]of Object.entries(r))e!=="default"&&e!=="migration"&&e!=="migrations"&&this.isMigration(o)&&i.push(o);return i}isMigration(t){if(!t||typeof t!="object")return!1;const r=t;return typeof r.name=="string"&&typeof r.description=="string"&&typeof r.isCompleted=="function"&&typeof r.isFailing=="function"&&typeof r.hasFailed=="function"&&typeof r.markComplete=="function"&&typeof r.markFailed=="function"&&typeof r.up=="function"}}class S{static{c(this,"MigrationRunner")}constructor(t,r){this.registry=t,this.context=r}runningMigrations=new Set;async runPending(t={}){const r=this.buildContext(t),{skipCompleted:i=!0,continueOnError:e=!1}=t;r.logger.info("Starting pending migrations...");const o=this.registry.getAll(),s=[];for(const g of o)try{if(i&&await g.isCompleted(r)){r.logger.info(`Skipping completed migration: ${g.name}`),s.push({name:g.name,status:"skipped",duration:0,startedAt:new Date,completedAt:new Date});continue}const u=await this.run(g.name,t);s.push(u)}catch(u){if(r.logger.error(`Migration ${g.name} failed`,{error:u instanceof Error?u.message:String(u)}),s.push({name:g.name,status:"failed",duration:0,startedAt:new Date,completedAt:new Date,error:u instanceof Error?u:new Error(String(u))}),!e)throw u}const l=s.filter(g=>g.status==="completed").length,a=s.filter(g=>g.status==="failed").length,h=s.filter(g=>g.status==="skipped").length;return r.logger.info("All pending migrations processed",{total:s.length,completed:l,failed:a,skipped:h}),s}async run(t,r={}){const i=this.registry.get(t),e=this.buildContext(r),{skipCompleted:o=!0,timeout:s}=r;if(this.runningMigrations.has(t))throw new p(t);const l=await i.isCompleted(e);if(o&&l&&!e.dryRun)throw new m(t);this.runningMigrations.add(t);try{const a=Date.now();if(e.logger.info(e.dryRun?`[DRY RUN] Starting migration: ${t}`:`Starting migration: ${t}`,{description:i.description}),i.validate){const u=await i.validate(e);if(!u.valid)throw new y(t,u.message||"Validation failed");if(u.warnings&&u.warnings.length>0)for(const k of u.warnings)e.logger.warn(k)}s?await this.runWithTimeout(()=>i.up(e),s,t):await i.up(e);const h=Date.now()-a,g={name:t,status:"completed",duration:h,startedAt:new Date(a),completedAt:new Date};return e.dryRun||await i.markComplete(e,g),e.logger.info(e.dryRun?`[DRY RUN] Migration completed: ${t}`:`Migration completed: ${t}`,{duration:`${h}ms`}),g}catch(a){const h=a instanceof Error?a:new Error(String(a));throw e.logger.error(`Migration failed: ${t}`,{error:h.message,stack:h.stack}),e.dryRun||await i.markFailed(e,h),new M(t,h)}finally{this.runningMigrations.delete(t)}}async rollback(t,r={}){const i=this.registry.get(t),e=this.buildContext(r),{timeout:o}=r;if(!i.down)throw new R(t);if(this.runningMigrations.has(t))throw new p(t);this.runningMigrations.add(t);try{const s=Date.now();e.logger.info(e.dryRun?`[DRY RUN] Rolling back migration: ${t}`:`Rolling back migration: ${t}`),o?await this.runWithTimeout(async()=>{if(!i.down)throw new Error("Rollback function not defined");await i.down(e)},o,t):await i.down(e);const l=Date.now()-s,a={name:t,status:"rolled-back",duration:l,startedAt:new Date(s),completedAt:new Date};return e.logger.info(e.dryRun?`[DRY RUN] Migration rolled back: ${t}`:`Migration rolled back: ${t}`,{duration:`${l}ms`}),e.dryRun||await i.markRollback(e,a),a}catch(s){const l=s instanceof Error?s:new Error(String(s));throw e.logger.error(`Rollback failed: ${t}`,{error:l.message,stack:l.stack}),new w(t,l)}finally{this.runningMigrations.delete(t)}}async getStatus(){const t=this.buildContext({}),r=this.registry.getAll(),i=await Promise.all(r.map(async e=>{const[o,s,l,a]=await Promise.all([e.isCompleted(t),e.isFailing(t),e.hasFailed(t),Promise.resolve(this.runningMigrations.has(e.name))]);return{name:e.name,description:e.description,isCompleted:o,isFailing:s,hasFailed:l,isRunning:a,canRollback:!!e.down}}));return{total:i.length,pending:i.filter(e=>!e.isCompleted&&!e.hasFailed&&!e.isRunning),completed:i.filter(e=>e.isCompleted&&!e.isRunning),failed:{currentlyFailing:i.filter(e=>e.isFailing&&!e.isRunning),previouslyFailed:i.filter(e=>e.hasFailed&&!e.isRunning),recovered:i.filter(e=>e.isCompleted&&!e.isFailing&&e.hasFailed&&!e.isRunning)},running:i.filter(e=>e.isRunning)}}buildContext(t){const r={runId:I(),startedAt:new Date,triggeredBy:t.triggeredBy};return{...this.context,...t,metadata:r,dryRun:t.dryRun??!1}}async runWithTimeout(t,r,i){const e=new Promise((o,s)=>{setTimeout(()=>{s(new E(i,r))},r)});await Promise.race([t(),e])}}export{D as ConsoleLogger,A as DuplicateMigrationError,m as MigrationAlreadyCompletedError,p as MigrationAlreadyRunningError,d as MigrationError,M as MigrationExecutionError,$ as MigrationNotFoundError,R as MigrationNotRollbackableError,L as MigrationProgress,P as MigrationRegistry,w as MigrationRollbackError,S as MigrationRunner,E as MigrationTimeoutError,y as MigrationValidationError,O as createDefaultLogger};
4
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":["../src/errors.ts","../src/logger.ts","../src/progress.ts","../src/registry.ts","../src/runner.ts"],"sourcesContent":["/**\n * Base error class for all migration errors\n */\nexport class MigrationError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly migrationName?: string,\n\t\tpublic readonly code?: string\n\t) {\n\t\tsuper(message);\n\t\tthis.name = 'MigrationError';\n\t\tError.captureStackTrace(this, this.constructor);\n\t}\n}\n\n/**\n * Thrown when a migration is already running\n */\nexport class MigrationAlreadyRunningError extends MigrationError {\n\tconstructor(migrationName: string) {\n\t\tsuper(\n\t\t\t`Migration \"${migrationName}\" is already running`,\n\t\t\tmigrationName,\n\t\t\t'MIGRATION_ALREADY_RUNNING'\n\t\t);\n\t\tthis.name = 'MigrationAlreadyRunningError';\n\t}\n}\n\n/**\n * Thrown when a migration has already been completed\n */\nexport class MigrationAlreadyCompletedError extends MigrationError {\n\tconstructor(migrationName: string) {\n\t\tsuper(\n\t\t\t`Migration \"${migrationName}\" has already been completed`,\n\t\t\tmigrationName,\n\t\t\t'MIGRATION_ALREADY_COMPLETED'\n\t\t);\n\t\tthis.name = 'MigrationAlreadyCompletedError';\n\t}\n}\n\n/**\n * Thrown when a migration validation fails\n */\nexport class MigrationValidationError extends MigrationError {\n\tconstructor(\n\t\tmigrationName: string,\n\t\tpublic readonly validationMessage: string\n\t) {\n\t\tsuper(\n\t\t\t`Migration \"${migrationName}\" validation failed: ${validationMessage}`,\n\t\t\tmigrationName,\n\t\t\t'MIGRATION_VALIDATION_FAILED'\n\t\t);\n\t\tthis.name = 'MigrationValidationError';\n\t}\n}\n\n/**\n * Thrown when a migration execution fails\n */\nexport class MigrationExecutionError extends MigrationError {\n\tconstructor(\n\t\tmigrationName: string,\n\t\tpublic override readonly cause: Error\n\t) {\n\t\tsuper(\n\t\t\t`Migration \"${migrationName}\" execution failed: ${cause.message}`,\n\t\t\tmigrationName,\n\t\t\t'MIGRATION_EXECUTION_FAILED'\n\t\t);\n\t\tthis.name = 'MigrationExecutionError';\n\t\tthis.stack = cause.stack;\n\t}\n}\n\n/**\n * Thrown when a migration rollback fails\n */\nexport class MigrationRollbackError extends MigrationError {\n\tconstructor(\n\t\tmigrationName: string,\n\t\tpublic override readonly cause: Error\n\t) {\n\t\tsuper(\n\t\t\t`Migration \"${migrationName}\" rollback failed: ${cause.message}`,\n\t\t\tmigrationName,\n\t\t\t'MIGRATION_ROLLBACK_FAILED'\n\t\t);\n\t\tthis.name = 'MigrationRollbackError';\n\t\tthis.stack = cause.stack;\n\t}\n}\n\n/**\n * Thrown when attempting to rollback a migration that doesn't support it\n */\nexport class MigrationNotRollbackableError extends MigrationError {\n\tconstructor(migrationName: string) {\n\t\tsuper(\n\t\t\t`Migration \"${migrationName}\" does not support rollback`,\n\t\t\tmigrationName,\n\t\t\t'MIGRATION_NOT_ROLLBACKABLE'\n\t\t);\n\t\tthis.name = 'MigrationNotRollbackableError';\n\t}\n}\n\n/**\n * Thrown when a migration is not found in the registry\n */\nexport class MigrationNotFoundError extends MigrationError {\n\tconstructor(migrationName: string) {\n\t\tsuper(\n\t\t\t`Migration \"${migrationName}\" not found in registry`,\n\t\t\tmigrationName,\n\t\t\t'MIGRATION_NOT_FOUND'\n\t\t);\n\t\tthis.name = 'MigrationNotFoundError';\n\t}\n}\n\n/**\n * Thrown when a migration times out\n */\nexport class MigrationTimeoutError extends MigrationError {\n\tconstructor(\n\t\tmigrationName: string,\n\t\tpublic readonly timeout: number\n\t) {\n\t\tsuper(\n\t\t\t`Migration \"${migrationName}\" timed out after ${timeout}ms`,\n\t\t\tmigrationName,\n\t\t\t'MIGRATION_TIMEOUT'\n\t\t);\n\t\tthis.name = 'MigrationTimeoutError';\n\t}\n}\n\n/**\n * Thrown when attempting to register a duplicate migration\n */\nexport class DuplicateMigrationError extends MigrationError {\n\tconstructor(migrationName: string) {\n\t\tsuper(\n\t\t\t`Migration \"${migrationName}\" is already registered`,\n\t\t\tmigrationName,\n\t\t\t'DUPLICATE_MIGRATION'\n\t\t);\n\t\tthis.name = 'DuplicateMigrationError';\n\t}\n}\n","import type { MigrationLogger } from './types';\n\n/**\n * ANSI color codes for console output\n */\nconst colors = {\n\treset: '\\x1b[0m',\n\tbright: '\\x1b[1m',\n\tdim: '\\x1b[2m',\n\tred: '\\x1b[31m',\n\tgreen: '\\x1b[32m',\n\tyellow: '\\x1b[33m',\n\tblue: '\\x1b[34m',\n\tcyan: '\\x1b[36m',\n\tgray: '\\x1b[90m',\n} as const;\n\n/**\n * Format metadata for logging\n */\nfunction formatMeta(meta?: Record<string, unknown>): string {\n\tif (!meta || Object.keys(meta).length === 0) {\n\t\treturn '';\n\t}\n\n\ttry {\n\t\treturn (\n\t\t\t' ' +\n\t\t\tJSON.stringify(meta, null, 2)\n\t\t\t\t.split('\\n')\n\t\t\t\t.map((line, i) => (i === 0 ? line : ` ${line}`))\n\t\t\t\t.join('\\n')\n\t\t);\n\t} catch {\n\t\treturn ' [Object]';\n\t}\n}\n\n/**\n * Get current timestamp string\n */\nfunction getTimestamp(): string {\n\treturn new Date().toISOString();\n}\n\n/**\n * Default console logger implementation\n */\nexport class ConsoleLogger implements MigrationLogger {\n\tconstructor(private useColors = true) {}\n\n\tprivate log(\n\t\tlevel: 'INFO' | 'WARN' | 'ERROR' | 'DEBUG',\n\t\tcolor: string,\n\t\tmessage: string,\n\t\tmeta?: Record<string, unknown>\n\t): void {\n\t\tconst timestamp = this.useColors\n\t\t\t? `${colors.gray}${getTimestamp()}${colors.reset}`\n\t\t\t: getTimestamp();\n\n\t\tconst levelStr = this.useColors\n\t\t\t? `${color}${level.padEnd(5)}${colors.reset}`\n\t\t\t: level.padEnd(5);\n\n\t\tconst formattedMessage = this.useColors\n\t\t\t? `${colors.bright}${message}${colors.reset}`\n\t\t\t: message;\n\n\t\tconst metaStr = formatMeta(meta);\n\n\t\tconsole.log(`${timestamp} ${levelStr} ${formattedMessage}${metaStr}`);\n\t}\n\n\tinfo(message: string, meta?: Record<string, unknown>): void {\n\t\tthis.log('INFO', colors.blue, message, meta);\n\t}\n\n\twarn(message: string, meta?: Record<string, unknown>): void {\n\t\tthis.log('WARN', colors.yellow, message, meta);\n\t}\n\n\terror(message: string, meta?: Record<string, unknown>): void {\n\t\tthis.log('ERROR', colors.red, message, meta);\n\t}\n\n\tdebug(message: string, meta?: Record<string, unknown>): void {\n\t\tthis.log('DEBUG', colors.cyan, message, meta);\n\t}\n}\n\n/**\n * Create a default logger instance\n */\nexport function createDefaultLogger(): MigrationLogger {\n\tconst isColorSupported =\n\t\tprocess.stdout.isTTY &&\n\t\tprocess.env.FORCE_COLOR !== '0' &&\n\t\tprocess.env.NODE_DISABLE_COLORS === undefined;\n\n\treturn new ConsoleLogger(isColorSupported);\n}\n","import type { MigrationLogger } from './types';\n\n/**\n * Progress tracker for long-running migrations\n */\nexport class MigrationProgress {\n\tprivate processed = 0;\n\tprivate lastLoggedPercent = 0;\n\tprivate startTime = Date.now();\n\n\tconstructor(\n\t\tprivate logger: MigrationLogger,\n\t\tprivate total: number,\n\t\tprivate logInterval = 10 // Log every 10% by default\n\t) {\n\t\tthis.logger.info(\n\t\t\t`Starting processing of ${this.formatNumber(total)} items`\n\t\t);\n\t}\n\n\t/**\n\t * Increment progress by one\n\t */\n\tincrement(): void {\n\t\tthis.processed++;\n\t\tthis.checkAndLog();\n\t}\n\n\t/**\n\t * Increment progress by a specific amount\n\t */\n\tadd(amount: number): void {\n\t\tthis.processed += amount;\n\t\tthis.checkAndLog();\n\t}\n\n\t/**\n\t * Set current progress to a specific value\n\t */\n\tset(value: number): void {\n\t\tthis.processed = value;\n\t\tthis.checkAndLog();\n\t}\n\n\t/**\n\t * Get current progress percentage\n\t */\n\tgetPercent(): number {\n\t\treturn this.total === 0\n\t\t\t? 100\n\t\t\t: Math.floor((this.processed / this.total) * 100);\n\t}\n\n\t/**\n\t * Get elapsed time in milliseconds\n\t */\n\tgetElapsed(): number {\n\t\treturn Date.now() - this.startTime;\n\t}\n\n\t/**\n\t * Get estimated time remaining in milliseconds\n\t */\n\tgetETA(): number {\n\t\tif (this.processed === 0) return 0;\n\n\t\tconst elapsed = this.getElapsed();\n\t\tconst rate = this.processed / elapsed;\n\t\tconst remaining = this.total - this.processed;\n\n\t\treturn remaining / rate;\n\t}\n\n\t/**\n\t * Complete the progress tracking\n\t */\n\tcomplete(): void {\n\t\tthis.processed = this.total;\n\t\tconst elapsed = this.getElapsed();\n\t\tconst rate = this.total / (elapsed / 1000);\n\n\t\tthis.logger.info(\n\t\t\t`Completed processing ${this.formatNumber(this.total)} items in ${this.formatDuration(elapsed)}`,\n\t\t\t{\n\t\t\t\ttotal: this.total,\n\t\t\t\tduration: elapsed,\n\t\t\t\trate: `${rate.toFixed(2)} items/sec`,\n\t\t\t}\n\t\t);\n\t}\n\n\t/**\n\t * Check if we should log progress and do so\n\t */\n\tprivate checkAndLog(): void {\n\t\tconst currentPercent = this.getPercent();\n\n\t\t// Log at intervals or when complete\n\t\tif (\n\t\t\tcurrentPercent >= this.lastLoggedPercent + this.logInterval ||\n\t\t\tthis.processed === this.total\n\t\t) {\n\t\t\tthis.logProgress();\n\t\t\tthis.lastLoggedPercent = currentPercent;\n\t\t}\n\t}\n\n\t/**\n\t * Log current progress\n\t */\n\tprivate logProgress(): void {\n\t\tconst percent = this.getPercent();\n\t\tconst elapsed = this.getElapsed();\n\t\tconst eta = this.getETA();\n\n\t\tconst progressBar = this.createProgressBar(percent);\n\n\t\tthis.logger.info(\n\t\t\t`Progress: ${progressBar} ${percent}% (${this.formatNumber(this.processed)}/${this.formatNumber(this.total)})`,\n\t\t\t{\n\t\t\t\telapsed: this.formatDuration(elapsed),\n\t\t\t\teta: this.formatDuration(eta),\n\t\t\t}\n\t\t);\n\t}\n\n\t/**\n\t * Create a visual progress bar\n\t */\n\tprivate createProgressBar(percent: number, width = 20): string {\n\t\tconst filled = Math.floor((percent / 100) * width);\n\t\tconst empty = width - filled;\n\n\t\treturn `[${'█'.repeat(filled)}${' '.repeat(empty)}]`;\n\t}\n\n\t/**\n\t * Format a number with thousands separators\n\t */\n\tprivate formatNumber(num: number): string {\n\t\treturn num.toLocaleString();\n\t}\n\n\t/**\n\t * Format duration in milliseconds to human-readable string\n\t */\n\tprivate formatDuration(ms: number): string {\n\t\tif (ms < 1000) return `${Math.round(ms)}ms`;\n\t\tif (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n\t\tif (ms < 3600000) return `${(ms / 60000).toFixed(1)}m`;\n\t\treturn `${(ms / 3600000).toFixed(1)}h`;\n\t}\n}\n","import * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport { DuplicateMigrationError, MigrationNotFoundError } from './errors';\nimport type { BaseMigrationContext, Migration, RegistryOptions } from './types';\n\n/**\n * Registry for managing migrations\n */\nexport class MigrationRegistry<\n\tTContext extends BaseMigrationContext = BaseMigrationContext,\n> {\n\tprivate migrations = new Map<string, Migration<TContext>>();\n\tprivate options: RegistryOptions;\n\n\tconstructor(options: RegistryOptions = {}) {\n\t\tthis.options = options;\n\t}\n\n\t/**\n\t * Register a migration\n\t */\n\tregister(migration: Migration<TContext>): void {\n\t\tif (this.migrations.has(migration.name)) {\n\t\t\tthrow new DuplicateMigrationError(migration.name);\n\t\t}\n\n\t\tthis.migrations.set(migration.name, migration);\n\n\t\tif (this.options.debug) {\n\t\t\tconsole.debug(\n\t\t\t\t`[MigrationRegistry] Registered migration: ${migration.name}`\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Register multiple migrations at once\n\t */\n\tregisterMany(migrations: Migration<TContext>[]): void {\n\t\tfor (const migration of migrations) {\n\t\t\tthis.register(migration);\n\t\t}\n\t}\n\n\t/**\n\t * Get a migration by name\n\t */\n\tget(name: string): Migration<TContext> {\n\t\tconst migration = this.migrations.get(name);\n\n\t\tif (!migration) {\n\t\t\tthrow new MigrationNotFoundError(name);\n\t\t}\n\n\t\treturn migration;\n\t}\n\n\t/**\n\t * Check if a migration exists\n\t */\n\thas(name: string): boolean {\n\t\treturn this.migrations.has(name);\n\t}\n\n\t/**\n\t * Get all registered migrations\n\t */\n\tgetAll(): Migration<TContext>[] {\n\t\treturn Array.from(this.migrations.values());\n\t}\n\n\t/**\n\t * Get all migration names\n\t */\n\tgetNames(): string[] {\n\t\treturn Array.from(this.migrations.keys());\n\t}\n\n\t/**\n\t * Get count of registered migrations\n\t */\n\tcount(): number {\n\t\treturn this.migrations.size;\n\t}\n\n\t/**\n\t * Clear all migrations from registry\n\t */\n\tclear(): void {\n\t\tthis.migrations.clear();\n\t}\n\n\t/**\n\t * Auto-discover and register migrations from a directory\n\t *\n\t * @param directory - Absolute path to directory containing migration files\n\t * @param options - Discovery options\n\t */\n\tasync discoverFrom(\n\t\tdirectory: string,\n\t\toptions: {\n\t\t\t/** File pattern to match (default: \"*.migration.{ts,js}\") */\n\t\t\tpattern?: RegExp;\n\t\t\t/** Recursively search subdirectories */\n\t\t\trecursive?: boolean;\n\t\t} = {}\n\t): Promise<number> {\n\t\tconst { pattern = /\\.migration\\.(ts|js)$/, recursive = false } = options;\n\n\t\tconst discovered: Migration<TContext>[] = [];\n\n\t\ttry {\n\t\t\tconst files = await this.scanDirectory(directory, pattern, recursive);\n\n\t\t\tfor (const file of files) {\n\t\t\t\ttry {\n\t\t\t\t\tconst migrations = await this.loadMigrationFile(file);\n\t\t\t\t\tdiscovered.push(...migrations);\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (this.options.debug) {\n\t\t\t\t\t\tconsole.error(\n\t\t\t\t\t\t\t`[MigrationRegistry] Failed to load migration from ${file}:`,\n\t\t\t\t\t\t\terror\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.registerMany(discovered);\n\n\t\t\tif (this.options.debug) {\n\t\t\t\tconsole.debug(\n\t\t\t\t\t`[MigrationRegistry] Discovered ${discovered.length} migrations from ${directory}`\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn discovered.length;\n\t\t} catch (error) {\n\t\t\tif (this.options.debug) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`[MigrationRegistry] Failed to discover migrations from ${directory}:`,\n\t\t\t\t\terror\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Scan directory for migration files\n\t */\n\tprivate async scanDirectory(\n\t\tdirectory: string,\n\t\tpattern: RegExp,\n\t\trecursive: boolean\n\t): Promise<string[]> {\n\t\tconst files: string[] = [];\n\n\t\ttry {\n\t\t\tconst entries = await fs.readdir(directory, { withFileTypes: true });\n\n\t\t\tfor (const entry of entries) {\n\t\t\t\tconst fullPath = path.join(directory, entry.name);\n\n\t\t\t\tif (entry.isDirectory() && recursive) {\n\t\t\t\t\tconst subFiles = await this.scanDirectory(\n\t\t\t\t\t\tfullPath,\n\t\t\t\t\t\tpattern,\n\t\t\t\t\t\trecursive\n\t\t\t\t\t);\n\t\t\t\t\tfiles.push(...subFiles);\n\t\t\t\t} else if (entry.isFile() && pattern.test(entry.name)) {\n\t\t\t\t\tfiles.push(fullPath);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Directory doesn't exist or not accessible\n\t\t\tif (this.options.debug) {\n\t\t\t\tconsole.debug(\n\t\t\t\t\t`[MigrationRegistry] Cannot scan directory ${directory}:`,\n\t\t\t\t\terror\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn files;\n\t}\n\n\t/**\n\t * Load migration(s) from a file\n\t */\n\tprivate async loadMigrationFile(\n\t\tfilePath: string\n\t): Promise<Migration<TContext>[]> {\n\t\t// Dynamic import works for both .ts (with ts-node/tsx) and .js\n\t\tconst module = await import(filePath);\n\n\t\tconst migrations: Migration<TContext>[] = [];\n\n\t\t// Support different export patterns:\n\t\t// 1. export default migration\n\t\t// 2. export const migration = ...\n\t\t// 3. export const migrations = [...]\n\t\t// 4. Named exports that are migration objects\n\n\t\tif (module.default) {\n\t\t\tif (this.isMigration(module.default)) {\n\t\t\t\tmigrations.push(module.default);\n\t\t\t} else if (Array.isArray(module.default)) {\n\t\t\t\tmigrations.push(...module.default.filter(this.isMigration));\n\t\t\t}\n\t\t}\n\n\t\t// Check for \"migration\" or \"migrations\" exports\n\t\tif (this.isMigration(module.migration)) {\n\t\t\tmigrations.push(module.migration);\n\t\t}\n\n\t\tif (Array.isArray(module.migrations)) {\n\t\t\tmigrations.push(...module.migrations.filter(this.isMigration));\n\t\t}\n\n\t\t// Check all named exports\n\t\tfor (const [key, value] of Object.entries(module)) {\n\t\t\tif (key !== 'default' && key !== 'migration' && key !== 'migrations') {\n\t\t\t\tif (this.isMigration(value)) {\n\t\t\t\t\tmigrations.push(value as Migration<TContext>);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn migrations;\n\t}\n\n\t/**\n\t * Type guard to check if an object is a valid migration\n\t */\n\tprivate isMigration(obj: unknown): obj is Migration<TContext> {\n\t\tif (!obj || typeof obj !== 'object') return false;\n\n\t\tconst migration = obj as Partial<Migration<TContext>>;\n\n\t\treturn (\n\t\t\ttypeof migration.name === 'string' &&\n\t\t\ttypeof migration.description === 'string' &&\n\t\t\ttypeof migration.isCompleted === 'function' &&\n\t\t\ttypeof migration.isFailing === 'function' &&\n\t\t\ttypeof migration.hasFailed === 'function' &&\n\t\t\ttypeof migration.markComplete === 'function' &&\n\t\t\ttypeof migration.markFailed === 'function' &&\n\t\t\ttypeof migration.up === 'function'\n\t\t);\n\t}\n}\n","import { randomUUID } from 'node:crypto';\nimport {\n\tMigrationAlreadyCompletedError,\n\tMigrationAlreadyRunningError,\n\tMigrationExecutionError,\n\tMigrationNotRollbackableError,\n\tMigrationRollbackError,\n\tMigrationTimeoutError,\n\tMigrationValidationError,\n} from './errors';\nimport type { MigrationRegistry } from './registry';\nimport type {\n\tBaseMigrationContext,\n\tMigrationMetadata,\n\tMigrationResult,\n\tMigrationSystemStatus,\n\tRunOptions,\n} from './types';\n\n/**\n * Migration runner - executes migrations from a registry\n */\nexport class MigrationRunner<\n\tTContext extends BaseMigrationContext = BaseMigrationContext,\n> {\n\tprivate runningMigrations = new Set<string>();\n\n\tconstructor(\n\t\tprivate registry: MigrationRegistry<TContext>,\n\t\tprivate context: Omit<TContext, 'dryRun' | 'metadata'>\n\t) {}\n\n\t/**\n\t * Run all pending migrations\n\t */\n\tasync runPending(options: RunOptions = {}): Promise<MigrationResult[]> {\n\t\tconst fullContext = this.buildContext(options);\n\t\tconst { skipCompleted = true, continueOnError = false } = options;\n\n\t\tfullContext.logger.info('Starting pending migrations...');\n\n\t\tconst allMigrations = this.registry.getAll();\n\t\tconst results: MigrationResult[] = [];\n\n\t\tfor (const migration of allMigrations) {\n\t\t\ttry {\n\t\t\t\t// Check if already completed\n\t\t\t\tif (skipCompleted && (await migration.isCompleted(fullContext))) {\n\t\t\t\t\tfullContext.logger.info(\n\t\t\t\t\t\t`Skipping completed migration: ${migration.name}`\n\t\t\t\t\t);\n\t\t\t\t\tresults.push({\n\t\t\t\t\t\tname: migration.name,\n\t\t\t\t\t\tstatus: 'skipped',\n\t\t\t\t\t\tduration: 0,\n\t\t\t\t\t\tstartedAt: new Date(),\n\t\t\t\t\t\tcompletedAt: new Date(),\n\t\t\t\t\t});\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst result = await this.run(migration.name, options);\n\t\t\t\tresults.push(result);\n\t\t\t} catch (error) {\n\t\t\t\tfullContext.logger.error(`Migration ${migration.name} failed`, {\n\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t});\n\n\t\t\t\tresults.push({\n\t\t\t\t\tname: migration.name,\n\t\t\t\t\tstatus: 'failed',\n\t\t\t\t\tduration: 0,\n\t\t\t\t\tstartedAt: new Date(),\n\t\t\t\t\tcompletedAt: new Date(),\n\t\t\t\t\terror: error instanceof Error ? error : new Error(String(error)),\n\t\t\t\t});\n\n\t\t\t\tif (!continueOnError) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst completed = results.filter((r) => r.status === 'completed').length;\n\t\tconst failed = results.filter((r) => r.status === 'failed').length;\n\t\tconst skipped = results.filter((r) => r.status === 'skipped').length;\n\n\t\tfullContext.logger.info('All pending migrations processed', {\n\t\t\ttotal: results.length,\n\t\t\tcompleted,\n\t\t\tfailed,\n\t\t\tskipped,\n\t\t});\n\n\t\treturn results;\n\t}\n\n\t/**\n\t * Run a specific migration by name\n\t */\n\tasync run(\n\t\tmigrationName: string,\n\t\toptions: RunOptions = {}\n\t): Promise<MigrationResult> {\n\t\tconst migration = this.registry.get(migrationName);\n\t\tconst fullContext = this.buildContext(options);\n\t\tconst { skipCompleted = true, timeout } = options;\n\n\t\t// Check if already running\n\t\tif (this.runningMigrations.has(migrationName)) {\n\t\t\tthrow new MigrationAlreadyRunningError(migrationName);\n\t\t}\n\n\t\t// Check if already completed\n\t\tconst isCompleted = await migration.isCompleted(fullContext);\n\t\tif (skipCompleted && isCompleted) {\n\t\t\tif (!fullContext.dryRun) {\n\t\t\t\tthrow new MigrationAlreadyCompletedError(migrationName);\n\t\t\t}\n\t\t}\n\n\t\tthis.runningMigrations.add(migrationName);\n\n\t\ttry {\n\t\t\tconst startTime = Date.now();\n\t\t\tfullContext.logger.info(\n\t\t\t\tfullContext.dryRun\n\t\t\t\t\t? `[DRY RUN] Starting migration: ${migrationName}`\n\t\t\t\t\t: `Starting migration: ${migrationName}`,\n\t\t\t\t{ description: migration.description }\n\t\t\t);\n\n\t\t\t// Validate if validation function exists\n\t\t\tif (migration.validate) {\n\t\t\t\tconst validation = await migration.validate(fullContext);\n\t\t\t\tif (!validation.valid) {\n\t\t\t\t\tthrow new MigrationValidationError(\n\t\t\t\t\t\tmigrationName,\n\t\t\t\t\t\tvalidation.message || 'Validation failed'\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tif (validation.warnings && validation.warnings.length > 0) {\n\t\t\t\t\tfor (const warning of validation.warnings) {\n\t\t\t\t\t\tfullContext.logger.warn(warning);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Execute migration with optional timeout\n\t\t\tif (timeout) {\n\t\t\t\tawait this.runWithTimeout(\n\t\t\t\t\t() => migration.up(fullContext),\n\t\t\t\t\ttimeout,\n\t\t\t\t\tmigrationName\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tawait migration.up(fullContext);\n\t\t\t}\n\n\t\t\tconst duration = Date.now() - startTime;\n\t\t\tconst result: MigrationResult = {\n\t\t\t\tname: migrationName,\n\t\t\t\tstatus: 'completed',\n\t\t\t\tduration,\n\t\t\t\tstartedAt: new Date(startTime),\n\t\t\t\tcompletedAt: new Date(),\n\t\t\t};\n\n\t\t\t// Mark as complete (unless dry run)\n\t\t\tif (!fullContext.dryRun) {\n\t\t\t\tawait migration.markComplete(fullContext, result);\n\t\t\t}\n\n\t\t\tfullContext.logger.info(\n\t\t\t\tfullContext.dryRun\n\t\t\t\t\t? `[DRY RUN] Migration completed: ${migrationName}`\n\t\t\t\t\t: `Migration completed: ${migrationName}`,\n\t\t\t\t{ duration: `${duration}ms` }\n\t\t\t);\n\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tconst err = error instanceof Error ? error : new Error(String(error));\n\n\t\t\tfullContext.logger.error(`Migration failed: ${migrationName}`, {\n\t\t\t\terror: err.message,\n\t\t\t\tstack: err.stack,\n\t\t\t});\n\n\t\t\t// Mark as failed (unless dry run)\n\t\t\tif (!fullContext.dryRun) {\n\t\t\t\tawait migration.markFailed(fullContext, err);\n\t\t\t}\n\n\t\t\tthrow new MigrationExecutionError(migrationName, err);\n\t\t} finally {\n\t\t\tthis.runningMigrations.delete(migrationName);\n\t\t}\n\t}\n\n\t/**\n\t * Rollback a specific migration by name\n\t */\n\tasync rollback(\n\t\tmigrationName: string,\n\t\toptions: RunOptions = {}\n\t): Promise<MigrationResult> {\n\t\tconst migration = this.registry.get(migrationName);\n\t\tconst fullContext = this.buildContext(options);\n\t\tconst { timeout } = options;\n\n\t\t// Check if rollback is supported\n\t\tif (!migration.down) {\n\t\t\tthrow new MigrationNotRollbackableError(migrationName);\n\t\t}\n\n\t\t// Check if migration is running\n\t\tif (this.runningMigrations.has(migrationName)) {\n\t\t\tthrow new MigrationAlreadyRunningError(migrationName);\n\t\t}\n\n\t\tthis.runningMigrations.add(migrationName);\n\n\t\ttry {\n\t\t\tconst startTime = Date.now();\n\t\t\tfullContext.logger.info(\n\t\t\t\tfullContext.dryRun\n\t\t\t\t\t? `[DRY RUN] Rolling back migration: ${migrationName}`\n\t\t\t\t\t: `Rolling back migration: ${migrationName}`\n\t\t\t);\n\n\t\t\t// Execute rollback with optional timeout\n\t\t\tif (timeout) {\n\t\t\t\tawait this.runWithTimeout(\n\t\t\t\t\tasync () => {\n\t\t\t\t\t\tif (!migration.down) {\n\t\t\t\t\t\t\tthrow new Error('Rollback function not defined');\n\t\t\t\t\t\t}\n\t\t\t\t\t\tawait migration.down(fullContext);\n\t\t\t\t\t},\n\t\t\t\t\ttimeout,\n\t\t\t\t\tmigrationName\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tawait migration.down(fullContext);\n\t\t\t}\n\n\t\t\tconst duration = Date.now() - startTime;\n\t\t\tconst result: MigrationResult = {\n\t\t\t\tname: migrationName,\n\t\t\t\tstatus: 'rolled-back',\n\t\t\t\tduration,\n\t\t\t\tstartedAt: new Date(startTime),\n\t\t\t\tcompletedAt: new Date(),\n\t\t\t};\n\n\t\t\tfullContext.logger.info(\n\t\t\t\tfullContext.dryRun\n\t\t\t\t\t? `[DRY RUN] Migration rolled back: ${migrationName}`\n\t\t\t\t\t: `Migration rolled back: ${migrationName}`,\n\t\t\t\t{ duration: `${duration}ms` }\n\t\t\t);\n\n\t\t\tif (!fullContext.dryRun) {\n\t\t\t\tawait migration.markRollback(fullContext, result);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tconst err = error instanceof Error ? error : new Error(String(error));\n\n\t\t\tfullContext.logger.error(`Rollback failed: ${migrationName}`, {\n\t\t\t\terror: err.message,\n\t\t\t\tstack: err.stack,\n\t\t\t});\n\n\t\t\tthrow new MigrationRollbackError(migrationName, err);\n\t\t} finally {\n\t\t\tthis.runningMigrations.delete(migrationName);\n\t\t}\n\t}\n\n\t/**\n\t * Get the status of all migrations\n\t */\n\tasync getStatus(): Promise<MigrationSystemStatus> {\n\t\tconst fullContext = this.buildContext({});\n\t\tconst allMigrations = this.registry.getAll();\n\n\t\tconst statuses = await Promise.all(\n\t\t\tallMigrations.map(async (migration) => {\n\t\t\t\tconst [isCompleted, isFailing, hasFailed, isRunning] =\n\t\t\t\t\tawait Promise.all([\n\t\t\t\t\t\tmigration.isCompleted(fullContext),\n\t\t\t\t\t\tmigration.isFailing(fullContext),\n\t\t\t\t\t\tmigration.hasFailed(fullContext),\n\t\t\t\t\t\tPromise.resolve(this.runningMigrations.has(migration.name)),\n\t\t\t\t\t]);\n\n\t\t\t\treturn {\n\t\t\t\t\tname: migration.name,\n\t\t\t\t\tdescription: migration.description,\n\t\t\t\t\tisCompleted,\n\t\t\t\t\tisFailing,\n\t\t\t\t\thasFailed,\n\t\t\t\t\tisRunning,\n\t\t\t\t\tcanRollback: !!migration.down,\n\t\t\t\t};\n\t\t\t})\n\t\t);\n\n\t\treturn {\n\t\t\ttotal: statuses.length,\n\t\t\tpending: statuses.filter(\n\t\t\t\t(s) => !s.isCompleted && !s.hasFailed && !s.isRunning\n\t\t\t),\n\t\t\tcompleted: statuses.filter((s) => s.isCompleted && !s.isRunning),\n\t\t\tfailed: {\n\t\t\t\tcurrentlyFailing: statuses.filter((s) => s.isFailing && !s.isRunning),\n\t\t\t\tpreviouslyFailed: statuses.filter((s) => s.hasFailed && !s.isRunning),\n\t\t\t\trecovered: statuses.filter(\n\t\t\t\t\t(s) => s.isCompleted && !s.isFailing && s.hasFailed && !s.isRunning\n\t\t\t\t),\n\t\t\t},\n\t\t\trunning: statuses.filter((s) => s.isRunning),\n\t\t};\n\t}\n\n\t/**\n\t * Build full context from partial context\n\t */\n\tprivate buildContext(\n\t\tpartial: Partial<Pick<RunOptions, 'dryRun' | 'signal'>>\n\t) {\n\t\tconst metadata: MigrationMetadata = {\n\t\t\trunId: randomUUID(),\n\t\t\tstartedAt: new Date(),\n\t\t\ttriggeredBy: (\n\t\t\t\tpartial as {\n\t\t\t\t\ttriggeredBy?: string;\n\t\t\t\t}\n\t\t\t).triggeredBy,\n\t\t};\n\n\t\treturn {\n\t\t\t...this.context,\n\t\t\t...partial,\n\t\t\tmetadata,\n\t\t\tdryRun: partial.dryRun ?? false,\n\t\t} as TContext & {\n\t\t\tmetadata: MigrationMetadata;\n\t\t};\n\t}\n\n\t/**\n\t * Run a function with a timeout\n\t */\n\tprivate async runWithTimeout(\n\t\tfn: () => Promise<void>,\n\t\ttimeout: number,\n\t\tmigrationName: string\n\t): Promise<void> {\n\t\tconst timeoutPromise = new Promise<never>((_, reject) => {\n\t\t\tsetTimeout(() => {\n\t\t\t\treject(new MigrationTimeoutError(migrationName, timeout));\n\t\t\t}, timeout);\n\t\t});\n\n\t\tawait Promise.race([fn(), timeoutPromise]);\n\t}\n}\n"],"names":["MigrationError","__name","message","migrationName","code","MigrationAlreadyRunningError","MigrationAlreadyCompletedError","MigrationValidationError","validationMessage","MigrationExecutionError","cause","MigrationRollbackError","MigrationNotRollbackableError","MigrationNotFoundError","MigrationTimeoutError","timeout","DuplicateMigrationError","colors","formatMeta","meta","line","i","getTimestamp","ConsoleLogger","useColors","level","color","timestamp","levelStr","formattedMessage","metaStr","createDefaultLogger","isColorSupported","MigrationProgress","logger","total","logInterval","amount","value","elapsed","rate","currentPercent","percent","eta","progressBar","width","filled","empty","num","ms","MigrationRegistry","options","migration","migrations","name","directory","pattern","recursive","discovered","files","file","error","entries","fs","entry","fullPath","path","subFiles","filePath","module","key","obj","MigrationRunner","registry","context","fullContext","skipCompleted","continueOnError","allMigrations","results","result","completed","r","failed","skipped","isCompleted","startTime","validation","warning","duration","err","statuses","isFailing","hasFailed","isRunning","s","partial","metadata","randomUUID","fn","timeoutPromise","_","reject"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@md-oss/migrations",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "publishConfig": {
6
+ "access": "public",
7
+ "registry": "https://registry.npmjs.org/"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+ssh://git@github.com/Mirasaki-OSS/monorepo-template.git",
12
+ "directory": "vendor/migrations"
13
+ },
14
+ "type": "module",
15
+ "description": "Lightweight migration runner with registry, validation, progress, and rollback support",
16
+ "license": "ISC",
17
+ "main": "./dist/index.cjs",
18
+ "module": "./dist/index.mjs",
19
+ "types": "./dist/index.d.cts",
20
+ "files": [
21
+ "dist/**/*",
22
+ "README.md",
23
+ "LICENSE"
24
+ ],
25
+ "exports": {
26
+ "require": {
27
+ "types": "./dist/index.d.cts",
28
+ "default": "./dist/index.cjs"
29
+ },
30
+ "import": {
31
+ "types": "./dist/index.d.mts",
32
+ "default": "./dist/index.mjs"
33
+ }
34
+ },
35
+ "dependencies": {
36
+ "@md-oss/config": "^0.1.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^25.0.9",
40
+ "pkgroll": "^2.21.5",
41
+ "typescript": "^5.9.3"
42
+ },
43
+ "scripts": {
44
+ "build": "pkgroll --minify --clean-dist --sourcemap --define.process.env.NODE_ENV='\"production\"' --define.DEBUG=false",
45
+ "clean": "git clean -xdf .turbo dist node_modules tsconfig.tsbuildinfo",
46
+ "dev": "tsc --watch",
47
+ "typecheck": "tsc --noEmit"
48
+ }
49
+ }