@catbee/utils 2.0.0-next.1 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +55 -38
  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 +233 -131
  68. package/server/index.d.ts +81 -1
  69. package/server/index.mjs +229 -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 +10 -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}`;
@@ -860,6 +860,7 @@ var ExpressServer = class {
860
860
  async initialize() {
861
861
  await this.runHook("beforeInit", this);
862
862
  await this.setupMiddleware();
863
+ await this.runHook("beforeRoutes", this.app);
863
864
  await this.setupRoutes();
864
865
  await this.runHook("afterInit", this);
865
866
  }
@@ -886,6 +887,24 @@ var ExpressServer = class {
886
887
  if (this.config.https) {
887
888
  await this.validateHttpsFiles();
888
889
  }
890
+ this.setupBasicMiddleware();
891
+ this.setupSecurityMiddleware();
892
+ this.setupGlobalHeaders();
893
+ this.setupTimeoutMiddleware();
894
+ this.setupResponseTimeMiddleware();
895
+ this.setupRateLimitingMiddleware();
896
+ this.setupRequestLoggingMiddleware();
897
+ this.setupCompressionMiddleware();
898
+ this.setupStaticFilesMiddleware();
899
+ this.setupBodyParsingMiddleware();
900
+ this.setupCookieParsingMiddleware();
901
+ await this.setupOpenApiMiddleware();
902
+ this.setupMetricsMiddleware();
903
+ }
904
+ /**
905
+ * Set up basic middleware (trust proxy, request ID, context).
906
+ */
907
+ setupBasicMiddleware() {
889
908
  this.app.disable("x-powered-by");
890
909
  if (this.config.trustProxy) {
891
910
  this.app.set("trust proxy", true);
@@ -902,11 +921,15 @@ var ExpressServer = class {
902
921
  this.app.use((_req, res, next) => {
903
922
  if (this.isShuttingDown) {
904
923
  res.setHeader("Connection", "close");
905
- return res.status(httpStatusCodes.HttpStatusCodes.SERVICE_UNAVAILABLE).json(new exception.ServiceUnavailableException("Server is shutting down"));
924
+ res.status(httpStatusCodes.HttpStatusCodes.SERVICE_UNAVAILABLE).json(new exception.ServiceUnavailableException("Server is shutting down"));
906
925
  }
907
926
  next();
908
- return;
909
927
  });
928
+ }
929
+ /**
930
+ * Set up security middleware (Helmet, CORS).
931
+ */
932
+ setupSecurityMiddleware() {
910
933
  if (this.config.helmet) {
911
934
  const helmet = async.optionalRequire("helmet");
912
935
  if (!helmet) {
@@ -925,6 +948,11 @@ var ExpressServer = class {
925
948
  }
926
949
  this.app.use(cors(this.config.cors === true ? {} : this.config.cors));
927
950
  }
951
+ }
952
+ /**
953
+ * Set up global headers middleware.
954
+ */
955
+ setupGlobalHeaders() {
928
956
  this.app.use((_req, res, next) => {
929
957
  if (this.config.globalHeaders) {
930
958
  for (const key in this.config.globalHeaders) {
@@ -941,15 +969,30 @@ var ExpressServer = class {
941
969
  }
942
970
  next();
943
971
  });
972
+ }
973
+ /**
974
+ * Set up request timeout middleware.
975
+ */
976
+ setupTimeoutMiddleware() {
944
977
  if (this.config.requestTimeout) {
945
978
  this.app.use(middleware.timeout(this.config.requestTimeout));
946
979
  }
980
+ }
981
+ /**
982
+ * Set up response time tracking middleware.
983
+ */
984
+ setupResponseTimeMiddleware() {
947
985
  if (this.config.responseTime?.enable) {
948
986
  this.app.use(middleware.responseTime({
949
987
  addHeader: this.config.responseTime.addHeader,
950
988
  logOnComplete: this.config.responseTime.logOnComplete
951
989
  }));
952
990
  }
991
+ }
992
+ /**
993
+ * Set up rate limiting middleware.
994
+ */
995
+ setupRateLimitingMiddleware() {
953
996
  if (this.config.rateLimit?.enable) {
954
997
  const rateLimit = async.optionalRequire("express-rate-limit");
955
998
  if (!rateLimit) {
@@ -967,6 +1010,11 @@ var ExpressServer = class {
967
1010
  legacyHeaders: this.config.rateLimit.legacyHeaders ?? false
968
1011
  }));
969
1012
  }
1013
+ }
1014
+ /**
1015
+ * Set up request logging middleware.
1016
+ */
1017
+ setupRequestLoggingMiddleware() {
970
1018
  if (this.config.requestLogging?.enable) {
971
1019
  this.app.use((req, res, next) => {
972
1020
  if (typeof this.config.requestLogging?.ignorePaths === "function") {
@@ -990,6 +1038,11 @@ var ExpressServer = class {
990
1038
  if (this.hooks.onRequest) {
991
1039
  this.app.use(this.hooks.onRequest);
992
1040
  }
1041
+ }
1042
+ /**
1043
+ * Set up response compression middleware.
1044
+ */
1045
+ setupCompressionMiddleware() {
993
1046
  if (this.config.compression) {
994
1047
  const compression = async.optionalRequire("compression");
995
1048
  if (!compression) {
@@ -1001,6 +1054,11 @@ var ExpressServer = class {
1001
1054
  this.app.use(compression());
1002
1055
  }
1003
1056
  }
1057
+ }
1058
+ /**
1059
+ * Set up static file serving middleware.
1060
+ */
1061
+ setupStaticFilesMiddleware() {
1004
1062
  if (this.config.staticFolders) {
1005
1063
  this.config.staticFolders.forEach((folder) => {
1006
1064
  this.app.use(this.normalizePath(folder.path ?? "/"), express__default.default.static(folder.directory, {
@@ -1013,6 +1071,11 @@ var ExpressServer = class {
1013
1071
  logger.getLogger().info(`Serving static folder: ${folder.directory} at path ${folder.path || "/"}`);
1014
1072
  });
1015
1073
  }
1074
+ }
1075
+ /**
1076
+ * Set up body parsing middleware.
1077
+ */
1078
+ setupBodyParsingMiddleware() {
1016
1079
  if (this.config.bodyParser) {
1017
1080
  if (this.config.bodyParser.json) {
1018
1081
  this.app.use(express__default.default.json(this.config.bodyParser.json));
@@ -1021,6 +1084,11 @@ var ExpressServer = class {
1021
1084
  this.app.use(express__default.default.urlencoded(this.config.bodyParser.urlencoded));
1022
1085
  }
1023
1086
  }
1087
+ }
1088
+ /**
1089
+ * Set up cookie parsing middleware.
1090
+ */
1091
+ setupCookieParsingMiddleware() {
1024
1092
  if (this.config.cookieParser) {
1025
1093
  const cookieParser = async.optionalRequire("cookie-parser");
1026
1094
  if (!cookieParser) {
@@ -1032,6 +1100,11 @@ var ExpressServer = class {
1032
1100
  this.app.use(cookieParser());
1033
1101
  }
1034
1102
  }
1103
+ }
1104
+ /**
1105
+ * Set up OpenAPI documentation middleware.
1106
+ */
1107
+ async setupOpenApiMiddleware() {
1035
1108
  if (this.config.openApi?.enable) {
1036
1109
  try {
1037
1110
  const openApiMountPath = this.normalizePath(this.config.openApi.mountPath ?? "/docs", this.config.openApi.withGlobalPrefix);
@@ -1072,6 +1145,11 @@ var ExpressServer = class {
1072
1145
  if (this.hooks.onResponse) {
1073
1146
  this.app.use(this.globalPrefix, this.hooks.onResponse);
1074
1147
  }
1148
+ }
1149
+ /**
1150
+ * Set up metrics tracking middleware.
1151
+ */
1152
+ setupMetricsMiddleware() {
1075
1153
  if (this.config.metrics?.enable) {
1076
1154
  this.app.use((req, res, next) => {
1077
1155
  const start = process.hrtime();
@@ -1120,45 +1198,7 @@ var ExpressServer = class {
1120
1198
  async setupRoutes() {
1121
1199
  const healthCheckPath = this.normalizePath(this.config.healthCheck?.path || "/healthz", this.config.healthCheck?.withGlobalPrefix);
1122
1200
  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
- }
1201
+ return this.handleHealthCheckRequest(res);
1162
1202
  });
1163
1203
  if (this.config.metrics?.enable) {
1164
1204
  const metricsPath = this.normalizePath(this.config.metrics.path ?? "/metrics", this.config.metrics?.withGlobalPrefix);
@@ -1169,6 +1209,7 @@ var ExpressServer = class {
1169
1209
  }
1170
1210
  const routerToUse = this.externalRouter || this.rootRouter;
1171
1211
  this.app.use(this.globalPrefix, routerToUse);
1212
+ await this.runHook("afterRoutes", this.app);
1172
1213
  this.app.use((req, res) => {
1173
1214
  const status = httpStatusCodes.HttpStatusCodes.NOT_FOUND;
1174
1215
  const response$1 = response.createFinalErrorResponse(req, status, `Route ${req.method.toUpperCase()} ${req.path} not found`);
@@ -1190,6 +1231,56 @@ var ExpressServer = class {
1190
1231
  });
1191
1232
  }
1192
1233
  /**
1234
+ * Execute health check and return response.
1235
+ */
1236
+ async handleHealthCheckRequest(res) {
1237
+ try {
1238
+ if (!this.healthChecks.length || config.getCatbeeServerGlobalConfig().skipHealthzChecksValidation) {
1239
+ return res.status(httpStatusCodes.HttpStatusCodes.OK).json(new response.SuccessResponse("OK"));
1240
+ }
1241
+ const results = await this.executeHealthChecks();
1242
+ const allOk = results.every((r) => r.status);
1243
+ const status = allOk ? httpStatusCodes.HttpStatusCodes.OK : httpStatusCodes.HttpStatusCodes.SERVICE_UNAVAILABLE;
1244
+ const response$1 = new response.SuccessResponse(allOk ? "OK" : "Service unavailable");
1245
+ if (!allOk) response$1.error = true;
1246
+ if (this.config.healthCheck?.detailed) response$1.data = {
1247
+ checks: results
1248
+ };
1249
+ return res.status(status).json(response$1);
1250
+ } catch {
1251
+ return res.status(httpStatusCodes.HttpStatusCodes.INTERNAL_SERVER_ERROR).json(new exception.InternalServerErrorException("Health check failed"));
1252
+ }
1253
+ }
1254
+ /**
1255
+ * Execute all registered health checks and return results.
1256
+ */
1257
+ async executeHealthChecks() {
1258
+ const checkResults = await Promise.allSettled(this.healthChecks.map(async ({ name, check }) => {
1259
+ try {
1260
+ const status = await Promise.resolve(check());
1261
+ return {
1262
+ name,
1263
+ status,
1264
+ error: null
1265
+ };
1266
+ } catch (error) {
1267
+ return {
1268
+ name,
1269
+ status: false,
1270
+ error: error.message
1271
+ };
1272
+ }
1273
+ }));
1274
+ return checkResults.map((result) => {
1275
+ if (result.status === "fulfilled") return result.value;
1276
+ return {
1277
+ name: "unknown",
1278
+ status: false,
1279
+ error: result.reason
1280
+ };
1281
+ });
1282
+ }
1283
+ /**
1193
1284
  * Register a new health check function for monitoring service dependencies.
1194
1285
  *
1195
1286
  * Health checks are executed when the health endpoint is accessed and
@@ -1220,28 +1311,8 @@ var ExpressServer = class {
1220
1311
  */
1221
1312
  async ready() {
1222
1313
  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
- });
1314
+ if (!this.healthChecks.length || config.getCatbeeServerGlobalConfig().skipHealthzChecksValidation) return true;
1315
+ const results = await this.executeHealthChecks();
1245
1316
  return results.every((r) => r.status === true);
1246
1317
  } catch (err) {
1247
1318
  logger.getLogger().error({
@@ -1286,58 +1357,83 @@ var ExpressServer = class {
1286
1357
  await this.runHook("beforeStart", this.app);
1287
1358
  return new Promise((resolve, reject) => {
1288
1359
  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
- });
1360
+ const onListening = /* @__PURE__ */ __name(async () => {
1361
+ this.logServerStartInfo();
1362
+ if (this.server) await this.runHook("afterStart", this.server);
1363
+ resolve(this.server);
1364
+ }, "onListening");
1365
+ this.server = this.createServerInstance(onListening);
1366
+ this.runHook("onServerCreated", this.server);
1367
+ this.setupConnectionTracking();
1368
+ this.setupServerErrorHandling(reject);
1335
1369
  } catch (error) {
1336
1370
  reject(error);
1337
1371
  }
1338
1372
  });
1339
1373
  }
1340
1374
  /**
1375
+ * Create HTTP or HTTPS server instance.
1376
+ */
1377
+ createServerInstance(onListening) {
1378
+ const listenArgs = [
1379
+ this.config.port,
1380
+ this.config.host,
1381
+ onListening
1382
+ ];
1383
+ if (this.config.https) {
1384
+ const httpsOptions = {
1385
+ ...this.config.https,
1386
+ key: fs.readFileSync(this.config.https.key),
1387
+ cert: fs.readFileSync(this.config.https.cert)
1388
+ };
1389
+ if (this.config.https.ca) {
1390
+ httpsOptions.ca = fs.readFileSync(this.config.https.ca);
1391
+ }
1392
+ if (this.config.https.passphrase) {
1393
+ httpsOptions.passphrase = this.config.https.passphrase;
1394
+ }
1395
+ return https__default.default.createServer(httpsOptions, this.app).listen(...listenArgs);
1396
+ }
1397
+ return this.app.listen(...listenArgs);
1398
+ }
1399
+ /**
1400
+ * Set up connection tracking for graceful shutdown.
1401
+ */
1402
+ setupConnectionTracking() {
1403
+ this.server.on("connection", (conn) => {
1404
+ this.connections.add(conn);
1405
+ conn.on("close", () => this.connections.delete(conn));
1406
+ });
1407
+ }
1408
+ /**
1409
+ * Set up error handling for server startup.
1410
+ */
1411
+ setupServerErrorHandling(reject) {
1412
+ this.server.on("error", (err) => {
1413
+ logger.getLogger().error({
1414
+ err
1415
+ }, "Server failed to start");
1416
+ reject(err);
1417
+ });
1418
+ }
1419
+ /**
1420
+ * Log server startup information.
1421
+ */
1422
+ logServerStartInfo() {
1423
+ const protocol = this.config.https ? "https" : "http";
1424
+ const url = `${protocol}://${this.config.host}:${this.config.port}`;
1425
+ logger.getLogger().info(`Server running on ${url}`);
1426
+ if (this.config.healthCheck?.path) {
1427
+ logger.getLogger().info(`Health check available at ${url}${this.normalizePath(this.config.healthCheck.path, this.config.healthCheck.withGlobalPrefix)}`);
1428
+ }
1429
+ if (this.config.metrics?.enable && this.config.metrics.path) {
1430
+ logger.getLogger().info(`Metrics available at ${url}${this.normalizePath(this.config.metrics.path, this.config.metrics.withGlobalPrefix)}`);
1431
+ }
1432
+ if (this.config.openApi?.enable) {
1433
+ logger.getLogger().info(`API docs available at ${url}${this.normalizePath(this.config.openApi.mountPath, this.config.openApi.withGlobalPrefix)}`);
1434
+ }
1435
+ }
1436
+ /**
1341
1437
  * Stop the HTTP server gracefully.
1342
1438
  *
1343
1439
  * This method:
@@ -1364,6 +1460,23 @@ var ExpressServer = class {
1364
1460
  }
1365
1461
  this.isShuttingDown = true;
1366
1462
  await this.runHook("beforeStop", this.server);
1463
+ try {
1464
+ await this.gracefulShutdown();
1465
+ } catch (err) {
1466
+ logger.getLogger().error({
1467
+ err
1468
+ }, "Graceful shutdown timed out");
1469
+ if (force) {
1470
+ logger.getLogger().warn("Forcing connection destroy due to shutdown timeout");
1471
+ }
1472
+ } finally {
1473
+ await this.destroyConnections();
1474
+ }
1475
+ }
1476
+ /**
1477
+ * Perform graceful server shutdown with timeout.
1478
+ */
1479
+ async gracefulShutdown() {
1367
1480
  const shutdownTimeout = 1e4;
1368
1481
  const serverClosePromise = new Promise((resolve, reject) => {
1369
1482
  this.server.close(async (err) => {
@@ -1382,21 +1495,10 @@ var ExpressServer = class {
1382
1495
  });
1383
1496
  });
1384
1497
  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
- }
1498
+ await Promise.race([
1499
+ serverClosePromise,
1500
+ timeoutPromise
1501
+ ]);
1400
1502
  }
1401
1503
  /**
1402
1504
  * 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
  *