@catbee/utils 2.0.0-next.1 → 2.0.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.
Files changed (87) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +50 -36
  3. package/array/index.cjs +37 -5
  4. package/array/index.d.ts +59 -8
  5. package/array/index.mjs +32 -4
  6. package/async/index.cjs +24 -3
  7. package/async/index.d.ts +18 -2
  8. package/async/index.mjs +24 -4
  9. package/cache/index.cjs +1 -1
  10. package/cache/index.d.ts +1 -1
  11. package/cache/index.mjs +1 -1
  12. package/config/index.cjs +8 -8
  13. package/config/index.d.ts +1 -1
  14. package/config/index.mjs +4 -4
  15. package/context-store/index.cjs +2 -3
  16. package/context-store/index.d.ts +1 -1
  17. package/context-store/index.mjs +2 -3
  18. package/crypto/index.cjs +55 -5
  19. package/crypto/index.d.ts +63 -2
  20. package/crypto/index.mjs +52 -7
  21. package/date/index.cjs +630 -1
  22. package/date/index.d.ts +487 -2
  23. package/date/index.mjs +621 -2
  24. package/{decorators → decorator}/index.cjs +425 -304
  25. package/{decorators → decorator}/index.d.ts +3 -2
  26. package/{decorators → decorator}/index.mjs +425 -304
  27. package/{dir → directory}/index.cjs +1 -1
  28. package/{dir → directory}/index.d.ts +1 -1
  29. package/{dir → directory}/index.mjs +1 -1
  30. package/env/index.cjs +90 -42
  31. package/env/index.d.ts +12 -1
  32. package/env/index.mjs +90 -42
  33. package/exception/index.cjs +1 -1
  34. package/exception/index.d.ts +1 -1
  35. package/exception/index.mjs +1 -1
  36. package/fs/index.cjs +1 -1
  37. package/fs/index.d.ts +1 -1
  38. package/fs/index.mjs +1 -1
  39. package/http-status-codes/index.cjs +1 -1
  40. package/http-status-codes/index.d.ts +1 -1
  41. package/http-status-codes/index.mjs +1 -1
  42. package/id/index.cjs +1 -1
  43. package/id/index.d.ts +1 -1
  44. package/id/index.mjs +1 -1
  45. package/index.cjs +10 -10
  46. package/index.d.ts +4 -4
  47. package/index.mjs +4 -4
  48. package/logger/index.cjs +2 -4
  49. package/logger/index.d.ts +2 -2
  50. package/logger/index.mjs +2 -4
  51. package/middleware/index.cjs +1 -1
  52. package/middleware/index.d.ts +1 -1
  53. package/middleware/index.mjs +1 -1
  54. package/{obj → object}/index.cjs +182 -108
  55. package/{obj → object}/index.d.ts +38 -2
  56. package/{obj → object}/index.mjs +180 -109
  57. package/package.json +31 -13
  58. package/performance/index.cjs +2 -2
  59. package/performance/index.d.ts +1 -1
  60. package/performance/index.mjs +2 -2
  61. package/request/index.cjs +36 -24
  62. package/request/index.d.ts +1 -1
  63. package/request/index.mjs +36 -24
  64. package/response/index.cjs +1 -1
  65. package/response/index.d.ts +1 -1
  66. package/response/index.mjs +1 -1
  67. package/server/index.cjs +230 -131
  68. package/server/index.d.ts +81 -1
  69. package/server/index.mjs +226 -127
  70. package/stream/index.cjs +1 -1
  71. package/stream/index.d.ts +1 -1
  72. package/stream/index.mjs +1 -1
  73. package/string/index.cjs +34 -1
  74. package/string/index.d.ts +45 -2
  75. package/string/index.mjs +31 -2
  76. package/type/index.cjs +18 -1
  77. package/type/index.d.ts +38 -2
  78. package/type/index.mjs +16 -2
  79. package/types/index.cjs +1 -1
  80. package/types/index.d.ts +3 -3
  81. package/types/index.mjs +1 -1
  82. package/url/index.cjs +61 -1
  83. package/url/index.d.ts +59 -2
  84. package/url/index.mjs +57 -2
  85. package/validation/index.cjs +3 -3
  86. package/validation/index.d.ts +1 -1
  87. package/validation/index.mjs +3 -3
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * The MIT License
3
3
  *
4
- * Copyright (c) 2025 Catbee Technologies. https://catbee.in/license
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
package/server/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * The MIT License
3
3
  *
4
- * Copyright (c) 2025 Catbee Technologies. https://catbee.in/license
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
@@ -33,7 +33,7 @@ var env = require('@catbee/utils/env');
33
33
  var logger = require('@catbee/utils/logger');
34
34
  var exception = require('@catbee/utils/exception');
35
35
  var config = require('@catbee/utils/config');
36
- var obj = require('@catbee/utils/obj');
36
+ var object = require('@catbee/utils/object');
37
37
  var fs = require('@catbee/utils/fs');
38
38
  var validation = require('@catbee/utils/validation');
39
39
  var async = require('@catbee/utils/async');
@@ -611,7 +611,7 @@ var ServerConfigBuilder = class {
611
611
  * ```
612
612
  */
613
613
  withCustom(overrides) {
614
- this.config = obj.deepObjMerge({}, this.config, overrides);
614
+ this.config = object.deepObjMerge({}, this.config, overrides);
615
615
  return this;
616
616
  }
617
617
  /**
@@ -651,7 +651,7 @@ var ServerConfigBuilder = class {
651
651
  * ```
652
652
  */
653
653
  build() {
654
- const config$1 = obj.deepObjMerge({}, config.getCatbeeServerGlobalConfig(), this.config);
654
+ const config$1 = object.deepObjMerge({}, config.getCatbeeServerGlobalConfig(), this.config);
655
655
  if (config$1.openApi?.enable && !config$1.openApi.filePath) {
656
656
  throw new Error("OpenAPI is enabled but no filePath is specified");
657
657
  }
@@ -661,8 +661,8 @@ var ServerConfigBuilder = class {
661
661
  });
662
662
  }
663
663
  mergeConfig(key, value) {
664
- const current = this.config[key] && typeof this.config[key] === "object" ? obj.deepClone(this.config[key]) : {};
665
- this.config[key] = obj.deepObjMerge({}, current, value);
664
+ const current = this.config[key] && typeof this.config[key] === "object" ? object.deepClone(this.config[key]) : {};
665
+ this.config[key] = object.deepObjMerge({}, current, value);
666
666
  }
667
667
  setEnabled(key, enable, overrides = {}) {
668
668
  this.mergeConfig(key, {
@@ -744,7 +744,7 @@ var ExpressServer = class {
744
744
  if (this.hasBuildMarker(config$1)) {
745
745
  this.config = config$1;
746
746
  } else {
747
- this.config = obj.deepObjMerge({}, config.getCatbeeServerGlobalConfig(), config$1);
747
+ this.config = object.deepObjMerge({}, config.getCatbeeServerGlobalConfig(), config$1);
748
748
  }
749
749
  if (!validation.isPort(this.config.port)) {
750
750
  const msg = `Port must be a valid number between 1 and 65535, got: ${this.config.port}`;
@@ -886,6 +886,24 @@ var ExpressServer = class {
886
886
  if (this.config.https) {
887
887
  await this.validateHttpsFiles();
888
888
  }
889
+ this.setupBasicMiddleware();
890
+ this.setupSecurityMiddleware();
891
+ this.setupGlobalHeaders();
892
+ this.setupTimeoutMiddleware();
893
+ this.setupResponseTimeMiddleware();
894
+ this.setupRateLimitingMiddleware();
895
+ this.setupRequestLoggingMiddleware();
896
+ this.setupCompressionMiddleware();
897
+ this.setupStaticFilesMiddleware();
898
+ this.setupBodyParsingMiddleware();
899
+ this.setupCookieParsingMiddleware();
900
+ await this.setupOpenApiMiddleware();
901
+ this.setupMetricsMiddleware();
902
+ }
903
+ /**
904
+ * Set up basic middleware (trust proxy, request ID, context).
905
+ */
906
+ setupBasicMiddleware() {
889
907
  this.app.disable("x-powered-by");
890
908
  if (this.config.trustProxy) {
891
909
  this.app.set("trust proxy", true);
@@ -902,11 +920,15 @@ var ExpressServer = class {
902
920
  this.app.use((_req, res, next) => {
903
921
  if (this.isShuttingDown) {
904
922
  res.setHeader("Connection", "close");
905
- return res.status(httpStatusCodes.HttpStatusCodes.SERVICE_UNAVAILABLE).json(new exception.ServiceUnavailableException("Server is shutting down"));
923
+ res.status(httpStatusCodes.HttpStatusCodes.SERVICE_UNAVAILABLE).json(new exception.ServiceUnavailableException("Server is shutting down"));
906
924
  }
907
925
  next();
908
- return;
909
926
  });
927
+ }
928
+ /**
929
+ * Set up security middleware (Helmet, CORS).
930
+ */
931
+ setupSecurityMiddleware() {
910
932
  if (this.config.helmet) {
911
933
  const helmet = async.optionalRequire("helmet");
912
934
  if (!helmet) {
@@ -925,6 +947,11 @@ var ExpressServer = class {
925
947
  }
926
948
  this.app.use(cors(this.config.cors === true ? {} : this.config.cors));
927
949
  }
950
+ }
951
+ /**
952
+ * Set up global headers middleware.
953
+ */
954
+ setupGlobalHeaders() {
928
955
  this.app.use((_req, res, next) => {
929
956
  if (this.config.globalHeaders) {
930
957
  for (const key in this.config.globalHeaders) {
@@ -941,15 +968,30 @@ var ExpressServer = class {
941
968
  }
942
969
  next();
943
970
  });
971
+ }
972
+ /**
973
+ * Set up request timeout middleware.
974
+ */
975
+ setupTimeoutMiddleware() {
944
976
  if (this.config.requestTimeout) {
945
977
  this.app.use(middleware.timeout(this.config.requestTimeout));
946
978
  }
979
+ }
980
+ /**
981
+ * Set up response time tracking middleware.
982
+ */
983
+ setupResponseTimeMiddleware() {
947
984
  if (this.config.responseTime?.enable) {
948
985
  this.app.use(middleware.responseTime({
949
986
  addHeader: this.config.responseTime.addHeader,
950
987
  logOnComplete: this.config.responseTime.logOnComplete
951
988
  }));
952
989
  }
990
+ }
991
+ /**
992
+ * Set up rate limiting middleware.
993
+ */
994
+ setupRateLimitingMiddleware() {
953
995
  if (this.config.rateLimit?.enable) {
954
996
  const rateLimit = async.optionalRequire("express-rate-limit");
955
997
  if (!rateLimit) {
@@ -967,6 +1009,11 @@ var ExpressServer = class {
967
1009
  legacyHeaders: this.config.rateLimit.legacyHeaders ?? false
968
1010
  }));
969
1011
  }
1012
+ }
1013
+ /**
1014
+ * Set up request logging middleware.
1015
+ */
1016
+ setupRequestLoggingMiddleware() {
970
1017
  if (this.config.requestLogging?.enable) {
971
1018
  this.app.use((req, res, next) => {
972
1019
  if (typeof this.config.requestLogging?.ignorePaths === "function") {
@@ -990,6 +1037,11 @@ var ExpressServer = class {
990
1037
  if (this.hooks.onRequest) {
991
1038
  this.app.use(this.hooks.onRequest);
992
1039
  }
1040
+ }
1041
+ /**
1042
+ * Set up response compression middleware.
1043
+ */
1044
+ setupCompressionMiddleware() {
993
1045
  if (this.config.compression) {
994
1046
  const compression = async.optionalRequire("compression");
995
1047
  if (!compression) {
@@ -1001,6 +1053,11 @@ var ExpressServer = class {
1001
1053
  this.app.use(compression());
1002
1054
  }
1003
1055
  }
1056
+ }
1057
+ /**
1058
+ * Set up static file serving middleware.
1059
+ */
1060
+ setupStaticFilesMiddleware() {
1004
1061
  if (this.config.staticFolders) {
1005
1062
  this.config.staticFolders.forEach((folder) => {
1006
1063
  this.app.use(this.normalizePath(folder.path ?? "/"), express__default.default.static(folder.directory, {
@@ -1013,6 +1070,11 @@ var ExpressServer = class {
1013
1070
  logger.getLogger().info(`Serving static folder: ${folder.directory} at path ${folder.path || "/"}`);
1014
1071
  });
1015
1072
  }
1073
+ }
1074
+ /**
1075
+ * Set up body parsing middleware.
1076
+ */
1077
+ setupBodyParsingMiddleware() {
1016
1078
  if (this.config.bodyParser) {
1017
1079
  if (this.config.bodyParser.json) {
1018
1080
  this.app.use(express__default.default.json(this.config.bodyParser.json));
@@ -1021,6 +1083,11 @@ var ExpressServer = class {
1021
1083
  this.app.use(express__default.default.urlencoded(this.config.bodyParser.urlencoded));
1022
1084
  }
1023
1085
  }
1086
+ }
1087
+ /**
1088
+ * Set up cookie parsing middleware.
1089
+ */
1090
+ setupCookieParsingMiddleware() {
1024
1091
  if (this.config.cookieParser) {
1025
1092
  const cookieParser = async.optionalRequire("cookie-parser");
1026
1093
  if (!cookieParser) {
@@ -1032,6 +1099,11 @@ var ExpressServer = class {
1032
1099
  this.app.use(cookieParser());
1033
1100
  }
1034
1101
  }
1102
+ }
1103
+ /**
1104
+ * Set up OpenAPI documentation middleware.
1105
+ */
1106
+ async setupOpenApiMiddleware() {
1035
1107
  if (this.config.openApi?.enable) {
1036
1108
  try {
1037
1109
  const openApiMountPath = this.normalizePath(this.config.openApi.mountPath ?? "/docs", this.config.openApi.withGlobalPrefix);
@@ -1072,6 +1144,11 @@ var ExpressServer = class {
1072
1144
  if (this.hooks.onResponse) {
1073
1145
  this.app.use(this.globalPrefix, this.hooks.onResponse);
1074
1146
  }
1147
+ }
1148
+ /**
1149
+ * Set up metrics tracking middleware.
1150
+ */
1151
+ setupMetricsMiddleware() {
1075
1152
  if (this.config.metrics?.enable) {
1076
1153
  this.app.use((req, res, next) => {
1077
1154
  const start = process.hrtime();
@@ -1120,45 +1197,7 @@ var ExpressServer = class {
1120
1197
  async setupRoutes() {
1121
1198
  const healthCheckPath = this.normalizePath(this.config.healthCheck?.path || "/healthz", this.config.healthCheck?.withGlobalPrefix);
1122
1199
  this.app.get(healthCheckPath, async (_req, res) => {
1123
- try {
1124
- if (!this.healthChecks.length || config.getCatbeeServerGlobalConfig().skipHealthz) {
1125
- return res.status(httpStatusCodes.HttpStatusCodes.OK).json(new response.SuccessResponse("OK"));
1126
- }
1127
- const checkResults = await Promise.allSettled(this.healthChecks.map(async ({ name, check }) => {
1128
- try {
1129
- const status2 = await Promise.resolve(check());
1130
- return {
1131
- name,
1132
- status: status2,
1133
- error: null
1134
- };
1135
- } catch (error) {
1136
- return {
1137
- name,
1138
- status: false,
1139
- error: error.message
1140
- };
1141
- }
1142
- }));
1143
- const results = checkResults.map((result) => {
1144
- if (result.status === "fulfilled") return result.value;
1145
- return {
1146
- name: "unknown",
1147
- status: false,
1148
- error: result.reason
1149
- };
1150
- });
1151
- const allOk = results.every((r) => r.status);
1152
- const status = allOk ? httpStatusCodes.HttpStatusCodes.OK : httpStatusCodes.HttpStatusCodes.SERVICE_UNAVAILABLE;
1153
- const response$1 = new response.SuccessResponse(allOk ? "OK" : "Service unavailable");
1154
- if (!allOk) response$1.error = true;
1155
- if (this.config.healthCheck?.detailed) response$1.data = {
1156
- checks: results
1157
- };
1158
- return res.status(status).json(response$1);
1159
- } catch {
1160
- return res.status(httpStatusCodes.HttpStatusCodes.INTERNAL_SERVER_ERROR).json(new exception.InternalServerErrorException("Health check failed"));
1161
- }
1200
+ return this.handleHealthCheckRequest(res);
1162
1201
  });
1163
1202
  if (this.config.metrics?.enable) {
1164
1203
  const metricsPath = this.normalizePath(this.config.metrics.path ?? "/metrics", this.config.metrics?.withGlobalPrefix);
@@ -1190,6 +1229,56 @@ var ExpressServer = class {
1190
1229
  });
1191
1230
  }
1192
1231
  /**
1232
+ * Execute health check and return response.
1233
+ */
1234
+ async handleHealthCheckRequest(res) {
1235
+ try {
1236
+ if (!this.healthChecks.length || config.getCatbeeServerGlobalConfig().skipHealthzChecksValidation) {
1237
+ return res.status(httpStatusCodes.HttpStatusCodes.OK).json(new response.SuccessResponse("OK"));
1238
+ }
1239
+ const results = await this.executeHealthChecks();
1240
+ const allOk = results.every((r) => r.status);
1241
+ const status = allOk ? httpStatusCodes.HttpStatusCodes.OK : httpStatusCodes.HttpStatusCodes.SERVICE_UNAVAILABLE;
1242
+ const response$1 = new response.SuccessResponse(allOk ? "OK" : "Service unavailable");
1243
+ if (!allOk) response$1.error = true;
1244
+ if (this.config.healthCheck?.detailed) response$1.data = {
1245
+ checks: results
1246
+ };
1247
+ return res.status(status).json(response$1);
1248
+ } catch {
1249
+ return res.status(httpStatusCodes.HttpStatusCodes.INTERNAL_SERVER_ERROR).json(new exception.InternalServerErrorException("Health check failed"));
1250
+ }
1251
+ }
1252
+ /**
1253
+ * Execute all registered health checks and return results.
1254
+ */
1255
+ async executeHealthChecks() {
1256
+ const checkResults = await Promise.allSettled(this.healthChecks.map(async ({ name, check }) => {
1257
+ try {
1258
+ const status = await Promise.resolve(check());
1259
+ return {
1260
+ name,
1261
+ status,
1262
+ error: null
1263
+ };
1264
+ } catch (error) {
1265
+ return {
1266
+ name,
1267
+ status: false,
1268
+ error: error.message
1269
+ };
1270
+ }
1271
+ }));
1272
+ return checkResults.map((result) => {
1273
+ if (result.status === "fulfilled") return result.value;
1274
+ return {
1275
+ name: "unknown",
1276
+ status: false,
1277
+ error: result.reason
1278
+ };
1279
+ });
1280
+ }
1281
+ /**
1193
1282
  * Register a new health check function for monitoring service dependencies.
1194
1283
  *
1195
1284
  * Health checks are executed when the health endpoint is accessed and
@@ -1220,28 +1309,8 @@ var ExpressServer = class {
1220
1309
  */
1221
1310
  async ready() {
1222
1311
  try {
1223
- if (!this.healthChecks.length || config.getCatbeeServerGlobalConfig().skipHealthz) return true;
1224
- const checkResults = await Promise.allSettled(this.healthChecks.map(async ({ name, check }) => {
1225
- try {
1226
- const status = await Promise.resolve(check());
1227
- return {
1228
- name,
1229
- status,
1230
- error: null
1231
- };
1232
- } catch (error) {
1233
- return {
1234
- name,
1235
- status: false,
1236
- error: error.message
1237
- };
1238
- }
1239
- }));
1240
- const results = checkResults.map((result) => result.status === "fulfilled" ? result.value : {
1241
- name: "unknown",
1242
- status: false,
1243
- error: result.reason
1244
- });
1312
+ if (!this.healthChecks.length || config.getCatbeeServerGlobalConfig().skipHealthzChecksValidation) return true;
1313
+ const results = await this.executeHealthChecks();
1245
1314
  return results.every((r) => r.status === true);
1246
1315
  } catch (err) {
1247
1316
  logger.getLogger().error({
@@ -1286,58 +1355,82 @@ var ExpressServer = class {
1286
1355
  await this.runHook("beforeStart", this.app);
1287
1356
  return new Promise((resolve, reject) => {
1288
1357
  try {
1289
- const listenArgs = [
1290
- this.config.port,
1291
- this.config.host,
1292
- async () => {
1293
- const protocol = this.config.https ? "https" : "http";
1294
- const url = `${protocol}://${this.config.host}:${this.config.port}`;
1295
- logger.getLogger().info(`Server running on ${url}`);
1296
- if (this.config.healthCheck?.path) {
1297
- logger.getLogger().info(`Health check available at ${url}${this.normalizePath(this.config.healthCheck.path, this.config.healthCheck.withGlobalPrefix)}`);
1298
- }
1299
- if (this.config.metrics?.enable && this.config.metrics.path) {
1300
- logger.getLogger().info(`Metrics available at ${url}${this.normalizePath(this.config.metrics.path, this.config.metrics.withGlobalPrefix)}`);
1301
- }
1302
- if (this.config.openApi?.enable) {
1303
- logger.getLogger().info(`API docs available at ${url}${this.normalizePath(this.config.openApi.mountPath, this.config.openApi.withGlobalPrefix)}`);
1304
- }
1305
- if (this.server) await this.runHook("afterStart", this.server);
1306
- resolve(this.server);
1307
- }
1308
- ];
1309
- if (this.config.https) {
1310
- const httpsOptions = {
1311
- ...this.config.https,
1312
- key: fs.readFileSync(this.config.https.key),
1313
- cert: fs.readFileSync(this.config.https.cert)
1314
- };
1315
- if (this.config.https.ca) {
1316
- httpsOptions.ca = fs.readFileSync(this.config.https.ca);
1317
- }
1318
- if (this.config.https.passphrase) {
1319
- httpsOptions.passphrase = this.config.https.passphrase;
1320
- }
1321
- this.server = https__default.default.createServer(httpsOptions, this.app).listen(...listenArgs);
1322
- } else {
1323
- this.server = this.app.listen(...listenArgs);
1324
- }
1325
- this.server.on("connection", (conn) => {
1326
- this.connections.add(conn);
1327
- conn.on("close", () => this.connections.delete(conn));
1328
- });
1329
- this.server.on("error", (err) => {
1330
- logger.getLogger().error({
1331
- err
1332
- }, "Server failed to start");
1333
- reject(err);
1334
- });
1358
+ const onListening = /* @__PURE__ */ __name(async () => {
1359
+ this.logServerStartInfo();
1360
+ if (this.server) await this.runHook("afterStart", this.server);
1361
+ resolve(this.server);
1362
+ }, "onListening");
1363
+ this.server = this.createServerInstance(onListening);
1364
+ this.setupConnectionTracking();
1365
+ this.setupServerErrorHandling(reject);
1335
1366
  } catch (error) {
1336
1367
  reject(error);
1337
1368
  }
1338
1369
  });
1339
1370
  }
1340
1371
  /**
1372
+ * Create HTTP or HTTPS server instance.
1373
+ */
1374
+ createServerInstance(onListening) {
1375
+ const listenArgs = [
1376
+ this.config.port,
1377
+ this.config.host,
1378
+ onListening
1379
+ ];
1380
+ if (this.config.https) {
1381
+ const httpsOptions = {
1382
+ ...this.config.https,
1383
+ key: fs.readFileSync(this.config.https.key),
1384
+ cert: fs.readFileSync(this.config.https.cert)
1385
+ };
1386
+ if (this.config.https.ca) {
1387
+ httpsOptions.ca = fs.readFileSync(this.config.https.ca);
1388
+ }
1389
+ if (this.config.https.passphrase) {
1390
+ httpsOptions.passphrase = this.config.https.passphrase;
1391
+ }
1392
+ return https__default.default.createServer(httpsOptions, this.app).listen(...listenArgs);
1393
+ }
1394
+ return this.app.listen(...listenArgs);
1395
+ }
1396
+ /**
1397
+ * Set up connection tracking for graceful shutdown.
1398
+ */
1399
+ setupConnectionTracking() {
1400
+ this.server.on("connection", (conn) => {
1401
+ this.connections.add(conn);
1402
+ conn.on("close", () => this.connections.delete(conn));
1403
+ });
1404
+ }
1405
+ /**
1406
+ * Set up error handling for server startup.
1407
+ */
1408
+ setupServerErrorHandling(reject) {
1409
+ this.server.on("error", (err) => {
1410
+ logger.getLogger().error({
1411
+ err
1412
+ }, "Server failed to start");
1413
+ reject(err);
1414
+ });
1415
+ }
1416
+ /**
1417
+ * Log server startup information.
1418
+ */
1419
+ logServerStartInfo() {
1420
+ const protocol = this.config.https ? "https" : "http";
1421
+ const url = `${protocol}://${this.config.host}:${this.config.port}`;
1422
+ logger.getLogger().info(`Server running on ${url}`);
1423
+ if (this.config.healthCheck?.path) {
1424
+ logger.getLogger().info(`Health check available at ${url}${this.normalizePath(this.config.healthCheck.path, this.config.healthCheck.withGlobalPrefix)}`);
1425
+ }
1426
+ if (this.config.metrics?.enable && this.config.metrics.path) {
1427
+ logger.getLogger().info(`Metrics available at ${url}${this.normalizePath(this.config.metrics.path, this.config.metrics.withGlobalPrefix)}`);
1428
+ }
1429
+ if (this.config.openApi?.enable) {
1430
+ logger.getLogger().info(`API docs available at ${url}${this.normalizePath(this.config.openApi.mountPath, this.config.openApi.withGlobalPrefix)}`);
1431
+ }
1432
+ }
1433
+ /**
1341
1434
  * Stop the HTTP server gracefully.
1342
1435
  *
1343
1436
  * This method:
@@ -1364,6 +1457,23 @@ var ExpressServer = class {
1364
1457
  }
1365
1458
  this.isShuttingDown = true;
1366
1459
  await this.runHook("beforeStop", this.server);
1460
+ try {
1461
+ await this.gracefulShutdown();
1462
+ } catch (err) {
1463
+ logger.getLogger().error({
1464
+ err
1465
+ }, "Graceful shutdown timed out");
1466
+ if (force) {
1467
+ logger.getLogger().warn("Forcing connection destroy due to shutdown timeout");
1468
+ }
1469
+ } finally {
1470
+ await this.destroyConnections();
1471
+ }
1472
+ }
1473
+ /**
1474
+ * Perform graceful server shutdown with timeout.
1475
+ */
1476
+ async gracefulShutdown() {
1367
1477
  const shutdownTimeout = 1e4;
1368
1478
  const serverClosePromise = new Promise((resolve, reject) => {
1369
1479
  this.server.close(async (err) => {
@@ -1382,21 +1492,10 @@ var ExpressServer = class {
1382
1492
  });
1383
1493
  });
1384
1494
  const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error("Shutdown timeout")), shutdownTimeout));
1385
- try {
1386
- await Promise.race([
1387
- serverClosePromise,
1388
- timeoutPromise
1389
- ]);
1390
- } catch (err) {
1391
- logger.getLogger().error({
1392
- err
1393
- }, "Graceful shutdown timed out");
1394
- if (force) {
1395
- logger.getLogger().warn("Forcing connection destroy due to shutdown timeout");
1396
- }
1397
- } finally {
1398
- await this.destroyConnections();
1399
- }
1495
+ await Promise.race([
1496
+ serverClosePromise,
1497
+ timeoutPromise
1498
+ ]);
1400
1499
  }
1401
1500
  /**
1402
1501
  * Enable graceful shutdown on OS signals for production deployment.
package/server/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * The MIT License
3
3
  *
4
- * Copyright (c) 2025 Catbee Technologies. https://catbee.in/license
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
@@ -138,6 +138,58 @@ declare class ExpressServer {
138
138
  * 14. Custom response hooks
139
139
  */
140
140
  protected setupMiddleware(): Promise<void>;
141
+ /**
142
+ * Set up basic middleware (trust proxy, request ID, context).
143
+ */
144
+ private setupBasicMiddleware;
145
+ /**
146
+ * Set up security middleware (Helmet, CORS).
147
+ */
148
+ private setupSecurityMiddleware;
149
+ /**
150
+ * Set up global headers middleware.
151
+ */
152
+ private setupGlobalHeaders;
153
+ /**
154
+ * Set up request timeout middleware.
155
+ */
156
+ private setupTimeoutMiddleware;
157
+ /**
158
+ * Set up response time tracking middleware.
159
+ */
160
+ private setupResponseTimeMiddleware;
161
+ /**
162
+ * Set up rate limiting middleware.
163
+ */
164
+ private setupRateLimitingMiddleware;
165
+ /**
166
+ * Set up request logging middleware.
167
+ */
168
+ private setupRequestLoggingMiddleware;
169
+ /**
170
+ * Set up response compression middleware.
171
+ */
172
+ private setupCompressionMiddleware;
173
+ /**
174
+ * Set up static file serving middleware.
175
+ */
176
+ private setupStaticFilesMiddleware;
177
+ /**
178
+ * Set up body parsing middleware.
179
+ */
180
+ private setupBodyParsingMiddleware;
181
+ /**
182
+ * Set up cookie parsing middleware.
183
+ */
184
+ private setupCookieParsingMiddleware;
185
+ /**
186
+ * Set up OpenAPI documentation middleware.
187
+ */
188
+ private setupOpenApiMiddleware;
189
+ /**
190
+ * Set up metrics tracking middleware.
191
+ */
192
+ private setupMetricsMiddleware;
141
193
  /**
142
194
  * Configure server routes and error handling.
143
195
  * Sets up in following order:
@@ -148,6 +200,14 @@ declare class ExpressServer {
148
200
  * 4. Error handler
149
201
  */
150
202
  protected setupRoutes(): Promise<void>;
203
+ /**
204
+ * Execute health check and return response.
205
+ */
206
+ private handleHealthCheckRequest;
207
+ /**
208
+ * Execute all registered health checks and return results.
209
+ */
210
+ private executeHealthChecks;
151
211
  /**
152
212
  * Register a new health check function for monitoring service dependencies.
153
213
  *
@@ -200,6 +260,22 @@ declare class ExpressServer {
200
260
  * @throws Error if server fails to start or port is already in use
201
261
  */
202
262
  start(): Promise<http.Server | https.Server>;
263
+ /**
264
+ * Create HTTP or HTTPS server instance.
265
+ */
266
+ private createServerInstance;
267
+ /**
268
+ * Set up connection tracking for graceful shutdown.
269
+ */
270
+ private setupConnectionTracking;
271
+ /**
272
+ * Set up error handling for server startup.
273
+ */
274
+ private setupServerErrorHandling;
275
+ /**
276
+ * Log server startup information.
277
+ */
278
+ private logServerStartInfo;
203
279
  /**
204
280
  * Stop the HTTP server gracefully.
205
281
  *
@@ -217,6 +293,10 @@ declare class ExpressServer {
217
293
  * - Monitoring systems are notified
218
294
  */
219
295
  stop(force?: boolean): Promise<void>;
296
+ /**
297
+ * Perform graceful server shutdown with timeout.
298
+ */
299
+ private gracefulShutdown;
220
300
  /**
221
301
  * Enable graceful shutdown on OS signals for production deployment.
222
302
  *