"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.processExit = exports.offBeforeExit = exports.onBeforeExit = exports.onExit = exports.sleepForever = exports.sleepUntil = exports.sleep = exports.getPathParts = exports.createProcessEnv = exports.killProcessTree = exports.ERROR_TIMEOUT_REACHED = void 0; const tslib_1 = require("tslib"); const utils_object_1 = require("@ionic/utils-object"); const utils_terminal_1 = require("@ionic/utils-terminal"); const debug_1 = require("debug"); const pathlib = tslib_1.__importStar(require("path")); const signal_exit_1 = tslib_1.__importDefault(require("signal-exit")); const tree_kill_1 = tslib_1.__importDefault(require("tree-kill")); const debug = (0, debug_1.debug)('ionic:utils-process'); exports.ERROR_TIMEOUT_REACHED = new Error('TIMEOUT_REACHED'); function killProcessTree(pid, signal = 'SIGTERM') { return new Promise((resolve, reject) => { (0, tree_kill_1.default)(pid, signal, err => { if (err) { debug('error while killing process tree for %d: %O', pid, err); return reject(err); } resolve(); }); }); } exports.killProcessTree = killProcessTree; /** * Creates an alternative implementation of `process.env` object. * * On a Windows shell, `process.env` is a magic object that offers * case-insensitive environment variable access. On other platforms, case * sensitivity matters. This method creates an empty "`process.env`" object * type that works for all platforms. */ function createProcessEnv(...sources) { return Object.assign(utils_terminal_1.TERMINAL_INFO.windows ? (0, utils_object_1.createCaseInsensitiveObject)() : {}, ...sources); } exports.createProcessEnv = createProcessEnv; /** * Split a PATH string into path parts. */ function getPathParts(envpath = process.env.PATH || '') { return envpath.split(pathlib.delimiter); } exports.getPathParts = getPathParts; /** * Resolves when the given amount of milliseconds has passed. */ async function sleep(ms) { return new Promise(resolve => { setTimeout(resolve, ms); }); } exports.sleep = sleep; /** * Resolves when a given predicate is true or a timeout is reached. * * Configure `interval` to set how often the `predicate` is called. * * By default, `timeout` is Infinity. If given a value (in ms), and that * timeout value is reached, this function will reject with * the `ERROR_TIMEOUT_REACHED` error. */ async function sleepUntil(predicate, { interval = 30, timeout = Infinity }) { let ms = 0; while (!predicate()) { await sleep(interval); ms += interval; if (ms > timeout) { throw exports.ERROR_TIMEOUT_REACHED; } } } exports.sleepUntil = sleepUntil; /** * Never resolves and keeps Node running. */ async function sleepForever() { return new Promise(() => { setInterval(() => { }, 1000); }); } exports.sleepForever = sleepForever; /** * Register a synchronous function to be called once the process exits. */ function onExit(fn) { (0, signal_exit_1.default)(() => { debug('onExit: process.exit/normal shutdown'); fn(); }); } exports.onExit = onExit; const exitFns = new Set(); /** * Register an asynchronous function to be called when the process wants to * exit. * * A handler will be registered for the 'SIGINT', 'SIGTERM', 'SIGHUP', * 'SIGBREAK' signals. If any of the signal events is emitted, `fn` will be * called exactly once, awaited upon, and then the process will exit once all * registered functions are resolved. */ function onBeforeExit(fn) { exitFns.add(fn); } exports.onBeforeExit = onBeforeExit; /** * Remove a function that was registered with `onBeforeExit`. */ function offBeforeExit(fn) { exitFns.delete(fn); } exports.offBeforeExit = offBeforeExit; const once = (fn) => { let called = false; return async () => { if (!called) { await fn(); called = true; } }; }; const beforeExitHandlerWrapper = (signal) => once(async () => { debug('onBeforeExit handler: %O received', signal); debug('onBeforeExit handler: running %O functions', exitFns.size); await Promise.all([...exitFns.values()].map(async (fn) => { try { await fn(); } catch (e) { debug('onBeforeExit handler: error from function: %O', e); } })); if (signal !== 'process.exit') { debug('onBeforeExit handler: killing self (exit code %O, signal %O)', process.exitCode ? process.exitCode : 0, signal); process.removeListener(signal, BEFORE_EXIT_SIGNAL_LISTENERS[signal]); process.kill(process.pid, signal); } }); const BEFORE_EXIT_SIGNAL_LISTENERS = { SIGINT: beforeExitHandlerWrapper('SIGINT'), SIGTERM: beforeExitHandlerWrapper('SIGTERM'), SIGHUP: beforeExitHandlerWrapper('SIGHUP'), SIGBREAK: beforeExitHandlerWrapper('SIGBREAK'), }; for (const [signal, fn] of Object.entries(BEFORE_EXIT_SIGNAL_LISTENERS)) { process.on(signal, fn); } const processExitHandler = beforeExitHandlerWrapper('process.exit'); /** * Asynchronous `process.exit()`, for running functions registered with * `onBeforeExit`. */ async function processExit(exitCode = 0) { process.exitCode = exitCode; await processExitHandler(); debug('processExit: exiting (exit code: %O)', process.exitCode); process.exit(); } exports.processExit = processExit;