wynkjs 1.0.4 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +296 -313
- package/dist/cors.d.ts +28 -0
- package/dist/cors.d.ts.map +1 -0
- package/dist/cors.js +121 -0
- package/dist/decorators/exception.decorators.d.ts +1 -0
- package/dist/decorators/exception.decorators.d.ts.map +1 -1
- package/dist/decorators/exception.decorators.js +20 -3
- package/dist/decorators/guard.decorators.d.ts.map +1 -1
- package/dist/decorators/guard.decorators.js +11 -5
- package/dist/decorators/http.decorators.d.ts +8 -3
- package/dist/decorators/http.decorators.d.ts.map +1 -1
- package/dist/decorators/http.decorators.js +9 -2
- package/dist/decorators/interceptor.advanced.d.ts +9 -9
- package/dist/decorators/interceptor.advanced.d.ts.map +1 -1
- package/dist/decorators/interceptor.advanced.js +7 -7
- package/dist/decorators/interceptor.decorators.d.ts +9 -7
- package/dist/decorators/interceptor.decorators.d.ts.map +1 -1
- package/dist/decorators/interceptor.decorators.js +29 -18
- package/dist/decorators/param.decorators.d.ts +2 -2
- package/dist/decorators/param.decorators.js +1 -1
- package/dist/decorators/pipe.decorators.d.ts +2 -2
- package/dist/decorators/pipe.decorators.js +2 -2
- package/dist/factory.d.ts +29 -1
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +155 -180
- package/dist/global-prefix.d.ts +49 -0
- package/dist/global-prefix.d.ts.map +1 -0
- package/dist/global-prefix.js +155 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/interfaces/interceptor.interface.d.ts +15 -0
- package/dist/interfaces/interceptor.interface.d.ts.map +1 -0
- package/dist/interfaces/interceptor.interface.js +1 -0
- package/dist/optimized-handler.d.ts +31 -0
- package/dist/optimized-handler.d.ts.map +1 -0
- package/dist/optimized-handler.js +180 -0
- package/dist/pipes/validation.pipe.d.ts +10 -10
- package/dist/pipes/validation.pipe.js +4 -4
- package/dist/ultra-optimized-handler.d.ts +51 -0
- package/dist/ultra-optimized-handler.d.ts.map +1 -0
- package/dist/ultra-optimized-handler.js +302 -0
- package/package.json +10 -8
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { Elysia } from "elysia";
|
|
2
|
+
/**
|
|
3
|
+
* Apply global prefix to all routes
|
|
4
|
+
* @param app - Elysia instance
|
|
5
|
+
* @param prefix - Prefix string (e.g., '/api', '/v1')
|
|
6
|
+
* @param options - Additional options like route exclusions
|
|
7
|
+
* @returns Modified Elysia instance
|
|
8
|
+
*/
|
|
9
|
+
export function applyGlobalPrefix(app, prefix) {
|
|
10
|
+
// Normalize prefix
|
|
11
|
+
let prefixStr;
|
|
12
|
+
let excludedRoutes = [];
|
|
13
|
+
if (typeof prefix === "string") {
|
|
14
|
+
prefixStr = prefix;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
prefixStr = prefix.prefix;
|
|
18
|
+
excludedRoutes = prefix.exclude || [];
|
|
19
|
+
}
|
|
20
|
+
// Validate and normalize prefix
|
|
21
|
+
prefixStr = normalizePrefixPath(prefixStr);
|
|
22
|
+
if (!prefixStr) {
|
|
23
|
+
console.warn("⚠️ Global prefix is empty after normalization. Skipping prefix setup.");
|
|
24
|
+
return app;
|
|
25
|
+
}
|
|
26
|
+
// Create a new Elysia instance with the prefix
|
|
27
|
+
const prefixedApp = new Elysia({ prefix: prefixStr });
|
|
28
|
+
// Note: Elysia's prefix option automatically applies to all routes
|
|
29
|
+
// registered on this instance. We'll return the prefixed instance.
|
|
30
|
+
console.log(`✅ Global prefix applied: ${prefixStr}`);
|
|
31
|
+
if (excludedRoutes.length > 0) {
|
|
32
|
+
console.log(` Excluded routes: ${excludedRoutes.join(", ")}`);
|
|
33
|
+
}
|
|
34
|
+
return prefixedApp;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Normalize prefix path
|
|
38
|
+
* - Ensures it starts with /
|
|
39
|
+
* - Removes trailing /
|
|
40
|
+
* - Validates format
|
|
41
|
+
* @param prefix - Raw prefix string
|
|
42
|
+
* @returns Normalized prefix
|
|
43
|
+
*/
|
|
44
|
+
export function normalizePrefixPath(prefix) {
|
|
45
|
+
if (!prefix) {
|
|
46
|
+
return "";
|
|
47
|
+
}
|
|
48
|
+
let normalized = prefix.trim();
|
|
49
|
+
// Ensure starts with /
|
|
50
|
+
if (!normalized.startsWith("/")) {
|
|
51
|
+
normalized = "/" + normalized;
|
|
52
|
+
}
|
|
53
|
+
// Remove trailing /
|
|
54
|
+
if (normalized.endsWith("/") && normalized.length > 1) {
|
|
55
|
+
normalized = normalized.slice(0, -1);
|
|
56
|
+
}
|
|
57
|
+
// Validate format (no spaces, no special chars except /, -, _)
|
|
58
|
+
const validPattern = /^\/[\w\-\/]*$/;
|
|
59
|
+
if (!validPattern.test(normalized)) {
|
|
60
|
+
throw new Error(`Invalid global prefix format: "${prefix}". Only alphanumeric, hyphens, underscores, and forward slashes are allowed.`);
|
|
61
|
+
}
|
|
62
|
+
return normalized;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Check if a route should be excluded from global prefix
|
|
66
|
+
* @param path - Route path
|
|
67
|
+
* @param excludedRoutes - List of routes to exclude
|
|
68
|
+
* @returns true if route should be excluded
|
|
69
|
+
*/
|
|
70
|
+
export function isRouteExcluded(path, excludedRoutes) {
|
|
71
|
+
if (!excludedRoutes || excludedRoutes.length === 0) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
// Normalize the path
|
|
75
|
+
const normalizedPath = path.startsWith("/") ? path : "/" + path;
|
|
76
|
+
// Check for exact match or wildcard match
|
|
77
|
+
return excludedRoutes.some((excluded) => {
|
|
78
|
+
// Exact match
|
|
79
|
+
if (excluded === normalizedPath) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
// Wildcard match (e.g., '/health/*')
|
|
83
|
+
if (excluded.endsWith("/*")) {
|
|
84
|
+
const base = excluded.slice(0, -2);
|
|
85
|
+
return normalizedPath.startsWith(base);
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Apply global prefix to an existing Elysia app by wrapping it
|
|
92
|
+
* This is useful when you want to add prefix to already configured app
|
|
93
|
+
* @param app - Existing Elysia instance
|
|
94
|
+
* @param prefix - Prefix to apply
|
|
95
|
+
* @returns New Elysia instance with prefix
|
|
96
|
+
*/
|
|
97
|
+
export function wrapWithPrefix(app, prefix) {
|
|
98
|
+
let prefixStr;
|
|
99
|
+
let excludedRoutes = [];
|
|
100
|
+
if (typeof prefix === "string") {
|
|
101
|
+
prefixStr = prefix;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
prefixStr = prefix.prefix;
|
|
105
|
+
excludedRoutes = prefix.exclude || [];
|
|
106
|
+
}
|
|
107
|
+
prefixStr = normalizePrefixPath(prefixStr);
|
|
108
|
+
// Create wrapper app with prefix
|
|
109
|
+
const wrapper = new Elysia();
|
|
110
|
+
// Mount the original app under the prefix
|
|
111
|
+
wrapper.use(new Elysia({
|
|
112
|
+
prefix: prefixStr,
|
|
113
|
+
}).use(app));
|
|
114
|
+
console.log(`✅ Wrapped existing app with prefix: ${prefixStr}`);
|
|
115
|
+
return wrapper;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Validate global prefix configuration
|
|
119
|
+
* @param prefix - Prefix configuration
|
|
120
|
+
* @returns true if valid, throws error if invalid
|
|
121
|
+
*/
|
|
122
|
+
export function validateGlobalPrefix(prefix) {
|
|
123
|
+
if (typeof prefix === "string") {
|
|
124
|
+
// Validate string prefix
|
|
125
|
+
if (prefix.length === 0) {
|
|
126
|
+
throw new Error("Global prefix cannot be an empty string");
|
|
127
|
+
}
|
|
128
|
+
normalizePrefixPath(prefix); // This will throw if invalid
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
if (typeof prefix === "object" && prefix !== null) {
|
|
132
|
+
// Validate object prefix
|
|
133
|
+
if (!prefix.prefix) {
|
|
134
|
+
throw new Error("GlobalPrefixOptions must have a 'prefix' property");
|
|
135
|
+
}
|
|
136
|
+
if (typeof prefix.prefix !== "string") {
|
|
137
|
+
throw new Error("Global prefix must be a string");
|
|
138
|
+
}
|
|
139
|
+
normalizePrefixPath(prefix.prefix); // This will throw if invalid
|
|
140
|
+
// Validate exclude array
|
|
141
|
+
if (prefix.exclude !== undefined) {
|
|
142
|
+
if (!Array.isArray(prefix.exclude)) {
|
|
143
|
+
throw new Error("GlobalPrefixOptions.exclude must be an array");
|
|
144
|
+
}
|
|
145
|
+
// Validate each excluded route
|
|
146
|
+
prefix.exclude.forEach((route) => {
|
|
147
|
+
if (typeof route !== "string") {
|
|
148
|
+
throw new Error("All routes in GlobalPrefixOptions.exclude must be strings");
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
throw new Error("Global prefix must be string or GlobalPrefixOptions object");
|
|
155
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,10 @@ export * from "./decorators/database.decorators";
|
|
|
23
23
|
export * from "./dto";
|
|
24
24
|
export { schemaRegistry } from "./schema-registry";
|
|
25
25
|
export * from "./factory";
|
|
26
|
+
export type { CorsOptions } from "./cors";
|
|
27
|
+
export { setupCors, validateCorsOptions } from "./cors";
|
|
28
|
+
export type { GlobalPrefixOptions } from "./global-prefix";
|
|
29
|
+
export { applyGlobalPrefix, normalizePrefixPath, validateGlobalPrefix, wrapWithPrefix, } from "./global-prefix";
|
|
26
30
|
export * from "./testing";
|
|
27
31
|
/**
|
|
28
32
|
* Framework version
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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,mCAAmC,CAAC;AAClD,cAAc,6BAA6B,CAAC;AAI5C,cAAc,kCAAkC,CAAC;AASjD,cAAc,OAAO,CAAC;AAGtB,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGnD,cAAc,WAAW,CAAC;AAG1B,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,mCAAmC,CAAC;AAClD,cAAc,6BAA6B,CAAC;AAI5C,cAAc,kCAAkC,CAAC;AASjD,cAAc,OAAO,CAAC;AAGtB,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGnD,cAAc,WAAW,CAAC;AAG1B,YAAY,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAGxD,YAAY,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,GACf,MAAM,iBAAiB,CAAC;AAGzB,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
|
@@ -44,6 +44,8 @@ export * from "./dto";
|
|
|
44
44
|
export { schemaRegistry } from "./schema-registry";
|
|
45
45
|
// Application Factory
|
|
46
46
|
export * from "./factory";
|
|
47
|
+
export { setupCors, validateCorsOptions } from "./cors";
|
|
48
|
+
export { applyGlobalPrefix, normalizePrefixPath, validateGlobalPrefix, wrapWithPrefix, } from "./global-prefix";
|
|
47
49
|
// Testing Module
|
|
48
50
|
export * from "./testing";
|
|
49
51
|
/**
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface InterceptorContext {
|
|
2
|
+
getRequest<T = any>(): T;
|
|
3
|
+
getResponse<T = any>(): T;
|
|
4
|
+
getContext<T = any>(): T;
|
|
5
|
+
getHandler(): Function;
|
|
6
|
+
getClass(): any;
|
|
7
|
+
request?: any;
|
|
8
|
+
body?: any;
|
|
9
|
+
params?: any;
|
|
10
|
+
query?: any;
|
|
11
|
+
headers?: any;
|
|
12
|
+
path?: string;
|
|
13
|
+
method?: string;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=interceptor.interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"interceptor.interface.d.ts","sourceRoot":"","sources":["../../core/interfaces/interceptor.interface.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC;IACzB,WAAW,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC;IAC1B,UAAU,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC;IACzB,UAAU,IAAI,QAAQ,CAAC;IACvB,QAAQ,IAAI,GAAG,CAAC;IAEhB,OAAO,CAAC,EAAE,GAAG,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;IAEd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optimized Handler Builder for WynkJS
|
|
3
|
+
* Builds route handlers conditionally - only adds overhead when features are actually used
|
|
4
|
+
*/
|
|
5
|
+
import { ParamMetadata } from "./decorators/param.decorators";
|
|
6
|
+
export interface HandlerBuildOptions {
|
|
7
|
+
instance: any;
|
|
8
|
+
methodName: string;
|
|
9
|
+
ControllerClass: any;
|
|
10
|
+
params: ParamMetadata[];
|
|
11
|
+
allGuards: any[];
|
|
12
|
+
allInterceptors: any[];
|
|
13
|
+
allPipes: any[];
|
|
14
|
+
allFilters: any[];
|
|
15
|
+
httpCode?: number;
|
|
16
|
+
headers?: Record<string, string>;
|
|
17
|
+
redirect?: {
|
|
18
|
+
url: string;
|
|
19
|
+
statusCode: number;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Build optimized handler - only include overhead when features are used
|
|
24
|
+
* This eliminates conditional checks on every request for unused features
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildOptimizedHandler(options: HandlerBuildOptions): (ctx: any) => Promise<any>;
|
|
27
|
+
/**
|
|
28
|
+
* Build middleware chain - only if middleware exists
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildMiddlewareChain(handler: (ctx: any) => Promise<any>, middlewares: any[]): (ctx: any) => Promise<any>;
|
|
31
|
+
//# sourceMappingURL=optimized-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"optimized-handler.d.ts","sourceRoot":"","sources":["../core/optimized-handler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAYH,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAE9D,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,GAAG,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,GAAG,CAAC;IACrB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,SAAS,EAAE,GAAG,EAAE,CAAC;IACjB,eAAe,EAAE,GAAG,EAAE,CAAC;IACvB,QAAQ,EAAE,GAAG,EAAE,CAAC;IAChB,UAAU,EAAE,GAAG,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;CAChD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,mBAAmB,GAC3B,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAyM5B;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,EACnC,WAAW,EAAE,GAAG,EAAE,GACjB,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAW5B"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optimized Handler Builder for WynkJS
|
|
3
|
+
* Builds route handlers conditionally - only adds overhead when features are actually used
|
|
4
|
+
*/
|
|
5
|
+
import { createExecutionContext, executeGuards, } from "./decorators/guard.decorators";
|
|
6
|
+
import { executeInterceptors } from "./decorators/interceptor.decorators";
|
|
7
|
+
import { executePipes } from "./decorators/pipe.decorators";
|
|
8
|
+
import { executeExceptionFilters, HttpException, } from "./decorators/exception.decorators";
|
|
9
|
+
/**
|
|
10
|
+
* Build optimized handler - only include overhead when features are used
|
|
11
|
+
* This eliminates conditional checks on every request for unused features
|
|
12
|
+
*/
|
|
13
|
+
export function buildOptimizedHandler(options) {
|
|
14
|
+
const { instance, methodName, ControllerClass, params, allGuards, allInterceptors, allPipes, allFilters, httpCode, headers, redirect, } = options;
|
|
15
|
+
// Pre-sort params once during registration (not on every request)
|
|
16
|
+
if (params.length > 0) {
|
|
17
|
+
params.sort((a, b) => a.index - b.index);
|
|
18
|
+
}
|
|
19
|
+
// Base parameter extractor and method executor
|
|
20
|
+
const extractParamsAndExecute = async (ctx) => {
|
|
21
|
+
if (params.length === 0) {
|
|
22
|
+
// No parameter decorators - pass full context
|
|
23
|
+
return await instance[methodName](ctx);
|
|
24
|
+
}
|
|
25
|
+
// Build arguments array
|
|
26
|
+
const args = new Array(params.length);
|
|
27
|
+
for (const param of params) {
|
|
28
|
+
let value;
|
|
29
|
+
// Extract value based on type (optimized switch)
|
|
30
|
+
switch (param.type) {
|
|
31
|
+
case "body":
|
|
32
|
+
value = param.data ? ctx.body?.[param.data] : ctx.body;
|
|
33
|
+
break;
|
|
34
|
+
case "param":
|
|
35
|
+
value = param.data ? ctx.params?.[param.data] : ctx.params;
|
|
36
|
+
break;
|
|
37
|
+
case "query":
|
|
38
|
+
value = param.data ? ctx.query?.[param.data] : ctx.query;
|
|
39
|
+
break;
|
|
40
|
+
case "headers":
|
|
41
|
+
value = param.data
|
|
42
|
+
? ctx.headers?.get?.(param.data) ||
|
|
43
|
+
ctx.request?.headers?.get?.(param.data)
|
|
44
|
+
: ctx.headers || ctx.request?.headers;
|
|
45
|
+
break;
|
|
46
|
+
case "request":
|
|
47
|
+
value = ctx.request || ctx;
|
|
48
|
+
break;
|
|
49
|
+
case "response":
|
|
50
|
+
value = ctx.set || ctx.response;
|
|
51
|
+
break;
|
|
52
|
+
case "context":
|
|
53
|
+
if (param.data) {
|
|
54
|
+
const keys = param.data.split(".");
|
|
55
|
+
value = keys.reduce((obj, key) => obj?.[key], ctx);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
value = ctx;
|
|
59
|
+
}
|
|
60
|
+
break;
|
|
61
|
+
case "user":
|
|
62
|
+
value = param.data ? ctx.user?.[param.data] : ctx.user;
|
|
63
|
+
break;
|
|
64
|
+
case "file":
|
|
65
|
+
value = ctx.body?.file || ctx.file;
|
|
66
|
+
break;
|
|
67
|
+
case "files":
|
|
68
|
+
value = ctx.body?.files || ctx.files;
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
// Apply parameter-specific pipes
|
|
72
|
+
if (param.pipes && param.pipes.length > 0) {
|
|
73
|
+
const metadata = {
|
|
74
|
+
type: param.type,
|
|
75
|
+
data: param.data,
|
|
76
|
+
};
|
|
77
|
+
value = await executePipes(param.pipes, value, metadata);
|
|
78
|
+
}
|
|
79
|
+
// Apply global/controller/method pipes
|
|
80
|
+
if (allPipes.length > 0) {
|
|
81
|
+
const metadata = {
|
|
82
|
+
type: param.type,
|
|
83
|
+
data: param.data,
|
|
84
|
+
};
|
|
85
|
+
value = await executePipes(allPipes, value, metadata);
|
|
86
|
+
}
|
|
87
|
+
args[param.index] = value;
|
|
88
|
+
}
|
|
89
|
+
return await instance[methodName].apply(instance, args);
|
|
90
|
+
};
|
|
91
|
+
// Build handler layers conditionally
|
|
92
|
+
let handler = extractParamsAndExecute;
|
|
93
|
+
// Wrap with interceptors (only if they exist)
|
|
94
|
+
if (allInterceptors.length > 0) {
|
|
95
|
+
const prevHandler = handler;
|
|
96
|
+
handler = async (ctx) => {
|
|
97
|
+
const executionContext = createExecutionContext(ctx, instance[methodName], ControllerClass);
|
|
98
|
+
return await executeInterceptors(allInterceptors, executionContext, () => prevHandler(ctx));
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
// Wrap with guards (only if they exist)
|
|
102
|
+
if (allGuards.length > 0) {
|
|
103
|
+
const prevHandler = handler;
|
|
104
|
+
handler = async (ctx) => {
|
|
105
|
+
const executionContext = createExecutionContext(ctx, instance[methodName], ControllerClass);
|
|
106
|
+
const canActivate = await executeGuards(allGuards, executionContext);
|
|
107
|
+
if (!canActivate) {
|
|
108
|
+
throw new HttpException("Forbidden", 403, "Access denied");
|
|
109
|
+
}
|
|
110
|
+
return await prevHandler(ctx);
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
// Final handler with response modifications
|
|
114
|
+
const finalHandler = async (ctx) => {
|
|
115
|
+
try {
|
|
116
|
+
const result = await handler(ctx);
|
|
117
|
+
// Handle redirect (if configured)
|
|
118
|
+
if (redirect) {
|
|
119
|
+
ctx.set.redirect = redirect.url;
|
|
120
|
+
ctx.set.status = redirect.statusCode;
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
// Set custom HTTP code (if configured)
|
|
124
|
+
if (httpCode) {
|
|
125
|
+
ctx.set.status = httpCode;
|
|
126
|
+
}
|
|
127
|
+
// Set custom headers (if configured)
|
|
128
|
+
if (headers) {
|
|
129
|
+
Object.entries(headers).forEach(([key, value]) => {
|
|
130
|
+
ctx.set.headers[key] = value;
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
catch (error) {
|
|
136
|
+
// Execute exception filters (only if they exist)
|
|
137
|
+
if (allFilters.length > 0) {
|
|
138
|
+
const executionContext = createExecutionContext(ctx, instance[methodName], ControllerClass);
|
|
139
|
+
try {
|
|
140
|
+
const result = await executeExceptionFilters(allFilters, error, executionContext);
|
|
141
|
+
if (result) {
|
|
142
|
+
if (result.statusCode) {
|
|
143
|
+
ctx.set.status = result.statusCode;
|
|
144
|
+
}
|
|
145
|
+
return result;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (filterError) {
|
|
149
|
+
error = filterError;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Default error handling
|
|
153
|
+
if (error instanceof HttpException) {
|
|
154
|
+
ctx.set.status = error.getStatus();
|
|
155
|
+
return error.getResponse();
|
|
156
|
+
}
|
|
157
|
+
ctx.set.status = 500;
|
|
158
|
+
return {
|
|
159
|
+
statusCode: 500,
|
|
160
|
+
message: error.message || "Internal server error",
|
|
161
|
+
error: "Internal Server Error",
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
return finalHandler;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Build middleware chain - only if middleware exists
|
|
169
|
+
*/
|
|
170
|
+
export function buildMiddlewareChain(handler, middlewares) {
|
|
171
|
+
if (middlewares.length === 0) {
|
|
172
|
+
return handler;
|
|
173
|
+
}
|
|
174
|
+
// Build middleware chain (removed console.log for performance)
|
|
175
|
+
return middlewares.reduceRight((next, middleware) => {
|
|
176
|
+
return async (ctx) => {
|
|
177
|
+
return await middleware(ctx, () => next(ctx));
|
|
178
|
+
};
|
|
179
|
+
}, handler);
|
|
180
|
+
}
|
|
@@ -15,9 +15,9 @@ export interface ArgumentMetadata {
|
|
|
15
15
|
data?: string;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
* Validation Error structure (
|
|
18
|
+
* Validation Error structure (WynkJS format)
|
|
19
19
|
*/
|
|
20
|
-
export interface
|
|
20
|
+
export interface WynkJSValidationError {
|
|
21
21
|
type: "validation";
|
|
22
22
|
on: "body" | "params" | "query" | "headers";
|
|
23
23
|
property: string;
|
|
@@ -36,36 +36,36 @@ export interface ElysiaValidationError {
|
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
38
|
* Base Validation Pipe
|
|
39
|
-
* Handles
|
|
39
|
+
* Handles WynkJS validation errors and formats them
|
|
40
40
|
*/
|
|
41
41
|
export declare class ValidationPipe implements ValidationPipeTransform {
|
|
42
42
|
protected options: {
|
|
43
|
-
exceptionFactory?: (errors:
|
|
43
|
+
exceptionFactory?: (errors: WynkJSValidationError) => any;
|
|
44
44
|
transform?: boolean;
|
|
45
45
|
whitelist?: boolean;
|
|
46
46
|
};
|
|
47
47
|
constructor(options?: {
|
|
48
|
-
exceptionFactory?: (errors:
|
|
48
|
+
exceptionFactory?: (errors: WynkJSValidationError) => any;
|
|
49
49
|
transform?: boolean;
|
|
50
50
|
whitelist?: boolean;
|
|
51
51
|
});
|
|
52
52
|
/**
|
|
53
|
-
* Transform method (not used for
|
|
53
|
+
* Transform method (not used for WynkJS validation, but required by interface)
|
|
54
54
|
*/
|
|
55
55
|
transform(value: any, metadata: ArgumentMetadata): any;
|
|
56
56
|
/**
|
|
57
|
-
* Format
|
|
57
|
+
* Format WynkJS validation error
|
|
58
58
|
* This is called by the exception filter
|
|
59
59
|
*/
|
|
60
60
|
formatError(exception: any): any;
|
|
61
61
|
/**
|
|
62
|
-
* Parse
|
|
62
|
+
* Parse WynkJS validation error from exception
|
|
63
63
|
*/
|
|
64
|
-
protected parseValidationError(exception: any):
|
|
64
|
+
protected parseValidationError(exception: any): WynkJSValidationError;
|
|
65
65
|
/**
|
|
66
66
|
* Default error formatting with custom errorMessage support
|
|
67
67
|
*/
|
|
68
|
-
protected defaultFormatError(error:
|
|
68
|
+
protected defaultFormatError(error: WynkJSValidationError, schemaKey?: string): any;
|
|
69
69
|
}
|
|
70
70
|
/**
|
|
71
71
|
* Custom Format Error Pipe
|
|
@@ -2,7 +2,7 @@ import "reflect-metadata";
|
|
|
2
2
|
import { schemaRegistry } from "../schema-registry";
|
|
3
3
|
/**
|
|
4
4
|
* Base Validation Pipe
|
|
5
|
-
* Handles
|
|
5
|
+
* Handles WynkJS validation errors and formats them
|
|
6
6
|
*/
|
|
7
7
|
export class ValidationPipe {
|
|
8
8
|
options;
|
|
@@ -10,13 +10,13 @@ export class ValidationPipe {
|
|
|
10
10
|
this.options = options || {};
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
|
-
* Transform method (not used for
|
|
13
|
+
* Transform method (not used for WynkJS validation, but required by interface)
|
|
14
14
|
*/
|
|
15
15
|
transform(value, metadata) {
|
|
16
16
|
return value;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
|
-
* Format
|
|
19
|
+
* Format WynkJS validation error
|
|
20
20
|
* This is called by the exception filter
|
|
21
21
|
*/
|
|
22
22
|
formatError(exception) {
|
|
@@ -30,7 +30,7 @@ export class ValidationPipe {
|
|
|
30
30
|
return this.defaultFormatError(validationError);
|
|
31
31
|
}
|
|
32
32
|
/**
|
|
33
|
-
* Parse
|
|
33
|
+
* Parse WynkJS validation error from exception
|
|
34
34
|
*/
|
|
35
35
|
parseValidationError(exception) {
|
|
36
36
|
let validationData;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ULTRA-OPTIMIZED Handler Builder for WynkJS
|
|
3
|
+
*
|
|
4
|
+
* KEY OPTIMIZATION: Eliminate nested async/await and IIFEs
|
|
5
|
+
*
|
|
6
|
+
* Performance findings:
|
|
7
|
+
* - Direct sync: 32ms per 1M calls
|
|
8
|
+
* - Single async: 42ms per 1M calls
|
|
9
|
+
* - Nested async + IIFE: 232ms per 1M calls (5.7x SLOWER!)
|
|
10
|
+
*
|
|
11
|
+
* Root cause: The current pattern creates an IIFE for every request:
|
|
12
|
+
* return (async () => { try { ... } catch { ... } })();
|
|
13
|
+
*
|
|
14
|
+
* This creates massive overhead because:
|
|
15
|
+
* 1. Creates a new Promise on EVERY request
|
|
16
|
+
* 2. Wraps it in a try-catch block
|
|
17
|
+
* 3. Immediately invokes it (IIFE pattern)
|
|
18
|
+
* 4. Then awaits the result
|
|
19
|
+
*
|
|
20
|
+
* Solution: Build a SINGLE async function at registration time,
|
|
21
|
+
* not nested functions that get called on every request.
|
|
22
|
+
*/
|
|
23
|
+
import { ParamMetadata } from "./decorators/param.decorators";
|
|
24
|
+
export interface HandlerBuildOptions {
|
|
25
|
+
instance: any;
|
|
26
|
+
methodName: string;
|
|
27
|
+
ControllerClass: any;
|
|
28
|
+
params: ParamMetadata[];
|
|
29
|
+
allGuards: any[];
|
|
30
|
+
allInterceptors: any[];
|
|
31
|
+
allPipes: any[];
|
|
32
|
+
allFilters: any[];
|
|
33
|
+
httpCode?: number;
|
|
34
|
+
headers?: Record<string, string>;
|
|
35
|
+
redirect?: {
|
|
36
|
+
url: string;
|
|
37
|
+
statusCode: number;
|
|
38
|
+
};
|
|
39
|
+
routePath: string;
|
|
40
|
+
routeMethod: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Ultra-optimized handler builder
|
|
44
|
+
* Builds a SINGLE async function, not nested closures
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildUltraOptimizedHandler(options: HandlerBuildOptions): (ctx: any) => Promise<any>;
|
|
47
|
+
/**
|
|
48
|
+
* Build middleware chain - same as before
|
|
49
|
+
*/
|
|
50
|
+
export declare function buildMiddlewareChain(handler: (ctx: any) => Promise<any>, middlewares: any[]): (ctx: any) => Promise<any>;
|
|
51
|
+
//# sourceMappingURL=ultra-optimized-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ultra-optimized-handler.d.ts","sourceRoot":"","sources":["../core/ultra-optimized-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAYH,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAE9D,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,GAAG,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,GAAG,CAAC;IACrB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,SAAS,EAAE,GAAG,EAAE,CAAC;IACjB,eAAe,EAAE,GAAG,EAAE,CAAC;IACvB,QAAQ,EAAE,GAAG,EAAE,CAAC;IAChB,UAAU,EAAE,GAAG,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,QAAQ,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,mBAAmB,GAC3B,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAiU5B;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,EACnC,WAAW,EAAE,GAAG,EAAE,GACjB,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAU5B"}
|