@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.mjs CHANGED
@@ -14,29 +14,6 @@ import { URL as URL$1, URLSearchParams } from 'url';
14
14
  import express from 'express';
15
15
  import https from 'https';
16
16
 
17
- /*
18
- * The MIT License
19
- *
20
- * Copyright (c) 2025 Catbee Technologies. https://catbee.npm.hprasath.com/license
21
- *
22
- * Permission is hereby granted, free of charge, to any person obtaining a copy
23
- * of this software and associated documentation files (the "Software"), to deal
24
- * in the Software without restriction, including without limitation the rights
25
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
26
- * copies of the Software, and to permit persons to whom the Software is
27
- * furnished to do so, subject to the following conditions:
28
- *
29
- * The above copyright notice and this permission notice shall be included in all
30
- * copies or substantial portions of the Software.
31
- *
32
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
38
- * SOFTWARE.
39
- */
40
17
  var __defProp = Object.defineProperty;
41
18
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
42
19
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
@@ -960,16 +937,30 @@ function setValueByPath(obj, path3, value) {
960
937
  if (!obj || typeof obj !== "object") return obj;
961
938
  const parts = path3.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
962
939
  if (parts.length === 0) return obj;
940
+ const forbiddenKeys = /* @__PURE__ */ new Set([
941
+ "__proto__",
942
+ "prototype",
943
+ "constructor"
944
+ ]);
963
945
  let current = obj;
964
946
  for (let i = 0; i < parts.length - 1; i++) {
965
947
  const key = parts[i];
966
- const isArrayIndex = !isNaN(Number(parts[i + 1]));
948
+ if (forbiddenKeys.has(key)) {
949
+ return obj;
950
+ }
951
+ const nextKey = parts[i + 1];
952
+ const isArrayIndex = !isNaN(Number(nextKey));
967
953
  if (current[key] === void 0) {
968
- current[key] = isArrayIndex ? [] : {};
954
+ current[key] = isArrayIndex ? [] : /* @__PURE__ */ Object.create(null);
955
+ } else if (typeof current[key] !== "object" || current[key] === null) {
956
+ current[key] = isArrayIndex ? [] : /* @__PURE__ */ Object.create(null);
969
957
  }
970
958
  current = current[key];
971
959
  }
972
960
  const finalKey = parts[parts.length - 1];
961
+ if (forbiddenKeys.has(finalKey)) {
962
+ return obj;
963
+ }
973
964
  current[finalKey] = value;
974
965
  return obj;
975
966
  }
@@ -1045,48 +1036,21 @@ function getAllPaths(obj, parentPath = "") {
1045
1036
  __name(getAllPaths, "getAllPaths");
1046
1037
 
1047
1038
  // src/config.ts
1048
- var config = {
1039
+ var defaultCatbeeConfig = {
1049
1040
  logger: {
1050
- /**
1051
- * Logging level (e.g., 'info', 'debug', 'warn', 'error').
1052
- */
1053
- level: Env.get("LOGGER_LEVEL", "info"),
1054
- /**
1055
- * Name of the logger instance (defaults to npm package name).
1056
- */
1041
+ level: Env.get("LOGGER_LEVEL", Env.isDev() ? "debug" : "info"),
1057
1042
  name: Env.get("LOGGER_NAME", Env.get("npm_package_name", "@catbee/utils")),
1058
- /**
1059
- * Enables pretty-print logging in development.
1060
- * Has no effect in production.
1061
- */
1062
1043
  pretty: Env.getBoolean("LOGGER_PRETTY", true),
1063
- /**
1064
- * Enables colorized output for pretty-print (default: true)
1065
- */
1066
1044
  colorize: Env.getBoolean("LOGGER_PRETTY_COLORIZE", true),
1067
- /**
1068
- * Single line output for pretty-print (default: false)
1069
- */
1070
- singleLine: Env.getBoolean("LOGGER_PRETTY_SINGLE_LINE", false)
1045
+ singleLine: Env.getBoolean("LOGGER_PRETTY_SINGLE_LINE", false),
1046
+ dir: Env.getPath("LOGGER_DIR", "", {
1047
+ mustExist: false
1048
+ })
1071
1049
  },
1072
1050
  cache: {
1073
- /**
1074
- * Default TTL (time to live) for cache entries in seconds
1075
- */
1076
1051
  defaultTtl: Env.getNumber("CACHE_DEFAULT_TTL_SECONDS", 3600) * 1e3
1077
1052
  },
1078
1053
  server: {
1079
- /**
1080
- * Skip healthz endpoint even if health checks are configured
1081
- * Default: false
1082
- * Set to true to return 200 OK for /healthz without checks
1083
- * Useful in environments where a simple liveness probe is needed
1084
- * without performing actual health checks
1085
- * Example: Kubernetes liveness probe
1086
- * Note: This does not disable the health check functionality itself
1087
- * Health checks can still be performed programmatically
1088
- * or via other endpoints if needed
1089
- */
1090
1054
  skipHealthz: Env.getBoolean("SERVER_SKIP_HEALTHZ", false)
1091
1055
  }
1092
1056
  };
@@ -1164,11 +1128,11 @@ var defaultServerConfig = {
1164
1128
  }
1165
1129
  };
1166
1130
  function setConfig(value) {
1167
- deepObjMerge(config, value);
1131
+ deepObjMerge(defaultCatbeeConfig, value);
1168
1132
  }
1169
1133
  __name(setConfig, "setConfig");
1170
1134
  function getConfig() {
1171
- return config;
1135
+ return defaultCatbeeConfig;
1172
1136
  }
1173
1137
  __name(getConfig, "getConfig");
1174
1138
 
@@ -1605,27 +1569,66 @@ var defaultSensitiveFields = [
1605
1569
  "password",
1606
1570
  "secret",
1607
1571
  "token",
1608
- "apiKey",
1609
1572
  "api_key",
1610
1573
  "auth",
1574
+ "private_key",
1575
+ "public_key",
1611
1576
  "jwt",
1612
1577
  "access_token",
1613
- "client_secret",
1578
+ "refresh_token",
1614
1579
  "session_token",
1615
- "refresh_token"
1580
+ "csrf_token",
1581
+ "authorization",
1582
+ "bearer",
1583
+ "x-api-key",
1584
+ "x-auth-token",
1585
+ "x-access-token",
1586
+ "client_secret",
1587
+ "passphrase",
1588
+ "otp",
1589
+ "api_secret",
1590
+ "token_secret"
1616
1591
  ];
1617
- var globalRedactCensor = /* @__PURE__ */ __name((value, path3, sensitiveFields = defaultSensitiveFields) => {
1592
+ var defaultRedactPaths = [
1593
+ "req.authorization",
1594
+ "req.headers.authorization",
1595
+ "req.headers.cookie",
1596
+ 'req.headers["x-api-key"]',
1597
+ 'req.headers["x-auth-token"]',
1598
+ 'req.headers["x-access-token"]',
1599
+ "req.body.password",
1600
+ "req.body.token",
1601
+ "req.body.secret",
1602
+ "req.query.token",
1603
+ "req.query.api_key",
1604
+ "req.query.apiKey",
1605
+ "res.authorization",
1606
+ "res.headers.authorization",
1607
+ 'res.headers["set-cookie"]',
1608
+ "headers.authorization",
1609
+ "headers.cookies",
1610
+ 'headers["set-cookie"]',
1611
+ 'headers["x-api-key"]',
1612
+ 'headers["x-auth-token"]',
1613
+ 'headers["x-access-token"]',
1614
+ "url",
1615
+ "uri",
1616
+ "href",
1617
+ "redirect_uri",
1618
+ "redirectUri"
1619
+ ];
1620
+ var globalRedactCensor = /* @__PURE__ */ __name((value, path3, sensitiveFields = getExpandedSensitiveFields()) => {
1618
1621
  if (typeof value !== "string") return "***";
1619
- const lowerPath = path3.map((p) => p.toLowerCase());
1622
+ const lowerPath = path3.filter((p) => typeof p === "string").map((p) => p.toLowerCase());
1620
1623
  const lowerSensitiveFields = sensitiveFields.map((f) => f.toLowerCase());
1621
1624
  if (lowerPath.some((p) => lowerSensitiveFields.includes(p))) return "***";
1622
- if (lowerPath[0] === "url") {
1625
+ if (lowerPath[0] === "url" || lowerPath[0] === "uri" || lowerPath[0] === "href" || lowerPath.includes("redirect_uri") || lowerPath.includes("redirecturi")) {
1623
1626
  return value.replace(new RegExp(`([?&](${lowerSensitiveFields.join("|")})=)[^&#]*`, "gi"), "$1***");
1624
1627
  }
1625
1628
  if (lowerPath.some((p) => p.includes("authorization") || p.includes("auth"))) {
1626
1629
  return value.replace(/^(\S+)\s+.+$/, "$1 ***") || "***";
1627
1630
  }
1628
- if (lowerPath.some((pathPart) => lowerSensitiveFields.some((field) => pathPart.includes(field)))) {
1631
+ if (lowerPath.some((p) => lowerSensitiveFields.some((f) => p.includes(f)))) {
1629
1632
  return "***";
1630
1633
  }
1631
1634
  if (path3.length > 0 && ![
@@ -1651,34 +1654,44 @@ function redact(value, path3, sensitiveFields) {
1651
1654
  __name(redact, "redact");
1652
1655
  function addRedactFields(fields) {
1653
1656
  const prev = globalRedactCensor;
1654
- globalRedactCensor = /* @__PURE__ */ __name((value, path3, sensitiveFields = defaultSensitiveFields) => prev(value, path3, [
1657
+ globalRedactCensor = /* @__PURE__ */ __name((value, path3, sensitiveFields = getExpandedSensitiveFields()) => prev(value, path3, [
1655
1658
  ...sensitiveFields || [],
1656
1659
  ...fields
1657
1660
  ]), "globalRedactCensor");
1661
+ cachedExpandedFields = null;
1658
1662
  }
1659
1663
  __name(addRedactFields, "addRedactFields");
1664
+ var cachedExpandedFields = null;
1665
+ function getExpandedSensitiveFields() {
1666
+ if (!cachedExpandedFields) {
1667
+ cachedExpandedFields = expandSensitiveFields(defaultSensitiveFields);
1668
+ }
1669
+ return cachedExpandedFields;
1670
+ }
1671
+ __name(getExpandedSensitiveFields, "getExpandedSensitiveFields");
1660
1672
  function setSensitiveFields(fields) {
1661
1673
  defaultSensitiveFields.splice(0, defaultSensitiveFields.length, ...fields);
1674
+ cachedExpandedFields = null;
1662
1675
  }
1663
1676
  __name(setSensitiveFields, "setSensitiveFields");
1664
1677
  function addSensitiveFields(fields) {
1665
1678
  defaultSensitiveFields.push(...fields);
1679
+ cachedExpandedFields = null;
1666
1680
  }
1667
1681
  __name(addSensitiveFields, "addSensitiveFields");
1668
1682
  var _globalThis = typeof globalThis === "object" ? globalThis : global;
1669
1683
  var _global = _globalThis;
1670
1684
  function setupLogger(isGlobal = true) {
1685
+ const sensitiveFields = getExpandedSensitiveFields();
1686
+ const paths = /* @__PURE__ */ new Set([
1687
+ ...defaultRedactPaths,
1688
+ ...sensitiveFields.flatMap((field) => generateDeepPaths(field, 2))
1689
+ ]);
1671
1690
  const logParams = {
1672
- name: config.logger.name || "@catbee/utils",
1673
- level: config.logger.level || "info",
1691
+ name: defaultCatbeeConfig.logger?.name || "@catbee/utils",
1692
+ level: defaultCatbeeConfig.logger?.level || "info",
1674
1693
  redact: {
1675
- paths: [
1676
- "req.authorization",
1677
- "res.authorization",
1678
- "url",
1679
- "headers.authorization",
1680
- "headers.cookies"
1681
- ],
1694
+ paths: Array.from(paths),
1682
1695
  censor: /* @__PURE__ */ __name((value, path3) => redact(value, path3), "censor")
1683
1696
  },
1684
1697
  serializers: {
@@ -1691,64 +1704,147 @@ function setupLogger(isGlobal = true) {
1691
1704
  },
1692
1705
  timestamp: stdTimeFunctions.isoTime
1693
1706
  };
1694
- const logger2 = config.logger.pretty ? pino(logParams, pino.transport({
1695
- target: "pino-pretty",
1696
- options: {
1697
- colorize: config.logger.colorize,
1698
- translateTime: "SYS:standard",
1699
- ignore: "pid,hostname",
1700
- singleLine: config.logger.singleLine,
1701
- levelFirst: true
1702
- }
1703
- })) : pino(logParams);
1707
+ let logger;
1708
+ const logDir = defaultCatbeeConfig.logger?.dir?.trim();
1709
+ const hasFileLogging = Boolean(logDir);
1710
+ if (hasFileLogging && defaultCatbeeConfig.logger?.pretty) {
1711
+ logger = pino(logParams, pino.transport({
1712
+ targets: [
1713
+ {
1714
+ target: "pino-pretty",
1715
+ level: defaultCatbeeConfig.logger?.level ?? "info",
1716
+ options: {
1717
+ colorize: defaultCatbeeConfig.logger?.colorize,
1718
+ translateTime: "SYS:standard",
1719
+ ignore: "pid,hostname",
1720
+ singleLine: defaultCatbeeConfig.logger?.singleLine,
1721
+ levelFirst: true
1722
+ }
1723
+ },
1724
+ {
1725
+ target: "pino/file",
1726
+ level: defaultCatbeeConfig.logger?.level ?? "info",
1727
+ options: {
1728
+ destination: `${logDir}/app.log`,
1729
+ mkdir: true
1730
+ }
1731
+ }
1732
+ ]
1733
+ }));
1734
+ } else if (hasFileLogging) {
1735
+ logger = pino(logParams, pino.transport({
1736
+ target: "pino/file",
1737
+ options: {
1738
+ destination: `${logDir}/app.log`,
1739
+ mkdir: true
1740
+ }
1741
+ }));
1742
+ } else if (defaultCatbeeConfig.logger?.pretty) {
1743
+ logger = pino(logParams, pino.transport({
1744
+ target: "pino-pretty",
1745
+ options: {
1746
+ colorize: defaultCatbeeConfig.logger?.colorize,
1747
+ translateTime: "SYS:standard",
1748
+ ignore: "pid,hostname",
1749
+ singleLine: defaultCatbeeConfig.logger?.singleLine,
1750
+ levelFirst: true
1751
+ }
1752
+ }));
1753
+ } else {
1754
+ logger = pino(logParams);
1755
+ }
1704
1756
  if (isGlobal) {
1705
- _global[GLOBAL_LOGGER_KEY] = logger2;
1706
- _global[GLOBAL_LOGGER_KEY]?.debug("Logger initialized");
1757
+ _global[GLOBAL_LOGGER_KEY] = logger;
1758
+ _global[GLOBAL_LOGGER_KEY]?.debug({
1759
+ logDir: logDir || "none"
1760
+ }, "Logger initialized");
1707
1761
  }
1708
- return logger2;
1762
+ return logger;
1709
1763
  }
1710
1764
  __name(setupLogger, "setupLogger");
1711
1765
  function getLogger(newInstance = false) {
1712
1766
  if (newInstance) {
1713
1767
  return setupLogger(false);
1714
1768
  }
1715
- const logger2 = ContextStore.get(StoreKeys.LOGGER);
1716
- if (logger2) return logger2;
1769
+ const logger = ContextStore.get(StoreKeys.LOGGER);
1770
+ if (logger) return logger;
1717
1771
  if (!_global[GLOBAL_LOGGER_KEY]) {
1718
1772
  setupLogger();
1719
1773
  }
1720
1774
  return _global[GLOBAL_LOGGER_KEY];
1721
1775
  }
1722
1776
  __name(getLogger, "getLogger");
1723
- var logger = getLogger(false);
1724
1777
  function createChildLogger(bindings, parentLogger) {
1725
- const logger2 = parentLogger || getLogger();
1726
- return logger2.child(bindings);
1778
+ const logger = parentLogger || getLogger();
1779
+ return logger.child(bindings);
1727
1780
  }
1728
1781
  __name(createChildLogger, "createChildLogger");
1729
1782
  function createRequestLogger(requestId2, additionalContext = {}) {
1730
- const logger2 = createChildLogger({
1783
+ const logger = createChildLogger({
1731
1784
  requestId: requestId2,
1732
1785
  ...additionalContext
1733
1786
  });
1734
1787
  try {
1735
- ContextStore.set(StoreKeys.LOGGER, logger2);
1788
+ ContextStore.set(StoreKeys.LOGGER, logger);
1736
1789
  } catch {
1737
- logger2.debug("Failed to store logger in context - AsyncLocalStorage not initialized");
1790
+ logger.debug("Failed to store logger in context - AsyncLocalStorage not initialized");
1738
1791
  }
1739
- return logger2;
1792
+ return logger;
1740
1793
  }
1741
1794
  __name(createRequestLogger, "createRequestLogger");
1742
1795
  function logError(error, message, context) {
1743
- const logger2 = getLogger();
1796
+ const logger = getLogger();
1744
1797
  const errObj = error instanceof Error ? error : new Error(String(error));
1745
1798
  const logContext = {
1746
1799
  ...context,
1747
1800
  error: errObj
1748
1801
  };
1749
- logger2.error(logContext, message || errObj.message);
1802
+ logger.error(logContext, message || errObj.message);
1750
1803
  }
1751
1804
  __name(logError, "logError");
1805
+ function expandSensitiveFields(fields) {
1806
+ const set = /* @__PURE__ */ new Set();
1807
+ for (const field of fields) {
1808
+ for (const v of expandSensitiveField(field)) {
1809
+ set.add(v);
1810
+ }
1811
+ }
1812
+ return Array.from(set);
1813
+ }
1814
+ __name(expandSensitiveFields, "expandSensitiveFields");
1815
+ function expandSensitiveField(field) {
1816
+ const parts = field.split(/[_-]/g).filter(Boolean);
1817
+ const camel = parts[0].toLowerCase() + parts.slice(1).map((p) => p.charAt(0).toUpperCase() + p.slice(1).toLowerCase()).join("");
1818
+ const mergedLower = parts.join("").toLowerCase();
1819
+ const mergedUpper = parts.join("").toUpperCase();
1820
+ const kebab = parts.map((p) => p.toLowerCase()).join("-");
1821
+ const snakeLower = parts.map((p) => p.toLowerCase()).join("_");
1822
+ const snakeUpper = parts.map((p) => p.toUpperCase()).join("_");
1823
+ return Array.from(/* @__PURE__ */ new Set([
1824
+ field,
1825
+ camel,
1826
+ mergedLower,
1827
+ mergedUpper,
1828
+ kebab,
1829
+ snakeLower,
1830
+ snakeUpper
1831
+ ]));
1832
+ }
1833
+ __name(expandSensitiveField, "expandSensitiveField");
1834
+ function generateDeepPaths(field, depth) {
1835
+ const set = /* @__PURE__ */ new Set([
1836
+ field
1837
+ ]);
1838
+ let prefix = "";
1839
+ for (let i = 1; i <= depth; i++) {
1840
+ prefix = prefix ? `${prefix}.*` : "*";
1841
+ set.add(`${prefix}.${field}`);
1842
+ }
1843
+ return [
1844
+ ...set
1845
+ ];
1846
+ }
1847
+ __name(generateDeepPaths, "generateDeepPaths");
1752
1848
 
1753
1849
  // src/utils/async.utils.ts
1754
1850
  function sleep(ms) {
@@ -2128,7 +2224,7 @@ var TTLCache = class {
2128
2224
  * @param options - Configuration options for the cache
2129
2225
  */
2130
2226
  constructor(options = {}) {
2131
- this.ttlMs = options.ttlMs ?? config.cache.defaultTtl;
2227
+ this.ttlMs = options.ttlMs ?? defaultCatbeeConfig.cache.defaultTtl;
2132
2228
  this.maxSize = options.maxSize;
2133
2229
  const autoCleanupMs = options.autoCleanupMs;
2134
2230
  if (autoCleanupMs && autoCleanupMs > 0) {
@@ -3739,7 +3835,7 @@ function Timeout(ms) {
3739
3835
  __name(Timeout, "Timeout");
3740
3836
  function Log(options) {
3741
3837
  return (target, propertyKey, _descriptor) => {
3742
- const config2 = {
3838
+ const config = {
3743
3839
  logEntry: true,
3744
3840
  logExit: true,
3745
3841
  logBody: false,
@@ -3748,9 +3844,9 @@ function Log(options) {
3748
3844
  ...options
3749
3845
  };
3750
3846
  if (typeof propertyKey === "undefined") {
3751
- Reflect.defineMetadata(LOG_KEY, config2, target);
3847
+ Reflect.defineMetadata(LOG_KEY, config, target);
3752
3848
  } else {
3753
- Reflect.defineMetadata(LOG_KEY, config2, target, propertyKey);
3849
+ Reflect.defineMetadata(LOG_KEY, config, target, propertyKey);
3754
3850
  }
3755
3851
  };
3756
3852
  }
@@ -3860,7 +3956,7 @@ function registerControllers(router, controllers) {
3860
3956
  res.setHeader("Cache-Control", `public, max-age=${cache.ttlSeconds}`);
3861
3957
  }
3862
3958
  if (logConfig?.logEntry) {
3863
- const logger2 = getLogger();
3959
+ const logger = getLogger();
3864
3960
  const logData = {
3865
3961
  method: req.method,
3866
3962
  url: req.originalUrl || req.url,
@@ -3871,7 +3967,7 @@ function registerControllers(router, controllers) {
3871
3967
  logData.query = req.query;
3872
3968
  }
3873
3969
  if (logConfig.logBody) logData.body = req.body;
3874
- logger2.info({
3970
+ logger.info({
3875
3971
  entry: logData
3876
3972
  }, "Route Entry:");
3877
3973
  }
@@ -3949,7 +4045,7 @@ function registerControllers(router, controllers) {
3949
4045
  res.json(awaited);
3950
4046
  }
3951
4047
  if (logConfig?.logExit) {
3952
- const logger2 = getLogger();
4048
+ const logger = getLogger();
3953
4049
  const logData = {
3954
4050
  method: req.method,
3955
4051
  url: req.originalUrl || req.url,
@@ -3957,7 +4053,7 @@ function registerControllers(router, controllers) {
3957
4053
  duration: `${Date.now() - req.startTime}ms`
3958
4054
  };
3959
4055
  if (logConfig.logResponse) logData.response = awaited;
3960
- logger2.info({
4056
+ logger.info({
3961
4057
  exit: logData
3962
4058
  }, "Route Exit:");
3963
4059
  }
@@ -4828,36 +4924,36 @@ function timeSync(fn, options = {}) {
4828
4924
  label
4829
4925
  };
4830
4926
  if (log) {
4831
- const logger2 = getLogger();
4927
+ const logger = getLogger();
4832
4928
  const message = `${label} completed in ${durationMs.toFixed(2)}ms`;
4833
4929
  switch (logLevel) {
4834
4930
  case "trace":
4835
- logger2.trace({
4931
+ logger.trace({
4836
4932
  timing
4837
4933
  }, message);
4838
4934
  break;
4839
4935
  case "debug":
4840
- logger2.debug({
4936
+ logger.debug({
4841
4937
  timing
4842
4938
  }, message);
4843
4939
  break;
4844
4940
  case "info":
4845
- logger2.info({
4941
+ logger.info({
4846
4942
  timing
4847
4943
  }, message);
4848
4944
  break;
4849
4945
  case "warn":
4850
- logger2.warn({
4946
+ logger.warn({
4851
4947
  timing
4852
4948
  }, message);
4853
4949
  break;
4854
4950
  case "error":
4855
- logger2.error({
4951
+ logger.error({
4856
4952
  timing
4857
4953
  }, message);
4858
4954
  break;
4859
4955
  default:
4860
- logger2.debug({
4956
+ logger.debug({
4861
4957
  timing
4862
4958
  }, message);
4863
4959
  break;
@@ -4884,36 +4980,36 @@ async function timeAsync(fn, options = {}) {
4884
4980
  label
4885
4981
  };
4886
4982
  if (log) {
4887
- const logger2 = getLogger();
4983
+ const logger = getLogger();
4888
4984
  const message = `${label} completed in ${durationMs.toFixed(2)}ms`;
4889
4985
  switch (logLevel) {
4890
4986
  case "trace":
4891
- logger2.trace({
4987
+ logger.trace({
4892
4988
  timing
4893
4989
  }, message);
4894
4990
  break;
4895
4991
  case "debug":
4896
- logger2.debug({
4992
+ logger.debug({
4897
4993
  timing
4898
4994
  }, message);
4899
4995
  break;
4900
4996
  case "info":
4901
- logger2.info({
4997
+ logger.info({
4902
4998
  timing
4903
4999
  }, message);
4904
5000
  break;
4905
5001
  case "warn":
4906
- logger2.warn({
5002
+ logger.warn({
4907
5003
  timing
4908
5004
  }, message);
4909
5005
  break;
4910
5006
  case "error":
4911
- logger2.error({
5007
+ logger.error({
4912
5008
  timing
4913
5009
  }, message);
4914
5010
  break;
4915
5011
  default:
4916
- logger2.debug({
5012
+ logger.debug({
4917
5013
  timing
4918
5014
  }, message);
4919
5015
  break;
@@ -5584,10 +5680,26 @@ function isPort(value) {
5584
5680
  }
5585
5681
  __name(isPort, "isPort");
5586
5682
  function isEmail(str) {
5587
- if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(str)) return false;
5588
- const [local, domain] = str.split("@");
5683
+ if (typeof str !== "string" || str.length > 254) return false;
5684
+ const atIndex = str.indexOf("@");
5685
+ if (atIndex === -1 || atIndex !== str.lastIndexOf("@")) return false;
5686
+ const local = str.slice(0, atIndex);
5687
+ const domain = str.slice(atIndex + 1);
5589
5688
  if (!local || !domain) return false;
5590
- if (local.includes("..") || domain.includes("..")) return false;
5689
+ const dotAtom = /^[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+(\.[A-Za-z0-9!#$%&'*+/=?^_`{|}~-]+)*$/;
5690
+ const quotedLocal = /^"([\s\x21\x23-\x5B\x5D-\x7E]|\\[\x20-\x7E])*"$/;
5691
+ if (!dotAtom.test(local) && !quotedLocal.test(local)) return false;
5692
+ if (domain.includes("..")) return false;
5693
+ const labels = domain.split(".");
5694
+ if (labels.some((l) => !l.length)) return false;
5695
+ if (labels.length < 2) return false;
5696
+ const labelRegex = /^[A-Za-z0-9-]{1,63}$/;
5697
+ for (const label of labels) {
5698
+ if (!labelRegex.test(label)) return false;
5699
+ if (label.startsWith("-") || label.endsWith("-")) return false;
5700
+ }
5701
+ const tld = labels[labels.length - 1];
5702
+ if (!/^[A-Za-z]{2,63}$/.test(tld)) return false;
5591
5703
  return true;
5592
5704
  }
5593
5705
  __name(isEmail, "isEmail");
@@ -6331,12 +6443,12 @@ var ServerConfigBuilder = class {
6331
6443
  * ```
6332
6444
  */
6333
6445
  build() {
6334
- const config2 = deepObjMerge({}, defaultServerConfig, this.config);
6335
- if (config2.openApi?.enable && !config2.openApi.filePath) {
6446
+ const config = deepObjMerge({}, defaultServerConfig, this.config);
6447
+ if (config.openApi?.enable && !config.openApi.filePath) {
6336
6448
  throw new Error("OpenAPI is enabled but no filePath is specified");
6337
6449
  }
6338
6450
  return Object.freeze({
6339
- ...config2,
6451
+ ...config,
6340
6452
  [BUILD_MARKER]: true
6341
6453
  });
6342
6454
  }
@@ -6407,11 +6519,11 @@ var ExpressServer = class _ExpressServer {
6407
6519
  * - Health checks
6408
6520
  * - Request tracing
6409
6521
  */
6410
- constructor(config2, hooks = {}) {
6411
- if (_ExpressServer.isBuiltServerConfig(config2)) {
6412
- this.config = config2;
6522
+ constructor(config, hooks = {}) {
6523
+ if (_ExpressServer.isBuiltServerConfig(config)) {
6524
+ this.config = config;
6413
6525
  } else {
6414
- this.config = deepObjMerge({}, defaultServerConfig, config2);
6526
+ this.config = deepObjMerge({}, defaultServerConfig, config);
6415
6527
  }
6416
6528
  if (!isPort(this.config.port)) {
6417
6529
  getLogger().error(`Port must be a valid number between 1 and 65535, got: ${this.config.port}`);
@@ -6496,8 +6608,8 @@ var ExpressServer = class _ExpressServer {
6496
6608
  prefix: `${safeAppName}_`
6497
6609
  });
6498
6610
  }
6499
- if (config2.healthCheck?.checks) {
6500
- this.healthChecks.push(...config2.healthCheck.checks);
6611
+ if (config.healthCheck?.checks) {
6612
+ this.healthChecks.push(...config.healthCheck.checks);
6501
6613
  }
6502
6614
  this.globalPrefix = this.normalizePath(this.config.globalPrefix ?? "", false);
6503
6615
  this.hooks = hooks;
@@ -6654,14 +6766,14 @@ var ExpressServer = class _ExpressServer {
6654
6766
  const skip = this.config.requestLogging?.ignorePaths?.some((path3) => req.path.startsWith(path3));
6655
6767
  if (skip) return next();
6656
6768
  }
6657
- const logger2 = getLogger();
6769
+ const logger = getLogger();
6658
6770
  const incomingRequestMetaData = {
6659
6771
  requestId: req.id,
6660
6772
  method: req.method,
6661
6773
  url: req.originalUrl || req.url,
6662
6774
  ip: req.ip
6663
6775
  };
6664
- logger2.info(incomingRequestMetaData, "Incoming Request");
6776
+ logger.info(incomingRequestMetaData, "Incoming Request");
6665
6777
  next();
6666
6778
  });
6667
6779
  }
@@ -6806,7 +6918,7 @@ var ExpressServer = class _ExpressServer {
6806
6918
  const healthCheckPath = this.normalizePath(this.config.healthCheck?.path || "/healthz", this.config.healthCheck?.withGlobalPrefix);
6807
6919
  this.app.get(healthCheckPath, async (_req, res) => {
6808
6920
  try {
6809
- if (!this.healthChecks.length || config.server.skipHealthz) {
6921
+ if (!this.healthChecks.length || defaultCatbeeConfig.server.skipHealthz) {
6810
6922
  return res.status(HttpStatusCodes.OK).json(new SuccessResponse("OK"));
6811
6923
  }
6812
6924
  const checkResults = await Promise.allSettled(this.healthChecks.map(async ({ name, check }) => {
@@ -7256,8 +7368,8 @@ var ExpressServer = class _ExpressServer {
7256
7368
  process.exit(1);
7257
7369
  }
7258
7370
  }
7259
- static isBuiltServerConfig(config2) {
7260
- if (config2[BUILD_MARKER]) {
7371
+ static isBuiltServerConfig(config) {
7372
+ if (config[BUILD_MARKER]) {
7261
7373
  return true;
7262
7374
  }
7263
7375
  return false;
@@ -7271,4 +7383,4 @@ var ExpressServer = class _ExpressServer {
7271
7383
  }
7272
7384
  };
7273
7385
 
7274
- export { After, BUILD_MARKER, BadGatewayException, BadRequestException, Before, Body, Cache, CircuitBreakerOpenError, CircuitBreakerState, ConflictException, Connect, ContentType, ContextStore, Controller, DIContainer, Delete, Env, Environment, ErrorResponse, ExpressServer, ForbiddenException, GatewayTimeoutException, Get, Head, Header, Headers, HttpCode, HttpError, HttpStatusCodes, Inject, Injectable, InsufficientStorageException, InternalServerErrorException, Log, MethodNotAllowedException, NoContentResponse, NotAcceptableException, NotFoundException, Options, PaginatedResponse, Param, Patch, PayloadTooLargeException, Post, Put, Query, RateLimit, Redirect, RedirectResponse, Req, ReqCookie, ReqHeader, ReqId, ReqLogger, RequestTimeoutException, Res, Roles, ServerConfigBuilder, ServiceUnavailableException, SortDirection, StoreKeys, SuccessResponse, TTLCache, Timeout, TooManyRequestsException, Trace, TypedContextKey, UnauthorizedException, UnprocessableEntityException, UnsupportedMediaTypeException, Use, Version, _globalThis, abortable, addRedactFields, addSensitiveFields, addToDate, appendQueryParams, appendTextFile, bufferToStream, capitalize, chunk, circuitBreaker, compact, copyDir, copyFile, countBy, countOccurrences, createBatchStream, createChildLogger, createDeferred, createDirectory, createErrorResponse, createFinalErrorResponse, createHttpError, createLineStream, createPaginatedResponse, createParamDecorator, createParamDecoratorWithoutParam, createRequestLogger, createSignedToken, createSuccessResponse, createTaskQueue, createTempDir, createTempFile, createThrottleStream, createUrlBuilder, dateDiff, daysInMonth, debounce, decrypt, deepFreeze, deepObjMerge, defaultSensitiveFields, deleteDirRecursive, deleteFileIfExists, difference, emptyDir, encrypt, endOf, ensureDir, ensureEmptyDir, ensureType, equalsIgnoreCase, errorHandler, extractFilterParams, extractPaginationParams, extractQueryParams, extractSortParams, fileExists, filterObject, findFilesByPattern, findInDir, findNewestFile, findOldestFile, flattenDeep, flattenObject, formatDate, formatRelativeTime, generateApiKey, generateRandomBytes, generateRandomBytesAsString, getAllPaths, getConfig, getDirSize, getDirStats, getDomain, getErrorMessage, getExtension, getFileSize, getFileStats, getFromContext, getLogger, getPaginationParams, getRedactCensor, getRequestId, getSubdirectories, getTypeOf, getValueByPath, groupBy, hasErrorShape, hasRequiredProps, hash, healthCheck, hmac, inject, intersect, isAlpha, isAlphanumeric, isArray, isArrayOf, isBase64, isBetween, isCreditCard, isDateInRange, isDirectory, isEmail, isEqual, isFile, isHexColor, isHttpError, isIPv4, isIPv6, isISODate, isLeapYear, isLengthBetween, isNumberBetween, isNumeric, isObjEmpty, isObject, isPhone, isPlainObject, isPort, isPrimitiveType, isStrongPassword, isURL, isUUID, isValidJSON, isValidUrl, joinPaths, listFiles, logError, logger, mapObject, mask, matchesPattern, md5, memoize, memoizeAsync, mergeSort, moveDir, moveFile, nanoId, normalizeUrl, omit, parseBooleanParam, parseDate, parseNumberParam, parseQueryString, parseTypedQueryParams, partition, pick, pluck, random, randomBase64, randomHex, randomInt, randomString, range, rateLimit, readDirectory, readFileBuffer, readJsonFile, readTextFile, redact, registerControllers, removeQueryParams, requestId, responseTime, retry, reverse, runInBatches, runInSeries, runWithConcurrency, safeCompare, safeReadJsonFile, sendResponse, setConfig, setRedactCensor, setSensitiveFields, setValueByPath, settleAll, setupRequestContext, sha1, sha256, sha256Hmac, shuffle, singletonAsync, sleep, slugify, startOf, streamFile, streamToBuffer, streamToString, stripHtml, take, takeWhile, throttle, timeAsync, timeSync, timed, timeout, toBool, toCamelCase, toKebabCase, toNum, toPascalCase, toSnakeCase, toStr, trackMemoryUsage, truncate, unique, uuid, validateAll, verifySignedToken, walkDir, watchDir, watchDirRecursive, waterfall, withErrorHandling, withTimeout, writeJsonFile, writeTextFile, zip };
7386
+ export { After, BUILD_MARKER, BadGatewayException, BadRequestException, Before, Body, Cache, CircuitBreakerOpenError, CircuitBreakerState, ConflictException, Connect, ContentType, ContextStore, Controller, DIContainer, Delete, Env, Environment, ErrorResponse, ExpressServer, ForbiddenException, GatewayTimeoutException, Get, Head, Header, Headers, HttpCode, HttpError, HttpStatusCodes, Inject, Injectable, InsufficientStorageException, InternalServerErrorException, Log, MethodNotAllowedException, NoContentResponse, NotAcceptableException, NotFoundException, Options, PaginatedResponse, Param, Patch, PayloadTooLargeException, Post, Put, Query, RateLimit, Redirect, RedirectResponse, Req, ReqCookie, ReqHeader, ReqId, ReqLogger, RequestTimeoutException, Res, Roles, ServerConfigBuilder, ServiceUnavailableException, SortDirection, StoreKeys, SuccessResponse, TTLCache, Timeout, TooManyRequestsException, Trace, TypedContextKey, UnauthorizedException, UnprocessableEntityException, UnsupportedMediaTypeException, Use, Version, _globalThis, abortable, addRedactFields, addSensitiveFields, addToDate, appendQueryParams, appendTextFile, bufferToStream, capitalize, chunk, circuitBreaker, compact, copyDir, copyFile, countBy, countOccurrences, createBatchStream, createChildLogger, createDeferred, createDirectory, createErrorResponse, createFinalErrorResponse, createHttpError, createLineStream, createPaginatedResponse, createParamDecorator, createParamDecoratorWithoutParam, createRequestLogger, createSignedToken, createSuccessResponse, createTaskQueue, createTempDir, createTempFile, createThrottleStream, createUrlBuilder, dateDiff, daysInMonth, debounce, decrypt, deepFreeze, deepObjMerge, defaultSensitiveFields, deleteDirRecursive, deleteFileIfExists, difference, emptyDir, encrypt, endOf, ensureDir, ensureEmptyDir, ensureType, equalsIgnoreCase, errorHandler, expandSensitiveField, expandSensitiveFields, extractFilterParams, extractPaginationParams, extractQueryParams, extractSortParams, fileExists, filterObject, findFilesByPattern, findInDir, findNewestFile, findOldestFile, flattenDeep, flattenObject, formatDate, formatRelativeTime, generateApiKey, generateDeepPaths, generateRandomBytes, generateRandomBytesAsString, getAllPaths, getConfig, getDirSize, getDirStats, getDomain, getErrorMessage, getExpandedSensitiveFields, getExtension, getFileSize, getFileStats, getFromContext, getLogger, getPaginationParams, getRedactCensor, getRequestId, getSubdirectories, getTypeOf, getValueByPath, groupBy, hasErrorShape, hasRequiredProps, hash, healthCheck, hmac, inject, intersect, isAlpha, isAlphanumeric, isArray, isArrayOf, isBase64, isBetween, isCreditCard, isDateInRange, isDirectory, isEmail, isEqual, isFile, isHexColor, isHttpError, isIPv4, isIPv6, isISODate, isLeapYear, isLengthBetween, isNumberBetween, isNumeric, isObjEmpty, isObject, isPhone, isPlainObject, isPort, isPrimitiveType, isStrongPassword, isURL, isUUID, isValidJSON, isValidUrl, joinPaths, listFiles, logError, mapObject, mask, matchesPattern, md5, memoize, memoizeAsync, mergeSort, moveDir, moveFile, nanoId, normalizeUrl, omit, parseBooleanParam, parseDate, parseNumberParam, parseQueryString, parseTypedQueryParams, partition, pick, pluck, random, randomBase64, randomHex, randomInt, randomString, range, rateLimit, readDirectory, readFileBuffer, readJsonFile, readTextFile, redact, registerControllers, removeQueryParams, requestId, responseTime, retry, reverse, runInBatches, runInSeries, runWithConcurrency, safeCompare, safeReadJsonFile, sendResponse, setConfig, setRedactCensor, setSensitiveFields, setValueByPath, settleAll, setupRequestContext, sha1, sha256, sha256Hmac, shuffle, singletonAsync, sleep, slugify, startOf, streamFile, streamToBuffer, streamToString, stripHtml, take, takeWhile, throttle, timeAsync, timeSync, timed, timeout, toBool, toCamelCase, toKebabCase, toNum, toPascalCase, toSnakeCase, toStr, trackMemoryUsage, truncate, unique, uuid, validateAll, verifySignedToken, walkDir, watchDir, watchDirRecursive, waterfall, withErrorHandling, withTimeout, writeJsonFile, writeTextFile, zip };