request-scope-api 1.0.22 → 1.0.23

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/dist/cjs/index.js CHANGED
@@ -15,11 +15,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
16
  exports.requestscopeMiddleware = exports.errorHandler = exports.setup = void 0;
17
17
  exports.dashboard = dashboard;
18
+ exports.shutdown = shutdown;
18
19
  const express_1 = __importDefault(require("express"));
19
20
  const middleware_js_1 = require("./middleware.js");
20
21
  Object.defineProperty(exports, "errorHandler", { enumerable: true, get: function () { return middleware_js_1.errorHandler; } });
21
22
  Object.defineProperty(exports, "setup", { enumerable: true, get: function () { return middleware_js_1.setup; } });
22
23
  const router_js_1 = require("./dashboard/router.js");
24
+ const shutdown_manager_js_1 = require("./shutdown-manager.js");
23
25
  // ---------------------------------------------------------------------------
24
26
  // Main middleware factory
25
27
  // ---------------------------------------------------------------------------
@@ -56,6 +58,26 @@ function dashboard(config, adapter) {
56
58
  // Attach dashboard as a named property on the default export for convenience
57
59
  middleware_js_1.requestscope.dashboard = dashboard;
58
60
  // ---------------------------------------------------------------------------
61
+ // Shutdown function
62
+ // ---------------------------------------------------------------------------
63
+ /**
64
+ * Gracefully shuts down all RequestScope resources.
65
+ *
66
+ * This should be called by the host application during shutdown (e.g., on SIGTERM/SIGINT).
67
+ * It will:
68
+ * - Stop the retention scheduler
69
+ * - Drain the queue with a timeout
70
+ * - Destroy the queue (clear timers)
71
+ * - Close all database connections
72
+ *
73
+ * @param options - Optional configuration for shutdown
74
+ * @param options.drainTimeoutMs - Timeout for queue drain in milliseconds (default: 5000)
75
+ * @param options.logHandles - Whether to log active handles after shutdown (default: false)
76
+ */
77
+ async function shutdown(options) {
78
+ await shutdown_manager_js_1.shutdownManager.shutdown(options);
79
+ }
80
+ // ---------------------------------------------------------------------------
59
81
  // Named exports for convenience
60
82
  // ---------------------------------------------------------------------------
61
83
  var middleware_js_2 = require("./middleware.js");
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;;;;AAuCH,8BAaC;AAjDD,sDAA8B;AAC9B,mDAA8F;AA+DrF,6FA/DwC,4BAAY,OA+DxC;AANZ,sFAzDsD,qBAAK,OAyDtD;AAxDd,qDAA8D;AAW9D,8EAA8E;AAC9E,0BAA0B;AAC1B,8EAA8E;AAE9E;;;;;GAKG;AACH,kBAAe,4BAAsB,CAAC;AAEtC,8EAA8E;AAC9E,2BAA2B;AAC3B,8EAA8E;AAE9E;;;;;;GAMG;AACH,SAAgB,SAAS,CAAC,MAAwB,EAAE,OAAwB;IAC1E,6CAA6C;IAC7C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;IACrC,IAAI,OAAO,KAAK,YAAY,EAAE,CAAC;QAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qFAAqF,CAAC,CAAC;QAC5G,MAAM,MAAM,GAAG,iBAAO,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAQ,EAAE,GAAQ,EAAE,IAAS,EAAE,EAAE;YAC3C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,8DAA8D,CAAC,CAAC;QACvF,CAAC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,IAAA,iCAAqB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAChD,CAAC;AAED,6EAA6E;AAC5E,4BAA8B,CAAC,SAAS,GAAG,SAAS,CAAC;AA4BtD,8EAA8E;AAC9E,gCAAgC;AAChC,8EAA8E;AAE9E,iDAAyE;AAAhE,uHAAA,YAAY,OAA0B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;;;;AAwCH,8BAaC;AAmCD,4BAEC;AAvFD,sDAA8B;AAC9B,mDAA8F;AAgErF,6FAhEwC,4BAAY,OAgExC;AANZ,sFA1DsD,qBAAK,OA0DtD;AAzDd,qDAA8D;AAC9D,+DAAwD;AAWxD,8EAA8E;AAC9E,0BAA0B;AAC1B,8EAA8E;AAE9E;;;;;GAKG;AACH,kBAAe,4BAAsB,CAAC;AAEtC,8EAA8E;AAC9E,2BAA2B;AAC3B,8EAA8E;AAE9E;;;;;;GAMG;AACH,SAAgB,SAAS,CAAC,MAAwB,EAAE,OAAwB;IAC1E,6CAA6C;IAC7C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;IACrC,IAAI,OAAO,KAAK,YAAY,EAAE,CAAC;QAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qFAAqF,CAAC,CAAC;QAC5G,MAAM,MAAM,GAAG,iBAAO,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAQ,EAAE,GAAQ,EAAE,IAAS,EAAE,EAAE;YAC3C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,8DAA8D,CAAC,CAAC;QACvF,CAAC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,OAAO,IAAA,iCAAqB,EAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAChD,CAAC;AAED,6EAA6E;AAC5E,4BAA8B,CAAC,SAAS,GAAG,SAAS,CAAC;AActD,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E;;;;;;;;;;;;;GAaG;AACI,KAAK,UAAU,QAAQ,CAAC,OAA2D;IACxF,MAAM,qCAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC1C,CAAC;AAgBD,8EAA8E;AAC9E,gCAAgC;AAChC,8EAA8E;AAE9E,iDAAyE;AAAhE,uHAAA,YAAY,OAA0B"}
@@ -21,6 +21,7 @@ const retention_js_1 = require("./retention.js");
21
21
  const capture_js_1 = require("./capture.js");
22
22
  const router_js_1 = require("./dashboard/router.js");
23
23
  const adapter_singleton_js_1 = require("./adapter-singleton.js");
24
+ const shutdown_manager_js_1 = require("./shutdown-manager.js");
24
25
  const ALWAYS_IGNORED_PATHS = ['/favicon.ico', '/requestscope/api', '/requestscope/assets'];
25
26
  // ---------------------------------------------------------------------------
26
27
  // Factory function
@@ -76,25 +77,16 @@ function requestscope(config, adapter) {
76
77
  const retentionDays = config.retentionDays ?? 30;
77
78
  const retentionScheduler = new retention_js_1.RetentionScheduler(adapter, retentionDays);
78
79
  retentionScheduler.start();
79
- // 5. Attach graceful shutdown handlers (only once per process)
80
- const shutdown = async () => {
81
- retentionScheduler.stop();
82
- await queue.drain(10000);
83
- queue.destroy();
84
- // Close all database connections via singleton
85
- await adapter_singleton_js_1.adapterSingleton.closeAll();
86
- };
87
- // Use a flag to ensure shutdown handlers are only attached once
88
- const shutdownKey = '__requestscope_shutdown_attached__';
89
- if (!process[shutdownKey]) {
90
- process[shutdownKey] = true;
91
- process.on('SIGTERM', () => {
92
- void shutdown();
93
- });
94
- process.on('SIGINT', () => {
95
- void shutdown();
96
- });
97
- }
80
+ // 5. Register resources with shutdown manager
81
+ shutdown_manager_js_1.shutdownManager.registerResources({
82
+ queue: {
83
+ drain: (timeoutMs) => queue.drain(timeoutMs),
84
+ destroy: () => queue.destroy(),
85
+ },
86
+ retentionScheduler: {
87
+ stop: () => retentionScheduler.stop(),
88
+ },
89
+ });
98
90
  // 6. Return the request handler middleware
99
91
  return async (req, res, next) => {
100
92
  // Check ignore list
@@ -1 +1 @@
1
- {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/middleware.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/middleware.ts"],"names":[],"mappings":"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"}
@@ -30,6 +30,10 @@ class RetentionScheduler {
30
30
  this.intervalHandle = setInterval(() => {
31
31
  void this.runOnce();
32
32
  }, TWENTY_FOUR_HOURS_MS);
33
+ // Allow the Node.js process to exit even if this timer is still active.
34
+ if (this.intervalHandle.unref) {
35
+ this.intervalHandle.unref();
36
+ }
33
37
  }
34
38
  /**
35
39
  * Stops the scheduled retention job. Inflight `runOnce()` calls are allowed
@@ -1 +1 @@
1
- {"version":3,"file":"retention.js","sourceRoot":"","sources":["../../src/retention.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAIH,MAAM,oBAAoB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,gBAAgB;AAElE,MAAa,kBAAkB;IAG7B,YACmB,OAAuB,EACvB,aAAqB;QADrB,YAAO,GAAP,OAAO,CAAgB;QACvB,kBAAa,GAAb,aAAa,CAAQ;QAJhC,mBAAc,GAA0C,IAAI,CAAC;IAKlE,CAAC;IAEJ;;;;OAIG;IACH,KAAK;QACH,yEAAyE;QACzE,yCAAyC;QACzC,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;QAEpB,2CAA2C;QAC3C,IAAI,IAAI,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;YACjC,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACrC,CAAC;QAED,IAAI,CAAC,cAAc,GAAG,WAAW,CAAC,GAAG,EAAE;YACrC,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;QACtB,CAAC,EAAE,oBAAoB,CAAC,CAAC;IAC3B,CAAC;IAED;;;OAGG;IACH,IAAI;QACF,IAAI,IAAI,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;YACjC,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACnC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC7B,CAAC;IACH,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,OAAO;QACnB,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,aAAa,GAAG,QAAU,CAAC,CAAC;QACtE,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;YAC3D,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;gBAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,8CAA8C,OAAO,yBAAyB,MAAM,CAAC,WAAW,EAAE,IAAI,CACvG,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,4CAA4C,OAAO,IAAI,CACxD,CAAC;QACJ,CAAC;IACH,CAAC;CACF;AA5DD,gDA4DC"}
1
+ {"version":3,"file":"retention.js","sourceRoot":"","sources":["../../src/retention.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAIH,MAAM,oBAAoB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,gBAAgB;AAElE,MAAa,kBAAkB;IAG7B,YACmB,OAAuB,EACvB,aAAqB;QADrB,YAAO,GAAP,OAAO,CAAgB;QACvB,kBAAa,GAAb,aAAa,CAAQ;QAJhC,mBAAc,GAA0C,IAAI,CAAC;IAKlE,CAAC;IAEJ;;;;OAIG;IACH,KAAK;QACH,yEAAyE;QACzE,yCAAyC;QACzC,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;QAEpB,2CAA2C;QAC3C,IAAI,IAAI,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;YACjC,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACrC,CAAC;QAED,IAAI,CAAC,cAAc,GAAG,WAAW,CAAC,GAAG,EAAE;YACrC,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC;QACtB,CAAC,EAAE,oBAAoB,CAAC,CAAC;QAEzB,wEAAwE;QACxE,IAAI,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;YAC9B,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;QAC9B,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,IAAI;QACF,IAAI,IAAI,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;YACjC,aAAa,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;YACnC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;QAC7B,CAAC;IACH,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,OAAO;QACnB,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,aAAa,GAAG,QAAU,CAAC,CAAC;QACtE,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;YAC3D,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;gBAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,8CAA8C,OAAO,yBAAyB,MAAM,CAAC,WAAW,EAAE,IAAI,CACvG,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACjE,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,4CAA4C,OAAO,IAAI,CACxD,CAAC;QACJ,CAAC;IACH,CAAC;CACF;AAjED,gDAiEC"}
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ /**
3
+ * ShutdownManager — Manages graceful shutdown of all RequestScope resources.
4
+ *
5
+ * This centralizes shutdown logic to prevent multiple shutdown executions,
6
+ * add comprehensive logging, and expose a shutdown() method that the host
7
+ * application can call instead of relying on global signal handlers.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.shutdownManager = void 0;
11
+ const adapter_singleton_js_1 = require("./adapter-singleton.js");
12
+ class ShutdownManager {
13
+ constructor() {
14
+ this.isShuttingDown = false;
15
+ this.resources = {};
16
+ }
17
+ /**
18
+ * Registers resources that need to be cleaned up during shutdown.
19
+ */
20
+ registerResources(resources) {
21
+ this.resources = { ...this.resources, ...resources };
22
+ }
23
+ /**
24
+ * Performs graceful shutdown of all registered resources.
25
+ *
26
+ * Steps:
27
+ * 1. Prevents multiple shutdown executions
28
+ * 2. Stops retention scheduler
29
+ * 3. Drains queue with timeout
30
+ * 4. Destroys queue
31
+ * 5. Closes all database connections
32
+ * 6. Logs remaining active handles for debugging
33
+ */
34
+ async shutdown(options = {}) {
35
+ const { drainTimeoutMs = 5000, logHandles = false } = options;
36
+ // Prevent multiple shutdown executions
37
+ if (this.isShuttingDown) {
38
+ process.stderr.write('[RequestScope] Shutdown already in progress, skipping duplicate call\n');
39
+ return;
40
+ }
41
+ this.isShuttingDown = true;
42
+ process.stderr.write('[RequestScope] Starting graceful shutdown...\n');
43
+ const startTime = Date.now();
44
+ try {
45
+ // 1. Stop retention scheduler
46
+ if (this.resources.retentionScheduler) {
47
+ process.stderr.write('[RequestScope] Stopping retention scheduler...\n');
48
+ this.resources.retentionScheduler.stop();
49
+ }
50
+ // 2. Drain queue with timeout
51
+ if (this.resources.queue) {
52
+ process.stderr.write(`[RequestScope] Draining queue (timeout: ${drainTimeoutMs}ms)...\n`);
53
+ try {
54
+ await Promise.race([
55
+ this.resources.queue.drain(drainTimeoutMs),
56
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Queue drain timeout')), drainTimeoutMs))
57
+ ]);
58
+ process.stderr.write('[RequestScope] Queue drained successfully\n');
59
+ }
60
+ catch (err) {
61
+ const message = err instanceof Error ? err.message : String(err);
62
+ process.stderr.write(`[RequestScope] Queue drain warning: ${message}\n`);
63
+ }
64
+ // 3. Destroy queue (clears timer)
65
+ process.stderr.write('[RequestScope] Destroying queue...\n');
66
+ this.resources.queue.destroy();
67
+ }
68
+ // 4. Close all database connections
69
+ process.stderr.write('[RequestScope] Closing database connections...\n');
70
+ await adapter_singleton_js_1.adapterSingleton.closeAll();
71
+ process.stderr.write('[RequestScope] Database connections closed\n');
72
+ const duration = Date.now() - startTime;
73
+ process.stderr.write(`[RequestScope] Graceful shutdown completed in ${duration}ms\n`);
74
+ // 5. Log remaining active handles for debugging
75
+ if (logHandles) {
76
+ this.logActiveHandles();
77
+ }
78
+ }
79
+ catch (err) {
80
+ const message = err instanceof Error ? err.message : String(err);
81
+ process.stderr.write(`[RequestScope] Shutdown error: ${message}\n`);
82
+ // Log active handles even on error
83
+ if (logHandles) {
84
+ this.logActiveHandles();
85
+ }
86
+ }
87
+ }
88
+ /**
89
+ * Logs active handles to help identify what's preventing process exit.
90
+ */
91
+ logActiveHandles() {
92
+ try {
93
+ // @ts-ignore - _getActiveHandles is not in TypeScript definitions
94
+ const handles = process._getActiveHandles();
95
+ process.stderr.write(`[RequestScope] Active handles (${handles.length}):\n`);
96
+ for (let i = 0; i < Math.min(handles.length, 20); i++) {
97
+ const handle = handles[i];
98
+ const type = handle.constructor.name;
99
+ // @ts-ignore
100
+ const details = handle._handle?.type || '';
101
+ process.stderr.write(` [${i}] ${type}${details ? ` (${details})` : ''}\n`);
102
+ }
103
+ if (handles.length > 20) {
104
+ process.stderr.write(` ... and ${handles.length - 20} more\n`);
105
+ }
106
+ }
107
+ catch (err) {
108
+ process.stderr.write('[RequestScope] Could not log active handles\n');
109
+ }
110
+ }
111
+ /**
112
+ * Returns whether shutdown is currently in progress.
113
+ */
114
+ isShutdownInProgress() {
115
+ return this.isShuttingDown;
116
+ }
117
+ }
118
+ // Export singleton instance
119
+ exports.shutdownManager = new ShutdownManager();
120
+ //# sourceMappingURL=shutdown-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shutdown-manager.js","sourceRoot":"","sources":["../../src/shutdown-manager.ts"],"names":[],"mappings":"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"}
package/dist/esm/index.js CHANGED
@@ -11,6 +11,7 @@
11
11
  import express from 'express';
12
12
  import { requestscope as requestscopeMiddleware, errorHandler, setup } from './middleware.js';
13
13
  import { createDashboardRouter } from './dashboard/router.js';
14
+ import { shutdownManager } from './shutdown-manager.js';
14
15
  // ---------------------------------------------------------------------------
15
16
  // Main middleware factory
16
17
  // ---------------------------------------------------------------------------
@@ -55,6 +56,26 @@ export { setup };
55
56
  // ---------------------------------------------------------------------------
56
57
  export { errorHandler };
57
58
  // ---------------------------------------------------------------------------
59
+ // Shutdown function
60
+ // ---------------------------------------------------------------------------
61
+ /**
62
+ * Gracefully shuts down all RequestScope resources.
63
+ *
64
+ * This should be called by the host application during shutdown (e.g., on SIGTERM/SIGINT).
65
+ * It will:
66
+ * - Stop the retention scheduler
67
+ * - Drain the queue with a timeout
68
+ * - Destroy the queue (clear timers)
69
+ * - Close all database connections
70
+ *
71
+ * @param options - Optional configuration for shutdown
72
+ * @param options.drainTimeoutMs - Timeout for queue drain in milliseconds (default: 5000)
73
+ * @param options.logHandles - Whether to log active handles after shutdown (default: false)
74
+ */
75
+ export async function shutdown(options) {
76
+ await shutdownManager.shutdown(options);
77
+ }
78
+ // ---------------------------------------------------------------------------
58
79
  // Named exports for convenience
59
80
  // ---------------------------------------------------------------------------
60
81
  export { requestscope as requestscopeMiddleware } from './middleware.js';
@@ -16,6 +16,7 @@ import { RetentionScheduler } from './retention.js';
16
16
  import { bufferRequestBody, wrapResponse, normalizeRequestUrl, ERROR_DATA_SYMBOL, } from './capture.js';
17
17
  import { createDashboardRouter } from './dashboard/router.js';
18
18
  import { adapterSingleton } from './adapter-singleton.js';
19
+ import { shutdownManager } from './shutdown-manager.js';
19
20
  const ALWAYS_IGNORED_PATHS = ['/favicon.ico', '/requestscope/api', '/requestscope/assets'];
20
21
  // ---------------------------------------------------------------------------
21
22
  // Factory function
@@ -71,25 +72,16 @@ export function requestscope(config, adapter) {
71
72
  const retentionDays = config.retentionDays ?? 30;
72
73
  const retentionScheduler = new RetentionScheduler(adapter, retentionDays);
73
74
  retentionScheduler.start();
74
- // 5. Attach graceful shutdown handlers (only once per process)
75
- const shutdown = async () => {
76
- retentionScheduler.stop();
77
- await queue.drain(10000);
78
- queue.destroy();
79
- // Close all database connections via singleton
80
- await adapterSingleton.closeAll();
81
- };
82
- // Use a flag to ensure shutdown handlers are only attached once
83
- const shutdownKey = '__requestscope_shutdown_attached__';
84
- if (!process[shutdownKey]) {
85
- process[shutdownKey] = true;
86
- process.on('SIGTERM', () => {
87
- void shutdown();
88
- });
89
- process.on('SIGINT', () => {
90
- void shutdown();
91
- });
92
- }
75
+ // 5. Register resources with shutdown manager
76
+ shutdownManager.registerResources({
77
+ queue: {
78
+ drain: (timeoutMs) => queue.drain(timeoutMs),
79
+ destroy: () => queue.destroy(),
80
+ },
81
+ retentionScheduler: {
82
+ stop: () => retentionScheduler.stop(),
83
+ },
84
+ });
93
85
  // 6. Return the request handler middleware
94
86
  return async (req, res, next) => {
95
87
  // Check ignore list
@@ -27,6 +27,10 @@ export class RetentionScheduler {
27
27
  this.intervalHandle = setInterval(() => {
28
28
  void this.runOnce();
29
29
  }, TWENTY_FOUR_HOURS_MS);
30
+ // Allow the Node.js process to exit even if this timer is still active.
31
+ if (this.intervalHandle.unref) {
32
+ this.intervalHandle.unref();
33
+ }
30
34
  }
31
35
  /**
32
36
  * Stops the scheduled retention job. Inflight `runOnce()` calls are allowed
@@ -0,0 +1,116 @@
1
+ /**
2
+ * ShutdownManager — Manages graceful shutdown of all RequestScope resources.
3
+ *
4
+ * This centralizes shutdown logic to prevent multiple shutdown executions,
5
+ * add comprehensive logging, and expose a shutdown() method that the host
6
+ * application can call instead of relying on global signal handlers.
7
+ */
8
+ import { adapterSingleton } from './adapter-singleton.js';
9
+ class ShutdownManager {
10
+ constructor() {
11
+ this.isShuttingDown = false;
12
+ this.resources = {};
13
+ }
14
+ /**
15
+ * Registers resources that need to be cleaned up during shutdown.
16
+ */
17
+ registerResources(resources) {
18
+ this.resources = { ...this.resources, ...resources };
19
+ }
20
+ /**
21
+ * Performs graceful shutdown of all registered resources.
22
+ *
23
+ * Steps:
24
+ * 1. Prevents multiple shutdown executions
25
+ * 2. Stops retention scheduler
26
+ * 3. Drains queue with timeout
27
+ * 4. Destroys queue
28
+ * 5. Closes all database connections
29
+ * 6. Logs remaining active handles for debugging
30
+ */
31
+ async shutdown(options = {}) {
32
+ const { drainTimeoutMs = 5000, logHandles = false } = options;
33
+ // Prevent multiple shutdown executions
34
+ if (this.isShuttingDown) {
35
+ process.stderr.write('[RequestScope] Shutdown already in progress, skipping duplicate call\n');
36
+ return;
37
+ }
38
+ this.isShuttingDown = true;
39
+ process.stderr.write('[RequestScope] Starting graceful shutdown...\n');
40
+ const startTime = Date.now();
41
+ try {
42
+ // 1. Stop retention scheduler
43
+ if (this.resources.retentionScheduler) {
44
+ process.stderr.write('[RequestScope] Stopping retention scheduler...\n');
45
+ this.resources.retentionScheduler.stop();
46
+ }
47
+ // 2. Drain queue with timeout
48
+ if (this.resources.queue) {
49
+ process.stderr.write(`[RequestScope] Draining queue (timeout: ${drainTimeoutMs}ms)...\n`);
50
+ try {
51
+ await Promise.race([
52
+ this.resources.queue.drain(drainTimeoutMs),
53
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Queue drain timeout')), drainTimeoutMs))
54
+ ]);
55
+ process.stderr.write('[RequestScope] Queue drained successfully\n');
56
+ }
57
+ catch (err) {
58
+ const message = err instanceof Error ? err.message : String(err);
59
+ process.stderr.write(`[RequestScope] Queue drain warning: ${message}\n`);
60
+ }
61
+ // 3. Destroy queue (clears timer)
62
+ process.stderr.write('[RequestScope] Destroying queue...\n');
63
+ this.resources.queue.destroy();
64
+ }
65
+ // 4. Close all database connections
66
+ process.stderr.write('[RequestScope] Closing database connections...\n');
67
+ await adapterSingleton.closeAll();
68
+ process.stderr.write('[RequestScope] Database connections closed\n');
69
+ const duration = Date.now() - startTime;
70
+ process.stderr.write(`[RequestScope] Graceful shutdown completed in ${duration}ms\n`);
71
+ // 5. Log remaining active handles for debugging
72
+ if (logHandles) {
73
+ this.logActiveHandles();
74
+ }
75
+ }
76
+ catch (err) {
77
+ const message = err instanceof Error ? err.message : String(err);
78
+ process.stderr.write(`[RequestScope] Shutdown error: ${message}\n`);
79
+ // Log active handles even on error
80
+ if (logHandles) {
81
+ this.logActiveHandles();
82
+ }
83
+ }
84
+ }
85
+ /**
86
+ * Logs active handles to help identify what's preventing process exit.
87
+ */
88
+ logActiveHandles() {
89
+ try {
90
+ // @ts-ignore - _getActiveHandles is not in TypeScript definitions
91
+ const handles = process._getActiveHandles();
92
+ process.stderr.write(`[RequestScope] Active handles (${handles.length}):\n`);
93
+ for (let i = 0; i < Math.min(handles.length, 20); i++) {
94
+ const handle = handles[i];
95
+ const type = handle.constructor.name;
96
+ // @ts-ignore
97
+ const details = handle._handle?.type || '';
98
+ process.stderr.write(` [${i}] ${type}${details ? ` (${details})` : ''}\n`);
99
+ }
100
+ if (handles.length > 20) {
101
+ process.stderr.write(` ... and ${handles.length - 20} more\n`);
102
+ }
103
+ }
104
+ catch (err) {
105
+ process.stderr.write('[RequestScope] Could not log active handles\n');
106
+ }
107
+ }
108
+ /**
109
+ * Returns whether shutdown is currently in progress.
110
+ */
111
+ isShutdownInProgress() {
112
+ return this.isShuttingDown;
113
+ }
114
+ }
115
+ // Export singleton instance
116
+ export const shutdownManager = new ShutdownManager();
@@ -28,5 +28,23 @@ export default requestscopeMiddleware;
28
28
  export declare function dashboard(config?: DashboardConfig, adapter?: StorageAdapter): express.Router;
29
29
  export { setup };
30
30
  export { errorHandler };
31
+ /**
32
+ * Gracefully shuts down all RequestScope resources.
33
+ *
34
+ * This should be called by the host application during shutdown (e.g., on SIGTERM/SIGINT).
35
+ * It will:
36
+ * - Stop the retention scheduler
37
+ * - Drain the queue with a timeout
38
+ * - Destroy the queue (clear timers)
39
+ * - Close all database connections
40
+ *
41
+ * @param options - Optional configuration for shutdown
42
+ * @param options.drainTimeoutMs - Timeout for queue drain in milliseconds (default: 5000)
43
+ * @param options.logHandles - Whether to log active handles after shutdown (default: false)
44
+ */
45
+ export declare function shutdown(options?: {
46
+ drainTimeoutMs?: number;
47
+ logHandles?: boolean;
48
+ }): Promise<void>;
31
49
  export type { RequestScopeConfig, StorageConfig, AuthConfig, RequestRecord, StorageAdapter, QueryFilters, DashboardConfig, };
32
50
  export { requestscope as requestscopeMiddleware } from './middleware.js';
@@ -0,0 +1,49 @@
1
+ /**
2
+ * ShutdownManager — Manages graceful shutdown of all RequestScope resources.
3
+ *
4
+ * This centralizes shutdown logic to prevent multiple shutdown executions,
5
+ * add comprehensive logging, and expose a shutdown() method that the host
6
+ * application can call instead of relying on global signal handlers.
7
+ */
8
+ interface ShutdownResources {
9
+ queue?: {
10
+ drain: (timeoutMs: number) => Promise<void>;
11
+ destroy: () => void;
12
+ };
13
+ retentionScheduler?: {
14
+ stop: () => void;
15
+ };
16
+ }
17
+ declare class ShutdownManager {
18
+ private isShuttingDown;
19
+ private resources;
20
+ /**
21
+ * Registers resources that need to be cleaned up during shutdown.
22
+ */
23
+ registerResources(resources: ShutdownResources): void;
24
+ /**
25
+ * Performs graceful shutdown of all registered resources.
26
+ *
27
+ * Steps:
28
+ * 1. Prevents multiple shutdown executions
29
+ * 2. Stops retention scheduler
30
+ * 3. Drains queue with timeout
31
+ * 4. Destroys queue
32
+ * 5. Closes all database connections
33
+ * 6. Logs remaining active handles for debugging
34
+ */
35
+ shutdown(options?: {
36
+ drainTimeoutMs?: number;
37
+ logHandles?: boolean;
38
+ }): Promise<void>;
39
+ /**
40
+ * Logs active handles to help identify what's preventing process exit.
41
+ */
42
+ private logActiveHandles;
43
+ /**
44
+ * Returns whether shutdown is currently in progress.
45
+ */
46
+ isShutdownInProgress(): boolean;
47
+ }
48
+ export declare const shutdownManager: ShutdownManager;
49
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "request-scope-api",
3
- "version": "1.0.22",
3
+ "version": "1.0.23",
4
4
  "description": "Zero-friction API request observability for Express.js applications",
5
5
  "keywords": [
6
6
  "express",