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