Created
September 18, 2025 12:37
-
-
Save AceCodePt/0e6beea05f1e1b5b62c1d9a835a3bb19 to your computer and use it in GitHub Desktop.
This is a prexit typescript implementation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| declare global { | |
| namespace NodeJS { | |
| interface Process { | |
| emit(event: "prexit", error?: Error): boolean; | |
| } | |
| } | |
| } | |
| export type PrexitSignal = | |
| | "exit" | |
| | "beforeExit" | |
| | "uncaughtException" | |
| | "unhandledRejection" | |
| | "SIGTSTP" | |
| | "SIGQUIT" | |
| | "SIGHUP" | |
| | "SIGTERM" | |
| | "SIGINT"; | |
| export type PrexitCallback = ( | |
| signal: string, | |
| error?: Error, | |
| ) => unknown | Promise<unknown>; | |
| export type LastCallback = (signal: string) => void; | |
| export interface Prexit { | |
| (callback: PrexitCallback): void; | |
| (signals: PrexitSignal | PrexitSignal[], callback: PrexitCallback): void; | |
| signals: PrexitSignal[]; | |
| logExceptions: boolean; | |
| exiting: boolean; | |
| last(fn: LastCallback): void; | |
| exit(code: number, error?: Error): void; | |
| exit(signal?: string, code?: number, error?: Error): void; | |
| ondone(signal: string, error?: Error): void; | |
| } | |
| const handlers: Record<string, PrexitCallback[]> = {}; | |
| const last: LastCallback[] = []; | |
| let finished = false; | |
| const prexit: Prexit = ( | |
| signalsOrFn: PrexitSignal | PrexitSignal[] | PrexitCallback, | |
| fn?: PrexitCallback, | |
| ) => { | |
| let signals: string | string[] = prexit.signals; | |
| let callback: PrexitCallback; | |
| if (typeof signalsOrFn === "function") { | |
| callback = signalsOrFn; | |
| } else { | |
| if (!fn) { | |
| throw new Error( | |
| "A callback function must be provided when specifying signals.", | |
| ); | |
| } | |
| callback = fn; | |
| signals = signalsOrFn; | |
| } | |
| let called = false; | |
| const signalList: string[] = ["prexit"].concat(signals); | |
| signalList.forEach((signal) => | |
| handle(signal, (signal, error) => { | |
| if (called) return; | |
| called = true; | |
| return callback(signal, error); | |
| }), | |
| ); | |
| }; | |
| prexit.signals = [ | |
| "exit", | |
| "beforeExit", | |
| "uncaughtException", | |
| "unhandledRejection", | |
| "SIGTSTP", | |
| "SIGQUIT", | |
| "SIGHUP", | |
| "SIGTERM", | |
| "SIGINT", | |
| ]; | |
| prexit.logExceptions = true; | |
| prexit.exiting = false; | |
| prexit.last = function addLast(fn: LastCallback): void { | |
| if (last.length === 0) { | |
| prexit(() => {}); | |
| } | |
| last.push(fn); | |
| }; | |
| function exit(signal?: PrexitSignal, code?: number): void; | |
| function exit(code: number): void; | |
| function exit( | |
| signalOrCode?: PrexitSignal | number, | |
| codeOrError?: number | Error, | |
| error?: Error, | |
| ): void { | |
| let code: number | undefined; | |
| let finalError: Error | undefined; | |
| if (typeof signalOrCode === "number") { | |
| finalError = codeOrError as Error | undefined; | |
| code = signalOrCode; | |
| } else { | |
| code = codeOrError as number | undefined; | |
| finalError = error; | |
| } | |
| if (code !== undefined) { | |
| process.exitCode = code; | |
| } | |
| if (Object.keys(handlers).length > 0) { | |
| process.emit("prexit", finalError); | |
| } else { | |
| process.exit(); | |
| } | |
| } | |
| prexit.exit = exit; | |
| prexit.ondone = function ondone(_signal: string, _error?: Error): void { | |
| process.exit(); | |
| }; | |
| function handle(signal: string, fn: PrexitCallback): void { | |
| const existingFns = handlers[signal]; | |
| if (existingFns) { | |
| existingFns.push(fn); | |
| return; | |
| } | |
| const fns = (handlers[signal] = [fn]); | |
| process.on(signal, async (errorOrSignalValue: Error | any) => { | |
| prexit.exiting = true; | |
| if (errorOrSignalValue === signal) { | |
| errorOrSignalValue = null; | |
| } | |
| const error = | |
| errorOrSignalValue instanceof Error ? errorOrSignalValue : undefined; | |
| if ( | |
| (signal === "uncaughtException" || signal === "unhandledRejection") && | |
| prexit.logExceptions | |
| ) { | |
| console.error(errorOrSignalValue); | |
| } | |
| try { | |
| const promises = fns | |
| .map((fn) => fn(signal, error)) | |
| .filter((p): p is Promise<unknown> => p instanceof Promise); | |
| if (promises.length > 0) { | |
| await Promise.all(promises); | |
| } | |
| } catch (err: any) { | |
| if (process.exitCode === undefined) { | |
| process.exitCode = 1; | |
| } | |
| if (prexit.logExceptions) { | |
| console.error(err); | |
| } | |
| } | |
| done(signal, error); | |
| }); | |
| } | |
| function done(signal: string, error?: Error): void { | |
| if (finished) return; | |
| finished = true; | |
| let finalError = error; | |
| try { | |
| last.forEach((fn) => fn(signal)); | |
| } catch (err: any) { | |
| if (finalError) { | |
| console.error(err); | |
| } else { | |
| finalError = err; | |
| } | |
| } | |
| prexit.ondone(signal, finalError); | |
| } | |
| export default prexit; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment