db-model-router 1.0.17 → 1.0.19
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/.serena/project.yml +57 -30
- package/README.md +117 -104
- package/dbmr.postgres.schema.json +5 -1
- package/dbmr.postgres.test.schema.json +5 -1
- package/dbmr.schema.json +5 -1
- package/docs/dbmr-schema-spec.md +28 -4
- package/package.json +1 -1
- package/scripts/demo-create.js +56 -87
- package/skill/SKILL.md +77 -46
- package/src/cli/commands/generate.js +32 -115
- package/src/cli/commands/help.js +55 -62
- package/src/cli/commands/init.js +95 -72
- package/src/cli/flags.js +4 -1
- package/src/cli/init/generators.js +2 -2
- package/src/cli/init.js +13 -77
- package/src/cli/main.js +4 -25
- package/src/cli/saas/generate-saas-migrations.js +2 -1
- package/src/schema/schema-parser.js +18 -1
- package/src/schema/schema-validator.js +75 -0
- package/src/cli/init/prompt.js +0 -191
package/src/cli/commands/help.js
CHANGED
|
@@ -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
|
-
|
|
11
|
-
|
|
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
|
|
42
|
-
<output>/routes/index.js
|
|
43
|
-
<output>/routes/health.js
|
|
44
|
-
<output>/
|
|
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
|
|
48
|
-
db-model-router init
|
|
49
|
-
db-model-router init --
|
|
50
|
-
db-model-router init --
|
|
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
|
package/src/cli/commands/init.js
CHANGED
|
@@ -10,70 +10,99 @@ const {
|
|
|
10
10
|
printSummary,
|
|
11
11
|
ensurePackageJson,
|
|
12
12
|
} = require("../init");
|
|
13
|
-
const {
|
|
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
|
|
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
|
|
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
|
-
|
|
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 (
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
//
|
|
73
|
-
|
|
74
|
-
const
|
|
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
|
-
//
|
|
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
|
-
//
|
|
92
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
117
|
-
|
|
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
|
-
}
|
|
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
|
-
*
|
|
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(
|
|
172
|
-
lines.push("
|
|
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
|
-
*
|
|
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
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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: "
|
|
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
|
-
["
|
|
46
|
-
["--
|
|
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": [
|
|
@@ -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",
|
|
@@ -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
|
|
138
|
+
options,
|
|
122
139
|
};
|
|
123
140
|
}
|
|
124
141
|
|