@catbee/utils 1.0.5 → 1.1.0

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/build/index.cjs CHANGED
@@ -26,29 +26,6 @@ var fsp__default = /*#__PURE__*/_interopDefault(fsp);
26
26
  var express__default = /*#__PURE__*/_interopDefault(express);
27
27
  var https__default = /*#__PURE__*/_interopDefault(https);
28
28
 
29
- /*
30
- * The MIT License
31
- *
32
- * Copyright (c) 2025 Catbee Technologies. https://catbee.npm.hprasath.com/license
33
- *
34
- * Permission is hereby granted, free of charge, to any person obtaining a copy
35
- * of this software and associated documentation files (the "Software"), to deal
36
- * in the Software without restriction, including without limitation the rights
37
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
38
- * copies of the Software, and to permit persons to whom the Software is
39
- * furnished to do so, subject to the following conditions:
40
- *
41
- * The above copyright notice and this permission notice shall be included in all
42
- * copies or substantial portions of the Software.
43
- *
44
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
45
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
46
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
47
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
48
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
49
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
50
- * SOFTWARE.
51
- */
52
29
  var __defProp = Object.defineProperty;
53
30
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
54
31
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
@@ -972,16 +949,30 @@ function setValueByPath(obj, path3, value) {
972
949
  if (!obj || typeof obj !== "object") return obj;
973
950
  const parts = path3.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
974
951
  if (parts.length === 0) return obj;
952
+ const forbiddenKeys = /* @__PURE__ */ new Set([
953
+ "__proto__",
954
+ "prototype",
955
+ "constructor"
956
+ ]);
975
957
  let current = obj;
976
958
  for (let i = 0; i < parts.length - 1; i++) {
977
959
  const key = parts[i];
978
- const isArrayIndex = !isNaN(Number(parts[i + 1]));
960
+ if (forbiddenKeys.has(key)) {
961
+ return obj;
962
+ }
963
+ const nextKey = parts[i + 1];
964
+ const isArrayIndex = !isNaN(Number(nextKey));
979
965
  if (current[key] === void 0) {
980
- current[key] = isArrayIndex ? [] : {};
966
+ current[key] = isArrayIndex ? [] : /* @__PURE__ */ Object.create(null);
967
+ } else if (typeof current[key] !== "object" || current[key] === null) {
968
+ current[key] = isArrayIndex ? [] : /* @__PURE__ */ Object.create(null);
981
969
  }
982
970
  current = current[key];
983
971
  }
984
972
  const finalKey = parts[parts.length - 1];
973
+ if (forbiddenKeys.has(finalKey)) {
974
+ return obj;
975
+ }
985
976
  current[finalKey] = value;
986
977
  return obj;
987
978
  }
@@ -1057,48 +1048,21 @@ function getAllPaths(obj, parentPath = "") {
1057
1048
  __name(getAllPaths, "getAllPaths");
1058
1049
 
1059
1050
  // src/config.ts
1060
- var config = {
1051
+ var defaultCatbeeConfig = {
1061
1052
  logger: {
1062
- /**
1063
- * Logging level (e.g., 'info', 'debug', 'warn', 'error').
1064
- */
1065
- level: Env.get("LOGGER_LEVEL", "info"),
1066
- /**
1067
- * Name of the logger instance (defaults to npm package name).
1068
- */
1053
+ level: Env.get("LOGGER_LEVEL", Env.isDev() ? "debug" : "info"),
1069
1054
  name: Env.get("LOGGER_NAME", Env.get("npm_package_name", "@catbee/utils")),
1070
- /**
1071
- * Enables pretty-print logging in development.
1072
- * Has no effect in production.
1073
- */
1074
1055
  pretty: Env.getBoolean("LOGGER_PRETTY", true),
1075
- /**
1076
- * Enables colorized output for pretty-print (default: true)
1077
- */
1078
1056
  colorize: Env.getBoolean("LOGGER_PRETTY_COLORIZE", true),
1079
- /**
1080
- * Single line output for pretty-print (default: false)
1081
- */
1082
- singleLine: Env.getBoolean("LOGGER_PRETTY_SINGLE_LINE", false)
1057
+ singleLine: Env.getBoolean("LOGGER_PRETTY_SINGLE_LINE", false),
1058
+ dir: Env.getPath("LOGGER_DIR", "", {
1059
+ mustExist: false
1060
+ })
1083
1061
  },
1084
1062
  cache: {
1085
- /**
1086
- * Default TTL (time to live) for cache entries in seconds
1087
- */
1088
1063
  defaultTtl: Env.getNumber("CACHE_DEFAULT_TTL_SECONDS", 3600) * 1e3
1089
1064
  },
1090
1065
  server: {
1091
- /**
1092
- * Skip healthz endpoint even if health checks are configured
1093
- * Default: false
1094
- * Set to true to return 200 OK for /healthz without checks
1095
- * Useful in environments where a simple liveness probe is needed
1096
- * without performing actual health checks
1097
- * Example: Kubernetes liveness probe
1098
- * Note: This does not disable the health check functionality itself
1099
- * Health checks can still be performed programmatically
1100
- * or via other endpoints if needed
1101
- */
1102
1066
  skipHealthz: Env.getBoolean("SERVER_SKIP_HEALTHZ", false)
1103
1067
  }
1104
1068
  };
@@ -1176,11 +1140,11 @@ var defaultServerConfig = {
1176
1140
  }
1177
1141
  };
1178
1142
  function setConfig(value) {
1179
- deepObjMerge(config, value);
1143
+ deepObjMerge(defaultCatbeeConfig, value);
1180
1144
  }
1181
1145
  __name(setConfig, "setConfig");
1182
1146
  function getConfig() {
1183
- return config;
1147
+ return defaultCatbeeConfig;
1184
1148
  }
1185
1149
  __name(getConfig, "getConfig");
1186
1150
 
@@ -1617,27 +1581,66 @@ var defaultSensitiveFields = [
1617
1581
  "password",
1618
1582
  "secret",
1619
1583
  "token",
1620
- "apiKey",
1621
1584
  "api_key",
1622
1585
  "auth",
1586
+ "private_key",
1587
+ "public_key",
1623
1588
  "jwt",
1624
1589
  "access_token",
1625
- "client_secret",
1590
+ "refresh_token",
1626
1591
  "session_token",
1627
- "refresh_token"
1592
+ "csrf_token",
1593
+ "authorization",
1594
+ "bearer",
1595
+ "x-api-key",
1596
+ "x-auth-token",
1597
+ "x-access-token",
1598
+ "client_secret",
1599
+ "passphrase",
1600
+ "otp",
1601
+ "api_secret",
1602
+ "token_secret"
1628
1603
  ];
1629
- var globalRedactCensor = /* @__PURE__ */ __name((value, path3, sensitiveFields = defaultSensitiveFields) => {
1604
+ var defaultRedactPaths = [
1605
+ "req.authorization",
1606
+ "req.headers.authorization",
1607
+ "req.headers.cookie",
1608
+ 'req.headers["x-api-key"]',
1609
+ 'req.headers["x-auth-token"]',
1610
+ 'req.headers["x-access-token"]',
1611
+ "req.body.password",
1612
+ "req.body.token",
1613
+ "req.body.secret",
1614
+ "req.query.token",
1615
+ "req.query.api_key",
1616
+ "req.query.apiKey",
1617
+ "res.authorization",
1618
+ "res.headers.authorization",
1619
+ 'res.headers["set-cookie"]',
1620
+ "headers.authorization",
1621
+ "headers.cookies",
1622
+ 'headers["set-cookie"]',
1623
+ 'headers["x-api-key"]',
1624
+ 'headers["x-auth-token"]',
1625
+ 'headers["x-access-token"]',
1626
+ "url",
1627
+ "uri",
1628
+ "href",
1629
+ "redirect_uri",
1630
+ "redirectUri"
1631
+ ];
1632
+ var globalRedactCensor = /* @__PURE__ */ __name((value, path3, sensitiveFields = getExpandedSensitiveFields()) => {
1630
1633
  if (typeof value !== "string") return "***";
1631
- const lowerPath = path3.map((p) => p.toLowerCase());
1634
+ const lowerPath = path3.filter((p) => typeof p === "string").map((p) => p.toLowerCase());
1632
1635
  const lowerSensitiveFields = sensitiveFields.map((f) => f.toLowerCase());
1633
1636
  if (lowerPath.some((p) => lowerSensitiveFields.includes(p))) return "***";
1634
- if (lowerPath[0] === "url") {
1637
+ if (lowerPath[0] === "url" || lowerPath[0] === "uri" || lowerPath[0] === "href" || lowerPath.includes("redirect_uri") || lowerPath.includes("redirecturi")) {
1635
1638
  return value.replace(new RegExp(`([?&](${lowerSensitiveFields.join("|")})=)[^&#]*`, "gi"), "$1***");
1636
1639
  }
1637
1640
  if (lowerPath.some((p) => p.includes("authorization") || p.includes("auth"))) {
1638
1641
  return value.replace(/^(\S+)\s+.+$/, "$1 ***") || "***";
1639
1642
  }
1640
- if (lowerPath.some((pathPart) => lowerSensitiveFields.some((field) => pathPart.includes(field)))) {
1643
+ if (lowerPath.some((p) => lowerSensitiveFields.some((f) => p.includes(f)))) {
1641
1644
  return "***";
1642
1645
  }
1643
1646
  if (path3.length > 0 && ![
@@ -1663,34 +1666,44 @@ function redact(value, path3, sensitiveFields) {
1663
1666
  __name(redact, "redact");
1664
1667
  function addRedactFields(fields) {
1665
1668
  const prev = globalRedactCensor;
1666
- globalRedactCensor = /* @__PURE__ */ __name((value, path3, sensitiveFields = defaultSensitiveFields) => prev(value, path3, [
1669
+ globalRedactCensor = /* @__PURE__ */ __name((value, path3, sensitiveFields = getExpandedSensitiveFields()) => prev(value, path3, [
1667
1670
  ...sensitiveFields || [],
1668
1671
  ...fields
1669
1672
  ]), "globalRedactCensor");
1673
+ cachedExpandedFields = null;
1670
1674
  }
1671
1675
  __name(addRedactFields, "addRedactFields");
1676
+ var cachedExpandedFields = null;
1677
+ function getExpandedSensitiveFields() {
1678
+ if (!cachedExpandedFields) {
1679
+ cachedExpandedFields = expandSensitiveFields(defaultSensitiveFields);
1680
+ }
1681
+ return cachedExpandedFields;
1682
+ }
1683
+ __name(getExpandedSensitiveFields, "getExpandedSensitiveFields");
1672
1684
  function setSensitiveFields(fields) {
1673
1685
  defaultSensitiveFields.splice(0, defaultSensitiveFields.length, ...fields);
1686
+ cachedExpandedFields = null;
1674
1687
  }
1675
1688
  __name(setSensitiveFields, "setSensitiveFields");
1676
1689
  function addSensitiveFields(fields) {
1677
1690
  defaultSensitiveFields.push(...fields);
1691
+ cachedExpandedFields = null;
1678
1692
  }
1679
1693
  __name(addSensitiveFields, "addSensitiveFields");
1680
1694
  var _globalThis = typeof globalThis === "object" ? globalThis : global;
1681
1695
  var _global = _globalThis;
1682
1696
  function setupLogger(isGlobal = true) {
1697
+ const sensitiveFields = getExpandedSensitiveFields();
1698
+ const paths = /* @__PURE__ */ new Set([
1699
+ ...defaultRedactPaths,
1700
+ ...sensitiveFields.flatMap((field) => generateDeepPaths(field, 2))
1701
+ ]);
1683
1702
  const logParams = {
1684
- name: config.logger.name || "@catbee/utils",
1685
- level: config.logger.level || "info",
1703
+ name: defaultCatbeeConfig.logger?.name || "@catbee/utils",
1704
+ level: defaultCatbeeConfig.logger?.level || "info",
1686
1705
  redact: {
1687
- paths: [
1688
- "req.authorization",
1689
- "res.authorization",
1690
- "url",
1691
- "headers.authorization",
1692
- "headers.cookies"
1693
- ],
1706
+ paths: Array.from(paths),
1694
1707
  censor: /* @__PURE__ */ __name((value, path3) => redact(value, path3), "censor")
1695
1708
  },
1696
1709
  serializers: {
@@ -1703,64 +1716,147 @@ function setupLogger(isGlobal = true) {
1703
1716
  },
1704
1717
  timestamp: pino.stdTimeFunctions.isoTime
1705
1718
  };
1706
- const logger2 = config.logger.pretty ? pino__default.default(logParams, pino__default.default.transport({
1707
- target: "pino-pretty",
1708
- options: {
1709
- colorize: config.logger.colorize,
1710
- translateTime: "SYS:standard",
1711
- ignore: "pid,hostname",
1712
- singleLine: config.logger.singleLine,
1713
- levelFirst: true
1714
- }
1715
- })) : pino__default.default(logParams);
1719
+ let logger;
1720
+ const logDir = defaultCatbeeConfig.logger?.dir?.trim();
1721
+ const hasFileLogging = Boolean(logDir);
1722
+ if (hasFileLogging && defaultCatbeeConfig.logger?.pretty) {
1723
+ logger = pino__default.default(logParams, pino__default.default.transport({
1724
+ targets: [
1725
+ {
1726
+ target: "pino-pretty",
1727
+ level: defaultCatbeeConfig.logger?.level ?? "info",
1728
+ options: {
1729
+ colorize: defaultCatbeeConfig.logger?.colorize,
1730
+ translateTime: "SYS:standard",
1731
+ ignore: "pid,hostname",
1732
+ singleLine: defaultCatbeeConfig.logger?.singleLine,
1733
+ levelFirst: true
1734
+ }
1735
+ },
1736
+ {
1737
+ target: "pino/file",
1738
+ level: defaultCatbeeConfig.logger?.level ?? "info",
1739
+ options: {
1740
+ destination: `${logDir}/app.log`,
1741
+ mkdir: true
1742
+ }
1743
+ }
1744
+ ]
1745
+ }));
1746
+ } else if (hasFileLogging) {
1747
+ logger = pino__default.default(logParams, pino__default.default.transport({
1748
+ target: "pino/file",
1749
+ options: {
1750
+ destination: `${logDir}/app.log`,
1751
+ mkdir: true
1752
+ }
1753
+ }));
1754
+ } else if (defaultCatbeeConfig.logger?.pretty) {
1755
+ logger = pino__default.default(logParams, pino__default.default.transport({
1756
+ target: "pino-pretty",
1757
+ options: {
1758
+ colorize: defaultCatbeeConfig.logger?.colorize,
1759
+ translateTime: "SYS:standard",
1760
+ ignore: "pid,hostname",
1761
+ singleLine: defaultCatbeeConfig.logger?.singleLine,
1762
+ levelFirst: true
1763
+ }
1764
+ }));
1765
+ } else {
1766
+ logger = pino__default.default(logParams);
1767
+ }
1716
1768
  if (isGlobal) {
1717
- _global[GLOBAL_LOGGER_KEY] = logger2;
1718
- _global[GLOBAL_LOGGER_KEY]?.debug("Logger initialized");
1769
+ _global[GLOBAL_LOGGER_KEY] = logger;
1770
+ _global[GLOBAL_LOGGER_KEY]?.debug({
1771
+ logDir: logDir || "none"
1772
+ }, "Logger initialized");
1719
1773
  }
1720
- return logger2;
1774
+ return logger;
1721
1775
  }
1722
1776
  __name(setupLogger, "setupLogger");
1723
1777
  function getLogger(newInstance = false) {
1724
1778
  if (newInstance) {
1725
1779
  return setupLogger(false);
1726
1780
  }
1727
- const logger2 = ContextStore.get(StoreKeys.LOGGER);
1728
- if (logger2) return logger2;
1781
+ const logger = ContextStore.get(StoreKeys.LOGGER);
1782
+ if (logger) return logger;
1729
1783
  if (!_global[GLOBAL_LOGGER_KEY]) {
1730
1784
  setupLogger();
1731
1785
  }
1732
1786
  return _global[GLOBAL_LOGGER_KEY];
1733
1787
  }
1734
1788
  __name(getLogger, "getLogger");
1735
- var logger = getLogger(false);
1736
1789
  function createChildLogger(bindings, parentLogger) {
1737
- const logger2 = parentLogger || getLogger();
1738
- return logger2.child(bindings);
1790
+ const logger = parentLogger || getLogger();
1791
+ return logger.child(bindings);
1739
1792
  }
1740
1793
  __name(createChildLogger, "createChildLogger");
1741
1794
  function createRequestLogger(requestId2, additionalContext = {}) {
1742
- const logger2 = createChildLogger({
1795
+ const logger = createChildLogger({
1743
1796
  requestId: requestId2,
1744
1797
  ...additionalContext
1745
1798
  });
1746
1799
  try {
1747
- ContextStore.set(StoreKeys.LOGGER, logger2);
1800
+ ContextStore.set(StoreKeys.LOGGER, logger);
1748
1801
  } catch {
1749
- logger2.debug("Failed to store logger in context - AsyncLocalStorage not initialized");
1802
+ logger.debug("Failed to store logger in context - AsyncLocalStorage not initialized");
1750
1803
  }
1751
- return logger2;
1804
+ return logger;
1752
1805
  }
1753
1806
  __name(createRequestLogger, "createRequestLogger");
1754
1807
  function logError(error, message, context) {
1755
- const logger2 = getLogger();
1808
+ const logger = getLogger();
1756
1809
  const errObj = error instanceof Error ? error : new Error(String(error));
1757
1810
  const logContext = {
1758
1811
  ...context,
1759
1812
  error: errObj
1760
1813
  };
1761
- logger2.error(logContext, message || errObj.message);
1814
+ logger.error(logContext, message || errObj.message);
1762
1815
  }
1763
1816
  __name(logError, "logError");
1817
+ function expandSensitiveFields(fields) {
1818
+ const set = /* @__PURE__ */ new Set();
1819
+ for (const field of fields) {
1820
+ for (const v of expandSensitiveField(field)) {
1821
+ set.add(v);
1822
+ }
1823
+ }
1824
+ return Array.from(set);
1825
+ }
1826
+ __name(expandSensitiveFields, "expandSensitiveFields");
1827
+ function expandSensitiveField(field) {
1828
+ const parts = field.split(/[_-]/g).filter(Boolean);
1829
+ const camel = parts[0].toLowerCase() + parts.slice(1).map((p) => p.charAt(0).toUpperCase() + p.slice(1).toLowerCase()).join("");
1830
+ const mergedLower = parts.join("").toLowerCase();
1831
+ const mergedUpper = parts.join("").toUpperCase();
1832
+ const kebab = parts.map((p) => p.toLowerCase()).join("-");
1833
+ const snakeLower = parts.map((p) => p.toLowerCase()).join("_");
1834
+ const snakeUpper = parts.map((p) => p.toUpperCase()).join("_");
1835
+ return Array.from(/* @__PURE__ */ new Set([
1836
+ field,
1837
+ camel,
1838
+ mergedLower,
1839
+ mergedUpper,
1840
+ kebab,
1841
+ snakeLower,
1842
+ snakeUpper
1843
+ ]));
1844
+ }
1845
+ __name(expandSensitiveField, "expandSensitiveField");
1846
+ function generateDeepPaths(field, depth) {
1847
+ const set = /* @__PURE__ */ new Set([
1848
+ field
1849
+ ]);
1850
+ let prefix = "";
1851
+ for (let i = 1; i <= depth; i++) {
1852
+ prefix = prefix ? `${prefix}.*` : "*";
1853
+ set.add(`${prefix}.${field}`);
1854
+ }
1855
+ return [
1856
+ ...set
1857
+ ];
1858
+ }
1859
+ __name(generateDeepPaths, "generateDeepPaths");
1764
1860
 
1765
1861
  // src/utils/async.utils.ts
1766
1862
  function sleep(ms) {
@@ -2140,7 +2236,7 @@ var TTLCache = class {
2140
2236
  * @param options - Configuration options for the cache
2141
2237
  */
2142
2238
  constructor(options = {}) {
2143
- this.ttlMs = options.ttlMs ?? config.cache.defaultTtl;
2239
+ this.ttlMs = options.ttlMs ?? defaultCatbeeConfig.cache.defaultTtl;
2144
2240
  this.maxSize = options.maxSize;
2145
2241
  const autoCleanupMs = options.autoCleanupMs;
2146
2242
  if (autoCleanupMs && autoCleanupMs > 0) {
@@ -3751,7 +3847,7 @@ function Timeout(ms) {
3751
3847
  __name(Timeout, "Timeout");
3752
3848
  function Log(options) {
3753
3849
  return (target, propertyKey, _descriptor) => {
3754
- const config2 = {
3850
+ const config = {
3755
3851
  logEntry: true,
3756
3852
  logExit: true,
3757
3853
  logBody: false,
@@ -3760,9 +3856,9 @@ function Log(options) {
3760
3856
  ...options
3761
3857
  };
3762
3858
  if (typeof propertyKey === "undefined") {
3763
- Reflect.defineMetadata(LOG_KEY, config2, target);
3859
+ Reflect.defineMetadata(LOG_KEY, config, target);
3764
3860
  } else {
3765
- Reflect.defineMetadata(LOG_KEY, config2, target, propertyKey);
3861
+ Reflect.defineMetadata(LOG_KEY, config, target, propertyKey);
3766
3862
  }
3767
3863
  };
3768
3864
  }
@@ -3872,7 +3968,7 @@ function registerControllers(router, controllers) {
3872
3968
  res.setHeader("Cache-Control", `public, max-age=${cache.ttlSeconds}`);
3873
3969
  }
3874
3970
  if (logConfig?.logEntry) {
3875
- const logger2 = getLogger();
3971
+ const logger = getLogger();
3876
3972
  const logData = {
3877
3973
  method: req.method,
3878
3974
  url: req.originalUrl || req.url,
@@ -3883,7 +3979,7 @@ function registerControllers(router, controllers) {
3883
3979
  logData.query = req.query;
3884
3980
  }
3885
3981
  if (logConfig.logBody) logData.body = req.body;
3886
- logger2.info({
3982
+ logger.info({
3887
3983
  entry: logData
3888
3984
  }, "Route Entry:");
3889
3985
  }
@@ -3961,7 +4057,7 @@ function registerControllers(router, controllers) {
3961
4057
  res.json(awaited);
3962
4058
  }
3963
4059
  if (logConfig?.logExit) {
3964
- const logger2 = getLogger();
4060
+ const logger = getLogger();
3965
4061
  const logData = {
3966
4062
  method: req.method,
3967
4063
  url: req.originalUrl || req.url,
@@ -3969,7 +4065,7 @@ function registerControllers(router, controllers) {
3969
4065
  duration: `${Date.now() - req.startTime}ms`
3970
4066
  };
3971
4067
  if (logConfig.logResponse) logData.response = awaited;
3972
- logger2.info({
4068
+ logger.info({
3973
4069
  exit: logData
3974
4070
  }, "Route Exit:");
3975
4071
  }
@@ -4840,36 +4936,36 @@ function timeSync(fn, options = {}) {
4840
4936
  label
4841
4937
  };
4842
4938
  if (log) {
4843
- const logger2 = getLogger();
4939
+ const logger = getLogger();
4844
4940
  const message = `${label} completed in ${durationMs.toFixed(2)}ms`;
4845
4941
  switch (logLevel) {
4846
4942
  case "trace":
4847
- logger2.trace({
4943
+ logger.trace({
4848
4944
  timing
4849
4945
  }, message);
4850
4946
  break;
4851
4947
  case "debug":
4852
- logger2.debug({
4948
+ logger.debug({
4853
4949
  timing
4854
4950
  }, message);
4855
4951
  break;
4856
4952
  case "info":
4857
- logger2.info({
4953
+ logger.info({
4858
4954
  timing
4859
4955
  }, message);
4860
4956
  break;
4861
4957
  case "warn":
4862
- logger2.warn({
4958
+ logger.warn({
4863
4959
  timing
4864
4960
  }, message);
4865
4961
  break;
4866
4962
  case "error":
4867
- logger2.error({
4963
+ logger.error({
4868
4964
  timing
4869
4965
  }, message);
4870
4966
  break;
4871
4967
  default:
4872
- logger2.debug({
4968
+ logger.debug({
4873
4969
  timing
4874
4970
  }, message);
4875
4971
  break;
@@ -4896,36 +4992,36 @@ async function timeAsync(fn, options = {}) {
4896
4992
  label
4897
4993
  };
4898
4994
  if (log) {
4899
- const logger2 = getLogger();
4995
+ const logger = getLogger();
4900
4996
  const message = `${label} completed in ${durationMs.toFixed(2)}ms`;
4901
4997
  switch (logLevel) {
4902
4998
  case "trace":
4903
- logger2.trace({
4999
+ logger.trace({
4904
5000
  timing
4905
5001
  }, message);
4906
5002
  break;
4907
5003
  case "debug":
4908
- logger2.debug({
5004
+ logger.debug({
4909
5005
  timing
4910
5006
  }, message);
4911
5007
  break;
4912
5008
  case "info":
4913
- logger2.info({
5009
+ logger.info({
4914
5010
  timing
4915
5011
  }, message);
4916
5012
  break;
4917
5013
  case "warn":
4918
- logger2.warn({
5014
+ logger.warn({
4919
5015
  timing
4920
5016
  }, message);
4921
5017
  break;
4922
5018
  case "error":
4923
- logger2.error({
5019
+ logger.error({
4924
5020
  timing
4925
5021
  }, message);
4926
5022
  break;
4927
5023
  default:
4928
- logger2.debug({
5024
+ logger.debug({
4929
5025
  timing
4930
5026
  }, message);
4931
5027
  break;
@@ -5596,10 +5692,26 @@ function isPort(value) {
5596
5692
  }
5597
5693
  __name(isPort, "isPort");
5598
5694
  function isEmail(str) {
5599
- if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str)) return false;
5600
- const [local, domain] = str.split("@");
5695
+ if (typeof str !== "string" || str.length > 254) return false;
5696
+ const atIndex = str.indexOf("@");
5697
+ if (atIndex === -1 || atIndex !== str.lastIndexOf("@")) return false;
5698
+ const local = str.slice(0, atIndex);
5699
+ const domain = str.slice(atIndex + 1);
5601
5700
  if (!local || !domain) return false;
5602
- if (local.includes("..") || domain.includes("..")) return false;
5701
+ const dotAtom = /^[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*$/;
5702
+ const quotedLocal = /^"([\s\x21\x23-\x5B\x5D-\x7E]|\\[\x20-\x7E])*"$/;
5703
+ if (!dotAtom.test(local) && !quotedLocal.test(local)) return false;
5704
+ if (domain.includes("..")) return false;
5705
+ const labels = domain.split(".");
5706
+ if (labels.some((l) => !l.length)) return false;
5707
+ if (labels.length < 2) return false;
5708
+ const labelRegex = /^[A-Za-z0-9-]{1,63}$/;
5709
+ for (const label of labels) {
5710
+ if (!labelRegex.test(label)) return false;
5711
+ if (label.startsWith("-") || label.endsWith("-")) return false;
5712
+ }
5713
+ const tld = labels[labels.length - 1];
5714
+ if (!/^[A-Za-z]{2,63}$/.test(tld)) return false;
5603
5715
  return true;
5604
5716
  }
5605
5717
  __name(isEmail, "isEmail");
@@ -6343,12 +6455,12 @@ var ServerConfigBuilder = class {
6343
6455
  * ```
6344
6456
  */
6345
6457
  build() {
6346
- const config2 = deepObjMerge({}, defaultServerConfig, this.config);
6347
- if (config2.openApi?.enable && !config2.openApi.filePath) {
6458
+ const config = deepObjMerge({}, defaultServerConfig, this.config);
6459
+ if (config.openApi?.enable && !config.openApi.filePath) {
6348
6460
  throw new Error("OpenAPI is enabled but no filePath is specified");
6349
6461
  }
6350
6462
  return Object.freeze({
6351
- ...config2,
6463
+ ...config,
6352
6464
  [BUILD_MARKER]: true
6353
6465
  });
6354
6466
  }
@@ -6419,11 +6531,11 @@ var ExpressServer = class _ExpressServer {
6419
6531
  * - Health checks
6420
6532
  * - Request tracing
6421
6533
  */
6422
- constructor(config2, hooks = {}) {
6423
- if (_ExpressServer.isBuiltServerConfig(config2)) {
6424
- this.config = config2;
6534
+ constructor(config, hooks = {}) {
6535
+ if (_ExpressServer.isBuiltServerConfig(config)) {
6536
+ this.config = config;
6425
6537
  } else {
6426
- this.config = deepObjMerge({}, defaultServerConfig, config2);
6538
+ this.config = deepObjMerge({}, defaultServerConfig, config);
6427
6539
  }
6428
6540
  if (!isPort(this.config.port)) {
6429
6541
  getLogger().error(`Port must be a valid number between 1 and 65535, got: ${this.config.port}`);
@@ -6508,8 +6620,8 @@ var ExpressServer = class _ExpressServer {
6508
6620
  prefix: `${safeAppName}_`
6509
6621
  });
6510
6622
  }
6511
- if (config2.healthCheck?.checks) {
6512
- this.healthChecks.push(...config2.healthCheck.checks);
6623
+ if (config.healthCheck?.checks) {
6624
+ this.healthChecks.push(...config.healthCheck.checks);
6513
6625
  }
6514
6626
  this.globalPrefix = this.normalizePath(this.config.globalPrefix ?? "", false);
6515
6627
  this.hooks = hooks;
@@ -6666,14 +6778,14 @@ var ExpressServer = class _ExpressServer {
6666
6778
  const skip = this.config.requestLogging?.ignorePaths?.some((path3) => req.path.startsWith(path3));
6667
6779
  if (skip) return next();
6668
6780
  }
6669
- const logger2 = getLogger();
6781
+ const logger = getLogger();
6670
6782
  const incomingRequestMetaData = {
6671
6783
  requestId: req.id,
6672
6784
  method: req.method,
6673
6785
  url: req.originalUrl || req.url,
6674
6786
  ip: req.ip
6675
6787
  };
6676
- logger2.info(incomingRequestMetaData, "Incoming Request");
6788
+ logger.info(incomingRequestMetaData, "Incoming Request");
6677
6789
  next();
6678
6790
  });
6679
6791
  }
@@ -6818,7 +6930,7 @@ var ExpressServer = class _ExpressServer {
6818
6930
  const healthCheckPath = this.normalizePath(this.config.healthCheck?.path || "/healthz", this.config.healthCheck?.withGlobalPrefix);
6819
6931
  this.app.get(healthCheckPath, async (_req, res) => {
6820
6932
  try {
6821
- if (!this.healthChecks.length || config.server.skipHealthz) {
6933
+ if (!this.healthChecks.length || defaultCatbeeConfig.server.skipHealthz) {
6822
6934
  return res.status(HttpStatusCodes.OK).json(new SuccessResponse("OK"));
6823
6935
  }
6824
6936
  const checkResults = await Promise.allSettled(this.healthChecks.map(async ({ name, check }) => {
@@ -7268,8 +7380,8 @@ var ExpressServer = class _ExpressServer {
7268
7380
  process.exit(1);
7269
7381
  }
7270
7382
  }
7271
- static isBuiltServerConfig(config2) {
7272
- if (config2[BUILD_MARKER]) {
7383
+ static isBuiltServerConfig(config) {
7384
+ if (config[BUILD_MARKER]) {
7273
7385
  return true;
7274
7386
  }
7275
7387
  return false;
@@ -7408,6 +7520,8 @@ exports.ensureEmptyDir = ensureEmptyDir;
7408
7520
  exports.ensureType = ensureType;
7409
7521
  exports.equalsIgnoreCase = equalsIgnoreCase;
7410
7522
  exports.errorHandler = errorHandler;
7523
+ exports.expandSensitiveField = expandSensitiveField;
7524
+ exports.expandSensitiveFields = expandSensitiveFields;
7411
7525
  exports.extractFilterParams = extractFilterParams;
7412
7526
  exports.extractPaginationParams = extractPaginationParams;
7413
7527
  exports.extractQueryParams = extractQueryParams;
@@ -7423,6 +7537,7 @@ exports.flattenObject = flattenObject;
7423
7537
  exports.formatDate = formatDate;
7424
7538
  exports.formatRelativeTime = formatRelativeTime;
7425
7539
  exports.generateApiKey = generateApiKey;
7540
+ exports.generateDeepPaths = generateDeepPaths;
7426
7541
  exports.generateRandomBytes = generateRandomBytes;
7427
7542
  exports.generateRandomBytesAsString = generateRandomBytesAsString;
7428
7543
  exports.getAllPaths = getAllPaths;
@@ -7431,6 +7546,7 @@ exports.getDirSize = getDirSize;
7431
7546
  exports.getDirStats = getDirStats;
7432
7547
  exports.getDomain = getDomain;
7433
7548
  exports.getErrorMessage = getErrorMessage;
7549
+ exports.getExpandedSensitiveFields = getExpandedSensitiveFields;
7434
7550
  exports.getExtension = getExtension;
7435
7551
  exports.getFileSize = getFileSize;
7436
7552
  exports.getFileStats = getFileStats;
@@ -7485,7 +7601,6 @@ exports.isValidUrl = isValidUrl;
7485
7601
  exports.joinPaths = joinPaths;
7486
7602
  exports.listFiles = listFiles;
7487
7603
  exports.logError = logError;
7488
- exports.logger = logger;
7489
7604
  exports.mapObject = mapObject;
7490
7605
  exports.mask = mask;
7491
7606
  exports.matchesPattern = matchesPattern;