@danceroutine/tango-migrations 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +114 -0
- package/dist/{CollectingBuilder-C6qnwyrb.js → CollectingBuilder--4fqDQdE.js} +17 -2
- package/dist/CollectingBuilder--4fqDQdE.js.map +1 -0
- package/dist/{CompilerStrategy-Cv1woBmO.js → CompilerStrategy-yKw-Egxv.js} +19 -8
- package/dist/CompilerStrategy-yKw-Egxv.js.map +1 -0
- package/dist/InternalColumnType-G9zV9StN.js +16 -0
- package/dist/InternalColumnType-G9zV9StN.js.map +1 -0
- package/dist/InternalOperationKind-Bt6Weuon.js +19 -0
- package/dist/InternalOperationKind-Bt6Weuon.js.map +1 -0
- package/dist/{IntrospectorStrategy-BM1Eizfc.js → IntrospectorStrategy-blvwSU3_.js} +13 -4
- package/dist/IntrospectorStrategy-blvwSU3_.js.map +1 -0
- package/dist/Migration-DYQ0hUG7.js +30 -0
- package/dist/Migration-DYQ0hUG7.js.map +1 -0
- package/dist/{MigrationGenerator-Z39LTKmC.js → MigrationGenerator-B1p0jHnx.js} +20 -12
- package/dist/MigrationGenerator-B1p0jHnx.js.map +1 -0
- package/dist/{MigrationRunner-CCFuPUlr.js → MigrationRunner-D1ZfbbS-.js} +52 -9
- package/dist/MigrationRunner-D1ZfbbS-.js.map +1 -0
- package/dist/MigrationSqlSafetyAdapter-CGRbB2k2.js +62 -0
- package/dist/MigrationSqlSafetyAdapter-CGRbB2k2.js.map +1 -0
- package/dist/{SqliteCompilerFactory-DwMwO7xY.js → SqliteCompilerFactory-BAodJW9n.js} +73 -51
- package/dist/SqliteCompilerFactory-BAodJW9n.js.map +1 -0
- package/dist/{SqliteIntrospector-BRdNt6KG.js → SqliteIntrospector-CWwPWhmA.js} +49 -5
- package/dist/SqliteIntrospector-CWwPWhmA.js.map +1 -0
- package/dist/builder/contracts/ColumnSpec.d.ts +2 -1
- package/dist/builder/contracts/UpdateReferentialAction.d.ts +1 -1
- package/dist/builder/index.js +4 -4
- package/dist/builder/ops/OpBuilder.d.ts +48 -7
- package/dist/builder/runtime/CollectingBuilder.d.ts +19 -1
- package/dist/{builder-Dtk8oP_Y.js → builder-xJ-Bq2pk.js} +51 -21
- package/dist/builder-xJ-Bq2pk.js.map +1 -0
- package/dist/cli-DhCn8xiS.js +313 -0
- package/dist/cli-DhCn8xiS.js.map +1 -0
- package/dist/cli.js +17 -169
- package/dist/cli.js.map +1 -1
- package/dist/commands/cli.d.ts +5 -0
- package/dist/commands/index.d.ts +4 -0
- package/dist/commands/index.js +17 -0
- package/dist/commands-DIJepqNg.js +10 -0
- package/dist/commands-DIJepqNg.js.map +1 -0
- package/dist/compilers/contracts/CompilerFactory.d.ts +4 -0
- package/dist/compilers/contracts/SQLCompiler.d.ts +4 -0
- package/dist/compilers/dialects/PostgresCompiler.d.ts +10 -1
- package/dist/compilers/dialects/SqliteCompiler.d.ts +10 -0
- package/dist/compilers/factories/PostgresCompilerFactory.d.ts +9 -0
- package/dist/compilers/factories/SqliteCompilerFactory.d.ts +9 -0
- package/dist/compilers/index.js +5 -4
- package/dist/{compilers-D8DJuTnQ.js → compilers-dRN0Hzev.js} +2 -2
- package/dist/{compilers-D8DJuTnQ.js.map → compilers-dRN0Hzev.js.map} +1 -1
- package/dist/diff/diffSchema.d.ts +6 -1
- package/dist/diff/index.js +6 -6
- package/dist/{diff-Cs0TPEGR.js → diff-CZZbXAPN.js} +2 -2
- package/dist/{diff-Cs0TPEGR.js.map → diff-CZZbXAPN.js.map} +1 -1
- package/dist/{diffSchema-KgGHP-s3.js → diffSchema-D4oemTWS.js} +5 -4
- package/dist/diffSchema-D4oemTWS.js.map +1 -0
- package/dist/domain/Migration.d.ts +17 -0
- package/dist/domain/MigrationOperation.d.ts +14 -13
- package/dist/domain/index.js +2 -2
- package/dist/domain/internal/InternalColumnType.d.ts +11 -10
- package/dist/domain/internal/InternalDialect.d.ts +5 -4
- package/dist/domain/internal/InternalMigrationMode.d.ts +5 -4
- package/dist/domain/internal/InternalOperationKind.d.ts +14 -13
- package/dist/domain/internal/InternalReferentialAction.d.ts +7 -6
- package/dist/{domain-BXVlG0C0.js → domain-CwR-kUNS.js} +2 -2
- package/dist/{domain-BXVlG0C0.js.map → domain-CwR-kUNS.js.map} +1 -1
- package/dist/generator/MigrationGenerator.d.ts +13 -0
- package/dist/generator/index.js +3 -3
- package/dist/{generator-3yC60b1u.js → generator-DK-_f-PF.js} +2 -2
- package/dist/{generator-3yC60b1u.js.map → generator-DK-_f-PF.js.map} +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +24 -20
- package/dist/internal/MigrationSqlSafetyAdapter.d.ts +24 -0
- package/dist/introspect/DatabaseIntrospector.d.ts +8 -0
- package/dist/introspect/PostgresIntrospector.d.ts +14 -0
- package/dist/introspect/SqliteIntrospector.d.ts +13 -0
- package/dist/introspect/index.js +3 -2
- package/dist/{introspect-ks-QSodq.js → introspect-TPv_jeD6.js} +2 -2
- package/dist/{introspect-ks-QSodq.js.map → introspect-TPv_jeD6.js.map} +1 -1
- package/dist/runner/MigrationRunner.d.ts +7 -1
- package/dist/runner/index.js +9 -8
- package/dist/{runner-BOs-tItW.js → runner-C97xT8_W.js} +2 -2
- package/dist/{runner-BOs-tItW.js.map → runner-C97xT8_W.js.map} +1 -1
- package/dist/runtime/loadModule.d.ts +15 -0
- package/dist/strategies/CompilerStrategy.d.ts +19 -1
- package/dist/strategies/IntrospectorStrategy.d.ts +16 -1
- package/dist/strategies/index.js +8 -7
- package/dist/{strategies-BvHwf4as.js → strategies-D9ymSvbG.js} +3 -3
- package/dist/{strategies-BvHwf4as.js.map → strategies-D9ymSvbG.js.map} +1 -1
- package/package.json +98 -92
- package/dist/CollectingBuilder-C6qnwyrb.js.map +0 -1
- package/dist/CompilerStrategy-Cv1woBmO.js.map +0 -1
- package/dist/InternalColumnType-_YAz7RqI.js +0 -17
- package/dist/InternalColumnType-_YAz7RqI.js.map +0 -1
- package/dist/InternalOperationKind-BPVoOQwD.js +0 -20
- package/dist/InternalOperationKind-BPVoOQwD.js.map +0 -1
- package/dist/IntrospectorStrategy-BM1Eizfc.js.map +0 -1
- package/dist/Migration-D9J6ZbLP.js +0 -25
- package/dist/Migration-D9J6ZbLP.js.map +0 -1
- package/dist/MigrationGenerator-Z39LTKmC.js.map +0 -1
- package/dist/MigrationRunner-CCFuPUlr.js.map +0 -1
- package/dist/SqliteCompilerFactory-DwMwO7xY.js.map +0 -1
- package/dist/SqliteIntrospector-BRdNt6KG.js.map +0 -1
- package/dist/builder/ops/OpBuilder.js +0 -173
- package/dist/builder-Dtk8oP_Y.js.map +0 -1
- package/dist/diffSchema-KgGHP-s3.js.map +0 -1
- package/dist/domain/MigrationOperation.js +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Pedro Del Moral Lopez
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# @danceroutine/tango-migrations
|
|
2
|
+
|
|
3
|
+
`@danceroutine/tango-migrations` manages schema evolution for Tango applications.
|
|
4
|
+
|
|
5
|
+
This package exists because model metadata and database schema do not stay aligned on their own. Once a project begins to change, teams need a disciplined way to describe schema changes, compare model intent with the actual database, review what will happen next, and apply those changes in a predictable order. Tango keeps that workflow in one package so that migrations remain a first-class part of application development rather than an afterthought bolted onto the ORM.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pnpm add @danceroutine/tango-migrations
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Install the database driver for the dialect you use:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add pg
|
|
17
|
+
# or
|
|
18
|
+
pnpm add better-sqlite3
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
If you want the `tango` executable for generation and apply workflows, also install:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pnpm add -D @danceroutine/tango-cli
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## How the migration workflow fits together
|
|
28
|
+
|
|
29
|
+
The package supports three related jobs:
|
|
30
|
+
|
|
31
|
+
1. describe schema changes with migration classes and operation builders
|
|
32
|
+
2. compare model metadata with a live database schema and generate a migration plan
|
|
33
|
+
3. apply migrations in order and record what has already run
|
|
34
|
+
|
|
35
|
+
That gives you a workflow that moves from intent to review to execution, rather than asking the database schema to evolve through ad hoc scripts.
|
|
36
|
+
|
|
37
|
+
## Quick start
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { Migration, op } from '@danceroutine/tango-migrations';
|
|
41
|
+
|
|
42
|
+
export default class CreatePosts extends Migration {
|
|
43
|
+
id = '20260302_create_posts';
|
|
44
|
+
|
|
45
|
+
up(m) {
|
|
46
|
+
m.run(
|
|
47
|
+
op.table('posts').create((cols) => {
|
|
48
|
+
cols.add('id', (b) => b.serial().primaryKey());
|
|
49
|
+
cols.add('title', (b) => b.text().notNull());
|
|
50
|
+
})
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
down(m) {
|
|
55
|
+
m.run(op.table('posts').drop());
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
This class-based structure is the core of the package. A migration names a change set and defines how to apply it and, when possible, how to reverse it.
|
|
61
|
+
|
|
62
|
+
## Using the CLI
|
|
63
|
+
|
|
64
|
+
Most application developers should use this package through the shared `tango` CLI:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
tango make:migrations --dialect sqlite --models ./src/models.ts --dir ./migrations --name add_posts
|
|
68
|
+
tango plan --dialect sqlite --dir ./migrations --db ./app.sqlite
|
|
69
|
+
tango migrate --dialect sqlite --dir ./migrations --db ./app.sqlite
|
|
70
|
+
tango status --dialect sqlite --dir ./migrations --db ./app.sqlite
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
That command surface is provided by `@danceroutine/tango-cli`, but the migration behavior itself still lives in this package.
|
|
74
|
+
|
|
75
|
+
## Public API
|
|
76
|
+
|
|
77
|
+
The root export gives you the main concepts you need:
|
|
78
|
+
|
|
79
|
+
- `Migration`, the base class for migration files
|
|
80
|
+
- `op`, `OpBuilder`, and `CollectingBuilder`, which describe migration operations
|
|
81
|
+
- `diffSchema()`, which compares model metadata to an introspected schema
|
|
82
|
+
- `MigrationGenerator` and `MigrationRunner`
|
|
83
|
+
- SQL compilers, introspectors, and dialect strategies for PostgreSQL and SQLite
|
|
84
|
+
- `registerMigrationsCommands()`, which mounts the migration command tree into the CLI
|
|
85
|
+
|
|
86
|
+
The package also exposes subpaths such as `builder`, `runner`, `generator`, `diff`, `compilers`, `introspect`, `strategies`, and `commands` when you want a narrower import boundary.
|
|
87
|
+
|
|
88
|
+
## Documentation
|
|
89
|
+
|
|
90
|
+
- Official documentation: <https://tangowebframework.dev>
|
|
91
|
+
- Migrations topic: <https://tangowebframework.dev/topics/migrations>
|
|
92
|
+
- Generate and apply migrations: <https://tangowebframework.dev/how-to/generate-and-apply-migrations>
|
|
93
|
+
|
|
94
|
+
## Development
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
pnpm --filter @danceroutine/tango-migrations build
|
|
98
|
+
pnpm --filter @danceroutine/tango-migrations typecheck
|
|
99
|
+
pnpm --filter @danceroutine/tango-migrations test
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The package also has integration coverage for dialect-specific behavior:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
pnpm --filter @danceroutine/tango-migrations test:integration
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
For the wider contributor workflow, use:
|
|
109
|
+
|
|
110
|
+
- <https://tangowebframework.dev/contributing>
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
MIT
|
|
@@ -2,22 +2,37 @@
|
|
|
2
2
|
//#region src/builder/runtime/CollectingBuilder.ts
|
|
3
3
|
var CollectingBuilder = class CollectingBuilder {
|
|
4
4
|
static BRAND = "tango.migrations.collecting_builder";
|
|
5
|
-
mode;
|
|
6
5
|
__tangoBrand = CollectingBuilder.BRAND;
|
|
7
6
|
ops = [];
|
|
8
7
|
dataFns = [];
|
|
8
|
+
mode;
|
|
9
|
+
/**
|
|
10
|
+
* Narrow an unknown value to the in-memory builder used during migration collection.
|
|
11
|
+
*/
|
|
9
12
|
static isCollectingBuilder(value) {
|
|
10
13
|
return typeof value === "object" && value !== null && value.__tangoBrand === CollectingBuilder.BRAND;
|
|
11
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Append schema operations to the collection.
|
|
17
|
+
*/
|
|
12
18
|
run(...ops) {
|
|
13
19
|
this.ops.push(...ops);
|
|
14
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Register a data migration callback.
|
|
23
|
+
*/
|
|
15
24
|
data(fn) {
|
|
16
25
|
this.dataFns.push(fn);
|
|
17
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Set execution options for the migration.
|
|
29
|
+
*/
|
|
18
30
|
options(o) {
|
|
19
31
|
this.mode = o.mode;
|
|
20
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Get the currently configured migration mode.
|
|
35
|
+
*/
|
|
21
36
|
getMode() {
|
|
22
37
|
return this.mode;
|
|
23
38
|
}
|
|
@@ -25,4 +40,4 @@ var CollectingBuilder = class CollectingBuilder {
|
|
|
25
40
|
|
|
26
41
|
//#endregion
|
|
27
42
|
export { CollectingBuilder };
|
|
28
|
-
//# sourceMappingURL=CollectingBuilder
|
|
43
|
+
//# sourceMappingURL=CollectingBuilder--4fqDQdE.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CollectingBuilder--4fqDQdE.js","names":["value: unknown","fn: (ctx: { query(sql: string, params?: readonly unknown[]): Promise<void> }) => Promise<void>","o: { mode?: MigrationMode }"],"sources":["../src/builder/runtime/CollectingBuilder.ts"],"sourcesContent":["import type { Builder } from '../contracts/Builder';\nimport type { MigrationOperation } from '../../domain/MigrationOperation';\nimport type { MigrationMode } from '../../domain/MigrationMode';\n\n/**\n * In-memory builder that collects migration operations and data callbacks.\n */\nexport class CollectingBuilder implements Builder {\n static readonly BRAND = 'tango.migrations.collecting_builder' as const;\n readonly __tangoBrand: typeof CollectingBuilder.BRAND = CollectingBuilder.BRAND;\n ops: MigrationOperation[] = [];\n dataFns: Array<(ctx: { query(sql: string, params?: readonly unknown[]): Promise<void> }) => Promise<void>> = [];\n private mode?: MigrationMode;\n\n /**\n * Narrow an unknown value to the in-memory builder used during migration collection.\n */\n static isCollectingBuilder(value: unknown): value is CollectingBuilder {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === CollectingBuilder.BRAND\n );\n }\n\n /**\n * Append schema operations to the collection.\n */\n run(...ops: MigrationOperation[]): void {\n this.ops.push(...ops);\n }\n\n /**\n * Register a data migration callback.\n */\n data(fn: (ctx: { query(sql: string, params?: readonly unknown[]): Promise<void> }) => Promise<void>): void {\n this.dataFns.push(fn);\n }\n\n /**\n * Set execution options for the migration.\n */\n options(o: { mode?: MigrationMode }): void {\n this.mode = o.mode;\n }\n\n /**\n * Get the currently configured migration mode.\n */\n getMode(): MigrationMode | undefined {\n return this.mode;\n }\n}\n"],"mappings":";;IAOa,oBAAN,MAAM,kBAAqC;CAC9C,OAAgB,QAAQ;CACxB,eAAwD,kBAAkB;CAC1E,MAA4B,CAAE;CAC9B,UAA6G,CAAE;CAC/G;;;;CAKA,OAAO,oBAAoBA,OAA4C;AACnE,gBACW,UAAU,YACjB,UAAU,QACT,MAAqC,iBAAiB,kBAAkB;CAEhF;;;;CAKD,IAAI,GAAG,KAAiC;AACpC,OAAK,IAAI,KAAK,GAAG,IAAI;CACxB;;;;CAKD,KAAKC,IAAsG;AACvG,OAAK,QAAQ,KAAK,GAAG;CACxB;;;;CAKD,QAAQC,GAAmC;AACvC,OAAK,OAAO,EAAE;CACjB;;;;CAKD,UAAqC;AACjC,SAAO,KAAK;CACf;AACJ"}
|
|
@@ -1,25 +1,30 @@
|
|
|
1
|
-
import { PostgresCompilerFactory, SqliteCompilerFactory } from "./SqliteCompilerFactory-
|
|
1
|
+
import { PostgresCompilerFactory, SqliteCompilerFactory } from "./SqliteCompilerFactory-BAodJW9n.js";
|
|
2
2
|
|
|
3
3
|
//#region src/domain/internal/InternalDialect.ts
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}({});
|
|
4
|
+
const InternalDialect = {
|
|
5
|
+
POSTGRES: "postgres",
|
|
6
|
+
SQLITE: "sqlite"
|
|
7
|
+
};
|
|
9
8
|
|
|
10
9
|
//#endregion
|
|
11
10
|
//#region src/strategies/CompilerStrategy.ts
|
|
12
11
|
var CompilerStrategy = class CompilerStrategy {
|
|
13
12
|
static BRAND = "tango.migrations.compiler_strategy";
|
|
13
|
+
__tangoBrand = CompilerStrategy.BRAND;
|
|
14
14
|
compilerCache = new Map();
|
|
15
15
|
customHandlers = new Map();
|
|
16
|
-
__tangoBrand = CompilerStrategy.BRAND;
|
|
17
16
|
constructor(factories) {
|
|
18
17
|
this.factories = factories;
|
|
19
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Narrow an unknown value to the dialect-aware migration compiler strategy.
|
|
21
|
+
*/
|
|
20
22
|
static isCompilerStrategy(value) {
|
|
21
23
|
return typeof value === "object" && value !== null && value.__tangoBrand === CompilerStrategy.BRAND;
|
|
22
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Compile a migration operation to SQL for a target dialect.
|
|
27
|
+
*/
|
|
23
28
|
compile(dialect, operation) {
|
|
24
29
|
if (operation.kind === "custom") {
|
|
25
30
|
const handler = this.customHandlers.get(operation.name);
|
|
@@ -29,10 +34,16 @@ var CompilerStrategy = class CompilerStrategy {
|
|
|
29
34
|
const compiler = this.getCompiler(dialect);
|
|
30
35
|
return compiler.compile(operation);
|
|
31
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Register a handler for custom migration operations.
|
|
39
|
+
*/
|
|
32
40
|
registerCustomHandler(name, handler) {
|
|
33
41
|
this.customHandlers.set(name, handler);
|
|
34
42
|
return this;
|
|
35
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Resolve and cache a compiler instance for a dialect.
|
|
46
|
+
*/
|
|
36
47
|
getCompiler(dialect) {
|
|
37
48
|
const cached = this.compilerCache.get(dialect);
|
|
38
49
|
if (cached) return cached;
|
|
@@ -52,4 +63,4 @@ function createDefaultCompilerStrategy() {
|
|
|
52
63
|
|
|
53
64
|
//#endregion
|
|
54
65
|
export { CompilerStrategy, InternalDialect, createDefaultCompilerStrategy };
|
|
55
|
-
//# sourceMappingURL=CompilerStrategy-
|
|
66
|
+
//# sourceMappingURL=CompilerStrategy-yKw-Egxv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CompilerStrategy-yKw-Egxv.js","names":["factories: CompilerFactoryRegistry","value: unknown","dialect: Dialect","operation: MigrationOperation","name: TName","handler: (dialect: Dialect, op: CustomMigrationOperation<TName, TArgs>) => SQL[]"],"sources":["../src/domain/internal/InternalDialect.ts","../src/strategies/CompilerStrategy.ts"],"sourcesContent":["export const InternalDialect = {\n POSTGRES: 'postgres',\n SQLITE: 'sqlite',\n} as const;\n\nexport type InternalDialect = (typeof InternalDialect)[keyof typeof InternalDialect];\n","import { PostgresCompilerFactory } from '../compilers/factories/PostgresCompilerFactory';\nimport { SqliteCompilerFactory } from '../compilers/factories/SqliteCompilerFactory';\nimport type { CompilerFactory } from '../compilers/contracts/CompilerFactory';\nimport type { Dialect } from '../domain/Dialect';\nimport type { CustomMigrationOperation, MigrationOperation } from '../domain/MigrationOperation';\nimport type { SQL } from '../compilers/contracts/SQL';\nimport type { SQLCompiler } from '../compilers/contracts/SQLCompiler';\nimport { InternalDialect } from '../domain/internal/InternalDialect';\n\ntype CompilerFactoryRegistry = Record<Dialect, CompilerFactory>;\n\n/**\n * Dialect-aware SQL compiler orchestration with optional custom-op handlers.\n */\nexport class CompilerStrategy {\n static readonly BRAND = 'tango.migrations.compiler_strategy' as const;\n readonly __tangoBrand: typeof CompilerStrategy.BRAND = CompilerStrategy.BRAND;\n private readonly compilerCache = new Map<Dialect, SQLCompiler>();\n private readonly customHandlers = new Map<string, (dialect: Dialect, op: CustomMigrationOperation) => SQL[]>();\n\n constructor(private readonly factories: CompilerFactoryRegistry) {}\n\n /**\n * Narrow an unknown value to the dialect-aware migration compiler strategy.\n */\n static isCompilerStrategy(value: unknown): value is CompilerStrategy {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === CompilerStrategy.BRAND\n );\n }\n\n /**\n * Compile a migration operation to SQL for a target dialect.\n */\n compile(dialect: Dialect, operation: MigrationOperation): SQL[] {\n if (operation.kind === 'custom') {\n const handler = this.customHandlers.get(operation.name);\n if (!handler) {\n throw new Error(`Unsupported custom migration op: ${operation.name}`);\n }\n return handler(dialect, operation);\n }\n const compiler = this.getCompiler(dialect);\n return compiler.compile(operation);\n }\n\n /**\n * Register a handler for custom migration operations.\n */\n registerCustomHandler<TName extends string, TArgs extends object>(\n name: TName,\n handler: (dialect: Dialect, op: CustomMigrationOperation<TName, TArgs>) => SQL[]\n ): this {\n this.customHandlers.set(name, handler as (dialect: Dialect, op: CustomMigrationOperation) => SQL[]);\n return this;\n }\n\n /**\n * Resolve and cache a compiler instance for a dialect.\n */\n getCompiler(dialect: Dialect): SQLCompiler {\n const cached = this.compilerCache.get(dialect);\n if (cached) {\n return cached;\n }\n\n const factory = this.factories[dialect];\n if (!factory) {\n throw new Error(`No SQL compiler factory registered for dialect: ${String(dialect)}`);\n }\n const compiler = factory.create();\n this.compilerCache.set(dialect, compiler);\n return compiler;\n }\n}\n\n/**\n * Create the default compiler strategy with built-in dialect factories.\n */\nexport function createDefaultCompilerStrategy(): CompilerStrategy {\n return new CompilerStrategy({\n [InternalDialect.POSTGRES]: new PostgresCompilerFactory(),\n [InternalDialect.SQLITE]: new SqliteCompilerFactory(),\n });\n}\n"],"mappings":";;;MAAa,kBAAkB;CAC3B,UAAU;CACV,QAAQ;AACX;;;;ICWY,mBAAN,MAAM,iBAAiB;CAC1B,OAAgB,QAAQ;CACxB,eAAuD,iBAAiB;CACxE,gBAAiC,IAAI;CACrC,iBAAkC,IAAI;CAEtC,YAA6BA,WAAoC;AAAA,OAApC,YAAA;CAAsC;;;;CAKnE,OAAO,mBAAmBC,OAA2C;AACjE,gBACW,UAAU,YACjB,UAAU,QACT,MAAqC,iBAAiB,iBAAiB;CAE/E;;;;CAKD,QAAQC,SAAkBC,WAAsC;AAC5D,MAAI,UAAU,SAAS,UAAU;GAC7B,MAAM,UAAU,KAAK,eAAe,IAAI,UAAU,KAAK;AACvD,QAAK,QACD,OAAM,IAAI,OAAO,mCAAmC,UAAU,KAAK;AAEvE,UAAO,QAAQ,SAAS,UAAU;EACrC;EACD,MAAM,WAAW,KAAK,YAAY,QAAQ;AAC1C,SAAO,SAAS,QAAQ,UAAU;CACrC;;;;CAKD,sBACIC,MACAC,SACI;AACJ,OAAK,eAAe,IAAI,MAAM,QAAqE;AACnG,SAAO;CACV;;;;CAKD,YAAYH,SAA+B;EACvC,MAAM,SAAS,KAAK,cAAc,IAAI,QAAQ;AAC9C,MAAI,OACA,QAAO;EAGX,MAAM,UAAU,KAAK,UAAU;AAC/B,OAAK,QACD,OAAM,IAAI,OAAO,kDAAkD,OAAO,QAAQ,CAAC;EAEvF,MAAM,WAAW,QAAQ,QAAQ;AACjC,OAAK,cAAc,IAAI,SAAS,SAAS;AACzC,SAAO;CACV;AACJ;AAKM,SAAS,gCAAkD;AAC9D,QAAO,IAAI,iBAAiB;GACvB,gBAAgB,WAAW,IAAI;GAC/B,gBAAgB,SAAS,IAAI;CACjC;AACJ"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/domain/internal/InternalColumnType.ts
|
|
3
|
+
const InternalColumnType = {
|
|
4
|
+
SERIAL: "serial",
|
|
5
|
+
INT: "int",
|
|
6
|
+
BIGINT: "bigint",
|
|
7
|
+
TEXT: "text",
|
|
8
|
+
BOOL: "bool",
|
|
9
|
+
TIMESTAMPTZ: "timestamptz",
|
|
10
|
+
JSONB: "jsonb",
|
|
11
|
+
UUID: "uuid"
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
//#endregion
|
|
15
|
+
export { InternalColumnType };
|
|
16
|
+
//# sourceMappingURL=InternalColumnType-G9zV9StN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InternalColumnType-G9zV9StN.js","names":[],"sources":["../src/domain/internal/InternalColumnType.ts"],"sourcesContent":["export const InternalColumnType = {\n SERIAL: 'serial',\n INT: 'int',\n BIGINT: 'bigint',\n TEXT: 'text',\n BOOL: 'bool',\n TIMESTAMPTZ: 'timestamptz',\n JSONB: 'jsonb',\n UUID: 'uuid',\n} as const;\n\nexport type InternalColumnType = (typeof InternalColumnType)[keyof typeof InternalColumnType];\n"],"mappings":";;MAAa,qBAAqB;CAC9B,QAAQ;CACR,KAAK;CACL,QAAQ;CACR,MAAM;CACN,MAAM;CACN,aAAa;CACb,OAAO;CACP,MAAM;AACT"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/domain/internal/InternalOperationKind.ts
|
|
3
|
+
const InternalOperationKind = {
|
|
4
|
+
TABLE_CREATE: "table.create",
|
|
5
|
+
TABLE_DROP: "table.drop",
|
|
6
|
+
COLUMN_ADD: "column.add",
|
|
7
|
+
COLUMN_DROP: "column.drop",
|
|
8
|
+
COLUMN_ALTER: "column.alter",
|
|
9
|
+
COLUMN_RENAME: "column.rename",
|
|
10
|
+
INDEX_CREATE: "index.create",
|
|
11
|
+
INDEX_DROP: "index.drop",
|
|
12
|
+
FK_CREATE: "fk.create",
|
|
13
|
+
FK_VALIDATE: "fk.validate",
|
|
14
|
+
FK_DROP: "fk.drop"
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
export { InternalOperationKind };
|
|
19
|
+
//# sourceMappingURL=InternalOperationKind-Bt6Weuon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InternalOperationKind-Bt6Weuon.js","names":[],"sources":["../src/domain/internal/InternalOperationKind.ts"],"sourcesContent":["export const InternalOperationKind = {\n TABLE_CREATE: 'table.create',\n TABLE_DROP: 'table.drop',\n COLUMN_ADD: 'column.add',\n COLUMN_DROP: 'column.drop',\n COLUMN_ALTER: 'column.alter',\n COLUMN_RENAME: 'column.rename',\n INDEX_CREATE: 'index.create',\n INDEX_DROP: 'index.drop',\n FK_CREATE: 'fk.create',\n FK_VALIDATE: 'fk.validate',\n FK_DROP: 'fk.drop',\n} as const;\n\nexport type InternalOperationKind = (typeof InternalOperationKind)[keyof typeof InternalOperationKind];\n"],"mappings":";;MAAa,wBAAwB;CACjC,cAAc;CACd,YAAY;CACZ,YAAY;CACZ,aAAa;CACb,cAAc;CACd,eAAe;CACf,cAAc;CACd,YAAY;CACZ,WAAW;CACX,aAAa;CACb,SAAS;AACZ"}
|
|
@@ -1,21 +1,30 @@
|
|
|
1
|
-
import { InternalDialect } from "./CompilerStrategy-
|
|
2
|
-
import { PostgresIntrospector, SqliteIntrospector } from "./SqliteIntrospector-
|
|
1
|
+
import { InternalDialect } from "./CompilerStrategy-yKw-Egxv.js";
|
|
2
|
+
import { PostgresIntrospector, SqliteIntrospector } from "./SqliteIntrospector-CWwPWhmA.js";
|
|
3
3
|
|
|
4
4
|
//#region src/strategies/IntrospectorStrategy.ts
|
|
5
5
|
var IntrospectorStrategy = class IntrospectorStrategy {
|
|
6
6
|
static BRAND = "tango.migrations.introspector_strategy";
|
|
7
|
-
introspectorCache = new Map();
|
|
8
7
|
__tangoBrand = IntrospectorStrategy.BRAND;
|
|
8
|
+
introspectorCache = new Map();
|
|
9
9
|
constructor(factories) {
|
|
10
10
|
this.factories = factories;
|
|
11
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Narrow an unknown value to the dialect-aware schema introspection strategy.
|
|
14
|
+
*/
|
|
12
15
|
static isIntrospectorStrategy(value) {
|
|
13
16
|
return typeof value === "object" && value !== null && value.__tangoBrand === IntrospectorStrategy.BRAND;
|
|
14
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Introspect database schema using a dialect-specific introspector.
|
|
20
|
+
*/
|
|
15
21
|
introspect(dialect, client) {
|
|
16
22
|
const introspector = this.getIntrospector(dialect);
|
|
17
23
|
return introspector.introspect(client);
|
|
18
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Resolve and cache an introspector instance for a dialect.
|
|
27
|
+
*/
|
|
19
28
|
getIntrospector(dialect) {
|
|
20
29
|
const cached = this.introspectorCache.get(dialect);
|
|
21
30
|
if (cached) return cached;
|
|
@@ -35,4 +44,4 @@ function createDefaultIntrospectorStrategy() {
|
|
|
35
44
|
|
|
36
45
|
//#endregion
|
|
37
46
|
export { IntrospectorStrategy, createDefaultIntrospectorStrategy };
|
|
38
|
-
//# sourceMappingURL=IntrospectorStrategy-
|
|
47
|
+
//# sourceMappingURL=IntrospectorStrategy-blvwSU3_.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IntrospectorStrategy-blvwSU3_.js","names":["factories: IntrospectorFactoryRegistry","value: unknown","dialect: Dialect","client: DBClient"],"sources":["../src/strategies/IntrospectorStrategy.ts"],"sourcesContent":["import type { DBClient, DatabaseIntrospector } from '../introspect/DatabaseIntrospector';\nimport type { Dialect } from '../domain/Dialect';\nimport { InternalDialect } from '../domain/internal/InternalDialect';\nimport type { DbSchema } from '../introspect/PostgresIntrospector';\nimport { PostgresIntrospector } from '../introspect/PostgresIntrospector';\nimport { SqliteIntrospector } from '../introspect/SqliteIntrospector';\n\ntype IntrospectorFactory = {\n create(): DatabaseIntrospector;\n};\n\ntype IntrospectorFactoryRegistry = Record<Dialect, IntrospectorFactory>;\n\n/**\n * Dialect-aware schema introspection orchestration.\n */\nexport class IntrospectorStrategy {\n static readonly BRAND = 'tango.migrations.introspector_strategy' as const;\n readonly __tangoBrand: typeof IntrospectorStrategy.BRAND = IntrospectorStrategy.BRAND;\n private readonly introspectorCache = new Map<Dialect, DatabaseIntrospector>();\n\n constructor(private readonly factories: IntrospectorFactoryRegistry) {}\n\n /**\n * Narrow an unknown value to the dialect-aware schema introspection strategy.\n */\n static isIntrospectorStrategy(value: unknown): value is IntrospectorStrategy {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === IntrospectorStrategy.BRAND\n );\n }\n\n /**\n * Introspect database schema using a dialect-specific introspector.\n */\n introspect(dialect: Dialect, client: DBClient): Promise<DbSchema> {\n const introspector = this.getIntrospector(dialect);\n return introspector.introspect(client);\n }\n\n /**\n * Resolve and cache an introspector instance for a dialect.\n */\n getIntrospector(dialect: Dialect): DatabaseIntrospector {\n const cached = this.introspectorCache.get(dialect);\n if (cached) {\n return cached;\n }\n\n const factory = this.factories[dialect];\n if (!factory) {\n throw new Error(`No database introspector factory registered for dialect: ${String(dialect)}`);\n }\n\n const introspector = factory.create();\n this.introspectorCache.set(dialect, introspector);\n return introspector;\n }\n}\n\n/**\n * Create the default introspector strategy with built-in dialect support.\n */\nexport function createDefaultIntrospectorStrategy(): IntrospectorStrategy {\n return new IntrospectorStrategy({\n [InternalDialect.POSTGRES]: { create: () => new PostgresIntrospector() },\n [InternalDialect.SQLITE]: { create: () => new SqliteIntrospector() },\n });\n}\n"],"mappings":";;;;IAgBa,uBAAN,MAAM,qBAAqB;CAC9B,OAAgB,QAAQ;CACxB,eAA2D,qBAAqB;CAChF,oBAAqC,IAAI;CAEzC,YAA6BA,WAAwC;AAAA,OAAxC,YAAA;CAA0C;;;;CAKvE,OAAO,uBAAuBC,OAA+C;AACzE,gBACW,UAAU,YACjB,UAAU,QACT,MAAqC,iBAAiB,qBAAqB;CAEnF;;;;CAKD,WAAWC,SAAkBC,QAAqC;EAC9D,MAAM,eAAe,KAAK,gBAAgB,QAAQ;AAClD,SAAO,aAAa,WAAW,OAAO;CACzC;;;;CAKD,gBAAgBD,SAAwC;EACpD,MAAM,SAAS,KAAK,kBAAkB,IAAI,QAAQ;AAClD,MAAI,OACA,QAAO;EAGX,MAAM,UAAU,KAAK,UAAU;AAC/B,OAAK,QACD,OAAM,IAAI,OAAO,2DAA2D,OAAO,QAAQ,CAAC;EAGhG,MAAM,eAAe,QAAQ,QAAQ;AACrC,OAAK,kBAAkB,IAAI,SAAS,aAAa;AACjD,SAAO;CACV;AACJ;AAKM,SAAS,oCAA0D;AACtE,QAAO,IAAI,qBAAqB;GAC3B,gBAAgB,WAAW,EAAE,QAAQ,MAAM,IAAI,uBAAwB;GACvE,gBAAgB,SAAS,EAAE,QAAQ,MAAM,IAAI,qBAAsB;CACvE;AACJ"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/domain/Migration.ts
|
|
3
|
+
var Migration = class Migration {
|
|
4
|
+
static BRAND = "tango.migration";
|
|
5
|
+
static CONSTRUCTOR_BRAND = "tango.migration.constructor";
|
|
6
|
+
static __tangoConstructorBrand = Migration.CONSTRUCTOR_BRAND;
|
|
7
|
+
__tangoBrand = Migration.BRAND;
|
|
8
|
+
/** Optional execution mode override (`online`/`offline`). */
|
|
9
|
+
mode;
|
|
10
|
+
/**
|
|
11
|
+
* Narrow an unknown value to a migration instance.
|
|
12
|
+
*/
|
|
13
|
+
static isMigration(value) {
|
|
14
|
+
return typeof value === "object" && value !== null && value.__tangoBrand === Migration.BRAND;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Narrow an unknown value to a migration constructor.
|
|
18
|
+
*/
|
|
19
|
+
static isMigrationConstructor(value) {
|
|
20
|
+
if (typeof value !== "function") return false;
|
|
21
|
+
const prototype = value.prototype;
|
|
22
|
+
if (typeof prototype !== "object" || prototype === null) return false;
|
|
23
|
+
if (typeof prototype.up !== "function" || typeof prototype.down !== "function") return false;
|
|
24
|
+
return value.__tangoConstructorBrand === Migration.CONSTRUCTOR_BRAND;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
export { Migration };
|
|
30
|
+
//# sourceMappingURL=Migration-DYQ0hUG7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Migration-DYQ0hUG7.js","names":["value: unknown"],"sources":["../src/domain/Migration.ts"],"sourcesContent":["import type { Builder } from '../builder/contracts/Builder';\nimport type { MigrationMode } from './MigrationMode';\n\n/**\n * Base migration contract.\n *\n * Concrete migrations provide a stable `id` and define reversible schema/data\n * operations through `up` and `down`.\n */\nexport abstract class Migration {\n static readonly BRAND = 'tango.migration' as const;\n static readonly CONSTRUCTOR_BRAND = 'tango.migration.constructor' as const;\n static readonly __tangoConstructorBrand: typeof Migration.CONSTRUCTOR_BRAND = Migration.CONSTRUCTOR_BRAND;\n readonly __tangoBrand: typeof Migration.BRAND = Migration.BRAND;\n\n abstract id: string;\n /** Optional execution mode override (`online`/`offline`). */\n mode?: MigrationMode;\n /** Apply migration operations. */\n abstract up(m: Builder): void | Promise<void>;\n /** Revert migration operations. */\n abstract down(m: Builder): void | Promise<void>;\n\n /**\n * Narrow an unknown value to a migration instance.\n */\n static isMigration(value: unknown): value is Migration {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === Migration.BRAND\n );\n }\n\n /**\n * Narrow an unknown value to a migration constructor.\n */\n static isMigrationConstructor(value: unknown): value is new () => Migration {\n if (typeof value !== 'function') {\n return false;\n }\n\n const prototype = (value as { prototype?: unknown }).prototype;\n if (typeof prototype !== 'object' || prototype === null) {\n return false;\n }\n\n if (\n typeof (prototype as { up?: unknown }).up !== 'function' ||\n typeof (prototype as { down?: unknown }).down !== 'function'\n ) {\n return false;\n }\n\n return (value as { __tangoConstructorBrand?: unknown }).__tangoConstructorBrand === Migration.CONSTRUCTOR_BRAND;\n }\n}\n"],"mappings":";;IASsB,YAAf,MAAe,UAAU;CAC5B,OAAgB,QAAQ;CACxB,OAAgB,oBAAoB;CACpC,OAAgB,0BAA8D,UAAU;CACxF,eAAgD,UAAU;;CAI1D;;;;CASA,OAAO,YAAYA,OAAoC;AACnD,gBACW,UAAU,YACjB,UAAU,QACT,MAAqC,iBAAiB,UAAU;CAExE;;;;CAKD,OAAO,uBAAuBA,OAA8C;AACxE,aAAW,UAAU,WACjB,QAAO;EAGX,MAAM,YAAa,MAAkC;AACrD,aAAW,cAAc,YAAY,cAAc,KAC/C,QAAO;AAGX,aACY,UAA+B,OAAO,qBACtC,UAAiC,SAAS,WAElD,QAAO;AAGX,SAAQ,MAAgD,4BAA4B,UAAU;CACjG;AACJ"}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import { InternalOperationKind } from "./InternalOperationKind-
|
|
2
|
-
import { join } from "node:path";
|
|
1
|
+
import { InternalOperationKind } from "./InternalOperationKind-Bt6Weuon.js";
|
|
3
2
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { isTrustedSqlFragment } from "@danceroutine/tango-core";
|
|
4
5
|
|
|
5
6
|
//#region src/generator/MigrationGenerator.ts
|
|
6
7
|
var MigrationGenerator = class MigrationGenerator {
|
|
7
8
|
static BRAND = "tango.migrations.generator";
|
|
8
9
|
__tangoBrand = MigrationGenerator.BRAND;
|
|
10
|
+
/**
|
|
11
|
+
* Narrow an unknown value to `MigrationGenerator`.
|
|
12
|
+
*/
|
|
9
13
|
static isMigrationGenerator(value) {
|
|
10
14
|
return typeof value === "object" && value !== null && value.__tangoBrand === MigrationGenerator.BRAND;
|
|
11
15
|
}
|
|
@@ -30,15 +34,16 @@ var MigrationGenerator = class MigrationGenerator {
|
|
|
30
34
|
*/
|
|
31
35
|
render(id, operations) {
|
|
32
36
|
const upOps = operations.map((operation) => this.renderOperation(operation));
|
|
33
|
-
const downOps = operations.map((operation) => this.renderReverseOperation(operation))
|
|
37
|
+
const downOps = operations.map((operation) => this.renderReverseOperation(operation));
|
|
38
|
+
downOps.reverse();
|
|
34
39
|
const className = this.renderClassName(id);
|
|
35
40
|
const lines = [
|
|
36
|
-
`import { Migration, op } from '@danceroutine/tango-migrations';`,
|
|
41
|
+
`import { Migration, op, trustedSql, type Builder } from '@danceroutine/tango-migrations';`,
|
|
37
42
|
``,
|
|
38
43
|
`export default class ${className} extends Migration {`,
|
|
39
44
|
` id = '${id}';`,
|
|
40
45
|
``,
|
|
41
|
-
` up(m) {`,
|
|
46
|
+
` up(m: Builder) {`,
|
|
42
47
|
` m.run(`,
|
|
43
48
|
...upOps.map((code, index) => {
|
|
44
49
|
const comma = index < upOps.length - 1 ? "," : "";
|
|
@@ -47,7 +52,7 @@ var MigrationGenerator = class MigrationGenerator {
|
|
|
47
52
|
` );`,
|
|
48
53
|
` }`,
|
|
49
54
|
``,
|
|
50
|
-
` down(m) {`,
|
|
55
|
+
` down(m: Builder) {`,
|
|
51
56
|
` m.run(`,
|
|
52
57
|
...downOps.map((code, index) => {
|
|
53
58
|
const comma = index < downOps.length - 1 ? "," : "";
|
|
@@ -126,8 +131,8 @@ var MigrationGenerator = class MigrationGenerator {
|
|
|
126
131
|
if (operation.to.notNull !== undefined) parts.push(`notNull: ${operation.to.notNull}`);
|
|
127
132
|
if (operation.to.default !== undefined) {
|
|
128
133
|
if (operation.to.default === null) parts.push(`default: null`);
|
|
129
|
-
else if (
|
|
130
|
-
else if (
|
|
134
|
+
else if (this.isNowDefault(operation.to.default)) parts.push(`default: { now: true }`);
|
|
135
|
+
else if (isTrustedSqlFragment(operation.to.default)) parts.push(`default: trustedSql(${JSON.stringify(operation.to.default.sql)})`);
|
|
131
136
|
}
|
|
132
137
|
return `op.table('${operation.table}').alterColumn('${operation.column}', { ${parts.join(", ")} })`;
|
|
133
138
|
}
|
|
@@ -141,7 +146,7 @@ else if (typeof operation.to.default === "object" && operation.to.default.now) p
|
|
|
141
146
|
`on: [${operation.on.map((c) => `'${c}'`).join(", ")}]`
|
|
142
147
|
];
|
|
143
148
|
if (operation.unique) parts.push(`unique: true`);
|
|
144
|
-
if (operation.where) parts.push(`where:
|
|
149
|
+
if (operation.where) parts.push(`where: trustedSql(${JSON.stringify(operation.where.sql)})`);
|
|
145
150
|
if (operation.concurrently) parts.push(`concurrently: true`);
|
|
146
151
|
return `op.index.create({ ${parts.join(", ")} })`;
|
|
147
152
|
}
|
|
@@ -168,8 +173,8 @@ else if (typeof operation.to.default === "object" && operation.to.default.now) p
|
|
|
168
173
|
if (col.type) parts.push(`.${col.type}()`);
|
|
169
174
|
if (col.notNull) parts.push(`.notNull()`);
|
|
170
175
|
if (col.default !== undefined && col.default !== null) {
|
|
171
|
-
if (
|
|
172
|
-
else if (
|
|
176
|
+
if (this.isNowDefault(col.default)) parts.push(`.defaultNow()`);
|
|
177
|
+
else if (isTrustedSqlFragment(col.default)) parts.push(`.default(trustedSql(${JSON.stringify(col.default.sql)}))`);
|
|
173
178
|
} else if (col.default === null) parts.push(`.default(null)`);
|
|
174
179
|
if (col.primaryKey) parts.push(`.primaryKey()`);
|
|
175
180
|
if (col.unique) parts.push(`.unique()`);
|
|
@@ -192,8 +197,11 @@ else if (typeof col.default === "object" && col.default.now) parts.push(`.defaul
|
|
|
192
197
|
const seconds = String(now.getSeconds()).padStart(2, "0");
|
|
193
198
|
return `${year}${month}${day}${hours}${minutes}${seconds}`;
|
|
194
199
|
}
|
|
200
|
+
isNowDefault(value) {
|
|
201
|
+
return typeof value === "object" && value !== null && "now" in value && value.now === true;
|
|
202
|
+
}
|
|
195
203
|
};
|
|
196
204
|
|
|
197
205
|
//#endregion
|
|
198
206
|
export { MigrationGenerator };
|
|
199
|
-
//# sourceMappingURL=MigrationGenerator-
|
|
207
|
+
//# sourceMappingURL=MigrationGenerator-B1p0jHnx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MigrationGenerator-B1p0jHnx.js","names":["value: unknown","options: GenerateMigrationOptions","id: string","operations: MigrationOperation[]","code: string","index: number","operation: MigrationOperation","operation: TableCreate","operation: TableDrop","operation: ColumnAdd","operation: ColumnDrop","operation: ColumnAlter","parts: string[]","operation: ColumnRename","operation: IndexCreate","operation: IndexDrop","operation: ForeignKeyCreate","operation: ForeignKeyDrop","col: ColumnSpec","refParts: string[]","value: ColumnSpec['default']"],"sources":["../src/generator/MigrationGenerator.ts"],"sourcesContent":["import { isTrustedSqlFragment } from '@danceroutine/tango-core';\nimport type {\n MigrationOperation,\n TableCreate,\n TableDrop,\n ColumnAdd,\n ColumnDrop,\n ColumnAlter,\n ColumnRename,\n IndexCreate,\n IndexDrop,\n ForeignKeyCreate,\n ForeignKeyDrop,\n} from '../domain/MigrationOperation';\nimport type { ColumnSpec } from '../builder/contracts/ColumnSpec';\nimport { InternalOperationKind } from '../domain/internal/InternalOperationKind';\nimport { writeFile, mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\n\n/**\n * Input contract for generating a migration source file.\n */\nexport interface GenerateMigrationOptions {\n /** Human-readable suffix used in file name/id generation. */\n name: string;\n /** Ordered migration operations to render. */\n operations: MigrationOperation[];\n /** Output directory for generated migration files. */\n directory: string;\n}\n\n/**\n * Source generator for class-based migration files.\n */\nexport class MigrationGenerator {\n static readonly BRAND = 'tango.migrations.generator' as const;\n readonly __tangoBrand: typeof MigrationGenerator.BRAND = MigrationGenerator.BRAND;\n\n /**\n * Narrow an unknown value to `MigrationGenerator`.\n */\n static isMigrationGenerator(value: unknown): value is MigrationGenerator {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === MigrationGenerator.BRAND\n );\n }\n\n /**\n * Generate a migration file and write it to disk.\n * Returns the file path of the created migration.\n */\n async generate(options: GenerateMigrationOptions): Promise<string> {\n const { name, operations, directory } = options;\n\n if (operations.length === 0) {\n throw new Error('No operations to generate — models and database are in sync');\n }\n\n const timestamp = this.timestamp();\n const id = `${timestamp}_${name}`;\n const filename = `${id}.ts`;\n const filepath = join(directory, filename);\n\n const source = this.render(id, operations);\n\n await mkdir(directory, { recursive: true });\n await writeFile(filepath, source, 'utf-8');\n\n return filepath;\n }\n\n /**\n * Render migration operations to a TypeScript source string without writing to disk.\n */\n render(id: string, operations: MigrationOperation[]): string {\n const upOps = operations.map((operation) => this.renderOperation(operation));\n const downOps = operations.map((operation) => this.renderReverseOperation(operation));\n downOps.reverse();\n const className = this.renderClassName(id);\n\n const lines = [\n `import { Migration, op, trustedSql, type Builder } from '@danceroutine/tango-migrations';`,\n ``,\n `export default class ${className} extends Migration {`,\n ` id = '${id}';`,\n ``,\n ` up(m: Builder) {`,\n ` m.run(`,\n ...upOps.map((code: string, index: number) => {\n const comma = index < upOps.length - 1 ? ',' : '';\n return ` ${code}${comma}`;\n }),\n ` );`,\n ` }`,\n ``,\n ` down(m: Builder) {`,\n ` m.run(`,\n ...downOps.map((code: string, index: number) => {\n const comma = index < downOps.length - 1 ? ',' : '';\n return ` ${code}${comma}`;\n }),\n ` );`,\n ` }`,\n `}`,\n ``,\n ];\n\n return lines.join('\\n');\n }\n\n private renderClassName(id: string): string {\n const normalized = id.replace(/[^a-zA-Z0-9]+/g, '_').replace(/^(\\d)/, '_$1');\n return `Migration_${normalized}`;\n }\n\n private renderOperation(operation: MigrationOperation): string {\n switch (operation.kind) {\n case InternalOperationKind.TABLE_CREATE:\n return this.renderTableCreate(operation);\n case InternalOperationKind.TABLE_DROP:\n return this.renderTableDrop(operation);\n case InternalOperationKind.COLUMN_ADD:\n return this.renderColumnAdd(operation);\n case InternalOperationKind.COLUMN_DROP:\n return this.renderColumnDrop(operation);\n case InternalOperationKind.COLUMN_ALTER:\n return this.renderColumnAlter(operation);\n case InternalOperationKind.COLUMN_RENAME:\n return this.renderColumnRename(operation);\n case InternalOperationKind.INDEX_CREATE:\n return this.renderIndexCreate(operation);\n case InternalOperationKind.INDEX_DROP:\n return this.renderIndexDrop(operation);\n case InternalOperationKind.FK_CREATE:\n return this.renderForeignKeyCreate(operation);\n case InternalOperationKind.FK_DROP:\n return this.renderForeignKeyDrop(operation);\n case InternalOperationKind.FK_VALIDATE:\n return `op.foreignKeyValidate({ table: '${operation.table}', name: '${operation.name}' })`;\n case 'custom':\n return `/* custom operation '${operation.name}' cannot be code-generated */`;\n default:\n return `/* unsupported operation */`;\n }\n }\n\n private renderReverseOperation(operation: MigrationOperation): string {\n switch (operation.kind) {\n case InternalOperationKind.TABLE_CREATE:\n return `op.table('${operation.table}').drop()`;\n case InternalOperationKind.TABLE_DROP:\n return `/* manual reverse required: recreate dropped table '${operation.table}' */`;\n case InternalOperationKind.COLUMN_ADD:\n return `op.table('${operation.table}').dropColumn('${operation.column.name}')`;\n case InternalOperationKind.COLUMN_DROP:\n return `/* manual reverse required: restore dropped column '${operation.column}' */`;\n case InternalOperationKind.COLUMN_ALTER:\n return `/* manual reverse required: revert ALTER COLUMN '${operation.column}' on '${operation.table}' */`;\n case InternalOperationKind.COLUMN_RENAME:\n return `op.table('${operation.table}').renameColumn('${operation.to}', '${operation.from}')`;\n case InternalOperationKind.INDEX_CREATE:\n return `op.index.drop({ name: '${operation.name}', table: '${operation.table}' })`;\n case InternalOperationKind.INDEX_DROP:\n return `/* manual reverse required: recreate dropped index '${operation.name}' */`;\n case InternalOperationKind.FK_CREATE:\n return `op.foreignKeyDrop({ table: '${operation.table}', name: '${operation.name ?? `${operation.table}_${operation.columns.join('_')}_fkey`}' })`;\n case InternalOperationKind.FK_DROP:\n return `/* manual reverse required: recreate dropped FK '${operation.name}' */`;\n case InternalOperationKind.FK_VALIDATE:\n return `/* no reverse needed for FK_VALIDATE */`;\n case 'custom':\n return `/* manual reverse required: custom operation '${operation.name}' */`;\n default:\n return `/* unsupported reverse operation */`;\n }\n }\n\n private renderTableCreate(operation: TableCreate): string {\n const columnLines = operation.columns.map((col) => {\n const chain = this.renderColumnChain(col);\n return ` cols.add('${col.name}', (b) => b${chain});`;\n });\n\n return [`op.table('${operation.table}').create((cols) => {`, ...columnLines, ` })`].join('\\n');\n }\n\n private renderTableDrop(operation: TableDrop): string {\n if (operation.cascade) {\n return `op.table('${operation.table}').drop({ cascade: true })`;\n }\n return `op.table('${operation.table}').drop()`;\n }\n\n private renderColumnAdd(operation: ColumnAdd): string {\n const chain = this.renderColumnChain(operation.column);\n return `op.table('${operation.table}').addColumn('${operation.column.name}', (b) => b${chain})`;\n }\n\n private renderColumnDrop(operation: ColumnDrop): string {\n return `op.table('${operation.table}').dropColumn('${operation.column}')`;\n }\n\n private renderColumnAlter(operation: ColumnAlter): string {\n const parts: string[] = [];\n if (operation.to.type) {\n parts.push(`type: '${operation.to.type}'`);\n }\n if (operation.to.notNull !== undefined) {\n parts.push(`notNull: ${operation.to.notNull}`);\n }\n if (operation.to.default !== undefined) {\n if (operation.to.default === null) {\n parts.push(`default: null`);\n } else if (this.isNowDefault(operation.to.default)) {\n parts.push(`default: { now: true }`);\n } else if (isTrustedSqlFragment(operation.to.default)) {\n parts.push(`default: trustedSql(${JSON.stringify(operation.to.default.sql)})`);\n }\n }\n return `op.table('${operation.table}').alterColumn('${operation.column}', { ${parts.join(', ')} })`;\n }\n\n private renderColumnRename(operation: ColumnRename): string {\n return `op.table('${operation.table}').renameColumn('${operation.from}', '${operation.to}')`;\n }\n\n private renderIndexCreate(operation: IndexCreate): string {\n const parts: string[] = [\n `name: '${operation.name}'`,\n `table: '${operation.table}'`,\n `on: [${operation.on.map((c) => `'${c}'`).join(', ')}]`,\n ];\n if (operation.unique) {\n parts.push(`unique: true`);\n }\n if (operation.where) {\n parts.push(`where: trustedSql(${JSON.stringify(operation.where.sql)})`);\n }\n if (operation.concurrently) {\n parts.push(`concurrently: true`);\n }\n return `op.index.create({ ${parts.join(', ')} })`;\n }\n\n private renderIndexDrop(operation: IndexDrop): string {\n return `op.index.drop({ name: '${operation.name}', table: '${operation.table}' })`;\n }\n\n private renderForeignKeyCreate(operation: ForeignKeyCreate): string {\n const parts: string[] = [\n `table: '${operation.table}'`,\n `columns: [${operation.columns.map((c) => `'${c}'`).join(', ')}]`,\n `references: { table: '${operation.refTable}', columns: [${operation.refColumns.map((c) => `'${c}'`).join(', ')}] }`,\n ];\n if (operation.name) {\n parts.push(`name: '${operation.name}'`);\n }\n if (operation.onDelete) {\n parts.push(`onDelete: '${operation.onDelete}'`);\n }\n if (operation.onUpdate) {\n parts.push(`onUpdate: '${operation.onUpdate}'`);\n }\n if (operation.notValid) {\n parts.push(`notValid: true`);\n }\n return `op.foreignKey({ ${parts.join(', ')} })`;\n }\n\n private renderForeignKeyDrop(operation: ForeignKeyDrop): string {\n return `op.foreignKeyDrop({ table: '${operation.table}', name: '${operation.name}' })`;\n }\n\n private renderColumnChain(col: ColumnSpec): string {\n const parts: string[] = [];\n\n if (col.type) {\n parts.push(`.${col.type}()`);\n }\n if (col.notNull) {\n parts.push(`.notNull()`);\n }\n if (col.default !== undefined && col.default !== null) {\n if (this.isNowDefault(col.default)) {\n parts.push(`.defaultNow()`);\n } else if (isTrustedSqlFragment(col.default)) {\n parts.push(`.default(trustedSql(${JSON.stringify(col.default.sql)}))`);\n }\n } else if (col.default === null) {\n parts.push(`.default(null)`);\n }\n if (col.primaryKey) {\n parts.push(`.primaryKey()`);\n }\n if (col.unique) {\n parts.push(`.unique()`);\n }\n if (col.references) {\n const refParts: string[] = [];\n if (col.references.onDelete) {\n refParts.push(`onDelete: '${col.references.onDelete}'`);\n }\n if (col.references.onUpdate) {\n refParts.push(`onUpdate: '${col.references.onUpdate}'`);\n }\n const opts = refParts.length > 0 ? `, { ${refParts.join(', ')} }` : '';\n parts.push(`.references('${col.references.table}', '${col.references.column}'${opts})`);\n }\n\n return parts.join('');\n }\n\n private timestamp(): string {\n const now = new Date();\n const year = now.getFullYear();\n const month = String(now.getMonth() + 1).padStart(2, '0');\n const day = String(now.getDate()).padStart(2, '0');\n const hours = String(now.getHours()).padStart(2, '0');\n const minutes = String(now.getMinutes()).padStart(2, '0');\n const seconds = String(now.getSeconds()).padStart(2, '0');\n return `${year}${month}${day}${hours}${minutes}${seconds}`;\n }\n\n private isNowDefault(value: ColumnSpec['default']): value is { now: true } {\n return typeof value === 'object' && value !== null && 'now' in value && value.now === true;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,21 +1,58 @@
|
|
|
1
|
-
import { CollectingBuilder } from "./CollectingBuilder
|
|
2
|
-
import { Migration } from "./Migration-
|
|
3
|
-
import { InternalDialect, createDefaultCompilerStrategy } from "./CompilerStrategy-
|
|
4
|
-
import { resolve } from "node:path";
|
|
1
|
+
import { CollectingBuilder } from "./CollectingBuilder--4fqDQdE.js";
|
|
2
|
+
import { Migration } from "./Migration-DYQ0hUG7.js";
|
|
3
|
+
import { InternalDialect, createDefaultCompilerStrategy } from "./CompilerStrategy-yKw-Egxv.js";
|
|
5
4
|
import { readdir } from "node:fs/promises";
|
|
5
|
+
import { extname, resolve, resolve as resolve$1 } from "node:path";
|
|
6
|
+
import { isError } from "@danceroutine/tango-core";
|
|
7
|
+
import { pathToFileURL } from "node:url";
|
|
8
|
+
import { createJiti } from "jiti";
|
|
6
9
|
|
|
10
|
+
//#region src/runtime/loadModule.ts
|
|
11
|
+
const TS_EXTENSIONS = new Set([
|
|
12
|
+
".ts",
|
|
13
|
+
".tsx",
|
|
14
|
+
".mts",
|
|
15
|
+
".cts"
|
|
16
|
+
]);
|
|
17
|
+
function toAbsolutePath(modulePath, projectRoot) {
|
|
18
|
+
return resolve$1(projectRoot, modulePath);
|
|
19
|
+
}
|
|
20
|
+
function isTypeScriptModule(modulePath) {
|
|
21
|
+
return TS_EXTENSIONS.has(extname(modulePath).toLowerCase());
|
|
22
|
+
}
|
|
23
|
+
async function loadModule(modulePath, options) {
|
|
24
|
+
const projectRoot = options?.projectRoot ?? process.cwd();
|
|
25
|
+
const absolutePath = toAbsolutePath(modulePath, projectRoot);
|
|
26
|
+
if (isTypeScriptModule(absolutePath)) {
|
|
27
|
+
const jiti = createJiti(resolve$1(projectRoot, "tango.config.ts"), {
|
|
28
|
+
interopDefault: true,
|
|
29
|
+
moduleCache: true
|
|
30
|
+
});
|
|
31
|
+
return await jiti.import(absolutePath);
|
|
32
|
+
}
|
|
33
|
+
return await import(pathToFileURL(absolutePath).href);
|
|
34
|
+
}
|
|
35
|
+
async function loadDefaultExport(modulePath, options) {
|
|
36
|
+
const loaded = await loadModule(modulePath, options);
|
|
37
|
+
return loaded.default ?? loaded;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
//#endregion
|
|
7
41
|
//#region src/runner/MigrationRunner.ts
|
|
8
42
|
const JOURNAL = "_tango_migrations";
|
|
9
43
|
var MigrationRunner = class MigrationRunner {
|
|
10
44
|
static BRAND = "tango.migrations.runner";
|
|
11
|
-
compilerStrategy;
|
|
12
45
|
__tangoBrand = MigrationRunner.BRAND;
|
|
46
|
+
compilerStrategy;
|
|
13
47
|
constructor(client, dialect, migrationsDir = "migrations", compilerStrategy) {
|
|
14
48
|
this.client = client;
|
|
15
49
|
this.dialect = dialect;
|
|
16
50
|
this.migrationsDir = migrationsDir;
|
|
17
51
|
this.compilerStrategy = compilerStrategy ?? createDefaultCompilerStrategy();
|
|
18
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Narrow an unknown value to `MigrationRunner`.
|
|
55
|
+
*/
|
|
19
56
|
static isMigrationRunner(value) {
|
|
20
57
|
return typeof value === "object" && value !== null && value.__tangoBrand === MigrationRunner.BRAND;
|
|
21
58
|
}
|
|
@@ -86,8 +123,14 @@ var MigrationRunner = class MigrationRunner {
|
|
|
86
123
|
const files = (await readdir(this.migrationsDir)).filter((f) => f.endsWith(".ts") || f.endsWith(".js")).sort();
|
|
87
124
|
const migrations = [];
|
|
88
125
|
for (const file of files) {
|
|
89
|
-
const
|
|
90
|
-
|
|
126
|
+
const absolutePath = resolve(this.migrationsDir, file);
|
|
127
|
+
let loaded;
|
|
128
|
+
try {
|
|
129
|
+
loaded = await loadDefaultExport(absolutePath, { projectRoot: process.cwd() });
|
|
130
|
+
} catch (error) {
|
|
131
|
+
const reason = isError(error) ? error.message : String(error);
|
|
132
|
+
throw new Error(`Failed to load migration module '${file}': ${reason}`, { cause: error });
|
|
133
|
+
}
|
|
91
134
|
if (Migration.isMigration(loaded)) {
|
|
92
135
|
migrations.push(loaded);
|
|
93
136
|
continue;
|
|
@@ -140,5 +183,5 @@ var MigrationRunner = class MigrationRunner {
|
|
|
140
183
|
};
|
|
141
184
|
|
|
142
185
|
//#endregion
|
|
143
|
-
export { MigrationRunner };
|
|
144
|
-
//# sourceMappingURL=MigrationRunner-
|
|
186
|
+
export { MigrationRunner, loadModule };
|
|
187
|
+
//# sourceMappingURL=MigrationRunner-D1ZfbbS-.js.map
|