@catbee/utils 0.0.8-rc.0 → 0.0.8-rc.2

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 (281) hide show
  1. package/README.md +25 -1
  2. package/build/esm/config.d.ts +0 -1
  3. package/build/esm/config.js +6 -7
  4. package/build/esm/index.d.ts +0 -1
  5. package/build/esm/index.js +0 -1
  6. package/build/esm/servers/server.builder.d.ts +0 -1
  7. package/build/esm/servers/server.builder.js +99 -147
  8. package/build/esm/servers/server.d.ts +0 -1
  9. package/build/esm/servers/server.js +591 -876
  10. package/build/esm/types/api-response.d.ts +0 -1
  11. package/build/esm/types/api-response.js +0 -1
  12. package/build/esm/types/index.d.ts +0 -1
  13. package/build/esm/types/index.js +0 -1
  14. package/build/esm/types/server.d.ts +0 -1
  15. package/build/esm/types/server.js +0 -1
  16. package/build/esm/utils/array.utils.d.ts +0 -1
  17. package/build/esm/utils/array.utils.js +48 -86
  18. package/build/esm/utils/async.utils.d.ts +0 -1
  19. package/build/esm/utils/async.utils.js +247 -524
  20. package/build/esm/utils/cache.utils.d.ts +0 -1
  21. package/build/esm/utils/cache.utils.js +96 -301
  22. package/build/esm/utils/context-store.utils.d.ts +0 -1
  23. package/build/esm/utils/context-store.utils.js +62 -79
  24. package/build/esm/utils/crypto.utils.d.ts +0 -1
  25. package/build/esm/utils/crypto.utils.js +78 -175
  26. package/build/esm/utils/date.utils.d.ts +0 -1
  27. package/build/esm/utils/date.utils.js +47 -53
  28. package/build/esm/utils/decorators.utils.d.ts +201 -5
  29. package/build/esm/utils/decorators.utils.js +632 -285
  30. package/build/esm/utils/dir.utils.d.ts +0 -1
  31. package/build/esm/utils/dir.utils.js +264 -786
  32. package/build/esm/utils/env.utils.d.ts +0 -1
  33. package/build/esm/utils/env.utils.js +180 -271
  34. package/build/esm/utils/exception.utils.d.ts +0 -1
  35. package/build/esm/utils/exception.utils.js +112 -289
  36. package/build/esm/utils/fs.utils.d.ts +0 -1
  37. package/build/esm/utils/fs.utils.js +169 -401
  38. package/build/esm/utils/http-status-codes.d.ts +0 -1
  39. package/build/esm/utils/http-status-codes.js +0 -1
  40. package/build/esm/utils/id.utils.d.ts +0 -1
  41. package/build/esm/utils/id.utils.js +5 -9
  42. package/build/esm/utils/logger.utils.d.ts +0 -1
  43. package/build/esm/utils/logger.utils.js +34 -71
  44. package/build/esm/utils/middleware.utils.d.ts +0 -1
  45. package/build/esm/utils/middleware.utils.js +65 -155
  46. package/build/esm/utils/obj.utils.d.ts +0 -1
  47. package/build/esm/utils/obj.utils.js +69 -176
  48. package/build/esm/utils/performance.utils.d.ts +0 -1
  49. package/build/esm/utils/performance.utils.js +92 -195
  50. package/build/esm/utils/request.utils.d.ts +0 -1
  51. package/build/esm/utils/request.utils.js +41 -74
  52. package/build/esm/utils/response.utils.d.ts +0 -1
  53. package/build/esm/utils/response.utils.js +84 -107
  54. package/build/esm/utils/stream.utils.d.ts +0 -1
  55. package/build/esm/utils/stream.utils.js +38 -113
  56. package/build/esm/utils/string.utils.d.ts +0 -1
  57. package/build/esm/utils/string.utils.js +6 -11
  58. package/build/esm/utils/type.utils.d.ts +0 -1
  59. package/build/esm/utils/type.utils.js +8 -12
  60. package/build/esm/utils/url.utils.d.ts +0 -1
  61. package/build/esm/utils/url.utils.js +49 -127
  62. package/build/esm/utils/validate.utils.d.ts +0 -1
  63. package/build/esm/utils/validate.utils.js +23 -40
  64. package/package.json +3 -33
  65. package/build/esm/config.js.map +0 -1
  66. package/build/esm/index.js.map +0 -1
  67. package/build/esm/servers/server.builder.js.map +0 -1
  68. package/build/esm/servers/server.js.map +0 -1
  69. package/build/esm/types/api-response.js.map +0 -1
  70. package/build/esm/types/index.js.map +0 -1
  71. package/build/esm/types/server.js.map +0 -1
  72. package/build/esm/utils/array.utils.js.map +0 -1
  73. package/build/esm/utils/async.utils.js.map +0 -1
  74. package/build/esm/utils/cache.utils.js.map +0 -1
  75. package/build/esm/utils/context-store.utils.js.map +0 -1
  76. package/build/esm/utils/crypto.utils.js.map +0 -1
  77. package/build/esm/utils/date.utils.js.map +0 -1
  78. package/build/esm/utils/decorators.utils.js.map +0 -1
  79. package/build/esm/utils/dir.utils.js.map +0 -1
  80. package/build/esm/utils/env.utils.js.map +0 -1
  81. package/build/esm/utils/exception.utils.js.map +0 -1
  82. package/build/esm/utils/fs.utils.js.map +0 -1
  83. package/build/esm/utils/http-status-codes.js.map +0 -1
  84. package/build/esm/utils/id.utils.js.map +0 -1
  85. package/build/esm/utils/logger.utils.js.map +0 -1
  86. package/build/esm/utils/middleware.utils.js.map +0 -1
  87. package/build/esm/utils/obj.utils.js.map +0 -1
  88. package/build/esm/utils/performance.utils.js.map +0 -1
  89. package/build/esm/utils/request.utils.js.map +0 -1
  90. package/build/esm/utils/response.utils.js.map +0 -1
  91. package/build/esm/utils/stream.utils.js.map +0 -1
  92. package/build/esm/utils/string.utils.js.map +0 -1
  93. package/build/esm/utils/type.utils.js.map +0 -1
  94. package/build/esm/utils/url.utils.js.map +0 -1
  95. package/build/esm/utils/validate.utils.js.map +0 -1
  96. package/build/esnext/config.d.ts +0 -122
  97. package/build/esnext/config.js +0 -133
  98. package/build/esnext/config.js.map +0 -1
  99. package/build/esnext/index.d.ts +0 -27
  100. package/build/esnext/index.js +0 -50
  101. package/build/esnext/index.js.map +0 -1
  102. package/build/esnext/servers/server.builder.d.ts +0 -508
  103. package/build/esnext/servers/server.builder.js +0 -657
  104. package/build/esnext/servers/server.builder.js.map +0 -1
  105. package/build/esnext/servers/server.d.ts +0 -256
  106. package/build/esnext/servers/server.js +0 -955
  107. package/build/esnext/servers/server.js.map +0 -1
  108. package/build/esnext/types/api-response.d.ts +0 -152
  109. package/build/esnext/types/api-response.js +0 -34
  110. package/build/esnext/types/api-response.js.map +0 -1
  111. package/build/esnext/types/index.d.ts +0 -125
  112. package/build/esnext/types/index.js +0 -25
  113. package/build/esnext/types/index.js.map +0 -1
  114. package/build/esnext/types/server.d.ts +0 -268
  115. package/build/esnext/types/server.js +0 -25
  116. package/build/esnext/types/server.js.map +0 -1
  117. package/build/esnext/utils/array.utils.d.ts +0 -168
  118. package/build/esnext/utils/array.utils.js +0 -345
  119. package/build/esnext/utils/array.utils.js.map +0 -1
  120. package/build/esnext/utils/async.utils.d.ts +0 -265
  121. package/build/esnext/utils/async.utils.js +0 -620
  122. package/build/esnext/utils/async.utils.js.map +0 -1
  123. package/build/esnext/utils/cache.utils.d.ts +0 -153
  124. package/build/esnext/utils/cache.utils.js +0 -293
  125. package/build/esnext/utils/cache.utils.js.map +0 -1
  126. package/build/esnext/utils/context-store.utils.d.ts +0 -189
  127. package/build/esnext/utils/context-store.utils.js +0 -289
  128. package/build/esnext/utils/context-store.utils.js.map +0 -1
  129. package/build/esnext/utils/crypto.utils.d.ts +0 -160
  130. package/build/esnext/utils/crypto.utils.js +0 -276
  131. package/build/esnext/utils/crypto.utils.js.map +0 -1
  132. package/build/esnext/utils/date.utils.d.ts +0 -159
  133. package/build/esnext/utils/date.utils.js +0 -384
  134. package/build/esnext/utils/date.utils.js.map +0 -1
  135. package/build/esnext/utils/decorators.utils.d.ts +0 -315
  136. package/build/esnext/utils/decorators.utils.js +0 -504
  137. package/build/esnext/utils/decorators.utils.js.map +0 -1
  138. package/build/esnext/utils/dir.utils.d.ts +0 -196
  139. package/build/esnext/utils/dir.utils.js +0 -477
  140. package/build/esnext/utils/dir.utils.js.map +0 -1
  141. package/build/esnext/utils/env.utils.d.ts +0 -377
  142. package/build/esnext/utils/env.utils.js +0 -785
  143. package/build/esnext/utils/env.utils.js.map +0 -1
  144. package/build/esnext/utils/exception.utils.d.ts +0 -230
  145. package/build/esnext/utils/exception.utils.js +0 -381
  146. package/build/esnext/utils/exception.utils.js.map +0 -1
  147. package/build/esnext/utils/fs.utils.d.ts +0 -164
  148. package/build/esnext/utils/fs.utils.js +0 -349
  149. package/build/esnext/utils/fs.utils.js.map +0 -1
  150. package/build/esnext/utils/http-status-codes.d.ts +0 -266
  151. package/build/esnext/utils/http-status-codes.js +0 -295
  152. package/build/esnext/utils/http-status-codes.js.map +0 -1
  153. package/build/esnext/utils/id.utils.d.ts +0 -36
  154. package/build/esnext/utils/id.utils.js +0 -84
  155. package/build/esnext/utils/id.utils.js.map +0 -1
  156. package/build/esnext/utils/logger.utils.d.ts +0 -160
  157. package/build/esnext/utils/logger.utils.js +0 -300
  158. package/build/esnext/utils/logger.utils.js.map +0 -1
  159. package/build/esnext/utils/middleware.utils.d.ts +0 -100
  160. package/build/esnext/utils/middleware.utils.js +0 -236
  161. package/build/esnext/utils/middleware.utils.js.map +0 -1
  162. package/build/esnext/utils/obj.utils.d.ts +0 -124
  163. package/build/esnext/utils/obj.utils.js +0 -413
  164. package/build/esnext/utils/obj.utils.js.map +0 -1
  165. package/build/esnext/utils/performance.utils.d.ts +0 -136
  166. package/build/esnext/utils/performance.utils.js +0 -271
  167. package/build/esnext/utils/performance.utils.js.map +0 -1
  168. package/build/esnext/utils/request.utils.d.ts +0 -86
  169. package/build/esnext/utils/request.utils.js +0 -186
  170. package/build/esnext/utils/request.utils.js.map +0 -1
  171. package/build/esnext/utils/response.utils.d.ts +0 -163
  172. package/build/esnext/utils/response.utils.js +0 -247
  173. package/build/esnext/utils/response.utils.js.map +0 -1
  174. package/build/esnext/utils/stream.utils.d.ts +0 -88
  175. package/build/esnext/utils/stream.utils.js +0 -210
  176. package/build/esnext/utils/stream.utils.js.map +0 -1
  177. package/build/esnext/utils/string.utils.d.ts +0 -93
  178. package/build/esnext/utils/string.utils.js +0 -165
  179. package/build/esnext/utils/string.utils.js.map +0 -1
  180. package/build/esnext/utils/type.utils.d.ts +0 -90
  181. package/build/esnext/utils/type.utils.js +0 -187
  182. package/build/esnext/utils/type.utils.js.map +0 -1
  183. package/build/esnext/utils/url.utils.d.ts +0 -141
  184. package/build/esnext/utils/url.utils.js +0 -304
  185. package/build/esnext/utils/url.utils.js.map +0 -1
  186. package/build/esnext/utils/validate.utils.d.ts +0 -177
  187. package/build/esnext/utils/validate.utils.js +0 -320
  188. package/build/esnext/utils/validate.utils.js.map +0 -1
  189. package/build/src/config.d.ts +0 -122
  190. package/build/src/config.js +0 -138
  191. package/build/src/config.js.map +0 -1
  192. package/build/src/index.d.ts +0 -27
  193. package/build/src/index.js +0 -69
  194. package/build/src/index.js.map +0 -1
  195. package/build/src/servers/server.builder.d.ts +0 -508
  196. package/build/src/servers/server.builder.js +0 -661
  197. package/build/src/servers/server.builder.js.map +0 -1
  198. package/build/src/servers/server.d.ts +0 -256
  199. package/build/src/servers/server.js +0 -962
  200. package/build/src/servers/server.js.map +0 -1
  201. package/build/src/types/api-response.d.ts +0 -152
  202. package/build/src/types/api-response.js +0 -37
  203. package/build/src/types/api-response.js.map +0 -1
  204. package/build/src/types/index.d.ts +0 -125
  205. package/build/src/types/index.js +0 -26
  206. package/build/src/types/index.js.map +0 -1
  207. package/build/src/types/server.d.ts +0 -268
  208. package/build/src/types/server.js +0 -26
  209. package/build/src/types/server.js.map +0 -1
  210. package/build/src/utils/array.utils.d.ts +0 -168
  211. package/build/src/utils/array.utils.js +0 -364
  212. package/build/src/utils/array.utils.js.map +0 -1
  213. package/build/src/utils/async.utils.d.ts +0 -265
  214. package/build/src/utils/async.utils.js +0 -641
  215. package/build/src/utils/async.utils.js.map +0 -1
  216. package/build/src/utils/cache.utils.d.ts +0 -153
  217. package/build/src/utils/cache.utils.js +0 -297
  218. package/build/src/utils/cache.utils.js.map +0 -1
  219. package/build/src/utils/context-store.utils.d.ts +0 -189
  220. package/build/src/utils/context-store.utils.js +0 -296
  221. package/build/src/utils/context-store.utils.js.map +0 -1
  222. package/build/src/utils/crypto.utils.d.ts +0 -160
  223. package/build/src/utils/crypto.utils.js +0 -293
  224. package/build/src/utils/crypto.utils.js.map +0 -1
  225. package/build/src/utils/date.utils.d.ts +0 -159
  226. package/build/src/utils/date.utils.js +0 -396
  227. package/build/src/utils/date.utils.js.map +0 -1
  228. package/build/src/utils/decorators.utils.d.ts +0 -315
  229. package/build/src/utils/decorators.utils.js +0 -516
  230. package/build/src/utils/decorators.utils.js.map +0 -1
  231. package/build/src/utils/dir.utils.d.ts +0 -196
  232. package/build/src/utils/dir.utils.js +0 -501
  233. package/build/src/utils/dir.utils.js.map +0 -1
  234. package/build/src/utils/env.utils.d.ts +0 -377
  235. package/build/src/utils/env.utils.js +0 -789
  236. package/build/src/utils/env.utils.js.map +0 -1
  237. package/build/src/utils/exception.utils.d.ts +0 -230
  238. package/build/src/utils/exception.utils.js +0 -407
  239. package/build/src/utils/exception.utils.js.map +0 -1
  240. package/build/src/utils/fs.utils.d.ts +0 -164
  241. package/build/src/utils/fs.utils.js +0 -372
  242. package/build/src/utils/fs.utils.js.map +0 -1
  243. package/build/src/utils/http-status-codes.d.ts +0 -266
  244. package/build/src/utils/http-status-codes.js +0 -298
  245. package/build/src/utils/http-status-codes.js.map +0 -1
  246. package/build/src/utils/id.utils.d.ts +0 -36
  247. package/build/src/utils/id.utils.js +0 -91
  248. package/build/src/utils/id.utils.js.map +0 -1
  249. package/build/src/utils/logger.utils.d.ts +0 -160
  250. package/build/src/utils/logger.utils.js +0 -346
  251. package/build/src/utils/logger.utils.js.map +0 -1
  252. package/build/src/utils/middleware.utils.d.ts +0 -100
  253. package/build/src/utils/middleware.utils.js +0 -244
  254. package/build/src/utils/middleware.utils.js.map +0 -1
  255. package/build/src/utils/obj.utils.d.ts +0 -124
  256. package/build/src/utils/obj.utils.js +0 -429
  257. package/build/src/utils/obj.utils.js.map +0 -1
  258. package/build/src/utils/performance.utils.d.ts +0 -136
  259. package/build/src/utils/performance.utils.js +0 -278
  260. package/build/src/utils/performance.utils.js.map +0 -1
  261. package/build/src/utils/request.utils.d.ts +0 -86
  262. package/build/src/utils/request.utils.js +0 -195
  263. package/build/src/utils/request.utils.js.map +0 -1
  264. package/build/src/utils/response.utils.d.ts +0 -163
  265. package/build/src/utils/response.utils.js +0 -260
  266. package/build/src/utils/response.utils.js.map +0 -1
  267. package/build/src/utils/stream.utils.d.ts +0 -88
  268. package/build/src/utils/stream.utils.js +0 -218
  269. package/build/src/utils/stream.utils.js.map +0 -1
  270. package/build/src/utils/string.utils.d.ts +0 -93
  271. package/build/src/utils/string.utils.js +0 -179
  272. package/build/src/utils/string.utils.js.map +0 -1
  273. package/build/src/utils/type.utils.d.ts +0 -90
  274. package/build/src/utils/type.utils.js +0 -196
  275. package/build/src/utils/type.utils.js.map +0 -1
  276. package/build/src/utils/url.utils.d.ts +0 -141
  277. package/build/src/utils/url.utils.js +0 -317
  278. package/build/src/utils/url.utils.js.map +0 -1
  279. package/build/src/utils/validate.utils.d.ts +0 -177
  280. package/build/src/utils/validate.utils.js +0 -345
  281. package/build/src/utils/validate.utils.js.map +0 -1
@@ -21,78 +21,6 @@
21
21
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
22
  * SOFTWARE.
23
23
  */
24
- var __assign = (this && this.__assign) || function () {
25
- __assign = Object.assign || function(t) {
26
- for (var s, i = 1, n = arguments.length; i < n; i++) {
27
- s = arguments[i];
28
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
29
- t[p] = s[p];
30
- }
31
- return t;
32
- };
33
- return __assign.apply(this, arguments);
34
- };
35
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
- return new (P || (P = Promise))(function (resolve, reject) {
38
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
- step((generator = generator.apply(thisArg, _arguments || [])).next());
42
- });
43
- };
44
- var __generator = (this && this.__generator) || function (thisArg, body) {
45
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
46
- return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
47
- function verb(n) { return function (v) { return step([n, v]); }; }
48
- function step(op) {
49
- if (f) throw new TypeError("Generator is already executing.");
50
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
51
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
52
- if (y = 0, t) op = [op[0] & 2, t.value];
53
- switch (op[0]) {
54
- case 0: case 1: t = op; break;
55
- case 4: _.label++; return { value: op[1], done: false };
56
- case 5: _.label++; y = op[1]; op = [0]; continue;
57
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
58
- default:
59
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
60
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
61
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
62
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
63
- if (t[2]) _.ops.pop();
64
- _.trys.pop(); continue;
65
- }
66
- op = body.call(thisArg, _);
67
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
68
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
69
- }
70
- };
71
- var __read = (this && this.__read) || function (o, n) {
72
- var m = typeof Symbol === "function" && o[Symbol.iterator];
73
- if (!m) return o;
74
- var i = m.call(o), r, ar = [], e;
75
- try {
76
- while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
77
- }
78
- catch (error) { e = { error: error }; }
79
- finally {
80
- try {
81
- if (r && !r.done && (m = i["return"])) m.call(i);
82
- }
83
- finally { if (e) throw e.error; }
84
- }
85
- return ar;
86
- };
87
- var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
88
- if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
89
- if (ar || !(i in from)) {
90
- if (!ar) ar = Array.prototype.slice.call(from, 0, i);
91
- ar[i] = from[i];
92
- }
93
- }
94
- return to.concat(ar || Array.prototype.slice.call(from));
95
- };
96
24
  import express from 'express';
97
25
  import https from 'https';
98
26
  import { HttpStatusCodes } from '../utils/http-status-codes';
@@ -122,7 +50,39 @@ import { isPort } from '../utils/validate.utils';
122
50
  * Designed for microservices and production workloads.
123
51
  * Includes K8s readiness probes and zero-downtime support.
124
52
  */
125
- var ExpressServer = /** @class */ (function () {
53
+ export class ExpressServer {
54
+ /** Prometheus client registry for metrics collection */
55
+ register = null;
56
+ /** HTTP server instance (null when not running) */
57
+ server = null;
58
+ /** Merged configuration with defaults applied */
59
+ config;
60
+ /** User-defined lifecycle hooks */
61
+ hooks;
62
+ /** Global API prefix (from config) */
63
+ globalPrefix;
64
+ /** Internal fallback router */
65
+ rootRouter;
66
+ /** User-supplied router */
67
+ externalRouter;
68
+ /** Internal Express app instance */
69
+ app;
70
+ /** Set of active WebSocket connections */
71
+ connections = new Set();
72
+ /** Flag indicating if the server is shutting down */
73
+ isShuttingDown = false;
74
+ /**
75
+ * Collection of registered health check functions.
76
+ * These are executed when the health check endpoint is accessed.
77
+ */
78
+ healthChecks = [];
79
+ /** Prometheus metrics for monitoring */
80
+ requestCounter;
81
+ routeTimings;
82
+ requestSizes;
83
+ clientIPs;
84
+ /** Promise that resolves when initialization (middleware + routes) is complete */
85
+ initPromise;
126
86
  /**
127
87
  * Initializes server with intelligent defaults and security best practices.
128
88
  * All settings can be customized via config and hooks.
@@ -140,23 +100,7 @@ var ExpressServer = /** @class */ (function () {
140
100
  * - Health checks
141
101
  * - Request tracing
142
102
  */
143
- function ExpressServer(config, hooks) {
144
- var _a;
145
- if (hooks === void 0) { hooks = {}; }
146
- var _b, _c, _d;
147
- /** Prometheus client registry for metrics collection */
148
- this.register = null;
149
- /** HTTP server instance (null when not running) */
150
- this.server = null;
151
- /** Set of active WebSocket connections */
152
- this.connections = new Set();
153
- /** Flag indicating if the server is shutting down */
154
- this.isShuttingDown = false;
155
- /**
156
- * Collection of registered health check functions.
157
- * These are executed when the health check endpoint is accessed.
158
- */
159
- this.healthChecks = [];
103
+ constructor(config, hooks = {}) {
160
104
  if (ExpressServer.isBuiltServerConfig(config)) {
161
105
  this.config = config;
162
106
  }
@@ -165,13 +109,13 @@ var ExpressServer = /** @class */ (function () {
165
109
  this.config = deepObjMerge({}, defaultServerConfig, config);
166
110
  }
167
111
  if (!isPort(this.config.port)) {
168
- getLogger().error("Port must be a valid number between 1 and 65535, got: ".concat(this.config.port));
112
+ getLogger().error(`Port must be a valid number between 1 and 65535, got: ${this.config.port}`);
169
113
  process.exit(1);
170
114
  }
171
115
  // Sanitize app name for metrics (replace invalid characters with underscore)
172
- var safeAppName = (this.config.appName || 'express_app').toLowerCase().replace(/[^a-z0-9_]/g, '_');
173
- if ((_b = this.config.metrics) === null || _b === void 0 ? void 0 : _b.enable) {
174
- var client = ExpressServer.optionalRequire('prom-client');
116
+ const safeAppName = (this.config.appName || 'express_app').toLowerCase().replace(/[^a-z0-9_]/g, '_');
117
+ if (this.config.metrics?.enable) {
118
+ const client = ExpressServer.optionalRequire('prom-client');
175
119
  if (!client) {
176
120
  getLogger().error({ command: 'npm install prom-client' }, 'prom-client is required for metrics but not installed. Please add it to your dependencies');
177
121
  process.exit(1);
@@ -179,27 +123,27 @@ var ExpressServer = /** @class */ (function () {
179
123
  this.register = new client.Registry();
180
124
  // Initialize Prometheus metrics with sanitized names
181
125
  this.requestCounter = new client.Counter({
182
- name: "".concat(safeAppName, "_http_requests_total"),
126
+ name: `${safeAppName}_http_requests_total`,
183
127
  help: 'Total HTTP requests',
184
128
  labelNames: ['method', 'route', 'status'],
185
129
  registers: [this.register]
186
130
  });
187
131
  this.routeTimings = new client.Histogram({
188
- name: "".concat(safeAppName, "_http_request_duration_seconds"),
132
+ name: `${safeAppName}_http_request_duration_seconds`,
189
133
  help: 'Duration of HTTP requests by route',
190
134
  labelNames: ['method', 'route', 'status'],
191
135
  buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10],
192
136
  registers: [this.register]
193
137
  });
194
138
  this.requestSizes = new client.Histogram({
195
- name: "".concat(safeAppName, "_http_request_size_bytes"),
139
+ name: `${safeAppName}_http_request_size_bytes`,
196
140
  help: 'Size of HTTP request bodies',
197
141
  labelNames: ['method', 'route'],
198
142
  buckets: [100, 1000, 10000, 100000, 1000000],
199
143
  registers: [this.register]
200
144
  });
201
145
  this.clientIPs = new client.Counter({
202
- name: "".concat(safeAppName, "_http_client_ip_total"),
146
+ name: `${safeAppName}_http_client_ip_total`,
203
147
  help: 'Client IP request counter',
204
148
  labelNames: ['ip', 'method'],
205
149
  registers: [this.register]
@@ -207,15 +151,15 @@ var ExpressServer = /** @class */ (function () {
207
151
  // Default system metrics (CPU, memory, event loop lag, etc.)
208
152
  client.collectDefaultMetrics({
209
153
  register: this.register,
210
- prefix: "".concat(safeAppName, "_")
154
+ prefix: `${safeAppName}_`
211
155
  });
212
156
  }
213
157
  // Health checks
214
- if ((_c = config.healthCheck) === null || _c === void 0 ? void 0 : _c.checks) {
215
- (_a = this.healthChecks).push.apply(_a, __spreadArray([], __read(config.healthCheck.checks), false));
158
+ if (config.healthCheck?.checks) {
159
+ this.healthChecks.push(...config.healthCheck.checks);
216
160
  }
217
161
  // Set global prefix (normalize to empty string or "/prefix" without trailing slash)
218
- this.globalPrefix = this.normalizePath((_d = this.config.globalPrefix) !== null && _d !== void 0 ? _d : '', false);
162
+ this.globalPrefix = this.normalizePath(this.config.globalPrefix ?? '', false);
219
163
  this.hooks = hooks;
220
164
  this.app = express();
221
165
  this.rootRouter = express.Router();
@@ -229,61 +173,27 @@ var ExpressServer = /** @class */ (function () {
229
173
  * @param hook Name of the lifecycle hook to execute
230
174
  * @param args Arguments to pass to the hook function
231
175
  */
232
- ExpressServer.prototype.runHook = function (hook) {
233
- var args = [];
234
- for (var _i = 1; _i < arguments.length; _i++) {
235
- args[_i - 1] = arguments[_i];
176
+ async runHook(hook, ...args) {
177
+ try {
178
+ const fn = this.hooks[hook];
179
+ if (fn)
180
+ await fn.apply(null, args);
236
181
  }
237
- return __awaiter(this, void 0, void 0, function () {
238
- var fn, err_1;
239
- return __generator(this, function (_a) {
240
- switch (_a.label) {
241
- case 0:
242
- _a.trys.push([0, 3, , 4]);
243
- fn = this.hooks[hook];
244
- if (!fn) return [3 /*break*/, 2];
245
- return [4 /*yield*/, fn.apply(null, args)];
246
- case 1:
247
- _a.sent();
248
- _a.label = 2;
249
- case 2: return [3 /*break*/, 4];
250
- case 3:
251
- err_1 = _a.sent();
252
- getLogger().error({ err: err_1, hook: hook }, "Error executing ".concat(hook, " hook:"));
253
- return [3 /*break*/, 4];
254
- case 4: return [2 /*return*/];
255
- }
256
- });
257
- });
258
- };
182
+ catch (err) {
183
+ getLogger().error({ err, hook }, `Error executing ${hook} hook:`);
184
+ }
185
+ }
259
186
  /**
260
187
  * Initialize the Express server with middleware and routes.
261
188
  */
262
- ExpressServer.prototype.initialize = function () {
263
- return __awaiter(this, void 0, void 0, function () {
264
- return __generator(this, function (_a) {
265
- switch (_a.label) {
266
- case 0: return [4 /*yield*/, this.runHook('beforeInit', this)];
267
- case 1:
268
- _a.sent();
269
- // Set up middleware stack (order is critical)
270
- return [4 /*yield*/, this.setupMiddleware()];
271
- case 2:
272
- // Set up middleware stack (order is critical)
273
- _a.sent();
274
- // Set up default routes and error handling
275
- return [4 /*yield*/, this.setupRoutes()];
276
- case 3:
277
- // Set up default routes and error handling
278
- _a.sent();
279
- return [4 /*yield*/, this.runHook('afterInit', this)];
280
- case 4:
281
- _a.sent();
282
- return [2 /*return*/];
283
- }
284
- });
285
- });
286
- };
189
+ async initialize() {
190
+ await this.runHook('beforeInit', this);
191
+ // Set up middleware stack (order is critical)
192
+ await this.setupMiddleware();
193
+ // Set up default routes and error handling
194
+ await this.setupRoutes();
195
+ await this.runHook('afterInit', this);
196
+ }
287
197
  /**
288
198
  * Configure and register all middlewares in the optimal order.
289
199
  *
@@ -303,290 +213,260 @@ var ExpressServer = /** @class */ (function () {
303
213
  * 13. Global headers
304
214
  * 14. Custom response hooks
305
215
  */
306
- ExpressServer.prototype.setupMiddleware = function () {
307
- return __awaiter(this, void 0, void 0, function () {
308
- var helmet, cors, rateLimit, compression, cookieParser, openApiMountPath, openApiFilePath, isOpenApiFilePathExists, apiReference, _a, _b, _c, _d, err_2;
309
- var _e, _f;
310
- var _this = this;
311
- var _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
312
- return __generator(this, function (_y) {
313
- switch (_y.label) {
314
- case 0:
315
- if (!this.config.https) return [3 /*break*/, 2];
316
- return [4 /*yield*/, this.validateHttpsFiles()];
317
- case 1:
318
- _y.sent();
319
- _y.label = 2;
320
- case 2:
321
- // Basic middleware should be first
322
- this.app.disable('x-powered-by');
323
- if (this.config.trustProxy) {
324
- this.app.set('trust proxy', true);
325
- }
326
- // Request ID generation - must be first for proper tracing
327
- this.app.use(requestId({
328
- headerName: (_g = this.config.requestId) === null || _g === void 0 ? void 0 : _g.headerName,
329
- exposeHeader: (_h = this.config.requestId) === null || _h === void 0 ? void 0 : _h.exposeHeader,
330
- generator: (_j = this.config.requestId) === null || _j === void 0 ? void 0 : _j.generator
331
- }));
332
- // Request context setup for logging correlation
333
- this.app.use(setupRequestContext({
334
- headerName: (_k = this.config.requestId) === null || _k === void 0 ? void 0 : _k.headerName,
335
- autoLog: false
336
- }));
337
- // Early shutdown-awareness middleware (lets load balancers drain connections gracefully)
338
- this.app.use(function (_req, res, next) {
339
- if (_this.isShuttingDown) {
340
- res.setHeader('Connection', 'close');
341
- return res
342
- .status(HttpStatusCodes.SERVICE_UNAVAILABLE)
343
- .json(new ServiceUnavailableException('Server is shutting down'));
344
- }
345
- next();
346
- return;
347
- });
348
- // Security middleware should come early
349
- if (this.config.helmet) {
350
- helmet = ExpressServer.optionalRequire('helmet');
351
- if (!helmet) {
352
- getLogger().error({ command: 'npm install helmet' }, 'helmet is required but not installed. Please add it to your dependencies');
353
- process.exit(1);
354
- }
355
- if (typeof this.config.helmet === 'object') {
356
- this.app.use(helmet(this.config.helmet));
357
- }
358
- else {
359
- this.app.use(helmet());
360
- }
361
- }
362
- // CORS middleware should be early
363
- if (this.config.cors) {
364
- cors = ExpressServer.optionalRequire('cors');
365
- if (!cors) {
366
- getLogger().error({ command: 'npm install cors' }, 'cors is required but not installed. Please add it to your dependencies');
367
- process.exit(1);
368
- }
369
- this.app.use(cors(this.config.cors === true ? {} : this.config.cors));
370
- }
371
- // Global headers
372
- this.app.use(function (_req, res, next) {
373
- var _a, _b, _c, _d;
374
- if (_this.config.globalHeaders) {
375
- for (var key in _this.config.globalHeaders) {
376
- var value = _this.config.globalHeaders[key];
377
- res.setHeader(key, typeof value === 'function' ? value() : value);
378
- }
379
- }
380
- if (_this.config.isMicroservice) {
381
- res.setHeader('X-Microservice', _this.config.appName || 'express_app');
382
- }
383
- if ((_a = _this.config.serviceVersion) === null || _a === void 0 ? void 0 : _a.enable) {
384
- var version = typeof ((_b = _this.config.serviceVersion) === null || _b === void 0 ? void 0 : _b.version) === 'function'
385
- ? _this.config.serviceVersion.version()
386
- : (_c = _this.config.serviceVersion) === null || _c === void 0 ? void 0 : _c.version;
387
- res.setHeader(((_d = _this.config.serviceVersion) === null || _d === void 0 ? void 0 : _d.headerName) || 'x-service-version', version || '0.0.0');
388
- }
389
- next();
390
- });
391
- // Global request timeout protection
392
- if (this.config.requestTimeout) {
393
- this.app.use(timeout(this.config.requestTimeout));
394
- }
395
- // Response time tracking for performance monitoring
396
- if ((_l = this.config.responseTime) === null || _l === void 0 ? void 0 : _l.enable) {
397
- this.app.use(responseTime({
398
- addHeader: this.config.responseTime.addHeader,
399
- logOnComplete: this.config.responseTime.logOnComplete
400
- }));
401
- }
402
- // Rate limiting should be early to prevent unnecessary processing
403
- if ((_m = this.config.rateLimit) === null || _m === void 0 ? void 0 : _m.enable) {
404
- rateLimit = ExpressServer.optionalRequire('express-rate-limit');
405
- if (!rateLimit) {
406
- getLogger().error({ command: 'npm install express-rate-limit' }, 'express-rate-limit is required but not installed. Please add it to your dependencies');
407
- process.exit(1);
408
- }
409
- this.app.use(rateLimit({
410
- windowMs: (_o = this.config.rateLimit.windowMs) !== null && _o !== void 0 ? _o : 15 * 60 * 1000,
411
- max: (_p = this.config.rateLimit.max) !== null && _p !== void 0 ? _p : 100,
412
- handler: function (req, res) {
413
- var _a;
414
- var status = HttpStatusCodes.TOO_MANY_REQUESTS;
415
- var response = createFinalErrorResponse(req, status, ((_a = _this.config.rateLimit) === null || _a === void 0 ? void 0 : _a.message) || 'Too many requests');
416
- res.status(status).json(response);
417
- },
418
- standardHeaders: (_q = this.config.rateLimit.standardHeaders) !== null && _q !== void 0 ? _q : true,
419
- legacyHeaders: (_r = this.config.rateLimit.legacyHeaders) !== null && _r !== void 0 ? _r : false
420
- }));
421
- }
422
- // Request logging with filtering
423
- if ((_s = this.config.requestLogging) === null || _s === void 0 ? void 0 : _s.enable) {
424
- this.app.use(function (req, res, next) {
425
- var _a, _b, _c, _d, _e, _f;
426
- if (typeof ((_a = _this.config.requestLogging) === null || _a === void 0 ? void 0 : _a.ignorePaths) === 'function') {
427
- var skip = (_c = (_b = _this.config.requestLogging) === null || _b === void 0 ? void 0 : _b.ignorePaths) === null || _c === void 0 ? void 0 : _c.call(_b, req, res);
428
- if (skip)
429
- return next();
430
- }
431
- else if (Array.isArray((_d = _this.config.requestLogging) === null || _d === void 0 ? void 0 : _d.ignorePaths)) {
432
- var skip = (_f = (_e = _this.config.requestLogging) === null || _e === void 0 ? void 0 : _e.ignorePaths) === null || _f === void 0 ? void 0 : _f.includes(req.path);
433
- if (skip)
434
- return next();
435
- }
436
- var logger = getLogger();
437
- var incomingRequestMetaData = {
438
- requestId: req.id,
439
- method: req.method,
440
- url: req.originalUrl || req.url,
441
- ip: req.ip
442
- };
443
- logger.info(incomingRequestMetaData, 'Incoming Request');
444
- next();
445
- });
446
- }
447
- // Custom request preprocessing hook
448
- if (this.hooks.onRequest) {
449
- this.app.use(this.hooks.onRequest);
450
- }
451
- // Response compression for better performance
452
- if (this.config.compression) {
453
- compression = ExpressServer.optionalRequire('compression');
454
- if (!compression) {
455
- getLogger().error({ command: 'npm install compression' }, 'compression is required but not installed. Please add it to your dependencies');
456
- process.exit(1);
457
- }
458
- if (typeof this.config.compression === 'object') {
459
- this.app.use(compression(this.config.compression));
460
- }
461
- else {
462
- this.app.use(compression());
463
- }
464
- }
465
- // Static file serving (do NOT normalize filesystem path; only normalize route)
466
- if (this.config.staticFolders) {
467
- this.config.staticFolders.forEach(function (folder) {
468
- var _a;
469
- _this.app.use(_this.normalizePath((_a = folder.path) !== null && _a !== void 0 ? _a : '/'), express.static(folder.directory, {
470
- maxAge: folder.maxAge || 0,
471
- etag: folder.etag !== false,
472
- immutable: folder.immutable === true,
473
- lastModified: folder.lastModified !== false,
474
- cacheControl: folder.cacheControl !== false
475
- }));
476
- getLogger().info("Serving static folder: ".concat(folder.directory, " at path ").concat(folder.path || '/'));
477
- });
478
- }
479
- // Request body parsing with size limits
480
- if (this.config.bodyParser) {
481
- if (this.config.bodyParser.json) {
482
- this.app.use(express.json(this.config.bodyParser.json));
483
- }
484
- if (this.config.bodyParser.urlencoded) {
485
- this.app.use(express.urlencoded(this.config.bodyParser.urlencoded));
486
- }
487
- }
488
- // Cookie parser middleware
489
- if (this.config.cookieParser) {
490
- cookieParser = ExpressServer.optionalRequire('cookie-parser');
491
- if (!cookieParser) {
492
- getLogger().error({ command: 'npm install cookie-parser' }, 'cookie-parser is required but not installed. Please add it to your dependencies');
493
- process.exit(1);
494
- }
495
- if (typeof this.config.cookieParser === 'object') {
496
- this.app.use(cookieParser(undefined, this.config.cookieParser));
497
- }
498
- else {
499
- this.app.use(cookieParser());
500
- }
501
- }
502
- if (!((_t = this.config.openApi) === null || _t === void 0 ? void 0 : _t.enable)) return [3 /*break*/, 7];
503
- _y.label = 3;
504
- case 3:
505
- _y.trys.push([3, 6, , 7]);
506
- openApiMountPath = this.normalizePath((_u = this.config.openApi.mountPath) !== null && _u !== void 0 ? _u : '/docs', this.config.openApi.withGlobalPrefix);
507
- openApiFilePath = this.config.openApi.filePath;
508
- if (!openApiFilePath) {
509
- getLogger().error('OpenAPI file path is required');
510
- process.exit(1);
511
- }
512
- return [4 /*yield*/, fileExists(openApiFilePath)];
513
- case 4:
514
- isOpenApiFilePathExists = _y.sent();
515
- if (!isOpenApiFilePathExists) {
516
- getLogger().error("OpenAPI spec file not found at ".concat(openApiFilePath));
517
- process.exit(1);
518
- }
519
- if ((_v = this.config.openApi) === null || _v === void 0 ? void 0 : _v.verbose) {
520
- getLogger().info("Mounting OpenAPI docs at ".concat(openApiMountPath));
521
- getLogger().info("Using OpenAPI spec file at ".concat(openApiFilePath));
522
- }
523
- apiReference = ExpressServer.optionalRequire('@scalar/express-api-reference').apiReference;
524
- if (!apiReference) {
525
- getLogger().error({ command: 'npm install @scalar/express-api-reference' }, '@scalar/express-api-reference is required for OpenAPI docs but not installed. Please add it to your dependencies');
526
- process.exit(1);
527
- }
528
- _b = (_a = this.app).use;
529
- _c = [openApiMountPath];
530
- _d = apiReference;
531
- _e = {};
532
- _f = {};
533
- return [4 /*yield*/, fs.promises.readFile(openApiFilePath, 'utf8')];
534
- case 5:
535
- _b.apply(_a, _c.concat([_d.apply(void 0, [(_e.spec = (_f.content = _y.sent(),
536
- _f),
537
- _e)])]));
538
- if ((_w = this.config.openApi) === null || _w === void 0 ? void 0 : _w.verbose) {
539
- getLogger().info("Mounted OpenAPI docs at ".concat(openApiMountPath));
540
- }
541
- return [3 /*break*/, 7];
542
- case 6:
543
- err_2 = _y.sent();
544
- getLogger().error({ err: err_2 }, 'Failed to mount OpenAPI docs');
545
- return [3 /*break*/, 7];
546
- case 7:
547
- // Custom response preprocessing hook (apply global prefix if set)
548
- if (this.hooks.onResponse) {
549
- this.app.use(this.globalPrefix, this.hooks.onResponse);
550
- }
551
- if ((_x = this.config.metrics) === null || _x === void 0 ? void 0 : _x.enable) {
552
- // Add metrics tracking middleware
553
- this.app.use(function (req, res, next) {
554
- var _a, _b;
555
- var start = process.hrtime();
556
- // Track client IPs
557
- (_a = _this.clientIPs) === null || _a === void 0 ? void 0 : _a.inc({ ip: req.ip, method: req.method });
558
- // Track request sizes (parse safely)
559
- var cl = req.headers['content-length'];
560
- if (cl) {
561
- var size = Number(cl);
562
- if (!Number.isNaN(size) && size >= 0) {
563
- var route = _this.normalizeRouteForMetrics(req, res);
564
- (_b = _this.requestSizes) === null || _b === void 0 ? void 0 : _b.observe({ method: req.method, route: route }, size);
565
- }
566
- }
567
- res.once('finish', function () {
568
- var _a, _b;
569
- var _c = __read(process.hrtime(start), 2), seconds = _c[0], nanoseconds = _c[1];
570
- var finalRoute = _this.normalizeRouteForMetrics(req, res);
571
- (_a = _this.requestCounter) === null || _a === void 0 ? void 0 : _a.inc({
572
- method: req.method,
573
- route: finalRoute,
574
- status: res.statusCode.toString()
575
- });
576
- (_b = _this.routeTimings) === null || _b === void 0 ? void 0 : _b.observe({
577
- method: req.method,
578
- route: finalRoute,
579
- status: res.statusCode.toString()
580
- }, seconds + nanoseconds / 1e9);
581
- });
582
- next();
583
- });
584
- }
585
- return [2 /*return*/];
216
+ async setupMiddleware() {
217
+ if (this.config.https) {
218
+ await this.validateHttpsFiles();
219
+ }
220
+ // Basic middleware should be first
221
+ this.app.disable('x-powered-by');
222
+ if (this.config.trustProxy) {
223
+ this.app.set('trust proxy', true);
224
+ }
225
+ // Request ID generation - must be first for proper tracing
226
+ this.app.use(requestId({
227
+ headerName: this.config.requestId?.headerName,
228
+ exposeHeader: this.config.requestId?.exposeHeader,
229
+ generator: this.config.requestId?.generator
230
+ }));
231
+ // Request context setup for logging correlation
232
+ this.app.use(setupRequestContext({
233
+ headerName: this.config.requestId?.headerName,
234
+ autoLog: false
235
+ }));
236
+ // Early shutdown-awareness middleware (lets load balancers drain connections gracefully)
237
+ this.app.use((_req, res, next) => {
238
+ if (this.isShuttingDown) {
239
+ res.setHeader('Connection', 'close');
240
+ return res
241
+ .status(HttpStatusCodes.SERVICE_UNAVAILABLE)
242
+ .json(new ServiceUnavailableException('Server is shutting down'));
243
+ }
244
+ next();
245
+ return;
246
+ });
247
+ // Security middleware should come early
248
+ if (this.config.helmet) {
249
+ const helmet = ExpressServer.optionalRequire('helmet');
250
+ if (!helmet) {
251
+ getLogger().error({ command: 'npm install helmet' }, 'helmet is required but not installed. Please add it to your dependencies');
252
+ process.exit(1);
253
+ }
254
+ if (typeof this.config.helmet === 'object') {
255
+ this.app.use(helmet(this.config.helmet));
256
+ }
257
+ else {
258
+ this.app.use(helmet());
259
+ }
260
+ }
261
+ // CORS middleware should be early
262
+ if (this.config.cors) {
263
+ const cors = ExpressServer.optionalRequire('cors');
264
+ if (!cors) {
265
+ getLogger().error({ command: 'npm install cors' }, 'cors is required but not installed. Please add it to your dependencies');
266
+ process.exit(1);
267
+ }
268
+ this.app.use(cors(this.config.cors === true ? {} : this.config.cors));
269
+ }
270
+ // Global headers
271
+ this.app.use((_req, res, next) => {
272
+ if (this.config.globalHeaders) {
273
+ for (const key in this.config.globalHeaders) {
274
+ const value = this.config.globalHeaders[key];
275
+ res.setHeader(key, typeof value === 'function' ? value() : value);
586
276
  }
587
- });
277
+ }
278
+ if (this.config.isMicroservice) {
279
+ res.setHeader('X-Microservice', this.config.appName || 'express_app');
280
+ }
281
+ if (this.config.serviceVersion?.enable) {
282
+ const version = typeof this.config.serviceVersion?.version === 'function'
283
+ ? this.config.serviceVersion.version()
284
+ : this.config.serviceVersion?.version;
285
+ res.setHeader(this.config.serviceVersion?.headerName || 'x-service-version', version || '0.0.0');
286
+ }
287
+ next();
588
288
  });
589
- };
289
+ // Global request timeout protection
290
+ if (this.config.requestTimeout) {
291
+ this.app.use(timeout(this.config.requestTimeout));
292
+ }
293
+ // Response time tracking for performance monitoring
294
+ if (this.config.responseTime?.enable) {
295
+ this.app.use(responseTime({
296
+ addHeader: this.config.responseTime.addHeader,
297
+ logOnComplete: this.config.responseTime.logOnComplete
298
+ }));
299
+ }
300
+ // Rate limiting should be early to prevent unnecessary processing
301
+ if (this.config.rateLimit?.enable) {
302
+ const rateLimit = ExpressServer.optionalRequire('express-rate-limit');
303
+ if (!rateLimit) {
304
+ getLogger().error({ command: 'npm install express-rate-limit' }, 'express-rate-limit is required but not installed. Please add it to your dependencies');
305
+ process.exit(1);
306
+ }
307
+ this.app.use(rateLimit({
308
+ windowMs: this.config.rateLimit.windowMs ?? 15 * 60 * 1000,
309
+ max: this.config.rateLimit.max ?? 100,
310
+ handler: (req, res) => {
311
+ const status = HttpStatusCodes.TOO_MANY_REQUESTS;
312
+ const response = createFinalErrorResponse(req, status, this.config.rateLimit?.message || 'Too many requests');
313
+ res.status(status).json(response);
314
+ },
315
+ standardHeaders: this.config.rateLimit.standardHeaders ?? true,
316
+ legacyHeaders: this.config.rateLimit.legacyHeaders ?? false
317
+ }));
318
+ }
319
+ // Request logging with filtering
320
+ if (this.config.requestLogging?.enable) {
321
+ this.app.use((req, res, next) => {
322
+ if (typeof this.config.requestLogging?.ignorePaths === 'function') {
323
+ const skip = this.config.requestLogging?.ignorePaths?.(req, res);
324
+ if (skip)
325
+ return next();
326
+ }
327
+ else if (Array.isArray(this.config.requestLogging?.ignorePaths)) {
328
+ const skip = this.config.requestLogging?.ignorePaths?.includes(req.path);
329
+ if (skip)
330
+ return next();
331
+ }
332
+ const logger = getLogger();
333
+ const incomingRequestMetaData = {
334
+ requestId: req.id,
335
+ method: req.method,
336
+ url: req.originalUrl || req.url,
337
+ ip: req.ip
338
+ };
339
+ logger.info(incomingRequestMetaData, 'Incoming Request');
340
+ next();
341
+ });
342
+ }
343
+ // Custom request preprocessing hook
344
+ if (this.hooks.onRequest) {
345
+ this.app.use(this.hooks.onRequest);
346
+ }
347
+ // Response compression for better performance
348
+ if (this.config.compression) {
349
+ const compression = ExpressServer.optionalRequire('compression');
350
+ if (!compression) {
351
+ getLogger().error({ command: 'npm install compression' }, 'compression is required but not installed. Please add it to your dependencies');
352
+ process.exit(1);
353
+ }
354
+ if (typeof this.config.compression === 'object') {
355
+ this.app.use(compression(this.config.compression));
356
+ }
357
+ else {
358
+ this.app.use(compression());
359
+ }
360
+ }
361
+ // Static file serving (do NOT normalize filesystem path; only normalize route)
362
+ if (this.config.staticFolders) {
363
+ this.config.staticFolders.forEach(folder => {
364
+ this.app.use(this.normalizePath(folder.path ?? '/'), express.static(folder.directory, {
365
+ maxAge: folder.maxAge || 0,
366
+ etag: folder.etag !== false,
367
+ immutable: folder.immutable === true,
368
+ lastModified: folder.lastModified !== false,
369
+ cacheControl: folder.cacheControl !== false
370
+ }));
371
+ getLogger().info(`Serving static folder: ${folder.directory} at path ${folder.path || '/'}`);
372
+ });
373
+ }
374
+ // Request body parsing with size limits
375
+ if (this.config.bodyParser) {
376
+ if (this.config.bodyParser.json) {
377
+ this.app.use(express.json(this.config.bodyParser.json));
378
+ }
379
+ if (this.config.bodyParser.urlencoded) {
380
+ this.app.use(express.urlencoded(this.config.bodyParser.urlencoded));
381
+ }
382
+ }
383
+ // Cookie parser middleware
384
+ if (this.config.cookieParser) {
385
+ const cookieParser = ExpressServer.optionalRequire('cookie-parser');
386
+ if (!cookieParser) {
387
+ getLogger().error({ command: 'npm install cookie-parser' }, 'cookie-parser is required but not installed. Please add it to your dependencies');
388
+ process.exit(1);
389
+ }
390
+ if (typeof this.config.cookieParser === 'object') {
391
+ this.app.use(cookieParser(undefined, this.config.cookieParser));
392
+ }
393
+ else {
394
+ this.app.use(cookieParser());
395
+ }
396
+ }
397
+ // OpenAPI docs via @scalar/express-api-reference
398
+ if (this.config.openApi?.enable) {
399
+ try {
400
+ const openApiMountPath = this.normalizePath(this.config.openApi.mountPath ?? '/docs', this.config.openApi.withGlobalPrefix);
401
+ const openApiFilePath = this.config.openApi.filePath;
402
+ if (!openApiFilePath) {
403
+ getLogger().error('OpenAPI file path is required');
404
+ process.exit(1);
405
+ }
406
+ const isOpenApiFilePathExists = await fileExists(openApiFilePath);
407
+ if (!isOpenApiFilePathExists) {
408
+ getLogger().error(`OpenAPI spec file not found at ${openApiFilePath}`);
409
+ process.exit(1);
410
+ }
411
+ if (this.config.openApi?.verbose) {
412
+ getLogger().info(`Mounting OpenAPI docs at ${openApiMountPath}`);
413
+ getLogger().info(`Using OpenAPI spec file at ${openApiFilePath}`);
414
+ }
415
+ const apiReference = ExpressServer.optionalRequire('@scalar/express-api-reference').apiReference;
416
+ if (!apiReference) {
417
+ getLogger().error({ command: 'npm install @scalar/express-api-reference' }, '@scalar/express-api-reference is required for OpenAPI docs but not installed. Please add it to your dependencies');
418
+ process.exit(1);
419
+ }
420
+ this.app.use(openApiMountPath, apiReference({
421
+ spec: {
422
+ content: await fs.promises.readFile(openApiFilePath, 'utf8')
423
+ }
424
+ }));
425
+ if (this.config.openApi?.verbose) {
426
+ getLogger().info(`Mounted OpenAPI docs at ${openApiMountPath}`);
427
+ }
428
+ }
429
+ catch (err) {
430
+ getLogger().error({ err }, 'Failed to mount OpenAPI docs');
431
+ }
432
+ }
433
+ // Custom response preprocessing hook (apply global prefix if set)
434
+ if (this.hooks.onResponse) {
435
+ this.app.use(this.globalPrefix, this.hooks.onResponse);
436
+ }
437
+ if (this.config.metrics?.enable) {
438
+ // Add metrics tracking middleware
439
+ this.app.use((req, res, next) => {
440
+ const start = process.hrtime();
441
+ // Track client IPs
442
+ this.clientIPs?.inc({ ip: req.ip, method: req.method });
443
+ // Track request sizes (parse safely)
444
+ const cl = req.headers['content-length'];
445
+ if (cl) {
446
+ const size = Number(cl);
447
+ if (!Number.isNaN(size) && size >= 0) {
448
+ const route = this.normalizeRouteForMetrics(req, res);
449
+ this.requestSizes?.observe({ method: req.method, route }, size);
450
+ }
451
+ }
452
+ res.once('finish', () => {
453
+ const [seconds, nanoseconds] = process.hrtime(start);
454
+ const finalRoute = this.normalizeRouteForMetrics(req, res);
455
+ this.requestCounter?.inc({
456
+ method: req.method,
457
+ route: finalRoute,
458
+ status: res.statusCode.toString()
459
+ });
460
+ this.routeTimings?.observe({
461
+ method: req.method,
462
+ route: finalRoute,
463
+ status: res.statusCode.toString()
464
+ }, seconds + nanoseconds / 1e9);
465
+ });
466
+ next();
467
+ });
468
+ }
469
+ }
590
470
  /**
591
471
  * Configure server routes and error handling.
592
472
  * Sets up in following order:
@@ -596,118 +476,82 @@ var ExpressServer = /** @class */ (function () {
596
476
  * 3. 404 handler
597
477
  * 4. Error handler
598
478
  */
599
- ExpressServer.prototype.setupRoutes = function () {
600
- return __awaiter(this, void 0, void 0, function () {
601
- var healthCheckPath, metricsPath, routerToUse;
602
- var _this = this;
603
- var _a, _b, _c, _d, _e;
604
- return __generator(this, function (_f) {
605
- healthCheckPath = this.normalizePath(((_a = this.config.healthCheck) === null || _a === void 0 ? void 0 : _a.path) || '/healthz', (_b = this.config.healthCheck) === null || _b === void 0 ? void 0 : _b.withGlobalPrefix);
606
- this.app.get(healthCheckPath, function (_req, res) { return __awaiter(_this, void 0, void 0, function () {
607
- var checkResults, results, allOk, status_1, response, _a;
608
- var _this = this;
609
- var _b;
610
- return __generator(this, function (_c) {
611
- switch (_c.label) {
612
- case 0:
613
- _c.trys.push([0, 2, , 3]);
614
- if (!this.healthChecks.length) {
615
- return [2 /*return*/, res.status(HttpStatusCodes.OK).json(new SuccessResponse('OK'))];
616
- }
617
- return [4 /*yield*/, Promise.allSettled(this.healthChecks.map(function (_a) { return __awaiter(_this, [_a], void 0, function (_b) {
618
- var status_2, error_1;
619
- var name = _b.name, check = _b.check;
620
- return __generator(this, function (_c) {
621
- switch (_c.label) {
622
- case 0:
623
- _c.trys.push([0, 2, , 3]);
624
- return [4 /*yield*/, Promise.resolve(check())];
625
- case 1:
626
- status_2 = _c.sent();
627
- return [2 /*return*/, { name: name, status: status_2, error: null }];
628
- case 2:
629
- error_1 = _c.sent();
630
- return [2 /*return*/, { name: name, status: false, error: error_1.message }];
631
- case 3: return [2 /*return*/];
632
- }
633
- });
634
- }); }))];
635
- case 1:
636
- checkResults = _c.sent();
637
- results = checkResults.map(function (result) {
638
- if (result.status === 'fulfilled')
639
- return result.value;
640
- return { name: 'unknown', status: false, error: result.reason };
641
- });
642
- allOk = results.every(function (r) { return r.status; });
643
- status_1 = allOk ? HttpStatusCodes.OK : HttpStatusCodes.SERVICE_UNAVAILABLE;
644
- response = new SuccessResponse(allOk ? 'OK' : 'Service unavailable');
645
- if (!allOk)
646
- response.error = true;
647
- if ((_b = this.config.healthCheck) === null || _b === void 0 ? void 0 : _b.detailed)
648
- response.data = { checks: results };
649
- return [2 /*return*/, res.status(status_1).json(response)];
650
- case 2:
651
- _a = _c.sent();
652
- return [2 /*return*/, res
653
- .status(HttpStatusCodes.INTERNAL_SERVER_ERROR)
654
- .json(new InternalServerErrorException('Health check failed'))];
655
- case 3: return [2 /*return*/];
656
- }
657
- });
658
- }); });
659
- // Metrics endpoint
660
- if ((_c = this.config.metrics) === null || _c === void 0 ? void 0 : _c.enable) {
661
- metricsPath = this.normalizePath((_d = this.config.metrics.path) !== null && _d !== void 0 ? _d : '/metrics', (_e = this.config.metrics) === null || _e === void 0 ? void 0 : _e.withGlobalPrefix);
662
- this.app.get(metricsPath, function (_req, res) { return __awaiter(_this, void 0, void 0, function () {
663
- var _a, _b;
664
- return __generator(this, function (_c) {
665
- switch (_c.label) {
666
- case 0:
667
- res.set('Content-Type', this.register.contentType);
668
- _b = (_a = res).end;
669
- return [4 /*yield*/, this.register.metrics()];
670
- case 1:
671
- _b.apply(_a, [_c.sent()]);
672
- return [2 /*return*/];
673
- }
674
- });
675
- }); });
479
+ async setupRoutes() {
480
+ // Health check endpoint
481
+ const healthCheckPath = this.normalizePath(this.config.healthCheck?.path || '/healthz', this.config.healthCheck?.withGlobalPrefix);
482
+ this.app.get(healthCheckPath, async (_req, res) => {
483
+ try {
484
+ if (!this.healthChecks.length) {
485
+ return res.status(HttpStatusCodes.OK).json(new SuccessResponse('OK'));
676
486
  }
677
- routerToUse = this.externalRouter || this.rootRouter;
678
- this.app.use(this.globalPrefix, routerToUse);
679
- // 404 handler (must be after all other routes)
680
- this.app.use(function (req, res) {
681
- var status = HttpStatusCodes.NOT_FOUND;
682
- var response = createFinalErrorResponse(req, status, "Route ".concat(req.method.toUpperCase(), " ").concat(req.path, " not found"));
683
- res.status(status).json(response);
684
- });
685
- // Global error handler (must be the last middleware)
686
- this.app.use(function (err, req, res, next) {
687
- var _a;
688
- // Check if this is a 404 error that should be handled with special logging rules
689
- var isNotFoundError = err instanceof NotFoundException;
690
- var shouldSkipLogging = !_this.hooks.onError &&
691
- isNotFoundError &&
692
- ((_a = _this.config.requestLogging) === null || _a === void 0 ? void 0 : _a.enable) &&
693
- _this.config.requestLogging.skipNotFoundRoutes === true;
694
- if (_this.hooks.onError) {
695
- // Use custom error handler if provided
696
- _this.hooks.onError(err, req, res, next);
487
+ const checkResults = await Promise.allSettled(this.healthChecks.map(async ({ name, check }) => {
488
+ try {
489
+ const status = await Promise.resolve(check());
490
+ return { name, status, error: null };
697
491
  }
698
- else {
699
- // Default error handler with logging
700
- var errorHandlerMiddleware = errorHandler({
701
- logErrors: !shouldSkipLogging,
702
- includeDetails: Env.isDev() // Only show stack traces in development
703
- });
704
- errorHandlerMiddleware(err, req, res, next);
492
+ catch (error) {
493
+ return { name, status: false, error: error.message };
705
494
  }
495
+ }));
496
+ const results = checkResults.map(result => {
497
+ if (result.status === 'fulfilled')
498
+ return result.value;
499
+ return { name: 'unknown', status: false, error: result.reason };
706
500
  });
707
- return [2 /*return*/];
501
+ const allOk = results.every(r => r.status);
502
+ const status = allOk ? HttpStatusCodes.OK : HttpStatusCodes.SERVICE_UNAVAILABLE;
503
+ const response = new SuccessResponse(allOk ? 'OK' : 'Service unavailable');
504
+ if (!allOk)
505
+ response.error = true;
506
+ if (this.config.healthCheck?.detailed)
507
+ response.data = { checks: results };
508
+ return res.status(status).json(response);
509
+ }
510
+ catch {
511
+ return res
512
+ .status(HttpStatusCodes.INTERNAL_SERVER_ERROR)
513
+ .json(new InternalServerErrorException('Health check failed'));
514
+ }
515
+ });
516
+ // Metrics endpoint
517
+ if (this.config.metrics?.enable) {
518
+ const metricsPath = this.normalizePath(this.config.metrics.path ?? '/metrics', this.config.metrics?.withGlobalPrefix);
519
+ this.app.get(metricsPath, async (_req, res) => {
520
+ res.set('Content-Type', this.register.contentType);
521
+ res.end(await this.register.metrics());
708
522
  });
523
+ }
524
+ // Application routes
525
+ const routerToUse = this.externalRouter || this.rootRouter;
526
+ this.app.use(this.globalPrefix, routerToUse);
527
+ // 404 handler (must be after all other routes)
528
+ this.app.use((req, res) => {
529
+ const status = HttpStatusCodes.NOT_FOUND;
530
+ const response = createFinalErrorResponse(req, status, `Route ${req.method.toUpperCase()} ${req.path} not found`);
531
+ res.status(status).json(response);
532
+ });
533
+ // Global error handler (must be the last middleware)
534
+ this.app.use((err, req, res, next) => {
535
+ // Check if this is a 404 error that should be handled with special logging rules
536
+ const isNotFoundError = err instanceof NotFoundException;
537
+ const shouldSkipLogging = !this.hooks.onError &&
538
+ isNotFoundError &&
539
+ this.config.requestLogging?.enable &&
540
+ this.config.requestLogging.skipNotFoundRoutes === true;
541
+ if (this.hooks.onError) {
542
+ // Use custom error handler if provided
543
+ this.hooks.onError(err, req, res, next);
544
+ }
545
+ else {
546
+ // Default error handler with logging
547
+ const errorHandlerMiddleware = errorHandler({
548
+ logErrors: !shouldSkipLogging,
549
+ includeDetails: Env.isDev() // Only show stack traces in development
550
+ });
551
+ errorHandlerMiddleware(err, req, res, next);
552
+ }
709
553
  });
710
- };
554
+ }
711
555
  /**
712
556
  * Register a new health check function for monitoring service dependencies.
713
557
  *
@@ -724,28 +568,28 @@ var ExpressServer = /** @class */ (function () {
724
568
  * @param check Function returning boolean or Promise<boolean> indicating health
725
569
  * @returns This instance for method chaining
726
570
  */
727
- ExpressServer.prototype.registerHealthCheck = function (name, check) {
728
- this.healthChecks.push({ name: name, check: check });
571
+ registerHealthCheck(name, check) {
572
+ this.healthChecks.push({ name, check });
729
573
  return this;
730
- };
574
+ }
731
575
  /**
732
576
  * Get the underlying Express application instance.
733
577
  * Use this for advanced Express features not exposed by this wrapper.
734
578
  *
735
579
  * @returns The raw Express app instance
736
580
  */
737
- ExpressServer.prototype.getApp = function () {
581
+ getApp() {
738
582
  return this.app;
739
- };
583
+ }
740
584
  /**
741
585
  * Get the active HTTP/HTTPS server instance.
742
586
  * Returns null if the server is not currently running.
743
587
  *
744
588
  * @returns The HTTP/HTTPS server instance or null
745
589
  */
746
- ExpressServer.prototype.getServer = function () {
590
+ getServer() {
747
591
  return this.server;
748
- };
592
+ }
749
593
  /**
750
594
  * Start the HTTP server and begin listening for requests.
751
595
  *
@@ -759,90 +603,68 @@ var ExpressServer = /** @class */ (function () {
759
603
  * @returns Promise resolving to the running HTTP server instance
760
604
  * @throws Error if server fails to start or port is already in use
761
605
  */
762
- ExpressServer.prototype.start = function () {
763
- return __awaiter(this, void 0, void 0, function () {
764
- var _this = this;
765
- return __generator(this, function (_a) {
766
- switch (_a.label) {
767
- case 0:
768
- // Ensure initialization (middleware + routes) completed before starting
769
- return [4 /*yield*/, this.initPromise];
770
- case 1:
771
- // Ensure initialization (middleware + routes) completed before starting
772
- _a.sent();
773
- return [4 /*yield*/, this.runHook('beforeStart', this.app)];
774
- case 2:
775
- _a.sent();
776
- return [2 /*return*/, new Promise(function (resolve, reject) {
777
- var _a, _b;
778
- try {
779
- // Prepare listen arguments with optional host parameter
780
- var listenArgs = [
781
- _this.config.port,
782
- _this.config.host,
783
- function () { return __awaiter(_this, void 0, void 0, function () {
784
- var protocol, url;
785
- var _a, _b, _c;
786
- return __generator(this, function (_d) {
787
- switch (_d.label) {
788
- case 0:
789
- protocol = this.config.https ? 'https' : 'http';
790
- url = "".concat(protocol, "://").concat(this.config.host, ":").concat(this.config.port);
791
- getLogger().info("Server running on ".concat(url));
792
- if ((_a = this.config.healthCheck) === null || _a === void 0 ? void 0 : _a.path) {
793
- getLogger().info("Health check available at ".concat(url).concat(this.normalizePath(this.config.healthCheck.path, this.config.healthCheck.withGlobalPrefix)));
794
- }
795
- if (((_b = this.config.metrics) === null || _b === void 0 ? void 0 : _b.enable) && this.config.metrics.path) {
796
- getLogger().info("Metrics available at ".concat(url).concat(this.normalizePath(this.config.metrics.path, this.config.metrics.withGlobalPrefix)));
797
- }
798
- if ((_c = this.config.openApi) === null || _c === void 0 ? void 0 : _c.enable) {
799
- getLogger().info("API docs available at ".concat(url).concat(this.normalizePath(this.config.openApi.mountPath, this.config.openApi.withGlobalPrefix)));
800
- }
801
- if (!this.server) return [3 /*break*/, 2];
802
- return [4 /*yield*/, this.runHook('afterStart', this.server)];
803
- case 1:
804
- _d.sent();
805
- _d.label = 2;
806
- case 2:
807
- resolve(this.server);
808
- return [2 /*return*/];
809
- }
810
- });
811
- }); }
812
- ];
813
- if (_this.config.https) {
814
- var httpsOptions = __assign(__assign({}, _this.config.https), { key: fs.readFileSync(_this.config.https.key), cert: fs.readFileSync(_this.config.https.cert) });
815
- if (_this.config.https.ca) {
816
- httpsOptions.ca = fs.readFileSync(_this.config.https.ca);
817
- }
818
- if (_this.config.https.passphrase) {
819
- httpsOptions.passphrase = _this.config.https.passphrase;
820
- }
821
- _this.server = (_a = https.createServer(httpsOptions, _this.app)).listen.apply(_a, __spreadArray([], __read(listenArgs), false));
822
- }
823
- else {
824
- // Start the HTTP server
825
- _this.server = (_b = _this.app).listen.apply(_b, __spreadArray([], __read(listenArgs), false));
826
- }
827
- // Track connections
828
- _this.server.on('connection', function (conn) {
829
- _this.connections.add(conn);
830
- conn.on('close', function () { return _this.connections.delete(conn); });
831
- });
832
- // Handle server startup errors (port in use, permission denied, etc.)
833
- _this.server.on('error', function (err) {
834
- getLogger().error({ err: err }, 'Server failed to start');
835
- reject(err);
836
- });
837
- }
838
- catch (error) {
839
- reject(error);
840
- }
841
- })];
606
+ async start() {
607
+ // Ensure initialization (middleware + routes) completed before starting
608
+ await this.initPromise;
609
+ await this.runHook('beforeStart', this.app);
610
+ return new Promise((resolve, reject) => {
611
+ try {
612
+ // Prepare listen arguments with optional host parameter
613
+ const listenArgs = [
614
+ this.config.port,
615
+ this.config.host,
616
+ async () => {
617
+ const protocol = this.config.https ? 'https' : 'http';
618
+ const url = `${protocol}://${this.config.host}:${this.config.port}`;
619
+ getLogger().info(`Server running on ${url}`);
620
+ if (this.config.healthCheck?.path) {
621
+ getLogger().info(`Health check available at ${url}${this.normalizePath(this.config.healthCheck.path, this.config.healthCheck.withGlobalPrefix)}`);
622
+ }
623
+ if (this.config.metrics?.enable && this.config.metrics.path) {
624
+ getLogger().info(`Metrics available at ${url}${this.normalizePath(this.config.metrics.path, this.config.metrics.withGlobalPrefix)}`);
625
+ }
626
+ if (this.config.openApi?.enable) {
627
+ getLogger().info(`API docs available at ${url}${this.normalizePath(this.config.openApi.mountPath, this.config.openApi.withGlobalPrefix)}`);
628
+ }
629
+ if (this.server)
630
+ await this.runHook('afterStart', this.server);
631
+ resolve(this.server);
632
+ }
633
+ ];
634
+ if (this.config.https) {
635
+ const httpsOptions = {
636
+ ...this.config.https,
637
+ key: fs.readFileSync(this.config.https.key),
638
+ cert: fs.readFileSync(this.config.https.cert)
639
+ };
640
+ if (this.config.https.ca) {
641
+ httpsOptions.ca = fs.readFileSync(this.config.https.ca);
642
+ }
643
+ if (this.config.https.passphrase) {
644
+ httpsOptions.passphrase = this.config.https.passphrase;
645
+ }
646
+ this.server = https.createServer(httpsOptions, this.app).listen(...listenArgs);
842
647
  }
843
- });
648
+ else {
649
+ // Start the HTTP server
650
+ this.server = this.app.listen(...listenArgs);
651
+ }
652
+ // Track connections
653
+ this.server.on('connection', (conn) => {
654
+ this.connections.add(conn);
655
+ conn.on('close', () => this.connections.delete(conn));
656
+ });
657
+ // Handle server startup errors (port in use, permission denied, etc.)
658
+ this.server.on('error', err => {
659
+ getLogger().error({ err }, 'Server failed to start');
660
+ reject(err);
661
+ });
662
+ }
663
+ catch (error) {
664
+ reject(error);
665
+ }
844
666
  });
845
- };
667
+ }
846
668
  /**
847
669
  * Stop the HTTP server gracefully.
848
670
  *
@@ -859,78 +681,47 @@ var ExpressServer = /** @class */ (function () {
859
681
  * - Resources are properly cleaned up
860
682
  * - Monitoring systems are notified
861
683
  */
862
- ExpressServer.prototype.stop = function () {
863
- return __awaiter(this, arguments, void 0, function (force) {
864
- var shutdownTimeout, serverClosePromise, timeoutPromise, err_3;
865
- var _this = this;
866
- if (force === void 0) { force = false; }
867
- return __generator(this, function (_a) {
868
- switch (_a.label) {
869
- case 0:
870
- if (!this.server) {
871
- getLogger().warn('Stop called but server is not running');
872
- return [2 /*return*/];
873
- }
874
- if (this.isShuttingDown) {
875
- getLogger().warn('Stop called while shutdown is already in progress');
876
- return [2 /*return*/];
877
- }
878
- this.isShuttingDown = true;
879
- return [4 /*yield*/, this.runHook('beforeStop', this.server)];
880
- case 1:
881
- _a.sent();
882
- shutdownTimeout = 10000;
883
- serverClosePromise = new Promise(function (resolve, reject) {
884
- _this.server.close(function (err) { return __awaiter(_this, void 0, void 0, function () {
885
- return __generator(this, function (_a) {
886
- switch (_a.label) {
887
- case 0:
888
- if (err) {
889
- getLogger().error({ err: err }, 'Error while closing server');
890
- reject(err);
891
- return [2 /*return*/];
892
- }
893
- this.server = null;
894
- this.isShuttingDown = false;
895
- getLogger().info('Server stopped gracefully');
896
- return [4 /*yield*/, this.runHook('afterStop')];
897
- case 1:
898
- _a.sent();
899
- resolve();
900
- return [2 /*return*/];
901
- }
902
- });
903
- }); });
904
- });
905
- timeoutPromise = new Promise(function (_, reject) {
906
- return setTimeout(function () { return reject(new Error('Shutdown timeout')); }, shutdownTimeout);
907
- });
908
- _a.label = 2;
909
- case 2:
910
- _a.trys.push([2, 4, 5, 7]);
911
- return [4 /*yield*/, Promise.race([serverClosePromise, timeoutPromise])];
912
- case 3:
913
- _a.sent();
914
- return [3 /*break*/, 7];
915
- case 4:
916
- err_3 = _a.sent();
917
- getLogger().error({ err: err_3 }, 'Graceful shutdown timed out');
918
- if (force) {
919
- getLogger().warn('Forcing connection destroy due to shutdown timeout');
920
- }
921
- return [3 /*break*/, 7];
922
- case 5:
923
- // Always clean up connections
924
- return [4 /*yield*/, this.destroyConnections()];
925
- case 6:
926
- // Always clean up connections
927
- _a.sent();
928
- return [7 /*endfinally*/];
929
- case 7: return [2 /*return*/];
684
+ async stop(force = false) {
685
+ if (!this.server) {
686
+ getLogger().warn('Stop called but server is not running');
687
+ return;
688
+ }
689
+ if (this.isShuttingDown) {
690
+ getLogger().warn('Stop called while shutdown is already in progress');
691
+ return;
692
+ }
693
+ this.isShuttingDown = true;
694
+ await this.runHook('beforeStop', this.server);
695
+ const shutdownTimeout = 10_000; // 10s max wait
696
+ const serverClosePromise = new Promise((resolve, reject) => {
697
+ this.server.close(async (err) => {
698
+ if (err) {
699
+ getLogger().error({ err }, 'Error while closing server');
700
+ reject(err);
701
+ return;
930
702
  }
703
+ this.server = null;
704
+ this.isShuttingDown = false;
705
+ getLogger().info('Server stopped gracefully');
706
+ await this.runHook('afterStop');
707
+ resolve();
931
708
  });
932
709
  });
933
- };
710
+ const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Shutdown timeout')), shutdownTimeout));
711
+ try {
712
+ await Promise.race([serverClosePromise, timeoutPromise]);
713
+ }
714
+ catch (err) {
715
+ getLogger().error({ err }, 'Graceful shutdown timed out');
716
+ if (force) {
717
+ getLogger().warn('Forcing connection destroy due to shutdown timeout');
718
+ }
719
+ }
720
+ finally {
721
+ // Always clean up connections
722
+ await this.destroyConnections();
723
+ }
724
+ }
934
725
  /**
935
726
  * Enable graceful shutdown on OS signals for production deployment.
936
727
  *
@@ -942,66 +733,46 @@ var ExpressServer = /** @class */ (function () {
942
733
  *
943
734
  * @param signals Array of process signals to listen for (default: SIGINT, SIGTERM)
944
735
  */
945
- ExpressServer.prototype.enableGracefulShutdown = function (signals) {
946
- var _this = this;
947
- if (signals === void 0) { signals = ['SIGINT', 'SIGTERM']; }
948
- signals.forEach(function (signal) {
949
- process.on(signal, function () { return __awaiter(_this, void 0, void 0, function () {
950
- var err_4, forceError_1;
951
- return __generator(this, function (_a) {
952
- switch (_a.label) {
953
- case 0:
954
- getLogger().info("Received ".concat(signal, ", initiating graceful shutdown..."));
955
- _a.label = 1;
956
- case 1:
957
- _a.trys.push([1, 3, , 8]);
958
- return [4 /*yield*/, this.stop()];
959
- case 2:
960
- _a.sent();
961
- process.exit(0);
962
- return [3 /*break*/, 8];
963
- case 3:
964
- err_4 = _a.sent();
965
- getLogger().error({ err: err_4 }, 'Error during graceful shutdown, forcing stop...');
966
- _a.label = 4;
967
- case 4:
968
- _a.trys.push([4, 6, , 7]);
969
- return [4 /*yield*/, this.stop(true)];
970
- case 5:
971
- _a.sent(); // fallback to forced shutdown
972
- process.exit(1);
973
- return [3 /*break*/, 7];
974
- case 6:
975
- forceError_1 = _a.sent();
976
- getLogger().fatal({ forceError: forceError_1 }, 'Forced shutdown failed, exiting hard');
977
- process.exit(1);
978
- return [3 /*break*/, 7];
979
- case 7: return [3 /*break*/, 8];
980
- case 8: return [2 /*return*/];
736
+ enableGracefulShutdown(signals = ['SIGINT', 'SIGTERM']) {
737
+ signals.forEach(signal => {
738
+ process.on(signal, async () => {
739
+ getLogger().info(`Received ${signal}, initiating graceful shutdown...`);
740
+ try {
741
+ await this.stop();
742
+ process.exit(0);
743
+ }
744
+ catch (err) {
745
+ getLogger().error({ err }, 'Error during graceful shutdown, forcing stop...');
746
+ try {
747
+ await this.stop(true); // fallback to forced shutdown
748
+ process.exit(1);
981
749
  }
982
- });
983
- }); });
750
+ catch (forceError) {
751
+ getLogger().fatal({ forceError }, 'Forced shutdown failed, exiting hard');
752
+ process.exit(1);
753
+ }
754
+ }
755
+ });
984
756
  });
985
757
  return this;
986
- };
758
+ }
987
759
  /**
988
760
  * Set an externally created base router.
989
761
  * This will override the internal rootRouter.
990
762
  */
991
- ExpressServer.prototype.setBaseRouter = function (router) {
763
+ setBaseRouter(router) {
992
764
  this.externalRouter = router;
993
765
  return this;
994
- };
766
+ }
995
767
  /**
996
768
  * Create and register a new router (only used if not injecting one externally - use `setBaseRouter` instead).
997
769
  */
998
- ExpressServer.prototype.createRouter = function (prefix) {
999
- if (prefix === void 0) { prefix = ''; }
1000
- var router = express.Router();
1001
- var path = this.normalizePath(prefix, true);
770
+ createRouter(prefix = '') {
771
+ const router = express.Router();
772
+ const path = this.normalizePath(prefix, true);
1002
773
  this.rootRouter.use(path, router);
1003
774
  return router;
1004
- };
775
+ }
1005
776
  /**
1006
777
  * Register a new route handler with support for multiple HTTP methods.
1007
778
  * The route is automatically registered under the globalPrefix if set.
@@ -1011,13 +782,9 @@ var ExpressServer = /** @class */ (function () {
1011
782
  * @param handlers One or more Express request handlers (middleware + final handler)
1012
783
  * @returns This instance for method chaining
1013
784
  */
1014
- ExpressServer.prototype.registerRoute = function (methods, path) {
1015
- var handlers = [];
1016
- for (var _i = 2; _i < arguments.length; _i++) {
1017
- handlers[_i - 2] = arguments[_i];
1018
- }
1019
- var fullPath = this.normalizePath(path, true);
1020
- var methodMap = {
785
+ registerRoute(methods, path, ...handlers) {
786
+ const fullPath = this.normalizePath(path, true);
787
+ const methodMap = {
1021
788
  get: this.app.get.bind(this.app),
1022
789
  post: this.app.post.bind(this.app),
1023
790
  put: this.app.put.bind(this.app),
@@ -1026,17 +793,17 @@ var ExpressServer = /** @class */ (function () {
1026
793
  options: this.app.options.bind(this.app),
1027
794
  head: this.app.head.bind(this.app)
1028
795
  };
1029
- methods.forEach(function (m) {
1030
- var fn = methodMap[m];
796
+ methods.forEach(m => {
797
+ const fn = methodMap[m];
1031
798
  if (fn) {
1032
- fn.apply(void 0, __spreadArray([fullPath], __read(handlers), false));
799
+ fn(fullPath, ...handlers);
1033
800
  }
1034
801
  else {
1035
- throw new Error("Unsupported HTTP method: ".concat(m));
802
+ throw new Error(`Unsupported HTTP method: ${m}`);
1036
803
  }
1037
804
  });
1038
805
  return this;
1039
- };
806
+ }
1040
807
  /**
1041
808
  * Register custom middleware with optional path restriction.
1042
809
  *
@@ -1050,9 +817,9 @@ var ExpressServer = /** @class */ (function () {
1050
817
  * @param middleware Middleware handler (required if path is provided)
1051
818
  * @returns This instance for method chaining
1052
819
  */
1053
- ExpressServer.prototype.registerMiddleware = function (path, middleware) {
820
+ registerMiddleware(path, middleware) {
1054
821
  if (typeof path === 'string') {
1055
- var normalizedPath = this.normalizePath(path);
822
+ const normalizedPath = this.normalizePath(path);
1056
823
  if (normalizedPath) {
1057
824
  this.app.use(normalizedPath, middleware);
1058
825
  }
@@ -1064,7 +831,7 @@ var ExpressServer = /** @class */ (function () {
1064
831
  this.app.use(path);
1065
832
  }
1066
833
  return this;
1067
- };
834
+ }
1068
835
  /**
1069
836
  * Register one or more middleware functions to be applied globally.
1070
837
  * This is a simpler alternative to registerMiddleware when you just want
@@ -1073,56 +840,40 @@ var ExpressServer = /** @class */ (function () {
1073
840
  * @param middlewares One or more Express middleware functions
1074
841
  * @returns This instance for method chaining
1075
842
  */
1076
- ExpressServer.prototype.useMiddleware = function () {
1077
- var _this = this;
1078
- var middlewares = [];
1079
- for (var _i = 0; _i < arguments.length; _i++) {
1080
- middlewares[_i] = arguments[_i];
1081
- }
1082
- middlewares.forEach(function (middleware) {
1083
- _this.app.use(middleware);
843
+ useMiddleware(...middlewares) {
844
+ middlewares.forEach(middleware => {
845
+ this.app.use(middleware);
1084
846
  });
1085
847
  return this;
1086
- };
848
+ }
1087
849
  /**
1088
850
  * Get Prometheus registry (to add custom counters/histograms)
1089
851
  *
1090
852
  * @return {*} {client.Registry}
1091
853
  */
1092
- ExpressServer.prototype.getMetricsRegistry = function () {
1093
- var _a;
1094
- if (!((_a = this.config.metrics) === null || _a === void 0 ? void 0 : _a.enable)) {
854
+ getMetricsRegistry() {
855
+ if (!this.config.metrics?.enable) {
1095
856
  throw new Error('Metrics are not enabled in the server configuration');
1096
857
  }
1097
858
  return this.register;
1098
- };
859
+ }
1099
860
  /**
1100
861
  * Get server configuration
1101
862
  *
1102
863
  * @return {*} {ServerConfig}
1103
864
  */
1104
- ExpressServer.prototype.getConfig = function () {
865
+ getConfig() {
1105
866
  return this.config;
1106
- };
867
+ }
1107
868
  /**
1108
869
  * Wait until server initialization (middleware + routes) has completed.
1109
870
  * Useful for integration tests that inspect app before starting.
1110
871
  */
1111
- ExpressServer.prototype.waitUntilReady = function () {
1112
- return __awaiter(this, void 0, void 0, function () {
1113
- return __generator(this, function (_a) {
1114
- switch (_a.label) {
1115
- case 0: return [4 /*yield*/, this.initPromise];
1116
- case 1:
1117
- _a.sent();
1118
- return [2 /*return*/];
1119
- }
1120
- });
1121
- });
1122
- };
1123
- ExpressServer.prototype.normalizePath = function (path, withGlobalPrefix) {
1124
- if (withGlobalPrefix === void 0) { withGlobalPrefix = false; }
1125
- var sanitize = function (p) {
872
+ async waitUntilReady() {
873
+ await this.initPromise;
874
+ }
875
+ normalizePath(path, withGlobalPrefix = false) {
876
+ const sanitize = (p) => {
1126
877
  return ('/' +
1127
878
  p
1128
879
  .trim()
@@ -1131,118 +882,82 @@ var ExpressServer = /** @class */ (function () {
1131
882
  .replace(/\/+$/, '')); // remove trailing slash
1132
883
  };
1133
884
  // Resolve global prefix if enabled
1134
- var prefix = withGlobalPrefix && this.globalPrefix ? sanitize(this.globalPrefix) : '';
885
+ const prefix = withGlobalPrefix && this.globalPrefix ? sanitize(this.globalPrefix) : '';
1135
886
  // If path is invalid, default to prefix or root
1136
887
  if (typeof path !== 'string' || !path.trim()) {
1137
888
  return prefix || '/';
1138
889
  }
1139
890
  return sanitize(prefix + '/' + path);
1140
- };
1141
- ExpressServer.prototype.normalizeRouteForMetrics = function (req, res) {
1142
- var _a;
891
+ }
892
+ normalizeRouteForMetrics(req, res) {
1143
893
  // Prevent high cardinality metrics by normalizing routes
1144
- if ((_a = req === null || req === void 0 ? void 0 : req.route) === null || _a === void 0 ? void 0 : _a.path) {
894
+ if (req?.route?.path) {
1145
895
  // Use Express route pattern instead of actual URL
1146
896
  return req.route.path;
1147
897
  }
1148
898
  // Group common patterns
1149
899
  if (res.statusCode === 404)
1150
900
  return '/404';
1151
- var path = (req.path || 'unknown').split('?')[0];
901
+ const path = (req.path || 'unknown').split('?')[0];
1152
902
  // Replace IDs and UUIDs with placeholders
1153
903
  return path
1154
904
  .replace(/\/[0-9]+/g, '/:id')
1155
905
  .replace(/\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/g, '/:uuid');
1156
- };
906
+ }
1157
907
  /**
1158
908
  * Destroy all active connections (gracefully if possible).
1159
909
  * If a connection does not close cleanly, it will be force-destroyed.
1160
910
  */
1161
- ExpressServer.prototype.destroyConnections = function () {
1162
- return __awaiter(this, void 0, void 0, function () {
1163
- var total, timeoutMs;
1164
- return __generator(this, function (_a) {
1165
- switch (_a.label) {
1166
- case 0:
1167
- total = this.connections.size;
1168
- if (total === 0) {
1169
- getLogger().debug('No active connections to close');
1170
- return [2 /*return*/];
1171
- }
1172
- timeoutMs = 5000;
1173
- return [4 /*yield*/, Promise.race([
1174
- Promise.all(Array.from(this.connections).map(function (conn) {
1175
- return new Promise(function (resolve) {
1176
- conn.end(function () {
1177
- if (!conn.destroyed)
1178
- conn.destroy();
1179
- resolve();
1180
- });
1181
- conn.on('error', function () {
1182
- conn.destroy();
1183
- resolve();
1184
- });
1185
- });
1186
- })),
1187
- new Promise(function (resolve) { return setTimeout(resolve, timeoutMs); })
1188
- ])];
1189
- case 1:
1190
- _a.sent();
1191
- this.connections.clear();
1192
- getLogger().info("Closed ".concat(total, " active connections"));
1193
- return [2 /*return*/];
1194
- }
1195
- });
1196
- });
1197
- };
1198
- ExpressServer.prototype.validateHttpsFiles = function () {
1199
- return __awaiter(this, void 0, void 0, function () {
1200
- var _a;
1201
- return __generator(this, function (_b) {
1202
- switch (_b.label) {
1203
- case 0: return [4 /*yield*/, fileExists(this.config.https.key)];
1204
- case 1:
1205
- if (!(_b.sent())) {
1206
- getLogger().error("HTTPS key file not found: ".concat(this.config.https.key));
1207
- process.exit(1);
1208
- }
1209
- return [4 /*yield*/, fileExists(this.config.https.cert)];
1210
- case 2:
1211
- if (!(_b.sent())) {
1212
- getLogger().error("HTTPS cert file not found: ".concat(this.config.https.cert));
1213
- process.exit(1);
1214
- }
1215
- _a = this.config.https.ca;
1216
- if (!_a) return [3 /*break*/, 4];
1217
- return [4 /*yield*/, fileExists(this.config.https.ca)];
1218
- case 3:
1219
- _a = !(_b.sent());
1220
- _b.label = 4;
1221
- case 4:
1222
- if (_a) {
1223
- getLogger().error("HTTPS CA file not found: ".concat(this.config.https.ca));
1224
- process.exit(1);
1225
- }
1226
- return [2 /*return*/];
1227
- }
1228
- });
1229
- });
1230
- };
1231
- ExpressServer.isBuiltServerConfig = function (config) {
911
+ async destroyConnections() {
912
+ const total = this.connections.size;
913
+ if (total === 0) {
914
+ getLogger().debug('No active connections to close');
915
+ return;
916
+ }
917
+ const timeoutMs = 5000;
918
+ await Promise.race([
919
+ Promise.all(Array.from(this.connections).map(conn => new Promise(resolve => {
920
+ conn.end(() => {
921
+ if (!conn.destroyed)
922
+ conn.destroy();
923
+ resolve();
924
+ });
925
+ conn.on('error', () => {
926
+ conn.destroy();
927
+ resolve();
928
+ });
929
+ }))),
930
+ new Promise(resolve => setTimeout(resolve, timeoutMs))
931
+ ]);
932
+ this.connections.clear();
933
+ getLogger().info(`Closed ${total} active connections`);
934
+ }
935
+ async validateHttpsFiles() {
936
+ if (!(await fileExists(this.config.https.key))) {
937
+ getLogger().error(`HTTPS key file not found: ${this.config.https.key}`);
938
+ process.exit(1);
939
+ }
940
+ if (!(await fileExists(this.config.https.cert))) {
941
+ getLogger().error(`HTTPS cert file not found: ${this.config.https.cert}`);
942
+ process.exit(1);
943
+ }
944
+ if (this.config.https.ca && !(await fileExists(this.config.https.ca))) {
945
+ getLogger().error(`HTTPS CA file not found: ${this.config.https.ca}`);
946
+ process.exit(1);
947
+ }
948
+ }
949
+ static isBuiltServerConfig(config) {
1232
950
  if (config[BUILD_MARKER]) {
1233
951
  return true;
1234
952
  }
1235
953
  return false;
1236
- };
1237
- ExpressServer.optionalRequire = function (name) {
954
+ }
955
+ static optionalRequire(name) {
1238
956
  try {
1239
957
  return require(name);
1240
958
  }
1241
- catch (_a) {
959
+ catch {
1242
960
  return null;
1243
961
  }
1244
- };
1245
- return ExpressServer;
1246
- }());
1247
- export { ExpressServer };
1248
- //# sourceMappingURL=server.js.map
962
+ }
963
+ }