@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.
- package/README.md +251 -123
- package/dist/cli.d.ts +54 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +53 -0
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -7
- package/dist/lib/column.d.ts +1 -1
- package/dist/lib/column.d.ts.map +1 -1
- package/dist/lib/column.js +10 -1
- package/dist/lib/database/execution-context.d.ts +5 -5
- package/dist/lib/database/execution-context.d.ts.map +1 -1
- package/dist/lib/database/execution-context.js +1 -0
- package/dist/lib/database/helpers.js +6 -6
- package/dist/lib/database/query-execution.d.ts.map +1 -1
- package/dist/lib/database/query-execution.js +17 -17
- package/dist/lib/database/relations.js +5 -5
- package/dist/lib/database/write-lifecycle.d.ts +2 -2
- package/dist/lib/database/write-lifecycle.d.ts.map +1 -1
- package/dist/lib/database/write-lifecycle.js +5 -5
- package/dist/lib/database.d.ts +74 -59
- package/dist/lib/database.d.ts.map +1 -1
- package/dist/lib/database.js +176 -83
- package/dist/lib/driver.d.ts +287 -0
- package/dist/lib/driver.d.ts.map +1 -0
- package/dist/lib/errors.d.ts +2 -2
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +4 -4
- package/dist/lib/migrations/directive.d.ts +12 -0
- package/dist/lib/migrations/directive.d.ts.map +1 -0
- package/dist/lib/migrations/directive.js +28 -0
- package/dist/lib/migrations/directory-name.d.ts +12 -0
- package/dist/lib/migrations/directory-name.d.ts.map +1 -0
- package/dist/lib/migrations/directory-name.js +18 -0
- package/dist/lib/migrations/journal-store.d.ts +7 -7
- package/dist/lib/migrations/journal-store.d.ts.map +1 -1
- package/dist/lib/migrations/journal-store.js +37 -28
- package/dist/lib/migrations/registry.d.ts +1 -1
- package/dist/lib/migrations/registry.js +1 -1
- package/dist/lib/migrations/runner.d.ts +12 -19
- package/dist/lib/migrations/runner.d.ts.map +1 -1
- package/dist/lib/migrations/runner.js +170 -175
- package/dist/lib/migrations-node.d.ts +26 -5
- package/dist/lib/migrations-node.d.ts.map +1 -1
- package/dist/lib/migrations-node.js +73 -41
- package/dist/lib/migrations.d.ts +63 -209
- package/dist/lib/migrations.d.ts.map +1 -1
- package/dist/lib/migrations.js +1 -38
- package/dist/lib/operators.d.ts +1 -1
- package/dist/lib/operators.js +1 -1
- package/dist/lib/query.d.ts +34 -1
- package/dist/lib/query.d.ts.map +1 -1
- package/dist/lib/query.js +32 -4
- package/dist/lib/sql-helpers.d.ts +1 -7
- package/dist/lib/sql-helpers.d.ts.map +1 -1
- package/dist/lib/sql-helpers.js +0 -16
- package/dist/lib/sql.d.ts +1 -1
- package/dist/lib/sql.js +1 -1
- package/dist/lib/table.d.ts +1 -1
- package/dist/lib/table.d.ts.map +1 -1
- package/dist/lib/table.js +5 -5
- package/dist/migrations/node.d.ts +1 -1
- package/dist/migrations/node.d.ts.map +1 -1
- package/dist/migrations/node.js +1 -1
- package/dist/migrations.d.ts +2 -6
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +2 -5
- package/dist/operators.js +1 -1
- package/dist/sql-helpers.d.ts +1 -1
- package/dist/sql-helpers.d.ts.map +1 -1
- package/dist/sql-helpers.js +1 -1
- package/package.json +13 -9
- package/src/cli.ts +120 -0
- package/src/index.ts +20 -54
- package/src/lib/column.ts +14 -2
- package/src/lib/database/execution-context.ts +10 -6
- package/src/lib/database/helpers.ts +1 -1
- package/src/lib/database/query-execution.ts +15 -11
- package/src/lib/database/write-lifecycle.ts +4 -4
- package/src/lib/database.ts +223 -96
- package/src/lib/driver.ts +333 -0
- package/src/lib/errors.ts +4 -4
- package/src/lib/migrations/directive.ts +38 -0
- package/src/lib/migrations/directory-name.ts +23 -0
- package/src/lib/migrations/journal-store.ts +42 -28
- package/src/lib/migrations/registry.ts +1 -1
- package/src/lib/migrations/runner.ts +224 -205
- package/src/lib/migrations-node.ts +80 -38
- package/src/lib/migrations.ts +62 -228
- package/src/lib/operators.ts +1 -1
- package/src/lib/query.ts +34 -1
- package/src/lib/sql-helpers.ts +1 -22
- package/src/lib/sql.ts +1 -1
- package/src/lib/table.ts +1 -1
- package/src/migrations/node.ts +1 -1
- package/src/migrations.ts +5 -16
- package/src/sql-helpers.ts +0 -1
- package/dist/lib/adapter.d.ts +0 -550
- package/dist/lib/adapter.d.ts.map +0 -1
- package/dist/lib/migrations/filename.d.ts +0 -12
- package/dist/lib/migrations/filename.d.ts.map +0 -1
- package/dist/lib/migrations/filename.js +0 -20
- package/dist/lib/migrations/helpers.d.ts +0 -11
- package/dist/lib/migrations/helpers.d.ts.map +0 -1
- package/dist/lib/migrations/helpers.js +0 -77
- package/dist/lib/migrations/schema-api.d.ts +0 -7
- package/dist/lib/migrations/schema-api.d.ts.map +0 -1
- package/dist/lib/migrations/schema-api.js +0 -326
- package/src/lib/adapter.ts +0 -653
- package/src/lib/migrations/filename.ts +0 -25
- package/src/lib/migrations/helpers.ts +0 -108
- package/src/lib/migrations/schema-api.ts +0 -417
- /package/dist/lib/{adapter.js → driver.js} +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { deleteJournalRow, ensureMigrationJournal, getBatch, hasMigrationJournal, insertJournalRow, loadJournalRows,
|
|
3
|
-
import { resolveMigrations } from
|
|
4
|
-
import { createMigrationSchema } from "./schema-api.js";
|
|
1
|
+
import { parseTransactionDirective } from './directive.js';
|
|
2
|
+
import { deleteJournalRow, ensureMigrationJournal, getBatch, hasMigrationJournal, insertJournalRow, loadJournalRows, computeChecksum, } from './journal-store.js';
|
|
3
|
+
import { resolveMigrations } from './registry.js';
|
|
5
4
|
function assertStepOption(step) {
|
|
6
5
|
if (step === undefined) {
|
|
7
6
|
return;
|
|
@@ -10,28 +9,42 @@ function assertStepOption(step) {
|
|
|
10
9
|
throw new Error('Invalid migration step option. Expected a positive integer.');
|
|
11
10
|
}
|
|
12
11
|
}
|
|
13
|
-
function
|
|
12
|
+
function assertMigrationOperationOptions(options) {
|
|
14
13
|
if (options.to !== undefined && options.step !== undefined) {
|
|
15
14
|
throw new Error('Cannot combine "to" and "step" migration options in the same run');
|
|
16
15
|
}
|
|
17
16
|
}
|
|
18
|
-
function
|
|
17
|
+
function resolveTargetOption(migrations, to) {
|
|
19
18
|
if (!to) {
|
|
20
|
-
return;
|
|
19
|
+
return undefined;
|
|
21
20
|
}
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
// Accept either a bare migration id or the full `id_name` directory form and
|
|
22
|
+
// normalize to the bare id so range filtering compares ids consistently.
|
|
23
|
+
let matches = migrations.filter((migration) => migration.id === to || migration.id + '_' + migration.name === to);
|
|
24
|
+
if (matches.length === 0) {
|
|
24
25
|
throw new Error('Unknown migration target: ' + to);
|
|
25
26
|
}
|
|
27
|
+
if (matches.length > 1) {
|
|
28
|
+
throw new Error('Ambiguous migration target "' +
|
|
29
|
+
to +
|
|
30
|
+
'". Matches: ' +
|
|
31
|
+
matches.map((migration) => migration.id + '_' + migration.name).join(', '));
|
|
32
|
+
}
|
|
33
|
+
return matches[0].id;
|
|
26
34
|
}
|
|
27
|
-
function
|
|
35
|
+
async function assertMigrationIntegrity(migrations, journal, direction) {
|
|
28
36
|
let migrationMap = new Map(migrations.map((migration) => [migration.id, migration]));
|
|
29
37
|
for (let row of journal) {
|
|
30
38
|
let migration = migrationMap.get(row.id);
|
|
31
39
|
if (!migration) {
|
|
32
|
-
|
|
40
|
+
// Rolling back must stay possible when journal rows have no matching
|
|
41
|
+
// migration files, so only forward runs hard-error on orphaned entries.
|
|
42
|
+
if (direction === 'down') {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
throw new Error('Applied migration "' + row.id + '_' + row.name + '" is missing from current migrations');
|
|
33
46
|
}
|
|
34
|
-
let expected =
|
|
47
|
+
let expected = await computeChecksum(migration);
|
|
35
48
|
if (expected !== row.checksum) {
|
|
36
49
|
throw new Error('Migration checksum drift detected for "' +
|
|
37
50
|
row.id +
|
|
@@ -43,190 +56,154 @@ function assertNoMigrationDrift(migrations, journal) {
|
|
|
43
56
|
}
|
|
44
57
|
}
|
|
45
58
|
}
|
|
46
|
-
function
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return adapter.compileSql(operation);
|
|
56
|
-
},
|
|
57
|
-
async hasTable(table) {
|
|
58
|
-
return adapter.hasTable(table);
|
|
59
|
-
},
|
|
60
|
-
async hasColumn(table, column) {
|
|
61
|
-
return adapter.hasColumn(table, column);
|
|
62
|
-
},
|
|
63
|
-
execute: throwDryRunError,
|
|
64
|
-
migrate: throwDryRunError,
|
|
65
|
-
beginTransaction: throwDryRunError,
|
|
66
|
-
commitTransaction: throwDryRunError,
|
|
67
|
-
rollbackTransaction: throwDryRunError,
|
|
68
|
-
createSavepoint: throwDryRunError,
|
|
69
|
-
rollbackToSavepoint: throwDryRunError,
|
|
70
|
-
releaseSavepoint: throwDryRunError,
|
|
71
|
-
};
|
|
72
|
-
return createDatabase(dryRunAdapter);
|
|
59
|
+
function resolveTransactionMode(migration) {
|
|
60
|
+
if (migration.transaction) {
|
|
61
|
+
return migration.transaction;
|
|
62
|
+
}
|
|
63
|
+
let directive = parseTransactionDirective(migration.up);
|
|
64
|
+
if (directive) {
|
|
65
|
+
return directive;
|
|
66
|
+
}
|
|
67
|
+
return 'auto';
|
|
73
68
|
}
|
|
74
69
|
async function runMigrations(input) {
|
|
75
|
-
|
|
70
|
+
if (input.driver.capabilities.migrationLock && !input.driver.withMigrationLock) {
|
|
71
|
+
throw new Error('Database driver reports migration lock support but does not implement withMigrationLock()');
|
|
72
|
+
}
|
|
73
|
+
if (input.driver.withMigrationLock) {
|
|
74
|
+
return input.driver.withMigrationLock(input.journalTable, (driver) => runMigrationsUnlocked({ ...input, driver }));
|
|
75
|
+
}
|
|
76
|
+
return runMigrationsUnlocked(input);
|
|
77
|
+
}
|
|
78
|
+
async function runMigrationsUnlocked(input) {
|
|
79
|
+
let driver = input.driver;
|
|
76
80
|
let migrations = input.migrations;
|
|
77
81
|
let journalTable = input.journalTable;
|
|
78
82
|
let dryRun = Boolean(input.options.dryRun);
|
|
79
|
-
let target = input.options.to;
|
|
80
83
|
let step = input.options.step;
|
|
81
|
-
|
|
84
|
+
assertMigrationOperationOptions(input.options);
|
|
82
85
|
assertStepOption(step);
|
|
83
|
-
|
|
86
|
+
let target = resolveTargetOption(migrations, input.options.to);
|
|
84
87
|
let sql = [];
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
let
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
88
|
+
let journal = [];
|
|
89
|
+
if (dryRun) {
|
|
90
|
+
let canReadJournal = await hasMigrationJournal(driver, journalTable);
|
|
91
|
+
if (canReadJournal) {
|
|
92
|
+
journal = await loadJournalRows(driver, journalTable);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
await ensureMigrationJournal(driver, journalTable);
|
|
97
|
+
journal = await loadJournalRows(driver, journalTable);
|
|
98
|
+
}
|
|
99
|
+
let appliedMap = new Map(journal.map((row) => [row.id, row]));
|
|
100
|
+
await assertMigrationIntegrity(migrations, journal, input.direction);
|
|
101
|
+
let toRun = [];
|
|
102
|
+
if (input.direction === 'up') {
|
|
103
|
+
for (let migration of migrations) {
|
|
104
|
+
if (!appliedMap.has(migration.id)) {
|
|
105
|
+
toRun.push(migration);
|
|
92
106
|
}
|
|
93
107
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
journal = await loadJournalRows(adapter, journalTable);
|
|
108
|
+
if (target) {
|
|
109
|
+
toRun = toRun.filter((migration) => migration.id <= target);
|
|
97
110
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
let toRun = [];
|
|
101
|
-
if (input.direction === 'up') {
|
|
102
|
-
for (let migration of migrations) {
|
|
103
|
-
if (!appliedMap.has(migration.id)) {
|
|
104
|
-
toRun.push(migration);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
if (target) {
|
|
108
|
-
toRun = toRun.filter((migration) => migration.id <= target);
|
|
109
|
-
}
|
|
110
|
-
if (step !== undefined) {
|
|
111
|
-
toRun = toRun.slice(0, step);
|
|
112
|
-
}
|
|
111
|
+
if (step !== undefined) {
|
|
112
|
+
toRun = toRun.slice(0, step);
|
|
113
113
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
appliedMigrations = appliedMigrations.slice(0, step);
|
|
123
|
-
}
|
|
124
|
-
toRun = appliedMigrations;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
toRun = migrations.filter((migration) => appliedMap.has(migration.id)).reverse();
|
|
117
|
+
if (target) {
|
|
118
|
+
toRun = toRun.filter((migration) => migration.id >= target);
|
|
119
|
+
}
|
|
120
|
+
if (step !== undefined) {
|
|
121
|
+
toRun = toRun.slice(0, step);
|
|
125
122
|
}
|
|
126
|
-
let applied = [];
|
|
127
|
-
let reverted = [];
|
|
128
|
-
let batch = getBatch(journal);
|
|
129
123
|
for (let migration of toRun) {
|
|
130
|
-
if (migration.
|
|
131
|
-
|
|
132
|
-
throw new Error('Migration "' +
|
|
133
|
-
migration.id +
|
|
134
|
-
'" requires transactional DDL, but adapter does not support it');
|
|
124
|
+
if (migration.down === undefined) {
|
|
125
|
+
throw new Error('Migration "' + migration.id + '" has no down script');
|
|
135
126
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
let applied = [];
|
|
130
|
+
let reverted = [];
|
|
131
|
+
let batch = getBatch(journal);
|
|
132
|
+
for (let migration of toRun) {
|
|
133
|
+
let script = (input.direction === 'up' ? migration.up : migration.down);
|
|
134
|
+
let mode = resolveTransactionMode(migration);
|
|
135
|
+
if (mode === 'required' && !driver.capabilities.transactionalDdl) {
|
|
136
|
+
throw new Error('Migration "' +
|
|
137
|
+
migration.id +
|
|
138
|
+
'" requires transactional DDL, but the database does not support it');
|
|
139
|
+
}
|
|
140
|
+
let shouldUseTransaction = !dryRun && mode !== 'none' && driver.capabilities.transactionalDdl;
|
|
141
|
+
let token;
|
|
142
|
+
if (shouldUseTransaction) {
|
|
143
|
+
token = await driver.beginTransaction();
|
|
144
|
+
}
|
|
145
|
+
sql.push(script);
|
|
146
|
+
try {
|
|
147
|
+
if (!dryRun) {
|
|
148
|
+
if (script.trim().length > 0) {
|
|
149
|
+
await driver.executeScript(script, token);
|
|
150
|
+
}
|
|
142
151
|
}
|
|
143
|
-
|
|
144
|
-
? createDryRunDatabase(adapter)
|
|
145
|
-
: token
|
|
146
|
-
? createDatabaseWithTransaction(adapter, token)
|
|
147
|
-
: createDatabase(adapter);
|
|
148
|
-
let schema = createMigrationSchema(db, async (operation) => {
|
|
149
|
-
let compiled = adapter.compileSql(operation);
|
|
150
|
-
sql.push(...compiled);
|
|
152
|
+
if (input.direction === 'up') {
|
|
151
153
|
if (!dryRun) {
|
|
152
|
-
await
|
|
153
|
-
}
|
|
154
|
-
}, { transaction: token });
|
|
155
|
-
let context = {
|
|
156
|
-
db,
|
|
157
|
-
schema,
|
|
158
|
-
};
|
|
159
|
-
try {
|
|
160
|
-
if (input.direction === 'up') {
|
|
161
|
-
await migration.migration.up(context);
|
|
162
|
-
if (!dryRun) {
|
|
163
|
-
await insertJournalRow(adapter, journalTable, {
|
|
164
|
-
id: migration.id,
|
|
165
|
-
name: migration.name,
|
|
166
|
-
checksum: normalizeChecksum(migration),
|
|
167
|
-
batch,
|
|
168
|
-
}, token);
|
|
169
|
-
}
|
|
170
|
-
applied.push({
|
|
154
|
+
await insertJournalRow(driver, journalTable, {
|
|
171
155
|
id: migration.id,
|
|
172
156
|
name: migration.name,
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
else {
|
|
177
|
-
await migration.migration.down(context);
|
|
178
|
-
if (!dryRun) {
|
|
179
|
-
await deleteJournalRow(adapter, journalTable, migration.id, token);
|
|
180
|
-
}
|
|
181
|
-
reverted.push({
|
|
182
|
-
id: migration.id,
|
|
183
|
-
name: migration.name,
|
|
184
|
-
status: 'pending',
|
|
185
|
-
});
|
|
157
|
+
checksum: await computeChecksum(migration),
|
|
158
|
+
batch,
|
|
159
|
+
}, token);
|
|
186
160
|
}
|
|
187
|
-
|
|
188
|
-
|
|
161
|
+
applied.push({
|
|
162
|
+
id: migration.id,
|
|
163
|
+
name: migration.name,
|
|
164
|
+
status: 'applied',
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
if (!dryRun) {
|
|
169
|
+
await deleteJournalRow(driver, journalTable, migration.id, token);
|
|
189
170
|
}
|
|
171
|
+
reverted.push({
|
|
172
|
+
id: migration.id,
|
|
173
|
+
name: migration.name,
|
|
174
|
+
status: 'pending',
|
|
175
|
+
});
|
|
190
176
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
if (token) {
|
|
180
|
+
try {
|
|
181
|
+
await driver.rollbackTransaction(token);
|
|
182
|
+
}
|
|
183
|
+
catch (rollbackError) {
|
|
184
|
+
throw new AggregateError([error, rollbackError], 'Migration and rollback both failed', {
|
|
185
|
+
cause: error,
|
|
186
|
+
});
|
|
194
187
|
}
|
|
195
|
-
throw error;
|
|
196
188
|
}
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
if (token) {
|
|
192
|
+
await driver.commitTransaction(token);
|
|
197
193
|
}
|
|
198
|
-
return {
|
|
199
|
-
applied,
|
|
200
|
-
reverted,
|
|
201
|
-
sql,
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
finally {
|
|
205
|
-
await adapter.releaseMigrationLock?.();
|
|
206
194
|
}
|
|
195
|
+
return {
|
|
196
|
+
applied,
|
|
197
|
+
reverted,
|
|
198
|
+
sql,
|
|
199
|
+
};
|
|
207
200
|
}
|
|
208
|
-
|
|
209
|
-
* Creates a migration runner for applying/reverting migrations against an adapter.
|
|
210
|
-
* @param adapter Database adapter used to compile and execute migration operations.
|
|
211
|
-
* @param migrations Migration descriptors or registry.
|
|
212
|
-
* @param options Optional runner configuration.
|
|
213
|
-
* @returns A migration runner instance.
|
|
214
|
-
* @example
|
|
215
|
-
* ```ts
|
|
216
|
-
* import { createMigrationRunner } from 'remix/data-table/migrations'
|
|
217
|
-
*
|
|
218
|
-
* let runner = createMigrationRunner(adapter, migrations, {
|
|
219
|
-
* journalTable: 'app_migrations',
|
|
220
|
-
* })
|
|
221
|
-
* await runner.up()
|
|
222
|
-
* ```
|
|
223
|
-
*/
|
|
224
|
-
export function createMigrationRunner(adapter, migrations, options = {}) {
|
|
201
|
+
export function createMigrationRunner(driver, migrations, options = {}) {
|
|
225
202
|
let journalTable = options.journalTable ?? 'data_table_migrations';
|
|
226
203
|
return {
|
|
227
204
|
async up(runOptions = {}) {
|
|
228
205
|
return runMigrations({
|
|
229
|
-
|
|
206
|
+
driver,
|
|
230
207
|
migrations: resolveMigrations(migrations),
|
|
231
208
|
journalTable,
|
|
232
209
|
direction: 'up',
|
|
@@ -235,7 +212,7 @@ export function createMigrationRunner(adapter, migrations, options = {}) {
|
|
|
235
212
|
},
|
|
236
213
|
async down(runOptions = {}) {
|
|
237
214
|
return runMigrations({
|
|
238
|
-
|
|
215
|
+
driver,
|
|
239
216
|
migrations: resolveMigrations(migrations),
|
|
240
217
|
journalTable,
|
|
241
218
|
direction: 'down',
|
|
@@ -243,21 +220,25 @@ export function createMigrationRunner(adapter, migrations, options = {}) {
|
|
|
243
220
|
});
|
|
244
221
|
},
|
|
245
222
|
async status() {
|
|
246
|
-
await
|
|
247
|
-
|
|
223
|
+
let journal = (await hasMigrationJournal(driver, journalTable))
|
|
224
|
+
? await loadJournalRows(driver, journalTable)
|
|
225
|
+
: [];
|
|
248
226
|
let journalMap = new Map(journal.map((row) => [row.id, row]));
|
|
249
227
|
let sortedMigrations = resolveMigrations(migrations);
|
|
250
|
-
|
|
228
|
+
let migrationIds = new Set(sortedMigrations.map((migration) => migration.id));
|
|
229
|
+
let statuses = [];
|
|
230
|
+
for (let migration of sortedMigrations) {
|
|
251
231
|
let journalRow = journalMap.get(migration.id);
|
|
252
232
|
if (!journalRow) {
|
|
253
|
-
|
|
233
|
+
statuses.push({
|
|
254
234
|
id: migration.id,
|
|
255
235
|
name: migration.name,
|
|
256
236
|
status: 'pending',
|
|
257
|
-
};
|
|
237
|
+
});
|
|
238
|
+
continue;
|
|
258
239
|
}
|
|
259
|
-
let checksum =
|
|
260
|
-
|
|
240
|
+
let checksum = await computeChecksum(migration);
|
|
241
|
+
statuses.push({
|
|
261
242
|
id: migration.id,
|
|
262
243
|
name: migration.name,
|
|
263
244
|
status: checksum === journalRow.checksum
|
|
@@ -266,8 +247,22 @@ export function createMigrationRunner(adapter, migrations, options = {}) {
|
|
|
266
247
|
appliedAt: journalRow.appliedAt,
|
|
267
248
|
batch: journalRow.batch,
|
|
268
249
|
checksum: journalRow.checksum,
|
|
269
|
-
};
|
|
270
|
-
}
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
for (let journalRow of journal) {
|
|
253
|
+
if (migrationIds.has(journalRow.id)) {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
statuses.push({
|
|
257
|
+
id: journalRow.id,
|
|
258
|
+
name: journalRow.name,
|
|
259
|
+
status: 'missing',
|
|
260
|
+
appliedAt: journalRow.appliedAt,
|
|
261
|
+
batch: journalRow.batch,
|
|
262
|
+
checksum: journalRow.checksum,
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
return statuses.sort((left, right) => left.id.localeCompare(right.id));
|
|
271
266
|
},
|
|
272
267
|
};
|
|
273
268
|
}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
-
import type { MigrationDescriptor } from './migrations.ts';
|
|
1
|
+
import type { MigrationDescriptor, Seed } from './migrations.ts';
|
|
2
2
|
/**
|
|
3
|
-
* Loads
|
|
3
|
+
* Loads SQL-file migrations from a directory on Node.js.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Each migration is a directory named `YYYYMMDDHHmmss_<slug>` containing:
|
|
6
|
+
* - `up.sql` (required)
|
|
7
|
+
* - `down.sql` (optional; omit for irreversible migrations)
|
|
8
|
+
*
|
|
9
|
+
* `id` and `name` are inferred from the directory name.
|
|
10
|
+
* @param directory Absolute or relative directory containing migration directories.
|
|
8
11
|
* @returns A sorted list of loaded migration descriptors.
|
|
9
12
|
* @example
|
|
10
13
|
* ```ts
|
|
@@ -14,4 +17,22 @@ import type { MigrationDescriptor } from './migrations.ts';
|
|
|
14
17
|
* ```
|
|
15
18
|
*/
|
|
16
19
|
export declare function loadMigrations(directory: string): Promise<MigrationDescriptor[]>;
|
|
20
|
+
/**
|
|
21
|
+
* Loads a SQL seed file on Node.js.
|
|
22
|
+
*
|
|
23
|
+
* The file may contain multiple SQL statements. Seeds that must be safe to
|
|
24
|
+
* run against an already-seeded database should use idempotent statements
|
|
25
|
+
* (for example, `insert or ignore` on SQLite).
|
|
26
|
+
*
|
|
27
|
+
* @param filename Absolute or relative path to a SQL seed file.
|
|
28
|
+
* @returns A seed function that executes the file's SQL script.
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* import { loadSeed } from 'remix/data-table/migrations/node'
|
|
32
|
+
*
|
|
33
|
+
* let seed = await loadSeed('./app/data/seed.sql')
|
|
34
|
+
* await db.reset({ migrations, seed })
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function loadSeed(filename: string): Promise<Seed>;
|
|
17
38
|
//# sourceMappingURL=migrations-node.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrations-node.d.ts","sourceRoot":"","sources":["../../src/lib/migrations-node.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"migrations-node.d.ts","sourceRoot":"","sources":["../../src/lib/migrations-node.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAA;AAGhE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC,CA0DtF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAG9D"}
|
|
@@ -1,22 +1,15 @@
|
|
|
1
|
-
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
-
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
-
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
-
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
-
});
|
|
6
|
-
}
|
|
7
|
-
return path;
|
|
8
|
-
};
|
|
9
|
-
import { createHash } from 'node:crypto';
|
|
10
1
|
import { promises as fs } from 'node:fs';
|
|
11
2
|
import path from 'node:path';
|
|
12
|
-
import {
|
|
13
|
-
import { parseMigrationFilename } from "./migrations/filename.js";
|
|
3
|
+
import { parseMigrationDirectoryName } from './migrations/directory-name.js';
|
|
14
4
|
/**
|
|
15
|
-
* Loads
|
|
5
|
+
* Loads SQL-file migrations from a directory on Node.js.
|
|
6
|
+
*
|
|
7
|
+
* Each migration is a directory named `YYYYMMDDHHmmss_<slug>` containing:
|
|
8
|
+
* - `up.sql` (required)
|
|
9
|
+
* - `down.sql` (optional; omit for irreversible migrations)
|
|
16
10
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* @param directory Absolute or relative directory containing migration files.
|
|
11
|
+
* `id` and `name` are inferred from the directory name.
|
|
12
|
+
* @param directory Absolute or relative directory containing migration directories.
|
|
20
13
|
* @returns A sorted list of loaded migration descriptors.
|
|
21
14
|
* @example
|
|
22
15
|
* ```ts
|
|
@@ -26,40 +19,79 @@ import { parseMigrationFilename } from "./migrations/filename.js";
|
|
|
26
19
|
* ```
|
|
27
20
|
*/
|
|
28
21
|
export async function loadMigrations(directory) {
|
|
29
|
-
let
|
|
30
|
-
|
|
22
|
+
let entries = await fs.readdir(directory, { withFileTypes: true });
|
|
23
|
+
let directories = entries
|
|
24
|
+
.filter((entry) => entry.isDirectory())
|
|
31
25
|
.map((entry) => entry.name)
|
|
32
26
|
.sort((left, right) => left.localeCompare(right));
|
|
33
|
-
let files = [];
|
|
34
|
-
for (let file of allFiles) {
|
|
35
|
-
if (!/\.(?:m?ts|m?js|cts|cjs)$/.test(file)) {
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
let parsed = parseMigrationFilename(file);
|
|
39
|
-
files.push({ file, id: parsed.id, name: parsed.name });
|
|
40
|
-
}
|
|
41
27
|
let migrations = [];
|
|
42
28
|
let seenIds = new Set();
|
|
43
|
-
for (let
|
|
44
|
-
|
|
45
|
-
|
|
29
|
+
for (let directoryName of directories) {
|
|
30
|
+
let parsed = parseMigrationDirectoryName(directoryName);
|
|
31
|
+
if (seenIds.has(parsed.id)) {
|
|
32
|
+
throw new Error('Duplicate migration id "' +
|
|
33
|
+
parsed.id +
|
|
34
|
+
'" inferred from directory "' +
|
|
35
|
+
directoryName +
|
|
36
|
+
'"');
|
|
46
37
|
}
|
|
47
|
-
seenIds.add(
|
|
48
|
-
let
|
|
49
|
-
let
|
|
50
|
-
let
|
|
51
|
-
let
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
38
|
+
seenIds.add(parsed.id);
|
|
39
|
+
let directoryPath = path.join(directory, directoryName);
|
|
40
|
+
let upPath = path.join(directoryPath, 'up.sql');
|
|
41
|
+
let downPath = path.join(directoryPath, 'down.sql');
|
|
42
|
+
let up;
|
|
43
|
+
try {
|
|
44
|
+
up = await fs.readFile(upPath, 'utf8');
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (isNodeFileNotFoundError(error)) {
|
|
48
|
+
throw new Error('Migration directory "' + directoryName + '" is missing up.sql');
|
|
49
|
+
}
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
let down;
|
|
53
|
+
try {
|
|
54
|
+
down = await fs.readFile(downPath, 'utf8');
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
if (!isNodeFileNotFoundError(error)) {
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
55
60
|
}
|
|
56
61
|
migrations.push({
|
|
57
|
-
id:
|
|
58
|
-
name:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
id: parsed.id,
|
|
63
|
+
name: parsed.name,
|
|
64
|
+
up,
|
|
65
|
+
down,
|
|
66
|
+
path: directoryPath,
|
|
62
67
|
});
|
|
63
68
|
}
|
|
64
69
|
return migrations;
|
|
65
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Loads a SQL seed file on Node.js.
|
|
73
|
+
*
|
|
74
|
+
* The file may contain multiple SQL statements. Seeds that must be safe to
|
|
75
|
+
* run against an already-seeded database should use idempotent statements
|
|
76
|
+
* (for example, `insert or ignore` on SQLite).
|
|
77
|
+
*
|
|
78
|
+
* @param filename Absolute or relative path to a SQL seed file.
|
|
79
|
+
* @returns A seed function that executes the file's SQL script.
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* import { loadSeed } from 'remix/data-table/migrations/node'
|
|
83
|
+
*
|
|
84
|
+
* let seed = await loadSeed('./app/data/seed.sql')
|
|
85
|
+
* await db.reset({ migrations, seed })
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export async function loadSeed(filename) {
|
|
89
|
+
let sql = await fs.readFile(filename, 'utf8');
|
|
90
|
+
return (db) => db.executeScript(sql);
|
|
91
|
+
}
|
|
92
|
+
function isNodeFileNotFoundError(error) {
|
|
93
|
+
return (typeof error === 'object' &&
|
|
94
|
+
error !== null &&
|
|
95
|
+
'code' in error &&
|
|
96
|
+
error.code === 'ENOENT');
|
|
97
|
+
}
|