61 lines
1.8 KiB
TypeScript
61 lines
1.8 KiB
TypeScript
import { errorMessage, isTransientStreamError } from "@/shared/errors/index.js";
|
|||
|
|
import type { Logger } from "@/shared/logger/index.js";
|
||
|
|
import type { GracefulShutdown } from "./shutdown.js";
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Process-level signal + error guards.
|
||
|
|
*
|
||
|
|
* Extracted from bootstrap so the "what keeps the gateway alive vs what
|
||
|
|
* shuts it down" policy lives in exactly one place.
|
||
|
|
*
|
||
|
|
* Policy: transient stream-teardown failures (EPIPE / ERR_STREAM_DESTROYED /
|
||
|
|
* ERR_STREAM_WRITE_AFTER_END / ECONNRESET) are logged and IGNORED — crashing
|
||
|
|
* the gateway on them (voice stop/disconnect races, a child process stdin
|
||
|
|
* closed while we still write) takes the whole bot offline mid-operation.
|
||
|
|
* Anything else is a real bug: log with stack and shut down cleanly.
|
||
|
|
*/
|
||
|
|
export function registerProcessGuards(
|
||
|
|
logger: Logger,
|
||
|
|
gracefulShutdown: GracefulShutdown,
|
||
|
|
): void {
|
||
|
|
process.on("SIGINT", () => {
|
||
|
|
gracefulShutdown("SIGINT");
|
||
|
|
});
|
||
|
|
|
||
|
|
process.on("SIGTERM", () => {
|
||
|
|
gracefulShutdown("SIGTERM");
|
||
|
|
});
|
||
|
|
|
||
|
|
process.on("uncaughtException", (err) => {
|
||
|
|
if (isTransientStreamError(err)) {
|
||
|
|
logger.warn(
|
||
|
|
{ error: err },
|
||
|
|
"Uncaught transient stream error — continuing",
|
||
|
|
);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
logger.error(
|
||
|
|
{
|
||
|
|
err,
|
||
|
|
errorMsg: errorMessage(err),
|
||
|
|
stack: err?.stack,
|
||
|
|
},
|
||
|
|
"Uncaught exception",
|
||
|
|
);
|
||
|
|
gracefulShutdown("uncaughtException");
|
||
|
|
});
|
||
|
|
|
||
|
|
process.on("unhandledRejection", (reason) => {
|
||
|
|
const err =
|
||
|
|
reason instanceof Error ? reason : new Error(String(reason ?? "unknown"));
|
||
|
|
if (isTransientStreamError(err)) {
|
||
|
|
logger.warn(
|
||
|
|
{ error: err },
|
||
|
|
"Unhandled rejection transient stream error — continuing",
|
||
|
|
);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
logger.error({ error: err, reason: String(reason) }, "Unhandled rejection");
|
||
|
|
gracefulShutdown("unhandledRejection");
|
||
|
|
});
|
||
|
|
}
|