(signal: string)
| 766 | }); |
| 767 | |
| 768 | async function shutdown(signal: string) { |
| 769 | log.info(`Received ${signal}, shutting down`); |
| 770 | const { drainBrowserPool } = await import("@hyperframes/engine"); |
| 771 | await drainBrowserPool().catch(() => {}); |
| 772 | // Bounded await: if the worker hasn't come online within 1.5s of |
| 773 | // shutdown there's no useful cleanup left to do — `worker.terminate()` |
| 774 | // from process exit will kill the thread regardless, and we'd rather |
| 775 | // not let a hung-startup worker keep the SIGTERM path waiting. |
| 776 | const handle = await Promise.race<HealthWorkerHandle | null>([ |
| 777 | healthWorkerPromise, |
| 778 | new Promise<null>((res) => setTimeout(() => res(null), 1_500).unref()), |
| 779 | ]); |
| 780 | if (handle) { |
| 781 | await handle.shutdown().catch(() => {}); |
| 782 | } |
| 783 | server.close(() => { |
| 784 | log.info("Server closed"); |
| 785 | process.exit(0); |
| 786 | }); |
| 787 | setTimeout(() => { |
| 788 | log.warn("Forced exit after 30s timeout"); |
| 789 | process.exit(1); |
| 790 | }, 30_000).unref(); |
| 791 | } |
| 792 | |
| 793 | process.on("SIGTERM", () => shutdown("SIGTERM")); |
| 794 | process.on("SIGINT", () => shutdown("SIGINT")); |
no test coverage detected