@syncular/migrations 0.0.6-56 → 0.0.6-66
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/dist/define.d.ts.map +1 -1
- package/dist/define.js +19 -3
- package/dist/define.js.map +1 -1
- package/dist/runner.d.ts +5 -1
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +86 -42
- package/dist/runner.js.map +1 -1
- package/dist/tracking.d.ts +4 -0
- package/dist/tracking.d.ts.map +1 -1
- package/dist/tracking.js +10 -0
- package/dist/tracking.js.map +1 -1
- package/dist/types.d.ts +34 -3
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/define.ts +29 -3
- package/src/runner.ts +114 -48
- package/src/tracking.ts +16 -0
- package/src/types.ts +44 -3
package/dist/define.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,iBAAiB,
|
|
1
|
+
{"version":3,"file":"define.d.ts","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,iBAAiB,EAGjB,eAAe,EACf,eAAe,EAChB,MAAM,SAAS,CAAC;AA8JjB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,CAC9B,EAAE,GAAG,OAAO,EACZ,CAAC,SAAS,eAAe,CAAC,EAAE,CAAC,GAAG,eAAe,CAAC,EAAE,CAAC,EACnD,mBAAmB,EAAE,CAAC,GAAG,iBAAiB,CAAC,EAAE,CAAC,CA6D/C;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EACrC,SAAS,EAAE,eAAe,CAAC,EAAE,CAAC,GAC7B,MAAM,CAER"}
|
package/dist/define.js
CHANGED
|
@@ -133,6 +133,9 @@ function computeChecksum(fn) {
|
|
|
133
133
|
}
|
|
134
134
|
return hash.toString(16).padStart(8, '0');
|
|
135
135
|
}
|
|
136
|
+
function isMigrationDefinitionObject(value) {
|
|
137
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
138
|
+
}
|
|
136
139
|
/**
|
|
137
140
|
* Define versioned migrations with automatic version parsing and sorting.
|
|
138
141
|
*
|
|
@@ -155,7 +158,7 @@ function computeChecksum(fn) {
|
|
|
155
158
|
*/
|
|
156
159
|
export function defineMigrations(versionedMigrations) {
|
|
157
160
|
const migrations = [];
|
|
158
|
-
for (const [key,
|
|
161
|
+
for (const [key, definition] of Object.entries(versionedMigrations)) {
|
|
159
162
|
const version = parseVersionKey(key);
|
|
160
163
|
if (version === null) {
|
|
161
164
|
throw new Error(`Invalid migration key "${key}": must be a version number (e.g., 'v1', 'v2', '1', '2')`);
|
|
@@ -163,10 +166,23 @@ export function defineMigrations(versionedMigrations) {
|
|
|
163
166
|
if (version < 1) {
|
|
164
167
|
throw new Error(`Invalid migration version ${version}: versions must be >= 1`);
|
|
165
168
|
}
|
|
169
|
+
const up = isMigrationDefinitionObject(definition)
|
|
170
|
+
? definition.up
|
|
171
|
+
: definition;
|
|
172
|
+
const down = isMigrationDefinitionObject(definition)
|
|
173
|
+
? definition.down
|
|
174
|
+
: undefined;
|
|
175
|
+
if (typeof up !== 'function') {
|
|
176
|
+
throw new Error(`Invalid migration "${key}": expected an async function or { up, down? } object.`);
|
|
177
|
+
}
|
|
178
|
+
if (down !== undefined && typeof down !== 'function') {
|
|
179
|
+
throw new Error(`Invalid migration "${key}": "down" must be a function when provided.`);
|
|
180
|
+
}
|
|
166
181
|
migrations.push({
|
|
167
182
|
version,
|
|
168
183
|
name: key,
|
|
169
|
-
|
|
184
|
+
up,
|
|
185
|
+
down,
|
|
170
186
|
});
|
|
171
187
|
}
|
|
172
188
|
// Sort by version number
|
|
@@ -190,6 +206,6 @@ export function defineMigrations(versionedMigrations) {
|
|
|
190
206
|
* Get the checksum for a migration.
|
|
191
207
|
*/
|
|
192
208
|
export function getMigrationChecksum(migration) {
|
|
193
|
-
return computeChecksum(migration.
|
|
209
|
+
return computeChecksum(migration.up);
|
|
194
210
|
}
|
|
195
211
|
//# sourceMappingURL=define.js.map
|
package/dist/define.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"define.js","sourceRoot":"","sources":["../src/define.ts"],"names":[],"mappings":"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"}
|
package/dist/runner.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @syncular/migrations - Migration runner
|
|
3
3
|
*/
|
|
4
|
-
import type { RunMigrationsOptions, RunMigrationsResult } from './types';
|
|
4
|
+
import type { RunMigrationsOptions, RunMigrationsResult, RunMigrationsToVersionOptions, RunMigrationsToVersionResult } from './types';
|
|
5
5
|
/**
|
|
6
6
|
* Run pending migrations and track their state.
|
|
7
7
|
*
|
|
@@ -25,6 +25,10 @@ import type { RunMigrationsOptions, RunMigrationsResult } from './types';
|
|
|
25
25
|
* ```
|
|
26
26
|
*/
|
|
27
27
|
export declare function runMigrations<DB>(options: RunMigrationsOptions<DB>): Promise<RunMigrationsResult>;
|
|
28
|
+
/**
|
|
29
|
+
* Migrate to an explicit target version, supporting both up and down paths.
|
|
30
|
+
*/
|
|
31
|
+
export declare function runMigrationsToVersion<DB>(options: RunMigrationsToVersionOptions<DB>): Promise<RunMigrationsToVersionResult>;
|
|
28
32
|
/**
|
|
29
33
|
* Get the current schema version without running any migrations.
|
|
30
34
|
*/
|
package/dist/runner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA;;GAEG;AAUH,OAAO,KAAK,EACV,oBAAoB,EACpB,mBAAmB,EACnB,6BAA6B,EAC7B,4BAA4B,EAC7B,MAAM,SAAS,CAAC;AAwCjB;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,aAAa,CAAC,EAAE,EACpC,OAAO,EAAE,oBAAoB,CAAC,EAAE,CAAC,GAChC,OAAO,CAAC,mBAAmB,CAAC,CAU9B;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,EAAE,EAC7C,OAAO,EAAE,6BAA6B,CAAC,EAAE,CAAC,GACzC,OAAO,CAAC,4BAA4B,CAAC,CAgJvC;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,EAAE,EACvC,EAAE,EAAE,OAAO,QAAQ,EAAE,MAAM,CAAC,EAAE,CAAC,EAC/B,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,MAAM,CAAC,CAKjB"}
|
package/dist/runner.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @syncular/migrations - Migration runner
|
|
3
3
|
*/
|
|
4
4
|
import { getMigrationChecksum } from './define.js';
|
|
5
|
-
import { clearAppliedMigrations, ensureTrackingTable, getAppliedMigrations, recordAppliedMigration, } from './tracking.js';
|
|
5
|
+
import { clearAppliedMigrations, ensureTrackingTable, getAppliedMigrations, recordAppliedMigration, removeAppliedMigration, } from './tracking.js';
|
|
6
6
|
const DEFAULT_TRACKING_TABLE = 'sync_migration_state';
|
|
7
7
|
const migrationRunQueues = new Map();
|
|
8
8
|
function toErrorMessage(error) {
|
|
@@ -55,10 +55,30 @@ async function runWithMigrationQueue(queueKey, task) {
|
|
|
55
55
|
* ```
|
|
56
56
|
*/
|
|
57
57
|
export async function runMigrations(options) {
|
|
58
|
-
const
|
|
58
|
+
const result = await runMigrationsToVersion({
|
|
59
|
+
...options,
|
|
60
|
+
targetVersion: options.migrations.currentVersion,
|
|
61
|
+
});
|
|
62
|
+
return {
|
|
63
|
+
applied: result.applied,
|
|
64
|
+
currentVersion: result.currentVersion,
|
|
65
|
+
wasReset: result.wasReset,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Migrate to an explicit target version, supporting both up and down paths.
|
|
70
|
+
*/
|
|
71
|
+
export async function runMigrationsToVersion(options) {
|
|
72
|
+
const { db, migrations, targetVersion } = options;
|
|
59
73
|
const trackingTable = options.trackingTable ?? DEFAULT_TRACKING_TABLE;
|
|
60
74
|
const onChecksumMismatch = options.onChecksumMismatch ?? 'error';
|
|
61
75
|
const beforeReset = options.beforeReset;
|
|
76
|
+
if (!Number.isInteger(targetVersion) || targetVersion < 0) {
|
|
77
|
+
throw new Error(`Invalid target version ${targetVersion}. Target version must be an integer >= 0.`);
|
|
78
|
+
}
|
|
79
|
+
if (targetVersion > migrations.currentVersion) {
|
|
80
|
+
throw new Error(`Invalid target version ${targetVersion}. Maximum defined version is ${migrations.currentVersion}.`);
|
|
81
|
+
}
|
|
62
82
|
// Serialize migration runs per tracking table to avoid duplicate CREATE TABLE
|
|
63
83
|
// races when startup paths invoke migrations concurrently (e.g. React StrictMode).
|
|
64
84
|
return runWithMigrationQueue(`tracking:${trackingTable}`, async () => {
|
|
@@ -68,6 +88,7 @@ export async function runMigrations(options) {
|
|
|
68
88
|
let applied = await getAppliedMigrations(db, trackingTable);
|
|
69
89
|
let appliedByVersion = new Map(applied.map((m) => [m.version, m]));
|
|
70
90
|
const appliedVersions = [];
|
|
91
|
+
const revertedVersions = [];
|
|
71
92
|
let wasReset = false;
|
|
72
93
|
let recoveredFromSchemaConflict = false;
|
|
73
94
|
// Check for checksum mismatches up-front when reset mode is enabled
|
|
@@ -76,7 +97,7 @@ export async function runMigrations(options) {
|
|
|
76
97
|
const existing = appliedByVersion.get(migration.version);
|
|
77
98
|
if (!existing)
|
|
78
99
|
return false;
|
|
79
|
-
return
|
|
100
|
+
return getMigrationChecksum(migration) !== existing.checksum;
|
|
80
101
|
});
|
|
81
102
|
if (hasMismatch) {
|
|
82
103
|
// Let caller drop application tables first
|
|
@@ -89,54 +110,77 @@ export async function runMigrations(options) {
|
|
|
89
110
|
appliedByVersion = new Map(applied.map((m) => [m.version, m]));
|
|
90
111
|
}
|
|
91
112
|
}
|
|
92
|
-
for (
|
|
93
|
-
const migration = migrations.migrations[index];
|
|
113
|
+
for (const migration of migrations.migrations) {
|
|
94
114
|
const existing = appliedByVersion.get(migration.version);
|
|
95
|
-
if (existing) {
|
|
96
|
-
// Migration already applied - verify checksum hasn't changed
|
|
97
|
-
const currentChecksum = getMigrationChecksum(migration);
|
|
98
|
-
if (existing.checksum !== currentChecksum) {
|
|
99
|
-
throw new Error(`Migration v${migration.version} (${migration.name}) has changed since it was applied. ` +
|
|
100
|
-
`Expected checksum ${existing.checksum}, got ${currentChecksum}. ` +
|
|
101
|
-
'Migrations must not be modified after being applied.');
|
|
102
|
-
}
|
|
115
|
+
if (!existing) {
|
|
103
116
|
continue;
|
|
104
117
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
118
|
+
const currentChecksum = getMigrationChecksum(migration);
|
|
119
|
+
if (currentChecksum !== existing.checksum) {
|
|
120
|
+
throw new Error(`Migration v${migration.version} (${migration.name}) has changed since it was applied. ` +
|
|
121
|
+
`Stored checksum ${existing.checksum} is not compatible with current checksum ${currentChecksum}. ` +
|
|
122
|
+
'Migrations must not be modified after being applied.');
|
|
108
123
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (
|
|
115
|
-
|
|
124
|
+
}
|
|
125
|
+
const currentVersion = applied.length > 0 ? applied[applied.length - 1].version : 0;
|
|
126
|
+
if (targetVersion > currentVersion) {
|
|
127
|
+
for (let index = 0; index < migrations.migrations.length; index += 1) {
|
|
128
|
+
const migration = migrations.migrations[index];
|
|
129
|
+
if (migration.version <= currentVersion) {
|
|
130
|
+
continue;
|
|
116
131
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
132
|
+
if (migration.version > targetVersion) {
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
await migration.up(db);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
const canRecoverFromConflict = onChecksumMismatch === 'reset' &&
|
|
140
|
+
typeof beforeReset === 'function' &&
|
|
141
|
+
!recoveredFromSchemaConflict &&
|
|
142
|
+
isAlreadyExistsSchemaError(error);
|
|
143
|
+
if (!canRecoverFromConflict) {
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
// Recover once from partially-applied state where app tables exist
|
|
147
|
+
// but migration tracking rows were not committed.
|
|
148
|
+
await beforeReset(db);
|
|
149
|
+
await clearAppliedMigrations(db, trackingTable);
|
|
150
|
+
wasReset = true;
|
|
151
|
+
recoveredFromSchemaConflict = true;
|
|
152
|
+
applied = await getAppliedMigrations(db, trackingTable);
|
|
153
|
+
appliedByVersion = new Map(applied.map((m) => [m.version, m]));
|
|
154
|
+
appliedVersions.length = 0;
|
|
155
|
+
index = -1;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
await recordAppliedMigration(db, trackingTable, {
|
|
159
|
+
version: migration.version,
|
|
160
|
+
name: migration.name,
|
|
161
|
+
checksum: getMigrationChecksum(migration),
|
|
162
|
+
});
|
|
163
|
+
appliedVersions.push(migration.version);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
else if (targetVersion < currentVersion) {
|
|
167
|
+
for (let version = currentVersion; version > targetVersion; version -= 1) {
|
|
168
|
+
const migration = migrations.getMigration(version);
|
|
169
|
+
if (!migration) {
|
|
170
|
+
throw new Error(`Cannot revert migration v${version}: migration is not defined in current migration set.`);
|
|
171
|
+
}
|
|
172
|
+
if (typeof migration.down !== 'function') {
|
|
173
|
+
throw new Error(`Cannot revert migration v${version} (${migration.name}): down migration is not defined.`);
|
|
174
|
+
}
|
|
175
|
+
await migration.down(db);
|
|
176
|
+
await removeAppliedMigration(db, trackingTable, version);
|
|
177
|
+
revertedVersions.push(version);
|
|
128
178
|
}
|
|
129
|
-
// Record it as applied
|
|
130
|
-
await recordAppliedMigration(db, trackingTable, {
|
|
131
|
-
version: migration.version,
|
|
132
|
-
name: migration.name,
|
|
133
|
-
checksum: getMigrationChecksum(migration),
|
|
134
|
-
});
|
|
135
|
-
appliedVersions.push(migration.version);
|
|
136
179
|
}
|
|
137
180
|
return {
|
|
138
181
|
applied: appliedVersions,
|
|
139
|
-
|
|
182
|
+
reverted: revertedVersions,
|
|
183
|
+
currentVersion: targetVersion,
|
|
140
184
|
wasReset,
|
|
141
185
|
};
|
|
142
186
|
});
|
package/dist/runner.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
|
package/dist/tracking.d.ts
CHANGED
|
@@ -15,6 +15,10 @@ export declare function getAppliedMigrations<DB, TTableName extends string>(db:
|
|
|
15
15
|
* Record a migration as applied in the tracking table.
|
|
16
16
|
*/
|
|
17
17
|
export declare function recordAppliedMigration<DB, TTableName extends string>(db: Kysely<DB>, tableName: TTableName, migration: Omit<MigrationStateRow, 'applied_at'>): Promise<void>;
|
|
18
|
+
/**
|
|
19
|
+
* Remove one migration row from the tracking table.
|
|
20
|
+
*/
|
|
21
|
+
export declare function removeAppliedMigration<DB, TTableName extends string>(db: Kysely<DB>, tableName: TTableName, version: number): Promise<void>;
|
|
18
22
|
/**
|
|
19
23
|
* Clear all rows from the migration tracking table.
|
|
20
24
|
* Used when resetting the database after a checksum mismatch.
|
package/dist/tracking.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracking.d.ts","sourceRoot":"","sources":["../src/tracking.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,MAAM,EAAO,MAAM,QAAQ,CAAC;AAC1C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,EAAE,EAC1C,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,EAAE,EAAE,UAAU,SAAS,MAAM,EACtE,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EACd,SAAS,EAAE,UAAU,GACpB,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAU9B;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,EAAE,EAAE,UAAU,SAAS,MAAM,EACxE,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EACd,SAAS,EAAE,UAAU,EACrB,SAAS,EAAE,IAAI,CAAC,iBAAiB,EAAE,YAAY,CAAC,GAC/C,OAAO,CAAC,IAAI,CAAC,CAYf;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,EAAE,EAC7C,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAEf;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,UAAU,SAAS,MAAM,EACnE,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EACd,SAAS,EAAE,UAAU,GACpB,OAAO,CAAC,MAAM,CAAC,CAIjB"}
|
|
1
|
+
{"version":3,"file":"tracking.d.ts","sourceRoot":"","sources":["../src/tracking.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,KAAK,MAAM,EAAO,MAAM,QAAQ,CAAC;AAC1C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,EAAE,EAC1C,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CAAC,EAAE,EAAE,UAAU,SAAS,MAAM,EACtE,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EACd,SAAS,EAAE,UAAU,GACpB,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAU9B;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,EAAE,EAAE,UAAU,SAAS,MAAM,EACxE,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EACd,SAAS,EAAE,UAAU,EACrB,SAAS,EAAE,IAAI,CAAC,iBAAiB,EAAE,YAAY,CAAC,GAC/C,OAAO,CAAC,IAAI,CAAC,CAYf;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,EAAE,EAAE,UAAU,SAAS,MAAM,EACxE,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EACd,SAAS,EAAE,UAAU,EACrB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,EAAE,EAC7C,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAEf;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CAAC,EAAE,EAAE,UAAU,SAAS,MAAM,EACnE,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,EACd,SAAS,EAAE,UAAU,GACpB,OAAO,CAAC,MAAM,CAAC,CAIjB"}
|
package/dist/tracking.js
CHANGED
|
@@ -42,6 +42,16 @@ export async function recordAppliedMigration(db, tableName, migration) {
|
|
|
42
42
|
)
|
|
43
43
|
`.execute(db);
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Remove one migration row from the tracking table.
|
|
47
|
+
*/
|
|
48
|
+
export async function removeAppliedMigration(db, tableName, version) {
|
|
49
|
+
await ensureTrackingTable(db, tableName);
|
|
50
|
+
await sql `
|
|
51
|
+
delete from ${sql.table(tableName)}
|
|
52
|
+
where version = ${version}
|
|
53
|
+
`.execute(db);
|
|
54
|
+
}
|
|
45
55
|
/**
|
|
46
56
|
* Clear all rows from the migration tracking table.
|
|
47
57
|
* Used when resetting the database after a checksum mismatch.
|
package/dist/tracking.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracking.js","sourceRoot":"","sources":["../src/tracking.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAe,GAAG,EAAE,MAAM,QAAQ,CAAC;AAG1C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,EAAc,EACd,SAAiB,EACF;IACf,MAAM,EAAE,CAAC,MAAM;SACZ,WAAW,CAAC,SAAS,CAAC;SACtB,WAAW,EAAE;SACb,SAAS,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC;SAC1D,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACjD,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACvD,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACrD,OAAO,EAAE,CAAC;AAAA,CACd;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,EAAc,EACd,SAAqB,EACS;IAC9B,MAAM,mBAAmB,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IAEzC,MAAM,MAAM,GAAG,MAAM,GAAG,CAAmB;;WAElC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC;;GAE5B,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAEd,OAAO,MAAM,CAAC,IAAI,CAAC;AAAA,CACpB;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,EAAc,EACd,SAAqB,EACrB,SAAgD,EACjC;IACf,MAAM,mBAAmB,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IAEzC,MAAM,GAAG,CAAA;kBACO,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC;;QAE9B,SAAS,CAAC,OAAO;QACjB,SAAS,CAAC,IAAI;QACd,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACxB,SAAS,CAAC,QAAQ;;GAEvB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAAA,CACf;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,EAAc,EACd,SAAiB,EACF;IACf,MAAM,GAAG,CAAA,eAAe,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAAA,CAC5D;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,EAAc,EACd,SAAqB,EACJ;IACjB,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IAC1D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACnC,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,OAAO,CAAC;AAAA,CAC7C"}
|
|
1
|
+
{"version":3,"file":"tracking.js","sourceRoot":"","sources":["../src/tracking.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAe,GAAG,EAAE,MAAM,QAAQ,CAAC;AAG1C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,EAAc,EACd,SAAiB,EACF;IACf,MAAM,EAAE,CAAC,MAAM;SACZ,WAAW,CAAC,SAAS,CAAC;SACtB,WAAW,EAAE;SACb,SAAS,CAAC,SAAS,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC;SAC1D,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACjD,SAAS,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACvD,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACrD,OAAO,EAAE,CAAC;AAAA,CACd;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,EAAc,EACd,SAAqB,EACS;IAC9B,MAAM,mBAAmB,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IAEzC,MAAM,MAAM,GAAG,MAAM,GAAG,CAAmB;;WAElC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC;;GAE5B,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAEd,OAAO,MAAM,CAAC,IAAI,CAAC;AAAA,CACpB;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,EAAc,EACd,SAAqB,EACrB,SAAgD,EACjC;IACf,MAAM,mBAAmB,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IAEzC,MAAM,GAAG,CAAA;kBACO,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC;;QAE9B,SAAS,CAAC,OAAO;QACjB,SAAS,CAAC,IAAI;QACd,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACxB,SAAS,CAAC,QAAQ;;GAEvB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAAA,CACf;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,EAAc,EACd,SAAqB,EACrB,OAAe,EACA;IACf,MAAM,mBAAmB,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IAEzC,MAAM,GAAG,CAAA;kBACO,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC;sBAChB,OAAO;GAC1B,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAAA,CACf;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,EAAc,EACd,SAAiB,EACF;IACf,MAAM,GAAG,CAAA,eAAe,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAAA,CAC5D;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,EAAc,EACd,SAAqB,EACJ;IACjB,MAAM,OAAO,GAAG,MAAM,oBAAoB,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;IAC1D,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IACnC,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,OAAO,CAAC;AAAA,CAC7C"}
|
package/dist/types.d.ts
CHANGED
|
@@ -6,17 +6,34 @@ import type { Kysely } from 'kysely';
|
|
|
6
6
|
* A single migration function that modifies the database schema.
|
|
7
7
|
*/
|
|
8
8
|
export type MigrationFn<DB = unknown> = (db: Kysely<DB>) => Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* A reversible migration definition.
|
|
11
|
+
*/
|
|
12
|
+
export interface ReversibleMigrationDefinition<DB = unknown> {
|
|
13
|
+
/** Apply schema/data changes for this version. */
|
|
14
|
+
up: MigrationFn<DB>;
|
|
15
|
+
/** Revert schema/data changes for this version. */
|
|
16
|
+
down?: MigrationFn<DB>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A migration definition can be a single "up" function or
|
|
20
|
+
* an object with explicit up/down handlers.
|
|
21
|
+
*/
|
|
22
|
+
export type MigrationDefinition<DB = unknown> = MigrationFn<DB> | ReversibleMigrationDefinition<DB>;
|
|
9
23
|
/**
|
|
10
24
|
* Record of versioned migrations keyed by version string (e.g., 'v1', 'v2').
|
|
11
25
|
*/
|
|
12
|
-
export type MigrationRecord<DB = unknown> = Record<string,
|
|
26
|
+
export type MigrationRecord<DB = unknown> = Record<string, MigrationDefinition<DB>>;
|
|
13
27
|
/**
|
|
14
|
-
* Parsed migration with version number and
|
|
28
|
+
* Parsed migration with version number and handlers.
|
|
15
29
|
*/
|
|
16
30
|
export interface ParsedMigration<DB = unknown> {
|
|
17
31
|
version: number;
|
|
18
32
|
name: string;
|
|
19
|
-
|
|
33
|
+
/** Up migration function. */
|
|
34
|
+
up: MigrationFn<DB>;
|
|
35
|
+
/** Optional down migration function. */
|
|
36
|
+
down?: MigrationFn<DB>;
|
|
20
37
|
}
|
|
21
38
|
/**
|
|
22
39
|
* Result of defineMigrations() - contains migrations and metadata.
|
|
@@ -65,4 +82,18 @@ export interface RunMigrationsResult {
|
|
|
65
82
|
/** True if a checksum mismatch triggered a full reset */
|
|
66
83
|
wasReset: boolean;
|
|
67
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Options for migrating to an explicit target version.
|
|
87
|
+
*/
|
|
88
|
+
export interface RunMigrationsToVersionOptions<DB = unknown> extends RunMigrationsOptions<DB> {
|
|
89
|
+
/** Target schema version (0..migrations.currentVersion). */
|
|
90
|
+
targetVersion: number;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Result of migrating to an explicit target version.
|
|
94
|
+
*/
|
|
95
|
+
export interface RunMigrationsToVersionResult extends RunMigrationsResult {
|
|
96
|
+
/** Versions that were reverted in this run, highest to lowest. */
|
|
97
|
+
reverted: number[];
|
|
98
|
+
}
|
|
68
99
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,EAAE,GAAG,OAAO,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1E;;GAEG;AACH,MAAM,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,EAAE,GAAG,OAAO,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1E;;GAEG;AACH,MAAM,WAAW,6BAA6B,CAAC,EAAE,GAAG,OAAO;IACzD,kDAAkD;IAClD,EAAE,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IACpB,mDAAmD;IACnD,IAAI,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,MAAM,mBAAmB,CAAC,EAAE,GAAG,OAAO,IACxC,WAAW,CAAC,EAAE,CAAC,GACf,6BAA6B,CAAC,EAAE,CAAC,CAAC;AAEtC;;GAEG;AACH,MAAM,MAAM,eAAe,CAAC,EAAE,GAAG,OAAO,IAAI,MAAM,CAChD,MAAM,EACN,mBAAmB,CAAC,EAAE,CAAC,CACxB,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,eAAe,CAAC,EAAE,GAAG,OAAO;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,EAAE,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;IACpB,wCAAwC;IACxC,IAAI,CAAC,EAAE,WAAW,CAAC,EAAE,CAAC,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB,CAAC,EAAE,GAAG,OAAO;IAC7C,gCAAgC;IAChC,UAAU,EAAE,eAAe,CAAC,EAAE,CAAC,EAAE,CAAC;IAClC,sCAAsC;IACtC,cAAc,EAAE,MAAM,CAAC;IACvB,sCAAsC;IACtC,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC;CAChE;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB,CAAC,EAAE,GAAG,OAAO;IAChD,+BAA+B;IAC/B,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACf,iDAAiD;IACjD,UAAU,EAAE,iBAAiB,CAAC,EAAE,CAAC,CAAC;IAClC,mEAAmE;IACnE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6EAA6E;IAC7E,kBAAkB,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IACvC;+EAC2E;IAC3E,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,6CAA6C;IAC7C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,6CAA6C;IAC7C,cAAc,EAAE,MAAM,CAAC;IACvB,yDAAyD;IACzD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,6BAA6B,CAAC,EAAE,GAAG,OAAO,CACzD,SAAQ,oBAAoB,CAAC,EAAE,CAAC;IAChC,4DAA4D;IAC5D,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,4BAA6B,SAAQ,mBAAmB;IACvE,kEAAkE;IAClE,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB"}
|
package/package.json
CHANGED
package/src/define.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import type {
|
|
6
6
|
DefinedMigrations,
|
|
7
|
+
MigrationDefinition,
|
|
7
8
|
MigrationFn,
|
|
8
9
|
MigrationRecord,
|
|
9
10
|
ParsedMigration,
|
|
@@ -159,6 +160,12 @@ function computeChecksum<DB>(fn: MigrationFn<DB>): string {
|
|
|
159
160
|
return hash.toString(16).padStart(8, '0');
|
|
160
161
|
}
|
|
161
162
|
|
|
163
|
+
function isMigrationDefinitionObject<DB>(
|
|
164
|
+
value: MigrationDefinition<DB>
|
|
165
|
+
): value is { up: MigrationFn<DB>; down?: MigrationFn<DB> } {
|
|
166
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
167
|
+
}
|
|
168
|
+
|
|
162
169
|
/**
|
|
163
170
|
* Define versioned migrations with automatic version parsing and sorting.
|
|
164
171
|
*
|
|
@@ -185,7 +192,7 @@ export function defineMigrations<
|
|
|
185
192
|
>(versionedMigrations: T): DefinedMigrations<DB> {
|
|
186
193
|
const migrations: ParsedMigration<DB>[] = [];
|
|
187
194
|
|
|
188
|
-
for (const [key,
|
|
195
|
+
for (const [key, definition] of Object.entries(versionedMigrations)) {
|
|
189
196
|
const version = parseVersionKey(key);
|
|
190
197
|
if (version === null) {
|
|
191
198
|
throw new Error(
|
|
@@ -197,10 +204,29 @@ export function defineMigrations<
|
|
|
197
204
|
`Invalid migration version ${version}: versions must be >= 1`
|
|
198
205
|
);
|
|
199
206
|
}
|
|
207
|
+
|
|
208
|
+
const up = isMigrationDefinitionObject(definition)
|
|
209
|
+
? definition.up
|
|
210
|
+
: definition;
|
|
211
|
+
const down = isMigrationDefinitionObject(definition)
|
|
212
|
+
? definition.down
|
|
213
|
+
: undefined;
|
|
214
|
+
if (typeof up !== 'function') {
|
|
215
|
+
throw new Error(
|
|
216
|
+
`Invalid migration "${key}": expected an async function or { up, down? } object.`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
if (down !== undefined && typeof down !== 'function') {
|
|
220
|
+
throw new Error(
|
|
221
|
+
`Invalid migration "${key}": "down" must be a function when provided.`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
200
225
|
migrations.push({
|
|
201
226
|
version,
|
|
202
227
|
name: key,
|
|
203
|
-
|
|
228
|
+
up,
|
|
229
|
+
down,
|
|
204
230
|
});
|
|
205
231
|
}
|
|
206
232
|
|
|
@@ -232,5 +258,5 @@ export function defineMigrations<
|
|
|
232
258
|
export function getMigrationChecksum<DB>(
|
|
233
259
|
migration: ParsedMigration<DB>
|
|
234
260
|
): string {
|
|
235
|
-
return computeChecksum(migration.
|
|
261
|
+
return computeChecksum(migration.up);
|
|
236
262
|
}
|
package/src/runner.ts
CHANGED
|
@@ -8,8 +8,14 @@ import {
|
|
|
8
8
|
ensureTrackingTable,
|
|
9
9
|
getAppliedMigrations,
|
|
10
10
|
recordAppliedMigration,
|
|
11
|
+
removeAppliedMigration,
|
|
11
12
|
} from './tracking';
|
|
12
|
-
import type {
|
|
13
|
+
import type {
|
|
14
|
+
RunMigrationsOptions,
|
|
15
|
+
RunMigrationsResult,
|
|
16
|
+
RunMigrationsToVersionOptions,
|
|
17
|
+
RunMigrationsToVersionResult,
|
|
18
|
+
} from './types';
|
|
13
19
|
|
|
14
20
|
const DEFAULT_TRACKING_TABLE = 'sync_migration_state';
|
|
15
21
|
const migrationRunQueues = new Map<string, Promise<void>>();
|
|
@@ -74,10 +80,37 @@ async function runWithMigrationQueue<T>(
|
|
|
74
80
|
export async function runMigrations<DB>(
|
|
75
81
|
options: RunMigrationsOptions<DB>
|
|
76
82
|
): Promise<RunMigrationsResult> {
|
|
77
|
-
const
|
|
83
|
+
const result = await runMigrationsToVersion({
|
|
84
|
+
...options,
|
|
85
|
+
targetVersion: options.migrations.currentVersion,
|
|
86
|
+
});
|
|
87
|
+
return {
|
|
88
|
+
applied: result.applied,
|
|
89
|
+
currentVersion: result.currentVersion,
|
|
90
|
+
wasReset: result.wasReset,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Migrate to an explicit target version, supporting both up and down paths.
|
|
96
|
+
*/
|
|
97
|
+
export async function runMigrationsToVersion<DB>(
|
|
98
|
+
options: RunMigrationsToVersionOptions<DB>
|
|
99
|
+
): Promise<RunMigrationsToVersionResult> {
|
|
100
|
+
const { db, migrations, targetVersion } = options;
|
|
78
101
|
const trackingTable = options.trackingTable ?? DEFAULT_TRACKING_TABLE;
|
|
79
102
|
const onChecksumMismatch = options.onChecksumMismatch ?? 'error';
|
|
80
103
|
const beforeReset = options.beforeReset;
|
|
104
|
+
if (!Number.isInteger(targetVersion) || targetVersion < 0) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
`Invalid target version ${targetVersion}. Target version must be an integer >= 0.`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
if (targetVersion > migrations.currentVersion) {
|
|
110
|
+
throw new Error(
|
|
111
|
+
`Invalid target version ${targetVersion}. Maximum defined version is ${migrations.currentVersion}.`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
81
114
|
|
|
82
115
|
// Serialize migration runs per tracking table to avoid duplicate CREATE TABLE
|
|
83
116
|
// races when startup paths invoke migrations concurrently (e.g. React StrictMode).
|
|
@@ -90,6 +123,7 @@ export async function runMigrations<DB>(
|
|
|
90
123
|
let appliedByVersion = new Map(applied.map((m) => [m.version, m]));
|
|
91
124
|
|
|
92
125
|
const appliedVersions: number[] = [];
|
|
126
|
+
const revertedVersions: number[] = [];
|
|
93
127
|
let wasReset = false;
|
|
94
128
|
let recoveredFromSchemaConflict = false;
|
|
95
129
|
|
|
@@ -98,7 +132,7 @@ export async function runMigrations<DB>(
|
|
|
98
132
|
const hasMismatch = migrations.migrations.some((migration) => {
|
|
99
133
|
const existing = appliedByVersion.get(migration.version);
|
|
100
134
|
if (!existing) return false;
|
|
101
|
-
return
|
|
135
|
+
return getMigrationChecksum(migration) !== existing.checksum;
|
|
102
136
|
});
|
|
103
137
|
|
|
104
138
|
if (hasMismatch) {
|
|
@@ -114,63 +148,95 @@ export async function runMigrations<DB>(
|
|
|
114
148
|
}
|
|
115
149
|
}
|
|
116
150
|
|
|
117
|
-
for (
|
|
118
|
-
const migration = migrations.migrations[index]!;
|
|
151
|
+
for (const migration of migrations.migrations) {
|
|
119
152
|
const existing = appliedByVersion.get(migration.version);
|
|
120
|
-
|
|
121
|
-
if (existing) {
|
|
122
|
-
// Migration already applied - verify checksum hasn't changed
|
|
123
|
-
const currentChecksum = getMigrationChecksum(migration);
|
|
124
|
-
if (existing.checksum !== currentChecksum) {
|
|
125
|
-
throw new Error(
|
|
126
|
-
`Migration v${migration.version} (${migration.name}) has changed since it was applied. ` +
|
|
127
|
-
`Expected checksum ${existing.checksum}, got ${currentChecksum}. ` +
|
|
128
|
-
'Migrations must not be modified after being applied.'
|
|
129
|
-
);
|
|
130
|
-
}
|
|
153
|
+
if (!existing) {
|
|
131
154
|
continue;
|
|
132
155
|
}
|
|
156
|
+
const currentChecksum = getMigrationChecksum(migration);
|
|
157
|
+
if (currentChecksum !== existing.checksum) {
|
|
158
|
+
throw new Error(
|
|
159
|
+
`Migration v${migration.version} (${migration.name}) has changed since it was applied. ` +
|
|
160
|
+
`Stored checksum ${existing.checksum} is not compatible with current checksum ${currentChecksum}. ` +
|
|
161
|
+
'Migrations must not be modified after being applied.'
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
133
165
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
throw error;
|
|
166
|
+
const currentVersion =
|
|
167
|
+
applied.length > 0 ? applied[applied.length - 1]!.version : 0;
|
|
168
|
+
|
|
169
|
+
if (targetVersion > currentVersion) {
|
|
170
|
+
for (let index = 0; index < migrations.migrations.length; index += 1) {
|
|
171
|
+
const migration = migrations.migrations[index]!;
|
|
172
|
+
if (migration.version <= currentVersion) {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (migration.version > targetVersion) {
|
|
176
|
+
break;
|
|
146
177
|
}
|
|
147
178
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
appliedVersions.length = 0;
|
|
157
|
-
index = -1;
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
179
|
+
try {
|
|
180
|
+
await migration.up(db);
|
|
181
|
+
} catch (error) {
|
|
182
|
+
const canRecoverFromConflict =
|
|
183
|
+
onChecksumMismatch === 'reset' &&
|
|
184
|
+
typeof beforeReset === 'function' &&
|
|
185
|
+
!recoveredFromSchemaConflict &&
|
|
186
|
+
isAlreadyExistsSchemaError(error);
|
|
160
187
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
188
|
+
if (!canRecoverFromConflict) {
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Recover once from partially-applied state where app tables exist
|
|
193
|
+
// but migration tracking rows were not committed.
|
|
194
|
+
await beforeReset(db);
|
|
195
|
+
await clearAppliedMigrations(db, trackingTable);
|
|
196
|
+
wasReset = true;
|
|
197
|
+
recoveredFromSchemaConflict = true;
|
|
198
|
+
applied = await getAppliedMigrations(db, trackingTable);
|
|
199
|
+
appliedByVersion = new Map(applied.map((m) => [m.version, m]));
|
|
200
|
+
appliedVersions.length = 0;
|
|
201
|
+
index = -1;
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
await recordAppliedMigration(db, trackingTable, {
|
|
206
|
+
version: migration.version,
|
|
207
|
+
name: migration.name,
|
|
208
|
+
checksum: getMigrationChecksum(migration),
|
|
209
|
+
});
|
|
210
|
+
appliedVersions.push(migration.version);
|
|
211
|
+
}
|
|
212
|
+
} else if (targetVersion < currentVersion) {
|
|
213
|
+
for (
|
|
214
|
+
let version = currentVersion;
|
|
215
|
+
version > targetVersion;
|
|
216
|
+
version -= 1
|
|
217
|
+
) {
|
|
218
|
+
const migration = migrations.getMigration(version);
|
|
219
|
+
if (!migration) {
|
|
220
|
+
throw new Error(
|
|
221
|
+
`Cannot revert migration v${version}: migration is not defined in current migration set.`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
if (typeof migration.down !== 'function') {
|
|
225
|
+
throw new Error(
|
|
226
|
+
`Cannot revert migration v${version} (${migration.name}): down migration is not defined.`
|
|
227
|
+
);
|
|
228
|
+
}
|
|
167
229
|
|
|
168
|
-
|
|
230
|
+
await migration.down(db);
|
|
231
|
+
await removeAppliedMigration(db, trackingTable, version);
|
|
232
|
+
revertedVersions.push(version);
|
|
233
|
+
}
|
|
169
234
|
}
|
|
170
235
|
|
|
171
236
|
return {
|
|
172
237
|
applied: appliedVersions,
|
|
173
|
-
|
|
238
|
+
reverted: revertedVersions,
|
|
239
|
+
currentVersion: targetVersion,
|
|
174
240
|
wasReset,
|
|
175
241
|
};
|
|
176
242
|
});
|
package/src/tracking.ts
CHANGED
|
@@ -61,6 +61,22 @@ export async function recordAppliedMigration<DB, TTableName extends string>(
|
|
|
61
61
|
`.execute(db);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Remove one migration row from the tracking table.
|
|
66
|
+
*/
|
|
67
|
+
export async function removeAppliedMigration<DB, TTableName extends string>(
|
|
68
|
+
db: Kysely<DB>,
|
|
69
|
+
tableName: TTableName,
|
|
70
|
+
version: number
|
|
71
|
+
): Promise<void> {
|
|
72
|
+
await ensureTrackingTable(db, tableName);
|
|
73
|
+
|
|
74
|
+
await sql`
|
|
75
|
+
delete from ${sql.table(tableName)}
|
|
76
|
+
where version = ${version}
|
|
77
|
+
`.execute(db);
|
|
78
|
+
}
|
|
79
|
+
|
|
64
80
|
/**
|
|
65
81
|
* Clear all rows from the migration tracking table.
|
|
66
82
|
* Used when resetting the database after a checksum mismatch.
|
package/src/types.ts
CHANGED
|
@@ -9,18 +9,42 @@ import type { Kysely } from 'kysely';
|
|
|
9
9
|
*/
|
|
10
10
|
export type MigrationFn<DB = unknown> = (db: Kysely<DB>) => Promise<void>;
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* A reversible migration definition.
|
|
14
|
+
*/
|
|
15
|
+
export interface ReversibleMigrationDefinition<DB = unknown> {
|
|
16
|
+
/** Apply schema/data changes for this version. */
|
|
17
|
+
up: MigrationFn<DB>;
|
|
18
|
+
/** Revert schema/data changes for this version. */
|
|
19
|
+
down?: MigrationFn<DB>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A migration definition can be a single "up" function or
|
|
24
|
+
* an object with explicit up/down handlers.
|
|
25
|
+
*/
|
|
26
|
+
export type MigrationDefinition<DB = unknown> =
|
|
27
|
+
| MigrationFn<DB>
|
|
28
|
+
| ReversibleMigrationDefinition<DB>;
|
|
29
|
+
|
|
12
30
|
/**
|
|
13
31
|
* Record of versioned migrations keyed by version string (e.g., 'v1', 'v2').
|
|
14
32
|
*/
|
|
15
|
-
export type MigrationRecord<DB = unknown> = Record<
|
|
33
|
+
export type MigrationRecord<DB = unknown> = Record<
|
|
34
|
+
string,
|
|
35
|
+
MigrationDefinition<DB>
|
|
36
|
+
>;
|
|
16
37
|
|
|
17
38
|
/**
|
|
18
|
-
* Parsed migration with version number and
|
|
39
|
+
* Parsed migration with version number and handlers.
|
|
19
40
|
*/
|
|
20
41
|
export interface ParsedMigration<DB = unknown> {
|
|
21
42
|
version: number;
|
|
22
43
|
name: string;
|
|
23
|
-
|
|
44
|
+
/** Up migration function. */
|
|
45
|
+
up: MigrationFn<DB>;
|
|
46
|
+
/** Optional down migration function. */
|
|
47
|
+
down?: MigrationFn<DB>;
|
|
24
48
|
}
|
|
25
49
|
|
|
26
50
|
/**
|
|
@@ -73,3 +97,20 @@ export interface RunMigrationsResult {
|
|
|
73
97
|
/** True if a checksum mismatch triggered a full reset */
|
|
74
98
|
wasReset: boolean;
|
|
75
99
|
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Options for migrating to an explicit target version.
|
|
103
|
+
*/
|
|
104
|
+
export interface RunMigrationsToVersionOptions<DB = unknown>
|
|
105
|
+
extends RunMigrationsOptions<DB> {
|
|
106
|
+
/** Target schema version (0..migrations.currentVersion). */
|
|
107
|
+
targetVersion: number;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Result of migrating to an explicit target version.
|
|
112
|
+
*/
|
|
113
|
+
export interface RunMigrationsToVersionResult extends RunMigrationsResult {
|
|
114
|
+
/** Versions that were reverted in this run, highest to lowest. */
|
|
115
|
+
reverted: number[];
|
|
116
|
+
}
|