connect-ready 1.0.13 → 2.0.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.
- package/README.md +95 -50
- package/index.d.ts +54 -0
- package/index.js +230 -91
- package/package.json +14 -20
- package/.editorconfig +0 -14
- package/.eslintrc.json +0 -4
- package/.github/workflows/npm-publish.yml +0 -41
- package/.travis.yml +0 -7
- package/alerts.jsonl +0 -849
- package/assets.jsonl +0 -1
- package/test/index.js +0 -225
package/README.md
CHANGED
|
@@ -1,77 +1,122 @@
|
|
|
1
1
|
# connect-ready
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Express/Connect readiness route for Kubernetes applications.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Requirements
|
|
6
6
|
|
|
7
|
+
- Node.js 22 or newer
|
|
7
8
|
|
|
8
|
-
##
|
|
9
|
+
## Installation
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
```shell
|
|
12
|
+
npm install connect-ready
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Readiness route
|
|
11
16
|
|
|
12
|
-
The
|
|
17
|
+
The route returns `503` until the application explicitly becomes ready.
|
|
13
18
|
|
|
14
|
-
|
|
19
|
+
```javascript
|
|
20
|
+
'use strict';
|
|
15
21
|
|
|
16
|
-
|
|
22
|
+
const http = require('node:http');
|
|
23
|
+
const express = require('express');
|
|
24
|
+
const ready = require('connect-ready');
|
|
25
|
+
|
|
26
|
+
const app = express();
|
|
27
|
+
const server = http.createServer(app);
|
|
28
|
+
|
|
29
|
+
app.get('/ready', ready.route);
|
|
17
30
|
|
|
31
|
+
server.listen(3000, () => {
|
|
32
|
+
ready.setStatus(204);
|
|
33
|
+
});
|
|
34
|
+
```
|
|
18
35
|
|
|
19
|
-
|
|
36
|
+
Kubernetes considers HTTP responses from 200 through 399 successful. Use a failure status such as `503` whenever the application cannot accept traffic:
|
|
20
37
|
|
|
21
38
|
```javascript
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
var express = require('express');
|
|
25
|
-
var ready = require('connect-ready');
|
|
39
|
+
ready.setStatus(503);
|
|
40
|
+
```
|
|
26
41
|
|
|
27
|
-
|
|
28
|
-
var server = http.createServer(app);
|
|
42
|
+
## Graceful shutdown in Kubernetes
|
|
29
43
|
|
|
44
|
+
Modern Kubernetes marks a terminating Pod endpoint as not ready. The application must still stop accepting new connections and allow active requests to finish.
|
|
30
45
|
|
|
31
|
-
|
|
46
|
+
`registerShutdownHandlers()` installs a consistent lifecycle for Node HTTP servers:
|
|
32
47
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
48
|
+
- `SIGTERM` and `SIGINT` drain active requests and exit `0`;
|
|
49
|
+
- `uncaughtException` and `unhandledRejection` attempt bounded cleanup and exit `1`;
|
|
50
|
+
- readiness changes to `503` as soon as shutdown starts;
|
|
51
|
+
- `server.close()` immediately stops new connections and drains active requests;
|
|
52
|
+
- the process force-closes HTTP connections and exits `1` if its deadline expires.
|
|
37
53
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
54
|
+
```javascript
|
|
55
|
+
const shutdown = ready.registerShutdownHandlers(server, {
|
|
56
|
+
// Keep this below the Pod's terminationGracePeriodSeconds.
|
|
57
|
+
timeoutMs: Number.parseInt(process.env.SHUTDOWN_TIMEOUT_MS ?? '30000', 10),
|
|
58
|
+
|
|
59
|
+
// Fatal process errors should not drain for as long as a normal rollout.
|
|
60
|
+
fatalTimeoutMs: 30_000,
|
|
61
|
+
|
|
62
|
+
async cleanup() {
|
|
63
|
+
await database.close();
|
|
64
|
+
await log4js.shutdown();
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
onFatal(error, origin) {
|
|
68
|
+
logger.fatal({ error, origin }, 'Fatal process error');
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
// Optional: server.closeAllConnections() does not close upgraded protocols.
|
|
72
|
+
forceClose() {
|
|
73
|
+
webSocketServer.close();
|
|
74
|
+
},
|
|
41
75
|
});
|
|
76
|
+
```
|
|
42
77
|
|
|
78
|
+
Node.js 22 `server.close()` stops accepting new connections, closes idle keep-alive connections, and waits for active HTTP requests to complete. Dependencies are cleaned up after those requests drain.
|
|
43
79
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
* pre-stop hook in the kubernetes manifest.
|
|
54
|
-
*/
|
|
55
|
-
setTimeout(function() {
|
|
56
|
-
server.close(function() {
|
|
57
|
-
console.log('all connections closed');
|
|
58
|
-
process.exit(0);
|
|
59
|
-
});
|
|
60
|
-
}, 2000);
|
|
61
|
-
});
|
|
80
|
+
Set `timeoutMs` slightly below the Pod's `terminationGracePeriodSeconds`. The Kubernetes grace period must be long enough for the longest active request plus dependency cleanup and a safety margin. Applications with requests lasting up to 45 minutes should configure both deadlines accordingly.
|
|
81
|
+
|
|
82
|
+
An uncaught exception can leave application state inconsistent, so `fatalTimeoutMs` defaults to the smaller of 30 seconds and `timeoutMs`. Fatal shutdown still attempts to drain and clean up, but it must exit non-zero within that shorter deadline.
|
|
83
|
+
|
|
84
|
+
A controller can also initiate shutdown or remove its process listeners explicitly:
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
shutdown.close(); // Start a normal programmatic shutdown.
|
|
88
|
+
shutdown.dispose(); // Or unregister the handlers if another component takes ownership.
|
|
62
89
|
```
|
|
63
90
|
|
|
64
|
-
##
|
|
91
|
+
## API
|
|
92
|
+
|
|
93
|
+
### `setStatus(code)`
|
|
94
|
+
|
|
95
|
+
Sets the readiness HTTP status. The code must be an integer from 100 through 599.
|
|
96
|
+
|
|
97
|
+
### `getStatus()`
|
|
98
|
+
|
|
99
|
+
Returns the current readiness HTTP status.
|
|
100
|
+
|
|
101
|
+
### `route(req, res)`
|
|
102
|
+
|
|
103
|
+
Express/Connect route that responds with the current readiness status.
|
|
104
|
+
|
|
105
|
+
### `registerShutdownHandlers(server, options)`
|
|
106
|
+
|
|
107
|
+
Registers handlers for normal process signals and fatal process events. Returns a shutdown controller.
|
|
65
108
|
|
|
66
|
-
|
|
109
|
+
Options:
|
|
67
110
|
|
|
68
|
-
|
|
111
|
+
- `timeoutMs`: normal shutdown deadline; defaults to 30 seconds.
|
|
112
|
+
- `fatalTimeoutMs`: fatal-error deadline; defaults to at most 30 seconds.
|
|
113
|
+
- `cleanup(context)`: async dependency cleanup after HTTP requests drain.
|
|
114
|
+
- `onFatal(error, origin)`: fatal-error reporting hook.
|
|
115
|
+
- `forceClose(context)`: closes upgraded or custom connections at the deadline.
|
|
69
116
|
|
|
70
|
-
|
|
117
|
+
The controller provides:
|
|
71
118
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
```
|
|
77
|
-
Where 70 is the lag as defined in [toobusy-js](https://github.com/STRML/node-toobusy)
|
|
119
|
+
- `close()`: starts a normal programmatic shutdown.
|
|
120
|
+
- `shutdown(request)`: starts shutdown with an explicit reason or error.
|
|
121
|
+
- `dispose()`: unregisters the installed process handlers.
|
|
122
|
+
- `isShuttingDown`: indicates whether shutdown has started.
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
interface ShutdownContext {
|
|
2
|
+
reason: string;
|
|
3
|
+
error?: Error;
|
|
4
|
+
origin?: string;
|
|
5
|
+
fatal: boolean;
|
|
6
|
+
forced: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface ShutdownOptions {
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
fatalTimeoutMs?: number;
|
|
12
|
+
cleanup?: (context: ShutdownContext) => void | Promise<void>;
|
|
13
|
+
forceClose?: (context: ShutdownContext) => void;
|
|
14
|
+
onFatal?: (error: Error, origin: string) => void;
|
|
15
|
+
exit?: (code: number) => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface ShutdownRequest {
|
|
19
|
+
reason?: string;
|
|
20
|
+
error?: unknown;
|
|
21
|
+
fatal?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface ClosableServer {
|
|
25
|
+
close(callback: (error?: Error) => void): unknown;
|
|
26
|
+
closeAllConnections?(): void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface ShutdownController {
|
|
30
|
+
readonly isShuttingDown: boolean;
|
|
31
|
+
close(): Promise<number>;
|
|
32
|
+
dispose(): void;
|
|
33
|
+
shutdown(request?: ShutdownRequest): Promise<number>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
declare function setStatus(code: number): void;
|
|
37
|
+
declare function getStatus(): number;
|
|
38
|
+
declare function route(req: any, res: any): void;
|
|
39
|
+
declare function registerShutdownHandlers(
|
|
40
|
+
server: ClosableServer,
|
|
41
|
+
options?: ShutdownOptions,
|
|
42
|
+
): ShutdownController;
|
|
43
|
+
|
|
44
|
+
export {
|
|
45
|
+
ClosableServer,
|
|
46
|
+
ShutdownContext,
|
|
47
|
+
ShutdownController,
|
|
48
|
+
ShutdownOptions,
|
|
49
|
+
ShutdownRequest,
|
|
50
|
+
getStatus,
|
|
51
|
+
registerShutdownHandlers,
|
|
52
|
+
route,
|
|
53
|
+
setStatus,
|
|
54
|
+
};
|
package/index.js
CHANGED
|
@@ -1,113 +1,252 @@
|
|
|
1
|
-
|
|
2
|
-
var fs = require("fs");
|
|
1
|
+
'use strict';
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
var toobusy = false;
|
|
6
|
-
var stopping = false;
|
|
3
|
+
let status = 503;
|
|
7
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Set the HTTP status returned by the readiness route.
|
|
7
|
+
*
|
|
8
|
+
* Kubernetes considers responses from 200 through 399 successful.
|
|
9
|
+
*
|
|
10
|
+
* @param {number} code HTTP status code
|
|
11
|
+
*/
|
|
8
12
|
function setStatus(code) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
status = code;
|
|
15
|
-
}
|
|
13
|
+
if (!Number.isInteger(code) || code < 100 || code > 599) {
|
|
14
|
+
const error = new Error('status should be an integer between 100 and 599');
|
|
15
|
+
error.status = code;
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
return status;
|
|
19
|
+
status = code;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
|
-
*
|
|
23
|
+
* Return the status currently exposed by the readiness route.
|
|
24
|
+
*
|
|
25
|
+
* @returns {number} HTTP status code
|
|
23
26
|
*/
|
|
24
|
-
function
|
|
25
|
-
|
|
26
|
-
res.sendStatus(503).end();
|
|
27
|
-
} else {
|
|
28
|
-
res.sendStatus(status).end();
|
|
29
|
-
}
|
|
27
|
+
function getStatus() {
|
|
28
|
+
return status;
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
/**
|
|
33
|
-
*
|
|
32
|
+
* Express/Connect readiness route.
|
|
34
33
|
*/
|
|
35
|
-
function
|
|
36
|
-
|
|
37
|
-
res.set("Connection", "close");
|
|
38
|
-
}
|
|
39
|
-
next();
|
|
34
|
+
function route(_req, res) {
|
|
35
|
+
res.sendStatus(status);
|
|
40
36
|
}
|
|
41
37
|
|
|
42
|
-
function
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
38
|
+
function validateTimeout(name, value) {
|
|
39
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
40
|
+
throw new TypeError(`${name} should be a non-negative integer`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function validateFunction(name, value) {
|
|
45
|
+
if (value !== undefined && typeof value !== 'function') {
|
|
46
|
+
throw new TypeError(`${name} should be a function`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function normalizeError(value, message) {
|
|
51
|
+
if (value instanceof Error) return value;
|
|
52
|
+
return new Error(message, { cause: value });
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
56
|
+
* Register a bounded graceful-shutdown lifecycle for a Node HTTP server.
|
|
57
|
+
*
|
|
58
|
+
* Normal process signals drain active HTTP requests and exit successfully.
|
|
59
|
+
* Fatal process events use a shorter deadline and exit unsuccessfully.
|
|
60
|
+
*
|
|
61
|
+
* @param {object} server Node HTTP server
|
|
62
|
+
* @param {object} [options] lifecycle options
|
|
63
|
+
* @returns {object} shutdown controller
|
|
59
64
|
*/
|
|
60
|
-
function
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
65
|
+
function registerShutdownHandlers(server, options = {}) {
|
|
66
|
+
if (!server || typeof server.close !== 'function') {
|
|
67
|
+
throw new TypeError('server should provide a close(callback) function');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const timeoutMs = options.timeoutMs ?? 30_000;
|
|
71
|
+
const fatalTimeoutMs = options.fatalTimeoutMs ?? Math.min(timeoutMs, 30_000);
|
|
72
|
+
const cleanup = options.cleanup ?? (async () => {});
|
|
73
|
+
const forceClose = options.forceClose;
|
|
74
|
+
const onFatal = options.onFatal;
|
|
75
|
+
const exit = options.exit ?? process.exit.bind(process);
|
|
76
|
+
|
|
77
|
+
validateTimeout('timeoutMs', timeoutMs);
|
|
78
|
+
validateTimeout('fatalTimeoutMs', fatalTimeoutMs);
|
|
79
|
+
validateFunction('cleanup', cleanup);
|
|
80
|
+
validateFunction('forceClose', forceClose);
|
|
81
|
+
validateFunction('onFatal', onFatal);
|
|
82
|
+
validateFunction('exit', exit);
|
|
83
|
+
|
|
84
|
+
let shuttingDown = false;
|
|
85
|
+
let finished = false;
|
|
86
|
+
let exitCode = 0;
|
|
87
|
+
let timer;
|
|
88
|
+
let timerDeadline = Infinity;
|
|
89
|
+
let resolveCompletion;
|
|
90
|
+
let completion;
|
|
91
|
+
let context;
|
|
92
|
+
|
|
93
|
+
function dispose() {
|
|
94
|
+
process.removeListener('SIGTERM', onSigterm);
|
|
95
|
+
process.removeListener('SIGINT', onSigint);
|
|
96
|
+
process.removeListener('uncaughtException', onUncaughtException);
|
|
97
|
+
process.removeListener('unhandledRejection', onUnhandledRejection);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function reportFatal(error, origin) {
|
|
101
|
+
exitCode = 1;
|
|
102
|
+
context.error = error;
|
|
103
|
+
context.origin = origin;
|
|
104
|
+
context.fatal = true;
|
|
105
|
+
|
|
106
|
+
if (onFatal) {
|
|
107
|
+
try {
|
|
108
|
+
onFatal(error, origin);
|
|
109
|
+
} catch (reportingError) {
|
|
110
|
+
console.error(reportingError);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function complete(code) {
|
|
116
|
+
if (finished) return;
|
|
117
|
+
finished = true;
|
|
118
|
+
clearTimeout(timer);
|
|
119
|
+
dispose();
|
|
120
|
+
resolveCompletion(code);
|
|
121
|
+
exit(code);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function forceShutdown() {
|
|
125
|
+
if (finished) return;
|
|
126
|
+
|
|
127
|
+
const forcedContext = { ...context, forced: true };
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
forceClose?.(forcedContext);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
reportFatal(error, 'forceClose');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
server.closeAllConnections?.();
|
|
137
|
+
} catch (error) {
|
|
138
|
+
reportFatal(error, 'server.closeAllConnections');
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
complete(1);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function scheduleDeadline(delayMs) {
|
|
145
|
+
const deadline = Date.now() + delayMs;
|
|
146
|
+
if (deadline >= timerDeadline) return;
|
|
147
|
+
|
|
148
|
+
clearTimeout(timer);
|
|
149
|
+
timerDeadline = deadline;
|
|
150
|
+
timer = setTimeout(forceShutdown, delayMs);
|
|
151
|
+
timer.unref?.();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function finishGracefully(serverError) {
|
|
155
|
+
if (finished) return;
|
|
156
|
+
|
|
157
|
+
if (serverError) {
|
|
158
|
+
reportFatal(serverError, 'server.close');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
await cleanup({ ...context, forced: false });
|
|
163
|
+
} catch (error) {
|
|
164
|
+
reportFatal(error, 'cleanup');
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
complete(exitCode);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function shutdown(request = {}) {
|
|
171
|
+
const reason = request.reason ?? 'manual';
|
|
172
|
+
const error = request.error;
|
|
173
|
+
const fatal = request.fatal ?? error !== undefined;
|
|
174
|
+
|
|
175
|
+
if (shuttingDown) {
|
|
176
|
+
if (fatal) {
|
|
177
|
+
const fatalError = normalizeError(error, `Fatal shutdown requested by ${reason}`);
|
|
178
|
+
reportFatal(fatalError, reason);
|
|
179
|
+
scheduleDeadline(fatalTimeoutMs);
|
|
180
|
+
}
|
|
181
|
+
return completion;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
shuttingDown = true;
|
|
185
|
+
exitCode = fatal ? 1 : 0;
|
|
186
|
+
context = { reason, error: undefined, origin: undefined, fatal, forced: false };
|
|
187
|
+
completion = new Promise(resolve => {
|
|
188
|
+
resolveCompletion = resolve;
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
setStatus(503);
|
|
192
|
+
|
|
193
|
+
if (fatal) {
|
|
194
|
+
const fatalError = normalizeError(error, `Fatal shutdown requested by ${reason}`);
|
|
195
|
+
reportFatal(fatalError, reason);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
scheduleDeadline(fatal ? fatalTimeoutMs : timeoutMs);
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
server.close(serverError => {
|
|
202
|
+
void finishGracefully(serverError);
|
|
203
|
+
});
|
|
204
|
+
} catch (serverError) {
|
|
205
|
+
void finishGracefully(serverError);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return completion;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function close() {
|
|
212
|
+
return shutdown({ reason: 'manual' });
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function onSigterm() {
|
|
216
|
+
void shutdown({ reason: 'SIGTERM' });
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function onSigint() {
|
|
220
|
+
void shutdown({ reason: 'SIGINT' });
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function onUncaughtException(error, origin) {
|
|
224
|
+
void shutdown({ reason: origin ?? 'uncaughtException', error, fatal: true });
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function onUnhandledRejection(reason) {
|
|
228
|
+
const error = normalizeError(reason, 'Unhandled promise rejection');
|
|
229
|
+
void shutdown({ reason: 'unhandledRejection', error, fatal: true });
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
process.once('SIGTERM', onSigterm);
|
|
233
|
+
process.once('SIGINT', onSigint);
|
|
234
|
+
process.once('uncaughtException', onUncaughtException);
|
|
235
|
+
process.once('unhandledRejection', onUnhandledRejection);
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
close,
|
|
239
|
+
dispose,
|
|
240
|
+
shutdown,
|
|
241
|
+
get isShuttingDown() {
|
|
242
|
+
return shuttingDown;
|
|
243
|
+
},
|
|
244
|
+
};
|
|
104
245
|
}
|
|
105
246
|
|
|
106
247
|
module.exports = {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
enableTooBusy: enableTooBusy,
|
|
112
|
-
gracefulShutdownKeepaliveConnections: gracefulShutdownKeepaliveConnections,
|
|
248
|
+
setStatus,
|
|
249
|
+
getStatus,
|
|
250
|
+
route,
|
|
251
|
+
registerShutdownHandlers,
|
|
113
252
|
};
|
package/package.json
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "connect-ready",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Kubernetes readiness and graceful shutdown for Node.js HTTP services",
|
|
5
5
|
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"index.js",
|
|
9
|
+
"index.d.ts"
|
|
10
|
+
],
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=22"
|
|
13
|
+
},
|
|
6
14
|
"scripts": {
|
|
7
|
-
"test": "
|
|
8
|
-
"test:cover": "
|
|
9
|
-
"lint": "
|
|
15
|
+
"test": "node --test",
|
|
16
|
+
"test:cover": "node --experimental-test-coverage --test",
|
|
17
|
+
"lint": "node --check index.js && node --check test/index.test.js"
|
|
10
18
|
},
|
|
11
19
|
"repository": {
|
|
12
20
|
"type": "git",
|
|
@@ -22,19 +30,5 @@
|
|
|
22
30
|
"bugs": {
|
|
23
31
|
"url": "https://github.com/dcolens/connect-ready/issues"
|
|
24
32
|
},
|
|
25
|
-
"homepage": "https://github.com/dcolens/connect-ready#readme"
|
|
26
|
-
"devDependencies": {
|
|
27
|
-
"c8": "^7.11.0",
|
|
28
|
-
"coveralls": "^3.1.1",
|
|
29
|
-
"eslint": "^8.9.0",
|
|
30
|
-
"eslint-config-prettier": "^8.3.0",
|
|
31
|
-
"eslint-plugin-prettier": "^4.0.0",
|
|
32
|
-
"mocha": "^9.2.0",
|
|
33
|
-
"prettier": "^2.5.1",
|
|
34
|
-
"toobusy-js": "^0.5.1"
|
|
35
|
-
},
|
|
36
|
-
"optionalDependencies": {
|
|
37
|
-
"toobusy-js": "^0.5.1"
|
|
38
|
-
},
|
|
39
|
-
"dependencies": {}
|
|
33
|
+
"homepage": "https://github.com/dcolens/connect-ready#readme"
|
|
40
34
|
}
|
package/.editorconfig
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
# EditorConfig is awesome: http://EditorConfig.org
|
|
2
|
-
|
|
3
|
-
root = true
|
|
4
|
-
|
|
5
|
-
[*.js]
|
|
6
|
-
charset = utf-8
|
|
7
|
-
end_of_line = lf
|
|
8
|
-
indent_size = 1
|
|
9
|
-
indent_style = tab
|
|
10
|
-
insert_final_newline = true
|
|
11
|
-
trim_trailing_whitespace = true
|
|
12
|
-
|
|
13
|
-
[*.md]
|
|
14
|
-
trim_trailing_whitespace = false
|
package/.eslintrc.json
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
|
|
2
|
-
# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
|
|
3
|
-
|
|
4
|
-
name: Node.js Package
|
|
5
|
-
|
|
6
|
-
on:
|
|
7
|
-
release:
|
|
8
|
-
types: [created]
|
|
9
|
-
|
|
10
|
-
jobs:
|
|
11
|
-
build:
|
|
12
|
-
runs-on: ubuntu-latest
|
|
13
|
-
|
|
14
|
-
strategy:
|
|
15
|
-
matrix:
|
|
16
|
-
node-version: [10.x, 12.x, 14.x, 15.x]
|
|
17
|
-
|
|
18
|
-
steps:
|
|
19
|
-
- uses: actions/checkout@v2
|
|
20
|
-
- name: Use Node.js ${{ matrix.node-version }}
|
|
21
|
-
uses: actions/setup-node@v1
|
|
22
|
-
with:
|
|
23
|
-
node-version: ${{ matrix.node-version }}
|
|
24
|
-
- run: npm ci
|
|
25
|
-
- run: npm run build --if-present
|
|
26
|
-
- run: npm test
|
|
27
|
-
|
|
28
|
-
publish-npm:
|
|
29
|
-
needs: build
|
|
30
|
-
runs-on: ubuntu-latest
|
|
31
|
-
steps:
|
|
32
|
-
- uses: actions/checkout@v2
|
|
33
|
-
- uses: actions/setup-node@v1
|
|
34
|
-
with:
|
|
35
|
-
node-version: 14
|
|
36
|
-
registry-url: https://registry.npmjs.org/
|
|
37
|
-
- run: npm ci
|
|
38
|
-
- run: npm publish
|
|
39
|
-
env:
|
|
40
|
-
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
|
|
41
|
-
|