8616 lines
258 KiB
TypeScript
8616 lines
258 KiB
TypeScript
/**
|
||
* Provides low-level building blocks for streaming data through Effect.
|
||
*
|
||
* A `Channel` can read input elements, write output elements, fail with a typed
|
||
* error, and finish with a typed result while managing resources safely.
|
||
* Streams and sinks are built on channels, so most application code uses those
|
||
* higher-level modules instead. This module is useful when implementing stream
|
||
* operators or specialized streaming workflows.
|
||
*
|
||
* @since 2.0.0
|
||
*/
|
||
// @effect-diagnostics returnEffectInGen:off
|
||
import * as Arr from "./Array.ts"
|
||
import * as Cause from "./Cause.ts"
|
||
import * as Chunk from "./Chunk.ts"
|
||
import * as Context from "./Context.ts"
|
||
import * as Effect from "./Effect.ts"
|
||
import * as Exit from "./Exit.ts"
|
||
import * as Fiber from "./Fiber.ts"
|
||
import type * as Filter from "./Filter.ts"
|
||
import type { LazyArg } from "./Function.ts"
|
||
import { constant, constTrue, constVoid, dual, identity as identity_ } from "./Function.ts"
|
||
import { ClockRef, endSpan } from "./internal/effect.ts"
|
||
import { addSpanStackTrace } from "./internal/tracer.ts"
|
||
import * as Iterable from "./Iterable.ts"
|
||
import * as Latch from "./Latch.ts"
|
||
import * as Layer from "./Layer.ts"
|
||
import type { Severity } from "./LogLevel.ts"
|
||
import * as Option from "./Option.ts"
|
||
import type { Pipeable } from "./Pipeable.ts"
|
||
import { pipeArguments } from "./Pipeable.ts"
|
||
import type * as Predicate from "./Predicate.ts"
|
||
import { hasProperty, isTagged } from "./Predicate.ts"
|
||
import * as PubSub from "./PubSub.ts"
|
||
import * as Pull from "./Pull.ts"
|
||
import * as Queue from "./Queue.ts"
|
||
import { TracerTimingEnabled } from "./References.ts"
|
||
import * as Result from "./Result.ts"
|
||
import * as Schedule from "./Schedule.ts"
|
||
import * as Scope from "./Scope.ts"
|
||
import * as Semaphore from "./Semaphore.ts"
|
||
import * as String from "./String.ts"
|
||
import * as Take from "./Take.ts"
|
||
import { ParentSpan, type SpanOptions } from "./Tracer.ts"
|
||
import type * as Types from "./Types.ts"
|
||
import type * as Unify from "./Unify.ts"
|
||
|
||
/**
|
||
* String literal type used as the unique brand for `Channel` values.
|
||
*
|
||
* @category type IDs
|
||
* @since 4.0.0
|
||
*/
|
||
export type TypeId = "~effect/Channel"
|
||
|
||
/**
|
||
* Runtime identifier stored on `Channel` values and used by `isChannel` to
|
||
* recognize them.
|
||
*
|
||
* @category type IDs
|
||
* @since 4.0.0
|
||
*/
|
||
export const TypeId: TypeId = "~effect/Channel"
|
||
|
||
/**
|
||
* Checks whether a value is a `Channel`.
|
||
*
|
||
* **Example** (Checking for channels)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* const channel = Channel.succeed(42)
|
||
* console.log(Channel.isChannel(channel)) // true
|
||
* console.log(Channel.isChannel("not a channel")) // false
|
||
* ```
|
||
*
|
||
* @category guards
|
||
* @since 3.5.4
|
||
*/
|
||
export const isChannel = (
|
||
u: unknown
|
||
): u is Channel<unknown, unknown, unknown, unknown, unknown, unknown, unknown> => hasProperty(u, TypeId)
|
||
|
||
/**
|
||
* A `Channel` is a nexus of I/O operations, which supports both reading and
|
||
* writing. A channel may read values of type `InElem` and write values of type
|
||
* `OutElem`. When the channel finishes, it yields a value of type `OutDone`. A
|
||
* channel may fail with a value of type `OutErr`.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Channels are the foundation of Streams: both streams and sinks are built on
|
||
* channels. Most users shouldn't have to use channels directly, as streams and
|
||
* sinks are much more convenient and cover all common use cases. However, when
|
||
* adding new stream and sink operators, or doing something highly specialized,
|
||
* it may be useful to use channels directly.
|
||
*
|
||
* Channels compose in a variety of ways:
|
||
*
|
||
* - **Piping**: One channel can be piped to another channel, assuming the
|
||
* input type of the second is the same as the output type of the first.
|
||
* - **Sequencing**: The terminal value of one channel can be used to create
|
||
* another channel, and both the first channel and the function that makes
|
||
* the second channel can be composed into a channel.
|
||
* - **Concatenating**: The output of one channel can be used to create other
|
||
* channels, which are all concatenated together. The first channel and the
|
||
* function that makes the other channels can be composed into a channel.
|
||
*
|
||
* **Example** (Typing channels)
|
||
*
|
||
* ```ts
|
||
* import type { Channel } from "effect"
|
||
*
|
||
* // A channel that outputs numbers and requires no environment
|
||
* type NumberChannel = Channel.Channel<number>
|
||
*
|
||
* // A channel that outputs strings, can fail with Error, completes with boolean
|
||
* type StringChannel = Channel.Channel<string, Error, boolean>
|
||
*
|
||
* // A channel with all type parameters specified
|
||
* type FullChannel = Channel.Channel<
|
||
* string, // OutElem - output elements
|
||
* Error, // OutErr - output errors
|
||
* number, // OutDone - completion value
|
||
* number, // InElem - input elements
|
||
* string, // InErr - input errors
|
||
* boolean, // InDone - input completion
|
||
* { db: string } // Env - required environment
|
||
* >
|
||
* ```
|
||
*
|
||
* @category models
|
||
* @since 2.0.0
|
||
*/
|
||
export interface Channel<
|
||
out OutElem,
|
||
out OutErr = never,
|
||
out OutDone = void,
|
||
in InElem = unknown,
|
||
in InErr = unknown,
|
||
in InDone = unknown,
|
||
out Env = never
|
||
> extends Variance<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>, Pipeable {
|
||
[Unify.typeSymbol]?: unknown
|
||
[Unify.unifySymbol]?: ChannelUnify<this>
|
||
[Unify.ignoreSymbol]?: ChannelUnifyIgnore
|
||
}
|
||
|
||
/**
|
||
* Type-level unification support for `Channel` values.
|
||
*
|
||
* **Details**
|
||
*
|
||
* This preserves all `Channel` type parameters when `Unify` normalizes unions
|
||
* or generic return types that include channels. Users normally do not need to
|
||
* reference this interface directly.
|
||
*
|
||
* @category models
|
||
* @since 2.0.0
|
||
*/
|
||
export interface ChannelUnify<A extends { [Unify.typeSymbol]?: any }> extends Effect.EffectUnify<A> {
|
||
Channel?: () => A[Unify.typeSymbol] extends
|
||
| Channel<infer OutElem, infer OutErr, infer OutDone, infer InElem, infer InErr, infer InDone, infer Env>
|
||
| infer _ ? Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
: never
|
||
}
|
||
|
||
/**
|
||
* Marker used by `Unify` while resolving `Channel` values.
|
||
*
|
||
* **Details**
|
||
*
|
||
* It prevents the inherited `Effect` unifier from being selected when the
|
||
* channel-specific unifier should preserve `Channel` input, output, and
|
||
* environment type parameters. Users normally do not need to reference this
|
||
* interface directly.
|
||
*
|
||
* @category models
|
||
* @since 2.0.0
|
||
*/
|
||
export interface ChannelUnifyIgnore {
|
||
Effect?: true
|
||
}
|
||
|
||
type TagsWithReason<E> = {
|
||
[T in Types.Tags<E>]: Types.ReasonTags<Types.ExtractTag<E, T>> extends never ? never : T
|
||
}[Types.Tags<E>]
|
||
|
||
/**
|
||
* Phantom variance marker for the type parameters of `Channel`.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Output element, output error, output done, and environment types are
|
||
* covariant. Input element, input error, and input done types are
|
||
* contravariant. This is type-level machinery and is not used directly at
|
||
* runtime.
|
||
*
|
||
* @category models
|
||
* @since 2.0.0
|
||
*/
|
||
export interface Variance<
|
||
out OutElem,
|
||
out OutErr,
|
||
out OutDone,
|
||
in InElem,
|
||
in InErr,
|
||
in InDone,
|
||
out Env
|
||
> {
|
||
readonly [TypeId]: VarianceStruct<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
}
|
||
/**
|
||
* Structural encoding used by `Variance` to record each `Channel` type
|
||
* parameter's variance.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The `_OutElem`, `_OutErr`, `_OutDone`, and `_Env` fields are covariant; the
|
||
* `_InElem`, `_InErr`, and `_InDone` fields are contravariant. Users normally
|
||
* do not need to reference this interface directly.
|
||
*
|
||
* @category models
|
||
* @since 2.0.0
|
||
*/
|
||
export interface VarianceStruct<
|
||
out OutElem,
|
||
out OutErr,
|
||
out OutDone,
|
||
in InElem,
|
||
in InErr,
|
||
in InDone,
|
||
out Env
|
||
> {
|
||
_Env: Types.Covariant<Env>
|
||
_InErr: Types.Contravariant<InErr>
|
||
_InElem: Types.Contravariant<InElem>
|
||
_InDone: Types.Contravariant<InDone>
|
||
_OutErr: Types.Covariant<OutErr>
|
||
_OutElem: Types.Covariant<OutElem>
|
||
_OutDone: Types.Covariant<OutDone>
|
||
}
|
||
|
||
const ChannelProto = {
|
||
[TypeId]: {
|
||
_Env: identity_,
|
||
_InErr: identity_,
|
||
_InElem: identity_,
|
||
_OutErr: identity_,
|
||
_OutElem: identity_
|
||
},
|
||
pipe() {
|
||
return pipeArguments(this, arguments)
|
||
}
|
||
}
|
||
|
||
// -----------------------------------------------------------------------------
|
||
// Constructors
|
||
// -----------------------------------------------------------------------------
|
||
|
||
/**
|
||
* Creates a `Channel` from a transformation function that operates on upstream pulls.
|
||
*
|
||
* **Example** (Creating channels from transforms)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Effect } from "effect"
|
||
*
|
||
* const channel = Channel.fromTransform((upstream, scope) =>
|
||
* Effect.succeed(upstream)
|
||
* )
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const fromTransform = <OutElem, OutErr, OutDone, InElem, InErr, InDone, EX, EnvX, Env>(
|
||
transform: (
|
||
upstream: Pull.Pull<InElem, InErr, InDone>,
|
||
scope: Scope.Scope
|
||
) => Effect.Effect<Pull.Pull<OutElem, OutErr, OutDone, EnvX>, EX, Env>
|
||
): Channel<
|
||
OutElem,
|
||
Pull.ExcludeDone<OutErr> | EX,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env | EnvX
|
||
> => {
|
||
const self = Object.create(ChannelProto)
|
||
self.transform = (upstream: any, scope: Scope.Scope) =>
|
||
Effect.catchCause(transform(upstream, scope), (cause) => Effect.succeed(Effect.failCause(cause)))
|
||
return self
|
||
}
|
||
|
||
/**
|
||
* Transforms a Channel by applying a function to its Pull implementation.
|
||
*
|
||
* **Example** (Transforming pull behavior)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Effect } from "effect"
|
||
*
|
||
* // Transform a channel by modifying its pull behavior
|
||
* const originalChannel = Channel.fromIterable([1, 2, 3])
|
||
*
|
||
* const transformedChannel = Channel.transformPull(
|
||
* originalChannel,
|
||
* (pull, scope) =>
|
||
* Effect.succeed(
|
||
* Effect.map(pull, (value) => value * 2)
|
||
* )
|
||
* )
|
||
* // Outputs: 2, 4, 6
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const transformPull = <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem2,
|
||
OutErr2,
|
||
OutDone2,
|
||
Env2,
|
||
OutErrX,
|
||
EnvX
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (
|
||
pull: Pull.Pull<OutElem, OutErr, OutDone>,
|
||
scope: Scope.Scope
|
||
) => Effect.Effect<Pull.Pull<OutElem2, OutErr2, OutDone2, Env2>, OutErrX, EnvX>
|
||
): Channel<
|
||
OutElem2,
|
||
Pull.ExcludeDone<OutErr2> | OutErrX,
|
||
OutDone2,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env | Env2 | EnvX
|
||
> => fromTransform((upstream, scope) => Effect.flatMap(toTransform(self)(upstream, scope), (pull) => f(pull, scope)))
|
||
|
||
/**
|
||
* Creates a `Channel` from an `Effect` that produces a `Pull`.
|
||
*
|
||
* **Example** (Creating channels from pulls)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Effect } from "effect"
|
||
*
|
||
* const channel = Channel.fromPull(
|
||
* Effect.succeed(Effect.succeed(42))
|
||
* )
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const fromPull = <OutElem, OutErr, OutDone, EX, EnvX, Env>(
|
||
effect: Effect.Effect<Pull.Pull<OutElem, OutErr, OutDone, EnvX>, EX, Env>
|
||
): Channel<OutElem, Pull.ExcludeDone<OutErr> | EX, OutDone, unknown, unknown, unknown, Env | EnvX> =>
|
||
fromTransform((_, __) => effect) as any
|
||
|
||
/**
|
||
* Creates a `Channel` from a transformation function that operates on upstream
|
||
* pulls, but also provides a forked scope that closes when the resulting
|
||
* Channel completes.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use when building channels that require scoped resource lifecycle management,
|
||
* providing both the channel scope and a forked scope that automatically closes
|
||
* when the channel completes.
|
||
*
|
||
* @see {@link fromTransform} for a simpler transformation without a forked scope
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const fromTransformBracket = <OutElem, OutErr, OutDone, InElem, InErr, InDone, EX, EnvX, Env>(
|
||
f: (
|
||
upstream: Pull.Pull<InElem, InErr, InDone>,
|
||
scope: Scope.Scope,
|
||
forkedScope: Scope.Scope
|
||
) => Effect.Effect<Pull.Pull<OutElem, OutErr, OutDone, EnvX>, EX, Env>
|
||
): Channel<OutElem, Pull.ExcludeDone<OutErr> | EX, OutDone, InElem, InErr, InDone, Env | EnvX> =>
|
||
fromTransform(
|
||
Effect.fnUntraced(function*(upstream, scope) {
|
||
const closableScope = Scope.forkUnsafe(scope)
|
||
const onCause = (cause: Cause.Cause<EX | OutErr | Cause.Done<OutDone>>) =>
|
||
Scope.close(closableScope, Pull.doneExitFromCause(cause))
|
||
const pull = yield* Effect.onError(
|
||
f(upstream, scope, closableScope),
|
||
onCause
|
||
)
|
||
return Effect.onError(pull, onCause)
|
||
})
|
||
)
|
||
|
||
/**
|
||
* Converts a `Channel` back to its underlying transformation function.
|
||
*
|
||
* **Example** (Extracting channel transforms)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* const channel = Channel.succeed(42)
|
||
* const transform = Channel.toTransform(channel)
|
||
* // transform can now be used directly
|
||
* ```
|
||
*
|
||
* @category destructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const toTransform = <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
channel: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
): (
|
||
upstream: Pull.Pull<InElem, InErr, InDone>,
|
||
scope: Scope.Scope
|
||
) => Effect.Effect<Pull.Pull<OutElem, OutErr, OutDone>, never, Env> => (channel as any).transform
|
||
|
||
/**
|
||
* The default chunk size used by channels for batching operations.
|
||
*
|
||
* **Example** (Reading the default chunk size)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* console.log(Channel.DefaultChunkSize) // 4096
|
||
* ```
|
||
*
|
||
* @category constants
|
||
* @since 4.0.0
|
||
*/
|
||
export const DefaultChunkSize: number = 4096
|
||
|
||
const asyncQueue = <A, E = never, R = never>(
|
||
scope: Scope.Scope,
|
||
f: (queue: Queue.Queue<A, E | Cause.Done>) => Effect.Effect<unknown, E, R | Scope.Scope>,
|
||
options?: {
|
||
readonly bufferSize?: number | undefined
|
||
readonly strategy?: "sliding" | "dropping" | "suspend" | undefined
|
||
}
|
||
) =>
|
||
Queue.make<A, E | Cause.Done>({
|
||
capacity: options?.bufferSize,
|
||
strategy: options?.strategy
|
||
}).pipe(
|
||
Effect.tap((queue) => Scope.addFinalizer(scope, Queue.shutdown(queue))),
|
||
Effect.tap((queue) => Effect.forkIn(Scope.provide(f(queue), scope), scope))
|
||
)
|
||
|
||
/**
|
||
* Creates a `Channel` that interacts with a callback function using a queue.
|
||
*
|
||
* **Example** (Creating channels from callbacks)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Effect, Queue } from "effect"
|
||
*
|
||
* const channel = Channel.callback<number>((queue) =>
|
||
* Effect.gen(function*() {
|
||
* yield* Queue.offer(queue, 1)
|
||
* yield* Queue.offer(queue, 2)
|
||
* yield* Queue.offer(queue, 3)
|
||
* })
|
||
* )
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const callback = <A, E = never, R = never>(
|
||
f: (queue: Queue.Queue<A, E | Cause.Done>) => Effect.Effect<unknown, E, R | Scope.Scope>,
|
||
options?: {
|
||
readonly bufferSize?: number | undefined
|
||
readonly strategy?: "sliding" | "dropping" | "suspend" | undefined
|
||
}
|
||
): Channel<A, E, void, unknown, unknown, unknown, Exclude<R, Scope.Scope>> =>
|
||
fromTransform((_, scope) => Effect.map(asyncQueue(scope, f, options), Queue.take))
|
||
|
||
/**
|
||
* Creates a `Channel` that interacts with a callback function using a queue, emitting arrays.
|
||
*
|
||
* **Example** (Creating array channels from callbacks)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Effect, Queue } from "effect"
|
||
*
|
||
* const channel = Channel.callbackArray<number>(Effect.fn(function*(queue) {
|
||
* yield* Queue.offer(queue, 1)
|
||
* yield* Queue.offer(queue, 2)
|
||
* }))
|
||
* // Emits arrays of numbers instead of individual numbers
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const callbackArray = <A, E = never, R = never>(
|
||
f: (queue: Queue.Queue<A, E | Cause.Done>) => Effect.Effect<unknown, E, R | Scope.Scope>,
|
||
options?: {
|
||
readonly bufferSize?: number | undefined
|
||
readonly strategy?: "sliding" | "dropping" | "suspend" | undefined
|
||
}
|
||
): Channel<Arr.NonEmptyReadonlyArray<A>, E, void, unknown, unknown, unknown, Exclude<R, Scope.Scope>> =>
|
||
fromTransform((_, scope) => Effect.map(asyncQueue(scope, f, options), Queue.takeAll))
|
||
|
||
/**
|
||
* Creates a `Channel` that lazily evaluates to another channel.
|
||
*
|
||
* **Example** (Suspending channel creation)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* const channel = Channel.suspend(() => Channel.succeed(42))
|
||
* // The inner channel is not created until the suspended channel is run
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 2.0.0
|
||
*/
|
||
export const suspend = <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
evaluate: LazyArg<Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>>
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env> =>
|
||
fromTransform((upstream, scope) => Effect.suspend(() => toTransform(evaluate())(upstream, scope)))
|
||
|
||
/**
|
||
* Acquires a resource, uses it to build a `Channel`, and guarantees that
|
||
* `release` runs with the channel's `Exit` when the channel completes, fails,
|
||
* or is interrupted.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Acquisition is uninterruptible. If acquisition fails, `use` is not run and
|
||
* `release` is not registered.
|
||
*
|
||
* **Example** (Managing resources with acquire-use-release)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Effect } from "effect"
|
||
*
|
||
* const channel = Channel.acquireUseRelease(
|
||
* Effect.succeed("resource"),
|
||
* (resource) => Channel.succeed(resource.toUpperCase()),
|
||
* (resource, exit) => Effect.log(`Released: ${resource}`)
|
||
* )
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 2.0.0
|
||
*/
|
||
export const acquireUseRelease = <A, E, R, OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
acquire: Effect.Effect<A, E, R>,
|
||
use: (a: A) => Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
release: (a: A, exit: Exit.Exit<OutDone, OutErr>) => Effect.Effect<unknown>
|
||
): Channel<OutElem, OutErr | E, OutDone, InElem, InErr, InDone, Env | R> =>
|
||
fromTransformBracket(
|
||
Effect.fnUntraced(function*(upstream, scope, forkedScope) {
|
||
let option = Option.none<A>()
|
||
yield* Scope.addFinalizerExit(forkedScope, (exit) =>
|
||
Option.isSome(option)
|
||
? release(option.value, exit as any)
|
||
: Effect.void)
|
||
const value = yield* Effect.uninterruptible(acquire)
|
||
option = Option.some(value)
|
||
return yield* toTransform(use(value))(upstream, scope)
|
||
})
|
||
)
|
||
|
||
/**
|
||
* Acquires a resource, emits the acquired value as a single channel element,
|
||
* and registers `release` in the channel scope.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The release action runs when the channel scope closes and receives the scope
|
||
* exit. If acquisition fails, no element is emitted and `release` is not
|
||
* registered.
|
||
*
|
||
* **Example** (Managing resources with acquire-release)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Effect } from "effect"
|
||
*
|
||
* const channel = Channel.acquireRelease(
|
||
* Effect.succeed("resource"),
|
||
* (resource, exit) => Effect.log(`Released: ${resource}`)
|
||
* )
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const acquireRelease: {
|
||
<Z>(
|
||
release: (z: Z, e: Exit.Exit<unknown, unknown>) => Effect.Effect<unknown>
|
||
): <E, R>(self: Effect.Effect<Z, E, R>) => Channel<Z, E, void, unknown, unknown, unknown, R>
|
||
<Z, E, R>(
|
||
self: Effect.Effect<Z, E, R>,
|
||
release: (z: Z, e: Exit.Exit<unknown, unknown>) => Effect.Effect<unknown>
|
||
): Channel<Z, E, void, unknown, unknown, unknown, R>
|
||
} = dual(2, <Z, E, R>(
|
||
self: Effect.Effect<Z, E, R>,
|
||
release: (z: Z, e: Exit.Exit<unknown, unknown>) => Effect.Effect<unknown>
|
||
): Channel<Z, E, void, unknown, unknown, unknown, R> =>
|
||
unwrap(Effect.map(
|
||
Effect.acquireRelease(self, release),
|
||
succeed
|
||
)))
|
||
|
||
/**
|
||
* Creates a `Channel` from an iterator.
|
||
*
|
||
* **Example** (Creating channels from iterators)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* const numbers = [1, 2, 3, 4, 5]
|
||
* const channel = Channel.fromIterator(() => numbers[Symbol.iterator]())
|
||
* // Emits: 1, 2, 3, 4, 5
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const fromIterator = <A, L>(iterator: LazyArg<Iterator<A, L>>): Channel<A, never, L> =>
|
||
fromPull(
|
||
Effect.sync(() => {
|
||
const iter = iterator()
|
||
return Effect.suspend(() => {
|
||
const state = iter.next()
|
||
return state.done ? Cause.done(state.value) : Effect.succeed(state.value)
|
||
})
|
||
})
|
||
)
|
||
|
||
/**
|
||
* Creates a `Channel` that emits all elements from an array.
|
||
*
|
||
* **Example** (Creating channels from arrays)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* const channel = Channel.fromArray([1, 2, 3, 4, 5])
|
||
* // Emits: 1, 2, 3, 4, 5
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const fromArray = <A>(array: ReadonlyArray<A>): Channel<A> =>
|
||
fromPull(Effect.sync(() => {
|
||
let index = 0
|
||
return Effect.suspend(() => index >= array.length ? Cause.done() : Effect.succeed(array[index++]))
|
||
}))
|
||
|
||
/**
|
||
* Creates a `Channel` that emits all elements from a chunk.
|
||
*
|
||
* **Example** (Creating channels from chunks)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Chunk } from "effect"
|
||
*
|
||
* const chunk = Chunk.make(1, 2, 3)
|
||
* const channel = Channel.fromChunk(chunk)
|
||
* // Emits: 1, 2, 3
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const fromChunk = <A>(chunk: Chunk.Chunk<A>): Channel<A> => fromArray(Chunk.toReadonlyArray(chunk))
|
||
|
||
/**
|
||
* Creates a `Channel` from an iterator that emits arrays of elements.
|
||
*
|
||
* **Example** (Batching iterator output)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* // Create a channel from a simple iterator
|
||
* const numberIterator = (): Iterator<number, string> => {
|
||
* let count = 0
|
||
* return {
|
||
* next: () => {
|
||
* if (count < 3) {
|
||
* return { value: count++, done: false }
|
||
* }
|
||
* return { value: "finished", done: true }
|
||
* }
|
||
* }
|
||
* }
|
||
*
|
||
* const channel = Channel.fromIteratorArray(() => numberIterator(), 2)
|
||
* // This will emit arrays: [0, 1], [2], then complete with "finished"
|
||
* ```
|
||
*
|
||
* **Example** (Batching generator output)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* // Create channel from a generator function
|
||
* function* fibonacci(): Generator<number, void, unknown> {
|
||
* let a = 0, b = 1
|
||
* for (let i = 0; i < 5; i++) {
|
||
* yield a
|
||
* ;[a, b] = [b, a + b]
|
||
* }
|
||
* }
|
||
*
|
||
* const fibChannel = Channel.fromIteratorArray(() => fibonacci(), 3)
|
||
* // Emits: [0, 1, 1], [2, 3], then completes
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const fromIteratorArray = <A, L>(
|
||
iterator: LazyArg<Iterator<A, L>>,
|
||
chunkSize = DefaultChunkSize
|
||
): Channel<Arr.NonEmptyReadonlyArray<A>, never, L> =>
|
||
fromPull(
|
||
Effect.sync(() => {
|
||
const iter = iterator()
|
||
let done = Option.none<L>()
|
||
return Effect.suspend(() => {
|
||
if (done._tag === "Some") return Cause.done(done.value)
|
||
const buffer: Array<A> = []
|
||
while (buffer.length < chunkSize) {
|
||
const state = iter.next()
|
||
if (state.done) {
|
||
if (buffer.length === 0) {
|
||
return Cause.done(state.value)
|
||
}
|
||
done = Option.some(state.value)
|
||
break
|
||
}
|
||
buffer.push(state.value)
|
||
}
|
||
return Effect.succeed(buffer as any as Arr.NonEmptyReadonlyArray<A>)
|
||
})
|
||
})
|
||
)
|
||
|
||
/**
|
||
* Creates a `Channel` that emits all elements from an iterable.
|
||
*
|
||
* **Example** (Creating channels from iterables)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* const set = new Set([1, 2, 3])
|
||
* const channel = Channel.fromIterable(set)
|
||
* // Emits: 1, 2, 3
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const fromIterable = <A, L>(iterable: Iterable<A, L>): Channel<A, never, L> =>
|
||
fromIterator(() => iterable[Symbol.iterator]())
|
||
|
||
/**
|
||
* Creates a `Channel` that emits arrays of elements from an iterable.
|
||
*
|
||
* **Example** (Batching iterable output)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* const numbers = [1, 2, 3, 4, 5]
|
||
* const channel = Channel.fromIterableArray(numbers)
|
||
* // Emits arrays like: [1, 2, 3, 4], [5] (based on chunk size)
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const fromIterableArray = <A, L>(
|
||
iterable: Iterable<A, L>,
|
||
chunkSize = DefaultChunkSize
|
||
): Channel<Arr.NonEmptyReadonlyArray<A>, never, L> => fromIteratorArray(() => iterable[Symbol.iterator](), chunkSize)
|
||
|
||
/**
|
||
* Creates a `Channel` that emits a single value and then ends.
|
||
*
|
||
* **Example** (Creating channels that succeed)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* const channel = Channel.succeed(42)
|
||
* // Emits: 42
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 2.0.0
|
||
*/
|
||
export const succeed = <A>(value: A): Channel<A> => fromEffect(Effect.succeed(value))
|
||
|
||
/**
|
||
* Creates a `Channel` that immediately ends with the specified value.
|
||
*
|
||
* **Example** (Ending with a value)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* const channel = Channel.end("done")
|
||
* // Ends immediately with "done", emits nothing
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const end = <A>(value: A): Channel<never, never, A> => fromPull(Effect.succeed(Cause.done(value)))
|
||
|
||
/**
|
||
* Creates a `Channel` that immediately ends with the lazily evaluated value.
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const endSync = <A>(evaluate: LazyArg<A>): Channel<never, never, A> =>
|
||
fromPull(Effect.sync(() => Cause.done(evaluate())))
|
||
|
||
/**
|
||
* Creates a `Channel` that emits a single value computed by a lazy evaluation.
|
||
*
|
||
* **Example** (Computing values lazily)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* let requests = 0
|
||
*
|
||
* const channel = Channel.sync(() => {
|
||
* requests += 1
|
||
* return `request-${requests}`
|
||
* })
|
||
* // Emits "request-1" when the channel runs for the first time
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 2.0.0
|
||
*/
|
||
export const sync = <A>(evaluate: LazyArg<A>): Channel<A> => fromEffect(Effect.sync(evaluate))
|
||
|
||
/**
|
||
* Represents a `Channel` that emits no elements.
|
||
*
|
||
* **Example** (Creating empty channels)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* // Create an empty channel
|
||
* const emptyChannel = Channel.empty
|
||
*
|
||
* // Use empty channel in composition
|
||
* const combined = Channel.concatWith(emptyChannel, () => Channel.succeed(42))
|
||
* // Will immediately provide the second channel's output
|
||
*
|
||
* // Empty channel can be used as a no-op in conditional logic
|
||
* const conditionalChannel = (shouldEmit: boolean) =>
|
||
* shouldEmit ? Channel.succeed("data") : Channel.empty
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const empty: Channel<never> = fromPull(Effect.succeed(Cause.done()))
|
||
|
||
/**
|
||
* Represents a `Channel` that never completes.
|
||
*
|
||
* **Example** (Creating non-terminating channels)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* // Create a channel that never completes
|
||
* const neverChannel = Channel.never
|
||
*
|
||
* // Use in conditional logic
|
||
* const withFallback = Channel.concatWith(
|
||
* neverChannel,
|
||
* () => Channel.succeed("fallback")
|
||
* )
|
||
*
|
||
* // Never channel is useful for testing or as a placeholder
|
||
* const conditionalChannel = (shouldComplete: boolean) =>
|
||
* shouldComplete ? Channel.succeed("done") : Channel.never
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 2.0.0
|
||
*/
|
||
export const never: Channel<never, never, never> = fromPull(Effect.succeed(Effect.never))
|
||
|
||
/**
|
||
* Constructs a channel that fails immediately with the specified error.
|
||
*
|
||
* **Example** (Failing with an error)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* // Create a channel that fails with a string error
|
||
* const failedChannel = Channel.fail("Something went wrong")
|
||
*
|
||
* // Create a channel that fails with a custom error
|
||
* class CustomError extends Error {
|
||
* constructor(message: string) {
|
||
* super(message)
|
||
* this.name = "CustomError"
|
||
* }
|
||
* }
|
||
* const customErrorChannel = Channel.fail(new CustomError("Custom error"))
|
||
*
|
||
* // Use in error handling by piping to another channel
|
||
* const channelWithFallback = Channel.concatWith(
|
||
* failedChannel,
|
||
* () => Channel.succeed("fallback value")
|
||
* )
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 2.0.0
|
||
*/
|
||
export const fail = <E>(error: E): Channel<never, E, never> => fromPull(Effect.succeed(Effect.fail(error)))
|
||
|
||
/**
|
||
* Constructs a channel that fails immediately with the specified lazily
|
||
* evaluated error.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use when the error value should be computed each time the channel runs instead
|
||
* of when the channel is constructed.
|
||
*
|
||
* **Example** (Failing with a lazy error)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* // Create a channel that fails with a lazily computed error
|
||
* const failedChannel = Channel.failSync(() => {
|
||
* console.log("Computing error...")
|
||
* return new Error("Computed at runtime")
|
||
* })
|
||
*
|
||
* // The error computation is deferred until the channel runs
|
||
* let attempts = 0
|
||
* const conditionalError = Channel.failSync(() => {
|
||
* attempts += 1
|
||
* return `Error after attempt ${attempts}`
|
||
* })
|
||
*
|
||
* // Use with expensive error construction
|
||
* const expensiveError = Channel.failSync(() => {
|
||
* const requestId = "request-123"
|
||
* return new Error(`Failed while processing ${requestId}`)
|
||
* })
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 2.0.0
|
||
*/
|
||
export const failSync = <E>(evaluate: LazyArg<E>): Channel<never, E, never> => fromPull(Effect.failSync(evaluate))
|
||
|
||
/**
|
||
* Constructs a channel that fails immediately with the specified `Cause`.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use when the channel failure must preserve a full `Cause`, such as defects,
|
||
* interruptions, or combined failures.
|
||
*
|
||
* **Example** (Failing with causes)
|
||
*
|
||
* ```ts
|
||
* import { Cause, Channel } from "effect"
|
||
*
|
||
* // Create a channel that fails with a simple cause
|
||
* const simpleCause = Cause.fail("Simple error")
|
||
* const failedChannel = Channel.failCause(simpleCause)
|
||
*
|
||
* // Create a channel with a die cause
|
||
* const dieCause = Cause.die(new Error("System error"))
|
||
* const dieFailure = Channel.failCause(dieCause)
|
||
*
|
||
* // Create a channel with a simple fail cause
|
||
* const failCause = Cause.fail("Simple error")
|
||
* const simpleFail = Channel.failCause(failCause)
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 2.0.0
|
||
*/
|
||
export const failCause = <E>(cause: Cause.Cause<E>): Channel<never, E, never> => fromPull(Effect.failCause(cause))
|
||
|
||
/**
|
||
* Constructs a channel that fails immediately with the specified lazily
|
||
* evaluated `Cause`.
|
||
*
|
||
* **Example** (Failing with lazy causes)
|
||
*
|
||
* ```ts
|
||
* import { Cause, Channel } from "effect"
|
||
*
|
||
* // Create a channel that fails with a lazily computed cause
|
||
* let attempts = 0
|
||
* const failedChannel = Channel.failCauseSync(() => {
|
||
* attempts += 1
|
||
* return Cause.fail(`Runtime error after attempt ${attempts}`)
|
||
* })
|
||
*
|
||
* // Create a channel with die cause computation
|
||
* const dieCauseChannel = Channel.failCauseSync(() => {
|
||
* const operation = "load-profile"
|
||
* return Cause.die(`Unexpected defect during ${operation}`)
|
||
* })
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 2.0.0
|
||
*/
|
||
export const failCauseSync = <E>(
|
||
evaluate: LazyArg<Cause.Cause<E>>
|
||
): Channel<never, E, never> => fromPull(Effect.failCauseSync(evaluate))
|
||
|
||
/**
|
||
* Constructs a channel that fails immediately with the specified defect.
|
||
*
|
||
* **Example** (Dying with defects)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* // Create a channel that dies with a string defect
|
||
* const diedChannel = Channel.die("Unrecoverable error")
|
||
*
|
||
* // Create a channel that dies with an Error object
|
||
* const errorDefect = Channel.die(new Error("System failure"))
|
||
*
|
||
* // Die with any value as a defect
|
||
* const objectDefect = Channel.die({
|
||
* code: "SYSTEM_FAILURE",
|
||
* details: "Critical system component failed"
|
||
* })
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const die = (defect: unknown): Channel<never, never, never> => failCause(Cause.die(defect))
|
||
|
||
/**
|
||
* Uses an effect to write a single value to the channel.
|
||
*
|
||
* **Example** (Creating channels from effects)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data, Effect } from "effect"
|
||
*
|
||
* class DatabaseError extends Data.TaggedError("DatabaseError")<{
|
||
* readonly message: string
|
||
* }> {}
|
||
*
|
||
* // Create a channel from a successful effect
|
||
* const successChannel = Channel.fromEffect(
|
||
* Effect.succeed("Hello from effect!")
|
||
* )
|
||
*
|
||
* // Create a channel from an effect that might fail
|
||
* const fetchUserChannel = Channel.fromEffect(
|
||
* Effect.tryPromise({
|
||
* try: () => fetch("/api/user").then((res) => res.json()),
|
||
* catch: (error) => new DatabaseError({ message: String(error) })
|
||
* })
|
||
* )
|
||
*
|
||
* // Channel from effect with async computation
|
||
* const asyncChannel = Channel.fromEffect(
|
||
* Effect.gen(function*() {
|
||
* yield* Effect.sleep("100 millis")
|
||
* return "Async result"
|
||
* })
|
||
* )
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 2.0.0
|
||
*/
|
||
export const fromEffect = <A, E, R>(
|
||
effect: Effect.Effect<A, E, R>
|
||
): Channel<A, Pull.ExcludeDone<E>, void, unknown, unknown, unknown, R> =>
|
||
fromPull(
|
||
Effect.sync(() => {
|
||
let done = false
|
||
return Effect.suspend((): Pull.Pull<A, E, void, R> => {
|
||
if (done) return Cause.done()
|
||
done = true
|
||
return effect
|
||
})
|
||
})
|
||
)
|
||
|
||
/**
|
||
* Creates a channel that evaluates an effect and uses its successful value as
|
||
* the channel's done value without emitting any output elements.
|
||
*
|
||
* **Details**
|
||
*
|
||
* If the effect fails, the channel fails with the effect's error.
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const fromEffectDone = <A, E, R>(
|
||
effect: Effect.Effect<A, E, R>
|
||
): Channel<never, Pull.ExcludeDone<E>, A, unknown, unknown, unknown, R> =>
|
||
fromPull(Effect.succeed(Effect.flatMap(effect, Cause.done)))
|
||
|
||
/**
|
||
* Uses an effect and discards its result.
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const fromEffectDrain = <A, E, R>(
|
||
effect: Effect.Effect<A, E, R>
|
||
): Channel<never, E, void, unknown, unknown, unknown, R> => fromPull(Effect.flatMap(effect, () => Cause.done())) as any
|
||
|
||
/**
|
||
* Creates a channel from an effect that produces a `Take`.
|
||
*
|
||
* **Details**
|
||
*
|
||
* A successful `Take` emits a non-empty array of output elements. A failed
|
||
* `Take` fails the channel. A done `Take` completes the channel with its done
|
||
* value.
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const fromEffectTake = <A, E, Done, E2, R>(
|
||
effect: Effect.Effect<Take.Take<A, E, Done>, E2, R>
|
||
): Channel<Arr.NonEmptyReadonlyArray<A>, E | E2, Done, unknown, unknown, unknown, R> =>
|
||
fromPull(Effect.succeed(Effect.flatMap(effect, Take.toPull)))
|
||
|
||
/**
|
||
* Creates a channel from a queue.
|
||
*
|
||
* **Example** (Creating channels from queues)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data, Effect, Queue } from "effect"
|
||
*
|
||
* class QueueError extends Data.TaggedError("QueueError")<{
|
||
* readonly reason: string
|
||
* }> {}
|
||
*
|
||
* const program = Effect.gen(function*() {
|
||
* // Create a bounded queue
|
||
* const queue = yield* Queue.bounded<string, QueueError>(10)
|
||
*
|
||
* // Add some items to the queue
|
||
* yield* Queue.offer(queue, "item1")
|
||
* yield* Queue.offer(queue, "item2")
|
||
* yield* Queue.offer(queue, "item3")
|
||
*
|
||
* // Create a channel from the queue
|
||
* const channel = Channel.fromQueue(queue)
|
||
*
|
||
* // The channel will read items from the queue one by one
|
||
* return channel
|
||
* })
|
||
*
|
||
* // Sliding queue example
|
||
* const slidingProgram = Effect.gen(function*() {
|
||
* const slidingQueue = yield* Queue.sliding<number, QueueError>(5)
|
||
* yield* Queue.offerAll(slidingQueue, [1, 2, 3, 4, 5, 6])
|
||
* return Channel.fromQueue(slidingQueue)
|
||
* })
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 2.0.0
|
||
*/
|
||
export const fromQueue = <A, E>(
|
||
queue: Queue.Dequeue<A, E>
|
||
): Channel<A, Exclude<E, Cause.Done>> => fromPull(Effect.succeed(Queue.take(queue)))
|
||
|
||
/**
|
||
* Creates a channel from a queue that emits arrays of elements.
|
||
*
|
||
* **Example** (Creating batched channels from queues)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data, Effect, Queue } from "effect"
|
||
*
|
||
* class ProcessingError extends Data.TaggedError("ProcessingError")<{
|
||
* readonly stage: string
|
||
* }> {}
|
||
*
|
||
* const program = Effect.gen(function*() {
|
||
* // Create a queue for batch processing
|
||
* const queue = yield* Queue.bounded<number, ProcessingError>(100)
|
||
*
|
||
* // Fill queue with data
|
||
* yield* Queue.offerAll(queue, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
||
*
|
||
* // Create a channel that reads arrays from the queue
|
||
* const arrayChannel = Channel.fromQueueArray(queue)
|
||
*
|
||
* // This will emit non-empty arrays of elements instead of individual items
|
||
* // Useful for batch processing scenarios
|
||
* return arrayChannel
|
||
* })
|
||
*
|
||
* // High-throughput processing example
|
||
* const batchProcessor = Effect.gen(function*() {
|
||
* const dataQueue = yield* Queue.dropping<string, ProcessingError>(1000)
|
||
* const batchChannel = Channel.fromQueueArray(dataQueue)
|
||
*
|
||
* // Process data in batches for better performance
|
||
* return Channel.map(
|
||
* batchChannel,
|
||
* (batch) => batch.map((item) => item.toUpperCase())
|
||
* )
|
||
* })
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const fromQueueArray = <A, E>(
|
||
queue: Queue.Dequeue<A, E>
|
||
): Channel<Arr.NonEmptyReadonlyArray<A>, Exclude<E, Cause.Done>> => fromPull(Effect.succeed(Queue.takeAll(queue)))
|
||
|
||
/**
|
||
* Creates a channel that forwards upstream input elements, input errors, and
|
||
* the upstream done value unchanged.
|
||
*
|
||
* @category constructors
|
||
* @since 2.0.0
|
||
*/
|
||
export const identity = <Elem, Err, Done>(): Channel<Elem, Err, Done, Elem, Err, Done> =>
|
||
fromTransform((upstream, _scope) => Effect.succeed(upstream))
|
||
|
||
/**
|
||
* Creates a channel from a PubSub subscription.
|
||
*
|
||
* **Example** (Creating channels from subscriptions)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data, Effect, PubSub } from "effect"
|
||
*
|
||
* class SubscriptionError extends Data.TaggedError("SubscriptionError")<{
|
||
* readonly reason: string
|
||
* }> {}
|
||
*
|
||
* const program = Effect.gen(function*() {
|
||
* // Create a PubSub
|
||
* const pubsub = yield* PubSub.bounded<string>(32)
|
||
*
|
||
* // Create a subscription
|
||
* const subscription = yield* PubSub.subscribe(pubsub)
|
||
*
|
||
* // Publish some messages
|
||
* yield* PubSub.publish(pubsub, "Hello")
|
||
* yield* PubSub.publish(pubsub, "World")
|
||
* yield* PubSub.publish(pubsub, "from")
|
||
* yield* PubSub.publish(pubsub, "PubSub")
|
||
*
|
||
* // Create a channel from the subscription
|
||
* const channel = Channel.fromSubscription(subscription)
|
||
*
|
||
* // The channel will receive all published messages
|
||
* return channel
|
||
* })
|
||
*
|
||
* // Real-time notifications example
|
||
* const notificationChannel = Effect.gen(function*() {
|
||
* const eventBus = yield* PubSub.unbounded<{ type: string; payload: any }>()
|
||
* const userSubscription = yield* PubSub.subscribe(eventBus)
|
||
*
|
||
* return Channel.fromSubscription(userSubscription)
|
||
* })
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const fromSubscription = <A>(
|
||
subscription: PubSub.Subscription<A>
|
||
): Channel<A> => fromPull(Effect.succeed(Effect.onInterrupt(PubSub.take(subscription), () => Cause.done())))
|
||
|
||
/**
|
||
* Creates a channel from a PubSub subscription that outputs arrays of values.
|
||
*
|
||
* **Details**
|
||
*
|
||
* This constructor creates a channel that reads from a PubSub subscription and outputs
|
||
* arrays of values in chunks. It's useful when you want to process multiple values at once
|
||
* for better performance.
|
||
*
|
||
* **Example** (Batching subscription values)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data, Effect, PubSub } from "effect"
|
||
*
|
||
* class StreamError extends Data.TaggedError("StreamError")<{
|
||
* readonly message: string
|
||
* }> {}
|
||
*
|
||
* const program = Effect.gen(function*() {
|
||
* const pubsub = yield* PubSub.bounded<number>(16)
|
||
* const subscription = yield* PubSub.subscribe(pubsub)
|
||
*
|
||
* // Create a channel that reads arrays of values
|
||
* const channel = Channel.fromSubscriptionArray(subscription)
|
||
*
|
||
* // Publish some values
|
||
* yield* PubSub.publish(pubsub, 1)
|
||
* yield* PubSub.publish(pubsub, 2)
|
||
* yield* PubSub.publish(pubsub, 3)
|
||
* yield* PubSub.publish(pubsub, 4)
|
||
*
|
||
* // The channel will output arrays like [1, 2, 3] and [4]
|
||
* return channel
|
||
* })
|
||
* ```
|
||
*
|
||
* **Example** (Processing subscription values in batches)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data, Effect, PubSub } from "effect"
|
||
*
|
||
* class BatchProcessingError extends Data.TaggedError("BatchProcessingError")<{
|
||
* readonly reason: string
|
||
* }> {}
|
||
*
|
||
* const batchProcessor = Effect.gen(function*() {
|
||
* const pubsub = yield* PubSub.bounded<string>(32)
|
||
* const subscription = yield* PubSub.subscribe(pubsub)
|
||
*
|
||
* // Create a channel that processes items in batches
|
||
* const batchChannel = Channel.fromSubscriptionArray(subscription)
|
||
*
|
||
* // Transform to process each batch
|
||
* const processedChannel = Channel.map(batchChannel, (batch) => {
|
||
* console.log(`Processing batch of ${batch.length} items:`, batch)
|
||
* return batch.map((item) => item.toUpperCase())
|
||
* })
|
||
*
|
||
* return processedChannel
|
||
* })
|
||
* ```
|
||
*
|
||
* **Example** (Aggregating subscription metrics)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Effect, PubSub } from "effect"
|
||
*
|
||
* const metricsAggregator = Effect.gen(function*() {
|
||
* const metricsPubSub = yield* PubSub.bounded<
|
||
* { timestamp: number; value: number }
|
||
* >(100)
|
||
* const subscription = yield* PubSub.subscribe(metricsPubSub)
|
||
*
|
||
* // Create a channel that collects metrics in chunks
|
||
* const metricsChannel = Channel.fromSubscriptionArray(subscription)
|
||
*
|
||
* // Transform to calculate aggregate statistics
|
||
* const aggregatedChannel = Channel.map(metricsChannel, (metrics) => {
|
||
* const values = metrics.map((m) => m.value)
|
||
* const sum = values.reduce((a, b) => a + b, 0)
|
||
* const avg = sum / values.length
|
||
* const min = Math.min(...values)
|
||
* const max = Math.max(...values)
|
||
*
|
||
* return {
|
||
* count: values.length,
|
||
* sum,
|
||
* average: avg,
|
||
* min,
|
||
* max,
|
||
* firstTimestamp: Math.min(...metrics.map((m) => m.timestamp)),
|
||
* lastTimestamp: Math.max(...metrics.map((m) => m.timestamp))
|
||
* }
|
||
* })
|
||
*
|
||
* return aggregatedChannel
|
||
* })
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const fromSubscriptionArray = <A>(
|
||
subscription: PubSub.Subscription<A>
|
||
): Channel<Arr.NonEmptyReadonlyArray<A>> =>
|
||
fromPull(Effect.succeed(Effect.onInterrupt(PubSub.takeAll(subscription), () => Cause.done())))
|
||
|
||
/**
|
||
* Creates a channel from a PubSub that outputs individual values.
|
||
*
|
||
* **Details**
|
||
*
|
||
* This constructor creates a channel that reads from a PubSub by automatically
|
||
* subscribing to it. The channel outputs individual values as they are published
|
||
* to the PubSub, making it ideal for real-time streaming scenarios.
|
||
*
|
||
* **Example** (Creating channels from PubSubs)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data, Effect, PubSub } from "effect"
|
||
*
|
||
* class StreamError extends Data.TaggedError("StreamError")<{
|
||
* readonly message: string
|
||
* }> {}
|
||
*
|
||
* const program = Effect.gen(function*() {
|
||
* const pubsub = yield* PubSub.bounded<number>(16)
|
||
*
|
||
* // Create a channel that reads individual values
|
||
* const channel = Channel.fromPubSub(pubsub)
|
||
*
|
||
* // Publish some values
|
||
* yield* PubSub.publish(pubsub, 1)
|
||
* yield* PubSub.publish(pubsub, 2)
|
||
* yield* PubSub.publish(pubsub, 3)
|
||
*
|
||
* // The channel will output: 1, 2, 3 (individual values)
|
||
* return channel
|
||
* })
|
||
* ```
|
||
*
|
||
* **Example** (Streaming PubSub notifications)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Effect, PubSub } from "effect"
|
||
*
|
||
* const notificationService = Effect.gen(function*() {
|
||
* const notificationPubSub = yield* PubSub.bounded<string>(50)
|
||
*
|
||
* // Create a channel for real-time notifications
|
||
* const notificationChannel = Channel.fromPubSub(notificationPubSub)
|
||
*
|
||
* // Transform notifications to add timestamps
|
||
* const receivedAt = "2024-01-01T00:00:00.000Z"
|
||
* const timestampedChannel = Channel.map(notificationChannel, (message) => ({
|
||
* message,
|
||
* receivedAt,
|
||
* id: `notification:${message}`
|
||
* }))
|
||
*
|
||
* return timestampedChannel
|
||
* })
|
||
* ```
|
||
*
|
||
* **Example** (Processing PubSub events)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Effect, PubSub } from "effect"
|
||
*
|
||
* interface DomainEvent {
|
||
* readonly type: string
|
||
* readonly payload: unknown
|
||
* readonly timestamp: number
|
||
* }
|
||
*
|
||
* const eventProcessor = Effect.gen(function*() {
|
||
* const eventPubSub = yield* PubSub.bounded<DomainEvent>(100)
|
||
*
|
||
* // Create a channel for processing domain events
|
||
* const eventChannel = Channel.fromPubSub(eventPubSub)
|
||
*
|
||
* // Filter and transform events
|
||
* const processedChannel = Channel.map(eventChannel, (event) => {
|
||
* if (event.type === "user.created") {
|
||
* return {
|
||
* ...event,
|
||
* processed: true,
|
||
* processedAt: event.timestamp + 1
|
||
* }
|
||
* }
|
||
* return event
|
||
* })
|
||
*
|
||
* return processedChannel
|
||
* })
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 2.0.0
|
||
*/
|
||
export const fromPubSub = <A>(
|
||
pubsub: PubSub.PubSub<A>
|
||
): Channel<A> => unwrap(Effect.map(PubSub.subscribe(pubsub), fromSubscription))
|
||
|
||
/**
|
||
* Creates a channel from a PubSub that outputs arrays of values.
|
||
*
|
||
* **Details**
|
||
*
|
||
* This constructor creates a channel that reads from a PubSub by automatically
|
||
* subscribing to it and collecting values into arrays. The channel outputs
|
||
* arrays of values in chunks, making it ideal for batch processing scenarios.
|
||
*
|
||
* **Example** (Batching PubSub values)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data, Effect, PubSub } from "effect"
|
||
*
|
||
* class BatchError extends Data.TaggedError("BatchError")<{
|
||
* readonly message: string
|
||
* }> {}
|
||
*
|
||
* const program = Effect.gen(function*() {
|
||
* const pubsub = yield* PubSub.bounded<number>(16)
|
||
*
|
||
* // Create a channel that reads arrays of values
|
||
* const channel = Channel.fromPubSubArray(pubsub)
|
||
*
|
||
* // Publish some values
|
||
* yield* PubSub.publish(pubsub, 1)
|
||
* yield* PubSub.publish(pubsub, 2)
|
||
* yield* PubSub.publish(pubsub, 3)
|
||
* yield* PubSub.publish(pubsub, 4)
|
||
*
|
||
* // The channel will output arrays like [1, 2, 3] and [4]
|
||
* return channel
|
||
* })
|
||
* ```
|
||
*
|
||
* **Example** (Processing PubSub orders in batches)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Effect, PubSub } from "effect"
|
||
*
|
||
* interface Order {
|
||
* readonly id: string
|
||
* readonly customerId: string
|
||
* readonly items: ReadonlyArray<string>
|
||
* readonly total: number
|
||
* readonly submittedAt: number
|
||
* }
|
||
*
|
||
* const orderBatchProcessor = Effect.gen(function*() {
|
||
* const orderPubSub = yield* PubSub.bounded<Order>(100)
|
||
*
|
||
* // Create a channel that processes orders in batches
|
||
* const orderChannel = Channel.fromPubSubArray(orderPubSub)
|
||
*
|
||
* // Transform to process each batch of orders
|
||
* const processedChannel = Channel.map(orderChannel, (orderBatch) => {
|
||
* const totalRevenue = orderBatch.reduce((sum, order) => sum + order.total, 0)
|
||
* const customerCount = new Set(orderBatch.map((order) =>
|
||
* order.customerId
|
||
* )).size
|
||
*
|
||
* return {
|
||
* batchSize: orderBatch.length,
|
||
* totalRevenue,
|
||
* uniqueCustomers: customerCount,
|
||
* firstSubmittedAt: Math.min(...orderBatch.map((order) => order.submittedAt)),
|
||
* orders: orderBatch
|
||
* }
|
||
* })
|
||
*
|
||
* return processedChannel
|
||
* })
|
||
* ```
|
||
*
|
||
* **Example** (Processing PubSub logs in batches)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Effect, PubSub } from "effect"
|
||
*
|
||
* interface LogEntry {
|
||
* readonly timestamp: number
|
||
* readonly level: "info" | "warn" | "error"
|
||
* readonly message: string
|
||
* readonly source: string
|
||
* }
|
||
*
|
||
* const logAggregator = Effect.gen(function*() {
|
||
* const logPubSub = yield* PubSub.bounded<LogEntry>(500)
|
||
*
|
||
* // Create a channel that collects logs in batches
|
||
* const logChannel = Channel.fromPubSubArray(logPubSub)
|
||
*
|
||
* // Transform to analyze log batches
|
||
* const analysisChannel = Channel.map(logChannel, (logBatch) => {
|
||
* const errorCount = logBatch.filter((log) => log.level === "error").length
|
||
* const warnCount = logBatch.filter((log) => log.level === "warn").length
|
||
* const infoCount = logBatch.filter((log) => log.level === "info").length
|
||
*
|
||
* const timeRange = {
|
||
* start: Math.min(...logBatch.map((log) => log.timestamp)),
|
||
* end: Math.max(...logBatch.map((log) => log.timestamp))
|
||
* }
|
||
*
|
||
* return {
|
||
* batchId: `${timeRange.start}-${timeRange.end}`,
|
||
* totalEntries: logBatch.length,
|
||
* errorCount,
|
||
* warnCount,
|
||
* infoCount,
|
||
* timeRange,
|
||
* sources: [...new Set(logBatch.map((log) => log.source))]
|
||
* }
|
||
* })
|
||
*
|
||
* return analysisChannel
|
||
* })
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const fromPubSubArray = <A>(pubsub: PubSub.PubSub<A>): Channel<Arr.NonEmptyReadonlyArray<A>> =>
|
||
unwrap(Effect.map(PubSub.subscribe(pubsub), fromSubscriptionArray))
|
||
|
||
/**
|
||
* Subscribes to a `PubSub` of `Take` values and exposes them as a channel.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Output `Take` values are emitted as non-empty arrays. Failed `Take` values
|
||
* fail the channel. Done `Take` values complete the channel.
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const fromPubSubTake = <A, E, Done>(
|
||
pubsub: PubSub.PubSub<Take.Take<A, E, Done>>
|
||
): Channel<Arr.NonEmptyReadonlyArray<A>, E, Done> =>
|
||
unwrap(Effect.map(PubSub.subscribe(pubsub), (sub) => fromEffectTake(PubSub.take(sub))))
|
||
|
||
/**
|
||
* Creates a Channel from a Schedule.
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const fromSchedule = <O, E, R>(
|
||
schedule: Schedule.Schedule<O, unknown, E, R>
|
||
): Channel<O, E, O, unknown, unknown, unknown, R> =>
|
||
fromPull(Effect.map(Schedule.toStepWithSleep(schedule), (step) => step(void 0)))
|
||
|
||
/**
|
||
* Creates a channel that pulls values from an `AsyncIterable`.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Each yielded value is emitted as an output element. The iterator's return
|
||
* value becomes the channel's done value. Thrown or rejected iterator errors
|
||
* are converted with `onError`. If the channel scope closes early and the
|
||
* iterator has a `return` method, that method is called.
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const fromAsyncIterable = <A, D, E>(
|
||
iterable: AsyncIterable<A, D>,
|
||
onError: (error: unknown) => E
|
||
): Channel<A, E, D> =>
|
||
fromTransform(Effect.fnUntraced(function*(_, scope) {
|
||
const iter = iterable[Symbol.asyncIterator]()
|
||
if (iter.return) {
|
||
yield* Scope.addFinalizer(scope, Effect.promise(() => iter.return!()))
|
||
}
|
||
return Effect.flatMap(
|
||
Effect.tryPromise({
|
||
try: () => iter.next(),
|
||
catch: onError
|
||
}),
|
||
(result) => result.done ? Cause.done(result.value) : Effect.succeed(result.value)
|
||
)
|
||
}))
|
||
|
||
/**
|
||
* Creates a channel from an `AsyncIterable`, emitting each yielded value as a
|
||
* single-element non-empty array.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The iterator's return value becomes the channel's done value. Thrown or
|
||
* rejected iterator errors are converted with `onError`. If the channel scope
|
||
* closes early and the iterator has a `return` method, that method is called.
|
||
*
|
||
* @category constructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const fromAsyncIterableArray = <A, D, E>(
|
||
iterable: AsyncIterable<A, D>,
|
||
onError: (error: unknown) => E
|
||
): Channel<Arr.NonEmptyReadonlyArray<A>, E, D> => map(fromAsyncIterable(iterable, onError), Arr.of)
|
||
|
||
/**
|
||
* Maps the output of this channel using the specified function.
|
||
*
|
||
* **Example** (Mapping channel output)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data } from "effect"
|
||
*
|
||
* class TransformError extends Data.TaggedError("TransformError")<{
|
||
* readonly reason: string
|
||
* }> {}
|
||
*
|
||
* // Basic mapping of channel values
|
||
* const numbersChannel = Channel.fromIterable([1, 2, 3, 4, 5])
|
||
* const doubledChannel = Channel.map(numbersChannel, (n) => n * 2)
|
||
* // Outputs: 2, 4, 6, 8, 10
|
||
*
|
||
* // Transform string data
|
||
* const wordsChannel = Channel.fromIterable(["hello", "world", "effect"])
|
||
* const upperCaseChannel = Channel.map(wordsChannel, (word) => word.toUpperCase())
|
||
* // Outputs: "HELLO", "WORLD", "EFFECT"
|
||
*
|
||
* // Complex object transformation
|
||
* type User = { id: number; name: string }
|
||
* type UserDisplay = { displayName: string; isActive: boolean }
|
||
*
|
||
* const usersChannel = Channel.fromIterable([
|
||
* { id: 1, name: "Alice" },
|
||
* { id: 2, name: "Bob" }
|
||
* ])
|
||
* const displayChannel = Channel.map(usersChannel, (user): UserDisplay => ({
|
||
* displayName: `User: ${user.name}`,
|
||
* isActive: true
|
||
* }))
|
||
* ```
|
||
*
|
||
* @category sequencing
|
||
* @since 2.0.0
|
||
*/
|
||
export const map: {
|
||
<OutElem, OutElem2>(
|
||
f: (o: OutElem, i: number) => OutElem2
|
||
): <OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem2, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, OutElem2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (o: OutElem, i: number) => OutElem2
|
||
): Channel<OutElem2, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
} = dual(
|
||
2,
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, OutElem2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (o: OutElem, i: number) => OutElem2
|
||
): Channel<OutElem2, OutErr, OutDone, InElem, InErr, InDone, Env> =>
|
||
transformPull(self, (pull) =>
|
||
Effect.sync(() => {
|
||
let i = 0
|
||
return Effect.map(pull, (o) => f(o, i++))
|
||
}))
|
||
)
|
||
|
||
/**
|
||
* Maps the done value of this channel using the specified function.
|
||
*
|
||
* @category sequencing
|
||
* @since 4.0.0
|
||
*/
|
||
export const mapDone: {
|
||
<OutDone, OutDone2>(
|
||
f: (o: OutDone) => OutDone2
|
||
): <OutElem, OutErr, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, OutErr, OutDone2, InElem, InErr, InDone, Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, OutDone2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (o: OutDone) => OutDone2
|
||
): Channel<OutElem, OutErr, OutDone2, InElem, InErr, InDone, Env>
|
||
} = dual(
|
||
2,
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, OutDone2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (o: OutDone) => OutDone2
|
||
): Channel<OutElem, OutErr, OutDone2, InElem, InErr, InDone, Env> => mapDoneEffect(self, (o) => Effect.succeed(f(o)))
|
||
)
|
||
|
||
/**
|
||
* Maps the done value of this channel using the specified effectful function.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use when the terminal done value transformation needs services or can fail,
|
||
* while emitted elements should pass through unchanged.
|
||
*
|
||
* @category sequencing
|
||
* @since 4.0.0
|
||
*/
|
||
export const mapDoneEffect: {
|
||
<OutDone, OutDone2, E, R>(
|
||
f: (o: OutDone) => Effect.Effect<OutDone2, E, R>
|
||
): <OutElem, OutErr, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, OutErr | E, OutDone2, InElem, InErr, InDone, Env | R>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, OutDone2, E, R>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (o: OutDone) => Effect.Effect<OutDone2, E, R>
|
||
): Channel<OutElem, OutErr | E, OutDone2, InElem, InErr, InDone, Env | R>
|
||
} = dual(
|
||
2,
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, OutDone2, E, R>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (o: OutDone) => Effect.Effect<OutDone2, E, R>
|
||
): Channel<OutElem, OutErr | E, OutDone2, InElem, InErr, InDone, Env | R> =>
|
||
transformPull(self, (pull) =>
|
||
Effect.succeed(Pull.catchDone(
|
||
pull,
|
||
(done) => Effect.flatMap(f(done as OutDone), Cause.done)
|
||
)))
|
||
)
|
||
|
||
const concurrencyIsSequential = (
|
||
concurrency: number | "unbounded" | undefined
|
||
) => concurrency === undefined || (concurrency !== "unbounded" && concurrency <= 1)
|
||
|
||
/**
|
||
* Maps each output element with an effectful function, preserving the source
|
||
* channel's done value.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use when transforming each channel output needs an Effect, service
|
||
* dependency, failure channel, or configured concurrency.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The mapping function receives the output element and its zero-based index.
|
||
* By default elements are mapped sequentially. Use `options.concurrency` to
|
||
* map multiple elements concurrently, and `options.unordered` to allow
|
||
* concurrently mapped outputs to be emitted as soon as they complete.
|
||
*
|
||
* **Example** (Mapping channel output with effects)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data, Effect } from "effect"
|
||
*
|
||
* class NetworkError extends Data.TaggedError("NetworkError")<{
|
||
* readonly url: string
|
||
* }> {}
|
||
*
|
||
* // Transform values using effectful operations
|
||
* const urlsChannel = Channel.fromIterable([
|
||
* "/api/users/1",
|
||
* "/api/users/2",
|
||
* "/api/users/3"
|
||
* ])
|
||
*
|
||
* const fetchDataChannel = Channel.mapEffect(
|
||
* urlsChannel,
|
||
* (url) =>
|
||
* Effect.tryPromise({
|
||
* try: () => fetch(url).then((res) => res.json()),
|
||
* catch: () => new NetworkError({ url })
|
||
* })
|
||
* )
|
||
*
|
||
* // Concurrent processing with options
|
||
* const numbersChannel = Channel.fromIterable([1, 2, 3, 4, 5])
|
||
* const processedChannel = Channel.mapEffect(
|
||
* numbersChannel,
|
||
* (n) =>
|
||
* Effect.gen(function*() {
|
||
* yield* Effect.sleep("100 millis") // Simulate async work
|
||
* return n * n
|
||
* }),
|
||
* { concurrency: 3, unordered: true }
|
||
* )
|
||
* ```
|
||
*
|
||
* @category sequencing
|
||
* @since 2.0.0
|
||
*/
|
||
export const mapEffect: {
|
||
<OutElem, OutElem1, OutErr1, Env1>(
|
||
f: (d: OutElem, i: number) => Effect.Effect<OutElem1, OutErr1, Env1>,
|
||
options?: {
|
||
readonly concurrency?: number | "unbounded" | undefined
|
||
readonly unordered?: boolean | undefined
|
||
}
|
||
): <OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem1, OutErr1 | OutErr, OutDone, InElem, InErr, InDone, Env1 | Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, OutElem1, OutErr1, Env1>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (d: OutElem, i: number) => Effect.Effect<OutElem1, OutErr1, Env1>,
|
||
options?: {
|
||
readonly concurrency?: number | "unbounded" | undefined
|
||
readonly unordered?: boolean | undefined
|
||
}
|
||
): Channel<OutElem1, OutErr | OutErr1, OutDone, InElem, InErr, InDone, Env | Env1>
|
||
} = dual(
|
||
(args) => isChannel(args[0]),
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, OutElem1, OutErr1, Env1>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (d: OutElem, i: number) => Effect.Effect<OutElem1, OutErr1, Env1>,
|
||
options?: {
|
||
readonly concurrency?: number | "unbounded" | undefined
|
||
readonly unordered?: boolean | undefined
|
||
}
|
||
): Channel<OutElem1, OutErr | OutErr1, OutDone, InElem, InErr, InDone, Env | Env1> =>
|
||
concurrencyIsSequential(options?.concurrency)
|
||
? mapEffectSequential(self, f)
|
||
: mapEffectConcurrent(self, f, options as any)
|
||
)
|
||
|
||
const mapEffectSequential = <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem2,
|
||
EX,
|
||
RX
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (o: OutElem, i: number) => Effect.Effect<OutElem2, EX, RX>
|
||
): Channel<OutElem2, OutErr | EX, OutDone, InElem, InErr, InDone, Env | RX> =>
|
||
fromTransform((upstream, scope) => {
|
||
let i = 0
|
||
return Effect.map(toTransform(self)(upstream, scope), Effect.flatMap((o) => f(o, i++)))
|
||
})
|
||
|
||
const mapEffectConcurrent = <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem2,
|
||
EX,
|
||
RX
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (o: OutElem, i: number) => Effect.Effect<OutElem2, EX, RX>,
|
||
options: {
|
||
readonly concurrency: number | "unbounded"
|
||
readonly unordered?: boolean | undefined
|
||
}
|
||
): Channel<OutElem2, OutErr | EX, OutDone, InElem, InErr, InDone, Env | RX> =>
|
||
fromTransformBracket(
|
||
Effect.fnUntraced(function*(upstream, scope, forkedScope) {
|
||
let i = 0
|
||
const pull = yield* toTransform(self)(upstream, scope)
|
||
const concurrencyN = options.concurrency === "unbounded"
|
||
? Number.MAX_SAFE_INTEGER
|
||
: options.concurrency
|
||
const queue = yield* Queue.bounded<OutElem2, OutErr | EX | Cause.Done<OutDone>>(0)
|
||
yield* Scope.addFinalizer(forkedScope, Queue.shutdown(queue))
|
||
|
||
const runFork = Effect.runForkWith(yield* Effect.context<RX>())
|
||
const trackFiber = Fiber.runIn(forkedScope)
|
||
|
||
if (options.unordered) {
|
||
const semaphore = Semaphore.makeUnsafe(concurrencyN)
|
||
const release = constant(semaphore.release(1))
|
||
const handle = Effect.matchCauseEffect({
|
||
onFailure: (cause: Cause.Cause<EX>) => Effect.flatMap(Queue.failCause(queue, cause), release),
|
||
onSuccess: (value: OutElem2) => Effect.flatMap(Queue.offer(queue, value), release)
|
||
})
|
||
yield* semaphore.take(1).pipe(
|
||
Effect.flatMap(() => pull),
|
||
Effect.flatMap((value) => {
|
||
trackFiber(runFork(handle(f(value, i++))))
|
||
return Effect.void
|
||
}),
|
||
Effect.forever({ disableYield: true }),
|
||
Effect.catchCause((cause) =>
|
||
semaphore.withPermits(concurrencyN - 1)(
|
||
Queue.failCause(queue, cause)
|
||
)
|
||
),
|
||
Effect.forkIn(forkedScope)
|
||
)
|
||
} else {
|
||
// capacity is n - 2 because
|
||
// - 1 for the offer *after* starting a fiber
|
||
// - 1 for the current processing fiber
|
||
const effects = yield* Queue.bounded<
|
||
Effect.Effect<OutElem2, OutErr | EX | Cause.Done<OutDone>>,
|
||
OutErr | EX | Cause.Done<OutDone>
|
||
>(concurrencyN - 2)
|
||
yield* Scope.addFinalizer(forkedScope, Queue.shutdown(queue))
|
||
|
||
yield* Queue.take(effects).pipe(
|
||
Effect.flatten,
|
||
Effect.flatMap((value) => Queue.offer(queue, value)),
|
||
Effect.forever({ disableYield: true }),
|
||
Effect.catchCause((cause) => Queue.failCause(queue, cause)),
|
||
Effect.forkIn(forkedScope)
|
||
)
|
||
|
||
let errorCause: Cause.Cause<EX> | undefined
|
||
const onExit = (exit: Exit.Exit<OutElem2, EX>) => {
|
||
if (exit._tag === "Success") return
|
||
errorCause = exit.cause
|
||
Queue.failCauseUnsafe(queue, exit.cause)
|
||
}
|
||
yield* pull.pipe(
|
||
Effect.flatMap((value) => {
|
||
if (errorCause) return Effect.failCause(errorCause)
|
||
const fiber = runFork(f(value, i++))
|
||
trackFiber(fiber)
|
||
fiber.addObserver(onExit)
|
||
return Queue.offer(effects, Fiber.join(fiber))
|
||
}),
|
||
Effect.forever({ disableYield: true }),
|
||
Effect.catchCause((cause) =>
|
||
Queue.offer(effects, Exit.failCause(cause)).pipe(
|
||
Effect.andThen(Queue.failCause(effects, cause))
|
||
)
|
||
),
|
||
Effect.forkIn(forkedScope)
|
||
)
|
||
}
|
||
|
||
return Queue.take(queue)
|
||
})
|
||
)
|
||
|
||
/**
|
||
* Returns a new channel which is the same as this one but applies the given
|
||
* function to the input channel’s input elements.
|
||
*
|
||
* @category sequencing
|
||
* @since 2.0.0
|
||
*/
|
||
export const mapInput: {
|
||
<InElem, InElem2, InErr, R = never>(
|
||
f: (i: InElem2) => Effect.Effect<InElem, InErr, R>
|
||
): <OutElem, OutErr, OutDone, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env | R>
|
||
) => Channel<OutElem, OutErr, OutDone, InElem2, InErr, InDone, Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, InElem2, R = never>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (i: InElem2) => Effect.Effect<InElem, InErr, R>
|
||
): Channel<OutElem, OutErr, OutDone, InElem2, InErr, InDone, Env | R>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, InElem2, R = never>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (i: InElem2) => Effect.Effect<InElem, InErr, R>
|
||
): Channel<OutElem, OutErr, OutDone, InElem2, InErr, InDone, Env | R> =>
|
||
fromTransform((upstream, scope) =>
|
||
toTransform(self)(
|
||
Effect.flatMap(upstream, (el) => f(el)) as Pull.Pull<InElem, InErr, InDone>,
|
||
scope
|
||
)
|
||
))
|
||
|
||
/**
|
||
* Returns a new channel which is the same as this one but applies the given
|
||
* function to the input errors.
|
||
*
|
||
* @category sequencing
|
||
* @since 2.0.0
|
||
*/
|
||
export const mapInputError: {
|
||
<InErr, InErr2, R = never>(
|
||
f: (i: InErr2) => Effect.Effect<InErr, InErr, R>
|
||
): <OutElem, OutErr, OutDone, InElem, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env | R>
|
||
) => Channel<OutElem, OutErr, OutDone, InElem, InErr2, InDone, Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, InErr2, R = never>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (i: InErr2) => Effect.Effect<InErr, InErr, R>
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr2, InDone, Env | R>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, InErr2, R = never>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (i: InErr2) => Effect.Effect<InErr, InErr, R>
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr2, InDone, Env | R> =>
|
||
fromTransform((upstream, scope) =>
|
||
toTransform(self)(
|
||
Effect.catch(upstream, (err): Pull.Pull<never, InErr, InDone> => {
|
||
if (Cause.isDone(err)) return Effect.fail(err)
|
||
return Effect.flatMap(f(err), Effect.fail) as Pull.Pull<never, InErr, InDone>
|
||
}),
|
||
scope
|
||
)
|
||
))
|
||
|
||
/**
|
||
* Applies a side effect function to each output element of the channel,
|
||
* returning a new channel that emits the same elements.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The `tap` function allows you to perform side effects (like logging or
|
||
* debugging) on each element emitted by a channel without modifying the
|
||
* elements themselves.
|
||
*
|
||
* **Example** (Tapping channel output)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Console, Data } from "effect"
|
||
*
|
||
* class LogError extends Data.TaggedError("LogError")<{
|
||
* readonly message: string
|
||
* }> {}
|
||
*
|
||
* // Create a channel that outputs numbers
|
||
* const numberChannel = Channel.fromIterable([1, 2, 3])
|
||
*
|
||
* // Tap into each output element to perform side effects
|
||
* const tappedChannel = Channel.tap(
|
||
* numberChannel,
|
||
* (n) => Console.log(`Processing number: ${n}`)
|
||
* )
|
||
*
|
||
* // The channel still outputs the same elements but logs each one
|
||
* // Outputs: 1, 2, 3 (while logging each)
|
||
* ```
|
||
*
|
||
* @category sequencing
|
||
* @since 4.0.0
|
||
*/
|
||
export const tap: {
|
||
<OutElem, X, OutErr1, Env1>(
|
||
f: (d: Types.NoInfer<OutElem>) => Effect.Effect<X, OutErr1, Env1>,
|
||
options?: {
|
||
readonly concurrency?: number | "unbounded" | undefined
|
||
}
|
||
): <OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, OutErr1 | OutErr, OutDone, InElem, InErr, InDone, Env1 | Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, X, OutErr1, Env1>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (d: Types.NoInfer<OutElem>) => Effect.Effect<X, OutErr1, Env1>,
|
||
options?: {
|
||
readonly concurrency?: number | "unbounded" | undefined
|
||
}
|
||
): Channel<OutElem, OutErr | OutErr1, OutDone, InElem, InErr, InDone, Env | Env1>
|
||
} = dual(
|
||
(args) => isChannel(args[0]),
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, X, OutErr1, Env1>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (d: Types.NoInfer<OutElem>) => Effect.Effect<X, OutErr1, Env1>,
|
||
options?: {
|
||
readonly concurrency?: number | "unbounded" | undefined
|
||
}
|
||
): Channel<OutElem, OutErr | OutErr1, OutDone, InElem, InErr, InDone, Env | Env1> =>
|
||
mapEffect(self, (a) => Effect.as(f(a), a), options)
|
||
)
|
||
|
||
/**
|
||
* Maps each output element to a channel and flattens the child channel
|
||
* outputs.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The source channel's done value is preserved. Child channel done values are
|
||
* used only for child-channel completion. By default child channels are run
|
||
* sequentially. Use `options.concurrency` and `options.bufferSize` to run child
|
||
* channels concurrently.
|
||
*
|
||
* **Example** (Flat mapping channel output)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data } from "effect"
|
||
*
|
||
* class ProcessError extends Data.TaggedError("ProcessError")<{
|
||
* readonly cause: string
|
||
* }> {}
|
||
*
|
||
* // Create a channel that outputs numbers
|
||
* const numberChannel = Channel.fromIterable([1, 2, 3])
|
||
*
|
||
* // FlatMap each number to create new channels
|
||
* const flatMappedChannel = Channel.flatMap(
|
||
* numberChannel,
|
||
* (n) =>
|
||
* Channel.fromIterable(Array.from({ length: n }, (_, i) => `item-${n}-${i}`))
|
||
* )
|
||
*
|
||
* // Flattens nested channels into a single stream
|
||
* // Outputs: "item-1-0", "item-2-0", "item-2-1", "item-3-0", "item-3-1", "item-3-2"
|
||
* ```
|
||
*
|
||
* @category sequencing
|
||
* @since 2.0.0
|
||
*/
|
||
export const flatMap: {
|
||
<OutElem, OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>(
|
||
f: (d: OutElem) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
options?: {
|
||
readonly concurrency?: number | "unbounded" | undefined
|
||
readonly bufferSize?: number | undefined
|
||
}
|
||
): <OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<
|
||
OutElem1,
|
||
OutErr1 | OutErr,
|
||
OutDone,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env1 | Env
|
||
>
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (d: OutElem) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
options?: {
|
||
readonly concurrency?: number | "unbounded" | undefined
|
||
readonly bufferSize?: number | undefined
|
||
}
|
||
): Channel<
|
||
OutElem1,
|
||
OutErr | OutErr1,
|
||
OutDone,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
>
|
||
} = dual(
|
||
(args) => isChannel(args[0]),
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (d: OutElem) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
options?: {
|
||
readonly concurrency?: number | "unbounded" | undefined
|
||
readonly bufferSize?: number | undefined
|
||
}
|
||
): Channel<
|
||
OutElem1,
|
||
OutErr | OutErr1,
|
||
OutDone,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
> =>
|
||
concurrencyIsSequential(options?.concurrency)
|
||
? flatMapSequential(self, f)
|
||
: flatMapConcurrent(self, f, options as any)
|
||
)
|
||
|
||
const flatMapSequential = <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (d: OutElem) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): Channel<
|
||
OutElem1,
|
||
OutErr | OutErr1,
|
||
OutDone,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
> =>
|
||
fromTransform((upstream, scope) =>
|
||
Effect.map(toTransform(self)(upstream, scope), (pull) => {
|
||
let childPull: Effect.Effect<OutElem1, OutErr1, Env1> | undefined
|
||
let childScope: Scope.Closeable | undefined
|
||
const makePull: Pull.Pull<
|
||
OutElem1,
|
||
OutErr | OutErr1,
|
||
OutDone,
|
||
Env1
|
||
> = Effect.flatMap(pull, (value) => {
|
||
childScope ??= Scope.forkUnsafe(scope)
|
||
return Effect.flatMapEager(toTransform(f(value))(upstream, childScope), (pull) => {
|
||
childPull = catchHalt(pull) as any
|
||
return childPull!
|
||
})
|
||
})
|
||
const catchHalt = Pull.catchDone((_) => {
|
||
childPull = undefined
|
||
// we can reuse the scope if the only finalizer is the "fork" one
|
||
if (childScope!.state._tag === "Open" && childScope!.state.finalizers.size === 1) {
|
||
return makePull
|
||
}
|
||
const close = Scope.close(childScope!, Exit.void)
|
||
childScope = undefined
|
||
return Effect.flatMap(close, () => makePull)
|
||
})
|
||
return Effect.suspend(() => childPull ?? makePull)
|
||
})
|
||
)
|
||
|
||
const flatMapConcurrent = <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (d: OutElem) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
options: {
|
||
readonly concurrency: number | "unbounded"
|
||
readonly bufferSize?: number | undefined
|
||
}
|
||
): Channel<
|
||
OutElem1,
|
||
OutErr | OutErr1,
|
||
OutDone,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
> => self.pipe(map(f), mergeAll(options))
|
||
|
||
/**
|
||
* Concatenates this channel with another channel created from the terminal value
|
||
* of this channel. The new channel is created using the provided function.
|
||
*
|
||
* **Example** (Concatenating with completion values)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data } from "effect"
|
||
*
|
||
* class ConcatError extends Data.TaggedError("ConcatError")<{
|
||
* readonly reason: string
|
||
* }> {}
|
||
*
|
||
* // Create a channel that outputs numbers and terminates with sum
|
||
* const numberChannel = Channel.fromIterable([1, 2, 3]).pipe(
|
||
* Channel.concatWith((sum: void) => Channel.succeed(`Completed processing`))
|
||
* )
|
||
*
|
||
* // Concatenates additional channel based on completion value
|
||
* // Outputs: 1, 2, 3, then "Completed processing"
|
||
* ```
|
||
*
|
||
* @category sequencing
|
||
* @since 4.0.0
|
||
*/
|
||
export const concatWith: {
|
||
<OutDone, OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>(
|
||
f: (leftover: Types.NoInfer<OutDone>) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): <OutElem, OutErr, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<
|
||
OutElem | OutElem1,
|
||
OutErr1 | OutErr,
|
||
OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env1 | Env
|
||
>
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (leftover: Types.NoInfer<OutDone>) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): Channel<
|
||
OutElem | OutElem1,
|
||
OutErr1 | OutErr,
|
||
OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env1 | Env
|
||
>
|
||
} = dual(2, <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (leftover: Types.NoInfer<OutDone>) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): Channel<
|
||
OutElem | OutElem1,
|
||
OutErr1 | OutErr,
|
||
OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env1 | Env
|
||
> =>
|
||
fromTransform((upstream, scope) =>
|
||
Effect.sync(() => {
|
||
let currentPull: Pull.Pull<OutElem | OutElem1, OutErr1 | OutErr, OutDone1, Env1 | Env> | undefined
|
||
const forkedScope = Scope.forkUnsafe(scope)
|
||
const makePull = Effect.flatMap(toTransform(self)(upstream, forkedScope), (pull) => {
|
||
currentPull = Pull.catchDone(pull, (leftover) => {
|
||
return Scope.close(forkedScope, Exit.void).pipe(
|
||
Effect.flatMap(() => toTransform(f(leftover as OutDone))(upstream, scope)),
|
||
Effect.flatMap((pull) => {
|
||
currentPull = pull
|
||
return pull
|
||
})
|
||
)
|
||
})
|
||
return currentPull
|
||
})
|
||
return Effect.suspend(() => currentPull ?? makePull)
|
||
})
|
||
))
|
||
|
||
/**
|
||
* Concatenates this channel with another channel, so that the second channel
|
||
* starts emitting values after the first channel has completed.
|
||
*
|
||
* **Example** (Concatenating channels)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data } from "effect"
|
||
*
|
||
* class ConcatError extends Data.TaggedError("ConcatError")<{
|
||
* readonly reason: string
|
||
* }> {}
|
||
*
|
||
* // Create two channels
|
||
* const firstChannel = Channel.fromIterable([1, 2, 3])
|
||
* const secondChannel = Channel.fromIterable(["a", "b", "c"])
|
||
*
|
||
* // Concatenate them
|
||
* const concatenatedChannel = Channel.concat(firstChannel, secondChannel)
|
||
*
|
||
* // Outputs: 1, 2, 3, "a", "b", "c"
|
||
* ```
|
||
*
|
||
* @category sequencing
|
||
* @since 4.0.0
|
||
*/
|
||
export const concat: {
|
||
<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>(
|
||
that: Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<
|
||
OutElem | OutElem1,
|
||
OutErr1 | OutErr,
|
||
OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env1 | Env
|
||
>
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
that: Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): Channel<
|
||
OutElem | OutElem1,
|
||
OutErr1 | OutErr,
|
||
OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env1 | Env
|
||
>
|
||
} = dual(2, <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
that: Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): Channel<
|
||
OutElem | OutElem1,
|
||
OutErr1 | OutErr,
|
||
OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env1 | Env
|
||
> => concatWith(self, (_) => that))
|
||
|
||
/**
|
||
* Combines two channels with a stateful pull function.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use to coordinate pulling from two channels when each output element depends
|
||
* on both sides and local state.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The combining function receives the current state and pull functions for the
|
||
* left and right channels. It returns the next output element together with the
|
||
* next state.
|
||
*
|
||
* @category sequencing
|
||
* @since 4.0.0
|
||
*/
|
||
export const combine: {
|
||
<OutElem2, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2, S, OutElem, OutErr, OutDone, A, E, R>(
|
||
that: Channel<OutElem2, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>,
|
||
s: LazyArg<S>,
|
||
f: (
|
||
s: S,
|
||
pullLeft: Pull.Pull<OutElem, OutErr, OutDone>,
|
||
pullRight: Pull.Pull<OutElem2, OutErr2, OutDone2>
|
||
) => Effect.Effect<readonly [A, S], E, R>
|
||
): <InElem, InErr, InDone, Env>(self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>) => Channel<
|
||
A,
|
||
Pull.ExcludeDone<E>,
|
||
Cause.Done.Extract<E>,
|
||
InElem & InElem2,
|
||
InErr & InErr2,
|
||
InDone & InDone2,
|
||
Env | Env2 | R
|
||
>
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem2,
|
||
OutErr2,
|
||
OutDone2,
|
||
InElem2,
|
||
InErr2,
|
||
InDone2,
|
||
Env2,
|
||
S,
|
||
A,
|
||
E,
|
||
R
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
that: Channel<OutElem2, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>,
|
||
s: LazyArg<S>,
|
||
f: (
|
||
s: S,
|
||
pullLeft: Pull.Pull<OutElem, OutErr, OutDone>,
|
||
pullRight: Pull.Pull<OutElem2, OutErr2, OutDone2>
|
||
) => Effect.Effect<readonly [A, S], E, R>
|
||
): Channel<
|
||
A,
|
||
Pull.ExcludeDone<E>,
|
||
Cause.Done.Extract<E>,
|
||
InElem & InElem2,
|
||
InErr & InErr2,
|
||
InDone & InDone2,
|
||
Env | Env2 | R
|
||
>
|
||
} = dual(4, <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem2,
|
||
OutErr2,
|
||
OutDone2,
|
||
InElem2,
|
||
InErr2,
|
||
InDone2,
|
||
Env2,
|
||
S,
|
||
A,
|
||
E,
|
||
R
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
that: Channel<OutElem2, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>,
|
||
s: LazyArg<S>,
|
||
f: (
|
||
s: S,
|
||
pullLeft: Pull.Pull<OutElem, OutErr, OutDone>,
|
||
pullRight: Pull.Pull<OutElem2, OutErr2, OutDone2>
|
||
) => Effect.Effect<readonly [A, S], E, R>
|
||
): Channel<
|
||
A,
|
||
Pull.ExcludeDone<E>,
|
||
Cause.Done.Extract<E>,
|
||
InElem & InElem2,
|
||
InErr & InErr2,
|
||
InDone & InDone2,
|
||
Env | Env2 | R
|
||
> =>
|
||
fromTransform(Effect.fnUntraced(function*(upstream, scope) {
|
||
const leftPull = yield* toTransform(self)(upstream, scope)
|
||
const rightPull = yield* toTransform(that)(upstream, scope)
|
||
let state = s()
|
||
return Effect.suspend(() => {
|
||
const combinedPull = f(state, leftPull, rightPull)
|
||
return Effect.map(combinedPull, ([a, s1]) => {
|
||
state = s1
|
||
return a
|
||
})
|
||
})
|
||
})))
|
||
|
||
/**
|
||
* Runs a fallback channel if this channel completes without emitting any
|
||
* output elements.
|
||
*
|
||
* **Details**
|
||
*
|
||
* If the source emits at least one element, the source is used unchanged. If
|
||
* the source completes before emitting an element, the fallback function
|
||
* receives the source done value and returns the replacement channel.
|
||
*
|
||
* @category sequencing
|
||
* @since 4.0.0
|
||
*/
|
||
export const orElseIfEmpty: {
|
||
<OutDone, OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>(
|
||
f: (leftover: Types.NoInfer<OutDone>) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): <OutElem, OutErr, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<
|
||
OutElem | OutElem1,
|
||
OutErr1 | OutErr,
|
||
OutDone | OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env1 | Env
|
||
>
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (leftover: Types.NoInfer<OutDone>) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): Channel<
|
||
OutElem | OutElem1,
|
||
OutErr1 | OutErr,
|
||
OutDone | OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env1 | Env
|
||
>
|
||
} = dual(2, <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (leftover: Types.NoInfer<OutDone>) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): Channel<
|
||
OutElem | OutElem1,
|
||
OutErr1 | OutErr,
|
||
OutDone | OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env1 | Env
|
||
> =>
|
||
fromTransform((upstream, scope) =>
|
||
Effect.sync(() => {
|
||
let currentPull: Pull.Pull<OutElem | OutElem1, OutErr1 | OutErr, OutDone | OutDone1, Env1 | Env> | undefined
|
||
const forkedScope = Scope.forkUnsafe(scope)
|
||
const makePull = Effect.flatMap(toTransform(self)(upstream, forkedScope), (pull) => {
|
||
const next = pull.pipe(
|
||
Effect.tap(() => {
|
||
currentPull = pull
|
||
return Effect.void
|
||
}),
|
||
Pull.catchDone((leftover) =>
|
||
Scope.close(forkedScope, Exit.succeed(leftover)).pipe(
|
||
Effect.andThen(toTransform(f(leftover as OutDone))(upstream, scope)),
|
||
Effect.flatMap((pull) => {
|
||
currentPull = pull
|
||
return pull
|
||
})
|
||
)
|
||
)
|
||
)
|
||
currentPull = next
|
||
return next
|
||
})
|
||
return Effect.suspend(() => currentPull ?? makePull)
|
||
})
|
||
))
|
||
|
||
/**
|
||
* Flattens a channel of channels.
|
||
*
|
||
* **Example** (Flattening nested channels)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data } from "effect"
|
||
*
|
||
* class FlattenError extends Data.TaggedError("FlattenError")<{
|
||
* readonly cause: string
|
||
* }> {}
|
||
*
|
||
* // Create a channel that outputs channels
|
||
* const nestedChannels = Channel.fromIterable([
|
||
* Channel.fromIterable([1, 2]),
|
||
* Channel.fromIterable([3, 4]),
|
||
* Channel.fromIterable([5, 6])
|
||
* ])
|
||
*
|
||
* // Flatten the nested channels
|
||
* const flattenedChannel = Channel.flatten(nestedChannels)
|
||
*
|
||
* // Outputs: 1, 2, 3, 4, 5, 6
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 2.0.0
|
||
*/
|
||
export const flatten = <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
channels: Channel<
|
||
Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>
|
||
): Channel<OutElem, OutErr | OutErr1, OutDone1, InElem & InElem1, InErr & InErr1, InDone & InDone1, Env | Env1> =>
|
||
flatMap(channels, identity_)
|
||
|
||
/**
|
||
* Flattens a channel that outputs arrays into a channel that outputs individual elements.
|
||
*
|
||
* **Example** (Flattening arrays of channel output)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data } from "effect"
|
||
*
|
||
* class FlattenError extends Data.TaggedError("FlattenError")<{
|
||
* readonly message: string
|
||
* }> {}
|
||
*
|
||
* // Create a channel that outputs arrays
|
||
* const arrayChannel = Channel.fromIterable([
|
||
* [1, 2, 3],
|
||
* [4, 5],
|
||
* [6, 7, 8, 9]
|
||
* ])
|
||
*
|
||
* // Flatten the arrays into individual elements
|
||
* const flattenedChannel = Channel.flattenArray(arrayChannel)
|
||
*
|
||
* // Outputs: 1, 2, 3, 4, 5, 6, 7, 8, 9
|
||
* ```
|
||
*
|
||
* @category transforming
|
||
* @since 4.0.0
|
||
*/
|
||
export const flattenArray = <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>(
|
||
self: Channel<ReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env> =>
|
||
transformPull(self, (pull) => {
|
||
let array: ReadonlyArray<OutElem> | undefined
|
||
let index = 0
|
||
const pump = Effect.suspend(function loop(): Pull.Pull<OutElem, OutErr, OutDone> {
|
||
if (array === undefined) {
|
||
return Effect.flatMap(pull, (array_) => {
|
||
switch (array_.length) {
|
||
case 0:
|
||
return loop()
|
||
case 1:
|
||
return Effect.succeed(array_[0])
|
||
default: {
|
||
array = array_
|
||
return Effect.succeed(array_[index++])
|
||
}
|
||
}
|
||
})
|
||
}
|
||
const next = array[index++]
|
||
if (index >= array.length) {
|
||
array = undefined
|
||
index = 0
|
||
}
|
||
return Effect.succeed(next)
|
||
})
|
||
return Effect.succeed(pump)
|
||
})
|
||
|
||
/**
|
||
* Flattens a channel that emits `Take` values into a channel that emits the
|
||
* `Take` outputs directly.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Output `Take` values are emitted as non-empty arrays. Failed `Take` values
|
||
* fail the returned channel. Done `Take` values complete the returned channel.
|
||
*
|
||
* @category transforming
|
||
* @since 4.0.0
|
||
*/
|
||
export const flattenTake = <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
OutErr2,
|
||
OutDone2,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>(
|
||
self: Channel<Take.Take<OutElem, OutErr, OutDone>, OutErr2, OutDone2, InElem, InErr, InDone, Env>
|
||
): Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr | OutErr2, OutDone, InElem, InErr, InDone, Env> =>
|
||
mapEffectSequential(self, Take.toPull) as any
|
||
|
||
/**
|
||
* Creates a new channel that consumes all output from the source channel
|
||
* but emits nothing, preserving only the completion value.
|
||
*
|
||
* **Example** (Draining channel output)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* // Create a channel that outputs values
|
||
* const sourceChannel = Channel.fromIterable([1, 2, 3, 4, 5])
|
||
*
|
||
* // Drain all output, keeping only the completion
|
||
* const drainedChannel = Channel.drain(sourceChannel)
|
||
*
|
||
* // The channel completes but emits no values
|
||
* // Useful for consuming side effects without collecting output
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 2.0.0
|
||
*/
|
||
export const drain = <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>(
|
||
self: Channel<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>
|
||
): Channel<never, OutErr, OutDone, InElem, InErr, InDone, Env> =>
|
||
transformPull(self, (pull) => Effect.succeed(Effect.forever(pull, { disableYield: true })))
|
||
|
||
/**
|
||
* Repeats this channel according to the provided schedule.
|
||
*
|
||
* @category repetition
|
||
* @since 4.0.0
|
||
*/
|
||
export const repeat: {
|
||
<SO, OutDone, SE, SR>(
|
||
schedule:
|
||
| Schedule.Schedule<SO, Types.NoInfer<OutDone>, SE, SR>
|
||
| ((
|
||
$: <SO, SE, SR>(_: Schedule.Schedule<SO, NoInfer<OutDone>, SE, SR>) => Schedule.Schedule<SO, OutDone, SE, SR>
|
||
) => Schedule.Schedule<SO, Types.NoInfer<OutDone>, SE, SR>)
|
||
): <OutElem, OutErr, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr | SE, OutDone, InElem, InErr, InDone, Env | SR>
|
||
) => Channel<OutElem, OutErr | SE, OutDone, InElem, InErr, InDone, Env | SR>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, SO, SE, SR>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
schedule:
|
||
| Schedule.Schedule<SO, OutDone, SE, SR>
|
||
| ((
|
||
$: <SO, SE, SR>(_: Schedule.Schedule<SO, NoInfer<OutDone>, SE, SR>) => Schedule.Schedule<SO, OutDone, SE, SR>
|
||
) => Schedule.Schedule<SO, Types.NoInfer<OutDone>, SE, SR>)
|
||
): Channel<OutElem, OutErr | SE, OutDone, InElem, InErr, InDone, Env | SR>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, SO, SE, SR>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
schedule:
|
||
| Schedule.Schedule<SO, OutDone, SE, SR>
|
||
| ((
|
||
$: <SO, SE, SR>(_: Schedule.Schedule<SO, NoInfer<OutDone>, SE, SR>) => Schedule.Schedule<SO, OutDone, SE, SR>
|
||
) => Schedule.Schedule<SO, Types.NoInfer<OutDone>, SE, SR>)
|
||
): Channel<OutElem, OutErr | SE, OutDone, InElem, InErr, InDone, Env | SR> =>
|
||
Schedule.toStepWithMetadata(typeof schedule === "function" ? schedule(identity_) : schedule).pipe(
|
||
Effect.map((step) => {
|
||
let meta = Schedule.CurrentMetadata.defaultValue()
|
||
const loop: Channel<
|
||
OutElem,
|
||
OutErr | SE,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env | SR
|
||
> = concatWith(
|
||
provideServiceEffect(self, Schedule.CurrentMetadata, Effect.sync(() => meta)),
|
||
(done) =>
|
||
step(done).pipe(
|
||
Effect.map((meta_) => {
|
||
meta = meta_
|
||
return loop
|
||
}),
|
||
Pull.catchDone(() => Effect.succeed(end(done))),
|
||
unwrap
|
||
)
|
||
)
|
||
return loop
|
||
}),
|
||
unwrap
|
||
))
|
||
|
||
/**
|
||
* Repeats this channel forever.
|
||
*
|
||
* @category repetition
|
||
* @since 4.0.0
|
||
*/
|
||
export const forever = <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
): Channel<OutElem, OutErr, never, InElem, InErr, InDone, Env> => concatWith(self, () => forever(self))
|
||
|
||
/**
|
||
* Runs a schedule step for each output element while preserving the emitted
|
||
* elements.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The schedule receives each output element as input. Schedule delays are
|
||
* applied between emitted elements. If the schedule fails, the returned channel
|
||
* fails. If the schedule finishes, the returned channel completes with the
|
||
* schedule output.
|
||
*
|
||
* @category sequencing
|
||
* @since 4.0.0
|
||
*/
|
||
export const schedule: {
|
||
<SO, OutElem, SE, SR>(
|
||
schedule: Schedule.Schedule<SO, Types.NoInfer<OutElem>, SE, SR>
|
||
): <OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr | SE, OutDone, InElem, InErr, InDone, Env | SR>
|
||
) => Channel<OutElem, OutErr | SE, OutDone | SO, InElem, InErr, InDone, Env | SR>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, SO, SE, SR>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
schedule: Schedule.Schedule<SO, OutElem, SE, SR>
|
||
): Channel<OutElem, OutErr | SE, OutDone | SO, InElem, InErr, InDone, Env | SR>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, SO, SE, SR>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
schedule: Schedule.Schedule<SO, OutElem, SE, SR>
|
||
): Channel<OutElem, OutErr | SE, OutDone | SO, InElem, InErr, InDone, Env | SR> =>
|
||
transformPull(
|
||
self,
|
||
(pull, _scope) =>
|
||
Effect.map(
|
||
Schedule.toStepWithSleep(schedule),
|
||
(step) => {
|
||
const pullWithStep: Pull.Pull<
|
||
OutElem,
|
||
OutErr | SE,
|
||
OutDone | SO,
|
||
SR
|
||
> = Effect.tap(pull, step)
|
||
return pullWithStep
|
||
}
|
||
)
|
||
))
|
||
|
||
/**
|
||
* Filters the output elements of a channel using a predicate function.
|
||
* Elements that don't match the predicate are discarded.
|
||
*
|
||
* **Example** (Filtering channel output)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* // Create a channel with mixed numbers
|
||
* const numbersChannel = Channel.fromIterable([1, 2, 3, 4, 5, 6, 7, 8])
|
||
*
|
||
* // Filter to keep only even numbers
|
||
* const evenChannel = Channel.filter(numbersChannel, (n) => n % 2 === 0)
|
||
* // Outputs: 2, 4, 6, 8
|
||
*
|
||
* // Filter with type refinement
|
||
* const mixedChannel = Channel.fromIterable([1, "hello", 2, "world", 3])
|
||
* const numbersOnlyChannel = Channel.filter(
|
||
* mixedChannel,
|
||
* (value): value is number => typeof value === "number"
|
||
* )
|
||
* // Outputs: 1, 2, 3 (all typed as numbers)
|
||
* ```
|
||
*
|
||
* @category filtering
|
||
* @since 4.0.0
|
||
*/
|
||
export const filter: {
|
||
<OutElem, B extends OutElem>(
|
||
refinement: Predicate.Refinement<OutElem, B>
|
||
): <OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<B, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
<OutElem>(
|
||
predicate: Predicate.Predicate<OutElem>
|
||
): <OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, B extends OutElem>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
refinement: Predicate.Refinement<OutElem, B>
|
||
): Channel<B, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
predicate: Predicate.Predicate<OutElem>
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
predicate: Predicate.Predicate<OutElem>
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env> =>
|
||
fromTransform((upstream, scope) =>
|
||
Effect.map(
|
||
toTransform(self)(upstream, scope),
|
||
(pull) =>
|
||
Effect.flatMap(pull, function loop(elem): Pull.Pull<OutElem, OutErr, OutDone> {
|
||
return predicate(elem)
|
||
? Effect.succeed(elem)
|
||
: Effect.flatMap(pull, loop)
|
||
})
|
||
)
|
||
))
|
||
|
||
/**
|
||
* Filters and maps output elements using a `Filter`.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use to keep only channel output elements accepted by a `Filter` and emit
|
||
* each filter success value.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Successful filter results are emitted as mapped values. Failed filter
|
||
* results are discarded. The source channel's errors and done value are
|
||
* preserved.
|
||
*
|
||
* @see {@link filter} for keeping original output elements with a predicate
|
||
* @see {@link filterMapEffect} for using an effectful `Filter`
|
||
* @see {@link filterMapArray} for filtering arrays of output elements
|
||
*
|
||
* @category filtering
|
||
* @since 4.0.0
|
||
*/
|
||
export const filterMap: {
|
||
<OutElem, B, X>(
|
||
filter: Filter.Filter<OutElem, B, X>
|
||
): <OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<B, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, B, X>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
filter: Filter.Filter<OutElem, B, X>
|
||
): Channel<B, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, B, X>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
filter: Filter.Filter<OutElem, B, X>
|
||
): Channel<B, OutErr, OutDone, InElem, InErr, InDone, Env> =>
|
||
fromTransform((upstream, scope) =>
|
||
Effect.map(
|
||
toTransform(self)(upstream, scope),
|
||
(pull) =>
|
||
Effect.flatMap(pull, function loop(elem): Pull.Pull<B, OutErr, OutDone> {
|
||
const result = filter(elem)
|
||
return Result.isFailure(result)
|
||
? Effect.flatMap(pull, loop)
|
||
: Effect.succeed(result.success)
|
||
})
|
||
)
|
||
))
|
||
|
||
/**
|
||
* Filters output elements with an effectful predicate.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use when the keep/discard decision depends on an Effect or service and
|
||
* predicate failures should fail the returned channel.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Elements for which the predicate succeeds with `true` are emitted. Elements
|
||
* for which the predicate succeeds with `false` are discarded. Predicate
|
||
* failures fail the returned channel.
|
||
*
|
||
* @category filtering
|
||
* @since 4.0.0
|
||
*/
|
||
export const filterEffect: {
|
||
<OutElem, E, R>(
|
||
predicate: (a: OutElem) => Effect.Effect<boolean, E, R>
|
||
): <OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, OutErr | E, OutDone, InElem, InErr, InDone, Env | R>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, E, R>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
predicate: (a: OutElem) => Effect.Effect<boolean, E, R>
|
||
): Channel<OutElem, OutErr | E, OutDone, InElem, InErr, InDone, Env | R>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, E, R>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
predicate: (a: OutElem) => Effect.Effect<boolean, E, R>
|
||
): Channel<OutElem, OutErr | E, OutDone, InElem, InErr, InDone, Env | R> =>
|
||
fromTransform((upstream, scope) =>
|
||
Effect.map(
|
||
toTransform(self)(upstream, scope),
|
||
(pull) =>
|
||
Effect.flatMap(pull, function loop(elem): Pull.Pull<OutElem, OutErr | E, OutDone, R> {
|
||
return Effect.flatMap(
|
||
predicate(elem),
|
||
(passes) =>
|
||
passes
|
||
? Effect.succeed(elem)
|
||
: Effect.flatMap(pull, loop)
|
||
)
|
||
})
|
||
)
|
||
))
|
||
|
||
/**
|
||
* Filters and maps output elements using an effectful `Filter`.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use to apply effectful logic that can discard channel output elements and
|
||
* emit transformed values for the elements that pass.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Successful filter results are emitted as mapped values. Failed filter
|
||
* results are discarded. Failures from the effectful filter fail the returned
|
||
* channel.
|
||
*
|
||
* @see {@link filterMap} for using a synchronous `Filter`
|
||
* @see {@link filterEffect} for effectfully keeping original output elements
|
||
* @see {@link mapEffect} for effectfully transforming every output element
|
||
* @see {@link filterMapArrayEffect} for effectful filtering of array outputs
|
||
*
|
||
* @category filtering
|
||
* @since 4.0.0
|
||
*/
|
||
export const filterMapEffect: {
|
||
<OutElem, B, X, EX, RX>(
|
||
filter: Filter.FilterEffect<OutElem, B, X, EX, RX>
|
||
): <OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<B, OutErr | EX, OutDone, InElem, InErr, InDone, Env | RX>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, B, X, EX, RX>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
filter: Filter.FilterEffect<OutElem, B, X, EX, RX>
|
||
): Channel<B, OutErr | EX, OutDone, InElem, InErr, InDone, Env | RX>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, B, X, EX, RX>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
filter: Filter.FilterEffect<OutElem, B, X, EX, RX>
|
||
): Channel<B, OutErr | EX, OutDone, InElem, InErr, InDone, Env | RX> =>
|
||
fromTransform((upstream, scope) =>
|
||
Effect.map(
|
||
toTransform(self)(upstream, scope),
|
||
(pull) =>
|
||
Effect.flatMap(pull, function loop(elem): Pull.Pull<B, OutErr | EX, OutDone, RX> {
|
||
return Effect.flatMap(
|
||
filter(elem),
|
||
(result) =>
|
||
Result.isFailure(result)
|
||
? Effect.flatMap(pull, loop)
|
||
: Effect.succeed(result.success)
|
||
)
|
||
})
|
||
)
|
||
))
|
||
|
||
/**
|
||
* Filters arrays of elements emitted by a channel, applying the filter
|
||
* to each element within the arrays and only emitting non-empty filtered arrays.
|
||
*
|
||
* **Example** (Filtering array output)
|
||
*
|
||
* ```ts
|
||
* import { Array, Channel } from "effect"
|
||
*
|
||
* const nonEmptyArrayPredicate = Array.isReadonlyArrayNonEmpty
|
||
*
|
||
* // Create a channel that outputs arrays of mixed data
|
||
* const arrayChannel = Channel.fromIterable([
|
||
* Array.make(1, 2, 3, 4, 5),
|
||
* Array.make(6, 7, 8, 9, 10),
|
||
* Array.make(11, 12, 13, 14, 15)
|
||
* ]).pipe(Channel.filter(nonEmptyArrayPredicate))
|
||
*
|
||
* // Filter arrays to keep only even numbers
|
||
* const evenArraysChannel = Channel.filterArray(arrayChannel, (n) => n % 2 === 0)
|
||
* // Outputs: [2, 4], [6, 8, 10], [12, 14]
|
||
* // Note: Only non-empty filtered arrays are emitted
|
||
*
|
||
* // Arrays that would become empty after filtering are discarded entirely
|
||
* const oddChannel = Channel.fromIterable([
|
||
* Array.make(1, 3, 5),
|
||
* Array.make(2, 4),
|
||
* Array.make(7, 9)
|
||
* ]).pipe(Channel.filter(nonEmptyArrayPredicate))
|
||
* const filteredOddChannel = Channel.filterArray(oddChannel, (n) => n % 2 === 0)
|
||
* // Outputs: [2, 4] (the arrays [1,3,5] and [7,9] are discarded)
|
||
* ```
|
||
*
|
||
* @category filtering
|
||
* @since 4.0.0
|
||
*/
|
||
export const filterArray: {
|
||
<OutElem, B extends OutElem>(
|
||
refinement: Predicate.Refinement<OutElem, B>
|
||
): <OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<Arr.NonEmptyReadonlyArray<B>, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
<OutElem>(
|
||
predicate: Predicate.Predicate<Types.NoInfer<OutElem>>
|
||
): <OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, B extends OutElem>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
refinement: Predicate.Refinement<OutElem, B>
|
||
): Channel<Arr.NonEmptyReadonlyArray<B>, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
predicate: Predicate.Predicate<Types.NoInfer<OutElem>>
|
||
): Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
predicate: Predicate.Predicate<Types.NoInfer<OutElem>>
|
||
): Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env> =>
|
||
transformPull(self, (pull) =>
|
||
Effect.succeed(Effect.flatMap(
|
||
pull,
|
||
function loop(arr): Pull.Pull<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone> {
|
||
const passes: Array<OutElem> = []
|
||
for (let i = 0; i < arr.length; i++) {
|
||
if (predicate(arr[i] as Types.NoInfer<OutElem>)) {
|
||
passes.push(arr[i])
|
||
}
|
||
}
|
||
return Arr.isReadonlyArrayNonEmpty(passes)
|
||
? Effect.succeed(passes)
|
||
: Effect.flatMap(pull, loop)
|
||
}
|
||
))))
|
||
|
||
/**
|
||
* Filters and maps each element inside emitted non-empty arrays using a
|
||
* `Filter`.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Successful filter results are kept as mapped values. Failed filter results
|
||
* are removed from the array. Arrays that become empty are discarded.
|
||
*
|
||
* @category filtering
|
||
* @since 4.0.0
|
||
*/
|
||
export const filterMapArray: {
|
||
<OutElem, B, X>(
|
||
filter: Filter.Filter<Types.NoInfer<OutElem>, B, X>
|
||
): <OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<Arr.NonEmptyReadonlyArray<B>, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, B, X>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
filter: Filter.Filter<OutElem, B, X>
|
||
): Channel<Arr.NonEmptyReadonlyArray<B>, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, B, X>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
filter: Filter.Filter<OutElem, B, X>
|
||
): Channel<Arr.NonEmptyReadonlyArray<B>, OutErr, OutDone, InElem, InErr, InDone, Env> =>
|
||
transformPull(self, (pull) =>
|
||
Effect.succeed(Effect.flatMap(
|
||
pull,
|
||
function loop(arr): Pull.Pull<Arr.NonEmptyReadonlyArray<B>, OutErr, OutDone> {
|
||
const passes: Array<B> = []
|
||
for (let i = 0; i < arr.length; i++) {
|
||
const result = filter(arr[i])
|
||
if (Result.isSuccess(result)) {
|
||
passes.push(result.success)
|
||
}
|
||
}
|
||
return Arr.isReadonlyArrayNonEmpty(passes)
|
||
? Effect.succeed(passes)
|
||
: Effect.flatMap(pull, loop)
|
||
}
|
||
))))
|
||
|
||
/**
|
||
* Filters each element inside emitted non-empty arrays with an effectful
|
||
* predicate.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use when filtering array-valued channel outputs requires Effects or services,
|
||
* and arrays that become empty should be skipped.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The predicate receives the element and its index within the array. Elements
|
||
* for which the predicate succeeds with `true` are kept. Arrays that become
|
||
* empty are discarded. Predicate failures fail the returned channel.
|
||
*
|
||
* @category filtering
|
||
* @since 4.0.0
|
||
*/
|
||
export const filterArrayEffect: {
|
||
<OutElem, E, R>(
|
||
predicate: (a: Types.NoInfer<OutElem>, index: number) => Effect.Effect<boolean, E, R>
|
||
): <OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr | E, OutDone, InElem, InErr, InDone, Env | R>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, E, R>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
predicate: (a: Types.NoInfer<OutElem>, index: number) => Effect.Effect<boolean, E, R>
|
||
): Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr | E, OutDone, InElem, InErr, InDone, Env | R>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, E, R>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
predicate: (a: Types.NoInfer<OutElem>, index: number) => Effect.Effect<boolean, E, R>
|
||
): Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr | E, OutDone, InElem, InErr, InDone, Env | R> =>
|
||
transformPull(self, (pull) => {
|
||
const f = Effect.flatMap(pull, (arr) => Effect.filter(arr, predicate))
|
||
return Effect.succeed(Effect.flatMap(
|
||
f,
|
||
function loop(arr): Pull.Pull<Arr.NonEmptyReadonlyArray<OutElem>, OutErr | E, OutDone, R> {
|
||
return Arr.isReadonlyArrayNonEmpty(arr) ? Effect.succeed(arr) : Effect.flatMap(f, loop)
|
||
}
|
||
))
|
||
}))
|
||
|
||
/**
|
||
* Filters and maps each element inside emitted non-empty arrays using an
|
||
* effectful `Filter`.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use when array-valued channel outputs need an effectful filter-map that can
|
||
* fail and can discard arrays that become empty.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Successful filter results are kept as mapped values. Failed filter results
|
||
* are removed from the array. Arrays that become empty are discarded. Failures
|
||
* from the effectful filter fail the returned channel.
|
||
*
|
||
* @category filtering
|
||
* @since 4.0.0
|
||
*/
|
||
export const filterMapArrayEffect: {
|
||
<OutElem, B, X, EX, RX>(
|
||
filter: Filter.FilterEffect<Types.NoInfer<OutElem>, B, X, EX, RX>
|
||
): <OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<Arr.NonEmptyReadonlyArray<B>, OutErr | EX, OutDone, InElem, InErr, InDone, Env | RX>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, B, X, EX, RX>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
filter: Filter.FilterEffect<OutElem, B, X, EX, RX>
|
||
): Channel<Arr.NonEmptyReadonlyArray<B>, OutErr | EX, OutDone, InElem, InErr, InDone, Env | RX>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, B, X, EX, RX>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
filter: Filter.FilterEffect<OutElem, B, X, EX, RX>
|
||
): Channel<Arr.NonEmptyReadonlyArray<B>, OutErr | EX, OutDone, InElem, InErr, InDone, Env | RX> =>
|
||
transformPull(self, (pull) =>
|
||
Effect.succeed(Effect.flatMap(
|
||
pull,
|
||
function loop(arr): Pull.Pull<Arr.NonEmptyReadonlyArray<B>, OutErr | EX, OutDone, RX> {
|
||
return Effect.flatMap(
|
||
Effect.filterMapEffect(arr, filter as any),
|
||
(passes) =>
|
||
Arr.isReadonlyArrayNonEmpty(passes)
|
||
? Effect.succeed(passes as Arr.NonEmptyReadonlyArray<B>)
|
||
: Effect.flatMap(pull, loop)
|
||
)
|
||
}
|
||
))))
|
||
|
||
/**
|
||
* Maps over a channel statefully with an accumulator, where each element can produce multiple output values.
|
||
*
|
||
* **Example** (Mapping with accumulated state)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Effect } from "effect"
|
||
*
|
||
* // Create a channel with numbers
|
||
* const numbersChannel = Channel.fromIterable([1, 2, 3, 4])
|
||
*
|
||
* // Use mapAccum to create running sums and emit both current and sum
|
||
* const runningSum = Channel.mapAccum(
|
||
* numbersChannel,
|
||
* () => 0, // initial accumulator state
|
||
* (sum, current) => {
|
||
* const newSum = sum + current
|
||
* // Return [newState, outputValues]
|
||
* return [newSum, [current, newSum]] as const
|
||
* }
|
||
* )
|
||
* // Outputs: 1, 1, 2, 3, 3, 6, 4, 10
|
||
*
|
||
* // Using with Effect for async processing
|
||
* const asyncMapAccum = Channel.mapAccum(
|
||
* numbersChannel,
|
||
* () => "",
|
||
* (acc, value) =>
|
||
* Effect.gen(function*() {
|
||
* const newAcc = acc + value.toString()
|
||
* return [newAcc, [`${value}-processed`, newAcc]] as const
|
||
* })
|
||
* )
|
||
* ```
|
||
*
|
||
* @category sequencing
|
||
* @since 4.0.0
|
||
*/
|
||
export const mapAccum: {
|
||
<S, OutElem, B, E = never, R = never>(
|
||
initial: LazyArg<S>,
|
||
f: (
|
||
s: S,
|
||
a: Types.NoInfer<OutElem>
|
||
) =>
|
||
| Effect.Effect<readonly [state: S, values: ReadonlyArray<B>], E, R>
|
||
| readonly [state: S, values: ReadonlyArray<B>],
|
||
options?: {
|
||
readonly onHalt?: ((state: S) => Array<B>) | undefined
|
||
}
|
||
): <
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>(self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>) => Channel<
|
||
B,
|
||
OutErr | E,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env | R
|
||
>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, S, B, E = never, R = never>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
initial: LazyArg<S>,
|
||
f: (
|
||
s: S,
|
||
a: Types.NoInfer<OutElem>
|
||
) =>
|
||
| Effect.Effect<readonly [state: S, values: ReadonlyArray<B>], E, R>
|
||
| readonly [state: S, values: ReadonlyArray<B>],
|
||
options?: {
|
||
readonly onHalt?: ((state: S) => Array<B>) | undefined
|
||
}
|
||
): Channel<B, OutErr | E, OutDone, InElem, InErr, InDone, Env | R>
|
||
} = dual(
|
||
(args) => isChannel(args[0]),
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, S, B, E = never, R = never>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
initial: LazyArg<S>,
|
||
f: (
|
||
s: S,
|
||
a: Types.NoInfer<OutElem>
|
||
) =>
|
||
| Effect.Effect<readonly [state: S, values: ReadonlyArray<B>], E, R>
|
||
| readonly [state: S, values: ReadonlyArray<B>],
|
||
options?: {
|
||
readonly onHalt?: ((state: S) => ReadonlyArray<B>) | undefined
|
||
}
|
||
): Channel<B, OutErr | E, OutDone, InElem, InErr, InDone, Env | R> =>
|
||
fromTransform((upstream, scope) =>
|
||
Effect.map(toTransform(self)(upstream, scope), (pull) => {
|
||
let state = initial()
|
||
let current: ReadonlyArray<B> | undefined
|
||
let index = 0
|
||
let cause: Cause.Cause<OutErr | Cause.Done<OutDone>> | undefined
|
||
const pullNext = Effect.matchCauseEffect(pull, {
|
||
onFailure(cause_) {
|
||
cause = cause_
|
||
const b = options?.onHalt && options.onHalt(state)
|
||
return b && b.length > 0
|
||
? Effect.succeed([state, b] as const)
|
||
: Effect.failCause(cause_)
|
||
},
|
||
onSuccess(a): Effect.Effect<readonly [state: S, values: ReadonlyArray<B>], E, R> {
|
||
const b = f(state, a)
|
||
return Arr.isArray(b)
|
||
? Effect.succeed(b as any)
|
||
: b as any
|
||
}
|
||
})
|
||
const pump = Effect.suspend(function loop(): Pull.Pull<B, OutErr | E, OutDone, R> {
|
||
if (current === undefined) {
|
||
if (cause) return Effect.failCause(cause)
|
||
return Effect.flatMap(pullNext, ([newState, values]) => {
|
||
state = newState
|
||
if (values.length === 0) {
|
||
return loop()
|
||
} else if (values.length === 1) {
|
||
return Effect.succeed(values[0])
|
||
}
|
||
current = values
|
||
return loop()
|
||
})
|
||
}
|
||
const next = current[index++]
|
||
if (index >= current.length) {
|
||
current = undefined
|
||
index = 0
|
||
}
|
||
return Effect.succeed(next)
|
||
})
|
||
return pump
|
||
})
|
||
)
|
||
)
|
||
|
||
/**
|
||
* Transforms a channel statefully by scanning over its output with an accumulator function.
|
||
* Emits the intermediate results of the scan operation.
|
||
*
|
||
* **Example** (Scanning channel output)
|
||
*
|
||
* ```ts
|
||
* import { Channel } from "effect"
|
||
*
|
||
* // Create a channel with numbers
|
||
* const numbersChannel = Channel.fromIterable([1, 2, 3, 4, 5])
|
||
*
|
||
* // Scan to create running sum
|
||
* const runningSumChannel = Channel.scan(numbersChannel, 0, (sum, n) => sum + n)
|
||
* // Outputs: 0, 1, 3, 6, 10, 15
|
||
* // Note: emits the initial value and each intermediate result
|
||
*
|
||
* // Scan with string concatenation
|
||
* const wordsChannel = Channel.fromIterable(["hello", "world", "from", "effect"])
|
||
* const sentenceChannel = Channel.scan(
|
||
* wordsChannel,
|
||
* "",
|
||
* (sentence, word) => sentence === "" ? word : `${sentence} ${word}`
|
||
* )
|
||
* // Outputs: "", "hello", "hello world", "hello world from", "hello world from effect"
|
||
* ```
|
||
*
|
||
* @category sequencing
|
||
* @since 4.0.0
|
||
*/
|
||
export const scan: {
|
||
<S, OutElem>(initial: S, f: (s: S, a: Types.NoInfer<OutElem>) => S): <
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>(self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>) => Channel<
|
||
S,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, S>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
initial: S,
|
||
f: (s: S, a: Types.NoInfer<OutElem>) => S
|
||
): Channel<S, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
} = dual(3, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, S>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
initial: S,
|
||
f: (s: S, a: Types.NoInfer<OutElem>) => S
|
||
): Channel<S, OutErr, OutDone, InElem, InErr, InDone, Env> =>
|
||
scanEffect(self, initial, (s, a) => Effect.succeed(f(s, a))))
|
||
|
||
/**
|
||
* Transforms a channel statefully by scanning over its output with an effectful accumulator function.
|
||
* Emits the intermediate results of the scan operation.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use when maintaining accumulated state over channel output requires Effects
|
||
* or can fail, while still emitting each intermediate state.
|
||
*
|
||
* **Example** (Scanning channel output with effects)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data, Effect } from "effect"
|
||
*
|
||
* class ScanError extends Data.TaggedError("ScanError")<{
|
||
* readonly reason: string
|
||
* }> {}
|
||
*
|
||
* // Create a channel with numbers
|
||
* const numbersChannel = Channel.fromIterable([1, 2, 3, 4])
|
||
*
|
||
* // Effectful scan with async operations
|
||
* const asyncScanChannel = Channel.scanEffect(
|
||
* numbersChannel,
|
||
* "",
|
||
* (acc, value) =>
|
||
* Effect.gen(function*() {
|
||
* // Simulate async work
|
||
* yield* Effect.sleep("10 millis")
|
||
* return acc + value.toString()
|
||
* })
|
||
* )
|
||
* // Outputs: "", "1", "12", "123", "1234"
|
||
*
|
||
* // Scan with error handling
|
||
* const errorHandlingScan = Channel.scanEffect(
|
||
* numbersChannel,
|
||
* 0,
|
||
* (sum, n) => {
|
||
* if (n < 0) {
|
||
* return Effect.fail(new ScanError({ reason: "negative number" }))
|
||
* }
|
||
* return Effect.succeed(sum + n)
|
||
* }
|
||
* )
|
||
* ```
|
||
*
|
||
* @category sequencing
|
||
* @since 4.0.0
|
||
*/
|
||
export const scanEffect: {
|
||
<S, OutElem, E, R>(initial: S, f: (s: S, a: Types.NoInfer<OutElem>) => Effect.Effect<S, E, R>): <
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>(self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>) => Channel<
|
||
S,
|
||
OutErr | E,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env | R
|
||
>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, S, E, R>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
initial: S,
|
||
f: (s: S, a: Types.NoInfer<OutElem>) => Effect.Effect<S, E, R>
|
||
): Channel<S, OutErr | E, OutDone, InElem, InErr, InDone, Env | R>
|
||
} = dual(3, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, S, E, R>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
initial: S,
|
||
f: (s: S, a: Types.NoInfer<OutElem>) => Effect.Effect<S, E, R>
|
||
): Channel<S, OutErr | E, OutDone, InElem, InErr, InDone, Env | R> =>
|
||
fromTransform((upstream, scope) =>
|
||
Effect.map(toTransform(self)(upstream, scope), (pull) => {
|
||
let state = initial
|
||
let isFirst = true
|
||
return Effect.suspend(() => {
|
||
if (isFirst) {
|
||
isFirst = false
|
||
return Effect.succeed(state)
|
||
}
|
||
return Effect.map(
|
||
Effect.flatMap(pull, (a) => f(state, a)),
|
||
(newState) => {
|
||
state = newState
|
||
return state
|
||
}
|
||
)
|
||
})
|
||
})
|
||
))
|
||
|
||
/**
|
||
* Catches any cause of failure from the channel and allows recovery by
|
||
* creating a new channel based on the caught cause.
|
||
*
|
||
* **Example** (Recovering from failure causes)
|
||
*
|
||
* ```ts
|
||
* import { Cause, Channel, Data } from "effect"
|
||
*
|
||
* class ProcessError extends Data.TaggedError("ProcessError")<{
|
||
* readonly reason: string
|
||
* }> {}
|
||
*
|
||
* class RecoveryError extends Data.TaggedError("RecoveryError")<{
|
||
* readonly message: string
|
||
* }> {}
|
||
*
|
||
* // Create a failing channel
|
||
* const failingChannel = Channel.fail(
|
||
* new ProcessError({ reason: "network error" })
|
||
* )
|
||
*
|
||
* // Catch the cause and provide recovery
|
||
* const recoveredChannel = Channel.catchCause(failingChannel, (cause) => {
|
||
* if (Cause.hasFails(cause)) {
|
||
* return Channel.succeed("Recovered from failure")
|
||
* }
|
||
* return Channel.succeed("Recovered from interruption")
|
||
* })
|
||
*
|
||
* // The channel recovers gracefully from errors
|
||
* ```
|
||
*
|
||
* @category error handling
|
||
* @since 2.0.0
|
||
*/
|
||
export const catchCause: {
|
||
<OutErr, OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>(
|
||
f: (d: Cause.Cause<OutErr>) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): <
|
||
OutElem,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>(self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>) => Channel<
|
||
OutElem | OutElem1,
|
||
OutErr1,
|
||
OutDone | OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
>
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (d: Cause.Cause<OutErr>) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): Channel<
|
||
OutElem | OutElem1,
|
||
OutErr1,
|
||
OutDone | OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
>
|
||
} = dual(2, <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (d: Cause.Cause<OutErr>) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): Channel<
|
||
OutElem | OutElem1,
|
||
OutErr1,
|
||
OutDone | OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
> =>
|
||
fromTransform((upstream, scope) => {
|
||
let forkedScope = Scope.forkUnsafe(scope)
|
||
return Effect.map(toTransform(self)(upstream, forkedScope), (pull) => {
|
||
let currentPull: Pull.Pull<OutElem | OutElem1, OutErr1, OutDone | OutDone1, Env | Env1> = pull.pipe(
|
||
Effect.catchCause((cause): Pull.Pull<OutElem1, OutErr1, OutDone | OutDone1, Env1> => {
|
||
if (Pull.isDoneCause(cause)) {
|
||
return Effect.failCause(cause as Cause.Cause<Cause.Done<OutDone>>)
|
||
}
|
||
const toClose = forkedScope
|
||
forkedScope = Scope.forkUnsafe(scope)
|
||
return Scope.close(toClose, Exit.failCause(cause)).pipe(
|
||
Effect.andThen(toTransform(f(cause as Cause.Cause<OutErr>))(upstream, forkedScope)),
|
||
Effect.flatMap((childPull) => {
|
||
currentPull = childPull
|
||
return childPull
|
||
})
|
||
)
|
||
})
|
||
)
|
||
return Effect.suspend(() => currentPull)
|
||
})
|
||
}))
|
||
|
||
/**
|
||
* Runs an effect with the full failure `Cause` when the channel fails, then
|
||
* fails the returned channel with the original cause.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use when observing the full channel failure `Cause` is needed without
|
||
* changing successful output or replacing the original cause.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Use this for observing failures, such as logging or metrics. If the observer
|
||
* effect fails, that failure can fail the returned channel.
|
||
*
|
||
* @category error handling
|
||
* @since 4.0.0
|
||
*/
|
||
export const tapCause: {
|
||
<OutErr, A, E, R>(
|
||
f: (d: Cause.Cause<OutErr>) => Effect.Effect<A, E, R>
|
||
): <
|
||
OutElem,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>(self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>) => Channel<
|
||
OutElem,
|
||
OutErr | E,
|
||
OutDone | void,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env | R
|
||
>
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
A,
|
||
E,
|
||
R
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (d: Cause.Cause<OutErr>) => Effect.Effect<A, E, R>
|
||
): Channel<
|
||
OutElem,
|
||
OutErr | E,
|
||
OutDone | void,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env | R
|
||
>
|
||
} = dual(2, <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
A,
|
||
E,
|
||
R
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (d: Cause.Cause<OutErr>) => Effect.Effect<A, E, R>
|
||
): Channel<
|
||
OutElem,
|
||
OutErr | E,
|
||
OutDone | void,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env | R
|
||
> => catchCause(self, (cause) => fromEffectDrain(Effect.flatMap(f(cause), (_) => Effect.failCause(cause)))))
|
||
|
||
/**
|
||
* Catches causes of failure that match a specific filter, allowing
|
||
* conditional error recovery based on the type of failure.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use to recover a channel only when its full `Cause` satisfies a boolean
|
||
* predicate.
|
||
*
|
||
* **Details**
|
||
*
|
||
* When the predicate matches, the recovery function receives the original
|
||
* cause. When it does not match, the returned channel fails with the original
|
||
* cause.
|
||
*
|
||
* @see {@link catchCauseFilter} for selecting causes with a `Filter`
|
||
* @see {@link catchCause} for recovering from every cause
|
||
* @see {@link catchIf} for recovering from typed channel errors
|
||
*
|
||
* @category error handling
|
||
* @since 4.0.0
|
||
*/
|
||
export const catchCauseIf: {
|
||
<
|
||
OutErr,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
predicate: Predicate.Predicate<Cause.Cause<OutErr>>,
|
||
f: (cause: Cause.Cause<OutErr>) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): <
|
||
OutElem,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>(self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>) => Channel<
|
||
OutElem | OutElem1,
|
||
OutErr | OutErr1,
|
||
OutDone | OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
>
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
predicate: Predicate.Predicate<Cause.Cause<OutErr>>,
|
||
f: (cause: Cause.Cause<OutErr>) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): Channel<
|
||
OutElem | OutElem1,
|
||
OutErr | OutErr1,
|
||
OutDone | OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
>
|
||
} = dual(3, <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
predicate: Predicate.Predicate<Cause.Cause<OutErr>>,
|
||
f: (cause: Cause.Cause<OutErr>) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): Channel<
|
||
OutElem | OutElem1,
|
||
OutErr | OutErr1,
|
||
OutDone | OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
> =>
|
||
catchCause(
|
||
self,
|
||
(
|
||
cause
|
||
): Channel<
|
||
OutElem1,
|
||
OutErr | OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
> => {
|
||
return predicate(cause)
|
||
? f(cause)
|
||
: failCause(cause as any)
|
||
}
|
||
))
|
||
|
||
/**
|
||
* Recovers from channel failures whose full `Cause` is selected by a `Filter`.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use when you need to recover a channel only from causes selected by a
|
||
* `Filter`, while giving the recovery both the selected value and the original
|
||
* `Cause`.
|
||
*
|
||
* **Details**
|
||
*
|
||
* When the filter succeeds, the recovery function receives the selected value
|
||
* and the original cause. When the filter fails, the returned channel fails
|
||
* with the residual cause produced by the filter.
|
||
*
|
||
* @see {@link catchCauseIf} for selecting causes with a predicate
|
||
* @see {@link catchFilter} for selecting typed errors with a `Filter`
|
||
* @see {@link catchCause} for recovering from every cause
|
||
*
|
||
* @category error handling
|
||
* @since 4.0.0
|
||
*/
|
||
export const catchCauseFilter: {
|
||
<
|
||
OutErr,
|
||
EB,
|
||
X extends Cause.Cause<any>,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
filter: Filter.Filter<Cause.Cause<OutErr>, EB, X>,
|
||
f: (
|
||
failure: EB,
|
||
cause: Cause.Cause<OutErr>
|
||
) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): <
|
||
OutElem,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>(self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>) => Channel<
|
||
OutElem | OutElem1,
|
||
Cause.Cause.Error<X> | OutErr1,
|
||
OutDone | OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
>
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
EB,
|
||
X extends Cause.Cause<any>,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
filter: Filter.Filter<Cause.Cause<OutErr>, EB, X>,
|
||
f: (
|
||
failure: EB,
|
||
cause: Cause.Cause<OutErr>
|
||
) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): Channel<
|
||
OutElem | OutElem1,
|
||
Cause.Cause.Error<X> | OutErr1,
|
||
OutDone | OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
>
|
||
} = dual(3, <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
EB,
|
||
X extends Cause.Cause<any>,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
filter: Filter.Filter<Cause.Cause<OutErr>, EB, X>,
|
||
f: (
|
||
failure: EB,
|
||
cause: Cause.Cause<OutErr>
|
||
) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): Channel<
|
||
OutElem | OutElem1,
|
||
Cause.Cause.Error<X> | OutErr1,
|
||
OutDone | OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
> =>
|
||
catchCause(
|
||
self,
|
||
(
|
||
cause
|
||
): Channel<
|
||
OutElem1,
|
||
Cause.Cause.Error<X> | OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
> => {
|
||
const result = filter(cause)
|
||
return Result.isFailure(result)
|
||
? failCause(result.failure)
|
||
: f(result.success, cause)
|
||
}
|
||
))
|
||
|
||
const catch_: {
|
||
<OutErr, OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>(
|
||
f: (d: OutErr) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): <
|
||
OutElem,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>(self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>) => Channel<
|
||
OutElem | OutElem1,
|
||
OutErr1,
|
||
OutDone | OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
>
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (d: OutErr) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): Channel<
|
||
OutElem | OutElem1,
|
||
OutErr1,
|
||
OutDone | OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
>
|
||
} = dual(2, <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (d: OutErr) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>
|
||
): Channel<
|
||
OutElem | OutElem1,
|
||
OutErr1,
|
||
OutDone | OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
> => catchCauseFilter(self, Cause.findError, (e) => f(e)))
|
||
|
||
export {
|
||
/**
|
||
* Recovers from typed channel errors by running a fallback channel.
|
||
*
|
||
* @category error handling
|
||
* @since 4.0.0
|
||
*/
|
||
catch_ as catch
|
||
}
|
||
|
||
/**
|
||
* Runs an effect when the channel fails with a typed error, then preserves the
|
||
* original channel failure.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The effect is not run for normal channel completion. If the observer effect
|
||
* fails, that failure can fail the returned channel.
|
||
*
|
||
* @category error handling
|
||
* @since 4.0.0
|
||
*/
|
||
export const tapError: {
|
||
<OutErr, A, E, R>(
|
||
f: (d: OutErr) => Effect.Effect<A, E, R>
|
||
): <
|
||
OutElem,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>(self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>) => Channel<
|
||
OutElem,
|
||
OutErr | E,
|
||
OutDone | void,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env | R
|
||
>
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
A,
|
||
E,
|
||
R
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (d: OutErr) => Effect.Effect<A, E, R>
|
||
): Channel<
|
||
OutElem,
|
||
OutErr | E,
|
||
OutDone | void,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env | R
|
||
>
|
||
} = dual(2, <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
A,
|
||
E,
|
||
R
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (d: OutErr) => Effect.Effect<A, E, R>
|
||
): Channel<
|
||
OutElem,
|
||
OutErr | E,
|
||
OutDone | void,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env | R
|
||
> =>
|
||
transformPull(
|
||
self,
|
||
(pull) =>
|
||
Effect.succeed(Effect.tapError(
|
||
pull,
|
||
(err) => Cause.isDone(err) ? Effect.void : Effect.asVoid(f(err))
|
||
))
|
||
))
|
||
|
||
/**
|
||
* Recovers from typed channel errors that match a predicate or refinement.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use to recover from typed channel errors when a predicate or refinement
|
||
* selects the failures that should switch to a recovery channel.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Matching errors are handled by the recovery function. Non-matching errors
|
||
* are handled by `orElse` when provided. Without `orElse`, non-matching errors
|
||
* are re-failed.
|
||
*
|
||
* @see {@link catch_ catch} for recovering from every typed channel error
|
||
* @see {@link catchFilter} for selecting typed errors with a `Filter`
|
||
* @see {@link catchTag} for selecting tagged typed errors
|
||
* @see {@link catchCauseFilter} for selecting full causes with a `Filter`
|
||
*
|
||
* @category error handling
|
||
* @since 4.0.0
|
||
*/
|
||
export const catchIf: {
|
||
<
|
||
OutErr,
|
||
EB extends OutErr,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1,
|
||
OutElem2 = Types.unassigned,
|
||
OutErr2 = never,
|
||
OutDone2 = never,
|
||
InElem2 = unknown,
|
||
InErr2 = unknown,
|
||
InDone2 = unknown,
|
||
Env2 = never
|
||
>(
|
||
refinement: Predicate.Refinement<OutErr, EB>,
|
||
f: (failure: EB) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
orElse?:
|
||
| ((
|
||
failure: Exclude<OutErr, EB>
|
||
) => Channel<OutElem2, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>)
|
||
| undefined
|
||
): <
|
||
OutElem,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>(self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>) => Channel<
|
||
OutElem | OutElem1 | Exclude<OutElem2, Types.unassigned>,
|
||
OutErr1 | OutErr2 | (OutElem2 extends Types.unassigned ? Exclude<OutErr, EB> : never),
|
||
OutDone | OutDone1 | OutDone2,
|
||
InElem & InElem1 & InElem2,
|
||
InErr & InErr1 & InErr2,
|
||
InDone & InDone1 & InDone2,
|
||
Env | Env1 | Env2
|
||
>
|
||
<
|
||
OutErr,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1,
|
||
OutElem2 = Types.unassigned,
|
||
OutErr2 = never,
|
||
OutDone2 = never,
|
||
InElem2 = unknown,
|
||
InErr2 = unknown,
|
||
InDone2 = unknown,
|
||
Env2 = never
|
||
>(
|
||
predicate: Predicate.Predicate<OutErr>,
|
||
f: (failure: OutErr) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
orElse?:
|
||
| ((
|
||
failure: OutErr
|
||
) => Channel<OutElem2, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>)
|
||
| undefined
|
||
): <
|
||
OutElem,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>(self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>) => Channel<
|
||
OutElem | OutElem1 | Exclude<OutElem2, Types.unassigned>,
|
||
OutErr1 | OutErr2 | (OutElem2 extends Types.unassigned ? OutErr : never),
|
||
OutDone | OutDone1 | OutDone2,
|
||
InElem & InElem1 & InElem2,
|
||
InErr & InErr1 & InErr2,
|
||
InDone & InDone1 & InDone2,
|
||
Env | Env1 | Env2
|
||
>
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
EB extends OutErr,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1,
|
||
OutElem2 = Types.unassigned,
|
||
OutErr2 = never,
|
||
OutDone2 = never,
|
||
InElem2 = unknown,
|
||
InErr2 = unknown,
|
||
InDone2 = unknown,
|
||
Env2 = never
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
refinement: Predicate.Refinement<OutErr, EB>,
|
||
f: (failure: EB) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
orElse?:
|
||
| ((
|
||
failure: Exclude<OutErr, EB>
|
||
) => Channel<OutElem2, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>)
|
||
| undefined
|
||
): Channel<
|
||
OutElem | OutElem1 | Exclude<OutElem2, Types.unassigned>,
|
||
OutErr1 | OutErr2 | (OutElem2 extends Types.unassigned ? Exclude<OutErr, EB> : never),
|
||
OutDone | OutDone1 | OutDone2,
|
||
InElem & InElem1 & InElem2,
|
||
InErr & InErr1 & InErr2,
|
||
InDone & InDone1 & InDone2,
|
||
Env | Env1 | Env2
|
||
>
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1,
|
||
OutElem2 = Types.unassigned,
|
||
OutErr2 = never,
|
||
OutDone2 = never,
|
||
InElem2 = unknown,
|
||
InErr2 = unknown,
|
||
InDone2 = unknown,
|
||
Env2 = never
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
predicate: Predicate.Predicate<OutErr>,
|
||
f: (failure: OutErr) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
orElse?:
|
||
| ((
|
||
failure: OutErr
|
||
) => Channel<OutElem2, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>)
|
||
| undefined
|
||
): Channel<
|
||
OutElem | OutElem1 | Exclude<OutElem2, Types.unassigned>,
|
||
OutErr1 | OutErr2 | (OutElem2 extends Types.unassigned ? OutErr : never),
|
||
OutDone | OutDone1 | OutDone2,
|
||
InElem & InElem1 & InElem2,
|
||
InErr & InErr1 & InErr2,
|
||
InDone & InDone1 & InDone2,
|
||
Env | Env1 | Env2
|
||
>
|
||
} = dual((args) => isChannel(args[0]), <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1,
|
||
OutElem2 = never,
|
||
OutErr2 = OutErr,
|
||
OutDone2 = never,
|
||
InElem2 = unknown,
|
||
InErr2 = unknown,
|
||
InDone2 = unknown,
|
||
Env2 = never
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
predicate: Predicate.Predicate<OutErr>,
|
||
f: (failure: OutErr) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
orElse?:
|
||
| ((
|
||
failure: OutErr
|
||
) => Channel<OutElem2, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>)
|
||
| undefined
|
||
): Channel<
|
||
OutElem | OutElem1 | OutElem2,
|
||
OutErr1 | OutErr2,
|
||
OutDone | OutDone1 | OutDone2,
|
||
InElem & InElem1 & InElem2,
|
||
InErr & InErr1 & InErr2,
|
||
InDone & InDone1 & InDone2,
|
||
Env | Env1 | Env2
|
||
> =>
|
||
catch_(
|
||
self,
|
||
(err): Channel<
|
||
OutElem1 | OutElem2,
|
||
OutErr1 | OutErr2,
|
||
OutDone1 | OutDone2,
|
||
InElem1 & InElem2,
|
||
InErr1 & InErr2,
|
||
InDone1 & InDone2,
|
||
Env1 | Env2
|
||
> => {
|
||
return predicate(err)
|
||
? f(err)
|
||
: orElse
|
||
? orElse(err)
|
||
: fail(err as any) as any
|
||
}
|
||
))
|
||
|
||
/**
|
||
* Recovers from typed channel errors selected by a `Filter`.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use to recover from channel errors with a reusable `Filter` when matching
|
||
* can also narrow or transform the error before choosing the recovery channel.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Successful filter results are handled by the recovery function. Failed
|
||
* filter results are handled by `orElse` when provided. Without `orElse`,
|
||
* failed filter results are re-failed.
|
||
*
|
||
* @see {@link catchIf} for selecting typed errors with a predicate
|
||
* @see {@link catchTag} for selecting tagged typed errors
|
||
* @see {@link catchCauseFilter} for selecting full causes with a `Filter`
|
||
*
|
||
* @category error handling
|
||
* @since 4.0.0
|
||
*/
|
||
export const catchFilter: {
|
||
<
|
||
OutErr,
|
||
EB,
|
||
X,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1,
|
||
OutElem2 = Types.unassigned,
|
||
OutErr2 = never,
|
||
OutDone2 = never,
|
||
InElem2 = unknown,
|
||
InErr2 = unknown,
|
||
InDone2 = unknown,
|
||
Env2 = never
|
||
>(
|
||
filter: Filter.Filter<OutErr, EB, X>,
|
||
f: (failure: EB) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
orElse?:
|
||
| ((
|
||
failure: X
|
||
) => Channel<OutElem2, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>)
|
||
| undefined
|
||
): <
|
||
OutElem,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>(self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>) => Channel<
|
||
OutElem | OutElem1 | Exclude<OutElem2, Types.unassigned>,
|
||
OutErr1 | OutErr2 | (OutElem2 extends Types.unassigned ? X : never),
|
||
OutDone | OutDone1 | OutDone2,
|
||
InElem & InElem1 & InElem2,
|
||
InErr & InErr1 & InErr2,
|
||
InDone & InDone1 & InDone2,
|
||
Env | Env1 | Env2
|
||
>
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
EB,
|
||
X,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1,
|
||
OutElem2 = Types.unassigned,
|
||
OutErr2 = never,
|
||
OutDone2 = never,
|
||
InElem2 = unknown,
|
||
InErr2 = unknown,
|
||
InDone2 = unknown,
|
||
Env2 = never
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
filter: Filter.Filter<OutErr, EB, X>,
|
||
f: (failure: EB) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
orElse?:
|
||
| ((
|
||
failure: X
|
||
) => Channel<OutElem2, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>)
|
||
| undefined
|
||
): Channel<
|
||
OutElem | OutElem1 | Exclude<OutElem2, Types.unassigned>,
|
||
OutErr1 | OutErr2 | (OutElem2 extends Types.unassigned ? X : never),
|
||
OutDone | OutDone1 | OutDone2,
|
||
InElem & InElem1 & InElem2,
|
||
InErr & InErr1 & InErr2,
|
||
InDone & InDone1 & InDone2,
|
||
Env | Env1 | Env2
|
||
>
|
||
} = dual((args) => isChannel(args[0]), <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
EB,
|
||
X,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1,
|
||
OutElem2 = never,
|
||
OutErr2 = X,
|
||
OutDone2 = never,
|
||
InElem2 = unknown,
|
||
InErr2 = unknown,
|
||
InDone2 = unknown,
|
||
Env2 = never
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
filter: Filter.Filter<OutErr, EB, X>,
|
||
f: (failure: EB) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
orElse?:
|
||
| ((
|
||
failure: X
|
||
) => Channel<OutElem2, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>)
|
||
| undefined
|
||
): Channel<
|
||
OutElem | OutElem1 | OutElem2,
|
||
OutErr1 | OutErr2,
|
||
OutDone | OutDone1 | OutDone2,
|
||
InElem & InElem1 & InElem2,
|
||
InErr & InErr1 & InErr2,
|
||
InDone & InDone1 & InDone2,
|
||
Env | Env1 | Env2
|
||
> =>
|
||
catch_(
|
||
self,
|
||
(err): Channel<
|
||
OutElem1 | OutElem2,
|
||
OutErr1 | OutErr2,
|
||
OutDone1 | OutDone2,
|
||
InElem1 & InElem2,
|
||
InErr1 & InErr2,
|
||
InDone1 & InDone2,
|
||
Env1 | Env2
|
||
> => {
|
||
const result = filter(err)
|
||
return Result.isFailure(result)
|
||
? orElse
|
||
? orElse(result.failure)
|
||
: fail(result.failure as any) as any
|
||
: f(result.success)
|
||
}
|
||
))
|
||
|
||
/**
|
||
* Recovers from tagged channel errors whose `_tag` matches one or more tags.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Matching tagged errors are handled by the recovery function. Non-matching
|
||
* errors are handled by `orElse` when provided. Without `orElse`,
|
||
* non-matching errors are re-failed.
|
||
*
|
||
* @category error handling
|
||
* @since 4.0.0
|
||
*/
|
||
export const catchTag: {
|
||
<
|
||
OutErr,
|
||
const K extends Types.Tags<OutErr> | Arr.NonEmptyReadonlyArray<Types.Tags<OutErr>>,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1,
|
||
OutElem2 = Types.unassigned,
|
||
OutErr2 = never,
|
||
OutDone2 = never,
|
||
InElem2 = unknown,
|
||
InErr2 = unknown,
|
||
InDone2 = unknown,
|
||
Env2 = never
|
||
>(
|
||
k: K,
|
||
f: (
|
||
e: Types.ExtractTag<NoInfer<OutErr>, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>
|
||
) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
orElse?:
|
||
| ((
|
||
e: Types.ExcludeTag<NoInfer<OutErr>, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>
|
||
) => Channel<OutElem2, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>)
|
||
| undefined
|
||
): <
|
||
OutElem,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>(self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>) => Channel<
|
||
OutElem | OutElem1 | Exclude<OutElem2, Types.unassigned>,
|
||
| OutErr1
|
||
| OutErr2
|
||
| (OutElem2 extends Types.unassigned
|
||
? Types.ExcludeTag<OutErr, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>
|
||
: never),
|
||
OutDone | OutDone1 | OutDone2,
|
||
InElem & InElem1 & InElem2,
|
||
InErr & InErr1 & InErr2,
|
||
InDone & InDone1 & InDone2,
|
||
Env | Env1 | Env2
|
||
>
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
const K extends Types.Tags<OutErr> | Arr.NonEmptyReadonlyArray<Types.Tags<OutErr>>,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1,
|
||
OutElem2 = Types.unassigned,
|
||
OutErr2 = never,
|
||
OutDone2 = never,
|
||
InElem2 = unknown,
|
||
InErr2 = unknown,
|
||
InDone2 = unknown,
|
||
Env2 = never
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
k: K,
|
||
f: (
|
||
e: Types.ExtractTag<NoInfer<OutErr>, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>
|
||
) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
orElse?:
|
||
| ((
|
||
e: Types.ExcludeTag<NoInfer<OutErr>, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>
|
||
) => Channel<OutElem2, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>)
|
||
| undefined
|
||
): Channel<
|
||
OutElem | OutElem1 | Exclude<OutElem2, Types.unassigned>,
|
||
| OutErr1
|
||
| OutErr2
|
||
| (OutElem2 extends Types.unassigned
|
||
? Types.ExcludeTag<OutErr, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>
|
||
: never),
|
||
OutDone | OutDone1 | OutDone2,
|
||
InElem & InElem1 & InElem2,
|
||
InErr & InErr1 & InErr2,
|
||
InDone & InDone1 & InDone2,
|
||
Env | Env1 | Env2
|
||
>
|
||
} = dual((args) => isChannel(args[0]), <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
const K extends Types.Tags<OutErr> | Arr.NonEmptyReadonlyArray<Types.Tags<OutErr>>,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1,
|
||
OutElem2 = never,
|
||
OutErr2 = Types.ExcludeTag<OutErr, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>,
|
||
OutDone2 = never,
|
||
InElem2 = unknown,
|
||
InErr2 = unknown,
|
||
InDone2 = unknown,
|
||
Env2 = never
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
k: K,
|
||
f: (
|
||
e: Types.ExtractTag<NoInfer<OutErr>, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>
|
||
) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
orElse?:
|
||
| ((
|
||
e: Types.ExcludeTag<NoInfer<OutErr>, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>
|
||
) => Channel<OutElem2, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>)
|
||
| undefined
|
||
): Channel<
|
||
OutElem | OutElem1 | OutElem2,
|
||
OutErr1 | OutErr2,
|
||
OutDone | OutDone1 | OutDone2,
|
||
InElem & InElem1 & InElem2,
|
||
InErr & InErr1 & InErr2,
|
||
InDone & InDone1 & InDone2,
|
||
Env | Env1 | Env2
|
||
> => {
|
||
const pred = Array.isArray(k)
|
||
? ((e: OutErr): e is any => hasProperty(e, "_tag") && k.includes(e._tag))
|
||
: isTagged(k as string)
|
||
return catchIf(self, pred, f, orElse as any) as any
|
||
})
|
||
|
||
/**
|
||
* Catches a specific reason within a tagged error.
|
||
*
|
||
* **Example** (Recovering from nested reasons)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data } from "effect"
|
||
*
|
||
* class RateLimitError extends Data.TaggedError("RateLimitError")<{
|
||
* retryAfter: number
|
||
* }> {}
|
||
*
|
||
* class QuotaExceededError extends Data.TaggedError("QuotaExceededError")<{
|
||
* limit: number
|
||
* }> {}
|
||
*
|
||
* class AiError extends Data.TaggedError("AiError")<{
|
||
* reason: RateLimitError | QuotaExceededError
|
||
* }> {}
|
||
*
|
||
* const channel = Channel.fail(
|
||
* new AiError({ reason: new RateLimitError({ retryAfter: 60 }) })
|
||
* )
|
||
*
|
||
* const recovered = channel.pipe(
|
||
* Channel.catchReason("AiError", "RateLimitError", (reason) =>
|
||
* Channel.succeed(`retry: ${reason.retryAfter}`)
|
||
* )
|
||
* )
|
||
* ```
|
||
*
|
||
* @category error handling
|
||
* @since 4.0.0
|
||
*/
|
||
export const catchReason: {
|
||
<
|
||
OutErr,
|
||
K extends Types.Tags<OutErr>,
|
||
RK extends Types.ReasonTags<Types.ExtractTag<Types.NoInfer<OutErr>, K>>,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1,
|
||
OutElem2 = Types.unassigned,
|
||
OutErr2 = never,
|
||
OutDone2 = never,
|
||
InElem2 = unknown,
|
||
InErr2 = unknown,
|
||
InDone2 = unknown,
|
||
Env2 = never
|
||
>(
|
||
errorTag: K,
|
||
reasonTag: RK,
|
||
f: (
|
||
reason: Types.ExtractReason<Types.ExtractTag<Types.NoInfer<OutErr>, K>, RK>,
|
||
error: Types.NarrowReason<Types.ExtractTag<Types.NoInfer<OutErr>, K>, RK>
|
||
) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
orElse?:
|
||
| ((
|
||
reason: Types.ExcludeReason<Types.ExtractTag<Types.NoInfer<OutErr>, K>, RK>,
|
||
error: Types.OmitReason<Types.ExtractTag<Types.NoInfer<OutErr>, K>, RK>
|
||
) => Channel<OutElem2, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>)
|
||
| undefined
|
||
): <
|
||
OutElem,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<
|
||
OutElem | OutElem1 | Exclude<OutElem2, Types.unassigned>,
|
||
| Types.ExcludeTag<OutErr, K>
|
||
| OutErr1
|
||
| OutErr2
|
||
| (OutElem2 extends Types.unassigned ? Types.ExtractTag<OutErr, K> : never),
|
||
OutDone | OutDone1 | OutDone2,
|
||
InElem & InElem1 & InElem2,
|
||
InErr & InErr1 & InErr2,
|
||
InDone & InDone1 & InDone2,
|
||
Env | Env1 | Env2
|
||
>
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
K extends Types.Tags<OutErr>,
|
||
RK extends Types.ReasonTags<Types.ExtractTag<Types.NoInfer<OutErr>, K>>,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1,
|
||
OutElem2 = Types.unassigned,
|
||
OutErr2 = never,
|
||
OutDone2 = never,
|
||
InElem2 = unknown,
|
||
InErr2 = unknown,
|
||
InDone2 = unknown,
|
||
Env2 = never
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
errorTag: K,
|
||
reasonTag: RK,
|
||
f: (
|
||
reason: Types.ExtractReason<Types.ExtractTag<Types.NoInfer<OutErr>, K>, RK>,
|
||
error: Types.NarrowReason<Types.ExtractTag<Types.NoInfer<OutErr>, K>, RK>
|
||
) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
orElse?:
|
||
| ((
|
||
reason: Types.ExcludeReason<Types.ExtractTag<Types.NoInfer<OutErr>, K>, RK>,
|
||
error: Types.OmitReason<Types.ExtractTag<Types.NoInfer<OutErr>, K>, RK>
|
||
) => Channel<OutElem2, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>)
|
||
| undefined
|
||
): Channel<
|
||
OutElem | OutElem1 | Exclude<OutElem2, Types.unassigned>,
|
||
| Types.ExcludeTag<OutErr, K>
|
||
| OutErr1
|
||
| OutErr2
|
||
| (OutElem2 extends Types.unassigned ? Types.ExtractTag<OutErr, K> : never),
|
||
OutDone | OutDone1 | OutDone2,
|
||
InElem & InElem1 & InElem2,
|
||
InErr & InErr1 & InErr2,
|
||
InDone & InDone1 & InDone2,
|
||
Env | Env1 | Env2
|
||
>
|
||
} = dual((args) => isChannel(args[0]), <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
K extends Types.Tags<OutErr>,
|
||
RK extends Types.ReasonTags<Types.ExtractTag<Types.NoInfer<OutErr>, K>>,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1,
|
||
OutElem2 = Types.unassigned,
|
||
OutErr2 = never,
|
||
OutDone2 = never,
|
||
InElem2 = unknown,
|
||
InErr2 = unknown,
|
||
InDone2 = unknown,
|
||
Env2 = never
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
errorTag: K,
|
||
reasonTag: RK,
|
||
f: (
|
||
reason: Types.ExtractReason<Types.ExtractTag<Types.NoInfer<OutErr>, K>, RK>,
|
||
error: Types.NarrowReason<Types.ExtractTag<Types.NoInfer<OutErr>, K>, RK>
|
||
) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
orElse?:
|
||
| ((
|
||
reason: Types.ExcludeReason<Types.ExtractTag<Types.NoInfer<OutErr>, K>, RK>,
|
||
error: Types.OmitReason<Types.ExtractTag<Types.NoInfer<OutErr>, K>, RK>
|
||
) => Channel<OutElem2, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>)
|
||
| undefined
|
||
): Channel<
|
||
OutElem | OutElem1 | Exclude<OutElem2, Types.unassigned>,
|
||
| Types.ExcludeTag<OutErr, K>
|
||
| OutErr1
|
||
| OutErr2
|
||
| (OutElem2 extends Types.unassigned ? Types.ExtractTag<OutErr, K> : never),
|
||
OutDone | OutDone1 | OutDone2,
|
||
InElem & InElem1 & InElem2,
|
||
InErr & InErr1 & InErr2,
|
||
InDone & InDone1 & InDone2,
|
||
Env | Env1 | Env2
|
||
> =>
|
||
catch_(
|
||
self,
|
||
(error): Channel<
|
||
OutElem1 | Exclude<OutElem2, Types.unassigned>,
|
||
OutErr1 | OutErr2,
|
||
OutDone1 | OutDone2,
|
||
InElem1 & InElem2,
|
||
InErr1 & InErr2,
|
||
InDone1 & InDone2,
|
||
Env1 | Env2
|
||
> => {
|
||
if (isTagged(error, errorTag) && hasProperty(error, "reason")) {
|
||
const reason = error.reason as Types.ExcludeReason<Types.ExtractTag<Types.NoInfer<OutErr>, K>, RK>
|
||
if (isTagged(reason, reasonTag)) {
|
||
return f(reason as any, error as any)
|
||
}
|
||
return orElse ? orElse(reason, error as any) as any : fail(error) as any
|
||
}
|
||
return fail(error) as any
|
||
}
|
||
))
|
||
|
||
/**
|
||
* Catches multiple reasons within a tagged error using an object of handlers.
|
||
*
|
||
* @category error handling
|
||
* @since 4.0.0
|
||
*/
|
||
export const catchReasons: {
|
||
<
|
||
K extends Types.Tags<OutErr>,
|
||
OutErr,
|
||
Cases extends {
|
||
[RK in Types.ReasonTags<Types.ExtractTag<Types.NoInfer<OutErr>, K>>]+?: (
|
||
reason: Types.ExtractReason<Types.ExtractTag<Types.NoInfer<OutErr>, K>, RK>,
|
||
error: Types.NarrowReason<Types.ExtractTag<Types.NoInfer<OutErr>, K>, RK>
|
||
) => Channel<any, any, any, any, any, any, any>
|
||
},
|
||
OutElem2 = Types.unassigned,
|
||
OutErr2 = never,
|
||
OutDone2 = never,
|
||
InElem2 = unknown,
|
||
InErr2 = unknown,
|
||
InDone2 = unknown,
|
||
Env2 = never
|
||
>(
|
||
errorTag: K,
|
||
cases: Cases,
|
||
orElse?:
|
||
| ((
|
||
reason: Types.ExcludeReason<Types.ExtractTag<Types.NoInfer<OutErr>, K>, Extract<keyof Cases, string>>,
|
||
error: Types.OmitReason<Types.ExtractTag<Types.NoInfer<OutErr>, K>, Extract<keyof Cases, string>>
|
||
) => Channel<OutElem2, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>)
|
||
| undefined
|
||
): <OutElem, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<
|
||
| OutElem
|
||
| Exclude<OutElem2, Types.unassigned>
|
||
| {
|
||
[RK in keyof Cases]: Cases[RK] extends
|
||
(...args: Array<any>) => Channel<infer OutElem1, any, any, any, any, any, any> ? OutElem1 : never
|
||
}[keyof Cases],
|
||
| Types.ExcludeTag<OutErr, K>
|
||
| OutErr2
|
||
| (OutElem2 extends Types.unassigned ? Types.ExtractTag<OutErr, K> : never)
|
||
| {
|
||
[RK in keyof Cases]: Cases[RK] extends
|
||
(...args: Array<any>) => Channel<any, infer OutErr1, any, any, any, any, any> ? OutErr1 : never
|
||
}[keyof Cases],
|
||
| OutDone
|
||
| OutDone2
|
||
| {
|
||
[RK in keyof Cases]: Cases[RK] extends
|
||
(...args: Array<any>) => Channel<any, any, infer OutDone1, any, any, any, any> ? OutDone1 : never
|
||
}[keyof Cases],
|
||
& InElem
|
||
& InElem2
|
||
& {
|
||
[RK in keyof Cases]: Cases[RK] extends
|
||
(...args: Array<any>) => Channel<any, any, any, infer InElem1, any, any, any> ? InElem1 : never
|
||
}[keyof Cases],
|
||
& InErr
|
||
& InErr2
|
||
& {
|
||
[RK in keyof Cases]: Cases[RK] extends
|
||
(...args: Array<any>) => Channel<any, any, any, any, infer InErr1, any, any> ? InErr1 : never
|
||
}[keyof Cases],
|
||
& InDone
|
||
& InDone2
|
||
& {
|
||
[RK in keyof Cases]: Cases[RK] extends
|
||
(...args: Array<any>) => Channel<any, any, any, any, any, infer InDone1, any> ? InDone1 : never
|
||
}[keyof Cases],
|
||
| Env
|
||
| Env2
|
||
| {
|
||
[RK in keyof Cases]: Cases[RK] extends
|
||
(...args: Array<any>) => Channel<any, any, any, any, any, any, infer Env1> ? Env1 : never
|
||
}[keyof Cases]
|
||
>
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
K extends Types.Tags<OutErr>,
|
||
Cases extends {
|
||
[RK in Types.ReasonTags<Types.ExtractTag<OutErr, K>>]+?: (
|
||
reason: Types.ExtractReason<Types.ExtractTag<OutErr, K>, RK>,
|
||
error: Types.NarrowReason<Types.ExtractTag<OutErr, K>, RK>
|
||
) => Channel<any, any, any, any, any, any, any>
|
||
},
|
||
OutElem2 = Types.unassigned,
|
||
OutErr2 = never,
|
||
OutDone2 = never,
|
||
InElem2 = unknown,
|
||
InErr2 = unknown,
|
||
InDone2 = unknown,
|
||
Env2 = never
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
errorTag: K,
|
||
cases: Cases,
|
||
orElse?:
|
||
| ((
|
||
reason: Types.ExcludeReason<Types.ExtractTag<Types.NoInfer<OutErr>, K>, Extract<keyof Cases, string>>,
|
||
error: Types.OmitReason<Types.ExtractTag<Types.NoInfer<OutErr>, K>, Extract<keyof Cases, string>>
|
||
) => Channel<OutElem2, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>)
|
||
| undefined
|
||
): Channel<
|
||
| OutElem
|
||
| Exclude<OutElem2, Types.unassigned>
|
||
| {
|
||
[RK in keyof Cases]: Cases[RK] extends
|
||
(...args: Array<any>) => Channel<infer OutElem1, any, any, any, any, any, any> ? OutElem1 : never
|
||
}[keyof Cases],
|
||
| Types.ExcludeTag<OutErr, K>
|
||
| OutErr2
|
||
| (OutElem2 extends Types.unassigned ? Types.ExtractTag<OutErr, K> : never)
|
||
| {
|
||
[RK in keyof Cases]: Cases[RK] extends
|
||
(...args: Array<any>) => Channel<any, infer OutErr1, any, any, any, any, any> ? OutErr1 : never
|
||
}[keyof Cases],
|
||
| OutDone
|
||
| OutDone2
|
||
| {
|
||
[RK in keyof Cases]: Cases[RK] extends
|
||
(...args: Array<any>) => Channel<any, any, infer OutDone1, any, any, any, any> ? OutDone1 : never
|
||
}[keyof Cases],
|
||
& InElem
|
||
& InElem2
|
||
& {
|
||
[RK in keyof Cases]: Cases[RK] extends
|
||
(...args: Array<any>) => Channel<any, any, any, infer InElem1, any, any, any> ? InElem1 : never
|
||
}[keyof Cases],
|
||
& InErr
|
||
& InErr2
|
||
& {
|
||
[RK in keyof Cases]: Cases[RK] extends
|
||
(...args: Array<any>) => Channel<any, any, any, any, infer InErr1, any, any> ? InErr1 : never
|
||
}[keyof Cases],
|
||
& InDone
|
||
& InDone2
|
||
& {
|
||
[RK in keyof Cases]: Cases[RK] extends
|
||
(...args: Array<any>) => Channel<any, any, any, any, any, infer InDone1, any> ? InDone1 : never
|
||
}[keyof Cases],
|
||
| Env
|
||
| Env2
|
||
| {
|
||
[RK in keyof Cases]: Cases[RK] extends
|
||
(...args: Array<any>) => Channel<any, any, any, any, any, any, infer Env1> ? Env1 : never
|
||
}[keyof Cases]
|
||
>
|
||
} = dual((args) => isChannel(args[0]), (self, errorTag, cases, orElse) => {
|
||
let keys: Set<string>
|
||
return catch_(self, (error) => {
|
||
if (
|
||
isTagged(error, errorTag) &&
|
||
hasProperty(error, "reason") &&
|
||
hasProperty(error.reason, "_tag") &&
|
||
String.isString(error.reason._tag)
|
||
) {
|
||
const reason = error.reason as { readonly _tag: string }
|
||
keys ??= new Set(Object.keys(cases))
|
||
if (keys.has(reason._tag)) {
|
||
return (cases as any)[reason._tag](reason as any, error)
|
||
}
|
||
return orElse ? orElse(reason, error) as any : fail(error) as any
|
||
}
|
||
return fail(error) as any
|
||
})
|
||
})
|
||
|
||
/**
|
||
* Promotes nested reason errors into the channel error, replacing the parent error.
|
||
*
|
||
* **Example** (Promoting nested reasons)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data } from "effect"
|
||
*
|
||
* class RateLimitError extends Data.TaggedError("RateLimitError")<{
|
||
* retryAfter: number
|
||
* }> {}
|
||
*
|
||
* class QuotaExceededError extends Data.TaggedError("QuotaExceededError")<{
|
||
* limit: number
|
||
* }> {}
|
||
*
|
||
* class AiError extends Data.TaggedError("AiError")<{
|
||
* reason: RateLimitError | QuotaExceededError
|
||
* }> {}
|
||
*
|
||
* const channel = Channel.fail(
|
||
* new AiError({ reason: new RateLimitError({ retryAfter: 60 }) })
|
||
* )
|
||
*
|
||
* const unwrapped = channel.pipe(Channel.unwrapReason("AiError"))
|
||
* ```
|
||
*
|
||
* @category error handling
|
||
* @since 4.0.0
|
||
*/
|
||
export const unwrapReason: {
|
||
<
|
||
K extends TagsWithReason<OutErr>,
|
||
OutErr
|
||
>(
|
||
errorTag: K
|
||
): <OutElem, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<
|
||
OutElem,
|
||
Types.ExcludeTag<OutErr, K> | Types.ReasonOf<Types.ExtractTag<OutErr, K>>,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
K extends TagsWithReason<OutErr>
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
errorTag: K
|
||
): Channel<
|
||
OutElem,
|
||
Types.ExcludeTag<OutErr, K> | Types.ReasonOf<Types.ExtractTag<OutErr, K>>,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>
|
||
} = dual(2, <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
K extends TagsWithReason<OutErr>
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
errorTag: K
|
||
): Channel<
|
||
OutElem,
|
||
Types.ExcludeTag<OutErr, K> | Types.ReasonOf<Types.ExtractTag<OutErr, K>>,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
> =>
|
||
catchFilter(
|
||
self,
|
||
(error) =>
|
||
isTagged(error, errorTag) && hasProperty(error, "reason") ? Result.succeed(error.reason) : Result.fail(error),
|
||
fail
|
||
) as any)
|
||
|
||
/**
|
||
* Returns a new channel, which is the same as this one, except the failure
|
||
* value of the returned channel is created by applying the specified function
|
||
* to the failure value of this channel.
|
||
*
|
||
* @category error handling
|
||
* @since 2.0.0
|
||
*/
|
||
export const mapError: {
|
||
<OutErr, OutErr2>(
|
||
f: (err: OutErr) => OutErr2
|
||
): <OutElem, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, OutErr2, OutDone, InElem, InErr, InDone, Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, OutErr2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (err: OutErr) => OutErr2
|
||
): Channel<OutElem, OutErr2, OutDone, InElem, InErr, InDone, Env>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, OutErr2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (err: OutErr) => OutErr2
|
||
): Channel<OutElem, OutErr2, OutDone, InElem, InErr, InDone, Env> => catch_(self, (err) => fail(f(err))))
|
||
|
||
/**
|
||
* Converts all errors in the channel to defects (unrecoverable failures).
|
||
* This is useful when you want to treat errors as programming errors.
|
||
*
|
||
* **Example** (Converting failures to defects)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data } from "effect"
|
||
*
|
||
* class ValidationError extends Data.TaggedError("ValidationError")<{
|
||
* readonly field: string
|
||
* }> {}
|
||
*
|
||
* // Create a channel that might fail
|
||
* const failingChannel = Channel.fail(new ValidationError({ field: "email" }))
|
||
*
|
||
* // Convert failures to defects
|
||
* const fatalChannel = Channel.orDie(failingChannel)
|
||
*
|
||
* // Any failure will now become a defect (uncaught exception)
|
||
* ```
|
||
*
|
||
* @category error handling
|
||
* @since 2.0.0
|
||
*/
|
||
export const orDie = <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
): Channel<OutElem, never, OutDone, InElem, InErr, InDone, Env> => catch_(self, die)
|
||
|
||
/**
|
||
* Ignores all errors in the channel, converting them to an empty channel.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Use the `log` option to emit the full {@link Cause} when the channel fails.
|
||
*
|
||
* @category error handling
|
||
* @since 4.0.0
|
||
*/
|
||
export const ignore: <
|
||
Arg extends Channel<any, any, any, any, any, any, any> | {
|
||
readonly log?: boolean | Severity | undefined
|
||
} | undefined = {
|
||
readonly log?: boolean | Severity | undefined
|
||
}
|
||
>(
|
||
selfOrOptions: Arg,
|
||
options?: {
|
||
readonly log?: boolean | Severity | undefined
|
||
} | undefined
|
||
) => [Arg] extends
|
||
[Channel<infer OutElem, infer _OutErr, infer OutDone, infer InElem, infer InErr, infer InDone, infer Env>]
|
||
? Channel<OutElem, never, OutDone | void, InElem, InErr, InDone, Env>
|
||
: <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, never, OutDone | void, InElem, InErr, InDone, Env> = dual(
|
||
(args) => isChannel(args[0]),
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
options?: {
|
||
readonly log?: boolean | Severity | undefined
|
||
} | undefined
|
||
): Channel<OutElem, never, OutDone | void, InElem, InErr, InDone, Env> => {
|
||
if (!options?.log) {
|
||
return catch_(self, () => empty)
|
||
}
|
||
const logEffect = Effect.logWithLevel(options.log === true ? undefined : options.log)
|
||
return catch_(
|
||
tapCause(self, (cause) => Cause.hasFails(cause) ? logEffect(cause) : Effect.void),
|
||
() => empty
|
||
)
|
||
}
|
||
)
|
||
|
||
const ignoreCause_ = <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
): Channel<OutElem, never, OutDone | void, InElem, InErr, InDone, Env> => catchCause(self, () => empty)
|
||
|
||
/**
|
||
* Ignores all errors in the channel including defects, converting them to an empty channel.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use when a channel should become best-effort and all failure causes, including
|
||
* defects and interruptions, can be converted to empty output.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Use the `log` option to emit the full {@link Cause} when the channel fails.
|
||
*
|
||
* @category error handling
|
||
* @since 4.0.0
|
||
*/
|
||
export const ignoreCause: <
|
||
Arg extends Channel<any, any, any, any, any, any, any> | {
|
||
readonly log?: boolean | Severity | undefined
|
||
} | undefined = {
|
||
readonly log?: boolean | Severity | undefined
|
||
}
|
||
>(
|
||
selfOrOptions: Arg,
|
||
options?: {
|
||
readonly log?: boolean | Severity | undefined
|
||
} | undefined
|
||
) => [Arg] extends
|
||
[Channel<infer OutElem, infer _OutErr, infer OutDone, infer InElem, infer InErr, infer InDone, infer Env>]
|
||
? Channel<OutElem, never, OutDone | void, InElem, InErr, InDone, Env>
|
||
: <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, never, OutDone | void, InElem, InErr, InDone, Env> = dual(
|
||
(args) => isChannel(args[0]),
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
options?: { readonly log?: boolean | Severity | undefined } | undefined
|
||
): Channel<OutElem, never, OutDone | void, InElem, InErr, InDone, Env> => {
|
||
if (!options?.log) return ignoreCause_(self)
|
||
const logEffect = Effect.logWithLevel(options.log === true ? undefined : options.log)
|
||
return ignoreCause_(tapCause(self, (cause) => logEffect(cause)))
|
||
}
|
||
)
|
||
|
||
/**
|
||
* Returns a new channel that retries this channel according to the specified
|
||
* schedule whenever it fails.
|
||
*
|
||
* @category error handling
|
||
* @since 4.0.0
|
||
*/
|
||
export const retry: {
|
||
<SO, OutErr, SE, SR>(
|
||
schedule:
|
||
| Schedule.Schedule<SO, Types.NoInfer<OutErr>, SE, SR>
|
||
| ((
|
||
$: <SO, SE, SR>(
|
||
_: Schedule.Schedule<SO, Types.NoInfer<OutErr>, SE, SR>
|
||
) => Schedule.Schedule<SO, OutErr, SE, SR>
|
||
) => Schedule.Schedule<SO, Types.NoInfer<OutErr>, SE, SR>)
|
||
): <OutElem, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr | SE, OutDone, InElem, InErr, InDone, Env | SR>
|
||
) => Channel<OutElem, OutErr | SE, OutDone, InElem, InErr, InDone, Env | SR>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, SO, SE, SR>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
schedule:
|
||
| Schedule.Schedule<SO, OutErr, SE, SR>
|
||
| ((
|
||
$: <SO, SE, SR>(
|
||
_: Schedule.Schedule<SO, Types.NoInfer<OutErr>, SE, SR>
|
||
) => Schedule.Schedule<SO, OutErr, SE, SR>
|
||
) => Schedule.Schedule<SO, Types.NoInfer<OutErr>, SE, SR>)
|
||
): Channel<OutElem, OutErr | SE, OutDone, InElem, InErr, InDone, Env | SR>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, SO, SE, SR>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
schedule:
|
||
| Schedule.Schedule<SO, OutErr, SE, SR>
|
||
| ((
|
||
$: <O, SE, R>(_: Schedule.Schedule<O, Types.NoInfer<OutErr>, SE, R>) => Schedule.Schedule<O, OutErr, SE, R>
|
||
) => Schedule.Schedule<SO, Types.NoInfer<OutErr>, SE, SR>)
|
||
): Channel<OutElem, OutErr | SE, OutDone, InElem, InErr, InDone, Env | SR> =>
|
||
suspend(() => {
|
||
let step: ((input: OutErr) => Pull.Pull<Schedule.Metadata<SO, OutErr>, SE, SO, SR>) | undefined = undefined
|
||
let meta = Schedule.CurrentMetadata.defaultValue()
|
||
const selfWithMeta = provideServiceEffect(self, Schedule.CurrentMetadata, Effect.sync(() => meta))
|
||
const withReset = onFirst(selfWithMeta, () => {
|
||
step = undefined
|
||
return Effect.void
|
||
})
|
||
const resolvedSchedule = typeof schedule === "function" ? schedule(identity_) : schedule
|
||
const loop: Channel<
|
||
OutElem,
|
||
OutErr | SE,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env | SR
|
||
> = catch_(
|
||
withReset,
|
||
Effect.fnUntraced(
|
||
function*(error) {
|
||
if (!step) {
|
||
step = yield* Schedule.toStepWithMetadata(resolvedSchedule)
|
||
}
|
||
meta = yield* step(error)
|
||
return loop
|
||
},
|
||
(effect, error) => Pull.catchDone(effect, () => Effect.succeed(fail(error))),
|
||
unwrap
|
||
)
|
||
)
|
||
return loop
|
||
}))
|
||
|
||
/**
|
||
* Maps each output element to a channel and emits values from the most recent
|
||
* active child channels.
|
||
*
|
||
* **Details**
|
||
*
|
||
* With the default concurrency of `1`, starting a new child channel interrupts
|
||
* the previous child channel. Use `options.concurrency` to allow more active
|
||
* child channels. The source channel's done value is preserved.
|
||
*
|
||
* **Example** (Switching mapped channels)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data } from "effect"
|
||
*
|
||
* class SwitchError extends Data.TaggedError("SwitchError")<{
|
||
* readonly reason: string
|
||
* }> {}
|
||
*
|
||
* // Create a channel that outputs numbers
|
||
* const numberChannel = Channel.fromIterable([1, 2, 3])
|
||
*
|
||
* // Switch to new channels based on each value
|
||
* const switchedChannel = Channel.switchMap(
|
||
* numberChannel,
|
||
* (n) => Channel.fromIterable([`value-${n}`])
|
||
* )
|
||
*
|
||
* // Outputs: "value-1", "value-2", "value-3"
|
||
* ```
|
||
*
|
||
* @category sequencing
|
||
* @since 4.0.0
|
||
*/
|
||
export const switchMap: {
|
||
<OutElem, OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>(
|
||
f: (d: OutElem) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
options?: {
|
||
readonly concurrency?: number | "unbounded" | undefined
|
||
readonly bufferSize?: number | undefined
|
||
}
|
||
): <OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<
|
||
OutElem1,
|
||
OutErr1 | OutErr,
|
||
OutDone,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env1 | Env
|
||
>
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (d: OutElem) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
options?: {
|
||
readonly concurrency?: number | "unbounded" | undefined
|
||
readonly bufferSize?: number | undefined
|
||
}
|
||
): Channel<
|
||
OutElem1,
|
||
OutErr | OutErr1,
|
||
OutDone,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
>
|
||
} = dual(
|
||
(args) => isChannel(args[0]),
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
f: (d: OutElem) => Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
options?: {
|
||
readonly concurrency?: number | "unbounded" | undefined
|
||
readonly bufferSize?: number | undefined
|
||
}
|
||
): Channel<
|
||
OutElem1,
|
||
OutErr | OutErr1,
|
||
OutDone,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
> =>
|
||
self.pipe(
|
||
map(f),
|
||
mergeAll({
|
||
...options,
|
||
concurrency: options?.concurrency ?? 1,
|
||
switch: true
|
||
})
|
||
)
|
||
)
|
||
|
||
/**
|
||
* Merges multiple channels with specified concurrency and buffering options.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use when channel outputs are themselves channels and multiple inner channels
|
||
* should run with configured concurrency and buffering.
|
||
*
|
||
* **Example** (Merging nested channels)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data } from "effect"
|
||
*
|
||
* class MergeAllError extends Data.TaggedError("MergeAllError")<{
|
||
* readonly reason: string
|
||
* }> {}
|
||
*
|
||
* // Create channels that output other channels
|
||
* const nestedChannels = Channel.fromIterable([
|
||
* Channel.fromIterable([1, 2]),
|
||
* Channel.fromIterable([3, 4]),
|
||
* Channel.fromIterable([5, 6])
|
||
* ])
|
||
*
|
||
* // Merge all channels with bounded concurrency
|
||
* const mergedChannel = Channel.mergeAll({
|
||
* concurrency: 2,
|
||
* bufferSize: 16
|
||
* })(nestedChannels)
|
||
*
|
||
* // Outputs: 1, 2, 3, 4, 5, 6 (order may vary due to concurrency)
|
||
* ```
|
||
*
|
||
* @category combining
|
||
* @since 2.0.0
|
||
*/
|
||
export const mergeAll: {
|
||
(options: {
|
||
readonly concurrency: number | "unbounded"
|
||
readonly bufferSize?: number | undefined
|
||
readonly switch?: boolean | undefined
|
||
}): <OutElem, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
channels: Channel<
|
||
Channel<OutElem, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>
|
||
) => Channel<
|
||
OutElem,
|
||
OutErr1 | OutErr,
|
||
OutDone,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env1 | Env
|
||
>
|
||
<OutElem, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
channels: Channel<
|
||
Channel<OutElem, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>,
|
||
options: {
|
||
readonly concurrency: number | "unbounded"
|
||
readonly bufferSize?: number | undefined
|
||
readonly switch?: boolean | undefined
|
||
}
|
||
): Channel<
|
||
OutElem,
|
||
OutErr1 | OutErr,
|
||
OutDone,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env1 | Env
|
||
>
|
||
} = dual(
|
||
2,
|
||
<OutElem, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
channels: Channel<
|
||
Channel<OutElem, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>,
|
||
{ bufferSize = 16, concurrency, switch: switch_ = false }: {
|
||
readonly concurrency: number | "unbounded"
|
||
readonly bufferSize?: number | undefined
|
||
readonly switch?: boolean | undefined
|
||
}
|
||
): Channel<
|
||
OutElem,
|
||
OutErr1 | OutErr,
|
||
OutDone,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env1 | Env
|
||
> =>
|
||
fromTransformBracket(
|
||
Effect.fnUntraced(function*(upstream, scope, forkedScope) {
|
||
const concurrencyN = concurrency === "unbounded"
|
||
? Number.MAX_SAFE_INTEGER
|
||
: Math.max(1, concurrency)
|
||
const semaphore = switch_ ? undefined : Semaphore.makeUnsafe(concurrencyN)
|
||
const doneLatch = yield* Latch.make(true)
|
||
const fibers = new Set<Fiber.Fiber<any, any>>()
|
||
|
||
const queue = yield* Queue.bounded<OutElem, OutErr | OutErr1 | Cause.Done<OutDone>>(
|
||
bufferSize
|
||
)
|
||
yield* Scope.addFinalizer(forkedScope, Queue.shutdown(queue))
|
||
|
||
const pull = yield* toTransform(channels)(upstream, scope)
|
||
|
||
yield* Effect.gen(function*() {
|
||
while (true) {
|
||
if (semaphore) yield* semaphore.take(1)
|
||
const channel = yield* pull
|
||
const childScope = Scope.forkUnsafe(forkedScope)
|
||
const childPull = yield* toTransform(channel)(upstream, childScope)
|
||
|
||
while (fibers.size >= concurrencyN) {
|
||
const fiber = Iterable.headUnsafe(fibers)
|
||
fibers.delete(fiber)
|
||
if (fibers.size === 0) yield* doneLatch.open
|
||
yield* Fiber.interrupt(fiber)
|
||
}
|
||
|
||
const fiber = yield* childPull.pipe(
|
||
Effect.tap(() => Effect.yieldNow),
|
||
Effect.flatMap((value) => Queue.offer(queue, value)),
|
||
Effect.forever({ disableYield: true }),
|
||
Effect.onError(Effect.fnUntraced(function*(cause) {
|
||
const halt = Pull.filterDone(cause)
|
||
yield* Effect.exit(Scope.close(
|
||
childScope,
|
||
!Result.isFailure(halt) ? Exit.succeed(halt.success.value) : Exit.failCause(halt.failure)
|
||
))
|
||
if (!fibers.has(fiber)) return
|
||
fibers.delete(fiber)
|
||
if (semaphore) yield* semaphore.release(1)
|
||
if (fibers.size === 0) yield* doneLatch.open
|
||
if (Result.isSuccess(halt)) return
|
||
return yield* Queue.failCause(queue, cause as any)
|
||
})),
|
||
Effect.forkChild
|
||
)
|
||
|
||
doneLatch.closeUnsafe()
|
||
fibers.add(fiber)
|
||
}
|
||
}).pipe(
|
||
Effect.catchCause((cause) => doneLatch.whenOpen(Queue.failCause(queue, cause))),
|
||
Effect.forkIn(forkedScope)
|
||
)
|
||
|
||
return Queue.take(queue)
|
||
})
|
||
)
|
||
)
|
||
|
||
/**
|
||
* Represents strategies for halting merged channels when one completes or fails.
|
||
*
|
||
* **Example** (Choosing merge halt strategies)
|
||
*
|
||
* ```ts
|
||
* import type { Channel } from "effect"
|
||
*
|
||
* // Different halt strategies for channel merging
|
||
* const leftFirst: Channel.HaltStrategy = "left" // Stop when left channel halts
|
||
* const rightFirst: Channel.HaltStrategy = "right" // Stop when right channel halts
|
||
* const both: Channel.HaltStrategy = "both" // Stop when both channels halt
|
||
* const either: Channel.HaltStrategy = "either" // Stop when either channel halts
|
||
* ```
|
||
*
|
||
* @category models
|
||
* @since 4.0.0
|
||
*/
|
||
export type HaltStrategy = "left" | "right" | "both" | "either"
|
||
|
||
/**
|
||
* Returns a new channel, which is the merge of this channel and the specified
|
||
* channel.
|
||
*
|
||
* **Example** (Merging channels)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data } from "effect"
|
||
*
|
||
* class MergeError extends Data.TaggedError("MergeError")<{
|
||
* readonly source: string
|
||
* }> {}
|
||
*
|
||
* // Create two channels
|
||
* const leftChannel = Channel.fromIterable([1, 2, 3])
|
||
* const rightChannel = Channel.fromIterable(["a", "b", "c"])
|
||
*
|
||
* // Merge them with "either" halt strategy
|
||
* const mergedChannel = Channel.merge(leftChannel, rightChannel, {
|
||
* haltStrategy: "either"
|
||
* })
|
||
*
|
||
* // Outputs elements from both channels concurrently
|
||
* // Order may vary: 1, "a", 2, "b", 3, "c"
|
||
* ```
|
||
*
|
||
* @category combining
|
||
* @since 4.0.0
|
||
*/
|
||
export const merge: {
|
||
<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>(
|
||
right: Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
options?: {
|
||
readonly haltStrategy?: HaltStrategy | undefined
|
||
} | undefined
|
||
): <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
left: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<
|
||
OutElem1 | OutElem,
|
||
OutErr | OutErr1,
|
||
OutDone | OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env1 | Env
|
||
>
|
||
<
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
left: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
right: Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
options?: {
|
||
readonly haltStrategy?: HaltStrategy | undefined
|
||
} | undefined
|
||
): Channel<
|
||
OutElem | OutElem1,
|
||
OutErr | OutErr1,
|
||
OutDone | OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
>
|
||
} = dual((args) => isChannel(args[0]) && isChannel(args[1]), <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
OutElem1,
|
||
OutErr1,
|
||
OutDone1,
|
||
InElem1,
|
||
InErr1,
|
||
InDone1,
|
||
Env1
|
||
>(
|
||
left: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
right: Channel<OutElem1, OutErr1, OutDone1, InElem1, InErr1, InDone1, Env1>,
|
||
options?: {
|
||
readonly haltStrategy?: HaltStrategy | undefined
|
||
} | undefined
|
||
): Channel<
|
||
OutElem | OutElem1,
|
||
OutErr | OutErr1,
|
||
OutDone | OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
> =>
|
||
fromTransformBracket(Effect.fnUntraced(function*(upstream, _scope, forkedScope) {
|
||
const strategy = options?.haltStrategy ?? "both"
|
||
const queue = yield* Queue.bounded<OutElem | OutElem1, OutErr | OutErr1 | Cause.Done<OutDone | OutDone1>>(0)
|
||
yield* Scope.addFinalizer(forkedScope, Queue.shutdown(queue))
|
||
let done = 0
|
||
function onExit(
|
||
side: "left" | "right",
|
||
cause: Cause.Cause<OutErr | OutErr1 | Cause.Done<OutDone | OutDone1>>
|
||
): Effect.Effect<void> {
|
||
done++
|
||
if (!Pull.isDoneCause(cause)) {
|
||
return Queue.failCause(queue, cause)
|
||
}
|
||
switch (strategy) {
|
||
case "both": {
|
||
return done === 2 ? Queue.failCause(queue, cause) : Effect.void
|
||
}
|
||
case "left":
|
||
case "right": {
|
||
return side === strategy ? Queue.failCause(queue, cause) : Effect.void
|
||
}
|
||
case "either": {
|
||
return Queue.failCause(queue, cause)
|
||
}
|
||
}
|
||
}
|
||
const runSide = (
|
||
side: "left" | "right",
|
||
channel: Channel<
|
||
OutElem | OutElem1,
|
||
OutErr | OutErr1,
|
||
OutDone | OutDone1,
|
||
InElem & InElem1,
|
||
InErr & InErr1,
|
||
InDone & InDone1,
|
||
Env | Env1
|
||
>,
|
||
scope: Scope.Closeable
|
||
) =>
|
||
toTransform(channel)(upstream, scope).pipe(
|
||
Effect.flatMap((pull) =>
|
||
pull.pipe(
|
||
Effect.flatMap((value) => Queue.offer(queue, value)),
|
||
Effect.forever
|
||
)
|
||
),
|
||
Effect.onError((cause) =>
|
||
Effect.andThen(
|
||
Scope.close(scope, Pull.doneExitFromCause(cause)),
|
||
onExit(side, cause)
|
||
)
|
||
),
|
||
Effect.forkIn(forkedScope)
|
||
)
|
||
yield* runSide("left", left, Scope.forkUnsafe(forkedScope))
|
||
yield* runSide("right", right, Scope.forkUnsafe(forkedScope))
|
||
return Queue.take(queue)
|
||
})))
|
||
|
||
/**
|
||
* Runs an effect concurrently with a channel while emitting only the channel's
|
||
* output elements.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use when a side effect should run for the lifetime of a channel and only the
|
||
* channel's output elements should be emitted.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The effect's successful value is ignored. If the effect fails while the
|
||
* channel is running, the returned channel fails with that error.
|
||
*
|
||
* @category combining
|
||
* @since 4.0.0
|
||
*/
|
||
export const mergeEffect: {
|
||
<X, E, R>(
|
||
effect: Effect.Effect<X, E, R>
|
||
): <OutElem, OutDone, OutErr, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, OutErr | E, OutDone, InElem, InErr, InDone, Env | R>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, X, E, R>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
effect: Effect.Effect<X, E, R>
|
||
): Channel<OutElem, OutErr | E, OutDone, InElem, InErr, InDone, Env | R>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, X, E, R>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
effect: Effect.Effect<X, E, R>
|
||
): Channel<OutElem, OutErr | E, OutDone, InElem, InErr, InDone, Env | R> =>
|
||
merge(
|
||
self,
|
||
fromEffectDrain(effect),
|
||
{ haltStrategy: "left" }
|
||
) as any)
|
||
|
||
/**
|
||
* Splits upstream string chunks into lines, recognizing `\n`, `\r\n`, and
|
||
* standalone `\r` as line terminators. The behavior matches
|
||
* `String.linesIterator` regardless of how the input is chunked.
|
||
*
|
||
* **Details**
|
||
*
|
||
* A line terminator at the very end of the stream does **not** produce a
|
||
* trailing empty line (consistent with `String.linesIterator`). Conversely,
|
||
* if the stream ends without a terminator the final partial line is still
|
||
* emitted.
|
||
*
|
||
* **Example** (Splitting string chunks into lines)
|
||
*
|
||
* ```ts
|
||
* import { Effect, Stream } from "effect"
|
||
*
|
||
* Effect.runPromise(Effect.gen(function*() {
|
||
* const result = yield* Stream.runCollect(
|
||
* Stream.splitLines(Stream.make("hel", "lo\r\nwor", "ld\n"))
|
||
* )
|
||
* console.log(result)
|
||
* // [ 'hello', 'world' ]
|
||
* }))
|
||
* ```
|
||
*
|
||
* @category String manipulation
|
||
* @since 2.0.0
|
||
*/
|
||
export const splitLines = <Err, Done>(): Channel<
|
||
Arr.NonEmptyReadonlyArray<string>,
|
||
Err,
|
||
Done,
|
||
Arr.NonEmptyReadonlyArray<string>,
|
||
Err,
|
||
Done
|
||
> =>
|
||
fromTransform((upstream, _scope) =>
|
||
Effect.sync(() => {
|
||
// Accumulates text that has not yet been terminated by a line break.
|
||
// Content is carried across chunks until a terminator is found.
|
||
let stringBuilder = ""
|
||
// Set when a chunk ends with \r so the next chunk can check whether
|
||
// the following character is \n (completing a \r\n pair) or not
|
||
// (standalone \r, which is itself a line terminator).
|
||
let midCRLF = false
|
||
// Remembers the upstream Done value after the first time the upstream
|
||
// signals completion, so subsequent pulls return Done immediately
|
||
// without pulling upstream again.
|
||
let done = Option.none<Done>()
|
||
|
||
function splitLinesArray(chunk: Arr.NonEmptyReadonlyArray<string>): Arr.NonEmptyReadonlyArray<string> | null {
|
||
const chunkBuilder: Array<string> = []
|
||
|
||
function pushLine(segment: string): void {
|
||
if (stringBuilder.length === 0) {
|
||
chunkBuilder.push(segment)
|
||
} else {
|
||
chunkBuilder.push(stringBuilder + segment)
|
||
stringBuilder = ""
|
||
}
|
||
}
|
||
|
||
for (let i = 0; i < chunk.length; i++) {
|
||
const str = chunk[i]
|
||
if (str.length !== 0) {
|
||
let from = 0
|
||
let indexOfCR = str.indexOf("\r")
|
||
let indexOfLF = str.indexOf("\n")
|
||
if (midCRLF) {
|
||
if (indexOfLF === 0) {
|
||
pushLine("")
|
||
from = 1
|
||
indexOfLF = str.indexOf("\n", from)
|
||
} else {
|
||
pushLine("")
|
||
}
|
||
midCRLF = false
|
||
}
|
||
while (indexOfCR !== -1 || indexOfLF !== -1) {
|
||
if (indexOfCR === -1 || (indexOfLF !== -1 && indexOfLF < indexOfCR)) {
|
||
pushLine(str.substring(from, indexOfLF))
|
||
from = indexOfLF + 1
|
||
indexOfLF = str.indexOf("\n", from)
|
||
} else {
|
||
if (str.length === indexOfCR + 1) {
|
||
midCRLF = true
|
||
indexOfCR = -1
|
||
} else {
|
||
pushLine(str.substring(from, indexOfCR))
|
||
from = indexOfCR + (indexOfLF === indexOfCR + 1 ? 2 : 1)
|
||
indexOfCR = str.indexOf("\r", from)
|
||
indexOfLF = str.indexOf("\n", from)
|
||
}
|
||
}
|
||
}
|
||
stringBuilder = stringBuilder + str.substring(from, str.length - (midCRLF ? 1 : 0))
|
||
}
|
||
}
|
||
return Arr.isReadonlyArrayNonEmpty(chunkBuilder) ? chunkBuilder : null
|
||
}
|
||
|
||
const pullOrFlush: Pull.Pull<Arr.NonEmptyReadonlyArray<string>, Err, Done> = Effect.suspend(() => {
|
||
if (done._tag === "Some") {
|
||
return Cause.done(done.value)
|
||
}
|
||
return Pull.matchEffect(upstream, {
|
||
onSuccess: loop,
|
||
onFailure: Effect.failCause,
|
||
onDone: (leftover) => {
|
||
done = Option.some(leftover)
|
||
if (stringBuilder.length > 0 || midCRLF) {
|
||
const last = stringBuilder
|
||
stringBuilder = ""
|
||
midCRLF = false
|
||
return Effect.succeed([last] as Arr.NonEmptyReadonlyArray<string>)
|
||
}
|
||
return Cause.done(leftover)
|
||
}
|
||
})
|
||
})
|
||
|
||
function loop(chunk: Arr.NonEmptyReadonlyArray<string>): Pull.Pull<Arr.NonEmptyReadonlyArray<string>, Err, Done> {
|
||
const lines = splitLinesArray(chunk)
|
||
return lines !== null ? Effect.succeed(lines) : pullOrFlush
|
||
}
|
||
|
||
return pullOrFlush
|
||
})
|
||
)
|
||
|
||
/**
|
||
* Decodes incoming `Uint8Array` chunks into strings using `TextDecoder`.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Input chunks are decoded with streaming enabled so multi-byte characters may
|
||
* span `Uint8Array` boundaries. The optional `encoding` and `options` are
|
||
* passed to `TextDecoder`.
|
||
*
|
||
* @category String manipulation
|
||
* @since 4.0.0
|
||
*/
|
||
export const decodeText = <Err, Done>(encoding?: string, options?: TextDecoderOptions): Channel<
|
||
Arr.NonEmptyReadonlyArray<string>,
|
||
Err,
|
||
Done,
|
||
Arr.NonEmptyReadonlyArray<Uint8Array>,
|
||
Err,
|
||
Done
|
||
> =>
|
||
fromTransform((upstream, _scope) =>
|
||
Effect.sync(() => {
|
||
const decoder = new TextDecoder(encoding, options)
|
||
const streamOptions = { stream: true }
|
||
return Effect.map(upstream, Arr.map((line) => decoder.decode(line, streamOptions)))
|
||
})
|
||
)
|
||
|
||
/**
|
||
* Encodes incoming string chunks into `Uint8Array` values using `TextEncoder`.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Each string inside an emitted array is encoded independently.
|
||
*
|
||
* @category String manipulation
|
||
* @since 4.0.0
|
||
*/
|
||
export const encodeText = <Err, Done>(): Channel<
|
||
Arr.NonEmptyReadonlyArray<Uint8Array>,
|
||
Err,
|
||
Done,
|
||
Arr.NonEmptyReadonlyArray<string>,
|
||
Err,
|
||
Done
|
||
> =>
|
||
fromTransform((upstream, _scope) =>
|
||
Effect.sync(() => {
|
||
const encoder = new TextEncoder()
|
||
return Effect.map(upstream, Arr.map((line) => encoder.encode(line)))
|
||
})
|
||
)
|
||
|
||
/**
|
||
* Returns a new channel that pipes the output of this channel into the
|
||
* specified channel. The returned channel has the input type of this channel,
|
||
* and the output type of the specified channel, terminating with the value of
|
||
* the specified channel.
|
||
*
|
||
* **Example** (Piping one channel into another)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data } from "effect"
|
||
*
|
||
* class PipeError extends Data.TaggedError("PipeError")<{
|
||
* readonly stage: string
|
||
* }> {}
|
||
*
|
||
* // Create source and transform channels
|
||
* const sourceChannel = Channel.fromIterable([1, 2, 3])
|
||
* const transformChannel = Channel.map(sourceChannel, (n: number) => n * 2)
|
||
*
|
||
* // Pipe the source into the transform
|
||
* const pipedChannel = Channel.pipeTo(sourceChannel, transformChannel)
|
||
*
|
||
* // Outputs: 2, 4, 6
|
||
* ```
|
||
*
|
||
* @category sequencing
|
||
* @since 2.0.0
|
||
*/
|
||
export const pipeTo: {
|
||
<OutElem2, OutErr2, OutDone2, OutElem, OutErr, OutDone, Env2>(
|
||
that: Channel<OutElem2, OutErr2, OutDone2, OutElem, OutErr, OutDone, Env2>
|
||
): <InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem2, OutErr2, OutDone2, InElem, InErr, InDone, Env2 | Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, OutElem2, OutErr2, OutDone2, Env2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
that: Channel<OutElem2, OutErr2, OutDone2, OutElem, OutErr, OutDone, Env2>
|
||
): Channel<OutElem2, OutErr2, OutDone2, InElem, InErr, InDone, Env2 | Env>
|
||
} = dual(
|
||
2,
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, OutElem2, OutErr2, OutDone2, Env2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
that: Channel<OutElem2, OutErr2, OutDone2, OutElem, OutErr, OutDone, Env2>
|
||
): Channel<OutElem2, OutErr2, OutDone2, InElem, InErr, InDone, Env2 | Env> =>
|
||
fromTransform((upstream, scope) =>
|
||
Effect.flatMap(toTransform(self)(upstream, scope), (upstream) => toTransform(that)(upstream, scope))
|
||
)
|
||
)
|
||
|
||
/**
|
||
* Returns a new channel that pipes the output of this channel into the
|
||
* specified channel and preserves this channel's failures without providing
|
||
* them to the other channel for observation.
|
||
*
|
||
* **Example** (Piping while preserving failures)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data } from "effect"
|
||
*
|
||
* class SourceError extends Data.TaggedError("SourceError")<{
|
||
* readonly code: number
|
||
* }> {}
|
||
*
|
||
* // Create a failing source channel
|
||
* const failingSource = Channel.fail(new SourceError({ code: 404 }))
|
||
* const safeTransform = Channel.succeed("transformed")
|
||
*
|
||
* // Pipe while preserving source failures
|
||
* const safePipedChannel = Channel.pipeToOrFail(failingSource, safeTransform)
|
||
*
|
||
* // Source errors are preserved and not sent to transform channel
|
||
* ```
|
||
*
|
||
* @category sequencing
|
||
* @since 2.0.0
|
||
*/
|
||
export const pipeToOrFail: {
|
||
<OutElem2, OutErr2, OutDone2, OutElem, OutDone, Env2>(
|
||
that: Channel<OutElem2, OutErr2, OutDone2, OutElem, never, OutDone, Env2>
|
||
): <OutErr, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem2, OutErr | OutErr2, OutDone2, InElem, InErr, InDone, Env2 | Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, OutElem2, OutErr2, OutDone2, Env2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
that: Channel<OutElem2, OutErr2, OutDone2, OutElem, never, OutDone, Env2>
|
||
): Channel<OutElem2, OutErr | OutErr2, OutDone2, InElem, InErr, InDone, Env2 | Env>
|
||
} = dual(
|
||
2,
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, OutElem2, OutErr2, OutDone2, Env2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
that: Channel<OutElem2, OutErr2, OutDone2, OutElem, never, OutDone, Env2>
|
||
): Channel<OutElem2, OutErr | OutErr2, OutDone2, InElem, InErr, InDone, Env2 | Env> =>
|
||
fromTransform((upstream, scope) =>
|
||
Effect.flatMap(toTransform(self)(upstream, scope), (upstream) => {
|
||
const upstreamPull = Effect.catchCause(
|
||
upstream,
|
||
(cause) => Pull.isDoneCause(cause) ? Effect.failCause(cause) : Effect.die(Cause.Done(cause))
|
||
) as Pull.Pull<OutElem, never, OutDone>
|
||
|
||
return Effect.map(
|
||
toTransform(that)(upstreamPull, scope),
|
||
(pull) =>
|
||
Effect.catchDefect(
|
||
pull,
|
||
(defect) =>
|
||
Cause.isDone(defect) ? Effect.failCause(defect.value as Cause.Cause<OutErr>) : Effect.die(defect)
|
||
)
|
||
)
|
||
})
|
||
)
|
||
)
|
||
|
||
/**
|
||
* Constructs a `Channel` from a scoped effect that will result in a
|
||
* `Channel` if successful.
|
||
*
|
||
* **Example** (Unwrapping channel effects)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data, Effect } from "effect"
|
||
*
|
||
* class UnwrapError extends Data.TaggedError("UnwrapError")<{
|
||
* readonly reason: string
|
||
* }> {}
|
||
*
|
||
* // Create an effect that produces a channel
|
||
* const channelEffect = Effect.succeed(
|
||
* Channel.fromIterable([1, 2, 3])
|
||
* )
|
||
*
|
||
* // Unwrap the effect to get the channel
|
||
* const unwrappedChannel = Channel.unwrap(channelEffect)
|
||
*
|
||
* // The resulting channel outputs: 1, 2, 3
|
||
* ```
|
||
*
|
||
* @category constructors
|
||
* @since 2.0.0
|
||
*/
|
||
export const unwrap = <OutElem, OutErr, OutDone, InElem, InErr, InDone, R2, E, R>(
|
||
channel: Effect.Effect<Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, R2>, E, R>
|
||
): Channel<OutElem, E | OutErr, OutDone, InElem, InErr, InDone, Exclude<R, Scope.Scope> | R2> =>
|
||
fromTransform((upstream, scope) => {
|
||
let pull: Pull.Pull<OutElem, E | OutErr, OutDone> | undefined
|
||
return Effect.succeed(Effect.suspend(() => {
|
||
if (pull) return pull
|
||
return channel.pipe(
|
||
Scope.provide(scope),
|
||
Effect.flatMap((channel) => toTransform(channel)(upstream, scope)),
|
||
Effect.flatMap((pull_) => pull = pull_)
|
||
)
|
||
}))
|
||
})
|
||
|
||
/**
|
||
* Runs a channel with a scope provided for the duration of the channel
|
||
* execution, removing the channel's `Scope` requirement.
|
||
*
|
||
* @category resource management
|
||
* @since 2.0.0
|
||
*/
|
||
export const scoped = <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Exclude<Env, Scope.Scope>> =>
|
||
fromTransformBracket((upstream, scope, forkedScope) =>
|
||
Effect.map(
|
||
Scope.provide(toTransform(self)(upstream, scope), forkedScope),
|
||
Scope.provide(forkedScope)
|
||
)
|
||
)
|
||
|
||
/**
|
||
* Runs an input handler against the upstream pull while the wrapped channel
|
||
* runs without receiving upstream input directly.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The input handler is forked in the channel scope. The wrapped channel is run
|
||
* with an already-completed input.
|
||
*
|
||
* **Example** (Embedding custom input handling)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Effect } from "effect"
|
||
*
|
||
* // Create a base channel
|
||
* const baseChannel = Channel.fromIterable([1, 2, 3])
|
||
*
|
||
* // Drain the embedded input while the base channel runs
|
||
* const embeddedChannel = Channel.embedInput(
|
||
* baseChannel,
|
||
* (upstream) =>
|
||
* upstream.pipe(
|
||
* Effect.tap((message) =>
|
||
* Effect.sync(() => console.log(message))
|
||
* ),
|
||
* Effect.forever,
|
||
* Effect.ignore
|
||
* )
|
||
* )
|
||
* ```
|
||
*
|
||
* @category sequencing
|
||
* @since 2.0.0
|
||
*/
|
||
export const embedInput: {
|
||
<InElem, InErr, InDone, R>(
|
||
input: (
|
||
upstream: Pull.Pull<InElem, InErr, InDone>
|
||
) => Effect.Effect<void, never, R>
|
||
): <OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
) => Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env | R>
|
||
<OutElem, OutErr, OutDone, Env, InErr, InElem, InDone, R>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
input: (
|
||
upstream: Pull.Pull<InElem, InErr, InDone>
|
||
) => Effect.Effect<void, never, R>
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env | R>
|
||
} = dual(
|
||
2,
|
||
<OutElem, OutErr, OutDone, Env, InErr, InElem, InDone, R>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
input: (
|
||
upstream: Pull.Pull<InElem, InErr, InDone>
|
||
) => Effect.Effect<void, never, R>
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env | R> =>
|
||
fromTransformBracket((upstream, scope, forkedScope) =>
|
||
Effect.andThen(
|
||
Effect.forkIn(input(upstream), forkedScope),
|
||
toTransform(self)(Cause.done(), scope)
|
||
)
|
||
)
|
||
)
|
||
|
||
/**
|
||
* Buffers individual output elements in a queue with the configured `capacity`
|
||
* so a faster producer can progress independently of a slower consumer.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use when output elements can be decoupled from downstream demand and the
|
||
* configured backpressure or loss strategy is acceptable.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Finite queues use the `strategy` option. The default `"suspend"` strategy
|
||
* applies backpressure, while `"dropping"` and `"sliding"` can discard output
|
||
* elements when the queue is full. `"unbounded"` capacity does not use a finite
|
||
* capacity strategy.
|
||
*
|
||
* **Gotchas**
|
||
*
|
||
* Dropping and sliding strategies can lose output elements under backpressure.
|
||
*
|
||
* @see {@link bufferArray} for buffering elements from array outputs
|
||
*
|
||
* @category Buffering
|
||
* @since 2.0.0
|
||
*/
|
||
export const buffer: {
|
||
(
|
||
options: { readonly capacity: "unbounded" } | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
}
|
||
): <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
options: { readonly capacity: "unbounded" } | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
}
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
options: { readonly capacity: "unbounded" } | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
}
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env> =>
|
||
fromTransform(Effect.fnUntraced(function*(upstream, scope) {
|
||
const pull = yield* toTransform(self)(upstream, scope)
|
||
const queue = yield* Queue.make<OutElem, OutErr | Cause.Done<OutDone>>({
|
||
capacity: options.capacity === "unbounded" ? undefined : options.capacity,
|
||
strategy: options.capacity === "unbounded" ? undefined : options.strategy
|
||
})
|
||
yield* Scope.addFinalizer(scope, Queue.shutdown(queue))
|
||
yield* pull.pipe(
|
||
Effect.flatMap((value) => Queue.offer(queue, value)),
|
||
Effect.forever({ disableYield: true }),
|
||
Effect.onError((cause) => Queue.failCause(queue, cause)),
|
||
Effect.forkIn(scope)
|
||
)
|
||
return Queue.take(queue)
|
||
})))
|
||
|
||
/**
|
||
* Buffers array output elements in a queue with the configured `capacity` so a
|
||
* faster producer can progress independently of a slower consumer.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use when emitted arrays are batches of elements and it is acceptable for
|
||
* buffering to flatten and rebuild those batches.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Finite queues use the `strategy` option. The default `"suspend"` strategy
|
||
* applies backpressure, while `"dropping"` and `"sliding"` can discard output
|
||
* elements when the queue is full. `"unbounded"` capacity does not use a finite
|
||
* capacity strategy.
|
||
*
|
||
* **Gotchas**
|
||
*
|
||
* Input arrays are offered to the queue element-by-element and outputs are
|
||
* rebuilt from the currently available queued elements, so upstream array
|
||
* boundaries are not preserved.
|
||
*
|
||
* @see {@link buffer} for buffering output elements without flattening arrays
|
||
*
|
||
* @category Buffering
|
||
* @since 4.0.0
|
||
*/
|
||
export const bufferArray: {
|
||
(
|
||
options: { readonly capacity: "unbounded" } | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
}
|
||
): <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
options: { readonly capacity: "unbounded" } | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
}
|
||
): Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
options: { readonly capacity: "unbounded" } | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
}
|
||
): Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, InElem, InErr, InDone, Env> =>
|
||
fromTransform(Effect.fnUntraced(function*(upstream, scope) {
|
||
const pull = yield* toTransform(self)(upstream, scope)
|
||
const queue = yield* Queue.make<OutElem, OutErr | Cause.Done<OutDone>>({
|
||
capacity: options.capacity === "unbounded" ? undefined : options.capacity,
|
||
strategy: options.capacity === "unbounded" ? undefined : options.strategy
|
||
})
|
||
yield* Scope.addFinalizer(scope, Queue.shutdown(queue))
|
||
yield* pull.pipe(
|
||
Effect.flatMap((value) => Queue.offerAll(queue, value)),
|
||
Effect.forever({ disableYield: true }),
|
||
Effect.onError((cause) => Queue.failCause(queue, cause)),
|
||
Effect.forkIn(scope)
|
||
)
|
||
return Queue.takeAll(queue)
|
||
})))
|
||
|
||
/**
|
||
* Interrupts a channel when another effect completes.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use to race channel execution against an external effect whose success can
|
||
* become the channel's done value.
|
||
*
|
||
* **Details**
|
||
*
|
||
* If the effect completes first, its success value becomes the returned
|
||
* channel's done value. If the channel completes first, the original channel's
|
||
* done value is preserved.
|
||
*
|
||
* @category interruption
|
||
* @since 2.0.0
|
||
*/
|
||
export const interruptWhen: {
|
||
<OutDone2, OutErr2, Env2>(
|
||
effect: Effect.Effect<OutDone2, OutErr2, Env2>
|
||
): <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, OutErr | OutErr2, OutDone | OutDone2, InElem, InErr, InDone, Env2 | Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, OutDone2, OutErr2, Env2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
effect: Effect.Effect<OutDone2, OutErr2, Env2>
|
||
): Channel<OutElem, OutErr | OutErr2, OutDone | OutDone2, InElem, InErr, InDone, Env2 | Env>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, OutDone2, OutErr2, Env2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
effect: Effect.Effect<OutDone2, OutErr2, Env2>
|
||
): Channel<OutElem, OutErr | OutErr2, OutDone | OutDone2, InElem, InErr, InDone, Env2 | Env> =>
|
||
merge(
|
||
self,
|
||
fromPull(Effect.succeed(Effect.flatMap(effect, Cause.done))),
|
||
{ haltStrategy: "either" }
|
||
))
|
||
|
||
/**
|
||
* Stops a channel when the specified effect completes or fails.
|
||
*
|
||
* **Details**
|
||
*
|
||
* If the effect completes before the channel is done, its success value becomes
|
||
* the returned channel's done value. If the effect fails, the returned channel
|
||
* fails with that error. If the channel completes first, the channel's done
|
||
* value is preserved.
|
||
*
|
||
* @category interruption
|
||
* @since 4.0.0
|
||
*/
|
||
export const haltWhen: {
|
||
<OutDone2, OutErr2, Env2>(
|
||
effect: Effect.Effect<OutDone2, OutErr2, Env2>
|
||
): <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, OutErr | OutErr2, OutDone | OutDone2, InElem, InErr, InDone, Env2 | Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, OutDone2, OutErr2, Env2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
effect: Effect.Effect<OutDone2, OutErr2, Env2>
|
||
): Channel<OutElem, OutErr | OutErr2, OutDone | OutDone2, InElem, InErr, InDone, Env2 | Env>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, OutDone2, OutErr2, Env2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
effect: Effect.Effect<OutDone2, OutErr2, Env2>
|
||
): Channel<OutElem, OutErr | OutErr2, OutDone | OutDone2, InElem, InErr, InDone, Env2 | Env> =>
|
||
fromTransformBracket(Effect.fnUntraced(function*(upstream, scope, forkedScope) {
|
||
const pull = yield* toTransform(self)(upstream, scope)
|
||
let haltCause: Cause.Cause<OutErr2 | Cause.Done<OutDone2>> | undefined = undefined
|
||
yield* effect.pipe(
|
||
Effect.catchCause((cause) => {
|
||
haltCause = cause
|
||
return Effect.void
|
||
}),
|
||
Effect.forkIn(forkedScope)
|
||
)
|
||
return Effect.suspend((): Pull.Pull<OutElem, OutErr | OutErr2, OutDone | OutDone2> =>
|
||
haltCause ? Effect.failCause(haltCause) : pull
|
||
)
|
||
})))
|
||
|
||
/**
|
||
* Attaches a finalizer that runs only when the channel exits with failure.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The finalizer receives the failure `Cause`. The original channel failure is
|
||
* preserved. The finalizer itself must not fail.
|
||
*
|
||
* @category error handling
|
||
* @since 4.0.0
|
||
*/
|
||
export const onError: {
|
||
<OutDone, OutErr, Env2>(
|
||
finalizer: (cause: Cause.Cause<OutErr>) => Effect.Effect<unknown, never, Env2>
|
||
): <OutElem, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env2 | Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, Env2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
finalizer: (cause: Cause.Cause<OutErr>) => Effect.Effect<unknown, never, Env2>
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env2 | Env>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, Env2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
finalizer: (cause: Cause.Cause<OutErr>) => Effect.Effect<unknown, never, Env2>
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env2 | Env> =>
|
||
onExit(self, (exit) => Exit.isFailure(exit) ? finalizer(exit.cause) : Effect.void))
|
||
|
||
/**
|
||
* Returns a channel with an exit-aware finalizer that is guaranteed to run once
|
||
* the channel begins execution, whether it succeeds or fails.
|
||
*
|
||
* **Example** (Running exit finalizers)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Console, Data, Exit } from "effect"
|
||
*
|
||
* class ExitError extends Data.TaggedError("ExitError")<{
|
||
* readonly stage: string
|
||
* }> {}
|
||
*
|
||
* // Create a channel
|
||
* const dataChannel = Channel.fromIterable([1, 2, 3])
|
||
*
|
||
* // Attach exit handler
|
||
* const channelWithExit = Channel.onExit(dataChannel, (exit) => {
|
||
* if (Exit.isSuccess(exit)) {
|
||
* return Console.log(`Channel completed successfully with: ${exit.value}`)
|
||
* } else {
|
||
* return Console.log(`Channel failed with: ${exit.cause}`)
|
||
* }
|
||
* })
|
||
* ```
|
||
*
|
||
* @category resource management
|
||
* @since 4.0.0
|
||
*/
|
||
export const onExit: {
|
||
<OutDone, OutErr, Env2>(
|
||
finalizer: (e: Exit.Exit<OutDone, OutErr>) => Effect.Effect<unknown, never, Env2>
|
||
): <OutElem, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env2 | Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, Env2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
finalizer: (e: Exit.Exit<OutDone, OutErr>) => Effect.Effect<unknown, never, Env2>
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env2 | Env>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, Env2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
finalizer: (e: Exit.Exit<OutDone, OutErr>) => Effect.Effect<unknown, never, Env2>
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env2 | Env> =>
|
||
fromTransformBracket((upstream, scope, forkedScope) =>
|
||
Scope.addFinalizerExit(forkedScope, finalizer as any).pipe(
|
||
Effect.andThen(toTransform(self)(upstream, scope))
|
||
)
|
||
))
|
||
|
||
/**
|
||
* Runs an effect before the channel starts.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The effect's successful value is ignored. If the effect fails, the returned
|
||
* channel fails before running the source channel.
|
||
*
|
||
* @category hooks
|
||
* @since 4.0.0
|
||
*/
|
||
export const onStart: {
|
||
<A, E, R>(
|
||
onStart: Effect.Effect<A, E, R>
|
||
): <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, OutErr | E, OutDone, InElem, InErr, InDone, Env | R>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, A, E, R>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
onStart: Effect.Effect<A, E, R>
|
||
): Channel<OutElem, OutErr | E, OutDone, InElem, InErr, InDone, Env | R>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, A, E, R>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
onStart: Effect.Effect<A, E, R>
|
||
): Channel<OutElem, OutErr | E, OutDone, InElem, InErr, InDone, Env | R> => unwrap(Effect.as(onStart, self)))
|
||
|
||
/**
|
||
* Runs an effect the first time the channel emits an output element.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use when initialization depends on the first output element rather than only
|
||
* on channel startup.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The effect receives the first emitted element. The first element is still
|
||
* emitted unchanged. The effect is not run if the channel completes without
|
||
* emitting an element.
|
||
*
|
||
* @category hooks
|
||
* @since 4.0.0
|
||
*/
|
||
export const onFirst: {
|
||
<OutElem, A, E, R>(
|
||
onFirst: (element: Types.NoInfer<OutElem>) => Effect.Effect<A, E, R>
|
||
): <OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, OutErr | E, OutDone, InElem, InErr, InDone, Env | R>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, A, E, R>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
onFirst: (element: Types.NoInfer<OutElem>) => Effect.Effect<A, E, R>
|
||
): Channel<OutElem, OutErr | E, OutDone, InElem, InErr, InDone, Env | R>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, A, E, R>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
onFirst: (element: Types.NoInfer<OutElem>) => Effect.Effect<A, E, R>
|
||
): Channel<OutElem, OutErr | E, OutDone, InElem, InErr, InDone, Env | R> =>
|
||
transformPull(self, (pull) =>
|
||
Effect.sync(() => {
|
||
let isFirst = true
|
||
const pullFirst = Effect.tap(pull, (element) => {
|
||
isFirst = false
|
||
return onFirst(element)
|
||
})
|
||
return Effect.suspend(() => isFirst ? pullFirst : pull)
|
||
})))
|
||
|
||
/**
|
||
* Runs an effect when the channel completes successfully.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The effect runs before the original done value is propagated. The effect is
|
||
* not run when the channel fails. If the effect fails, the returned channel
|
||
* fails with that error.
|
||
*
|
||
* @category hooks
|
||
* @since 4.0.0
|
||
*/
|
||
export const onEnd: {
|
||
<A, E, R>(
|
||
onEnd: Effect.Effect<A, E, R>
|
||
): <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, OutErr | E, OutDone, InElem, InErr, InDone, Env | R>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, A, E, R>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
onEnd: Effect.Effect<A, E, R>
|
||
): Channel<OutElem, OutErr | E, OutDone, InElem, InErr, InDone, Env | R>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, A, E, R>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
onEnd: Effect.Effect<A, E, R>
|
||
): Channel<OutElem, OutErr | E, OutDone, InElem, InErr, InDone, Env | R> =>
|
||
transformPull(self, (pull) =>
|
||
Effect.succeed(Pull.catchDone(
|
||
pull,
|
||
(leftover) => Effect.flatMap(onEnd, () => Cause.done(leftover as OutDone))
|
||
))))
|
||
|
||
/**
|
||
* Returns a channel with a finalizer effect that is guaranteed to run once the
|
||
* channel begins execution, whether it succeeds or fails.
|
||
*
|
||
* **Example** (Ensuring cleanup runs)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Console, Data } from "effect"
|
||
*
|
||
* class EnsureError extends Data.TaggedError("EnsureError")<{
|
||
* readonly operation: string
|
||
* }> {}
|
||
*
|
||
* // Create a channel
|
||
* const dataChannel = Channel.fromIterable([1, 2, 3])
|
||
*
|
||
* // Ensure cleanup always runs
|
||
* const channelWithCleanup = Channel.ensuring(
|
||
* dataChannel,
|
||
* Console.log("Cleanup executed regardless of success or failure")
|
||
* )
|
||
* ```
|
||
*
|
||
* @category resource management
|
||
* @since 2.0.0
|
||
*/
|
||
export const ensuring: {
|
||
<Env2>(
|
||
finalizer: Effect.Effect<unknown, never, Env2>
|
||
): <OutElem, OutDone, OutErr, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env2 | Env>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, Env2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
finalizer: Effect.Effect<unknown, never, Env2>
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env2 | Env>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, Env2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
finalizer: Effect.Effect<unknown, never, Env2>
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env2 | Env> => onExit(self, (_) => finalizer))
|
||
|
||
const runWith = <
|
||
OutElem,
|
||
OutErr,
|
||
OutDone,
|
||
Env,
|
||
EX,
|
||
RX,
|
||
AH = OutDone,
|
||
EH = never,
|
||
RH = never
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
f: (pull: Pull.Pull<OutElem, OutErr, OutDone>) => Effect.Effect<void, EX, RX>,
|
||
onHalt?: (leftover: OutDone) => Effect.Effect<AH, EH, RH>
|
||
): Effect.Effect<AH, Pull.ExcludeDone<EX> | EH, Env | RX | RH> =>
|
||
Effect.suspend(() => {
|
||
const scope = Scope.makeUnsafe()
|
||
const makePull = toTransform(self)(Cause.done(), scope)
|
||
return Pull.catchDone(Effect.flatMap(makePull, f), onHalt ? onHalt : Effect.succeed as any).pipe(
|
||
Effect.onExit((exit) => Scope.close(scope, exit))
|
||
) as any
|
||
})
|
||
|
||
/**
|
||
* Creates a channel from the specified services.
|
||
*
|
||
* @category services
|
||
* @since 2.0.0
|
||
*/
|
||
export const contextWith = <Env, OutElem, OutErr, OutDone, InElem, InErr, InDone, Env2>(
|
||
f: (context: Context.Context<Env>) => Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env2>
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env | Env2> =>
|
||
fromTransform((upstream, scope) =>
|
||
Effect.contextWith((context: Context.Context<Env>) => toTransform(f(context))(upstream, scope))
|
||
)
|
||
|
||
/**
|
||
* Provides a `Context` to the channel, removing the corresponding service
|
||
* requirements from the returned channel.
|
||
*
|
||
* @category services
|
||
* @since 2.0.0
|
||
*/
|
||
export const provideContext: {
|
||
<R2>(
|
||
context: Context.Context<R2>
|
||
): <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Exclude<Env, R2>>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, R2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
context: Context.Context<R2>
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Exclude<Env, R2>>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, R2>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
context: Context.Context<R2>
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Exclude<Env, R2>> =>
|
||
fromTransform((upstream, scope) =>
|
||
Effect.map(
|
||
Effect.provideContext(toTransform(self)(upstream, scope), context),
|
||
Effect.provideContext(context)
|
||
)
|
||
))
|
||
|
||
/**
|
||
* Provides a concrete service for a context key, removing that service
|
||
* requirement from the returned channel.
|
||
*
|
||
* @category services
|
||
* @since 2.0.0
|
||
*/
|
||
export const provideService: {
|
||
<I, S>(
|
||
key: Context.Key<I, S>,
|
||
service: NoInfer<S>
|
||
): <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Exclude<Env, I>>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, I, S>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
key: Context.Key<I, S>,
|
||
service: NoInfer<S>
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Exclude<Env, I>>
|
||
} = dual(3, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, I, S>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
key: Context.Key<I, S>,
|
||
service: NoInfer<S>
|
||
): Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Exclude<Env, I>> =>
|
||
fromTransform((upstream, scope) =>
|
||
Effect.map(
|
||
Effect.provideService(toTransform(self)(upstream, scope), key, service),
|
||
Effect.provideService(key, service)
|
||
)
|
||
))
|
||
|
||
/**
|
||
* Provides a service to the channel after obtaining it from an effect.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use to supply a channel dependency when constructing the service itself is
|
||
* effectful or can fail.
|
||
*
|
||
* **Details**
|
||
*
|
||
* If the service effect fails, the returned channel fails. The provided service
|
||
* removes the corresponding service requirement from the returned channel.
|
||
*
|
||
* @category services
|
||
* @since 4.0.0
|
||
*/
|
||
export const provideServiceEffect: {
|
||
<I, S, ES, RS>(
|
||
key: Context.Key<I, S>,
|
||
service: Effect.Effect<NoInfer<S>, ES, RS>
|
||
): <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, OutErr | ES, OutDone, InElem, InErr, InDone, Exclude<Env, I> | RS>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, I, S, ES, RS>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
key: Context.Key<I, S>,
|
||
service: Effect.Effect<NoInfer<S>, ES, RS>
|
||
): Channel<OutElem, OutErr | ES, OutDone, InElem, InErr, InDone, Exclude<Env, I> | RS>
|
||
} = dual(3, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, I, S, ES, RS>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
key: Context.Key<I, S>,
|
||
service: Effect.Effect<NoInfer<S>, ES, RS>
|
||
): Channel<OutElem, OutErr | ES, OutDone, InElem, InErr, InDone, Exclude<Env, I> | RS> =>
|
||
fromTransform((upstream, scope) =>
|
||
Effect.flatMap(
|
||
service,
|
||
(s) => toTransform(provideService(self, key, s))(upstream, scope)
|
||
)
|
||
))
|
||
|
||
/**
|
||
* Provides a `Layer` or `Context` to the channel, removing the corresponding
|
||
* service requirements.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Providing a `Context` delegates to `provideContext`. Providing a `Layer`
|
||
* builds the layer in the channel scope. Use `options.local` to build a fresh
|
||
* layer instance for this provision.
|
||
*
|
||
* @category services
|
||
* @since 4.0.0
|
||
*/
|
||
export const provide: {
|
||
<A, E = never, R = never>(
|
||
layer: Layer.Layer<A, E, R> | Context.Context<A>,
|
||
options?: {
|
||
readonly local?: boolean | undefined
|
||
} | undefined
|
||
): <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<OutElem, OutErr | E, OutDone, InElem, InErr, InDone, Exclude<Env, A> | R>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, A, E = never, R = never>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
layer: Layer.Layer<A, E, R> | Context.Context<A>,
|
||
options?: {
|
||
readonly local?: boolean | undefined
|
||
} | undefined
|
||
): Channel<OutElem, OutErr | E, OutDone, InElem, InErr, InDone, Exclude<Env, A> | R>
|
||
} = dual((args) => isChannel(args[0]), <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, A, E = never, R = never>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
layer: Layer.Layer<A, E, R> | Context.Context<A>,
|
||
options?: {
|
||
readonly local?: boolean | undefined
|
||
} | undefined
|
||
): Channel<OutElem, OutErr | E, OutDone, InElem, InErr, InDone, Exclude<Env, A> | R> =>
|
||
Context.isContext(layer) ? provideContext(self, layer) : fromTransform((upstream, scope) =>
|
||
Effect.flatMap(
|
||
options?.local
|
||
? Layer.buildWithMemoMap(layer, Layer.makeMemoMapUnsafe(), scope)
|
||
: Layer.buildWithScope(layer, scope),
|
||
(context) =>
|
||
Effect.map(
|
||
Effect.provideContext(toTransform(self)(upstream, scope), context),
|
||
Effect.provideContext(context)
|
||
)
|
||
)
|
||
))
|
||
|
||
/**
|
||
* Transforms the current context before running the channel.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The function receives the surrounding context and returns the context to
|
||
* provide to the channel. The returned channel requires the services needed to
|
||
* build that context.
|
||
*
|
||
* @category services
|
||
* @since 4.0.0
|
||
*/
|
||
export const updateContext: {
|
||
<Env, R2>(
|
||
f: (context: Context.Context<R2>) => Context.Context<Env>
|
||
): <OutElem, OutErr, OutDone, InElem, InErr, InDone>(
|
||
self: Channel<OutElem, InElem, OutErr, InErr, OutDone, InDone, Env>
|
||
) => Channel<OutElem, InElem, OutErr, InErr, OutDone, InDone, R2>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, R2>(
|
||
self: Channel<OutElem, InElem, OutErr, InErr, OutDone, InDone, Env>,
|
||
f: (context: Context.Context<R2>) => Context.Context<Env>
|
||
): Channel<OutElem, InElem, OutErr, InErr, OutDone, InDone, R2>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, R2>(
|
||
self: Channel<OutElem, InElem, OutErr, InErr, OutDone, InDone, Env>,
|
||
f: (context: Context.Context<R2>) => Context.Context<Env>
|
||
): Channel<OutElem, InElem, OutErr, InErr, OutDone, InDone, R2> =>
|
||
fromTransform((upstream, scope) =>
|
||
Effect.contextWith((context) => {
|
||
const toProvide = f(context)
|
||
return toTransform(provideContext(self, toProvide))(upstream, scope)
|
||
})
|
||
))
|
||
|
||
/**
|
||
* Updates a service in the current context before running the channel.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The existing service is read from the context. The updated service is
|
||
* provided to the channel under the same key.
|
||
*
|
||
* @category services
|
||
* @since 2.0.0
|
||
*/
|
||
export const updateService: {
|
||
<I, S>(
|
||
key: Context.Key<I, S>,
|
||
f: (service: NoInfer<S>) => S
|
||
): <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, InElem, OutErr, InErr, OutDone, InDone, Env>
|
||
) => Channel<OutElem, InElem, OutErr, InErr, OutDone, InDone, Env | I>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, I, S>(
|
||
self: Channel<OutElem, InElem, OutErr, InErr, OutDone, InDone, Env>,
|
||
service: Context.Key<I, S>,
|
||
f: (service: NoInfer<S>) => S
|
||
): Channel<OutElem, InElem, OutErr, InErr, OutDone, InDone, Env | I>
|
||
} = dual(3, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, I, S>(
|
||
self: Channel<OutElem, InElem, OutErr, InErr, OutDone, InDone, Env>,
|
||
service: Context.Key<I, S>,
|
||
f: (service: NoInfer<S>) => S
|
||
): Channel<OutElem, InElem, OutErr, InErr, OutDone, InDone, Env | I> =>
|
||
updateContext(self, (context) =>
|
||
Context.add(
|
||
context,
|
||
service,
|
||
f(Context.get(context, service))
|
||
)))
|
||
|
||
/**
|
||
* Runs the channel inside a tracing span with the specified name and options.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The created span is provided as the current parent span while the channel
|
||
* runs. The span is ended with the channel's exit value.
|
||
*
|
||
* @category tracing
|
||
* @since 2.0.0
|
||
*/
|
||
export const withSpan: {
|
||
(
|
||
name: string,
|
||
options?: SpanOptions
|
||
): <OutElem, OutErr, OutDone, InElem, InErr, InDone, R>(
|
||
self: Channel<OutElem, InElem, OutErr, InErr, OutDone, InDone, R>
|
||
) => Channel<OutElem, InElem, OutErr, InErr, OutDone, InDone, Exclude<R, ParentSpan>>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, R>(
|
||
self: Channel<OutElem, InElem, OutErr, InErr, OutDone, InDone, R>,
|
||
name: string,
|
||
options?: SpanOptions
|
||
): Channel<OutElem, InElem, OutErr, InErr, OutDone, InDone, Exclude<R, ParentSpan>>
|
||
} = function() {
|
||
const dataFirst = isChannel(arguments[0])
|
||
const name = dataFirst ? arguments[1] : arguments[0]
|
||
const options = addSpanStackTrace(dataFirst ? arguments[2] : arguments[1])
|
||
if (dataFirst) {
|
||
const self = arguments[0]
|
||
return withSpanImpl(self, name, options)
|
||
}
|
||
return (self: any) => withSpanImpl(self, name, options)
|
||
} as any
|
||
|
||
const withSpanImpl = <OutElem, OutErr, OutDone, InElem, InErr, InDone, R>(
|
||
self: Channel<OutElem, InElem, OutErr, InErr, OutDone, InDone, R>,
|
||
name: string,
|
||
options?: SpanOptions
|
||
): Channel<OutElem, InElem, OutErr, InErr, OutDone, InDone, Exclude<R, ParentSpan>> =>
|
||
acquireUseRelease(
|
||
Effect.makeSpan(name, options),
|
||
(span) => provideService(self, ParentSpan, span),
|
||
(span, exit) =>
|
||
Effect.withFiber((fiber) => {
|
||
const clock = fiber.getRef(ClockRef)
|
||
const timingEnabled = fiber.getRef(TracerTimingEnabled)
|
||
return endSpan(span, exit, clock, timingEnabled)
|
||
})
|
||
)
|
||
|
||
/**
|
||
* The starting channel for Do notation, emitting an empty object.
|
||
*
|
||
* @category do notation
|
||
* @since 4.0.0
|
||
*/
|
||
export const Do: Channel<{}> = succeed({})
|
||
|
||
const let_: {
|
||
<N extends string, OutElem extends object, B>(
|
||
name: Exclude<N, keyof OutElem>,
|
||
f: (a: NoInfer<OutElem>) => B
|
||
): <OutErr, OutDone, InElem, InErr, InDone, R>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, R>
|
||
) => Channel<
|
||
{ [K in N | keyof OutElem]: K extends keyof OutElem ? OutElem[K] : B },
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
R
|
||
>
|
||
<OutElem extends object, OutErr, OutDone, InElem, InErr, InDone, R, N extends string, B>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, R>,
|
||
name: Exclude<N, keyof OutElem>,
|
||
f: (a: NoInfer<OutElem>) => B
|
||
): Channel<
|
||
{ [K in N | keyof OutElem]: K extends keyof OutElem ? OutElem[K] : B },
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
R
|
||
>
|
||
} = dual(3, <OutElem extends object, OutErr, OutDone, InElem, InErr, InDone, R, N extends string, B>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, R>,
|
||
name: Exclude<N, keyof OutElem>,
|
||
f: (a: NoInfer<OutElem>) => B
|
||
): Channel<
|
||
{ [K in N | keyof OutElem]: K extends keyof OutElem ? OutElem[K] : B },
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
R
|
||
> =>
|
||
map(self, (elem) => (({
|
||
...elem,
|
||
[name]: f(elem)
|
||
}) as any)))
|
||
export {
|
||
/**
|
||
* Adds a computed field to each object emitted by a channel.
|
||
*
|
||
* @category do notation
|
||
* @since 4.0.0
|
||
*/
|
||
let_ as let
|
||
}
|
||
|
||
/**
|
||
* Adds a field to each object emitted by a channel by running another channel
|
||
* derived from that object.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The field name must not already exist on the emitted object. The derived
|
||
* channel's output becomes the value of the new field. `options.concurrency`
|
||
* and `options.bufferSize` control how derived channels are flattened.
|
||
*
|
||
* @category do notation
|
||
* @since 4.0.0
|
||
*/
|
||
export const bind: {
|
||
<N extends string, OutElem extends object, B, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>(
|
||
name: Exclude<N, keyof OutElem>,
|
||
f: (a: NoInfer<OutElem>) => Channel<B, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>,
|
||
options?: {
|
||
readonly concurrency?: number | "unbounded" | undefined
|
||
readonly bufferSize?: number | undefined
|
||
}
|
||
): <OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<
|
||
{ [K in N | keyof OutElem]: K extends keyof OutElem ? OutElem[K] : B },
|
||
OutErr2 | OutErr,
|
||
OutDone,
|
||
InElem & InElem2,
|
||
InErr & InErr2,
|
||
InDone & InDone2,
|
||
Env2 | Env
|
||
>
|
||
<
|
||
OutElem extends object,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
N extends string,
|
||
B,
|
||
OutErr2,
|
||
OutDone2,
|
||
InElem2,
|
||
InErr2,
|
||
InDone2,
|
||
Env2
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
name: Exclude<N, keyof OutElem>,
|
||
f: (a: NoInfer<OutElem>) => Channel<B, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>,
|
||
options?: {
|
||
readonly concurrency?: number | "unbounded" | undefined
|
||
readonly bufferSize?: number | undefined
|
||
}
|
||
): Channel<
|
||
{ [K in N | keyof OutElem]: K extends keyof OutElem ? OutElem[K] : B },
|
||
OutErr2 | OutErr,
|
||
OutDone,
|
||
InElem & InElem2,
|
||
InErr & InErr2,
|
||
InDone & InDone2,
|
||
Env2 | Env
|
||
>
|
||
} = dual((args) => isChannel(args[0]), <
|
||
OutElem extends object,
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env,
|
||
N extends string,
|
||
B,
|
||
OutErr2,
|
||
OutDone2,
|
||
InElem2,
|
||
InErr2,
|
||
InDone2,
|
||
Env2
|
||
>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
name: Exclude<N, keyof OutElem>,
|
||
f: (a: NoInfer<OutElem>) => Channel<B, OutErr2, OutDone2, InElem2, InErr2, InDone2, Env2>,
|
||
options?: {
|
||
readonly concurrency?: number | "unbounded" | undefined
|
||
readonly bufferSize?: number | undefined
|
||
}
|
||
): Channel<
|
||
{ [K in N | keyof OutElem]: K extends keyof OutElem ? OutElem[K] : B },
|
||
OutErr2 | OutErr,
|
||
OutDone,
|
||
InElem & InElem2,
|
||
InErr & InErr2,
|
||
InDone & InDone2,
|
||
Env2 | Env
|
||
> =>
|
||
flatMap(
|
||
self,
|
||
(elem) => map(f(elem), (b) => ({ ...elem, [name]: b } as any)),
|
||
options
|
||
))
|
||
|
||
/**
|
||
* Wraps each output element in an object under the specified field name.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use when you need to start a Channel Do-notation chain from an existing
|
||
* output value by assigning that value to a field name.
|
||
*
|
||
* @see {@link Do} for starting Do notation from an empty object
|
||
* @see {@link bind} for adding a field produced by another channel
|
||
* @see {@link let_ let} for adding a computed field
|
||
*
|
||
* @category do notation
|
||
* @since 4.0.0
|
||
*/
|
||
export const bindTo: {
|
||
<N extends string>(name: N): <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>
|
||
) => Channel<
|
||
{ [K in N]: OutElem },
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>
|
||
<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, N extends string>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
name: N
|
||
): Channel<
|
||
{ [K in N]: OutElem },
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
>
|
||
} = dual(2, <OutElem, OutErr, OutDone, InElem, InErr, InDone, Env, N extends string>(
|
||
self: Channel<OutElem, OutErr, OutDone, InElem, InErr, InDone, Env>,
|
||
name: N
|
||
): Channel<
|
||
{ [K in N]: OutElem },
|
||
OutErr,
|
||
OutDone,
|
||
InElem,
|
||
InErr,
|
||
InDone,
|
||
Env
|
||
> => map(self, (elem) => ({ [name]: elem } as any)))
|
||
|
||
/**
|
||
* Runs a channel and counts the number of elements it outputs.
|
||
*
|
||
* **Example** (Counting channel output)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data } from "effect"
|
||
*
|
||
* class CountError extends Data.TaggedError("CountError")<{
|
||
* readonly reason: string
|
||
* }> {}
|
||
*
|
||
* // Create a channel with multiple elements
|
||
* const numbersChannel = Channel.fromIterable([1, 2, 3, 4, 5])
|
||
*
|
||
* // Count the elements
|
||
* const countEffect = Channel.runCount(numbersChannel)
|
||
*
|
||
* // Effect.runSync(countEffect) // Returns: 5
|
||
* ```
|
||
*
|
||
* @category execution
|
||
* @since 4.0.0
|
||
*/
|
||
export const runCount = <OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
): Effect.Effect<void, OutErr, Env> => runFold(self, () => 0, (acc) => acc + 1)
|
||
|
||
/**
|
||
* Runs a channel and discards all output elements, returning only the final result.
|
||
*
|
||
* **Example** (Draining channel output at runtime)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data } from "effect"
|
||
*
|
||
* class DrainError extends Data.TaggedError("DrainError")<{
|
||
* readonly stage: string
|
||
* }> {}
|
||
*
|
||
* // Create a channel that outputs elements and completes with a result
|
||
* const resultChannel = Channel.fromIterable([1, 2, 3])
|
||
* const completedChannel = Channel.concatWith(
|
||
* resultChannel,
|
||
* () => Channel.succeed("completed")
|
||
* )
|
||
*
|
||
* // Drain all elements and get only the final result
|
||
* const drainEffect = Channel.runDrain(completedChannel)
|
||
*
|
||
* // Effect.runSync(drainEffect) // Returns: "completed"
|
||
* ```
|
||
*
|
||
* @category execution
|
||
* @since 2.0.0
|
||
*/
|
||
export const runDrain = <OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
): Effect.Effect<OutDone, OutErr, Env> => runWith(self, (pull) => Effect.forever(pull, { disableYield: true }))
|
||
|
||
/**
|
||
* Runs a channel and applies an effect to each output element.
|
||
*
|
||
* **Example** (Running effects for each output)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Console, Data } from "effect"
|
||
*
|
||
* class ForEachError extends Data.TaggedError("ForEachError")<{
|
||
* readonly element: unknown
|
||
* }> {}
|
||
*
|
||
* // Create a channel with numbers
|
||
* const numbersChannel = Channel.fromIterable([1, 2, 3])
|
||
*
|
||
* // Run forEach to log each element
|
||
* const forEachEffect = Channel.runForEach(
|
||
* numbersChannel,
|
||
* (n) => Console.log(`Processing: ${n}`)
|
||
* )
|
||
*
|
||
* // Logs: "Processing: 1", "Processing: 2", "Processing: 3"
|
||
* ```
|
||
*
|
||
* @category execution
|
||
* @since 4.0.0
|
||
*/
|
||
export const runForEach: {
|
||
<OutElem, EX, RX>(
|
||
f: (o: OutElem) => Effect.Effect<void, EX, RX>
|
||
): <OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
) => Effect.Effect<OutDone, OutErr | EX, Env | RX>
|
||
<OutElem, OutErr, OutDone, Env, EX, RX>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
f: (o: OutElem) => Effect.Effect<void, EX, RX>
|
||
): Effect.Effect<OutDone, OutErr | EX, Env | RX>
|
||
} = dual(
|
||
2,
|
||
<OutElem, OutErr, OutDone, Env, EX, RX>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
f: (o: OutElem) => Effect.Effect<void, EX, RX>
|
||
): Effect.Effect<OutDone, OutErr | EX, Env | RX> =>
|
||
runWith(self, (pull) => Effect.forever(Effect.flatMap(pull, f), { disableYield: true }))
|
||
)
|
||
|
||
/**
|
||
* Runs a channel and applies an effectful predicate to each output element
|
||
* until the predicate returns `false`.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Returning `true` continues consuming the channel. Returning `false` stops
|
||
* consumption early. The returned effect completes with `void`.
|
||
*
|
||
* @category execution
|
||
* @since 4.0.0
|
||
*/
|
||
export const runForEachWhile: {
|
||
<OutElem, EX, RX>(
|
||
f: (o: OutElem) => Effect.Effect<boolean, EX, RX>
|
||
): <OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
) => Effect.Effect<void, OutErr | EX, Env | RX>
|
||
<OutElem, OutErr, OutDone, Env, EX, RX>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
f: (o: OutElem) => Effect.Effect<boolean, EX, RX>
|
||
): Effect.Effect<void, OutErr | EX, Env | RX>
|
||
} = dual(
|
||
2,
|
||
<OutElem, OutErr, OutDone, Env, EX, RX>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
f: (o: OutElem) => Effect.Effect<boolean, EX, RX>
|
||
): Effect.Effect<void, OutErr | EX, Env | RX> =>
|
||
runWith(self, (pull) =>
|
||
pull.pipe(
|
||
Effect.flatMap(f),
|
||
Effect.flatMap((cont) => (cont ? Effect.void : Cause.done())),
|
||
Effect.forever({ disableYield: true })
|
||
))
|
||
)
|
||
|
||
/**
|
||
* Runs a channel and collects all output elements into an array.
|
||
*
|
||
* **Example** (Collecting channel output)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data } from "effect"
|
||
*
|
||
* class CollectError extends Data.TaggedError("CollectError")<{
|
||
* readonly reason: string
|
||
* }> {}
|
||
*
|
||
* // Create a channel with elements
|
||
* const numbersChannel = Channel.fromIterable([1, 2, 3, 4, 5])
|
||
*
|
||
* // Collect all elements into an array
|
||
* const collectEffect = Channel.runCollect(numbersChannel)
|
||
*
|
||
* // Effect.runSync(collectEffect) // Returns: [1, 2, 3, 4, 5]
|
||
* ```
|
||
*
|
||
* @category execution
|
||
* @since 2.0.0
|
||
*/
|
||
export const runCollect = <OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
): Effect.Effect<Array<OutElem>, OutErr, Env> =>
|
||
runFold(self, () => [] as Array<OutElem>, (acc, o) => {
|
||
acc.push(o)
|
||
return acc
|
||
})
|
||
|
||
/**
|
||
* Runs a channel and outputs the done value.
|
||
*
|
||
* @category execution
|
||
* @since 4.0.0
|
||
*/
|
||
export const runDone = <OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
): Effect.Effect<OutDone, OutErr, Env> => runWith(self, identity_, Effect.succeed)
|
||
|
||
/**
|
||
* Runs a channel until the first output element is available, returning it in
|
||
* an `Option`.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Returns `Option.some` with the first output element, or `Option.none` if the
|
||
* channel completes without emitting output.
|
||
*
|
||
* @category execution
|
||
* @since 4.0.0
|
||
*/
|
||
export const runHead = <OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
): Effect.Effect<Option.Option<OutElem>, OutErr, Env> =>
|
||
Effect.suspend(() => {
|
||
let head = Option.none<OutElem>()
|
||
return runWith(self, (pull) =>
|
||
pull.pipe(
|
||
Effect.asSome,
|
||
Effect.flatMap((head_) => {
|
||
head = head_
|
||
return Cause.done()
|
||
})
|
||
), () => Effect.succeed(head))
|
||
})
|
||
|
||
/**
|
||
* Runs a channel to completion and returns the last output element in an
|
||
* `Option`.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Returns `Option.some` with the last emitted element, or `Option.none` if the
|
||
* channel completes without emitting output.
|
||
*
|
||
* @category execution
|
||
* @since 4.0.0
|
||
*/
|
||
export const runLast = <OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
): Effect.Effect<Option.Option<OutElem>, OutErr, Env> =>
|
||
Effect.suspend(() => {
|
||
const absent = Symbol() // Prevent boxing
|
||
let last: typeof absent | OutElem = absent
|
||
return runWith(
|
||
self,
|
||
(pull) =>
|
||
Effect.forever(
|
||
Effect.flatMap(pull, (item) => {
|
||
last = item
|
||
return Effect.void
|
||
}),
|
||
{ disableYield: true }
|
||
),
|
||
() => last === absent ? Effect.succeedNone : Effect.succeedSome(last)
|
||
)
|
||
})
|
||
|
||
/**
|
||
* Runs a channel and folds over all output elements with an accumulator.
|
||
*
|
||
* **Example** (Folding channel output)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data } from "effect"
|
||
*
|
||
* class FoldError extends Data.TaggedError("FoldError")<{
|
||
* readonly operation: string
|
||
* }> {}
|
||
*
|
||
* // Create a channel with numbers
|
||
* const numbersChannel = Channel.fromIterable([1, 2, 3, 4, 5])
|
||
*
|
||
* // Fold to calculate sum
|
||
* const sumEffect = Channel.runFold(numbersChannel, () => 0, (acc, n) => acc + n)
|
||
*
|
||
* // Effect.runSync(sumEffect) // Returns: 15
|
||
* ```
|
||
*
|
||
* @category execution
|
||
* @since 4.0.0
|
||
*/
|
||
export const runFold: {
|
||
<Z, OutElem>(
|
||
initial: LazyArg<Z>,
|
||
f: (acc: Z, o: OutElem) => Z
|
||
): <OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
) => Effect.Effect<Z, OutErr, Env>
|
||
<OutElem, OutErr, OutDone, Env, Z>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
initial: LazyArg<Z>,
|
||
f: (acc: Z, o: OutElem) => Z
|
||
): Effect.Effect<Z, OutErr, Env>
|
||
} = dual(3, <OutElem, OutErr, OutDone, Env, Z>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
initial: LazyArg<Z>,
|
||
f: (acc: Z, o: OutElem) => Z
|
||
): Effect.Effect<Z, OutErr, Env> =>
|
||
Effect.suspend(() => {
|
||
let state = initial()
|
||
return runWith(
|
||
self,
|
||
(pull) =>
|
||
Effect.whileLoop({
|
||
while: constTrue,
|
||
body: () => pull,
|
||
step: (value) => {
|
||
state = f(state, value)
|
||
}
|
||
}),
|
||
() => Effect.succeed(state)
|
||
)
|
||
}))
|
||
|
||
/**
|
||
* Runs a channel and effectfully folds all output elements with an accumulator.
|
||
*
|
||
* **When to use**
|
||
*
|
||
* Use when folding channel output needs effects, services, or an additional
|
||
* failure channel during accumulation.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The initial accumulator is evaluated lazily. Each output element is passed to
|
||
* the effectful accumulator function. The returned effect succeeds with the
|
||
* final accumulator value.
|
||
*
|
||
* @category execution
|
||
* @since 4.0.0
|
||
*/
|
||
export const runFoldEffect: {
|
||
<OutElem, Z, E, R>(
|
||
initial: LazyArg<Z>,
|
||
f: (acc: Z, o: OutElem) => Effect.Effect<Z, E, R>
|
||
): <OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
) => Effect.Effect<Z, OutErr | E, Env | R>
|
||
<OutElem, OutErr, OutDone, Env, Z, E, R>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
initial: LazyArg<Z>,
|
||
f: (acc: Z, o: OutElem) => Effect.Effect<Z, E, R>
|
||
): Effect.Effect<Z, OutErr | E, Env | R>
|
||
} = dual(3, <OutElem, OutErr, OutDone, Env, Z, E, R>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
initial: LazyArg<Z>,
|
||
f: (acc: Z, o: OutElem) => Effect.Effect<Z, E, R>
|
||
): Effect.Effect<Z, OutErr | E, Env | R> =>
|
||
Effect.suspend(() => {
|
||
let state = initial()
|
||
return runWith(
|
||
self,
|
||
(pull) =>
|
||
Effect.whileLoop({
|
||
while: constTrue,
|
||
body: constant(pull.pipe(
|
||
Effect.flatMap((o) => f(state, o)),
|
||
Effect.map((s) => {
|
||
state = s
|
||
})
|
||
)),
|
||
step: constVoid
|
||
}),
|
||
() => Effect.succeed(state)
|
||
)
|
||
}))
|
||
|
||
/**
|
||
* Converts a channel to a scoped `Pull` for low-level consumption.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The effect requires a `Scope`. The returned pull should be consumed only
|
||
* while that scope remains open. Pulls are serialized so only one pull is
|
||
* evaluated at a time.
|
||
*
|
||
* **Example** (Converting channels to pulls)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data, Effect } from "effect"
|
||
*
|
||
* class PullError extends Data.TaggedError("PullError")<{
|
||
* readonly step: string
|
||
* }> {}
|
||
*
|
||
* // Create a channel
|
||
* const numbersChannel = Channel.fromIterable([1, 2, 3])
|
||
*
|
||
* // Convert to Pull within a scope
|
||
* const pullEffect = Effect.scoped(
|
||
* Channel.toPull(numbersChannel)
|
||
* )
|
||
*
|
||
* // Use the Pull to manually consume elements
|
||
* ```
|
||
*
|
||
* @category destructors
|
||
* @since 2.0.0
|
||
*/
|
||
export const toPull: <OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
) => Effect.Effect<
|
||
Pull.Pull<OutElem, OutErr, OutDone>,
|
||
never,
|
||
Env | Scope.Scope
|
||
> = Effect.fnUntraced(
|
||
function*<OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
) {
|
||
const semaphore = Semaphore.makeUnsafe(1)
|
||
const context = yield* Effect.context<Env | Scope.Scope>()
|
||
const scope = Context.get(context, Scope.Scope)
|
||
const pull = yield* toTransform(self)(Cause.done(), scope)
|
||
return pull.pipe(
|
||
Effect.provideContext(context),
|
||
semaphore.withPermits(1)
|
||
)
|
||
},
|
||
// ensure errors are redirected to the pull effect
|
||
Effect.catchCause((cause) => Effect.succeed(Effect.failCause(cause)))
|
||
) as any
|
||
|
||
/**
|
||
* Converts a channel to a Pull within an existing scope.
|
||
*
|
||
* **Example** (Converting channels to scoped pulls)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data, Effect, Scope } from "effect"
|
||
*
|
||
* class ScopedPullError extends Data.TaggedError("ScopedPullError")<{
|
||
* readonly reason: string
|
||
* }> {}
|
||
*
|
||
* // Create a channel
|
||
* const numbersChannel = Channel.fromIterable([1, 2, 3])
|
||
*
|
||
* // Convert to Pull with explicit scope
|
||
* const scopedPullEffect = Effect.gen(function*() {
|
||
* const scope = yield* Scope.make()
|
||
* const pull = yield* Channel.toPullScoped(numbersChannel, scope)
|
||
* return pull
|
||
* })
|
||
* ```
|
||
*
|
||
* @category destructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const toPullScoped = <OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
scope: Scope.Scope
|
||
): Effect.Effect<Pull.Pull<OutElem, OutErr, OutDone, Env>, never, Env> => toTransform(self)(Cause.done(), scope)
|
||
|
||
/**
|
||
* Runs a channel and offers each output element into a queue.
|
||
*
|
||
* **Details**
|
||
*
|
||
* When the channel completes, the queue is ended. When the channel fails, the
|
||
* queue is failed with the channel's cause. The returned effect itself
|
||
* completes with `void`.
|
||
*
|
||
* @category destructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const runIntoQueue: {
|
||
<OutElem, OutErr>(queue: Queue.Queue<OutElem, OutErr | Cause.Done>): <OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
) => Effect.Effect<void, never, Env>
|
||
<OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
queue: Queue.Queue<OutElem, OutErr | Cause.Done>
|
||
): Effect.Effect<void, never, Env>
|
||
} = dual(
|
||
(args) => isChannel(args[0]),
|
||
<OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
queue: Queue.Queue<OutElem, OutErr | Cause.Done>
|
||
): Effect.Effect<void, never, Env> =>
|
||
Effect.uninterruptibleMask((restore) =>
|
||
runForEach(self, (value) => Queue.offer(queue, value)).pipe(
|
||
restore,
|
||
Effect.exit,
|
||
Effect.flatMap((exit) => {
|
||
if (Exit.isSuccess(exit)) {
|
||
Queue.endUnsafe(queue)
|
||
} else {
|
||
Queue.failCauseUnsafe(queue, exit.cause)
|
||
}
|
||
return Effect.void
|
||
})
|
||
)
|
||
)
|
||
)
|
||
|
||
/**
|
||
* Runs a channel that emits non-empty arrays and offers each array element into
|
||
* a queue.
|
||
*
|
||
* **Details**
|
||
*
|
||
* When the channel completes, the queue is ended. When the channel fails, the
|
||
* queue is failed with the channel's cause. The returned effect itself
|
||
* completes with `void`.
|
||
*
|
||
* @category destructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const runIntoQueueArray: {
|
||
<OutElem, OutErr>(queue: Queue.Queue<OutElem, OutErr | Cause.Done>): <OutDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
) => Effect.Effect<void, never, Env>
|
||
<OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
queue: Queue.Queue<OutElem, OutErr | Cause.Done>
|
||
): Effect.Effect<void, never, Env>
|
||
} = dual(
|
||
(args) => isChannel(args[0]),
|
||
<OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
queue: Queue.Queue<OutElem, OutErr | Cause.Done>
|
||
): Effect.Effect<void, never, Env> =>
|
||
Effect.uninterruptibleMask((restore) =>
|
||
runForEach(self, (value) => Queue.offerAll(queue, value)).pipe(
|
||
restore,
|
||
Effect.exit,
|
||
Effect.flatMap((exit) => {
|
||
if (Exit.isSuccess(exit)) {
|
||
Queue.endUnsafe(queue)
|
||
} else {
|
||
Queue.failCauseUnsafe(queue, exit.cause)
|
||
}
|
||
return Effect.void
|
||
})
|
||
)
|
||
)
|
||
)
|
||
|
||
/**
|
||
* Creates a scoped queue and forks the channel to feed it for concurrent
|
||
* consumption.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Output elements are offered to the queue. Channel completion and failure are
|
||
* signaled through the queue. The queue is shut down when the surrounding scope
|
||
* closes.
|
||
*
|
||
* **Example** (Converting channels to queues)
|
||
*
|
||
* ```ts
|
||
* import { Channel, Data } from "effect"
|
||
*
|
||
* class QueueError extends Data.TaggedError("QueueError")<{
|
||
* readonly operation: string
|
||
* }> {}
|
||
*
|
||
* // Create a channel with data
|
||
* const dataChannel = Channel.fromIterable([1, 2, 3, 4, 5])
|
||
*
|
||
* // Convert to queue for concurrent processing
|
||
* const queueEffect = Channel.toQueue(dataChannel, { capacity: 32 })
|
||
*
|
||
* // The queue can be used for concurrent consumption
|
||
* // Multiple consumers can read from the queue
|
||
* ```
|
||
*
|
||
* @category destructors
|
||
* @since 2.0.0
|
||
*/
|
||
export const toQueue: {
|
||
(
|
||
options: {
|
||
readonly capacity: "unbounded"
|
||
} | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
}
|
||
): <OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
) => Effect.Effect<Queue.Dequeue<OutElem, OutErr | Cause.Done>, never, Env | Scope.Scope>
|
||
<OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
options: {
|
||
readonly capacity: "unbounded"
|
||
} | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
}
|
||
): Effect.Effect<Queue.Dequeue<OutElem, OutErr | Cause.Done>, never, Env | Scope.Scope>
|
||
} = dual(
|
||
(args) => isChannel(args[0]),
|
||
Effect.fnUntraced(function*<OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
options: {
|
||
readonly capacity: "unbounded"
|
||
} | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
}
|
||
) {
|
||
const scope = yield* Effect.scope
|
||
const queue = yield* Queue.make<OutElem, OutErr | Cause.Done>({
|
||
capacity: typeof options.capacity === "number" ? options.capacity : undefined,
|
||
strategy: typeof options.capacity === "number" ? options.strategy : undefined
|
||
})
|
||
yield* Scope.addFinalizer(scope, Queue.shutdown(queue))
|
||
yield* Effect.forkIn(runIntoQueue(self, queue), scope)
|
||
return queue
|
||
})
|
||
)
|
||
|
||
/**
|
||
* Creates a scoped queue and forks an array-emitting channel to feed it.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Each element inside emitted non-empty arrays is offered to the queue. Channel
|
||
* completion and failure are signaled through the queue. The queue is shut down
|
||
* when the surrounding scope closes.
|
||
*
|
||
* @category destructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const toQueueArray: {
|
||
(
|
||
options: {
|
||
readonly capacity: "unbounded"
|
||
} | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
}
|
||
): <OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
) => Effect.Effect<Queue.Dequeue<OutElem, OutErr | Cause.Done>, never, Env | Scope.Scope>
|
||
<OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
options: {
|
||
readonly capacity: "unbounded"
|
||
} | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
}
|
||
): Effect.Effect<Queue.Dequeue<OutElem, OutErr | Cause.Done>, never, Env | Scope.Scope>
|
||
} = dual(
|
||
(args) => isChannel(args[0]),
|
||
Effect.fnUntraced(function*<OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
options: {
|
||
readonly capacity: "unbounded"
|
||
} | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
}
|
||
) {
|
||
const scope = yield* Effect.scope
|
||
const queue = yield* Queue.make<OutElem, OutErr | Cause.Done>({
|
||
capacity: typeof options.capacity === "number" ? options.capacity : undefined,
|
||
strategy: typeof options.capacity === "number" ? options.strategy : undefined
|
||
})
|
||
yield* Scope.addFinalizer(scope, Queue.shutdown(queue))
|
||
yield* Effect.forkIn(runIntoQueueArray(self, queue), scope)
|
||
return queue
|
||
})
|
||
)
|
||
|
||
/**
|
||
* Converts a channel to a PubSub for concurrent consumption.
|
||
*
|
||
* **Details**
|
||
*
|
||
* `shutdownOnEnd` indicates whether the PubSub should be shut down when the
|
||
* channel ends. By default this is `true`.
|
||
*
|
||
* @category destructors
|
||
* @since 2.0.0
|
||
*/
|
||
export const toPubSub: {
|
||
(
|
||
options: {
|
||
readonly capacity: "unbounded"
|
||
readonly replay?: number | undefined
|
||
readonly shutdownOnEnd?: boolean | undefined
|
||
} | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
readonly replay?: number | undefined
|
||
readonly shutdownOnEnd?: boolean | undefined
|
||
}
|
||
): <OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
) => Effect.Effect<PubSub.PubSub<OutElem>, never, Env | Scope.Scope>
|
||
<OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
options: {
|
||
readonly capacity: "unbounded"
|
||
readonly replay?: number | undefined
|
||
readonly shutdownOnEnd?: boolean | undefined
|
||
} | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
readonly replay?: number | undefined
|
||
readonly shutdownOnEnd?: boolean | undefined
|
||
}
|
||
): Effect.Effect<PubSub.PubSub<OutElem>, never, Env | Scope.Scope>
|
||
} = dual(
|
||
2,
|
||
Effect.fnUntraced(function*<OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
options: {
|
||
readonly capacity: "unbounded"
|
||
readonly replay?: number | undefined
|
||
readonly shutdownOnEnd?: boolean | undefined
|
||
} | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
readonly replay?: number | undefined
|
||
readonly shutdownOnEnd?: boolean | undefined
|
||
}
|
||
) {
|
||
const pubsub = yield* makePubSub<OutElem>(options)
|
||
yield* Effect.forkScoped(runIntoPubSub(self, pubsub, {
|
||
shutdownOnEnd: options.shutdownOnEnd !== false
|
||
}))
|
||
return pubsub
|
||
})
|
||
)
|
||
|
||
/**
|
||
* Runs a channel and publishes each output element to a `PubSub`.
|
||
*
|
||
* **Details**
|
||
*
|
||
* The channel's output values are published as individual PubSub messages. Use
|
||
* `options.shutdownOnEnd` to shut down the PubSub when channel execution ends.
|
||
*
|
||
* @category destructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const runIntoPubSub: {
|
||
<OutElem>(
|
||
pubsub: PubSub.PubSub<OutElem>,
|
||
options?: {
|
||
readonly shutdownOnEnd?: boolean | undefined
|
||
} | undefined
|
||
): <OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
) => Effect.Effect<void, never, Env>
|
||
<OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
pubsub: PubSub.PubSub<OutElem>,
|
||
options?: {
|
||
readonly shutdownOnEnd?: boolean | undefined
|
||
} | undefined
|
||
): Effect.Effect<void, never, Env>
|
||
} = dual(
|
||
(args) => isChannel(args[0]),
|
||
<OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<OutElem, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
pubsub: PubSub.PubSub<OutElem>,
|
||
options?: {
|
||
readonly shutdownOnEnd?: boolean | undefined
|
||
} | undefined
|
||
) =>
|
||
runForEach(self, (value) => PubSub.publish(pubsub, value)).pipe(
|
||
options?.shutdownOnEnd === true ? Effect.ensuring(PubSub.shutdown(pubsub)) : identity_
|
||
)
|
||
)
|
||
|
||
const makePubSub = <A>(
|
||
options: {
|
||
readonly capacity: "unbounded"
|
||
readonly replay?: number | undefined
|
||
} | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
readonly replay?: number | undefined
|
||
}
|
||
) =>
|
||
Effect.acquireRelease(
|
||
options.capacity === "unbounded"
|
||
? PubSub.unbounded<A>(options)
|
||
: options.strategy === "dropping"
|
||
? PubSub.dropping<A>(options)
|
||
: options.strategy === "sliding"
|
||
? PubSub.sliding<A>(options)
|
||
: PubSub.bounded<A>(options),
|
||
PubSub.shutdown
|
||
)
|
||
|
||
/**
|
||
* Converts an array-emitting channel to a scoped `PubSub` for concurrent
|
||
* consumption.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Each element inside emitted non-empty arrays is published as an individual
|
||
* PubSub message. `shutdownOnEnd` indicates whether the PubSub should be shut
|
||
* down when the channel ends. By default this is `true`.
|
||
*
|
||
* @category destructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const toPubSubArray: {
|
||
(
|
||
options: {
|
||
readonly capacity: "unbounded"
|
||
readonly replay?: number | undefined
|
||
readonly shutdownOnEnd?: boolean | undefined
|
||
} | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
readonly replay?: number | undefined
|
||
readonly shutdownOnEnd?: boolean | undefined
|
||
}
|
||
): <OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
) => Effect.Effect<PubSub.PubSub<OutElem>, never, Env | Scope.Scope>
|
||
<OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
options: {
|
||
readonly capacity: "unbounded"
|
||
readonly replay?: number | undefined
|
||
readonly shutdownOnEnd?: boolean | undefined
|
||
} | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
readonly replay?: number | undefined
|
||
readonly shutdownOnEnd?: boolean | undefined
|
||
}
|
||
): Effect.Effect<PubSub.PubSub<OutElem>, never, Env | Scope.Scope>
|
||
} = dual(
|
||
2,
|
||
Effect.fnUntraced(function*<OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
options: {
|
||
readonly capacity: "unbounded"
|
||
readonly replay?: number | undefined
|
||
readonly shutdownOnEnd?: boolean | undefined
|
||
} | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
readonly replay?: number | undefined
|
||
readonly shutdownOnEnd?: boolean | undefined
|
||
}
|
||
) {
|
||
const pubsub = yield* makePubSub<OutElem>(options)
|
||
yield* Effect.forkScoped(runIntoPubSubArray(self, pubsub, {
|
||
shutdownOnEnd: options.shutdownOnEnd !== false
|
||
}))
|
||
return pubsub
|
||
})
|
||
)
|
||
|
||
/**
|
||
* Runs an array-emitting channel and publishes each array element to a
|
||
* `PubSub`.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Each element inside emitted non-empty arrays is published as an individual
|
||
* PubSub message. Use `options.shutdownOnEnd` to shut down the PubSub when
|
||
* channel execution ends.
|
||
*
|
||
* @category destructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const runIntoPubSubArray: {
|
||
<OutElem>(
|
||
pubsub: PubSub.PubSub<OutElem>,
|
||
options?: {
|
||
readonly shutdownOnEnd?: boolean | undefined
|
||
} | undefined
|
||
): <OutErr, OutDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
) => Effect.Effect<OutDone, OutErr, Env>
|
||
<OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
pubsub: PubSub.PubSub<OutElem>,
|
||
options?: {
|
||
readonly shutdownOnEnd?: boolean | undefined
|
||
} | undefined
|
||
): Effect.Effect<OutDone, OutErr, Env>
|
||
} = dual(
|
||
(args) => isChannel(args[0]),
|
||
<OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
pubsub: PubSub.PubSub<OutElem>,
|
||
options?: {
|
||
readonly shutdownOnEnd?: boolean | undefined
|
||
} | undefined
|
||
) =>
|
||
runForEach(self, (value) => PubSub.publishAll(pubsub, value)).pipe(
|
||
options?.shutdownOnEnd === true ? Effect.ensuring(PubSub.shutdown(pubsub)) : identity_
|
||
)
|
||
)
|
||
|
||
/**
|
||
* Converts a channel to a scoped `PubSub` of `Take` values.
|
||
*
|
||
* **Details**
|
||
*
|
||
* Emitted non-empty arrays are published as output `Take` values. When the
|
||
* channel ends, its final `Exit` is published so subscribers can observe
|
||
* completion or failure.
|
||
*
|
||
* @category destructors
|
||
* @since 4.0.0
|
||
*/
|
||
export const toPubSubTake: {
|
||
(
|
||
options: {
|
||
readonly capacity: "unbounded"
|
||
readonly replay?: number | undefined
|
||
} | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
readonly replay?: number | undefined
|
||
}
|
||
): <OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutDone>, OutErr, OutDone, unknown, unknown, unknown, Env>
|
||
) => Effect.Effect<
|
||
PubSub.PubSub<Take.Take<OutElem, OutErr, OutDone>>,
|
||
never,
|
||
Env | Scope.Scope
|
||
>
|
||
<OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
options: {
|
||
readonly capacity: "unbounded"
|
||
readonly replay?: number | undefined
|
||
} | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
readonly replay?: number | undefined
|
||
}
|
||
): Effect.Effect<
|
||
PubSub.PubSub<Take.Take<OutElem, OutErr, OutDone>>,
|
||
never,
|
||
Env | Scope.Scope
|
||
>
|
||
} = dual(
|
||
2,
|
||
Effect.fnUntraced(function*<OutElem, OutErr, OutDone, Env>(
|
||
self: Channel<Arr.NonEmptyReadonlyArray<OutElem>, OutErr, OutDone, unknown, unknown, unknown, Env>,
|
||
options: {
|
||
readonly capacity: "unbounded"
|
||
readonly replay?: number | undefined
|
||
} | {
|
||
readonly capacity: number
|
||
readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
|
||
readonly replay?: number | undefined
|
||
}
|
||
) {
|
||
const pubsub = yield* makePubSub<Take.Take<OutElem, OutErr, OutDone>>(options)
|
||
yield* runForEach(self, (value) => PubSub.publish(pubsub, value)).pipe(
|
||
Effect.onExit((exit) => PubSub.publish(pubsub, exit)),
|
||
Effect.forkScoped
|
||
)
|
||
return pubsub
|
||
})
|
||
)
|