1"use strict";
2var _a;
3Object.defineProperty(exports, "__esModule", { value: true });
4exports.unload = exports.load = exports.onExit = exports.signals = void 0;
5// Note: since nyc uses this module to output coverage, any lines
6// that are in the direct sync flow of nyc's outputCoverage are
7// ignored, since we can never get coverage for them.
8// grab a reference to node's real process object right away
9const signals_js_1 = require("./signals.js");
10Object.defineProperty(exports, "signals", { enumerable: true, get: function () { return signals_js_1.signals; } });
11const processOk = (process) => !!process &&
12    typeof process === 'object' &&
13    typeof process.removeListener === 'function' &&
14    typeof process.emit === 'function' &&
15    typeof process.reallyExit === 'function' &&
16    typeof process.listeners === 'function' &&
17    typeof process.kill === 'function' &&
18    typeof process.pid === 'number' &&
19    typeof process.on === 'function';
20const kExitEmitter = Symbol.for('signal-exit emitter');
21const global = globalThis;
22const ObjectDefineProperty = Object.defineProperty.bind(Object);
23// teeny special purpose ee
24class Emitter {
25    emitted = {
26        afterExit: false,
27        exit: false,
28    };
29    listeners = {
30        afterExit: [],
31        exit: [],
32    };
33    count = 0;
34    id = Math.random();
35    constructor() {
36        if (global[kExitEmitter]) {
37            return global[kExitEmitter];
38        }
39        ObjectDefineProperty(global, kExitEmitter, {
40            value: this,
41            writable: false,
42            enumerable: false,
43            configurable: false,
44        });
45    }
46    on(ev, fn) {
47        this.listeners[ev].push(fn);
48    }
49    removeListener(ev, fn) {
50        const list = this.listeners[ev];
51        const i = list.indexOf(fn);
52        /* c8 ignore start */
53        if (i === -1) {
54            return;
55        }
56        /* c8 ignore stop */
57        if (i === 0 && list.length === 1) {
58            list.length = 0;
59        }
60        else {
61            list.splice(i, 1);
62        }
63    }
64    emit(ev, code, signal) {
65        if (this.emitted[ev]) {
66            return false;
67        }
68        this.emitted[ev] = true;
69        let ret = false;
70        for (const fn of this.listeners[ev]) {
71            ret = fn(code, signal) === true || ret;
72        }
73        if (ev === 'exit') {
74            ret = this.emit('afterExit', code, signal) || ret;
75        }
76        return ret;
77    }
78}
79class SignalExitBase {
80}
81const signalExitWrap = (handler) => {
82    return {
83        onExit(cb, opts) {
84            return handler.onExit(cb, opts);
85        },
86        load() {
87            return handler.load();
88        },
89        unload() {
90            return handler.unload();
91        },
92    };
93};
94class SignalExitFallback extends SignalExitBase {
95    onExit() {
96        return () => { };
97    }
98    load() { }
99    unload() { }
100}
101class SignalExit extends SignalExitBase {
102    // "SIGHUP" throws an `ENOSYS` error on Windows,
103    // so use a supported signal instead
104    /* c8 ignore start */
105    #hupSig = process.platform === 'win32' ? 'SIGINT' : 'SIGHUP';
106    /* c8 ignore stop */
107    #emitter = new Emitter();
108    #process;
109    #originalProcessEmit;
110    #originalProcessReallyExit;
111    #sigListeners = {};
112    #loaded = false;
113    constructor(process) {
114        super();
115        this.#process = process;
116        // { <signal>: <listener fn>, ... }
117        this.#sigListeners = {};
118        for (const sig of signals_js_1.signals) {
119            this.#sigListeners[sig] = () => {
120                // If there are no other listeners, an exit is coming!
121                // Simplest way: remove us and then re-send the signal.
122                // We know that this will kill the process, so we can
123                // safely emit now.
124                const listeners = this.#process.listeners(sig);
125                let { count } = this.#emitter;
126                // This is a workaround for the fact that signal-exit v3 and signal
127                // exit v4 are not aware of each other, and each will attempt to let
128                // the other handle it, so neither of them do. To correct this, we
129                // detect if we're the only handler *except* for previous versions
130                // of signal-exit, and increment by the count of listeners it has
131                // created.
132                /* c8 ignore start */
133                const p = process;
134                if (typeof p.__signal_exit_emitter__ === 'object' &&
135                    typeof p.__signal_exit_emitter__.count === 'number') {
136                    count += p.__signal_exit_emitter__.count;
137                }
138                /* c8 ignore stop */
139                if (listeners.length === count) {
140                    this.unload();
141                    const ret = this.#emitter.emit('exit', null, sig);
142                    /* c8 ignore start */
143                    const s = sig === 'SIGHUP' ? this.#hupSig : sig;
144                    if (!ret)
145                        process.kill(process.pid, s);
146                    /* c8 ignore stop */
147                }
148            };
149        }
150        this.#originalProcessReallyExit = process.reallyExit;
151        this.#originalProcessEmit = process.emit;
152    }
153    onExit(cb, opts) {
154        /* c8 ignore start */
155        if (!processOk(this.#process)) {
156            return () => { };
157        }
158        /* c8 ignore stop */
159        if (this.#loaded === false) {
160            this.load();
161        }
162        const ev = opts?.alwaysLast ? 'afterExit' : 'exit';
163        this.#emitter.on(ev, cb);
164        return () => {
165            this.#emitter.removeListener(ev, cb);
166            if (this.#emitter.listeners['exit'].length === 0 &&
167                this.#emitter.listeners['afterExit'].length === 0) {
168                this.unload();
169            }
170        };
171    }
172    load() {
173        if (this.#loaded) {
174            return;
175        }
176        this.#loaded = true;
177        // This is the number of onSignalExit's that are in play.
178        // It's important so that we can count the correct number of
179        // listeners on signals, and don't wait for the other one to
180        // handle it instead of us.
181        this.#emitter.count += 1;
182        for (const sig of signals_js_1.signals) {
183            try {
184                const fn = this.#sigListeners[sig];
185                if (fn)
186                    this.#process.on(sig, fn);
187            }
188            catch (_) { }
189        }
190        this.#process.emit = (ev, ...a) => {
191            return this.#processEmit(ev, ...a);
192        };
193        this.#process.reallyExit = (code) => {
194            return this.#processReallyExit(code);
195        };
196    }
197    unload() {
198        if (!this.#loaded) {
199            return;
200        }
201        this.#loaded = false;
202        signals_js_1.signals.forEach(sig => {
203            const listener = this.#sigListeners[sig];
204            /* c8 ignore start */
205            if (!listener) {
206                throw new Error('Listener not defined for signal: ' + sig);
207            }
208            /* c8 ignore stop */
209            try {
210                this.#process.removeListener(sig, listener);
211                /* c8 ignore start */
212            }
213            catch (_) { }
214            /* c8 ignore stop */
215        });
216        this.#process.emit = this.#originalProcessEmit;
217        this.#process.reallyExit = this.#originalProcessReallyExit;
218        this.#emitter.count -= 1;
219    }
220    #processReallyExit(code) {
221        /* c8 ignore start */
222        if (!processOk(this.#process)) {
223            return 0;
224        }
225        this.#process.exitCode = code || 0;
226        /* c8 ignore stop */
227        this.#emitter.emit('exit', this.#process.exitCode, null);
228        return this.#originalProcessReallyExit.call(this.#process, this.#process.exitCode);
229    }
230    #processEmit(ev, ...args) {
231        const og = this.#originalProcessEmit;
232        if (ev === 'exit' && processOk(this.#process)) {
233            if (typeof args[0] === 'number') {
234                this.#process.exitCode = args[0];
235                /* c8 ignore start */
236            }
237            /* c8 ignore start */
238            const ret = og.call(this.#process, ev, ...args);
239            /* c8 ignore start */
240            this.#emitter.emit('exit', this.#process.exitCode, null);
241            /* c8 ignore stop */
242            return ret;
243        }
244        else {
245            return og.call(this.#process, ev, ...args);
246        }
247    }
248}
249const process = globalThis.process;
250// wrap so that we call the method on the actual handler, without
251// exporting it directly.
252_a = signalExitWrap(processOk(process) ? new SignalExit(process) : new SignalExitFallback()),
253/**
254 * Called when the process is exiting, whether via signal, explicit
255 * exit, or running out of stuff to do.
256 *
257 * If the global process object is not suitable for instrumentation,
258 * then this will be a no-op.
259 *
260 * Returns a function that may be used to unload signal-exit.
261 */
262exports.onExit = _a.onExit,
263/**
264 * Load the listeners.  Likely you never need to call this, unless
265 * doing a rather deep integration with signal-exit functionality.
266 * Mostly exposed for the benefit of testing.
267 *
268 * @internal
269 */
270exports.load = _a.load,
271/**
272 * Unload the listeners.  Likely you never need to call this, unless
273 * doing a rather deep integration with signal-exit functionality.
274 * Mostly exposed for the benefit of testing.
275 *
276 * @internal
277 */
278exports.unload = _a.unload;
279//# sourceMappingURL=index.js.map