import { Duration, Effect, Schedule, Scope } from 'effect'; import type { Leadership, LeadershipIdentity, LeadershipObserver, } from '../../platform/leadership/index.js'; import type { StrategyFlow } from '../sync-flow/index.js'; import type { LeadershipState } from '../../domain/sync-event/index.js'; export const superviseStrategy = (args: { leadership: Leadership; identity: LeadershipIdentity; flow?: StrategyFlow; run: (scope: Scope.Scope) => Effect.Effect; onError: (error: E) => Effect.Effect; onDefect?: (defect: unknown) => Effect.Effect; onLeadership?: ( state: LeadershipState, ) => Effect.Effect; }): Effect.Effect => Effect.flatMap(Effect.context(), (context) => { const transition = (message: string, state: LeadershipState) => Effect.all( [ args.flow ? args.flow.event(message, { attributes: { leadership: state, leadershipIdentity: args.identity, }, }) : Effect.void, args.onLeadership?.(state) ?? Effect.void, ], { discard: true }, ).pipe(Effect.provide(context)); const observer: LeadershipObserver = { requested: transition('Leadership requested', 'waiting'), acquired: transition('Leadership acquired', 'leading'), released: transition('Leadership released', 'released'), }; return args.leadership .run( args.identity, Effect.scoped( Effect.gen(function* () { const scope = yield* Effect.scope; yield* args.run(scope); }), ).pipe(Effect.andThen(Effect.never)), observer, ) .pipe( Effect.tapError(args.onError), args.onDefect ? Effect.tapDefect(args.onDefect) : (effect) => effect, Effect.retry( Schedule.spaced(Duration.seconds(2)).pipe(Schedule.jittered), ), ); });