mirror of
https://github.com/sern-handler/handler
synced 2026-06-06 01:16:55 +00:00
Compare commits
5 Commits
v4.2.6
...
fix/dispos
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2caa8b0f21 | ||
|
|
fb41e33aa6 | ||
|
|
b3c5a9e050 | ||
|
|
d566178315 | ||
|
|
64c6c08a01 |
3014
package-lock.json
generated
3014
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -48,7 +48,7 @@
|
|||||||
"@typescript-eslint/eslint-plugin": "5.58.0",
|
"@typescript-eslint/eslint-plugin": "5.58.0",
|
||||||
"@typescript-eslint/parser": "5.59.1",
|
"@typescript-eslint/parser": "5.59.1",
|
||||||
"discord.js": "^14.14.1",
|
"discord.js": "^14.14.1",
|
||||||
"eslint": "8.39.0",
|
"eslint": "9.0.0",
|
||||||
"typescript": "5.0.2",
|
"typescript": "5.0.2",
|
||||||
"vitest": "^1.6.0"
|
"vitest": "^1.6.0"
|
||||||
},
|
},
|
||||||
|
|||||||
111
src/cleanup.ts
Normal file
111
src/cleanup.ts
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
// It's this package but without default console log / error https://github.com/trevorr/async-cleanup
|
||||||
|
|
||||||
|
/** A possibly asynchronous function invoked with the process is about to exit. */
|
||||||
|
export type CleanupListener = () => void | Promise<void>;
|
||||||
|
|
||||||
|
let cleanupListeners: Set<CleanupListener> | undefined;
|
||||||
|
|
||||||
|
/** Registers a new cleanup listener. Adding the same listener more than once has no effect. */
|
||||||
|
export function addCleanupListener(listener: CleanupListener): void {
|
||||||
|
// Install exit listeners on initial cleanup listener
|
||||||
|
if (!cleanupListeners) {
|
||||||
|
installExitListeners();
|
||||||
|
cleanupListeners = new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanupListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Removes an existing cleanup listener, and returns whether the listener was registered. */
|
||||||
|
export function removeCleanupListener(listener: CleanupListener): boolean {
|
||||||
|
return cleanupListeners != null && cleanupListeners.delete(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Executes all cleanup listeners and then exits the process. Call this instead of `process.exit` to ensure all listeners are fully executed. */
|
||||||
|
export async function exitAfterCleanup(code = 0): Promise<never> {
|
||||||
|
await executeCleanupListeners();
|
||||||
|
process.exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Executes all cleanup listeners and then kills the process with the given signal. */
|
||||||
|
export async function killAfterCleanup(signal: ExitSignal): Promise<void> {
|
||||||
|
await executeCleanupListeners();
|
||||||
|
process.kill(process.pid, signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function executeCleanupListeners(): Promise<void> {
|
||||||
|
if (cleanupListeners) {
|
||||||
|
// Remove exit listeners to restore normal event handling
|
||||||
|
uninstallExitListeners();
|
||||||
|
|
||||||
|
// Clear cleanup listeners to reset state for testing
|
||||||
|
const listeners = cleanupListeners;
|
||||||
|
cleanupListeners = undefined;
|
||||||
|
|
||||||
|
// Call listeners in order added with async listeners running concurrently
|
||||||
|
const promises: Promise<void>[] = [];
|
||||||
|
for (const listener of listeners) {
|
||||||
|
try {
|
||||||
|
const promise = listener();
|
||||||
|
if (promise) promises.push(promise);
|
||||||
|
} catch (err) {
|
||||||
|
// console.error("Uncaught exception during cleanup", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all listeners to complete and log any rejections
|
||||||
|
const results = await Promise.allSettled(promises);
|
||||||
|
for (const result of results) {
|
||||||
|
if (result.status === "rejected") {
|
||||||
|
console.error("Unhandled rejection during cleanup", result.reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function beforeExitListener(code: number): void {
|
||||||
|
// console.log(`Exiting with code ${code} due to empty event loop`);
|
||||||
|
void exitAfterCleanup(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
function uncaughtExceptionListener(error: Error): void {
|
||||||
|
// console.error("Exiting with code 1 due to uncaught exception", error);
|
||||||
|
void exitAfterCleanup(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function signalListener(signal: ExitSignal): void {
|
||||||
|
// console.log(`Exiting due to signal ${signal}`);
|
||||||
|
void killAfterCleanup(signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Listenable signals that terminate the process by default
|
||||||
|
// (except SIGQUIT, which generates a core dump and should not trigger cleanup)
|
||||||
|
// See https://nodejs.org/api/process.html#signal-events
|
||||||
|
const listenedSignals = [
|
||||||
|
"SIGBREAK", // Ctrl-Break on Windows
|
||||||
|
"SIGHUP", // Parent terminal closed
|
||||||
|
"SIGINT", // Terminal interrupt, usually by Ctrl-C
|
||||||
|
"SIGTERM", // Graceful termination
|
||||||
|
"SIGUSR2", // Used by Nodemon
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
/** Signals that can terminate the process. */
|
||||||
|
export type ExitSignal =
|
||||||
|
| typeof listenedSignals[number]
|
||||||
|
| "SIGKILL"
|
||||||
|
| "SIGQUIT"
|
||||||
|
| "SIGSTOP";
|
||||||
|
|
||||||
|
function installExitListeners(): void {
|
||||||
|
process.on("beforeExit", beforeExitListener);
|
||||||
|
process.on("uncaughtException", uncaughtExceptionListener);
|
||||||
|
listenedSignals.forEach((signal) => process.on(signal, signalListener));
|
||||||
|
}
|
||||||
|
|
||||||
|
function uninstallExitListeners(): void {
|
||||||
|
process.removeListener("beforeExit", beforeExitListener);
|
||||||
|
process.removeListener("uncaughtException", uncaughtExceptionListener);
|
||||||
|
listenedSignals.forEach((signal) =>
|
||||||
|
process.removeListener(signal, signalListener)
|
||||||
|
);
|
||||||
|
}
|
||||||
10
src/sern.ts
10
src/sern.ts
@@ -14,6 +14,7 @@ import { presenceHandler } from './handlers/presence';
|
|||||||
import type { Payload, UnpackedDependencies, Wrapper } from './types/utility';
|
import type { Payload, UnpackedDependencies, Wrapper } from './types/utility';
|
||||||
import type { Presence} from './core/presences';
|
import type { Presence} from './core/presences';
|
||||||
import { registerTasks } from './handlers/tasks';
|
import { registerTasks } from './handlers/tasks';
|
||||||
|
import { addCleanupListener } from './cleanup';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -76,5 +77,12 @@ export function init(maybeWrapper: Wrapper = { commands: "./dist/commands" }) {
|
|||||||
})
|
})
|
||||||
.catch(err => { throw err });
|
.catch(err => { throw err });
|
||||||
interactionHandler(deps, maybeWrapper.defaultPrefix);
|
interactionHandler(deps, maybeWrapper.defaultPrefix);
|
||||||
messageHandler(deps, maybeWrapper.defaultPrefix)
|
messageHandler(deps, maybeWrapper.defaultPrefix);
|
||||||
|
|
||||||
|
addCleanupListener(async () => {
|
||||||
|
const duration = ((performance.now() - startTime) / 1000).toFixed(2)
|
||||||
|
deps['@sern/logger']?.info({ 'message': 'sern is shutting down after '+duration +" seconds" })
|
||||||
|
await useContainerRaw().disposeAll();
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user