db-model-router 1.0.16 → 1.0.18

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.
@@ -5,25 +5,33 @@
5
5
  * Each key matches a subcommand name from main.js.
6
6
  */
7
7
  const COMMAND_HELP = {
8
- init: `Usage: db-model-router init [options]
8
+ init: `Usage: db-model-router init [schemaPath] [options]
9
9
 
10
- Scaffold a new project from a schema file or interactively.
11
- Creates app.js, .env, commons/, routes/, middleware/, and migrations/.
10
+ First-buildout ONLY. Scaffolds a complete project (app.js, commons/, routes/,
11
+ middleware/, migrations/, models/, tests, OpenAPI spec, and optional SaaS
12
+ structure) from a single dbmr.schema.json file.
13
+
14
+ ALL project configuration lives in the schema's "options" block. There are no
15
+ config flags on this command — set everything in dbmr.schema.json:
16
+
17
+ options:
18
+ output Relative/absolute dir for source files (null = cwd root)
19
+ saasStructure boolean (default: true) — generate multi-tenant SaaS scaffold
20
+ apiBasePath string starting with "/" (default: "/api")
21
+ port positive integer (default: 3000)
22
+ session "memory" | "redis" | "database" (default: "memory")
23
+ rateLimiting boolean (default: true)
24
+ helmet boolean (default: true)
25
+ logger boolean (default: true)
26
+ loki boolean (default: false)
27
+
28
+ refuses to run if a project already exists in cwd (app.js or package.json
29
+ present). init is NOT for adding new tables, models, or routes — see below.
30
+
31
+ Arguments:
32
+ schemaPath Path to schema file (default: ./dbmr.schema.json)
12
33
 
13
34
  Options:
14
- --from <path> Read adapter, framework, and options from a schema file
15
- --framework <name> Express framework: express, ultimate-express
16
- --database <name> Database adapter: mysql, mariadb, postgres, sqlite3, mongodb,
17
- mssql, cockroachdb, oracle, redis, dynamodb
18
- --db <name> Alias for --database
19
- --session <type> Session store: memory, redis, database
20
- --output <dir> Directory for backend source files (relative to cwd).
21
- package.json and app.js stay in root; commons/, routes/,
22
- middleware/, and migrations/ go inside this folder.
23
- --rateLimiting Enable rate limiting (default: yes)
24
- --helmet Enable Helmet security headers (default: yes)
25
- --logger Enable Winston + Loki logger for Grafana (default: yes)
26
- --yes Accept all defaults without prompting
27
35
  --json Output machine-readable JSON
28
36
  --dry-run Preview planned files without writing
29
37
  --no-install Skip npm install after scaffolding
@@ -31,23 +39,45 @@ Options:
31
39
 
32
40
  Generated files:
33
41
  app.js Express app entry point
34
- .env / .env.example Environment configuration
42
+ .env / .env.example Environment configuration (PORT, API_BASE_PATH)
35
43
  .gitignore Git ignore rules
36
44
  <output>/commons/db.js Database init, connect, and global.db
37
45
  <output>/commons/session.js Session configuration
38
46
  <output>/commons/migrate.js Migration runner (also runs as script)
39
47
  <output>/commons/add_migration.js Migration creation helper (also runs as script)
40
48
  <output>/commons/security.js Helmet, rate limiting, custom headers
41
- <output>/middleware/logger.js Winston + Loki request logger
42
- <output>/routes/index.js Central route mounting
43
- <output>/routes/health.js GET /health endpoint
44
- <output>/migrations/ Initial migration files
49
+ <output>/middleware/logger.js Winston + Loki request logger
50
+ <output>/routes/index.js Central route mounting
51
+ <output>/routes/health.js GET /health endpoint
52
+ <output>/routes/docs.js Swagger UI at /docs
53
+ <output>/routes/<table>/index.js CRUD route per table (nested by parent)
54
+ <output>/models/<table>.js Model with CRUD operations per table
55
+ <output>/migrations/ CREATE TABLE migrations (SQL) or per-table (NoSQL)
56
+ <output>/test/<table>.test.js CRUD endpoint tests per table
57
+ <output>/openapi.json OpenAPI 3.0 spec
58
+ SaaS files (when saasStructure=true): tenants/users/roles middleware + routes + seeds
45
59
 
46
60
  Examples:
47
- db-model-router init --from dbmr.schema.json --yes --no-install
48
- db-model-router init --framework express --database postgres --output backend --yes
49
- db-model-router init --database mysql --session redis --helmet --rateLimiting
50
- db-model-router init --dry-run`,
61
+ db-model-router init # uses ./dbmr.schema.json
62
+ db-model-router init ./my.schema.json
63
+ db-model-router init --dry-run
64
+ db-model-router init --no-install --json
65
+
66
+ ADDING NEW TABLES / MODELS / ROUTES AFTER BUILDOUT (manual — do NOT re-run init):
67
+ 1. Migration: create a new timestamped migration under migrations/ with the
68
+ CREATE TABLE (SQL) or create_<table>.js (NoSQL). Use the scaffold helper:
69
+ node commons/add_migration.js <name>
70
+ then write your CREATE TABLE SQL / collection setup inside it.
71
+ 2. Model: add models/<table>.js mirroring an existing model:
72
+ const model = require('#commons/model');
73
+ const db = require('#commons/db');
74
+ module.exports = model(db, '<table>', structure, '<pk>', uniqueKeys, option);
75
+ 3. Route: add routes/<table>/index.js via route(model):
76
+ const route = require('#commons/route');
77
+ module.exports = route(require('#models/<table>'));
78
+ then mount it in routes/index.js at the desired endpoint:
79
+ router.use('/<endpoint>', require('./<table>/index.js'));
80
+ 4. Run the new migration, then restart the server.`,
51
81
 
52
82
  inspect: `Usage: db-model-router inspect [options]
53
83
 
@@ -70,43 +100,6 @@ Examples:
70
100
  db-model-router inspect --type sqlite3 --out schema.json --tables users,posts
71
101
  db-model-router inspect --type mysql --json`,
72
102
 
73
- generate: `Usage: db-model-router generate [options]
74
-
75
- Generate models, routes, tests, OpenAPI spec, and LLM docs from a schema file.
76
- When no artifact flags are provided, all artifact types are generated.
77
-
78
- Options:
79
- --from <path> Path to schema file (default: dbmr.schema.json)
80
- --models Generate only model files
81
- --routes Generate only route files (including child routes and index)
82
- --openapi Generate only OpenAPI spec + Swagger UI docs route
83
- --tests Generate only test files
84
- --migrations Generate only database migration files
85
- --yes Accept all defaults without prompting
86
- --json Output machine-readable JSON
87
- --dry-run Report planned files without writing
88
- --help Show this help message
89
-
90
- Generated files:
91
- models/<table>.js Model with CRUD operations
92
- routes/<table>.js Express route handlers
93
- routes/<parent>/<child>.js Child route (scoped by FK)
94
- routes/docs.js Swagger UI at /docs
95
- routes/index.js Route mounting index
96
- migrations/<timestamp>_create_tables.sql Database migration (SQL adapters)
97
- migrations/<timestamp>_create_<t>.js Database migration (NoSQL adapters)
98
- test/<table>.test.js CRUD endpoint tests
99
- openapi.json OpenAPI 3.0 spec
100
- llms.txt LLM quick reference
101
- docs/llm.md Full LLM reference
102
-
103
- Examples:
104
- db-model-router generate --from dbmr.schema.json
105
- db-model-router generate --models --dry-run
106
- db-model-router generate --routes --tests
107
- db-model-router generate --migrations
108
- db-model-router generate --from dbmr.schema.json --json`,
109
-
110
103
  doctor: `Usage: db-model-router doctor [options]
111
104
 
112
105
  Validate schema, check adapter driver dependencies, and verify generated
@@ -10,70 +10,99 @@ const {
10
10
  printSummary,
11
11
  ensurePackageJson,
12
12
  } = require("../init");
13
- const { promptUser } = require("../init/prompt");
14
- const generateCmd = require("./generate");
15
-
16
- /**
17
- * Default answers used when --yes is provided and no schema is available.
18
- */
19
- const DEFAULT_ANSWERS = {
20
- framework: "express",
21
- database: "postgres",
22
- session: "memory",
23
- rateLimiting: true,
24
- helmet: true,
25
- logger: true,
26
- loki: false,
27
- };
13
+ const { buildSchemaArtifacts } = require("./generate");
28
14
 
29
15
  /**
30
16
  * Init command handler for the unified CLI.
31
17
  *
32
- * Scaffolds a new project from a schema file or interactively.
18
+ * First-buildout ONLY. Scaffolds a full project (app.js, commons/, routes/,
19
+ * migrations/, models/, tests, OpenAPI spec, optional SaaS structure) from a
20
+ * single `dbmr.schema.json` file. All project config lives in the schema's
21
+ * `options` block — no config flags here. Refuses to run if a project already
22
+ * exists (app.js or package.json present in cwd).
23
+ *
24
+ * To add new tables/models/routes after buildout, do it manually — see
25
+ * `db-model-router help init` for the procedure.
33
26
  *
34
- * @param {object} args - Parsed positional/key-value args (e.g. { from, framework, database })
27
+ * @param {object} args - Parsed args; args._[0] or args.from may hold schema path
35
28
  * @param {object} flags - Universal flags: { yes, json, dryRun, noInstall, help }
36
29
  * @param {import('../flags').OutputContext} ctx - Output context for --json support
37
30
  */
38
31
  async function init(args, flags, ctx) {
39
- let answers;
32
+ // Resolve schema path: positional arg, --from, or default ./dbmr.schema.json
33
+ const schemaPathArg =
34
+ (args._ && args._[0]) || args.from || "./dbmr.schema.json";
35
+ const schemaPath = path.resolve(schemaPathArg);
40
36
 
41
- if (args.from) {
42
- // --from points to a schema file: read adapter/framework from it
43
- const schemaPath = path.resolve(args.from);
44
- if (!fs.existsSync(schemaPath)) {
45
- throw new Error(`Schema file not found: ${args.from}`);
37
+ if (!fs.existsSync(schemaPath)) {
38
+ const msg = `Schema file not found: ${schemaPathArg}`;
39
+ if (flags.json) {
40
+ ctx.result({ error: true, code: "SCHEMA_NOT_FOUND", message: msg });
41
+ } else {
42
+ ctx.log(`Error: ${msg}`);
46
43
  }
44
+ process.exitCode = 1;
45
+ return;
46
+ }
47
+
48
+ let schema;
49
+ try {
47
50
  const raw = fs.readFileSync(schemaPath, "utf8");
48
- const schema = parseSchema(raw);
49
-
50
- answers = {
51
- framework: schema.framework,
52
- database: schema.adapter,
53
- session: (schema.options && schema.options.session) || "memory",
54
- rateLimiting: !!(schema.options && schema.options.rateLimiting),
55
- helmet: !!(schema.options && schema.options.helmet),
56
- logger: !!(schema.options && schema.options.logger),
57
- loki: !!(schema.options && schema.options.loki),
58
- };
59
- } else if (flags.yes) {
60
- // --yes with no schema: use defaults, but allow CLI overrides
61
- answers = Object.assign({}, DEFAULT_ANSWERS);
62
- if (args.framework) answers.framework = args.framework;
63
- if (args.database) answers.database = args.database;
64
- } else {
65
- // Interactive: build prefilled from CLI args, prompt for the rest
66
- const prefilled = {};
67
- if (args.framework) prefilled.framework = args.framework;
68
- if (args.database) prefilled.database = args.database;
69
- answers = await promptUser(prefilled);
51
+ schema = parseSchema(raw);
52
+ } catch (err) {
53
+ const msg = `Schema parse error: ${err.message}`;
54
+ if (flags.json) {
55
+ ctx.result({
56
+ error: true,
57
+ code: "SCHEMA_VALIDATION",
58
+ message: msg,
59
+ errors: err.errors || [],
60
+ });
61
+ } else {
62
+ ctx.log(`Error: ${msg}`);
63
+ }
64
+ process.exitCode = 1;
65
+ return;
70
66
  }
71
67
 
72
- // Resolve --output directory (relative to cwd)
73
- // CLI --output flag takes precedence, then interactive prompt answer
74
- const outputDir = args.output || answers.output || "";
68
+ // All config comes from schema.options.
69
+ const outputDir = (schema.options && schema.options.output) || "";
70
+ const baseDir = path.resolve(outputDir || process.cwd());
71
+
72
+ const answers = {
73
+ framework: schema.framework,
74
+ database: schema.adapter,
75
+ session: (schema.options && schema.options.session) || "memory",
76
+ rateLimiting: !!(schema.options && schema.options.rateLimiting),
77
+ helmet: !!(schema.options && schema.options.helmet),
78
+ logger: !!(schema.options && schema.options.logger),
79
+ loki: !!(schema.options && schema.options.loki),
80
+ port: schema.options && schema.options.port,
81
+ apiBasePath: schema.options && schema.options.apiBasePath,
82
+ output: outputDir,
83
+ };
84
+
85
+ // First-buildout-only guard: refuse if a project already exists in cwd.
86
+ // app.js is the db-model-router project marker (package.json may pre-exist
87
+ // as a legit npm project, so it alone does not block a first buildout).
88
+ if (!flags.dryRun) {
89
+ const cwd = process.cwd();
90
+ if (fs.existsSync(path.join(cwd, "app.js"))) {
91
+ const msg =
92
+ `init is first-buildout only; a project already exists in ${cwd} ` +
93
+ `(app.js present). To add new tables, models, or routes, do it ` +
94
+ `manually — see: db-model-router help init`;
95
+ if (flags.json) {
96
+ ctx.result({ error: true, code: "PROJECT_EXISTS", message: msg });
97
+ } else {
98
+ ctx.log(`Error: ${msg}`);
99
+ }
100
+ process.exitCode = 1;
101
+ return;
102
+ }
103
+ }
75
104
 
76
- // --dry-run: report planned files without writing
105
+ // --- dry-run: report planned files without writing ---
77
106
  if (flags.dryRun) {
78
107
  const planned = planFiles(answers, outputDir);
79
108
  if (flags.json) {
@@ -83,43 +112,32 @@ async function init(args, flags, ctx) {
83
112
  actions: ["dry-run"],
84
113
  });
85
114
  } else {
86
- ctx.log("Dry run — the following files would be created:");
115
+ ctx.log("Dry run — the following scaffold files would be created:");
87
116
  for (const f of planned) {
88
117
  ctx.log(` ${f}`);
89
118
  }
90
119
  }
91
- // Also preview schema-generated artifacts when --from is used
92
- if (args.from) {
93
- await generateCmd(args, flags, ctx);
94
- }
120
+ // Preview schema-driven artifacts too (non-writing)
121
+ await buildSchemaArtifacts({ schema, baseDir, ctx, flags });
95
122
  if (!flags.json) {
96
123
  ctx.log("\nNo files were written.");
97
124
  }
98
125
  return;
99
126
  }
100
127
 
101
- // Ensure package.json exists
128
+ // --- real run ---
102
129
  ensurePackageJson();
103
130
 
104
- // Generate project files
105
131
  const generated = generateFiles(answers, outputDir);
106
-
107
- // Update package.json with deps and scripts
108
132
  updatePackageJson(answers, outputDir);
109
133
 
110
- // npm install (unless --no-install)
111
134
  const installed = !flags.noInstall;
112
135
  if (installed) {
113
136
  runInstall();
114
137
  }
115
138
 
116
- // When --from points to a schema, also generate models, routes, tests, etc.
117
- if (args.from) {
118
- await generateCmd(args, flags, ctx);
119
- if (process.exitCode) return; // bail if generate reported an error
120
- }
121
-
122
- // Output
139
+ // Build init's JSON result first so it lands at ctx._results[0] (buildSchemaArtifacts
140
+ // pushes its own result afterward).
123
141
  const allFiles = [
124
142
  ...generated.files,
125
143
  ...generated.migrationFiles.map((m) => {
@@ -127,14 +145,20 @@ async function init(args, flags, ctx) {
127
145
  return base === "." ? `migrations/${m}` : `${base}/migrations/${m}`;
128
146
  }),
129
147
  ];
130
-
131
148
  if (flags.json) {
132
149
  ctx.result({
133
150
  files: allFiles,
134
151
  dependencies: { installed },
135
152
  actions: installed ? ["scaffolded", "installed"] : ["scaffolded"],
136
153
  });
137
- } else {
154
+ }
155
+
156
+ // Schema-driven artifacts: models, routes, migrations, openapi, tests, SaaS
157
+ await buildSchemaArtifacts({ schema, baseDir, ctx, flags });
158
+ if (process.exitCode) return; // bail if build reported an error
159
+
160
+ // Human-readable summary (non-json only)
161
+ if (!flags.json) {
138
162
  printSummary(generated);
139
163
  if (!installed) {
140
164
  ctx.log(
@@ -145,8 +169,8 @@ async function init(args, flags, ctx) {
145
169
  }
146
170
 
147
171
  /**
148
- * Compute the list of files that would be created (for --dry-run).
149
- * This mirrors the file list from generateFiles() without writing anything.
172
+ * Compute the list of scaffold files that would be created (for --dry-run).
173
+ * Mirrors generateFiles() without writing anything.
150
174
  *
151
175
  * @param {object} answers
152
176
  * @param {string} [outputDir] - relative output directory for source files
@@ -166,7 +190,6 @@ function planFiles(answers, outputDir) {
166
190
  ".dockerignore",
167
191
  ];
168
192
 
169
- // docker-compose.yml for databases that need Docker
170
193
  if (answers.database !== "sqlite3") {
171
194
  files.push("docker-compose.yml");
172
195
  }
@@ -191,4 +214,4 @@ function planFiles(answers, outputDir) {
191
214
  return files;
192
215
  }
193
216
 
194
- module.exports = init;
217
+ module.exports = init;
package/src/cli/flags.js CHANGED
@@ -25,7 +25,7 @@ function parseFlags(argv) {
25
25
  help: false,
26
26
  };
27
27
 
28
- const args = {};
28
+ const args = { _: [] };
29
29
  let subcommand = null;
30
30
 
31
31
  for (let i = 0; i < argv.length; i++) {
@@ -53,6 +53,9 @@ function parseFlags(argv) {
53
53
  }
54
54
  } else if (subcommand === null) {
55
55
  subcommand = arg;
56
+ } else {
57
+ // Extra positional args after the subcommand
58
+ args._.push(arg);
56
59
  }
57
60
  }
58
61
 
@@ -168,8 +168,8 @@ function buildEnvContent(answers, mode, secrets) {
168
168
  const pick = mode === "placeholder" ? "placeholder" : "defaultValue";
169
169
  const lines = [];
170
170
  lines.push("# Server");
171
- lines.push("PORT=3000");
172
- lines.push("API_BASE_PATH=/api");
171
+ lines.push(`PORT=${answers.port != null ? answers.port : 3000}`);
172
+ lines.push(`API_BASE_PATH=${answers.apiBasePath || "/api"}`);
173
173
  lines.push("");
174
174
  lines.push("# Database");
175
175
  lines.push(`DB_TYPE=${answers.database}`);
package/src/cli/init.js CHANGED
@@ -28,7 +28,6 @@ const {
28
28
  } = require("./init/generators");
29
29
 
30
30
  const { collectDependencies, getScripts } = require("./init/dependencies");
31
- const { promptUser, parseInitArgs } = require("./init/prompt");
32
31
 
33
32
  /**
34
33
  * Ensure a package.json exists in the current directory.
@@ -304,84 +303,21 @@ function printSummary(generated) {
304
303
  }
305
304
 
306
305
  /**
307
- * Main orchestrator.
306
+ * Legacy direct-entry orchestrator.
307
+ *
308
+ * This module is no longer a user-facing entry point. The unified CLI
309
+ * (`src/cli/main.js` → `db-model-router init`) drives project buildout via
310
+ * `dbmr.schema.json` and calls `generateFiles` / `updatePackageJson` /
311
+ * `runInstall` / `printSummary` directly. This stub kept only so that running
312
+ * `node src/cli/init.js` points users at the unified CLI instead of silently
313
+ * doing nothing.
308
314
  */
309
315
  async function main() {
310
- const cliArgs = parseInitArgs(process.argv.slice(2));
311
-
312
- // If --help flag, print usage and exit
313
- if (process.argv.includes("--help")) {
314
- printUsage();
315
- process.exit(0);
316
- }
317
-
318
- ensurePackageJson();
319
-
320
- let answers;
321
- try {
322
- answers = await promptUser(cliArgs);
323
- } catch (err) {
324
- // Handle Ctrl+C (inquirer throws on user cancel)
325
- console.log("\nAborted.");
326
- process.exit(1);
327
- }
328
-
329
- let generated;
330
- try {
331
- generated = generateFiles(answers);
332
- } catch (err) {
333
- if (err.code === "EACCES" || err.code === "EPERM") {
334
- console.error(
335
- `Error: Permission denied writing files. Check directory permissions.\n ${err.message}`,
336
- );
337
- } else {
338
- console.error(`Error: Failed to generate files.\n ${err.message}`);
339
- }
340
- process.exit(1);
341
- }
342
-
343
- updatePackageJson(answers);
344
- runInstall();
345
- printSummary(generated);
346
- }
347
-
348
- /**
349
- * Print CLI usage information.
350
- */
351
- function printUsage() {
352
- console.log(`
353
- Usage: db-model-router-init [options]
354
-
355
- Scaffolds a complete Express-based REST API project. When all options are
356
- provided, runs non-interactively (no prompts). Missing options are prompted.
357
-
358
- Options:
359
- --framework <name> Express framework: ultimate-express, express
360
- --database <name> Database: mysql, postgres, sqlite3, mongodb, mssql,
361
- cockroachdb, oracle, redis, dynamodb
362
- --db <name> Alias for --database
363
- --session <type> Session store: memory, redis, database
364
- --output <dir> Directory for backend source files (e.g. --output backend).
365
- package.json stays in root; index.js, commons/, routes/,
366
- middleware/, and migrations/ go inside the output folder.
367
- --rateLimiting Enable rate limiting (express-rate-limit)
368
- --helmet Enable Helmet security headers
369
- --logger Enable request/response logger (express-mung)
370
- --help Show this help message
371
-
372
- Examples:
373
- # Fully non-interactive (LLM-friendly)
374
- db-model-router-init --framework express --database postgres --session redis --rateLimiting --helmet --logger
375
-
376
- # With output directory
377
- db-model-router-init --framework express --database postgres --output backend --yes
378
-
379
- # Partial — only prompts for missing values
380
- db-model-router-init --database mysql --session memory
381
-
382
- # Interactive (no flags)
383
- db-model-router-init
384
- `);
316
+ console.error(
317
+ "db-model-router init is now the unified entry point.\n" +
318
+ "Run: db-model-router init [schemaPath] (default: ./dbmr.schema.json)",
319
+ );
320
+ process.exit(1);
385
321
  }
386
322
 
387
323
  if (require.main === module) {
package/src/cli/main.js CHANGED
@@ -5,7 +5,6 @@ const { parseFlags, OutputContext } = require("./flags");
5
5
 
6
6
  const initCmd = require("./commands/init");
7
7
  const inspectCmd = require("./commands/inspect");
8
- const generateCmd = require("./commands/generate");
9
8
  const doctorCmd = require("./commands/doctor");
10
9
  const diffCmd = require("./commands/diff");
11
10
  const helpCmd = require("./commands/help");
@@ -17,7 +16,6 @@ const dbManagerCmd = require("./commands/db-manager");
17
16
  const COMMANDS = {
18
17
  init: initCmd,
19
18
  inspect: inspectCmd,
20
- generate: generateCmd,
21
19
  doctor: doctorCmd,
22
20
  diff: diffCmd,
23
21
  "db-manager": dbManagerCmd,
@@ -28,9 +26,8 @@ const COMMANDS = {
28
26
  * Descriptions for each subcommand, used in help output.
29
27
  */
30
28
  const COMMAND_DESCRIPTIONS = {
31
- init: "Scaffold a new project from a schema file or interactively",
29
+ init: "First-buildout only: scaffold a full project from dbmr.schema.json",
32
30
  inspect: "Introspect a live database and produce a schema file",
33
- generate: "Generate models, routes, tests, and OpenAPI spec from a schema",
34
31
  doctor: "Validate schema, check dependencies, and verify file sync",
35
32
  diff: "Preview changes between current files and what the schema would produce",
36
33
  "db-manager": "Start a live database management UI",
@@ -42,18 +39,9 @@ const COMMAND_DESCRIPTIONS = {
42
39
  */
43
40
  const COMMAND_FLAGS = {
44
41
  init: [
45
- ["--from <path>", "Read config from a schema file"],
46
- ["--framework <name>", "express, ultimate-express"],
47
- [
48
- "--database <name>",
49
- "mysql, mariadb, postgres, sqlite3, mongodb, mssql, cockroachdb, oracle, redis, dynamodb",
50
- ],
51
- ["--db <name>", "Alias for --database"],
52
- ["--session <type>", "memory, redis, database"],
53
- ["--output <dir>", "Directory for backend source files"],
54
- ["--rateLimiting", "Enable rate limiting (default: yes)"],
55
- ["--helmet", "Enable Helmet security headers (default: yes)"],
56
- ["--logger", "Enable Winston + Loki logger (default: yes)"],
42
+ ["[schemaPath]", "Path to schema file (default: ./dbmr.schema.json)"],
43
+ ["--dry-run", "Preview planned files without writing"],
44
+ ["--no-install", "Skip npm install after scaffolding"],
57
45
  ],
58
46
  inspect: [
59
47
  ["--type <adapter>", "Database adapter (required)"],
@@ -61,15 +49,6 @@ const COMMAND_FLAGS = {
61
49
  ["--out <path>", "Output file (default: dbmr.schema.json)"],
62
50
  ["--tables <list>", "Comma-separated table filter"],
63
51
  ],
64
- generate: [
65
- ["--from <path>", "Schema file (default: dbmr.schema.json)"],
66
- ["--output <dir>", "Directory for generated files (default: cwd)"],
67
- ["--models", "Generate only model files"],
68
- ["--routes", "Generate only route files"],
69
- ["--openapi", "Generate only OpenAPI spec"],
70
- ["--tests", "Generate only test files"],
71
- ["--db-manager", "Generate DB Manager UI (SQL adapters only)"],
72
- ],
73
52
  doctor: [["--from <path>", "Schema file (default: dbmr.schema.json)"]],
74
53
  diff: [["--from <path>", "Schema file (default: dbmr.schema.json)"]],
75
54
  "db-manager": [
@@ -393,7 +393,7 @@ async function remove(table, filter, safeDelete = null) {
393
393
  let sql;
394
394
  let params;
395
395
  if (safeDelete != null) {
396
- sql = `UPDATE ${escapeId(table)} SET ${escapeId(safeDelete)} = 1 ${whereData.query}`;
396
+ sql = `UPDATE ${escapeId(table)} SET ${escapeId(safeDelete)} = TRUE ${whereData.query}`;
397
397
  params = whereData.value;
398
398
  } else {
399
399
  sql = `DELETE FROM ${escapeId(table)} ${whereData.query}`;
@@ -113,12 +113,29 @@ function parseSchema(input) {
113
113
  }
114
114
  }
115
115
 
116
+ // Normalize options with defaults for buildout config.
117
+ // output defaults to null (caller resolves to cwd); saasStructure defaults on;
118
+ // apiBasePath defaults to "/api"; port defaults to 3000.
119
+ const rawOptions = raw.options ? { ...raw.options } : {};
120
+ const options = {
121
+ ...rawOptions,
122
+ session: rawOptions.session || "memory",
123
+ rateLimiting: rawOptions.rateLimiting !== undefined ? !!rawOptions.rateLimiting : true,
124
+ helmet: rawOptions.helmet !== undefined ? !!rawOptions.helmet : true,
125
+ logger: rawOptions.logger !== undefined ? !!rawOptions.logger : true,
126
+ loki: rawOptions.loki !== undefined ? !!rawOptions.loki : false,
127
+ output: rawOptions.output !== undefined ? rawOptions.output : null,
128
+ saasStructure: rawOptions.saasStructure !== undefined ? !!rawOptions.saasStructure : true,
129
+ apiBasePath: rawOptions.apiBasePath || "/api",
130
+ port: rawOptions.port !== undefined ? rawOptions.port : 3000,
131
+ };
132
+
116
133
  return {
117
134
  adapter: raw.adapter,
118
135
  framework: raw.framework,
119
136
  tables,
120
137
  relationships: derivedRelationships,
121
- options: raw.options ? { ...raw.options } : {},
138
+ options,
122
139
  };
123
140
  }
124
141