wynkjs 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,24 +30,25 @@ WynkJS combines the **speed of Elysia** with the **elegant decorator syntax of N
30
30
 
31
31
  - 🚀 **20x Faster** - Built on Elysia, one of the fastest web frameworks
32
32
  - 🎨 **Decorator-Based** - Familiar NestJS-style decorators
33
- - 💉 **Dependency Injection** - Built-in DI with tsyringe
33
+ - 💉 **Dependency Injection** - Built-in DI (no need to import reflect-metadata!)
34
34
  - 🔒 **Type-Safe** - Full TypeScript support
35
35
  - 🎯 **Simple & Clean** - Easy to learn, powerful to use
36
36
  - 🔌 **Middleware Support** - Guards, interceptors, pipes, filters
37
37
  - ⚡ **Hot Reload** - Fast development with Bun
38
+ - 📦 **Single Import** - Everything from `wynkjs` (Injectable, Controller, Get, etc.)
38
39
 
39
40
  ---
40
41
 
41
42
  ## 📦 Installation
42
43
 
43
44
  ```bash
44
- npm install wynkjs elysia reflect-metadata
45
+ npm install wynkjs elysia
45
46
  ```
46
47
 
47
48
  Or with Bun:
48
49
 
49
50
  ```bash
50
- bun add wynkjs elysia reflect-metadata
51
+ bun add wynkjs elysia
51
52
  ```
52
53
 
53
54
  ---
@@ -57,10 +58,9 @@ bun add wynkjs elysia reflect-metadata
57
58
  ### 1. Create Your First Controller
58
59
 
59
60
  ```typescript
60
- import { Controller, Get, Post, Body } from "wynkjs";
61
- import { injectable } from "tsyringe";
61
+ import { Controller, Get, Post, Body, Param, Injectable } from "wynkjs";
62
62
 
63
- @injectable()
63
+ @Injectable()
64
64
  @Controller("/users")
65
65
  export class UserController {
66
66
  @Get("/")
@@ -83,11 +83,10 @@ export class UserController {
83
83
  ### 2. Create Your Application
84
84
 
85
85
  ```typescript
86
- import "reflect-metadata";
87
- import { WynkFramework } from "wynkjs";
86
+ import { WynkFactory } from "wynkjs";
88
87
  import { UserController } from "./controllers/user.controller";
89
88
 
90
- const app = await WynkFramework.create({
89
+ const app = WynkFactory.create({
91
90
  controllers: [UserController],
92
91
  });
93
92
 
@@ -96,6 +95,8 @@ await app.listen(3000);
96
95
  console.log("🚀 Server running on http://localhost:3000");
97
96
  ```
98
97
 
98
+ **Note**: No need to import `reflect-metadata` - WynkJS handles it automatically! ✨
99
+
99
100
  ### 3. Run Your Server
100
101
 
101
102
  ```bash
@@ -211,19 +212,20 @@ export class AdminController {
211
212
  ### 💉 Dependency Injection
212
213
 
213
214
  ```typescript
214
- import { injectable, inject } from "tsyringe";
215
+ // Option 1: Capital-cased (recommended for consistency)
216
+ import { Injectable, Inject, Controller, Get } from "wynkjs";
215
217
 
216
- @injectable()
218
+ @Injectable()
217
219
  export class UserService {
218
220
  async findAll() {
219
221
  return [{ id: 1, name: "Alice" }];
220
222
  }
221
223
  }
222
224
 
223
- @injectable()
225
+ @Injectable()
224
226
  @Controller("/users")
225
227
  export class UserController {
226
- constructor(@inject(UserService) private userService: UserService) {}
228
+ constructor(private userService: UserService) {}
227
229
 
228
230
  @Get("/")
229
231
  async list() {
@@ -233,6 +235,22 @@ export class UserController {
233
235
  }
234
236
  ```
235
237
 
238
+ ```typescript
239
+ // Option 2: Lowercase (tsyringe convention)
240
+ import { Injectable, Inject, Controller, Get } from "wynkjs";
241
+
242
+ @Injectable()
243
+ export class UserService {
244
+ async findAll() {
245
+ return [{ id: 1, name: "Alice" }];
246
+ }
247
+ }
248
+ ```
249
+
250
+ **Available DI decorators**:
251
+
252
+ - Capital: `Injectable`, `Inject`, `Singleton`, `AutoInjectable`, `Container`
253
+
236
254
  ### 🗃️ Database Integration (Drizzle ORM)
237
255
 
238
256
  ```typescript
@@ -250,7 +268,7 @@ const userTable = pgTable("users", {
250
268
  // Register tables
251
269
  registerTables({ userTable });
252
270
 
253
- @injectable()
271
+ @Injectable()
254
272
  export class UserService {
255
273
  private db = drizzle(process.env.DATABASE_URL);
256
274
 
@@ -264,14 +282,16 @@ export class UserService {
264
282
 
265
283
  ### 📝 Request Validation
266
284
 
285
+ WynkJS provides automatic request validation with customizable error formats:
286
+
267
287
  ```typescript
268
- import { Post, Body, UsePipes } from "wynkjs";
269
- import { t } from "elysia";
288
+ import { Post, Body, DTO, WynkFactory, FormatErrorFormatter } from "wynkjs";
270
289
 
271
- const CreateUserDTO = t.Object({
272
- name: t.String({ minLength: 2 }),
273
- email: t.String({ format: "email" }),
274
- password: t.String({ minLength: 8 }),
290
+ // Define your DTO with validation rules
291
+ const CreateUserDTO = DTO.Strict({
292
+ name: DTO.String({ minLength: 2 }),
293
+ email: DTO.String({ format: "email", minLength: 5 }),
294
+ age: DTO.Number({ minimum: 18 }),
275
295
  });
276
296
 
277
297
  @Controller("/users")
@@ -285,8 +305,21 @@ export class UserController {
285
305
  return { message: "User created", data: body };
286
306
  }
287
307
  }
308
+
309
+ // Choose your validation error format
310
+ const app = WynkFactory.create({
311
+ controllers: [UserController],
312
+ // Option 1: Default format (recommended)
313
+ // validationErrorFormatter: new FormatErrorFormatter(), // NestJS-style
314
+ // Option 2: Simple array format
315
+ // validationErrorFormatter: new SimpleErrorFormatter(),
316
+ // Option 3: Detailed format with field info
317
+ // validationErrorFormatter: new DetailedErrorFormatter(),
318
+ });
288
319
  ```
289
320
 
321
+ **See [VALIDATION_FORMATTERS.md](./docs/VALIDATION_FORMATTERS.md) for all available error formats**
322
+
290
323
  ### 🔄 Multiple Middleware
291
324
 
292
325
  ```typescript
@@ -357,16 +390,26 @@ console.log("🚀 Server running on http://localhost:3000");
357
390
 
358
391
  ```typescript
359
392
  // controllers/user.controller.ts
360
- import { Controller, Get, Post, Put, Delete, Body, Param, Use } from "wynkjs";
361
- import { injectable, inject } from "tsyringe";
393
+ import {
394
+ Injectable,
395
+ Inject,
396
+ Controller,
397
+ Get,
398
+ Post,
399
+ Put,
400
+ Delete,
401
+ Body,
402
+ Param,
403
+ Use,
404
+ } from "wynkjs";
362
405
  import { UserService } from "../services/user.service";
363
406
  import { jwtGuard } from "../middleware/jwt.guard";
364
407
 
365
- @injectable()
408
+ @Injectable()
366
409
  @Controller("/users")
367
410
  @Use(jwtGuard)
368
411
  export class UserController {
369
- constructor(@inject(UserService) private userService: UserService) {}
412
+ constructor(@Inject(UserService) private userService: UserService) {}
370
413
 
371
414
  @Get("/")
372
415
  async list() {
@@ -402,9 +445,8 @@ export class UserController {
402
445
 
403
446
  ```typescript
404
447
  // services/user.service.ts
405
- import { injectable } from "tsyringe";
406
448
 
407
- @injectable()
449
+ @Injectable()
408
450
  export class UserService {
409
451
  private users = [
410
452
  { id: "1", name: "Alice", email: "alice@example.com" },
@@ -1,27 +1,56 @@
1
1
  import "reflect-metadata";
2
2
  import { WynkExceptionFilter } from "./exception.decorators";
3
3
  import { ExecutionContext } from "./guard.decorators";
4
- import { NotFoundException, BadRequestException, UnauthorizedException, ForbiddenException } from "./exception.decorators";
4
+ import { NotFoundException, UnauthorizedException, ForbiddenException } from "./exception.decorators";
5
5
  /**
6
6
  * Advanced Exception Filters for WynkJS Framework
7
7
  * Specialized filters for different error scenarios
8
8
  */
9
9
  /**
10
- * Validation Exception Filter - Handles validation errors
10
+ * Error Formatter Interface
11
+ * Used by ValidationExceptionFilter to format validation errors
12
+ */
13
+ export interface ErrorFormatter {
14
+ format(validationError: any): any;
15
+ }
16
+ /**
17
+ * FormatErrorFormatter - Formats as { field: [messages] } like NestJS
18
+ */
19
+ export declare class FormatErrorFormatter implements ErrorFormatter {
20
+ format(error: any): any;
21
+ }
22
+ /**
23
+ * SimpleErrorFormatter - Formats as simple array of messages
24
+ */
25
+ export declare class SimpleErrorFormatter implements ErrorFormatter {
26
+ format(error: any): any;
27
+ }
28
+ /**
29
+ * DetailedErrorFormatter - Formats with detailed field info
30
+ */
31
+ export declare class DetailedErrorFormatter implements ErrorFormatter {
32
+ format(error: any): any;
33
+ }
34
+ /**
35
+ * Validation Exception Filter - Handles validation errors with customizable formatting
11
36
  * @example
12
- * @UseFilters(ValidationExceptionFilter)
13
- * @Post()
14
- * async create(@Body() data: any) {}
37
+ * // With FormatErrorFormatter (NestJS-style)
38
+ * app.useGlobalFilters(new ValidationExceptionFilter(new FormatErrorFormatter()));
39
+ *
40
+ * // With SimpleErrorFormatter
41
+ * app.useGlobalFilters(new ValidationExceptionFilter(new SimpleErrorFormatter()));
42
+ *
43
+ * // With DetailedErrorFormatter
44
+ * app.useGlobalFilters(new ValidationExceptionFilter(new DetailedErrorFormatter()));
45
+ *
46
+ * // Without formatter (default detailed format)
47
+ * app.useGlobalFilters(new ValidationExceptionFilter());
15
48
  */
16
- export declare class ValidationExceptionFilter implements WynkExceptionFilter<BadRequestException> {
17
- catch(exception: BadRequestException, context: ExecutionContext): {
18
- statusCode: number;
19
- error: string;
20
- message: string;
21
- errors: any;
22
- timestamp: string;
23
- path: any;
24
- };
49
+ export declare class ValidationExceptionFilter implements WynkExceptionFilter {
50
+ private formatter;
51
+ constructor(formatter?: ErrorFormatter);
52
+ catch(exception: any, context: ExecutionContext): any;
53
+ private isValidationError;
25
54
  }
26
55
  /**
27
56
  * Database Exception Filter - Handles database errors
@@ -1 +1 @@
1
- {"version":3,"file":"exception.advanced.d.ts","sourceRoot":"","sources":["../../core/decorators/exception.advanced.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAEL,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,kBAAkB,EAEnB,MAAM,wBAAwB,CAAC;AAEhC;;;GAGG;AAEH;;;;;;GAMG;AACH,qBAAa,yBACX,YAAW,mBAAmB,CAAC,mBAAmB,CAAC;IAEnD,KAAK,CAAC,SAAS,EAAE,mBAAmB,EAAE,OAAO,EAAE,gBAAgB;;;;;;;;CAahE;AAED;;;;;;GAMG;AACH,qBAAa,uBAAwB,YAAW,mBAAmB;IACjE,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB;;;;;;;CAiChD;AAED;;;;;;GAMG;AACH,qBAAa,6BACX,YAAW,mBAAmB,CAAC,qBAAqB,CAAC;IAErD,KAAK,CAAC,SAAS,EAAE,qBAAqB,EAAE,OAAO,EAAE,gBAAgB;;;;;;;;CAalE;AAED;;;;;;GAMG;AACH,qBAAa,4BACX,YAAW,mBAAmB,CAAC,kBAAkB,CAAC;IAElD,KAAK,CAAC,SAAS,EAAE,kBAAkB,EAAE,OAAO,EAAE,gBAAgB;;;;;;;;CAe/D;AAED;;;;;;GAMG;AACH,qBAAa,uBACX,YAAW,mBAAmB,CAAC,iBAAiB,CAAC;IAEjD,KAAK,CAAC,SAAS,EAAE,iBAAiB,EAAE,OAAO,EAAE,gBAAgB;;;;;;;;CAa9D;AAED;;;;;;GAMG;AACH,qBAAa,wBAAyB,YAAW,mBAAmB;IAClE,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB;;;;;;;;;CAchD;AAED;;;;;;GAMG;AACH,qBAAa,4BAA6B,YAAW,mBAAmB;IACtE,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB;;;;;;;;;CAchD;AAED;;;;;;GAMG;AACH,qBAAa,yBAA0B,YAAW,mBAAmB;IACnE,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB;;;;;;;CAsBhD;AAED;;;;GAIG;AACH,qBAAa,qBAAsB,YAAW,mBAAmB;IAC/D,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB;;;;;;;;CA2BhD"}
1
+ {"version":3,"file":"exception.advanced.d.ts","sourceRoot":"","sources":["../../core/decorators/exception.advanced.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAEL,iBAAiB,EAEjB,qBAAqB,EACrB,kBAAkB,EAEnB,MAAM,wBAAwB,CAAC;AAEhC;;;GAGG;AAEH;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,eAAe,EAAE,GAAG,GAAG,GAAG,CAAC;CACnC;AAED;;GAEG;AACH,qBAAa,oBAAqB,YAAW,cAAc;IACzD,MAAM,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG;CAsBxB;AAED;;GAEG;AACH,qBAAa,oBAAqB,YAAW,cAAc;IACzD,MAAM,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG;CAiBxB;AAED;;GAEG;AACH,qBAAa,sBAAuB,YAAW,cAAc;IAC3D,MAAM,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG;CAgCxB;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,yBAA0B,YAAW,mBAAmB;IACnE,OAAO,CAAC,SAAS,CAA+B;gBAEpC,SAAS,CAAC,EAAE,cAAc;IAItC,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB;IAgC/C,OAAO,CAAC,iBAAiB;CAiB1B;AAED;;;;;;GAMG;AACH,qBAAa,uBAAwB,YAAW,mBAAmB;IACjE,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB;;;;;;;CAiChD;AAED;;;;;;GAMG;AACH,qBAAa,6BACX,YAAW,mBAAmB,CAAC,qBAAqB,CAAC;IAErD,KAAK,CAAC,SAAS,EAAE,qBAAqB,EAAE,OAAO,EAAE,gBAAgB;;;;;;;;CAalE;AAED;;;;;;GAMG;AACH,qBAAa,4BACX,YAAW,mBAAmB,CAAC,kBAAkB,CAAC;IAElD,KAAK,CAAC,SAAS,EAAE,kBAAkB,EAAE,OAAO,EAAE,gBAAgB;;;;;;;;CAe/D;AAED;;;;;;GAMG;AACH,qBAAa,uBACX,YAAW,mBAAmB,CAAC,iBAAiB,CAAC;IAEjD,KAAK,CAAC,SAAS,EAAE,iBAAiB,EAAE,OAAO,EAAE,gBAAgB;;;;;;;;CAa9D;AAED;;;;;;GAMG;AACH,qBAAa,wBAAyB,YAAW,mBAAmB;IAClE,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB;;;;;;;;;CAchD;AAED;;;;;;GAMG;AACH,qBAAa,4BAA6B,YAAW,mBAAmB;IACtE,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB;;;;;;;;;CAchD;AAED;;;;;;GAMG;AACH,qBAAa,yBAA0B,YAAW,mBAAmB;IACnE,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB;;;;;;;CAsBhD;AAED;;;;GAIG;AACH,qBAAa,qBAAsB,YAAW,mBAAmB;IAC/D,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB;;;;;;;;CA2BhD"}
@@ -1,27 +1,144 @@
1
1
  import "reflect-metadata";
2
2
  /**
3
- * Advanced Exception Filters for WynkJS Framework
4
- * Specialized filters for different error scenarios
3
+ * FormatErrorFormatter - Formats as { field: [messages] } like NestJS
5
4
  */
5
+ export class FormatErrorFormatter {
6
+ format(error) {
7
+ const formattedErrors = {};
8
+ if (error.errors && error.errors.length > 0) {
9
+ error.errors.forEach((err) => {
10
+ const field = err.path?.replace(/^\//, "") || "unknown";
11
+ if (!formattedErrors[field]) {
12
+ formattedErrors[field] = [];
13
+ }
14
+ formattedErrors[field].push(err.summary || err.message);
15
+ });
16
+ }
17
+ else {
18
+ const field = error.property?.replace(/^\//, "") || "unknown";
19
+ formattedErrors[field] = [error.summary || error.message];
20
+ }
21
+ return {
22
+ statusCode: 400,
23
+ message: "Validation failed",
24
+ errors: formattedErrors,
25
+ };
26
+ }
27
+ }
28
+ /**
29
+ * SimpleErrorFormatter - Formats as simple array of messages
30
+ */
31
+ export class SimpleErrorFormatter {
32
+ format(error) {
33
+ const messages = [];
34
+ if (error.errors && error.errors.length > 0) {
35
+ error.errors.forEach((err) => {
36
+ messages.push(err.summary || err.message);
37
+ });
38
+ }
39
+ else {
40
+ messages.push(error.summary || error.message);
41
+ }
42
+ return {
43
+ statusCode: 400,
44
+ message: "Validation failed",
45
+ errors: messages,
46
+ };
47
+ }
48
+ }
6
49
  /**
7
- * Validation Exception Filter - Handles validation errors
50
+ * DetailedErrorFormatter - Formats with detailed field info
51
+ */
52
+ export class DetailedErrorFormatter {
53
+ format(error) {
54
+ const errors = [];
55
+ if (error.errors && error.errors.length > 0) {
56
+ error.errors.forEach((err) => {
57
+ errors.push({
58
+ field: err.path?.replace(/^\//, "") || "unknown",
59
+ message: err.summary || err.message,
60
+ value: err.value,
61
+ expected: err.schema,
62
+ });
63
+ });
64
+ }
65
+ else {
66
+ errors.push({
67
+ field: error.property?.replace(/^\//, "") || "unknown",
68
+ message: error.summary || error.message,
69
+ value: error.found,
70
+ expected: error.expected,
71
+ });
72
+ }
73
+ return {
74
+ statusCode: 400,
75
+ message: "Validation failed",
76
+ errors,
77
+ };
78
+ }
79
+ }
80
+ /**
81
+ * Validation Exception Filter - Handles validation errors with customizable formatting
8
82
  * @example
9
- * @UseFilters(ValidationExceptionFilter)
10
- * @Post()
11
- * async create(@Body() data: any) {}
83
+ * // With FormatErrorFormatter (NestJS-style)
84
+ * app.useGlobalFilters(new ValidationExceptionFilter(new FormatErrorFormatter()));
85
+ *
86
+ * // With SimpleErrorFormatter
87
+ * app.useGlobalFilters(new ValidationExceptionFilter(new SimpleErrorFormatter()));
88
+ *
89
+ * // With DetailedErrorFormatter
90
+ * app.useGlobalFilters(new ValidationExceptionFilter(new DetailedErrorFormatter()));
91
+ *
92
+ * // Without formatter (default detailed format)
93
+ * app.useGlobalFilters(new ValidationExceptionFilter());
12
94
  */
13
95
  export class ValidationExceptionFilter {
96
+ formatter = null;
97
+ constructor(formatter) {
98
+ this.formatter = formatter || null;
99
+ }
14
100
  catch(exception, context) {
15
- const response = context.getResponse();
16
101
  const request = context.getRequest();
17
- return {
18
- statusCode: exception.statusCode,
19
- error: "Validation Error",
20
- message: exception.message,
21
- errors: exception.errors || [],
22
- timestamp: new Date().toISOString(),
23
- path: request.url,
24
- };
102
+ // Check if this is a validation error from Elysia
103
+ const isValidationError = this.isValidationError(exception);
104
+ if (!isValidationError) {
105
+ // Not a validation error, re-throw to let other filters handle it
106
+ throw exception;
107
+ }
108
+ // Parse the validation error
109
+ let validationError;
110
+ if (typeof exception.message === "string") {
111
+ try {
112
+ validationError = JSON.parse(exception.message);
113
+ }
114
+ catch {
115
+ validationError = { message: exception.message };
116
+ }
117
+ }
118
+ else {
119
+ validationError = exception;
120
+ }
121
+ // Format the error using the provided formatter
122
+ if (this.formatter) {
123
+ return this.formatter.format(validationError);
124
+ }
125
+ // Default format (detailed)
126
+ return new DetailedErrorFormatter().format(validationError);
127
+ }
128
+ isValidationError(exception) {
129
+ if (!exception)
130
+ return false;
131
+ // Check if it's a validation error by looking at the error structure
132
+ if (exception.message && typeof exception.message === "string") {
133
+ try {
134
+ const parsed = JSON.parse(exception.message);
135
+ return parsed.type === "validation";
136
+ }
137
+ catch {
138
+ return false;
139
+ }
140
+ }
141
+ return (exception.type === "validation" || exception.code === "VALIDATION_ERROR");
25
142
  }
26
143
  }
27
144
  /**
@@ -21,13 +21,15 @@ export interface RouteOptions {
21
21
  export declare function Controller(path?: string): ClassDecorator;
22
22
  /**
23
23
  * HTTP GET decorator
24
- * @param path Route path
25
- * @param options Route configuration options
24
+ * @param pathOrOptions Route path or options with DTO
26
25
  * @example
27
26
  * @Get('/profile')
28
27
  * async getProfile() {}
28
+ *
29
+ * @Get({ path: '/:id', params: UserIdDTO, query: QueryDTO })
30
+ * async findOne(@Param('id') id: string, @Query() query: any) {}
29
31
  */
30
- export declare function Get(path?: string, options?: RouteOptions): MethodDecorator;
32
+ export declare function Get(pathOrOptions?: string | RouteOptions): MethodDecorator;
31
33
  /**
32
34
  * @Post decorator - Define a POST route
33
35
  * @param pathOrOptions Optional route path or options with DTO
@@ -66,25 +68,25 @@ export declare function Put(pathOrOptions?: string | RouteOptions): MethodDecora
66
68
  export declare function Patch(pathOrOptions?: string | RouteOptions): MethodDecorator;
67
69
  /**
68
70
  * HTTP DELETE decorator
69
- * @param path Route path
70
- * @param options Route configuration options
71
+ * @param pathOrOptions Route path or options with DTO
71
72
  * @example
72
73
  * @Delete('/:id')
73
74
  * async remove(@Param('id') id: string) {}
75
+ *
76
+ * @Delete({ path: '/:id', params: UserIdDTO })
77
+ * async remove(@Param('id') id: string) {}
74
78
  */
75
- export declare function Delete(path?: string, options?: RouteOptions): MethodDecorator;
79
+ export declare function Delete(pathOrOptions?: string | RouteOptions): MethodDecorator;
76
80
  /**
77
81
  * HTTP OPTIONS decorator
78
- * @param path Route path
79
- * @param options Route configuration options
82
+ * @param pathOrOptions Route path or options with DTO
80
83
  */
81
- export declare function Options(path?: string, options?: RouteOptions): MethodDecorator;
84
+ export declare function Options(pathOrOptions?: string | RouteOptions): MethodDecorator;
82
85
  /**
83
86
  * HTTP HEAD decorator
84
- * @param path Route path
85
- * @param options Route configuration options
87
+ * @param pathOrOptions Route path or options with DTO
86
88
  */
87
- export declare function Head(path?: string, options?: RouteOptions): MethodDecorator;
89
+ export declare function Head(pathOrOptions?: string | RouteOptions): MethodDecorator;
88
90
  /**
89
91
  * Set custom HTTP status code for a route
90
92
  * @param code HTTP status code
@@ -1 +1 @@
1
- {"version":3,"file":"http.decorators.d.ts","sourceRoot":"","sources":["../../core/decorators/http.decorators.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B;;;GAGG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,QAAQ,CAAC,EAAE,GAAG,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,GAAE,MAAW,GAAG,cAAc,CAK5D;AAED;;;;;;;GAOG;AACH,wBAAgB,GAAG,CACjB,IAAI,GAAE,MAAW,EACjB,OAAO,CAAC,EAAE,YAAY,GACrB,eAAe,CAEjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,IAAI,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,eAAe,CAM3E;AAED;;;;;;;;;GASG;AACH,wBAAgB,GAAG,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,eAAe,CAM1E;AAED;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,eAAe,CAM5E;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CACpB,IAAI,GAAE,MAAW,EACjB,OAAO,CAAC,EAAE,YAAY,GACrB,eAAe,CAEjB;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CACrB,IAAI,GAAE,MAAW,EACjB,OAAO,CAAC,EAAE,YAAY,GACrB,eAAe,CAEjB;AAED;;;;GAIG;AACH,wBAAgB,IAAI,CAClB,IAAI,GAAE,MAAW,EACjB,OAAO,CAAC,EAAE,YAAY,GACrB,eAAe,CAEjB;AAiDD;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAStD;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,CAYnE;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CACtB,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,MAAY,GACvB,eAAe,CAcjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,GAAG,CAAC,GAAG,WAAW,EAAE,GAAG,EAAE,GAAG,cAAc,GAAG,eAAe,CAuB3E"}
1
+ {"version":3,"file":"http.decorators.d.ts","sourceRoot":"","sources":["../../core/decorators/http.decorators.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B;;;GAGG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,QAAQ,CAAC,EAAE,GAAG,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,GAAE,MAAW,GAAG,cAAc,CAK5D;AAED;;;;;;;;;GASG;AACH,wBAAgB,GAAG,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,eAAe,CAM1E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,IAAI,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,eAAe,CAM3E;AAED;;;;;;;;;GASG;AACH,wBAAgB,GAAG,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,eAAe,CAM1E;AAED;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,eAAe,CAM5E;AAED;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,eAAe,CAM7E;AAED;;;GAGG;AACH,wBAAgB,OAAO,CACrB,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,GACpC,eAAe,CAMjB;AAED;;;GAGG;AACH,wBAAgB,IAAI,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,eAAe,CAM3E;AAmDD;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAStD;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,CAYnE;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CACtB,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,MAAY,GACvB,eAAe,CAcjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,GAAG,CAAC,GAAG,WAAW,EAAE,GAAG,EAAE,GAAG,cAAc,GAAG,eAAe,CAuB3E"}
@@ -14,14 +14,20 @@ export function Controller(path = "") {
14
14
  }
15
15
  /**
16
16
  * HTTP GET decorator
17
- * @param path Route path
18
- * @param options Route configuration options
17
+ * @param pathOrOptions Route path or options with DTO
19
18
  * @example
20
19
  * @Get('/profile')
21
20
  * async getProfile() {}
21
+ *
22
+ * @Get({ path: '/:id', params: UserIdDTO, query: QueryDTO })
23
+ * async findOne(@Param('id') id: string, @Query() query: any) {}
22
24
  */
23
- export function Get(path = "", options) {
24
- return createRouteDecorator("GET", path, options);
25
+ export function Get(pathOrOptions) {
26
+ if (typeof pathOrOptions === "string") {
27
+ return createRouteDecorator("GET", pathOrOptions);
28
+ }
29
+ const options = pathOrOptions || {};
30
+ return createRouteDecorator("GET", options.path || "", options);
25
31
  }
26
32
  /**
27
33
  * @Post decorator - Define a POST route
@@ -79,30 +85,42 @@ export function Patch(pathOrOptions) {
79
85
  }
80
86
  /**
81
87
  * HTTP DELETE decorator
82
- * @param path Route path
83
- * @param options Route configuration options
88
+ * @param pathOrOptions Route path or options with DTO
84
89
  * @example
85
90
  * @Delete('/:id')
86
91
  * async remove(@Param('id') id: string) {}
92
+ *
93
+ * @Delete({ path: '/:id', params: UserIdDTO })
94
+ * async remove(@Param('id') id: string) {}
87
95
  */
88
- export function Delete(path = "", options) {
89
- return createRouteDecorator("DELETE", path, options);
96
+ export function Delete(pathOrOptions) {
97
+ if (typeof pathOrOptions === "string") {
98
+ return createRouteDecorator("DELETE", pathOrOptions);
99
+ }
100
+ const options = pathOrOptions || {};
101
+ return createRouteDecorator("DELETE", options.path || "", options);
90
102
  }
91
103
  /**
92
104
  * HTTP OPTIONS decorator
93
- * @param path Route path
94
- * @param options Route configuration options
105
+ * @param pathOrOptions Route path or options with DTO
95
106
  */
96
- export function Options(path = "", options) {
97
- return createRouteDecorator("OPTIONS", path, options);
107
+ export function Options(pathOrOptions) {
108
+ if (typeof pathOrOptions === "string") {
109
+ return createRouteDecorator("OPTIONS", pathOrOptions);
110
+ }
111
+ const options = pathOrOptions || {};
112
+ return createRouteDecorator("OPTIONS", options.path || "", options);
98
113
  }
99
114
  /**
100
115
  * HTTP HEAD decorator
101
- * @param path Route path
102
- * @param options Route configuration options
116
+ * @param pathOrOptions Route path or options with DTO
103
117
  */
104
- export function Head(path = "", options) {
105
- return createRouteDecorator("HEAD", path, options);
118
+ export function Head(pathOrOptions) {
119
+ if (typeof pathOrOptions === "string") {
120
+ return createRouteDecorator("HEAD", pathOrOptions);
121
+ }
122
+ const options = pathOrOptions || {};
123
+ return createRouteDecorator("HEAD", options.path || "", options);
106
124
  }
107
125
  /**
108
126
  * Helper function to create route decorators
package/dist/dto.d.ts CHANGED
@@ -35,8 +35,9 @@ export declare const CommonDTO: {
35
35
  Name: (options?: {}) => import("@sinclair/typebox").TString;
36
36
  /**
37
37
  * Email validation
38
+ * Uses format: email which validates basic email structure
38
39
  */
39
- Email: (options?: {}) => import("@sinclair/typebox").TString;
40
+ Email: (options?: any) => import("@sinclair/typebox").TString;
40
41
  /**
41
42
  * Password validation (min 6 characters)
42
43
  */
package/dist/dto.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"dto.d.ts","sourceRoot":"","sources":["../core/dto.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;;;;;;;;;;;;;;;;;;;;GAqBG;AAMH,eAAO,MAAM,GAAG,EAAE,GAAO,CAAC;AAE1B;;GAEG;AACH,eAAO,MAAM,SAAS;IACpB;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAQH;;OAEG;;IAGH;;OAEG;;;;;IAOH;;OAEG;mBACY,MAAM,EAAE;;;;CAKxB,CAAC;AAEF;;GAEG;AACH,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC"}
1
+ {"version":3,"file":"dto.d.ts","sourceRoot":"","sources":["../core/dto.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;;;;;;;;;;;;;;;;;;;;GAqBG;AAMH,eAAO,MAAM,GAAG,EAAE,GAAO,CAAC;AAiC1B;;GAEG;AACH,eAAO,MAAM,SAAS;IACpB;;OAEG;;IAGH;;;OAGG;sBACc,GAAG;IAOpB;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;IAQH;;OAEG;;IAGH;;OAEG;;;;;IAOH;;OAEG;mBACY,MAAM,EAAE;;;;CAKxB,CAAC;AAEF;;GAEG;AACH,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC"}
package/dist/dto.js CHANGED
@@ -31,6 +31,33 @@ import { t } from "elysia";
31
31
  // Consumers still get runtime validation; for static typing, prefer using
32
32
  // `Static<TSchema>` from Elysia which we re-export below.
33
33
  export const DTO = t;
34
+ /**
35
+ * Helper to attach strict validation to a DTO Object schema
36
+ * Sets additionalProperties to false which makes Elysia strip unknown fields
37
+ *
38
+ * NOTE: Elysia's default behavior is to STRIP additional properties, not throw errors.
39
+ * This is secure by default. If you need to throw errors on unknown properties,
40
+ * use the approach documented in STRICT_VALIDATION.md
41
+ *
42
+ * @param properties - Object properties schema
43
+ * @param options - Additional schema options
44
+ * @returns TypeBox schema with additionalProperties: false
45
+ *
46
+ * @example
47
+ * export const UserDTO = DTO.Strict({
48
+ * email: DTO.String(),
49
+ * age: DTO.Number()
50
+ * });
51
+ *
52
+ * // Request: { email: "test@test.com", age: 25, extra: "field" }
53
+ * // Result: { email: "test@test.com", age: 25 } ← extra stripped (secure)
54
+ */
55
+ DTO.Strict = (properties, options = {}) => {
56
+ return t.Object(properties, {
57
+ ...options,
58
+ additionalProperties: false,
59
+ });
60
+ };
34
61
  /**
35
62
  * Common DTO patterns for quick use
36
63
  */
@@ -41,8 +68,13 @@ export const CommonDTO = {
41
68
  Name: (options = {}) => t.String({ minLength: 2, maxLength: 50, ...options }),
42
69
  /**
43
70
  * Email validation
71
+ * Uses format: email which validates basic email structure
44
72
  */
45
- Email: (options = {}) => t.String({ format: "email", ...options }),
73
+ Email: (options = {}) => t.String({
74
+ format: "email",
75
+ error: "Invalid email address",
76
+ ...options,
77
+ }),
46
78
  /**
47
79
  * Password validation (min 6 characters)
48
80
  */
package/dist/factory.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Elysia } from "elysia";
2
2
  import "reflect-metadata";
3
+ import { ErrorFormatter } from "./decorators/exception.advanced";
3
4
  /**
4
5
  * Application Factory for WynkJS Framework
5
6
  * Creates and configures Elysia app with all decorators support
@@ -8,6 +9,7 @@ export interface ApplicationOptions {
8
9
  cors?: boolean | any;
9
10
  globalPrefix?: string;
10
11
  logger?: boolean;
12
+ validationErrorFormatter?: ErrorFormatter;
11
13
  }
12
14
  export declare class WynkFramework {
13
15
  private app;
@@ -16,6 +18,7 @@ export declare class WynkFramework {
16
18
  private globalInterceptors;
17
19
  private globalPipes;
18
20
  private globalFilters;
21
+ private validationFormatter?;
19
22
  constructor(options?: ApplicationOptions);
20
23
  /**
21
24
  * Static convenience creator to align with documentation examples
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../core/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,kBAAkB,CAAC;AAc1B;;;GAGG;AAEH,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,OAAO,GAAG,GAAG,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,aAAa,CAAa;gBAEtB,OAAO,GAAE,kBAAuB;IAgB5C;;OAEG;IACH,MAAM,CAAC,MAAM,CACX,OAAO,GAAE,kBAAkB,GAAG;QAAE,WAAW,CAAC,EAAE,GAAG,EAAE,CAAA;KAAO,GACzD,aAAa;IAQhB;;OAEG;IACH,mBAAmB,CAAC,GAAG,WAAW,EAAE,GAAG,EAAE,GAAG,IAAI;IAKhD;;OAEG;IACH,eAAe,CAAC,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI;IAKvC;;OAEG;IACH,qBAAqB,CAAC,GAAG,YAAY,EAAE,GAAG,EAAE,GAAG,IAAI;IAKnD;;OAEG;IACH,cAAc,CAAC,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,IAAI;IAKrC;;OAEG;IACH,gBAAgB,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI;IAKzC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAkD9B;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMzC;;OAEG;IACH,MAAM,IAAI,MAAM;IAIhB;;OAEG;YACW,kBAAkB;CAuVjC;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,GAAE,kBAAuB,GAAG,aAAa,CAEzE;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,MAAM,CAAC,MAAM,CACX,OAAO,GAAE,kBAAkB,GAAG;QAAE,WAAW,CAAC,EAAE,GAAG,EAAE,CAAA;KAAO,GACzD,aAAa;CASjB;AAGD,OAAO,EAAE,aAAa,IAAI,eAAe,EAAE,CAAC"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../core/factory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,kBAAkB,CAAC;AAc1B,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEjE;;;GAGG;AAEH,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,OAAO,GAAG,GAAG,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wBAAwB,CAAC,EAAE,cAAc,CAAC;CAC3C;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,YAAY,CAAa;IACjC,OAAO,CAAC,kBAAkB,CAAa;IACvC,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,mBAAmB,CAAC,CAAiB;gBAEjC,OAAO,GAAE,kBAAuB;IAmH5C;;OAEG;IACH,MAAM,CAAC,MAAM,CACX,OAAO,GAAE,kBAAkB,GAAG;QAAE,WAAW,CAAC,EAAE,GAAG,EAAE,CAAA;KAAO,GACzD,aAAa;IAQhB;;OAEG;IACH,mBAAmB,CAAC,GAAG,WAAW,EAAE,GAAG,EAAE,GAAG,IAAI;IAKhD;;OAEG;IACH,eAAe,CAAC,GAAG,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI;IAKvC;;OAEG;IACH,qBAAqB,CAAC,GAAG,YAAY,EAAE,GAAG,EAAE,GAAG,IAAI;IAKnD;;OAEG;IACH,cAAc,CAAC,GAAG,KAAK,EAAE,GAAG,EAAE,GAAG,IAAI;IAKrC;;OAEG;IACH,gBAAgB,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI;IAKzC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAkD9B;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMzC;;OAEG;IACH,MAAM,IAAI,MAAM;IAIhB;;OAEG;YACW,kBAAkB;CAuVjC;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,GAAE,kBAAuB,GAAG,aAAa,CAEzE;AAED;;GAEG;AACH,qBAAa,WAAW;IACtB,MAAM,CAAC,MAAM,CACX,OAAO,GAAE,kBAAkB,GAAG;QAAE,WAAW,CAAC,EAAE,GAAG,EAAE,CAAA;KAAO,GACzD,aAAa;CASjB;AAGD,OAAO,EAAE,aAAa,IAAI,eAAe,EAAE,CAAC"}
package/dist/factory.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Elysia } from "elysia";
2
2
  import "reflect-metadata";
3
3
  import { container } from "tsyringe";
4
+ import { Value } from "@sinclair/typebox/value";
4
5
  import { createExecutionContext, executeGuards, } from "./decorators/guard.decorators";
5
6
  import { executeInterceptors } from "./decorators/interceptor.decorators";
6
7
  import { executePipes } from "./decorators/pipe.decorators";
@@ -12,8 +13,94 @@ export class WynkFramework {
12
13
  globalInterceptors = [];
13
14
  globalPipes = [];
14
15
  globalFilters = [];
16
+ validationFormatter;
15
17
  constructor(options = {}) {
16
18
  this.app = new Elysia();
19
+ this.validationFormatter = options.validationErrorFormatter;
20
+ // Configure Elysia's error handling for validation errors
21
+ this.app.onError(({ code, error, set }) => {
22
+ // Handle ValidationError from Elysia
23
+ if (code === "VALIDATION" ||
24
+ error?.constructor?.name === "ValidationError") {
25
+ const validationError = error;
26
+ set.status = 400;
27
+ // Try to collect all validation errors using TypeBox
28
+ const allErrors = {};
29
+ // Check if we have the validator and value to collect all errors
30
+ if (validationError.validator && validationError.value) {
31
+ const schema = validationError.validator.schema || validationError.validator;
32
+ // Use TypeBox's Errors iterator to collect ALL validation errors
33
+ try {
34
+ const errors = [...Value.Errors(schema, validationError.value)];
35
+ if (errors.length > 0) {
36
+ errors.forEach((err) => {
37
+ const field = err.path?.replace(/^\//, "") || "unknown";
38
+ if (!allErrors[field]) {
39
+ allErrors[field] = [];
40
+ }
41
+ allErrors[field].push(err.message || "Validation failed");
42
+ });
43
+ }
44
+ else {
45
+ // Fallback to single error
46
+ const field = validationError.valueError?.path?.replace(/^\//, "") ||
47
+ validationError.on ||
48
+ "body";
49
+ const message = validationError.customError ||
50
+ validationError.valueError?.message ||
51
+ "Validation failed";
52
+ allErrors[field] = [message];
53
+ }
54
+ }
55
+ catch (e) {
56
+ // Fallback to single error if TypeBox iteration fails
57
+ const field = validationError.valueError?.path?.replace(/^\//, "") ||
58
+ validationError.on ||
59
+ "body";
60
+ const message = validationError.customError ||
61
+ validationError.valueError?.message ||
62
+ "Validation failed";
63
+ allErrors[field] = [message];
64
+ }
65
+ }
66
+ else {
67
+ // Fallback to single error
68
+ const field = validationError.valueError?.path?.replace(/^\//, "") ||
69
+ validationError.on ||
70
+ "body";
71
+ const message = validationError.customError ||
72
+ validationError.valueError?.message ||
73
+ "Validation failed";
74
+ allErrors[field] = [message];
75
+ }
76
+ // If a custom formatter is provided, use it
77
+ if (this.validationFormatter) {
78
+ // Convert allErrors to the format expected by formatters
79
+ const formattedError = {
80
+ errors: Object.entries(allErrors).map(([field, messages]) => ({
81
+ path: `/${field}`,
82
+ summary: messages[0],
83
+ message: messages.join(", "),
84
+ })),
85
+ };
86
+ return this.validationFormatter.format(formattedError);
87
+ }
88
+ // Default format: { statusCode, message, errors: { field: [messages] } }
89
+ return {
90
+ statusCode: 400,
91
+ message: "Validation failed",
92
+ errors: allErrors,
93
+ };
94
+ }
95
+ // Default error handling
96
+ const err = error;
97
+ set.status = err.status || 500;
98
+ return {
99
+ statusCode: err.status || 500,
100
+ message: err.message || "Internal server error",
101
+ error: err.name || "Error",
102
+ };
103
+ });
17
104
  // Apply CORS if enabled
18
105
  if (options.cors) {
19
106
  // CORS configuration would go here
package/dist/index.d.ts CHANGED
@@ -5,6 +5,10 @@
5
5
  * @author WynkJS Team
6
6
  * @license MIT
7
7
  */
8
+ import "reflect-metadata";
9
+ export { injectable, inject, singleton, autoInjectable, registry, container, } from "tsyringe";
10
+ export type { DependencyContainer } from "tsyringe";
11
+ export { injectable as Injectable, inject as Inject, singleton as Singleton, autoInjectable as AutoInjectable, registry as Registry, container as Container, } from "tsyringe";
8
12
  export * from "./decorators/http.decorators";
9
13
  export * from "./decorators/param.decorators";
10
14
  export * from "./decorators/guard.decorators";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../core/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,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;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,iCAAiC,CAAC;AAIhD,cAAc,kCAAkC,CAAC;AASjD,cAAc,OAAO,CAAC;AAGtB,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
@@ -5,6 +5,14 @@
5
5
  * @author WynkJS Team
6
6
  * @license MIT
7
7
  */
8
+ // Import reflect-metadata at the top of the framework
9
+ // This ensures users don't need to import it manually
10
+ import "reflect-metadata";
11
+ // Re-export tsyringe decorators for dependency injection
12
+ // Users can import these from 'wynkjs' instead of 'tsyringe'
13
+ export { injectable, inject, singleton, autoInjectable, registry, container, } from "tsyringe";
14
+ // Capital-cased aliases for consistency with WynkJS naming convention
15
+ export { injectable as Injectable, inject as Inject, singleton as Singleton, autoInjectable as AutoInjectable, registry as Registry, container as Container, } from "tsyringe";
8
16
  // HTTP Method Decorators
9
17
  export * from "./decorators/http.decorators";
10
18
  // Parameter Decorators
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wynkjs",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "A high-performance TypeScript framework built on Elysia with NestJS-style decorators - 20x faster than Express",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",