or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

tessl/npm-signal-exit

Event handler that fires when a Node.js process exits regardless of how termination occurs.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/signal-exit@4.1.x

To install, run

npx @tessl/cli install tessl/npm-signal-exit@4.1.0

0

# 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