wynkjs 1.0.3 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +252 -55
  2. package/dist/database.d.ts +1 -1
  3. package/dist/database.js +1 -1
  4. package/dist/decorators/exception.advanced.d.ts +286 -18
  5. package/dist/decorators/exception.advanced.d.ts.map +1 -1
  6. package/dist/decorators/exception.advanced.js +410 -17
  7. package/dist/decorators/exception.decorators.d.ts +92 -2
  8. package/dist/decorators/exception.decorators.d.ts.map +1 -1
  9. package/dist/decorators/exception.decorators.js +120 -5
  10. package/dist/decorators/formatter.decorators.d.ts +93 -0
  11. package/dist/decorators/formatter.decorators.d.ts.map +1 -0
  12. package/dist/decorators/formatter.decorators.js +131 -0
  13. package/dist/decorators/guard.decorators.d.ts +2 -2
  14. package/dist/decorators/http.decorators.d.ts +3 -2
  15. package/dist/decorators/http.decorators.d.ts.map +1 -1
  16. package/dist/decorators/pipe.decorators.d.ts +2 -2
  17. package/dist/decorators/pipe.decorators.d.ts.map +1 -1
  18. package/dist/decorators/pipe.decorators.js +2 -2
  19. package/dist/dto.js +1 -1
  20. package/dist/factory.d.ts +1 -1
  21. package/dist/factory.d.ts.map +1 -1
  22. package/dist/factory.js +55 -6
  23. package/dist/filters/exception.filters.d.ts +124 -0
  24. package/dist/filters/exception.filters.d.ts.map +1 -0
  25. package/dist/filters/exception.filters.js +208 -0
  26. package/dist/index.d.ts +3 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +4 -1
  29. package/dist/pipes/validation.pipe.d.ts +3 -3
  30. package/dist/pipes/validation.pipe.d.ts.map +1 -1
  31. package/dist/pipes/validation.pipe.js +39 -11
  32. package/dist/schema-registry.d.ts +51 -0
  33. package/dist/schema-registry.d.ts.map +1 -0
  34. package/dist/schema-registry.js +134 -0
  35. package/dist/testing/index.d.ts +2 -2
  36. package/dist/testing/index.js +2 -2
  37. package/package.json +8 -3
@@ -0,0 +1,124 @@
1
+ import { NotFoundException, WynkExceptionFilter } from "../decorators/exception.decorators";
2
+ import { ExecutionContext } from "../decorators/guard.decorators";
3
+ /**
4
+ * Database Exception Filter - Handles database errors ONLY (not HttpExceptions)
5
+ *
6
+ * This filter catches actual database errors (like unique constraint violations,
7
+ * foreign key errors, etc.) and converts them to user-friendly messages.
8
+ *
9
+ * It will NOT catch HttpException or its subclasses (ConflictException, etc.)
10
+ * that you throw manually - those will pass through to be handled correctly.
11
+ *
12
+ * @example
13
+ * // Use as global filter
14
+ * app.useGlobalFilters(new DatabaseExceptionFilter());
15
+ *
16
+ * // Use on specific controller
17
+ * @UseFilters(DatabaseExceptionFilter)
18
+ * @Controller('/users')
19
+ * export class UserController {
20
+ * @Post()
21
+ * async create(@Body() data: any) {
22
+ * // If you throw manually, it passes through:
23
+ * if (await this.userExists(data.email)) {
24
+ * throw new ConflictException('User with this email already exists'); // ✅ Works correctly
25
+ * }
26
+ *
27
+ * // If database throws error, filter catches it:
28
+ * return await this.db.insert(users).values(data); // ❌ DB unique constraint error → caught by filter
29
+ * }
30
+ * }
31
+ *
32
+ * Handles these database error codes:
33
+ * - 23505: Unique constraint violation → 409 Conflict
34
+ * - 23503: Foreign key constraint violation → 400 Bad Request
35
+ * - 23502: Not null constraint violation → 400 Bad Request
36
+ */
37
+ export declare class DatabaseExceptionFilter implements WynkExceptionFilter {
38
+ catch(exception: any, context: ExecutionContext): {
39
+ statusCode: number;
40
+ error: string;
41
+ message: string;
42
+ timestamp: string;
43
+ path: any;
44
+ };
45
+ }
46
+ /**
47
+ * Not Found Exception Filter - Handles 404 errors with smart detection
48
+ *
49
+ * This filter is SMART - it only handles NotFoundExceptionFilter if:
50
+ * 1. The exception is NotFoundException, AND
51
+ * 2. No response data has been set (empty, null, empty array, or empty object)
52
+ *
53
+ * This allows it to be used globally without breaking routes that return
54
+ * legitimate empty responses or have their own error handling.
55
+ *
56
+ * @example
57
+ * // ✅ Can be used globally - smart filtering
58
+ * app.useGlobalFilters(
59
+ * new NotFoundExceptionFilter(), // Safe to use globally now!
60
+ * new GlobalExceptionFilter()
61
+ * );
62
+ *
63
+ */
64
+ export declare class NotFoundExceptionFilter implements WynkExceptionFilter<NotFoundException> {
65
+ catch(exception: NotFoundException, context: ExecutionContext): {
66
+ statusCode: number;
67
+ error: string;
68
+ message: string;
69
+ timestamp: string;
70
+ path: any;
71
+ suggestion: string;
72
+ };
73
+ /**
74
+ * Check if response has meaningful data
75
+ * Returns false for: null, undefined, {}, [], ""
76
+ * Returns true for: anything else
77
+ */
78
+ private hasResponseData;
79
+ }
80
+ /**
81
+ * File Upload Exception Filter - Handles file upload errors
82
+ * @example
83
+ * @UseFilters(FileUploadExceptionFilter)
84
+ * @Post('/upload')
85
+ * async upload(@UploadedFile() file: any) {}
86
+ */
87
+ export declare class FileUploadExceptionFilter implements WynkExceptionFilter {
88
+ catch(exception: any, context: ExecutionContext): {
89
+ statusCode: number;
90
+ error: string;
91
+ message: string;
92
+ timestamp: string;
93
+ path: any;
94
+ };
95
+ }
96
+ /**
97
+ * Global Exception Filter - Catches all unhandled exceptions
98
+ * @example
99
+ * app.useGlobalFilters(new GlobalExceptionFilter());
100
+ */
101
+ export declare class GlobalExceptionFilter implements WynkExceptionFilter {
102
+ catch(exception: any, context: ExecutionContext): {
103
+ stack?: any;
104
+ statusCode: any;
105
+ error: any;
106
+ message: any;
107
+ timestamp: string;
108
+ path: any;
109
+ };
110
+ }
111
+ /**
112
+ // ============================================================================
113
+ // KEY TAKEAWAYS
114
+ // ============================================================================
115
+ //
116
+ // 1. Order matters: Specific → General
117
+ // 2. Filters can re-throw exceptions they don't handle
118
+ // 3. HttpException and subclasses should pass through specialized filters
119
+ // 4. Global filters catch everything not handled by controller/method filters
120
+ // 5. Always have a GlobalExceptionFilter as the last filter (catch-all)
121
+ //
122
+ // ============================================================================
123
+ */
124
+ //# sourceMappingURL=exception.filters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exception.filters.d.ts","sourceRoot":"","sources":["../../core/filters/exception.filters.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,iBAAiB,EACjB,mBAAmB,EACpB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAElE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,qBAAa,uBAAwB,YAAW,mBAAmB;IACjE,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB;;;;;;;CAuChD;AAED;;;;;;;;;;;;;;;;;GAiBG;AAEH,qBAAa,uBACX,YAAW,mBAAmB,CAAC,iBAAiB,CAAC;IAEjD,KAAK,CAAC,SAAS,EAAE,iBAAiB,EAAE,OAAO,EAAE,gBAAgB;;;;;;;;IAmB7D;;;;OAIG;IACH,OAAO,CAAC,eAAe;CAuBxB;AAED;;;;;;GAMG;AACH,qBAAa,yBAA0B,YAAW,mBAAmB;IACnE,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB;;;;;;;CA2BhD;AAED;;;;GAIG;AACH,qBAAa,qBAAsB,YAAW,mBAAmB;IAC/D,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB;;;;;;;;CA2BhD;AAED;;;;;;;;;;;;EAYE"}
@@ -0,0 +1,208 @@
1
+ import { HttpException, } from "../decorators/exception.decorators";
2
+ /**
3
+ * Database Exception Filter - Handles database errors ONLY (not HttpExceptions)
4
+ *
5
+ * This filter catches actual database errors (like unique constraint violations,
6
+ * foreign key errors, etc.) and converts them to user-friendly messages.
7
+ *
8
+ * It will NOT catch HttpException or its subclasses (ConflictException, etc.)
9
+ * that you throw manually - those will pass through to be handled correctly.
10
+ *
11
+ * @example
12
+ * // Use as global filter
13
+ * app.useGlobalFilters(new DatabaseExceptionFilter());
14
+ *
15
+ * // Use on specific controller
16
+ * @UseFilters(DatabaseExceptionFilter)
17
+ * @Controller('/users')
18
+ * export class UserController {
19
+ * @Post()
20
+ * async create(@Body() data: any) {
21
+ * // If you throw manually, it passes through:
22
+ * if (await this.userExists(data.email)) {
23
+ * throw new ConflictException('User with this email already exists'); // ✅ Works correctly
24
+ * }
25
+ *
26
+ * // If database throws error, filter catches it:
27
+ * return await this.db.insert(users).values(data); // ❌ DB unique constraint error → caught by filter
28
+ * }
29
+ * }
30
+ *
31
+ * Handles these database error codes:
32
+ * - 23505: Unique constraint violation → 409 Conflict
33
+ * - 23503: Foreign key constraint violation → 400 Bad Request
34
+ * - 23502: Not null constraint violation → 400 Bad Request
35
+ */
36
+ export class DatabaseExceptionFilter {
37
+ catch(exception, context) {
38
+ const response = context.getResponse();
39
+ const request = context.getRequest();
40
+ // Don't catch HttpException or its subclasses (like ConflictException)
41
+ // These are intentionally thrown by the user
42
+ if (exception instanceof HttpException) {
43
+ throw exception;
44
+ }
45
+ // Check for common database errors
46
+ let message = "Database error occurred";
47
+ let statusCode = 500;
48
+ if (exception.code === "23505" || exception.message?.includes("unique")) {
49
+ message = "Resource already exists";
50
+ statusCode = 409; // Conflict
51
+ }
52
+ else if (exception.code === "23503" ||
53
+ exception.message?.includes("foreign key")) {
54
+ message = "Referenced resource does not exist";
55
+ statusCode = 400;
56
+ }
57
+ else if (exception.code === "23502" ||
58
+ exception.message?.includes("not null")) {
59
+ message = "Required field is missing";
60
+ statusCode = 400;
61
+ }
62
+ return {
63
+ statusCode,
64
+ error: "Database Error",
65
+ message,
66
+ timestamp: new Date().toISOString(),
67
+ path: request.url,
68
+ };
69
+ }
70
+ }
71
+ /**
72
+ * Not Found Exception Filter - Handles 404 errors with smart detection
73
+ *
74
+ * This filter is SMART - it only handles NotFoundExceptionFilter if:
75
+ * 1. The exception is NotFoundException, AND
76
+ * 2. No response data has been set (empty, null, empty array, or empty object)
77
+ *
78
+ * This allows it to be used globally without breaking routes that return
79
+ * legitimate empty responses or have their own error handling.
80
+ *
81
+ * @example
82
+ * // ✅ Can be used globally - smart filtering
83
+ * app.useGlobalFilters(
84
+ * new NotFoundExceptionFilter(), // Safe to use globally now!
85
+ * new GlobalExceptionFilter()
86
+ * );
87
+ *
88
+ */
89
+ export class NotFoundExceptionFilter {
90
+ catch(exception, context) {
91
+ const response = context.getResponse();
92
+ const request = context.getRequest();
93
+ const hasResponseData = this.hasResponseData(response);
94
+ if (hasResponseData) {
95
+ throw exception;
96
+ }
97
+ return {
98
+ statusCode: exception.statusCode,
99
+ error: "Not Found",
100
+ message: exception.message || "Resource not found",
101
+ timestamp: new Date().toISOString(),
102
+ path: request.url,
103
+ suggestion: "Please check the resource ID or URL",
104
+ };
105
+ }
106
+ /**
107
+ * Check if response has meaningful data
108
+ * Returns false for: null, undefined, {}, [], ""
109
+ * Returns true for: anything else
110
+ */
111
+ hasResponseData(response) {
112
+ if (response === null || response === undefined) {
113
+ return false;
114
+ }
115
+ // Check for empty object
116
+ if (typeof response === "object" && !Array.isArray(response)) {
117
+ return Object.keys(response).length > 0;
118
+ }
119
+ // Check for empty array
120
+ if (Array.isArray(response)) {
121
+ return response.length > 0;
122
+ }
123
+ // Check for empty string
124
+ if (typeof response === "string") {
125
+ return response.length > 0;
126
+ }
127
+ // For numbers, booleans, etc. - consider them as having data
128
+ return true;
129
+ }
130
+ }
131
+ /**
132
+ * File Upload Exception Filter - Handles file upload errors
133
+ * @example
134
+ * @UseFilters(FileUploadExceptionFilter)
135
+ * @Post('/upload')
136
+ * async upload(@UploadedFile() file: any) {}
137
+ */
138
+ export class FileUploadExceptionFilter {
139
+ catch(exception, context) {
140
+ const response = context.getResponse();
141
+ const request = context.getRequest();
142
+ // Don't catch HttpException or its subclasses
143
+ if (exception instanceof HttpException) {
144
+ throw exception;
145
+ }
146
+ let message = "File upload failed";
147
+ if (exception.message?.includes("size")) {
148
+ message = "File size exceeds limit";
149
+ }
150
+ else if (exception.message?.includes("type")) {
151
+ message = "Invalid file type";
152
+ }
153
+ else if (exception.message?.includes("required")) {
154
+ message = "File is required";
155
+ }
156
+ return {
157
+ statusCode: 400,
158
+ error: "File Upload Error",
159
+ message,
160
+ timestamp: new Date().toISOString(),
161
+ path: request.url,
162
+ };
163
+ }
164
+ }
165
+ /**
166
+ * Global Exception Filter - Catches all unhandled exceptions
167
+ * @example
168
+ * app.useGlobalFilters(new GlobalExceptionFilter());
169
+ */
170
+ export class GlobalExceptionFilter {
171
+ catch(exception, context) {
172
+ const response = context.getResponse();
173
+ const request = context.getRequest();
174
+ const statusCode = exception.statusCode || 500;
175
+ const message = exception.message || "Internal server error";
176
+ // Log the error for debugging
177
+ console.error("❌ Unhandled exception:", {
178
+ statusCode,
179
+ message,
180
+ path: request.url,
181
+ method: request.method,
182
+ stack: exception.stack,
183
+ });
184
+ return {
185
+ statusCode,
186
+ error: exception.name || "Error",
187
+ message,
188
+ timestamp: new Date().toISOString(),
189
+ path: request.url,
190
+ ...(process.env.NODE_ENV === "development" && {
191
+ stack: exception.stack,
192
+ }),
193
+ };
194
+ }
195
+ }
196
+ /**
197
+ // ============================================================================
198
+ // KEY TAKEAWAYS
199
+ // ============================================================================
200
+ //
201
+ // 1. Order matters: Specific → General
202
+ // 2. Filters can re-throw exceptions they don't handle
203
+ // 3. HttpException and subclasses should pass through specialized filters
204
+ // 4. Global filters catch everything not handled by controller/method filters
205
+ // 5. Always have a GlobalExceptionFilter as the last filter (catch-all)
206
+ //
207
+ // ============================================================================
208
+ */
package/dist/index.d.ts CHANGED
@@ -17,9 +17,11 @@ export * from "./decorators/interceptor.advanced";
17
17
  export * from "./decorators/pipe.decorators";
18
18
  export * from "./decorators/pipe.advanced";
19
19
  export * from "./decorators/exception.decorators";
20
- export * from "./decorators/exception.advanced";
20
+ export * from "./decorators/formatter.decorators";
21
+ export * from "./filters/exception.filters";
21
22
  export * from "./decorators/database.decorators";
22
23
  export * from "./dto";
24
+ export { schemaRegistry } from "./schema-registry";
23
25
  export * from "./factory";
24
26
  export * from "./testing";
25
27
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../core/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,kBAAkB,CAAC;AAI1B,OAAO,EACL,UAAU,EACV,MAAM,EACN,SAAS,EACT,cAAc,EACd,QAAQ,EACR,SAAS,GACV,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAGpD,OAAO,EACL,UAAU,IAAI,UAAU,EACxB,MAAM,IAAI,MAAM,EAChB,SAAS,IAAI,SAAS,EACtB,cAAc,IAAI,cAAc,EAChC,QAAQ,IAAI,QAAQ,EACpB,SAAS,IAAI,SAAS,GACvB,MAAM,UAAU,CAAC;AAGlB,cAAc,8BAA8B,CAAC;AAG7C,cAAc,+BAA+B,CAAC;AAG9C,cAAc,+BAA+B,CAAC;AAG9C,cAAc,qCAAqC,CAAC;AACpD,cAAc,mCAAmC,CAAC;AAGlD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC;AAI3C,cAAc,mCAAmC,CAAC;AAClD,cAAc,iCAAiC,CAAC;AAIhD,cAAc,kCAAkC,CAAC;AASjD,cAAc,OAAO,CAAC;AAGtB,cAAc,WAAW,CAAC;AAG1B,cAAc,WAAW,CAAC;AAE1B;;GAEG;AACH,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B;;GAEG;AACH,eAAO,MAAM,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../core/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,kBAAkB,CAAC;AAI1B,OAAO,EACL,UAAU,EACV,MAAM,EACN,SAAS,EACT,cAAc,EACd,QAAQ,EACR,SAAS,GACV,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAGpD,OAAO,EACL,UAAU,IAAI,UAAU,EACxB,MAAM,IAAI,MAAM,EAChB,SAAS,IAAI,SAAS,EACtB,cAAc,IAAI,cAAc,EAChC,QAAQ,IAAI,QAAQ,EACpB,SAAS,IAAI,SAAS,GACvB,MAAM,UAAU,CAAC;AAGlB,cAAc,8BAA8B,CAAC;AAG7C,cAAc,+BAA+B,CAAC;AAG9C,cAAc,+BAA+B,CAAC;AAG9C,cAAc,qCAAqC,CAAC;AACpD,cAAc,mCAAmC,CAAC;AAGlD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC;AAI3C,cAAc,mCAAmC,CAAC;AAClD,cAAc,mCAAmC,CAAC;AAClD,cAAc,6BAA6B,CAAC;AAI5C,cAAc,kCAAkC,CAAC;AASjD,cAAc,OAAO,CAAC;AAGtB,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGnD,cAAc,WAAW,CAAC;AAG1B,cAAc,WAAW,CAAC;AAE1B;;GAEG;AACH,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B;;GAEG;AACH,eAAO,MAAM,cAAc,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -28,7 +28,8 @@ export * from "./decorators/pipe.advanced";
28
28
  // Note: validation.pipe.ts is deprecated, use pipe.decorators.ts instead
29
29
  // Exception Filters
30
30
  export * from "./decorators/exception.decorators";
31
- export * from "./decorators/exception.advanced";
31
+ export * from "./decorators/formatter.decorators";
32
+ export * from "./filters/exception.filters";
32
33
  // Database Registry (General-Purpose)
33
34
  // Works with ANY ORM/ODM: Drizzle, Mongoose, Prisma, TypeORM, custom Database classes
34
35
  export * from "./decorators/database.decorators";
@@ -39,6 +40,8 @@ export * from "./decorators/database.decorators";
39
40
  // See: plugins/drizzle and plugins/mongoose
40
41
  // DTO Utilities
41
42
  export * from "./dto";
43
+ // Schema Registry for custom error messages
44
+ export { schemaRegistry } from "./schema-registry";
42
45
  // Application Factory
43
46
  export * from "./factory";
44
47
  // Testing Module
@@ -63,12 +63,12 @@ export declare class ValidationPipe implements ValidationPipeTransform {
63
63
  */
64
64
  protected parseValidationError(exception: any): ElysiaValidationError;
65
65
  /**
66
- * Default error formatting
66
+ * Default error formatting with custom errorMessage support
67
67
  */
68
- protected defaultFormatError(error: ElysiaValidationError): any;
68
+ protected defaultFormatError(error: ElysiaValidationError, schemaKey?: string): any;
69
69
  }
70
70
  /**
71
- * Custom Format Error Pipe (like NestJS example)
71
+ * Custom Format Error Pipe
72
72
  * Formats validation errors as { [field]: [messages] }
73
73
  */
74
74
  export declare class FormatErrorPipe extends ValidationPipe {
@@ -1 +1 @@
1
- {"version":3,"file":"validation.pipe.d.ts","sourceRoot":"","sources":["../../core/pipes/validation.pipe.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B;;;GAGG;AACH,MAAM,WAAW,uBAAuB,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG;IACvD,SAAS,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,gBAAgB,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACjE;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC5C,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,YAAY,CAAC;IACnB,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,MAAM,CAAC,EAAE,KAAK,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,GAAG,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,GAAG,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;CACJ;AAED;;;GAGG;AACH,qBAAa,cAAe,YAAW,uBAAuB;IAC5D,SAAS,CAAC,OAAO,EAAE;QACjB,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,KAAK,GAAG,CAAC;QAC1D,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;gBAEU,OAAO,CAAC,EAAE;QACpB,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,KAAK,GAAG,CAAC;QAC1D,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB;IAID;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,gBAAgB,GAAG,GAAG;IAItD;;;OAGG;IACH,WAAW,CAAC,SAAS,EAAE,GAAG,GAAG,GAAG;IAahC;;OAEG;IACH,SAAS,CAAC,oBAAoB,CAAC,SAAS,EAAE,GAAG,GAAG,qBAAqB;IAgBrE;;OAEG;IACH,SAAS,CAAC,kBAAkB,CAAC,KAAK,EAAE,qBAAqB,GAAG,GAAG;CAyBhE;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,cAAc;;CA2BlD;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,cAAc;;CAsBlD;AAED;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,cAAc;;CAgCpD"}
1
+ {"version":3,"file":"validation.pipe.d.ts","sourceRoot":"","sources":["../../core/pipes/validation.pipe.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAG1B;;;GAGG;AACH,MAAM,WAAW,uBAAuB,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG;IACvD,SAAS,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,gBAAgB,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACjE;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC;IAC5C,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,YAAY,CAAC;IACnB,EAAE,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,CAAC;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,MAAM,CAAC,EAAE,KAAK,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,GAAG,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,GAAG,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;CACJ;AAED;;;GAGG;AACH,qBAAa,cAAe,YAAW,uBAAuB;IAC5D,SAAS,CAAC,OAAO,EAAE;QACjB,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,KAAK,GAAG,CAAC;QAC1D,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;gBAEU,OAAO,CAAC,EAAE;QACpB,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,KAAK,GAAG,CAAC;QAC1D,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB;IAID;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,gBAAgB,GAAG,GAAG;IAItD;;;OAGG;IACH,WAAW,CAAC,SAAS,EAAE,GAAG,GAAG,GAAG;IAahC;;OAEG;IACH,SAAS,CAAC,oBAAoB,CAAC,SAAS,EAAE,GAAG,GAAG,qBAAqB;IAgBrE;;OAEG;IACH,SAAS,CAAC,kBAAkB,CAC1B,KAAK,EAAE,qBAAqB,EAC5B,SAAS,CAAC,EAAE,MAAM,GACjB,GAAG;CA2CP;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,cAAc;;CAiClD;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,cAAc;;CA4BlD;AAED;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,cAAc;;CAoCpD"}
@@ -1,4 +1,5 @@
1
1
  import "reflect-metadata";
2
+ import { schemaRegistry } from "../schema-registry";
2
3
  /**
3
4
  * Base Validation Pipe
4
5
  * Handles Elysia validation errors and formats them
@@ -47,23 +48,35 @@ export class ValidationPipe {
47
48
  return validationData;
48
49
  }
49
50
  /**
50
- * Default error formatting
51
+ * Default error formatting with custom errorMessage support
51
52
  */
52
- defaultFormatError(error) {
53
+ defaultFormatError(error, schemaKey) {
53
54
  const errors = [];
54
55
  if (error.errors && error.errors.length > 0) {
55
56
  error.errors.forEach((err) => {
57
+ const fieldPath = err.path?.replace(/^\//, "") || "unknown";
58
+ // Try to get custom message from schema registry first
59
+ let message = err.summary || err.message;
60
+ if (schemaKey) {
61
+ const customMessage = schemaRegistry.getErrorMessage(schemaKey, fieldPath);
62
+ if (customMessage) {
63
+ message = customMessage;
64
+ }
65
+ }
56
66
  errors.push({
57
- field: err.path?.replace(/^\//, "") || "unknown",
58
- message: err.summary || err.message,
67
+ field: fieldPath,
68
+ message: message,
59
69
  value: err.value,
60
70
  });
61
71
  });
62
72
  }
63
73
  else {
74
+ // Check schema for custom errorMessage
75
+ const customMessage = error.schema?.errorMessage;
76
+ const message = customMessage || error.summary || error.message;
64
77
  errors.push({
65
78
  field: error.property?.replace(/^\//, "") || "unknown",
66
- message: error.summary || error.message,
79
+ message: message,
67
80
  value: error.value,
68
81
  });
69
82
  }
@@ -75,7 +88,7 @@ export class ValidationPipe {
75
88
  }
76
89
  }
77
90
  /**
78
- * Custom Format Error Pipe (like NestJS example)
91
+ * Custom Format Error Pipe
79
92
  * Formats validation errors as { [field]: [messages] }
80
93
  */
81
94
  export class FormatErrorPipe extends ValidationPipe {
@@ -89,12 +102,18 @@ export class FormatErrorPipe extends ValidationPipe {
89
102
  if (!formattedErrors[field]) {
90
103
  formattedErrors[field] = [];
91
104
  }
92
- formattedErrors[field].push(err.summary || err.message);
105
+ // Use custom errorMessage if available
106
+ const customMessage = err.schema?.errorMessage;
107
+ const message = customMessage || err.summary || err.message;
108
+ formattedErrors[field].push(message);
93
109
  });
94
110
  }
95
111
  else {
96
112
  const field = error.property?.replace(/^\//, "") || "unknown";
97
- formattedErrors[field] = [error.summary || error.message];
113
+ // Use custom errorMessage if available
114
+ const customMessage = error.schema?.errorMessage;
115
+ const message = customMessage || error.summary || error.message;
116
+ formattedErrors[field] = [message];
98
117
  }
99
118
  return {
100
119
  statusCode: 400,
@@ -116,11 +135,17 @@ export class SimpleErrorPipe extends ValidationPipe {
116
135
  const messages = [];
117
136
  if (error.errors && error.errors.length > 0) {
118
137
  error.errors.forEach((err) => {
119
- messages.push(err.summary || err.message);
138
+ // Use custom errorMessage if available
139
+ const customMessage = err.schema?.errorMessage;
140
+ const message = customMessage || err.summary || err.message;
141
+ messages.push(message);
120
142
  });
121
143
  }
122
144
  else {
123
- messages.push(error.summary || error.message);
145
+ // Use custom errorMessage if available
146
+ const customMessage = error.schema?.errorMessage;
147
+ const message = customMessage || error.summary || error.message;
148
+ messages.push(message);
124
149
  }
125
150
  return {
126
151
  statusCode: 400,
@@ -142,9 +167,12 @@ export class DetailedErrorPipe extends ValidationPipe {
142
167
  const errors = [];
143
168
  if (error.errors && error.errors.length > 0) {
144
169
  error.errors.forEach((err) => {
170
+ // Use custom errorMessage if available
171
+ const customMessage = err.schema?.errorMessage;
172
+ const message = customMessage || err.summary || err.message;
145
173
  errors.push({
146
174
  field: err.path?.replace(/^\//, "") || "unknown",
147
- message: err.summary || err.message,
175
+ message: message,
148
176
  value: err.value,
149
177
  expected: err.schema?.format
150
178
  ? `${err.schema.type} (format: ${err.schema.format})`
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Schema Registry for storing and retrieving custom error messages
3
+ * This allows validation pipes to look up custom error messages from schemas
4
+ */
5
+ declare class SchemaRegistry {
6
+ private static instance;
7
+ private errorMessages;
8
+ private routeSchemas;
9
+ private constructor();
10
+ static getInstance(): SchemaRegistry;
11
+ /**
12
+ * Register custom error messages for a schema
13
+ * @param schemaKey Unique key for the schema (e.g., class name + method)
14
+ * @param schema The TypeBox schema object
15
+ */
16
+ registerSchema(schemaKey: string, schema: any): void;
17
+ /**
18
+ * Register route-to-schema mapping
19
+ * @param method HTTP method (GET, POST, etc.)
20
+ * @param path Route path
21
+ * @param schemaKey The schema key
22
+ * @param validationType Type of validation (body, query, params)
23
+ */
24
+ registerRoute(method: string, path: string, schemaKey: string, validationType: "body" | "query" | "params"): void;
25
+ /**
26
+ * Get schema key for a route and validation type
27
+ * @param method HTTP method
28
+ * @param path Route path (actual request path with values)
29
+ * @param validationType Type of validation
30
+ * @returns Schema key or undefined
31
+ */
32
+ getSchemaKeyForRoute(method: string, path: string, validationType: "body" | "query" | "params"): string | undefined;
33
+ /**
34
+ * Recursively extract error messages from schema
35
+ */
36
+ private extractErrorMessages;
37
+ /**
38
+ * Get custom error message for a field path
39
+ * @param schemaKey The schema key
40
+ * @param fieldPath The field path (e.g., "user.email")
41
+ * @returns Custom error message or undefined
42
+ */
43
+ getErrorMessage(schemaKey: string, fieldPath: string): string | undefined;
44
+ /**
45
+ * Clear all registered schemas (useful for testing)
46
+ */
47
+ clear(): void;
48
+ }
49
+ export declare const schemaRegistry: SchemaRegistry;
50
+ export {};
51
+ //# sourceMappingURL=schema-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-registry.d.ts","sourceRoot":"","sources":["../core/schema-registry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAgBH,cAAM,cAAc;IAClB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAiB;IACxC,OAAO,CAAC,aAAa,CAA2B;IAChD,OAAO,CAAC,YAAY,CAAsB;IAE1C,OAAO;IAEP,MAAM,CAAC,WAAW,IAAI,cAAc;IAOpC;;;;OAIG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI;IAWpD;;;;;;OAMG;IACH,aAAa,CACX,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAC1C,IAAI;IAUP;;;;;;OAMG;IACH,oBAAoB,CAClB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,cAAc,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,GAC1C,MAAM,GAAG,SAAS;IAyCrB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAyB5B;;;;;OAKG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAkBzE;;OAEG;IACH,KAAK,IAAI,IAAI;CAId;AAED,eAAO,MAAM,cAAc,gBAA+B,CAAC"}