@darkobits/adeiu
Advanced tools
Comparing version 0.2.3 to 0.2.4
@@ -5,2 +5,6 @@ # Changelog | ||
### [0.2.4](https://github.com/darkobits/adeiu/compare/v0.2.3...v0.2.4) (2019-06-29) | ||
### [0.2.3](https://github.com/darkobits/adeiu/compare/v0.2.2...v0.2.3) (2019-06-29) | ||
@@ -7,0 +11,0 @@ |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const chalk_1 = __importDefault(require("chalk")); | ||
const ow_1 = __importDefault(require("ow")); | ||
/** | ||
* List of default POSIX signals to register handlers for. | ||
*/ | ||
exports.SIGNALS = [ | ||
'SIGINT', | ||
'SIGQUIT', | ||
'SIGTERM', | ||
'SIGUSR2' | ||
]; | ||
/** | ||
* Tracks which signals we have registered process listeners for, and which user | ||
* callbacks should be invoked for each signal. | ||
*/ | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = adeiu; | ||
exports.SIGNALS = void 0; | ||
var _chalk = _interopRequireDefault(require("chalk")); | ||
var _ow = _interopRequireDefault(require("ow")); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
const SIGNALS = ['SIGINT', 'SIGQUIT', 'SIGTERM', 'SIGUSR2']; | ||
exports.SIGNALS = SIGNALS; | ||
const signalCallbacks = new Map(); | ||
/** | ||
* Provided an `adeiu` callback and an error it threw, logs the error to | ||
* stderr. | ||
* | ||
* @example | ||
* | ||
* const myCallback = () => { | ||
* throw new TypeError('Oh noes!'); | ||
* }; | ||
* | ||
* ``` | ||
* Error: [adeiu] SIGINT handler `myCallback` threw: TypeError: Oh noes! | ||
* at myCallback (foo.js:42:3) | ||
* ``` | ||
*/ | ||
function writeErrorToStderr(cb, signal, err) { | ||
if (err && err.stack) { | ||
const errType = err.constructor ? err.constructor.name : 'Error'; | ||
const cbName = cb.name ? `${signal} handler \`${cb.name}\`` : 'Anonymous callback'; | ||
const stackLines = err.stack.split('\n'); | ||
stackLines[0] = `${chalk_1.default.red(`Error: [adeiu] ${cbName} threw:`)} ${errType}: ${err.message}`; | ||
process.stderr.write(`${stackLines.join('\n')}\n`); | ||
if (err && err.stack) { | ||
const errType = err.constructor ? err.constructor.name : 'Error'; | ||
const cbName = cb.name ? `${signal} handler \`${cb.name}\`` : 'Anonymous callback'; | ||
const stackLines = err.stack.split('\n'); | ||
stackLines[0] = `${_chalk.default.red(`Error: [adeiu] ${cbName} threw:`)} ${errType}: ${err.message}`; | ||
process.stderr.write(`${stackLines.join('\n')}\n`); | ||
} | ||
} | ||
async function handler(signal) { | ||
const callbacksForSignal = signalCallbacks.get(signal); | ||
if (!callbacksForSignal || callbacksForSignal.length === 0) { | ||
throw new Error(`Unexpected error: Expected at least 1 callback for signal ${signal}, but found none.`); | ||
} | ||
const results = await Promise.all(callbacksForSignal.map(async cb => { | ||
try { | ||
await cb(signal); | ||
return true; | ||
} catch (err) { | ||
writeErrorToStderr(cb, signal, err); | ||
return false; | ||
} | ||
})); | ||
if (results.includes(false)) { | ||
process.exit(1); | ||
} else { | ||
process.kill(process.pid, signal); | ||
} | ||
} | ||
/** | ||
* Common signal handler; concurrently calls each callback registered for the | ||
* provided signal. If any callbacks throw or reject, the process will exit with | ||
* code 1. | ||
*/ | ||
async function handler(signal) { | ||
// Get an array of user callbacks we need to invoke for the provided signal. | ||
function adeiu(cb, { | ||
signals = [] | ||
} = {}) { | ||
(0, _ow.default)(signals, 'signals', _ow.default.array.ofType(_ow.default.string)); | ||
const finalSignals = signals.length > 0 ? signals : SIGNALS; | ||
finalSignals.forEach(signal => { | ||
const callbacksForSignal = signalCallbacks.get(signal); | ||
// If this occurs, it means there is an error in our handler (un)installation | ||
// logic. | ||
if (!callbacksForSignal || callbacksForSignal.length === 0) { | ||
throw new Error(`Unexpected error: Expected at least 1 callback for signal ${signal}, but found none.`); | ||
signalCallbacks.set(signal, [cb]); | ||
process.prependOnceListener(signal, handler); | ||
} else { | ||
signalCallbacks.set(signal, [...callbacksForSignal, cb]); | ||
} | ||
// Map our array of functions into an array of promises that will resolve with | ||
// `true` if the function returns/resolves and `false` if the function throws | ||
// or rejects. | ||
const results = await Promise.all(callbacksForSignal.map(async (cb) => { | ||
try { | ||
await cb(signal); | ||
return true; | ||
} | ||
catch (err) { | ||
writeErrorToStderr(cb, signal, err); | ||
return false; | ||
} | ||
})); | ||
if (results.includes(false)) { | ||
// If any functions threw/rejected, exit with code 1. | ||
process.exit(1); | ||
} | ||
else { | ||
// N.B. We use process.kill() here rather than process.exit() because it | ||
// causes any potential Node debuggers that are attached to detach from the | ||
// process so that it can cleanly exit. | ||
process.kill(process.pid, signal); | ||
} | ||
} | ||
/** | ||
* Provided a function, registers a callback with several common POSIX signals | ||
* that will invoke the function upon receipt of any of the signals. | ||
* | ||
* Returns a function that, when invoked, will unregister the callback. | ||
*/ | ||
function adeiu(cb, { signals = [] } = {}) { | ||
// Validate options. | ||
ow_1.default(signals, 'signals', ow_1.default.array); | ||
// If the user provided a custom list of signals, use it. Otherwise, use the | ||
// default list. | ||
const finalSignals = signals.length > 0 ? signals : exports.SIGNALS; | ||
}); | ||
return () => { | ||
finalSignals.forEach(signal => { | ||
const callbacksForSignal = signalCallbacks.get(signal); | ||
if (!callbacksForSignal || callbacksForSignal.length === 0) { | ||
signalCallbacks.set(signal, [cb]); | ||
// Since this is the first callback being registered for this signal, | ||
// install our handler for it. | ||
process.prependOnceListener(signal, handler); | ||
} | ||
else { | ||
signalCallbacks.set(signal, [...callbacksForSignal, cb]); | ||
} | ||
const callbacksForSignal = signalCallbacks.get(signal); | ||
if (!callbacksForSignal || callbacksForSignal.length === 0) { | ||
return; | ||
} | ||
if (callbacksForSignal.length === 1 && callbacksForSignal[0] === cb) { | ||
signalCallbacks.set(signal, []); | ||
process.off(signal, handler); | ||
} else { | ||
signalCallbacks.set(signal, callbacksForSignal.filter(curCallback => curCallback !== cb)); | ||
} | ||
}); | ||
return () => { | ||
finalSignals.forEach(signal => { | ||
const callbacksForSignal = signalCallbacks.get(signal); | ||
if (!callbacksForSignal || callbacksForSignal.length === 0) { | ||
// User may have alreay called this function previously. | ||
return; | ||
} | ||
if (callbacksForSignal.length === 1 && callbacksForSignal[0] === cb) { | ||
signalCallbacks.set(signal, []); | ||
// This means we are un-registering the last remaining callback for this | ||
// signal, so uninstall our handler for it. | ||
process.off(signal, handler); | ||
} | ||
else { | ||
signalCallbacks.set(signal, callbacksForSignal.filter(curCallback => curCallback !== cb)); | ||
} | ||
}); | ||
}; | ||
}; | ||
} | ||
exports.default = adeiu; | ||
//# sourceMappingURL=adeiu.js.map |
{ | ||
"name": "@darkobits/adeiu", | ||
"version": "0.2.3", | ||
"version": "0.2.4", | ||
"description": "Yet another POSIX signal handler.", | ||
@@ -53,3 +53,3 @@ "license": "WTFPL", | ||
"devDependencies": { | ||
"@darkobits/ts-unified": "^2.1.4", | ||
"@darkobits/ts-unified": "^3.0.2", | ||
"@types/jest": "^24.0.15", | ||
@@ -56,0 +56,0 @@ "@types/node": "^11.13.15", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
17040
7
97