import { Cause, Effect, Exit } from "effect"; import type { WorkerContextService, WorkerContextWaitUntilOptions } from "../Worker"; export type RunWaitUntilEffect = ( effect: Effect.Effect, ) => Promise>; const causeError = (cause: Cause.Cause) => new Error(Cause.pretty(cause)); const failureHandler = ( cause: Cause.Cause, options: WorkerContextWaitUntilOptions | undefined, ) => ( options?.onFailure?.(cause) ?? Effect.logError("WorkerContext.waitUntil failed", Cause.pretty(cause)) ).pipe( Effect.catchCause((handlerCause) => Effect.logError( "WorkerContext.waitUntil failure handler failed", Cause.pretty(cause), Cause.pretty(handlerCause), ), ), ); export const fromExecutionContext = ( ctx: globalThis.ExecutionContext, runPromiseExit: RunWaitUntilEffect, ): WorkerContextService => { const schedule = ( effect: Effect.Effect, options: WorkerContextWaitUntilOptions | undefined, mode: "observe" | "propagate", ) => Effect.context().pipe( Effect.flatMap((context) => Effect.sync(() => { const runHandler = (cause: Cause.Cause) => runPromiseExit( Effect.scoped(Effect.provideContext(failureHandler(cause, options), context)), ).then((exit) => { if (Exit.isFailure(exit)) { console.error( "WorkerContext.waitUntil failure handler failed", Cause.pretty(exit.cause), ); } }); ctx.waitUntil( runPromiseExit(Effect.scoped(Effect.provideContext(effect, context))).then( async (exit) => { if (Exit.isSuccess(exit)) { return; } await runHandler(exit.cause as Cause.Cause); if (mode === "propagate") { throw causeError(exit.cause as Cause.Cause); } }, ), ); }), ), ); return { raw: ctx, waitUntil: ( effect: Effect.Effect, options?: WorkerContextWaitUntilOptions, ) => schedule(effect, options, options?.mode ?? "observe"), waitUntilPropagating: ( effect: Effect.Effect, options?: Omit, "mode">, ) => schedule(effect, options, "propagate"), passThroughOnException: Effect.sync(() => ctx.passThroughOnException()), }; };