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
package/dist/dto.d.ts ADDED
@@ -0,0 +1,83 @@
1
+ /**
2
+ * DTO Utilities for WynkJS Framework
3
+ * Re-exports Elysia's TypeBox for DTO validation
4
+ */
5
+ /**
6
+ * Export Elysia's TypeBox as DTO builder
7
+ * This provides runtime validation for request bodies, queries, params, etc.
8
+ *
9
+ * @example
10
+ * import { DTO } from '@wynkjs/framework';
11
+ *
12
+ * export const CreateUserDTO = DTO.Object({
13
+ * name: DTO.String({ minLength: 2, maxLength: 50 }),
14
+ * email: DTO.String({ format: 'email' }),
15
+ * age: DTO.Optional(DTO.Number({ minimum: 18 })),
16
+ * });
17
+ *
18
+ * @Controller('/users')
19
+ * export class UserController {
20
+ * @Post({ body: CreateUserDTO })
21
+ * async create(@Body() data: any) {
22
+ * // data is automatically validated against CreateUserDTO
23
+ * return { created: data };
24
+ * }
25
+ * }
26
+ */
27
+ export declare const DTO: any;
28
+ /**
29
+ * Common DTO patterns for quick use
30
+ */
31
+ export declare const CommonDTO: {
32
+ /**
33
+ * Name validation (2-50 characters)
34
+ */
35
+ Name: (options?: {}) => import("@sinclair/typebox").TString;
36
+ /**
37
+ * Email validation
38
+ */
39
+ Email: (options?: {}) => import("@sinclair/typebox").TString;
40
+ /**
41
+ * Password validation (min 6 characters)
42
+ */
43
+ Password: (options?: {}) => import("@sinclair/typebox").TString;
44
+ /**
45
+ * UUID validation
46
+ */
47
+ UUID: (options?: {}) => import("@sinclair/typebox").TString;
48
+ /**
49
+ * URL validation
50
+ */
51
+ URL: (options?: {}) => import("@sinclair/typebox").TString;
52
+ /**
53
+ * Date string validation (ISO 8601)
54
+ */
55
+ DateString: (options?: {}) => import("@sinclair/typebox").TString;
56
+ /**
57
+ * Phone number (Indian format)
58
+ */
59
+ PhoneIN: (options?: {}) => import("@sinclair/typebox").TString;
60
+ /**
61
+ * Integer ID
62
+ */
63
+ ID: (options?: {}) => import("@sinclair/typebox").TNumber;
64
+ /**
65
+ * Pagination query
66
+ */
67
+ Pagination: () => import("@sinclair/typebox").TObject<{
68
+ page: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
69
+ limit: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
70
+ }>;
71
+ /**
72
+ * Sort query
73
+ */
74
+ Sort: (fields: string[]) => import("@sinclair/typebox").TObject<{
75
+ sortBy: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<import("@sinclair/typebox").TLiteral<string>[]>>;
76
+ sortOrder: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"asc">, import("@sinclair/typebox").TLiteral<"desc">]>>;
77
+ }>;
78
+ };
79
+ /**
80
+ * Export TypeBox types for TypeScript inference
81
+ */
82
+ export type { TSchema, Static } from "elysia";
83
+ //# sourceMappingURL=dto.d.ts.map
@@ -0,0 +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"}
package/dist/dto.js ADDED
@@ -0,0 +1,88 @@
1
+ /**
2
+ * DTO Utilities for WynkJS Framework
3
+ * Re-exports Elysia's TypeBox for DTO validation
4
+ */
5
+ import { t } from "elysia";
6
+ /**
7
+ * Export Elysia's TypeBox as DTO builder
8
+ * This provides runtime validation for request bodies, queries, params, etc.
9
+ *
10
+ * @example
11
+ * import { DTO } from '@wynkjs/framework';
12
+ *
13
+ * export const CreateUserDTO = DTO.Object({
14
+ * name: DTO.String({ minLength: 2, maxLength: 50 }),
15
+ * email: DTO.String({ format: 'email' }),
16
+ * age: DTO.Optional(DTO.Number({ minimum: 18 })),
17
+ * });
18
+ *
19
+ * @Controller('/users')
20
+ * export class UserController {
21
+ * @Post({ body: CreateUserDTO })
22
+ * async create(@Body() data: any) {
23
+ * // data is automatically validated against CreateUserDTO
24
+ * return { created: data };
25
+ * }
26
+ * }
27
+ */
28
+ // Note: Elysia's type builder exposes internal option types that TypeScript
29
+ // cannot emit in d.ts files (TS4023). To keep declaration generation clean
30
+ // while preserving runtime behavior, we export DTO with an `any` type.
31
+ // Consumers still get runtime validation; for static typing, prefer using
32
+ // `Static<TSchema>` from Elysia which we re-export below.
33
+ export const DTO = t;
34
+ /**
35
+ * Common DTO patterns for quick use
36
+ */
37
+ export const CommonDTO = {
38
+ /**
39
+ * Name validation (2-50 characters)
40
+ */
41
+ Name: (options = {}) => t.String({ minLength: 2, maxLength: 50, ...options }),
42
+ /**
43
+ * Email validation
44
+ */
45
+ Email: (options = {}) => t.String({ format: "email", ...options }),
46
+ /**
47
+ * Password validation (min 6 characters)
48
+ */
49
+ Password: (options = {}) => t.String({ minLength: 6, ...options }),
50
+ /**
51
+ * UUID validation
52
+ */
53
+ UUID: (options = {}) => t.String({ format: "uuid", ...options }),
54
+ /**
55
+ * URL validation
56
+ */
57
+ URL: (options = {}) => t.String({ format: "uri", ...options }),
58
+ /**
59
+ * Date string validation (ISO 8601)
60
+ */
61
+ DateString: (options = {}) => t.String({ format: "date-time", ...options }),
62
+ /**
63
+ * Phone number (Indian format)
64
+ */
65
+ PhoneIN: (options = {}) => t.String({
66
+ pattern: "^[6-9]{1}[0-9]{9}$",
67
+ errorMessage: "Invalid mobile number",
68
+ ...options,
69
+ }),
70
+ /**
71
+ * Integer ID
72
+ */
73
+ ID: (options = {}) => t.Number({ minimum: 1, ...options }),
74
+ /**
75
+ * Pagination query
76
+ */
77
+ Pagination: () => t.Object({
78
+ page: t.Optional(t.Number({ minimum: 1, default: 1 })),
79
+ limit: t.Optional(t.Number({ minimum: 1, maximum: 100, default: 10 })),
80
+ }),
81
+ /**
82
+ * Sort query
83
+ */
84
+ Sort: (fields) => t.Object({
85
+ sortBy: t.Optional(t.Union(fields.map((f) => t.Literal(f)))),
86
+ sortOrder: t.Optional(t.Union([t.Literal("asc"), t.Literal("desc")])),
87
+ }),
88
+ };
@@ -0,0 +1,76 @@
1
+ import { Elysia } from "elysia";
2
+ import "reflect-metadata";
3
+ /**
4
+ * Application Factory for WynkJS Framework
5
+ * Creates and configures Elysia app with all decorators support
6
+ */
7
+ export interface ApplicationOptions {
8
+ cors?: boolean | any;
9
+ globalPrefix?: string;
10
+ logger?: boolean;
11
+ }
12
+ export declare class WynkFramework {
13
+ private app;
14
+ private controllers;
15
+ private globalGuards;
16
+ private globalInterceptors;
17
+ private globalPipes;
18
+ private globalFilters;
19
+ constructor(options?: ApplicationOptions);
20
+ /**
21
+ * Static convenience creator to align with documentation examples
22
+ */
23
+ static create(options?: ApplicationOptions & {
24
+ controllers?: any[];
25
+ }): WynkFramework;
26
+ /**
27
+ * Register controllers with the application
28
+ */
29
+ registerControllers(...controllers: any[]): this;
30
+ /**
31
+ * Register global guards
32
+ */
33
+ useGlobalGuards(...guards: any[]): this;
34
+ /**
35
+ * Register global interceptors
36
+ */
37
+ useGlobalInterceptors(...interceptors: any[]): this;
38
+ /**
39
+ * Register global pipes
40
+ */
41
+ useGlobalPipes(...pipes: any[]): this;
42
+ /**
43
+ * Register global exception filters
44
+ */
45
+ useGlobalFilters(...filters: any[]): this;
46
+ /**
47
+ * Build the application - register all routes
48
+ */
49
+ build(): Promise<Elysia>;
50
+ /**
51
+ * Start listening on a port
52
+ */
53
+ listen(port: number): Promise<void>;
54
+ /**
55
+ * Get the underlying Elysia instance
56
+ */
57
+ getApp(): Elysia;
58
+ /**
59
+ * Register a single controller
60
+ */
61
+ private registerController;
62
+ }
63
+ /**
64
+ * Factory function to create a new application
65
+ */
66
+ export declare function createApp(options?: ApplicationOptions): WynkFramework;
67
+ /**
68
+ * Alias for WynkFramework with static create method
69
+ */
70
+ export declare class WynkFactory {
71
+ static create(options?: ApplicationOptions & {
72
+ controllers?: any[];
73
+ }): WynkFramework;
74
+ }
75
+ export { WynkFramework as ElysiaFramework };
76
+ //# sourceMappingURL=factory.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,410 @@
1
+ import { Elysia } from "elysia";
2
+ import "reflect-metadata";
3
+ import { container } from "tsyringe";
4
+ import { createExecutionContext, executeGuards, } from "./decorators/guard.decorators";
5
+ import { executeInterceptors } from "./decorators/interceptor.decorators";
6
+ import { executePipes } from "./decorators/pipe.decorators";
7
+ import { executeExceptionFilters, HttpException, } from "./decorators/exception.decorators";
8
+ export class WynkFramework {
9
+ app;
10
+ controllers = [];
11
+ globalGuards = [];
12
+ globalInterceptors = [];
13
+ globalPipes = [];
14
+ globalFilters = [];
15
+ constructor(options = {}) {
16
+ this.app = new Elysia();
17
+ // Apply CORS if enabled
18
+ if (options.cors) {
19
+ // CORS configuration would go here
20
+ }
21
+ // Apply global prefix if specified
22
+ if (options.globalPrefix) {
23
+ // Global prefix handling
24
+ }
25
+ return this;
26
+ }
27
+ /**
28
+ * Static convenience creator to align with documentation examples
29
+ */
30
+ static create(options = {}) {
31
+ const app = new WynkFramework(options);
32
+ if (options.controllers && options.controllers.length) {
33
+ app.registerControllers(...options.controllers);
34
+ }
35
+ return app;
36
+ }
37
+ /**
38
+ * Register controllers with the application
39
+ */
40
+ registerControllers(...controllers) {
41
+ this.controllers.push(...controllers);
42
+ return this;
43
+ }
44
+ /**
45
+ * Register global guards
46
+ */
47
+ useGlobalGuards(...guards) {
48
+ this.globalGuards.push(...guards);
49
+ return this;
50
+ }
51
+ /**
52
+ * Register global interceptors
53
+ */
54
+ useGlobalInterceptors(...interceptors) {
55
+ this.globalInterceptors.push(...interceptors);
56
+ return this;
57
+ }
58
+ /**
59
+ * Register global pipes
60
+ */
61
+ useGlobalPipes(...pipes) {
62
+ this.globalPipes.push(...pipes);
63
+ return this;
64
+ }
65
+ /**
66
+ * Register global exception filters
67
+ */
68
+ useGlobalFilters(...filters) {
69
+ this.globalFilters.push(...filters);
70
+ return this;
71
+ }
72
+ /**
73
+ * Build the application - register all routes
74
+ */
75
+ async build() {
76
+ // Register global error handler if filters exist
77
+ if (this.globalFilters.length > 0) {
78
+ this.app.onError(async ({ error, set, request }) => {
79
+ console.log("🔴 ELYSIA ON_ERROR HOOK TRIGGERED");
80
+ console.log("Error:", error?.message || String(error));
81
+ console.log("Global filters:", this.globalFilters.length);
82
+ // Create a simple execution context
83
+ const executionContext = {
84
+ getRequest: () => request,
85
+ getResponse: () => set,
86
+ getContext: () => ({ request, set }),
87
+ getHandler: () => null,
88
+ getClass: () => null,
89
+ };
90
+ try {
91
+ const result = await executeExceptionFilters(this.globalFilters, error, executionContext);
92
+ if (result) {
93
+ if (result.statusCode) {
94
+ set.status = result.statusCode;
95
+ }
96
+ return result;
97
+ }
98
+ }
99
+ catch (filterError) {
100
+ console.error("Filter error:", filterError);
101
+ }
102
+ // Fallback error handling
103
+ set.status = 500;
104
+ return {
105
+ statusCode: 500,
106
+ message: error?.message || "Internal server error",
107
+ error: "Internal Server Error",
108
+ };
109
+ });
110
+ }
111
+ for (const ControllerClass of this.controllers) {
112
+ await this.registerController(ControllerClass);
113
+ }
114
+ return this.app;
115
+ }
116
+ /**
117
+ * Start listening on a port
118
+ */
119
+ async listen(port) {
120
+ await this.build();
121
+ this.app.listen(port);
122
+ console.log(`🚀 Application is running on http://localhost:${port}`);
123
+ }
124
+ /**
125
+ * Get the underlying Elysia instance
126
+ */
127
+ getApp() {
128
+ return this.app;
129
+ }
130
+ /**
131
+ * Register a single controller
132
+ */
133
+ async registerController(ControllerClass) {
134
+ // Use tsyringe container to resolve controller with all dependencies
135
+ const instance = container.resolve(ControllerClass);
136
+ const basePath = Reflect.getMetadata("basePath", ControllerClass) || "";
137
+ // Try reading from multiple locations
138
+ const routes1 = Reflect.getMetadata("routes", ControllerClass) || [];
139
+ const routes2 = Reflect.getMetadata("routes", ControllerClass.prototype) || [];
140
+ const routes3 = Reflect.getMetadata("controller:routes", instance) || [];
141
+ console.log(`🔍 Routes on ControllerClass: ${routes1.length}`);
142
+ console.log(`🔍 Routes on prototype: ${routes2.length}`);
143
+ console.log(`🔍 Routes on instance: ${routes3.length}`);
144
+ const routes = routes1.length > 0 ? routes1 : routes2.length > 0 ? routes2 : routes3;
145
+ console.log(`📦 Registering controller ${ControllerClass.name} with ${routes.length} routes`);
146
+ const controllerGuards = Reflect.getMetadata("guards", ControllerClass) || [];
147
+ const controllerInterceptors = Reflect.getMetadata("interceptors", ControllerClass) || [];
148
+ const controllerPipes = Reflect.getMetadata("pipes", ControllerClass) || [];
149
+ const controllerFilters = Reflect.getMetadata("filters", ControllerClass) || [];
150
+ // Get @Use() middleware (simple pattern like user's working code)
151
+ const controllerUses = Reflect.getMetadata("uses", ControllerClass) || [];
152
+ for (const route of routes) {
153
+ const fullPath = basePath + route.path;
154
+ const method = route.method.toLowerCase();
155
+ const methodName = route.methodName;
156
+ // Get method-specific metadata
157
+ const methodGuards = Reflect.getMetadata("guards", instance, methodName) || [];
158
+ const methodInterceptors = Reflect.getMetadata("interceptors", instance, methodName) || [];
159
+ const methodPipes = Reflect.getMetadata("pipes", instance, methodName) || [];
160
+ const methodFilters = Reflect.getMetadata("filters", instance, methodName) || [];
161
+ // Get @Use() middleware for this method
162
+ const methodUses = Reflect.getMetadata("uses", instance, methodName) || [];
163
+ const params = Reflect.getMetadata("params", instance, methodName) || [];
164
+ const httpCode = Reflect.getMetadata("route:httpCode", instance, methodName);
165
+ const headers = Reflect.getMetadata("route:headers", instance, methodName);
166
+ const redirect = Reflect.getMetadata("route:redirect", instance, methodName);
167
+ // Combine guards, interceptors, pipes, filters (global -> controller -> method)
168
+ const allGuards = [
169
+ ...this.globalGuards,
170
+ ...controllerGuards,
171
+ ...methodGuards,
172
+ ];
173
+ const allInterceptors = [
174
+ ...this.globalInterceptors,
175
+ ...controllerInterceptors,
176
+ ...methodInterceptors,
177
+ ];
178
+ const allPipes = [
179
+ ...this.globalPipes,
180
+ ...controllerPipes,
181
+ ...methodPipes,
182
+ ];
183
+ const allFilters = [
184
+ ...this.globalFilters,
185
+ ...controllerFilters,
186
+ ...methodFilters,
187
+ ];
188
+ // Get route options (for body validation schema)
189
+ const routeOptions = route.options || {};
190
+ const bodySchema = Reflect.getMetadata("route:bodySchema", instance, methodName);
191
+ if (bodySchema && !routeOptions.body) {
192
+ routeOptions.body = bodySchema;
193
+ }
194
+ // Create route handler
195
+ const handler = async (ctx) => {
196
+ try {
197
+ // Create execution context
198
+ const executionContext = createExecutionContext(ctx, instance[methodName], ControllerClass);
199
+ // Execute guards
200
+ if (allGuards.length > 0) {
201
+ const canActivate = await executeGuards(allGuards, executionContext);
202
+ if (!canActivate) {
203
+ throw new HttpException("Forbidden", 403, "Access denied");
204
+ }
205
+ }
206
+ // Prepare handler with parameters and pipes
207
+ const executeHandler = async () => {
208
+ // Build arguments for the controller method
209
+ const args = [];
210
+ if (params.length === 0) {
211
+ // No parameter decorators, pass full context
212
+ args.push(ctx);
213
+ }
214
+ else {
215
+ // Sort params by index
216
+ params.sort((a, b) => a.index - b.index);
217
+ for (const param of params) {
218
+ let value;
219
+ // Extract value based on type
220
+ switch (param.type) {
221
+ case "body":
222
+ value = param.data ? ctx.body?.[param.data] : ctx.body;
223
+ break;
224
+ case "param":
225
+ value = param.data ? ctx.params?.[param.data] : ctx.params;
226
+ break;
227
+ case "query":
228
+ value = param.data ? ctx.query?.[param.data] : ctx.query;
229
+ break;
230
+ case "headers":
231
+ value = param.data
232
+ ? ctx.headers?.get?.(param.data) ||
233
+ ctx.request?.headers?.get?.(param.data)
234
+ : ctx.headers || ctx.request?.headers;
235
+ break;
236
+ case "request":
237
+ value = ctx.request || ctx;
238
+ break;
239
+ case "response":
240
+ value = ctx.set || ctx.response;
241
+ break;
242
+ case "context":
243
+ if (param.data) {
244
+ // Access nested property like "session.userId"
245
+ const keys = param.data.split(".");
246
+ value = keys.reduce((obj, key) => obj?.[key], ctx);
247
+ }
248
+ else {
249
+ value = ctx;
250
+ }
251
+ break;
252
+ case "user":
253
+ value = param.data ? ctx.user?.[param.data] : ctx.user;
254
+ break;
255
+ case "file":
256
+ value = ctx.body?.file || ctx.file;
257
+ break;
258
+ case "files":
259
+ value = ctx.body?.files || ctx.files;
260
+ break;
261
+ }
262
+ // Apply pipes if any
263
+ if (param.pipes && param.pipes.length > 0) {
264
+ const metadata = {
265
+ type: param.type,
266
+ data: param.data,
267
+ };
268
+ value = await executePipes(param.pipes, value, metadata);
269
+ }
270
+ // Apply global/controller/method pipes
271
+ if (allPipes.length > 0) {
272
+ const metadata = {
273
+ type: param.type,
274
+ data: param.data,
275
+ };
276
+ value = await executePipes(allPipes, value, metadata);
277
+ }
278
+ args[param.index] = value;
279
+ }
280
+ }
281
+ // Call controller method
282
+ return await instance[methodName].apply(instance, args);
283
+ };
284
+ // Execute interceptors
285
+ let result;
286
+ if (allInterceptors.length > 0) {
287
+ result = await executeInterceptors(allInterceptors, executionContext, executeHandler);
288
+ }
289
+ else {
290
+ result = await executeHandler();
291
+ }
292
+ // Handle redirect
293
+ if (redirect) {
294
+ ctx.set.redirect = redirect.url;
295
+ ctx.set.status = redirect.statusCode;
296
+ return;
297
+ }
298
+ // Set custom HTTP code
299
+ if (httpCode) {
300
+ ctx.set.status = httpCode;
301
+ }
302
+ // Set custom headers
303
+ if (headers) {
304
+ Object.entries(headers).forEach(([key, value]) => {
305
+ ctx.set.headers[key] = value;
306
+ });
307
+ }
308
+ return result;
309
+ }
310
+ catch (error) {
311
+ console.log("🔴 ERROR CAUGHT IN FACTORY");
312
+ console.log("allFilters.length:", allFilters.length);
313
+ console.log("Error:", error?.message);
314
+ // Execute exception filters
315
+ if (allFilters.length > 0) {
316
+ console.log("✅ Executing exception filters...");
317
+ const executionContext = createExecutionContext(ctx, instance[methodName], ControllerClass);
318
+ try {
319
+ const result = await executeExceptionFilters(allFilters, error, executionContext);
320
+ if (result) {
321
+ if (result.statusCode) {
322
+ ctx.set.status = result.statusCode;
323
+ }
324
+ return result;
325
+ }
326
+ }
327
+ catch (filterError) {
328
+ // If filter doesn't handle it, continue to default error handling
329
+ error = filterError;
330
+ }
331
+ }
332
+ else {
333
+ console.log("❌ No filters registered for this route");
334
+ }
335
+ // Default error handling
336
+ if (error instanceof HttpException) {
337
+ ctx.set.status = error.getStatus();
338
+ return error.getResponse();
339
+ }
340
+ // Unknown error
341
+ ctx.set.status = 500;
342
+ return {
343
+ statusCode: 500,
344
+ message: error.message || "Internal server error",
345
+ error: "Internal Server Error",
346
+ };
347
+ }
348
+ };
349
+ // Wrap handler with @Use() middleware if present
350
+ // Combine controller and method middleware
351
+ const allUses = [...controllerUses, ...methodUses];
352
+ let finalHandler = handler;
353
+ if (allUses.length > 0) {
354
+ console.log(`🔗 Building middleware chain for ${method} ${fullPath}:`);
355
+ console.log(` Middleware count: ${allUses.length}`);
356
+ allUses.forEach((m, i) => console.log(` [${i}] ${m.name || "anonymous"}`));
357
+ // Build middleware chain using reduce (O(n) complexity)
358
+ // Builds from right to left: handler <- middleware[n-1] <- ... <- middleware[0]
359
+ // Executes left to right: middleware[0] -> ... -> middleware[n-1] -> handler
360
+ finalHandler = allUses.reduceRight((next, middleware, index) => {
361
+ return async (ctx) => {
362
+ console.log(`▶️ Executing middleware [${index}]: ${middleware.name || "anonymous"}`);
363
+ return await middleware(ctx, () => next(ctx));
364
+ };
365
+ }, handler);
366
+ }
367
+ // Register route with Elysia
368
+ const elysiaOptions = {};
369
+ if (routeOptions.body || bodySchema) {
370
+ elysiaOptions.body = routeOptions.body || bodySchema;
371
+ }
372
+ if (routeOptions.query) {
373
+ elysiaOptions.query = routeOptions.query;
374
+ }
375
+ if (routeOptions.params) {
376
+ elysiaOptions.params = routeOptions.params;
377
+ }
378
+ if (routeOptions.headers) {
379
+ elysiaOptions.headers = routeOptions.headers;
380
+ }
381
+ // Register with options if any validation schemas are present
382
+ if (Object.keys(elysiaOptions).length > 0) {
383
+ this.app[method](fullPath, finalHandler, elysiaOptions);
384
+ }
385
+ else {
386
+ this.app[method](fullPath, finalHandler);
387
+ }
388
+ }
389
+ }
390
+ }
391
+ /**
392
+ * Factory function to create a new application
393
+ */
394
+ export function createApp(options = {}) {
395
+ return new WynkFramework(options);
396
+ }
397
+ /**
398
+ * Alias for WynkFramework with static create method
399
+ */
400
+ export class WynkFactory {
401
+ static create(options = {}) {
402
+ const app = new WynkFramework(options);
403
+ if (options.controllers) {
404
+ app.registerControllers(...options.controllers);
405
+ }
406
+ return app;
407
+ }
408
+ }
409
+ // Export ElysiaFramework as alias for backwards compatibility
410
+ export { WynkFramework as ElysiaFramework };