@catbee/utils 2.0.3 → 2.0.4

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/server/index.cjs CHANGED
@@ -305,42 +305,6 @@ var ServerConfigBuilder = class {
305
305
  return this.setEnabled("requestLogging", false);
306
306
  }
307
307
  /**
308
- * Configures server metrics collection and endpoints.
309
- *
310
- * @param opts - Metrics configuration options
311
- * @returns The builder instance for chaining
312
- * @default - { enable: false, path: '/metrics', withGlobalPrefix: false }
313
- *
314
- * @example
315
- * ```typescript
316
- * builder.withMetrics({
317
- * enable: true,
318
- * path: '/metrics'
319
- * })
320
- * ```
321
- */
322
- withMetrics(opts) {
323
- this.mergeConfig("metrics", opts);
324
- return this;
325
- }
326
- /**
327
- * Enables Prometheus metrics collection and endpoint
328
- *
329
- * @param opts - Optional metrics configuration
330
- * @returns The builder instance for chaining
331
- */
332
- enableMetrics(opts = {}) {
333
- return this.setEnabled("metrics", true, opts);
334
- }
335
- /**
336
- * Disables Prometheus metrics
337
- *
338
- * @returns The builder instance for chaining
339
- */
340
- disableMetrics() {
341
- return this.setEnabled("metrics", false);
342
- }
343
- /**
344
308
  * Configures server health check endpoint.
345
309
  *
346
310
  * @param opts - Health check configuration options
@@ -360,6 +324,19 @@ var ServerConfigBuilder = class {
360
324
  return this;
361
325
  }
362
326
  /**
327
+ * Enables health check endpoint with default or custom settings
328
+ * @param opts - Optional health check configuration
329
+ * @returns The builder instance for chaining
330
+ *
331
+ * @example
332
+ * ```typescript
333
+ * builder.disableHealthCheck()
334
+ * ```
335
+ */
336
+ disableHealthCheck() {
337
+ return this.setEnabled("healthCheck", false);
338
+ }
339
+ /**
363
340
  * Configures OpenAPI/Swagger documentation for the API.
364
341
  *
365
342
  * @param opts - OpenAPI configuration options
@@ -370,7 +347,7 @@ var ServerConfigBuilder = class {
370
347
  * ```typescript
371
348
  * builder.withOpenApi({
372
349
  * enable: true,
373
- * path: '/api-docs',
350
+ * mountPath: '/api-docs',
374
351
  * filePath: './openapi.yaml'
375
352
  * })
376
353
  * ```
@@ -710,15 +687,12 @@ var DependencyErrors = {
710
687
  compression: getDependencyErrorMessage("compression"),
711
688
  "express-rate-limit": getDependencyErrorMessage("express-rate-limit"),
712
689
  "cookie-parser": getDependencyErrorMessage("cookie-parser"),
713
- "@scalar/express-api-reference": getDependencyErrorMessage("@scalar/express-api-reference"),
714
- "prom-client": getDependencyErrorMessage("prom-client")
690
+ "@scalar/express-api-reference": getDependencyErrorMessage("@scalar/express-api-reference")
715
691
  };
716
692
  var ExpressServer = class {
717
693
  static {
718
694
  __name(this, "ExpressServer");
719
695
  }
720
- /** Prometheus client registry for metrics collection */
721
- register = null;
722
696
  /** HTTP server instance (null when not running) */
723
697
  server = null;
724
698
  /** Merged configuration with defaults applied */
@@ -737,16 +711,13 @@ var ExpressServer = class {
737
711
  connections = /* @__PURE__ */ new Set();
738
712
  /** Flag indicating if the server is shutting down */
739
713
  isShuttingDown = false;
714
+ /** Flag indicating if graceful shutdown handlers are registered */
715
+ gracefulShutdownRegistered = false;
740
716
  /**
741
717
  * Collection of registered health check functions.
742
718
  * These are executed when the health check endpoint is accessed.
743
719
  */
744
720
  healthChecks = [];
745
- /** Prometheus metrics for monitoring */
746
- requestCounter;
747
- routeTimings;
748
- requestSizes;
749
- clientIPs;
750
721
  /** Promise that resolves when initialization (middleware + routes) is complete */
751
722
  initPromise;
752
723
  /**
@@ -762,7 +733,6 @@ var ExpressServer = class {
762
733
  *
763
734
  * Default Monitoring:
764
735
  * - Request/Response logging
765
- * - Prometheus metrics
766
736
  * - Health checks
767
737
  * - Request tracing
768
738
  */
@@ -780,82 +750,6 @@ var ExpressServer = class {
780
750
  logger.getLogger().error(msg);
781
751
  throw new Error(msg);
782
752
  }
783
- const safeAppName = (this.config.appName || "express_app").toLowerCase().replace(/[^a-z0-9_]/g, "_");
784
- if (this.config.metrics?.enable) {
785
- const client = async.optionalRequire("prom-client");
786
- if (!client) {
787
- this.throwDependancyError("prom-client");
788
- }
789
- this.register = new client.Registry();
790
- this.requestCounter = new client.Counter({
791
- name: `${safeAppName}_http_requests_total`,
792
- help: "Total HTTP requests",
793
- labelNames: [
794
- "method",
795
- "route",
796
- "status"
797
- ],
798
- registers: [
799
- this.register
800
- ]
801
- });
802
- this.routeTimings = new client.Histogram({
803
- name: `${safeAppName}_http_request_duration_seconds`,
804
- help: "Duration of HTTP requests by route",
805
- labelNames: [
806
- "method",
807
- "route",
808
- "status"
809
- ],
810
- buckets: [
811
- 0.1,
812
- 0.3,
813
- 0.5,
814
- 0.7,
815
- 1,
816
- 3,
817
- 5,
818
- 7,
819
- 10
820
- ],
821
- registers: [
822
- this.register
823
- ]
824
- });
825
- this.requestSizes = new client.Histogram({
826
- name: `${safeAppName}_http_request_size_bytes`,
827
- help: "Size of HTTP request bodies",
828
- labelNames: [
829
- "method",
830
- "route"
831
- ],
832
- buckets: [
833
- 100,
834
- 1e3,
835
- 1e4,
836
- 1e5,
837
- 1e6
838
- ],
839
- registers: [
840
- this.register
841
- ]
842
- });
843
- this.clientIPs = new client.Counter({
844
- name: `${safeAppName}_http_client_ip_total`,
845
- help: "Client IP request counter",
846
- labelNames: [
847
- "ip",
848
- "method"
849
- ],
850
- registers: [
851
- this.register
852
- ]
853
- });
854
- client.collectDefaultMetrics({
855
- register: this.register,
856
- prefix: `${safeAppName}_`
857
- });
858
- }
859
753
  if (config$1?.healthCheck?.checks) {
860
754
  this.healthChecks.push(...config$1.healthCheck.checks);
861
755
  }
@@ -928,7 +822,6 @@ var ExpressServer = class {
928
822
  this.setupBodyParsingMiddleware();
929
823
  this.setupCookieParsingMiddleware();
930
824
  await this.setupOpenApiMiddleware();
931
- this.setupMetricsMiddleware();
932
825
  }
933
826
  /**
934
827
  * Set up basic middleware (trust proxy, request ID, context).
@@ -951,6 +844,7 @@ var ExpressServer = class {
951
844
  if (this.isShuttingDown) {
952
845
  res.setHeader("Connection", "close");
953
846
  res.status(httpStatusCodes.HttpStatusCodes.SERVICE_UNAVAILABLE).json(new exception.ServiceUnavailableException("Server is shutting down"));
847
+ return;
954
848
  }
955
849
  next();
956
850
  });
@@ -962,7 +856,7 @@ var ExpressServer = class {
962
856
  if (this.config.helmet) {
963
857
  const helmet = async.optionalRequire("helmet");
964
858
  if (!helmet) {
965
- this.throwDependancyError("helmet");
859
+ this.throwDependencyError("helmet");
966
860
  }
967
861
  if (typeof this.config.helmet === "object") {
968
862
  this.app.use(helmet(this.config.helmet));
@@ -973,7 +867,7 @@ var ExpressServer = class {
973
867
  if (this.config.cors) {
974
868
  const cors = async.optionalRequire("cors");
975
869
  if (!cors) {
976
- this.throwDependancyError("cors");
870
+ this.throwDependencyError("cors");
977
871
  }
978
872
  this.app.use(cors(this.config.cors === true ? {} : this.config.cors));
979
873
  }
@@ -1025,7 +919,7 @@ var ExpressServer = class {
1025
919
  if (this.config.rateLimit?.enable) {
1026
920
  const rateLimit = async.optionalRequire("express-rate-limit");
1027
921
  if (!rateLimit) {
1028
- this.throwDependancyError("express-rate-limit");
922
+ this.throwDependencyError("express-rate-limit");
1029
923
  }
1030
924
  this.app.use(rateLimit({
1031
925
  windowMs: this.config.rateLimit.windowMs ?? 15 * 60 * 1e3,
@@ -1074,7 +968,7 @@ var ExpressServer = class {
1074
968
  if (this.config.compression) {
1075
969
  const compression = async.optionalRequire("compression");
1076
970
  if (!compression) {
1077
- this.throwDependancyError("compression");
971
+ this.throwDependencyError("compression");
1078
972
  }
1079
973
  if (typeof this.config.compression === "object") {
1080
974
  this.app.use(compression(this.config.compression));
@@ -1130,7 +1024,7 @@ var ExpressServer = class {
1130
1024
  if (this.config.cookieParser) {
1131
1025
  const cookieParser = async.optionalRequire("cookie-parser");
1132
1026
  if (!cookieParser) {
1133
- this.throwDependancyError("cookie-parser");
1027
+ this.throwDependencyError("cookie-parser");
1134
1028
  }
1135
1029
  if (typeof this.config.cookieParser === "object") {
1136
1030
  this.app.use(cookieParser(void 0, this.config.cookieParser));
@@ -1164,7 +1058,7 @@ var ExpressServer = class {
1164
1058
  }
1165
1059
  const apiReference = async.optionalRequire("@scalar/express-api-reference")?.apiReference;
1166
1060
  if (!apiReference) {
1167
- this.throwDependancyError("@scalar/express-api-reference", getDependencyErrorMessage("@scalar/express-api-reference", "OpenAPI docs"));
1061
+ this.throwDependencyError("@scalar/express-api-reference", getDependencyErrorMessage("@scalar/express-api-reference", "OpenAPI docs"));
1168
1062
  }
1169
1063
  this.app.use(openApiMountPath, apiReference({
1170
1064
  spec: {
@@ -1185,50 +1079,10 @@ var ExpressServer = class {
1185
1079
  }
1186
1080
  }
1187
1081
  /**
1188
- * Set up metrics tracking middleware.
1189
- */
1190
- setupMetricsMiddleware() {
1191
- if (this.config.metrics?.enable) {
1192
- this.app.use((req, res, next) => {
1193
- const start = process.hrtime();
1194
- this.clientIPs?.inc({
1195
- ip: req.ip,
1196
- method: req.method
1197
- });
1198
- const cl = req.headers["content-length"];
1199
- if (cl) {
1200
- const size = Number(cl);
1201
- if (!Number.isNaN(size) && size >= 0) {
1202
- const route = this.normalizeRouteForMetrics(req, res);
1203
- this.requestSizes?.observe({
1204
- method: req.method,
1205
- route
1206
- }, size);
1207
- }
1208
- }
1209
- res.once("finish", () => {
1210
- const [seconds, nanoseconds] = process.hrtime(start);
1211
- const finalRoute = this.normalizeRouteForMetrics(req, res);
1212
- this.requestCounter?.inc({
1213
- method: req.method,
1214
- route: finalRoute,
1215
- status: res.statusCode.toString()
1216
- });
1217
- this.routeTimings?.observe({
1218
- method: req.method,
1219
- route: finalRoute,
1220
- status: res.statusCode.toString()
1221
- }, seconds + nanoseconds / 1e9);
1222
- });
1223
- next();
1224
- });
1225
- }
1226
- }
1227
- /**
1228
1082
  * Configure server routes and error handling.
1229
1083
  * Sets up in following order:
1230
1084
  *
1231
- * 1. Built-in routes (health, metrics)
1085
+ * 1. Built-in routes (health)
1232
1086
  * 2. Application routes
1233
1087
  * 3. 404 handler
1234
1088
  * 4. Error handler
@@ -1238,13 +1092,6 @@ var ExpressServer = class {
1238
1092
  this.app.get(healthCheckPath, async (_req, res) => {
1239
1093
  return this.handleHealthCheckRequest(res);
1240
1094
  });
1241
- if (this.config.metrics?.enable) {
1242
- const metricsPath = this.normalizePath(this.config.metrics.path ?? "/metrics", this.config.metrics?.withGlobalPrefix);
1243
- this.app.get(metricsPath, async (_req, res) => {
1244
- res.set("Content-Type", this.register.contentType);
1245
- res.end(await this.register.metrics());
1246
- });
1247
- }
1248
1095
  const routerToUse = this.externalRouter || this.rootRouter;
1249
1096
  this.app.use(this.globalPrefix, routerToUse);
1250
1097
  await this.runHook("afterRoutes", this.app);
@@ -1391,6 +1238,10 @@ var ExpressServer = class {
1391
1238
  * @throws Error if server fails to start or port is already in use
1392
1239
  */
1393
1240
  async start() {
1241
+ if (this.server) {
1242
+ logger.getLogger().warn("Server is already running, returning existing instance");
1243
+ return this.server;
1244
+ }
1394
1245
  await this.initPromise;
1395
1246
  await this.runHook("beforeStart", this.app);
1396
1247
  return new Promise((resolve, reject) => {
@@ -1463,12 +1314,9 @@ var ExpressServer = class {
1463
1314
  const host = this.formatHostForUrl(this.config.host || "localhost");
1464
1315
  const url = `${protocol}://${host}:${port}`;
1465
1316
  logger.getLogger().info(`Server running on ${url}`);
1466
- if (this.config.healthCheck?.path) {
1317
+ if (this.config.healthCheck && this.config.healthCheck?.path) {
1467
1318
  logger.getLogger().info(`Health check available at ${url}${this.normalizePath(this.config.healthCheck.path, this.config.healthCheck.withGlobalPrefix)}`);
1468
1319
  }
1469
- if (this.config.metrics?.enable && this.config.metrics.path) {
1470
- logger.getLogger().info(`Metrics available at ${url}${this.normalizePath(this.config.metrics.path, this.config.metrics.withGlobalPrefix)}`);
1471
- }
1472
1320
  if (this.config.openApi?.enable) {
1473
1321
  logger.getLogger().info(`API docs available at ${url}${this.normalizePath(this.config.openApi.mountPath, this.config.openApi.withGlobalPrefix)}`);
1474
1322
  }
@@ -1495,50 +1343,70 @@ var ExpressServer = class {
1495
1343
  return;
1496
1344
  }
1497
1345
  if (this.isShuttingDown) {
1498
- logger.getLogger().warn("Stop called while shutdown is already in progress");
1346
+ logger.getLogger().warn("Shutdown already in progress");
1499
1347
  return;
1500
1348
  }
1501
1349
  this.isShuttingDown = true;
1502
1350
  await this.runHook("beforeStop", this.server);
1503
1351
  try {
1504
- await this.gracefulShutdown();
1505
- } catch (err) {
1506
- logger.getLogger().error({
1507
- err
1508
- }, "Graceful shutdown timed out");
1509
- if (force) {
1510
- logger.getLogger().warn("Forcing connection destroy due to shutdown timeout");
1511
- }
1352
+ await this.gracefulShutdown(force);
1512
1353
  } finally {
1513
- await this.destroyConnections();
1354
+ this.isShuttingDown = false;
1514
1355
  }
1515
1356
  }
1516
1357
  /**
1517
1358
  * Perform graceful server shutdown with timeout.
1518
1359
  */
1519
- async gracefulShutdown() {
1360
+ async gracefulShutdown(force = false) {
1520
1361
  const shutdownTimeout = 1e4;
1362
+ const server = this.server;
1363
+ let timer;
1364
+ let afterStopCalled = false;
1365
+ const runAfterStop = /* @__PURE__ */ __name(async () => {
1366
+ if (afterStopCalled) return;
1367
+ afterStopCalled = true;
1368
+ await this.runHook("afterStop");
1369
+ }, "runAfterStop");
1521
1370
  const serverClosePromise = new Promise((resolve, reject) => {
1522
- this.server.close(async (err) => {
1371
+ server.close(async (err) => {
1372
+ if (timer) clearTimeout(timer);
1523
1373
  if (err) {
1524
1374
  logger.getLogger().error({
1525
1375
  err
1526
- }, "Error while closing server");
1376
+ }, "Error during server shutdown");
1527
1377
  reject(err);
1528
1378
  return;
1529
1379
  }
1530
1380
  this.server = null;
1531
- this.isShuttingDown = false;
1532
1381
  logger.getLogger().info("Server stopped gracefully");
1533
- await this.runHook("afterStop");
1382
+ await runAfterStop();
1534
1383
  resolve();
1535
1384
  });
1536
1385
  });
1537
- const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error("Shutdown timeout")), shutdownTimeout));
1538
- await Promise.race([
1539
- serverClosePromise,
1540
- timeoutPromise
1541
- ]);
1386
+ const timeoutPromise = new Promise((_, reject) => {
1387
+ timer = setTimeout(() => reject(new Error("Shutdown timeout")), shutdownTimeout);
1388
+ });
1389
+ try {
1390
+ await Promise.race([
1391
+ serverClosePromise,
1392
+ timeoutPromise
1393
+ ]);
1394
+ } catch (err) {
1395
+ if (timer) clearTimeout(timer);
1396
+ logger.getLogger().warn({
1397
+ err
1398
+ }, "Graceful shutdown timed out");
1399
+ if (!force) {
1400
+ throw err;
1401
+ }
1402
+ logger.getLogger().warn("Escalating to forced shutdown");
1403
+ server.closeIdleConnections?.();
1404
+ server.closeAllConnections?.();
1405
+ await this.destroyConnections();
1406
+ this.server = null;
1407
+ logger.getLogger().info("Forced shutdown completed");
1408
+ await runAfterStop();
1409
+ }
1542
1410
  }
1543
1411
  /**
1544
1412
  * Enable graceful shutdown on OS signals for production deployment.
@@ -1555,31 +1423,64 @@ var ExpressServer = class {
1555
1423
  "SIGINT",
1556
1424
  "SIGTERM"
1557
1425
  ]) {
1426
+ if (this.gracefulShutdownRegistered) {
1427
+ logger.getLogger().warn("Graceful shutdown handlers are already registered");
1428
+ return this;
1429
+ }
1430
+ let signalHandled = false;
1558
1431
  signals.forEach((signal) => {
1559
1432
  process.on(signal, async () => {
1433
+ if (signalHandled) {
1434
+ logger.getLogger().warn(`Ignoring duplicate ${signal}`);
1435
+ return;
1436
+ }
1437
+ signalHandled = true;
1560
1438
  logger.getLogger().info(`Received ${signal}, initiating graceful shutdown...`);
1561
1439
  try {
1562
- await this.stop();
1440
+ await this.stop(true);
1563
1441
  process.exit(0);
1564
1442
  } catch (err) {
1565
- logger.getLogger().error({
1443
+ logger.getLogger().fatal({
1566
1444
  err
1567
- }, "Error during graceful shutdown, forcing stop...");
1568
- try {
1569
- await this.stop(true);
1570
- process.exit(1);
1571
- } catch (forceError) {
1572
- logger.getLogger().fatal({
1573
- forceError
1574
- }, "Forced shutdown failed, exiting hard");
1575
- process.exit(1);
1576
- }
1445
+ }, "Shutdown failed");
1446
+ process.exit(1);
1577
1447
  }
1578
1448
  });
1579
1449
  });
1450
+ this.gracefulShutdownRegistered = true;
1580
1451
  return this;
1581
1452
  }
1582
1453
  /**
1454
+ * Destroy all active connections (gracefully if possible).
1455
+ * If a connection does not close cleanly, it will be force-destroyed.
1456
+ */
1457
+ async destroyConnections() {
1458
+ if (!this.connections.size) {
1459
+ return;
1460
+ }
1461
+ const sockets = [
1462
+ ...this.connections
1463
+ ];
1464
+ await Promise.allSettled(sockets.map((socket) => new Promise((resolve) => {
1465
+ socket.end();
1466
+ const timer = setTimeout(() => {
1467
+ socket.destroy();
1468
+ resolve();
1469
+ }, 1e3);
1470
+ socket.once("close", () => {
1471
+ clearTimeout(timer);
1472
+ resolve();
1473
+ });
1474
+ socket.once("error", () => {
1475
+ clearTimeout(timer);
1476
+ socket.destroy();
1477
+ resolve();
1478
+ });
1479
+ })));
1480
+ this.connections.clear();
1481
+ logger.getLogger().info(`Closed ${sockets.length} active connection(s)`);
1482
+ }
1483
+ /**
1583
1484
  * Set an externally created base router.
1584
1485
  * This will override the internal rootRouter.
1585
1486
  */
@@ -1607,14 +1508,15 @@ var ExpressServer = class {
1607
1508
  */
1608
1509
  registerRoute(methods, path, ...handlers) {
1609
1510
  const fullPath = this.normalizePath(path, true);
1511
+ const routerToUse = this.externalRouter || this.rootRouter;
1610
1512
  const methodMap = {
1611
- get: this.app.get.bind(this.app),
1612
- post: this.app.post.bind(this.app),
1613
- put: this.app.put.bind(this.app),
1614
- delete: this.app.delete.bind(this.app),
1615
- patch: this.app.patch.bind(this.app),
1616
- options: this.app.options.bind(this.app),
1617
- head: this.app.head.bind(this.app)
1513
+ get: routerToUse.get.bind(routerToUse),
1514
+ post: routerToUse.post.bind(routerToUse),
1515
+ put: routerToUse.put.bind(routerToUse),
1516
+ delete: routerToUse.delete.bind(routerToUse),
1517
+ patch: routerToUse.patch.bind(routerToUse),
1518
+ options: routerToUse.options.bind(routerToUse),
1519
+ head: routerToUse.head.bind(routerToUse)
1618
1520
  };
1619
1521
  methods.forEach((m) => {
1620
1522
  const fn = methodMap[m];
@@ -1640,15 +1542,16 @@ var ExpressServer = class {
1640
1542
  * @returns This instance for method chaining
1641
1543
  */
1642
1544
  registerMiddleware(path, middleware) {
1545
+ const routerToUse = this.externalRouter || this.rootRouter;
1643
1546
  if (typeof path === "string") {
1644
1547
  const normalizedPath = this.normalizePath(path);
1645
1548
  if (normalizedPath) {
1646
- this.app.use(normalizedPath, middleware);
1549
+ routerToUse.use(normalizedPath, middleware);
1647
1550
  } else {
1648
- this.app.use(middleware);
1551
+ routerToUse.use(middleware);
1649
1552
  }
1650
1553
  } else {
1651
- this.app.use(path);
1554
+ routerToUse.use(path);
1652
1555
  }
1653
1556
  return this;
1654
1557
  }
@@ -1662,22 +1565,11 @@ var ExpressServer = class {
1662
1565
  */
1663
1566
  useMiddleware(...middlewares) {
1664
1567
  middlewares.forEach((middleware) => {
1665
- this.app.use(middleware);
1568
+ (this.externalRouter || this.rootRouter).use(middleware);
1666
1569
  });
1667
1570
  return this;
1668
1571
  }
1669
1572
  /**
1670
- * Get Prometheus registry (to add custom counters/histograms)
1671
- *
1672
- * @return {*} {client.Registry}
1673
- */
1674
- getMetricsRegistry() {
1675
- if (!this.config.metrics?.enable) {
1676
- logger.getLogger().warn("Metrics are not enabled in the server configuration \nPlease enable metrics to use this feature.");
1677
- }
1678
- return this.register;
1679
- }
1680
- /**
1681
1573
  * Get server configuration
1682
1574
  *
1683
1575
  * @return {*} {CatbeeServerConfig}
@@ -1826,41 +1718,6 @@ var ExpressServer = class {
1826
1718
  }
1827
1719
  return sanitize(prefix + "/" + path);
1828
1720
  }
1829
- normalizeRouteForMetrics(req, res) {
1830
- if (req?.route?.path) {
1831
- return req.route.path;
1832
- }
1833
- if (res.statusCode === 404) return "/404";
1834
- const path = (req.path || "unknown").split("?")[0];
1835
- return path.replace(/\/[0-9]+/g, "/:id").replace(/\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/g, "/:uuid");
1836
- }
1837
- /**
1838
- * Destroy all active connections (gracefully if possible).
1839
- * If a connection does not close cleanly, it will be force-destroyed.
1840
- */
1841
- async destroyConnections() {
1842
- const total = this.connections.size;
1843
- if (total === 0) {
1844
- logger.getLogger().debug("No active connections to close");
1845
- return;
1846
- }
1847
- const timeoutMs = 5e3;
1848
- await Promise.race([
1849
- Promise.all(Array.from(this.connections).map((conn) => new Promise((resolve) => {
1850
- conn.end(() => {
1851
- if (!conn.destroyed) conn.destroy();
1852
- resolve();
1853
- });
1854
- conn.on("error", () => {
1855
- conn.destroy();
1856
- resolve();
1857
- });
1858
- }))),
1859
- new Promise((resolve) => setTimeout(resolve, timeoutMs))
1860
- ]);
1861
- this.connections.clear();
1862
- logger.getLogger().info(`Closed ${total} active connections`);
1863
- }
1864
1721
  async validateHttpsFiles() {
1865
1722
  if (!await fs.fileExists(this.config.https.key)) {
1866
1723
  const msg = `HTTPS key file not found: ${this.config.https.key}`;
@@ -1884,7 +1741,7 @@ var ExpressServer = class {
1884
1741
  }
1885
1742
  return false;
1886
1743
  }
1887
- throwDependancyError(packageName, msg) {
1744
+ throwDependencyError(packageName, msg) {
1888
1745
  logger.getLogger().error({
1889
1746
  command: `npm install ${packageName}`
1890
1747
  }, msg || DependencyErrors[packageName]);