@open-mercato/cli 0.4.2-canary-b3b9723b95 → 0.4.2-canary-3f5eaf79f7

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.
@@ -81,22 +81,11 @@ async function loadModuleEntities(entry, resolver) {
81
81
  }
82
82
  function getMigrationsPath(entry, resolver) {
83
83
  const from = entry.from || "@open-mercato/core";
84
- let pkgModRoot;
85
- if (from === "@open-mercato/core") {
86
- pkgModRoot = path.join(resolver.getRootDir(), "packages/core/src/modules", entry.id);
87
- } else if (/^@open-mercato\//.test(from)) {
88
- const segs = from.split("/");
89
- if (segs.length > 1 && segs[1]) {
90
- pkgModRoot = path.join(resolver.getRootDir(), `packages/${segs[1]}/src/modules`, entry.id);
91
- } else {
92
- pkgModRoot = path.join(resolver.getRootDir(), "packages/core/src/modules", entry.id);
93
- }
94
- } else if (from === "@app") {
95
- pkgModRoot = path.join(resolver.getAppDir(), "src/modules", entry.id);
96
- } else {
97
- pkgModRoot = path.join(resolver.getRootDir(), "packages/core/src/modules", entry.id);
84
+ if (from === "@app") {
85
+ return path.join(resolver.getAppDir(), "src/modules", entry.id, "migrations");
98
86
  }
99
- return path.join(pkgModRoot, "migrations");
87
+ const { pkgBase } = resolver.getModulePaths(entry);
88
+ return path.join(pkgBase, "migrations");
100
89
  }
101
90
  async function dbGenerate(resolver, options = {}) {
102
91
  const modules = resolver.loadEnabledModules();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/lib/db/commands.ts"],
4
- "sourcesContent": ["import path from 'node:path'\nimport fs from 'node:fs'\nimport { MikroORM, type Logger } from '@mikro-orm/core'\nimport { Migrator } from '@mikro-orm/migrations'\nimport { PostgreSqlDriver } from '@mikro-orm/postgresql'\nimport type { PackageResolver, ModuleEntry } from '../resolver'\n\nconst QUIET_MODE = process.env.OM_CLI_QUIET === '1' || process.env.MERCATO_QUIET === '1'\nconst PROGRESS_EMOJI = ''\n\nfunction formatResult(modId: string, message: string, emoji = '\u2022') {\n return `${emoji} ${modId}: ${message}`\n}\n\nfunction createProgressRenderer(total: number) {\n const width = 20\n const normalizedTotal = total > 0 ? total : 1\n return (current: number) => {\n const clamped = Math.min(Math.max(current, 0), normalizedTotal)\n const filled = Math.round((clamped / normalizedTotal) * width)\n const bar = `${'='.repeat(filled)}${'.'.repeat(Math.max(width - filled, 0))}`\n return `[${bar}] ${clamped}/${normalizedTotal}`\n }\n}\n\nfunction createMinimalLogger(): Logger {\n return {\n log: () => {},\n error: (_namespace, message) => console.error(message),\n warn: (_namespace, message) => {\n if (!QUIET_MODE) console.warn(message)\n },\n logQuery: () => {},\n setDebugMode: () => {},\n isEnabled: () => false,\n }\n}\n\nfunction getClientUrl(): string {\n const url = process.env.DATABASE_URL\n if (!url) throw new Error('DATABASE_URL is not set')\n return url\n}\n\nfunction sortModules(mods: ModuleEntry[]): ModuleEntry[] {\n // Sort modules alphabetically since they are now isomorphic\n return mods.slice().sort((a, b) => a.id.localeCompare(b.id))\n}\n\n/**\n * Sanitizes a module ID for use in SQL identifiers (table names).\n * Replaces non-alphanumeric characters with underscores to prevent SQL injection.\n * @public Exported for testing\n */\nexport function sanitizeModuleId(modId: string): string {\n return modId.replace(/[^a-z0-9_]/gi, '_')\n}\n\n/**\n * Validates that a table name is safe for use in SQL queries.\n * @throws Error if the table name contains invalid characters.\n * @public Exported for testing\n */\nexport function validateTableName(tableName: string): void {\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tableName)) {\n throw new Error(`Invalid table name: ${tableName}. Table names must start with a letter or underscore and contain only alphanumeric characters and underscores.`)\n }\n}\n\nasync function loadModuleEntities(entry: ModuleEntry, resolver: PackageResolver): Promise<any[]> {\n const roots = resolver.getModulePaths(entry)\n const imps = resolver.getModuleImportBase(entry)\n const isAppModule = entry.from === '@app'\n const bases = [\n path.join(roots.appBase, 'data'),\n path.join(roots.pkgBase, 'data'),\n path.join(roots.appBase, 'db'),\n path.join(roots.pkgBase, 'db'),\n ]\n const candidates = ['entities.ts', 'schema.ts']\n\n for (const base of bases) {\n for (const f of candidates) {\n const p = path.join(base, f)\n if (fs.existsSync(p)) {\n const sub = path.basename(base)\n const fromApp = base.startsWith(roots.appBase)\n // For @app modules, use file:// URL since @/ alias doesn't work in Node.js runtime\n const importPath = (isAppModule && fromApp)\n ? `file://${p.replace(/\\.ts$/, '.js')}`\n : `${fromApp ? imps.appBase : imps.pkgBase}/${sub}/${f.replace(/\\.ts$/, '')}`\n try {\n const mod = await import(importPath)\n const entities = Object.values(mod).filter((v) => typeof v === 'function')\n if (entities.length) return entities as any[]\n } catch (err) {\n // For @app modules with TypeScript files, they can't be directly imported\n // Skip and let MikroORM handle entities through discovery\n if (isAppModule) continue\n throw err\n }\n }\n }\n }\n return []\n}\n\nfunction getMigrationsPath(entry: ModuleEntry, resolver: PackageResolver): string {\n const from = entry.from || '@open-mercato/core'\n let pkgModRoot: string\n\n if (from === '@open-mercato/core') {\n pkgModRoot = path.join(resolver.getRootDir(), 'packages/core/src/modules', entry.id)\n } else if (/^@open-mercato\\//.test(from)) {\n const segs = from.split('/')\n if (segs.length > 1 && segs[1]) {\n pkgModRoot = path.join(resolver.getRootDir(), `packages/${segs[1]}/src/modules`, entry.id)\n } else {\n pkgModRoot = path.join(resolver.getRootDir(), 'packages/core/src/modules', entry.id)\n }\n } else if (from === '@app') {\n // For @app modules, use the app directory not the monorepo root\n pkgModRoot = path.join(resolver.getAppDir(), 'src/modules', entry.id)\n } else {\n pkgModRoot = path.join(resolver.getRootDir(), 'packages/core/src/modules', entry.id)\n }\n\n return path.join(pkgModRoot, 'migrations')\n}\n\nexport interface DbOptions {\n quiet?: boolean\n}\n\nexport interface GreenfieldOptions extends DbOptions {\n yes: boolean\n}\n\nexport async function dbGenerate(resolver: PackageResolver, options: DbOptions = {}): Promise<void> {\n const modules = resolver.loadEnabledModules()\n const ordered = sortModules(modules)\n const results: string[] = []\n\n for (const entry of ordered) {\n const modId = entry.id\n const sanitizedModId = sanitizeModuleId(modId)\n const entities = await loadModuleEntities(entry, resolver)\n if (!entities.length) continue\n\n const migrationsPath = getMigrationsPath(entry, resolver)\n fs.mkdirSync(migrationsPath, { recursive: true })\n\n const tableName = `mikro_orm_migrations_${sanitizedModId}`\n validateTableName(tableName)\n\n const orm = await MikroORM.init<PostgreSqlDriver>({\n driver: PostgreSqlDriver,\n clientUrl: getClientUrl(),\n loggerFactory: () => createMinimalLogger(),\n entities,\n migrations: {\n path: migrationsPath,\n glob: '!(*.d).{ts,js}',\n tableName,\n dropTables: false,\n },\n schemaGenerator: {\n disableForeignKeys: true,\n },\n pool: {\n min: 1,\n max: 3,\n idleTimeoutMillis: 30000,\n acquireTimeoutMillis: 60000,\n destroyTimeoutMillis: 30000,\n },\n })\n\n const migrator = orm.getMigrator() as Migrator\n const diff = await migrator.createMigration()\n if (diff && diff.fileName) {\n try {\n const orig = diff.fileName\n const base = path.basename(orig)\n const dir = path.dirname(orig)\n const ext = path.extname(base)\n const stem = base.replace(ext, '')\n const suffix = `_${modId}`\n const newBase = stem.endsWith(suffix) ? base : `${stem}${suffix}${ext}`\n const newPath = path.join(dir, newBase)\n let content = fs.readFileSync(orig, 'utf8')\n // Rename class to ensure uniqueness as well\n content = content.replace(\n /export class (Migration\\d+)/,\n `export class $1_${modId.replace(/[^a-zA-Z0-9]/g, '_')}`\n )\n fs.writeFileSync(newPath, content, 'utf8')\n if (newPath !== orig) fs.unlinkSync(orig)\n results.push(formatResult(modId, `generated ${newBase}`, ''))\n } catch {\n results.push(formatResult(modId, `generated ${path.basename(diff.fileName)} (rename failed)`, ''))\n }\n } else {\n results.push(formatResult(modId, 'no changes', ''))\n }\n\n await orm.close(true)\n }\n\n console.log(results.join('\\n'))\n}\n\nexport async function dbMigrate(resolver: PackageResolver, options: DbOptions = {}): Promise<void> {\n const modules = resolver.loadEnabledModules()\n const ordered = sortModules(modules)\n const results: string[] = []\n\n for (const entry of ordered) {\n const modId = entry.id\n const sanitizedModId = sanitizeModuleId(modId)\n const entities = await loadModuleEntities(entry, resolver)\n\n const migrationsPath = getMigrationsPath(entry, resolver)\n\n // Skip if no entities AND no migrations directory exists\n // (allows @app modules to run migrations even if entities can't be dynamically imported)\n if (!entities.length && !fs.existsSync(migrationsPath)) continue\n fs.mkdirSync(migrationsPath, { recursive: true })\n\n const tableName = `mikro_orm_migrations_${sanitizedModId}`\n validateTableName(tableName)\n\n // For @app modules, entities may be empty since TypeScript files can't be imported at runtime\n // Use discovery.warnWhenNoEntities: false to allow running migrations without entities\n const orm = await MikroORM.init<PostgreSqlDriver>({\n driver: PostgreSqlDriver,\n clientUrl: getClientUrl(),\n loggerFactory: () => createMinimalLogger(),\n entities: entities.length ? entities : [],\n discovery: { warnWhenNoEntities: false },\n migrations: {\n path: migrationsPath,\n glob: '!(*.d).{ts,js}',\n tableName,\n dropTables: false,\n },\n schemaGenerator: {\n disableForeignKeys: true,\n },\n pool: {\n min: 1,\n max: 3,\n idleTimeoutMillis: 30000,\n acquireTimeoutMillis: 60000,\n destroyTimeoutMillis: 30000,\n },\n })\n\n const migrator = orm.getMigrator() as Migrator\n const pending = await migrator.getPendingMigrations()\n if (!pending.length) {\n results.push(formatResult(modId, 'no pending migrations', ''))\n } else {\n const renderProgress = createProgressRenderer(pending.length)\n let applied = 0\n if (!QUIET_MODE) {\n process.stdout.write(` ${PROGRESS_EMOJI} ${modId}: ${renderProgress(applied)}`)\n }\n for (const migration of pending) {\n const migrationName =\n typeof migration === 'string'\n ? migration\n : (migration as any).name ?? (migration as any).fileName\n await migrator.up(migrationName ? { migrations: [migrationName] } : undefined)\n applied += 1\n if (!QUIET_MODE) {\n process.stdout.write(`\\r ${PROGRESS_EMOJI} ${modId}: ${renderProgress(applied)}`)\n }\n }\n if (!QUIET_MODE) process.stdout.write('\\n')\n results.push(\n formatResult(modId, `${pending.length} migration${pending.length === 1 ? '' : 's'} applied`, '')\n )\n }\n\n await orm.close(true)\n }\n\n console.log(results.join('\\n'))\n}\n\nexport async function dbGreenfield(resolver: PackageResolver, options: GreenfieldOptions): Promise<void> {\n if (!options.yes) {\n console.error('This command will DELETE all data. Use --yes to confirm.')\n process.exit(1)\n }\n\n console.log('Cleaning up migrations and snapshots for greenfield setup...')\n\n const modules = resolver.loadEnabledModules()\n const ordered = sortModules(modules)\n const results: string[] = []\n const outputDir = resolver.getOutputDir()\n\n for (const entry of ordered) {\n const modId = entry.id\n const migrationsPath = getMigrationsPath(entry, resolver)\n\n if (fs.existsSync(migrationsPath)) {\n // Remove all migration files\n const migrationFiles = fs\n .readdirSync(migrationsPath)\n .filter((file) => file.endsWith('.ts') && file.startsWith('Migration'))\n\n for (const file of migrationFiles) {\n fs.unlinkSync(path.join(migrationsPath, file))\n }\n\n // Remove snapshot files\n const snapshotFiles = fs\n .readdirSync(migrationsPath)\n .filter((file) => file.endsWith('.json') && file.includes('snapshot'))\n\n for (const file of snapshotFiles) {\n fs.unlinkSync(path.join(migrationsPath, file))\n }\n\n if (migrationFiles.length > 0 || snapshotFiles.length > 0) {\n results.push(\n formatResult(modId, `cleaned ${migrationFiles.length} migrations, ${snapshotFiles.length} snapshots`, '')\n )\n } else {\n results.push(formatResult(modId, 'already clean', ''))\n }\n } else {\n results.push(formatResult(modId, 'no migrations directory', ''))\n }\n\n // Clean up checksum files using glob pattern\n if (fs.existsSync(outputDir)) {\n const files = fs.readdirSync(outputDir)\n const checksumFiles = files.filter((file) => file.endsWith('.checksum'))\n\n for (const file of checksumFiles) {\n fs.unlinkSync(path.join(outputDir, file))\n }\n\n if (checksumFiles.length > 0) {\n results.push(formatResult(modId, `cleaned ${checksumFiles.length} checksum files`, ''))\n }\n }\n }\n\n console.log(results.join('\\n'))\n\n // Drop per-module MikroORM migration tables to ensure clean slate\n console.log('Dropping per-module migration tables...')\n try {\n const { Client } = await import('pg')\n const client = new Client({ connectionString: getClientUrl() })\n await client.connect()\n try {\n await client.query('BEGIN')\n for (const entry of ordered) {\n const modId = entry.id\n const sanitizedModId = sanitizeModuleId(modId)\n const tableName = `mikro_orm_migrations_${sanitizedModId}`\n validateTableName(tableName)\n await client.query(`DROP TABLE IF EXISTS \"${tableName}\"`)\n console.log(` ${modId}: dropped table ${tableName}`)\n }\n await client.query('COMMIT')\n } catch (e) {\n await client.query('ROLLBACK')\n throw e\n } finally {\n try {\n await client.end()\n } catch {}\n }\n } catch (e) {\n console.error('Failed to drop migration tables:', (e as any)?.message || e)\n throw e\n }\n\n // Drop all existing user tables to ensure fresh CREATE-only migrations\n console.log('Dropping ALL public tables for true greenfield...')\n try {\n const { Client } = await import('pg')\n const client = new Client({ connectionString: getClientUrl() })\n await client.connect()\n try {\n const res = await client.query(`SELECT tablename FROM pg_tables WHERE schemaname = 'public'`)\n const tables: string[] = (res.rows || []).map((r: any) => String(r.tablename))\n if (tables.length) {\n await client.query('BEGIN')\n try {\n await client.query(\"SET session_replication_role = 'replica'\")\n for (const t of tables) {\n await client.query(`DROP TABLE IF EXISTS \"${t}\" CASCADE`)\n }\n await client.query(\"SET session_replication_role = 'origin'\")\n await client.query('COMMIT')\n console.log(` Dropped ${tables.length} tables.`)\n } catch (e) {\n await client.query('ROLLBACK')\n throw e\n }\n } else {\n console.log(' No tables found to drop.')\n }\n } finally {\n try {\n await client.end()\n } catch {}\n }\n } catch (e) {\n console.error('Failed to drop public tables:', (e as any)?.message || e)\n throw e\n }\n\n // Generate fresh migrations for all modules\n console.log('Generating fresh migrations for all modules...')\n await dbGenerate(resolver)\n\n // Apply migrations\n console.log('Applying migrations...')\n await dbMigrate(resolver)\n\n console.log('Greenfield reset complete! Fresh migrations generated and applied.')\n}\n"],
5
- "mappings": "AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,SAAS,gBAA6B;AAEtC,SAAS,wBAAwB;AAGjC,MAAM,aAAa,QAAQ,IAAI,iBAAiB,OAAO,QAAQ,IAAI,kBAAkB;AACrF,MAAM,iBAAiB;AAEvB,SAAS,aAAa,OAAe,SAAiB,QAAQ,UAAK;AACjE,SAAO,GAAG,KAAK,IAAI,KAAK,KAAK,OAAO;AACtC;AAEA,SAAS,uBAAuB,OAAe;AAC7C,QAAM,QAAQ;AACd,QAAM,kBAAkB,QAAQ,IAAI,QAAQ;AAC5C,SAAO,CAAC,YAAoB;AAC1B,UAAM,UAAU,KAAK,IAAI,KAAK,IAAI,SAAS,CAAC,GAAG,eAAe;AAC9D,UAAM,SAAS,KAAK,MAAO,UAAU,kBAAmB,KAAK;AAC7D,UAAM,MAAM,GAAG,IAAI,OAAO,MAAM,CAAC,GAAG,IAAI,OAAO,KAAK,IAAI,QAAQ,QAAQ,CAAC,CAAC,CAAC;AAC3E,WAAO,IAAI,GAAG,KAAK,OAAO,IAAI,eAAe;AAAA,EAC/C;AACF;AAEA,SAAS,sBAA8B;AACrC,SAAO;AAAA,IACL,KAAK,MAAM;AAAA,IAAC;AAAA,IACZ,OAAO,CAAC,YAAY,YAAY,QAAQ,MAAM,OAAO;AAAA,IACrD,MAAM,CAAC,YAAY,YAAY;AAC7B,UAAI,CAAC,WAAY,SAAQ,KAAK,OAAO;AAAA,IACvC;AAAA,IACA,UAAU,MAAM;AAAA,IAAC;AAAA,IACjB,cAAc,MAAM;AAAA,IAAC;AAAA,IACrB,WAAW,MAAM;AAAA,EACnB;AACF;AAEA,SAAS,eAAuB;AAC9B,QAAM,MAAM,QAAQ,IAAI;AACxB,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,yBAAyB;AACnD,SAAO;AACT;AAEA,SAAS,YAAY,MAAoC;AAEvD,SAAO,KAAK,MAAM,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,GAAG,cAAc,EAAE,EAAE,CAAC;AAC7D;AAOO,SAAS,iBAAiB,OAAuB;AACtD,SAAO,MAAM,QAAQ,gBAAgB,GAAG;AAC1C;AAOO,SAAS,kBAAkB,WAAyB;AACzD,MAAI,CAAC,2BAA2B,KAAK,SAAS,GAAG;AAC/C,UAAM,IAAI,MAAM,uBAAuB,SAAS,gHAAgH;AAAA,EAClK;AACF;AAEA,eAAe,mBAAmB,OAAoB,UAA2C;AAC/F,QAAM,QAAQ,SAAS,eAAe,KAAK;AAC3C,QAAM,OAAO,SAAS,oBAAoB,KAAK;AAC/C,QAAM,cAAc,MAAM,SAAS;AACnC,QAAM,QAAQ;AAAA,IACZ,KAAK,KAAK,MAAM,SAAS,MAAM;AAAA,IAC/B,KAAK,KAAK,MAAM,SAAS,MAAM;AAAA,IAC/B,KAAK,KAAK,MAAM,SAAS,IAAI;AAAA,IAC7B,KAAK,KAAK,MAAM,SAAS,IAAI;AAAA,EAC/B;AACA,QAAM,aAAa,CAAC,eAAe,WAAW;AAE9C,aAAW,QAAQ,OAAO;AACxB,eAAW,KAAK,YAAY;AAC1B,YAAM,IAAI,KAAK,KAAK,MAAM,CAAC;AAC3B,UAAI,GAAG,WAAW,CAAC,GAAG;AACpB,cAAM,MAAM,KAAK,SAAS,IAAI;AAC9B,cAAM,UAAU,KAAK,WAAW,MAAM,OAAO;AAE7C,cAAM,aAAc,eAAe,UAC/B,UAAU,EAAE,QAAQ,SAAS,KAAK,CAAC,KACnC,GAAG,UAAU,KAAK,UAAU,KAAK,OAAO,IAAI,GAAG,IAAI,EAAE,QAAQ,SAAS,EAAE,CAAC;AAC7E,YAAI;AACF,gBAAM,MAAM,MAAM,OAAO;AACzB,gBAAM,WAAW,OAAO,OAAO,GAAG,EAAE,OAAO,CAAC,MAAM,OAAO,MAAM,UAAU;AACzE,cAAI,SAAS,OAAQ,QAAO;AAAA,QAC9B,SAAS,KAAK;AAGZ,cAAI,YAAa;AACjB,gBAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO,CAAC;AACV;AAEA,SAAS,kBAAkB,OAAoB,UAAmC;AAChF,QAAM,OAAO,MAAM,QAAQ;AAC3B,MAAI;AAEJ,MAAI,SAAS,sBAAsB;AACjC,iBAAa,KAAK,KAAK,SAAS,WAAW,GAAG,6BAA6B,MAAM,EAAE;AAAA,EACrF,WAAW,mBAAmB,KAAK,IAAI,GAAG;AACxC,UAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,QAAI,KAAK,SAAS,KAAK,KAAK,CAAC,GAAG;AAC9B,mBAAa,KAAK,KAAK,SAAS,WAAW,GAAG,YAAY,KAAK,CAAC,CAAC,gBAAgB,MAAM,EAAE;AAAA,IAC3F,OAAO;AACL,mBAAa,KAAK,KAAK,SAAS,WAAW,GAAG,6BAA6B,MAAM,EAAE;AAAA,IACrF;AAAA,EACF,WAAW,SAAS,QAAQ;AAE1B,iBAAa,KAAK,KAAK,SAAS,UAAU,GAAG,eAAe,MAAM,EAAE;AAAA,EACtE,OAAO;AACL,iBAAa,KAAK,KAAK,SAAS,WAAW,GAAG,6BAA6B,MAAM,EAAE;AAAA,EACrF;AAEA,SAAO,KAAK,KAAK,YAAY,YAAY;AAC3C;AAUA,eAAsB,WAAW,UAA2B,UAAqB,CAAC,GAAkB;AAClG,QAAM,UAAU,SAAS,mBAAmB;AAC5C,QAAM,UAAU,YAAY,OAAO;AACnC,QAAM,UAAoB,CAAC;AAE3B,aAAW,SAAS,SAAS;AAC3B,UAAM,QAAQ,MAAM;AACpB,UAAM,iBAAiB,iBAAiB,KAAK;AAC7C,UAAM,WAAW,MAAM,mBAAmB,OAAO,QAAQ;AACzD,QAAI,CAAC,SAAS,OAAQ;AAEtB,UAAM,iBAAiB,kBAAkB,OAAO,QAAQ;AACxD,OAAG,UAAU,gBAAgB,EAAE,WAAW,KAAK,CAAC;AAEhD,UAAM,YAAY,wBAAwB,cAAc;AACxD,sBAAkB,SAAS;AAE3B,UAAM,MAAM,MAAM,SAAS,KAAuB;AAAA,MAChD,QAAQ;AAAA,MACR,WAAW,aAAa;AAAA,MACxB,eAAe,MAAM,oBAAoB;AAAA,MACzC;AAAA,MACA,YAAY;AAAA,QACV,MAAM;AAAA,QACN,MAAM;AAAA,QACN;AAAA,QACA,YAAY;AAAA,MACd;AAAA,MACA,iBAAiB;AAAA,QACf,oBAAoB;AAAA,MACtB;AAAA,MACA,MAAM;AAAA,QACJ,KAAK;AAAA,QACL,KAAK;AAAA,QACL,mBAAmB;AAAA,QACnB,sBAAsB;AAAA,QACtB,sBAAsB;AAAA,MACxB;AAAA,IACF,CAAC;AAED,UAAM,WAAW,IAAI,YAAY;AACjC,UAAM,OAAO,MAAM,SAAS,gBAAgB;AAC5C,QAAI,QAAQ,KAAK,UAAU;AACzB,UAAI;AACF,cAAM,OAAO,KAAK;AAClB,cAAM,OAAO,KAAK,SAAS,IAAI;AAC/B,cAAM,MAAM,KAAK,QAAQ,IAAI;AAC7B,cAAM,MAAM,KAAK,QAAQ,IAAI;AAC7B,cAAM,OAAO,KAAK,QAAQ,KAAK,EAAE;AACjC,cAAM,SAAS,IAAI,KAAK;AACxB,cAAM,UAAU,KAAK,SAAS,MAAM,IAAI,OAAO,GAAG,IAAI,GAAG,MAAM,GAAG,GAAG;AACrE,cAAM,UAAU,KAAK,KAAK,KAAK,OAAO;AACtC,YAAI,UAAU,GAAG,aAAa,MAAM,MAAM;AAE1C,kBAAU,QAAQ;AAAA,UAChB;AAAA,UACA,mBAAmB,MAAM,QAAQ,iBAAiB,GAAG,CAAC;AAAA,QACxD;AACA,WAAG,cAAc,SAAS,SAAS,MAAM;AACzC,YAAI,YAAY,KAAM,IAAG,WAAW,IAAI;AACxC,gBAAQ,KAAK,aAAa,OAAO,aAAa,OAAO,IAAI,EAAE,CAAC;AAAA,MAC9D,QAAQ;AACN,gBAAQ,KAAK,aAAa,OAAO,aAAa,KAAK,SAAS,KAAK,QAAQ,CAAC,oBAAoB,EAAE,CAAC;AAAA,MACnG;AAAA,IACF,OAAO;AACL,cAAQ,KAAK,aAAa,OAAO,cAAc,EAAE,CAAC;AAAA,IACpD;AAEA,UAAM,IAAI,MAAM,IAAI;AAAA,EACtB;AAEA,UAAQ,IAAI,QAAQ,KAAK,IAAI,CAAC;AAChC;AAEA,eAAsB,UAAU,UAA2B,UAAqB,CAAC,GAAkB;AACjG,QAAM,UAAU,SAAS,mBAAmB;AAC5C,QAAM,UAAU,YAAY,OAAO;AACnC,QAAM,UAAoB,CAAC;AAE3B,aAAW,SAAS,SAAS;AAC3B,UAAM,QAAQ,MAAM;AACpB,UAAM,iBAAiB,iBAAiB,KAAK;AAC7C,UAAM,WAAW,MAAM,mBAAmB,OAAO,QAAQ;AAEzD,UAAM,iBAAiB,kBAAkB,OAAO,QAAQ;AAIxD,QAAI,CAAC,SAAS,UAAU,CAAC,GAAG,WAAW,cAAc,EAAG;AACxD,OAAG,UAAU,gBAAgB,EAAE,WAAW,KAAK,CAAC;AAEhD,UAAM,YAAY,wBAAwB,cAAc;AACxD,sBAAkB,SAAS;AAI3B,UAAM,MAAM,MAAM,SAAS,KAAuB;AAAA,MAChD,QAAQ;AAAA,MACR,WAAW,aAAa;AAAA,MACxB,eAAe,MAAM,oBAAoB;AAAA,MACzC,UAAU,SAAS,SAAS,WAAW,CAAC;AAAA,MACxC,WAAW,EAAE,oBAAoB,MAAM;AAAA,MACvC,YAAY;AAAA,QACV,MAAM;AAAA,QACN,MAAM;AAAA,QACN;AAAA,QACA,YAAY;AAAA,MACd;AAAA,MACA,iBAAiB;AAAA,QACf,oBAAoB;AAAA,MACtB;AAAA,MACA,MAAM;AAAA,QACJ,KAAK;AAAA,QACL,KAAK;AAAA,QACL,mBAAmB;AAAA,QACnB,sBAAsB;AAAA,QACtB,sBAAsB;AAAA,MACxB;AAAA,IACF,CAAC;AAED,UAAM,WAAW,IAAI,YAAY;AACjC,UAAM,UAAU,MAAM,SAAS,qBAAqB;AACpD,QAAI,CAAC,QAAQ,QAAQ;AACnB,cAAQ,KAAK,aAAa,OAAO,yBAAyB,EAAE,CAAC;AAAA,IAC/D,OAAO;AACL,YAAM,iBAAiB,uBAAuB,QAAQ,MAAM;AAC5D,UAAI,UAAU;AACd,UAAI,CAAC,YAAY;AACf,gBAAQ,OAAO,MAAM,MAAM,cAAc,IAAI,KAAK,KAAK,eAAe,OAAO,CAAC,EAAE;AAAA,MAClF;AACA,iBAAW,aAAa,SAAS;AAC/B,cAAM,gBACJ,OAAO,cAAc,WACjB,YACC,UAAkB,QAAS,UAAkB;AACpD,cAAM,SAAS,GAAG,gBAAgB,EAAE,YAAY,CAAC,aAAa,EAAE,IAAI,MAAS;AAC7E,mBAAW;AACX,YAAI,CAAC,YAAY;AACf,kBAAQ,OAAO,MAAM,QAAQ,cAAc,IAAI,KAAK,KAAK,eAAe,OAAO,CAAC,EAAE;AAAA,QACpF;AAAA,MACF;AACA,UAAI,CAAC,WAAY,SAAQ,OAAO,MAAM,IAAI;AAC1C,cAAQ;AAAA,QACN,aAAa,OAAO,GAAG,QAAQ,MAAM,aAAa,QAAQ,WAAW,IAAI,KAAK,GAAG,YAAY,EAAE;AAAA,MACjG;AAAA,IACF;AAEA,UAAM,IAAI,MAAM,IAAI;AAAA,EACtB;AAEA,UAAQ,IAAI,QAAQ,KAAK,IAAI,CAAC;AAChC;AAEA,eAAsB,aAAa,UAA2B,SAA2C;AACvG,MAAI,CAAC,QAAQ,KAAK;AAChB,YAAQ,MAAM,0DAA0D;AACxE,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,UAAQ,IAAI,8DAA8D;AAE1E,QAAM,UAAU,SAAS,mBAAmB;AAC5C,QAAM,UAAU,YAAY,OAAO;AACnC,QAAM,UAAoB,CAAC;AAC3B,QAAM,YAAY,SAAS,aAAa;AAExC,aAAW,SAAS,SAAS;AAC3B,UAAM,QAAQ,MAAM;AACpB,UAAM,iBAAiB,kBAAkB,OAAO,QAAQ;AAExD,QAAI,GAAG,WAAW,cAAc,GAAG;AAEjC,YAAM,iBAAiB,GACpB,YAAY,cAAc,EAC1B,OAAO,CAAC,SAAS,KAAK,SAAS,KAAK,KAAK,KAAK,WAAW,WAAW,CAAC;AAExE,iBAAW,QAAQ,gBAAgB;AACjC,WAAG,WAAW,KAAK,KAAK,gBAAgB,IAAI,CAAC;AAAA,MAC/C;AAGA,YAAM,gBAAgB,GACnB,YAAY,cAAc,EAC1B,OAAO,CAAC,SAAS,KAAK,SAAS,OAAO,KAAK,KAAK,SAAS,UAAU,CAAC;AAEvE,iBAAW,QAAQ,eAAe;AAChC,WAAG,WAAW,KAAK,KAAK,gBAAgB,IAAI,CAAC;AAAA,MAC/C;AAEA,UAAI,eAAe,SAAS,KAAK,cAAc,SAAS,GAAG;AACzD,gBAAQ;AAAA,UACN,aAAa,OAAO,WAAW,eAAe,MAAM,gBAAgB,cAAc,MAAM,cAAc,EAAE;AAAA,QAC1G;AAAA,MACF,OAAO;AACL,gBAAQ,KAAK,aAAa,OAAO,iBAAiB,EAAE,CAAC;AAAA,MACvD;AAAA,IACF,OAAO;AACL,cAAQ,KAAK,aAAa,OAAO,2BAA2B,EAAE,CAAC;AAAA,IACjE;AAGA,QAAI,GAAG,WAAW,SAAS,GAAG;AAC5B,YAAM,QAAQ,GAAG,YAAY,SAAS;AACtC,YAAM,gBAAgB,MAAM,OAAO,CAAC,SAAS,KAAK,SAAS,WAAW,CAAC;AAEvE,iBAAW,QAAQ,eAAe;AAChC,WAAG,WAAW,KAAK,KAAK,WAAW,IAAI,CAAC;AAAA,MAC1C;AAEA,UAAI,cAAc,SAAS,GAAG;AAC5B,gBAAQ,KAAK,aAAa,OAAO,WAAW,cAAc,MAAM,mBAAmB,EAAE,CAAC;AAAA,MACxF;AAAA,IACF;AAAA,EACF;AAEA,UAAQ,IAAI,QAAQ,KAAK,IAAI,CAAC;AAG9B,UAAQ,IAAI,yCAAyC;AACrD,MAAI;AACF,UAAM,EAAE,OAAO,IAAI,MAAM,OAAO,IAAI;AACpC,UAAM,SAAS,IAAI,OAAO,EAAE,kBAAkB,aAAa,EAAE,CAAC;AAC9D,UAAM,OAAO,QAAQ;AACrB,QAAI;AACF,YAAM,OAAO,MAAM,OAAO;AAC1B,iBAAW,SAAS,SAAS;AAC3B,cAAM,QAAQ,MAAM;AACpB,cAAM,iBAAiB,iBAAiB,KAAK;AAC7C,cAAM,YAAY,wBAAwB,cAAc;AACxD,0BAAkB,SAAS;AAC3B,cAAM,OAAO,MAAM,yBAAyB,SAAS,GAAG;AACxD,gBAAQ,IAAI,MAAM,KAAK,mBAAmB,SAAS,EAAE;AAAA,MACvD;AACA,YAAM,OAAO,MAAM,QAAQ;AAAA,IAC7B,SAAS,GAAG;AACV,YAAM,OAAO,MAAM,UAAU;AAC7B,YAAM;AAAA,IACR,UAAE;AACA,UAAI;AACF,cAAM,OAAO,IAAI;AAAA,MACnB,QAAQ;AAAA,MAAC;AAAA,IACX;AAAA,EACF,SAAS,GAAG;AACV,YAAQ,MAAM,oCAAqC,GAAW,WAAW,CAAC;AAC1E,UAAM;AAAA,EACR;AAGA,UAAQ,IAAI,mDAAmD;AAC/D,MAAI;AACF,UAAM,EAAE,OAAO,IAAI,MAAM,OAAO,IAAI;AACpC,UAAM,SAAS,IAAI,OAAO,EAAE,kBAAkB,aAAa,EAAE,CAAC;AAC9D,UAAM,OAAO,QAAQ;AACrB,QAAI;AACF,YAAM,MAAM,MAAM,OAAO,MAAM,6DAA6D;AAC5F,YAAM,UAAoB,IAAI,QAAQ,CAAC,GAAG,IAAI,CAAC,MAAW,OAAO,EAAE,SAAS,CAAC;AAC7E,UAAI,OAAO,QAAQ;AACjB,cAAM,OAAO,MAAM,OAAO;AAC1B,YAAI;AACF,gBAAM,OAAO,MAAM,0CAA0C;AAC7D,qBAAW,KAAK,QAAQ;AACtB,kBAAM,OAAO,MAAM,yBAAyB,CAAC,WAAW;AAAA,UAC1D;AACA,gBAAM,OAAO,MAAM,yCAAyC;AAC5D,gBAAM,OAAO,MAAM,QAAQ;AAC3B,kBAAQ,IAAI,cAAc,OAAO,MAAM,UAAU;AAAA,QACnD,SAAS,GAAG;AACV,gBAAM,OAAO,MAAM,UAAU;AAC7B,gBAAM;AAAA,QACR;AAAA,MACF,OAAO;AACL,gBAAQ,IAAI,6BAA6B;AAAA,MAC3C;AAAA,IACF,UAAE;AACA,UAAI;AACF,cAAM,OAAO,IAAI;AAAA,MACnB,QAAQ;AAAA,MAAC;AAAA,IACX;AAAA,EACF,SAAS,GAAG;AACV,YAAQ,MAAM,iCAAkC,GAAW,WAAW,CAAC;AACvE,UAAM;AAAA,EACR;AAGA,UAAQ,IAAI,gDAAgD;AAC5D,QAAM,WAAW,QAAQ;AAGzB,UAAQ,IAAI,wBAAwB;AACpC,QAAM,UAAU,QAAQ;AAExB,UAAQ,IAAI,oEAAoE;AAClF;",
4
+ "sourcesContent": ["import path from 'node:path'\nimport fs from 'node:fs'\nimport { MikroORM, type Logger } from '@mikro-orm/core'\nimport { Migrator } from '@mikro-orm/migrations'\nimport { PostgreSqlDriver } from '@mikro-orm/postgresql'\nimport type { PackageResolver, ModuleEntry } from '../resolver'\n\nconst QUIET_MODE = process.env.OM_CLI_QUIET === '1' || process.env.MERCATO_QUIET === '1'\nconst PROGRESS_EMOJI = ''\n\nfunction formatResult(modId: string, message: string, emoji = '\u2022') {\n return `${emoji} ${modId}: ${message}`\n}\n\nfunction createProgressRenderer(total: number) {\n const width = 20\n const normalizedTotal = total > 0 ? total : 1\n return (current: number) => {\n const clamped = Math.min(Math.max(current, 0), normalizedTotal)\n const filled = Math.round((clamped / normalizedTotal) * width)\n const bar = `${'='.repeat(filled)}${'.'.repeat(Math.max(width - filled, 0))}`\n return `[${bar}] ${clamped}/${normalizedTotal}`\n }\n}\n\nfunction createMinimalLogger(): Logger {\n return {\n log: () => {},\n error: (_namespace, message) => console.error(message),\n warn: (_namespace, message) => {\n if (!QUIET_MODE) console.warn(message)\n },\n logQuery: () => {},\n setDebugMode: () => {},\n isEnabled: () => false,\n }\n}\n\nfunction getClientUrl(): string {\n const url = process.env.DATABASE_URL\n if (!url) throw new Error('DATABASE_URL is not set')\n return url\n}\n\nfunction sortModules(mods: ModuleEntry[]): ModuleEntry[] {\n // Sort modules alphabetically since they are now isomorphic\n return mods.slice().sort((a, b) => a.id.localeCompare(b.id))\n}\n\n/**\n * Sanitizes a module ID for use in SQL identifiers (table names).\n * Replaces non-alphanumeric characters with underscores to prevent SQL injection.\n * @public Exported for testing\n */\nexport function sanitizeModuleId(modId: string): string {\n return modId.replace(/[^a-z0-9_]/gi, '_')\n}\n\n/**\n * Validates that a table name is safe for use in SQL queries.\n * @throws Error if the table name contains invalid characters.\n * @public Exported for testing\n */\nexport function validateTableName(tableName: string): void {\n if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(tableName)) {\n throw new Error(`Invalid table name: ${tableName}. Table names must start with a letter or underscore and contain only alphanumeric characters and underscores.`)\n }\n}\n\nasync function loadModuleEntities(entry: ModuleEntry, resolver: PackageResolver): Promise<any[]> {\n const roots = resolver.getModulePaths(entry)\n const imps = resolver.getModuleImportBase(entry)\n const isAppModule = entry.from === '@app'\n const bases = [\n path.join(roots.appBase, 'data'),\n path.join(roots.pkgBase, 'data'),\n path.join(roots.appBase, 'db'),\n path.join(roots.pkgBase, 'db'),\n ]\n const candidates = ['entities.ts', 'schema.ts']\n\n for (const base of bases) {\n for (const f of candidates) {\n const p = path.join(base, f)\n if (fs.existsSync(p)) {\n const sub = path.basename(base)\n const fromApp = base.startsWith(roots.appBase)\n // For @app modules, use file:// URL since @/ alias doesn't work in Node.js runtime\n const importPath = (isAppModule && fromApp)\n ? `file://${p.replace(/\\.ts$/, '.js')}`\n : `${fromApp ? imps.appBase : imps.pkgBase}/${sub}/${f.replace(/\\.ts$/, '')}`\n try {\n const mod = await import(importPath)\n const entities = Object.values(mod).filter((v) => typeof v === 'function')\n if (entities.length) return entities as any[]\n } catch (err) {\n // For @app modules with TypeScript files, they can't be directly imported\n // Skip and let MikroORM handle entities through discovery\n if (isAppModule) continue\n throw err\n }\n }\n }\n }\n return []\n}\n\nfunction getMigrationsPath(entry: ModuleEntry, resolver: PackageResolver): string {\n const from = entry.from || '@open-mercato/core'\n\n if (from === '@app') {\n // For @app modules, use the app directory\n return path.join(resolver.getAppDir(), 'src/modules', entry.id, 'migrations')\n }\n\n // Use resolver's getModulePaths which handles monorepo vs production mode\n const { pkgBase } = resolver.getModulePaths(entry)\n return path.join(pkgBase, 'migrations')\n}\n\nexport interface DbOptions {\n quiet?: boolean\n}\n\nexport interface GreenfieldOptions extends DbOptions {\n yes: boolean\n}\n\nexport async function dbGenerate(resolver: PackageResolver, options: DbOptions = {}): Promise<void> {\n const modules = resolver.loadEnabledModules()\n const ordered = sortModules(modules)\n const results: string[] = []\n\n for (const entry of ordered) {\n const modId = entry.id\n const sanitizedModId = sanitizeModuleId(modId)\n const entities = await loadModuleEntities(entry, resolver)\n if (!entities.length) continue\n\n const migrationsPath = getMigrationsPath(entry, resolver)\n fs.mkdirSync(migrationsPath, { recursive: true })\n\n const tableName = `mikro_orm_migrations_${sanitizedModId}`\n validateTableName(tableName)\n\n const orm = await MikroORM.init<PostgreSqlDriver>({\n driver: PostgreSqlDriver,\n clientUrl: getClientUrl(),\n loggerFactory: () => createMinimalLogger(),\n entities,\n migrations: {\n path: migrationsPath,\n glob: '!(*.d).{ts,js}',\n tableName,\n dropTables: false,\n },\n schemaGenerator: {\n disableForeignKeys: true,\n },\n pool: {\n min: 1,\n max: 3,\n idleTimeoutMillis: 30000,\n acquireTimeoutMillis: 60000,\n destroyTimeoutMillis: 30000,\n },\n })\n\n const migrator = orm.getMigrator() as Migrator\n const diff = await migrator.createMigration()\n if (diff && diff.fileName) {\n try {\n const orig = diff.fileName\n const base = path.basename(orig)\n const dir = path.dirname(orig)\n const ext = path.extname(base)\n const stem = base.replace(ext, '')\n const suffix = `_${modId}`\n const newBase = stem.endsWith(suffix) ? base : `${stem}${suffix}${ext}`\n const newPath = path.join(dir, newBase)\n let content = fs.readFileSync(orig, 'utf8')\n // Rename class to ensure uniqueness as well\n content = content.replace(\n /export class (Migration\\d+)/,\n `export class $1_${modId.replace(/[^a-zA-Z0-9]/g, '_')}`\n )\n fs.writeFileSync(newPath, content, 'utf8')\n if (newPath !== orig) fs.unlinkSync(orig)\n results.push(formatResult(modId, `generated ${newBase}`, ''))\n } catch {\n results.push(formatResult(modId, `generated ${path.basename(diff.fileName)} (rename failed)`, ''))\n }\n } else {\n results.push(formatResult(modId, 'no changes', ''))\n }\n\n await orm.close(true)\n }\n\n console.log(results.join('\\n'))\n}\n\nexport async function dbMigrate(resolver: PackageResolver, options: DbOptions = {}): Promise<void> {\n const modules = resolver.loadEnabledModules()\n const ordered = sortModules(modules)\n const results: string[] = []\n\n for (const entry of ordered) {\n const modId = entry.id\n const sanitizedModId = sanitizeModuleId(modId)\n const entities = await loadModuleEntities(entry, resolver)\n\n const migrationsPath = getMigrationsPath(entry, resolver)\n\n // Skip if no entities AND no migrations directory exists\n // (allows @app modules to run migrations even if entities can't be dynamically imported)\n if (!entities.length && !fs.existsSync(migrationsPath)) continue\n fs.mkdirSync(migrationsPath, { recursive: true })\n\n const tableName = `mikro_orm_migrations_${sanitizedModId}`\n validateTableName(tableName)\n\n // For @app modules, entities may be empty since TypeScript files can't be imported at runtime\n // Use discovery.warnWhenNoEntities: false to allow running migrations without entities\n const orm = await MikroORM.init<PostgreSqlDriver>({\n driver: PostgreSqlDriver,\n clientUrl: getClientUrl(),\n loggerFactory: () => createMinimalLogger(),\n entities: entities.length ? entities : [],\n discovery: { warnWhenNoEntities: false },\n migrations: {\n path: migrationsPath,\n glob: '!(*.d).{ts,js}',\n tableName,\n dropTables: false,\n },\n schemaGenerator: {\n disableForeignKeys: true,\n },\n pool: {\n min: 1,\n max: 3,\n idleTimeoutMillis: 30000,\n acquireTimeoutMillis: 60000,\n destroyTimeoutMillis: 30000,\n },\n })\n\n const migrator = orm.getMigrator() as Migrator\n const pending = await migrator.getPendingMigrations()\n if (!pending.length) {\n results.push(formatResult(modId, 'no pending migrations', ''))\n } else {\n const renderProgress = createProgressRenderer(pending.length)\n let applied = 0\n if (!QUIET_MODE) {\n process.stdout.write(` ${PROGRESS_EMOJI} ${modId}: ${renderProgress(applied)}`)\n }\n for (const migration of pending) {\n const migrationName =\n typeof migration === 'string'\n ? migration\n : (migration as any).name ?? (migration as any).fileName\n await migrator.up(migrationName ? { migrations: [migrationName] } : undefined)\n applied += 1\n if (!QUIET_MODE) {\n process.stdout.write(`\\r ${PROGRESS_EMOJI} ${modId}: ${renderProgress(applied)}`)\n }\n }\n if (!QUIET_MODE) process.stdout.write('\\n')\n results.push(\n formatResult(modId, `${pending.length} migration${pending.length === 1 ? '' : 's'} applied`, '')\n )\n }\n\n await orm.close(true)\n }\n\n console.log(results.join('\\n'))\n}\n\nexport async function dbGreenfield(resolver: PackageResolver, options: GreenfieldOptions): Promise<void> {\n if (!options.yes) {\n console.error('This command will DELETE all data. Use --yes to confirm.')\n process.exit(1)\n }\n\n console.log('Cleaning up migrations and snapshots for greenfield setup...')\n\n const modules = resolver.loadEnabledModules()\n const ordered = sortModules(modules)\n const results: string[] = []\n const outputDir = resolver.getOutputDir()\n\n for (const entry of ordered) {\n const modId = entry.id\n const migrationsPath = getMigrationsPath(entry, resolver)\n\n if (fs.existsSync(migrationsPath)) {\n // Remove all migration files\n const migrationFiles = fs\n .readdirSync(migrationsPath)\n .filter((file) => file.endsWith('.ts') && file.startsWith('Migration'))\n\n for (const file of migrationFiles) {\n fs.unlinkSync(path.join(migrationsPath, file))\n }\n\n // Remove snapshot files\n const snapshotFiles = fs\n .readdirSync(migrationsPath)\n .filter((file) => file.endsWith('.json') && file.includes('snapshot'))\n\n for (const file of snapshotFiles) {\n fs.unlinkSync(path.join(migrationsPath, file))\n }\n\n if (migrationFiles.length > 0 || snapshotFiles.length > 0) {\n results.push(\n formatResult(modId, `cleaned ${migrationFiles.length} migrations, ${snapshotFiles.length} snapshots`, '')\n )\n } else {\n results.push(formatResult(modId, 'already clean', ''))\n }\n } else {\n results.push(formatResult(modId, 'no migrations directory', ''))\n }\n\n // Clean up checksum files using glob pattern\n if (fs.existsSync(outputDir)) {\n const files = fs.readdirSync(outputDir)\n const checksumFiles = files.filter((file) => file.endsWith('.checksum'))\n\n for (const file of checksumFiles) {\n fs.unlinkSync(path.join(outputDir, file))\n }\n\n if (checksumFiles.length > 0) {\n results.push(formatResult(modId, `cleaned ${checksumFiles.length} checksum files`, ''))\n }\n }\n }\n\n console.log(results.join('\\n'))\n\n // Drop per-module MikroORM migration tables to ensure clean slate\n console.log('Dropping per-module migration tables...')\n try {\n const { Client } = await import('pg')\n const client = new Client({ connectionString: getClientUrl() })\n await client.connect()\n try {\n await client.query('BEGIN')\n for (const entry of ordered) {\n const modId = entry.id\n const sanitizedModId = sanitizeModuleId(modId)\n const tableName = `mikro_orm_migrations_${sanitizedModId}`\n validateTableName(tableName)\n await client.query(`DROP TABLE IF EXISTS \"${tableName}\"`)\n console.log(` ${modId}: dropped table ${tableName}`)\n }\n await client.query('COMMIT')\n } catch (e) {\n await client.query('ROLLBACK')\n throw e\n } finally {\n try {\n await client.end()\n } catch {}\n }\n } catch (e) {\n console.error('Failed to drop migration tables:', (e as any)?.message || e)\n throw e\n }\n\n // Drop all existing user tables to ensure fresh CREATE-only migrations\n console.log('Dropping ALL public tables for true greenfield...')\n try {\n const { Client } = await import('pg')\n const client = new Client({ connectionString: getClientUrl() })\n await client.connect()\n try {\n const res = await client.query(`SELECT tablename FROM pg_tables WHERE schemaname = 'public'`)\n const tables: string[] = (res.rows || []).map((r: any) => String(r.tablename))\n if (tables.length) {\n await client.query('BEGIN')\n try {\n await client.query(\"SET session_replication_role = 'replica'\")\n for (const t of tables) {\n await client.query(`DROP TABLE IF EXISTS \"${t}\" CASCADE`)\n }\n await client.query(\"SET session_replication_role = 'origin'\")\n await client.query('COMMIT')\n console.log(` Dropped ${tables.length} tables.`)\n } catch (e) {\n await client.query('ROLLBACK')\n throw e\n }\n } else {\n console.log(' No tables found to drop.')\n }\n } finally {\n try {\n await client.end()\n } catch {}\n }\n } catch (e) {\n console.error('Failed to drop public tables:', (e as any)?.message || e)\n throw e\n }\n\n // Generate fresh migrations for all modules\n console.log('Generating fresh migrations for all modules...')\n await dbGenerate(resolver)\n\n // Apply migrations\n console.log('Applying migrations...')\n await dbMigrate(resolver)\n\n console.log('Greenfield reset complete! Fresh migrations generated and applied.')\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -10,6 +10,15 @@ import {
10
10
  logGenerationResult,
11
11
  createGeneratorResult
12
12
  } from "../utils.js";
13
+ function findModuleFile(basePath, ...segments) {
14
+ const name = segments.pop();
15
+ const dir = segments.length ? path.join(basePath, ...segments) : basePath;
16
+ const tsPath = path.join(dir, `${name}.ts`);
17
+ if (fs.existsSync(tsPath)) return tsPath;
18
+ const jsPath = path.join(dir, `${name}.js`);
19
+ if (fs.existsSync(jsPath)) return jsPath;
20
+ return null;
21
+ }
13
22
  async function generateModuleRegistry(options) {
14
23
  const { resolver, quiet = false } = options;
15
24
  const result = createGeneratorResult();
@@ -60,9 +69,9 @@ async function generateModuleRegistry(options) {
60
69
  const dashboardWidgets = [];
61
70
  const injectionWidgets = [];
62
71
  let injectionTableImportName = null;
63
- const appIndex = path.join(roots.appBase, "index.ts");
64
- const pkgIndex = path.join(roots.pkgBase, "index.ts");
65
- const indexTs = fs.existsSync(appIndex) ? appIndex : fs.existsSync(pkgIndex) ? pkgIndex : null;
72
+ const appIndex = findModuleFile(roots.appBase, "index");
73
+ const pkgIndex = findModuleFile(roots.pkgBase, "index");
74
+ const indexTs = appIndex ?? pkgIndex;
66
75
  if (indexTs) {
67
76
  infoImportName = `I${importId++}_${toVar(modId)}`;
68
77
  const importPath = indexTs.startsWith(roots.appBase) ? `${appImportBase}/index` : `${imps.pkgBase}/index`;
@@ -161,10 +170,10 @@ async function generateModuleRegistry(options) {
161
170
  }
162
171
  }
163
172
  {
164
- const appFile = path.join(roots.appBase, "data", "extensions.ts");
165
- const pkgFile = path.join(roots.pkgBase, "data", "extensions.ts");
166
- const hasApp = fs.existsSync(appFile);
167
- const hasPkg = fs.existsSync(pkgFile);
173
+ const appFile = findModuleFile(roots.appBase, "data", "extensions");
174
+ const pkgFile = findModuleFile(roots.pkgBase, "data", "extensions");
175
+ const hasApp = !!appFile;
176
+ const hasPkg = !!pkgFile;
168
177
  if (hasApp || hasPkg) {
169
178
  const importName = `X_${toVar(modId)}_${importId++}`;
170
179
  const importPath = hasApp ? `${appImportBase}/data/extensions` : `${imps.pkgBase}/data/extensions`;
@@ -173,22 +182,22 @@ async function generateModuleRegistry(options) {
173
182
  }
174
183
  }
175
184
  {
176
- const rootApp = path.join(roots.appBase, "acl.ts");
177
- const rootPkg = path.join(roots.pkgBase, "acl.ts");
178
- const hasRoot = fs.existsSync(rootApp) || fs.existsSync(rootPkg);
185
+ const rootApp = findModuleFile(roots.appBase, "acl");
186
+ const rootPkg = findModuleFile(roots.pkgBase, "acl");
187
+ const hasRoot = rootApp || rootPkg;
179
188
  if (hasRoot) {
180
189
  const importName = `ACL_${toVar(modId)}_${importId++}`;
181
- const useApp = fs.existsSync(rootApp) ? rootApp : rootPkg;
190
+ const useApp = rootApp ?? rootPkg;
182
191
  const importPath = useApp.startsWith(roots.appBase) ? `${appImportBase}/acl` : `${imps.pkgBase}/acl`;
183
192
  imports.push(`import * as ${importName} from '${importPath}'`);
184
193
  featuresImportName = importName;
185
194
  }
186
195
  }
187
196
  {
188
- const appFile = path.join(roots.appBase, "ce.ts");
189
- const pkgFile = path.join(roots.pkgBase, "ce.ts");
190
- const hasApp = fs.existsSync(appFile);
191
- const hasPkg = fs.existsSync(pkgFile);
197
+ const appFile = findModuleFile(roots.appBase, "ce");
198
+ const pkgFile = findModuleFile(roots.pkgBase, "ce");
199
+ const hasApp = !!appFile;
200
+ const hasPkg = !!pkgFile;
192
201
  if (hasApp || hasPkg) {
193
202
  const importName = `CE_${toVar(modId)}_${importId++}`;
194
203
  const importPath = hasApp ? `${appImportBase}/ce` : `${imps.pkgBase}/ce`;
@@ -197,10 +206,10 @@ async function generateModuleRegistry(options) {
197
206
  }
198
207
  }
199
208
  {
200
- const appFile = path.join(roots.appBase, "search.ts");
201
- const pkgFile = path.join(roots.pkgBase, "search.ts");
202
- const hasApp = fs.existsSync(appFile);
203
- const hasPkg = fs.existsSync(pkgFile);
209
+ const appFile = findModuleFile(roots.appBase, "search");
210
+ const pkgFile = findModuleFile(roots.pkgBase, "search");
211
+ const hasApp = !!appFile;
212
+ const hasPkg = !!pkgFile;
204
213
  if (hasApp || hasPkg) {
205
214
  const importName = `SEARCH_${toVar(modId)}_${importId++}`;
206
215
  const importPath = hasApp ? `${appImportBase}/search` : `${imps.pkgBase}/search`;
@@ -211,10 +220,10 @@ async function generateModuleRegistry(options) {
211
220
  }
212
221
  }
213
222
  {
214
- const appFile = path.join(roots.appBase, "data", "fields.ts");
215
- const pkgFile = path.join(roots.pkgBase, "data", "fields.ts");
216
- const hasApp = fs.existsSync(appFile);
217
- const hasPkg = fs.existsSync(pkgFile);
223
+ const appFile = findModuleFile(roots.appBase, "data", "fields");
224
+ const pkgFile = findModuleFile(roots.pkgBase, "data", "fields");
225
+ const hasApp = !!appFile;
226
+ const hasPkg = !!pkgFile;
218
227
  if (hasApp || hasPkg) {
219
228
  const importName = `F_${toVar(modId)}_${importId++}`;
220
229
  const importPath = hasApp ? `${appImportBase}/data/fields` : `${imps.pkgBase}/data/fields`;
@@ -419,9 +428,9 @@ async function generateModuleRegistry(options) {
419
428
  }
420
429
  }
421
430
  }
422
- const cliApp = path.join(roots.appBase, "cli.ts");
423
- const cliPkg = path.join(roots.pkgBase, "cli.ts");
424
- const cliPath = fs.existsSync(cliApp) ? cliApp : fs.existsSync(cliPkg) ? cliPkg : null;
431
+ const cliApp = findModuleFile(roots.appBase, "cli");
432
+ const cliPkg = findModuleFile(roots.pkgBase, "cli");
433
+ const cliPath = cliApp ?? cliPkg;
425
434
  if (cliPath) {
426
435
  const importName = `CLI_${toVar(modId)}`;
427
436
  const importPath = cliPath.startsWith(roots.appBase) ? `${appImportBase}/cli` : `${imps.pkgBase}/cli`;
@@ -819,9 +828,9 @@ async function generateModuleRegistryCli(options) {
819
828
  let customEntitiesImportName = null;
820
829
  let vectorImportName = null;
821
830
  let customFieldSetsExpr = "[]";
822
- const appIndex = path.join(roots.appBase, "index.ts");
823
- const pkgIndex = path.join(roots.pkgBase, "index.ts");
824
- const indexTs = fs.existsSync(appIndex) ? appIndex : fs.existsSync(pkgIndex) ? pkgIndex : null;
831
+ const appIndex = findModuleFile(roots.appBase, "index");
832
+ const pkgIndex = findModuleFile(roots.pkgBase, "index");
833
+ const indexTs = appIndex ?? pkgIndex;
825
834
  if (indexTs) {
826
835
  infoImportName = `I${importId++}_${toVar(modId)}`;
827
836
  const importPath = indexTs.startsWith(roots.appBase) ? `${appImportBase}/index` : `${imps.pkgBase}/index`;
@@ -834,10 +843,10 @@ async function generateModuleRegistryCli(options) {
834
843
  }
835
844
  }
836
845
  {
837
- const appFile = path.join(roots.appBase, "data", "extensions.ts");
838
- const pkgFile = path.join(roots.pkgBase, "data", "extensions.ts");
839
- const hasApp = fs.existsSync(appFile);
840
- const hasPkg = fs.existsSync(pkgFile);
846
+ const appFile = findModuleFile(roots.appBase, "data", "extensions");
847
+ const pkgFile = findModuleFile(roots.pkgBase, "data", "extensions");
848
+ const hasApp = !!appFile;
849
+ const hasPkg = !!pkgFile;
841
850
  if (hasApp || hasPkg) {
842
851
  const importName = `X_${toVar(modId)}_${importId++}`;
843
852
  const importPath = hasApp ? `${appImportBase}/data/extensions` : `${imps.pkgBase}/data/extensions`;
@@ -846,22 +855,22 @@ async function generateModuleRegistryCli(options) {
846
855
  }
847
856
  }
848
857
  {
849
- const rootApp = path.join(roots.appBase, "acl.ts");
850
- const rootPkg = path.join(roots.pkgBase, "acl.ts");
851
- const hasRoot = fs.existsSync(rootApp) || fs.existsSync(rootPkg);
858
+ const rootApp = findModuleFile(roots.appBase, "acl");
859
+ const rootPkg = findModuleFile(roots.pkgBase, "acl");
860
+ const hasRoot = rootApp || rootPkg;
852
861
  if (hasRoot) {
853
862
  const importName = `ACL_${toVar(modId)}_${importId++}`;
854
- const useApp = fs.existsSync(rootApp) ? rootApp : rootPkg;
863
+ const useApp = rootApp ?? rootPkg;
855
864
  const importPath = useApp.startsWith(roots.appBase) ? `${appImportBase}/acl` : `${imps.pkgBase}/acl`;
856
865
  imports.push(`import * as ${importName} from '${importPath}'`);
857
866
  featuresImportName = importName;
858
867
  }
859
868
  }
860
869
  {
861
- const appFile = path.join(roots.appBase, "ce.ts");
862
- const pkgFile = path.join(roots.pkgBase, "ce.ts");
863
- const hasApp = fs.existsSync(appFile);
864
- const hasPkg = fs.existsSync(pkgFile);
870
+ const appFile = findModuleFile(roots.appBase, "ce");
871
+ const pkgFile = findModuleFile(roots.pkgBase, "ce");
872
+ const hasApp = !!appFile;
873
+ const hasPkg = !!pkgFile;
865
874
  if (hasApp || hasPkg) {
866
875
  const importName = `CE_${toVar(modId)}_${importId++}`;
867
876
  const importPath = hasApp ? `${appImportBase}/ce` : `${imps.pkgBase}/ce`;
@@ -870,10 +879,10 @@ async function generateModuleRegistryCli(options) {
870
879
  }
871
880
  }
872
881
  {
873
- const appFile = path.join(roots.appBase, "vector.ts");
874
- const pkgFile = path.join(roots.pkgBase, "vector.ts");
875
- const hasApp = fs.existsSync(appFile);
876
- const hasPkg = fs.existsSync(pkgFile);
882
+ const appFile = findModuleFile(roots.appBase, "vector");
883
+ const pkgFile = findModuleFile(roots.pkgBase, "vector");
884
+ const hasApp = !!appFile;
885
+ const hasPkg = !!pkgFile;
877
886
  if (hasApp || hasPkg) {
878
887
  const importName = `VECTOR_${toVar(modId)}_${importId++}`;
879
888
  const importPath = hasApp ? `${appImportBase}/vector` : `${imps.pkgBase}/vector`;
@@ -882,10 +891,10 @@ async function generateModuleRegistryCli(options) {
882
891
  }
883
892
  }
884
893
  {
885
- const appFile = path.join(roots.appBase, "data", "fields.ts");
886
- const pkgFile = path.join(roots.pkgBase, "data", "fields.ts");
887
- const hasApp = fs.existsSync(appFile);
888
- const hasPkg = fs.existsSync(pkgFile);
894
+ const appFile = findModuleFile(roots.appBase, "data", "fields");
895
+ const pkgFile = findModuleFile(roots.pkgBase, "data", "fields");
896
+ const hasApp = !!appFile;
897
+ const hasPkg = !!pkgFile;
889
898
  if (hasApp || hasPkg) {
890
899
  const importName = `F_${toVar(modId)}_${importId++}`;
891
900
  const importPath = hasApp ? `${appImportBase}/data/fields` : `${imps.pkgBase}/data/fields`;
@@ -893,9 +902,9 @@ async function generateModuleRegistryCli(options) {
893
902
  fieldsImportName = importName;
894
903
  }
895
904
  }
896
- const cliApp = path.join(roots.appBase, "cli.ts");
897
- const cliPkg = path.join(roots.pkgBase, "cli.ts");
898
- const cliPath = fs.existsSync(cliApp) ? cliApp : fs.existsSync(cliPkg) ? cliPkg : null;
905
+ const cliApp = findModuleFile(roots.appBase, "cli");
906
+ const cliPkg = findModuleFile(roots.pkgBase, "cli");
907
+ const cliPath = cliApp ?? cliPkg;
899
908
  if (cliPath) {
900
909
  const importName = `CLI_${toVar(modId)}`;
901
910
  const importPath = cliPath.startsWith(roots.appBase) ? `${appImportBase}/cli` : `${imps.pkgBase}/cli`;