lapeh 2.4.7 → 2.4.9
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 +54 -5
- package/dist/lib/bootstrap.d.ts +1 -0
- package/dist/lib/bootstrap.d.ts.map +1 -1
- package/dist/lib/bootstrap.js +39 -12
- package/dist/lib/utils/validator.d.ts +3 -3
- package/dist/lib/utils/validator.d.ts.map +1 -1
- package/dist/lib/utils/validator.js +4 -4
- package/dist/src/controllers/authController.js +1 -1
- package/doc/en/CHANGELOG.md +17 -2
- package/doc/en/CHEATSHEET.md +1 -1
- package/doc/en/FEATURES.md +3 -3
- package/doc/en/INTRODUCTION.md +10 -8
- package/doc/en/STRUCTURE.md +1 -1
- package/doc/id/CHANGELOG.md +17 -2
- package/doc/id/CHEATSHEET.md +1 -1
- package/doc/id/FEATURES.md +5 -3
- package/doc/id/INTRODUCTION.md +10 -8
- package/doc/id/STRUCTURE.md +1 -1
- package/lib/bootstrap.ts +53 -21
- package/lib/utils/validator.ts +4 -4
- package/package.json +9 -3
- package/readme.md +1 -1
- package/src/controllers/authController.ts +1 -1
- package/storage/logs/.0337f5062fe676994d1dc340156e089444e3d6e0-audit.json +5 -0
- package/storage/logs/lapeh-2025-12-28.log +725 -0
- package/tsconfig.json +2 -1
package/bin/index.js
CHANGED
|
@@ -231,7 +231,7 @@ function runBuild() {
|
|
|
231
231
|
|
|
232
232
|
// Compile TS
|
|
233
233
|
try {
|
|
234
|
-
execSync('npx tsc && npx tsc-alias', { stdio: 'inherit' });
|
|
234
|
+
execSync('npx tsc -p tsconfig.build.json && npx tsc-alias -p tsconfig.build.json', { stdio: 'inherit' });
|
|
235
235
|
} catch (e) {
|
|
236
236
|
console.error('❌ Build failed.');
|
|
237
237
|
process.exit(1);
|
|
@@ -255,7 +255,7 @@ async function upgradeProject() {
|
|
|
255
255
|
|
|
256
256
|
// Files/Folders to overwrite/copy
|
|
257
257
|
const filesToSync = [
|
|
258
|
-
'bin', //
|
|
258
|
+
// 'bin', // Removed: CLI script is managed by package
|
|
259
259
|
'lib', // Ensure core framework files are updated
|
|
260
260
|
'scripts',
|
|
261
261
|
'docker-compose.yml',
|
|
@@ -268,7 +268,47 @@ async function upgradeProject() {
|
|
|
268
268
|
'src/prisma.ts', // Core framework file
|
|
269
269
|
];
|
|
270
270
|
|
|
271
|
-
// Helper to copy
|
|
271
|
+
// Helper to sync directory (copy new/updated, delete removed)
|
|
272
|
+
function syncDirectory(src, dest) {
|
|
273
|
+
if (!fs.existsSync(src)) return;
|
|
274
|
+
|
|
275
|
+
// Ensure dest exists
|
|
276
|
+
if (!fs.existsSync(dest)) {
|
|
277
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// 1. Copy/Update files from src to dest
|
|
281
|
+
const srcEntries = fs.readdirSync(src, { withFileTypes: true });
|
|
282
|
+
const srcEntryNames = new Set();
|
|
283
|
+
|
|
284
|
+
for (const entry of srcEntries) {
|
|
285
|
+
srcEntryNames.add(entry.name);
|
|
286
|
+
const srcPath = path.join(src, entry.name);
|
|
287
|
+
const destPath = path.join(dest, entry.name);
|
|
288
|
+
|
|
289
|
+
if (entry.isDirectory()) {
|
|
290
|
+
syncDirectory(srcPath, destPath);
|
|
291
|
+
} else {
|
|
292
|
+
fs.copyFileSync(srcPath, destPath);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// 2. Delete files in dest that are not in src (only if we are syncing a folder)
|
|
297
|
+
const destEntries = fs.readdirSync(dest, { withFileTypes: true });
|
|
298
|
+
for (const entry of destEntries) {
|
|
299
|
+
if (!srcEntryNames.has(entry.name)) {
|
|
300
|
+
const destPath = path.join(dest, entry.name);
|
|
301
|
+
console.log(`🗑️ Removing obsolete file/directory: ${destPath}`);
|
|
302
|
+
if (entry.isDirectory()) {
|
|
303
|
+
fs.rmSync(destPath, { recursive: true, force: true });
|
|
304
|
+
} else {
|
|
305
|
+
fs.unlinkSync(destPath);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Helper to copy recursive (legacy, kept for other uses if any, but replaced by syncDirectory for upgrade)
|
|
272
312
|
function copyRecursive(src, dest) {
|
|
273
313
|
if (!fs.existsSync(src)) return;
|
|
274
314
|
const stats = fs.statSync(src);
|
|
@@ -309,8 +349,17 @@ async function upgradeProject() {
|
|
|
309
349
|
const destPath = path.join(currentDir, item);
|
|
310
350
|
|
|
311
351
|
if (fs.existsSync(srcPath)) {
|
|
312
|
-
|
|
313
|
-
|
|
352
|
+
const stats = fs.statSync(srcPath);
|
|
353
|
+
if (stats.isDirectory()) {
|
|
354
|
+
console.log(`🔄 Syncing directory ${item}...`);
|
|
355
|
+
syncDirectory(srcPath, destPath);
|
|
356
|
+
} else {
|
|
357
|
+
console.log(`🔄 Updating file ${item}...`);
|
|
358
|
+
// Ensure dir exists
|
|
359
|
+
const destDir = path.dirname(destPath);
|
|
360
|
+
if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
|
|
361
|
+
fs.copyFileSync(srcPath, destPath);
|
|
362
|
+
}
|
|
314
363
|
}
|
|
315
364
|
}
|
|
316
365
|
|
package/dist/lib/bootstrap.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../lib/bootstrap.ts"],"names":[],"mappings":"AAmBA,wBAAsB,SAAS,
|
|
1
|
+
{"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../lib/bootstrap.ts"],"names":[],"mappings":"AAmBA,wBAAsB,SAAS,yDAyG9B;AAED,wBAAsB,SAAS,kBAwD9B"}
|
package/dist/lib/bootstrap.js
CHANGED
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createApp = createApp;
|
|
6
7
|
exports.bootstrap = bootstrap;
|
|
7
8
|
const dotenv_1 = __importDefault(require("dotenv"));
|
|
8
9
|
dotenv_1.default.config();
|
|
@@ -21,18 +22,11 @@ const error_1 = require("./middleware/error");
|
|
|
21
22
|
const rateLimit_1 = require("./middleware/rateLimit");
|
|
22
23
|
const requestLogger_1 = require("./middleware/requestLogger");
|
|
23
24
|
const response_1 = require("./utils/response");
|
|
24
|
-
async function
|
|
25
|
+
async function createApp() {
|
|
25
26
|
// Register aliases for production runtime
|
|
26
27
|
// Since user code (compiled JS) uses require('@lapeh/...')
|
|
27
28
|
// We map '@lapeh' to the directory containing this file (lib/ or dist/lib/)
|
|
28
29
|
module_alias_1.default.addAlias("@lapeh", __dirname);
|
|
29
|
-
// Validasi Environment Variables
|
|
30
|
-
const requiredEnvs = ["DATABASE_URL", "JWT_SECRET"];
|
|
31
|
-
const missingEnvs = requiredEnvs.filter((key) => !process.env[key]);
|
|
32
|
-
if (missingEnvs.length > 0) {
|
|
33
|
-
console.error(`❌ Missing required environment variables: ${missingEnvs.join(", ")}`);
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
36
30
|
const app = (0, express_1.default)();
|
|
37
31
|
app.disable("x-powered-by");
|
|
38
32
|
app.use((0, compression_1.default)());
|
|
@@ -76,15 +70,48 @@ async function bootstrap() {
|
|
|
76
70
|
? path_1.default.join(process.cwd(), "dist", "src", "routes")
|
|
77
71
|
: path_1.default.join(process.cwd(), "src", "routes");
|
|
78
72
|
// Gunakan require agar sinkron dan mudah dicatch
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
73
|
+
// Check if file exists before requiring to avoid crash in tests/clean env
|
|
74
|
+
try {
|
|
75
|
+
// In test environment, we might need to point to src/routes explicitly if not compiled
|
|
76
|
+
// const routesPath = process.env.NODE_ENV === 'test'
|
|
77
|
+
// ? path.join(process.cwd(), "src", "routes", "index.ts")
|
|
78
|
+
// : userRoutesPath;
|
|
79
|
+
// Note: For TS files in jest, we rely on ts-jest handling 'require' if it points to .ts or we need to use 'import'
|
|
80
|
+
// But 'require' in jest with ts-jest should work if configured.
|
|
81
|
+
// However, require(path) with .ts extension might be tricky.
|
|
82
|
+
// Let's stick to userRoutesPath but maybe adjust for test env.
|
|
83
|
+
// Check if we are in test environment and using ts-jest
|
|
84
|
+
// If so, we might need to import the TS file directly via relative path if alias is not working for require
|
|
85
|
+
const { apiRouter } = require(userRoutesPath);
|
|
86
|
+
app.use("/api", apiRouter);
|
|
87
|
+
console.log(`✅ User routes loaded successfully from ${isProduction ? "dist/" : ""}src/routes`);
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
// If it's just missing module, maybe we are in test mode or fresh install
|
|
91
|
+
if (process.env.NODE_ENV !== "test") {
|
|
92
|
+
console.warn(`⚠️ Could not load user routes from ${userRoutesPath}. (This is expected during initial setup or if src/routes is missing)`);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
// In test mode, we really want to know if it failed to load
|
|
96
|
+
console.error(`Error loading routes in test mode from ${userRoutesPath}:`, e);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
82
99
|
}
|
|
83
100
|
catch (error) {
|
|
84
|
-
console.warn("⚠️ Could not load user routes. Make sure you export 'apiRouter'.");
|
|
85
101
|
console.error(error);
|
|
86
102
|
}
|
|
87
103
|
app.use(error_1.errorHandler);
|
|
104
|
+
return app;
|
|
105
|
+
}
|
|
106
|
+
async function bootstrap() {
|
|
107
|
+
// Validasi Environment Variables
|
|
108
|
+
const requiredEnvs = ["DATABASE_URL", "JWT_SECRET"];
|
|
109
|
+
const missingEnvs = requiredEnvs.filter((key) => !process.env[key]);
|
|
110
|
+
if (missingEnvs.length > 0) {
|
|
111
|
+
console.error(`❌ Missing required environment variables: ${missingEnvs.join(", ")}`);
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
const app = await createApp();
|
|
88
115
|
const port = process.env.PORT ? Number(process.env.PORT) : 4000;
|
|
89
116
|
const server = http_1.default.createServer(app);
|
|
90
117
|
(0, realtime_1.initRealtime)(server);
|
|
@@ -10,11 +10,11 @@ export declare class Validator {
|
|
|
10
10
|
/**
|
|
11
11
|
* Create a new Validator instance
|
|
12
12
|
* @param data The input data to validate
|
|
13
|
-
* @param schema Zod schema or object of Zod schemas /
|
|
14
|
-
* @param messages Optional custom error messages (
|
|
13
|
+
* @param schema Zod schema or object of Zod schemas / string-based rules
|
|
14
|
+
* @param messages Optional custom error messages (style: 'field.rule' => 'message')
|
|
15
15
|
*/
|
|
16
16
|
static make(data: any, schema: ZodSchema<any> | Record<string, any>, messages?: Record<string, string>): Validator;
|
|
17
|
-
private static
|
|
17
|
+
private static parseStringRule;
|
|
18
18
|
/**
|
|
19
19
|
* Check if validation fails
|
|
20
20
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../lib/utils/validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAK,SAAS,EAAsB,MAAM,KAAK,CAAC;AAGvD,qBAAa,SAAS;IACpB,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,MAAM,CAAkB;gBAG9B,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5C,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM;IAavC;;;;;OAKG;IACH,MAAM,CAAC,IAAI,CACT,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5C,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM;IAiDvC,OAAO,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../lib/utils/validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAK,SAAS,EAAsB,MAAM,KAAK,CAAC;AAGvD,qBAAa,SAAS;IACpB,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,MAAM,CAAkB;gBAG9B,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5C,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM;IAavC;;;;;OAKG;IACH,MAAM,CAAC,IAAI,CACT,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5C,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM;IAiDvC,OAAO,CAAC,MAAM,CAAC,eAAe;IAyN9B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC;IAK/B;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAIhC;;OAEG;IACH,MAAM;IAKN;;OAEG;IACG,SAAS;YAUD,GAAG;IAgBjB,OAAO,CAAC,YAAY;IAuBpB,OAAO,CAAC,gBAAgB;CAoDzB"}
|
|
@@ -21,8 +21,8 @@ class Validator {
|
|
|
21
21
|
/**
|
|
22
22
|
* Create a new Validator instance
|
|
23
23
|
* @param data The input data to validate
|
|
24
|
-
* @param schema Zod schema or object of Zod schemas /
|
|
25
|
-
* @param messages Optional custom error messages (
|
|
24
|
+
* @param schema Zod schema or object of Zod schemas / string-based rules
|
|
25
|
+
* @param messages Optional custom error messages (style: 'field.rule' => 'message')
|
|
26
26
|
*/
|
|
27
27
|
static make(data, schema, messages = {}) {
|
|
28
28
|
if (schema instanceof zod_1.ZodSchema) {
|
|
@@ -44,7 +44,7 @@ class Validator {
|
|
|
44
44
|
sameRules.push({ field: key, target: args });
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
-
parsedSchema[key] = Validator.
|
|
47
|
+
parsedSchema[key] = Validator.parseStringRule(rule);
|
|
48
48
|
}
|
|
49
49
|
else {
|
|
50
50
|
parsedSchema[key] = rule;
|
|
@@ -66,7 +66,7 @@ class Validator {
|
|
|
66
66
|
}
|
|
67
67
|
return new Validator(data, objectSchema, messages);
|
|
68
68
|
}
|
|
69
|
-
static
|
|
69
|
+
static parseStringRule(rule) {
|
|
70
70
|
const rules = Array.isArray(rule)
|
|
71
71
|
? rule
|
|
72
72
|
: rule.split("|").map((r) => r.trim());
|
|
@@ -107,7 +107,7 @@ async function register(req, res) {
|
|
|
107
107
|
},
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
|
-
(0, response_1.sendFastSuccess)(res,
|
|
110
|
+
(0, response_1.sendFastSuccess)(res, 201, registerSerializer, {
|
|
111
111
|
status: "success",
|
|
112
112
|
message: "Registration successful",
|
|
113
113
|
data: {
|
package/doc/en/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
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-28] - Sunday, December 28, 2025 - Upgrade & Testing Improvements (v2.4.9)
|
|
6
|
+
|
|
7
|
+
### 🚀 Features & Fixes
|
|
8
|
+
|
|
9
|
+
- **Smart Upgrade CLI**:
|
|
10
|
+
|
|
11
|
+
- Updated `npx lapeh upgrade` to perform full synchronization (mirroring).
|
|
12
|
+
- Files removed in the latest framework version are now automatically removed from user projects, keeping them clean.
|
|
13
|
+
- Removed `bin` folder from synchronization as it is managed by the package.
|
|
14
|
+
|
|
15
|
+
- **Comprehensive Testing Support**:
|
|
16
|
+
- Updated `tsconfig.json` to support `@lapeh/*` path aliases within the `tests` folder.
|
|
17
|
+
- The `tests` folder is now excluded from production builds (via `tsconfig.build.json`), resulting in a cleaner `dist/` folder.
|
|
18
|
+
- Jest documentation and configuration have been adjusted for seamless integration.
|
|
19
|
+
|
|
5
20
|
## [2025-12-28] - Minggu, 28 Desember 2025 - Perbaikan Kompatibilitas & Automasi
|
|
6
21
|
|
|
7
22
|
### 🛠️ Perbaikan Bug (Bug Fixes)
|
|
@@ -61,8 +76,8 @@ File ini mencatat semua perubahan, pembaruan, dan perbaikan yang dilakukan pada
|
|
|
61
76
|
|
|
62
77
|
### 🚀 Fitur Baru
|
|
63
78
|
|
|
64
|
-
- **
|
|
65
|
-
- Implementasi utility `Validator` baru di `src/utils/validator.ts`
|
|
79
|
+
- **Expressive Validator**:
|
|
80
|
+
- Implementasi utility `Validator` baru di `src/utils/validator.ts` dengan gaya validasi yang lebih ekspresif.
|
|
66
81
|
- Mendukung rule string seperti `required|string|min:3|email`.
|
|
67
82
|
- Penambahan rule `unique` untuk pengecekan database otomatis (Prisma).
|
|
68
83
|
- Penambahan rule `mimes`, `image`, `max` (file size) untuk validasi upload file.
|
package/doc/en/CHEATSHEET.md
CHANGED
|
@@ -18,7 +18,7 @@ Referensi cepat untuk perintah dan kode yang sering digunakan.
|
|
|
18
18
|
| **`npm run db:seed`** | Isi data dummy. |
|
|
19
19
|
| **`npm run db:reset`** | Hapus DB & mulai dari nol. |
|
|
20
20
|
|
|
21
|
-
## 🛡️ Validator Rules (
|
|
21
|
+
## 🛡️ Validator Rules (Simple Syntax)
|
|
22
22
|
|
|
23
23
|
Gunakan di `Validator.make(data, rules)`.
|
|
24
24
|
|
package/doc/en/FEATURES.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
This document explains the key features of Lapeh Framework and how to use them in depth.
|
|
4
4
|
|
|
5
|
-
## 1. Data Validation (
|
|
5
|
+
## 1. Data Validation (Simple & Powerful)
|
|
6
6
|
|
|
7
|
-
The framework provides a `Validator` utility inspired by
|
|
7
|
+
The framework provides a `Validator` utility inspired by expressive modern validation styles, using `zod` behind the scenes but with an API that is more string-based and readable.
|
|
8
8
|
|
|
9
9
|
**Location:** `@lapeh/utils/validator`
|
|
10
10
|
|
|
@@ -42,7 +42,7 @@ export async function createProduct(req: Request, res: Response) {
|
|
|
42
42
|
- `image`: File must be an image (jpg, png, webp, etc).
|
|
43
43
|
- `mimes:types`: File must be a specific type (e.g., `mimes:pdf,docx`).
|
|
44
44
|
|
|
45
|
-
## 2. High Performance Response (Fastify-
|
|
45
|
+
## 2. High Performance Response (Fastify-Like)
|
|
46
46
|
|
|
47
47
|
For endpoints requiring high performance (e.g., large data lists), use schema-based serialization. This is much faster than standard Express `res.json`.
|
|
48
48
|
|
package/doc/en/INTRODUCTION.md
CHANGED
|
@@ -4,33 +4,35 @@
|
|
|
4
4
|
|
|
5
5
|
**Lapeh** is a Backend Framework for Node.js built on top of **Express** and **TypeScript**.
|
|
6
6
|
|
|
7
|
-
If you have ever used
|
|
7
|
+
If you have ever used other modern frameworks, you will feel very familiar. Lapeh adopts the philosophy of ease-of-use & clean structure, while maintaining the flexibility and speed of Express.
|
|
8
8
|
|
|
9
9
|
The name "Lapeh" comes from the Minang language which means "Loose" or "Free", symbolizing the freedom for developers to build applications quickly without being burdened by complicated configurations.
|
|
10
10
|
|
|
11
11
|
## Why was Lapeh Created?
|
|
12
12
|
|
|
13
13
|
In the Node.js ecosystem, developers often experience "Decision Fatigue":
|
|
14
|
+
|
|
14
15
|
- "Which ORM to use? Prisma, TypeORM, or Drizzle?"
|
|
15
16
|
- "Validation using Joi, Zod, or express-validator?"
|
|
16
17
|
- "How about the folder structure? MVC? Clean Architecture?"
|
|
17
18
|
- "How to handle Auth?"
|
|
18
19
|
|
|
19
20
|
Lapeh answers all of that with **Opinionated Defaults**:
|
|
21
|
+
|
|
20
22
|
1. **ORM**: Prisma (Current industry standard).
|
|
21
|
-
2. **Validation**: Zod (
|
|
23
|
+
2. **Validation**: Zod (Powerful and readable schema validation).
|
|
22
24
|
3. **Structure**: Modular MVC (Controller, Model, Route separated but cohesive).
|
|
23
25
|
4. **Auth**: Ready-to-use JWT + RBAC (Role Based Access Control).
|
|
24
26
|
|
|
25
27
|
## Comparison with Other Frameworks
|
|
26
28
|
|
|
27
|
-
| Feature
|
|
28
|
-
|
|
|
29
|
+
| Feature | Express (Raw) | NestJS | Lapeh Framework |
|
|
30
|
+
| :----------------- | :---------------------------- | :------------------------------- | :------------------------------------- |
|
|
29
31
|
| **Learning Curve** | Low (but confusing structure) | High (Angular-style, Decorators) | **Medium** (Express + Clear Structure) |
|
|
30
|
-
| **Boilerplate**
|
|
31
|
-
| **Type Safety**
|
|
32
|
-
| **Dev Speed**
|
|
33
|
-
| **Flexibility**
|
|
32
|
+
| **Boilerplate** | Empty | Very Heavy | **Just Right (Ready to use)** |
|
|
33
|
+
| **Type Safety** | Manual | Strict | **Strict (Native TypeScript)** |
|
|
34
|
+
| **Dev Speed** | Slow (manual setup) | Medium | **Fast (CLI Generator)** |
|
|
35
|
+
| **Flexibility** | Very High | Rigid | **High** |
|
|
34
36
|
|
|
35
37
|
## "The Lapeh Way" Philosophy
|
|
36
38
|
|
package/doc/en/STRUCTURE.md
CHANGED
|
@@ -67,7 +67,7 @@ Built-in framework middleware.
|
|
|
67
67
|
|
|
68
68
|
Built-in Helper functions.
|
|
69
69
|
|
|
70
|
-
- `validator.ts`:
|
|
70
|
+
- `validator.ts`: Powerful and expressive input validation.
|
|
71
71
|
- `response.ts`: Standard JSON response format (`sendFastSuccess`, `sendError`).
|
|
72
72
|
- `logger.ts`: Logging system (Winston).
|
|
73
73
|
|
package/doc/id/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
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-28] - Minggu, 28 Desember 2025 - Perbaikan Upgrade & Testing (v2.4.9)
|
|
6
|
+
|
|
7
|
+
### 🚀 Fitur & Perbaikan
|
|
8
|
+
|
|
9
|
+
- **Smart Upgrade CLI**:
|
|
10
|
+
|
|
11
|
+
- Memperbarui perintah `npx lapeh upgrade` agar melakukan sinkronisasi penuh (mirroring).
|
|
12
|
+
- File yang dihapus di versi terbaru framework sekarang akan otomatis dihapus juga dari proyek pengguna, menjaga proyek tetap bersih.
|
|
13
|
+
- Menghapus folder `bin` dari proses sinkronisasi ke proyek pengguna karena folder tersebut dikelola oleh paket.
|
|
14
|
+
|
|
15
|
+
- **Dukungan Testing Komprehensif**:
|
|
16
|
+
- Konfigurasi `tsconfig.json` diperbarui untuk mendukung path alias `@lapeh/*` di dalam folder `tests`.
|
|
17
|
+
- Folder `tests` sekarang dikecualikan dari proses build produksi (via `tsconfig.build.json`), menghasilkan folder `dist/` yang lebih bersih.
|
|
18
|
+
- Dokumentasi dan konfigurasi Jest telah disesuaikan untuk integrasi yang mulus.
|
|
19
|
+
|
|
5
20
|
## [2025-12-28] - Minggu, 28 Desember 2025 - Perbaikan Kompatibilitas & Automasi
|
|
6
21
|
|
|
7
22
|
### 🛠️ Perbaikan Bug (Bug Fixes)
|
|
@@ -61,8 +76,8 @@ File ini mencatat semua perubahan, pembaruan, dan perbaikan yang dilakukan pada
|
|
|
61
76
|
|
|
62
77
|
### 🚀 Fitur Baru
|
|
63
78
|
|
|
64
|
-
- **
|
|
65
|
-
- Implementasi utility `Validator` baru di `src/utils/validator.ts`
|
|
79
|
+
- **Expressive Validator**:
|
|
80
|
+
- Implementasi utility `Validator` baru di `src/utils/validator.ts` dengan gaya validasi yang lebih ekspresif.
|
|
66
81
|
- Mendukung rule string seperti `required|string|min:3|email`.
|
|
67
82
|
- Penambahan rule `unique` untuk pengecekan database otomatis (Prisma).
|
|
68
83
|
- Penambahan rule `mimes`, `image`, `max` (file size) untuk validasi upload file.
|
package/doc/id/CHEATSHEET.md
CHANGED
|
@@ -18,7 +18,7 @@ Referensi cepat untuk perintah dan kode yang sering digunakan.
|
|
|
18
18
|
| **`npm run db:seed`** | Isi data dummy. |
|
|
19
19
|
| **`npm run db:reset`** | Hapus DB & mulai dari nol. |
|
|
20
20
|
|
|
21
|
-
## 🛡️ Validator Rules (
|
|
21
|
+
## 🛡️ Validator Rules (Simple Syntax)
|
|
22
22
|
|
|
23
23
|
Gunakan di `Validator.make(data, rules)`.
|
|
24
24
|
|
package/doc/id/FEATURES.md
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Dokumen ini menjelaskan fitur-fitur utama Lapeh Framework dan cara penggunaannya secara mendalam.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
3. **Explicit is Better than Implicit**: Tidak ada "sihir" yang terlalu gelap. Kode controller Anda adalah kode Express biasa yang Anda mengerti.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## 1. Validasi Data (Simple & Powerful)
|
|
8
|
+
|
|
9
|
+
Framework ini menyediakan utility `Validator` yang terinspirasi dari gaya validasi modern yang ekspresif, menggunakan `zod` di belakang layar namun dengan API yang lebih string-based dan mudah dibaca.
|
|
8
10
|
|
|
9
11
|
**Lokasi:** `@lapeh/utils/validator`
|
|
10
12
|
|
|
@@ -42,7 +44,7 @@ export async function createProduct(req: Request, res: Response) {
|
|
|
42
44
|
- `image`: File harus berupa gambar (jpg, png, webp, dll).
|
|
43
45
|
- `mimes:types`: File harus tipe tertentu (misal: `mimes:pdf,docx`).
|
|
44
46
|
|
|
45
|
-
## 2. High Performance Response (Fastify-
|
|
47
|
+
## 2. High Performance Response (Fastify-Like)
|
|
46
48
|
|
|
47
49
|
Untuk endpoint yang membutuhkan performa tinggi (misalnya list data besar), gunakan serialisasi berbasis schema. Ini jauh lebih cepat daripada `res.json` standar Express.
|
|
48
50
|
|
package/doc/id/INTRODUCTION.md
CHANGED
|
@@ -4,33 +4,35 @@
|
|
|
4
4
|
|
|
5
5
|
**Lapeh** adalah framework Backend untuk Node.js yang dibangun di atas **Express** dan **TypeScript**.
|
|
6
6
|
|
|
7
|
-
Jika Anda pernah menggunakan
|
|
7
|
+
Jika Anda pernah menggunakan framework modern lainnya, Anda akan merasa sangat familiar. Lapeh mengambil filosofi kemudahan & struktur rapi, namun tetap mempertahankan fleksibilitas dan kecepatan Express.
|
|
8
8
|
|
|
9
9
|
Nama "Lapeh" diambil dari bahasa Minang yang berarti "Lepas" atau "Bebas", melambangkan kebebasan developer untuk membangun aplikasi dengan cepat tanpa terbebani konfigurasi yang rumit.
|
|
10
10
|
|
|
11
11
|
## Mengapa Lapeh Dibuat?
|
|
12
12
|
|
|
13
13
|
Di ekosistem Node.js, developer sering mengalami "Decision Fatigue" (Kelelahan memilih):
|
|
14
|
+
|
|
14
15
|
- "Pakai ORM apa? Prisma, TypeORM, atau Drizzle?"
|
|
15
16
|
- "Validasi pakai Joi, Zod, atau express-validator?"
|
|
16
17
|
- "Struktur foldernya gimana? MVC? Clean Architecture?"
|
|
17
18
|
- "Auth-nya gimana?"
|
|
18
19
|
|
|
19
20
|
Lapeh menjawab semua itu dengan **Opinionated Defaults**:
|
|
21
|
+
|
|
20
22
|
1. **ORM**: Prisma (Standar industri saat ini).
|
|
21
|
-
2. **Validasi**: Zod (
|
|
23
|
+
2. **Validasi**: Zod (Validasi skema yang kuat dan mudah dibaca).
|
|
22
24
|
3. **Struktur**: MVC Modular (Controller, Model, Route terpisah tapi kohesif).
|
|
23
25
|
4. **Auth**: JWT + RBAC (Role Based Access Control) siap pakai.
|
|
24
26
|
|
|
25
27
|
## Perbandingan dengan Framework Lain
|
|
26
28
|
|
|
27
|
-
| Fitur
|
|
28
|
-
|
|
|
29
|
+
| Fitur | Express (Raw) | NestJS | Lapeh Framework |
|
|
30
|
+
| :----------------- | :----------------------------- | :--------------------------------- | :------------------------------------ |
|
|
29
31
|
| **Learning Curve** | Rendah (tapi bingung struktur) | Tinggi (Angular-style, Decorators) | **Sedang** (Express + Struktur Jelas) |
|
|
30
|
-
| **Boilerplate**
|
|
31
|
-
| **Type Safety**
|
|
32
|
-
| **Kecepatan Dev**
|
|
33
|
-
| **Fleksibilitas**
|
|
32
|
+
| **Boilerplate** | Kosong | Sangat Banyak | **Pas (Ready to use)** |
|
|
33
|
+
| **Type Safety** | Manual | Strict | **Strict (TypeScript Native)** |
|
|
34
|
+
| **Kecepatan Dev** | Lambat (setup manual) | Sedang | **Cepat (CLI Generator)** |
|
|
35
|
+
| **Fleksibilitas** | Sangat Tinggi | Kaku | **Tinggi** |
|
|
34
36
|
|
|
35
37
|
## Filosofi "The Lapeh Way"
|
|
36
38
|
|
package/doc/id/STRUCTURE.md
CHANGED
|
@@ -67,7 +67,7 @@ Middleware bawaan framework.
|
|
|
67
67
|
|
|
68
68
|
Fungsi bantuan (Helper) bawaan.
|
|
69
69
|
|
|
70
|
-
- `validator.ts`: Validasi input
|
|
70
|
+
- `validator.ts`: Validasi input yang kuat dan ekspresif.
|
|
71
71
|
- `response.ts`: Standar format JSON response (`sendFastSuccess`, `sendError`).
|
|
72
72
|
- `logger.ts`: Sistem logging (Winston).
|
|
73
73
|
|
package/lib/bootstrap.ts
CHANGED
|
@@ -17,22 +17,12 @@ import { apiLimiter } from "./middleware/rateLimit";
|
|
|
17
17
|
import { requestLogger } from "./middleware/requestLogger";
|
|
18
18
|
import { sendSuccess } from "./utils/response";
|
|
19
19
|
|
|
20
|
-
export async function
|
|
20
|
+
export async function createApp() {
|
|
21
21
|
// Register aliases for production runtime
|
|
22
22
|
// Since user code (compiled JS) uses require('@lapeh/...')
|
|
23
23
|
// We map '@lapeh' to the directory containing this file (lib/ or dist/lib/)
|
|
24
24
|
moduleAlias.addAlias("@lapeh", __dirname);
|
|
25
25
|
|
|
26
|
-
// Validasi Environment Variables
|
|
27
|
-
const requiredEnvs = ["DATABASE_URL", "JWT_SECRET"];
|
|
28
|
-
const missingEnvs = requiredEnvs.filter((key) => !process.env[key]);
|
|
29
|
-
if (missingEnvs.length > 0) {
|
|
30
|
-
console.error(
|
|
31
|
-
`❌ Missing required environment variables: ${missingEnvs.join(", ")}`
|
|
32
|
-
);
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
26
|
const app = express();
|
|
37
27
|
|
|
38
28
|
app.disable("x-powered-by");
|
|
@@ -88,22 +78,64 @@ export async function bootstrap() {
|
|
|
88
78
|
: path.join(process.cwd(), "src", "routes");
|
|
89
79
|
|
|
90
80
|
// Gunakan require agar sinkron dan mudah dicatch
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
81
|
+
// Check if file exists before requiring to avoid crash in tests/clean env
|
|
82
|
+
try {
|
|
83
|
+
// In test environment, we might need to point to src/routes explicitly if not compiled
|
|
84
|
+
// const routesPath = process.env.NODE_ENV === 'test'
|
|
85
|
+
// ? path.join(process.cwd(), "src", "routes", "index.ts")
|
|
86
|
+
// : userRoutesPath;
|
|
87
|
+
|
|
88
|
+
// Note: For TS files in jest, we rely on ts-jest handling 'require' if it points to .ts or we need to use 'import'
|
|
89
|
+
// But 'require' in jest with ts-jest should work if configured.
|
|
90
|
+
|
|
91
|
+
// However, require(path) with .ts extension might be tricky.
|
|
92
|
+
// Let's stick to userRoutesPath but maybe adjust for test env.
|
|
93
|
+
|
|
94
|
+
// Check if we are in test environment and using ts-jest
|
|
95
|
+
// If so, we might need to import the TS file directly via relative path if alias is not working for require
|
|
96
|
+
|
|
97
|
+
const { apiRouter } = require(userRoutesPath);
|
|
98
|
+
app.use("/api", apiRouter);
|
|
99
|
+
console.log(
|
|
100
|
+
`✅ User routes loaded successfully from ${
|
|
101
|
+
isProduction ? "dist/" : ""
|
|
102
|
+
}src/routes`
|
|
103
|
+
);
|
|
104
|
+
} catch (e) {
|
|
105
|
+
// If it's just missing module, maybe we are in test mode or fresh install
|
|
106
|
+
if (process.env.NODE_ENV !== "test") {
|
|
107
|
+
console.warn(
|
|
108
|
+
`⚠️ Could not load user routes from ${userRoutesPath}. (This is expected during initial setup or if src/routes is missing)`
|
|
109
|
+
);
|
|
110
|
+
} else {
|
|
111
|
+
// In test mode, we really want to know if it failed to load
|
|
112
|
+
console.error(
|
|
113
|
+
`Error loading routes in test mode from ${userRoutesPath}:`,
|
|
114
|
+
e
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
98
118
|
} catch (error) {
|
|
99
|
-
console.warn(
|
|
100
|
-
"⚠️ Could not load user routes. Make sure you export 'apiRouter'."
|
|
101
|
-
);
|
|
102
119
|
console.error(error);
|
|
103
120
|
}
|
|
104
121
|
|
|
105
122
|
app.use(errorHandler);
|
|
106
123
|
|
|
124
|
+
return app;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export async function bootstrap() {
|
|
128
|
+
// Validasi Environment Variables
|
|
129
|
+
const requiredEnvs = ["DATABASE_URL", "JWT_SECRET"];
|
|
130
|
+
const missingEnvs = requiredEnvs.filter((key) => !process.env[key]);
|
|
131
|
+
if (missingEnvs.length > 0) {
|
|
132
|
+
console.error(
|
|
133
|
+
`❌ Missing required environment variables: ${missingEnvs.join(", ")}`
|
|
134
|
+
);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const app = await createApp();
|
|
107
139
|
const port = process.env.PORT ? Number(process.env.PORT) : 4000;
|
|
108
140
|
const server = http.createServer(app);
|
|
109
141
|
|
package/lib/utils/validator.ts
CHANGED
|
@@ -28,8 +28,8 @@ export class Validator {
|
|
|
28
28
|
/**
|
|
29
29
|
* Create a new Validator instance
|
|
30
30
|
* @param data The input data to validate
|
|
31
|
-
* @param schema Zod schema or object of Zod schemas /
|
|
32
|
-
* @param messages Optional custom error messages (
|
|
31
|
+
* @param schema Zod schema or object of Zod schemas / string-based rules
|
|
32
|
+
* @param messages Optional custom error messages (style: 'field.rule' => 'message')
|
|
33
33
|
*/
|
|
34
34
|
static make(
|
|
35
35
|
data: any,
|
|
@@ -58,7 +58,7 @@ export class Validator {
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
parsedSchema[key] = Validator.
|
|
61
|
+
parsedSchema[key] = Validator.parseStringRule(rule);
|
|
62
62
|
} else {
|
|
63
63
|
parsedSchema[key] = rule as ZodSchema;
|
|
64
64
|
}
|
|
@@ -83,7 +83,7 @@ export class Validator {
|
|
|
83
83
|
return new Validator(data, objectSchema, messages);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
private static
|
|
86
|
+
private static parseStringRule(rule: string | string[]): ZodSchema {
|
|
87
87
|
const rules = Array.isArray(rule)
|
|
88
88
|
? rule
|
|
89
89
|
: rule.split("|").map((r) => r.trim());
|