@ftschopp/dynatable-migrations 1.3.1 → 2.0.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/CHANGELOG.md +50 -0
- package/README.md +2 -2
- package/USAGE.md +2 -2
- package/dist/cli.js +51 -68
- package/dist/cli.js.map +1 -1
- package/dist/commands/create.d.ts.map +1 -1
- package/dist/commands/create.js +1 -1
- package/dist/commands/create.js.map +1 -1
- package/dist/commands/down.d.ts.map +1 -1
- package/dist/commands/down.js +3 -2
- package/dist/commands/down.js.map +1 -1
- package/dist/commands/init.js +1 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +12 -7
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/unlock.js +1 -1
- package/dist/commands/unlock.js.map +1 -1
- package/dist/commands/up.d.ts.map +1 -1
- package/dist/commands/up.js +3 -2
- package/dist/commands/up.js.map +1 -1
- package/dist/core/config.d.ts +4 -23
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +72 -102
- package/dist/core/config.js.map +1 -1
- package/dist/core/loader.d.ts +16 -62
- package/dist/core/loader.d.ts.map +1 -1
- package/dist/core/loader.js +165 -216
- package/dist/core/loader.js.map +1 -1
- package/dist/core/runner.d.ts +2 -28
- package/dist/core/runner.d.ts.map +1 -1
- package/dist/core/runner.js +181 -218
- package/dist/core/runner.js.map +1 -1
- package/dist/core/tracker.d.ts +10 -65
- package/dist/core/tracker.d.ts.map +1 -1
- package/dist/core/tracker.js +229 -282
- package/dist/core/tracker.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/core/runner.js
CHANGED
|
@@ -1,199 +1,78 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.createMigrationRunner = void 0;
|
|
4
4
|
const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
|
|
5
5
|
const tracker_1 = require("./tracker");
|
|
6
6
|
const loader_1 = require("./loader");
|
|
7
7
|
const semver_1 = require("./semver");
|
|
8
8
|
const lock_heartbeat_1 = require("./lock-heartbeat");
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
const DYNAMODB_COMMANDS = {
|
|
10
|
+
ScanCommand: lib_dynamodb_1.ScanCommand,
|
|
11
|
+
QueryCommand: lib_dynamodb_1.QueryCommand,
|
|
12
|
+
GetCommand: lib_dynamodb_1.GetCommand,
|
|
13
|
+
PutCommand: lib_dynamodb_1.PutCommand,
|
|
14
|
+
UpdateCommand: lib_dynamodb_1.UpdateCommand,
|
|
15
|
+
DeleteCommand: lib_dynamodb_1.DeleteCommand,
|
|
16
|
+
BatchGetCommand: lib_dynamodb_1.BatchGetCommand,
|
|
17
|
+
BatchWriteCommand: lib_dynamodb_1.BatchWriteCommand,
|
|
18
|
+
TransactWriteCommand: lib_dynamodb_1.TransactWriteCommand,
|
|
19
|
+
TransactGetCommand: lib_dynamodb_1.TransactGetCommand,
|
|
20
|
+
};
|
|
21
|
+
const createContext = (client, config, tracker) => ({
|
|
22
|
+
client,
|
|
23
|
+
tableName: config.tableName,
|
|
24
|
+
tracker,
|
|
25
|
+
config,
|
|
26
|
+
dynamodb: DYNAMODB_COMMANDS,
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* Run the body under an exclusive migration lock with a refreshing heartbeat.
|
|
30
|
+
* Skipped entirely when `dryRun` is true so dry runs never need the lock.
|
|
31
|
+
*/
|
|
32
|
+
const withLock = async (tracker, dryRun, body) => {
|
|
33
|
+
if (dryRun)
|
|
34
|
+
return body();
|
|
35
|
+
const lockAcquired = await tracker.acquireLock();
|
|
36
|
+
if (!lockAcquired) {
|
|
37
|
+
throw new Error('Could not acquire migration lock. Another migration may be in progress. ' +
|
|
38
|
+
'If you believe this is an error, wait a few minutes and try again.');
|
|
15
39
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
async initialize() {
|
|
20
|
-
await this.tracker.initialize();
|
|
40
|
+
const stopHeartbeat = (0, lock_heartbeat_1.startLockHeartbeat)(tracker, tracker.lockTtlSeconds);
|
|
41
|
+
try {
|
|
42
|
+
return await body();
|
|
21
43
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
44
|
+
finally {
|
|
45
|
+
// Always stop the heartbeat and release the lock — in that order, so
|
|
46
|
+
// we don't refresh a lock we're about to delete.
|
|
47
|
+
stopHeartbeat();
|
|
48
|
+
await tracker.releaseLock();
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const createMigrationRunner = (client, config) => {
|
|
52
|
+
const tracker = (0, tracker_1.createMigrationTracker)(client, config);
|
|
53
|
+
const loader = (0, loader_1.createMigrationLoader)(config.migrationsDir || './migrations');
|
|
54
|
+
const initialize = () => tracker.initialize();
|
|
55
|
+
const up = async (options = {}) => {
|
|
26
56
|
const { limit, dryRun = false } = options;
|
|
27
57
|
if (limit !== undefined && (!Number.isInteger(limit) || limit <= 0)) {
|
|
28
58
|
throw new Error(`runner.up({ limit }) requires a positive integer (got ${JSON.stringify(limit)}).`);
|
|
29
59
|
}
|
|
30
|
-
await
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const lockAcquired = await this.tracker.acquireLock();
|
|
35
|
-
if (!lockAcquired) {
|
|
36
|
-
throw new Error('Could not acquire migration lock. Another migration may be in progress. ' +
|
|
37
|
-
'If you believe this is an error, wait a few minutes and try again.');
|
|
38
|
-
}
|
|
39
|
-
stopHeartbeat = (0, lock_heartbeat_1.startLockHeartbeat)(this.tracker, this.tracker.lockTtlSeconds);
|
|
40
|
-
}
|
|
41
|
-
try {
|
|
42
|
-
const appliedMigrations = await this.tracker.getAppliedMigrations();
|
|
43
|
-
const appliedVersions = appliedMigrations
|
|
44
|
-
.filter((m) => m.status === 'applied')
|
|
45
|
-
.map((m) => m.version);
|
|
46
|
-
// Load every migration once and index by version. Without this, the
|
|
47
|
-
// checksum-mismatch loop below would call `loader.getMigration(...)`
|
|
48
|
-
// for each applied migration — and each of those re-reads the
|
|
49
|
-
// directory and re-imports every file. The loader caches the result
|
|
50
|
-
// internally, but going through a Map here also flattens the per-call
|
|
51
|
-
// microtask overhead.
|
|
52
|
-
const allMigrations = await this.loader.loadMigrations();
|
|
53
|
-
const migrationByVersion = new Map(allMigrations.map((m) => [m.version, m]));
|
|
54
|
-
// Check for checksum mismatches on applied migrations
|
|
55
|
-
for (const applied of appliedMigrations.filter((m) => m.status === 'applied')) {
|
|
56
|
-
const file = migrationByVersion.get(applied.version);
|
|
57
|
-
if (file && applied.checksum && file.checksum !== applied.checksum) {
|
|
58
|
-
console.warn(`⚠️ Warning: Migration ${applied.version} has been modified since it was applied. ` +
|
|
59
|
-
`Original checksum: ${applied.checksum}, Current: ${file.checksum}`);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
let pendingMigrations = allMigrations.filter((m) => !appliedVersions.includes(m.version));
|
|
63
|
-
// Apply limit if specified
|
|
64
|
-
if (limit) {
|
|
65
|
-
pendingMigrations = pendingMigrations.slice(0, limit);
|
|
66
|
-
}
|
|
67
|
-
if (pendingMigrations.length === 0) {
|
|
68
|
-
console.log('✅ No pending migrations');
|
|
69
|
-
return [];
|
|
70
|
-
}
|
|
71
|
-
if (dryRun) {
|
|
72
|
-
console.log(`\n🔍 DRY RUN - Would apply ${pendingMigrations.length} migration(s):\n`);
|
|
73
|
-
for (const migrationFile of pendingMigrations) {
|
|
74
|
-
console.log(` ${migrationFile.version}: ${migrationFile.name}`);
|
|
75
|
-
if (migrationFile.migration.description) {
|
|
76
|
-
console.log(` ${migrationFile.migration.description}`);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
console.log('\nNo changes were made.\n');
|
|
80
|
-
return pendingMigrations;
|
|
81
|
-
}
|
|
82
|
-
console.log(`\n📦 Found ${pendingMigrations.length} pending migration(s)\n`);
|
|
83
|
-
const executed = [];
|
|
84
|
-
for (const migrationFile of pendingMigrations) {
|
|
85
|
-
try {
|
|
86
|
-
console.log(`⬆️ Applying ${migrationFile.version}: ${migrationFile.name}`);
|
|
87
|
-
const context = this.createContext();
|
|
88
|
-
await migrationFile.migration.up(context);
|
|
89
|
-
await this.tracker.markAsApplied(migrationFile.version, migrationFile.name, migrationFile.migration.schema, undefined, migrationFile.checksum);
|
|
90
|
-
console.log(`✅ Applied ${migrationFile.version}: ${migrationFile.name}\n`);
|
|
91
|
-
executed.push(migrationFile);
|
|
92
|
-
}
|
|
93
|
-
catch (error) {
|
|
94
|
-
console.error(`❌ Failed to apply ${migrationFile.version}: ${error.message}\n`);
|
|
95
|
-
await this.tracker.markAsFailed(migrationFile.version, error.message);
|
|
96
|
-
throw new Error(`Migration ${migrationFile.version} failed: ${error.message}`);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return executed;
|
|
100
|
-
}
|
|
101
|
-
finally {
|
|
102
|
-
// Always stop the heartbeat and release the lock — in that order, so
|
|
103
|
-
// we don't refresh a lock we're about to delete.
|
|
104
|
-
if (stopHeartbeat)
|
|
105
|
-
stopHeartbeat();
|
|
106
|
-
if (!dryRun) {
|
|
107
|
-
await this.tracker.releaseLock();
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Rollback last migration
|
|
113
|
-
*/
|
|
114
|
-
async down(steps = 1, dryRun = false) {
|
|
60
|
+
await initialize();
|
|
61
|
+
return withLock(tracker, dryRun, () => runPending(tracker, loader, client, config, limit, dryRun));
|
|
62
|
+
};
|
|
63
|
+
const down = async (steps = 1, dryRun = false) => {
|
|
115
64
|
if (!Number.isInteger(steps) || steps <= 0) {
|
|
116
65
|
throw new Error(`runner.down(steps) requires a positive integer (got ${JSON.stringify(steps)}).`);
|
|
117
66
|
}
|
|
118
|
-
await
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
try {
|
|
129
|
-
const appliedMigrations = await this.tracker.getAppliedMigrations();
|
|
130
|
-
// Full applied list, semver-descending. We need the whole thing (not
|
|
131
|
-
// just the slice we're about to roll back) so we can tell the tracker
|
|
132
|
-
// what version the CURRENT pointer should fall back to after each
|
|
133
|
-
// step — without making it re-Query the migration history every time.
|
|
134
|
-
const allApplied = appliedMigrations
|
|
135
|
-
.filter((m) => m.status === 'applied')
|
|
136
|
-
.sort((a, b) => (0, semver_1.compareSemver)(b.version, a.version));
|
|
137
|
-
const applied = allApplied.slice(0, steps);
|
|
138
|
-
if (applied.length === 0) {
|
|
139
|
-
console.log('✅ No migrations to rollback');
|
|
140
|
-
return [];
|
|
141
|
-
}
|
|
142
|
-
if (dryRun) {
|
|
143
|
-
console.log(`\n🔍 DRY RUN - Would rollback ${applied.length} migration(s):\n`);
|
|
144
|
-
for (const record of applied) {
|
|
145
|
-
console.log(` ${record.version}: ${record.name}`);
|
|
146
|
-
}
|
|
147
|
-
console.log('\nNo changes were made.\n');
|
|
148
|
-
return [];
|
|
149
|
-
}
|
|
150
|
-
console.log(`\n📦 Rolling back ${applied.length} migration(s)\n`);
|
|
151
|
-
// Load every migration once. The loader caches internally, but routing
|
|
152
|
-
// through a Map here keeps the hot loop free of per-call awaits.
|
|
153
|
-
const allMigrations = await this.loader.loadMigrations();
|
|
154
|
-
const migrationByVersion = new Map(allMigrations.map((m) => [m.version, m]));
|
|
155
|
-
const rolledBack = [];
|
|
156
|
-
for (let i = 0; i < applied.length; i++) {
|
|
157
|
-
const record = applied[i];
|
|
158
|
-
try {
|
|
159
|
-
const migrationFile = migrationByVersion.get(record.version);
|
|
160
|
-
if (!migrationFile) {
|
|
161
|
-
throw new Error(`Migration file not found for version ${record.version}`);
|
|
162
|
-
}
|
|
163
|
-
console.log(`⬇️ Rolling back ${migrationFile.version}: ${migrationFile.name}`);
|
|
164
|
-
const context = this.createContext();
|
|
165
|
-
await migrationFile.migration.down(context);
|
|
166
|
-
// After rolling this one back, the new CURRENT is whatever applied
|
|
167
|
-
// version sits one slot deeper in `allApplied`. If we've consumed
|
|
168
|
-
// the whole list, fall back to v0000 (no schema applied).
|
|
169
|
-
const previousVersion = allApplied[i + 1]?.version ?? 'v0000';
|
|
170
|
-
await this.tracker.markAsRolledBack(migrationFile.version, previousVersion);
|
|
171
|
-
console.log(`✅ Rolled back ${migrationFile.version}: ${migrationFile.name}\n`);
|
|
172
|
-
rolledBack.push(migrationFile);
|
|
173
|
-
}
|
|
174
|
-
catch (error) {
|
|
175
|
-
console.error(`❌ Failed to rollback ${record.version}: ${error.message}\n`);
|
|
176
|
-
await this.tracker.markAsFailed(record.version, error.message);
|
|
177
|
-
throw new Error(`Rollback of ${record.version} failed: ${error.message}`);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
return rolledBack;
|
|
181
|
-
}
|
|
182
|
-
finally {
|
|
183
|
-
if (stopHeartbeat)
|
|
184
|
-
stopHeartbeat();
|
|
185
|
-
if (!dryRun) {
|
|
186
|
-
await this.tracker.releaseLock();
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Get migration status
|
|
192
|
-
*/
|
|
193
|
-
async status() {
|
|
194
|
-
await this.initialize();
|
|
195
|
-
const allMigrations = await this.loader.loadMigrations();
|
|
196
|
-
const appliedMigrations = await this.tracker.getAppliedMigrations();
|
|
67
|
+
await initialize();
|
|
68
|
+
return withLock(tracker, dryRun, () => runRollback(tracker, loader, client, config, steps, dryRun));
|
|
69
|
+
};
|
|
70
|
+
const status = async () => {
|
|
71
|
+
await initialize();
|
|
72
|
+
const [allMigrations, appliedMigrations] = await Promise.all([
|
|
73
|
+
loader.loadMigrations(),
|
|
74
|
+
tracker.getAppliedMigrations(),
|
|
75
|
+
]);
|
|
197
76
|
const appliedMap = new Map(appliedMigrations.map((m) => [m.version, m]));
|
|
198
77
|
return allMigrations.map((migrationFile) => {
|
|
199
78
|
const record = appliedMap.get(migrationFile.version);
|
|
@@ -212,50 +91,134 @@ class MigrationRunner {
|
|
|
212
91
|
error: record.error,
|
|
213
92
|
};
|
|
214
93
|
});
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Reset all migrations (rollback everything)
|
|
225
|
-
*/
|
|
226
|
-
async reset() {
|
|
227
|
-
const appliedMigrations = await this.tracker.getAppliedMigrations();
|
|
94
|
+
};
|
|
95
|
+
const getCurrentVersion = async () => {
|
|
96
|
+
await initialize();
|
|
97
|
+
return tracker.getCurrentVersion();
|
|
98
|
+
};
|
|
99
|
+
const reset = async () => {
|
|
100
|
+
const appliedMigrations = await tracker.getAppliedMigrations();
|
|
228
101
|
const appliedCount = appliedMigrations.filter((m) => m.status === 'applied').length;
|
|
229
102
|
if (appliedCount === 0) {
|
|
230
103
|
console.log('✅ No migrations to reset');
|
|
231
104
|
return;
|
|
232
105
|
}
|
|
233
106
|
console.log(`\n⚠️ Resetting ${appliedCount} migration(s)\n`);
|
|
234
|
-
await
|
|
107
|
+
await down(appliedCount);
|
|
108
|
+
};
|
|
109
|
+
return { initialize, up, down, status, getCurrentVersion, reset };
|
|
110
|
+
};
|
|
111
|
+
exports.createMigrationRunner = createMigrationRunner;
|
|
112
|
+
const runPending = async (tracker, loader, client, config, limit, dryRun) => {
|
|
113
|
+
const appliedMigrations = await tracker.getAppliedMigrations();
|
|
114
|
+
const appliedVersions = appliedMigrations
|
|
115
|
+
.filter((m) => m.status === 'applied')
|
|
116
|
+
.map((m) => m.version);
|
|
117
|
+
// Load every migration once and index by version. Without this, the
|
|
118
|
+
// checksum-mismatch loop below would call `loader.getMigration(...)`
|
|
119
|
+
// for each applied migration — and each of those re-reads the
|
|
120
|
+
// directory and re-imports every file. The loader caches the result
|
|
121
|
+
// internally, but going through a Map here also flattens the per-call
|
|
122
|
+
// microtask overhead.
|
|
123
|
+
const allMigrations = await loader.loadMigrations();
|
|
124
|
+
const migrationByVersion = new Map(allMigrations.map((m) => [m.version, m]));
|
|
125
|
+
// Check for checksum mismatches on applied migrations
|
|
126
|
+
appliedMigrations
|
|
127
|
+
.filter((m) => m.status === 'applied')
|
|
128
|
+
.forEach((applied) => {
|
|
129
|
+
const file = migrationByVersion.get(applied.version);
|
|
130
|
+
if (file && applied.checksum && file.checksum !== applied.checksum) {
|
|
131
|
+
console.warn(`⚠️ Warning: Migration ${applied.version} has been modified since it was applied. ` +
|
|
132
|
+
`Original checksum: ${applied.checksum}, Current: ${file.checksum}`);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
const pendingAll = allMigrations.filter((m) => !appliedVersions.includes(m.version));
|
|
136
|
+
const pendingMigrations = limit ? pendingAll.slice(0, limit) : pendingAll;
|
|
137
|
+
if (pendingMigrations.length === 0) {
|
|
138
|
+
console.log('✅ No pending migrations');
|
|
139
|
+
return [];
|
|
235
140
|
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
141
|
+
if (dryRun) {
|
|
142
|
+
console.log(`\n🔍 DRY RUN - Would apply ${pendingMigrations.length} migration(s):\n`);
|
|
143
|
+
pendingMigrations.forEach((migrationFile) => {
|
|
144
|
+
console.log(` ${migrationFile.version}: ${migrationFile.name}`);
|
|
145
|
+
if (migrationFile.migration.description) {
|
|
146
|
+
console.log(` ${migrationFile.migration.description}`);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
console.log('\nNo changes were made.\n');
|
|
150
|
+
return pendingMigrations;
|
|
151
|
+
}
|
|
152
|
+
console.log(`\n📦 Found ${pendingMigrations.length} pending migration(s)\n`);
|
|
153
|
+
const context = createContext(client, config, tracker);
|
|
154
|
+
const executed = [];
|
|
155
|
+
for (const migrationFile of pendingMigrations) {
|
|
156
|
+
try {
|
|
157
|
+
console.log(`⬆️ Applying ${migrationFile.version}: ${migrationFile.name}`);
|
|
158
|
+
await migrationFile.migration.up(context);
|
|
159
|
+
await tracker.markAsApplied(migrationFile.version, migrationFile.name, migrationFile.migration.schema, undefined, migrationFile.checksum);
|
|
160
|
+
console.log(`✅ Applied ${migrationFile.version}: ${migrationFile.name}\n`);
|
|
161
|
+
executed.push(migrationFile);
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
165
|
+
console.error(`❌ Failed to apply ${migrationFile.version}: ${message}\n`);
|
|
166
|
+
await tracker.markAsFailed(migrationFile.version, message);
|
|
167
|
+
throw new Error(`Migration ${migrationFile.version} failed: ${message}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return executed;
|
|
171
|
+
};
|
|
172
|
+
const runRollback = async (tracker, loader, client, config, steps, dryRun) => {
|
|
173
|
+
const appliedMigrations = await tracker.getAppliedMigrations();
|
|
174
|
+
// Full applied list, semver-descending. We need the whole thing (not
|
|
175
|
+
// just the slice we're about to roll back) so we can tell the tracker
|
|
176
|
+
// what version the CURRENT pointer should fall back to after each
|
|
177
|
+
// step — without making it re-Query the migration history every time.
|
|
178
|
+
const allApplied = appliedMigrations
|
|
179
|
+
.filter((m) => m.status === 'applied')
|
|
180
|
+
.sort((a, b) => (0, semver_1.compareSemver)(b.version, a.version));
|
|
181
|
+
const applied = allApplied.slice(0, steps);
|
|
182
|
+
if (applied.length === 0) {
|
|
183
|
+
console.log('✅ No migrations to rollback');
|
|
184
|
+
return [];
|
|
185
|
+
}
|
|
186
|
+
if (dryRun) {
|
|
187
|
+
console.log(`\n🔍 DRY RUN - Would rollback ${applied.length} migration(s):\n`);
|
|
188
|
+
applied.forEach((record) => console.log(` ${record.version}: ${record.name}`));
|
|
189
|
+
console.log('\nNo changes were made.\n');
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
console.log(`\n📦 Rolling back ${applied.length} migration(s)\n`);
|
|
193
|
+
// Load every migration once. The loader caches internally, but routing
|
|
194
|
+
// through a Map here keeps the hot loop free of per-call awaits.
|
|
195
|
+
const allMigrations = await loader.loadMigrations();
|
|
196
|
+
const migrationByVersion = new Map(allMigrations.map((m) => [m.version, m]));
|
|
197
|
+
const context = createContext(client, config, tracker);
|
|
198
|
+
const rolledBack = [];
|
|
199
|
+
for (const [i, record] of applied.entries()) {
|
|
200
|
+
try {
|
|
201
|
+
const migrationFile = migrationByVersion.get(record.version);
|
|
202
|
+
if (!migrationFile) {
|
|
203
|
+
throw new Error(`Migration file not found for version ${record.version}`);
|
|
204
|
+
}
|
|
205
|
+
console.log(`⬇️ Rolling back ${migrationFile.version}: ${migrationFile.name}`);
|
|
206
|
+
await migrationFile.migration.down(context);
|
|
207
|
+
// After rolling this one back, the new CURRENT is whatever applied
|
|
208
|
+
// version sits one slot deeper in `allApplied`. If we've consumed
|
|
209
|
+
// the whole list, fall back to v0000 (no schema applied).
|
|
210
|
+
const previousVersion = allApplied[i + 1]?.version ?? 'v0000';
|
|
211
|
+
await tracker.markAsRolledBack(migrationFile.version, previousVersion);
|
|
212
|
+
console.log(`✅ Rolled back ${migrationFile.version}: ${migrationFile.name}\n`);
|
|
213
|
+
rolledBack.push(migrationFile);
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
217
|
+
console.error(`❌ Failed to rollback ${record.version}: ${message}\n`);
|
|
218
|
+
await tracker.markAsFailed(record.version, message);
|
|
219
|
+
throw new Error(`Rollback of ${record.version} failed: ${message}`);
|
|
220
|
+
}
|
|
258
221
|
}
|
|
259
|
-
|
|
260
|
-
|
|
222
|
+
return rolledBack;
|
|
223
|
+
};
|
|
261
224
|
//# sourceMappingURL=runner.js.map
|
package/dist/core/runner.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/core/runner.ts"],"names":[],"mappings":";;;AACA,wDAW+B;AAE/B,
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/core/runner.ts"],"names":[],"mappings":"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"}
|
package/dist/core/tracker.d.ts
CHANGED
|
@@ -2,82 +2,27 @@ import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
|
2
2
|
import { MigrationTracker, MigrationRecord, SchemaChange, MigrationConfig } from '../types';
|
|
3
3
|
/** Default lock TTL in seconds. Configurable via MigrationConfig.lockTtlSeconds. */
|
|
4
4
|
export declare const DEFAULT_LOCK_TTL_SECONDS = 300;
|
|
5
|
-
export
|
|
6
|
-
private client;
|
|
7
|
-
private tableName;
|
|
8
|
-
private trackingPrefix;
|
|
9
|
-
private gsi1Name;
|
|
10
|
-
private lockId;
|
|
11
|
-
/** TTL for newly-acquired or refreshed locks. */
|
|
5
|
+
export interface MigrationTrackerHandle extends MigrationTracker {
|
|
12
6
|
readonly lockTtlSeconds: number;
|
|
13
|
-
constructor(client: DynamoDBDocumentClient, config: MigrationConfig);
|
|
14
|
-
private get lockKey();
|
|
15
|
-
/** Throws if there is no active lock. Call before any tracker write. */
|
|
16
|
-
private requireLock;
|
|
17
|
-
/** Builds a TransactWrite ConditionCheck that asserts we still own the lock. */
|
|
18
|
-
private lockOwnershipCheck;
|
|
19
|
-
initialize(): Promise<void>;
|
|
20
|
-
/**
|
|
21
|
-
* Acquire a distributed lock to prevent concurrent migrations
|
|
22
|
-
*/
|
|
23
7
|
acquireLock(): Promise<boolean>;
|
|
24
|
-
/**
|
|
25
|
-
* Extend the lock's expiration by another `lockTtlSeconds`. Throws
|
|
26
|
-
* `ConditionalCheckFailedException` if another worker has already taken
|
|
27
|
-
* the lock — callers should treat that as "we lost the race; stop
|
|
28
|
-
* making writes". Silent no-op if no lock is currently held.
|
|
29
|
-
*/
|
|
30
8
|
refreshLock(): Promise<void>;
|
|
31
|
-
/**
|
|
32
|
-
* Release the distributed lock
|
|
33
|
-
*/
|
|
34
9
|
releaseLock(): Promise<void>;
|
|
35
|
-
|
|
36
|
-
* Get all applied migrations with pagination support.
|
|
37
|
-
*
|
|
38
|
-
* Projects only the lightweight bookkeeping fields — the heavy
|
|
39
|
-
* `schemaDefinition` and `schemaChanges` blobs are intentionally omitted,
|
|
40
|
-
* because no internal caller reads them and including them blows up the
|
|
41
|
-
* 1MB-per-page budget (and consumed RCU) on tables with many migrations.
|
|
42
|
-
* If a future caller needs the full record, fetch by version with
|
|
43
|
-
* `getMigration(version)`.
|
|
44
|
-
*/
|
|
10
|
+
initialize(): Promise<void>;
|
|
45
11
|
getAppliedMigrations(): Promise<MigrationRecord[]>;
|
|
46
12
|
getCurrentVersion(): Promise<string | null>;
|
|
47
|
-
/**
|
|
48
|
-
* Mark migration as applied using TransactWrite for atomicity
|
|
49
|
-
* Handles both new migrations and re-applying rolled back migrations
|
|
50
|
-
*/
|
|
51
13
|
markAsApplied(version: string, name: string, schemaDefinition?: Record<string, any>, schemaChanges?: SchemaChange[], checksum?: string): Promise<void>;
|
|
52
|
-
/**
|
|
53
|
-
* Inspect a `TransactionCanceledException` raised by `markAsApplied` and
|
|
54
|
-
* either swallow it (idempotent re-apply) or re-throw a typed error.
|
|
55
|
-
*
|
|
56
|
-
* The TransactWrite items are: [lockOwnershipCheck, migrationRow, currentPointer].
|
|
57
|
-
* Each item produces an entry in `CancellationReasons`.
|
|
58
|
-
* - reasons[0] failing → the lock was lost → MigrationLockLostError.
|
|
59
|
-
* - reasons[1] failing → the migration row already exists in a state the
|
|
60
|
-
* write refused. Re-fetch the record:
|
|
61
|
-
* * status === 'applied' → idempotent re-apply, return silently.
|
|
62
|
-
* * other states → MigrationAlreadyAppliedError with the
|
|
63
|
-
* current state, so the caller can decide what to do.
|
|
64
|
-
*/
|
|
65
|
-
private handleMarkAsAppliedCancellation;
|
|
66
|
-
/**
|
|
67
|
-
* Mark migration as rolled back using TransactWrite for atomicity.
|
|
68
|
-
*
|
|
69
|
-
* @param previousVersion Optional. The version the CURRENT pointer should
|
|
70
|
-
* move to after this rollback. When the caller already knows it (the
|
|
71
|
-
* runner does — it loaded the applied list once at the top of `down()`),
|
|
72
|
-
* passing it here skips a `getAppliedMigrations()` Query per rollback.
|
|
73
|
-
* Without this hint, a multi-step rollback was N×(paginated Query of
|
|
74
|
-
* the entire migration history). When omitted, the tracker falls back
|
|
75
|
-
* to looking it up.
|
|
76
|
-
*/
|
|
77
14
|
markAsRolledBack(version: string, previousVersion?: string): Promise<void>;
|
|
78
15
|
markAsFailed(version: string, error: string): Promise<void>;
|
|
79
16
|
recordSchemaChange(change: SchemaChange): Promise<void>;
|
|
80
17
|
getMigration(version: string): Promise<MigrationRecord | null>;
|
|
81
18
|
isApplied(version: string): Promise<boolean>;
|
|
82
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Create a DynamoDB-backed migration tracker.
|
|
22
|
+
*
|
|
23
|
+
* The tracker carries one piece of genuinely mutable state: the current
|
|
24
|
+
* lock id, set by `acquireLock()` and cleared by `releaseLock()`. Everything
|
|
25
|
+
* else is derived from the config or read from DynamoDB on demand.
|
|
26
|
+
*/
|
|
27
|
+
export declare const createMigrationTracker: (client: DynamoDBDocumentClient, config: MigrationConfig) => MigrationTrackerHandle;
|
|
83
28
|
//# sourceMappingURL=tracker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../src/core/tracker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAS/D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAI5F,oFAAoF;AACpF,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,
|
|
1
|
+
{"version":3,"file":"tracker.d.ts","sourceRoot":"","sources":["../../src/core/tracker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAS/D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAI5F,oFAAoF;AACpF,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,MAAM,WAAW,sBAAuB,SAAQ,gBAAgB;IAC9D,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,oBAAoB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IACnD,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5C,aAAa,CACX,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EACZ,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACtC,aAAa,CAAC,EAAE,YAAY,EAAE,EAC9B,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;IAC/D,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9C;AAED;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,GACjC,QAAQ,sBAAsB,EAC9B,QAAQ,eAAe,KACtB,sBA4fF,CAAC"}
|