@remix-run/data-table 0.2.1 → 0.4.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.
Files changed (113) hide show
  1. package/README.md +251 -123
  2. package/dist/cli.d.ts +54 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +53 -0
  5. package/dist/index.d.ts +5 -4
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +7 -7
  8. package/dist/lib/column.d.ts +1 -1
  9. package/dist/lib/column.d.ts.map +1 -1
  10. package/dist/lib/column.js +10 -1
  11. package/dist/lib/database/execution-context.d.ts +5 -5
  12. package/dist/lib/database/execution-context.d.ts.map +1 -1
  13. package/dist/lib/database/execution-context.js +1 -0
  14. package/dist/lib/database/helpers.js +6 -6
  15. package/dist/lib/database/query-execution.d.ts.map +1 -1
  16. package/dist/lib/database/query-execution.js +17 -17
  17. package/dist/lib/database/relations.js +5 -5
  18. package/dist/lib/database/write-lifecycle.d.ts +2 -2
  19. package/dist/lib/database/write-lifecycle.d.ts.map +1 -1
  20. package/dist/lib/database/write-lifecycle.js +5 -5
  21. package/dist/lib/database.d.ts +74 -59
  22. package/dist/lib/database.d.ts.map +1 -1
  23. package/dist/lib/database.js +176 -83
  24. package/dist/lib/driver.d.ts +287 -0
  25. package/dist/lib/driver.d.ts.map +1 -0
  26. package/dist/lib/errors.d.ts +2 -2
  27. package/dist/lib/errors.d.ts.map +1 -1
  28. package/dist/lib/errors.js +4 -4
  29. package/dist/lib/migrations/directive.d.ts +12 -0
  30. package/dist/lib/migrations/directive.d.ts.map +1 -0
  31. package/dist/lib/migrations/directive.js +28 -0
  32. package/dist/lib/migrations/directory-name.d.ts +12 -0
  33. package/dist/lib/migrations/directory-name.d.ts.map +1 -0
  34. package/dist/lib/migrations/directory-name.js +18 -0
  35. package/dist/lib/migrations/journal-store.d.ts +7 -7
  36. package/dist/lib/migrations/journal-store.d.ts.map +1 -1
  37. package/dist/lib/migrations/journal-store.js +37 -28
  38. package/dist/lib/migrations/registry.d.ts +1 -1
  39. package/dist/lib/migrations/registry.js +1 -1
  40. package/dist/lib/migrations/runner.d.ts +12 -19
  41. package/dist/lib/migrations/runner.d.ts.map +1 -1
  42. package/dist/lib/migrations/runner.js +170 -175
  43. package/dist/lib/migrations-node.d.ts +26 -5
  44. package/dist/lib/migrations-node.d.ts.map +1 -1
  45. package/dist/lib/migrations-node.js +73 -41
  46. package/dist/lib/migrations.d.ts +63 -209
  47. package/dist/lib/migrations.d.ts.map +1 -1
  48. package/dist/lib/migrations.js +1 -38
  49. package/dist/lib/operators.d.ts +1 -1
  50. package/dist/lib/operators.js +1 -1
  51. package/dist/lib/query.d.ts +34 -1
  52. package/dist/lib/query.d.ts.map +1 -1
  53. package/dist/lib/query.js +32 -4
  54. package/dist/lib/sql-helpers.d.ts +1 -7
  55. package/dist/lib/sql-helpers.d.ts.map +1 -1
  56. package/dist/lib/sql-helpers.js +0 -16
  57. package/dist/lib/sql.d.ts +1 -1
  58. package/dist/lib/sql.js +1 -1
  59. package/dist/lib/table.d.ts +1 -1
  60. package/dist/lib/table.d.ts.map +1 -1
  61. package/dist/lib/table.js +5 -5
  62. package/dist/migrations/node.d.ts +1 -1
  63. package/dist/migrations/node.d.ts.map +1 -1
  64. package/dist/migrations/node.js +1 -1
  65. package/dist/migrations.d.ts +2 -6
  66. package/dist/migrations.d.ts.map +1 -1
  67. package/dist/migrations.js +2 -5
  68. package/dist/operators.js +1 -1
  69. package/dist/sql-helpers.d.ts +1 -1
  70. package/dist/sql-helpers.d.ts.map +1 -1
  71. package/dist/sql-helpers.js +1 -1
  72. package/package.json +13 -9
  73. package/src/cli.ts +120 -0
  74. package/src/index.ts +20 -54
  75. package/src/lib/column.ts +14 -2
  76. package/src/lib/database/execution-context.ts +10 -6
  77. package/src/lib/database/helpers.ts +1 -1
  78. package/src/lib/database/query-execution.ts +15 -11
  79. package/src/lib/database/write-lifecycle.ts +4 -4
  80. package/src/lib/database.ts +223 -96
  81. package/src/lib/driver.ts +333 -0
  82. package/src/lib/errors.ts +4 -4
  83. package/src/lib/migrations/directive.ts +38 -0
  84. package/src/lib/migrations/directory-name.ts +23 -0
  85. package/src/lib/migrations/journal-store.ts +42 -28
  86. package/src/lib/migrations/registry.ts +1 -1
  87. package/src/lib/migrations/runner.ts +224 -205
  88. package/src/lib/migrations-node.ts +80 -38
  89. package/src/lib/migrations.ts +62 -228
  90. package/src/lib/operators.ts +1 -1
  91. package/src/lib/query.ts +34 -1
  92. package/src/lib/sql-helpers.ts +1 -22
  93. package/src/lib/sql.ts +1 -1
  94. package/src/lib/table.ts +1 -1
  95. package/src/migrations/node.ts +1 -1
  96. package/src/migrations.ts +5 -16
  97. package/src/sql-helpers.ts +0 -1
  98. package/dist/lib/adapter.d.ts +0 -550
  99. package/dist/lib/adapter.d.ts.map +0 -1
  100. package/dist/lib/migrations/filename.d.ts +0 -12
  101. package/dist/lib/migrations/filename.d.ts.map +0 -1
  102. package/dist/lib/migrations/filename.js +0 -20
  103. package/dist/lib/migrations/helpers.d.ts +0 -11
  104. package/dist/lib/migrations/helpers.d.ts.map +0 -1
  105. package/dist/lib/migrations/helpers.js +0 -77
  106. package/dist/lib/migrations/schema-api.d.ts +0 -7
  107. package/dist/lib/migrations/schema-api.d.ts.map +0 -1
  108. package/dist/lib/migrations/schema-api.js +0 -326
  109. package/src/lib/adapter.ts +0 -653
  110. package/src/lib/migrations/filename.ts +0 -25
  111. package/src/lib/migrations/helpers.ts +0 -108
  112. package/src/lib/migrations/schema-api.ts +0 -417
  113. /package/dist/lib/{adapter.js → driver.js} +0 -0
@@ -1,21 +1,27 @@
1
- import { createDatabase, createDatabaseWithTransaction } from '../database.ts'
2
- import type { Database } from '../database.ts'
3
- import type { DatabaseAdapter, TransactionToken } from '../adapter.ts'
4
- import type { SqlStatement } from '../sql.ts'
1
+ import type { DatabaseDriver, TransactionToken } from '../driver.ts'
5
2
  import type {
6
- MigrateOptions,
3
+ MigrationOperationOptions,
7
4
  MigrateResult,
8
- MigrationContext,
9
5
  MigrationDescriptor,
10
6
  MigrationDirection,
11
7
  MigrationJournalRow,
12
8
  MigrationRegistry,
13
- MigrationRunner,
14
- MigrationRunnerOptions,
15
9
  MigrationStatus,
16
10
  MigrationStatusEntry,
11
+ MigrationTransactionMode,
17
12
  } from '../migrations.ts'
18
13
 
14
+ interface MigrationRunnerOptions {
15
+ journalTable?: string
16
+ }
17
+
18
+ interface MigrationRunner {
19
+ up(options?: MigrationOperationOptions): Promise<MigrateResult>
20
+ down(options?: MigrationOperationOptions): Promise<MigrateResult>
21
+ status(): Promise<MigrationStatusEntry[]>
22
+ }
23
+
24
+ import { parseTransactionDirective } from './directive.ts'
19
25
  import {
20
26
  deleteJournalRow,
21
27
  ensureMigrationJournal,
@@ -23,17 +29,18 @@ import {
23
29
  hasMigrationJournal,
24
30
  insertJournalRow,
25
31
  loadJournalRows,
26
- normalizeChecksum,
32
+ computeChecksum,
27
33
  } from './journal-store.ts'
28
34
  import { resolveMigrations } from './registry.ts'
29
- import { createMigrationSchema } from './schema-api.ts'
35
+
36
+ type MigrationRunnerContext = DatabaseDriver
30
37
 
31
38
  type RunMigrationsInput = {
32
- adapter: DatabaseAdapter
39
+ driver: MigrationRunnerContext
33
40
  migrations: MigrationDescriptor[]
34
41
  journalTable: string
35
42
  direction: MigrationDirection
36
- options: MigrateOptions
43
+ options: MigrationOperationOptions
37
44
  }
38
45
 
39
46
  function assertStepOption(step: number | undefined): void {
@@ -46,38 +53,65 @@ function assertStepOption(step: number | undefined): void {
46
53
  }
47
54
  }
48
55
 
49
- function assertMigrateOptions(options: MigrateOptions): void {
56
+ function assertMigrationOperationOptions(options: MigrationOperationOptions): void {
50
57
  if (options.to !== undefined && options.step !== undefined) {
51
58
  throw new Error('Cannot combine "to" and "step" migration options in the same run')
52
59
  }
53
60
  }
54
61
 
55
- function assertTargetOption(migrations: MigrationDescriptor[], to: string | undefined): void {
62
+ function resolveTargetOption(
63
+ migrations: MigrationDescriptor[],
64
+ to: string | undefined,
65
+ ): string | undefined {
56
66
  if (!to) {
57
- return
67
+ return undefined
58
68
  }
59
69
 
60
- let target = migrations.find((migration) => migration.id === to)
70
+ // Accept either a bare migration id or the full `id_name` directory form and
71
+ // normalize to the bare id so range filtering compares ids consistently.
72
+ let matches = migrations.filter(
73
+ (migration) => migration.id === to || migration.id + '_' + migration.name === to,
74
+ )
61
75
 
62
- if (!target) {
76
+ if (matches.length === 0) {
63
77
  throw new Error('Unknown migration target: ' + to)
64
78
  }
79
+
80
+ if (matches.length > 1) {
81
+ throw new Error(
82
+ 'Ambiguous migration target "' +
83
+ to +
84
+ '". Matches: ' +
85
+ matches.map((migration) => migration.id + '_' + migration.name).join(', '),
86
+ )
87
+ }
88
+
89
+ return matches[0].id
65
90
  }
66
91
 
67
- function assertNoMigrationDrift(
92
+ async function assertMigrationIntegrity(
68
93
  migrations: MigrationDescriptor[],
69
94
  journal: MigrationJournalRow[],
70
- ): void {
95
+ direction: MigrationDirection,
96
+ ): Promise<void> {
71
97
  let migrationMap = new Map(migrations.map((migration) => [migration.id, migration]))
72
98
 
73
99
  for (let row of journal) {
74
100
  let migration = migrationMap.get(row.id)
75
101
 
76
102
  if (!migration) {
77
- continue
103
+ // Rolling back must stay possible when journal rows have no matching
104
+ // migration files, so only forward runs hard-error on orphaned entries.
105
+ if (direction === 'down') {
106
+ continue
107
+ }
108
+
109
+ throw new Error(
110
+ 'Applied migration "' + row.id + '_' + row.name + '" is missing from current migrations',
111
+ )
78
112
  }
79
113
 
80
- let expected = normalizeChecksum(migration)
114
+ let expected = await computeChecksum(migration)
81
115
 
82
116
  if (expected !== row.checksum) {
83
117
  throw new Error(
@@ -93,244 +127,208 @@ function assertNoMigrationDrift(
93
127
  }
94
128
  }
95
129
 
96
- function createDryRunDatabase(adapter: DatabaseAdapter): Database {
97
- let error = new Error('Cannot execute data operations while running migrations with dryRun')
98
- let throwDryRunError = async (): Promise<never> => {
99
- throw error
130
+ function resolveTransactionMode(migration: MigrationDescriptor): MigrationTransactionMode {
131
+ if (migration.transaction) {
132
+ return migration.transaction
100
133
  }
101
- let dryRunAdapter: DatabaseAdapter = {
102
- dialect: adapter.dialect,
103
- capabilities: adapter.capabilities,
104
- compileSql(operation) {
105
- return adapter.compileSql(operation)
106
- },
107
- async hasTable(table) {
108
- return adapter.hasTable(table)
109
- },
110
- async hasColumn(table, column) {
111
- return adapter.hasColumn(table, column)
112
- },
113
- execute: throwDryRunError,
114
- migrate: throwDryRunError,
115
- beginTransaction: throwDryRunError,
116
- commitTransaction: throwDryRunError,
117
- rollbackTransaction: throwDryRunError,
118
- createSavepoint: throwDryRunError,
119
- rollbackToSavepoint: throwDryRunError,
120
- releaseSavepoint: throwDryRunError,
134
+
135
+ let directive = parseTransactionDirective(migration.up)
136
+
137
+ if (directive) {
138
+ return directive
121
139
  }
122
140
 
123
- return createDatabase(dryRunAdapter)
141
+ return 'auto'
124
142
  }
125
143
 
126
144
  async function runMigrations(input: RunMigrationsInput): Promise<MigrateResult> {
127
- let adapter = input.adapter
145
+ if (input.driver.capabilities.migrationLock && !input.driver.withMigrationLock) {
146
+ throw new Error(
147
+ 'Database driver reports migration lock support but does not implement withMigrationLock()',
148
+ )
149
+ }
150
+
151
+ if (input.driver.withMigrationLock) {
152
+ return input.driver.withMigrationLock(input.journalTable, (driver) =>
153
+ runMigrationsUnlocked({ ...input, driver }),
154
+ )
155
+ }
156
+
157
+ return runMigrationsUnlocked(input)
158
+ }
159
+
160
+ async function runMigrationsUnlocked(input: RunMigrationsInput): Promise<MigrateResult> {
161
+ let driver = input.driver
128
162
  let migrations = input.migrations
129
163
  let journalTable = input.journalTable
130
164
  let dryRun = Boolean(input.options.dryRun)
131
- let target = input.options.to
132
165
  let step = input.options.step
133
166
 
134
- assertMigrateOptions(input.options)
167
+ assertMigrationOperationOptions(input.options)
135
168
  assertStepOption(step)
136
- assertTargetOption(migrations, target)
137
169
 
138
- let sql: SqlStatement[] = []
170
+ let target = resolveTargetOption(migrations, input.options.to)
139
171
 
140
- await adapter.acquireMigrationLock?.()
172
+ let sql: string[] = []
141
173
 
142
- try {
143
- let journal: MigrationJournalRow[] = []
174
+ let journal: MigrationJournalRow[] = []
144
175
 
145
- if (dryRun) {
146
- let canReadJournal = await hasMigrationJournal(adapter, journalTable)
176
+ if (dryRun) {
177
+ let canReadJournal = await hasMigrationJournal(driver, journalTable)
147
178
 
148
- if (canReadJournal) {
149
- journal = await loadJournalRows(adapter, journalTable)
150
- }
151
- } else {
152
- await ensureMigrationJournal(adapter, journalTable)
153
- journal = await loadJournalRows(adapter, journalTable)
179
+ if (canReadJournal) {
180
+ journal = await loadJournalRows(driver, journalTable)
154
181
  }
182
+ } else {
183
+ await ensureMigrationJournal(driver, journalTable)
184
+ journal = await loadJournalRows(driver, journalTable)
185
+ }
155
186
 
156
- let appliedMap = new Map(journal.map((row) => [row.id, row]))
157
- assertNoMigrationDrift(migrations, journal)
158
- let toRun: MigrationDescriptor[] = []
159
-
160
- if (input.direction === 'up') {
161
- for (let migration of migrations) {
162
- if (!appliedMap.has(migration.id)) {
163
- toRun.push(migration)
164
- }
165
- }
166
-
167
- if (target) {
168
- toRun = toRun.filter((migration) => migration.id <= target)
169
- }
187
+ let appliedMap = new Map(journal.map((row) => [row.id, row]))
188
+ await assertMigrationIntegrity(migrations, journal, input.direction)
189
+ let toRun: MigrationDescriptor[] = []
170
190
 
171
- if (step !== undefined) {
172
- toRun = toRun.slice(0, step)
191
+ if (input.direction === 'up') {
192
+ for (let migration of migrations) {
193
+ if (!appliedMap.has(migration.id)) {
194
+ toRun.push(migration)
173
195
  }
174
- } else {
175
- let appliedMigrations = migrations
176
- .filter((migration) => appliedMap.has(migration.id))
177
- .reverse()
196
+ }
178
197
 
179
- if (target) {
180
- appliedMigrations = appliedMigrations.filter((migration) => migration.id >= target)
181
- }
198
+ if (target) {
199
+ toRun = toRun.filter((migration) => migration.id <= target)
200
+ }
182
201
 
183
- if (step !== undefined) {
184
- appliedMigrations = appliedMigrations.slice(0, step)
185
- }
202
+ if (step !== undefined) {
203
+ toRun = toRun.slice(0, step)
204
+ }
205
+ } else {
206
+ toRun = migrations.filter((migration) => appliedMap.has(migration.id)).reverse()
186
207
 
187
- toRun = appliedMigrations
208
+ if (target) {
209
+ toRun = toRun.filter((migration) => migration.id >= target)
188
210
  }
189
211
 
190
- let applied: MigrationStatusEntry[] = []
191
- let reverted: MigrationStatusEntry[] = []
192
- let batch = getBatch(journal)
212
+ if (step !== undefined) {
213
+ toRun = toRun.slice(0, step)
214
+ }
193
215
 
194
216
  for (let migration of toRun) {
195
- if (
196
- migration.migration.transaction === 'required' &&
197
- !adapter.capabilities.transactionalDdl
198
- ) {
199
- throw new Error(
200
- 'Migration "' +
201
- migration.id +
202
- '" requires transactional DDL, but adapter does not support it',
203
- )
217
+ if (migration.down === undefined) {
218
+ throw new Error('Migration "' + migration.id + '" has no down script')
204
219
  }
220
+ }
221
+ }
205
222
 
206
- let shouldUseTransaction =
207
- !dryRun &&
208
- migration.migration.transaction !== 'none' &&
209
- adapter.capabilities.transactionalDdl
210
- let token: TransactionToken | undefined
223
+ let applied: MigrationStatusEntry[] = []
224
+ let reverted: MigrationStatusEntry[] = []
225
+ let batch = getBatch(journal)
211
226
 
212
- if (shouldUseTransaction) {
213
- token = await adapter.beginTransaction()
214
- }
227
+ for (let migration of toRun) {
228
+ let script = (input.direction === 'up' ? migration.up : migration.down) as string
229
+ let mode = resolveTransactionMode(migration)
215
230
 
216
- let db = dryRun
217
- ? createDryRunDatabase(adapter)
218
- : token
219
- ? createDatabaseWithTransaction(adapter, token)
220
- : createDatabase(adapter)
221
-
222
- let schema = createMigrationSchema(
223
- db,
224
- async (operation) => {
225
- let compiled = adapter.compileSql(operation)
226
- sql.push(...compiled)
227
-
228
- if (!dryRun) {
229
- await adapter.migrate({ operation, transaction: token })
230
- }
231
- },
232
- { transaction: token },
231
+ if (mode === 'required' && !driver.capabilities.transactionalDdl) {
232
+ throw new Error(
233
+ 'Migration "' +
234
+ migration.id +
235
+ '" requires transactional DDL, but the database does not support it',
233
236
  )
234
- let context: MigrationContext = {
235
- db,
236
- schema,
237
- }
237
+ }
238
238
 
239
- try {
240
- if (input.direction === 'up') {
241
- await migration.migration.up(context)
242
-
243
- if (!dryRun) {
244
- await insertJournalRow(
245
- adapter,
246
- journalTable,
247
- {
248
- id: migration.id,
249
- name: migration.name,
250
- checksum: normalizeChecksum(migration),
251
- batch,
252
- },
253
- token,
254
- )
255
- }
256
-
257
- applied.push({
258
- id: migration.id,
259
- name: migration.name,
260
- status: 'applied',
261
- })
262
- } else {
263
- await migration.migration.down(context)
239
+ let shouldUseTransaction = !dryRun && mode !== 'none' && driver.capabilities.transactionalDdl
240
+ let token: TransactionToken | undefined
264
241
 
265
- if (!dryRun) {
266
- await deleteJournalRow(adapter, journalTable, migration.id, token)
267
- }
242
+ if (shouldUseTransaction) {
243
+ token = await driver.beginTransaction()
244
+ }
268
245
 
269
- reverted.push({
270
- id: migration.id,
271
- name: migration.name,
272
- status: 'pending',
273
- })
246
+ sql.push(script)
247
+
248
+ try {
249
+ if (!dryRun) {
250
+ if (script.trim().length > 0) {
251
+ await driver.executeScript(script, token)
274
252
  }
253
+ }
275
254
 
276
- if (token) {
277
- await adapter.commitTransaction(token)
255
+ if (input.direction === 'up') {
256
+ if (!dryRun) {
257
+ await insertJournalRow(
258
+ driver,
259
+ journalTable,
260
+ {
261
+ id: migration.id,
262
+ name: migration.name,
263
+ checksum: await computeChecksum(migration),
264
+ batch,
265
+ },
266
+ token,
267
+ )
278
268
  }
279
- } catch (error) {
280
- if (token) {
281
- await adapter.rollbackTransaction(token)
269
+
270
+ applied.push({
271
+ id: migration.id,
272
+ name: migration.name,
273
+ status: 'applied',
274
+ })
275
+ } else {
276
+ if (!dryRun) {
277
+ await deleteJournalRow(driver, journalTable, migration.id, token)
282
278
  }
283
279
 
284
- throw error
280
+ reverted.push({
281
+ id: migration.id,
282
+ name: migration.name,
283
+ status: 'pending',
284
+ })
285
285
  }
286
+ } catch (error) {
287
+ if (token) {
288
+ try {
289
+ await driver.rollbackTransaction(token)
290
+ } catch (rollbackError) {
291
+ throw new AggregateError([error, rollbackError], 'Migration and rollback both failed', {
292
+ cause: error,
293
+ })
294
+ }
295
+ }
296
+
297
+ throw error
286
298
  }
287
299
 
288
- return {
289
- applied,
290
- reverted,
291
- sql,
300
+ if (token) {
301
+ await driver.commitTransaction(token)
292
302
  }
293
- } finally {
294
- await adapter.releaseMigrationLock?.()
303
+ }
304
+
305
+ return {
306
+ applied,
307
+ reverted,
308
+ sql,
295
309
  }
296
310
  }
297
311
 
298
- /**
299
- * Creates a migration runner for applying/reverting migrations against an adapter.
300
- * @param adapter Database adapter used to compile and execute migration operations.
301
- * @param migrations Migration descriptors or registry.
302
- * @param options Optional runner configuration.
303
- * @returns A migration runner instance.
304
- * @example
305
- * ```ts
306
- * import { createMigrationRunner } from 'remix/data-table/migrations'
307
- *
308
- * let runner = createMigrationRunner(adapter, migrations, {
309
- * journalTable: 'app_migrations',
310
- * })
311
- * await runner.up()
312
- * ```
313
- */
314
312
  export function createMigrationRunner(
315
- adapter: DatabaseAdapter,
313
+ driver: DatabaseDriver,
316
314
  migrations: MigrationDescriptor[] | MigrationRegistry,
317
315
  options: MigrationRunnerOptions = {},
318
316
  ): MigrationRunner {
319
317
  let journalTable = options.journalTable ?? 'data_table_migrations'
320
318
 
321
319
  return {
322
- async up(runOptions: MigrateOptions = {}): Promise<MigrateResult> {
320
+ async up(runOptions: MigrationOperationOptions = {}): Promise<MigrateResult> {
323
321
  return runMigrations({
324
- adapter,
322
+ driver,
325
323
  migrations: resolveMigrations(migrations),
326
324
  journalTable,
327
325
  direction: 'up',
328
326
  options: runOptions,
329
327
  })
330
328
  },
331
- async down(runOptions: MigrateOptions = {}): Promise<MigrateResult> {
329
+ async down(runOptions: MigrationOperationOptions = {}): Promise<MigrateResult> {
332
330
  return runMigrations({
333
- adapter,
331
+ driver,
334
332
  migrations: resolveMigrations(migrations),
335
333
  journalTable,
336
334
  direction: 'down',
@@ -338,26 +336,30 @@ export function createMigrationRunner(
338
336
  })
339
337
  },
340
338
  async status(): Promise<MigrationStatusEntry[]> {
341
- await ensureMigrationJournal(adapter, journalTable)
342
-
343
- let journal = await loadJournalRows(adapter, journalTable)
339
+ let journal = (await hasMigrationJournal(driver, journalTable))
340
+ ? await loadJournalRows(driver, journalTable)
341
+ : []
344
342
  let journalMap = new Map(journal.map((row) => [row.id, row]))
345
343
  let sortedMigrations = resolveMigrations(migrations)
344
+ let migrationIds = new Set(sortedMigrations.map((migration) => migration.id))
346
345
 
347
- return sortedMigrations.map((migration) => {
346
+ let statuses: MigrationStatusEntry[] = []
347
+
348
+ for (let migration of sortedMigrations) {
348
349
  let journalRow = journalMap.get(migration.id)
349
350
 
350
351
  if (!journalRow) {
351
- return {
352
+ statuses.push({
352
353
  id: migration.id,
353
354
  name: migration.name,
354
355
  status: 'pending' as MigrationStatus,
355
- }
356
+ })
357
+ continue
356
358
  }
357
359
 
358
- let checksum = normalizeChecksum(migration)
360
+ let checksum = await computeChecksum(migration)
359
361
 
360
- return {
362
+ statuses.push({
361
363
  id: migration.id,
362
364
  name: migration.name,
363
365
  status:
@@ -367,8 +369,25 @@ export function createMigrationRunner(
367
369
  appliedAt: journalRow.appliedAt,
368
370
  batch: journalRow.batch,
369
371
  checksum: journalRow.checksum,
372
+ })
373
+ }
374
+
375
+ for (let journalRow of journal) {
376
+ if (migrationIds.has(journalRow.id)) {
377
+ continue
370
378
  }
371
- })
379
+
380
+ statuses.push({
381
+ id: journalRow.id,
382
+ name: journalRow.name,
383
+ status: 'missing',
384
+ appliedAt: journalRow.appliedAt,
385
+ batch: journalRow.batch,
386
+ checksum: journalRow.checksum,
387
+ })
388
+ }
389
+
390
+ return statuses.sort((left, right) => left.id.localeCompare(right.id))
372
391
  },
373
392
  }
374
393
  }