@danceroutine/tango-migrations 1.11.15 → 1.12.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/dist/CompilerStrategy-CosQJr-g.js.map +1 -1
- package/dist/IntrospectorStrategy-D_9umttn.js.map +1 -1
- package/dist/MigrationGenerator-D-sSbsFG.js.map +1 -1
- package/dist/MigrationRunner-DCCeU1kq.js.map +1 -1
- package/dist/MigrationSqlSafetyAdapter-yP6fPjeC.js.map +1 -1
- package/dist/SqliteCompilerFactory-C_56xoQM.js.map +1 -1
- package/dist/SqliteIntrospector-CfItmGgA.js.map +1 -1
- package/dist/builder-Bhm_to6b.js.map +1 -1
- package/dist/cli-Dhp2BZsB.js.map +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/diff-DdwU_MyI.js.map +1 -1
- package/package.json +8 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CompilerStrategy-CosQJr-g.js","names":[],"sources":["../src/strategies/CompilerStrategy.ts"],"sourcesContent":["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":";;;;;;AAcA,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,
|
|
1
|
+
{"version":3,"file":"CompilerStrategy-CosQJr-g.js","names":[],"sources":["../src/strategies/CompilerStrategy.ts"],"sourcesContent":["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":";;;;;;AAcA,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,CAAC,CAAC,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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IntrospectorStrategy-D_9umttn.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,
|
|
1
|
+
{"version":3,"file":"IntrospectorStrategy-D_9umttn.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,CAAC,CAAC,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 +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
|
+
{"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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MigrationRunner-DCCeU1kq.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';\nimport { webcrypto } from 'node:crypto';\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\ninterface AppliedMigrationRecord {\n id: string;\n checksum: string;\n}\n\ninterface MigrationChecksums {\n current: string;\n legacyOperations: string;\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 const storedChecksum = applied.get(migration.id);\n if (storedChecksum !== undefined) {\n await this.assertAppliedChecksum(migration, storedChecksum);\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 const statuses: { id: string; applied: boolean }[] = [];\n\n for (const migration of migrations) {\n const storedChecksum = applied.get(migration.id);\n if (storedChecksum !== undefined) {\n await this.assertAppliedChecksum(migration, storedChecksum);\n }\n\n statuses.push({\n id: migration.id,\n applied: storedChecksum !== undefined,\n });\n }\n\n return statuses;\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<Map<string, string>> {\n const table = this.dialect === InternalDialect.POSTGRES ? `\"${JOURNAL}\"` : JOURNAL;\n const { rows } = await this.client.query<AppliedMigrationRecord>(`SELECT id, checksum FROM ${table}`);\n return new Map(rows.map((record) => [record.id, record.checksum]));\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 = await this.calculateBuilderChecksum(builder);\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 private async assertAppliedChecksum(migration: Migration, storedChecksum: string): Promise<void> {\n const checksums = await this.calculateChecksums(migration);\n\n // Older Tango versions stored operation-only checksums. Keep those\n // journals readable while writing data-aware checksums for new rows.\n if (checksums.current !== storedChecksum && checksums.legacyOperations !== storedChecksum) {\n throw new Error(\n `Applied migration '${migration.id}' checksum mismatch. The migration file may have been modified after it was applied.`\n );\n }\n }\n\n private async calculateChecksums(migration: Migration): Promise<MigrationChecksums> {\n const builder = new CollectingBuilder();\n await migration.up(builder);\n\n return {\n current: await this.calculateBuilderChecksum(builder),\n legacyOperations: await this.calculateLegacyOperationsChecksum(builder),\n };\n }\n\n private async calculateBuilderChecksum(builder: CollectingBuilder): Promise<string> {\n return this.hashPayload({\n operations: builder.ops,\n dataFns: builder.dataFns.map((fn) => fn.toString()),\n });\n }\n\n private async calculateLegacyOperationsChecksum(builder: CollectingBuilder): Promise<string> {\n return this.hashPayload(builder.ops);\n }\n\n /**\n * Compute a SHA-256 digest of a migration checksum payload.\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 */\n private async hashPayload(payload: unknown): Promise<string> {\n const digest = await webcrypto.subtle.digest('SHA-256', Buffer.from(JSON.stringify(payload), 'utf8'));\n return Buffer.from(digest).toString('hex');\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-DCCeU1kq.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';\nimport { webcrypto } from 'node:crypto';\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\ninterface AppliedMigrationRecord {\n id: string;\n checksum: string;\n}\n\ninterface MigrationChecksums {\n current: string;\n legacyOperations: string;\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 const storedChecksum = applied.get(migration.id);\n if (storedChecksum !== undefined) {\n await this.assertAppliedChecksum(migration, storedChecksum);\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 const statuses: { id: string; applied: boolean }[] = [];\n\n for (const migration of migrations) {\n const storedChecksum = applied.get(migration.id);\n if (storedChecksum !== undefined) {\n await this.assertAppliedChecksum(migration, storedChecksum);\n }\n\n statuses.push({\n id: migration.id,\n applied: storedChecksum !== undefined,\n });\n }\n\n return statuses;\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<Map<string, string>> {\n const table = this.dialect === InternalDialect.POSTGRES ? `\"${JOURNAL}\"` : JOURNAL;\n const { rows } = await this.client.query<AppliedMigrationRecord>(`SELECT id, checksum FROM ${table}`);\n return new Map(rows.map((record) => [record.id, record.checksum]));\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 = await this.calculateBuilderChecksum(builder);\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 private async assertAppliedChecksum(migration: Migration, storedChecksum: string): Promise<void> {\n const checksums = await this.calculateChecksums(migration);\n\n // Older Tango versions stored operation-only checksums. Keep those\n // journals readable while writing data-aware checksums for new rows.\n if (checksums.current !== storedChecksum && checksums.legacyOperations !== storedChecksum) {\n throw new Error(\n `Applied migration '${migration.id}' checksum mismatch. The migration file may have been modified after it was applied.`\n );\n }\n }\n\n private async calculateChecksums(migration: Migration): Promise<MigrationChecksums> {\n const builder = new CollectingBuilder();\n await migration.up(builder);\n\n return {\n current: await this.calculateBuilderChecksum(builder),\n legacyOperations: await this.calculateLegacyOperationsChecksum(builder),\n };\n }\n\n private async calculateBuilderChecksum(builder: CollectingBuilder): Promise<string> {\n return this.hashPayload({\n operations: builder.ops,\n dataFns: builder.dataFns.map((fn) => fn.toString()),\n });\n }\n\n private async calculateLegacyOperationsChecksum(builder: CollectingBuilder): Promise<string> {\n return this.hashPayload(builder.ops);\n }\n\n /**\n * Compute a SHA-256 digest of a migration checksum payload.\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 */\n private async hashPayload(payload: unknown): Promise<string> {\n const digest = await webcrypto.subtle.digest('SHA-256', Buffer.from(JSON.stringify(payload), 'utf8'));\n return Buffer.from(digest).toString('hex');\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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MigrationSqlSafetyAdapter-yP6fPjeC.js","names":[],"sources":["../src/internal/MigrationSqlSafetyAdapter.ts"],"sourcesContent":["import {\n SqlSafetyEngine,\n isTrustedSqlFragment,\n quoteSqlIdentifier,\n type SqlDialect,\n type SqlIdentifierRole,\n type TrustedSqlFragment,\n} from '@danceroutine/tango-core';\n\ntype IdentifierRole = Extract<SqlIdentifierRole, 'table' | 'column' | 'index' | 'constraint' | 'schema'>;\n\n/**\n * Migrations-local adapter that maps migration operations into the shared SQL\n * safety engine and returns quoted identifiers or trusted raw fragments.\n */\nexport class MigrationSqlSafetyAdapter {\n constructor(\n private readonly dialect: SqlDialect,\n private readonly engine: SqlSafetyEngine = new SqlSafetyEngine()\n ) {}\n\n table(value: string): string {\n return this.quote('table', 'table', value);\n }\n\n column(value: string, allowlist?: readonly string[]): string {\n return this.quote('column', 'column', value, allowlist);\n }\n\n columns(values: readonly string[], allowlist?: readonly string[]): string[] {\n return values.map((value, index) => this.quote(`column:${index}`, 'column', value, allowlist));\n }\n\n index(value: string): string {\n return this.quote('index', 'index', value);\n }\n\n constraint(value: string): string {\n return this.quote('constraint', 'constraint', value);\n }\n\n schema(value: string): string {\n return this.quote('schema', 'schema', value);\n }\n\n rawFragment(key: string, value: TrustedSqlFragment): string {\n return this.engine.validate({\n rawFragments: [{ key, value }],\n }).rawFragments[key]!.sql;\n }\n\n optionalRawFragment(key: string, value?: TrustedSqlFragment): string | undefined {\n if (!value) {\n return undefined;\n }\n\n return this.rawFragment(key, value);\n }\n\n rawDefault(\n value: TrustedSqlFragment | { now: true } | null | undefined,\n nowSql: string\n ): string | null | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n if (value === null) {\n return null;\n }\n\n if (this.isNowDefault(value)) {\n return nowSql;\n }\n\n return this.rawFragment('default', value);\n }\n\n isTrustedFragment(value: unknown): value is TrustedSqlFragment {\n return isTrustedSqlFragment(value);\n }\n\n private quote(key: string, role: IdentifierRole, value: string, allowlist?: readonly string[]): string {\n const validated = this.engine.validate({\n identifiers: [{ key, role, value, allowlist }],\n });\n\n return quoteSqlIdentifier(validated.identifiers[key]!, this.dialect);\n }\n\n private isNowDefault(value: TrustedSqlFragment | { now: true }): value is { now: true } {\n return typeof value === 'object' && value !== null && 'now' in value && value.now === true;\n }\n}\n"],"mappings":";;;;;;AAeA,IAAa,4BAAb,MAAuC;CAEd;CACA;CAFrB,YACI,SACA,SAA2C,IAAI,gBAAgB,GACjE;EAFmB,KAAA,UAAA;EACA,KAAA,SAAA;CAClB;CAEH,MAAM,OAAuB;EACzB,OAAO,KAAK,MAAM,SAAS,SAAS,KAAK;CAC7C;CAEA,OAAO,OAAe,WAAuC;EACzD,OAAO,KAAK,MAAM,UAAU,UAAU,OAAO,SAAS;CAC1D;CAEA,QAAQ,QAA2B,WAAyC;EACxE,OAAO,OAAO,KAAK,OAAO,UAAU,KAAK,MAAM,UAAU,SAAS,UAAU,OAAO,SAAS,CAAC;CACjG;CAEA,MAAM,OAAuB;EACzB,OAAO,KAAK,MAAM,SAAS,SAAS,KAAK;CAC7C;CAEA,WAAW,OAAuB;EAC9B,OAAO,KAAK,MAAM,cAAc,cAAc,KAAK;CACvD;CAEA,OAAO,OAAuB;EAC1B,OAAO,KAAK,MAAM,UAAU,UAAU,KAAK;CAC/C;CAEA,YAAY,KAAa,OAAmC;EACxD,OAAO,KAAK,OAAO,SAAS,EACxB,cAAc,CAAC;GAAE;GAAK;EAAM,CAAC,EACjC,CAAC,
|
|
1
|
+
{"version":3,"file":"MigrationSqlSafetyAdapter-yP6fPjeC.js","names":[],"sources":["../src/internal/MigrationSqlSafetyAdapter.ts"],"sourcesContent":["import {\n SqlSafetyEngine,\n isTrustedSqlFragment,\n quoteSqlIdentifier,\n type SqlDialect,\n type SqlIdentifierRole,\n type TrustedSqlFragment,\n} from '@danceroutine/tango-core';\n\ntype IdentifierRole = Extract<SqlIdentifierRole, 'table' | 'column' | 'index' | 'constraint' | 'schema'>;\n\n/**\n * Migrations-local adapter that maps migration operations into the shared SQL\n * safety engine and returns quoted identifiers or trusted raw fragments.\n */\nexport class MigrationSqlSafetyAdapter {\n constructor(\n private readonly dialect: SqlDialect,\n private readonly engine: SqlSafetyEngine = new SqlSafetyEngine()\n ) {}\n\n table(value: string): string {\n return this.quote('table', 'table', value);\n }\n\n column(value: string, allowlist?: readonly string[]): string {\n return this.quote('column', 'column', value, allowlist);\n }\n\n columns(values: readonly string[], allowlist?: readonly string[]): string[] {\n return values.map((value, index) => this.quote(`column:${index}`, 'column', value, allowlist));\n }\n\n index(value: string): string {\n return this.quote('index', 'index', value);\n }\n\n constraint(value: string): string {\n return this.quote('constraint', 'constraint', value);\n }\n\n schema(value: string): string {\n return this.quote('schema', 'schema', value);\n }\n\n rawFragment(key: string, value: TrustedSqlFragment): string {\n return this.engine.validate({\n rawFragments: [{ key, value }],\n }).rawFragments[key]!.sql;\n }\n\n optionalRawFragment(key: string, value?: TrustedSqlFragment): string | undefined {\n if (!value) {\n return undefined;\n }\n\n return this.rawFragment(key, value);\n }\n\n rawDefault(\n value: TrustedSqlFragment | { now: true } | null | undefined,\n nowSql: string\n ): string | null | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n if (value === null) {\n return null;\n }\n\n if (this.isNowDefault(value)) {\n return nowSql;\n }\n\n return this.rawFragment('default', value);\n }\n\n isTrustedFragment(value: unknown): value is TrustedSqlFragment {\n return isTrustedSqlFragment(value);\n }\n\n private quote(key: string, role: IdentifierRole, value: string, allowlist?: readonly string[]): string {\n const validated = this.engine.validate({\n identifiers: [{ key, role, value, allowlist }],\n });\n\n return quoteSqlIdentifier(validated.identifiers[key]!, this.dialect);\n }\n\n private isNowDefault(value: TrustedSqlFragment | { now: true }): value is { now: true } {\n return typeof value === 'object' && value !== null && 'now' in value && value.now === true;\n }\n}\n"],"mappings":";;;;;;AAeA,IAAa,4BAAb,MAAuC;CAEd;CACA;CAFrB,YACI,SACA,SAA2C,IAAI,gBAAgB,GACjE;EAFmB,KAAA,UAAA;EACA,KAAA,SAAA;CAClB;CAEH,MAAM,OAAuB;EACzB,OAAO,KAAK,MAAM,SAAS,SAAS,KAAK;CAC7C;CAEA,OAAO,OAAe,WAAuC;EACzD,OAAO,KAAK,MAAM,UAAU,UAAU,OAAO,SAAS;CAC1D;CAEA,QAAQ,QAA2B,WAAyC;EACxE,OAAO,OAAO,KAAK,OAAO,UAAU,KAAK,MAAM,UAAU,SAAS,UAAU,OAAO,SAAS,CAAC;CACjG;CAEA,MAAM,OAAuB;EACzB,OAAO,KAAK,MAAM,SAAS,SAAS,KAAK;CAC7C;CAEA,WAAW,OAAuB;EAC9B,OAAO,KAAK,MAAM,cAAc,cAAc,KAAK;CACvD;CAEA,OAAO,OAAuB;EAC1B,OAAO,KAAK,MAAM,UAAU,UAAU,KAAK;CAC/C;CAEA,YAAY,KAAa,OAAmC;EACxD,OAAO,KAAK,OAAO,SAAS,EACxB,cAAc,CAAC;GAAE;GAAK;EAAM,CAAC,EACjC,CAAC,CAAC,CAAC,aAAa,IAAI,CAAE;CAC1B;CAEA,oBAAoB,KAAa,OAAgD;EAC7E,IAAI,CAAC,OACD;EAGJ,OAAO,KAAK,YAAY,KAAK,KAAK;CACtC;CAEA,WACI,OACA,QACyB;EACzB,IAAI,UAAU,KAAA,GACV;EAGJ,IAAI,UAAU,MACV,OAAO;EAGX,IAAI,KAAK,aAAa,KAAK,GACvB,OAAO;EAGX,OAAO,KAAK,YAAY,WAAW,KAAK;CAC5C;CAEA,kBAAkB,OAA6C;EAC3D,OAAO,qBAAqB,KAAK;CACrC;CAEA,MAAc,KAAa,MAAsB,OAAe,WAAuC;EAKnG,OAAO,mBAJW,KAAK,OAAO,SAAS,EACnC,aAAa,CAAC;GAAE;GAAK;GAAM;GAAO;EAAU,CAAC,EACjD,CAEkC,CAAC,CAAC,YAAY,MAAO,KAAK,OAAO;CACvE;CAEA,aAAqB,OAAmE;EACpF,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,SAAS,SAAS,MAAM,QAAQ;CAC1F;AACJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SqliteCompilerFactory-C_56xoQM.js","names":["exhaustive"],"sources":["../src/compilers/dialects/PostgresCompiler.ts","../src/compilers/factories/PostgresCompilerFactory.ts","../src/compilers/dialects/SqliteCompiler.ts","../src/compilers/factories/SqliteCompilerFactory.ts"],"sourcesContent":["import type { ForeignKeyCreate, MigrationOperation, TableCreate } from '../../domain/MigrationOperation';\nimport type { SQL } from '../contracts/SQL';\nimport type { ColumnSpec } from '../../builder/contracts/ColumnSpec';\nimport type { ColumnType } from '../../builder/contracts/ColumnType';\nimport type { SQLCompiler } from '../contracts/SQLCompiler';\nimport { InternalOperationKind } from '../../domain/internal/InternalOperationKind';\nimport { InternalColumnType } from '../../domain/internal/InternalColumnType';\nimport { MigrationSqlSafetyAdapter } from '../../internal/MigrationSqlSafetyAdapter';\n\n/**\n * PostgreSQL SQL compiler for migration operations.\n */\nexport class PostgresCompiler implements SQLCompiler {\n static readonly BRAND = 'tango.migrations.postgres_compiler' as const;\n readonly __tangoBrand: typeof PostgresCompiler.BRAND = PostgresCompiler.BRAND;\n private readonly sqlSafety = new MigrationSqlSafetyAdapter('postgres');\n\n /**\n * Narrow an unknown value to the PostgreSQL migration compiler implementation.\n */\n static isPostgresCompiler(value: unknown): value is PostgresCompiler {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === PostgresCompiler.BRAND\n );\n }\n\n /**\n * Rewrite migration operations into PostgreSQL's preferred execution\n * order, including separating inline foreign keys from table creation.\n */\n prepareOperations(operations: MigrationOperation[]): MigrationOperation[] {\n const tableCreates: TableCreate[] = [];\n const remainder: MigrationOperation[] = [];\n\n for (const operation of operations) {\n if (operation.kind === InternalOperationKind.TABLE_CREATE) {\n tableCreates.push(operation);\n } else {\n remainder.push(operation);\n }\n }\n\n const strippedCreates: TableCreate[] = [];\n const foreignKeys: ForeignKeyCreate[] = [];\n\n for (const operation of tableCreates) {\n const { create, fks } = this.stripTableCreateForeignKeys(operation);\n strippedCreates.push(create);\n foreignKeys.push(...fks);\n }\n\n return [\n ...strippedCreates.sort((left, right) => left.table.localeCompare(right.table)),\n ...foreignKeys,\n ...remainder,\n ];\n }\n\n /**\n * Compile a migration operation into one or more PostgreSQL statements.\n */\n compile(op: MigrationOperation): SQL[] {\n switch (op.kind) {\n case InternalOperationKind.TABLE_CREATE: {\n const cols = op.columns.map((c) => this.colDDL(c)).join(', ');\n const pkCols = op.columns.filter((c) => c.primaryKey).map((c) => this.sqlSafety.column(c.name));\n const constraints: string[] = [];\n\n if (pkCols.length) {\n constraints.push(`PRIMARY KEY (${pkCols.join(', ')})`);\n }\n\n op.columns\n .filter((column) => column.references)\n .forEach((column) => {\n const references = column.references!;\n const fkName = `${op.table}_${column.name}_fkey`;\n let fk = `CONSTRAINT ${this.sqlSafety.constraint(fkName)} FOREIGN KEY (${this.sqlSafety.column(column.name)}) REFERENCES ${this.sqlSafety.table(references.table)}(${this.sqlSafety.column(references.column)})`;\n if (references.onDelete) {\n fk += ` ON DELETE ${references.onDelete}`;\n }\n if (references.onUpdate) {\n fk += ` ON UPDATE ${references.onUpdate}`;\n }\n constraints.push(fk);\n });\n\n const allParts = [cols, ...constraints].join(', ');\n const sql = `CREATE TABLE ${this.sqlSafety.table(op.table)} (${allParts})`;\n return [{ sql, params: [] }];\n }\n\n case InternalOperationKind.TABLE_DROP:\n return [\n { sql: `DROP TABLE ${this.sqlSafety.table(op.table)}${op.cascade ? ' CASCADE' : ''}`, params: [] },\n ];\n\n case InternalOperationKind.COLUMN_ADD:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ADD COLUMN ${this.colDDL(op.column)}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.COLUMN_DROP:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} DROP COLUMN ${this.sqlSafety.column(op.column)}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.COLUMN_ALTER: {\n const out: SQL[] = [];\n if (op.to.type) {\n out.push({\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ALTER COLUMN ${this.sqlSafety.column(op.column)} TYPE ${this.typeToSQL(op.to.type)}`,\n params: [],\n });\n }\n if (op.to.notNull !== undefined) {\n out.push({\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ALTER COLUMN ${this.sqlSafety.column(op.column)} ${op.to.notNull ? 'SET NOT NULL' : 'DROP NOT NULL'}`,\n params: [],\n });\n }\n out.push(...this.compileDefaultChange(op.table, op.column, op.to.default));\n return out;\n }\n\n case InternalOperationKind.COLUMN_RENAME:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} RENAME COLUMN ${this.sqlSafety.column(op.from)} TO ${this.sqlSafety.column(op.to)}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.INDEX_CREATE: {\n const cols = this.sqlSafety.columns(op.on).join(', ');\n const uniq = op.unique ? 'UNIQUE ' : '';\n const conc = op.concurrently ? 'CONCURRENTLY ' : '';\n const where = this.sqlSafety.optionalRawFragment('where', op.where);\n return [\n {\n sql: `CREATE ${uniq}INDEX ${conc}${this.sqlSafety.index(op.name)} ON ${this.sqlSafety.table(op.table)} (${cols})${where ? ` WHERE ${where}` : ''}`,\n params: [],\n },\n ];\n }\n\n case InternalOperationKind.INDEX_DROP: {\n const conc = op.concurrently ? 'CONCURRENTLY ' : '';\n return [{ sql: `DROP INDEX ${conc}${this.sqlSafety.index(op.name)}`, params: [] }];\n }\n\n case InternalOperationKind.FK_CREATE: {\n const cols = this.sqlSafety.columns(op.columns).join(', ');\n const refs = this.sqlSafety.columns(op.refColumns).join(', ');\n const name = op.name ?? `${op.table}_${op.columns.join('_')}_fkey`;\n const notValid = op.notValid ? ' NOT VALID' : '';\n const onDel = op.onDelete ? ` ON DELETE ${op.onDelete}` : '';\n const onUpd = op.onUpdate ? ` ON UPDATE ${op.onUpdate}` : '';\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ADD CONSTRAINT ${this.sqlSafety.constraint(name)} FOREIGN KEY (${cols}) REFERENCES ${this.sqlSafety.table(op.refTable)} (${refs})${onDel}${onUpd}${notValid}`,\n params: [],\n },\n ];\n }\n\n case InternalOperationKind.FK_VALIDATE:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} VALIDATE CONSTRAINT ${this.sqlSafety.constraint(op.name)}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.FK_DROP:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} DROP CONSTRAINT ${this.sqlSafety.constraint(op.name)}`,\n params: [],\n },\n ];\n\n default:\n return [];\n }\n }\n\n /**\n * Compile a DEFAULT value change into ALTER TABLE statements.\n * Extracted to flatten the nested conditional logic.\n */\n private compileDefaultChange(table: string, column: string, defaultValue: unknown): SQL[] {\n if (defaultValue === undefined) {\n return [];\n }\n\n if (defaultValue === null) {\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(table)} ALTER COLUMN ${this.sqlSafety.column(column)} DROP DEFAULT`,\n params: [],\n },\n ];\n }\n\n if (this.sqlSafety.isTrustedFragment(defaultValue)) {\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(table)} ALTER COLUMN ${this.sqlSafety.column(column)} SET DEFAULT ${this.sqlSafety.rawFragment('default', defaultValue)}`,\n params: [],\n },\n ];\n }\n\n if (\n defaultValue &&\n typeof defaultValue === 'object' &&\n 'now' in defaultValue &&\n (defaultValue as { now?: unknown }).now\n ) {\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(table)} ALTER COLUMN ${this.sqlSafety.column(column)} SET DEFAULT now()`,\n params: [],\n },\n ];\n }\n\n return [];\n }\n\n private stripTableCreateForeignKeys(op: TableCreate): { create: TableCreate; fks: ForeignKeyCreate[] } {\n const fks: ForeignKeyCreate[] = [];\n const columns = op.columns.map((column) => {\n if (!column.references) {\n return column;\n }\n\n const references = column.references;\n fks.push({\n kind: InternalOperationKind.FK_CREATE,\n table: op.table,\n columns: [column.name],\n refTable: references.table,\n refColumns: [references.column],\n onDelete: references.onDelete,\n onUpdate: references.onUpdate,\n });\n\n const { references: _references, ...rest } = column;\n return { ...rest };\n });\n\n return { create: { ...op, columns }, fks };\n }\n\n private colDDL(column: ColumnSpec): string {\n const parts: string[] = [this.sqlSafety.column(column.name)];\n\n switch (column.type) {\n case InternalColumnType.SERIAL:\n parts.push('SERIAL');\n break;\n case InternalColumnType.INT:\n parts.push('INTEGER');\n break;\n case InternalColumnType.BIGINT:\n parts.push('BIGINT');\n break;\n case InternalColumnType.TEXT:\n parts.push('TEXT');\n break;\n case InternalColumnType.BOOL:\n parts.push('BOOLEAN');\n break;\n case InternalColumnType.TIMESTAMPTZ:\n parts.push('TIMESTAMPTZ');\n break;\n case InternalColumnType.JSONB:\n parts.push('JSONB');\n break;\n case InternalColumnType.UUID:\n parts.push('UUID');\n break;\n }\n\n if (column.notNull) {\n parts.push('NOT NULL');\n }\n const defaultSql = this.sqlSafety.rawDefault(column.default, 'now()');\n if (defaultSql) {\n parts.push(`DEFAULT ${defaultSql}`);\n }\n if (column.unique && !column.primaryKey) {\n parts.push('UNIQUE');\n }\n\n return parts.join(' ');\n }\n\n private typeToSQL(type: ColumnType): string {\n switch (type) {\n case InternalColumnType.SERIAL:\n return 'SERIAL';\n case InternalColumnType.INT:\n return 'INTEGER';\n case InternalColumnType.BIGINT:\n return 'BIGINT';\n case InternalColumnType.TEXT:\n return 'TEXT';\n case InternalColumnType.BOOL:\n return 'BOOLEAN';\n case InternalColumnType.TIMESTAMPTZ:\n return 'TIMESTAMPTZ';\n case InternalColumnType.JSONB:\n return 'JSONB';\n case InternalColumnType.UUID:\n return 'UUID';\n default: {\n const exhaustive: never = type;\n throw new Error(`Unsupported column type: ${exhaustive}`);\n }\n }\n }\n}\n","import type { CompilerFactory } from '../contracts/CompilerFactory';\nimport type { SQLCompiler } from '../contracts/SQLCompiler';\nimport { PostgresCompiler } from '../dialects/PostgresCompiler';\n\n/**\n * Factory for PostgreSQL migration compilers.\n */\nexport class PostgresCompilerFactory implements CompilerFactory {\n static readonly BRAND = 'tango.migrations.postgres_compiler_factory' as const;\n readonly __tangoBrand: typeof PostgresCompilerFactory.BRAND = PostgresCompilerFactory.BRAND;\n\n /**\n * Narrow an unknown value to the factory that provisions PostgreSQL compilers.\n */\n static isPostgresCompilerFactory(value: unknown): value is PostgresCompilerFactory {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === PostgresCompilerFactory.BRAND\n );\n }\n\n /**\n * Create a PostgreSQL SQL compiler instance.\n */\n create(): SQLCompiler {\n return new PostgresCompiler();\n }\n}\n","import type { ColumnAdd, IndexCreate, MigrationOperation, TableCreate } from '../../domain/MigrationOperation';\nimport type { SQL } from '../contracts/SQL';\nimport type { ColumnSpec } from '../../builder/contracts/ColumnSpec';\nimport type { SQLCompiler } from '../contracts/SQLCompiler';\nimport { InternalOperationKind } from '../../domain/internal/InternalOperationKind';\nimport { InternalColumnType } from '../../domain/internal/InternalColumnType';\nimport { MigrationSqlSafetyAdapter } from '../../internal/MigrationSqlSafetyAdapter';\n\n/**\n * SQLite SQL compiler for migration operations.\n */\nexport class SqliteCompiler implements SQLCompiler {\n static readonly BRAND = 'tango.migrations.sqlite_compiler' as const;\n readonly __tangoBrand: typeof SqliteCompiler.BRAND = SqliteCompiler.BRAND;\n private readonly sqlSafety = new MigrationSqlSafetyAdapter('sqlite');\n\n /**\n * Narrow an unknown value to the SQLite migration compiler implementation.\n */\n static isSqliteCompiler(value: unknown): value is SqliteCompiler {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === SqliteCompiler.BRAND\n );\n }\n\n /**\n * Rewrite migration operations into SQLite's safe execution order,\n * including topological table creation and unique-column add expansion.\n */\n prepareOperations(operations: MigrationOperation[]): MigrationOperation[] {\n const tableCreates: TableCreate[] = [];\n const remainder: MigrationOperation[] = [];\n\n for (const operation of operations) {\n if (operation.kind === InternalOperationKind.TABLE_CREATE) {\n tableCreates.push(operation);\n } else {\n remainder.push(operation);\n }\n }\n\n const preparedRemainder = remainder.flatMap((operation) =>\n operation.kind === InternalOperationKind.COLUMN_ADD ? this.prepareColumnAdd(operation) : [operation]\n );\n\n return [...this.topologicalSortTableCreatesWithReferences(tableCreates), ...preparedRemainder];\n }\n\n /**\n * Compile a migration operation into one or more SQLite statements.\n */\n compile(op: MigrationOperation): SQL[] {\n switch (op.kind) {\n case InternalOperationKind.TABLE_CREATE: {\n const cols = op.columns.map((c) => this.colDDL(c));\n const pkCols = op.columns\n .filter((c) => c.primaryKey && c.type !== InternalColumnType.SERIAL)\n .map((c) => this.sqlSafety.column(c.name));\n\n if (pkCols.length) {\n cols.push(`PRIMARY KEY (${pkCols.join(', ')})`);\n }\n\n op.columns\n .filter((column) => column.references)\n .forEach((column) => {\n const references = column.references!;\n cols.push(\n `FOREIGN KEY (${this.sqlSafety.column(column.name)}) REFERENCES ${this.sqlSafety.table(references.table)}(${this.sqlSafety.column(references.column)})${references.onDelete ? ` ON DELETE ${references.onDelete}` : ''}${references.onUpdate ? ` ON UPDATE ${references.onUpdate}` : ''}`\n );\n });\n\n const sql = `CREATE TABLE ${this.sqlSafety.table(op.table)} (${cols.join(', ')})`;\n return [{ sql, params: [] }];\n }\n\n case InternalOperationKind.TABLE_DROP:\n return [{ sql: `DROP TABLE ${this.sqlSafety.table(op.table)}`, params: [] }];\n\n case InternalOperationKind.COLUMN_ADD:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ADD COLUMN ${this.colDDL({\n ...op.column,\n unique: false,\n })}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.COLUMN_DROP:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} DROP COLUMN ${this.sqlSafety.column(op.column)}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.COLUMN_RENAME:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} RENAME COLUMN ${this.sqlSafety.column(op.from)} TO ${this.sqlSafety.column(op.to)}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.INDEX_CREATE: {\n const cols = this.sqlSafety.columns(op.on).join(', ');\n const uniq = op.unique ? 'UNIQUE ' : '';\n const where = this.sqlSafety.optionalRawFragment('where', op.where);\n return [\n {\n sql: `CREATE ${uniq}INDEX ${this.sqlSafety.index(op.name)} ON ${this.sqlSafety.table(op.table)} (${cols})${where ? ` WHERE ${where}` : ''}`,\n params: [],\n },\n ];\n }\n\n case InternalOperationKind.INDEX_DROP:\n return [{ sql: `DROP INDEX ${this.sqlSafety.index(op.name)}`, params: [] }];\n\n case InternalOperationKind.COLUMN_ALTER:\n case InternalOperationKind.FK_CREATE:\n case InternalOperationKind.FK_VALIDATE:\n case InternalOperationKind.FK_DROP:\n return [];\n\n default:\n return [];\n }\n }\n\n private colDDL(column: ColumnSpec): string {\n const parts: string[] = [this.sqlSafety.column(column.name)];\n\n switch (column.type) {\n case InternalColumnType.SERIAL:\n parts.push('INTEGER PRIMARY KEY AUTOINCREMENT');\n return parts.join(' ');\n case InternalColumnType.INT:\n parts.push('INTEGER');\n break;\n case InternalColumnType.BIGINT:\n parts.push('INTEGER');\n break;\n case InternalColumnType.TEXT:\n parts.push('TEXT');\n break;\n case InternalColumnType.BOOL:\n parts.push('INTEGER');\n break;\n case InternalColumnType.TIMESTAMPTZ:\n parts.push('TEXT');\n break;\n case InternalColumnType.JSONB:\n parts.push('TEXT');\n break;\n case InternalColumnType.UUID:\n parts.push('TEXT');\n break;\n }\n\n if (column.notNull) {\n parts.push('NOT NULL');\n }\n const defaultSql = this.sqlSafety.rawDefault(column.default, \"(datetime('now'))\");\n if (defaultSql) {\n parts.push(`DEFAULT ${defaultSql}`);\n }\n if (column.unique && !column.primaryKey) {\n parts.push('UNIQUE');\n }\n\n return parts.join(' ');\n }\n\n private prepareColumnAdd(op: ColumnAdd): MigrationOperation[] {\n const preparedColumn = op.column;\n if (preparedColumn.notNull && preparedColumn.default === undefined && !preparedColumn.primaryKey) {\n throw new Error(\n `SQLite cannot add NOT NULL column '${preparedColumn.name}' to '${op.table}' without a default or backfill path.`\n );\n }\n\n if (!preparedColumn.unique) {\n return [op];\n }\n\n const addColumn: ColumnAdd = {\n ...op,\n column: {\n ...preparedColumn,\n unique: false,\n },\n };\n const createIndex: IndexCreate = {\n kind: InternalOperationKind.INDEX_CREATE,\n name: `${op.table}_${preparedColumn.name}_idx`,\n table: op.table,\n on: [preparedColumn.name],\n unique: true,\n };\n\n return [addColumn, createIndex];\n }\n\n private topologicalSortTableCreatesWithReferences(creates: TableCreate[]): TableCreate[] {\n if (creates.length <= 1) {\n return creates;\n }\n\n const tableSet = new Set(creates.map((create) => create.table));\n const byTable = new Map(creates.map((create) => [create.table, create]));\n const incoming = new Map<string, number>();\n const dependents = new Map<string, Set<string>>();\n\n for (const table of tableSet) {\n incoming.set(table, 0);\n }\n\n for (const create of creates) {\n const seenParents = new Set<string>();\n for (const column of create.columns) {\n if (!column.references) {\n continue;\n }\n const refTable = column.references.table;\n if (refTable === create.table || !tableSet.has(refTable)) {\n continue;\n }\n if (seenParents.has(refTable)) {\n continue;\n }\n seenParents.add(refTable);\n incoming.set(create.table, incoming.get(create.table)! + 1);\n if (!dependents.has(refTable)) {\n dependents.set(refTable, new Set());\n }\n dependents.get(refTable)!.add(create.table);\n }\n }\n\n const ready = [...tableSet].filter((table) => incoming.get(table) === 0);\n ready.sort((left, right) => left.localeCompare(right));\n\n const sorted: TableCreate[] = [];\n while (ready.length) {\n const next = ready.shift()!;\n sorted.push(byTable.get(next)!);\n\n for (const dependent of dependents.get(next) ?? []) {\n incoming.set(dependent, incoming.get(dependent)! - 1);\n if (incoming.get(dependent) === 0) {\n ready.push(dependent);\n ready.sort((left, right) => left.localeCompare(right));\n }\n }\n }\n\n if (sorted.length !== creates.length) {\n return [...creates].sort((left, right) => left.table.localeCompare(right.table));\n }\n\n return sorted;\n }\n}\n","import type { CompilerFactory } from '../contracts/CompilerFactory';\nimport type { SQLCompiler } from '../contracts/SQLCompiler';\nimport { SqliteCompiler } from '../dialects/SqliteCompiler';\n\n/**\n * Factory for SQLite migration compilers.\n */\nexport class SqliteCompilerFactory implements CompilerFactory {\n static readonly BRAND = 'tango.migrations.sqlite_compiler_factory' as const;\n readonly __tangoBrand: typeof SqliteCompilerFactory.BRAND = SqliteCompilerFactory.BRAND;\n\n /**\n * Narrow an unknown value to the factory that provisions SQLite compilers.\n */\n static isSqliteCompilerFactory(value: unknown): value is SqliteCompilerFactory {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === SqliteCompilerFactory.BRAND\n );\n }\n\n /**\n * Create a SQLite SQL compiler instance.\n */\n create(): SQLCompiler {\n return new SqliteCompiler();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SqliteCompilerFactory-C_56xoQM.js","names":["exhaustive"],"sources":["../src/compilers/dialects/PostgresCompiler.ts","../src/compilers/factories/PostgresCompilerFactory.ts","../src/compilers/dialects/SqliteCompiler.ts","../src/compilers/factories/SqliteCompilerFactory.ts"],"sourcesContent":["import type { ForeignKeyCreate, MigrationOperation, TableCreate } from '../../domain/MigrationOperation';\nimport type { SQL } from '../contracts/SQL';\nimport type { ColumnSpec } from '../../builder/contracts/ColumnSpec';\nimport type { ColumnType } from '../../builder/contracts/ColumnType';\nimport type { SQLCompiler } from '../contracts/SQLCompiler';\nimport { InternalOperationKind } from '../../domain/internal/InternalOperationKind';\nimport { InternalColumnType } from '../../domain/internal/InternalColumnType';\nimport { MigrationSqlSafetyAdapter } from '../../internal/MigrationSqlSafetyAdapter';\n\n/**\n * PostgreSQL SQL compiler for migration operations.\n */\nexport class PostgresCompiler implements SQLCompiler {\n static readonly BRAND = 'tango.migrations.postgres_compiler' as const;\n readonly __tangoBrand: typeof PostgresCompiler.BRAND = PostgresCompiler.BRAND;\n private readonly sqlSafety = new MigrationSqlSafetyAdapter('postgres');\n\n /**\n * Narrow an unknown value to the PostgreSQL migration compiler implementation.\n */\n static isPostgresCompiler(value: unknown): value is PostgresCompiler {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === PostgresCompiler.BRAND\n );\n }\n\n /**\n * Rewrite migration operations into PostgreSQL's preferred execution\n * order, including separating inline foreign keys from table creation.\n */\n prepareOperations(operations: MigrationOperation[]): MigrationOperation[] {\n const tableCreates: TableCreate[] = [];\n const remainder: MigrationOperation[] = [];\n\n for (const operation of operations) {\n if (operation.kind === InternalOperationKind.TABLE_CREATE) {\n tableCreates.push(operation);\n } else {\n remainder.push(operation);\n }\n }\n\n const strippedCreates: TableCreate[] = [];\n const foreignKeys: ForeignKeyCreate[] = [];\n\n for (const operation of tableCreates) {\n const { create, fks } = this.stripTableCreateForeignKeys(operation);\n strippedCreates.push(create);\n foreignKeys.push(...fks);\n }\n\n return [\n ...strippedCreates.sort((left, right) => left.table.localeCompare(right.table)),\n ...foreignKeys,\n ...remainder,\n ];\n }\n\n /**\n * Compile a migration operation into one or more PostgreSQL statements.\n */\n compile(op: MigrationOperation): SQL[] {\n switch (op.kind) {\n case InternalOperationKind.TABLE_CREATE: {\n const cols = op.columns.map((c) => this.colDDL(c)).join(', ');\n const pkCols = op.columns.filter((c) => c.primaryKey).map((c) => this.sqlSafety.column(c.name));\n const constraints: string[] = [];\n\n if (pkCols.length) {\n constraints.push(`PRIMARY KEY (${pkCols.join(', ')})`);\n }\n\n op.columns\n .filter((column) => column.references)\n .forEach((column) => {\n const references = column.references!;\n const fkName = `${op.table}_${column.name}_fkey`;\n let fk = `CONSTRAINT ${this.sqlSafety.constraint(fkName)} FOREIGN KEY (${this.sqlSafety.column(column.name)}) REFERENCES ${this.sqlSafety.table(references.table)}(${this.sqlSafety.column(references.column)})`;\n if (references.onDelete) {\n fk += ` ON DELETE ${references.onDelete}`;\n }\n if (references.onUpdate) {\n fk += ` ON UPDATE ${references.onUpdate}`;\n }\n constraints.push(fk);\n });\n\n const allParts = [cols, ...constraints].join(', ');\n const sql = `CREATE TABLE ${this.sqlSafety.table(op.table)} (${allParts})`;\n return [{ sql, params: [] }];\n }\n\n case InternalOperationKind.TABLE_DROP:\n return [\n { sql: `DROP TABLE ${this.sqlSafety.table(op.table)}${op.cascade ? ' CASCADE' : ''}`, params: [] },\n ];\n\n case InternalOperationKind.COLUMN_ADD:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ADD COLUMN ${this.colDDL(op.column)}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.COLUMN_DROP:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} DROP COLUMN ${this.sqlSafety.column(op.column)}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.COLUMN_ALTER: {\n const out: SQL[] = [];\n if (op.to.type) {\n out.push({\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ALTER COLUMN ${this.sqlSafety.column(op.column)} TYPE ${this.typeToSQL(op.to.type)}`,\n params: [],\n });\n }\n if (op.to.notNull !== undefined) {\n out.push({\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ALTER COLUMN ${this.sqlSafety.column(op.column)} ${op.to.notNull ? 'SET NOT NULL' : 'DROP NOT NULL'}`,\n params: [],\n });\n }\n out.push(...this.compileDefaultChange(op.table, op.column, op.to.default));\n return out;\n }\n\n case InternalOperationKind.COLUMN_RENAME:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} RENAME COLUMN ${this.sqlSafety.column(op.from)} TO ${this.sqlSafety.column(op.to)}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.INDEX_CREATE: {\n const cols = this.sqlSafety.columns(op.on).join(', ');\n const uniq = op.unique ? 'UNIQUE ' : '';\n const conc = op.concurrently ? 'CONCURRENTLY ' : '';\n const where = this.sqlSafety.optionalRawFragment('where', op.where);\n return [\n {\n sql: `CREATE ${uniq}INDEX ${conc}${this.sqlSafety.index(op.name)} ON ${this.sqlSafety.table(op.table)} (${cols})${where ? ` WHERE ${where}` : ''}`,\n params: [],\n },\n ];\n }\n\n case InternalOperationKind.INDEX_DROP: {\n const conc = op.concurrently ? 'CONCURRENTLY ' : '';\n return [{ sql: `DROP INDEX ${conc}${this.sqlSafety.index(op.name)}`, params: [] }];\n }\n\n case InternalOperationKind.FK_CREATE: {\n const cols = this.sqlSafety.columns(op.columns).join(', ');\n const refs = this.sqlSafety.columns(op.refColumns).join(', ');\n const name = op.name ?? `${op.table}_${op.columns.join('_')}_fkey`;\n const notValid = op.notValid ? ' NOT VALID' : '';\n const onDel = op.onDelete ? ` ON DELETE ${op.onDelete}` : '';\n const onUpd = op.onUpdate ? ` ON UPDATE ${op.onUpdate}` : '';\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ADD CONSTRAINT ${this.sqlSafety.constraint(name)} FOREIGN KEY (${cols}) REFERENCES ${this.sqlSafety.table(op.refTable)} (${refs})${onDel}${onUpd}${notValid}`,\n params: [],\n },\n ];\n }\n\n case InternalOperationKind.FK_VALIDATE:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} VALIDATE CONSTRAINT ${this.sqlSafety.constraint(op.name)}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.FK_DROP:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} DROP CONSTRAINT ${this.sqlSafety.constraint(op.name)}`,\n params: [],\n },\n ];\n\n default:\n return [];\n }\n }\n\n /**\n * Compile a DEFAULT value change into ALTER TABLE statements.\n * Extracted to flatten the nested conditional logic.\n */\n private compileDefaultChange(table: string, column: string, defaultValue: unknown): SQL[] {\n if (defaultValue === undefined) {\n return [];\n }\n\n if (defaultValue === null) {\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(table)} ALTER COLUMN ${this.sqlSafety.column(column)} DROP DEFAULT`,\n params: [],\n },\n ];\n }\n\n if (this.sqlSafety.isTrustedFragment(defaultValue)) {\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(table)} ALTER COLUMN ${this.sqlSafety.column(column)} SET DEFAULT ${this.sqlSafety.rawFragment('default', defaultValue)}`,\n params: [],\n },\n ];\n }\n\n if (\n defaultValue &&\n typeof defaultValue === 'object' &&\n 'now' in defaultValue &&\n (defaultValue as { now?: unknown }).now\n ) {\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(table)} ALTER COLUMN ${this.sqlSafety.column(column)} SET DEFAULT now()`,\n params: [],\n },\n ];\n }\n\n return [];\n }\n\n private stripTableCreateForeignKeys(op: TableCreate): { create: TableCreate; fks: ForeignKeyCreate[] } {\n const fks: ForeignKeyCreate[] = [];\n const columns = op.columns.map((column) => {\n if (!column.references) {\n return column;\n }\n\n const references = column.references;\n fks.push({\n kind: InternalOperationKind.FK_CREATE,\n table: op.table,\n columns: [column.name],\n refTable: references.table,\n refColumns: [references.column],\n onDelete: references.onDelete,\n onUpdate: references.onUpdate,\n });\n\n const { references: _references, ...rest } = column;\n return { ...rest };\n });\n\n return { create: { ...op, columns }, fks };\n }\n\n private colDDL(column: ColumnSpec): string {\n const parts: string[] = [this.sqlSafety.column(column.name)];\n\n switch (column.type) {\n case InternalColumnType.SERIAL:\n parts.push('SERIAL');\n break;\n case InternalColumnType.INT:\n parts.push('INTEGER');\n break;\n case InternalColumnType.BIGINT:\n parts.push('BIGINT');\n break;\n case InternalColumnType.TEXT:\n parts.push('TEXT');\n break;\n case InternalColumnType.BOOL:\n parts.push('BOOLEAN');\n break;\n case InternalColumnType.TIMESTAMPTZ:\n parts.push('TIMESTAMPTZ');\n break;\n case InternalColumnType.JSONB:\n parts.push('JSONB');\n break;\n case InternalColumnType.UUID:\n parts.push('UUID');\n break;\n }\n\n if (column.notNull) {\n parts.push('NOT NULL');\n }\n const defaultSql = this.sqlSafety.rawDefault(column.default, 'now()');\n if (defaultSql) {\n parts.push(`DEFAULT ${defaultSql}`);\n }\n if (column.unique && !column.primaryKey) {\n parts.push('UNIQUE');\n }\n\n return parts.join(' ');\n }\n\n private typeToSQL(type: ColumnType): string {\n switch (type) {\n case InternalColumnType.SERIAL:\n return 'SERIAL';\n case InternalColumnType.INT:\n return 'INTEGER';\n case InternalColumnType.BIGINT:\n return 'BIGINT';\n case InternalColumnType.TEXT:\n return 'TEXT';\n case InternalColumnType.BOOL:\n return 'BOOLEAN';\n case InternalColumnType.TIMESTAMPTZ:\n return 'TIMESTAMPTZ';\n case InternalColumnType.JSONB:\n return 'JSONB';\n case InternalColumnType.UUID:\n return 'UUID';\n default: {\n const exhaustive: never = type;\n throw new Error(`Unsupported column type: ${exhaustive}`);\n }\n }\n }\n}\n","import type { CompilerFactory } from '../contracts/CompilerFactory';\nimport type { SQLCompiler } from '../contracts/SQLCompiler';\nimport { PostgresCompiler } from '../dialects/PostgresCompiler';\n\n/**\n * Factory for PostgreSQL migration compilers.\n */\nexport class PostgresCompilerFactory implements CompilerFactory {\n static readonly BRAND = 'tango.migrations.postgres_compiler_factory' as const;\n readonly __tangoBrand: typeof PostgresCompilerFactory.BRAND = PostgresCompilerFactory.BRAND;\n\n /**\n * Narrow an unknown value to the factory that provisions PostgreSQL compilers.\n */\n static isPostgresCompilerFactory(value: unknown): value is PostgresCompilerFactory {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === PostgresCompilerFactory.BRAND\n );\n }\n\n /**\n * Create a PostgreSQL SQL compiler instance.\n */\n create(): SQLCompiler {\n return new PostgresCompiler();\n }\n}\n","import type { ColumnAdd, IndexCreate, MigrationOperation, TableCreate } from '../../domain/MigrationOperation';\nimport type { SQL } from '../contracts/SQL';\nimport type { ColumnSpec } from '../../builder/contracts/ColumnSpec';\nimport type { SQLCompiler } from '../contracts/SQLCompiler';\nimport { InternalOperationKind } from '../../domain/internal/InternalOperationKind';\nimport { InternalColumnType } from '../../domain/internal/InternalColumnType';\nimport { MigrationSqlSafetyAdapter } from '../../internal/MigrationSqlSafetyAdapter';\n\n/**\n * SQLite SQL compiler for migration operations.\n */\nexport class SqliteCompiler implements SQLCompiler {\n static readonly BRAND = 'tango.migrations.sqlite_compiler' as const;\n readonly __tangoBrand: typeof SqliteCompiler.BRAND = SqliteCompiler.BRAND;\n private readonly sqlSafety = new MigrationSqlSafetyAdapter('sqlite');\n\n /**\n * Narrow an unknown value to the SQLite migration compiler implementation.\n */\n static isSqliteCompiler(value: unknown): value is SqliteCompiler {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === SqliteCompiler.BRAND\n );\n }\n\n /**\n * Rewrite migration operations into SQLite's safe execution order,\n * including topological table creation and unique-column add expansion.\n */\n prepareOperations(operations: MigrationOperation[]): MigrationOperation[] {\n const tableCreates: TableCreate[] = [];\n const remainder: MigrationOperation[] = [];\n\n for (const operation of operations) {\n if (operation.kind === InternalOperationKind.TABLE_CREATE) {\n tableCreates.push(operation);\n } else {\n remainder.push(operation);\n }\n }\n\n const preparedRemainder = remainder.flatMap((operation) =>\n operation.kind === InternalOperationKind.COLUMN_ADD ? this.prepareColumnAdd(operation) : [operation]\n );\n\n return [...this.topologicalSortTableCreatesWithReferences(tableCreates), ...preparedRemainder];\n }\n\n /**\n * Compile a migration operation into one or more SQLite statements.\n */\n compile(op: MigrationOperation): SQL[] {\n switch (op.kind) {\n case InternalOperationKind.TABLE_CREATE: {\n const cols = op.columns.map((c) => this.colDDL(c));\n const pkCols = op.columns\n .filter((c) => c.primaryKey && c.type !== InternalColumnType.SERIAL)\n .map((c) => this.sqlSafety.column(c.name));\n\n if (pkCols.length) {\n cols.push(`PRIMARY KEY (${pkCols.join(', ')})`);\n }\n\n op.columns\n .filter((column) => column.references)\n .forEach((column) => {\n const references = column.references!;\n cols.push(\n `FOREIGN KEY (${this.sqlSafety.column(column.name)}) REFERENCES ${this.sqlSafety.table(references.table)}(${this.sqlSafety.column(references.column)})${references.onDelete ? ` ON DELETE ${references.onDelete}` : ''}${references.onUpdate ? ` ON UPDATE ${references.onUpdate}` : ''}`\n );\n });\n\n const sql = `CREATE TABLE ${this.sqlSafety.table(op.table)} (${cols.join(', ')})`;\n return [{ sql, params: [] }];\n }\n\n case InternalOperationKind.TABLE_DROP:\n return [{ sql: `DROP TABLE ${this.sqlSafety.table(op.table)}`, params: [] }];\n\n case InternalOperationKind.COLUMN_ADD:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} ADD COLUMN ${this.colDDL({\n ...op.column,\n unique: false,\n })}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.COLUMN_DROP:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} DROP COLUMN ${this.sqlSafety.column(op.column)}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.COLUMN_RENAME:\n return [\n {\n sql: `ALTER TABLE ${this.sqlSafety.table(op.table)} RENAME COLUMN ${this.sqlSafety.column(op.from)} TO ${this.sqlSafety.column(op.to)}`,\n params: [],\n },\n ];\n\n case InternalOperationKind.INDEX_CREATE: {\n const cols = this.sqlSafety.columns(op.on).join(', ');\n const uniq = op.unique ? 'UNIQUE ' : '';\n const where = this.sqlSafety.optionalRawFragment('where', op.where);\n return [\n {\n sql: `CREATE ${uniq}INDEX ${this.sqlSafety.index(op.name)} ON ${this.sqlSafety.table(op.table)} (${cols})${where ? ` WHERE ${where}` : ''}`,\n params: [],\n },\n ];\n }\n\n case InternalOperationKind.INDEX_DROP:\n return [{ sql: `DROP INDEX ${this.sqlSafety.index(op.name)}`, params: [] }];\n\n case InternalOperationKind.COLUMN_ALTER:\n case InternalOperationKind.FK_CREATE:\n case InternalOperationKind.FK_VALIDATE:\n case InternalOperationKind.FK_DROP:\n return [];\n\n default:\n return [];\n }\n }\n\n private colDDL(column: ColumnSpec): string {\n const parts: string[] = [this.sqlSafety.column(column.name)];\n\n switch (column.type) {\n case InternalColumnType.SERIAL:\n parts.push('INTEGER PRIMARY KEY AUTOINCREMENT');\n return parts.join(' ');\n case InternalColumnType.INT:\n parts.push('INTEGER');\n break;\n case InternalColumnType.BIGINT:\n parts.push('INTEGER');\n break;\n case InternalColumnType.TEXT:\n parts.push('TEXT');\n break;\n case InternalColumnType.BOOL:\n parts.push('INTEGER');\n break;\n case InternalColumnType.TIMESTAMPTZ:\n parts.push('TEXT');\n break;\n case InternalColumnType.JSONB:\n parts.push('TEXT');\n break;\n case InternalColumnType.UUID:\n parts.push('TEXT');\n break;\n }\n\n if (column.notNull) {\n parts.push('NOT NULL');\n }\n const defaultSql = this.sqlSafety.rawDefault(column.default, \"(datetime('now'))\");\n if (defaultSql) {\n parts.push(`DEFAULT ${defaultSql}`);\n }\n if (column.unique && !column.primaryKey) {\n parts.push('UNIQUE');\n }\n\n return parts.join(' ');\n }\n\n private prepareColumnAdd(op: ColumnAdd): MigrationOperation[] {\n const preparedColumn = op.column;\n if (preparedColumn.notNull && preparedColumn.default === undefined && !preparedColumn.primaryKey) {\n throw new Error(\n `SQLite cannot add NOT NULL column '${preparedColumn.name}' to '${op.table}' without a default or backfill path.`\n );\n }\n\n if (!preparedColumn.unique) {\n return [op];\n }\n\n const addColumn: ColumnAdd = {\n ...op,\n column: {\n ...preparedColumn,\n unique: false,\n },\n };\n const createIndex: IndexCreate = {\n kind: InternalOperationKind.INDEX_CREATE,\n name: `${op.table}_${preparedColumn.name}_idx`,\n table: op.table,\n on: [preparedColumn.name],\n unique: true,\n };\n\n return [addColumn, createIndex];\n }\n\n private topologicalSortTableCreatesWithReferences(creates: TableCreate[]): TableCreate[] {\n if (creates.length <= 1) {\n return creates;\n }\n\n const tableSet = new Set(creates.map((create) => create.table));\n const byTable = new Map(creates.map((create) => [create.table, create]));\n const incoming = new Map<string, number>();\n const dependents = new Map<string, Set<string>>();\n\n for (const table of tableSet) {\n incoming.set(table, 0);\n }\n\n for (const create of creates) {\n const seenParents = new Set<string>();\n for (const column of create.columns) {\n if (!column.references) {\n continue;\n }\n const refTable = column.references.table;\n if (refTable === create.table || !tableSet.has(refTable)) {\n continue;\n }\n if (seenParents.has(refTable)) {\n continue;\n }\n seenParents.add(refTable);\n incoming.set(create.table, incoming.get(create.table)! + 1);\n if (!dependents.has(refTable)) {\n dependents.set(refTable, new Set());\n }\n dependents.get(refTable)!.add(create.table);\n }\n }\n\n const ready = [...tableSet].filter((table) => incoming.get(table) === 0);\n ready.sort((left, right) => left.localeCompare(right));\n\n const sorted: TableCreate[] = [];\n while (ready.length) {\n const next = ready.shift()!;\n sorted.push(byTable.get(next)!);\n\n for (const dependent of dependents.get(next) ?? []) {\n incoming.set(dependent, incoming.get(dependent)! - 1);\n if (incoming.get(dependent) === 0) {\n ready.push(dependent);\n ready.sort((left, right) => left.localeCompare(right));\n }\n }\n }\n\n if (sorted.length !== creates.length) {\n return [...creates].sort((left, right) => left.table.localeCompare(right.table));\n }\n\n return sorted;\n }\n}\n","import type { CompilerFactory } from '../contracts/CompilerFactory';\nimport type { SQLCompiler } from '../contracts/SQLCompiler';\nimport { SqliteCompiler } from '../dialects/SqliteCompiler';\n\n/**\n * Factory for SQLite migration compilers.\n */\nexport class SqliteCompilerFactory implements CompilerFactory {\n static readonly BRAND = 'tango.migrations.sqlite_compiler_factory' as const;\n readonly __tangoBrand: typeof SqliteCompilerFactory.BRAND = SqliteCompilerFactory.BRAND;\n\n /**\n * Narrow an unknown value to the factory that provisions SQLite compilers.\n */\n static isSqliteCompilerFactory(value: unknown): value is SqliteCompilerFactory {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === SqliteCompilerFactory.BRAND\n );\n }\n\n /**\n * Create a SQLite SQL compiler instance.\n */\n create(): SQLCompiler {\n return new SqliteCompiler();\n }\n}\n"],"mappings":";;;;;;AAYA,IAAa,mBAAb,MAAa,iBAAwC;CACjD,OAAgB,QAAQ;CACxB,eAAuD,iBAAiB;CACxE,YAA6B,IAAI,0BAA0B,UAAU;;;;CAKrE,OAAO,mBAAmB,OAA2C;EACjE,OACI,OAAO,UAAU,YACjB,UAAU,QACT,MAAqC,iBAAiB,iBAAiB;CAEhF;;;;;CAMA,kBAAkB,YAAwD;EACtE,MAAM,eAA8B,CAAC;EACrC,MAAM,YAAkC,CAAC;EAEzC,KAAK,MAAM,aAAa,YACpB,IAAI,UAAU,SAAS,sBAAsB,cACzC,aAAa,KAAK,SAAS;OAE3B,UAAU,KAAK,SAAS;EAIhC,MAAM,kBAAiC,CAAC;EACxC,MAAM,cAAkC,CAAC;EAEzC,KAAK,MAAM,aAAa,cAAc;GAClC,MAAM,EAAE,QAAQ,QAAQ,KAAK,4BAA4B,SAAS;GAClE,gBAAgB,KAAK,MAAM;GAC3B,YAAY,KAAK,GAAG,GAAG;EAC3B;EAEA,OAAO;GACH,GAAG,gBAAgB,MAAM,MAAM,UAAU,KAAK,MAAM,cAAc,MAAM,KAAK,CAAC;GAC9E,GAAG;GACH,GAAG;EACP;CACJ;;;;CAKA,QAAQ,IAA+B;EACnC,QAAQ,GAAG,MAAX;GACI,KAAK,sBAAsB,cAAc;IACrC,MAAM,OAAO,GAAG,QAAQ,KAAK,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI;IAC5D,MAAM,SAAS,GAAG,QAAQ,QAAQ,MAAM,EAAE,UAAU,CAAC,CAAC,KAAK,MAAM,KAAK,UAAU,OAAO,EAAE,IAAI,CAAC;IAC9F,MAAM,cAAwB,CAAC;IAE/B,IAAI,OAAO,QACP,YAAY,KAAK,gBAAgB,OAAO,KAAK,IAAI,EAAE,EAAE;IAGzD,GAAG,QACE,QAAQ,WAAW,OAAO,UAAU,CAAC,CACrC,SAAS,WAAW;KACjB,MAAM,aAAa,OAAO;KAC1B,MAAM,SAAS,GAAG,GAAG,MAAM,GAAG,OAAO,KAAK;KAC1C,IAAI,KAAK,cAAc,KAAK,UAAU,WAAW,MAAM,EAAE,gBAAgB,KAAK,UAAU,OAAO,OAAO,IAAI,EAAE,eAAe,KAAK,UAAU,MAAM,WAAW,KAAK,EAAE,GAAG,KAAK,UAAU,OAAO,WAAW,MAAM,EAAE;KAC9M,IAAI,WAAW,UACX,MAAM,cAAc,WAAW;KAEnC,IAAI,WAAW,UACX,MAAM,cAAc,WAAW;KAEnC,YAAY,KAAK,EAAE;IACvB,CAAC;IAEL,MAAM,WAAW,CAAC,MAAM,GAAG,WAAW,CAAC,CAAC,KAAK,IAAI;IAEjD,OAAO,CAAC;KAAE,KAAA,gBADkB,KAAK,UAAU,MAAM,GAAG,KAAK,EAAE,IAAI,SAAS;KACzD,QAAQ,CAAC;IAAE,CAAC;GAC/B;GAEA,KAAK,sBAAsB,YACvB,OAAO,CACH;IAAE,KAAK,cAAc,KAAK,UAAU,MAAM,GAAG,KAAK,IAAI,GAAG,UAAU,aAAa;IAAM,QAAQ,CAAC;GAAE,CACrG;GAEJ,KAAK,sBAAsB,YACvB,OAAO,CACH;IACI,KAAK,eAAe,KAAK,UAAU,MAAM,GAAG,KAAK,EAAE,cAAc,KAAK,OAAO,GAAG,MAAM;IACtF,QAAQ,CAAC;GACb,CACJ;GAEJ,KAAK,sBAAsB,aACvB,OAAO,CACH;IACI,KAAK,eAAe,KAAK,UAAU,MAAM,GAAG,KAAK,EAAE,eAAe,KAAK,UAAU,OAAO,GAAG,MAAM;IACjG,QAAQ,CAAC;GACb,CACJ;GAEJ,KAAK,sBAAsB,cAAc;IACrC,MAAM,MAAa,CAAC;IACpB,IAAI,GAAG,GAAG,MACN,IAAI,KAAK;KACL,KAAK,eAAe,KAAK,UAAU,MAAM,GAAG,KAAK,EAAE,gBAAgB,KAAK,UAAU,OAAO,GAAG,MAAM,EAAE,QAAQ,KAAK,UAAU,GAAG,GAAG,IAAI;KACrI,QAAQ,CAAC;IACb,CAAC;IAEL,IAAI,GAAG,GAAG,YAAY,KAAA,GAClB,IAAI,KAAK;KACL,KAAK,eAAe,KAAK,UAAU,MAAM,GAAG,KAAK,EAAE,gBAAgB,KAAK,UAAU,OAAO,GAAG,MAAM,EAAE,GAAG,GAAG,GAAG,UAAU,iBAAiB;KACxI,QAAQ,CAAC;IACb,CAAC;IAEL,IAAI,KAAK,GAAG,KAAK,qBAAqB,GAAG,OAAO,GAAG,QAAQ,GAAG,GAAG,OAAO,CAAC;IACzE,OAAO;GACX;GAEA,KAAK,sBAAsB,eACvB,OAAO,CACH;IACI,KAAK,eAAe,KAAK,UAAU,MAAM,GAAG,KAAK,EAAE,iBAAiB,KAAK,UAAU,OAAO,GAAG,IAAI,EAAE,MAAM,KAAK,UAAU,OAAO,GAAG,EAAE;IACpI,QAAQ,CAAC;GACb,CACJ;GAEJ,KAAK,sBAAsB,cAAc;IACrC,MAAM,OAAO,KAAK,UAAU,QAAQ,GAAG,EAAE,CAAC,CAAC,KAAK,IAAI;IACpD,MAAM,OAAO,GAAG,SAAS,YAAY;IACrC,MAAM,OAAO,GAAG,eAAe,kBAAkB;IACjD,MAAM,QAAQ,KAAK,UAAU,oBAAoB,SAAS,GAAG,KAAK;IAClE,OAAO,CACH;KACI,KAAK,UAAU,KAAK,QAAQ,OAAO,KAAK,UAAU,MAAM,GAAG,IAAI,EAAE,MAAM,KAAK,UAAU,MAAM,GAAG,KAAK,EAAE,IAAI,KAAK,GAAG,QAAQ,UAAU,UAAU;KAC9I,QAAQ,CAAC;IACb,CACJ;GACJ;GAEA,KAAK,sBAAsB,YAEvB,OAAO,CAAC;IAAE,KAAK,cADF,GAAG,eAAe,kBAAkB,KACb,KAAK,UAAU,MAAM,GAAG,IAAI;IAAK,QAAQ,CAAC;GAAE,CAAC;GAGrF,KAAK,sBAAsB,WAAW;IAClC,MAAM,OAAO,KAAK,UAAU,QAAQ,GAAG,OAAO,CAAC,CAAC,KAAK,IAAI;IACzD,MAAM,OAAO,KAAK,UAAU,QAAQ,GAAG,UAAU,CAAC,CAAC,KAAK,IAAI;IAC5D,MAAM,OAAO,GAAG,QAAQ,GAAG,GAAG,MAAM,GAAG,GAAG,QAAQ,KAAK,GAAG,EAAE;IAC5D,MAAM,WAAW,GAAG,WAAW,eAAe;IAC9C,MAAM,QAAQ,GAAG,WAAW,cAAc,GAAG,aAAa;IAC1D,MAAM,QAAQ,GAAG,WAAW,cAAc,GAAG,aAAa;IAC1D,OAAO,CACH;KACI,KAAK,eAAe,KAAK,UAAU,MAAM,GAAG,KAAK,EAAE,kBAAkB,KAAK,UAAU,WAAW,IAAI,EAAE,gBAAgB,KAAK,eAAe,KAAK,UAAU,MAAM,GAAG,QAAQ,EAAE,IAAI,KAAK,GAAG,QAAQ,QAAQ;KACvM,QAAQ,CAAC;IACb,CACJ;GACJ;GAEA,KAAK,sBAAsB,aACvB,OAAO,CACH;IACI,KAAK,eAAe,KAAK,UAAU,MAAM,GAAG,KAAK,EAAE,uBAAuB,KAAK,UAAU,WAAW,GAAG,IAAI;IAC3G,QAAQ,CAAC;GACb,CACJ;GAEJ,KAAK,sBAAsB,SACvB,OAAO,CACH;IACI,KAAK,eAAe,KAAK,UAAU,MAAM,GAAG,KAAK,EAAE,mBAAmB,KAAK,UAAU,WAAW,GAAG,IAAI;IACvG,QAAQ,CAAC;GACb,CACJ;GAEJ,SACI,OAAO,CAAC;EAChB;CACJ;;;;;CAMA,qBAA6B,OAAe,QAAgB,cAA8B;EACtF,IAAI,iBAAiB,KAAA,GACjB,OAAO,CAAC;EAGZ,IAAI,iBAAiB,MACjB,OAAO,CACH;GACI,KAAK,eAAe,KAAK,UAAU,MAAM,KAAK,EAAE,gBAAgB,KAAK,UAAU,OAAO,MAAM,EAAE;GAC9F,QAAQ,CAAC;EACb,CACJ;EAGJ,IAAI,KAAK,UAAU,kBAAkB,YAAY,GAC7C,OAAO,CACH;GACI,KAAK,eAAe,KAAK,UAAU,MAAM,KAAK,EAAE,gBAAgB,KAAK,UAAU,OAAO,MAAM,EAAE,eAAe,KAAK,UAAU,YAAY,WAAW,YAAY;GAC/J,QAAQ,CAAC;EACb,CACJ;EAGJ,IACI,gBACA,OAAO,iBAAiB,YACxB,SAAS,gBACR,aAAmC,KAEpC,OAAO,CACH;GACI,KAAK,eAAe,KAAK,UAAU,MAAM,KAAK,EAAE,gBAAgB,KAAK,UAAU,OAAO,MAAM,EAAE;GAC9F,QAAQ,CAAC;EACb,CACJ;EAGJ,OAAO,CAAC;CACZ;CAEA,4BAAoC,IAAmE;EACnG,MAAM,MAA0B,CAAC;EACjC,MAAM,UAAU,GAAG,QAAQ,KAAK,WAAW;GACvC,IAAI,CAAC,OAAO,YACR,OAAO;GAGX,MAAM,aAAa,OAAO;GAC1B,IAAI,KAAK;IACL,MAAM,sBAAsB;IAC5B,OAAO,GAAG;IACV,SAAS,CAAC,OAAO,IAAI;IACrB,UAAU,WAAW;IACrB,YAAY,CAAC,WAAW,MAAM;IAC9B,UAAU,WAAW;IACrB,UAAU,WAAW;GACzB,CAAC;GAED,MAAM,EAAE,YAAY,aAAa,GAAG,SAAS;GAC7C,OAAO,EAAE,GAAG,KAAK;EACrB,CAAC;EAED,OAAO;GAAE,QAAQ;IAAE,GAAG;IAAI;GAAQ;GAAG;EAAI;CAC7C;CAEA,OAAe,QAA4B;EACvC,MAAM,QAAkB,CAAC,KAAK,UAAU,OAAO,OAAO,IAAI,CAAC;EAE3D,QAAQ,OAAO,MAAf;GACI,KAAK,mBAAmB;IACpB,MAAM,KAAK,QAAQ;IACnB;GACJ,KAAK,mBAAmB;IACpB,MAAM,KAAK,SAAS;IACpB;GACJ,KAAK,mBAAmB;IACpB,MAAM,KAAK,QAAQ;IACnB;GACJ,KAAK,mBAAmB;IACpB,MAAM,KAAK,MAAM;IACjB;GACJ,KAAK,mBAAmB;IACpB,MAAM,KAAK,SAAS;IACpB;GACJ,KAAK,mBAAmB;IACpB,MAAM,KAAK,aAAa;IACxB;GACJ,KAAK,mBAAmB;IACpB,MAAM,KAAK,OAAO;IAClB;GACJ,KAAK,mBAAmB;IACpB,MAAM,KAAK,MAAM;IACjB;EACR;EAEA,IAAI,OAAO,SACP,MAAM,KAAK,UAAU;EAEzB,MAAM,aAAa,KAAK,UAAU,WAAW,OAAO,SAAS,OAAO;EACpE,IAAI,YACA,MAAM,KAAK,WAAW,YAAY;EAEtC,IAAI,OAAO,UAAU,CAAC,OAAO,YACzB,MAAM,KAAK,QAAQ;EAGvB,OAAO,MAAM,KAAK,GAAG;CACzB;CAEA,UAAkB,MAA0B;EACxC,QAAQ,MAAR;GACI,KAAK,mBAAmB,QACpB,OAAO;GACX,KAAK,mBAAmB,KACpB,OAAO;GACX,KAAK,mBAAmB,QACpB,OAAO;GACX,KAAK,mBAAmB,MACpB,OAAO;GACX,KAAK,mBAAmB,MACpB,OAAO;GACX,KAAK,mBAAmB,aACpB,OAAO;GACX,KAAK,mBAAmB,OACpB,OAAO;GACX,KAAK,mBAAmB,MACpB,OAAO;GACX,SAEI,MAAM,IAAI,MAAM,4BAA4BA,MAAY;EAEhE;CACJ;AACJ;;;;;;ACrUA,IAAa,0BAAb,MAAa,wBAAmD;CAC5D,OAAgB,QAAQ;CACxB,eAA8D,wBAAwB;;;;CAKtF,OAAO,0BAA0B,OAAkD;EAC/E,OACI,OAAO,UAAU,YACjB,UAAU,QACT,MAAqC,iBAAiB,wBAAwB;CAEvF;;;;CAKA,SAAsB;EAClB,OAAO,IAAI,iBAAiB;CAChC;AACJ;;;;;;ACjBA,IAAa,iBAAb,MAAa,eAAsC;CAC/C,OAAgB,QAAQ;CACxB,eAAqD,eAAe;CACpE,YAA6B,IAAI,0BAA0B,QAAQ;;;;CAKnE,OAAO,iBAAiB,OAAyC;EAC7D,OACI,OAAO,UAAU,YACjB,UAAU,QACT,MAAqC,iBAAiB,eAAe;CAE9E;;;;;CAMA,kBAAkB,YAAwD;EACtE,MAAM,eAA8B,CAAC;EACrC,MAAM,YAAkC,CAAC;EAEzC,KAAK,MAAM,aAAa,YACpB,IAAI,UAAU,SAAS,sBAAsB,cACzC,aAAa,KAAK,SAAS;OAE3B,UAAU,KAAK,SAAS;EAIhC,MAAM,oBAAoB,UAAU,SAAS,cACzC,UAAU,SAAS,sBAAsB,aAAa,KAAK,iBAAiB,SAAS,IAAI,CAAC,SAAS,CACvG;EAEA,OAAO,CAAC,GAAG,KAAK,0CAA0C,YAAY,GAAG,GAAG,iBAAiB;CACjG;;;;CAKA,QAAQ,IAA+B;EACnC,QAAQ,GAAG,MAAX;GACI,KAAK,sBAAsB,cAAc;IACrC,MAAM,OAAO,GAAG,QAAQ,KAAK,MAAM,KAAK,OAAO,CAAC,CAAC;IACjD,MAAM,SAAS,GAAG,QACb,QAAQ,MAAM,EAAE,cAAc,EAAE,SAAS,mBAAmB,MAAM,CAAC,CACnE,KAAK,MAAM,KAAK,UAAU,OAAO,EAAE,IAAI,CAAC;IAE7C,IAAI,OAAO,QACP,KAAK,KAAK,gBAAgB,OAAO,KAAK,IAAI,EAAE,EAAE;IAGlD,GAAG,QACE,QAAQ,WAAW,OAAO,UAAU,CAAC,CACrC,SAAS,WAAW;KACjB,MAAM,aAAa,OAAO;KAC1B,KAAK,KACD,gBAAgB,KAAK,UAAU,OAAO,OAAO,IAAI,EAAE,eAAe,KAAK,UAAU,MAAM,WAAW,KAAK,EAAE,GAAG,KAAK,UAAU,OAAO,WAAW,MAAM,EAAE,GAAG,WAAW,WAAW,cAAc,WAAW,aAAa,KAAK,WAAW,WAAW,cAAc,WAAW,aAAa,IACzR;IACJ,CAAC;IAGL,OAAO,CAAC;KAAE,KAAA,gBADkB,KAAK,UAAU,MAAM,GAAG,KAAK,EAAE,IAAI,KAAK,KAAK,IAAI,EAAE;KAChE,QAAQ,CAAC;IAAE,CAAC;GAC/B;GAEA,KAAK,sBAAsB,YACvB,OAAO,CAAC;IAAE,KAAK,cAAc,KAAK,UAAU,MAAM,GAAG,KAAK;IAAK,QAAQ,CAAC;GAAE,CAAC;GAE/E,KAAK,sBAAsB,YACvB,OAAO,CACH;IACI,KAAK,eAAe,KAAK,UAAU,MAAM,GAAG,KAAK,EAAE,cAAc,KAAK,OAAO;KACzE,GAAG,GAAG;KACN,QAAQ;IACZ,CAAC;IACD,QAAQ,CAAC;GACb,CACJ;GAEJ,KAAK,sBAAsB,aACvB,OAAO,CACH;IACI,KAAK,eAAe,KAAK,UAAU,MAAM,GAAG,KAAK,EAAE,eAAe,KAAK,UAAU,OAAO,GAAG,MAAM;IACjG,QAAQ,CAAC;GACb,CACJ;GAEJ,KAAK,sBAAsB,eACvB,OAAO,CACH;IACI,KAAK,eAAe,KAAK,UAAU,MAAM,GAAG,KAAK,EAAE,iBAAiB,KAAK,UAAU,OAAO,GAAG,IAAI,EAAE,MAAM,KAAK,UAAU,OAAO,GAAG,EAAE;IACpI,QAAQ,CAAC;GACb,CACJ;GAEJ,KAAK,sBAAsB,cAAc;IACrC,MAAM,OAAO,KAAK,UAAU,QAAQ,GAAG,EAAE,CAAC,CAAC,KAAK,IAAI;IACpD,MAAM,OAAO,GAAG,SAAS,YAAY;IACrC,MAAM,QAAQ,KAAK,UAAU,oBAAoB,SAAS,GAAG,KAAK;IAClE,OAAO,CACH;KACI,KAAK,UAAU,KAAK,QAAQ,KAAK,UAAU,MAAM,GAAG,IAAI,EAAE,MAAM,KAAK,UAAU,MAAM,GAAG,KAAK,EAAE,IAAI,KAAK,GAAG,QAAQ,UAAU,UAAU;KACvI,QAAQ,CAAC;IACb,CACJ;GACJ;GAEA,KAAK,sBAAsB,YACvB,OAAO,CAAC;IAAE,KAAK,cAAc,KAAK,UAAU,MAAM,GAAG,IAAI;IAAK,QAAQ,CAAC;GAAE,CAAC;GAE9E,KAAK,sBAAsB;GAC3B,KAAK,sBAAsB;GAC3B,KAAK,sBAAsB;GAC3B,KAAK,sBAAsB,SACvB,OAAO,CAAC;GAEZ,SACI,OAAO,CAAC;EAChB;CACJ;CAEA,OAAe,QAA4B;EACvC,MAAM,QAAkB,CAAC,KAAK,UAAU,OAAO,OAAO,IAAI,CAAC;EAE3D,QAAQ,OAAO,MAAf;GACI,KAAK,mBAAmB;IACpB,MAAM,KAAK,mCAAmC;IAC9C,OAAO,MAAM,KAAK,GAAG;GACzB,KAAK,mBAAmB;IACpB,MAAM,KAAK,SAAS;IACpB;GACJ,KAAK,mBAAmB;IACpB,MAAM,KAAK,SAAS;IACpB;GACJ,KAAK,mBAAmB;IACpB,MAAM,KAAK,MAAM;IACjB;GACJ,KAAK,mBAAmB;IACpB,MAAM,KAAK,SAAS;IACpB;GACJ,KAAK,mBAAmB;IACpB,MAAM,KAAK,MAAM;IACjB;GACJ,KAAK,mBAAmB;IACpB,MAAM,KAAK,MAAM;IACjB;GACJ,KAAK,mBAAmB;IACpB,MAAM,KAAK,MAAM;IACjB;EACR;EAEA,IAAI,OAAO,SACP,MAAM,KAAK,UAAU;EAEzB,MAAM,aAAa,KAAK,UAAU,WAAW,OAAO,SAAS,mBAAmB;EAChF,IAAI,YACA,MAAM,KAAK,WAAW,YAAY;EAEtC,IAAI,OAAO,UAAU,CAAC,OAAO,YACzB,MAAM,KAAK,QAAQ;EAGvB,OAAO,MAAM,KAAK,GAAG;CACzB;CAEA,iBAAyB,IAAqC;EAC1D,MAAM,iBAAiB,GAAG;EAC1B,IAAI,eAAe,WAAW,eAAe,YAAY,KAAA,KAAa,CAAC,eAAe,YAClF,MAAM,IAAI,MACN,sCAAsC,eAAe,KAAK,QAAQ,GAAG,MAAM,sCAC/E;EAGJ,IAAI,CAAC,eAAe,QAChB,OAAO,CAAC,EAAE;EAkBd,OAAO,CAAC;GAdJ,GAAG;GACH,QAAQ;IACJ,GAAG;IACH,QAAQ;GACZ;EAUY,GAAG;GAPf,MAAM,sBAAsB;GAC5B,MAAM,GAAG,GAAG,MAAM,GAAG,eAAe,KAAK;GACzC,OAAO,GAAG;GACV,IAAI,CAAC,eAAe,IAAI;GACxB,QAAQ;EAGiB,CAAC;CAClC;CAEA,0CAAkD,SAAuC;EACrF,IAAI,QAAQ,UAAU,GAClB,OAAO;EAGX,MAAM,WAAW,IAAI,IAAI,QAAQ,KAAK,WAAW,OAAO,KAAK,CAAC;EAC9D,MAAM,UAAU,IAAI,IAAI,QAAQ,KAAK,WAAW,CAAC,OAAO,OAAO,MAAM,CAAC,CAAC;EACvE,MAAM,2BAAW,IAAI,IAAoB;EACzC,MAAM,6BAAa,IAAI,IAAyB;EAEhD,KAAK,MAAM,SAAS,UAChB,SAAS,IAAI,OAAO,CAAC;EAGzB,KAAK,MAAM,UAAU,SAAS;GAC1B,MAAM,8BAAc,IAAI,IAAY;GACpC,KAAK,MAAM,UAAU,OAAO,SAAS;IACjC,IAAI,CAAC,OAAO,YACR;IAEJ,MAAM,WAAW,OAAO,WAAW;IACnC,IAAI,aAAa,OAAO,SAAS,CAAC,SAAS,IAAI,QAAQ,GACnD;IAEJ,IAAI,YAAY,IAAI,QAAQ,GACxB;IAEJ,YAAY,IAAI,QAAQ;IACxB,SAAS,IAAI,OAAO,OAAO,SAAS,IAAI,OAAO,KAAK,IAAK,CAAC;IAC1D,IAAI,CAAC,WAAW,IAAI,QAAQ,GACxB,WAAW,IAAI,0BAAU,IAAI,IAAI,CAAC;IAEtC,WAAW,IAAI,QAAQ,CAAC,CAAE,IAAI,OAAO,KAAK;GAC9C;EACJ;EAEA,MAAM,QAAQ,CAAC,GAAG,QAAQ,CAAC,CAAC,QAAQ,UAAU,SAAS,IAAI,KAAK,MAAM,CAAC;EACvE,MAAM,MAAM,MAAM,UAAU,KAAK,cAAc,KAAK,CAAC;EAErD,MAAM,SAAwB,CAAC;EAC/B,OAAO,MAAM,QAAQ;GACjB,MAAM,OAAO,MAAM,MAAM;GACzB,OAAO,KAAK,QAAQ,IAAI,IAAI,CAAE;GAE9B,KAAK,MAAM,aAAa,WAAW,IAAI,IAAI,KAAK,CAAC,GAAG;IAChD,SAAS,IAAI,WAAW,SAAS,IAAI,SAAS,IAAK,CAAC;IACpD,IAAI,SAAS,IAAI,SAAS,MAAM,GAAG;KAC/B,MAAM,KAAK,SAAS;KACpB,MAAM,MAAM,MAAM,UAAU,KAAK,cAAc,KAAK,CAAC;IACzD;GACJ;EACJ;EAEA,IAAI,OAAO,WAAW,QAAQ,QAC1B,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,MAAM,MAAM,UAAU,KAAK,MAAM,cAAc,MAAM,KAAK,CAAC;EAGnF,OAAO;CACX;AACJ;;;;;;ACpQA,IAAa,wBAAb,MAAa,sBAAiD;CAC1D,OAAgB,QAAQ;CACxB,eAA4D,sBAAsB;;;;CAKlF,OAAO,wBAAwB,OAAgD;EAC3E,OACI,OAAO,UAAU,YACjB,UAAU,QACT,MAAqC,iBAAiB,sBAAsB;CAErF;;;;CAKA,SAAsB;EAClB,OAAO,IAAI,eAAe;CAC9B;AACJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SqliteIntrospector-CfItmGgA.js","names":[],"sources":["../src/introspect/PostgresIntrospector.ts","../src/introspect/SqliteIntrospector.ts"],"sourcesContent":["import type { DBClient, DatabaseIntrospector } from './DatabaseIntrospector';\n\n/** Introspected column metadata. */\nexport interface DbColumn {\n name: string;\n type: string;\n notNull: boolean;\n default: string | null;\n isPk: boolean;\n isUnique: boolean;\n}\n\n/** Introspected index metadata. */\nexport interface DbIndex {\n name: string;\n table: string;\n unique: boolean;\n columns: string[];\n where: string | null;\n}\n\n/** Introspected foreign key metadata. */\nexport interface DbForeignKey {\n name: string;\n table: string;\n columns: string[];\n refTable: string;\n refColumns: string[];\n onDelete: string | null;\n onUpdate: string | null;\n validated: boolean;\n}\n\n/** Introspected table metadata. */\nexport interface DbTable {\n name: string;\n columns: Record<string, DbColumn>;\n pks: string[];\n indexes: Record<string, DbIndex>;\n fks: Record<string, DbForeignKey>;\n}\n\n/** Introspected schema metadata. */\nexport interface DbSchema {\n tables: Record<string, DbTable>;\n}\n\n/**\n * PostgreSQL implementation of schema introspection.\n */\nexport class PostgresIntrospector implements DatabaseIntrospector {\n static readonly BRAND = 'tango.migrations.postgres_introspector' as const;\n readonly __tangoBrand: typeof PostgresIntrospector.BRAND = PostgresIntrospector.BRAND;\n\n /**\n * Narrow an unknown value to the PostgreSQL schema introspector.\n */\n static isPostgresIntrospector(value: unknown): value is PostgresIntrospector {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === PostgresIntrospector.BRAND\n );\n }\n\n /**\n * Read table and column metadata from PostgreSQL system catalogs.\n */\n async introspect(client: DBClient): Promise<DbSchema> {\n const schema: DbSchema = { tables: {} };\n\n const tablesRes = await client.query<{ tbl_oid: string; table: string }>(`\n SELECT c.oid AS tbl_oid, c.relname AS table\n FROM pg_class c\n JOIN pg_namespace n ON n.oid = c.relnamespace\n WHERE c.relkind = 'r' AND n.nspname NOT IN ('pg_catalog','information_schema')\n `);\n\n await Promise.all(\n tablesRes.rows.map(async (tableRow) => {\n const table = tableRow.table as string;\n\n const colsRes = await client.query<{\n name: string;\n type: string;\n not_null: boolean;\n default_expr: string | null;\n }>(`\n SELECT a.attname AS name,\n pg_catalog.format_type(a.atttypid,a.atttypmod) AS type,\n a.attnotnull AS not_null,\n pg_get_expr(ad.adbin, ad.adrelid) AS default_expr\n FROM pg_attribute a\n LEFT JOIN pg_attrdef ad ON ad.adrelid = a.attrelid AND ad.adnum = a.attnum\n WHERE a.attrelid = '${tableRow.tbl_oid}'::oid AND a.attnum > 0 AND NOT a.attisdropped\n ORDER BY a.attnum\n `);\n\n const pkRes = await client.query<{ col: string }>(`\n SELECT a.attname AS col\n FROM pg_index i\n JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)\n WHERE i.indrelid = '${tableRow.tbl_oid}'::oid AND i.indisprimary\n `);\n const pks = pkRes.rows.map((pkRow) => pkRow.col as string);\n\n const idxRes = await client.query<{\n name: string;\n unique: boolean;\n where_clause: string | null;\n columns: string[];\n }>(`\n SELECT\n idx.relname AS name,\n i.indisunique AS unique,\n pg_get_expr(i.indpred, i.indrelid) AS where_clause,\n ARRAY(\n SELECT a.attname\n FROM unnest(i.indkey) WITH ORDINALITY AS k(attnum, ord)\n JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = k.attnum\n ORDER BY k.ord\n ) AS columns\n FROM pg_index i\n JOIN pg_class idx ON idx.oid = i.indexrelid\n LEFT JOIN pg_constraint con ON con.conindid = i.indexrelid\n WHERE i.indrelid = '${tableRow.tbl_oid}'::oid\n AND NOT i.indisprimary\n AND con.oid IS NULL\n `);\n\n const indexes = idxRes.rows.reduce<Record<string, DbIndex>>((accumulator, indexRow) => {\n const name = String(indexRow.name);\n accumulator[name] = {\n name,\n table,\n unique: !!indexRow.unique,\n columns: Array.isArray(indexRow.columns) ? indexRow.columns.map(String) : [],\n where: indexRow.where_clause ? String(indexRow.where_clause) : null,\n };\n return accumulator;\n }, {});\n\n const columns = colsRes.rows.reduce<Record<string, DbColumn>>((accumulator, columnRow) => {\n const name = columnRow.name as string;\n const isPk = pks.includes(name);\n accumulator[name] = {\n name,\n type: String(columnRow.type),\n notNull: !!columnRow.not_null,\n default: columnRow.default_expr ? String(columnRow.default_expr) : null,\n isPk,\n isUnique: false,\n };\n return accumulator;\n }, {});\n\n schema.tables[table] = {\n name: table,\n columns,\n pks,\n indexes,\n fks: {},\n };\n })\n );\n\n return schema;\n }\n}\n","import type { DBClient, DatabaseIntrospector } from './DatabaseIntrospector';\nimport { MigrationSqlSafetyAdapter } from '../internal/MigrationSqlSafetyAdapter';\n\n/** Introspected column metadata. */\nexport interface DbColumn {\n name: string;\n type: string;\n notNull: boolean;\n default: string | null;\n isPk: boolean;\n isUnique: boolean;\n}\n\n/** Introspected table metadata. */\nexport interface DbTable {\n name: string;\n columns: Record<string, DbColumn>;\n pks: string[];\n indexes: Record<string, { name: string; table: string; unique: boolean; columns: string[]; where: string | null }>;\n fks: Record<\n string,\n {\n name: string;\n table: string;\n columns: string[];\n refTable: string;\n refColumns: string[];\n onDelete: string | null;\n onUpdate: string | null;\n validated: boolean;\n }\n >;\n}\n\n/** Introspected schema metadata. */\nexport interface DbSchema {\n tables: Record<string, DbTable>;\n}\n\n/**\n * SQLite implementation of schema introspection.\n */\nexport class SqliteIntrospector implements DatabaseIntrospector {\n static readonly BRAND = 'tango.migrations.sqlite_introspector' as const;\n readonly __tangoBrand: typeof SqliteIntrospector.BRAND = SqliteIntrospector.BRAND;\n private readonly sqlSafety = new MigrationSqlSafetyAdapter('sqlite');\n\n /**\n * Narrow an unknown value to the SQLite schema introspector.\n */\n static isSqliteIntrospector(value: unknown): value is SqliteIntrospector {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === SqliteIntrospector.BRAND\n );\n }\n\n /**\n * Read table/column/index metadata from SQLite pragmas.\n */\n async introspect(client: DBClient): Promise<DbSchema> {\n const schema: DbSchema = { tables: {} };\n\n const tablesRes = await client.query<{ name: string }>(`\n SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'\n `);\n\n for (const tableRow of tablesRes.rows) {\n const table = tableRow.name as string;\n const safeTable = this.sqlSafety.table(table);\n\n const colsRes = await client.query<{\n name: string;\n pk: number;\n type: string;\n notnull: number;\n dflt_value: string | null;\n }>(`PRAGMA table_info(${safeTable})`);\n\n const columns: Record<string, DbColumn> = {};\n const pks: string[] = [];\n const indexes: Record<\n string,\n { name: string; table: string; unique: boolean; columns: string[]; where: string | null }\n > = {};\n\n for (const columnRow of colsRes.rows) {\n const name = columnRow.name as string;\n const isPk = columnRow.pk === 1;\n if (isPk) {\n pks.push(name);\n }\n\n columns[name] = {\n name,\n type: String(columnRow.type),\n notNull: columnRow.notnull === 1,\n default: columnRow.dflt_value || null,\n isPk,\n isUnique: false,\n };\n }\n\n const indexListRes = await client.query<{ name: string; unique: number }>(\n `PRAGMA index_list(${safeTable})`\n );\n for (const indexRow of indexListRes.rows) {\n const name = String(indexRow.name);\n if (name.startsWith('sqlite_autoindex_')) {\n continue;\n }\n\n const indexInfoRes = await client.query<{ name: string }>(\n `PRAGMA index_info(${this.sqlSafety.index(name)})`\n );\n const on = indexInfoRes.rows.map((infoRow) => String(infoRow.name)).filter(Boolean);\n indexes[name] = {\n name,\n table,\n columns: on,\n unique: indexRow.unique === 1,\n where: null,\n };\n }\n\n schema.tables[table] = {\n name: table,\n columns,\n pks,\n indexes,\n fks: {},\n };\n }\n\n return schema;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SqliteIntrospector-CfItmGgA.js","names":[],"sources":["../src/introspect/PostgresIntrospector.ts","../src/introspect/SqliteIntrospector.ts"],"sourcesContent":["import type { DBClient, DatabaseIntrospector } from './DatabaseIntrospector';\n\n/** Introspected column metadata. */\nexport interface DbColumn {\n name: string;\n type: string;\n notNull: boolean;\n default: string | null;\n isPk: boolean;\n isUnique: boolean;\n}\n\n/** Introspected index metadata. */\nexport interface DbIndex {\n name: string;\n table: string;\n unique: boolean;\n columns: string[];\n where: string | null;\n}\n\n/** Introspected foreign key metadata. */\nexport interface DbForeignKey {\n name: string;\n table: string;\n columns: string[];\n refTable: string;\n refColumns: string[];\n onDelete: string | null;\n onUpdate: string | null;\n validated: boolean;\n}\n\n/** Introspected table metadata. */\nexport interface DbTable {\n name: string;\n columns: Record<string, DbColumn>;\n pks: string[];\n indexes: Record<string, DbIndex>;\n fks: Record<string, DbForeignKey>;\n}\n\n/** Introspected schema metadata. */\nexport interface DbSchema {\n tables: Record<string, DbTable>;\n}\n\n/**\n * PostgreSQL implementation of schema introspection.\n */\nexport class PostgresIntrospector implements DatabaseIntrospector {\n static readonly BRAND = 'tango.migrations.postgres_introspector' as const;\n readonly __tangoBrand: typeof PostgresIntrospector.BRAND = PostgresIntrospector.BRAND;\n\n /**\n * Narrow an unknown value to the PostgreSQL schema introspector.\n */\n static isPostgresIntrospector(value: unknown): value is PostgresIntrospector {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === PostgresIntrospector.BRAND\n );\n }\n\n /**\n * Read table and column metadata from PostgreSQL system catalogs.\n */\n async introspect(client: DBClient): Promise<DbSchema> {\n const schema: DbSchema = { tables: {} };\n\n const tablesRes = await client.query<{ tbl_oid: string; table: string }>(`\n SELECT c.oid AS tbl_oid, c.relname AS table\n FROM pg_class c\n JOIN pg_namespace n ON n.oid = c.relnamespace\n WHERE c.relkind = 'r' AND n.nspname NOT IN ('pg_catalog','information_schema')\n `);\n\n await Promise.all(\n tablesRes.rows.map(async (tableRow) => {\n const table = tableRow.table as string;\n\n const colsRes = await client.query<{\n name: string;\n type: string;\n not_null: boolean;\n default_expr: string | null;\n }>(`\n SELECT a.attname AS name,\n pg_catalog.format_type(a.atttypid,a.atttypmod) AS type,\n a.attnotnull AS not_null,\n pg_get_expr(ad.adbin, ad.adrelid) AS default_expr\n FROM pg_attribute a\n LEFT JOIN pg_attrdef ad ON ad.adrelid = a.attrelid AND ad.adnum = a.attnum\n WHERE a.attrelid = '${tableRow.tbl_oid}'::oid AND a.attnum > 0 AND NOT a.attisdropped\n ORDER BY a.attnum\n `);\n\n const pkRes = await client.query<{ col: string }>(`\n SELECT a.attname AS col\n FROM pg_index i\n JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)\n WHERE i.indrelid = '${tableRow.tbl_oid}'::oid AND i.indisprimary\n `);\n const pks = pkRes.rows.map((pkRow) => pkRow.col as string);\n\n const idxRes = await client.query<{\n name: string;\n unique: boolean;\n where_clause: string | null;\n columns: string[];\n }>(`\n SELECT\n idx.relname AS name,\n i.indisunique AS unique,\n pg_get_expr(i.indpred, i.indrelid) AS where_clause,\n ARRAY(\n SELECT a.attname\n FROM unnest(i.indkey) WITH ORDINALITY AS k(attnum, ord)\n JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = k.attnum\n ORDER BY k.ord\n ) AS columns\n FROM pg_index i\n JOIN pg_class idx ON idx.oid = i.indexrelid\n LEFT JOIN pg_constraint con ON con.conindid = i.indexrelid\n WHERE i.indrelid = '${tableRow.tbl_oid}'::oid\n AND NOT i.indisprimary\n AND con.oid IS NULL\n `);\n\n const indexes = idxRes.rows.reduce<Record<string, DbIndex>>((accumulator, indexRow) => {\n const name = String(indexRow.name);\n accumulator[name] = {\n name,\n table,\n unique: !!indexRow.unique,\n columns: Array.isArray(indexRow.columns) ? indexRow.columns.map(String) : [],\n where: indexRow.where_clause ? String(indexRow.where_clause) : null,\n };\n return accumulator;\n }, {});\n\n const columns = colsRes.rows.reduce<Record<string, DbColumn>>((accumulator, columnRow) => {\n const name = columnRow.name as string;\n const isPk = pks.includes(name);\n accumulator[name] = {\n name,\n type: String(columnRow.type),\n notNull: !!columnRow.not_null,\n default: columnRow.default_expr ? String(columnRow.default_expr) : null,\n isPk,\n isUnique: false,\n };\n return accumulator;\n }, {});\n\n schema.tables[table] = {\n name: table,\n columns,\n pks,\n indexes,\n fks: {},\n };\n })\n );\n\n return schema;\n }\n}\n","import type { DBClient, DatabaseIntrospector } from './DatabaseIntrospector';\nimport { MigrationSqlSafetyAdapter } from '../internal/MigrationSqlSafetyAdapter';\n\n/** Introspected column metadata. */\nexport interface DbColumn {\n name: string;\n type: string;\n notNull: boolean;\n default: string | null;\n isPk: boolean;\n isUnique: boolean;\n}\n\n/** Introspected table metadata. */\nexport interface DbTable {\n name: string;\n columns: Record<string, DbColumn>;\n pks: string[];\n indexes: Record<string, { name: string; table: string; unique: boolean; columns: string[]; where: string | null }>;\n fks: Record<\n string,\n {\n name: string;\n table: string;\n columns: string[];\n refTable: string;\n refColumns: string[];\n onDelete: string | null;\n onUpdate: string | null;\n validated: boolean;\n }\n >;\n}\n\n/** Introspected schema metadata. */\nexport interface DbSchema {\n tables: Record<string, DbTable>;\n}\n\n/**\n * SQLite implementation of schema introspection.\n */\nexport class SqliteIntrospector implements DatabaseIntrospector {\n static readonly BRAND = 'tango.migrations.sqlite_introspector' as const;\n readonly __tangoBrand: typeof SqliteIntrospector.BRAND = SqliteIntrospector.BRAND;\n private readonly sqlSafety = new MigrationSqlSafetyAdapter('sqlite');\n\n /**\n * Narrow an unknown value to the SQLite schema introspector.\n */\n static isSqliteIntrospector(value: unknown): value is SqliteIntrospector {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === SqliteIntrospector.BRAND\n );\n }\n\n /**\n * Read table/column/index metadata from SQLite pragmas.\n */\n async introspect(client: DBClient): Promise<DbSchema> {\n const schema: DbSchema = { tables: {} };\n\n const tablesRes = await client.query<{ name: string }>(`\n SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'\n `);\n\n for (const tableRow of tablesRes.rows) {\n const table = tableRow.name as string;\n const safeTable = this.sqlSafety.table(table);\n\n const colsRes = await client.query<{\n name: string;\n pk: number;\n type: string;\n notnull: number;\n dflt_value: string | null;\n }>(`PRAGMA table_info(${safeTable})`);\n\n const columns: Record<string, DbColumn> = {};\n const pks: string[] = [];\n const indexes: Record<\n string,\n { name: string; table: string; unique: boolean; columns: string[]; where: string | null }\n > = {};\n\n for (const columnRow of colsRes.rows) {\n const name = columnRow.name as string;\n const isPk = columnRow.pk === 1;\n if (isPk) {\n pks.push(name);\n }\n\n columns[name] = {\n name,\n type: String(columnRow.type),\n notNull: columnRow.notnull === 1,\n default: columnRow.dflt_value || null,\n isPk,\n isUnique: false,\n };\n }\n\n const indexListRes = await client.query<{ name: string; unique: number }>(\n `PRAGMA index_list(${safeTable})`\n );\n for (const indexRow of indexListRes.rows) {\n const name = String(indexRow.name);\n if (name.startsWith('sqlite_autoindex_')) {\n continue;\n }\n\n const indexInfoRes = await client.query<{ name: string }>(\n `PRAGMA index_info(${this.sqlSafety.index(name)})`\n );\n const on = indexInfoRes.rows.map((infoRow) => String(infoRow.name)).filter(Boolean);\n indexes[name] = {\n name,\n table,\n columns: on,\n unique: indexRow.unique === 1,\n where: null,\n };\n }\n\n schema.tables[table] = {\n name: table,\n columns,\n pks,\n indexes,\n fks: {},\n };\n }\n\n return schema;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder-Bhm_to6b.js","names":["exhaustive"],"sources":["../src/builder/contracts/index.ts","../src/builder/ops/OpBuilder.ts","../src/builder/ops/index.ts","../src/builder/runtime/index.ts","../src/builder/index.ts"],"sourcesContent":["/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport type { Builder } from './Builder';\nexport type { ColumnSpec } from './ColumnSpec';\nexport type { ColumnType } from './ColumnType';\nexport type { DeleteReferentialAction } from './DeleteReferentialAction';\nexport type { UpdateReferentialAction } from './UpdateReferentialAction';\n","import type { TrustedSqlFragment } from '@danceroutine/tango-core';\nimport type {\n CustomMigrationOperation,\n ForeignKeyCreate,\n ForeignKeyDrop,\n ForeignKeyValidate,\n IndexCreate,\n IndexDrop,\n TableCreate,\n TableDrop,\n ColumnAdd,\n ColumnDrop,\n ColumnAlter,\n ColumnRename,\n} from '../../domain/MigrationOperation';\nimport type { ColumnSpec } from '../contracts/ColumnSpec';\nimport type { ColumnType } from '../contracts/ColumnType';\nimport { InternalColumnType } from '../../domain/internal/InternalColumnType';\nimport type { DeleteReferentialAction } from '../contracts/DeleteReferentialAction';\nimport type { UpdateReferentialAction } from '../contracts/UpdateReferentialAction';\nimport { InternalOperationKind } from '../../domain/internal/InternalOperationKind';\n\n/**\n * Fluent builder for column specifications used by table operations.\n */\nclass ColumnBuilder {\n static readonly BRAND = 'tango.migrations.column_builder' as const;\n readonly __tangoBrand: typeof ColumnBuilder.BRAND = ColumnBuilder.BRAND;\n private spec: Partial<ColumnSpec> = {};\n\n constructor(name: string) {\n this.spec.name = name;\n }\n\n static isColumnBuilder(value: unknown): value is ColumnBuilder {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === ColumnBuilder.BRAND\n );\n }\n\n /** Set column type to serial/auto-increment. */\n serial(): ColumnBuilder {\n this.spec.type = InternalColumnType.SERIAL;\n return this;\n }\n /** Set column type to integer. */\n int(): ColumnBuilder {\n this.spec.type = InternalColumnType.INT;\n return this;\n }\n /** Set column type to bigint. */\n bigint(): ColumnBuilder {\n this.spec.type = InternalColumnType.BIGINT;\n return this;\n }\n /** Set column type to text. */\n text(): ColumnBuilder {\n this.spec.type = InternalColumnType.TEXT;\n return this;\n }\n /** Set column type to boolean. */\n bool(): ColumnBuilder {\n this.spec.type = InternalColumnType.BOOL;\n return this;\n }\n /** Set column type to timestamptz. */\n timestamptz(): ColumnBuilder {\n this.spec.type = InternalColumnType.TIMESTAMPTZ;\n return this;\n }\n /** Set column type to JSONB. */\n jsonb(): ColumnBuilder {\n this.spec.type = InternalColumnType.JSONB;\n return this;\n }\n /** Set column type to UUID. */\n uuid(): ColumnBuilder {\n this.spec.type = InternalColumnType.UUID;\n return this;\n }\n\n /** Mark column as NOT NULL. */\n notNull(): ColumnBuilder {\n this.spec.notNull = true;\n return this;\n }\n /** Set default to current timestamp. */\n defaultNow(): ColumnBuilder {\n this.spec.default = { now: true };\n return this;\n }\n /** Set reviewed raw SQL default expression. */\n default(v: TrustedSqlFragment | null): ColumnBuilder {\n this.spec.default = v;\n return this;\n }\n /** Mark column as part of primary key. */\n primaryKey(): ColumnBuilder {\n this.spec.primaryKey = true;\n return this;\n }\n /** Mark column as unique. */\n unique(): ColumnBuilder {\n this.spec.unique = true;\n return this;\n }\n /** Configure foreign key reference metadata. */\n references(\n table: string,\n column: string,\n opts?: {\n onDelete?: DeleteReferentialAction;\n onUpdate?: UpdateReferentialAction;\n }\n ): ColumnBuilder {\n this.spec.references = {\n table,\n column,\n onDelete: opts?.onDelete,\n onUpdate: opts?.onUpdate,\n };\n return this;\n }\n\n _done(): ColumnSpec {\n return this.spec as ColumnSpec;\n }\n}\n\ntype TableOperationBuilder = {\n create(def: (cols: { add: (name: string, cb: (b: ColumnBuilder) => ColumnBuilder) => void }) => void): TableCreate;\n drop(opts?: { cascade?: boolean }): TableDrop;\n addColumn(name: string, cb: (b: ColumnBuilder) => ColumnBuilder): ColumnAdd;\n dropColumn(name: string): ColumnDrop;\n alterColumn(name: string, to: Partial<ColumnSpec>): ColumnAlter;\n renameColumn(from: string, to: string): ColumnRename;\n};\n\n/**\n * Static factory for migration operations.\n */\nexport class OpBuilder {\n static readonly BRAND = 'tango.migrations.op_builder' as const;\n static index = {\n /** Build an index create operation. */\n create(p: {\n name: string;\n table: string;\n on: string[];\n unique?: boolean;\n where?: TrustedSqlFragment;\n concurrently?: boolean;\n }): IndexCreate {\n return { kind: InternalOperationKind.INDEX_CREATE, ...p };\n },\n /** Build an index drop operation. */\n drop(p: { name: string; table: string; concurrently?: boolean }): IndexDrop {\n return { kind: InternalOperationKind.INDEX_DROP, ...p };\n },\n };\n private static customOperations = new Map<string, (args: Record<string, unknown>) => CustomMigrationOperation>();\n readonly __tangoBrand: typeof OpBuilder.BRAND = OpBuilder.BRAND;\n\n /**\n * Narrow an unknown value to the shared migration operation builder type.\n */\n static isOpBuilder(value: unknown): value is OpBuilder {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === OpBuilder.BRAND\n );\n }\n\n /**\n * Build table-scoped migration operations.\n */\n static table(table: string): TableOperationBuilder {\n return {\n create(\n def: (cols: { add: (name: string, cb: (b: ColumnBuilder) => ColumnBuilder) => void }) => void\n ): TableCreate {\n const columns: ColumnSpec[] = [];\n def({\n add(name, cb) {\n columns.push(cb(new ColumnBuilder(name))._done());\n },\n });\n return { kind: InternalOperationKind.TABLE_CREATE, table, columns };\n },\n drop(opts?: { cascade?: boolean }): TableDrop {\n return { kind: InternalOperationKind.TABLE_DROP, table, cascade: opts?.cascade };\n },\n addColumn(name: string, cb: (b: ColumnBuilder) => ColumnBuilder): ColumnAdd {\n return { kind: InternalOperationKind.COLUMN_ADD, table, column: cb(new ColumnBuilder(name))._done() };\n },\n dropColumn(name: string): ColumnDrop {\n return { kind: InternalOperationKind.COLUMN_DROP, table, column: name };\n },\n alterColumn(name: string, to: Partial<ColumnSpec>): ColumnAlter {\n return { kind: InternalOperationKind.COLUMN_ALTER, table, column: name, to };\n },\n renameColumn(from: string, to: string): ColumnRename {\n return { kind: InternalOperationKind.COLUMN_RENAME, table, from, to };\n },\n };\n }\n\n /** Build a foreign key create operation. */\n static foreignKey(p: {\n table: string;\n name?: string;\n columns: string[];\n references: { table: string; columns: string[] };\n onDelete?: string;\n onUpdate?: string;\n notValid?: boolean;\n }): ForeignKeyCreate {\n return {\n kind: InternalOperationKind.FK_CREATE,\n table: p.table,\n name: p.name,\n columns: p.columns,\n refTable: p.references.table,\n refColumns: p.references.columns,\n onDelete: p.onDelete,\n onUpdate: p.onUpdate,\n notValid: p.notValid,\n };\n }\n\n /** Build a foreign key validation operation. */\n static foreignKeyValidate(p: { table: string; name: string }): ForeignKeyValidate {\n return { kind: InternalOperationKind.FK_VALIDATE, ...p };\n }\n\n /** Build a foreign key drop operation. */\n static foreignKeyDrop(p: { table: string; name: string }): ForeignKeyDrop {\n return { kind: InternalOperationKind.FK_DROP, ...p };\n }\n\n /**\n * Register a custom migration operation builder.\n */\n static registerCustomOperation<TName extends string, TArgs extends object>(\n name: TName,\n builder: (args: TArgs) => CustomMigrationOperation<TName, TArgs>\n ): void {\n this.customOperations.set(name, builder as (args: Record<string, unknown>) => CustomMigrationOperation);\n }\n\n /**\n * Resolve a previously registered custom operation builder.\n */\n static getCustomOperation<TName extends string, TArgs extends object>(\n name: TName\n ): ((args: TArgs) => CustomMigrationOperation<TName, TArgs>) | undefined {\n return this.customOperations.get(name) as ((args: TArgs) => CustomMigrationOperation<TName, TArgs>) | undefined;\n }\n}\n\n/**\n * Apply a domain field type to a column builder.\n */\nexport function applyFieldType(builder: ColumnBuilder, fieldType: ColumnType): ColumnBuilder {\n switch (fieldType) {\n case InternalColumnType.SERIAL:\n return builder.serial();\n case InternalColumnType.INT:\n return builder.int();\n case InternalColumnType.BIGINT:\n return builder.bigint();\n case InternalColumnType.TEXT:\n return builder.text();\n case InternalColumnType.BOOL:\n return builder.bool();\n case InternalColumnType.TIMESTAMPTZ:\n return builder.timestamptz();\n case InternalColumnType.JSONB:\n return builder.jsonb();\n case InternalColumnType.UUID:\n return builder.uuid();\n default: {\n const exhaustive: never = fieldType;\n throw new Error(`Unsupported field type: ${exhaustive}`);\n }\n }\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { OpBuilder, OpBuilder as op, applyFieldType } from './OpBuilder';\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { CollectingBuilder } from './CollectingBuilder';\n","/**\n * Domain boundary barrel: exposes namespaced exports for Django-style drill-down\n * imports and curated flat exports for TS-native ergonomics.\n */\n\nexport * as contracts from './contracts/index';\nexport * as ops from './ops/index';\nexport * as runtime from './runtime/index';\n\nexport type {\n Builder,\n ColumnSpec,\n ColumnType,\n DeleteReferentialAction,\n UpdateReferentialAction,\n} from './contracts/index';\nexport { OpBuilder, op, applyFieldType } from './ops/index';\nexport { CollectingBuilder } from './runtime/index';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"builder-Bhm_to6b.js","names":["exhaustive"],"sources":["../src/builder/contracts/index.ts","../src/builder/ops/OpBuilder.ts","../src/builder/ops/index.ts","../src/builder/runtime/index.ts","../src/builder/index.ts"],"sourcesContent":["/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport type { Builder } from './Builder';\nexport type { ColumnSpec } from './ColumnSpec';\nexport type { ColumnType } from './ColumnType';\nexport type { DeleteReferentialAction } from './DeleteReferentialAction';\nexport type { UpdateReferentialAction } from './UpdateReferentialAction';\n","import type { TrustedSqlFragment } from '@danceroutine/tango-core';\nimport type {\n CustomMigrationOperation,\n ForeignKeyCreate,\n ForeignKeyDrop,\n ForeignKeyValidate,\n IndexCreate,\n IndexDrop,\n TableCreate,\n TableDrop,\n ColumnAdd,\n ColumnDrop,\n ColumnAlter,\n ColumnRename,\n} from '../../domain/MigrationOperation';\nimport type { ColumnSpec } from '../contracts/ColumnSpec';\nimport type { ColumnType } from '../contracts/ColumnType';\nimport { InternalColumnType } from '../../domain/internal/InternalColumnType';\nimport type { DeleteReferentialAction } from '../contracts/DeleteReferentialAction';\nimport type { UpdateReferentialAction } from '../contracts/UpdateReferentialAction';\nimport { InternalOperationKind } from '../../domain/internal/InternalOperationKind';\n\n/**\n * Fluent builder for column specifications used by table operations.\n */\nclass ColumnBuilder {\n static readonly BRAND = 'tango.migrations.column_builder' as const;\n readonly __tangoBrand: typeof ColumnBuilder.BRAND = ColumnBuilder.BRAND;\n private spec: Partial<ColumnSpec> = {};\n\n constructor(name: string) {\n this.spec.name = name;\n }\n\n static isColumnBuilder(value: unknown): value is ColumnBuilder {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === ColumnBuilder.BRAND\n );\n }\n\n /** Set column type to serial/auto-increment. */\n serial(): ColumnBuilder {\n this.spec.type = InternalColumnType.SERIAL;\n return this;\n }\n /** Set column type to integer. */\n int(): ColumnBuilder {\n this.spec.type = InternalColumnType.INT;\n return this;\n }\n /** Set column type to bigint. */\n bigint(): ColumnBuilder {\n this.spec.type = InternalColumnType.BIGINT;\n return this;\n }\n /** Set column type to text. */\n text(): ColumnBuilder {\n this.spec.type = InternalColumnType.TEXT;\n return this;\n }\n /** Set column type to boolean. */\n bool(): ColumnBuilder {\n this.spec.type = InternalColumnType.BOOL;\n return this;\n }\n /** Set column type to timestamptz. */\n timestamptz(): ColumnBuilder {\n this.spec.type = InternalColumnType.TIMESTAMPTZ;\n return this;\n }\n /** Set column type to JSONB. */\n jsonb(): ColumnBuilder {\n this.spec.type = InternalColumnType.JSONB;\n return this;\n }\n /** Set column type to UUID. */\n uuid(): ColumnBuilder {\n this.spec.type = InternalColumnType.UUID;\n return this;\n }\n\n /** Mark column as NOT NULL. */\n notNull(): ColumnBuilder {\n this.spec.notNull = true;\n return this;\n }\n /** Set default to current timestamp. */\n defaultNow(): ColumnBuilder {\n this.spec.default = { now: true };\n return this;\n }\n /** Set reviewed raw SQL default expression. */\n default(v: TrustedSqlFragment | null): ColumnBuilder {\n this.spec.default = v;\n return this;\n }\n /** Mark column as part of primary key. */\n primaryKey(): ColumnBuilder {\n this.spec.primaryKey = true;\n return this;\n }\n /** Mark column as unique. */\n unique(): ColumnBuilder {\n this.spec.unique = true;\n return this;\n }\n /** Configure foreign key reference metadata. */\n references(\n table: string,\n column: string,\n opts?: {\n onDelete?: DeleteReferentialAction;\n onUpdate?: UpdateReferentialAction;\n }\n ): ColumnBuilder {\n this.spec.references = {\n table,\n column,\n onDelete: opts?.onDelete,\n onUpdate: opts?.onUpdate,\n };\n return this;\n }\n\n _done(): ColumnSpec {\n return this.spec as ColumnSpec;\n }\n}\n\ntype TableOperationBuilder = {\n create(def: (cols: { add: (name: string, cb: (b: ColumnBuilder) => ColumnBuilder) => void }) => void): TableCreate;\n drop(opts?: { cascade?: boolean }): TableDrop;\n addColumn(name: string, cb: (b: ColumnBuilder) => ColumnBuilder): ColumnAdd;\n dropColumn(name: string): ColumnDrop;\n alterColumn(name: string, to: Partial<ColumnSpec>): ColumnAlter;\n renameColumn(from: string, to: string): ColumnRename;\n};\n\n/**\n * Static factory for migration operations.\n */\nexport class OpBuilder {\n static readonly BRAND = 'tango.migrations.op_builder' as const;\n static index = {\n /** Build an index create operation. */\n create(p: {\n name: string;\n table: string;\n on: string[];\n unique?: boolean;\n where?: TrustedSqlFragment;\n concurrently?: boolean;\n }): IndexCreate {\n return { kind: InternalOperationKind.INDEX_CREATE, ...p };\n },\n /** Build an index drop operation. */\n drop(p: { name: string; table: string; concurrently?: boolean }): IndexDrop {\n return { kind: InternalOperationKind.INDEX_DROP, ...p };\n },\n };\n private static customOperations = new Map<string, (args: Record<string, unknown>) => CustomMigrationOperation>();\n readonly __tangoBrand: typeof OpBuilder.BRAND = OpBuilder.BRAND;\n\n /**\n * Narrow an unknown value to the shared migration operation builder type.\n */\n static isOpBuilder(value: unknown): value is OpBuilder {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as { __tangoBrand?: unknown }).__tangoBrand === OpBuilder.BRAND\n );\n }\n\n /**\n * Build table-scoped migration operations.\n */\n static table(table: string): TableOperationBuilder {\n return {\n create(\n def: (cols: { add: (name: string, cb: (b: ColumnBuilder) => ColumnBuilder) => void }) => void\n ): TableCreate {\n const columns: ColumnSpec[] = [];\n def({\n add(name, cb) {\n columns.push(cb(new ColumnBuilder(name))._done());\n },\n });\n return { kind: InternalOperationKind.TABLE_CREATE, table, columns };\n },\n drop(opts?: { cascade?: boolean }): TableDrop {\n return { kind: InternalOperationKind.TABLE_DROP, table, cascade: opts?.cascade };\n },\n addColumn(name: string, cb: (b: ColumnBuilder) => ColumnBuilder): ColumnAdd {\n return { kind: InternalOperationKind.COLUMN_ADD, table, column: cb(new ColumnBuilder(name))._done() };\n },\n dropColumn(name: string): ColumnDrop {\n return { kind: InternalOperationKind.COLUMN_DROP, table, column: name };\n },\n alterColumn(name: string, to: Partial<ColumnSpec>): ColumnAlter {\n return { kind: InternalOperationKind.COLUMN_ALTER, table, column: name, to };\n },\n renameColumn(from: string, to: string): ColumnRename {\n return { kind: InternalOperationKind.COLUMN_RENAME, table, from, to };\n },\n };\n }\n\n /** Build a foreign key create operation. */\n static foreignKey(p: {\n table: string;\n name?: string;\n columns: string[];\n references: { table: string; columns: string[] };\n onDelete?: string;\n onUpdate?: string;\n notValid?: boolean;\n }): ForeignKeyCreate {\n return {\n kind: InternalOperationKind.FK_CREATE,\n table: p.table,\n name: p.name,\n columns: p.columns,\n refTable: p.references.table,\n refColumns: p.references.columns,\n onDelete: p.onDelete,\n onUpdate: p.onUpdate,\n notValid: p.notValid,\n };\n }\n\n /** Build a foreign key validation operation. */\n static foreignKeyValidate(p: { table: string; name: string }): ForeignKeyValidate {\n return { kind: InternalOperationKind.FK_VALIDATE, ...p };\n }\n\n /** Build a foreign key drop operation. */\n static foreignKeyDrop(p: { table: string; name: string }): ForeignKeyDrop {\n return { kind: InternalOperationKind.FK_DROP, ...p };\n }\n\n /**\n * Register a custom migration operation builder.\n */\n static registerCustomOperation<TName extends string, TArgs extends object>(\n name: TName,\n builder: (args: TArgs) => CustomMigrationOperation<TName, TArgs>\n ): void {\n this.customOperations.set(name, builder as (args: Record<string, unknown>) => CustomMigrationOperation);\n }\n\n /**\n * Resolve a previously registered custom operation builder.\n */\n static getCustomOperation<TName extends string, TArgs extends object>(\n name: TName\n ): ((args: TArgs) => CustomMigrationOperation<TName, TArgs>) | undefined {\n return this.customOperations.get(name) as ((args: TArgs) => CustomMigrationOperation<TName, TArgs>) | undefined;\n }\n}\n\n/**\n * Apply a domain field type to a column builder.\n */\nexport function applyFieldType(builder: ColumnBuilder, fieldType: ColumnType): ColumnBuilder {\n switch (fieldType) {\n case InternalColumnType.SERIAL:\n return builder.serial();\n case InternalColumnType.INT:\n return builder.int();\n case InternalColumnType.BIGINT:\n return builder.bigint();\n case InternalColumnType.TEXT:\n return builder.text();\n case InternalColumnType.BOOL:\n return builder.bool();\n case InternalColumnType.TIMESTAMPTZ:\n return builder.timestamptz();\n case InternalColumnType.JSONB:\n return builder.jsonb();\n case InternalColumnType.UUID:\n return builder.uuid();\n default: {\n const exhaustive: never = fieldType;\n throw new Error(`Unsupported field type: ${exhaustive}`);\n }\n }\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { OpBuilder, OpBuilder as op, applyFieldType } from './OpBuilder';\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { CollectingBuilder } from './CollectingBuilder';\n","/**\n * Domain boundary barrel: exposes namespaced exports for Django-style drill-down\n * imports and curated flat exports for TS-native ergonomics.\n */\n\nexport * as contracts from './contracts/index';\nexport * as ops from './ops/index';\nexport * as runtime from './runtime/index';\n\nexport type {\n Builder,\n ColumnSpec,\n ColumnType,\n DeleteReferentialAction,\n UpdateReferentialAction,\n} from './contracts/index';\nexport { OpBuilder, op, applyFieldType } from './ops/index';\nexport { CollectingBuilder } from './runtime/index';\n"],"mappings":"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"}
|
package/dist/cli-Dhp2BZsB.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli-Dhp2BZsB.js","names":["fsConstants"],"sources":["../src/commands/cli.ts"],"sourcesContent":["import { access, mkdir } from 'node:fs/promises';\nimport { constants as fsConstants } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport type { Argv } from 'yargs';\nimport { MigrationRunner } from '../runner/MigrationRunner';\nimport { MigrationGenerator } from '../generator/MigrationGenerator';\nimport { buildMigrationModelMetadataProjection, diffSchema } from '../diff/index';\nimport type { DbSchema } from '../introspect/PostgresIntrospector';\nimport type { Dialect } from '../domain/Dialect';\nimport type { ColumnType } from '../builder/contracts/ColumnType';\nimport type { DeleteReferentialAction } from '../builder/contracts/DeleteReferentialAction';\nimport type { UpdateReferentialAction } from '../builder/contracts/UpdateReferentialAction';\nimport { createDefaultIntrospectorStrategy } from '../strategies/IntrospectorStrategy';\nimport { InternalDialect } from '../domain/internal/InternalDialect';\nimport { loadConfig } from '@danceroutine/tango-config';\nimport { loadProjectModule } from '@danceroutine/tango-codegen/commands';\nimport { writeRelationRegistryArtifacts } from '@danceroutine/tango-codegen/generators';\nimport { getLogger } from '@danceroutine/tango-core';\nimport { GENERATED_RELATION_REGISTRY_DIRNAME, ModelRegistry } from '@danceroutine/tango-schema';\nimport { loadModule } from '../runtime/loadModule';\n\nconst logger = getLogger('tango.migrations');\n\ntype ConfigEnvironment = 'development' | 'test' | 'production';\n\ntype OptionalMigrationDefaults = {\n dialect?: Dialect;\n db?: string;\n dir?: string;\n autoApply?: boolean;\n};\n\ntype ModelMetadataLike = {\n table: string;\n fields: Array<{\n name: string;\n type: ColumnType;\n notNull?: boolean;\n default?: string | { now: true } | null;\n primaryKey?: boolean;\n unique?: boolean;\n references?: {\n table: string;\n column: string;\n onDelete?: DeleteReferentialAction;\n onUpdate?: UpdateReferentialAction;\n };\n }>;\n indexes?: Array<{ name: string; on: string[]; unique?: boolean }>;\n};\n\ntype CliDbClient = {\n query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{ rows: T[] }>;\n close(): Promise<void>;\n};\n\ntype LoadedModelsResult = {\n models: ModelMetadataLike[];\n registry: ModelRegistry;\n modelTypeAccessors: Record<string, string>;\n};\n\ntype ModelContainerLike = {\n metadata: ModelMetadataLike;\n};\n\nasync function importModule(modulePath: string): Promise<Record<string, unknown>> {\n return loadModule(modulePath, { projectRoot: process.cwd() });\n}\n\nasync function tryLoadMigrationDefaults(\n configPathArg: string | undefined,\n configEnvArg: ConfigEnvironment | undefined\n): Promise<OptionalMigrationDefaults> {\n const explicitConfigPath = typeof configPathArg === 'string' && configPathArg.trim().length > 0;\n const resolvedPath = resolve(process.cwd(), configPathArg?.trim() || './tango.config.ts');\n\n try {\n await access(resolvedPath, fsConstants.F_OK);\n } catch (error) {\n if (explicitConfigPath) {\n throw new Error(`Config file not found: ${resolvedPath}`, { cause: error });\n }\n return {};\n }\n\n const module = await importModule(resolvedPath);\n const fileConfig = (module.default ?? module) as { current?: ConfigEnvironment } & Record<string, unknown>;\n\n const loaded = loadConfig(() => ({\n ...fileConfig,\n ...(configEnvArg ? { current: configEnvArg } : {}),\n }));\n\n const { db, migrations } = loaded.current;\n const inferredDialect = db.adapter as Dialect;\n const inferredDb = resolveDbTarget(db);\n\n return {\n dialect: inferredDialect,\n db: inferredDb,\n dir: migrations.dir,\n autoApply: (migrations as { autoApply?: boolean }).autoApply,\n };\n}\n\nfunction resolveDbTarget(db: {\n adapter: string;\n url?: string;\n filename?: string;\n host?: string;\n port?: number;\n database?: string;\n user?: string;\n password?: string;\n}): string | undefined {\n if (db.adapter === InternalDialect.SQLITE) {\n return db.filename ?? db.url;\n }\n\n if (db.url) {\n return db.url;\n }\n\n if (!db.database) {\n return undefined;\n }\n\n const host = db.host ?? 'localhost';\n const port = db.port ?? 5432;\n const encodedUser = db.user ? encodeURIComponent(db.user) : '';\n const encodedPassword = db.password ? encodeURIComponent(db.password) : '';\n const userInfo =\n encodedUser.length > 0\n ? encodedPassword.length > 0\n ? `${encodedUser}:${encodedPassword}@`\n : `${encodedUser}@`\n : '';\n\n return `postgres://${userInfo}${host}:${String(port)}/${db.database}`;\n}\n\nasync function resolveCommandInputs(argv: {\n dialect?: string;\n dir?: string;\n db?: string;\n config?: string;\n env?: ConfigEnvironment;\n}): Promise<{ dialect: Dialect; dir: string; db?: string; autoApply: boolean }> {\n const defaults = await tryLoadMigrationDefaults(argv.config, argv.env);\n\n const resolvedDialect = (argv.dialect as Dialect | undefined) ?? defaults.dialect ?? InternalDialect.POSTGRES;\n const resolvedDir = argv.dir ?? defaults.dir ?? 'migrations';\n const resolvedDb = argv.db ?? defaults.db;\n\n return {\n dialect: resolvedDialect,\n dir: resolvedDir,\n db: resolvedDb,\n autoApply: defaults.autoApply ?? true,\n };\n}\n\nfunction isModelContainerLike(value: unknown): value is ModelContainerLike {\n return typeof value === 'object' && value !== null && 'metadata' in value;\n}\n\nfunction collectExportedModels(moduleValue: unknown): ModelMetadataLike[] {\n if (!moduleValue || typeof moduleValue !== 'object') {\n return [];\n }\n\n const models: ModelMetadataLike[] = [];\n for (const value of Object.values(moduleValue as Record<string, unknown>)) {\n if (isModelContainerLike(value)) {\n models.push(value.metadata);\n continue;\n }\n\n if (!value || typeof value !== 'object') {\n continue;\n }\n\n for (const nestedValue of Object.values(value as Record<string, unknown>)) {\n if (isModelContainerLike(nestedValue)) {\n models.push(nestedValue.metadata);\n }\n }\n }\n\n return models;\n}\n\nasync function loadModels(modelsPath: string): Promise<LoadedModelsResult> {\n const {\n loaded: mod,\n registry,\n modelTypeAccessors,\n } = await loadProjectModule(modelsPath, {\n projectRoot: process.cwd(),\n outputDir: resolve(process.cwd(), GENERATED_RELATION_REGISTRY_DIRNAME),\n });\n const moduleValue = (mod.default ?? mod) as unknown;\n\n const models = isModelContainerLike(moduleValue)\n ? [moduleValue.metadata, ...collectExportedModels(moduleValue)]\n : collectExportedModels(moduleValue);\n\n if (models.length === 0) {\n throw new Error(`No models found in '${modelsPath}'. Ensure the module exports Model() definitions.`);\n }\n\n return {\n models,\n registry,\n modelTypeAccessors,\n };\n}\n\nasync function connectAndIntrospect(dbUrl: string, dialect: string): Promise<DbSchema> {\n const dbClient = await connectDbClient(dbUrl, dialect as Dialect);\n\n try {\n const strategy = createDefaultIntrospectorStrategy();\n return await strategy.introspect(dialect as Dialect, dbClient);\n } finally {\n await dbClient.close();\n }\n}\n\nasync function connectDbClient(db: string, dialect: Dialect): Promise<CliDbClient> {\n if (dialect === InternalDialect.POSTGRES) {\n const pg = await import('pg');\n const client = new pg.default.Client({ connectionString: db });\n await client.connect();\n\n return {\n async query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{ rows: T[] }> {\n const result = await client.query(sql, params as unknown[] | undefined);\n return { rows: result.rows as T[] };\n },\n async close(): Promise<void> {\n await client.end();\n },\n };\n }\n\n if (dialect === InternalDialect.SQLITE) {\n const sqlite = await import('better-sqlite3');\n const DatabaseCtor = (sqlite.default ?? sqlite) as new (filename: string) => {\n prepare(sql: string): {\n all(...params: unknown[]): unknown[];\n run(...params: unknown[]): unknown;\n };\n close(): void;\n };\n\n const filename = normalizeSqliteFilename(db);\n await ensureSqliteParentDirectory(filename);\n const connection = new DatabaseCtor(filename);\n\n return {\n async query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{ rows: T[] }> {\n const statement = connection.prepare(sql);\n const values = [...(params ?? [])];\n const isSelectLike = /^\\s*(SELECT|PRAGMA|WITH)\\b/i.test(sql);\n if (isSelectLike) {\n return { rows: statement.all(...values) as T[] };\n }\n\n statement.run(...values);\n return { rows: [] as T[] };\n },\n async close(): Promise<void> {\n connection.close();\n },\n };\n }\n\n throw new Error(`Unsupported dialect: ${dialect}`);\n}\n\nasync function ensureSqliteParentDirectory(filename: string): Promise<void> {\n if (filename === ':memory:' || filename === 'file::memory:') {\n return;\n }\n const directory = dirname(filename);\n if (directory === '.' || directory.length === 0) {\n return;\n }\n await mkdir(directory, { recursive: true });\n}\n\nfunction normalizeSqliteFilename(db: string): string {\n if (db.startsWith('sqlite://')) {\n return db.slice('sqlite://'.length);\n }\n return db;\n}\n\n/**\n * Register Tango's migration commands on an existing yargs parser.\n */\nexport function registerMigrationsCommands(yargsBuilder: Argv): Argv {\n return yargsBuilder\n .command(\n 'migrate',\n 'Apply pending migrations to the database',\n (builder) =>\n builder\n .option('dir', {\n type: 'string',\n describe: 'Migrations directory',\n })\n .option('db', {\n type: 'string',\n describe: 'Database connection URL',\n })\n .option('dialect', {\n type: 'string',\n choices: ['postgres', 'sqlite'] as const,\n describe: 'Database dialect',\n })\n .option('config', {\n type: 'string',\n describe: 'Path to tango.config.ts (auto-loads ./tango.config.ts when present)',\n })\n .option('env', {\n type: 'string',\n choices: ['development', 'test', 'production'] as const,\n describe: 'Config environment override',\n })\n .option('to', {\n type: 'string',\n describe: 'Target migration ID (apply up to this migration)',\n }),\n async (argv) => {\n const resolved = await resolveCommandInputs({\n dialect: argv.dialect as string | undefined,\n dir: argv.dir as string | undefined,\n db: argv.db as string | undefined,\n config: argv.config as string | undefined,\n env: argv.env as ConfigEnvironment | undefined,\n });\n\n if (!resolved.autoApply) {\n logger.info('Auto-migration disabled (autoApply: false). Skipping.');\n return;\n }\n\n if (!resolved.db) {\n throw new Error('No database target provided. Pass --db or define db settings in tango.config.ts.');\n }\n\n const dbClient = await connectDbClient(resolved.db, resolved.dialect);\n\n const runner = new MigrationRunner(dbClient, resolved.dialect, resolved.dir);\n await runner.apply(argv.to);\n\n await dbClient.close();\n logger.info('Migrations applied successfully');\n }\n )\n .command(\n 'make:migrations',\n 'Generate migration file by comparing models to database',\n (builder) =>\n builder\n .option('dir', {\n type: 'string',\n describe: 'Migrations directory',\n })\n .option('name', {\n type: 'string',\n demandOption: true,\n describe: 'Migration name (e.g. \"create_users\")',\n })\n .option('models', {\n type: 'string',\n demandOption: true,\n describe: 'Path to module exporting Model definitions (e.g. \"./src/models.ts\")',\n })\n .option('db', {\n type: 'string',\n describe: 'Database connection URL for introspection (omit for initial migration)',\n })\n .option('dialect', {\n type: 'string',\n choices: ['postgres', 'sqlite'] as const,\n describe: 'Database dialect',\n })\n .option('config', {\n type: 'string',\n describe: 'Path to tango.config.ts (auto-loads ./tango.config.ts when present)',\n })\n .option('env', {\n type: 'string',\n choices: ['development', 'test', 'production'] as const,\n describe: 'Config environment override',\n }),\n async (argv) => {\n const resolved = await resolveCommandInputs({\n dialect: argv.dialect as string | undefined,\n dir: argv.dir as string | undefined,\n db: argv.db as string | undefined,\n config: argv.config as string | undefined,\n env: argv.env as ConfigEnvironment | undefined,\n });\n const loaded = await loadModels(argv.models);\n const projectedModels = buildMigrationModelMetadataProjection(loaded.registry, {\n dialect: resolved.dialect,\n });\n logger.info(\n `Found ${loaded.models.length} exported model(s); ${projectedModels.length} model(s) after projection: ${projectedModels.map((m) => m.table).join(', ')}`\n );\n try {\n await writeRelationRegistryArtifacts({\n registry: loaded.registry,\n modelTypeAccessors: loaded.modelTypeAccessors,\n outputDir: resolve(process.cwd(), GENERATED_RELATION_REGISTRY_DIRNAME),\n });\n } catch (error) {\n logger.warn(\n `Unable to refresh generated relation registry during make:migrations. Continuing without updated relation artifacts: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n let dbState: DbSchema;\n if (resolved.db) {\n logger.info('Introspecting database...');\n dbState = await connectAndIntrospect(resolved.db, resolved.dialect);\n } else {\n dbState = { tables: {} };\n }\n\n const operations = diffSchema(dbState, projectedModels);\n\n if (operations.length === 0) {\n logger.info('No changes detected — models and database are in sync');\n return;\n }\n\n const generator = new MigrationGenerator();\n const filepath = await generator.generate({\n name: argv.name,\n operations,\n directory: resolved.dir,\n });\n\n logger.info(`Generated migration: ${filepath}`);\n logger.info(` ${operations.length} operation(s)`);\n }\n )\n .command(\n 'plan',\n 'Print migration SQL without applying',\n (builder) =>\n builder\n .option('dir', {\n type: 'string',\n describe: 'Migrations directory',\n })\n .option('dialect', {\n type: 'string',\n choices: ['postgres', 'sqlite'] as const,\n describe: 'Database dialect',\n })\n .option('config', {\n type: 'string',\n describe: 'Path to tango.config.ts (auto-loads ./tango.config.ts when present)',\n })\n .option('env', {\n type: 'string',\n choices: ['development', 'test', 'production'] as const,\n describe: 'Config environment override',\n }),\n async (argv) => {\n const resolved = await resolveCommandInputs({\n dialect: argv.dialect as string | undefined,\n dir: argv.dir as string | undefined,\n db: argv.db as string | undefined,\n config: argv.config as string | undefined,\n env: argv.env as ConfigEnvironment | undefined,\n });\n const runner = new MigrationRunner(\n { query: async () => ({ rows: [] }), close: async () => {} },\n resolved.dialect,\n resolved.dir\n );\n const output = await runner.plan();\n logger.info(output);\n }\n )\n .command(\n 'status',\n 'Show applied/pending status of all migrations',\n (builder) =>\n builder\n .option('dir', {\n type: 'string',\n describe: 'Migrations directory',\n })\n .option('db', {\n type: 'string',\n describe: 'Database connection URL',\n })\n .option('dialect', {\n type: 'string',\n choices: ['postgres', 'sqlite'] as const,\n describe: 'Database dialect',\n })\n .option('config', {\n type: 'string',\n describe: 'Path to tango.config.ts (auto-loads ./tango.config.ts when present)',\n })\n .option('env', {\n type: 'string',\n choices: ['development', 'test', 'production'] as const,\n describe: 'Config environment override',\n }),\n async (argv) => {\n const resolved = await resolveCommandInputs({\n dialect: argv.dialect as string | undefined,\n dir: argv.dir as string | undefined,\n db: argv.db as string | undefined,\n config: argv.config as string | undefined,\n env: argv.env as ConfigEnvironment | undefined,\n });\n if (!resolved.db) {\n throw new Error('No database target provided. Pass --db or define db settings in tango.config.ts.');\n }\n\n const dbClient = await connectDbClient(resolved.db, resolved.dialect);\n\n const runner = new MigrationRunner(dbClient, resolved.dialect, resolved.dir);\n const statuses = await runner.status();\n\n if (statuses.length === 0) {\n logger.info('No migrations found');\n } else {\n statuses.forEach((statusItem) => {\n const marker = statusItem.applied ? '[x]' : '[ ]';\n logger.info(` ${marker} ${statusItem.id}`);\n });\n }\n\n await dbClient.close();\n }\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli-Dhp2BZsB.js","names":["fsConstants"],"sources":["../src/commands/cli.ts"],"sourcesContent":["import { access, mkdir } from 'node:fs/promises';\nimport { constants as fsConstants } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport type { Argv } from 'yargs';\nimport { MigrationRunner } from '../runner/MigrationRunner';\nimport { MigrationGenerator } from '../generator/MigrationGenerator';\nimport { buildMigrationModelMetadataProjection, diffSchema } from '../diff/index';\nimport type { DbSchema } from '../introspect/PostgresIntrospector';\nimport type { Dialect } from '../domain/Dialect';\nimport type { ColumnType } from '../builder/contracts/ColumnType';\nimport type { DeleteReferentialAction } from '../builder/contracts/DeleteReferentialAction';\nimport type { UpdateReferentialAction } from '../builder/contracts/UpdateReferentialAction';\nimport { createDefaultIntrospectorStrategy } from '../strategies/IntrospectorStrategy';\nimport { InternalDialect } from '../domain/internal/InternalDialect';\nimport { loadConfig } from '@danceroutine/tango-config';\nimport { loadProjectModule } from '@danceroutine/tango-codegen/commands';\nimport { writeRelationRegistryArtifacts } from '@danceroutine/tango-codegen/generators';\nimport { getLogger } from '@danceroutine/tango-core';\nimport { GENERATED_RELATION_REGISTRY_DIRNAME, ModelRegistry } from '@danceroutine/tango-schema';\nimport { loadModule } from '../runtime/loadModule';\n\nconst logger = getLogger('tango.migrations');\n\ntype ConfigEnvironment = 'development' | 'test' | 'production';\n\ntype OptionalMigrationDefaults = {\n dialect?: Dialect;\n db?: string;\n dir?: string;\n autoApply?: boolean;\n};\n\ntype ModelMetadataLike = {\n table: string;\n fields: Array<{\n name: string;\n type: ColumnType;\n notNull?: boolean;\n default?: string | { now: true } | null;\n primaryKey?: boolean;\n unique?: boolean;\n references?: {\n table: string;\n column: string;\n onDelete?: DeleteReferentialAction;\n onUpdate?: UpdateReferentialAction;\n };\n }>;\n indexes?: Array<{ name: string; on: string[]; unique?: boolean }>;\n};\n\ntype CliDbClient = {\n query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{ rows: T[] }>;\n close(): Promise<void>;\n};\n\ntype LoadedModelsResult = {\n models: ModelMetadataLike[];\n registry: ModelRegistry;\n modelTypeAccessors: Record<string, string>;\n};\n\ntype ModelContainerLike = {\n metadata: ModelMetadataLike;\n};\n\nasync function importModule(modulePath: string): Promise<Record<string, unknown>> {\n return loadModule(modulePath, { projectRoot: process.cwd() });\n}\n\nasync function tryLoadMigrationDefaults(\n configPathArg: string | undefined,\n configEnvArg: ConfigEnvironment | undefined\n): Promise<OptionalMigrationDefaults> {\n const explicitConfigPath = typeof configPathArg === 'string' && configPathArg.trim().length > 0;\n const resolvedPath = resolve(process.cwd(), configPathArg?.trim() || './tango.config.ts');\n\n try {\n await access(resolvedPath, fsConstants.F_OK);\n } catch (error) {\n if (explicitConfigPath) {\n throw new Error(`Config file not found: ${resolvedPath}`, { cause: error });\n }\n return {};\n }\n\n const module = await importModule(resolvedPath);\n const fileConfig = (module.default ?? module) as { current?: ConfigEnvironment } & Record<string, unknown>;\n\n const loaded = loadConfig(() => ({\n ...fileConfig,\n ...(configEnvArg ? { current: configEnvArg } : {}),\n }));\n\n const { db, migrations } = loaded.current;\n const inferredDialect = db.adapter as Dialect;\n const inferredDb = resolveDbTarget(db);\n\n return {\n dialect: inferredDialect,\n db: inferredDb,\n dir: migrations.dir,\n autoApply: (migrations as { autoApply?: boolean }).autoApply,\n };\n}\n\nfunction resolveDbTarget(db: {\n adapter: string;\n url?: string;\n filename?: string;\n host?: string;\n port?: number;\n database?: string;\n user?: string;\n password?: string;\n}): string | undefined {\n if (db.adapter === InternalDialect.SQLITE) {\n return db.filename ?? db.url;\n }\n\n if (db.url) {\n return db.url;\n }\n\n if (!db.database) {\n return undefined;\n }\n\n const host = db.host ?? 'localhost';\n const port = db.port ?? 5432;\n const encodedUser = db.user ? encodeURIComponent(db.user) : '';\n const encodedPassword = db.password ? encodeURIComponent(db.password) : '';\n const userInfo =\n encodedUser.length > 0\n ? encodedPassword.length > 0\n ? `${encodedUser}:${encodedPassword}@`\n : `${encodedUser}@`\n : '';\n\n return `postgres://${userInfo}${host}:${String(port)}/${db.database}`;\n}\n\nasync function resolveCommandInputs(argv: {\n dialect?: string;\n dir?: string;\n db?: string;\n config?: string;\n env?: ConfigEnvironment;\n}): Promise<{ dialect: Dialect; dir: string; db?: string; autoApply: boolean }> {\n const defaults = await tryLoadMigrationDefaults(argv.config, argv.env);\n\n const resolvedDialect = (argv.dialect as Dialect | undefined) ?? defaults.dialect ?? InternalDialect.POSTGRES;\n const resolvedDir = argv.dir ?? defaults.dir ?? 'migrations';\n const resolvedDb = argv.db ?? defaults.db;\n\n return {\n dialect: resolvedDialect,\n dir: resolvedDir,\n db: resolvedDb,\n autoApply: defaults.autoApply ?? true,\n };\n}\n\nfunction isModelContainerLike(value: unknown): value is ModelContainerLike {\n return typeof value === 'object' && value !== null && 'metadata' in value;\n}\n\nfunction collectExportedModels(moduleValue: unknown): ModelMetadataLike[] {\n if (!moduleValue || typeof moduleValue !== 'object') {\n return [];\n }\n\n const models: ModelMetadataLike[] = [];\n for (const value of Object.values(moduleValue as Record<string, unknown>)) {\n if (isModelContainerLike(value)) {\n models.push(value.metadata);\n continue;\n }\n\n if (!value || typeof value !== 'object') {\n continue;\n }\n\n for (const nestedValue of Object.values(value as Record<string, unknown>)) {\n if (isModelContainerLike(nestedValue)) {\n models.push(nestedValue.metadata);\n }\n }\n }\n\n return models;\n}\n\nasync function loadModels(modelsPath: string): Promise<LoadedModelsResult> {\n const {\n loaded: mod,\n registry,\n modelTypeAccessors,\n } = await loadProjectModule(modelsPath, {\n projectRoot: process.cwd(),\n outputDir: resolve(process.cwd(), GENERATED_RELATION_REGISTRY_DIRNAME),\n });\n const moduleValue = (mod.default ?? mod) as unknown;\n\n const models = isModelContainerLike(moduleValue)\n ? [moduleValue.metadata, ...collectExportedModels(moduleValue)]\n : collectExportedModels(moduleValue);\n\n if (models.length === 0) {\n throw new Error(`No models found in '${modelsPath}'. Ensure the module exports Model() definitions.`);\n }\n\n return {\n models,\n registry,\n modelTypeAccessors,\n };\n}\n\nasync function connectAndIntrospect(dbUrl: string, dialect: string): Promise<DbSchema> {\n const dbClient = await connectDbClient(dbUrl, dialect as Dialect);\n\n try {\n const strategy = createDefaultIntrospectorStrategy();\n return await strategy.introspect(dialect as Dialect, dbClient);\n } finally {\n await dbClient.close();\n }\n}\n\nasync function connectDbClient(db: string, dialect: Dialect): Promise<CliDbClient> {\n if (dialect === InternalDialect.POSTGRES) {\n const pg = await import('pg');\n const client = new pg.default.Client({ connectionString: db });\n await client.connect();\n\n return {\n async query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{ rows: T[] }> {\n const result = await client.query(sql, params as unknown[] | undefined);\n return { rows: result.rows as T[] };\n },\n async close(): Promise<void> {\n await client.end();\n },\n };\n }\n\n if (dialect === InternalDialect.SQLITE) {\n const sqlite = await import('better-sqlite3');\n const DatabaseCtor = (sqlite.default ?? sqlite) as new (filename: string) => {\n prepare(sql: string): {\n all(...params: unknown[]): unknown[];\n run(...params: unknown[]): unknown;\n };\n close(): void;\n };\n\n const filename = normalizeSqliteFilename(db);\n await ensureSqliteParentDirectory(filename);\n const connection = new DatabaseCtor(filename);\n\n return {\n async query<T = unknown>(sql: string, params?: readonly unknown[]): Promise<{ rows: T[] }> {\n const statement = connection.prepare(sql);\n const values = [...(params ?? [])];\n const isSelectLike = /^\\s*(SELECT|PRAGMA|WITH)\\b/i.test(sql);\n if (isSelectLike) {\n return { rows: statement.all(...values) as T[] };\n }\n\n statement.run(...values);\n return { rows: [] as T[] };\n },\n async close(): Promise<void> {\n connection.close();\n },\n };\n }\n\n throw new Error(`Unsupported dialect: ${dialect}`);\n}\n\nasync function ensureSqliteParentDirectory(filename: string): Promise<void> {\n if (filename === ':memory:' || filename === 'file::memory:') {\n return;\n }\n const directory = dirname(filename);\n if (directory === '.' || directory.length === 0) {\n return;\n }\n await mkdir(directory, { recursive: true });\n}\n\nfunction normalizeSqliteFilename(db: string): string {\n if (db.startsWith('sqlite://')) {\n return db.slice('sqlite://'.length);\n }\n return db;\n}\n\n/**\n * Register Tango's migration commands on an existing yargs parser.\n */\nexport function registerMigrationsCommands(yargsBuilder: Argv): Argv {\n return yargsBuilder\n .command(\n 'migrate',\n 'Apply pending migrations to the database',\n (builder) =>\n builder\n .option('dir', {\n type: 'string',\n describe: 'Migrations directory',\n })\n .option('db', {\n type: 'string',\n describe: 'Database connection URL',\n })\n .option('dialect', {\n type: 'string',\n choices: ['postgres', 'sqlite'] as const,\n describe: 'Database dialect',\n })\n .option('config', {\n type: 'string',\n describe: 'Path to tango.config.ts (auto-loads ./tango.config.ts when present)',\n })\n .option('env', {\n type: 'string',\n choices: ['development', 'test', 'production'] as const,\n describe: 'Config environment override',\n })\n .option('to', {\n type: 'string',\n describe: 'Target migration ID (apply up to this migration)',\n }),\n async (argv) => {\n const resolved = await resolveCommandInputs({\n dialect: argv.dialect as string | undefined,\n dir: argv.dir as string | undefined,\n db: argv.db as string | undefined,\n config: argv.config as string | undefined,\n env: argv.env as ConfigEnvironment | undefined,\n });\n\n if (!resolved.autoApply) {\n logger.info('Auto-migration disabled (autoApply: false). Skipping.');\n return;\n }\n\n if (!resolved.db) {\n throw new Error('No database target provided. Pass --db or define db settings in tango.config.ts.');\n }\n\n const dbClient = await connectDbClient(resolved.db, resolved.dialect);\n\n const runner = new MigrationRunner(dbClient, resolved.dialect, resolved.dir);\n await runner.apply(argv.to);\n\n await dbClient.close();\n logger.info('Migrations applied successfully');\n }\n )\n .command(\n 'make:migrations',\n 'Generate migration file by comparing models to database',\n (builder) =>\n builder\n .option('dir', {\n type: 'string',\n describe: 'Migrations directory',\n })\n .option('name', {\n type: 'string',\n demandOption: true,\n describe: 'Migration name (e.g. \"create_users\")',\n })\n .option('models', {\n type: 'string',\n demandOption: true,\n describe: 'Path to module exporting Model definitions (e.g. \"./src/models.ts\")',\n })\n .option('db', {\n type: 'string',\n describe: 'Database connection URL for introspection (omit for initial migration)',\n })\n .option('dialect', {\n type: 'string',\n choices: ['postgres', 'sqlite'] as const,\n describe: 'Database dialect',\n })\n .option('config', {\n type: 'string',\n describe: 'Path to tango.config.ts (auto-loads ./tango.config.ts when present)',\n })\n .option('env', {\n type: 'string',\n choices: ['development', 'test', 'production'] as const,\n describe: 'Config environment override',\n }),\n async (argv) => {\n const resolved = await resolveCommandInputs({\n dialect: argv.dialect as string | undefined,\n dir: argv.dir as string | undefined,\n db: argv.db as string | undefined,\n config: argv.config as string | undefined,\n env: argv.env as ConfigEnvironment | undefined,\n });\n const loaded = await loadModels(argv.models);\n const projectedModels = buildMigrationModelMetadataProjection(loaded.registry, {\n dialect: resolved.dialect,\n });\n logger.info(\n `Found ${loaded.models.length} exported model(s); ${projectedModels.length} model(s) after projection: ${projectedModels.map((m) => m.table).join(', ')}`\n );\n try {\n await writeRelationRegistryArtifacts({\n registry: loaded.registry,\n modelTypeAccessors: loaded.modelTypeAccessors,\n outputDir: resolve(process.cwd(), GENERATED_RELATION_REGISTRY_DIRNAME),\n });\n } catch (error) {\n logger.warn(\n `Unable to refresh generated relation registry during make:migrations. Continuing without updated relation artifacts: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n\n let dbState: DbSchema;\n if (resolved.db) {\n logger.info('Introspecting database...');\n dbState = await connectAndIntrospect(resolved.db, resolved.dialect);\n } else {\n dbState = { tables: {} };\n }\n\n const operations = diffSchema(dbState, projectedModels);\n\n if (operations.length === 0) {\n logger.info('No changes detected — models and database are in sync');\n return;\n }\n\n const generator = new MigrationGenerator();\n const filepath = await generator.generate({\n name: argv.name,\n operations,\n directory: resolved.dir,\n });\n\n logger.info(`Generated migration: ${filepath}`);\n logger.info(` ${operations.length} operation(s)`);\n }\n )\n .command(\n 'plan',\n 'Print migration SQL without applying',\n (builder) =>\n builder\n .option('dir', {\n type: 'string',\n describe: 'Migrations directory',\n })\n .option('dialect', {\n type: 'string',\n choices: ['postgres', 'sqlite'] as const,\n describe: 'Database dialect',\n })\n .option('config', {\n type: 'string',\n describe: 'Path to tango.config.ts (auto-loads ./tango.config.ts when present)',\n })\n .option('env', {\n type: 'string',\n choices: ['development', 'test', 'production'] as const,\n describe: 'Config environment override',\n }),\n async (argv) => {\n const resolved = await resolveCommandInputs({\n dialect: argv.dialect as string | undefined,\n dir: argv.dir as string | undefined,\n db: argv.db as string | undefined,\n config: argv.config as string | undefined,\n env: argv.env as ConfigEnvironment | undefined,\n });\n const runner = new MigrationRunner(\n { query: async () => ({ rows: [] }), close: async () => {} },\n resolved.dialect,\n resolved.dir\n );\n const output = await runner.plan();\n logger.info(output);\n }\n )\n .command(\n 'status',\n 'Show applied/pending status of all migrations',\n (builder) =>\n builder\n .option('dir', {\n type: 'string',\n describe: 'Migrations directory',\n })\n .option('db', {\n type: 'string',\n describe: 'Database connection URL',\n })\n .option('dialect', {\n type: 'string',\n choices: ['postgres', 'sqlite'] as const,\n describe: 'Database dialect',\n })\n .option('config', {\n type: 'string',\n describe: 'Path to tango.config.ts (auto-loads ./tango.config.ts when present)',\n })\n .option('env', {\n type: 'string',\n choices: ['development', 'test', 'production'] as const,\n describe: 'Config environment override',\n }),\n async (argv) => {\n const resolved = await resolveCommandInputs({\n dialect: argv.dialect as string | undefined,\n dir: argv.dir as string | undefined,\n db: argv.db as string | undefined,\n config: argv.config as string | undefined,\n env: argv.env as ConfigEnvironment | undefined,\n });\n if (!resolved.db) {\n throw new Error('No database target provided. Pass --db or define db settings in tango.config.ts.');\n }\n\n const dbClient = await connectDbClient(resolved.db, resolved.dialect);\n\n const runner = new MigrationRunner(dbClient, resolved.dialect, resolved.dir);\n const statuses = await runner.status();\n\n if (statuses.length === 0) {\n logger.info('No migrations found');\n } else {\n statuses.forEach((statusItem) => {\n const marker = statusItem.applied ? '[x]' : '[ ]';\n logger.info(` ${marker} ${statusItem.id}`);\n });\n }\n\n await dbClient.close();\n }\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAqBA,MAAM,SAAS,UAAU,kBAAkB;AA6C3C,eAAe,aAAa,YAAsD;CAC9E,OAAO,WAAW,YAAY,EAAE,aAAa,QAAQ,IAAI,EAAE,CAAC;AAChE;AAEA,eAAe,yBACX,eACA,cACkC;CAClC,MAAM,qBAAqB,OAAO,kBAAkB,YAAY,cAAc,KAAK,CAAC,CAAC,SAAS;CAC9F,MAAM,eAAe,QAAQ,QAAQ,IAAI,GAAG,eAAe,KAAK,KAAK,mBAAmB;CAExF,IAAI;EACA,MAAM,OAAO,cAAcA,UAAY,IAAI;CAC/C,SAAS,OAAO;EACZ,IAAI,oBACA,MAAM,IAAI,MAAM,0BAA0B,gBAAgB,EAAE,OAAO,MAAM,CAAC;EAE9E,OAAO,CAAC;CACZ;CAEA,MAAM,SAAS,MAAM,aAAa,YAAY;CAC9C,MAAM,aAAc,OAAO,WAAW;CAOtC,MAAM,EAAE,IAAI,eALG,kBAAkB;EAC7B,GAAG;EACH,GAAI,eAAe,EAAE,SAAS,aAAa,IAAI,CAAC;CACpD,EAEgC,CAAC,CAAC;CAIlC,OAAO;EACH,SAJoB,GAAG;EAKvB,IAJe,gBAAgB,EAIlB;EACb,KAAK,WAAW;EAChB,WAAY,WAAuC;CACvD;AACJ;AAEA,SAAS,gBAAgB,IASF;CACnB,IAAI,GAAG,YAAY,gBAAgB,QAC/B,OAAO,GAAG,YAAY,GAAG;CAG7B,IAAI,GAAG,KACH,OAAO,GAAG;CAGd,IAAI,CAAC,GAAG,UACJ;CAGJ,MAAM,OAAO,GAAG,QAAQ;CACxB,MAAM,OAAO,GAAG,QAAQ;CACxB,MAAM,cAAc,GAAG,OAAO,mBAAmB,GAAG,IAAI,IAAI;CAC5D,MAAM,kBAAkB,GAAG,WAAW,mBAAmB,GAAG,QAAQ,IAAI;CAQxE,OAAO,cANH,YAAY,SAAS,IACf,gBAAgB,SAAS,IACrB,GAAG,YAAY,GAAG,gBAAgB,KAClC,GAAG,YAAY,KACnB,KAEsB,KAAK,GAAG,OAAO,IAAI,EAAE,GAAG,GAAG;AAC/D;AAEA,eAAe,qBAAqB,MAM4C;CAC5E,MAAM,WAAW,MAAM,yBAAyB,KAAK,QAAQ,KAAK,GAAG;CAMrE,OAAO;EACH,SALqB,KAAK,WAAmC,SAAS,WAAW,gBAAgB;EAMjG,KALgB,KAAK,OAAO,SAAS,OAAO;EAM5C,IALe,KAAK,MAAM,SAAS;EAMnC,WAAW,SAAS,aAAa;CACrC;AACJ;AAEA,SAAS,qBAAqB,OAA6C;CACvE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,cAAc;AACxE;AAEA,SAAS,sBAAsB,aAA2C;CACtE,IAAI,CAAC,eAAe,OAAO,gBAAgB,UACvC,OAAO,CAAC;CAGZ,MAAM,SAA8B,CAAC;CACrC,KAAK,MAAM,SAAS,OAAO,OAAO,WAAsC,GAAG;EACvE,IAAI,qBAAqB,KAAK,GAAG;GAC7B,OAAO,KAAK,MAAM,QAAQ;GAC1B;EACJ;EAEA,IAAI,CAAC,SAAS,OAAO,UAAU,UAC3B;EAGJ,KAAK,MAAM,eAAe,OAAO,OAAO,KAAgC,GACpE,IAAI,qBAAqB,WAAW,GAChC,OAAO,KAAK,YAAY,QAAQ;CAG5C;CAEA,OAAO;AACX;AAEA,eAAe,WAAW,YAAiD;CACvE,MAAM,EACF,QAAQ,KACR,UACA,uBACA,MAAM,kBAAkB,YAAY;EACpC,aAAa,QAAQ,IAAI;EACzB,WAAW,QAAQ,QAAQ,IAAI,GAAG,mCAAmC;CACzE,CAAC;CACD,MAAM,cAAe,IAAI,WAAW;CAEpC,MAAM,SAAS,qBAAqB,WAAW,IACzC,CAAC,YAAY,UAAU,GAAG,sBAAsB,WAAW,CAAC,IAC5D,sBAAsB,WAAW;CAEvC,IAAI,OAAO,WAAW,GAClB,MAAM,IAAI,MAAM,uBAAuB,WAAW,kDAAkD;CAGxG,OAAO;EACH;EACA;EACA;CACJ;AACJ;AAEA,eAAe,qBAAqB,OAAe,SAAoC;CACnF,MAAM,WAAW,MAAM,gBAAgB,OAAO,OAAkB;CAEhE,IAAI;EAEA,OAAO,MADU,kCACG,CAAC,CAAC,WAAW,SAAoB,QAAQ;CACjE,UAAU;EACN,MAAM,SAAS,MAAM;CACzB;AACJ;AAEA,eAAe,gBAAgB,IAAY,SAAwC;CAC/E,IAAI,YAAY,gBAAgB,UAAU;EAEtC,MAAM,SAAS,KAAI,OADF,OAAO,OAAA,CACF,QAAQ,OAAO,EAAE,kBAAkB,GAAG,CAAC;EAC7D,MAAM,OAAO,QAAQ;EAErB,OAAO;GACH,MAAM,MAAmB,KAAa,QAAqD;IAEvF,OAAO,EAAE,OAAM,MADM,OAAO,MAAM,KAAK,MAA+B,EAAA,CAChD,KAAY;GACtC;GACA,MAAM,QAAuB;IACzB,MAAM,OAAO,IAAI;GACrB;EACJ;CACJ;CAEA,IAAI,YAAY,gBAAgB,QAAQ;EACpC,MAAM,SAAS,MAAM,OAAO;EAC5B,MAAM,eAAgB,OAAO,WAAW;EAQxC,MAAM,WAAW,wBAAwB,EAAE;EAC3C,MAAM,4BAA4B,QAAQ;EAC1C,MAAM,aAAa,IAAI,aAAa,QAAQ;EAE5C,OAAO;GACH,MAAM,MAAmB,KAAa,QAAqD;IACvF,MAAM,YAAY,WAAW,QAAQ,GAAG;IACxC,MAAM,SAAS,CAAC,GAAI,UAAU,CAAC,CAAE;IAEjC,IADqB,8BAA8B,KAAK,GACzC,GACX,OAAO,EAAE,MAAM,UAAU,IAAI,GAAG,MAAM,EAAS;IAGnD,UAAU,IAAI,GAAG,MAAM;IACvB,OAAO,EAAE,MAAM,CAAC,EAAS;GAC7B;GACA,MAAM,QAAuB;IACzB,WAAW,MAAM;GACrB;EACJ;CACJ;CAEA,MAAM,IAAI,MAAM,wBAAwB,SAAS;AACrD;AAEA,eAAe,4BAA4B,UAAiC;CACxE,IAAI,aAAa,cAAc,aAAa,iBACxC;CAEJ,MAAM,YAAY,QAAQ,QAAQ;CAClC,IAAI,cAAc,OAAO,UAAU,WAAW,GAC1C;CAEJ,MAAM,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAC9C;AAEA,SAAS,wBAAwB,IAAoB;CACjD,IAAI,GAAG,WAAW,WAAW,GACzB,OAAO,GAAG,MAAM,CAAkB;CAEtC,OAAO;AACX;;;;AAKA,SAAgB,2BAA2B,cAA0B;CACjE,OAAO,aACF,QACG,WACA,6CACC,YACG,QACK,OAAO,OAAO;EACX,MAAM;EACN,UAAU;CACd,CAAC,CAAC,CACD,OAAO,MAAM;EACV,MAAM;EACN,UAAU;CACd,CAAC,CAAC,CACD,OAAO,WAAW;EACf,MAAM;EACN,SAAS,CAAC,YAAY,QAAQ;EAC9B,UAAU;CACd,CAAC,CAAC,CACD,OAAO,UAAU;EACd,MAAM;EACN,UAAU;CACd,CAAC,CAAC,CACD,OAAO,OAAO;EACX,MAAM;EACN,SAAS;GAAC;GAAe;GAAQ;EAAY;EAC7C,UAAU;CACd,CAAC,CAAC,CACD,OAAO,MAAM;EACV,MAAM;EACN,UAAU;CACd,CAAC,GACT,OAAO,SAAS;EACZ,MAAM,WAAW,MAAM,qBAAqB;GACxC,SAAS,KAAK;GACd,KAAK,KAAK;GACV,IAAI,KAAK;GACT,QAAQ,KAAK;GACb,KAAK,KAAK;EACd,CAAC;EAED,IAAI,CAAC,SAAS,WAAW;GACrB,OAAO,KAAK,uDAAuD;GACnE;EACJ;EAEA,IAAI,CAAC,SAAS,IACV,MAAM,IAAI,MAAM,kFAAkF;EAGtG,MAAM,WAAW,MAAM,gBAAgB,SAAS,IAAI,SAAS,OAAO;EAGpE,MAAM,IADa,gBAAgB,UAAU,SAAS,SAAS,SAAS,GAC7D,CAAC,CAAC,MAAM,KAAK,EAAE;EAE1B,MAAM,SAAS,MAAM;EACrB,OAAO,KAAK,iCAAiC;CACjD,CACJ,CAAC,CACA,QACG,mBACA,4DACC,YACG,QACK,OAAO,OAAO;EACX,MAAM;EACN,UAAU;CACd,CAAC,CAAC,CACD,OAAO,QAAQ;EACZ,MAAM;EACN,cAAc;EACd,UAAU;CACd,CAAC,CAAC,CACD,OAAO,UAAU;EACd,MAAM;EACN,cAAc;EACd,UAAU;CACd,CAAC,CAAC,CACD,OAAO,MAAM;EACV,MAAM;EACN,UAAU;CACd,CAAC,CAAC,CACD,OAAO,WAAW;EACf,MAAM;EACN,SAAS,CAAC,YAAY,QAAQ;EAC9B,UAAU;CACd,CAAC,CAAC,CACD,OAAO,UAAU;EACd,MAAM;EACN,UAAU;CACd,CAAC,CAAC,CACD,OAAO,OAAO;EACX,MAAM;EACN,SAAS;GAAC;GAAe;GAAQ;EAAY;EAC7C,UAAU;CACd,CAAC,GACT,OAAO,SAAS;EACZ,MAAM,WAAW,MAAM,qBAAqB;GACxC,SAAS,KAAK;GACd,KAAK,KAAK;GACV,IAAI,KAAK;GACT,QAAQ,KAAK;GACb,KAAK,KAAK;EACd,CAAC;EACD,MAAM,SAAS,MAAM,WAAW,KAAK,MAAM;EAC3C,MAAM,kBAAkB,sCAAsC,OAAO,UAAU,EAC3E,SAAS,SAAS,QACtB,CAAC;EACD,OAAO,KACH,SAAS,OAAO,OAAO,OAAO,sBAAsB,gBAAgB,OAAO,8BAA8B,gBAAgB,KAAK,MAAM,EAAE,KAAK,CAAC,CAAC,KAAK,IAAI,GAC1J;EACA,IAAI;GACA,MAAM,+BAA+B;IACjC,UAAU,OAAO;IACjB,oBAAoB,OAAO;IAC3B,WAAW,QAAQ,QAAQ,IAAI,GAAG,mCAAmC;GACzE,CAAC;EACL,SAAS,OAAO;GACZ,OAAO,KACH,wHAAwH,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GACjL;EACJ;EAEA,IAAI;EACJ,IAAI,SAAS,IAAI;GACb,OAAO,KAAK,2BAA2B;GACvC,UAAU,MAAM,qBAAqB,SAAS,IAAI,SAAS,OAAO;EACtE,OACI,UAAU,EAAE,QAAQ,CAAC,EAAE;EAG3B,MAAM,aAAa,WAAW,SAAS,eAAe;EAEtD,IAAI,WAAW,WAAW,GAAG;GACzB,OAAO,KAAK,uDAAuD;GACnE;EACJ;EAGA,MAAM,WAAW,MAAM,IADD,mBACS,CAAC,CAAC,SAAS;GACtC,MAAM,KAAK;GACX;GACA,WAAW,SAAS;EACxB,CAAC;EAED,OAAO,KAAK,wBAAwB,UAAU;EAC9C,OAAO,KAAK,KAAK,WAAW,OAAO,cAAc;CACrD,CACJ,CAAC,CACA,QACG,QACA,yCACC,YACG,QACK,OAAO,OAAO;EACX,MAAM;EACN,UAAU;CACd,CAAC,CAAC,CACD,OAAO,WAAW;EACf,MAAM;EACN,SAAS,CAAC,YAAY,QAAQ;EAC9B,UAAU;CACd,CAAC,CAAC,CACD,OAAO,UAAU;EACd,MAAM;EACN,UAAU;CACd,CAAC,CAAC,CACD,OAAO,OAAO;EACX,MAAM;EACN,SAAS;GAAC;GAAe;GAAQ;EAAY;EAC7C,UAAU;CACd,CAAC,GACT,OAAO,SAAS;EACZ,MAAM,WAAW,MAAM,qBAAqB;GACxC,SAAS,KAAK;GACd,KAAK,KAAK;GACV,IAAI,KAAK;GACT,QAAQ,KAAK;GACb,KAAK,KAAK;EACd,CAAC;EAMD,MAAM,SAAS,MAAM,IALF,gBACf;GAAE,OAAO,aAAa,EAAE,MAAM,CAAC,EAAE;GAAI,OAAO,YAAY,CAAC;EAAE,GAC3D,SAAS,SACT,SAAS,GAEa,CAAC,CAAC,KAAK;EACjC,OAAO,KAAK,MAAM;CACtB,CACJ,CAAC,CACA,QACG,UACA,kDACC,YACG,QACK,OAAO,OAAO;EACX,MAAM;EACN,UAAU;CACd,CAAC,CAAC,CACD,OAAO,MAAM;EACV,MAAM;EACN,UAAU;CACd,CAAC,CAAC,CACD,OAAO,WAAW;EACf,MAAM;EACN,SAAS,CAAC,YAAY,QAAQ;EAC9B,UAAU;CACd,CAAC,CAAC,CACD,OAAO,UAAU;EACd,MAAM;EACN,UAAU;CACd,CAAC,CAAC,CACD,OAAO,OAAO;EACX,MAAM;EACN,SAAS;GAAC;GAAe;GAAQ;EAAY;EAC7C,UAAU;CACd,CAAC,GACT,OAAO,SAAS;EACZ,MAAM,WAAW,MAAM,qBAAqB;GACxC,SAAS,KAAK;GACd,KAAK,KAAK;GACV,IAAI,KAAK;GACT,QAAQ,KAAK;GACb,KAAK,KAAK;EACd,CAAC;EACD,IAAI,CAAC,SAAS,IACV,MAAM,IAAI,MAAM,kFAAkF;EAGtG,MAAM,WAAW,MAAM,gBAAgB,SAAS,IAAI,SAAS,OAAO;EAGpE,MAAM,WAAW,MAAM,IADJ,gBAAgB,UAAU,SAAS,SAAS,SAAS,GAC5C,CAAC,CAAC,OAAO;EAErC,IAAI,SAAS,WAAW,GACpB,OAAO,KAAK,qBAAqB;OAEjC,SAAS,SAAS,eAAe;GAC7B,MAAM,SAAS,WAAW,UAAU,QAAQ;GAC5C,OAAO,KAAK,KAAK,OAAO,GAAG,WAAW,IAAI;EAC9C,CAAC;EAGL,MAAM,SAAS,MAAM;CACzB,CACJ;AACR"}
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport yargs from 'yargs';\nimport { hideBin } from 'yargs/helpers';\nimport { registerMigrationsCommands } from './commands/cli';\n\nfunction runMigrationsCli(): void {\n registerMigrationsCommands(yargs(hideBin(process.argv)))\n .scriptName('tango')\n .demandCommand(1, 'You must specify a command')\n .strict()\n .help()\n .alias('help', 'h')\n .alias('version', 'v')\n .parse();\n}\n\nrunMigrationsCli();\n"],"mappings":";;;;;AAKA,SAAS,mBAAyB;CAC9B,2BAA2B,MAAM,QAAQ,QAAQ,IAAI,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport yargs from 'yargs';\nimport { hideBin } from 'yargs/helpers';\nimport { registerMigrationsCommands } from './commands/cli';\n\nfunction runMigrationsCli(): void {\n registerMigrationsCommands(yargs(hideBin(process.argv)))\n .scriptName('tango')\n .demandCommand(1, 'You must specify a command')\n .strict()\n .help()\n .alias('help', 'h')\n .alias('version', 'v')\n .parse();\n}\n\nrunMigrationsCli();\n"],"mappings":";;;;;AAKA,SAAS,mBAAyB;CAC9B,2BAA2B,MAAM,QAAQ,QAAQ,IAAI,CAAC,CAAC,CAAC,CACnD,WAAW,OAAO,CAAC,CACnB,cAAc,GAAG,4BAA4B,CAAC,CAC9C,OAAO,CAAC,CACR,KAAK,CAAC,CACN,MAAM,QAAQ,GAAG,CAAC,CAClB,MAAM,WAAW,GAAG,CAAC,CACrB,MAAM;AACf;AAEA,iBAAiB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff-DdwU_MyI.js","names":["op"],"sources":["../src/diff/diffSchema.ts","../src/schema/field/strategy/ModelFieldMapperStrategy.ts","../src/schema/field/strategy/PostgresModelFieldMapperStrategy.ts","../src/schema/field/strategy/SqliteModelFieldMapperStrategy.ts","../src/schema/field/strategy/createModelFieldMapperStrategy.ts","../src/schema/buildMigrationModelMetadataProjection.ts","../src/diff/index.ts"],"sourcesContent":["import { trustedSql } from '@danceroutine/tango-core';\nimport type { DbSchema } from '../introspect/PostgresIntrospector';\nimport type { MigrationOperation } from '../domain/MigrationOperation';\nimport type { ColumnType } from '../builder/contracts/ColumnType';\nimport type { DeleteReferentialAction } from '../builder/contracts/DeleteReferentialAction';\nimport type { UpdateReferentialAction } from '../builder/contracts/UpdateReferentialAction';\nimport { op, applyFieldType } from '../builder/index';\n\ntype ModelField = {\n name: string;\n type: ColumnType;\n notNull?: boolean;\n default?: string | { now: true } | null;\n primaryKey?: boolean;\n unique?: boolean;\n references?: {\n table: string;\n column: string;\n onDelete?: DeleteReferentialAction;\n onUpdate?: UpdateReferentialAction;\n };\n};\n\ntype ModelIndex = {\n name: string;\n on: string[];\n unique?: boolean;\n};\n\nexport type ModelMetadataLike = {\n name?: string;\n table: string;\n fields: ModelField[];\n indexes?: ModelIndex[];\n managed?: boolean;\n};\n\n/**\n * Compare model metadata with an introspected database schema and return the\n * operations needed to bring the database into alignment.\n */\nexport function diffSchema(db: DbSchema, models: ModelMetadataLike[]): MigrationOperation[] {\n const ops: MigrationOperation[] = [];\n models.forEach((model) => {\n if (model.managed === false) {\n return;\n }\n\n const dbTable = db.tables[model.table];\n\n if (!dbTable) {\n ops.push(\n op.table(model.table).create((cols) => {\n model.fields.forEach((field) => {\n cols.add(field.name, (builder) => {\n builder = applyFieldType(builder, field.type);\n\n if (field.notNull) {\n builder = builder.notNull();\n }\n\n if (field.default === null) {\n builder = builder.default(null);\n } else if (field.default && typeof field.default === 'object' && 'now' in field.default) {\n builder = builder.defaultNow();\n } else if (typeof field.default === 'string') {\n builder = builder.default(trustedSql(field.default));\n }\n\n if (field.primaryKey) {\n builder = builder.primaryKey();\n }\n\n if (field.unique) {\n builder = builder.unique();\n }\n\n if (field.references) {\n builder = builder.references(field.references.table, field.references.column, {\n onDelete: field.references.onDelete,\n onUpdate: field.references.onUpdate,\n });\n }\n\n return builder;\n });\n });\n })\n );\n\n (model.indexes ?? []).forEach((index) => {\n ops.push(\n op.index.create({\n name: index.name,\n table: model.table,\n on: index.on,\n unique: !!index.unique,\n })\n );\n });\n return;\n }\n\n const modelFieldNames = new Set(model.fields.map((field) => field.name));\n const dbFieldNames = new Set(Object.keys(dbTable.columns));\n\n model.fields.forEach((field) => {\n if (!dbFieldNames.has(field.name)) {\n ops.push(\n op.table(model.table).addColumn(field.name, (builder) => {\n builder = applyFieldType(builder, field.type);\n\n if (field.notNull) {\n builder = builder.notNull();\n }\n if (field.default === null) {\n builder = builder.default(null);\n } else if (field.default && typeof field.default === 'object' && 'now' in field.default) {\n builder = builder.defaultNow();\n } else if (typeof field.default === 'string') {\n builder = builder.default(trustedSql(field.default));\n }\n if (field.primaryKey) {\n builder = builder.primaryKey();\n }\n if (field.unique) {\n builder = builder.unique();\n }\n if (field.references) {\n builder = builder.references(field.references.table, field.references.column, {\n onDelete: field.references.onDelete,\n onUpdate: field.references.onUpdate,\n });\n }\n\n return builder;\n })\n );\n }\n });\n\n dbFieldNames.forEach((dbColumnName) => {\n if (!modelFieldNames.has(dbColumnName)) {\n ops.push(op.table(model.table).dropColumn(dbColumnName));\n }\n });\n\n const modelIndexes = new Map((model.indexes ?? []).map((index) => [index.name, index] as const));\n const dbIndexNames = new Set(Object.keys(dbTable.indexes));\n\n modelIndexes.forEach((index, indexName) => {\n if (!dbIndexNames.has(indexName)) {\n ops.push(\n op.index.create({\n name: index.name,\n table: model.table,\n on: index.on,\n unique: !!index.unique,\n })\n );\n }\n });\n\n // Index drift is intentionally conservative. Extra indexes in the live\n // database may be app-owned or dialect-managed, so migrations only add\n // missing declared indexes and avoid destructive index drops here.\n });\n\n return ops;\n}\n","import type { Field } from '@danceroutine/tango-schema/domain';\nimport type { ColumnType } from '../../../builder/contracts/ColumnType';\nimport type { ModelMetadataLike } from '../../../diff/diffSchema';\n\nexport class ModelFieldMapperStrategy {\n mapField(field: Field): ModelMetadataLike['fields'][number] {\n return {\n name: field.name,\n type: this.mapType(field),\n notNull: field.notNull,\n default: field.default,\n primaryKey: field.primaryKey,\n unique: field.unique,\n references: field.references as ModelMetadataLike['fields'][number]['references'],\n };\n }\n\n protected mapType(field: Field): ColumnType {\n return field.type as ColumnType;\n }\n}\n","import type { Field } from '@danceroutine/tango-schema/domain';\nimport type { ColumnType } from '../../../builder/contracts/ColumnType';\nimport { InternalColumnType } from '../../../domain/internal/InternalColumnType';\nimport { ModelFieldMapperStrategy } from './ModelFieldMapperStrategy';\n\nexport class PostgresModelFieldMapperStrategy extends ModelFieldMapperStrategy {\n protected override mapType(field: Field): ColumnType {\n // Postgres auto-incrementing primary keys are modeled as serial in migrations,\n // while SQLite must preserve the schema-level integer type to avoid churn.\n if (field.primaryKey && field.type === InternalColumnType.INT) {\n return InternalColumnType.SERIAL;\n }\n\n return super.mapType(field);\n }\n}\n","import { ModelFieldMapperStrategy } from './ModelFieldMapperStrategy';\n\nexport class SqliteModelFieldMapperStrategy extends ModelFieldMapperStrategy {}\n","import type { Dialect } from '../../../domain/Dialect';\nimport { InternalDialect } from '../../../domain/internal/InternalDialect';\nimport { ModelFieldMapperStrategy } from './ModelFieldMapperStrategy';\nimport { PostgresModelFieldMapperStrategy } from './PostgresModelFieldMapperStrategy';\nimport { SqliteModelFieldMapperStrategy } from './SqliteModelFieldMapperStrategy';\n\nexport function createModelFieldMapperStrategy(dialect?: Dialect): ModelFieldMapperStrategy {\n switch (dialect) {\n case undefined:\n case InternalDialect.SQLITE:\n return new SqliteModelFieldMapperStrategy();\n case InternalDialect.POSTGRES:\n return new PostgresModelFieldMapperStrategy();\n }\n}\n","import type { ModelRegistry } from '@danceroutine/tango-schema';\nimport type { Dialect } from '../domain/Dialect';\nimport type { ModelMetadataLike } from '../diff/diffSchema';\nimport { createModelFieldMapperStrategy } from './field/strategy/createModelFieldMapperStrategy';\n\nexport type BuildMigrationModelMetadataProjectionOptions = {\n dialect?: Dialect;\n};\n\nexport function buildMigrationModelMetadataProjection(\n registry: ModelRegistry,\n options?: BuildMigrationModelMetadataProjectionOptions\n): ModelMetadataLike[] {\n registry.finalizeStorageArtifacts();\n const fieldMapperStrategy = createModelFieldMapperStrategy(options?.dialect);\n const projection: ModelMetadataLike[] = [];\n for (const model of registry.values()) {\n const finalized = registry.getFinalizedFields(model.metadata.key);\n projection.push({\n name: model.metadata.name,\n table: model.metadata.table,\n managed: model.metadata.managed ?? true,\n fields: finalized.map((field) => fieldMapperStrategy.mapField(field)),\n indexes: model.metadata.indexes?.map((index) => ({\n name: index.name,\n on: [...index.on],\n unique: index.unique,\n })),\n });\n }\n return projection;\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { diffSchema } from './diffSchema';\nexport { buildMigrationModelMetadataProjection } from '../schema/buildMigrationModelMetadataProjection';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"diff-DdwU_MyI.js","names":["op"],"sources":["../src/diff/diffSchema.ts","../src/schema/field/strategy/ModelFieldMapperStrategy.ts","../src/schema/field/strategy/PostgresModelFieldMapperStrategy.ts","../src/schema/field/strategy/SqliteModelFieldMapperStrategy.ts","../src/schema/field/strategy/createModelFieldMapperStrategy.ts","../src/schema/buildMigrationModelMetadataProjection.ts","../src/diff/index.ts"],"sourcesContent":["import { trustedSql } from '@danceroutine/tango-core';\nimport type { DbSchema } from '../introspect/PostgresIntrospector';\nimport type { MigrationOperation } from '../domain/MigrationOperation';\nimport type { ColumnType } from '../builder/contracts/ColumnType';\nimport type { DeleteReferentialAction } from '../builder/contracts/DeleteReferentialAction';\nimport type { UpdateReferentialAction } from '../builder/contracts/UpdateReferentialAction';\nimport { op, applyFieldType } from '../builder/index';\n\ntype ModelField = {\n name: string;\n type: ColumnType;\n notNull?: boolean;\n default?: string | { now: true } | null;\n primaryKey?: boolean;\n unique?: boolean;\n references?: {\n table: string;\n column: string;\n onDelete?: DeleteReferentialAction;\n onUpdate?: UpdateReferentialAction;\n };\n};\n\ntype ModelIndex = {\n name: string;\n on: string[];\n unique?: boolean;\n};\n\nexport type ModelMetadataLike = {\n name?: string;\n table: string;\n fields: ModelField[];\n indexes?: ModelIndex[];\n managed?: boolean;\n};\n\n/**\n * Compare model metadata with an introspected database schema and return the\n * operations needed to bring the database into alignment.\n */\nexport function diffSchema(db: DbSchema, models: ModelMetadataLike[]): MigrationOperation[] {\n const ops: MigrationOperation[] = [];\n models.forEach((model) => {\n if (model.managed === false) {\n return;\n }\n\n const dbTable = db.tables[model.table];\n\n if (!dbTable) {\n ops.push(\n op.table(model.table).create((cols) => {\n model.fields.forEach((field) => {\n cols.add(field.name, (builder) => {\n builder = applyFieldType(builder, field.type);\n\n if (field.notNull) {\n builder = builder.notNull();\n }\n\n if (field.default === null) {\n builder = builder.default(null);\n } else if (field.default && typeof field.default === 'object' && 'now' in field.default) {\n builder = builder.defaultNow();\n } else if (typeof field.default === 'string') {\n builder = builder.default(trustedSql(field.default));\n }\n\n if (field.primaryKey) {\n builder = builder.primaryKey();\n }\n\n if (field.unique) {\n builder = builder.unique();\n }\n\n if (field.references) {\n builder = builder.references(field.references.table, field.references.column, {\n onDelete: field.references.onDelete,\n onUpdate: field.references.onUpdate,\n });\n }\n\n return builder;\n });\n });\n })\n );\n\n (model.indexes ?? []).forEach((index) => {\n ops.push(\n op.index.create({\n name: index.name,\n table: model.table,\n on: index.on,\n unique: !!index.unique,\n })\n );\n });\n return;\n }\n\n const modelFieldNames = new Set(model.fields.map((field) => field.name));\n const dbFieldNames = new Set(Object.keys(dbTable.columns));\n\n model.fields.forEach((field) => {\n if (!dbFieldNames.has(field.name)) {\n ops.push(\n op.table(model.table).addColumn(field.name, (builder) => {\n builder = applyFieldType(builder, field.type);\n\n if (field.notNull) {\n builder = builder.notNull();\n }\n if (field.default === null) {\n builder = builder.default(null);\n } else if (field.default && typeof field.default === 'object' && 'now' in field.default) {\n builder = builder.defaultNow();\n } else if (typeof field.default === 'string') {\n builder = builder.default(trustedSql(field.default));\n }\n if (field.primaryKey) {\n builder = builder.primaryKey();\n }\n if (field.unique) {\n builder = builder.unique();\n }\n if (field.references) {\n builder = builder.references(field.references.table, field.references.column, {\n onDelete: field.references.onDelete,\n onUpdate: field.references.onUpdate,\n });\n }\n\n return builder;\n })\n );\n }\n });\n\n dbFieldNames.forEach((dbColumnName) => {\n if (!modelFieldNames.has(dbColumnName)) {\n ops.push(op.table(model.table).dropColumn(dbColumnName));\n }\n });\n\n const modelIndexes = new Map((model.indexes ?? []).map((index) => [index.name, index] as const));\n const dbIndexNames = new Set(Object.keys(dbTable.indexes));\n\n modelIndexes.forEach((index, indexName) => {\n if (!dbIndexNames.has(indexName)) {\n ops.push(\n op.index.create({\n name: index.name,\n table: model.table,\n on: index.on,\n unique: !!index.unique,\n })\n );\n }\n });\n\n // Index drift is intentionally conservative. Extra indexes in the live\n // database may be app-owned or dialect-managed, so migrations only add\n // missing declared indexes and avoid destructive index drops here.\n });\n\n return ops;\n}\n","import type { Field } from '@danceroutine/tango-schema/domain';\nimport type { ColumnType } from '../../../builder/contracts/ColumnType';\nimport type { ModelMetadataLike } from '../../../diff/diffSchema';\n\nexport class ModelFieldMapperStrategy {\n mapField(field: Field): ModelMetadataLike['fields'][number] {\n return {\n name: field.name,\n type: this.mapType(field),\n notNull: field.notNull,\n default: field.default,\n primaryKey: field.primaryKey,\n unique: field.unique,\n references: field.references as ModelMetadataLike['fields'][number]['references'],\n };\n }\n\n protected mapType(field: Field): ColumnType {\n return field.type as ColumnType;\n }\n}\n","import type { Field } from '@danceroutine/tango-schema/domain';\nimport type { ColumnType } from '../../../builder/contracts/ColumnType';\nimport { InternalColumnType } from '../../../domain/internal/InternalColumnType';\nimport { ModelFieldMapperStrategy } from './ModelFieldMapperStrategy';\n\nexport class PostgresModelFieldMapperStrategy extends ModelFieldMapperStrategy {\n protected override mapType(field: Field): ColumnType {\n // Postgres auto-incrementing primary keys are modeled as serial in migrations,\n // while SQLite must preserve the schema-level integer type to avoid churn.\n if (field.primaryKey && field.type === InternalColumnType.INT) {\n return InternalColumnType.SERIAL;\n }\n\n return super.mapType(field);\n }\n}\n","import { ModelFieldMapperStrategy } from './ModelFieldMapperStrategy';\n\nexport class SqliteModelFieldMapperStrategy extends ModelFieldMapperStrategy {}\n","import type { Dialect } from '../../../domain/Dialect';\nimport { InternalDialect } from '../../../domain/internal/InternalDialect';\nimport { ModelFieldMapperStrategy } from './ModelFieldMapperStrategy';\nimport { PostgresModelFieldMapperStrategy } from './PostgresModelFieldMapperStrategy';\nimport { SqliteModelFieldMapperStrategy } from './SqliteModelFieldMapperStrategy';\n\nexport function createModelFieldMapperStrategy(dialect?: Dialect): ModelFieldMapperStrategy {\n switch (dialect) {\n case undefined:\n case InternalDialect.SQLITE:\n return new SqliteModelFieldMapperStrategy();\n case InternalDialect.POSTGRES:\n return new PostgresModelFieldMapperStrategy();\n }\n}\n","import type { ModelRegistry } from '@danceroutine/tango-schema';\nimport type { Dialect } from '../domain/Dialect';\nimport type { ModelMetadataLike } from '../diff/diffSchema';\nimport { createModelFieldMapperStrategy } from './field/strategy/createModelFieldMapperStrategy';\n\nexport type BuildMigrationModelMetadataProjectionOptions = {\n dialect?: Dialect;\n};\n\nexport function buildMigrationModelMetadataProjection(\n registry: ModelRegistry,\n options?: BuildMigrationModelMetadataProjectionOptions\n): ModelMetadataLike[] {\n registry.finalizeStorageArtifacts();\n const fieldMapperStrategy = createModelFieldMapperStrategy(options?.dialect);\n const projection: ModelMetadataLike[] = [];\n for (const model of registry.values()) {\n const finalized = registry.getFinalizedFields(model.metadata.key);\n projection.push({\n name: model.metadata.name,\n table: model.metadata.table,\n managed: model.metadata.managed ?? true,\n fields: finalized.map((field) => fieldMapperStrategy.mapField(field)),\n indexes: model.metadata.indexes?.map((index) => ({\n name: index.name,\n on: [...index.on],\n unique: index.unique,\n })),\n });\n }\n return projection;\n}\n","/**\n * Domain boundary barrel: centralizes this subdomain's public contract.\n */\n\nexport { diffSchema } from './diffSchema';\nexport { buildMigrationModelMetadataProjection } from '../schema/buildMigrationModelMetadataProjection';\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@danceroutine/tango-migrations",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"description": "Auto-migration system with ops DSL for Tango",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -67,10 +67,10 @@
|
|
|
67
67
|
"jiti": "^2.7.0",
|
|
68
68
|
"kleur": "^4.1.5",
|
|
69
69
|
"yargs": "^18.0.0",
|
|
70
|
-
"@danceroutine/tango-config": "1.
|
|
71
|
-
"@danceroutine/tango-codegen": "1.
|
|
72
|
-
"@danceroutine/tango-core": "1.
|
|
73
|
-
"@danceroutine/tango-schema": "1.
|
|
70
|
+
"@danceroutine/tango-config": "1.12.0",
|
|
71
|
+
"@danceroutine/tango-codegen": "1.12.0",
|
|
72
|
+
"@danceroutine/tango-core": "1.12.0",
|
|
73
|
+
"@danceroutine/tango-schema": "1.12.0"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
76
|
"pg": "^8.20.0",
|
|
@@ -91,12 +91,12 @@
|
|
|
91
91
|
"@types/yargs": "^17.0.33",
|
|
92
92
|
"better-sqlite3": "^12.10.0",
|
|
93
93
|
"pg": "^8.20.0",
|
|
94
|
-
"tsdown": "^0.22.
|
|
94
|
+
"tsdown": "^0.22.2",
|
|
95
95
|
"typescript": "^6.0.3",
|
|
96
96
|
"vitest": "^4.1.8",
|
|
97
97
|
"zod": "^4.0.0",
|
|
98
|
-
"@danceroutine/tango-testing": "1.
|
|
99
|
-
"@danceroutine/tango-orm": "1.
|
|
98
|
+
"@danceroutine/tango-testing": "1.12.0",
|
|
99
|
+
"@danceroutine/tango-orm": "1.12.0"
|
|
100
100
|
},
|
|
101
101
|
"scripts": {
|
|
102
102
|
"build": "tsdown",
|