diff --git a/packages/appkit/src/plugins/server/index.ts b/packages/appkit/src/plugins/server/index.ts index e66abf5a..2ac43de8 100644 --- a/packages/appkit/src/plugins/server/index.ts +++ b/packages/appkit/src/plugins/server/index.ts @@ -23,6 +23,10 @@ dotenv.config({ path: path.resolve(process.cwd(), "./.env") }); const logger = createLogger("server"); +// Registered once per process; repeated start() calls (e.g. in tests) must not +// stack handlers on the shared `process`. +let processHandlersRegistered = false; + /** Dev-only: try `requested` then consecutive ports (see `get-port` `portNumbers`). */ const devListenPortSpan = 100; @@ -160,8 +164,7 @@ export class ServerPlugin extends Plugin { // attach server to remote tunnel controller this.remoteTunnelController.setServer(server); - process.on("SIGTERM", () => this._gracefulShutdown()); - process.on("SIGINT", () => this._gracefulShutdown()); + this._registerProcessHandlers(); if (process.env.NODE_ENV === "development") { const allRoutes = getRoutes(this.serverApplication._router.stack); @@ -398,7 +401,22 @@ export class ServerPlugin extends Plugin { } } - private async _gracefulShutdown() { + private _registerProcessHandlers() { + if (processHandlersRegistered) return; + processHandlersRegistered = true; + + process.on("SIGTERM", () => this._gracefulShutdown()); + process.on("SIGINT", () => this._gracefulShutdown()); + process.on("uncaughtException", (error: Error, origin) => { + logger.error("Uncaught exception (%s), shutting down: %O", origin, error); + this._gracefulShutdown(1); + }); + process.on("unhandledRejection", (reason) => { + logger.error("Unhandled promise rejection: %O", reason); + }); + } + + private async _gracefulShutdown(exitCode = 0) { logger.info("Starting graceful shutdown..."); if (this.viteDevServer) { @@ -433,7 +451,7 @@ export class ServerPlugin extends Plugin { if (this.server) { this.server.close(() => { logger.debug("Server closed gracefully"); - process.exit(0); + process.exit(exitCode); }); // 3. timeout to force shutdown after 15 seconds @@ -442,7 +460,7 @@ export class ServerPlugin extends Plugin { process.exit(1); }, 15000); } else { - process.exit(0); + process.exit(exitCode); } }