db-model-router 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +283 -25
  2. package/TODO.md +14 -0
  3. package/dbmr.schema.json +333 -0
  4. package/demo/.dockerignore +7 -0
  5. package/demo/.env.example +13 -0
  6. package/demo/Dockerfile +20 -0
  7. package/demo/app.js +37 -0
  8. package/demo/commons/add_migration.js +43 -0
  9. package/demo/commons/db.js +17 -0
  10. package/demo/commons/migrate.js +65 -0
  11. package/demo/commons/security.js +30 -0
  12. package/demo/commons/session.js +13 -0
  13. package/demo/dbmr.schema.json +362 -0
  14. package/demo/docs/llm.md +197 -0
  15. package/demo/llms.txt +70 -0
  16. package/demo/middleware/logger.js +67 -0
  17. package/demo/migrations/20260430155808_create_migrations_table.sql +6 -0
  18. package/demo/migrations/20260430155809_create_tables.sql +207 -0
  19. package/demo/models/addresses.js +22 -0
  20. package/demo/models/cart_items.js +18 -0
  21. package/demo/models/carts.js +16 -0
  22. package/demo/models/categories.js +20 -0
  23. package/demo/models/coupons.js +23 -0
  24. package/demo/models/order_items.js +21 -0
  25. package/demo/models/orders.js +25 -0
  26. package/demo/models/payments.js +21 -0
  27. package/demo/models/product_images.js +18 -0
  28. package/demo/models/product_reviews.js +20 -0
  29. package/demo/models/product_variants.js +20 -0
  30. package/demo/models/products.js +30 -0
  31. package/demo/models/shipments.js +19 -0
  32. package/demo/models/users.js +19 -0
  33. package/demo/models/wishlists.js +15 -0
  34. package/demo/openapi.json +5872 -0
  35. package/demo/package-lock.json +2810 -0
  36. package/demo/package.json +34 -0
  37. package/demo/routes/addresses.js +6 -0
  38. package/demo/routes/carts/cart_items.js +7 -0
  39. package/demo/routes/carts.js +6 -0
  40. package/demo/routes/categories.js +6 -0
  41. package/demo/routes/coupons.js +6 -0
  42. package/demo/routes/docs.js +18 -0
  43. package/demo/routes/health.js +35 -0
  44. package/demo/routes/index.js +39 -0
  45. package/demo/routes/orders/order_items.js +7 -0
  46. package/demo/routes/orders/payments.js +7 -0
  47. package/demo/routes/orders/shipments.js +7 -0
  48. package/demo/routes/orders.js +6 -0
  49. package/demo/routes/products/product_images.js +7 -0
  50. package/demo/routes/products/product_reviews.js +7 -0
  51. package/demo/routes/products/product_variants.js +7 -0
  52. package/demo/routes/products.js +6 -0
  53. package/demo/routes/users.js +6 -0
  54. package/demo/routes/wishlists.js +6 -0
  55. package/docker-compose.yml +1 -1
  56. package/package.json +16 -7
  57. package/scripts/demo-create.js +47 -0
  58. package/skill/SKILL.md +464 -0
  59. package/skill/references/cockroachdb.md +49 -0
  60. package/skill/references/dynamodb.md +53 -0
  61. package/skill/references/mongodb.md +56 -0
  62. package/skill/references/mssql.md +55 -0
  63. package/skill/references/oracle.md +52 -0
  64. package/skill/references/postgres.md +50 -0
  65. package/skill/references/redis.md +53 -0
  66. package/skill/references/sqlite3.md +43 -0
  67. package/src/cli/commands/generate.js +58 -17
  68. package/src/cli/commands/help.js +185 -0
  69. package/src/cli/commands/init.js +42 -14
  70. package/src/cli/commands/inspect.js +21 -3
  71. package/src/cli/diff-engine.js +52 -22
  72. package/src/cli/generate-docs-route.js +31 -0
  73. package/src/cli/generate-migration.js +356 -0
  74. package/src/cli/generate-model.js +5 -4
  75. package/src/cli/generate-route.js +79 -45
  76. package/src/cli/init/dependencies.js +17 -5
  77. package/src/cli/init/generators.js +1073 -64
  78. package/src/cli/init/prompt.js +37 -5
  79. package/src/cli/init.js +148 -25
  80. package/src/cli/main.js +90 -10
  81. package/src/cockroachdb/db.js +90 -59
  82. package/src/commons/route.js +20 -20
  83. package/src/commons/validator.js +58 -1
  84. package/src/dynamodb/db.js +50 -27
  85. package/src/index.js +2 -0
  86. package/src/mongodb/db.js +1 -0
  87. package/src/mssql/db.js +89 -61
  88. package/src/mysql/db.js +1 -0
  89. package/src/oracle/db.js +1 -0
  90. package/src/postgres/db.js +61 -41
  91. package/src/redis/db.js +1 -0
  92. package/src/schema/schema-parser.js +43 -1
  93. package/src/schema/schema-printer.js +8 -5
  94. package/src/schema/schema-to-meta.js +4 -0
  95. package/src/schema/schema-validator.js +20 -1
  96. package/src/sqlite3/db.js +1 -0
  97. package/docs/SKILL.md +0 -374
@@ -11,6 +11,7 @@ const {
11
11
  generateChildTestFile,
12
12
  } = require("./generate-route.js");
13
13
  const { generateOpenAPISpec } = require("./generate-openapi.js");
14
+ const { generateDocsRoute } = require("./generate-docs-route.js");
14
15
 
15
16
  /**
16
17
  * Simple line-by-line diff between two strings.
@@ -52,54 +53,63 @@ function buildExpectedFiles(meta, relationships) {
52
53
  const modelsRelPath = "../models";
53
54
  const tableNames = meta.map((m) => m.table).sort();
54
55
 
56
+ // Collect child tables
57
+ const nestedChildren = new Set();
58
+ for (const rel of relationships) {
59
+ nestedChildren.add(rel.child);
60
+ }
61
+
55
62
  // Model files
56
63
  for (const m of meta) {
57
64
  expected.set(`models/${m.table}.js`, generateModelFile(m));
58
65
  }
59
66
 
60
- // Route files (one per table)
67
+ // Route files (top-level only, skip children)
61
68
  for (const m of meta) {
69
+ if (nestedChildren.has(m.table)) continue;
62
70
  expected.set(
63
71
  `routes/${m.table}.js`,
64
72
  generateRouteFile(m.table, modelsRelPath),
65
73
  );
66
74
  }
67
75
 
68
- // Child route files (one per relationship)
76
+ // Child route files in subfolders: routes/<parent>/<child>.js
69
77
  for (const rel of relationships) {
70
- const childMeta = meta.find((m) => m.table === rel.child);
71
- const pk = childMeta ? childMeta.primary_key : "id";
72
78
  expected.set(
73
- `routes/${rel.child}_child_of_${rel.parent}.js`,
79
+ `routes/${rel.parent}/${rel.child}.js`,
74
80
  generateChildRouteFile(
75
81
  rel.child,
76
82
  rel.parent,
77
83
  rel.foreignKey,
78
- modelsRelPath,
84
+ `../../models`,
79
85
  ),
80
86
  );
81
87
  }
82
88
 
83
- // Routes index file
89
+ // Routes index file (with docs route)
84
90
  expected.set(
85
91
  "routes/index.js",
86
- generateRoutesIndexFile(tableNames, relationships),
92
+ generateRoutesIndexFile(tableNames, relationships, { includeDocs: true }),
87
93
  );
88
94
 
89
- // Test files (one per table)
95
+ // Docs route (Swagger UI)
96
+ expected.set("routes/docs.js", generateDocsRoute());
97
+
98
+ // Test files (top-level only, skip children)
90
99
  for (const m of meta) {
100
+ if (nestedChildren.has(m.table)) continue;
91
101
  expected.set(
92
102
  `test/${m.table}.test.js`,
93
103
  generateTestFile(m.table, m.primary_key),
94
104
  );
95
105
  }
96
106
 
97
- // Child test files (one per relationship)
107
+ // Child test files in subfolders: test/<parent>/<child>.test.js
98
108
  for (const rel of relationships) {
99
109
  const childMeta = meta.find((m) => m.table === rel.child);
100
110
  const pk = childMeta ? childMeta.primary_key : "id";
101
111
  expected.set(
102
- `test/${rel.child}_child_of_${rel.parent}.test.js`,
112
+ `test/${rel.parent}/${rel.child}.test.js`,
103
113
  generateChildTestFile(rel.child, rel.parent, rel.foreignKey, pk),
104
114
  );
105
115
  }
@@ -115,7 +125,7 @@ function buildExpectedFiles(meta, relationships) {
115
125
 
116
126
  /**
117
127
  * Scan known artifact directories on disk and return a set of relative paths
118
- * that exist.
128
+ * that exist. Recursively scans subdirectories.
119
129
  *
120
130
  * @param {string} baseDir
121
131
  * @returns {Set<string>}
@@ -123,22 +133,42 @@ function buildExpectedFiles(meta, relationships) {
123
133
  function scanDiskFiles(baseDir) {
124
134
  const files = new Set();
125
135
 
126
- const dirs = [
127
- { dir: "models", ext: ".js" },
128
- { dir: "routes", ext: ".js" },
129
- { dir: "test", ext: ".test.js" },
130
- ];
136
+ function scanDir(dir, prefix) {
137
+ const fullDir = path.join(baseDir, dir);
138
+ if (!fs.existsSync(fullDir)) return;
139
+ for (const entry of fs.readdirSync(fullDir, { withFileTypes: true })) {
140
+ const relPath = prefix
141
+ ? `${prefix}/${entry.name}`
142
+ : `${dir}/${entry.name}`;
143
+ if (entry.isDirectory()) {
144
+ scanDir(path.join(dir, entry.name), relPath);
145
+ } else if (entry.name.endsWith(".js")) {
146
+ files.add(relPath);
147
+ }
148
+ }
149
+ }
150
+
151
+ scanDir("models");
152
+ scanDir("routes");
131
153
 
132
- for (const { dir, ext } of dirs) {
154
+ // For test dir, only include .test.js files
155
+ function scanTestDir(dir, prefix) {
133
156
  const fullDir = path.join(baseDir, dir);
134
- if (!fs.existsSync(fullDir)) continue;
135
- for (const file of fs.readdirSync(fullDir)) {
136
- if (file.endsWith(ext)) {
137
- files.add(`${dir}/${file}`);
157
+ if (!fs.existsSync(fullDir)) return;
158
+ for (const entry of fs.readdirSync(fullDir, { withFileTypes: true })) {
159
+ const relPath = prefix
160
+ ? `${prefix}/${entry.name}`
161
+ : `${dir}/${entry.name}`;
162
+ if (entry.isDirectory()) {
163
+ scanTestDir(path.join(dir, entry.name), relPath);
164
+ } else if (entry.name.endsWith(".test.js")) {
165
+ files.add(relPath);
138
166
  }
139
167
  }
140
168
  }
141
169
 
170
+ scanTestDir("test");
171
+
142
172
  // Check for openapi.json at root
143
173
  const openapiPath = path.join(baseDir, "openapi.json");
144
174
  if (fs.existsSync(openapiPath)) {
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Generate a routes/docs.js file that serves Swagger UI for the OpenAPI spec.
5
+ * Uses swagger-ui-express to mount at /docs.
6
+ *
7
+ * @returns {string}
8
+ */
9
+ function generateDocsRoute() {
10
+ return `import express from "express";
11
+ import swaggerUi from "swagger-ui-express";
12
+ import { readFileSync } from "fs";
13
+ import { dirname, join } from "path";
14
+ import { fileURLToPath } from "url";
15
+
16
+ const __dirname = dirname(fileURLToPath(import.meta.url));
17
+ const spec = JSON.parse(readFileSync(join(__dirname, "../openapi.json"), "utf8"));
18
+
19
+ const router = express.Router();
20
+
21
+ router.use("/", swaggerUi.serve);
22
+ router.get("/", swaggerUi.setup(spec, {
23
+ customSiteTitle: "API Documentation",
24
+ customCss: ".swagger-ui .topbar { display: none }",
25
+ }));
26
+
27
+ export default router;
28
+ `;
29
+ }
30
+
31
+ module.exports = { generateDocsRoute };
@@ -0,0 +1,356 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Generate database migration SQL/JS from parsed schema tables.
5
+ *
6
+ * Produces CREATE TABLE statements (or equivalent) for each table,
7
+ * with proper column types, constraints, and indexes per adapter.
8
+ */
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Column type mapping per adapter
12
+ // ---------------------------------------------------------------------------
13
+
14
+ /**
15
+ * Map a dbmr column rule to a SQL column type for the given adapter.
16
+ *
17
+ * @param {string} rule - e.g. "required|string", "auto_increment", "integer"
18
+ * @param {string} adapter - e.g. "postgres", "mysql", "sqlite3"
19
+ * @returns {{ sqlType: string, nullable: boolean, isAutoIncrement: boolean }}
20
+ */
21
+ function mapColumnType(rule, adapter) {
22
+ const parts = rule.split("|");
23
+ const isRequired = parts.includes("required");
24
+ const baseType = parts.filter((p) => p !== "required")[0] || "string";
25
+
26
+ let sqlType;
27
+ let isAutoIncrement = false;
28
+
29
+ switch (baseType) {
30
+ case "auto_increment":
31
+ isAutoIncrement = true;
32
+ sqlType = autoIncrementType(adapter);
33
+ break;
34
+ case "string":
35
+ sqlType = stringType(adapter);
36
+ break;
37
+ case "integer":
38
+ sqlType = integerType(adapter);
39
+ break;
40
+ case "numeric":
41
+ sqlType = numericType(adapter);
42
+ break;
43
+ case "boolean":
44
+ sqlType = booleanType(adapter);
45
+ break;
46
+ case "datetime":
47
+ sqlType = datetimeType(adapter);
48
+ break;
49
+ case "object":
50
+ sqlType = jsonType(adapter);
51
+ break;
52
+ default:
53
+ sqlType = stringType(adapter);
54
+ }
55
+
56
+ return {
57
+ sqlType,
58
+ nullable: !isRequired && !isAutoIncrement,
59
+ isAutoIncrement,
60
+ };
61
+ }
62
+
63
+ function autoIncrementType(adapter) {
64
+ switch (adapter) {
65
+ case "postgres":
66
+ case "cockroachdb":
67
+ return "SERIAL";
68
+ case "mssql":
69
+ return "INT IDENTITY(1,1)";
70
+ case "oracle":
71
+ return "NUMBER GENERATED BY DEFAULT AS IDENTITY";
72
+ case "sqlite3":
73
+ return "INTEGER";
74
+ default:
75
+ // mysql, mariadb
76
+ return "INT AUTO_INCREMENT";
77
+ }
78
+ }
79
+
80
+ function stringType(adapter) {
81
+ if (adapter === "oracle") return "VARCHAR2(255)";
82
+ return "VARCHAR(255)";
83
+ }
84
+
85
+ function integerType(adapter) {
86
+ if (adapter === "oracle") return "NUMBER(10)";
87
+ return "INTEGER";
88
+ }
89
+
90
+ function numericType(adapter) {
91
+ if (adapter === "oracle") return "NUMBER(12,2)";
92
+ if (adapter === "mssql") return "DECIMAL(12,2)";
93
+ return "DECIMAL(12,2)";
94
+ }
95
+
96
+ function booleanType(adapter) {
97
+ if (adapter === "oracle") return "NUMBER(1)";
98
+ if (adapter === "mssql") return "BIT";
99
+ if (adapter === "mysql" || adapter === "mariadb") return "TINYINT(1)";
100
+ return "BOOLEAN";
101
+ }
102
+
103
+ function datetimeType(adapter) {
104
+ if (adapter === "mssql") return "DATETIME";
105
+ return "TIMESTAMP";
106
+ }
107
+
108
+ function jsonType(adapter) {
109
+ if (adapter === "postgres" || adapter === "cockroachdb") return "JSONB";
110
+ if (adapter === "oracle") return "CLOB";
111
+ if (adapter === "mssql") return "NVARCHAR(MAX)";
112
+ if (adapter === "sqlite3") return "TEXT";
113
+ return "JSON";
114
+ }
115
+
116
+ // ---------------------------------------------------------------------------
117
+ // SQL migration generators
118
+ // ---------------------------------------------------------------------------
119
+
120
+ /**
121
+ * Generate a CREATE TABLE SQL statement for a single table.
122
+ *
123
+ * @param {string} tableName
124
+ * @param {object} tableDef - parsed table definition from schema
125
+ * @param {string} adapter
126
+ * @returns {string}
127
+ */
128
+ function generateCreateTableSQL(tableName, tableDef, adapter) {
129
+ const lines = [];
130
+ const pk = tableDef.pk;
131
+
132
+ for (const [colName, rule] of Object.entries(tableDef.columns)) {
133
+ const { sqlType, nullable, isAutoIncrement } = mapColumnType(rule, adapter);
134
+ let line = ` ${quoteIdent(colName, adapter)} ${sqlType}`;
135
+
136
+ if (colName === pk) {
137
+ line += " PRIMARY KEY";
138
+ if (isAutoIncrement && adapter === "sqlite3") {
139
+ // SQLite needs AUTOINCREMENT after PRIMARY KEY for INTEGER type
140
+ line = ` ${quoteIdent(colName, adapter)} INTEGER PRIMARY KEY AUTOINCREMENT`;
141
+ }
142
+ } else {
143
+ if (!nullable) line += " NOT NULL";
144
+ }
145
+
146
+ // Default values for timestamps
147
+ if (tableDef.timestamps) {
148
+ if (
149
+ colName === tableDef.timestamps.created_at ||
150
+ colName === tableDef.timestamps.modified_at
151
+ ) {
152
+ line += defaultTimestamp(adapter);
153
+ }
154
+ }
155
+
156
+ // Default for boolean softDelete column
157
+ if (tableDef.softDelete && colName === tableDef.softDelete) {
158
+ line += defaultBoolean(adapter);
159
+ }
160
+
161
+ lines.push(line);
162
+ }
163
+
164
+ // Unique constraints (excluding PK which is already PRIMARY KEY)
165
+ if (tableDef.unique && tableDef.unique.length > 0) {
166
+ const uniqueCols = tableDef.unique.filter((c) => c !== pk);
167
+ for (const col of uniqueCols) {
168
+ lines.push(` UNIQUE (${quoteIdent(col, adapter)})`);
169
+ }
170
+ }
171
+
172
+ const createPrefix =
173
+ adapter === "oracle" || adapter === "mssql"
174
+ ? `CREATE TABLE ${quoteIdent(tableName, adapter)}`
175
+ : `CREATE TABLE IF NOT EXISTS ${quoteIdent(tableName, adapter)}`;
176
+
177
+ return `${createPrefix} (\n${lines.join(",\n")}\n);\n`;
178
+ }
179
+
180
+ /**
181
+ * Quote an identifier based on adapter.
182
+ */
183
+ function quoteIdent(name, adapter) {
184
+ // Most adapters are fine without quoting for simple names
185
+ // but we keep it safe for reserved words
186
+ if (adapter === "mssql") return `[${name}]`;
187
+ if (adapter === "oracle") return `"${name.toUpperCase()}"`;
188
+ return name;
189
+ }
190
+
191
+ /**
192
+ * Default timestamp expression per adapter.
193
+ */
194
+ function defaultTimestamp(adapter) {
195
+ switch (adapter) {
196
+ case "mssql":
197
+ return " DEFAULT GETDATE()";
198
+ case "oracle":
199
+ return " DEFAULT CURRENT_TIMESTAMP";
200
+ case "sqlite3":
201
+ return " DEFAULT CURRENT_TIMESTAMP";
202
+ default:
203
+ return " DEFAULT CURRENT_TIMESTAMP";
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Default boolean false expression per adapter.
209
+ */
210
+ function defaultBoolean(adapter) {
211
+ if (adapter === "oracle" || adapter === "mssql") return " DEFAULT 0";
212
+ if (adapter === "mysql" || adapter === "mariadb") return " DEFAULT 0";
213
+ return " DEFAULT FALSE";
214
+ }
215
+
216
+ // ---------------------------------------------------------------------------
217
+ // NoSQL migration generators
218
+ // ---------------------------------------------------------------------------
219
+
220
+ /**
221
+ * Generate a MongoDB migration JS file for a single table (collection).
222
+ */
223
+ function generateMongoDBMigration(tableName, tableDef) {
224
+ const uniqueCols = (tableDef.unique || []).filter((c) => c !== tableDef.pk);
225
+ const indexLines = uniqueCols
226
+ .map(
227
+ (col) =>
228
+ ` await db.collection("${tableName}").createIndex({ ${col}: 1 }, { unique: true });`,
229
+ )
230
+ .join("\n");
231
+
232
+ return `"use strict";
233
+
234
+ module.exports = {
235
+ async up(db) {
236
+ await db.createCollection("${tableName}");
237
+ ${indexLines ? indexLines + "\n" : ""} },
238
+
239
+ async down(db) {
240
+ await db.collection("${tableName}").drop();
241
+ },
242
+ };
243
+ `;
244
+ }
245
+
246
+ /**
247
+ * Generate a DynamoDB migration JS file for a single table.
248
+ */
249
+ function generateDynamoDBMigration(tableName, tableDef) {
250
+ const pk = tableDef.pk;
251
+ return `import { CreateTableCommand, DeleteTableCommand } from "@aws-sdk/client-dynamodb";
252
+
253
+ export async function up(db) {
254
+ await db.send(new CreateTableCommand({
255
+ TableName: "${tableName}",
256
+ KeySchema: [{ AttributeName: "${pk}", KeyType: "HASH" }],
257
+ AttributeDefinitions: [{ AttributeName: "${pk}", AttributeType: "N" }],
258
+ BillingMode: "PAY_PER_REQUEST",
259
+ }));
260
+ }
261
+
262
+ export async function down(db) {
263
+ await db.send(new DeleteTableCommand({ TableName: "${tableName}" }));
264
+ }
265
+ `;
266
+ }
267
+
268
+ /**
269
+ * Generate a Redis migration JS file (Redis doesn't need table creation,
270
+ * but we create a placeholder for consistency).
271
+ */
272
+ function generateRedisMigration(tableName) {
273
+ return `"use strict";
274
+
275
+ module.exports = {
276
+ async up(db) {
277
+ // Redis is schema-less. This migration is a placeholder.
278
+ // Data for "${tableName}" will be stored as hash keys: ${tableName}:<id>
279
+ console.log("Redis: ${tableName} collection ready (schema-less).");
280
+ },
281
+
282
+ async down(db) {
283
+ // Warning: this deletes ALL keys matching the pattern
284
+ // In production, use SCAN instead of KEYS
285
+ const keys = await db.keys("${tableName}:*");
286
+ if (keys.length > 0) {
287
+ await db.del(...keys);
288
+ }
289
+ },
290
+ };
291
+ `;
292
+ }
293
+
294
+ // ---------------------------------------------------------------------------
295
+ // Public API
296
+ // ---------------------------------------------------------------------------
297
+
298
+ const SQL_ADAPTERS = [
299
+ "mysql",
300
+ "mariadb",
301
+ "postgres",
302
+ "sqlite3",
303
+ "mssql",
304
+ "cockroachdb",
305
+ "oracle",
306
+ ];
307
+
308
+ /**
309
+ * Generate migration file content for all tables in the schema.
310
+ *
311
+ * For SQL adapters: produces a single .sql file with all CREATE TABLE statements.
312
+ * For NoSQL adapters: produces one .js file per table.
313
+ *
314
+ * @param {object} schema - parsed schema from parseSchema()
315
+ * @returns {Array<{ filename: string, content: string }>}
316
+ */
317
+ function generateMigrationFiles(schema) {
318
+ const adapter = schema.adapter;
319
+ const tables = schema.tables;
320
+ const tableNames = Object.keys(tables).sort();
321
+
322
+ if (SQL_ADAPTERS.includes(adapter)) {
323
+ // Single SQL file with all CREATE TABLE statements
324
+ const statements = [];
325
+ for (const name of tableNames) {
326
+ statements.push(generateCreateTableSQL(name, tables[name], adapter));
327
+ }
328
+ const content = statements.join("\n");
329
+ return [{ filename: `create_tables.sql`, content }];
330
+ }
331
+
332
+ // NoSQL: one file per table
333
+ const files = [];
334
+ for (const name of tableNames) {
335
+ let content;
336
+ if (adapter === "mongodb") {
337
+ content = generateMongoDBMigration(name, tables[name]);
338
+ } else if (adapter === "dynamodb") {
339
+ content = generateDynamoDBMigration(name, tables[name]);
340
+ } else if (adapter === "redis") {
341
+ content = generateRedisMigration(name);
342
+ } else {
343
+ // Fallback
344
+ content = `// Migration for ${name}\n`;
345
+ }
346
+ files.push({ filename: `create_${name}.js`, content });
347
+ }
348
+
349
+ return files;
350
+ }
351
+
352
+ module.exports = {
353
+ generateMigrationFiles,
354
+ generateCreateTableSQL,
355
+ mapColumnType,
356
+ };
@@ -475,7 +475,7 @@ function mysqlTypeToValidator(t) {
475
475
  if (/json/.test(t)) return "object";
476
476
  if (/text|char|varchar|enum|set/.test(t)) return "string";
477
477
  if (/blob|binary/.test(t)) return "string";
478
- if (/date|time|year/.test(t)) return "string";
478
+ if (/date|time|year/.test(t)) return "datetime";
479
479
  if (/bool/.test(t)) return "integer";
480
480
  return "string";
481
481
  }
@@ -487,7 +487,7 @@ function pgTypeToValidator(t) {
487
487
  if (/json/.test(t)) return "object";
488
488
  if (/bool/.test(t)) return "integer";
489
489
  if (/char|text|varchar|uuid/.test(t)) return "string";
490
- if (/date|time|interval/.test(t)) return "string";
490
+ if (/date|time|interval/.test(t)) return "datetime";
491
491
  return "string";
492
492
  }
493
493
 
@@ -496,6 +496,7 @@ function sqliteTypeToValidator(t) {
496
496
  if (/int/.test(t)) return "integer";
497
497
  if (/real|float|double|numeric|decimal/.test(t)) return "numeric";
498
498
  if (/json/.test(t)) return "object";
499
+ if (/date|time/.test(t)) return "datetime";
499
500
  if (/blob/.test(t)) return "string";
500
501
  return "string";
501
502
  }
@@ -506,7 +507,7 @@ function mssqlTypeToValidator(t) {
506
507
  if (/decimal|numeric|float|real|money/.test(t)) return "numeric";
507
508
  if (/bit/.test(t)) return "integer";
508
509
  if (/char|text|varchar|nchar|nvarchar|ntext/.test(t)) return "string";
509
- if (/date|time|datetime/.test(t)) return "string";
510
+ if (/date|time|datetime/.test(t)) return "datetime";
510
511
  if (/uniqueidentifier/.test(t)) return "string";
511
512
  return "string";
512
513
  }
@@ -515,7 +516,7 @@ function oracleTypeToValidator(t) {
515
516
  if (/NUMBER|INTEGER|FLOAT|BINARY_FLOAT|BINARY_DOUBLE/.test(t))
516
517
  return "numeric";
517
518
  if (/CLOB|BLOB|RAW|LONG/.test(t)) return "string";
518
- if (/DATE|TIMESTAMP/.test(t)) return "string";
519
+ if (/DATE|TIMESTAMP/.test(t)) return "datetime";
519
520
  if (/CHAR|VARCHAR|NCHAR|NVARCHAR/.test(t)) return "string";
520
521
  return "string";
521
522
  }