wynkjs 1.0.4 → 1.0.7

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 (46) hide show
  1. package/README.md +361 -315
  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/decorators/exception.decorators.d.ts +1 -0
  6. package/dist/decorators/exception.decorators.d.ts.map +1 -1
  7. package/dist/decorators/exception.decorators.js +20 -3
  8. package/dist/decorators/guard.decorators.d.ts.map +1 -1
  9. package/dist/decorators/guard.decorators.js +11 -5
  10. package/dist/decorators/http.decorators.d.ts +8 -3
  11. package/dist/decorators/http.decorators.d.ts.map +1 -1
  12. package/dist/decorators/http.decorators.js +9 -2
  13. package/dist/decorators/interceptor.advanced.d.ts +9 -9
  14. package/dist/decorators/interceptor.advanced.d.ts.map +1 -1
  15. package/dist/decorators/interceptor.advanced.js +7 -7
  16. package/dist/decorators/interceptor.decorators.d.ts +9 -7
  17. package/dist/decorators/interceptor.decorators.d.ts.map +1 -1
  18. package/dist/decorators/interceptor.decorators.js +29 -18
  19. package/dist/decorators/param.decorators.d.ts +2 -2
  20. package/dist/decorators/param.decorators.js +1 -1
  21. package/dist/decorators/pipe.decorators.d.ts +2 -2
  22. package/dist/decorators/pipe.decorators.js +2 -2
  23. package/dist/factory.d.ts +62 -1
  24. package/dist/factory.d.ts.map +1 -1
  25. package/dist/factory.js +191 -180
  26. package/dist/global-prefix.d.ts +49 -0
  27. package/dist/global-prefix.d.ts.map +1 -0
  28. package/dist/global-prefix.js +155 -0
  29. package/dist/index.d.ts +6 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +4 -0
  32. package/dist/interfaces/interceptor.interface.d.ts +15 -0
  33. package/dist/interfaces/interceptor.interface.d.ts.map +1 -0
  34. package/dist/interfaces/interceptor.interface.js +1 -0
  35. package/dist/optimized-handler.d.ts +31 -0
  36. package/dist/optimized-handler.d.ts.map +1 -0
  37. package/dist/optimized-handler.js +180 -0
  38. package/dist/pipes/validation.pipe.d.ts +10 -10
  39. package/dist/pipes/validation.pipe.js +4 -4
  40. package/dist/plugins/compression.d.ts +75 -0
  41. package/dist/plugins/compression.d.ts.map +1 -0
  42. package/dist/plugins/compression.js +125 -0
  43. package/dist/ultra-optimized-handler.d.ts +51 -0
  44. package/dist/ultra-optimized-handler.d.ts.map +1 -0
  45. package/dist/ultra-optimized-handler.js +302 -0
  46. package/package.json +17 -10
@@ -1,6 +1,6 @@
1
1
  import "reflect-metadata";
2
2
  /**
3
- * Parameter Decorators for ElysiaJS Framework
3
+ * Parameter Decorators for WynkJS Framework
4
4
  * Extract data from request context
5
5
  */
6
6
  export type ParamType = "body" | "param" | "query" | "headers" | "request" | "response" | "context" | "user" | "file" | "files";
@@ -80,7 +80,7 @@ export declare function Res(): ParameterDecorator;
80
80
  */
81
81
  export declare const Response: typeof Res;
82
82
  /**
83
- * @Context decorator - Injects full Elysia context
83
+ * @Context decorator - Injects full WynkJS context
84
84
  * @example
85
85
  * @Get()
86
86
  * getData(@Context() ctx: any) {}
@@ -98,7 +98,7 @@ export function Res() {
98
98
  */
99
99
  export const Response = Res;
100
100
  /**
101
- * @Context decorator - Injects full Elysia context
101
+ * @Context decorator - Injects full WynkJS context
102
102
  * @example
103
103
  * @Get()
104
104
  * getData(@Context() ctx: any) {}
@@ -66,12 +66,12 @@ export declare class ValidationPipe implements WynkPipeTransform {
66
66
  });
67
67
  transform(value: any, metadata: ArgumentMetadata): Promise<any>;
68
68
  /**
69
- * Format Elysia validation error
69
+ * Format WynkJS validation error
70
70
  * Called by ValidationExceptionFilter
71
71
  */
72
72
  formatError(exception: any): any;
73
73
  /**
74
- * Parse validation error from Elysia exception
74
+ * Parse validation error from WynkJS exception
75
75
  */
76
76
  private parseValidationError;
77
77
  /**
@@ -92,7 +92,7 @@ export class ValidationPipe {
92
92
  return value;
93
93
  }
94
94
  /**
95
- * Format Elysia validation error
95
+ * Format WynkJS validation error
96
96
  * Called by ValidationExceptionFilter
97
97
  */
98
98
  formatError(exception) {
@@ -105,7 +105,7 @@ export class ValidationPipe {
105
105
  return this.defaultFormatError(validationError);
106
106
  }
107
107
  /**
108
- * Parse validation error from Elysia exception
108
+ * Parse validation error from WynkJS exception
109
109
  */
110
110
  parseValidationError(exception) {
111
111
  let validationData;
package/dist/factory.d.ts CHANGED
@@ -1,31 +1,43 @@
1
1
  import { Elysia } from "elysia";
2
2
  import "reflect-metadata";
3
3
  import { ErrorFormatter } from "./decorators/formatter.decorators";
4
+ import { CorsOptions } from "./cors";
4
5
  /**
5
6
  * Application Factory for WynkJS Framework
6
7
  * Creates and configures Elysia app with all decorators support
7
8
  */
8
9
  export interface ApplicationOptions {
9
- cors?: boolean | any;
10
+ cors?: boolean | CorsOptions;
10
11
  globalPrefix?: string;
11
12
  logger?: boolean;
12
13
  validationErrorFormatter?: ErrorFormatter;
14
+ providers?: any[];
13
15
  }
14
16
  export declare class WynkFramework {
15
17
  private app;
16
18
  private controllers;
19
+ private providers;
17
20
  private globalGuards;
18
21
  private globalInterceptors;
19
22
  private globalPipes;
20
23
  private globalFilters;
21
24
  private validationFormatter?;
25
+ private shutdownHandlersRegistered;
26
+ private globalPrefix?;
27
+ private isBuilt;
22
28
  constructor(options?: ApplicationOptions);
23
29
  /**
24
30
  * Static convenience creator to align with documentation examples
25
31
  */
26
32
  static create(options?: ApplicationOptions & {
27
33
  controllers?: any[];
34
+ providers?: any[];
28
35
  }): WynkFramework;
36
+ /**
37
+ * Register providers with the application
38
+ * Providers are singleton services that are initialized when the app starts
39
+ */
40
+ registerProviders(...providers: any[]): this;
29
41
  /**
30
42
  * Register controllers with the application
31
43
  */
@@ -46,10 +58,53 @@ export declare class WynkFramework {
46
58
  * Register global exception filters
47
59
  */
48
60
  useGlobalFilters(...filters: any[]): this;
61
+ /**
62
+ * Initialize all registered providers
63
+ * Providers with onModuleInit() method will be called
64
+ */
65
+ private initializeProviders;
49
66
  /**
50
67
  * Build the application - register all routes
51
68
  */
52
69
  build(): Promise<Elysia>;
70
+ /**
71
+ * Cleanup all providers when app shuts down
72
+ * Providers with onModuleDestroy() method will be called
73
+ */
74
+ private destroyProviders;
75
+ /**
76
+ * Use an Elysia plugin or middleware (fully compatible with Elysia.js ecosystem)
77
+ *
78
+ * This method directly proxies to Elysia's use() method, making all Elysia plugins
79
+ * and middleware work seamlessly with WynkJS.
80
+ *
81
+ * @param plugin - Any Elysia plugin or instance
82
+ * @returns this for method chaining
83
+ *
84
+ * @example
85
+ * ```typescript
86
+ * import { WynkFactory } from "wynkjs";
87
+ * import { compression } from "wynkjs";
88
+ * import { cors } from "@elysiajs/cors";
89
+ * import { swagger } from "@elysiajs/swagger";
90
+ * import { jwt } from "@elysiajs/jwt";
91
+ *
92
+ * const app = WynkFactory.create({
93
+ * controllers: [UserController],
94
+ * });
95
+ *
96
+ * // Use WynkJS built-in plugins
97
+ * app.use(compression({ threshold: 1024 }));
98
+ *
99
+ * // Use any Elysia plugin from npm
100
+ * app.use(cors());
101
+ * app.use(swagger());
102
+ * app.use(jwt({ name: 'jwt', secret: 'secret' }));
103
+ *
104
+ * await app.listen(3000);
105
+ * ```
106
+ */
107
+ use(...plugins: any[]): this;
53
108
  /**
54
109
  * Start listening on a port
55
110
  */
@@ -58,6 +113,11 @@ export declare class WynkFramework {
58
113
  * Get the underlying Elysia instance
59
114
  */
60
115
  getApp(): Elysia;
116
+ /**
117
+ * Handle an HTTP request
118
+ * Automatically builds the app if not already built
119
+ */
120
+ handle(request: Request): Promise<Response>;
61
121
  /**
62
122
  * Register a single controller
63
123
  */
@@ -73,6 +133,7 @@ export declare function createApp(options?: ApplicationOptions): WynkFramework;
73
133
  export declare class WynkFactory {
74
134
  static create(options?: ApplicationOptions & {
75
135
  controllers?: any[];
136
+ providers?: any[];
76
137
  }): WynkFramework;
77
138
  }
78
139
  export { WynkFramework as ElysiaFramework };
@@ -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,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAGnE;;;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;IAgL5C;;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;CAuWjC;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,mCAAmC,CAAC;AAEnE,OAAO,EAAE,WAAW,EAAa,MAAM,QAAQ,CAAC;AAOhD;;;GAGG;AAEH,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,OAAO,GAAG,WAAW,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wBAAwB,CAAC,EAAE,cAAc,CAAC;IAC1C,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC;CACnB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,SAAS,CAAa;IAC9B,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;IAC7C,OAAO,CAAC,0BAA0B,CAAS;IAC3C,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,GAAE,kBAAuB;IA2L5C;;OAEG;IACH,MAAM,CAAC,MAAM,CACX,OAAO,GAAE,kBAAkB,GAAG;QAC5B,WAAW,CAAC,EAAE,GAAG,EAAE,CAAC;QACpB,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC;KACd,GACL,aAAa;IAahB;;;OAGG;IACH,iBAAiB,CAAC,GAAG,SAAS,EAAE,GAAG,EAAE,GAAG,IAAI;IAK5C;;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;;;OAGG;YACW,mBAAmB;IAkCjC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAwD9B;;;OAGG;YACW,gBAAgB;IA0B9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,GAAG,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,GAAG,IAAI;IAK5B;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkCzC;;OAEG;IACH,MAAM,IAAI,MAAM;IAIhB;;;OAGG;IACG,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;IAOjD;;OAEG;YACW,kBAAkB;CAwLjC;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;QAC5B,WAAW,CAAC,EAAE,GAAG,EAAE,CAAC;QACpB,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC;KACd,GACL,aAAa;CAYjB;AAGD,OAAO,EAAE,aAAa,IAAI,eAAe,EAAE,CAAC"}
package/dist/factory.js CHANGED
@@ -2,22 +2,38 @@ import { Elysia } from "elysia";
2
2
  import "reflect-metadata";
3
3
  import { container } from "tsyringe";
4
4
  import { Value } from "@sinclair/typebox/value";
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";
5
+ import { executeExceptionFilters, } from "./decorators/exception.decorators";
9
6
  import { schemaRegistry } from "./schema-registry";
7
+ import { setupCors } from "./cors";
8
+ import { normalizePrefixPath } from "./global-prefix";
9
+ import { buildUltraOptimizedHandler, buildMiddlewareChain, } from "./ultra-optimized-handler";
10
10
  export class WynkFramework {
11
11
  app;
12
12
  controllers = [];
13
+ providers = []; // Store registered providers
13
14
  globalGuards = [];
14
15
  globalInterceptors = [];
15
16
  globalPipes = [];
16
17
  globalFilters = [];
17
18
  validationFormatter;
19
+ shutdownHandlersRegistered = false; // Prevent duplicate signal handlers
20
+ globalPrefix; // Store global prefix for route registration
21
+ isBuilt = false; // Track if build() has been called
18
22
  constructor(options = {}) {
19
23
  this.app = new Elysia();
20
24
  this.validationFormatter = options.validationErrorFormatter;
25
+ // Store global prefix for later use
26
+ if (options.globalPrefix) {
27
+ this.globalPrefix = normalizePrefixPath(options.globalPrefix);
28
+ }
29
+ // Register providers if provided
30
+ if (options.providers && options.providers.length > 0) {
31
+ this.providers.push(...options.providers);
32
+ }
33
+ // Apply CORS configuration
34
+ if (options.cors) {
35
+ setupCors(this.app, options.cors);
36
+ }
21
37
  // Configure Elysia's error handling for validation errors
22
38
  this.app.onError(({ code, error, set, request }) => {
23
39
  // Handle ValidationError from Elysia
@@ -137,13 +153,10 @@ export class WynkFramework {
137
153
  error: err.name || "Error",
138
154
  };
139
155
  });
140
- // Apply CORS if enabled
141
- if (options.cors) {
142
- // CORS configuration would go here
143
- }
144
156
  // Apply global prefix if specified
145
157
  if (options.globalPrefix) {
146
- // Global prefix handling
158
+ // Global prefix is handled during route registration
159
+ console.log(`✅ Global prefix configured: ${this.globalPrefix}`);
147
160
  }
148
161
  return this;
149
162
  }
@@ -152,11 +165,21 @@ export class WynkFramework {
152
165
  */
153
166
  static create(options = {}) {
154
167
  const app = new WynkFramework(options);
168
+ // Don't re-register providers/controllers if they were already added in constructor
169
+ // The constructor already handles options.providers
155
170
  if (options.controllers && options.controllers.length) {
156
171
  app.registerControllers(...options.controllers);
157
172
  }
158
173
  return app;
159
174
  }
175
+ /**
176
+ * Register providers with the application
177
+ * Providers are singleton services that are initialized when the app starts
178
+ */
179
+ registerProviders(...providers) {
180
+ this.providers.push(...providers);
181
+ return this;
182
+ }
160
183
  /**
161
184
  * Register controllers with the application
162
185
  */
@@ -192,10 +215,42 @@ export class WynkFramework {
192
215
  this.globalFilters.push(...filters);
193
216
  return this;
194
217
  }
218
+ /**
219
+ * Initialize all registered providers
220
+ * Providers with onModuleInit() method will be called
221
+ */
222
+ async initializeProviders() {
223
+ console.log(`🔧 Initializing ${this.providers.length} providers...`);
224
+ for (const ProviderClass of this.providers) {
225
+ try {
226
+ console.log(` ⚙️ Initializing provider: ${ProviderClass.name}`);
227
+ // Resolve provider instance from DI container
228
+ const instance = container.resolve(ProviderClass);
229
+ // Check if provider has onModuleInit lifecycle hook
230
+ if (typeof instance.onModuleInit === "function") {
231
+ await instance.onModuleInit();
232
+ console.log(` ✅ ${ProviderClass.name} initialized successfully`);
233
+ }
234
+ else {
235
+ // Just register in container for injection
236
+ console.log(` ✅ ${ProviderClass.name} registered in container`);
237
+ }
238
+ }
239
+ catch (error) {
240
+ console.error(` ❌ Failed to initialize provider ${ProviderClass.name}:`, error);
241
+ throw new Error(`Provider initialization failed for ${ProviderClass.name}: ${error.message}`);
242
+ }
243
+ }
244
+ console.log(`✅ All providers initialized successfully\n`);
245
+ }
195
246
  /**
196
247
  * Build the application - register all routes
197
248
  */
198
249
  async build() {
250
+ // Initialize providers first (database connections, etc.)
251
+ if (this.providers.length > 0) {
252
+ await this.initializeProviders();
253
+ }
199
254
  // Register global error handler if filters exist
200
255
  if (this.globalFilters.length > 0) {
201
256
  this.app.onError(async ({ error, set, request }) => {
@@ -234,8 +289,69 @@ export class WynkFramework {
234
289
  for (const ControllerClass of this.controllers) {
235
290
  await this.registerController(ControllerClass);
236
291
  }
292
+ this.isBuilt = true;
237
293
  return this.app;
238
294
  }
295
+ /**
296
+ * Cleanup all providers when app shuts down
297
+ * Providers with onModuleDestroy() method will be called
298
+ */
299
+ async destroyProviders() {
300
+ console.log(`\n🔧 Cleaning up ${this.providers.length} providers...`);
301
+ for (const ProviderClass of this.providers) {
302
+ try {
303
+ // Resolve provider instance from DI container
304
+ const instance = container.resolve(ProviderClass);
305
+ // Check if provider has onModuleDestroy lifecycle hook
306
+ if (typeof instance.onModuleDestroy === "function") {
307
+ console.log(` 🧹 Destroying provider: ${ProviderClass.name}`);
308
+ await instance.onModuleDestroy();
309
+ console.log(` ✅ ${ProviderClass.name} destroyed successfully`);
310
+ }
311
+ }
312
+ catch (error) {
313
+ console.error(` ❌ Failed to destroy provider ${ProviderClass.name}:`, error);
314
+ // Continue cleanup even if one provider fails
315
+ }
316
+ }
317
+ console.log(`✅ All providers cleaned up\n`);
318
+ }
319
+ /**
320
+ * Use an Elysia plugin or middleware (fully compatible with Elysia.js ecosystem)
321
+ *
322
+ * This method directly proxies to Elysia's use() method, making all Elysia plugins
323
+ * and middleware work seamlessly with WynkJS.
324
+ *
325
+ * @param plugin - Any Elysia plugin or instance
326
+ * @returns this for method chaining
327
+ *
328
+ * @example
329
+ * ```typescript
330
+ * import { WynkFactory } from "wynkjs";
331
+ * import { compression } from "wynkjs";
332
+ * import { cors } from "@elysiajs/cors";
333
+ * import { swagger } from "@elysiajs/swagger";
334
+ * import { jwt } from "@elysiajs/jwt";
335
+ *
336
+ * const app = WynkFactory.create({
337
+ * controllers: [UserController],
338
+ * });
339
+ *
340
+ * // Use WynkJS built-in plugins
341
+ * app.use(compression({ threshold: 1024 }));
342
+ *
343
+ * // Use any Elysia plugin from npm
344
+ * app.use(cors());
345
+ * app.use(swagger());
346
+ * app.use(jwt({ name: 'jwt', secret: 'secret' }));
347
+ *
348
+ * await app.listen(3000);
349
+ * ```
350
+ */
351
+ use(...plugins) {
352
+ this.app.use(...plugins);
353
+ return this;
354
+ }
239
355
  /**
240
356
  * Start listening on a port
241
357
  */
@@ -243,6 +359,29 @@ export class WynkFramework {
243
359
  await this.build();
244
360
  this.app.listen(port);
245
361
  console.log(`🚀 Application is running on http://localhost:${port}`);
362
+ // Register signal handlers only once to prevent memory leaks
363
+ if (!this.shutdownHandlersRegistered) {
364
+ this.shutdownHandlersRegistered = true;
365
+ // Setup graceful shutdown handlers
366
+ const gracefulShutdown = async (signal) => {
367
+ console.log(`\n📡 Received ${signal}, shutting down gracefully...`);
368
+ try {
369
+ // Cleanup providers (close database connections, etc.)
370
+ await this.destroyProviders();
371
+ // Stop the Elysia server
372
+ await this.app.stop();
373
+ console.log("👋 Application shut down successfully");
374
+ process.exit(0);
375
+ }
376
+ catch (error) {
377
+ console.error("❌ Error during shutdown:", error);
378
+ process.exit(1);
379
+ }
380
+ };
381
+ // Register signal handlers (only once)
382
+ process.once("SIGTERM", () => gracefulShutdown("SIGTERM"));
383
+ process.once("SIGINT", () => gracefulShutdown("SIGINT"));
384
+ }
246
385
  }
247
386
  /**
248
387
  * Get the underlying Elysia instance
@@ -250,6 +389,16 @@ export class WynkFramework {
250
389
  getApp() {
251
390
  return this.app;
252
391
  }
392
+ /**
393
+ * Handle an HTTP request
394
+ * Automatically builds the app if not already built
395
+ */
396
+ async handle(request) {
397
+ if (!this.isBuilt) {
398
+ await this.build();
399
+ }
400
+ return this.app.handle(request);
401
+ }
253
402
  /**
254
403
  * Register a single controller
255
404
  */
@@ -273,7 +422,11 @@ export class WynkFramework {
273
422
  // Get @Use() middleware (simple pattern like user's working code)
274
423
  const controllerUses = Reflect.getMetadata("uses", ControllerClass) || [];
275
424
  for (const route of routes) {
276
- const fullPath = basePath + route.path;
425
+ // Apply global prefix to route path
426
+ let fullPath = basePath + route.path;
427
+ if (this.globalPrefix) {
428
+ fullPath = this.globalPrefix + fullPath;
429
+ }
277
430
  const method = route.method.toLowerCase();
278
431
  const methodName = route.methodName;
279
432
  // Get method-specific metadata
@@ -284,19 +437,24 @@ export class WynkFramework {
284
437
  // Get @Use() middleware for this method
285
438
  const methodUses = Reflect.getMetadata("uses", instance, methodName) || [];
286
439
  const params = Reflect.getMetadata("params", instance, methodName) || [];
440
+ // Sort params once during registration, not on every request
441
+ if (params.length > 0) {
442
+ params.sort((a, b) => a.index - b.index);
443
+ }
287
444
  const httpCode = Reflect.getMetadata("route:httpCode", instance, methodName);
288
445
  const headers = Reflect.getMetadata("route:headers", instance, methodName);
289
446
  const redirect = Reflect.getMetadata("route:redirect", instance, methodName);
290
- // Combine guards, interceptors, pipes, filters (global -> controller -> method)
447
+ // Combine guards, interceptors, pipes, filters
448
+ // Order: method -> controller -> global (method is innermost/closest to handler)
291
449
  const allGuards = [
292
450
  ...this.globalGuards,
293
451
  ...controllerGuards,
294
452
  ...methodGuards,
295
453
  ];
296
454
  const allInterceptors = [
297
- ...this.globalInterceptors,
298
- ...controllerInterceptors,
299
455
  ...methodInterceptors,
456
+ ...controllerInterceptors,
457
+ ...this.globalInterceptors,
300
458
  ];
301
459
  const allPipes = [
302
460
  ...this.globalPipes,
@@ -314,178 +472,29 @@ export class WynkFramework {
314
472
  if (bodySchema && !routeOptions.body) {
315
473
  routeOptions.body = bodySchema;
316
474
  }
317
- // Create route handler
318
- const handler = async (ctx) => {
319
- try {
320
- // Create execution context
321
- const executionContext = createExecutionContext(ctx, instance[methodName], ControllerClass);
322
- // Execute guards
323
- if (allGuards.length > 0) {
324
- const canActivate = await executeGuards(allGuards, executionContext);
325
- if (!canActivate) {
326
- throw new HttpException("Forbidden", 403, "Access denied");
327
- }
328
- }
329
- // Prepare handler with parameters and pipes
330
- const executeHandler = async () => {
331
- // Build arguments for the controller method
332
- const args = [];
333
- if (params.length === 0) {
334
- // No parameter decorators, pass full context
335
- args.push(ctx);
336
- }
337
- else {
338
- // Sort params by index
339
- params.sort((a, b) => a.index - b.index);
340
- for (const param of params) {
341
- let value;
342
- // Extract value based on type
343
- switch (param.type) {
344
- case "body":
345
- value = param.data ? ctx.body?.[param.data] : ctx.body;
346
- break;
347
- case "param":
348
- value = param.data ? ctx.params?.[param.data] : ctx.params;
349
- break;
350
- case "query":
351
- value = param.data ? ctx.query?.[param.data] : ctx.query;
352
- break;
353
- case "headers":
354
- value = param.data
355
- ? ctx.headers?.get?.(param.data) ||
356
- ctx.request?.headers?.get?.(param.data)
357
- : ctx.headers || ctx.request?.headers;
358
- break;
359
- case "request":
360
- value = ctx.request || ctx;
361
- break;
362
- case "response":
363
- value = ctx.set || ctx.response;
364
- break;
365
- case "context":
366
- if (param.data) {
367
- // Access nested property like "session.userId"
368
- const keys = param.data.split(".");
369
- value = keys.reduce((obj, key) => obj?.[key], ctx);
370
- }
371
- else {
372
- value = ctx;
373
- }
374
- break;
375
- case "user":
376
- value = param.data ? ctx.user?.[param.data] : ctx.user;
377
- break;
378
- case "file":
379
- value = ctx.body?.file || ctx.file;
380
- break;
381
- case "files":
382
- value = ctx.body?.files || ctx.files;
383
- break;
384
- }
385
- // Apply pipes if any
386
- if (param.pipes && param.pipes.length > 0) {
387
- const metadata = {
388
- type: param.type,
389
- data: param.data,
390
- };
391
- value = await executePipes(param.pipes, value, metadata);
392
- }
393
- // Apply global/controller/method pipes
394
- if (allPipes.length > 0) {
395
- const metadata = {
396
- type: param.type,
397
- data: param.data,
398
- };
399
- value = await executePipes(allPipes, value, metadata);
400
- }
401
- args[param.index] = value;
402
- }
403
- }
404
- // Call controller method
405
- return await instance[methodName].apply(instance, args);
406
- };
407
- // Execute interceptors
408
- let result;
409
- if (allInterceptors.length > 0) {
410
- result = await executeInterceptors(allInterceptors, executionContext, executeHandler);
411
- }
412
- else {
413
- result = await executeHandler();
414
- }
415
- // Handle redirect
416
- if (redirect) {
417
- ctx.set.redirect = redirect.url;
418
- ctx.set.status = redirect.statusCode;
419
- return;
420
- }
421
- // Set custom HTTP code
422
- if (httpCode) {
423
- ctx.set.status = httpCode;
424
- }
425
- // Set custom headers
426
- if (headers) {
427
- Object.entries(headers).forEach(([key, value]) => {
428
- ctx.set.headers[key] = value;
429
- });
430
- }
431
- return result;
432
- }
433
- catch (error) {
434
- console.log("🔴 ERROR CAUGHT IN FACTORY");
435
- console.log("allFilters.length:", allFilters.length);
436
- console.log("Error:", error?.message);
437
- // Execute exception filters
438
- if (allFilters.length > 0) {
439
- console.log("✅ Executing exception filters...");
440
- const executionContext = createExecutionContext(ctx, instance[methodName], ControllerClass);
441
- try {
442
- const result = await executeExceptionFilters(allFilters, error, executionContext);
443
- if (result) {
444
- if (result.statusCode) {
445
- ctx.set.status = result.statusCode;
446
- }
447
- return result;
448
- }
449
- }
450
- catch (filterError) {
451
- // If filter doesn't handle it, continue to default error handling
452
- error = filterError;
453
- }
454
- }
455
- else {
456
- console.log("❌ No filters registered for this route");
457
- }
458
- // Default error handling
459
- if (error instanceof HttpException) {
460
- ctx.set.status = error.getStatus();
461
- return error.getResponse();
462
- }
463
- // Unknown error
464
- ctx.set.status = 500;
465
- return {
466
- statusCode: 500,
467
- message: error.message || "Internal server error",
468
- error: "Internal Server Error",
469
- };
470
- }
471
- };
475
+ // ULTRA-OPTIMIZED HANDLER - Use specialized builder
476
+ // This eliminates nested async/await and IIFEs for maximum performance
477
+ const handler = buildUltraOptimizedHandler({
478
+ instance,
479
+ methodName,
480
+ ControllerClass,
481
+ params,
482
+ allGuards,
483
+ allInterceptors,
484
+ allPipes,
485
+ allFilters,
486
+ httpCode,
487
+ headers,
488
+ redirect,
489
+ routePath: route.path,
490
+ routeMethod: method.toUpperCase(),
491
+ });
472
492
  // Wrap handler with @Use() middleware if present
473
493
  // Combine controller and method middleware
474
494
  const allUses = [...controllerUses, ...methodUses];
475
495
  let finalHandler = handler;
476
496
  if (allUses.length > 0) {
477
- console.log(`🔗 Building middleware chain for ${method} ${fullPath}:`);
478
- console.log(` Middleware count: ${allUses.length}`);
479
- allUses.forEach((m, i) => console.log(` [${i}] ${m.name || "anonymous"}`));
480
- // Build middleware chain using reduce (O(n) complexity)
481
- // Builds from right to left: handler <- middleware[n-1] <- ... <- middleware[0]
482
- // Executes left to right: middleware[0] -> ... -> middleware[n-1] -> handler
483
- finalHandler = allUses.reduceRight((next, middleware, index) => {
484
- return async (ctx) => {
485
- console.log(`▶️ Executing middleware [${index}]: ${middleware.name || "anonymous"}`);
486
- return await middleware(ctx, () => next(ctx));
487
- };
488
- }, handler);
497
+ finalHandler = buildMiddlewareChain(handler, allUses);
489
498
  }
490
499
  // Register route with Elysia
491
500
  const elysiaOptions = {};
@@ -536,6 +545,8 @@ export function createApp(options = {}) {
536
545
  export class WynkFactory {
537
546
  static create(options = {}) {
538
547
  const app = new WynkFramework(options);
548
+ // Don't re-register providers if they were already added in constructor
549
+ // The constructor already handles options.providers
539
550
  if (options.controllers) {
540
551
  app.registerControllers(...options.controllers);
541
552
  }