lapeh 2.2.5 → 2.2.7

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 (55) hide show
  1. package/api-testing-sepuluh/.env.example +19 -0
  2. package/api-testing-sepuluh/doc/ARCHITECTURE_GUIDE.md +73 -0
  3. package/api-testing-sepuluh/doc/CHANGELOG.md +77 -0
  4. package/api-testing-sepuluh/doc/CHEATSHEET.md +94 -0
  5. package/api-testing-sepuluh/doc/CLI.md +106 -0
  6. package/api-testing-sepuluh/doc/CONTRIBUTING.md +105 -0
  7. package/api-testing-sepuluh/doc/DEPLOYMENT.md +122 -0
  8. package/api-testing-sepuluh/doc/FAQ.md +81 -0
  9. package/api-testing-sepuluh/doc/FEATURES.md +165 -0
  10. package/api-testing-sepuluh/doc/GETTING_STARTED.md +108 -0
  11. package/api-testing-sepuluh/doc/INTRODUCTION.md +60 -0
  12. package/api-testing-sepuluh/doc/PACKAGES.md +66 -0
  13. package/api-testing-sepuluh/doc/PERFORMANCE.md +91 -0
  14. package/api-testing-sepuluh/doc/ROADMAP.md +93 -0
  15. package/api-testing-sepuluh/doc/SECURITY.md +93 -0
  16. package/api-testing-sepuluh/doc/STRUCTURE.md +90 -0
  17. package/api-testing-sepuluh/doc/TUTORIAL.md +192 -0
  18. package/api-testing-sepuluh/docker-compose.yml +24 -0
  19. package/api-testing-sepuluh/eslint.config.mjs +26 -0
  20. package/api-testing-sepuluh/framework.md +168 -0
  21. package/api-testing-sepuluh/nodemon.json +6 -0
  22. package/api-testing-sepuluh/package-lock.json +5539 -0
  23. package/api-testing-sepuluh/package.json +103 -0
  24. package/api-testing-sepuluh/prisma/base.prisma.template +7 -0
  25. package/api-testing-sepuluh/prisma/migrations/20251227034737_init_setup/migration.sql +248 -0
  26. package/api-testing-sepuluh/prisma/migrations/migration_lock.toml +3 -0
  27. package/api-testing-sepuluh/prisma/schema.prisma +183 -0
  28. package/api-testing-sepuluh/prisma/seed.ts +411 -0
  29. package/api-testing-sepuluh/prisma.config.ts +15 -0
  30. package/api-testing-sepuluh/readme.md +414 -0
  31. package/api-testing-sepuluh/scripts/check-update.js +92 -0
  32. package/api-testing-sepuluh/scripts/compile-schema.js +29 -0
  33. package/api-testing-sepuluh/scripts/config-clear.js +45 -0
  34. package/api-testing-sepuluh/scripts/generate-jwt-secret.js +38 -0
  35. package/api-testing-sepuluh/scripts/init-project.js +178 -0
  36. package/api-testing-sepuluh/scripts/make-controller.js +205 -0
  37. package/api-testing-sepuluh/scripts/make-model.js +42 -0
  38. package/api-testing-sepuluh/scripts/make-module.js +158 -0
  39. package/api-testing-sepuluh/scripts/verify-rbac-functional.js +187 -0
  40. package/api-testing-sepuluh/src/controllers/authController.ts +469 -0
  41. package/api-testing-sepuluh/src/controllers/petController.ts +194 -0
  42. package/api-testing-sepuluh/src/controllers/rbacController.ts +478 -0
  43. package/api-testing-sepuluh/src/models/core.prisma +163 -0
  44. package/api-testing-sepuluh/src/models/pets.prisma +9 -0
  45. package/api-testing-sepuluh/src/routes/auth.ts +74 -0
  46. package/api-testing-sepuluh/src/routes/index.ts +10 -0
  47. package/api-testing-sepuluh/src/routes/pets.ts +13 -0
  48. package/api-testing-sepuluh/src/routes/rbac.ts +42 -0
  49. package/api-testing-sepuluh/storage/logs/.gitkeep +0 -0
  50. package/api-testing-sepuluh/tsconfig.json +39 -0
  51. package/bin/index.js +142 -18
  52. package/lib/bootstrap.ts +5 -0
  53. package/package.json +3 -2
  54. package/prisma/seed.ts +4 -1
  55. package/prisma.config.ts +1 -1
@@ -0,0 +1,163 @@
1
+ model cache {
2
+ key String @id
3
+ value String
4
+ expiration Int
5
+ }
6
+
7
+ model cache_locks {
8
+ key String @id
9
+ owner String
10
+ expiration Int
11
+ }
12
+
13
+ model failed_jobs {
14
+ id BigInt @id @default(autoincrement())
15
+ uuid String @unique
16
+ connection String
17
+ queue String
18
+ payload String
19
+ exception String
20
+ failed_at DateTime @default(now())
21
+ }
22
+
23
+ model job_batches {
24
+ id String @id
25
+ name String
26
+ total_jobs Int
27
+ pending_jobs Int
28
+ failed_jobs Int
29
+ failed_job_ids String
30
+ options String?
31
+ cancelled_at Int?
32
+ created_at Int
33
+ finished_at Int?
34
+ }
35
+
36
+ model jobs {
37
+ id BigInt @id @default(autoincrement())
38
+ queue String
39
+ payload String
40
+ attempts Int
41
+ reserved_at Int?
42
+ available_at Int
43
+ created_at Int
44
+
45
+ @@index([queue])
46
+ }
47
+
48
+ model migrations {
49
+ id Int @id @default(autoincrement())
50
+ migration String
51
+ batch Int
52
+ }
53
+
54
+ model password_reset_tokens {
55
+ email String @id
56
+ token String
57
+ created_at DateTime?
58
+ }
59
+
60
+ model personal_access_tokens {
61
+ id BigInt @id @default(autoincrement())
62
+ tokenable_type String
63
+ tokenable_id BigInt
64
+ name String
65
+ token String @unique
66
+ abilities String?
67
+ last_used_at DateTime?
68
+ expires_at DateTime?
69
+ created_at DateTime?
70
+ updated_at DateTime?
71
+
72
+ @@index([expires_at])
73
+ @@index([tokenable_type, tokenable_id])
74
+ }
75
+
76
+ model sessions {
77
+ id String @id
78
+ user_id BigInt?
79
+ ip_address String?
80
+ user_agent String?
81
+ payload String
82
+ last_activity Int
83
+
84
+ @@index([last_activity])
85
+ @@index([user_id])
86
+ }
87
+
88
+ model users {
89
+ id BigInt @id @default(autoincrement())
90
+ uuid String @unique
91
+ name String
92
+ email String @unique
93
+ avatar String?
94
+ avatar_url String?
95
+ email_verified_at DateTime?
96
+ password String
97
+ remember_token String?
98
+ created_at DateTime?
99
+ updated_at DateTime?
100
+
101
+ user_roles user_roles[]
102
+ user_permissions user_permissions[]
103
+ }
104
+
105
+ model roles {
106
+ id BigInt @id @default(autoincrement())
107
+ name String
108
+ slug String @unique
109
+ description String?
110
+ created_at DateTime?
111
+ updated_at DateTime?
112
+
113
+ user_roles user_roles[]
114
+ role_permissions role_permissions[]
115
+ }
116
+
117
+ model permissions {
118
+ id BigInt @id @default(autoincrement())
119
+ name String
120
+ slug String @unique
121
+ description String?
122
+ created_at DateTime?
123
+ updated_at DateTime?
124
+
125
+ role_permissions role_permissions[]
126
+ user_permissions user_permissions[]
127
+ }
128
+
129
+ model user_roles {
130
+ id BigInt @id @default(autoincrement())
131
+ user_id BigInt
132
+ role_id BigInt
133
+ created_at DateTime?
134
+
135
+ user users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
136
+ role roles @relation(fields: [role_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
137
+
138
+ @@unique([user_id, role_id])
139
+ }
140
+
141
+ model role_permissions {
142
+ id BigInt @id @default(autoincrement())
143
+ role_id BigInt
144
+ permission_id BigInt
145
+ created_at DateTime?
146
+
147
+ role roles @relation(fields: [role_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
148
+ permission permissions @relation(fields: [permission_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
149
+
150
+ @@unique([role_id, permission_id])
151
+ }
152
+
153
+ model user_permissions {
154
+ id BigInt @id @default(autoincrement())
155
+ user_id BigInt
156
+ permission_id BigInt
157
+ created_at DateTime?
158
+
159
+ user users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
160
+ permission permissions @relation(fields: [permission_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
161
+
162
+ @@unique([user_id, permission_id])
163
+ }
@@ -0,0 +1,9 @@
1
+
2
+ model pets {
3
+ id BigInt @id @default(autoincrement())
4
+ name String
5
+ species String
6
+ age Int
7
+ created_at DateTime?
8
+ updated_at DateTime?
9
+ }
@@ -0,0 +1,74 @@
1
+ import { Router } from "express";
2
+ import rateLimit from "express-rate-limit";
3
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
4
+ const multer = require("multer");
5
+ import path from "path";
6
+ import fs from "fs";
7
+ import {
8
+ register,
9
+ login,
10
+ me,
11
+ logout,
12
+ refreshToken,
13
+ updatePassword,
14
+ updateProfile,
15
+ updateAvatar,
16
+ } from "@/controllers/authController";
17
+ import { requireAuth } from "@lapeh/middleware/auth";
18
+
19
+ const authLimiter = rateLimit({
20
+ windowMs: 15 * 60 * 1000,
21
+ max: 50,
22
+ standardHeaders: true,
23
+ legacyHeaders: false,
24
+ });
25
+
26
+ const avatarUploadDir = process.env.AVATAR_UPLOAD_DIR || "uploads/avatars";
27
+ if (!fs.existsSync(avatarUploadDir)) {
28
+ fs.mkdirSync(avatarUploadDir, { recursive: true });
29
+ }
30
+
31
+ const storage = (multer as any).diskStorage({
32
+ destination(
33
+ _req: any,
34
+ _file: any,
35
+ cb: (error: Error | null, destination: string) => void
36
+ ) {
37
+ cb(null, avatarUploadDir);
38
+ },
39
+ filename(
40
+ _req: any,
41
+ file: any,
42
+ cb: (error: Error | null, filename: string) => void
43
+ ) {
44
+ const ext = path.extname(file.originalname);
45
+ const base = path.basename(file.originalname, ext);
46
+ const unique = Date.now() + "-" + Math.round(Math.random() * 1e9);
47
+ cb(null, base + "-" + unique + ext);
48
+ },
49
+ });
50
+
51
+ const uploadAvatar = multer({ storage });
52
+
53
+ export const authRouter = Router();
54
+
55
+ authRouter.post("/register", authLimiter, register);
56
+
57
+ authRouter.post("/login", authLimiter, login);
58
+
59
+ authRouter.get("/me", requireAuth, me);
60
+
61
+ authRouter.post("/logout", requireAuth, logout);
62
+
63
+ authRouter.post("/refresh", authLimiter, refreshToken);
64
+
65
+ authRouter.put("/password", requireAuth, updatePassword);
66
+
67
+ authRouter.put("/profile", requireAuth, updateProfile);
68
+
69
+ authRouter.post(
70
+ "/avatar",
71
+ requireAuth,
72
+ uploadAvatar.single("avatar"),
73
+ updateAvatar
74
+ );
@@ -0,0 +1,10 @@
1
+ import { Router } from "express";
2
+ import { authRouter } from "@/routes/auth";
3
+ import { rbacRouter } from "@/routes/rbac";
4
+ import petRouter from "@/routes/pets";
5
+
6
+ export const apiRouter = Router();
7
+
8
+ apiRouter.use("/auth", authRouter);
9
+ apiRouter.use("/rbac", rbacRouter);
10
+ apiRouter.use("/pets", petRouter);
@@ -0,0 +1,13 @@
1
+ import { Router } from "express";
2
+ import * as PetController from "@/controllers/petController";
3
+ import { parseMultipart } from "@lapeh/middleware/multipart";
4
+
5
+ const router = Router();
6
+
7
+ router.get("/", PetController.index);
8
+ router.get("/:id", PetController.show);
9
+ router.post("/", parseMultipart, PetController.store);
10
+ router.put("/:id", parseMultipart, PetController.update);
11
+ router.delete("/:id", PetController.destroy);
12
+
13
+ export default router;
@@ -0,0 +1,42 @@
1
+ import { Router } from "express";
2
+ import { requireAdmin, requireAuth } from "@lapeh/middleware/auth";
3
+ import {
4
+ createRole,
5
+ listRoles,
6
+ updateRole,
7
+ deleteRole,
8
+ createPermission,
9
+ listPermissions,
10
+ updatePermission,
11
+ deletePermission,
12
+ assignRoleToUser,
13
+ removeRoleFromUser,
14
+ assignPermissionToRole,
15
+ removePermissionFromRole,
16
+ assignPermissionToUser,
17
+ removePermissionFromUser,
18
+ } from "@/controllers/rbacController";
19
+
20
+ export const rbacRouter = Router();
21
+
22
+ rbacRouter.use(requireAuth);
23
+ rbacRouter.use(requireAdmin);
24
+
25
+ rbacRouter.post("/roles", createRole);
26
+ rbacRouter.get("/roles", listRoles);
27
+ rbacRouter.put("/roles/:id", updateRole);
28
+ rbacRouter.delete("/roles/:id", deleteRole);
29
+
30
+ rbacRouter.post("/permissions", createPermission);
31
+ rbacRouter.get("/permissions", listPermissions);
32
+ rbacRouter.put("/permissions/:id", updatePermission);
33
+ rbacRouter.delete("/permissions/:id", deletePermission);
34
+
35
+ rbacRouter.post("/users/assign-role", assignRoleToUser);
36
+ rbacRouter.post("/users/remove-role", removeRoleFromUser);
37
+
38
+ rbacRouter.post("/roles/assign-permission", assignPermissionToRole);
39
+ rbacRouter.post("/roles/remove-permission", removePermissionFromRole);
40
+
41
+ rbacRouter.post("/users/assign-permission", assignPermissionToUser);
42
+ rbacRouter.post("/users/remove-permission", removePermissionFromUser);
File without changes
@@ -0,0 +1,39 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "CommonJS",
5
+ "outDir": "dist",
6
+ "rootDir": ".",
7
+ "declaration": true,
8
+ "declarationMap": true,
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "noUnusedLocals": true,
14
+ "noUnusedParameters": true,
15
+ "paths": {
16
+ "@lapeh/*": [
17
+ "./node_modules/lapeh/lib/*"
18
+ ],
19
+ "@/*": [
20
+ "./src/*"
21
+ ]
22
+ }
23
+ },
24
+ "include": [
25
+ "lib",
26
+ "src",
27
+ "prisma",
28
+ "generated"
29
+ ],
30
+ "exclude": [
31
+ "node_modules",
32
+ "dist"
33
+ ],
34
+ "ts-node": {
35
+ "ignore": [
36
+ "node_modules/(?!lapeh)"
37
+ ]
38
+ }
39
+ }
package/bin/index.js CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
@@ -36,9 +36,14 @@ function runDev() {
36
36
  try {
37
37
  const tsNodePath = require.resolve('ts-node/register');
38
38
  const tsConfigPathsPath = require.resolve('tsconfig-paths/register');
39
+
40
+ // Resolve bootstrap file relative to this script
41
+ // If run from node_modules, it will find ../lib/bootstrap.ts
42
+ // If run from source, it will find ../lib/bootstrap.ts
43
+ const bootstrapPath = path.resolve(__dirname, '../lib/bootstrap.ts');
44
+
39
45
  // We execute a script that requires ts-node to run lib/bootstrap.ts
40
- // In a real package, this would be `node dist/lib/bootstrap.js`
41
- execSync(`npx nodemon --exec "node -r ${tsNodePath} -r ${tsConfigPathsPath}" -e "require('./lib/bootstrap').bootstrap()"`, { stdio: 'inherit' });
46
+ execSync(`npx nodemon --watch src --watch lib --ext ts,json --exec "node -r ${tsNodePath} -r ${tsConfigPathsPath} ${bootstrapPath}"`, { stdio: 'inherit' });
42
47
  } catch (error) {
43
48
  // Ignore error
44
49
  }
@@ -77,6 +82,8 @@ async function upgradeProject() {
77
82
 
78
83
  // Files/Folders to overwrite/copy
79
84
  const filesToSync = [
85
+ 'bin', // Ensure CLI script is updated
86
+ 'lib', // Ensure core framework files are updated
80
87
  'scripts',
81
88
  'docker-compose.yml',
82
89
  '.env.example',
@@ -141,7 +148,11 @@ async function upgradeProject() {
141
148
  // Update scripts
142
149
  currentPackageJson.scripts = {
143
150
  ...currentPackageJson.scripts,
144
- ...templatePackageJson.scripts
151
+ ...templatePackageJson.scripts,
152
+ "dev": "lapeh dev",
153
+ "start": "lapeh start",
154
+ "build": "lapeh build",
155
+ "start:prod": "lapeh start"
145
156
  };
146
157
 
147
158
  // Update dependencies
@@ -157,10 +168,32 @@ async function upgradeProject() {
157
168
  };
158
169
 
159
170
  // Update Lapeh version tag
160
- currentPackageJson.dependencies["lapeh"] = templatePackageJson.version;
171
+ // For local development, we use file reference. For production publish, use version.
172
+ currentPackageJson.dependencies["lapeh"] = "file:../";
161
173
 
162
174
  fs.writeFileSync(packageJsonPath, JSON.stringify(currentPackageJson, null, 2));
163
175
 
176
+ // Update tsconfig.json to support framework-as-dependency
177
+ console.log('🔧 Configuring tsconfig.json...');
178
+ const tsconfigPath = path.join(currentDir, 'tsconfig.json');
179
+ if (fs.existsSync(tsconfigPath)) {
180
+ // Use comment-json or just basic parsing if no comments (standard JSON)
181
+ // Since our template tsconfig is standard JSON, require is fine or JSON.parse
182
+ const tsconfig = require(tsconfigPath);
183
+
184
+ // Update paths
185
+ if (tsconfig.compilerOptions && tsconfig.compilerOptions.paths) {
186
+ tsconfig.compilerOptions.paths["@lapeh/*"] = ["./node_modules/lapeh/lib/*"];
187
+ }
188
+
189
+ // Add ts-node ignore configuration
190
+ tsconfig["ts-node"] = {
191
+ "ignore": ["node_modules/(?!lapeh)"]
192
+ };
193
+
194
+ fs.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2));
195
+ }
196
+
164
197
  // Run npm install
165
198
  console.log('📦 Installing updated dependencies...');
166
199
  try {
@@ -177,6 +210,7 @@ async function upgradeProject() {
177
210
  function createProject() {
178
211
  const projectName = args.find(arg => !arg.startsWith('-'));
179
212
  const isFull = args.includes('--full');
213
+ const useDefaults = args.includes('--defaults');
180
214
 
181
215
  if (!projectName) {
182
216
  console.error('❌ Please specify the project name:');
@@ -233,27 +267,45 @@ function createProject() {
233
267
 
234
268
  // --- DATABASE SELECTION ---
235
269
  console.log("\n--- Database Configuration ---");
236
- const dbType = await selectOption("Database apa yang akan digunakan?", [
237
- { key: "pgsql", label: "PostgreSQL", provider: "postgresql", defaultPort: "5432" },
238
- { key: "mysql", label: "MySQL", provider: "mysql", defaultPort: "3306" },
239
- ]);
270
+ let dbType, host, port, user, password, dbName;
271
+
272
+ if (useDefaults) {
273
+ console.log("ℹ️ Using default configuration (--defaults)...");
274
+ dbType = { key: "pgsql", label: "PostgreSQL", provider: "postgresql", defaultPort: "5432" };
275
+ host = "localhost";
276
+ port = "5432";
277
+ user = "postgres"; // Default postgres user is usually postgres, not root
278
+ password = "password"; // Default password
279
+ dbName = projectName.replace(/-/g, '_');
280
+ } else {
281
+ dbType = await selectOption("Database apa yang akan digunakan?", [
282
+ { key: "pgsql", label: "PostgreSQL", provider: "postgresql", defaultPort: "5432" },
283
+ { key: "mysql", label: "MySQL", provider: "mysql", defaultPort: "3306" },
284
+ ]);
285
+
286
+ host = await ask("Database Host", "localhost");
287
+ port = await ask("Database Port", dbType.defaultPort);
288
+ user = await ask("Database User", "root");
289
+ password = await ask("Database Password", "");
290
+ dbName = await ask("Database Name", projectName.replace(/-/g, '_')); // Default db name based on project name
291
+ }
240
292
 
241
293
  let dbUrl = "";
242
294
  let dbProvider = dbType.provider;
243
295
 
244
- const host = await ask("Database Host", "localhost");
245
- const port = await ask("Database Port", dbType.defaultPort);
246
- const user = await ask("Database User", "root");
247
- const password = await ask("Database Password", "");
248
- const dbName = await ask("Database Name", projectName.replace(/-/g, '_')); // Default db name based on project name
249
-
250
296
  if (dbType.key === "pgsql") {
251
297
  dbUrl = `postgresql://${user}:${password}@${host}:${port}/${dbName}?schema=public`;
252
298
  } else {
253
299
  dbUrl = `mysql://${user}:${password}@${host}:${port}/${dbName}`;
254
300
  }
255
301
 
256
- rl.close();
302
+ if (!useDefaults) {
303
+ rl.close();
304
+ } else {
305
+ // If we didn't use rl, we might not need to close it if we didn't open it?
306
+ // Actually rl is created at the top. We should close it.
307
+ rl.close();
308
+ }
257
309
 
258
310
  // List of files/folders to exclude
259
311
  const ignoreList = [
@@ -261,7 +313,8 @@ function createProject() {
261
313
  'dist',
262
314
  '.git',
263
315
  '.env',
264
- 'bin', // Don't copy the CLI script itself
316
+ 'bin', // Exclude bin folder, using dependency instead
317
+ 'lib', // Exclude lib folder, using dependency instead
265
318
  'package-lock.json',
266
319
  '.DS_Store',
267
320
  'prisma/migrations', // Exclude existing migrations
@@ -306,15 +359,86 @@ function createProject() {
306
359
  packageJson.name = projectName;
307
360
  // Add lapeh framework version to dependencies to track it like react-router
308
361
  packageJson.dependencies = packageJson.dependencies || {};
309
- packageJson.dependencies["lapeh"] = packageJson.version;
362
+ // For local development, we use file reference. For production publish, use version.
363
+ packageJson.dependencies["lapeh"] = "file:../";
364
+
365
+ // Ensure prisma CLI is available in devDependencies for the new project
366
+ packageJson.devDependencies = packageJson.devDependencies || {};
367
+ packageJson.devDependencies["prisma"] = "7.2.0";
310
368
 
369
+ // Add missing types for dev
370
+ packageJson.devDependencies["@types/express"] = "^5.0.0";
371
+ packageJson.devDependencies["@types/compression"] = "^1.7.5";
372
+
311
373
  packageJson.version = '1.0.0';
312
374
  packageJson.description = 'Generated by lapeh';
313
375
  delete packageJson.bin; // Remove the bin entry from the generated project
314
376
  delete packageJson.repository; // Remove repository info if specific to the template
315
377
 
378
+ // Update scripts to use lapeh binary
379
+ packageJson.scripts = {
380
+ ...packageJson.scripts,
381
+ "dev": "lapeh dev",
382
+ "start": "lapeh start",
383
+ "build": "lapeh build",
384
+ "start:prod": "lapeh start"
385
+ };
386
+
316
387
  fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
317
388
 
389
+ // Update tsconfig.json to support framework-as-dependency
390
+ console.log('🔧 Configuring tsconfig.json...');
391
+ const tsconfigPath = path.join(projectDir, 'tsconfig.json');
392
+ if (fs.existsSync(tsconfigPath)) {
393
+ // Use comment-json or just basic parsing if no comments (standard JSON)
394
+ // Since our template tsconfig is standard JSON, require is fine or JSON.parse
395
+ const tsconfig = require(tsconfigPath);
396
+
397
+ // Update paths
398
+ if (tsconfig.compilerOptions && tsconfig.compilerOptions.paths) {
399
+ tsconfig.compilerOptions.paths["@lapeh/*"] = ["./node_modules/lapeh/lib/*"];
400
+ }
401
+
402
+ // Add ts-node ignore configuration
403
+ tsconfig["ts-node"] = {
404
+ "ignore": ["node_modules/(?!lapeh)"]
405
+ };
406
+
407
+ fs.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2));
408
+ }
409
+
410
+ // Configure prisma.config.ts to use tsconfig-paths
411
+ const prismaConfigPath = path.join(projectDir, 'prisma.config.ts');
412
+ if (fs.existsSync(prismaConfigPath)) {
413
+ console.log('🔧 Configuring prisma.config.ts...');
414
+ let prismaConfigContent = fs.readFileSync(prismaConfigPath, 'utf8');
415
+ prismaConfigContent = prismaConfigContent.replace(
416
+ /seed:\s*"ts-node\s+prisma\/seed\.ts"/g,
417
+ 'seed: "ts-node -r tsconfig-paths/register prisma/seed.ts"'
418
+ );
419
+ fs.writeFileSync(prismaConfigPath, prismaConfigContent);
420
+ }
421
+
422
+ // Configure prisma/seed.ts imports
423
+ const prismaSeedPath = path.join(projectDir, 'prisma', 'seed.ts');
424
+ if (fs.existsSync(prismaSeedPath)) {
425
+ console.log('🔧 Configuring prisma/seed.ts...');
426
+ let seedContent = fs.readFileSync(prismaSeedPath, 'utf8');
427
+
428
+ // Add dotenv config if missing
429
+ if (!seedContent.includes('dotenv.config()')) {
430
+ seedContent = 'import dotenv from "dotenv";\ndotenv.config();\n\n' + seedContent;
431
+ }
432
+
433
+ // Update import path
434
+ seedContent = seedContent.replace(
435
+ /import\s+{\s*prisma\s*}\s+from\s+["']@lapeh\/core\/database["']/,
436
+ 'import { prisma } from "@lapeh/core/database"'
437
+ );
438
+
439
+ fs.writeFileSync(prismaSeedPath, seedContent);
440
+ }
441
+
318
442
  // Create .env from .env.example with correct DB config
319
443
  console.log('⚙️ Configuring environment...');
320
444
  const envExamplePath = path.join(projectDir, '.env.example');
package/lib/bootstrap.ts CHANGED
@@ -143,3 +143,8 @@ export async function bootstrap() {
143
143
  shutdown("uncaughtException");
144
144
  });
145
145
  }
146
+
147
+ // Self-executing if run directly
148
+ if (require.main === module) {
149
+ bootstrap();
150
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lapeh",
3
- "version": "2.2.5",
3
+ "version": "2.2.7",
4
4
  "description": "Framework API Express yang siap pakai (Standardized)",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -87,7 +87,8 @@
87
87
  "uuid": "13.0.0",
88
88
  "winston": "^3.19.0",
89
89
  "winston-daily-rotate-file": "^5.0.0",
90
- "zod": "3.23.8"
90
+ "zod": "3.23.8",
91
+ "prisma": "7.2.0"
91
92
  },
92
93
  "devDependencies": {
93
94
  "@eslint/js": "^9.39.2",
package/prisma/seed.ts CHANGED
@@ -1,4 +1,7 @@
1
- import { prisma } from "../lib/core/database";
1
+ import dotenv from "dotenv";
2
+ dotenv.config();
3
+
4
+ import { prisma } from "@lapeh/core/database";
2
5
  import bcrypt from "bcryptjs";
3
6
  import { v4 as uuidv4 } from "uuid";
4
7
 
package/prisma.config.ts CHANGED
@@ -7,7 +7,7 @@ export default defineConfig({
7
7
  schema: "prisma/schema.prisma",
8
8
  migrations: {
9
9
  path: "prisma/migrations",
10
- seed: "ts-node prisma/seed.ts",
10
+ seed: "ts-node -r tsconfig-paths/register prisma/seed.ts",
11
11
  },
12
12
  datasource: {
13
13
  url: env("DATABASE_URL"),