@catbee/utils 2.0.0-next.1 → 2.0.1
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/LICENSE +1 -1
- package/README.md +55 -38
- package/array/index.cjs +37 -5
- package/array/index.d.ts +59 -8
- package/array/index.mjs +32 -4
- package/async/index.cjs +24 -3
- package/async/index.d.ts +18 -2
- package/async/index.mjs +24 -4
- package/cache/index.cjs +1 -1
- package/cache/index.d.ts +1 -1
- package/cache/index.mjs +1 -1
- package/config/index.cjs +8 -8
- package/config/index.d.ts +1 -1
- package/config/index.mjs +4 -4
- package/context-store/index.cjs +2 -3
- package/context-store/index.d.ts +1 -1
- package/context-store/index.mjs +2 -3
- package/crypto/index.cjs +55 -5
- package/crypto/index.d.ts +63 -2
- package/crypto/index.mjs +52 -7
- package/date/index.cjs +630 -1
- package/date/index.d.ts +487 -2
- package/date/index.mjs +621 -2
- package/{decorators → decorator}/index.cjs +425 -304
- package/{decorators → decorator}/index.d.ts +3 -2
- package/{decorators → decorator}/index.mjs +425 -304
- package/{dir → directory}/index.cjs +1 -1
- package/{dir → directory}/index.d.ts +1 -1
- package/{dir → directory}/index.mjs +1 -1
- package/env/index.cjs +90 -42
- package/env/index.d.ts +12 -1
- package/env/index.mjs +90 -42
- package/exception/index.cjs +1 -1
- package/exception/index.d.ts +1 -1
- package/exception/index.mjs +1 -1
- package/fs/index.cjs +1 -1
- package/fs/index.d.ts +1 -1
- package/fs/index.mjs +1 -1
- package/http-status-codes/index.cjs +1 -1
- package/http-status-codes/index.d.ts +1 -1
- package/http-status-codes/index.mjs +1 -1
- package/id/index.cjs +1 -1
- package/id/index.d.ts +1 -1
- package/id/index.mjs +1 -1
- package/index.cjs +10 -10
- package/index.d.ts +4 -4
- package/index.mjs +4 -4
- package/logger/index.cjs +2 -4
- package/logger/index.d.ts +2 -2
- package/logger/index.mjs +2 -4
- package/middleware/index.cjs +1 -1
- package/middleware/index.d.ts +1 -1
- package/middleware/index.mjs +1 -1
- package/{obj → object}/index.cjs +182 -108
- package/{obj → object}/index.d.ts +38 -2
- package/{obj → object}/index.mjs +180 -109
- package/package.json +31 -13
- package/performance/index.cjs +2 -2
- package/performance/index.d.ts +1 -1
- package/performance/index.mjs +2 -2
- package/request/index.cjs +36 -24
- package/request/index.d.ts +1 -1
- package/request/index.mjs +36 -24
- package/response/index.cjs +1 -1
- package/response/index.d.ts +1 -1
- package/response/index.mjs +1 -1
- package/server/index.cjs +233 -131
- package/server/index.d.ts +81 -1
- package/server/index.mjs +229 -127
- package/stream/index.cjs +1 -1
- package/stream/index.d.ts +1 -1
- package/stream/index.mjs +1 -1
- package/string/index.cjs +34 -1
- package/string/index.d.ts +45 -2
- package/string/index.mjs +31 -2
- package/type/index.cjs +18 -1
- package/type/index.d.ts +38 -2
- package/type/index.mjs +16 -2
- package/types/index.cjs +1 -1
- package/types/index.d.ts +10 -3
- package/types/index.mjs +1 -1
- package/url/index.cjs +61 -1
- package/url/index.d.ts +59 -2
- package/url/index.mjs +57 -2
- package/validation/index.cjs +3 -3
- package/validation/index.d.ts +1 -1
- package/validation/index.mjs +3 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* The MIT License
|
|
3
3
|
*
|
|
4
|
-
* Copyright (c)
|
|
4
|
+
* Copyright (c) 2026 Catbee Technologies. https://catbee.in/license
|
|
5
5
|
*
|
|
6
6
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
7
|
* of this software and associated documentation files (the "Software"), to deal
|
|
@@ -1152,7 +1152,7 @@ var require_Reflect = __commonJS({
|
|
|
1152
1152
|
}
|
|
1153
1153
|
});
|
|
1154
1154
|
|
|
1155
|
-
// src/
|
|
1155
|
+
// src/decorator/decorator.utils.ts
|
|
1156
1156
|
__toESM(require_Reflect());
|
|
1157
1157
|
var ROUTES_KEY = Symbol("routes");
|
|
1158
1158
|
var MIDDLEWARE_KEY = Symbol("middlewares");
|
|
@@ -1276,7 +1276,7 @@ var DIContainer = class {
|
|
|
1276
1276
|
};
|
|
1277
1277
|
var diContainer = new DIContainer();
|
|
1278
1278
|
function normalizeHeaderValue(value) {
|
|
1279
|
-
if (
|
|
1279
|
+
if (value === void 0) return void 0;
|
|
1280
1280
|
if (typeof value === "string") return value;
|
|
1281
1281
|
if (Array.isArray(value) && value.every((item) => typeof item === "string")) {
|
|
1282
1282
|
return value;
|
|
@@ -1615,223 +1615,309 @@ function Log(options) {
|
|
|
1615
1615
|
__name(Log, "Log");
|
|
1616
1616
|
function registerControllers(router, controllers) {
|
|
1617
1617
|
controllers.forEach((ControllerClass) => {
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1618
|
+
registerSingleController(router, ControllerClass);
|
|
1619
|
+
});
|
|
1620
|
+
}
|
|
1621
|
+
__name(registerControllers, "registerControllers");
|
|
1622
|
+
function registerSingleController(router, ControllerClass) {
|
|
1623
|
+
if (!Reflect.getMetadata("injectable", ControllerClass)) {
|
|
1624
|
+
Reflect.defineMetadata("injectable", true, ControllerClass);
|
|
1625
|
+
diContainer.register(ControllerClass);
|
|
1626
|
+
}
|
|
1627
|
+
const instance = diContainer.get(ControllerClass);
|
|
1628
|
+
const controllerMetadata = getControllerMetadata(ControllerClass);
|
|
1629
|
+
const routes = Reflect.getMetadata(ROUTES_KEY, ControllerClass) || [];
|
|
1630
|
+
routes.forEach((routeDef) => {
|
|
1631
|
+
registerRoute(router, instance, controllerMetadata, routeDef);
|
|
1632
|
+
});
|
|
1633
|
+
}
|
|
1634
|
+
__name(registerSingleController, "registerSingleController");
|
|
1635
|
+
function getControllerMetadata(ControllerClass) {
|
|
1636
|
+
let basePath = Reflect.getMetadata("basePath", ControllerClass) || "";
|
|
1637
|
+
const controllerVersion = Reflect.getMetadata(VERSION_KEY, ControllerClass);
|
|
1638
|
+
if (controllerVersion?.options?.addPrefix) {
|
|
1639
|
+
basePath = `/${controllerVersion.version}${basePath || ""}`;
|
|
1640
|
+
}
|
|
1641
|
+
return {
|
|
1642
|
+
basePath,
|
|
1643
|
+
rateLimit: Reflect.getMetadata(RATE_LIMIT_KEY, ControllerClass),
|
|
1644
|
+
cache: Reflect.getMetadata(CACHE_KEY, ControllerClass),
|
|
1645
|
+
timeout: Reflect.getMetadata(TIMEOUT_KEY, ControllerClass),
|
|
1646
|
+
version: controllerVersion,
|
|
1647
|
+
roles: Reflect.getMetadata(ROLES_KEY, ControllerClass),
|
|
1648
|
+
logConfig: Reflect.getMetadata(LOG_KEY, ControllerClass),
|
|
1649
|
+
headers: Reflect.getMetadata(HEADER_KEY, ControllerClass) || {},
|
|
1650
|
+
middlewares: Reflect.getMetadata(MIDDLEWARE_KEY, ControllerClass) || [],
|
|
1651
|
+
beforeHooks: Reflect.getMetadata(BEFORE_KEY, ControllerClass) || [],
|
|
1652
|
+
afterHooks: Reflect.getMetadata(AFTER_KEY, ControllerClass) || [],
|
|
1653
|
+
contentType: Reflect.getMetadata(CONTENT_TYPE_KEY, ControllerClass)
|
|
1654
|
+
};
|
|
1655
|
+
}
|
|
1656
|
+
__name(getControllerMetadata, "getControllerMetadata");
|
|
1657
|
+
function registerRoute(router, instance, controllerMetadata, routeDef) {
|
|
1658
|
+
const { path, method, handlerName } = routeDef;
|
|
1659
|
+
const routeMetadata = getRouteMetadata(instance, handlerName, controllerMetadata);
|
|
1660
|
+
const handler = createRouteHandler(instance, handlerName, routeMetadata);
|
|
1661
|
+
const finalPath = calculateFinalPath(path, routeMetadata, controllerMetadata);
|
|
1662
|
+
router[method](controllerMetadata.basePath + finalPath, ...routeMetadata.middlewares, handler);
|
|
1663
|
+
}
|
|
1664
|
+
__name(registerRoute, "registerRoute");
|
|
1665
|
+
function getRouteMetadata(instance, handlerName, controllerMetadata) {
|
|
1666
|
+
const methodMiddlewares = Reflect.getMetadata(MIDDLEWARE_KEY, instance, handlerName) || [];
|
|
1667
|
+
const params = Reflect.getMetadata(PARAMS_KEY, instance, handlerName) || [];
|
|
1668
|
+
const httpCode = Reflect.getMetadata(HTTP_CODE_KEY, instance, handlerName);
|
|
1669
|
+
const methodHeaders = Reflect.getMetadata(HEADER_KEY, instance, handlerName) || {};
|
|
1670
|
+
const methodBeforeHooks = Reflect.getMetadata(BEFORE_KEY, instance, handlerName) || [];
|
|
1671
|
+
const methodAfterHooks = Reflect.getMetadata(AFTER_KEY, instance, handlerName) || [];
|
|
1672
|
+
const redirect = Reflect.getMetadata(REDIRECT_KEY, instance, handlerName);
|
|
1673
|
+
const methodContentType = Reflect.getMetadata(CONTENT_TYPE_KEY, instance, handlerName);
|
|
1674
|
+
const methodVersion = Reflect.getMetadata(VERSION_KEY, instance, handlerName);
|
|
1675
|
+
const headers = {
|
|
1676
|
+
...controllerMetadata.headers,
|
|
1677
|
+
...methodHeaders
|
|
1678
|
+
};
|
|
1679
|
+
const middlewares = [
|
|
1680
|
+
...controllerMetadata.middlewares,
|
|
1681
|
+
...methodMiddlewares
|
|
1682
|
+
];
|
|
1683
|
+
const beforeHooks = [
|
|
1684
|
+
...controllerMetadata.beforeHooks,
|
|
1685
|
+
...methodBeforeHooks
|
|
1686
|
+
];
|
|
1687
|
+
const afterHooks = [
|
|
1688
|
+
...methodAfterHooks,
|
|
1689
|
+
...controllerMetadata.afterHooks
|
|
1690
|
+
];
|
|
1691
|
+
const contentType = methodContentType || controllerMetadata.contentType;
|
|
1692
|
+
const roles = Reflect.getMetadata(ROLES_KEY, instance, handlerName) || controllerMetadata.roles || [];
|
|
1693
|
+
const cache = Reflect.getMetadata(CACHE_KEY, instance, handlerName) || controllerMetadata.cache;
|
|
1694
|
+
const rateLimitOptions = Reflect.getMetadata(RATE_LIMIT_KEY, instance, handlerName) || controllerMetadata.rateLimit;
|
|
1695
|
+
const version = methodVersion || controllerMetadata.version;
|
|
1696
|
+
const timeout = Reflect.getMetadata(TIMEOUT_KEY, instance, handlerName) || controllerMetadata.timeout;
|
|
1697
|
+
const logConfig = Reflect.getMetadata(LOG_KEY, instance, handlerName) || controllerMetadata.logConfig;
|
|
1698
|
+
let rateLimiter = null;
|
|
1699
|
+
if (rateLimitOptions) {
|
|
1700
|
+
try {
|
|
1701
|
+
rateLimiter = rateLimiterCache.get(rateLimitOptions);
|
|
1702
|
+
} catch (err) {
|
|
1703
|
+
logger.getLogger().warn({
|
|
1704
|
+
err
|
|
1705
|
+
}, "express-rate-limit not available, skipping rate limiting for this route");
|
|
1638
1706
|
}
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
if (
|
|
1674
|
-
|
|
1675
|
-
rateLimiter = rateLimiterCache.get(rateLimitOptions);
|
|
1676
|
-
} catch (err) {
|
|
1677
|
-
logger.getLogger().warn({
|
|
1678
|
-
err
|
|
1679
|
-
}, "express-rate-limit not available, skipping rate limiting for this route");
|
|
1680
|
-
}
|
|
1707
|
+
}
|
|
1708
|
+
return {
|
|
1709
|
+
params,
|
|
1710
|
+
httpCode,
|
|
1711
|
+
headers,
|
|
1712
|
+
middlewares,
|
|
1713
|
+
beforeHooks,
|
|
1714
|
+
afterHooks,
|
|
1715
|
+
redirect,
|
|
1716
|
+
contentType,
|
|
1717
|
+
roles,
|
|
1718
|
+
cache,
|
|
1719
|
+
rateLimiter,
|
|
1720
|
+
version,
|
|
1721
|
+
methodVersion,
|
|
1722
|
+
timeout,
|
|
1723
|
+
logConfig
|
|
1724
|
+
};
|
|
1725
|
+
}
|
|
1726
|
+
__name(getRouteMetadata, "getRouteMetadata");
|
|
1727
|
+
function calculateFinalPath(path, routeMetadata, controllerMetadata) {
|
|
1728
|
+
let finalPath = path;
|
|
1729
|
+
if (routeMetadata.methodVersion?.options?.addPrefix && routeMetadata.methodVersion.version !== controllerMetadata.version?.version) {
|
|
1730
|
+
finalPath = `/${routeMetadata.methodVersion.version}${path}`;
|
|
1731
|
+
}
|
|
1732
|
+
return finalPath;
|
|
1733
|
+
}
|
|
1734
|
+
__name(calculateFinalPath, "calculateFinalPath");
|
|
1735
|
+
function createRouteHandler(instance, handlerName, routeMetadata) {
|
|
1736
|
+
return async (req, res, next) => {
|
|
1737
|
+
req["startTime"] = Date.now();
|
|
1738
|
+
const timeoutState = setupTimeout(req, res, routeMetadata.timeout);
|
|
1739
|
+
try {
|
|
1740
|
+
if (timeoutState.timedOut) return;
|
|
1741
|
+
if (routeMetadata.rateLimiter) {
|
|
1742
|
+
await applyRateLimiting(req, res, routeMetadata.rateLimiter);
|
|
1681
1743
|
}
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1744
|
+
applyResponseHeaders(res, routeMetadata);
|
|
1745
|
+
logRouteEntry(req, routeMetadata.logConfig);
|
|
1746
|
+
if (!checkRoles(req, res, routeMetadata.roles, timeoutState)) {
|
|
1747
|
+
return;
|
|
1685
1748
|
}
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
if (logConfig.logBody) logData.body = req.body;
|
|
1732
|
-
logger$1.info({
|
|
1733
|
-
entry: logData
|
|
1734
|
-
}, "Route Entry:");
|
|
1735
|
-
}
|
|
1736
|
-
if (roles.length && !req?.user?.roles?.some((role) => roles.includes(role))) {
|
|
1737
|
-
const errorResponse = response.createFinalErrorResponse(req, httpStatusCodes.HttpStatusCodes.FORBIDDEN, "Forbidden Insufficient Roles");
|
|
1738
|
-
res.status(httpStatusCodes.HttpStatusCodes.FORBIDDEN).json(errorResponse);
|
|
1739
|
-
if (timeoutId) {
|
|
1740
|
-
clearTimeout(timeoutId);
|
|
1741
|
-
}
|
|
1742
|
-
return;
|
|
1743
|
-
}
|
|
1744
|
-
if (redirect?.url) {
|
|
1745
|
-
if (timeoutId) {
|
|
1746
|
-
clearTimeout(timeoutId);
|
|
1747
|
-
}
|
|
1748
|
-
return res.redirect(redirect.statusCode, redirect.url);
|
|
1749
|
-
}
|
|
1750
|
-
for (const fn of beforeHooks) await fn(req, res);
|
|
1751
|
-
const args = [];
|
|
1752
|
-
if (params.length) {
|
|
1753
|
-
params.forEach(({ index, type, key, options }) => {
|
|
1754
|
-
let rawValue;
|
|
1755
|
-
switch (type) {
|
|
1756
|
-
case "query":
|
|
1757
|
-
rawValue = key ? req.query[key] : req.query;
|
|
1758
|
-
args[index] = options ? applyParamOptions(rawValue, options, key) : rawValue;
|
|
1759
|
-
break;
|
|
1760
|
-
case "param":
|
|
1761
|
-
rawValue = key ? req.params[key] : req.params;
|
|
1762
|
-
args[index] = options ? applyParamOptions(rawValue, options, key) : rawValue;
|
|
1763
|
-
break;
|
|
1764
|
-
case "body":
|
|
1765
|
-
args[index] = key ? req.body?.[key] : req.body;
|
|
1766
|
-
break;
|
|
1767
|
-
case "req":
|
|
1768
|
-
args[index] = req;
|
|
1769
|
-
break;
|
|
1770
|
-
case "res":
|
|
1771
|
-
args[index] = res;
|
|
1772
|
-
break;
|
|
1773
|
-
case "logger":
|
|
1774
|
-
args[index] = logger.getLogger();
|
|
1775
|
-
break;
|
|
1776
|
-
case "reqHeader":
|
|
1777
|
-
args[index] = key ? req.headers[key.toLowerCase()] : req.headers;
|
|
1778
|
-
break;
|
|
1779
|
-
case "reqId":
|
|
1780
|
-
args[index] = req.headers["x-request-id"] || req?.id || void 0;
|
|
1781
|
-
break;
|
|
1782
|
-
case "cookie":
|
|
1783
|
-
args[index] = key ? req.cookies?.[key] : req.cookies;
|
|
1784
|
-
break;
|
|
1785
|
-
}
|
|
1786
|
-
});
|
|
1787
|
-
}
|
|
1788
|
-
const result = instance[handlerName](...args);
|
|
1789
|
-
const awaited = result instanceof Promise ? await result : result;
|
|
1790
|
-
if (timeoutId) {
|
|
1791
|
-
clearTimeout(timeoutId);
|
|
1792
|
-
}
|
|
1793
|
-
if (timedOut) return;
|
|
1794
|
-
if (redirect && awaited && typeof awaited === "object" && "url" in awaited) {
|
|
1795
|
-
const redirectUrl = awaited.url;
|
|
1796
|
-
const redirectStatus = awaited.statusCode || redirect.statusCode;
|
|
1797
|
-
return res.redirect(redirectStatus, redirectUrl);
|
|
1798
|
-
}
|
|
1799
|
-
if (!res.headersSent && typeof awaited !== "undefined") {
|
|
1800
|
-
if (httpCode) res.status(httpCode);
|
|
1801
|
-
for (const [k, v] of Object.entries(headers)) {
|
|
1802
|
-
const normalized = normalizeHeaderValue(v);
|
|
1803
|
-
if (typeof normalized !== "undefined") {
|
|
1804
|
-
res.set(k, normalized);
|
|
1805
|
-
}
|
|
1806
|
-
}
|
|
1807
|
-
res.json(awaited);
|
|
1808
|
-
}
|
|
1809
|
-
if (logConfig?.logExit) {
|
|
1810
|
-
const logger$1 = logger.getLogger();
|
|
1811
|
-
const logData = {
|
|
1812
|
-
method: req.method,
|
|
1813
|
-
url: req.originalUrl || req.url,
|
|
1814
|
-
statusCode: res.statusCode,
|
|
1815
|
-
duration: `${Date.now() - req.startTime}ms`
|
|
1816
|
-
};
|
|
1817
|
-
if (logConfig.logResponse) logData.response = awaited;
|
|
1818
|
-
logger$1.info({
|
|
1819
|
-
exit: logData
|
|
1820
|
-
}, "Route Exit:");
|
|
1821
|
-
}
|
|
1822
|
-
for (const fn of afterHooks) await fn(req, res, awaited);
|
|
1823
|
-
} catch (err) {
|
|
1824
|
-
if (timeoutId) {
|
|
1825
|
-
clearTimeout(timeoutId);
|
|
1826
|
-
}
|
|
1827
|
-
next(err);
|
|
1828
|
-
}
|
|
1829
|
-
}, "handler");
|
|
1830
|
-
router[method](basePath + finalPath, ...middlewares, handler);
|
|
1749
|
+
if (routeMetadata.redirect?.url) {
|
|
1750
|
+
clearTimeout(timeoutState.timeoutId);
|
|
1751
|
+
return res.redirect(routeMetadata.redirect.statusCode, routeMetadata.redirect.url);
|
|
1752
|
+
}
|
|
1753
|
+
for (const fn of routeMetadata.beforeHooks) await fn(req, res);
|
|
1754
|
+
const args = extractHandlerArgs(req, res, routeMetadata.params);
|
|
1755
|
+
const result = instance[handlerName](...args);
|
|
1756
|
+
const awaited = result instanceof Promise ? await result : result;
|
|
1757
|
+
clearTimeout(timeoutState.timeoutId);
|
|
1758
|
+
if (timeoutState.timedOut) return;
|
|
1759
|
+
if (handleDynamicRedirect(res, routeMetadata.redirect, awaited)) {
|
|
1760
|
+
return;
|
|
1761
|
+
}
|
|
1762
|
+
sendResponse(res, awaited, routeMetadata.httpCode, routeMetadata.headers);
|
|
1763
|
+
logRouteExit(req, res, awaited, routeMetadata.logConfig);
|
|
1764
|
+
for (const fn of routeMetadata.afterHooks) await fn(req, res, awaited);
|
|
1765
|
+
} catch (err) {
|
|
1766
|
+
clearTimeout(timeoutState.timeoutId);
|
|
1767
|
+
next(err);
|
|
1768
|
+
}
|
|
1769
|
+
};
|
|
1770
|
+
}
|
|
1771
|
+
__name(createRouteHandler, "createRouteHandler");
|
|
1772
|
+
function setupTimeout(req, res, timeout) {
|
|
1773
|
+
const state = {
|
|
1774
|
+
timeoutId: void 0,
|
|
1775
|
+
timedOut: false
|
|
1776
|
+
};
|
|
1777
|
+
if (timeout) {
|
|
1778
|
+
state.timeoutId = setTimeout(() => {
|
|
1779
|
+
if (!res.headersSent && !state.timedOut) {
|
|
1780
|
+
state.timedOut = true;
|
|
1781
|
+
const errorResponse = response.createFinalErrorResponse(req, httpStatusCodes.HttpStatusCodes.REQUEST_TIMEOUT, "Request timed out");
|
|
1782
|
+
res.status(httpStatusCodes.HttpStatusCodes.REQUEST_TIMEOUT).json(errorResponse);
|
|
1783
|
+
}
|
|
1784
|
+
}, timeout.ms);
|
|
1785
|
+
}
|
|
1786
|
+
return state;
|
|
1787
|
+
}
|
|
1788
|
+
__name(setupTimeout, "setupTimeout");
|
|
1789
|
+
async function applyRateLimiting(req, res, rateLimiter) {
|
|
1790
|
+
await new Promise((resolve, reject) => {
|
|
1791
|
+
rateLimiter(req, res, (err) => {
|
|
1792
|
+
if (err) reject(err);
|
|
1793
|
+
else resolve();
|
|
1831
1794
|
});
|
|
1832
1795
|
});
|
|
1833
1796
|
}
|
|
1834
|
-
__name(
|
|
1797
|
+
__name(applyRateLimiting, "applyRateLimiting");
|
|
1798
|
+
function applyResponseHeaders(res, routeMetadata) {
|
|
1799
|
+
if (!res.headersSent && routeMetadata.contentType) {
|
|
1800
|
+
res.setHeader("Content-Type", routeMetadata.contentType.type);
|
|
1801
|
+
}
|
|
1802
|
+
if (routeMetadata.version?.options?.addHeader && routeMetadata.version?.options?.headerName && routeMetadata.version?.version) {
|
|
1803
|
+
if (!res.headersSent) {
|
|
1804
|
+
res.setHeader(routeMetadata.version.options.headerName, routeMetadata.version.version);
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
if (routeMetadata.cache && !res.headersSent) {
|
|
1808
|
+
res.setHeader("Cache-Control", `public, max-age=${routeMetadata.cache.ttlSeconds}`);
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
__name(applyResponseHeaders, "applyResponseHeaders");
|
|
1812
|
+
function logRouteEntry(req, logConfig) {
|
|
1813
|
+
if (logConfig?.logEntry) {
|
|
1814
|
+
const logger$1 = logger.getLogger();
|
|
1815
|
+
const logData = {
|
|
1816
|
+
method: req.method,
|
|
1817
|
+
url: req.originalUrl || req.url,
|
|
1818
|
+
userAgent: req.get("User-Agent")
|
|
1819
|
+
};
|
|
1820
|
+
if (logConfig.logParams) {
|
|
1821
|
+
logData.params = req.params;
|
|
1822
|
+
logData.query = req.query;
|
|
1823
|
+
}
|
|
1824
|
+
if (logConfig.logBody) logData.body = req.body;
|
|
1825
|
+
logger$1.info({
|
|
1826
|
+
entry: logData
|
|
1827
|
+
}, "Route Entry:");
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
__name(logRouteEntry, "logRouteEntry");
|
|
1831
|
+
function logRouteExit(req, res, result, logConfig) {
|
|
1832
|
+
if (logConfig?.logExit) {
|
|
1833
|
+
const logger$1 = logger.getLogger();
|
|
1834
|
+
const logData = {
|
|
1835
|
+
method: req.method,
|
|
1836
|
+
url: req.originalUrl || req.url,
|
|
1837
|
+
statusCode: res.statusCode,
|
|
1838
|
+
duration: `${Date.now() - req.startTime}ms`
|
|
1839
|
+
};
|
|
1840
|
+
if (logConfig.logResponse) logData.response = result;
|
|
1841
|
+
logger$1.info({
|
|
1842
|
+
exit: logData
|
|
1843
|
+
}, "Route Exit:");
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
__name(logRouteExit, "logRouteExit");
|
|
1847
|
+
function checkRoles(req, res, roles, timeoutState) {
|
|
1848
|
+
if (roles.length && !req?.user?.roles?.some((role) => roles.includes(role))) {
|
|
1849
|
+
const errorResponse = response.createFinalErrorResponse(req, httpStatusCodes.HttpStatusCodes.FORBIDDEN, "Forbidden Insufficient Roles");
|
|
1850
|
+
res.status(httpStatusCodes.HttpStatusCodes.FORBIDDEN).json(errorResponse);
|
|
1851
|
+
clearTimeout(timeoutState.timeoutId);
|
|
1852
|
+
return false;
|
|
1853
|
+
}
|
|
1854
|
+
return true;
|
|
1855
|
+
}
|
|
1856
|
+
__name(checkRoles, "checkRoles");
|
|
1857
|
+
function extractHandlerArgs(req, res, params) {
|
|
1858
|
+
const args = [];
|
|
1859
|
+
if (params.length) {
|
|
1860
|
+
params.forEach(({ index, type, key, options }) => {
|
|
1861
|
+
let rawValue;
|
|
1862
|
+
switch (type) {
|
|
1863
|
+
case "query":
|
|
1864
|
+
rawValue = key ? req.query[key] : req.query;
|
|
1865
|
+
args[index] = options ? applyParamOptions(rawValue, options, key) : rawValue;
|
|
1866
|
+
break;
|
|
1867
|
+
case "param":
|
|
1868
|
+
rawValue = key ? req.params[key] : req.params;
|
|
1869
|
+
args[index] = options ? applyParamOptions(rawValue, options, key) : rawValue;
|
|
1870
|
+
break;
|
|
1871
|
+
case "body":
|
|
1872
|
+
args[index] = key ? req.body?.[key] : req.body;
|
|
1873
|
+
break;
|
|
1874
|
+
case "req":
|
|
1875
|
+
args[index] = req;
|
|
1876
|
+
break;
|
|
1877
|
+
case "res":
|
|
1878
|
+
args[index] = res;
|
|
1879
|
+
break;
|
|
1880
|
+
case "logger":
|
|
1881
|
+
args[index] = logger.getLogger();
|
|
1882
|
+
break;
|
|
1883
|
+
case "reqHeader":
|
|
1884
|
+
args[index] = key ? req.headers[key.toLowerCase()] : req.headers;
|
|
1885
|
+
break;
|
|
1886
|
+
case "reqId":
|
|
1887
|
+
args[index] = req.headers["x-request-id"] || req?.id || void 0;
|
|
1888
|
+
break;
|
|
1889
|
+
case "cookie":
|
|
1890
|
+
args[index] = key ? req.cookies?.[key] : req.cookies;
|
|
1891
|
+
break;
|
|
1892
|
+
}
|
|
1893
|
+
});
|
|
1894
|
+
}
|
|
1895
|
+
return args;
|
|
1896
|
+
}
|
|
1897
|
+
__name(extractHandlerArgs, "extractHandlerArgs");
|
|
1898
|
+
function handleDynamicRedirect(res, redirect, result) {
|
|
1899
|
+
if (redirect && result && typeof result === "object" && "url" in result) {
|
|
1900
|
+
const redirectUrl = result.url;
|
|
1901
|
+
const redirectStatus = result.statusCode || redirect.statusCode;
|
|
1902
|
+
res.redirect(redirectStatus, redirectUrl);
|
|
1903
|
+
return true;
|
|
1904
|
+
}
|
|
1905
|
+
return false;
|
|
1906
|
+
}
|
|
1907
|
+
__name(handleDynamicRedirect, "handleDynamicRedirect");
|
|
1908
|
+
function sendResponse(res, result, httpCode, headers) {
|
|
1909
|
+
if (!res.headersSent && result !== void 0) {
|
|
1910
|
+
if (httpCode) res.status(httpCode);
|
|
1911
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
1912
|
+
const normalized = normalizeHeaderValue(v);
|
|
1913
|
+
if (normalized !== void 0) {
|
|
1914
|
+
res.set(k, normalized);
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1917
|
+
res.json(result);
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
__name(sendResponse, "sendResponse");
|
|
1835
1921
|
function applyParamOptions(rawValue, options, key) {
|
|
1836
1922
|
if (!options) return rawValue;
|
|
1837
1923
|
let value = rawValue;
|
|
@@ -1846,98 +1932,11 @@ function applyParamOptions(rawValue, options, key) {
|
|
|
1846
1932
|
}
|
|
1847
1933
|
if (value !== void 0 && value !== null) {
|
|
1848
1934
|
if (options.dataType === "array") {
|
|
1849
|
-
|
|
1850
|
-
if (typeof value === "string") {
|
|
1851
|
-
value = value.split(delimiter).map((v) => v.trim());
|
|
1852
|
-
} else if (!Array.isArray(value)) {
|
|
1853
|
-
value = [
|
|
1854
|
-
value
|
|
1855
|
-
];
|
|
1856
|
-
}
|
|
1857
|
-
if (options.type === "string" && options.pattern) {
|
|
1858
|
-
const hasInvalidPattern = value.some((v) => !options.pattern.test(v));
|
|
1859
|
-
if (hasInvalidPattern) {
|
|
1860
|
-
throw new exception.BadRequestException(`Parameter '${key}' array contains values that do not match pattern: ${options.patternName || options.pattern}`);
|
|
1861
|
-
}
|
|
1862
|
-
}
|
|
1863
|
-
if (options.type === "number" && options.pattern) {
|
|
1864
|
-
const hasInvalidPattern = value.some((v) => !options.pattern.test(String(v)));
|
|
1865
|
-
if (hasInvalidPattern) {
|
|
1866
|
-
throw new exception.BadRequestException(`Parameter '${key}' array contains values that do not match pattern: ${options.patternName || options.pattern}`);
|
|
1867
|
-
}
|
|
1868
|
-
}
|
|
1869
|
-
if (options.type === "number" && options.throwError !== false) {
|
|
1870
|
-
const hasInvalidNumber = value.some((v) => Number.isNaN(Number(v)));
|
|
1871
|
-
if (hasInvalidNumber) {
|
|
1872
|
-
throw new exception.BadRequestException(`Type error: expected number array${paramName}`);
|
|
1873
|
-
}
|
|
1874
|
-
if (options.min !== void 0) {
|
|
1875
|
-
const hasBelowMin = value.some((v) => Number(v) < options.min);
|
|
1876
|
-
if (hasBelowMin) {
|
|
1877
|
-
throw new exception.BadRequestException(`Validation error: number array values must be >= ${options.min}${paramName}`);
|
|
1878
|
-
}
|
|
1879
|
-
}
|
|
1880
|
-
if (options.max !== void 0) {
|
|
1881
|
-
const hasAboveMax = value.some((v) => Number(v) > options.max);
|
|
1882
|
-
if (hasAboveMax) {
|
|
1883
|
-
throw new exception.BadRequestException(`Validation error: number array values must be <= ${options.max}${paramName}`);
|
|
1884
|
-
}
|
|
1885
|
-
}
|
|
1886
|
-
}
|
|
1887
|
-
if (options.type === "boolean" && options.throwError !== false) {
|
|
1888
|
-
const hasInvalidBoolean = value.some((v) => !isValidBooleanInput(v));
|
|
1889
|
-
if (hasInvalidBoolean) {
|
|
1890
|
-
throw new exception.BadRequestException(`Type error: expected boolean array${paramName}`);
|
|
1891
|
-
}
|
|
1892
|
-
}
|
|
1893
|
-
if (options.type) {
|
|
1894
|
-
value = value.map((v) => convertType(v, options.type || "string"));
|
|
1895
|
-
}
|
|
1935
|
+
value = handleArrayParam(value, options, key);
|
|
1896
1936
|
} else if (options.dataType === "object") {
|
|
1897
|
-
|
|
1898
|
-
try {
|
|
1899
|
-
value = JSON.parse(value);
|
|
1900
|
-
} catch (_e) {
|
|
1901
|
-
if (options.throwError !== false) {
|
|
1902
|
-
throw new exception.BadRequestException(`Invalid JSON object${paramName}`);
|
|
1903
|
-
}
|
|
1904
|
-
}
|
|
1905
|
-
}
|
|
1937
|
+
value = handleObjectParam(value, options, key);
|
|
1906
1938
|
} else if (options.type) {
|
|
1907
|
-
|
|
1908
|
-
value = convertType(value, options.type);
|
|
1909
|
-
if (options.throwError === false) {
|
|
1910
|
-
if (options.type === "number" && typeof value === "number") {
|
|
1911
|
-
if (options.min !== void 0 && value < options.min) {
|
|
1912
|
-
value = void 0;
|
|
1913
|
-
}
|
|
1914
|
-
if (options.max !== void 0 && value > options.max) {
|
|
1915
|
-
value = void 0;
|
|
1916
|
-
}
|
|
1917
|
-
}
|
|
1918
|
-
}
|
|
1919
|
-
const paramNotMatchMsg = `Parameter '${key}' does not match pattern: ${options.patternName || options.pattern}`;
|
|
1920
|
-
if (options.type === "string" && options.pattern && typeof value === "string" && !options.pattern.test(value)) {
|
|
1921
|
-
throw new exception.BadRequestException(paramNotMatchMsg);
|
|
1922
|
-
} else if (options.type === "number" && options.pattern && typeof value === "number" && !options.pattern.test(String(value))) {
|
|
1923
|
-
throw new exception.BadRequestException(paramNotMatchMsg);
|
|
1924
|
-
}
|
|
1925
|
-
if (options.throwError !== false) {
|
|
1926
|
-
if (options.type === "number" && typeof value === "number" && Number.isNaN(value)) {
|
|
1927
|
-
throw new exception.BadRequestException(`Type error: expected number${paramName}`);
|
|
1928
|
-
}
|
|
1929
|
-
if (options.type === "number" && typeof value === "number") {
|
|
1930
|
-
if (options.min !== void 0 && value < options.min) {
|
|
1931
|
-
throw new exception.BadRequestException(`Validation error: number must be >= ${options.min}${paramName}`);
|
|
1932
|
-
}
|
|
1933
|
-
if (options.max !== void 0 && value > options.max) {
|
|
1934
|
-
throw new exception.BadRequestException(`Validation error: number must be <= ${options.max}${paramName}`);
|
|
1935
|
-
}
|
|
1936
|
-
}
|
|
1937
|
-
if (options.type === "boolean" && !isValidBooleanInput(originalValue)) {
|
|
1938
|
-
throw new exception.BadRequestException(`Type error: expected boolean${paramName}`);
|
|
1939
|
-
}
|
|
1940
|
-
}
|
|
1939
|
+
value = handleSingleParam(value, options, key);
|
|
1941
1940
|
}
|
|
1942
1941
|
}
|
|
1943
1942
|
if (options.transform && typeof options.transform === "function") {
|
|
@@ -1954,6 +1953,128 @@ function applyParamOptions(rawValue, options, key) {
|
|
|
1954
1953
|
return value;
|
|
1955
1954
|
}
|
|
1956
1955
|
__name(applyParamOptions, "applyParamOptions");
|
|
1956
|
+
function handleArrayParam(value, options, key) {
|
|
1957
|
+
const delimiter = options.delimiter || ",";
|
|
1958
|
+
if (typeof value === "string") {
|
|
1959
|
+
value = value.split(delimiter).map((v) => v.trim());
|
|
1960
|
+
} else if (!Array.isArray(value)) {
|
|
1961
|
+
value = [
|
|
1962
|
+
value
|
|
1963
|
+
];
|
|
1964
|
+
}
|
|
1965
|
+
validateArrayPattern(value, options, key);
|
|
1966
|
+
validateArrayElements(value, options, key);
|
|
1967
|
+
if (options.type) {
|
|
1968
|
+
value = value.map((v) => convertType(v, options.type || "string"));
|
|
1969
|
+
}
|
|
1970
|
+
return value;
|
|
1971
|
+
}
|
|
1972
|
+
__name(handleArrayParam, "handleArrayParam");
|
|
1973
|
+
function validateArrayPattern(value, options, key) {
|
|
1974
|
+
if (options.type === "string" && options.pattern) {
|
|
1975
|
+
const hasInvalidPattern = value.some((v) => !options.pattern.test(v));
|
|
1976
|
+
if (hasInvalidPattern) {
|
|
1977
|
+
throw new exception.BadRequestException(`Parameter '${key}' array contains values that do not match pattern: ${options.patternName || options.pattern}`);
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
if (options.type === "number" && options.pattern) {
|
|
1981
|
+
const hasInvalidPattern = value.some((v) => !options.pattern.test(String(v)));
|
|
1982
|
+
if (hasInvalidPattern) {
|
|
1983
|
+
throw new exception.BadRequestException(`Parameter '${key}' array contains values that do not match pattern: ${options.patternName || options.pattern}`);
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
__name(validateArrayPattern, "validateArrayPattern");
|
|
1988
|
+
function validateArrayElements(value, options, key) {
|
|
1989
|
+
const paramName = key ? `: ${key}` : "";
|
|
1990
|
+
if (options.type === "number" && options.throwError !== false) {
|
|
1991
|
+
const hasInvalidNumber = value.some((v) => Number.isNaN(Number(v)));
|
|
1992
|
+
if (hasInvalidNumber) {
|
|
1993
|
+
throw new exception.BadRequestException(`Type error: expected number array${paramName}`);
|
|
1994
|
+
}
|
|
1995
|
+
if (options.min !== void 0) {
|
|
1996
|
+
const hasBelowMin = value.some((v) => Number(v) < options.min);
|
|
1997
|
+
if (hasBelowMin) {
|
|
1998
|
+
throw new exception.BadRequestException(`Validation error: number array values must be >= ${options.min}${paramName}`);
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
if (options.max !== void 0) {
|
|
2002
|
+
const hasAboveMax = value.some((v) => Number(v) > options.max);
|
|
2003
|
+
if (hasAboveMax) {
|
|
2004
|
+
throw new exception.BadRequestException(`Validation error: number array values must be <= ${options.max}${paramName}`);
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
if (options.type === "boolean" && options.throwError !== false) {
|
|
2009
|
+
const hasInvalidBoolean = value.some((v) => !isValidBooleanInput(v));
|
|
2010
|
+
if (hasInvalidBoolean) {
|
|
2011
|
+
throw new exception.BadRequestException(`Type error: expected boolean array${paramName}`);
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
__name(validateArrayElements, "validateArrayElements");
|
|
2016
|
+
function handleObjectParam(value, options, key) {
|
|
2017
|
+
const paramName = key ? `: ${key}` : "";
|
|
2018
|
+
if (typeof value === "string") {
|
|
2019
|
+
try {
|
|
2020
|
+
value = JSON.parse(value);
|
|
2021
|
+
} catch (_e) {
|
|
2022
|
+
if (options.throwError !== false) {
|
|
2023
|
+
throw new exception.BadRequestException(`Invalid JSON object${paramName}`);
|
|
2024
|
+
}
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
return value;
|
|
2028
|
+
}
|
|
2029
|
+
__name(handleObjectParam, "handleObjectParam");
|
|
2030
|
+
function handleSingleParam(value, options, key) {
|
|
2031
|
+
const paramName = key ? `: ${key}` : "";
|
|
2032
|
+
const originalValue = value;
|
|
2033
|
+
const targetType = options.type ?? "string";
|
|
2034
|
+
value = convertType(value, targetType);
|
|
2035
|
+
if (options.throwError === false) {
|
|
2036
|
+
if (options.type === "number" && typeof value === "number") {
|
|
2037
|
+
if (options.min !== void 0 && value < options.min) {
|
|
2038
|
+
value = void 0;
|
|
2039
|
+
}
|
|
2040
|
+
if (options.max !== void 0 && value > options.max) {
|
|
2041
|
+
value = void 0;
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
}
|
|
2045
|
+
validateSinglePattern(value, options, key);
|
|
2046
|
+
validateSingleType(value, originalValue, options, paramName);
|
|
2047
|
+
return value;
|
|
2048
|
+
}
|
|
2049
|
+
__name(handleSingleParam, "handleSingleParam");
|
|
2050
|
+
function validateSinglePattern(value, options, key) {
|
|
2051
|
+
const paramNotMatchMsg = `Parameter '${key}' does not match pattern: ${options.patternName || options.pattern}`;
|
|
2052
|
+
if (options.type === "string" && options.pattern && typeof value === "string" && !options.pattern.test(value)) {
|
|
2053
|
+
throw new exception.BadRequestException(paramNotMatchMsg);
|
|
2054
|
+
} else if (options.type === "number" && options.pattern && typeof value === "number" && !options.pattern.test(String(value))) {
|
|
2055
|
+
throw new exception.BadRequestException(paramNotMatchMsg);
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
__name(validateSinglePattern, "validateSinglePattern");
|
|
2059
|
+
function validateSingleType(value, originalValue, options, paramName) {
|
|
2060
|
+
if (options.throwError !== false) {
|
|
2061
|
+
if (options.type === "number" && typeof value === "number" && Number.isNaN(value)) {
|
|
2062
|
+
throw new exception.BadRequestException(`Type error: expected number${paramName}`);
|
|
2063
|
+
}
|
|
2064
|
+
if (options.type === "number" && typeof value === "number") {
|
|
2065
|
+
if (options.min !== void 0 && value < options.min) {
|
|
2066
|
+
throw new exception.BadRequestException(`Validation error: number must be >= ${options.min}${paramName}`);
|
|
2067
|
+
}
|
|
2068
|
+
if (options.max !== void 0 && value > options.max) {
|
|
2069
|
+
throw new exception.BadRequestException(`Validation error: number must be <= ${options.max}${paramName}`);
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
if (options.type === "boolean" && !isValidBooleanInput(originalValue)) {
|
|
2073
|
+
throw new exception.BadRequestException(`Type error: expected boolean${paramName}`);
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
__name(validateSingleType, "validateSingleType");
|
|
1957
2078
|
function isValidBooleanInput(value) {
|
|
1958
2079
|
if (typeof value === "boolean") return true;
|
|
1959
2080
|
if (typeof value === "number") return value === 0 || value === 1;
|