@catbee/utils 2.0.0-next.0 → 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 (122) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +102 -52
  3. package/array/index.cjs +215 -74
  4. package/array/index.d.ts +345 -2
  5. package/array/index.mjs +201 -74
  6. package/async/index.cjs +116 -39
  7. package/async/index.d.ts +292 -2
  8. package/async/index.mjs +116 -40
  9. package/cache/index.cjs +2 -2
  10. package/cache/index.d.ts +156 -2
  11. package/cache/index.mjs +3 -3
  12. package/config/index.cjs +80 -66
  13. package/config/index.d.ts +65 -3
  14. package/config/index.mjs +77 -65
  15. package/context-store/index.cjs +2 -3
  16. package/context-store/index.d.ts +193 -2
  17. package/context-store/index.mjs +2 -3
  18. package/crypto/index.cjs +55 -5
  19. package/crypto/index.d.ts +225 -2
  20. package/crypto/index.mjs +52 -7
  21. package/date/index.cjs +676 -2
  22. package/date/index.d.ts +676 -2
  23. package/date/index.mjs +665 -3
  24. package/decorator/index.cjs +2172 -0
  25. package/{decorators/decorators.utils.d.ts → decorator/index.d.ts} +58 -54
  26. package/decorator/index.mjs +2131 -0
  27. package/{dir → directory}/index.cjs +5 -4
  28. package/{dir/dir.utils.d.ts → directory/index.d.ts} +24 -21
  29. package/{dir → directory}/index.mjs +5 -4
  30. package/env/index.cjs +100 -68
  31. package/env/index.d.ts +391 -2
  32. package/env/index.mjs +100 -68
  33. package/exception/index.cjs +1 -1
  34. package/exception/index.d.ts +233 -2
  35. package/exception/index.mjs +1 -1
  36. package/fs/index.cjs +71 -37
  37. package/fs/index.d.ts +206 -2
  38. package/fs/index.mjs +65 -35
  39. package/http-status-codes/index.cjs +1 -1
  40. package/http-status-codes/index.d.ts +268 -2
  41. package/http-status-codes/index.mjs +1 -1
  42. package/id/index.cjs +1 -1
  43. package/id/index.d.ts +38 -2
  44. package/id/index.mjs +1 -1
  45. package/index.cjs +13 -13
  46. package/index.d.ts +5 -5
  47. package/index.mjs +5 -5
  48. package/logger/index.cjs +13 -15
  49. package/logger/index.d.ts +190 -2
  50. package/logger/index.mjs +14 -16
  51. package/middleware/index.cjs +1 -1
  52. package/middleware/index.d.ts +104 -2
  53. package/middleware/index.mjs +1 -1
  54. package/object/index.cjs +379 -0
  55. package/{obj/obj.utils.d.ts → object/index.d.ts} +73 -33
  56. package/object/index.mjs +360 -0
  57. package/package.json +41 -23
  58. package/performance/index.cjs +4 -4
  59. package/performance/index.d.ts +139 -2
  60. package/performance/index.mjs +4 -4
  61. package/request/index.cjs +37 -25
  62. package/request/index.d.ts +242 -3
  63. package/request/index.mjs +37 -25
  64. package/response/index.cjs +1 -1
  65. package/response/index.d.ts +319 -3
  66. package/response/index.mjs +1 -1
  67. package/server/index.cjs +249 -146
  68. package/server/index.d.ts +866 -5
  69. package/server/index.mjs +248 -144
  70. package/stream/index.cjs +1 -1
  71. package/stream/index.d.ts +91 -2
  72. package/stream/index.mjs +1 -1
  73. package/string/index.cjs +34 -1
  74. package/string/index.d.ts +146 -2
  75. package/string/index.mjs +31 -2
  76. package/type/index.cjs +19 -2
  77. package/type/index.d.ts +144 -2
  78. package/type/index.mjs +17 -3
  79. package/types/index.cjs +1 -1
  80. package/types/index.d.ts +775 -5
  81. package/types/index.mjs +1 -1
  82. package/url/index.cjs +63 -5
  83. package/url/index.d.ts +200 -2
  84. package/url/index.mjs +59 -6
  85. package/{validate → validation}/index.cjs +91 -44
  86. package/{validate/validate.utils.d.ts → validation/index.d.ts} +33 -24
  87. package/{validate → validation}/index.mjs +87 -44
  88. package/array/array.utils.d.ts +0 -191
  89. package/async/async.utils.d.ts +0 -296
  90. package/cache/cache.utils.d.ts +0 -176
  91. package/config/config.d.ts +0 -57
  92. package/context-store/context-store.utils.d.ts +0 -212
  93. package/crypto/crypto.utils.d.ts +0 -183
  94. package/date/date.utils.d.ts +0 -190
  95. package/decorators/index.cjs +0 -913
  96. package/decorators/index.d.ts +0 -25
  97. package/decorators/index.mjs +0 -872
  98. package/dir/index.d.ts +0 -25
  99. package/env/env.utils.d.ts +0 -400
  100. package/exception/exception.utils.d.ts +0 -253
  101. package/fs/fs.utils.d.ts +0 -196
  102. package/http-status-codes/http-status-codes.d.ts +0 -289
  103. package/id/id.utils.d.ts +0 -59
  104. package/logger/logger.utils.d.ts +0 -210
  105. package/middleware/middleware.utils.d.ts +0 -123
  106. package/obj/index.cjs +0 -317
  107. package/obj/index.d.ts +0 -25
  108. package/obj/index.mjs +0 -301
  109. package/performance/performance.utils.d.ts +0 -159
  110. package/request/request.utils.d.ts +0 -109
  111. package/response/response.utils.d.ts +0 -186
  112. package/server/server.builder.d.ts +0 -531
  113. package/server/server.d.ts +0 -303
  114. package/stream/stream.utils.d.ts +0 -111
  115. package/string/string.utils.d.ts +0 -124
  116. package/type/type.utils.d.ts +0 -129
  117. package/types/api-response.d.ts +0 -175
  118. package/types/common.d.ts +0 -148
  119. package/types/config.d.ts +0 -88
  120. package/types/server.d.ts +0 -291
  121. package/url/url.utils.d.ts +0 -164
  122. package/validate/index.d.ts +0 -25
package/server/index.mjs 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
@@ -25,17 +25,17 @@
25
25
  import express from 'express';
26
26
  import https from 'https';
27
27
  import { HttpStatusCodes } from '@catbee/utils/http-status-codes';
28
- import { SuccessResponse, createFinalErrorResponse } from '@catbee/utils/response';
28
+ import { createFinalErrorResponse, SuccessResponse } from '@catbee/utils/response';
29
29
  import { requestId, setupRequestContext, timeout, responseTime, errorHandler } from '@catbee/utils/middleware';
30
30
  import { Env } from '@catbee/utils/env';
31
31
  import { getLogger } from '@catbee/utils/logger';
32
32
  import { ServiceUnavailableException, InternalServerErrorException, NotFoundException } from '@catbee/utils/exception';
33
- import fs from 'fs';
34
- import { defaultServerConfig, defaultCatbeeConfig } from '@catbee/utils/config';
35
- import { deepObjMerge } from '@catbee/utils/obj';
36
- import { fileExists } from '@catbee/utils/fs';
37
- import { isPort } from '@catbee/utils/validate';
33
+ import { getCatbeeServerGlobalConfig } from '@catbee/utils/config';
34
+ import { deepObjMerge, deepClone } from '@catbee/utils/object';
35
+ import { fileExists, readFile, readFileSync } from '@catbee/utils/fs';
36
+ import { isPort } from '@catbee/utils/validation';
38
37
  import { optionalRequire } from '@catbee/utils/async';
38
+ import { uuid } from '@catbee/utils/id';
39
39
 
40
40
  var __defProp = Object.defineProperty;
41
41
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
@@ -498,7 +498,10 @@ var ServerConfigBuilder = class {
498
498
  * ```
499
499
  */
500
500
  withBodyParser(opts) {
501
- this.config.bodyParser = deepObjMerge({}, this.config.bodyParser ?? {}, opts);
501
+ this.config.bodyParser = {
502
+ ...this.config.bodyParser,
503
+ ...opts
504
+ };
502
505
  return this;
503
506
  }
504
507
  /**
@@ -641,7 +644,7 @@ var ServerConfigBuilder = class {
641
644
  * ```
642
645
  */
643
646
  build() {
644
- const config = deepObjMerge({}, defaultServerConfig, this.config);
647
+ const config = deepObjMerge({}, getCatbeeServerGlobalConfig(), this.config);
645
648
  if (config.openApi?.enable && !config.openApi.filePath) {
646
649
  throw new Error("OpenAPI is enabled but no filePath is specified");
647
650
  }
@@ -651,7 +654,7 @@ var ServerConfigBuilder = class {
651
654
  });
652
655
  }
653
656
  mergeConfig(key, value) {
654
- const current = typeof this.config[key] === "object" && this.config[key] !== null ? this.config[key] : {};
657
+ const current = this.config[key] && typeof this.config[key] === "object" ? deepClone(this.config[key]) : {};
655
658
  this.config[key] = deepObjMerge({}, current, value);
656
659
  }
657
660
  setEnabled(key, enable, overrides = {}) {
@@ -662,9 +665,11 @@ var ServerConfigBuilder = class {
662
665
  return this;
663
666
  }
664
667
  };
665
-
666
- // src/server/server.ts
667
- var getDependencyErrorMessage = /* @__PURE__ */ __name((packageName, x) => `Missing required dependency ${x ? `for ${x}` : ""}: ${packageName}. Please install it to proceed.`, "getDependencyErrorMessage");
668
+ var getDependencyErrorMessage = /* @__PURE__ */ __name((packageName, context) => {
669
+ const ctxPart = context ? `for ${context}` : "";
670
+ const spacer = ctxPart ? ` ${ctxPart}` : "";
671
+ return `Missing required dependency${spacer}: ${packageName}. Please install it to proceed.`;
672
+ }, "getDependencyErrorMessage");
668
673
  var DependencyErrors = {
669
674
  express: getDependencyErrorMessage("express"),
670
675
  helmet: getDependencyErrorMessage("helmet"),
@@ -729,10 +734,10 @@ var ExpressServer = class {
729
734
  * - Request tracing
730
735
  */
731
736
  constructor(config, hooks = {}) {
732
- if (this.isBuiltServerConfig(config)) {
737
+ if (this.hasBuildMarker(config)) {
733
738
  this.config = config;
734
739
  } else {
735
- this.config = deepObjMerge({}, defaultServerConfig, config);
740
+ this.config = deepObjMerge({}, getCatbeeServerGlobalConfig(), config);
736
741
  }
737
742
  if (!isPort(this.config.port)) {
738
743
  const msg = `Port must be a valid number between 1 and 65535, got: ${this.config.port}`;
@@ -834,7 +839,7 @@ var ExpressServer = class {
834
839
  async runHook(hook, ...args) {
835
840
  try {
836
841
  const fn = this.hooks[hook];
837
- if (fn) await fn.apply(null, args);
842
+ if (fn) await fn(...args);
838
843
  } catch (err) {
839
844
  getLogger().error({
840
845
  err,
@@ -874,6 +879,24 @@ var ExpressServer = class {
874
879
  if (this.config.https) {
875
880
  await this.validateHttpsFiles();
876
881
  }
882
+ this.setupBasicMiddleware();
883
+ this.setupSecurityMiddleware();
884
+ this.setupGlobalHeaders();
885
+ this.setupTimeoutMiddleware();
886
+ this.setupResponseTimeMiddleware();
887
+ this.setupRateLimitingMiddleware();
888
+ this.setupRequestLoggingMiddleware();
889
+ this.setupCompressionMiddleware();
890
+ this.setupStaticFilesMiddleware();
891
+ this.setupBodyParsingMiddleware();
892
+ this.setupCookieParsingMiddleware();
893
+ await this.setupOpenApiMiddleware();
894
+ this.setupMetricsMiddleware();
895
+ }
896
+ /**
897
+ * Set up basic middleware (trust proxy, request ID, context).
898
+ */
899
+ setupBasicMiddleware() {
877
900
  this.app.disable("x-powered-by");
878
901
  if (this.config.trustProxy) {
879
902
  this.app.set("trust proxy", true);
@@ -881,7 +904,7 @@ var ExpressServer = class {
881
904
  this.app.use(requestId({
882
905
  headerName: this.config.requestId?.headerName,
883
906
  exposeHeader: this.config.requestId?.exposeHeader,
884
- generator: this.config.requestId?.generator
907
+ generator: this.config.requestId?.generator || uuid
885
908
  }));
886
909
  this.app.use(setupRequestContext({
887
910
  headerName: this.config.requestId?.headerName,
@@ -890,11 +913,15 @@ var ExpressServer = class {
890
913
  this.app.use((_req, res, next) => {
891
914
  if (this.isShuttingDown) {
892
915
  res.setHeader("Connection", "close");
893
- return res.status(HttpStatusCodes.SERVICE_UNAVAILABLE).json(new ServiceUnavailableException("Server is shutting down"));
916
+ res.status(HttpStatusCodes.SERVICE_UNAVAILABLE).json(new ServiceUnavailableException("Server is shutting down"));
894
917
  }
895
918
  next();
896
- return;
897
919
  });
920
+ }
921
+ /**
922
+ * Set up security middleware (Helmet, CORS).
923
+ */
924
+ setupSecurityMiddleware() {
898
925
  if (this.config.helmet) {
899
926
  const helmet = optionalRequire("helmet");
900
927
  if (!helmet) {
@@ -913,6 +940,11 @@ var ExpressServer = class {
913
940
  }
914
941
  this.app.use(cors(this.config.cors === true ? {} : this.config.cors));
915
942
  }
943
+ }
944
+ /**
945
+ * Set up global headers middleware.
946
+ */
947
+ setupGlobalHeaders() {
916
948
  this.app.use((_req, res, next) => {
917
949
  if (this.config.globalHeaders) {
918
950
  for (const key in this.config.globalHeaders) {
@@ -929,15 +961,30 @@ var ExpressServer = class {
929
961
  }
930
962
  next();
931
963
  });
964
+ }
965
+ /**
966
+ * Set up request timeout middleware.
967
+ */
968
+ setupTimeoutMiddleware() {
932
969
  if (this.config.requestTimeout) {
933
970
  this.app.use(timeout(this.config.requestTimeout));
934
971
  }
972
+ }
973
+ /**
974
+ * Set up response time tracking middleware.
975
+ */
976
+ setupResponseTimeMiddleware() {
935
977
  if (this.config.responseTime?.enable) {
936
978
  this.app.use(responseTime({
937
979
  addHeader: this.config.responseTime.addHeader,
938
980
  logOnComplete: this.config.responseTime.logOnComplete
939
981
  }));
940
982
  }
983
+ }
984
+ /**
985
+ * Set up rate limiting middleware.
986
+ */
987
+ setupRateLimitingMiddleware() {
941
988
  if (this.config.rateLimit?.enable) {
942
989
  const rateLimit = optionalRequire("express-rate-limit");
943
990
  if (!rateLimit) {
@@ -955,6 +1002,11 @@ var ExpressServer = class {
955
1002
  legacyHeaders: this.config.rateLimit.legacyHeaders ?? false
956
1003
  }));
957
1004
  }
1005
+ }
1006
+ /**
1007
+ * Set up request logging middleware.
1008
+ */
1009
+ setupRequestLoggingMiddleware() {
958
1010
  if (this.config.requestLogging?.enable) {
959
1011
  this.app.use((req, res, next) => {
960
1012
  if (typeof this.config.requestLogging?.ignorePaths === "function") {
@@ -978,6 +1030,11 @@ var ExpressServer = class {
978
1030
  if (this.hooks.onRequest) {
979
1031
  this.app.use(this.hooks.onRequest);
980
1032
  }
1033
+ }
1034
+ /**
1035
+ * Set up response compression middleware.
1036
+ */
1037
+ setupCompressionMiddleware() {
981
1038
  if (this.config.compression) {
982
1039
  const compression = optionalRequire("compression");
983
1040
  if (!compression) {
@@ -989,6 +1046,11 @@ var ExpressServer = class {
989
1046
  this.app.use(compression());
990
1047
  }
991
1048
  }
1049
+ }
1050
+ /**
1051
+ * Set up static file serving middleware.
1052
+ */
1053
+ setupStaticFilesMiddleware() {
992
1054
  if (this.config.staticFolders) {
993
1055
  this.config.staticFolders.forEach((folder) => {
994
1056
  this.app.use(this.normalizePath(folder.path ?? "/"), express.static(folder.directory, {
@@ -1001,6 +1063,11 @@ var ExpressServer = class {
1001
1063
  getLogger().info(`Serving static folder: ${folder.directory} at path ${folder.path || "/"}`);
1002
1064
  });
1003
1065
  }
1066
+ }
1067
+ /**
1068
+ * Set up body parsing middleware.
1069
+ */
1070
+ setupBodyParsingMiddleware() {
1004
1071
  if (this.config.bodyParser) {
1005
1072
  if (this.config.bodyParser.json) {
1006
1073
  this.app.use(express.json(this.config.bodyParser.json));
@@ -1009,6 +1076,11 @@ var ExpressServer = class {
1009
1076
  this.app.use(express.urlencoded(this.config.bodyParser.urlencoded));
1010
1077
  }
1011
1078
  }
1079
+ }
1080
+ /**
1081
+ * Set up cookie parsing middleware.
1082
+ */
1083
+ setupCookieParsingMiddleware() {
1012
1084
  if (this.config.cookieParser) {
1013
1085
  const cookieParser = optionalRequire("cookie-parser");
1014
1086
  if (!cookieParser) {
@@ -1020,6 +1092,11 @@ var ExpressServer = class {
1020
1092
  this.app.use(cookieParser());
1021
1093
  }
1022
1094
  }
1095
+ }
1096
+ /**
1097
+ * Set up OpenAPI documentation middleware.
1098
+ */
1099
+ async setupOpenApiMiddleware() {
1023
1100
  if (this.config.openApi?.enable) {
1024
1101
  try {
1025
1102
  const openApiMountPath = this.normalizePath(this.config.openApi.mountPath ?? "/docs", this.config.openApi.withGlobalPrefix);
@@ -1045,7 +1122,7 @@ var ExpressServer = class {
1045
1122
  }
1046
1123
  this.app.use(openApiMountPath, apiReference({
1047
1124
  spec: {
1048
- content: await fs.promises.readFile(openApiFilePath, "utf8")
1125
+ content: await readFile(openApiFilePath, "utf8")
1049
1126
  }
1050
1127
  }));
1051
1128
  if (this.config.openApi?.verbose) {
@@ -1060,6 +1137,11 @@ var ExpressServer = class {
1060
1137
  if (this.hooks.onResponse) {
1061
1138
  this.app.use(this.globalPrefix, this.hooks.onResponse);
1062
1139
  }
1140
+ }
1141
+ /**
1142
+ * Set up metrics tracking middleware.
1143
+ */
1144
+ setupMetricsMiddleware() {
1063
1145
  if (this.config.metrics?.enable) {
1064
1146
  this.app.use((req, res, next) => {
1065
1147
  const start = process.hrtime();
@@ -1108,45 +1190,7 @@ var ExpressServer = class {
1108
1190
  async setupRoutes() {
1109
1191
  const healthCheckPath = this.normalizePath(this.config.healthCheck?.path || "/healthz", this.config.healthCheck?.withGlobalPrefix);
1110
1192
  this.app.get(healthCheckPath, async (_req, res) => {
1111
- try {
1112
- if (!this.healthChecks.length || defaultCatbeeConfig.server.skipHealthz) {
1113
- return res.status(HttpStatusCodes.OK).json(new SuccessResponse("OK"));
1114
- }
1115
- const checkResults = await Promise.allSettled(this.healthChecks.map(async ({ name, check }) => {
1116
- try {
1117
- const status2 = await Promise.resolve(check());
1118
- return {
1119
- name,
1120
- status: status2,
1121
- error: null
1122
- };
1123
- } catch (error) {
1124
- return {
1125
- name,
1126
- status: false,
1127
- error: error.message
1128
- };
1129
- }
1130
- }));
1131
- const results = checkResults.map((result) => {
1132
- if (result.status === "fulfilled") return result.value;
1133
- return {
1134
- name: "unknown",
1135
- status: false,
1136
- error: result.reason
1137
- };
1138
- });
1139
- const allOk = results.every((r) => r.status);
1140
- const status = allOk ? HttpStatusCodes.OK : HttpStatusCodes.SERVICE_UNAVAILABLE;
1141
- const response = new SuccessResponse(allOk ? "OK" : "Service unavailable");
1142
- if (!allOk) response.error = true;
1143
- if (this.config.healthCheck?.detailed) response.data = {
1144
- checks: results
1145
- };
1146
- return res.status(status).json(response);
1147
- } catch {
1148
- return res.status(HttpStatusCodes.INTERNAL_SERVER_ERROR).json(new InternalServerErrorException("Health check failed"));
1149
- }
1193
+ return this.handleHealthCheckRequest(res);
1150
1194
  });
1151
1195
  if (this.config.metrics?.enable) {
1152
1196
  const metricsPath = this.normalizePath(this.config.metrics.path ?? "/metrics", this.config.metrics?.withGlobalPrefix);
@@ -1178,6 +1222,56 @@ var ExpressServer = class {
1178
1222
  });
1179
1223
  }
1180
1224
  /**
1225
+ * Execute health check and return response.
1226
+ */
1227
+ async handleHealthCheckRequest(res) {
1228
+ try {
1229
+ if (!this.healthChecks.length || getCatbeeServerGlobalConfig().skipHealthzChecksValidation) {
1230
+ return res.status(HttpStatusCodes.OK).json(new SuccessResponse("OK"));
1231
+ }
1232
+ const results = await this.executeHealthChecks();
1233
+ const allOk = results.every((r) => r.status);
1234
+ const status = allOk ? HttpStatusCodes.OK : HttpStatusCodes.SERVICE_UNAVAILABLE;
1235
+ const response = new SuccessResponse(allOk ? "OK" : "Service unavailable");
1236
+ if (!allOk) response.error = true;
1237
+ if (this.config.healthCheck?.detailed) response.data = {
1238
+ checks: results
1239
+ };
1240
+ return res.status(status).json(response);
1241
+ } catch {
1242
+ return res.status(HttpStatusCodes.INTERNAL_SERVER_ERROR).json(new InternalServerErrorException("Health check failed"));
1243
+ }
1244
+ }
1245
+ /**
1246
+ * Execute all registered health checks and return results.
1247
+ */
1248
+ async executeHealthChecks() {
1249
+ const checkResults = await Promise.allSettled(this.healthChecks.map(async ({ name, check }) => {
1250
+ try {
1251
+ const status = await Promise.resolve(check());
1252
+ return {
1253
+ name,
1254
+ status,
1255
+ error: null
1256
+ };
1257
+ } catch (error) {
1258
+ return {
1259
+ name,
1260
+ status: false,
1261
+ error: error.message
1262
+ };
1263
+ }
1264
+ }));
1265
+ return checkResults.map((result) => {
1266
+ if (result.status === "fulfilled") return result.value;
1267
+ return {
1268
+ name: "unknown",
1269
+ status: false,
1270
+ error: result.reason
1271
+ };
1272
+ });
1273
+ }
1274
+ /**
1181
1275
  * Register a new health check function for monitoring service dependencies.
1182
1276
  *
1183
1277
  * Health checks are executed when the health endpoint is accessed and
@@ -1208,28 +1302,8 @@ var ExpressServer = class {
1208
1302
  */
1209
1303
  async ready() {
1210
1304
  try {
1211
- if (!this.healthChecks.length || defaultCatbeeConfig.server.skipHealthz) return true;
1212
- const checkResults = await Promise.allSettled(this.healthChecks.map(async ({ name, check }) => {
1213
- try {
1214
- const status = await Promise.resolve(check());
1215
- return {
1216
- name,
1217
- status,
1218
- error: null
1219
- };
1220
- } catch (error) {
1221
- return {
1222
- name,
1223
- status: false,
1224
- error: error.message
1225
- };
1226
- }
1227
- }));
1228
- const results = checkResults.map((result) => result.status === "fulfilled" ? result.value : {
1229
- name: "unknown",
1230
- status: false,
1231
- error: result.reason
1232
- });
1305
+ if (!this.healthChecks.length || getCatbeeServerGlobalConfig().skipHealthzChecksValidation) return true;
1306
+ const results = await this.executeHealthChecks();
1233
1307
  return results.every((r) => r.status === true);
1234
1308
  } catch (err) {
1235
1309
  getLogger().error({
@@ -1274,58 +1348,82 @@ var ExpressServer = class {
1274
1348
  await this.runHook("beforeStart", this.app);
1275
1349
  return new Promise((resolve, reject) => {
1276
1350
  try {
1277
- const listenArgs = [
1278
- this.config.port,
1279
- this.config.host,
1280
- async () => {
1281
- const protocol = this.config.https ? "https" : "http";
1282
- const url = `${protocol}://${this.config.host}:${this.config.port}`;
1283
- getLogger().info(`Server running on ${url}`);
1284
- if (this.config.healthCheck?.path) {
1285
- getLogger().info(`Health check available at ${url}${this.normalizePath(this.config.healthCheck.path, this.config.healthCheck.withGlobalPrefix)}`);
1286
- }
1287
- if (this.config.metrics?.enable && this.config.metrics.path) {
1288
- getLogger().info(`Metrics available at ${url}${this.normalizePath(this.config.metrics.path, this.config.metrics.withGlobalPrefix)}`);
1289
- }
1290
- if (this.config.openApi?.enable) {
1291
- getLogger().info(`API docs available at ${url}${this.normalizePath(this.config.openApi.mountPath, this.config.openApi.withGlobalPrefix)}`);
1292
- }
1293
- if (this.server) await this.runHook("afterStart", this.server);
1294
- resolve(this.server);
1295
- }
1296
- ];
1297
- if (this.config.https) {
1298
- const httpsOptions = {
1299
- ...this.config.https,
1300
- key: fs.readFileSync(this.config.https.key),
1301
- cert: fs.readFileSync(this.config.https.cert)
1302
- };
1303
- if (this.config.https.ca) {
1304
- httpsOptions.ca = fs.readFileSync(this.config.https.ca);
1305
- }
1306
- if (this.config.https.passphrase) {
1307
- httpsOptions.passphrase = this.config.https.passphrase;
1308
- }
1309
- this.server = https.createServer(httpsOptions, this.app).listen(...listenArgs);
1310
- } else {
1311
- this.server = this.app.listen(...listenArgs);
1312
- }
1313
- this.server.on("connection", (conn) => {
1314
- this.connections.add(conn);
1315
- conn.on("close", () => this.connections.delete(conn));
1316
- });
1317
- this.server.on("error", (err) => {
1318
- getLogger().error({
1319
- err
1320
- }, "Server failed to start");
1321
- reject(err);
1322
- });
1351
+ const onListening = /* @__PURE__ */ __name(async () => {
1352
+ this.logServerStartInfo();
1353
+ if (this.server) await this.runHook("afterStart", this.server);
1354
+ resolve(this.server);
1355
+ }, "onListening");
1356
+ this.server = this.createServerInstance(onListening);
1357
+ this.setupConnectionTracking();
1358
+ this.setupServerErrorHandling(reject);
1323
1359
  } catch (error) {
1324
1360
  reject(error);
1325
1361
  }
1326
1362
  });
1327
1363
  }
1328
1364
  /**
1365
+ * Create HTTP or HTTPS server instance.
1366
+ */
1367
+ createServerInstance(onListening) {
1368
+ const listenArgs = [
1369
+ this.config.port,
1370
+ this.config.host,
1371
+ onListening
1372
+ ];
1373
+ if (this.config.https) {
1374
+ const httpsOptions = {
1375
+ ...this.config.https,
1376
+ key: readFileSync(this.config.https.key),
1377
+ cert: readFileSync(this.config.https.cert)
1378
+ };
1379
+ if (this.config.https.ca) {
1380
+ httpsOptions.ca = readFileSync(this.config.https.ca);
1381
+ }
1382
+ if (this.config.https.passphrase) {
1383
+ httpsOptions.passphrase = this.config.https.passphrase;
1384
+ }
1385
+ return https.createServer(httpsOptions, this.app).listen(...listenArgs);
1386
+ }
1387
+ return this.app.listen(...listenArgs);
1388
+ }
1389
+ /**
1390
+ * Set up connection tracking for graceful shutdown.
1391
+ */
1392
+ setupConnectionTracking() {
1393
+ this.server.on("connection", (conn) => {
1394
+ this.connections.add(conn);
1395
+ conn.on("close", () => this.connections.delete(conn));
1396
+ });
1397
+ }
1398
+ /**
1399
+ * Set up error handling for server startup.
1400
+ */
1401
+ setupServerErrorHandling(reject) {
1402
+ this.server.on("error", (err) => {
1403
+ getLogger().error({
1404
+ err
1405
+ }, "Server failed to start");
1406
+ reject(err);
1407
+ });
1408
+ }
1409
+ /**
1410
+ * Log server startup information.
1411
+ */
1412
+ logServerStartInfo() {
1413
+ const protocol = this.config.https ? "https" : "http";
1414
+ const url = `${protocol}://${this.config.host}:${this.config.port}`;
1415
+ getLogger().info(`Server running on ${url}`);
1416
+ if (this.config.healthCheck?.path) {
1417
+ getLogger().info(`Health check available at ${url}${this.normalizePath(this.config.healthCheck.path, this.config.healthCheck.withGlobalPrefix)}`);
1418
+ }
1419
+ if (this.config.metrics?.enable && this.config.metrics.path) {
1420
+ getLogger().info(`Metrics available at ${url}${this.normalizePath(this.config.metrics.path, this.config.metrics.withGlobalPrefix)}`);
1421
+ }
1422
+ if (this.config.openApi?.enable) {
1423
+ getLogger().info(`API docs available at ${url}${this.normalizePath(this.config.openApi.mountPath, this.config.openApi.withGlobalPrefix)}`);
1424
+ }
1425
+ }
1426
+ /**
1329
1427
  * Stop the HTTP server gracefully.
1330
1428
  *
1331
1429
  * This method:
@@ -1352,6 +1450,23 @@ var ExpressServer = class {
1352
1450
  }
1353
1451
  this.isShuttingDown = true;
1354
1452
  await this.runHook("beforeStop", this.server);
1453
+ try {
1454
+ await this.gracefulShutdown();
1455
+ } catch (err) {
1456
+ getLogger().error({
1457
+ err
1458
+ }, "Graceful shutdown timed out");
1459
+ if (force) {
1460
+ getLogger().warn("Forcing connection destroy due to shutdown timeout");
1461
+ }
1462
+ } finally {
1463
+ await this.destroyConnections();
1464
+ }
1465
+ }
1466
+ /**
1467
+ * Perform graceful server shutdown with timeout.
1468
+ */
1469
+ async gracefulShutdown() {
1355
1470
  const shutdownTimeout = 1e4;
1356
1471
  const serverClosePromise = new Promise((resolve, reject) => {
1357
1472
  this.server.close(async (err) => {
@@ -1370,21 +1485,10 @@ var ExpressServer = class {
1370
1485
  });
1371
1486
  });
1372
1487
  const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error("Shutdown timeout")), shutdownTimeout));
1373
- try {
1374
- await Promise.race([
1375
- serverClosePromise,
1376
- timeoutPromise
1377
- ]);
1378
- } catch (err) {
1379
- getLogger().error({
1380
- err
1381
- }, "Graceful shutdown timed out");
1382
- if (force) {
1383
- getLogger().warn("Forcing connection destroy due to shutdown timeout");
1384
- }
1385
- } finally {
1386
- await this.destroyConnections();
1387
- }
1488
+ await Promise.race([
1489
+ serverClosePromise,
1490
+ timeoutPromise
1491
+ ]);
1388
1492
  }
1389
1493
  /**
1390
1494
  * Enable graceful shutdown on OS signals for production deployment.
@@ -1526,7 +1630,7 @@ var ExpressServer = class {
1526
1630
  /**
1527
1631
  * Get server configuration
1528
1632
  *
1529
- * @return {*} {ServerConfig}
1633
+ * @return {*} {CatbeeServerConfig}
1530
1634
  */
1531
1635
  getConfig() {
1532
1636
  return this.config;
@@ -1600,7 +1704,7 @@ var ExpressServer = class {
1600
1704
  throw new Error(msg);
1601
1705
  }
1602
1706
  }
1603
- isBuiltServerConfig(config) {
1707
+ hasBuildMarker(config) {
1604
1708
  if (config?.[BUILD_MARKER]) {
1605
1709
  return true;
1606
1710
  }
package/stream/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