@danceroutine/tango-migrations 1.11.3 → 1.11.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/{CompilerStrategy-vcZKg8qf.js → CompilerStrategy-Dc5_3-nw.js} +2 -2
  2. package/dist/{CompilerStrategy-vcZKg8qf.js.map → CompilerStrategy-Dc5_3-nw.js.map} +1 -1
  3. package/dist/{InternalOperationKind-M4a4H9OZ.js → InternalOperationKind-BJ7n-pUH.js} +14 -2
  4. package/dist/InternalOperationKind-BJ7n-pUH.js.map +1 -0
  5. package/dist/{IntrospectorStrategy-BijuyIaN.js → IntrospectorStrategy-BCTs_4wl.js} +2 -2
  6. package/dist/{IntrospectorStrategy-BijuyIaN.js.map → IntrospectorStrategy-BCTs_4wl.js.map} +1 -1
  7. package/dist/{MigrationGenerator-BmmerPXJ.js → MigrationGenerator-D-sSbsFG.js} +65 -43
  8. package/dist/MigrationGenerator-D-sSbsFG.js.map +1 -0
  9. package/dist/{MigrationRunner-B5AJel12.js → MigrationRunner-vG1lVlkz.js} +2 -2
  10. package/dist/{MigrationRunner-B5AJel12.js.map → MigrationRunner-vG1lVlkz.js.map} +1 -1
  11. package/dist/{SqliteCompilerFactory-CXlPAclY.js → SqliteCompilerFactory-C_56xoQM.js} +2 -3
  12. package/dist/{SqliteCompilerFactory-CXlPAclY.js.map → SqliteCompilerFactory-C_56xoQM.js.map} +1 -1
  13. package/dist/builder/index.js +1 -1
  14. package/dist/{builder-BSepa_PF.js → builder-Bhm_to6b.js} +2 -3
  15. package/dist/{builder-BSepa_PF.js.map → builder-Bhm_to6b.js.map} +1 -1
  16. package/dist/{cli-e8I1-dab.js → cli-B3MNpH29.js} +6 -6
  17. package/dist/{cli-e8I1-dab.js.map → cli-B3MNpH29.js.map} +1 -1
  18. package/dist/cli.js +1 -1
  19. package/dist/commands/index.js +1 -1
  20. package/dist/compilers/index.js +2 -2
  21. package/dist/{compilers-BW-WALoD.js → compilers-BlLQwwmu.js} +2 -2
  22. package/dist/{compilers-BW-WALoD.js.map → compilers-BlLQwwmu.js.map} +1 -1
  23. package/dist/diff/index.js +1 -1
  24. package/dist/{diff-7Xw8k4vp.js → diff-BsQr9Sl8.js} +2 -2
  25. package/dist/{diff-7Xw8k4vp.js.map → diff-BsQr9Sl8.js.map} +1 -1
  26. package/dist/generator/index.d.ts +1 -1
  27. package/dist/generator/index.js +1 -1
  28. package/dist/{index-B8VoE0M4.d.ts → index-Dcuh1zcQ.d.ts} +5 -1
  29. package/dist/index.d.ts +1 -1
  30. package/dist/index.js +9 -9
  31. package/dist/runner/index.js +1 -1
  32. package/dist/strategies/index.js +2 -2
  33. package/package.json +7 -7
  34. package/dist/InternalColumnType-Dzs9T6a6.js +0 -15
  35. package/dist/InternalColumnType-Dzs9T6a6.js.map +0 -1
  36. package/dist/InternalOperationKind-M4a4H9OZ.js.map +0 -1
  37. package/dist/MigrationGenerator-BmmerPXJ.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { r as PostgresCompilerFactory, t as SqliteCompilerFactory } from "./SqliteCompilerFactory-CXlPAclY.js";
1
+ import { r as PostgresCompilerFactory, t as SqliteCompilerFactory } from "./SqliteCompilerFactory-C_56xoQM.js";
2
2
  //#region src/domain/internal/InternalDialect.ts
3
3
  const InternalDialect = {
4
4
  POSTGRES: "postgres",
@@ -74,4 +74,4 @@ function createDefaultCompilerStrategy() {
74
74
  //#endregion
75
75
  export { createDefaultCompilerStrategy as n, InternalDialect as r, CompilerStrategy as t };
76
76
 
77
- //# sourceMappingURL=CompilerStrategy-vcZKg8qf.js.map
77
+ //# sourceMappingURL=CompilerStrategy-Dc5_3-nw.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CompilerStrategy-vcZKg8qf.js","names":[],"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 * Prepare a batch of migration operations for a target dialect.\n */\n prepareOperations(dialect: Dialect, operations: MigrationOperation[]): MigrationOperation[] {\n const compiler = this.getCompiler(dialect);\n return compiler.prepareOperations ? compiler.prepareOperations(operations) : operations;\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":";;AAAA,MAAa,kBAAkB;CAC3B,UAAU;CACV,QAAQ;AACZ;;;;;;ACWA,IAAa,mBAAb,MAAa,iBAAiB;CAMG;CAL7B,OAAgB,QAAQ;CACxB,eAAuD,iBAAiB;CACxE,gCAAiC,IAAI,IAA0B;CAC/D,iCAAkC,IAAI,IAAuE;CAE7G,YAAY,WAAqD;EAApC,KAAA,YAAA;CAAqC;;;;CAKlE,OAAO,mBAAmB,OAA2C;EACjE,OACI,OAAO,UAAU,YACjB,UAAU,QACT,MAAqC,iBAAiB,iBAAiB;CAEhF;;;;CAKA,kBAAkB,SAAkB,YAAwD;EACxF,MAAM,WAAW,KAAK,YAAY,OAAO;EACzC,OAAO,SAAS,oBAAoB,SAAS,kBAAkB,UAAU,IAAI;CACjF;;;;CAKA,QAAQ,SAAkB,WAAsC;EAC5D,IAAI,UAAU,SAAS,UAAU;GAC7B,MAAM,UAAU,KAAK,eAAe,IAAI,UAAU,IAAI;GACtD,IAAI,CAAC,SACD,MAAM,IAAI,MAAM,oCAAoC,UAAU,MAAM;GAExE,OAAO,QAAQ,SAAS,SAAS;EACrC;EAEA,OADiB,KAAK,YAAY,OACpB,EAAE,QAAQ,SAAS;CACrC;;;;CAKA,sBACI,MACA,SACI;EACJ,KAAK,eAAe,IAAI,MAAM,OAAoE;EAClG,OAAO;CACX;;;;CAKA,YAAY,SAA+B;EACvC,MAAM,SAAS,KAAK,cAAc,IAAI,OAAO;EAC7C,IAAI,QACA,OAAO;EAGX,MAAM,UAAU,KAAK,UAAU;EAC/B,IAAI,CAAC,SACD,MAAM,IAAI,MAAM,mDAAmD,OAAO,OAAO,GAAG;EAExF,MAAM,WAAW,QAAQ,OAAO;EAChC,KAAK,cAAc,IAAI,SAAS,QAAQ;EACxC,OAAO;CACX;AACJ;;;;AAKA,SAAgB,gCAAkD;CAC9D,OAAO,IAAI,iBAAiB;GACvB,gBAAgB,WAAW,IAAI,wBAAwB;GACvD,gBAAgB,SAAS,IAAI,sBAAsB;CACxD,CAAC;AACL"}
1
+ {"version":3,"file":"CompilerStrategy-Dc5_3-nw.js","names":[],"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 * Prepare a batch of migration operations for a target dialect.\n */\n prepareOperations(dialect: Dialect, operations: MigrationOperation[]): MigrationOperation[] {\n const compiler = this.getCompiler(dialect);\n return compiler.prepareOperations ? compiler.prepareOperations(operations) : operations;\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":";;AAAA,MAAa,kBAAkB;CAC3B,UAAU;CACV,QAAQ;AACZ;;;;;;ACWA,IAAa,mBAAb,MAAa,iBAAiB;CAMG;CAL7B,OAAgB,QAAQ;CACxB,eAAuD,iBAAiB;CACxE,gCAAiC,IAAI,IAA0B;CAC/D,iCAAkC,IAAI,IAAuE;CAE7G,YAAY,WAAqD;EAApC,KAAA,YAAA;CAAqC;;;;CAKlE,OAAO,mBAAmB,OAA2C;EACjE,OACI,OAAO,UAAU,YACjB,UAAU,QACT,MAAqC,iBAAiB,iBAAiB;CAEhF;;;;CAKA,kBAAkB,SAAkB,YAAwD;EACxF,MAAM,WAAW,KAAK,YAAY,OAAO;EACzC,OAAO,SAAS,oBAAoB,SAAS,kBAAkB,UAAU,IAAI;CACjF;;;;CAKA,QAAQ,SAAkB,WAAsC;EAC5D,IAAI,UAAU,SAAS,UAAU;GAC7B,MAAM,UAAU,KAAK,eAAe,IAAI,UAAU,IAAI;GACtD,IAAI,CAAC,SACD,MAAM,IAAI,MAAM,oCAAoC,UAAU,MAAM;GAExE,OAAO,QAAQ,SAAS,SAAS;EACrC;EAEA,OADiB,KAAK,YAAY,OACpB,EAAE,QAAQ,SAAS;CACrC;;;;CAKA,sBACI,MACA,SACI;EACJ,KAAK,eAAe,IAAI,MAAM,OAAoE;EAClG,OAAO;CACX;;;;CAKA,YAAY,SAA+B;EACvC,MAAM,SAAS,KAAK,cAAc,IAAI,OAAO;EAC7C,IAAI,QACA,OAAO;EAGX,MAAM,UAAU,KAAK,UAAU;EAC/B,IAAI,CAAC,SACD,MAAM,IAAI,MAAM,mDAAmD,OAAO,OAAO,GAAG;EAExF,MAAM,WAAW,QAAQ,OAAO;EAChC,KAAK,cAAc,IAAI,SAAS,QAAQ;EACxC,OAAO;CACX;AACJ;;;;AAKA,SAAgB,gCAAkD;CAC9D,OAAO,IAAI,iBAAiB;GACvB,gBAAgB,WAAW,IAAI,wBAAwB;GACvD,gBAAgB,SAAS,IAAI,sBAAsB;CACxD,CAAC;AACL"}
@@ -1,3 +1,15 @@
1
+ //#region src/domain/internal/InternalColumnType.ts
2
+ const InternalColumnType = {
3
+ SERIAL: "serial",
4
+ INT: "int",
5
+ BIGINT: "bigint",
6
+ TEXT: "text",
7
+ BOOL: "bool",
8
+ TIMESTAMPTZ: "timestamptz",
9
+ JSONB: "jsonb",
10
+ UUID: "uuid"
11
+ };
12
+ //#endregion
1
13
  //#region src/domain/internal/InternalOperationKind.ts
2
14
  const InternalOperationKind = {
3
15
  TABLE_CREATE: "table.create",
@@ -13,6 +25,6 @@ const InternalOperationKind = {
13
25
  FK_DROP: "fk.drop"
14
26
  };
15
27
  //#endregion
16
- export { InternalOperationKind as t };
28
+ export { InternalColumnType as n, InternalOperationKind as t };
17
29
 
18
- //# sourceMappingURL=InternalOperationKind-M4a4H9OZ.js.map
30
+ //# sourceMappingURL=InternalOperationKind-BJ7n-pUH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InternalOperationKind-BJ7n-pUH.js","names":[],"sources":["../src/domain/internal/InternalColumnType.ts","../src/domain/internal/InternalOperationKind.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","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":";AAAA,MAAa,qBAAqB;CAC9B,QAAQ;CACR,KAAK;CACL,QAAQ;CACR,MAAM;CACN,MAAM;CACN,aAAa;CACb,OAAO;CACP,MAAM;AACV;;;ACTA,MAAa,wBAAwB;CACjC,cAAc;CACd,YAAY;CACZ,YAAY;CACZ,aAAa;CACb,cAAc;CACd,eAAe;CACf,cAAc;CACd,YAAY;CACZ,WAAW;CACX,aAAa;CACb,SAAS;AACb"}
@@ -1,4 +1,4 @@
1
- import { r as InternalDialect } from "./CompilerStrategy-vcZKg8qf.js";
1
+ import { r as InternalDialect } from "./CompilerStrategy-Dc5_3-nw.js";
2
2
  import { n as PostgresIntrospector, t as SqliteIntrospector } from "./SqliteIntrospector-CfItmGgA.js";
3
3
  //#region src/strategies/IntrospectorStrategy.ts
4
4
  /**
@@ -49,4 +49,4 @@ function createDefaultIntrospectorStrategy() {
49
49
  //#endregion
50
50
  export { createDefaultIntrospectorStrategy as n, IntrospectorStrategy as t };
51
51
 
52
- //# sourceMappingURL=IntrospectorStrategy-BijuyIaN.js.map
52
+ //# sourceMappingURL=IntrospectorStrategy-BCTs_4wl.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"IntrospectorStrategy-BijuyIaN.js","names":[],"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":";;;;;;AAgBA,IAAa,uBAAb,MAAa,qBAAqB;CAKD;CAJ7B,OAAgB,QAAQ;CACxB,eAA2D,qBAAqB;CAChF,oCAAqC,IAAI,IAAmC;CAE5E,YAAY,WAAyD;EAAxC,KAAA,YAAA;CAAyC;;;;CAKtE,OAAO,uBAAuB,OAA+C;EACzE,OACI,OAAO,UAAU,YACjB,UAAU,QACT,MAAqC,iBAAiB,qBAAqB;CAEpF;;;;CAKA,WAAW,SAAkB,QAAqC;EAE9D,OADqB,KAAK,gBAAgB,OACxB,EAAE,WAAW,MAAM;CACzC;;;;CAKA,gBAAgB,SAAwC;EACpD,MAAM,SAAS,KAAK,kBAAkB,IAAI,OAAO;EACjD,IAAI,QACA,OAAO;EAGX,MAAM,UAAU,KAAK,UAAU;EAC/B,IAAI,CAAC,SACD,MAAM,IAAI,MAAM,4DAA4D,OAAO,OAAO,GAAG;EAGjG,MAAM,eAAe,QAAQ,OAAO;EACpC,KAAK,kBAAkB,IAAI,SAAS,YAAY;EAChD,OAAO;CACX;AACJ;;;;AAKA,SAAgB,oCAA0D;CACtE,OAAO,IAAI,qBAAqB;GAC3B,gBAAgB,WAAW,EAAE,cAAc,IAAI,qBAAqB,EAAE;GACtE,gBAAgB,SAAS,EAAE,cAAc,IAAI,mBAAmB,EAAE;CACvE,CAAC;AACL"}
1
+ {"version":3,"file":"IntrospectorStrategy-BCTs_4wl.js","names":[],"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":";;;;;;AAgBA,IAAa,uBAAb,MAAa,qBAAqB;CAKD;CAJ7B,OAAgB,QAAQ;CACxB,eAA2D,qBAAqB;CAChF,oCAAqC,IAAI,IAAmC;CAE5E,YAAY,WAAyD;EAAxC,KAAA,YAAA;CAAyC;;;;CAKtE,OAAO,uBAAuB,OAA+C;EACzE,OACI,OAAO,UAAU,YACjB,UAAU,QACT,MAAqC,iBAAiB,qBAAqB;CAEpF;;;;CAKA,WAAW,SAAkB,QAAqC;EAE9D,OADqB,KAAK,gBAAgB,OACxB,EAAE,WAAW,MAAM;CACzC;;;;CAKA,gBAAgB,SAAwC;EACpD,MAAM,SAAS,KAAK,kBAAkB,IAAI,OAAO;EACjD,IAAI,QACA,OAAO;EAGX,MAAM,UAAU,KAAK,UAAU;EAC/B,IAAI,CAAC,SACD,MAAM,IAAI,MAAM,4DAA4D,OAAO,OAAO,GAAG;EAGjG,MAAM,eAAe,QAAQ,OAAO;EACpC,KAAK,kBAAkB,IAAI,SAAS,YAAY;EAChD,OAAO;CACX;AACJ;;;;AAKA,SAAgB,oCAA0D;CACtE,OAAO,IAAI,qBAAqB;GAC3B,gBAAgB,WAAW,EAAE,cAAc,IAAI,qBAAqB,EAAE;GACtE,gBAAgB,SAAS,EAAE,cAAc,IAAI,mBAAmB,EAAE;CACvE,CAAC;AACL"}
@@ -1,4 +1,4 @@
1
- import { t as InternalOperationKind } from "./InternalOperationKind-M4a4H9OZ.js";
1
+ import { n as InternalColumnType, t as InternalOperationKind } from "./InternalOperationKind-BJ7n-pUH.js";
2
2
  import { isTrustedSqlFragment } from "@danceroutine/tango-core";
3
3
  import { mkdir, writeFile } from "node:fs/promises";
4
4
  import { join } from "node:path";
@@ -40,7 +40,7 @@ var MigrationGenerator = class MigrationGenerator {
40
40
  `import { Migration, op, trustedSql, type Builder } from '@danceroutine/tango-migrations';`,
41
41
  ``,
42
42
  `export default class ${this.renderClassName(id)} extends Migration {`,
43
- ` id = '${id}';`,
43
+ ` id = ${this.renderStringLiteral(id)};`,
44
44
  ``,
45
45
  ` up(m: Builder) {`,
46
46
  ` m.run(`,
@@ -76,112 +76,134 @@ var MigrationGenerator = class MigrationGenerator {
76
76
  case InternalOperationKind.INDEX_DROP: return this.renderIndexDrop(operation);
77
77
  case InternalOperationKind.FK_CREATE: return this.renderForeignKeyCreate(operation);
78
78
  case InternalOperationKind.FK_DROP: return this.renderForeignKeyDrop(operation);
79
- case InternalOperationKind.FK_VALIDATE: return `op.foreignKeyValidate({ table: '${operation.table}', name: '${operation.name}' })`;
80
- case "custom": return `/* custom operation '${operation.name}' cannot be code-generated */`;
79
+ case InternalOperationKind.FK_VALIDATE: return `op.foreignKeyValidate({ table: ${this.renderStringLiteral(operation.table)}, name: ${this.renderStringLiteral(operation.name)} })`;
80
+ case "custom": return `/* custom operation ${this.renderCommentStringLiteral(operation.name)} cannot be code-generated */`;
81
81
  default: return `/* unsupported operation */`;
82
82
  }
83
83
  }
84
84
  renderReverseOperation(operation) {
85
85
  switch (operation.kind) {
86
- case InternalOperationKind.TABLE_CREATE: return `op.table('${operation.table}').drop()`;
87
- case InternalOperationKind.TABLE_DROP: return `/* manual reverse required: recreate dropped table '${operation.table}' */`;
88
- case InternalOperationKind.COLUMN_ADD: return `op.table('${operation.table}').dropColumn('${operation.column.name}')`;
89
- case InternalOperationKind.COLUMN_DROP: return `/* manual reverse required: restore dropped column '${operation.column}' */`;
90
- case InternalOperationKind.COLUMN_ALTER: return `/* manual reverse required: revert ALTER COLUMN '${operation.column}' on '${operation.table}' */`;
91
- case InternalOperationKind.COLUMN_RENAME: return `op.table('${operation.table}').renameColumn('${operation.to}', '${operation.from}')`;
92
- case InternalOperationKind.INDEX_CREATE: return `op.index.drop({ name: '${operation.name}', table: '${operation.table}' })`;
93
- case InternalOperationKind.INDEX_DROP: return `/* manual reverse required: recreate dropped index '${operation.name}' */`;
94
- case InternalOperationKind.FK_CREATE: return `op.foreignKeyDrop({ table: '${operation.table}', name: '${operation.name ?? `${operation.table}_${operation.columns.join("_")}_fkey`}' })`;
95
- case InternalOperationKind.FK_DROP: return `/* manual reverse required: recreate dropped FK '${operation.name}' */`;
86
+ case InternalOperationKind.TABLE_CREATE: return `op.table(${this.renderStringLiteral(operation.table)}).drop()`;
87
+ case InternalOperationKind.TABLE_DROP: return `/* manual reverse required: recreate dropped table ${this.renderCommentStringLiteral(operation.table)} */`;
88
+ case InternalOperationKind.COLUMN_ADD: return `op.table(${this.renderStringLiteral(operation.table)}).dropColumn(${this.renderStringLiteral(operation.column.name)})`;
89
+ case InternalOperationKind.COLUMN_DROP: return `/* manual reverse required: restore dropped column ${this.renderCommentStringLiteral(operation.column)} */`;
90
+ case InternalOperationKind.COLUMN_ALTER: return `/* manual reverse required: revert ALTER COLUMN ${this.renderCommentStringLiteral(operation.column)} on ${this.renderCommentStringLiteral(operation.table)} */`;
91
+ case InternalOperationKind.COLUMN_RENAME: return `op.table(${this.renderStringLiteral(operation.table)}).renameColumn(${this.renderStringLiteral(operation.to)}, ${this.renderStringLiteral(operation.from)})`;
92
+ case InternalOperationKind.INDEX_CREATE: return `op.index.drop({ name: ${this.renderStringLiteral(operation.name)}, table: ${this.renderStringLiteral(operation.table)} })`;
93
+ case InternalOperationKind.INDEX_DROP: return `/* manual reverse required: recreate dropped index ${this.renderCommentStringLiteral(operation.name)} */`;
94
+ case InternalOperationKind.FK_CREATE: return `op.foreignKeyDrop({ table: ${this.renderStringLiteral(operation.table)}, name: ${this.renderStringLiteral(operation.name ?? `${operation.table}_${operation.columns.join("_")}_fkey`)} })`;
95
+ case InternalOperationKind.FK_DROP: return `/* manual reverse required: recreate dropped FK ${this.renderCommentStringLiteral(operation.name)} */`;
96
96
  case InternalOperationKind.FK_VALIDATE: return `/* no reverse needed for FK_VALIDATE */`;
97
- case "custom": return `/* manual reverse required: custom operation '${operation.name}' */`;
97
+ case "custom": return `/* manual reverse required: custom operation ${this.renderCommentStringLiteral(operation.name)} */`;
98
98
  default: return `/* unsupported reverse operation */`;
99
99
  }
100
100
  }
101
101
  renderTableCreate(operation) {
102
102
  const columnLines = operation.columns.map((col) => {
103
103
  const chain = this.renderColumnChain(col);
104
- return ` cols.add('${col.name}', (b) => b${chain});`;
104
+ return ` cols.add(${this.renderStringLiteral(col.name)}, (b) => b${chain});`;
105
105
  });
106
106
  return [
107
- `op.table('${operation.table}').create((cols) => {`,
107
+ `op.table(${this.renderStringLiteral(operation.table)}).create((cols) => {`,
108
108
  ...columnLines,
109
109
  ` })`
110
110
  ].join("\n");
111
111
  }
112
112
  renderTableDrop(operation) {
113
- if (operation.cascade) return `op.table('${operation.table}').drop({ cascade: true })`;
114
- return `op.table('${operation.table}').drop()`;
113
+ if (operation.cascade) return `op.table(${this.renderStringLiteral(operation.table)}).drop({ cascade: true })`;
114
+ return `op.table(${this.renderStringLiteral(operation.table)}).drop()`;
115
115
  }
116
116
  renderColumnAdd(operation) {
117
117
  const chain = this.renderColumnChain(operation.column);
118
- return `op.table('${operation.table}').addColumn('${operation.column.name}', (b) => b${chain})`;
118
+ return `op.table(${this.renderStringLiteral(operation.table)}).addColumn(${this.renderStringLiteral(operation.column.name)}, (b) => b${chain})`;
119
119
  }
120
120
  renderColumnDrop(operation) {
121
- return `op.table('${operation.table}').dropColumn('${operation.column}')`;
121
+ return `op.table(${this.renderStringLiteral(operation.table)}).dropColumn(${this.renderStringLiteral(operation.column)})`;
122
122
  }
123
123
  renderColumnAlter(operation) {
124
124
  const parts = [];
125
- if (operation.to.type) parts.push(`type: '${operation.to.type}'`);
125
+ if (operation.to.type) parts.push(`type: ${this.renderStringLiteral(operation.to.type)}`);
126
126
  if (operation.to.notNull !== void 0) parts.push(`notNull: ${operation.to.notNull}`);
127
127
  if (operation.to.default !== void 0) {
128
128
  if (operation.to.default === null) parts.push(`default: null`);
129
129
  else if (this.isNowDefault(operation.to.default)) parts.push(`default: { now: true }`);
130
- else if (isTrustedSqlFragment(operation.to.default)) parts.push(`default: trustedSql(${JSON.stringify(operation.to.default.sql)})`);
130
+ else if (isTrustedSqlFragment(operation.to.default)) parts.push(`default: trustedSql(${this.renderStringLiteral(operation.to.default.sql)})`);
131
131
  }
132
- return `op.table('${operation.table}').alterColumn('${operation.column}', { ${parts.join(", ")} })`;
132
+ return `op.table(${this.renderStringLiteral(operation.table)}).alterColumn(${this.renderStringLiteral(operation.column)}, { ${parts.join(", ")} })`;
133
133
  }
134
134
  renderColumnRename(operation) {
135
- return `op.table('${operation.table}').renameColumn('${operation.from}', '${operation.to}')`;
135
+ return `op.table(${this.renderStringLiteral(operation.table)}).renameColumn(${this.renderStringLiteral(operation.from)}, ${this.renderStringLiteral(operation.to)})`;
136
136
  }
137
137
  renderIndexCreate(operation) {
138
138
  const parts = [
139
- `name: '${operation.name}'`,
140
- `table: '${operation.table}'`,
141
- `on: [${operation.on.map((c) => `'${c}'`).join(", ")}]`
139
+ `name: ${this.renderStringLiteral(operation.name)}`,
140
+ `table: ${this.renderStringLiteral(operation.table)}`,
141
+ `on: ${this.renderStringArrayLiteral(operation.on)}`
142
142
  ];
143
143
  if (operation.unique) parts.push(`unique: true`);
144
- if (operation.where) parts.push(`where: trustedSql(${JSON.stringify(operation.where.sql)})`);
144
+ if (operation.where) parts.push(`where: trustedSql(${this.renderStringLiteral(operation.where.sql)})`);
145
145
  if (operation.concurrently) parts.push(`concurrently: true`);
146
146
  return `op.index.create({ ${parts.join(", ")} })`;
147
147
  }
148
148
  renderIndexDrop(operation) {
149
- return `op.index.drop({ name: '${operation.name}', table: '${operation.table}' })`;
149
+ return `op.index.drop({ name: ${this.renderStringLiteral(operation.name)}, table: ${this.renderStringLiteral(operation.table)} })`;
150
150
  }
151
151
  renderForeignKeyCreate(operation) {
152
152
  const parts = [
153
- `table: '${operation.table}'`,
154
- `columns: [${operation.columns.map((c) => `'${c}'`).join(", ")}]`,
155
- `references: { table: '${operation.refTable}', columns: [${operation.refColumns.map((c) => `'${c}'`).join(", ")}] }`
153
+ `table: ${this.renderStringLiteral(operation.table)}`,
154
+ `columns: ${this.renderStringArrayLiteral(operation.columns)}`,
155
+ `references: { table: ${this.renderStringLiteral(operation.refTable)}, columns: ${this.renderStringArrayLiteral(operation.refColumns)} }`
156
156
  ];
157
- if (operation.name) parts.push(`name: '${operation.name}'`);
158
- if (operation.onDelete) parts.push(`onDelete: '${operation.onDelete}'`);
159
- if (operation.onUpdate) parts.push(`onUpdate: '${operation.onUpdate}'`);
157
+ if (operation.name) parts.push(`name: ${this.renderStringLiteral(operation.name)}`);
158
+ if (operation.onDelete) parts.push(`onDelete: ${this.renderStringLiteral(operation.onDelete)}`);
159
+ if (operation.onUpdate) parts.push(`onUpdate: ${this.renderStringLiteral(operation.onUpdate)}`);
160
160
  if (operation.notValid) parts.push(`notValid: true`);
161
161
  return `op.foreignKey({ ${parts.join(", ")} })`;
162
162
  }
163
163
  renderForeignKeyDrop(operation) {
164
- return `op.foreignKeyDrop({ table: '${operation.table}', name: '${operation.name}' })`;
164
+ return `op.foreignKeyDrop({ table: ${this.renderStringLiteral(operation.table)}, name: ${this.renderStringLiteral(operation.name)} })`;
165
165
  }
166
166
  renderColumnChain(col) {
167
167
  const parts = [];
168
- if (col.type) parts.push(`.${col.type}()`);
168
+ if (col.type) parts.push(`.${this.renderColumnTypeMethod(col.type)}()`);
169
169
  if (col.notNull) parts.push(`.notNull()`);
170
170
  if (col.default !== void 0 && col.default !== null) {
171
171
  if (this.isNowDefault(col.default)) parts.push(`.defaultNow()`);
172
- else if (isTrustedSqlFragment(col.default)) parts.push(`.default(trustedSql(${JSON.stringify(col.default.sql)}))`);
172
+ else if (isTrustedSqlFragment(col.default)) parts.push(`.default(trustedSql(${this.renderStringLiteral(col.default.sql)}))`);
173
173
  } else if (col.default === null) parts.push(`.default(null)`);
174
174
  if (col.primaryKey) parts.push(`.primaryKey()`);
175
175
  if (col.unique) parts.push(`.unique()`);
176
176
  if (col.references) {
177
177
  const refParts = [];
178
- if (col.references.onDelete) refParts.push(`onDelete: '${col.references.onDelete}'`);
179
- if (col.references.onUpdate) refParts.push(`onUpdate: '${col.references.onUpdate}'`);
178
+ if (col.references.onDelete) refParts.push(`onDelete: ${this.renderStringLiteral(col.references.onDelete)}`);
179
+ if (col.references.onUpdate) refParts.push(`onUpdate: ${this.renderStringLiteral(col.references.onUpdate)}`);
180
180
  const opts = refParts.length > 0 ? `, { ${refParts.join(", ")} }` : "";
181
- parts.push(`.references('${col.references.table}', '${col.references.column}'${opts})`);
181
+ parts.push(`.references(${this.renderStringLiteral(col.references.table)}, ${this.renderStringLiteral(col.references.column)}${opts})`);
182
182
  }
183
183
  return parts.join("");
184
184
  }
185
+ renderStringLiteral(value) {
186
+ return JSON.stringify(value);
187
+ }
188
+ renderStringArrayLiteral(values) {
189
+ return `[${values.map((value) => this.renderStringLiteral(value)).join(", ")}]`;
190
+ }
191
+ renderCommentStringLiteral(value) {
192
+ return this.renderStringLiteral(value).replaceAll("*/", "*\\/");
193
+ }
194
+ renderColumnTypeMethod(type) {
195
+ switch (type) {
196
+ case InternalColumnType.SERIAL:
197
+ case InternalColumnType.INT:
198
+ case InternalColumnType.BIGINT:
199
+ case InternalColumnType.TEXT:
200
+ case InternalColumnType.BOOL:
201
+ case InternalColumnType.TIMESTAMPTZ:
202
+ case InternalColumnType.JSONB:
203
+ case InternalColumnType.UUID: return type;
204
+ default: throw new Error(`Unsupported column type in migration source generation: ${this.renderStringLiteral(type)}`);
205
+ }
206
+ }
185
207
  timestamp() {
186
208
  const now = /* @__PURE__ */ new Date();
187
209
  return `${now.getFullYear()}${String(now.getMonth() + 1).padStart(2, "0")}${String(now.getDate()).padStart(2, "0")}${String(now.getHours()).padStart(2, "0")}${String(now.getMinutes()).padStart(2, "0")}${String(now.getSeconds()).padStart(2, "0")}`;
@@ -193,4 +215,4 @@ var MigrationGenerator = class MigrationGenerator {
193
215
  //#endregion
194
216
  export { MigrationGenerator as t };
195
217
 
196
- //# sourceMappingURL=MigrationGenerator-BmmerPXJ.js.map
218
+ //# sourceMappingURL=MigrationGenerator-D-sSbsFG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MigrationGenerator-D-sSbsFG.js","names":[],"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 { InternalColumnType } from '../domain/internal/InternalColumnType';\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 = ${this.renderStringLiteral(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: ${this.renderStringLiteral(operation.table)}, name: ${this.renderStringLiteral(operation.name)} })`;\n case 'custom':\n return `/* custom operation ${this.renderCommentStringLiteral(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(${this.renderStringLiteral(operation.table)}).drop()`;\n case InternalOperationKind.TABLE_DROP:\n return `/* manual reverse required: recreate dropped table ${this.renderCommentStringLiteral(operation.table)} */`;\n case InternalOperationKind.COLUMN_ADD:\n return `op.table(${this.renderStringLiteral(operation.table)}).dropColumn(${this.renderStringLiteral(operation.column.name)})`;\n case InternalOperationKind.COLUMN_DROP:\n return `/* manual reverse required: restore dropped column ${this.renderCommentStringLiteral(operation.column)} */`;\n case InternalOperationKind.COLUMN_ALTER:\n return `/* manual reverse required: revert ALTER COLUMN ${this.renderCommentStringLiteral(operation.column)} on ${this.renderCommentStringLiteral(operation.table)} */`;\n case InternalOperationKind.COLUMN_RENAME:\n return `op.table(${this.renderStringLiteral(operation.table)}).renameColumn(${this.renderStringLiteral(operation.to)}, ${this.renderStringLiteral(operation.from)})`;\n case InternalOperationKind.INDEX_CREATE:\n return `op.index.drop({ name: ${this.renderStringLiteral(operation.name)}, table: ${this.renderStringLiteral(operation.table)} })`;\n case InternalOperationKind.INDEX_DROP:\n return `/* manual reverse required: recreate dropped index ${this.renderCommentStringLiteral(operation.name)} */`;\n case InternalOperationKind.FK_CREATE:\n return `op.foreignKeyDrop({ table: ${this.renderStringLiteral(operation.table)}, name: ${this.renderStringLiteral(operation.name ?? `${operation.table}_${operation.columns.join('_')}_fkey`)} })`;\n case InternalOperationKind.FK_DROP:\n return `/* manual reverse required: recreate dropped FK ${this.renderCommentStringLiteral(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 ${this.renderCommentStringLiteral(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(${this.renderStringLiteral(col.name)}, (b) => b${chain});`;\n });\n\n return [\n `op.table(${this.renderStringLiteral(operation.table)}).create((cols) => {`,\n ...columnLines,\n ` })`,\n ].join('\\n');\n }\n\n private renderTableDrop(operation: TableDrop): string {\n if (operation.cascade) {\n return `op.table(${this.renderStringLiteral(operation.table)}).drop({ cascade: true })`;\n }\n return `op.table(${this.renderStringLiteral(operation.table)}).drop()`;\n }\n\n private renderColumnAdd(operation: ColumnAdd): string {\n const chain = this.renderColumnChain(operation.column);\n return `op.table(${this.renderStringLiteral(operation.table)}).addColumn(${this.renderStringLiteral(operation.column.name)}, (b) => b${chain})`;\n }\n\n private renderColumnDrop(operation: ColumnDrop): string {\n return `op.table(${this.renderStringLiteral(operation.table)}).dropColumn(${this.renderStringLiteral(operation.column)})`;\n }\n\n private renderColumnAlter(operation: ColumnAlter): string {\n const parts: string[] = [];\n if (operation.to.type) {\n parts.push(`type: ${this.renderStringLiteral(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(${this.renderStringLiteral(operation.to.default.sql)})`);\n }\n }\n return `op.table(${this.renderStringLiteral(operation.table)}).alterColumn(${this.renderStringLiteral(operation.column)}, { ${parts.join(', ')} })`;\n }\n\n private renderColumnRename(operation: ColumnRename): string {\n return `op.table(${this.renderStringLiteral(operation.table)}).renameColumn(${this.renderStringLiteral(operation.from)}, ${this.renderStringLiteral(operation.to)})`;\n }\n\n private renderIndexCreate(operation: IndexCreate): string {\n const parts: string[] = [\n `name: ${this.renderStringLiteral(operation.name)}`,\n `table: ${this.renderStringLiteral(operation.table)}`,\n `on: ${this.renderStringArrayLiteral(operation.on)}`,\n ];\n if (operation.unique) {\n parts.push(`unique: true`);\n }\n if (operation.where) {\n parts.push(`where: trustedSql(${this.renderStringLiteral(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: ${this.renderStringLiteral(operation.name)}, table: ${this.renderStringLiteral(operation.table)} })`;\n }\n\n private renderForeignKeyCreate(operation: ForeignKeyCreate): string {\n const parts: string[] = [\n `table: ${this.renderStringLiteral(operation.table)}`,\n `columns: ${this.renderStringArrayLiteral(operation.columns)}`,\n `references: { table: ${this.renderStringLiteral(operation.refTable)}, columns: ${this.renderStringArrayLiteral(operation.refColumns)} }`,\n ];\n if (operation.name) {\n parts.push(`name: ${this.renderStringLiteral(operation.name)}`);\n }\n if (operation.onDelete) {\n parts.push(`onDelete: ${this.renderStringLiteral(operation.onDelete)}`);\n }\n if (operation.onUpdate) {\n parts.push(`onUpdate: ${this.renderStringLiteral(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: ${this.renderStringLiteral(operation.table)}, name: ${this.renderStringLiteral(operation.name)} })`;\n }\n\n private renderColumnChain(col: ColumnSpec): string {\n const parts: string[] = [];\n\n if (col.type) {\n parts.push(`.${this.renderColumnTypeMethod(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(${this.renderStringLiteral(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: ${this.renderStringLiteral(col.references.onDelete)}`);\n }\n if (col.references.onUpdate) {\n refParts.push(`onUpdate: ${this.renderStringLiteral(col.references.onUpdate)}`);\n }\n const opts = refParts.length > 0 ? `, { ${refParts.join(', ')} }` : '';\n parts.push(\n `.references(${this.renderStringLiteral(col.references.table)}, ${this.renderStringLiteral(col.references.column)}${opts})`\n );\n }\n\n return parts.join('');\n }\n\n private renderStringLiteral(value: string): string {\n return JSON.stringify(value);\n }\n\n private renderStringArrayLiteral(values: string[]): string {\n return `[${values.map((value) => this.renderStringLiteral(value)).join(', ')}]`;\n }\n\n private renderCommentStringLiteral(value: string): string {\n return this.renderStringLiteral(value).replaceAll('*/', '*\\\\/');\n }\n\n private renderColumnTypeMethod(type: ColumnSpec['type']): string {\n switch (type) {\n case InternalColumnType.SERIAL:\n case InternalColumnType.INT:\n case InternalColumnType.BIGINT:\n case InternalColumnType.TEXT:\n case InternalColumnType.BOOL:\n case InternalColumnType.TIMESTAMPTZ:\n case InternalColumnType.JSONB:\n case InternalColumnType.UUID:\n return type;\n default:\n throw new Error(\n `Unsupported column type in migration source generation: ${this.renderStringLiteral(type)}`\n );\n }\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,6 +1,6 @@
1
1
  import { t as Migration } from "./Migration-DxHHPyzn.js";
2
2
  import { t as CollectingBuilder } from "./CollectingBuilder-BIfAKs_x.js";
3
- import { n as createDefaultCompilerStrategy, r as InternalDialect } from "./CompilerStrategy-vcZKg8qf.js";
3
+ import { n as createDefaultCompilerStrategy, r as InternalDialect } from "./CompilerStrategy-Dc5_3-nw.js";
4
4
  import { isError } from "@danceroutine/tango-core";
5
5
  import { readdir } from "node:fs/promises";
6
6
  import { extname, resolve } from "node:path";
@@ -219,4 +219,4 @@ var MigrationRunner = class MigrationRunner {
219
219
  //#endregion
220
220
  export { loadModule as n, MigrationRunner as t };
221
221
 
222
- //# sourceMappingURL=MigrationRunner-B5AJel12.js.map
222
+ //# sourceMappingURL=MigrationRunner-vG1lVlkz.js.map
@@ -1 +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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"}
1
+ {"version":3,"file":"MigrationRunner-vG1lVlkz.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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"}
@@ -1,5 +1,4 @@
1
- import { t as InternalColumnType } from "./InternalColumnType-Dzs9T6a6.js";
2
- import { t as InternalOperationKind } from "./InternalOperationKind-M4a4H9OZ.js";
1
+ import { n as InternalColumnType, t as InternalOperationKind } from "./InternalOperationKind-BJ7n-pUH.js";
3
2
  import { t as MigrationSqlSafetyAdapter } from "./MigrationSqlSafetyAdapter-yP6fPjeC.js";
4
3
  //#region src/compilers/dialects/PostgresCompiler.ts
5
4
  /**
@@ -439,4 +438,4 @@ var SqliteCompilerFactory = class SqliteCompilerFactory {
439
438
  //#endregion
440
439
  export { PostgresCompiler as i, SqliteCompiler as n, PostgresCompilerFactory as r, SqliteCompilerFactory as t };
441
440
 
442
- //# sourceMappingURL=SqliteCompilerFactory-CXlPAclY.js.map
441
+ //# sourceMappingURL=SqliteCompilerFactory-C_56xoQM.js.map