@@ -253,16 +253,47 @@ export const createExecutorHandle = async (options: LocalExecutorOptions = {}) =
253253 } ;
254254} ;
255255
256+ class SharedHandleCreateError extends Data . TaggedError ( "SharedHandleCreateError" ) < {
257+ readonly cause : unknown ;
258+ } > { }
259+
256260export type ExecutorHandle = Awaited < ReturnType < typeof createExecutorHandle > > ;
257261
258262let sharedHandlePromise : ReturnType < typeof createExecutorHandle > | null = null ;
259263let sharedHandleLifecycle : Promise < void > = Promise . resolve ( ) ;
260264
261- const loadSharedHandle = ( ) => {
262- if ( ! sharedHandlePromise ) {
263- sharedHandlePromise = sharedHandleLifecycle . then ( ( ) => createExecutorHandle ( ) ) ;
265+ const loadSharedHandle = ( ) : Promise < ExecutorHandle > => {
266+ if ( sharedHandlePromise ) {
267+ return sharedHandlePromise ;
264268 }
265- return sharedHandlePromise ;
269+
270+ // Capture the lifecycle tail at call time so creation stays ordered behind
271+ // in-flight dispose
272+ const lifecycle = sharedHandleLifecycle ;
273+
274+ // Identity token the heal closure compares against. Using a `let` declared
275+ // up front avoids any reference-before-init ambiguity in the closure.
276+ let slot : Promise < ExecutorHandle > ;
277+
278+ const acquire = Effect . tryPromise ( {
279+ try : ( ) => lifecycle . then ( ( ) => createExecutorHandle ( ) ) ,
280+ catch : ( cause ) => new SharedHandleCreateError ( { cause } ) ,
281+ } ) . pipe (
282+ // Self-heal: a failed creation must not poison the memo. Clear the slot on
283+ // any non-success outcome so the next getExecutor() retries, but only if a
284+ // dispose/reload hasn't already swapped in a newer promise (identity guard).
285+ Effect . onError ( ( ) =>
286+ Effect . sync ( ( ) => {
287+ if ( sharedHandlePromise === slot ) {
288+ sharedHandlePromise = null ;
289+ }
290+ } ) ,
291+ ) ,
292+ ) ;
293+
294+ slot = Effect . runPromise ( acquire ) ;
295+ sharedHandlePromise = slot ;
296+ return slot ;
266297} ;
267298
268299export const getExecutor = ( ) => loadSharedHandle ( ) . then ( ( handle ) => handle . executor ) ;
0 commit comments