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.
@@ -104,12 +104,87 @@ function validateSchema(raw) {
104
104
  Array.isArray(raw.options)
105
105
  ) {
106
106
  errors.push({ path: "options", message: "options must be an object" });
107
+ } else {
108
+ validateOptions(raw.options, errors);
107
109
  }
108
110
  }
109
111
 
110
112
  return { valid: errors.length === 0, errors };
111
113
  }
112
114
 
115
+ /**
116
+ * Validate the buildout-config keys inside `options`.
117
+ * Only the known keys are type-checked; unknown keys are passed through silently.
118
+ * - output: string (relative or absolute path)
119
+ * - saasStructure: boolean
120
+ * - apiBasePath: string starting with "/"
121
+ * - port: positive integer
122
+ * - session: "memory" | "redis" | "database"
123
+ * - rateLimiting / helmet / logger / loki: boolean
124
+ */
125
+ function validateOptions(options, errors) {
126
+ if (options.output !== undefined && options.output !== null) {
127
+ if (typeof options.output !== "string" || options.output.length === 0) {
128
+ errors.push({
129
+ path: "options.output",
130
+ message: "options.output must be a non-empty string path or null",
131
+ });
132
+ }
133
+ }
134
+
135
+ if (options.saasStructure !== undefined) {
136
+ if (typeof options.saasStructure !== "boolean") {
137
+ errors.push({
138
+ path: "options.saasStructure",
139
+ message: "options.saasStructure must be a boolean",
140
+ });
141
+ }
142
+ }
143
+
144
+ if (options.apiBasePath !== undefined) {
145
+ if (
146
+ typeof options.apiBasePath !== "string" ||
147
+ !options.apiBasePath.startsWith("/")
148
+ ) {
149
+ errors.push({
150
+ path: "options.apiBasePath",
151
+ message: 'options.apiBasePath must be a string starting with "/" (e.g. "/api")',
152
+ });
153
+ }
154
+ }
155
+
156
+ if (options.port !== undefined) {
157
+ if (
158
+ typeof options.port !== "number" ||
159
+ !Number.isInteger(options.port) ||
160
+ options.port <= 0
161
+ ) {
162
+ errors.push({
163
+ path: "options.port",
164
+ message: "options.port must be a positive integer",
165
+ });
166
+ }
167
+ }
168
+
169
+ if (options.session !== undefined) {
170
+ if (!["memory", "redis", "database"].includes(options.session)) {
171
+ errors.push({
172
+ path: "options.session",
173
+ message: 'options.session must be one of: memory, redis, database',
174
+ });
175
+ }
176
+ }
177
+
178
+ for (const key of ["rateLimiting", "helmet", "logger", "loki"]) {
179
+ if (options[key] !== undefined && typeof options[key] !== "boolean") {
180
+ errors.push({
181
+ path: `options.${key}`,
182
+ message: `options.${key} must be a boolean`,
183
+ });
184
+ }
185
+ }
186
+ }
187
+
113
188
  /**
114
189
  * Validate all table entries.
115
190
  */
@@ -1,191 +0,0 @@
1
- "use strict";
2
-
3
- const inquirer = require("inquirer");
4
-
5
- const VALID_FRAMEWORKS = ["ultimate-express", "express"];
6
- const VALID_DATABASES = [
7
- "mysql",
8
- "mariadb",
9
- "postgres",
10
- "sqlite3",
11
- "mongodb",
12
- "mssql",
13
- "cockroachdb",
14
- "oracle",
15
- "redis",
16
- "dynamodb",
17
- ];
18
- const VALID_SESSIONS = ["memory", "redis", "database"];
19
-
20
- /**
21
- * Parse CLI arguments into a partial answers object.
22
- * Supports: --framework, --database, --session, --rateLimiting, --helmet, --logger
23
- * Boolean flags accept: true/false, yes/no, 1/0, or just --flag (implies true)
24
- * @param {string[]} argv
25
- * @returns {Partial<import('./types').InitAnswers>}
26
- */
27
- function parseInitArgs(argv) {
28
- const args = {};
29
- for (let i = 0; i < argv.length; i++) {
30
- const arg = argv[i];
31
- if (arg.startsWith("--")) {
32
- const key = arg.slice(2);
33
- const next = argv[i + 1];
34
- if (next && !next.startsWith("--")) {
35
- args[key] = next;
36
- i++;
37
- } else {
38
- args[key] = "true";
39
- }
40
- }
41
- }
42
-
43
- const partial = {};
44
-
45
- if (args.framework && VALID_FRAMEWORKS.includes(args.framework)) {
46
- partial.framework = args.framework;
47
- }
48
- if (args.database && VALID_DATABASES.includes(args.database)) {
49
- partial.database = args.database;
50
- }
51
- // Accept --db as alias for --database
52
- if (!partial.database && args.db && VALID_DATABASES.includes(args.db)) {
53
- partial.database = args.db;
54
- }
55
- if (args.session && VALID_SESSIONS.includes(args.session)) {
56
- partial.session = args.session;
57
- }
58
-
59
- // Boolean flags
60
- for (const flag of ["rateLimiting", "helmet", "logger", "loki"]) {
61
- if (args[flag] !== undefined) {
62
- partial[flag] = parseBool(args[flag]);
63
- }
64
- }
65
-
66
- // Output directory
67
- if (args.output !== undefined && args.output !== "true") {
68
- partial.output = args.output;
69
- }
70
-
71
- return partial;
72
- }
73
-
74
- /**
75
- * Parse a string as a boolean.
76
- * @param {string} val
77
- * @returns {boolean}
78
- */
79
- function parseBool(val) {
80
- return ["true", "yes", "1"].includes(String(val).toLowerCase());
81
- }
82
-
83
- /**
84
- * Run the interactive prompt flow to collect user preferences.
85
- * Any values already present in `prefilledAnswers` are skipped (not prompted).
86
- * When all 6 values are provided, no prompts are shown at all.
87
- * @param {Partial<import('./types').InitAnswers>} [prefilledAnswers={}]
88
- * @returns {Promise<import('./types').InitAnswers>}
89
- */
90
- async function promptUser(prefilledAnswers) {
91
- const prefilled = prefilledAnswers || {};
92
-
93
- const questions = [];
94
-
95
- if (prefilled.framework === undefined) {
96
- questions.push({
97
- type: "list",
98
- name: "framework",
99
- message: "Select your Express framework:",
100
- choices: VALID_FRAMEWORKS,
101
- default: "ultimate-express",
102
- });
103
- }
104
-
105
- if (prefilled.database === undefined) {
106
- questions.push({
107
- type: "list",
108
- name: "database",
109
- message: "Select your database:",
110
- choices: VALID_DATABASES,
111
- });
112
- }
113
-
114
- if (prefilled.session === undefined) {
115
- questions.push({
116
- type: "list",
117
- name: "session",
118
- message: "Select your session store:",
119
- choices: VALID_SESSIONS,
120
- });
121
- }
122
-
123
- if (prefilled.output === undefined) {
124
- questions.push({
125
- type: "input",
126
- name: "output",
127
- message:
128
- "Output directory for backend source files (leave empty for root):",
129
- default: "",
130
- });
131
- }
132
-
133
- if (prefilled.rateLimiting === undefined) {
134
- questions.push({
135
- type: "confirm",
136
- name: "rateLimiting",
137
- message: "Enable rate limiting?",
138
- default: true,
139
- });
140
- }
141
-
142
- if (prefilled.helmet === undefined) {
143
- questions.push({
144
- type: "confirm",
145
- name: "helmet",
146
- message: "Enable Helmet security headers?",
147
- default: true,
148
- });
149
- }
150
-
151
- if (prefilled.logger === undefined) {
152
- questions.push({
153
- type: "confirm",
154
- name: "logger",
155
- message: "Enable request/response logger (Winston)?",
156
- default: true,
157
- });
158
- }
159
-
160
- // If all values are prefilled, skip prompts entirely
161
- if (questions.length === 0) {
162
- return /** @type {import('./types').InitAnswers} */ (prefilled);
163
- }
164
-
165
- const prompted = await inquirer.prompt(questions);
166
-
167
- // Follow-up: if logger is enabled, ask about Loki
168
- if (prompted.logger && prefilled.loki === undefined) {
169
- const lokiAnswer = await inquirer.prompt([
170
- {
171
- type: "confirm",
172
- name: "loki",
173
- message: "Send logs to Grafana Loki?",
174
- default: false,
175
- },
176
- ]);
177
- prompted.loki = lokiAnswer.loki;
178
- } else if (!prompted.logger && prefilled.logger === undefined) {
179
- prompted.loki = false;
180
- }
181
-
182
- return Object.assign({}, prefilled, prompted);
183
- }
184
-
185
- module.exports = {
186
- promptUser,
187
- parseInitArgs,
188
- VALID_FRAMEWORKS,
189
- VALID_DATABASES,
190
- VALID_SESSIONS,
191
- };