lapeh 3.0.4 → 3.0.6

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/bin/index.js CHANGED
@@ -133,7 +133,7 @@ sendTelemetry(command || 'init');
133
133
 
134
134
  switch (command) {
135
135
  case 'dev':
136
- runDev();
136
+ (async () => { await runDev(); })();
137
137
  break;
138
138
  case 'start':
139
139
  (async () => { await runStart(); })();
@@ -155,8 +155,69 @@ switch (command) {
155
155
  break;
156
156
  }
157
157
 
158
- function runDev() {
158
+ async function checkUpdate() {
159
+ try {
160
+ const pkg = require(path.join(__dirname, '../package.json'));
161
+ const currentVersion = pkg.version;
162
+
163
+ // Fetch latest version from npm registry
164
+ const latestVersion = await new Promise((resolve) => {
165
+ const https = require('https');
166
+ const req = https.get('https://registry.npmjs.org/lapeh/latest', {
167
+ headers: { 'User-Agent': 'Lapeh-CLI' },
168
+ timeout: 1500 // 1.5s timeout
169
+ }, (res) => {
170
+ let data = '';
171
+ res.on('data', chunk => data += chunk);
172
+ res.on('end', () => {
173
+ try {
174
+ const json = JSON.parse(data);
175
+ resolve(json.version);
176
+ } catch (e) {
177
+ resolve(null);
178
+ }
179
+ });
180
+ });
181
+
182
+ req.on('error', () => resolve(null));
183
+ req.on('timeout', () => {
184
+ req.destroy();
185
+ resolve(null);
186
+ });
187
+ });
188
+
189
+ if (latestVersion && latestVersion !== currentVersion) {
190
+ const currentParts = currentVersion.split('.').map(Number);
191
+ const latestParts = latestVersion.split('.').map(Number);
192
+
193
+ let isOutdated = false;
194
+ for(let i=0; i<3; i++) {
195
+ if (latestParts[i] > currentParts[i]) {
196
+ isOutdated = true;
197
+ break;
198
+ } else if (latestParts[i] < currentParts[i]) {
199
+ break;
200
+ }
201
+ }
202
+
203
+ if (isOutdated) {
204
+ console.log('\n');
205
+ console.log('\x1b[33m┌────────────────────────────────────────────────────────────┐\x1b[0m');
206
+ console.log(`\x1b[33m│\x1b[0m \x1b[1mUpdate available!\x1b[0m \x1b[31m${currentVersion}\x1b[0m → \x1b[32m${latestVersion}\x1b[0m \x1b[33m│\x1b[0m`);
207
+ console.log(`\x1b[33m│\x1b[0m Run \x1b[36mnpm install lapeh@latest\x1b[0m to update \x1b[33m│\x1b[0m`);
208
+ console.log(`\x1b[33m│\x1b[0m Then run \x1b[36mnpx lapeh upgrade\x1b[0m to sync files \x1b[33m│\x1b[0m`);
209
+ console.log('\x1b[33m└────────────────────────────────────────────────────────────┘\x1b[0m');
210
+ console.log('\n');
211
+ }
212
+ }
213
+ } catch (e) {
214
+ // Ignore errors during update check
215
+ }
216
+ }
217
+
218
+ async function runDev() {
159
219
  console.log('🚀 Starting Lapeh in development mode...');
220
+ await checkUpdate();
160
221
  try {
161
222
  const tsNodePath = require.resolve('ts-node/register');
162
223
  const tsConfigPathsPath = require.resolve('tsconfig-paths/register');
@@ -305,6 +366,12 @@ async function upgradeProject() {
305
366
  'src/redis.ts'
306
367
  ];
307
368
 
369
+ const updateStats = {
370
+ updated: [],
371
+ created: [],
372
+ removed: []
373
+ };
374
+
308
375
  function syncDirectory(src, dest, clean = false) {
309
376
  if (!fs.existsSync(src)) return;
310
377
  if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
@@ -316,11 +383,28 @@ async function upgradeProject() {
316
383
  srcEntryNames.add(entry.name);
317
384
  const srcPath = path.join(src, entry.name);
318
385
  const destPath = path.join(dest, entry.name);
386
+ const relativePath = path.relative(currentDir, destPath);
319
387
 
320
388
  if (entry.isDirectory()) {
321
389
  syncDirectory(srcPath, destPath, clean);
322
390
  } else {
323
- fs.copyFileSync(srcPath, destPath);
391
+ let shouldCopy = true;
392
+
393
+ if (fs.existsSync(destPath)) {
394
+ const srcContent = fs.readFileSync(srcPath);
395
+ const destContent = fs.readFileSync(destPath);
396
+ if (srcContent.equals(destContent)) {
397
+ shouldCopy = false;
398
+ } else {
399
+ updateStats.updated.push(relativePath);
400
+ }
401
+ } else {
402
+ updateStats.created.push(relativePath);
403
+ }
404
+
405
+ if (shouldCopy) {
406
+ fs.copyFileSync(srcPath, destPath);
407
+ }
324
408
  }
325
409
  }
326
410
 
@@ -329,7 +413,11 @@ async function upgradeProject() {
329
413
  for (const entry of destEntries) {
330
414
  if (!srcEntryNames.has(entry.name)) {
331
415
  const destPath = path.join(dest, entry.name);
416
+ const relativePath = path.relative(currentDir, destPath);
417
+
332
418
  console.log(`🗑️ Removing obsolete file/directory: ${destPath}`);
419
+ updateStats.removed.push(relativePath);
420
+
333
421
  if (entry.isDirectory()) {
334
422
  fs.rmSync(destPath, { recursive: true, force: true });
335
423
  } else {
@@ -343,6 +431,7 @@ async function upgradeProject() {
343
431
  for (const item of filesToSync) {
344
432
  const srcPath = path.join(templateDir, item);
345
433
  const destPath = path.join(currentDir, item);
434
+ const relativePath = item; // Since item is relative to templateDir/currentDir
346
435
 
347
436
  if (fs.existsSync(srcPath)) {
348
437
  const stats = fs.statSync(srcPath);
@@ -350,10 +439,26 @@ async function upgradeProject() {
350
439
  console.log(`🔄 Syncing directory ${item}...`);
351
440
  syncDirectory(srcPath, destPath, item === 'lib');
352
441
  } else {
353
- console.log(`🔄 Updating file ${item}...`);
442
+ console.log(`🔄 Checking file ${item}...`);
354
443
  const destDir = path.dirname(destPath);
355
444
  if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
356
- fs.copyFileSync(srcPath, destPath);
445
+
446
+ let shouldCopy = true;
447
+ if (fs.existsSync(destPath)) {
448
+ const srcContent = fs.readFileSync(srcPath);
449
+ const destContent = fs.readFileSync(destPath);
450
+ if (srcContent.equals(destContent)) {
451
+ shouldCopy = false;
452
+ } else {
453
+ updateStats.updated.push(relativePath);
454
+ }
455
+ } else {
456
+ updateStats.created.push(relativePath);
457
+ }
458
+
459
+ if (shouldCopy) {
460
+ fs.copyFileSync(srcPath, destPath);
461
+ }
357
462
  }
358
463
  }
359
464
  }
@@ -430,7 +535,27 @@ async function upgradeProject() {
430
535
  }
431
536
 
432
537
  console.log('\n✅ Upgrade completed successfully!');
433
- console.log(' Please check your .env file against .env.example for any new required variables.');
538
+
539
+ if (updateStats.created.length > 0) {
540
+ console.log('\n✨ Created files:');
541
+ updateStats.created.forEach(f => console.log(` \x1b[32m+ ${f}\x1b[0m`));
542
+ }
543
+
544
+ if (updateStats.updated.length > 0) {
545
+ console.log('\n📝 Updated files:');
546
+ updateStats.updated.forEach(f => console.log(` \x1b[33m~ ${f}\x1b[0m`));
547
+ }
548
+
549
+ if (updateStats.removed.length > 0) {
550
+ console.log('\n🗑️ Removed files:');
551
+ updateStats.removed.forEach(f => console.log(` \x1b[31m- ${f}\x1b[0m`));
552
+ }
553
+
554
+ if (updateStats.created.length === 0 && updateStats.updated.length === 0 && updateStats.removed.length === 0) {
555
+ console.log(' No files were changed.');
556
+ }
557
+
558
+ console.log('\n Please check your .env file against .env.example for any new required variables.');
434
559
  }
435
560
 
436
561
  function createProject(skipFirstArg = false) {
@@ -520,7 +645,15 @@ function createProject(skipFirstArg = false) {
520
645
  if (ignoreList.includes(entry.name)) continue;
521
646
  const srcPath = path.join(src, entry.name);
522
647
  const destPath = path.join(dest, entry.name);
523
-
648
+
649
+ // Clean storage/logs: skip everything except .gitkeep
650
+ // Check if we are inside storage/logs
651
+ const relPath = path.relative(templateDir, srcPath);
652
+ const isInLogs = relPath.includes(path.join('storage', 'logs')) || relPath.includes('storage/logs') || relPath.includes('storage\\logs');
653
+
654
+ if (isInLogs && !entry.isDirectory() && entry.name !== '.gitkeep') {
655
+ continue;
656
+ }
524
657
 
525
658
  if (entry.isDirectory()) {
526
659
  fs.mkdirSync(destPath);
@@ -1,84 +1,79 @@
1
- # Project Structure Breakdown
1
+ # 🏗️ Project Structure
2
2
 
3
- To fully understand Lapeh Framework, you need to know what each file and folder does. Here is a complete "Tour" of the project directory.
3
+ Lapeh Framework is designed to be **intuitive**. We separate your application code (_User Space_) from the framework engine (_Core_), so you can focus on building features without being distracted by system complexity.
4
4
 
5
- ## Root Directory
5
+ ## 🗺️ Navigation Map
6
6
 
7
- | File/Folder | Description |
8
- | :------------------- | :----------------------------------------------------------------------------- |
9
- | `bin/` | Contains execution scripts for CLI (`npx lapeh`). You rarely touch this. |
10
- | `doc/` | Project documentation resides here. |
11
- | `lib/` | **Framework Core**. Internal parts of the framework you rarely touch. |
12
- | `scripts/` | Collection of Node.js utility scripts (generators, schema compilers, etc). |
13
- | `src/` | **Main Source Code**. 99% of your coding happens here. |
14
- | `.env` | Secret variables (Database URL, API Keys). **Do not commit this file to Git!** |
15
- | `docker-compose.yml` | Docker configuration for running local Database & Redis. |
16
- | `nodemon.json` | Auto-restart configuration during development. |
17
- | `package.json` | List of libraries (dependencies) and commands (`npm run ...`). |
18
- | `tsconfig.json` | TypeScript configuration. |
7
+ Here is a mental map to understand Lapeh folders:
19
8
 
20
- ## `src/` Folder (Source Code - User Space)
9
+ | Folder | Icon | Function | Status |
10
+ | :------------- | :--: | :---------------------------------------------------------------------- | :-------------- |
11
+ | **`src/`** | 🏠 | **YOUR APP CODE**. You work here every day. | **Must Edit** |
12
+ | **`scripts/`** | 🤖 | **Automation Assistants**. Scripts for release, module generation, etc. | **Can Edit** |
13
+ | **`bin/`** | 🚀 | **CLI Tools**. Entry point for `npx lapeh` commands. | **Rarely Edit** |
14
+ | **`lib/`** | ⚙️ | **Framework Engine**. Core server & database logic. | **DO NOT Edit** |
21
15
 
22
- This is where you work every day.
16
+ ---
23
17
 
24
- ### `src/modules/` (Modular Architecture)
18
+ ## 1. 🏠 Folder `src/` (User Space)
25
19
 
26
- Lapeh uses a **Modular** approach. Each feature is grouped into a single module folder to keep the code organized.
20
+ This is your "home". 99% of new feature code will be written here.
27
21
 
28
- Example `Auth` module structure:
22
+ ### `src/modules/` (Modular Architecture)
29
23
 
30
- - `Auth/auth.controller.ts`: Application logic (Controller).
24
+ Lapeh uses a **Modular** approach. One feature = One folder.
25
+
26
+ - **Example**: `src/modules/Auth/` folder contains all login/register logic.
27
+ - **Benefit**: Code is neat, easy to find, and easy to delete if the feature is no longer used.
31
28
 
32
29
  ### `src/routes/`
33
30
 
34
- Defines endpoint URLs.
31
+ Your API gateway.
35
32
 
36
- - Connects URLs (e.g., `/api/login`) to functions in Controllers.
37
- - Attaches Middleware (e.g., `requireAuth`).
33
+ - Defines URLs (e.g., `/api/users`).
34
+ - Connects URLs to **Controllers**.
38
35
 
39
36
  ### `src/config/`
40
37
 
41
- Static application configuration.
42
-
43
- - `app.ts`: General application configuration.
44
- - `cors.ts`: CORS (Cross-Origin Resource Sharing) configuration.
38
+ Application settings center.
45
39
 
46
- ## `lib/` Folder (Framework Internals)
40
+ - `app.ts`: Global configuration.
41
+ - `cors.ts`: Domain access security.
47
42
 
48
- This part is similar to `node_modules` or the `.next` folder in Next.js. This is the framework engine.
49
-
50
- ### `lib/core/`
43
+ ---
51
44
 
52
- The "Engine" part of the framework.
45
+ ## 2. 🤖 Folder `scripts/` (Robot Assistants)
53
46
 
54
- - `server.ts`: Express App setup.
55
- - `redis.ts`: Redis connection.
56
- - `serializer.ts`: JSON Schema caching logic.
47
+ Don't do boring things manually! Lapeh provides robots here:
57
48
 
58
- ### `lib/middleware/`
49
+ - **`release.js`**: **Super Script** for releasing new versions.
50
+ - ✨ Automatic version bump (package.json).
51
+ - 📝 **Auto-Blog**: Automatically creates release articles from Git Commit history.
52
+ - 🔄 **Auto-Sync**: Synchronizes documentation to the website.
53
+ - 🚀 Push to Git & Publish to NPM in one command.
54
+ - **`make-module.js`**: Generator to create new module folder structures instantly.
59
55
 
60
- Built-in framework middleware.
56
+ ---
61
57
 
62
- - `auth.ts`: Check JWT Token.
63
- - `rateLimit.ts`: Limit request count.
64
- - `requestLogger.ts`: Log every incoming request.
58
+ ## 3. 🚀 Folder `bin/` (CLI & Update)
65
59
 
66
- ### `lib/utils/`
60
+ This folder handles `npx lapeh` terminal commands.
67
61
 
68
- Built-in Helper functions.
62
+ - Provides **Auto-Update Check** feature when you run `npm run dev`.
63
+ - Handles `upgrade` command to sync your project with the latest Lapeh version without breaking your code.
69
64
 
70
- - `validator.ts`: Powerful and expressive input validation.
71
- - `response.ts`: Standard JSON response format (`sendFastSuccess`, `sendError`).
72
- - `logger.ts`: Logging system (Winston).
65
+ ---
73
66
 
74
- ## `scripts/` Folder
67
+ ## 4. ⚙️ Folder `lib/` (The Core)
75
68
 
76
- "Magic" scripts executed by `npm run`.
69
+ Lapeh's "Engine Room". Contains Express setup, Database connection, Logger, and basic Middleware.
77
70
 
78
- - `make-module.js`: New module generator (Controller).
79
- - `init-project.js`: Initial setup wizard.
80
- - `generate-jwt-secret.js`: Automatic JWT secret key generator.
71
+ > ⚠️ **Warning**: Changing the contents of this folder can make the application difficult to update to the next Lapeh version.
81
72
 
82
73
  ---
83
74
 
84
- By understanding this structure, you won't get lost when adding new features or debugging.
75
+ ## 📄 Other Important Files
76
+
77
+ - **`.env`**: **Secret Keys**. Store DB passwords and API Keys here. (Do not upload to Git!)
78
+ - **`package.json`**: List of your project's library "shopping list".
79
+ - **`ecosystem.config.js`**: Ready to deploy? This file configures how the application runs on a production server (VPS) using PM2.
@@ -1,84 +1,79 @@
1
- # Bedah Struktur Proyek
1
+ # 🏗️ Struktur Proyek
2
2
 
3
- Untuk memahami Lapeh Framework sepenuhnya, Anda perlu tahu apa fungsi setiap file dan folder. Berikut adalah "Tour" lengkap ke dalam direktori proyek.
3
+ Lapeh Framework didesain agar **intuitif**. Kami memisahkan kode aplikasi Anda (_User Space_) dari mesin framework (_Core_), sehingga Anda bisa fokus membangun fitur tanpa terganggu kompleksitas sistem.
4
4
 
5
- ## Root Directory
5
+ ## 🗺️ Peta Navigasi
6
6
 
7
- | File/Folder | Deskripsi |
8
- | :------------------- | :---------------------------------------------------------------------------- |
9
- | `bin/` | Berisi script eksekusi untuk CLI (`npx lapeh`). Anda jarang menyentuh ini. |
10
- | `doc/` | Dokumentasi proyek ini berada. |
11
- | `lib/` | **Framework Core**. Bagian internal framework yang jarang Anda sentuh. |
12
- | `scripts/` | Kumpulan script Node.js untuk utility (generator, compiler schema, dll). |
13
- | `src/` | **Source Code Utama**. 99% kodingan Anda ada di sini. |
14
- | `.env` | Variabel rahasia (Database URL, API Keys). **Jangan commit file ini ke Git!** |
15
- | `docker-compose.yml` | Konfigurasi Docker untuk menjalankan Database & Redis lokal. |
16
- | `nodemon.json` | Konfigurasi auto-restart saat development. |
17
- | `package.json` | Daftar library (dependencies) dan perintah (`npm run ...`). |
18
- | `tsconfig.json` | Konfigurasi TypeScript. |
7
+ Berikut adalah peta mental untuk memahami folder Lapeh:
19
8
 
20
- ## Folder `src/` (Source Code - User Space)
9
+ | Folder | Ikon | Fungsi | Status |
10
+ | :------------- | :--: | :------------------------------------------------------------- | :---------------- |
11
+ | **`src/`** | 🏠 | **KODE APLIKASI ANDA**. Disini Anda bekerja setiap hari. | **Wajib Edit** |
12
+ | **`scripts/`** | 🤖 | **Asisten Otomatis**. Script untuk rilis, generate modul, dll. | **Boleh Edit** |
13
+ | **`bin/`** | 🚀 | **CLI Tools**. Entry point untuk perintah `npx lapeh`. | **Jarang Edit** |
14
+ | **`lib/`** | ⚙️ | **Mesin Framework**. Core logic server & database. | **DILARANG Edit** |
21
15
 
22
- Ini adalah tempat Anda bekerja setiap hari.
16
+ ---
17
+
18
+ ## 1. 🏠 Folder `src/` (User Space)
23
19
 
24
- ### `src/modules/` (Modular Architecture)
20
+ Ini adalah "kandang" Anda. 99% kode fitur baru akan ditulis di sini.
25
21
 
26
- Lapeh menggunakan pendekatan **Modular**. Setiap fitur dikelompokkan dalam satu folder modul agar kode lebih terorganisir.
22
+ ### `src/modules/` (Arsitektur Modular)
27
23
 
28
- Contoh struktur modul `Auth`:
24
+ Lapeh menggunakan pendekatan **Modular**. Satu fitur = Satu folder.
29
25
 
30
- - `Auth/auth.controller.ts`: Logika aplikasi (Controller).
26
+ - **Contoh**: Folder `src/modules/Auth/` berisi semua logika login/register.
27
+ - **Keuntungan**: Kode rapi, mudah dicari, dan mudah dihapus jika fitur tidak dipakai lagi.
31
28
 
32
29
  ### `src/routes/`
33
30
 
34
- Mendefinisikan URL endpoint.
31
+ Pintu gerbang API Anda.
35
32
 
36
- - Menghubungkan URL (misal `/api/login`) ke fungsi di Controller.
37
- - Menempelkan Middleware (misal `requireAuth`).
33
+ - Mendefinisikan URL (misal: `/api/users`).
34
+ - Menghubungkan URL ke **Controller**.
38
35
 
39
36
  ### `src/config/`
40
37
 
41
- Konfigurasi aplikasi yang bersifat statis.
42
-
43
- - `app.ts`: Konfigurasi umum aplikasi.
44
- - `cors.ts`: Konfigurasi CORS (Cross-Origin Resource Sharing).
45
-
46
- ## Folder `lib/` (Framework Internals)
38
+ Pusat pengaturan aplikasi.
47
39
 
48
- Bagian ini mirip dengan `node_modules` atau folder `.next` di Next.js. Ini adalah mesin framework.
40
+ - `app.ts`: Konfigurasi global.
41
+ - `cors.ts`: Keamanan akses domain.
49
42
 
50
- ### `lib/core/`
43
+ ---
51
44
 
52
- Bagian "Mesin" framework.
45
+ ## 2. 🤖 Folder `scripts/` (Asisten Robot)
53
46
 
54
- - `server.ts`: Setup Express App.
55
- - `redis.ts`: Koneksi Redis.
56
- - `serializer.ts`: Logic caching JSON Schema.
47
+ Jangan lakukan hal membosankan secara manual! Lapeh menyediakan robot di sini:
57
48
 
58
- ### `lib/middleware/`
49
+ - **`release.js`**: **Super Script** untuk merilis versi baru.
50
+ - ✨ Otomatis bump versi (package.json).
51
+ - 📝 **Auto-Blog**: Membuat artikel rilis otomatis dari history Git Commit.
52
+ - 🔄 **Auto-Sync**: Sinkronisasi dokumentasi ke website.
53
+ - 🚀 Push ke Git & Publish ke NPM dalam satu perintah.
54
+ - **`make-module.js`**: Generator untuk membuat struktur folder module baru dalam sekejap.
59
55
 
60
- Middleware bawaan framework.
56
+ ---
61
57
 
62
- - `auth.ts`: Cek JWT Token.
63
- - `rateLimit.ts`: Batasi jumlah request.
64
- - `requestLogger.ts`: Log setiap request yang masuk.
58
+ ## 3. 🚀 Folder `bin/` (CLI & Update)
65
59
 
66
- ### `lib/utils/`
60
+ Folder ini menangani perintah terminal `npx lapeh`.
67
61
 
68
- Fungsi bantuan (Helper) bawaan.
62
+ - Menyediakan fitur **Auto-Update Check** saat Anda menjalankan `npm run dev`.
63
+ - Menangani perintah `upgrade` untuk menyinkronkan proyek Anda dengan versi Lapeh terbaru tanpa merusak kode Anda.
69
64
 
70
- - `validator.ts`: Validasi input yang kuat dan ekspresif.
71
- - `response.ts`: Standar format JSON response (`sendFastSuccess`, `sendError`).
72
- - `logger.ts`: Sistem logging (Winston).
65
+ ---
73
66
 
74
- ## Folder `scripts/`
67
+ ## 4. ⚙️ Folder `lib/` (The Core)
75
68
 
76
- Script-script "Magic" yang dijalankan `npm run`.
69
+ "Ruang Mesin" Lapeh. Berisi setup Express, koneksi Database, Logger, dan Middleware dasar.
77
70
 
78
- - `make-module.js`: Generator modul baru (Controller).
79
- - `init-project.js`: Wizard setup awal.
80
- - `generate-jwt-secret.js`: Generator kunci rahasia JWT otomatis.
71
+ > ⚠️ **Peringatan**: Mengubah isi folder ini bisa membuat aplikasi sulit di-update ke versi Lapeh selanjutnya.
81
72
 
82
73
  ---
83
74
 
84
- Dengan memahami struktur ini, Anda tidak akan tersesat saat ingin menambah fitur baru atau mencari bug.
75
+ ## 📄 File Penting Lainnya
76
+
77
+ - **`.env`**: **Kunci Rahasia**. Simpan password DB dan API Key disini. (Jangan di-upload ke Git!)
78
+ - **`package.json`**: Daftar "belanjaan" library proyek Anda.
79
+ - **`ecosystem.config.js`**: Siap deploy? File ini mengatur cara aplikasi berjalan di server production (VPS) menggunakan PM2.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lapeh",
3
- "version": "3.0.4",
3
+ "version": "3.0.6",
4
4
  "description": "Framework API Express yang siap pakai (Standardized)",
5
5
  "engines": {
6
6
  "node": ">=18.0.0",
@@ -73,8 +73,64 @@ function incrementPatch(version) {
73
73
  return parts.join('.');
74
74
  }
75
75
 
76
+ // Helper to generate auto commit message
77
+ function generateAutoCommitMessage() {
78
+ try {
79
+ const status = execSync('git status --porcelain', { stdio: 'pipe' }).toString().trim();
80
+ if (!status) return 'chore: no changes detected';
81
+
82
+ const files = status.split('\n').map(line => line.substring(3).trim());
83
+
84
+ const hasDocs = files.some(f => f.startsWith('website/') || f.startsWith('doc/') || f.endsWith('.md'));
85
+ const hasScripts = files.some(f => f.startsWith('scripts/'));
86
+ const hasPackage = files.some(f => f.includes('package.json'));
87
+ const hasSrc = files.some(f => !f.startsWith('website/') && !f.startsWith('doc/') && !f.startsWith('scripts/') && !f.includes('package.json') && !f.startsWith('.'));
88
+
89
+ let types = [];
90
+ if (hasDocs) types.push('docs');
91
+ if (hasScripts) types.push('scripts');
92
+ if (hasPackage) types.push('deps');
93
+ if (hasSrc) types.push('feat/fix');
94
+
95
+ if (types.length === 0) return 'chore: update project files';
96
+
97
+ if (hasDocs && !hasSrc && !hasScripts) return 'docs: update documentation';
98
+ if (hasScripts && !hasSrc) return 'chore: update build scripts';
99
+
100
+ return `chore: update ${types.join(', ')}`;
101
+ } catch (e) {
102
+ return 'chore: update project files';
103
+ }
104
+ }
105
+
76
106
  async function main() {
77
107
  try {
108
+ // 0. Quick Git Update Check
109
+ const quickGit = await question('\n⚡ Apakah ini hanya Quick Git Update (tanpa rilis versi)? (y/n): ');
110
+ if (quickGit.toLowerCase() === 'y') {
111
+ console.log('\n🤖 Auto-generating commit message...');
112
+ const commitMsg = generateAutoCommitMessage();
113
+ console.log(`📝 Commit Message: "${commitMsg}"`);
114
+
115
+ try {
116
+ console.log('🔄 Syncing documentation (just in case)...');
117
+ try {
118
+ execSync('node scripts/sync-docs.js', { cwd: websiteDir, stdio: 'inherit' });
119
+ } catch (e) {
120
+ console.log('⚠️ Warning: Doc sync failed, continuing...');
121
+ }
122
+
123
+ execSync('git add .', { stdio: 'inherit' });
124
+ execSync(`git commit -m "${commitMsg}"`, { stdio: 'inherit' });
125
+ execSync('git push origin HEAD', { stdio: 'inherit' });
126
+ console.log('✅ Quick Git Update selesai!');
127
+ process.exit(0);
128
+ } catch (e) {
129
+ console.error('❌ Error during git operations:', e.message);
130
+ process.exit(1);
131
+ }
132
+ }
133
+
78
134
  // 1. Check versions and ask for new one
79
135
  console.log('🔍 Checking npm version...');
80
136
  const npmVersion = getNpmVersion();
@@ -139,10 +195,10 @@ async function main() {
139
195
 
140
196
  // Indonesian Blog Content
141
197
  const idContent = `---
142
- title: Rilis v${newVersion} - ${titleID}
198
+ title: "Rilis v${newVersion} - ${titleID.replace(/"/g, '\\"')}"
143
199
  date: ${date}
144
200
  author: Tim Lapeh
145
- description: ${descriptionID}
201
+ description: "${descriptionID.replace(/"/g, '\\"')}"
146
202
  ---
147
203
 
148
204
  # Rilis v${newVersion}: ${titleID}
@@ -168,10 +224,10 @@ Terima kasih telah menggunakan Lapeh Framework!
168
224
 
169
225
  // English Blog Content
170
226
  const enContent = `---
171
- title: Release v${newVersion} - ${titleEN}
227
+ title: "Release v${newVersion} - ${titleEN.replace(/"/g, '\\"')}"
172
228
  date: ${date}
173
229
  author: Lapeh Team
174
- description: ${descriptionEN}
230
+ description: "${descriptionEN.replace(/"/g, '\\"')}"
175
231
  ---
176
232
 
177
233
  # Release v${newVersion}: ${titleEN}
@@ -208,12 +264,18 @@ Thank you for using Lapeh Framework!
208
264
  // 3. Question: Documentation
209
265
  const updateDocs = await question('\n2. Apa Anda ingin update dokumentasi? (y/n): ');
210
266
  if (updateDocs.toLowerCase() === 'y') {
211
- console.log('\n📚 Documentation Reminder:');
267
+ console.log('\n📚 Documentation Update:');
268
+ console.log('Sistem akan menjalankan sinkronisasi otomatis:');
269
+ console.log(' - Menyalin file dari `doc/id` ke `website/docs`');
270
+ console.log(' - Menyalin file dari `doc/en` ke `website/en/docs`');
271
+ console.log(' - Mengubah nama file menjadi format URL-friendly (contoh: GETTING_STARTED.md -> getting-started.md)');
272
+
273
+ console.log('\n⚠️ Manual Action Required (If applicable):');
212
274
  console.log('Jika ada package/method baru, silakan update file berikut secara manual sekarang:');
213
275
  console.log(' - website/docs/packages.md');
214
276
  console.log(' - website/docs/api.md');
215
277
 
216
- await question('Tekan Enter jika Anda sudah selesai update manual (atau jika tidak diperlukan)...');
278
+ await question('Tekan Enter untuk menjalankan sinkronisasi otomatis (setelah Anda selesai update manual)...');
217
279
 
218
280
  console.log('🔄 Syncing documentation...');
219
281
  execSync('node scripts/sync-docs.js', { cwd: websiteDir, stdio: 'inherit' });
@@ -228,10 +290,30 @@ Thank you for using Lapeh Framework!
228
290
  ? `chore: release v${newVersion} - ${blogTitleEN}`
229
291
  : `chore: release v${newVersion}`;
230
292
 
231
- execSync('git add .', { stdio: 'inherit' });
232
- execSync(`git commit -m "${commitMsg}"`, { stdio: 'inherit' });
293
+ try {
294
+ execSync('git add .', { stdio: 'inherit' });
295
+ execSync(`git commit -m "${commitMsg}"`, { stdio: 'inherit' });
296
+ } catch (e) {
297
+ console.log('⚠️ No changes to commit or commit failed. Continuing...');
298
+ }
299
+
300
+ try {
301
+ // Delete tag if it exists locally to avoid "already exists" error
302
+ execSync(`git tag -d v${newVersion}`, { stdio: 'ignore' });
303
+ } catch (e) {
304
+ // Ignore if tag doesn't exist
305
+ }
306
+
233
307
  execSync(`git tag v${newVersion}`, { stdio: 'inherit' });
234
- execSync(`git push origin HEAD && git push origin v${newVersion}`, { stdio: 'inherit' });
308
+ execSync(`git push origin HEAD`, { stdio: 'inherit' });
309
+
310
+ try {
311
+ execSync(`git push origin v${newVersion}`, { stdio: 'inherit' });
312
+ } catch (e) {
313
+ console.log('⚠️ Tag push failed. Trying force push (updating existing tag)...');
314
+ execSync(`git push origin v${newVersion} --force`, { stdio: 'inherit' });
315
+ }
316
+
235
317
  console.log('✅ Git push & tag complete');
236
318
  } else {
237
319
  console.log('⏭️ Skipping Git push.');
@@ -240,8 +322,20 @@ Thank you for using Lapeh Framework!
240
322
  // 5. Question: NPM
241
323
  const publishNpm = await question('\n4. Apa ingin publish ke NPM? (y/n): ');
242
324
  if (publishNpm.toLowerCase() === 'y') {
243
- execSync('npm publish', { stdio: 'inherit' });
244
- console.log(' NPM publish complete');
325
+ try {
326
+ execSync('npm publish', { stdio: 'inherit' });
327
+ console.log('✅ NPM publish complete');
328
+ } catch (error) {
329
+ console.log('\n⚠️ NPM Publish failed. This might be due to 2FA.');
330
+ const otp = await question('🔐 Masukkan kode OTP (Authenticator App) Anda: ');
331
+ if (otp && otp.trim() !== '') {
332
+ execSync(`npm publish --otp=${otp.trim()}`, { stdio: 'inherit' });
333
+ console.log('✅ NPM publish complete');
334
+ } else {
335
+ console.log('❌ NPM publish aborted.');
336
+ throw error;
337
+ }
338
+ }
245
339
  } else {
246
340
  console.log('⏭️ Skipping NPM publish.');
247
341
  }
@@ -19,6 +19,11 @@
19
19
  "date": 1767106155460,
20
20
  "name": "E:\\PROJECT\\ANY\\2026\\lapeh\\storage\\logs\\lapeh-2025-12-30.log",
21
21
  "hash": "f090b6426f3343ed97dadce1dfe5cf1b15b0e4f173b3a0b4fbfe19d9f86dec03"
22
+ },
23
+ {
24
+ "date": 1767116159016,
25
+ "name": "E:\\PROJECT\\ANY\\2026\\lapeh\\storage\\logs\\lapeh-2025-12-31.log",
26
+ "hash": "d0e8ef584ec2262b3b615e4747e50c0f7f2bf687c8d4264b6881edbe00f039e0"
22
27
  }
23
28
  ],
24
29
  "hashType": "sha256"
File without changes