EdgeAIG's picture
download
raw
154 kB
/**
* Builds and wires services for Effect applications.
*
* A `Layer<ROut, E, RIn>` describes how to acquire one or more services, which
* services are required to build them, and which errors can occur during
* acquisition. Layers can manage scoped resources, memoize shared services,
* combine with other layers, provide services to effects or streams, and attach
* error handling, tracing, or lifecycle hooks.
*
* @since 2.0.0
*/
import type { NonEmptyArray, NonEmptyReadonlyArray } from "./Array.ts"
import type * as Cause from "./Cause.ts"
import type * as Channel from "./Channel.ts"
import * as Context from "./Context.ts"
import * as Deferred from "./Deferred.ts"
import type { Effect } from "./Effect.ts"
import type * as Exit from "./Exit.ts"
import type { LazyArg } from "./Function.ts"
import { constant, constTrue, constUndefined, dual, identity } from "./Function.ts"
import * as core from "./internal/core.ts"
import * as internalEffect from "./internal/effect.ts"
import type { ErrorWithStackTraceLimit } from "./internal/tracer.ts"
import * as internalTracer from "./internal/tracer.ts"
import { type Pipeable, pipeArguments } from "./Pipeable.ts"
import { hasProperty } from "./Predicate.ts"
import { CurrentStackFrame } from "./References.ts"
import * as Scope from "./Scope.ts"
import type * as Stream from "./Stream.ts"
import * as Tracer from "./Tracer.ts"
import type * as Types from "./Types.ts"
import type * as Unify from "./Unify.ts"
const TypeId = "~effect/Layer"
/**
* A `Layer` describes how to build one or more services for dependency injection.
*
* **When to use**
*
* Use to model construction of application services for dependency injection,
* especially when services have dependencies, can fail during construction, or
* need scoped setup and release.
*
* **Details**
*
* A `Layer<ROut, E, RIn>` represents `ROut` as the services this layer
* provides, `E` as the possible errors during layer construction, and `RIn` as
* the services this layer requires as dependencies.
*
* @category models
* @since 2.0.0
*/
export interface Layer<in ROut, out E = never, out RIn = never> extends Variance<ROut, E, RIn>, Pipeable {
/** @internal */
build(memoMap: MemoMap, scope: Scope.Scope): Effect<Context.Context<ROut>, E, RIn>
[Unify.typeSymbol]?: unknown
[Unify.unifySymbol]?: LayerUnify<this>
[Unify.ignoreSymbol]?: LayerUnifyIgnore
}
/**
* Type-level hook that allows `Layer` values to participate in `Unify`
* inference.
*
* **Details**
*
* This is used by Effect's pipe and unification machinery to preserve the
* provided services, error, and requirements of a `Layer`.
*
* @category models
* @since 4.0.0
*/
export interface LayerUnify<A extends { [Unify.typeSymbol]?: any }> {
Layer?: () => A[Unify.typeSymbol] extends Layer<any, any, any> | infer _ ? Layer<
Success<Extract<A[Unify.typeSymbol], Any>>,
Error<Extract<A[Unify.typeSymbol], Any>>,
Services<Extract<A[Unify.typeSymbol], Any>>
>
: never
}
/**
* Type-level marker used by `Unify` for `Layer` types that should be ignored
* during unification.
*
* @category models
* @since 4.0.0
*/
export interface LayerUnifyIgnore {}
/**
* The variance interface for Layer type parameters.
*
* @category models
* @since 2.0.0
*/
export interface Variance<in ROut, out E, out RIn> {
readonly [TypeId]: {
readonly _ROut: Types.Contravariant<ROut>
readonly _E: Types.Covariant<E>
readonly _RIn: Types.Covariant<RIn>
}
}
/**
* A type-level constraint for working with any `Layer` type.
*
* **When to use**
*
* Use to constrain generic parameters or layer collections to any `Layer`
* value while preserving its provided, error, and required service types for
* inference.
*
* **Details**
*
* This interface is used to constrain generic types to `Layer` values without
* specifying exact type parameters.
*
* @see {@link Layer} for the concrete layer interface
* @see {@link Services} for extracting required services from a layer type
* @see {@link Error} for extracting construction errors from a layer type
* @see {@link Success} for extracting provided services from a layer type
*
* @category utility types
* @since 3.9.0
*/
export interface Any {
readonly [TypeId]: {
readonly _ROut: any
readonly _E: any
readonly _RIn: any
}
}
/**
* Extracts the service requirements (`RIn`) from a `Layer` type.
*
* **When to use**
*
* Use to derive the dependency requirements of a generic or inferred `Layer`
* without restating its `RIn` type parameter.
*
* @see {@link Success} for extracting the services provided by the same `Layer`
* @see {@link Error} for extracting the construction failure type from the same `Layer`
*
* @category utility types
* @since 4.0.0
*/
export type Services<T extends Any> = T extends infer L
? L extends Layer<infer _ROut, infer _E, infer _RIn> ? _RIn : never
: never
/**
* Extracts the error type (`E`) from a `Layer` type.
*
* **When to use**
*
* Use to derive a layer construction error type for helper types, wrappers, or
* APIs that preserve a layer failure channel.
*
* @see {@link Success} for extracting the services provided by the same `Layer`
* @see {@link Services} for extracting the dependency requirements of the same `Layer`
*
* @category utility types
* @since 2.0.0
*/
export type Error<T extends Any> = T extends Layer<infer _ROut, infer _E, infer _RIn> ? _E : never
/**
* Extracts the service output type (`ROut`) from a `Layer` type.
*
* **When to use**
*
* Use to derive the services provided by an existing or generic `Layer` without
* restating its `ROut` type parameter.
*
* @see {@link Error} for extracting the layer construction error type instead
* @see {@link Services} for extracting the layer input service requirements instead
*
* @category utility types
* @since 2.0.0
*/
export type Success<T extends Any> = T extends Layer<infer _ROut, infer _E, infer _RIn> ? _ROut : never
const MemoMapTypeId = "~effect/Layer/MemoMap"
/**
* A `MemoMap` is used to memoize layer construction and ensure sharing of
* layers.
*
* **Details**
*
* The `MemoMap` prevents duplicate construction of the same layer instance,
* enabling efficient resource sharing across layer dependencies.
*
* **Example** (Sharing layer construction with a memo map)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* // Create a custom MemoMap for manual layer building
* const program = Effect.gen(function*() {
* const memoMap = yield* Layer.makeMemoMap
* const scope = yield* Effect.scope
*
* const dbLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
* })
* const context = yield* Layer.buildWithMemoMap(dbLayer, memoMap, scope)
*
* return Context.get(context, Database)
* })
* ```
*
* @category models
* @since 2.0.0
*/
export interface MemoMap {
readonly [MemoMapTypeId]: typeof MemoMapTypeId
readonly get: <RIn, E, ROut>(
layer: Layer<ROut, E, RIn>,
scope: Scope.Scope
) => Effect<Context.Context<ROut>, E, RIn> | undefined
readonly getOrElseMemoize: <RIn, E, ROut>(
layer: Layer<ROut, E, RIn>,
scope: Scope.Scope,
build: (memoMap: MemoMap, scope: Scope.Scope) => Effect<Context.Context<ROut>, E, RIn>
) => Effect<Context.Context<ROut>, E, RIn>
}
type MemoMapEntry = {
observers: number
effect: Effect<Context.Context<any>, any>
readonly finalizer: (exit: Exit.Exit<unknown, unknown>) => Effect<void>
}
const memoMapReuse = <RIn, E, ROut>(
entry: MemoMapEntry,
scope: Scope.Scope
): Effect<Context.Context<ROut>, E, RIn> => {
entry.observers++
return internalEffect.andThen(
internalEffect.scopeAddFinalizerExit(scope, (exit) => entry.finalizer(exit)),
entry.effect
)
}
/**
* Returns `true` if the specified value is a `Layer`, `false` otherwise.
*
* **Example** (Checking whether a value is a layer)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* const dbLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
* })
* const notALayer = { someProperty: "value" }
*
* console.log(Layer.isLayer(dbLayer)) // true
* console.log(Layer.isLayer(notALayer)) // false
* ```
*
* @category getters
* @since 2.0.0
*/
export const isLayer = (u: unknown): u is Layer<unknown, unknown, unknown> => hasProperty(u, TypeId)
const LayerProto = {
[TypeId]: {
_ROut: identity,
_E: identity,
_RIn: identity
},
pipe() {
return pipeArguments(this, arguments)
}
}
const fromBuildUnsafe = <ROut, E, RIn>(
build: (
memoMap: MemoMap,
scope: Scope.Scope
) => Effect<Context.Context<ROut>, E, RIn>
): Layer<ROut, E, RIn> => {
const self = Object.create(LayerProto)
self.build = build
return self
}
/**
* Constructs a `Layer` from a function that uses a `MemoMap` and `Scope` to
* build the layer.
*
* **Details**
*
* The function receives a `MemoMap` for memoization and a `Scope` for resource management.
* A child scope is created, and if the build fails, the child scope is closed.
*
* **Example** (Constructing a layer from a build function)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* const databaseLayer = Layer.fromBuild(() =>
* Effect.sync(() =>
* Context.make(Database, {
* query: (sql: string) => Effect.succeed("result")
* })
* )
* )
* ```
*
* @category constructors
* @since 4.0.0
*/
export const fromBuild = <ROut, E, RIn>(
build: (
memoMap: MemoMap,
scope: Scope.Scope
) => Effect<Context.Context<ROut>, E, RIn>
): Layer<ROut, E, RIn> =>
fromBuildUnsafe((memoMap: MemoMap, scope: Scope.Scope) => {
const layerScope = Scope.forkUnsafe(scope)
return internalEffect.onExit(
build(memoMap, layerScope),
(exit) => exit._tag === "Failure" ? Scope.close(layerScope, exit) : internalEffect.void
)
})
/**
* Constructs a `Layer` from a function that uses a `MemoMap` and `Scope` to
* build the layer, with automatic memoization.
*
* **Details**
*
* This is similar to `fromBuild` but provides automatic memoization of the layer construction.
* The layer will be memoized based on the provided `MemoMap`.
*
* **Example** (Memoizing layer construction)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* const databaseLayer = Layer.fromBuildMemo(() =>
* Effect.sync(() =>
* Context.make(Database, {
* query: (sql: string) => Effect.succeed("result")
* })
* )
* )
* ```
*
* @category constructors
* @since 4.0.0
*/
export const fromBuildMemo = <ROut, E, RIn>(
build: (
memoMap: MemoMap,
scope: Scope.Scope
) => Effect<Context.Context<ROut>, E, RIn>
): Layer<ROut, E, RIn> => {
const self: Layer<ROut, E, RIn> = fromBuild((memoMap, scope) => memoMap.getOrElseMemoize(self, scope, build))
return self
}
const memoMapBuild = <RIn, E, ROut>(
memoMap: MemoMapImpl,
layer: Layer<ROut, E, RIn>,
scope: Scope.Scope,
build: (memoMap: MemoMap, scope: Scope.Scope) => Effect<Context.Context<ROut>, E, RIn>
): Effect<Context.Context<ROut>, E, RIn> => {
const layerScope = Scope.makeUnsafe()
const deferred = Deferred.makeUnsafe<Context.Context<ROut>, E>()
const entry: MemoMapEntry = {
observers: 1,
effect: Deferred.await(deferred),
finalizer: (exit: Exit.Exit<unknown, unknown>) =>
internalEffect.suspend(() => {
entry.observers--
if (entry.observers === 0) {
memoMap.map.delete(layer)
return Scope.close(layerScope, exit)
}
return internalEffect.void
})
}
memoMap.map.set(layer, entry)
return internalEffect.scopeAddFinalizerExit(scope, entry.finalizer).pipe(
internalEffect.flatMap(() => build(memoMap, layerScope)),
internalEffect.onExit((exit) => {
entry.effect = exit
return Deferred.done(deferred, exit)
})
)
}
class MemoMapImpl implements MemoMap {
get [MemoMapTypeId](): typeof MemoMapTypeId {
return MemoMapTypeId
}
readonly parent: MemoMap | undefined
constructor(parent?: MemoMap) {
this.parent = parent
}
readonly map = new Map<Layer<any, any, any>, MemoMapEntry>()
get<RIn, E, ROut>(
layer: Layer<ROut, E, RIn>,
scope: Scope.Scope
): Effect<Context.Context<ROut>, E, RIn> | undefined {
const local = this.map.get(layer)
if (local) {
return memoMapReuse(local, scope)
}
return this.parent?.get(layer, scope)
}
getOrElseMemoize<RIn, E, ROut>(
layer: Layer<ROut, E, RIn>,
scope: Scope.Scope,
build: (memoMap: MemoMap, scope: Scope.Scope) => Effect<Context.Context<ROut>, E, RIn>
): Effect<Context.Context<ROut>, E, RIn> {
const existing = this.get(layer, scope)
if (existing) {
return existing
}
return memoMapBuild(this, layer, scope, build)
}
}
/**
* Constructs a `MemoMap` synchronously so it can be used to build additional layers.
*
* **Example** (Creating a memo map unsafely)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* // Create a memo map for manual layer building
* const program = Effect.gen(function*() {
* const memoMap = Layer.makeMemoMapUnsafe()
* const scope = yield* Effect.scope
*
* const dbLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
* })
* const context = yield* Layer.buildWithMemoMap(dbLayer, memoMap, scope)
*
* return Context.get(context, Database)
* })
* ```
*
* @category memo map
* @since 4.0.0
*/
export const makeMemoMapUnsafe = (): MemoMap => new MemoMapImpl()
/**
* Constructs a child `MemoMap` synchronously, allowing it to reuse layers
* already memoized in the parent while isolating any new layer allocations to
* the child map.
*
* **When to use**
*
* Use to synchronously fork a memo map for manual layer building when child
* builds should see parent memoized layers without writing newly built layers
* back to the parent.
*
* @see {@link forkMemoMap} for allocating the child memo map inside `Effect`
* @see {@link makeMemoMapUnsafe} for creating a root memo map without a parent
*
* @category memo map
* @since 4.0.0
*/
export const forkMemoMapUnsafe = (parent: MemoMap): MemoMap => new MemoMapImpl(parent)
/**
* Constructs a `MemoMap` effectfully so it can be used to build additional layers.
*
* **Example** (Creating a memo map in an effect)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* // Create a memo map safely within an Effect
* const program = Effect.gen(function*() {
* const memoMap = yield* Layer.makeMemoMap
* const scope = yield* Effect.scope
*
* const dbLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
* })
* const context = yield* Layer.buildWithMemoMap(dbLayer, memoMap, scope)
*
* return Context.get(context, Database)
* })
* ```
*
* @category memo map
* @since 2.0.0
*/
export const makeMemoMap: Effect<MemoMap> = internalEffect.sync(makeMemoMapUnsafe)
/**
* Constructs a child `MemoMap` effectfully, allowing it to reuse layers already
* memoized in the parent while isolating any new layer allocations to the child
* map.
*
* **When to use**
*
* Use when a layer build should inherit already memoized layers from an
* existing `MemoMap` while keeping newly memoized layers out of the parent map.
*
* @see {@link makeMemoMap} for creating a root memo map in an `Effect`
* @see {@link forkMemoMapUnsafe} for the synchronous constructor variant
* @see {@link buildWithMemoMap} for building layers with an explicit memo map
*
* @category memo map
* @since 4.0.0
*/
export const forkMemoMap = (parent: MemoMap): Effect<MemoMap> => internalEffect.sync(() => forkMemoMapUnsafe(parent))
/**
* Context service for the current `MemoMap` used in layer construction.
*
* **When to use**
*
* Use when building custom layer operations that need to access the current
* memoization map from the fiber context.
*
* **Details**
*
* This service wraps a `MemoMap` as a `Context.Service`, making it available
* for dependency injection during layer construction.
*
* @see {@link MemoMap} the memoization map type wrapped by this service
*
* @category models
* @since 3.13.0
*/
export class CurrentMemoMap extends Context.Service<CurrentMemoMap, MemoMap>()("effect/Layer/CurrentMemoMap") {
static getOrCreate: <Services>(self: Context.Context<Services>) => MemoMap = Context.getOrElse(
this,
makeMemoMapUnsafe
)
}
/**
* Builds a layer into an `Effect` value, using the specified `MemoMap` to memoize
* the layer construction.
*
* **Example** (Building layers with an explicit memo map)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* // Build layers with explicit memoization control
* const program = Effect.gen(function*() {
* const memoMap = yield* Layer.makeMemoMap
* const scope = yield* Effect.scope
*
* // Build database layer with memoization
* const dbLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
* })
* const dbContext = yield* Layer.buildWithMemoMap(dbLayer, memoMap, scope)
*
* // Build logger layer with same memoization (reuses memo if same layer)
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(msg)))
* })
* const loggerContext = yield* Layer.buildWithMemoMap(
* loggerLayer,
* memoMap,
* scope
* )
*
* return {
* database: Context.get(dbContext, Database),
* logger: Context.get(loggerContext, Logger)
* }
* })
* ```
*
* @category memo map
* @since 2.0.0
*/
export const buildWithMemoMap: {
/**
* Builds a layer into an `Effect` value, using the specified `MemoMap` to memoize
* the layer construction.
*
* **Example** (Building layers with an explicit memo map)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* // Build layers with explicit memoization control
* const program = Effect.gen(function*() {
* const memoMap = yield* Layer.makeMemoMap
* const scope = yield* Effect.scope
*
* // Build database layer with memoization
* const dbLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
* })
* const dbContext = yield* Layer.buildWithMemoMap(dbLayer, memoMap, scope)
*
* // Build logger layer with same memoization (reuses memo if same layer)
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(msg)))
* })
* const loggerContext = yield* Layer.buildWithMemoMap(
* loggerLayer,
* memoMap,
* scope
* )
*
* return {
* database: Context.get(dbContext, Database),
* logger: Context.get(loggerContext, Logger)
* }
* })
* ```
*
* @category memo map
* @since 2.0.0
*/
(memoMap: MemoMap, scope: Scope.Scope): <RIn, E, ROut>(self: Layer<ROut, E, RIn>) => Effect<Context.Context<ROut>, E, RIn>
/**
* Builds a layer into an `Effect` value, using the specified `MemoMap` to memoize
* the layer construction.
*
* **Example** (Building layers with an explicit memo map)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* // Build layers with explicit memoization control
* const program = Effect.gen(function*() {
* const memoMap = yield* Layer.makeMemoMap
* const scope = yield* Effect.scope
*
* // Build database layer with memoization
* const dbLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
* })
* const dbContext = yield* Layer.buildWithMemoMap(dbLayer, memoMap, scope)
*
* // Build logger layer with same memoization (reuses memo if same layer)
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(msg)))
* })
* const loggerContext = yield* Layer.buildWithMemoMap(
* loggerLayer,
* memoMap,
* scope
* )
*
* return {
* database: Context.get(dbContext, Database),
* logger: Context.get(loggerContext, Logger)
* }
* })
* ```
*
* @category memo map
* @since 2.0.0
*/
<RIn, E, ROut>(self: Layer<ROut, E, RIn>, memoMap: MemoMap, scope: Scope.Scope): Effect<Context.Context<ROut>, E, RIn>
} = dual(3, <RIn, E, ROut>(
self: Layer<ROut, E, RIn>,
memoMap: MemoMap,
scope: Scope.Scope
): Effect<Context.Context<ROut>, E, RIn> =>
internalEffect.provideService(
internalEffect.map(self.build(memoMap, scope), Context.add(CurrentMemoMap, memoMap)),
CurrentMemoMap,
memoMap
))
/**
* Builds a layer into a scoped value.
*
* **Example** (Building a layer into a context)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* // Build a layer to get its services
* const program = Effect.gen(function*() {
* const dbLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
* })
*
* // Build the layer into Context - automatically manages scope and memoization
* const context = yield* Layer.build(dbLayer)
*
* // Extract the specific service from the built layer
* const database = Context.get(context, Database)
*
* return yield* database.query("SELECT * FROM users")
* })
* ```
*
* @category destructors
* @since 2.0.0
*/
export const build = <RIn, E, ROut>(
self: Layer<ROut, E, RIn>
): Effect<Context.Context<ROut>, E, RIn | Scope.Scope> =>
core.withFiber((fiber) =>
buildWithMemoMap(
self,
CurrentMemoMap.getOrCreate(fiber.context),
Context.getUnsafe(fiber.context, Scope.Scope)
)
)
/**
* Builds a layer using an explicit scope.
*
* **When to use**
*
* Use to control the lifetime of layer resources with a scope supplied by the
* caller.
*
* **Details**
*
* Resources created by the layer are released when the supplied scope is
* closed, unless a resource extends its own scope.
*
* **Example** (Building a layer with an explicit scope)
*
* ```ts
* import { Context, Effect, Layer, Scope } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* // Build a layer with explicit scope control
* const program = Effect.gen(function*() {
* const scope = yield* Effect.scope
*
* const dbLayer = Layer.effect(Database, Effect.gen(function*() {
* console.log("Initializing database...")
* yield* Scope.addFinalizer(
* scope,
* Effect.sync(() => console.log("Database closed"))
* )
* return { query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result: ${sql}`)) }
* }))
*
* // Build with specific scope - resources tied to this scope
* const context = yield* Layer.buildWithScope(dbLayer, scope)
* const database = Context.get(context, Database)
*
* return yield* database.query("SELECT * FROM users")
* // Database will be closed when scope is closed
* })
* ```
*
* @category destructors
* @since 2.0.0
*/
export const buildWithScope: {
/**
* Builds a layer using an explicit scope.
*
* **When to use**
*
* Use to control the lifetime of layer resources with a scope supplied by the
* caller.
*
* **Details**
*
* Resources created by the layer are released when the supplied scope is
* closed, unless a resource extends its own scope.
*
* **Example** (Building a layer with an explicit scope)
*
* ```ts
* import { Context, Effect, Layer, Scope } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* // Build a layer with explicit scope control
* const program = Effect.gen(function*() {
* const scope = yield* Effect.scope
*
* const dbLayer = Layer.effect(Database, Effect.gen(function*() {
* console.log("Initializing database...")
* yield* Scope.addFinalizer(
* scope,
* Effect.sync(() => console.log("Database closed"))
* )
* return { query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result: ${sql}`)) }
* }))
*
* // Build with specific scope - resources tied to this scope
* const context = yield* Layer.buildWithScope(dbLayer, scope)
* const database = Context.get(context, Database)
*
* return yield* database.query("SELECT * FROM users")
* // Database will be closed when scope is closed
* })
* ```
*
* @category destructors
* @since 2.0.0
*/
(scope: Scope.Scope): <RIn, E, ROut>(self: Layer<ROut, E, RIn>) => Effect<Context.Context<ROut>, E, RIn>
/**
* Builds a layer using an explicit scope.
*
* **When to use**
*
* Use to control the lifetime of layer resources with a scope supplied by the
* caller.
*
* **Details**
*
* Resources created by the layer are released when the supplied scope is
* closed, unless a resource extends its own scope.
*
* **Example** (Building a layer with an explicit scope)
*
* ```ts
* import { Context, Effect, Layer, Scope } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* // Build a layer with explicit scope control
* const program = Effect.gen(function*() {
* const scope = yield* Effect.scope
*
* const dbLayer = Layer.effect(Database, Effect.gen(function*() {
* console.log("Initializing database...")
* yield* Scope.addFinalizer(
* scope,
* Effect.sync(() => console.log("Database closed"))
* )
* return { query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result: ${sql}`)) }
* }))
*
* // Build with specific scope - resources tied to this scope
* const context = yield* Layer.buildWithScope(dbLayer, scope)
* const database = Context.get(context, Database)
*
* return yield* database.query("SELECT * FROM users")
* // Database will be closed when scope is closed
* })
* ```
*
* @category destructors
* @since 2.0.0
*/
<RIn, E, ROut>(self: Layer<ROut, E, RIn>, scope: Scope.Scope): Effect<Context.Context<ROut>, E, RIn>
} = dual(2, <RIn, E, ROut>(
self: Layer<ROut, E, RIn>,
scope: Scope.Scope
): Effect<Context.Context<ROut>, E, RIn> =>
core.withFiber((fiber) =>
buildWithMemoMap(
self,
CurrentMemoMap.getOrCreate(fiber.context),
scope
)
))
/**
* Constructs a layer that provides a single service from an already available
* value.
*
* **When to use**
*
* Use when you need a `Layer` that provides a service from an already
* constructed implementation without effectful acquisition.
*
* **Example** (Creating a layer from a service implementation)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* const DatabaseLive = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Query result: ${sql}`))
* })
* ```
*
* @see {@link sync} for constructing layers from lazy values
*
* @category constructors
* @since 2.0.0
*/
export const succeed: {
/**
* Constructs a layer that provides a single service from an already available
* value.
*
* **When to use**
*
* Use when you need a `Layer` that provides a service from an already
* constructed implementation without effectful acquisition.
*
* **Example** (Creating a layer from a service implementation)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* const DatabaseLive = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Query result: ${sql}`))
* })
* ```
*
* @see {@link sync} for constructing layers from lazy values
*
* @category constructors
* @since 2.0.0
*/
<I, S>(service: Context.Key<I, S>): (resource: S) => Layer<I>
/**
* Constructs a layer that provides a single service from an already available
* value.
*
* **When to use**
*
* Use when you need a `Layer` that provides a service from an already
* constructed implementation without effectful acquisition.
*
* **Example** (Creating a layer from a service implementation)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* const DatabaseLive = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Query result: ${sql}`))
* })
* ```
*
* @see {@link sync} for constructing layers from lazy values
*
* @category constructors
* @since 2.0.0
*/
<I, S>(service: Context.Key<I, S>, resource: Types.NoInfer<S>): Layer<I>
} = function() {
if (arguments.length === 1) {
return (resource: any) => succeedContext(Context.make(arguments[0], resource))
}
return succeedContext(Context.make(arguments[0], arguments[1]))
} as any
/**
* Constructs a layer that provides all services in an already available
* `Context`.
*
* **When to use**
*
* Use when you need a `Layer` built from an existing `Context`, including when
* you need to provide multiple services at once.
*
* **Details**
*
* This is a more general version of `succeed` that allows you to provide
* multiple services at once through a `Context`.
*
* **Example** (Providing multiple services from a context)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* const context = Context.make(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
* }).pipe(
* Context.add(Logger, {
* log: (msg: string) => Effect.sync(() => console.log(msg))
* })
* )
*
* const layer = Layer.succeedContext(context)
* ```
*
* @see {@link succeed} for providing a single service from a value
*
* @category constructors
* @since 2.0.0
*/
export const succeedContext = <A>(context: Context.Context<A>): Layer<A> =>
fromBuildUnsafe(constant(internalEffect.succeed(context)))
/**
* An empty layer that provides no services, cannot fail, has no requirements,
* and performs no construction or finalization work.
*
* **When to use**
*
* Use as the no-op branch when conditionally composing layers.
*
* **Example** (Disabling optional lifecycle work)
*
* ```ts
* import { Console, Layer } from "effect"
*
* declare const flag: boolean
*
* const StartupLogLive = flag
* ? Layer.effectDiscard(Console.log("application starting"))
* : Layer.empty
* ```
*
* @see {@link effectDiscard} for running an effect while providing no services
*
* @category constructors
* @since 2.0.0
*/
export const empty: Layer<never> = succeedContext(Context.empty())
/**
* Constructs a layer lazily that provides a single service.
*
* **When to use**
*
* Use when you need a `Layer` that provides one service whose value is created
* synchronously, but creation should be deferred until the layer is built.
*
* **Details**
*
* This is a lazy version of `succeed` where the service value is computed
* synchronously only when the layer is built.
*
* **Example** (Lazily providing a service)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* const layer = Layer.sync(Database, () => ({
* query: (sql: string) => Effect.succeed(`Query: ${sql}`)
* }))
* ```
*
* @see {@link succeed} for constructing layers from static values
*
* @category constructors
* @since 2.0.0
*/
export const sync: {
/**
* Constructs a layer lazily that provides a single service.
*
* **When to use**
*
* Use when you need a `Layer` that provides one service whose value is created
* synchronously, but creation should be deferred until the layer is built.
*
* **Details**
*
* This is a lazy version of `succeed` where the service value is computed
* synchronously only when the layer is built.
*
* **Example** (Lazily providing a service)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* const layer = Layer.sync(Database, () => ({
* query: (sql: string) => Effect.succeed(`Query: ${sql}`)
* }))
* ```
*
* @see {@link succeed} for constructing layers from static values
*
* @category constructors
* @since 2.0.0
*/
<I, S>(service: Context.Key<I, S>): (evaluate: LazyArg<S>) => Layer<I>
/**
* Constructs a layer lazily that provides a single service.
*
* **When to use**
*
* Use when you need a `Layer` that provides one service whose value is created
* synchronously, but creation should be deferred until the layer is built.
*
* **Details**
*
* This is a lazy version of `succeed` where the service value is computed
* synchronously only when the layer is built.
*
* **Example** (Lazily providing a service)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* const layer = Layer.sync(Database, () => ({
* query: (sql: string) => Effect.succeed(`Query: ${sql}`)
* }))
* ```
*
* @see {@link succeed} for constructing layers from static values
*
* @category constructors
* @since 2.0.0
*/
<I, S>(service: Context.Key<I, S>, evaluate: LazyArg<Types.NoInfer<S>>): Layer<I>
} = function() {
if (arguments.length === 1) {
return (evaluate: LazyArg<any>) => syncContext(() => Context.make(arguments[0], evaluate()))
}
return syncContext(() => Context.make(arguments[0], arguments[1]()))
} as any
/**
* Constructs a layer lazily that provides all services in a `Context`.
*
* **When to use**
*
* Use when you need a `Layer` that creates multiple services synchronously but
* defers that work until the layer is built.
*
* **Details**
*
* This is a lazy version of `succeedContext` where the `Context` is computed
* synchronously only when the layer is built.
*
* **Example** (Lazily providing a context)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* const layer = Layer.syncContext(() =>
* Context.make(Database, {
* query: (sql: string) => Effect.succeed(`Query: ${sql}`)
* })
* )
* ```
*
* @see {@link sync} for lazily providing a single service
* @see {@link succeedContext} for providing an already available context
*
* @category constructors
* @since 2.0.0
*/
export const syncContext = <A>(evaluate: LazyArg<Context.Context<A>>): Layer<A> =>
fromBuildMemo(constant(internalEffect.sync(evaluate)))
/**
* Constructs a layer from an effect that produces a single service.
*
* **When to use**
*
* Use when you need to construct a `Layer`-provided service with an `Effect`,
* dependencies, or scoped resource acquisition.
*
* **Details**
*
* This allows you to create a `Layer` from an `Effect` that produces a service.
* The `Effect` is executed in the scope of the layer, allowing for proper
* resource management.
*
* **Example** (Creating a layer from an effect)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* const layer = Layer.effect(Database,
* Effect.sync(() => ({
* query: (sql: string) => Effect.succeed(`Query: ${sql}`)
* }))
* )
* ```
*
* @see {@link effectContext} for effectfully providing multiple services
* @see {@link effectDiscard} for running construction work without providing services
*
* @category constructors
* @since 2.0.0
*/
export const effect: {
/**
* Constructs a layer from an effect that produces a single service.
*
* **When to use**
*
* Use when you need to construct a `Layer`-provided service with an `Effect`,
* dependencies, or scoped resource acquisition.
*
* **Details**
*
* This allows you to create a `Layer` from an `Effect` that produces a service.
* The `Effect` is executed in the scope of the layer, allowing for proper
* resource management.
*
* **Example** (Creating a layer from an effect)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* const layer = Layer.effect(Database,
* Effect.sync(() => ({
* query: (sql: string) => Effect.succeed(`Query: ${sql}`)
* }))
* )
* ```
*
* @see {@link effectContext} for effectfully providing multiple services
* @see {@link effectDiscard} for running construction work without providing services
*
* @category constructors
* @since 2.0.0
*/
<I, S>(service: Context.Key<I, S>): <E, R>(
effect: Effect<S, E, R>
) => Layer<I, E, Exclude<R, Scope.Scope>>
/**
* Constructs a layer from an effect that produces a single service.
*
* **When to use**
*
* Use when you need to construct a `Layer`-provided service with an `Effect`,
* dependencies, or scoped resource acquisition.
*
* **Details**
*
* This allows you to create a `Layer` from an `Effect` that produces a service.
* The `Effect` is executed in the scope of the layer, allowing for proper
* resource management.
*
* **Example** (Creating a layer from an effect)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* const layer = Layer.effect(Database,
* Effect.sync(() => ({
* query: (sql: string) => Effect.succeed(`Query: ${sql}`)
* }))
* )
* ```
*
* @see {@link effectContext} for effectfully providing multiple services
* @see {@link effectDiscard} for running construction work without providing services
*
* @category constructors
* @since 2.0.0
*/
<I, S, E, R>(service: Context.Key<I, S>, effect: Effect<Types.NoInfer<S>, E, R>): Layer<I, E, Exclude<R, Scope.Scope>>
} = function() {
if (arguments.length === 1) {
return (effect: any) => effectImpl(arguments[0], effect)
}
return effectImpl(arguments[0], arguments[1])
} as any
const effectImpl = <I, S, E, R>(
service: Context.Key<I, S>,
effect: Effect<S, E, R>
): Layer<I, E, Exclude<R, Scope.Scope>> =>
effectContext(internalEffect.map(effect, (value) => Context.make(service, value)))
/**
* Constructs a layer from an effect that produces all services in a `Context`.
*
* **When to use**
*
* Use when you need a `Layer` that effectfully constructs a `Context` with
* multiple services.
*
* **Details**
*
* This allows you to create a `Layer` from an effectful computation that
* returns multiple services. The `Effect` is executed in the scope of the
* layer.
*
* **Example** (Creating a layer from an effectful context)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<
* Database,
* { readonly query: (sql: string) => Effect.Effect<string> }
* >()("Database") {}
*
* const layer = Layer.effectContext(
* Effect.succeed(Context.make(Database, {
* query: (sql: string) => Effect.succeed(`Query: ${sql}`)
* }))
* )
* ```
*
* @see {@link effect} for effectfully providing a single service
*
* @category constructors
* @since 2.0.0
*/
export const effectContext = <A, E, R>(
effect: Effect<Context.Context<A>, E, R>
): Layer<A, E, Exclude<R, Scope.Scope>> => fromBuildMemo((_, scope) => Scope.provide(effect, scope))
/**
* Constructs a layer from an effect, discarding its value and providing no
* services.
*
* **When to use**
*
* Use when layer construction should run an Effect for its side effects while providing no
* services.
*
* **Example** (Running an effect during layer construction)
*
* ```ts
* import { Effect, Layer } from "effect"
*
* const initLayer = Layer.effectDiscard(
* Effect.sync(() => {
* console.log("Initializing application...")
* })
* )
* ```
*
* @see {@link empty} for a no-op layer that performs no construction work
*
* @category constructors
* @since 2.0.0
*/
export const effectDiscard = <X, E, R>(effect: Effect<X, E, R>): Layer<never, E, Exclude<R, Scope.Scope>> =>
effectContext(internalEffect.as(effect, Context.empty()))
/**
* Constructs a layer lazily using the specified factory.
*
* **Details**
*
* The factory is evaluated only when the suspended layer is first built, and
* the result is memoized with normal layer sharing semantics.
*
* **Example** (Choosing a layer lazily)
*
* ```ts
* import { Context, Layer } from "effect"
*
* class Config extends Context.Service<Config, string>()("Config") {}
*
* const useProd = true
*
* const layer = Layer.suspend(() =>
* useProd
* ? Layer.succeed(Config, "https://api.example.com")
* : Layer.succeed(Config, "http://localhost:3000")
* )
* ```
*
* @category constructors
* @since 2.0.0
*/
export const suspend = <A, E, R>(evaluate: LazyArg<Layer<A, E, R>>): Layer<A, E, R> =>
fromBuildMemo((memoMap, scope) => internalEffect.suspend(() => evaluate().build(memoMap, scope)))
/**
* Unwraps a `Layer` from an `Effect`, flattening the nested structure.
*
* **When to use**
*
* Use when you have an `Effect` that produces a `Layer` and you want to
* use that layer directly.
*
* **Details**
*
* The resulting Layer will have the combined error and dependency types from
* both the outer Effect and the inner Layer.
*
* **Example** (Unwrapping an effectful layer)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* const layerEffect = Effect.succeed(
* Layer.succeed(Database, { query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result")) })
* )
*
* const unwrappedLayer = Layer.unwrap(layerEffect)
* ```
*
* @category converting
* @since 4.0.0
*/
export const unwrap = <A, E1, R1, E, R>(
self: Effect<Layer<A, E1, R1>, E, R>
): Layer<A, E | E1, R1 | Exclude<R, Scope.Scope>> => {
const service = Context.Service<Layer<A, E1, R1>>("effect/Layer/unwrap")
return flatMap(effect(service)(self), Context.get(service))
}
const mergeAllEffect = <Layers extends [Layer<never, any, any>, ...Array<Layer<never, any, any>>]>(
layers: Layers,
memoMap: MemoMap,
scope: Scope.Scope
): Effect<
Context.Context<{ [k in keyof Layers]: Success<Layers[k]> }[number]>,
{ [k in keyof Layers]: Error<Layers[k]> }[number],
{ [k in keyof Layers]: Services<Layers[k]> }[number]
> => {
const parentScope = Scope.forkUnsafe(scope, "parallel")
return internalEffect.forEach(layers, (layer) => layer.build(memoMap, Scope.forkUnsafe(parentScope, "sequential")), {
concurrency: layers.length
}).pipe(
internalEffect.map((context) => Context.mergeAll(...(context as any)))
)
}
/**
* Combines all the provided layers concurrently, creating a new layer with
* merged input, error, and output types.
*
* **When to use**
*
* Use when you need to combine multiple independent layers.
*
* **Details**
*
* All layers are built concurrently, and their outputs are merged into a single layer.
*
* If multiple merged layers depend on the same layer value, that dependency is
* shared by default. Reuse a named layer value when you want services to share
* the same resource, such as one database pool.
*
* **Example** (Merging independent layers)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* const dbLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
* })
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(msg)))
* })
*
* const mergedLayer = Layer.mergeAll(dbLayer, loggerLayer)
* ```
*
* @see {@link merge} for merging one layer with another layer or array
*
* @category zipping
* @since 2.0.0
*/
export const mergeAll = <Layers extends [Layer<never, any, any>, ...Array<Layer<never, any, any>>]>(
...layers: Layers
): Layer<
Success<Layers[number]>,
Error<Layers[number]>,
Services<Layers[number]>
> => fromBuild((memoMap, scope) => mergeAllEffect(layers, memoMap, scope))
/**
* Merges this layer with another layer concurrently, producing a new layer with
* combined input, error, and output types.
*
* **When to use**
*
* Use to combine an existing `Layer` with another `Layer` or an array of
* layers while preserving pipeline style.
*
* **Details**
*
* This is a binary version of `mergeAll` that merges exactly two layers or one
* layer with an array of layers. The layers are built concurrently and their
* outputs are combined.
*
* **Example** (Merging two layers)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* const dbLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
* })
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(msg)))
* })
*
* const mergedLayer = Layer.merge(dbLayer, loggerLayer)
* ```
*
* @see {@link mergeAll} for merging several layers at once
*
* @category zipping
* @since 2.0.0
*/
export const merge: {
/**
* Merges this layer with another layer concurrently, producing a new layer with
* combined input, error, and output types.
*
* **When to use**
*
* Use to combine an existing `Layer` with another `Layer` or an array of
* layers while preserving pipeline style.
*
* **Details**
*
* This is a binary version of `mergeAll` that merges exactly two layers or one
* layer with an array of layers. The layers are built concurrently and their
* outputs are combined.
*
* **Example** (Merging two layers)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* const dbLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
* })
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(msg)))
* })
*
* const mergedLayer = Layer.merge(dbLayer, loggerLayer)
* ```
*
* @see {@link mergeAll} for merging several layers at once
*
* @category zipping
* @since 2.0.0
*/
<RIn, E, ROut>(that: Layer<ROut, E, RIn>): <RIn2, E2, ROut2>(self: Layer<ROut2, E2, RIn2>) => Layer<ROut | ROut2, E | E2, RIn | RIn2>
/**
* Merges this layer with another layer concurrently, producing a new layer with
* combined input, error, and output types.
*
* **When to use**
*
* Use to combine an existing `Layer` with another `Layer` or an array of
* layers while preserving pipeline style.
*
* **Details**
*
* This is a binary version of `mergeAll` that merges exactly two layers or one
* layer with an array of layers. The layers are built concurrently and their
* outputs are combined.
*
* **Example** (Merging two layers)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* const dbLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
* })
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(msg)))
* })
*
* const mergedLayer = Layer.merge(dbLayer, loggerLayer)
* ```
*
* @see {@link mergeAll} for merging several layers at once
*
* @category zipping
* @since 2.0.0
*/
<const Layers extends [Any, ...Array<Any>]>(that: Layers): <A, E, R>(
self: Layer<A, E, R>
) => Layer<
A | Success<Layers[number]>,
E | Error<Layers[number]>,
| Services<Layers[number]>
| R
>
/**
* Merges this layer with another layer concurrently, producing a new layer with
* combined input, error, and output types.
*
* **When to use**
*
* Use to combine an existing `Layer` with another `Layer` or an array of
* layers while preserving pipeline style.
*
* **Details**
*
* This is a binary version of `mergeAll` that merges exactly two layers or one
* layer with an array of layers. The layers are built concurrently and their
* outputs are combined.
*
* **Example** (Merging two layers)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* const dbLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
* })
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(msg)))
* })
*
* const mergedLayer = Layer.merge(dbLayer, loggerLayer)
* ```
*
* @see {@link mergeAll} for merging several layers at once
*
* @category zipping
* @since 2.0.0
*/
<RIn2, E2, ROut2, RIn, E, ROut>(self: Layer<ROut2, E2, RIn2>, that: Layer<ROut, E, RIn>): Layer<ROut | ROut2, E | E2, RIn | RIn2>
/**
* Merges this layer with another layer concurrently, producing a new layer with
* combined input, error, and output types.
*
* **When to use**
*
* Use to combine an existing `Layer` with another `Layer` or an array of
* layers while preserving pipeline style.
*
* **Details**
*
* This is a binary version of `mergeAll` that merges exactly two layers or one
* layer with an array of layers. The layers are built concurrently and their
* outputs are combined.
*
* **Example** (Merging two layers)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* const dbLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed("result"))
* })
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(msg)))
* })
*
* const mergedLayer = Layer.merge(dbLayer, loggerLayer)
* ```
*
* @see {@link mergeAll} for merging several layers at once
*
* @category zipping
* @since 2.0.0
*/
<A, E, R, const Layers extends [Any, ...Array<Any>]>(self: Layer<A, E, R>, that: Layers): Layer<
A | Success<Layers[number]>,
E | Error<Layers[number]>,
| Services<Layers[number]>
| R
>
} = dual(2, (
self: Layer<any, any, any>,
that: Layer<any, any, any> | ReadonlyArray<Layer<any, any, any>>
) => mergeAll(self, ...(Array.isArray(that) ? that : [that])))
const provideWith = (
self: Layer<any, any, any>,
that: Layer<any, any, any> | ReadonlyArray<Layer<any, any, any>>,
f: (
selfContext: Context.Context<any>,
thatContext: Context.Context<any>
) => Context.Context<any>
) =>
fromBuild((memoMap, scope) =>
internalEffect.flatMap(
Array.isArray(that)
? mergeAllEffect(that as NonEmptyArray<Layer<any, any, any>>, memoMap, scope)
: (that as Layer<any, any, any>).build(memoMap, scope),
(context) =>
self.build(memoMap, scope).pipe(
internalEffect.provideContext(context),
internalEffect.map((merged) => f(merged, context))
)
)
)
/**
* Feeds the output services of the dependency layer into the requirements of
* this layer, returning a layer that only provides the services from this layer.
*
* **When to use**
*
* Use when you need to hide an implementation dependency layer from callers.
*
* **Details**
*
* In `serviceLayer.pipe(Layer.provide(dependencyLayer))`, the dependency layer is
* built first and is used to satisfy the requirements of `serviceLayer`.
*
* **Example** (Providing layer dependencies)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class UserService extends Context.Service<UserService, {
* readonly getUser: (id: string) => Effect.Effect<{
* id: string
* name: string
* }>
* }>()("UserService") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* // Create dependency layers
* const databaseLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
* })
*
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(`[LOG] ${msg}`)))
* })
*
* // UserService depends on Database and Logger
* const userServiceLayer = Layer.effect(UserService, Effect.gen(function*() {
* const database = yield* Database
* const logger = yield* Logger
*
* return {
* getUser: Effect.fn("UserService.getUser")(function*(id: string) {
* yield* logger.log(`Looking up user ${id}`)
* const result = yield* database.query(
* `SELECT * FROM users WHERE id = ${id}`
* )
* return { id, name: result }
* })
* }
* }))
*
* // Provide dependencies to UserService layer
* const userServiceWithDependencies = userServiceLayer.pipe(
* Layer.provide(Layer.mergeAll(databaseLayer, loggerLayer))
* )
*
* // Now UserService layer has no dependencies
* const program = Effect.gen(function*() {
* const userService = yield* UserService
* return yield* userService.getUser("123")
* }).pipe(
* Effect.provide(userServiceWithDependencies)
* )
* ```
*
* @see {@link provideMerge} for retaining the dependency services
*
* @category providing services
* @since 2.0.0
*/
export const provide: {
/**
* Feeds the output services of the dependency layer into the requirements of
* this layer, returning a layer that only provides the services from this layer.
*
* **When to use**
*
* Use when you need to hide an implementation dependency layer from callers.
*
* **Details**
*
* In `serviceLayer.pipe(Layer.provide(dependencyLayer))`, the dependency layer is
* built first and is used to satisfy the requirements of `serviceLayer`.
*
* **Example** (Providing layer dependencies)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class UserService extends Context.Service<UserService, {
* readonly getUser: (id: string) => Effect.Effect<{
* id: string
* name: string
* }>
* }>()("UserService") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* // Create dependency layers
* const databaseLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
* })
*
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(`[LOG] ${msg}`)))
* })
*
* // UserService depends on Database and Logger
* const userServiceLayer = Layer.effect(UserService, Effect.gen(function*() {
* const database = yield* Database
* const logger = yield* Logger
*
* return {
* getUser: Effect.fn("UserService.getUser")(function*(id: string) {
* yield* logger.log(`Looking up user ${id}`)
* const result = yield* database.query(
* `SELECT * FROM users WHERE id = ${id}`
* )
* return { id, name: result }
* })
* }
* }))
*
* // Provide dependencies to UserService layer
* const userServiceWithDependencies = userServiceLayer.pipe(
* Layer.provide(Layer.mergeAll(databaseLayer, loggerLayer))
* )
*
* // Now UserService layer has no dependencies
* const program = Effect.gen(function*() {
* const userService = yield* UserService
* return yield* userService.getUser("123")
* }).pipe(
* Effect.provide(userServiceWithDependencies)
* )
* ```
*
* @see {@link provideMerge} for retaining the dependency services
*
* @category providing services
* @since 2.0.0
*/
<RIn, E, ROut>(that: Layer<ROut, E, RIn>): <RIn2, E2, ROut2>(self: Layer<ROut2, E2, RIn2>) => Layer<ROut2, E | E2, RIn | Exclude<RIn2, ROut>>
/**
* Feeds the output services of the dependency layer into the requirements of
* this layer, returning a layer that only provides the services from this layer.
*
* **When to use**
*
* Use when you need to hide an implementation dependency layer from callers.
*
* **Details**
*
* In `serviceLayer.pipe(Layer.provide(dependencyLayer))`, the dependency layer is
* built first and is used to satisfy the requirements of `serviceLayer`.
*
* **Example** (Providing layer dependencies)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class UserService extends Context.Service<UserService, {
* readonly getUser: (id: string) => Effect.Effect<{
* id: string
* name: string
* }>
* }>()("UserService") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* // Create dependency layers
* const databaseLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
* })
*
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(`[LOG] ${msg}`)))
* })
*
* // UserService depends on Database and Logger
* const userServiceLayer = Layer.effect(UserService, Effect.gen(function*() {
* const database = yield* Database
* const logger = yield* Logger
*
* return {
* getUser: Effect.fn("UserService.getUser")(function*(id: string) {
* yield* logger.log(`Looking up user ${id}`)
* const result = yield* database.query(
* `SELECT * FROM users WHERE id = ${id}`
* )
* return { id, name: result }
* })
* }
* }))
*
* // Provide dependencies to UserService layer
* const userServiceWithDependencies = userServiceLayer.pipe(
* Layer.provide(Layer.mergeAll(databaseLayer, loggerLayer))
* )
*
* // Now UserService layer has no dependencies
* const program = Effect.gen(function*() {
* const userService = yield* UserService
* return yield* userService.getUser("123")
* }).pipe(
* Effect.provide(userServiceWithDependencies)
* )
* ```
*
* @see {@link provideMerge} for retaining the dependency services
*
* @category providing services
* @since 2.0.0
*/
<const Layers extends [Any, ...Array<Any>]>(that: Layers): <A, E, R>(
self: Layer<A, E, R>
) => Layer<
A,
E | Error<Layers[number]>,
| Services<Layers[number]>
| Exclude<R, Success<Layers[number]>>
>
/**
* Feeds the output services of the dependency layer into the requirements of
* this layer, returning a layer that only provides the services from this layer.
*
* **When to use**
*
* Use when you need to hide an implementation dependency layer from callers.
*
* **Details**
*
* In `serviceLayer.pipe(Layer.provide(dependencyLayer))`, the dependency layer is
* built first and is used to satisfy the requirements of `serviceLayer`.
*
* **Example** (Providing layer dependencies)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class UserService extends Context.Service<UserService, {
* readonly getUser: (id: string) => Effect.Effect<{
* id: string
* name: string
* }>
* }>()("UserService") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* // Create dependency layers
* const databaseLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
* })
*
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(`[LOG] ${msg}`)))
* })
*
* // UserService depends on Database and Logger
* const userServiceLayer = Layer.effect(UserService, Effect.gen(function*() {
* const database = yield* Database
* const logger = yield* Logger
*
* return {
* getUser: Effect.fn("UserService.getUser")(function*(id: string) {
* yield* logger.log(`Looking up user ${id}`)
* const result = yield* database.query(
* `SELECT * FROM users WHERE id = ${id}`
* )
* return { id, name: result }
* })
* }
* }))
*
* // Provide dependencies to UserService layer
* const userServiceWithDependencies = userServiceLayer.pipe(
* Layer.provide(Layer.mergeAll(databaseLayer, loggerLayer))
* )
*
* // Now UserService layer has no dependencies
* const program = Effect.gen(function*() {
* const userService = yield* UserService
* return yield* userService.getUser("123")
* }).pipe(
* Effect.provide(userServiceWithDependencies)
* )
* ```
*
* @see {@link provideMerge} for retaining the dependency services
*
* @category providing services
* @since 2.0.0
*/
<RIn2, E2, ROut2, RIn, E, ROut>(self: Layer<ROut2, E2, RIn2>, that: Layer<ROut, E, RIn>): Layer<ROut2, E | E2, RIn | Exclude<RIn2, ROut>>
/**
* Feeds the output services of the dependency layer into the requirements of
* this layer, returning a layer that only provides the services from this layer.
*
* **When to use**
*
* Use when you need to hide an implementation dependency layer from callers.
*
* **Details**
*
* In `serviceLayer.pipe(Layer.provide(dependencyLayer))`, the dependency layer is
* built first and is used to satisfy the requirements of `serviceLayer`.
*
* **Example** (Providing layer dependencies)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class UserService extends Context.Service<UserService, {
* readonly getUser: (id: string) => Effect.Effect<{
* id: string
* name: string
* }>
* }>()("UserService") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* // Create dependency layers
* const databaseLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
* })
*
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(`[LOG] ${msg}`)))
* })
*
* // UserService depends on Database and Logger
* const userServiceLayer = Layer.effect(UserService, Effect.gen(function*() {
* const database = yield* Database
* const logger = yield* Logger
*
* return {
* getUser: Effect.fn("UserService.getUser")(function*(id: string) {
* yield* logger.log(`Looking up user ${id}`)
* const result = yield* database.query(
* `SELECT * FROM users WHERE id = ${id}`
* )
* return { id, name: result }
* })
* }
* }))
*
* // Provide dependencies to UserService layer
* const userServiceWithDependencies = userServiceLayer.pipe(
* Layer.provide(Layer.mergeAll(databaseLayer, loggerLayer))
* )
*
* // Now UserService layer has no dependencies
* const program = Effect.gen(function*() {
* const userService = yield* UserService
* return yield* userService.getUser("123")
* }).pipe(
* Effect.provide(userServiceWithDependencies)
* )
* ```
*
* @see {@link provideMerge} for retaining the dependency services
*
* @category providing services
* @since 2.0.0
*/
<A, E, R, const Layers extends [Any, ...Array<Any>]>(self: Layer<A, E, R>, that: Layers): Layer<
A,
E | Error<Layers[number]>,
| Services<Layers[number]>
| Exclude<R, Success<Layers[number]>>
>
} = dual(2, (
self: Layer<any, any, any>,
that: Layer<any, any, any> | ReadonlyArray<Layer<any, any, any>>
) => provideWith(self, that, identity))
/**
* Feeds the output services of the dependency layer into the requirements of
* this layer, returning a layer that provides both sets of services.
*
* **When to use**
*
* Use when you need to compose `Layer`s while keeping both the constructed
* service and the dependency used to build it available.
*
* **Details**
*
* Prefer {@link provide} when the dependency should stay private.
*
* **Example** (Providing dependencies while retaining services)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* class UserService extends Context.Service<UserService, {
* readonly getUser: (id: string) => Effect.Effect<{
* id: string
* name: string
* }>
* }>()("UserService") {}
*
* // Create dependency layers
* const databaseLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
* })
*
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(`[LOG] ${msg}`)))
* })
*
* // UserService depends on Database and Logger
* const userServiceLayer = Layer.effect(UserService, Effect.gen(function*() {
* const database = yield* Database
* const logger = yield* Logger
*
* return {
* getUser: Effect.fn("UserService.getUser")(function*(id: string) {
* yield* logger.log(`Looking up user ${id}`)
* const result = yield* database.query(
* `SELECT * FROM users WHERE id = ${id}`
* )
* return { id, name: result }
* })
* }
* }))
*
* // Provide dependencies and merge all services together
* const allServicesLayer = userServiceLayer.pipe(
* Layer.provideMerge(Layer.mergeAll(databaseLayer, loggerLayer))
* )
*
* // Now the resulting layer provides UserService, Database, AND Logger
* const program = Effect.gen(function*() {
* const userService = yield* UserService
* const logger = yield* Logger // Still available!
* const database = yield* Database // Still available!
*
* const user = yield* userService.getUser("123")
* yield* logger.log(`Found user: ${user.name}`)
*
* return user
* }).pipe(
* Effect.provide(allServicesLayer)
* )
* ```
*
* @see {@link provide} for keeping dependency services private
*
* @category providing services
* @since 2.0.0
*/
export const provideMerge: {
/**
* Feeds the output services of the dependency layer into the requirements of
* this layer, returning a layer that provides both sets of services.
*
* **When to use**
*
* Use when you need to compose `Layer`s while keeping both the constructed
* service and the dependency used to build it available.
*
* **Details**
*
* Prefer {@link provide} when the dependency should stay private.
*
* **Example** (Providing dependencies while retaining services)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* class UserService extends Context.Service<UserService, {
* readonly getUser: (id: string) => Effect.Effect<{
* id: string
* name: string
* }>
* }>()("UserService") {}
*
* // Create dependency layers
* const databaseLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
* })
*
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(`[LOG] ${msg}`)))
* })
*
* // UserService depends on Database and Logger
* const userServiceLayer = Layer.effect(UserService, Effect.gen(function*() {
* const database = yield* Database
* const logger = yield* Logger
*
* return {
* getUser: Effect.fn("UserService.getUser")(function*(id: string) {
* yield* logger.log(`Looking up user ${id}`)
* const result = yield* database.query(
* `SELECT * FROM users WHERE id = ${id}`
* )
* return { id, name: result }
* })
* }
* }))
*
* // Provide dependencies and merge all services together
* const allServicesLayer = userServiceLayer.pipe(
* Layer.provideMerge(Layer.mergeAll(databaseLayer, loggerLayer))
* )
*
* // Now the resulting layer provides UserService, Database, AND Logger
* const program = Effect.gen(function*() {
* const userService = yield* UserService
* const logger = yield* Logger // Still available!
* const database = yield* Database // Still available!
*
* const user = yield* userService.getUser("123")
* yield* logger.log(`Found user: ${user.name}`)
*
* return user
* }).pipe(
* Effect.provide(allServicesLayer)
* )
* ```
*
* @see {@link provide} for keeping dependency services private
*
* @category providing services
* @since 2.0.0
*/
<RIn, E, ROut>(that: Layer<ROut, E, RIn>): <RIn2, E2, ROut2>(self: Layer<ROut2, E2, RIn2>) => Layer<ROut | ROut2, E | E2, RIn | Exclude<RIn2, ROut>>
/**
* Feeds the output services of the dependency layer into the requirements of
* this layer, returning a layer that provides both sets of services.
*
* **When to use**
*
* Use when you need to compose `Layer`s while keeping both the constructed
* service and the dependency used to build it available.
*
* **Details**
*
* Prefer {@link provide} when the dependency should stay private.
*
* **Example** (Providing dependencies while retaining services)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* class UserService extends Context.Service<UserService, {
* readonly getUser: (id: string) => Effect.Effect<{
* id: string
* name: string
* }>
* }>()("UserService") {}
*
* // Create dependency layers
* const databaseLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
* })
*
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(`[LOG] ${msg}`)))
* })
*
* // UserService depends on Database and Logger
* const userServiceLayer = Layer.effect(UserService, Effect.gen(function*() {
* const database = yield* Database
* const logger = yield* Logger
*
* return {
* getUser: Effect.fn("UserService.getUser")(function*(id: string) {
* yield* logger.log(`Looking up user ${id}`)
* const result = yield* database.query(
* `SELECT * FROM users WHERE id = ${id}`
* )
* return { id, name: result }
* })
* }
* }))
*
* // Provide dependencies and merge all services together
* const allServicesLayer = userServiceLayer.pipe(
* Layer.provideMerge(Layer.mergeAll(databaseLayer, loggerLayer))
* )
*
* // Now the resulting layer provides UserService, Database, AND Logger
* const program = Effect.gen(function*() {
* const userService = yield* UserService
* const logger = yield* Logger // Still available!
* const database = yield* Database // Still available!
*
* const user = yield* userService.getUser("123")
* yield* logger.log(`Found user: ${user.name}`)
*
* return user
* }).pipe(
* Effect.provide(allServicesLayer)
* )
* ```
*
* @see {@link provide} for keeping dependency services private
*
* @category providing services
* @since 2.0.0
*/
<const Layers extends [Any, ...Array<Any>]>(that: Layers): <A, E, R>(
self: Layer<A, E, R>
) => Layer<
A | Success<Layers[number]>,
E | Error<Layers[number]>,
| Services<Layers[number]>
| Exclude<R, Success<Layers[number]>>
>
/**
* Feeds the output services of the dependency layer into the requirements of
* this layer, returning a layer that provides both sets of services.
*
* **When to use**
*
* Use when you need to compose `Layer`s while keeping both the constructed
* service and the dependency used to build it available.
*
* **Details**
*
* Prefer {@link provide} when the dependency should stay private.
*
* **Example** (Providing dependencies while retaining services)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* class UserService extends Context.Service<UserService, {
* readonly getUser: (id: string) => Effect.Effect<{
* id: string
* name: string
* }>
* }>()("UserService") {}
*
* // Create dependency layers
* const databaseLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
* })
*
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(`[LOG] ${msg}`)))
* })
*
* // UserService depends on Database and Logger
* const userServiceLayer = Layer.effect(UserService, Effect.gen(function*() {
* const database = yield* Database
* const logger = yield* Logger
*
* return {
* getUser: Effect.fn("UserService.getUser")(function*(id: string) {
* yield* logger.log(`Looking up user ${id}`)
* const result = yield* database.query(
* `SELECT * FROM users WHERE id = ${id}`
* )
* return { id, name: result }
* })
* }
* }))
*
* // Provide dependencies and merge all services together
* const allServicesLayer = userServiceLayer.pipe(
* Layer.provideMerge(Layer.mergeAll(databaseLayer, loggerLayer))
* )
*
* // Now the resulting layer provides UserService, Database, AND Logger
* const program = Effect.gen(function*() {
* const userService = yield* UserService
* const logger = yield* Logger // Still available!
* const database = yield* Database // Still available!
*
* const user = yield* userService.getUser("123")
* yield* logger.log(`Found user: ${user.name}`)
*
* return user
* }).pipe(
* Effect.provide(allServicesLayer)
* )
* ```
*
* @see {@link provide} for keeping dependency services private
*
* @category providing services
* @since 2.0.0
*/
<RIn2, E2, ROut2, RIn, E, ROut>(self: Layer<ROut2, E2, RIn2>, that: Layer<ROut, E, RIn>): Layer<ROut | ROut2, E | E2, RIn | Exclude<RIn2, ROut>>
/**
* Feeds the output services of the dependency layer into the requirements of
* this layer, returning a layer that provides both sets of services.
*
* **When to use**
*
* Use when you need to compose `Layer`s while keeping both the constructed
* service and the dependency used to build it available.
*
* **Details**
*
* Prefer {@link provide} when the dependency should stay private.
*
* **Example** (Providing dependencies while retaining services)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* class UserService extends Context.Service<UserService, {
* readonly getUser: (id: string) => Effect.Effect<{
* id: string
* name: string
* }>
* }>()("UserService") {}
*
* // Create dependency layers
* const databaseLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
* })
*
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(`[LOG] ${msg}`)))
* })
*
* // UserService depends on Database and Logger
* const userServiceLayer = Layer.effect(UserService, Effect.gen(function*() {
* const database = yield* Database
* const logger = yield* Logger
*
* return {
* getUser: Effect.fn("UserService.getUser")(function*(id: string) {
* yield* logger.log(`Looking up user ${id}`)
* const result = yield* database.query(
* `SELECT * FROM users WHERE id = ${id}`
* )
* return { id, name: result }
* })
* }
* }))
*
* // Provide dependencies and merge all services together
* const allServicesLayer = userServiceLayer.pipe(
* Layer.provideMerge(Layer.mergeAll(databaseLayer, loggerLayer))
* )
*
* // Now the resulting layer provides UserService, Database, AND Logger
* const program = Effect.gen(function*() {
* const userService = yield* UserService
* const logger = yield* Logger // Still available!
* const database = yield* Database // Still available!
*
* const user = yield* userService.getUser("123")
* yield* logger.log(`Found user: ${user.name}`)
*
* return user
* }).pipe(
* Effect.provide(allServicesLayer)
* )
* ```
*
* @see {@link provide} for keeping dependency services private
*
* @category providing services
* @since 2.0.0
*/
<A, E, R, const Layers extends [Any, ...Array<Any>]>(self: Layer<A, E, R>, that: Layers): Layer<
A | Success<Layers[number]>,
E | Error<Layers[number]>,
| Services<Layers[number]>
| Exclude<R, Success<Layers[number]>>
>
} = dual(2, (
self: Layer<any, any, any>,
that: Layer<any, any, any> | ReadonlyArray<Layer<any, any, any>>
) =>
provideWith(
self,
that,
(self, that) => Context.merge(that, self)
))
/**
* Constructs a layer dynamically based on the output of this layer.
*
* **Example** (Creating services from layer output)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Config extends Context.Service<Config, {
* readonly dbUrl: string
* readonly logLevel: string
* }>()("Config") {}
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* // Base config layer
* const configLayer = Layer.succeed(Config, {
* dbUrl: "postgres://localhost:5432/mydb",
* logLevel: "debug"
* })
*
* // Dynamically create services based on config
* const dynamicServiceLayer = configLayer.pipe(
* Layer.flatMap((context) => {
* const config = Context.get(context, Config)
*
* // Create database layer based on config
* const dbLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) =>
* Effect.succeed(
* `Querying ${config.dbUrl}: ${sql}`
* ))
* })
*
* // Create logger layer based on config
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) =>
* config.logLevel === "debug"
* ? Effect.sync(() => console.log(`[DEBUG] ${msg}`))
* : Effect.sync(() => console.log(msg))
* )
* })
*
* // Return combined layer
* return Layer.mergeAll(dbLayer, loggerLayer)
* })
* )
*
* // Use the dynamic services
* const program = Effect.gen(function*() {
* const database = yield* Database
* const logger = yield* Logger
*
* yield* logger.log("Starting database query")
* const result = yield* database.query("SELECT * FROM users")
*
* return result
* }).pipe(
* Effect.provide(dynamicServiceLayer)
* )
* ```
*
* @category sequencing
* @since 2.0.0
*/
export const flatMap: {
/**
* Constructs a layer dynamically based on the output of this layer.
*
* **Example** (Creating services from layer output)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Config extends Context.Service<Config, {
* readonly dbUrl: string
* readonly logLevel: string
* }>()("Config") {}
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* // Base config layer
* const configLayer = Layer.succeed(Config, {
* dbUrl: "postgres://localhost:5432/mydb",
* logLevel: "debug"
* })
*
* // Dynamically create services based on config
* const dynamicServiceLayer = configLayer.pipe(
* Layer.flatMap((context) => {
* const config = Context.get(context, Config)
*
* // Create database layer based on config
* const dbLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) =>
* Effect.succeed(
* `Querying ${config.dbUrl}: ${sql}`
* ))
* })
*
* // Create logger layer based on config
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) =>
* config.logLevel === "debug"
* ? Effect.sync(() => console.log(`[DEBUG] ${msg}`))
* : Effect.sync(() => console.log(msg))
* )
* })
*
* // Return combined layer
* return Layer.mergeAll(dbLayer, loggerLayer)
* })
* )
*
* // Use the dynamic services
* const program = Effect.gen(function*() {
* const database = yield* Database
* const logger = yield* Logger
*
* yield* logger.log("Starting database query")
* const result = yield* database.query("SELECT * FROM users")
*
* return result
* }).pipe(
* Effect.provide(dynamicServiceLayer)
* )
* ```
*
* @category sequencing
* @since 2.0.0
*/
<A, A2, E2, R2>(f: (context: Context.Context<A>) => Layer<A2, E2, R2>): <E, R>(self: Layer<A, E, R>) => Layer<A2, E2 | E, R2 | R>
/**
* Constructs a layer dynamically based on the output of this layer.
*
* **Example** (Creating services from layer output)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Config extends Context.Service<Config, {
* readonly dbUrl: string
* readonly logLevel: string
* }>()("Config") {}
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* // Base config layer
* const configLayer = Layer.succeed(Config, {
* dbUrl: "postgres://localhost:5432/mydb",
* logLevel: "debug"
* })
*
* // Dynamically create services based on config
* const dynamicServiceLayer = configLayer.pipe(
* Layer.flatMap((context) => {
* const config = Context.get(context, Config)
*
* // Create database layer based on config
* const dbLayer = Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) =>
* Effect.succeed(
* `Querying ${config.dbUrl}: ${sql}`
* ))
* })
*
* // Create logger layer based on config
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) =>
* config.logLevel === "debug"
* ? Effect.sync(() => console.log(`[DEBUG] ${msg}`))
* : Effect.sync(() => console.log(msg))
* )
* })
*
* // Return combined layer
* return Layer.mergeAll(dbLayer, loggerLayer)
* })
* )
*
* // Use the dynamic services
* const program = Effect.gen(function*() {
* const database = yield* Database
* const logger = yield* Logger
*
* yield* logger.log("Starting database query")
* const result = yield* database.query("SELECT * FROM users")
*
* return result
* }).pipe(
* Effect.provide(dynamicServiceLayer)
* )
* ```
*
* @category sequencing
* @since 2.0.0
*/
<A, E, R, A2, E2, R2>(
self: Layer<A, E, R>,
f: (context: Context.Context<A>) => Layer<A2, E2, R2>
): Layer<A2, E | E2, R | R2>
} = dual(2, <A, E, R, A2, E2, R2>(
self: Layer<A, E, R>,
f: (context: Context.Context<A>) => Layer<A2, E2, R2>
): Layer<A2, E | E2, R | R2> =>
fromBuild((memoMap, scope) =>
internalEffect.flatMap(
self.build(memoMap, scope),
(context) => f(context).build(memoMap, scope)
)
))
/**
* Performs the specified effect if this layer succeeds.
*
* **When to use**
*
* Use to run an effectful observation after a layer has been built
* successfully, such as logging or metrics, without changing the services the
* layer provides.
*
* **Details**
*
* The callback receives the services produced by this layer. Its result is
* discarded, and the original layer output is preserved.
*
* @see {@link tapError} for running an effect when layer construction fails with a typed error
* @see {@link tapCause} for running an effect when layer construction fails with any cause
*
* @category sequencing
* @since 2.0.0
*/
export const tap: {
/**
* Performs the specified effect if this layer succeeds.
*
* **When to use**
*
* Use to run an effectful observation after a layer has been built
* successfully, such as logging or metrics, without changing the services the
* layer provides.
*
* **Details**
*
* The callback receives the services produced by this layer. Its result is
* discarded, and the original layer output is preserved.
*
* @see {@link tapError} for running an effect when layer construction fails with a typed error
* @see {@link tapCause} for running an effect when layer construction fails with any cause
*
* @category sequencing
* @since 2.0.0
*/
<ROut, XR extends ROut, RIn2, E2, X>(f: (context: Context.Context<XR>) => Effect<X, E2, RIn2>): <RIn, E>(self: Layer<ROut, E, RIn>) => Layer<ROut, E | E2, RIn | Exclude<RIn2, Scope.Scope>>
/**
* Performs the specified effect if this layer succeeds.
*
* **When to use**
*
* Use to run an effectful observation after a layer has been built
* successfully, such as logging or metrics, without changing the services the
* layer provides.
*
* **Details**
*
* The callback receives the services produced by this layer. Its result is
* discarded, and the original layer output is preserved.
*
* @see {@link tapError} for running an effect when layer construction fails with a typed error
* @see {@link tapCause} for running an effect when layer construction fails with any cause
*
* @category sequencing
* @since 2.0.0
*/
<RIn, E, ROut, XR extends ROut, RIn2, E2, X>(
self: Layer<ROut, E, RIn>,
f: (context: Context.Context<XR>) => Effect<X, E2, RIn2>
): Layer<ROut, E | E2, RIn | Exclude<RIn2, Scope.Scope>>
} = dual(2, <RIn, E, ROut, XR extends ROut, RIn2, E2, X>(
self: Layer<ROut, E, RIn>,
f: (context: Context.Context<XR>) => Effect<X, E2, RIn2>
): Layer<ROut, E | E2, RIn | Exclude<RIn2, Scope.Scope>> =>
fromBuild((memoMap, scope) =>
internalEffect.flatMap(
self.build(memoMap, scope),
(context) => Scope.provide(internalEffect.as(f(context as Context.Context<XR>), context), scope)
)
))
/**
* Performs the specified effect if this layer fails.
*
* **When to use**
*
* Use to run logging, metrics, or other effects when layer construction fails
* while preserving the original typed error.
*
* **Details**
*
* The callback receives the typed error. If the callback succeeds, the layer
* still fails with the original error; if the callback fails, that failure is
* added to the layer's error type.
*
* @see {@link tap} for running an effect when layer construction succeeds
* @see {@link tapCause} for inspecting the full failure cause, including defects and interruption
*
* @category sequencing
* @since 2.0.0
*/
export const tapError: {
/**
* Performs the specified effect if this layer fails.
*
* **When to use**
*
* Use to run logging, metrics, or other effects when layer construction fails
* while preserving the original typed error.
*
* **Details**
*
* The callback receives the typed error. If the callback succeeds, the layer
* still fails with the original error; if the callback fails, that failure is
* added to the layer's error type.
*
* @see {@link tap} for running an effect when layer construction succeeds
* @see {@link tapCause} for inspecting the full failure cause, including defects and interruption
*
* @category sequencing
* @since 2.0.0
*/
<E, XE extends E, RIn2, E2, X>(f: (e: XE) => Effect<X, E2, RIn2>): <RIn, ROut>(self: Layer<ROut, E, RIn>) => Layer<ROut, E | E2, RIn | Exclude<RIn2, Scope.Scope>>
/**
* Performs the specified effect if this layer fails.
*
* **When to use**
*
* Use to run logging, metrics, or other effects when layer construction fails
* while preserving the original typed error.
*
* **Details**
*
* The callback receives the typed error. If the callback succeeds, the layer
* still fails with the original error; if the callback fails, that failure is
* added to the layer's error type.
*
* @see {@link tap} for running an effect when layer construction succeeds
* @see {@link tapCause} for inspecting the full failure cause, including defects and interruption
*
* @category sequencing
* @since 2.0.0
*/
<RIn, E, XE extends E, ROut, RIn2, E2, X>(self: Layer<ROut, E, RIn>, f: (e: XE) => Effect<X, E2, RIn2>): Layer<ROut, E | E2, RIn | Exclude<RIn2, Scope.Scope>>
} = dual(2, <RIn, E, XE extends E, ROut, RIn2, E2, X>(
self: Layer<ROut, E, RIn>,
f: (e: XE) => Effect<X, E2, RIn2>
): Layer<ROut, E | E2, RIn | Exclude<RIn2, Scope.Scope>> =>
fromBuild((memoMap, scope) =>
internalEffect.catch_(
self.build(memoMap, scope),
(error) => Scope.provide(internalEffect.andThen(f(error as XE), internalEffect.fail(error)), scope)
)
))
/**
* Performs the specified effect when this layer fails with any cause.
*
* **When to use**
*
* Use to run diagnostics or reporting when layer construction fails and the
* full `Cause` is needed.
*
* **Details**
*
* The callback receives the layer's `Cause`, so it can inspect typed errors,
* defects, and interruption information. If the callback succeeds, the layer
* fails again with the original cause; if the callback fails, that failure is
* added to the layer's error type.
*
* @see {@link tapError} for observing only typed layer construction errors
* @see {@link catchCause} for recovering from a layer construction failure by switching to another layer
*
* @category sequencing
* @since 4.0.0
*/
export const tapCause: {
/**
* Performs the specified effect when this layer fails with any cause.
*
* **When to use**
*
* Use to run diagnostics or reporting when layer construction fails and the
* full `Cause` is needed.
*
* **Details**
*
* The callback receives the layer's `Cause`, so it can inspect typed errors,
* defects, and interruption information. If the callback succeeds, the layer
* fails again with the original cause; if the callback fails, that failure is
* added to the layer's error type.
*
* @see {@link tapError} for observing only typed layer construction errors
* @see {@link catchCause} for recovering from a layer construction failure by switching to another layer
*
* @category sequencing
* @since 4.0.0
*/
<E, XE extends E, RIn2, E2, X>(f: (cause: Cause.Cause<XE>) => Effect<X, E2, RIn2>): <RIn, ROut>(self: Layer<ROut, E, RIn>) => Layer<ROut, E | E2, RIn | Exclude<RIn2, Scope.Scope>>
/**
* Performs the specified effect when this layer fails with any cause.
*
* **When to use**
*
* Use to run diagnostics or reporting when layer construction fails and the
* full `Cause` is needed.
*
* **Details**
*
* The callback receives the layer's `Cause`, so it can inspect typed errors,
* defects, and interruption information. If the callback succeeds, the layer
* fails again with the original cause; if the callback fails, that failure is
* added to the layer's error type.
*
* @see {@link tapError} for observing only typed layer construction errors
* @see {@link catchCause} for recovering from a layer construction failure by switching to another layer
*
* @category sequencing
* @since 4.0.0
*/
<RIn, E, XE extends E, ROut, RIn2, E2, X>(
self: Layer<ROut, E, RIn>,
f: (cause: Cause.Cause<XE>) => Effect<X, E2, RIn2>
): Layer<ROut, E | E2, RIn | Exclude<RIn2, Scope.Scope>>
} = dual(2, <RIn, E, XE extends E, ROut, RIn2, E2, X>(
self: Layer<ROut, E, RIn>,
f: (cause: Cause.Cause<XE>) => Effect<X, E2, RIn2>
): Layer<ROut, E | E2, RIn | Exclude<RIn2, Scope.Scope>> =>
fromBuild((memoMap, scope) =>
internalEffect.catchCause(
self.build(memoMap, scope),
(cause) =>
Scope.provide(internalEffect.andThen(f(cause as Cause.Cause<XE>), internalEffect.failCause(cause)), scope)
)
))
/**
* Converts layer construction failures into defects, removing them from the
* layer's error type.
*
* **Details**
*
* Use this only when failures should be treated as unrecoverable defects rather
* than typed errors that callers can handle.
*
* **Example** (Converting layer failures to defects)
*
* ```ts
* import { Context, Data, Effect, Layer } from "effect"
*
* class DatabaseError extends Data.TaggedError("DatabaseError")<{
* message: string
* }> {}
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* // Layer that can fail during construction
* const flakyDatabaseLayer = Layer.effect(Database, Effect.gen(function*() {
* console.log("connecting")
* return yield* new DatabaseError({ message: "Connection failed" })
* }))
*
* // Convert failures to fiber death - removes error from type
* const reliableDatabaseLayer = flakyDatabaseLayer.pipe(Layer.orDie)
*
* // Now the layer type is Layer<Database, never, never> - no error in type
* const program = Effect.gen(function*() {
* const database = yield* Database
* return yield* database.query("SELECT * FROM users")
* }).pipe(
* Effect.provide(reliableDatabaseLayer)
* )
*
* // Running the program prints "connecting", then the DatabaseError is
* // converted into a fiber defect instead of remaining a typed error.
* ```
*
* @category error handling
* @since 2.0.0
*/
export const orDie = <A, E, R>(self: Layer<A, E, R>): Layer<A, never, R> =>
fromBuildUnsafe((memoMap, scope) => internalEffect.orDie(self.build(memoMap, scope)))
const catch_: {
<E, RIn2, E2, ROut2>(
onError: (error: E) => Layer<ROut2, E2, RIn2>
): <RIn, ROut>(self: Layer<ROut, E, RIn>) => Layer<ROut & ROut2, E2, RIn2 | RIn>
<RIn, E, ROut, RIn2, E2, ROut2>(
self: Layer<ROut, E, RIn>,
onError: (error: E) => Layer<ROut2, E2, RIn2>
): Layer<ROut & ROut2, E2, RIn | RIn2>
} = dual(2, <RIn, E, ROut, RIn2, E2, ROut2>(
self: Layer<ROut, E, RIn>,
onError: (error: E) => Layer<ROut2, E2, RIn2>
): Layer<ROut & ROut2, E2, RIn | RIn2> =>
fromBuildUnsafe((memoMap, scope) =>
internalEffect.catch_(
self.build(memoMap, scope),
(e) => onError(e).build(memoMap, scope)
) as any
))
export {
/**
* Recovers from all typed errors by switching to another layer.
*
* **When to use**
*
* Use when every typed `Layer` construction error should use the same
* recovery path.
*
* @see {@link catchTag} for recovering from specific tagged errors
* @see {@link catchCause} for recovering with access to the full cause
*
* @category error handling
* @since 4.0.0
*/
catch_ as catch
}
/**
* Recovers from specific tagged errors.
*
* **When to use**
*
* Use when only some tagged `Layer` construction errors should be recovered.
*
* **Example** (Recovering from tagged layer errors)
*
* ```ts
* import { Context, Data, Effect, Layer } from "effect"
*
* class ConfigError extends Data.TaggedError("ConfigError") {}
*
* class Config extends Context.Service<Config, {
* readonly apiUrl: string
* }>()("Config") {}
*
* const configLayer = Layer.effect(Config, Effect.fail(new ConfigError()))
*
* const fallbackLayer = Layer.succeed(Config, { apiUrl: "http://localhost" })
*
* const recovered = configLayer.pipe(
* Layer.catchTag("ConfigError", () => fallbackLayer)
* )
* ```
*
* @see {@link catchCause} for recovering with access to the full cause
*
* @category error handling
* @since 4.0.0
*/
export const catchTag: {
/**
* Recovers from specific tagged errors.
*
* **When to use**
*
* Use when only some tagged `Layer` construction errors should be recovered.
*
* **Example** (Recovering from tagged layer errors)
*
* ```ts
* import { Context, Data, Effect, Layer } from "effect"
*
* class ConfigError extends Data.TaggedError("ConfigError") {}
*
* class Config extends Context.Service<Config, {
* readonly apiUrl: string
* }>()("Config") {}
*
* const configLayer = Layer.effect(Config, Effect.fail(new ConfigError()))
*
* const fallbackLayer = Layer.succeed(Config, { apiUrl: "http://localhost" })
*
* const recovered = configLayer.pipe(
* Layer.catchTag("ConfigError", () => fallbackLayer)
* )
* ```
*
* @see {@link catchCause} for recovering with access to the full cause
*
* @category error handling
* @since 4.0.0
*/
<const K extends Types.Tags<E> | NonEmptyReadonlyArray<Types.Tags<E>>, E, RIn2, E2, ROut2>(
k: K,
f: (
e: Types.ExtractTag<Types.NoInfer<E>, K extends NonEmptyReadonlyArray<string> ? K[number] : K>
) => Layer<ROut2, E2, RIn2>
): <RIn, ROut>(
self: Layer<ROut, E, RIn>
) => Layer<
ROut & ROut2,
E2 | Types.ExcludeTag<E, K extends NonEmptyReadonlyArray<string> ? K[number] : K>,
RIn2 | RIn
>
/**
* Recovers from specific tagged errors.
*
* **When to use**
*
* Use when only some tagged `Layer` construction errors should be recovered.
*
* **Example** (Recovering from tagged layer errors)
*
* ```ts
* import { Context, Data, Effect, Layer } from "effect"
*
* class ConfigError extends Data.TaggedError("ConfigError") {}
*
* class Config extends Context.Service<Config, {
* readonly apiUrl: string
* }>()("Config") {}
*
* const configLayer = Layer.effect(Config, Effect.fail(new ConfigError()))
*
* const fallbackLayer = Layer.succeed(Config, { apiUrl: "http://localhost" })
*
* const recovered = configLayer.pipe(
* Layer.catchTag("ConfigError", () => fallbackLayer)
* )
* ```
*
* @see {@link catchCause} for recovering with access to the full cause
*
* @category error handling
* @since 4.0.0
*/
<
RIn,
E,
ROut,
const K extends Types.Tags<E> | NonEmptyReadonlyArray<Types.Tags<E>>,
RIn2,
E2,
ROut2
>(
self: Layer<ROut, E, RIn>,
k: K,
f: (e: Types.ExtractTag<E, K extends NonEmptyReadonlyArray<string> ? K[number] : K>) => Layer<ROut2, E2, RIn2>
): Layer<
ROut & ROut2,
E2 | Types.ExcludeTag<E, K extends NonEmptyReadonlyArray<string> ? K[number] : K>,
RIn | RIn2
>
} = dual(3, <
RIn,
E,
ROut,
const K extends Types.Tags<E> | NonEmptyReadonlyArray<Types.Tags<E>>,
RIn2,
E2,
ROut2
>(
self: Layer<ROut, E, RIn>,
k: K,
f: (e: Types.ExtractTag<E, K extends NonEmptyReadonlyArray<string> ? K[number] : K>) => Layer<ROut2, E2, RIn2>
): Layer<ROut & ROut2, E2 | Types.ExcludeTag<E, K extends NonEmptyReadonlyArray<string> ? K[number] : K>, RIn | RIn2> =>
fromBuildUnsafe((memoMap, scope) =>
internalEffect.catchTag(
self.build(memoMap, scope),
k,
(error) => f(error).build(memoMap, scope)
) as any
))
/**
* Recovers from any failure cause by switching to another layer.
*
* **When to use**
*
* Use when you need `Layer` recovery to inspect more than the typed error,
* such as defects or interruption information.
*
* **Details**
*
* The handler receives the full `Cause` of the failed layer, including typed
* errors, unexpected defects, and interruption information, and returns the
* fallback layer to build instead. Finalizers for resources acquired by the
* failed layer are still run before the fallback layer is acquired.
*
* **Example** (Recovering from layer failures by cause)
*
* ```ts
* import { Context, Data, Effect, Layer } from "effect"
*
* class DatabaseError extends Data.TaggedError("DatabaseError")<{
* message: string
* }> {}
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* const primaryDatabaseLayer = Layer.effect(Database,
* Effect.fail(new DatabaseError({ message: "Primary DB unreachable" }))
* )
*
* const databaseWithFallback = primaryDatabaseLayer.pipe(
* Layer.catchCause(() => {
* return Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Memory: ${sql}`))
* })
* })
* )
*
* const program = Effect.gen(function*() {
* const database = yield* Database
* const result = yield* database.query("SELECT * FROM users")
* console.log(result)
* }).pipe(
* Effect.provide(databaseWithFallback)
* )
*
* Effect.runPromise(program)
* // Memory: SELECT * FROM users
* ```
*
* @see {@link catchTag} for recovering from specific tagged errors
*
* @category error handling
* @since 4.0.0
*/
export const catchCause: {
/**
* Recovers from any failure cause by switching to another layer.
*
* **When to use**
*
* Use when you need `Layer` recovery to inspect more than the typed error,
* such as defects or interruption information.
*
* **Details**
*
* The handler receives the full `Cause` of the failed layer, including typed
* errors, unexpected defects, and interruption information, and returns the
* fallback layer to build instead. Finalizers for resources acquired by the
* failed layer are still run before the fallback layer is acquired.
*
* **Example** (Recovering from layer failures by cause)
*
* ```ts
* import { Context, Data, Effect, Layer } from "effect"
*
* class DatabaseError extends Data.TaggedError("DatabaseError")<{
* message: string
* }> {}
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* const primaryDatabaseLayer = Layer.effect(Database,
* Effect.fail(new DatabaseError({ message: "Primary DB unreachable" }))
* )
*
* const databaseWithFallback = primaryDatabaseLayer.pipe(
* Layer.catchCause(() => {
* return Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Memory: ${sql}`))
* })
* })
* )
*
* const program = Effect.gen(function*() {
* const database = yield* Database
* const result = yield* database.query("SELECT * FROM users")
* console.log(result)
* }).pipe(
* Effect.provide(databaseWithFallback)
* )
*
* Effect.runPromise(program)
* // Memory: SELECT * FROM users
* ```
*
* @see {@link catchTag} for recovering from specific tagged errors
*
* @category error handling
* @since 4.0.0
*/
<E, RIn2, E2, ROut2>(onError: (cause: Cause.Cause<E>) => Layer<ROut2, E2, RIn2>): <RIn, ROut>(self: Layer<ROut, E, RIn>) => Layer<ROut & ROut2, E2, RIn2 | RIn>
/**
* Recovers from any failure cause by switching to another layer.
*
* **When to use**
*
* Use when you need `Layer` recovery to inspect more than the typed error,
* such as defects or interruption information.
*
* **Details**
*
* The handler receives the full `Cause` of the failed layer, including typed
* errors, unexpected defects, and interruption information, and returns the
* fallback layer to build instead. Finalizers for resources acquired by the
* failed layer are still run before the fallback layer is acquired.
*
* **Example** (Recovering from layer failures by cause)
*
* ```ts
* import { Context, Data, Effect, Layer } from "effect"
*
* class DatabaseError extends Data.TaggedError("DatabaseError")<{
* message: string
* }> {}
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* const primaryDatabaseLayer = Layer.effect(Database,
* Effect.fail(new DatabaseError({ message: "Primary DB unreachable" }))
* )
*
* const databaseWithFallback = primaryDatabaseLayer.pipe(
* Layer.catchCause(() => {
* return Layer.succeed(Database, {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Memory: ${sql}`))
* })
* })
* )
*
* const program = Effect.gen(function*() {
* const database = yield* Database
* const result = yield* database.query("SELECT * FROM users")
* console.log(result)
* }).pipe(
* Effect.provide(databaseWithFallback)
* )
*
* Effect.runPromise(program)
* // Memory: SELECT * FROM users
* ```
*
* @see {@link catchTag} for recovering from specific tagged errors
*
* @category error handling
* @since 4.0.0
*/
<RIn, E, ROut, RIn2, E2, ROut22>(
self: Layer<ROut, E, RIn>,
onError: (cause: Cause.Cause<E>) => Layer<ROut22, E2, RIn2>
): Layer<ROut & ROut22, E2, RIn | RIn2>
} = dual(2, <RIn, E, ROut, RIn2, E2, ROut2>(
self: Layer<ROut, E, RIn>,
onError: (cause: Cause.Cause<E>) => Layer<ROut2, E2, RIn2>
): Layer<ROut & ROut2, E2, RIn | RIn2> =>
fromBuildUnsafe((memoMap, scope) =>
internalEffect.catchCause(
self.build(memoMap, scope),
(cause) => onError(cause).build(memoMap, scope)
) as any
))
/**
* Updates a service in the context with a new implementation.
*
* **When to use**
*
* Use to adapt or extend a service's behavior during the creation of a
* layer.
*
* **Details**
*
* This function modifies the existing implementation of a service in the
* context. It retrieves the current service, applies the provided
* transformation function `f`, and replaces the old service with the
* transformed one.
*
* @category providing services
* @since 3.13.0
*/
export const updateService: {
/**
* Updates a service in the context with a new implementation.
*
* **When to use**
*
* Use to adapt or extend a service's behavior during the creation of a
* layer.
*
* **Details**
*
* This function modifies the existing implementation of a service in the
* context. It retrieves the current service, applies the provided
* transformation function `f`, and replaces the old service with the
* transformed one.
*
* @category providing services
* @since 3.13.0
*/
<I, A>(service: Context.Key<I, A>, f: (a: Types.NoInfer<A>) => A): <A1, E1, R1>(layer: Layer<A1, E1, R1>) => Layer<A1, E1, I | R1>
/**
* Updates a service in the context with a new implementation.
*
* **When to use**
*
* Use to adapt or extend a service's behavior during the creation of a
* layer.
*
* **Details**
*
* This function modifies the existing implementation of a service in the
* context. It retrieves the current service, applies the provided
* transformation function `f`, and replaces the old service with the
* transformed one.
*
* @category providing services
* @since 3.13.0
*/
<A1, E1, R1, I, A>(
layer: Layer<A1, E1, R1>,
service: Context.Key<I, A>,
f: (a: Types.NoInfer<A>) => A
): Layer<A1, E1, I | R1>
} = dual(
3,
<A1, E1, R1, I, A>(
layer: Layer<A1, E1, R1>,
service: Context.Key<I, A>,
f: (a: Types.NoInfer<A>) => A
): Layer<A1, E1, I | R1> => provide(layer, effect(service, internalEffect.map(service, f)))
)
/**
* Creates a fresh version of this layer that will not be shared.
*
* **When to use**
*
* Use when you need two parts of an application to receive separate instances
* of a resource, such as two independent client sessions.
*
* **Gotchas**
*
* Do not use it just to work around confusing composition. By default, sharing
* the same layer value is usually the desired behavior.
*
* **Example** (Creating non-shared layer instances)
*
* ```ts
* import { Context, Effect, Layer, Ref } from "effect"
*
* class Counter extends Context.Service<Counter, {
* readonly id: number
* }>()("Counter") {}
*
* class Left extends Context.Service<Left, {
* readonly counterId: number
* }>()("Left") {}
*
* class Right extends Context.Service<Right, {
* readonly counterId: number
* }>()("Right") {}
*
* const leftLayer = Layer.effect(Left, Effect.gen(function*() {
* const counter = yield* Counter
* return { counterId: counter.id }
* }))
*
* const rightLayer = Layer.effect(Right, Effect.gen(function*() {
* const counter = yield* Counter
* return { counterId: counter.id }
* }))
*
* const showIds = Effect.gen(function*() {
* const left = yield* Left
* const right = yield* Right
* console.log(`same Counter: ${left.counterId === right.counterId}`)
* })
*
* const program = Effect.gen(function*() {
* const nextId = yield* Ref.make(0)
*
* const counterLayer = Layer.effect(Counter, Effect.gen(function*() {
* const id = yield* Ref.updateAndGet(nextId, (n) => n + 1)
* console.log("constructed Counter")
* return { id }
* }))
*
* const shared = Layer.merge(
* Layer.provide(leftLayer, counterLayer),
* Layer.provide(rightLayer, counterLayer)
* )
*
* yield* Effect.provide(showIds, shared)
*
* const freshCounterLayer = Layer.fresh(counterLayer)
* const fresh = Layer.merge(
* Layer.provide(leftLayer, freshCounterLayer),
* Layer.provide(rightLayer, freshCounterLayer)
* )
*
* yield* Effect.provide(showIds, fresh)
* })
*
* Effect.runPromise(program)
* // constructed Counter
* // same Counter: true
* // constructed Counter
* // constructed Counter
* // same Counter: false
* ```
*
* @category layers
* @since 2.0.0
*/
export const fresh = <A, E, R>(self: Layer<A, E, R>): Layer<A, E, R> =>
fromBuildUnsafe((_, scope) => self.build(makeMemoMapUnsafe(), scope))
/**
* Builds this layer and keeps it alive until the returned effect is interrupted.
*
* **When to use**
*
* Use when you model your entire application as a layer, such as an HTTP
* server.
*
* **Details**
*
* When the returned effect is interrupted, the layer scope is closed and all
* finalizers registered during layer acquisition are run.
*
* **Example** (Launching an application layer)
*
* ```ts
* import { Console, Context, Effect, Layer } from "effect"
*
* class HttpServer extends Context.Service<HttpServer, {
* readonly start: () => Effect.Effect<string>
* readonly stop: () => Effect.Effect<string>
* }>()("HttpServer") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* // Server layer that starts an HTTP server
* const serverLayer = Layer.effect(HttpServer, Effect.gen(function*() {
* yield* Console.log("Starting HTTP server...")
*
* return {
* start: Effect.fn("HttpServer.start")(function*() {
* yield* Console.log("Server listening on port 3000")
* return "Server started"
* }),
* stop: Effect.fn("HttpServer.stop")(function*() {
* yield* Console.log("Server stopped gracefully")
* return "Server stopped"
* })
* }
* }))
*
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Console.log(`[LOG] ${msg}`))
* })
*
* // Application layer combining all services
* const appLayer = Layer.mergeAll(serverLayer, loggerLayer)
*
* // Launch the application - runs until interrupted
* const application = appLayer.pipe(
* Layer.launch,
* Effect.tapError((error) => Console.log(`Application failed: ${error}`)),
* Effect.tap(() => Console.log("Application completed"))
* )
*
* // This will run forever until externally interrupted
* // Effect.runFork(application)
* ```
*
* @category converting
* @since 2.0.0
*/
export const launch = <RIn, E, ROut>(self: Layer<ROut, E, RIn>): Effect<never, E, RIn> =>
internalEffect.scoped(internalEffect.andThen(build(self), internalEffect.never))
/**
* A utility type for creating partial mocks of services in testing.
*
* **When to use**
*
* Use to type partial test service implementations where only exercised
* effectful members are stubbed.
*
* **Details**
*
* This type makes `Effect`, `Stream`, and `Channel` values and functions
* returning them optional, while keeping non-effectful properties required.
* This allows you to provide only the methods you need to test while leaving
* others unimplemented.
*
* @see {@link mock} for creating a mock layer from a partial service implementation
*
* @category testing
* @since 3.17.0
*/
export type PartialEffectful<A extends object> = Types.Simplify<
& {
[K in keyof A as A[K] extends AnyEffectOrStream ? K : never]?: A[K]
}
& {
[K in keyof A as A[K] extends AnyEffectOrStream ? never : K]: A[K]
}
>
type AnyEffectOrStream =
| Effect<any, any, any>
| Stream.Stream<any, any, any>
| Channel.Channel<any, any, any, any, any, any, any>
| ((...args: any) => Effect<any, any, any>)
| ((...args: any) => Stream.Stream<any, any, any>)
| ((...args: any) => Channel.Channel<any, any, any, any, any, any, any>)
/**
* Creates a mock layer for testing purposes. You can provide a partial
* implementation of the service. Any missing members that are `Effect`s,
* `Stream`s, `Channel`s, or functions returning them will fail with an
* unimplemented defect when used.
*
* **Details**
*
* Missing members are represented by a value that can be used as an `Effect`,
* `Stream`, `Channel`, or as a function returning an `Effect`. This lets the
* mock preserve the shape of common service methods while still failing loudly
* when an unimplemented member is exercised.
*
* **Example** (Mocking services for tests)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class UserService extends Context.Service<UserService, {
* readonly config: { apiUrl: string }
* readonly getUser: (
* id: string
* ) => Effect.Effect<{ id: string; name: string }, Error>
* readonly deleteUser: (id: string) => Effect.Effect<void, Error>
* readonly updateUser: (
* id: string,
* data: object
* ) => Effect.Effect<{ id: string; name: string }, Error>
* }>()("UserService") {}
*
* // Create a partial mock - only implement what you need for testing
* const testUserLayer = Layer.mock(UserService, {
* config: { apiUrl: "https://test-api.com" }, // Required - non-Effect property
* getUser: (id: string) => Effect.succeed({ id, name: "Test User" }) // Mock implementation
* // deleteUser and updateUser are omitted - will throw UnimplementedError if called
* })
*
* // Use in tests
* const testProgram = Effect.gen(function*() {
* const userService = yield* UserService
*
* // This works - we provided an implementation
* const user = yield* userService.getUser("123")
* console.log(user.name) // "Test User"
*
* // This would throw - we didn't implement deleteUser
* // yield* userService.deleteUser("123") // UnimplementedError
* }).pipe(
* Effect.provide(testUserLayer)
* )
* ```
*
* @category testing
* @since 3.17.0
*/
export const mock: {
/**
* Creates a mock layer for testing purposes. You can provide a partial
* implementation of the service. Any missing members that are `Effect`s,
* `Stream`s, `Channel`s, or functions returning them will fail with an
* unimplemented defect when used.
*
* **Details**
*
* Missing members are represented by a value that can be used as an `Effect`,
* `Stream`, `Channel`, or as a function returning an `Effect`. This lets the
* mock preserve the shape of common service methods while still failing loudly
* when an unimplemented member is exercised.
*
* **Example** (Mocking services for tests)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class UserService extends Context.Service<UserService, {
* readonly config: { apiUrl: string }
* readonly getUser: (
* id: string
* ) => Effect.Effect<{ id: string; name: string }, Error>
* readonly deleteUser: (id: string) => Effect.Effect<void, Error>
* readonly updateUser: (
* id: string,
* data: object
* ) => Effect.Effect<{ id: string; name: string }, Error>
* }>()("UserService") {}
*
* // Create a partial mock - only implement what you need for testing
* const testUserLayer = Layer.mock(UserService, {
* config: { apiUrl: "https://test-api.com" }, // Required - non-Effect property
* getUser: (id: string) => Effect.succeed({ id, name: "Test User" }) // Mock implementation
* // deleteUser and updateUser are omitted - will throw UnimplementedError if called
* })
*
* // Use in tests
* const testProgram = Effect.gen(function*() {
* const userService = yield* UserService
*
* // This works - we provided an implementation
* const user = yield* userService.getUser("123")
* console.log(user.name) // "Test User"
*
* // This would throw - we didn't implement deleteUser
* // yield* userService.deleteUser("123") // UnimplementedError
* }).pipe(
* Effect.provide(testUserLayer)
* )
* ```
*
* @category testing
* @since 3.17.0
*/
<I, S extends object>(service: Context.Key<I, S>): (implementation: PartialEffectful<S>) => Layer<I>
/**
* Creates a mock layer for testing purposes. You can provide a partial
* implementation of the service. Any missing members that are `Effect`s,
* `Stream`s, `Channel`s, or functions returning them will fail with an
* unimplemented defect when used.
*
* **Details**
*
* Missing members are represented by a value that can be used as an `Effect`,
* `Stream`, `Channel`, or as a function returning an `Effect`. This lets the
* mock preserve the shape of common service methods while still failing loudly
* when an unimplemented member is exercised.
*
* **Example** (Mocking services for tests)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class UserService extends Context.Service<UserService, {
* readonly config: { apiUrl: string }
* readonly getUser: (
* id: string
* ) => Effect.Effect<{ id: string; name: string }, Error>
* readonly deleteUser: (id: string) => Effect.Effect<void, Error>
* readonly updateUser: (
* id: string,
* data: object
* ) => Effect.Effect<{ id: string; name: string }, Error>
* }>()("UserService") {}
*
* // Create a partial mock - only implement what you need for testing
* const testUserLayer = Layer.mock(UserService, {
* config: { apiUrl: "https://test-api.com" }, // Required - non-Effect property
* getUser: (id: string) => Effect.succeed({ id, name: "Test User" }) // Mock implementation
* // deleteUser and updateUser are omitted - will throw UnimplementedError if called
* })
*
* // Use in tests
* const testProgram = Effect.gen(function*() {
* const userService = yield* UserService
*
* // This works - we provided an implementation
* const user = yield* userService.getUser("123")
* console.log(user.name) // "Test User"
*
* // This would throw - we didn't implement deleteUser
* // yield* userService.deleteUser("123") // UnimplementedError
* }).pipe(
* Effect.provide(testUserLayer)
* )
* ```
*
* @category testing
* @since 3.17.0
*/
<I, S extends object>(
service: Context.Key<I, S>,
implementation: Types.NoInfer<PartialEffectful<S>>
): Layer<I>
} = function() {
if (arguments.length === 1) {
return (implementation: any) => mockImpl(arguments[0], implementation)
}
return mockImpl(arguments[0], arguments[1])
} as any
const mockImpl = <I, S extends object>(service: Context.Key<I, S>, implementation: PartialEffectful<S>): Layer<I> =>
succeed(service)(
new Proxy({ ...implementation as object } as S, {
get(target, prop, _receiver) {
if (prop in target) {
return target[prop as keyof S]
}
const prevLimit = (Error as ErrorWithStackTraceLimit).stackTraceLimit
;(Error as ErrorWithStackTraceLimit).stackTraceLimit = 2
const error = new Error(`${service.key}: Unimplemented method "${prop.toString()}"`)
;(Error as ErrorWithStackTraceLimit).stackTraceLimit = prevLimit
error.name = "UnimplementedError"
return makeUnimplemented(error)
},
has: constTrue
})
)
const makeUnimplemented = (error: globalThis.Error) => {
const dead = Object.assign(internalEffect.die(error), {
[StreamTypeId]: StreamTypeId,
channel: {
[ChannelTypeId]: ChannelTypeId,
transform: () => internalEffect.succeed(dead),
pipe() {
return pipeArguments(this, arguments)
}
},
[ChannelTypeId]: ChannelTypeId,
transform: () => internalEffect.succeed(dead)
})
function unimplemented() {
return dead
}
// @effect-diagnostics-next-line floatingEffect:off
Object.assign(unimplemented, dead)
Object.setPrototypeOf(unimplemented, Object.getPrototypeOf(dead))
return unimplemented
}
const StreamTypeId: Stream.TypeId = "~effect/Stream"
const ChannelTypeId: Channel.TypeId = "~effect/Channel"
// -----------------------------------------------------------------------------
// Type constraints
// -----------------------------------------------------------------------------
/**
* Ensures that a layer's success type extends a given type `ROut`.
*
* **Details**
*
* This function provides compile-time type checking to ensure that the success
* value of a layer conforms to a specific type constraint.
*
* **Example** (Constraining layer success types)
*
* ```ts
* import { Layer } from "effect"
*
* declare const FortyTwoLayer: Layer.Layer<42, never, never>
* declare const StringLayer: Layer.Layer<string, never, never>
*
* // Define a constraint that the success type must be a number
* const satisfiesNumber = Layer.satisfiesSuccessType<number>()
*
* // This works - Layer<42, never, never> extends Layer<number, never, never>
* const validLayer = satisfiesNumber(FortyTwoLayer)
*
* // This would cause a TypeScript compilation error:
* // const invalidLayer = satisfiesNumber(StringLayer)
* // ^^^^^^^^^^^
* // Type 'string' is not assignable to type 'number'
* ```
*
* @category utility types
* @since 4.0.0
*/
export const satisfiesSuccessType =
<ROut>() => <ROut2 extends ROut, E, RIn>(layer: Layer<ROut2, E, RIn>): Layer<ROut2, E, RIn> => layer
/**
* Ensures that a layer's error type extends a given type `E`.
*
* **Details**
*
* This function provides compile-time type checking to ensure that the error
* type of a layer conforms to a specific type constraint.
*
* **Example** (Constraining layer error types)
*
* ```ts
* import { Layer } from "effect"
*
* declare const ErrorLayer: Layer.Layer<never, Error, never>
* declare const TypeErrorLayer: Layer.Layer<never, TypeError, never>
* declare const StringLayer: Layer.Layer<never, string, never>
*
* // Define a constraint that the error type must be an Error
* const satisfiesError = Layer.satisfiesErrorType<Error>()
*
* // This works - Layer<never, TypeError, never> extends Layer<never, Error, never>
* const validLayer = satisfiesError(TypeErrorLayer)
*
* // This would cause a TypeScript compilation error:
* // const invalidLayer = satisfiesError(StringLayer)
* // ^^^^^^^^^^^
* // Type 'string' is not assignable to type 'Error'
* ```
*
* @category utility types
* @since 4.0.0
*/
export const satisfiesErrorType =
<E>() => <ROut, E2 extends E, RIn>(layer: Layer<ROut, E2, RIn>): Layer<ROut, E2, RIn> => layer
/**
* Ensures that a layer's requirements type extends a given type `R`.
*
* **Details**
*
* This function provides compile-time type checking to ensure that the
* requirements type of a layer conforms to a specific type constraint.
*
* **Example** (Constraining layer service requirements)
*
* ```ts
* import { Layer } from "effect"
*
* declare const FortyTwoLayer: Layer.Layer<never, never, 42>
* declare const StringLayer: Layer.Layer<never, never, string>
*
* // Define a constraint that the service requirements must be numbers
* const satisfiesNumber = Layer.satisfiesServicesType<number>()
*
* // This works - Layer<never, never, 42> extends Layer<never, never, number>
* const validLayer = satisfiesNumber(FortyTwoLayer)
*
* // This would cause a TypeScript compilation error:
* // const invalidLayer = satisfiesNumber(StringLayer)
* // ^^^^^^^^^^^
* // Type 'string' is not assignable to type 'number'
* ```
*
* @category utility types
* @since 4.0.0
*/
export const satisfiesServicesType =
<RIn>() => <ROut, E, RIn2 extends RIn>(layer: Layer<ROut, E, RIn2>): Layer<ROut, E, RIn2> => layer
// -----------------------------------------------------------------------------
// Tracing
// -----------------------------------------------------------------------------
/**
* Represents options that can be used to control the behavior of spans created
* for layers.
*
* **When to use**
*
* Use to configure tracing metadata, stack trace capture, and `onEnd`
* finalization for spans created by `Layer.span` and `Layer.withSpan` during
* layer construction.
*
* **Details**
*
* Extends `Tracer.SpanOptions` with `onEnd`, which runs when the layer span
* ends as the layer scope closes.
*
* @see {@link span} for creating a layer span
* @see {@link withSpan} for wrapping layer construction in a span
*
* @category options
* @since 4.0.0
*/
export interface SpanOptions extends Tracer.SpanOptions {
/**
* Runs when the span associated with the layer ends, which happens when the
* layer scope is closed.
*/
readonly onEnd?:
| ((span: Tracer.Span, exit: Exit.Exit<unknown, unknown>) => Effect<void>)
| undefined
}
/**
* Constructs a new `Layer` which creates a span and registers it as the current
* parent span.
*
* **Details**
*
* This allows you to create a traced scope for layer construction, making all
* operations within the layer constructor part of the same trace span. The span
* is automatically ended when the layer's scope is closed. If `onEnd` is
* provided, it receives the span and the layer scope's exit value when the span
* ends.
*
* **Example** (Tracing layer construction with a span)
*
* ```ts
* import { Console, Context, Effect, Layer } from "effect"
* import type { Tracer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* // Create a traced layer - all operations performed during construction of
* // the `Database` service are part of the "database-init" span
* const databaseLayer = Layer.effect(Database, Effect.gen(function*() {
* // These operations are traced under "database-init" span
* yield* Effect.log("Connecting to database")
* yield* Effect.sleep("100 millis")
* yield* Effect.log("Database connected")
*
* const parentSpan = yield* Effect.currentParentSpan
* yield* Console.log((parentSpan as Tracer.Span).name) // "database-init"
*
* return {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result: ${sql}`))
* }
* })).pipe(Layer.provide(Layer.span("database-init")))
*
* // Can also use the `onEnd` callback to execute logic when the span ends
* const tracedLayer = Layer.span("service-initialization", {
* attributes: { version: "1.0.0" },
* onEnd: (span, exit) =>
* Effect.sync(() => {
* console.log(`Span ${span.name} ended with:`, exit._tag)
* })
* })
* ```
*
* @category tracing
* @since 2.0.0
*/
export const span = (
name: string,
options?: SpanOptions
): Layer<Tracer.ParentSpan> => {
options = internalTracer.addSpanStackTrace(options)
return effect(
Tracer.ParentSpan,
options?.onEnd
? internalEffect.tap(
internalEffect.makeSpanScoped(name, options),
(span) => internalEffect.addFinalizer((exit) => options.onEnd!(span, exit))
)
: internalEffect.makeSpanScoped(name, options)
)
}
/**
* Constructs a layer that provides an existing span as the current parent span.
*
* **Details**
*
* The supplied span is made available through `Tracer.ParentSpan` for layers
* that are built with this layer. This API does not create, end, or close the
* span; the caller remains responsible for the span's lifetime.
*
* **Example** (Using an existing parent span)
*
* ```ts
* import { Console, Context, Effect, Layer, Tracer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* // Create a layer that uses an existing span as parent
* const databaseLayer = Layer.effect(
* Database,
* Effect.gen(function*() {
* yield* Effect.log("Initializing database")
*
* const parentSpan = yield* Effect.currentParentSpan
* yield* Console.log(parentSpan.spanId) // "42"
*
* return {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result: ${sql}`))
* }
* })
* ).pipe(Layer.provide(Layer.parentSpan(Tracer.externalSpan({
* spanId: "42",
* traceId: "000"
* }))))
* ```
*
* @category tracing
* @since 2.0.0
*/
export const parentSpan = (span: Tracer.AnySpan): Layer<Tracer.ParentSpan> =>
succeedContext(Tracer.ParentSpan.context(span))
/**
* Wraps a `Layer` with a new tracing span, making all operations in the layer
* constructor part of the named trace span.
*
* **Details**
*
* This creates a new span for the layer's construction and execution. The span
* is automatically ended when the layer's scope is closed. This is useful for
* tracking the lifecycle and performance of layer initialization.
*
* **Example** (Wrapping a layer with a span)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* // Create layers with tracing
* const databaseLayer = Layer.effect(Database, Effect.gen(function*() {
* yield* Effect.log("Connecting to database")
* yield* Effect.sleep("100 millis")
* return {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result: ${sql}`))
* }
* })).pipe(Layer.withSpan("database-initialization", {
* attributes: { dbType: "postgres" }
* }))
*
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(msg)))
* }).pipe(Layer.withSpan("logger-initialization"))
*
* // Combine traced layers
* const appLayer = Layer.mergeAll(databaseLayer, loggerLayer).pipe(
* Layer.withSpan("app-initialization", {
* onEnd: (span, exit) =>
* Effect.sync(() => {
* console.log(`Application initialization completed: ${exit._tag}`)
* })
* })
* )
*
* const program = Effect.gen(function*() {
* const database = yield* Database
* const logger = yield* Logger
*
* yield* logger.log("Application ready")
* return yield* database.query("SELECT * FROM users")
* }).pipe(Effect.provide(appLayer))
* ```
*
* @category tracing
* @since 2.0.0
*/
export const withSpan: {
/**
* Wraps a `Layer` with a new tracing span, making all operations in the layer
* constructor part of the named trace span.
*
* **Details**
*
* This creates a new span for the layer's construction and execution. The span
* is automatically ended when the layer's scope is closed. This is useful for
* tracking the lifecycle and performance of layer initialization.
*
* **Example** (Wrapping a layer with a span)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* // Create layers with tracing
* const databaseLayer = Layer.effect(Database, Effect.gen(function*() {
* yield* Effect.log("Connecting to database")
* yield* Effect.sleep("100 millis")
* return {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result: ${sql}`))
* }
* })).pipe(Layer.withSpan("database-initialization", {
* attributes: { dbType: "postgres" }
* }))
*
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(msg)))
* }).pipe(Layer.withSpan("logger-initialization"))
*
* // Combine traced layers
* const appLayer = Layer.mergeAll(databaseLayer, loggerLayer).pipe(
* Layer.withSpan("app-initialization", {
* onEnd: (span, exit) =>
* Effect.sync(() => {
* console.log(`Application initialization completed: ${exit._tag}`)
* })
* })
* )
*
* const program = Effect.gen(function*() {
* const database = yield* Database
* const logger = yield* Logger
*
* yield* logger.log("Application ready")
* return yield* database.query("SELECT * FROM users")
* }).pipe(Effect.provide(appLayer))
* ```
*
* @category tracing
* @since 2.0.0
*/
(name: string, options?: SpanOptions): <A, E, R>(
self: Layer<A, E, R>
) => Layer<A, E, Exclude<R, Tracer.ParentSpan>>
/**
* Wraps a `Layer` with a new tracing span, making all operations in the layer
* constructor part of the named trace span.
*
* **Details**
*
* This creates a new span for the layer's construction and execution. The span
* is automatically ended when the layer's scope is closed. This is useful for
* tracking the lifecycle and performance of layer initialization.
*
* **Example** (Wrapping a layer with a span)
*
* ```ts
* import { Context, Effect, Layer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Logger extends Context.Service<Logger, {
* readonly log: (msg: string) => Effect.Effect<void>
* }>()("Logger") {}
*
* // Create layers with tracing
* const databaseLayer = Layer.effect(Database, Effect.gen(function*() {
* yield* Effect.log("Connecting to database")
* yield* Effect.sleep("100 millis")
* return {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`Result: ${sql}`))
* }
* })).pipe(Layer.withSpan("database-initialization", {
* attributes: { dbType: "postgres" }
* }))
*
* const loggerLayer = Layer.succeed(Logger, {
* log: Effect.fn("Logger.log")((msg: string) => Effect.sync(() => console.log(msg)))
* }).pipe(Layer.withSpan("logger-initialization"))
*
* // Combine traced layers
* const appLayer = Layer.mergeAll(databaseLayer, loggerLayer).pipe(
* Layer.withSpan("app-initialization", {
* onEnd: (span, exit) =>
* Effect.sync(() => {
* console.log(`Application initialization completed: ${exit._tag}`)
* })
* })
* )
*
* const program = Effect.gen(function*() {
* const database = yield* Database
* const logger = yield* Logger
*
* yield* logger.log("Application ready")
* return yield* database.query("SELECT * FROM users")
* }).pipe(Effect.provide(appLayer))
* ```
*
* @category tracing
* @since 2.0.0
*/
<A, E, R>(self: Layer<A, E, R>, name: string, options?: SpanOptions): Layer<A, E, Exclude<R, Tracer.ParentSpan>>
} = function() {
const dataFirst = typeof arguments[0] !== "string"
const name = dataFirst ? arguments[1] : arguments[0]
const options = internalTracer.addSpanStackTrace(dataFirst ? arguments[2] : arguments[1]) as SpanOptions
if (dataFirst) {
const self = arguments[0]
return unwrap(
internalEffect.map(
options?.onEnd !== undefined
? internalEffect.tap(
internalEffect.makeSpanScoped(name, options),
(span) => internalEffect.addFinalizer((exit) => options.onEnd!(span, exit))
)
: internalEffect.makeSpanScoped(name, options),
(span) => withParentSpan(self, span)
)
)
}
return (self: Layer<any, any, any>) =>
unwrap(
internalEffect.map(
options?.onEnd !== undefined
? internalEffect.tap(
internalEffect.makeSpanScoped(name, options),
(span) => internalEffect.addFinalizer((exit) => options.onEnd!(span, exit))
)
: internalEffect.makeSpanScoped(name, options),
(span) => withParentSpan(self, span)
)
)
} as any
/**
* Wraps a layer so spans created during its construction use the supplied span
* as their parent.
*
* **Details**
*
* Use this to attach layer construction to an existing trace hierarchy. This API
* does not create or end the supplied parent span.
*
* When the supplied span is a native `Span`, layer construction also receives
* diagnostic information that helps associate failures with the layer call site.
* External spans are only installed as the parent span and do not add this
* diagnostic call-site information.
*
* **Example** (Attaching layers to an existing parent span)
*
* ```ts
* import { Context, Effect, Layer, Tracer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Cache extends Context.Service<Cache, {
* readonly get: (key: string) => Effect.Effect<string | null>
* }>()("Cache") {}
*
* // Create layers
* const DatabaseLayer = Layer.effect(Database, Effect.gen(function*() {
* yield* Effect.log("Connecting to database")
* return {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
* }
* }))
*
* const CacheLayer = Layer.effect(Cache, Effect.gen(function*() {
* yield* Effect.log("Connecting to cache")
* return {
* get: Effect.fn("Cache.get")((key: string) => Effect.succeed(`Cache: ${key}`))
* }
* }))
*
* // Use with an existing parent span from Effect.withSpan
* const program = Effect.withSpan("application-startup")(
* Effect.gen(function*() {
* const parentSpan = yield* Tracer.ParentSpan
*
* // Both layers will be children of "application-startup" span
* const AppLayer = Layer.mergeAll(DatabaseLayer, CacheLayer).pipe(
* Layer.withParentSpan(parentSpan)
* )
*
* const context = yield* Layer.build(AppLayer)
* const database = Context.get(context, Database)
* const cache = Context.get(context, Cache)
*
* const dbResult = yield* database.query("SELECT * FROM users")
* const cacheResult = yield* cache.get("user:123")
*
* return { dbResult, cacheResult }
* })
* )
* ```
*
* @category tracing
* @since 2.0.0
*/
export const withParentSpan: {
/**
* Wraps a layer so spans created during its construction use the supplied span
* as their parent.
*
* **Details**
*
* Use this to attach layer construction to an existing trace hierarchy. This API
* does not create or end the supplied parent span.
*
* When the supplied span is a native `Span`, layer construction also receives
* diagnostic information that helps associate failures with the layer call site.
* External spans are only installed as the parent span and do not add this
* diagnostic call-site information.
*
* **Example** (Attaching layers to an existing parent span)
*
* ```ts
* import { Context, Effect, Layer, Tracer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Cache extends Context.Service<Cache, {
* readonly get: (key: string) => Effect.Effect<string | null>
* }>()("Cache") {}
*
* // Create layers
* const DatabaseLayer = Layer.effect(Database, Effect.gen(function*() {
* yield* Effect.log("Connecting to database")
* return {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
* }
* }))
*
* const CacheLayer = Layer.effect(Cache, Effect.gen(function*() {
* yield* Effect.log("Connecting to cache")
* return {
* get: Effect.fn("Cache.get")((key: string) => Effect.succeed(`Cache: ${key}`))
* }
* }))
*
* // Use with an existing parent span from Effect.withSpan
* const program = Effect.withSpan("application-startup")(
* Effect.gen(function*() {
* const parentSpan = yield* Tracer.ParentSpan
*
* // Both layers will be children of "application-startup" span
* const AppLayer = Layer.mergeAll(DatabaseLayer, CacheLayer).pipe(
* Layer.withParentSpan(parentSpan)
* )
*
* const context = yield* Layer.build(AppLayer)
* const database = Context.get(context, Database)
* const cache = Context.get(context, Cache)
*
* const dbResult = yield* database.query("SELECT * FROM users")
* const cacheResult = yield* cache.get("user:123")
*
* return { dbResult, cacheResult }
* })
* )
* ```
*
* @category tracing
* @since 2.0.0
*/
(span: Tracer.AnySpan, options?: Tracer.TraceOptions): <A, E, R>(
self: Layer<A, E, R>
) => Layer<A, E, Exclude<R, Tracer.ParentSpan>>
/**
* Wraps a layer so spans created during its construction use the supplied span
* as their parent.
*
* **Details**
*
* Use this to attach layer construction to an existing trace hierarchy. This API
* does not create or end the supplied parent span.
*
* When the supplied span is a native `Span`, layer construction also receives
* diagnostic information that helps associate failures with the layer call site.
* External spans are only installed as the parent span and do not add this
* diagnostic call-site information.
*
* **Example** (Attaching layers to an existing parent span)
*
* ```ts
* import { Context, Effect, Layer, Tracer } from "effect"
*
* class Database extends Context.Service<Database, {
* readonly query: (sql: string) => Effect.Effect<string>
* }>()("Database") {}
*
* class Cache extends Context.Service<Cache, {
* readonly get: (key: string) => Effect.Effect<string | null>
* }>()("Cache") {}
*
* // Create layers
* const DatabaseLayer = Layer.effect(Database, Effect.gen(function*() {
* yield* Effect.log("Connecting to database")
* return {
* query: Effect.fn("Database.query")((sql: string) => Effect.succeed(`DB: ${sql}`))
* }
* }))
*
* const CacheLayer = Layer.effect(Cache, Effect.gen(function*() {
* yield* Effect.log("Connecting to cache")
* return {
* get: Effect.fn("Cache.get")((key: string) => Effect.succeed(`Cache: ${key}`))
* }
* }))
*
* // Use with an existing parent span from Effect.withSpan
* const program = Effect.withSpan("application-startup")(
* Effect.gen(function*() {
* const parentSpan = yield* Tracer.ParentSpan
*
* // Both layers will be children of "application-startup" span
* const AppLayer = Layer.mergeAll(DatabaseLayer, CacheLayer).pipe(
* Layer.withParentSpan(parentSpan)
* )
*
* const context = yield* Layer.build(AppLayer)
* const database = Context.get(context, Database)
* const cache = Context.get(context, Cache)
*
* const dbResult = yield* database.query("SELECT * FROM users")
* const cacheResult = yield* cache.get("user:123")
*
* return { dbResult, cacheResult }
* })
* )
* ```
*
* @category tracing
* @since 2.0.0
*/
<A, E, R>(self: Layer<A, E, R>, span: Tracer.AnySpan, options?: Tracer.TraceOptions): Layer<A, E, Exclude<R, Tracer.ParentSpan>>
} = function() {
const dataFirst = isLayer(arguments[0])
const span: Tracer.AnySpan = dataFirst ? arguments[1] : arguments[0]
let options = dataFirst ? arguments[2] : arguments[1]
let provideStackFrame: <A, E, R>(self: Layer<A, E, R>) => Layer<A, E, R> = identity
if (span._tag === "Span") {
options = internalTracer.addSpanStackTrace(options)
provideStackFrame = provideSpanStackFrame(span.name, options?.captureStackTrace)
}
const parentSpanLayer = parentSpan(span)
if (dataFirst) {
return provide(provideStackFrame(arguments[0]), parentSpanLayer)
}
return (self: Layer<any, any, any>) => provide(provideStackFrame(self), parentSpanLayer)
} as any
const provideSpanStackFrame = (name: string, stack: (() => string | undefined) | undefined) => {
stack = typeof stack === "function" ? stack : constUndefined
return updateService(CurrentStackFrame, (parent) => ({
name,
stack,
parent
}))
}

Xet Storage Details

Size:
154 kB
·
Xet hash:
bea9952c656495d0e10c7e7480e478f986479eb550967356e8ee51b2f3aa62d2

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.