db-model-router 1.0.18 → 1.0.20

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.
@@ -8,7 +8,7 @@
8
8
  "logger": true,
9
9
  "loki": false,
10
10
  "output": null,
11
- "saasStructure": true,
11
+ "saasStructure": false,
12
12
  "apiBasePath": "/api",
13
13
  "port": 3000
14
14
  },
@@ -0,0 +1,360 @@
1
+ {
2
+ "adapter": "mysql",
3
+ "framework": "express",
4
+ "options": {
5
+ "session": "memory",
6
+ "rateLimiting": true,
7
+ "helmet": true,
8
+ "logger": true,
9
+ "loki": false,
10
+ "saasStructure": false,
11
+ "apiBasePath": "/api",
12
+ "output": "backend",
13
+ "port": 6000
14
+ },
15
+ "tables": {
16
+ "languages": {
17
+ "columns": {
18
+ "language_id": "auto_increment",
19
+ "name": "required|string|maxLength:100",
20
+ "slug": "required|string|regex:^[a-z0-9-]+$|maxLength:100",
21
+ "file_extension": "required|string|maxLength:20",
22
+ "runtime_family": "string|maxLength:50",
23
+ "package_registry": "string|maxLength:50",
24
+ "is_active": "boolean",
25
+ "created_at": "datetime"
26
+ },
27
+ "pk": "language_id",
28
+ "unique": ["slug"],
29
+ "timestamps": {
30
+ "created_at": "created_at"
31
+ },
32
+ "parent": null
33
+ },
34
+
35
+ "frameworks": {
36
+ "columns": {
37
+ "framework_id": "auto_increment",
38
+ "language_id": "required|integer:unsigned",
39
+ "name": "required|string|maxLength:150",
40
+ "slug": "required|string|regex:^[a-z0-9-]+$|maxLength:150",
41
+ "package_name": "string|maxLength:150",
42
+ "homepage_url": "string|maxLength:500",
43
+ "latest_version": "string|maxLength:50",
44
+ "weekly_downloads": "integer:unsigned",
45
+ "popularity_index": "numeric:decimal(5,2)",
46
+ "popularity_updated_at": "datetime",
47
+ "created_at": "datetime"
48
+ },
49
+ "pk": "framework_id",
50
+ "unique": ["slug"],
51
+ "search_columns": ["name", "package_name"],
52
+ "timestamps": {
53
+ "created_at": "created_at"
54
+ },
55
+ "parent": null
56
+ },
57
+
58
+ "libraries": {
59
+ "columns": {
60
+ "library_id": "auto_increment",
61
+ "language_id": "required|integer:unsigned",
62
+ "framework_id": "integer:unsigned",
63
+ "name": "required|string|maxLength:150",
64
+ "slug": "required|string|regex:^[a-z0-9-]+$|maxLength:150",
65
+ "package_name": "string|maxLength:150",
66
+ "homepage_url": "string|maxLength:500",
67
+ "latest_version": "string|maxLength:50",
68
+ "weekly_downloads": "integer:unsigned",
69
+ "popularity_index": "numeric:decimal(5,2)",
70
+ "popularity_updated_at": "datetime",
71
+ "created_at": "datetime"
72
+ },
73
+ "pk": "library_id",
74
+ "unique": ["slug"],
75
+ "search_columns": ["name", "package_name"],
76
+ "timestamps": {
77
+ "created_at": "created_at"
78
+ },
79
+ "parent": null
80
+ },
81
+
82
+ "tasks": {
83
+ "columns": {
84
+ "task_id": "auto_increment",
85
+ "name": "required|string|maxLength:150",
86
+ "slug": "required|string|regex:^[a-z0-9-]+$|maxLength:150",
87
+ "category": "required|string|in:auth,storage,networking,data-processing,messaging,payments,caching,middleware,ai-integration,database,validation,integration,utility",
88
+ "description": "string:text",
89
+ "created_at": "datetime"
90
+ },
91
+ "pk": "task_id",
92
+ "unique": ["slug"],
93
+ "search_columns": ["name", "description"],
94
+ "timestamps": {
95
+ "created_at": "created_at"
96
+ },
97
+ "parent": null
98
+ },
99
+
100
+ "tags": {
101
+ "columns": {
102
+ "tag_id": "auto_increment",
103
+ "name": "required|string|maxLength:100",
104
+ "slug": "required|string|regex:^[a-z0-9-]+$|maxLength:100"
105
+ },
106
+ "pk": "tag_id",
107
+ "unique": [["name"], ["slug"]],
108
+ "parent": null
109
+ },
110
+
111
+ "popularity_snapshots": {
112
+ "columns": {
113
+ "snapshot_id": "auto_increment",
114
+ "entity_type": "required|string|in:framework,library",
115
+ "entity_id": "required|integer:unsigned",
116
+ "weekly_downloads": "required|integer:unsigned",
117
+ "source": "string|maxLength:50",
118
+ "snapshot_date": "required|datetime"
119
+ },
120
+ "pk": "snapshot_id",
121
+ "parent": null
122
+ },
123
+
124
+ "sandbox_images": {
125
+ "columns": {
126
+ "image_id": "auto_increment",
127
+ "language_id": "required|integer:unsigned",
128
+ "image_name": "required|string|maxLength:255",
129
+ "os_base": "string|maxLength:100",
130
+ "installed_toolchain": "string:text",
131
+ "is_active": "boolean",
132
+ "created_at": "datetime"
133
+ },
134
+ "pk": "image_id",
135
+ "unique": ["image_name"],
136
+ "timestamps": {
137
+ "created_at": "created_at"
138
+ },
139
+ "parent": null
140
+ },
141
+
142
+ "snippets": {
143
+ "columns": {
144
+ "snippet_id": "required|string:uuid",
145
+ "title": "required|string|maxLength:255",
146
+ "description": "string:text",
147
+ "usecase": "string:text",
148
+ "task_id": "required|integer:unsigned",
149
+ "language_id": "required|integer:unsigned",
150
+ "framework_id": "integer:unsigned",
151
+ "status": "required|string|in:draft,in_verification,verified,failed,deprecated",
152
+ "current_stage": "required|string|in:execution,lint,static_analysis,security_scan,performance,published",
153
+ "verified": "boolean",
154
+ "quality_score": "numeric:decimal(5,2)",
155
+ "source": "required|string|in:ai-generated,human-curated,imported",
156
+ "generation_provider": "string|maxLength:64",
157
+ "generation_model": "string|maxLength:128",
158
+ "enrichment_model": "string|maxLength:128",
159
+ "usage_count": "integer:unsigned",
160
+ "last_tested_at": "datetime",
161
+ "created_at": "datetime",
162
+ "modified_at": "datetime"
163
+ },
164
+ "pk": "snippet_id",
165
+ "search_columns": ["title", "description", "usecase"],
166
+ "timestamps": {
167
+ "created_at": "created_at",
168
+ "modified_at": "modified_at"
169
+ },
170
+ "parent": null
171
+ },
172
+
173
+ "snippet_files": {
174
+ "columns": {
175
+ "file_id": "auto_increment",
176
+ "snippet_id": "required|string:uuid",
177
+ "file_role": "required|string|in:code,test,dependency,readme",
178
+ "file_name": "required|string|maxLength:255",
179
+ "content": "required|string:longtext",
180
+ "sort_order": "integer:unsigned",
181
+ "created_at": "datetime",
182
+ "modified_at": "datetime"
183
+ },
184
+ "pk": "file_id",
185
+ "timestamps": {
186
+ "created_at": "created_at",
187
+ "modified_at": "modified_at"
188
+ },
189
+ "parent": "snippets"
190
+ },
191
+
192
+ "snippet_dependencies": {
193
+ "columns": {
194
+ "dependency_id": "auto_increment",
195
+ "snippet_id": "required|string:uuid",
196
+ "library_id": "required|integer:unsigned",
197
+ "version_constraint": "string|maxLength:50",
198
+ "is_primary": "boolean",
199
+ "created_at": "datetime"
200
+ },
201
+ "pk": "dependency_id",
202
+ "unique": [["snippet_id", "library_id"]],
203
+ "timestamps": {
204
+ "created_at": "created_at"
205
+ },
206
+ "parent": "snippets"
207
+ },
208
+
209
+ "snippet_tags": {
210
+ "columns": {
211
+ "snippet_id": "required|string:uuid",
212
+ "tag_id": "required|integer:unsigned"
213
+ },
214
+ "pk": "snippet_id",
215
+ "unique": [["snippet_id", "tag_id"]],
216
+ "parent": "snippets"
217
+ },
218
+
219
+ "testing_logs": {
220
+ "columns": {
221
+ "testing_id": "auto_increment",
222
+ "snippet_id": "required|string:uuid",
223
+ "queue_id": "integer:bigint",
224
+ "language_id": "required|integer:unsigned",
225
+ "framework_id": "integer:unsigned",
226
+ "code_snapshot": "required|string:longtext",
227
+ "test_snapshot": "required|string:longtext",
228
+ "dependency_snapshot": "required|string:longtext",
229
+ "result": "required|string|in:pass,fail,error,timeout",
230
+ "exit_code": "integer",
231
+ "stdout": "string:longtext",
232
+ "stderr": "string:longtext",
233
+ "response_time_ms": "integer",
234
+ "sandbox_image": "string|maxLength:128",
235
+ "tested_at": "datetime"
236
+ },
237
+ "pk": "testing_id",
238
+ "timestamps": {
239
+ "tested_at": "tested_at"
240
+ },
241
+ "parent": "snippets"
242
+ },
243
+
244
+ "verification_stage_results": {
245
+ "columns": {
246
+ "result_id": "auto_increment",
247
+ "snippet_id": "required|string:uuid",
248
+ "queue_id": "integer:bigint",
249
+ "stage": "required|string|in:lint,static_analysis,security_scan,performance",
250
+ "status": "required|string|in:pass,fail,warning",
251
+ "score": "numeric:decimal(5,2)",
252
+ "tool_used": "string|maxLength:64",
253
+ "findings": "object",
254
+ "executed_at": "datetime"
255
+ },
256
+ "pk": "result_id",
257
+ "timestamps": {
258
+ "executed_at": "executed_at"
259
+ },
260
+ "parent": "snippets"
261
+ },
262
+
263
+ "enrichment_jobs": {
264
+ "columns": {
265
+ "job_id": "auto_increment",
266
+ "snippet_id": "required|string:uuid",
267
+ "provider": "required|string|maxLength:64",
268
+ "model": "required|string|maxLength:128",
269
+ "prompt_version": "string|maxLength:50",
270
+ "status": "required|string|in:pending,success,failed",
271
+ "raw_response": "string:longtext",
272
+ "tokens_used": "integer",
273
+ "latency_ms": "integer",
274
+ "created_at": "datetime"
275
+ },
276
+ "pk": "job_id",
277
+ "timestamps": {
278
+ "created_at": "created_at"
279
+ },
280
+ "parent": "snippets"
281
+ },
282
+
283
+ "generation_attempts": {
284
+ "columns": {
285
+ "attempt_id": "auto_increment",
286
+ "snippet_id": "required|string:uuid",
287
+ "attempt_number": "required|integer|min:1",
288
+ "previous_attempt_id": "integer:unsigned",
289
+ "prompt": "required|string:longtext",
290
+ "generated_code": "required|string:longtext",
291
+ "generated_test": "required|string:longtext",
292
+ "generated_dependency_manifest": "required|string:longtext",
293
+ "failure_stage": "string|in:execution,lint,static_analysis,security_scan",
294
+ "error_output": "string:longtext",
295
+ "outcome": "required|string|in:failed,succeeded",
296
+ "generation_provider": "string|maxLength:64",
297
+ "generation_model": "string|maxLength:128",
298
+ "created_at": "datetime"
299
+ },
300
+ "pk": "attempt_id",
301
+ "timestamps": {
302
+ "created_at": "created_at"
303
+ },
304
+ "parent": "snippets"
305
+ },
306
+
307
+ "execution_queue": {
308
+ "columns": {
309
+ "queue_id": "auto_increment",
310
+ "snippet_id": "required|string:uuid",
311
+ "stage": "required|string|in:execution,lint,static_analysis,security_scan,performance",
312
+ "status": "required|string|in:pending,locked,completed,failed",
313
+ "locked_by": "string|maxLength:100",
314
+ "locked_at": "datetime",
315
+ "lease_expires_at": "datetime",
316
+ "attempt_count": "integer:unsigned",
317
+ "max_attempts": "integer:unsigned",
318
+ "created_at": "datetime",
319
+ "updated_at": "datetime"
320
+ },
321
+ "pk": "queue_id",
322
+ "timestamps": {
323
+ "created_at": "created_at",
324
+ "updated_at": "updated_at"
325
+ },
326
+ "parent": "snippets"
327
+ },
328
+
329
+ "search_query_log": {
330
+ "columns": {
331
+ "log_id": "auto_increment",
332
+ "queries_json": "required|object",
333
+ "results_snippet_ids": "required|object",
334
+ "agent_session_id": "string|maxLength:100",
335
+ "created_at": "datetime"
336
+ },
337
+ "pk": "log_id",
338
+ "timestamps": {
339
+ "created_at": "created_at"
340
+ },
341
+ "parent": null
342
+ },
343
+
344
+ "snippet_feedback": {
345
+ "columns": {
346
+ "feedback_id": "auto_increment",
347
+ "snippet_id": "required|string:uuid",
348
+ "outcome": "required|string|in:used-success,used-failure,rejected-irrelevant",
349
+ "notes": "string:text",
350
+ "agent_session_id": "string|maxLength:100",
351
+ "created_at": "datetime"
352
+ },
353
+ "pk": "feedback_id",
354
+ "timestamps": {
355
+ "created_at": "created_at"
356
+ },
357
+ "parent": "snippets"
358
+ }
359
+ }
360
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "db-model-router",
3
- "version": "1.0.18",
3
+ "version": "1.0.20",
4
4
  "description": "Generative API Creation using mysql2 and express libraries in node js",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -24,6 +24,8 @@
24
24
  "test:command": "mocha test/commands/*.test.js --timeout 30000 --exit",
25
25
  "demo:clear": "node -e \"var fs=require('fs'),p=require('path'),d=p.join(__dirname,'demo');fs.existsSync(d)&&fs.rmSync(d,{recursive:true,force:true});fs.mkdirSync(d,{recursive:true})\"",
26
26
  "demo:create": "node scripts/demo-create.js",
27
+ "demo:create-mysql": "node scripts/demo-create.js mysql",
28
+ "demo:create-mariadb": "node scripts/demo-create.js mariadb",
27
29
  "demo:create-postgres": "node scripts/demo-create.js postgres"
28
30
  },
29
31
  "repository": {
@@ -29,20 +29,26 @@ const run = (cmd, cwd) => {
29
29
  };
30
30
 
31
31
  // 2. Copy schema into the demo folder
32
- const schemaPath =
33
- ADAPTER === "postgres"
34
- ? path.join(ROOT, "dbmr.postgres.test.schema.json")
35
- : path.join(ROOT, "dbmr.schema.json");
32
+ const SCHEMA_BY_ADAPTER = {
33
+ postgres: "dbmr.postgres.test.schema.json",
34
+ mysql: "dbmr.schema.mysql.json",
35
+ mariadb: "dbmr.schema.mysql.json",
36
+ sqlite3: "dbmr.schema.json",
37
+ };
38
+ const schemaPath = path.join(ROOT, SCHEMA_BY_ADAPTER[ADAPTER]);
36
39
 
37
40
  const schema = JSON.parse(fs.readFileSync(schemaPath, "utf8"));
38
41
 
39
- // Patch adapter-specific options for the sqlite3 demo
42
+ // Patch adapter for sqlite3/mariadb demos. mysql/postgres use dedicated
43
+ // schemas with adapter already set; mariadb reuses the mysql schema.
40
44
  if (ADAPTER === "sqlite3") {
41
45
  schema.adapter = "sqlite3";
42
46
  if (schema.options) {
43
47
  delete schema.options.session;
44
48
  delete schema.options.loki;
45
49
  }
50
+ } else if (ADAPTER === "mariadb") {
51
+ schema.adapter = "mariadb";
46
52
  }
47
53
 
48
54
  const schemaDest = path.join(DEMO, "dbmr.schema.json");
@@ -3,7 +3,7 @@
3
3
  const fs = require("fs");
4
4
  const path = require("path");
5
5
  const { schemaToModelMeta } = require("../../schema/schema-to-meta");
6
- const { generateModelFile } = require("../generate-model");
6
+ const { generateModelFile, generateIndexFile } = require("../generate-model");
7
7
  const {
8
8
  generateRouteFile,
9
9
  generateParentRouteFile,
@@ -26,6 +26,7 @@ const { generateSaasOpenAPIPaths } = require("../saas/generate-saas-openapi");
26
26
  const SUPPORTED_ADAPTERS = [
27
27
  "postgres",
28
28
  "mysql",
29
+ "mariadb",
29
30
  "sqlite3",
30
31
  "mssql",
31
32
  "oracle",
@@ -103,6 +104,14 @@ async function buildSchemaArtifacts({ schema, baseDir, ctx, flags }) {
103
104
  content: generateModelFile(m),
104
105
  });
105
106
  }
107
+
108
+ // Barrel re-exporting all schema models. When SaaS is enabled, the SaaS
109
+ // generator emits a combined barrel (SaaS + schema tables) and replaces
110
+ // this one below; without SaaS, this barrel is the final models/index.js.
111
+ planned.push({
112
+ relPath: "models/index.js",
113
+ content: generateIndexFile(meta),
114
+ });
106
115
  }
107
116
 
108
117
  // --- Route files ---
@@ -257,14 +266,15 @@ async function buildSchemaArtifacts({ schema, baseDir, ctx, flags }) {
257
266
  baseDir,
258
267
  });
259
268
 
260
- // The SaaS generator produces a combined routes/index.js that includes
261
- // both SaaS routes and dbmr schema-generated routes. Remove any previously
262
- // planned routes/index.js from the schema generator.
263
- const existingIndexIdx = planned.findIndex(
264
- (p) => p.relPath === "routes/index.js",
265
- );
266
- if (existingIndexIdx !== -1) {
267
- planned.splice(existingIndexIdx, 1);
269
+ // The SaaS generator produces a combined routes/index.js and models/index.js
270
+ // that include both SaaS and dbmr schema-generated entries. Remove the
271
+ // plain barrels previously planned by the schema generator so the combined
272
+ // SaaS versions win.
273
+ for (const barrel of ["routes/index.js", "models/index.js"]) {
274
+ const existingIdx = planned.findIndex((p) => p.relPath === barrel);
275
+ if (existingIdx !== -1) {
276
+ planned.splice(existingIdx, 1);
277
+ }
268
278
  }
269
279
 
270
280
  for (const entry of saasFiles) {
@@ -132,9 +132,6 @@ async function init(args, flags, ctx) {
132
132
  updatePackageJson(answers, outputDir);
133
133
 
134
134
  const installed = !flags.noInstall;
135
- if (installed) {
136
- runInstall();
137
- }
138
135
 
139
136
  // Build init's JSON result first so it lands at ctx._results[0] (buildSchemaArtifacts
140
137
  // pushes its own result afterward).
@@ -153,10 +150,17 @@ async function init(args, flags, ctx) {
153
150
  });
154
151
  }
155
152
 
156
- // Schema-driven artifacts: models, routes, migrations, openapi, tests, SaaS
153
+ // Schema-driven artifacts: models, routes, migrations, openapi, tests, SaaS.
154
+ // Generated BEFORE npm install so all files exist on disk first; bail skips
155
+ // install if the build reported an error.
157
156
  await buildSchemaArtifacts({ schema, baseDir, ctx, flags });
158
157
  if (process.exitCode) return; // bail if build reported an error
159
158
 
159
+ // Install dependencies only after every project file has been written.
160
+ if (installed) {
161
+ runInstall();
162
+ }
163
+
160
164
  // Human-readable summary (non-json only)
161
165
  if (!flags.json) {
162
166
  printSummary(generated);
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * Generates CREATE TABLE migration files for all SaaS tables with proper
7
7
  * column types, foreign key constraints, and unique constraints per adapter.
8
- * Supports SQL adapters (postgres, mysql, sqlite3, mssql, oracle, cockroachdb)
8
+ * Supports SQL adapters (postgres, mysql, mariadb, sqlite3, mssql, oracle, cockroachdb)
9
9
  * and NoSQL adapters (mongodb, dynamodb, redis).
10
10
  */
11
11
 
@@ -17,6 +17,7 @@ const { mapColumnType } = require("../generate-migration");
17
17
 
18
18
  const SQL_ADAPTERS = [
19
19
  "mysql",
20
+ "mariadb",
20
21
  "postgres",
21
22
  "sqlite3",
22
23
  "mssql",