@remix-run/data-table 0.1.0 → 0.2.1
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 +307 -56
- package/dist/index.d.ts +9 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/lib/adapter.d.ts +386 -16
- package/dist/lib/adapter.d.ts.map +1 -1
- 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 +141 -238
- package/dist/lib/database.d.ts.map +1 -1
- package/dist/lib/database.js +73 -1122
- package/dist/lib/errors.d.ts +9 -0
- package/dist/lib/errors.d.ts.map +1 -1
- package/dist/lib/errors.js +9 -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 +3 -0
- package/dist/lib/operators.d.ts.map +1 -1
- 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 +0 -1
- package/dist/lib/references.d.ts.map +1 -1
- 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 +23 -6
- package/dist/lib/sql.d.ts.map +1 -1
- package/dist/lib/sql.js +19 -5
- package/dist/lib/table.d.ts +355 -40
- package/dist/lib/table.d.ts.map +1 -1
- package/dist/lib/table.js +113 -90
- 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 +24 -10
- package/src/index.ts +93 -10
- package/src/lib/adapter.ts +469 -25
- 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 +246 -1848
- package/src/lib/errors.ts +10 -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 +3 -0
- package/src/lib/query.ts +958 -0
- package/src/lib/references.ts +0 -1
- package/src/lib/sql-helpers.ts +146 -0
- package/src/lib/sql.ts +23 -6
- package/src/lib/table.ts +484 -156
- 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
package/dist/lib/errors.d.ts
CHANGED
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
* Base error for all `data-table` failures.
|
|
3
3
|
*/
|
|
4
4
|
export declare class DataTableError extends Error {
|
|
5
|
+
/**
|
|
6
|
+
* Stable error code identifying the failure category.
|
|
7
|
+
*/
|
|
5
8
|
code: string;
|
|
9
|
+
/**
|
|
10
|
+
* Optional structured metadata attached to the failure.
|
|
11
|
+
*/
|
|
6
12
|
metadata?: Record<string, unknown>;
|
|
7
13
|
constructor(message: string, options?: {
|
|
8
14
|
code?: string;
|
|
@@ -14,6 +20,9 @@ export declare class DataTableError extends Error {
|
|
|
14
20
|
* Thrown when input data fails schema validation.
|
|
15
21
|
*/
|
|
16
22
|
export declare class DataTableValidationError extends DataTableError {
|
|
23
|
+
/**
|
|
24
|
+
* Validation issues reported by the schema validator.
|
|
25
|
+
*/
|
|
17
26
|
issues: ReadonlyArray<unknown>;
|
|
18
27
|
constructor(message: string, issues: ReadonlyArray<unknown>, options?: {
|
|
19
28
|
cause?: unknown;
|
package/dist/lib/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACvC,IAAI,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/lib/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACvC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAA;IAEZ;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAElC,YACE,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,KAAK,CAAC,EAAE,OAAO,CAAA;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KACnC,EAMF;CACF;AAED;;GAEG;AACH,qBAAa,wBAAyB,SAAQ,cAAc;IAC1D;;OAEG;IACH,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;IAE9B,YACE,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,aAAa,CAAC,OAAO,CAAC,EAC9B,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,OAAO,CAAA;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KACnC,EAUF;CACF;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,cAAc;IACrD,YACE,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,OAAO,CAAA;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KACnC,EASF;CACF;AAED;;GAEG;AACH,qBAAa,qBAAsB,SAAQ,cAAc;IACvD,YACE,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,OAAO,CAAA;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KACnC,EASF;CACF;AAED;;GAEG;AACH,qBAAa,wBAAyB,SAAQ,cAAc;IAC1D,YACE,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,OAAO,CAAA;QACf,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KACnC,EASF;CACF"}
|
package/dist/lib/errors.js
CHANGED
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
* Base error for all `data-table` failures.
|
|
3
3
|
*/
|
|
4
4
|
export class DataTableError extends Error {
|
|
5
|
+
/**
|
|
6
|
+
* Stable error code identifying the failure category.
|
|
7
|
+
*/
|
|
5
8
|
code;
|
|
9
|
+
/**
|
|
10
|
+
* Optional structured metadata attached to the failure.
|
|
11
|
+
*/
|
|
6
12
|
metadata;
|
|
7
13
|
constructor(message, options) {
|
|
8
14
|
super(message, { cause: options?.cause });
|
|
@@ -15,6 +21,9 @@ export class DataTableError extends Error {
|
|
|
15
21
|
* Thrown when input data fails schema validation.
|
|
16
22
|
*/
|
|
17
23
|
export class DataTableValidationError extends DataTableError {
|
|
24
|
+
/**
|
|
25
|
+
* Validation issues reported by the schema validator.
|
|
26
|
+
*/
|
|
18
27
|
issues;
|
|
19
28
|
constructor(message, issues, options) {
|
|
20
29
|
super(message, {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parses a migration filename into `{ id, name }`.
|
|
3
|
+
*
|
|
4
|
+
* Expected format: `YYYYMMDDHHmmss_name.(ts|js|mts|mjs|cts|cjs)`.
|
|
5
|
+
* @param filename Migration file basename.
|
|
6
|
+
* @returns Parsed migration id and name.
|
|
7
|
+
*/
|
|
8
|
+
export declare function parseMigrationFilename(filename: string): {
|
|
9
|
+
id: string;
|
|
10
|
+
name: string;
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=filename.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filename.d.ts","sourceRoot":"","sources":["../../../src/lib/migrations/filename.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAerF"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const migrationFilenamePattern = /^(\d{14})_(.+)\.(?:m?ts|m?js|cts|cjs)$/;
|
|
2
|
+
/**
|
|
3
|
+
* Parses a migration filename into `{ id, name }`.
|
|
4
|
+
*
|
|
5
|
+
* Expected format: `YYYYMMDDHHmmss_name.(ts|js|mts|mjs|cts|cjs)`.
|
|
6
|
+
* @param filename Migration file basename.
|
|
7
|
+
* @returns Parsed migration id and name.
|
|
8
|
+
*/
|
|
9
|
+
export function parseMigrationFilename(filename) {
|
|
10
|
+
let match = filename.match(migrationFilenamePattern);
|
|
11
|
+
if (!match) {
|
|
12
|
+
throw new Error('Invalid migration filename "' +
|
|
13
|
+
filename +
|
|
14
|
+
'". Expected format YYYYMMDDHHmmss_name.ts (or .js/.mts/.mjs/.cts/.cjs)');
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
id: match[1],
|
|
18
|
+
name: match[2],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TableRef } from '../adapter.ts';
|
|
2
|
+
import type { IndexColumns, KeyColumns } from '../migrations.ts';
|
|
3
|
+
export declare function toTableRef(name: string): TableRef;
|
|
4
|
+
export declare function normalizeIndexColumns(columns: IndexColumns): string[];
|
|
5
|
+
export declare function normalizeKeyColumns(columns: KeyColumns): string[];
|
|
6
|
+
export declare function createPrimaryKeyName(table: TableRef): string;
|
|
7
|
+
export declare function createUniqueName(table: TableRef, columns: string[]): string;
|
|
8
|
+
export declare function createForeignKeyName(table: TableRef, columns: string[], references: TableRef, referenceColumns: string[]): string;
|
|
9
|
+
export declare function createCheckName(table: TableRef, expression: string): string;
|
|
10
|
+
export declare function createIndexName(table: TableRef, columns: string[]): string;
|
|
11
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/lib/migrations/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAEhE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAWjD;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,EAAE,CAErE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,EAAE,CAMjE;AAgDD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAE5D;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAE3E;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,QAAQ,EACf,OAAO,EAAE,MAAM,EAAE,EACjB,UAAU,EAAE,QAAQ,EACpB,gBAAgB,EAAE,MAAM,EAAE,GACzB,MAAM,CAWR;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAG3E;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAE1E"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export function toTableRef(name) {
|
|
2
|
+
let segments = name.split('.');
|
|
3
|
+
if (segments.length === 1) {
|
|
4
|
+
return { name };
|
|
5
|
+
}
|
|
6
|
+
return {
|
|
7
|
+
schema: segments[0],
|
|
8
|
+
name: segments.slice(1).join('.'),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function normalizeIndexColumns(columns) {
|
|
12
|
+
return normalizeKeyColumns(columns);
|
|
13
|
+
}
|
|
14
|
+
export function normalizeKeyColumns(columns) {
|
|
15
|
+
if (Array.isArray(columns)) {
|
|
16
|
+
return [...columns];
|
|
17
|
+
}
|
|
18
|
+
return [columns];
|
|
19
|
+
}
|
|
20
|
+
function normalizeNamePart(value) {
|
|
21
|
+
let normalized = value
|
|
22
|
+
.toLowerCase()
|
|
23
|
+
.replace(/[^a-z0-9]+/g, '_')
|
|
24
|
+
.replace(/^_+|_+$/g, '');
|
|
25
|
+
if (normalized.length === 0) {
|
|
26
|
+
return 'item';
|
|
27
|
+
}
|
|
28
|
+
return normalized;
|
|
29
|
+
}
|
|
30
|
+
function tableNamePart(table) {
|
|
31
|
+
if (table.schema) {
|
|
32
|
+
return normalizeNamePart(table.schema + '_' + table.name);
|
|
33
|
+
}
|
|
34
|
+
return normalizeNamePart(table.name);
|
|
35
|
+
}
|
|
36
|
+
function hashString(value) {
|
|
37
|
+
let hash = 5381;
|
|
38
|
+
for (let index = 0; index < value.length; index++) {
|
|
39
|
+
hash = ((hash << 5) + hash) ^ value.charCodeAt(index);
|
|
40
|
+
}
|
|
41
|
+
return hash >>> 0;
|
|
42
|
+
}
|
|
43
|
+
function withNameLimit(name) {
|
|
44
|
+
let limit = 63;
|
|
45
|
+
if (name.length <= limit) {
|
|
46
|
+
return name;
|
|
47
|
+
}
|
|
48
|
+
let suffix = hashString(name).toString(36).padStart(8, '0').slice(0, 8);
|
|
49
|
+
return name.slice(0, limit - 9) + '_' + suffix;
|
|
50
|
+
}
|
|
51
|
+
function columnsNamePart(columns) {
|
|
52
|
+
return columns.map((column) => normalizeNamePart(column)).join('_');
|
|
53
|
+
}
|
|
54
|
+
export function createPrimaryKeyName(table) {
|
|
55
|
+
return withNameLimit(tableNamePart(table) + '_pk');
|
|
56
|
+
}
|
|
57
|
+
export function createUniqueName(table, columns) {
|
|
58
|
+
return withNameLimit(tableNamePart(table) + '_' + columnsNamePart(columns) + '_uq');
|
|
59
|
+
}
|
|
60
|
+
export function createForeignKeyName(table, columns, references, referenceColumns) {
|
|
61
|
+
let base = tableNamePart(table) +
|
|
62
|
+
'_' +
|
|
63
|
+
columnsNamePart(columns) +
|
|
64
|
+
'_' +
|
|
65
|
+
tableNamePart(references) +
|
|
66
|
+
'_' +
|
|
67
|
+
columnsNamePart(referenceColumns) +
|
|
68
|
+
'_fk';
|
|
69
|
+
return withNameLimit(base);
|
|
70
|
+
}
|
|
71
|
+
export function createCheckName(table, expression) {
|
|
72
|
+
let suffix = hashString(expression).toString(36);
|
|
73
|
+
return withNameLimit(tableNamePart(table) + '_chk_' + suffix);
|
|
74
|
+
}
|
|
75
|
+
export function createIndexName(table, columns) {
|
|
76
|
+
return withNameLimit(tableNamePart(table) + '_' + columnsNamePart(columns) + '_idx');
|
|
77
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DatabaseAdapter, TransactionToken } from '../adapter.ts';
|
|
2
|
+
import type { MigrationDescriptor, MigrationJournalRow } from '../migrations.ts';
|
|
3
|
+
export declare function normalizeChecksum(migration: MigrationDescriptor): string;
|
|
4
|
+
export declare function ensureMigrationJournal(adapter: DatabaseAdapter, tableName: string): Promise<void>;
|
|
5
|
+
export declare function hasMigrationJournal(adapter: DatabaseAdapter, tableName: string): Promise<boolean>;
|
|
6
|
+
export declare function loadJournalRows(adapter: DatabaseAdapter, tableName: string): Promise<MigrationJournalRow[]>;
|
|
7
|
+
export declare function insertJournalRow(adapter: DatabaseAdapter, tableName: string, row: {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
checksum: string;
|
|
11
|
+
batch: number;
|
|
12
|
+
}, transaction?: TransactionToken): Promise<void>;
|
|
13
|
+
export declare function deleteJournalRow(adapter: DatabaseAdapter, tableName: string, id: string, transaction?: TransactionToken): Promise<void>;
|
|
14
|
+
export declare function getBatch(rows: MigrationJournalRow[]): number;
|
|
15
|
+
//# sourceMappingURL=journal-store.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { rawSql } from "../sql.js";
|
|
2
|
+
export function normalizeChecksum(migration) {
|
|
3
|
+
if (migration.checksum) {
|
|
4
|
+
return migration.checksum;
|
|
5
|
+
}
|
|
6
|
+
return migration.id + ':' + migration.name;
|
|
7
|
+
}
|
|
8
|
+
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
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export async function hasMigrationJournal(adapter, tableName) {
|
|
25
|
+
try {
|
|
26
|
+
await adapter.execute({
|
|
27
|
+
operation: {
|
|
28
|
+
kind: 'raw',
|
|
29
|
+
sql: rawSql('select 1 from ' + tableName + ' limit 1'),
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export async function loadJournalRows(adapter, tableName) {
|
|
39
|
+
let result = await adapter.execute({
|
|
40
|
+
operation: {
|
|
41
|
+
kind: 'raw',
|
|
42
|
+
sql: rawSql('select id, name, checksum, batch, applied_at from ' + tableName + ' order by id asc'),
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
let rows = result.rows ?? [];
|
|
46
|
+
return rows.map((row) => ({
|
|
47
|
+
id: String(row.id),
|
|
48
|
+
name: String(row.name),
|
|
49
|
+
checksum: String(row.checksum),
|
|
50
|
+
batch: Number(row.batch),
|
|
51
|
+
appliedAt: new Date(String(row.applied_at)),
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
export async function insertJournalRow(adapter, tableName, row, transaction) {
|
|
55
|
+
await adapter.execute({
|
|
56
|
+
operation: {
|
|
57
|
+
kind: 'raw',
|
|
58
|
+
sql: rawSql('insert into ' + tableName + ' (id, name, checksum, batch) values (?, ?, ?, ?)', [
|
|
59
|
+
row.id,
|
|
60
|
+
row.name,
|
|
61
|
+
row.checksum,
|
|
62
|
+
row.batch,
|
|
63
|
+
]),
|
|
64
|
+
},
|
|
65
|
+
transaction,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
export async function deleteJournalRow(adapter, tableName, id, transaction) {
|
|
69
|
+
await adapter.execute({
|
|
70
|
+
operation: {
|
|
71
|
+
kind: 'raw',
|
|
72
|
+
sql: rawSql('delete from ' + tableName + ' where id = ?', [id]),
|
|
73
|
+
},
|
|
74
|
+
transaction,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
export function getBatch(rows) {
|
|
78
|
+
if (rows.length === 0) {
|
|
79
|
+
return 1;
|
|
80
|
+
}
|
|
81
|
+
let max = Math.max(...rows.map((row) => row.batch));
|
|
82
|
+
return max + 1;
|
|
83
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { MigrationDescriptor, MigrationRegistry } from '../migrations.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Returns a new array of migrations ordered by migration id.
|
|
4
|
+
* @param migrations Migration descriptors to sort.
|
|
5
|
+
* @returns A newly sorted migration descriptor array.
|
|
6
|
+
*/
|
|
7
|
+
export declare function sortMigrations(migrations: MigrationDescriptor[]): MigrationDescriptor[];
|
|
8
|
+
/**
|
|
9
|
+
* Resolves a migration source into a sorted migration list.
|
|
10
|
+
* @param input Migration list or registry.
|
|
11
|
+
* @returns A sorted migration descriptor list.
|
|
12
|
+
*/
|
|
13
|
+
export declare function resolveMigrations(input: MigrationDescriptor[] | MigrationRegistry): MigrationDescriptor[];
|
|
14
|
+
/**
|
|
15
|
+
* Creates an in-memory migration registry.
|
|
16
|
+
* @param initial Optional initial migration list.
|
|
17
|
+
* @returns A migration registry with duplicate-id protection.
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* import { createMigrationRegistry } from 'remix/data-table/migrations'
|
|
21
|
+
*
|
|
22
|
+
* let registry = createMigrationRegistry()
|
|
23
|
+
* registry.register({ id, name, migration })
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function createMigrationRegistry(initial?: MigrationDescriptor[]): MigrationRegistry;
|
|
27
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/lib/migrations/registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AAE9E;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,mBAAmB,EAAE,GAAG,mBAAmB,EAAE,CAEvF;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,mBAAmB,EAAE,GAAG,iBAAiB,GAC/C,mBAAmB,EAAE,CAMvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,GAAE,mBAAmB,EAAO,GAAG,iBAAiB,CAuB9F"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns a new array of migrations ordered by migration id.
|
|
3
|
+
* @param migrations Migration descriptors to sort.
|
|
4
|
+
* @returns A newly sorted migration descriptor array.
|
|
5
|
+
*/
|
|
6
|
+
export function sortMigrations(migrations) {
|
|
7
|
+
return [...migrations].sort((left, right) => left.id.localeCompare(right.id));
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Resolves a migration source into a sorted migration list.
|
|
11
|
+
* @param input Migration list or registry.
|
|
12
|
+
* @returns A sorted migration descriptor list.
|
|
13
|
+
*/
|
|
14
|
+
export function resolveMigrations(input) {
|
|
15
|
+
if (Array.isArray(input)) {
|
|
16
|
+
return sortMigrations(input);
|
|
17
|
+
}
|
|
18
|
+
return input.list();
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Creates an in-memory migration registry.
|
|
22
|
+
* @param initial Optional initial migration list.
|
|
23
|
+
* @returns A migration registry with duplicate-id protection.
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* import { createMigrationRegistry } from 'remix/data-table/migrations'
|
|
27
|
+
*
|
|
28
|
+
* let registry = createMigrationRegistry()
|
|
29
|
+
* registry.register({ id, name, migration })
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export function createMigrationRegistry(initial = []) {
|
|
33
|
+
let migrations = new Map();
|
|
34
|
+
for (let migration of initial) {
|
|
35
|
+
if (migrations.has(migration.id)) {
|
|
36
|
+
throw new Error('Duplicate migration id: ' + migration.id);
|
|
37
|
+
}
|
|
38
|
+
migrations.set(migration.id, migration);
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
register(migration) {
|
|
42
|
+
if (migrations.has(migration.id)) {
|
|
43
|
+
throw new Error('Duplicate migration id: ' + migration.id);
|
|
44
|
+
}
|
|
45
|
+
migrations.set(migration.id, migration);
|
|
46
|
+
},
|
|
47
|
+
list() {
|
|
48
|
+
return sortMigrations(Array.from(migrations.values()));
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { DatabaseAdapter } from '../adapter.ts';
|
|
2
|
+
import type { MigrationDescriptor, MigrationRegistry, MigrationRunner, MigrationRunnerOptions } from '../migrations.ts';
|
|
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.
|
|
6
|
+
* @param migrations Migration descriptors or registry.
|
|
7
|
+
* @param options Optional runner configuration.
|
|
8
|
+
* @returns A migration runner instance.
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { createMigrationRunner } from 'remix/data-table/migrations'
|
|
12
|
+
*
|
|
13
|
+
* let runner = createMigrationRunner(adapter, migrations, {
|
|
14
|
+
* journalTable: 'app_migrations',
|
|
15
|
+
* })
|
|
16
|
+
* await runner.up()
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare function createMigrationRunner(adapter: DatabaseAdapter, migrations: MigrationDescriptor[] | MigrationRegistry, options?: MigrationRunnerOptions): MigrationRunner;
|
|
20
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { createDatabase, createDatabaseWithTransaction } from "../database.js";
|
|
2
|
+
import { deleteJournalRow, ensureMigrationJournal, getBatch, hasMigrationJournal, insertJournalRow, loadJournalRows, normalizeChecksum, } from "./journal-store.js";
|
|
3
|
+
import { resolveMigrations } from "./registry.js";
|
|
4
|
+
import { createMigrationSchema } from "./schema-api.js";
|
|
5
|
+
function assertStepOption(step) {
|
|
6
|
+
if (step === undefined) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
if (!Number.isInteger(step) || step < 1) {
|
|
10
|
+
throw new Error('Invalid migration step option. Expected a positive integer.');
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function assertMigrateOptions(options) {
|
|
14
|
+
if (options.to !== undefined && options.step !== undefined) {
|
|
15
|
+
throw new Error('Cannot combine "to" and "step" migration options in the same run');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function assertTargetOption(migrations, to) {
|
|
19
|
+
if (!to) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
let target = migrations.find((migration) => migration.id === to);
|
|
23
|
+
if (!target) {
|
|
24
|
+
throw new Error('Unknown migration target: ' + to);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function assertNoMigrationDrift(migrations, journal) {
|
|
28
|
+
let migrationMap = new Map(migrations.map((migration) => [migration.id, migration]));
|
|
29
|
+
for (let row of journal) {
|
|
30
|
+
let migration = migrationMap.get(row.id);
|
|
31
|
+
if (!migration) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
let expected = normalizeChecksum(migration);
|
|
35
|
+
if (expected !== row.checksum) {
|
|
36
|
+
throw new Error('Migration checksum drift detected for "' +
|
|
37
|
+
row.id +
|
|
38
|
+
'" (journal=' +
|
|
39
|
+
row.checksum +
|
|
40
|
+
', current=' +
|
|
41
|
+
expected +
|
|
42
|
+
')');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
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);
|
|
73
|
+
}
|
|
74
|
+
async function runMigrations(input) {
|
|
75
|
+
let adapter = input.adapter;
|
|
76
|
+
let migrations = input.migrations;
|
|
77
|
+
let journalTable = input.journalTable;
|
|
78
|
+
let dryRun = Boolean(input.options.dryRun);
|
|
79
|
+
let target = input.options.to;
|
|
80
|
+
let step = input.options.step;
|
|
81
|
+
assertMigrateOptions(input.options);
|
|
82
|
+
assertStepOption(step);
|
|
83
|
+
assertTargetOption(migrations, target);
|
|
84
|
+
let sql = [];
|
|
85
|
+
await adapter.acquireMigrationLock?.();
|
|
86
|
+
try {
|
|
87
|
+
let journal = [];
|
|
88
|
+
if (dryRun) {
|
|
89
|
+
let canReadJournal = await hasMigrationJournal(adapter, journalTable);
|
|
90
|
+
if (canReadJournal) {
|
|
91
|
+
journal = await loadJournalRows(adapter, journalTable);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
await ensureMigrationJournal(adapter, journalTable);
|
|
96
|
+
journal = await loadJournalRows(adapter, journalTable);
|
|
97
|
+
}
|
|
98
|
+
let appliedMap = new Map(journal.map((row) => [row.id, row]));
|
|
99
|
+
assertNoMigrationDrift(migrations, journal);
|
|
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
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
let appliedMigrations = migrations
|
|
116
|
+
.filter((migration) => appliedMap.has(migration.id))
|
|
117
|
+
.reverse();
|
|
118
|
+
if (target) {
|
|
119
|
+
appliedMigrations = appliedMigrations.filter((migration) => migration.id >= target);
|
|
120
|
+
}
|
|
121
|
+
if (step !== undefined) {
|
|
122
|
+
appliedMigrations = appliedMigrations.slice(0, step);
|
|
123
|
+
}
|
|
124
|
+
toRun = appliedMigrations;
|
|
125
|
+
}
|
|
126
|
+
let applied = [];
|
|
127
|
+
let reverted = [];
|
|
128
|
+
let batch = getBatch(journal);
|
|
129
|
+
for (let migration of toRun) {
|
|
130
|
+
if (migration.migration.transaction === 'required' &&
|
|
131
|
+
!adapter.capabilities.transactionalDdl) {
|
|
132
|
+
throw new Error('Migration "' +
|
|
133
|
+
migration.id +
|
|
134
|
+
'" requires transactional DDL, but adapter does not support it');
|
|
135
|
+
}
|
|
136
|
+
let shouldUseTransaction = !dryRun &&
|
|
137
|
+
migration.migration.transaction !== 'none' &&
|
|
138
|
+
adapter.capabilities.transactionalDdl;
|
|
139
|
+
let token;
|
|
140
|
+
if (shouldUseTransaction) {
|
|
141
|
+
token = await adapter.beginTransaction();
|
|
142
|
+
}
|
|
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);
|
|
151
|
+
if (!dryRun) {
|
|
152
|
+
await adapter.migrate({ operation, transaction: token });
|
|
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({
|
|
171
|
+
id: migration.id,
|
|
172
|
+
name: migration.name,
|
|
173
|
+
status: 'applied',
|
|
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
|
+
});
|
|
186
|
+
}
|
|
187
|
+
if (token) {
|
|
188
|
+
await adapter.commitTransaction(token);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
if (token) {
|
|
193
|
+
await adapter.rollbackTransaction(token);
|
|
194
|
+
}
|
|
195
|
+
throw error;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
applied,
|
|
200
|
+
reverted,
|
|
201
|
+
sql,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
finally {
|
|
205
|
+
await adapter.releaseMigrationLock?.();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
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 = {}) {
|
|
225
|
+
let journalTable = options.journalTable ?? 'data_table_migrations';
|
|
226
|
+
return {
|
|
227
|
+
async up(runOptions = {}) {
|
|
228
|
+
return runMigrations({
|
|
229
|
+
adapter,
|
|
230
|
+
migrations: resolveMigrations(migrations),
|
|
231
|
+
journalTable,
|
|
232
|
+
direction: 'up',
|
|
233
|
+
options: runOptions,
|
|
234
|
+
});
|
|
235
|
+
},
|
|
236
|
+
async down(runOptions = {}) {
|
|
237
|
+
return runMigrations({
|
|
238
|
+
adapter,
|
|
239
|
+
migrations: resolveMigrations(migrations),
|
|
240
|
+
journalTable,
|
|
241
|
+
direction: 'down',
|
|
242
|
+
options: runOptions,
|
|
243
|
+
});
|
|
244
|
+
},
|
|
245
|
+
async status() {
|
|
246
|
+
await ensureMigrationJournal(adapter, journalTable);
|
|
247
|
+
let journal = await loadJournalRows(adapter, journalTable);
|
|
248
|
+
let journalMap = new Map(journal.map((row) => [row.id, row]));
|
|
249
|
+
let sortedMigrations = resolveMigrations(migrations);
|
|
250
|
+
return sortedMigrations.map((migration) => {
|
|
251
|
+
let journalRow = journalMap.get(migration.id);
|
|
252
|
+
if (!journalRow) {
|
|
253
|
+
return {
|
|
254
|
+
id: migration.id,
|
|
255
|
+
name: migration.name,
|
|
256
|
+
status: 'pending',
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
let checksum = normalizeChecksum(migration);
|
|
260
|
+
return {
|
|
261
|
+
id: migration.id,
|
|
262
|
+
name: migration.name,
|
|
263
|
+
status: checksum === journalRow.checksum
|
|
264
|
+
? 'applied'
|
|
265
|
+
: 'drifted',
|
|
266
|
+
appliedAt: journalRow.appliedAt,
|
|
267
|
+
batch: journalRow.batch,
|
|
268
|
+
checksum: journalRow.checksum,
|
|
269
|
+
};
|
|
270
|
+
});
|
|
271
|
+
},
|
|
272
|
+
};
|
|
273
|
+
}
|