lapeh 2.6.17 → 3.0.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.
Files changed (67) hide show
  1. package/.env.example +1 -6
  2. package/README.md +19 -85
  3. package/bin/index.js +84 -180
  4. package/dist/lib/bootstrap.d.ts.map +1 -1
  5. package/dist/lib/bootstrap.js +17 -16
  6. package/dist/lib/core/store.d.ts +55 -0
  7. package/dist/lib/core/store.d.ts.map +1 -0
  8. package/dist/lib/core/store.js +66 -0
  9. package/dist/lib/middleware/error.d.ts.map +1 -1
  10. package/dist/lib/middleware/error.js +1 -20
  11. package/dist/lib/utils/validator.d.ts.map +1 -1
  12. package/dist/lib/utils/validator.js +3 -32
  13. package/dist/src/modules/Auth/auth.controller.d.ts.map +1 -1
  14. package/dist/src/modules/Auth/auth.controller.js +118 -105
  15. package/dist/src/modules/Rbac/rbac.controller.d.ts.map +1 -1
  16. package/dist/src/modules/Rbac/rbac.controller.js +141 -140
  17. package/dist/src/routes/index.d.ts.map +1 -1
  18. package/dist/src/routes/index.js +0 -5
  19. package/doc/en/CHEATSHEET.md +3 -7
  20. package/doc/en/CLI.md +16 -41
  21. package/doc/en/DEPLOYMENT.md +171 -245
  22. package/doc/en/GETTING_STARTED.md +1 -25
  23. package/doc/en/PACKAGES.md +2 -3
  24. package/doc/en/STRUCTURE.md +1 -11
  25. package/doc/en/TUTORIAL.md +61 -119
  26. package/doc/id/CHANGELOG.md +16 -0
  27. package/doc/id/CHEATSHEET.md +0 -4
  28. package/doc/id/CLI.md +19 -54
  29. package/doc/id/DEPLOYMENT.md +171 -245
  30. package/doc/id/GETTING_STARTED.md +91 -115
  31. package/doc/id/PACKAGES.md +0 -1
  32. package/doc/id/STRUCTURE.md +1 -11
  33. package/doc/id/TUTORIAL.md +51 -109
  34. package/gitignore.template +0 -10
  35. package/lib/bootstrap.ts +39 -38
  36. package/lib/core/store.ts +116 -0
  37. package/lib/middleware/error.ts +1 -21
  38. package/lib/utils/validator.ts +3 -39
  39. package/package.json +4 -18
  40. package/scripts/init-project.js +2 -108
  41. package/scripts/make-module.js +1 -12
  42. package/scripts/seed-json.js +158 -0
  43. package/src/modules/Auth/auth.controller.ts +156 -106
  44. package/src/modules/Rbac/rbac.controller.ts +193 -138
  45. package/src/routes/index.ts +0 -3
  46. package/src/routes/rbac.ts +42 -42
  47. package/storage/logs/.0337f5062fe676994d1dc340156e089444e3d6e0-audit.json +5 -10
  48. package/storage/logs/lapeh-2025-12-30.log +1093 -0
  49. package/tsconfig.build.json +1 -3
  50. package/tsconfig.json +0 -1
  51. package/lib/core/database.ts +0 -5
  52. package/prisma/base.prisma.template +0 -8
  53. package/prisma/migrations/20251225163737_init/migration.sql +0 -236
  54. package/prisma/migrations/20251226000329_create_pets_table/migration.sql +0 -11
  55. package/prisma/migrations/20251226001249_create_pets_table/migration.sql +0 -82
  56. package/prisma/migrations/20251226001717_restore_core_models/migration.sql +0 -236
  57. package/prisma/migrations/migration_lock.toml +0 -3
  58. package/prisma/schema.prisma +0 -197
  59. package/prisma/seed.ts +0 -411
  60. package/scripts/compile-schema.js +0 -64
  61. package/src/modules/Auth/auth.prisma +0 -106
  62. package/src/modules/Pets/pets.controller.ts +0 -238
  63. package/src/modules/Pets/pets.prisma +0 -9
  64. package/src/modules/Rbac/rbac.prisma +0 -68
  65. package/src/routes/pets.ts +0 -13
  66. package/storage/logs/lapeh-2025-12-26.log +0 -88
  67. package/storage/logs/lapeh-2025-12-27.log +0 -217
package/.env.example CHANGED
@@ -1,6 +1,4 @@
1
1
  PORT=8000
2
- DATABASE_PROVIDER="postgresql"
3
- DATABASE_URL="postgresql://sianu:12341234@localhost:5432/db_example_test?schema=public"
4
2
 
5
3
  # Used for all encryption-related tasks in the framework (JWT, etc.)
6
4
  JWT_SECRET="replace_this_with_a_secure_random_string"
@@ -11,9 +9,6 @@ TZ="Asia/Jakarta"
11
9
  # Redis Configuration (Optional)
12
10
  # If REDIS_URL is not set or connection fails, the framework will automatically
13
11
  # switch to an in-memory Redis mock (bundled). No installation required for development.
14
- # REDIS_URL="redis://lapeh:12341234@localhost:6379"
15
- # NO_REDIS="true"
16
-
17
- # To force disable Redis and use in-memory mock even if Redis is available:
12
+ # REDIS_URL="redis://localhost:6379"
18
13
  # NO_REDIS="true"
19
14
 
package/README.md CHANGED
@@ -2,15 +2,15 @@
2
2
 
3
3
  **Lapeh** adalah framework **Node.js** berbasis **Express** dan **TypeScript** yang dirancang untuk kecepatan dan skalabilitas. Menggabungkan fleksibilitas Express dengan struktur solid ala **Laravel** dan **NestJS**, Lapeh memberikan pengalaman development **REST API** yang cepat, terstandarisasi, dan siap produksi.
4
4
 
5
- Cocok untuk developer yang mencari **Express boilerplate** dengan fitur lengkap: Prisma ORM, Authentication, RBAC, dan Zero-Config Redis.
5
+ Cocok untuk developer yang mencari **Express boilerplate** dengan fitur lengkap: Authentication, dan Zero-Config Redis.
6
6
 
7
7
  ## šŸš€ Fitur Utama
8
8
 
9
9
  - **Production Ready**: Struktur folder modular (MVC) yang mudah dikembangkan.
10
10
  - **TypeScript First**: Full type-safety untuk mengurangi runtime error.
11
- - **Prisma ORM Integration**: Database modern dengan dukungan PostgreSQL dan MySQL.
11
+ - **Database Agnostic**: Bebas pilih database dan ORM (Prisma, TypeORM, Drizzle, dll).
12
12
  - **Standardized Structure**: Controller, Service, dan Route yang terpisah rapi.
13
- - **Auto CLI Generator**: Buat modul, model, dan controller dengan satu perintah.
13
+ - **Auto CLI Generator**: Buat modul dan controller dengan satu perintah.
14
14
  - **Smart Caching**: Otomatis menggunakan Redis jika tersedia, fallback ke in-memory jika tidak.
15
15
  - **Secure by Default**: Dilengkapi Helmet, Rate Limiting, CORS, dan JWT Auth.
16
16
  - **Robust Validation**: Validasi request otomatis menggunakan Zod.
@@ -57,9 +57,7 @@ Kami menyusun "Learning Path" agar Anda bisa memahami framework ini dari nol hin
57
57
 
58
58
  ## šŸ“¦ Instalasi & Penggunaan
59
59
 
60
- Anda dapat menginstall framework ini menggunakan versi terbaru atau versi spesifik agar lebih fleksibel:
61
-
62
- ### 1. Menggunakan Versi Terbaru (Recommended)
60
+ Anda dapat menginstall framework ini menggunakan versi terbaru:
63
61
 
64
62
  ```bash
65
63
  npx lapeh@latest nama-project-anda
@@ -69,30 +67,13 @@ Perintah di atas akan membuat proyek **bersih** (clean slate):
69
67
 
70
68
  - Struktur folder dibuat.
71
69
  - Dependensi diinstall.
72
- - Database dikonfigurasi & dimigrasi (hanya Schema, **tanpa data**).
73
70
  - Folder `bin` dan `lib` framework tersembunyi di `node_modules` agar root proyek Anda tetap rapi.
74
71
 
75
- ### 2. Setup Lengkap dengan Demo Data (`--full`)
76
-
77
- Jika Anda ingin mencoba fitur lengkap dengan data demo (Users, Roles, Pets), gunakan flag `--full`:
78
-
79
- ```bash
80
- npx lapeh@latest nama-project-anda --full
81
- ```
82
-
83
- Apa bedanya?
84
-
85
- - **Tanpa `--full`**: Database kosong (hanya tabel). Cocok untuk memulai proyek baru dari nol.
86
- - **Dengan `--full`**: Database otomatis di-seed dengan data User (Super Admin), Roles, Permissions, dan 50.000 data demo Pets.
87
-
88
72
  ### Apa yang terjadi otomatis?
89
73
 
90
74
  1. Struktur project dibuat (Core framework tersembunyi sebagai dependency).
91
75
  2. Dependencies diinstall.
92
- 3. Database dipilih & dikonfigurasi secara interaktif.
93
- 4. **Database** dibuat dan dimigrasi otomatis.
94
- 5. **JWT Secret** di-generate otomatis.
95
- 6. **Seeding Data** (Hanya jika menggunakan `--full`).
76
+ 3. **JWT Secret** di-generate otomatis.
96
77
 
97
78
  Masuk ke folder project dan jalankan:
98
79
 
@@ -112,18 +93,6 @@ Framework ini didesain dengan memprioritaskan keamanan:
112
93
  - **Zero-Vulnerability Policy**: Kami secara rutin melakukan audit dependensi (`npm audit`) untuk memastikan tidak ada celah keamanan.
113
94
  - **Framework-as-Dependency**: Dengan menyembunyikan core logic di `node_modules`, pembaruan framework menjadi lebih mudah (cukup update versi `lapeh` di `package.json`) tanpa merusak kode aplikasi Anda.
114
95
 
115
- ### šŸ”‘ Akun Default (Jika menggunakan `--full` atau `npm run db:seed`)
116
-
117
- Jika Anda melakukan setup dengan flag `--full`, database akan terisi dengan akun default berikut:
118
-
119
- | Role | Email | Password |
120
- | :-------------- | :---------- | :------- |
121
- | **Super Admin** | `sa@sa.com` | `string` |
122
- | **Admin** | `a@a.com` | `string` |
123
- | **User** | `u@u.com` | `string` |
124
-
125
- > **Catatan:** Segera ubah password akun-akun ini jika Anda mendeploy ke production!
126
-
127
96
  ---
128
97
 
129
98
  ## šŸ”„ Upgrade Project
@@ -202,18 +171,7 @@ Command ini akan membuat:
202
171
  - `src/services/product.service.ts`
203
172
  - `src/routes/product.route.ts` (dan otomatis didaftarkan di `src/routes/index.ts` jika memungkinkan)
204
173
 
205
- ### 2. Membuat Model Database
206
-
207
- Membuat file model Prisma baru di dalam folder `src/models/` (atau `prisma/models` tergantung konfigurasi).
208
-
209
- ```bash
210
- npm run make:model NamaModel
211
- # Contoh: npm run make:model User
212
- ```
213
-
214
- Ini akan membuat file `src/models/User.prisma`.
215
-
216
- ### 3. Membuat Controller
174
+ ### 2. Membuat Controller
217
175
 
218
176
  Membuat file Controller baru. Gunakan flag `-r` untuk membuat controller lengkap dengan method CRUD (index, show, store, update, destroy).
219
177
 
@@ -225,33 +183,21 @@ npm run make:controller NamaController
225
183
  npm run make:controller PaymentController -r
226
184
  ```
227
185
 
228
- ### 4. Workflow Database (Prisma)
186
+ ### 3. Database (No-ORM)
229
187
 
230
- Karena framework ini menggunakan **Schema Terpisah** (split schema), Anda **TIDAK BOLEH** mengedit `prisma/schema.prisma` secara manual.
188
+ Since v3.0.0, Lapeh Framework **does not include a default ORM** (like Prisma). We believe in giving you full control over your database stack.
231
189
 
232
- - **Edit Models**: Lakukan perubahan di `src/models/*.prisma`.
233
- - **Apply Changes**: Jalankan perintah migrasi standar, sistem akan otomatis menggabungkan (compile) schema Anda.
190
+ You can freely choose to use:
234
191
 
235
- ```bash
236
- # Generate Prisma Client (setiap ada perubahan model)
237
- npm run prisma:generate
238
-
239
- # Migrasi Database (Development)
240
- npm run prisma:migrate
192
+ - **Prisma** (Manual installation)
193
+ - **TypeORM**
194
+ - **Drizzle ORM**
195
+ - **Mongoose**
196
+ - **Raw SQL** (pg, mysql2)
241
197
 
242
- # Membuka GUI Database (Prisma Studio)
243
- npm run db:studio
198
+ The framework provides a `Validator` class for request validation and a `Serializer` for response formatting, but data persistence is up to you.
244
199
 
245
- # Migrasi Database Dan Seed (Development - Reset Total default option for development)
246
- npm run db:reset
247
-
248
- # Deploy ke Production
249
- npm run prisma:deploy
250
- ```
251
-
252
- > **Catatan:** Script `compile-schema.js` akan otomatis berjalan sebelum perintah prisma di atas dieksekusi.
253
-
254
- ### 5. Generate JWT Secret
200
+ ### 4. Generate JWT Secret
255
201
 
256
202
  Jika Anda perlu me-refresh secret key JWT:
257
203
 
@@ -259,7 +205,7 @@ Jika Anda perlu me-refresh secret key JWT:
259
205
  npm run generate:jwt
260
206
  ```
261
207
 
262
- ### 6. Maintenance (Clear Config)
208
+ ### 5. Maintenance (Clear Config)
263
209
 
264
210
  Membersihkan cache framework, NPM, build artifacts, dan temporary files (sangat berguna jika mengalami isu cache aneh atau ingin reset environment development).
265
211
 
@@ -281,14 +227,10 @@ src/
281
227
  ā”œā”€ā”€ controllers/ # Logika Request & Response
282
228
  ā”œā”€ā”€ services/ # Business Logic
283
229
  ā”œā”€ā”€ routes/ # Definisi Route API
284
- ā”œā”€ā”€ models/ # Definisi Schema Prisma per Model
285
230
  ā”œā”€ā”€ middleware/ # Auth, Validation, Error Handling
286
231
  ā”œā”€ā”€ schema/ # Zod Validation Schemas
287
232
  ā”œā”€ā”€ utils/ # Helper Functions
288
233
  └── index.ts # App Entry Point
289
- prisma/
290
- ā”œā”€ā”€ schema.prisma # [GENERATED] Jangan edit file ini
291
- └── base.prisma.template # Konfigurasi Datasource & Generator
292
234
  ```
293
235
 
294
236
  ## šŸ“ Lisensi
@@ -299,24 +241,17 @@ MIT
299
241
 
300
242
  ## šŸš€ Deployment Guide
301
243
 
302
- ### 1) Build & Generate Prisma Client (Otomatis)
244
+ ### 1) Build
303
245
 
304
246
  - Build: `npm run build`
305
247
  - Start (dev): `npm run start`
306
248
  - Start (prod): `npm run start:prod`
307
- - Hooks otomatis:
308
- - `prebuild`, `prestart`, dan `prestart:prod` akan memanggil `npm run prisma:generate` sehingga Prisma Client selalu tersedia tanpa error.
309
249
 
310
250
  ### 2) Production Environment
311
251
 
312
252
  - Pastikan `.env` berisi kredensial production:
313
- - `DATABASE_URL` dan `DATABASE_PROVIDER` (mysql/postgresql)
314
253
  - `JWT_SECRET` (gunakan `npm run generate:jwt` untuk mengganti)
315
- - Terapkan migrasi production (tanpa reset data):
316
-
317
- ```bash
318
- npm run prisma:deploy
319
- ```
254
+ - Database credentials (sesuai pilihan ORM/DB Anda)
320
255
 
321
256
  ### 3) Menjalankan dengan PM2
322
257
 
@@ -417,7 +352,6 @@ sudo systemctl reload apache2
417
352
 
418
353
  ### 6) Checklist Produksi
419
354
 
420
- - `npm run prisma:deploy` sukses dan tabel terbentuk
421
355
  - `pm2 status` menunjukkan proses hidup
422
356
  - Proxy (Nginx/Apache) menuju port aplikasi (default 4000)
423
357
  - `.env` aman dan tidak di-commit ke repository
package/bin/index.js CHANGED
@@ -2,9 +2,52 @@
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
- const { execSync } = require('child_process');
5
+ const { execSync, spawn } = require('child_process');
6
6
  const readline = require('readline');
7
7
 
8
+ // --- Helper Functions for Animation ---
9
+
10
+ async function spin(text, fn) {
11
+ const frames = ['ā ‹', 'ā ™', 'ā ¹', 'ā ø', 'ā ¼', 'ā “', 'ā ¦', 'ā §', 'ā ‡', 'ā '];
12
+ let i = 0;
13
+ process.stdout.write(`\x1b[?25l`); // Hide cursor
14
+
15
+ const interval = setInterval(() => {
16
+ process.stdout.write(`\r\x1b[36m${frames[i]} ${text}\x1b[0m`);
17
+ i = (i + 1) % frames.length;
18
+ }, 80);
19
+
20
+ try {
21
+ const result = await fn();
22
+ clearInterval(interval);
23
+ process.stdout.write(`\r\x1b[32māœ” ${text}\x1b[0m\n`);
24
+ return result;
25
+ } catch (e) {
26
+ clearInterval(interval);
27
+ process.stdout.write(`\r\x1b[31māœ– ${text}\x1b[0m\n`);
28
+ throw e;
29
+ } finally {
30
+ process.stdout.write(`\x1b[?25h`); // Show cursor
31
+ }
32
+ }
33
+
34
+ function runCommand(cmd, cwd) {
35
+ return new Promise((resolve, reject) => {
36
+ // Use spawn to capture output or run silently
37
+ // Using shell: true to handle cross-platform command execution
38
+ const child = spawn(cmd, { cwd, shell: true, stdio: 'pipe' });
39
+ let output = '';
40
+
41
+ child.stdout.on('data', (data) => { output += data.toString(); });
42
+ child.stderr.on('data', (data) => { output += data.toString(); });
43
+
44
+ child.on('close', (code) => {
45
+ if (code === 0) resolve(output);
46
+ else reject(new Error(`Command failed with code ${code}\n${output}`));
47
+ });
48
+ });
49
+ }
50
+
8
51
  const args = process.argv.slice(2);
9
52
  const command = args[0];
10
53
 
@@ -93,10 +136,10 @@ switch (command) {
93
136
  runDev();
94
137
  break;
95
138
  case 'start':
96
- runStart();
139
+ (async () => { await runStart(); })();
97
140
  break;
98
141
  case 'build':
99
- runBuild();
142
+ (async () => { await runBuild(); })();
100
143
  break;
101
144
  case 'upgrade':
102
145
  (async () => {
@@ -115,23 +158,6 @@ switch (command) {
115
158
  function runDev() {
116
159
  console.log('šŸš€ Starting Lapeh in development mode...');
117
160
  try {
118
- // Generate Prisma Client before starting
119
- console.log('šŸ”„ Generating Prisma Client...');
120
- const compileSchemaPath = path.join(process.cwd(), 'scripts/compile-schema.js');
121
- if (fs.existsSync(compileSchemaPath)) {
122
- try {
123
- execSync('node scripts/compile-schema.js', { stdio: 'inherit' });
124
- } catch (e) {
125
- console.warn('āš ļø Failed to run compile-schema.js', e.message);
126
- }
127
- }
128
-
129
- try {
130
- execSync('npx prisma generate', { stdio: 'inherit' });
131
- } catch (e) {
132
- console.warn('āš ļø Failed to run prisma generate. Continuing...', e.message);
133
- }
134
-
135
161
  const tsNodePath = require.resolve('ts-node/register');
136
162
  const tsConfigPathsPath = require.resolve('tsconfig-paths/register');
137
163
 
@@ -165,8 +191,10 @@ function runDev() {
165
191
  }
166
192
  }
167
193
 
168
- function runStart() {
169
- console.log('šŸš€ Starting Lapeh production server...');
194
+ async function runStart() {
195
+ await spin('Starting Lapeh production server...', async () => {
196
+ await new Promise(r => setTimeout(r, 1500)); // Simulate startup checks animation
197
+ });
170
198
 
171
199
  let bootstrapPath;
172
200
  try {
@@ -243,23 +271,6 @@ function runStart() {
243
271
  function runBuild() {
244
272
  console.log('šŸ› ļø Building Lapeh project...');
245
273
 
246
- const compileSchemaPath = path.join(process.cwd(), 'scripts/compile-schema.js');
247
- if (fs.existsSync(compileSchemaPath)) {
248
- try {
249
- execSync('node scripts/compile-schema.js', { stdio: 'inherit' });
250
- } catch (e) {
251
- console.error('āŒ Failed to compile schema.');
252
- process.exit(1);
253
- }
254
- }
255
-
256
- try {
257
- execSync('npx prisma generate', { stdio: 'inherit' });
258
- } catch (e) {
259
- console.error('āŒ Failed to generate prisma client.');
260
- process.exit(1);
261
- }
262
-
263
274
  try {
264
275
  execSync('npx tsc -p tsconfig.build.json && npx tsc-alias -p tsconfig.build.json', { stdio: 'inherit' });
265
276
  } catch (e) {
@@ -291,10 +302,7 @@ async function upgradeProject() {
291
302
  'tsconfig.json',
292
303
  'README.md',
293
304
  'ecosystem.config.js',
294
- 'src/redis.ts',
295
- 'src/prisma.ts',
296
- 'prisma/base.prisma.template', // Sync base template for upgrade
297
- 'prisma.config.ts' // Sync prisma config for upgrade
305
+ 'src/redis.ts'
298
306
  ];
299
307
 
300
308
  function syncDirectory(src, dest, clean = false) {
@@ -332,25 +340,6 @@ async function upgradeProject() {
332
340
  }
333
341
  }
334
342
 
335
- // Rename .model -> .prisma (Legacy migration)
336
- const modelsDir = path.join(currentDir, 'src', 'models');
337
- if (fs.existsSync(modelsDir)) {
338
- console.log('šŸ”„ Checking for legacy .model files...');
339
- const files = fs.readdirSync(modelsDir);
340
- let renamedCount = 0;
341
- files.forEach(file => {
342
- if (file.endsWith('.model')) {
343
- const oldPath = path.join(modelsDir, file);
344
- const newPath = path.join(modelsDir, file.replace('.model', '.prisma'));
345
- fs.renameSync(oldPath, newPath);
346
- renamedCount++;
347
- }
348
- });
349
- if (renamedCount > 0) {
350
- console.log(`āœ… Migrated ${renamedCount} files from .model to .prisma`);
351
- }
352
- }
353
-
354
343
  for (const item of filesToSync) {
355
344
  const srcPath = path.join(templateDir, item);
356
345
  const destPath = path.join(currentDir, item);
@@ -497,61 +486,31 @@ function createProject(skipFirstArg = false) {
497
486
  };
498
487
 
499
488
  (async () => {
500
- console.log(`šŸš€ Creating a new API Lapeh project in ${projectDir}...`);
501
- fs.mkdirSync(projectDir);
489
+ // Animation Lapeh "L"
490
+ const lFrames = [
491
+ "ā–ˆā–ˆā•— ",
492
+ "ā–ˆā–ˆā•‘ ",
493
+ "ā–ˆā–ˆā•‘ ",
494
+ "ā–ˆā–ˆā•‘ ",
495
+ "ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā•—",
496
+ "ā•šā•ā•ā•ā•ā•ā•ā•"
497
+ ];
502
498
 
503
- console.log("\n--- ORM Configuration ---");
504
- let usePrisma = true;
505
-
506
- if (!useDefaults) {
507
- const ormChoice = await selectOption("Apakah ingin menggunakan ORM (Prisma)?", [
508
- { key: "Y", label: "Ya (Disarankan)" },
509
- { key: "T", label: "Tidak (Setup Manual)" }
510
- ]);
511
- usePrisma = ormChoice.key === "Y";
499
+ console.clear();
500
+ console.log('\n');
501
+ for (let i = 0; i < lFrames.length; i++) {
502
+ await new Promise(r => setTimeout(r, 100));
503
+ console.log(`\x1b[36m ${lFrames[i]}\x1b[0m`);
512
504
  }
505
+ console.log('\n\x1b[36m L A P E H F R A M E W O R K\x1b[0m\n');
506
+ await new Promise(r => setTimeout(r, 800));
507
+
508
+ console.log(`šŸš€ Creating a new API Lapeh project in ${projectDir}...`);
509
+ fs.mkdirSync(projectDir);
513
510
 
514
- let dbType, host, port, user, password, dbName;
515
- let dbUrl = "";
516
- let dbProvider = "postgresql";
517
-
518
- if (usePrisma) {
519
- if (useDefaults) {
520
- console.log("ā„¹ļø Using default database configuration (PostgreSQL)...");
521
- dbType = { key: "pgsql", label: "PostgreSQL", provider: "postgresql", defaultPort: "5432" };
522
- host = "localhost";
523
- port = "5432";
524
- user = "postgres";
525
- password = "password";
526
- dbName = projectName.replace(/-/g, '_');
527
- } else {
528
- console.log("\n--- Database Configuration ---");
529
- dbType = await selectOption("Database apa yang akan digunakan?", [
530
- { key: "pgsql", label: "PostgreSQL", provider: "postgresql", defaultPort: "5432" },
531
- { key: "mysql", label: "MySQL", provider: "mysql", defaultPort: "3306" },
532
- ]);
533
-
534
- host = await ask("Database Host", "localhost");
535
- port = await ask("Database Port", dbType.defaultPort);
536
- user = await ask("Database User", "root");
537
- password = await ask("Database Password", "");
538
- dbName = await ask("Database Name", projectName.replace(/-/g, '_'));
539
- }
540
-
541
- dbProvider = dbType.provider;
542
- if (dbType.key === "pgsql") {
543
- dbUrl = `postgresql://${user}:${password}@${host}:${port}/${dbName}?schema=public`;
544
- } else if (dbType.key === "mysql") {
545
- dbUrl = `mysql://${user}:${password}@${host}:${port}/${dbName}`;
546
- }
547
- } else {
548
- console.log("ā„¹ļø Skipping ORM setup. You will need to configure your own database access.");
549
- }
550
-
551
511
  const ignoreList = [
552
512
  'node_modules', 'dist', '.git', '.env', 'bin', 'lib',
553
- 'package-lock.json', '.DS_Store', 'prisma/migrations',
554
- 'prisma/dev.db', 'prisma/dev.db-journal', 'website',
513
+ 'package-lock.json', '.DS_Store', 'prisma', 'website',
555
514
  'init', 'test-local-run', 'coverage', 'doc', projectName
556
515
  ];
557
516
 
@@ -562,7 +521,6 @@ function createProject(skipFirstArg = false) {
562
521
  const srcPath = path.join(src, entry.name);
563
522
  const destPath = path.join(dest, entry.name);
564
523
 
565
- if (entry.name === 'migrations' && srcPath.includes('prisma')) continue;
566
524
 
567
525
  if (entry.isDirectory()) {
568
526
  fs.mkdirSync(destPath);
@@ -587,17 +545,6 @@ function createProject(skipFirstArg = false) {
587
545
 
588
546
  if (fs.existsSync(envExamplePath)) {
589
547
  let envContent = fs.readFileSync(envExamplePath, 'utf8');
590
- if (usePrisma) {
591
- envContent = envContent.replace(/DATABASE_URL=".+"/g, `DATABASE_URL="${dbUrl}"`);
592
- envContent = envContent.replace(/DATABASE_URL=.+/g, `DATABASE_URL="${dbUrl}"`);
593
- envContent = envContent.replace(/DATABASE_PROVIDER=".+"/g, `DATABASE_PROVIDER="${dbProvider}"`);
594
- envContent = envContent.replace(/DATABASE_PROVIDER=.+/g, `DATABASE_PROVIDER="${dbProvider}"`);
595
- } else {
596
- envContent = envContent.replace(/DATABASE_URL=".+"/g, `DATABASE_URL=""`);
597
- envContent = envContent.replace(/DATABASE_URL=.+/g, `DATABASE_URL=""`);
598
- envContent = envContent.replace(/DATABASE_PROVIDER=".+"/g, `DATABASE_PROVIDER="none"`);
599
- envContent = envContent.replace(/DATABASE_PROVIDER=.+/g, `DATABASE_PROVIDER="none"`);
600
- }
601
548
  fs.writeFileSync(envPath, envContent);
602
549
  }
603
550
 
@@ -614,14 +561,7 @@ function createProject(skipFirstArg = false) {
614
561
  packageJson.dependencies["lapeh"] = `file:${lapehPath}`;
615
562
  }
616
563
 
617
- // Ensure @prisma/client is in dependencies for the new project
618
- if (usePrisma) {
619
- packageJson.dependencies["@prisma/client"] = "^6.0.0";
620
- packageJson.prisma = {
621
- seed: "npx ts-node -r tsconfig-paths/register prisma/seed.ts"
622
- };
623
- }
624
-
564
+
625
565
  packageJson.version = '1.0.0';
626
566
  delete packageJson.bin;
627
567
  delete packageJson.peerDependencies;
@@ -654,64 +594,28 @@ function createProject(skipFirstArg = false) {
654
594
  }
655
595
 
656
596
  const prismaBaseFile = path.join(projectDir, "prisma", "base.prisma.template");
657
- if (usePrisma && fs.existsSync(prismaBaseFile)) {
658
- let baseContent = fs.readFileSync(prismaBaseFile, "utf8");
659
- // Update provider
660
- baseContent = baseContent.replace(
661
- /(datasource\s+db\s+\{[\s\S]*?provider\s*=\s*")[^"]+(")/,
662
- `$1${dbProvider}$2`
663
- );
664
- fs.writeFileSync(prismaBaseFile, baseContent);
665
- }
597
+ // Removed Prisma base file handling
666
598
 
667
- console.log('šŸ“¦ Installing dependencies...');
668
599
  try {
669
- execSync('npm install', { cwd: projectDir, stdio: 'inherit' });
600
+ await spin('Installing dependencies...', async () => {
601
+ await runCommand('npm install', projectDir);
602
+ });
670
603
  } catch (e) {
671
604
  console.error('āŒ Error installing dependencies.');
605
+ console.error(e.message);
672
606
  process.exit(1);
673
607
  }
674
608
 
675
609
  try {
676
- execSync('npm run generate:jwt', { cwd: projectDir, stdio: 'inherit' });
610
+ // Also silence the JWT generation output or animate it if needed, but for now just silence/pipe
611
+ // Or keep inherit if user wants to see the key.
612
+ // The original code used 'inherit', and printed "āœ… JWT Secret generated..."
613
+ // Let's keep it simple or use runCommand to just do it silently.
614
+ await runCommand('npm run generate:jwt', projectDir);
615
+ console.log('āœ… JWT Secret generated.');
677
616
  } catch (e) {}
678
617
 
679
- if (usePrisma) {
680
- console.log('šŸ—„ļø Setting up database...');
681
- try {
682
- execSync('node scripts/compile-schema.js', { cwd: projectDir, stdio: 'inherit' });
683
-
684
- console.log(' Running migration...');
685
- if (dbProvider === 'mongodb') {
686
- execSync('npx prisma db push', { cwd: projectDir, stdio: 'inherit' });
687
- } else {
688
- // For Prisma v7, ensure prisma.config.ts is used/detected
689
- execSync('npx prisma migrate dev --name init_setup', { cwd: projectDir, stdio: 'inherit' });
690
- }
691
-
692
- // Explicitly generate Prisma Client to ensure .prisma/client/default exists
693
- console.log(' Generating Prisma Client...');
694
- execSync('npx prisma generate', { cwd: projectDir, stdio: 'inherit' });
695
-
696
- let runSeed = false;
697
- if (!useDefaults) {
698
- const seedChoice = await selectOption("Jalankan seeder?", [
699
- { key: "Y", label: "Ya" },
700
- { key: "T", label: "Tidak" }
701
- ]);
702
- runSeed = seedChoice.key === "Y";
703
- } else {
704
- runSeed = isFull;
705
- }
706
-
707
- if (runSeed) {
708
- console.log(' Seeding database...');
709
- execSync('npm run db:seed', { cwd: projectDir, stdio: 'inherit' });
710
- }
711
- } catch (e) {
712
- console.warn('āš ļø Database setup failed. Check .env and run manually.');
713
- }
714
- }
618
+ // Removed Prisma setup steps
715
619
 
716
620
  console.log(`\nāœ… Project ${projectName} created successfully!`);
717
621
  rl.close();
@@ -1 +1 @@
1
- {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../lib/bootstrap.ts"],"names":[],"mappings":"AAmBA,wBAAsB,SAAS,yDA8H9B;AAED,wBAAsB,SAAS,kBAwD9B"}
1
+ {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../lib/bootstrap.ts"],"names":[],"mappings":"AAkBA,wBAAsB,SAAS,yDAiI9B;AAED,wBAAsB,SAAS,kBAuD9B"}
@@ -16,7 +16,6 @@ const http_1 = __importDefault(require("http"));
16
16
  const path_1 = __importDefault(require("path"));
17
17
  const realtime_1 = require("./core/realtime");
18
18
  const redis_1 = require("./core/redis");
19
- const database_1 = require("./core/database");
20
19
  const visitor_1 = require("./middleware/visitor");
21
20
  const error_1 = require("./middleware/error");
22
21
  const rateLimit_1 = require("./middleware/rateLimit");
@@ -27,8 +26,12 @@ async function createApp() {
27
26
  // Since user code (compiled JS) uses require('@lapeh/...')
28
27
  // We map '@lapeh' to the directory containing this file (lib/ or dist/lib/)
29
28
  module_alias_1.default.addAlias("@lapeh", __dirname);
30
- // LOAD USER CONFIG
29
+ // Register alias for src directory (@/) to support imports in controllers/routes
31
30
  const isProduction = process.env.NODE_ENV === "production";
31
+ module_alias_1.default.addAlias("@", isProduction
32
+ ? path_1.default.join(process.cwd(), "dist", "src")
33
+ : path_1.default.join(process.cwd(), "src"));
34
+ // LOAD USER CONFIG
32
35
  const configPath = isProduction
33
36
  ? path_1.default.join(process.cwd(), "dist", "src", "config")
34
37
  : path_1.default.join(process.cwd(), "src", "config");
@@ -88,26 +91,22 @@ async function createApp() {
88
91
  });
89
92
  // DYNAMIC ROUTE LOADING
90
93
  try {
94
+ console.log("BOOTSTRAP: Loading routes. NODE_ENV=", process.env.NODE_ENV);
91
95
  const isProduction = process.env.NODE_ENV === "production";
92
- const userRoutesPath = isProduction
96
+ let userRoutesPath = isProduction
93
97
  ? path_1.default.join(process.cwd(), "dist", "src", "routes")
94
98
  : path_1.default.join(process.cwd(), "src", "routes");
99
+ // In test environment, explicitly point to index to ensure resolution
100
+ if (process.env.NODE_ENV === "test") {
101
+ // In test environment (ts-jest), we need to point to the TS file
102
+ // And we might need to use the full path with extension
103
+ userRoutesPath = path_1.default.join(process.cwd(), "src", "routes", "index.ts");
104
+ }
95
105
  // Gunakan require agar sinkron dan mudah dicatch
96
106
  // Check if file exists before requiring to avoid crash in tests/clean env
97
107
  try {
98
- // In test environment, we might need to point to src/routes explicitly if not compiled
99
- // const routesPath = process.env.NODE_ENV === 'test'
100
- // ? path.join(process.cwd(), "src", "routes", "index.ts")
101
- // : userRoutesPath;
102
- // Note: For TS files in jest, we rely on ts-jest handling 'require' if it points to .ts or we need to use 'import'
103
- // But 'require' in jest with ts-jest should work if configured.
104
- // However, require(path) with .ts extension might be tricky.
105
- // Let's stick to userRoutesPath but maybe adjust for test env.
106
- // Check if we are in test environment and using ts-jest
107
- // If so, we might need to import the TS file directly via relative path if alias is not working for require
108
108
  const { apiRouter } = require(userRoutesPath);
109
109
  app.use("/api", apiRouter);
110
- console.log(`āœ… User routes loaded successfully from ${isProduction ? "dist/" : ""}src/routes`);
111
110
  }
112
111
  catch (e) {
113
112
  // If it's just missing module, maybe we are in test mode or fresh install
@@ -117,18 +116,21 @@ async function createApp() {
117
116
  else {
118
117
  // In test mode, we really want to know if it failed to load
119
118
  console.error(`Error loading routes in test mode from ${userRoutesPath}:`, e);
119
+ throw e;
120
120
  }
121
121
  }
122
122
  }
123
123
  catch (error) {
124
124
  console.error(error);
125
+ if (process.env.NODE_ENV === "test")
126
+ throw error;
125
127
  }
126
128
  app.use(error_1.errorHandler);
127
129
  return app;
128
130
  }
129
131
  async function bootstrap() {
130
132
  // Validasi Environment Variables
131
- const requiredEnvs = ["DATABASE_URL", "JWT_SECRET"];
133
+ const requiredEnvs = ["JWT_SECRET"];
132
134
  const missingEnvs = requiredEnvs.filter((key) => !process.env[key]);
133
135
  if (missingEnvs.length > 0) {
134
136
  console.error(`āŒ Missing required environment variables: ${missingEnvs.join(", ")}`);
@@ -160,7 +162,6 @@ async function bootstrap() {
160
162
  console.log(`\nšŸ›‘ ${signal} received. Closing resources...`);
161
163
  server.close(() => console.log("Http server closed."));
162
164
  try {
163
- await database_1.prisma.$disconnect();
164
165
  if (redis_1.redis && redis_1.redis.status === "ready")
165
166
  await redis_1.redis.quit();
166
167
  process.exit(0);