@kysera/migrations 0.7.3 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +128 -92
- package/dist/index.d.ts +23 -8
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/schemas.d.ts +45 -13
- package/dist/schemas.js +1 -1
- package/dist/schemas.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +2 -7
package/README.md
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
11
|
### Core Migration Management
|
|
12
|
+
|
|
12
13
|
- **Simple API** - Intuitive migration creation and execution
|
|
13
14
|
- **Type-safe** - Full TypeScript support with Kysely integration
|
|
14
15
|
- **State tracking** - Automatic migration history in database
|
|
@@ -16,6 +17,7 @@
|
|
|
16
17
|
- **Dry run mode** - Preview changes before execution
|
|
17
18
|
|
|
18
19
|
### Advanced Features
|
|
20
|
+
|
|
19
21
|
- **Rollback support** - Roll back one or multiple migrations
|
|
20
22
|
- **Partial migration** - Run up to specific migration
|
|
21
23
|
- **Status reporting** - View executed and pending migrations
|
|
@@ -24,12 +26,14 @@
|
|
|
24
26
|
- **Duplicate detection** - Validates unique migration names
|
|
25
27
|
|
|
26
28
|
### Developer Experience (v0.5.0+)
|
|
29
|
+
|
|
27
30
|
- **`defineMigrations()`** - Object-based migration definition
|
|
28
31
|
- **`runMigrations()`** - One-liner to run pending migrations
|
|
29
32
|
- **`rollbackMigrations()`** - One-liner for rollbacks
|
|
30
33
|
- **Migration metadata** - Description, breaking flag, tags, timing
|
|
31
34
|
|
|
32
35
|
### Plugin System (v0.5.0+)
|
|
36
|
+
|
|
33
37
|
- **Plugin hooks** - Before/after migration events
|
|
34
38
|
- **Built-in plugins** - Logging and metrics plugins
|
|
35
39
|
- **Extensible** - Create custom plugins for your needs
|
|
@@ -38,18 +42,20 @@
|
|
|
38
42
|
|
|
39
43
|
```bash
|
|
40
44
|
# pnpm (recommended)
|
|
41
|
-
pnpm add @kysera/migrations kysely
|
|
45
|
+
pnpm add @kysera/migrations kysely zod
|
|
42
46
|
|
|
43
47
|
# npm
|
|
44
|
-
npm install @kysera/migrations kysely
|
|
48
|
+
npm install @kysera/migrations kysely zod
|
|
45
49
|
|
|
46
50
|
# yarn
|
|
47
|
-
yarn add @kysera/migrations kysely
|
|
51
|
+
yarn add @kysera/migrations kysely zod
|
|
48
52
|
|
|
49
53
|
# bun
|
|
50
|
-
bun add @kysera/migrations kysely
|
|
54
|
+
bun add @kysera/migrations kysely zod
|
|
51
55
|
```
|
|
52
56
|
|
|
57
|
+
**Note:** Zod is a **required peer dependency** (not optional) for schema validation in the migration system.
|
|
58
|
+
|
|
53
59
|
## Quick Start
|
|
54
60
|
|
|
55
61
|
### Basic Usage
|
|
@@ -62,7 +68,7 @@ import { createMigrationRunner, createMigration } from '@kysera/migrations'
|
|
|
62
68
|
const migrations = [
|
|
63
69
|
createMigration(
|
|
64
70
|
'001_create_users',
|
|
65
|
-
async
|
|
71
|
+
async db => {
|
|
66
72
|
await db.schema
|
|
67
73
|
.createTable('users')
|
|
68
74
|
.addColumn('id', 'serial', col => col.primaryKey())
|
|
@@ -70,14 +76,14 @@ const migrations = [
|
|
|
70
76
|
.addColumn('name', 'varchar(255)', col => col.notNull())
|
|
71
77
|
.execute()
|
|
72
78
|
},
|
|
73
|
-
async
|
|
79
|
+
async db => {
|
|
74
80
|
await db.schema.dropTable('users').execute()
|
|
75
81
|
}
|
|
76
82
|
),
|
|
77
83
|
|
|
78
84
|
createMigration(
|
|
79
85
|
'002_create_posts',
|
|
80
|
-
async
|
|
86
|
+
async db => {
|
|
81
87
|
await db.schema
|
|
82
88
|
.createTable('posts')
|
|
83
89
|
.addColumn('id', 'serial', col => col.primaryKey())
|
|
@@ -87,14 +93,16 @@ const migrations = [
|
|
|
87
93
|
.addColumn('title', 'varchar(255)', col => col.notNull())
|
|
88
94
|
.execute()
|
|
89
95
|
},
|
|
90
|
-
async
|
|
96
|
+
async db => {
|
|
91
97
|
await db.schema.dropTable('posts').execute()
|
|
92
98
|
}
|
|
93
99
|
)
|
|
94
100
|
]
|
|
95
101
|
|
|
96
102
|
// Create migration runner
|
|
97
|
-
const db = new Kysely<Database>({
|
|
103
|
+
const db = new Kysely<Database>({
|
|
104
|
+
/* ... */
|
|
105
|
+
})
|
|
98
106
|
const runner = createMigrationRunner(db, migrations)
|
|
99
107
|
|
|
100
108
|
// Run all pending migrations
|
|
@@ -117,30 +125,30 @@ import { defineMigrations, runMigrations } from '@kysera/migrations'
|
|
|
117
125
|
const migrations = defineMigrations({
|
|
118
126
|
'001_create_users': {
|
|
119
127
|
description: 'Create users table with email and name',
|
|
120
|
-
up: async
|
|
128
|
+
up: async db => {
|
|
121
129
|
await db.schema
|
|
122
130
|
.createTable('users')
|
|
123
131
|
.addColumn('id', 'serial', col => col.primaryKey())
|
|
124
132
|
.addColumn('email', 'varchar(255)', col => col.notNull().unique())
|
|
125
133
|
.execute()
|
|
126
134
|
},
|
|
127
|
-
down: async
|
|
135
|
+
down: async db => {
|
|
128
136
|
await db.schema.dropTable('users').execute()
|
|
129
|
-
}
|
|
137
|
+
}
|
|
130
138
|
},
|
|
131
139
|
|
|
132
140
|
'002_create_posts': {
|
|
133
141
|
description: 'Create posts table',
|
|
134
142
|
breaking: false,
|
|
135
143
|
tags: ['schema'],
|
|
136
|
-
up: async
|
|
144
|
+
up: async db => {
|
|
137
145
|
await db.schema
|
|
138
146
|
.createTable('posts')
|
|
139
147
|
.addColumn('id', 'serial', col => col.primaryKey())
|
|
140
148
|
.addColumn('title', 'varchar(255)', col => col.notNull())
|
|
141
149
|
.execute()
|
|
142
|
-
}
|
|
143
|
-
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
144
152
|
})
|
|
145
153
|
|
|
146
154
|
// One-liner to run all migrations
|
|
@@ -178,10 +186,10 @@ interface Migration {
|
|
|
178
186
|
|
|
179
187
|
```typescript
|
|
180
188
|
interface MigrationWithMeta extends Migration {
|
|
181
|
-
description?: string
|
|
182
|
-
breaking?: boolean
|
|
189
|
+
description?: string // Shown in logs
|
|
190
|
+
breaking?: boolean // Shows warning
|
|
183
191
|
estimatedDuration?: number // In milliseconds
|
|
184
|
-
tags?: string[]
|
|
192
|
+
tags?: string[] // For categorization
|
|
185
193
|
}
|
|
186
194
|
```
|
|
187
195
|
|
|
@@ -199,11 +207,11 @@ interface MigrationStatus {
|
|
|
199
207
|
|
|
200
208
|
```typescript
|
|
201
209
|
interface MigrationResult {
|
|
202
|
-
executed: string[]
|
|
203
|
-
skipped: string[]
|
|
204
|
-
failed: string[]
|
|
205
|
-
duration: number
|
|
206
|
-
dryRun: boolean
|
|
210
|
+
executed: string[] // Successfully executed
|
|
211
|
+
skipped: string[] // Already executed or no down()
|
|
212
|
+
failed: string[] // Failed migrations
|
|
213
|
+
duration: number // Total time in ms
|
|
214
|
+
dryRun: boolean // Whether dry run mode
|
|
207
215
|
}
|
|
208
216
|
```
|
|
209
217
|
|
|
@@ -211,11 +219,11 @@ interface MigrationResult {
|
|
|
211
219
|
|
|
212
220
|
```typescript
|
|
213
221
|
interface MigrationRunnerOptions {
|
|
214
|
-
dryRun?: boolean
|
|
215
|
-
logger?: KyseraLogger
|
|
222
|
+
dryRun?: boolean // Preview only (default: false)
|
|
223
|
+
logger?: KyseraLogger // Logger instance from @kysera/core (default: silentLogger)
|
|
216
224
|
useTransactions?: boolean // Wrap in transactions (default: false)
|
|
217
|
-
stopOnError?: boolean
|
|
218
|
-
verbose?: boolean
|
|
225
|
+
stopOnError?: boolean // Stop on first error (default: true)
|
|
226
|
+
verbose?: boolean // Show metadata (default: true)
|
|
219
227
|
}
|
|
220
228
|
```
|
|
221
229
|
|
|
@@ -249,7 +257,10 @@ interface MigrationRunnerWithPluginsOptions extends MigrationRunnerOptions {
|
|
|
249
257
|
#### `MigrationErrorCode`
|
|
250
258
|
|
|
251
259
|
```typescript
|
|
252
|
-
type MigrationErrorCode =
|
|
260
|
+
type MigrationErrorCode =
|
|
261
|
+
| 'MIGRATION_UP_FAILED'
|
|
262
|
+
| 'MIGRATION_DOWN_FAILED'
|
|
263
|
+
| 'MIGRATION_VALIDATION_FAILED'
|
|
253
264
|
```
|
|
254
265
|
|
|
255
266
|
### Factory Functions
|
|
@@ -261,8 +272,12 @@ Create a simple migration:
|
|
|
261
272
|
```typescript
|
|
262
273
|
const migration = createMigration(
|
|
263
274
|
'001_create_users',
|
|
264
|
-
async
|
|
265
|
-
|
|
275
|
+
async db => {
|
|
276
|
+
/* up */
|
|
277
|
+
},
|
|
278
|
+
async db => {
|
|
279
|
+
/* down */
|
|
280
|
+
}
|
|
266
281
|
)
|
|
267
282
|
```
|
|
268
283
|
|
|
@@ -276,8 +291,12 @@ const migration = createMigrationWithMeta('001_create_users', {
|
|
|
276
291
|
breaking: true,
|
|
277
292
|
tags: ['schema', 'users'],
|
|
278
293
|
estimatedDuration: 5000,
|
|
279
|
-
up: async
|
|
280
|
-
|
|
294
|
+
up: async db => {
|
|
295
|
+
/* ... */
|
|
296
|
+
},
|
|
297
|
+
down: async db => {
|
|
298
|
+
/* ... */
|
|
299
|
+
}
|
|
281
300
|
})
|
|
282
301
|
```
|
|
283
302
|
|
|
@@ -289,7 +308,7 @@ Create a MigrationRunner instance:
|
|
|
289
308
|
const runner = createMigrationRunner(db, migrations, {
|
|
290
309
|
dryRun: false,
|
|
291
310
|
logger: console.log,
|
|
292
|
-
useTransactions: true
|
|
311
|
+
useTransactions: true
|
|
293
312
|
})
|
|
294
313
|
```
|
|
295
314
|
|
|
@@ -300,7 +319,7 @@ Create a MigrationRunner with plugin support (async factory):
|
|
|
300
319
|
```typescript
|
|
301
320
|
const runner = await createMigrationRunnerWithPlugins(db, migrations, {
|
|
302
321
|
plugins: [createLoggingPlugin(), createMetricsPlugin()],
|
|
303
|
-
useTransactions: true
|
|
322
|
+
useTransactions: true
|
|
304
323
|
})
|
|
305
324
|
|
|
306
325
|
// Runner is ready with plugins initialized via onInit
|
|
@@ -317,9 +336,13 @@ Define migrations using object syntax:
|
|
|
317
336
|
const migrations = defineMigrations({
|
|
318
337
|
'001_users': {
|
|
319
338
|
description: 'Create users',
|
|
320
|
-
up: async
|
|
321
|
-
|
|
322
|
-
|
|
339
|
+
up: async db => {
|
|
340
|
+
/* ... */
|
|
341
|
+
},
|
|
342
|
+
down: async db => {
|
|
343
|
+
/* ... */
|
|
344
|
+
}
|
|
345
|
+
}
|
|
323
346
|
})
|
|
324
347
|
```
|
|
325
348
|
|
|
@@ -337,8 +360,8 @@ const result = await runMigrations(db, migrations, { dryRun: true })
|
|
|
337
360
|
Rollback migrations:
|
|
338
361
|
|
|
339
362
|
```typescript
|
|
340
|
-
await rollbackMigrations(db, migrations)
|
|
341
|
-
await rollbackMigrations(db, migrations, 3)
|
|
363
|
+
await rollbackMigrations(db, migrations) // Last 1
|
|
364
|
+
await rollbackMigrations(db, migrations, 3) // Last 3
|
|
342
365
|
await rollbackMigrations(db, migrations, 1, { dryRun: true })
|
|
343
366
|
```
|
|
344
367
|
|
|
@@ -368,8 +391,8 @@ console.log(`Executed: ${result.executed.length} migrations`)
|
|
|
368
391
|
Rollback last N migrations:
|
|
369
392
|
|
|
370
393
|
```typescript
|
|
371
|
-
await runner.down(1)
|
|
372
|
-
await runner.down(3)
|
|
394
|
+
await runner.down(1) // Rollback last one
|
|
395
|
+
await runner.down(3) // Rollback last three
|
|
373
396
|
```
|
|
374
397
|
|
|
375
398
|
#### `status(): Promise<MigrationStatus>`
|
|
@@ -386,7 +409,7 @@ const status = await runner.status()
|
|
|
386
409
|
Rollback all migrations:
|
|
387
410
|
|
|
388
411
|
```typescript
|
|
389
|
-
await runner.reset()
|
|
412
|
+
await runner.reset() // Dangerous! Rolls back everything
|
|
390
413
|
```
|
|
391
414
|
|
|
392
415
|
#### `upTo(targetName): Promise<MigrationResult>`
|
|
@@ -448,9 +471,17 @@ interface MigrationPlugin {
|
|
|
448
471
|
// Called once when runner is initialized (consistent with repository Plugin.onInit)
|
|
449
472
|
onInit?(runner: MigrationRunner): Promise<void> | void
|
|
450
473
|
beforeMigration?(migration: Migration, operation: 'up' | 'down'): Promise<void> | void
|
|
451
|
-
afterMigration?(
|
|
474
|
+
afterMigration?(
|
|
475
|
+
migration: Migration,
|
|
476
|
+
operation: 'up' | 'down',
|
|
477
|
+
duration: number
|
|
478
|
+
): Promise<void> | void
|
|
452
479
|
// Unknown error type for consistency with repository Plugin.onError
|
|
453
|
-
onMigrationError?(
|
|
480
|
+
onMigrationError?(
|
|
481
|
+
migration: Migration,
|
|
482
|
+
operation: 'up' | 'down',
|
|
483
|
+
error: unknown
|
|
484
|
+
): Promise<void> | void
|
|
454
485
|
}
|
|
455
486
|
```
|
|
456
487
|
|
|
@@ -463,7 +494,7 @@ import { createLoggingPlugin } from '@kysera/migrations'
|
|
|
463
494
|
|
|
464
495
|
const loggingPlugin = createLoggingPlugin(console.log)
|
|
465
496
|
// or with custom logger
|
|
466
|
-
const loggingPlugin = createLoggingPlugin(
|
|
497
|
+
const loggingPlugin = createLoggingPlugin(msg => logger.info(msg))
|
|
467
498
|
```
|
|
468
499
|
|
|
469
500
|
#### Metrics Plugin
|
|
@@ -503,7 +534,7 @@ const notificationPlugin: MigrationPlugin = {
|
|
|
503
534
|
// Error is unknown type - handle appropriately
|
|
504
535
|
const message = error instanceof Error ? error.message : String(error)
|
|
505
536
|
await pagerduty.alert(`Migration failed: ${message}`)
|
|
506
|
-
}
|
|
537
|
+
}
|
|
507
538
|
}
|
|
508
539
|
```
|
|
509
540
|
|
|
@@ -588,8 +619,12 @@ try {
|
|
|
588
619
|
```typescript
|
|
589
620
|
createMigration(
|
|
590
621
|
'001_create_users',
|
|
591
|
-
async
|
|
592
|
-
|
|
622
|
+
async db => {
|
|
623
|
+
/* up */
|
|
624
|
+
},
|
|
625
|
+
async db => {
|
|
626
|
+
/* down - always provide this! */
|
|
627
|
+
}
|
|
593
628
|
)
|
|
594
629
|
```
|
|
595
630
|
|
|
@@ -598,9 +633,11 @@ createMigration(
|
|
|
598
633
|
```typescript
|
|
599
634
|
createMigrationWithMeta('005_big_refactor', {
|
|
600
635
|
description: 'Refactors user permissions system',
|
|
601
|
-
breaking: true,
|
|
636
|
+
breaking: true, // Will show warning
|
|
602
637
|
tags: ['breaking', 'permissions'],
|
|
603
|
-
up: async
|
|
638
|
+
up: async db => {
|
|
639
|
+
/* ... */
|
|
640
|
+
}
|
|
604
641
|
})
|
|
605
642
|
```
|
|
606
643
|
|
|
@@ -618,7 +655,7 @@ await runMigrations(db, migrations)
|
|
|
618
655
|
|
|
619
656
|
```typescript
|
|
620
657
|
const runner = createMigrationRunner(db, migrations, {
|
|
621
|
-
useTransactions: true
|
|
658
|
+
useTransactions: true // Each migration wrapped in transaction
|
|
622
659
|
})
|
|
623
660
|
```
|
|
624
661
|
|
|
@@ -628,7 +665,12 @@ const runner = createMigrationRunner(db, migrations, {
|
|
|
628
665
|
// scripts/migrate.ts
|
|
629
666
|
import { Kysely, PostgresDialect } from 'kysely'
|
|
630
667
|
import { Pool } from 'pg'
|
|
631
|
-
import {
|
|
668
|
+
import {
|
|
669
|
+
runMigrations,
|
|
670
|
+
rollbackMigrations,
|
|
671
|
+
getMigrationStatus,
|
|
672
|
+
defineMigrations
|
|
673
|
+
} from '@kysera/migrations'
|
|
632
674
|
|
|
633
675
|
const migrations = defineMigrations({
|
|
634
676
|
// ... your migrations
|
|
@@ -694,58 +736,51 @@ main()
|
|
|
694
736
|
### PostgreSQL
|
|
695
737
|
|
|
696
738
|
```typescript
|
|
697
|
-
createMigration(
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
col.notNull().defaultTo(db.fn('now'))
|
|
705
|
-
)
|
|
706
|
-
.execute()
|
|
707
|
-
}
|
|
708
|
-
)
|
|
739
|
+
createMigration('001_create_users', async db => {
|
|
740
|
+
await db.schema
|
|
741
|
+
.createTable('users')
|
|
742
|
+
.addColumn('id', 'serial', col => col.primaryKey())
|
|
743
|
+
.addColumn('created_at', 'timestamp', col => col.notNull().defaultTo(db.fn('now')))
|
|
744
|
+
.execute()
|
|
745
|
+
})
|
|
709
746
|
```
|
|
710
747
|
|
|
711
748
|
### MySQL
|
|
712
749
|
|
|
713
750
|
```typescript
|
|
714
|
-
createMigration(
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
)
|
|
722
|
-
.addColumn('created_at', 'datetime', col =>
|
|
723
|
-
col.notNull().defaultTo(db.fn('now'))
|
|
724
|
-
)
|
|
725
|
-
.execute()
|
|
726
|
-
}
|
|
727
|
-
)
|
|
751
|
+
createMigration('001_create_users', async db => {
|
|
752
|
+
await db.schema
|
|
753
|
+
.createTable('users')
|
|
754
|
+
.addColumn('id', 'integer', col => col.primaryKey().autoIncrement())
|
|
755
|
+
.addColumn('created_at', 'datetime', col => col.notNull().defaultTo(db.fn('now')))
|
|
756
|
+
.execute()
|
|
757
|
+
})
|
|
728
758
|
```
|
|
729
759
|
|
|
730
760
|
### SQLite
|
|
731
761
|
|
|
732
762
|
```typescript
|
|
733
|
-
createMigration(
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
)
|
|
741
|
-
.addColumn('created_at', 'text', col =>
|
|
742
|
-
col.notNull().defaultTo(sql`CURRENT_TIMESTAMP`)
|
|
743
|
-
)
|
|
744
|
-
.execute()
|
|
745
|
-
}
|
|
746
|
-
)
|
|
763
|
+
createMigration('001_create_users', async db => {
|
|
764
|
+
await db.schema
|
|
765
|
+
.createTable('users')
|
|
766
|
+
.addColumn('id', 'integer', col => col.primaryKey().autoIncrement())
|
|
767
|
+
.addColumn('created_at', 'text', col => col.notNull().defaultTo(sql`CURRENT_TIMESTAMP`))
|
|
768
|
+
.execute()
|
|
769
|
+
})
|
|
747
770
|
```
|
|
748
771
|
|
|
772
|
+
## Implementation Details
|
|
773
|
+
|
|
774
|
+
### Version File Fallback Pattern
|
|
775
|
+
|
|
776
|
+
The migration system uses a robust version file fallback pattern to ensure compatibility across different build environments:
|
|
777
|
+
|
|
778
|
+
1. **Primary:** Try to load `version.ts` (development/source code)
|
|
779
|
+
2. **Fallback:** Try to load `version.js` (compiled/production code)
|
|
780
|
+
3. **Default:** Use `'0.0.0-dev'` if both fail
|
|
781
|
+
|
|
782
|
+
This pattern ensures migrations work correctly in both development and production environments without requiring specific build configurations.
|
|
783
|
+
|
|
749
784
|
## Changelog
|
|
750
785
|
|
|
751
786
|
### v0.5.1
|
|
@@ -758,6 +793,7 @@ createMigration(
|
|
|
758
793
|
- **Added** `MigrationDefinition` and `MigrationDefinitions` type exports
|
|
759
794
|
- **Added** `MigrationRunnerWithPluginsOptions` interface export
|
|
760
795
|
- **Added** `DatabaseError` and `BadRequestError` re-exports from `@kysera/core`
|
|
796
|
+
- **Added** Version file fallback pattern for cross-environment compatibility
|
|
761
797
|
- **Changed** Validation errors now throw `BadRequestError` instead of generic `Error`
|
|
762
798
|
|
|
763
799
|
### v0.5.0
|
package/dist/index.d.ts
CHANGED
|
@@ -121,10 +121,12 @@ declare function setupMigrations(db: Kysely<unknown>): Promise<void>;
|
|
|
121
121
|
* defaults to unknown for maximum flexibility
|
|
122
122
|
*/
|
|
123
123
|
declare class MigrationRunner<DB = unknown> {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
124
|
+
protected logger: KyseraLogger;
|
|
125
|
+
protected runnerOptions: Required<Omit<MigrationRunnerOptions, 'logger'>> & {
|
|
126
|
+
logger: KyseraLogger;
|
|
127
|
+
};
|
|
128
|
+
protected db: Kysely<DB>;
|
|
129
|
+
protected migrations: Migration<DB>[];
|
|
128
130
|
constructor(db: Kysely<DB>, migrations: Migration<DB>[], options?: MigrationRunnerOptions);
|
|
129
131
|
/**
|
|
130
132
|
* Get list of executed migrations from database
|
|
@@ -144,11 +146,11 @@ declare class MigrationRunner<DB = unknown> {
|
|
|
144
146
|
/**
|
|
145
147
|
* Log migration metadata if available
|
|
146
148
|
*/
|
|
147
|
-
|
|
149
|
+
protected logMigrationMeta(migration: Migration<DB>): void;
|
|
148
150
|
/**
|
|
149
151
|
* Execute a single migration with optional transaction wrapping
|
|
150
152
|
*/
|
|
151
|
-
|
|
153
|
+
protected executeMigration(migration: Migration<DB>, operation: 'up' | 'down'): Promise<void>;
|
|
152
154
|
/**
|
|
153
155
|
* Run all pending migrations
|
|
154
156
|
*/
|
|
@@ -261,6 +263,9 @@ declare function createMigrationRunnerWithPlugins<DB = unknown>(db: Kysely<DB>,
|
|
|
261
263
|
* Extended migration runner with plugin support
|
|
262
264
|
* Generic DB type allows type-safe migrations when schema is known,
|
|
263
265
|
* defaults to unknown for maximum flexibility
|
|
266
|
+
*
|
|
267
|
+
* This class overrides up() and down() to call plugin lifecycle hooks
|
|
268
|
+
* (beforeMigration, afterMigration, onMigrationError) around each migration execution.
|
|
264
269
|
*/
|
|
265
270
|
declare class MigrationRunnerWithPlugins<DB = unknown> extends MigrationRunner<DB> {
|
|
266
271
|
private plugins;
|
|
@@ -284,6 +289,16 @@ declare class MigrationRunnerWithPlugins<DB = unknown> extends MigrationRunner<D
|
|
|
284
289
|
* Get the list of registered plugins
|
|
285
290
|
*/
|
|
286
291
|
getPlugins(): MigrationPlugin<DB>[];
|
|
292
|
+
/**
|
|
293
|
+
* Run all pending migrations with plugin hooks
|
|
294
|
+
* Overrides parent to call beforeMigration/afterMigration/onMigrationError hooks
|
|
295
|
+
*/
|
|
296
|
+
up(): Promise<MigrationResult>;
|
|
297
|
+
/**
|
|
298
|
+
* Rollback last N migrations with plugin hooks
|
|
299
|
+
* Overrides parent to call beforeMigration/afterMigration/onMigrationError hooks
|
|
300
|
+
*/
|
|
301
|
+
down(steps?: number): Promise<MigrationResult>;
|
|
287
302
|
}
|
|
288
303
|
/**
|
|
289
304
|
* Logging plugin - logs migration events with timing
|
|
@@ -296,12 +311,12 @@ declare function createLoggingPlugin<DB = unknown>(logger?: KyseraLogger): Migra
|
|
|
296
311
|
*/
|
|
297
312
|
declare function createMetricsPlugin<DB = unknown>(): MigrationPlugin<DB> & {
|
|
298
313
|
getMetrics(): {
|
|
299
|
-
migrations:
|
|
314
|
+
migrations: {
|
|
300
315
|
name: string;
|
|
301
316
|
operation: string;
|
|
302
317
|
duration: number;
|
|
303
318
|
success: boolean;
|
|
304
|
-
}
|
|
319
|
+
}[];
|
|
305
320
|
};
|
|
306
321
|
};
|
|
307
322
|
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {sql}from'kysely';import {silentLogger,DatabaseError,BadRequestError,NotFoundError}from'@kysera/core';export{BadRequestError,DatabaseError,NotFoundError,silentLogger}from'@kysera/core';import {z as z$1}from'zod';var d=z$1.object({dryRun:z$1.boolean().default(false),logger:z$1.any().optional(),useTransactions:z$1.boolean().default(false),stopOnError:z$1.boolean().default(true),verbose:z$1.boolean().default(true)}),m=z$1.object({name:z$1.string().min(1,"Migration name is required"),description:z$1.string().optional(),breaking:z$1.boolean().default(false),tags:z$1.array(z$1.string()).default([]),estimatedDuration:z$1.string().optional()}),x=z$1.object({logger:z$1.any().optional()}),D=z$1.object({name:z$1.string().min(1,"Plugin name is required"),version:z$1.string().min(1,"Plugin version is required"),onInit:z$1.any().optional(),beforeMigration:z$1.any().optional(),afterMigration:z$1.any().optional(),onMigrationError:z$1.any().optional()}),b=z$1.object({executed:z$1.array(z$1.string()),pending:z$1.array(z$1.string()),total:z$1.number().int().nonnegative()}),P=z$1.object({executed:z$1.array(z$1.string()),skipped:z$1.array(z$1.string()),failed:z$1.array(z$1.string()),duration:z$1.number().nonnegative(),dryRun:z$1.boolean()}),B=d.extend({plugins:z$1.array(D).optional()});function k(e){return d.parse(e)}function O(e){return d.safeParse(e)}function E(e){return m.parse(e)}function v(e){return m.safeParse(e)}var f=class extends DatabaseError{migrationName;operation;constructor(n,i,t,o){let a=t==="up"?"MIGRATION_UP_FAILED":"MIGRATION_DOWN_FAILED";super(n,a,i),this.name="MigrationError",this.migrationName=i,this.operation=t,o&&(this.cause=o);}toJSON(){let n=this.cause instanceof Error?this.cause:void 0;return {...super.toJSON(),migrationName:this.migrationName,operation:this.operation,cause:n?.message}}};async function c(e){await e.schema.createTable("migrations").ifNotExists().addColumn("name","varchar(255)",n=>n.primaryKey()).addColumn("executed_at","timestamp",n=>n.notNull().defaultTo(sql`CURRENT_TIMESTAMP`)).execute();}function h(e){return "description"in e||"breaking"in e||"tags"in e}function M(e){return e instanceof Error?e.message:String(e)}function N(e){let n=new Set;for(let i of e){if(n.has(i.name))throw new BadRequestError(`Duplicate migration name: ${i.name}`);n.add(i.name);}}var p=class{constructor(n,i,t={}){this.db=n;this.migrations=i;let o=d.safeParse(t);if(!o.success)throw new BadRequestError(`Invalid migration runner options: ${o.error.message}`);this.logger=t.logger??silentLogger,this.options={dryRun:o.data.dryRun,logger:this.logger,useTransactions:o.data.useTransactions,stopOnError:o.data.stopOnError,verbose:o.data.verbose},N(i);}logger;options;async getExecutedMigrations(){return await c(this.db),(await this.db.selectFrom("migrations").select("name").orderBy("executed_at","asc").execute()).map(i=>i.name)}async markAsExecuted(n){await this.db.insertInto("migrations").values({name:n}).execute();}async markAsRolledBack(n){await this.db.deleteFrom("migrations").where("name","=",n).execute();}logMigrationMeta(n){if(!this.options.verbose||!h(n))return;let i=n;if(i.description&&this.logger.info(` Description: ${i.description}`),i.breaking&&this.logger.warn(" BREAKING CHANGE - Review carefully before proceeding"),i.tags&&i.tags.length>0&&this.logger.info(` Tags: ${i.tags.join(", ")}`),i.estimatedDuration){let t=(i.estimatedDuration/1e3).toFixed(1);this.logger.info(` Estimated: ${t}s`);}}async executeMigration(n,i){let t=i==="up"?n.up:n.down;t&&(this.options.useTransactions?await this.db.transaction().execute(async o=>{await t(o);}):await t(this.db));}async up(){let n=Date.now(),i={executed:[],skipped:[],failed:[],duration:0,dryRun:this.options.dryRun};await c(this.db);let t=await this.getExecutedMigrations();if(this.migrations.filter(a=>!t.includes(a.name)).length===0)return this.logger.info("No pending migrations"),i.skipped=t,i.duration=Date.now()-n,i;this.options.dryRun&&this.logger.info("DRY RUN - No changes will be made");for(let a of this.migrations){if(t.includes(a.name)){this.logger.info(`${a.name} (already executed)`),i.skipped.push(a.name);continue}try{this.logger.info(`Running ${a.name}...`),this.logMigrationMeta(a),this.options.dryRun||(await this.executeMigration(a,"up"),await this.markAsExecuted(a.name)),this.logger.info(`${a.name} completed`),i.executed.push(a.name);}catch(g){let s=M(g);if(this.logger.error(`${a.name} failed: ${s}`),i.failed.push(a.name),this.options.stopOnError)throw new f(`Migration ${a.name} failed: ${s}`,a.name,"up",g instanceof Error?g:void 0)}}return this.options.dryRun?this.logger.info("Dry run completed - no changes made"):this.logger.info("All migrations completed successfully"),i.duration=Date.now()-n,i}async down(n=1){let i=Date.now(),t={executed:[],skipped:[],failed:[],duration:0,dryRun:this.options.dryRun};await c(this.db);let o=await this.getExecutedMigrations();if(o.length===0)return this.logger.warn("No executed migrations to rollback"),t.duration=Date.now()-i,t;let a=o.slice(-n).reverse();this.options.dryRun&&this.logger.info("DRY RUN - No changes will be made");for(let g of a){let s=this.migrations.find(u=>u.name===g);if(!s){this.logger.warn(`Migration ${g} not found in codebase`),t.skipped.push(g);continue}if(!s.down){this.logger.warn(`Migration ${g} has no down method - skipping`),t.skipped.push(g);continue}try{this.logger.info(`Rolling back ${g}...`),this.logMigrationMeta(s),this.options.dryRun||(await this.executeMigration(s,"down"),await this.markAsRolledBack(g)),this.logger.info(`${g} rolled back`),t.executed.push(g);}catch(u){let l=M(u);if(this.logger.error(`${g} rollback failed: ${l}`),t.failed.push(g),this.options.stopOnError)throw new f(`Rollback of ${g} failed: ${l}`,g,"down",u instanceof Error?u:void 0)}}return this.options.dryRun?this.logger.info("Dry run completed - no changes made"):this.logger.info("Rollback completed successfully"),t.duration=Date.now()-i,t}async status(){await c(this.db);let n=await this.getExecutedMigrations(),i=this.migrations.filter(t=>!n.includes(t.name)).map(t=>t.name);if(this.logger.info("Migration Status:"),this.logger.info(` Executed: ${n.length}`),this.logger.info(` Pending: ${i.length}`),this.logger.info(` Total: ${this.migrations.length}`),n.length>0){this.logger.info("Executed migrations:");for(let t of n){let o=this.migrations.find(a=>a.name===t);o&&h(o)&&o.description?this.logger.info(` ${t} - ${o.description}`):this.logger.info(` ${t}`);}}if(i.length>0){this.logger.info("Pending migrations:");for(let t of i){let o=this.migrations.find(a=>a.name===t);if(o&&h(o)){let a=o,g=a.breaking?" BREAKING":"",s=a.description?` - ${a.description}`:"";this.logger.info(` ${t}${s}${g}`);}else this.logger.info(` ${t}`);}}return {executed:n,pending:i,total:this.migrations.length}}async reset(){let n=Date.now();await c(this.db);let i=await this.getExecutedMigrations();if(i.length===0)return this.logger.warn("No migrations to reset"),{executed:[],skipped:[],failed:[],duration:Date.now()-n,dryRun:this.options.dryRun};if(this.logger.warn(`Resetting ${i.length} migrations...`),this.options.dryRun){this.logger.info("DRY RUN - Would rollback the following migrations:");for(let o of [...i].reverse())this.migrations.find(g=>g.name===o)?.down?this.logger.info(` ${o}`):this.logger.warn(` ${o} (no down method - would be skipped)`);return this.logger.info("Dry run completed - no changes made"),{executed:[],skipped:i,failed:[],duration:Date.now()-n,dryRun:true}}let t=await this.down(i.length);return this.logger.info("All migrations reset"),t}async upTo(n){let i=Date.now(),t={executed:[],skipped:[],failed:[],duration:0,dryRun:this.options.dryRun};await c(this.db);let o=await this.getExecutedMigrations(),a=this.migrations.findIndex(s=>s.name===n);if(a===-1)throw new NotFoundError("Migration",{name:n});let g=this.migrations.slice(0,a+1);this.options.dryRun&&this.logger.info("DRY RUN - No changes will be made");for(let s of g){if(o.includes(s.name)){this.logger.info(`${s.name} (already executed)`),t.skipped.push(s.name);continue}try{this.logger.info(`Running ${s.name}...`),this.logMigrationMeta(s),this.options.dryRun||(await this.executeMigration(s,"up"),await this.markAsExecuted(s.name)),this.logger.info(`${s.name} completed`),t.executed.push(s.name);}catch(u){let l=M(u);throw this.logger.error(`${s.name} failed: ${l}`),t.failed.push(s.name),new f(`Migration ${s.name} failed: ${l}`,s.name,"up",u instanceof Error?u:void 0)}}return this.options.dryRun?this.logger.info(`Dry run completed - would migrate up to ${n}`):this.logger.info(`Migrated up to ${n}`),t.duration=Date.now()-i,t}};function z(e,n,i){return new p(e,n,i)}function _(e,n,i){let t={name:e,up:n};return i!==void 0&&(t.down=i),t}function F(e,n){let i={name:e,up:n.up};return n.down!==void 0&&(i.down=n.down),n.description!==void 0&&(i.description=n.description),n.breaking!==void 0&&(i.breaking=n.breaking),n.estimatedDuration!==void 0&&(i.estimatedDuration=n.estimatedDuration),n.tags!==void 0&&(i.tags=n.tags),i}function j(e){return Object.entries(e).map(([n,i])=>{let t={name:n,up:i.up};return i.down!==void 0&&(t.down=i.down),i.description!==void 0&&(t.description=i.description),i.breaking!==void 0&&(t.breaking=i.breaking),i.estimatedDuration!==void 0&&(t.estimatedDuration=i.estimatedDuration),i.tags!==void 0&&(t.tags=i.tags),t})}async function G(e,n,i){return new p(e,n,i).up()}async function q(e,n,i=1,t){return new p(e,n,t).down(i)}async function C(e,n,i){return new p(e,n,i).status()}async function U(e,n,i){let t=new y(e,n,i);if(i?.plugins){for(let o of i.plugins)if(o.onInit){let a=o.onInit(t);a instanceof Promise&&await a;}}return t}var y=class extends p{plugins;constructor(n,i,t={}){super(n,i,t),this.plugins=t.plugins??[];}async runBeforeHooks(n,i){for(let t of this.plugins)t.beforeMigration&&await t.beforeMigration(n,i);}async runAfterHooks(n,i,t){for(let o of this.plugins)o.afterMigration&&await o.afterMigration(n,i,t);}async runErrorHooks(n,i,t){for(let o of this.plugins)o.onMigrationError&&await o.onMigrationError(n,i,t);}getPlugins(){return [...this.plugins]}};function H(e=silentLogger){return {name:"@kysera/migrations/logging",version:"0.5.1",beforeMigration(n,i){e.info(`Starting ${i} for ${n.name}`);},afterMigration(n,i,t){e.info(`Completed ${i} for ${n.name} in ${t}ms`);},onMigrationError(n,i,t){let o=t instanceof Error?t.message:String(t);e.error(`Error during ${i} for ${n.name}: ${o}`);}}}function Y(){let e=[];return {name:"@kysera/migrations/metrics",version:"0.5.1",afterMigration(n,i,t){e.push({name:n.name,operation:i,duration:t,success:true});},onMigrationError(n,i){e.push({name:n.name,operation:i,duration:0,success:false});},getMetrics(){return {migrations:[...e]}}}}export{m as MigrationDefinitionSchema,f as MigrationError,x as MigrationPluginOptionsSchema,D as MigrationPluginSchema,P as MigrationResultSchema,p as MigrationRunner,d as MigrationRunnerOptionsSchema,y as MigrationRunnerWithPlugins,B as MigrationRunnerWithPluginsOptionsSchema,b as MigrationStatusSchema,H as createLoggingPlugin,Y as createMetricsPlugin,_ as createMigration,z as createMigrationRunner,U as createMigrationRunnerWithPlugins,F as createMigrationWithMeta,j as defineMigrations,C as getMigrationStatus,E as parseMigrationDefinition,k as parseMigrationRunnerOptions,q as rollbackMigrations,G as runMigrations,v as safeParseMigrationDefinition,O as safeParseMigrationRunnerOptions,c as setupMigrations};//# sourceMappingURL=index.js.map
|
|
1
|
+
import {sql}from'kysely';import {silentLogger,DatabaseError,BadRequestError,NotFoundError}from'@kysera/core';export{BadRequestError,DatabaseError,NotFoundError,silentLogger}from'@kysera/core';import {z}from'zod';var R="__VERSION__",h=R.startsWith("__")?"0.0.0-dev":R;var x=z.object({trace:z.function(),debug:z.function(),info:z.function(),warn:z.function(),error:z.function(),fatal:z.function()}).passthrough(),f=z.object({dryRun:z.boolean().default(false),logger:x.optional(),useTransactions:z.boolean().default(false),stopOnError:z.boolean().default(true),verbose:z.boolean().default(true)}),M=z.object({name:z.string().min(1,"Migration name is required"),description:z.string().optional(),breaking:z.boolean().default(false),tags:z.array(z.string()).default([]),estimatedDuration:z.string().optional()}),B=z.object({logger:x.optional()}),b=z.object({name:z.string().min(1,"Plugin name is required"),version:z.string().min(1,"Plugin version is required"),onInit:z.function().optional(),beforeMigration:z.function().optional(),afterMigration:z.function().optional(),onMigrationError:z.function().optional()}),P=z.object({executed:z.array(z.string()),pending:z.array(z.string()),total:z.number().int().nonnegative()}),E=z.object({executed:z.array(z.string()),skipped:z.array(z.string()),failed:z.array(z.string()),duration:z.number().nonnegative(),dryRun:z.boolean()}),$=f.extend({plugins:z.array(b).optional()});function v(o){return f.parse(o)}function S(o){return f.safeParse(o)}function I(o){return M.parse(o)}function N(o){return M.safeParse(o)}var p=class extends DatabaseError{migrationName;operation;constructor(i,n,t,r){let e=t==="up"?"MIGRATION_UP_FAILED":"MIGRATION_DOWN_FAILED";super(i,e,n),this.name="MigrationError",this.migrationName=n,this.operation=t,r&&(this.cause=r);}toJSON(){let i=this.cause instanceof Error?this.cause:void 0;return {...super.toJSON(),migrationName:this.migrationName,operation:this.operation,cause:i?.message}}};async function c(o){await o.schema.createTable("migrations").ifNotExists().addColumn("name","varchar(255)",i=>i.primaryKey()).addColumn("executed_at","timestamp",i=>i.notNull().defaultTo(sql`CURRENT_TIMESTAMP`)).execute();try{await o.schema.createIndex("idx_migrations_name").on("migrations").column("name").execute();}catch{}}function y(o){return "description"in o||"breaking"in o||"tags"in o}function m(o){return o instanceof Error?o.message:String(o)}function W(o){let i=new Set;for(let n of o){if(i.has(n.name))throw new BadRequestError(`Duplicate migration name: ${n.name}`);i.add(n.name);}}var l=class{logger;runnerOptions;db;migrations;constructor(i,n,t={}){let r=f.safeParse(t);if(!r.success)throw new BadRequestError(`Invalid migration runner options: ${r.error.message}`);this.db=i,this.migrations=n,this.logger=t.logger??silentLogger,this.runnerOptions={dryRun:r.data.dryRun,logger:this.logger,useTransactions:r.data.useTransactions,stopOnError:r.data.stopOnError,verbose:r.data.verbose},W(n);}async getExecutedMigrations(){return await c(this.db),(await this.db.selectFrom("migrations").select("name").orderBy("executed_at","asc").execute()).map(n=>n.name)}async markAsExecuted(i){await this.db.insertInto("migrations").values({name:i}).execute();}async markAsRolledBack(i){await this.db.deleteFrom("migrations").where("name","=",i).execute();}logMigrationMeta(i){if(!this.runnerOptions.verbose||!y(i))return;let n=i;if(n.description&&this.logger.info(` Description: ${n.description}`),n.breaking&&this.logger.warn(" BREAKING CHANGE - Review carefully before proceeding"),n.tags&&n.tags.length>0&&this.logger.info(` Tags: ${n.tags.join(", ")}`),n.estimatedDuration){let t=(n.estimatedDuration/1e3).toFixed(1);this.logger.info(` Estimated: ${t}s`);}}async executeMigration(i,n){let t=n==="up"?i.up:i.down;t&&(this.runnerOptions.useTransactions?await this.db.transaction().execute(async r=>{await t(r);}):await t(this.db));}async up(){let i=Date.now(),n={executed:[],skipped:[],failed:[],duration:0,dryRun:this.runnerOptions.dryRun};await c(this.db);let t=await this.getExecutedMigrations();if(this.migrations.filter(e=>!t.includes(e.name)).length===0)return this.logger.info("No pending migrations"),n.skipped=t,n.duration=Date.now()-i,n;this.runnerOptions.dryRun&&this.logger.info("DRY RUN - No changes will be made");for(let e of this.migrations){if(t.includes(e.name)){this.logger.info(`${e.name} (already executed)`),n.skipped.push(e.name);continue}try{this.logger.info(`Running ${e.name}...`),this.logMigrationMeta(e),this.runnerOptions.dryRun||(await this.executeMigration(e,"up"),await this.markAsExecuted(e.name)),this.logger.info(`${e.name} completed`),n.executed.push(e.name);}catch(g){let s=m(g);if(this.logger.error(`${e.name} failed: ${s}`),n.failed.push(e.name),this.runnerOptions.stopOnError)throw new p(`Migration ${e.name} failed: ${s}`,e.name,"up",g instanceof Error?g:void 0)}}return this.runnerOptions.dryRun?this.logger.info("Dry run completed - no changes made"):this.logger.info("All migrations completed successfully"),n.duration=Date.now()-i,n}async down(i=1){let n=Date.now(),t={executed:[],skipped:[],failed:[],duration:0,dryRun:this.runnerOptions.dryRun};await c(this.db);let r=await this.getExecutedMigrations();if(r.length===0)return this.logger.warn("No executed migrations to rollback"),t.duration=Date.now()-n,t;let e=r.slice(-i).reverse();this.runnerOptions.dryRun&&this.logger.info("DRY RUN - No changes will be made");for(let g of e){let s=this.migrations.find(u=>u.name===g);if(!s){this.logger.warn(`Migration ${g} not found in codebase`),t.skipped.push(g);continue}if(!s.down){this.logger.warn(`Migration ${g} has no down method - skipping`),t.skipped.push(g);continue}try{this.logger.info(`Rolling back ${g}...`),this.logMigrationMeta(s),this.runnerOptions.dryRun||(await this.executeMigration(s,"down"),await this.markAsRolledBack(g)),this.logger.info(`${g} rolled back`),t.executed.push(g);}catch(u){let d=m(u);if(this.logger.error(`${g} rollback failed: ${d}`),t.failed.push(g),this.runnerOptions.stopOnError)throw new p(`Rollback of ${g} failed: ${d}`,g,"down",u instanceof Error?u:void 0)}}return this.runnerOptions.dryRun?this.logger.info("Dry run completed - no changes made"):this.logger.info("Rollback completed successfully"),t.duration=Date.now()-n,t}async status(){await c(this.db);let i=await this.getExecutedMigrations(),n=this.migrations.filter(t=>!i.includes(t.name)).map(t=>t.name);if(this.logger.info("Migration Status:"),this.logger.info(` Executed: ${i.length}`),this.logger.info(` Pending: ${n.length}`),this.logger.info(` Total: ${this.migrations.length}`),i.length>0){this.logger.info("Executed migrations:");for(let t of i){let r=this.migrations.find(e=>e.name===t);r&&y(r)&&r.description?this.logger.info(` ${t} - ${r.description}`):this.logger.info(` ${t}`);}}if(n.length>0){this.logger.info("Pending migrations:");for(let t of n){let r=this.migrations.find(e=>e.name===t);if(r&&y(r)){let e=r,g=e.breaking?" BREAKING":"",s=e.description?` - ${e.description}`:"";this.logger.info(` ${t}${s}${g}`);}else this.logger.info(` ${t}`);}}return {executed:i,pending:n,total:this.migrations.length}}async reset(){let i=Date.now();await c(this.db);let n=await this.getExecutedMigrations();if(n.length===0)return this.logger.warn("No migrations to reset"),{executed:[],skipped:[],failed:[],duration:Date.now()-i,dryRun:this.runnerOptions.dryRun};if(this.logger.warn(`Resetting ${n.length} migrations...`),this.runnerOptions.dryRun){this.logger.info("DRY RUN - Would rollback the following migrations:");for(let r of [...n].reverse())this.migrations.find(g=>g.name===r)?.down?this.logger.info(` ${r}`):this.logger.warn(` ${r} (no down method - would be skipped)`);return this.logger.info("Dry run completed - no changes made"),{executed:[],skipped:n,failed:[],duration:Date.now()-i,dryRun:true}}let t=await this.down(n.length);return this.logger.info("All migrations reset"),t}async upTo(i){let n=Date.now(),t={executed:[],skipped:[],failed:[],duration:0,dryRun:this.runnerOptions.dryRun};await c(this.db);let r=await this.getExecutedMigrations(),e=this.migrations.findIndex(s=>s.name===i);if(e===-1)throw new NotFoundError("Migration",{name:i});let g=this.migrations.slice(0,e+1);this.runnerOptions.dryRun&&this.logger.info("DRY RUN - No changes will be made");for(let s of g){if(r.includes(s.name)){this.logger.info(`${s.name} (already executed)`),t.skipped.push(s.name);continue}try{this.logger.info(`Running ${s.name}...`),this.logMigrationMeta(s),this.runnerOptions.dryRun||(await this.executeMigration(s,"up"),await this.markAsExecuted(s.name)),this.logger.info(`${s.name} completed`),t.executed.push(s.name);}catch(u){let d=m(u);throw this.logger.error(`${s.name} failed: ${d}`),t.failed.push(s.name),new p(`Migration ${s.name} failed: ${d}`,s.name,"up",u instanceof Error?u:void 0)}}return this.runnerOptions.dryRun?this.logger.info(`Dry run completed - would migrate up to ${i}`):this.logger.info(`Migrated up to ${i}`),t.duration=Date.now()-n,t}};function G(o,i,n){return new l(o,i,n)}function q(o,i,n){let t={name:o,up:i};return n!==void 0&&(t.down=n),t}function C(o,i){let n={name:o,up:i.up};return i.down!==void 0&&(n.down=i.down),i.description!==void 0&&(n.description=i.description),i.breaking!==void 0&&(n.breaking=i.breaking),i.estimatedDuration!==void 0&&(n.estimatedDuration=i.estimatedDuration),i.tags!==void 0&&(n.tags=i.tags),n}function Y(o){return Object.entries(o).map(([i,n])=>{let t={name:i,up:n.up};return n.down!==void 0&&(t.down=n.down),n.description!==void 0&&(t.description=n.description),n.breaking!==void 0&&(t.breaking=n.breaking),n.estimatedDuration!==void 0&&(t.estimatedDuration=n.estimatedDuration),n.tags!==void 0&&(t.tags=n.tags),t})}async function V(o,i,n){return await new l(o,i,n).up()}async function J(o,i,n=1,t){return await new l(o,i,t).down(n)}async function Q(o,i,n){return await new l(o,i,n).status()}async function X(o,i,n){let t=new w(o,i,n);if(n?.plugins){for(let r of n.plugins)if(r.onInit){let e=r.onInit(t);e instanceof Promise&&await e;}}return t}var w=class extends l{plugins;constructor(i,n,t={}){super(i,n,t),this.plugins=t.plugins??[];}async runBeforeHooks(i,n){for(let t of this.plugins)t.beforeMigration&&await t.beforeMigration(i,n);}async runAfterHooks(i,n,t){for(let r of this.plugins)r.afterMigration&&await r.afterMigration(i,n,t);}async runErrorHooks(i,n,t){for(let r of this.plugins)r.onMigrationError&&await r.onMigrationError(i,n,t);}getPlugins(){return [...this.plugins]}async up(){let i=Date.now(),n={executed:[],skipped:[],failed:[],duration:0,dryRun:this.runnerOptions.dryRun};await c(this.db);let t=await this.getExecutedMigrations();if(this.migrations.filter(e=>!t.includes(e.name)).length===0)return this.logger.info("No pending migrations"),n.skipped=t,n.duration=Date.now()-i,n;this.runnerOptions.dryRun&&this.logger.info("DRY RUN - No changes will be made");for(let e of this.migrations){if(t.includes(e.name)){this.logger.info(`${e.name} (already executed)`),n.skipped.push(e.name);continue}let g=Date.now();try{await this.runBeforeHooks(e,"up"),this.logger.info(`Running ${e.name}...`),this.logMigrationMeta(e),this.runnerOptions.dryRun||(await this.executeMigration(e,"up"),await this.markAsExecuted(e.name));let s=Date.now()-g;await this.runAfterHooks(e,"up",s),this.logger.info(`${e.name} completed`),n.executed.push(e.name);}catch(s){await this.runErrorHooks(e,"up",s);let u=m(s);if(this.logger.error(`${e.name} failed: ${u}`),n.failed.push(e.name),this.runnerOptions.stopOnError)throw new p(`Migration ${e.name} failed: ${u}`,e.name,"up",s instanceof Error?s:void 0)}}return this.runnerOptions.dryRun?this.logger.info("Dry run completed - no changes made"):this.logger.info("All migrations completed successfully"),n.duration=Date.now()-i,n}async down(i=1){let n=Date.now(),t={executed:[],skipped:[],failed:[],duration:0,dryRun:this.runnerOptions.dryRun};await c(this.db);let r=await this.getExecutedMigrations();if(r.length===0)return this.logger.warn("No executed migrations to rollback"),t.duration=Date.now()-n,t;let e=r.slice(-i).reverse();this.runnerOptions.dryRun&&this.logger.info("DRY RUN - No changes will be made");for(let g of e){let s=this.migrations.find(d=>d.name===g);if(!s){this.logger.warn(`Migration ${g} not found in codebase`),t.skipped.push(g);continue}if(!s.down){this.logger.warn(`Migration ${g} has no down method - skipping`),t.skipped.push(g);continue}let u=Date.now();try{await this.runBeforeHooks(s,"down"),this.logger.info(`Rolling back ${g}...`),this.logMigrationMeta(s),this.runnerOptions.dryRun||(await this.executeMigration(s,"down"),await this.markAsRolledBack(g));let d=Date.now()-u;await this.runAfterHooks(s,"down",d),this.logger.info(`${g} rolled back`),t.executed.push(g);}catch(d){await this.runErrorHooks(s,"down",d);let D=m(d);if(this.logger.error(`${g} rollback failed: ${D}`),t.failed.push(g),this.runnerOptions.stopOnError)throw new p(`Rollback of ${g} failed: ${D}`,g,"down",d instanceof Error?d:void 0)}}return this.runnerOptions.dryRun?this.logger.info("Dry run completed - no changes made"):this.logger.info("Rollback completed successfully"),t.duration=Date.now()-n,t}};function Z(o=silentLogger){return {name:"@kysera/migrations/logging",version:h,beforeMigration(i,n){o.info(`Starting ${n} for ${i.name}`);},afterMigration(i,n,t){o.info(`Completed ${n} for ${i.name} in ${t}ms`);},onMigrationError(i,n,t){let r=t instanceof Error?t.message:String(t);o.error(`Error during ${n} for ${i.name}: ${r}`);}}}function nn(){let o=[];return {name:"@kysera/migrations/metrics",version:h,afterMigration(i,n,t){o.push({name:i.name,operation:n,duration:t,success:true});},onMigrationError(i,n){o.push({name:i.name,operation:n,duration:0,success:false});},getMetrics(){return {migrations:[...o]}}}}export{M as MigrationDefinitionSchema,p as MigrationError,B as MigrationPluginOptionsSchema,b as MigrationPluginSchema,E as MigrationResultSchema,l as MigrationRunner,f as MigrationRunnerOptionsSchema,w as MigrationRunnerWithPlugins,$ as MigrationRunnerWithPluginsOptionsSchema,P as MigrationStatusSchema,Z as createLoggingPlugin,nn as createMetricsPlugin,q as createMigration,G as createMigrationRunner,X as createMigrationRunnerWithPlugins,C as createMigrationWithMeta,Y as defineMigrations,Q as getMigrationStatus,I as parseMigrationDefinition,v as parseMigrationRunnerOptions,J as rollbackMigrations,V as runMigrations,N as safeParseMigrationDefinition,S as safeParseMigrationRunnerOptions,c as setupMigrations};//# sourceMappingURL=index.js.map
|
|
2
2
|
//# sourceMappingURL=index.js.map
|