lapeh 2.3.6 → 2.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/index.js +39 -56
- package/dist/generated/prisma/browser.d.ts +80 -0
- package/dist/generated/prisma/browser.d.ts.map +1 -0
- package/dist/generated/prisma/browser.js +56 -0
- package/dist/generated/prisma/client.d.ts +97 -0
- package/dist/generated/prisma/client.d.ts.map +1 -0
- package/dist/generated/prisma/client.js +68 -0
- package/dist/generated/prisma/commonInputTypes.d.ts +486 -0
- package/dist/generated/prisma/commonInputTypes.d.ts.map +1 -0
- package/dist/generated/prisma/commonInputTypes.js +11 -0
- package/dist/generated/prisma/enums.d.ts +2 -0
- package/dist/generated/prisma/enums.d.ts.map +1 -0
- package/dist/generated/prisma/enums.js +11 -0
- package/dist/generated/prisma/internal/class.d.ts +281 -0
- package/dist/generated/prisma/internal/class.d.ts.map +1 -0
- package/dist/generated/prisma/internal/class.js +76 -0
- package/dist/generated/prisma/internal/prismaNamespace.d.ts +1734 -0
- package/dist/generated/prisma/internal/prismaNamespace.d.ts.map +1 -0
- package/dist/generated/prisma/internal/prismaNamespace.js +260 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts +200 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.d.ts.map +1 -0
- package/dist/generated/prisma/internal/prismaNamespaceBrowser.js +231 -0
- package/dist/generated/prisma/models/cache.d.ts +986 -0
- package/dist/generated/prisma/models/cache.d.ts.map +1 -0
- package/dist/generated/prisma/models/cache.js +2 -0
- package/dist/generated/prisma/models/cache_locks.d.ts +976 -0
- package/dist/generated/prisma/models/cache_locks.d.ts.map +1 -0
- package/dist/generated/prisma/models/cache_locks.js +2 -0
- package/dist/generated/prisma/models/failed_jobs.d.ts +1098 -0
- package/dist/generated/prisma/models/failed_jobs.d.ts.map +1 -0
- package/dist/generated/prisma/models/failed_jobs.js +2 -0
- package/dist/generated/prisma/models/job_batches.d.ts +1212 -0
- package/dist/generated/prisma/models/job_batches.d.ts.map +1 -0
- package/dist/generated/prisma/models/job_batches.js +2 -0
- package/dist/generated/prisma/models/jobs.d.ts +1112 -0
- package/dist/generated/prisma/models/jobs.d.ts.map +1 -0
- package/dist/generated/prisma/models/jobs.js +2 -0
- package/dist/generated/prisma/models/migrations.d.ts +979 -0
- package/dist/generated/prisma/models/migrations.d.ts.map +1 -0
- package/dist/generated/prisma/models/migrations.js +2 -0
- package/dist/generated/prisma/models/password_reset_tokens.d.ts +941 -0
- package/dist/generated/prisma/models/password_reset_tokens.d.ts.map +1 -0
- package/dist/generated/prisma/models/password_reset_tokens.js +2 -0
- package/dist/generated/prisma/models/permissions.d.ts +1333 -0
- package/dist/generated/prisma/models/permissions.d.ts.map +1 -0
- package/dist/generated/prisma/models/permissions.js +2 -0
- package/dist/generated/prisma/models/personal_access_tokens.d.ts +1178 -0
- package/dist/generated/prisma/models/personal_access_tokens.d.ts.map +1 -0
- package/dist/generated/prisma/models/personal_access_tokens.js +2 -0
- package/dist/generated/prisma/models/role_permissions.d.ts +1291 -0
- package/dist/generated/prisma/models/role_permissions.d.ts.map +1 -0
- package/dist/generated/prisma/models/role_permissions.js +2 -0
- package/dist/generated/prisma/models/roles.d.ts +1333 -0
- package/dist/generated/prisma/models/roles.d.ts.map +1 -0
- package/dist/generated/prisma/models/roles.js +2 -0
- package/dist/generated/prisma/models/sessions.d.ts +1073 -0
- package/dist/generated/prisma/models/sessions.d.ts.map +1 -0
- package/dist/generated/prisma/models/sessions.js +2 -0
- package/dist/generated/prisma/models/user_permissions.d.ts +1291 -0
- package/dist/generated/prisma/models/user_permissions.d.ts.map +1 -0
- package/dist/generated/prisma/models/user_permissions.js +2 -0
- package/dist/generated/prisma/models/user_roles.d.ts +1291 -0
- package/dist/generated/prisma/models/user_roles.d.ts.map +1 -0
- package/dist/generated/prisma/models/user_roles.js +2 -0
- package/dist/generated/prisma/models/users.d.ts +1513 -0
- package/dist/generated/prisma/models/users.d.ts.map +1 -0
- package/dist/generated/prisma/models/users.js +2 -0
- package/dist/generated/prisma/models.d.ts +17 -0
- package/dist/generated/prisma/models.d.ts.map +1 -0
- package/dist/generated/prisma/models.js +2 -0
- package/dist/lib/bootstrap.d.ts +2 -0
- package/dist/lib/bootstrap.d.ts.map +1 -0
- package/dist/lib/bootstrap.js +133 -0
- package/dist/lib/core/database.d.ts +3 -0
- package/dist/lib/core/database.d.ts.map +1 -0
- package/dist/lib/core/database.js +34 -0
- package/dist/lib/core/realtime.d.ts +3 -0
- package/dist/lib/core/realtime.d.ts.map +1 -0
- package/dist/lib/core/realtime.js +36 -0
- package/dist/lib/core/redis.d.ts +8 -0
- package/dist/lib/core/redis.d.ts.map +1 -0
- package/dist/lib/core/redis.js +123 -0
- package/dist/lib/core/serializer.d.ts +43 -0
- package/dist/lib/core/serializer.d.ts.map +1 -0
- package/dist/lib/core/serializer.js +66 -0
- package/dist/lib/core/server.d.ts +2 -0
- package/dist/lib/core/server.d.ts.map +1 -0
- package/dist/lib/core/server.js +60 -0
- package/dist/lib/middleware/auth.d.ts +4 -0
- package/dist/lib/middleware/auth.d.ts.map +1 -0
- package/dist/lib/middleware/auth.js +55 -0
- package/dist/lib/middleware/error.d.ts +3 -0
- package/dist/lib/middleware/error.d.ts.map +1 -0
- package/dist/lib/middleware/error.js +60 -0
- package/dist/lib/middleware/multipart.d.ts +4 -0
- package/dist/lib/middleware/multipart.d.ts.map +1 -0
- package/dist/lib/middleware/multipart.js +17 -0
- package/dist/lib/middleware/rateLimit.d.ts +2 -0
- package/dist/lib/middleware/rateLimit.d.ts.map +1 -0
- package/dist/lib/middleware/rateLimit.js +19 -0
- package/dist/lib/middleware/requestLogger.d.ts +3 -0
- package/dist/lib/middleware/requestLogger.d.ts.map +1 -0
- package/dist/lib/middleware/requestLogger.js +22 -0
- package/dist/lib/middleware/visitor.d.ts +3 -0
- package/dist/lib/middleware/visitor.d.ts.map +1 -0
- package/dist/lib/middleware/visitor.js +144 -0
- package/dist/lib/utils/logger.d.ts +11 -0
- package/dist/lib/utils/logger.d.ts.map +1 -0
- package/dist/lib/utils/logger.js +81 -0
- package/dist/lib/utils/pagination.d.ts +19 -0
- package/dist/lib/utils/pagination.d.ts.map +1 -0
- package/dist/lib/utils/pagination.js +34 -0
- package/dist/lib/utils/response.d.ts +11 -0
- package/dist/lib/utils/response.d.ts.map +1 -0
- package/dist/lib/utils/response.js +57 -0
- package/dist/lib/utils/validator.d.ts +38 -0
- package/dist/lib/utils/validator.d.ts.map +1 -0
- package/dist/lib/utils/validator.js +369 -0
- package/dist/prisma/seed.d.ts +2 -0
- package/dist/prisma/seed.d.ts.map +1 -0
- package/dist/prisma/seed.js +381 -0
- package/dist/src/controllers/authController.d.ts +11 -0
- package/dist/src/controllers/authController.d.ts.map +1 -0
- package/dist/src/controllers/authController.js +414 -0
- package/dist/src/controllers/petController.d.ts +7 -0
- package/dist/src/controllers/petController.d.ts.map +1 -0
- package/dist/src/controllers/petController.js +163 -0
- package/dist/src/controllers/rbacController.d.ts +16 -0
- package/dist/src/controllers/rbacController.d.ts.map +1 -0
- package/dist/src/controllers/rbacController.js +437 -0
- package/dist/src/core/database.d.ts +3 -0
- package/dist/src/core/database.d.ts.map +1 -0
- package/dist/src/core/database.js +34 -0
- package/dist/src/core/realtime.d.ts +3 -0
- package/dist/src/core/realtime.d.ts.map +1 -0
- package/dist/src/core/realtime.js +36 -0
- package/dist/src/core/redis.d.ts +8 -0
- package/dist/src/core/redis.d.ts.map +1 -0
- package/dist/src/core/redis.js +123 -0
- package/dist/src/core/serializer.d.ts +43 -0
- package/dist/src/core/serializer.d.ts.map +1 -0
- package/dist/src/core/serializer.js +66 -0
- package/dist/src/core/server.d.ts +2 -0
- package/dist/src/core/server.d.ts.map +1 -0
- package/dist/src/core/server.js +60 -0
- package/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +98 -0
- package/dist/src/middleware/auth.d.ts +4 -0
- package/dist/src/middleware/auth.d.ts.map +1 -0
- package/dist/src/middleware/auth.js +48 -0
- package/dist/src/middleware/error.d.ts +3 -0
- package/dist/src/middleware/error.d.ts.map +1 -0
- package/dist/src/middleware/error.js +60 -0
- package/dist/src/middleware/multipart.d.ts +4 -0
- package/dist/src/middleware/multipart.d.ts.map +1 -0
- package/dist/src/middleware/multipart.js +17 -0
- package/dist/src/middleware/rateLimit.d.ts +2 -0
- package/dist/src/middleware/rateLimit.d.ts.map +1 -0
- package/dist/src/middleware/rateLimit.js +19 -0
- package/dist/src/middleware/requestLogger.d.ts +3 -0
- package/dist/src/middleware/requestLogger.d.ts.map +1 -0
- package/dist/src/middleware/requestLogger.js +22 -0
- package/dist/src/middleware/visitor.d.ts +3 -0
- package/dist/src/middleware/visitor.d.ts.map +1 -0
- package/dist/src/middleware/visitor.js +144 -0
- package/dist/src/prisma.d.ts +3 -0
- package/dist/src/prisma.d.ts.map +1 -0
- package/dist/src/prisma.js +34 -0
- package/dist/src/realtime.d.ts +3 -0
- package/dist/src/realtime.d.ts.map +1 -0
- package/dist/src/realtime.js +36 -0
- package/dist/src/redis.d.ts +8 -0
- package/dist/src/redis.d.ts.map +1 -0
- package/dist/src/redis.js +122 -0
- package/dist/src/routes/auth.d.ts +2 -0
- package/dist/src/routes/auth.d.ts.map +1 -0
- package/dist/src/routes/auth.js +45 -0
- package/dist/src/routes/index.d.ts +2 -0
- package/dist/src/routes/index.d.ts.map +1 -0
- package/dist/src/routes/index.js +14 -0
- package/dist/src/routes/pets.d.ts +3 -0
- package/dist/src/routes/pets.d.ts.map +1 -0
- package/dist/src/routes/pets.js +45 -0
- package/dist/src/routes/rbac.d.ts +2 -0
- package/dist/src/routes/rbac.d.ts.map +1 -0
- package/dist/src/routes/rbac.js +23 -0
- package/dist/src/schema/auth-schema.d.ts +76 -0
- package/dist/src/schema/auth-schema.d.ts.map +1 -0
- package/dist/src/schema/auth-schema.js +63 -0
- package/dist/src/schema/pet-schema.d.ts +28 -0
- package/dist/src/schema/pet-schema.d.ts.map +1 -0
- package/dist/src/schema/pet-schema.js +14 -0
- package/dist/src/server.d.ts +2 -0
- package/dist/src/server.d.ts.map +1 -0
- package/dist/src/server.js +31 -0
- package/dist/src/utils/logger.d.ts +11 -0
- package/dist/src/utils/logger.d.ts.map +1 -0
- package/dist/src/utils/logger.js +81 -0
- package/dist/src/utils/pagination.d.ts +19 -0
- package/dist/src/utils/pagination.d.ts.map +1 -0
- package/dist/src/utils/pagination.js +34 -0
- package/dist/src/utils/response.d.ts +11 -0
- package/dist/src/utils/response.d.ts.map +1 -0
- package/dist/src/utils/response.js +57 -0
- package/dist/src/utils/validator.d.ts +38 -0
- package/dist/src/utils/validator.d.ts.map +1 -0
- package/dist/src/utils/validator.js +369 -0
- package/lib/bootstrap.ts +6 -0
- package/package.json +26 -14
- package/.env.example +0 -19
- package/doc/ARCHITECTURE_GUIDE.md +0 -73
- package/doc/CHANGELOG.md +0 -77
- package/doc/CHEATSHEET.md +0 -94
- package/doc/CLI.md +0 -139
- package/doc/CONTRIBUTING.md +0 -105
- package/doc/DEPLOYMENT.md +0 -122
- package/doc/FAQ.md +0 -81
- package/doc/FEATURES.md +0 -165
- package/doc/GETTING_STARTED.md +0 -108
- package/doc/INTRODUCTION.md +0 -60
- package/doc/PACKAGES.md +0 -66
- package/doc/PERFORMANCE.md +0 -91
- package/doc/ROADMAP.md +0 -93
- package/doc/SECURITY.md +0 -93
- package/doc/STRUCTURE.md +0 -90
- package/doc/TUTORIAL.md +0 -192
- package/docker-compose.yml +0 -24
- package/eslint.config.mjs +0 -26
- package/framework.md +0 -168
- package/nodemon.json +0 -6
- package/prisma.config.ts +0 -15
- package/src/controllers/authController.ts +0 -469
- package/src/controllers/petController.ts +0 -194
- package/src/controllers/rbacController.ts +0 -478
- package/src/models/core.prisma +0 -163
- package/src/models/pets.prisma +0 -9
- package/src/routes/auth.ts +0 -74
- package/src/routes/index.ts +0 -10
- package/src/routes/pets.ts +0 -13
- package/src/routes/rbac.ts +0 -42
- package/storage/logs/.gitkeep +0 -0
- package/tsconfig.json +0 -30
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sendSuccess = sendSuccess;
|
|
4
|
+
exports.sendFastSuccess = sendFastSuccess;
|
|
5
|
+
exports.sendError = sendError;
|
|
6
|
+
const logger_1 = require("./logger");
|
|
7
|
+
function toJsonSafe(value) {
|
|
8
|
+
if (value instanceof Date) {
|
|
9
|
+
return value.toISOString();
|
|
10
|
+
}
|
|
11
|
+
if (typeof value === "bigint") {
|
|
12
|
+
return value.toString();
|
|
13
|
+
}
|
|
14
|
+
if (Array.isArray(value)) {
|
|
15
|
+
return value.map((item) => toJsonSafe(item));
|
|
16
|
+
}
|
|
17
|
+
if (value && typeof value === "object") {
|
|
18
|
+
const result = {};
|
|
19
|
+
for (const [key, val] of Object.entries(value)) {
|
|
20
|
+
result[key] = toJsonSafe(val);
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
function sendSuccess(res, statusCode, message, data) {
|
|
27
|
+
const body = { status: "success", message, data };
|
|
28
|
+
return res.status(statusCode).json(toJsonSafe(body));
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Mengirim response sukses dengan performa tinggi menggunakan Schema Serialization (Fastify-style).
|
|
32
|
+
* Melewati proses JSON.stringify standar yang lambat.
|
|
33
|
+
*
|
|
34
|
+
* @param serializer Fungsi serializer yang sudah dicompile dari src/core/serializer
|
|
35
|
+
*/
|
|
36
|
+
function sendFastSuccess(res, statusCode, serializer, data) {
|
|
37
|
+
// Set header manual karena kita mengirim raw string
|
|
38
|
+
res.setHeader("Content-Type", "application/json");
|
|
39
|
+
res.status(statusCode);
|
|
40
|
+
// Serializer mengembalikan string JSON
|
|
41
|
+
const jsonString = serializer(data);
|
|
42
|
+
return res.send(jsonString);
|
|
43
|
+
}
|
|
44
|
+
function sendError(res, statusCode, message, errors) {
|
|
45
|
+
// Log the error
|
|
46
|
+
if (statusCode >= 500) {
|
|
47
|
+
logger_1.Log.error(message, { statusCode, errors });
|
|
48
|
+
}
|
|
49
|
+
else if (statusCode >= 400) {
|
|
50
|
+
logger_1.Log.warn(message, { statusCode, errors });
|
|
51
|
+
}
|
|
52
|
+
const body = { status: "error", message };
|
|
53
|
+
if (errors !== undefined) {
|
|
54
|
+
body.errors = errors;
|
|
55
|
+
}
|
|
56
|
+
return res.status(statusCode).json(toJsonSafe(body));
|
|
57
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ZodSchema } from "zod";
|
|
2
|
+
export declare class Validator {
|
|
3
|
+
private data;
|
|
4
|
+
private schema;
|
|
5
|
+
private customMessages;
|
|
6
|
+
private result;
|
|
7
|
+
private errorResult;
|
|
8
|
+
private hasRun;
|
|
9
|
+
constructor(data: any, schema: ZodSchema<any> | Record<string, any>, messages?: Record<string, string>);
|
|
10
|
+
/**
|
|
11
|
+
* Create a new Validator instance
|
|
12
|
+
* @param data The input data to validate
|
|
13
|
+
* @param schema Zod schema or object of Zod schemas / Laravel-style rules
|
|
14
|
+
* @param messages Optional custom error messages (Laravel style: 'field.rule' => 'message')
|
|
15
|
+
*/
|
|
16
|
+
static make(data: any, schema: ZodSchema<any> | Record<string, any>, messages?: Record<string, string>): Validator;
|
|
17
|
+
private static parseLaravelRule;
|
|
18
|
+
/**
|
|
19
|
+
* Check if validation fails
|
|
20
|
+
*/
|
|
21
|
+
fails(): Promise<boolean>;
|
|
22
|
+
/**
|
|
23
|
+
* Check if validation passes
|
|
24
|
+
*/
|
|
25
|
+
passes(): Promise<boolean>;
|
|
26
|
+
/**
|
|
27
|
+
* Get the validation errors
|
|
28
|
+
*/
|
|
29
|
+
errors(): any;
|
|
30
|
+
/**
|
|
31
|
+
* Get the validated data
|
|
32
|
+
*/
|
|
33
|
+
validated(): Promise<any>;
|
|
34
|
+
private run;
|
|
35
|
+
private formatErrors;
|
|
36
|
+
private getCustomMessage;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../../lib/utils/validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAK,SAAS,EAAsB,MAAM,KAAK,CAAC;AAGvD,qBAAa,SAAS;IACpB,OAAO,CAAC,IAAI,CAAM;IAClB,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,MAAM,CAAkB;gBAG9B,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5C,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM;IAavC;;;;;OAKG;IACH,MAAM,CAAC,IAAI,CACT,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,SAAS,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5C,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM;IAiDvC,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAyN/B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC;IAK/B;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC;IAIhC;;OAEG;IACH,MAAM;IAKN;;OAEG;IACG,SAAS;YAUD,GAAG;IAgBjB,OAAO,CAAC,YAAY;IAuBpB,OAAO,CAAC,gBAAgB;CAoDzB"}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Validator = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const database_1 = require("../core/database");
|
|
6
|
+
class Validator {
|
|
7
|
+
constructor(data, schema, messages = {}) {
|
|
8
|
+
this.result = null;
|
|
9
|
+
this.errorResult = null;
|
|
10
|
+
this.hasRun = false;
|
|
11
|
+
this.data = data;
|
|
12
|
+
this.customMessages = messages;
|
|
13
|
+
// If it's a raw object, wrap it in z.object()
|
|
14
|
+
if (schema instanceof zod_1.ZodSchema) {
|
|
15
|
+
this.schema = schema;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
this.schema = zod_1.z.object(schema);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create a new Validator instance
|
|
23
|
+
* @param data The input data to validate
|
|
24
|
+
* @param schema Zod schema or object of Zod schemas / Laravel-style rules
|
|
25
|
+
* @param messages Optional custom error messages (Laravel style: 'field.rule' => 'message')
|
|
26
|
+
*/
|
|
27
|
+
static make(data, schema, messages = {}) {
|
|
28
|
+
if (schema instanceof zod_1.ZodSchema) {
|
|
29
|
+
return new Validator(data, schema, messages);
|
|
30
|
+
}
|
|
31
|
+
const parsedSchema = {};
|
|
32
|
+
const sameRules = [];
|
|
33
|
+
for (const [key, rule] of Object.entries(schema)) {
|
|
34
|
+
if (rule instanceof zod_1.ZodSchema) {
|
|
35
|
+
parsedSchema[key] = rule;
|
|
36
|
+
}
|
|
37
|
+
else if (typeof rule === "string" || Array.isArray(rule)) {
|
|
38
|
+
const ruleStr = Array.isArray(rule) ? rule.join("|") : rule;
|
|
39
|
+
// Check for 'same:target'
|
|
40
|
+
const parts = ruleStr.split("|");
|
|
41
|
+
for (const part of parts) {
|
|
42
|
+
const [name, args] = part.split(":");
|
|
43
|
+
if (name === "same" && args) {
|
|
44
|
+
sameRules.push({ field: key, target: args });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
parsedSchema[key] = Validator.parseLaravelRule(rule);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
parsedSchema[key] = rule;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
let objectSchema = zod_1.z.object(parsedSchema);
|
|
54
|
+
if (sameRules.length > 0) {
|
|
55
|
+
objectSchema = objectSchema.superRefine((val, ctx) => {
|
|
56
|
+
sameRules.forEach(({ field, target }) => {
|
|
57
|
+
if (val[field] !== val[target]) {
|
|
58
|
+
ctx.addIssue({
|
|
59
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
60
|
+
message: `The ${field} and ${target} must match.`,
|
|
61
|
+
path: [field],
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return new Validator(data, objectSchema, messages);
|
|
68
|
+
}
|
|
69
|
+
static parseLaravelRule(rule) {
|
|
70
|
+
const rules = Array.isArray(rule)
|
|
71
|
+
? rule
|
|
72
|
+
: rule.split("|").map((r) => r.trim());
|
|
73
|
+
let schema = zod_1.z.string(); // Default base
|
|
74
|
+
let isOptional = false;
|
|
75
|
+
let isNullable = false;
|
|
76
|
+
// 1. Determine Base Type
|
|
77
|
+
const isFile = rules.includes("file") ||
|
|
78
|
+
rules.includes("image") ||
|
|
79
|
+
rules.includes("mimes");
|
|
80
|
+
if (rules.includes("numeric") || rules.includes("integer")) {
|
|
81
|
+
schema = zod_1.z.coerce.number();
|
|
82
|
+
if (rules.includes("integer"))
|
|
83
|
+
schema = schema.int();
|
|
84
|
+
}
|
|
85
|
+
else if (rules.includes("boolean")) {
|
|
86
|
+
schema = zod_1.z.boolean();
|
|
87
|
+
}
|
|
88
|
+
else if (rules.includes("array")) {
|
|
89
|
+
schema = zod_1.z.array(zod_1.z.any());
|
|
90
|
+
}
|
|
91
|
+
else if (rules.includes("date")) {
|
|
92
|
+
schema = zod_1.z.coerce.date();
|
|
93
|
+
}
|
|
94
|
+
else if (isFile) {
|
|
95
|
+
schema = zod_1.z.any().refine((val) => {
|
|
96
|
+
// Check if it looks like a Multer file object
|
|
97
|
+
return (typeof val === "object" &&
|
|
98
|
+
val !== null &&
|
|
99
|
+
"fieldname" in val &&
|
|
100
|
+
"originalname" in val &&
|
|
101
|
+
"encoding" in val &&
|
|
102
|
+
"mimetype" in val &&
|
|
103
|
+
"size" in val);
|
|
104
|
+
}, { message: "The field must be a file." });
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
schema = zod_1.z.string();
|
|
108
|
+
}
|
|
109
|
+
// 2. Constraints & Modifiers
|
|
110
|
+
for (const r of rules) {
|
|
111
|
+
const [name, args] = r.split(":");
|
|
112
|
+
const params = args ? args.split(",") : [];
|
|
113
|
+
switch (name) {
|
|
114
|
+
case "email":
|
|
115
|
+
if (schema instanceof zod_1.z.ZodString)
|
|
116
|
+
schema = schema.email();
|
|
117
|
+
break;
|
|
118
|
+
case "url":
|
|
119
|
+
if (schema instanceof zod_1.z.ZodString)
|
|
120
|
+
schema = schema.url();
|
|
121
|
+
break;
|
|
122
|
+
case "uuid":
|
|
123
|
+
if (schema instanceof zod_1.z.ZodString)
|
|
124
|
+
schema = schema.uuid();
|
|
125
|
+
break;
|
|
126
|
+
case "min":
|
|
127
|
+
if (!isNaN(Number(params[0])))
|
|
128
|
+
schema = schema.min(Number(params[0]));
|
|
129
|
+
break;
|
|
130
|
+
case "max":
|
|
131
|
+
if (!isNaN(Number(params[0]))) {
|
|
132
|
+
if (isFile) {
|
|
133
|
+
schema = schema.refine((val) => {
|
|
134
|
+
if (!val)
|
|
135
|
+
return true; // Let nullable/optional handle nulls
|
|
136
|
+
// But wait, if we are in strict mode (not nullable), we should fail?
|
|
137
|
+
// No, max() usually doesn't enforce existence. required() does.
|
|
138
|
+
// But here val is the file object.
|
|
139
|
+
// If val is missing, and it's required, Base Type would have failed (if I revert Base Type).
|
|
140
|
+
// So here val is likely a file object.
|
|
141
|
+
return val.size <= Number(params[0]) * 1024;
|
|
142
|
+
}, {
|
|
143
|
+
message: `The file may not be greater than ${params[0]} kilobytes.`,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
schema = schema.max(Number(params[0]));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
case "image":
|
|
152
|
+
schema = schema.refine((val) => {
|
|
153
|
+
if (!val || typeof val !== "object" || !val.mimetype)
|
|
154
|
+
return false;
|
|
155
|
+
return val.mimetype.startsWith("image/");
|
|
156
|
+
}, { message: "The field must be an image." });
|
|
157
|
+
break;
|
|
158
|
+
case "in":
|
|
159
|
+
if (params.length > 0) {
|
|
160
|
+
// For strings, we can use regex or refine
|
|
161
|
+
schema = schema.refine((val) => params.includes(String(val)), {
|
|
162
|
+
message: `The selected ${name} is invalid.`,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
break;
|
|
166
|
+
case "not_in":
|
|
167
|
+
if (params.length > 0) {
|
|
168
|
+
schema = schema.refine((val) => !params.includes(String(val)), {
|
|
169
|
+
message: `The selected ${name} is invalid.`,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
break;
|
|
173
|
+
case "alpha":
|
|
174
|
+
if (schema instanceof zod_1.z.ZodString)
|
|
175
|
+
schema = schema.regex(/^[a-zA-Z]+$/, "The field must only contain letters.");
|
|
176
|
+
break;
|
|
177
|
+
case "alpha_dash":
|
|
178
|
+
if (schema instanceof zod_1.z.ZodString)
|
|
179
|
+
schema = schema.regex(/^[a-zA-Z0-9_-]+$/, "The field must only contain letters, numbers, dashes, and underscores.");
|
|
180
|
+
break;
|
|
181
|
+
case "alpha_num":
|
|
182
|
+
if (schema instanceof zod_1.z.ZodString)
|
|
183
|
+
schema = schema.regex(/^[a-zA-Z0-9]+$/, "The field must only contain letters and numbers.");
|
|
184
|
+
break;
|
|
185
|
+
case "mimes":
|
|
186
|
+
// For file validation
|
|
187
|
+
schema = schema.refine((val) => {
|
|
188
|
+
if (!val || typeof val !== "object" || !val.mimetype)
|
|
189
|
+
return false;
|
|
190
|
+
// params are extensions (jpg, png). We need to map to mimetypes or check loosely.
|
|
191
|
+
// Simplification: check if mimetype includes one of the params
|
|
192
|
+
// Or better: map extensions to mimetypes? Too big.
|
|
193
|
+
// Let's assume params are extensions, and we check if mimetype matches.
|
|
194
|
+
// Actually, typically mimes:jpg,png means we check extension or mime.
|
|
195
|
+
// For now, let's just check if mimetype contains the string (imperfect but simple)
|
|
196
|
+
// OR check originalname extension.
|
|
197
|
+
const extension = val.originalname
|
|
198
|
+
?.split(".")
|
|
199
|
+
.pop()
|
|
200
|
+
?.toLowerCase();
|
|
201
|
+
return params.includes(extension);
|
|
202
|
+
}, {
|
|
203
|
+
message: `The file must be a file of type: ${params.join(", ")}.`,
|
|
204
|
+
});
|
|
205
|
+
break;
|
|
206
|
+
case "unique":
|
|
207
|
+
// unique:table,column,ignore,idColumn
|
|
208
|
+
const [table, column = "id", ignoreValue, ignoreColumn = "id"] = params;
|
|
209
|
+
schema = schema.refine(async (val) => {
|
|
210
|
+
if (!val)
|
|
211
|
+
return true;
|
|
212
|
+
const where = { [column]: val };
|
|
213
|
+
if (ignoreValue && ignoreValue !== "null") {
|
|
214
|
+
// Try to handle numeric IDs if ignoreValue looks numeric
|
|
215
|
+
const ignoreVal = !isNaN(Number(ignoreValue))
|
|
216
|
+
? Number(ignoreValue)
|
|
217
|
+
: ignoreValue;
|
|
218
|
+
// But Prisma uses BigInt for IDs often in this project?
|
|
219
|
+
// Let's assume string or number is fine, user can cast if needed.
|
|
220
|
+
// In this project, IDs are BigInt.
|
|
221
|
+
if (typeof ignoreVal === "number" ||
|
|
222
|
+
/^\d+$/.test(String(ignoreValue))) {
|
|
223
|
+
where[ignoreColumn] = { not: BigInt(ignoreValue) };
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
where[ignoreColumn] = { not: ignoreValue };
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
// @ts-ignore
|
|
231
|
+
const count = await database_1.prisma[table].count({ where });
|
|
232
|
+
return count === 0;
|
|
233
|
+
}
|
|
234
|
+
catch (e) {
|
|
235
|
+
console.error(`Validator unique check failed for table ${table}:`, e);
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
}, { message: `The ${column} has already been taken.` });
|
|
239
|
+
break;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// 3. Modifiers
|
|
243
|
+
const isRequired = rules.includes("required");
|
|
244
|
+
isNullable = rules.includes("nullable");
|
|
245
|
+
isOptional = rules.includes("sometimes") || !isRequired;
|
|
246
|
+
if (isNullable) {
|
|
247
|
+
schema = schema.nullable();
|
|
248
|
+
}
|
|
249
|
+
if (isOptional) {
|
|
250
|
+
schema = schema.optional();
|
|
251
|
+
}
|
|
252
|
+
return schema;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Check if validation fails
|
|
256
|
+
*/
|
|
257
|
+
async fails() {
|
|
258
|
+
await this.run();
|
|
259
|
+
return this.errorResult !== null;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Check if validation passes
|
|
263
|
+
*/
|
|
264
|
+
async passes() {
|
|
265
|
+
return !(await this.fails());
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Get the validation errors
|
|
269
|
+
*/
|
|
270
|
+
errors() {
|
|
271
|
+
// Should be called after fails()
|
|
272
|
+
return this.errorResult || {};
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Get the validated data
|
|
276
|
+
*/
|
|
277
|
+
async validated() {
|
|
278
|
+
await this.run();
|
|
279
|
+
if (this.errorResult) {
|
|
280
|
+
throw new Error("Validation failed. Check errors() before calling validated().");
|
|
281
|
+
}
|
|
282
|
+
return this.result;
|
|
283
|
+
}
|
|
284
|
+
async run() {
|
|
285
|
+
if (this.hasRun)
|
|
286
|
+
return;
|
|
287
|
+
this.hasRun = true;
|
|
288
|
+
// safeParseAsync to handle async refinements (like unique)
|
|
289
|
+
const parseResult = await this.schema.safeParseAsync(this.data);
|
|
290
|
+
if (parseResult.success) {
|
|
291
|
+
this.result = parseResult.data;
|
|
292
|
+
this.errorResult = null;
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
this.result = null;
|
|
296
|
+
this.errorResult = this.formatErrors(parseResult.error);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
formatErrors(error) {
|
|
300
|
+
// If we have custom messages, we try to apply them
|
|
301
|
+
if (Object.keys(this.customMessages).length > 0) {
|
|
302
|
+
error.issues = error.issues.map((issue) => {
|
|
303
|
+
const message = this.getCustomMessage(issue);
|
|
304
|
+
if (message) {
|
|
305
|
+
return { ...issue, message };
|
|
306
|
+
}
|
|
307
|
+
return issue;
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
const flattened = error.flatten();
|
|
311
|
+
const errors = flattened.fieldErrors;
|
|
312
|
+
// Add formErrors if any
|
|
313
|
+
if (flattened.formErrors.length > 0) {
|
|
314
|
+
errors.formErrors = flattened.formErrors;
|
|
315
|
+
}
|
|
316
|
+
return errors;
|
|
317
|
+
}
|
|
318
|
+
getCustomMessage(issue) {
|
|
319
|
+
const path = issue.path.join(".");
|
|
320
|
+
let rule = "";
|
|
321
|
+
// Map Zod issue codes to "Laravel-like" rules for key lookup
|
|
322
|
+
switch (issue.code) {
|
|
323
|
+
case "invalid_type":
|
|
324
|
+
if (issue.received === "undefined" || issue.received === "null") {
|
|
325
|
+
rule = "required";
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
// e.g. string, number, boolean
|
|
329
|
+
rule = issue.expected;
|
|
330
|
+
}
|
|
331
|
+
break;
|
|
332
|
+
case "invalid_string":
|
|
333
|
+
if (typeof issue.validation === "string") {
|
|
334
|
+
rule = issue.validation; // email, url, uuid, etc.
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
rule = "string";
|
|
338
|
+
}
|
|
339
|
+
break;
|
|
340
|
+
case "too_small":
|
|
341
|
+
rule = "min";
|
|
342
|
+
break;
|
|
343
|
+
case "too_big":
|
|
344
|
+
rule = "max";
|
|
345
|
+
break;
|
|
346
|
+
case "custom":
|
|
347
|
+
// Custom rules often have messages already, but we can override
|
|
348
|
+
// For unique, mimes, etc.
|
|
349
|
+
// We might need to inspect the message to guess the rule?
|
|
350
|
+
// Or just use the path.
|
|
351
|
+
rule = "custom";
|
|
352
|
+
break;
|
|
353
|
+
// Add more mappings as needed
|
|
354
|
+
default:
|
|
355
|
+
rule = issue.code;
|
|
356
|
+
}
|
|
357
|
+
// Try keys: "field.rule" -> "field"
|
|
358
|
+
const specificKey = `${path}.${rule}`;
|
|
359
|
+
if (this.customMessages[specificKey]) {
|
|
360
|
+
return this.customMessages[specificKey];
|
|
361
|
+
}
|
|
362
|
+
// Fallback for generic field message "field"
|
|
363
|
+
if (this.customMessages[path]) {
|
|
364
|
+
return this.customMessages[path];
|
|
365
|
+
}
|
|
366
|
+
return undefined;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
exports.Validator = Validator;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../../prisma/seed.ts"],"names":[],"mappings":""}
|