@remix-run/data-table 0.2.0 → 0.3.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 (66) hide show
  1. package/README.md +77 -75
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/lib/adapter.d.ts +10 -271
  5. package/dist/lib/adapter.d.ts.map +1 -1
  6. package/dist/lib/column.d.ts.map +1 -1
  7. package/dist/lib/column.js +10 -1
  8. package/dist/lib/migrations/directive.d.ts +12 -0
  9. package/dist/lib/migrations/directive.d.ts.map +1 -0
  10. package/dist/lib/migrations/directive.js +28 -0
  11. package/dist/lib/migrations/directory-name.d.ts +12 -0
  12. package/dist/lib/migrations/directory-name.d.ts.map +1 -0
  13. package/dist/lib/migrations/directory-name.js +18 -0
  14. package/dist/lib/migrations/journal-store.d.ts +1 -1
  15. package/dist/lib/migrations/journal-store.d.ts.map +1 -1
  16. package/dist/lib/migrations/journal-store.js +18 -18
  17. package/dist/lib/migrations/registry.d.ts +1 -1
  18. package/dist/lib/migrations/registry.js +1 -1
  19. package/dist/lib/migrations/runner.d.ts +2 -2
  20. package/dist/lib/migrations/runner.d.ts.map +1 -1
  21. package/dist/lib/migrations/runner.js +44 -71
  22. package/dist/lib/migrations-node.d.ts +7 -4
  23. package/dist/lib/migrations-node.d.ts.map +1 -1
  24. package/dist/lib/migrations-node.js +52 -41
  25. package/dist/lib/migrations.d.ts +19 -200
  26. package/dist/lib/migrations.d.ts.map +1 -1
  27. package/dist/lib/migrations.js +1 -38
  28. package/dist/lib/query.d.ts +33 -0
  29. package/dist/lib/query.d.ts.map +1 -1
  30. package/dist/lib/query.js +28 -0
  31. package/dist/lib/sql-helpers.d.ts +1 -7
  32. package/dist/lib/sql-helpers.d.ts.map +1 -1
  33. package/dist/lib/sql-helpers.js +0 -16
  34. package/dist/migrations.d.ts +2 -5
  35. package/dist/migrations.d.ts.map +1 -1
  36. package/dist/migrations.js +1 -3
  37. package/dist/sql-helpers.d.ts +1 -1
  38. package/dist/sql-helpers.d.ts.map +1 -1
  39. package/dist/sql-helpers.js +1 -1
  40. package/package.json +7 -8
  41. package/src/index.ts +1 -34
  42. package/src/lib/adapter.ts +10 -330
  43. package/src/lib/column.ts +14 -2
  44. package/src/lib/migrations/directive.ts +38 -0
  45. package/src/lib/migrations/directory-name.ts +23 -0
  46. package/src/lib/migrations/journal-store.ts +22 -18
  47. package/src/lib/migrations/registry.ts +1 -1
  48. package/src/lib/migrations/runner.ts +51 -85
  49. package/src/lib/migrations-node.ts +58 -38
  50. package/src/lib/migrations.ts +19 -224
  51. package/src/lib/query.ts +33 -0
  52. package/src/lib/sql-helpers.ts +1 -22
  53. package/src/migrations.ts +3 -13
  54. package/src/sql-helpers.ts +0 -1
  55. package/dist/lib/migrations/filename.d.ts +0 -12
  56. package/dist/lib/migrations/filename.d.ts.map +0 -1
  57. package/dist/lib/migrations/filename.js +0 -20
  58. package/dist/lib/migrations/helpers.d.ts +0 -11
  59. package/dist/lib/migrations/helpers.d.ts.map +0 -1
  60. package/dist/lib/migrations/helpers.js +0 -77
  61. package/dist/lib/migrations/schema-api.d.ts +0 -7
  62. package/dist/lib/migrations/schema-api.d.ts.map +0 -1
  63. package/dist/lib/migrations/schema-api.js +0 -326
  64. package/src/lib/migrations/filename.ts +0 -25
  65. package/src/lib/migrations/helpers.ts +0 -108
  66. package/src/lib/migrations/schema-api.ts +0 -417
@@ -0,0 +1 @@
1
+ {"version":3,"file":"directive.d.ts","sourceRoot":"","sources":["../../../src/lib/migrations/directive.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAA;AAIhE;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,wBAAwB,GAAG,SAAS,CAwB3F"}
@@ -0,0 +1,28 @@
1
+ const directivePattern = /^\s*--\s*data-table\/transaction\s*:\s*(auto|required|none)\s*$/i;
2
+ /**
3
+ * Parses an optional `-- data-table/transaction: auto|required|none` directive
4
+ * from any single-line SQL comment in the script.
5
+ *
6
+ * Returns `undefined` when no directive is present.
7
+ * @param sql SQL script contents.
8
+ * @returns The declared transaction mode, or `undefined` when not declared.
9
+ * @throws when more than one directive is present or the value is invalid.
10
+ */
11
+ export function parseTransactionDirective(sql) {
12
+ let match;
13
+ for (let line of sql.split(/\r?\n/)) {
14
+ let trimmed = line.trim();
15
+ if (!trimmed.startsWith('--')) {
16
+ continue;
17
+ }
18
+ let result = directivePattern.exec(trimmed);
19
+ if (!result) {
20
+ continue;
21
+ }
22
+ if (match !== undefined) {
23
+ throw new Error('Migration script declares more than one transaction directive');
24
+ }
25
+ match = result[1].toLowerCase();
26
+ }
27
+ return match;
28
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Parses a migration directory name into `{ id, name }`.
3
+ *
4
+ * Expected format: `YYYYMMDDHHmmss_name`.
5
+ * @param name Migration directory basename.
6
+ * @returns Parsed migration id and name.
7
+ */
8
+ export declare function parseMigrationDirectoryName(name: string): {
9
+ id: string;
10
+ name: string;
11
+ };
12
+ //# sourceMappingURL=directory-name.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"directory-name.d.ts","sourceRoot":"","sources":["../../../src/lib/migrations/directory-name.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAatF"}
@@ -0,0 +1,18 @@
1
+ const migrationDirectoryPattern = /^(\d{14})_(.+)$/;
2
+ /**
3
+ * Parses a migration directory name into `{ id, name }`.
4
+ *
5
+ * Expected format: `YYYYMMDDHHmmss_name`.
6
+ * @param name Migration directory basename.
7
+ * @returns Parsed migration id and name.
8
+ */
9
+ export function parseMigrationDirectoryName(name) {
10
+ let match = name.match(migrationDirectoryPattern);
11
+ if (!match) {
12
+ throw new Error('Invalid migration directory name "' + name + '". Expected format YYYYMMDDHHmmss_name');
13
+ }
14
+ return {
15
+ id: match[1],
16
+ name: match[2],
17
+ };
18
+ }
@@ -1,6 +1,6 @@
1
1
  import type { DatabaseAdapter, TransactionToken } from '../adapter.ts';
2
2
  import type { MigrationDescriptor, MigrationJournalRow } from '../migrations.ts';
3
- export declare function normalizeChecksum(migration: MigrationDescriptor): string;
3
+ export declare function computeChecksum(migration: MigrationDescriptor): Promise<string>;
4
4
  export declare function ensureMigrationJournal(adapter: DatabaseAdapter, tableName: string): Promise<void>;
5
5
  export declare function hasMigrationJournal(adapter: DatabaseAdapter, tableName: string): Promise<boolean>;
6
6
  export declare function loadJournalRows(adapter: DatabaseAdapter, tableName: string): Promise<MigrationJournalRow[]>;
@@ -1 +1 @@
1
- {"version":3,"file":"journal-store.d.ts","sourceRoot":"","sources":["../../../src/lib/migrations/journal-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAEtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAEhF,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,mBAAmB,GAAG,MAAM,CAMxE;AAED,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAef;AAED,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,CAalB;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAmBhC;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE;IACH,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;CACd,EACD,WAAW,CAAC,EAAE,gBAAgB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,MAAM,EACV,WAAW,CAAC,EAAE,gBAAgB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAQf;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,mBAAmB,EAAE,GAAG,MAAM,CAO5D"}
1
+ {"version":3,"file":"journal-store.d.ts","sourceRoot":"","sources":["../../../src/lib/migrations/journal-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AAEtE,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAEhF,wBAAsB,eAAe,CAAC,SAAS,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAGrF;AAYD,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAYf;AAED,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,OAAO,CAAC,CAalB;AAED,wBAAsB,eAAe,CACnC,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAmBhC;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE;IACH,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;CACd,EACD,WAAW,CAAC,EAAE,gBAAgB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,MAAM,EACV,WAAW,CAAC,EAAE,gBAAgB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAQf;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,mBAAmB,EAAE,GAAG,MAAM,CAO5D"}
@@ -1,25 +1,25 @@
1
1
  import { rawSql } from "../sql.js";
2
- export function normalizeChecksum(migration) {
3
- if (migration.checksum) {
4
- return migration.checksum;
2
+ export async function computeChecksum(migration) {
3
+ let digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(migration.up));
4
+ return bytesToHex(new Uint8Array(digest));
5
+ }
6
+ function bytesToHex(bytes) {
7
+ let hex = '';
8
+ for (let byte of bytes) {
9
+ hex += byte.toString(16).padStart(2, '0');
5
10
  }
6
- return migration.id + ':' + migration.name;
11
+ return hex;
7
12
  }
8
13
  export async function ensureMigrationJournal(adapter, tableName) {
9
- await adapter.migrate({
10
- operation: {
11
- kind: 'createTable',
12
- table: { name: tableName },
13
- ifNotExists: true,
14
- columns: {
15
- id: { type: 'varchar', length: 64, nullable: false, primaryKey: true },
16
- name: { type: 'varchar', length: 255, nullable: false },
17
- checksum: { type: 'varchar', length: 128, nullable: false },
18
- batch: { type: 'integer', nullable: false },
19
- applied_at: { type: 'timestamp', nullable: false, default: { kind: 'now' } },
20
- },
21
- },
22
- });
14
+ await adapter.executeScript('create table if not exists ' +
15
+ tableName +
16
+ ' (' +
17
+ 'id varchar(64) not null primary key, ' +
18
+ 'name varchar(255) not null, ' +
19
+ 'checksum varchar(128) not null, ' +
20
+ 'batch integer not null, ' +
21
+ 'applied_at timestamp not null default current_timestamp' +
22
+ ')');
23
23
  }
24
24
  export async function hasMigrationJournal(adapter, tableName) {
25
25
  try {
@@ -20,7 +20,7 @@ export declare function resolveMigrations(input: MigrationDescriptor[] | Migrati
20
20
  * import { createMigrationRegistry } from 'remix/data-table/migrations'
21
21
  *
22
22
  * let registry = createMigrationRegistry()
23
- * registry.register({ id, name, migration })
23
+ * registry.register({ id, name, up, down })
24
24
  * ```
25
25
  */
26
26
  export declare function createMigrationRegistry(initial?: MigrationDescriptor[]): MigrationRegistry;
@@ -26,7 +26,7 @@ export function resolveMigrations(input) {
26
26
  * import { createMigrationRegistry } from 'remix/data-table/migrations'
27
27
  *
28
28
  * let registry = createMigrationRegistry()
29
- * registry.register({ id, name, migration })
29
+ * registry.register({ id, name, up, down })
30
30
  * ```
31
31
  */
32
32
  export function createMigrationRegistry(initial = []) {
@@ -1,8 +1,8 @@
1
1
  import type { DatabaseAdapter } from '../adapter.ts';
2
2
  import type { MigrationDescriptor, MigrationRegistry, MigrationRunner, MigrationRunnerOptions } from '../migrations.ts';
3
3
  /**
4
- * Creates a migration runner for applying/reverting migrations against an adapter.
5
- * @param adapter Database adapter used to compile and execute migration operations.
4
+ * Creates a migration runner for applying/reverting SQL migrations against an adapter.
5
+ * @param adapter Database adapter used to execute migration scripts.
6
6
  * @param migrations Migration descriptors or registry.
7
7
  * @param options Optional runner configuration.
8
8
  * @returns A migration runner instance.
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/lib/migrations/runner.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAoB,MAAM,eAAe,CAAA;AAEtE,OAAO,KAAK,EAIV,mBAAmB,EAGnB,iBAAiB,EACjB,eAAe,EACf,sBAAsB,EAGvB,MAAM,kBAAkB,CAAA;AAyRzB;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,eAAe,EACxB,UAAU,EAAE,mBAAmB,EAAE,GAAG,iBAAiB,EACrD,OAAO,GAAE,sBAA2B,GACnC,eAAe,CAwDjB"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/lib/migrations/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAoB,MAAM,eAAe,CAAA;AACtE,OAAO,KAAK,EAGV,mBAAmB,EAGnB,iBAAiB,EACjB,eAAe,EACf,sBAAsB,EAIvB,MAAM,kBAAkB,CAAA;AAqPzB;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,eAAe,EACxB,UAAU,EAAE,mBAAmB,EAAE,GAAG,iBAAiB,EACrD,OAAO,GAAE,sBAA2B,GACnC,eAAe,CA6DjB"}
@@ -1,7 +1,6 @@
1
- import { createDatabase, createDatabaseWithTransaction } from "../database.js";
2
- import { deleteJournalRow, ensureMigrationJournal, getBatch, hasMigrationJournal, insertJournalRow, loadJournalRows, normalizeChecksum, } from "./journal-store.js";
1
+ import { parseTransactionDirective } from "./directive.js";
2
+ import { deleteJournalRow, ensureMigrationJournal, getBatch, hasMigrationJournal, insertJournalRow, loadJournalRows, computeChecksum, } from "./journal-store.js";
3
3
  import { resolveMigrations } from "./registry.js";
4
- import { createMigrationSchema } from "./schema-api.js";
5
4
  function assertStepOption(step) {
6
5
  if (step === undefined) {
7
6
  return;
@@ -24,14 +23,14 @@ function assertTargetOption(migrations, to) {
24
23
  throw new Error('Unknown migration target: ' + to);
25
24
  }
26
25
  }
27
- function assertNoMigrationDrift(migrations, journal) {
26
+ async function assertNoMigrationDrift(migrations, journal) {
28
27
  let migrationMap = new Map(migrations.map((migration) => [migration.id, migration]));
29
28
  for (let row of journal) {
30
29
  let migration = migrationMap.get(row.id);
31
30
  if (!migration) {
32
31
  continue;
33
32
  }
34
- let expected = normalizeChecksum(migration);
33
+ let expected = await computeChecksum(migration);
35
34
  if (expected !== row.checksum) {
36
35
  throw new Error('Migration checksum drift detected for "' +
37
36
  row.id +
@@ -43,33 +42,15 @@ function assertNoMigrationDrift(migrations, journal) {
43
42
  }
44
43
  }
45
44
  }
46
- function createDryRunDatabase(adapter) {
47
- let error = new Error('Cannot execute data operations while running migrations with dryRun');
48
- let throwDryRunError = async () => {
49
- throw error;
50
- };
51
- let dryRunAdapter = {
52
- dialect: adapter.dialect,
53
- capabilities: adapter.capabilities,
54
- compileSql(operation) {
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);
45
+ function resolveTransactionMode(migration) {
46
+ if (migration.transaction) {
47
+ return migration.transaction;
48
+ }
49
+ let directive = parseTransactionDirective(migration.up);
50
+ if (directive) {
51
+ return directive;
52
+ }
53
+ return 'auto';
73
54
  }
74
55
  async function runMigrations(input) {
75
56
  let adapter = input.adapter;
@@ -96,7 +77,7 @@ async function runMigrations(input) {
96
77
  journal = await loadJournalRows(adapter, journalTable);
97
78
  }
98
79
  let appliedMap = new Map(journal.map((row) => [row.id, row]));
99
- assertNoMigrationDrift(migrations, journal);
80
+ await assertNoMigrationDrift(migrations, journal);
100
81
  let toRun = [];
101
82
  if (input.direction === 'up') {
102
83
  for (let migration of migrations) {
@@ -112,58 +93,48 @@ async function runMigrations(input) {
112
93
  }
113
94
  }
114
95
  else {
115
- let appliedMigrations = migrations
116
- .filter((migration) => appliedMap.has(migration.id))
117
- .reverse();
96
+ toRun = migrations.filter((migration) => appliedMap.has(migration.id)).reverse();
118
97
  if (target) {
119
- appliedMigrations = appliedMigrations.filter((migration) => migration.id >= target);
98
+ toRun = toRun.filter((migration) => migration.id >= target);
120
99
  }
121
100
  if (step !== undefined) {
122
- appliedMigrations = appliedMigrations.slice(0, step);
101
+ toRun = toRun.slice(0, step);
102
+ }
103
+ for (let migration of toRun) {
104
+ if (migration.down === undefined) {
105
+ throw new Error('Migration "' + migration.id + '" has no down script');
106
+ }
123
107
  }
124
- toRun = appliedMigrations;
125
108
  }
126
109
  let applied = [];
127
110
  let reverted = [];
128
111
  let batch = getBatch(journal);
129
112
  for (let migration of toRun) {
130
- if (migration.migration.transaction === 'required' &&
131
- !adapter.capabilities.transactionalDdl) {
113
+ let script = (input.direction === 'up' ? migration.up : migration.down);
114
+ let mode = resolveTransactionMode(migration);
115
+ if (mode === 'required' && !adapter.capabilities.transactionalDdl) {
132
116
  throw new Error('Migration "' +
133
117
  migration.id +
134
118
  '" requires transactional DDL, but adapter does not support it');
135
119
  }
136
- let shouldUseTransaction = !dryRun &&
137
- migration.migration.transaction !== 'none' &&
138
- adapter.capabilities.transactionalDdl;
120
+ let shouldUseTransaction = !dryRun && mode !== 'none' && adapter.capabilities.transactionalDdl;
139
121
  let token;
140
122
  if (shouldUseTransaction) {
141
123
  token = await adapter.beginTransaction();
142
124
  }
143
- let db = dryRun
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);
125
+ sql.push(script);
126
+ try {
151
127
  if (!dryRun) {
152
- await adapter.migrate({ operation, transaction: token });
128
+ if (script.trim().length > 0) {
129
+ await adapter.executeScript(script, token);
130
+ }
153
131
  }
154
- }, { transaction: token });
155
- let context = {
156
- db,
157
- schema,
158
- };
159
- try {
160
132
  if (input.direction === 'up') {
161
- await migration.migration.up(context);
162
133
  if (!dryRun) {
163
134
  await insertJournalRow(adapter, journalTable, {
164
135
  id: migration.id,
165
136
  name: migration.name,
166
- checksum: normalizeChecksum(migration),
137
+ checksum: await computeChecksum(migration),
167
138
  batch,
168
139
  }, token);
169
140
  }
@@ -174,7 +145,6 @@ async function runMigrations(input) {
174
145
  });
175
146
  }
176
147
  else {
177
- await migration.migration.down(context);
178
148
  if (!dryRun) {
179
149
  await deleteJournalRow(adapter, journalTable, migration.id, token);
180
150
  }
@@ -206,8 +176,8 @@ async function runMigrations(input) {
206
176
  }
207
177
  }
208
178
  /**
209
- * Creates a migration runner for applying/reverting migrations against an adapter.
210
- * @param adapter Database adapter used to compile and execute migration operations.
179
+ * Creates a migration runner for applying/reverting SQL migrations against an adapter.
180
+ * @param adapter Database adapter used to execute migration scripts.
211
181
  * @param migrations Migration descriptors or registry.
212
182
  * @param options Optional runner configuration.
213
183
  * @returns A migration runner instance.
@@ -247,17 +217,19 @@ export function createMigrationRunner(adapter, migrations, options = {}) {
247
217
  let journal = await loadJournalRows(adapter, journalTable);
248
218
  let journalMap = new Map(journal.map((row) => [row.id, row]));
249
219
  let sortedMigrations = resolveMigrations(migrations);
250
- return sortedMigrations.map((migration) => {
220
+ let statuses = [];
221
+ for (let migration of sortedMigrations) {
251
222
  let journalRow = journalMap.get(migration.id);
252
223
  if (!journalRow) {
253
- return {
224
+ statuses.push({
254
225
  id: migration.id,
255
226
  name: migration.name,
256
227
  status: 'pending',
257
- };
228
+ });
229
+ continue;
258
230
  }
259
- let checksum = normalizeChecksum(migration);
260
- return {
231
+ let checksum = await computeChecksum(migration);
232
+ statuses.push({
261
233
  id: migration.id,
262
234
  name: migration.name,
263
235
  status: checksum === journalRow.checksum
@@ -266,8 +238,9 @@ export function createMigrationRunner(adapter, migrations, options = {}) {
266
238
  appliedAt: journalRow.appliedAt,
267
239
  batch: journalRow.batch,
268
240
  checksum: journalRow.checksum,
269
- };
270
- });
241
+ });
242
+ }
243
+ return statuses;
271
244
  },
272
245
  };
273
246
  }
@@ -1,10 +1,13 @@
1
1
  import type { MigrationDescriptor } from './migrations.ts';
2
2
  /**
3
- * Loads migration modules from a directory on Node.js.
3
+ * Loads SQL-file migrations from a directory on Node.js.
4
4
  *
5
- * Filenames are used to infer migration `id` and `name`.
6
- * Each file must default-export `createMigration(...)`.
7
- * @param directory Absolute or relative directory containing migration files.
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
@@ -1 +1 @@
1
- {"version":3,"file":"migrations-node.d.ts","sourceRoot":"","sources":["../../src/lib/migrations-node.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAa,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAGrE;;;;;;;;;;;;;GAaG;AACH,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAiDtF"}
1
+ {"version":3,"file":"migrations-node.d.ts","sourceRoot":"","sources":["../../src/lib/migrations-node.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAG1D;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC,CA0DtF"}
@@ -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 { pathToFileURL } from 'node:url';
13
- import { parseMigrationFilename } from "./migrations/filename.js";
3
+ import { parseMigrationDirectoryName } from "./migrations/directory-name.js";
14
4
  /**
15
- * Loads migration modules from a directory on Node.js.
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
- * Filenames are used to infer migration `id` and `name`.
18
- * Each file must default-export `createMigration(...)`.
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,58 @@ import { parseMigrationFilename } from "./migrations/filename.js";
26
19
  * ```
27
20
  */
28
21
  export async function loadMigrations(directory) {
29
- let allFiles = (await fs.readdir(directory, { withFileTypes: true }))
30
- .filter((entry) => entry.isFile())
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 entry of files) {
44
- if (seenIds.has(entry.id)) {
45
- throw new Error('Duplicate migration id "' + entry.id + '" inferred from filename "' + entry.file + '"');
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(entry.id);
48
- let fullPath = path.join(directory, entry.file);
49
- let source = await fs.readFile(fullPath, 'utf8');
50
- let checksum = createHash('sha256').update(source).digest('hex');
51
- let module = (await import(__rewriteRelativeImportExtension(pathToFileURL(fullPath).href)));
52
- let migration = module.default;
53
- if (!migration || typeof migration.up !== 'function' || typeof migration.down !== 'function') {
54
- throw new Error('Migration file "' + entry.file + '" must default-export createMigration(...)');
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: entry.id,
58
- name: entry.name,
59
- path: fullPath,
60
- checksum,
61
- migration,
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
+ function isNodeFileNotFoundError(error) {
72
+ return (typeof error === 'object' &&
73
+ error !== null &&
74
+ 'code' in error &&
75
+ error.code === 'ENOENT');
76
+ }