crudora 0.1.0-alpha.2 → 0.1.0-alpha.3

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
@@ -16,6 +16,12 @@ Automatic CRUD API generator for TypeScript with Prisma - Build REST APIs in min
16
16
  - 🔌 **Extensible**: Custom routes and middleware support
17
17
  - 📖 **Schema Generation**: Auto-generate Prisma schemas from models
18
18
  - 🗄️ **Repository Pattern**: Built-in repository for database operations
19
+ - 🔄 **Lifecycle Hooks**: beforeCreate, afterCreate, beforeUpdate, afterUpdate, beforeDelete, afterDelete, beforeFind, afterFind
20
+ - 🔒 **Field Security**: Hidden fields support with automatic filtering
21
+ - 🎛️ **Dynamic Selection**: Smart field selection based on fillable and hidden properties
22
+ - ⚡ **Auto Setup**: Intelligent postinstall script for quick project initialization
23
+ - 🔄 **TypeScript First**: Native TypeScript support with ESM modules
24
+ - 🖥️ **CLI Tool**: Command-line interface for project initialization and scaffolding
19
25
 
20
26
  ## Installation
21
27
 
@@ -25,9 +31,45 @@ npm install crudora prisma @prisma/client
25
31
  yarn add crudora prisma @prisma/client
26
32
  ```
27
33
 
34
+ **Note**: After installation, Crudora automatically sets up your project with:
35
+
36
+ - Prisma schema template
37
+ - Environment configuration (.env)
38
+ - Basic server setup (server.ts)
39
+ - Useful npm scripts
40
+
41
+ ## CLI Usage
42
+
43
+ Crudora comes with a built-in CLI tool for quick project initialization and database management:
44
+
45
+ ```bash
46
+ # Initialize a new Crudora project in the current directory
47
+ npx crudora init
48
+
49
+ # Start Prisma Studio for database management
50
+ npx crudora studio
51
+
52
+ # Generate Prisma Client
53
+ npx crudora generate
54
+
55
+ # Push Prisma schema to database
56
+ npx crudora push
57
+
58
+ # Run Prisma migrations
59
+ npx crudora migrate
60
+ ```
61
+
62
+ ### CLI Commands
63
+
64
+ - `init` - Initialize a new Crudora project with necessary files (schema.prisma, .env, server.ts, tsconfig.json)
65
+ - `studio` - Start Prisma Studio for visual database management
66
+ - `generate` - Generate Prisma Client based on your schema
67
+ - `push` - Push Prisma schema to your database without migrations
68
+ - `migrate` - Run Prisma migrations for schema changes
69
+
28
70
  ## Quick Start
29
71
 
30
- ### Method 1: Simple Inheritance (Recommended)
72
+ ### Basic Usage
31
73
 
32
74
  ```typescript
33
75
  import { CrudoraServer, Model } from "crudora";
@@ -35,26 +77,34 @@ import { PrismaClient } from "@prisma/client";
35
77
 
36
78
  const prisma = new PrismaClient();
37
79
 
38
- // Define your models
39
80
  class User extends Model {
40
81
  static tableName = "users";
41
82
  static primaryKey = "id";
42
83
  static timestamps = true;
43
- static fillable = ["name", "email"];
84
+ static fillable = ["name", "email", "password"];
44
85
  static hidden = ["password"];
86
+
87
+ // Lifecycle hooks
88
+ static async beforeCreate(data: any): Promise<any> {
89
+ data.password = await hashPassword(data.password);
90
+ return data;
91
+ }
92
+
93
+ static async afterCreate(data: any, result: any): Promise<any> {
94
+ console.log(`User created: ${result.email}`);
95
+ return result;
96
+ }
45
97
  }
46
98
 
47
99
  class Post extends Model {
48
100
  static tableName = "posts";
49
101
  static fillable = ["title", "content", "authorId"];
102
+ static hidden = ["deletedAt"];
50
103
  }
51
104
 
52
- // Create server and register models
53
105
  const server = new CrudoraServer({
54
106
  port: 3000,
55
107
  prisma: prisma,
56
- cors: true,
57
- basePath: "/api",
58
108
  });
59
109
 
60
110
  server
@@ -65,40 +115,6 @@ server
65
115
  });
66
116
  ```
67
117
 
68
- ### Method 2: Decorator Pattern
69
-
70
- ```typescript
71
- import { CrudoraServer, Model, Field } from "crudora";
72
- import { PrismaClient } from "@prisma/client";
73
-
74
- const prisma = new PrismaClient();
75
-
76
- @Model({ tableName: "users" })
77
- class User {
78
- @Field({ type: "uuid", primary: true })
79
- id!: string;
80
-
81
- @Field({ type: "string", required: true })
82
- name!: string;
83
-
84
- @Field({ type: "string", unique: true, required: true })
85
- email!: string;
86
-
87
- @Field({ type: "date" })
88
- createdAt!: Date;
89
-
90
- @Field({ type: "date" })
91
- updatedAt!: Date;
92
- }
93
-
94
- const server = new CrudoraServer({
95
- port: 3000,
96
- prisma: prisma,
97
- });
98
-
99
- server.registerModel(User).generateRoutes().listen();
100
- ```
101
-
102
118
  ## Generated API Endpoints
103
119
 
104
120
  For each registered model, Crudora automatically generates:
@@ -118,19 +134,97 @@ For each registered model, Crudora automatically generates:
118
134
 
119
135
  ## Advanced Usage
120
136
 
137
+ ### Lifecycle Hooks
138
+
139
+ Crudora supports comprehensive lifecycle hooks for all CRUD operations:
140
+
141
+ ```typescript
142
+ class User extends Model {
143
+ static tableName = "users";
144
+ static fillable = ["name", "email", "password"];
145
+ static hidden = ["password"];
146
+
147
+ // Create hooks
148
+ static async beforeCreate(data: any): Promise<any> {
149
+ data.password = await hashPassword(data.password);
150
+ data.createdAt = new Date();
151
+ return data;
152
+ }
153
+
154
+ static async afterCreate(data: any, result: any): Promise<any> {
155
+ await sendWelcomeEmail(result.email);
156
+ await logUserCreation(result.id);
157
+ return result;
158
+ }
159
+
160
+ // Update hooks
161
+ static async beforeUpdate(id: string, data: any): Promise<any> {
162
+ data.updatedAt = new Date();
163
+ return data;
164
+ }
165
+
166
+ static async afterUpdate(id: string, data: any, result: any): Promise<any> {
167
+ await logUserUpdate(id, data);
168
+ return result;
169
+ }
170
+
171
+ // Delete hooks
172
+ static async beforeDelete(id: string): Promise<void> {
173
+ await archiveUserData(id);
174
+ }
175
+
176
+ static async afterDelete(id: string, result: any): Promise<any> {
177
+ await logUserDeletion(id);
178
+ return result;
179
+ }
180
+
181
+ // Find hooks
182
+ static async beforeFind(options: any): Promise<any> {
183
+ // Add default filters
184
+ options.where = { ...options.where, active: true };
185
+ return options;
186
+ }
187
+
188
+ static async afterFind(result: any): Promise<any> {
189
+ // Transform result
190
+ if (Array.isArray(result)) {
191
+ return result.map((user) => ({ ...user, displayName: user.name }));
192
+ }
193
+ return { ...result, displayName: result.name };
194
+ }
195
+ }
196
+ ```
197
+
198
+ ### Field Security and Dynamic Selection
199
+
200
+ Crudora automatically handles field security and dynamic selection:
201
+
202
+ ```typescript
203
+ class User extends Model {
204
+ static tableName = "users";
205
+ static fillable = ["name", "email", "bio"]; // Only these fields can be mass-assigned
206
+ static hidden = ["password", "secret"]; // These fields are automatically excluded from responses
207
+ }
208
+
209
+ // API responses automatically exclude hidden fields
210
+ // Only fillable fields are included in select queries for better performance
211
+ ```
212
+
121
213
  ### Using Repositories
122
214
 
123
215
  ```typescript
124
216
  const crudora = server.getCrudora();
125
217
  const userRepo = crudora.getRepository(User);
126
218
 
127
- // Create user
219
+ // Create user (triggers beforeCreate and afterCreate hooks)
128
220
  const user = await userRepo.create({
129
221
  name: "John Doe",
130
222
  email: "john@example.com",
223
+ password: "plaintext", // Will be hashed by beforeCreate hook
131
224
  });
225
+ // Response excludes password due to hidden field
132
226
 
133
- // Find users
227
+ // Find users (triggers beforeFind and afterFind hooks)
134
228
  const users = await userRepo.findAll({
135
229
  skip: 0,
136
230
  take: 10,
@@ -138,6 +232,11 @@ const users = await userRepo.findAll({
138
232
  orderBy: { createdAt: "desc" },
139
233
  });
140
234
 
235
+ // Update user (triggers beforeUpdate and afterUpdate hooks)
236
+ const updatedUser = await userRepo.update("user-id", {
237
+ name: "John Updated",
238
+ });
239
+
141
240
  // Count users
142
241
  const count = await userRepo.count({ active: true });
143
242
  ```
@@ -178,11 +277,43 @@ const partialSchema = crudora.getValidationSchema(User);
178
277
  const strictSchema = crudora.getStrictValidationSchema(User);
179
278
  ```
180
279
 
280
+ ## Project Setup
281
+
282
+ After installing Crudora, run these commands to complete setup:
283
+
284
+ ```bash
285
+ # Install additional dependencies
286
+ npm install @prisma/client prisma dotenv
287
+
288
+ # Generate Prisma client
289
+ npm run db:generate
290
+
291
+ # Push database schema
292
+ npm run db:push
293
+
294
+ # Start development server
295
+ npm run dev
296
+ ```
297
+
298
+ ## Available Scripts
299
+
300
+ Crudora automatically adds these scripts to your package.json:
301
+
302
+ - `npm run dev` - Start development server with ts-node
303
+ - `npm run build` - Build TypeScript to JavaScript
304
+ - `npm run start` - Start production server from built JavaScript
305
+ - `npm run start:prod` - Build and start production server
306
+ - `npm run db:generate` - Generate Prisma client
307
+ - `npm run db:push` - Push schema to database
308
+ - `npm run db:migrate` - Run database migrations
309
+ - `npm run db:studio` - Open Prisma Studio
310
+
181
311
  ## Documentation
182
312
 
183
313
  - [API Reference](./docs/api.md)
184
314
  - [Model Definition Guide](./docs/models.md)
185
315
  - [Custom Routes](./docs/custom-routes.md)
316
+ - [CLI Reference](./docs/cli.md)
186
317
  - [Deployment Guide](./docs/deployment.md)
187
318
 
188
319
  ## Contributing
package/bin/crudora.js ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { fileURLToPath } from 'url';
4
+ import { dirname } from 'path';
5
+ import { createRequire } from 'module';
6
+
7
+ const require = createRequire(import.meta.url);
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+
11
+ let cliPath;
12
+ try {
13
+ cliPath = new URL('../dist/cli.js', import.meta.url);
14
+ await import(cliPath);
15
+ } catch (error) {
16
+ try {
17
+ cliPath = new URL('../src/cli.ts', import.meta.url);
18
+ const tsNodePath = require.resolve('ts-node/register');
19
+ require(tsNodePath);
20
+ await import(cliPath);
21
+ } catch (innerError) {
22
+ console.error('Error loading CLI:', innerError);
23
+ process.exit(1);
24
+ }
25
+ }
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { fileURLToPath } from 'url';
4
+ import { dirname } from 'path';
5
+ import { createRequire } from 'module';
6
+
7
+ const require = createRequire(import.meta.url);
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+
11
+ let cliPath;
12
+ try {
13
+ cliPath = new URL('../dist/cli.js', import.meta.url);
14
+ await import(cliPath);
15
+ } catch (error) {
16
+ try {
17
+ cliPath = new URL('../src/cli.ts', import.meta.url);
18
+ const tsNodePath = require.resolve('ts-node/register');
19
+ require(tsNodePath);
20
+ await import(cliPath);
21
+ } catch (innerError) {
22
+ console.error('Error loading CLI:', innerError);
23
+ process.exit(1);
24
+ }
25
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ // Ubah semua import menjadi require
7
+ const commander_1 = require("commander");
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const child_process_1 = require("child_process");
11
+ function initProject() {
12
+ const templatePath = path_1.default.join(__dirname, '..', 'templates', 'schema.prisma');
13
+ const targetPrismaPath = path_1.default.join(process.cwd(), 'prisma', 'schema.prisma');
14
+ const targetEnvPath = path_1.default.join(process.cwd(), '.env');
15
+ const targetServerPath = path_1.default.join(process.cwd(), 'src', 'server.ts'); // Ubah ke .ts
16
+ const targetPackagePath = path_1.default.join(process.cwd(), 'package.json');
17
+ console.log('🚀 Setting up Crudora project...');
18
+ // 1. Create prisma directory and schema
19
+ if (!fs_1.default.existsSync(path_1.default.dirname(targetPrismaPath))) {
20
+ console.log('📋 Creating Prisma schema...');
21
+ fs_1.default.mkdirSync(path_1.default.dirname(targetPrismaPath), { recursive: true });
22
+ fs_1.default.copyFileSync(templatePath, targetPrismaPath);
23
+ console.log('✅ Prisma schema created at prisma/schema.prisma');
24
+ }
25
+ else {
26
+ console.log('📋 Prisma directory already exists, skipping schema creation');
27
+ }
28
+ // 2. Create .env file
29
+ if (!fs_1.default.existsSync(targetEnvPath)) {
30
+ console.log('🔧 Creating .env file...');
31
+ const envContent = fs_1.default.readFileSync(path_1.default.join(__dirname, '..', 'templates', '.env.example'), 'utf8');
32
+ fs_1.default.writeFileSync(targetEnvPath, envContent);
33
+ console.log('✅ .env file created');
34
+ }
35
+ else {
36
+ console.log('🔧 .env file already exists, skipping creation');
37
+ }
38
+ // 3. Create basic server setup with TypeScript and ESM
39
+ if (!fs_1.default.existsSync(targetServerPath)) {
40
+ console.log('🖥️ Creating server setup...');
41
+ const serverContent = `import { CrudoraServer } from 'crudora';
42
+ import { PrismaClient } from '@prisma/client';
43
+ import dotenv from 'dotenv';
44
+ dotenv.config();
45
+
46
+ const prisma = new PrismaClient();
47
+
48
+ // Example User model (uncomment and modify as needed)
49
+ /*
50
+ class User {
51
+ static tableName = 'users';
52
+ static primaryKey = 'id';
53
+ static timestamps = true;
54
+ static fillable = ['name', 'email'];
55
+ static hidden = ['password'];
56
+ }
57
+ */
58
+
59
+ const server = new CrudoraServer({
60
+ port: Number(process.env.PORT) || 3000,
61
+ prisma: prisma,
62
+ cors: true,
63
+ basePath: process.env.API_BASE_PATH || '/api'
64
+ });
65
+
66
+ // Register your models here
67
+ // server.registerModel(User);
68
+
69
+ // Add custom routes if needed
70
+ server.get('/health', (req, res) => {
71
+ res.json({ status: 'ok', timestamp: new Date() });
72
+ });
73
+
74
+ // Generate routes and start server
75
+ server
76
+ .generateRoutes()
77
+ .listen(() => {
78
+ console.log('🚀 Crudora server is running!');
79
+ console.log('📚 API documentation: http://localhost:\${process.env.PORT || 3000}\${process.env.API_BASE_PATH || '/api'}');
80
+ });
81
+ `;
82
+ fs_1.default.writeFileSync(targetServerPath, serverContent);
83
+ console.log('✅ Server setup created at server.ts');
84
+ }
85
+ else {
86
+ console.log('🖥️ server.ts already exists, skipping creation');
87
+ }
88
+ // 4. Update package.json scripts (if package.json exists)
89
+ if (fs_1.default.existsSync(targetPackagePath)) {
90
+ try {
91
+ const packageJson = JSON.parse(fs_1.default.readFileSync(targetPackagePath, 'utf8'));
92
+ if (!packageJson.scripts) {
93
+ packageJson.scripts = {};
94
+ }
95
+ // Add useful scripts if they don't exist
96
+ const scriptsToAdd = {
97
+ 'dev': 'ts-node server.ts',
98
+ 'start': 'ts-node server.ts',
99
+ 'build': 'tsc',
100
+ 'start:prod': 'node dist/server.js',
101
+ };
102
+ let scriptsAdded = false;
103
+ for (const [scriptName, scriptCommand] of Object.entries(scriptsToAdd)) {
104
+ if (!packageJson.scripts[scriptName]) {
105
+ packageJson.scripts[scriptName] = scriptCommand;
106
+ scriptsAdded = true;
107
+ }
108
+ }
109
+ if (scriptsAdded) {
110
+ fs_1.default.writeFileSync(targetPackagePath, JSON.stringify(packageJson, null, 2));
111
+ console.log('✅ Package.json scripts updated');
112
+ }
113
+ }
114
+ catch (error) {
115
+ console.log('⚠️ Could not update package.json scripts:', error.message);
116
+ }
117
+ }
118
+ // 5. Create tsconfig.json if it doesn't exist
119
+ const targetTsConfigPath = path_1.default.join(process.cwd(), 'tsconfig.json');
120
+ if (!fs_1.default.existsSync(targetTsConfigPath)) {
121
+ console.log('📝 Creating tsconfig.json...');
122
+ const tsConfigContent = {
123
+ "compilerOptions": {
124
+ "target": "ES2020",
125
+ "module": "NodeNext",
126
+ "moduleResolution": "NodeNext",
127
+ "lib": ["ES2020"],
128
+ "outDir": "./dist",
129
+ "rootDir": "./",
130
+ "strict": true,
131
+ "esModuleInterop": true,
132
+ "skipLibCheck": true,
133
+ "forceConsistentCasingInFileNames": true,
134
+ "experimentalDecorators": true,
135
+ "emitDecoratorMetadata": true,
136
+ "resolveJsonModule": true,
137
+ "declaration": true,
138
+ "sourceMap": true
139
+ },
140
+ "include": ["*.ts", "src/**/*"],
141
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
142
+ };
143
+ fs_1.default.writeFileSync(targetTsConfigPath, JSON.stringify(tsConfigContent, null, 2));
144
+ console.log('✅ tsconfig.json created');
145
+ }
146
+ console.log('\n🎉 Crudora setup complete!');
147
+ console.log('\n📝 Next steps:');
148
+ console.log('1. Install dependencies: npm install @prisma/client prisma dotenv ts-node typescript');
149
+ console.log('2. Generate Prisma client: npx prisma generate');
150
+ console.log('3. Push database schema: npx prisma db push');
151
+ console.log('4. Define your models in server.ts');
152
+ console.log('5. Start development server: npm run dev');
153
+ console.log('\n📖 Documentation: https://github.com/suryamsj/crudora#readme');
154
+ }
155
+ function runPrismaStudio() {
156
+ console.log('🚀 Starting Prisma Studio...');
157
+ try {
158
+ (0, child_process_1.execSync)('npx prisma studio', { stdio: 'inherit' });
159
+ }
160
+ catch (error) {
161
+ console.error('❌ Failed to start Prisma Studio:', error.message);
162
+ process.exit(1);
163
+ }
164
+ }
165
+ function generatePrismaClient() {
166
+ console.log('🔧 Generating Prisma Client...');
167
+ try {
168
+ (0, child_process_1.execSync)('npx prisma generate', { stdio: 'inherit' });
169
+ console.log('✅ Prisma Client generated successfully');
170
+ }
171
+ catch (error) {
172
+ console.error('❌ Failed to generate Prisma Client:', error.message);
173
+ process.exit(1);
174
+ }
175
+ }
176
+ function pushPrismaSchema() {
177
+ console.log('🔄 Pushing Prisma schema to database...');
178
+ try {
179
+ (0, child_process_1.execSync)('npx prisma db push', { stdio: 'inherit' });
180
+ console.log('✅ Schema pushed to database successfully');
181
+ }
182
+ catch (error) {
183
+ console.error('❌ Failed to push schema to database:', error.message);
184
+ process.exit(1);
185
+ }
186
+ }
187
+ function migratePrismaSchema() {
188
+ console.log('🔄 Running Prisma migrations...');
189
+ try {
190
+ (0, child_process_1.execSync)('npx prisma migrate dev', { stdio: 'inherit' });
191
+ console.log('✅ Migrations completed successfully');
192
+ }
193
+ catch (error) {
194
+ console.error('❌ Failed to run migrations:', error.message);
195
+ process.exit(1);
196
+ }
197
+ }
198
+ // Membuat CLI program
199
+ const program = new commander_1.Command();
200
+ program
201
+ .name('crudora')
202
+ .description('CLI for Crudora - TypeScript framework for automated CRUD API generation')
203
+ .version('0.1.0-alpha.3');
204
+ program
205
+ .command('init')
206
+ .description('Initialize a new Crudora project')
207
+ .action(initProject);
208
+ program
209
+ .command('studio')
210
+ .description('Start Prisma Studio')
211
+ .action(runPrismaStudio);
212
+ program
213
+ .command('generate')
214
+ .description('Generate Prisma Client')
215
+ .action(generatePrismaClient);
216
+ program
217
+ .command('push')
218
+ .description('Push Prisma schema to database')
219
+ .action(pushPrismaSchema);
220
+ program
221
+ .command('migrate')
222
+ .description('Run Prisma migrations')
223
+ .action(migratePrismaSchema);
224
+ program.parse();
225
+ // Di akhir file, ubah export jika ada
226
+ module.exports = {
227
+ initProject,
228
+ runPrismaStudio,
229
+ generatePrismaClient,
230
+ pushPrismaSchema,
231
+ migratePrismaSchema
232
+ };
233
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"crudora.d.ts","sourceRoot":"","sources":["../../src/core/crudora.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAElD,qBAAa,OAAO;IAClB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,MAAM,CAA4C;IAC1D,OAAO,CAAC,YAAY,CAA2C;IAC/D,OAAO,CAAC,YAAY,CAIZ;gBAEI,MAAM,CAAC,EAAE,YAAY;IAQjC,aAAa,CAAC,GAAG,YAAY,EAAE,gBAAgB,EAAE,GAAG,IAAI;IAWxD,aAAa,CAAC,CAAC,SAAS,KAAK,EAAE,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;IAQ9E,oBAAoB,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM;IAKvD,mBAAmB,CAAC,CAAC,SAAS,KAAK,EAAE,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAI5F,yBAAyB,CAAC,CAAC,SAAS,KAAK,EAAE,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAKzF,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAK9D,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAK/D,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAK9D,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAKjE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAMhE,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,GAAE,MAAe,GAAG,IAAI;CA+G9D"}
1
+ {"version":3,"file":"crudora.d.ts","sourceRoot":"","sources":["../../src/core/crudora.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAElD,qBAAa,OAAO;IAClB,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,MAAM,CAA4C;IAC1D,OAAO,CAAC,YAAY,CAA2C;IAC/D,OAAO,CAAC,YAAY,CAIZ;gBAEI,MAAM,CAAC,EAAE,YAAY;IAQjC,aAAa,CAAC,GAAG,YAAY,EAAE,gBAAgB,EAAE,GAAG,IAAI;IAWxD,aAAa,CAAC,CAAC,SAAS,KAAK,EAAE,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;IAQ9E,oBAAoB,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM;IAKvD,mBAAmB,CAAC,CAAC,SAAS,KAAK,EAAE,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAI5F,yBAAyB,CAAC,CAAC,SAAS,KAAK,EAAE,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAKzF,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAK9D,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAK/D,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAK9D,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAKjE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAOhE,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,GAAE,MAAe,GAAG,IAAI;CAsK9D"}
@@ -64,7 +64,52 @@ class Crudora {
64
64
  return this;
65
65
  }
66
66
  // Auto-generate REST API routes
67
+ // Auto-generate REST API routes
67
68
  generateRoutes(app, basePath = '/api') {
69
+ // Add API documentation endpoint
70
+ app.get(basePath, (req, res) => {
71
+ const routes = [];
72
+ // Add CRUD routes for each model
73
+ for (const [modelName, modelClass] of this.models) {
74
+ const routePath = `${basePath}/${modelClass.getTableName()}`;
75
+ routes.push({
76
+ method: 'GET',
77
+ path: routePath,
78
+ description: `List all ${modelClass.getTableName()}`,
79
+ type: 'CRUD'
80
+ }, {
81
+ method: 'GET',
82
+ path: `${routePath}/:id`,
83
+ description: `Get ${modelClass.getTableName()} by ID`,
84
+ type: 'CRUD'
85
+ }, {
86
+ method: 'POST',
87
+ path: routePath,
88
+ description: `Create new ${modelClass.getTableName()}`,
89
+ type: 'CRUD'
90
+ }, {
91
+ method: 'PUT',
92
+ path: `${routePath}/:id`,
93
+ description: `Update ${modelClass.getTableName()} by ID`,
94
+ type: 'CRUD'
95
+ }, {
96
+ method: 'DELETE',
97
+ path: `${routePath}/:id`,
98
+ description: `Delete ${modelClass.getTableName()} by ID`,
99
+ type: 'CRUD'
100
+ });
101
+ }
102
+ // Add custom routes
103
+ for (const route of this.customRoutes) {
104
+ routes.push({
105
+ method: route.method,
106
+ path: `${basePath}${route.path}`,
107
+ description: `Custom ${route.method} route`,
108
+ type: 'Custom'
109
+ });
110
+ }
111
+ res.json({ routes });
112
+ });
68
113
  // Generate CRUD routes for models
69
114
  for (const [modelName, modelClass] of this.models) {
70
115
  const repository = this.getRepository(modelClass);
@@ -108,11 +153,11 @@ class Crudora {
108
153
  res.status(500).json({ error: 'Internal server error' });
109
154
  }
110
155
  });
111
- // POST /api/model - Create (gunakan strict validation)
156
+ // POST /api/model - Create
112
157
  app.post(routePath, async (req, res) => {
113
158
  try {
114
- const strictValidationSchema = this.getStrictValidationSchema(modelClass);
115
- const validatedData = strictValidationSchema.parse(req.body);
159
+ // Gunakan partial validation schema instead of strict
160
+ const validatedData = validationSchema.parse(req.body);
116
161
  const item = await repository.create(validatedData);
117
162
  res.status(201).json(item);
118
163
  }