crudora 0.1.0-alpha.1
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/LICENSE +21 -0
- package/README.md +202 -0
- package/dist/core/crudora.d.ts +24 -0
- package/dist/core/crudora.d.ts.map +1 -0
- package/dist/core/crudora.js +176 -0
- package/dist/core/crudora.js.map +1 -0
- package/dist/core/crudoraServer.d.ts +30 -0
- package/dist/core/crudoraServer.d.ts.map +1 -0
- package/dist/core/crudoraServer.js +83 -0
- package/dist/core/crudoraServer.js.map +1 -0
- package/dist/core/model.d.ts +20 -0
- package/dist/core/model.d.ts.map +1 -0
- package/dist/core/model.js +15 -0
- package/dist/core/model.js.map +1 -0
- package/dist/core/repository.d.ts +20 -0
- package/dist/core/repository.d.ts.map +1 -0
- package/dist/core/repository.js +42 -0
- package/dist/core/repository.js.map +1 -0
- package/dist/core/schemaGenerator.d.ts +6 -0
- package/dist/core/schemaGenerator.d.ts.map +1 -0
- package/dist/core/schemaGenerator.js +43 -0
- package/dist/core/schemaGenerator.js.map +1 -0
- package/dist/decorators/model.d.ts +9 -0
- package/dist/decorators/model.d.ts.map +1 -0
- package/dist/decorators/model.js +46 -0
- package/dist/decorators/model.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/scripts/copy-assets.js +27 -0
- package/dist/scripts/postinstall.js +14 -0
- package/dist/templates/schema.prisma +22 -0
- package/dist/types/model.type.d.ts +13 -0
- package/dist/types/model.type.d.ts.map +1 -0
- package/dist/types/model.type.js +3 -0
- package/dist/types/model.type.js.map +1 -0
- package/dist/utils/validation.d.ts +7 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +67 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +82 -0
- package/scripts/copy-assets.js +27 -0
- package/scripts/postinstall.js +14 -0
- package/templates/schema.prisma +22 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Muhammad Surya Jayadiprana
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Crudora
|
|
2
|
+
|
|
3
|
+
Automatic CRUD API generator for TypeScript with Prisma - Build REST APIs in minutes, not hours.
|
|
4
|
+
|
|
5
|
+
[](https://badge.fury.io/js/crudora)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://www.typescriptlang.org/)
|
|
8
|
+
|
|
9
|
+
## ✨ Features
|
|
10
|
+
|
|
11
|
+
- 🚀 **Zero Configuration**: Generate CRUD APIs instantly
|
|
12
|
+
- 🎯 **Type-Safe**: Full TypeScript support with Prisma integration
|
|
13
|
+
- 🔧 **Flexible**: Support for both decorator and inheritance patterns
|
|
14
|
+
- 📊 **Auto Pagination**: Built-in pagination and filtering
|
|
15
|
+
- 🛡️ **Validation**: Automatic request validation with Zod
|
|
16
|
+
- 🔌 **Extensible**: Custom routes and middleware support
|
|
17
|
+
- 📖 **Schema Generation**: Auto-generate Prisma schemas from models
|
|
18
|
+
- 🗄️ **Repository Pattern**: Built-in repository for database operations
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install crudora prisma @prisma/client reflect-metadata
|
|
24
|
+
# or
|
|
25
|
+
yarn add crudora prisma @prisma/client reflect-metadata
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
### Method 1: Simple Inheritance (Recommended)
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import { CrudoraServer, Model } from "crudora";
|
|
34
|
+
import { PrismaClient } from "@prisma/client";
|
|
35
|
+
|
|
36
|
+
const prisma = new PrismaClient();
|
|
37
|
+
|
|
38
|
+
// Define your models
|
|
39
|
+
class User extends Model {
|
|
40
|
+
static tableName = "users";
|
|
41
|
+
static primaryKey = "id";
|
|
42
|
+
static timestamps = true;
|
|
43
|
+
static fillable = ["name", "email"];
|
|
44
|
+
static hidden = ["password"];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
class Post extends Model {
|
|
48
|
+
static tableName = "posts";
|
|
49
|
+
static fillable = ["title", "content", "authorId"];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Create server and register models
|
|
53
|
+
const server = new CrudoraServer({
|
|
54
|
+
port: 3000,
|
|
55
|
+
prisma: prisma,
|
|
56
|
+
cors: true,
|
|
57
|
+
basePath: "/api"
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
server
|
|
61
|
+
.registerModel(User, Post)
|
|
62
|
+
.generateRoutes()
|
|
63
|
+
.listen(() => {
|
|
64
|
+
console.log('Server running on port 3000');
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Method 2: Decorator Pattern
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import "reflect-metadata";
|
|
72
|
+
import { CrudoraServer, Model, Field } from "crudora";
|
|
73
|
+
import { PrismaClient } from "@prisma/client";
|
|
74
|
+
|
|
75
|
+
const prisma = new PrismaClient();
|
|
76
|
+
|
|
77
|
+
@Model({ tableName: "users" })
|
|
78
|
+
class User {
|
|
79
|
+
@Field({ type: "uuid", primary: true })
|
|
80
|
+
id!: string;
|
|
81
|
+
|
|
82
|
+
@Field({ type: "string", required: true })
|
|
83
|
+
name!: string;
|
|
84
|
+
|
|
85
|
+
@Field({ type: "string", unique: true, required: true })
|
|
86
|
+
email!: string;
|
|
87
|
+
|
|
88
|
+
@Field({ type: "date" })
|
|
89
|
+
createdAt!: Date;
|
|
90
|
+
|
|
91
|
+
@Field({ type: "date" })
|
|
92
|
+
updatedAt!: Date;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const server = new CrudoraServer({
|
|
96
|
+
port: 3000,
|
|
97
|
+
prisma: prisma,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
server
|
|
101
|
+
.registerModel(User)
|
|
102
|
+
.generateRoutes()
|
|
103
|
+
.listen();
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Generated API Endpoints
|
|
107
|
+
|
|
108
|
+
For each registered model, Crudora automatically generates:
|
|
109
|
+
|
|
110
|
+
- `GET /api/{tableName}` - List all records with pagination and filtering
|
|
111
|
+
- `GET /api/{tableName}/:id` - Get record by ID
|
|
112
|
+
- `POST /api/{tableName}` - Create new record
|
|
113
|
+
- `PUT /api/{tableName}/:id` - Update record
|
|
114
|
+
- `DELETE /api/{tableName}/:id` - Delete record
|
|
115
|
+
|
|
116
|
+
### Query Parameters
|
|
117
|
+
|
|
118
|
+
- `skip` - Number of records to skip (pagination)
|
|
119
|
+
- `take` - Number of records to take (pagination)
|
|
120
|
+
- `orderBy` - Sort order (e.g., `createdAt:desc`)
|
|
121
|
+
- `where` - Filter conditions
|
|
122
|
+
|
|
123
|
+
## Advanced Usage
|
|
124
|
+
|
|
125
|
+
### Using Repositories
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
const crudora = server.getCrudora();
|
|
129
|
+
const userRepo = crudora.getRepository(User);
|
|
130
|
+
|
|
131
|
+
// Create user
|
|
132
|
+
const user = await userRepo.create({
|
|
133
|
+
name: "John Doe",
|
|
134
|
+
email: "john@example.com"
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Find users
|
|
138
|
+
const users = await userRepo.findAll({
|
|
139
|
+
skip: 0,
|
|
140
|
+
take: 10,
|
|
141
|
+
where: { active: true },
|
|
142
|
+
orderBy: { createdAt: 'desc' }
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Count users
|
|
146
|
+
const count = await userRepo.count({ active: true });
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Custom Routes
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
server
|
|
153
|
+
.get("/health", (req, res) => {
|
|
154
|
+
res.json({ status: "ok", timestamp: new Date() });
|
|
155
|
+
})
|
|
156
|
+
.post("/users/:id/activate", async (req, res) => {
|
|
157
|
+
const userRepo = server.getCrudora().getRepository(User);
|
|
158
|
+
const user = await userRepo.update(req.params.id, { active: true });
|
|
159
|
+
res.json(user);
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Schema Generation
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
// Generate Prisma schema from your models
|
|
167
|
+
const schema = server.getCrudora().generatePrismaSchema("postgresql");
|
|
168
|
+
console.log(schema);
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Validation
|
|
172
|
+
|
|
173
|
+
Crudora automatically generates Zod validation schemas:
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
const crudora = server.getCrudora();
|
|
177
|
+
|
|
178
|
+
// Get partial validation schema (for updates)
|
|
179
|
+
const partialSchema = crudora.getValidationSchema(User);
|
|
180
|
+
|
|
181
|
+
// Get strict validation schema (for creation)
|
|
182
|
+
const strictSchema = crudora.getStrictValidationSchema(User);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Documentation
|
|
186
|
+
|
|
187
|
+
- [API Reference](./docs/api.md)
|
|
188
|
+
- [Model Definition Guide](./docs/models.md)
|
|
189
|
+
- [Custom Routes](./docs/custom-routes.md)
|
|
190
|
+
- [Deployment Guide](./docs/deployment.md)
|
|
191
|
+
|
|
192
|
+
## Contributing
|
|
193
|
+
|
|
194
|
+
We welcome contributions! Please see our [Contributing Guide](./CONTRIBUTING.md) for details.
|
|
195
|
+
|
|
196
|
+
## License
|
|
197
|
+
|
|
198
|
+
MIT © [Crudora](https://github.com/suryamsj/crudora)
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
**⚠️ Alpha Version - Not recommended for production use**
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { PrismaClient } from '@prisma/client';
|
|
2
|
+
import { Express } from 'express';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { Repository } from './repository';
|
|
5
|
+
import { Model, ModelConstructor } from './model';
|
|
6
|
+
export declare class Crudora {
|
|
7
|
+
private prisma;
|
|
8
|
+
private models;
|
|
9
|
+
private repositories;
|
|
10
|
+
private customRoutes;
|
|
11
|
+
constructor(prisma?: PrismaClient);
|
|
12
|
+
registerModel(...modelClasses: ModelConstructor[]): this;
|
|
13
|
+
getRepository<T extends Model>(modelClass: ModelConstructor<T>): Repository<T>;
|
|
14
|
+
generatePrismaSchema(databaseProvider?: string): string;
|
|
15
|
+
getValidationSchema<T extends Model>(modelClass: ModelConstructor<T>): z.ZodType<Partial<T>>;
|
|
16
|
+
getStrictValidationSchema<T extends Model>(modelClass: ModelConstructor<T>): z.ZodType<T>;
|
|
17
|
+
get(path: string, handler: (req: any, res: any) => void): this;
|
|
18
|
+
post(path: string, handler: (req: any, res: any) => void): this;
|
|
19
|
+
put(path: string, handler: (req: any, res: any) => void): this;
|
|
20
|
+
delete(path: string, handler: (req: any, res: any) => void): this;
|
|
21
|
+
patch(path: string, handler: (req: any, res: any) => void): this;
|
|
22
|
+
generateRoutes(app: Express, basePath?: string): void;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=crudora.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Crudora = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const repository_1 = require("./repository");
|
|
6
|
+
const schemaGenerator_1 = require("./schemaGenerator");
|
|
7
|
+
const validation_1 = require("../utils/validation");
|
|
8
|
+
class Crudora {
|
|
9
|
+
constructor(prisma) {
|
|
10
|
+
this.models = new Map();
|
|
11
|
+
this.repositories = new Map();
|
|
12
|
+
this.customRoutes = [];
|
|
13
|
+
if (!prisma) {
|
|
14
|
+
console.warn('⚠️ No Prisma client provided. Make sure to install @prisma/client and prisma, then initialize PrismaClient.');
|
|
15
|
+
throw new Error('PrismaClient is required. Please provide a PrismaClient instance.');
|
|
16
|
+
}
|
|
17
|
+
this.prisma = prisma;
|
|
18
|
+
}
|
|
19
|
+
registerModel(...modelClasses) {
|
|
20
|
+
for (const modelClass of modelClasses) {
|
|
21
|
+
const modelName = modelClass.name;
|
|
22
|
+
this.models.set(modelName, modelClass);
|
|
23
|
+
const repository = new repository_1.Repository(modelClass, this.prisma);
|
|
24
|
+
this.repositories.set(modelName, repository);
|
|
25
|
+
}
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
getRepository(modelClass) {
|
|
29
|
+
const repository = this.repositories.get(modelClass.name);
|
|
30
|
+
if (!repository) {
|
|
31
|
+
throw new Error(`Repository for ${modelClass.name} not found. Did you register the model?`);
|
|
32
|
+
}
|
|
33
|
+
return repository;
|
|
34
|
+
}
|
|
35
|
+
generatePrismaSchema(databaseProvider) {
|
|
36
|
+
const modelClasses = Array.from(this.models.values());
|
|
37
|
+
return schemaGenerator_1.SchemaGenerator.generatePrismaSchema(modelClasses, databaseProvider);
|
|
38
|
+
}
|
|
39
|
+
getValidationSchema(modelClass) {
|
|
40
|
+
return validation_1.ValidationGenerator.generateZodSchema(modelClass);
|
|
41
|
+
}
|
|
42
|
+
getStrictValidationSchema(modelClass) {
|
|
43
|
+
return validation_1.ValidationGenerator.generateStrictZodSchema(modelClass);
|
|
44
|
+
}
|
|
45
|
+
// Custom route methods
|
|
46
|
+
get(path, handler) {
|
|
47
|
+
this.customRoutes.push({ method: 'GET', path, handler });
|
|
48
|
+
return this;
|
|
49
|
+
}
|
|
50
|
+
post(path, handler) {
|
|
51
|
+
this.customRoutes.push({ method: 'POST', path, handler });
|
|
52
|
+
return this;
|
|
53
|
+
}
|
|
54
|
+
put(path, handler) {
|
|
55
|
+
this.customRoutes.push({ method: 'PUT', path, handler });
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
delete(path, handler) {
|
|
59
|
+
this.customRoutes.push({ method: 'DELETE', path, handler });
|
|
60
|
+
return this;
|
|
61
|
+
}
|
|
62
|
+
patch(path, handler) {
|
|
63
|
+
this.customRoutes.push({ method: 'PATCH', path, handler });
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
// Auto-generate REST API routes
|
|
67
|
+
generateRoutes(app, basePath = '/api') {
|
|
68
|
+
// Generate CRUD routes for models
|
|
69
|
+
for (const [modelName, modelClass] of this.models) {
|
|
70
|
+
const repository = this.getRepository(modelClass);
|
|
71
|
+
const validationSchema = this.getValidationSchema(modelClass);
|
|
72
|
+
const routePath = `${basePath}/${modelClass.getTableName()}`;
|
|
73
|
+
// GET /api/model - List all
|
|
74
|
+
app.get(routePath, async (req, res) => {
|
|
75
|
+
try {
|
|
76
|
+
const { page = 1, limit = 10, ...filters } = req.query;
|
|
77
|
+
const skip = (Number(page) - 1) * Number(limit);
|
|
78
|
+
const items = await repository.findAll({
|
|
79
|
+
skip,
|
|
80
|
+
take: Number(limit),
|
|
81
|
+
where: filters
|
|
82
|
+
});
|
|
83
|
+
const total = await repository.count(filters);
|
|
84
|
+
res.json({
|
|
85
|
+
data: items,
|
|
86
|
+
pagination: {
|
|
87
|
+
page: Number(page),
|
|
88
|
+
limit: Number(limit),
|
|
89
|
+
total,
|
|
90
|
+
pages: Math.ceil(total / Number(limit))
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
// GET /api/model/:id - Get by ID
|
|
99
|
+
app.get(`${routePath}/:id`, async (req, res) => {
|
|
100
|
+
try {
|
|
101
|
+
const item = await repository.findById(req.params.id);
|
|
102
|
+
if (!item) {
|
|
103
|
+
return res.status(404).json({ error: 'Not found' });
|
|
104
|
+
}
|
|
105
|
+
res.json(item);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
// POST /api/model - Create (gunakan strict validation)
|
|
112
|
+
app.post(routePath, async (req, res) => {
|
|
113
|
+
try {
|
|
114
|
+
const strictValidationSchema = this.getStrictValidationSchema(modelClass);
|
|
115
|
+
const validatedData = strictValidationSchema.parse(req.body);
|
|
116
|
+
const item = await repository.create(validatedData);
|
|
117
|
+
res.status(201).json(item);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (error instanceof zod_1.z.ZodError) {
|
|
121
|
+
return res.status(400).json({ error: 'Validation error', details: error.issues });
|
|
122
|
+
}
|
|
123
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
// PUT /api/model/:id - Update (gunakan partial validation)
|
|
127
|
+
app.put(`${routePath}/:id`, async (req, res) => {
|
|
128
|
+
try {
|
|
129
|
+
const validationSchema = this.getValidationSchema(modelClass);
|
|
130
|
+
const validatedData = validationSchema.parse(req.body);
|
|
131
|
+
const item = await repository.update(req.params.id, validatedData);
|
|
132
|
+
res.json(item);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
if (error instanceof zod_1.z.ZodError) {
|
|
136
|
+
return res.status(400).json({ error: 'Validation error', details: error.issues });
|
|
137
|
+
}
|
|
138
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
// DELETE /api/model/:id - Delete
|
|
142
|
+
app.delete(`${routePath}/:id`, async (req, res) => {
|
|
143
|
+
try {
|
|
144
|
+
await repository.delete(req.params.id);
|
|
145
|
+
res.status(204).send();
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
// Generate custom routes
|
|
153
|
+
for (const route of this.customRoutes) {
|
|
154
|
+
const fullPath = `${basePath}${route.path}`;
|
|
155
|
+
switch (route.method.toLowerCase()) {
|
|
156
|
+
case 'get':
|
|
157
|
+
app.get(fullPath, route.handler);
|
|
158
|
+
break;
|
|
159
|
+
case 'post':
|
|
160
|
+
app.post(fullPath, route.handler);
|
|
161
|
+
break;
|
|
162
|
+
case 'put':
|
|
163
|
+
app.put(fullPath, route.handler);
|
|
164
|
+
break;
|
|
165
|
+
case 'delete':
|
|
166
|
+
app.delete(fullPath, route.handler);
|
|
167
|
+
break;
|
|
168
|
+
case 'patch':
|
|
169
|
+
app.patch(fullPath, route.handler);
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
exports.Crudora = Crudora;
|
|
176
|
+
//# sourceMappingURL=crudora.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crudora.js","sourceRoot":"","sources":["../../src/core/crudora.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Express } from 'express';
|
|
2
|
+
import { Crudora } from './crudora';
|
|
3
|
+
import { PrismaClient } from '@prisma/client';
|
|
4
|
+
import { ModelConstructor } from './model';
|
|
5
|
+
export interface CrudoraServerConfig {
|
|
6
|
+
port?: number;
|
|
7
|
+
cors?: boolean;
|
|
8
|
+
bodyParser?: boolean;
|
|
9
|
+
prisma?: PrismaClient;
|
|
10
|
+
basePath?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare class CrudoraServer {
|
|
13
|
+
private app;
|
|
14
|
+
private crudora;
|
|
15
|
+
private config;
|
|
16
|
+
constructor(config?: CrudoraServerConfig);
|
|
17
|
+
private setupMiddleware;
|
|
18
|
+
registerModel(...modelClasses: ModelConstructor[]): this;
|
|
19
|
+
generateRoutes(): this;
|
|
20
|
+
use(middleware: any): this;
|
|
21
|
+
listen(callback?: () => void): void;
|
|
22
|
+
getApp(): Express;
|
|
23
|
+
getCrudora(): Crudora;
|
|
24
|
+
get(path: string, handler: (req: any, res: any) => void): this;
|
|
25
|
+
post(path: string, handler: (req: any, res: any) => void): this;
|
|
26
|
+
put(path: string, handler: (req: any, res: any) => void): this;
|
|
27
|
+
delete(path: string, handler: (req: any, res: any) => void): this;
|
|
28
|
+
patch(path: string, handler: (req: any, res: any) => void): this;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=crudoraServer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crudoraServer.d.ts","sourceRoot":"","sources":["../../src/core/crudoraServer.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAS,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAElD,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,GAAG,CAAU;IACrB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,MAAM,CAAsB;gBAExB,MAAM,GAAE,mBAAwB;IAO5C,OAAO,CAAC,eAAe;IAoBvB,aAAa,CAAC,GAAG,YAAY,EAAE,gBAAgB,EAAE,GAAG,IAAI;IAKxD,cAAc,IAAI,IAAI;IAKtB,GAAG,CAAC,UAAU,EAAE,GAAG,GAAG,IAAI;IAK1B,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI;IAQnC,MAAM,IAAI,OAAO;IAIjB,UAAU,IAAI,OAAO;IAIrB,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;CAIjE"}
|
|
@@ -0,0 +1,83 @@
|
|
|
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
|
+
exports.CrudoraServer = void 0;
|
|
7
|
+
const express_1 = __importDefault(require("express"));
|
|
8
|
+
const crudora_1 = require("./crudora");
|
|
9
|
+
class CrudoraServer {
|
|
10
|
+
constructor(config = {}) {
|
|
11
|
+
this.config = { port: 3000, cors: true, bodyParser: true, basePath: '/api', ...config };
|
|
12
|
+
this.app = (0, express_1.default)();
|
|
13
|
+
this.crudora = new crudora_1.Crudora(config.prisma);
|
|
14
|
+
this.setupMiddleware();
|
|
15
|
+
}
|
|
16
|
+
setupMiddleware() {
|
|
17
|
+
if (this.config.bodyParser) {
|
|
18
|
+
this.app.use(express_1.default.json());
|
|
19
|
+
this.app.use(express_1.default.urlencoded({ extended: true }));
|
|
20
|
+
}
|
|
21
|
+
if (this.config.cors) {
|
|
22
|
+
this.app.use((req, res, next) => {
|
|
23
|
+
res.header('Access-Control-Allow-Origin', '*');
|
|
24
|
+
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
|
|
25
|
+
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
26
|
+
if (req.method === 'OPTIONS') {
|
|
27
|
+
res.sendStatus(200);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
next();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
registerModel(...modelClasses) {
|
|
36
|
+
this.crudora.registerModel(...modelClasses);
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
generateRoutes() {
|
|
40
|
+
this.crudora.generateRoutes(this.app, this.config.basePath);
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
use(middleware) {
|
|
44
|
+
this.app.use(middleware);
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
listen(callback) {
|
|
48
|
+
this.app.listen(this.config.port, () => {
|
|
49
|
+
console.log(`🚀 Crudora server running on port ${this.config.port}`);
|
|
50
|
+
console.log(`📚 API available at http://localhost:${this.config.port}${this.config.basePath}`);
|
|
51
|
+
if (callback)
|
|
52
|
+
callback();
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
getApp() {
|
|
56
|
+
return this.app;
|
|
57
|
+
}
|
|
58
|
+
getCrudora() {
|
|
59
|
+
return this.crudora;
|
|
60
|
+
}
|
|
61
|
+
get(path, handler) {
|
|
62
|
+
this.crudora.get(path, handler);
|
|
63
|
+
return this;
|
|
64
|
+
}
|
|
65
|
+
post(path, handler) {
|
|
66
|
+
this.crudora.post(path, handler);
|
|
67
|
+
return this;
|
|
68
|
+
}
|
|
69
|
+
put(path, handler) {
|
|
70
|
+
this.crudora.put(path, handler);
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
delete(path, handler) {
|
|
74
|
+
this.crudora.delete(path, handler);
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
patch(path, handler) {
|
|
78
|
+
this.crudora.patch(path, handler);
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.CrudoraServer = CrudoraServer;
|
|
83
|
+
//# sourceMappingURL=crudoraServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crudoraServer.js","sourceRoot":"","sources":["../../src/core/crudoraServer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare abstract class Model {
|
|
2
|
+
static tableName?: string;
|
|
3
|
+
static primaryKey?: string;
|
|
4
|
+
static timestamps?: boolean;
|
|
5
|
+
static fillable?: string[];
|
|
6
|
+
static hidden?: string[];
|
|
7
|
+
static getTableName(): string;
|
|
8
|
+
static getPrimaryKey(): string;
|
|
9
|
+
}
|
|
10
|
+
export type ModelConstructor<T extends Model = Model> = {
|
|
11
|
+
new (): T;
|
|
12
|
+
tableName?: string;
|
|
13
|
+
primaryKey?: string;
|
|
14
|
+
timestamps?: boolean;
|
|
15
|
+
fillable?: string[];
|
|
16
|
+
hidden?: string[];
|
|
17
|
+
getTableName(): string;
|
|
18
|
+
getPrimaryKey(): string;
|
|
19
|
+
} & typeof Model;
|
|
20
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/core/model.ts"],"names":[],"mappings":"AAAA,8BAAsB,KAAK;IACzB,MAAM,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,CAAQ;IAClC,MAAM,CAAC,UAAU,CAAC,EAAE,OAAO,CAAQ;IACnC,MAAM,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,MAAM,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAEzB,MAAM,CAAC,YAAY,IAAI,MAAM;IAI7B,MAAM,CAAC,aAAa,IAAI,MAAM;CAG/B;AAGD,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,KAAK,GAAG,KAAK,IAAI;IACtD,QAAQ,CAAC,CAAC;IACV,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,IAAI,MAAM,CAAC;IACvB,aAAa,IAAI,MAAM,CAAC;CACzB,GAAG,OAAO,KAAK,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Model = void 0;
|
|
4
|
+
class Model {
|
|
5
|
+
static getTableName() {
|
|
6
|
+
return this.tableName || this.name.toLowerCase() + 's';
|
|
7
|
+
}
|
|
8
|
+
static getPrimaryKey() {
|
|
9
|
+
return this.primaryKey || 'id';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.Model = Model;
|
|
13
|
+
Model.primaryKey = 'id';
|
|
14
|
+
Model.timestamps = true;
|
|
15
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/core/model.ts"],"names":[],"mappings":";;;AAAA,MAAsB,KAAK;IAOzB,MAAM,CAAC,YAAY;QACjB,OAAO,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,GAAG,CAAC;IACzD,CAAC;IAED,MAAM,CAAC,aAAa;QAClB,OAAO,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC;IACjC,CAAC;;AAbH,sBAcC;AAZQ,gBAAU,GAAY,IAAI,CAAC;AAC3B,gBAAU,GAAa,IAAI,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { PrismaClient } from '@prisma/client';
|
|
2
|
+
import { Model, ModelConstructor } from './model';
|
|
3
|
+
export declare class Repository<T extends Model> {
|
|
4
|
+
private prisma;
|
|
5
|
+
private modelName;
|
|
6
|
+
private modelClass;
|
|
7
|
+
constructor(modelClass: ModelConstructor<T>, prisma: PrismaClient);
|
|
8
|
+
create(data: Partial<T>): Promise<T>;
|
|
9
|
+
findById(id: string): Promise<T | null>;
|
|
10
|
+
findAll(options?: {
|
|
11
|
+
skip?: number;
|
|
12
|
+
take?: number;
|
|
13
|
+
where?: any;
|
|
14
|
+
orderBy?: any;
|
|
15
|
+
}): Promise<T[]>;
|
|
16
|
+
update(id: string, data: Partial<T>): Promise<T>;
|
|
17
|
+
delete(id: string): Promise<T>;
|
|
18
|
+
count(where?: any): Promise<number>;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/core/repository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C,OAAO,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAElD,qBAAa,UAAU,CAAC,CAAC,SAAS,KAAK;IACrC,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAsB;gBAE5B,UAAU,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY;IAS3D,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAKpC,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IAKvC,OAAO,CAAC,OAAO,CAAC,EAAE;QACtB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,GAAG,CAAC;QACZ,OAAO,CAAC,EAAE,GAAG,CAAC;KACf,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAKV,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAQhD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAK9B,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC;CAI1C"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Repository = void 0;
|
|
4
|
+
const model_1 = require("../decorators/model");
|
|
5
|
+
class Repository {
|
|
6
|
+
constructor(modelClass, prisma) {
|
|
7
|
+
this.prisma = prisma;
|
|
8
|
+
this.modelClass = modelClass;
|
|
9
|
+
// Try to get metadata from decorator first, then fallback to static methods
|
|
10
|
+
const metadata = (0, model_1.getModelMetadata)(modelClass);
|
|
11
|
+
this.modelName = metadata?.tableName || modelClass.getTableName();
|
|
12
|
+
}
|
|
13
|
+
async create(data) {
|
|
14
|
+
const model = this.prisma[this.modelName];
|
|
15
|
+
return await model.create({ data });
|
|
16
|
+
}
|
|
17
|
+
async findById(id) {
|
|
18
|
+
const model = this.prisma[this.modelName];
|
|
19
|
+
return await model.findUnique({ where: { id } });
|
|
20
|
+
}
|
|
21
|
+
async findAll(options) {
|
|
22
|
+
const model = this.prisma[this.modelName];
|
|
23
|
+
return await model.findMany(options);
|
|
24
|
+
}
|
|
25
|
+
async update(id, data) {
|
|
26
|
+
const model = this.prisma[this.modelName];
|
|
27
|
+
return await model.update({
|
|
28
|
+
where: { id },
|
|
29
|
+
data
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
async delete(id) {
|
|
33
|
+
const model = this.prisma[this.modelName];
|
|
34
|
+
return await model.delete({ where: { id } });
|
|
35
|
+
}
|
|
36
|
+
async count(where) {
|
|
37
|
+
const model = this.prisma[this.modelName];
|
|
38
|
+
return await model.count({ where });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.Repository = Repository;
|
|
42
|
+
//# sourceMappingURL=repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository.js","sourceRoot":"","sources":["../../src/core/repository.ts"],"names":[],"mappings":";;;AACA,+CAAuD;AAGvD,MAAa,UAAU;IAKrB,YAAY,UAA+B,EAAE,MAAoB;QAC/D,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;QAE7B,4EAA4E;QAC5E,MAAM,QAAQ,GAAG,IAAA,wBAAgB,EAAC,UAAiB,CAAC,CAAC;QACrD,IAAI,CAAC,SAAS,GAAG,QAAQ,EAAE,SAAS,IAAI,UAAU,CAAC,YAAY,EAAE,CAAC;IACpE,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAgB;QAC3B,MAAM,KAAK,GAAI,IAAI,CAAC,MAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACnD,OAAO,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,EAAU;QACvB,MAAM,KAAK,GAAI,IAAI,CAAC,MAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACnD,OAAO,MAAM,KAAK,CAAC,UAAU,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAKb;QACC,MAAM,KAAK,GAAI,IAAI,CAAC,MAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACnD,OAAO,MAAM,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU,EAAE,IAAgB;QACvC,MAAM,KAAK,GAAI,IAAI,CAAC,MAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACnD,OAAO,MAAM,KAAK,CAAC,MAAM,CAAC;YACxB,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,IAAI;SACL,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,MAAM,KAAK,GAAI,IAAI,CAAC,MAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACnD,OAAO,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,KAAW;QACrB,MAAM,KAAK,GAAI,IAAI,CAAC,MAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACnD,OAAO,MAAM,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACtC,CAAC;CACF;AAnDD,gCAmDC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemaGenerator.d.ts","sourceRoot":"","sources":["../../src/core/schemaGenerator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,qBAAa,eAAe;IAC1B,MAAM,CAAC,oBAAoB,CAAC,MAAM,EAAE,CAAC,OAAO,KAAK,CAAC,EAAE,EAAE,gBAAgB,GAAE,MAAiB,GAAG,MAAM;IAmBlG,OAAO,CAAC,MAAM,CAAC,4BAA4B;CA2B5C"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SchemaGenerator = void 0;
|
|
4
|
+
class SchemaGenerator {
|
|
5
|
+
static generatePrismaSchema(models, databaseProvider = 'sqlite') {
|
|
6
|
+
let schema = `// Auto-generated Prisma schema\n// Generated by Crudora Framework\n\n`;
|
|
7
|
+
schema += `generator client {\n provider = "prisma-client-js"\n}\n\n`;
|
|
8
|
+
// Fix: Use the provided database provider directly
|
|
9
|
+
if (databaseProvider === 'sqlite') {
|
|
10
|
+
schema += `datasource db {\n provider = "sqlite"\n url = env("DATABASE_URL")\n}\n\n`;
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
schema += `datasource db {\n provider = "${databaseProvider}"\n url = env("DATABASE_URL")\n}\n\n`;
|
|
14
|
+
}
|
|
15
|
+
for (const model of models) {
|
|
16
|
+
schema += this.generateModelSchemaFromClass(model);
|
|
17
|
+
}
|
|
18
|
+
return schema;
|
|
19
|
+
}
|
|
20
|
+
static generateModelSchemaFromClass(modelClass) {
|
|
21
|
+
const tableName = modelClass.getTableName();
|
|
22
|
+
const primaryKey = modelClass.getPrimaryKey();
|
|
23
|
+
let schema = `model ${modelClass.name} {\n`;
|
|
24
|
+
// Add primary key
|
|
25
|
+
schema += ` ${primaryKey} String @id @default(uuid())\n`;
|
|
26
|
+
// Add common fields (you can customize this based on your needs)
|
|
27
|
+
schema += ` createdAt DateTime @default(now())\n`;
|
|
28
|
+
schema += ` updatedAt DateTime @updatedAt\n`;
|
|
29
|
+
// Add custom fields if defined in static properties
|
|
30
|
+
if (modelClass.fillable) {
|
|
31
|
+
for (const field of modelClass.fillable) {
|
|
32
|
+
if (field !== primaryKey) {
|
|
33
|
+
schema += ` ${field} String?\n`;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
schema += `\n @@map("${tableName}")\n`;
|
|
38
|
+
schema += `}\n\n`;
|
|
39
|
+
return schema;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.SchemaGenerator = SchemaGenerator;
|
|
43
|
+
//# sourceMappingURL=schemaGenerator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemaGenerator.js","sourceRoot":"","sources":["../../src/core/schemaGenerator.ts"],"names":[],"mappings":";;;AAEA,MAAa,eAAe;IAC1B,MAAM,CAAC,oBAAoB,CAAC,MAAwB,EAAE,mBAA2B,QAAQ;QACvF,IAAI,MAAM,GAAG,wEAAwE,CAAC;QAEtF,MAAM,IAAI,4DAA4D,CAAC;QAEvE,mDAAmD;QACnD,IAAI,gBAAgB,KAAK,QAAQ,EAAE,CAAC;YAClC,MAAM,IAAI,4EAA4E,CAAC;QACzF,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,kCAAkC,gBAAgB,uCAAuC,CAAC;QACtG,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,MAAM,IAAI,IAAI,CAAC,4BAA4B,CAAC,KAAK,CAAC,CAAC;QACrD,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,MAAM,CAAC,4BAA4B,CAAC,UAAwB;QAClE,MAAM,SAAS,GAAG,UAAU,CAAC,YAAY,EAAE,CAAC;QAC5C,MAAM,UAAU,GAAG,UAAU,CAAC,aAAa,EAAE,CAAC;QAE9C,IAAI,MAAM,GAAG,SAAS,UAAU,CAAC,IAAI,MAAM,CAAC;QAE5C,kBAAkB;QAClB,MAAM,IAAI,KAAK,UAAU,gCAAgC,CAAC;QAE1D,iEAAiE;QACjE,MAAM,IAAI,wCAAwC,CAAC;QACnD,MAAM,IAAI,mCAAmC,CAAC;QAE9C,oDAAoD;QACpD,IAAI,UAAU,CAAC,QAAQ,EAAE,CAAC;YACxB,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,QAAQ,EAAE,CAAC;gBACxC,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC;oBACzB,MAAM,IAAI,KAAK,KAAK,YAAY,CAAC;gBACnC,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,IAAI,cAAc,SAAS,MAAM,CAAC;QACxC,MAAM,IAAI,OAAO,CAAC;QAElB,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AA/CD,0CA+CC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { FieldOptions, ModelOptions } from '../types/model.type';
|
|
3
|
+
export declare function Model(options?: ModelOptions): <T extends {
|
|
4
|
+
new (...args: any[]): {};
|
|
5
|
+
}>(constructor: T) => T;
|
|
6
|
+
export declare function Field(options: FieldOptions): (target: any, context: any) => void;
|
|
7
|
+
export declare function getModelMetadata(target: any): any;
|
|
8
|
+
export declare function getFieldMetadata(target: any): any;
|
|
9
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/decorators/model.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAKjE,wBAAgB,KAAK,CAAC,OAAO,GAAE,YAAiB,IAC7B,CAAC,SAAS;IAAE,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,CAAA;CAAE,EAAE,aAAa,CAAC,OAQxE;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,YAAY,IACxB,QAAQ,GAAG,EAAE,SAAS,GAAG,UAiB3C;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,GAAG,OAE3C;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,GAAG,OAE3C"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Model = Model;
|
|
4
|
+
exports.Field = Field;
|
|
5
|
+
exports.getModelMetadata = getModelMetadata;
|
|
6
|
+
exports.getFieldMetadata = getFieldMetadata;
|
|
7
|
+
require("reflect-metadata");
|
|
8
|
+
const MODEL_METADATA_KEY = Symbol('model');
|
|
9
|
+
const FIELD_METADATA_KEY = Symbol('field');
|
|
10
|
+
function Model(options = {}) {
|
|
11
|
+
return function (constructor) {
|
|
12
|
+
Reflect.defineMetadata(MODEL_METADATA_KEY, {
|
|
13
|
+
tableName: options.tableName || constructor.name.toLowerCase(),
|
|
14
|
+
timestamps: options.timestamps !== false,
|
|
15
|
+
constructor
|
|
16
|
+
}, constructor);
|
|
17
|
+
return constructor;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function Field(options) {
|
|
21
|
+
return function (target, context) {
|
|
22
|
+
// Support both old and new decorator syntax
|
|
23
|
+
if (typeof context === 'string') {
|
|
24
|
+
// Old decorator syntax (target, propertyKey)
|
|
25
|
+
const propertyKey = context;
|
|
26
|
+
const existingFields = Reflect.getMetadata(FIELD_METADATA_KEY, target.constructor) || {};
|
|
27
|
+
existingFields[propertyKey] = options;
|
|
28
|
+
Reflect.defineMetadata(FIELD_METADATA_KEY, existingFields, target.constructor);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
// New decorator syntax (target, context)
|
|
32
|
+
const propertyKey = context.name;
|
|
33
|
+
const constructor = target.constructor;
|
|
34
|
+
const existingFields = Reflect.getMetadata(FIELD_METADATA_KEY, constructor) || {};
|
|
35
|
+
existingFields[propertyKey] = options;
|
|
36
|
+
Reflect.defineMetadata(FIELD_METADATA_KEY, existingFields, constructor);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function getModelMetadata(target) {
|
|
41
|
+
return Reflect.getMetadata(MODEL_METADATA_KEY, target);
|
|
42
|
+
}
|
|
43
|
+
function getFieldMetadata(target) {
|
|
44
|
+
return Reflect.getMetadata(FIELD_METADATA_KEY, target) || {};
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/decorators/model.ts"],"names":[],"mappings":";;AAMA,sBASC;AAED,sBAkBC;AAED,4CAEC;AAED,4CAEC;AA3CD,4BAA0B;AAG1B,MAAM,kBAAkB,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;AAC3C,MAAM,kBAAkB,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;AAE3C,SAAgB,KAAK,CAAC,UAAwB,EAAE;IAC9C,OAAO,UAAkD,WAAc;QACrE,OAAO,CAAC,cAAc,CAAC,kBAAkB,EAAE;YACzC,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,WAAW,CAAC,IAAI,CAAC,WAAW,EAAE;YAC9D,UAAU,EAAE,OAAO,CAAC,UAAU,KAAK,KAAK;YACxC,WAAW;SACZ,EAAE,WAAW,CAAC,CAAC;QAChB,OAAO,WAAW,CAAC;IACrB,CAAC,CAAC;AACJ,CAAC;AAED,SAAgB,KAAK,CAAC,OAAqB;IACzC,OAAO,UAAU,MAAW,EAAE,OAAY;QACxC,4CAA4C;QAC5C,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAChC,6CAA6C;YAC7C,MAAM,WAAW,GAAG,OAAO,CAAC;YAC5B,MAAM,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC,kBAAkB,EAAE,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;YACzF,cAAc,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC;YACtC,OAAO,CAAC,cAAc,CAAC,kBAAkB,EAAE,cAAc,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;QACjF,CAAC;aAAM,CAAC;YACN,yCAAyC;YACzC,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;YACjC,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;YACvC,MAAM,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC,kBAAkB,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC;YAClF,cAAc,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC;YACtC,OAAO,CAAC,cAAc,CAAC,kBAAkB,EAAE,cAAc,EAAE,WAAW,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,SAAgB,gBAAgB,CAAC,MAAW;IAC1C,OAAO,OAAO,CAAC,WAAW,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC;AACzD,CAAC;AAED,SAAgB,gBAAgB,CAAC,MAAW;IAC1C,OAAO,OAAO,CAAC,WAAW,CAAC,kBAAkB,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;AAC/D,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { Crudora } from './core/crudora';
|
|
2
|
+
export { CrudoraServer } from './core/crudoraServer';
|
|
3
|
+
export { Repository } from './core/repository';
|
|
4
|
+
export { SchemaGenerator } from './core/schemaGenerator';
|
|
5
|
+
export { ValidationGenerator } from './utils/validation';
|
|
6
|
+
export { Model } from './core/model';
|
|
7
|
+
export type { CrudoraServerConfig } from './core/crudoraServer';
|
|
8
|
+
export { Model as ModelDecorator, Field } from './decorators/model';
|
|
9
|
+
export type { ModelOptions, FieldOptions } from './types/model.type';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,YAAY,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAGhE,OAAO,EAAE,KAAK,IAAI,cAAc,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AACpE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Field = exports.ModelDecorator = exports.Model = exports.ValidationGenerator = exports.SchemaGenerator = exports.Repository = exports.CrudoraServer = exports.Crudora = void 0;
|
|
4
|
+
var crudora_1 = require("./core/crudora");
|
|
5
|
+
Object.defineProperty(exports, "Crudora", { enumerable: true, get: function () { return crudora_1.Crudora; } });
|
|
6
|
+
var crudoraServer_1 = require("./core/crudoraServer");
|
|
7
|
+
Object.defineProperty(exports, "CrudoraServer", { enumerable: true, get: function () { return crudoraServer_1.CrudoraServer; } });
|
|
8
|
+
var repository_1 = require("./core/repository");
|
|
9
|
+
Object.defineProperty(exports, "Repository", { enumerable: true, get: function () { return repository_1.Repository; } });
|
|
10
|
+
var schemaGenerator_1 = require("./core/schemaGenerator");
|
|
11
|
+
Object.defineProperty(exports, "SchemaGenerator", { enumerable: true, get: function () { return schemaGenerator_1.SchemaGenerator; } });
|
|
12
|
+
var validation_1 = require("./utils/validation");
|
|
13
|
+
Object.defineProperty(exports, "ValidationGenerator", { enumerable: true, get: function () { return validation_1.ValidationGenerator; } });
|
|
14
|
+
var model_1 = require("./core/model"); // New export
|
|
15
|
+
Object.defineProperty(exports, "Model", { enumerable: true, get: function () { return model_1.Model; } });
|
|
16
|
+
// Keep decorator exports for backward compatibility
|
|
17
|
+
var model_2 = require("./decorators/model");
|
|
18
|
+
Object.defineProperty(exports, "ModelDecorator", { enumerable: true, get: function () { return model_2.Model; } });
|
|
19
|
+
Object.defineProperty(exports, "Field", { enumerable: true, get: function () { return model_2.Field; } });
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,0CAAyC;AAAhC,kGAAA,OAAO,OAAA;AAChB,sDAAqD;AAA5C,8GAAA,aAAa,OAAA;AACtB,gDAA+C;AAAtC,wGAAA,UAAU,OAAA;AACnB,0DAAyD;AAAhD,kHAAA,eAAe,OAAA;AACxB,iDAAyD;AAAhD,iHAAA,mBAAmB,OAAA;AAC5B,sCAAqC,CAAC,aAAa;AAA1C,8FAAA,KAAK,OAAA;AAGd,oDAAoD;AACpD,4CAAoE;AAA3D,uGAAA,KAAK,OAAkB;AAAE,8FAAA,KAAK,OAAA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
function copyDir(src, dest) {
|
|
5
|
+
if (!fs.existsSync(dest)) {
|
|
6
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
10
|
+
|
|
11
|
+
for (const entry of entries) {
|
|
12
|
+
const srcPath = path.join(src, entry.name);
|
|
13
|
+
const destPath = path.join(dest, entry.name);
|
|
14
|
+
|
|
15
|
+
if (entry.isDirectory()) {
|
|
16
|
+
copyDir(srcPath, destPath);
|
|
17
|
+
} else {
|
|
18
|
+
fs.copyFileSync(srcPath, destPath);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Copy templates
|
|
24
|
+
copyDir('templates', 'dist/templates');
|
|
25
|
+
copyDir('scripts', 'dist/scripts');
|
|
26
|
+
|
|
27
|
+
console.log('✅ Assets copied to dist/');
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const templatePath = path.join(__dirname, '..', 'templates', 'schema.prisma');
|
|
5
|
+
const targetPath = path.join(process.cwd(), 'prisma', 'schema.prisma');
|
|
6
|
+
|
|
7
|
+
// Only copy if prisma directory doesn't exist
|
|
8
|
+
if (!fs.existsSync(path.dirname(targetPath))) {
|
|
9
|
+
console.log('📋 Creating default Prisma schema...');
|
|
10
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
11
|
+
fs.copyFileSync(templatePath, targetPath);
|
|
12
|
+
console.log('✅ Default schema.prisma created in prisma/schema.prisma');
|
|
13
|
+
console.log('🔧 Don\'t forget to set your DATABASE_URL in .env file');
|
|
14
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// This is your Prisma schema file,
|
|
2
|
+
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
|
3
|
+
|
|
4
|
+
generator client {
|
|
5
|
+
provider = "prisma-client-js"
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
datasource db {
|
|
9
|
+
provider = "sqlite"
|
|
10
|
+
url = env("DATABASE_URL")
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Add your models here
|
|
14
|
+
// Example:
|
|
15
|
+
// model User {
|
|
16
|
+
// id String @id @default(uuid())
|
|
17
|
+
// name String
|
|
18
|
+
// email String @unique
|
|
19
|
+
// password String
|
|
20
|
+
// createdAt DateTime @default(now())
|
|
21
|
+
// updatedAt DateTime @updatedAt
|
|
22
|
+
// }
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ModelOptions {
|
|
2
|
+
tableName?: string;
|
|
3
|
+
timestamps?: boolean;
|
|
4
|
+
}
|
|
5
|
+
export interface FieldOptions {
|
|
6
|
+
type: 'string' | 'number' | 'boolean' | 'date' | 'uuid';
|
|
7
|
+
required?: boolean;
|
|
8
|
+
unique?: boolean;
|
|
9
|
+
default?: any;
|
|
10
|
+
length?: number;
|
|
11
|
+
primary?: boolean;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=model.type.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.type.d.ts","sourceRoot":"","sources":["../../src/types/model.type.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;IACxD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.type.js","sourceRoot":"","sources":["../../src/types/model.type.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare class ValidationGenerator {
|
|
3
|
+
static generateZodSchema<T>(modelClass: new () => T): z.ZodType<Partial<T>>;
|
|
4
|
+
static generateStrictZodSchema<T>(modelClass: new () => T): z.ZodType<T>;
|
|
5
|
+
private static createZodField;
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,qBAAa,mBAAmB;IAC9B,MAAM,CAAC,iBAAiB,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAgB3E,MAAM,CAAC,uBAAuB,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAoBxE,OAAO,CAAC,MAAM,CAAC,cAAc;CAiC9B"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ValidationGenerator = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const model_1 = require("../decorators/model");
|
|
6
|
+
class ValidationGenerator {
|
|
7
|
+
static generateZodSchema(modelClass) {
|
|
8
|
+
const fieldMetadata = (0, model_1.getFieldMetadata)(modelClass);
|
|
9
|
+
const schemaFields = {};
|
|
10
|
+
// If no field metadata, return empty partial schema
|
|
11
|
+
if (Object.keys(fieldMetadata).length === 0) {
|
|
12
|
+
return zod_1.z.object({}).partial();
|
|
13
|
+
}
|
|
14
|
+
for (const [fieldName, options] of Object.entries(fieldMetadata)) {
|
|
15
|
+
schemaFields[fieldName] = this.createZodField(options);
|
|
16
|
+
}
|
|
17
|
+
return zod_1.z.object(schemaFields).partial();
|
|
18
|
+
}
|
|
19
|
+
static generateStrictZodSchema(modelClass) {
|
|
20
|
+
const fieldMetadata = (0, model_1.getFieldMetadata)(modelClass);
|
|
21
|
+
const schemaFields = {};
|
|
22
|
+
// If no field metadata, return empty schema
|
|
23
|
+
if (Object.keys(fieldMetadata).length === 0) {
|
|
24
|
+
return zod_1.z.object({});
|
|
25
|
+
}
|
|
26
|
+
for (const [fieldName, options] of Object.entries(fieldMetadata)) {
|
|
27
|
+
// For strict schema, skip primary key fields that are auto-generated
|
|
28
|
+
if (options.primary && options.type === 'uuid') {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
schemaFields[fieldName] = this.createZodField(options);
|
|
32
|
+
}
|
|
33
|
+
return zod_1.z.object(schemaFields);
|
|
34
|
+
}
|
|
35
|
+
static createZodField(options) {
|
|
36
|
+
let field;
|
|
37
|
+
switch (options.type) {
|
|
38
|
+
case 'string':
|
|
39
|
+
field = zod_1.z.string();
|
|
40
|
+
if (options.length) {
|
|
41
|
+
field = field.max(options.length);
|
|
42
|
+
}
|
|
43
|
+
break;
|
|
44
|
+
case 'number':
|
|
45
|
+
field = zod_1.z.number();
|
|
46
|
+
break;
|
|
47
|
+
case 'boolean':
|
|
48
|
+
field = zod_1.z.boolean();
|
|
49
|
+
break;
|
|
50
|
+
case 'date':
|
|
51
|
+
field = zod_1.z.date();
|
|
52
|
+
break;
|
|
53
|
+
case 'uuid':
|
|
54
|
+
field = zod_1.z.string().uuid();
|
|
55
|
+
break;
|
|
56
|
+
default:
|
|
57
|
+
field = zod_1.z.string();
|
|
58
|
+
}
|
|
59
|
+
// If required is explicitly false, make field optional
|
|
60
|
+
if (options.required === false) {
|
|
61
|
+
field = field.optional();
|
|
62
|
+
}
|
|
63
|
+
return field;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.ValidationGenerator = ValidationGenerator;
|
|
67
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":";;;AAAA,6BAAwB;AACxB,+CAAuD;AAGvD,MAAa,mBAAmB;IAC9B,MAAM,CAAC,iBAAiB,CAAI,UAAuB;QACjD,MAAM,aAAa,GAAG,IAAA,wBAAgB,EAAC,UAAU,CAAC,CAAC;QACnD,MAAM,YAAY,GAAiC,EAAE,CAAC;QAEtD,oDAAoD;QACpD,IAAI,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5C,OAAO,OAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,OAAO,EAA2B,CAAC;QACzD,CAAC;QAED,KAAK,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAA6B,EAAE,CAAC;YAC7F,YAAY,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QACzD,CAAC;QAED,OAAO,OAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,EAA2B,CAAC;IACnE,CAAC;IAED,MAAM,CAAC,uBAAuB,CAAI,UAAuB;QACvD,MAAM,aAAa,GAAG,IAAA,wBAAgB,EAAC,UAAU,CAAC,CAAC;QACnD,MAAM,YAAY,GAAiC,EAAE,CAAC;QAEtD,4CAA4C;QAC5C,IAAI,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5C,OAAO,OAAC,CAAC,MAAM,CAAC,EAAE,CAAiB,CAAC;QACtC,CAAC;QAED,KAAK,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAA6B,EAAE,CAAC;YAC7F,qEAAqE;YACrE,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;gBAC/C,SAAS;YACX,CAAC;YACD,YAAY,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QACzD,CAAC;QAED,OAAO,OAAC,CAAC,MAAM,CAAC,YAAY,CAAiB,CAAC;IAChD,CAAC;IAEO,MAAM,CAAC,cAAc,CAAC,OAAqB;QACjD,IAAI,KAAmB,CAAC;QAExB,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;YACrB,KAAK,QAAQ;gBACX,KAAK,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC;gBACnB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;oBACnB,KAAK,GAAI,KAAqB,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;gBACrD,CAAC;gBACD,MAAM;YACR,KAAK,QAAQ;gBACX,KAAK,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC;gBACnB,MAAM;YACR,KAAK,SAAS;gBACZ,KAAK,GAAG,OAAC,CAAC,OAAO,EAAE,CAAC;gBACpB,MAAM;YACR,KAAK,MAAM;gBACT,KAAK,GAAG,OAAC,CAAC,IAAI,EAAE,CAAC;gBACjB,MAAM;YACR,KAAK,MAAM;gBACT,KAAK,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC;gBAC1B,MAAM;YACR;gBACE,KAAK,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC;QACvB,CAAC;QAED,uDAAuD;QACvD,IAAI,OAAO,CAAC,QAAQ,KAAK,KAAK,EAAE,CAAC;YAC/B,KAAK,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;QAC3B,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;CACF;AAtED,kDAsEC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "crudora",
|
|
3
|
+
"version": "0.1.0-alpha.1",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist",
|
|
8
|
+
"README.md",
|
|
9
|
+
"LICENSE",
|
|
10
|
+
"templates",
|
|
11
|
+
"scripts"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc && node scripts/copy-assets.js",
|
|
15
|
+
"copy-assets": "node scripts/copy-assets.js",
|
|
16
|
+
"build:watch": "tsc --watch",
|
|
17
|
+
"clean": "rimraf dist",
|
|
18
|
+
"prebuild": "npm run clean",
|
|
19
|
+
"prepublishOnly": "npm run build",
|
|
20
|
+
"dev": "ts-node src/index.ts",
|
|
21
|
+
"test": "jest",
|
|
22
|
+
"test:watch": "jest --watch",
|
|
23
|
+
"test:coverage": "jest --coverage",
|
|
24
|
+
"lint": "eslint src/**/*.ts",
|
|
25
|
+
"format": "prettier --write src/**/*.ts",
|
|
26
|
+
"postinstall": "node scripts/postinstall.js"
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"crud",
|
|
30
|
+
"api",
|
|
31
|
+
"typescript",
|
|
32
|
+
"prisma",
|
|
33
|
+
"express",
|
|
34
|
+
"framework",
|
|
35
|
+
"backend",
|
|
36
|
+
"rest-api",
|
|
37
|
+
"orm",
|
|
38
|
+
"validation",
|
|
39
|
+
"zod",
|
|
40
|
+
"decorators",
|
|
41
|
+
"auto-generation"
|
|
42
|
+
],
|
|
43
|
+
"repository": {
|
|
44
|
+
"type": "git",
|
|
45
|
+
"url": "https://github.com/suryamsj/crudora.git"
|
|
46
|
+
},
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/suryamsj/crudora/issues"
|
|
49
|
+
},
|
|
50
|
+
"homepage": "https://github.com/suryamsj/crudora#readme",
|
|
51
|
+
"author": {
|
|
52
|
+
"name": "Muhammad Surya J",
|
|
53
|
+
"url": "https://suryamsj.my.id"
|
|
54
|
+
},
|
|
55
|
+
"license": "MIT",
|
|
56
|
+
"description": "TypeScript framework for automated CRUD API generation with Prisma and Express",
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@types/express": "^5.0.3",
|
|
59
|
+
"express": "^5.1.0",
|
|
60
|
+
"reflect-metadata": "^0.2.2",
|
|
61
|
+
"zod": "^4.0.5"
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@types/jest": "^30.0.0",
|
|
65
|
+
"@types/node": "^24.0.15",
|
|
66
|
+
"@types/supertest": "^6.0.3",
|
|
67
|
+
"@typescript-eslint/eslint-plugin": "^8.38.0",
|
|
68
|
+
"@typescript-eslint/parser": "^8.38.0",
|
|
69
|
+
"eslint": "^9.31.0",
|
|
70
|
+
"jest": "^30.0.4",
|
|
71
|
+
"prettier": "^3.6.2",
|
|
72
|
+
"rimraf": "^6.0.1",
|
|
73
|
+
"supertest": "^7.1.3",
|
|
74
|
+
"ts-jest": "^29.4.0",
|
|
75
|
+
"ts-node": "^10.9.2",
|
|
76
|
+
"typescript": "^5.8.3"
|
|
77
|
+
},
|
|
78
|
+
"peerDependencies": {
|
|
79
|
+
"@prisma/client": "^6.12.0",
|
|
80
|
+
"prisma": "^6.12.0"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
function copyDir(src, dest) {
|
|
5
|
+
if (!fs.existsSync(dest)) {
|
|
6
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
10
|
+
|
|
11
|
+
for (const entry of entries) {
|
|
12
|
+
const srcPath = path.join(src, entry.name);
|
|
13
|
+
const destPath = path.join(dest, entry.name);
|
|
14
|
+
|
|
15
|
+
if (entry.isDirectory()) {
|
|
16
|
+
copyDir(srcPath, destPath);
|
|
17
|
+
} else {
|
|
18
|
+
fs.copyFileSync(srcPath, destPath);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Copy templates
|
|
24
|
+
copyDir('templates', 'dist/templates');
|
|
25
|
+
copyDir('scripts', 'dist/scripts');
|
|
26
|
+
|
|
27
|
+
console.log('✅ Assets copied to dist/');
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const templatePath = path.join(__dirname, '..', 'templates', 'schema.prisma');
|
|
5
|
+
const targetPath = path.join(process.cwd(), 'prisma', 'schema.prisma');
|
|
6
|
+
|
|
7
|
+
// Only copy if prisma directory doesn't exist
|
|
8
|
+
if (!fs.existsSync(path.dirname(targetPath))) {
|
|
9
|
+
console.log('📋 Creating default Prisma schema...');
|
|
10
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
11
|
+
fs.copyFileSync(templatePath, targetPath);
|
|
12
|
+
console.log('✅ Default schema.prisma created in prisma/schema.prisma');
|
|
13
|
+
console.log('🔧 Don\'t forget to set your DATABASE_URL in .env file');
|
|
14
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// This is your Prisma schema file,
|
|
2
|
+
// learn more about it in the docs: https://pris.ly/d/prisma-schema
|
|
3
|
+
|
|
4
|
+
generator client {
|
|
5
|
+
provider = "prisma-client-js"
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
datasource db {
|
|
9
|
+
provider = "sqlite"
|
|
10
|
+
url = env("DATABASE_URL")
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Add your models here
|
|
14
|
+
// Example:
|
|
15
|
+
// model User {
|
|
16
|
+
// id String @id @default(uuid())
|
|
17
|
+
// name String
|
|
18
|
+
// email String @unique
|
|
19
|
+
// password String
|
|
20
|
+
// createdAt DateTime @default(now())
|
|
21
|
+
// updatedAt DateTime @updatedAt
|
|
22
|
+
// }
|