@danceroutine/tango-migrations 1.11.1 → 1.11.2
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/dist/Builder-y8vj7XXN.d.ts +25 -0
- package/dist/{CollectingBuilder--4fqDQdE.js → CollectingBuilder-BIfAKs_x.js} +6 -4
- package/dist/CollectingBuilder-BIfAKs_x.js.map +1 -0
- package/dist/CompilerFactory-Czv-zEOS.d.ts +30 -0
- package/dist/CompilerStrategy-DqmcqAC-.d.ts +44 -0
- package/dist/{CompilerStrategy-_AiXiyjS.js → CompilerStrategy-vcZKg8qf.js} +14 -10
- package/dist/CompilerStrategy-vcZKg8qf.js.map +1 -0
- package/dist/Dialect-Cp4r7UfW.d.ts +12 -0
- package/dist/{InternalColumnType-G9zV9StN.js → InternalColumnType-Dzs9T6a6.js} +3 -4
- package/dist/{InternalColumnType-G9zV9StN.js.map → InternalColumnType-Dzs9T6a6.js.map} +1 -1
- package/dist/{InternalOperationKind-Bt6Weuon.js → InternalOperationKind-M4a4H9OZ.js} +3 -4
- package/dist/{InternalOperationKind-Bt6Weuon.js.map → InternalOperationKind-M4a4H9OZ.js.map} +1 -1
- package/dist/{IntrospectorStrategy-BEIG5GqA.js → IntrospectorStrategy-BijuyIaN.js} +14 -9
- package/dist/IntrospectorStrategy-BijuyIaN.js.map +1 -0
- package/dist/{Migration-DYQ0hUG7.js → Migration-DxHHPyzn.js} +9 -4
- package/dist/Migration-DxHHPyzn.js.map +1 -0
- package/dist/{MigrationGenerator-B1p0jHnx.js → MigrationGenerator-BmmerPXJ.js} +24 -35
- package/dist/MigrationGenerator-BmmerPXJ.js.map +1 -0
- package/dist/MigrationOperation-qpdhPEs9.d.ts +145 -0
- package/dist/{MigrationRunner-DomrOZIn.js → MigrationRunner-B5AJel12.js} +52 -24
- package/dist/MigrationRunner-B5AJel12.js.map +1 -0
- package/dist/{MigrationSqlSafetyAdapter-CGRbB2k2.js → MigrationSqlSafetyAdapter-yP6fPjeC.js} +13 -9
- package/dist/MigrationSqlSafetyAdapter-yP6fPjeC.js.map +1 -0
- package/dist/PostgresIntrospector-DQDTZUW_.d.ts +77 -0
- package/dist/{SqliteCompilerFactory-BvdJ0kBl.js → SqliteCompilerFactory-CXlPAclY.js} +38 -40
- package/dist/SqliteCompilerFactory-CXlPAclY.js.map +1 -0
- package/dist/{SqliteIntrospector-CWwPWhmA.js → SqliteIntrospector-CfItmGgA.js} +15 -15
- package/dist/SqliteIntrospector-CfItmGgA.js.map +1 -0
- package/dist/builder/index.d.ts +4 -10
- package/dist/builder/index.js +3 -6
- package/dist/{builder-xJ-Bq2pk.js → builder-BSepa_PF.js} +24 -24
- package/dist/builder-BSepa_PF.js.map +1 -0
- package/dist/chunk-D7D4PA-g.js +13 -0
- package/dist/{cli-7j3R1Y1r.js → cli-e8I1-dab.js} +34 -47
- package/dist/cli-e8I1-dab.js.map +1 -0
- package/dist/cli.d.ts +1 -2
- package/dist/cli.js +3 -16
- package/dist/cli.js.map +1 -1
- package/dist/commands/index.d.ts +2 -4
- package/dist/commands/index.js +7 -16
- package/dist/commands/index.js.map +1 -0
- package/dist/compilers/index.d.ts +3 -10
- package/dist/compilers/index.js +3 -7
- package/dist/{compilers-C-GiumJB.js → compilers-BW-WALoD.js} +9 -16
- package/dist/{compilers-C-GiumJB.js.map → compilers-BW-WALoD.js.map} +1 -1
- package/dist/diff/index.d.ts +2 -5
- package/dist/diff/index.js +2 -7
- package/dist/{diff-B9MhagJF.js → diff-7Xw8k4vp.js} +14 -14
- package/dist/diff-7Xw8k4vp.js.map +1 -0
- package/dist/domain/index.d.ts +5 -7
- package/dist/domain/index.js +7 -3
- package/dist/domain/index.js.map +1 -0
- package/dist/generator/index.d.ts +2 -4
- package/dist/generator/index.js +7 -4
- package/dist/generator/index.js.map +1 -0
- package/dist/index-B8VoE0M4.d.ts +56 -0
- package/dist/index-CsTGwtZ0.d.ts +110 -0
- package/dist/index-CzdR_Ig9.d.ts +47 -0
- package/dist/index-CzpjLzoS.d.ts +180 -0
- package/dist/index-D7qe9iKG.d.ts +13 -0
- package/dist/index-DdCF5yCg.d.ts +38 -0
- package/dist/index-a1Y--85Y.d.ts +41 -0
- package/dist/index-ni7Db8Lv.d.ts +86 -0
- package/dist/index-sywIP4pt.d.ts +61 -0
- package/dist/index.d.ts +17 -27
- package/dist/index.js +19 -23
- package/dist/introspect/index.d.ts +3 -6
- package/dist/introspect/index.js +10 -4
- package/dist/introspect/index.js.map +1 -0
- package/dist/runner/index.d.ts +2 -4
- package/dist/runner/index.js +7 -10
- package/dist/runner/index.js.map +1 -0
- package/dist/strategies/index.d.ts +3 -5
- package/dist/strategies/index.js +13 -9
- package/dist/strategies/index.js.map +1 -0
- package/package.json +8 -8
- package/dist/CollectingBuilder--4fqDQdE.js.map +0 -1
- package/dist/CompilerStrategy-_AiXiyjS.js.map +0 -1
- package/dist/IntrospectorStrategy-BEIG5GqA.js.map +0 -1
- package/dist/Migration-DYQ0hUG7.js.map +0 -1
- package/dist/MigrationGenerator-B1p0jHnx.js.map +0 -1
- package/dist/MigrationRunner-DomrOZIn.js.map +0 -1
- package/dist/MigrationSqlSafetyAdapter-CGRbB2k2.js.map +0 -1
- package/dist/SqliteCompilerFactory-BvdJ0kBl.js.map +0 -1
- package/dist/SqliteIntrospector-CWwPWhmA.js.map +0 -1
- package/dist/builder/contracts/Builder.d.ts +0 -11
- package/dist/builder/contracts/ColumnSpec.d.ts +0 -20
- package/dist/builder/contracts/ColumnType.d.ts +0 -2
- package/dist/builder/contracts/DeleteReferentialAction.d.ts +0 -2
- package/dist/builder/contracts/UpdateReferentialAction.d.ts +0 -2
- package/dist/builder/contracts/index.d.ts +0 -8
- package/dist/builder/ops/OpBuilder.d.ts +0 -129
- package/dist/builder/ops/index.d.ts +0 -4
- package/dist/builder/runtime/CollectingBuilder.d.ts +0 -39
- package/dist/builder/runtime/index.d.ts +0 -4
- package/dist/builder-xJ-Bq2pk.js.map +0 -1
- package/dist/chunk-BkvOhyD0.js +0 -12
- package/dist/cli-7j3R1Y1r.js.map +0 -1
- package/dist/commands/cli.d.ts +0 -5
- package/dist/commands-Cl2MU7tq.js +0 -10
- package/dist/commands-Cl2MU7tq.js.map +0 -1
- package/dist/compilers/contracts/CompilerFactory.d.ts +0 -8
- package/dist/compilers/contracts/SQL.d.ts +0 -4
- package/dist/compilers/contracts/SQLCompiler.d.ts +0 -11
- package/dist/compilers/contracts/index.d.ts +0 -6
- package/dist/compilers/dialects/PostgresCompiler.d.ts +0 -32
- package/dist/compilers/dialects/SqliteCompiler.d.ts +0 -27
- package/dist/compilers/dialects/index.d.ts +0 -5
- package/dist/compilers/factories/PostgresCompilerFactory.d.ts +0 -17
- package/dist/compilers/factories/SqliteCompilerFactory.d.ts +0 -17
- package/dist/compilers/factories/index.d.ts +0 -5
- package/dist/diff/diffSchema.d.ts +0 -39
- package/dist/diff-B9MhagJF.js.map +0 -1
- package/dist/domain/Dialect.d.ts +0 -2
- package/dist/domain/Migration.d.ts +0 -29
- package/dist/domain/MigrationMode.d.ts +0 -2
- package/dist/domain/MigrationOperation.d.ts +0 -77
- package/dist/domain/internal/InternalColumnType.d.ts +0 -11
- package/dist/domain/internal/InternalDialect.d.ts +0 -5
- package/dist/domain/internal/InternalMigrationMode.d.ts +0 -5
- package/dist/domain/internal/InternalOperationKind.d.ts +0 -14
- package/dist/domain/internal/InternalReferentialAction.d.ts +0 -7
- package/dist/domain-CwR-kUNS.js +0 -10
- package/dist/domain-CwR-kUNS.js.map +0 -1
- package/dist/generator/MigrationGenerator.d.ts +0 -48
- package/dist/generator-DK-_f-PF.js +0 -10
- package/dist/generator-DK-_f-PF.js.map +0 -1
- package/dist/internal/MigrationSqlSafetyAdapter.d.ts +0 -24
- package/dist/introspect/DatabaseIntrospector.d.ts +0 -17
- package/dist/introspect/PostgresIntrospector.d.ts +0 -56
- package/dist/introspect/SqliteIntrospector.d.ts +0 -53
- package/dist/introspect-DD3fm15e.js +0 -13
- package/dist/introspect-DD3fm15e.js.map +0 -1
- package/dist/runner/MigrationRunner.d.ts +0 -79
- package/dist/runner-C97xT8_W.js +0 -10
- package/dist/runner-C97xT8_W.js.map +0 -1
- package/dist/runtime/loadModule.d.ts +0 -18
- package/dist/schema/buildMigrationModelMetadataProjection.d.ts +0 -3
- package/dist/strategies/CompilerStrategy.d.ts +0 -42
- package/dist/strategies/IntrospectorStrategy.d.ts +0 -34
- package/dist/strategies-HwUWvmLC.js +0 -16
- package/dist/strategies-HwUWvmLC.js.map +0 -1
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { TrustedSqlFragment } from "@danceroutine/tango-core";
|
|
2
|
+
|
|
3
|
+
//#region src/domain/internal/InternalOperationKind.d.ts
|
|
4
|
+
declare const InternalOperationKind: {
|
|
5
|
+
readonly TABLE_CREATE: "table.create";
|
|
6
|
+
readonly TABLE_DROP: "table.drop";
|
|
7
|
+
readonly COLUMN_ADD: "column.add";
|
|
8
|
+
readonly COLUMN_DROP: "column.drop";
|
|
9
|
+
readonly COLUMN_ALTER: "column.alter";
|
|
10
|
+
readonly COLUMN_RENAME: "column.rename";
|
|
11
|
+
readonly INDEX_CREATE: "index.create";
|
|
12
|
+
readonly INDEX_DROP: "index.drop";
|
|
13
|
+
readonly FK_CREATE: "fk.create";
|
|
14
|
+
readonly FK_VALIDATE: "fk.validate";
|
|
15
|
+
readonly FK_DROP: "fk.drop";
|
|
16
|
+
};
|
|
17
|
+
type InternalOperationKind = (typeof InternalOperationKind)[keyof typeof InternalOperationKind];
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/domain/internal/InternalColumnType.d.ts
|
|
20
|
+
declare const InternalColumnType: {
|
|
21
|
+
readonly SERIAL: "serial";
|
|
22
|
+
readonly INT: "int";
|
|
23
|
+
readonly BIGINT: "bigint";
|
|
24
|
+
readonly TEXT: "text";
|
|
25
|
+
readonly BOOL: "bool";
|
|
26
|
+
readonly TIMESTAMPTZ: "timestamptz";
|
|
27
|
+
readonly JSONB: "jsonb";
|
|
28
|
+
readonly UUID: "uuid";
|
|
29
|
+
};
|
|
30
|
+
type InternalColumnType = (typeof InternalColumnType)[keyof typeof InternalColumnType];
|
|
31
|
+
//#endregion
|
|
32
|
+
//#region src/builder/contracts/ColumnType.d.ts
|
|
33
|
+
type ColumnType = (typeof InternalColumnType)[keyof typeof InternalColumnType];
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/domain/internal/InternalReferentialAction.d.ts
|
|
36
|
+
declare const InternalReferentialAction: {
|
|
37
|
+
readonly CASCADE: "CASCADE";
|
|
38
|
+
readonly SET_NULL: "SET NULL";
|
|
39
|
+
readonly RESTRICT: "RESTRICT";
|
|
40
|
+
readonly NO_ACTION: "NO ACTION";
|
|
41
|
+
};
|
|
42
|
+
type InternalReferentialAction = (typeof InternalReferentialAction)[keyof typeof InternalReferentialAction];
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/builder/contracts/DeleteReferentialAction.d.ts
|
|
45
|
+
type DeleteReferentialAction = (typeof InternalReferentialAction)[keyof typeof InternalReferentialAction];
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/builder/contracts/UpdateReferentialAction.d.ts
|
|
48
|
+
type UpdateReferentialAction = Extract<InternalReferentialAction, 'CASCADE' | 'RESTRICT' | 'NO_ACTION'>;
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/builder/contracts/ColumnSpec.d.ts
|
|
51
|
+
interface ColumnSpec {
|
|
52
|
+
name: string;
|
|
53
|
+
type: ColumnType;
|
|
54
|
+
notNull?: boolean;
|
|
55
|
+
default?: TrustedSqlFragment | {
|
|
56
|
+
now: true;
|
|
57
|
+
} | null;
|
|
58
|
+
primaryKey?: boolean;
|
|
59
|
+
unique?: boolean;
|
|
60
|
+
references?: {
|
|
61
|
+
table: string;
|
|
62
|
+
column: string;
|
|
63
|
+
onDelete?: DeleteReferentialAction;
|
|
64
|
+
onUpdate?: UpdateReferentialAction;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region src/domain/MigrationOperation.d.ts
|
|
69
|
+
type TableCreate = {
|
|
70
|
+
kind: typeof InternalOperationKind.TABLE_CREATE;
|
|
71
|
+
table: string;
|
|
72
|
+
columns: ColumnSpec[];
|
|
73
|
+
};
|
|
74
|
+
type TableDrop = {
|
|
75
|
+
kind: typeof InternalOperationKind.TABLE_DROP;
|
|
76
|
+
table: string;
|
|
77
|
+
cascade?: boolean;
|
|
78
|
+
};
|
|
79
|
+
type ColumnAdd = {
|
|
80
|
+
kind: typeof InternalOperationKind.COLUMN_ADD;
|
|
81
|
+
table: string;
|
|
82
|
+
column: ColumnSpec;
|
|
83
|
+
};
|
|
84
|
+
type ColumnDrop = {
|
|
85
|
+
kind: typeof InternalOperationKind.COLUMN_DROP;
|
|
86
|
+
table: string;
|
|
87
|
+
column: string;
|
|
88
|
+
};
|
|
89
|
+
type ColumnAlter = {
|
|
90
|
+
kind: typeof InternalOperationKind.COLUMN_ALTER;
|
|
91
|
+
table: string;
|
|
92
|
+
column: string;
|
|
93
|
+
to: Partial<ColumnSpec>;
|
|
94
|
+
};
|
|
95
|
+
type ColumnRename = {
|
|
96
|
+
kind: typeof InternalOperationKind.COLUMN_RENAME;
|
|
97
|
+
table: string;
|
|
98
|
+
from: string;
|
|
99
|
+
to: string;
|
|
100
|
+
};
|
|
101
|
+
type IndexCreate = {
|
|
102
|
+
kind: typeof InternalOperationKind.INDEX_CREATE;
|
|
103
|
+
table: string;
|
|
104
|
+
name: string;
|
|
105
|
+
on: string[];
|
|
106
|
+
unique?: boolean;
|
|
107
|
+
where?: TrustedSqlFragment;
|
|
108
|
+
concurrently?: boolean;
|
|
109
|
+
};
|
|
110
|
+
type IndexDrop = {
|
|
111
|
+
kind: typeof InternalOperationKind.INDEX_DROP;
|
|
112
|
+
table: string;
|
|
113
|
+
name: string;
|
|
114
|
+
concurrently?: boolean;
|
|
115
|
+
};
|
|
116
|
+
type ForeignKeyCreate = {
|
|
117
|
+
kind: typeof InternalOperationKind.FK_CREATE;
|
|
118
|
+
table: string;
|
|
119
|
+
name?: string;
|
|
120
|
+
columns: string[];
|
|
121
|
+
refTable: string;
|
|
122
|
+
refColumns: string[];
|
|
123
|
+
onDelete?: string;
|
|
124
|
+
onUpdate?: string;
|
|
125
|
+
notValid?: boolean;
|
|
126
|
+
};
|
|
127
|
+
type ForeignKeyValidate = {
|
|
128
|
+
kind: typeof InternalOperationKind.FK_VALIDATE;
|
|
129
|
+
table: string;
|
|
130
|
+
name: string;
|
|
131
|
+
};
|
|
132
|
+
type ForeignKeyDrop = {
|
|
133
|
+
kind: typeof InternalOperationKind.FK_DROP;
|
|
134
|
+
table: string;
|
|
135
|
+
name: string;
|
|
136
|
+
};
|
|
137
|
+
type CustomMigrationOperation<TName extends string = string, TArgs extends object = Record<string, unknown>> = {
|
|
138
|
+
kind: 'custom';
|
|
139
|
+
name: TName;
|
|
140
|
+
args: TArgs;
|
|
141
|
+
};
|
|
142
|
+
type MigrationOperation = TableCreate | TableDrop | ColumnAdd | ColumnDrop | ColumnAlter | ColumnRename | IndexCreate | IndexDrop | ForeignKeyCreate | ForeignKeyValidate | ForeignKeyDrop | CustomMigrationOperation;
|
|
143
|
+
//#endregion
|
|
144
|
+
export { ColumnType as _, CustomMigrationOperation as a, ForeignKeyValidate as c, MigrationOperation as d, TableCreate as f, DeleteReferentialAction as g, UpdateReferentialAction as h, ColumnRename as i, IndexCreate as l, ColumnSpec as m, ColumnAlter as n, ForeignKeyCreate as o, TableDrop as p, ColumnDrop as r, ForeignKeyDrop as s, ColumnAdd as t, IndexDrop as u };
|
|
145
|
+
//# sourceMappingURL=MigrationOperation-qpdhPEs9.d.ts.map
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { readdir } from "node:fs/promises";
|
|
5
|
-
import { extname, resolve, resolve as resolve$1 } from "node:path";
|
|
1
|
+
import { t as Migration } from "./Migration-DxHHPyzn.js";
|
|
2
|
+
import { t as CollectingBuilder } from "./CollectingBuilder-BIfAKs_x.js";
|
|
3
|
+
import { n as createDefaultCompilerStrategy, r as InternalDialect } from "./CompilerStrategy-vcZKg8qf.js";
|
|
6
4
|
import { isError } from "@danceroutine/tango-core";
|
|
5
|
+
import { readdir } from "node:fs/promises";
|
|
6
|
+
import { extname, resolve } from "node:path";
|
|
7
7
|
import { pathToFileURL } from "node:url";
|
|
8
8
|
import { createJiti } from "jiti";
|
|
9
9
|
import { ModelRegistry } from "@danceroutine/tango-schema";
|
|
10
|
-
|
|
11
10
|
//#region src/runtime/loadModule.ts
|
|
12
11
|
const TS_EXTENSIONS = new Set([
|
|
13
12
|
".ts",
|
|
@@ -16,36 +15,68 @@ const TS_EXTENSIONS = new Set([
|
|
|
16
15
|
".cts"
|
|
17
16
|
]);
|
|
18
17
|
function toAbsolutePath(modulePath, projectRoot) {
|
|
19
|
-
return resolve
|
|
18
|
+
return resolve(projectRoot, modulePath);
|
|
20
19
|
}
|
|
21
20
|
function isTypeScriptModule(modulePath) {
|
|
22
21
|
return TS_EXTENSIONS.has(extname(modulePath).toLowerCase());
|
|
23
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Load a module from a Tango app project root.
|
|
25
|
+
*
|
|
26
|
+
* TypeScript modules are loaded through jiti and JavaScript/ESM modules are loaded
|
|
27
|
+
* through native dynamic import so published installs behave like end-user runtime.
|
|
28
|
+
*/
|
|
24
29
|
async function loadModule(modulePath, options) {
|
|
25
30
|
const projectRoot = options?.projectRoot ?? process.cwd();
|
|
26
31
|
const absolutePath = toAbsolutePath(modulePath, projectRoot);
|
|
27
32
|
const executeImport = async () => {
|
|
28
|
-
if (isTypeScriptModule(absolutePath)) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
});
|
|
33
|
-
return await jiti.import(absolutePath);
|
|
34
|
-
}
|
|
33
|
+
if (isTypeScriptModule(absolutePath)) return await createJiti(resolve(projectRoot, "tango.config.ts"), {
|
|
34
|
+
interopDefault: true,
|
|
35
|
+
moduleCache: options?.moduleCache ?? true
|
|
36
|
+
}).import(absolutePath);
|
|
35
37
|
return await import(pathToFileURL(absolutePath).href);
|
|
36
38
|
};
|
|
37
39
|
if (options?.registry) return ModelRegistry.runWithRegistry(options.registry, executeImport);
|
|
38
40
|
return executeImport();
|
|
39
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Load a module and return default export when present.
|
|
44
|
+
*/
|
|
40
45
|
async function loadDefaultExport(modulePath, options) {
|
|
41
46
|
const loaded = await loadModule(modulePath, options);
|
|
42
47
|
return loaded.default ?? loaded;
|
|
43
48
|
}
|
|
44
|
-
|
|
45
49
|
//#endregion
|
|
46
50
|
//#region src/runner/MigrationRunner.ts
|
|
47
51
|
const JOURNAL = "_tango_migrations";
|
|
52
|
+
/**
|
|
53
|
+
* Manages the lifecycle of database migrations: applying, planning, and tracking status.
|
|
54
|
+
*
|
|
55
|
+
* The runner reads migration files from a directory, compiles operations to SQL
|
|
56
|
+
* for the target dialect, and maintains a journal table to track which migrations
|
|
57
|
+
* have been applied. Each applied migration is checksummed to detect tampering.
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* const runner = new MigrationRunner(client, 'postgres', './migrations');
|
|
62
|
+
*
|
|
63
|
+
* // Apply all pending migrations
|
|
64
|
+
* await runner.apply();
|
|
65
|
+
*
|
|
66
|
+
* // Apply up to a specific migration
|
|
67
|
+
* await runner.apply('003_add_indexes');
|
|
68
|
+
*
|
|
69
|
+
* // Preview the SQL that would be generated
|
|
70
|
+
* const sql = await runner.plan();
|
|
71
|
+
*
|
|
72
|
+
* // Check which migrations are applied
|
|
73
|
+
* const statuses = await runner.status();
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
48
76
|
var MigrationRunner = class MigrationRunner {
|
|
77
|
+
client;
|
|
78
|
+
dialect;
|
|
79
|
+
migrationsDir;
|
|
49
80
|
static BRAND = "tango.migrations.runner";
|
|
50
81
|
__tangoBrand = MigrationRunner.BRAND;
|
|
51
82
|
compilerStrategy;
|
|
@@ -86,8 +117,7 @@ var MigrationRunner = class MigrationRunner {
|
|
|
86
117
|
migrations.forEach((migration) => {
|
|
87
118
|
const builder = new CollectingBuilder();
|
|
88
119
|
migration.up(builder);
|
|
89
|
-
const
|
|
90
|
-
const sqls = preparedOps.flatMap((op) => this.compileOperation(op));
|
|
120
|
+
const sqls = this.compilerStrategy.prepareOperations(this.dialect, builder.ops).flatMap((op) => this.compileOperation(op));
|
|
91
121
|
output += `# ${migration.id}\n`;
|
|
92
122
|
sqls.forEach((statement) => {
|
|
93
123
|
output += statement.sql + ";\n";
|
|
@@ -102,8 +132,7 @@ var MigrationRunner = class MigrationRunner {
|
|
|
102
132
|
*/
|
|
103
133
|
async status() {
|
|
104
134
|
const applied = await this.listApplied();
|
|
105
|
-
|
|
106
|
-
return migrations.map((m) => ({
|
|
135
|
+
return (await this.loadMigrations()).map((m) => ({
|
|
107
136
|
id: m.id,
|
|
108
137
|
applied: applied.has(m.id)
|
|
109
138
|
}));
|
|
@@ -152,8 +181,7 @@ var MigrationRunner = class MigrationRunner {
|
|
|
152
181
|
async applyMigration(migration) {
|
|
153
182
|
const builder = new CollectingBuilder();
|
|
154
183
|
await migration.up(builder);
|
|
155
|
-
const
|
|
156
|
-
const sqls = preparedOps.flatMap((op) => this.compileOperation(op));
|
|
184
|
+
const sqls = this.compilerStrategy.prepareOperations(this.dialect, builder.ops).flatMap((op) => this.compileOperation(op));
|
|
157
185
|
const checksum = String(this.hashJSON(builder.ops));
|
|
158
186
|
const isOnline = (migration.mode ?? builder.getMode()) === "online";
|
|
159
187
|
if (!isOnline && this.dialect === InternalDialect.POSTGRES) await this.client.query("BEGIN");
|
|
@@ -188,7 +216,7 @@ var MigrationRunner = class MigrationRunner {
|
|
|
188
216
|
return this.compilerStrategy.compile(this.dialect, op);
|
|
189
217
|
}
|
|
190
218
|
};
|
|
191
|
-
|
|
192
219
|
//#endregion
|
|
193
|
-
export {
|
|
194
|
-
|
|
220
|
+
export { loadModule as n, MigrationRunner as t };
|
|
221
|
+
|
|
222
|
+
//# sourceMappingURL=MigrationRunner-B5AJel12.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MigrationRunner-B5AJel12.js","names":[],"sources":["../src/runtime/loadModule.ts","../src/runner/MigrationRunner.ts"],"sourcesContent":["import { resolve, extname } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { createJiti } from 'jiti';\nimport { ModelRegistry } from '@danceroutine/tango-schema';\n\nconst TS_EXTENSIONS = new Set(['.ts', '.tsx', '.mts', '.cts']);\n\nfunction toAbsolutePath(modulePath: string, projectRoot: string): string {\n return resolve(projectRoot, modulePath);\n}\n\nfunction isTypeScriptModule(modulePath: string): boolean {\n return TS_EXTENSIONS.has(extname(modulePath).toLowerCase());\n}\n\n/**\n * Load a module from a Tango app project root.\n *\n * TypeScript modules are loaded through jiti and JavaScript/ESM modules are loaded\n * through native dynamic import so published installs behave like end-user runtime.\n */\nexport async function loadModule(\n modulePath: string,\n options?: { projectRoot?: string; registry?: ModelRegistry; moduleCache?: boolean }\n): Promise<Record<string, unknown>> {\n const projectRoot = options?.projectRoot ?? process.cwd();\n const absolutePath = toAbsolutePath(modulePath, projectRoot);\n const executeImport = async (): Promise<Record<string, unknown>> => {\n if (isTypeScriptModule(absolutePath)) {\n const jiti = createJiti(resolve(projectRoot, 'tango.config.ts'), {\n interopDefault: true,\n moduleCache: options?.moduleCache ?? true,\n });\n return (await jiti.import<Record<string, unknown>>(absolutePath)) as Record<string, unknown>;\n }\n\n return (await import(pathToFileURL(absolutePath).href)) as Record<string, unknown>;\n };\n\n if (options?.registry) {\n return ModelRegistry.runWithRegistry(options.registry, executeImport);\n }\n\n return executeImport();\n}\n\n/**\n * Load a module and return default export when present.\n */\nexport async function loadDefaultExport(modulePath: string, options?: { projectRoot?: string }): Promise<unknown> {\n const loaded = await loadModule(modulePath, options);\n return loaded.default ?? loaded;\n}\n","import { CollectingBuilder } from '../builder/runtime/CollectingBuilder';\nimport type { Dialect } from '../domain/Dialect';\nimport { Migration } from '../domain/Migration';\nimport type { SQL } from '../compilers/contracts/SQL';\nimport type { MigrationOperation } from '../domain/MigrationOperation';\nimport type { CompilerStrategy } from '../strategies/CompilerStrategy';\nimport { createDefaultCompilerStrategy } from '../strategies/CompilerStrategy';\nimport { InternalDialect } from '../domain/internal/InternalDialect';\nimport { isError } from '@danceroutine/tango-core';\nimport { readdir } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport { loadDefaultExport } from '../runtime/loadModule';\n\nconst JOURNAL = '_tango_migrations';\n\n/** DB client contract required by migration execution. */\ninterface DBClient {\n /** Execute SQL with optional parameters. */\n query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{ rows: T[] }>;\n /** Release underlying database resources. */\n close(): Promise<void>;\n}\n\n/**\n * Manages the lifecycle of database migrations: applying, planning, and tracking status.\n *\n * The runner reads migration files from a directory, compiles operations to SQL\n * for the target dialect, and maintains a journal table to track which migrations\n * have been applied. Each applied migration is checksummed to detect tampering.\n *\n * @example\n * ```typescript\n * const runner = new MigrationRunner(client, 'postgres', './migrations');\n *\n * // Apply all pending migrations\n * await runner.apply();\n *\n * // Apply up to a specific migration\n * await runner.apply('003_add_indexes');\n *\n * // Preview the SQL that would be generated\n * const sql = await runner.plan();\n *\n * // Check which migrations are applied\n * const statuses = await runner.status();\n * ```\n */\nexport class MigrationRunner {\n static readonly BRAND = 'tango.migrations.runner' as const;\n readonly __tangoBrand: typeof MigrationRunner.BRAND = MigrationRunner.BRAND;\n private compilerStrategy: CompilerStrategy;\n\n constructor(\n private client: DBClient,\n private dialect: Dialect,\n private migrationsDir: string = 'migrations',\n compilerStrategy?: CompilerStrategy\n ) {\n this.compilerStrategy = compilerStrategy ?? createDefaultCompilerStrategy();\n }\n\n /**\n * Narrow an unknown value to `MigrationRunner`.\n */\n static isMigrationRunner(value: unknown): value is MigrationRunner {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === MigrationRunner.BRAND\n );\n }\n\n /**\n * Apply all pending migrations, optionally stopping at a specific migration ID.\n * Migrations are applied in file-sort order. Already-applied migrations are skipped.\n * Non-online migrations are wrapped in a transaction on Postgres.\n */\n async apply(toId?: string): Promise<void> {\n await this.ensureJournal();\n const applied = await this.listApplied();\n const migrations = await this.loadMigrations();\n\n for (const migration of migrations) {\n if (toId && migration.id > toId) {\n break;\n }\n if (applied.has(migration.id)) {\n continue;\n }\n\n await this.applyMigration(migration);\n }\n }\n\n /**\n * Generate a dry-run SQL plan for all migrations without executing anything.\n * Useful for reviewing what SQL would be run before applying.\n */\n async plan(): Promise<string> {\n const migrations = await this.loadMigrations();\n let output = '';\n\n migrations.forEach((migration) => {\n const builder = new CollectingBuilder();\n migration.up(builder);\n const preparedOps = this.compilerStrategy.prepareOperations(this.dialect, builder.ops);\n const sqls = preparedOps.flatMap((op) => this.compileOperation(op));\n\n output += `# ${migration.id}\\n`;\n sqls.forEach((statement) => {\n output += statement.sql + ';\\n';\n });\n if (builder.dataFns.length) {\n output += '-- (data step present)\\n';\n }\n output += '\\n';\n });\n\n return output;\n }\n\n /**\n * Return the applied/pending status of every migration found on disk.\n */\n async status(): Promise<{ id: string; applied: boolean }[]> {\n const applied = await this.listApplied();\n const migrations = await this.loadMigrations();\n\n return migrations.map((m) => ({\n id: m.id,\n applied: applied.has(m.id),\n }));\n }\n\n private async ensureJournal(): Promise<void> {\n const sql =\n this.dialect === InternalDialect.POSTGRES\n ? `CREATE TABLE IF NOT EXISTS \"${JOURNAL}\" (\n id TEXT PRIMARY KEY,\n applied_at TIMESTAMPTZ NOT NULL DEFAULT now(),\n checksum TEXT NOT NULL\n )`\n : `CREATE TABLE IF NOT EXISTS ${JOURNAL} (\n id TEXT PRIMARY KEY,\n applied_at TEXT NOT NULL DEFAULT (datetime('now')),\n checksum TEXT NOT NULL\n )`;\n\n await this.client.query(sql);\n }\n\n private async listApplied(): Promise<Set<string>> {\n const table = this.dialect === InternalDialect.POSTGRES ? `\"${JOURNAL}\"` : JOURNAL;\n const { rows } = await this.client.query<{ id: string }>(`SELECT id FROM ${table}`);\n return new Set(rows.map((r) => r.id));\n }\n\n private async loadMigrations(): Promise<Migration[]> {\n const files = (await readdir(this.migrationsDir)).filter((f) => f.endsWith('.ts') || f.endsWith('.js')).sort();\n\n const migrations: Migration[] = [];\n\n for (const file of files) {\n const absolutePath = resolve(this.migrationsDir, file);\n let loaded: unknown;\n try {\n loaded = await loadDefaultExport(absolutePath, { projectRoot: process.cwd() });\n } catch (error) {\n const reason = isError(error) ? error.message : String(error);\n throw new Error(`Failed to load migration module '${file}': ${reason}`, { cause: error });\n }\n\n if (Migration.isMigration(loaded)) {\n migrations.push(loaded);\n continue;\n }\n\n if (Migration.isMigrationConstructor(loaded)) {\n migrations.push(new loaded());\n continue;\n }\n\n throw new Error(\n `Invalid migration module '${file}'. Default export must be a Migration subclass or instance.`\n );\n }\n\n return migrations;\n }\n\n private async applyMigration(migration: Migration): Promise<void> {\n const builder = new CollectingBuilder();\n await migration.up(builder);\n\n const preparedOps = this.compilerStrategy.prepareOperations(this.dialect, builder.ops);\n const sqls = preparedOps.flatMap((op) => this.compileOperation(op));\n const checksum = String(this.hashJSON(builder.ops));\n\n const isOnline = (migration.mode ?? builder.getMode()) === 'online';\n\n if (!isOnline && this.dialect === InternalDialect.POSTGRES) {\n await this.client.query('BEGIN');\n }\n\n try {\n for (const statement of sqls) {\n await this.client.query(statement.sql, statement.params);\n }\n\n for (const fn of builder.dataFns) {\n await fn({ query: (sql, params) => this.client.query(sql, params).then(() => {}) });\n }\n\n const table = this.dialect === InternalDialect.POSTGRES ? `\"${JOURNAL}\"` : JOURNAL;\n const placeholder = this.dialect === InternalDialect.POSTGRES ? '$1, $2' : '?, ?';\n await this.client.query(`INSERT INTO ${table} (id, checksum) VALUES (${placeholder})`, [\n migration.id,\n checksum,\n ]);\n\n if (!isOnline && this.dialect === InternalDialect.POSTGRES) {\n await this.client.query('COMMIT');\n }\n } catch (error) {\n if (!isOnline && this.dialect === InternalDialect.POSTGRES) {\n await this.client.query('ROLLBACK');\n }\n throw error;\n }\n }\n\n /**\n * Compute a simple hash of the migration's operation list.\n * Stored alongside each applied migration in the journal table to detect\n * if a migration file has been modified after it was already applied.\n * Uses a djb2-like hash over the JSON-serialized operations.\n */\n private hashJSON(x: unknown): number {\n const s = JSON.stringify(x);\n let h = 0;\n for (let i = 0; i < s.length; i++) {\n // oxlint-disable-next-line prefer-code-point\n h = Math.imul(31, h) + s.charCodeAt(i);\n // oxlint-disable-next-line prefer-math-trunc\n h = h | 0;\n }\n // oxlint-disable-next-line unicorn/prefer-math-trunc\n return h >>> 0;\n }\n\n private compileOperation(op: MigrationOperation): SQL[] {\n return this.compilerStrategy.compile(this.dialect, op);\n }\n}\n"],"mappings":"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"}
|
package/dist/{MigrationSqlSafetyAdapter-CGRbB2k2.js → MigrationSqlSafetyAdapter-yP6fPjeC.js}
RENAMED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { SqlSafetyEngine, isTrustedSqlFragment, quoteSqlIdentifier } from "@danceroutine/tango-core";
|
|
2
|
-
|
|
3
2
|
//#region src/internal/MigrationSqlSafetyAdapter.ts
|
|
3
|
+
/**
|
|
4
|
+
* Migrations-local adapter that maps migration operations into the shared SQL
|
|
5
|
+
* safety engine and returns quoted identifiers or trusted raw fragments.
|
|
6
|
+
*/
|
|
4
7
|
var MigrationSqlSafetyAdapter = class {
|
|
8
|
+
dialect;
|
|
9
|
+
engine;
|
|
5
10
|
constructor(dialect, engine = new SqlSafetyEngine()) {
|
|
6
11
|
this.dialect = dialect;
|
|
7
12
|
this.engine = engine;
|
|
@@ -31,11 +36,11 @@ var MigrationSqlSafetyAdapter = class {
|
|
|
31
36
|
}] }).rawFragments[key].sql;
|
|
32
37
|
}
|
|
33
38
|
optionalRawFragment(key, value) {
|
|
34
|
-
if (!value) return
|
|
39
|
+
if (!value) return;
|
|
35
40
|
return this.rawFragment(key, value);
|
|
36
41
|
}
|
|
37
42
|
rawDefault(value, nowSql) {
|
|
38
|
-
if (value ===
|
|
43
|
+
if (value === void 0) return;
|
|
39
44
|
if (value === null) return null;
|
|
40
45
|
if (this.isNowDefault(value)) return nowSql;
|
|
41
46
|
return this.rawFragment("default", value);
|
|
@@ -44,19 +49,18 @@ var MigrationSqlSafetyAdapter = class {
|
|
|
44
49
|
return isTrustedSqlFragment(value);
|
|
45
50
|
}
|
|
46
51
|
quote(key, role, value, allowlist) {
|
|
47
|
-
|
|
52
|
+
return quoteSqlIdentifier(this.engine.validate({ identifiers: [{
|
|
48
53
|
key,
|
|
49
54
|
role,
|
|
50
55
|
value,
|
|
51
56
|
allowlist
|
|
52
|
-
}] });
|
|
53
|
-
return quoteSqlIdentifier(validated.identifiers[key], this.dialect);
|
|
57
|
+
}] }).identifiers[key], this.dialect);
|
|
54
58
|
}
|
|
55
59
|
isNowDefault(value) {
|
|
56
60
|
return typeof value === "object" && value !== null && "now" in value && value.now === true;
|
|
57
61
|
}
|
|
58
62
|
};
|
|
59
|
-
|
|
60
63
|
//#endregion
|
|
61
|
-
export { MigrationSqlSafetyAdapter };
|
|
62
|
-
|
|
64
|
+
export { MigrationSqlSafetyAdapter as t };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=MigrationSqlSafetyAdapter-yP6fPjeC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MigrationSqlSafetyAdapter-yP6fPjeC.js","names":[],"sources":["../src/internal/MigrationSqlSafetyAdapter.ts"],"sourcesContent":["import {\n SqlSafetyEngine,\n isTrustedSqlFragment,\n quoteSqlIdentifier,\n type SqlDialect,\n type SqlIdentifierRole,\n type TrustedSqlFragment,\n} from '@danceroutine/tango-core';\n\ntype IdentifierRole = Extract<SqlIdentifierRole, 'table' | 'column' | 'index' | 'constraint' | 'schema'>;\n\n/**\n * Migrations-local adapter that maps migration operations into the shared SQL\n * safety engine and returns quoted identifiers or trusted raw fragments.\n */\nexport class MigrationSqlSafetyAdapter {\n constructor(\n private readonly dialect: SqlDialect,\n private readonly engine: SqlSafetyEngine = new SqlSafetyEngine()\n ) {}\n\n table(value: string): string {\n return this.quote('table', 'table', value);\n }\n\n column(value: string, allowlist?: readonly string[]): string {\n return this.quote('column', 'column', value, allowlist);\n }\n\n columns(values: readonly string[], allowlist?: readonly string[]): string[] {\n return values.map((value, index) => this.quote(`column:${index}`, 'column', value, allowlist));\n }\n\n index(value: string): string {\n return this.quote('index', 'index', value);\n }\n\n constraint(value: string): string {\n return this.quote('constraint', 'constraint', value);\n }\n\n schema(value: string): string {\n return this.quote('schema', 'schema', value);\n }\n\n rawFragment(key: string, value: TrustedSqlFragment): string {\n return this.engine.validate({\n rawFragments: [{ key, value }],\n }).rawFragments[key]!.sql;\n }\n\n optionalRawFragment(key: string, value?: TrustedSqlFragment): string | undefined {\n if (!value) {\n return undefined;\n }\n\n return this.rawFragment(key, value);\n }\n\n rawDefault(\n value: TrustedSqlFragment | { now: true } | null | undefined,\n nowSql: string\n ): string | null | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n if (value === null) {\n return null;\n }\n\n if (this.isNowDefault(value)) {\n return nowSql;\n }\n\n return this.rawFragment('default', value);\n }\n\n isTrustedFragment(value: unknown): value is TrustedSqlFragment {\n return isTrustedSqlFragment(value);\n }\n\n private quote(key: string, role: IdentifierRole, value: string, allowlist?: readonly string[]): string {\n const validated = this.engine.validate({\n identifiers: [{ key, role, value, allowlist }],\n });\n\n return quoteSqlIdentifier(validated.identifiers[key]!, this.dialect);\n }\n\n private isNowDefault(value: TrustedSqlFragment | { now: true }): value is { now: true } {\n return typeof value === 'object' && value !== null && 'now' in value && value.now === true;\n }\n}\n"],"mappings":";;;;;;AAeA,IAAa,4BAAb,MAAuC;CAEd;CACA;CAFrB,YACI,SACA,SAA2C,IAAI,gBAAgB,GACjE;EAFmB,KAAA,UAAA;EACA,KAAA,SAAA;CAClB;CAEH,MAAM,OAAuB;EACzB,OAAO,KAAK,MAAM,SAAS,SAAS,KAAK;CAC7C;CAEA,OAAO,OAAe,WAAuC;EACzD,OAAO,KAAK,MAAM,UAAU,UAAU,OAAO,SAAS;CAC1D;CAEA,QAAQ,QAA2B,WAAyC;EACxE,OAAO,OAAO,KAAK,OAAO,UAAU,KAAK,MAAM,UAAU,SAAS,UAAU,OAAO,SAAS,CAAC;CACjG;CAEA,MAAM,OAAuB;EACzB,OAAO,KAAK,MAAM,SAAS,SAAS,KAAK;CAC7C;CAEA,WAAW,OAAuB;EAC9B,OAAO,KAAK,MAAM,cAAc,cAAc,KAAK;CACvD;CAEA,OAAO,OAAuB;EAC1B,OAAO,KAAK,MAAM,UAAU,UAAU,KAAK;CAC/C;CAEA,YAAY,KAAa,OAAmC;EACxD,OAAO,KAAK,OAAO,SAAS,EACxB,cAAc,CAAC;GAAE;GAAK;EAAM,CAAC,EACjC,CAAC,EAAE,aAAa,KAAM;CAC1B;CAEA,oBAAoB,KAAa,OAAgD;EAC7E,IAAI,CAAC,OACD;EAGJ,OAAO,KAAK,YAAY,KAAK,KAAK;CACtC;CAEA,WACI,OACA,QACyB;EACzB,IAAI,UAAU,KAAA,GACV;EAGJ,IAAI,UAAU,MACV,OAAO;EAGX,IAAI,KAAK,aAAa,KAAK,GACvB,OAAO;EAGX,OAAO,KAAK,YAAY,WAAW,KAAK;CAC5C;CAEA,kBAAkB,OAA6C;EAC3D,OAAO,qBAAqB,KAAK;CACrC;CAEA,MAAc,KAAa,MAAsB,OAAe,WAAuC;EAKnG,OAAO,mBAJW,KAAK,OAAO,SAAS,EACnC,aAAa,CAAC;GAAE;GAAK;GAAM;GAAO;EAAU,CAAC,EACjD,CAEkC,EAAE,YAAY,MAAO,KAAK,OAAO;CACvE;CAEA,aAAqB,OAAmE;EACpF,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,SAAS,SAAS,MAAM,QAAQ;CAC1F;AACJ"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
//#region src/introspect/DatabaseIntrospector.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Minimal DB client shape required by schema introspection.
|
|
4
|
+
*/
|
|
5
|
+
interface DBClient {
|
|
6
|
+
/** Execute a SQL statement and return row results. */
|
|
7
|
+
query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{
|
|
8
|
+
rows: T[];
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Dialect-specific schema introspection contract.
|
|
13
|
+
*/
|
|
14
|
+
interface DatabaseIntrospector {
|
|
15
|
+
/** Read the current database schema state. */
|
|
16
|
+
introspect(client: DBClient): Promise<DbSchema>;
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/introspect/PostgresIntrospector.d.ts
|
|
20
|
+
/** Introspected column metadata. */
|
|
21
|
+
interface DbColumn {
|
|
22
|
+
name: string;
|
|
23
|
+
type: string;
|
|
24
|
+
notNull: boolean;
|
|
25
|
+
default: string | null;
|
|
26
|
+
isPk: boolean;
|
|
27
|
+
isUnique: boolean;
|
|
28
|
+
}
|
|
29
|
+
/** Introspected index metadata. */
|
|
30
|
+
interface DbIndex {
|
|
31
|
+
name: string;
|
|
32
|
+
table: string;
|
|
33
|
+
unique: boolean;
|
|
34
|
+
columns: string[];
|
|
35
|
+
where: string | null;
|
|
36
|
+
}
|
|
37
|
+
/** Introspected foreign key metadata. */
|
|
38
|
+
interface DbForeignKey {
|
|
39
|
+
name: string;
|
|
40
|
+
table: string;
|
|
41
|
+
columns: string[];
|
|
42
|
+
refTable: string;
|
|
43
|
+
refColumns: string[];
|
|
44
|
+
onDelete: string | null;
|
|
45
|
+
onUpdate: string | null;
|
|
46
|
+
validated: boolean;
|
|
47
|
+
}
|
|
48
|
+
/** Introspected table metadata. */
|
|
49
|
+
interface DbTable {
|
|
50
|
+
name: string;
|
|
51
|
+
columns: Record<string, DbColumn>;
|
|
52
|
+
pks: string[];
|
|
53
|
+
indexes: Record<string, DbIndex>;
|
|
54
|
+
fks: Record<string, DbForeignKey>;
|
|
55
|
+
}
|
|
56
|
+
/** Introspected schema metadata. */
|
|
57
|
+
interface DbSchema {
|
|
58
|
+
tables: Record<string, DbTable>;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* PostgreSQL implementation of schema introspection.
|
|
62
|
+
*/
|
|
63
|
+
declare class PostgresIntrospector implements DatabaseIntrospector {
|
|
64
|
+
static readonly BRAND: "tango.migrations.postgres_introspector";
|
|
65
|
+
readonly __tangoBrand: typeof PostgresIntrospector.BRAND;
|
|
66
|
+
/**
|
|
67
|
+
* Narrow an unknown value to the PostgreSQL schema introspector.
|
|
68
|
+
*/
|
|
69
|
+
static isPostgresIntrospector(value: unknown): value is PostgresIntrospector;
|
|
70
|
+
/**
|
|
71
|
+
* Read table and column metadata from PostgreSQL system catalogs.
|
|
72
|
+
*/
|
|
73
|
+
introspect(client: DBClient): Promise<DbSchema>;
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
export { DbTable as a, DatabaseIntrospector as c, DbSchema as i, DbForeignKey as n, PostgresIntrospector as o, DbIndex as r, DBClient as s, DbColumn as t };
|
|
77
|
+
//# sourceMappingURL=PostgresIntrospector-DQDTZUW_.d.ts.map
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { MigrationSqlSafetyAdapter } from "./MigrationSqlSafetyAdapter-
|
|
4
|
-
|
|
1
|
+
import { t as InternalColumnType } from "./InternalColumnType-Dzs9T6a6.js";
|
|
2
|
+
import { t as InternalOperationKind } from "./InternalOperationKind-M4a4H9OZ.js";
|
|
3
|
+
import { t as MigrationSqlSafetyAdapter } from "./MigrationSqlSafetyAdapter-yP6fPjeC.js";
|
|
5
4
|
//#region src/compilers/dialects/PostgresCompiler.ts
|
|
5
|
+
/**
|
|
6
|
+
* PostgreSQL SQL compiler for migration operations.
|
|
7
|
+
*/
|
|
6
8
|
var PostgresCompiler = class PostgresCompiler {
|
|
7
9
|
static BRAND = "tango.migrations.postgres_compiler";
|
|
8
10
|
__tangoBrand = PostgresCompiler.BRAND;
|
|
@@ -21,7 +23,7 @@ var PostgresCompiler = class PostgresCompiler {
|
|
|
21
23
|
const tableCreates = [];
|
|
22
24
|
const remainder = [];
|
|
23
25
|
for (const operation of operations) if (operation.kind === InternalOperationKind.TABLE_CREATE) tableCreates.push(operation);
|
|
24
|
-
else remainder.push(operation);
|
|
26
|
+
else remainder.push(operation);
|
|
25
27
|
const strippedCreates = [];
|
|
26
28
|
const foreignKeys = [];
|
|
27
29
|
for (const operation of tableCreates) {
|
|
@@ -54,9 +56,8 @@ else remainder.push(operation);
|
|
|
54
56
|
constraints.push(fk);
|
|
55
57
|
});
|
|
56
58
|
const allParts = [cols, ...constraints].join(", ");
|
|
57
|
-
const sql = `CREATE TABLE ${this.sqlSafety.table(op.table)} (${allParts})`;
|
|
58
59
|
return [{
|
|
59
|
-
sql
|
|
60
|
+
sql: `CREATE TABLE ${this.sqlSafety.table(op.table)} (${allParts})`,
|
|
60
61
|
params: []
|
|
61
62
|
}];
|
|
62
63
|
}
|
|
@@ -78,7 +79,7 @@ else remainder.push(operation);
|
|
|
78
79
|
sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ALTER COLUMN ${this.sqlSafety.column(op.column)} TYPE ${this.typeToSQL(op.to.type)}`,
|
|
79
80
|
params: []
|
|
80
81
|
});
|
|
81
|
-
if (op.to.notNull !==
|
|
82
|
+
if (op.to.notNull !== void 0) out.push({
|
|
82
83
|
sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ALTER COLUMN ${this.sqlSafety.column(op.column)} ${op.to.notNull ? "SET NOT NULL" : "DROP NOT NULL"}`,
|
|
83
84
|
params: []
|
|
84
85
|
});
|
|
@@ -99,13 +100,10 @@ else remainder.push(operation);
|
|
|
99
100
|
params: []
|
|
100
101
|
}];
|
|
101
102
|
}
|
|
102
|
-
case InternalOperationKind.INDEX_DROP: {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
params: []
|
|
107
|
-
}];
|
|
108
|
-
}
|
|
103
|
+
case InternalOperationKind.INDEX_DROP: return [{
|
|
104
|
+
sql: `DROP INDEX ${op.concurrently ? "CONCURRENTLY " : ""}${this.sqlSafety.index(op.name)}`,
|
|
105
|
+
params: []
|
|
106
|
+
}];
|
|
109
107
|
case InternalOperationKind.FK_CREATE: {
|
|
110
108
|
const cols = this.sqlSafety.columns(op.columns).join(", ");
|
|
111
109
|
const refs = this.sqlSafety.columns(op.refColumns).join(", ");
|
|
@@ -134,7 +132,7 @@ else remainder.push(operation);
|
|
|
134
132
|
* Extracted to flatten the nested conditional logic.
|
|
135
133
|
*/
|
|
136
134
|
compileDefaultChange(table, column, defaultValue) {
|
|
137
|
-
if (defaultValue ===
|
|
135
|
+
if (defaultValue === void 0) return [];
|
|
138
136
|
if (defaultValue === null) return [{
|
|
139
137
|
sql: `ALTER TABLE ${this.sqlSafety.table(table)} ALTER COLUMN ${this.sqlSafety.column(column)} DROP DEFAULT`,
|
|
140
138
|
params: []
|
|
@@ -163,7 +161,7 @@ else remainder.push(operation);
|
|
|
163
161
|
onDelete: references.onDelete,
|
|
164
162
|
onUpdate: references.onUpdate
|
|
165
163
|
});
|
|
166
|
-
const { references: _references
|
|
164
|
+
const { references: _references, ...rest } = column;
|
|
167
165
|
return { ...rest };
|
|
168
166
|
});
|
|
169
167
|
return {
|
|
@@ -218,16 +216,15 @@ else remainder.push(operation);
|
|
|
218
216
|
case InternalColumnType.TIMESTAMPTZ: return "TIMESTAMPTZ";
|
|
219
217
|
case InternalColumnType.JSONB: return "JSONB";
|
|
220
218
|
case InternalColumnType.UUID: return "UUID";
|
|
221
|
-
default: {
|
|
222
|
-
const exhaustive = type;
|
|
223
|
-
throw new Error(`Unsupported column type: ${exhaustive}`);
|
|
224
|
-
}
|
|
219
|
+
default: throw new Error(`Unsupported column type: ${type}`);
|
|
225
220
|
}
|
|
226
221
|
}
|
|
227
222
|
};
|
|
228
|
-
|
|
229
223
|
//#endregion
|
|
230
224
|
//#region src/compilers/factories/PostgresCompilerFactory.ts
|
|
225
|
+
/**
|
|
226
|
+
* Factory for PostgreSQL migration compilers.
|
|
227
|
+
*/
|
|
231
228
|
var PostgresCompilerFactory = class PostgresCompilerFactory {
|
|
232
229
|
static BRAND = "tango.migrations.postgres_compiler_factory";
|
|
233
230
|
__tangoBrand = PostgresCompilerFactory.BRAND;
|
|
@@ -244,9 +241,11 @@ var PostgresCompilerFactory = class PostgresCompilerFactory {
|
|
|
244
241
|
return new PostgresCompiler();
|
|
245
242
|
}
|
|
246
243
|
};
|
|
247
|
-
|
|
248
244
|
//#endregion
|
|
249
245
|
//#region src/compilers/dialects/SqliteCompiler.ts
|
|
246
|
+
/**
|
|
247
|
+
* SQLite SQL compiler for migration operations.
|
|
248
|
+
*/
|
|
250
249
|
var SqliteCompiler = class SqliteCompiler {
|
|
251
250
|
static BRAND = "tango.migrations.sqlite_compiler";
|
|
252
251
|
__tangoBrand = SqliteCompiler.BRAND;
|
|
@@ -265,7 +264,7 @@ var SqliteCompiler = class SqliteCompiler {
|
|
|
265
264
|
const tableCreates = [];
|
|
266
265
|
const remainder = [];
|
|
267
266
|
for (const operation of operations) if (operation.kind === InternalOperationKind.TABLE_CREATE) tableCreates.push(operation);
|
|
268
|
-
else remainder.push(operation);
|
|
267
|
+
else remainder.push(operation);
|
|
269
268
|
const preparedRemainder = remainder.flatMap((operation) => operation.kind === InternalOperationKind.COLUMN_ADD ? this.prepareColumnAdd(operation) : [operation]);
|
|
270
269
|
return [...this.topologicalSortTableCreatesWithReferences(tableCreates), ...preparedRemainder];
|
|
271
270
|
}
|
|
@@ -282,9 +281,8 @@ else remainder.push(operation);
|
|
|
282
281
|
const references = column.references;
|
|
283
282
|
cols.push(`FOREIGN KEY (${this.sqlSafety.column(column.name)}) REFERENCES ${this.sqlSafety.table(references.table)}(${this.sqlSafety.column(references.column)})${references.onDelete ? ` ON DELETE ${references.onDelete}` : ""}${references.onUpdate ? ` ON UPDATE ${references.onUpdate}` : ""}`);
|
|
284
283
|
});
|
|
285
|
-
const sql = `CREATE TABLE ${this.sqlSafety.table(op.table)} (${cols.join(", ")})`;
|
|
286
284
|
return [{
|
|
287
|
-
sql,
|
|
285
|
+
sql: `CREATE TABLE ${this.sqlSafety.table(op.table)} (${cols.join(", ")})`,
|
|
288
286
|
params: []
|
|
289
287
|
}];
|
|
290
288
|
}
|
|
@@ -363,33 +361,31 @@ else remainder.push(operation);
|
|
|
363
361
|
}
|
|
364
362
|
prepareColumnAdd(op) {
|
|
365
363
|
const preparedColumn = op.column;
|
|
366
|
-
if (preparedColumn.notNull && preparedColumn.default ===
|
|
364
|
+
if (preparedColumn.notNull && preparedColumn.default === void 0 && !preparedColumn.primaryKey) throw new Error(`SQLite cannot add NOT NULL column '${preparedColumn.name}' to '${op.table}' without a default or backfill path.`);
|
|
367
365
|
if (!preparedColumn.unique) return [op];
|
|
368
|
-
|
|
366
|
+
return [{
|
|
369
367
|
...op,
|
|
370
368
|
column: {
|
|
371
369
|
...preparedColumn,
|
|
372
370
|
unique: false
|
|
373
371
|
}
|
|
374
|
-
}
|
|
375
|
-
const createIndex = {
|
|
372
|
+
}, {
|
|
376
373
|
kind: InternalOperationKind.INDEX_CREATE,
|
|
377
374
|
name: `${op.table}_${preparedColumn.name}_idx`,
|
|
378
375
|
table: op.table,
|
|
379
376
|
on: [preparedColumn.name],
|
|
380
377
|
unique: true
|
|
381
|
-
};
|
|
382
|
-
return [addColumn, createIndex];
|
|
378
|
+
}];
|
|
383
379
|
}
|
|
384
380
|
topologicalSortTableCreatesWithReferences(creates) {
|
|
385
381
|
if (creates.length <= 1) return creates;
|
|
386
382
|
const tableSet = new Set(creates.map((create) => create.table));
|
|
387
383
|
const byTable = new Map(creates.map((create) => [create.table, create]));
|
|
388
|
-
const incoming = new Map();
|
|
389
|
-
const dependents = new Map();
|
|
384
|
+
const incoming = /* @__PURE__ */ new Map();
|
|
385
|
+
const dependents = /* @__PURE__ */ new Map();
|
|
390
386
|
for (const table of tableSet) incoming.set(table, 0);
|
|
391
387
|
for (const create of creates) {
|
|
392
|
-
const seenParents = new Set();
|
|
388
|
+
const seenParents = /* @__PURE__ */ new Set();
|
|
393
389
|
for (const column of create.columns) {
|
|
394
390
|
if (!column.references) continue;
|
|
395
391
|
const refTable = column.references.table;
|
|
@@ -397,7 +393,7 @@ else remainder.push(operation);
|
|
|
397
393
|
if (seenParents.has(refTable)) continue;
|
|
398
394
|
seenParents.add(refTable);
|
|
399
395
|
incoming.set(create.table, incoming.get(create.table) + 1);
|
|
400
|
-
if (!dependents.has(refTable)) dependents.set(refTable, new Set());
|
|
396
|
+
if (!dependents.has(refTable)) dependents.set(refTable, /* @__PURE__ */ new Set());
|
|
401
397
|
dependents.get(refTable).add(create.table);
|
|
402
398
|
}
|
|
403
399
|
}
|
|
@@ -419,9 +415,11 @@ else remainder.push(operation);
|
|
|
419
415
|
return sorted;
|
|
420
416
|
}
|
|
421
417
|
};
|
|
422
|
-
|
|
423
418
|
//#endregion
|
|
424
419
|
//#region src/compilers/factories/SqliteCompilerFactory.ts
|
|
420
|
+
/**
|
|
421
|
+
* Factory for SQLite migration compilers.
|
|
422
|
+
*/
|
|
425
423
|
var SqliteCompilerFactory = class SqliteCompilerFactory {
|
|
426
424
|
static BRAND = "tango.migrations.sqlite_compiler_factory";
|
|
427
425
|
__tangoBrand = SqliteCompilerFactory.BRAND;
|
|
@@ -438,7 +436,7 @@ var SqliteCompilerFactory = class SqliteCompilerFactory {
|
|
|
438
436
|
return new SqliteCompiler();
|
|
439
437
|
}
|
|
440
438
|
};
|
|
441
|
-
|
|
442
439
|
//#endregion
|
|
443
|
-
export { PostgresCompiler
|
|
444
|
-
|
|
440
|
+
export { PostgresCompiler as i, SqliteCompiler as n, PostgresCompilerFactory as r, SqliteCompilerFactory as t };
|
|
441
|
+
|
|
442
|
+
//# sourceMappingURL=SqliteCompilerFactory-CXlPAclY.js.map
|