wynkjs 1.0.0

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 (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +522 -0
  3. package/dist/database.d.ts +36 -0
  4. package/dist/database.d.ts.map +1 -0
  5. package/dist/database.js +162 -0
  6. package/dist/decorators/database.decorators.d.ts +55 -0
  7. package/dist/decorators/database.decorators.d.ts.map +1 -0
  8. package/dist/decorators/database.decorators.js +131 -0
  9. package/dist/decorators/exception.advanced.d.ts +160 -0
  10. package/dist/decorators/exception.advanced.d.ts.map +1 -0
  11. package/dist/decorators/exception.advanced.js +232 -0
  12. package/dist/decorators/exception.decorators.d.ts +121 -0
  13. package/dist/decorators/exception.decorators.d.ts.map +1 -0
  14. package/dist/decorators/exception.decorators.js +242 -0
  15. package/dist/decorators/guard.decorators.d.ts +43 -0
  16. package/dist/decorators/guard.decorators.d.ts.map +1 -0
  17. package/dist/decorators/guard.decorators.js +67 -0
  18. package/dist/decorators/http.decorators.d.ts +130 -0
  19. package/dist/decorators/http.decorators.d.ts.map +1 -0
  20. package/dist/decorators/http.decorators.js +209 -0
  21. package/dist/decorators/interceptor.advanced.d.ts +93 -0
  22. package/dist/decorators/interceptor.advanced.d.ts.map +1 -0
  23. package/dist/decorators/interceptor.advanced.js +228 -0
  24. package/dist/decorators/interceptor.decorators.d.ts +91 -0
  25. package/dist/decorators/interceptor.decorators.d.ts.map +1 -0
  26. package/dist/decorators/interceptor.decorators.js +163 -0
  27. package/dist/decorators/param.decorators.d.ts +144 -0
  28. package/dist/decorators/param.decorators.d.ts.map +1 -0
  29. package/dist/decorators/param.decorators.js +205 -0
  30. package/dist/decorators/pipe.advanced.d.ts +125 -0
  31. package/dist/decorators/pipe.advanced.d.ts.map +1 -0
  32. package/dist/decorators/pipe.advanced.js +263 -0
  33. package/dist/decorators/pipe.decorators.d.ts +226 -0
  34. package/dist/decorators/pipe.decorators.d.ts.map +1 -0
  35. package/dist/decorators/pipe.decorators.js +420 -0
  36. package/dist/dto.d.ts +83 -0
  37. package/dist/dto.d.ts.map +1 -0
  38. package/dist/dto.js +88 -0
  39. package/dist/factory.d.ts +76 -0
  40. package/dist/factory.d.ts.map +1 -0
  41. package/dist/factory.js +410 -0
  42. package/dist/index.d.ts +28 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +43 -0
  45. package/dist/pipes/validation.pipe.d.ts +91 -0
  46. package/dist/pipes/validation.pipe.d.ts.map +1 -0
  47. package/dist/pipes/validation.pipe.js +163 -0
  48. package/package.json +68 -0
@@ -0,0 +1,226 @@
1
+ import "reflect-metadata";
2
+ /**
3
+ * Pipe Decorators and Interfaces for WynkJS Framework
4
+ * Pipes for data validation and transformation
5
+ */
6
+ /**
7
+ * WynkPipeTransform interface - All pipes must implement this
8
+ */
9
+ export interface WynkPipeTransform<T = any, R = any> {
10
+ transform(value: T, metadata?: ArgumentMetadata): R | Promise<R>;
11
+ }
12
+ /**
13
+ * Argument metadata interface
14
+ */
15
+ export interface ArgumentMetadata {
16
+ type: "body" | "query" | "param" | "custom";
17
+ metatype?: any;
18
+ data?: string;
19
+ }
20
+ /**
21
+ * @UsePipes decorator - Apply pipes to routes, controllers, or parameters
22
+ * @param pipes Pipe classes to apply
23
+ * @example
24
+ * @UsePipes(ValidationPipe)
25
+ * @Controller('/users')
26
+ * export class UserController {}
27
+ *
28
+ * @Post()
29
+ * @UsePipes(ValidationPipe, TransformPipe)
30
+ * create(@Body() dto: CreateDto) {}
31
+ */
32
+ export declare function UsePipes(...pipes: (Function | WynkPipeTransform)[]): MethodDecorator & ClassDecorator;
33
+ /**
34
+ * Helper function to execute pipes
35
+ */
36
+ export declare function executePipes(pipes: (Function | WynkPipeTransform)[], value: any, metadata: ArgumentMetadata): Promise<any>;
37
+ /**
38
+ * Built-in Pipes
39
+ */
40
+ /**
41
+ * ValidationPipe - Validates and transforms input data
42
+ * Supports custom error formatting via exceptionFactory
43
+ *
44
+ * @example
45
+ * // Default formatting
46
+ * @UsePipes(new ValidationPipe())
47
+ * create(@Body() dto: CreateDto) {}
48
+ *
49
+ * @example
50
+ * // Custom formatting (like NestJS)
51
+ * const customPipe = new ValidationPipe({
52
+ * exceptionFactory: (errors) => ({
53
+ * statusCode: 400,
54
+ * message: 'Validation failed',
55
+ * errors: errors
56
+ * })
57
+ * });
58
+ */
59
+ export declare class ValidationPipe implements WynkPipeTransform {
60
+ private options;
61
+ constructor(options?: {
62
+ whitelist?: boolean;
63
+ forbidNonWhitelisted?: boolean;
64
+ transform?: boolean;
65
+ exceptionFactory?: (errors: any) => any;
66
+ });
67
+ transform(value: any, metadata: ArgumentMetadata): Promise<any>;
68
+ /**
69
+ * Format Elysia validation error
70
+ * Called by ValidationExceptionFilter
71
+ */
72
+ formatError(exception: any): any;
73
+ /**
74
+ * Parse validation error from Elysia exception
75
+ */
76
+ private parseValidationError;
77
+ /**
78
+ * Default error formatting
79
+ */
80
+ private defaultFormatError;
81
+ private isPrimitive;
82
+ }
83
+ /**
84
+ * ParseIntPipe - Transforms string to integer
85
+ * @example
86
+ * @Get('/:id')
87
+ * findOne(@Param('id', ParseIntPipe) id: number) {}
88
+ */
89
+ export declare class ParseIntPipe implements WynkPipeTransform<string, number> {
90
+ transform(value: string, metadata: ArgumentMetadata): Promise<number>;
91
+ }
92
+ /**
93
+ * ParseFloatPipe - Transforms string to float
94
+ * @example
95
+ * @Get()
96
+ * search(@Query('price', ParseFloatPipe) price: number) {}
97
+ */
98
+ export declare class ParseFloatPipe implements WynkPipeTransform<string, number> {
99
+ transform(value: string, metadata: ArgumentMetadata): Promise<number>;
100
+ }
101
+ /**
102
+ * ParseBoolPipe - Transforms string to boolean
103
+ * @example
104
+ * @Get()
105
+ * search(@Query('active', ParseBoolPipe) active: boolean) {}
106
+ */
107
+ export declare class ParseBoolPipe implements WynkPipeTransform<string, boolean> {
108
+ transform(value: string, metadata: ArgumentMetadata): Promise<boolean>;
109
+ }
110
+ /**
111
+ * ParseArrayPipe - Transforms string to array
112
+ * @example
113
+ * @Get()
114
+ * search(@Query('ids', ParseArrayPipe) ids: string[]) {}
115
+ */
116
+ export declare class ParseArrayPipe implements WynkPipeTransform<string, string[]> {
117
+ private separator;
118
+ constructor(separator?: string);
119
+ transform(value: string, metadata: ArgumentMetadata): Promise<string[]>;
120
+ }
121
+ /**
122
+ * ParseUUIDPipe - Validates UUID format
123
+ * @example
124
+ * @Get('/:id')
125
+ * findOne(@Param('id', ParseUUIDPipe) id: string) {}
126
+ */
127
+ export declare class ParseUUIDPipe implements WynkPipeTransform<string, string> {
128
+ transform(value: string, metadata: ArgumentMetadata): Promise<string>;
129
+ }
130
+ /**
131
+ * ParseEnumPipe - Validates enum values
132
+ * @example
133
+ * enum Status { ACTIVE, INACTIVE }
134
+ * @Get()
135
+ * search(@Query('status', new ParseEnumPipe(Status)) status: Status) {}
136
+ */
137
+ export declare class ParseEnumPipe<T = any> implements WynkPipeTransform<string, T> {
138
+ private enumType;
139
+ constructor(enumType: any);
140
+ transform(value: string, metadata: ArgumentMetadata): Promise<T>;
141
+ }
142
+ /**
143
+ * DefaultValuePipe - Provides default value if undefined
144
+ * @example
145
+ * @Get()
146
+ * search(@Query('page', new DefaultValuePipe(1)) page: number) {}
147
+ */
148
+ export declare class DefaultValuePipe<T = any> implements WynkPipeTransform<T, T> {
149
+ private defaultValue;
150
+ constructor(defaultValue: T);
151
+ transform(value: T, metadata: ArgumentMetadata): Promise<T>;
152
+ }
153
+ /**
154
+ * TrimPipe - Trims whitespace from strings
155
+ * @example
156
+ * @Post()
157
+ * create(@Body('name', TrimPipe) name: string) {}
158
+ */
159
+ export declare class TrimPipe implements WynkPipeTransform<string, string> {
160
+ transform(value: string, metadata: ArgumentMetadata): Promise<string>;
161
+ }
162
+ /**
163
+ * FormatErrorPipe - Formats validation errors as { [field]: [messages] }
164
+ * Like NestJS format with field names as keys
165
+ *
166
+ * @example
167
+ * // In index.ts or controller
168
+ * app.useGlobalPipes(new FormatErrorPipe());
169
+ *
170
+ * @example
171
+ * // Response format:
172
+ * {
173
+ * statusCode: 400,
174
+ * message: "Validation failed",
175
+ * errors: {
176
+ * email: ["Property 'email' should be email"],
177
+ * mobile: ["Expected string to match '^[6-9]{1}[0-9]{9}$'"]
178
+ * }
179
+ * }
180
+ */
181
+ export declare class FormatErrorPipe extends ValidationPipe {
182
+ constructor();
183
+ }
184
+ /**
185
+ * SimpleErrorPipe - Returns flat array of error messages
186
+ *
187
+ * @example
188
+ * app.useGlobalPipes(new SimpleErrorPipe());
189
+ *
190
+ * @example
191
+ * // Response format:
192
+ * {
193
+ * statusCode: 400,
194
+ * message: "Property 'email' should be email, Invalid mobile number",
195
+ * errors: [
196
+ * "Property 'email' should be email",
197
+ * "Invalid mobile number"
198
+ * ]
199
+ * }
200
+ */
201
+ export declare class SimpleErrorPipe extends ValidationPipe {
202
+ constructor();
203
+ }
204
+ /**
205
+ * DetailedErrorPipe - Returns detailed information for each field
206
+ *
207
+ * @example
208
+ * app.useGlobalPipes(new DetailedErrorPipe());
209
+ *
210
+ * @example
211
+ * // Response format:
212
+ * {
213
+ * statusCode: 400,
214
+ * message: "Validation failed",
215
+ * errors: [{
216
+ * field: "email",
217
+ * message: "Property 'email' should be email",
218
+ * value: "demo@demo.",
219
+ * expected: "string (format: email)"
220
+ * }]
221
+ * }
222
+ */
223
+ export declare class DetailedErrorPipe extends ValidationPipe {
224
+ constructor();
225
+ }
226
+ //# sourceMappingURL=pipe.decorators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pipe.decorators.d.ts","sourceRoot":"","sources":["../../core/decorators/pipe.decorators.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,420 @@
1
+ import "reflect-metadata";
2
+ /**
3
+ * @UsePipes decorator - Apply pipes to routes, controllers, or parameters
4
+ * @param pipes Pipe classes to apply
5
+ * @example
6
+ * @UsePipes(ValidationPipe)
7
+ * @Controller('/users')
8
+ * export class UserController {}
9
+ *
10
+ * @Post()
11
+ * @UsePipes(ValidationPipe, TransformPipe)
12
+ * create(@Body() dto: CreateDto) {}
13
+ */
14
+ export function UsePipes(...pipes) {
15
+ return (target, propertyKey, descriptor) => {
16
+ if (propertyKey && descriptor) {
17
+ // Method decorator
18
+ const existing = Reflect.getMetadata("pipes", target, propertyKey) || [];
19
+ Reflect.defineMetadata("pipes", [...existing, ...pipes], target, propertyKey);
20
+ return descriptor;
21
+ }
22
+ else {
23
+ // Class decorator
24
+ const existing = Reflect.getMetadata("pipes", target) || [];
25
+ Reflect.defineMetadata("pipes", [...existing, ...pipes], target);
26
+ return target;
27
+ }
28
+ };
29
+ }
30
+ /**
31
+ * Helper function to execute pipes
32
+ */
33
+ export async function executePipes(pipes, value, metadata) {
34
+ let transformedValue = value;
35
+ for (const pipe of pipes) {
36
+ let pipeInstance;
37
+ if (typeof pipe === "function") {
38
+ pipeInstance = new pipe();
39
+ }
40
+ else {
41
+ pipeInstance = pipe;
42
+ }
43
+ if (!pipeInstance.transform) {
44
+ throw new Error(`Pipe must implement WynkPipeTransform interface`);
45
+ }
46
+ transformedValue = await pipeInstance.transform(transformedValue, metadata);
47
+ }
48
+ return transformedValue;
49
+ }
50
+ /**
51
+ * Built-in Pipes
52
+ */
53
+ /**
54
+ * ValidationPipe - Validates and transforms input data
55
+ * Supports custom error formatting via exceptionFactory
56
+ *
57
+ * @example
58
+ * // Default formatting
59
+ * @UsePipes(new ValidationPipe())
60
+ * create(@Body() dto: CreateDto) {}
61
+ *
62
+ * @example
63
+ * // Custom formatting (like NestJS)
64
+ * const customPipe = new ValidationPipe({
65
+ * exceptionFactory: (errors) => ({
66
+ * statusCode: 400,
67
+ * message: 'Validation failed',
68
+ * errors: errors
69
+ * })
70
+ * });
71
+ */
72
+ export class ValidationPipe {
73
+ options;
74
+ constructor(options = {}) {
75
+ this.options = {
76
+ whitelist: true,
77
+ forbidNonWhitelisted: false,
78
+ transform: true,
79
+ ...options,
80
+ };
81
+ }
82
+ async transform(value, metadata) {
83
+ // If no metatype or primitive type, return as is
84
+ if (!metadata.metatype || this.isPrimitive(metadata.metatype)) {
85
+ return value;
86
+ }
87
+ // Here you would integrate with class-validator
88
+ // For now, basic validation
89
+ if (value === undefined || value === null) {
90
+ throw new Error(`Validation failed: Value is required`);
91
+ }
92
+ return value;
93
+ }
94
+ /**
95
+ * Format Elysia validation error
96
+ * Called by ValidationExceptionFilter
97
+ */
98
+ formatError(exception) {
99
+ const validationError = this.parseValidationError(exception);
100
+ // If user provided custom exception factory, use it
101
+ if (this.options.exceptionFactory) {
102
+ return this.options.exceptionFactory(validationError);
103
+ }
104
+ // Default formatting
105
+ return this.defaultFormatError(validationError);
106
+ }
107
+ /**
108
+ * Parse validation error from Elysia exception
109
+ */
110
+ parseValidationError(exception) {
111
+ let validationData;
112
+ if (typeof exception.message === "string") {
113
+ try {
114
+ validationData = JSON.parse(exception.message);
115
+ }
116
+ catch {
117
+ validationData = { type: "validation", message: exception.message };
118
+ }
119
+ }
120
+ else {
121
+ validationData = exception;
122
+ }
123
+ return validationData;
124
+ }
125
+ /**
126
+ * Default error formatting
127
+ */
128
+ defaultFormatError(error) {
129
+ const errors = [];
130
+ if (error.errors && error.errors.length > 0) {
131
+ error.errors.forEach((err) => {
132
+ errors.push({
133
+ field: err.path?.replace(/^\//, "") || "unknown",
134
+ message: err.summary || err.message,
135
+ value: err.value,
136
+ });
137
+ });
138
+ }
139
+ else {
140
+ errors.push({
141
+ field: error.property?.replace(/^\//, "") || "unknown",
142
+ message: error.summary || error.message,
143
+ value: error.value,
144
+ });
145
+ }
146
+ return {
147
+ statusCode: 400,
148
+ message: "Validation failed",
149
+ errors,
150
+ };
151
+ }
152
+ isPrimitive(metatype) {
153
+ const types = [String, Boolean, Number, Array, Object];
154
+ return types.includes(metatype);
155
+ }
156
+ }
157
+ /**
158
+ * ParseIntPipe - Transforms string to integer
159
+ * @example
160
+ * @Get('/:id')
161
+ * findOne(@Param('id', ParseIntPipe) id: number) {}
162
+ */
163
+ export class ParseIntPipe {
164
+ async transform(value, metadata) {
165
+ const val = parseInt(value, 10);
166
+ if (isNaN(val)) {
167
+ throw new Error(`Validation failed: "${value}" is not an integer`);
168
+ }
169
+ return val;
170
+ }
171
+ }
172
+ /**
173
+ * ParseFloatPipe - Transforms string to float
174
+ * @example
175
+ * @Get()
176
+ * search(@Query('price', ParseFloatPipe) price: number) {}
177
+ */
178
+ export class ParseFloatPipe {
179
+ async transform(value, metadata) {
180
+ const val = parseFloat(value);
181
+ if (isNaN(val)) {
182
+ throw new Error(`Validation failed: "${value}" is not a number`);
183
+ }
184
+ return val;
185
+ }
186
+ }
187
+ /**
188
+ * ParseBoolPipe - Transforms string to boolean
189
+ * @example
190
+ * @Get()
191
+ * search(@Query('active', ParseBoolPipe) active: boolean) {}
192
+ */
193
+ export class ParseBoolPipe {
194
+ async transform(value, metadata) {
195
+ if (value === "true" || value === "1")
196
+ return true;
197
+ if (value === "false" || value === "0")
198
+ return false;
199
+ throw new Error(`Validation failed: "${value}" is not a boolean`);
200
+ }
201
+ }
202
+ /**
203
+ * ParseArrayPipe - Transforms string to array
204
+ * @example
205
+ * @Get()
206
+ * search(@Query('ids', ParseArrayPipe) ids: string[]) {}
207
+ */
208
+ export class ParseArrayPipe {
209
+ separator;
210
+ constructor(separator = ",") {
211
+ this.separator = separator;
212
+ }
213
+ async transform(value, metadata) {
214
+ if (Array.isArray(value))
215
+ return value;
216
+ if (typeof value === "string") {
217
+ return value.split(this.separator).map((item) => item.trim());
218
+ }
219
+ throw new Error(`Validation failed: "${value}" cannot be parsed to array`);
220
+ }
221
+ }
222
+ /**
223
+ * ParseUUIDPipe - Validates UUID format
224
+ * @example
225
+ * @Get('/:id')
226
+ * findOne(@Param('id', ParseUUIDPipe) id: string) {}
227
+ */
228
+ export class ParseUUIDPipe {
229
+ async transform(value, metadata) {
230
+ const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
231
+ if (!uuidRegex.test(value)) {
232
+ throw new Error(`Validation failed: "${value}" is not a valid UUID`);
233
+ }
234
+ return value;
235
+ }
236
+ }
237
+ /**
238
+ * ParseEnumPipe - Validates enum values
239
+ * @example
240
+ * enum Status { ACTIVE, INACTIVE }
241
+ * @Get()
242
+ * search(@Query('status', new ParseEnumPipe(Status)) status: Status) {}
243
+ */
244
+ export class ParseEnumPipe {
245
+ enumType;
246
+ constructor(enumType) {
247
+ this.enumType = enumType;
248
+ }
249
+ async transform(value, metadata) {
250
+ const enumValues = Object.values(this.enumType);
251
+ if (!enumValues.includes(value)) {
252
+ throw new Error(`Validation failed: "${value}" is not a valid enum value. Valid values: ${enumValues.join(", ")}`);
253
+ }
254
+ return value;
255
+ }
256
+ }
257
+ /**
258
+ * DefaultValuePipe - Provides default value if undefined
259
+ * @example
260
+ * @Get()
261
+ * search(@Query('page', new DefaultValuePipe(1)) page: number) {}
262
+ */
263
+ export class DefaultValuePipe {
264
+ defaultValue;
265
+ constructor(defaultValue) {
266
+ this.defaultValue = defaultValue;
267
+ }
268
+ async transform(value, metadata) {
269
+ if (value === undefined || value === null) {
270
+ return this.defaultValue;
271
+ }
272
+ return value;
273
+ }
274
+ }
275
+ /**
276
+ * TrimPipe - Trims whitespace from strings
277
+ * @example
278
+ * @Post()
279
+ * create(@Body('name', TrimPipe) name: string) {}
280
+ */
281
+ export class TrimPipe {
282
+ async transform(value, metadata) {
283
+ if (typeof value !== "string") {
284
+ return value;
285
+ }
286
+ return value.trim();
287
+ }
288
+ }
289
+ /**
290
+ * FormatErrorPipe - Formats validation errors as { [field]: [messages] }
291
+ * Like NestJS format with field names as keys
292
+ *
293
+ * @example
294
+ * // In index.ts or controller
295
+ * app.useGlobalPipes(new FormatErrorPipe());
296
+ *
297
+ * @example
298
+ * // Response format:
299
+ * {
300
+ * statusCode: 400,
301
+ * message: "Validation failed",
302
+ * errors: {
303
+ * email: ["Property 'email' should be email"],
304
+ * mobile: ["Expected string to match '^[6-9]{1}[0-9]{9}$'"]
305
+ * }
306
+ * }
307
+ */
308
+ export class FormatErrorPipe extends ValidationPipe {
309
+ constructor() {
310
+ super({
311
+ whitelist: true,
312
+ forbidNonWhitelisted: true,
313
+ exceptionFactory: (error) => {
314
+ const formattedErrors = {};
315
+ if (error.errors && error.errors.length > 0) {
316
+ error.errors.forEach((err) => {
317
+ const field = err.path?.replace(/^\//, "") || "unknown";
318
+ if (!formattedErrors[field]) {
319
+ formattedErrors[field] = [];
320
+ }
321
+ formattedErrors[field].push(err.summary || err.message);
322
+ });
323
+ }
324
+ else {
325
+ const field = error.property?.replace(/^\//, "") || "unknown";
326
+ formattedErrors[field] = [error.summary || error.message];
327
+ }
328
+ return {
329
+ statusCode: 400,
330
+ message: "Validation failed",
331
+ errors: formattedErrors,
332
+ };
333
+ },
334
+ });
335
+ }
336
+ }
337
+ /**
338
+ * SimpleErrorPipe - Returns flat array of error messages
339
+ *
340
+ * @example
341
+ * app.useGlobalPipes(new SimpleErrorPipe());
342
+ *
343
+ * @example
344
+ * // Response format:
345
+ * {
346
+ * statusCode: 400,
347
+ * message: "Property 'email' should be email, Invalid mobile number",
348
+ * errors: [
349
+ * "Property 'email' should be email",
350
+ * "Invalid mobile number"
351
+ * ]
352
+ * }
353
+ */
354
+ export class SimpleErrorPipe extends ValidationPipe {
355
+ constructor() {
356
+ super({
357
+ exceptionFactory: (error) => {
358
+ const messages = [];
359
+ if (error.errors && error.errors.length > 0) {
360
+ error.errors.forEach((err) => {
361
+ messages.push(err.summary || err.message);
362
+ });
363
+ }
364
+ else {
365
+ messages.push(error.summary || error.message);
366
+ }
367
+ return {
368
+ statusCode: 400,
369
+ message: messages.join(", "),
370
+ errors: messages,
371
+ };
372
+ },
373
+ });
374
+ }
375
+ }
376
+ /**
377
+ * DetailedErrorPipe - Returns detailed information for each field
378
+ *
379
+ * @example
380
+ * app.useGlobalPipes(new DetailedErrorPipe());
381
+ *
382
+ * @example
383
+ * // Response format:
384
+ * {
385
+ * statusCode: 400,
386
+ * message: "Validation failed",
387
+ * errors: [{
388
+ * field: "email",
389
+ * message: "Property 'email' should be email",
390
+ * value: "demo@demo.",
391
+ * expected: "string (format: email)"
392
+ * }]
393
+ * }
394
+ */
395
+ export class DetailedErrorPipe extends ValidationPipe {
396
+ constructor() {
397
+ super({
398
+ exceptionFactory: (error) => {
399
+ const errors = [];
400
+ if (error.errors && error.errors.length > 0) {
401
+ error.errors.forEach((err) => {
402
+ errors.push({
403
+ field: err.path?.replace(/^\//, "") || "unknown",
404
+ message: err.summary || err.message,
405
+ value: err.value,
406
+ expected: err.schema?.format
407
+ ? `${err.schema.type} (format: ${err.schema.format})`
408
+ : err.schema?.type,
409
+ });
410
+ });
411
+ }
412
+ return {
413
+ statusCode: 400,
414
+ message: "Validation failed",
415
+ errors,
416
+ };
417
+ },
418
+ });
419
+ }
420
+ }