db-model-router 1.0.19 → 1.0.21

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.
@@ -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.19",
3
+ "version": "1.0.21",
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,
@@ -104,6 +104,14 @@ async function buildSchemaArtifacts({ schema, baseDir, ctx, flags }) {
104
104
  content: generateModelFile(m),
105
105
  });
106
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
+ });
107
115
  }
108
116
 
109
117
  // --- Route files ---
@@ -258,14 +266,15 @@ async function buildSchemaArtifacts({ schema, baseDir, ctx, flags }) {
258
266
  baseDir,
259
267
  });
260
268
 
261
- // The SaaS generator produces a combined routes/index.js that includes
262
- // both SaaS routes and dbmr schema-generated routes. Remove any previously
263
- // planned routes/index.js from the schema generator.
264
- const existingIndexIdx = planned.findIndex(
265
- (p) => p.relPath === "routes/index.js",
266
- );
267
- if (existingIndexIdx !== -1) {
268
- 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
+ }
269
278
  }
270
279
 
271
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);
@@ -2,7 +2,7 @@
2
2
 
3
3
  const fs = require("fs");
4
4
  const path = require("path");
5
- const { generateModelFile } = require("./generate-model.js");
5
+ const { generateModelFile, generateIndexFile } = require("./generate-model.js");
6
6
  const {
7
7
  generateRouteFile,
8
8
  generateParentRouteFile,
@@ -87,6 +87,10 @@ function buildExpectedFiles(meta, relationships, options = {}) {
87
87
  expected.set(`models/${m.table}.js`, generateModelFile(m));
88
88
  }
89
89
 
90
+ // Model barrel. SaaS mode overwrites this with the combined SaaS+dbmr
91
+ // barrel when saasFiles are merged below.
92
+ expected.set("models/index.js", generateIndexFile(meta));
93
+
90
94
  // Route files: exactly one per table at its correct nested path
91
95
  for (const m of meta) {
92
96
  const tableName = m.table;
@@ -703,9 +703,17 @@ function generateInitialMigration(answers, date) {
703
703
  executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
704
704
  checksum VARCHAR2(64) NOT NULL
705
705
  );
706
+ `;
707
+ } else if (answers.database === "mysql" || answers.database === "mariadb") {
708
+ content = `CREATE TABLE IF NOT EXISTS _migrations (
709
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
710
+ filename VARCHAR(255) NOT NULL UNIQUE,
711
+ executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
712
+ checksum VARCHAR(64) NOT NULL
713
+ );
706
714
  `;
707
715
  } else {
708
- // mysql, sqlite3
716
+ // sqlite3
709
717
  content = `CREATE TABLE IF NOT EXISTS _migrations (
710
718
  id INTEGER PRIMARY KEY AUTOINCREMENT,
711
719
  filename VARCHAR(255) NOT NULL UNIQUE,
package/src/mysql/db.js CHANGED
@@ -7,6 +7,12 @@ function connect(credentails) {
7
7
  // Force UTC timezone so timestamps are consistent
8
8
  // dateStrings: true returns raw strings without JS Date conversion
9
9
  credentails.timezone = "+00:00";
10
+ // Enable multiple statements by default so multi-query migration files
11
+ // (e.g. a single .sql with many CREATE TABLE statements) run in one call.
12
+ // Applied to mysql and mariadb (both use this adapter).
13
+ if (credentails.multipleStatements === undefined) {
14
+ credentails.multipleStatements = true;
15
+ }
10
16
  pool = mysql.createPool(credentails);
11
17
  return pool;
12
18
  }