@nextlyhq/adapter-drizzle 0.0.2-alpha.7 → 0.0.2-alpha.70

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.
Files changed (64) hide show
  1. package/README.md +25 -1
  2. package/dist/adapter-BypoOEEN.d.cts +1166 -0
  3. package/dist/adapter-CSP-Ek-L.d.ts +1166 -0
  4. package/dist/index-BYb6cDli.d.ts +1168 -0
  5. package/dist/index-C4HJsfwA.d.cts +1168 -0
  6. package/dist/index.cjs +2302 -1085
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.d.cts +5 -9
  9. package/dist/index.d.ts +5 -9
  10. package/dist/index.mjs +2301 -1083
  11. package/dist/index.mjs.map +1 -1
  12. package/dist/migrations.cjs +345 -146
  13. package/dist/migrations.cjs.map +1 -1
  14. package/dist/migrations.d.cts +93 -109
  15. package/dist/migrations.d.ts +93 -109
  16. package/dist/migrations.mjs +344 -144
  17. package/dist/migrations.mjs.map +1 -1
  18. package/dist/schema/index.cjs +21 -6
  19. package/dist/schema/index.cjs.map +1 -1
  20. package/dist/schema/index.d.cts +5 -116
  21. package/dist/schema/index.d.ts +5 -116
  22. package/dist/schema/index.mjs +20 -4
  23. package/dist/schema/index.mjs.map +1 -1
  24. package/dist/schema-Bkkr3JMC.d.cts +264 -0
  25. package/dist/schema-Bkkr3JMC.d.ts +264 -0
  26. package/dist/schema-name.cjs +42 -0
  27. package/dist/schema-name.cjs.map +1 -0
  28. package/dist/schema-name.d.cts +12 -0
  29. package/dist/schema-name.d.ts +12 -0
  30. package/dist/schema-name.mjs +40 -0
  31. package/dist/schema-name.mjs.map +1 -0
  32. package/dist/text-match.cjs +50 -0
  33. package/dist/text-match.cjs.map +1 -0
  34. package/dist/text-match.d.cts +32 -0
  35. package/dist/text-match.d.ts +32 -0
  36. package/dist/text-match.mjs +48 -0
  37. package/dist/text-match.mjs.map +1 -0
  38. package/dist/types/index.cjs +8 -24
  39. package/dist/types/index.d.cts +3 -210
  40. package/dist/types/index.d.ts +3 -210
  41. package/dist/types/index.mjs +1 -21
  42. package/dist/types-CNyA1oIm.cjs +185 -0
  43. package/dist/types-CNyA1oIm.cjs.map +1 -0
  44. package/dist/types-CaF_5jNk.mjs +150 -0
  45. package/dist/types-CaF_5jNk.mjs.map +1 -0
  46. package/dist/version-check.cjs +119 -127
  47. package/dist/version-check.cjs.map +1 -1
  48. package/dist/version-check.d.cts +54 -39
  49. package/dist/version-check.d.ts +54 -39
  50. package/dist/version-check.mjs +117 -125
  51. package/dist/version-check.mjs.map +1 -1
  52. package/package.json +23 -17
  53. package/dist/adapter-BxJVtttb.d.ts +0 -592
  54. package/dist/adapter-nvlxFkF-.d.cts +0 -592
  55. package/dist/core-CVO7WYDj.d.cts +0 -74
  56. package/dist/core-CVO7WYDj.d.ts +0 -74
  57. package/dist/error-um1d_3Uo.d.cts +0 -105
  58. package/dist/error-um1d_3Uo.d.ts +0 -105
  59. package/dist/migration-BbO5meEV.d.cts +0 -622
  60. package/dist/migration-Qe70wDOC.d.ts +0 -622
  61. package/dist/schema-BDn8WfSL.d.cts +0 -200
  62. package/dist/schema-BIQ0YQZ_.d.ts +0 -200
  63. package/dist/types/index.cjs.map +0 -1
  64. package/dist/types/index.mjs.map +0 -1
@@ -1,120 +1,323 @@
1
- import { createHash } from 'crypto';
2
-
1
+ import { createHash } from "crypto";
3
2
  // src/migrations.ts
3
+ /**
4
+ * Database migration utilities.
5
+ *
6
+ * @remarks
7
+ * Provides core utilities for migration management including checksum calculation,
8
+ * sorting, filtering, and validation. Also includes optional helpers for adapters
9
+ * to use for common migration operations.
10
+ *
11
+ * @packageDocumentation
12
+ */
13
+ /**
14
+ * Calculate SHA-256 checksum for a migration.
15
+ *
16
+ * @remarks
17
+ * Creates a checksum of the entire migration object including id, name, timestamp,
18
+ * and the string representation of up/down functions. This allows detection of
19
+ * any changes to the migration after it has been applied.
20
+ *
21
+ * @param migration - Migration to calculate checksum for
22
+ * @returns Hexadecimal SHA-256 hash
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * const migration: Migration = {
27
+ * id: "20250104_001_create_users",
28
+ * name: "Create users table",
29
+ * timestamp: 1704326400000,
30
+ * up: "CREATE TABLE users (id UUID PRIMARY KEY);",
31
+ * };
32
+ *
33
+ * const checksum = calculateChecksum(migration);
34
+ * // Returns: "a3f5b8c9d2e1f0..."
35
+ * ```
36
+ *
37
+ * @public
38
+ */
4
39
  function calculateChecksum(migration) {
5
- const content = JSON.stringify({
6
- id: migration.id,
7
- name: migration.name,
8
- timestamp: migration.timestamp,
9
- up: typeof migration.up === "string" ? migration.up : migration.up.toString(),
10
- down: typeof migration.down === "string" ? migration.down : migration.down?.toString() || null
11
- });
12
- return createHash("sha256").update(content, "utf8").digest("hex");
40
+ const content = JSON.stringify({
41
+ id: migration.id,
42
+ name: migration.name,
43
+ timestamp: migration.timestamp,
44
+ up: typeof migration.up === "string" ? migration.up : migration.up.toString(),
45
+ down: typeof migration.down === "string" ? migration.down : migration.down?.toString() || null
46
+ });
47
+ return createHash("sha256").update(content, "utf8").digest("hex");
13
48
  }
49
+ /**
50
+ * Sort migrations by timestamp in ascending order.
51
+ *
52
+ * @remarks
53
+ * Provides deterministic ordering of migrations. If timestamps are equal,
54
+ * falls back to sorting by migration ID alphabetically.
55
+ *
56
+ * @param migrations - Array of migrations to sort
57
+ * @returns New sorted array (original array is not modified)
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * const migrations = [
62
+ * { id: "003", timestamp: 1704412800000, ... },
63
+ * { id: "001", timestamp: 1704326400000, ... },
64
+ * { id: "002", timestamp: 1704326400000, ... },
65
+ * ];
66
+ *
67
+ * const sorted = sortMigrations(migrations);
68
+ * // Returns: [001, 002, 003] (001 and 002 same timestamp, sorted by id)
69
+ * ```
70
+ *
71
+ * @public
72
+ */
14
73
  function sortMigrations(migrations) {
15
- return [...migrations].sort((a, b) => {
16
- if (a.timestamp !== b.timestamp) {
17
- return a.timestamp - b.timestamp;
18
- }
19
- return a.id.localeCompare(b.id);
20
- });
74
+ return [...migrations].sort((a, b) => {
75
+ if (a.timestamp !== b.timestamp) return a.timestamp - b.timestamp;
76
+ return a.id.localeCompare(b.id);
77
+ });
21
78
  }
79
+ /**
80
+ * Filter migrations to get only pending (unapplied) migrations.
81
+ *
82
+ * @remarks
83
+ * Returns migrations that have not been applied to the database yet.
84
+ * A migration is considered pending if its ID is not present in the
85
+ * applied migration records.
86
+ *
87
+ * @param migrations - All available migrations
88
+ * @param applied - Records of applied migrations from database
89
+ * @returns Array of pending migrations
90
+ *
91
+ * @example
92
+ * ```typescript
93
+ * const allMigrations = [
94
+ * { id: "001_create_users", ... },
95
+ * { id: "002_create_posts", ... },
96
+ * { id: "003_create_comments", ... },
97
+ * ];
98
+ *
99
+ * const appliedRecords = [
100
+ * { id: "001_create_users", appliedAt: new Date(), ... },
101
+ * ];
102
+ *
103
+ * const pending = filterPending(allMigrations, appliedRecords);
104
+ * // Returns: [002_create_posts, 003_create_comments]
105
+ * ```
106
+ *
107
+ * @public
108
+ */
22
109
  function filterPending(migrations, applied) {
23
- const appliedIds = new Set(applied.map((record) => record.id));
24
- return migrations.filter((migration) => !appliedIds.has(migration.id));
110
+ const appliedIds = new Set(applied.map((record) => record.id));
111
+ return migrations.filter((migration) => !appliedIds.has(migration.id));
25
112
  }
113
+ /**
114
+ * Filter migration records to get only those that match given migrations.
115
+ *
116
+ * @remarks
117
+ * Returns applied migration records that correspond to the given migrations.
118
+ * Useful for getting the subset of applied records relevant to a specific
119
+ * set of migrations.
120
+ *
121
+ * @param migrations - Migrations to match
122
+ * @param applied - All applied migration records from database
123
+ * @returns Array of matching applied records
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * const migrations = [
128
+ * { id: "001_create_users", ... },
129
+ * { id: "002_create_posts", ... },
130
+ * ];
131
+ *
132
+ * const allApplied = [
133
+ * { id: "001_create_users", appliedAt: new Date(), ... },
134
+ * { id: "002_create_posts", appliedAt: new Date(), ... },
135
+ * { id: "003_create_comments", appliedAt: new Date(), ... },
136
+ * ];
137
+ *
138
+ * const relevant = filterApplied(migrations, allApplied);
139
+ * // Returns: [001_create_users, 002_create_posts]
140
+ * ```
141
+ *
142
+ * @public
143
+ */
26
144
  function filterApplied(migrations, applied) {
27
- const migrationIds = new Set(migrations.map((migration) => migration.id));
28
- return applied.filter((record) => migrationIds.has(record.id));
145
+ const migrationIds = new Set(migrations.map((migration) => migration.id));
146
+ return applied.filter((record) => migrationIds.has(record.id));
29
147
  }
148
+ /**
149
+ * Validate that a migration's checksum matches its applied record.
150
+ *
151
+ * @remarks
152
+ * Compares the calculated checksum of a migration against the stored checksum
153
+ * in its migration record. Returns false if checksums don't match or if the
154
+ * record has no checksum.
155
+ *
156
+ * @param migration - Migration to validate
157
+ * @param record - Applied migration record with stored checksum
158
+ * @returns true if checksums match, false otherwise
159
+ *
160
+ * @example
161
+ * ```typescript
162
+ * const migration: Migration = { id: "001", ... };
163
+ * const record: MigrationRecord = {
164
+ * id: "001",
165
+ * checksum: "a3f5b8c9...",
166
+ * ...
167
+ * };
168
+ *
169
+ * const isValid = validateChecksum(migration, record);
170
+ * // Returns: true if checksums match, false if modified
171
+ * ```
172
+ *
173
+ * @public
174
+ */
30
175
  function validateChecksum(migration, record) {
31
- if (!record.checksum) {
32
- return false;
33
- }
34
- const currentChecksum = calculateChecksum(migration);
35
- return currentChecksum === record.checksum;
176
+ if (!record.checksum) return false;
177
+ return calculateChecksum(migration) === record.checksum;
36
178
  }
179
+ /**
180
+ * Detect migrations that have been modified after being applied.
181
+ *
182
+ * @remarks
183
+ * Compares checksums of migrations against their applied records to detect
184
+ * any modifications. Returns records for migrations that have been changed
185
+ * since they were applied to the database.
186
+ *
187
+ * This is critical for detecting potentially dangerous situations where a
188
+ * migration that has already run has been modified.
189
+ *
190
+ * @param migrations - Current migrations
191
+ * @param applied - Applied migration records with checksums
192
+ * @returns Array of records for modified migrations
193
+ *
194
+ * @example
195
+ * ```typescript
196
+ * const migrations = [{ id: "001", up: "CREATE TABLE users_v2 ..." }];
197
+ * const applied = [{ id: "001", checksum: "original_hash", ... }];
198
+ *
199
+ * const modified = detectModified(migrations, applied);
200
+ * // Returns: [{ id: "001", ... }] if migration was changed
201
+ * ```
202
+ *
203
+ * @public
204
+ */
37
205
  function detectModified(migrations, applied) {
38
- const migrationMap = new Map(migrations.map((m) => [m.id, m]));
39
- const modified = [];
40
- for (const record of applied) {
41
- const migration = migrationMap.get(record.id);
42
- if (migration && !validateChecksum(migration, record)) {
43
- modified.push(record);
44
- }
45
- }
46
- return modified;
206
+ const migrationMap = new Map(migrations.map((m) => [m.id, m]));
207
+ const modified = [];
208
+ for (const record of applied) {
209
+ const migration = migrationMap.get(record.id);
210
+ if (migration && !validateChecksum(migration, record)) modified.push(record);
211
+ }
212
+ return modified;
47
213
  }
214
+ /**
215
+ * Get comprehensive migration status.
216
+ *
217
+ * @remarks
218
+ * Analyzes migrations and applied records to produce a complete status report
219
+ * including current migration, applied/pending counts, and full lists.
220
+ *
221
+ * @param migrations - All available migrations
222
+ * @param applied - Applied migration records from database
223
+ * @returns Complete migration status information
224
+ *
225
+ * @example
226
+ * ```typescript
227
+ * const status = getMigrationStatus(allMigrations, appliedRecords);
228
+ * console.log(`Current: ${status.current}`);
229
+ * console.log(`Applied: ${status.appliedCount}, Pending: ${status.pendingCount}`);
230
+ * ```
231
+ *
232
+ * @public
233
+ */
48
234
  function getMigrationStatus(migrations, applied) {
49
- const sortedMigrations = sortMigrations(migrations);
50
- const sortedApplied = [...applied].sort(
51
- (a, b) => b.appliedAt.getTime() - a.appliedAt.getTime()
52
- );
53
- const pending = filterPending(sortedMigrations, applied);
54
- const current = sortedApplied.length > 0 ? sortedApplied[0].id : null;
55
- return {
56
- current,
57
- appliedCount: applied.length,
58
- pendingCount: pending.length,
59
- applied,
60
- pending
61
- };
235
+ const sortedMigrations = sortMigrations(migrations);
236
+ const sortedApplied = [...applied].sort((a, b) => b.appliedAt.getTime() - a.appliedAt.getTime());
237
+ const pending = filterPending(sortedMigrations, applied);
238
+ return {
239
+ current: sortedApplied.length > 0 ? sortedApplied[0].id : null,
240
+ appliedCount: applied.length,
241
+ pendingCount: pending.length,
242
+ applied,
243
+ pending
244
+ };
62
245
  }
246
+ /**
247
+ * Validate migrations for common issues.
248
+ *
249
+ * @remarks
250
+ * Performs comprehensive validation including:
251
+ * - Duplicate migration IDs
252
+ * - Modified applied migrations (checksum mismatch)
253
+ * - Missing required fields
254
+ * - Invalid timestamps
255
+ *
256
+ * @param migrations - Migrations to validate
257
+ * @param applied - Applied migration records (for checksum validation)
258
+ * @param options - Migration options (for strictness configuration)
259
+ * @returns Validation result with errors and warnings
260
+ *
261
+ * @example
262
+ * ```typescript
263
+ * const result = validateMigrations(migrations, applied, {
264
+ * strictChecksums: true
265
+ * });
266
+ *
267
+ * if (!result.valid) {
268
+ * console.error("Validation errors:", result.errors);
269
+ * throw new Error("Migration validation failed");
270
+ * }
271
+ * ```
272
+ *
273
+ * @public
274
+ */
63
275
  function validateMigrations(migrations, applied, options) {
64
- const errors = [];
65
- const warnings = [];
66
- const duplicates = [];
67
- const idCounts = /* @__PURE__ */ new Map();
68
- for (const migration of migrations) {
69
- const count = (idCounts.get(migration.id) || 0) + 1;
70
- idCounts.set(migration.id, count);
71
- if (count > 1) {
72
- duplicates.push(migration.id);
73
- }
74
- }
75
- if (duplicates.length > 0) {
76
- errors.push(`Duplicate migration IDs found: ${duplicates.join(", ")}`);
77
- }
78
- for (const migration of migrations) {
79
- if (!migration.id) {
80
- errors.push("Migration missing required field: id");
81
- }
82
- if (!migration.name) {
83
- errors.push(`Migration ${migration.id} missing required field: name`);
84
- }
85
- if (!migration.timestamp || typeof migration.timestamp !== "number") {
86
- errors.push(`Migration ${migration.id} missing or invalid timestamp`);
87
- }
88
- if (!migration.up) {
89
- errors.push(`Migration ${migration.id} missing required field: up`);
90
- }
91
- }
92
- const modified = detectModified(migrations, applied);
93
- if (modified.length > 0) {
94
- const modifiedIds = modified.map((r) => r.id).join(", ");
95
- const message = `Applied migrations have been modified: ${modifiedIds}`;
96
- const strictChecksums = options?.strictChecksums ?? true;
97
- if (strictChecksums) {
98
- errors.push(message);
99
- } else {
100
- warnings.push(message);
101
- }
102
- }
103
- return {
104
- valid: errors.length === 0,
105
- errors,
106
- warnings,
107
- modified,
108
- duplicates
109
- };
276
+ const errors = [];
277
+ const warnings = [];
278
+ const duplicates = [];
279
+ const idCounts = /* @__PURE__ */ new Map();
280
+ for (const migration of migrations) {
281
+ const count = (idCounts.get(migration.id) || 0) + 1;
282
+ idCounts.set(migration.id, count);
283
+ if (count > 1) duplicates.push(migration.id);
284
+ }
285
+ if (duplicates.length > 0) errors.push(`Duplicate migration IDs found: ${duplicates.join(", ")}`);
286
+ for (const migration of migrations) {
287
+ if (!migration.id) errors.push("Migration missing required field: id");
288
+ if (!migration.name) errors.push(`Migration ${migration.id} missing required field: name`);
289
+ if (!migration.timestamp || typeof migration.timestamp !== "number") errors.push(`Migration ${migration.id} missing or invalid timestamp`);
290
+ if (!migration.up) errors.push(`Migration ${migration.id} missing required field: up`);
291
+ }
292
+ const modified = detectModified(migrations, applied);
293
+ if (modified.length > 0) {
294
+ const message = `Applied migrations have been modified: ${modified.map((r) => r.id).join(", ")}`;
295
+ if (options?.strictChecksums ?? true) errors.push(message);
296
+ else warnings.push(message);
297
+ }
298
+ return {
299
+ valid: errors.length === 0,
300
+ errors,
301
+ warnings,
302
+ modified,
303
+ duplicates
304
+ };
110
305
  }
111
- var migrationHelpers = {
112
- async createMigrationsTable(adapter) {
113
- const caps = adapter.getCapabilities();
114
- const dialect = caps.dialect;
115
- let sql = "";
116
- if (dialect === "postgresql") {
117
- sql = `
306
+ /**
307
+ * Implementation of migration helpers.
308
+ *
309
+ * @remarks
310
+ * Provides default implementations using the adapter's query methods.
311
+ * These work across all dialects but adapters can optimize for their
312
+ * specific database if needed.
313
+ *
314
+ * @public
315
+ */
316
+ const migrationHelpers = {
317
+ async createMigrationsTable(adapter) {
318
+ const dialect = adapter.getCapabilities().dialect;
319
+ let sql = "";
320
+ if (dialect === "postgresql") sql = `
118
321
  CREATE TABLE IF NOT EXISTS nextly_migrations (
119
322
  id VARCHAR(255) PRIMARY KEY,
120
323
  name VARCHAR(255) NOT NULL,
@@ -122,8 +325,7 @@ var migrationHelpers = {
122
325
  checksum VARCHAR(64)
123
326
  )
124
327
  `;
125
- } else if (dialect === "mysql") {
126
- sql = `
328
+ else if (dialect === "mysql") sql = `
127
329
  CREATE TABLE IF NOT EXISTS nextly_migrations (
128
330
  id VARCHAR(255) PRIMARY KEY,
129
331
  name VARCHAR(255) NOT NULL,
@@ -131,8 +333,7 @@ var migrationHelpers = {
131
333
  checksum VARCHAR(64)
132
334
  )
133
335
  `;
134
- } else if (dialect === "sqlite") {
135
- sql = `
336
+ else if (dialect === "sqlite") sql = `
136
337
  CREATE TABLE IF NOT EXISTS nextly_migrations (
137
338
  id TEXT PRIMARY KEY,
138
339
  name TEXT NOT NULL,
@@ -140,46 +341,45 @@ var migrationHelpers = {
140
341
  checksum TEXT
141
342
  )
142
343
  `;
143
- } else {
144
- throw new Error(`Unsupported dialect: ${String(dialect)}`);
145
- }
146
- await adapter.executeQuery(sql);
147
- },
148
- async getAppliedMigrations(adapter) {
149
- const results = await adapter.select("nextly_migrations", {
150
- orderBy: [{ column: "applied_at", direction: "desc" }]
151
- });
152
- return results.map((row) => ({
153
- id: row.id,
154
- name: row.name,
155
- appliedAt: new Date(row.applied_at),
156
- checksum: row.checksum
157
- }));
158
- },
159
- async recordMigration(adapter, migration) {
160
- const checksum = calculateChecksum(migration);
161
- await adapter.insert("nextly_migrations", {
162
- id: migration.id,
163
- name: migration.name,
164
- applied_at: /* @__PURE__ */ new Date(),
165
- checksum
166
- });
167
- },
168
- async removeMigrationRecord(adapter, migrationId) {
169
- await adapter.delete("nextly_migrations", {
170
- and: [{ column: "id", op: "=", value: migrationId }]
171
- });
172
- },
173
- async migrationsTableExists(adapter) {
174
- try {
175
- await adapter.select("nextly_migrations", { limit: 1 });
176
- return true;
177
- } catch {
178
- return false;
179
- }
180
- }
344
+ else throw new Error(`Unsupported dialect: ${String(dialect)}`);
345
+ await adapter.executeQuery(sql);
346
+ },
347
+ async getAppliedMigrations(adapter) {
348
+ return (await adapter.select("nextly_migrations", { orderBy: [{
349
+ column: "applied_at",
350
+ direction: "desc"
351
+ }] })).map((row) => ({
352
+ id: row.id,
353
+ name: row.name,
354
+ appliedAt: new Date(row.applied_at),
355
+ checksum: row.checksum
356
+ }));
357
+ },
358
+ async recordMigration(adapter, migration) {
359
+ const checksum = calculateChecksum(migration);
360
+ await adapter.insert("nextly_migrations", {
361
+ id: migration.id,
362
+ name: migration.name,
363
+ applied_at: /* @__PURE__ */ new Date(),
364
+ checksum
365
+ });
366
+ },
367
+ async removeMigrationRecord(adapter, migrationId) {
368
+ await adapter.delete("nextly_migrations", { and: [{
369
+ column: "id",
370
+ op: "=",
371
+ value: migrationId
372
+ }] });
373
+ },
374
+ async migrationsTableExists(adapter) {
375
+ try {
376
+ await adapter.select("nextly_migrations", { limit: 1 });
377
+ return true;
378
+ } catch {
379
+ return false;
380
+ }
381
+ }
181
382
  };
182
-
183
383
  export { calculateChecksum, detectModified, filterApplied, filterPending, getMigrationStatus, migrationHelpers, sortMigrations, validateChecksum, validateMigrations };
184
- //# sourceMappingURL=migrations.mjs.map
384
+
185
385
  //# sourceMappingURL=migrations.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/migrations.ts"],"names":[],"mappings":"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","file":"migrations.mjs","sourcesContent":["/**\n * Database migration utilities.\n *\n * @remarks\n * Provides core utilities for migration management including checksum calculation,\n * sorting, filtering, and validation. Also includes optional helpers for adapters\n * to use for common migration operations.\n *\n * @packageDocumentation\n */\n\nimport { createHash } from \"crypto\";\n\nimport type { DrizzleAdapter } from \"./adapter\";\nimport type {\n Migration,\n MigrationRecord,\n MigrationStatus,\n MigrationOptions,\n} from \"./types/migration\";\n\n// ============================================================\n// Core Utilities\n// ============================================================\n\n/**\n * Calculate SHA-256 checksum for a migration.\n *\n * @remarks\n * Creates a checksum of the entire migration object including id, name, timestamp,\n * and the string representation of up/down functions. This allows detection of\n * any changes to the migration after it has been applied.\n *\n * @param migration - Migration to calculate checksum for\n * @returns Hexadecimal SHA-256 hash\n *\n * @example\n * ```typescript\n * const migration: Migration = {\n * id: \"20250104_001_create_users\",\n * name: \"Create users table\",\n * timestamp: 1704326400000,\n * up: \"CREATE TABLE users (id UUID PRIMARY KEY);\",\n * };\n *\n * const checksum = calculateChecksum(migration);\n * // Returns: \"a3f5b8c9d2e1f0...\"\n * ```\n *\n * @public\n */\nexport function calculateChecksum(migration: Migration): string {\n // Serialize migration to a consistent string format\n const content = JSON.stringify({\n id: migration.id,\n name: migration.name,\n timestamp: migration.timestamp,\n up:\n typeof migration.up === \"string\" ? migration.up : migration.up.toString(),\n down:\n typeof migration.down === \"string\"\n ? migration.down\n : migration.down?.toString() || null,\n });\n\n // Create SHA-256 hash\n return createHash(\"sha256\").update(content, \"utf8\").digest(\"hex\");\n}\n\n/**\n * Sort migrations by timestamp in ascending order.\n *\n * @remarks\n * Provides deterministic ordering of migrations. If timestamps are equal,\n * falls back to sorting by migration ID alphabetically.\n *\n * @param migrations - Array of migrations to sort\n * @returns New sorted array (original array is not modified)\n *\n * @example\n * ```typescript\n * const migrations = [\n * { id: \"003\", timestamp: 1704412800000, ... },\n * { id: \"001\", timestamp: 1704326400000, ... },\n * { id: \"002\", timestamp: 1704326400000, ... },\n * ];\n *\n * const sorted = sortMigrations(migrations);\n * // Returns: [001, 002, 003] (001 and 002 same timestamp, sorted by id)\n * ```\n *\n * @public\n */\nexport function sortMigrations(migrations: Migration[]): Migration[] {\n return [...migrations].sort((a, b) => {\n // Primary sort: timestamp ascending\n if (a.timestamp !== b.timestamp) {\n return a.timestamp - b.timestamp;\n }\n // Secondary sort: id alphabetically (for deterministic ordering)\n return a.id.localeCompare(b.id);\n });\n}\n\n/**\n * Filter migrations to get only pending (unapplied) migrations.\n *\n * @remarks\n * Returns migrations that have not been applied to the database yet.\n * A migration is considered pending if its ID is not present in the\n * applied migration records.\n *\n * @param migrations - All available migrations\n * @param applied - Records of applied migrations from database\n * @returns Array of pending migrations\n *\n * @example\n * ```typescript\n * const allMigrations = [\n * { id: \"001_create_users\", ... },\n * { id: \"002_create_posts\", ... },\n * { id: \"003_create_comments\", ... },\n * ];\n *\n * const appliedRecords = [\n * { id: \"001_create_users\", appliedAt: new Date(), ... },\n * ];\n *\n * const pending = filterPending(allMigrations, appliedRecords);\n * // Returns: [002_create_posts, 003_create_comments]\n * ```\n *\n * @public\n */\nexport function filterPending(\n migrations: Migration[],\n applied: MigrationRecord[]\n): Migration[] {\n const appliedIds = new Set(applied.map(record => record.id));\n return migrations.filter(migration => !appliedIds.has(migration.id));\n}\n\n/**\n * Filter migration records to get only those that match given migrations.\n *\n * @remarks\n * Returns applied migration records that correspond to the given migrations.\n * Useful for getting the subset of applied records relevant to a specific\n * set of migrations.\n *\n * @param migrations - Migrations to match\n * @param applied - All applied migration records from database\n * @returns Array of matching applied records\n *\n * @example\n * ```typescript\n * const migrations = [\n * { id: \"001_create_users\", ... },\n * { id: \"002_create_posts\", ... },\n * ];\n *\n * const allApplied = [\n * { id: \"001_create_users\", appliedAt: new Date(), ... },\n * { id: \"002_create_posts\", appliedAt: new Date(), ... },\n * { id: \"003_create_comments\", appliedAt: new Date(), ... },\n * ];\n *\n * const relevant = filterApplied(migrations, allApplied);\n * // Returns: [001_create_users, 002_create_posts]\n * ```\n *\n * @public\n */\nexport function filterApplied(\n migrations: Migration[],\n applied: MigrationRecord[]\n): MigrationRecord[] {\n const migrationIds = new Set(migrations.map(migration => migration.id));\n return applied.filter(record => migrationIds.has(record.id));\n}\n\n/**\n * Validate that a migration's checksum matches its applied record.\n *\n * @remarks\n * Compares the calculated checksum of a migration against the stored checksum\n * in its migration record. Returns false if checksums don't match or if the\n * record has no checksum.\n *\n * @param migration - Migration to validate\n * @param record - Applied migration record with stored checksum\n * @returns true if checksums match, false otherwise\n *\n * @example\n * ```typescript\n * const migration: Migration = { id: \"001\", ... };\n * const record: MigrationRecord = {\n * id: \"001\",\n * checksum: \"a3f5b8c9...\",\n * ...\n * };\n *\n * const isValid = validateChecksum(migration, record);\n * // Returns: true if checksums match, false if modified\n * ```\n *\n * @public\n */\nexport function validateChecksum(\n migration: Migration,\n record: MigrationRecord\n): boolean {\n if (!record.checksum) {\n // No checksum stored, cannot validate\n return false;\n }\n\n const currentChecksum = calculateChecksum(migration);\n return currentChecksum === record.checksum;\n}\n\n/**\n * Detect migrations that have been modified after being applied.\n *\n * @remarks\n * Compares checksums of migrations against their applied records to detect\n * any modifications. Returns records for migrations that have been changed\n * since they were applied to the database.\n *\n * This is critical for detecting potentially dangerous situations where a\n * migration that has already run has been modified.\n *\n * @param migrations - Current migrations\n * @param applied - Applied migration records with checksums\n * @returns Array of records for modified migrations\n *\n * @example\n * ```typescript\n * const migrations = [{ id: \"001\", up: \"CREATE TABLE users_v2 ...\" }];\n * const applied = [{ id: \"001\", checksum: \"original_hash\", ... }];\n *\n * const modified = detectModified(migrations, applied);\n * // Returns: [{ id: \"001\", ... }] if migration was changed\n * ```\n *\n * @public\n */\nexport function detectModified(\n migrations: Migration[],\n applied: MigrationRecord[]\n): MigrationRecord[] {\n const migrationMap = new Map(migrations.map(m => [m.id, m]));\n const modified: MigrationRecord[] = [];\n\n for (const record of applied) {\n const migration = migrationMap.get(record.id);\n if (migration && !validateChecksum(migration, record)) {\n modified.push(record);\n }\n }\n\n return modified;\n}\n\n/**\n * Get comprehensive migration status.\n *\n * @remarks\n * Analyzes migrations and applied records to produce a complete status report\n * including current migration, applied/pending counts, and full lists.\n *\n * @param migrations - All available migrations\n * @param applied - Applied migration records from database\n * @returns Complete migration status information\n *\n * @example\n * ```typescript\n * const status = getMigrationStatus(allMigrations, appliedRecords);\n * console.log(`Current: ${status.current}`);\n * console.log(`Applied: ${status.appliedCount}, Pending: ${status.pendingCount}`);\n * ```\n *\n * @public\n */\nexport function getMigrationStatus(\n migrations: Migration[],\n applied: MigrationRecord[]\n): MigrationStatus {\n const sortedMigrations = sortMigrations(migrations);\n const sortedApplied = [...applied].sort(\n (a, b) => b.appliedAt.getTime() - a.appliedAt.getTime()\n );\n\n const pending = filterPending(sortedMigrations, applied);\n const current = sortedApplied.length > 0 ? sortedApplied[0].id : null;\n\n return {\n current,\n appliedCount: applied.length,\n pendingCount: pending.length,\n applied,\n pending,\n };\n}\n\n// ============================================================\n// Migration Validation\n// ============================================================\n\n/**\n * Validation result for migrations.\n *\n * @public\n */\nexport interface MigrationValidationResult {\n /** Whether all validations passed */\n valid: boolean;\n\n /** List of validation errors */\n errors: string[];\n\n /** List of validation warnings */\n warnings: string[];\n\n /** Migrations that have been modified */\n modified: MigrationRecord[];\n\n /** Duplicate migration IDs found */\n duplicates: string[];\n}\n\n/**\n * Validate migrations for common issues.\n *\n * @remarks\n * Performs comprehensive validation including:\n * - Duplicate migration IDs\n * - Modified applied migrations (checksum mismatch)\n * - Missing required fields\n * - Invalid timestamps\n *\n * @param migrations - Migrations to validate\n * @param applied - Applied migration records (for checksum validation)\n * @param options - Migration options (for strictness configuration)\n * @returns Validation result with errors and warnings\n *\n * @example\n * ```typescript\n * const result = validateMigrations(migrations, applied, {\n * strictChecksums: true\n * });\n *\n * if (!result.valid) {\n * console.error(\"Validation errors:\", result.errors);\n * throw new Error(\"Migration validation failed\");\n * }\n * ```\n *\n * @public\n */\nexport function validateMigrations(\n migrations: Migration[],\n applied: MigrationRecord[],\n options?: MigrationOptions\n): MigrationValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n const duplicates: string[] = [];\n\n // Check for duplicate IDs\n const idCounts = new Map<string, number>();\n for (const migration of migrations) {\n const count = (idCounts.get(migration.id) || 0) + 1;\n idCounts.set(migration.id, count);\n if (count > 1) {\n duplicates.push(migration.id);\n }\n }\n\n if (duplicates.length > 0) {\n errors.push(`Duplicate migration IDs found: ${duplicates.join(\", \")}`);\n }\n\n // Validate required fields\n for (const migration of migrations) {\n if (!migration.id) {\n errors.push(\"Migration missing required field: id\");\n }\n if (!migration.name) {\n errors.push(`Migration ${migration.id} missing required field: name`);\n }\n if (!migration.timestamp || typeof migration.timestamp !== \"number\") {\n errors.push(`Migration ${migration.id} missing or invalid timestamp`);\n }\n if (!migration.up) {\n errors.push(`Migration ${migration.id} missing required field: up`);\n }\n }\n\n // Check for modified migrations\n const modified = detectModified(migrations, applied);\n\n if (modified.length > 0) {\n const modifiedIds = modified.map(r => r.id).join(\", \");\n const message = `Applied migrations have been modified: ${modifiedIds}`;\n\n // Use strictChecksums option if available, default to true (strict)\n const strictChecksums = options?.strictChecksums ?? true;\n\n if (strictChecksums) {\n errors.push(message);\n } else {\n warnings.push(message);\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n modified,\n duplicates,\n };\n}\n\n// ============================================================\n// Migration Helpers (for adapters)\n// ============================================================\n\n/**\n * Helper functions for managing migrations in database adapters.\n *\n * @remarks\n * These helpers reduce boilerplate code in dialect-specific adapters by\n * providing common operations like creating migration tables, recording\n * migrations, and querying migration status.\n *\n * Adapters can use these helpers or implement their own logic.\n *\n * @public\n */\nexport interface MigrationHelpers {\n /**\n * Create the migrations tracking table if it doesn't exist.\n *\n * @remarks\n * Creates a table named `nextly_migrations` with columns:\n * - id (string, primary key)\n * - name (string)\n * - applied_at (timestamp)\n * - checksum (string, optional)\n *\n * Uses CREATE TABLE IF NOT EXISTS for safety.\n *\n * @param adapter - Database adapter to use\n * @returns Promise that resolves when table is created\n */\n createMigrationsTable(adapter: DrizzleAdapter): Promise<void>;\n\n /**\n * Get all applied migration records from the database.\n *\n * @remarks\n * Queries the `nextly_migrations` table and returns all records\n * sorted by applied_at descending (most recent first).\n *\n * @param adapter - Database adapter to use\n * @returns Promise with array of applied migration records\n */\n getAppliedMigrations(adapter: DrizzleAdapter): Promise<MigrationRecord[]>;\n\n /**\n * Record a migration as applied in the database.\n *\n * @remarks\n * Inserts a new record into the `nextly_migrations` table with:\n * - Migration ID, name\n * - Current timestamp for applied_at\n * - Calculated checksum\n *\n * Should be called within a transaction during migration execution.\n *\n * @param adapter - Database adapter to use\n * @param migration - Migration that was applied\n * @returns Promise that resolves when record is inserted\n */\n recordMigration(adapter: DrizzleAdapter, migration: Migration): Promise<void>;\n\n /**\n * Remove a migration record from the database.\n *\n * @remarks\n * Deletes a record from the `nextly_migrations` table.\n * Used during migration rollback operations.\n *\n * Should be called within a transaction during rollback.\n *\n * @param adapter - Database adapter to use\n * @param migrationId - ID of the migration to remove\n * @returns Promise that resolves when record is deleted\n */\n removeMigrationRecord(\n adapter: DrizzleAdapter,\n migrationId: string\n ): Promise<void>;\n\n /**\n * Check if migrations table exists.\n *\n * @remarks\n * Queries database metadata to check if the `nextly_migrations` table exists.\n * Useful for determining if initialization is needed.\n *\n * @param adapter - Database adapter to use\n * @returns Promise with boolean indicating if table exists\n */\n migrationsTableExists(adapter: DrizzleAdapter): Promise<boolean>;\n}\n\n/**\n * Implementation of migration helpers.\n *\n * @remarks\n * Provides default implementations using the adapter's query methods.\n * These work across all dialects but adapters can optimize for their\n * specific database if needed.\n *\n * @public\n */\nexport const migrationHelpers: MigrationHelpers = {\n async createMigrationsTable(adapter: DrizzleAdapter): Promise<void> {\n const caps = adapter.getCapabilities();\n const dialect = caps.dialect;\n\n // Build CREATE TABLE statement with dialect-specific syntax\n let sql = \"\";\n\n if (dialect === \"postgresql\") {\n sql = `\n CREATE TABLE IF NOT EXISTS nextly_migrations (\n id VARCHAR(255) PRIMARY KEY,\n name VARCHAR(255) NOT NULL,\n applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n checksum VARCHAR(64)\n )\n `;\n } else if (dialect === \"mysql\") {\n sql = `\n CREATE TABLE IF NOT EXISTS nextly_migrations (\n id VARCHAR(255) PRIMARY KEY,\n name VARCHAR(255) NOT NULL,\n applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n checksum VARCHAR(64)\n )\n `;\n } else if (dialect === \"sqlite\") {\n sql = `\n CREATE TABLE IF NOT EXISTS nextly_migrations (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n applied_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n checksum TEXT\n )\n `;\n } else {\n throw new Error(`Unsupported dialect: ${String(dialect)}`);\n }\n\n await adapter.executeQuery(sql);\n },\n\n async getAppliedMigrations(\n adapter: DrizzleAdapter\n ): Promise<MigrationRecord[]> {\n const results = await adapter.select<{\n id: string;\n name: string;\n applied_at: Date;\n checksum?: string;\n }>(\"nextly_migrations\", {\n orderBy: [{ column: \"applied_at\", direction: \"desc\" }],\n });\n\n return results.map(row => ({\n id: row.id,\n name: row.name,\n appliedAt: new Date(row.applied_at),\n checksum: row.checksum,\n }));\n },\n\n async recordMigration(\n adapter: DrizzleAdapter,\n migration: Migration\n ): Promise<void> {\n const checksum = calculateChecksum(migration);\n\n await adapter.insert(\"nextly_migrations\", {\n id: migration.id,\n name: migration.name,\n applied_at: new Date(),\n checksum,\n });\n },\n\n async removeMigrationRecord(\n adapter: DrizzleAdapter,\n migrationId: string\n ): Promise<void> {\n await adapter.delete(\"nextly_migrations\", {\n and: [{ column: \"id\", op: \"=\", value: migrationId }],\n });\n },\n\n async migrationsTableExists(adapter: DrizzleAdapter): Promise<boolean> {\n try {\n // Try to query the table - if it doesn't exist, this will throw\n await adapter.select(\"nextly_migrations\", { limit: 1 });\n return true;\n } catch {\n return false;\n }\n },\n};\n\n// ============================================================\n// Type Exports\n// ============================================================\n\n// Re-export migration types for convenience\nexport type {\n Migration,\n MigrationRecord,\n MigrationResult,\n MigrationStatus,\n MigrationOptions,\n} from \"./types/migration\";\n"]}
1
+ {"version":3,"file":"migrations.mjs","names":[],"sources":["../src/migrations.ts"],"sourcesContent":["/**\n * Database migration utilities.\n *\n * @remarks\n * Provides core utilities for migration management including checksum calculation,\n * sorting, filtering, and validation. Also includes optional helpers for adapters\n * to use for common migration operations.\n *\n * @packageDocumentation\n */\n\nimport { createHash } from \"crypto\";\n\nimport type { DrizzleAdapter } from \"./adapter\";\nimport type {\n Migration,\n MigrationRecord,\n MigrationStatus,\n MigrationOptions,\n} from \"./types/migration\";\n\n// ============================================================\n// Core Utilities\n// ============================================================\n\n/**\n * Calculate SHA-256 checksum for a migration.\n *\n * @remarks\n * Creates a checksum of the entire migration object including id, name, timestamp,\n * and the string representation of up/down functions. This allows detection of\n * any changes to the migration after it has been applied.\n *\n * @param migration - Migration to calculate checksum for\n * @returns Hexadecimal SHA-256 hash\n *\n * @example\n * ```typescript\n * const migration: Migration = {\n * id: \"20250104_001_create_users\",\n * name: \"Create users table\",\n * timestamp: 1704326400000,\n * up: \"CREATE TABLE users (id UUID PRIMARY KEY);\",\n * };\n *\n * const checksum = calculateChecksum(migration);\n * // Returns: \"a3f5b8c9d2e1f0...\"\n * ```\n *\n * @public\n */\nexport function calculateChecksum(migration: Migration): string {\n // Serialize migration to a consistent string format\n const content = JSON.stringify({\n id: migration.id,\n name: migration.name,\n timestamp: migration.timestamp,\n up:\n typeof migration.up === \"string\" ? migration.up : migration.up.toString(),\n down:\n typeof migration.down === \"string\"\n ? migration.down\n : migration.down?.toString() || null,\n });\n\n // Create SHA-256 hash\n return createHash(\"sha256\").update(content, \"utf8\").digest(\"hex\");\n}\n\n/**\n * Sort migrations by timestamp in ascending order.\n *\n * @remarks\n * Provides deterministic ordering of migrations. If timestamps are equal,\n * falls back to sorting by migration ID alphabetically.\n *\n * @param migrations - Array of migrations to sort\n * @returns New sorted array (original array is not modified)\n *\n * @example\n * ```typescript\n * const migrations = [\n * { id: \"003\", timestamp: 1704412800000, ... },\n * { id: \"001\", timestamp: 1704326400000, ... },\n * { id: \"002\", timestamp: 1704326400000, ... },\n * ];\n *\n * const sorted = sortMigrations(migrations);\n * // Returns: [001, 002, 003] (001 and 002 same timestamp, sorted by id)\n * ```\n *\n * @public\n */\nexport function sortMigrations(migrations: Migration[]): Migration[] {\n return [...migrations].sort((a, b) => {\n // Primary sort: timestamp ascending\n if (a.timestamp !== b.timestamp) {\n return a.timestamp - b.timestamp;\n }\n // Secondary sort: id alphabetically (for deterministic ordering)\n return a.id.localeCompare(b.id);\n });\n}\n\n/**\n * Filter migrations to get only pending (unapplied) migrations.\n *\n * @remarks\n * Returns migrations that have not been applied to the database yet.\n * A migration is considered pending if its ID is not present in the\n * applied migration records.\n *\n * @param migrations - All available migrations\n * @param applied - Records of applied migrations from database\n * @returns Array of pending migrations\n *\n * @example\n * ```typescript\n * const allMigrations = [\n * { id: \"001_create_users\", ... },\n * { id: \"002_create_posts\", ... },\n * { id: \"003_create_comments\", ... },\n * ];\n *\n * const appliedRecords = [\n * { id: \"001_create_users\", appliedAt: new Date(), ... },\n * ];\n *\n * const pending = filterPending(allMigrations, appliedRecords);\n * // Returns: [002_create_posts, 003_create_comments]\n * ```\n *\n * @public\n */\nexport function filterPending(\n migrations: Migration[],\n applied: MigrationRecord[]\n): Migration[] {\n const appliedIds = new Set(applied.map(record => record.id));\n return migrations.filter(migration => !appliedIds.has(migration.id));\n}\n\n/**\n * Filter migration records to get only those that match given migrations.\n *\n * @remarks\n * Returns applied migration records that correspond to the given migrations.\n * Useful for getting the subset of applied records relevant to a specific\n * set of migrations.\n *\n * @param migrations - Migrations to match\n * @param applied - All applied migration records from database\n * @returns Array of matching applied records\n *\n * @example\n * ```typescript\n * const migrations = [\n * { id: \"001_create_users\", ... },\n * { id: \"002_create_posts\", ... },\n * ];\n *\n * const allApplied = [\n * { id: \"001_create_users\", appliedAt: new Date(), ... },\n * { id: \"002_create_posts\", appliedAt: new Date(), ... },\n * { id: \"003_create_comments\", appliedAt: new Date(), ... },\n * ];\n *\n * const relevant = filterApplied(migrations, allApplied);\n * // Returns: [001_create_users, 002_create_posts]\n * ```\n *\n * @public\n */\nexport function filterApplied(\n migrations: Migration[],\n applied: MigrationRecord[]\n): MigrationRecord[] {\n const migrationIds = new Set(migrations.map(migration => migration.id));\n return applied.filter(record => migrationIds.has(record.id));\n}\n\n/**\n * Validate that a migration's checksum matches its applied record.\n *\n * @remarks\n * Compares the calculated checksum of a migration against the stored checksum\n * in its migration record. Returns false if checksums don't match or if the\n * record has no checksum.\n *\n * @param migration - Migration to validate\n * @param record - Applied migration record with stored checksum\n * @returns true if checksums match, false otherwise\n *\n * @example\n * ```typescript\n * const migration: Migration = { id: \"001\", ... };\n * const record: MigrationRecord = {\n * id: \"001\",\n * checksum: \"a3f5b8c9...\",\n * ...\n * };\n *\n * const isValid = validateChecksum(migration, record);\n * // Returns: true if checksums match, false if modified\n * ```\n *\n * @public\n */\nexport function validateChecksum(\n migration: Migration,\n record: MigrationRecord\n): boolean {\n if (!record.checksum) {\n // No checksum stored, cannot validate\n return false;\n }\n\n const currentChecksum = calculateChecksum(migration);\n return currentChecksum === record.checksum;\n}\n\n/**\n * Detect migrations that have been modified after being applied.\n *\n * @remarks\n * Compares checksums of migrations against their applied records to detect\n * any modifications. Returns records for migrations that have been changed\n * since they were applied to the database.\n *\n * This is critical for detecting potentially dangerous situations where a\n * migration that has already run has been modified.\n *\n * @param migrations - Current migrations\n * @param applied - Applied migration records with checksums\n * @returns Array of records for modified migrations\n *\n * @example\n * ```typescript\n * const migrations = [{ id: \"001\", up: \"CREATE TABLE users_v2 ...\" }];\n * const applied = [{ id: \"001\", checksum: \"original_hash\", ... }];\n *\n * const modified = detectModified(migrations, applied);\n * // Returns: [{ id: \"001\", ... }] if migration was changed\n * ```\n *\n * @public\n */\nexport function detectModified(\n migrations: Migration[],\n applied: MigrationRecord[]\n): MigrationRecord[] {\n const migrationMap = new Map(migrations.map(m => [m.id, m]));\n const modified: MigrationRecord[] = [];\n\n for (const record of applied) {\n const migration = migrationMap.get(record.id);\n if (migration && !validateChecksum(migration, record)) {\n modified.push(record);\n }\n }\n\n return modified;\n}\n\n/**\n * Get comprehensive migration status.\n *\n * @remarks\n * Analyzes migrations and applied records to produce a complete status report\n * including current migration, applied/pending counts, and full lists.\n *\n * @param migrations - All available migrations\n * @param applied - Applied migration records from database\n * @returns Complete migration status information\n *\n * @example\n * ```typescript\n * const status = getMigrationStatus(allMigrations, appliedRecords);\n * console.log(`Current: ${status.current}`);\n * console.log(`Applied: ${status.appliedCount}, Pending: ${status.pendingCount}`);\n * ```\n *\n * @public\n */\nexport function getMigrationStatus(\n migrations: Migration[],\n applied: MigrationRecord[]\n): MigrationStatus {\n const sortedMigrations = sortMigrations(migrations);\n const sortedApplied = [...applied].sort(\n (a, b) => b.appliedAt.getTime() - a.appliedAt.getTime()\n );\n\n const pending = filterPending(sortedMigrations, applied);\n const current = sortedApplied.length > 0 ? sortedApplied[0].id : null;\n\n return {\n current,\n appliedCount: applied.length,\n pendingCount: pending.length,\n applied,\n pending,\n };\n}\n\n// ============================================================\n// Migration Validation\n// ============================================================\n\n/**\n * Validation result for migrations.\n *\n * @public\n */\nexport interface MigrationValidationResult {\n /** Whether all validations passed */\n valid: boolean;\n\n /** List of validation errors */\n errors: string[];\n\n /** List of validation warnings */\n warnings: string[];\n\n /** Migrations that have been modified */\n modified: MigrationRecord[];\n\n /** Duplicate migration IDs found */\n duplicates: string[];\n}\n\n/**\n * Validate migrations for common issues.\n *\n * @remarks\n * Performs comprehensive validation including:\n * - Duplicate migration IDs\n * - Modified applied migrations (checksum mismatch)\n * - Missing required fields\n * - Invalid timestamps\n *\n * @param migrations - Migrations to validate\n * @param applied - Applied migration records (for checksum validation)\n * @param options - Migration options (for strictness configuration)\n * @returns Validation result with errors and warnings\n *\n * @example\n * ```typescript\n * const result = validateMigrations(migrations, applied, {\n * strictChecksums: true\n * });\n *\n * if (!result.valid) {\n * console.error(\"Validation errors:\", result.errors);\n * throw new Error(\"Migration validation failed\");\n * }\n * ```\n *\n * @public\n */\nexport function validateMigrations(\n migrations: Migration[],\n applied: MigrationRecord[],\n options?: MigrationOptions\n): MigrationValidationResult {\n const errors: string[] = [];\n const warnings: string[] = [];\n const duplicates: string[] = [];\n\n // Check for duplicate IDs\n const idCounts = new Map<string, number>();\n for (const migration of migrations) {\n const count = (idCounts.get(migration.id) || 0) + 1;\n idCounts.set(migration.id, count);\n if (count > 1) {\n duplicates.push(migration.id);\n }\n }\n\n if (duplicates.length > 0) {\n errors.push(`Duplicate migration IDs found: ${duplicates.join(\", \")}`);\n }\n\n // Validate required fields\n for (const migration of migrations) {\n if (!migration.id) {\n errors.push(\"Migration missing required field: id\");\n }\n if (!migration.name) {\n errors.push(`Migration ${migration.id} missing required field: name`);\n }\n if (!migration.timestamp || typeof migration.timestamp !== \"number\") {\n errors.push(`Migration ${migration.id} missing or invalid timestamp`);\n }\n if (!migration.up) {\n errors.push(`Migration ${migration.id} missing required field: up`);\n }\n }\n\n // Check for modified migrations\n const modified = detectModified(migrations, applied);\n\n if (modified.length > 0) {\n const modifiedIds = modified.map(r => r.id).join(\", \");\n const message = `Applied migrations have been modified: ${modifiedIds}`;\n\n // Use strictChecksums option if available, default to true (strict)\n const strictChecksums = options?.strictChecksums ?? true;\n\n if (strictChecksums) {\n errors.push(message);\n } else {\n warnings.push(message);\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n warnings,\n modified,\n duplicates,\n };\n}\n\n// ============================================================\n// Migration Helpers (for adapters)\n// ============================================================\n\n/**\n * Helper functions for managing migrations in database adapters.\n *\n * @remarks\n * These helpers reduce boilerplate code in dialect-specific adapters by\n * providing common operations like creating migration tables, recording\n * migrations, and querying migration status.\n *\n * Adapters can use these helpers or implement their own logic.\n *\n * @public\n */\nexport interface MigrationHelpers {\n /**\n * Create the migrations tracking table if it doesn't exist.\n *\n * @remarks\n * Creates a table named `nextly_migrations` with columns:\n * - id (string, primary key)\n * - name (string)\n * - applied_at (timestamp)\n * - checksum (string, optional)\n *\n * Uses CREATE TABLE IF NOT EXISTS for safety.\n *\n * @param adapter - Database adapter to use\n * @returns Promise that resolves when table is created\n */\n createMigrationsTable(adapter: DrizzleAdapter): Promise<void>;\n\n /**\n * Get all applied migration records from the database.\n *\n * @remarks\n * Queries the `nextly_migrations` table and returns all records\n * sorted by applied_at descending (most recent first).\n *\n * @param adapter - Database adapter to use\n * @returns Promise with array of applied migration records\n */\n getAppliedMigrations(adapter: DrizzleAdapter): Promise<MigrationRecord[]>;\n\n /**\n * Record a migration as applied in the database.\n *\n * @remarks\n * Inserts a new record into the `nextly_migrations` table with:\n * - Migration ID, name\n * - Current timestamp for applied_at\n * - Calculated checksum\n *\n * Should be called within a transaction during migration execution.\n *\n * @param adapter - Database adapter to use\n * @param migration - Migration that was applied\n * @returns Promise that resolves when record is inserted\n */\n recordMigration(adapter: DrizzleAdapter, migration: Migration): Promise<void>;\n\n /**\n * Remove a migration record from the database.\n *\n * @remarks\n * Deletes a record from the `nextly_migrations` table.\n * Used during migration rollback operations.\n *\n * Should be called within a transaction during rollback.\n *\n * @param adapter - Database adapter to use\n * @param migrationId - ID of the migration to remove\n * @returns Promise that resolves when record is deleted\n */\n removeMigrationRecord(\n adapter: DrizzleAdapter,\n migrationId: string\n ): Promise<void>;\n\n /**\n * Check if migrations table exists.\n *\n * @remarks\n * Queries database metadata to check if the `nextly_migrations` table exists.\n * Useful for determining if initialization is needed.\n *\n * @param adapter - Database adapter to use\n * @returns Promise with boolean indicating if table exists\n */\n migrationsTableExists(adapter: DrizzleAdapter): Promise<boolean>;\n}\n\n/**\n * Implementation of migration helpers.\n *\n * @remarks\n * Provides default implementations using the adapter's query methods.\n * These work across all dialects but adapters can optimize for their\n * specific database if needed.\n *\n * @public\n */\nexport const migrationHelpers: MigrationHelpers = {\n async createMigrationsTable(adapter: DrizzleAdapter): Promise<void> {\n const caps = adapter.getCapabilities();\n const dialect = caps.dialect;\n\n // Build CREATE TABLE statement with dialect-specific syntax\n let sql = \"\";\n\n if (dialect === \"postgresql\") {\n sql = `\n CREATE TABLE IF NOT EXISTS nextly_migrations (\n id VARCHAR(255) PRIMARY KEY,\n name VARCHAR(255) NOT NULL,\n applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n checksum VARCHAR(64)\n )\n `;\n } else if (dialect === \"mysql\") {\n sql = `\n CREATE TABLE IF NOT EXISTS nextly_migrations (\n id VARCHAR(255) PRIMARY KEY,\n name VARCHAR(255) NOT NULL,\n applied_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,\n checksum VARCHAR(64)\n )\n `;\n } else if (dialect === \"sqlite\") {\n sql = `\n CREATE TABLE IF NOT EXISTS nextly_migrations (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n applied_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,\n checksum TEXT\n )\n `;\n } else {\n throw new Error(`Unsupported dialect: ${String(dialect)}`);\n }\n\n await adapter.executeQuery(sql);\n },\n\n async getAppliedMigrations(\n adapter: DrizzleAdapter\n ): Promise<MigrationRecord[]> {\n const results = await adapter.select<{\n id: string;\n name: string;\n applied_at: Date;\n checksum?: string;\n }>(\"nextly_migrations\", {\n orderBy: [{ column: \"applied_at\", direction: \"desc\" }],\n });\n\n return results.map(row => ({\n id: row.id,\n name: row.name,\n appliedAt: new Date(row.applied_at),\n checksum: row.checksum,\n }));\n },\n\n async recordMigration(\n adapter: DrizzleAdapter,\n migration: Migration\n ): Promise<void> {\n const checksum = calculateChecksum(migration);\n\n await adapter.insert(\"nextly_migrations\", {\n id: migration.id,\n name: migration.name,\n applied_at: new Date(),\n checksum,\n });\n },\n\n async removeMigrationRecord(\n adapter: DrizzleAdapter,\n migrationId: string\n ): Promise<void> {\n await adapter.delete(\"nextly_migrations\", {\n and: [{ column: \"id\", op: \"=\", value: migrationId }],\n });\n },\n\n async migrationsTableExists(adapter: DrizzleAdapter): Promise<boolean> {\n try {\n // Try to query the table - if it doesn't exist, this will throw\n await adapter.select(\"nextly_migrations\", { limit: 1 });\n return true;\n } catch {\n return false;\n }\n },\n};\n\n// ============================================================\n// Type Exports\n// ============================================================\n\n// Re-export migration types for convenience\nexport type {\n Migration,\n MigrationRecord,\n MigrationResult,\n MigrationStatus,\n MigrationOptions,\n} from \"./types/migration\";\n"],"mappings":"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"}