wynkjs 1.0.3 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +528 -348
- package/dist/cors.d.ts +28 -0
- package/dist/cors.d.ts.map +1 -0
- package/dist/cors.js +121 -0
- package/dist/database.d.ts +1 -1
- package/dist/database.js +1 -1
- package/dist/decorators/exception.advanced.d.ts +286 -18
- package/dist/decorators/exception.advanced.d.ts.map +1 -1
- package/dist/decorators/exception.advanced.js +410 -17
- package/dist/decorators/exception.decorators.d.ts +93 -2
- package/dist/decorators/exception.decorators.d.ts.map +1 -1
- package/dist/decorators/exception.decorators.js +140 -8
- package/dist/decorators/formatter.decorators.d.ts +93 -0
- package/dist/decorators/formatter.decorators.d.ts.map +1 -0
- package/dist/decorators/formatter.decorators.js +131 -0
- package/dist/decorators/guard.decorators.d.ts +2 -2
- 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 +10 -4
- 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 +4 -4
- package/dist/decorators/pipe.decorators.d.ts.map +1 -1
- package/dist/decorators/pipe.decorators.js +4 -4
- package/dist/dto.js +1 -1
- package/dist/factory.d.ts +30 -2
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +210 -186
- package/dist/filters/exception.filters.d.ts +124 -0
- package/dist/filters/exception.filters.d.ts.map +1 -0
- package/dist/filters/exception.filters.js +208 -0
- 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 +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- 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 +12 -12
- package/dist/pipes/validation.pipe.d.ts.map +1 -1
- package/dist/pipes/validation.pipe.js +43 -15
- package/dist/schema-registry.d.ts +51 -0
- package/dist/schema-registry.d.ts.map +1 -0
- package/dist/schema-registry.js +134 -0
- package/dist/testing/index.d.ts +2 -2
- package/dist/testing/index.js +2 -2
- 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 +17 -10
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exception.filters.d.ts","sourceRoot":"","sources":["../../core/filters/exception.filters.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,iBAAiB,EACjB,mBAAmB,EACpB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAElE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,qBAAa,uBAAwB,YAAW,mBAAmB;IACjE,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB;;;;;;;CAuChD;AAED;;;;;;;;;;;;;;;;;GAiBG;AAEH,qBAAa,uBACX,YAAW,mBAAmB,CAAC,iBAAiB,CAAC;IAEjD,KAAK,CAAC,SAAS,EAAE,iBAAiB,EAAE,OAAO,EAAE,gBAAgB;;;;;;;;IAmB7D;;;;OAIG;IACH,OAAO,CAAC,eAAe;CAuBxB;AAED;;;;;;GAMG;AACH,qBAAa,yBAA0B,YAAW,mBAAmB;IACnE,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB;;;;;;;CA2BhD;AAED;;;;GAIG;AACH,qBAAa,qBAAsB,YAAW,mBAAmB;IAC/D,KAAK,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,gBAAgB;;;;;;;;CA2BhD;AAED;;;;;;;;;;;;EAYE"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { HttpException, } from "../decorators/exception.decorators";
|
|
2
|
+
/**
|
|
3
|
+
* Database Exception Filter - Handles database errors ONLY (not HttpExceptions)
|
|
4
|
+
*
|
|
5
|
+
* This filter catches actual database errors (like unique constraint violations,
|
|
6
|
+
* foreign key errors, etc.) and converts them to user-friendly messages.
|
|
7
|
+
*
|
|
8
|
+
* It will NOT catch HttpException or its subclasses (ConflictException, etc.)
|
|
9
|
+
* that you throw manually - those will pass through to be handled correctly.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // Use as global filter
|
|
13
|
+
* app.useGlobalFilters(new DatabaseExceptionFilter());
|
|
14
|
+
*
|
|
15
|
+
* // Use on specific controller
|
|
16
|
+
* @UseFilters(DatabaseExceptionFilter)
|
|
17
|
+
* @Controller('/users')
|
|
18
|
+
* export class UserController {
|
|
19
|
+
* @Post()
|
|
20
|
+
* async create(@Body() data: any) {
|
|
21
|
+
* // If you throw manually, it passes through:
|
|
22
|
+
* if (await this.userExists(data.email)) {
|
|
23
|
+
* throw new ConflictException('User with this email already exists'); // ✅ Works correctly
|
|
24
|
+
* }
|
|
25
|
+
*
|
|
26
|
+
* // If database throws error, filter catches it:
|
|
27
|
+
* return await this.db.insert(users).values(data); // ❌ DB unique constraint error → caught by filter
|
|
28
|
+
* }
|
|
29
|
+
* }
|
|
30
|
+
*
|
|
31
|
+
* Handles these database error codes:
|
|
32
|
+
* - 23505: Unique constraint violation → 409 Conflict
|
|
33
|
+
* - 23503: Foreign key constraint violation → 400 Bad Request
|
|
34
|
+
* - 23502: Not null constraint violation → 400 Bad Request
|
|
35
|
+
*/
|
|
36
|
+
export class DatabaseExceptionFilter {
|
|
37
|
+
catch(exception, context) {
|
|
38
|
+
const response = context.getResponse();
|
|
39
|
+
const request = context.getRequest();
|
|
40
|
+
// Don't catch HttpException or its subclasses (like ConflictException)
|
|
41
|
+
// These are intentionally thrown by the user
|
|
42
|
+
if (exception instanceof HttpException) {
|
|
43
|
+
throw exception;
|
|
44
|
+
}
|
|
45
|
+
// Check for common database errors
|
|
46
|
+
let message = "Database error occurred";
|
|
47
|
+
let statusCode = 500;
|
|
48
|
+
if (exception.code === "23505" || exception.message?.includes("unique")) {
|
|
49
|
+
message = "Resource already exists";
|
|
50
|
+
statusCode = 409; // Conflict
|
|
51
|
+
}
|
|
52
|
+
else if (exception.code === "23503" ||
|
|
53
|
+
exception.message?.includes("foreign key")) {
|
|
54
|
+
message = "Referenced resource does not exist";
|
|
55
|
+
statusCode = 400;
|
|
56
|
+
}
|
|
57
|
+
else if (exception.code === "23502" ||
|
|
58
|
+
exception.message?.includes("not null")) {
|
|
59
|
+
message = "Required field is missing";
|
|
60
|
+
statusCode = 400;
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
statusCode,
|
|
64
|
+
error: "Database Error",
|
|
65
|
+
message,
|
|
66
|
+
timestamp: new Date().toISOString(),
|
|
67
|
+
path: request.url,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Not Found Exception Filter - Handles 404 errors with smart detection
|
|
73
|
+
*
|
|
74
|
+
* This filter is SMART - it only handles NotFoundExceptionFilter if:
|
|
75
|
+
* 1. The exception is NotFoundException, AND
|
|
76
|
+
* 2. No response data has been set (empty, null, empty array, or empty object)
|
|
77
|
+
*
|
|
78
|
+
* This allows it to be used globally without breaking routes that return
|
|
79
|
+
* legitimate empty responses or have their own error handling.
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* // ✅ Can be used globally - smart filtering
|
|
83
|
+
* app.useGlobalFilters(
|
|
84
|
+
* new NotFoundExceptionFilter(), // Safe to use globally now!
|
|
85
|
+
* new GlobalExceptionFilter()
|
|
86
|
+
* );
|
|
87
|
+
*
|
|
88
|
+
*/
|
|
89
|
+
export class NotFoundExceptionFilter {
|
|
90
|
+
catch(exception, context) {
|
|
91
|
+
const response = context.getResponse();
|
|
92
|
+
const request = context.getRequest();
|
|
93
|
+
const hasResponseData = this.hasResponseData(response);
|
|
94
|
+
if (hasResponseData) {
|
|
95
|
+
throw exception;
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
statusCode: exception.statusCode,
|
|
99
|
+
error: "Not Found",
|
|
100
|
+
message: exception.message || "Resource not found",
|
|
101
|
+
timestamp: new Date().toISOString(),
|
|
102
|
+
path: request.url,
|
|
103
|
+
suggestion: "Please check the resource ID or URL",
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Check if response has meaningful data
|
|
108
|
+
* Returns false for: null, undefined, {}, [], ""
|
|
109
|
+
* Returns true for: anything else
|
|
110
|
+
*/
|
|
111
|
+
hasResponseData(response) {
|
|
112
|
+
if (response === null || response === undefined) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
// Check for empty object
|
|
116
|
+
if (typeof response === "object" && !Array.isArray(response)) {
|
|
117
|
+
return Object.keys(response).length > 0;
|
|
118
|
+
}
|
|
119
|
+
// Check for empty array
|
|
120
|
+
if (Array.isArray(response)) {
|
|
121
|
+
return response.length > 0;
|
|
122
|
+
}
|
|
123
|
+
// Check for empty string
|
|
124
|
+
if (typeof response === "string") {
|
|
125
|
+
return response.length > 0;
|
|
126
|
+
}
|
|
127
|
+
// For numbers, booleans, etc. - consider them as having data
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* File Upload Exception Filter - Handles file upload errors
|
|
133
|
+
* @example
|
|
134
|
+
* @UseFilters(FileUploadExceptionFilter)
|
|
135
|
+
* @Post('/upload')
|
|
136
|
+
* async upload(@UploadedFile() file: any) {}
|
|
137
|
+
*/
|
|
138
|
+
export class FileUploadExceptionFilter {
|
|
139
|
+
catch(exception, context) {
|
|
140
|
+
const response = context.getResponse();
|
|
141
|
+
const request = context.getRequest();
|
|
142
|
+
// Don't catch HttpException or its subclasses
|
|
143
|
+
if (exception instanceof HttpException) {
|
|
144
|
+
throw exception;
|
|
145
|
+
}
|
|
146
|
+
let message = "File upload failed";
|
|
147
|
+
if (exception.message?.includes("size")) {
|
|
148
|
+
message = "File size exceeds limit";
|
|
149
|
+
}
|
|
150
|
+
else if (exception.message?.includes("type")) {
|
|
151
|
+
message = "Invalid file type";
|
|
152
|
+
}
|
|
153
|
+
else if (exception.message?.includes("required")) {
|
|
154
|
+
message = "File is required";
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
statusCode: 400,
|
|
158
|
+
error: "File Upload Error",
|
|
159
|
+
message,
|
|
160
|
+
timestamp: new Date().toISOString(),
|
|
161
|
+
path: request.url,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Global Exception Filter - Catches all unhandled exceptions
|
|
167
|
+
* @example
|
|
168
|
+
* app.useGlobalFilters(new GlobalExceptionFilter());
|
|
169
|
+
*/
|
|
170
|
+
export class GlobalExceptionFilter {
|
|
171
|
+
catch(exception, context) {
|
|
172
|
+
const response = context.getResponse();
|
|
173
|
+
const request = context.getRequest();
|
|
174
|
+
const statusCode = exception.statusCode || 500;
|
|
175
|
+
const message = exception.message || "Internal server error";
|
|
176
|
+
// Log the error for debugging
|
|
177
|
+
console.error("❌ Unhandled exception:", {
|
|
178
|
+
statusCode,
|
|
179
|
+
message,
|
|
180
|
+
path: request.url,
|
|
181
|
+
method: request.method,
|
|
182
|
+
stack: exception.stack,
|
|
183
|
+
});
|
|
184
|
+
return {
|
|
185
|
+
statusCode,
|
|
186
|
+
error: exception.name || "Error",
|
|
187
|
+
message,
|
|
188
|
+
timestamp: new Date().toISOString(),
|
|
189
|
+
path: request.url,
|
|
190
|
+
...(process.env.NODE_ENV === "development" && {
|
|
191
|
+
stack: exception.stack,
|
|
192
|
+
}),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
// ============================================================================
|
|
198
|
+
// KEY TAKEAWAYS
|
|
199
|
+
// ============================================================================
|
|
200
|
+
//
|
|
201
|
+
// 1. Order matters: Specific → General
|
|
202
|
+
// 2. Filters can re-throw exceptions they don't handle
|
|
203
|
+
// 3. HttpException and subclasses should pass through specialized filters
|
|
204
|
+
// 4. Global filters catch everything not handled by controller/method filters
|
|
205
|
+
// 5. Always have a GlobalExceptionFilter as the last filter (catch-all)
|
|
206
|
+
//
|
|
207
|
+
// ============================================================================
|
|
208
|
+
*/
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Elysia } from "elysia";
|
|
2
|
+
/**
|
|
3
|
+
* Global Prefix Module for WynkJS Framework
|
|
4
|
+
* Adds a prefix to all routes in the application
|
|
5
|
+
* Separated from factory.ts for better maintainability
|
|
6
|
+
*/
|
|
7
|
+
export interface GlobalPrefixOptions {
|
|
8
|
+
prefix: string;
|
|
9
|
+
exclude?: string[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Apply global prefix to all routes
|
|
13
|
+
* @param app - Elysia instance
|
|
14
|
+
* @param prefix - Prefix string (e.g., '/api', '/v1')
|
|
15
|
+
* @param options - Additional options like route exclusions
|
|
16
|
+
* @returns Modified Elysia instance
|
|
17
|
+
*/
|
|
18
|
+
export declare function applyGlobalPrefix(app: Elysia, prefix: string | GlobalPrefixOptions): any;
|
|
19
|
+
/**
|
|
20
|
+
* Normalize prefix path
|
|
21
|
+
* - Ensures it starts with /
|
|
22
|
+
* - Removes trailing /
|
|
23
|
+
* - Validates format
|
|
24
|
+
* @param prefix - Raw prefix string
|
|
25
|
+
* @returns Normalized prefix
|
|
26
|
+
*/
|
|
27
|
+
export declare function normalizePrefixPath(prefix: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Check if a route should be excluded from global prefix
|
|
30
|
+
* @param path - Route path
|
|
31
|
+
* @param excludedRoutes - List of routes to exclude
|
|
32
|
+
* @returns true if route should be excluded
|
|
33
|
+
*/
|
|
34
|
+
export declare function isRouteExcluded(path: string, excludedRoutes: string[]): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Apply global prefix to an existing Elysia app by wrapping it
|
|
37
|
+
* This is useful when you want to add prefix to already configured app
|
|
38
|
+
* @param app - Existing Elysia instance
|
|
39
|
+
* @param prefix - Prefix to apply
|
|
40
|
+
* @returns New Elysia instance with prefix
|
|
41
|
+
*/
|
|
42
|
+
export declare function wrapWithPrefix(app: Elysia, prefix: string | GlobalPrefixOptions): any;
|
|
43
|
+
/**
|
|
44
|
+
* Validate global prefix configuration
|
|
45
|
+
* @param prefix - Prefix configuration
|
|
46
|
+
* @returns true if valid, throws error if invalid
|
|
47
|
+
*/
|
|
48
|
+
export declare function validateGlobalPrefix(prefix: string | GlobalPrefixOptions): boolean;
|
|
49
|
+
//# sourceMappingURL=global-prefix.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"global-prefix.d.ts","sourceRoot":"","sources":["../core/global-prefix.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC;;;;GAIG;AAEH,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GAAG,mBAAmB,GACnC,GAAG,CAkCL;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA0B1D;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,cAAc,EAAE,MAAM,EAAE,GACvB,OAAO,CAuBT;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GAAG,mBAAmB,GACnC,GAAG,CA0BL;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,GAAG,mBAAmB,GACnC,OAAO,CA2CT"}
|
|
@@ -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
|
@@ -17,10 +17,16 @@ export * from "./decorators/interceptor.advanced";
|
|
|
17
17
|
export * from "./decorators/pipe.decorators";
|
|
18
18
|
export * from "./decorators/pipe.advanced";
|
|
19
19
|
export * from "./decorators/exception.decorators";
|
|
20
|
-
export * from "./decorators/
|
|
20
|
+
export * from "./decorators/formatter.decorators";
|
|
21
|
+
export * from "./filters/exception.filters";
|
|
21
22
|
export * from "./decorators/database.decorators";
|
|
22
23
|
export * from "./dto";
|
|
24
|
+
export { schemaRegistry } from "./schema-registry";
|
|
23
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";
|
|
24
30
|
export * from "./testing";
|
|
25
31
|
/**
|
|
26
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,
|
|
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
|
@@ -28,7 +28,8 @@ export * from "./decorators/pipe.advanced";
|
|
|
28
28
|
// Note: validation.pipe.ts is deprecated, use pipe.decorators.ts instead
|
|
29
29
|
// Exception Filters
|
|
30
30
|
export * from "./decorators/exception.decorators";
|
|
31
|
-
export * from "./decorators/
|
|
31
|
+
export * from "./decorators/formatter.decorators";
|
|
32
|
+
export * from "./filters/exception.filters";
|
|
32
33
|
// Database Registry (General-Purpose)
|
|
33
34
|
// Works with ANY ORM/ODM: Drizzle, Mongoose, Prisma, TypeORM, custom Database classes
|
|
34
35
|
export * from "./decorators/database.decorators";
|
|
@@ -39,8 +40,12 @@ export * from "./decorators/database.decorators";
|
|
|
39
40
|
// See: plugins/drizzle and plugins/mongoose
|
|
40
41
|
// DTO Utilities
|
|
41
42
|
export * from "./dto";
|
|
43
|
+
// Schema Registry for custom error messages
|
|
44
|
+
export { schemaRegistry } from "./schema-registry";
|
|
42
45
|
// Application Factory
|
|
43
46
|
export * from "./factory";
|
|
47
|
+
export { setupCors, validateCorsOptions } from "./cors";
|
|
48
|
+
export { applyGlobalPrefix, normalizePrefixPath, validateGlobalPrefix, wrapWithPrefix, } from "./global-prefix";
|
|
44
49
|
// Testing Module
|
|
45
50
|
export * from "./testing";
|
|
46
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"}
|