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 +22 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/middleware.js +11 -19
- package/dist/cjs/middleware.js.map +1 -1
- package/dist/cjs/retention.js +4 -0
- package/dist/cjs/retention.js.map +1 -1
- package/dist/cjs/shutdown-manager.js +120 -0
- package/dist/cjs/shutdown-manager.js.map +1 -0
- package/dist/esm/index.js +21 -0
- package/dist/esm/middleware.js +11 -19
- package/dist/esm/retention.js +4 -0
- package/dist/esm/shutdown-manager.js +116 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/shutdown-manager.d.ts +49 -0
- package/package.json +1 -1
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");
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;;;;
|
|
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"}
|
package/dist/cjs/middleware.js
CHANGED
|
@@ -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.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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":";AAAA;;;;;;;;;;;GAWG;;
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/middleware.ts"],"names":[],"mappings":"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"}
|
package/dist/cjs/retention.js
CHANGED
|
@@ -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;
|
|
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';
|
package/dist/esm/middleware.js
CHANGED
|
@@ -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.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
package/dist/esm/retention.js
CHANGED
|
@@ -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();
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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 {};
|