@@ -23,56 +23,53 @@ export const gracefulShutdownPlugin: ServerPlugin = (server) => {
2323 ) {
2424 return ;
2525 }
26- const gracefulShutdown =
26+ const gracefulTimeout =
2727 config === true || ! config ?. gracefulTimeout
28- ? Number . parseInt ( process . env . SERVER_SHUTDOWN_TIMEOUT || "" ) || 3
28+ ? Number . parseInt ( process . env . SERVER_SHUTDOWN_TIMEOUT || "" ) || 5
2929 : config . gracefulTimeout ;
30- const forceShutdown =
31- config === true || ! config ?. forceTimeout
32- ? Number . parseInt ( process . env . SERVER_FORCE_SHUTDOWN_TIMEOUT || "" ) || 5
33- : config . forceTimeout ;
30+
3431 let isShuttingDown = false ;
35- let forceClose : ( ( ) => void ) | undefined ;
32+
33+ const w = server . options . silent ? ( ) => { } : process . stderr . write . bind ( process . stderr ) ;
34+
35+ const forceClose = async ( ) => {
36+ w ( c . red ( "\x1b[2K\rForcibly closing connections...\n" ) ) ;
37+ await server . close ( true ) ;
38+ w ( c . yellow ( "Server closed.\n" ) ) ;
39+ globalThis . process . exit ( 0 ) ;
40+ } ;
41+
3642 const shutdown = async ( ) => {
43+ // Second call: force close immediately
3744 if ( isShuttingDown ) {
38- forceClose ?.( ) ;
39- return ;
45+ return forceClose ( ) ;
4046 }
47+
4148 isShuttingDown = true ;
42- const w = process . stderr . write . bind ( process . stderr ) ;
43- w (
44- c . gray (
45- `\nShutting down server in ${ gracefulShutdown } s... (press Ctrl+C again to force close)` ,
46- ) ,
47- ) ;
48- let timeout : any ;
49- await Promise . race ( [
50- // Graceful shutdown
51- server . close ( ) . finally ( ( ) => {
52- clearTimeout ( timeout ) ;
53- w ( c . gray ( " Server closed.\n" ) ) ;
54- } ) ,
55- new Promise < void > ( ( resolve ) => {
56- forceClose = ( ) => {
57- clearTimeout ( timeout ) ;
58- w ( c . gray ( "\nForce closing...\n" ) ) ;
59- server . close ( true ) ;
60- resolve ( ) ;
61- } ;
62- timeout = setTimeout ( ( ) => {
63- // Graceful shutdown timeout
64- w ( c . gray ( `\nForce closing connections in ${ forceShutdown } s...` ) ) ;
65- timeout = setTimeout ( ( ) => {
66- // Force shutdown timeout
67- w ( c . red ( "\nCould not close connections in time, force exiting." ) ) ;
68- resolve ( ) ;
69- } , forceShutdown * 1000 ) ;
70- return server . close ( true ) ;
71- } , gracefulShutdown * 1000 ) ;
72- } ) ,
73- ] ) ;
74- globalThis . process . exit ( 0 ) ;
49+ const closePromise = server . close ( ) ;
50+
51+ // Countdown with updates each second
52+ for ( let remaining = gracefulTimeout ; remaining > 0 ; remaining -- ) {
53+ w (
54+ c . gray (
55+ `\rStopping server gracefully (${ remaining } s)... Press ${ c . bold ( "Ctrl+C" ) } again to force close.` ,
56+ ) ,
57+ ) ;
58+ const closed = await Promise . race ( [
59+ closePromise . then ( ( ) => true ) ,
60+ new Promise < false > ( ( r ) => setTimeout ( ( ) => r ( false ) , 1000 ) ) ,
61+ ] ) ;
62+ if ( closed ) {
63+ w ( "\x1b[2K\r" + c . green ( "Server closed successfully.\n" ) ) ;
64+ globalThis . process . exit ( 0 ) ;
65+ }
66+ }
67+
68+ // Graceful period expired: force close
69+ w ( "\x1b[2K\rGraceful shutdown timed out.\n" ) ;
70+ await forceClose ( ) ;
7571 } ;
72+
7673 for ( const sig of [ "SIGINT" , "SIGTERM" ] as const ) {
7774 globalThis . process . on ( sig , shutdown ) ;
7875 }
0 commit comments