UNPKG

@darkobits/adeiu

Version:

Yet another POSIX signal handler.

103 lines (100 loc) 3.46 kB
import { constants } from "node:os"; const DEFAULT_SIGNALS = [ "SIGINT", "SIGQUIT", "SIGTERM", // Signal sent by nodemon to child processes when it needs to restart them. "SIGUSR2" ]; const signalHandlers = /* @__PURE__ */ new Map(); const signalsHandled = {}; async function rejectAfter(timeout, reason) { return new Promise((resolve, reject) => { setTimeout(() => reject(reason), timeout); }); } function signalToExitCode(signal) { const signalNum = constants.signals[signal]; return signalNum ? 128 + signalNum : void 0; } function getHandlersForSignal(signal) { if (!signalHandlers.has(signal)) signalHandlers.set(signal, /* @__PURE__ */ new Map()); return signalHandlers.get(signal); } function logErrors(signal, errors) { const supportsColor = Boolean(process.stdout.isTTY && !process.env.NO_COLOR || process.env.FORCE_COLOR); const red = (text) => supportsColor ? `\x1B[31m${text}\x1B[0m` : text; process.stderr.write(red(`Encountered the following errors while responding to ${signal}: `)); errors.forEach(([error, handler]) => { var _a; const handlerName = handler.name || "anonymous"; const stackLines = ((_a = error.stack) == null ? void 0 : _a.split("\n")) ?? [error.message]; process.stderr.write(`${stackLines.map((line, lineNumber) => { return lineNumber === 0 ? red(`• ${line} (via handler: ${handlerName})`) : line; }).join("\n")} `); }); } async function handleSignal(signal) { if (signalsHandled[signal]) return; signalsHandled[signal] = true; const handlersForSignal = getHandlersForSignal(signal); if (handlersForSignal.size === 0) throw new Error(`Unexpected error: Expected at least 1 handler for signal ${signal}, got 0.`); const errors = []; await Promise.allSettled([...handlersForSignal.entries()].map(async ([handler, options]) => { try { if (options.timeout) { await Promise.race([ handler(signal), rejectAfter(options.timeout, new Error(`Operation timed-out after ${options.timeout}ms.`)) ]); } else { await handler(signal); } } catch (err) { errors.push([err, handler]); } })); if (errors.length > 0) { logErrors(signal, errors); process.exit(signalToExitCode(signal) ?? 1); } else { process.kill(process.pid, signal); } } function adeiu(handler, options = {}) { const { signals = DEFAULT_SIGNALS, timeout } = options; signals.forEach((signal) => { if (typeof signal !== "string") throw new TypeError(`Expected signal to be of type "string", got "${typeof signal}".`); if (!signal.startsWith("SIG")) throw new Error(`Invalid signal: ${signal}`); }); if (typeof timeout !== "number" && timeout !== void 0) throw new TypeError(`Expected type of "timeout" to be "number" or "undefined", got "${typeof timeout}".`); signals.forEach((signal) => { const handlersForSignal = getHandlersForSignal(signal); if (handlersForSignal.size === 0) { process.prependListener(signal, handleSignal); } handlersForSignal.set(handler, options); }); return () => { for (const signal of signals) { const handlersForSignal = getHandlersForSignal(signal); handlersForSignal.delete(handler); if (handlersForSignal.size === 0) { process.removeListener(signal, handleSignal); } } }; } export { DEFAULT_SIGNALS, adeiu as default }; //# sourceMappingURL=adeiu.js.map