lapeh 3.0.8 → 3.0.10

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
@@ -84,7 +84,7 @@ npm run dev
84
84
 
85
85
  > **Catatan**: Perintah `npm run dev` sekarang menggunakan CLI internal framework (`lapeh dev`), memberikan pengalaman development yang lebih stabil dan terstandarisasi. Core framework (`bin` dan `lib`) tidak lagi memenuhi root folder Anda, tetapi tersimpan aman sebagai dependency.
86
86
 
87
- Server akan berjalan di `http://localhost:4000`.
87
+ Server akan berjalan di `http://localhost:8000`.
88
88
 
89
89
  ### šŸ›”ļø Keamanan & Pembaruan
90
90
 
@@ -296,7 +296,7 @@ server {
296
296
  proxy_set_header X-Real-IP $remote_addr;
297
297
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
298
298
  proxy_set_header X-Forwarded-Proto $scheme;
299
- proxy_pass http://127.0.0.1:4000;
299
+ proxy_pass http://127.0.0.1:8000;
300
300
  }
301
301
  }
302
302
  ```
@@ -335,8 +335,8 @@ sudo systemctl reload apache2
335
335
  <Proxy *>
336
336
  Require all granted
337
337
  </Proxy>
338
- ProxyPass / http://127.0.0.1:4000/
339
- ProxyPassReverse / http://127.0.0.1:4000/
338
+ ProxyPass / http://127.0.0.1:8000/
339
+ ProxyPassReverse / http://127.0.0.1:8000/
340
340
  ErrorLog ${APACHE_LOG_DIR}/lapeh-error.log
341
341
  CustomLog ${APACHE_LOG_DIR}/lapeh-access.log combined
342
342
  </VirtualHost>
@@ -353,5 +353,5 @@ sudo systemctl reload apache2
353
353
  ### 6) Checklist Produksi
354
354
 
355
355
  - `pm2 status` menunjukkan proses hidup
356
- - Proxy (Nginx/Apache) menuju port aplikasi (default 4000)
356
+ - Proxy (Nginx/Apache) menuju port aplikasi (default 8000)
357
357
  - `.env` aman dan tidak di-commit ke repository
@@ -137,7 +137,7 @@ async function bootstrap() {
137
137
  process.exit(1);
138
138
  }
139
139
  const app = await createApp();
140
- const port = process.env.PORT ? Number(process.env.PORT) : 4000;
140
+ const port = process.env.PORT ? Number(process.env.PORT) : 8000;
141
141
  const server = http_1.default.createServer(app);
142
142
  (0, realtime_1.initRealtime)(server);
143
143
  try {
@@ -2,6 +2,14 @@
2
2
 
3
3
  File ini mencatat semua perubahan, pembaruan, dan perbaikan yang dilakukan pada framework Lapeh, diurutkan berdasarkan tanggal.
4
4
 
5
+ ## [2025-12-30] - Tuesday, December 30, 2025 - Smart Blog Automation (v3.0.8)
6
+
7
+ ### šŸ¤– Release Script Improvements
8
+
9
+ - **Smart Blog Generation**: The `release.js` script now intelligently reads content from `CHANGELOG.md` (both ID and EN) to generate release blog posts. No more generic "Routine maintenance" content!
10
+ - **Flexible Version Detection**: Improved Regex to detect versions in various changelog header formats.
11
+ - **Bilingual Support**: Ensures release notes are accurately available in both Indonesian and English.
12
+
5
13
  ## [2025-12-30] - Tuesday, December 30, 2025 - Documentation & CLI (v3.0.7)
6
14
 
7
15
  ### šŸ“š Documentation
@@ -107,14 +107,14 @@ You can combine multiple Node.js apps in one `ecosystem.config.js`.
107
107
 
108
108
  ### 8. Reverse Proxy (Nginx)
109
109
 
110
- Do not expose port 4000 directly. Use Nginx in front of it.
110
+ Do not expose port 8000 directly. Use Nginx in front of it.
111
111
  Nginx block config:
112
112
 
113
113
  ```nginx
114
114
  server {
115
115
  server_name api.your-domain.com;
116
116
  location / {
117
- proxy_pass http://localhost:4000;
117
+ proxy_pass http://localhost:8000;
118
118
  proxy_http_version 1.1;
119
119
  proxy_set_header Upgrade $http_upgrade;
120
120
  proxy_set_header Connection 'upgrade';
@@ -141,7 +141,7 @@ RUN npm install
141
141
  COPY . .
142
142
  RUN npm run build
143
143
 
144
- EXPOSE 4000
144
+ EXPOSE 8000
145
145
  CMD ["npm", "run", "start:prod"]
146
146
  ```
147
147
 
@@ -149,7 +149,7 @@ CMD ["npm", "run", "start:prod"]
149
149
 
150
150
  ```bash
151
151
  docker build -t my-lapeh-app .
152
- docker run -p 4000:4000 --env-file .env my-lapeh-app
152
+ docker run -p 8000:8000 --env-file .env my-lapeh-app
153
153
  ```
154
154
 
155
155
  ## Strategy 3: PaaS (Railway / Render / Vercel)
package/doc/en/FAQ.md CHANGED
@@ -5,19 +5,23 @@ Kumpulan pertanyaan umum dan solusi untuk masalah yang sering dihadapi.
5
5
  ## Database
6
6
 
7
7
  ### Q: Database apa yang didukung?
8
+
8
9
  **A:**
9
10
  Lapeh bersifat **Database Agnostic**. Anda bisa menggunakan PostgreSQL, MySQL, MongoDB, atau database apapun. Framework tidak membatasi pilihan database Anda. Kami menyediakan wrapper `db` generik yang bisa Anda konfigurasi sesuai kebutuhan.
10
11
 
11
12
  ## Redis & Caching
12
13
 
13
14
  ### Q: Saya tidak ingin pakai Redis di local, ribet installnya.
15
+
14
16
  **A:**
15
17
  Tenang, Lapeh otomatis mendeteksi jika Redis tidak berjalan dan akan menggunakan **In-Memory Mock** (simulasi Redis di RAM). Aplikasi tetap jalan normal tanpa error. Anda tidak perlu config apa-apa.
16
18
 
17
19
  ### Q: Bagaimana cara clear cache Redis?
20
+
18
21
  **A:**
19
22
  Jika punya akses CLI Redis: `redis-cli FLUSHALL`.
20
23
  Atau via kode:
24
+
21
25
  ```typescript
22
26
  import { redis } from "@/core/redis";
23
27
  await redis.flushall();
@@ -26,33 +30,40 @@ await redis.flushall();
26
30
  ## Fitur & Kustomisasi
27
31
 
28
32
  ### Q: Bagaimana cara handle File Upload?
33
+
29
34
  **A:**
30
35
  Gunakan `multer`. Framework sudah menyiapkannya di `src/routes/auth.ts` sebagai contoh (upload avatar).
31
36
  Anda bisa copy logic konfigurasi `multer` tersebut ke route lain.
32
37
 
33
38
  ### Q: Bagaimana cara menambah middleware global baru?
39
+
34
40
  **A:**
35
41
  Buka `src/core/server.ts`. Di sana ada bagian `// Global Middlewares`. Tambahkan middleware Express Anda di situ:
42
+
36
43
  ```typescript
37
44
  app.use(myCustomMiddleware);
38
45
  ```
39
46
 
40
47
  ### Q: Saya ingin mengubah port server.
48
+
41
49
  **A:**
42
- Cukup ubah variabel `PORT` di file `.env`. Defaultnya adalah `4000`.
50
+ Cukup ubah variabel `PORT` di file `.env`. Defaultnya adalah `8000`.
43
51
 
44
52
  ## Troubleshooting
45
53
 
46
- ### Q: Error `EADDRINUSE: address already in use :::4000`
54
+ ### Q: Error `EADDRINUSE: address already in use :::8000`
55
+
47
56
  **A:**
48
- Artinya port 4000 sedang dipakai program lain (atau instance Lapeh sebelumnya yang nyangkut).
57
+ Artinya port 8000 sedang dipakai program lain (atau instance Lapeh sebelumnya yang nyangkut).
49
58
  **Solusi:**
59
+
50
60
  1. Matikan terminal.
51
61
  2. Jalankan perintah kill (framework biasanya memberi saran commandnya saat error muncul).
52
62
  - Windows: `taskkill /F /IM node.exe` (Hati-hati ini mematikan semua node process).
53
- - Atau cari PID nya: `netstat -ano | findstr :4000`.
63
+ - Atau cari PID nya: `netstat -ano | findstr :8000`.
54
64
 
55
65
  ### Q: Error `Unique constraint failed` saat seeding
66
+
56
67
  **A:**
57
68
  Data yang mau dimasukkan sudah ada (misal email `sa@sa.com`).
58
69
  Jalankan `npm run db:reset` untuk menghapus semua data dan mulai dari nol.
@@ -43,7 +43,7 @@ This script will automatically perform the following:
43
43
  npm run dev
44
44
  ```
45
45
 
46
- The server will run at `http://localhost:4000` (or the port specified in `.env`).
46
+ The server will run at `http://localhost:8000` (or the port specified in `.env`).
47
47
 
48
48
  ## Directory Structure
49
49
 
@@ -72,7 +72,7 @@ The `.env` file stores important configurations. Here are the key variables:
72
72
 
73
73
  ```ini
74
74
  # Server
75
- PORT=4000
75
+ PORT=8000
76
76
  NODE_ENV=development
77
77
 
78
78
  # Security
@@ -1,6 +1,7 @@
1
1
  # Tutorial: Building a Library API
2
2
 
3
3
  In this tutorial, we will build a simple "Book Management" feature using Lapeh Framework's core features:
4
+
4
5
  1. **CLI** for code generation.
5
6
  2. **Validator** for input validation.
6
7
  3. **Fast Serialization** for high-performance responses.
@@ -16,6 +17,7 @@ npm run make:module Book
16
17
  ```
17
18
 
18
19
  The framework will generate:
20
+
19
21
  - `src/controllers/bookController.ts`
20
22
  - `src/routes/book.ts`
21
23
 
@@ -49,16 +51,22 @@ const bookSchema = {
49
51
  title: { type: "string" },
50
52
  author: { type: "string" },
51
53
  isbn: { type: "string" },
52
- stock: { type: "number" }
53
- }
54
+ stock: { type: "number" },
55
+ },
54
56
  };
55
57
 
56
58
  // 2. Create Serializer
57
- const bookDetailSerializer = getSerializer("book-detail", createResponseSchema(bookSchema));
58
- const bookListSerializer = getSerializer("book-list", createResponseSchema({
59
- type: "array",
60
- items: bookSchema
61
- }));
59
+ const bookDetailSerializer = getSerializer(
60
+ "book-detail",
61
+ createResponseSchema(bookSchema)
62
+ );
63
+ const bookListSerializer = getSerializer(
64
+ "book-list",
65
+ createResponseSchema({
66
+ type: "array",
67
+ items: bookSchema,
68
+ })
69
+ );
62
70
  ```
63
71
 
64
72
  ### Implement Create (with Validation)
@@ -70,7 +78,7 @@ export async function createBook(req: Request, res: Response) {
70
78
  title: "required|string|min:3",
71
79
  author: "required|string",
72
80
  isbn: "required|string",
73
- stock: "required|number|min:1"
81
+ stock: "required|number|min:1",
74
82
  });
75
83
 
76
84
  if (validator.fails()) {
@@ -80,7 +88,7 @@ export async function createBook(req: Request, res: Response) {
80
88
  // 2. Save Data (In-Memory)
81
89
  const newBook: Book = {
82
90
  id: Date.now().toString(),
83
- ...validator.validated()
91
+ ...validator.validated(),
84
92
  };
85
93
  books.push(newBook);
86
94
 
@@ -113,6 +121,7 @@ export default router;
113
121
  ## Step 4: Test Your API
114
122
 
115
123
  Run the server:
124
+
116
125
  ```bash
117
126
  npm run dev
118
127
  ```
@@ -120,15 +129,17 @@ npm run dev
120
129
  Test with curl or Postman:
121
130
 
122
131
  **Create Book:**
132
+
123
133
  ```bash
124
- curl -X POST http://localhost:4000/api/book \
134
+ curl -X POST http://localhost:8000/api/book \
125
135
  -H "Content-Type: application/json" \
126
136
  -d '{"title":"Lapeh Guide", "author":"Team", "isbn":"12345", "stock":10}'
127
137
  ```
128
138
 
129
139
  **List Books:**
140
+
130
141
  ```bash
131
- curl http://localhost:4000/api/book
142
+ curl http://localhost:8000/api/book
132
143
  ```
133
144
 
134
145
  Congratulations! You have built a fast, validated API without getting bogged down in database configuration.
@@ -2,6 +2,14 @@
2
2
 
3
3
  File ini mencatat semua perubahan, pembaruan, dan perbaikan yang dilakukan pada framework Lapeh, diurutkan berdasarkan tanggal.
4
4
 
5
+ ## [2025-12-30] - Selasa, 30 Desember 2025 - Otomatisasi Blog Cerdas (v3.0.8)
6
+
7
+ ### šŸ¤– Peningkatan Script Rilis
8
+
9
+ - **Smart Blog Generation**: Script `release.js` kini secara cerdas membaca konten dari `CHANGELOG.md` (baik ID maupun EN) untuk membuat postingan blog rilis. Tidak ada lagi konten generik "Routine maintenance"!
10
+ - **Deteksi Versi Fleksibel**: Peningkatan Regex untuk mendeteksi versi dalam format header changelog yang bervariasi.
11
+ - **Dukungan Dwibahasa**: Memastikan rilis notes tersedia dalam Bahasa Indonesia dan Inggris secara akurat.
12
+
5
13
  ## [2025-12-30] - Selasa, 30 Desember 2025 - Dokumentasi & CLI (v3.0.7)
6
14
 
7
15
  ### šŸ“š Dokumentasi
@@ -107,14 +107,14 @@ Anda bisa menggabungkannya dalam satu file `ecosystem.config.js`.
107
107
 
108
108
  ### 8. Reverse Proxy (Nginx)
109
109
 
110
- Jangan expose port 4000 langsung. Gunakan Nginx di depannya.
110
+ Jangan expose port 8000 langsung. Gunakan Nginx di depannya.
111
111
  Config Nginx block:
112
112
 
113
113
  ```nginx
114
114
  server {
115
115
  server_name api.domain-anda.com;
116
116
  location / {
117
- proxy_pass http://localhost:4000;
117
+ proxy_pass http://localhost:8000;
118
118
  proxy_http_version 1.1;
119
119
  proxy_set_header Upgrade $http_upgrade;
120
120
  proxy_set_header Connection 'upgrade';
@@ -141,7 +141,7 @@ RUN npm install
141
141
  COPY . .
142
142
  RUN npm run build
143
143
 
144
- EXPOSE 4000
144
+ EXPOSE 8000
145
145
  CMD ["npm", "run", "start:prod"]
146
146
  ```
147
147
 
@@ -149,7 +149,7 @@ CMD ["npm", "run", "start:prod"]
149
149
 
150
150
  ```bash
151
151
  docker build -t my-lapeh-app .
152
- docker run -p 4000:4000 --env-file .env my-lapeh-app
152
+ docker run -p 8000:8000 --env-file .env my-lapeh-app
153
153
  ```
154
154
 
155
155
  ## Strategi 3: PaaS (Railway / Render / Vercel)
package/doc/id/FAQ.md CHANGED
@@ -5,19 +5,23 @@ Kumpulan pertanyaan umum dan solusi untuk masalah yang sering dihadapi.
5
5
  ## Database
6
6
 
7
7
  ### Q: Database apa yang didukung?
8
+
8
9
  **A:**
9
10
  Lapeh bersifat **Database Agnostic**. Anda bisa menggunakan PostgreSQL, MySQL, MongoDB, atau database apapun. Framework tidak membatasi pilihan database Anda. Kami menyediakan wrapper `db` generik yang bisa Anda konfigurasi sesuai kebutuhan.
10
11
 
11
12
  ## Redis & Caching
12
13
 
13
14
  ### Q: Saya tidak ingin pakai Redis di local, ribet installnya.
15
+
14
16
  **A:**
15
17
  Tenang, Lapeh otomatis mendeteksi jika Redis tidak berjalan dan akan menggunakan **In-Memory Mock** (simulasi Redis di RAM). Aplikasi tetap jalan normal tanpa error. Anda tidak perlu config apa-apa.
16
18
 
17
19
  ### Q: Bagaimana cara clear cache Redis?
20
+
18
21
  **A:**
19
22
  Jika punya akses CLI Redis: `redis-cli FLUSHALL`.
20
23
  Atau via kode:
24
+
21
25
  ```typescript
22
26
  import { redis } from "@/core/redis";
23
27
  await redis.flushall();
@@ -26,33 +30,40 @@ await redis.flushall();
26
30
  ## Fitur & Kustomisasi
27
31
 
28
32
  ### Q: Bagaimana cara handle File Upload?
33
+
29
34
  **A:**
30
35
  Gunakan `multer`. Framework sudah menyiapkannya di `src/routes/auth.ts` sebagai contoh (upload avatar).
31
36
  Anda bisa copy logic konfigurasi `multer` tersebut ke route lain.
32
37
 
33
38
  ### Q: Bagaimana cara menambah middleware global baru?
39
+
34
40
  **A:**
35
41
  Buka `src/core/server.ts`. Di sana ada bagian `// Global Middlewares`. Tambahkan middleware Express Anda di situ:
42
+
36
43
  ```typescript
37
44
  app.use(myCustomMiddleware);
38
45
  ```
39
46
 
40
47
  ### Q: Saya ingin mengubah port server.
48
+
41
49
  **A:**
42
- Cukup ubah variabel `PORT` di file `.env`. Defaultnya adalah `4000`.
50
+ Cukup ubah variabel `PORT` di file `.env`. Defaultnya adalah `8000`.
43
51
 
44
52
  ## Troubleshooting
45
53
 
46
- ### Q: Error `EADDRINUSE: address already in use :::4000`
54
+ ### Q: Error `EADDRINUSE: address already in use :::8000`
55
+
47
56
  **A:**
48
- Artinya port 4000 sedang dipakai program lain (atau instance Lapeh sebelumnya yang nyangkut).
57
+ Artinya port 8000 sedang dipakai program lain (atau instance Lapeh sebelumnya yang nyangkut).
49
58
  **Solusi:**
59
+
50
60
  1. Matikan terminal.
51
61
  2. Jalankan perintah kill (framework biasanya memberi saran commandnya saat error muncul).
52
62
  - Windows: `taskkill /F /IM node.exe` (Hati-hati ini mematikan semua node process).
53
- - Atau cari PID nya: `netstat -ano | findstr :4000`.
63
+ - Atau cari PID nya: `netstat -ano | findstr :8000`.
54
64
 
55
65
  ### Q: Error `Unique constraint failed` saat seeding
66
+
56
67
  **A:**
57
68
  Data yang mau dimasukkan sudah ada (misal email `sa@sa.com`).
58
69
  Jalankan `npm run db:reset` untuk menghapus semua data dan mulai dari nol.
@@ -43,7 +43,7 @@ Script ini akan melakukan hal-hal berikut secara otomatis:
43
43
  npm run dev
44
44
  ```
45
45
 
46
- Server akan berjalan di `http://localhost:4000` (atau port yang Anda tentukan di `.env`).
46
+ Server akan berjalan di `http://localhost:8000` (atau port yang Anda tentukan di `.env`).
47
47
 
48
48
  ## Struktur Direktori
49
49
 
@@ -72,7 +72,7 @@ File `.env` menyimpan konfigurasi penting. Berikut adalah variabel kunci:
72
72
 
73
73
  ```ini
74
74
  # Server
75
- PORT=4000
75
+ PORT=8000
76
76
  NODE_ENV=development
77
77
 
78
78
  # Security
@@ -1,6 +1,7 @@
1
1
  # Tutorial: Membangun API Perpustakaan
2
2
 
3
3
  Dalam tutorial ini, kita akan membangun fitur "Manajemen Buku" sederhana menggunakan semua fitur unggulan Lapeh Framework:
4
+
4
5
  1. **CLI** untuk generate kode.
5
6
  2. **Validator** untuk validasi input.
6
7
  3. **Fast Serialization** untuk respon cepat.
@@ -16,6 +17,7 @@ npm run make:module Book
16
17
  ```
17
18
 
18
19
  Framework akan membuat:
20
+
19
21
  - `src/controllers/bookController.ts`
20
22
  - `src/routes/book.ts`
21
23
 
@@ -49,16 +51,22 @@ const bookSchema = {
49
51
  title: { type: "string" },
50
52
  author: { type: "string" },
51
53
  isbn: { type: "string" },
52
- stock: { type: "number" }
53
- }
54
+ stock: { type: "number" },
55
+ },
54
56
  };
55
57
 
56
58
  // 2. Buat Serializer
57
- const bookDetailSerializer = getSerializer("book-detail", createResponseSchema(bookSchema));
58
- const bookListSerializer = getSerializer("book-list", createResponseSchema({
59
- type: "array",
60
- items: bookSchema
61
- }));
59
+ const bookDetailSerializer = getSerializer(
60
+ "book-detail",
61
+ createResponseSchema(bookSchema)
62
+ );
63
+ const bookListSerializer = getSerializer(
64
+ "book-list",
65
+ createResponseSchema({
66
+ type: "array",
67
+ items: bookSchema,
68
+ })
69
+ );
62
70
  ```
63
71
 
64
72
  ### Implementasi Create (dengan Validasi)
@@ -70,7 +78,7 @@ export async function createBook(req: Request, res: Response) {
70
78
  title: "required|string|min:3",
71
79
  author: "required|string",
72
80
  isbn: "required|string",
73
- stock: "required|number|min:1"
81
+ stock: "required|number|min:1",
74
82
  });
75
83
 
76
84
  if (validator.fails()) {
@@ -80,7 +88,7 @@ export async function createBook(req: Request, res: Response) {
80
88
  // 2. Simpan Data (In-Memory)
81
89
  const newBook: Book = {
82
90
  id: Date.now().toString(),
83
- ...validator.validated()
91
+ ...validator.validated(),
84
92
  };
85
93
  books.push(newBook);
86
94
 
@@ -113,6 +121,7 @@ export default router;
113
121
  ## Langkah 4: Test API Anda
114
122
 
115
123
  Jalankan server:
124
+
116
125
  ```bash
117
126
  npm run dev
118
127
  ```
@@ -120,15 +129,17 @@ npm run dev
120
129
  Test dengan curl atau Postman:
121
130
 
122
131
  **Buat Buku Baru:**
132
+
123
133
  ```bash
124
- curl -X POST http://localhost:4000/api/book \
134
+ curl -X POST http://localhost:8000/api/book \
125
135
  -H "Content-Type: application/json" \
126
136
  -d '{"title":"Panduan Lapeh", "author":"Tim Lapeh", "isbn":"12345", "stock":10}'
127
137
  ```
128
138
 
129
139
  **List Buku:**
140
+
130
141
  ```bash
131
- curl http://localhost:4000/api/book
142
+ curl http://localhost:8000/api/book
132
143
  ```
133
144
 
134
145
  Selamat! Anda telah membangun API yang cepat dan tervalidasi tanpa terjebak dalam konfigurasi database yang rumit.
package/lib/bootstrap.ts CHANGED
@@ -159,7 +159,7 @@ export async function bootstrap() {
159
159
  }
160
160
 
161
161
  const app = await createApp();
162
- const port = process.env.PORT ? Number(process.env.PORT) : 4000;
162
+ const port = process.env.PORT ? Number(process.env.PORT) : 8000;
163
163
  const server = http.createServer(app);
164
164
 
165
165
  initRealtime(server);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lapeh",
3
- "version": "3.0.8",
3
+ "version": "3.0.10",
4
4
  "description": "Framework API Express yang siap pakai (Standardized)",
5
5
  "engines": {
6
6
  "node": ">=18.0.0",
@@ -53,7 +53,7 @@ const selectOption = async (query, options) => {
53
53
  envContent = fs.readFileSync(envExample, "utf8");
54
54
  } else {
55
55
  // Fallback minimal env if example missing
56
- envContent = `PORT=4000\nJWT_SECRET="replace_this"\n`;
56
+ envContent = `PORT=8000\nJWT_SECRET="replace_this"\n`;
57
57
  }
58
58
 
59
59
  fs.writeFileSync(envFile, envContent);
@@ -103,21 +103,45 @@ function generateAutoCommitMessage() {
103
103
  }
104
104
  }
105
105
 
106
- // Helper to extract changelog entry
107
- function extractChangelogEntry(filePath, version) {
106
+ // Helper to parse changelog entry with structure
107
+ function parseChangelogEntry(filePath, version) {
108
108
  try {
109
109
  if (!fs.existsSync(filePath)) return null;
110
110
  const content = fs.readFileSync(filePath, 'utf8');
111
- // Regex to find the section for the version.
112
- // Matches "## [YYYY-MM-DD] ... vX.X.X ..." until the next "## ["
113
- // We allow the version to be anywhere in the header line
114
- const regex = new RegExp(`## \\[.*?\\] - .*?v${version}.*?([\\s\\S]*?)(?=\\n## \\[|$)`, 'i');
115
- const match = content.match(regex);
116
-
117
- if (match && match[1]) {
118
- return match[1].trim();
111
+
112
+ // 1. Find the header line to extract Title
113
+ // Regex matches: ## [Date] - Day, Date - Title (vVersion)
114
+ const headerRegex = new RegExp(`## \\[.*?\\] - .*? - (.*?) \\(v${version}\\)`, 'i');
115
+ const headerMatch = content.match(headerRegex);
116
+ const title = headerMatch ? headerMatch[1].trim() : null;
117
+
118
+ // 2. Extract the body
119
+ const bodyRegex = new RegExp(`## \\[.*?\\] - .*?v${version}.*?([\\s\\S]*?)(?=\\n## \\[|$)`, 'i');
120
+ const bodyMatch = content.match(bodyRegex);
121
+
122
+ if (!bodyMatch) return null;
123
+
124
+ let rawBody = bodyMatch[1].trim();
125
+
126
+ // 3. Extract Intro (text before first ###)
127
+ let intro = '';
128
+ let features = rawBody;
129
+
130
+ const firstHeaderIndex = rawBody.indexOf('###');
131
+ if (firstHeaderIndex > 0) {
132
+ intro = rawBody.substring(0, firstHeaderIndex).trim();
133
+ features = rawBody.substring(firstHeaderIndex).trim();
134
+ } else if (firstHeaderIndex === -1 && !rawBody.startsWith('-') && !rawBody.startsWith('*')) {
135
+ // If no subheaders and doesn't start with list, treat as intro
136
+ intro = rawBody;
137
+ features = '';
119
138
  }
120
- return null;
139
+
140
+ return {
141
+ title,
142
+ intro,
143
+ features
144
+ };
121
145
  } catch (e) {
122
146
  return null;
123
147
  }
@@ -178,47 +202,79 @@ async function main() {
178
202
  let blogTitleEN = '';
179
203
 
180
204
  if (createBlog.toLowerCase() === 'y') {
181
- console.log('\nšŸ¤– Auto-detecting changes from Git & Changelog...');
182
- const changes = getGitChanges();
183
205
 
184
- // Try to read from CHANGELOG.md first
185
- const changelogID = extractChangelogEntry(path.join(rootDir, 'doc/id/CHANGELOG.md'), newVersion);
186
- const changelogEN = extractChangelogEntry(path.join(rootDir, 'doc/en/CHANGELOG.md'), newVersion);
187
-
188
- let titleID, descriptionID, featuresID, featureListID;
189
- let titleEN, descriptionEN, featuresEN, featureListEN;
190
-
191
- if (changelogID) {
192
- console.log('āœ… Found entry in doc/id/CHANGELOG.md');
193
- // Extract title from first line of changelog entry if possible, or use default
194
- // Actually usually changelog entry body starts with ### Section.
195
- // We'll use a generic title and the full body as content.
196
- titleID = `Update Terbaru v${newVersion}`;
197
- // Try to find specific sections for description/features is hard without strict parsing.
198
- // We will treat the entire changelog body as the "Features" section.
199
- descriptionID = `Rilis versi ${newVersion} hadir dengan berbagai pembaruan dan perbaikan.`;
200
- featureListID = changelogID;
201
- } else {
202
- console.log('āš ļø No entry in doc/id/CHANGELOG.md, using git logs...');
203
- titleID = changes.length > 0 ? changes[0] : 'Maintenance Release';
204
- descriptionID = changes.length > 0 ? `Includes: ${changes.slice(0, 2).join(', ')}` : 'Routine maintenance and updates.';
205
- featureListID = changes.length > 0
206
- ? changes.map(f => `* **${f.trim()}**`).join('\n')
207
- : '* **Routine maintenance**';
208
- }
206
+ const useAuto = await question('šŸ¤– Auto-generate content from CHANGELOG/Git? (y/n): ');
207
+
208
+ let titleID, descriptionID, introID, featureListID;
209
+ let titleEN, descriptionEN, introEN, featureListEN;
210
+
211
+ if (useAuto.toLowerCase() === 'y') {
212
+ console.log('\nšŸ¤– Auto-detecting changes from Git & Changelog...');
213
+ const changes = getGitChanges();
214
+
215
+ // Try to read from CHANGELOG.md first
216
+ const parsedID = parseChangelogEntry(path.join(rootDir, 'doc/id/CHANGELOG.md'), newVersion);
217
+ const parsedEN = parseChangelogEntry(path.join(rootDir, 'doc/en/CHANGELOG.md'), newVersion);
218
+
219
+ if (parsedID) {
220
+ console.log('āœ… Found entry in doc/id/CHANGELOG.md');
221
+ titleID = parsedID.title || `Update Terbaru v${newVersion}`;
222
+ introID = parsedID.intro || `Kami dengan bangga mengumumkan rilis **Lapeh v${newVersion}**. Update ini menghadirkan **${parsedID.title || 'berbagai fitur baru'}** untuk meningkatkan pengalaman pengembangan Anda.`;
223
+ descriptionID = parsedID.intro ? parsedID.intro.split('\n')[0] : `Rilis versi ${newVersion} hadir dengan berbagai pembaruan dan perbaikan.`;
224
+ featureListID = parsedID.features;
225
+ } else {
226
+ console.log('āš ļø No entry in doc/id/CHANGELOG.md, using git logs...');
227
+ titleID = changes.length > 0 ? changes[0] : 'Maintenance Release';
228
+ descriptionID = changes.length > 0 ? `Includes: ${changes.slice(0, 2).join(', ')}` : 'Routine maintenance and updates.';
229
+ introID = `Kami dengan bangga mengumumkan rilis **Lapeh v${newVersion}**. Rilis ini mencakup pemeliharaan rutin dan perbaikan bug.`;
230
+ featureListID = changes.length > 0
231
+ ? changes.map(f => `* **${f.trim()}**`).join('\n')
232
+ : '* **Routine maintenance**';
233
+ }
209
234
 
210
- if (changelogEN) {
211
- console.log('āœ… Found entry in doc/en/CHANGELOG.md');
212
- titleEN = `Latest Update v${newVersion}`;
213
- descriptionEN = `Release version ${newVersion} comes with various updates and improvements.`;
214
- featureListEN = changelogEN;
235
+ if (parsedEN) {
236
+ console.log('āœ… Found entry in doc/en/CHANGELOG.md');
237
+ titleEN = parsedEN.title || `Latest Update v${newVersion}`;
238
+ introEN = parsedEN.intro || `We are proud to announce the release of **Lapeh v${newVersion}**. This update brings **${parsedEN.title || 'various new features'}** to enhance your development experience.`;
239
+ descriptionEN = parsedEN.intro ? parsedEN.intro.split('\n')[0] : `Release version ${newVersion} comes with various updates and improvements.`;
240
+ featureListEN = parsedEN.features;
241
+ } else {
242
+ console.log('āš ļø No entry in doc/en/CHANGELOG.md, using git logs...');
243
+ titleEN = changes.length > 0 ? changes[0] : 'Maintenance Release';
244
+ descriptionEN = changes.length > 0 ? `Includes: ${changes.slice(0, 2).join(', ')}` : 'Routine maintenance and updates.';
245
+ introEN = `We are proud to announce the release of **Lapeh v${newVersion}**. This release includes routine maintenance and bug fixes.`;
246
+ featureListEN = changes.length > 0
247
+ ? changes.map(f => `* **${f.trim()}**`).join('\n')
248
+ : '* **Routine maintenance**';
249
+ }
215
250
  } else {
216
- console.log('āš ļø No entry in doc/en/CHANGELOG.md, using git logs...');
217
- titleEN = changes.length > 0 ? changes[0] : 'Maintenance Release';
218
- descriptionEN = changes.length > 0 ? `Includes: ${changes.slice(0, 2).join(', ')}` : 'Routine maintenance and updates.';
219
- featureListEN = changes.length > 0
220
- ? changes.map(f => `* **${f.trim()}**`).join('\n')
221
- : '* **Routine maintenance**';
251
+ console.log('\nšŸ“ Manual Blog Entry');
252
+ console.log('Silakan masukkan detail blog secara manual.');
253
+
254
+ // ID Inputs
255
+ titleID = await question('Judul Blog (ID): ');
256
+ descriptionID = await question('Deskripsi Singkat (ID): ');
257
+ const contentID = await question('Konten Utama/Fitur (ID) - Gunakan format Markdown jika perlu: ');
258
+ introID = `Rilis versi ${newVersion} telah hadir.`; // Fallback for manual
259
+ featureListID = contentID;
260
+
261
+ console.log('\n--- English Version ---');
262
+
263
+ // EN Inputs
264
+ titleEN = await question('Blog Title (EN): ');
265
+ descriptionEN = await question('Short Description (EN): ');
266
+ const contentEN = await question('Main Content/Features (EN): ');
267
+ introEN = `Release version ${newVersion} is here.`; // Fallback for manual
268
+ featureListEN = contentEN;
269
+
270
+ // Set defaults if empty
271
+ if (!titleID) titleID = `Update v${newVersion}`;
272
+ if (!descriptionID) descriptionID = `Pembaruan versi ${newVersion}`;
273
+ if (!featureListID) featureListID = '* Pembaruan rutin';
274
+
275
+ if (!titleEN) titleEN = `Update v${newVersion}`;
276
+ if (!descriptionEN) descriptionEN = `Update version ${newVersion}`;
277
+ if (!featureListEN) featureListEN = '* Routine updates';
222
278
  }
223
279
 
224
280
  blogTitleEN = titleEN; // Save for commit message
@@ -232,52 +288,72 @@ async function main() {
232
288
 
233
289
  // Indonesian Blog Content
234
290
  const idContent = `---
235
- title: "Rilis v${newVersion}"
291
+ title: "Rilis Lapeh v${newVersion}: ${titleID}"
236
292
  date: ${date}
237
293
  author: Tim Lapeh
238
294
  description: "${descriptionID.replace(/"/g, '\\"')}"
239
295
  ---
240
296
 
241
- # Rilis v${newVersion}
297
+ # Rilis Lapeh v${newVersion}: ${titleID}
242
298
 
243
- ${descriptionID}
299
+ Ditulis pada **${dateString}** oleh **Tim Lapeh**
244
300
 
245
- ## Rincian Perubahan šŸ“
301
+ ${introID}
302
+
303
+ ## Apa yang Baru? šŸš€
246
304
 
247
305
  ${featureListID}
248
306
 
249
- ## Cara Update
307
+ ## Cara Upgrade
308
+
309
+ Bagi pengguna baru, cukup jalankan:
310
+
311
+ \`\`\`bash
312
+ npx lapeh init my-project
313
+ \`\`\`
314
+
315
+ Bagi pengguna lama yang ingin update ke versi terbaru:
250
316
 
251
317
  \`\`\`bash
252
318
  npm install lapeh@latest
253
319
  \`\`\`
254
320
 
255
- Terima kasih telah menggunakan Lapeh Framework!
321
+ Terima kasih telah menjadi bagian dari perjalanan Lapeh Framework!
256
322
  `;
257
323
 
258
324
  // English Blog Content
259
325
  const enContent = `---
260
- title: "Release v${newVersion}"
326
+ title: "Release Lapeh v${newVersion}: ${titleEN}"
261
327
  date: ${date}
262
328
  author: Lapeh Team
263
329
  description: "${descriptionEN.replace(/"/g, '\\"')}"
264
330
  ---
265
331
 
266
- # Release v${newVersion}
332
+ # Release Lapeh v${newVersion}: ${titleEN}
333
+
334
+ Written on **${dateStringEn}** by **Lapeh Team**
267
335
 
268
- ${descriptionEN}
336
+ ${introEN}
269
337
 
270
- ## Change Details šŸ“
338
+ ## What's New? šŸš€
271
339
 
272
340
  ${featureListEN}
273
341
 
274
- ## How to Update
342
+ ## How to Upgrade
343
+
344
+ For new users, simply run:
345
+
346
+ \`\`\`bash
347
+ npx lapeh init my-project
348
+ \`\`\`
349
+
350
+ For existing users who want to update to the latest version:
275
351
 
276
352
  \`\`\`bash
277
353
  npm install lapeh@latest
278
354
  \`\`\`
279
355
 
280
- Thank you for using Lapeh Framework!
356
+ Thank you for being part of the Lapeh Framework journey!
281
357
  `;
282
358
 
283
359
  fs.writeFileSync(path.join(websiteDir, 'blog', blogFileName), idContent);