@@ -5,7 +5,6 @@ import type { DevframeRpcConnection, WsOriginRegistry, WsRpcTransport } from 'de
55import type { H3 , H3Event } from 'h3'
66import type { Buffer } from 'node:buffer'
77import type { IncomingMessage , Server as NodeHttpServer , ServerResponse } from 'node:http'
8- import type { AddressInfo } from 'node:net'
98import type { Duplex } from 'node:stream'
109import type { ConnectionMeta , DevframeNodeContext , DevframeNodeRpcSession , DevframeNodeRpcSessionMeta , DevframeRpcClientFunctions , DevframeRpcServerFunctions } from '../types'
1110import type { DevframeSseOptions , DevframeWsOptions } from '../types/devframe'
@@ -145,16 +144,23 @@ async function bindHttpAndWs(options: BindHttpAndWsOptions): Promise<StartedServ
145144
146145 const address = httpServer . address ( )
147146 const resolvedPort = typeof address === 'object' && address ? address . port : port
148- const origin = httpServer instanceof TlsServer
149- ? `https://${ formatHostForUrl ( bindHost ) } :${ resolvedPort } `
150- : normalizeHttpServerUrl ( bindHost , resolvedPort )
147+ const origin = normalizeHttpServerUrl ( bindHost , resolvedPort )
148+ // A shared server may not be listening yet (Vite listens after plugins configure).
151149 let wsUrl : string | undefined
152- const cancelPublish = websocket
153- ? whenListening ( httpServer , ( boundPort ) => {
154- wsUrl = `${ wsScheme ( httpServer ) } ://${ formatHostForUrl ( bindHost ) } :${ boundPort } ${ options . path ?? '' } `
155- getInternalContext ( context ) . setWsEndpoint ( { url : wsUrl } )
156- } )
157- : ( ) => { }
150+ const publishWsEndpoint = ( ) : void => {
151+ const bound = httpServer . address ( )
152+ if ( ! bound || typeof bound === 'string' )
153+ return
154+ const scheme = httpServer instanceof TlsServer ? 'wss' : 'ws'
155+ wsUrl = `${ scheme } ://${ formatHostForUrl ( bindHost ) } :${ bound . port } ${ options . path ?? '' } `
156+ getInternalContext ( context ) . setWsEndpoint ( { url : wsUrl } )
157+ }
158+ if ( websocket ) {
159+ if ( httpServer . listening )
160+ publishWsEndpoint ( )
161+ else
162+ httpServer . once ( 'listening' , publishWsEndpoint )
163+ }
158164
159165 return {
160166 origin,
@@ -164,7 +170,7 @@ async function bindHttpAndWs(options: BindHttpAndWsOptions): Promise<StartedServ
164170 rpcGroup : core . rpcGroup ,
165171 connectionMeta : ( ) => websocketConnectionMeta ( rpcHost , options . path ) ,
166172 async close ( ) {
167- cancelPublish ( )
173+ httpServer . off ( 'listening' , publishWsEndpoint )
168174 await closeWs ( )
169175 if ( ownsHttpServer )
170176 await new Promise < void > ( r => httpServer . close ( ( ) => r ( ) ) )
@@ -174,24 +180,6 @@ async function bindHttpAndWs(options: BindHttpAndWsOptions): Promise<StartedServ
174180 }
175181}
176182
177- function wsScheme ( server : NodeHttpServer ) : 'ws' | 'wss' {
178- return server instanceof TlsServer ? 'wss' : 'ws'
179- }
180-
181- function whenListening ( server : NodeHttpServer , fn : ( port : number , address : AddressInfo ) => void ) : ( ) => void {
182- const run = ( ) : void => {
183- const address = server . address ( )
184- if ( typeof address === 'object' && address )
185- fn ( address . port , address )
186- }
187- if ( server . listening ) {
188- run ( )
189- return ( ) => { }
190- }
191- server . once ( 'listening' , run )
192- return ( ) => server . off ( 'listening' , run )
193- }
194-
195183/**
196184 * Structural view of `Bun.serve`, typed loosely so devframe carries no
197185 * dependency on Bun's own types (`tsc` runs with Node lib only).
@@ -965,12 +953,19 @@ export function createInstanceShell<TContext extends DevframeNodeContext>(
965953 * {@link bindHttpAndWs} does the same for the tiers it owns.
966954 */
967955 function publishWsEndpoint ( server : NodeHttpServer ) : void {
968- whenListening ( server , ( boundPort , address ) => {
956+ const record = ( ) : void => {
957+ const address = server . address ( )
958+ if ( typeof address !== 'object' || ! address )
959+ return
969960 const host = options . host ?? ( address . address === '::' || address . address === '0.0.0.0' ? 'localhost' : address . address )
970961 getInternalContext ( ctx ) . setWsEndpoint ( {
971- url : `${ wsScheme ( server ) } ://${ formatHostForUrl ( host ) } :${ boundPort } ${ routePath } ` ,
962+ url : `ws ://${ formatHostForUrl ( host ) } :${ address . port } ${ routePath } ` ,
972963 } )
973- } )
964+ }
965+ if ( server . listening )
966+ record ( )
967+ else
968+ server . once ( 'listening' , record )
974969 }
975970
976971 function handleUpgrade ( req : IncomingMessage , socket : Duplex , head : Buffer ) : void {
0 commit comments