@moreapp/common-nodejs 0.14.0 → 0.15.0
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.
|
@@ -35,6 +35,10 @@ class TerminationHandler {
|
|
|
35
35
|
logger_1.logger.error("Server threw an unrecoverable error, shutting down", err);
|
|
36
36
|
this.shutdown();
|
|
37
37
|
});
|
|
38
|
+
process.on("unhandledRejection", (reason) => {
|
|
39
|
+
logger_1.logger.error("Unhandled rejection, shutting down", { reason });
|
|
40
|
+
this.shutdown();
|
|
41
|
+
});
|
|
38
42
|
}
|
|
39
43
|
shutdown() {
|
|
40
44
|
if (this.isShuttingDown) {
|
|
@@ -5,12 +5,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const vitest_1 = require("vitest");
|
|
7
7
|
const node_events_1 = require("node:events");
|
|
8
|
+
const node_http_1 = __importDefault(require("node:http"));
|
|
9
|
+
const express_1 = __importDefault(require("express"));
|
|
8
10
|
const ExpressRequestTracker_1 = __importDefault(require("../ExpressRequestTracker"));
|
|
9
|
-
// Create a minimal Response mock that can emit the '
|
|
11
|
+
// Create a minimal Response mock that can emit the 'close' event
|
|
10
12
|
const createMockResponse = () => {
|
|
11
13
|
const emitter = new node_events_1.EventEmitter();
|
|
12
14
|
return Object.assign(emitter, {
|
|
13
|
-
|
|
15
|
+
emitClose: () => emitter.emit("close"),
|
|
14
16
|
});
|
|
15
17
|
};
|
|
16
18
|
(0, vitest_1.describe)("ExpressRequestTracker", () => {
|
|
@@ -23,8 +25,8 @@ const createMockResponse = () => {
|
|
|
23
25
|
middleware({}, res, next);
|
|
24
26
|
(0, vitest_1.expect)(tracker.nrOfActiveRequests()).toBe(1);
|
|
25
27
|
(0, vitest_1.expect)(next).toHaveBeenCalledTimes(1);
|
|
26
|
-
//
|
|
27
|
-
res.
|
|
28
|
+
// Close should decrement
|
|
29
|
+
res.emitClose();
|
|
28
30
|
(0, vitest_1.expect)(tracker.nrOfActiveRequests()).toBe(0);
|
|
29
31
|
});
|
|
30
32
|
(0, vitest_1.test)("decrements active requests when response finishes", () => {
|
|
@@ -33,7 +35,7 @@ const createMockResponse = () => {
|
|
|
33
35
|
const res = createMockResponse();
|
|
34
36
|
middleware({}, res, (() => { }));
|
|
35
37
|
(0, vitest_1.expect)(tracker.nrOfActiveRequests()).toBe(1);
|
|
36
|
-
res.
|
|
38
|
+
res.emitClose();
|
|
37
39
|
(0, vitest_1.expect)(tracker.nrOfActiveRequests()).toBe(0);
|
|
38
40
|
});
|
|
39
41
|
(0, vitest_1.test)("tracks multiple concurrent requests and decrements in order", () => {
|
|
@@ -45,10 +47,55 @@ const createMockResponse = () => {
|
|
|
45
47
|
middleware({}, res2, (() => { }));
|
|
46
48
|
(0, vitest_1.expect)(tracker.nrOfActiveRequests()).toBe(2);
|
|
47
49
|
// Finish the first request
|
|
48
|
-
res1.
|
|
50
|
+
res1.emitClose();
|
|
49
51
|
(0, vitest_1.expect)(tracker.nrOfActiveRequests()).toBe(1);
|
|
50
52
|
// Finish the second request
|
|
51
|
-
res2.
|
|
53
|
+
res2.emitClose();
|
|
52
54
|
(0, vitest_1.expect)(tracker.nrOfActiveRequests()).toBe(0);
|
|
53
55
|
});
|
|
56
|
+
(0, vitest_1.describe)("with a real HTTP server", () => {
|
|
57
|
+
const servers = [];
|
|
58
|
+
(0, vitest_1.afterEach)(() => {
|
|
59
|
+
servers.splice(0).forEach((server) => {
|
|
60
|
+
server.closeAllConnections();
|
|
61
|
+
server.close();
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
const startServer = async (handler) => {
|
|
65
|
+
const tracker = new ExpressRequestTracker_1.default();
|
|
66
|
+
const app = (0, express_1.default)();
|
|
67
|
+
app.use(tracker.track());
|
|
68
|
+
app.get("/", handler);
|
|
69
|
+
const server = app.listen(0);
|
|
70
|
+
servers.push(server);
|
|
71
|
+
await (0, node_events_1.once)(server, "listening");
|
|
72
|
+
const { port } = server.address();
|
|
73
|
+
return { tracker, port };
|
|
74
|
+
};
|
|
75
|
+
(0, vitest_1.test)("decrements once when the response completes", async () => {
|
|
76
|
+
let responseClosed;
|
|
77
|
+
const { tracker, port } = await startServer((_req, res) => {
|
|
78
|
+
responseClosed = (0, node_events_1.once)(res, "close");
|
|
79
|
+
res.send("ok");
|
|
80
|
+
});
|
|
81
|
+
const response = await fetch(`http://localhost:${port}/`);
|
|
82
|
+
await response.text();
|
|
83
|
+
await responseClosed;
|
|
84
|
+
(0, vitest_1.expect)(tracker.nrOfActiveRequests()).toBe(0);
|
|
85
|
+
});
|
|
86
|
+
(0, vitest_1.test)("decrements when the client aborts before the response finishes", async () => {
|
|
87
|
+
let responseClosed;
|
|
88
|
+
const { tracker, port } = await startServer((_req, res) => {
|
|
89
|
+
responseClosed = (0, node_events_1.once)(res, "close");
|
|
90
|
+
res.write("partial");
|
|
91
|
+
});
|
|
92
|
+
const req = node_http_1.default.get({ port, path: "/" });
|
|
93
|
+
const [response] = (await (0, node_events_1.once)(req, "response"));
|
|
94
|
+
await (0, node_events_1.once)(response, "data");
|
|
95
|
+
(0, vitest_1.expect)(tracker.nrOfActiveRequests()).toBe(1);
|
|
96
|
+
req.destroy();
|
|
97
|
+
await responseClosed;
|
|
98
|
+
(0, vitest_1.expect)(tracker.nrOfActiveRequests()).toBe(0);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
54
101
|
});
|
|
@@ -219,6 +219,30 @@ vitest_1.vi.mock("node:timers", () => ({
|
|
|
219
219
|
await new Promise((resolve) => setImmediate(resolve));
|
|
220
220
|
(0, vitest_1.expect)(exitSpy).toHaveBeenCalledWith(0);
|
|
221
221
|
});
|
|
222
|
+
(0, vitest_1.test)("shuts down on an unhandled rejection", async () => {
|
|
223
|
+
const server = createFakeServer();
|
|
224
|
+
const hs = {
|
|
225
|
+
setReady: vitest_1.vi.fn(),
|
|
226
|
+
stop: vitest_1.vi.fn(() => Promise.resolve()),
|
|
227
|
+
};
|
|
228
|
+
const requestTracker = { nrOfActiveRequests: vitest_1.vi.fn(() => 0) };
|
|
229
|
+
const exitSpy = vitest_1.vi.spyOn(process, "exit").mockImplementation(() => undefined);
|
|
230
|
+
new TerminationHandler_1.default(server, hs, requestTracker);
|
|
231
|
+
// Call the listener directly; emitting on process would also reach vitest's own handler
|
|
232
|
+
const listener = process.listeners("unhandledRejection").at(-1);
|
|
233
|
+
process.off("unhandledRejection", listener);
|
|
234
|
+
const reason = new Error("boom");
|
|
235
|
+
listener(reason, Promise.resolve());
|
|
236
|
+
(0, vitest_1.expect)(vitest_1.vi.mocked(logger_1.logger.error)).toHaveBeenCalledWith("Unhandled rejection, shutting down", {
|
|
237
|
+
reason,
|
|
238
|
+
});
|
|
239
|
+
(0, vitest_1.expect)(hs.setReady).toHaveBeenCalledWith(false);
|
|
240
|
+
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
241
|
+
(0, vitest_1.expect)(server.close).toHaveBeenCalled();
|
|
242
|
+
intervalCb?.();
|
|
243
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
244
|
+
(0, vitest_1.expect)(exitSpy).toHaveBeenCalledWith(0);
|
|
245
|
+
});
|
|
222
246
|
(0, vitest_1.test)("ignores a second shutdown call", () => {
|
|
223
247
|
const server = createFakeServer();
|
|
224
248
|
const hs = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moreapp/common-nodejs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -30,46 +30,47 @@
|
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@opentelemetry/api": "1.9.1",
|
|
33
|
-
"@opentelemetry/core": "2.
|
|
34
|
-
"@opentelemetry/exporter-trace-otlp-grpc": "0.
|
|
35
|
-
"@opentelemetry/instrumentation": "0.
|
|
36
|
-
"@opentelemetry/instrumentation-dns": "0.
|
|
37
|
-
"@opentelemetry/instrumentation-express": "0.
|
|
38
|
-
"@opentelemetry/instrumentation-http": "0.
|
|
39
|
-
"@opentelemetry/instrumentation-winston": "0.
|
|
40
|
-
"@opentelemetry/resources": "2.
|
|
41
|
-
"@opentelemetry/sdk-node": "0.
|
|
42
|
-
"@opentelemetry/sdk-trace-base": "2.
|
|
43
|
-
"@opentelemetry/sdk-trace-node": "2.
|
|
33
|
+
"@opentelemetry/core": "2.12.0",
|
|
34
|
+
"@opentelemetry/exporter-trace-otlp-grpc": "0.223.0",
|
|
35
|
+
"@opentelemetry/instrumentation": "0.223.0",
|
|
36
|
+
"@opentelemetry/instrumentation-dns": "0.66.0",
|
|
37
|
+
"@opentelemetry/instrumentation-express": "0.71.1",
|
|
38
|
+
"@opentelemetry/instrumentation-http": "0.223.0",
|
|
39
|
+
"@opentelemetry/instrumentation-winston": "0.68.1",
|
|
40
|
+
"@opentelemetry/resources": "2.12.0",
|
|
41
|
+
"@opentelemetry/sdk-node": "0.223.0",
|
|
42
|
+
"@opentelemetry/sdk-trace-base": "2.12.0",
|
|
43
|
+
"@opentelemetry/sdk-trace-node": "2.12.0",
|
|
44
44
|
"@opentelemetry/semantic-conventions": "1.43.0",
|
|
45
45
|
"axios": "1.20.0",
|
|
46
46
|
"content-disposition-parser": "1.0.2",
|
|
47
|
-
"date-and-time": "4.
|
|
48
|
-
"express": "5.
|
|
47
|
+
"date-and-time": "4.6.0",
|
|
48
|
+
"express": "5.3.0",
|
|
49
49
|
"lodash": "4.18.1",
|
|
50
50
|
"prom-client": "15.1.3",
|
|
51
51
|
"winston": "3.19.0",
|
|
52
|
-
"zod": "4.6.
|
|
52
|
+
"zod": "4.6.7"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@types/express": "5.0.6",
|
|
56
|
-
"@types/lodash": "4.17.
|
|
57
|
-
"@types/node": "
|
|
58
|
-
"@typescript-eslint/eslint-plugin": "8.
|
|
59
|
-
"@typescript-eslint/parser": "8.
|
|
60
|
-
"@vitest/coverage-v8": "
|
|
61
|
-
"@vitest/ui": "
|
|
56
|
+
"@types/lodash": "4.17.26",
|
|
57
|
+
"@types/node": "26.6.5",
|
|
58
|
+
"@typescript-eslint/eslint-plugin": "8.72.0",
|
|
59
|
+
"@typescript-eslint/parser": "8.72.0",
|
|
60
|
+
"@vitest/coverage-v8": "5.0.5",
|
|
61
|
+
"@vitest/ui": "5.0.5",
|
|
62
62
|
"eslint": "9.39.5",
|
|
63
63
|
"eslint-config-prettier": "10.1.8",
|
|
64
|
-
"eslint-plugin-prettier": "5.5.
|
|
64
|
+
"eslint-plugin-prettier": "5.5.7",
|
|
65
65
|
"husky": "9.1.7",
|
|
66
|
-
"lint-staged": "17.
|
|
67
|
-
"nock": "
|
|
68
|
-
"prettier": "3.9.
|
|
66
|
+
"lint-staged": "17.6.0",
|
|
67
|
+
"nock": "15.0.4",
|
|
68
|
+
"prettier": "3.9.10",
|
|
69
69
|
"ts-node": "10.9.2",
|
|
70
70
|
"typescript": "6.0.3",
|
|
71
|
-
"typescript-eslint": "8.
|
|
72
|
-
"
|
|
71
|
+
"typescript-eslint": "8.72.0",
|
|
72
|
+
"vite": "8.3.4",
|
|
73
|
+
"vitest": "5.0.5",
|
|
73
74
|
"vitest-mock-extended": "5.1.1"
|
|
74
75
|
},
|
|
75
76
|
"prettier": {
|