@@ -123,7 +123,7 @@ async function waitForHttp(url, timeoutMs = 30000, init) {
123123 while ( Date . now ( ) - start < timeoutMs ) {
124124 try {
125125 const res = await fetch ( url , init ) ;
126- if ( res . ok || res . status === 404 || res . status === 500 ) return ;
126+ if ( res . ok || res . status === 404 || res . status === 500 ) return res ;
127127 } catch { }
128128 await new Promise ( ( r ) => setTimeout ( r , 250 ) ) ;
129129 }
@@ -439,10 +439,15 @@ async function prodMode() {
439439 child . stderr . on ( 'data' , ( d ) => ( log += d ) ) ;
440440 return { child, getLog : ( ) => log } ;
441441 } ;
442- const stop = ( child ) => {
442+ const stop = async ( child ) => {
443+ if ( child . exitCode !== null || child . signalCode !== null ) return ;
444+ const exited = new Promise ( ( resolve ) => child . once ( 'exit' , resolve ) ) ;
443445 try {
444446 process . kill ( - child . pid , 'SIGTERM' ) ;
445- } catch { }
447+ } catch {
448+ child . kill ( 'SIGTERM' ) ;
449+ }
450+ await exited ;
446451 } ;
447452 const origin = `http://localhost:${ PREVIEW_PORT } ` ;
448453
@@ -455,7 +460,7 @@ async function prodMode() {
455460 record ( 'prod' , 'preview' , 'app SSRs with the client env value' , html . includes ( 'EnvApp' ) ) ;
456461 record ( 'prod' , 'preview' , 'server secret never in HTML' , ! html . includes ( SECRET ) ) ;
457462 checkEnvHeaders ( 'prod' , 'preview' , headers ) ;
458- stop ( server ) ;
463+ await stop ( server ) ;
459464
460465 // 2. Rotation without rebuild: the same artifact, a different secret in
461466 // the process environment (real env wins over the .env fold) — the
@@ -465,18 +470,19 @@ async function prodMode() {
465470 await waitForHttp ( origin + '/' , 30000 , { headers : { accept : 'text/html' } } ) ;
466471 const rotated = await fetchPage ( origin + '/' ) ;
467472 record ( 'prod' , 'rotation' , 'rotated secret visible without a rebuild (runtime env)' , rotated . headers . get ( 'x-env-secret-len' ) === String ( ROTATED . length ) , `len ${ rotated . headers . get ( 'x-env-secret-len' ) } , want ${ ROTATED . length } ` ) ;
468- stop ( server ) ;
473+ await stop ( server ) ;
469474
470475 // 3. Boot validation: the same artifact with an invalid environment (no
471476 // SESSION_SECRET anywhere) must fail at boot with the per-key report.
472477 writeFileSync ( ENV_FILE , ENV_CONTENT . replace ( / ^ S E S S I O N _ S E C R E T = .* $ / m, '' ) ) ;
473478 const failing = preview ( { } ) ;
474- await waitForHttp ( origin + '/' , 30000 , { headers : { accept : 'text/html' } } ) ;
475- const boot = await fetchPage ( origin + '/' ) ;
476- const bootReport = boot . html + failing . getLog ( ) ;
479+ const boot = await waitForHttp ( origin + '/' , 30000 , {
480+ headers : { accept : 'text/html' } ,
481+ } ) ;
482+ const bootReport = ( await boot . text ( ) . catch ( ( ) => '' ) ) + failing . getLog ( ) ;
477483 record ( 'prod' , 'boot' , 'invalid runtime env fails boot (no page served)' , boot . status !== 200 , `status ${ boot . status } ` ) ;
478484 record ( 'prod' , 'boot' , 'boot failure carries the per-key report' , / s e r v e r e n v v a l i d a t i o n f a i l e d a t b o o t / . test ( bootReport ) && / S E S S I O N _ S E C R E T / . test ( bootReport ) , bootReport . slice ( 0 , 300 ) ) ;
479- stop ( failing . child ) ;
485+ await stop ( failing . child ) ;
480486 writeFileSync ( ENV_FILE , ENV_CONTENT ) ;
481487}
482488
0 commit comments