@nodecg/types
Version:
Dynamic broadcast graphics rendered in a browser
123 lines • 4.48 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.exitHook = exitHook;
exports.asyncExitHook = asyncExitHook;
exports.gracefulExit = gracefulExit;
// Adapted from https://github.com/sindresorhus/exit-hook/blob/9fdc03b855f12f79a0d13246c38ca9b2114c8d2e/index.js
const node_process_1 = __importDefault(require("node:process"));
const asyncCallbacks = new Set();
const callbacks = new Set();
let isCalled = false;
let isRegistered = false;
async function exit(shouldManuallyExit, isSynchronous, signal) {
if (isCalled) {
return;
}
isCalled = true;
if (asyncCallbacks.size > 0 && isSynchronous) {
console.error([
"SYNCHRONOUS TERMINATION NOTICE:",
"When explicitly exiting the process via process.exit or via a parent process,",
"asynchronous tasks in your exitHooks will not run. Either remove these tasks,",
"use gracefulExit() instead of process.exit(), or ensure your parent process",
"sends a SIGINT to the process running this code.",
].join(" "));
}
const exitCode = 128 + signal;
const done = (force = false) => {
if (force || shouldManuallyExit) {
node_process_1.default.exit(exitCode);
}
};
for (const callback of callbacks) {
callback(exitCode);
}
if (isSynchronous) {
done();
return;
}
const promises = [];
let forceAfter = 0;
for (const [callback, wait] of asyncCallbacks) {
forceAfter = Math.max(forceAfter, wait ?? 0);
promises.push(Promise.resolve(callback(exitCode)));
}
// Force exit if we exceeded our wait value
const asyncTimer = setTimeout(() => {
done(true);
}, forceAfter);
await Promise.all(promises);
clearTimeout(asyncTimer);
done();
}
function addHook(options) {
const { onExit, minimumWait, isSynchronous } = options;
const asyncCallbackConfig = [
onExit,
minimumWait,
];
if (isSynchronous) {
callbacks.add(onExit);
}
else {
asyncCallbacks.add(asyncCallbackConfig);
}
if (!isRegistered) {
isRegistered = true;
// Exit cases that support asynchronous handling
node_process_1.default.once("beforeExit", exit.bind(undefined, true, false, -128));
node_process_1.default.once("SIGHUP", exit.bind(undefined, true, false, 1));
node_process_1.default.once("SIGINT", exit.bind(undefined, true, false, 2));
node_process_1.default.once("SIGTERM", exit.bind(undefined, true, false, 15));
// Explicit exit events. Calling will force an immediate exit and run all
// synchronous hooks. Explicit exits must not extend the node process
// artificially. Will log errors if asynchronous calls exist.
node_process_1.default.once("exit", exit.bind(undefined, false, true, 0));
// PM2 Cluster shutdown message. Caught to support async handlers with pm2,
// needed because explicitly calling process.exit() doesn't trigger the
// beforeExit event, and the exit event cannot support async handlers,
// since the event loop is never called after it.
node_process_1.default.on("message", (message) => {
if (message === "shutdown") {
void exit(true, true, -128);
}
});
}
return () => {
if (isSynchronous) {
callbacks.delete(onExit);
}
else {
asyncCallbacks.delete(asyncCallbackConfig);
}
};
}
function exitHook(onExit) {
if (typeof onExit !== "function") {
throw new TypeError("onExit must be a function");
}
return addHook({
onExit,
isSynchronous: true,
});
}
function asyncExitHook(onExit, options = {}) {
if (typeof onExit !== "function") {
throw new TypeError("onExit must be a function");
}
if (!(typeof options.minimumWait === "number" && options.minimumWait > 0)) {
throw new TypeError("minimumWait must be set to a positive numeric value");
}
return addHook({
onExit,
minimumWait: options.minimumWait,
isSynchronous: false,
});
}
function gracefulExit(signal = 0) {
void exit(true, false, -128 + signal);
}
//# sourceMappingURL=exit-hook.js.map