@remix-run/data-table 0.0.0 → 0.2.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/LICENSE +21 -0
- package/README.md +549 -2
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/lib/adapter.d.ts +550 -0
- package/dist/lib/adapter.d.ts.map +1 -0
- package/dist/lib/adapter.js +1 -0
- package/dist/lib/column.d.ts +193 -0
- package/dist/lib/column.d.ts.map +1 -0
- package/dist/lib/column.js +302 -0
- package/dist/lib/database/execution-context.d.ts +10 -0
- package/dist/lib/database/execution-context.d.ts.map +1 -0
- package/dist/lib/database/execution-context.js +1 -0
- package/dist/lib/database/helpers.d.ts +26 -0
- package/dist/lib/database/helpers.d.ts.map +1 -0
- package/dist/lib/database/helpers.js +116 -0
- package/dist/lib/database/query-execution.d.ts +7 -0
- package/dist/lib/database/query-execution.d.ts.map +1 -0
- package/dist/lib/database/query-execution.js +401 -0
- package/dist/lib/database/relations.d.ts +4 -0
- package/dist/lib/database/relations.d.ts.map +1 -0
- package/dist/lib/database/relations.js +207 -0
- package/dist/lib/database/write-lifecycle.d.ts +13 -0
- package/dist/lib/database/write-lifecycle.d.ts.map +1 -0
- package/dist/lib/database/write-lifecycle.js +279 -0
- package/dist/lib/database.d.ts +264 -0
- package/dist/lib/database.d.ts.map +1 -0
- package/dist/lib/database.js +319 -0
- package/dist/lib/errors.d.ts +59 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +76 -0
- package/dist/lib/inflection.d.ts +3 -0
- package/dist/lib/inflection.d.ts.map +1 -0
- package/dist/lib/inflection.js +56 -0
- package/dist/lib/migrations/filename.d.ts +12 -0
- package/dist/lib/migrations/filename.d.ts.map +1 -0
- package/dist/lib/migrations/filename.js +20 -0
- package/dist/lib/migrations/helpers.d.ts +11 -0
- package/dist/lib/migrations/helpers.d.ts.map +1 -0
- package/dist/lib/migrations/helpers.js +77 -0
- package/dist/lib/migrations/journal-store.d.ts +15 -0
- package/dist/lib/migrations/journal-store.d.ts.map +1 -0
- package/dist/lib/migrations/journal-store.js +83 -0
- package/dist/lib/migrations/registry.d.ts +27 -0
- package/dist/lib/migrations/registry.d.ts.map +1 -0
- package/dist/lib/migrations/registry.js +51 -0
- package/dist/lib/migrations/runner.d.ts +20 -0
- package/dist/lib/migrations/runner.d.ts.map +1 -0
- package/dist/lib/migrations/runner.js +273 -0
- package/dist/lib/migrations/schema-api.d.ts +7 -0
- package/dist/lib/migrations/schema-api.d.ts.map +1 -0
- package/dist/lib/migrations/schema-api.js +326 -0
- package/dist/lib/migrations-node.d.ts +17 -0
- package/dist/lib/migrations-node.d.ts.map +1 -0
- package/dist/lib/migrations-node.js +65 -0
- package/dist/lib/migrations.d.ts +292 -0
- package/dist/lib/migrations.d.ts.map +1 -0
- package/dist/lib/migrations.js +38 -0
- package/dist/lib/operators.d.ts +154 -0
- package/dist/lib/operators.d.ts.map +1 -0
- package/dist/lib/operators.js +218 -0
- package/dist/lib/query.d.ts +159 -0
- package/dist/lib/query.d.ts.map +1 -0
- package/dist/lib/query.js +401 -0
- package/dist/lib/references.d.ts +41 -0
- package/dist/lib/references.d.ts.map +1 -0
- package/dist/lib/references.js +33 -0
- package/dist/lib/sql-helpers.d.ts +50 -0
- package/dist/lib/sql-helpers.d.ts.map +1 -0
- package/dist/lib/sql-helpers.js +111 -0
- package/dist/lib/sql.d.ts +45 -0
- package/dist/lib/sql.d.ts.map +1 -0
- package/dist/lib/sql.js +65 -0
- package/dist/lib/table.d.ts +569 -0
- package/dist/lib/table.d.ts.map +1 -0
- package/dist/lib/table.js +519 -0
- package/dist/lib/types.d.ts +4 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +1 -0
- package/dist/migrations/node.d.ts +2 -0
- package/dist/migrations/node.d.ts.map +1 -0
- package/dist/migrations/node.js +1 -0
- package/dist/migrations.d.ts +8 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +5 -0
- package/dist/operators.d.ts +3 -0
- package/dist/operators.d.ts.map +1 -0
- package/dist/operators.js +1 -0
- package/dist/sql-helpers.d.ts +3 -0
- package/dist/sql-helpers.d.ts.map +1 -0
- package/dist/sql-helpers.js +1 -0
- package/package.json +56 -7
- package/src/index.ts +198 -0
- package/src/lib/adapter.ts +653 -0
- package/src/lib/column.ts +384 -0
- package/src/lib/database/execution-context.ts +15 -0
- package/src/lib/database/helpers.ts +216 -0
- package/src/lib/database/query-execution.ts +638 -0
- package/src/lib/database/relations.ts +332 -0
- package/src/lib/database/write-lifecycle.ts +487 -0
- package/src/lib/database.ts +856 -0
- package/src/lib/errors.ts +119 -0
- package/src/lib/inflection.ts +69 -0
- package/src/lib/migrations/filename.ts +25 -0
- package/src/lib/migrations/helpers.ts +108 -0
- package/src/lib/migrations/journal-store.ts +122 -0
- package/src/lib/migrations/registry.ts +62 -0
- package/src/lib/migrations/runner.ts +374 -0
- package/src/lib/migrations/schema-api.ts +417 -0
- package/src/lib/migrations-node.ts +71 -0
- package/src/lib/migrations.ts +328 -0
- package/src/lib/operators.ts +436 -0
- package/src/lib/query.ts +958 -0
- package/src/lib/references.ts +78 -0
- package/src/lib/sql-helpers.ts +146 -0
- package/src/lib/sql.ts +84 -0
- package/src/lib/table.ts +1309 -0
- package/src/lib/types.ts +3 -0
- package/src/migrations/node.ts +1 -0
- package/src/migrations.ts +26 -0
- package/src/operators.ts +18 -0
- package/src/sql-helpers.ts +9 -0
|
@@ -0,0 +1,374 @@
|
|
|
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'
|
|
5
|
+
import type {
|
|
6
|
+
MigrateOptions,
|
|
7
|
+
MigrateResult,
|
|
8
|
+
MigrationContext,
|
|
9
|
+
MigrationDescriptor,
|
|
10
|
+
MigrationDirection,
|
|
11
|
+
MigrationJournalRow,
|
|
12
|
+
MigrationRegistry,
|
|
13
|
+
MigrationRunner,
|
|
14
|
+
MigrationRunnerOptions,
|
|
15
|
+
MigrationStatus,
|
|
16
|
+
MigrationStatusEntry,
|
|
17
|
+
} from '../migrations.ts'
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
deleteJournalRow,
|
|
21
|
+
ensureMigrationJournal,
|
|
22
|
+
getBatch,
|
|
23
|
+
hasMigrationJournal,
|
|
24
|
+
insertJournalRow,
|
|
25
|
+
loadJournalRows,
|
|
26
|
+
normalizeChecksum,
|
|
27
|
+
} from './journal-store.ts'
|
|
28
|
+
import { resolveMigrations } from './registry.ts'
|
|
29
|
+
import { createMigrationSchema } from './schema-api.ts'
|
|
30
|
+
|
|
31
|
+
type RunMigrationsInput = {
|
|
32
|
+
adapter: DatabaseAdapter
|
|
33
|
+
migrations: MigrationDescriptor[]
|
|
34
|
+
journalTable: string
|
|
35
|
+
direction: MigrationDirection
|
|
36
|
+
options: MigrateOptions
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function assertStepOption(step: number | undefined): void {
|
|
40
|
+
if (step === undefined) {
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!Number.isInteger(step) || step < 1) {
|
|
45
|
+
throw new Error('Invalid migration step option. Expected a positive integer.')
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function assertMigrateOptions(options: MigrateOptions): void {
|
|
50
|
+
if (options.to !== undefined && options.step !== undefined) {
|
|
51
|
+
throw new Error('Cannot combine "to" and "step" migration options in the same run')
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function assertTargetOption(migrations: MigrationDescriptor[], to: string | undefined): void {
|
|
56
|
+
if (!to) {
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let target = migrations.find((migration) => migration.id === to)
|
|
61
|
+
|
|
62
|
+
if (!target) {
|
|
63
|
+
throw new Error('Unknown migration target: ' + to)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function assertNoMigrationDrift(
|
|
68
|
+
migrations: MigrationDescriptor[],
|
|
69
|
+
journal: MigrationJournalRow[],
|
|
70
|
+
): void {
|
|
71
|
+
let migrationMap = new Map(migrations.map((migration) => [migration.id, migration]))
|
|
72
|
+
|
|
73
|
+
for (let row of journal) {
|
|
74
|
+
let migration = migrationMap.get(row.id)
|
|
75
|
+
|
|
76
|
+
if (!migration) {
|
|
77
|
+
continue
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let expected = normalizeChecksum(migration)
|
|
81
|
+
|
|
82
|
+
if (expected !== row.checksum) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
'Migration checksum drift detected for "' +
|
|
85
|
+
row.id +
|
|
86
|
+
'" (journal=' +
|
|
87
|
+
row.checksum +
|
|
88
|
+
', current=' +
|
|
89
|
+
expected +
|
|
90
|
+
')',
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
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
|
|
100
|
+
}
|
|
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,
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return createDatabase(dryRunAdapter)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function runMigrations(input: RunMigrationsInput): Promise<MigrateResult> {
|
|
127
|
+
let adapter = input.adapter
|
|
128
|
+
let migrations = input.migrations
|
|
129
|
+
let journalTable = input.journalTable
|
|
130
|
+
let dryRun = Boolean(input.options.dryRun)
|
|
131
|
+
let target = input.options.to
|
|
132
|
+
let step = input.options.step
|
|
133
|
+
|
|
134
|
+
assertMigrateOptions(input.options)
|
|
135
|
+
assertStepOption(step)
|
|
136
|
+
assertTargetOption(migrations, target)
|
|
137
|
+
|
|
138
|
+
let sql: SqlStatement[] = []
|
|
139
|
+
|
|
140
|
+
await adapter.acquireMigrationLock?.()
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
let journal: MigrationJournalRow[] = []
|
|
144
|
+
|
|
145
|
+
if (dryRun) {
|
|
146
|
+
let canReadJournal = await hasMigrationJournal(adapter, journalTable)
|
|
147
|
+
|
|
148
|
+
if (canReadJournal) {
|
|
149
|
+
journal = await loadJournalRows(adapter, journalTable)
|
|
150
|
+
}
|
|
151
|
+
} else {
|
|
152
|
+
await ensureMigrationJournal(adapter, journalTable)
|
|
153
|
+
journal = await loadJournalRows(adapter, journalTable)
|
|
154
|
+
}
|
|
155
|
+
|
|
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
|
+
}
|
|
170
|
+
|
|
171
|
+
if (step !== undefined) {
|
|
172
|
+
toRun = toRun.slice(0, step)
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
let appliedMigrations = migrations
|
|
176
|
+
.filter((migration) => appliedMap.has(migration.id))
|
|
177
|
+
.reverse()
|
|
178
|
+
|
|
179
|
+
if (target) {
|
|
180
|
+
appliedMigrations = appliedMigrations.filter((migration) => migration.id >= target)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (step !== undefined) {
|
|
184
|
+
appliedMigrations = appliedMigrations.slice(0, step)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
toRun = appliedMigrations
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
let applied: MigrationStatusEntry[] = []
|
|
191
|
+
let reverted: MigrationStatusEntry[] = []
|
|
192
|
+
let batch = getBatch(journal)
|
|
193
|
+
|
|
194
|
+
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
|
+
)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
let shouldUseTransaction =
|
|
207
|
+
!dryRun &&
|
|
208
|
+
migration.migration.transaction !== 'none' &&
|
|
209
|
+
adapter.capabilities.transactionalDdl
|
|
210
|
+
let token: TransactionToken | undefined
|
|
211
|
+
|
|
212
|
+
if (shouldUseTransaction) {
|
|
213
|
+
token = await adapter.beginTransaction()
|
|
214
|
+
}
|
|
215
|
+
|
|
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 },
|
|
233
|
+
)
|
|
234
|
+
let context: MigrationContext = {
|
|
235
|
+
db,
|
|
236
|
+
schema,
|
|
237
|
+
}
|
|
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)
|
|
264
|
+
|
|
265
|
+
if (!dryRun) {
|
|
266
|
+
await deleteJournalRow(adapter, journalTable, migration.id, token)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
reverted.push({
|
|
270
|
+
id: migration.id,
|
|
271
|
+
name: migration.name,
|
|
272
|
+
status: 'pending',
|
|
273
|
+
})
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (token) {
|
|
277
|
+
await adapter.commitTransaction(token)
|
|
278
|
+
}
|
|
279
|
+
} catch (error) {
|
|
280
|
+
if (token) {
|
|
281
|
+
await adapter.rollbackTransaction(token)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
throw error
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
applied,
|
|
290
|
+
reverted,
|
|
291
|
+
sql,
|
|
292
|
+
}
|
|
293
|
+
} finally {
|
|
294
|
+
await adapter.releaseMigrationLock?.()
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
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
|
+
export function createMigrationRunner(
|
|
315
|
+
adapter: DatabaseAdapter,
|
|
316
|
+
migrations: MigrationDescriptor[] | MigrationRegistry,
|
|
317
|
+
options: MigrationRunnerOptions = {},
|
|
318
|
+
): MigrationRunner {
|
|
319
|
+
let journalTable = options.journalTable ?? 'data_table_migrations'
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
async up(runOptions: MigrateOptions = {}): Promise<MigrateResult> {
|
|
323
|
+
return runMigrations({
|
|
324
|
+
adapter,
|
|
325
|
+
migrations: resolveMigrations(migrations),
|
|
326
|
+
journalTable,
|
|
327
|
+
direction: 'up',
|
|
328
|
+
options: runOptions,
|
|
329
|
+
})
|
|
330
|
+
},
|
|
331
|
+
async down(runOptions: MigrateOptions = {}): Promise<MigrateResult> {
|
|
332
|
+
return runMigrations({
|
|
333
|
+
adapter,
|
|
334
|
+
migrations: resolveMigrations(migrations),
|
|
335
|
+
journalTable,
|
|
336
|
+
direction: 'down',
|
|
337
|
+
options: runOptions,
|
|
338
|
+
})
|
|
339
|
+
},
|
|
340
|
+
async status(): Promise<MigrationStatusEntry[]> {
|
|
341
|
+
await ensureMigrationJournal(adapter, journalTable)
|
|
342
|
+
|
|
343
|
+
let journal = await loadJournalRows(adapter, journalTable)
|
|
344
|
+
let journalMap = new Map(journal.map((row) => [row.id, row]))
|
|
345
|
+
let sortedMigrations = resolveMigrations(migrations)
|
|
346
|
+
|
|
347
|
+
return sortedMigrations.map((migration) => {
|
|
348
|
+
let journalRow = journalMap.get(migration.id)
|
|
349
|
+
|
|
350
|
+
if (!journalRow) {
|
|
351
|
+
return {
|
|
352
|
+
id: migration.id,
|
|
353
|
+
name: migration.name,
|
|
354
|
+
status: 'pending' as MigrationStatus,
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
let checksum = normalizeChecksum(migration)
|
|
359
|
+
|
|
360
|
+
return {
|
|
361
|
+
id: migration.id,
|
|
362
|
+
name: migration.name,
|
|
363
|
+
status:
|
|
364
|
+
checksum === journalRow.checksum
|
|
365
|
+
? ('applied' as MigrationStatus)
|
|
366
|
+
: ('drifted' as MigrationStatus),
|
|
367
|
+
appliedAt: journalRow.appliedAt,
|
|
368
|
+
batch: journalRow.batch,
|
|
369
|
+
checksum: journalRow.checksum,
|
|
370
|
+
}
|
|
371
|
+
})
|
|
372
|
+
},
|
|
373
|
+
}
|
|
374
|
+
}
|