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
|
@@ -13,7 +13,7 @@ import "reflect-metadata";
|
|
|
13
13
|
* export class HttpWynkExceptionFilter implements WynkExceptionFilter {}
|
|
14
14
|
*
|
|
15
15
|
* @Catch() // Catches all exceptions
|
|
16
|
-
* export class
|
|
16
|
+
* export class AllExceptions implements WynkExceptionFilter {}
|
|
17
17
|
*/
|
|
18
18
|
export function Catch(...exceptions) {
|
|
19
19
|
return (target) => {
|
|
@@ -63,6 +63,9 @@ export class HttpException extends Error {
|
|
|
63
63
|
this.error = error;
|
|
64
64
|
this.name = "HttpException";
|
|
65
65
|
}
|
|
66
|
+
get status() {
|
|
67
|
+
return this.statusCode;
|
|
68
|
+
}
|
|
66
69
|
getStatus() {
|
|
67
70
|
return this.statusCode;
|
|
68
71
|
}
|
|
@@ -179,13 +182,27 @@ export async function executeExceptionFilters(filters, exception, context) {
|
|
|
179
182
|
// Check if filter handles this exception type
|
|
180
183
|
const catchTypes = Reflect.getMetadata("catch:exceptions", filterInstance.constructor);
|
|
181
184
|
if (!catchTypes || catchTypes.length === 0) {
|
|
182
|
-
// Catches all exceptions
|
|
183
|
-
|
|
185
|
+
// Catches all exceptions - try it
|
|
186
|
+
try {
|
|
187
|
+
return await filterInstance.catch(exception, context);
|
|
188
|
+
}
|
|
189
|
+
catch (err) {
|
|
190
|
+
// Filter didn't handle it, try next filter
|
|
191
|
+
exception = err;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
184
194
|
}
|
|
185
195
|
// Check if exception matches any of the catch types
|
|
186
196
|
for (const catchType of catchTypes) {
|
|
187
197
|
if (exception instanceof catchType) {
|
|
188
|
-
|
|
198
|
+
try {
|
|
199
|
+
return await filterInstance.catch(exception, context);
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
// Filter didn't handle it, try next filter
|
|
203
|
+
exception = err;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
189
206
|
}
|
|
190
207
|
}
|
|
191
208
|
}
|
|
@@ -216,7 +233,7 @@ export { HttpWynkExceptionFilter };
|
|
|
216
233
|
/**
|
|
217
234
|
* All exceptions filter - catches everything
|
|
218
235
|
*/
|
|
219
|
-
let
|
|
236
|
+
let AllExceptions = class AllExceptions {
|
|
220
237
|
catch(exception, context) {
|
|
221
238
|
const response = context.getResponse();
|
|
222
239
|
if (exception instanceof HttpException) {
|
|
@@ -236,7 +253,122 @@ let AllExceptionsFilter = class AllExceptionsFilter {
|
|
|
236
253
|
};
|
|
237
254
|
}
|
|
238
255
|
};
|
|
239
|
-
|
|
256
|
+
AllExceptions = __decorate([
|
|
240
257
|
Catch()
|
|
241
|
-
],
|
|
242
|
-
export {
|
|
258
|
+
], AllExceptions);
|
|
259
|
+
export { AllExceptions };
|
|
260
|
+
/**
|
|
261
|
+
* Authentication Exception Filter - Handles auth errors
|
|
262
|
+
*
|
|
263
|
+
* ⚠️ IMPORTANT: This catches ALL UnauthorizedException instances!
|
|
264
|
+
* Use on specific routes/controllers, not globally.
|
|
265
|
+
*
|
|
266
|
+
* @example
|
|
267
|
+
* // ✅ GOOD: Use on auth-protected controller
|
|
268
|
+
* @UseFilters(AuthenticationExceptionFilter)
|
|
269
|
+
* @Controller('/auth')
|
|
270
|
+
* export class AuthController {
|
|
271
|
+
* @Post('/login')
|
|
272
|
+
* async login() {
|
|
273
|
+
* throw new UnauthorizedException('Invalid credentials');
|
|
274
|
+
* }
|
|
275
|
+
* }
|
|
276
|
+
*/
|
|
277
|
+
export class AuthenticationException {
|
|
278
|
+
catch(exception, context) {
|
|
279
|
+
const response = context.getResponse();
|
|
280
|
+
const request = context.getRequest();
|
|
281
|
+
return {
|
|
282
|
+
statusCode: exception.statusCode,
|
|
283
|
+
error: "Authentication Failed",
|
|
284
|
+
message: exception.message || "Invalid credentials",
|
|
285
|
+
timestamp: new Date().toISOString(),
|
|
286
|
+
path: request.url,
|
|
287
|
+
hint: "Please check your authentication token or credentials",
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Authorization Exception Filter - Handles permission errors
|
|
293
|
+
*
|
|
294
|
+
* ⚠️ IMPORTANT: This catches ALL ForbiddenException instances!
|
|
295
|
+
* Use on specific routes/controllers, not globally.
|
|
296
|
+
*
|
|
297
|
+
* @example
|
|
298
|
+
* // ✅ GOOD: Use on admin-only controller
|
|
299
|
+
* @UseFilters(AuthorizationExceptionFilter)
|
|
300
|
+
* @Controller('/admin')
|
|
301
|
+
* export class AdminController {
|
|
302
|
+
* @Get('/users')
|
|
303
|
+
* async getAllUsers() {
|
|
304
|
+
* throw new ForbiddenException('Admin access required');
|
|
305
|
+
* }
|
|
306
|
+
* }
|
|
307
|
+
*/
|
|
308
|
+
export class AuthorizationException {
|
|
309
|
+
catch(exception, context) {
|
|
310
|
+
const response = context.getResponse();
|
|
311
|
+
const request = context.getRequest();
|
|
312
|
+
return {
|
|
313
|
+
statusCode: exception.statusCode,
|
|
314
|
+
error: "Authorization Failed",
|
|
315
|
+
message: exception.message ||
|
|
316
|
+
"You don't have permission to access this resource",
|
|
317
|
+
timestamp: new Date().toISOString(),
|
|
318
|
+
path: request.url,
|
|
319
|
+
requiredPermissions: exception.requiredPermissions || [],
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Rate Limit Exception Filter - Handles rate limit errors
|
|
325
|
+
* @example
|
|
326
|
+
* @UseFilters(RateLimitExceptionFilter)
|
|
327
|
+
* @Post()
|
|
328
|
+
* async create() {}
|
|
329
|
+
*/
|
|
330
|
+
export class RateLimitException {
|
|
331
|
+
catch(exception, context) {
|
|
332
|
+
const response = context.getResponse();
|
|
333
|
+
const request = context.getRequest();
|
|
334
|
+
// Don't catch HttpException or its subclasses
|
|
335
|
+
if (exception instanceof HttpException) {
|
|
336
|
+
throw exception;
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
statusCode: 429,
|
|
340
|
+
error: "Too Many Requests",
|
|
341
|
+
message: exception.message || "Rate limit exceeded",
|
|
342
|
+
timestamp: new Date().toISOString(),
|
|
343
|
+
path: request.url,
|
|
344
|
+
retryAfter: exception.retryAfter || 60,
|
|
345
|
+
hint: "Please wait before making another request",
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Business Logic Exception Filter - Handles business rule violations
|
|
351
|
+
* @example
|
|
352
|
+
* @UseFilters(BusinessLogicExceptionFilter)
|
|
353
|
+
* @Post('/transfer')
|
|
354
|
+
* async transfer(@Body() data: any) {}
|
|
355
|
+
*/
|
|
356
|
+
export class BusinessLogicException {
|
|
357
|
+
catch(exception, context) {
|
|
358
|
+
const response = context.getResponse();
|
|
359
|
+
const request = context.getRequest();
|
|
360
|
+
// Don't catch HttpException or its subclasses
|
|
361
|
+
if (exception instanceof HttpException) {
|
|
362
|
+
throw exception;
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
statusCode: exception.statusCode || 422,
|
|
366
|
+
error: "Business Rule Violation",
|
|
367
|
+
message: exception.message || "Business logic constraint violated",
|
|
368
|
+
timestamp: new Date().toISOString(),
|
|
369
|
+
path: request.url,
|
|
370
|
+
rule: exception.rule || "unknown",
|
|
371
|
+
details: exception.details || {},
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
/**
|
|
3
|
+
* Error Formatter Interface
|
|
4
|
+
* Used by WynkFactory to format validation errors
|
|
5
|
+
*
|
|
6
|
+
* IMPORTANT: These formatters are passed to WynkFactory.create(), NOT to useGlobalFilters()!
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* // CORRECT usage:
|
|
10
|
+
* const app = WynkFactory.create({
|
|
11
|
+
* controllers: [UserController],
|
|
12
|
+
* validationErrorFormatter: new FormatErrorFormatter(), // ✅ Pass here
|
|
13
|
+
* });
|
|
14
|
+
*
|
|
15
|
+
* // WRONG usage:
|
|
16
|
+
* app.useGlobalFilters(
|
|
17
|
+
* new FormatErrorFormatter() // ❌ This is not a filter!
|
|
18
|
+
* );
|
|
19
|
+
*/
|
|
20
|
+
export interface ErrorFormatter {
|
|
21
|
+
format(validationError: any): any;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* FormatErrorFormatter - Formats as { field: [messages] } object structure
|
|
25
|
+
*
|
|
26
|
+
* Output example:
|
|
27
|
+
* {
|
|
28
|
+
* "statusCode": 400,
|
|
29
|
+
* "message": "Validation failed",
|
|
30
|
+
* "errors": {
|
|
31
|
+
* "email": ["Invalid email address"],
|
|
32
|
+
* "age": ["Must be at least 18"]
|
|
33
|
+
* }
|
|
34
|
+
* }
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* const app = WynkFactory.create({
|
|
38
|
+
* controllers: [UserController],
|
|
39
|
+
* validationErrorFormatter: new FormatErrorFormatter(),
|
|
40
|
+
* });
|
|
41
|
+
*/
|
|
42
|
+
export declare class FormatErrorFormatter implements ErrorFormatter {
|
|
43
|
+
format(error: any): any;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* SimpleErrorFormatter - Formats as simple array of messages
|
|
47
|
+
*
|
|
48
|
+
* Output example:
|
|
49
|
+
* {
|
|
50
|
+
* "statusCode": 400,
|
|
51
|
+
* "message": "Validation failed",
|
|
52
|
+
* "errors": [
|
|
53
|
+
* "Invalid email address",
|
|
54
|
+
* "Must be at least 18"
|
|
55
|
+
* ]
|
|
56
|
+
* }
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* const app = WynkFactory.create({
|
|
60
|
+
* controllers: [UserController],
|
|
61
|
+
* validationErrorFormatter: new SimpleErrorFormatter(),
|
|
62
|
+
* });
|
|
63
|
+
*/
|
|
64
|
+
export declare class SimpleErrorFormatter implements ErrorFormatter {
|
|
65
|
+
format(error: any): any;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* DetailedErrorFormatter - Formats with detailed field info
|
|
69
|
+
*
|
|
70
|
+
* Output example:
|
|
71
|
+
* {
|
|
72
|
+
* "statusCode": 400,
|
|
73
|
+
* "message": "Validation failed",
|
|
74
|
+
* "errors": [
|
|
75
|
+
* {
|
|
76
|
+
* "field": "email",
|
|
77
|
+
* "message": "Invalid email address",
|
|
78
|
+
* "value": "invalid-email",
|
|
79
|
+
* "expected": {...schema...}
|
|
80
|
+
* }
|
|
81
|
+
* ]
|
|
82
|
+
* }
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* const app = WynkFactory.create({
|
|
86
|
+
* controllers: [UserController],
|
|
87
|
+
* validationErrorFormatter: new DetailedErrorFormatter(),
|
|
88
|
+
* });
|
|
89
|
+
*/
|
|
90
|
+
export declare class DetailedErrorFormatter implements ErrorFormatter {
|
|
91
|
+
format(error: any): any;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=formatter.decorators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatter.decorators.d.ts","sourceRoot":"","sources":["../../core/decorators/formatter.decorators.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,eAAe,EAAE,GAAG,GAAG,GAAG,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,oBAAqB,YAAW,cAAc;IACzD,MAAM,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG;CAsBxB;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,oBAAqB,YAAW,cAAc;IACzD,MAAM,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG;CAiBxB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,sBAAuB,YAAW,cAAc;IAC3D,MAAM,CAAC,KAAK,EAAE,GAAG,GAAG,GAAG;CAgCxB"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import "reflect-metadata";
|
|
2
|
+
/**
|
|
3
|
+
* FormatErrorFormatter - Formats as { field: [messages] } object structure
|
|
4
|
+
*
|
|
5
|
+
* Output example:
|
|
6
|
+
* {
|
|
7
|
+
* "statusCode": 400,
|
|
8
|
+
* "message": "Validation failed",
|
|
9
|
+
* "errors": {
|
|
10
|
+
* "email": ["Invalid email address"],
|
|
11
|
+
* "age": ["Must be at least 18"]
|
|
12
|
+
* }
|
|
13
|
+
* }
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* const app = WynkFactory.create({
|
|
17
|
+
* controllers: [UserController],
|
|
18
|
+
* validationErrorFormatter: new FormatErrorFormatter(),
|
|
19
|
+
* });
|
|
20
|
+
*/
|
|
21
|
+
export class FormatErrorFormatter {
|
|
22
|
+
format(error) {
|
|
23
|
+
const formattedErrors = {};
|
|
24
|
+
if (error.errors && error.errors.length > 0) {
|
|
25
|
+
error.errors.forEach((err) => {
|
|
26
|
+
const field = err.path?.replace(/^\//, "") || "unknown";
|
|
27
|
+
if (!formattedErrors[field]) {
|
|
28
|
+
formattedErrors[field] = [];
|
|
29
|
+
}
|
|
30
|
+
formattedErrors[field].push(err.summary || err.message);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
const field = error.property?.replace(/^\//, "") || "unknown";
|
|
35
|
+
formattedErrors[field] = [error.summary || error.message];
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
statusCode: 400,
|
|
39
|
+
message: "Validation failed",
|
|
40
|
+
errors: formattedErrors,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* SimpleErrorFormatter - Formats as simple array of messages
|
|
46
|
+
*
|
|
47
|
+
* Output example:
|
|
48
|
+
* {
|
|
49
|
+
* "statusCode": 400,
|
|
50
|
+
* "message": "Validation failed",
|
|
51
|
+
* "errors": [
|
|
52
|
+
* "Invalid email address",
|
|
53
|
+
* "Must be at least 18"
|
|
54
|
+
* ]
|
|
55
|
+
* }
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* const app = WynkFactory.create({
|
|
59
|
+
* controllers: [UserController],
|
|
60
|
+
* validationErrorFormatter: new SimpleErrorFormatter(),
|
|
61
|
+
* });
|
|
62
|
+
*/
|
|
63
|
+
export class SimpleErrorFormatter {
|
|
64
|
+
format(error) {
|
|
65
|
+
const messages = [];
|
|
66
|
+
if (error.errors && error.errors.length > 0) {
|
|
67
|
+
error.errors.forEach((err) => {
|
|
68
|
+
messages.push(err.summary || err.message);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
messages.push(error.summary || error.message);
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
statusCode: 400,
|
|
76
|
+
message: "Validation failed",
|
|
77
|
+
errors: messages,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* DetailedErrorFormatter - Formats with detailed field info
|
|
83
|
+
*
|
|
84
|
+
* Output example:
|
|
85
|
+
* {
|
|
86
|
+
* "statusCode": 400,
|
|
87
|
+
* "message": "Validation failed",
|
|
88
|
+
* "errors": [
|
|
89
|
+
* {
|
|
90
|
+
* "field": "email",
|
|
91
|
+
* "message": "Invalid email address",
|
|
92
|
+
* "value": "invalid-email",
|
|
93
|
+
* "expected": {...schema...}
|
|
94
|
+
* }
|
|
95
|
+
* ]
|
|
96
|
+
* }
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* const app = WynkFactory.create({
|
|
100
|
+
* controllers: [UserController],
|
|
101
|
+
* validationErrorFormatter: new DetailedErrorFormatter(),
|
|
102
|
+
* });
|
|
103
|
+
*/
|
|
104
|
+
export class DetailedErrorFormatter {
|
|
105
|
+
format(error) {
|
|
106
|
+
const errors = [];
|
|
107
|
+
if (error.errors && error.errors.length > 0) {
|
|
108
|
+
error.errors.forEach((err) => {
|
|
109
|
+
errors.push({
|
|
110
|
+
field: err.path?.replace(/^\//, "") || "unknown",
|
|
111
|
+
message: err.summary || err.message,
|
|
112
|
+
value: err.value,
|
|
113
|
+
expected: err.schema,
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
errors.push({
|
|
119
|
+
field: error.property?.replace(/^\//, "") || "unknown",
|
|
120
|
+
message: error.summary || error.message,
|
|
121
|
+
value: error.found,
|
|
122
|
+
expected: error.expected,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
statusCode: 400,
|
|
127
|
+
message: "Validation failed",
|
|
128
|
+
errors,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
2
|
/**
|
|
3
|
-
* Guard Decorators and Interfaces for
|
|
4
|
-
*
|
|
3
|
+
* Guard Decorators and Interfaces for WynkJS Framework
|
|
4
|
+
* Guards for route protection and authorization
|
|
5
5
|
*/
|
|
6
6
|
/**
|
|
7
7
|
* Execution context interface - provides access to request details
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guard.decorators.d.ts","sourceRoot":"","sources":["../../core/decorators/guard.decorators.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B;;;GAGG;AAEH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,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;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACpE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CACvB,GAAG,MAAM,EAAE,CAAC,QAAQ,GAAG,WAAW,CAAC,EAAE,GACpC,eAAe,GAAG,cAAc,CAwBlC;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,QAAQ,EACjB,eAAe,EAAE,GAAG,GACnB,gBAAgB,CAQlB;
|
|
1
|
+
{"version":3,"file":"guard.decorators.d.ts","sourceRoot":"","sources":["../../core/decorators/guard.decorators.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B;;;GAGG;AAEH;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,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;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACpE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CACvB,GAAG,MAAM,EAAE,CAAC,QAAQ,GAAG,WAAW,CAAC,EAAE,GACpC,eAAe,GAAG,cAAc,CAwBlC;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,QAAQ,EACjB,eAAe,EAAE,GAAG,GACnB,gBAAgB,CAQlB;AAOD;;GAEG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,CAAC,QAAQ,GAAG,WAAW,CAAC,EAAE,EAClC,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,OAAO,CAAC,CA8BlB"}
|
|
@@ -39,6 +39,10 @@ export function createExecutionContext(ctx, handler, controllerClass) {
|
|
|
39
39
|
getClass: () => controllerClass,
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Guard instance cache for singleton pattern
|
|
44
|
+
*/
|
|
45
|
+
const guardInstanceCache = new Map();
|
|
42
46
|
/**
|
|
43
47
|
* Helper function to execute guards
|
|
44
48
|
*/
|
|
@@ -46,14 +50,16 @@ export async function executeGuards(guards, context) {
|
|
|
46
50
|
for (const guard of guards) {
|
|
47
51
|
let result;
|
|
48
52
|
if (typeof guard === "function") {
|
|
49
|
-
// Guard is a class,
|
|
50
|
-
|
|
51
|
-
if (guardInstance
|
|
52
|
-
|
|
53
|
+
// Guard is a class, get or create singleton instance
|
|
54
|
+
let guardInstance = guardInstanceCache.get(guard);
|
|
55
|
+
if (!guardInstance) {
|
|
56
|
+
guardInstance = new guard();
|
|
57
|
+
guardInstanceCache.set(guard, guardInstance);
|
|
53
58
|
}
|
|
54
|
-
|
|
59
|
+
if (!guardInstance.canActivate) {
|
|
55
60
|
throw new Error(`Guard ${guard.name} must implement CanActivate interface`);
|
|
56
61
|
}
|
|
62
|
+
result = await guardInstance.canActivate(context);
|
|
57
63
|
}
|
|
58
64
|
else {
|
|
59
65
|
// Guard is already an instance
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
2
|
/**
|
|
3
|
-
* HTTP Method Decorators for
|
|
4
|
-
*
|
|
3
|
+
* HTTP Method Decorators for WynkJS Framework
|
|
4
|
+
* RESTful route handlers with TypeScript decorators
|
|
5
|
+
* Optimized for WynkJS's performance on Bun runtime
|
|
5
6
|
*/
|
|
6
7
|
export interface RouteOptions {
|
|
7
8
|
path?: string;
|
|
@@ -13,12 +14,17 @@ export interface RouteOptions {
|
|
|
13
14
|
}
|
|
14
15
|
/**
|
|
15
16
|
* Controller decorator - Defines a controller with a base path
|
|
16
|
-
* @param
|
|
17
|
+
* @param pathOrOptions Base path string or options object with path
|
|
17
18
|
* @example
|
|
18
19
|
* @Controller('/users')
|
|
19
20
|
* export class UserController {}
|
|
21
|
+
*
|
|
22
|
+
* @Controller({ path: '/users' })
|
|
23
|
+
* export class UserController {}
|
|
20
24
|
*/
|
|
21
|
-
export declare function Controller(
|
|
25
|
+
export declare function Controller(pathOrOptions?: string | {
|
|
26
|
+
path?: string;
|
|
27
|
+
}): ClassDecorator;
|
|
22
28
|
/**
|
|
23
29
|
* HTTP GET decorator
|
|
24
30
|
* @param pathOrOptions Route path or options with DTO
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.decorators.d.ts","sourceRoot":"","sources":["../../core/decorators/http.decorators.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B
|
|
1
|
+
{"version":3,"file":"http.decorators.d.ts","sourceRoot":"","sources":["../../core/decorators/http.decorators.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B;;;;GAIG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,MAAM,CAAC,EAAE,GAAG,CAAC;IACb,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,OAAO,CAAC,EAAE,GAAG,CAAC;IACd,QAAQ,CAAC,EAAE,GAAG,CAAC;CAChB;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CACxB,aAAa,CAAC,EAAE,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACzC,cAAc,CAWhB;AAED;;;;;;;;;GASG;AACH,wBAAgB,GAAG,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,eAAe,CAM1E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,IAAI,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,eAAe,CAM3E;AAED;;;;;;;;;GASG;AACH,wBAAgB,GAAG,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,eAAe,CAM1E;AAED;;;;;;;;;GASG;AACH,wBAAgB,KAAK,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,eAAe,CAM5E;AAED;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,eAAe,CAM7E;AAED;;;GAGG;AACH,wBAAgB,OAAO,CACrB,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,GACpC,eAAe,CAMjB;AAED;;;GAGG;AACH,wBAAgB,IAAI,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,eAAe,CAM3E;AAmDD;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,CAStD;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,eAAe,CAYnE;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CACtB,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,MAAY,GACvB,eAAe,CAcjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,GAAG,CAAC,GAAG,WAAW,EAAE,GAAG,EAAE,GAAG,cAAc,GAAG,eAAe,CAuB3E"}
|
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
2
|
/**
|
|
3
3
|
* Controller decorator - Defines a controller with a base path
|
|
4
|
-
* @param
|
|
4
|
+
* @param pathOrOptions Base path string or options object with path
|
|
5
5
|
* @example
|
|
6
6
|
* @Controller('/users')
|
|
7
7
|
* export class UserController {}
|
|
8
|
+
*
|
|
9
|
+
* @Controller({ path: '/users' })
|
|
10
|
+
* export class UserController {}
|
|
8
11
|
*/
|
|
9
|
-
export function Controller(
|
|
12
|
+
export function Controller(pathOrOptions) {
|
|
10
13
|
return (target) => {
|
|
14
|
+
// Handle both string and object formats
|
|
15
|
+
const path = typeof pathOrOptions === "string"
|
|
16
|
+
? pathOrOptions
|
|
17
|
+
: pathOrOptions?.path || "";
|
|
11
18
|
Reflect.defineMetadata("basePath", path, target);
|
|
12
19
|
Reflect.defineMetadata("routes", [], target);
|
|
13
20
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import "reflect-metadata";
|
|
2
|
-
import { WynkInterceptor
|
|
3
|
-
import {
|
|
2
|
+
import { WynkInterceptor } from "./interceptor.decorators";
|
|
3
|
+
import { InterceptorContext } from "../interfaces/interceptor.interface";
|
|
4
4
|
/**
|
|
5
5
|
* Advanced Interceptors for WynkJS Framework
|
|
6
6
|
* Additional interceptors for common use cases
|
|
@@ -13,7 +13,7 @@ import { ExecutionContext } from "./guard.decorators";
|
|
|
13
13
|
* export class ApiController {}
|
|
14
14
|
*/
|
|
15
15
|
export declare class ResponseInterceptor implements WynkInterceptor {
|
|
16
|
-
intercept(context:
|
|
16
|
+
intercept(context: InterceptorContext, next: () => Promise<any>): Promise<any>;
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
19
|
* Error Handling Interceptor - Catches and transforms errors
|
|
@@ -23,7 +23,7 @@ export declare class ResponseInterceptor implements WynkInterceptor {
|
|
|
23
23
|
* async getData() {}
|
|
24
24
|
*/
|
|
25
25
|
export declare class ErrorHandlingInterceptor implements WynkInterceptor {
|
|
26
|
-
intercept(context:
|
|
26
|
+
intercept(context: InterceptorContext, next: () => Promise<any>): Promise<any>;
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
29
|
* Compression Interceptor - Simulates response compression metadata
|
|
@@ -35,7 +35,7 @@ export declare class ErrorHandlingInterceptor implements WynkInterceptor {
|
|
|
35
35
|
export declare class CompressionInterceptor implements WynkInterceptor {
|
|
36
36
|
private threshold;
|
|
37
37
|
constructor(threshold?: number);
|
|
38
|
-
intercept(context:
|
|
38
|
+
intercept(context: InterceptorContext, next: () => Promise<any>): Promise<any>;
|
|
39
39
|
}
|
|
40
40
|
/**
|
|
41
41
|
* Rate Limit Interceptor - Basic rate limiting
|
|
@@ -49,7 +49,7 @@ export declare class RateLimitInterceptor implements WynkInterceptor {
|
|
|
49
49
|
private windowMs;
|
|
50
50
|
private requests;
|
|
51
51
|
constructor(maxRequests?: number, windowMs?: number);
|
|
52
|
-
intercept(context:
|
|
52
|
+
intercept(context: InterceptorContext, next: () => Promise<any>): Promise<any>;
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
55
|
* CORS Interceptor - Add CORS headers to responses
|
|
@@ -65,7 +65,7 @@ export declare class CorsInterceptor implements WynkInterceptor {
|
|
|
65
65
|
methods?: string[];
|
|
66
66
|
credentials?: boolean;
|
|
67
67
|
});
|
|
68
|
-
intercept(context:
|
|
68
|
+
intercept(context: InterceptorContext, next: () => Promise<any>): Promise<any>;
|
|
69
69
|
}
|
|
70
70
|
/**
|
|
71
71
|
* Sanitize Interceptor - Sanitizes response data
|
|
@@ -77,7 +77,7 @@ export declare class CorsInterceptor implements WynkInterceptor {
|
|
|
77
77
|
export declare class SanitizeInterceptor implements WynkInterceptor {
|
|
78
78
|
private fieldsToRemove;
|
|
79
79
|
constructor(fieldsToRemove?: string[]);
|
|
80
|
-
intercept(context:
|
|
80
|
+
intercept(context: InterceptorContext, next: () => Promise<any>): Promise<any>;
|
|
81
81
|
private sanitize;
|
|
82
82
|
}
|
|
83
83
|
/**
|
|
@@ -88,6 +88,6 @@ export declare class SanitizeInterceptor implements WynkInterceptor {
|
|
|
88
88
|
* async getUsers(@Query('page') page: number, @Query('limit') limit: number) {}
|
|
89
89
|
*/
|
|
90
90
|
export declare class PaginationInterceptor implements WynkInterceptor {
|
|
91
|
-
intercept(context:
|
|
91
|
+
intercept(context: InterceptorContext, next: () => Promise<any>): Promise<any>;
|
|
92
92
|
}
|
|
93
93
|
//# sourceMappingURL=interceptor.advanced.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interceptor.advanced.d.ts","sourceRoot":"","sources":["../../core/decorators/interceptor.advanced.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,
|
|
1
|
+
{"version":3,"file":"interceptor.advanced.d.ts","sourceRoot":"","sources":["../../core/decorators/interceptor.advanced.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAEzE;;;GAGG;AAEH;;;;;;GAMG;AACH,qBAAa,mBAAoB,YAAW,eAAe;IACnD,SAAS,CACb,OAAO,EAAE,kBAAkB,EAC3B,IAAI,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,GACvB,OAAO,CAAC,GAAG,CAAC;CAsChB;AAED;;;;;;GAMG;AACH,qBAAa,wBAAyB,YAAW,eAAe;IACxD,SAAS,CACb,OAAO,EAAE,kBAAkB,EAC3B,IAAI,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,GACvB,OAAO,CAAC,GAAG,CAAC;CAoBhB;AAED;;;;;;GAMG;AACH,qBAAa,sBAAuB,YAAW,eAAe;IAChD,OAAO,CAAC,SAAS;gBAAT,SAAS,GAAE,MAAa;IAEtC,SAAS,CACb,OAAO,EAAE,kBAAkB,EAC3B,IAAI,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,GACvB,OAAO,CAAC,GAAG,CAAC;CAahB;AAED;;;;;;GAMG;AACH,qBAAa,oBAAqB,YAAW,eAAe;IAIxD,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,QAAQ;IAJlB,OAAO,CAAC,QAAQ,CAAoC;gBAG1C,WAAW,GAAE,MAAY,EACzB,QAAQ,GAAE,MAAc;IAG5B,SAAS,CACb,OAAO,EAAE,kBAAkB,EAC3B,IAAI,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,GACvB,OAAO,CAAC,GAAG,CAAC;CA0BhB;AAED;;;;;;GAMG;AACH,qBAAa,eAAgB,YAAW,eAAe;IAEnD,OAAO,CAAC,OAAO;gBAAP,OAAO,GAAE;QACf,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAC3B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;QACnB,WAAW,CAAC,EAAE,OAAO,CAAC;KAClB;IAGF,SAAS,CACb,OAAO,EAAE,kBAAkB,EAC3B,IAAI,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,GACvB,OAAO,CAAC,GAAG,CAAC;CA8BhB;AAED;;;;;;GAMG;AACH,qBAAa,mBAAoB,YAAW,eAAe;IACzD,OAAO,CAAC,cAAc,CAAW;gBAErB,cAAc,GAAE,MAAM,EAAoC;IAIhE,SAAS,CACb,OAAO,EAAE,kBAAkB,EAC3B,IAAI,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,GACvB,OAAO,CAAC,GAAG,CAAC;IAKf,OAAO,CAAC,QAAQ;CAmBjB;AAED;;;;;;GAMG;AACH,qBAAa,qBAAsB,YAAW,eAAe;IACrD,SAAS,CACb,OAAO,EAAE,kBAAkB,EAC3B,IAAI,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,GACvB,OAAO,CAAC,GAAG,CAAC;CA2BhB"}
|
|
@@ -15,7 +15,7 @@ export class ResponseInterceptor {
|
|
|
15
15
|
const request = context.getRequest();
|
|
16
16
|
const startTime = Date.now();
|
|
17
17
|
try {
|
|
18
|
-
const data = await next
|
|
18
|
+
const data = await next();
|
|
19
19
|
const duration = Date.now() - startTime;
|
|
20
20
|
return {
|
|
21
21
|
success: true,
|
|
@@ -58,7 +58,7 @@ export class ResponseInterceptor {
|
|
|
58
58
|
export class ErrorHandlingInterceptor {
|
|
59
59
|
async intercept(context, next) {
|
|
60
60
|
try {
|
|
61
|
-
return await next
|
|
61
|
+
return await next();
|
|
62
62
|
}
|
|
63
63
|
catch (error) {
|
|
64
64
|
console.error(`❌ Error in ${context.getRequest().method} ${context.getRequest().url}:`, error);
|
|
@@ -85,7 +85,7 @@ export class CompressionInterceptor {
|
|
|
85
85
|
this.threshold = threshold;
|
|
86
86
|
}
|
|
87
87
|
async intercept(context, next) {
|
|
88
|
-
const data = await next
|
|
88
|
+
const data = await next();
|
|
89
89
|
const dataSize = JSON.stringify(data).length;
|
|
90
90
|
if (dataSize > this.threshold) {
|
|
91
91
|
// In a real implementation, you'd compress the data here
|
|
@@ -128,7 +128,7 @@ export class RateLimitInterceptor {
|
|
|
128
128
|
// Add current request
|
|
129
129
|
history.push(now);
|
|
130
130
|
this.requests.set(clientIp, history);
|
|
131
|
-
return next
|
|
131
|
+
return next();
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
/**
|
|
@@ -162,7 +162,7 @@ export class CorsInterceptor {
|
|
|
162
162
|
response.headers.set("Access-Control-Allow-Credentials", "true");
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
|
-
return next
|
|
165
|
+
return next();
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
/**
|
|
@@ -178,7 +178,7 @@ export class SanitizeInterceptor {
|
|
|
178
178
|
this.fieldsToRemove = fieldsToRemove;
|
|
179
179
|
}
|
|
180
180
|
async intercept(context, next) {
|
|
181
|
-
const data = await next
|
|
181
|
+
const data = await next();
|
|
182
182
|
return this.sanitize(data);
|
|
183
183
|
}
|
|
184
184
|
sanitize(data) {
|
|
@@ -210,7 +210,7 @@ export class PaginationInterceptor {
|
|
|
210
210
|
const url = new URL(request.url, `http://${request.headers?.get?.("host")}`);
|
|
211
211
|
const page = parseInt(url.searchParams.get("page") || "1");
|
|
212
212
|
const limit = parseInt(url.searchParams.get("limit") || "10");
|
|
213
|
-
const data = await next
|
|
213
|
+
const data = await next();
|
|
214
214
|
// If data is an array, add pagination
|
|
215
215
|
if (Array.isArray(data)) {
|
|
216
216
|
return {
|