migratex 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
- # migratex
1
+ # Migratex
2
2
 
3
- a schema diff and migration engine that sits on top of your ORM. it treats database changes like a graph not a numbered list enabling deterministic migrations, drift detection, and safe CI/CD workflows.
3
+ A schema diff and migration engine that sits on top of your ORM. it treats database changes like a graph not a numbered list enabling deterministic migrations, drift detection, and safe CI/CD workflows.
4
4
 
5
- works with **drizzle**, **prisma**, and **typeorm**. supports **postgres** (mysql coming soon).
5
+ works with **drizzle**, **prisma**, and **typeorm**. supports **postgres** and **mysql**.
6
6
 
7
- ## why
7
+ ## Why
8
8
 
9
9
  every ORM handles migrations the same way: numbered files in a folder. `001_create_users.sql`, `002_add_posts.sql`, and so on. works fine solo. put a team on it and two devs on different branches will both create `004_*.sql`, someone has to manually rename theirs, and you pray the SQL doesn't conflict. at scale this is a constant source of broken deploys, merge pain, and wasted time.
10
10
 
@@ -56,16 +56,20 @@ npm install migratex
56
56
  ```bash
57
57
  $ migratex init
58
58
 
59
- ? ORM: drizzle
60
- ? Dialect: pg
61
- ? Schema path: ./src/schema.ts
62
- ? Connection: postgresql://user:pass@localhost:5432/mydb
63
- ? Migrations dir: ./migrations
59
+ Initializing migratex...
60
+
61
+ Detected ORM: drizzle # or prisma, typeorm — auto-detected from package.json
62
+ Detected database: pg (from drizzle config)
63
+ Schema path [./db/schema]:
64
64
 
65
65
  Created migratex.config.yaml
66
66
  Created migrations/
67
+
68
+ migratex will read the database connection from your drizzle config at runtime.
67
69
  ```
68
70
 
71
+ no connection setup needed — migratex auto-detects your ORM from `package.json`, reads the dialect from your ORM config (`drizzle.config.ts`, `schema.prisma`, `ormconfig.json`), and uses the same env vars your ORM already uses for the database connection.
72
+
69
73
  ### generate
70
74
 
71
75
  change your ORM schema, then:
@@ -178,11 +182,12 @@ each migration node has:
178
182
  # migratex.config.yaml
179
183
  orm: drizzle # drizzle | prisma | typeorm
180
184
  dialect: pg # pg | mysql
181
- connection: postgresql://user:pass@localhost:5432/mydb
182
- schemaPath: ./src/schema.ts
185
+ schemaPath: ./db/schema
183
186
  migrationsDir: ./migrations
184
187
  ```
185
188
 
189
+ no connection field needed — migratex reads it from your ORM's config at runtime. it checks `drizzle.config.ts`, `schema.prisma`, or `ormconfig.json` and uses the same env vars your ORM already uses (`DATABASE_URL`, `PGHOST`, etc.).
190
+
186
191
  ## license
187
192
 
188
193
  MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "migratex",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "Schema diff and migration engine for ORMs (Drizzle, Prisma, TypeORM)",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -15,7 +15,8 @@
15
15
  },
16
16
  "files": [
17
17
  "bin/migratex.js",
18
- "install.js"
18
+ "install.js",
19
+ "sidecar/dist/"
19
20
  ],
20
21
  "keywords": [
21
22
  "migrations",
@@ -0,0 +1,514 @@
1
+ #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __esm = (fn, res) => function __init() {
5
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
+ };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+
12
+ // src/drizzle.ts
13
+ var drizzle_exports = {};
14
+ __export(drizzle_exports, {
15
+ exportDrizzleSchema: () => exportDrizzleSchema
16
+ });
17
+ async function exportDrizzleSchema(schemaPath, dialect) {
18
+ const schemaModule = await import(schemaPath);
19
+ const tables = [];
20
+ const enumSet = /* @__PURE__ */ new Map();
21
+ for (const [, value] of Object.entries(schemaModule)) {
22
+ if (!isDrizzleTable(value)) continue;
23
+ const table = extractTable(value, dialect, enumSet);
24
+ if (table) tables.push(table);
25
+ }
26
+ return {
27
+ dialect,
28
+ tables,
29
+ enums: Array.from(enumSet.entries()).map(([name, values]) => ({
30
+ name,
31
+ values
32
+ }))
33
+ };
34
+ }
35
+ function isDrizzleTable(obj) {
36
+ return obj !== null && typeof obj === "object" && (DrizzleSymbols.IsDrizzleTable in obj || DrizzleSymbols.Columns in obj);
37
+ }
38
+ function extractTable(tableObj, dialect, enumSet) {
39
+ const tableName = tableObj[DrizzleSymbols.Name] || tableObj[DrizzleSymbols.BaseName];
40
+ if (!tableName) return null;
41
+ const drizzleColumns = tableObj[DrizzleSymbols.Columns];
42
+ if (!drizzleColumns) return null;
43
+ const columns = [];
44
+ const pkColumns = [];
45
+ const indexes = [];
46
+ const foreignKeys = [];
47
+ for (const [colName, colDef] of Object.entries(drizzleColumns)) {
48
+ const column = extractColumn(colName, colDef, dialect, enumSet);
49
+ columns.push(column);
50
+ if (colDef.primary || colDef.primaryKey) {
51
+ pkColumns.push(colName);
52
+ }
53
+ }
54
+ if (typeof tableObj.getSQL === "function") {
55
+ }
56
+ const table = {
57
+ name: tableName,
58
+ columns,
59
+ indexes,
60
+ foreignKeys
61
+ };
62
+ if (pkColumns.length > 0) {
63
+ table.primaryKey = { columns: pkColumns };
64
+ }
65
+ return table;
66
+ }
67
+ function extractColumn(name, colDef, dialect, enumSet) {
68
+ const colType = mapDrizzleType(colDef, dialect, enumSet);
69
+ const column = {
70
+ name: colDef.name || name,
71
+ type: colType,
72
+ nullable: colDef.notNull !== true
73
+ };
74
+ if (colDef.hasDefault && colDef.default !== void 0) {
75
+ if (typeof colDef.default === "function") {
76
+ column.default = { kind: "expression", value: String(colDef.default) };
77
+ } else {
78
+ column.default = { kind: "value", value: String(colDef.default) };
79
+ }
80
+ }
81
+ if (colDef.isUnique) {
82
+ column.unique = true;
83
+ }
84
+ return column;
85
+ }
86
+ function mapDrizzleType(colDef, dialect, enumSet) {
87
+ const dataType = (colDef.dataType || colDef.columnType || colDef.getSQLType?.() || "").toLowerCase();
88
+ const sqlName = (colDef.sqlName || "").toLowerCase();
89
+ if (dataType.includes("serial") || sqlName.includes("serial")) {
90
+ if (dataType.includes("bigserial") || sqlName.includes("bigserial")) {
91
+ return { kind: "serial", size: "bigserial" };
92
+ }
93
+ if (dataType.includes("smallserial") || sqlName.includes("smallserial")) {
94
+ return { kind: "serial", size: "smallserial" };
95
+ }
96
+ return { kind: "serial" };
97
+ }
98
+ if (dataType === "number" || dataType === "integer" || sqlName.includes("int")) {
99
+ if (sqlName.includes("bigint") || dataType.includes("bigint")) {
100
+ return { kind: "int", size: "bigint" };
101
+ }
102
+ if (sqlName.includes("smallint") || dataType.includes("smallint")) {
103
+ return { kind: "int", size: "smallint" };
104
+ }
105
+ return { kind: "int" };
106
+ }
107
+ if (dataType === "string" || sqlName.includes("text") || sqlName.includes("varchar")) {
108
+ const length = colDef.length || colDef.config?.length;
109
+ return { kind: "text", ...length ? { length } : {} };
110
+ }
111
+ if (dataType === "boolean" || sqlName.includes("bool")) {
112
+ return { kind: "boolean" };
113
+ }
114
+ if (sqlName.includes("timestamp") || dataType.includes("timestamp")) {
115
+ return {
116
+ kind: "timestamp",
117
+ withTimezone: sqlName.includes("tz") || colDef.withTimezone === true
118
+ };
119
+ }
120
+ if (sqlName === "date" || dataType === "date") {
121
+ return { kind: "date" };
122
+ }
123
+ if (sqlName.includes("json")) {
124
+ return { kind: "json", binary: sqlName === "jsonb" };
125
+ }
126
+ if (sqlName === "uuid" || dataType === "uuid") {
127
+ return { kind: "uuid" };
128
+ }
129
+ if (sqlName.includes("real") || sqlName.includes("float")) {
130
+ return { kind: "float" };
131
+ }
132
+ if (sqlName.includes("double")) {
133
+ return { kind: "float", size: "double" };
134
+ }
135
+ if (sqlName.includes("numeric") || sqlName.includes("decimal")) {
136
+ return {
137
+ kind: "decimal",
138
+ precision: colDef.precision,
139
+ scale: colDef.scale
140
+ };
141
+ }
142
+ if (colDef.enumValues && Array.isArray(colDef.enumValues)) {
143
+ const enumName = colDef.enumName || colDef.name || "unknown_enum";
144
+ enumSet.set(enumName, colDef.enumValues);
145
+ return { kind: "enum", enumName };
146
+ }
147
+ return { kind: "custom", raw: sqlName || dataType || "unknown" };
148
+ }
149
+ var DrizzleSymbols;
150
+ var init_drizzle = __esm({
151
+ "src/drizzle.ts"() {
152
+ "use strict";
153
+ DrizzleSymbols = {
154
+ Columns: /* @__PURE__ */ Symbol.for("drizzle:Columns"),
155
+ Name: /* @__PURE__ */ Symbol.for("drizzle:Name"),
156
+ Schema: /* @__PURE__ */ Symbol.for("drizzle:Schema"),
157
+ IsDrizzleTable: /* @__PURE__ */ Symbol.for("drizzle:IsDrizzleTable"),
158
+ BaseName: /* @__PURE__ */ Symbol.for("drizzle:BaseName")
159
+ };
160
+ }
161
+ });
162
+
163
+ // src/prisma.ts
164
+ var prisma_exports = {};
165
+ __export(prisma_exports, {
166
+ exportPrismaSchema: () => exportPrismaSchema
167
+ });
168
+ async function exportPrismaSchema(schemaPath) {
169
+ let getDMMF;
170
+ try {
171
+ const internals = await import("@prisma/internals");
172
+ getDMMF = internals.getDMMF;
173
+ } catch {
174
+ throw new Error(
175
+ "Cannot find @prisma/internals. Install it: npm install @prisma/internals"
176
+ );
177
+ }
178
+ const { readFileSync } = await import("fs");
179
+ const datamodel = readFileSync(schemaPath, "utf-8");
180
+ const dialect = detectDialect(datamodel);
181
+ const dmmf = await getDMMF({ datamodel });
182
+ const tables = [];
183
+ const enums = [];
184
+ for (const model of dmmf.datamodel.models) {
185
+ tables.push(convertModel(model, dmmf.datamodel.models, dialect));
186
+ }
187
+ for (const enumDef of dmmf.datamodel.enums) {
188
+ enums.push({
189
+ name: enumDef.name,
190
+ values: enumDef.values.map((v) => v.name)
191
+ });
192
+ }
193
+ return { dialect, tables, enums };
194
+ }
195
+ function detectDialect(datamodel) {
196
+ const match = datamodel.match(/provider\s*=\s*"(\w+)"/);
197
+ if (match) {
198
+ const provider = match[1].toLowerCase();
199
+ if (provider === "mysql") return "mysql";
200
+ }
201
+ return "pg";
202
+ }
203
+ function convertModel(model, allModels, dialect) {
204
+ const columns = [];
205
+ const foreignKeys = [];
206
+ const indexes = [];
207
+ const pkColumns = [];
208
+ for (const field of model.fields) {
209
+ if (field.kind === "object") continue;
210
+ const column = convertField(field, dialect);
211
+ columns.push(column);
212
+ if (field.isId) {
213
+ pkColumns.push(field.name);
214
+ }
215
+ }
216
+ for (const field of model.fields) {
217
+ if (field.kind === "object" && field.relationFromFields?.length > 0) {
218
+ const fk = {
219
+ name: `fk_${model.name.toLowerCase()}_${field.name}`,
220
+ columns: field.relationFromFields,
221
+ referencedTable: field.type.toLowerCase(),
222
+ referencedColumns: field.relationToFields || ["id"],
223
+ onDelete: mapPrismaAction(field.relationOnDelete),
224
+ onUpdate: mapPrismaAction(field.relationOnUpdate)
225
+ };
226
+ foreignKeys.push(fk);
227
+ }
228
+ }
229
+ if (model.uniqueFields) {
230
+ for (const fields of model.uniqueFields) {
231
+ indexes.push({
232
+ name: `${model.name.toLowerCase()}_${fields.join("_")}_unique`,
233
+ columns: fields,
234
+ unique: true
235
+ });
236
+ }
237
+ }
238
+ const table = {
239
+ // Prisma uses PascalCase model names; DB uses snake_case
240
+ // The @@map attribute overrides this, but dbName captures it
241
+ name: model.dbName || model.name.toLowerCase(),
242
+ columns,
243
+ indexes,
244
+ foreignKeys
245
+ };
246
+ if (pkColumns.length > 0) {
247
+ table.primaryKey = { columns: pkColumns };
248
+ }
249
+ return table;
250
+ }
251
+ function convertField(field, dialect) {
252
+ return {
253
+ name: field.dbName || field.name,
254
+ type: mapPrismaType(field, dialect),
255
+ nullable: !field.isRequired,
256
+ ...field.hasDefaultValue && field.default !== void 0 ? { default: parsePrismaDefault(field.default) } : {},
257
+ ...field.isId ? { primaryKey: true } : {},
258
+ ...field.isUnique ? { unique: true } : {}
259
+ };
260
+ }
261
+ function mapPrismaType(field, dialect) {
262
+ const nativeType = field.nativeType;
263
+ switch (field.type) {
264
+ case "Int":
265
+ if (nativeType) {
266
+ if (nativeType[0] === "SmallInt") return { kind: "int", size: "smallint" };
267
+ if (nativeType[0] === "BigInt") return { kind: "int", size: "bigint" };
268
+ }
269
+ return { kind: "int" };
270
+ case "BigInt":
271
+ return { kind: "int", size: "bigint" };
272
+ case "Float":
273
+ return { kind: "float", size: "double" };
274
+ case "Decimal":
275
+ return {
276
+ kind: "decimal",
277
+ precision: nativeType?.[1]?.precision || 65,
278
+ scale: nativeType?.[1]?.scale || 30
279
+ };
280
+ case "String":
281
+ if (nativeType?.[0] === "VarChar") {
282
+ return { kind: "text", length: nativeType[1]?.length || 255 };
283
+ }
284
+ return { kind: "text" };
285
+ case "Boolean":
286
+ return { kind: "boolean" };
287
+ case "DateTime":
288
+ return { kind: "timestamp", withTimezone: dialect === "pg" };
289
+ case "Json":
290
+ return { kind: "json", binary: dialect === "pg" };
291
+ case "Bytes":
292
+ return { kind: "bytea" };
293
+ default:
294
+ if (field.kind === "enum") {
295
+ return { kind: "enum", enumName: field.type };
296
+ }
297
+ return { kind: "custom", raw: field.type };
298
+ }
299
+ }
300
+ function parsePrismaDefault(def) {
301
+ if (typeof def === "object" && def.name) {
302
+ return { kind: "expression", value: `${def.name}()` };
303
+ }
304
+ return { kind: "value", value: String(def) };
305
+ }
306
+ function mapPrismaAction(action) {
307
+ if (!action) return void 0;
308
+ const map = {
309
+ Cascade: "CASCADE",
310
+ SetNull: "SET NULL",
311
+ SetDefault: "SET DEFAULT",
312
+ Restrict: "RESTRICT",
313
+ NoAction: "NO ACTION"
314
+ };
315
+ return map[action];
316
+ }
317
+ var init_prisma = __esm({
318
+ "src/prisma.ts"() {
319
+ "use strict";
320
+ }
321
+ });
322
+
323
+ // src/typeorm.ts
324
+ var typeorm_exports = {};
325
+ __export(typeorm_exports, {
326
+ exportTypeORMSchema: () => exportTypeORMSchema
327
+ });
328
+ async function exportTypeORMSchema(schemaPath, dialect) {
329
+ let getMetadataArgsStorage;
330
+ try {
331
+ const typeorm = await import("typeorm");
332
+ getMetadataArgsStorage = typeorm.getMetadataArgsStorage;
333
+ } catch {
334
+ throw new Error("Cannot find typeorm. Install it: npm install typeorm reflect-metadata");
335
+ }
336
+ await import(schemaPath);
337
+ const storage = getMetadataArgsStorage();
338
+ const tables = [];
339
+ for (const tableArgs of storage.tables) {
340
+ const table = convertTypeORMTable(tableArgs, storage, dialect);
341
+ tables.push(table);
342
+ }
343
+ return { dialect, tables };
344
+ }
345
+ function convertTypeORMTable(tableArgs, storage, dialect) {
346
+ const entityTarget = tableArgs.target;
347
+ const tableName = tableArgs.name || entityTarget.name?.toLowerCase() || "unknown";
348
+ const columnArgs = storage.columns.filter(
349
+ (c) => c.target === entityTarget || c.target?.prototype instanceof entityTarget
350
+ );
351
+ const columns = [];
352
+ const foreignKeys = [];
353
+ for (const colArg of columnArgs) {
354
+ columns.push(convertTypeORMColumn(colArg, dialect));
355
+ }
356
+ const relations = storage.relations.filter(
357
+ (r) => r.target === entityTarget
358
+ );
359
+ for (const rel of relations) {
360
+ const joinColumns = storage.joinColumns.filter(
361
+ (jc) => jc.target === entityTarget && jc.propertyName === rel.propertyName
362
+ );
363
+ for (const jc of joinColumns) {
364
+ foreignKeys.push({
365
+ name: `fk_${tableName}_${jc.name || rel.propertyName}`,
366
+ columns: [jc.name || `${rel.propertyName}Id`],
367
+ referencedTable: typeof rel.type === "function" ? rel.type().name?.toLowerCase() : String(rel.type).toLowerCase(),
368
+ referencedColumns: [jc.referencedColumnName || "id"]
369
+ });
370
+ }
371
+ }
372
+ const pkColumns = columns.filter((c) => c.primaryKey).map((c) => c.name);
373
+ const table = {
374
+ name: tableName,
375
+ columns,
376
+ foreignKeys
377
+ };
378
+ if (pkColumns.length > 0) {
379
+ table.primaryKey = { columns: pkColumns };
380
+ }
381
+ return table;
382
+ }
383
+ function convertTypeORMColumn(colArg, dialect) {
384
+ const options = colArg.options || {};
385
+ const isPrimary = colArg.mode === "regular" ? options.primary : colArg.mode === "objectId";
386
+ return {
387
+ name: options.name || colArg.propertyName,
388
+ type: mapTypeORMType(colArg, options, dialect),
389
+ nullable: options.nullable === true,
390
+ ...isPrimary ? { primaryKey: true } : {},
391
+ ...options.unique ? { unique: true } : {},
392
+ ...options.default !== void 0 ? {
393
+ default: {
394
+ kind: typeof options.default === "string" && options.default.includes("(") ? "expression" : "value",
395
+ value: String(options.default)
396
+ }
397
+ } : {}
398
+ };
399
+ }
400
+ function mapTypeORMType(colArg, options, dialect) {
401
+ const type = options.type || colArg.mode === "createDate" || colArg.mode === "updateDate" ? "timestamp" : "unknown";
402
+ switch (type.toLowerCase()) {
403
+ case "int":
404
+ case "integer":
405
+ case "int4":
406
+ return { kind: "int" };
407
+ case "smallint":
408
+ case "int2":
409
+ return { kind: "int", size: "smallint" };
410
+ case "bigint":
411
+ case "int8":
412
+ return { kind: "int", size: "bigint" };
413
+ case "varchar":
414
+ case "character varying":
415
+ return { kind: "text", length: options.length || 255 };
416
+ case "text":
417
+ return { kind: "text" };
418
+ case "boolean":
419
+ case "bool":
420
+ return { kind: "boolean" };
421
+ case "timestamp":
422
+ case "timestamp with time zone":
423
+ case "timestamptz":
424
+ return { kind: "timestamp", withTimezone: dialect === "pg" };
425
+ case "timestamp without time zone":
426
+ return { kind: "timestamp" };
427
+ case "date":
428
+ return { kind: "date" };
429
+ case "json":
430
+ return { kind: "json" };
431
+ case "jsonb":
432
+ return { kind: "json", binary: true };
433
+ case "uuid":
434
+ return { kind: "uuid" };
435
+ case "float":
436
+ case "real":
437
+ case "float4":
438
+ return { kind: "float" };
439
+ case "double precision":
440
+ case "float8":
441
+ return { kind: "float", size: "double" };
442
+ case "decimal":
443
+ case "numeric":
444
+ return {
445
+ kind: "decimal",
446
+ precision: options.precision,
447
+ scale: options.scale
448
+ };
449
+ case "bytea":
450
+ case "blob":
451
+ return { kind: "bytea" };
452
+ case "enum":
453
+ return { kind: "enum", enumName: options.enum?.name || "unknown" };
454
+ default:
455
+ return { kind: "custom", raw: type };
456
+ }
457
+ }
458
+ var init_typeorm = __esm({
459
+ "src/typeorm.ts"() {
460
+ "use strict";
461
+ }
462
+ });
463
+
464
+ // bin/migratex-export.ts
465
+ import { resolve } from "path";
466
+ async function main() {
467
+ const args = parseArgs(process.argv.slice(2));
468
+ if (!args.orm) {
469
+ console.error("Usage: migratex-export --orm <drizzle|prisma|typeorm> --schema <path> [--dialect <pg|mysql>]");
470
+ process.exit(1);
471
+ }
472
+ if (!args.schema) {
473
+ console.error("Error: --schema is required");
474
+ process.exit(1);
475
+ }
476
+ const schemaPath = resolve(process.cwd(), args.schema);
477
+ const dialect = args.dialect || "pg";
478
+ let schema;
479
+ switch (args.orm) {
480
+ case "drizzle": {
481
+ const { exportDrizzleSchema: exportDrizzleSchema2 } = await Promise.resolve().then(() => (init_drizzle(), drizzle_exports));
482
+ schema = await exportDrizzleSchema2(schemaPath, dialect);
483
+ break;
484
+ }
485
+ case "prisma": {
486
+ const { exportPrismaSchema: exportPrismaSchema2 } = await Promise.resolve().then(() => (init_prisma(), prisma_exports));
487
+ schema = await exportPrismaSchema2(schemaPath);
488
+ break;
489
+ }
490
+ case "typeorm": {
491
+ const { exportTypeORMSchema: exportTypeORMSchema2 } = await Promise.resolve().then(() => (init_typeorm(), typeorm_exports));
492
+ schema = await exportTypeORMSchema2(schemaPath, dialect);
493
+ break;
494
+ }
495
+ default:
496
+ console.error(`Unknown ORM: ${args.orm}. Supported: drizzle, prisma, typeorm`);
497
+ process.exit(1);
498
+ }
499
+ console.log(JSON.stringify(schema, null, 2));
500
+ }
501
+ function parseArgs(argv) {
502
+ const args = {};
503
+ for (let i = 0; i < argv.length; i++) {
504
+ if (argv[i].startsWith("--") && i + 1 < argv.length) {
505
+ args[argv[i].slice(2)] = argv[i + 1];
506
+ i++;
507
+ }
508
+ }
509
+ return args;
510
+ }
511
+ main().catch((err) => {
512
+ console.error("Error:", err.message);
513
+ process.exit(1);
514
+ });
@@ -0,0 +1,428 @@
1
+ // src/drizzle.ts
2
+ var DrizzleSymbols = {
3
+ Columns: /* @__PURE__ */ Symbol.for("drizzle:Columns"),
4
+ Name: /* @__PURE__ */ Symbol.for("drizzle:Name"),
5
+ Schema: /* @__PURE__ */ Symbol.for("drizzle:Schema"),
6
+ IsDrizzleTable: /* @__PURE__ */ Symbol.for("drizzle:IsDrizzleTable"),
7
+ BaseName: /* @__PURE__ */ Symbol.for("drizzle:BaseName")
8
+ };
9
+ async function exportDrizzleSchema(schemaPath, dialect) {
10
+ const schemaModule = await import(schemaPath);
11
+ const tables = [];
12
+ const enumSet = /* @__PURE__ */ new Map();
13
+ for (const [, value] of Object.entries(schemaModule)) {
14
+ if (!isDrizzleTable(value)) continue;
15
+ const table = extractTable(value, dialect, enumSet);
16
+ if (table) tables.push(table);
17
+ }
18
+ return {
19
+ dialect,
20
+ tables,
21
+ enums: Array.from(enumSet.entries()).map(([name, values]) => ({
22
+ name,
23
+ values
24
+ }))
25
+ };
26
+ }
27
+ function isDrizzleTable(obj) {
28
+ return obj !== null && typeof obj === "object" && (DrizzleSymbols.IsDrizzleTable in obj || DrizzleSymbols.Columns in obj);
29
+ }
30
+ function extractTable(tableObj, dialect, enumSet) {
31
+ const tableName = tableObj[DrizzleSymbols.Name] || tableObj[DrizzleSymbols.BaseName];
32
+ if (!tableName) return null;
33
+ const drizzleColumns = tableObj[DrizzleSymbols.Columns];
34
+ if (!drizzleColumns) return null;
35
+ const columns = [];
36
+ const pkColumns = [];
37
+ const indexes = [];
38
+ const foreignKeys = [];
39
+ for (const [colName, colDef] of Object.entries(drizzleColumns)) {
40
+ const column = extractColumn(colName, colDef, dialect, enumSet);
41
+ columns.push(column);
42
+ if (colDef.primary || colDef.primaryKey) {
43
+ pkColumns.push(colName);
44
+ }
45
+ }
46
+ if (typeof tableObj.getSQL === "function") {
47
+ }
48
+ const table = {
49
+ name: tableName,
50
+ columns,
51
+ indexes,
52
+ foreignKeys
53
+ };
54
+ if (pkColumns.length > 0) {
55
+ table.primaryKey = { columns: pkColumns };
56
+ }
57
+ return table;
58
+ }
59
+ function extractColumn(name, colDef, dialect, enumSet) {
60
+ const colType = mapDrizzleType(colDef, dialect, enumSet);
61
+ const column = {
62
+ name: colDef.name || name,
63
+ type: colType,
64
+ nullable: colDef.notNull !== true
65
+ };
66
+ if (colDef.hasDefault && colDef.default !== void 0) {
67
+ if (typeof colDef.default === "function") {
68
+ column.default = { kind: "expression", value: String(colDef.default) };
69
+ } else {
70
+ column.default = { kind: "value", value: String(colDef.default) };
71
+ }
72
+ }
73
+ if (colDef.isUnique) {
74
+ column.unique = true;
75
+ }
76
+ return column;
77
+ }
78
+ function mapDrizzleType(colDef, dialect, enumSet) {
79
+ const dataType = (colDef.dataType || colDef.columnType || colDef.getSQLType?.() || "").toLowerCase();
80
+ const sqlName = (colDef.sqlName || "").toLowerCase();
81
+ if (dataType.includes("serial") || sqlName.includes("serial")) {
82
+ if (dataType.includes("bigserial") || sqlName.includes("bigserial")) {
83
+ return { kind: "serial", size: "bigserial" };
84
+ }
85
+ if (dataType.includes("smallserial") || sqlName.includes("smallserial")) {
86
+ return { kind: "serial", size: "smallserial" };
87
+ }
88
+ return { kind: "serial" };
89
+ }
90
+ if (dataType === "number" || dataType === "integer" || sqlName.includes("int")) {
91
+ if (sqlName.includes("bigint") || dataType.includes("bigint")) {
92
+ return { kind: "int", size: "bigint" };
93
+ }
94
+ if (sqlName.includes("smallint") || dataType.includes("smallint")) {
95
+ return { kind: "int", size: "smallint" };
96
+ }
97
+ return { kind: "int" };
98
+ }
99
+ if (dataType === "string" || sqlName.includes("text") || sqlName.includes("varchar")) {
100
+ const length = colDef.length || colDef.config?.length;
101
+ return { kind: "text", ...length ? { length } : {} };
102
+ }
103
+ if (dataType === "boolean" || sqlName.includes("bool")) {
104
+ return { kind: "boolean" };
105
+ }
106
+ if (sqlName.includes("timestamp") || dataType.includes("timestamp")) {
107
+ return {
108
+ kind: "timestamp",
109
+ withTimezone: sqlName.includes("tz") || colDef.withTimezone === true
110
+ };
111
+ }
112
+ if (sqlName === "date" || dataType === "date") {
113
+ return { kind: "date" };
114
+ }
115
+ if (sqlName.includes("json")) {
116
+ return { kind: "json", binary: sqlName === "jsonb" };
117
+ }
118
+ if (sqlName === "uuid" || dataType === "uuid") {
119
+ return { kind: "uuid" };
120
+ }
121
+ if (sqlName.includes("real") || sqlName.includes("float")) {
122
+ return { kind: "float" };
123
+ }
124
+ if (sqlName.includes("double")) {
125
+ return { kind: "float", size: "double" };
126
+ }
127
+ if (sqlName.includes("numeric") || sqlName.includes("decimal")) {
128
+ return {
129
+ kind: "decimal",
130
+ precision: colDef.precision,
131
+ scale: colDef.scale
132
+ };
133
+ }
134
+ if (colDef.enumValues && Array.isArray(colDef.enumValues)) {
135
+ const enumName = colDef.enumName || colDef.name || "unknown_enum";
136
+ enumSet.set(enumName, colDef.enumValues);
137
+ return { kind: "enum", enumName };
138
+ }
139
+ return { kind: "custom", raw: sqlName || dataType || "unknown" };
140
+ }
141
+
142
+ // src/prisma.ts
143
+ async function exportPrismaSchema(schemaPath) {
144
+ let getDMMF;
145
+ try {
146
+ const internals = await import("@prisma/internals");
147
+ getDMMF = internals.getDMMF;
148
+ } catch {
149
+ throw new Error(
150
+ "Cannot find @prisma/internals. Install it: npm install @prisma/internals"
151
+ );
152
+ }
153
+ const { readFileSync } = await import("fs");
154
+ const datamodel = readFileSync(schemaPath, "utf-8");
155
+ const dialect = detectDialect(datamodel);
156
+ const dmmf = await getDMMF({ datamodel });
157
+ const tables = [];
158
+ const enums = [];
159
+ for (const model of dmmf.datamodel.models) {
160
+ tables.push(convertModel(model, dmmf.datamodel.models, dialect));
161
+ }
162
+ for (const enumDef of dmmf.datamodel.enums) {
163
+ enums.push({
164
+ name: enumDef.name,
165
+ values: enumDef.values.map((v) => v.name)
166
+ });
167
+ }
168
+ return { dialect, tables, enums };
169
+ }
170
+ function detectDialect(datamodel) {
171
+ const match = datamodel.match(/provider\s*=\s*"(\w+)"/);
172
+ if (match) {
173
+ const provider = match[1].toLowerCase();
174
+ if (provider === "mysql") return "mysql";
175
+ }
176
+ return "pg";
177
+ }
178
+ function convertModel(model, allModels, dialect) {
179
+ const columns = [];
180
+ const foreignKeys = [];
181
+ const indexes = [];
182
+ const pkColumns = [];
183
+ for (const field of model.fields) {
184
+ if (field.kind === "object") continue;
185
+ const column = convertField(field, dialect);
186
+ columns.push(column);
187
+ if (field.isId) {
188
+ pkColumns.push(field.name);
189
+ }
190
+ }
191
+ for (const field of model.fields) {
192
+ if (field.kind === "object" && field.relationFromFields?.length > 0) {
193
+ const fk = {
194
+ name: `fk_${model.name.toLowerCase()}_${field.name}`,
195
+ columns: field.relationFromFields,
196
+ referencedTable: field.type.toLowerCase(),
197
+ referencedColumns: field.relationToFields || ["id"],
198
+ onDelete: mapPrismaAction(field.relationOnDelete),
199
+ onUpdate: mapPrismaAction(field.relationOnUpdate)
200
+ };
201
+ foreignKeys.push(fk);
202
+ }
203
+ }
204
+ if (model.uniqueFields) {
205
+ for (const fields of model.uniqueFields) {
206
+ indexes.push({
207
+ name: `${model.name.toLowerCase()}_${fields.join("_")}_unique`,
208
+ columns: fields,
209
+ unique: true
210
+ });
211
+ }
212
+ }
213
+ const table = {
214
+ // Prisma uses PascalCase model names; DB uses snake_case
215
+ // The @@map attribute overrides this, but dbName captures it
216
+ name: model.dbName || model.name.toLowerCase(),
217
+ columns,
218
+ indexes,
219
+ foreignKeys
220
+ };
221
+ if (pkColumns.length > 0) {
222
+ table.primaryKey = { columns: pkColumns };
223
+ }
224
+ return table;
225
+ }
226
+ function convertField(field, dialect) {
227
+ return {
228
+ name: field.dbName || field.name,
229
+ type: mapPrismaType(field, dialect),
230
+ nullable: !field.isRequired,
231
+ ...field.hasDefaultValue && field.default !== void 0 ? { default: parsePrismaDefault(field.default) } : {},
232
+ ...field.isId ? { primaryKey: true } : {},
233
+ ...field.isUnique ? { unique: true } : {}
234
+ };
235
+ }
236
+ function mapPrismaType(field, dialect) {
237
+ const nativeType = field.nativeType;
238
+ switch (field.type) {
239
+ case "Int":
240
+ if (nativeType) {
241
+ if (nativeType[0] === "SmallInt") return { kind: "int", size: "smallint" };
242
+ if (nativeType[0] === "BigInt") return { kind: "int", size: "bigint" };
243
+ }
244
+ return { kind: "int" };
245
+ case "BigInt":
246
+ return { kind: "int", size: "bigint" };
247
+ case "Float":
248
+ return { kind: "float", size: "double" };
249
+ case "Decimal":
250
+ return {
251
+ kind: "decimal",
252
+ precision: nativeType?.[1]?.precision || 65,
253
+ scale: nativeType?.[1]?.scale || 30
254
+ };
255
+ case "String":
256
+ if (nativeType?.[0] === "VarChar") {
257
+ return { kind: "text", length: nativeType[1]?.length || 255 };
258
+ }
259
+ return { kind: "text" };
260
+ case "Boolean":
261
+ return { kind: "boolean" };
262
+ case "DateTime":
263
+ return { kind: "timestamp", withTimezone: dialect === "pg" };
264
+ case "Json":
265
+ return { kind: "json", binary: dialect === "pg" };
266
+ case "Bytes":
267
+ return { kind: "bytea" };
268
+ default:
269
+ if (field.kind === "enum") {
270
+ return { kind: "enum", enumName: field.type };
271
+ }
272
+ return { kind: "custom", raw: field.type };
273
+ }
274
+ }
275
+ function parsePrismaDefault(def) {
276
+ if (typeof def === "object" && def.name) {
277
+ return { kind: "expression", value: `${def.name}()` };
278
+ }
279
+ return { kind: "value", value: String(def) };
280
+ }
281
+ function mapPrismaAction(action) {
282
+ if (!action) return void 0;
283
+ const map = {
284
+ Cascade: "CASCADE",
285
+ SetNull: "SET NULL",
286
+ SetDefault: "SET DEFAULT",
287
+ Restrict: "RESTRICT",
288
+ NoAction: "NO ACTION"
289
+ };
290
+ return map[action];
291
+ }
292
+
293
+ // src/typeorm.ts
294
+ async function exportTypeORMSchema(schemaPath, dialect) {
295
+ let getMetadataArgsStorage;
296
+ try {
297
+ const typeorm = await import("typeorm");
298
+ getMetadataArgsStorage = typeorm.getMetadataArgsStorage;
299
+ } catch {
300
+ throw new Error("Cannot find typeorm. Install it: npm install typeorm reflect-metadata");
301
+ }
302
+ await import(schemaPath);
303
+ const storage = getMetadataArgsStorage();
304
+ const tables = [];
305
+ for (const tableArgs of storage.tables) {
306
+ const table = convertTypeORMTable(tableArgs, storage, dialect);
307
+ tables.push(table);
308
+ }
309
+ return { dialect, tables };
310
+ }
311
+ function convertTypeORMTable(tableArgs, storage, dialect) {
312
+ const entityTarget = tableArgs.target;
313
+ const tableName = tableArgs.name || entityTarget.name?.toLowerCase() || "unknown";
314
+ const columnArgs = storage.columns.filter(
315
+ (c) => c.target === entityTarget || c.target?.prototype instanceof entityTarget
316
+ );
317
+ const columns = [];
318
+ const foreignKeys = [];
319
+ for (const colArg of columnArgs) {
320
+ columns.push(convertTypeORMColumn(colArg, dialect));
321
+ }
322
+ const relations = storage.relations.filter(
323
+ (r) => r.target === entityTarget
324
+ );
325
+ for (const rel of relations) {
326
+ const joinColumns = storage.joinColumns.filter(
327
+ (jc) => jc.target === entityTarget && jc.propertyName === rel.propertyName
328
+ );
329
+ for (const jc of joinColumns) {
330
+ foreignKeys.push({
331
+ name: `fk_${tableName}_${jc.name || rel.propertyName}`,
332
+ columns: [jc.name || `${rel.propertyName}Id`],
333
+ referencedTable: typeof rel.type === "function" ? rel.type().name?.toLowerCase() : String(rel.type).toLowerCase(),
334
+ referencedColumns: [jc.referencedColumnName || "id"]
335
+ });
336
+ }
337
+ }
338
+ const pkColumns = columns.filter((c) => c.primaryKey).map((c) => c.name);
339
+ const table = {
340
+ name: tableName,
341
+ columns,
342
+ foreignKeys
343
+ };
344
+ if (pkColumns.length > 0) {
345
+ table.primaryKey = { columns: pkColumns };
346
+ }
347
+ return table;
348
+ }
349
+ function convertTypeORMColumn(colArg, dialect) {
350
+ const options = colArg.options || {};
351
+ const isPrimary = colArg.mode === "regular" ? options.primary : colArg.mode === "objectId";
352
+ return {
353
+ name: options.name || colArg.propertyName,
354
+ type: mapTypeORMType(colArg, options, dialect),
355
+ nullable: options.nullable === true,
356
+ ...isPrimary ? { primaryKey: true } : {},
357
+ ...options.unique ? { unique: true } : {},
358
+ ...options.default !== void 0 ? {
359
+ default: {
360
+ kind: typeof options.default === "string" && options.default.includes("(") ? "expression" : "value",
361
+ value: String(options.default)
362
+ }
363
+ } : {}
364
+ };
365
+ }
366
+ function mapTypeORMType(colArg, options, dialect) {
367
+ const type = options.type || colArg.mode === "createDate" || colArg.mode === "updateDate" ? "timestamp" : "unknown";
368
+ switch (type.toLowerCase()) {
369
+ case "int":
370
+ case "integer":
371
+ case "int4":
372
+ return { kind: "int" };
373
+ case "smallint":
374
+ case "int2":
375
+ return { kind: "int", size: "smallint" };
376
+ case "bigint":
377
+ case "int8":
378
+ return { kind: "int", size: "bigint" };
379
+ case "varchar":
380
+ case "character varying":
381
+ return { kind: "text", length: options.length || 255 };
382
+ case "text":
383
+ return { kind: "text" };
384
+ case "boolean":
385
+ case "bool":
386
+ return { kind: "boolean" };
387
+ case "timestamp":
388
+ case "timestamp with time zone":
389
+ case "timestamptz":
390
+ return { kind: "timestamp", withTimezone: dialect === "pg" };
391
+ case "timestamp without time zone":
392
+ return { kind: "timestamp" };
393
+ case "date":
394
+ return { kind: "date" };
395
+ case "json":
396
+ return { kind: "json" };
397
+ case "jsonb":
398
+ return { kind: "json", binary: true };
399
+ case "uuid":
400
+ return { kind: "uuid" };
401
+ case "float":
402
+ case "real":
403
+ case "float4":
404
+ return { kind: "float" };
405
+ case "double precision":
406
+ case "float8":
407
+ return { kind: "float", size: "double" };
408
+ case "decimal":
409
+ case "numeric":
410
+ return {
411
+ kind: "decimal",
412
+ precision: options.precision,
413
+ scale: options.scale
414
+ };
415
+ case "bytea":
416
+ case "blob":
417
+ return { kind: "bytea" };
418
+ case "enum":
419
+ return { kind: "enum", enumName: options.enum?.name || "unknown" };
420
+ default:
421
+ return { kind: "custom", raw: type };
422
+ }
423
+ }
424
+ export {
425
+ exportDrizzleSchema,
426
+ exportPrismaSchema,
427
+ exportTypeORMSchema
428
+ };