wynkjs 1.0.3 → 1.0.6

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 (63) hide show
  1. package/README.md +528 -348
  2. package/dist/cors.d.ts +28 -0
  3. package/dist/cors.d.ts.map +1 -0
  4. package/dist/cors.js +121 -0
  5. package/dist/database.d.ts +1 -1
  6. package/dist/database.js +1 -1
  7. package/dist/decorators/exception.advanced.d.ts +286 -18
  8. package/dist/decorators/exception.advanced.d.ts.map +1 -1
  9. package/dist/decorators/exception.advanced.js +410 -17
  10. package/dist/decorators/exception.decorators.d.ts +93 -2
  11. package/dist/decorators/exception.decorators.d.ts.map +1 -1
  12. package/dist/decorators/exception.decorators.js +140 -8
  13. package/dist/decorators/formatter.decorators.d.ts +93 -0
  14. package/dist/decorators/formatter.decorators.d.ts.map +1 -0
  15. package/dist/decorators/formatter.decorators.js +131 -0
  16. package/dist/decorators/guard.decorators.d.ts +2 -2
  17. package/dist/decorators/guard.decorators.d.ts.map +1 -1
  18. package/dist/decorators/guard.decorators.js +11 -5
  19. package/dist/decorators/http.decorators.d.ts +10 -4
  20. package/dist/decorators/http.decorators.d.ts.map +1 -1
  21. package/dist/decorators/http.decorators.js +9 -2
  22. package/dist/decorators/interceptor.advanced.d.ts +9 -9
  23. package/dist/decorators/interceptor.advanced.d.ts.map +1 -1
  24. package/dist/decorators/interceptor.advanced.js +7 -7
  25. package/dist/decorators/interceptor.decorators.d.ts +9 -7
  26. package/dist/decorators/interceptor.decorators.d.ts.map +1 -1
  27. package/dist/decorators/interceptor.decorators.js +29 -18
  28. package/dist/decorators/param.decorators.d.ts +2 -2
  29. package/dist/decorators/param.decorators.js +1 -1
  30. package/dist/decorators/pipe.decorators.d.ts +4 -4
  31. package/dist/decorators/pipe.decorators.d.ts.map +1 -1
  32. package/dist/decorators/pipe.decorators.js +4 -4
  33. package/dist/dto.js +1 -1
  34. package/dist/factory.d.ts +30 -2
  35. package/dist/factory.d.ts.map +1 -1
  36. package/dist/factory.js +210 -186
  37. package/dist/filters/exception.filters.d.ts +124 -0
  38. package/dist/filters/exception.filters.d.ts.map +1 -0
  39. package/dist/filters/exception.filters.js +208 -0
  40. package/dist/global-prefix.d.ts +49 -0
  41. package/dist/global-prefix.d.ts.map +1 -0
  42. package/dist/global-prefix.js +155 -0
  43. package/dist/index.d.ts +7 -1
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +6 -1
  46. package/dist/interfaces/interceptor.interface.d.ts +15 -0
  47. package/dist/interfaces/interceptor.interface.d.ts.map +1 -0
  48. package/dist/interfaces/interceptor.interface.js +1 -0
  49. package/dist/optimized-handler.d.ts +31 -0
  50. package/dist/optimized-handler.d.ts.map +1 -0
  51. package/dist/optimized-handler.js +180 -0
  52. package/dist/pipes/validation.pipe.d.ts +12 -12
  53. package/dist/pipes/validation.pipe.d.ts.map +1 -1
  54. package/dist/pipes/validation.pipe.js +43 -15
  55. package/dist/schema-registry.d.ts +51 -0
  56. package/dist/schema-registry.d.ts.map +1 -0
  57. package/dist/schema-registry.js +134 -0
  58. package/dist/testing/index.d.ts +2 -2
  59. package/dist/testing/index.js +2 -2
  60. package/dist/ultra-optimized-handler.d.ts +51 -0
  61. package/dist/ultra-optimized-handler.d.ts.map +1 -0
  62. package/dist/ultra-optimized-handler.js +302 -0
  63. package/package.json +17 -10
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Optimized Handler Builder for WynkJS
3
+ * Builds route handlers conditionally - only adds overhead when features are actually used
4
+ */
5
+ import { createExecutionContext, executeGuards, } from "./decorators/guard.decorators";
6
+ import { executeInterceptors } from "./decorators/interceptor.decorators";
7
+ import { executePipes } from "./decorators/pipe.decorators";
8
+ import { executeExceptionFilters, HttpException, } from "./decorators/exception.decorators";
9
+ /**
10
+ * Build optimized handler - only include overhead when features are used
11
+ * This eliminates conditional checks on every request for unused features
12
+ */
13
+ export function buildOptimizedHandler(options) {
14
+ const { instance, methodName, ControllerClass, params, allGuards, allInterceptors, allPipes, allFilters, httpCode, headers, redirect, } = options;
15
+ // Pre-sort params once during registration (not on every request)
16
+ if (params.length > 0) {
17
+ params.sort((a, b) => a.index - b.index);
18
+ }
19
+ // Base parameter extractor and method executor
20
+ const extractParamsAndExecute = async (ctx) => {
21
+ if (params.length === 0) {
22
+ // No parameter decorators - pass full context
23
+ return await instance[methodName](ctx);
24
+ }
25
+ // Build arguments array
26
+ const args = new Array(params.length);
27
+ for (const param of params) {
28
+ let value;
29
+ // Extract value based on type (optimized switch)
30
+ switch (param.type) {
31
+ case "body":
32
+ value = param.data ? ctx.body?.[param.data] : ctx.body;
33
+ break;
34
+ case "param":
35
+ value = param.data ? ctx.params?.[param.data] : ctx.params;
36
+ break;
37
+ case "query":
38
+ value = param.data ? ctx.query?.[param.data] : ctx.query;
39
+ break;
40
+ case "headers":
41
+ value = param.data
42
+ ? ctx.headers?.get?.(param.data) ||
43
+ ctx.request?.headers?.get?.(param.data)
44
+ : ctx.headers || ctx.request?.headers;
45
+ break;
46
+ case "request":
47
+ value = ctx.request || ctx;
48
+ break;
49
+ case "response":
50
+ value = ctx.set || ctx.response;
51
+ break;
52
+ case "context":
53
+ if (param.data) {
54
+ const keys = param.data.split(".");
55
+ value = keys.reduce((obj, key) => obj?.[key], ctx);
56
+ }
57
+ else {
58
+ value = ctx;
59
+ }
60
+ break;
61
+ case "user":
62
+ value = param.data ? ctx.user?.[param.data] : ctx.user;
63
+ break;
64
+ case "file":
65
+ value = ctx.body?.file || ctx.file;
66
+ break;
67
+ case "files":
68
+ value = ctx.body?.files || ctx.files;
69
+ break;
70
+ }
71
+ // Apply parameter-specific pipes
72
+ if (param.pipes && param.pipes.length > 0) {
73
+ const metadata = {
74
+ type: param.type,
75
+ data: param.data,
76
+ };
77
+ value = await executePipes(param.pipes, value, metadata);
78
+ }
79
+ // Apply global/controller/method pipes
80
+ if (allPipes.length > 0) {
81
+ const metadata = {
82
+ type: param.type,
83
+ data: param.data,
84
+ };
85
+ value = await executePipes(allPipes, value, metadata);
86
+ }
87
+ args[param.index] = value;
88
+ }
89
+ return await instance[methodName].apply(instance, args);
90
+ };
91
+ // Build handler layers conditionally
92
+ let handler = extractParamsAndExecute;
93
+ // Wrap with interceptors (only if they exist)
94
+ if (allInterceptors.length > 0) {
95
+ const prevHandler = handler;
96
+ handler = async (ctx) => {
97
+ const executionContext = createExecutionContext(ctx, instance[methodName], ControllerClass);
98
+ return await executeInterceptors(allInterceptors, executionContext, () => prevHandler(ctx));
99
+ };
100
+ }
101
+ // Wrap with guards (only if they exist)
102
+ if (allGuards.length > 0) {
103
+ const prevHandler = handler;
104
+ handler = async (ctx) => {
105
+ const executionContext = createExecutionContext(ctx, instance[methodName], ControllerClass);
106
+ const canActivate = await executeGuards(allGuards, executionContext);
107
+ if (!canActivate) {
108
+ throw new HttpException("Forbidden", 403, "Access denied");
109
+ }
110
+ return await prevHandler(ctx);
111
+ };
112
+ }
113
+ // Final handler with response modifications
114
+ const finalHandler = async (ctx) => {
115
+ try {
116
+ const result = await handler(ctx);
117
+ // Handle redirect (if configured)
118
+ if (redirect) {
119
+ ctx.set.redirect = redirect.url;
120
+ ctx.set.status = redirect.statusCode;
121
+ return;
122
+ }
123
+ // Set custom HTTP code (if configured)
124
+ if (httpCode) {
125
+ ctx.set.status = httpCode;
126
+ }
127
+ // Set custom headers (if configured)
128
+ if (headers) {
129
+ Object.entries(headers).forEach(([key, value]) => {
130
+ ctx.set.headers[key] = value;
131
+ });
132
+ }
133
+ return result;
134
+ }
135
+ catch (error) {
136
+ // Execute exception filters (only if they exist)
137
+ if (allFilters.length > 0) {
138
+ const executionContext = createExecutionContext(ctx, instance[methodName], ControllerClass);
139
+ try {
140
+ const result = await executeExceptionFilters(allFilters, error, executionContext);
141
+ if (result) {
142
+ if (result.statusCode) {
143
+ ctx.set.status = result.statusCode;
144
+ }
145
+ return result;
146
+ }
147
+ }
148
+ catch (filterError) {
149
+ error = filterError;
150
+ }
151
+ }
152
+ // Default error handling
153
+ if (error instanceof HttpException) {
154
+ ctx.set.status = error.getStatus();
155
+ return error.getResponse();
156
+ }
157
+ ctx.set.status = 500;
158
+ return {
159
+ statusCode: 500,
160
+ message: error.message || "Internal server error",
161
+ error: "Internal Server Error",
162
+ };
163
+ }
164
+ };
165
+ return finalHandler;
166
+ }
167
+ /**
168
+ * Build middleware chain - only if middleware exists
169
+ */
170
+ export function buildMiddlewareChain(handler, middlewares) {
171
+ if (middlewares.length === 0) {
172
+ return handler;
173
+ }
174
+ // Build middleware chain (removed console.log for performance)
175
+ return middlewares.reduceRight((next, middleware) => {
176
+ return async (ctx) => {
177
+ return await middleware(ctx, () => next(ctx));
178
+ };
179
+ }, handler);
180
+ }
@@ -15,9 +15,9 @@ export interface ArgumentMetadata {
15
15
  data?: string;
16
16
  }
17
17
  /**
18
- * Validation Error structure (Elysia format)
18
+ * Validation Error structure (WynkJS format)
19
19
  */
20
- export interface ElysiaValidationError {
20
+ export interface WynkJSValidationError {
21
21
  type: "validation";
22
22
  on: "body" | "params" | "query" | "headers";
23
23
  property: string;
@@ -36,39 +36,39 @@ export interface ElysiaValidationError {
36
36
  }
37
37
  /**
38
38
  * Base Validation Pipe
39
- * Handles Elysia validation errors and formats them
39
+ * Handles WynkJS validation errors and formats them
40
40
  */
41
41
  export declare class ValidationPipe implements ValidationPipeTransform {
42
42
  protected options: {
43
- exceptionFactory?: (errors: ElysiaValidationError) => any;
43
+ exceptionFactory?: (errors: WynkJSValidationError) => any;
44
44
  transform?: boolean;
45
45
  whitelist?: boolean;
46
46
  };
47
47
  constructor(options?: {
48
- exceptionFactory?: (errors: ElysiaValidationError) => any;
48
+ exceptionFactory?: (errors: WynkJSValidationError) => any;
49
49
  transform?: boolean;
50
50
  whitelist?: boolean;
51
51
  });
52
52
  /**
53
- * Transform method (not used for Elysia validation, but required by interface)
53
+ * Transform method (not used for WynkJS validation, but required by interface)
54
54
  */
55
55
  transform(value: any, metadata: ArgumentMetadata): any;
56
56
  /**
57
- * Format Elysia validation error
57
+ * Format WynkJS validation error
58
58
  * This is called by the exception filter
59
59
  */
60
60
  formatError(exception: any): any;
61
61
  /**
62
- * Parse Elysia validation error from exception
62
+ * Parse WynkJS validation error from exception
63
63
  */
64
- protected parseValidationError(exception: any): ElysiaValidationError;
64
+ protected parseValidationError(exception: any): WynkJSValidationError;
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: WynkJSValidationError, 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,7 +1,8 @@
1
1
  import "reflect-metadata";
2
+ import { schemaRegistry } from "../schema-registry";
2
3
  /**
3
4
  * Base Validation Pipe
4
- * Handles Elysia validation errors and formats them
5
+ * Handles WynkJS validation errors and formats them
5
6
  */
6
7
  export class ValidationPipe {
7
8
  options;
@@ -9,13 +10,13 @@ export class ValidationPipe {
9
10
  this.options = options || {};
10
11
  }
11
12
  /**
12
- * Transform method (not used for Elysia validation, but required by interface)
13
+ * Transform method (not used for WynkJS validation, but required by interface)
13
14
  */
14
15
  transform(value, metadata) {
15
16
  return value;
16
17
  }
17
18
  /**
18
- * Format Elysia validation error
19
+ * Format WynkJS validation error
19
20
  * This is called by the exception filter
20
21
  */
21
22
  formatError(exception) {
@@ -29,7 +30,7 @@ export class ValidationPipe {
29
30
  return this.defaultFormatError(validationError);
30
31
  }
31
32
  /**
32
- * Parse Elysia validation error from exception
33
+ * Parse WynkJS validation error from exception
33
34
  */
34
35
  parseValidationError(exception) {
35
36
  let validationData;
@@ -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"}
@@ -0,0 +1,134 @@
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
+ class SchemaRegistry {
6
+ static instance;
7
+ errorMessages = {};
8
+ routeSchemas = {};
9
+ constructor() { }
10
+ static getInstance() {
11
+ if (!SchemaRegistry.instance) {
12
+ SchemaRegistry.instance = new SchemaRegistry();
13
+ }
14
+ return SchemaRegistry.instance;
15
+ }
16
+ /**
17
+ * Register custom error messages for a schema
18
+ * @param schemaKey Unique key for the schema (e.g., class name + method)
19
+ * @param schema The TypeBox schema object
20
+ */
21
+ registerSchema(schemaKey, schema) {
22
+ if (!schema || typeof schema !== "object")
23
+ return;
24
+ const messages = {};
25
+ this.extractErrorMessages(schema, "", messages);
26
+ if (Object.keys(messages).length > 0) {
27
+ this.errorMessages[schemaKey] = messages;
28
+ }
29
+ }
30
+ /**
31
+ * Register route-to-schema mapping
32
+ * @param method HTTP method (GET, POST, etc.)
33
+ * @param path Route path
34
+ * @param schemaKey The schema key
35
+ * @param validationType Type of validation (body, query, params)
36
+ */
37
+ registerRoute(method, path, schemaKey, validationType) {
38
+ const routeKey = `${method.toUpperCase()}:${path}`;
39
+ if (!this.routeSchemas[routeKey]) {
40
+ this.routeSchemas[routeKey] = [];
41
+ }
42
+ this.routeSchemas[routeKey].push({ schemaKey, validationType });
43
+ }
44
+ /**
45
+ * Get schema key for a route and validation type
46
+ * @param method HTTP method
47
+ * @param path Route path (actual request path with values)
48
+ * @param validationType Type of validation
49
+ * @returns Schema key or undefined
50
+ */
51
+ getSchemaKeyForRoute(method, path, validationType) {
52
+ // First try exact match
53
+ const exactKey = `${method.toUpperCase()}:${path}`;
54
+ if (this.routeSchemas[exactKey]) {
55
+ const found = this.routeSchemas[exactKey].find((s) => s.validationType === validationType);
56
+ if (found)
57
+ return found.schemaKey;
58
+ }
59
+ // If no exact match, try to match patterns
60
+ // e.g., request path "/users/10/100" should match pattern "/users/:id1/:id2"
61
+ for (const [routeKey, schemas] of Object.entries(this.routeSchemas)) {
62
+ // Split only on the first colon to separate method from path
63
+ const colonIndex = routeKey.indexOf(":");
64
+ const routeMethod = routeKey.substring(0, colonIndex);
65
+ const routePath = routeKey.substring(colonIndex + 1);
66
+ if (routeMethod !== method.toUpperCase())
67
+ continue;
68
+ // Check if the routePath is a pattern (contains :param)
69
+ if (routePath.includes(":")) {
70
+ // Convert pattern to regex
71
+ const pattern = routePath
72
+ .replace(/:[^/]+/g, "([^/]+)") // Replace :param with regex group
73
+ .replace(/\//g, "\\/"); // Escape slashes
74
+ const regex = new RegExp(`^${pattern}$`);
75
+ if (regex.test(path)) {
76
+ const found = schemas.find((s) => s.validationType === validationType);
77
+ if (found)
78
+ return found.schemaKey;
79
+ }
80
+ }
81
+ }
82
+ return undefined;
83
+ }
84
+ /**
85
+ * Recursively extract error messages from schema
86
+ */
87
+ extractErrorMessages(schema, path, messages) {
88
+ // Check for custom error message at current level
89
+ if (schema.error || schema.errorMessage) {
90
+ messages[path || "root"] = schema.error || schema.errorMessage;
91
+ }
92
+ // Recurse into object properties
93
+ if (schema.type === "object" && schema.properties) {
94
+ for (const [key, value] of Object.entries(schema.properties)) {
95
+ const newPath = path ? `${path}.${key}` : key;
96
+ this.extractErrorMessages(value, newPath, messages);
97
+ }
98
+ }
99
+ // Recurse into array items
100
+ if (schema.type === "array" && schema.items) {
101
+ const newPath = path ? `${path}[]` : "[]";
102
+ this.extractErrorMessages(schema.items, newPath, messages);
103
+ }
104
+ }
105
+ /**
106
+ * Get custom error message for a field path
107
+ * @param schemaKey The schema key
108
+ * @param fieldPath The field path (e.g., "user.email")
109
+ * @returns Custom error message or undefined
110
+ */
111
+ getErrorMessage(schemaKey, fieldPath) {
112
+ const schemaMessages = this.errorMessages[schemaKey];
113
+ if (!schemaMessages)
114
+ return undefined;
115
+ // Try exact match first
116
+ if (schemaMessages[fieldPath]) {
117
+ return schemaMessages[fieldPath];
118
+ }
119
+ // Try without array indices (e.g., "items.0.name" -> "items[].name")
120
+ const normalizedPath = fieldPath.replace(/\.\d+\./g, "[].");
121
+ if (schemaMessages[normalizedPath]) {
122
+ return schemaMessages[normalizedPath];
123
+ }
124
+ return undefined;
125
+ }
126
+ /**
127
+ * Clear all registered schemas (useful for testing)
128
+ */
129
+ clear() {
130
+ this.errorMessages = {};
131
+ this.routeSchemas = {};
132
+ }
133
+ }
134
+ export const schemaRegistry = SchemaRegistry.getInstance();
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * WynkJS Testing Module
3
- * Provides utilities for testing WynkJS applications
4
- * Similar to @nestjs/testing
3
+ * Built-in testing utilities for WynkJS applications
4
+ * Works with Bun's native test runner
5
5
  */
6
6
  /**
7
7
  * Test class for creating isolated testing modules
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * WynkJS Testing Module
3
- * Provides utilities for testing WynkJS applications
4
- * Similar to @nestjs/testing
3
+ * Built-in testing utilities for WynkJS applications
4
+ * Works with Bun's native test runner
5
5
  */
6
6
  import { container } from "tsyringe";
7
7
  /**
@@ -0,0 +1,51 @@
1
+ /**
2
+ * ULTRA-OPTIMIZED Handler Builder for WynkJS
3
+ *
4
+ * KEY OPTIMIZATION: Eliminate nested async/await and IIFEs
5
+ *
6
+ * Performance findings:
7
+ * - Direct sync: 32ms per 1M calls
8
+ * - Single async: 42ms per 1M calls
9
+ * - Nested async + IIFE: 232ms per 1M calls (5.7x SLOWER!)
10
+ *
11
+ * Root cause: The current pattern creates an IIFE for every request:
12
+ * return (async () => { try { ... } catch { ... } })();
13
+ *
14
+ * This creates massive overhead because:
15
+ * 1. Creates a new Promise on EVERY request
16
+ * 2. Wraps it in a try-catch block
17
+ * 3. Immediately invokes it (IIFE pattern)
18
+ * 4. Then awaits the result
19
+ *
20
+ * Solution: Build a SINGLE async function at registration time,
21
+ * not nested functions that get called on every request.
22
+ */
23
+ import { ParamMetadata } from "./decorators/param.decorators";
24
+ export interface HandlerBuildOptions {
25
+ instance: any;
26
+ methodName: string;
27
+ ControllerClass: any;
28
+ params: ParamMetadata[];
29
+ allGuards: any[];
30
+ allInterceptors: any[];
31
+ allPipes: any[];
32
+ allFilters: any[];
33
+ httpCode?: number;
34
+ headers?: Record<string, string>;
35
+ redirect?: {
36
+ url: string;
37
+ statusCode: number;
38
+ };
39
+ routePath: string;
40
+ routeMethod: string;
41
+ }
42
+ /**
43
+ * Ultra-optimized handler builder
44
+ * Builds a SINGLE async function, not nested closures
45
+ */
46
+ export declare function buildUltraOptimizedHandler(options: HandlerBuildOptions): (ctx: any) => Promise<any>;
47
+ /**
48
+ * Build middleware chain - same as before
49
+ */
50
+ export declare function buildMiddlewareChain(handler: (ctx: any) => Promise<any>, middlewares: any[]): (ctx: any) => Promise<any>;
51
+ //# sourceMappingURL=ultra-optimized-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ultra-optimized-handler.d.ts","sourceRoot":"","sources":["../core/ultra-optimized-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAYH,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAE9D,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,GAAG,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,GAAG,CAAC;IACrB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,SAAS,EAAE,GAAG,EAAE,CAAC;IACjB,eAAe,EAAE,GAAG,EAAE,CAAC;IACvB,QAAQ,EAAE,GAAG,EAAE,CAAC;IAChB,UAAU,EAAE,GAAG,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,mBAAmB,GAC3B,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAiU5B;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,EACnC,WAAW,EAAE,GAAG,EAAE,GACjB,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAU5B"}