Event handler that fires when a Node.js process exits regardless of how termination occurs.
npx @tessl/cli install tessl/npm-signal-exit@4.1.00
# Signal Exit
1
2
Signal Exit provides a reliable way to execute cleanup code when a Node.js process exits, regardless of how the termination occurs - whether through normal completion, explicit `process.exit()` calls, signal handling, or fatal external signals.
3
4
## Package Information
5
6
- **Package Name**: signal-exit
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Installation**: `npm install signal-exit`
10
11
## Core Imports
12
13
```typescript
14
import { onExit } from "signal-exit";
15
```
16
17
For CommonJS:
18
19
```javascript
20
const { onExit } = require("signal-exit");
21
```
22
23
For signals array:
24
25
```typescript
26
import { signals } from "signal-exit/signals";
27
```
28
29
For browser environments:
30
31
```typescript
32
import { onExit } from "signal-exit/browser";
33
```
34
35
## Basic Usage
36
37
```typescript
38
import { onExit } from "signal-exit";
39
40
// Register an exit handler
41
const removeHandler = onExit((code, signal) => {
42
console.log(`Process exiting with code: ${code}, signal: ${signal}`);
43
// Perform cleanup operations here
44
});
45
46
// Remove the handler if needed
47
// removeHandler();
48
```
49
50
## Architecture
51
52
Signal Exit is built around several key components that work together to provide reliable exit handling:
53
54
- **Signal Handler Registration**: Platform-aware signal listener setup that automatically handles differences between Windows, Linux, and macOS
55
- **Event Emitter System**: Internal event system that coordinates multiple exit handlers and ensures proper execution order
56
- **Process Instrumentation**: Monkey-patching of `process.emit` and `process.reallyExit` to intercept all exit scenarios
57
- **Fallback Mechanisms**: Graceful degradation when process instrumentation is not possible (browser environments, limited process objects)
58
- **Handler Queue Management**: Dual-queue system supporting both normal and "always last" handler execution
59
60
The library uses a singleton pattern to ensure only one instance manages exit handling per process, with automatic cleanup and re-registration capabilities for complex scenarios.
61
62
## Capabilities
63
64
### Exit Handler Registration
65
66
Register a function to be called when the process exits for any reason.
67
68
```typescript { .api }
69
/**
70
* Called when the process is exiting, whether via signal, explicit
71
* exit, or running out of stuff to do.
72
*
73
* If the global process object is not suitable for instrumentation,
74
* then this will be a no-op.
75
*
76
* Returns a function that may be used to unload signal-exit.
77
*/
78
function onExit(
79
cb: Handler,
80
opts?: { alwaysLast?: boolean }
81
): () => void;
82
83
/**
84
* A function that takes an exit code and signal as arguments
85
*
86
* In the case of signal exits *only*, a return value of true
87
* will indicate that the signal is being handled, and we should
88
* not synthetically exit with the signal we received. Regardless
89
* of the handler return value, the handler is unloaded when an
90
* otherwise fatal signal is received, so you get exactly 1 shot
91
* at it, unless you add another onExit handler at that point.
92
*
93
* In the case of numeric code exits, we may already have committed
94
* to exiting the process, for example via a fatal exception or
95
* unhandled promise rejection, so it is impossible to stop safely.
96
*/
97
type Handler = (
98
code: number | null | undefined,
99
signal: NodeJS.Signals | null
100
) => true | void;
101
```
102
103
**Usage Examples:**
104
105
```typescript
106
import { onExit } from "signal-exit";
107
108
// Basic cleanup handler
109
onExit((code, signal) => {
110
console.log("Cleaning up resources...");
111
// Close database connections, save files, etc.
112
});
113
114
// Handler that runs after all other handlers
115
onExit((code, signal) => {
116
console.log("Final cleanup step");
117
}, { alwaysLast: true });
118
119
// Signal capture (prevent synthetic exit)
120
onExit((code, signal) => {
121
if (signal === 'SIGTERM') {
122
console.log("Graceful shutdown initiated");
123
// Perform graceful shutdown
124
return true; // Prevent synthetic process.kill
125
}
126
});
127
```
128
129
### Signal List Access
130
131
Access the array of signals that trigger exit handlers.
132
133
```typescript { .api }
134
/**
135
* Platform-specific array of signals that can trigger exit handlers.
136
* Contains signals like SIGHUP, SIGINT, SIGTERM, and others based on the platform.
137
*/
138
const signals: NodeJS.Signals[];
139
```
140
141
**Usage Examples:**
142
143
```typescript
144
import { signals } from "signal-exit/signals";
145
146
console.log("Monitored signals:", signals);
147
// On Linux: ['SIGHUP', 'SIGINT', 'SIGTERM', 'SIGALRM', 'SIGABRT', ...]
148
// On Windows: ['SIGHUP', 'SIGINT', 'SIGTERM']
149
```
150
151
### Internal Signal Management
152
153
Advanced functions for controlling the signal exit machinery (primarily for testing).
154
155
```typescript { .api }
156
/**
157
* Load the listeners. Likely you never need to call this, unless
158
* doing a rather deep integration with signal-exit functionality.
159
* Mostly exposed for the benefit of testing.
160
*
161
* @internal
162
*/
163
function load(): void;
164
165
/**
166
* Unload the listeners. Likely you never need to call this, unless
167
* doing a rather deep integration with signal-exit functionality.
168
* Mostly exposed for the benefit of testing.
169
*
170
* @internal
171
*/
172
function unload(): void;
173
```
174
175
### Browser Fallback
176
177
No-op implementations for browser environments where process signals don't exist.
178
179
```typescript { .api }
180
/**
181
* Browser-compatible version that provides the same interface
182
* but performs no operations (no-op functions).
183
*/
184
const onExit: (
185
cb: Handler,
186
opts: { alwaysLast?: boolean }
187
) => () => void;
188
189
const load: () => void;
190
const unload: () => void;
191
```
192
193
**Usage Examples:**
194
195
```typescript
196
import { onExit } from "signal-exit/browser";
197
198
// Works in browser but does nothing
199
const remove = onExit((code, signal) => {
200
console.log("This won't be called in browser");
201
});
202
```
203
204
## Advanced Usage Patterns
205
206
### Conditional Signal Handling
207
208
```typescript
209
import { onExit } from "signal-exit";
210
211
onExit((code, signal) => {
212
if (signal) {
213
console.log(`Received signal: ${signal}`);
214
215
// Handle different signals differently
216
switch (signal) {
217
case 'SIGTERM':
218
console.log("Graceful shutdown requested");
219
performGracefulShutdown();
220
return true; // Prevent synthetic kill
221
222
case 'SIGINT':
223
console.log("Interrupt signal received");
224
cleanup();
225
break;
226
227
default:
228
console.log("Other signal received");
229
cleanup();
230
}
231
} else {
232
console.log(`Process exiting with code: ${code}`);
233
cleanup();
234
}
235
});
236
```
237
238
### Multiple Handlers with Ordering
239
240
```typescript
241
import { onExit } from "signal-exit";
242
243
// This runs first
244
const remove1 = onExit((code, signal) => {
245
console.log("First handler - emergency cleanup");
246
emergencyCleanup();
247
});
248
249
// This runs second
250
const remove2 = onExit((code, signal) => {
251
console.log("Second handler - regular cleanup");
252
regularCleanup();
253
});
254
255
// This runs last (after all other handlers)
256
const remove3 = onExit((code, signal) => {
257
console.log("Final handler - logging and reporting");
258
logShutdown(code, signal);
259
}, { alwaysLast: true });
260
```
261
262
### Handler Cleanup
263
264
```typescript
265
import { onExit } from "signal-exit";
266
267
class Application {
268
private exitHandler?: () => void;
269
270
start() {
271
this.exitHandler = onExit((code, signal) => {
272
this.cleanup();
273
});
274
}
275
276
stop() {
277
// Remove the exit handler when no longer needed
278
if (this.exitHandler) {
279
this.exitHandler();
280
this.exitHandler = undefined;
281
}
282
}
283
284
cleanup() {
285
console.log("Cleaning up application resources");
286
}
287
}
288
```
289
290
## Platform Compatibility
291
292
- **Node.js**: Full functionality with platform-specific signal handling
293
- **Windows**: Limited signal support (SIGHUP mapped to SIGINT)
294
- **Linux/macOS**: Full POSIX signal support
295
- **Browser**: Fallback module provides compatible no-op interface
296
297
## Error Handling
298
299
The library is designed to be robust and handle edge cases:
300
301
- If the global `process` object is unavailable, `onExit` returns a no-op function
302
- Signal handlers are automatically cleaned up after execution
303
- Multiple versions of signal-exit can coexist (backward compatibility)
304
- Handlers are protected against re-execution during shutdown
305
306
## Common Use Cases
307
308
- **CLI Applications**: Cleanup temporary files and restore terminal state
309
- **Server Applications**: Graceful shutdown of HTTP servers and database connections
310
- **Testing Frameworks**: Reset global state between tests
311
- **Development Tools**: Save unsaved work and cleanup development artifacts
312
- **Process Managers**: Coordinate shutdown of child processes