@happyvertical/smrt-core 0.43.0 → 0.43.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +10 -1
- package/agents/schema-paths.md +8 -0
- package/dist/decorators/index.d.ts +18 -1
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/index.js +7 -0
- package/dist/decorators/index.js.map +1 -1
- package/dist/manifest/static-manifest.js +1 -1
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest.json +1 -1
- package/dist/migrations/differ.d.ts.map +1 -1
- package/dist/migrations/differ.js +30 -2
- package/dist/migrations/differ.js.map +1 -1
- package/dist/migrations/generator.d.ts.map +1 -1
- package/dist/migrations/generator.js +7 -0
- package/dist/migrations/generator.js.map +1 -1
- package/dist/registry/schema-builder.d.ts.map +1 -1
- package/dist/registry/schema-builder.js +4 -2
- package/dist/registry/schema-builder.js.map +1 -1
- package/dist/scanner/manifest-generator.d.ts.map +1 -1
- package/dist/scanner/manifest-generator.js +13 -3
- package/dist/scanner/manifest-generator.js.map +1 -1
- package/dist/scanner/types.d.ts +4 -2
- package/dist/scanner/types.d.ts.map +1 -1
- package/dist/scanner/types.js.map +1 -1
- package/dist/schema/ddl/base-strategy.js +2 -2
- package/dist/schema/ddl/base-strategy.js.map +1 -1
- package/dist/schema/ddl/duckdb-strategy.d.ts.map +1 -1
- package/dist/schema/ddl/duckdb-strategy.js +8 -2
- package/dist/schema/ddl/duckdb-strategy.js.map +1 -1
- package/dist/schema/foreign-key-ddl.d.ts +7 -0
- package/dist/schema/foreign-key-ddl.d.ts.map +1 -1
- package/dist/schema/foreign-key-ddl.js +34 -2
- package/dist/schema/foreign-key-ddl.js.map +1 -1
- package/dist/schema/foreign-key-planner.d.ts.map +1 -1
- package/dist/schema/foreign-key-planner.js +5 -5
- package/dist/schema/foreign-key-planner.js.map +1 -1
- package/dist/schema/generator.d.ts.map +1 -1
- package/dist/schema/generator.js +12 -4
- package/dist/schema/generator.js.map +1 -1
- package/dist/schema/manifest-schema.d.ts.map +1 -1
- package/dist/schema/manifest-schema.js +2 -1
- package/dist/schema/manifest-schema.js.map +1 -1
- package/dist/schema/types.d.ts +6 -2
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/utils.d.ts +1 -1
- package/dist/schema/utils.d.ts.map +1 -1
- package/dist/schema/utils.js +4 -3
- package/dist/schema/utils.js.map +1 -1
- package/dist/smrt-knowledge.json +7 -7
- package/dist/testing/database.d.ts.map +1 -1
- package/dist/testing/database.js +2 -2
- package/dist/testing/database.js.map +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/migrations/generator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,OAAO,KAAK,EACV,mBAAmB,EAGnB,UAAU,EACX,MAAM,oBAAoB,CAAC;AAG5B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIjD;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,MAAM,CAAC,EAAE,KAAK,GAAG,YAAY,CAAC;IAC9B,8CAA8C;IAC9C,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,wCAAwC;IACxC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,gCAAgC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,iDAAiD;IACjD,EAAE,EAAE,MAAM,CAAC;IACX,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,wBAAwB;IACxB,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,mDAAmD;IACnD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,WAAW,CAAU;IAC7B,OAAO,CAAC,WAAW,CAAC,CAAS;IAC7B,OAAO,CAAC,2BAA2B,CAAU;gBAG3C,OAAO,GAAE;QACP,MAAM,CAAC,EAAE,cAAc,CAAC;QACxB,MAAM,CAAC,EAAE,KAAK,GAAG,YAAY,CAAC;QAC9B,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB;;;;;;;;;;;;;;WAcG;QACH,2BAA2B,CAAC,EAAE,OAAO,CAAC;KAClC;IAUR;;OAEG;IACH,gBAAgB,CACd,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,wBAAwB,GAChC,kBAAkB;IA8DrB;;OAEG;IACH,eAAe,CACb,YAAY,EAAE,MAAM,EAAE,EACtB,cAAc,EAAE,MAAM,EAAE,EACxB,OAAO,EAAE,wBAAwB,GAChC,kBAAkB;IAIrB;;OAEG;IACH,aAAa,CAAC,OAAO,EAAE,wBAAwB,GAAG,kBAAkB;IAIpE;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAsCzB;;OAEG;IACH,OAAO,CAAC,eAAe;IA0CvB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA6C9B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAsC3B;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAI5B;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAI7B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAIzB;;OAEG;IACH,OAAO,CAAC,iBAAiB;
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/migrations/generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -204,6 +204,13 @@ ${downStatementsStr}
|
|
|
204
204
|
for (const suggestion of change.advisory.suggestedSql ?? []) up.push(`-- suggested: ${suggestion}`);
|
|
205
205
|
}
|
|
206
206
|
break;
|
|
207
|
+
case "drop_foreign_key":
|
|
208
|
+
if (sqlStatements.length > 0) up.push(...sqlStatements);
|
|
209
|
+
else if (change.advisory) {
|
|
210
|
+
up.push(`-- ${change.advisory.severity === "warning" ? "WARNING" : "NOTE"}: Foreign-key constraint ${change.table}.${change.name ?? "unknown"}`);
|
|
211
|
+
up.push(`-- ${change.advisory.message}`);
|
|
212
|
+
}
|
|
213
|
+
break;
|
|
207
214
|
case "drop_index":
|
|
208
215
|
if (change.name) up.push(this.generateDropIndex(change.name));
|
|
209
216
|
break;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.js","names":[],"sources":["../../src/migrations/generator.ts"],"sourcesContent":["/**\n * Migration Generator\n *\n * Generates migration files from schema diffs.\n * Supports both SQL and TypeScript formats.\n */\n\nimport { createLogger } from '@happyvertical/logger';\nimport { getDDLStrategy } from '../schema/ddl/index.js';\nimport { materializeManifestDDLForEngine } from '../schema/ddl/materialize-manifest.js';\nimport {\n planForeignKeyCreation,\n renderDeferredForeignKeyDrop,\n} from '../schema/foreign-key-planner.js';\nimport { cachedDdlTableConstraints } from '../schema/manifest-schema.js';\nimport type {\n MigrationDefinition,\n SchemaChange,\n SchemaDefinition,\n SchemaDiff,\n} from '../schema/types.js';\nimport { renderIndexTarget } from '../schema/utils.js';\nimport { computeChecksum } from './checksum.js';\nimport type { DatabaseEngine } from './types.js';\n\nconst logger = createLogger({ level: 'info' });\n\n/**\n * Options for migration generation\n */\nexport interface GenerateMigrationOptions {\n /** Migration name (without sequence number) */\n name: string;\n /** Output format */\n format?: 'sql' | 'typescript';\n /** Database engine for engine-specific SQL */\n engine?: DatabaseEngine;\n /** Include generated DOWN statements */\n includeDown?: boolean;\n /** Package name for metadata */\n packageName?: string;\n /** Description for the migration */\n description?: string;\n}\n\n/**\n * Result of migration generation\n */\nexport interface GeneratedMigration {\n /** Full migration ID (e.g., '0001_add_users') */\n id: string;\n /** Filename (e.g., '0001_add_users.sql') */\n filename: string;\n /** File content */\n content: string;\n /** UP SQL statements */\n up: string[];\n /** DOWN SQL statements (if includeDown is true) */\n down: string[];\n /** Checksum of the migration */\n checksum: string;\n}\n\n/**\n * MigrationGenerator class for creating migration files\n */\nexport class MigrationGenerator {\n private engine: DatabaseEngine;\n private format: 'sql' | 'typescript';\n private includeDown: boolean;\n private packageName?: string;\n private materializeStructuredSchema: boolean;\n\n constructor(\n options: {\n engine?: DatabaseEngine;\n format?: 'sql' | 'typescript';\n includeDown?: boolean;\n packageName?: string;\n /**\n * Render new tables from their structured columns through the engine's\n * DDL strategy (default, `true`) — the same renderer `db:migrate` uses,\n * so column types, indexes and triggers match what the differ expects\n * on the next run.\n *\n * @deprecated Passing `false` re-enables the retired cached-`ddl` string\n * path for the CREATE TABLE only (#2358): it emits the manifest's\n * engine-neutral CREATE TABLE with only bare PostgreSQL `TIMESTAMP`\n * rewritten, so `REAL`/`JSON` drift on PostgreSQL. The table's indexes\n * and triggers are still rendered through the engine strategy either\n * way — the flag never suppresses them. Schemas that expose no\n * structured columns always fall back to their cached `ddl` regardless\n * of this flag.\n */\n materializeStructuredSchema?: boolean;\n } = {},\n ) {\n this.engine = options.engine || 'sqlite';\n this.format = options.format || 'sql';\n this.includeDown = options.includeDown ?? true;\n this.packageName = options.packageName;\n this.materializeStructuredSchema =\n options.materializeStructuredSchema ?? true;\n }\n\n /**\n * Generate a migration from a schema diff\n */\n generateFromDiff(\n diff: SchemaDiff,\n options: GenerateMigrationOptions,\n ): GeneratedMigration {\n const upStatements: string[] = [];\n const downStatements: string[] = [];\n const addedTableDownStatements: string[] = [];\n\n // Handle new tables: CREATE TABLE plus the table's indexes and triggers,\n // exactly as the migration orchestrator emits them (#2358). The differ\n // only produces `add_index` changes for tables that already exist, so\n // there is no double emission against `diff.changes`.\n const tablePlan = planForeignKeyCreation(diff.added_tables, this.engine);\n for (const schema of tablePlan.schemas) {\n upStatements.push(this.generateCreateTable(schema));\n upStatements.push(...this.generateTableIndexes(schema));\n upStatements.push(...this.generateTableTriggers(schema));\n }\n upStatements.push(...tablePlan.deferredStatements);\n if (this.includeDown || options.includeDown) {\n if (this.engine === 'sqlite' && tablePlan.cyclicTables.length > 0) {\n // SQLite cannot drop either side of a populated mutual FK cycle with\n // immediate checks. Defer checks until the transaction commits, after\n // every member has been removed.\n addedTableDownStatements.push('PRAGMA defer_foreign_keys = ON');\n }\n // PostgreSQL mutual cycles are made acyclic first; every table can then\n // be dropped in the exact reverse of the parent-first creation order.\n addedTableDownStatements.push(\n ...[...tablePlan.deferredConstraints]\n .reverse()\n .map(({ table, foreignKey }) =>\n renderDeferredForeignKeyDrop(table, foreignKey),\n ),\n );\n addedTableDownStatements.push(\n ...[...tablePlan.schemas]\n .reverse()\n .map((schema) => this.generateDropTable(schema.tableName)),\n );\n }\n\n // Handle column and index changes\n for (const change of diff.changes) {\n const { up, down } = this.generateChangeSQL(change);\n upStatements.push(...up);\n if ((this.includeDown || options.includeDown) && down.length > 0) {\n downStatements.push(...down);\n }\n }\n\n // Changes are applied after table creation, so undo them before removing\n // the new tables they may reference. The table block is itself already\n // ordered as deferred-constraint drops followed by child-first drops.\n downStatements.push(...addedTableDownStatements);\n\n // Handle dropped tables (already in SQL form if present)\n for (const tableName of diff.dropped_tables) {\n upStatements.push(this.generateDropTable(tableName));\n // No automatic DOWN for drops - too dangerous\n }\n\n return this.generateMigration(upStatements, downStatements, options);\n }\n\n /**\n * Generate a migration from explicit SQL statements\n */\n generateFromSQL(\n upStatements: string[],\n downStatements: string[],\n options: GenerateMigrationOptions,\n ): GeneratedMigration {\n return this.generateMigration(upStatements, downStatements, options);\n }\n\n /**\n * Generate an empty migration template\n */\n generateEmpty(options: GenerateMigrationOptions): GeneratedMigration {\n return this.generateMigration([], [], options);\n }\n\n /**\n * Core migration generation logic\n */\n private generateMigration(\n upStatements: string[],\n downStatements: string[],\n options: GenerateMigrationOptions,\n ): GeneratedMigration {\n const id = options.name;\n const description = options.description || `Migration: ${options.name}`;\n const format = options.format || this.format;\n const filename = `${id}.${format === 'sql' ? 'sql' : 'ts'}`;\n const checksum = computeChecksum(upStatements);\n\n let content: string;\n if (format === 'sql') {\n content = this.generateSQLFile(\n id,\n description,\n upStatements,\n downStatements,\n );\n } else {\n content = this.generateTypeScriptFile(\n id,\n description,\n upStatements,\n downStatements,\n );\n }\n\n return {\n id,\n filename,\n content,\n up: upStatements,\n down: downStatements,\n checksum,\n };\n }\n\n /**\n * Generate SQL migration file content\n */\n private generateSQLFile(\n id: string,\n description: string,\n upStatements: string[],\n downStatements: string[],\n ): string {\n const lines: string[] = [\n `-- @id: ${id}`,\n `-- @description: ${description}`,\n `-- @generated: ${new Date().toISOString()}`,\n ];\n\n if (this.packageName) {\n lines.push(`-- @package: ${this.packageName}`);\n }\n\n lines.push('');\n lines.push('-- @up');\n\n if (upStatements.length === 0) {\n lines.push('-- Add your UP statements here');\n } else {\n for (const stmt of upStatements) {\n lines.push(this.terminateStatement(stmt));\n }\n }\n\n lines.push('');\n lines.push('-- @down');\n\n if (downStatements.length === 0) {\n lines.push('-- Add your DOWN statements here');\n } else {\n for (const stmt of downStatements) {\n lines.push(this.terminateStatement(stmt));\n }\n }\n\n lines.push('');\n return lines.join('\\n');\n }\n\n /**\n * Generate TypeScript migration file content\n */\n private generateTypeScriptFile(\n id: string,\n description: string,\n upStatements: string[],\n downStatements: string[],\n ): string {\n const upStatementsStr =\n upStatements.length > 0\n ? upStatements\n .map((s) => ` await db.query(\\`${this.escapeTemplate(s)}\\`);`)\n .join('\\n')\n : ' // Add your UP statements here';\n\n const downStatementsStr =\n downStatements.length > 0\n ? downStatements\n .map((s) => ` await db.query(\\`${this.escapeTemplate(s)}\\`);`)\n .join('\\n')\n : ' // Add your DOWN statements here';\n\n return `/**\n * Migration: ${id}\n * ${description}\n *\n * Generated: ${new Date().toISOString()}\n */\n\nimport type { Migration } from '@happyvertical/smrt-core';\n\nexport default {\n id: '${id}',\n description: '${description.replace(/'/g, \"\\\\'\")}',\n version: '1.0.0',\n\n up: async (db) => {\n${upStatementsStr}\n },\n\n down: async (db) => {\n${downStatementsStr}\n },\n} satisfies Migration;\n`;\n }\n\n /**\n * Generate CREATE TABLE SQL from schema definition\n */\n private generateCreateTable(schema: SchemaDefinition): string {\n if (Object.keys(schema.columns).length === 0 && schema.ddl?.trim()) {\n // Structured materialization cannot preserve constraints or vendor SQL\n // when an older/custom manifest exposes only executable DDL. Retain the\n // only valid representation even when materialization was requested.\n return materializeManifestDDLForEngine(schema.ddl, this.engine);\n }\n\n if (!this.materializeStructuredSchema && schema.ddl?.trim()) {\n // Deprecated opt-out (#2358): the cached string is engine-neutral and\n // index-less; only bare PostgreSQL TIMESTAMP is rewritten.\n return materializeManifestDDLForEngine(schema.ddl, this.engine);\n }\n\n // The manifest's pre-generated `ddl` is deliberately engine-neutral and\n // may contain abstract TIMESTAMP/JSON/UUID types. Materialize the\n // structured columns through the selected engine strategy; executing the\n // cached string on PostgreSQL would otherwise bypass TIMESTAMPTZ and lose\n // JavaScript Date offsets (#2069). This matches the migration orchestrator\n // and also prevents JSON/REAL/UUID drift after a create.\n if (schema.ddl?.trim()) {\n // Table-level constraints that live only in a hand-authored cached\n // string cannot be represented structurally and are dropped — as\n // `db:migrate` already drops them. Say so instead of losing them\n // silently (#2358).\n const constraints = cachedDdlTableConstraints(schema.ddl);\n if (constraints.length > 0) {\n logger.warn(\n `[MigrationGenerator] cached ddl for table \"${schema.tableName}\" declares ` +\n `${constraints.length} table constraint(s) that structured columns cannot ` +\n `carry (${constraints.join('; ')}); the structured schema is authoritative ` +\n `and they are not rendered (#2358).`,\n );\n }\n }\n return getDDLStrategy(this.engine).generateCreateTable(schema);\n }\n\n /**\n * Generate the CREATE INDEX statements for a new table through the engine\n * strategy (partial `WHERE`, JSON-path targets, inline-UNIQUE engines).\n */\n private generateTableIndexes(schema: SchemaDefinition): string[] {\n return getDDLStrategy(this.engine).generateIndexes(schema);\n }\n\n /**\n * Generate the CREATE TRIGGER statements for a new table (engines without\n * trigger support return none).\n */\n private generateTableTriggers(schema: SchemaDefinition): string[] {\n return getDDLStrategy(this.engine).generateTriggers(schema);\n }\n\n /**\n * Generate DROP TABLE SQL\n */\n private generateDropTable(tableName: string): string {\n return `DROP TABLE IF EXISTS ${this.quoteIdentifier(tableName)}`;\n }\n\n /**\n * Generate SQL for a schema change\n */\n private generateChangeSQL(change: SchemaChange): {\n up: string[];\n down: string[];\n } {\n const up: string[] = [];\n const down: string[] = [];\n const sqlStatements =\n change.sqlStatements ?? (change.sql ? [change.sql] : []);\n\n switch (change.type) {\n case 'add_column':\n if (sqlStatements.length > 0) {\n up.push(...sqlStatements);\n } else if (change.column && change.name) {\n up.push(\n this.generateAddColumn(change.table, change.name, change.column),\n );\n }\n if (change.name) {\n down.push(this.generateDropColumn(change.table, change.name));\n }\n break;\n\n case 'drop_column':\n if (sqlStatements.length > 0) {\n up.push(...sqlStatements);\n } else if (change.name) {\n up.push(this.generateDropColumn(change.table, change.name));\n }\n // No automatic DOWN for drops\n break;\n\n case 'add_index':\n if (sqlStatements.length > 0) {\n up.push(...sqlStatements);\n } else if (change.index) {\n up.push(this.generateAddIndex(change.table, change.index));\n }\n if (change.name) {\n down.push(this.generateDropIndex(change.name));\n }\n break;\n\n case 'add_foreign_key':\n if (sqlStatements.length > 0) {\n up.push(...sqlStatements);\n } else if (change.advisory) {\n up.push(\n `-- ${change.advisory.severity === 'warning' ? 'WARNING' : 'NOTE'}: Foreign-key constraint ${change.table}.${change.name ?? 'unknown'}`,\n );\n if (change.advisory.message) {\n up.push(`-- ${change.advisory.message}`);\n }\n for (const suggestion of change.advisory.suggestedSql ?? []) {\n up.push(`-- suggested: ${suggestion}`);\n }\n }\n // Constraint rollback is intentionally manual: names on older live\n // databases may not match the deterministic current generator name.\n break;\n\n case 'drop_index':\n if (change.name) {\n up.push(this.generateDropIndex(change.name));\n }\n // No automatic DOWN for index drops\n break;\n\n case 'type_upgrade':\n // Type upgrades are safe conversions that SMRT can handle automatically\n // The SQL is generated by the differ based on engine-specific requirements\n if (sqlStatements.length > 0) {\n for (const sql of sqlStatements) {\n // Skip comment-only SQL (indicates a no-op migration)\n if (!sql.startsWith('--')) {\n up.push(sql);\n }\n }\n }\n // No automatic DOWN for type changes - could lose data\n break;\n\n case 'type_mismatch':\n // Type mismatches require manual intervention\n up.push(`-- WARNING: Type mismatch for ${change.table}.${change.name}`);\n up.push(\n `-- Expected: ${change.mismatch?.expected}, Actual: ${change.mismatch?.actual}`,\n );\n up.push(`-- Manual migration required`);\n break;\n\n case 'alter_column':\n // Nullability/default repairs (#2369). Executable statements come\n // from the differ (backfill + ALTER COLUMN); comment-only SQL marks\n // an in-place limitation (SQLite) and is surfaced as a note. Report-\n // only advisories (relaxations not opted into) are not emitted.\n if (change.advisory && sqlStatements.length === 0) {\n break;\n }\n for (const sql of sqlStatements) {\n up.push(sql);\n }\n // No automatic DOWN: the previous nullability/default is not\n // captured with enough fidelity to restore it safely.\n break;\n\n case 'orphan_column':\n case 'orphan_index':\n // Report-only: surface the advisory as comments so a generated\n // migration file documents the drift without acting on it.\n up.push(\n `-- ${change.advisory?.severity === 'warning' ? 'WARNING' : 'NOTE'}: ${change.type === 'orphan_column' ? 'Orphan column' : 'Orphan unique constraint'} ${change.table}.${change.name} (${change.mismatch?.actual ?? 'unknown shape'})`,\n );\n if (change.advisory?.message) {\n up.push(`-- ${change.advisory.message}`);\n }\n for (const suggestion of change.advisory?.suggestedSql ?? []) {\n up.push(`-- suggested: ${suggestion}`);\n }\n break;\n\n default:\n // Unknown change type\n break;\n }\n\n return { up, down };\n }\n\n /**\n * Generate ADD COLUMN SQL\n */\n private generateAddColumn(\n tableName: string,\n colName: string,\n colDef: import('../schema/types.js').ColumnDefinition,\n ): string {\n const parts: string[] = [this.quoteIdentifier(colName), colDef.type];\n\n if (colDef.notNull) {\n parts.push('NOT NULL');\n }\n if (colDef.unique) {\n parts.push('UNIQUE');\n }\n if (colDef.defaultValue !== undefined) {\n parts.push(`DEFAULT ${this.formatDefaultValue(colDef.defaultValue)}`);\n }\n\n return `ALTER TABLE ${this.quoteIdentifier(tableName)} ADD COLUMN ${parts.join(' ')}`;\n }\n\n /**\n * Generate DROP COLUMN SQL\n */\n private generateDropColumn(tableName: string, colName: string): string {\n return `ALTER TABLE ${this.quoteIdentifier(tableName)} DROP COLUMN ${this.quoteIdentifier(colName)}`;\n }\n\n /**\n * Generate ADD INDEX SQL\n */\n private generateAddIndex(\n tableName: string,\n index: import('../schema/types.js').IndexDefinition,\n ): string {\n const uniqueStr = index.unique ? 'UNIQUE ' : '';\n const target = renderIndexTarget(index, this.engine);\n // Partial-index predicate. DuckDB (and the JSON adapter backed by it)\n // rejects partial indexes, so the declaration degrades to a full index\n // there — matching the differ and the DuckDB DDL strategy.\n const whereClause =\n this.engine !== 'duckdb' && index.where ? ` WHERE ${index.where}` : '';\n\n if (this.engine === 'postgres') {\n // PostgreSQL: use CONCURRENTLY for production safety\n return `CREATE ${uniqueStr}INDEX CONCURRENTLY IF NOT EXISTS ${this.quoteIdentifier(index.name)} ON ${this.quoteIdentifier(tableName)} (${target})${whereClause}`;\n }\n\n return `CREATE ${uniqueStr}INDEX IF NOT EXISTS ${this.quoteIdentifier(index.name)} ON ${this.quoteIdentifier(tableName)} (${target})${whereClause}`;\n }\n\n /**\n * Generate DROP INDEX SQL\n */\n private generateDropIndex(indexName: string): string {\n if (this.engine === 'postgres') {\n return `DROP INDEX CONCURRENTLY IF EXISTS ${this.quoteIdentifier(indexName)}`;\n }\n return `DROP INDEX IF EXISTS ${this.quoteIdentifier(indexName)}`;\n }\n\n /**\n * Quote a SQL identifier\n */\n private quoteIdentifier(name: string): string {\n return `\"${name.replace(/\"/g, '\"\"')}\"`;\n }\n\n /**\n * Format a default value for SQL\n */\n private formatDefaultValue(value: unknown): string {\n if (value === null) return 'NULL';\n if (typeof value === 'string') return `'${value.replace(/'/g, \"''\")}'`;\n if (typeof value === 'boolean') return value ? '1' : '0';\n if (typeof value === 'number') return String(value);\n return String(value);\n }\n\n /**\n * Terminate a statement with exactly one `;` for the SQL file format.\n *\n * Strategy-rendered statements (`generateCreateTable`, `generateIndexes`,\n * `generateTriggers`) already end with `;`, while differ-produced ALTERs do\n * not — appending unconditionally wrote `;;` (#2406 review).\n */\n private terminateStatement(stmt: string): string {\n return stmt.trimEnd().endsWith(';') ? stmt : `${stmt};`;\n }\n\n /**\n * Escape template literal content\n */\n private escapeTemplate(str: string): string {\n return str.replace(/`/g, '\\\\`').replace(/\\$/g, '\\\\$');\n }\n}\n\n/**\n * Generate a migration definition from UP and DOWN statements\n */\nexport function createMigrationDefinition(\n id: string,\n upStatements: string[],\n downStatements: string[] = [],\n options: {\n description?: string;\n version?: string;\n packageName?: string;\n sourceFile?: string;\n } = {},\n): MigrationDefinition {\n return {\n id,\n description: options.description || `Migration: ${id}`,\n version: options.version || '1.0.0',\n up: upStatements,\n down: downStatements,\n package_name: options.packageName,\n source_file: options.sourceFile,\n };\n}\n\n/**\n * Generate a sequence number for a new migration\n */\nexport function generateMigrationSequence(existingIds: string[]): string {\n const sequences = existingIds\n .map((id) => {\n const match = id.match(/^(\\d+)_/);\n return match ? parseInt(match[1], 10) : 0;\n })\n .filter((n) => !Number.isNaN(n));\n\n const maxSequence = sequences.length > 0 ? Math.max(...sequences) : 0;\n return String(maxSequence + 1).padStart(4, '0');\n}\n\n/**\n * Generate a timestamp-based migration ID\n */\nexport function generateMigrationTimestamp(): string {\n const now = new Date();\n return [\n now.getFullYear(),\n String(now.getMonth() + 1).padStart(2, '0'),\n String(now.getDate()).padStart(2, '0'),\n String(now.getHours()).padStart(2, '0'),\n String(now.getMinutes()).padStart(2, '0'),\n String(now.getSeconds()).padStart(2, '0'),\n ].join('');\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"generator.js","names":[],"sources":["../../src/migrations/generator.ts"],"sourcesContent":["/**\n * Migration Generator\n *\n * Generates migration files from schema diffs.\n * Supports both SQL and TypeScript formats.\n */\n\nimport { createLogger } from '@happyvertical/logger';\nimport { getDDLStrategy } from '../schema/ddl/index.js';\nimport { materializeManifestDDLForEngine } from '../schema/ddl/materialize-manifest.js';\nimport {\n planForeignKeyCreation,\n renderDeferredForeignKeyDrop,\n} from '../schema/foreign-key-planner.js';\nimport { cachedDdlTableConstraints } from '../schema/manifest-schema.js';\nimport type {\n MigrationDefinition,\n SchemaChange,\n SchemaDefinition,\n SchemaDiff,\n} from '../schema/types.js';\nimport { renderIndexTarget } from '../schema/utils.js';\nimport { computeChecksum } from './checksum.js';\nimport type { DatabaseEngine } from './types.js';\n\nconst logger = createLogger({ level: 'info' });\n\n/**\n * Options for migration generation\n */\nexport interface GenerateMigrationOptions {\n /** Migration name (without sequence number) */\n name: string;\n /** Output format */\n format?: 'sql' | 'typescript';\n /** Database engine for engine-specific SQL */\n engine?: DatabaseEngine;\n /** Include generated DOWN statements */\n includeDown?: boolean;\n /** Package name for metadata */\n packageName?: string;\n /** Description for the migration */\n description?: string;\n}\n\n/**\n * Result of migration generation\n */\nexport interface GeneratedMigration {\n /** Full migration ID (e.g., '0001_add_users') */\n id: string;\n /** Filename (e.g., '0001_add_users.sql') */\n filename: string;\n /** File content */\n content: string;\n /** UP SQL statements */\n up: string[];\n /** DOWN SQL statements (if includeDown is true) */\n down: string[];\n /** Checksum of the migration */\n checksum: string;\n}\n\n/**\n * MigrationGenerator class for creating migration files\n */\nexport class MigrationGenerator {\n private engine: DatabaseEngine;\n private format: 'sql' | 'typescript';\n private includeDown: boolean;\n private packageName?: string;\n private materializeStructuredSchema: boolean;\n\n constructor(\n options: {\n engine?: DatabaseEngine;\n format?: 'sql' | 'typescript';\n includeDown?: boolean;\n packageName?: string;\n /**\n * Render new tables from their structured columns through the engine's\n * DDL strategy (default, `true`) — the same renderer `db:migrate` uses,\n * so column types, indexes and triggers match what the differ expects\n * on the next run.\n *\n * @deprecated Passing `false` re-enables the retired cached-`ddl` string\n * path for the CREATE TABLE only (#2358): it emits the manifest's\n * engine-neutral CREATE TABLE with only bare PostgreSQL `TIMESTAMP`\n * rewritten, so `REAL`/`JSON` drift on PostgreSQL. The table's indexes\n * and triggers are still rendered through the engine strategy either\n * way — the flag never suppresses them. Schemas that expose no\n * structured columns always fall back to their cached `ddl` regardless\n * of this flag.\n */\n materializeStructuredSchema?: boolean;\n } = {},\n ) {\n this.engine = options.engine || 'sqlite';\n this.format = options.format || 'sql';\n this.includeDown = options.includeDown ?? true;\n this.packageName = options.packageName;\n this.materializeStructuredSchema =\n options.materializeStructuredSchema ?? true;\n }\n\n /**\n * Generate a migration from a schema diff\n */\n generateFromDiff(\n diff: SchemaDiff,\n options: GenerateMigrationOptions,\n ): GeneratedMigration {\n const upStatements: string[] = [];\n const downStatements: string[] = [];\n const addedTableDownStatements: string[] = [];\n\n // Handle new tables: CREATE TABLE plus the table's indexes and triggers,\n // exactly as the migration orchestrator emits them (#2358). The differ\n // only produces `add_index` changes for tables that already exist, so\n // there is no double emission against `diff.changes`.\n const tablePlan = planForeignKeyCreation(diff.added_tables, this.engine);\n for (const schema of tablePlan.schemas) {\n upStatements.push(this.generateCreateTable(schema));\n upStatements.push(...this.generateTableIndexes(schema));\n upStatements.push(...this.generateTableTriggers(schema));\n }\n upStatements.push(...tablePlan.deferredStatements);\n if (this.includeDown || options.includeDown) {\n if (this.engine === 'sqlite' && tablePlan.cyclicTables.length > 0) {\n // SQLite cannot drop either side of a populated mutual FK cycle with\n // immediate checks. Defer checks until the transaction commits, after\n // every member has been removed.\n addedTableDownStatements.push('PRAGMA defer_foreign_keys = ON');\n }\n // PostgreSQL mutual cycles are made acyclic first; every table can then\n // be dropped in the exact reverse of the parent-first creation order.\n addedTableDownStatements.push(\n ...[...tablePlan.deferredConstraints]\n .reverse()\n .map(({ table, foreignKey }) =>\n renderDeferredForeignKeyDrop(table, foreignKey),\n ),\n );\n addedTableDownStatements.push(\n ...[...tablePlan.schemas]\n .reverse()\n .map((schema) => this.generateDropTable(schema.tableName)),\n );\n }\n\n // Handle column and index changes\n for (const change of diff.changes) {\n const { up, down } = this.generateChangeSQL(change);\n upStatements.push(...up);\n if ((this.includeDown || options.includeDown) && down.length > 0) {\n downStatements.push(...down);\n }\n }\n\n // Changes are applied after table creation, so undo them before removing\n // the new tables they may reference. The table block is itself already\n // ordered as deferred-constraint drops followed by child-first drops.\n downStatements.push(...addedTableDownStatements);\n\n // Handle dropped tables (already in SQL form if present)\n for (const tableName of diff.dropped_tables) {\n upStatements.push(this.generateDropTable(tableName));\n // No automatic DOWN for drops - too dangerous\n }\n\n return this.generateMigration(upStatements, downStatements, options);\n }\n\n /**\n * Generate a migration from explicit SQL statements\n */\n generateFromSQL(\n upStatements: string[],\n downStatements: string[],\n options: GenerateMigrationOptions,\n ): GeneratedMigration {\n return this.generateMigration(upStatements, downStatements, options);\n }\n\n /**\n * Generate an empty migration template\n */\n generateEmpty(options: GenerateMigrationOptions): GeneratedMigration {\n return this.generateMigration([], [], options);\n }\n\n /**\n * Core migration generation logic\n */\n private generateMigration(\n upStatements: string[],\n downStatements: string[],\n options: GenerateMigrationOptions,\n ): GeneratedMigration {\n const id = options.name;\n const description = options.description || `Migration: ${options.name}`;\n const format = options.format || this.format;\n const filename = `${id}.${format === 'sql' ? 'sql' : 'ts'}`;\n const checksum = computeChecksum(upStatements);\n\n let content: string;\n if (format === 'sql') {\n content = this.generateSQLFile(\n id,\n description,\n upStatements,\n downStatements,\n );\n } else {\n content = this.generateTypeScriptFile(\n id,\n description,\n upStatements,\n downStatements,\n );\n }\n\n return {\n id,\n filename,\n content,\n up: upStatements,\n down: downStatements,\n checksum,\n };\n }\n\n /**\n * Generate SQL migration file content\n */\n private generateSQLFile(\n id: string,\n description: string,\n upStatements: string[],\n downStatements: string[],\n ): string {\n const lines: string[] = [\n `-- @id: ${id}`,\n `-- @description: ${description}`,\n `-- @generated: ${new Date().toISOString()}`,\n ];\n\n if (this.packageName) {\n lines.push(`-- @package: ${this.packageName}`);\n }\n\n lines.push('');\n lines.push('-- @up');\n\n if (upStatements.length === 0) {\n lines.push('-- Add your UP statements here');\n } else {\n for (const stmt of upStatements) {\n lines.push(this.terminateStatement(stmt));\n }\n }\n\n lines.push('');\n lines.push('-- @down');\n\n if (downStatements.length === 0) {\n lines.push('-- Add your DOWN statements here');\n } else {\n for (const stmt of downStatements) {\n lines.push(this.terminateStatement(stmt));\n }\n }\n\n lines.push('');\n return lines.join('\\n');\n }\n\n /**\n * Generate TypeScript migration file content\n */\n private generateTypeScriptFile(\n id: string,\n description: string,\n upStatements: string[],\n downStatements: string[],\n ): string {\n const upStatementsStr =\n upStatements.length > 0\n ? upStatements\n .map((s) => ` await db.query(\\`${this.escapeTemplate(s)}\\`);`)\n .join('\\n')\n : ' // Add your UP statements here';\n\n const downStatementsStr =\n downStatements.length > 0\n ? downStatements\n .map((s) => ` await db.query(\\`${this.escapeTemplate(s)}\\`);`)\n .join('\\n')\n : ' // Add your DOWN statements here';\n\n return `/**\n * Migration: ${id}\n * ${description}\n *\n * Generated: ${new Date().toISOString()}\n */\n\nimport type { Migration } from '@happyvertical/smrt-core';\n\nexport default {\n id: '${id}',\n description: '${description.replace(/'/g, \"\\\\'\")}',\n version: '1.0.0',\n\n up: async (db) => {\n${upStatementsStr}\n },\n\n down: async (db) => {\n${downStatementsStr}\n },\n} satisfies Migration;\n`;\n }\n\n /**\n * Generate CREATE TABLE SQL from schema definition\n */\n private generateCreateTable(schema: SchemaDefinition): string {\n if (Object.keys(schema.columns).length === 0 && schema.ddl?.trim()) {\n // Structured materialization cannot preserve constraints or vendor SQL\n // when an older/custom manifest exposes only executable DDL. Retain the\n // only valid representation even when materialization was requested.\n return materializeManifestDDLForEngine(schema.ddl, this.engine);\n }\n\n if (!this.materializeStructuredSchema && schema.ddl?.trim()) {\n // Deprecated opt-out (#2358): the cached string is engine-neutral and\n // index-less; only bare PostgreSQL TIMESTAMP is rewritten.\n return materializeManifestDDLForEngine(schema.ddl, this.engine);\n }\n\n // The manifest's pre-generated `ddl` is deliberately engine-neutral and\n // may contain abstract TIMESTAMP/JSON/UUID types. Materialize the\n // structured columns through the selected engine strategy; executing the\n // cached string on PostgreSQL would otherwise bypass TIMESTAMPTZ and lose\n // JavaScript Date offsets (#2069). This matches the migration orchestrator\n // and also prevents JSON/REAL/UUID drift after a create.\n if (schema.ddl?.trim()) {\n // Table-level constraints that live only in a hand-authored cached\n // string cannot be represented structurally and are dropped — as\n // `db:migrate` already drops them. Say so instead of losing them\n // silently (#2358).\n const constraints = cachedDdlTableConstraints(schema.ddl);\n if (constraints.length > 0) {\n logger.warn(\n `[MigrationGenerator] cached ddl for table \"${schema.tableName}\" declares ` +\n `${constraints.length} table constraint(s) that structured columns cannot ` +\n `carry (${constraints.join('; ')}); the structured schema is authoritative ` +\n `and they are not rendered (#2358).`,\n );\n }\n }\n return getDDLStrategy(this.engine).generateCreateTable(schema);\n }\n\n /**\n * Generate the CREATE INDEX statements for a new table through the engine\n * strategy (partial `WHERE`, JSON-path targets, inline-UNIQUE engines).\n */\n private generateTableIndexes(schema: SchemaDefinition): string[] {\n return getDDLStrategy(this.engine).generateIndexes(schema);\n }\n\n /**\n * Generate the CREATE TRIGGER statements for a new table (engines without\n * trigger support return none).\n */\n private generateTableTriggers(schema: SchemaDefinition): string[] {\n return getDDLStrategy(this.engine).generateTriggers(schema);\n }\n\n /**\n * Generate DROP TABLE SQL\n */\n private generateDropTable(tableName: string): string {\n return `DROP TABLE IF EXISTS ${this.quoteIdentifier(tableName)}`;\n }\n\n /**\n * Generate SQL for a schema change\n */\n private generateChangeSQL(change: SchemaChange): {\n up: string[];\n down: string[];\n } {\n const up: string[] = [];\n const down: string[] = [];\n const sqlStatements =\n change.sqlStatements ?? (change.sql ? [change.sql] : []);\n\n switch (change.type) {\n case 'add_column':\n if (sqlStatements.length > 0) {\n up.push(...sqlStatements);\n } else if (change.column && change.name) {\n up.push(\n this.generateAddColumn(change.table, change.name, change.column),\n );\n }\n if (change.name) {\n down.push(this.generateDropColumn(change.table, change.name));\n }\n break;\n\n case 'drop_column':\n if (sqlStatements.length > 0) {\n up.push(...sqlStatements);\n } else if (change.name) {\n up.push(this.generateDropColumn(change.table, change.name));\n }\n // No automatic DOWN for drops\n break;\n\n case 'add_index':\n if (sqlStatements.length > 0) {\n up.push(...sqlStatements);\n } else if (change.index) {\n up.push(this.generateAddIndex(change.table, change.index));\n }\n if (change.name) {\n down.push(this.generateDropIndex(change.name));\n }\n break;\n\n case 'add_foreign_key':\n if (sqlStatements.length > 0) {\n up.push(...sqlStatements);\n } else if (change.advisory) {\n up.push(\n `-- ${change.advisory.severity === 'warning' ? 'WARNING' : 'NOTE'}: Foreign-key constraint ${change.table}.${change.name ?? 'unknown'}`,\n );\n if (change.advisory.message) {\n up.push(`-- ${change.advisory.message}`);\n }\n for (const suggestion of change.advisory.suggestedSql ?? []) {\n up.push(`-- suggested: ${suggestion}`);\n }\n }\n // Constraint rollback is intentionally manual: names on older live\n // databases may not match the deterministic current generator name.\n break;\n\n case 'drop_foreign_key':\n if (sqlStatements.length > 0) {\n up.push(...sqlStatements);\n } else if (change.advisory) {\n up.push(\n `-- ${change.advisory.severity === 'warning' ? 'WARNING' : 'NOTE'}: Foreign-key constraint ${change.table}.${change.name ?? 'unknown'}`,\n );\n up.push(`-- ${change.advisory.message}`);\n }\n // Re-adding a removed constraint requires a fresh orphan check and is\n // therefore intentionally not generated as an automatic rollback.\n break;\n\n case 'drop_index':\n if (change.name) {\n up.push(this.generateDropIndex(change.name));\n }\n // No automatic DOWN for index drops\n break;\n\n case 'type_upgrade':\n // Type upgrades are safe conversions that SMRT can handle automatically\n // The SQL is generated by the differ based on engine-specific requirements\n if (sqlStatements.length > 0) {\n for (const sql of sqlStatements) {\n // Skip comment-only SQL (indicates a no-op migration)\n if (!sql.startsWith('--')) {\n up.push(sql);\n }\n }\n }\n // No automatic DOWN for type changes - could lose data\n break;\n\n case 'type_mismatch':\n // Type mismatches require manual intervention\n up.push(`-- WARNING: Type mismatch for ${change.table}.${change.name}`);\n up.push(\n `-- Expected: ${change.mismatch?.expected}, Actual: ${change.mismatch?.actual}`,\n );\n up.push(`-- Manual migration required`);\n break;\n\n case 'alter_column':\n // Nullability/default repairs (#2369). Executable statements come\n // from the differ (backfill + ALTER COLUMN); comment-only SQL marks\n // an in-place limitation (SQLite) and is surfaced as a note. Report-\n // only advisories (relaxations not opted into) are not emitted.\n if (change.advisory && sqlStatements.length === 0) {\n break;\n }\n for (const sql of sqlStatements) {\n up.push(sql);\n }\n // No automatic DOWN: the previous nullability/default is not\n // captured with enough fidelity to restore it safely.\n break;\n\n case 'orphan_column':\n case 'orphan_index':\n // Report-only: surface the advisory as comments so a generated\n // migration file documents the drift without acting on it.\n up.push(\n `-- ${change.advisory?.severity === 'warning' ? 'WARNING' : 'NOTE'}: ${change.type === 'orphan_column' ? 'Orphan column' : 'Orphan unique constraint'} ${change.table}.${change.name} (${change.mismatch?.actual ?? 'unknown shape'})`,\n );\n if (change.advisory?.message) {\n up.push(`-- ${change.advisory.message}`);\n }\n for (const suggestion of change.advisory?.suggestedSql ?? []) {\n up.push(`-- suggested: ${suggestion}`);\n }\n break;\n\n default:\n // Unknown change type\n break;\n }\n\n return { up, down };\n }\n\n /**\n * Generate ADD COLUMN SQL\n */\n private generateAddColumn(\n tableName: string,\n colName: string,\n colDef: import('../schema/types.js').ColumnDefinition,\n ): string {\n const parts: string[] = [this.quoteIdentifier(colName), colDef.type];\n\n if (colDef.notNull) {\n parts.push('NOT NULL');\n }\n if (colDef.unique) {\n parts.push('UNIQUE');\n }\n if (colDef.defaultValue !== undefined) {\n parts.push(`DEFAULT ${this.formatDefaultValue(colDef.defaultValue)}`);\n }\n\n return `ALTER TABLE ${this.quoteIdentifier(tableName)} ADD COLUMN ${parts.join(' ')}`;\n }\n\n /**\n * Generate DROP COLUMN SQL\n */\n private generateDropColumn(tableName: string, colName: string): string {\n return `ALTER TABLE ${this.quoteIdentifier(tableName)} DROP COLUMN ${this.quoteIdentifier(colName)}`;\n }\n\n /**\n * Generate ADD INDEX SQL\n */\n private generateAddIndex(\n tableName: string,\n index: import('../schema/types.js').IndexDefinition,\n ): string {\n const uniqueStr = index.unique ? 'UNIQUE ' : '';\n const target = renderIndexTarget(index, this.engine);\n // Partial-index predicate. DuckDB (and the JSON adapter backed by it)\n // rejects partial indexes, so the declaration degrades to a full index\n // there — matching the differ and the DuckDB DDL strategy.\n const whereClause =\n this.engine !== 'duckdb' && index.where ? ` WHERE ${index.where}` : '';\n\n if (this.engine === 'postgres') {\n // PostgreSQL: use CONCURRENTLY for production safety\n return `CREATE ${uniqueStr}INDEX CONCURRENTLY IF NOT EXISTS ${this.quoteIdentifier(index.name)} ON ${this.quoteIdentifier(tableName)} (${target})${whereClause}`;\n }\n\n return `CREATE ${uniqueStr}INDEX IF NOT EXISTS ${this.quoteIdentifier(index.name)} ON ${this.quoteIdentifier(tableName)} (${target})${whereClause}`;\n }\n\n /**\n * Generate DROP INDEX SQL\n */\n private generateDropIndex(indexName: string): string {\n if (this.engine === 'postgres') {\n return `DROP INDEX CONCURRENTLY IF EXISTS ${this.quoteIdentifier(indexName)}`;\n }\n return `DROP INDEX IF EXISTS ${this.quoteIdentifier(indexName)}`;\n }\n\n /**\n * Quote a SQL identifier\n */\n private quoteIdentifier(name: string): string {\n return `\"${name.replace(/\"/g, '\"\"')}\"`;\n }\n\n /**\n * Format a default value for SQL\n */\n private formatDefaultValue(value: unknown): string {\n if (value === null) return 'NULL';\n if (typeof value === 'string') return `'${value.replace(/'/g, \"''\")}'`;\n if (typeof value === 'boolean') return value ? '1' : '0';\n if (typeof value === 'number') return String(value);\n return String(value);\n }\n\n /**\n * Terminate a statement with exactly one `;` for the SQL file format.\n *\n * Strategy-rendered statements (`generateCreateTable`, `generateIndexes`,\n * `generateTriggers`) already end with `;`, while differ-produced ALTERs do\n * not — appending unconditionally wrote `;;` (#2406 review).\n */\n private terminateStatement(stmt: string): string {\n return stmt.trimEnd().endsWith(';') ? stmt : `${stmt};`;\n }\n\n /**\n * Escape template literal content\n */\n private escapeTemplate(str: string): string {\n return str.replace(/`/g, '\\\\`').replace(/\\$/g, '\\\\$');\n }\n}\n\n/**\n * Generate a migration definition from UP and DOWN statements\n */\nexport function createMigrationDefinition(\n id: string,\n upStatements: string[],\n downStatements: string[] = [],\n options: {\n description?: string;\n version?: string;\n packageName?: string;\n sourceFile?: string;\n } = {},\n): MigrationDefinition {\n return {\n id,\n description: options.description || `Migration: ${id}`,\n version: options.version || '1.0.0',\n up: upStatements,\n down: downStatements,\n package_name: options.packageName,\n source_file: options.sourceFile,\n };\n}\n\n/**\n * Generate a sequence number for a new migration\n */\nexport function generateMigrationSequence(existingIds: string[]): string {\n const sequences = existingIds\n .map((id) => {\n const match = id.match(/^(\\d+)_/);\n return match ? parseInt(match[1], 10) : 0;\n })\n .filter((n) => !Number.isNaN(n));\n\n const maxSequence = sequences.length > 0 ? Math.max(...sequences) : 0;\n return String(maxSequence + 1).padStart(4, '0');\n}\n\n/**\n * Generate a timestamp-based migration ID\n */\nexport function generateMigrationTimestamp(): string {\n const now = new Date();\n return [\n now.getFullYear(),\n String(now.getMonth() + 1).padStart(2, '0'),\n String(now.getDate()).padStart(2, '0'),\n String(now.getHours()).padStart(2, '0'),\n String(now.getMinutes()).padStart(2, '0'),\n String(now.getSeconds()).padStart(2, '0'),\n ].join('');\n}\n"],"mappings":";;;;;;;;;;;;;;;AAyBA,IAAM,SAAS,aAAa,EAAE,OAAO,OAAO,CAAC;;;;AAyC7C,IAAa,qBAAb,MAAgC;CAC9B;CACA;CACA;CACA;CACA;CAEA,YACE,UAqBI,CAAC,GACL;EACA,KAAK,SAAS,QAAQ,UAAU;EAChC,KAAK,SAAS,QAAQ,UAAU;EAChC,KAAK,cAAc,QAAQ,eAAe;EAC1C,KAAK,cAAc,QAAQ;EAC3B,KAAK,8BACH,QAAQ,+BAA+B;CAC3C;;;;CAKA,iBACE,MACA,SACoB;EACpB,MAAM,eAAyB,CAAC;EAChC,MAAM,iBAA2B,CAAC;EAClC,MAAM,2BAAqC,CAAC;EAM5C,MAAM,YAAY,uBAAuB,KAAK,cAAc,KAAK,MAAM;EACvE,KAAK,MAAM,UAAU,UAAU,SAAS;GACtC,aAAa,KAAK,KAAK,oBAAoB,MAAM,CAAC;GAClD,aAAa,KAAK,GAAG,KAAK,qBAAqB,MAAM,CAAC;GACtD,aAAa,KAAK,GAAG,KAAK,sBAAsB,MAAM,CAAC;EACzD;EACA,aAAa,KAAK,GAAG,UAAU,kBAAkB;EACjD,IAAI,KAAK,eAAe,QAAQ,aAAa;GAC3C,IAAI,KAAK,WAAW,YAAY,UAAU,aAAa,SAAS,GAI9D,yBAAyB,KAAK,gCAAgC;GAIhE,yBAAyB,KACvB,GAAG,CAAC,GAAG,UAAU,mBAAmB,CAAC,CAClC,QAAQ,CAAC,CACT,KAAK,EAAE,OAAO,iBACb,6BAA6B,OAAO,UAAU,CAChD,CACJ;GACA,yBAAyB,KACvB,GAAG,CAAC,GAAG,UAAU,OAAO,CAAC,CACtB,QAAQ,CAAC,CACT,KAAK,WAAW,KAAK,kBAAkB,OAAO,SAAS,CAAC,CAC7D;EACF;EAGA,KAAK,MAAM,UAAU,KAAK,SAAS;GACjC,MAAM,EAAE,IAAI,SAAS,KAAK,kBAAkB,MAAM;GAClD,aAAa,KAAK,GAAG,EAAE;GACvB,KAAK,KAAK,eAAe,QAAQ,gBAAgB,KAAK,SAAS,GAC7D,eAAe,KAAK,GAAG,IAAI;EAE/B;EAKA,eAAe,KAAK,GAAG,wBAAwB;EAG/C,KAAK,MAAM,aAAa,KAAK,gBAC3B,aAAa,KAAK,KAAK,kBAAkB,SAAS,CAAC;EAIrD,OAAO,KAAK,kBAAkB,cAAc,gBAAgB,OAAO;CACrE;;;;CAKA,gBACE,cACA,gBACA,SACoB;EACpB,OAAO,KAAK,kBAAkB,cAAc,gBAAgB,OAAO;CACrE;;;;CAKA,cAAc,SAAuD;EACnE,OAAO,KAAK,kBAAkB,CAAC,GAAG,CAAC,GAAG,OAAO;CAC/C;;;;CAKA,kBACE,cACA,gBACA,SACoB;EACpB,MAAM,KAAK,QAAQ;EACnB,MAAM,cAAc,QAAQ,eAAe,cAAc,QAAQ;EACjE,MAAM,SAAS,QAAQ,UAAU,KAAK;EACtC,MAAM,WAAW,GAAG,GAAG,GAAG,WAAW,QAAQ,QAAQ;EACrD,MAAM,WAAW,gBAAgB,YAAY;EAE7C,IAAI;EACJ,IAAI,WAAW,OACb,UAAU,KAAK,gBACb,IACA,aACA,cACA,cACF;OAEA,UAAU,KAAK,uBACb,IACA,aACA,cACA,cACF;EAGF,OAAO;GACL;GACA;GACA;GACA,IAAI;GACJ,MAAM;GACN;EACF;CACF;;;;CAKA,gBACE,IACA,aACA,cACA,gBACQ;EACR,MAAM,QAAkB;GACtB,WAAW;GACX,oBAAoB;GACpB,mCAAkB,IAAI,KAAK,EAAA,CAAE,YAAY;EAC3C;EAEA,IAAI,KAAK,aACP,MAAM,KAAK,gBAAgB,KAAK,aAAa;EAG/C,MAAM,KAAK,EAAE;EACb,MAAM,KAAK,QAAQ;EAEnB,IAAI,aAAa,WAAW,GAC1B,MAAM,KAAK,gCAAgC;OAE3C,KAAK,MAAM,QAAQ,cACjB,MAAM,KAAK,KAAK,mBAAmB,IAAI,CAAC;EAI5C,MAAM,KAAK,EAAE;EACb,MAAM,KAAK,UAAU;EAErB,IAAI,eAAe,WAAW,GAC5B,MAAM,KAAK,kCAAkC;OAE7C,KAAK,MAAM,QAAQ,gBACjB,MAAM,KAAK,KAAK,mBAAmB,IAAI,CAAC;EAI5C,MAAM,KAAK,EAAE;EACb,OAAO,MAAM,KAAK,IAAI;CACxB;;;;CAKA,uBACE,IACA,aACA,cACA,gBACQ;EACR,MAAM,kBACJ,aAAa,SAAS,IAClB,aACG,KAAK,MAAM,0BAA0B,KAAK,eAAe,CAAC,EAAE,KAAK,CAAC,CAClE,KAAK,IAAI,IACZ;EAEN,MAAM,oBACJ,eAAe,SAAS,IACpB,eACG,KAAK,MAAM,0BAA0B,KAAK,eAAe,CAAC,EAAE,KAAK,CAAC,CAClE,KAAK,IAAI,IACZ;EAEN,OAAO;gBACK,GAAG;KACd,YAAY;;iCAED,IAAI,KAAK,EAAA,CAAE,YAAY,EAAE;;;;;;SAMhC,GAAG;kBACM,YAAY,QAAQ,MAAM,KAAK,EAAE;;;;EAIjD,gBAAgB;;;;EAIhB,kBAAkB;;;;CAIlB;;;;CAKA,oBAA4B,QAAkC;EAC5D,IAAI,OAAO,KAAK,OAAO,OAAO,CAAC,CAAC,WAAW,KAAK,OAAO,KAAK,KAAK,GAI/D,OAAO,gCAAgC,OAAO,KAAK,KAAK,MAAM;EAGhE,IAAI,CAAC,KAAK,+BAA+B,OAAO,KAAK,KAAK,GAGxD,OAAO,gCAAgC,OAAO,KAAK,KAAK,MAAM;EAShE,IAAI,OAAO,KAAK,KAAK,GAAG;GAKtB,MAAM,cAAc,0BAA0B,OAAO,GAAG;GACxD,IAAI,YAAY,SAAS,GACvB,OAAO,KACL,8CAA8C,OAAO,UAAU,aAC1D,YAAY,OAAO,6DACZ,YAAY,KAAK,IAAI,EAAE,6EAErC;EAEJ;EACA,OAAO,eAAe,KAAK,MAAM,CAAC,CAAC,oBAAoB,MAAM;CAC/D;;;;;CAMA,qBAA6B,QAAoC;EAC/D,OAAO,eAAe,KAAK,MAAM,CAAC,CAAC,gBAAgB,MAAM;CAC3D;;;;;CAMA,sBAA8B,QAAoC;EAChE,OAAO,eAAe,KAAK,MAAM,CAAC,CAAC,iBAAiB,MAAM;CAC5D;;;;CAKA,kBAA0B,WAA2B;EACnD,OAAO,wBAAwB,KAAK,gBAAgB,SAAS;CAC/D;;;;CAKA,kBAA0B,QAGxB;EACA,MAAM,KAAe,CAAC;EACtB,MAAM,OAAiB,CAAC;EACxB,MAAM,gBACJ,OAAO,kBAAkB,OAAO,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;EAExD,QAAQ,OAAO,MAAf;GACE,KAAK;IACH,IAAI,cAAc,SAAS,GACzB,GAAG,KAAK,GAAG,aAAa;SACnB,IAAI,OAAO,UAAU,OAAO,MACjC,GAAG,KACD,KAAK,kBAAkB,OAAO,OAAO,OAAO,MAAM,OAAO,MAAM,CACjE;IAEF,IAAI,OAAO,MACT,KAAK,KAAK,KAAK,mBAAmB,OAAO,OAAO,OAAO,IAAI,CAAC;IAE9D;GAEF,KAAK;IACH,IAAI,cAAc,SAAS,GACzB,GAAG,KAAK,GAAG,aAAa;SACnB,IAAI,OAAO,MAChB,GAAG,KAAK,KAAK,mBAAmB,OAAO,OAAO,OAAO,IAAI,CAAC;IAG5D;GAEF,KAAK;IACH,IAAI,cAAc,SAAS,GACzB,GAAG,KAAK,GAAG,aAAa;SACnB,IAAI,OAAO,OAChB,GAAG,KAAK,KAAK,iBAAiB,OAAO,OAAO,OAAO,KAAK,CAAC;IAE3D,IAAI,OAAO,MACT,KAAK,KAAK,KAAK,kBAAkB,OAAO,IAAI,CAAC;IAE/C;GAEF,KAAK;IACH,IAAI,cAAc,SAAS,GACzB,GAAG,KAAK,GAAG,aAAa;SACnB,IAAI,OAAO,UAAU;KAC1B,GAAG,KACD,MAAM,OAAO,SAAS,aAAa,YAAY,YAAY,OAAO,2BAA2B,OAAO,MAAM,GAAG,OAAO,QAAQ,WAC9H;KACA,IAAI,OAAO,SAAS,SAClB,GAAG,KAAK,MAAM,OAAO,SAAS,SAAS;KAEzC,KAAK,MAAM,cAAc,OAAO,SAAS,gBAAgB,CAAC,GACxD,GAAG,KAAK,iBAAiB,YAAY;IAEzC;IAGA;GAEF,KAAK;IACH,IAAI,cAAc,SAAS,GACzB,GAAG,KAAK,GAAG,aAAa;SACnB,IAAI,OAAO,UAAU;KAC1B,GAAG,KACD,MAAM,OAAO,SAAS,aAAa,YAAY,YAAY,OAAO,2BAA2B,OAAO,MAAM,GAAG,OAAO,QAAQ,WAC9H;KACA,GAAG,KAAK,MAAM,OAAO,SAAS,SAAS;IACzC;IAGA;GAEF,KAAK;IACH,IAAI,OAAO,MACT,GAAG,KAAK,KAAK,kBAAkB,OAAO,IAAI,CAAC;IAG7C;GAEF,KAAK;IAGH,IAAI,cAAc,SAAS;UACpB,MAAM,OAAO,eAEhB,IAAI,CAAC,IAAI,WAAW,IAAI,GACtB,GAAG,KAAK,GAAG;IAAA;IAKjB;GAEF,KAAK;IAEH,GAAG,KAAK,iCAAiC,OAAO,MAAM,GAAG,OAAO,MAAM;IACtE,GAAG,KACD,gBAAgB,OAAO,UAAU,SAAS,YAAY,OAAO,UAAU,QACzE;IACA,GAAG,KAAK,8BAA8B;IACtC;GAEF,KAAK;IAKH,IAAI,OAAO,YAAY,cAAc,WAAW,GAC9C;IAEF,KAAK,MAAM,OAAO,eAChB,GAAG,KAAK,GAAG;IAIb;GAEF,KAAK;GACL,KAAK;IAGH,GAAG,KACD,MAAM,OAAO,UAAU,aAAa,YAAY,YAAY,OAAO,IAAI,OAAO,SAAS,kBAAkB,kBAAkB,2BAA2B,GAAG,OAAO,MAAM,GAAG,OAAO,KAAK,IAAI,OAAO,UAAU,UAAU,gBAAgB,EACtO;IACA,IAAI,OAAO,UAAU,SACnB,GAAG,KAAK,MAAM,OAAO,SAAS,SAAS;IAEzC,KAAK,MAAM,cAAc,OAAO,UAAU,gBAAgB,CAAC,GACzD,GAAG,KAAK,iBAAiB,YAAY;IAEvC;GAEF,SAEE;EACJ;EAEA,OAAO;GAAE;GAAI;EAAK;CACpB;;;;CAKA,kBACE,WACA,SACA,QACQ;EACR,MAAM,QAAkB,CAAC,KAAK,gBAAgB,OAAO,GAAG,OAAO,IAAI;EAEnE,IAAI,OAAO,SACT,MAAM,KAAK,UAAU;EAEvB,IAAI,OAAO,QACT,MAAM,KAAK,QAAQ;EAErB,IAAI,OAAO,iBAAiB,KAAA,GAC1B,MAAM,KAAK,WAAW,KAAK,mBAAmB,OAAO,YAAY,GAAG;EAGtE,OAAO,eAAe,KAAK,gBAAgB,SAAS,EAAE,cAAc,MAAM,KAAK,GAAG;CACpF;;;;CAKA,mBAA2B,WAAmB,SAAyB;EACrE,OAAO,eAAe,KAAK,gBAAgB,SAAS,EAAE,eAAe,KAAK,gBAAgB,OAAO;CACnG;;;;CAKA,iBACE,WACA,OACQ;EACR,MAAM,YAAY,MAAM,SAAS,YAAY;EAC7C,MAAM,SAAS,kBAAkB,OAAO,KAAK,MAAM;EAInD,MAAM,cACJ,KAAK,WAAW,YAAY,MAAM,QAAQ,UAAU,MAAM,UAAU;EAEtE,IAAI,KAAK,WAAW,YAElB,OAAO,UAAU,UAAU,mCAAmC,KAAK,gBAAgB,MAAM,IAAI,EAAE,MAAM,KAAK,gBAAgB,SAAS,EAAE,IAAI,OAAO,GAAG;EAGrJ,OAAO,UAAU,UAAU,sBAAsB,KAAK,gBAAgB,MAAM,IAAI,EAAE,MAAM,KAAK,gBAAgB,SAAS,EAAE,IAAI,OAAO,GAAG;CACxI;;;;CAKA,kBAA0B,WAA2B;EACnD,IAAI,KAAK,WAAW,YAClB,OAAO,qCAAqC,KAAK,gBAAgB,SAAS;EAE5E,OAAO,wBAAwB,KAAK,gBAAgB,SAAS;CAC/D;;;;CAKA,gBAAwB,MAAsB;EAC5C,OAAO,IAAI,KAAK,QAAQ,MAAM,MAAI,EAAE;CACtC;;;;CAKA,mBAA2B,OAAwB;EACjD,IAAI,UAAU,MAAM,OAAO;EAC3B,IAAI,OAAO,UAAU,UAAU,OAAO,IAAI,MAAM,QAAQ,MAAM,IAAI,EAAE;EACpE,IAAI,OAAO,UAAU,WAAW,OAAO,QAAQ,MAAM;EACrD,IAAI,OAAO,UAAU,UAAU,OAAO,OAAO,KAAK;EAClD,OAAO,OAAO,KAAK;CACrB;;;;;;;;CASA,mBAA2B,MAAsB;EAC/C,OAAO,KAAK,QAAQ,CAAC,CAAC,SAAS,GAAG,IAAI,OAAO,GAAG,KAAK;CACvD;;;;CAKA,eAAuB,KAAqB;EAC1C,OAAO,IAAI,QAAQ,MAAM,KAAK,CAAC,CAAC,QAAQ,OAAO,KAAK;CACtD;AACF;;;;AAKA,SAAgB,0BACd,IACA,cACA,iBAA2B,CAAC,GAC5B,UAKI,CAAC,GACgB;CACrB,OAAO;EACL;EACA,aAAa,QAAQ,eAAe,cAAc;EAClD,SAAS,QAAQ,WAAW;EAC5B,IAAI;EACJ,MAAM;EACN,cAAc,QAAQ;EACtB,aAAa,QAAQ;CACvB;AACF;;;;AAKA,SAAgB,0BAA0B,aAA+B;CACvE,MAAM,YAAY,YACf,KAAK,OAAO;EACX,MAAM,QAAQ,GAAG,MAAM,SAAS;EAChC,OAAO,QAAQ,SAAS,MAAM,IAAI,EAAE,IAAI;CAC1C,CAAC,CAAC,CACD,QAAQ,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC;CAEjC,MAAM,cAAc,UAAU,SAAS,IAAI,KAAK,IAAI,GAAG,SAAS,IAAI;CACpE,OAAO,OAAO,cAAc,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;AAChD;;;;AAKA,SAAgB,6BAAqC;CACnD,MAAM,sBAAM,IAAI,KAAK;CACrB,OAAO;EACL,IAAI,YAAY;EAChB,OAAO,IAAI,SAAS,IAAI,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;EAC1C,OAAO,IAAI,QAAQ,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;EACrC,OAAO,IAAI,SAAS,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;EACtC,OAAO,IAAI,WAAW,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;EACxC,OAAO,IAAI,WAAW,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;CAC1C,CAAC,CAAC,KAAK,EAAE;AACX"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-builder.d.ts","sourceRoot":"","sources":["../../src/registry/schema-builder.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAG3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAc7D,OAAO,KAAK,EACV,gBAAgB,EAEhB,gBAAgB,EAChB,WAAW,EACZ,MAAM,oBAAoB,CAAC;AAmL5B;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAIpE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,cAAc,GACtB,MAAM,GAAG,SAAS,CAWpB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAkB7D;
|
|
1
|
+
{"version":3,"file":"schema-builder.d.ts","sourceRoot":"","sources":["../../src/registry/schema-builder.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAG3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAc7D,OAAO,KAAK,EACV,gBAAgB,EAEhB,gBAAgB,EAChB,WAAW,EACZ,MAAM,oBAAoB,CAAC;AAmL5B;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAIpE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,cAAc,GACtB,MAAM,GAAG,SAAS,CAWpB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAkB7D;AAgZD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAC3B,MAAM,CAAC,EAAE,cAAc,GACtB,MAAM,CAAC,MAAM,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAuFxE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CA4C7E;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,EACzC,KAAK,UAAQ,EACb,MAAM,CAAC,EAAE,cAAc,GACtB,MAAM,CA2DR;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,EACpC,OAAO,CAAC,EAAE,sBAAsB,GAC/B,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAkHlC;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,eAAe,CAAC,MAAM,CAAC,GACjC,WAAW,CAqBb"}
|
|
@@ -234,7 +234,8 @@ function applyContributorForeignKeys(tableSchema, contributor) {
|
|
|
234
234
|
table: targetTable,
|
|
235
235
|
column: toSnakeCase(targetColumn),
|
|
236
236
|
onDelete: action,
|
|
237
|
-
onUpdate: "CASCADE"
|
|
237
|
+
onUpdate: "CASCADE",
|
|
238
|
+
...typeof field._meta?.constraint === "object" ? { engines: [...field._meta.constraint.engines] } : {}
|
|
238
239
|
};
|
|
239
240
|
}
|
|
240
241
|
}
|
|
@@ -565,7 +566,8 @@ function fieldsToColumns(fields, options) {
|
|
|
565
566
|
isConflictColumn: conflictColumns.has(toSnakeCase(fieldName)),
|
|
566
567
|
isTenantIdField: false
|
|
567
568
|
}).action,
|
|
568
|
-
onUpdate: fieldMeta?.onUpdate === void 0 ? "CASCADE" : requireForeignKeyAction(fieldMeta.onUpdate, `${fieldName} ON UPDATE`)
|
|
569
|
+
onUpdate: fieldMeta?.onUpdate === void 0 ? "CASCADE" : requireForeignKeyAction(fieldMeta.onUpdate, `${fieldName} ON UPDATE`),
|
|
570
|
+
...typeof fieldDef._meta?.constraint === "object" ? { engines: [...fieldDef._meta.constraint.engines] } : {}
|
|
569
571
|
};
|
|
570
572
|
}
|
|
571
573
|
columns[toSnakeCase(fieldName)] = column;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-builder.js","names":[],"sources":["../../src/registry/schema-builder.ts"],"sourcesContent":["/**\n * Schema building logic for the SMRT ObjectRegistry.\n *\n * Extracted from registry.ts as part of issue #1006.\n */\n\nimport { ObjectRegistry } from '../registry';\nimport type { FieldDefinition } from '../scanner/types.js';\nimport { conflictIndexName } from '../schema/conflict-target.js';\nimport { getDDLStrategy } from '../schema/ddl/index.js';\nimport type { DatabaseEngine } from '../schema/ddl/types.js';\nimport {\n renderForeignKeyConstraint,\n schemaForeignKeys,\n} from '../schema/foreign-key-ddl.js';\nimport {\n requireForeignKeyAction,\n resolveForeignKeyDeleteAction,\n} from '../schema/foreign-key-policy.js';\nimport { shortenIdentifier } from '../schema/index-utils.js';\nimport {\n formatDefaultValue as formatDefaultValueShared,\n quoteIdentifier,\n} from '../schema/sql-identifiers.js';\nimport type {\n ColumnDefinition,\n IndexDefinition,\n SchemaDefinition,\n SQLDataType,\n} from '../schema/types.js';\nimport { classnameToTablename, toSnakeCase } from '../utils';\nimport {\n type CollectionRegistrationLookup,\n isCollectionRegistration,\n} from './collection-resolution';\nimport { readFieldAttribute } from './manifest-field-merge';\nimport { findClass } from './name-resolver';\nimport { getClasses, getCollectionTableNames } from './shared-state';\nimport type { RegisteredClass } from './types';\n\ntype ForeignKeyAction = NonNullable<\n NonNullable<ColumnDefinition['foreignKey']>['onDelete']\n>;\n\nconst collectionRegistrationLookup: CollectionRegistrationLookup = {\n findClass,\n findClassInPackage: (packageName, className) =>\n ObjectRegistry.getClassInPackage(packageName, className),\n getInheritanceChain: (className) =>\n ObjectRegistry.getInheritanceChain(className),\n};\n\nfunction applyDecoratorSqlTypeOverrides(\n className: string,\n columns: Record<string, ColumnDefinition>,\n): Record<string, ColumnDefinition> {\n const decorators = ObjectRegistry.getFieldDecorators(className);\n if (!decorators.size) {\n return columns;\n }\n\n for (const [fieldName, options] of decorators) {\n if (!options?.sqlType) {\n continue;\n }\n\n const columnName = toSnakeCase(fieldName);\n const existing = columns[columnName];\n if (!existing) {\n continue;\n }\n\n const referenceKind = getReferenceKind(options as FieldDefinition);\n columns[columnName] = {\n ...existing,\n type: String(options.sqlType).toUpperCase() as SQLDataType,\n ...(referenceKind ? { referenceKind } : {}),\n };\n }\n\n return columns;\n}\n\nfunction mergeRuntimeFieldColumns(\n className: string,\n schemaColumns: Record<string, ColumnDefinition> | undefined,\n fields: Map<string, FieldDefinition>,\n options?: FieldsToColumnsOptions,\n): Record<string, ColumnDefinition> {\n const columnsToUse = { ...(schemaColumns || {}) };\n\n if (fields.size > 0) {\n const fieldColumns = fieldsToColumns(fields, options);\n for (const [columnName, columnDef] of Object.entries(fieldColumns)) {\n if (!columnsToUse[columnName]) {\n columnsToUse[columnName] = columnDef;\n }\n }\n\n for (const [fieldName, fieldDef] of fields) {\n const columnName = toSnakeCase(fieldName);\n const existing = columnsToUse[columnName];\n if (!existing) {\n continue;\n }\n\n const sqlType =\n fieldDef._meta?.sqlType ||\n (fieldDef as unknown as { sqlType?: string }).sqlType;\n const referenceKind = getReferenceKind(fieldDef);\n columnsToUse[columnName] = {\n ...existing,\n ...(sqlType\n ? { type: String(sqlType).toUpperCase() as SQLDataType }\n : {}),\n ...(referenceKind ? { referenceKind } : {}),\n };\n }\n }\n\n applyDecoratorSqlTypeOverrides(className, columnsToUse);\n return columnsToUse;\n}\n\nfunction getReferenceKind(\n fieldDef: FieldDefinition,\n): ColumnDefinition['referenceKind'] | undefined {\n if (\n (\n fieldDef as unknown as {\n __tenancy?: { isTenantIdField?: boolean };\n }\n ).__tenancy?.isTenantIdField ||\n fieldDef._meta?.__tenancy?.isTenantIdField\n ) {\n return 'tenantId';\n }\n\n if (fieldDef.type === 'foreignKey') {\n return 'foreignKey';\n }\n\n if (fieldDef.type === 'crossPackageRef') {\n return 'crossPackageRef';\n }\n\n return undefined;\n}\n\nfunction shouldEmitDefault(\n fieldDef: FieldDefinition,\n sqlType: SQLDataType,\n defaultValue: unknown,\n) {\n return !(\n getReferenceKind(fieldDef) === 'tenantId' &&\n sqlType === 'UUID' &&\n defaultValue === ''\n );\n}\n\n/**\n * Fallback base columns for a table whose contributing class carries no\n * manifest columns.\n *\n * These mirror what the manifest schema generators emit for the same table\n * (`generateSchemaFromManifest` / `generateSTISchemaFromManifest`), so a table\n * assembled from runtime field metadata alone has the same NOT NULL and\n * DEFAULT shape as one assembled from a manifest. Divergence here is what made\n * the merged table shape depend on which class registered first (#2372).\n */\nfunction createBaseColumns(\n registered: {\n config?: { idType?: 'uuid' | 'text' };\n },\n isSTI: boolean,\n): Record<string, ColumnDefinition> {\n const baseColumns: Record<string, ColumnDefinition> = {\n id: {\n type: registered.config?.idType === 'text' ? 'TEXT' : 'UUID',\n primaryKey: true,\n notNull: true,\n referenceKind: 'id',\n },\n slug: { type: 'TEXT', notNull: true },\n context: { type: 'TEXT', notNull: true, defaultValue: '' },\n created_at: {\n type: 'TIMESTAMP',\n notNull: true,\n defaultValue: 'current_timestamp',\n },\n updated_at: {\n type: 'TIMESTAMP',\n notNull: true,\n defaultValue: 'current_timestamp',\n },\n };\n\n // STI tables also carry the discriminator and the meta payload column\n // (issue #690: db:diff needs them to detect schema changes).\n if (isSTI) {\n baseColumns._meta_type = { type: 'TEXT', notNull: true };\n baseColumns._meta_data = { type: 'JSON', notNull: false };\n }\n\n return baseColumns;\n}\n\n/**\n * Get cached schema definition for a registered class\n *\n * @param name - Name of the registered class\n * @returns Schema definition or undefined if not found\n * @example\n * ```typescript\n * const schema = getSchema('Product');\n * console.log(schema.tableName); // 'products'\n * console.log(schema.ddl); // 'CREATE TABLE...'\n * ```\n */\nexport function getSchema(name: string): SchemaDefinition | undefined {\n // Issue #951: Use findClass for multi-strategy lookup\n const registered = findClass(name);\n return registered?.schema;\n}\n\n/**\n * Get SQL DDL statement for a registered class\n *\n * @param name - Name of the registered class\n * Cached manifest DDL contains abstract types and is not safe to execute on\n * PostgreSQL. New executable paths must pass a target engine. Omitting it is\n * retained for backward compatibility with consumers that only inspect the\n * legacy engine-neutral manifest string.\n *\n * @param engine - Database engine that will execute the DDL\n * @returns Engine-specific SQL DDL, or the legacy cached DDL when omitted\n * @example\n * ```typescript\n * const ddl = ObjectRegistry.getSchemaDDL('Product', 'postgres');\n * await db.query(ddl);\n * ```\n */\nexport function getSchemaDDL(\n name: string,\n engine?: DatabaseEngine,\n): string | undefined {\n const schema = getSchema(name);\n if (!schema) return undefined;\n if (engine && Object.keys(schema.columns).length === 0) {\n throw new Error(\n `Cannot materialize schema '${name}' for ${engine}: the manifest has no structured columns`,\n );\n }\n return engine\n ? getDDLStrategy(engine).generateCreateTable(schema)\n : schema.ddl;\n}\n\n/**\n * Get table name for a registered class\n *\n * @param name - Name of the registered class\n * @returns Table name or undefined if not found\n * @example\n * ```typescript\n * const tableName = getTableName('Product');\n * console.log(tableName); // 'products'\n * ```\n */\nexport function getTableName(name: string): string | undefined {\n // Check if this is a collection class - collections have their own tableName mapping\n const collectionTableName = getCollectionTableNames().get(name);\n if (collectionTableName) {\n return collectionTableName;\n }\n\n // For STI classes, return the STI base class's table name.\n // R5-canon: `getSTIBase` returns the qualified name; resolve `name`\n // (which may be simple) to its registration's qualified form for the\n // comparison.\n const stiBase = ObjectRegistry.getSTIBase(name);\n const registered = ObjectRegistry.getClass(name);\n const qualifiedName = registered?.qualifiedName ?? registered?.name ?? name;\n if (stiBase && stiBase !== qualifiedName) {\n return getSchema(stiBase)?.tableName;\n }\n return getSchema(name)?.tableName;\n}\n\nfunction withConflictIndex(\n tableName: string,\n columns: Record<string, ColumnDefinition>,\n indexes: IndexDefinition[],\n conflictColumns: string[],\n): IndexDefinition[] {\n if (\n conflictColumns.length === 0 ||\n !conflictColumns.every((column) => columns[column])\n ) {\n return indexes;\n }\n\n // `ON CONFLICT (id)` binds to the primary-key constraint itself; a second\n // unique index over the primary key column set adds nothing (#2359, A5).\n // Kept in step with SchemaGenerator.conflictColumnsArePrimaryKey().\n const primaryKeyColumns = Object.entries(columns)\n .filter(([, column]) => column.primaryKey === true)\n .map(([name]) => name);\n if (\n primaryKeyColumns.length > 0 &&\n primaryKeyColumns.length === conflictColumns.length &&\n primaryKeyColumns.every((column) => conflictColumns.includes(column))\n ) {\n return indexes;\n }\n\n const hasConflictIndex = indexes.some(\n (index) =>\n index.unique === true &&\n !index.where &&\n !index.jsonPath &&\n index.columns.length === conflictColumns.length &&\n index.columns.every((column) => conflictColumns.includes(column)),\n );\n if (hasConflictIndex) return indexes;\n\n // Same stable naming as SchemaGenerator (`schema/conflict-target.ts`), so\n // a manifest built before the runtime learned the tenant-aware default\n // (#2360) has its stale `<table>_slug_context_idx` REPLACED in place here\n // — the differ then swaps the live index by name — instead of a second,\n // suffixed unique index being appended beside the old global one.\n const tenantColumn = Object.entries(columns).find(\n ([, column]) => column.referenceKind === 'tenantId',\n )?.[0];\n // The composed name can exceed PostgreSQL's 63-byte limit on a long table.\n // Shortening here (rather than inside conflictIndexName) keeps the generator\n // paths, which run enforceIdentifierLimits() over their whole index list,\n // and this migrate leg agreeing on the final name (#2374).\n const name = shortenIdentifier(\n conflictIndexName(tableName, conflictColumns, tenantColumn),\n );\n const conflictIndex: IndexDefinition = {\n name,\n columns: conflictColumns,\n unique: true,\n };\n if (indexes.some((index) => index.name === name)) {\n return indexes.map((index) =>\n index.name === name ? conflictIndex : index,\n );\n }\n return [...indexes, conflictIndex];\n}\n\n/**\n * A registered class that contributes columns to one physical table.\n *\n * For a single-table-inheritance hierarchy every class in the hierarchy is a\n * contributor to the same table.\n */\ninterface TableContributor {\n registered: RegisteredClass;\n simpleName: string;\n qualifiedName: string;\n isSTI: boolean;\n /** True when this class is the STI base that owns the table. */\n isSTIBase: boolean;\n /** Inheritance depth; ancestors sort before descendants. */\n depth: number;\n /** Conflict-column lookup key (the STI base for STI members). */\n conflictKey: string;\n}\n\n/**\n * A merged physical table assembled from every class that contributes to it.\n */\ninterface MergedTableSchema {\n tableName: string;\n columns: Record<string, ColumnDefinition>;\n indexes: IndexDefinition[];\n ddl: string;\n isSTI: boolean;\n conflictColumns: string[];\n}\n\nfunction applyContributorForeignKeys(\n tableSchema: MergedTableSchema,\n contributor: TableContributor,\n): void {\n const conflictColumns = new Set(tableSchema.conflictColumns);\n for (const [fieldName, field] of contributor.registered.fields) {\n if (field.type !== 'foreignKey' || !field.related) continue;\n const columnName = toSnakeCase(fieldName);\n const column = tableSchema.columns[columnName];\n if (!column) continue;\n if (\n field._meta?.__tenancy?.isTenantIdField === true ||\n (\n field as FieldDefinition & {\n __tenancy?: { isTenantIdField?: boolean };\n }\n ).__tenancy?.isTenantIdField === true\n ) {\n column.foreignKey = undefined;\n continue;\n }\n const [targetName, declaredTargetColumn] = field.related.split('.');\n const targetBase = ObjectRegistry.getSTIBase(targetName) || targetName;\n const registeredTarget = ObjectRegistry.getClass(targetBase);\n const targetColumn =\n declaredTargetColumn ||\n Array.from(ObjectRegistry.getFields(targetBase).entries()).find(\n ([, targetField]) => targetField._meta?.primaryKey === true,\n )?.[0] ||\n 'id';\n if (registeredTarget && !field._meta?.sqlType) {\n const targetColumnName = toSnakeCase(targetColumn);\n const targetColumnType =\n registeredTarget.schema?.columns?.[targetColumnName]?.type ||\n (targetColumnName === 'id'\n ? registeredTarget.config.idType === 'text'\n ? 'TEXT'\n : 'UUID'\n : undefined);\n if (targetColumnType) {\n column.type = targetColumnType;\n }\n }\n if (field._meta?.constraint === false) {\n column.foreignKey = undefined;\n continue;\n }\n // A manifest may carry explicit actions that predate or differ from the\n // runtime decorator defaults. Merging runtime fields must not erase them.\n // The fallback produced from runtime fields, however, must be replaced so\n // conflict columns receive the same default CASCADE as app-side cleanup.\n const manifestForeignKey =\n contributor.registered.schema?.columns?.[columnName]?.foreignKey;\n if (manifestForeignKey) {\n column.foreignKey = manifestForeignKey;\n continue;\n }\n // A decorator-only runtime field has no authoritative physical target\n // until that target is registered. Scanner-produced manifests clear this\n // shape too; only an explicit manifest FK above may survive unloaded\n // runtime classes (the manifest-authority contract from #1120).\n if (!registeredTarget) {\n column.foreignKey = undefined;\n continue;\n }\n const targetTable =\n ObjectRegistry.getTableName(targetBase) ||\n classnameToTablename(targetBase);\n const { action } = resolveForeignKeyDeleteAction({\n declared: field._meta?.onDelete,\n isConflictColumn: conflictColumns.has(columnName),\n isTenantIdField: false,\n });\n column.foreignKey = {\n table: targetTable,\n column: toSnakeCase(targetColumn),\n onDelete: action,\n onUpdate: 'CASCADE',\n };\n }\n}\n\n/**\n * Resolve the physical table a registered class writes to.\n *\n * STI subclasses are folded onto their base's table. Returns `undefined` when\n * the class has no table (for example an unresolvable manifest stub).\n *\n * R5-canon: STI lookups use the qualified key so a colliding simple name in\n * another package cannot yield the wrong table strategy or STI base and move\n * this class's columns under that other package's table.\n */\nfunction resolveContributorTable(\n registered: RegisteredClass,\n fallbackName: string,\n): { tableName: string; contributor: TableContributor } | undefined {\n const simpleName = registered.name || fallbackName;\n const qualifiedName = registered.qualifiedName ?? simpleName;\n\n // STI subclasses loaded from external manifests can arrive with a null\n // tableName; registerFromManifest() then derives one from the class name.\n // Adopt the STI base's tableName instead so the subclass lands on the\n // shared table (issue #703).\n if (!registered.schema?.tableName && registered.extends) {\n if (ObjectRegistry.getTableStrategy(qualifiedName) === 'sti') {\n const stiBaseName = ObjectRegistry.getSTIBase(qualifiedName);\n if (stiBaseName && stiBaseName !== qualifiedName) {\n const stiBaseClass = findClass(stiBaseName);\n if (stiBaseClass?.schema?.tableName) {\n if (!registered.schema) {\n registered.schema = {\n tableName: '',\n ddl: '',\n columns: {},\n indexes: [],\n triggers: [],\n foreignKeys: [],\n dependencies: [],\n version: '',\n };\n }\n registered.schema.tableName = stiBaseClass.schema.tableName;\n }\n }\n }\n }\n\n if (!registered.schema?.tableName) {\n return undefined;\n }\n\n let tableName = registered.schema.tableName;\n const tableStrategy = ObjectRegistry.getTableStrategy(qualifiedName);\n const isSTI = tableStrategy === 'sti';\n let isSTIBase = isSTI;\n let conflictKey = qualifiedName;\n\n if (isSTI) {\n const stiBaseName = ObjectRegistry.getSTIBase(qualifiedName);\n if (stiBaseName) {\n conflictKey = stiBaseName;\n if (stiBaseName !== qualifiedName) {\n isSTIBase = false;\n // STI subclasses serialize to the base class's table even when they\n // carry a tableName of their own (issue #693).\n const stiBaseClass = findClass(stiBaseName);\n if (stiBaseClass?.schema?.tableName) {\n tableName = stiBaseClass.schema.tableName;\n }\n }\n }\n }\n\n return {\n tableName,\n contributor: {\n registered,\n simpleName,\n qualifiedName,\n isSTI,\n isSTIBase,\n depth: ObjectRegistry.getInheritanceChain(qualifiedName).length,\n conflictKey,\n },\n };\n}\n\n/**\n * Order the classes that share one table deterministically.\n *\n * The first contributor seeds the table: it supplies the base columns, the\n * `idType`, the conflict columns and the cached DDL, and its columns win every\n * merge conflict. Registration order must not decide that, or the same\n * hierarchy yields a different table shape depending on which class a manifest\n * happens to list first — child-first dropped NOT NULL and DEFAULT from the\n * base columns (#2372).\n *\n * Order: the STI base first, then ancestors before descendants, then by\n * qualified name so the result is a total order.\n */\nfunction sortTableContributors(contributors: TableContributor[]) {\n return [...contributors].sort((a, b) => {\n if (a.isSTIBase !== b.isSTIBase) {\n return a.isSTIBase ? -1 : 1;\n }\n if (a.depth !== b.depth) {\n return a.depth - b.depth;\n }\n return a.qualifiedName.localeCompare(b.qualifiedName);\n });\n}\n\n/**\n * Assemble every physical table from the classes that contribute to it.\n *\n * Shared by {@link getAllSchemas} and {@link getAllSchemasAsDefinitions} so\n * both produce the same merged shape. The result depends only on what is\n * registered, never on the order it was registered in.\n */\nfunction buildMergedTableSchemas(): Record<string, MergedTableSchema> {\n // Pass 1: group contributing classes by physical table.\n const contributorsByTable = new Map<string, TableContributor[]>();\n\n for (const [className, registered] of getClasses()) {\n // Collection classes have no table of their own; their schemas carry\n // collection properties (loaded, options, ...) rather than columns.\n if (\n isCollectionRegistration(\n className,\n registered,\n collectionRegistrationLookup,\n )\n ) {\n continue;\n }\n\n const resolved = resolveContributorTable(registered, className);\n if (!resolved) {\n continue;\n }\n\n const existing = contributorsByTable.get(resolved.tableName);\n if (existing) {\n existing.push(resolved.contributor);\n } else {\n contributorsByTable.set(resolved.tableName, [resolved.contributor]);\n }\n }\n\n // Pass 2: merge each table's contributors in deterministic order.\n const tableSchemas: Record<string, MergedTableSchema> = {};\n\n for (const [tableName, contributors] of contributorsByTable) {\n for (const contributor of sortTableContributors(contributors)) {\n const { registered, simpleName, isSTI } = contributor;\n const conflictColumns = ObjectRegistry.getConflictColumns(\n contributor.conflictKey,\n );\n\n // The manifest schema stays authoritative for the columns it defines.\n // Runtime field metadata backfills columns the manifest never had (for\n // example tenantScoped injections) and applies explicit sqlType\n // overrides, without erasing richer manifest metadata.\n const columnsToUse = mergeRuntimeFieldColumns(\n simpleName,\n registered.schema?.columns,\n registered.fields,\n { stiUnionColumns: isSTI, conflictColumns },\n );\n\n let tableSchema = tableSchemas[tableName];\n if (!tableSchema) {\n tableSchema = {\n tableName,\n columns: {\n ...createBaseColumns(registered, isSTI),\n ...columnsToUse,\n },\n indexes: [],\n ddl: registered.schema?.ddl || '',\n isSTI,\n conflictColumns,\n };\n tableSchemas[tableName] = tableSchema;\n } else {\n // Another class sharing this table (STI): add only columns the\n // seeding contributor did not already define.\n for (const [colName, colDef] of Object.entries(columnsToUse)) {\n if (!tableSchema.columns[colName]) {\n tableSchema.columns[colName] = colDef;\n }\n }\n }\n\n applyContributorForeignKeys(tableSchema, contributor);\n\n // Merge indexes, keeping the first definition of each name.\n const schemaIndexes = registered.schema?.indexes;\n if (schemaIndexes && schemaIndexes.length > 0) {\n const existingNames = new Set(\n tableSchema.indexes.map((index) => index.name),\n );\n for (const index of schemaIndexes) {\n // Legacy string-format indexes carry no columns and cannot be merged.\n if (typeof index === 'string') {\n continue;\n }\n if (!existingNames.has(index.name)) {\n tableSchema.indexes.push(index);\n existingNames.add(index.name);\n }\n }\n }\n }\n }\n\n return tableSchemas;\n}\n\n/**\n * Get all pre-generated schemas for explicit adapter bootstrap paths.\n *\n * Returns schemas in SDK SchemaProvider format for all registered classes.\n * Tooling and test helpers can pass these to `getDatabase()` when they want\n * to bootstrap schema before runtime. Core runtime no longer does this\n * implicitly.\n *\n * @returns Record of table names to schema definitions\n * @example\n * ```typescript\n * const schemas = ObjectRegistry.getAllSchemas('json');\n * const db = await getDatabase({ type: 'json', url: './data', schemas });\n * ```\n */\nexport function getAllSchemas(\n engine?: DatabaseEngine,\n): Record<string, { tableName: string; ddl: string; indexes?: string[] }> {\n // Step 1: Assemble every table from its contributing classes. The merge is\n // deterministic, so the shape does not depend on registration order (#2372).\n const tableSchemas = buildMergedTableSchemas();\n\n // Step 2: Convert to output format, regenerating DDL for merged schemas\n const schemas: Record<\n string,\n { tableName: string; ddl: string; indexes?: string[] }\n > = {};\n\n for (const [tableName, tableSchema] of Object.entries(tableSchemas)) {\n const engineIndexes = engine\n ? withConflictIndex(\n tableName,\n tableSchema.columns,\n tableSchema.indexes,\n tableSchema.conflictColumns,\n )\n : tableSchema.indexes;\n const mergedSchema: SchemaDefinition = {\n tableName,\n ddl: tableSchema.ddl,\n columns: tableSchema.columns,\n indexes: engineIndexes,\n triggers: [],\n foreignKeys: [],\n version: '',\n dependencies: [],\n };\n mergedSchema.foreignKeys = schemaForeignKeys(mergedSchema);\n mergedSchema.dependencies = mergedSchema.foreignKeys\n .map((foreignKey) => foreignKey.referencesTable)\n .filter((dependency) => dependency !== tableName);\n\n // Generate DDL from merged columns (or use original DDL if columns are empty)\n let ddl: string;\n if (Object.keys(tableSchema.columns).length === 0 && tableSchema.ddl) {\n if (engine) {\n throw new Error(\n `Cannot materialize schema '${tableName}' for ${engine}: the manifest has no structured columns`,\n );\n }\n // Preserve legacy inspection of cached engine-neutral DDL. Executable\n // target-engine paths fail above rather than returning unsafe SQL.\n ddl = tableSchema.ddl;\n } else if (Object.keys(tableSchema.columns).length === 0) {\n // Skip schemas with no columns and no original DDL\n continue;\n } else if (engine) {\n // Target-engine materialization must route the complete structured\n // schema through its strategy. In particular, DuckDB/JSON require\n // UNIQUE constraints inline for ON CONFLICT, and every strategy owns\n // CHECK/default/index-expression rendering.\n ddl = getDDLStrategy(engine).generateCreateTable(mergedSchema);\n } else {\n ddl = generateDDLFromColumns(\n tableName,\n tableSchema.columns,\n tableSchema.isSTI,\n );\n }\n\n // Convert index definitions to SQL strings for SDK compatibility\n let indexSQL: string[] | undefined;\n if (engineIndexes.length > 0) {\n indexSQL = engine\n ? getDDLStrategy(engine).generateIndexes(mergedSchema)\n : engineIndexes.map((idx) => {\n const indexType = idx.unique ? 'UNIQUE INDEX' : 'INDEX';\n const columnList = idx.columns\n .map((col) => quoteIdentifier(col))\n .join(', ');\n return `CREATE ${indexType} IF NOT EXISTS ${quoteIdentifier(\n idx.name,\n )} ON ${quoteIdentifier(tableName)} (${columnList});`;\n });\n }\n\n schemas[tableName] = {\n tableName,\n ddl,\n indexes: indexSQL,\n };\n }\n\n return schemas;\n}\n\n/**\n * Get all registered schemas as SchemaDefinition objects\n *\n * Similar to getAllSchemas(), but returns SchemaDefinition format suitable\n * for use with SchemaComparer (migrations/differ.ts).\n *\n * Key difference: Indexes are kept as IndexDefinition objects instead of\n * being converted to SQL strings.\n *\n * @returns Map of tableName to SchemaDefinition\n */\nexport function getAllSchemasAsDefinitions(): Record<string, SchemaDefinition> {\n // Step 1: Assemble every table from its contributing classes. The merge is\n // deterministic, so the shape does not depend on registration order (#2372).\n const tableSchemas = buildMergedTableSchemas();\n\n // Step 2: Convert to SchemaDefinition format\n const schemas: Record<string, SchemaDefinition> = {};\n\n for (const [tableName, tableSchema] of Object.entries(tableSchemas)) {\n if (Object.keys(tableSchema.columns).length === 0) {\n continue;\n }\n\n // Generate DDL from columns\n const ddl = generateDDLFromColumns(\n tableName,\n tableSchema.columns,\n tableSchema.isSTI,\n );\n const foreignKeys = schemaForeignKeys({\n columns: tableSchema.columns,\n foreignKeys: [],\n });\n\n schemas[tableName] = {\n tableName,\n ddl,\n columns: tableSchema.columns,\n indexes: withConflictIndex(\n tableName,\n tableSchema.columns,\n tableSchema.indexes,\n tableSchema.conflictColumns,\n ),\n triggers: [],\n foreignKeys,\n version: '',\n dependencies: foreignKeys\n .map((foreignKey) => foreignKey.referencesTable)\n .filter((dependency) => dependency !== tableName),\n };\n }\n\n return schemas;\n}\n\n/**\n * Generate DDL CREATE TABLE statement from columns\n *\n * Used internally by getAllSchemas() to regenerate DDL after merging\n * columns from multiple STI subtypes that share the same table.\n *\n * @param tableName - Name of the table\n * @param columns - Column definitions\n * @returns DDL CREATE TABLE statement\n */\nexport function generateDDLFromColumns(\n tableName: string,\n columns: Record<string, ColumnDefinition>,\n isSTI = false,\n engine?: DatabaseEngine,\n): string {\n let sql = `CREATE TABLE IF NOT EXISTS ${quoteIdentifier(tableName)} (\\n`;\n\n const columnLines: string[] = [];\n for (const [columnName, columnDef] of Object.entries(columns)) {\n const parts: string[] = [];\n\n // Column name and type\n const strategy = engine ? getDDLStrategy(engine) : undefined;\n const columnType = strategy\n ? strategy.mapType(columnDef.type)\n : columnDef.type;\n parts.push(` ${quoteIdentifier(columnName)} ${columnType}`);\n\n // Primary key\n if (columnDef.primaryKey) {\n parts.push('PRIMARY KEY');\n }\n\n // Not null constraint\n if (columnDef.notNull) {\n parts.push('NOT NULL');\n }\n\n // Unique constraint (skip if primary key already implies uniqueness)\n if (columnDef.unique && !columnDef.primaryKey) {\n parts.push('UNIQUE');\n }\n\n // Default value\n if (columnDef.defaultValue !== undefined) {\n const defaultSQL = strategy\n ? strategy.formatDefaultValue(columnDef.defaultValue, columnDef.type)\n : formatDefaultValue(columnDef.defaultValue, columnDef.type);\n parts.push(`DEFAULT ${defaultSQL}`);\n }\n\n columnLines.push(parts.join(' '));\n }\n for (const foreignKey of schemaForeignKeys({ columns, foreignKeys: [] })) {\n columnLines.push(` ${renderForeignKeyConstraint(tableName, foreignKey)}`);\n }\n\n sql += columnLines.join(',\\n');\n\n // Add UNIQUE constraint for UPSERT operations\n // For STI tables: UNIQUE(slug, context, _meta_type) - different types can have same slug+context\n // For non-STI tables: UNIQUE(slug, context)\n if (columns.slug && columns.context) {\n if (isSTI && columns._meta_type) {\n sql += ',\\n UNIQUE(slug, context, _meta_type)';\n } else {\n sql += ',\\n UNIQUE(slug, context)';\n }\n }\n\n sql += '\\n);';\n\n return sql;\n}\n\n/**\n * Format default value for SQL DDL.\n *\n * Thin wrapper over the shared, injection-safe formatter\n * (`schema/sql-identifiers.ts`) so the registry schema-builder uses the same\n * rules as the DDL strategies and schema generator: an allowlist of SQL\n * keyword/function defaults (not \"contains `(`\"), type-driven literal quoting,\n * and no folding of a literal string `\"null\"` into the SQL NULL keyword.\n *\n * @param value - Default value\n * @param type - Column SQL type\n * @returns Formatted SQL default value\n */\nexport function formatDefaultValue(value: unknown, type: string): string {\n return formatDefaultValueShared(value, type);\n}\n\n/**\n * Options for {@link fieldsToColumns}.\n */\nexport interface FieldsToColumnsOptions {\n /**\n * Shape the columns for a single-table-inheritance table.\n *\n * An STI table holds the union of every subtype's fields, so a column that\n * only one subtype declares must stay nullable — rows of sibling subtypes\n * never populate it. This matches `generateSTISchemaFromManifest`, which\n * emits `notNull: false` for every non-system column on an STI table.\n * Declared defaults are still emitted.\n */\n stiUnionColumns?: boolean;\n /**\n * Physical column names that form the table's natural conflict key.\n * Undeclared delete actions on foreign-key members use the shared CASCADE\n * default, matching app-side relationship cleanup.\n */\n conflictColumns?: readonly string[];\n}\n\n/**\n * Convert a Map of field definitions to column definitions\n *\n * Used by getAllSchemas() to generate columns from fields when a class\n * has no pre-generated schema (e.g., STI subclasses registered from manifest).\n *\n * `required`, `default`, and `description` are read from either the top level\n * or `_meta`: registry-sourced fields normalize them into `_meta`, so reading\n * only the top level dropped NOT NULL and DEFAULT for every such field\n * (#2372).\n *\n * @param fields - Map of field name to field definition\n * @param options - Table-shape options (see {@link FieldsToColumnsOptions})\n * @returns Record of column name to column definition\n * @private\n */\nexport function fieldsToColumns(\n fields: Map<string, FieldDefinition>,\n options?: FieldsToColumnsOptions,\n): Record<string, ColumnDefinition> {\n const columns: Record<string, ColumnDefinition> = {};\n const conflictColumns = new Set(\n (options?.conflictColumns ?? []).map((column) => toSnakeCase(column)),\n );\n\n for (const [fieldName, fieldDef] of fields) {\n // Skip id, timestamps - they're on the base table\n if (\n fieldName === 'id' ||\n fieldName === 'created_at' ||\n fieldName === 'updated_at' ||\n fieldName === 'slug' ||\n fieldName === 'context'\n ) {\n continue;\n }\n\n // Skip transient fields (non-persisted)\n if (fieldDef.transient || fieldDef._meta?.transient) {\n continue;\n }\n\n // Skip relationship fields that don't create columns\n // oneToMany and manyToMany are relationship metadata, not actual database columns\n if (fieldDef.type === 'oneToMany' || fieldDef.type === 'manyToMany') {\n continue;\n }\n\n // Skip meta fields - they're stored in _meta_data JSONB column\n if (fieldDef.type === 'meta') {\n continue;\n }\n\n // Map field type to SQL type\n const sqlType =\n fieldDef._meta?.sqlType ||\n (fieldDef.type === 'crossPackageRef' &&\n (fieldDef._meta?.idType === 'text' ||\n (fieldDef as unknown as { idType?: string }).idType === 'text')\n ? 'TEXT'\n : mapFieldTypeToSQL(fieldDef.type));\n const normalizedSqlType = String(sqlType).toUpperCase() as SQLDataType;\n const referenceKind = getReferenceKind(fieldDef);\n\n const required = readFieldAttribute(fieldDef, 'required');\n const defaultValue = readFieldAttribute(fieldDef, 'default');\n const description = readFieldAttribute(fieldDef, 'description');\n\n const column: ColumnDefinition = {\n type: normalizedSqlType,\n referenceKind,\n notNull: options?.stiUnionColumns\n ? false\n : fieldDef._meta?.nullable\n ? false\n : Boolean(required),\n unique: fieldDef._meta?.unique || false,\n description: description as string | undefined,\n };\n\n // Handle default values\n if (\n defaultValue !== undefined &&\n shouldEmitDefault(fieldDef, normalizedSqlType, defaultValue)\n ) {\n column.defaultValue = defaultValue;\n }\n\n // Handle foreign keys\n if (\n fieldDef.type === 'foreignKey' &&\n fieldDef.related &&\n fieldDef._meta?.constraint !== false &&\n fieldDef._meta?.__tenancy?.isTenantIdField !== true &&\n !(\n fieldDef as FieldDefinition & {\n __tenancy?: { isTenantIdField?: boolean };\n }\n ).__tenancy?.isTenantIdField\n ) {\n const [table, columnName = 'id'] = fieldDef.related.split('.');\n const fieldMeta = fieldDef._meta as\n | {\n onDelete?: ForeignKeyAction;\n onUpdate?: ForeignKeyAction;\n }\n | undefined;\n column.foreignKey = {\n table: classnameToTablename(table),\n column: columnName,\n onDelete: resolveForeignKeyDeleteAction({\n declared: fieldMeta?.onDelete,\n isConflictColumn: conflictColumns.has(toSnakeCase(fieldName)),\n isTenantIdField: false,\n }).action,\n onUpdate:\n fieldMeta?.onUpdate === undefined\n ? 'CASCADE'\n : requireForeignKeyAction(\n fieldMeta.onUpdate,\n `${fieldName} ON UPDATE`,\n ),\n };\n }\n\n // Use snake_case for column names\n columns[toSnakeCase(fieldName)] = column;\n }\n\n return columns;\n}\n\n/**\n * Map field type to SQL data type\n * @private\n */\nexport function mapFieldTypeToSQL(\n fieldType: FieldDefinition['type'],\n): SQLDataType {\n switch (fieldType) {\n case 'text':\n return 'TEXT';\n case 'integer':\n return 'INTEGER';\n case 'decimal':\n return 'REAL';\n case 'boolean':\n return 'BOOLEAN';\n case 'datetime':\n return 'TIMESTAMP';\n case 'json':\n return 'JSON';\n case 'foreignKey':\n return 'UUID';\n case 'crossPackageRef':\n return 'UUID';\n default:\n return 'TEXT';\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"schema-builder.js","names":[],"sources":["../../src/registry/schema-builder.ts"],"sourcesContent":["/**\n * Schema building logic for the SMRT ObjectRegistry.\n *\n * Extracted from registry.ts as part of issue #1006.\n */\n\nimport { ObjectRegistry } from '../registry';\nimport type { FieldDefinition } from '../scanner/types.js';\nimport { conflictIndexName } from '../schema/conflict-target.js';\nimport { getDDLStrategy } from '../schema/ddl/index.js';\nimport type { DatabaseEngine } from '../schema/ddl/types.js';\nimport {\n renderForeignKeyConstraint,\n schemaForeignKeys,\n} from '../schema/foreign-key-ddl.js';\nimport {\n requireForeignKeyAction,\n resolveForeignKeyDeleteAction,\n} from '../schema/foreign-key-policy.js';\nimport { shortenIdentifier } from '../schema/index-utils.js';\nimport {\n formatDefaultValue as formatDefaultValueShared,\n quoteIdentifier,\n} from '../schema/sql-identifiers.js';\nimport type {\n ColumnDefinition,\n IndexDefinition,\n SchemaDefinition,\n SQLDataType,\n} from '../schema/types.js';\nimport { classnameToTablename, toSnakeCase } from '../utils';\nimport {\n type CollectionRegistrationLookup,\n isCollectionRegistration,\n} from './collection-resolution';\nimport { readFieldAttribute } from './manifest-field-merge';\nimport { findClass } from './name-resolver';\nimport { getClasses, getCollectionTableNames } from './shared-state';\nimport type { RegisteredClass } from './types';\n\ntype ForeignKeyAction = NonNullable<\n NonNullable<ColumnDefinition['foreignKey']>['onDelete']\n>;\n\nconst collectionRegistrationLookup: CollectionRegistrationLookup = {\n findClass,\n findClassInPackage: (packageName, className) =>\n ObjectRegistry.getClassInPackage(packageName, className),\n getInheritanceChain: (className) =>\n ObjectRegistry.getInheritanceChain(className),\n};\n\nfunction applyDecoratorSqlTypeOverrides(\n className: string,\n columns: Record<string, ColumnDefinition>,\n): Record<string, ColumnDefinition> {\n const decorators = ObjectRegistry.getFieldDecorators(className);\n if (!decorators.size) {\n return columns;\n }\n\n for (const [fieldName, options] of decorators) {\n if (!options?.sqlType) {\n continue;\n }\n\n const columnName = toSnakeCase(fieldName);\n const existing = columns[columnName];\n if (!existing) {\n continue;\n }\n\n const referenceKind = getReferenceKind(options as FieldDefinition);\n columns[columnName] = {\n ...existing,\n type: String(options.sqlType).toUpperCase() as SQLDataType,\n ...(referenceKind ? { referenceKind } : {}),\n };\n }\n\n return columns;\n}\n\nfunction mergeRuntimeFieldColumns(\n className: string,\n schemaColumns: Record<string, ColumnDefinition> | undefined,\n fields: Map<string, FieldDefinition>,\n options?: FieldsToColumnsOptions,\n): Record<string, ColumnDefinition> {\n const columnsToUse = { ...(schemaColumns || {}) };\n\n if (fields.size > 0) {\n const fieldColumns = fieldsToColumns(fields, options);\n for (const [columnName, columnDef] of Object.entries(fieldColumns)) {\n if (!columnsToUse[columnName]) {\n columnsToUse[columnName] = columnDef;\n }\n }\n\n for (const [fieldName, fieldDef] of fields) {\n const columnName = toSnakeCase(fieldName);\n const existing = columnsToUse[columnName];\n if (!existing) {\n continue;\n }\n\n const sqlType =\n fieldDef._meta?.sqlType ||\n (fieldDef as unknown as { sqlType?: string }).sqlType;\n const referenceKind = getReferenceKind(fieldDef);\n columnsToUse[columnName] = {\n ...existing,\n ...(sqlType\n ? { type: String(sqlType).toUpperCase() as SQLDataType }\n : {}),\n ...(referenceKind ? { referenceKind } : {}),\n };\n }\n }\n\n applyDecoratorSqlTypeOverrides(className, columnsToUse);\n return columnsToUse;\n}\n\nfunction getReferenceKind(\n fieldDef: FieldDefinition,\n): ColumnDefinition['referenceKind'] | undefined {\n if (\n (\n fieldDef as unknown as {\n __tenancy?: { isTenantIdField?: boolean };\n }\n ).__tenancy?.isTenantIdField ||\n fieldDef._meta?.__tenancy?.isTenantIdField\n ) {\n return 'tenantId';\n }\n\n if (fieldDef.type === 'foreignKey') {\n return 'foreignKey';\n }\n\n if (fieldDef.type === 'crossPackageRef') {\n return 'crossPackageRef';\n }\n\n return undefined;\n}\n\nfunction shouldEmitDefault(\n fieldDef: FieldDefinition,\n sqlType: SQLDataType,\n defaultValue: unknown,\n) {\n return !(\n getReferenceKind(fieldDef) === 'tenantId' &&\n sqlType === 'UUID' &&\n defaultValue === ''\n );\n}\n\n/**\n * Fallback base columns for a table whose contributing class carries no\n * manifest columns.\n *\n * These mirror what the manifest schema generators emit for the same table\n * (`generateSchemaFromManifest` / `generateSTISchemaFromManifest`), so a table\n * assembled from runtime field metadata alone has the same NOT NULL and\n * DEFAULT shape as one assembled from a manifest. Divergence here is what made\n * the merged table shape depend on which class registered first (#2372).\n */\nfunction createBaseColumns(\n registered: {\n config?: { idType?: 'uuid' | 'text' };\n },\n isSTI: boolean,\n): Record<string, ColumnDefinition> {\n const baseColumns: Record<string, ColumnDefinition> = {\n id: {\n type: registered.config?.idType === 'text' ? 'TEXT' : 'UUID',\n primaryKey: true,\n notNull: true,\n referenceKind: 'id',\n },\n slug: { type: 'TEXT', notNull: true },\n context: { type: 'TEXT', notNull: true, defaultValue: '' },\n created_at: {\n type: 'TIMESTAMP',\n notNull: true,\n defaultValue: 'current_timestamp',\n },\n updated_at: {\n type: 'TIMESTAMP',\n notNull: true,\n defaultValue: 'current_timestamp',\n },\n };\n\n // STI tables also carry the discriminator and the meta payload column\n // (issue #690: db:diff needs them to detect schema changes).\n if (isSTI) {\n baseColumns._meta_type = { type: 'TEXT', notNull: true };\n baseColumns._meta_data = { type: 'JSON', notNull: false };\n }\n\n return baseColumns;\n}\n\n/**\n * Get cached schema definition for a registered class\n *\n * @param name - Name of the registered class\n * @returns Schema definition or undefined if not found\n * @example\n * ```typescript\n * const schema = getSchema('Product');\n * console.log(schema.tableName); // 'products'\n * console.log(schema.ddl); // 'CREATE TABLE...'\n * ```\n */\nexport function getSchema(name: string): SchemaDefinition | undefined {\n // Issue #951: Use findClass for multi-strategy lookup\n const registered = findClass(name);\n return registered?.schema;\n}\n\n/**\n * Get SQL DDL statement for a registered class\n *\n * @param name - Name of the registered class\n * Cached manifest DDL contains abstract types and is not safe to execute on\n * PostgreSQL. New executable paths must pass a target engine. Omitting it is\n * retained for backward compatibility with consumers that only inspect the\n * legacy engine-neutral manifest string.\n *\n * @param engine - Database engine that will execute the DDL\n * @returns Engine-specific SQL DDL, or the legacy cached DDL when omitted\n * @example\n * ```typescript\n * const ddl = ObjectRegistry.getSchemaDDL('Product', 'postgres');\n * await db.query(ddl);\n * ```\n */\nexport function getSchemaDDL(\n name: string,\n engine?: DatabaseEngine,\n): string | undefined {\n const schema = getSchema(name);\n if (!schema) return undefined;\n if (engine && Object.keys(schema.columns).length === 0) {\n throw new Error(\n `Cannot materialize schema '${name}' for ${engine}: the manifest has no structured columns`,\n );\n }\n return engine\n ? getDDLStrategy(engine).generateCreateTable(schema)\n : schema.ddl;\n}\n\n/**\n * Get table name for a registered class\n *\n * @param name - Name of the registered class\n * @returns Table name or undefined if not found\n * @example\n * ```typescript\n * const tableName = getTableName('Product');\n * console.log(tableName); // 'products'\n * ```\n */\nexport function getTableName(name: string): string | undefined {\n // Check if this is a collection class - collections have their own tableName mapping\n const collectionTableName = getCollectionTableNames().get(name);\n if (collectionTableName) {\n return collectionTableName;\n }\n\n // For STI classes, return the STI base class's table name.\n // R5-canon: `getSTIBase` returns the qualified name; resolve `name`\n // (which may be simple) to its registration's qualified form for the\n // comparison.\n const stiBase = ObjectRegistry.getSTIBase(name);\n const registered = ObjectRegistry.getClass(name);\n const qualifiedName = registered?.qualifiedName ?? registered?.name ?? name;\n if (stiBase && stiBase !== qualifiedName) {\n return getSchema(stiBase)?.tableName;\n }\n return getSchema(name)?.tableName;\n}\n\nfunction withConflictIndex(\n tableName: string,\n columns: Record<string, ColumnDefinition>,\n indexes: IndexDefinition[],\n conflictColumns: string[],\n): IndexDefinition[] {\n if (\n conflictColumns.length === 0 ||\n !conflictColumns.every((column) => columns[column])\n ) {\n return indexes;\n }\n\n // `ON CONFLICT (id)` binds to the primary-key constraint itself; a second\n // unique index over the primary key column set adds nothing (#2359, A5).\n // Kept in step with SchemaGenerator.conflictColumnsArePrimaryKey().\n const primaryKeyColumns = Object.entries(columns)\n .filter(([, column]) => column.primaryKey === true)\n .map(([name]) => name);\n if (\n primaryKeyColumns.length > 0 &&\n primaryKeyColumns.length === conflictColumns.length &&\n primaryKeyColumns.every((column) => conflictColumns.includes(column))\n ) {\n return indexes;\n }\n\n const hasConflictIndex = indexes.some(\n (index) =>\n index.unique === true &&\n !index.where &&\n !index.jsonPath &&\n index.columns.length === conflictColumns.length &&\n index.columns.every((column) => conflictColumns.includes(column)),\n );\n if (hasConflictIndex) return indexes;\n\n // Same stable naming as SchemaGenerator (`schema/conflict-target.ts`), so\n // a manifest built before the runtime learned the tenant-aware default\n // (#2360) has its stale `<table>_slug_context_idx` REPLACED in place here\n // — the differ then swaps the live index by name — instead of a second,\n // suffixed unique index being appended beside the old global one.\n const tenantColumn = Object.entries(columns).find(\n ([, column]) => column.referenceKind === 'tenantId',\n )?.[0];\n // The composed name can exceed PostgreSQL's 63-byte limit on a long table.\n // Shortening here (rather than inside conflictIndexName) keeps the generator\n // paths, which run enforceIdentifierLimits() over their whole index list,\n // and this migrate leg agreeing on the final name (#2374).\n const name = shortenIdentifier(\n conflictIndexName(tableName, conflictColumns, tenantColumn),\n );\n const conflictIndex: IndexDefinition = {\n name,\n columns: conflictColumns,\n unique: true,\n };\n if (indexes.some((index) => index.name === name)) {\n return indexes.map((index) =>\n index.name === name ? conflictIndex : index,\n );\n }\n return [...indexes, conflictIndex];\n}\n\n/**\n * A registered class that contributes columns to one physical table.\n *\n * For a single-table-inheritance hierarchy every class in the hierarchy is a\n * contributor to the same table.\n */\ninterface TableContributor {\n registered: RegisteredClass;\n simpleName: string;\n qualifiedName: string;\n isSTI: boolean;\n /** True when this class is the STI base that owns the table. */\n isSTIBase: boolean;\n /** Inheritance depth; ancestors sort before descendants. */\n depth: number;\n /** Conflict-column lookup key (the STI base for STI members). */\n conflictKey: string;\n}\n\n/**\n * A merged physical table assembled from every class that contributes to it.\n */\ninterface MergedTableSchema {\n tableName: string;\n columns: Record<string, ColumnDefinition>;\n indexes: IndexDefinition[];\n ddl: string;\n isSTI: boolean;\n conflictColumns: string[];\n}\n\nfunction applyContributorForeignKeys(\n tableSchema: MergedTableSchema,\n contributor: TableContributor,\n): void {\n const conflictColumns = new Set(tableSchema.conflictColumns);\n for (const [fieldName, field] of contributor.registered.fields) {\n if (field.type !== 'foreignKey' || !field.related) continue;\n const columnName = toSnakeCase(fieldName);\n const column = tableSchema.columns[columnName];\n if (!column) continue;\n if (\n field._meta?.__tenancy?.isTenantIdField === true ||\n (\n field as FieldDefinition & {\n __tenancy?: { isTenantIdField?: boolean };\n }\n ).__tenancy?.isTenantIdField === true\n ) {\n column.foreignKey = undefined;\n continue;\n }\n const [targetName, declaredTargetColumn] = field.related.split('.');\n const targetBase = ObjectRegistry.getSTIBase(targetName) || targetName;\n const registeredTarget = ObjectRegistry.getClass(targetBase);\n const targetColumn =\n declaredTargetColumn ||\n Array.from(ObjectRegistry.getFields(targetBase).entries()).find(\n ([, targetField]) => targetField._meta?.primaryKey === true,\n )?.[0] ||\n 'id';\n if (registeredTarget && !field._meta?.sqlType) {\n const targetColumnName = toSnakeCase(targetColumn);\n const targetColumnType =\n registeredTarget.schema?.columns?.[targetColumnName]?.type ||\n (targetColumnName === 'id'\n ? registeredTarget.config.idType === 'text'\n ? 'TEXT'\n : 'UUID'\n : undefined);\n if (targetColumnType) {\n column.type = targetColumnType;\n }\n }\n if (field._meta?.constraint === false) {\n column.foreignKey = undefined;\n continue;\n }\n // A manifest may carry explicit actions that predate or differ from the\n // runtime decorator defaults. Merging runtime fields must not erase them.\n // The fallback produced from runtime fields, however, must be replaced so\n // conflict columns receive the same default CASCADE as app-side cleanup.\n const manifestForeignKey =\n contributor.registered.schema?.columns?.[columnName]?.foreignKey;\n if (manifestForeignKey) {\n column.foreignKey = manifestForeignKey;\n continue;\n }\n // A decorator-only runtime field has no authoritative physical target\n // until that target is registered. Scanner-produced manifests clear this\n // shape too; only an explicit manifest FK above may survive unloaded\n // runtime classes (the manifest-authority contract from #1120).\n if (!registeredTarget) {\n column.foreignKey = undefined;\n continue;\n }\n const targetTable =\n ObjectRegistry.getTableName(targetBase) ||\n classnameToTablename(targetBase);\n const { action } = resolveForeignKeyDeleteAction({\n declared: field._meta?.onDelete,\n isConflictColumn: conflictColumns.has(columnName),\n isTenantIdField: false,\n });\n column.foreignKey = {\n table: targetTable,\n column: toSnakeCase(targetColumn),\n onDelete: action,\n onUpdate: 'CASCADE',\n ...(typeof field._meta?.constraint === 'object'\n ? { engines: [...field._meta.constraint.engines] }\n : {}),\n };\n }\n}\n\n/**\n * Resolve the physical table a registered class writes to.\n *\n * STI subclasses are folded onto their base's table. Returns `undefined` when\n * the class has no table (for example an unresolvable manifest stub).\n *\n * R5-canon: STI lookups use the qualified key so a colliding simple name in\n * another package cannot yield the wrong table strategy or STI base and move\n * this class's columns under that other package's table.\n */\nfunction resolveContributorTable(\n registered: RegisteredClass,\n fallbackName: string,\n): { tableName: string; contributor: TableContributor } | undefined {\n const simpleName = registered.name || fallbackName;\n const qualifiedName = registered.qualifiedName ?? simpleName;\n\n // STI subclasses loaded from external manifests can arrive with a null\n // tableName; registerFromManifest() then derives one from the class name.\n // Adopt the STI base's tableName instead so the subclass lands on the\n // shared table (issue #703).\n if (!registered.schema?.tableName && registered.extends) {\n if (ObjectRegistry.getTableStrategy(qualifiedName) === 'sti') {\n const stiBaseName = ObjectRegistry.getSTIBase(qualifiedName);\n if (stiBaseName && stiBaseName !== qualifiedName) {\n const stiBaseClass = findClass(stiBaseName);\n if (stiBaseClass?.schema?.tableName) {\n if (!registered.schema) {\n registered.schema = {\n tableName: '',\n ddl: '',\n columns: {},\n indexes: [],\n triggers: [],\n foreignKeys: [],\n dependencies: [],\n version: '',\n };\n }\n registered.schema.tableName = stiBaseClass.schema.tableName;\n }\n }\n }\n }\n\n if (!registered.schema?.tableName) {\n return undefined;\n }\n\n let tableName = registered.schema.tableName;\n const tableStrategy = ObjectRegistry.getTableStrategy(qualifiedName);\n const isSTI = tableStrategy === 'sti';\n let isSTIBase = isSTI;\n let conflictKey = qualifiedName;\n\n if (isSTI) {\n const stiBaseName = ObjectRegistry.getSTIBase(qualifiedName);\n if (stiBaseName) {\n conflictKey = stiBaseName;\n if (stiBaseName !== qualifiedName) {\n isSTIBase = false;\n // STI subclasses serialize to the base class's table even when they\n // carry a tableName of their own (issue #693).\n const stiBaseClass = findClass(stiBaseName);\n if (stiBaseClass?.schema?.tableName) {\n tableName = stiBaseClass.schema.tableName;\n }\n }\n }\n }\n\n return {\n tableName,\n contributor: {\n registered,\n simpleName,\n qualifiedName,\n isSTI,\n isSTIBase,\n depth: ObjectRegistry.getInheritanceChain(qualifiedName).length,\n conflictKey,\n },\n };\n}\n\n/**\n * Order the classes that share one table deterministically.\n *\n * The first contributor seeds the table: it supplies the base columns, the\n * `idType`, the conflict columns and the cached DDL, and its columns win every\n * merge conflict. Registration order must not decide that, or the same\n * hierarchy yields a different table shape depending on which class a manifest\n * happens to list first — child-first dropped NOT NULL and DEFAULT from the\n * base columns (#2372).\n *\n * Order: the STI base first, then ancestors before descendants, then by\n * qualified name so the result is a total order.\n */\nfunction sortTableContributors(contributors: TableContributor[]) {\n return [...contributors].sort((a, b) => {\n if (a.isSTIBase !== b.isSTIBase) {\n return a.isSTIBase ? -1 : 1;\n }\n if (a.depth !== b.depth) {\n return a.depth - b.depth;\n }\n return a.qualifiedName.localeCompare(b.qualifiedName);\n });\n}\n\n/**\n * Assemble every physical table from the classes that contribute to it.\n *\n * Shared by {@link getAllSchemas} and {@link getAllSchemasAsDefinitions} so\n * both produce the same merged shape. The result depends only on what is\n * registered, never on the order it was registered in.\n */\nfunction buildMergedTableSchemas(): Record<string, MergedTableSchema> {\n // Pass 1: group contributing classes by physical table.\n const contributorsByTable = new Map<string, TableContributor[]>();\n\n for (const [className, registered] of getClasses()) {\n // Collection classes have no table of their own; their schemas carry\n // collection properties (loaded, options, ...) rather than columns.\n if (\n isCollectionRegistration(\n className,\n registered,\n collectionRegistrationLookup,\n )\n ) {\n continue;\n }\n\n const resolved = resolveContributorTable(registered, className);\n if (!resolved) {\n continue;\n }\n\n const existing = contributorsByTable.get(resolved.tableName);\n if (existing) {\n existing.push(resolved.contributor);\n } else {\n contributorsByTable.set(resolved.tableName, [resolved.contributor]);\n }\n }\n\n // Pass 2: merge each table's contributors in deterministic order.\n const tableSchemas: Record<string, MergedTableSchema> = {};\n\n for (const [tableName, contributors] of contributorsByTable) {\n for (const contributor of sortTableContributors(contributors)) {\n const { registered, simpleName, isSTI } = contributor;\n const conflictColumns = ObjectRegistry.getConflictColumns(\n contributor.conflictKey,\n );\n\n // The manifest schema stays authoritative for the columns it defines.\n // Runtime field metadata backfills columns the manifest never had (for\n // example tenantScoped injections) and applies explicit sqlType\n // overrides, without erasing richer manifest metadata.\n const columnsToUse = mergeRuntimeFieldColumns(\n simpleName,\n registered.schema?.columns,\n registered.fields,\n { stiUnionColumns: isSTI, conflictColumns },\n );\n\n let tableSchema = tableSchemas[tableName];\n if (!tableSchema) {\n tableSchema = {\n tableName,\n columns: {\n ...createBaseColumns(registered, isSTI),\n ...columnsToUse,\n },\n indexes: [],\n ddl: registered.schema?.ddl || '',\n isSTI,\n conflictColumns,\n };\n tableSchemas[tableName] = tableSchema;\n } else {\n // Another class sharing this table (STI): add only columns the\n // seeding contributor did not already define.\n for (const [colName, colDef] of Object.entries(columnsToUse)) {\n if (!tableSchema.columns[colName]) {\n tableSchema.columns[colName] = colDef;\n }\n }\n }\n\n applyContributorForeignKeys(tableSchema, contributor);\n\n // Merge indexes, keeping the first definition of each name.\n const schemaIndexes = registered.schema?.indexes;\n if (schemaIndexes && schemaIndexes.length > 0) {\n const existingNames = new Set(\n tableSchema.indexes.map((index) => index.name),\n );\n for (const index of schemaIndexes) {\n // Legacy string-format indexes carry no columns and cannot be merged.\n if (typeof index === 'string') {\n continue;\n }\n if (!existingNames.has(index.name)) {\n tableSchema.indexes.push(index);\n existingNames.add(index.name);\n }\n }\n }\n }\n }\n\n return tableSchemas;\n}\n\n/**\n * Get all pre-generated schemas for explicit adapter bootstrap paths.\n *\n * Returns schemas in SDK SchemaProvider format for all registered classes.\n * Tooling and test helpers can pass these to `getDatabase()` when they want\n * to bootstrap schema before runtime. Core runtime no longer does this\n * implicitly.\n *\n * @returns Record of table names to schema definitions\n * @example\n * ```typescript\n * const schemas = ObjectRegistry.getAllSchemas('json');\n * const db = await getDatabase({ type: 'json', url: './data', schemas });\n * ```\n */\nexport function getAllSchemas(\n engine?: DatabaseEngine,\n): Record<string, { tableName: string; ddl: string; indexes?: string[] }> {\n // Step 1: Assemble every table from its contributing classes. The merge is\n // deterministic, so the shape does not depend on registration order (#2372).\n const tableSchemas = buildMergedTableSchemas();\n\n // Step 2: Convert to output format, regenerating DDL for merged schemas\n const schemas: Record<\n string,\n { tableName: string; ddl: string; indexes?: string[] }\n > = {};\n\n for (const [tableName, tableSchema] of Object.entries(tableSchemas)) {\n const engineIndexes = engine\n ? withConflictIndex(\n tableName,\n tableSchema.columns,\n tableSchema.indexes,\n tableSchema.conflictColumns,\n )\n : tableSchema.indexes;\n const mergedSchema: SchemaDefinition = {\n tableName,\n ddl: tableSchema.ddl,\n columns: tableSchema.columns,\n indexes: engineIndexes,\n triggers: [],\n foreignKeys: [],\n version: '',\n dependencies: [],\n };\n mergedSchema.foreignKeys = schemaForeignKeys(mergedSchema);\n mergedSchema.dependencies = mergedSchema.foreignKeys\n .map((foreignKey) => foreignKey.referencesTable)\n .filter((dependency) => dependency !== tableName);\n\n // Generate DDL from merged columns (or use original DDL if columns are empty)\n let ddl: string;\n if (Object.keys(tableSchema.columns).length === 0 && tableSchema.ddl) {\n if (engine) {\n throw new Error(\n `Cannot materialize schema '${tableName}' for ${engine}: the manifest has no structured columns`,\n );\n }\n // Preserve legacy inspection of cached engine-neutral DDL. Executable\n // target-engine paths fail above rather than returning unsafe SQL.\n ddl = tableSchema.ddl;\n } else if (Object.keys(tableSchema.columns).length === 0) {\n // Skip schemas with no columns and no original DDL\n continue;\n } else if (engine) {\n // Target-engine materialization must route the complete structured\n // schema through its strategy. In particular, DuckDB/JSON require\n // UNIQUE constraints inline for ON CONFLICT, and every strategy owns\n // CHECK/default/index-expression rendering.\n ddl = getDDLStrategy(engine).generateCreateTable(mergedSchema);\n } else {\n ddl = generateDDLFromColumns(\n tableName,\n tableSchema.columns,\n tableSchema.isSTI,\n );\n }\n\n // Convert index definitions to SQL strings for SDK compatibility\n let indexSQL: string[] | undefined;\n if (engineIndexes.length > 0) {\n indexSQL = engine\n ? getDDLStrategy(engine).generateIndexes(mergedSchema)\n : engineIndexes.map((idx) => {\n const indexType = idx.unique ? 'UNIQUE INDEX' : 'INDEX';\n const columnList = idx.columns\n .map((col) => quoteIdentifier(col))\n .join(', ');\n return `CREATE ${indexType} IF NOT EXISTS ${quoteIdentifier(\n idx.name,\n )} ON ${quoteIdentifier(tableName)} (${columnList});`;\n });\n }\n\n schemas[tableName] = {\n tableName,\n ddl,\n indexes: indexSQL,\n };\n }\n\n return schemas;\n}\n\n/**\n * Get all registered schemas as SchemaDefinition objects\n *\n * Similar to getAllSchemas(), but returns SchemaDefinition format suitable\n * for use with SchemaComparer (migrations/differ.ts).\n *\n * Key difference: Indexes are kept as IndexDefinition objects instead of\n * being converted to SQL strings.\n *\n * @returns Map of tableName to SchemaDefinition\n */\nexport function getAllSchemasAsDefinitions(): Record<string, SchemaDefinition> {\n // Step 1: Assemble every table from its contributing classes. The merge is\n // deterministic, so the shape does not depend on registration order (#2372).\n const tableSchemas = buildMergedTableSchemas();\n\n // Step 2: Convert to SchemaDefinition format\n const schemas: Record<string, SchemaDefinition> = {};\n\n for (const [tableName, tableSchema] of Object.entries(tableSchemas)) {\n if (Object.keys(tableSchema.columns).length === 0) {\n continue;\n }\n\n // Generate DDL from columns\n const ddl = generateDDLFromColumns(\n tableName,\n tableSchema.columns,\n tableSchema.isSTI,\n );\n const foreignKeys = schemaForeignKeys({\n columns: tableSchema.columns,\n foreignKeys: [],\n });\n\n schemas[tableName] = {\n tableName,\n ddl,\n columns: tableSchema.columns,\n indexes: withConflictIndex(\n tableName,\n tableSchema.columns,\n tableSchema.indexes,\n tableSchema.conflictColumns,\n ),\n triggers: [],\n foreignKeys,\n version: '',\n dependencies: foreignKeys\n .map((foreignKey) => foreignKey.referencesTable)\n .filter((dependency) => dependency !== tableName),\n };\n }\n\n return schemas;\n}\n\n/**\n * Generate DDL CREATE TABLE statement from columns\n *\n * Used internally by getAllSchemas() to regenerate DDL after merging\n * columns from multiple STI subtypes that share the same table.\n *\n * @param tableName - Name of the table\n * @param columns - Column definitions\n * @returns DDL CREATE TABLE statement\n */\nexport function generateDDLFromColumns(\n tableName: string,\n columns: Record<string, ColumnDefinition>,\n isSTI = false,\n engine?: DatabaseEngine,\n): string {\n let sql = `CREATE TABLE IF NOT EXISTS ${quoteIdentifier(tableName)} (\\n`;\n\n const columnLines: string[] = [];\n for (const [columnName, columnDef] of Object.entries(columns)) {\n const parts: string[] = [];\n\n // Column name and type\n const strategy = engine ? getDDLStrategy(engine) : undefined;\n const columnType = strategy\n ? strategy.mapType(columnDef.type)\n : columnDef.type;\n parts.push(` ${quoteIdentifier(columnName)} ${columnType}`);\n\n // Primary key\n if (columnDef.primaryKey) {\n parts.push('PRIMARY KEY');\n }\n\n // Not null constraint\n if (columnDef.notNull) {\n parts.push('NOT NULL');\n }\n\n // Unique constraint (skip if primary key already implies uniqueness)\n if (columnDef.unique && !columnDef.primaryKey) {\n parts.push('UNIQUE');\n }\n\n // Default value\n if (columnDef.defaultValue !== undefined) {\n const defaultSQL = strategy\n ? strategy.formatDefaultValue(columnDef.defaultValue, columnDef.type)\n : formatDefaultValue(columnDef.defaultValue, columnDef.type);\n parts.push(`DEFAULT ${defaultSQL}`);\n }\n\n columnLines.push(parts.join(' '));\n }\n for (const foreignKey of schemaForeignKeys({ columns, foreignKeys: [] })) {\n columnLines.push(` ${renderForeignKeyConstraint(tableName, foreignKey)}`);\n }\n\n sql += columnLines.join(',\\n');\n\n // Add UNIQUE constraint for UPSERT operations\n // For STI tables: UNIQUE(slug, context, _meta_type) - different types can have same slug+context\n // For non-STI tables: UNIQUE(slug, context)\n if (columns.slug && columns.context) {\n if (isSTI && columns._meta_type) {\n sql += ',\\n UNIQUE(slug, context, _meta_type)';\n } else {\n sql += ',\\n UNIQUE(slug, context)';\n }\n }\n\n sql += '\\n);';\n\n return sql;\n}\n\n/**\n * Format default value for SQL DDL.\n *\n * Thin wrapper over the shared, injection-safe formatter\n * (`schema/sql-identifiers.ts`) so the registry schema-builder uses the same\n * rules as the DDL strategies and schema generator: an allowlist of SQL\n * keyword/function defaults (not \"contains `(`\"), type-driven literal quoting,\n * and no folding of a literal string `\"null\"` into the SQL NULL keyword.\n *\n * @param value - Default value\n * @param type - Column SQL type\n * @returns Formatted SQL default value\n */\nexport function formatDefaultValue(value: unknown, type: string): string {\n return formatDefaultValueShared(value, type);\n}\n\n/**\n * Options for {@link fieldsToColumns}.\n */\nexport interface FieldsToColumnsOptions {\n /**\n * Shape the columns for a single-table-inheritance table.\n *\n * An STI table holds the union of every subtype's fields, so a column that\n * only one subtype declares must stay nullable — rows of sibling subtypes\n * never populate it. This matches `generateSTISchemaFromManifest`, which\n * emits `notNull: false` for every non-system column on an STI table.\n * Declared defaults are still emitted.\n */\n stiUnionColumns?: boolean;\n /**\n * Physical column names that form the table's natural conflict key.\n * Undeclared delete actions on foreign-key members use the shared CASCADE\n * default, matching app-side relationship cleanup.\n */\n conflictColumns?: readonly string[];\n}\n\n/**\n * Convert a Map of field definitions to column definitions\n *\n * Used by getAllSchemas() to generate columns from fields when a class\n * has no pre-generated schema (e.g., STI subclasses registered from manifest).\n *\n * `required`, `default`, and `description` are read from either the top level\n * or `_meta`: registry-sourced fields normalize them into `_meta`, so reading\n * only the top level dropped NOT NULL and DEFAULT for every such field\n * (#2372).\n *\n * @param fields - Map of field name to field definition\n * @param options - Table-shape options (see {@link FieldsToColumnsOptions})\n * @returns Record of column name to column definition\n * @private\n */\nexport function fieldsToColumns(\n fields: Map<string, FieldDefinition>,\n options?: FieldsToColumnsOptions,\n): Record<string, ColumnDefinition> {\n const columns: Record<string, ColumnDefinition> = {};\n const conflictColumns = new Set(\n (options?.conflictColumns ?? []).map((column) => toSnakeCase(column)),\n );\n\n for (const [fieldName, fieldDef] of fields) {\n // Skip id, timestamps - they're on the base table\n if (\n fieldName === 'id' ||\n fieldName === 'created_at' ||\n fieldName === 'updated_at' ||\n fieldName === 'slug' ||\n fieldName === 'context'\n ) {\n continue;\n }\n\n // Skip transient fields (non-persisted)\n if (fieldDef.transient || fieldDef._meta?.transient) {\n continue;\n }\n\n // Skip relationship fields that don't create columns\n // oneToMany and manyToMany are relationship metadata, not actual database columns\n if (fieldDef.type === 'oneToMany' || fieldDef.type === 'manyToMany') {\n continue;\n }\n\n // Skip meta fields - they're stored in _meta_data JSONB column\n if (fieldDef.type === 'meta') {\n continue;\n }\n\n // Map field type to SQL type\n const sqlType =\n fieldDef._meta?.sqlType ||\n (fieldDef.type === 'crossPackageRef' &&\n (fieldDef._meta?.idType === 'text' ||\n (fieldDef as unknown as { idType?: string }).idType === 'text')\n ? 'TEXT'\n : mapFieldTypeToSQL(fieldDef.type));\n const normalizedSqlType = String(sqlType).toUpperCase() as SQLDataType;\n const referenceKind = getReferenceKind(fieldDef);\n\n const required = readFieldAttribute(fieldDef, 'required');\n const defaultValue = readFieldAttribute(fieldDef, 'default');\n const description = readFieldAttribute(fieldDef, 'description');\n\n const column: ColumnDefinition = {\n type: normalizedSqlType,\n referenceKind,\n notNull: options?.stiUnionColumns\n ? false\n : fieldDef._meta?.nullable\n ? false\n : Boolean(required),\n unique: fieldDef._meta?.unique || false,\n description: description as string | undefined,\n };\n\n // Handle default values\n if (\n defaultValue !== undefined &&\n shouldEmitDefault(fieldDef, normalizedSqlType, defaultValue)\n ) {\n column.defaultValue = defaultValue;\n }\n\n // Handle foreign keys\n if (\n fieldDef.type === 'foreignKey' &&\n fieldDef.related &&\n fieldDef._meta?.constraint !== false &&\n fieldDef._meta?.__tenancy?.isTenantIdField !== true &&\n !(\n fieldDef as FieldDefinition & {\n __tenancy?: { isTenantIdField?: boolean };\n }\n ).__tenancy?.isTenantIdField\n ) {\n const [table, columnName = 'id'] = fieldDef.related.split('.');\n const fieldMeta = fieldDef._meta as\n | {\n onDelete?: ForeignKeyAction;\n onUpdate?: ForeignKeyAction;\n }\n | undefined;\n column.foreignKey = {\n table: classnameToTablename(table),\n column: columnName,\n onDelete: resolveForeignKeyDeleteAction({\n declared: fieldMeta?.onDelete,\n isConflictColumn: conflictColumns.has(toSnakeCase(fieldName)),\n isTenantIdField: false,\n }).action,\n onUpdate:\n fieldMeta?.onUpdate === undefined\n ? 'CASCADE'\n : requireForeignKeyAction(\n fieldMeta.onUpdate,\n `${fieldName} ON UPDATE`,\n ),\n ...(typeof fieldDef._meta?.constraint === 'object'\n ? { engines: [...fieldDef._meta.constraint.engines] }\n : {}),\n };\n }\n\n // Use snake_case for column names\n columns[toSnakeCase(fieldName)] = column;\n }\n\n return columns;\n}\n\n/**\n * Map field type to SQL data type\n * @private\n */\nexport function mapFieldTypeToSQL(\n fieldType: FieldDefinition['type'],\n): SQLDataType {\n switch (fieldType) {\n case 'text':\n return 'TEXT';\n case 'integer':\n return 'INTEGER';\n case 'decimal':\n return 'REAL';\n case 'boolean':\n return 'BOOLEAN';\n case 'datetime':\n return 'TIMESTAMP';\n case 'json':\n return 'JSON';\n case 'foreignKey':\n return 'UUID';\n case 'crossPackageRef':\n return 'UUID';\n default:\n return 'TEXT';\n }\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest-generator.d.ts","sourceRoot":"","sources":["../../src/scanner/manifest-generator.ts"],"names":[],"mappings":"AAAA;;GAEG;AA0BH,OAAO,KAAK,EAYV,UAAU,EAEV,mBAAmB,EACnB,cAAc,EAEf,MAAM,YAAY,CAAC;AAGpB,2EAA2E;AAC3E,UAAU,eAAe;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAmFD,qBAAa,iBAAiB;IAC5B;;;;;;;;;;;;OAYG;IACH,gBAAgB,CACd,WAAW,EAAE,UAAU,EAAE,EACzB,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,WAAW,CAAC,EAAE,eAAe,CAAC;QAC9B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;QAC5B,iBAAiB,CAAC,EAAE,cAAc,EAAE,CAAC;KACtC,GACA,mBAAmB;IA6GtB;;;;;;;;;;;;OAYG;IACH,qBAAqB,CACnB,QAAQ,EAAE,mBAAmB,EAC7B,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,eAAe,CAAA;KAAE,GAChE,IAAI;IA0CP;;;;;;OAMG;IACH,wBAAwB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI;IAM7D,OAAO,CAAC,uBAAuB;IAsD/B,gCAAgC,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI;IA0DrE,OAAO,CAAC,0BAA0B;IAoBlC,OAAO,CAAC,+BAA+B;IAqBvC,OAAO,CAAC,2BAA2B;IAyBnC;;;;;;;;;;OAUG;IACH,uBAAuB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI;IAiH5D;;;;;;;;;;;;OAYG;IACH,eAAe,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI;IA8EpD,sBAAsB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI;IAiC3D,0BAA0B,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI;IAO/D;;;;;;;;;;;;;;;;;;;OAmBG;IACH,wBAAwB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI;IA0B7D,OAAO,CAAC,uCAAuC;
|
|
1
|
+
{"version":3,"file":"manifest-generator.d.ts","sourceRoot":"","sources":["../../src/scanner/manifest-generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -446,7 +446,8 @@ var ManifestGenerator = class {
|
|
|
446
446
|
table: targetSchema.tableName,
|
|
447
447
|
column: targetColumn,
|
|
448
448
|
onDelete: action,
|
|
449
|
-
onUpdate: "CASCADE"
|
|
449
|
+
onUpdate: "CASCADE",
|
|
450
|
+
...typeof field._meta?.constraint === "object" ? { engines: [...field._meta.constraint.engines] } : {}
|
|
450
451
|
} : void 0
|
|
451
452
|
};
|
|
452
453
|
if (JSON.stringify(nextColumn) === JSON.stringify(sourceColumn)) continue;
|
|
@@ -494,7 +495,8 @@ var ManifestGenerator = class {
|
|
|
494
495
|
referencesTable: definition.foreignKey.table,
|
|
495
496
|
referencesColumn: definition.foreignKey.column,
|
|
496
497
|
onDelete: definition.foreignKey.onDelete,
|
|
497
|
-
onUpdate: definition.foreignKey.onUpdate
|
|
498
|
+
onUpdate: definition.foreignKey.onUpdate,
|
|
499
|
+
...definition.foreignKey.engines ? { engines: definition.foreignKey.engines } : {}
|
|
498
500
|
}] : []),
|
|
499
501
|
dependencies: Object.values(columns).flatMap((definition) => definition.foreignKey ? [definition.foreignKey.table] : []).filter((dependency) => dependency !== schema.tableName),
|
|
500
502
|
version: schema.version,
|
|
@@ -747,13 +749,21 @@ var ManifestGenerator = class {
|
|
|
747
749
|
}
|
|
748
750
|
logger.info(`[manifest-generator] Inheritance chain for ${obj.className}: ${inheritanceChain.join(" -> ")}`);
|
|
749
751
|
const mergedFields = {};
|
|
752
|
+
const mergedFieldOwners = /* @__PURE__ */ new Map();
|
|
750
753
|
const mergedMethods = {};
|
|
751
754
|
for (const ancestorName of inheritanceChain) {
|
|
752
755
|
const ancestor = objectsByName.get(ancestorName);
|
|
753
756
|
if (!ancestor) continue;
|
|
754
757
|
const ancestorIsFrameworkBase = FRAMEWORK_ABSTRACT_BASE_NAMES.has(ancestorName);
|
|
755
758
|
if (!usesSTI && !ancestorIsFrameworkBase) continue;
|
|
756
|
-
for (const [fieldName, fieldDef] of Object.entries(ancestor.fields))
|
|
759
|
+
for (const [fieldName, fieldDef] of Object.entries(ancestor.fields)) {
|
|
760
|
+
const existingOwner = mergedFieldOwners.get(fieldName);
|
|
761
|
+
const replacesFrameworkDefault = existingOwner !== void 0 && FRAMEWORK_ABSTRACT_BASE_NAMES.has(this.simpleClassName(existingOwner)) && !FRAMEWORK_ABSTRACT_BASE_NAMES.has(this.simpleClassName(ancestorName));
|
|
762
|
+
if (!existingOwner || replacesFrameworkDefault) {
|
|
763
|
+
mergedFields[fieldName] = this.normalizeFrameworkInheritedField(ancestorName, fieldName, fieldDef, obj.className);
|
|
764
|
+
mergedFieldOwners.set(fieldName, ancestorName);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
757
767
|
for (const [methodName, methodDef] of Object.entries(ancestor.methods || {})) if (!mergedMethods[methodName]) mergedMethods[methodName] = methodDef;
|
|
758
768
|
}
|
|
759
769
|
for (const [fieldName, fieldDef] of Object.entries(obj.fields)) mergedFields[fieldName] = fieldDef;
|