crypt-express-app 1.0.0 β†’ 1.3.0

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/README.md CHANGED
@@ -1 +1,321 @@
1
- # crypt-express-app
1
+ # crypt-express-app
2
+
3
+ πŸš€ A production-ready Express + TypeScript backend starter CLI with Prisma, Redis, i18n, Swagger, Docker, and scalable modular architecture.
4
+
5
+ ---
6
+
7
+ ## ✨ What is crypt-express-app?
8
+
9
+ `crypt-express-app` is a CLI tool that instantly scaffolds a fully structured, enterprise-grade Express backend application.
10
+
11
+ It generates:
12
+
13
+ - Express + TypeScript setup
14
+ - Prisma ORM configuration
15
+ - PostgreSQL + Redis Docker setup
16
+ - Swagger documentation
17
+ - i18next internationalization
18
+ - Modular folder architecture
19
+ - Logger + Error handling middleware
20
+ - Environment configuration
21
+ - Ready-to-run Docker Compose stack
22
+
23
+ This tool is built for developers who want a clean, scalable backend architecture in seconds.
24
+
25
+ ---
26
+
27
+ # πŸ“¦ Installation
28
+
29
+ You don’t install it globally.
30
+
31
+ Use directly with npx:
32
+
33
+ ```bash
34
+ npx crypt-express-app my-project
35
+ ```
36
+
37
+ Or inside current directory:
38
+
39
+ ```bash
40
+ npx crypt-express-app .
41
+ ```
42
+
43
+ ---
44
+
45
+ # 🧠 What It Generates
46
+
47
+ After running the CLI, your project will include:
48
+
49
+ ```
50
+ my-project/
51
+ β”‚
52
+ β”œβ”€β”€ src/
53
+ β”‚ β”œβ”€β”€ app.ts
54
+ β”‚ β”œβ”€β”€ routes.ts
55
+ β”‚ β”œβ”€β”€ prisma/
56
+ β”‚ β”‚ └── client.ts
57
+ β”‚ β”œβ”€β”€ utils/
58
+ β”‚ β”œβ”€β”€ middlewares/
59
+ β”‚ β”œβ”€β”€ locales/
60
+ β”‚ └── modules/
61
+ β”‚
62
+ β”œβ”€β”€ prisma/
63
+ β”‚ └── schema.prisma
64
+ β”œβ”€β”€ server.ts
65
+ β”œβ”€β”€ .env
66
+ β”œβ”€β”€ .env.dev
67
+ β”œβ”€β”€ .env.prod
68
+ β”œβ”€β”€ example.env
69
+ β”œβ”€β”€ Dockerfile
70
+ β”œβ”€β”€ docker-compose.yml
71
+ β”œβ”€β”€ tsconfig.json
72
+ └── package.json
73
+ ```
74
+
75
+ ---
76
+
77
+ # πŸ”₯ Features
78
+
79
+ ## βœ… Express + TypeScript
80
+ - Clean app architecture
81
+ - Modular routing system
82
+ - Centralized error handling
83
+ - Logger middleware
84
+
85
+ ## βœ… Prisma ORM
86
+ - PostgreSQL configuration
87
+ - Auto Prisma client setup
88
+ - prisma.config.ts support
89
+
90
+ ## βœ… Redis Cache
91
+ - Redis service container
92
+ - Cache service wrapper
93
+ - TTL-based caching support
94
+
95
+ ## βœ… Internationalization (i18n)
96
+ - English + Bengali support
97
+ - Language detection
98
+ - JSON-based translations
99
+
100
+ ## βœ… Swagger API Docs
101
+ - OpenAPI 3.0 setup
102
+ - Bearer auth support
103
+ - Persist authorization enabled
104
+
105
+ Access at:
106
+
107
+ ```
108
+ http://localhost:3000/api/docs
109
+ ```
110
+
111
+ ## βœ… Docker Ready
112
+ - Node service container
113
+ - PostgreSQL container
114
+ - Redis container
115
+ - Volume persistence
116
+ - Production-ready configuration
117
+
118
+ ---
119
+
120
+ # πŸš€ Quick Start
121
+
122
+ After project generation:
123
+
124
+ ```bash
125
+ cd my-project
126
+ ```
127
+
128
+ Start infrastructure:
129
+
130
+ ```bash
131
+ docker compose up -d postgres redis
132
+ ```
133
+
134
+ Generate Prisma client:
135
+
136
+ ```bash
137
+ npx prisma generate
138
+ ```
139
+
140
+ Run development server:
141
+
142
+ ```bash
143
+ pnpm dev
144
+ ```
145
+
146
+ Or build and run:
147
+
148
+ ```bash
149
+ pnpm build
150
+ pnpm start
151
+ ```
152
+
153
+ ---
154
+
155
+ # 🌍 Environment Variables
156
+
157
+ Default `.env`:
158
+
159
+ ```
160
+ PORT=3000
161
+ DATABASE_URL=postgresql://postgres:postgres@localhost:5432/app_db
162
+ REDIS_URL=redis://localhost:6379
163
+ ```
164
+
165
+ For Docker (`.env.dev`):
166
+
167
+ ```
168
+ DATABASE_URL=postgresql://postgres:postgres@postgres:5432/app_db
169
+ REDIS_URL=redis://redis:6379
170
+ ```
171
+
172
+ ---
173
+
174
+ # 🐳 Docker Usage
175
+
176
+ Start everything:
177
+
178
+ ```bash
179
+ docker compose up --build
180
+ ```
181
+
182
+ Only database + redis:
183
+
184
+ ```bash
185
+ docker compose up -d postgres redis
186
+ ```
187
+
188
+ Stop:
189
+
190
+ ```bash
191
+ docker compose down
192
+ ```
193
+
194
+ ---
195
+
196
+ # 🧩 Architecture Philosophy
197
+
198
+ crypt-express-app follows a clean separation:
199
+
200
+ - `app.ts` β†’ Express configuration
201
+ - `routes.ts` β†’ Route registration
202
+ - `modules/` β†’ Feature modules
203
+ - `utils/` β†’ Shared services
204
+ - `middlewares/` β†’ Cross-cutting concerns
205
+ - `prisma/` β†’ Database client layer
206
+
207
+ This makes scaling large backend systems easier.
208
+
209
+ ---
210
+
211
+ # πŸ“Œ CLI Behavior
212
+
213
+ If project name contains spaces:
214
+
215
+ ```
216
+ "My App Project"
217
+ ```
218
+
219
+ It auto converts to:
220
+
221
+ ```
222
+ my-app-project
223
+ ```
224
+
225
+ ---
226
+
227
+ # πŸ›  Scripts
228
+
229
+ Generated project includes:
230
+
231
+ ```json
232
+ "dev": "nodemon",
233
+ "build": "tsc && npm run copy-locales",
234
+ "start": "node dist/server.js"
235
+ ```
236
+
237
+ ---
238
+
239
+ # πŸ“š Requirements
240
+
241
+ - Node.js 18+
242
+ - pnpm or npm
243
+ - Docker (optional but recommended)
244
+ - PostgreSQL
245
+ - Redis
246
+
247
+ ---
248
+
249
+ # πŸ” Production Ready
250
+
251
+ This starter includes:
252
+
253
+ - Structured logging
254
+ - Error middleware
255
+ - Swagger security
256
+ - Environment separation
257
+ - Docker orchestration
258
+ - Prisma adapter configuration
259
+
260
+ You can safely use this as a base for:
261
+
262
+ - SaaS backend
263
+ - Microservices
264
+ - Enterprise REST APIs
265
+ - Auth servers
266
+ - Admin panels
267
+
268
+ ---
269
+
270
+ # 🀝 Contributing
271
+
272
+ Pull requests are welcome.
273
+
274
+ 1. Fork repository
275
+ 2. Create feature branch
276
+ 3. Submit PR
277
+
278
+ ---
279
+
280
+ # πŸ“„ License
281
+
282
+ ISC
283
+
284
+ ---
285
+
286
+ # πŸ‘¨β€πŸ’» Author
287
+
288
+ Abir Hosen
289
+
290
+ ---
291
+
292
+ # ⭐ Why crypt-express-app?
293
+
294
+ Because setting up backend boilerplate repeatedly is painful.
295
+
296
+ crypt-express-app removes:
297
+
298
+ - Folder setup time
299
+ - Docker setup time
300
+ - Prisma configuration time
301
+ - Redis setup time
302
+ - Swagger configuration time
303
+ - i18n wiring time
304
+
305
+ Start coding business logic immediately.
306
+
307
+ ---
308
+
309
+ # πŸš€ Future Roadmap
310
+
311
+ - Auth module generator
312
+ - Role-based access control
313
+ - GraphQL support
314
+ - Microservice mode
315
+ - CLI flags support
316
+ - Health check system
317
+ - CI/CD template
318
+
319
+ ---
320
+
321
+ Enjoy building scalable backends πŸš€
@@ -1,75 +1,43 @@
1
1
  // generate-module.ts
2
2
  import * as fs from 'fs';
3
3
  import * as path from 'path';
4
-
5
4
  // get module name from command line
6
5
  const args = process.argv.slice(2);
7
-
8
6
  // Check that exactly **one argument** is provided
9
7
  if (args.length !== 1) {
10
- console.error('Invalid module name: provide exactly one module name without spaces.');
11
- console.error('Example: npm run generate-module clientRole');
12
- process.exit(1);
8
+ console.error('Invalid module name: provide exactly one module name without spaces.');
9
+ console.error('Example: npm run generate-module clientRole');
10
+ process.exit(1);
13
11
  }
14
-
15
12
  const moduleInput = args[0];
16
-
17
13
  // Reject if input contains spaces
18
14
  if (/\s/.test(moduleInput)) {
19
- console.error('Invalid module name: spaces are not allowed. Use camelCase, kebab-case or snake_case.');
20
- process.exit(1);
15
+ console.error('Invalid module name: spaces are not allowed. Use camelCase, kebab-case or snake_case.');
16
+ process.exit(1);
21
17
  }
22
-
23
18
  if (!moduleInput) {
24
- console.error('Please provide a module name: npm run generate-module <module-name>');
25
- process.exit(1);
19
+ console.error('Please provide a module name: npm run generate-module <module-name>');
20
+ process.exit(1);
26
21
  }
27
-
28
22
  // Convert any input to camelCase
29
23
  const moduleName = moduleInput.includes('-') || moduleInput.includes('_') || moduleInput.includes(' ')
30
- ? moduleInput
31
- .toLowerCase()
32
- .replace(/[-_\s]+(.)/g, (_, c) => c.toUpperCase())
33
- : moduleInput; // preserve existing camelCase
34
-
35
- console.log("moduleName: ", moduleName)
36
-
24
+ ? moduleInput
25
+ .toLowerCase()
26
+ .replace(/[-_\s]+(.)/g, (_, c) => c.toUpperCase())
27
+ : moduleInput; // preserve existing camelCase
28
+ console.log("moduleName: ", moduleName);
37
29
  // Convert any input (camelCase, PascalCase, snake_case, spaces) to kebab-case
38
30
  const fileName = moduleName
39
- .replace(/([a-z])([A-Z])/g, '$1-$2') // add dash before uppercase letters
40
- .toLowerCase(); // lowercase everything
41
-
42
- console.log("fileName: ", fileName)
43
-
44
-
45
- // const basePath = path.join(__dirname, '..', 'src', 'modules', fileName);
46
- // const basePath = path.join(__dirname, '..', 'src', 'modules', fileName);
47
- const projectRoot = process.cwd();
48
- const modulesRoot = path.join(projectRoot, "src", "modules");
49
- const basePath = path.join(modulesRoot, fileName);
50
-
51
- // ensure modules directory exists
52
- if (!fs.existsSync(modulesRoot)) {
53
- fs.mkdirSync(modulesRoot, { recursive: true });
54
- }
55
-
56
- // prevent overwrite
57
- if (fs.existsSync(basePath)) {
58
- console.error(`❌ Module '${fileName}' already exists.`);
59
- process.exit(1);
60
- }
61
-
62
- fs.mkdirSync(basePath, { recursive: true });
63
-
64
-
31
+ .replace(/([a-z])([A-Z])/g, '$1-$2') // add dash before uppercase letters
32
+ .toLowerCase(); // lowercase everything
33
+ console.log("fileName: ", fileName);
34
+ const basePath = path.join(__dirname, '..', 'src', 'modules', fileName);
65
35
  // Create folder
66
36
  if (!fs.existsSync(basePath)) {
67
- fs.mkdirSync(basePath, { recursive: true });
37
+ fs.mkdirSync(basePath, { recursive: true });
68
38
  }
69
-
70
39
  // Capitalize first letter
71
40
  const ModuleClassName = moduleName.charAt(0).toUpperCase() + moduleName.slice(1);
72
-
73
41
  // Service
74
42
  const serviceContent = `import { Create${ModuleClassName}Dto, Update${ModuleClassName}Dto, Create${ModuleClassName}Response, Update${ModuleClassName}Response, Get${ModuleClassName}Response, List${ModuleClassName}Response } from "./${fileName}.dto";
75
43
  import { Prisma } from "@prisma/client";
@@ -158,9 +126,7 @@ export class ${ModuleClassName}Service {
158
126
 
159
127
  export default new ${ModuleClassName}Service()
160
128
  `;
161
-
162
129
  fs.writeFileSync(path.join(basePath, `${fileName}.service.ts`), serviceContent);
163
-
164
130
  // Controller
165
131
  const controllerContent = `
166
132
  import { Request, Response } from 'express';
@@ -277,9 +243,7 @@ export class ${ModuleClassName}Controller {
277
243
  }
278
244
  }
279
245
  `;
280
-
281
246
  fs.writeFileSync(path.join(basePath, `${fileName}.controller.ts`), controllerContent);
282
-
283
247
  // Routes
284
248
  const routesContent = `
285
249
  import express, { Router } from 'express';
@@ -287,13 +251,13 @@ import { ${ModuleClassName}Controller } from './${fileName}.controller';
287
251
  import { ${moduleName}Middleware } from './${fileName}.middlewares';
288
252
  import { authMiddleware } from '../../middlewares/auth.middleware';
289
253
  import ${moduleName}Service from './${fileName}.service';
290
- import { ActionsType, Resource, ResourceType } from '../../utils/consts';
254
+ import { TypeAction, Resource, TypeResource } from '../../utils/consts';
291
255
  import { authorizationMiddleware } from '../../middlewares/authorization.middleware';
292
256
 
293
257
  const router: Router = express.Router();
294
258
  const controller = new ${ModuleClassName}Controller(${moduleName}Service);
295
259
  router.use(authMiddleware);
296
- const guard = (actions: ActionsType[]) => authorizationMiddleware({ resource: Resource.COMMON, actions, resourceType: ResourceType.API_ENDPOINT });
260
+ const guard = (actions: TypeAction[]) => authorizationMiddleware({ resource: Resource.COMMON, actions, resourceType: TypeResource.API_ENDPOINT });
297
261
 
298
262
  /**
299
263
  * @openapi
@@ -322,7 +286,7 @@ const guard = (actions: ActionsType[]) => authorizationMiddleware({ resource: Re
322
286
  * schema:
323
287
  * $ref: '#/components/schemas/Create${ModuleClassName}ResponseDto'
324
288
  */
325
- router.post('/', ${moduleName}Middleware, guard([ActionsType.CREATE]), controller.create.bind(controller));
289
+ router.post('/', ${moduleName}Middleware, guard([TypeAction.CREATE]), controller.create.bind(controller));
326
290
 
327
291
  /**
328
292
  * @openapi
@@ -344,7 +308,7 @@ router.post('/', ${moduleName}Middleware, guard([ActionsType.CREATE]), controlle
344
308
  * schema:
345
309
  * $ref: '#/components/schemas/Get${ModuleClassName}ResponseDto'
346
310
  */
347
- router.get('/:${moduleName}Id', ${moduleName}Middleware, guard([ActionsType.READ]), controller.getById.bind(controller));
311
+ router.get('/:${moduleName}Id', ${moduleName}Middleware, guard([TypeAction.READ]), controller.getById.bind(controller));
348
312
 
349
313
  /**
350
314
  * @openapi
@@ -372,7 +336,7 @@ router.get('/:${moduleName}Id', ${moduleName}Middleware, guard([ActionsType.READ
372
336
  * schema:
373
337
  * $ref: '#/components/schemas/Update${ModuleClassName}ResponseDto'
374
338
  */
375
- router.put('/:${moduleName}Id', ${moduleName}Middleware, guard([ActionsType.UPDATE]), controller.update.bind(controller));
339
+ router.put('/:${moduleName}Id', ${moduleName}Middleware, guard([TypeAction.UPDATE]), controller.update.bind(controller));
376
340
 
377
341
  /**
378
342
  * @openapi
@@ -401,7 +365,7 @@ router.put('/:${moduleName}Id', ${moduleName}Middleware, guard([ActionsType.UPDA
401
365
  * message:
402
366
  * type: string
403
367
  */
404
- router.delete('/:${moduleName}Id', ${moduleName}Middleware, guard([ActionsType.DELETE]), controller.delete.bind(controller));
368
+ router.delete('/:${moduleName}Id', ${moduleName}Middleware, guard([TypeAction.DELETE]), controller.delete.bind(controller));
405
369
 
406
370
  /**
407
371
  * @openapi
@@ -444,7 +408,7 @@ router.delete('/:${moduleName}Id', ${moduleName}Middleware, guard([ActionsType.D
444
408
  * schema:
445
409
  * $ref: '#/components/schemas/List${ModuleClassName}ResponseDto'
446
410
  */
447
- router.get('/', ${moduleName}Middleware, guard([ActionsType.READ_ALL]), controller.list.bind(controller));
411
+ router.get('/', ${moduleName}Middleware, guard([TypeAction.READ_ALL]), controller.list.bind(controller));
448
412
 
449
413
  /**
450
414
  * ======================================================
@@ -461,8 +425,6 @@ router.get('/health/check', (_req, res) => {
461
425
 
462
426
  export default router;
463
427
  `;
464
-
465
-
466
428
  fs.writeFileSync(path.join(basePath, `${fileName}.routes.ts`), routesContent);
467
429
  const dtoContent = `import { ApiResponse, PaginatedData } from "../common/common.dto";
468
430
 
@@ -639,10 +601,7 @@ export interface Get${ModuleClassName}ResponseDto
639
601
  export interface List${ModuleClassName}ResponseDto
640
602
  extends ApiResponse<PaginatedData<Partial<${ModuleClassName}>>> {}
641
603
  `;
642
-
643
-
644
604
  fs.writeFileSync(path.join(basePath, `${fileName}.dto.ts`), dtoContent);
645
-
646
605
  // Middleware
647
606
  const middlewareContent = `
648
607
  import { Request, Response, NextFunction } from "express";
@@ -695,21 +654,5 @@ export async function ${moduleName}Middleware(req: Request, res: Response, next:
695
654
  }
696
655
  }
697
656
  `;
698
-
699
657
  fs.writeFileSync(path.join(basePath, `${fileName}.middlewares.ts`), middlewareContent);
700
-
701
- const routerIndexPath = path.join(projectRoot, "src", "routes.ts");
702
-
703
- if (fs.existsSync(routerIndexPath)) {
704
- fs.appendFileSync(
705
- routerIndexPath,
706
- `\nimport ${moduleName}Routes from "./modules/${fileName}/${fileName}.routes";`
707
- );
708
-
709
- fs.appendFileSync(
710
- routerIndexPath,
711
- `\nrouter.use("/${moduleName}", ${moduleName}Routes);\n`
712
- );
713
- }
714
-
715
658
  console.log(`Module '${moduleName}' generated successfully at ${basePath}`);
package/dist/index.js CHANGED
@@ -1,20 +1,125 @@
1
1
  #!/usr/bin/env node
2
2
  import { execSync } from "child_process";
3
- const projectName = process.argv[2];
4
- if (!projectName) {
5
- console.error("❌ project-name required");
6
- console.log("Usage: npx crypt-express-app <project-name>");
7
- process.exit(1);
3
+ import * as fs from "fs";
4
+ import * as path from "path";
5
+ import * as readline from "readline";
6
+ import { generateApp } from "./scripts/generate-app.js";
7
+ import { generateLocales } from "./scripts/generate-locales.js";
8
+ import { generateMiddlewares } from "./scripts/generate-middleware.js";
9
+ import { generateRootFiles } from "./scripts/generate-root-files.js";
10
+ import { generateModule } from "./scripts/generate-module.js";
11
+ import { generateUtils } from "./scripts/generate-utils.js";
12
+ import { generatePrismaClient } from "./scripts/setup-prisma.js";
13
+ /* ===================== CLI SETUP ===================== */
14
+ const rl = readline.createInterface({
15
+ input: process.stdin,
16
+ output: process.stdout,
17
+ });
18
+ function ask(question) {
19
+ return new Promise((resolve) => rl.question(question, resolve));
8
20
  }
9
- process.env.PROJECT_NAME = projectName;
10
- function run(cmd) {
11
- execSync(cmd, { stdio: "inherit" });
12
- }
13
- // run generators
14
- // run("tsx scripts/generate-root-files.ts");
15
- // run("tsx scripts/generate-utils.ts");
16
- // run("tsx scripts/generate-locales.ts");
17
- // run("tsx scripts/setup-prisma.ts");
18
- // run("tsx scripts/generate-middleware.ts");
19
- // run("tsx scripts/generate-module.ts common");
20
- console.log("βœ… project generated");
21
+ /* ===================== MAIN ===================== */
22
+ (async function main() {
23
+ let projectName = process.argv[2];
24
+ if (process.argv.length > 3) {
25
+ console.error("❌ only one project name allowed");
26
+ process.exit(1);
27
+ }
28
+ if (!projectName) {
29
+ projectName = await ask("Enter project name: ");
30
+ }
31
+ projectName = projectName.trim();
32
+ if (!projectName) {
33
+ console.error("❌ Project name required");
34
+ process.exit(1);
35
+ }
36
+ // allow spaces β†’ convert to kebab-case
37
+ const normalizedName = projectName
38
+ .toLowerCase()
39
+ .trim()
40
+ .replace(/\s+/g, "-") // space β†’ dash
41
+ .replace(/[^a-z0-9-]/g, ""); // remove invalid chars (optional)
42
+ const useCurrentDir = projectName === ".";
43
+ const projectDir = useCurrentDir
44
+ ? process.cwd()
45
+ : path.join(process.cwd(), projectName);
46
+ if (!useCurrentDir) {
47
+ if (/\s/.test(projectName)) {
48
+ console.error("❌ Name cannot contain spaces");
49
+ process.exit(1);
50
+ }
51
+ if (fs.existsSync(projectDir)) {
52
+ console.error("❌ Directory already exists");
53
+ process.exit(1);
54
+ }
55
+ fs.mkdirSync(projectDir, { recursive: true });
56
+ }
57
+ else {
58
+ const files = fs.readdirSync(projectDir).filter((f) => !f.startsWith("."));
59
+ if (files.length > 0) {
60
+ console.error("❌ Current directory must be empty");
61
+ process.exit(1);
62
+ }
63
+ }
64
+ rl.close();
65
+ const finalProjectName = useCurrentDir ? path.basename(process.cwd())
66
+ .toLowerCase()
67
+ .trim()
68
+ .replace(/\s+/g, "-") // space β†’ dash
69
+ .replace(/[^a-z0-9-]/g, "") // remove invalid chars (optional)
70
+ : normalizedName;
71
+ process.env.PROJECT_NAME = finalProjectName;
72
+ console.log(`πŸ“¦ Creating project: ${process.env.PROJECT_NAME}`);
73
+ generateRootFiles(projectDir, finalProjectName);
74
+ generateApp(projectDir);
75
+ generateMiddlewares(projectDir);
76
+ generateLocales(projectDir);
77
+ generateModule(projectDir, "common");
78
+ generateUtils(projectDir);
79
+ generatePrismaClient(projectDir);
80
+ console.log("πŸ“¦ Installing dependencies...");
81
+ try {
82
+ execSync("pnpm install", {
83
+ cwd: projectDir,
84
+ stdio: "inherit",
85
+ });
86
+ console.log("βœ… Dependencies installed");
87
+ }
88
+ catch (err) {
89
+ console.error("❌ pnpm install failed");
90
+ console.error(err);
91
+ }
92
+ try {
93
+ execSync("npx prisma generate", {
94
+ cwd: projectDir,
95
+ stdio: "inherit",
96
+ });
97
+ console.log("βœ… Prisma client generated");
98
+ }
99
+ catch (err) {
100
+ console.error("❌ Prisma client generation failed");
101
+ console.error(err);
102
+ }
103
+ // try {
104
+ // execSync("docker compose up -d redis postgres", {
105
+ // cwd: projectDir,
106
+ // stdio: "inherit",
107
+ // });
108
+ // console.log("βœ… Redis and Postgres started");
109
+ // } catch (err) {
110
+ // console.error("❌ Failed to start Redis and Postgres");
111
+ // console.error(err);
112
+ // }
113
+ // try {
114
+ // execSync("npx prisma db push", {
115
+ // cwd: projectDir,
116
+ // stdio: "inherit",
117
+ // });
118
+ // console.log("βœ… Prisma client generated");
119
+ // } catch (err) {
120
+ // console.error("❌ Prisma client generation failed");
121
+ // console.error(err);
122
+ // }
123
+ console.log("βœ… Project generated");
124
+ })();
125
+ /* ===================== CONFIG GENERATION ===================== */