@seip/blue-bird 1.3.0 → 1.3.2

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.
@@ -33,16 +33,44 @@ function getEnvVars() {
33
33
  * @returns {string} 'sqlite', 'postgres', 'mysql', or 'none'.
34
34
  */
35
35
  function getDbType(env = getEnvVars()) {
36
- if (env.DB_TYPE && (env.DB_TYPE.toLowerCase() === "sqlite" || env.DB_TYPE.toLowerCase() === "sqlite3")) {
36
+ const dbTypeRaw = (env.DB_TYPE || "").toLowerCase().trim();
37
+ if (
38
+ dbTypeRaw === "sqlite" ||
39
+ dbTypeRaw === "sqlite3" ||
40
+ dbTypeRaw === "sql" ||
41
+ dbTypeRaw === "better-sqlite3" ||
42
+ dbTypeRaw === "better-sqlite" ||
43
+ dbTypeRaw === "lite"
44
+ ) {
37
45
  return "sqlite";
38
46
  }
39
- if (env.DB_TYPE && (env.DB_TYPE.toLowerCase() === "postgres" || env.DB_TYPE.toLowerCase() === "postgresql" || env.DB_TYPE.toLowerCase() === "pg")) {
47
+ if (
48
+ dbTypeRaw === "postgres" ||
49
+ dbTypeRaw === "postgresql" ||
50
+ dbTypeRaw === "pg" ||
51
+ dbTypeRaw === "psql" ||
52
+ dbTypeRaw === "pgsql" ||
53
+ dbTypeRaw === "postgre" ||
54
+ dbTypeRaw === "postgr" ||
55
+ dbTypeRaw === "psgr"
56
+ ) {
40
57
  return "postgres";
41
58
  }
42
- if (env.DB_TYPE && env.DB_TYPE.toLowerCase() === "mysql") {
59
+ if (
60
+ dbTypeRaw === "mysql" ||
61
+ dbTypeRaw === "mariadb" ||
62
+ dbTypeRaw === "maria" ||
63
+ dbTypeRaw === "my"
64
+ ) {
43
65
  return "mysql";
44
66
  }
45
- if (env.DB_TYPE && env.DB_TYPE.toLowerCase() === "none") {
67
+ if (
68
+ dbTypeRaw === "none" ||
69
+ dbTypeRaw === "no" ||
70
+ dbTypeRaw === "false" ||
71
+ dbTypeRaw === "null" ||
72
+ dbTypeRaw === "0"
73
+ ) {
46
74
  return "none";
47
75
  }
48
76
  if (env.DATABASE_URL && !env.DATABASE_URL.startsWith("#")) {
@@ -268,7 +268,17 @@ export async function runDoctor() {
268
268
  // 5. Database & Cache Driver Status
269
269
  // -------------------------------------------------------------
270
270
  console.log(chalk.bold("[5/5] Database & Cache Architecture:"));
271
- const dbType = (env.DB_TYPE || "sqlite").toLowerCase();
271
+ const rawDbType = (env.DB_TYPE || "").toLowerCase().trim();
272
+ let dbType = "sqlite";
273
+ if (["sqlite", "sqlite3", "sql", "better-sqlite3", "better-sqlite", "lite"].includes(rawDbType)) {
274
+ dbType = "sqlite";
275
+ } else if (["postgres", "postgresql", "pg", "psql", "pgsql", "postgre", "postgr", "psgr"].includes(rawDbType)) {
276
+ dbType = "postgres";
277
+ } else if (["mysql", "mariadb", "maria", "my"].includes(rawDbType)) {
278
+ dbType = "mysql";
279
+ } else if (["none", "no", "false", "null", "0"].includes(rawDbType)) {
280
+ dbType = "none";
281
+ }
272
282
  console.log(chalk.green(` [INFO] Database Type: ${dbType.toUpperCase()}`));
273
283
  const cacheMode = (env.CACHE_MODE || "memory").toLowerCase();
274
284
  console.log(chalk.green(` [INFO] Cache Mode: ${cacheMode.toUpperCase()}`));
package/core/cli/init.js CHANGED
@@ -62,21 +62,47 @@ class ProjectInit {
62
62
  const cleanDbTypeAns = dbTypeAns.toLowerCase().trim();
63
63
  if (
64
64
  cleanDbTypeAns === "sqlite" ||
65
+ cleanDbTypeAns === "sqlite3" ||
65
66
  cleanDbTypeAns === "sql" ||
66
- cleanDbTypeAns === "sqlite3"
67
+ cleanDbTypeAns === "better-sqlite3" ||
68
+ cleanDbTypeAns === "better-sqlite" ||
69
+ cleanDbTypeAns === "lite"
67
70
  ) {
68
71
  dbType = "sqlite";
69
72
  } else if (
70
73
  cleanDbTypeAns === "postgres" ||
71
74
  cleanDbTypeAns === "postgresql" ||
72
75
  cleanDbTypeAns === "pg" ||
73
- cleanDbTypeAns === "postgr"
76
+ cleanDbTypeAns === "psql" ||
77
+ cleanDbTypeAns === "pgsql" ||
78
+ cleanDbTypeAns === "postgre" ||
79
+ cleanDbTypeAns === "postgr" ||
80
+ cleanDbTypeAns === "psgr"
74
81
  ) {
75
82
  dbType = "postgres";
76
- } else if (cleanDbTypeAns === "mysql") {
83
+ } else if (
84
+ cleanDbTypeAns === "mysql" ||
85
+ cleanDbTypeAns === "mariadb" ||
86
+ cleanDbTypeAns === "maria" ||
87
+ cleanDbTypeAns === "my"
88
+ ) {
77
89
  dbType = "mysql";
78
- } else {
90
+ } else if (
91
+ cleanDbTypeAns === "none" ||
92
+ cleanDbTypeAns === "no" ||
93
+ cleanDbTypeAns === "null" ||
94
+ cleanDbTypeAns === "false" ||
95
+ cleanDbTypeAns === "0" ||
96
+ cleanDbTypeAns === "n"
97
+ ) {
79
98
  dbType = "none";
99
+ } else {
100
+ console.log(
101
+ chalk.yellow(
102
+ `[WARN] Unknown database '${dbTypeAns}'. Defaulting to 'sqlite'.`,
103
+ ),
104
+ );
105
+ dbType = "sqlite";
80
106
  }
81
107
 
82
108
  if (dbType === "sqlite") {
@@ -87,14 +113,21 @@ class ProjectInit {
87
113
  if (!fs.existsSync(dbDir)) {
88
114
  fs.mkdirSync(dbDir, { recursive: true });
89
115
  }
90
- } else if (dbType !== "none") {
116
+ } else if (dbType === "mysql") {
91
117
  dbName = await ask("Database Name", dbName);
92
- dbUser = await ask(
93
- "Database User",
94
- dbType === "postgres" ? "postgres" : "root",
95
- );
96
- dbPassword = await ask("Database Password", dbPassword);
97
- const defaultDbPort = dbType === "postgres" ? 5432 : 3306;
118
+ dbUser = await ask("Database User", "root");
119
+ dbPassword = await ask("Database Password", "root");
120
+ const defaultDbPort = 3306;
121
+ const dbPortInput = await ask("Database Port", defaultDbPort);
122
+ dbPort = parseInt(dbPortInput, 10);
123
+ if (Number.isNaN(dbPort)) {
124
+ dbPort = defaultDbPort;
125
+ }
126
+ } else if (dbType === "postgres") {
127
+ dbName = await ask("Database Name", dbName);
128
+ dbUser = await ask("Database User", "postgres");
129
+ dbPassword = await ask("Database Password", "root");
130
+ const defaultDbPort = 5432;
98
131
  const dbPortInput = await ask("Database Port", defaultDbPort);
99
132
  dbPort = parseInt(dbPortInput, 10);
100
133
  if (Number.isNaN(dbPort)) {
@@ -176,106 +209,70 @@ class ProjectInit {
176
209
  );
177
210
  const composeDest = path.join(this.appDir, "docker-compose.yml");
178
211
  if (fs.existsSync(composeSrc)) {
179
- if (!fs.existsSync(composeDest)) {
180
- fs.copyFileSync(composeSrc, composeDest);
181
- console.log(
182
- chalk.green(`[OK] Created docker-compose.yml (${dbType} mode).`),
183
- );
184
- } else {
185
- console.log(
186
- chalk.yellow(`[SKIP] docker-compose.yml already exists, skipping.`),
187
- );
188
- }
212
+ fs.copyFileSync(composeSrc, composeDest);
213
+ console.log(
214
+ chalk.green(`[OK] Created docker-compose.yml (${dbType} mode).`),
215
+ );
189
216
  } else {
190
217
  const fallbackSrc = path.join(this.sourceDir, "docker-compose.yml");
191
- if (fs.existsSync(fallbackSrc) && !fs.existsSync(composeDest)) {
218
+ if (fs.existsSync(fallbackSrc)) {
192
219
  fs.copyFileSync(fallbackSrc, composeDest);
193
220
  console.log(chalk.green(`[OK] Created docker-compose.yml.`));
194
221
  }
195
222
  }
196
223
 
197
224
  const envPath = path.join(this.appDir, ".env");
198
- const envExamplePath = path.join(this.appDir, ".env_example");
199
-
200
- if (fs.existsSync(envExamplePath)) {
201
- let envContent = fs.readFileSync(envExamplePath, "utf-8");
202
-
203
- const jwtSecret = crypto.randomBytes(32).toString("hex");
204
- const composeProjectName =
205
- title
206
- .toLowerCase()
207
- .trim()
208
- .replace(/\s+/g, "-")
209
- .replace(/[^a-z0-9_-]/g, "") || "blue-bird";
210
-
211
- const updates = {
212
- COMPOSE_PROJECT_NAME: composeProjectName,
213
- TITLE: title,
214
- PORT: port,
215
- APP_URL: appUrl,
216
- JWT_SECRET: jwtSecret,
217
- DB_TYPE: dbType,
218
- };
219
-
220
- if (dbType === "sqlite") {
221
- updates.DB_TYPE = "sqlite";
222
- updates.DB_FILE = dbFile;
223
- updates.DATABASE_URL = `sqlite:${dbFile}`;
224
- } else if (dbType === "mysql") {
225
- updates.DB_TYPE = "mysql";
226
- updates.DB_NAME = dbName;
227
- updates.DB_USER = dbUser;
228
- updates.DB_PASSWORD = dbPassword;
229
- updates.DB_PORT = dbPort;
230
- updates.DATABASE_URL = `mysql://${dbUser}:${dbPassword}@localhost:${dbPort}/${dbName}`;
231
- } else if (dbType === "postgres") {
232
- updates.DB_TYPE = "postgres";
233
- updates.DB_NAME = dbName;
234
- updates.DB_USER = dbUser;
235
- updates.DB_PASSWORD = dbPassword;
236
- updates.DB_PORT = dbPort;
237
- updates.DATABASE_URL = `postgresql://${dbUser}:${dbPassword}@localhost:${dbPort}/${dbName}?schema=public`;
238
- }
225
+ const jwtSecret = crypto.randomBytes(32).toString("hex");
226
+ const composeProjectName =
227
+ title
228
+ .toLowerCase()
229
+ .trim()
230
+ .replace(/\s+/g, "-")
231
+ .replace(/[^a-z0-9_-]/g, "") || "blue-bird";
232
+
233
+ let dbEnvBlock = "";
234
+ if (dbType === "sqlite") {
235
+ dbEnvBlock = `# Database Configuration\n# SQLite (Default - uses WAL mode and busy timeout automatically)\nDB_TYPE="sqlite"\nDB_FILE="${dbFile}"\nDATABASE_URL="sqlite:${dbFile}"`;
236
+ } else if (dbType === "mysql") {
237
+ dbEnvBlock = `# Database Configuration\n# MySQL\nDB_TYPE="mysql"\nDB_HOST="localhost"\nDB_PORT=${dbPort}\nDB_NAME="${dbName}"\nDB_USER="${dbUser}"\nDB_PASSWORD="${dbPassword}"\nDATABASE_URL="mysql://${dbUser}:${dbPassword}@localhost:${dbPort}/${dbName}"`;
238
+ } else if (dbType === "postgres") {
239
+ dbEnvBlock = `# Database Configuration\n# PostgreSQL\nDB_TYPE="postgres"\nDB_HOST="localhost"\nDB_PORT=${dbPort}\nDB_NAME="${dbName}"\nDB_USER="${dbUser}"\nDB_PASSWORD="${dbPassword}"\nDATABASE_URL="postgresql://${dbUser}:${dbPassword}@localhost:${dbPort}/${dbName}?schema=public"`;
240
+ } else {
241
+ dbEnvBlock = `# Database Configuration\n# None\nDB_TYPE="none"`;
242
+ }
239
243
 
240
- const lines = envContent.split(/\r?\n/);
241
- let foundComposeProjectName = false;
242
- const updatedLines = lines.map((line) => {
243
- const match = line.match(/^([A-Z_]+)=(.+)/);
244
- if (match) {
245
- const key = match[1];
246
- if (key === "COMPOSE_PROJECT_NAME") {
247
- foundComposeProjectName = true;
248
- }
249
- if (updates[key] !== undefined) {
250
- const value = updates[key];
251
- if (typeof value === "string" && !value.startsWith('"')) {
252
- return `${key}="${value}"`;
253
- }
254
- return `${key}=${value}`;
255
- }
256
- }
257
- return line;
258
- });
259
-
260
- if (!foundComposeProjectName && updates.COMPOSE_PROJECT_NAME) {
261
- const titleIdx = updatedLines.findIndex((l) => l.startsWith("TITLE="));
262
- if (titleIdx !== -1) {
263
- updatedLines.splice(
264
- titleIdx,
265
- 0,
266
- `COMPOSE_PROJECT_NAME="${updates.COMPOSE_PROJECT_NAME}"`,
267
- );
268
- } else {
269
- updatedLines.push(
270
- `COMPOSE_PROJECT_NAME="${updates.COMPOSE_PROJECT_NAME}"`,
271
- );
272
- }
273
- }
274
- envContent = updatedLines.join("\n");
244
+ const envContent = `# Server and Application Configuration
245
+ DEBUG=true
246
+ PORT=${port}
247
+ HOST="localhost"
248
+ APP_URL="${appUrl}"
249
+ VERSION="1.0.0"
275
250
 
276
- fs.writeFileSync(envPath, envContent, "utf-8");
277
- console.log(chalk.green("[OK] Created and configured .env file."));
278
- }
251
+ # Docker / Swagger Config
252
+ COMPOSE_PROJECT_NAME="${composeProjectName}"
253
+ TITLE="${title}"
254
+ DESCRIPTION="Description project"
255
+
256
+ # Security Configuration
257
+ JWT_SECRET="${jwtSecret}"
258
+
259
+ # Cache Configuration
260
+ # Options: "memory" (Fast in-memory RAM), "redis" (Distributed container cache), "none" (Disabled)
261
+ CACHE_MODE="memory"
262
+
263
+ # Redis Configuration (Used when CACHE_MODE="redis")
264
+ REDIS_HOST="localhost"
265
+ REDIS_PORT=6379
266
+ REDIS_PASSWORD=""
267
+
268
+ # PM2 Clustering Instances (integer or 'max')
269
+ PM2_INSTANCES=1
270
+
271
+ ${dbEnvBlock}
272
+ `;
273
+
274
+ fs.writeFileSync(envPath, envContent, "utf-8");
275
+ console.log(chalk.green("[OK] Created and configured .env file."));
279
276
 
280
277
  this.updatePackageJson();
281
278
 
@@ -289,7 +286,7 @@ class ProjectInit {
289
286
  packagesToInstall.push("better-sqlite3");
290
287
  } else if (dbType === "postgres") {
291
288
  packagesToInstall.push("pg");
292
- } else {
289
+ } else if (dbType === "mysql") {
293
290
  packagesToInstall.push("mysql2");
294
291
  }
295
292
  execSync(`npm install ${packagesToInstall.join(" ")}`, {
@@ -426,9 +423,16 @@ function addCommand(feature) {
426
423
  "better-sqlite3": "better-sqlite3",
427
424
  mysql: "mysql2",
428
425
  mysql2: "mysql2",
426
+ mariadb: "mysql2",
427
+ maria: "mysql2",
429
428
  postgres: "pg",
430
429
  postgresql: "pg",
431
430
  pg: "pg",
431
+ psql: "pg",
432
+ pgsql: "pg",
433
+ psgr: "pg",
434
+ postgre: "pg",
435
+ postgr: "pg",
432
436
  bcrypt: "bcrypt",
433
437
  swagger: "swagger-ui-express",
434
438
  };
package/core/database.js CHANGED
@@ -3,7 +3,45 @@ import path from "node:path";
3
3
  import crypto from "node:crypto";
4
4
  import { getRedisClient } from "./cache.js";
5
5
 
6
- let DB_TYPE = (process.env.DB_TYPE || "").toLowerCase();
6
+ const rawDbType = (process.env.DB_TYPE || "").toLowerCase().trim();
7
+ let DB_TYPE = "";
8
+
9
+ if (
10
+ rawDbType === "sqlite" ||
11
+ rawDbType === "sqlite3" ||
12
+ rawDbType === "sql" ||
13
+ rawDbType === "better-sqlite3" ||
14
+ rawDbType === "better-sqlite" ||
15
+ rawDbType === "lite"
16
+ ) {
17
+ DB_TYPE = "sqlite";
18
+ } else if (
19
+ rawDbType === "postgres" ||
20
+ rawDbType === "postgresql" ||
21
+ rawDbType === "pg" ||
22
+ rawDbType === "psql" ||
23
+ rawDbType === "pgsql" ||
24
+ rawDbType === "postgre" ||
25
+ rawDbType === "postgr" ||
26
+ rawDbType === "psgr"
27
+ ) {
28
+ DB_TYPE = "postgres";
29
+ } else if (
30
+ rawDbType === "mysql" ||
31
+ rawDbType === "mariadb" ||
32
+ rawDbType === "maria" ||
33
+ rawDbType === "my"
34
+ ) {
35
+ DB_TYPE = "mysql";
36
+ } else if (
37
+ rawDbType === "none" ||
38
+ rawDbType === "no" ||
39
+ rawDbType === "false" ||
40
+ rawDbType === "null" ||
41
+ rawDbType === "0"
42
+ ) {
43
+ DB_TYPE = "none";
44
+ }
7
45
 
8
46
  if (
9
47
  !DB_TYPE &&
@@ -33,6 +33,7 @@
33
33
  <h4>Menu</h4>
34
34
  <a href="/">Home</a>
35
35
  <a href="/about" class="active badge badge-glow px-3 py-3">About</a>
36
+ <a href="https://seip25.github.io/Blue-bird" target="_blank">Blue bird</a>
36
37
  <a href="https://seip25.github.io/Blue-bird-css/" target="_blank">Blue Bird CSS</a>
37
38
  <a href="https://github.com/seip25/Blue-bird" target="_blank" role="button"
38
39
  class="btn-sm outline">GitHub</a>
@@ -33,6 +33,7 @@
33
33
  <h4>Menu</h4>
34
34
  <a href="/" class="active badge badge-glow px-3 py-3">Home</a>
35
35
  <a href="/about">About</a>
36
+ <a href="https://seip25.github.io/Blue-bird" target="_blank">Blue bird</a>
36
37
  <a href="https://seip25.github.io/Blue-bird-css/" target="_blank">Blue Bird CSS</a>
37
38
  <a href="https://github.com/seip25/Blue-bird" target="_blank" role="button"
38
39
  class="btn-sm outline">GitHub</a>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seip/blue-bird",
3
- "version": "1.3.0",
3
+ "version": "1.3.2",
4
4
  "description": "Express opinionated API framework with built-in JWT auth, validation, and caching",
5
5
  "type": "module",
6
6
  "types": "core/index.d.ts",