// ets_tracing: off import * as C from "../Cause/index.js" import * as CL from "../Clock/index.js" import * as Tp from "../Collections/Immutable/Tuple/index.js" import * as E from "../Either/index.js" import { identity as idFn, pipe } from "../Function/index.js" import type { Has, Tag } from "../Has/index.js" import type * as SCD from "../Schedule/Decision/index.js" import type * as SC from "../Schedule/index.js" import type { UnionToIntersection } from "../Utils/index.js" import type { Layer, MergeA, MergeE, MergeR } from "./definitions.js" import { build, compose_, fold, fromRawEffect, fromRawFunction, fromRawFunctionM, fromRawManaged, identity, LayerAllPar, LayerAllSeq, LayerChain, LayerFresh, LayerManaged, LayerMap, LayerSuspend, LayerZipWithPar, LayerZipWithSeq } from "./definitions.js" import * as T from "./deps-effect.js" import * as M from "./deps-managed.js" export * from "./definitions.js" /** * Lazily constructs a layer. This is useful to avoid infinite recursion when * creating layers that refer to themselves. */ export function suspend( f: () => Layer ): Layer { return new LayerSuspend(f) } /** * Combines this layer with the specified layer, producing a new layer that * has the inputs of both layers, and the outputs of both layers combined * using the specified function. */ export function zipWithPar_( self: Layer, that: Layer, f: (a: ROut, b: ROut1) => ROut2 ): Layer { return new LayerZipWithPar(self, that, f) } /** * Constructs a layer that fails with the specified value. */ export function fail(e: E): Layer { return fromRawManaged(M.fail(e)) } /** * Constructs a layer from the specified value. */ export function succeed(resource: T): Layer { return fromRawManaged(M.succeed(resource)) } /** * Combines this layer with the specified layer, producing a new layer that * has the inputs of both layers, and the outputs of both layers combined * using the specified function. */ export function zipWithPar( that: Layer, f: (a: ROut, b: ROut1) => ROut2 ) { return (self: Layer) => zipWithPar_(self, that, f) } /** * Combines this layer with the specified layer, producing a new layer that * has the inputs of both layers, and the outputs of both layers combined * into a tuple. */ export function zipPar_( self: Layer, that: Layer ): Layer> { return zipWithPar_(self, that, Tp.tuple) } /** * Combines this layer with the specified layer, producing a new layer that * has the inputs of both layers, and the outputs of both layers combined * into a tuple. */ export function zipPar(that: Layer) { return (self: Layer) => zipPar_(self, that) } /** * Construct a service layer from a value */ export function fromValue(has: Tag) { return (resource: T): Layer> => new LayerManaged(M.fromEffect(T.succeed(has.has(resource)))).setKey(has.key) } /** * Constructs a layer from the specified effect. * * @ets_data_first fromEffect_ */ export function fromEffect(has: Tag) { return (resource: T.Effect): Layer> => fromEffect_(resource, has) } /** * Constructs a layer from the specified effect. */ export function fromEffect_( resource: T.Effect, has: Tag ): Layer> { return new LayerManaged(M.map_(M.fromEffect(resource), has.has)).setKey(has.key) } /** * Constructs a layer from a managed resource. */ export function fromManaged(has: Tag) { return (resource: M.Managed): Layer> => new LayerManaged(M.map_(resource, has.has)).setKey(has.key) } /** * Constructs a layer from a managed resource. */ export function fromManaged_( resource: M.Managed, has: Tag ): Layer> { return new LayerManaged(M.map_(resource, has.has)).setKey(has.key) } /** * Constructs a layer from the environment using the specified function. */ export function fromFunction(tag: Tag) { return (f: (a: A) => B): Layer> => fromEffect(tag)(T.access(f)) } /** * Zips layers together */ export function zip_( self: Layer, that: Layer ): Layer> { return new LayerZipWithSeq(self, that, Tp.tuple) } /** * Zips layers together */ export function zip(right: Layer) { return (left: Layer) => zip_(left, right) } /** * Merges layers sequentially */ export function andSeq(that: Layer) { return (self: Layer) => andSeq_(self, that) } /** * Merges layers sequentially */ export function andSeq_( self: Layer, that: Layer ): Layer { return new LayerZipWithSeq(self, that, (l, r) => ({ ...l, ...r })) } /** * Merges all layers in parallel */ export function all[]>( ...ls: Ls & { 0: Layer } ): Layer, MergeE, MergeA> { return new LayerAllPar(ls) } /** * Merges all layers sequentially */ export function allSeq[]>( ...ls: Ls & { 0: Layer } ): Layer, MergeE, MergeA> { return new LayerAllSeq(ls) } /** * Type level bound to make sure a layer is complete */ export function main(layer: Layer) { return layer } /** * Converts a layer to a managed runtime */ export function toRuntime( _: Layer ): M.Managed> { return M.chain_(build(_), (a) => M.fromEffect( T.checkPlatform((platform) => T.succeedWith(() => T.makeCustomRuntime(a, platform)) ) ) ) } /** * Creates a fresh version of this layer that will not be shared. */ export function fresh(layer: Layer): Layer { return new LayerFresh(layer) } /** * Returns a new layer whose output is mapped by the specified function. */ export function map(f: (a: A) => B) { return (fa: Layer): Layer => map_(fa, f) } /** * Maps the output of the layer using f */ export function map_(fa: Layer, f: (a: A) => B): Layer { return new LayerMap(fa, f) } /** * Chains the output of the layer using f */ export function chain(f: (a: A) => Layer) { return (fa: Layer): Layer => chain_(fa, f) } /** * Chains the output of the layer using f */ export function chain_( fa: Layer, f: (a: A) => Layer ) { return new LayerChain(fa, f) } /** * Flatten `Layer< R, E, Layer< R2, E2, A>>` */ export function flatten( ffa: Layer> ): Layer { return chain_(ffa, idFn) } /** * Restrict output to only contain the specified services */ export function restrict[]>(...ts: Tags) { return ( self: Layer< R, E, UnionToIntersection< { [k in keyof Tags]: [Tags[k]] extends [Tag] ? Has : never }[number] > > ): Layer< R, E, UnionToIntersection< { [k in keyof Tags]: [Tags[k]] extends [Tag] ? Has : never }[number] > > => compose_( self, fromRawEffect( T.accessServicesT(...ts)((...servises) => servises .map((s, i) => ({ [ts[i]!.key]: s } as any)) .reduce((x, y) => ({ ...x, ...y })) ) ) ) as any } /** * Builds this layer and uses it until it is interrupted. This is useful when * your entire application is a layer, such as an HTTP server. */ export function launch(self: Layer): T.Effect { return M.useForever(build(self)) } /** * Recovers from all errors. */ export function catchAll(handler: Layer, E1, Out1>) { return (self: Layer): Layer => { return fold(self)( fromRawFunctionM(({ tuple: [r, cause] }: Tp.Tuple<[R1, C.Cause]>) => E.fold_( C.failureOrCause(cause), (e) => T.succeed(Tp.tuple(r, e)), (c) => T.halt(c) ) )[">=>"](handler) )(fromRawEffect(T.environment())) } } /** * A layer that passes along the first element of a tuple. */ export function first() { return fromRawFunction((_: Tp.Tuple<[A, unknown]>) => _.get(0)) } /** * A layer that passes along the second element of a tuple. */ export function second() { return fromRawFunction((_: Tp.Tuple<[unknown, A]>) => _.get(1)) } /** * Returns a layer with its error channel mapped using the specified * function. */ export function mapError( f: (e: E) => E1 ): (self: Layer) => Layer { return catchAll(fromRawFunctionM((_: Tp.Tuple<[unknown, E]>) => T.fail(f(_.get(1))))) } /** * Translates effect failure into death of the fiber, making all failures * unchecked and not a part of the type of the layer. */ export function orDie(self: Layer): Layer { return catchAll(fromRawFunctionM((_: Tp.Tuple<[unknown, E]>) => T.die(_.get(1))))( self ) } /** * Executes this layer and returns its output, if it succeeds, but otherwise * executes the specified layer. */ export function orElse(that: Layer) { return catchAll(first()[">=>"](that)) } function retryLoop( self: Layer ): Layer< Tp.Tuple< [RIn & RIn1 & CL.HasClock, SCD.StepFunction] >, E, ROut > { const update = fromRawFunctionM( ({ tuple: [ { tuple: [r, s] }, e ] }: Tp.Tuple< [ Tp.Tuple< [RIn & RIn1 & CL.HasClock, SCD.StepFunction] >, E ] >) => pipe( CL.currentTime, T.orDie, T.chain((now) => pipe( T.chain_(s(now, e), (result) => { if (result._tag === "Done") { return T.fail(e) } else { return pipe( CL.sleep(Math.abs(now - result.interval)), T.as(Tp.tuple(r, result.next)) ) } }) ) ), T.provideAll(r) ) ) return pipe( first()[">=>"](self), catchAll(update[">=>"](suspend(() => fresh(retryLoop(self))))) ) } /** * Retries constructing this layer according to the specified schedule. */ export function retry( self: Layer, schedule: SC.Schedule ): Layer { return zipPar_( identity(), fromRawEffect(T.succeed(schedule.step)) )[">=>"](retryLoop(self)) }