@@ -49,6 +49,7 @@ import {
4949 type FactoryRealmTemplate ,
5050 type FactorySupportContext ,
5151 type FactoryTestContext ,
52+ type PortReservation ,
5253 type RealmAction ,
5354 type RealmConfig ,
5455 type StartedFactoryRealm ,
@@ -82,22 +83,33 @@ export { startFactorySupportServices };
8283
8384export async function startFactoryGlobalContext (
8485 options : FactoryRealmOptions ,
86+ internal : { publicPortReservation ?: PortReservation } = { } ,
8587) : Promise < FactoryGlobalContextHandle > {
8688 return await logTimed ( harnessLog , 'startFactoryGlobalContext' , async ( ) => {
8789 let realms = resolveRealms ( options . realms ) ;
88- let realmServerURL = await resolveFactoryRealmServerURL (
89- options . realmServerURL ,
90- options . compatRealmServerPort ,
91- ) ;
90+ let { url : realmServerURL , portReservation } =
91+ await resolveFactoryRealmServerURL (
92+ options . realmServerURL ,
93+ options . compatRealmServerPort ,
94+ ) ;
95+ // Prefer a holder the caller already owns (startFactoryRealmServer
96+ // resolved + held the port before delegating here). Our own
97+ // portReservation is undefined in that case, since options.realmServerURL
98+ // is set.
99+ let publicPortReservation =
100+ internal . publicPortReservation ?? portReservation ;
92101 let primaryRealmURL = realmURLWithinServer ( realmServerURL , realms [ 0 ] . path ) ;
93102 let support = await startFactorySupportServices ( ) ;
94103 try {
95- let template = await ensureFactoryRealmTemplate ( {
96- ...options ,
97- realms,
98- realmServerURL,
99- context : support . context ,
100- } ) ;
104+ let template = await ensureFactoryRealmTemplate (
105+ {
106+ ...options ,
107+ realms,
108+ realmServerURL,
109+ context : support . context ,
110+ } ,
111+ { publicPortReservation } ,
112+ ) ;
101113
102114 let context : FactoryTestContext = {
103115 ...support . context ,
@@ -116,6 +128,12 @@ export async function startFactoryGlobalContext(
116128 } catch ( error ) {
117129 await support . stop ( ) ;
118130 throw error ;
131+ } finally {
132+ // The public port is only bound transiently, by the template build's
133+ // compat proxy. The global context never holds it once built, so
134+ // release the holder (idempotent: the build already released it just
135+ // before binding; on a cache hit it was never consumed).
136+ await publicPortReservation ?. release ( ) . catch ( ( ) => undefined ) ;
119137 }
120138 } ) ;
121139}
@@ -132,17 +150,29 @@ function resolveRealms(realms: RealmConfig[]): RealmConfig[] {
132150
133151export async function ensureFactoryRealmTemplate (
134152 options : FactoryRealmOptions & { forceRebuild ?: boolean } ,
153+ internal : { publicPortReservation ?: PortReservation } = { } ,
135154) : Promise < FactoryRealmTemplate > {
136155 return await logTimed ( harnessLog , 'ensureFactoryRealmTemplate' , async ( ) => {
137156 let realms = resolveRealms ( options . realms ) ;
138157 let contextRealmServerURL =
139158 options . context && hasTemplateDatabaseName ( options . context )
140159 ? new URL ( options . context . realmServerURL )
141160 : undefined ;
142- let realmServerURL = await resolveFactoryRealmServerURL (
143- options . realmServerURL ?? contextRealmServerURL ,
144- options . compatRealmServerPort ,
145- ) ;
161+ let { url : realmServerURL , portReservation } =
162+ await resolveFactoryRealmServerURL (
163+ options . realmServerURL ?? contextRealmServerURL ,
164+ options . compatRealmServerPort ,
165+ ) ;
166+ // The effective public-port holder: the caller's delegated reservation
167+ // when present, otherwise the one we freshly allocated above (e.g. the
168+ // cache:prepare path). On the build path startIsolatedRealmStack
169+ // releases it right before the compat proxy binds; we release it again
170+ // at the early (cache-hit) and finally exits as an idempotent backstop.
171+ // Releasing the *effective* holder (not just our own) matters when a
172+ // caller delegates it but the build throws before startIsolatedRealmStack
173+ // takes ownership — otherwise the delegated holder socket leaks.
174+ let publicPortReservation =
175+ internal . publicPortReservation ?? portReservation ;
146176 let primaryRealmURL = realmURLWithinServer ( realmServerURL , realms [ 0 ] . path ) ;
147177 let realmsHash = hashRealms ( realms ) ;
148178 let cacheKey = hashString (
@@ -163,6 +193,9 @@ export async function ensureFactoryRealmTemplate(
163193 hasTemplateDatabase &&
164194 cachedTemplateMetadata
165195 ) {
196+ // No build, so nothing downstream will consume the holder — release
197+ // the effective one (our own or a caller's delegated reservation).
198+ await publicPortReservation ?. release ( ) . catch ( ( ) => undefined ) ;
166199 return {
167200 cacheKey,
168201 templateDatabaseName,
@@ -211,6 +244,7 @@ export async function ensureFactoryRealmTemplate(
211244 context,
212245 cacheKey,
213246 templateDatabaseName,
247+ publicPortReservation,
214248 } ) ;
215249 writePreparedTemplateMetadata ( {
216250 realmDir : realms [ 0 ] . dir ,
@@ -231,6 +265,11 @@ export async function ensureFactoryRealmTemplate(
231265 } finally {
232266 configureLogger ( originalLogLevels ) ;
233267 await withSilentConsole ( async ( ) => ownedSupport ?. stop ( ) ) ;
268+ // Backstop for the effective holder (our own or a caller's delegated
269+ // reservation): a no-op once startIsolatedRealmStack has released it
270+ // before the compat-proxy bind, but covers a throw before the build
271+ // reached that point (e.g. a Postgres drop/clone failure).
272+ await publicPortReservation ?. release ( ) . catch ( ( ) => undefined ) ;
234273 }
235274 } ) ;
236275}
@@ -297,10 +336,11 @@ export async function startFactoryRealmServer(
297336 existingContext && hasTemplateDatabaseName ( existingContext )
298337 ? new URL ( existingContext . realmServerURL )
299338 : undefined ;
300- let realmServerURL = await resolveFactoryRealmServerURL (
301- options . realmServerURL ?? contextRealmServerURL ,
302- options . compatRealmServerPort ,
303- ) ;
339+ let { url : realmServerURL , portReservation : publicPortReservation } =
340+ await resolveFactoryRealmServerURL (
341+ options . realmServerURL ?? contextRealmServerURL ,
342+ options . compatRealmServerPort ,
343+ ) ;
304344 let primaryRealmURL = realmURLWithinServer (
305345 realmServerURL ,
306346 primaryRealm . path ,
@@ -311,24 +351,33 @@ export async function startFactoryRealmServer(
311351 let ownedGlobalContext : FactoryGlobalContextHandle | undefined ;
312352 let context = existingContext ;
313353 if ( ! context ) {
314- ownedGlobalContext = await startFactoryGlobalContext ( {
315- ...options ,
316- realms,
317- realmServerURL,
318- } ) ;
354+ // Hand the public-port holder to the template build so it's held
355+ // across that build's port allocations and released right before its
356+ // compat-proxy binds.
357+ ownedGlobalContext = await startFactoryGlobalContext (
358+ {
359+ ...options ,
360+ realms,
361+ realmServerURL,
362+ } ,
363+ { publicPortReservation } ,
364+ ) ;
319365 context = ownedGlobalContext . context ;
320366 }
321367
322368 if ( ! templateDatabaseName ) {
323369 templateDatabaseName = hasTemplateDatabaseName ( context )
324370 ? context . templateDatabaseName
325371 : (
326- await ensureFactoryRealmTemplate ( {
327- ...options ,
328- realms,
329- realmServerURL,
330- context,
331- } )
372+ await ensureFactoryRealmTemplate (
373+ {
374+ ...options ,
375+ realms,
376+ realmServerURL,
377+ context,
378+ } ,
379+ { publicPortReservation } ,
380+ )
332381 ) . templateDatabaseName ;
333382 }
334383
@@ -380,6 +429,11 @@ export async function startFactoryRealmServer(
380429 }
381430 }
382431
432+ // The template build (above) already released this holder before its
433+ // own compat-proxy bind; release again (idempotent) so the runtime
434+ // stack's compat proxy can re-acquire the same public port via its
435+ // own short-lived hold.
436+ await publicPortReservation ?. release ( ) . catch ( ( ) => undefined ) ;
383437 stack = await startIsolatedRealmStack ( {
384438 realms,
385439 realmServerURL,
@@ -394,6 +448,10 @@ export async function startFactoryRealmServer(
394448 } catch ( error ) {
395449 let cleanupError : unknown ;
396450
451+ // Backstop: release the public-port holder if an error fired before it
452+ // was handed off (idempotent — a no-op once already released).
453+ await publicPortReservation ?. release ( ) . catch ( ( ) => undefined ) ;
454+
397455 try {
398456 await dropDatabase ( databaseName ) ;
399457 } catch ( cleanupFailure ) {
0 commit comments