or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

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

index.mddocs/

Signal Exit

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.

Package Information

  • Package Name: signal-exit
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install signal-exit

Core Imports

import { onExit } from "signal-exit";

For CommonJS:

const { onExit } = require("signal-exit");

For signals array:

import { signals } from "signal-exit/signals";

For browser environments:

import { onExit } from "signal-exit/browser";

Basic Usage

import { onExit } from "signal-exit";

// Register an exit handler
const removeHandler = onExit((code, signal) => {
  console.log(`Process exiting with code: ${code}, signal: ${signal}`);
  // Perform cleanup operations here
});

// Remove the handler if needed
// removeHandler();

Architecture

Signal Exit is built around several key components that work together to provide reliable exit handling:

  • Signal Handler Registration: Platform-aware signal listener setup that automatically handles differences between Windows, Linux, and macOS
  • Event Emitter System: Internal event system that coordinates multiple exit handlers and ensures proper execution order
  • Process Instrumentation: Monkey-patching of process.emit and process.reallyExit to intercept all exit scenarios
  • Fallback Mechanisms: Graceful degradation when process instrumentation is not possible (browser environments, limited process objects)
  • Handler Queue Management: Dual-queue system supporting both normal and "always last" handler execution

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.

Capabilities

Exit Handler Registration

Register a function to be called when the process exits for any reason.

/**
 * Called when the process is exiting, whether via signal, explicit
 * exit, or running out of stuff to do.
 *
 * If the global process object is not suitable for instrumentation,
 * then this will be a no-op.
 *
 * Returns a function that may be used to unload signal-exit.
 */
function onExit(
  cb: Handler,
  opts?: { alwaysLast?: boolean }
): () => void;

/**
 * A function that takes an exit code and signal as arguments
 *
 * In the case of signal exits *only*, a return value of true
 * will indicate that the signal is being handled, and we should
 * not synthetically exit with the signal we received. Regardless
 * of the handler return value, the handler is unloaded when an
 * otherwise fatal signal is received, so you get exactly 1 shot
 * at it, unless you add another onExit handler at that point.
 *
 * In the case of numeric code exits, we may already have committed
 * to exiting the process, for example via a fatal exception or
 * unhandled promise rejection, so it is impossible to stop safely.
 */
type Handler = (
  code: number | null | undefined,
  signal: NodeJS.Signals | null
) => true | void;

Usage Examples:

import { onExit } from "signal-exit";

// Basic cleanup handler
onExit((code, signal) => {
  console.log("Cleaning up resources...");
  // Close database connections, save files, etc.
});

// Handler that runs after all other handlers
onExit((code, signal) => {
  console.log("Final cleanup step");
}, { alwaysLast: true });

// Signal capture (prevent synthetic exit)
onExit((code, signal) => {
  if (signal === 'SIGTERM') {
    console.log("Graceful shutdown initiated");
    // Perform graceful shutdown
    return true; // Prevent synthetic process.kill
  }
});

Signal List Access

Access the array of signals that trigger exit handlers.

/**
 * Platform-specific array of signals that can trigger exit handlers.
 * Contains signals like SIGHUP, SIGINT, SIGTERM, and others based on the platform.
 */
const signals: NodeJS.Signals[];

Usage Examples:

import { signals } from "signal-exit/signals";

console.log("Monitored signals:", signals);
// On Linux: ['SIGHUP', 'SIGINT', 'SIGTERM', 'SIGALRM', 'SIGABRT', ...]
// On Windows: ['SIGHUP', 'SIGINT', 'SIGTERM']

Internal Signal Management

Advanced functions for controlling the signal exit machinery (primarily for testing).

/**
 * Load the listeners. Likely you never need to call this, unless
 * doing a rather deep integration with signal-exit functionality.
 * Mostly exposed for the benefit of testing.
 *
 * @internal
 */
function load(): void;

/**
 * Unload the listeners. Likely you never need to call this, unless
 * doing a rather deep integration with signal-exit functionality.
 * Mostly exposed for the benefit of testing.
 *
 * @internal
 */
function unload(): void;

Browser Fallback

No-op implementations for browser environments where process signals don't exist.

/**
 * Browser-compatible version that provides the same interface
 * but performs no operations (no-op functions).
 */
const onExit: (
  cb: Handler,
  opts: { alwaysLast?: boolean }
) => () => void;

const load: () => void;
const unload: () => void;

Usage Examples:

import { onExit } from "signal-exit/browser";

// Works in browser but does nothing
const remove = onExit((code, signal) => {
  console.log("This won't be called in browser");
});

Advanced Usage Patterns

Conditional Signal Handling

import { onExit } from "signal-exit";

onExit((code, signal) => {
  if (signal) {
    console.log(`Received signal: ${signal}`);
    
    // Handle different signals differently
    switch (signal) {
      case 'SIGTERM':
        console.log("Graceful shutdown requested");
        performGracefulShutdown();
        return true; // Prevent synthetic kill
      
      case 'SIGINT':
        console.log("Interrupt signal received");
        cleanup();
        break;
      
      default:
        console.log("Other signal received");
        cleanup();
    }
  } else {
    console.log(`Process exiting with code: ${code}`);
    cleanup();
  }
});

Multiple Handlers with Ordering

import { onExit } from "signal-exit";

// This runs first
const remove1 = onExit((code, signal) => {
  console.log("First handler - emergency cleanup");
  emergencyCleanup();
});

// This runs second
const remove2 = onExit((code, signal) => {
  console.log("Second handler - regular cleanup");
  regularCleanup();
});

// This runs last (after all other handlers)
const remove3 = onExit((code, signal) => {
  console.log("Final handler - logging and reporting");
  logShutdown(code, signal);
}, { alwaysLast: true });

Handler Cleanup

import { onExit } from "signal-exit";

class Application {
  private exitHandler?: () => void;
  
  start() {
    this.exitHandler = onExit((code, signal) => {
      this.cleanup();
    });
  }
  
  stop() {
    // Remove the exit handler when no longer needed
    if (this.exitHandler) {
      this.exitHandler();
      this.exitHandler = undefined;
    }
  }
  
  cleanup() {
    console.log("Cleaning up application resources");
  }
}

Platform Compatibility

  • Node.js: Full functionality with platform-specific signal handling
  • Windows: Limited signal support (SIGHUP mapped to SIGINT)
  • Linux/macOS: Full POSIX signal support
  • Browser: Fallback module provides compatible no-op interface

Error Handling

The library is designed to be robust and handle edge cases:

  • If the global process object is unavailable, onExit returns a no-op function
  • Signal handlers are automatically cleaned up after execution
  • Multiple versions of signal-exit can coexist (backward compatibility)
  • Handlers are protected against re-execution during shutdown

Common Use Cases

  • CLI Applications: Cleanup temporary files and restore terminal state
  • Server Applications: Graceful shutdown of HTTP servers and database connections
  • Testing Frameworks: Reset global state between tests
  • Development Tools: Save unsaved work and cleanup development artifacts
  • Process Managers: Coordinate shutdown of child processes