@effect-ts/system
Version:
Effect-TS is a zero dependency set of libraries to write highly productive, purely functional TypeScript at scale.
447 lines (406 loc) • 11.5 kB
text/typescript
// 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<RIn, E, ROut>(
f: () => Layer<RIn, E, ROut>
): Layer<RIn, E, ROut> {
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_<RIn, RIn1, E, E1, ROut, ROut1, ROut2>(
self: Layer<RIn, E, ROut>,
that: Layer<RIn1, E1, ROut1>,
f: (a: ROut, b: ROut1) => ROut2
): Layer<RIn & RIn1, E | E1, ROut2> {
return new LayerZipWithPar(self, that, f)
}
/**
* Constructs a layer that fails with the specified value.
*/
export function fail<E>(e: E): Layer<unknown, E, never> {
return fromRawManaged(M.fail(e))
}
/**
* Constructs a layer from the specified value.
*/
export function succeed<T>(resource: T): Layer<unknown, never, T> {
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<RIn1, E1, ROut, ROut1, ROut2>(
that: Layer<RIn1, E1, ROut1>,
f: (a: ROut, b: ROut1) => ROut2
) {
return <RIn, E>(self: Layer<RIn, E, ROut>) => 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_<RIn, RIn1, E, E1, ROut, ROut1>(
self: Layer<RIn, E, ROut>,
that: Layer<RIn1, E1, ROut1>
): Layer<RIn & RIn1, E | E1, Tp.Tuple<[ROut, ROut1]>> {
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<RIn, RIn1, E, E1, ROut, ROut1>(that: Layer<RIn1, E1, ROut1>) {
return (self: Layer<RIn, E, ROut>) => zipPar_(self, that)
}
/**
* Construct a service layer from a value
*/
export function fromValue<T>(has: Tag<T>) {
return (resource: T): Layer<unknown, never, Has<T>> =>
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<T>(has: Tag<T>) {
return <R, E>(resource: T.Effect<R, E, T>): Layer<R, E, Has<T>> =>
fromEffect_(resource, has)
}
/**
* Constructs a layer from the specified effect.
*/
export function fromEffect_<R, E, T>(
resource: T.Effect<R, E, T>,
has: Tag<T>
): Layer<R, E, Has<T>> {
return new LayerManaged(M.map_(M.fromEffect(resource), has.has)).setKey(has.key)
}
/**
* Constructs a layer from a managed resource.
*/
export function fromManaged<T>(has: Tag<T>) {
return <R, E>(resource: M.Managed<R, E, T>): Layer<R, E, Has<T>> =>
new LayerManaged(M.map_(resource, has.has)).setKey(has.key)
}
/**
* Constructs a layer from a managed resource.
*/
export function fromManaged_<R, E, T>(
resource: M.Managed<R, E, T>,
has: Tag<T>
): Layer<R, E, Has<T>> {
return new LayerManaged(M.map_(resource, has.has)).setKey(has.key)
}
/**
* Constructs a layer from the environment using the specified function.
*/
export function fromFunction<B>(tag: Tag<B>) {
return <A>(f: (a: A) => B): Layer<A, never, Has<B>> => fromEffect(tag)(T.access(f))
}
/**
* Zips layers together
*/
export function zip_<R, E, A, R2, E2, A2>(
self: Layer<R, E, A>,
that: Layer<R2, E2, A2>
): Layer<R & R2, E | E2, Tp.Tuple<[A, A2]>> {
return new LayerZipWithSeq(self, that, Tp.tuple)
}
/**
* Zips layers together
*/
export function zip<R2, E2, A2>(right: Layer<R2, E2, A2>) {
return <R, E, A>(left: Layer<R, E, A>) => zip_(left, right)
}
/**
* Merges layers sequentially
*/
export function andSeq<R2, E2, A2>(that: Layer<R2, E2, A2>) {
return <R, E, A>(self: Layer<R, E, A>) => andSeq_(self, that)
}
/**
* Merges layers sequentially
*/
export function andSeq_<R, E, A, R2, E2, A2>(
self: Layer<R, E, A>,
that: Layer<R2, E2, A2>
): Layer<R & R2, E | E2, A & A2> {
return new LayerZipWithSeq(self, that, (l, r) => ({ ...l, ...r }))
}
/**
* Merges all layers in parallel
*/
export function all<Ls extends Layer<any, any, any>[]>(
...ls: Ls & { 0: Layer<any, any, any> }
): Layer<MergeR<Ls>, MergeE<Ls>, MergeA<Ls>> {
return new LayerAllPar(ls)
}
/**
* Merges all layers sequentially
*/
export function allSeq<Ls extends Layer<any, any, any>[]>(
...ls: Ls & { 0: Layer<any, any, any> }
): Layer<MergeR<Ls>, MergeE<Ls>, MergeA<Ls>> {
return new LayerAllSeq(ls)
}
/**
* Type level bound to make sure a layer is complete
*/
export function main<E, A>(layer: Layer<T.DefaultEnv, E, A>) {
return layer
}
/**
* Converts a layer to a managed runtime
*/
export function toRuntime<R, E, A>(
_: Layer<R, E, A>
): M.Managed<R, E, T.CustomRuntime<A, unknown>> {
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<R, E, A>(layer: Layer<R, E, A>): Layer<R, E, A> {
return new LayerFresh(layer)
}
/**
* Returns a new layer whose output is mapped by the specified function.
*/
export function map<A, B>(f: (a: A) => B) {
return <R, E>(fa: Layer<R, E, A>): Layer<R, E, B> => map_(fa, f)
}
/**
* Maps the output of the layer using f
*/
export function map_<R, E, A, B>(fa: Layer<R, E, A>, f: (a: A) => B): Layer<R, E, B> {
return new LayerMap(fa, f)
}
/**
* Chains the output of the layer using f
*/
export function chain<R2, E2, A, B>(f: (a: A) => Layer<R2, E2, B>) {
return <R, E>(fa: Layer<R, E, A>): Layer<R & R2, E | E2, B> => chain_(fa, f)
}
/**
* Chains the output of the layer using f
*/
export function chain_<R, E, A, R2, E2, B>(
fa: Layer<R, E, A>,
f: (a: A) => Layer<R2, E2, B>
) {
return new LayerChain(fa, f)
}
/**
* Flatten `Layer< R, E, Layer< R2, E2, A>>`
*/
export function flatten<R, E, R2, E2, B>(
ffa: Layer<R, E, Layer<R2, E2, B>>
): Layer<R & R2, E | E2, B> {
return chain_(ffa, idFn)
}
/**
* Restrict output to only contain the specified services
*/
export function restrict<Tags extends Tag<any>[]>(...ts: Tags) {
return <R, E>(
self: Layer<
R,
E,
UnionToIntersection<
{
[k in keyof Tags]: [Tags[k]] extends [Tag<infer A>] ? Has<A> : never
}[number]
>
>
): Layer<
R,
E,
UnionToIntersection<
{
[k in keyof Tags]: [Tags[k]] extends [Tag<infer A>] ? Has<A> : 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<R, E, A>(self: Layer<R, E, A>): T.Effect<R, E, never> {
return M.useForever(build(self))
}
/**
* Recovers from all errors.
*/
export function catchAll<R1, E, E1, Out1>(handler: Layer<Tp.Tuple<[R1, E]>, E1, Out1>) {
return <R, Out>(self: Layer<R, E, Out>): Layer<R & R1, E1, Out1 | Out> => {
return fold(self)(
fromRawFunctionM(({ tuple: [r, cause] }: Tp.Tuple<[R1, C.Cause<E>]>) =>
E.fold_(
C.failureOrCause(cause),
(e) => T.succeed(Tp.tuple(r, e)),
(c) => T.halt(c)
)
)[">=>"](handler)
)(fromRawEffect(T.environment<Out>()))
}
}
/**
* A layer that passes along the first element of a tuple.
*/
export function first<A>() {
return fromRawFunction((_: Tp.Tuple<[A, unknown]>) => _.get(0))
}
/**
* A layer that passes along the second element of a tuple.
*/
export function second<A>() {
return fromRawFunction((_: Tp.Tuple<[unknown, A]>) => _.get(1))
}
/**
* Returns a layer with its error channel mapped using the specified
* function.
*/
export function mapError<E, E1>(
f: (e: E) => E1
): <R, Out>(self: Layer<R, E, Out>) => Layer<R, E1, Out> {
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<R, E, Out>(self: Layer<R, E, Out>): Layer<R, never, Out> {
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<RIn1, E1, ROut1>(that: Layer<RIn1, E1, ROut1>) {
return catchAll(first<RIn1>()[">=>"](that))
}
function retryLoop<RIn, RIn1, E, ROut>(
self: Layer<RIn, E, ROut>
): Layer<
Tp.Tuple<
[RIn & RIn1 & CL.HasClock, SCD.StepFunction<RIn & RIn1 & CL.HasClock, E, any>]
>,
E,
ROut
> {
const update = fromRawFunctionM(
({
tuple: [
{
tuple: [r, s]
},
e
]
}: Tp.Tuple<
[
Tp.Tuple<
[RIn & RIn1 & CL.HasClock, SCD.StepFunction<RIn & RIn1 & CL.HasClock, E, any>]
>,
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<RIn>()[">=>"](self),
catchAll(update[">=>"](suspend(() => fresh(retryLoop(self)))))
)
}
/**
* Retries constructing this layer according to the specified schedule.
*/
export function retry<RIn, RIn1, E, ROut>(
self: Layer<RIn, E, ROut>,
schedule: SC.Schedule<RIn1 & CL.HasClock, E, any>
): Layer<RIn1 & RIn & CL.HasClock, E, ROut> {
return zipPar_(
identity<RIn & RIn1 & CL.HasClock>(),
fromRawEffect(T.succeed(schedule.step))
)[">=>"](retryLoop<RIn, RIn1, E, ROut>(self))
}