Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

node-graceful

Package Overview
Dependencies
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

node-graceful - npm Package Compare versions

Comparing version 2.0.1 to 3.0.0

index.ts

48

index.d.ts

@@ -1,23 +0,27 @@

type SignalListener = (done: () => void, event: any, signal: string) => (undefined | Promise<void> | Promise<any> | Promise<Error>)
type cancelSubscription = () => void
export declare module Graceful {
let exitOnDouble: boolean;
let timeout: number;
function on(event: string, listener: Function, deadly?: boolean): cancelSubscription;
function on(event: "SIGTERM", listener: SignalListener, deadly?: boolean): cancelSubscription;
function on(event: "SIGINT", listener: SignalListener, deadly?: boolean): cancelSubscription;
function on(event: "SIGBREAK", listener: SignalListener, deadly?: boolean): cancelSubscription;
function on(event: "SIGHUP", listener: SignalListener, deadly?: boolean): cancelSubscription;
function on(event: "exit", listener: SignalListener, deadly?: boolean): cancelSubscription;
function off(event: string, listener: Function): void;
function clear(signal?: string): void;
function exit(code?: number | string, signal?: string): void;
export default class Graceful {
private static DEADLY_SIGNALS;
static exitOnDouble: boolean;
static timeout: number;
private static _captureExceptions;
private static _captureRejections;
private static listeners;
private static isRegistered;
private static isExiting;
private static exceptionListener;
private static rejectionListener;
private static signalsListeners;
static get captureExceptions(): boolean;
static set captureExceptions(newValue: boolean);
static get captureRejections(): boolean;
static set captureRejections(newValue: boolean);
static on(signal: 'exit', listener: GracefulListener): GracefulSubscription;
static off(signal: 'exit', listener: GracefulListener): void;
static clear(): void;
static exit(code?: number | string, signal?: string): void;
private static onDeadlyEvent;
private static invokeListener;
private static updateRegistration;
private static killProcess;
}
export default Graceful
export declare type GracefulListener = (signal: string, details?: object) => (void | any | Promise<any> | Promise<void> | Promise<Error>);
export declare type GracefulSubscription = () => void;
// +----------------------------------------------------------------------+
// | node-graceful v2 (https://github.com/mrbar42/node-graceful) |
// | node-graceful v3 (https://github.com/mrbar42/node-graceful) |
// | Graceful process exit manager. |
// |----------------------------------------------------------------------|
'use strict';
function Graceful() {
// options
this.exitOnDouble = true;
this.timeout = 30000;
// constants
this.DEADLY_SIGNALS = ['SIGTERM', 'SIGINT', 'SIGBREAK', 'SIGHUP'];
// state
this._listeners = Object.create(null);
this.isExiting = false;
}
Graceful.prototype.on = function on(signal, listener, deadly) {
this._registerSignal(signal);
this._listeners[signal].push(listener);
// add signal to deadly list
if (deadly && this.DEADLY_SIGNALS.indexOf(signal) === -1) {
this.DEADLY_SIGNALS.push(signal);
Object.defineProperty(exports, "__esModule", { value: true });
var Graceful = /** @class */ (function () {
function Graceful() {
}
return () => this.off(signal, listener);
};
Graceful.prototype.off = function off(signal, listener) {
if (!this._listeners[signal]) return;
// remove listener if exists
let index = this._listeners[signal].indexOf(listener);
if (index !== -1) this._listeners[signal].splice(index, 1);
// clear master listener if no listeners left
if (!this._listeners[signal].length) {
this._unregisterSignal(signal);
}
};
Graceful.prototype.clear = function clear(signal) {
if (signal) {
delete this._listeners[signal];
this._unregisterSignal(signal);
}
else {
Object
.keys(this._listeners)
.forEach(sig => this.clear(sig));
}
};
Graceful.prototype.exit = function exit(code, signal) {
if (typeof code === 'number') {
process.exitCode = code;
}
let simulatedSignal = signal || this.DEADLY_SIGNALS[0];
this._processSignal(simulatedSignal);
};
Graceful.prototype._registerSignal = function _registerSignal(signal) {
if (this._listeners[signal]) return;
this._listeners[signal] = [];
let handler = event => this._processSignal(signal, event);
// handle special 'exit' event case
if (signal === 'exit') {
this.DEADLY_SIGNALS.forEach(deadlySignal => process.on(deadlySignal, handler));
}
else {
process.on(signal, handler);
}
// store handler on listeners array for future ref
this._listeners[signal].__handler__ = handler;
};
Graceful.prototype._unregisterSignal = function _unregisterSignal(signal) {
if (!this._listeners[signal]) return;
let handler = this._listeners[signal].__handler__;
// handle special 'exit' event case
if (signal === 'exit') {
this.DEADLY_SIGNALS.forEach(deadlySignal => process.removeListener(deadlySignal, handler));
}
else {
process.removeListener(signal, handler);
}
delete this._listeners[signal];
};
Graceful.prototype._processSignal = function _processSignal(signal, event) {
let deadly = signal === 'exit' || this.DEADLY_SIGNALS.indexOf(signal) !== -1;
let listeners = this._listeners[signal] && this._listeners[signal].slice();
let exitListeners = this._listeners['exit'] && this._listeners['exit'].slice();
let targetCount = listeners && listeners.length || 0;
// also include exit listeners if deadly
if (deadly && exitListeners && signal !== 'exit') {
targetCount += exitListeners.length;
}
// this should never happen
if (!targetCount) {
return process.nextTick(() => this._killProcess());
}
let quit = (() => {
let count = 0;
return () => {
count++;
if (count >= targetCount) {
if (deadly) this._killProcess();
Object.defineProperty(Graceful, "captureExceptions", {
get: function () {
return Graceful._captureExceptions;
},
set: function (newValue) {
if (Graceful._captureExceptions === newValue)
return;
Graceful._captureExceptions = newValue;
if (Graceful._captureExceptions) {
process.on('uncaughtException', Graceful.exceptionListener);
}
else {
process.removeListener('uncaughtException', Graceful.exceptionListener);
}
},
enumerable: true,
configurable: true
});
Object.defineProperty(Graceful, "captureRejections", {
get: function () {
return Graceful._captureRejections;
},
set: function (newValue) {
if (Graceful._captureRejections === newValue)
return;
Graceful._captureRejections = newValue;
if (Graceful._captureRejections) {
process.on('unhandledRejection', Graceful.rejectionListener);
}
else {
process.removeListener('unhandledRejection', Graceful.rejectionListener);
}
},
enumerable: true,
configurable: true
});
Graceful.on = function (signal, listener) {
if (signal !== 'exit')
throw new Error('Only supports \'exit\' signal');
Graceful.listeners.push(listener);
Graceful.updateRegistration();
return function () { return Graceful.off('exit', listener); };
};
Graceful.off = function (signal, listener) {
var index = Graceful.listeners.indexOf(listener);
if (index !== -1)
Graceful.listeners.splice(index, 1);
Graceful.updateRegistration();
};
Graceful.clear = function () {
Graceful.listeners.splice(0, Infinity);
Graceful.updateRegistration();
};
Graceful.exit = function (code, signal) {
if (signal === void 0) { signal = 'SIGTERM'; }
var exitSignal = typeof code === 'string' ? code : signal;
if (typeof code === 'number') {
process.exitCode = code;
}
Graceful.onDeadlyEvent(exitSignal, { reason: 'Manual call to Graceful.exit()' });
};
Graceful.onDeadlyEvent = function (signal, details) {
// console.log(signal, details);
if (Graceful.isExiting) {
if (Graceful.exitOnDouble)
Graceful.killProcess(true);
return;
}
var listeners = Graceful.listeners.slice(0);
Graceful.isExiting = true;
var completedListeners = 0;
var done = function () {
completedListeners++;
if (completedListeners === listeners.length) {
Graceful.killProcess(false);
}
};
})();
// exec signal specific listeners
if (listeners && signal !== 'exit') {
listeners.forEach(listener => this._invokeListener(listener, quit, event, signal));
}
// also invoke exit listeners
if (deadly && exitListeners) {
if (this.isExiting) {
if (this.exitOnDouble) this._killProcess(true);
if (Number(Graceful.timeout)) {
var timeoutRef = setTimeout(function () { return Graceful.killProcess(true); }, Graceful.timeout);
if (timeoutRef && timeoutRef.unref)
timeoutRef.unref();
}
for (var _i = 0, listeners_1 = listeners; _i < listeners_1.length; _i++) {
var listener = listeners_1[_i];
Graceful.invokeListener(listener, done, signal, details);
}
};
Graceful.invokeListener = function (listener, done, signal, details) {
var invoked = false;
var listenerDone = function () {
if (!invoked) {
invoked = true;
done();
}
};
var retVal = listener(signal, details);
// allow returning a promise
if (retVal && typeof retVal.then === 'function') {
retVal.then(listenerDone, listenerDone);
}
else {
this.isExiting = true;
if (Number(this.timeout)) {
setTimeout(() => this._killProcess(true), this.timeout);
listenerDone();
}
};
Graceful.updateRegistration = function () {
if (Graceful.listeners.length && !Graceful.isRegistered) {
var _loop_1 = function (deadlySignal) {
Graceful.signalsListeners[deadlySignal] = function () { return Graceful.onDeadlyEvent(deadlySignal); };
process.on(deadlySignal, Graceful.signalsListeners[deadlySignal]);
};
for (var _i = 0, _a = Graceful.DEADLY_SIGNALS; _i < _a.length; _i++) {
var deadlySignal = _a[_i];
_loop_1(deadlySignal);
}
exitListeners.forEach(listener => this._invokeListener(listener, quit, event, signal));
Graceful.isRegistered = true;
}
}
};
Graceful.prototype._killProcess = function _killProcess(force) {
process.exit(process.exitCode || (force ? 1 : 0));
};
Graceful.prototype._invokeListener = function _invokeListener(listener, quit, event, signal) {
let invoked = false;
// listener specific callback
let done = () => {
if (!invoked) {
invoked = true;
quit();
else if (!Graceful.listeners.length && Graceful.isRegistered) {
for (var _b = 0, _c = Graceful.DEADLY_SIGNALS; _b < _c.length; _b++) {
var deadlySignal = _c[_b];
if (Graceful.signalsListeners[deadlySignal]) {
process.removeListener(deadlySignal, Graceful.signalsListeners[deadlySignal]);
delete Graceful.signalsListeners[deadlySignal];
}
}
Graceful.isRegistered = false;
}
};
let retVal = listener(done, event, signal);
// allow returning a promise
if (retVal && typeof retVal.then === 'function' && typeof retVal.catch === 'function') {
retVal.then(done).catch(done);
}
};
let graceful = new Graceful();
module.exports = graceful;
// monkey patch exports to support both old & new & Typescript module systems
module.exports.Graceful = graceful;
module.exports.default = graceful;
Graceful.killProcess = function (force) {
process.exit(process.exitCode || (force ? 1 : 0));
};
Graceful.DEADLY_SIGNALS = ['SIGTERM', 'SIGINT', 'SIGBREAK', 'SIGHUP'];
Graceful.exitOnDouble = true;
Graceful.timeout = 30000;
Graceful._captureExceptions = false;
Graceful._captureRejections = false;
Graceful.listeners = [];
Graceful.isRegistered = false;
Graceful.isExiting = false;
Graceful.exceptionListener = function (event) {
process.exitCode = 1;
Graceful.onDeadlyEvent('uncaughtException', event);
};
Graceful.rejectionListener = function (event) {
process.exitCode = 1;
Graceful.onDeadlyEvent('unhandledRejection', event);
};
Graceful.signalsListeners = {};
return Graceful;
}());
exports.default = Graceful;
// @ts-ignore - simulate default export in way Typescript can't understand
Graceful.default = exports.default;
// Support all the possible commonjs variations including Typescript
module.exports = Graceful;
Object.defineProperty(Graceful, "__esModule", { value: true });
{
"name": "node-graceful",
"version": "2.0.1",
"version": "3.0.0",
"description": "Graceful process exit manager. allows waiting on multiple async services.",
"main": "index",
"main": "index.js",
"repository": {

@@ -17,3 +17,10 @@ "type": "git",

"SIGHUP",
"shutdown"
"shutdown",
"graceful exit",
"uncaught",
"uncaughtException",
"unhandledRejection",
"terminate",
"stop",
"process"
],

@@ -23,7 +30,13 @@ "scripts": {

"test:unit-ts": "ts-node tests/wait-for-multiple-promise.ts",
"test:lint": "tsc && tslint -c tslint.json --fix tests/*.ts",
"test:lint": "tsc --noEmit && tslint -c tslint.json --fix tests/*.ts",
"test": "npm run test:lint && npm run test:unit && npm run test:unit-ts",
"transpile": "tsc --allowJs --outFile es5/index.js --pretty index.js ",
"prepublishOnly": "npm run test && npm run transpile"
"build": "tsc --build tsconfig.json",
"prepublishOnly": "npm run test && npm run build"
},
"files": [
"index.ts",
"index.d.ts",
"index.js",
"README.md"
],
"author": "mrbar42",

@@ -33,6 +46,6 @@ "license": "MIT",

"@types/node": "8.10.48",
"ts-node": "5.0.1",
"tslint": "5.9.1",
"typescript": "2.7.2"
"ts-node": "^8.5.2",
"tslint": "^5.20.1",
"typescript": "^3.7.2"
}
}
# node-graceful
[![Build Status](https://travis-ci.org/mrbar42/node-graceful.svg?branch=master)](https://travis-ci.org/mrbar42/node-graceful)[![npm](https://img.shields.io/npm/v/node-graceful.svg)](https://www.npmjs.com/package/node-graceful)
[![Build Status](https://travis-ci.org/mrbar42/node-graceful.svg?branch=master)](https://travis-ci.org/mrbar42/node-graceful) [![npm](https://img.shields.io/npm/v/node-graceful.svg)](https://www.npmjs.com/package/node-graceful) ![npm bundle size (version)](https://img.shields.io/bundlephobia/minzip/node-graceful?label=Install%20size)

@@ -9,4 +9,8 @@ node-graceful is a small helper module without dependencies that aims to ease graceful exit

Installation:
`npm i -S node-graceful`
```sh
npm i -S node-graceful
yarn add node-graceful
```
Had any problem? open an [issue](https://github.com/mrbar42/node-graceful/issues/new)

@@ -18,51 +22,90 @@

const Graceful = require('node-graceful');
Graceful.captureExceptions = true;
Graceful.on('exit', (done, event, signal) => {
setTimeout(() => {
console.log(`Received ${signal} - Exiting gracefully`)
done()
}, 1000);
})
Graceful.on('exit', async () => {
console.log(`Received ${signal} - Exiting gracefully`);
await webServer.close();
});
// Gracefull will wait untill all listeners had finished
Graceful.on('exit', () => {
console.log("Another independant listener!");
return Promise.resolve('A promise to be waited on before dying');
// Graceful will wait until all listeners had finished
Graceful.on('exit', (signal) => {
return new Promise((resolve) => {
console.log("Another independent listener!");
setTimeout(() => resolve(), 1000);
});
});
```
The module is written in Node 4.x flavored es6.
To get the es5 transpiled version use `require('node-graceful/es5')`
Typescript
```typescript
import Graceful from 'node-graceful';
Graceful.captureExceptions = true;
Graceful.on('exit', async () => {
await server.close();
});
```
## Graceful
## Quick Docs
### Graceful.on({String} signal, {Function} listener [, {Boolean} deadly])
```typescript
interface Graceful {
// add exit listener
on(signal: 'exit', listener: GracefulListener): GracefulSubscription;
// remove exit listener
off(signal: 'exit', listener: GracefulListener): void;
// remove all exit listeners
clear(): void;
// trigger graceful process exit with or without exit code and signal
exit(): void;
exit(exitCode: number): void;
exit(exitSignal: string): void;
exit(exitCode: number, exitSignal: string): void;
Add a listener to a given signal.
Any signal can be listened on in the addition of `exit` event that will be triggered by all "Deadly events".
Graceful listens on every signal only once and propagate the event to its listeners
// whether to exit immediately when a second kill signal is received
exitOnDouble: boolean; // default: true
// maximum time to wait before hard-killing the process
timeout: number; // default: 30000
// whether to treat uncaught exceptions as process terminating events
captureExceptions: boolean; // default: false
// whether to treat unhandled promise rejections as process terminating events
captureRejections: boolean; // default: false
}
Default Deadly events: `SIGTERM` `SIGINT` `SIGBREAK` `SIGHUP`
type GracefulListener = (signal: string, details?: object) => (void | any | Promise<any> | Promise<Error>);
type GracefulSubscription = () => void;
```
Read bellow for full API reference.
## API Reference
### Graceful.on('exit', {Function} listener)
Add exit listener to be called when process exit is triggered.
`Graceful` listens on all terminating signals and triggers `exit` accordingly.
Terminating events: `SIGTERM` `SIGINT` `SIGBREAK` `SIGHUP`
#### Options
- `signal` - a string representing the signal name. this will be used directly as the event name on `process` object.
Common signals can be found [here](https://nodejs.org/api/process.html#process_signal_events).
its better to use the built in `exit` event as it catches all events that induce process exit.
- `listener(done, event, signal)` - listener function
- `done` - callback that should be called once all exiting tasks were completed
- `event` - if an event was provided by the process it will be provided as second argument else undefined
- `signal` - the signal that triggered the exit.example: 'SIGTERM'
- `listener(signal, details?)` - listener function
- `signal` - the signal that triggered the exit. example: 'SIGTERM'
- `details` - optional details provided by the trigger. for example in case of `unhandledException` this will be an error object. on external signal it will be undefined.
**note: Promise can be returned instead of calling `done`
- `deadly` - (options) boolean indicating weather this should be considered a process ending event.
e.g. should `exit` event should be called due to this event. default: false.
#### Examples
The listener function can return a promise that will delay the process exit until it's fulfilment.
```typescript
Graceful.on('exit', () => Promise.resolve('I Am A Promise!'));
Graceful.on('exit', async () => {
// async function always returns promise so shutdown will be delayed until this functions ends
await webServer.close();
#### Return value
the method returns a function that when invoked, removes the listener.
the function is a shorthand for `.off` method
return Promise.all([
controller.close(),
dbClient.close()
]);
});
```
##### example
```javascript
if old style callback is needed, wrap the logic with a promise
```
const server = require('http').createServer(function (req, res) {

@@ -72,27 +115,32 @@ res.write('ok');

})
Graceful.on('exit', () => {
return new Promise((resolve, reject) => {
server.close((err) => {
if (err) return reject(err);
resolve();
});
});
});
```
Graceful.on('exit', (done, event, signal) => {
console.log("Received exit signal");
server.close(() => {
console.log("Closed all connection. safe to exit");
done()
})
})
#### Return value
the method returns a function that when invoked, removes the listener subscription.
the function is a shorthand for `.off` method
##### example
```typescript
// use the return value to remove listener
const removeListener = Graceful.on('exit', () => {})
removeListener(); // listener was removed
const removeListener = Graceful.on('exit', () => {});
removeListener(); // listener was removed and will not be triggered
```
### Graceful.off('exit', {Function} listener)
### Graceful.off({String} signal, {Function} listener)
Remove a previously subscribed listener.
Remove listener.
##### example
```javascript
```typescript
const gracefulExit = () => {
console.log("exiting!");
}
};

@@ -104,39 +152,26 @@ // add listener

Graceful.off('SIGTERM', gracefulExit);
// or use return value of on
removeListener();
// same as invoking the return value
// removeListener();
```
### Graceful.clear({String} \[signal])
### Graceful.clear()
Remove all listeners of a given signal or all listeners of all signals.
Unsubscribe all `exit` listeners.
- `signal` - (optional) signal to be cleared from all of its listeners.
if no signal is provided all listeners for all signals are cleared
effectively resetting the module.
##### example
```javascript
const gracefulExit = () => {
console.log("exiting!");
}
// add listener
Graceful.on('exit', () => {
console.log("Received some exit signal!");
return Promise.resolve("A promise to be waited on before dying");
});
console.log("Received some exit signal!");
return Promise.resolve("A promise to be waited on before dying");
});
Graceful.on('exit', (done) => {
console.log("Another listener");
done();
});
console.log("Another listener");
done();
});
// remove all listener
Graceful.clear('exit');
// removes ALL listeners of ALL signals
// Graceful.clear();
Graceful.clear();
```

@@ -168,3 +203,5 @@

// exit code and signal can be specified
// Graceful.exit(1, 'SIGINT')
// Graceful.exit(1);
// Graceful.exit(1, 'SIGINT');
// Graceful.exit('SIGINT');
```

@@ -176,3 +213,3 @@

#### Graceful.exitOnDouble = true {Boolean}
#### Graceful.exitOnDouble = true {boolean}

@@ -183,3 +220,3 @@ Whether to exit immediately when a second deadly event is received,

#### Graceful.timeout = 30000 {Number}
#### Graceful.timeout = 30000 {number}

@@ -192,5 +229,25 @@ Maximum time to wait for exit listeners in `ms`.

#### Graceful.captureExceptions = false {boolean}
Whether to treat `uncaughtException` event as a terminating event and trigger graceful shutdown.
```typescript
Graceful.captureExceptions = true;
throw new Error('DANG!'); // this will now trigger graceful shutdown
```
#### Graceful.captureExceptions = false {boolean}
Whether to treat `unhandledRejection` event as a terminating event and trigger graceful shutdown.
On newer `node` versions `unhandledRejection` is in-fact a terminating event
```typescript
Graceful.captureRejections = true;
Promise.reject(new Error('DANG!')); // this will now trigger graceful shutdown
```
#### exitCode
Graceful will obey process.exitCode property value when exiting
apart from forced exit cases where the exit code must be non-zero.
`Graceful` will obey `process.exitCode` property value when exiting unless the exit is forced (double signal, timeout) in which case the exit code must be non-zero.
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc