import { type Cause, Context, Effect, Layer, Option, Pipeable, Predicate, PubSub, type Scope } from "effect" export const ErrorObserverTypeId: unique symbol = Symbol.for("@effect-fc/ErrorObserver/ErrorObserver") export type ErrorObserverTypeId = typeof ErrorObserverTypeId export interface ErrorObserver extends Pipeable.Pipeable { readonly [ErrorObserverTypeId]: ErrorObserverTypeId handle(effect: Effect.Effect): Effect.Effect readonly subscribe: Effect.Effect>, never, Scope.Scope> } export const ErrorObserver = () => Context.Service>( "@effect-fc/ErrorObserver/ErrorObserver", ) export class ErrorObserverImpl extends Pipeable.Class implements ErrorObserver { readonly [ErrorObserverTypeId]: ErrorObserverTypeId = ErrorObserverTypeId constructor(readonly pubsub: PubSub.PubSub>) { super() } get subscribe(): Effect.Effect>, never, Scope.Scope> { return PubSub.subscribe(this.pubsub) } handle(effect: Effect.Effect): Effect.Effect { return Effect.tapCause(effect, cause => Effect.asVoid( PubSub.publish(this.pubsub, cause as unknown as Cause.Cause), )) } } export const isErrorObserver = (u: unknown): u is ErrorObserver => Predicate.hasProperty(u, ErrorObserverTypeId) export const layer: Layer.Layer = Layer.effect(ErrorObserver())( Effect.map(PubSub.unbounded>(), pubsub => new ErrorObserverImpl(pubsub)), ) export const handle = (effect: Effect.Effect): Effect.Effect => Effect.flatMap( Effect.serviceOption(ErrorObserver()), Option.match({ onSome: observer => observer.handle(effect), onNone: () => effect, }), )