@catbee/utils 0.0.8-rc.0 → 0.0.8-rc.2
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 +25 -1
- package/build/esm/config.d.ts +0 -1
- package/build/esm/config.js +6 -7
- package/build/esm/index.d.ts +0 -1
- package/build/esm/index.js +0 -1
- package/build/esm/servers/server.builder.d.ts +0 -1
- package/build/esm/servers/server.builder.js +99 -147
- package/build/esm/servers/server.d.ts +0 -1
- package/build/esm/servers/server.js +591 -876
- package/build/esm/types/api-response.d.ts +0 -1
- package/build/esm/types/api-response.js +0 -1
- package/build/esm/types/index.d.ts +0 -1
- package/build/esm/types/index.js +0 -1
- package/build/esm/types/server.d.ts +0 -1
- package/build/esm/types/server.js +0 -1
- package/build/esm/utils/array.utils.d.ts +0 -1
- package/build/esm/utils/array.utils.js +48 -86
- package/build/esm/utils/async.utils.d.ts +0 -1
- package/build/esm/utils/async.utils.js +247 -524
- package/build/esm/utils/cache.utils.d.ts +0 -1
- package/build/esm/utils/cache.utils.js +96 -301
- package/build/esm/utils/context-store.utils.d.ts +0 -1
- package/build/esm/utils/context-store.utils.js +62 -79
- package/build/esm/utils/crypto.utils.d.ts +0 -1
- package/build/esm/utils/crypto.utils.js +78 -175
- package/build/esm/utils/date.utils.d.ts +0 -1
- package/build/esm/utils/date.utils.js +47 -53
- package/build/esm/utils/decorators.utils.d.ts +201 -5
- package/build/esm/utils/decorators.utils.js +632 -285
- package/build/esm/utils/dir.utils.d.ts +0 -1
- package/build/esm/utils/dir.utils.js +264 -786
- package/build/esm/utils/env.utils.d.ts +0 -1
- package/build/esm/utils/env.utils.js +180 -271
- package/build/esm/utils/exception.utils.d.ts +0 -1
- package/build/esm/utils/exception.utils.js +112 -289
- package/build/esm/utils/fs.utils.d.ts +0 -1
- package/build/esm/utils/fs.utils.js +169 -401
- package/build/esm/utils/http-status-codes.d.ts +0 -1
- package/build/esm/utils/http-status-codes.js +0 -1
- package/build/esm/utils/id.utils.d.ts +0 -1
- package/build/esm/utils/id.utils.js +5 -9
- package/build/esm/utils/logger.utils.d.ts +0 -1
- package/build/esm/utils/logger.utils.js +34 -71
- package/build/esm/utils/middleware.utils.d.ts +0 -1
- package/build/esm/utils/middleware.utils.js +65 -155
- package/build/esm/utils/obj.utils.d.ts +0 -1
- package/build/esm/utils/obj.utils.js +69 -176
- package/build/esm/utils/performance.utils.d.ts +0 -1
- package/build/esm/utils/performance.utils.js +92 -195
- package/build/esm/utils/request.utils.d.ts +0 -1
- package/build/esm/utils/request.utils.js +41 -74
- package/build/esm/utils/response.utils.d.ts +0 -1
- package/build/esm/utils/response.utils.js +84 -107
- package/build/esm/utils/stream.utils.d.ts +0 -1
- package/build/esm/utils/stream.utils.js +38 -113
- package/build/esm/utils/string.utils.d.ts +0 -1
- package/build/esm/utils/string.utils.js +6 -11
- package/build/esm/utils/type.utils.d.ts +0 -1
- package/build/esm/utils/type.utils.js +8 -12
- package/build/esm/utils/url.utils.d.ts +0 -1
- package/build/esm/utils/url.utils.js +49 -127
- package/build/esm/utils/validate.utils.d.ts +0 -1
- package/build/esm/utils/validate.utils.js +23 -40
- package/package.json +3 -33
- package/build/esm/config.js.map +0 -1
- package/build/esm/index.js.map +0 -1
- package/build/esm/servers/server.builder.js.map +0 -1
- package/build/esm/servers/server.js.map +0 -1
- package/build/esm/types/api-response.js.map +0 -1
- package/build/esm/types/index.js.map +0 -1
- package/build/esm/types/server.js.map +0 -1
- package/build/esm/utils/array.utils.js.map +0 -1
- package/build/esm/utils/async.utils.js.map +0 -1
- package/build/esm/utils/cache.utils.js.map +0 -1
- package/build/esm/utils/context-store.utils.js.map +0 -1
- package/build/esm/utils/crypto.utils.js.map +0 -1
- package/build/esm/utils/date.utils.js.map +0 -1
- package/build/esm/utils/decorators.utils.js.map +0 -1
- package/build/esm/utils/dir.utils.js.map +0 -1
- package/build/esm/utils/env.utils.js.map +0 -1
- package/build/esm/utils/exception.utils.js.map +0 -1
- package/build/esm/utils/fs.utils.js.map +0 -1
- package/build/esm/utils/http-status-codes.js.map +0 -1
- package/build/esm/utils/id.utils.js.map +0 -1
- package/build/esm/utils/logger.utils.js.map +0 -1
- package/build/esm/utils/middleware.utils.js.map +0 -1
- package/build/esm/utils/obj.utils.js.map +0 -1
- package/build/esm/utils/performance.utils.js.map +0 -1
- package/build/esm/utils/request.utils.js.map +0 -1
- package/build/esm/utils/response.utils.js.map +0 -1
- package/build/esm/utils/stream.utils.js.map +0 -1
- package/build/esm/utils/string.utils.js.map +0 -1
- package/build/esm/utils/type.utils.js.map +0 -1
- package/build/esm/utils/url.utils.js.map +0 -1
- package/build/esm/utils/validate.utils.js.map +0 -1
- package/build/esnext/config.d.ts +0 -122
- package/build/esnext/config.js +0 -133
- package/build/esnext/config.js.map +0 -1
- package/build/esnext/index.d.ts +0 -27
- package/build/esnext/index.js +0 -50
- package/build/esnext/index.js.map +0 -1
- package/build/esnext/servers/server.builder.d.ts +0 -508
- package/build/esnext/servers/server.builder.js +0 -657
- package/build/esnext/servers/server.builder.js.map +0 -1
- package/build/esnext/servers/server.d.ts +0 -256
- package/build/esnext/servers/server.js +0 -955
- package/build/esnext/servers/server.js.map +0 -1
- package/build/esnext/types/api-response.d.ts +0 -152
- package/build/esnext/types/api-response.js +0 -34
- package/build/esnext/types/api-response.js.map +0 -1
- package/build/esnext/types/index.d.ts +0 -125
- package/build/esnext/types/index.js +0 -25
- package/build/esnext/types/index.js.map +0 -1
- package/build/esnext/types/server.d.ts +0 -268
- package/build/esnext/types/server.js +0 -25
- package/build/esnext/types/server.js.map +0 -1
- package/build/esnext/utils/array.utils.d.ts +0 -168
- package/build/esnext/utils/array.utils.js +0 -345
- package/build/esnext/utils/array.utils.js.map +0 -1
- package/build/esnext/utils/async.utils.d.ts +0 -265
- package/build/esnext/utils/async.utils.js +0 -620
- package/build/esnext/utils/async.utils.js.map +0 -1
- package/build/esnext/utils/cache.utils.d.ts +0 -153
- package/build/esnext/utils/cache.utils.js +0 -293
- package/build/esnext/utils/cache.utils.js.map +0 -1
- package/build/esnext/utils/context-store.utils.d.ts +0 -189
- package/build/esnext/utils/context-store.utils.js +0 -289
- package/build/esnext/utils/context-store.utils.js.map +0 -1
- package/build/esnext/utils/crypto.utils.d.ts +0 -160
- package/build/esnext/utils/crypto.utils.js +0 -276
- package/build/esnext/utils/crypto.utils.js.map +0 -1
- package/build/esnext/utils/date.utils.d.ts +0 -159
- package/build/esnext/utils/date.utils.js +0 -384
- package/build/esnext/utils/date.utils.js.map +0 -1
- package/build/esnext/utils/decorators.utils.d.ts +0 -315
- package/build/esnext/utils/decorators.utils.js +0 -504
- package/build/esnext/utils/decorators.utils.js.map +0 -1
- package/build/esnext/utils/dir.utils.d.ts +0 -196
- package/build/esnext/utils/dir.utils.js +0 -477
- package/build/esnext/utils/dir.utils.js.map +0 -1
- package/build/esnext/utils/env.utils.d.ts +0 -377
- package/build/esnext/utils/env.utils.js +0 -785
- package/build/esnext/utils/env.utils.js.map +0 -1
- package/build/esnext/utils/exception.utils.d.ts +0 -230
- package/build/esnext/utils/exception.utils.js +0 -381
- package/build/esnext/utils/exception.utils.js.map +0 -1
- package/build/esnext/utils/fs.utils.d.ts +0 -164
- package/build/esnext/utils/fs.utils.js +0 -349
- package/build/esnext/utils/fs.utils.js.map +0 -1
- package/build/esnext/utils/http-status-codes.d.ts +0 -266
- package/build/esnext/utils/http-status-codes.js +0 -295
- package/build/esnext/utils/http-status-codes.js.map +0 -1
- package/build/esnext/utils/id.utils.d.ts +0 -36
- package/build/esnext/utils/id.utils.js +0 -84
- package/build/esnext/utils/id.utils.js.map +0 -1
- package/build/esnext/utils/logger.utils.d.ts +0 -160
- package/build/esnext/utils/logger.utils.js +0 -300
- package/build/esnext/utils/logger.utils.js.map +0 -1
- package/build/esnext/utils/middleware.utils.d.ts +0 -100
- package/build/esnext/utils/middleware.utils.js +0 -236
- package/build/esnext/utils/middleware.utils.js.map +0 -1
- package/build/esnext/utils/obj.utils.d.ts +0 -124
- package/build/esnext/utils/obj.utils.js +0 -413
- package/build/esnext/utils/obj.utils.js.map +0 -1
- package/build/esnext/utils/performance.utils.d.ts +0 -136
- package/build/esnext/utils/performance.utils.js +0 -271
- package/build/esnext/utils/performance.utils.js.map +0 -1
- package/build/esnext/utils/request.utils.d.ts +0 -86
- package/build/esnext/utils/request.utils.js +0 -186
- package/build/esnext/utils/request.utils.js.map +0 -1
- package/build/esnext/utils/response.utils.d.ts +0 -163
- package/build/esnext/utils/response.utils.js +0 -247
- package/build/esnext/utils/response.utils.js.map +0 -1
- package/build/esnext/utils/stream.utils.d.ts +0 -88
- package/build/esnext/utils/stream.utils.js +0 -210
- package/build/esnext/utils/stream.utils.js.map +0 -1
- package/build/esnext/utils/string.utils.d.ts +0 -93
- package/build/esnext/utils/string.utils.js +0 -165
- package/build/esnext/utils/string.utils.js.map +0 -1
- package/build/esnext/utils/type.utils.d.ts +0 -90
- package/build/esnext/utils/type.utils.js +0 -187
- package/build/esnext/utils/type.utils.js.map +0 -1
- package/build/esnext/utils/url.utils.d.ts +0 -141
- package/build/esnext/utils/url.utils.js +0 -304
- package/build/esnext/utils/url.utils.js.map +0 -1
- package/build/esnext/utils/validate.utils.d.ts +0 -177
- package/build/esnext/utils/validate.utils.js +0 -320
- package/build/esnext/utils/validate.utils.js.map +0 -1
- package/build/src/config.d.ts +0 -122
- package/build/src/config.js +0 -138
- package/build/src/config.js.map +0 -1
- package/build/src/index.d.ts +0 -27
- package/build/src/index.js +0 -69
- package/build/src/index.js.map +0 -1
- package/build/src/servers/server.builder.d.ts +0 -508
- package/build/src/servers/server.builder.js +0 -661
- package/build/src/servers/server.builder.js.map +0 -1
- package/build/src/servers/server.d.ts +0 -256
- package/build/src/servers/server.js +0 -962
- package/build/src/servers/server.js.map +0 -1
- package/build/src/types/api-response.d.ts +0 -152
- package/build/src/types/api-response.js +0 -37
- package/build/src/types/api-response.js.map +0 -1
- package/build/src/types/index.d.ts +0 -125
- package/build/src/types/index.js +0 -26
- package/build/src/types/index.js.map +0 -1
- package/build/src/types/server.d.ts +0 -268
- package/build/src/types/server.js +0 -26
- package/build/src/types/server.js.map +0 -1
- package/build/src/utils/array.utils.d.ts +0 -168
- package/build/src/utils/array.utils.js +0 -364
- package/build/src/utils/array.utils.js.map +0 -1
- package/build/src/utils/async.utils.d.ts +0 -265
- package/build/src/utils/async.utils.js +0 -641
- package/build/src/utils/async.utils.js.map +0 -1
- package/build/src/utils/cache.utils.d.ts +0 -153
- package/build/src/utils/cache.utils.js +0 -297
- package/build/src/utils/cache.utils.js.map +0 -1
- package/build/src/utils/context-store.utils.d.ts +0 -189
- package/build/src/utils/context-store.utils.js +0 -296
- package/build/src/utils/context-store.utils.js.map +0 -1
- package/build/src/utils/crypto.utils.d.ts +0 -160
- package/build/src/utils/crypto.utils.js +0 -293
- package/build/src/utils/crypto.utils.js.map +0 -1
- package/build/src/utils/date.utils.d.ts +0 -159
- package/build/src/utils/date.utils.js +0 -396
- package/build/src/utils/date.utils.js.map +0 -1
- package/build/src/utils/decorators.utils.d.ts +0 -315
- package/build/src/utils/decorators.utils.js +0 -516
- package/build/src/utils/decorators.utils.js.map +0 -1
- package/build/src/utils/dir.utils.d.ts +0 -196
- package/build/src/utils/dir.utils.js +0 -501
- package/build/src/utils/dir.utils.js.map +0 -1
- package/build/src/utils/env.utils.d.ts +0 -377
- package/build/src/utils/env.utils.js +0 -789
- package/build/src/utils/env.utils.js.map +0 -1
- package/build/src/utils/exception.utils.d.ts +0 -230
- package/build/src/utils/exception.utils.js +0 -407
- package/build/src/utils/exception.utils.js.map +0 -1
- package/build/src/utils/fs.utils.d.ts +0 -164
- package/build/src/utils/fs.utils.js +0 -372
- package/build/src/utils/fs.utils.js.map +0 -1
- package/build/src/utils/http-status-codes.d.ts +0 -266
- package/build/src/utils/http-status-codes.js +0 -298
- package/build/src/utils/http-status-codes.js.map +0 -1
- package/build/src/utils/id.utils.d.ts +0 -36
- package/build/src/utils/id.utils.js +0 -91
- package/build/src/utils/id.utils.js.map +0 -1
- package/build/src/utils/logger.utils.d.ts +0 -160
- package/build/src/utils/logger.utils.js +0 -346
- package/build/src/utils/logger.utils.js.map +0 -1
- package/build/src/utils/middleware.utils.d.ts +0 -100
- package/build/src/utils/middleware.utils.js +0 -244
- package/build/src/utils/middleware.utils.js.map +0 -1
- package/build/src/utils/obj.utils.d.ts +0 -124
- package/build/src/utils/obj.utils.js +0 -429
- package/build/src/utils/obj.utils.js.map +0 -1
- package/build/src/utils/performance.utils.d.ts +0 -136
- package/build/src/utils/performance.utils.js +0 -278
- package/build/src/utils/performance.utils.js.map +0 -1
- package/build/src/utils/request.utils.d.ts +0 -86
- package/build/src/utils/request.utils.js +0 -195
- package/build/src/utils/request.utils.js.map +0 -1
- package/build/src/utils/response.utils.d.ts +0 -163
- package/build/src/utils/response.utils.js +0 -260
- package/build/src/utils/response.utils.js.map +0 -1
- package/build/src/utils/stream.utils.d.ts +0 -88
- package/build/src/utils/stream.utils.js +0 -218
- package/build/src/utils/stream.utils.js.map +0 -1
- package/build/src/utils/string.utils.d.ts +0 -93
- package/build/src/utils/string.utils.js +0 -179
- package/build/src/utils/string.utils.js.map +0 -1
- package/build/src/utils/type.utils.d.ts +0 -90
- package/build/src/utils/type.utils.js +0 -196
- package/build/src/utils/type.utils.js.map +0 -1
- package/build/src/utils/url.utils.d.ts +0 -141
- package/build/src/utils/url.utils.js +0 -317
- package/build/src/utils/url.utils.js.map +0 -1
- package/build/src/utils/validate.utils.d.ts +0 -177
- package/build/src/utils/validate.utils.js +0 -345
- package/build/src/utils/validate.utils.js.map +0 -1
|
@@ -21,91 +21,153 @@
|
|
|
21
21
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
22
|
* SOFTWARE.
|
|
23
23
|
*/
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
57
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
24
|
+
import 'reflect-metadata';
|
|
25
|
+
import { getLogger } from './logger.utils';
|
|
26
|
+
import { createFinalErrorResponse } from './response.utils';
|
|
27
|
+
import { HttpStatusCodes } from './http-status-codes';
|
|
28
|
+
import { rateLimit } from 'express-rate-limit';
|
|
29
|
+
import { TTLCache } from './cache.utils';
|
|
30
|
+
// Metadata keys
|
|
31
|
+
const ROUTES_KEY = Symbol('routes');
|
|
32
|
+
const MIDDLEWARE_KEY = Symbol('middlewares');
|
|
33
|
+
const PARAMS_KEY = Symbol('params');
|
|
34
|
+
const HTTP_CODE_KEY = Symbol('httpCode');
|
|
35
|
+
const HEADER_KEY = Symbol('headers');
|
|
36
|
+
const BEFORE_KEY = Symbol('before');
|
|
37
|
+
const AFTER_KEY = Symbol('after');
|
|
38
|
+
const ROLES_KEY = Symbol('roles');
|
|
39
|
+
const REDIRECT_KEY = Symbol('redirect');
|
|
40
|
+
const CACHE_KEY = Symbol('cache');
|
|
41
|
+
const RATE_LIMIT_KEY = Symbol('rateLimit');
|
|
42
|
+
const CONTENT_TYPE_KEY = Symbol('contentType');
|
|
43
|
+
const VERSION_KEY = Symbol('version');
|
|
44
|
+
const TIMEOUT_KEY = Symbol('timeout');
|
|
45
|
+
const LOG_KEY = Symbol('log');
|
|
46
|
+
/**
|
|
47
|
+
* RateLimiter cache that uses TTLCache for automatic TTL and LRU handling.
|
|
48
|
+
*/
|
|
49
|
+
class RateLimiterCache {
|
|
50
|
+
cache;
|
|
51
|
+
constructor(maxSize = 100, ttlMs = 5 * 60 * 1000) {
|
|
52
|
+
this.cache = new TTLCache({
|
|
53
|
+
maxSize,
|
|
54
|
+
ttlMs
|
|
55
|
+
});
|
|
58
56
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
62
|
-
if (!m) return o;
|
|
63
|
-
var i = m.call(o), r, ar = [], e;
|
|
64
|
-
try {
|
|
65
|
-
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
57
|
+
generateKey(options) {
|
|
58
|
+
return `${options.max}:${options.windowMs}:${options.standardHeaders}:${options.legacyHeaders}`;
|
|
66
59
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
60
|
+
get(options) {
|
|
61
|
+
const key = this.generateKey(options);
|
|
62
|
+
const cached = this.cache.get(key);
|
|
63
|
+
if (cached) {
|
|
64
|
+
return cached.limiter;
|
|
71
65
|
}
|
|
72
|
-
|
|
66
|
+
// Create new limiter
|
|
67
|
+
const limiter = rateLimit({
|
|
68
|
+
...options,
|
|
69
|
+
handler: (req, res) => {
|
|
70
|
+
const errorResponse = createFinalErrorResponse(req, HttpStatusCodes.TOO_MANY_REQUESTS, 'Too Many Requests');
|
|
71
|
+
res.status(HttpStatusCodes.TOO_MANY_REQUESTS).json(errorResponse);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
// Store in cache
|
|
75
|
+
this.cache.set(key, {
|
|
76
|
+
limiter,
|
|
77
|
+
config: key
|
|
78
|
+
});
|
|
79
|
+
return limiter;
|
|
73
80
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
clear() {
|
|
82
|
+
this.cache.clear();
|
|
83
|
+
}
|
|
84
|
+
size() {
|
|
85
|
+
return this.cache.size();
|
|
86
|
+
}
|
|
87
|
+
destroy() {
|
|
88
|
+
this.cache.destroy();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Global cache instance
|
|
92
|
+
const rateLimiterCache = new RateLimiterCache();
|
|
93
|
+
export class DIContainer {
|
|
94
|
+
instances = new Map();
|
|
95
|
+
constructing = new Map();
|
|
96
|
+
register(target) {
|
|
97
|
+
// Mark as injectable, but do not instantiate yet
|
|
98
|
+
if (!this.instances.has(target) && !this.constructing.has(target)) {
|
|
99
|
+
// No-op: instantiation is deferred until get()
|
|
81
100
|
}
|
|
82
101
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return { value: o && o[i++], done: !o };
|
|
102
|
+
get(target) {
|
|
103
|
+
// Return existing instance if available
|
|
104
|
+
if (this.instances.has(target)) {
|
|
105
|
+
return this.instances.get(target);
|
|
106
|
+
}
|
|
107
|
+
// If currently constructing, return the proxy (for circular refs)
|
|
108
|
+
if (this.constructing.has(target)) {
|
|
109
|
+
return this.constructing.get(target);
|
|
92
110
|
}
|
|
111
|
+
// Mark as constructing (for circular dependency support)
|
|
112
|
+
let proxy = {};
|
|
113
|
+
this.constructing.set(target, proxy);
|
|
114
|
+
// Resolve constructor dependencies
|
|
115
|
+
const paramTypes = Reflect.getMetadata('design:paramtypes', target) || [];
|
|
116
|
+
const dependencies = paramTypes.map(dep => this.get(dep));
|
|
117
|
+
const instance = new target(...dependencies);
|
|
118
|
+
// Copy instance properties to proxy (for circular refs)
|
|
119
|
+
Object.assign(proxy, instance);
|
|
120
|
+
// Replace proxy with real instance
|
|
121
|
+
this.instances.set(target, proxy);
|
|
122
|
+
this.constructing.delete(target);
|
|
123
|
+
// Copy prototype (for instanceof checks)
|
|
124
|
+
Object.setPrototypeOf(proxy, target.prototype);
|
|
125
|
+
return proxy;
|
|
126
|
+
}
|
|
127
|
+
clear() {
|
|
128
|
+
this.instances.clear();
|
|
129
|
+
this.constructing.clear();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const diContainer = new DIContainer();
|
|
133
|
+
function normalizeHeaderValue(value) {
|
|
134
|
+
if (typeof value === 'undefined')
|
|
135
|
+
return undefined;
|
|
136
|
+
if (typeof value === 'string')
|
|
137
|
+
return value;
|
|
138
|
+
if (Array.isArray(value) && value.every(item => typeof item === 'string')) {
|
|
139
|
+
return value;
|
|
140
|
+
}
|
|
141
|
+
return String(value);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Injectable decorator for marking classes as injectable.
|
|
145
|
+
*
|
|
146
|
+
* @returns Class decorator that marks a class as injectable and registers it with the DI container.
|
|
147
|
+
*/
|
|
148
|
+
export function Injectable() {
|
|
149
|
+
return target => {
|
|
150
|
+
Reflect.defineMetadata('injectable', true, target);
|
|
151
|
+
diContainer.register(target);
|
|
93
152
|
};
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Inject decorator for injecting dependencies into class properties.
|
|
156
|
+
*
|
|
157
|
+
* @param targetClass - The class to inject
|
|
158
|
+
* @returns Property decorator that injects the specified class into the property
|
|
159
|
+
*/
|
|
160
|
+
export function Inject(targetClass) {
|
|
161
|
+
return (target, propertyKey) => {
|
|
162
|
+
Object.defineProperty(target, propertyKey, {
|
|
163
|
+
get: function () {
|
|
164
|
+
return diContainer.get(targetClass);
|
|
165
|
+
},
|
|
166
|
+
enumerable: true,
|
|
167
|
+
configurable: true
|
|
168
|
+
});
|
|
169
|
+
};
|
|
170
|
+
}
|
|
109
171
|
/**
|
|
110
172
|
* Factory function that creates HTTP method decorators.
|
|
111
173
|
*
|
|
@@ -113,12 +175,12 @@ var REDIRECT_KEY = Symbol('redirect');
|
|
|
113
175
|
* @returns A method decorator function
|
|
114
176
|
*/
|
|
115
177
|
function createRouteDecorator(method) {
|
|
116
|
-
return
|
|
117
|
-
return
|
|
118
|
-
|
|
178
|
+
return (path) => {
|
|
179
|
+
return (target, propertyKey, descriptor) => {
|
|
180
|
+
const routes = Reflect.getMetadata(ROUTES_KEY, target.constructor) || [];
|
|
119
181
|
routes.push({
|
|
120
|
-
path
|
|
121
|
-
method
|
|
182
|
+
path,
|
|
183
|
+
method,
|
|
122
184
|
handlerName: propertyKey
|
|
123
185
|
});
|
|
124
186
|
Reflect.defineMetadata(ROUTES_KEY, routes, target.constructor);
|
|
@@ -139,7 +201,7 @@ function createRouteDecorator(method) {
|
|
|
139
201
|
* }
|
|
140
202
|
* ```
|
|
141
203
|
*/
|
|
142
|
-
export
|
|
204
|
+
export const Get = createRouteDecorator('get');
|
|
143
205
|
/**
|
|
144
206
|
* Decorator for POST HTTP method routes.
|
|
145
207
|
*
|
|
@@ -154,56 +216,56 @@ export var Get = createRouteDecorator('get');
|
|
|
154
216
|
* }
|
|
155
217
|
* ```
|
|
156
218
|
*/
|
|
157
|
-
export
|
|
219
|
+
export const Post = createRouteDecorator('post');
|
|
158
220
|
/**
|
|
159
221
|
* Decorator for PUT HTTP method routes.
|
|
160
222
|
*
|
|
161
223
|
* @param path - URL path for the route
|
|
162
224
|
* @returns Method decorator
|
|
163
225
|
*/
|
|
164
|
-
export
|
|
226
|
+
export const Put = createRouteDecorator('put');
|
|
165
227
|
/**
|
|
166
228
|
* Decorator for PATCH HTTP method routes.
|
|
167
229
|
*
|
|
168
230
|
* @param path - URL path for the route
|
|
169
231
|
* @returns Method decorator
|
|
170
232
|
*/
|
|
171
|
-
export
|
|
233
|
+
export const Patch = createRouteDecorator('patch');
|
|
172
234
|
/**
|
|
173
235
|
* Decorator for DELETE HTTP method routes.
|
|
174
236
|
*
|
|
175
237
|
* @param path - URL path for the route
|
|
176
238
|
* @returns Method decorator
|
|
177
239
|
*/
|
|
178
|
-
export
|
|
240
|
+
export const Delete = createRouteDecorator('delete');
|
|
179
241
|
/**
|
|
180
242
|
* Decorator for OPTIONS HTTP method routes.
|
|
181
243
|
*
|
|
182
244
|
* @param path - URL path for the route
|
|
183
245
|
* @returns Method decorator
|
|
184
246
|
*/
|
|
185
|
-
export
|
|
247
|
+
export const Options = createRouteDecorator('options');
|
|
186
248
|
/**
|
|
187
249
|
* Decorator for HEAD HTTP method routes.
|
|
188
250
|
*
|
|
189
251
|
* @param path - URL path for the route
|
|
190
252
|
* @returns Method decorator
|
|
191
253
|
*/
|
|
192
|
-
export
|
|
254
|
+
export const Head = createRouteDecorator('head');
|
|
193
255
|
/**
|
|
194
256
|
* Decorator for TRACE HTTP method routes.
|
|
195
257
|
*
|
|
196
258
|
* @param path - URL path for the route
|
|
197
259
|
* @returns Method decorator
|
|
198
260
|
*/
|
|
199
|
-
export
|
|
261
|
+
export const Trace = createRouteDecorator('trace');
|
|
200
262
|
/**
|
|
201
263
|
* Decorator for CONNECT HTTP method routes.
|
|
202
264
|
*
|
|
203
265
|
* @param path - URL path for the route
|
|
204
266
|
* @returns Method decorator
|
|
205
267
|
*/
|
|
206
|
-
export
|
|
268
|
+
export const Connect = createRouteDecorator('connect');
|
|
207
269
|
/**
|
|
208
270
|
* Decorator that marks a class as a controller with a base path.
|
|
209
271
|
* Used as the entry point for routing configuration.
|
|
@@ -220,7 +282,7 @@ export var Connect = createRouteDecorator('connect');
|
|
|
220
282
|
* ```
|
|
221
283
|
*/
|
|
222
284
|
export function Controller(basePath) {
|
|
223
|
-
return
|
|
285
|
+
return target => {
|
|
224
286
|
Reflect.defineMetadata('basePath', basePath, target);
|
|
225
287
|
};
|
|
226
288
|
}
|
|
@@ -240,14 +302,10 @@ export function Controller(basePath) {
|
|
|
240
302
|
* }
|
|
241
303
|
* ```
|
|
242
304
|
*/
|
|
243
|
-
export function Use() {
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
middlewares
|
|
247
|
-
}
|
|
248
|
-
return function (target, propertyKey, _descriptor) {
|
|
249
|
-
var existing = Reflect.getMetadata(MIDDLEWARE_KEY, target, propertyKey) || [];
|
|
250
|
-
Reflect.defineMetadata(MIDDLEWARE_KEY, __spreadArray(__spreadArray([], __read(existing), false), __read(middlewares), false), target, propertyKey);
|
|
305
|
+
export function Use(...middlewares) {
|
|
306
|
+
return (target, propertyKey, _descriptor) => {
|
|
307
|
+
const existing = Reflect.getMetadata(MIDDLEWARE_KEY, target, propertyKey) || [];
|
|
308
|
+
Reflect.defineMetadata(MIDDLEWARE_KEY, [...existing, ...middlewares], target, propertyKey);
|
|
251
309
|
};
|
|
252
310
|
}
|
|
253
311
|
/**
|
|
@@ -258,12 +316,12 @@ export function Use() {
|
|
|
258
316
|
* @returns Parameter decorator function
|
|
259
317
|
*/
|
|
260
318
|
function createParamDecorator(type, key) {
|
|
261
|
-
return
|
|
262
|
-
return
|
|
263
|
-
|
|
319
|
+
return (paramKey) => {
|
|
320
|
+
return (target, propertyKey, parameterIndex) => {
|
|
321
|
+
const params = Reflect.getMetadata(PARAMS_KEY, target, propertyKey) || [];
|
|
264
322
|
// Ensure parameters are ordered by index
|
|
265
|
-
params.push({ index: parameterIndex, type
|
|
266
|
-
params.sort(
|
|
323
|
+
params.push({ index: parameterIndex, type, key: paramKey || key });
|
|
324
|
+
params.sort((a, b) => a.index - b.index);
|
|
267
325
|
Reflect.defineMetadata(PARAMS_KEY, params, target, propertyKey);
|
|
268
326
|
};
|
|
269
327
|
};
|
|
@@ -282,7 +340,7 @@ function createParamDecorator(type, key) {
|
|
|
282
340
|
* }
|
|
283
341
|
* ```
|
|
284
342
|
*/
|
|
285
|
-
export
|
|
343
|
+
export const Query = createParamDecorator('query');
|
|
286
344
|
/**
|
|
287
345
|
* Decorator that extracts route parameters from request.
|
|
288
346
|
*
|
|
@@ -297,7 +355,7 @@ export var Query = createParamDecorator('query');
|
|
|
297
355
|
* }
|
|
298
356
|
* ```
|
|
299
357
|
*/
|
|
300
|
-
export
|
|
358
|
+
export const Param = createParamDecorator('param');
|
|
301
359
|
/**
|
|
302
360
|
* Decorator that extracts body or body property from request.
|
|
303
361
|
*
|
|
@@ -317,7 +375,7 @@ export var Param = createParamDecorator('param');
|
|
|
317
375
|
* }
|
|
318
376
|
* ```
|
|
319
377
|
*/
|
|
320
|
-
export
|
|
378
|
+
export const Body = createParamDecorator('body');
|
|
321
379
|
/**
|
|
322
380
|
* Decorator that injects the entire request object.
|
|
323
381
|
*
|
|
@@ -332,7 +390,7 @@ export var Body = createParamDecorator('body');
|
|
|
332
390
|
* }
|
|
333
391
|
* ```
|
|
334
392
|
*/
|
|
335
|
-
export
|
|
393
|
+
export const Req = createParamDecorator('req');
|
|
336
394
|
/**
|
|
337
395
|
* Decorator that injects the response object.
|
|
338
396
|
*
|
|
@@ -347,7 +405,7 @@ export var Req = createParamDecorator('req');
|
|
|
347
405
|
* }
|
|
348
406
|
* ```
|
|
349
407
|
*/
|
|
350
|
-
export
|
|
408
|
+
export const Res = createParamDecorator('res');
|
|
351
409
|
/**
|
|
352
410
|
* Decorator that sets a custom HTTP status code for a response.
|
|
353
411
|
*
|
|
@@ -365,15 +423,15 @@ export var Res = createParamDecorator('res');
|
|
|
365
423
|
* ```
|
|
366
424
|
*/
|
|
367
425
|
export function HttpCode(status) {
|
|
368
|
-
return
|
|
426
|
+
return (target, propertyKey, _descriptor) => {
|
|
369
427
|
Reflect.defineMetadata(HTTP_CODE_KEY, status, target, propertyKey);
|
|
370
428
|
};
|
|
371
429
|
}
|
|
372
430
|
/**
|
|
373
431
|
* Decorator that adds a custom HTTP header to the response.
|
|
374
432
|
*
|
|
375
|
-
* @param
|
|
376
|
-
* @param value - Header value
|
|
433
|
+
* @param header - Header name-value pairs or a single header name and value
|
|
434
|
+
* @param value - Header value if a single header name is provided
|
|
377
435
|
* @returns Method decorator
|
|
378
436
|
*
|
|
379
437
|
* @example
|
|
@@ -384,13 +442,53 @@ export function HttpCode(status) {
|
|
|
384
442
|
* // Response will include the Cache-Control header
|
|
385
443
|
* return { data: '...' };
|
|
386
444
|
* }
|
|
387
|
-
* ```
|
|
388
445
|
*/
|
|
389
446
|
export function Header(name, value) {
|
|
390
|
-
return
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
447
|
+
return Headers(name, value);
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Decorator that adds a custom HTTP headers to the response.
|
|
451
|
+
*
|
|
452
|
+
* @param headers - Header name-value pairs or a single header name and value
|
|
453
|
+
* @param value - Header value if a single header name is provided
|
|
454
|
+
* @returns Method decorator
|
|
455
|
+
*
|
|
456
|
+
* @example
|
|
457
|
+
* ```ts
|
|
458
|
+
* @Get('/data')
|
|
459
|
+
* @Headers('Cache-Control', 'max-age=60')
|
|
460
|
+
* getData() {
|
|
461
|
+
* // Response will include the Cache-Control header
|
|
462
|
+
* return { data: '...' };
|
|
463
|
+
* }
|
|
464
|
+
*
|
|
465
|
+
* @Get('/data/:id')
|
|
466
|
+
* @Headers({
|
|
467
|
+
* 'Cache-Control': 'max-age=60',
|
|
468
|
+
* 'X-Custom-Header': 'custom-value',
|
|
469
|
+
* 'Content-Security-Policy': "default-src 'self'"
|
|
470
|
+
* })
|
|
471
|
+
* getData() {
|
|
472
|
+
* // Response will include all specified headers
|
|
473
|
+
* return { data: '...' };
|
|
474
|
+
* }
|
|
475
|
+
*
|
|
476
|
+
* ```
|
|
477
|
+
*/
|
|
478
|
+
export function Headers(headers, value) {
|
|
479
|
+
return (target, propertyKey) => {
|
|
480
|
+
if (typeof propertyKey === 'undefined') {
|
|
481
|
+
// Class decorator
|
|
482
|
+
const existing = Reflect.getMetadata(HEADER_KEY, target) || {};
|
|
483
|
+
const newHeaders = typeof headers === 'string' ? { [headers]: value } : headers;
|
|
484
|
+
Reflect.defineMetadata(HEADER_KEY, { ...existing, ...newHeaders }, target);
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
// Method decorator
|
|
488
|
+
const existing = Reflect.getMetadata(HEADER_KEY, target, propertyKey) || {};
|
|
489
|
+
const newHeaders = typeof headers === 'string' ? { [headers]: value } : headers;
|
|
490
|
+
Reflect.defineMetadata(HEADER_KEY, { ...existing, ...newHeaders }, target, propertyKey);
|
|
491
|
+
}
|
|
394
492
|
};
|
|
395
493
|
}
|
|
396
494
|
/**
|
|
@@ -410,8 +508,8 @@ export function Header(name, value) {
|
|
|
410
508
|
* ```
|
|
411
509
|
*/
|
|
412
510
|
export function Before(fn) {
|
|
413
|
-
return
|
|
414
|
-
|
|
511
|
+
return (target, propertyKey, _descriptor) => {
|
|
512
|
+
const hooks = Reflect.getMetadata(BEFORE_KEY, target, propertyKey) || [];
|
|
415
513
|
hooks.push(fn);
|
|
416
514
|
Reflect.defineMetadata(BEFORE_KEY, hooks, target, propertyKey);
|
|
417
515
|
};
|
|
@@ -434,8 +532,8 @@ export function Before(fn) {
|
|
|
434
532
|
* ```
|
|
435
533
|
*/
|
|
436
534
|
export function After(fn) {
|
|
437
|
-
return
|
|
438
|
-
|
|
535
|
+
return (target, propertyKey, _descriptor) => {
|
|
536
|
+
const hooks = Reflect.getMetadata(AFTER_KEY, target, propertyKey) || [];
|
|
439
537
|
hooks.push(fn);
|
|
440
538
|
Reflect.defineMetadata(AFTER_KEY, hooks, target, propertyKey);
|
|
441
539
|
};
|
|
@@ -444,6 +542,8 @@ export function After(fn) {
|
|
|
444
542
|
* Decorator that requires specific roles for accessing a route.
|
|
445
543
|
* Must be used with authentication middleware.
|
|
446
544
|
*
|
|
545
|
+
* Check req.user.roles for user roles[].
|
|
546
|
+
*
|
|
447
547
|
* @param roles - List of roles that can access this route
|
|
448
548
|
* @returns Method decorator
|
|
449
549
|
*
|
|
@@ -457,13 +557,16 @@ export function After(fn) {
|
|
|
457
557
|
* }
|
|
458
558
|
* ```
|
|
459
559
|
*/
|
|
460
|
-
export function Roles() {
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
560
|
+
export function Roles(...roles) {
|
|
561
|
+
return (target, propertyKey, _descriptor) => {
|
|
562
|
+
if (typeof propertyKey === 'undefined') {
|
|
563
|
+
// Class decorator
|
|
564
|
+
Reflect.defineMetadata(ROLES_KEY, roles, target);
|
|
565
|
+
}
|
|
566
|
+
else {
|
|
567
|
+
// Method decorator
|
|
568
|
+
Reflect.defineMetadata(ROLES_KEY, roles, target, propertyKey);
|
|
569
|
+
}
|
|
467
570
|
};
|
|
468
571
|
}
|
|
469
572
|
/**
|
|
@@ -489,14 +592,220 @@ export function Roles() {
|
|
|
489
592
|
* }
|
|
490
593
|
* ```
|
|
491
594
|
*/
|
|
492
|
-
export function Redirect(url, statusCode) {
|
|
493
|
-
|
|
494
|
-
return function (target, propertyKey, descriptor) {
|
|
595
|
+
export function Redirect(url, statusCode = 302) {
|
|
596
|
+
return (target, propertyKey, descriptor) => {
|
|
495
597
|
// Store the redirect information in metadata
|
|
496
|
-
Reflect.defineMetadata(REDIRECT_KEY, { url
|
|
598
|
+
Reflect.defineMetadata(REDIRECT_KEY, { url, statusCode }, target, propertyKey);
|
|
497
599
|
return descriptor;
|
|
498
600
|
};
|
|
499
601
|
}
|
|
602
|
+
/**
|
|
603
|
+
* Decorator that adds caching to a route response.
|
|
604
|
+
*
|
|
605
|
+
* @param ttlSeconds - Time to live in seconds for the cache
|
|
606
|
+
* @returns Method decorator
|
|
607
|
+
*
|
|
608
|
+
* @example
|
|
609
|
+
* ```ts
|
|
610
|
+
* @Get('/data')
|
|
611
|
+
* @Cache(300) // Cache for 5 minutes
|
|
612
|
+
* getData() {
|
|
613
|
+
* return { data: 'expensive operation result' };
|
|
614
|
+
* }
|
|
615
|
+
* ```
|
|
616
|
+
*/
|
|
617
|
+
export function Cache(ttlSeconds) {
|
|
618
|
+
return (target, propertyKey, _descriptor) => {
|
|
619
|
+
if (typeof propertyKey === 'undefined') {
|
|
620
|
+
// Class decorator
|
|
621
|
+
Reflect.defineMetadata(CACHE_KEY, { ttlSeconds }, target);
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
// Method decorator
|
|
625
|
+
Reflect.defineMetadata(CACHE_KEY, { ttlSeconds }, target, propertyKey);
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Decorator that applies rate limiting to a route.
|
|
631
|
+
* Note: Requires 'express-rate-limit' package to be installed.
|
|
632
|
+
*
|
|
633
|
+
* @param limit - Maximum number of requests allowed in the window
|
|
634
|
+
* @param windowMs - Time window in milliseconds
|
|
635
|
+
* @returns Method decorator
|
|
636
|
+
*
|
|
637
|
+
* Default Options:
|
|
638
|
+
* - standardHeaders: true
|
|
639
|
+
* - legacyHeaders: false
|
|
640
|
+
*
|
|
641
|
+
* @example
|
|
642
|
+
* ```ts
|
|
643
|
+
* @Post('/login')
|
|
644
|
+
* @RateLimit({ max: 5, windowMs: 60000, standardHeaders: true, legacyHeaders: false }) // 5 requests per minute
|
|
645
|
+
* login(@Body() credentials: LoginDto) {
|
|
646
|
+
* return this.authService.login(credentials);
|
|
647
|
+
* }
|
|
648
|
+
* ```
|
|
649
|
+
*/
|
|
650
|
+
export function RateLimit(options) {
|
|
651
|
+
const opts = {
|
|
652
|
+
standardHeaders: true,
|
|
653
|
+
legacyHeaders: false,
|
|
654
|
+
...options
|
|
655
|
+
};
|
|
656
|
+
return (target, propertyKey, _descriptor) => {
|
|
657
|
+
if (typeof propertyKey === 'undefined') {
|
|
658
|
+
// Class decorator
|
|
659
|
+
Reflect.defineMetadata(RATE_LIMIT_KEY, opts, target);
|
|
660
|
+
}
|
|
661
|
+
else {
|
|
662
|
+
// Method decorator
|
|
663
|
+
Reflect.defineMetadata(RATE_LIMIT_KEY, opts, target, propertyKey);
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Decorator that sets the content type for the response.
|
|
669
|
+
*
|
|
670
|
+
* @param type - MIME type for the response
|
|
671
|
+
* @returns Method decorator
|
|
672
|
+
*
|
|
673
|
+
* @example
|
|
674
|
+
* ```ts
|
|
675
|
+
* @Get('/download')
|
|
676
|
+
* @ContentType('application/pdf')
|
|
677
|
+
* downloadPdf() {
|
|
678
|
+
* return this.fileService.generatePdf();
|
|
679
|
+
* }
|
|
680
|
+
* ```
|
|
681
|
+
*/
|
|
682
|
+
export function ContentType(type) {
|
|
683
|
+
return (target, propertyKey, _descriptor) => {
|
|
684
|
+
Reflect.defineMetadata(CONTENT_TYPE_KEY, { type }, target, propertyKey);
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Decorator that adds API versioning to a route.
|
|
689
|
+
*
|
|
690
|
+
* @param version - Version string for the API endpoint
|
|
691
|
+
* @param options - Versioning options
|
|
692
|
+
* @returns Method decorator
|
|
693
|
+
*
|
|
694
|
+
* Default Options:
|
|
695
|
+
* - addPrefix: true
|
|
696
|
+
* - addHeader: true
|
|
697
|
+
* - headerName: 'X-API-Version'
|
|
698
|
+
*
|
|
699
|
+
* @example
|
|
700
|
+
* ```ts
|
|
701
|
+
*
|
|
702
|
+
* @Get('/users')
|
|
703
|
+
* @Version('v2')
|
|
704
|
+
* getUsersV2() {
|
|
705
|
+
* return this.userService.findAllV2();
|
|
706
|
+
* }
|
|
707
|
+
*
|
|
708
|
+
* @Get('/users')
|
|
709
|
+
* @Version('v2', { addPrefix: true, addHeader: true, headerName: 'X-API-Version' })
|
|
710
|
+
* getUsersV2() {
|
|
711
|
+
* // Route becomes /v2/users
|
|
712
|
+
* return this.userService.findAllV2();
|
|
713
|
+
* }
|
|
714
|
+
* ```
|
|
715
|
+
*/
|
|
716
|
+
export function Version(version, options) {
|
|
717
|
+
const opts = {
|
|
718
|
+
addPrefix: true,
|
|
719
|
+
addHeader: true,
|
|
720
|
+
headerName: 'X-API-Version',
|
|
721
|
+
...options
|
|
722
|
+
};
|
|
723
|
+
return (target, propertyKey, _descriptor) => {
|
|
724
|
+
if (typeof propertyKey === 'undefined') {
|
|
725
|
+
// Class decorator
|
|
726
|
+
Reflect.defineMetadata(VERSION_KEY, { version, options: opts }, target);
|
|
727
|
+
}
|
|
728
|
+
else {
|
|
729
|
+
// Method decorator
|
|
730
|
+
Reflect.defineMetadata(VERSION_KEY, { version, options: opts }, target, propertyKey);
|
|
731
|
+
}
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Decorator that sets a timeout for route execution.
|
|
736
|
+
*
|
|
737
|
+
* @param ms - Timeout in milliseconds
|
|
738
|
+
* @returns Method decorator
|
|
739
|
+
*
|
|
740
|
+
* @example
|
|
741
|
+
* ```ts
|
|
742
|
+
* @Get('/slow-operation')
|
|
743
|
+
* @Timeout(30000) // 30 second timeout
|
|
744
|
+
* slowOperation() {
|
|
745
|
+
* return this.heavyService.processData();
|
|
746
|
+
* }
|
|
747
|
+
* ```
|
|
748
|
+
*/
|
|
749
|
+
export function Timeout(ms) {
|
|
750
|
+
return (target, propertyKey, _descriptor) => {
|
|
751
|
+
if (typeof propertyKey === 'undefined') {
|
|
752
|
+
// Class decorator
|
|
753
|
+
Reflect.defineMetadata(TIMEOUT_KEY, { ms }, target);
|
|
754
|
+
}
|
|
755
|
+
else {
|
|
756
|
+
// Method decorator
|
|
757
|
+
Reflect.defineMetadata(TIMEOUT_KEY, { ms }, target, propertyKey);
|
|
758
|
+
}
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Decorator that adds comprehensive logging to a route.
|
|
763
|
+
*
|
|
764
|
+
* @param options - Logging configuration options
|
|
765
|
+
* @returns Method decorator
|
|
766
|
+
*
|
|
767
|
+
* Default Options:
|
|
768
|
+
* - logEntry: true
|
|
769
|
+
* - logExit: true
|
|
770
|
+
* - logBody: false
|
|
771
|
+
* - logParams: false
|
|
772
|
+
* - logResponse: false
|
|
773
|
+
*
|
|
774
|
+
* @example
|
|
775
|
+
* ```ts
|
|
776
|
+
* @Post('/users')
|
|
777
|
+
* @Log({
|
|
778
|
+
* logEntry: true,
|
|
779
|
+
* logExit: true,
|
|
780
|
+
* logBody: true,
|
|
781
|
+
* logParams: true,
|
|
782
|
+
* logResponse: false
|
|
783
|
+
* })
|
|
784
|
+
* createUser(@Body() userData: any) {
|
|
785
|
+
* return this.userService.create(userData);
|
|
786
|
+
* }
|
|
787
|
+
* ```
|
|
788
|
+
*/
|
|
789
|
+
export function Log(options) {
|
|
790
|
+
return (target, propertyKey, _descriptor) => {
|
|
791
|
+
const config = {
|
|
792
|
+
logEntry: true,
|
|
793
|
+
logExit: true,
|
|
794
|
+
logBody: false,
|
|
795
|
+
logParams: false,
|
|
796
|
+
logResponse: false,
|
|
797
|
+
...options
|
|
798
|
+
};
|
|
799
|
+
if (typeof propertyKey === 'undefined') {
|
|
800
|
+
// Class decorator
|
|
801
|
+
Reflect.defineMetadata(LOG_KEY, config, target);
|
|
802
|
+
}
|
|
803
|
+
else {
|
|
804
|
+
// Method decorator
|
|
805
|
+
Reflect.defineMetadata(LOG_KEY, config, target, propertyKey);
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
}
|
|
500
809
|
/**
|
|
501
810
|
* Registers all controller classes with the provided router.
|
|
502
811
|
* This function processes all decorators and sets up the Express routes.
|
|
@@ -505,162 +814,200 @@ export function Redirect(url, statusCode) {
|
|
|
505
814
|
* @param controllers - Array of controller classes
|
|
506
815
|
*/
|
|
507
816
|
export function registerControllers(router, controllers) {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
return [7 /*endfinally*/];
|
|
567
|
-
case 8:
|
|
568
|
-
args_1 = [];
|
|
569
|
-
if (params.length) {
|
|
570
|
-
params.forEach(function (_a) {
|
|
571
|
-
var _b;
|
|
572
|
-
var index = _a.index, type = _a.type, key = _a.key;
|
|
573
|
-
switch (type) {
|
|
574
|
-
case 'query':
|
|
575
|
-
args_1[index] = key ? req.query[key] : req.query;
|
|
576
|
-
break;
|
|
577
|
-
case 'param':
|
|
578
|
-
args_1[index] = key ? req.params[key] : req.params;
|
|
579
|
-
break;
|
|
580
|
-
case 'body':
|
|
581
|
-
args_1[index] = key ? (_b = req.body) === null || _b === void 0 ? void 0 : _b[key] : req.body;
|
|
582
|
-
break;
|
|
583
|
-
case 'req':
|
|
584
|
-
args_1[index] = req;
|
|
585
|
-
break;
|
|
586
|
-
case 'res':
|
|
587
|
-
args_1[index] = res;
|
|
588
|
-
break;
|
|
589
|
-
}
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
result = (_f = instance)[handlerName].apply(_f, __spreadArray([], __read(args_1), false));
|
|
593
|
-
if (!(result instanceof Promise)) return [3 /*break*/, 10];
|
|
594
|
-
return [4 /*yield*/, result];
|
|
595
|
-
case 9:
|
|
596
|
-
_a = _o.sent();
|
|
597
|
-
return [3 /*break*/, 11];
|
|
598
|
-
case 10:
|
|
599
|
-
_a = result;
|
|
600
|
-
_o.label = 11;
|
|
601
|
-
case 11:
|
|
602
|
-
awaited = _a;
|
|
603
|
-
// Handle dynamic redirects
|
|
604
|
-
if (redirect && awaited && typeof awaited === 'object' && 'url' in awaited) {
|
|
605
|
-
redirectUrl = awaited.url;
|
|
606
|
-
redirectStatus = awaited.statusCode || redirect.statusCode;
|
|
607
|
-
return [2 /*return*/, res.redirect(redirectStatus, redirectUrl)];
|
|
817
|
+
controllers.forEach(ControllerClass => {
|
|
818
|
+
// Use DI container to resolve controller (constructor injection + property injection)
|
|
819
|
+
const instance = diContainer.get(ControllerClass);
|
|
820
|
+
const basePath = Reflect.getMetadata('basePath', ControllerClass) || '';
|
|
821
|
+
const routes = Reflect.getMetadata(ROUTES_KEY, ControllerClass) || [];
|
|
822
|
+
// Get controller-level decorators (fallback values)
|
|
823
|
+
const controllerRateLimit = Reflect.getMetadata(RATE_LIMIT_KEY, ControllerClass);
|
|
824
|
+
const controllerCache = Reflect.getMetadata(CACHE_KEY, ControllerClass);
|
|
825
|
+
const controllerTimeout = Reflect.getMetadata(TIMEOUT_KEY, ControllerClass);
|
|
826
|
+
const controllerVersion = Reflect.getMetadata(VERSION_KEY, ControllerClass);
|
|
827
|
+
const controllerRoles = Reflect.getMetadata(ROLES_KEY, ControllerClass);
|
|
828
|
+
const controllerLogConfig = Reflect.getMetadata(LOG_KEY, ControllerClass);
|
|
829
|
+
const controllerHeaders = Reflect.getMetadata(HEADER_KEY, ControllerClass) || {};
|
|
830
|
+
routes.forEach(({ path, method, handlerName }) => {
|
|
831
|
+
const middlewares = Reflect.getMetadata(MIDDLEWARE_KEY, instance, handlerName) || [];
|
|
832
|
+
const params = Reflect.getMetadata(PARAMS_KEY, instance, handlerName) || [];
|
|
833
|
+
const httpCode = Reflect.getMetadata(HTTP_CODE_KEY, instance, handlerName);
|
|
834
|
+
// Merge controller-level and method-level headers
|
|
835
|
+
const methodHeaders = Reflect.getMetadata(HEADER_KEY, instance, handlerName) || {};
|
|
836
|
+
const headers = { ...controllerHeaders, ...methodHeaders };
|
|
837
|
+
const beforeHooks = Reflect.getMetadata(BEFORE_KEY, instance, handlerName) || [];
|
|
838
|
+
const afterHooks = Reflect.getMetadata(AFTER_KEY, instance, handlerName) || [];
|
|
839
|
+
const redirect = Reflect.getMetadata(REDIRECT_KEY, instance, handlerName);
|
|
840
|
+
const contentType = Reflect.getMetadata(CONTENT_TYPE_KEY, instance, handlerName);
|
|
841
|
+
// Use method-level decorators if present, otherwise fall back to controller-level
|
|
842
|
+
const roles = Reflect.getMetadata(ROLES_KEY, instance, handlerName) || controllerRoles || [];
|
|
843
|
+
const cache = Reflect.getMetadata(CACHE_KEY, instance, handlerName) || controllerCache;
|
|
844
|
+
const rateLimitOptions = Reflect.getMetadata(RATE_LIMIT_KEY, instance, handlerName) || controllerRateLimit;
|
|
845
|
+
const version = Reflect.getMetadata(VERSION_KEY, instance, handlerName) || controllerVersion;
|
|
846
|
+
const timeout = Reflect.getMetadata(TIMEOUT_KEY, instance, handlerName) || controllerTimeout;
|
|
847
|
+
const logConfig = Reflect.getMetadata(LOG_KEY, instance, handlerName) || controllerLogConfig;
|
|
848
|
+
// Create rate limiter for this specific route if needed
|
|
849
|
+
let rateLimiter = null;
|
|
850
|
+
if (rateLimitOptions) {
|
|
851
|
+
try {
|
|
852
|
+
rateLimiter = rateLimiterCache.get(rateLimitOptions);
|
|
853
|
+
}
|
|
854
|
+
catch (err) {
|
|
855
|
+
getLogger().warn({ err }, 'express-rate-limit not available, skipping rate limiting for this route');
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
let finalPath = path;
|
|
859
|
+
if (version?.options?.addPrefix) {
|
|
860
|
+
finalPath = `/${version.version}${path}`;
|
|
861
|
+
}
|
|
862
|
+
const handler = async (req, res, next) => {
|
|
863
|
+
// Set start time for duration tracking
|
|
864
|
+
req['startTime'] = Date.now();
|
|
865
|
+
let timeoutId;
|
|
866
|
+
let timedOut = false;
|
|
867
|
+
try {
|
|
868
|
+
// Handle timeout setup
|
|
869
|
+
if (timeout) {
|
|
870
|
+
timeoutId = setTimeout(() => {
|
|
871
|
+
if (!res.headersSent && !timedOut) {
|
|
872
|
+
timedOut = true;
|
|
873
|
+
const errorResponse = createFinalErrorResponse(req, HttpStatusCodes.REQUEST_TIMEOUT, 'Request timed out');
|
|
874
|
+
res.status(HttpStatusCodes.REQUEST_TIMEOUT).json(errorResponse);
|
|
608
875
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
876
|
+
}, timeout.ms);
|
|
877
|
+
}
|
|
878
|
+
if (timedOut)
|
|
879
|
+
return;
|
|
880
|
+
// Handle rate limiting
|
|
881
|
+
if (rateLimiter) {
|
|
882
|
+
await new Promise((resolve, reject) => {
|
|
883
|
+
rateLimiter(req, res, (err) => {
|
|
884
|
+
if (err)
|
|
885
|
+
reject(err);
|
|
886
|
+
else
|
|
887
|
+
resolve();
|
|
888
|
+
});
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
// Handle content type
|
|
892
|
+
if (!res.headersSent && contentType) {
|
|
893
|
+
res.setHeader('Content-Type', contentType.type);
|
|
894
|
+
}
|
|
895
|
+
// Handle versioning header
|
|
896
|
+
if (version?.options?.addHeader && version?.options?.headerName && version?.version) {
|
|
897
|
+
if (!res.headersSent) {
|
|
898
|
+
res.setHeader(version.options.headerName, version.version);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
// Handle caching
|
|
902
|
+
if (cache && !res.headersSent) {
|
|
903
|
+
res.setHeader('Cache-Control', `public, max-age=${cache.ttlSeconds}`);
|
|
904
|
+
}
|
|
905
|
+
// Handle logging - entry
|
|
906
|
+
if (logConfig?.logEntry) {
|
|
907
|
+
const logger = getLogger();
|
|
908
|
+
const logData = {
|
|
909
|
+
method: req.method,
|
|
910
|
+
url: req.originalUrl || req.url,
|
|
911
|
+
userAgent: req.get('User-Agent')
|
|
912
|
+
};
|
|
913
|
+
if (logConfig.logParams) {
|
|
914
|
+
logData.params = req.params;
|
|
915
|
+
logData.query = req.query;
|
|
916
|
+
}
|
|
917
|
+
if (logConfig.logBody)
|
|
918
|
+
logData.body = req.body;
|
|
919
|
+
logger.info({ entry: logData }, 'Route Entry:');
|
|
920
|
+
}
|
|
921
|
+
// Handle roles-based access control
|
|
922
|
+
if (roles.length && !req?.user?.roles?.some((role) => roles.includes(role))) {
|
|
923
|
+
const errorResponse = createFinalErrorResponse(req, HttpStatusCodes.FORBIDDEN, 'Forbidden Insufficient Roles');
|
|
924
|
+
res.status(HttpStatusCodes.FORBIDDEN).json(errorResponse);
|
|
925
|
+
if (timeoutId) {
|
|
926
|
+
clearTimeout(timeoutId);
|
|
927
|
+
}
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
// Process static redirect if configured
|
|
931
|
+
if (redirect && redirect.url) {
|
|
932
|
+
if (timeoutId) {
|
|
933
|
+
clearTimeout(timeoutId);
|
|
934
|
+
}
|
|
935
|
+
return res.redirect(redirect.statusCode, redirect.url);
|
|
936
|
+
}
|
|
937
|
+
for (const fn of beforeHooks)
|
|
938
|
+
await fn(req, res);
|
|
939
|
+
const args = [];
|
|
940
|
+
if (params.length) {
|
|
941
|
+
params.forEach(({ index, type, key }) => {
|
|
942
|
+
switch (type) {
|
|
943
|
+
case 'query':
|
|
944
|
+
args[index] = key ? req.query[key] : req.query;
|
|
945
|
+
break;
|
|
946
|
+
case 'param':
|
|
947
|
+
args[index] = key ? req.params[key] : req.params;
|
|
948
|
+
break;
|
|
949
|
+
case 'body':
|
|
950
|
+
args[index] = key ? req.body?.[key] : req.body;
|
|
951
|
+
break;
|
|
952
|
+
case 'req':
|
|
953
|
+
args[index] = req;
|
|
954
|
+
break;
|
|
955
|
+
case 'res':
|
|
956
|
+
args[index] = res;
|
|
957
|
+
break;
|
|
626
958
|
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
const result = instance[handlerName](...args);
|
|
962
|
+
// Support both sync and async handlers
|
|
963
|
+
const awaited = result instanceof Promise ? await result : result;
|
|
964
|
+
// Clear timeout if operation completed
|
|
965
|
+
if (timeoutId) {
|
|
966
|
+
clearTimeout(timeoutId);
|
|
967
|
+
}
|
|
968
|
+
if (timedOut)
|
|
969
|
+
return;
|
|
970
|
+
// Handle dynamic redirects
|
|
971
|
+
if (redirect && awaited && typeof awaited === 'object' && 'url' in awaited) {
|
|
972
|
+
const redirectUrl = awaited.url;
|
|
973
|
+
const redirectStatus = awaited.statusCode || redirect.statusCode;
|
|
974
|
+
return res.redirect(redirectStatus, redirectUrl);
|
|
975
|
+
}
|
|
976
|
+
if (!res.headersSent && typeof awaited !== 'undefined') {
|
|
977
|
+
if (httpCode)
|
|
978
|
+
res.status(httpCode);
|
|
979
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
980
|
+
const normalized = normalizeHeaderValue(v);
|
|
981
|
+
if (typeof normalized !== 'undefined') {
|
|
982
|
+
res.set(k, normalized);
|
|
650
983
|
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
984
|
+
}
|
|
985
|
+
res.json(awaited);
|
|
986
|
+
}
|
|
987
|
+
// Handle logging - exit
|
|
988
|
+
if (logConfig?.logExit) {
|
|
989
|
+
const logger = getLogger();
|
|
990
|
+
const logData = {
|
|
991
|
+
method: req.method,
|
|
992
|
+
url: req.originalUrl || req.url,
|
|
993
|
+
statusCode: res.statusCode,
|
|
994
|
+
duration: `${Date.now() - req.startTime}ms`
|
|
995
|
+
};
|
|
996
|
+
if (logConfig.logResponse)
|
|
997
|
+
logData.response = awaited;
|
|
998
|
+
logger.info({ exit: logData }, 'Route Exit:');
|
|
999
|
+
}
|
|
1000
|
+
for (const fn of afterHooks)
|
|
1001
|
+
await fn(req, res, awaited);
|
|
1002
|
+
}
|
|
1003
|
+
catch (err) {
|
|
1004
|
+
if (timeoutId) {
|
|
1005
|
+
clearTimeout(timeoutId);
|
|
659
1006
|
}
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
1007
|
+
next(err);
|
|
1008
|
+
}
|
|
1009
|
+
};
|
|
1010
|
+
router[method](basePath + finalPath, ...middlewares, handler);
|
|
663
1011
|
});
|
|
664
1012
|
});
|
|
665
1013
|
}
|
|
666
|
-
//# sourceMappingURL=decorators.utils.js.map
|