@catbee/utils 0.0.8-next.0 → 0.0.8-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -2
- package/build/esm/config.d.ts +122 -0
- package/build/esm/config.js +133 -0
- package/build/esm/config.js.map +1 -0
- package/build/esm/index.d.ts +27 -0
- package/build/esm/index.js +50 -0
- package/build/esm/index.js.map +1 -0
- package/build/esm/servers/server.builder.d.ts +508 -0
- package/build/esm/servers/server.builder.js +702 -0
- package/build/esm/servers/server.builder.js.map +1 -0
- package/build/esm/servers/server.d.ts +256 -0
- package/build/esm/servers/server.js +1248 -0
- package/build/esm/servers/server.js.map +1 -0
- package/build/esm/types/api-response.d.ts +152 -0
- package/build/esm/types/api-response.js +34 -0
- package/build/esm/types/api-response.js.map +1 -0
- package/build/esm/types/index.d.ts +125 -0
- package/build/esm/types/index.js +25 -0
- package/build/esm/types/index.js.map +1 -0
- package/build/esm/types/server.d.ts +268 -0
- package/build/esm/types/server.js +25 -0
- package/build/esm/types/server.js.map +1 -0
- package/build/esm/utils/array.utils.d.ts +168 -0
- package/build/esm/utils/array.utils.js +382 -0
- package/build/esm/utils/array.utils.js.map +1 -0
- package/build/esm/utils/async.utils.d.ts +265 -0
- package/build/esm/utils/async.utils.js +896 -0
- package/build/esm/utils/async.utils.js.map +1 -0
- package/build/esm/utils/cache.utils.d.ts +153 -0
- package/build/esm/utils/cache.utils.js +499 -0
- package/build/esm/utils/cache.utils.js.map +1 -0
- package/build/esm/utils/context-store.utils.d.ts +189 -0
- package/build/esm/utils/context-store.utils.js +307 -0
- package/build/esm/utils/context-store.utils.js.map +1 -0
- package/build/esm/utils/crypto.utils.d.ts +160 -0
- package/build/esm/utils/crypto.utils.js +375 -0
- package/build/esm/utils/crypto.utils.js.map +1 -0
- package/build/esm/utils/date.utils.d.ts +159 -0
- package/build/esm/utils/date.utils.js +389 -0
- package/build/esm/utils/date.utils.js.map +1 -0
- package/build/esm/utils/decorators.utils.d.ts +508 -0
- package/build/esm/utils/decorators.utils.js +1057 -0
- package/build/esm/utils/decorators.utils.js.map +1 -0
- package/build/esm/utils/dir.utils.d.ts +196 -0
- package/build/esm/utils/dir.utils.js +998 -0
- package/build/esm/utils/dir.utils.js.map +1 -0
- package/build/esm/utils/env.utils.d.ts +377 -0
- package/build/esm/utils/env.utils.js +873 -0
- package/build/esm/utils/env.utils.js.map +1 -0
- package/build/esm/utils/exception.utils.d.ts +230 -0
- package/build/esm/utils/exception.utils.js +559 -0
- package/build/esm/utils/exception.utils.js.map +1 -0
- package/build/esm/utils/fs.utils.d.ts +164 -0
- package/build/esm/utils/fs.utils.js +580 -0
- package/build/esm/utils/fs.utils.js.map +1 -0
- package/build/esm/utils/http-status-codes.d.ts +266 -0
- package/build/esm/utils/http-status-codes.js +295 -0
- package/build/esm/utils/http-status-codes.js.map +1 -0
- package/build/esm/utils/id.utils.d.ts +36 -0
- package/build/esm/utils/id.utils.js +87 -0
- package/build/esm/utils/id.utils.js.map +1 -0
- package/build/esm/utils/logger.utils.d.ts +160 -0
- package/build/esm/utils/logger.utils.js +341 -0
- package/build/esm/utils/logger.utils.js.map +1 -0
- package/build/esm/utils/middleware.utils.d.ts +100 -0
- package/build/esm/utils/middleware.utils.js +325 -0
- package/build/esm/utils/middleware.utils.js.map +1 -0
- package/build/esm/utils/obj.utils.d.ts +124 -0
- package/build/esm/utils/obj.utils.js +519 -0
- package/build/esm/utils/obj.utils.js.map +1 -0
- package/build/esm/utils/performance.utils.d.ts +136 -0
- package/build/esm/utils/performance.utils.js +376 -0
- package/build/esm/utils/performance.utils.js.map +1 -0
- package/build/esm/utils/request.utils.d.ts +86 -0
- package/build/esm/utils/request.utils.js +223 -0
- package/build/esm/utils/request.utils.js.map +1 -0
- package/build/esm/utils/response.utils.d.ts +163 -0
- package/build/esm/utils/response.utils.js +284 -0
- package/build/esm/utils/response.utils.js.map +1 -0
- package/build/esm/utils/stream.utils.d.ts +88 -0
- package/build/esm/utils/stream.utils.js +284 -0
- package/build/esm/utils/stream.utils.js.map +1 -0
- package/build/esm/utils/string.utils.d.ts +93 -0
- package/build/esm/utils/string.utils.js +169 -0
- package/build/esm/utils/string.utils.js.map +1 -0
- package/build/esm/utils/type.utils.d.ts +90 -0
- package/build/esm/utils/type.utils.js +190 -0
- package/build/esm/utils/type.utils.js.map +1 -0
- package/build/esm/utils/url.utils.d.ts +141 -0
- package/build/esm/utils/url.utils.js +381 -0
- package/build/esm/utils/url.utils.js.map +1 -0
- package/build/esm/utils/validate.utils.d.ts +177 -0
- package/build/esm/utils/validate.utils.js +336 -0
- package/build/esm/utils/validate.utils.js.map +1 -0
- package/build/esnext/config.d.ts +122 -0
- package/build/esnext/config.js +133 -0
- package/build/esnext/config.js.map +1 -0
- package/build/esnext/index.d.ts +27 -0
- package/build/esnext/index.js +50 -0
- package/build/esnext/index.js.map +1 -0
- package/build/esnext/servers/server.builder.d.ts +508 -0
- package/build/esnext/servers/server.builder.js +657 -0
- package/build/esnext/servers/server.builder.js.map +1 -0
- package/build/esnext/servers/server.d.ts +256 -0
- package/build/esnext/servers/server.js +955 -0
- package/build/esnext/servers/server.js.map +1 -0
- package/build/esnext/types/api-response.d.ts +152 -0
- package/build/esnext/types/api-response.js +34 -0
- package/build/esnext/types/api-response.js.map +1 -0
- package/build/esnext/types/index.d.ts +125 -0
- package/build/esnext/types/index.js +25 -0
- package/build/esnext/types/index.js.map +1 -0
- package/build/esnext/types/server.d.ts +268 -0
- package/build/esnext/types/server.js +25 -0
- package/build/esnext/types/server.js.map +1 -0
- package/build/esnext/utils/array.utils.d.ts +168 -0
- package/build/esnext/utils/array.utils.js +345 -0
- package/build/esnext/utils/array.utils.js.map +1 -0
- package/build/esnext/utils/async.utils.d.ts +265 -0
- package/build/esnext/utils/async.utils.js +620 -0
- package/build/esnext/utils/async.utils.js.map +1 -0
- package/build/esnext/utils/cache.utils.d.ts +153 -0
- package/build/esnext/utils/cache.utils.js +293 -0
- package/build/esnext/utils/cache.utils.js.map +1 -0
- package/build/esnext/utils/context-store.utils.d.ts +189 -0
- package/build/esnext/utils/context-store.utils.js +289 -0
- package/build/esnext/utils/context-store.utils.js.map +1 -0
- package/build/esnext/utils/crypto.utils.d.ts +160 -0
- package/build/esnext/utils/crypto.utils.js +276 -0
- package/build/esnext/utils/crypto.utils.js.map +1 -0
- package/build/esnext/utils/date.utils.d.ts +159 -0
- package/build/esnext/utils/date.utils.js +384 -0
- package/build/esnext/utils/date.utils.js.map +1 -0
- package/build/esnext/utils/decorators.utils.d.ts +508 -0
- package/build/esnext/utils/decorators.utils.js +877 -0
- package/build/esnext/utils/decorators.utils.js.map +1 -0
- package/build/esnext/utils/dir.utils.d.ts +196 -0
- package/build/esnext/utils/dir.utils.js +477 -0
- package/build/esnext/utils/dir.utils.js.map +1 -0
- package/build/esnext/utils/env.utils.d.ts +377 -0
- package/build/esnext/utils/env.utils.js +785 -0
- package/build/esnext/utils/env.utils.js.map +1 -0
- package/build/esnext/utils/exception.utils.d.ts +230 -0
- package/build/esnext/utils/exception.utils.js +381 -0
- package/build/esnext/utils/exception.utils.js.map +1 -0
- package/build/esnext/utils/fs.utils.d.ts +164 -0
- package/build/esnext/utils/fs.utils.js +349 -0
- package/build/esnext/utils/fs.utils.js.map +1 -0
- package/build/esnext/utils/http-status-codes.d.ts +266 -0
- package/build/esnext/utils/http-status-codes.js +295 -0
- package/build/esnext/utils/http-status-codes.js.map +1 -0
- package/build/esnext/utils/id.utils.d.ts +36 -0
- package/build/esnext/utils/id.utils.js +84 -0
- package/build/esnext/utils/id.utils.js.map +1 -0
- package/build/esnext/utils/logger.utils.d.ts +160 -0
- package/build/esnext/utils/logger.utils.js +300 -0
- package/build/esnext/utils/logger.utils.js.map +1 -0
- package/build/esnext/utils/middleware.utils.d.ts +100 -0
- package/build/esnext/utils/middleware.utils.js +236 -0
- package/build/esnext/utils/middleware.utils.js.map +1 -0
- package/build/esnext/utils/obj.utils.d.ts +124 -0
- package/build/esnext/utils/obj.utils.js +413 -0
- package/build/esnext/utils/obj.utils.js.map +1 -0
- package/build/esnext/utils/performance.utils.d.ts +136 -0
- package/build/esnext/utils/performance.utils.js +271 -0
- package/build/esnext/utils/performance.utils.js.map +1 -0
- package/build/esnext/utils/request.utils.d.ts +86 -0
- package/build/esnext/utils/request.utils.js +186 -0
- package/build/esnext/utils/request.utils.js.map +1 -0
- package/build/esnext/utils/response.utils.d.ts +163 -0
- package/build/esnext/utils/response.utils.js +247 -0
- package/build/esnext/utils/response.utils.js.map +1 -0
- package/build/esnext/utils/stream.utils.d.ts +88 -0
- package/build/esnext/utils/stream.utils.js +210 -0
- package/build/esnext/utils/stream.utils.js.map +1 -0
- package/build/esnext/utils/string.utils.d.ts +93 -0
- package/build/esnext/utils/string.utils.js +165 -0
- package/build/esnext/utils/string.utils.js.map +1 -0
- package/build/esnext/utils/type.utils.d.ts +90 -0
- package/build/esnext/utils/type.utils.js +187 -0
- package/build/esnext/utils/type.utils.js.map +1 -0
- package/build/esnext/utils/url.utils.d.ts +141 -0
- package/build/esnext/utils/url.utils.js +304 -0
- package/build/esnext/utils/url.utils.js.map +1 -0
- package/build/esnext/utils/validate.utils.d.ts +177 -0
- package/build/esnext/utils/validate.utils.js +320 -0
- package/build/esnext/utils/validate.utils.js.map +1 -0
- package/build/src/config.d.ts +122 -0
- package/build/src/config.js +138 -0
- package/build/src/config.js.map +1 -0
- package/build/src/index.d.ts +27 -0
- package/build/src/index.js +69 -0
- package/build/src/index.js.map +1 -0
- package/build/src/servers/server.builder.d.ts +508 -0
- package/build/src/servers/server.builder.js +661 -0
- package/build/src/servers/server.builder.js.map +1 -0
- package/build/src/servers/server.d.ts +256 -0
- package/build/src/servers/server.js +962 -0
- package/build/src/servers/server.js.map +1 -0
- package/build/src/types/api-response.d.ts +152 -0
- package/build/src/types/api-response.js +37 -0
- package/build/src/types/api-response.js.map +1 -0
- package/build/src/types/index.d.ts +125 -0
- package/build/src/types/index.js +26 -0
- package/build/src/types/index.js.map +1 -0
- package/build/src/types/server.d.ts +268 -0
- package/build/src/types/server.js +26 -0
- package/build/src/types/server.js.map +1 -0
- package/build/src/utils/array.utils.d.ts +168 -0
- package/build/src/utils/array.utils.js +364 -0
- package/build/src/utils/array.utils.js.map +1 -0
- package/build/src/utils/async.utils.d.ts +265 -0
- package/build/src/utils/async.utils.js +641 -0
- package/build/src/utils/async.utils.js.map +1 -0
- package/build/src/utils/cache.utils.d.ts +153 -0
- package/build/src/utils/cache.utils.js +297 -0
- package/build/src/utils/cache.utils.js.map +1 -0
- package/build/src/utils/context-store.utils.d.ts +189 -0
- package/build/src/utils/context-store.utils.js +296 -0
- package/build/src/utils/context-store.utils.js.map +1 -0
- package/build/src/utils/crypto.utils.d.ts +160 -0
- package/build/src/utils/crypto.utils.js +293 -0
- package/build/src/utils/crypto.utils.js.map +1 -0
- package/build/src/utils/date.utils.d.ts +159 -0
- package/build/src/utils/date.utils.js +396 -0
- package/build/src/utils/date.utils.js.map +1 -0
- package/build/src/utils/decorators.utils.d.ts +508 -0
- package/build/src/utils/decorators.utils.js +896 -0
- package/build/src/utils/decorators.utils.js.map +1 -0
- package/build/src/utils/dir.utils.d.ts +196 -0
- package/build/src/utils/dir.utils.js +501 -0
- package/build/src/utils/dir.utils.js.map +1 -0
- package/build/src/utils/env.utils.d.ts +377 -0
- package/build/src/utils/env.utils.js +789 -0
- package/build/src/utils/env.utils.js.map +1 -0
- package/build/src/utils/exception.utils.d.ts +230 -0
- package/build/src/utils/exception.utils.js +407 -0
- package/build/src/utils/exception.utils.js.map +1 -0
- package/build/src/utils/fs.utils.d.ts +164 -0
- package/build/src/utils/fs.utils.js +372 -0
- package/build/src/utils/fs.utils.js.map +1 -0
- package/build/src/utils/http-status-codes.d.ts +266 -0
- package/build/src/utils/http-status-codes.js +298 -0
- package/build/src/utils/http-status-codes.js.map +1 -0
- package/build/src/utils/id.utils.d.ts +36 -0
- package/build/src/utils/id.utils.js +91 -0
- package/build/src/utils/id.utils.js.map +1 -0
- package/build/src/utils/logger.utils.d.ts +160 -0
- package/build/src/utils/logger.utils.js +346 -0
- package/build/src/utils/logger.utils.js.map +1 -0
- package/build/src/utils/middleware.utils.d.ts +100 -0
- package/build/src/utils/middleware.utils.js +244 -0
- package/build/src/utils/middleware.utils.js.map +1 -0
- package/build/src/utils/obj.utils.d.ts +124 -0
- package/build/src/utils/obj.utils.js +429 -0
- package/build/src/utils/obj.utils.js.map +1 -0
- package/build/src/utils/performance.utils.d.ts +136 -0
- package/build/src/utils/performance.utils.js +278 -0
- package/build/src/utils/performance.utils.js.map +1 -0
- package/build/src/utils/request.utils.d.ts +86 -0
- package/build/src/utils/request.utils.js +195 -0
- package/build/src/utils/request.utils.js.map +1 -0
- package/build/src/utils/response.utils.d.ts +163 -0
- package/build/src/utils/response.utils.js +260 -0
- package/build/src/utils/response.utils.js.map +1 -0
- package/build/src/utils/stream.utils.d.ts +88 -0
- package/build/src/utils/stream.utils.js +218 -0
- package/build/src/utils/stream.utils.js.map +1 -0
- package/build/src/utils/string.utils.d.ts +93 -0
- package/build/src/utils/string.utils.js +179 -0
- package/build/src/utils/string.utils.js.map +1 -0
- package/build/src/utils/type.utils.d.ts +90 -0
- package/build/src/utils/type.utils.js +196 -0
- package/build/src/utils/type.utils.js.map +1 -0
- package/build/src/utils/url.utils.d.ts +141 -0
- package/build/src/utils/url.utils.js +317 -0
- package/build/src/utils/url.utils.js.map +1 -0
- package/build/src/utils/validate.utils.d.ts +177 -0
- package/build/src/utils/validate.utils.js +345 -0
- package/build/src/utils/validate.utils.js.map +1 -0
- package/package.json +66 -12
- package/build/index.cjs +0 -6888
- package/build/index.d.ts +0 -5242
- package/build/index.mjs +0 -6615
|
@@ -0,0 +1,1248 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The MIT License
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2025 Catbee Technologies. https://catbee-utils.npm.hprasath.com/license
|
|
5
|
+
*
|
|
6
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
7
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
8
|
+
* in the Software without restriction, including without limitation the rights
|
|
9
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
10
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
11
|
+
* furnished to do so, subject to the following conditions:
|
|
12
|
+
*
|
|
13
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
14
|
+
* copies or substantial portions of the Software.
|
|
15
|
+
*
|
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
17
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
18
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
19
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
20
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
21
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
22
|
+
* SOFTWARE.
|
|
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
|
+
import express from 'express';
|
|
97
|
+
import https from 'https';
|
|
98
|
+
import { HttpStatusCodes } from '../utils/http-status-codes';
|
|
99
|
+
import { createFinalErrorResponse, SuccessResponse } from '../utils/response.utils';
|
|
100
|
+
import { errorHandler, requestId, responseTime, setupRequestContext, timeout } from '../utils/middleware.utils';
|
|
101
|
+
import { Env } from '../utils/env.utils';
|
|
102
|
+
import { getLogger } from '../utils/logger.utils';
|
|
103
|
+
import { InternalServerErrorException, ServiceUnavailableException } from '../utils/exception.utils';
|
|
104
|
+
import { NotFoundException } from '../utils/exception.utils';
|
|
105
|
+
import fs from 'fs';
|
|
106
|
+
import { defaultServerConfig } from '../config';
|
|
107
|
+
import { deepObjMerge } from '../utils/obj.utils';
|
|
108
|
+
import { fileExists } from '../utils/fs.utils';
|
|
109
|
+
import { BUILD_MARKER } from './server.builder';
|
|
110
|
+
import { isPort } from '../utils/validate.utils';
|
|
111
|
+
/**
|
|
112
|
+
* Production-ready Express server with enterprise features.
|
|
113
|
+
*
|
|
114
|
+
* Core Features:
|
|
115
|
+
* - Security: Helmet, CORS, rate limiting, timeouts
|
|
116
|
+
* - Monitoring: Request logs, metrics, health checks
|
|
117
|
+
* - Performance: Compression, caching, static files
|
|
118
|
+
* - Reliability: Graceful shutdown, error handling
|
|
119
|
+
* - Developer UX: OpenAPI docs, debugging tools
|
|
120
|
+
* - Extensibility: Hooks, middleware, custom routes
|
|
121
|
+
*
|
|
122
|
+
* Designed for microservices and production workloads.
|
|
123
|
+
* Includes K8s readiness probes and zero-downtime support.
|
|
124
|
+
*/
|
|
125
|
+
var ExpressServer = /** @class */ (function () {
|
|
126
|
+
/**
|
|
127
|
+
* Initializes server with intelligent defaults and security best practices.
|
|
128
|
+
* All settings can be customized via config and hooks.
|
|
129
|
+
*
|
|
130
|
+
* Default Security:
|
|
131
|
+
* - Secure headers (Helmet)
|
|
132
|
+
* - Rate limiting
|
|
133
|
+
* - Request timeouts
|
|
134
|
+
* - Body size limits
|
|
135
|
+
* - CORS protection
|
|
136
|
+
*
|
|
137
|
+
* Default Monitoring:
|
|
138
|
+
* - Request/Response logging
|
|
139
|
+
* - Prometheus metrics
|
|
140
|
+
* - Health checks
|
|
141
|
+
* - Request tracing
|
|
142
|
+
*/
|
|
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 = [];
|
|
160
|
+
if (ExpressServer.isBuiltServerConfig(config)) {
|
|
161
|
+
this.config = config;
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
// Deep merge config with user overrides
|
|
165
|
+
this.config = deepObjMerge({}, defaultServerConfig, config);
|
|
166
|
+
}
|
|
167
|
+
if (!isPort(this.config.port)) {
|
|
168
|
+
getLogger().error("Port must be a valid number between 1 and 65535, got: ".concat(this.config.port));
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
171
|
+
// 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');
|
|
175
|
+
if (!client) {
|
|
176
|
+
getLogger().error({ command: 'npm install prom-client' }, 'prom-client is required for metrics but not installed. Please add it to your dependencies');
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
this.register = new client.Registry();
|
|
180
|
+
// Initialize Prometheus metrics with sanitized names
|
|
181
|
+
this.requestCounter = new client.Counter({
|
|
182
|
+
name: "".concat(safeAppName, "_http_requests_total"),
|
|
183
|
+
help: 'Total HTTP requests',
|
|
184
|
+
labelNames: ['method', 'route', 'status'],
|
|
185
|
+
registers: [this.register]
|
|
186
|
+
});
|
|
187
|
+
this.routeTimings = new client.Histogram({
|
|
188
|
+
name: "".concat(safeAppName, "_http_request_duration_seconds"),
|
|
189
|
+
help: 'Duration of HTTP requests by route',
|
|
190
|
+
labelNames: ['method', 'route', 'status'],
|
|
191
|
+
buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10],
|
|
192
|
+
registers: [this.register]
|
|
193
|
+
});
|
|
194
|
+
this.requestSizes = new client.Histogram({
|
|
195
|
+
name: "".concat(safeAppName, "_http_request_size_bytes"),
|
|
196
|
+
help: 'Size of HTTP request bodies',
|
|
197
|
+
labelNames: ['method', 'route'],
|
|
198
|
+
buckets: [100, 1000, 10000, 100000, 1000000],
|
|
199
|
+
registers: [this.register]
|
|
200
|
+
});
|
|
201
|
+
this.clientIPs = new client.Counter({
|
|
202
|
+
name: "".concat(safeAppName, "_http_client_ip_total"),
|
|
203
|
+
help: 'Client IP request counter',
|
|
204
|
+
labelNames: ['ip', 'method'],
|
|
205
|
+
registers: [this.register]
|
|
206
|
+
});
|
|
207
|
+
// Default system metrics (CPU, memory, event loop lag, etc.)
|
|
208
|
+
client.collectDefaultMetrics({
|
|
209
|
+
register: this.register,
|
|
210
|
+
prefix: "".concat(safeAppName, "_")
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
// 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));
|
|
216
|
+
}
|
|
217
|
+
// 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);
|
|
219
|
+
this.hooks = hooks;
|
|
220
|
+
this.app = express();
|
|
221
|
+
this.rootRouter = express.Router();
|
|
222
|
+
// Store initialization promise to prevent race conditions with start()
|
|
223
|
+
this.initPromise = this.initialize();
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Execute a lifecycle hook safely with comprehensive error handling.
|
|
227
|
+
* Prevents hook failures from crashing the server while logging issues.
|
|
228
|
+
*
|
|
229
|
+
* @param hook Name of the lifecycle hook to execute
|
|
230
|
+
* @param args Arguments to pass to the hook function
|
|
231
|
+
*/
|
|
232
|
+
ExpressServer.prototype.runHook = function (hook) {
|
|
233
|
+
var args = [];
|
|
234
|
+
for (var _i = 1; _i < arguments.length; _i++) {
|
|
235
|
+
args[_i - 1] = arguments[_i];
|
|
236
|
+
}
|
|
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
|
+
};
|
|
259
|
+
/**
|
|
260
|
+
* Initialize the Express server with middleware and routes.
|
|
261
|
+
*/
|
|
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
|
+
};
|
|
287
|
+
/**
|
|
288
|
+
* Configure and register all middlewares in the optimal order.
|
|
289
|
+
*
|
|
290
|
+
* Middleware Order (CRITICAL - don't change without understanding implications):
|
|
291
|
+
* 1. Basic server configuration (trust proxy, x-powered-by)
|
|
292
|
+
* 2. Request ID generation (for tracing)
|
|
293
|
+
* 3. Request context setup (for logging correlation)
|
|
294
|
+
* 4. Timeout protection (prevents hanging requests)
|
|
295
|
+
* 5. Response time tracking (for performance monitoring)
|
|
296
|
+
* 6. Request logging (after ID/context setup)
|
|
297
|
+
* 7. Custom request hooks
|
|
298
|
+
* 8. Security middleware (rate limiting, CORS, Helmet)
|
|
299
|
+
* 9. Response compression
|
|
300
|
+
* 10. Static file serving
|
|
301
|
+
* 11. Request parsing (body parsing, cookies)
|
|
302
|
+
* 12. API documentation (OpenAPI)
|
|
303
|
+
* 13. Global headers
|
|
304
|
+
* 14. Custom response hooks
|
|
305
|
+
*/
|
|
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*/];
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
});
|
|
589
|
+
};
|
|
590
|
+
/**
|
|
591
|
+
* Configure server routes and error handling.
|
|
592
|
+
* Sets up in following order:
|
|
593
|
+
*
|
|
594
|
+
* 1. Built-in routes (health, metrics)
|
|
595
|
+
* 2. Application routes
|
|
596
|
+
* 3. 404 handler
|
|
597
|
+
* 4. Error handler
|
|
598
|
+
*/
|
|
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
|
+
}); });
|
|
676
|
+
}
|
|
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);
|
|
697
|
+
}
|
|
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);
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
return [2 /*return*/];
|
|
708
|
+
});
|
|
709
|
+
});
|
|
710
|
+
};
|
|
711
|
+
/**
|
|
712
|
+
* Register a new health check function for monitoring service dependencies.
|
|
713
|
+
*
|
|
714
|
+
* Health checks are executed when the health endpoint is accessed and
|
|
715
|
+
* help determine if the service is ready to handle requests.
|
|
716
|
+
*
|
|
717
|
+
* Examples:
|
|
718
|
+
* - Database connectivity
|
|
719
|
+
* - External service availability
|
|
720
|
+
* - File system access
|
|
721
|
+
* - Memory/CPU usage checks
|
|
722
|
+
*
|
|
723
|
+
* @param name Unique identifier for the check (used in detailed responses)
|
|
724
|
+
* @param check Function returning boolean or Promise<boolean> indicating health
|
|
725
|
+
* @returns This instance for method chaining
|
|
726
|
+
*/
|
|
727
|
+
ExpressServer.prototype.registerHealthCheck = function (name, check) {
|
|
728
|
+
this.healthChecks.push({ name: name, check: check });
|
|
729
|
+
return this;
|
|
730
|
+
};
|
|
731
|
+
/**
|
|
732
|
+
* Get the underlying Express application instance.
|
|
733
|
+
* Use this for advanced Express features not exposed by this wrapper.
|
|
734
|
+
*
|
|
735
|
+
* @returns The raw Express app instance
|
|
736
|
+
*/
|
|
737
|
+
ExpressServer.prototype.getApp = function () {
|
|
738
|
+
return this.app;
|
|
739
|
+
};
|
|
740
|
+
/**
|
|
741
|
+
* Get the active HTTP/HTTPS server instance.
|
|
742
|
+
* Returns null if the server is not currently running.
|
|
743
|
+
*
|
|
744
|
+
* @returns The HTTP/HTTPS server instance or null
|
|
745
|
+
*/
|
|
746
|
+
ExpressServer.prototype.getServer = function () {
|
|
747
|
+
return this.server;
|
|
748
|
+
};
|
|
749
|
+
/**
|
|
750
|
+
* Start the HTTP server and begin listening for requests.
|
|
751
|
+
*
|
|
752
|
+
* This method:
|
|
753
|
+
* - Executes beforeStart hooks
|
|
754
|
+
* - Binds to the configured host/port
|
|
755
|
+
* - Sets up error handling for startup failures
|
|
756
|
+
* - Executes afterStart hooks on success
|
|
757
|
+
* - Logs startup information
|
|
758
|
+
*
|
|
759
|
+
* @returns Promise resolving to the running HTTP server instance
|
|
760
|
+
* @throws Error if server fails to start or port is already in use
|
|
761
|
+
*/
|
|
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
|
+
})];
|
|
842
|
+
}
|
|
843
|
+
});
|
|
844
|
+
});
|
|
845
|
+
};
|
|
846
|
+
/**
|
|
847
|
+
* Stop the HTTP server gracefully.
|
|
848
|
+
*
|
|
849
|
+
* This method:
|
|
850
|
+
* - Executes beforeStop hooks
|
|
851
|
+
* - Stops accepting new connections
|
|
852
|
+
* - Waits for existing connections to finish
|
|
853
|
+
* - Closes the server
|
|
854
|
+
* - Executes afterStop hooks
|
|
855
|
+
* - Logs shutdown information
|
|
856
|
+
*
|
|
857
|
+
* Graceful shutdown ensures:
|
|
858
|
+
* - No requests are dropped
|
|
859
|
+
* - Resources are properly cleaned up
|
|
860
|
+
* - Monitoring systems are notified
|
|
861
|
+
*/
|
|
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*/];
|
|
930
|
+
}
|
|
931
|
+
});
|
|
932
|
+
});
|
|
933
|
+
};
|
|
934
|
+
/**
|
|
935
|
+
* Enable graceful shutdown on OS signals for production deployment.
|
|
936
|
+
*
|
|
937
|
+
* This is essential for:
|
|
938
|
+
* - Container orchestration (Docker, Kubernetes)
|
|
939
|
+
* - Process managers (PM2, systemd)
|
|
940
|
+
* - Load balancer health checks
|
|
941
|
+
* - Zero-downtime deployments
|
|
942
|
+
*
|
|
943
|
+
* @param signals Array of process signals to listen for (default: SIGINT, SIGTERM)
|
|
944
|
+
*/
|
|
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*/];
|
|
981
|
+
}
|
|
982
|
+
});
|
|
983
|
+
}); });
|
|
984
|
+
});
|
|
985
|
+
return this;
|
|
986
|
+
};
|
|
987
|
+
/**
|
|
988
|
+
* Set an externally created base router.
|
|
989
|
+
* This will override the internal rootRouter.
|
|
990
|
+
*/
|
|
991
|
+
ExpressServer.prototype.setBaseRouter = function (router) {
|
|
992
|
+
this.externalRouter = router;
|
|
993
|
+
return this;
|
|
994
|
+
};
|
|
995
|
+
/**
|
|
996
|
+
* Create and register a new router (only used if not injecting one externally - use `setBaseRouter` instead).
|
|
997
|
+
*/
|
|
998
|
+
ExpressServer.prototype.createRouter = function (prefix) {
|
|
999
|
+
if (prefix === void 0) { prefix = ''; }
|
|
1000
|
+
var router = express.Router();
|
|
1001
|
+
var path = this.normalizePath(prefix, true);
|
|
1002
|
+
this.rootRouter.use(path, router);
|
|
1003
|
+
return router;
|
|
1004
|
+
};
|
|
1005
|
+
/**
|
|
1006
|
+
* Register a new route handler with support for multiple HTTP methods.
|
|
1007
|
+
* The route is automatically registered under the globalPrefix if set.
|
|
1008
|
+
*
|
|
1009
|
+
* @param methods Array of HTTP methods (get, post, put, delete, etc.)
|
|
1010
|
+
* @param path Route path with Express path patterns support
|
|
1011
|
+
* @param handlers One or more Express request handlers (middleware + final handler)
|
|
1012
|
+
* @returns This instance for method chaining
|
|
1013
|
+
*/
|
|
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 = {
|
|
1021
|
+
get: this.app.get.bind(this.app),
|
|
1022
|
+
post: this.app.post.bind(this.app),
|
|
1023
|
+
put: this.app.put.bind(this.app),
|
|
1024
|
+
delete: this.app.delete.bind(this.app),
|
|
1025
|
+
patch: this.app.patch.bind(this.app),
|
|
1026
|
+
options: this.app.options.bind(this.app),
|
|
1027
|
+
head: this.app.head.bind(this.app)
|
|
1028
|
+
};
|
|
1029
|
+
methods.forEach(function (m) {
|
|
1030
|
+
var fn = methodMap[m];
|
|
1031
|
+
if (fn) {
|
|
1032
|
+
fn.apply(void 0, __spreadArray([fullPath], __read(handlers), false));
|
|
1033
|
+
}
|
|
1034
|
+
else {
|
|
1035
|
+
throw new Error("Unsupported HTTP method: ".concat(m));
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
1038
|
+
return this;
|
|
1039
|
+
};
|
|
1040
|
+
/**
|
|
1041
|
+
* Register custom middleware with optional path restriction.
|
|
1042
|
+
*
|
|
1043
|
+
* Use this for:
|
|
1044
|
+
* - Adding authentication to specific routes
|
|
1045
|
+
* - Custom logging or validation
|
|
1046
|
+
* - Request transformation
|
|
1047
|
+
* - Third-party middleware integration
|
|
1048
|
+
*
|
|
1049
|
+
* @param path Optional path prefix or middleware function if no path
|
|
1050
|
+
* @param middleware Middleware handler (required if path is provided)
|
|
1051
|
+
* @returns This instance for method chaining
|
|
1052
|
+
*/
|
|
1053
|
+
ExpressServer.prototype.registerMiddleware = function (path, middleware) {
|
|
1054
|
+
if (typeof path === 'string') {
|
|
1055
|
+
var normalizedPath = this.normalizePath(path);
|
|
1056
|
+
if (normalizedPath) {
|
|
1057
|
+
this.app.use(normalizedPath, middleware);
|
|
1058
|
+
}
|
|
1059
|
+
else {
|
|
1060
|
+
this.app.use(middleware);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
else {
|
|
1064
|
+
this.app.use(path);
|
|
1065
|
+
}
|
|
1066
|
+
return this;
|
|
1067
|
+
};
|
|
1068
|
+
/**
|
|
1069
|
+
* Register one or more middleware functions to be applied globally.
|
|
1070
|
+
* This is a simpler alternative to registerMiddleware when you just want
|
|
1071
|
+
* to add middleware without path restrictions.
|
|
1072
|
+
*
|
|
1073
|
+
* @param middlewares One or more Express middleware functions
|
|
1074
|
+
* @returns This instance for method chaining
|
|
1075
|
+
*/
|
|
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);
|
|
1084
|
+
});
|
|
1085
|
+
return this;
|
|
1086
|
+
};
|
|
1087
|
+
/**
|
|
1088
|
+
* Get Prometheus registry (to add custom counters/histograms)
|
|
1089
|
+
*
|
|
1090
|
+
* @return {*} {client.Registry}
|
|
1091
|
+
*/
|
|
1092
|
+
ExpressServer.prototype.getMetricsRegistry = function () {
|
|
1093
|
+
var _a;
|
|
1094
|
+
if (!((_a = this.config.metrics) === null || _a === void 0 ? void 0 : _a.enable)) {
|
|
1095
|
+
throw new Error('Metrics are not enabled in the server configuration');
|
|
1096
|
+
}
|
|
1097
|
+
return this.register;
|
|
1098
|
+
};
|
|
1099
|
+
/**
|
|
1100
|
+
* Get server configuration
|
|
1101
|
+
*
|
|
1102
|
+
* @return {*} {ServerConfig}
|
|
1103
|
+
*/
|
|
1104
|
+
ExpressServer.prototype.getConfig = function () {
|
|
1105
|
+
return this.config;
|
|
1106
|
+
};
|
|
1107
|
+
/**
|
|
1108
|
+
* Wait until server initialization (middleware + routes) has completed.
|
|
1109
|
+
* Useful for integration tests that inspect app before starting.
|
|
1110
|
+
*/
|
|
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) {
|
|
1126
|
+
return ('/' +
|
|
1127
|
+
p
|
|
1128
|
+
.trim()
|
|
1129
|
+
.replace(/^\/+/, '') // remove leading slashes
|
|
1130
|
+
.replace(/\/{2,}/g, '/') // collapse multiple slashes
|
|
1131
|
+
.replace(/\/+$/, '')); // remove trailing slash
|
|
1132
|
+
};
|
|
1133
|
+
// Resolve global prefix if enabled
|
|
1134
|
+
var prefix = withGlobalPrefix && this.globalPrefix ? sanitize(this.globalPrefix) : '';
|
|
1135
|
+
// If path is invalid, default to prefix or root
|
|
1136
|
+
if (typeof path !== 'string' || !path.trim()) {
|
|
1137
|
+
return prefix || '/';
|
|
1138
|
+
}
|
|
1139
|
+
return sanitize(prefix + '/' + path);
|
|
1140
|
+
};
|
|
1141
|
+
ExpressServer.prototype.normalizeRouteForMetrics = function (req, res) {
|
|
1142
|
+
var _a;
|
|
1143
|
+
// 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) {
|
|
1145
|
+
// Use Express route pattern instead of actual URL
|
|
1146
|
+
return req.route.path;
|
|
1147
|
+
}
|
|
1148
|
+
// Group common patterns
|
|
1149
|
+
if (res.statusCode === 404)
|
|
1150
|
+
return '/404';
|
|
1151
|
+
var path = (req.path || 'unknown').split('?')[0];
|
|
1152
|
+
// Replace IDs and UUIDs with placeholders
|
|
1153
|
+
return path
|
|
1154
|
+
.replace(/\/[0-9]+/g, '/:id')
|
|
1155
|
+
.replace(/\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/g, '/:uuid');
|
|
1156
|
+
};
|
|
1157
|
+
/**
|
|
1158
|
+
* Destroy all active connections (gracefully if possible).
|
|
1159
|
+
* If a connection does not close cleanly, it will be force-destroyed.
|
|
1160
|
+
*/
|
|
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) {
|
|
1232
|
+
if (config[BUILD_MARKER]) {
|
|
1233
|
+
return true;
|
|
1234
|
+
}
|
|
1235
|
+
return false;
|
|
1236
|
+
};
|
|
1237
|
+
ExpressServer.optionalRequire = function (name) {
|
|
1238
|
+
try {
|
|
1239
|
+
return require(name);
|
|
1240
|
+
}
|
|
1241
|
+
catch (_a) {
|
|
1242
|
+
return null;
|
|
1243
|
+
}
|
|
1244
|
+
};
|
|
1245
|
+
return ExpressServer;
|
|
1246
|
+
}());
|
|
1247
|
+
export { ExpressServer };
|
|
1248
|
+
//# sourceMappingURL=server.js.map
|