/** * @since 2.0.0 */ // @effect-diagnostics returnEffectInGen:off import * as Arr from "./Array.ts" import * as Cause from "./Cause.ts" import * as Channel from "./Channel.ts" import { Clock } from "./Clock.ts" import * as Context from "./Context.ts" import * as Duration from "./Duration.ts" import * as Effect from "./Effect.ts" import * as Equal from "./Equal.ts" import * as ExecutionPlan from "./ExecutionPlan.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 } from "./Function.ts" import type { TypeLambda } from "./HKT.ts" import * as internalExecutionPlan from "./internal/executionPlan.ts" import * as internal from "./internal/stream.ts" import { addSpanStackTrace } from "./internal/tracer.ts" import * as Iterable from "./Iterable.ts" import * as Latch from "./Latch.ts" import type * as Layer from "./Layer.ts" import type { Severity } from "./LogLevel.ts" import * as MutableHashMap from "./MutableHashMap.ts" import * as MutableList from "./MutableList.ts" import * as Option from "./Option.ts" import type { Pipeable } from "./Pipeable.ts" import type { Predicate, Refinement } from "./Predicate.ts" import { hasProperty, isNotUndefined, isTagged } from "./Predicate.ts" import type * as PubSub from "./PubSub.ts" import * as Pull from "./Pull.ts" import * as Queue from "./Queue.ts" import * as RcMap from "./RcMap.ts" import * as RcRef from "./RcRef.ts" import * as Result from "./Result.ts" import * as Schedule from "./Schedule.ts" import * as Scope from "./Scope.ts" import * as Sink from "./Sink.ts" import { isString } from "./String.ts" import type * as Take from "./Take.ts" import type { ParentSpan, SpanOptions } from "./Tracer.ts" import type { Covariant, ExcludeReason, ExcludeTag, ExtractReason, ExtractTag, NarrowReason, NoInfer, OmitReason, ReasonTags, Tags, unassigned } from "./Types.ts" import type * as Unify from "./Unify.ts" /** * @since 4.0.0 * @category Type Identifiers */ export type TypeId = "~effect/Stream" /** * @since 4.0.0 * @category Type Identifiers */ export const TypeId: TypeId = "~effect/Stream" /** * A `Stream` describes a program that can emit many `A` values, fail * with `E`, and require `R`. * * Streams are pull-based with backpressure and emit chunks to amortize effect * evaluation. They support monadic composition and error handling similar to * `Effect`, adapted for multiple values. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * yield* Stream.make(1, 2, 3).pipe( * Stream.map((n) => n * 2), * Stream.runForEach((n) => Console.log(n)) * ) * }) * * Effect.runPromise(program) * // Output: * // 2 * // 4 * // 6 * ``` * * @since 2.0.0 * @category Models */ export interface Stream extends Variance, Pipeable { readonly channel: Channel.Channel, E, void, unknown, unknown, unknown, R> [Unify.typeSymbol]?: unknown [Unify.unifySymbol]?: StreamUnify [Unify.ignoreSymbol]?: StreamUnifyIgnore } /** * Type-level unification hook for Stream within the Effect type system. * * @example * ```ts * import { Effect, Stream } from "effect" * * // StreamUnify helps unify Stream and Effect types * declare const stream: Stream.Stream * declare const effect: Effect.Effect * * // The unification system handles mixed operations * const combined = Effect.zip(stream.pipe(Stream.runCollect), effect) * ``` * * @since 2.0.0 * @category Models */ export interface StreamUnify extends Effect.EffectUnify { Stream?: () => A[Unify.typeSymbol] extends Stream | infer _ ? Stream : never } /** * Type-level marker that excludes Stream from unification. * * @example * ```ts * import type * as Stream from "effect/Stream" * * // Used internally by the type system * // Users typically don't interact with this directly * type StreamIgnore = Stream.StreamUnifyIgnore * ``` * * @category Models * @since 2.0.0 */ export interface StreamUnifyIgnore { Effect?: true } /** * Type lambda for Stream used in higher-kinded type operations. * * @example * ```ts * import type { Kind } from "effect/HKT" * import type { StreamTypeLambda } from "effect/Stream" * * // Create a Stream type using the type lambda * type NumberStream = Kind * // Equivalent to: Stream * ``` * * @category Type Lambdas * @since 2.0.0 */ export interface StreamTypeLambda extends TypeLambda { readonly type: Stream } /** * Variance markers for Stream type parameters. * * @since 2.0.0 * @category Models */ export interface Variance { readonly [TypeId]: VarianceStruct } /** * Structural encoding of Stream type parameter variance. * * @since 2.0.0 * @category Models */ export interface VarianceStruct { readonly _A: Covariant readonly _E: Covariant readonly _R: Covariant } /** * Extract the success type from a Stream type. * * @example * ```ts * import type { Stream } from "effect" * * type NumberStream = Stream.Stream * type SuccessType = Stream.Success * // SuccessType is number * ``` * * @since 3.4.0 * @category Type-Level */ export type Success> = [T] extends [Stream] ? _A : never /** * Extract the error type from a Stream type. * * @example * ```ts * import type { Stream } from "effect" * * type NumberStream = Stream.Stream * type ErrorType = Stream.Error * // ErrorType is string * ``` * * @since 3.4.0 * @category Type-Level */ export type Error> = [T] extends [Stream] ? _E : never /** * Extract the services type from a Stream type. * * **Previously Known As:** * * This type alias was named `Context` in Effect 3.x. * * @example * ```ts * import type { Stream } from "effect" * * interface Database { * query: (sql: string) => unknown * } * type NumberStream = Stream.Stream * type RequiredServices = Stream.Services * // RequiredServices is { db: Database } * ``` * * @since 3.4.0 * @category Type-Level */ export type Services> = [T] extends [Stream] ? _R : never /** * Checks whether a value is a Stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 2, 3) * const notStream = { data: [1, 2, 3] } * * yield* Console.log(Stream.isStream(stream)) * // true * yield* Console.log(Stream.isStream(notStream)) * // false * }) * * Effect.runPromise(program) * ``` * * @since 2.0.0 * @category Guards */ export const isStream = (u: unknown): u is Stream => hasProperty(u, TypeId) /** * The default chunk size used by Stream constructors and combinators. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * yield* Console.log(Stream.DefaultChunkSize) * }) * * Effect.runPromise(program) * // Output: 4096 * ``` * * @category Constants * @since 2.0.0 */ export const DefaultChunkSize: number = Channel.DefaultChunkSize /** * Describes how merged streams decide when to halt. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `StreamHaltStrategy.HaltStrategy` * * @category Models * @since 2.0.0 */ export type HaltStrategy = Channel.HaltStrategy /** * Creates a stream from a array-emitting `Channel`. * * @example * ```ts * import { Channel, Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const channel = Channel.succeed([1, 2, 3] as const) * const stream = Stream.fromChannel(channel) * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Constructors */ export const fromChannel: , E, R>( channel: Channel.Channel ) => Stream ? A : never, E, R> = internal.fromChannel /** * Either emits the success value of this effect or terminates the stream * with the failure value of this effect. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.fromEffect(Effect.succeed(42)) * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 42 ] * ``` * * @since 2.0.0 * @category Constructors */ export const fromEffect = (effect: Effect.Effect): Stream => fromChannel(Channel.fromEffect(Effect.map(effect, Arr.of))) /** * Accesses a service from the context and emits it as a single element. * * @example * ```ts * import { Effect, Context, Stream } from "effect" * * class Greeter extends Context.Service string * }>()("Greeter") {} * * const stream = Stream.service(Greeter).pipe( * Stream.map((greeter) => greeter.greet("World")) * ) * * const program = Effect.gen(function*() { * return yield* stream.pipe( * Stream.provideService(Greeter, { * greet: (name) => `Hello, ${name}!` * }), * Stream.runCollect * ) * }) * * Effect.runPromise(program) * // Output: [ "Hello, World!" ] * ``` * * @since 4.0.0 * @category Context */ export const service = (service: Context.Key): Stream => fromEffect(Effect.service(service)) /** * Optionally accesses a service from the context and emits the result as a * single element. * * @example * ```ts * import { Effect, Option, Context, Stream } from "effect" * * class Greeter extends Context.Service string * }>()("Greeter") {} * * const stream = Stream.serviceOption(Greeter).pipe( * Stream.map((maybeGreeter) => * Option.match(maybeGreeter, { * onNone: () => "No greeter", * onSome: (greeter) => greeter.greet("World") * }) * ) * ) * * const program = Effect.gen(function*() { * return yield* stream.pipe( * Stream.provideService(Greeter, { * greet: (name) => `Hello, ${name}!` * }), * Stream.runCollect * ) * }) * * Effect.runPromise(program) * // Output: [ "Hello, World!" ] * ``` * * @since 4.0.0 * @category Context */ export const serviceOption = (service: Context.Key): Stream> => fromEffect(Effect.serviceOption(service)) /** * Creates a stream that runs the effect and emits no elements. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * yield* Stream.fromEffectDrain(Console.log("Draining side effect")).pipe( * Stream.runDrain * ) * }) * * Effect.runPromise(program) * // Output: Draining side effect * ``` * * @since 4.0.0 * @category Constructors */ export const fromEffectDrain = (effect: Effect.Effect): Stream => fromPull(Effect.succeed(Effect.flatMap(effect, () => Cause.done()))) /** * Creates a stream from an effect producing a value of type `A` which repeats forever. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.repeatEffect` * * @example * ```ts * import { Console, Effect, Random, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.fromEffectRepeat(Random.nextInt).pipe( * Stream.take(5) * ) * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 3891571149, 4239494205, 2352981603, 2339111046, 1488052210 ] * ``` * * @since 4.0.0 * @category Constructors */ export const fromEffectRepeat = (effect: Effect.Effect): Stream, R> => fromPull(Effect.succeed(Effect.map(effect, Arr.of))) /** * Creates a stream from an effect producing a value of type `A`, which is * repeated using the specified schedule. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.repeatEffectWithSchedule` * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.fromEffectSchedule( * Effect.succeed("ping"), * Schedule.recurs(2) * ) * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ "ping", "ping", "ping" ] * ``` * * @since 2.0.0 * @category Constructors */ export const fromEffectSchedule = ( effect: Effect.Effect, schedule: Schedule.Schedule ): Stream => fromPull(Effect.gen(function*() { const step = yield* Schedule.toStepWithMetadata(schedule) let s = yield* Effect.provideService(effect, Schedule.CurrentMetadata, Schedule.CurrentMetadata.defaultValue()) let initial = true const pull = Effect.suspend(() => step(s as AS)).pipe( Effect.flatMap((meta) => Effect.provideService(effect, Schedule.CurrentMetadata, meta)), Effect.map((next) => { s = next return Arr.of(next) }) ) as Pull.Pull, E | ES, void, R | RS> return Effect.suspend(() => { if (initial) { initial = false return Effect.succeed(Arr.of(s)) } return pull }) })) /** * Creates a stream that emits void values spaced by the specified duration. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const ticks = yield* Stream.tick("200 millis").pipe( * Stream.take(3), * Stream.runCollect * ) * yield* Console.log(ticks) * }) * * Effect.runPromise(program) * // Output: [ undefined, undefined, undefined ] * ``` * * @since 2.0.0 * @category Constructors */ export const tick = (interval: Duration.Input): Stream => fromPull(Effect.sync(() => { let first = true const effect = Effect.succeed(Arr.of(undefined)) const delayed = Effect.delay(effect, interval) return Effect.suspend(() => { if (first) { first = false return effect } return delayed }) })) /** * Creates a stream from a pull effect, such as one produced by `Stream.toPull`. * * A pull effect yields chunks on demand and completes when the upstream stream ends. * See `Stream.toPull` for a matching producer. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.scoped( * Effect.gen(function*() { * const source = Stream.make(1, 2, 3) * const pull = yield* Stream.toPull(source) * const stream = Stream.fromPull(Effect.succeed(pull)) * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * ) * * Effect.runPromise(program) * // Output: [1, 2, 3] * ``` * * @since 2.0.0 * @category Constructors */ export const fromPull = ( pull: Effect.Effect, E, void, R>, EX, RX> ): Stream | EX, R | RX> => fromChannel(Channel.fromPull(pull)) /** * Derive a stream by transforming its pull effect. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3) * * const transformed = Stream.transformPull(stream, (pull) => Effect.succeed(pull)) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(transformed) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 4.0.0 * @category Constructors */ export const transformPull = ( self: Stream, f: (pull: Pull.Pull, E, void>, scope: Scope.Scope) => Effect.Effect< Pull.Pull, E2, void, R2>, EX, RX > ): Stream, R | R2 | RX> => fromChannel( Channel.fromTransform((_, scope) => Effect.flatMap(Channel.toPullScoped(self.channel, scope), (pull) => f(pull as any, scope)) ) ) /** * Transforms a stream by effectfully transforming its pull effect. * * A forked scope is also provided to the transformation function, which is * closed once the resulting stream has finished processing. * * @example * ```ts * import { Console, Effect, Scope, Stream } from "effect" * * const stream = Stream.make(1, 2, 3) * * const transformed = Stream.transformPullBracket( * stream, * (pull, _scope, forkedScope) => * Effect.gen(function*() { * yield* Scope.addFinalizer(forkedScope, Console.log("Releasing scope")) * return pull * }) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(transformed) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [1, 2, 3] * // Releasing scope * ``` * * @since 4.0.0 * @category Constructors */ export const transformPullBracket = ( self: Stream, f: ( pull: Pull.Pull, E, void, R>, scope: Scope.Scope, forkedScope: Scope.Scope ) => Effect.Effect< Pull.Pull, E2, void, R2>, EX, RX > ): Stream, R | R2 | RX> => fromChannel( Channel.fromTransformBracket((_, scope, forkedScope) => Effect.flatMap(Channel.toPullScoped(self.channel, scope), (pull) => f(pull, scope, forkedScope)) ) ) /** * Creates a channel from a stream. * * @example * ```ts * import { Channel, Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 2, 3) * const channel = Stream.toChannel(stream) * const values = yield* Channel.runCollect(channel) * yield* Console.log(values.flat()) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Constructors */ export const toChannel = ( stream: Stream ): Channel.Channel, E, void, unknown, unknown, unknown, R> => stream.channel /** * Creates a stream from a callback that can emit values into a queue. * * You can use the `Queue` with the apis from the `Queue` module to emit * values to the stream or to signal the stream ending. * * By default it uses an "unbounded" buffer size. * You can customize the buffer size and strategy by passing an object as the * second argument with the `bufferSize` and `strategy` fields. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.async` * - `Stream.asyncEffect` * - `Stream.asyncPush` * - `Stream.asyncScoped` * * @example * ```ts * import { Console, Effect, Queue, Stream } from "effect" * * const stream = Stream.callback((queue) => * Effect.sync(() => { * // Emit values to the stream * Queue.offerUnsafe(queue, 1) * Queue.offerUnsafe(queue, 2) * Queue.offerUnsafe(queue, 3) * // Signal completion * Queue.endUnsafe(queue) * }) * ) * * const program = Effect.gen(function*() { * const values = yield* stream.pipe(Stream.runCollect) * yield* Console.log(values) * // [ 1, 2, 3 ] * }) * * Effect.runPromise(program) * ``` * * @since 2.0.0 * @category Constructors */ export const callback = ( f: (queue: Queue.Queue) => Effect.Effect, options?: { readonly bufferSize?: number | undefined readonly strategy?: "sliding" | "dropping" | "suspend" | undefined } ): Stream> => fromChannel(Channel.callbackArray(f, options)) /** * Creates an empty stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.empty.pipe(Stream.runCollect) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // [] * ``` * * @since 4.0.0 * @category Constructors */ export const empty: Stream = fromChannel(Channel.empty) /** * Creates a single-valued pure stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.succeed(3).pipe(Stream.runCollect) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // [ 3 ] * ``` * * @since 2.0.0 * @category Constructors */ export const succeed = (value: A): Stream => fromChannel(Channel.succeed(Arr.of(value))) /** * Creates a stream from a sequence of values. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) // [ 1, 2, 3 ] * }) * * Effect.runPromise(program) * ``` * * @since 2.0.0 * @category Constructors */ export const make = >(...values: As): Stream => fromArray(values) /** * Creates a stream that synchronously evaluates a function and emits the result as a single value. * * The function is evaluated each time the stream is run. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.sync(() => 2 + 1).pipe(Stream.runCollect) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 3 ] * ``` * * @since 2.0.0 * @category Constructors */ export const sync = (evaluate: LazyArg): Stream => fromChannel(Channel.sync(() => Arr.of(evaluate()))) /** * Creates a lazily constructed stream. * * The stream factory is evaluated each time the stream is run. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.suspend(() => Stream.make(1, 2, 3)).pipe(Stream.runCollect) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Constructors */ export const suspend = (stream: LazyArg>): Stream => fromChannel(Channel.suspend(() => stream().channel)) /** * Terminates with the specified error. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.fail("Uh oh!") * const exit = yield* Effect.exit(Stream.runCollect(stream)) * yield* Console.log(exit) * // Output: { _id: 'Exit', _tag: 'Failure', cause: { _id: 'Cause', _tag: 'Fail', failure: 'Uh oh!' } } * }) * * Effect.runPromise(program) * ``` * * @since 2.0.0 * @category Constructors */ export const fail = (error: E): Stream => fromChannel(Channel.fail(error)) /** * Terminates with the specified lazily evaluated error. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.failSync(() => "Uh oh!") * * const program = Effect.gen(function*() { * const exit = yield* Stream.runCollect(stream).pipe(Effect.exit) * yield* Console.log(exit) * }) * * Effect.runPromise(program) * // Output: * // { _id: 'Exit', _tag: 'Failure', cause: { _id: 'Cause', _tag: 'Fail', failure: 'Uh oh!' } } * ``` * * @since 2.0.0 * @category Constructors */ export const failSync = (evaluate: LazyArg): Stream => fromChannel(Channel.failSync(evaluate)) /** * Creates a stream that fails with the specified `Cause`. * * @example * ```ts * import { Cause, Console, Effect, Stream } from "effect" * * const stream = Stream.failCause(Cause.fail("Database connection failed")).pipe( * Stream.catchCause(() => Stream.succeed("recovered")) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * // Output: [ "recovered" ] * }) * * Effect.runPromise(program) * ``` * * @since 2.0.0 * @category Constructors */ export const failCause = (cause: Cause.Cause): Stream => fromChannel(Channel.failCause(cause)) /** * The stream that dies with the specified defect. * * @example * ```ts * import { Cause, Console, Effect, Exit, Stream } from "effect" * * const defect = new Error("Boom") * const stream = Stream.die(defect) * * const program = Effect.gen(function*() { * const exit = yield* Effect.exit(Stream.runCollect(stream)) * const message = Exit.match(exit, { * onSuccess: () => "Exit.Success", * onFailure: (cause) => { * const reason = cause.reasons[0] * const defect = Cause.isDieReason(reason) ? String(reason.defect) : "Unexpected reason" * return `Exit.Failure(${defect})` * } * }) * yield* Console.log(message) * }) * * Effect.runPromise(program) * // Output: Exit.Failure(Error: Boom) * ``` * * @since 2.0.0 * @category Constructors */ export const die = (defect: unknown): Stream => fromChannel(Channel.die(defect)) /** * The stream that always fails with the specified lazily evaluated `Cause`. * * @example * ```ts * import { Cause, Console, Effect, Stream } from "effect" * * const stream = Stream.failCauseSync(() => * Cause.fail("Connection timeout after retries") * ) * * const program = Effect.gen(function*() { * const exit = yield* Stream.runCollect(stream).pipe(Effect.exit) * yield* Console.log(exit) * }) * * Effect.runPromise(program) * // Output: * // { _id: 'Exit', _tag: 'Failure', cause: { _id: 'Cause', _tag: 'Fail', failure: 'Connection timeout after retries' } } * ``` * * @since 2.0.0 * @category Constructors */ export const failCauseSync = (evaluate: LazyArg>): Stream => fromChannel(Channel.failCauseSync(evaluate)) /** * Creates a stream that consumes values from an iterator. * * The `maxChunkSize` parameter controls how many values are pulled per chunk. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * function* numbers() { * yield 1 * yield 2 * yield 3 * } * * const stream = Stream.fromIteratorSucceed(numbers()) * * const program = Effect.gen(function* () { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Constructors */ export const fromIteratorSucceed = (iterator: IterableIterator, maxChunkSize?: number): Stream => fromChannel(Channel.fromIteratorArray(() => iterator, maxChunkSize)) /** * Creates a new `Stream` from an iterable collection of values. * * **Options** * * - `chunkSize`: Maximum number of values emitted per chunk. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const numbers = [1, 2, 3] * * const program = Effect.gen(function*() { * const stream = Stream.fromIterable(numbers) * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Constructors */ export const fromIterable = ( iterable: Iterable, options?: { readonly chunkSize?: number | undefined } ): Stream => Array.isArray(iterable) && options?.chunkSize === undefined ? fromArray(iterable) : fromChannel(Channel.fromIterableArray(iterable, options?.chunkSize)) /** * Creates a stream from an effect producing an iterable of values. * * @example * ```ts * import { Console, Effect, Context, Stream } from "effect" * * class UserRepo extends Context.Service> * }>()("UserRepo") {} * * const listUsers = Effect.service(UserRepo).pipe( * Effect.andThen((repo) => repo.list) * ) * * const stream = Stream.fromIterableEffect(listUsers) * * const program = Effect.gen(function*() { * const users = yield* stream.pipe( * Stream.provideService(UserRepo, { * list: Effect.succeed(["user1", "user2"]) * }), * Stream.runCollect * ) * yield* Console.log(users) * }) * * Effect.runPromise(program) * // Output: [ "user1", "user2" ] * ``` * * @since 2.0.0 * @category Constructors */ export const fromIterableEffect = (iterable: Effect.Effect, E, R>): Stream => unwrap(Effect.map(iterable, fromIterable)) /** * Creates a stream by repeatedly running an effect that yields an iterable of values. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.repeatEffectChunk` * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.fromIterableEffectRepeat(Effect.succeed([1, 2])).pipe( * Stream.take(5) * ) * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 1, 2, 1 ] * ``` * * @since 4.0.0 * @category Constructors */ export const fromIterableEffectRepeat = ( iterable: Effect.Effect, E, R> ): Stream, R> => flatMap(fromEffectRepeat(iterable), fromIterable) /** * Creates a stream from an array of values. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.fromChunk` * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.fromArray([1, 2, 3]) * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 4.0.0 * @category Constructors */ export const fromArray = (array: ReadonlyArray): Stream => Arr.isReadonlyArrayNonEmpty(array) ? fromChannel(Channel.succeed(array)) : empty /** * Creates a stream from an effect that produces an array of values. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.fromArrayEffect(Effect.succeed(["Ada", "Grace"])) * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ "Ada", "Grace" ] * ``` * * @since 4.0.0 * @category Constructors */ export const fromArrayEffect = ( effect: Effect.Effect, E, R> ): Stream, R> => unwrap(Effect.map(effect, fromArray)) as any /** * Creates a stream from an arbitrary number of arrays. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.fromChunks` * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.fromArrays([1, 2], [3, 4]) * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3, 4 ] * ``` * * @since 4.0.0 * @category Constructors */ export const fromArrays = >>( ...arrays: Arr ): Stream => fromChannel(Channel.fromArray(Arr.filter(arrays, Arr.isReadonlyArrayNonEmpty))) /** * Creates a stream from a queue of values. * * **Options** * * - `maxChunkSize`: The maximum number of queued elements to put in one chunk in the stream * - `shutdown`: If `true`, the queue will be shutdown after the stream is evaluated (defaults to `false`) * * @example * ```ts * import { Console, Effect, Queue, Stream } from "effect" * * const program = Effect.gen(function*() { * const queue = yield* Queue.unbounded() * yield* Queue.offer(queue, 1) * yield* Queue.offer(queue, 2) * yield* Queue.offer(queue, 3) * yield* Queue.shutdown(queue) * * const stream = Stream.fromQueue(queue) * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 4.0.0 * @category Constructors */ export const fromQueue = (queue: Queue.Dequeue): Stream> => fromChannel(Channel.fromQueueArray(queue)) /** * Creates a stream from a subscription to a `PubSub`. * * @example * ```ts * import { Console, Effect, Fiber, PubSub, Stream } from "effect" * * const program = Effect.gen(function*() { * const pubsub = yield* PubSub.unbounded() * * const fiber = yield* Stream.fromPubSub(pubsub).pipe( * Stream.take(3), * Stream.runCollect, * Effect.forkChild * ) * * yield* PubSub.publish(pubsub, 1) * yield* PubSub.publish(pubsub, 2) * yield* PubSub.publish(pubsub, 3) * * const values = yield* Fiber.join(fiber) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 4.0.0 * @category Constructors */ export const fromPubSub = (pubsub: PubSub.PubSub): Stream => fromChannel(Channel.fromPubSubArray(pubsub)) /** * Creates a stream from a PubSub of `Take` values. * * `Take` values include end and failure signals. * * @example * ```ts * import { Console, Effect, Exit, PubSub, Stream, Take } from "effect" * * const program = Effect.gen(function*() { * const pubsub = yield* PubSub.unbounded>({ * replay: 3 * }) * * yield* PubSub.publish(pubsub, [1]) * yield* PubSub.publish(pubsub, [2]) * yield* PubSub.publish(pubsub, Exit.succeed(undefined)) * * const values = yield* Stream.fromPubSubTake(pubsub).pipe(Stream.runCollect) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2 ] * ``` * * @since 4.0.0 * @category Constructors */ export const fromPubSubTake = (pubsub: PubSub.PubSub>): Stream => fromChannel(Channel.fromPubSubTake(pubsub)) /** * Creates a stream from a `ReadableStream`. * * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. * * @example * ```ts * import { Console, Data, Effect, Stream } from "effect" * * class StreamError extends Data.TaggedError("StreamError")<{ readonly cause: unknown }> {} * * const readableStream = new ReadableStream({ * start(controller) { * controller.enqueue(1) * controller.enqueue(2) * controller.enqueue(3) * controller.close() * } * }) * * const program = Effect.gen(function*() { * const stream = Stream.fromReadableStream({ * evaluate: () => readableStream, * onError: (cause) => new StreamError({ cause }) * }) * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Constructors */ export const fromReadableStream = ( options: { readonly evaluate: LazyArg> readonly onError: (error: unknown) => E readonly releaseLockOnEnd?: boolean | undefined } ): Stream => fromChannel(Channel.fromTransform(Effect.fnUntraced(function*(_, scope) { const reader = options.evaluate().getReader() yield* Scope.addFinalizer( scope, options.releaseLockOnEnd ? Effect.sync(() => reader.releaseLock()) : Effect.promise(() => reader.cancel()) ) return Effect.flatMap( Effect.tryPromise({ try: () => reader.read(), catch: (reason) => options.onError(reason) }), ({ done, value }) => done ? Cause.done() : Effect.succeed(Arr.of(value)) ) }))) /** * Creates a stream from an AsyncIterable. * * @example * ```ts * import { Console, Data, Effect, Stream } from "effect" * * class StreamError extends Data.TaggedError("StreamError")<{ readonly cause: unknown }> {} * * const iterable = (async function*() { * yield 1 * yield 2 * yield 3 * })() * * const program = Effect.gen(function*() { * const stream = Stream.fromAsyncIterable(iterable, (cause) => new StreamError({ cause })) * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Constructors */ export const fromAsyncIterable = ( iterable: AsyncIterable, onError: (error: unknown) => E ): Stream => fromChannel(Channel.fromAsyncIterableArray(iterable, onError)) /** * Creates a stream that emits each output of a schedule that does not require input, * for as long as the schedule continues. * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const program = Effect.gen(function*() { * const schedule = Schedule.spaced("50 millis").pipe( * Schedule.both(Schedule.recurs(2)) * ) * const stream = Stream.fromSchedule(schedule) * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 0, 1, 2 ] * ``` * * @since 2.0.0 * @category Constructors */ export const fromSchedule = (schedule: Schedule.Schedule): Stream => fromPull( Effect.map( Schedule.toStepWithSleep(schedule), (step) => Pull.catchDone(Effect.map(step(void 0), Arr.of), () => Cause.done()) ) ) /** * Creates a stream from a PubSub subscription. * * Use `PubSub.subscribe` to create the subscription and `Stream.take` or * cancellation to control how many values are consumed. * * @example * ```ts * import { Console, Effect, PubSub, Stream } from "effect" * * const program = Effect.scoped(Effect.gen(function*() { * const pubsub = yield* PubSub.unbounded() * const subscription = yield* PubSub.subscribe(pubsub) * * yield* PubSub.publish(pubsub, 1) * yield* PubSub.publish(pubsub, 2) * * const stream = Stream.fromSubscription(subscription) * const values = yield* stream.pipe(Stream.take(2), Stream.runCollect) * yield* Console.log(values) * })) * * Effect.runPromise(program) * // Output: [ 1, 2 ] * ``` * * @since 4.0.0 * @category Constructors */ export const fromSubscription = (pubsub: PubSub.Subscription): Stream => fromChannel(Channel.fromSubscriptionArray(pubsub)) /** * Interface representing an event listener target. * * @since 3.4.0 * @category Models */ export interface EventListener { addEventListener( event: string, f: (event: A) => void, options?: { readonly capture?: boolean readonly passive?: boolean readonly once?: boolean readonly signal?: AbortSignal } | boolean ): void removeEventListener( event: string, f: (event: A) => void, options?: { readonly capture?: boolean } | boolean ): void } /** * Creates a stream from an event listener. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * declare const target: Stream.EventListener * * const program = Effect.gen(function*() { * const stream = Stream.fromEventListener(target, "data").pipe( * Stream.take(3) * ) * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 3.1.0 * @category Constructors */ export const fromEventListener = ( target: EventListener, type: string, options?: boolean | { readonly capture?: boolean readonly passive?: boolean readonly once?: boolean readonly bufferSize?: number | undefined } | undefined ): Stream => callback((queue) => { function emit(event: A) { Queue.offerUnsafe(queue, event) } return Effect.acquireRelease( Effect.sync(() => target.addEventListener(type, emit, options)), () => Effect.sync(() => target.removeEventListener(type, emit, options)) ) }, { bufferSize: typeof options === "object" ? options.bufferSize : undefined }) /** * Creates a stream by peeling off successive layers of a state value. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.unfold(1, (n) => Effect.succeed([n, n + 1] as const)) * const values = yield* Stream.runCollect(stream.pipe(Stream.take(5))) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3, 4, 5 ] * ``` * * @since 2.0.0 * @category Constructors */ export const unfold = ( s: S, f: (s: S) => Effect.Effect ): Stream => fromPull(Effect.sync(() => { let state = s return Effect.flatMap(Effect.suspend(() => f(state)), (next) => { if (next === undefined) return Cause.done() state = next[1] return Effect.succeed(Arr.of(next[0])) }) })) /** * Like `Stream.unfold`, but allows the emission of values to end one step further * than the unfolding of the state. This is useful for embedding paginated APIs, * hence the name. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * import * as Option from "effect/Option" * * const stream = Stream.paginate(0, (n: number) => * Effect.succeed( * [ * [n], * n < 3 ? Option.some(n + 1) : Option.none() * ] as const * )) * * Effect.runPromise(Stream.runCollect(stream)).then(console.log) * // Output: [ 0, 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Constructors */ export const paginate = ( s: S, f: ( s: S ) => Effect.Effect, Option.Option], E, R> ): Stream => fromPull(Effect.sync(() => { let state = s let done = false return Effect.suspend(function loop(): Pull.Pull, E, void, R> { if (done) return Cause.done() return Effect.flatMap(f(state), ([a, s]) => { if (Option.isNone(s)) { done = true } else { state = s.value } if (!Arr.isReadonlyArrayNonEmpty(a)) return loop() return Effect.succeed(a) }) }) })) /** * Creates an infinite stream by repeatedly applying a function to a seed value. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.iterate(1, (n) => n + 1).pipe(Stream.take(3)) * * const program = Effect.gen(function* () { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Constructors */ export const iterate = (value: A, next: (value: A) => A): Stream => unfold(value, (a) => Effect.succeed([a, next(a)])) /** * Constructs a stream from a range of integers, including both endpoints. * * If the provided `min` is greater than `max`, the stream will not emit any * values. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.range(1, 5).pipe(Stream.runCollect) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3, 4, 5 ] * ``` * @since 4.0.0 * @category Constructors */ export const range = ( min: number, max: number, chunkSize = Channel.DefaultChunkSize ): Stream => min > max ? empty : fromPull(Effect.sync(() => { let start = min let done = false return Effect.suspend(() => { if (done) return Cause.done() const remaining = max - start + 1 if (remaining > chunkSize) { const chunk = Arr.range(start, start + chunkSize - 1) start += chunkSize return Effect.succeed(chunk) } const chunk = Arr.range(start, start + remaining - 1) done = true return Effect.succeed(chunk) }) })) /** * The stream that never produces any value or fails with any error. * * @example * ```ts * import { Effect, Stream } from "effect" * * const program = Stream.never.pipe( * Stream.take(0), * Stream.runCollect * ) * * Effect.runPromise(program).then(console.log) * // [] * ``` * * @since 4.0.0 * @category Constructors */ export const never: Stream = fromChannel(Channel.never) /** * Creates a stream produced from an `Effect`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const effect = Effect.succeed(Stream.make(1, 2, 3)) * * const stream = Stream.unwrap(effect) * * const program = Effect.gen(function*() { * const chunk = yield* Stream.runCollect(stream) * yield* Console.log(chunk) * }) * // [1, 2, 3] * ``` * * @since 2.0.0 * @category Constructors */ export const unwrap = ( effect: Effect.Effect, E, R> ): Stream> => fromChannel(Channel.unwrap(Effect.map(effect, toChannel))) /** * Runs a stream that requires `Scope` in a managed scope, ensuring its * finalizers are run when the stream completes. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.scoped( * Stream.fromEffect( * Effect.acquireRelease( * Console.log("acquire").pipe(Effect.as("resource")), * () => Console.log("release") * ) * ) * ) * * Effect.runPromise(Stream.runCollect(stream)).then(console.log) * // acquire * // release * // [ "resource" ] * ``` * * @since 2.0.0 * @category Constructors */ export const scoped = ( self: Stream ): Stream> => fromChannel(Channel.scoped(self.channel)) /** * Transforms the elements of this stream using the supplied function. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.fromArray([1, 2, 3]).pipe(Stream.map((n, i) => n + i)) * const program = Stream.runCollect(stream).pipe( * Effect.tap((values) => Console.log(values)) * ) * * Effect.runPromise(program) * // [ 1, 3, 5 ] * ``` * * @since 2.0.0 * @category Mapping */ export const map: { /** * Transforms the elements of this stream using the supplied function. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.fromArray([1, 2, 3]).pipe(Stream.map((n, i) => n + i)) * const program = Stream.runCollect(stream).pipe( * Effect.tap((values) => Console.log(values)) * ) * * Effect.runPromise(program) * // [ 1, 3, 5 ] * ``` * * @since 2.0.0 * @category Mapping */ (f: (a: A, i: number) => B): (self: Stream) => Stream /** * Transforms the elements of this stream using the supplied function. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.fromArray([1, 2, 3]).pipe(Stream.map((n, i) => n + i)) * const program = Stream.runCollect(stream).pipe( * Effect.tap((values) => Console.log(values)) * ) * * Effect.runPromise(program) * // [ 1, 3, 5 ] * ``` * * @since 2.0.0 * @category Mapping */ (self: Stream, f: (a: A, i: number) => B): Stream } = dual(2, (self: Stream, f: (a: A, i: number) => B): Stream => suspend(() => { let i = 0 return fromChannel(Channel.map( self.channel, Arr.map((o) => f(o, i++)) )) })) /** * Maps both the failure and success channels of a stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const mapper = { * onFailure: (error: string) => `error: ${error}`, * onSuccess: (value: number) => value * 2 * } * * const program = Effect.gen(function*() { * const success = yield* Stream.make(1, 2).pipe( * Stream.mapBoth(mapper), * Stream.runCollect * ) * yield* Console.log(success) * * const failure = yield* Stream.fail("boom").pipe( * Stream.mapBoth(mapper), * Stream.catch((error: string) => Stream.succeed(error)), * Stream.runCollect * ) * yield* Console.log(failure) * }) * * Effect.runPromise(program) * // Output: [ 2, 4 ] * // Output: [ "error: boom" ] * ``` * * @since 2.0.0 * @category Mapping */ export const mapBoth: { /** * Maps both the failure and success channels of a stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const mapper = { * onFailure: (error: string) => `error: ${error}`, * onSuccess: (value: number) => value * 2 * } * * const program = Effect.gen(function*() { * const success = yield* Stream.make(1, 2).pipe( * Stream.mapBoth(mapper), * Stream.runCollect * ) * yield* Console.log(success) * * const failure = yield* Stream.fail("boom").pipe( * Stream.mapBoth(mapper), * Stream.catch((error: string) => Stream.succeed(error)), * Stream.runCollect * ) * yield* Console.log(failure) * }) * * Effect.runPromise(program) * // Output: [ 2, 4 ] * // Output: [ "error: boom" ] * ``` * * @since 2.0.0 * @category Mapping */ ( options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } ): (self: Stream) => Stream /** * Maps both the failure and success channels of a stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const mapper = { * onFailure: (error: string) => `error: ${error}`, * onSuccess: (value: number) => value * 2 * } * * const program = Effect.gen(function*() { * const success = yield* Stream.make(1, 2).pipe( * Stream.mapBoth(mapper), * Stream.runCollect * ) * yield* Console.log(success) * * const failure = yield* Stream.fail("boom").pipe( * Stream.mapBoth(mapper), * Stream.catch((error: string) => Stream.succeed(error)), * Stream.runCollect * ) * yield* Console.log(failure) * }) * * Effect.runPromise(program) * // Output: [ 2, 4 ] * // Output: [ "error: boom" ] * ``` * * @since 2.0.0 * @category Mapping */ ( self: Stream, options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } ): Stream } = dual(2, ( self: Stream, options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } ): Stream => self.pipe( map(options.onSuccess), mapError(options.onFailure) )) /** * Transforms each emitted chunk using the provided function, which receives the chunk and its index. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.mapChunks` * * @example * ```ts * import { Array, Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3, 4).pipe( * Stream.rechunk(2), * Stream.mapArray((chunk, index) => Array.map(chunk, (n) => n + index)), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 4, 5 ] * ``` * * @since 2.0.0 * @category Mapping */ export const mapArray: { /** * Transforms each emitted chunk using the provided function, which receives the chunk and its index. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.mapChunks` * * @example * ```ts * import { Array, Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3, 4).pipe( * Stream.rechunk(2), * Stream.mapArray((chunk, index) => Array.map(chunk, (n) => n + index)), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 4, 5 ] * ``` * * @since 2.0.0 * @category Mapping */ ( f: (a: Arr.NonEmptyReadonlyArray, i: number) => Arr.NonEmptyReadonlyArray ): (self: Stream) => Stream /** * Transforms each emitted chunk using the provided function, which receives the chunk and its index. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.mapChunks` * * @example * ```ts * import { Array, Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3, 4).pipe( * Stream.rechunk(2), * Stream.mapArray((chunk, index) => Array.map(chunk, (n) => n + index)), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 4, 5 ] * ``` * * @since 2.0.0 * @category Mapping */ ( self: Stream, f: (a: Arr.NonEmptyReadonlyArray, i: number) => Arr.NonEmptyReadonlyArray ): Stream } = dual(2, ( self: Stream, f: (a: Arr.NonEmptyReadonlyArray, i: number) => Arr.NonEmptyReadonlyArray ): Stream => fromChannel(Channel.map(self.channel, f))) /** * Maps over elements of the stream with the specified effectful function. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3) * * const mappedStream = stream.pipe( * Stream.mapEffect((n) => * Effect.gen(function*() { * yield* Console.log(`Processing: ${n}`) * return n * 2 * }) * ) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(mappedStream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: * // Processing: 1 * // Processing: 2 * // Processing: 3 * // [2, 4, 6] * ``` * * @since 2.0.0 * @category Mapping */ export const mapEffect: { /** * Maps over elements of the stream with the specified effectful function. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3) * * const mappedStream = stream.pipe( * Stream.mapEffect((n) => * Effect.gen(function*() { * yield* Console.log(`Processing: ${n}`) * return n * 2 * }) * ) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(mappedStream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: * // Processing: 1 * // Processing: 2 * // Processing: 3 * // [2, 4, 6] * ``` * * @since 2.0.0 * @category Mapping */ ( f: (a: A, i: number) => Effect.Effect, options?: { readonly concurrency?: number | "unbounded" | undefined readonly unordered?: boolean | undefined } | undefined ): (self: Stream) => Stream /** * Maps over elements of the stream with the specified effectful function. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3) * * const mappedStream = stream.pipe( * Stream.mapEffect((n) => * Effect.gen(function*() { * yield* Console.log(`Processing: ${n}`) * return n * 2 * }) * ) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(mappedStream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: * // Processing: 1 * // Processing: 2 * // Processing: 3 * // [2, 4, 6] * ``` * * @since 2.0.0 * @category Mapping */ ( self: Stream, f: (a: A, i: number) => Effect.Effect, options?: { readonly concurrency?: number | "unbounded" | undefined readonly unordered?: boolean | undefined } | undefined ): Stream } = dual((args) => isStream(args[0]), ( self: Stream, f: (a: A, i: number) => Effect.Effect, options?: { readonly concurrency?: number | "unbounded" | undefined readonly unordered?: boolean | undefined } | undefined ): Stream => self.channel.pipe( Channel.flattenArray, Channel.mapEffect(f, options), Channel.map(Arr.of), fromChannel )) /** * Flattens a stream of `Effect` values into a stream of their results. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(Effect.succeed(1), Effect.succeed(2), Effect.succeed(3)) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(stream.pipe(Stream.flattenEffect())) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [1, 2, 3] * ``` * * @since 2.0.0 * @category Mapping */ export const flattenEffect: < Arg extends Stream, any, any> | { readonly concurrency?: number | "unbounded" | undefined readonly unordered?: boolean | undefined } | undefined = { readonly concurrency?: number | "unbounded" | undefined readonly unordered?: boolean | undefined } >( selfOrOptions?: Arg, options?: { readonly concurrency?: number | "unbounded" | undefined readonly unordered?: boolean | undefined } | undefined ) => [Arg] extends [Stream, infer _E, infer _R>] ? Stream<_A, _EX | _E, _RX | _R> : (self: Stream, E, R>) => Stream = dual( (args) => isStream(args[0]), ( self: Stream, E, R>, options?: { readonly concurrency?: number | "unbounded" | undefined readonly unordered?: boolean | undefined } | undefined ): Stream => mapEffect(self, identity, options) ) /** * Effectfully maps over non-empty array chunks emitted by the stream. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.mapChunksEffect` * * @example * ```ts * import { Array, Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.fromArray([1, 2, 3, 4]).pipe( * Stream.rechunk(2), * Stream.mapArrayEffect((chunk, index) => * Effect.succeed(Array.map(chunk, (n) => n + index * 10)) * ), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [1, 2, 13, 14] * ``` * * @since 4.0.0 * @category Mapping */ export const mapArrayEffect: { /** * Effectfully maps over non-empty array chunks emitted by the stream. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.mapChunksEffect` * * @example * ```ts * import { Array, Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.fromArray([1, 2, 3, 4]).pipe( * Stream.rechunk(2), * Stream.mapArrayEffect((chunk, index) => * Effect.succeed(Array.map(chunk, (n) => n + index * 10)) * ), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [1, 2, 13, 14] * ``` * * @since 4.0.0 * @category Mapping */ ( f: (a: Arr.NonEmptyReadonlyArray, i: number) => Effect.Effect, E2, R2> ): (self: Stream) => Stream /** * Effectfully maps over non-empty array chunks emitted by the stream. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.mapChunksEffect` * * @example * ```ts * import { Array, Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.fromArray([1, 2, 3, 4]).pipe( * Stream.rechunk(2), * Stream.mapArrayEffect((chunk, index) => * Effect.succeed(Array.map(chunk, (n) => n + index * 10)) * ), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [1, 2, 13, 14] * ``` * * @since 4.0.0 * @category Mapping */ ( self: Stream, f: (a: Arr.NonEmptyReadonlyArray, i: number) => Effect.Effect, E2, R2> ): Stream } = dual(2, ( self: Stream, f: (a: Arr.NonEmptyReadonlyArray, i: number) => Effect.Effect, E2, R2> ): Stream => fromChannel(Channel.mapEffect(self.channel, f))) /** * Lifts failures and successes into a `Result`, yielding a stream that cannot fail. * * The stream ends after the first failure, emitting a `Result.fail` value. * * **Previously Known As:** * * This API replaces the following from Effect 3.x: * * - `Stream.either` * * @example * ```ts * import { Console, Effect, Result, Stream } from "effect" * * const program = Effect.gen(function*() { * const results = yield* Stream.make(1, 2).pipe( * Stream.concat(Stream.fail("boom")), * Stream.result, * Stream.map(Result.match({ * onFailure: (error) => `failure: ${error}`, * onSuccess: (value) => `success: ${value}` * })), * Stream.runCollect * ) * yield* Console.log(results) * }) * * Effect.runPromise(program) * // Output: [ "success: 1", "success: 2", "failure: boom" ] * ``` * * @since 4.0.0 * @category Error Handling */ export const result = (self: Stream): Stream, never, R> => self.pipe( map(Result.succeed), catch_((e) => succeed(Result.fail(e))) ) /** * Runs the provided effect for each element while preserving the elements. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.fromArray([1, 2, 3]).pipe( * Stream.tap((n) => Console.log(`before mapping: ${n}`)), * Stream.map((n) => n * 2), * Stream.tap((n) => Console.log(`after mapping: ${n}`)), * Stream.runCollect * ) * * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: * // before mapping: 1 * // after mapping: 2 * // before mapping: 2 * // after mapping: 4 * // before mapping: 3 * // after mapping: 6 * // [ 2, 4, 6 ] * ``` * * @since 2.0.0 * @category Sequencing */ export const tap: { /** * Runs the provided effect for each element while preserving the elements. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.fromArray([1, 2, 3]).pipe( * Stream.tap((n) => Console.log(`before mapping: ${n}`)), * Stream.map((n) => n * 2), * Stream.tap((n) => Console.log(`after mapping: ${n}`)), * Stream.runCollect * ) * * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: * // before mapping: 1 * // after mapping: 2 * // before mapping: 2 * // after mapping: 4 * // before mapping: 3 * // after mapping: 6 * // [ 2, 4, 6 ] * ``` * * @since 2.0.0 * @category Sequencing */ ( f: (a: NoInfer) => Effect.Effect, options?: { readonly concurrency?: number | "unbounded" | undefined } | undefined ): (self: Stream) => Stream /** * Runs the provided effect for each element while preserving the elements. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.fromArray([1, 2, 3]).pipe( * Stream.tap((n) => Console.log(`before mapping: ${n}`)), * Stream.map((n) => n * 2), * Stream.tap((n) => Console.log(`after mapping: ${n}`)), * Stream.runCollect * ) * * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: * // before mapping: 1 * // after mapping: 2 * // before mapping: 2 * // after mapping: 4 * // before mapping: 3 * // after mapping: 6 * // [ 2, 4, 6 ] * ``` * * @since 2.0.0 * @category Sequencing */ ( self: Stream, f: (a: NoInfer) => Effect.Effect, options?: { readonly concurrency?: number | "unbounded" | undefined } | undefined ): Stream } = dual((args) => isStream(args[0]), ( self: Stream, f: (a: NoInfer) => Effect.Effect, options?: { readonly concurrency?: number | "unbounded" | undefined } | undefined ): Stream => mapEffect( self, (a) => Effect.as(f(a), a), options )) /** * Returns a stream that effectfully "peeks" at elements and failures. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 2).pipe( * Stream.concat(Stream.fail("boom")), * Stream.tapBoth({ * onElement: (value) => Console.log(`seen: ${value}`), * onError: (error) => Console.log(`error: ${error}`) * }), * Stream.catch(() => Stream.make(3)) * ) * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: * // seen: 1 * // seen: 2 * // error: boom * // [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Sequencing */ export const tapBoth: { /** * Returns a stream that effectfully "peeks" at elements and failures. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 2).pipe( * Stream.concat(Stream.fail("boom")), * Stream.tapBoth({ * onElement: (value) => Console.log(`seen: ${value}`), * onError: (error) => Console.log(`error: ${error}`) * }), * Stream.catch(() => Stream.make(3)) * ) * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: * // seen: 1 * // seen: 2 * // error: boom * // [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Sequencing */ ( options: { readonly onElement: (a: NoInfer) => Effect.Effect readonly onError: (a: NoInfer) => Effect.Effect readonly concurrency?: number | "unbounded" | undefined } ): (self: Stream) => Stream /** * Returns a stream that effectfully "peeks" at elements and failures. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 2).pipe( * Stream.concat(Stream.fail("boom")), * Stream.tapBoth({ * onElement: (value) => Console.log(`seen: ${value}`), * onError: (error) => Console.log(`error: ${error}`) * }), * Stream.catch(() => Stream.make(3)) * ) * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: * // seen: 1 * // seen: 2 * // error: boom * // [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Sequencing */ ( self: Stream, options: { readonly onElement: (a: NoInfer) => Effect.Effect readonly onError: (a: NoInfer) => Effect.Effect readonly concurrency?: number | "unbounded" | undefined } ): Stream } = dual(2, ( self: Stream, options: { readonly onElement: (a: NoInfer) => Effect.Effect readonly onError: (a: NoInfer) => Effect.Effect readonly concurrency?: number | "unbounded" | undefined } ): Stream => self.pipe( tapError(options.onError), tap(options.onElement, { concurrency: options.concurrency }) )) /** * Sends all elements emitted by this stream to the specified sink in addition * to emitting them. * * @example * ```ts * import { Console, Effect, Ref, Sink, Stream } from "effect" * * const program = Effect.gen(function*() { * const seen = yield* Ref.make>([]) * const sink = Sink.forEach((value: number) => * Ref.update(seen, (items) => [...items, value]) * ) * const result = yield* Stream.make(1, 2, 3).pipe( * Stream.tapSink(sink), * Stream.runCollect * ) * const tapped = yield* Ref.get(seen) * yield* Console.log(tapped) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [1, 2, 3] * // Output: [1, 2, 3] * ``` * * @since 2.0.0 * @category Sequencing */ export const tapSink: { /** * Sends all elements emitted by this stream to the specified sink in addition * to emitting them. * * @example * ```ts * import { Console, Effect, Ref, Sink, Stream } from "effect" * * const program = Effect.gen(function*() { * const seen = yield* Ref.make>([]) * const sink = Sink.forEach((value: number) => * Ref.update(seen, (items) => [...items, value]) * ) * const result = yield* Stream.make(1, 2, 3).pipe( * Stream.tapSink(sink), * Stream.runCollect * ) * const tapped = yield* Ref.get(seen) * yield* Console.log(tapped) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [1, 2, 3] * // Output: [1, 2, 3] * ``` * * @since 2.0.0 * @category Sequencing */ (sink: Sink.Sink): (self: Stream) => Stream /** * Sends all elements emitted by this stream to the specified sink in addition * to emitting them. * * @example * ```ts * import { Console, Effect, Ref, Sink, Stream } from "effect" * * const program = Effect.gen(function*() { * const seen = yield* Ref.make>([]) * const sink = Sink.forEach((value: number) => * Ref.update(seen, (items) => [...items, value]) * ) * const result = yield* Stream.make(1, 2, 3).pipe( * Stream.tapSink(sink), * Stream.runCollect * ) * const tapped = yield* Ref.get(seen) * yield* Console.log(tapped) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [1, 2, 3] * // Output: [1, 2, 3] * ``` * * @since 2.0.0 * @category Sequencing */ (self: Stream, sink: Sink.Sink): Stream } = dual( 2, ( self: Stream, sink: Sink.Sink ): Stream => transformPullBracket( self, Effect.fnUntraced(function*(pull, _, scope) { const upstreamLatch = Latch.makeUnsafe() const sinkLatch = Latch.makeUnsafe() let chunk: Arr.NonEmptyReadonlyArray | undefined = undefined let causeSink: Cause.Cause | undefined = undefined let sinkDone = false let streamDone = false const sinkUpstream = upstreamLatch.whenOpen(Effect.suspend(() => { if (chunk) { const arr = chunk! chunk = undefined if (!streamDone) upstreamLatch.closeUnsafe() return Effect.as(sinkLatch.open, arr) } return Cause.done() })) yield* Effect.suspend(() => sink.transform(sinkUpstream, scope)).pipe( (eff) => Effect.onExitPrimitive(eff, (exit) => { sinkDone = true if (Exit.isFailure(exit)) { causeSink = exit.cause } return sinkLatch.open }, true), Effect.forkIn(scope) ) const pullAndOffer = pull.pipe( Effect.flatMap((chunk_) => { chunk = chunk_ sinkLatch.closeUnsafe() upstreamLatch.openUnsafe() return Effect.as(sinkLatch.await, chunk_) }), Pull.catchDone(() => { streamDone = true sinkLatch.closeUnsafe() upstreamLatch.openUnsafe() return Effect.flatMap(sinkLatch.await, () => Cause.done()) }) ) return Effect.suspend((): Pull.Pull, E | E2, void, R> => { if (causeSink) { return Effect.failCause(causeSink) } else if (sinkDone) { return pull } return pullAndOffer }) }) ) ) /** * Maps each element to a stream and concatenates the results in order. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.flatMap((n) => Stream.make(n, n * 2)), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 2, 4, 3, 6 ] * ``` * * @since 2.0.0 * @category Mapping */ export const flatMap: { /** * Maps each element to a stream and concatenates the results in order. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.flatMap((n) => Stream.make(n, n * 2)), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 2, 4, 3, 6 ] * ``` * * @since 2.0.0 * @category Mapping */ ( f: (a: A) => Stream, options?: { readonly concurrency?: number | "unbounded" | undefined readonly bufferSize?: number | undefined } | undefined ): (self: Stream) => Stream /** * Maps each element to a stream and concatenates the results in order. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.flatMap((n) => Stream.make(n, n * 2)), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 2, 4, 3, 6 ] * ``` * * @since 2.0.0 * @category Mapping */ ( self: Stream, f: (a: A) => Stream, options?: { readonly concurrency?: number | "unbounded" | undefined readonly bufferSize?: number | undefined } | undefined ): Stream } = dual((args) => isStream(args[0]), ( self: Stream, f: (a: A) => Stream, options?: { readonly concurrency?: number | "unbounded" | undefined readonly bufferSize?: number | undefined } | undefined ): Stream => self.channel.pipe( Channel.flattenArray, Channel.flatMap((a) => f(a).channel, options), fromChannel )) /** * Switches to the latest stream produced by the mapping function, interrupting * the previous stream when a new element arrives. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Stream.make(1, 2, 3).pipe( * Stream.switchMap((n) => (n === 3 ? Stream.make(n) : Stream.never)), * Stream.runCollect * ) * * Effect.gen(function*() { * const result = yield* program * yield* Console.log(result) * // Output: [ 3 ] * }) * ``` * * @since 4.0.0 * @category Sequencing */ export const switchMap: { /** * Switches to the latest stream produced by the mapping function, interrupting * the previous stream when a new element arrives. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Stream.make(1, 2, 3).pipe( * Stream.switchMap((n) => (n === 3 ? Stream.make(n) : Stream.never)), * Stream.runCollect * ) * * Effect.gen(function*() { * const result = yield* program * yield* Console.log(result) * // Output: [ 3 ] * }) * ``` * * @since 4.0.0 * @category Sequencing */ ( f: (a: A) => Stream, options?: { readonly concurrency?: number | "unbounded" | undefined readonly bufferSize?: number | undefined } | undefined ): (self: Stream) => Stream /** * Switches to the latest stream produced by the mapping function, interrupting * the previous stream when a new element arrives. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Stream.make(1, 2, 3).pipe( * Stream.switchMap((n) => (n === 3 ? Stream.make(n) : Stream.never)), * Stream.runCollect * ) * * Effect.gen(function*() { * const result = yield* program * yield* Console.log(result) * // Output: [ 3 ] * }) * ``` * * @since 4.0.0 * @category Sequencing */ ( self: Stream, f: (a: A) => Stream, options?: { readonly concurrency?: number | "unbounded" | undefined readonly bufferSize?: number | undefined } | undefined ): Stream } = dual((args) => isStream(args[0]), ( self: Stream, f: (a: A) => Stream, options?: { readonly concurrency?: number | "unbounded" | undefined readonly bufferSize?: number | undefined } | undefined ): Stream => self.channel.pipe( Channel.flattenArray, Channel.switchMap((a) => f(a).channel, options), fromChannel )) /** * Flattens a stream of streams into a single stream by concatenating the * inner streams in strict order. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const streamOfStreams = Stream.make( * Stream.make(1, 2), * Stream.make(3, 4), * Stream.make(5, 6) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(Stream.flatten(streamOfStreams)) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3, 4, 5, 6 ] * ``` * * @since 2.0.0 * @category Mapping */ export const flatten: < Arg extends Stream, any, any> | { readonly concurrency?: number | "unbounded" | undefined readonly bufferSize?: number | undefined } | undefined = { readonly concurrency?: number | "unbounded" | undefined readonly bufferSize?: number | undefined } >( selfOrOptions?: Arg, options?: { readonly concurrency?: number | "unbounded" | undefined readonly bufferSize?: number | undefined } | undefined ) => [Arg] extends [Stream, infer _E2, infer _R2>] ? Stream<_A, _E | _E2, _R | _R2> : (self: Stream, E2, R2>) => Stream = dual( (args) => isStream(args[0]), ( self: Stream, E2, R2>, options?: { readonly concurrency?: number | "unbounded" | undefined readonly bufferSize?: number | undefined } | undefined ): Stream => flatMap(self, identity, options) ) /** * Flattens a stream of non-empty arrays into a stream of elements. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.flattenChunks` * * @example * ```ts * import { Array, Console, Effect, Stream } from "effect" * * const stream = Stream.make(Array.make(1, 2), Array.make(3)) * * const program = Effect.gen(function* () { * const result = yield* Stream.runCollect(Stream.flattenArray(stream)) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 4.0.0 * @category Sequencing */ export const flattenArray = (self: Stream, E, R>): Stream => fromChannel(Channel.flattenArray(self.channel)) /** * Converts this stream to one that runs its effects but emits no elements. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.range(1, 6).pipe(Stream.drain, Stream.runCollect) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [] * ``` * * @since 2.0.0 * @category Sequencing */ export const drain = (self: Stream): Stream => fromChannel(Channel.drain(self.channel)) /** * Runs the provided stream in the background while this stream runs, interrupting it * when this stream completes and failing if the background stream fails or defects. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const foreground = Stream.make(1, 2) * const background = Stream.fromEffect(Console.log("background task")) * * const program = Effect.gen(function*() { * const values = yield* foreground.pipe( * Stream.drainFork(background), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: background task * // Output: [ 1, 2 ] * ``` * * @since 2.0.0 * @category Sequencing */ export const drainFork: { /** * Runs the provided stream in the background while this stream runs, interrupting it * when this stream completes and failing if the background stream fails or defects. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const foreground = Stream.make(1, 2) * const background = Stream.fromEffect(Console.log("background task")) * * const program = Effect.gen(function*() { * const values = yield* foreground.pipe( * Stream.drainFork(background), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: background task * // Output: [ 1, 2 ] * ``` * * @since 2.0.0 * @category Sequencing */ (that: Stream): (self: Stream) => Stream /** * Runs the provided stream in the background while this stream runs, interrupting it * when this stream completes and failing if the background stream fails or defects. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const foreground = Stream.make(1, 2) * const background = Stream.fromEffect(Console.log("background task")) * * const program = Effect.gen(function*() { * const values = yield* foreground.pipe( * Stream.drainFork(background), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: background task * // Output: [ 1, 2 ] * ``` * * @since 2.0.0 * @category Sequencing */ (self: Stream, that: Stream): Stream } = dual( 2, (self: Stream, that: Stream): Stream => mergeEffect(self, runDrain(that)) ) /** * Repeats the entire stream according to the provided schedule. * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const program = Effect.gen(function* () { * const result = yield* Stream.make(1).pipe( * Stream.repeat(Schedule.recurs(4)), * Stream.runCollect * ) * * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 1, 1, 1, 1 ] * ``` * * @since 2.0.0 * @category Sequencing */ export const repeat: { /** * Repeats the entire stream according to the provided schedule. * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const program = Effect.gen(function* () { * const result = yield* Stream.make(1).pipe( * Stream.repeat(Schedule.recurs(4)), * Stream.runCollect * ) * * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 1, 1, 1, 1 ] * ``` * * @since 2.0.0 * @category Sequencing */ ( schedule: | Schedule.Schedule | (( $: (_: Schedule.Schedule) => Schedule.Schedule ) => Schedule.Schedule) ): (self: Stream) => Stream /** * Repeats the entire stream according to the provided schedule. * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const program = Effect.gen(function* () { * const result = yield* Stream.make(1).pipe( * Stream.repeat(Schedule.recurs(4)), * Stream.runCollect * ) * * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 1, 1, 1, 1 ] * ``` * * @since 2.0.0 * @category Sequencing */ ( self: Stream, schedule: | Schedule.Schedule | (( $: (_: Schedule.Schedule) => Schedule.Schedule ) => Schedule.Schedule) ): Stream } = dual(2, ( self: Stream, schedule: | Schedule.Schedule | (( $: (_: Schedule.Schedule) => Schedule.Schedule ) => Schedule.Schedule) ): Stream => fromChannel(Channel.repeat(self.channel, schedule))) /** * Spaces the stream's elements according to the provided `schedule`. * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3).pipe( * Stream.schedule(Schedule.spaced("10 millis")), * Stream.runCollect * ) * * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Rate Limiting */ export const schedule: { /** * Spaces the stream's elements according to the provided `schedule`. * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3).pipe( * Stream.schedule(Schedule.spaced("10 millis")), * Stream.runCollect * ) * * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Rate Limiting */ (schedule: Schedule.Schedule, E2, R2>): (self: Stream) => Stream /** * Spaces the stream's elements according to the provided `schedule`. * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3).pipe( * Stream.schedule(Schedule.spaced("10 millis")), * Stream.runCollect * ) * * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Rate Limiting */ (self: Stream, schedule: Schedule.Schedule, E2, R2>): Stream } = dual(2, ( self: Stream, schedule: Schedule.Schedule, E2, R2> ): Stream => self.channel.pipe( Channel.flattenArray, Channel.schedule(schedule), Channel.map(Arr.of), fromChannel )) /** * Ends the stream if it does not produce a value within the specified duration. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1).pipe( * Stream.concat(Stream.never), * Stream.timeout("1 second"), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1 ] * ``` * * @since 2.0.0 * @category Rate Limiting */ export const timeout: { /** * Ends the stream if it does not produce a value within the specified duration. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1).pipe( * Stream.concat(Stream.never), * Stream.timeout("1 second"), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1 ] * ``` * * @since 2.0.0 * @category Rate Limiting */ (duration: Duration.Input): (self: Stream) => Stream /** * Ends the stream if it does not produce a value within the specified duration. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1).pipe( * Stream.concat(Stream.never), * Stream.timeout("1 second"), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1 ] * ``` * * @since 2.0.0 * @category Rate Limiting */ (self: Stream, duration: Duration.Input): Stream } = dual( 2, (self: Stream, duration: Duration.Input): Stream => timeoutOrElse(self, { duration, orElse: () => empty }) ) /** * @since 2.0.0 * @category Rate Limiting */ export const timeoutOrElse: { /** * @since 2.0.0 * @category Rate Limiting */ ( options: { readonly duration: Duration.Input readonly orElse: () => Stream } ): (self: Stream) => Stream /** * @since 2.0.0 * @category Rate Limiting */ ( self: Stream, options: { readonly duration: Duration.Input readonly orElse: () => Stream } ): Stream } = dual( 2, ( self: Stream, options: { readonly duration: Duration.Input readonly orElse: () => Stream } ): Stream => { const duration = Duration.fromInputUnsafe(options.duration) if (!Duration.isFinite(duration)) return self if (Duration.isZero(duration)) return suspend(options.orElse) const timeoutSymbol = Symbol() return catchCause( suspend(() => { const parent = Fiber.getCurrent()! const clock = parent.getRef(Clock) const durationMs = Duration.toMillis(duration) let deadline: number | undefined = undefined const latch = Latch.makeUnsafe(false) return merge( transformPull(self, (pull, _scope) => Effect.suspend(() => { deadline = clock.currentTimeMillisUnsafe() + durationMs latch.openUnsafe() return pull }).pipe( Effect.map((arr) => { latch.closeUnsafe() deadline = undefined return arr }), Effect.succeed )), fromEffectDrain(Effect.gen(function*() { while (true) { yield* latch.await if (deadline === undefined) continue yield* Effect.sleep(deadline - clock.currentTimeMillisUnsafe()) if (deadline === undefined) continue const remaining = deadline - clock.currentTimeMillisUnsafe() if (remaining > 0) continue return yield* Effect.die(timeoutSymbol) } })), { haltStrategy: "left" } ) }), (cause): Stream => { const isTimeout = cause.reasons.find((r) => r._tag === "Die" && r.defect === timeoutSymbol) if (isTimeout) return options.orElse() return failCause(cause as Cause.Cause) } ) } ) /** * Repeats each element of the stream according to the provided schedule, * including the original emission. * * @since 2.0.0 * @category Sequencing * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make("A", "B", "C").pipe( * Stream.repeatElements(Schedule.recurs(1)), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ "A", "A", "B", "B", "C", "C" ] * ``` */ export const repeatElements: { /** * Repeats each element of the stream according to the provided schedule, * including the original emission. * * @since 2.0.0 * @category Sequencing * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make("A", "B", "C").pipe( * Stream.repeatElements(Schedule.recurs(1)), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ "A", "A", "B", "B", "C", "C" ] * ``` */ (schedule: Schedule.Schedule): (self: Stream) => Stream /** * Repeats each element of the stream according to the provided schedule, * including the original emission. * * @since 2.0.0 * @category Sequencing * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make("A", "B", "C").pipe( * Stream.repeatElements(Schedule.recurs(1)), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ "A", "A", "B", "B", "C", "C" ] * ``` */ (self: Stream, schedule: Schedule.Schedule): Stream } = dual( 2, ( self: Stream, schedule: Schedule.Schedule ): Stream => fromChannel(Channel.fromTransform((upstream, scope) => Effect.map( Channel.toTransform(Channel.flattenArray(self.channel))(upstream, scope), (pullElement) => { let pullRepeat: Pull.Pull, E | E2, void, R | R2> | undefined = undefined const pull: Pull.Pull< Arr.NonEmptyReadonlyArray, E, void, R | R2 > = Effect.gen(function*() { const element = yield* pullElement const chunk = Arr.of(element) const step = yield* Schedule.toStepWithSleep(schedule) pullRepeat = step(element).pipe( Effect.as(chunk), Pull.catchDone((_) => { pullRepeat = undefined return pull }) ) return chunk }) return Effect.suspend(() => pullRepeat ?? pull) } ) )) ) /** * Repeats this stream forever. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make("A", "B").pipe( * Stream.forever, * Stream.take(5) * ) * * const program = Effect.gen(function*() { * const output = yield* Stream.runCollect(stream) * yield* Console.log(output) * }) * * Effect.runPromise(program) * // Output: [ "A", "B", "A", "B", "A" ] * ``` * * @since 2.0.0 * @category Sequencing */ export const forever = (self: Stream): Stream => fromChannel(Channel.forever(self.channel)) /** * Submerges the iterables emitted by this stream into the stream's structure. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.flattenIterables` * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make([1, 2], [3, 4]).pipe(Stream.flattenIterable) * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3, 4 ] * ``` * * @since 4.0.0 * @category Mapping */ export const flattenIterable = (self: Stream, E, R>): Stream => flatMap(self, fromIterable) /** * Unwraps `Take` values, emitting elements from non-empty arrays and ending or * failing when the `Exit` signals completion. * * @example * ```ts * import { Array, Console, Effect, Exit, Stream } from "effect" * * const program = Effect.gen(function*() { * const takes = Stream.make( * Array.make(1, 2), * Array.make(3), * Exit.succeed(undefined) * ) * * const values = yield* Stream.flattenTake(takes).pipe(Stream.runCollect) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 4.0.0 * @category Sequencing */ export const flattenTake = (self: Stream, E2, R>): Stream => self.channel.pipe( Channel.flattenArray, Channel.flattenTake, fromChannel ) /** * Concatenates two streams, emitting all elements from the first stream * followed by all elements from the second stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.concat(Stream.make(1, 2, 3), Stream.make(4, 5, 6)) * * Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * // Output: [ 1, 2, 3, 4, 5, 6 ] * ``` * * @since 2.0.0 * @category Sequencing */ export const concat: { /** * Concatenates two streams, emitting all elements from the first stream * followed by all elements from the second stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.concat(Stream.make(1, 2, 3), Stream.make(4, 5, 6)) * * Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * // Output: [ 1, 2, 3, 4, 5, 6 ] * ``` * * @since 2.0.0 * @category Sequencing */ (that: Stream): (self: Stream) => Stream /** * Concatenates two streams, emitting all elements from the first stream * followed by all elements from the second stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.concat(Stream.make(1, 2, 3), Stream.make(4, 5, 6)) * * Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * // Output: [ 1, 2, 3, 4, 5, 6 ] * ``` * * @since 2.0.0 * @category Sequencing */ (self: Stream, that: Stream): Stream } = dual( 2, (self: Stream, that: Stream): Stream => flatten(fromArray>([self, that])) ) /** * Prepends the values from the provided iterable before the stream's elements. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(3, 4).pipe( * Stream.prepend([1, 2]), * Stream.runCollect * ) * * yield* Console.log(values) * // Output: [ 1, 2, 3, 4 ] * }) * * Effect.runPromise(program) * ``` * * @since 2.0.0 * @category Sequencing */ export const prepend: { /** * Prepends the values from the provided iterable before the stream's elements. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(3, 4).pipe( * Stream.prepend([1, 2]), * Stream.runCollect * ) * * yield* Console.log(values) * // Output: [ 1, 2, 3, 4 ] * }) * * Effect.runPromise(program) * ``` * * @since 2.0.0 * @category Sequencing */ (values: Iterable): (self: Stream) => Stream /** * Prepends the values from the provided iterable before the stream's elements. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(3, 4).pipe( * Stream.prepend([1, 2]), * Stream.runCollect * ) * * yield* Console.log(values) * // Output: [ 1, 2, 3, 4 ] * }) * * Effect.runPromise(program) * ``` * * @since 2.0.0 * @category Sequencing */ (self: Stream, values: Iterable): Stream } = dual(2, ( self: Stream, values: Iterable ): Stream => concat(fromIterable(values), self)) /** * Merges two streams, emitting elements from both as they arrive. * * By default, the merged stream ends when both streams end. Use * `haltStrategy` to change the termination behavior. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const fast = Stream.make(1, 2, 3) * const slow = Stream.fromEffect(Effect.delay(Effect.succeed(4), "50 millis")) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(Stream.merge(fast, slow)) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3, 4 ] * ``` * * @since 2.0.0 * @category Merging */ export const merge: { /** * Merges two streams, emitting elements from both as they arrive. * * By default, the merged stream ends when both streams end. Use * `haltStrategy` to change the termination behavior. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const fast = Stream.make(1, 2, 3) * const slow = Stream.fromEffect(Effect.delay(Effect.succeed(4), "50 millis")) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(Stream.merge(fast, slow)) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3, 4 ] * ``` * * @since 2.0.0 * @category Merging */ ( that: Stream, options?: { readonly haltStrategy?: HaltStrategy | undefined } | undefined ): (self: Stream) => Stream /** * Merges two streams, emitting elements from both as they arrive. * * By default, the merged stream ends when both streams end. Use * `haltStrategy` to change the termination behavior. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const fast = Stream.make(1, 2, 3) * const slow = Stream.fromEffect(Effect.delay(Effect.succeed(4), "50 millis")) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(Stream.merge(fast, slow)) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3, 4 ] * ``` * * @since 2.0.0 * @category Merging */ ( self: Stream, that: Stream, options?: { readonly haltStrategy?: HaltStrategy | undefined } | undefined ): Stream } = dual( (args) => isStream(args[0]) && isStream(args[1]), ( self: Stream, that: Stream, options?: { readonly haltStrategy?: HaltStrategy | undefined } | undefined ): Stream => fromChannel(Channel.merge(toChannel(self), toChannel(that), options)) ) /** * Merges this stream with a background effect, keeping the stream's elements. * * The effect runs concurrently, fails the stream if it fails, and is interrupted * when the stream completes. * * @since 4.0.0 * @category Merging * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.mergeEffect(Console.log("side task")), * Stream.runCollect * ) * * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: side task * // Output: [ 1, 2, 3 ] * ``` */ export const mergeEffect: { /** * Merges this stream with a background effect, keeping the stream's elements. * * The effect runs concurrently, fails the stream if it fails, and is interrupted * when the stream completes. * * @since 4.0.0 * @category Merging * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.mergeEffect(Console.log("side task")), * Stream.runCollect * ) * * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: side task * // Output: [ 1, 2, 3 ] * ``` */ (effect: Effect.Effect): (self: Stream) => Stream /** * Merges this stream with a background effect, keeping the stream's elements. * * The effect runs concurrently, fails the stream if it fails, and is interrupted * when the stream completes. * * @since 4.0.0 * @category Merging * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.mergeEffect(Console.log("side task")), * Stream.runCollect * ) * * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: side task * // Output: [ 1, 2, 3 ] * ``` */ (self: Stream, effect: Effect.Effect): Stream } = dual( 2, (self: Stream, effect: Effect.Effect): Stream => self.channel.pipe( Channel.mergeEffect(effect), fromChannel ) ) /** * Merges this stream and the specified stream together, tagging values from the * left stream as `Result.succeed` and values from the right stream as `Result.fail`. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.mergeEither` * * @example * ```ts * import { Console, Effect, Result, Stream } from "effect" * * const left = Stream.fromEffect(Effect.succeed("left")) * const right = Stream.fromEffect(Effect.delay(Effect.succeed("right"), "10 millis")) * * const merged = left.pipe( * Stream.mergeResult(right), * Stream.map( * Result.match({ * onFailure: (value) => `right:${value}`, * onSuccess: (value) => `left:${value}` * }) * ) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(merged) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ "left:left", "right:right" ] * ``` * * @since 2.0.0 * @category Merging */ export const mergeResult: { /** * Merges this stream and the specified stream together, tagging values from the * left stream as `Result.succeed` and values from the right stream as `Result.fail`. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.mergeEither` * * @example * ```ts * import { Console, Effect, Result, Stream } from "effect" * * const left = Stream.fromEffect(Effect.succeed("left")) * const right = Stream.fromEffect(Effect.delay(Effect.succeed("right"), "10 millis")) * * const merged = left.pipe( * Stream.mergeResult(right), * Stream.map( * Result.match({ * onFailure: (value) => `right:${value}`, * onSuccess: (value) => `left:${value}` * }) * ) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(merged) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ "left:left", "right:right" ] * ``` * * @since 2.0.0 * @category Merging */ (that: Stream): (self: Stream) => Stream, E2 | E, R2 | R> /** * Merges this stream and the specified stream together, tagging values from the * left stream as `Result.succeed` and values from the right stream as `Result.fail`. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.mergeEither` * * @example * ```ts * import { Console, Effect, Result, Stream } from "effect" * * const left = Stream.fromEffect(Effect.succeed("left")) * const right = Stream.fromEffect(Effect.delay(Effect.succeed("right"), "10 millis")) * * const merged = left.pipe( * Stream.mergeResult(right), * Stream.map( * Result.match({ * onFailure: (value) => `right:${value}`, * onSuccess: (value) => `left:${value}` * }) * ) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(merged) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ "left:left", "right:right" ] * ``` * * @since 2.0.0 * @category Merging */ (self: Stream, that: Stream): Stream, E | E2, R | R2> } = dual( 2, ( self: Stream, that: Stream ): Stream, E | E2, R | R2> => merge( map(self, Result.succeed), map(that, Result.fail) ) ) /** * Merges two streams while emitting only the values from the left stream. * * The right stream still runs for its effects, and any failures from the right * stream are propagated. The merged stream completes when the left stream * completes, interrupting the right stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const left = Stream.make(1, 2) * const right = Stream.make("a", "b") * const values = yield* left.pipe(Stream.mergeLeft(right), Stream.runCollect) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2 ] * ``` * * @since 2.0.0 * @category Merging */ export const mergeLeft: { /** * Merges two streams while emitting only the values from the left stream. * * The right stream still runs for its effects, and any failures from the right * stream are propagated. The merged stream completes when the left stream * completes, interrupting the right stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const left = Stream.make(1, 2) * const right = Stream.make("a", "b") * const values = yield* left.pipe(Stream.mergeLeft(right), Stream.runCollect) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2 ] * ``` * * @since 2.0.0 * @category Merging */ (right: Stream): (left: Stream) => Stream /** * Merges two streams while emitting only the values from the left stream. * * The right stream still runs for its effects, and any failures from the right * stream are propagated. The merged stream completes when the left stream * completes, interrupting the right stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const left = Stream.make(1, 2) * const right = Stream.make("a", "b") * const values = yield* left.pipe(Stream.mergeLeft(right), Stream.runCollect) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2 ] * ``` * * @since 2.0.0 * @category Merging */ (left: Stream, right: Stream): Stream } = dual( 2, (left: Stream, right: Stream): Stream => mergeEffect(left, runDrain(right)) ) /** * Merges this stream and the specified stream together, emitting only the * values from the right stream while the left stream runs for its effects. * * The merged stream ends when the right stream completes, interrupting the * left stream. Failures from the left stream still fail the merged stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const left = Stream.make("left-1", "left-2").pipe( * Stream.tap(() => Effect.sync(() => undefined)) * ) * const right = Stream.make(1, 2) * * const merged = Stream.mergeRight(left, right) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(merged) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2 ] * ``` * * @since 2.0.0 * @category Merging */ export const mergeRight: { /** * Merges this stream and the specified stream together, emitting only the * values from the right stream while the left stream runs for its effects. * * The merged stream ends when the right stream completes, interrupting the * left stream. Failures from the left stream still fail the merged stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const left = Stream.make("left-1", "left-2").pipe( * Stream.tap(() => Effect.sync(() => undefined)) * ) * const right = Stream.make(1, 2) * * const merged = Stream.mergeRight(left, right) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(merged) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2 ] * ``` * * @since 2.0.0 * @category Merging */ (right: Stream): (left: Stream) => Stream /** * Merges this stream and the specified stream together, emitting only the * values from the right stream while the left stream runs for its effects. * * The merged stream ends when the right stream completes, interrupting the * left stream. Failures from the left stream still fail the merged stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const left = Stream.make("left-1", "left-2").pipe( * Stream.tap(() => Effect.sync(() => undefined)) * ) * const right = Stream.make(1, 2) * * const merged = Stream.mergeRight(left, right) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(merged) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2 ] * ``` * * @since 2.0.0 * @category Merging */ (left: Stream, right: Stream): Stream } = dual( 2, (left: Stream, right: Stream): Stream => mergeEffect(right, runDrain(left)) ) /** * Merges a collection of streams, running up to the specified number concurrently. * * @since 2.0.0 * @category Merging * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const streams = [ * Stream.fromEffect(Effect.delay(Effect.succeed("A"), "20 millis")), * Stream.fromEffect(Effect.delay(Effect.succeed("B"), "10 millis")) * ] * * const program = Effect.gen(function*() { * const values = yield* Stream.mergeAll(streams, { concurrency: 2 }).pipe( * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ "B", "A" ] * ``` */ export const mergeAll: { /** * Merges a collection of streams, running up to the specified number concurrently. * * @since 2.0.0 * @category Merging * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const streams = [ * Stream.fromEffect(Effect.delay(Effect.succeed("A"), "20 millis")), * Stream.fromEffect(Effect.delay(Effect.succeed("B"), "10 millis")) * ] * * const program = Effect.gen(function*() { * const values = yield* Stream.mergeAll(streams, { concurrency: 2 }).pipe( * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ "B", "A" ] * ``` */ ( options: { readonly concurrency: number | "unbounded" readonly bufferSize?: number | undefined } ): (streams: Iterable>) => Stream /** * Merges a collection of streams, running up to the specified number concurrently. * * @since 2.0.0 * @category Merging * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const streams = [ * Stream.fromEffect(Effect.delay(Effect.succeed("A"), "20 millis")), * Stream.fromEffect(Effect.delay(Effect.succeed("B"), "10 millis")) * ] * * const program = Effect.gen(function*() { * const values = yield* Stream.mergeAll(streams, { concurrency: 2 }).pipe( * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ "B", "A" ] * ``` */ ( streams: Iterable>, options: { readonly concurrency: number | "unbounded" readonly bufferSize?: number | undefined } ): Stream } = dual(2, ( streams: Iterable>, options: { readonly concurrency: number | "unbounded" readonly bufferSize?: number | undefined } ): Stream => flatten(fromIterable(streams), options)) /** * Creates the cartesian product of two streams, running the `right` stream for * each element in the `left` stream. * * See also `Stream.zip` for the more common point-wise variant. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const left = Stream.make(1, 2) * const right = Stream.make("a", "b") * const values = yield* Stream.runCollect(Stream.cross(left, right)) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ [ 1, "a" ], [ 1, "b" ], [ 2, "a" ], [ 2, "b" ] ] * ``` * * @since 2.0.0 * @category Zipping */ export const cross: { /** * Creates the cartesian product of two streams, running the `right` stream for * each element in the `left` stream. * * See also `Stream.zip` for the more common point-wise variant. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const left = Stream.make(1, 2) * const right = Stream.make("a", "b") * const values = yield* Stream.runCollect(Stream.cross(left, right)) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ [ 1, "a" ], [ 1, "b" ], [ 2, "a" ], [ 2, "b" ] ] * ``` * * @since 2.0.0 * @category Zipping */ (right: Stream): (left: Stream) => Stream<[AL, AR], EL | ER, RL | RR> /** * Creates the cartesian product of two streams, running the `right` stream for * each element in the `left` stream. * * See also `Stream.zip` for the more common point-wise variant. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const left = Stream.make(1, 2) * const right = Stream.make("a", "b") * const values = yield* Stream.runCollect(Stream.cross(left, right)) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ [ 1, "a" ], [ 1, "b" ], [ 2, "a" ], [ 2, "b" ] ] * ``` * * @since 2.0.0 * @category Zipping */ (left: Stream, right: Stream): Stream<[AL, AR], EL | ER, RL | RR> } = dual(2, ( left: Stream, right: Stream ): Stream<[AL, AR], EL | ER, RL | RR> => crossWith(left, right, (l, r) => [l, r])) /** * Creates a cartesian product of elements from two streams using a function. * * The `right` stream is rerun for every element in the `left` stream. * * See also `Stream.zipWith` for the more common point-wise variant. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const left = Stream.make(1, 2) * const right = Stream.make("a", "b") * const combined = Stream.crossWith(left, right, (n, s) => `${n}-${s}`) * const result = yield* Stream.runCollect(combined) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ "1-a", "1-b", "2-a", "2-b" ] * ``` * * @since 2.0.0 * @category Zipping */ export const crossWith: { /** * Creates a cartesian product of elements from two streams using a function. * * The `right` stream is rerun for every element in the `left` stream. * * See also `Stream.zipWith` for the more common point-wise variant. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const left = Stream.make(1, 2) * const right = Stream.make("a", "b") * const combined = Stream.crossWith(left, right, (n, s) => `${n}-${s}`) * const result = yield* Stream.runCollect(combined) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ "1-a", "1-b", "2-a", "2-b" ] * ``` * * @since 2.0.0 * @category Zipping */ (right: Stream, f: (left: AL, right: AR) => A): (left: Stream) => Stream /** * Creates a cartesian product of elements from two streams using a function. * * The `right` stream is rerun for every element in the `left` stream. * * See also `Stream.zipWith` for the more common point-wise variant. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const left = Stream.make(1, 2) * const right = Stream.make("a", "b") * const combined = Stream.crossWith(left, right, (n, s) => `${n}-${s}`) * const result = yield* Stream.runCollect(combined) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ "1-a", "1-b", "2-a", "2-b" ] * ``` * * @since 2.0.0 * @category Zipping */ ( left: Stream, right: Stream, f: (left: AL, right: AR) => A ): Stream } = dual(3, ( left: Stream, right: Stream, f: (left: AL, right: AR) => A ): Stream => flatMap(left, (l) => map(right, (r) => f(l, r)))) /** * Zips two streams point-wise with a combining function, ending when either stream ends. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream1 = Stream.make(1, 2, 3, 4, 5, 6) * const stream2 = Stream.make("a", "b", "c") * * const zipped = Stream.zipWith(stream1, stream2, (n, s) => `${n}-${s}`) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(zipped) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ "1-a", "2-b", "3-c" ] * ``` * * @since 2.0.0 * @category Zipping */ export const zipWith: { /** * Zips two streams point-wise with a combining function, ending when either stream ends. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream1 = Stream.make(1, 2, 3, 4, 5, 6) * const stream2 = Stream.make("a", "b", "c") * * const zipped = Stream.zipWith(stream1, stream2, (n, s) => `${n}-${s}`) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(zipped) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ "1-a", "2-b", "3-c" ] * ``` * * @since 2.0.0 * @category Zipping */ (right: Stream, f: (left: AL, right: AR) => A): (left: Stream) => Stream /** * Zips two streams point-wise with a combining function, ending when either stream ends. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream1 = Stream.make(1, 2, 3, 4, 5, 6) * const stream2 = Stream.make("a", "b", "c") * * const zipped = Stream.zipWith(stream1, stream2, (n, s) => `${n}-${s}`) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(zipped) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ "1-a", "2-b", "3-c" ] * ``` * * @since 2.0.0 * @category Zipping */ ( left: Stream, right: Stream, f: (left: AL, right: AR) => A ): Stream } = dual(3, ( left: Stream, right: Stream, f: (left: AL, right: AR) => A ): Stream => zipWithArray(left, right, zipArrays(f))) const zipArrays = ( f: (left: AL, right: AR) => A ) => ( leftArr: Arr.NonEmptyReadonlyArray, rightArr: Arr.NonEmptyReadonlyArray ) => { const minLength = Math.min(leftArr.length, rightArr.length) const result: Arr.NonEmptyArray = [] as any for (let i = 0; i < minLength; i++) { result.push(f(leftArr[i], rightArr[i])) } return [result, leftArr.slice(minLength), rightArr.slice(minLength)] as const } /** * Zips two streams by applying a function to non-empty arrays of elements. * * The function returns output plus leftover arrays that carry into the next pull. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.zipWithChunks` * * @example * ```ts * import { Array, Console, Effect, Stream } from "effect" * * const left = Stream.fromArrays([1, 2, 3], [4, 5]) * const right = Stream.fromArrays(["a", "b"], ["c", "d", "e"]) * * const zipped = Stream.zipWithArray(left, right, (leftChunk, rightChunk) => { * const minLength = Math.min(leftChunk.length, rightChunk.length) * const output = Array.makeBy(minLength, (i) => [leftChunk[i], rightChunk[i]] as const) * * return [output, leftChunk.slice(minLength), rightChunk.slice(minLength)] * }) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(zipped) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [[1, "a"], [2, "b"], [3, "c"], [4, "d"], [5, "e"]] * ``` * * @since 2.0.0 * @category Zipping */ export const zipWithArray: { /** * Zips two streams by applying a function to non-empty arrays of elements. * * The function returns output plus leftover arrays that carry into the next pull. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.zipWithChunks` * * @example * ```ts * import { Array, Console, Effect, Stream } from "effect" * * const left = Stream.fromArrays([1, 2, 3], [4, 5]) * const right = Stream.fromArrays(["a", "b"], ["c", "d", "e"]) * * const zipped = Stream.zipWithArray(left, right, (leftChunk, rightChunk) => { * const minLength = Math.min(leftChunk.length, rightChunk.length) * const output = Array.makeBy(minLength, (i) => [leftChunk[i], rightChunk[i]] as const) * * return [output, leftChunk.slice(minLength), rightChunk.slice(minLength)] * }) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(zipped) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [[1, "a"], [2, "b"], [3, "c"], [4, "d"], [5, "e"]] * ``` * * @since 2.0.0 * @category Zipping */ ( right: Stream, f: ( left: Arr.NonEmptyReadonlyArray, right: Arr.NonEmptyReadonlyArray ) => readonly [ output: Arr.NonEmptyReadonlyArray, leftoverLeft: ReadonlyArray, leftoverRight: ReadonlyArray ] ): (left: Stream) => Stream /** * Zips two streams by applying a function to non-empty arrays of elements. * * The function returns output plus leftover arrays that carry into the next pull. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.zipWithChunks` * * @example * ```ts * import { Array, Console, Effect, Stream } from "effect" * * const left = Stream.fromArrays([1, 2, 3], [4, 5]) * const right = Stream.fromArrays(["a", "b"], ["c", "d", "e"]) * * const zipped = Stream.zipWithArray(left, right, (leftChunk, rightChunk) => { * const minLength = Math.min(leftChunk.length, rightChunk.length) * const output = Array.makeBy(minLength, (i) => [leftChunk[i], rightChunk[i]] as const) * * return [output, leftChunk.slice(minLength), rightChunk.slice(minLength)] * }) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(zipped) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [[1, "a"], [2, "b"], [3, "c"], [4, "d"], [5, "e"]] * ``` * * @since 2.0.0 * @category Zipping */ ( left: Stream, right: Stream, f: ( left: Arr.NonEmptyReadonlyArray, right: Arr.NonEmptyReadonlyArray ) => readonly [ output: Arr.NonEmptyReadonlyArray, leftoverLeft: ReadonlyArray, leftoverRight: ReadonlyArray ] ): Stream } = dual(3, ( left: Stream, right: Stream, f: ( left: Arr.NonEmptyReadonlyArray, right: Arr.NonEmptyReadonlyArray ) => readonly [ output: Arr.NonEmptyReadonlyArray, leftoverLeft: ReadonlyArray, leftoverRight: ReadonlyArray ] ): Stream => fromChannel(Channel.fromTransformBracket(Effect.fnUntraced(function*(_, scope) { const pullLeft = yield* Channel.toPullScoped(left.channel, scope) const pullRight = yield* Channel.toPullScoped(right.channel, scope) const pullBoth = Effect.gen(function*() { const fiberLeft = yield* Effect.forkIn(pullLeft, scope) const fiberRight = yield* Effect.forkIn(pullRight, scope) return (yield* Fiber.joinAll([fiberLeft, fiberRight])) as [ Arr.NonEmptyReadonlyArray, Arr.NonEmptyReadonlyArray ] }) type State = | { _tag: "PullBoth" } | { _tag: "PullLeft"; rightArray: Arr.NonEmptyReadonlyArray } | { _tag: "PullRight"; leftArray: Arr.NonEmptyReadonlyArray } let state: State = { _tag: "PullBoth" } const pull: Effect.Effect< Arr.NonEmptyReadonlyArray, EL | ER | Cause.Done, RL | RR > = Effect.gen(function*() { const [left, right] = state._tag === "PullBoth" ? yield* pullBoth : state._tag === "PullLeft" ? [yield* pullLeft, state.rightArray] : [state.leftArray, yield* pullRight] const result = f(left, right) if (Arr.isReadonlyArrayNonEmpty(result[1])) { state = { _tag: "PullRight", leftArray: result[1] } } else if (Arr.isReadonlyArrayNonEmpty(result[2])) { state = { _tag: "PullLeft", rightArray: result[2] } } else { state = { _tag: "PullBoth" } } return result[0] }) return pull })))) /** * Zips this stream with another point-wise and emits tuples of elements from * both streams. The new stream ends when either stream ends. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream1 = Stream.make(1, 2, 3) * const stream2 = Stream.make("a", "b", "c") * * const zipped = Stream.zip(stream1, stream2) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(zipped) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [[1, "a"], [2, "b"], [3, "c"]] * ``` * * @since 2.0.0 * @category Zipping */ export const zip: { /** * Zips this stream with another point-wise and emits tuples of elements from * both streams. The new stream ends when either stream ends. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream1 = Stream.make(1, 2, 3) * const stream2 = Stream.make("a", "b", "c") * * const zipped = Stream.zip(stream1, stream2) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(zipped) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [[1, "a"], [2, "b"], [3, "c"]] * ``` * * @since 2.0.0 * @category Zipping */ (that: Stream): (self: Stream) => Stream<[A, A2], E2 | E, R2 | R> /** * Zips this stream with another point-wise and emits tuples of elements from * both streams. The new stream ends when either stream ends. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream1 = Stream.make(1, 2, 3) * const stream2 = Stream.make("a", "b", "c") * * const zipped = Stream.zip(stream1, stream2) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(zipped) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [[1, "a"], [2, "b"], [3, "c"]] * ``` * * @since 2.0.0 * @category Zipping */ (self: Stream, that: Stream): Stream<[A, A2], E | E2, R | R2> } = dual( 2, ( self: Stream, that: Stream ): Stream<[A, A2], E | E2, R | R2> => zipWith(self, that, (a, a2) => [a, a2]) ) /** * Zips this stream with another point-wise and keeps only the values from * the left stream. * * The resulting stream ends when either side ends. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream1 = Stream.make(1, 2, 3, 4) * const stream2 = Stream.make("a", "b") * * const program = Effect.gen(function*() { * const result = yield* Stream.zipLeft(stream1, stream2).pipe(Stream.runCollect) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [1, 2] * ``` * * @since 2.0.0 * @category Zipping */ export const zipLeft: { /** * Zips this stream with another point-wise and keeps only the values from * the left stream. * * The resulting stream ends when either side ends. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream1 = Stream.make(1, 2, 3, 4) * const stream2 = Stream.make("a", "b") * * const program = Effect.gen(function*() { * const result = yield* Stream.zipLeft(stream1, stream2).pipe(Stream.runCollect) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [1, 2] * ``` * * @since 2.0.0 * @category Zipping */ (right: Stream): (left: Stream) => Stream /** * Zips this stream with another point-wise and keeps only the values from * the left stream. * * The resulting stream ends when either side ends. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream1 = Stream.make(1, 2, 3, 4) * const stream2 = Stream.make("a", "b") * * const program = Effect.gen(function*() { * const result = yield* Stream.zipLeft(stream1, stream2).pipe(Stream.runCollect) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [1, 2] * ``` * * @since 2.0.0 * @category Zipping */ (left: Stream, right: Stream): Stream } = dual( 2, ( left: Stream, right: Stream ): Stream => zipWithArray(left, right, (leftArr, rightArr) => { const minLength = Math.min(leftArr.length, rightArr.length) const output = leftArr.slice(0, minLength) as Arr.NonEmptyArray const leftoverLeft = leftArr.slice(minLength) const leftoverRight = rightArr.slice(minLength) return [output, leftoverLeft, leftoverRight] as const }) ) /** * Zips this stream with another point-wise, keeping only right values and ending when either stream ends. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream1 = Stream.make(1, 2) * const stream2 = Stream.make("a", "b", "c", "d") * * const program = Effect.gen(function*() { * const result = yield* Stream.zipRight(stream1, stream2).pipe(Stream.runCollect) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: ["a", "b"] * ``` * * @since 2.0.0 * @category Zipping */ export const zipRight: { /** * Zips this stream with another point-wise, keeping only right values and ending when either stream ends. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream1 = Stream.make(1, 2) * const stream2 = Stream.make("a", "b", "c", "d") * * const program = Effect.gen(function*() { * const result = yield* Stream.zipRight(stream1, stream2).pipe(Stream.runCollect) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: ["a", "b"] * ``` * * @since 2.0.0 * @category Zipping */ (right: Stream): (left: Stream) => Stream /** * Zips this stream with another point-wise, keeping only right values and ending when either stream ends. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream1 = Stream.make(1, 2) * const stream2 = Stream.make("a", "b", "c", "d") * * const program = Effect.gen(function*() { * const result = yield* Stream.zipRight(stream1, stream2).pipe(Stream.runCollect) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: ["a", "b"] * ``` * * @since 2.0.0 * @category Zipping */ (left: Stream, right: Stream): Stream } = dual( 2, ( left: Stream, right: Stream ): Stream => zipWithArray(left, right, (leftArr, rightArr) => { const minLength = Math.min(leftArr.length, rightArr.length) const output = rightArr.slice(0, minLength) as Arr.NonEmptyArray const leftoverLeft = leftArr.slice(minLength) const leftoverRight = rightArr.slice(minLength) return [output, leftoverLeft, leftoverRight] as const }) ) /** * Zips this stream with another point-wise and emits tuples of elements from * both streams, flattening the left tuple. * * The new stream will end when one of the sides ends. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream1 = Stream.make( * [1, "a"] as const, * [2, "b"] as const, * [3, "c"] as const * ) * const stream2 = Stream.make("x", "y", "z") * const result = yield* Stream.zipFlatten(stream1, stream2).pipe(Stream.runCollect) * * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [[1, "a", "x"], [2, "b", "y"], [3, "c", "z"]] * ``` * * @since 2.0.0 * @category Zipping */ export const zipFlatten: { /** * Zips this stream with another point-wise and emits tuples of elements from * both streams, flattening the left tuple. * * The new stream will end when one of the sides ends. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream1 = Stream.make( * [1, "a"] as const, * [2, "b"] as const, * [3, "c"] as const * ) * const stream2 = Stream.make("x", "y", "z") * const result = yield* Stream.zipFlatten(stream1, stream2).pipe(Stream.runCollect) * * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [[1, "a", "x"], [2, "b", "y"], [3, "c", "z"]] * ``` * * @since 2.0.0 * @category Zipping */ (that: Stream): , E, R>(self: Stream) => Stream<[...A, A2], E2 | E, R2 | R> /** * Zips this stream with another point-wise and emits tuples of elements from * both streams, flattening the left tuple. * * The new stream will end when one of the sides ends. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream1 = Stream.make( * [1, "a"] as const, * [2, "b"] as const, * [3, "c"] as const * ) * const stream2 = Stream.make("x", "y", "z") * const result = yield* Stream.zipFlatten(stream1, stream2).pipe(Stream.runCollect) * * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [[1, "a", "x"], [2, "b", "y"], [3, "c", "z"]] * ``` * * @since 2.0.0 * @category Zipping */ , E, R, A2, E2, R2>(self: Stream, that: Stream): Stream<[...A, A2], E | E2, R | R2> } = dual( 2, , E, R, A2, E2, R2>( self: Stream, that: Stream ): Stream<[...A, A2], E | E2, R | R2> => zipWith(self, that, (a, a2) => [...a, a2]) ) /** * Zips this stream together with the index of elements. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const indexed = yield* Stream.make("a", "b", "c", "d").pipe( * Stream.zipWithIndex, * Stream.runCollect * ) * yield* Console.log(indexed) * }) * * Effect.runPromise(program) * // Output: [["a", 0], ["b", 1], ["c", 2], ["d", 3]] * ``` * * @since 2.0.0 * @category Zipping */ export const zipWithIndex = (self: Stream): Stream<[A, number], E, R> => map(self, (a, i) => [a, i]) /** * Zips each element with the next element, pairing the final element with * `Option.none()`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.zipWithNext(Stream.make(1, 2, 3, 4)) * * Effect.runPromise(Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * })) * // Output: [ * // [ 1, { _id: 'Option', _tag: 'Some', value: 2 } ], * // [ 2, { _id: 'Option', _tag: 'Some', value: 3 } ], * // [ 3, { _id: 'Option', _tag: 'Some', value: 4 } ], * // [ 4, { _id: 'Option', _tag: 'None' } ] * // ] * ``` * * @since 2.0.0 * @category Zipping */ export const zipWithNext = (self: Stream): Stream<[A, Option.Option], E, R> => mapAccumArray(self, Option.none, (acc, arr) => { let i = 0 if (acc._tag === "None") { i = 1 acc = Option.some(arr[0]) as Option.Some } const pairs = Arr.empty<[A, Option.Option]>() for (; i < arr.length; i++) { const value = acc.value acc = Option.some(arr[i]) as Option.Some pairs.push([value, acc]) } return [acc, pairs] }, { onHalt(state) { return state._tag === "Some" ? [[state.value, Option.none()]] : [] } }) /** * Zips each element with its previous element, starting with `None`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.zipWithPrevious(Stream.make(1, 2, 3, 4)) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ * // [ { _id: 'Option', _tag: 'None' }, 1 ], * // [ { _id: 'Option', _tag: 'Some', value: 1 }, 2 ], * // [ { _id: 'Option', _tag: 'Some', value: 2 }, 3 ], * // [ { _id: 'Option', _tag: 'Some', value: 3 }, 4 ] * // ] * ``` * * @since 2.0.0 * @category Zipping */ export const zipWithPrevious = (self: Stream): Stream<[Option.Option, A], E, R> => mapAccumArray(self, Option.none, (acc, arr) => { const pairs = Arr.empty<[Option.Option, A]>() for (let i = 0; i < arr.length; i++) { const value = arr[i] pairs.push([acc, value]) acc = Option.some(arr[i]) } return [acc, pairs] }) /** * Zips each element with its previous and next values. * * @example * ```ts * import { Console, Effect, Option, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.zipWithPreviousAndNext, * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ [Option.none(), 1, Option.some(2)], [Option.some(1), 2, Option.some(3)], [Option.some(2), 3, Option.none()] ] * ``` * * @since 2.0.0 * @category Zipping */ export const zipWithPreviousAndNext = ( self: Stream ): Stream<[Option.Option, A, Option.Option], E, R> => mapAccumArray(self, () => ({ prev: Option.none(), current: Option.none() }), (acc, arr) => { let i = 0 let current: A if (acc.current._tag === "None") { i = 1 current = arr[0] acc.current = Option.some(current) } else { current = acc.current.value } const pairs = Arr.empty<[Option.Option, A, Option.Option]>() for (; i < arr.length; i++) { const element = arr[i] acc.current = Option.some(element) as Option.Some pairs.push([acc.prev, current, acc.current]) acc.prev = Option.some(current) current = element } return [acc, pairs] }, { onHalt(acc) { return acc.current._tag === "Some" ? [[acc.prev, acc.current.value, Option.none()]] : [] } }) /** * Zips multiple streams so that when a value is emitted by any stream, it is * combined with the latest values from the other streams to produce a result. * * Note: tracking the latest value is done on a per-array basis. That means * that emitted elements that are not the last value in arrays will never be * used for zipping. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.zipLatestAll( * Stream.make(1, 2, 3).pipe(Stream.rechunk(1)), * Stream.make("a", "b", "c").pipe(Stream.rechunk(1)), * Stream.make(true, false, true).pipe(Stream.rechunk(1)) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ [ 1, "a", true ], [ 2, "a", true ], [ 3, "a", true ], [ 3, "b", true ], [ 3, "c", true ], [ 3, "c", false ], [ 3, "c", true ] ] * ``` * * @since 2.0.0 * @category Zipping */ export const zipLatestAll = >>( ...streams: T ): Stream< [T[number]] extends [never] ? never : { [K in keyof T]: T[K] extends Stream ? A : never }, [T[number]] extends [never] ? never : T[number] extends Stream ? _E : never, [T[number]] extends [never] ? never : T[number] extends Stream ? _R : never > => fromChannel(Channel.suspend(() => { const latest: Array = [] const emitted = new Set() const readyLatch = Latch.makeUnsafe() return Channel.mergeAll( Channel.fromArray( streams.map((s, i) => s.channel.pipe( Channel.flattenArray, Channel.mapEffect((a) => { latest[i] = a if (!emitted.has(i)) { emitted.add(i) if (emitted.size < streams.length) { return readyLatch.await as Effect.Effect } return Effect.as(readyLatch.open, Arr.of(latest.slice())) } return Effect.succeed(Arr.of(latest.slice())) }), Channel.filter(isNotUndefined) ) ) ), { concurrency: "unbounded", bufferSize: 0 } ) })) as any /** * Combines two streams by emitting each new element with the latest value from the other stream. * * Note: tracking the latest value is done on a per-array basis. That means * that emitted elements that are not the last value in arrays will never be * used for zipping. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.zipLatest( * Stream.make(1), * Stream.make("a") * ).pipe(Stream.runCollect) * * yield* Console.log(result) * }) * // Output: [ [1, "a"] ] * ``` * * @since 2.0.0 * @category Zipping */ export const zipLatest: { /** * Combines two streams by emitting each new element with the latest value from the other stream. * * Note: tracking the latest value is done on a per-array basis. That means * that emitted elements that are not the last value in arrays will never be * used for zipping. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.zipLatest( * Stream.make(1), * Stream.make("a") * ).pipe(Stream.runCollect) * * yield* Console.log(result) * }) * // Output: [ [1, "a"] ] * ``` * * @since 2.0.0 * @category Zipping */ (right: Stream): (left: Stream) => Stream<[AL, AR], EL | ER, RL | RR> /** * Combines two streams by emitting each new element with the latest value from the other stream. * * Note: tracking the latest value is done on a per-array basis. That means * that emitted elements that are not the last value in arrays will never be * used for zipping. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.zipLatest( * Stream.make(1), * Stream.make("a") * ).pipe(Stream.runCollect) * * yield* Console.log(result) * }) * // Output: [ [1, "a"] ] * ``` * * @since 2.0.0 * @category Zipping */ (left: Stream, right: Stream): Stream<[AL, AR], EL | ER, RL | RR> } = dual( 2, ( left: Stream, right: Stream ): Stream<[AL, AR], EL | ER, RL | RR> => zipLatestAll(left, right) ) /** * Combines the latest values from both streams whenever either emits, using * the provided function. * * Note: tracking the latest value is done on a per-array basis. That means * that emitted elements that are not the last value in arrays will never be * used for zipping. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3).pipe( * Stream.rechunk(1), * Stream.zipLatestWith( * Stream.make(10, 20).pipe(Stream.rechunk(1)), * (n, m) => n + m * ), * Stream.runCollect * ) * * yield* Console.log(result) * // Output: [ 11, 12, 22, 23 ] * }) * ``` * * @since 2.0.0 * @category Zipping */ export const zipLatestWith: { /** * Combines the latest values from both streams whenever either emits, using * the provided function. * * Note: tracking the latest value is done on a per-array basis. That means * that emitted elements that are not the last value in arrays will never be * used for zipping. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3).pipe( * Stream.rechunk(1), * Stream.zipLatestWith( * Stream.make(10, 20).pipe(Stream.rechunk(1)), * (n, m) => n + m * ), * Stream.runCollect * ) * * yield* Console.log(result) * // Output: [ 11, 12, 22, 23 ] * }) * ``` * * @since 2.0.0 * @category Zipping */ (right: Stream, f: (left: AL, right: AR) => A): (left: Stream) => Stream /** * Combines the latest values from both streams whenever either emits, using * the provided function. * * Note: tracking the latest value is done on a per-array basis. That means * that emitted elements that are not the last value in arrays will never be * used for zipping. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3).pipe( * Stream.rechunk(1), * Stream.zipLatestWith( * Stream.make(10, 20).pipe(Stream.rechunk(1)), * (n, m) => n + m * ), * Stream.runCollect * ) * * yield* Console.log(result) * // Output: [ 11, 12, 22, 23 ] * }) * ``` * * @since 2.0.0 * @category Zipping */ ( left: Stream, right: Stream, f: (left: AL, right: AR) => A ): Stream } = dual( 3, ( left: Stream, right: Stream, f: (left: AL, right: AR) => A ): Stream => map(zipLatestAll(left, right), ([a, a2]) => f(a, a2)) ) /** * Races multiple streams and emits values from the first stream to produce a value, interrupting the rest. * * @since 3.7.0 * @category Racing * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.raceAll( * Stream.fromSchedule(Schedule.spaced("1 second")), * Stream.make(0, 1, 2) * ).pipe(Stream.runCollect) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 0, 1, 2 ] * ``` */ export const raceAll = >>( ...streams: S ): Stream, Error, Services> => fromChannel(Channel.fromTransform((_, scope) => Effect.sync(() => { let winner: | Pull.Pull>, Error, void, Services> | undefined const race = Effect.raceAll(streams.map((stream) => { const childScope = Scope.forkUnsafe(scope) return Channel.toPullScoped(stream.channel, childScope).pipe( Effect.flatMap((pull) => Effect.zip(Effect.succeed(pull), pull)), Effect.onExit((exit) => { if (exit._tag === "Success") { if (winner) { return Scope.close(childScope, exit) } winner = exit.value[0] return Effect.void } return Scope.close(childScope, exit) }), Effect.map(([, chunk]) => chunk) ) })) return Effect.suspend(() => winner ?? race) }) )) /** * Returns a stream that mirrors the first upstream to emit an item. * As soon as one stream emits, the other is interrupted and failures propagate. * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const stream = Stream.race( * Stream.make(0, 1, 2), * Stream.fromSchedule(Schedule.spaced("1 second")) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 0, 1, 2 ] * ``` * * @since 3.7.0 * @category Racing */ export const race: { /** * Returns a stream that mirrors the first upstream to emit an item. * As soon as one stream emits, the other is interrupted and failures propagate. * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const stream = Stream.race( * Stream.make(0, 1, 2), * Stream.fromSchedule(Schedule.spaced("1 second")) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 0, 1, 2 ] * ``` * * @since 3.7.0 * @category Racing */ (right: Stream): (left: Stream) => Stream /** * Returns a stream that mirrors the first upstream to emit an item. * As soon as one stream emits, the other is interrupted and failures propagate. * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const stream = Stream.race( * Stream.make(0, 1, 2), * Stream.fromSchedule(Schedule.spaced("1 second")) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 0, 1, 2 ] * ``` * * @since 3.7.0 * @category Racing */ (left: Stream, right: Stream): Stream } = dual(2, ( left: Stream, right: Stream ): Stream => raceAll(left, right)) /** * Filters a stream to the elements that satisfy a predicate. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 2, 3, 4).pipe( * Stream.filter((n) => n % 2 === 0) * ) * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 2, 4 ] * ``` * * @since 2.0.0 * @category Filtering */ export const filter: { /** * Filters a stream to the elements that satisfy a predicate. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 2, 3, 4).pipe( * Stream.filter((n) => n % 2 === 0) * ) * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 2, 4 ] * ``` * * @since 2.0.0 * @category Filtering */ (refinement: Refinement, B>): (self: Stream) => Stream /** * Filters a stream to the elements that satisfy a predicate. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 2, 3, 4).pipe( * Stream.filter((n) => n % 2 === 0) * ) * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 2, 4 ] * ``` * * @since 2.0.0 * @category Filtering */ (predicate: Predicate>): (self: Stream) => Stream /** * Filters a stream to the elements that satisfy a predicate. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 2, 3, 4).pipe( * Stream.filter((n) => n % 2 === 0) * ) * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 2, 4 ] * ``` * * @since 2.0.0 * @category Filtering */ (self: Stream, refinement: Refinement): Stream /** * Filters a stream to the elements that satisfy a predicate. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 2, 3, 4).pipe( * Stream.filter((n) => n % 2 === 0) * ) * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 2, 4 ] * ``` * * @since 2.0.0 * @category Filtering */ (self: Stream, predicate: Predicate): Stream } = dual( 2, ( self: Stream, predicate: Predicate ): Stream => fromChannel(Channel.filterArray(toChannel(self), predicate)) ) /** * Filters and maps stream elements in one pass using a `Filter`. * * @since 4.0.0 * @category Filtering */ export const filterMap: { /** * Filters and maps stream elements in one pass using a `Filter`. * * @since 4.0.0 * @category Filtering */ (filter: Filter.Filter, B, X>): (self: Stream) => Stream /** * Filters and maps stream elements in one pass using a `Filter`. * * @since 4.0.0 * @category Filtering */ (self: Stream, filter: Filter.Filter): Stream } = dual( 2, ( self: Stream, filter: Filter.Filter ): Stream => fromChannel(Channel.filterMapArray(toChannel(self), filter)) ) /** * Effectfully filters elements in a single pass. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3, 4).pipe(Stream.filterEffect((n) => Effect.succeed(n > 2))) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 3, 4 ] * ``` * * @since 2.0.0 * @category Filtering */ export const filterEffect: { /** * Effectfully filters elements in a single pass. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3, 4).pipe(Stream.filterEffect((n) => Effect.succeed(n > 2))) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 3, 4 ] * ``` * * @since 2.0.0 * @category Filtering */ (predicate: (a: NoInfer, i: number) => Effect.Effect): (self: Stream) => Stream /** * Effectfully filters elements in a single pass. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3, 4).pipe(Stream.filterEffect((n) => Effect.succeed(n > 2))) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 3, 4 ] * ``` * * @since 2.0.0 * @category Filtering */ ( self: Stream, predicate: (a: NoInfer, i: number) => Effect.Effect ): Stream } = dual( 2, ( self: Stream, predicate: (a: NoInfer, i: number) => Effect.Effect ): Stream => fromChannel(Channel.filterArrayEffect(toChannel(self), predicate)) ) /** * Effectfully filters and maps elements in a single pass. * * @since 4.0.0 * @category Filtering */ export const filterMapEffect: { /** * Effectfully filters and maps elements in a single pass. * * @since 4.0.0 * @category Filtering */ (filter: Filter.FilterEffect, B, X, EX, RX>): (self: Stream) => Stream /** * Effectfully filters and maps elements in a single pass. * * @since 4.0.0 * @category Filtering */ (self: Stream, filter: Filter.FilterEffect): Stream } = dual( 2, ( self: Stream, filter: Filter.FilterEffect ): Stream => fromChannel(Channel.filterMapArrayEffect(toChannel(self), filter)) ) /** * Partitions a stream using a `Filter` and exposes passing and failing values as queues. * * Each queue fails with the stream error or `Cause.Done` when the source ends. * * @example * ```ts * import { Console, Effect, Result, Stream } from "effect" * * const program = Effect.gen(function*() { * const [passes, fails] = yield* Stream.make(1, 2, 3, 4).pipe( * Stream.partitionQueue((n) => n % 2 === 0 ? Result.succeed(n) : Result.fail(n)) * ) * * const passValues = yield* Stream.fromQueue(passes).pipe(Stream.runCollect) * const failValues = yield* Stream.fromQueue(fails).pipe(Stream.runCollect) * * yield* Console.log(passValues) * // Output: [ 2, 4 ] * yield* Console.log(failValues) * // Output: [ 1, 3 ] * }) * * Effect.runPromise(Effect.scoped(program)) * ``` * * @since 4.0.0 * @category Filtering */ export const partitionQueue: { /** * Partitions a stream using a `Filter` and exposes passing and failing values as queues. * * Each queue fails with the stream error or `Cause.Done` when the source ends. * * @example * ```ts * import { Console, Effect, Result, Stream } from "effect" * * const program = Effect.gen(function*() { * const [passes, fails] = yield* Stream.make(1, 2, 3, 4).pipe( * Stream.partitionQueue((n) => n % 2 === 0 ? Result.succeed(n) : Result.fail(n)) * ) * * const passValues = yield* Stream.fromQueue(passes).pipe(Stream.runCollect) * const failValues = yield* Stream.fromQueue(fails).pipe(Stream.runCollect) * * yield* Console.log(passValues) * // Output: [ 2, 4 ] * yield* Console.log(failValues) * // Output: [ 1, 3 ] * }) * * Effect.runPromise(Effect.scoped(program)) * ``` * * @since 4.0.0 * @category Filtering */ ( filter: Filter.Filter, Pass, Fail>, options?: { readonly capacity?: number | "unbounded" | undefined } ): (self: Stream) => Effect.Effect< [ passes: Queue.Dequeue, fails: Queue.Dequeue ], never, R | Scope.Scope > /** * Partitions a stream using a `Filter` and exposes passing and failing values as queues. * * Each queue fails with the stream error or `Cause.Done` when the source ends. * * @example * ```ts * import { Console, Effect, Result, Stream } from "effect" * * const program = Effect.gen(function*() { * const [passes, fails] = yield* Stream.make(1, 2, 3, 4).pipe( * Stream.partitionQueue((n) => n % 2 === 0 ? Result.succeed(n) : Result.fail(n)) * ) * * const passValues = yield* Stream.fromQueue(passes).pipe(Stream.runCollect) * const failValues = yield* Stream.fromQueue(fails).pipe(Stream.runCollect) * * yield* Console.log(passValues) * // Output: [ 2, 4 ] * yield* Console.log(failValues) * // Output: [ 1, 3 ] * }) * * Effect.runPromise(Effect.scoped(program)) * ``` * * @since 4.0.0 * @category Filtering */ ( self: Stream, filter: Filter.Filter, Pass, Fail>, options?: { readonly capacity?: number | "unbounded" | undefined } ): Effect.Effect< [ passes: Queue.Dequeue, fails: Queue.Dequeue ], never, R | Scope.Scope > } = dual( (args) => isStream(args[0]), Effect.fnUntraced( function*( self: Stream, filter: Filter.Filter, Pass, Fail>, options?: { readonly capacity?: number | "unbounded" | undefined } ): Effect.fn.Return< [ passes: Queue.Dequeue, fails: Queue.Dequeue ], never, R | Scope.Scope > { const scope = yield* Effect.scope const pull = yield* Channel.toPullScoped(self.channel, scope) const capacity = options?.capacity === "unbounded" ? undefined : options?.capacity ?? DefaultChunkSize const passes = yield* Queue.make n % 2 === 0 ? Result.succeed(n) : Result.fail(n)) * ) * * const passValues = yield* Stream.fromQueue(passes).pipe(Stream.runCollect) * const failValues = yield* Stream.fromQueue(fails).pipe(Stream.runCollect) * * yield* Console.log(passValues) * // Output: [ 2, 4 ] * yield* Console.log(failValues) * // Output: [ 1, 3 ] * }) * * Effect.runPromise(Effect.scoped(program)) * ``` * * @since 4.0.0 * @category Filtering */ Pass, /** * Partitions a stream using a `Filter` and exposes passing and failing values as queues. * * Each queue fails with the stream error or `Cause.Done` when the source ends. * * @example * ```ts * import { Console, Effect, Result, Stream } from "effect" * * const program = Effect.gen(function*() { * const [passes, fails] = yield* Stream.make(1, 2, 3, 4).pipe( * Stream.partitionQueue((n) => n % 2 === 0 ? Result.succeed(n) : Result.fail(n)) * ) * * const passValues = yield* Stream.fromQueue(passes).pipe(Stream.runCollect) * const failValues = yield* Stream.fromQueue(fails).pipe(Stream.runCollect) * * yield* Console.log(passValues) * // Output: [ 2, 4 ] * yield* Console.log(failValues) * // Output: [ 1, 3 ] * }) * * Effect.runPromise(Effect.scoped(program)) * ``` * * @since 4.0.0 * @category Filtering */ E | Cause.Done>({ capacity }) const fails = yield* Queue.make n % 2 === 0 ? Result.succeed(n) : Result.fail(n)) * ) * * const passValues = yield* Stream.fromQueue(passes).pipe(Stream.runCollect) * const failValues = yield* Stream.fromQueue(fails).pipe(Stream.runCollect) * * yield* Console.log(passValues) * // Output: [ 2, 4 ] * yield* Console.log(failValues) * // Output: [ 1, 3 ] * }) * * Effect.runPromise(Effect.scoped(program)) * ``` * * @since 4.0.0 * @category Filtering */ Fail, /** * Partitions a stream using a `Filter` and exposes passing and failing values as queues. * * Each queue fails with the stream error or `Cause.Done` when the source ends. * * @example * ```ts * import { Console, Effect, Result, Stream } from "effect" * * const program = Effect.gen(function*() { * const [passes, fails] = yield* Stream.make(1, 2, 3, 4).pipe( * Stream.partitionQueue((n) => n % 2 === 0 ? Result.succeed(n) : Result.fail(n)) * ) * * const passValues = yield* Stream.fromQueue(passes).pipe(Stream.runCollect) * const failValues = yield* Stream.fromQueue(fails).pipe(Stream.runCollect) * * yield* Console.log(passValues) * // Output: [ 2, 4 ] * yield* Console.log(failValues) * // Output: [ 1, 3 ] * }) * * Effect.runPromise(Effect.scoped(program)) * ``` * * @since 4.0.0 * @category Filtering */ E | Cause.Done>({ capacity }) yield* Effect.gen(function*() { while (true) { const chunk = yield* pull const excluded: Array = [] const satisfying: Array = [] for (let i = 0; i < chunk.length; i++) { const result = filter(chunk[i] as NoInfer) if (Result.isFailure(result)) { excluded.push(result.failure) } else { satisfying.push(result.success) } } let passFiber: Fiber.Fiber | undefined = undefined if (satisfying.length > 0) { const leftover = Queue.offerAllUnsafe(passes, satisfying) if (leftover.length > 0) { passFiber = yield* Effect.forkChild(Queue.offerAll(passes, leftover)) } } if (excluded.length > 0) { const leftover = Queue.offerAllUnsafe(fails, excluded) if (leftover.length > 0) { yield* Queue.offerAll(fails, leftover) } } if (passFiber) yield* Fiber.join(passFiber) } }).pipe( Effect.onError((cause) => { Queue.failCauseUnsafe(passes, cause) Queue.failCauseUnsafe(fails, cause) return Effect.void }), Effect.forkIn(scope) ) return [passes, fails] } ) ) /** * Splits a stream using an effectful `Filter`, producing pass and fail streams. * * @since 4.0.0 * @category Filtering */ export const partitionEffect: { /** * Splits a stream using an effectful `Filter`, producing pass and fail streams. * * @since 4.0.0 * @category Filtering */ ( filter: Filter.FilterEffect, Pass, Fail, EX, RX>, options?: { readonly capacity?: number | "unbounded" | undefined readonly concurrency?: number | "unbounded" | undefined } ): (self: Stream) => Effect.Effect< [ passes: Stream, fails: Stream ], never, R | RX | Scope.Scope > /** * Splits a stream using an effectful `Filter`, producing pass and fail streams. * * @since 4.0.0 * @category Filtering */ ( self: Stream, filter: Filter.FilterEffect, Pass, Fail, EX, RX>, options?: { readonly capacity?: number | "unbounded" | undefined readonly concurrency?: number | "unbounded" | undefined } ): Effect.Effect< [ passes: Stream, fails: Stream ], never, R | RX | Scope.Scope > } = dual( (args) => isStream(args[0]), ( self: Stream, filter: Filter.FilterEffect, Pass, Fail, EX, RX>, options?: { readonly capacity?: number | "unbounded" | undefined readonly concurrency?: number | "unbounded" | undefined } ): Effect.Effect< [ passes: Stream, fails: Stream ], never, R | RX | Scope.Scope > => Effect.map( partitionQueue, /** * Splits a stream using an effectful `Filter`, producing pass and fail streams. * * @since 4.0.0 * @category Filtering */ E | EX, /** * Splits a stream using an effectful `Filter`, producing pass and fail streams. * * @since 4.0.0 * @category Filtering */ R | RX, /** * Splits a stream using an effectful `Filter`, producing pass and fail streams. * * @since 4.0.0 * @category Filtering */ Pass, /** * Splits a stream using an effectful `Filter`, producing pass and fail streams. * * @since 4.0.0 * @category Filtering */ Fail>( mapEffect(self, (a) => filter(a as NoInfer), options), (result) => result, options ), ([passes, fails]) => [fromQueue(passes), fromQueue(fails)] as const ) ) /** * Splits a stream into excluded and satisfying substreams using a `Filter`. * * The faster stream may advance up to `bufferSize` elements ahead of the slower * one. * * @since 4.0.0 * @category Filtering * * @example * ```ts * import { Console, Effect, Result, Stream } from "effect" * * const program = Effect.gen(function*() { * const [excluded, satisfying] = yield* Stream.partition( * Stream.make(1, 2, 3, 4), * (n) => n % 2 === 0 ? Result.succeed(n) : Result.fail(n) * ) * const left = yield* Stream.runCollect(excluded) * const right = yield* Stream.runCollect(satisfying) * yield* Console.log(left) * // Output: [ 1, 3 ] * yield* Console.log(right) * // Output: [ 2, 4 ] * }) * ``` */ export const partition: { /** * Splits a stream into excluded and satisfying substreams using a `Filter`. * * The faster stream may advance up to `bufferSize` elements ahead of the slower * one. * * @since 4.0.0 * @category Filtering * * @example * ```ts * import { Console, Effect, Result, Stream } from "effect" * * const program = Effect.gen(function*() { * const [excluded, satisfying] = yield* Stream.partition( * Stream.make(1, 2, 3, 4), * (n) => n % 2 === 0 ? Result.succeed(n) : Result.fail(n) * ) * const left = yield* Stream.runCollect(excluded) * const right = yield* Stream.runCollect(satisfying) * yield* Console.log(left) * // Output: [ 1, 3 ] * yield* Console.log(right) * // Output: [ 2, 4 ] * }) * ``` */ ( filter: Filter.Filter, Pass, Fail>, options?: { readonly bufferSize?: number | undefined } ): ( self: Stream ) => Effect.Effect< [excluded: Stream, satisfying: Stream], never, R | Scope.Scope > /** * Splits a stream into excluded and satisfying substreams using a `Filter`. * * The faster stream may advance up to `bufferSize` elements ahead of the slower * one. * * @since 4.0.0 * @category Filtering * * @example * ```ts * import { Console, Effect, Result, Stream } from "effect" * * const program = Effect.gen(function*() { * const [excluded, satisfying] = yield* Stream.partition( * Stream.make(1, 2, 3, 4), * (n) => n % 2 === 0 ? Result.succeed(n) : Result.fail(n) * ) * const left = yield* Stream.runCollect(excluded) * const right = yield* Stream.runCollect(satisfying) * yield* Console.log(left) * // Output: [ 1, 3 ] * yield* Console.log(right) * // Output: [ 2, 4 ] * }) * ``` */ ( self: Stream, filter: Filter.Filter, Pass, Fail>, options?: { readonly bufferSize?: number | undefined } ): Effect.Effect< [excluded: Stream, satisfying: Stream], never, R | Scope.Scope > } = dual( (args) => isStream(args[0]), ( self: Stream, filter: Filter.Filter, Pass, Fail>, options?: { readonly bufferSize?: number | undefined } ): Effect.Effect< [excluded: Stream, satisfying: Stream], never, R | Scope.Scope > => Effect.map( partitionQueue(self, filter, { capacity: options?.bufferSize ?? 16 }), ([passes, fails]) => [fromQueue(fails), fromQueue(passes)] as const ) ) /** * Returns the specified stream if the given condition is satisfied, otherwise * returns an empty stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect( * Stream.when(Stream.make(1, 2, 3), Effect.succeed(false)) * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [] * ``` * * @since 2.0.0 * @category Filtering */ export const when: { /** * Returns the specified stream if the given condition is satisfied, otherwise * returns an empty stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect( * Stream.when(Stream.make(1, 2, 3), Effect.succeed(false)) * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [] * ``` * * @since 2.0.0 * @category Filtering */ (test: Effect.Effect): (self: Stream) => Stream /** * Returns the specified stream if the given condition is satisfied, otherwise * returns an empty stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect( * Stream.when(Stream.make(1, 2, 3), Effect.succeed(false)) * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [] * ``` * * @since 2.0.0 * @category Filtering */ (self: Stream, test: Effect.Effect): Stream } = dual(2, ( self: Stream, test: Effect.Effect ): Stream => test.pipe( Effect.map((pass) => pass ? self : empty), unwrap )) /** * Runs a sink to peel off enough elements to produce a value and returns that * value with the remaining stream in a scope. * * The returned stream is only valid within the scope. * * @example * ```ts * import { Console, Effect, Sink, Stream } from "effect" * * const stream = Stream.fromArrays([1, 2, 3], [4, 5, 6]) * const sink = Sink.take(3) * * const program = Effect.scoped( * Effect.gen(function*() { * const [peeled, rest] = yield* Stream.peel(stream, sink) * const remaining = yield* Stream.runCollect(rest) * yield* Console.log([peeled, remaining]) * }) * ) * * Effect.runPromise(program) * // Output: [ [1, 2, 3], [4, 5, 6] ] * ``` * * @since 2.0.0 * @category Destructors */ export const peel: { /** * Runs a sink to peel off enough elements to produce a value and returns that * value with the remaining stream in a scope. * * The returned stream is only valid within the scope. * * @example * ```ts * import { Console, Effect, Sink, Stream } from "effect" * * const stream = Stream.fromArrays([1, 2, 3], [4, 5, 6]) * const sink = Sink.take(3) * * const program = Effect.scoped( * Effect.gen(function*() { * const [peeled, rest] = yield* Stream.peel(stream, sink) * const remaining = yield* Stream.runCollect(rest) * yield* Console.log([peeled, remaining]) * }) * ) * * Effect.runPromise(program) * // Output: [ [1, 2, 3], [4, 5, 6] ] * ``` * * @since 2.0.0 * @category Destructors */ (sink: Sink.Sink): (self: Stream) => Effect.Effect<[A2, Stream], E2 | E, Scope.Scope | R2 | R> /** * Runs a sink to peel off enough elements to produce a value and returns that * value with the remaining stream in a scope. * * The returned stream is only valid within the scope. * * @example * ```ts * import { Console, Effect, Sink, Stream } from "effect" * * const stream = Stream.fromArrays([1, 2, 3], [4, 5, 6]) * const sink = Sink.take(3) * * const program = Effect.scoped( * Effect.gen(function*() { * const [peeled, rest] = yield* Stream.peel(stream, sink) * const remaining = yield* Stream.runCollect(rest) * yield* Console.log([peeled, remaining]) * }) * ) * * Effect.runPromise(program) * // Output: [ [1, 2, 3], [4, 5, 6] ] * ``` * * @since 2.0.0 * @category Destructors */ (self: Stream, sink: Sink.Sink): Effect.Effect<[A2, Stream], E | E2, Scope.Scope | R | R2> } = dual( 2, Effect.fnUntraced(function*( self: Stream, sink: Sink.Sink ): Effect.fn.Return<[A2, Stream], E | E2, Scope.Scope | R | R2> { let cause: Cause.Cause> | undefined = undefined const originalPull = yield* Channel.toPull(self.channel) const pull: Pull.Pull< Arr.NonEmptyReadonlyArray, E > = Effect.catchCause(originalPull, (cause_) => { cause = cause_ return Effect.failCause(cause_) }) let stream = fromPull(Effect.succeed(pull)) as Stream const leftover = yield* run(stream, sink) if (cause) return [leftover, empty] stream = fromPull(Effect.succeed(originalPull)) return [leftover, stream] }) ) /** * Buffers up to `capacity` elements so a faster producer can progress * independently of a slower consumer. * * Note: This combinator destroys chunking. Use `Stream.rechunk` afterwards if * you need fixed chunk sizes. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.buffer({ capacity: 1 }), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Rate Limiting */ export const buffer: { /** * Buffers up to `capacity` elements so a faster producer can progress * independently of a slower consumer. * * Note: This combinator destroys chunking. Use `Stream.rechunk` afterwards if * you need fixed chunk sizes. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.buffer({ capacity: 1 }), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Rate Limiting */ ( options: { readonly capacity: "unbounded" } | { readonly capacity: number readonly strategy?: "dropping" | "sliding" | "suspend" | undefined } ): (self: Stream) => Stream /** * Buffers up to `capacity` elements so a faster producer can progress * independently of a slower consumer. * * Note: This combinator destroys chunking. Use `Stream.rechunk` afterwards if * you need fixed chunk sizes. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.buffer({ capacity: 1 }), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Rate Limiting */ ( self: Stream, options: { readonly capacity: "unbounded" } | { readonly capacity: number readonly strategy?: "dropping" | "sliding" | "suspend" | undefined } ): Stream } = dual(2, ( self: Stream, options: { readonly capacity: "unbounded" } | { readonly capacity: number readonly strategy?: "dropping" | "sliding" | "suspend" | undefined } ): Stream => fromChannel(Channel.bufferArray(self.channel, options))) /** * Allows a faster producer to progress independently of a slower consumer by * buffering up to `capacity` chunks in a queue. * * This combinator preserves chunking and is best with power-of-2 capacities. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.bufferChunks` * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.fromArrays([1, 2], [3, 4]).pipe( * Stream.bufferArray({ capacity: 2 }), * Stream.runCollect * ) * yield* Console.log(result) * }) * * // Output: [ 1, 2, 3, 4 ] * ``` * * @since 2.0.0 * @category Rate Limiting */ export const bufferArray: { /** * Allows a faster producer to progress independently of a slower consumer by * buffering up to `capacity` chunks in a queue. * * This combinator preserves chunking and is best with power-of-2 capacities. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.bufferChunks` * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.fromArrays([1, 2], [3, 4]).pipe( * Stream.bufferArray({ capacity: 2 }), * Stream.runCollect * ) * yield* Console.log(result) * }) * * // Output: [ 1, 2, 3, 4 ] * ``` * * @since 2.0.0 * @category Rate Limiting */ ( options: { readonly capacity: "unbounded" } | { readonly capacity: number readonly strategy?: "dropping" | "sliding" | "suspend" | undefined } ): (self: Stream) => Stream /** * Allows a faster producer to progress independently of a slower consumer by * buffering up to `capacity` chunks in a queue. * * This combinator preserves chunking and is best with power-of-2 capacities. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.bufferChunks` * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.fromArrays([1, 2], [3, 4]).pipe( * Stream.bufferArray({ capacity: 2 }), * Stream.runCollect * ) * yield* Console.log(result) * }) * * // Output: [ 1, 2, 3, 4 ] * ``` * * @since 2.0.0 * @category Rate Limiting */ ( self: Stream, options: { readonly capacity: "unbounded" } | { readonly capacity: number readonly strategy?: "dropping" | "sliding" | "suspend" | undefined } ): Stream } = dual(2, ( self: Stream, options: { readonly capacity: "unbounded" } | { readonly capacity: number readonly strategy?: "dropping" | "sliding" | "suspend" | undefined } ): Stream => fromChannel(Channel.buffer(self.channel, options))) /** * Switches over to the stream produced by the provided function in case this * one fails. Allows recovery from all causes of failure, including * interruption if the stream is uninterruptible. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.catchAllCause` * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2).pipe( * Stream.concat(Stream.fail("Oops!")), * Stream.concat(Stream.make(3, 4)) * ) * * const recovered = stream.pipe( * Stream.catchCause(() => Stream.make(999)) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(recovered) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 999 ] * ``` * * @since 4.0.0 * @category Error Handling */ export const catchCause: { /** * Switches over to the stream produced by the provided function in case this * one fails. Allows recovery from all causes of failure, including * interruption if the stream is uninterruptible. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.catchAllCause` * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2).pipe( * Stream.concat(Stream.fail("Oops!")), * Stream.concat(Stream.make(3, 4)) * ) * * const recovered = stream.pipe( * Stream.catchCause(() => Stream.make(999)) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(recovered) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 999 ] * ``` * * @since 4.0.0 * @category Error Handling */ (f: (cause: Cause.Cause) => Stream): (self: Stream) => Stream /** * Switches over to the stream produced by the provided function in case this * one fails. Allows recovery from all causes of failure, including * interruption if the stream is uninterruptible. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.catchAllCause` * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2).pipe( * Stream.concat(Stream.fail("Oops!")), * Stream.concat(Stream.make(3, 4)) * ) * * const recovered = stream.pipe( * Stream.catchCause(() => Stream.make(999)) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(recovered) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 999 ] * ``` * * @since 4.0.0 * @category Error Handling */ (self: Stream, f: (cause: Cause.Cause) => Stream): Stream } = dual(2, ( self: Stream, f: (cause: Cause.Cause) => Stream ): Stream => self.channel.pipe( Channel.catchCause((cause) => f(cause).channel), fromChannel )) /** * Runs an effect when the stream fails without changing its values or error, * unless the tap effect itself fails. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.tapErrorCause` * * @example * ```ts * import { Cause, Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2).pipe( * Stream.concat(Stream.fail("boom")), * Stream.tapCause((cause) => Console.log(Cause.isReason(cause))), * Stream.catch(() => Stream.succeed(0)) * ) * * const program = Effect.gen(function* () { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: true * // Output: [ 1, 2, 0 ] * ``` * * @since 4.0.0 * @category Error Handling */ export const tapCause: { /** * Runs an effect when the stream fails without changing its values or error, * unless the tap effect itself fails. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.tapErrorCause` * * @example * ```ts * import { Cause, Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2).pipe( * Stream.concat(Stream.fail("boom")), * Stream.tapCause((cause) => Console.log(Cause.isReason(cause))), * Stream.catch(() => Stream.succeed(0)) * ) * * const program = Effect.gen(function* () { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: true * // Output: [ 1, 2, 0 ] * ``` * * @since 4.0.0 * @category Error Handling */ (f: (cause: Cause.Cause) => Effect.Effect): (self: Stream) => Stream /** * Runs an effect when the stream fails without changing its values or error, * unless the tap effect itself fails. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.tapErrorCause` * * @example * ```ts * import { Cause, Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2).pipe( * Stream.concat(Stream.fail("boom")), * Stream.tapCause((cause) => Console.log(Cause.isReason(cause))), * Stream.catch(() => Stream.succeed(0)) * ) * * const program = Effect.gen(function* () { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: true * // Output: [ 1, 2, 0 ] * ``` * * @since 4.0.0 * @category Error Handling */ ( self: Stream, f: (cause: Cause.Cause) => Effect.Effect ): Stream } = dual(2, ( self: Stream, f: (cause: Cause.Cause) => Effect.Effect ): Stream => self.channel.pipe( Channel.tapCause(f), fromChannel )) const catch_: { ( f: (error: E) => Stream ): (self: Stream) => Stream ( self: Stream, f: (error: E) => Stream ): Stream } = dual(2, ( self: Stream, f: (error: E) => Stream ): Stream => fromChannel(Channel.catch(self.channel, (error) => f(error).channel))) export { /** * Switches over to the stream produced by the provided function if this one fails. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.catchAll` * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2).pipe( * Stream.concat(Stream.fail("Oops!")), * Stream.catch(() => Stream.make(999)) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 999 ] * ``` * * @since 4.0.0 * @category Error Handling */ catch_ as catch } /** * Effectfully peeks at errors without changing the stream unless the tap fails. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2).pipe( * Stream.concat(Stream.fail("boom")), * Stream.tapError((error) => Console.log(`tapError: ${error}`)), * Stream.catch(() => Stream.make(999)) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: * // tapError: boom * // [ 1, 2, 999 ] * ``` * * @since 4.0.0 * @category Error Handling */ export const tapError: { /** * Effectfully peeks at errors without changing the stream unless the tap fails. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2).pipe( * Stream.concat(Stream.fail("boom")), * Stream.tapError((error) => Console.log(`tapError: ${error}`)), * Stream.catch(() => Stream.make(999)) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: * // tapError: boom * // [ 1, 2, 999 ] * ``` * * @since 4.0.0 * @category Error Handling */ (f: (error: E) => Effect.Effect): (self: Stream) => Stream /** * Effectfully peeks at errors without changing the stream unless the tap fails. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2).pipe( * Stream.concat(Stream.fail("boom")), * Stream.tapError((error) => Console.log(`tapError: ${error}`)), * Stream.catch(() => Stream.make(999)) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: * // tapError: boom * // [ 1, 2, 999 ] * ``` * * @since 4.0.0 * @category Error Handling */ (self: Stream, f: (error: E) => Effect.Effect): Stream } = dual(2, ( self: Stream, f: (error: E) => Effect.Effect ): Stream => self.channel.pipe( Channel.tapError(f), fromChannel )) /** * Recovers from errors that match a predicate by switching to a recovery stream. * * When a failure matches the filter, the stream switches to the recovery * stream. Non-matching failures propagate downstream, so the error type is * preserved unless the filter narrows it. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.catchSome` * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2).pipe( * Stream.concat(Stream.fail(42)), * Stream.catchIf( * (error): error is 42 => error === 42, * () => Stream.make(999) * ) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * // Output: [ 1, 2, 999 ] * }) * * Effect.runPromise(program) * ``` * * @since 4.0.0 * @category Error Handling */ export const catchIf: { /** * Recovers from errors that match a predicate by switching to a recovery stream. * * When a failure matches the filter, the stream switches to the recovery * stream. Non-matching failures propagate downstream, so the error type is * preserved unless the filter narrows it. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.catchSome` * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2).pipe( * Stream.concat(Stream.fail(42)), * Stream.catchIf( * (error): error is 42 => error === 42, * () => Stream.make(999) * ) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * // Output: [ 1, 2, 999 ] * }) * * Effect.runPromise(program) * ``` * * @since 4.0.0 * @category Error Handling */ , R3 = never>( refinement: Refinement, EB>, f: (e: EB) => Stream, orElse?: ((e: Exclude) => Stream) | undefined ): (self: Stream) => Stream /** * Recovers from errors that match a predicate by switching to a recovery stream. * * When a failure matches the filter, the stream switches to the recovery * stream. Non-matching failures propagate downstream, so the error type is * preserved unless the filter narrows it. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.catchSome` * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2).pipe( * Stream.concat(Stream.fail(42)), * Stream.catchIf( * (error): error is 42 => error === 42, * () => Stream.make(999) * ) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * // Output: [ 1, 2, 999 ] * }) * * Effect.runPromise(program) * ``` * * @since 4.0.0 * @category Error Handling */ ( predicate: Predicate>, f: (e: NoInfer) => Stream, orElse?: ((e: NoInfer) => Stream) | undefined ): (self: Stream) => Stream /** * Recovers from errors that match a predicate by switching to a recovery stream. * * When a failure matches the filter, the stream switches to the recovery * stream. Non-matching failures propagate downstream, so the error type is * preserved unless the filter narrows it. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.catchSome` * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2).pipe( * Stream.concat(Stream.fail(42)), * Stream.catchIf( * (error): error is 42 => error === 42, * () => Stream.make(999) * ) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * // Output: [ 1, 2, 999 ] * }) * * Effect.runPromise(program) * ``` * * @since 4.0.0 * @category Error Handling */ , R3 = never>( self: Stream, refinement: Refinement, f: (e: EB) => Stream, orElse?: ((e: Exclude) => Stream) | undefined ): Stream /** * Recovers from errors that match a predicate by switching to a recovery stream. * * When a failure matches the filter, the stream switches to the recovery * stream. Non-matching failures propagate downstream, so the error type is * preserved unless the filter narrows it. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.catchSome` * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2).pipe( * Stream.concat(Stream.fail(42)), * Stream.catchIf( * (error): error is 42 => error === 42, * () => Stream.make(999) * ) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * // Output: [ 1, 2, 999 ] * }) * * Effect.runPromise(program) * ``` * * @since 4.0.0 * @category Error Handling */ ( self: Stream, predicate: Predicate, f: (e: E) => Stream, orElse?: ((e: E) => Stream) | undefined ): Stream } = dual((args) => isStream(args[0]), < A, E, R, A2, E2, R2, A3 = never, E3 = E, R3 = never >( self: Stream, predicate: Predicate, f: (failure: E) => Stream, orElse?: ((failure: E) => Stream) | undefined ): Stream => fromChannel( Channel.catchIf( toChannel(self), predicate, (e) => f(e).channel, orElse && ((e) => orElse(e).channel) ) )) /** * Recovers from errors that match a `Filter` by switching to a recovery * stream. * * @since 4.0.0 * @category Error Handling */ export const catchFilter: { /** * Recovers from errors that match a `Filter` by switching to a recovery * stream. * * @since 4.0.0 * @category Error Handling */ ( filter: Filter.Filter, EB, X>, f: (failure: EB) => Stream, orElse?: ((failure: X) => Stream) | undefined ): (self: Stream) => Stream /** * Recovers from errors that match a `Filter` by switching to a recovery * stream. * * @since 4.0.0 * @category Error Handling */ ( self: Stream, filter: Filter.Filter, EB, X>, f: (failure: EB) => Stream, orElse?: ((failure: X) => Stream) | undefined ): Stream } = dual((args) => isStream(args[0]), < A, E, R, EB, A2, E2, R2, X, A3 = never, E3 = X, R3 = never >( self: Stream, filter: Filter.Filter, EB, X>, f: (failure: EB) => Stream, orElse?: ((failure: X) => Stream) | undefined ): Stream => fromChannel( Channel.catchFilter( toChannel(self), filter, (e) => f(e).channel, orElse && ((e) => orElse(e).channel) ) )) /** * Recovers from failures whose `_tag` matches the provided value by switching to * the stream returned by `f`. * * **When to Use** * * Use `catchTag` when your error type is a tagged union with a readonly `_tag` * field and you want to handle a specific error case. * * @example * ```ts * import { Console, Data, Effect, Stream } from "effect" * * class HttpError extends Data.TaggedError("HttpError")<{ message: string }> {} * * const stream = Stream.fail(new HttpError({ message: "timeout" })) * * const recovered = Stream.catchTag(stream, "HttpError", (error) => * Stream.make(`Recovered: ${error.message}`) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(recovered) * yield* Console.log(values) * // Output: [ "Recovered: timeout" ] * }) * * Effect.runPromise(program) * ``` * * @since 4.0.0 * @category Error Handling */ export const catchTag: { /** * Recovers from failures whose `_tag` matches the provided value by switching to * the stream returned by `f`. * * **When to Use** * * Use `catchTag` when your error type is a tagged union with a readonly `_tag` * field and you want to handle a specific error case. * * @example * ```ts * import { Console, Data, Effect, Stream } from "effect" * * class HttpError extends Data.TaggedError("HttpError")<{ message: string }> {} * * const stream = Stream.fail(new HttpError({ message: "timeout" })) * * const recovered = Stream.catchTag(stream, "HttpError", (error) => * Stream.make(`Recovered: ${error.message}`) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(recovered) * yield* Console.log(values) * // Output: [ "Recovered: timeout" ] * }) * * Effect.runPromise(program) * ``` * * @since 4.0.0 * @category Error Handling */ < const K extends Tags | Arr.NonEmptyReadonlyArray>, E, A1, E1, R1, A2 = never, E2 = ExcludeTag ? K[number] : K>, R2 = never >( k: K, f: ( e: ExtractTag, K extends Arr.NonEmptyReadonlyArray ? K[number] : K> ) => Stream, orElse?: | ((e: ExcludeTag ? K[number] : K>) => Stream) | undefined ): ( self: Stream ) => Stream /** * Recovers from failures whose `_tag` matches the provided value by switching to * the stream returned by `f`. * * **When to Use** * * Use `catchTag` when your error type is a tagged union with a readonly `_tag` * field and you want to handle a specific error case. * * @example * ```ts * import { Console, Data, Effect, Stream } from "effect" * * class HttpError extends Data.TaggedError("HttpError")<{ message: string }> {} * * const stream = Stream.fail(new HttpError({ message: "timeout" })) * * const recovered = Stream.catchTag(stream, "HttpError", (error) => * Stream.make(`Recovered: ${error.message}`) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(recovered) * yield* Console.log(values) * // Output: [ "Recovered: timeout" ] * }) * * Effect.runPromise(program) * ``` * * @since 4.0.0 * @category Error Handling */ < A, E, R, const K extends Tags | Arr.NonEmptyReadonlyArray>, R1, E1, A1, A2 = never, E2 = ExcludeTag ? K[number] : K>, R2 = never >( self: Stream, k: K, f: (e: ExtractTag ? K[number] : K>) => Stream, orElse?: | ((e: ExcludeTag ? K[number] : K>) => Stream) | undefined ): Stream } = dual( (args) => isStream(args[0]), < A, E, R, const K extends Tags | Arr.NonEmptyReadonlyArray>, R1, E1, A1, A2 = never, E2 = ExcludeTag ? K[number] : K>, R2 = never >( self: Stream, k: K, f: (e: ExtractTag ? K[number] : K>) => Stream, orElse?: | ((e: ExcludeTag ? K[number] : K>) => Stream) | undefined ): Stream => { const pred = Array.isArray(k) ? ((e: E): e is any => hasProperty(e, "_tag") && k.includes(e._tag)) : isTagged(k as string) return catchIf(self, pred, f, orElse as any) as any } ) /** * Switches to a recovery stream based on matching `_tag` handlers. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * class NotFound { * readonly _tag = "NotFound" * constructor(readonly resource: string) {} * } * * class Unauthorized { * readonly _tag = "Unauthorized" * constructor(readonly user: string) {} * } * * const stream = Stream.fail(new NotFound("profile")) * * const program = Effect.gen(function* () { * const result = yield* stream.pipe( * Stream.catchTags({ * NotFound: () => Stream.succeed("fallback"), * Unauthorized: () => Stream.succeed("login") * }), * Stream.runCollect * ) * yield* Console.log(result) * }) * * // Output: [ "fallback" ] * ``` * * @since 4.0.0 * @category Error Handling */ export const catchTags: { /** * Switches to a recovery stream based on matching `_tag` handlers. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * class NotFound { * readonly _tag = "NotFound" * constructor(readonly resource: string) {} * } * * class Unauthorized { * readonly _tag = "Unauthorized" * constructor(readonly user: string) {} * } * * const stream = Stream.fail(new NotFound("profile")) * * const program = Effect.gen(function* () { * const result = yield* stream.pipe( * Stream.catchTags({ * NotFound: () => Stream.succeed("fallback"), * Unauthorized: () => Stream.succeed("login") * }), * Stream.runCollect * ) * yield* Console.log(result) * }) * * // Output: [ "fallback" ] * ``` * * @since 4.0.0 * @category Error Handling */ < E, Cases extends (E extends { _tag: string } ? { [K in E["_tag"]]+?: (error: Extract) => Stream } : {}), A2 = never, E2 = Exclude, R2 = never >( cases: Cases, orElse?: ((e: Exclude) => Stream) | undefined ): (self: Stream) => Stream< | A | A2 | { [K in keyof Cases]: Cases[K] extends ((...args: Array) => Stream) ? A : never }[keyof Cases], | E2 | { [K in keyof Cases]: Cases[K] extends ((...args: Array) => Stream) ? E : never }[keyof Cases], | R | R2 | { [K in keyof Cases]: Cases[K] extends ((...args: Array) => Stream) ? R : never }[keyof Cases] > /** * Switches to a recovery stream based on matching `_tag` handlers. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * class NotFound { * readonly _tag = "NotFound" * constructor(readonly resource: string) {} * } * * class Unauthorized { * readonly _tag = "Unauthorized" * constructor(readonly user: string) {} * } * * const stream = Stream.fail(new NotFound("profile")) * * const program = Effect.gen(function* () { * const result = yield* stream.pipe( * Stream.catchTags({ * NotFound: () => Stream.succeed("fallback"), * Unauthorized: () => Stream.succeed("login") * }), * Stream.runCollect * ) * yield* Console.log(result) * }) * * // Output: [ "fallback" ] * ``` * * @since 4.0.0 * @category Error Handling */ < R, E, A, Cases extends (E extends { _tag: string } ? { [K in E["_tag"]]+?: (error: Extract) => Stream } : {}), A2 = never, E2 = Exclude, R2 = never >( self: Stream, cases: Cases, orElse?: ((e: Exclude) => Stream) | undefined ): Stream< | A | A2 | { [K in keyof Cases]: Cases[K] extends ((...args: Array) => Stream) ? A : never }[keyof Cases], | E2 | { [K in keyof Cases]: Cases[K] extends ((...args: Array) => Stream) ? E : never }[keyof Cases], | R | R2 | { [K in keyof Cases]: Cases[K] extends ((...args: Array) => Stream) ? R : never }[keyof Cases] > } = dual((args) => isStream(args[0]), (self, cases, orElse) => { let keys: Array return catchFilter( self, (e: any) => { keys ??= Object.keys(cases) return hasProperty(e, "_tag") && isString(e["_tag"]) && keys.includes(e["_tag"]) ? Result.succeed(e) : Result.fail(e) }, (e: any) => cases[e["_tag"] as string](e), orElse ) }) /** * Catches a specific reason within a tagged error. * * Use this to handle nested error causes without removing the parent error * from the error channel. The handler receives the unwrapped reason. * * @example * ```ts * import { Console, Data, Effect, Stream } 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 stream = Stream.fail( * new AiError({ reason: new RateLimitError({ retryAfter: 60 }) }) * ) * * const program = Effect.gen(function*() { * const values = yield* stream.pipe( * Stream.catchReason("AiError", "RateLimitError", (reason) => * Stream.succeed(`retry: ${reason.retryAfter}`) * ), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ "retry: 60" ] * ``` * * @since 4.0.0 * @category Error Handling */ export const catchReason: { /** * Catches a specific reason within a tagged error. * * Use this to handle nested error causes without removing the parent error * from the error channel. The handler receives the unwrapped reason. * * @example * ```ts * import { Console, Data, Effect, Stream } 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 stream = Stream.fail( * new AiError({ reason: new RateLimitError({ retryAfter: 60 }) }) * ) * * const program = Effect.gen(function*() { * const values = yield* stream.pipe( * Stream.catchReason("AiError", "RateLimitError", (reason) => * Stream.succeed(`retry: ${reason.retryAfter}`) * ), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ "retry: 60" ] * ``` * * @since 4.0.0 * @category Error Handling */ < K extends Tags, E, RK extends ReasonTags, K>>, A2, E2, R2, A3 = unassigned, E3 = never, R3 = never >( errorTag: K, reasonTag: RK, f: ( reason: ExtractReason, K>, RK>, error: NarrowReason, K>, RK> ) => Stream, orElse?: | (( reason: ExcludeReason, K>, RK>, error: OmitReason, K>, RK> ) => Stream) | undefined ): ( self: Stream ) => Stream, (A3 extends unassigned ? E : ExcludeTag) | E2 | E3, R | R2 | R3> /** * Catches a specific reason within a tagged error. * * Use this to handle nested error causes without removing the parent error * from the error channel. The handler receives the unwrapped reason. * * @example * ```ts * import { Console, Data, Effect, Stream } 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 stream = Stream.fail( * new AiError({ reason: new RateLimitError({ retryAfter: 60 }) }) * ) * * const program = Effect.gen(function*() { * const values = yield* stream.pipe( * Stream.catchReason("AiError", "RateLimitError", (reason) => * Stream.succeed(`retry: ${reason.retryAfter}`) * ), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ "retry: 60" ] * ``` * * @since 4.0.0 * @category Error Handling */ < A, E, R, K extends Tags, RK extends ReasonTags>, A2, E2, R2, A3 = unassigned, E3 = never, R3 = never >( self: Stream, errorTag: K, reasonTag: RK, f: (reason: ExtractReason, RK>, error: NarrowReason, RK>) => Stream, orElse?: | ((reason: ExcludeReason, RK>, error: OmitReason, RK>) => Stream) | undefined ): Stream, (A3 extends unassigned ? E : ExcludeTag) | E2 | E3, R | R2 | R3> } = dual( (args) => isStream(args[0]), < A, E, R, K extends Tags, RK extends ReasonTags>, A2, E2, R2, A3 = unassigned, E3 = never, R3 = never >( self: Stream, errorTag: K, reasonTag: RK, f: (reason: ExtractReason, RK>, error: NarrowReason, RK>) => Stream, orElse?: | ((reason: ExcludeReason, RK>, error: OmitReason, RK>) => Stream) | undefined ): Stream, (A3 extends unassigned ? E : ExcludeTag) | E2 | E3, R | R2 | R3> => fromChannel( Channel.catchReason( toChannel(self), errorTag, reasonTag, (reason, error) => f(reason, error).channel, orElse && ((reason, error) => orElse(reason, error).channel) ) ) as any ) /** * Catches multiple reasons within a tagged error using an object of handlers. * * @example * ```ts * import { Console, Data, Effect, Stream } 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 stream = Stream.fail( * new AiError({ reason: new RateLimitError({ retryAfter: 60 }) }) * ) * * const program = Effect.gen(function*() { * const values = yield* stream.pipe( * Stream.catchReasons("AiError", { * RateLimitError: (reason) => Stream.succeed(`retry: ${reason.retryAfter}`), * QuotaExceededError: (reason) => Stream.succeed(`quota: ${reason.limit}`) * }), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ "retry: 60" ] * ``` * * @since 4.0.0 * @category Error Handling */ export const catchReasons: { /** * Catches multiple reasons within a tagged error using an object of handlers. * * @example * ```ts * import { Console, Data, Effect, Stream } 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 stream = Stream.fail( * new AiError({ reason: new RateLimitError({ retryAfter: 60 }) }) * ) * * const program = Effect.gen(function*() { * const values = yield* stream.pipe( * Stream.catchReasons("AiError", { * RateLimitError: (reason) => Stream.succeed(`retry: ${reason.retryAfter}`), * QuotaExceededError: (reason) => Stream.succeed(`quota: ${reason.limit}`) * }), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ "retry: 60" ] * ``` * * @since 4.0.0 * @category Error Handling */ < K extends Tags, E, Cases extends { [RK in ReasonTags, K>>]+?: ( reason: ExtractReason, K>, RK>, error: NarrowReason, K>, RK> ) => Stream }, A2 = unassigned, E2 = never, R2 = never >( errorTag: K, cases: Cases, orElse?: | (( reason: ExcludeReason, K>, Extract>, error: OmitReason, K>, Extract> ) => Stream) | undefined ): (self: Stream) => Stream< | A | Exclude | { [RK in keyof Cases]: Cases[RK] extends (...args: Array) => Stream ? A : never }[keyof Cases], | (A2 extends unassigned ? E : ExcludeTag) | E2 | { [RK in keyof Cases]: Cases[RK] extends (...args: Array) => Stream ? E : never }[keyof Cases], | R | R2 | { [RK in keyof Cases]: Cases[RK] extends (...args: Array) => Stream ? R : never }[keyof Cases] > /** * Catches multiple reasons within a tagged error using an object of handlers. * * @example * ```ts * import { Console, Data, Effect, Stream } 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 stream = Stream.fail( * new AiError({ reason: new RateLimitError({ retryAfter: 60 }) }) * ) * * const program = Effect.gen(function*() { * const values = yield* stream.pipe( * Stream.catchReasons("AiError", { * RateLimitError: (reason) => Stream.succeed(`retry: ${reason.retryAfter}`), * QuotaExceededError: (reason) => Stream.succeed(`quota: ${reason.limit}`) * }), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ "retry: 60" ] * ``` * * @since 4.0.0 * @category Error Handling */ < A, E, R, K extends Tags, Cases extends { [RK in ReasonTags>]+?: ( reason: ExtractReason, RK>, error: NarrowReason, RK> ) => Stream }, A2 = unassigned, E2 = never, R2 = never >( self: Stream, errorTag: K, cases: Cases, orElse?: | (( reason: ExcludeReason, K>, Extract>, error: OmitReason, K>, Extract> ) => Stream) | undefined ): Stream< | A | Exclude | { [RK in keyof Cases]: Cases[RK] extends (...args: Array) => Stream ? A : never }[keyof Cases], | (A2 extends unassigned ? E : ExcludeTag) | E2 | { [RK in keyof Cases]: Cases[RK] extends (...args: Array) => Stream ? E : never }[keyof Cases], | R | R2 | { [RK in keyof Cases]: Cases[RK] extends (...args: Array) => Stream ? R : never }[keyof Cases] > } = dual((args) => isStream(args[0]), (self, errorTag, cases, orElse) => { const handlers: Record Channel.Channel> = {} for (const key of Object.keys(cases)) { const handler = (cases as any)[key] handlers[key] = (reason, error) => handler(reason, error).channel } const orElseHandler = orElse && ((reason: any, error: any) => orElse(reason, error).channel) return fromChannel( Channel.catchReasons(self.channel, errorTag as any, handlers as any, orElseHandler as any) as Channel.Channel< Arr.NonEmptyReadonlyArray, any, void, unknown, unknown, unknown, any > ) as any }) /** * Transforms the errors emitted by this stream using `f`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.fail("bad").pipe( * Stream.mapError((error) => `mapped: ${error}`), * Stream.catch((error) => Stream.make(`recovered from ${error}`)), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ "recovered from mapped: bad" ] * ``` * * @since 2.0.0 * @category Error Handling */ export const mapError: { /** * Transforms the errors emitted by this stream using `f`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.fail("bad").pipe( * Stream.mapError((error) => `mapped: ${error}`), * Stream.catch((error) => Stream.make(`recovered from ${error}`)), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ "recovered from mapped: bad" ] * ``` * * @since 2.0.0 * @category Error Handling */ (f: (error: E) => E2): (self: Stream) => Stream /** * Transforms the errors emitted by this stream using `f`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.fail("bad").pipe( * Stream.mapError((error) => `mapped: ${error}`), * Stream.catch((error) => Stream.make(`recovered from ${error}`)), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ "recovered from mapped: bad" ] * ``` * * @since 2.0.0 * @category Error Handling */ (self: Stream, f: (error: E) => E2): Stream } = dual(2, ( self: Stream, f: (error: E) => E2 ): Stream => fromChannel(Channel.mapError(self.channel, f))) /** * Recovers from stream failures by filtering the `Cause` and switching to a recovery stream. * Non-matching causes are re-emitted as failures. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.catchSomeCause` * * @example * ```ts * import { Cause, Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const failingStream = Stream.fail("NetworkError") * const recovered = Stream.catchCauseIf( * failingStream, * (cause) => Cause.hasFails(cause), * (cause) => Stream.make(`Recovered: ${Cause.squash(cause)}`) * ) * * const output = yield* Stream.runCollect(recovered) * yield* Console.log(output) * }) * * Effect.runPromise(program) * // Output: [ "Recovered: NetworkError" ] * ``` * * @since 4.0.0 * @category Error Handling */ export const catchCauseIf: { /** * Recovers from stream failures by filtering the `Cause` and switching to a recovery stream. * Non-matching causes are re-emitted as failures. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.catchSomeCause` * * @example * ```ts * import { Cause, Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const failingStream = Stream.fail("NetworkError") * const recovered = Stream.catchCauseIf( * failingStream, * (cause) => Cause.hasFails(cause), * (cause) => Stream.make(`Recovered: ${Cause.squash(cause)}`) * ) * * const output = yield* Stream.runCollect(recovered) * yield* Console.log(output) * }) * * Effect.runPromise(program) * // Output: [ "Recovered: NetworkError" ] * ``` * * @since 4.0.0 * @category Error Handling */ ( predicate: Predicate>, f: (cause: Cause.Cause) => Stream ): ( self: Stream ) => Stream /** * Recovers from stream failures by filtering the `Cause` and switching to a recovery stream. * Non-matching causes are re-emitted as failures. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.catchSomeCause` * * @example * ```ts * import { Cause, Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const failingStream = Stream.fail("NetworkError") * const recovered = Stream.catchCauseIf( * failingStream, * (cause) => Cause.hasFails(cause), * (cause) => Stream.make(`Recovered: ${Cause.squash(cause)}`) * ) * * const output = yield* Stream.runCollect(recovered) * yield* Console.log(output) * }) * * Effect.runPromise(program) * // Output: [ "Recovered: NetworkError" ] * ``` * * @since 4.0.0 * @category Error Handling */ ( self: Stream, predicate: Predicate>, f: (cause: Cause.Cause) => Stream ): Stream } = dual(3, ( self: Stream, predicate: Predicate>, f: (cause: Cause.Cause) => Stream ): Stream => fromChannel( Channel.catchCauseIf( self.channel, predicate, (cause) => f(cause).channel ) )) /** * Recovers from stream failures by filtering the `Cause` and switching to a * recovery stream. * * @since 4.0.0 * @category Error Handling */ export const catchCauseFilter: { /** * Recovers from stream failures by filtering the `Cause` and switching to a * recovery stream. * * @since 4.0.0 * @category Error Handling */ >( filter: Filter.Filter, EB, X>, f: (failure: EB, cause: Cause.Cause) => Stream ): ( self: Stream ) => Stream | E2, R2 | R> /** * Recovers from stream failures by filtering the `Cause` and switching to a * recovery stream. * * @since 4.0.0 * @category Error Handling */ >( self: Stream, filter: Filter.Filter, EB, X>, f: (failure: EB, cause: Cause.Cause) => Stream ): Stream | E2, R | R2> } = dual(3, >( self: Stream, filter: Filter.Filter, EB, X>, f: (failure: EB, cause: Cause.Cause) => Stream ): Stream | E2, R | R2> => fromChannel( Channel.catchCauseFilter( self.channel, filter, (failure, cause) => f(failure, cause).channel ) )) /** * Switches to a fallback stream if this stream is empty. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.empty.pipe( * Stream.orElseIfEmpty(() => Stream.make(1, 2)), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2 ] * ``` * * @since 2.0.0 * @category Error Handling */ export const orElseIfEmpty: { /** * Switches to a fallback stream if this stream is empty. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.empty.pipe( * Stream.orElseIfEmpty(() => Stream.make(1, 2)), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2 ] * ``` * * @since 2.0.0 * @category Error Handling */ (orElse: LazyArg>): (self: Stream) => Stream /** * Switches to a fallback stream if this stream is empty. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.empty.pipe( * Stream.orElseIfEmpty(() => Stream.make(1, 2)), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2 ] * ``` * * @since 2.0.0 * @category Error Handling */ (self: Stream, orElse: LazyArg>): Stream } = dual(2, ( self: Stream, orElse: LazyArg> ): Stream => fromChannel(Channel.orElseIfEmpty( self.channel, (_) => toChannel(orElse()) ))) /** * Returns a stream that emits a fallback value when this stream fails. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.fail("NetworkError").pipe( * Stream.orElseSucceed((error) => `Recovered: ${error}`) * ) * * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ "Recovered: NetworkError" ] * ``` * * @since 2.0.0 * @category Error Handling */ export const orElseSucceed: { /** * Returns a stream that emits a fallback value when this stream fails. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.fail("NetworkError").pipe( * Stream.orElseSucceed((error) => `Recovered: ${error}`) * ) * * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ "Recovered: NetworkError" ] * ``` * * @since 2.0.0 * @category Error Handling */ (f: (error: E) => A2): (self: Stream) => Stream /** * Returns a stream that emits a fallback value when this stream fails. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.fail("NetworkError").pipe( * Stream.orElseSucceed((error) => `Recovered: ${error}`) * ) * * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ "Recovered: NetworkError" ] * ``` * * @since 2.0.0 * @category Error Handling */ (self: Stream, f: (error: E) => A2): Stream } = dual(2, ( self: Stream, f: (error: E) => A2 ): Stream => catch_(self, (e) => succeed(f(e)))) /** * Turns typed failures into defects, making the stream infallible. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.orDie, * Stream.runCollect * ) * * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Error Handling */ export const orDie = (self: Stream): Stream => fromChannel(Channel.orDie(self.channel)) /** * Ignores failures and ends the stream on error. * * Use the `log` option to emit the full {@link Cause} when the stream fails. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.concat(Stream.fail("boom")), * Stream.ignore, * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @example * ```ts * import { Stream } from "effect" * * const stream = Stream.fail("boom") * * const program = stream.pipe(Stream.ignore({ log: "Error" })) * ``` * * @since 4.0.0 * @category Error Handling */ export const ignore: < Arg extends Stream | { readonly log?: boolean | Severity | undefined } | undefined >( selfOrOptions: Arg, options?: { readonly log?: boolean | Severity | undefined } | undefined ) => [Arg] extends [Stream] ? Stream : (self: Stream) => Stream = dual( (args) => isStream(args[0]), ( self: Stream, options?: { readonly log?: boolean | Severity | undefined } | undefined ): Stream => fromChannel(Channel.ignore(self.channel, options)) ) /** * Ignores the stream's failure cause, including defects, and ends the stream. * * Use the `log` option to emit the full {@link Cause} when the stream fails. * * @example * ```ts * import { Effect, Stream } from "effect" * * const stream = Stream.make(1, 2).pipe( * Stream.concat(Stream.fail("boom")), * Stream.ignoreCause({ log: "Error" }) * ) * * const program = Stream.runCollect(stream) * ``` * * @since 4.0.0 * @category Error Handling */ export const ignoreCause: < Arg extends Stream | { readonly log?: boolean | Severity | undefined } | undefined >( streamOrOptions: Arg, options?: { readonly log?: boolean | Severity | undefined } | undefined ) => [Arg] extends [Stream] ? Stream : (self: Stream) => Stream = dual( (args) => isStream(args[0]), ( self: Stream, options?: { readonly log?: boolean | Severity | undefined } | undefined ): Stream => fromChannel(Channel.ignoreCause(self.channel, options)) ) /** * When the stream fails, retry it according to the given schedule. * * This retries the entire stream, so will re-execute all of the stream's * acquire operations. * * The schedule is reset as soon as the first element passes through the * stream again. * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1).pipe( * Stream.concat(Stream.fail("boom")), * Stream.retry(Schedule.recurs(1)), * Stream.take(2), * Stream.runCollect * ) * * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 1 ] * ``` * * @since 2.0.0 * @category Error Handling */ export const retry: { /** * When the stream fails, retry it according to the given schedule. * * This retries the entire stream, so will re-execute all of the stream's * acquire operations. * * The schedule is reset as soon as the first element passes through the * stream again. * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1).pipe( * Stream.concat(Stream.fail("boom")), * Stream.retry(Schedule.recurs(1)), * Stream.take(2), * Stream.runCollect * ) * * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 1 ] * ``` * * @since 2.0.0 * @category Error Handling */ ( policy: | Schedule.Schedule, E2, R2> | (( $: (_: Schedule.Schedule, SE, SR>) => Schedule.Schedule ) => Schedule.Schedule, E2, R2>) ): (self: Stream) => Stream /** * When the stream fails, retry it according to the given schedule. * * This retries the entire stream, so will re-execute all of the stream's * acquire operations. * * The schedule is reset as soon as the first element passes through the * stream again. * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1).pipe( * Stream.concat(Stream.fail("boom")), * Stream.retry(Schedule.recurs(1)), * Stream.take(2), * Stream.runCollect * ) * * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 1 ] * ``` * * @since 2.0.0 * @category Error Handling */ ( self: Stream, policy: | Schedule.Schedule, E2, R2> | (( $: (_: Schedule.Schedule, SE, SR>) => Schedule.Schedule ) => Schedule.Schedule, E2, R2>) ): Stream } = dual( 2, ( self: Stream, policy: | Schedule.Schedule, E2, R2> | (( $: (_: Schedule.Schedule, SE, SR>) => Schedule.Schedule ) => Schedule.Schedule, E2, R2>) ): Stream => fromChannel(Channel.retry(self.channel, policy)) ) /** * Apply an `ExecutionPlan` to a stream, retrying with step-provided resources * until it succeeds or the plan is exhausted. * * By default, a failing step can fallback even after emitting elements; set * `preventFallbackOnPartialStream` to fail instead of mixing partial output with * a later fallback. * * @example * ```ts * import { Console, Effect, ExecutionPlan, Layer, Context, Stream } from "effect" * * class Service extends Context.Service()("Service", { * make: Effect.succeed({ * stream: Stream.fail("A") as Stream.Stream * }) * }) { * static Bad = Layer.succeed(Service, Service.of({ stream: Stream.fail("A") })) * static Good = Layer.succeed(Service, Service.of({ stream: Stream.make(1, 2, 3) })) * } * * const plan = ExecutionPlan.make( * { provide: Service.Bad }, * { provide: Service.Good } * ) * * const stream = Stream.unwrap(Effect.map(Service, (_) => _.stream)) * * const program = Effect.gen(function*() { * const items = yield* stream.pipe(Stream.withExecutionPlan(plan), Stream.runCollect) * yield* Console.log(items) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 3.16.0 * @category Error Handling * @experimental */ export const withExecutionPlan: { /** * Apply an `ExecutionPlan` to a stream, retrying with step-provided resources * until it succeeds or the plan is exhausted. * * By default, a failing step can fallback even after emitting elements; set * `preventFallbackOnPartialStream` to fail instead of mixing partial output with * a later fallback. * * @example * ```ts * import { Console, Effect, ExecutionPlan, Layer, Context, Stream } from "effect" * * class Service extends Context.Service()("Service", { * make: Effect.succeed({ * stream: Stream.fail("A") as Stream.Stream * }) * }) { * static Bad = Layer.succeed(Service, Service.of({ stream: Stream.fail("A") })) * static Good = Layer.succeed(Service, Service.of({ stream: Stream.make(1, 2, 3) })) * } * * const plan = ExecutionPlan.make( * { provide: Service.Bad }, * { provide: Service.Good } * ) * * const stream = Stream.unwrap(Effect.map(Service, (_) => _.stream)) * * const program = Effect.gen(function*() { * const items = yield* stream.pipe(Stream.withExecutionPlan(plan), Stream.runCollect) * yield* Console.log(items) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 3.16.0 * @category Error Handling * @experimental */ ( policy: ExecutionPlan.ExecutionPlan<{ provides: Provides; input: Input; error: PolicyE; requirements: R2 }>, options?: { readonly preventFallbackOnPartialStream?: boolean | undefined } ): (self: Stream) => Stream> /** * Apply an `ExecutionPlan` to a stream, retrying with step-provided resources * until it succeeds or the plan is exhausted. * * By default, a failing step can fallback even after emitting elements; set * `preventFallbackOnPartialStream` to fail instead of mixing partial output with * a later fallback. * * @example * ```ts * import { Console, Effect, ExecutionPlan, Layer, Context, Stream } from "effect" * * class Service extends Context.Service()("Service", { * make: Effect.succeed({ * stream: Stream.fail("A") as Stream.Stream * }) * }) { * static Bad = Layer.succeed(Service, Service.of({ stream: Stream.fail("A") })) * static Good = Layer.succeed(Service, Service.of({ stream: Stream.make(1, 2, 3) })) * } * * const plan = ExecutionPlan.make( * { provide: Service.Bad }, * { provide: Service.Good } * ) * * const stream = Stream.unwrap(Effect.map(Service, (_) => _.stream)) * * const program = Effect.gen(function*() { * const items = yield* stream.pipe(Stream.withExecutionPlan(plan), Stream.runCollect) * yield* Console.log(items) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 3.16.0 * @category Error Handling * @experimental */ ( self: Stream, policy: ExecutionPlan.ExecutionPlan<{ provides: Provides; input: Input; error: PolicyE; requirements: R2 }>, options?: { readonly preventFallbackOnPartialStream?: boolean | undefined } ): Stream> } = dual((args) => isStream(args[0]), ( self: Stream, policy: ExecutionPlan.ExecutionPlan<{ provides: Provides input: Input error: PolicyE requirements: R2 }>, options?: { readonly preventFallbackOnPartialStream?: boolean | undefined } ): Stream> => suspend(() => { const preventFallbackOnPartialStream = options?.preventFallbackOnPartialStream ?? false let i = 0 let meta: ExecutionPlan.Metadata = { attempt: 0, stepIndex: 0 } const provideMeta = provideServiceEffect( ExecutionPlan.CurrentMetadata, Effect.sync(() => { meta = { attempt: meta.attempt + 1, stepIndex: i } return meta }) ) let lastError = Option.none()("Service", { * make: Effect.succeed({ * stream: Stream.fail("A") as Stream.Stream * }) * }) { * static Bad = Layer.succeed(Service, Service.of({ stream: Stream.fail("A") })) * static Good = Layer.succeed(Service, Service.of({ stream: Stream.make(1, 2, 3) })) * } * * const plan = ExecutionPlan.make( * { provide: Service.Bad }, * { provide: Service.Good } * ) * * const stream = Stream.unwrap(Effect.map(Service, (_) => _.stream)) * * const program = Effect.gen(function*() { * const items = yield* stream.pipe(Stream.withExecutionPlan(plan), Stream.runCollect) * yield* Console.log(items) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 3.16.0 * @category Error Handling * @experimental */ E | PolicyE>() const loop: Stream< A, E | PolicyE, R2 | Exclude > = suspend(() => { const step = policy.steps[i] if (!step) { return fail(Option.getOrThrow(lastError)) } let nextStream: Stream> = provideMeta(provide(self, step.provide)) let receivedElements = false if (Option.isSome(lastError)) { const error = lastError.value let attempted = false const wrapped = nextStream // ensure the schedule is applied at least once nextStream = suspend(() => { if (attempted) return wrapped attempted = true return fail(error) }) nextStream = retry(nextStream, internalExecutionPlan.scheduleFromStep(step, false) as any) } else { const schedule = internalExecutionPlan.scheduleFromStep(step, true) nextStream = schedule ? retry(nextStream, schedule as any) : nextStream } return catch_( preventFallbackOnPartialStream ? onFirst(nextStream, (_) => { receivedElements = true return Effect.void }) : nextStream, (error) => { i++ if (preventFallbackOnPartialStream && receivedElements) { return fail(error) } lastError = Option.some(error) return loop } ) }) return loop })) /** * Takes the first `n` elements from this stream, returning `Stream.empty` when `n < 1`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.take(3), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Filtering */ export const take: { /** * Takes the first `n` elements from this stream, returning `Stream.empty` when `n < 1`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.take(3), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Filtering */ (n: number): (self: Stream) => Stream /** * Takes the first `n` elements from this stream, returning `Stream.empty` when `n < 1`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.take(3), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Filtering */ (self: Stream, n: number): Stream } = dual( 2, (self: Stream, n: number): Stream => n < 1 ? empty : takeUntil(self, (_, i) => i === (n - 1)) ) /** * Keeps the last `n` elements from this stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.range(1, 6).pipe( * Stream.takeRight(3), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 4, 5, 6 ] * ``` * * @since 2.0.0 * @category Filtering */ export const takeRight: { /** * Keeps the last `n` elements from this stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.range(1, 6).pipe( * Stream.takeRight(3), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 4, 5, 6 ] * ``` * * @since 2.0.0 * @category Filtering */ (n: number): (self: Stream) => Stream /** * Keeps the last `n` elements from this stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.range(1, 6).pipe( * Stream.takeRight(3), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 4, 5, 6 ] * ``` * * @since 2.0.0 * @category Filtering */ (self: Stream, n: number): Stream } = dual( 2, (self: Stream, n: number): Stream => mapAccumArray(self, MutableList.make, (list, arr) => { MutableList.appendAll(list, arr) if (list.length > n) { MutableList.takeNVoid(list, list.length - n) } return [list, emptyArr] }, { onHalt(list) { return MutableList.takeAll(list) } }) ) /** * Takes elements until the predicate matches. * * When `excludeLast` is `true`, the matching element is dropped. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.range(1, 5) * * const program = Effect.gen(function*() { * const inclusive = yield* stream.pipe( * Stream.takeUntil((n) => n % 3 === 0), * Stream.runCollect * ) * yield* Console.log(inclusive) * // Output: [ 1, 2, 3 ] * * const exclusive = yield* stream.pipe( * Stream.takeUntil((n) => n % 3 === 0, { excludeLast: true }), * Stream.runCollect * ) * yield* Console.log(exclusive) * // Output: [ 1, 2 ] * }) * ``` * * @since 2.0.0 * @category Filtering */ export const takeUntil: { /** * Takes elements until the predicate matches. * * When `excludeLast` is `true`, the matching element is dropped. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.range(1, 5) * * const program = Effect.gen(function*() { * const inclusive = yield* stream.pipe( * Stream.takeUntil((n) => n % 3 === 0), * Stream.runCollect * ) * yield* Console.log(inclusive) * // Output: [ 1, 2, 3 ] * * const exclusive = yield* stream.pipe( * Stream.takeUntil((n) => n % 3 === 0, { excludeLast: true }), * Stream.runCollect * ) * yield* Console.log(exclusive) * // Output: [ 1, 2 ] * }) * ``` * * @since 2.0.0 * @category Filtering */ ( predicate: (a: NoInfer, n: number) => boolean, options?: { readonly excludeLast?: boolean | undefined } ): (self: Stream) => Stream /** * Takes elements until the predicate matches. * * When `excludeLast` is `true`, the matching element is dropped. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.range(1, 5) * * const program = Effect.gen(function*() { * const inclusive = yield* stream.pipe( * Stream.takeUntil((n) => n % 3 === 0), * Stream.runCollect * ) * yield* Console.log(inclusive) * // Output: [ 1, 2, 3 ] * * const exclusive = yield* stream.pipe( * Stream.takeUntil((n) => n % 3 === 0, { excludeLast: true }), * Stream.runCollect * ) * yield* Console.log(exclusive) * // Output: [ 1, 2 ] * }) * ``` * * @since 2.0.0 * @category Filtering */ ( self: Stream, predicate: (a: A, n: number) => boolean, options?: { readonly excludeLast?: boolean | undefined } ): Stream } = dual( (args) => isStream(args[0]), (self: Stream, predicate: (a: A, n: number) => boolean, options?: { readonly excludeLast?: boolean | undefined }): Stream => transformPull(self, (pull, _scope) => Effect.sync(() => { let i = 0 let done = false const pump: Pull.Pull, E, void, R> = Effect.flatMap( Effect.suspend(() => done ? Cause.done() : pull), (chunk) => { const index = chunk.findIndex((a) => predicate(a, i++)) if (index >= 0) { done = true const arr = chunk.slice(0, options?.excludeLast ? index : index + 1) return Arr.isReadonlyArrayNonEmpty(arr) ? Effect.succeed(arr) : Cause.done() } return Effect.succeed(chunk) } ) return pump })) ) /** * Effectful predicate version of `takeUntil`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.range(1, 5).pipe( * Stream.takeUntilEffect((n) => Effect.succeed(n % 3 === 0)), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Filtering */ export const takeUntilEffect: { /** * Effectful predicate version of `takeUntil`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.range(1, 5).pipe( * Stream.takeUntilEffect((n) => Effect.succeed(n % 3 === 0)), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Filtering */ ( predicate: (a: NoInfer, n: number) => Effect.Effect, options?: { readonly excludeLast?: boolean | undefined } ): (self: Stream) => Stream /** * Effectful predicate version of `takeUntil`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.range(1, 5).pipe( * Stream.takeUntilEffect((n) => Effect.succeed(n % 3 === 0)), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Filtering */ ( self: Stream, predicate: (a: A, n: number) => Effect.Effect, options?: { readonly excludeLast?: boolean | undefined } ): Stream } = dual((args) => isStream(args[0]), ( self: Stream, predicate: (a: A, n: number) => Effect.Effect, options?: { readonly excludeLast?: boolean | undefined } ): Stream => transformPull(self, (pull, _scope) => Effect.sync(() => { let i = 0 let done = false return Effect.gen(function*() { if (done) return yield* Cause.done() const chunk = yield* pull for (let j = 0; j < chunk.length; j++) { if (yield* predicate(chunk[j], i++)) { done = true const arr = chunk.slice(0, options?.excludeLast ? j : j + 1) return Arr.isReadonlyArrayNonEmpty(arr) ? arr : yield* Cause.done() } } return chunk }) }))) /** * Takes the longest initial prefix of elements that satisfy the predicate. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.range(1, 5).pipe( * Stream.takeWhile((n) => n % 3 !== 0) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2 ] * ``` * * @since 2.0.0 * @category Filtering */ export const takeWhile: { /** * Takes the longest initial prefix of elements that satisfy the predicate. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.range(1, 5).pipe( * Stream.takeWhile((n) => n % 3 !== 0) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2 ] * ``` * * @since 2.0.0 * @category Filtering */ (refinement: (a: NoInfer, n: number) => a is B): (self: Stream) => Stream /** * Takes the longest initial prefix of elements that satisfy the predicate. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.range(1, 5).pipe( * Stream.takeWhile((n) => n % 3 !== 0) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2 ] * ``` * * @since 2.0.0 * @category Filtering */ (predicate: (a: NoInfer, n: number) => boolean): (self: Stream) => Stream /** * Takes the longest initial prefix of elements that satisfy the predicate. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.range(1, 5).pipe( * Stream.takeWhile((n) => n % 3 !== 0) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2 ] * ``` * * @since 2.0.0 * @category Filtering */ (self: Stream, refinement: (a: NoInfer, n: number) => a is B): Stream /** * Takes the longest initial prefix of elements that satisfy the predicate. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.range(1, 5).pipe( * Stream.takeWhile((n) => n % 3 !== 0) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2 ] * ``` * * @since 2.0.0 * @category Filtering */ (self: Stream, predicate: (a: NoInfer, n: number) => boolean): Stream } = dual( 2, ( self: Stream, predicate: (a: A, n: number) => boolean ): Stream => transformPull(self, (pull, _scope) => Effect.sync(() => { let i = 0 let done = false const pump: Pull.Pull, E, void, R> = Effect.flatMap( Effect.suspend(() => done ? Cause.done() : pull), (chunk) => { const out: Array = [] for (let j = 0; j < chunk.length; j++) { if (!predicate(chunk[j], i++)) { done = true break } out.push(chunk[j]) } return Arr.isReadonlyArrayNonEmpty(out) ? Effect.succeed(out) : done ? Cause.done() : pump } ) return pump })) ) /** * Takes the longest initial prefix of elements that satisfy the filter. * * @since 4.0.0 * @category Filtering */ export const takeWhileFilter: { /** * Takes the longest initial prefix of elements that satisfy the filter. * * @since 4.0.0 * @category Filtering */ (f: Filter.Filter, B, X>): (self: Stream) => Stream /** * Takes the longest initial prefix of elements that satisfy the filter. * * @since 4.0.0 * @category Filtering */ (self: Stream, f: Filter.Filter, B, X>): Stream } = dual( 2, ( self: Stream, filter: Filter.Filter, B, X> ): Stream => transformPull(self, (pull, _scope) => Effect.sync(() => { let done = false const pump: Pull.Pull, E, void, R> = Effect.flatMap( Effect.suspend(() => done ? Cause.done() : pull), (chunk) => { const out: Array = [] for (let j = 0; j < chunk.length; j++) { const result = filter(chunk[j]) if (Result.isFailure(result)) { done = true break } out.push(result.success) } return Arr.isReadonlyArrayNonEmpty(out) ? Effect.succeed(out) : done ? Cause.done() : pump } ) return pump })) ) /** * Takes elements from the stream while the effectful predicate is `true`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.range(1, 5).pipe( * Stream.takeWhileEffect((n) => Effect.succeed(n % 3 !== 0)), * Stream.runCollect * ) * Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2 ] * ``` * * @since 2.0.0 * @category Filtering */ export const takeWhileEffect: { /** * Takes elements from the stream while the effectful predicate is `true`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.range(1, 5).pipe( * Stream.takeWhileEffect((n) => Effect.succeed(n % 3 !== 0)), * Stream.runCollect * ) * Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2 ] * ``` * * @since 2.0.0 * @category Filtering */ (predicate: (a: NoInfer, n: number) => Effect.Effect): (self: Stream) => Stream /** * Takes elements from the stream while the effectful predicate is `true`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.range(1, 5).pipe( * Stream.takeWhileEffect((n) => Effect.succeed(n % 3 !== 0)), * Stream.runCollect * ) * Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2 ] * ``` * * @since 2.0.0 * @category Filtering */ ( self: Stream, predicate: (a: NoInfer, n: number) => Effect.Effect ): Stream } = dual(2, ( self: Stream, predicate: (a: NoInfer, n: number) => Effect.Effect ) => takeUntilEffect(self, (a, n) => Effect.map( predicate(a, n), (b) => !b ), { excludeLast: true })) /** * Drops the first `n` elements from this stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3, 4, 5) * const result = Stream.drop(stream, 2) * * const program = Effect.gen(function*() { * const items = yield* Stream.runCollect(result) * yield* Console.log(items) * }) * * Effect.runPromise(program) * // Output: [ 3, 4, 5 ] * ``` * * @since 2.0.0 * @category Filtering */ export const drop: { /** * Drops the first `n` elements from this stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3, 4, 5) * const result = Stream.drop(stream, 2) * * const program = Effect.gen(function*() { * const items = yield* Stream.runCollect(result) * yield* Console.log(items) * }) * * Effect.runPromise(program) * // Output: [ 3, 4, 5 ] * ``` * * @since 2.0.0 * @category Filtering */ (n: number): (self: Stream) => Stream /** * Drops the first `n` elements from this stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3, 4, 5) * const result = Stream.drop(stream, 2) * * const program = Effect.gen(function*() { * const items = yield* Stream.runCollect(result) * yield* Console.log(items) * }) * * Effect.runPromise(program) * // Output: [ 3, 4, 5 ] * ``` * * @since 2.0.0 * @category Filtering */ (self: Stream, n: number): Stream } = dual( 2, (self: Stream, n: number): Stream => transformPull(self, (pull, _scope) => Effect.sync(() => { let dropped = 0 const pump: Pull.Pull, E, void, R> = pull.pipe( Effect.flatMap((chunk) => { if (dropped >= n) return Effect.succeed(chunk) dropped += chunk.length if (dropped <= n) return pump return Effect.succeed(chunk.slice(n - dropped) as Arr.NonEmptyArray) }) ) return pump })) ) /** * Drops elements until the specified predicate evaluates to `true`, then drops * that matching element. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3, 4, 5) * const result = Stream.dropUntil(stream, (n) => n >= 3) * * Effect.gen(function*() { * const output = yield* Stream.runCollect(result) * yield* Console.log(output) // Output: [ 4, 5 ] * }) * ``` * * @since 2.0.0 * @category Filtering */ export const dropUntil: { /** * Drops elements until the specified predicate evaluates to `true`, then drops * that matching element. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3, 4, 5) * const result = Stream.dropUntil(stream, (n) => n >= 3) * * Effect.gen(function*() { * const output = yield* Stream.runCollect(result) * yield* Console.log(output) // Output: [ 4, 5 ] * }) * ``` * * @since 2.0.0 * @category Filtering */ (predicate: (a: NoInfer, index: number) => boolean): (self: Stream) => Stream /** * Drops elements until the specified predicate evaluates to `true`, then drops * that matching element. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3, 4, 5) * const result = Stream.dropUntil(stream, (n) => n >= 3) * * Effect.gen(function*() { * const output = yield* Stream.runCollect(result) * yield* Console.log(output) // Output: [ 4, 5 ] * }) * ``` * * @since 2.0.0 * @category Filtering */ ( self: Stream, predicate: (a: NoInfer, index: number) => boolean ): Stream } = dual(2, ( self: Stream, predicate: (a: NoInfer, index: number) => boolean ): Stream => drop(dropWhile(self, (a, i) => !predicate(a, i)), 1)) /** * Drops all elements of the stream until the specified effectful predicate * evaluates to `true`. * * The first element that satisfies the predicate is also dropped. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.range(1, 5).pipe( * Stream.dropUntilEffect((n) => Effect.succeed(n % 3 === 0)), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 4, 5 ] * ``` * * @since 2.0.0 * @category Filtering */ export const dropUntilEffect: { /** * Drops all elements of the stream until the specified effectful predicate * evaluates to `true`. * * The first element that satisfies the predicate is also dropped. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.range(1, 5).pipe( * Stream.dropUntilEffect((n) => Effect.succeed(n % 3 === 0)), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 4, 5 ] * ``` * * @since 2.0.0 * @category Filtering */ ( predicate: (a: NoInfer, index: number) => Effect.Effect ): (self: Stream) => Stream /** * Drops all elements of the stream until the specified effectful predicate * evaluates to `true`. * * The first element that satisfies the predicate is also dropped. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.range(1, 5).pipe( * Stream.dropUntilEffect((n) => Effect.succeed(n % 3 === 0)), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 4, 5 ] * ``` * * @since 2.0.0 * @category Filtering */ ( self: Stream, predicate: (a: NoInfer, index: number) => Effect.Effect ): Stream } = dual(2, ( self: Stream, predicate: (a: NoInfer, index: number) => Effect.Effect ): Stream => drop( dropWhileEffect( self, (a, i) => Effect.map(predicate(a, i), (b) => !b) ), 1 )) /** * Drops elements from the stream while the specified predicate evaluates to `true`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.dropWhile((n) => n < 3), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 3, 4, 5 ] * ``` * * @since 2.0.0 * @category Filtering */ export const dropWhile: { /** * Drops elements from the stream while the specified predicate evaluates to `true`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.dropWhile((n) => n < 3), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 3, 4, 5 ] * ``` * * @since 2.0.0 * @category Filtering */ (predicate: (a: NoInfer, index: number) => boolean): (self: Stream) => Stream /** * Drops elements from the stream while the specified predicate evaluates to `true`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.dropWhile((n) => n < 3), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 3, 4, 5 ] * ``` * * @since 2.0.0 * @category Filtering */ ( self: Stream, predicate: (a: NoInfer, index: number) => boolean ): Stream } = dual(2, ( self: Stream, predicate: (a: A, index: number) => boolean ): Stream => transformPull(self, (pull, _scope) => Effect.sync(() => { let dropping = true let index = 0 const filtered: Pull.Pull, E> = Effect.flatMap(pull, (arr) => { const found = arr.findIndex((a) => !predicate(a, index++)) if (found === -1) return filtered dropping = false return Effect.succeed(arr.slice(found) as Arr.NonEmptyArray) }) return Effect.suspend(() => dropping ? filtered : pull) }))) /** * Drops elements while the filter succeeds. * * @since 4.0.0 * @category Filtering */ export const dropWhileFilter: { /** * Drops elements while the filter succeeds. * * @since 4.0.0 * @category Filtering */ (filter: Filter.Filter, B, X>): (self: Stream) => Stream /** * Drops elements while the filter succeeds. * * @since 4.0.0 * @category Filtering */ (self: Stream, filter: Filter.Filter, B, X>): Stream } = dual(2, ( self: Stream, filter: Filter.Filter, B, X> ): Stream => transformPull(self, (pull, _scope) => Effect.sync(() => { let dropping = true const filtered: Pull.Pull, E> = Effect.flatMap(pull, (arr) => { const found = arr.findIndex((a) => Result.isFailure(filter(a))) if (found === -1) return filtered dropping = false return Effect.succeed(arr.slice(found) as Arr.NonEmptyArray) }) return Effect.suspend(() => dropping ? filtered : pull) }))) /** * Drops elements while the specified effectful predicate evaluates to `true`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.dropWhileEffect((n) => Effect.succeed(n < 3)), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 3, 4, 5 ] * ``` * * @since 2.0.0 * @category Filtering */ export const dropWhileEffect: { /** * Drops elements while the specified effectful predicate evaluates to `true`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.dropWhileEffect((n) => Effect.succeed(n < 3)), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 3, 4, 5 ] * ``` * * @since 2.0.0 * @category Filtering */ ( predicate: (a: NoInfer, index: number) => Effect.Effect ): (self: Stream) => Stream /** * Drops elements while the specified effectful predicate evaluates to `true`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.dropWhileEffect((n) => Effect.succeed(n < 3)), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 3, 4, 5 ] * ``` * * @since 2.0.0 * @category Filtering */ ( self: Stream, predicate: (a: A, index: number) => Effect.Effect ): Stream } = dual(2, ( self: Stream, predicate: (a: NoInfer, index: number) => Effect.Effect ): Stream => transformPull(self, (pull, _scope) => Effect.sync(() => { let dropping = true let index = 0 const filtered: Pull.Pull, E | E2, void, R2> = Effect.gen(function*() { while (true) { const arr = yield* pull for (let i = 0; i < arr.length; i++) { const drop = yield* predicate(arr[i], index++) if (drop) continue dropping = false return arr.slice(i) as Arr.NonEmptyArray } } }) return Effect.suspend((): Pull.Pull, E | E2, void, R | R2> => dropping ? filtered : pull ) }))) /** * Drops the last specified number of elements from this stream. * * Keeps the last `n` elements in memory to drop them on completion. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.dropRight(2), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Filtering */ export const dropRight: { /** * Drops the last specified number of elements from this stream. * * Keeps the last `n` elements in memory to drop them on completion. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.dropRight(2), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Filtering */ (n: number): (self: Stream) => Stream /** * Drops the last specified number of elements from this stream. * * Keeps the last `n` elements in memory to drop them on completion. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.dropRight(2), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Filtering */ (self: Stream, n: number): Stream } = dual( 2, (self: Stream, n: number): Stream => { if (n <= 0) return self return transformPull(self, (pull, _scope) => Effect.sync(() => { const list = MutableList.make() const emit: Pull.Pull, E> = Effect.flatMap(pull, (arr) => { MutableList.appendAllUnsafe(list, arr) const toTake = list.length - n const items = MutableList.takeN(list, toTake) return Arr.isArrayNonEmpty(items) ? Effect.succeed(items) : emit }) return emit })); } ) /** * Exposes the underlying chunks as a stream of non-empty arrays. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const chunks = yield* Stream.make(1, 2, 3, 4).pipe( * Stream.rechunk(2), * Stream.chunks, * Stream.runCollect * ) * yield* Console.log(chunks) * }) * * Effect.runPromise(program) * // Output: [ [ 1, 2 ], [ 3, 4 ] ] * ``` * * @since 2.0.0 * @category Grouping */ export const chunks = (self: Stream): Stream, E, R> => self.channel.pipe( Channel.map(Arr.of), fromChannel ) /** * Re-chunks the stream into arrays of the specified size, preserving element order. * * The size is clamped to at least 1. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.rechunk(2), * Stream.chunks, * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ [ 1, 2 ], [ 3, 4 ], [ 5 ] ] * ``` * * @since 2.0.0 * @category Grouping */ export const rechunk: { /** * Re-chunks the stream into arrays of the specified size, preserving element order. * * The size is clamped to at least 1. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.rechunk(2), * Stream.chunks, * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ [ 1, 2 ], [ 3, 4 ], [ 5 ] ] * ``` * * @since 2.0.0 * @category Grouping */ (size: number): (self: Stream) => Stream /** * Re-chunks the stream into arrays of the specified size, preserving element order. * * The size is clamped to at least 1. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.rechunk(2), * Stream.chunks, * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ [ 1, 2 ], [ 3, 4 ], [ 5 ] ] * ``` * * @since 2.0.0 * @category Grouping */ (self: Stream, size: number): Stream } = dual(2, (self: Stream, target: number): Stream => { target = Math.max(1, target) return transformPull(self, (pull, _scope) => Effect.sync(() => { let chunk = Arr.empty() as Arr.NonEmptyArray let index = 0 let current: Arr.NonEmptyReadonlyArray | undefined let done = false return Effect.suspend(function loop(): Pull.Pull, E, void, R> { if (done) return Cause.done() else if (current === undefined) { return Effect.flatMap(pull, (arr) => { if (chunk.length === 0 && arr.length === target) { return Effect.succeed(arr) } else if (chunk.length + arr.length < target) { chunk.push(...arr) return loop() } current = arr return loop() }) } for (; index < current.length;) { chunk.push(current[index++]) if (chunk.length === target) { const result = chunk chunk = [] as any return Effect.succeed(result) } } index = 0 current = undefined return loop() }).pipe( Pull.catchDone(() => { if (chunk.length === 0) return Cause.done() const result = chunk done = true chunk = [] as any return Effect.succeed(result) }) ) })); }) /** * Emits a sliding window of `n` elements. * * @example * ```ts * import { Console, Effect, Stream, pipe } from "effect" * * Effect.gen(function*() { * const result = yield* pipe( * Stream.make(1, 2, 3, 4, 5), * Stream.sliding(2), * Stream.runCollect * ) * yield* Console.log(result) * }) * // Output: [ [1, 2], [2, 3], [3, 4], [4, 5] ] * ``` * * @since 2.0.0 * @category Grouping */ export const sliding: { /** * Emits a sliding window of `n` elements. * * @example * ```ts * import { Console, Effect, Stream, pipe } from "effect" * * Effect.gen(function*() { * const result = yield* pipe( * Stream.make(1, 2, 3, 4, 5), * Stream.sliding(2), * Stream.runCollect * ) * yield* Console.log(result) * }) * // Output: [ [1, 2], [2, 3], [3, 4], [4, 5] ] * ``` * * @since 2.0.0 * @category Grouping */ (chunkSize: number): (self: Stream) => Stream, E, R> /** * Emits a sliding window of `n` elements. * * @example * ```ts * import { Console, Effect, Stream, pipe } from "effect" * * Effect.gen(function*() { * const result = yield* pipe( * Stream.make(1, 2, 3, 4, 5), * Stream.sliding(2), * Stream.runCollect * ) * yield* Console.log(result) * }) * // Output: [ [1, 2], [2, 3], [3, 4], [4, 5] ] * ``` * * @since 2.0.0 * @category Grouping */ (self: Stream, chunkSize: number): Stream, E, R> } = dual( 2, (self: Stream, chunkSize: number): Stream, E, R> => slidingSize(self, chunkSize, 1) ) /** * Emits sliding windows of `chunkSize` elements, advancing by `stepSize`. * * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const chunks = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.slidingSize(3, 2), * Stream.runCollect * ) * yield* Console.log(chunks) * }) * * Effect.runPromise(program) * // Output: [ [ 1, 2, 3 ], [ 3, 4, 5 ] ] * ``` * * @since 2.0.0 * @category Grouping */ export const slidingSize: { /** * Emits sliding windows of `chunkSize` elements, advancing by `stepSize`. * * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const chunks = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.slidingSize(3, 2), * Stream.runCollect * ) * yield* Console.log(chunks) * }) * * Effect.runPromise(program) * // Output: [ [ 1, 2, 3 ], [ 3, 4, 5 ] ] * ``` * * @since 2.0.0 * @category Grouping */ (chunkSize: number, stepSize: number): (self: Stream) => Stream, E, R> /** * Emits sliding windows of `chunkSize` elements, advancing by `stepSize`. * * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const chunks = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.slidingSize(3, 2), * Stream.runCollect * ) * yield* Console.log(chunks) * }) * * Effect.runPromise(program) * // Output: [ [ 1, 2, 3 ], [ 3, 4, 5 ] ] * ``` * * @since 2.0.0 * @category Grouping */ (self: Stream, chunkSize: number, stepSize: number): Stream, E, R> } = dual( 3, (self: Stream, chunkSize: number, stepSize: number): Stream, E, R> => transformPull(self, (upstream, _scope) => Effect.sync(() => { let cause: Cause.Cause | null = null const list = MutableList.make() let emitted = false const pull: Pull.Pull< Arr.NonEmptyReadonlyArray>, E | Cause.Done > = Effect.matchCauseEffect(upstream, { onSuccess(arr) { MutableList.appendAllUnsafe(list, arr) if (list.length < chunkSize) return pull emitted = true const chunks = [] as any as Arr.NonEmptyArray> while (list.length >= chunkSize) { if (chunkSize === stepSize) { chunks.push(MutableList.takeN(list, chunkSize) as any) } else { chunks.push(MutableList.toArrayN(list, chunkSize) as any) if (chunkSize === 1) { MutableList.take(list) } else { MutableList.takeNVoid(list, stepSize) } } } return Effect.succeed(chunks) }, onFailure(cause_) { if (emitted) MutableList.takeNVoid(list, chunkSize - stepSize) if (list.length === 0) return Effect.failCause(cause_) cause = cause_ return Effect.succeed(Arr.of(MutableList.takeAll(list) as any)) } }) return Effect.suspend(() => cause ? Effect.failCause(cause) : pull) })) ) /** * Splits the stream into non-empty groups whenever the predicate matches. * * Matching elements act as delimiters and are not included in the output. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.range(0, 9).pipe( * Stream.split((n) => n % 4 === 0), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ [1, 2, 3], [5, 6, 7], [9] ] * ``` * * @since 2.0.0 * @category Grouping */ export const split: { /** * Splits the stream into non-empty groups whenever the predicate matches. * * Matching elements act as delimiters and are not included in the output. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.range(0, 9).pipe( * Stream.split((n) => n % 4 === 0), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ [1, 2, 3], [5, 6, 7], [9] ] * ``` * * @since 2.0.0 * @category Grouping */ (refinement: Refinement, B>): (self: Stream) => Stream>, E, R> /** * Splits the stream into non-empty groups whenever the predicate matches. * * Matching elements act as delimiters and are not included in the output. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.range(0, 9).pipe( * Stream.split((n) => n % 4 === 0), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ [1, 2, 3], [5, 6, 7], [9] ] * ``` * * @since 2.0.0 * @category Grouping */ (predicate: Predicate>): (self: Stream) => Stream, E, R> /** * Splits the stream into non-empty groups whenever the predicate matches. * * Matching elements act as delimiters and are not included in the output. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.range(0, 9).pipe( * Stream.split((n) => n % 4 === 0), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ [1, 2, 3], [5, 6, 7], [9] ] * ``` * * @since 2.0.0 * @category Grouping */ (self: Stream, refinement: Refinement): Stream>, E, R> /** * Splits the stream into non-empty groups whenever the predicate matches. * * Matching elements act as delimiters and are not included in the output. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.range(0, 9).pipe( * Stream.split((n) => n % 4 === 0), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ [1, 2, 3], [5, 6, 7], [9] ] * ``` * * @since 2.0.0 * @category Grouping */ (self: Stream, predicate: Predicate): Stream, E, R> } = dual(2, ( self: Stream, predicate: Predicate> ): Stream, E, R> => mapAccumArray(self, Arr.empty, (acc, arr) => { const out = Arr.empty n % 4 === 0), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ [1, 2, 3], [5, 6, 7], [9] ] * ``` * * @since 2.0.0 * @category Grouping */ Arr.NonEmptyReadonlyArray>() for (let i = 0; i < arr.length; i++) { if (predicate(arr[i])) { if (Arr.isArrayNonEmpty(acc)) { out.push(acc) acc = [] } } else { acc.push(arr[i]) } } return [acc, out] }, { onHalt(arr) { return Arr.isArrayNonEmpty(arr) ? Arr.of(arr) : emptyArr } })) /** * Combines elements from this stream and the specified stream by repeatedly * applying a stateful function that can pull from either side. * * Where possible, prefer `Stream.combineArray` for a more efficient * implementation. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.combine( * Stream.make("A", "B", "C"), * Stream.make(1, 2, 3), * () => true, * (takeLeft, pullLeft, pullRight) => * takeLeft * ? Effect.map(pullLeft, (value) => [`L:${value}`, false] as const) * : Effect.map(pullRight, (value) => [`R:${value}`, true] as const) * ) * * const program = Effect.gen(function*() { * const output = yield* Stream.runCollect(stream) * yield* Console.log(output) * }) * * Effect.runPromise(program) * // Output: [ "L:A", "R:1", "L:B", "R:2", "L:C", "R:3" ] * ``` * * @since 2.0.0 * @category Merging */ export const combine: { /** * Combines elements from this stream and the specified stream by repeatedly * applying a stateful function that can pull from either side. * * Where possible, prefer `Stream.combineArray` for a more efficient * implementation. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.combine( * Stream.make("A", "B", "C"), * Stream.make(1, 2, 3), * () => true, * (takeLeft, pullLeft, pullRight) => * takeLeft * ? Effect.map(pullLeft, (value) => [`L:${value}`, false] as const) * : Effect.map(pullRight, (value) => [`R:${value}`, true] as const) * ) * * const program = Effect.gen(function*() { * const output = yield* Stream.runCollect(stream) * yield* Console.log(output) * }) * * Effect.runPromise(program) * // Output: [ "L:A", "R:1", "L:B", "R:2", "L:C", "R:3" ] * ``` * * @since 2.0.0 * @category Merging */ ( that: Stream, s: LazyArg, f: ( s: S, pullLeft: Pull.Pull, pullRight: Pull.Pull ) => Effect.Effect ): (self: Stream) => Stream /** * Combines elements from this stream and the specified stream by repeatedly * applying a stateful function that can pull from either side. * * Where possible, prefer `Stream.combineArray` for a more efficient * implementation. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.combine( * Stream.make("A", "B", "C"), * Stream.make(1, 2, 3), * () => true, * (takeLeft, pullLeft, pullRight) => * takeLeft * ? Effect.map(pullLeft, (value) => [`L:${value}`, false] as const) * : Effect.map(pullRight, (value) => [`R:${value}`, true] as const) * ) * * const program = Effect.gen(function*() { * const output = yield* Stream.runCollect(stream) * yield* Console.log(output) * }) * * Effect.runPromise(program) * // Output: [ "L:A", "R:1", "L:B", "R:2", "L:C", "R:3" ] * ``` * * @since 2.0.0 * @category Merging */ ( self: Stream, that: Stream, s: LazyArg, f: ( s: S, pullLeft: Pull.Pull, pullRight: Pull.Pull ) => Effect.Effect ): Stream } = dual(4, ( self: Stream, that: Stream, s: LazyArg, f: ( s: S, pullLeft: Pull.Pull, pullRight: Pull.Pull ) => Effect.Effect ): Stream => Channel.combine( Channel.flattenArray(self.channel), Channel.flattenArray(that.channel), s, f ).pipe( Channel.map(Arr.of), fromChannel )) /** * Combines the arrays (chunks) from this stream and the specified stream by * repeatedly applying the function `f` to extract an array using both sides and * conceptually "offer" it to the destination stream. `f` can maintain some * internal state to control the combining process, with the initial state * being specified by `s`. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.combineChunks` * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2).pipe( * Stream.combineArray( * Stream.make(10, 20), * () => true, * (useLeft, pullLeft, pullRight) => * Effect.gen(function*() { * const array = useLeft ? yield* pullLeft : yield* pullRight * return [array, !useLeft] as const * }) * ) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 10, 20 ] * ``` * * @since 2.0.0 * @category Sequencing */ export const combineArray: { /** * Combines the arrays (chunks) from this stream and the specified stream by * repeatedly applying the function `f` to extract an array using both sides and * conceptually "offer" it to the destination stream. `f` can maintain some * internal state to control the combining process, with the initial state * being specified by `s`. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.combineChunks` * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2).pipe( * Stream.combineArray( * Stream.make(10, 20), * () => true, * (useLeft, pullLeft, pullRight) => * Effect.gen(function*() { * const array = useLeft ? yield* pullLeft : yield* pullRight * return [array, !useLeft] as const * }) * ) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 10, 20 ] * ``` * * @since 2.0.0 * @category Sequencing */ ( that: Stream, s: LazyArg, f: ( s: S, pullLeft: Pull.Pull, E, void>, pullRight: Pull.Pull, E2, void> ) => Effect.Effect, S], E3, R3> ): (self: Stream) => Stream, R2 | R3 | R> /** * Combines the arrays (chunks) from this stream and the specified stream by * repeatedly applying the function `f` to extract an array using both sides and * conceptually "offer" it to the destination stream. `f` can maintain some * internal state to control the combining process, with the initial state * being specified by `s`. * * **Previously Known As** * * This API replaces the following from Effect 3.x: * * - `Stream.combineChunks` * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2).pipe( * Stream.combineArray( * Stream.make(10, 20), * () => true, * (useLeft, pullLeft, pullRight) => * Effect.gen(function*() { * const array = useLeft ? yield* pullLeft : yield* pullRight * return [array, !useLeft] as const * }) * ) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 10, 20 ] * ``` * * @since 2.0.0 * @category Sequencing */ ( self: Stream, that: Stream, s: LazyArg, f: ( s: S, pullLeft: Pull.Pull, E, void>, pullRight: Pull.Pull, E2, void> ) => Effect.Effect, S], E3, R3> ): Stream, R | R2 | R3> } = dual(4, ( self: Stream, that: Stream, s: LazyArg, f: ( s: S, pullLeft: Pull.Pull, E, void>, pullRight: Pull.Pull, E2, void> ) => Effect.Effect, S], E3, R3> ): Stream, R | R2 | R3> => fromChannel(Channel.combine( self.channel, that.channel, s, f ))) /** * Statefully maps elements, emitting zero or more outputs per input. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const totals = yield* Stream.make(0, 1, 2, 3, 4, 5, 6).pipe( * Stream.mapAccum(() => 0, (total, n) => { * const next = total + n * return [next, [next]] as const * }), * Stream.runCollect * ) * * yield* Console.log(totals) * }) * * Effect.runPromise(program) * // Output: [ 0, 1, 3, 6, 10, 15, 21 ] * ``` * * @since 2.0.0 * @category Mapping */ export const mapAccum: { /** * Statefully maps elements, emitting zero or more outputs per input. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const totals = yield* Stream.make(0, 1, 2, 3, 4, 5, 6).pipe( * Stream.mapAccum(() => 0, (total, n) => { * const next = total + n * return [next, [next]] as const * }), * Stream.runCollect * ) * * yield* Console.log(totals) * }) * * Effect.runPromise(program) * // Output: [ 0, 1, 3, 6, 10, 15, 21 ] * ``` * * @since 2.0.0 * @category Mapping */ ( initial: LazyArg, f: (s: S, a: A) => readonly [state: S, values: ReadonlyArray], options?: { readonly onHalt?: ((state: S) => ReadonlyArray) | undefined } ): (self: Stream) => Stream /** * Statefully maps elements, emitting zero or more outputs per input. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const totals = yield* Stream.make(0, 1, 2, 3, 4, 5, 6).pipe( * Stream.mapAccum(() => 0, (total, n) => { * const next = total + n * return [next, [next]] as const * }), * Stream.runCollect * ) * * yield* Console.log(totals) * }) * * Effect.runPromise(program) * // Output: [ 0, 1, 3, 6, 10, 15, 21 ] * ``` * * @since 2.0.0 * @category Mapping */ ( self: Stream, initial: LazyArg, f: (s: S, a: A) => readonly [state: S, values: ReadonlyArray], options?: { readonly onHalt?: ((state: S) => ReadonlyArray) | undefined } ): Stream } = dual((args) => isStream(args[0]), ( self: Stream, initial: LazyArg, f: (s: S, a: A) => readonly [state: S, values: ReadonlyArray], options?: { readonly onHalt?: ((state: S) => ReadonlyArray) | undefined } ): Stream => fromChannel(Channel.mapAccum( self.channel, initial, (state, arr) => { const acc = Arr.empty 0, (total, n) => { * const next = total + n * return [next, [next]] as const * }), * Stream.runCollect * ) * * yield* Console.log(totals) * }) * * Effect.runPromise(program) * // Output: [ 0, 1, 3, 6, 10, 15, 21 ] * ``` * * @since 2.0.0 * @category Mapping */ B>() for (let index = 0; index < arr.length; index++) { const [newState, values] = f(state, arr[index]) state = newState acc.push(...values) } return [state, Arr.isArrayNonEmpty(acc) ? Arr.of(acc) : emptyArr] }, options?.onHalt ? { onHalt(state) { const arr = options.onHalt!(state) return Arr.isReadonlyArrayNonEmpty(arr) ? Arr.of(arr) : emptyArr } } : undefined ))) /** * Statefully maps over non-empty chunk arrays, emitting zero or more values per chunk. * * The mapping function runs once per chunk and the state is threaded across chunks. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const output = yield* Stream.make(1, 2, 3, 4, 5, 6).pipe( * Stream.rechunk(2), * Stream.mapAccumArray(() => 0, (sum: number, chunk) => { * const next = chunk.reduce((acc, n) => acc + n, sum) * return [next, [next]] * }), * Stream.runCollect * ) * yield* Console.log(output) * }) * * Effect.runPromise(program) * // Output: [ 3, 10, 21 ] * ``` * * @since 2.0.0 * @category Mapping */ export const mapAccumArray: { /** * Statefully maps over non-empty chunk arrays, emitting zero or more values per chunk. * * The mapping function runs once per chunk and the state is threaded across chunks. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const output = yield* Stream.make(1, 2, 3, 4, 5, 6).pipe( * Stream.rechunk(2), * Stream.mapAccumArray(() => 0, (sum: number, chunk) => { * const next = chunk.reduce((acc, n) => acc + n, sum) * return [next, [next]] * }), * Stream.runCollect * ) * yield* Console.log(output) * }) * * Effect.runPromise(program) * // Output: [ 3, 10, 21 ] * ``` * * @since 2.0.0 * @category Mapping */ ( initial: LazyArg, f: (s: S, a: Arr.NonEmptyReadonlyArray) => readonly [state: S, values: ReadonlyArray], options?: { readonly onHalt?: ((state: S) => ReadonlyArray) | undefined } ): (self: Stream) => Stream /** * Statefully maps over non-empty chunk arrays, emitting zero or more values per chunk. * * The mapping function runs once per chunk and the state is threaded across chunks. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const output = yield* Stream.make(1, 2, 3, 4, 5, 6).pipe( * Stream.rechunk(2), * Stream.mapAccumArray(() => 0, (sum: number, chunk) => { * const next = chunk.reduce((acc, n) => acc + n, sum) * return [next, [next]] * }), * Stream.runCollect * ) * yield* Console.log(output) * }) * * Effect.runPromise(program) * // Output: [ 3, 10, 21 ] * ``` * * @since 2.0.0 * @category Mapping */ ( self: Stream, initial: LazyArg, f: (s: S, a: Arr.NonEmptyReadonlyArray) => readonly [state: S, values: ReadonlyArray], options?: { readonly onHalt?: ((state: S) => Array) | undefined } ): Stream } = dual((args) => isStream(args[0]), ( self: Stream, initial: LazyArg, f: (s: S, a: Arr.NonEmptyReadonlyArray) => readonly [state: S, values: ReadonlyArray], options?: { readonly onHalt?: ((state: S) => ReadonlyArray) | undefined } ): Stream => fromChannel(Channel.mapAccum( self.channel, initial, (state, arr) => { const [newState, values] = f(state, arr) state = newState return [state, Arr.isReadonlyArrayNonEmpty(values) ? Arr.of(values) : emptyArr] }, options?.onHalt ? { onHalt(state) { const arr = options.onHalt!(state) return Arr.isReadonlyArrayNonEmpty(arr) ? Arr.of(arr) : emptyArr } } : undefined ))) const emptyArr = Arr.empty() /** * Statefully and effectfully maps over the elements of this stream to produce new elements. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.make(1, 1, 1).pipe( * Stream.mapAccumEffect(() => 0, (total, n) => * Effect.succeed([total + n, [total + n]]) * ), * Stream.runCollect * ) * * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Mapping */ export const mapAccumEffect: { /** * Statefully and effectfully maps over the elements of this stream to produce new elements. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.make(1, 1, 1).pipe( * Stream.mapAccumEffect(() => 0, (total, n) => * Effect.succeed([total + n, [total + n]]) * ), * Stream.runCollect * ) * * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Mapping */ ( initial: LazyArg, f: (s: S, a: A) => Effect.Effect], E2, R2>, options?: { readonly onHalt?: ((state: S) => ReadonlyArray) | undefined } ): (self: Stream) => Stream /** * Statefully and effectfully maps over the elements of this stream to produce new elements. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const result = yield* Stream.make(1, 1, 1).pipe( * Stream.mapAccumEffect(() => 0, (total, n) => * Effect.succeed([total + n, [total + n]]) * ), * Stream.runCollect * ) * * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Mapping */ ( self: Stream, initial: LazyArg, f: (s: S, a: A) => Effect.Effect], E2, R2>, options?: { readonly onHalt?: ((state: S) => ReadonlyArray) | undefined } ): Stream } = dual((args) => isStream(args[0]), ( self: Stream, initial: LazyArg, f: (s: S, a: A) => Effect.Effect], E2, R2>, options?: { readonly onHalt?: ((state: S) => ReadonlyArray) | undefined } ): Stream => self.channel.pipe( Channel.flattenArray, Channel.mapAccum( initial, (state, a) => Effect.map( f(state, a), ([state, values]) => [ state, Arr.isReadonlyArrayNonEmpty(values) ? Arr.of(values) : Arr.empty 0, (total, n) => * Effect.succeed([total + n, [total + n]]) * ), * Stream.runCollect * ) * * yield* Console.log(result) * }) * * Effect.runPromise(program) * // Output: [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Mapping */ Arr.NonEmptyReadonlyArray>() ] ), options?.onHalt ? { onHalt(state) { const arr = options.onHalt!(state) return Arr.isReadonlyArrayNonEmpty(arr) ? Arr.of(arr) : emptyArr } } : undefined ), fromChannel )) /** * Statefully and effectfully maps over chunks of this stream to emit new values. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const totals = yield* Stream.make(1, 2, 3, 4).pipe( * Stream.rechunk(2), * Stream.mapAccumArrayEffect(() => 0, (total, chunk) => * Effect.gen(function*() { * const next = chunk.reduce((sum, value) => sum + value, total) * return [next, [next]] as const * }) * ), * Stream.runCollect * ) * yield* Console.log(totals) * }) * * Effect.runPromise(program) * // Output: [ 3, 10 ] * ``` * * @since 2.0.0 * @category Mapping */ export const mapAccumArrayEffect: { /** * Statefully and effectfully maps over chunks of this stream to emit new values. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const totals = yield* Stream.make(1, 2, 3, 4).pipe( * Stream.rechunk(2), * Stream.mapAccumArrayEffect(() => 0, (total, chunk) => * Effect.gen(function*() { * const next = chunk.reduce((sum, value) => sum + value, total) * return [next, [next]] as const * }) * ), * Stream.runCollect * ) * yield* Console.log(totals) * }) * * Effect.runPromise(program) * // Output: [ 3, 10 ] * ``` * * @since 2.0.0 * @category Mapping */ ( initial: LazyArg, f: (s: S, a: Arr.NonEmptyReadonlyArray) => Effect.Effect], E2, R2>, options?: { readonly onHalt?: ((state: S) => ReadonlyArray) | undefined } ): (self: Stream) => Stream /** * Statefully and effectfully maps over chunks of this stream to emit new values. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const totals = yield* Stream.make(1, 2, 3, 4).pipe( * Stream.rechunk(2), * Stream.mapAccumArrayEffect(() => 0, (total, chunk) => * Effect.gen(function*() { * const next = chunk.reduce((sum, value) => sum + value, total) * return [next, [next]] as const * }) * ), * Stream.runCollect * ) * yield* Console.log(totals) * }) * * Effect.runPromise(program) * // Output: [ 3, 10 ] * ``` * * @since 2.0.0 * @category Mapping */ ( self: Stream, initial: LazyArg, f: (s: S, a: Arr.NonEmptyReadonlyArray) => Effect.Effect], E2, R2>, options?: { readonly onHalt?: ((state: S) => ReadonlyArray) | undefined } ): Stream } = dual((args) => isStream(args), ( self: Stream, initial: LazyArg, f: (s: S, a: Arr.NonEmptyReadonlyArray) => Effect.Effect], E2, R2>, options?: { readonly onHalt?: ((state: S) => ReadonlyArray) | undefined } ): Stream => self.channel.pipe( Channel.mapAccum( initial, (state, a) => Effect.map( f(state, a), ([state, values]) => [ state, Arr.isReadonlyArrayNonEmpty(values) ? Arr.of(values) : emptyArr ] ), options?.onHalt ? { onHalt(state) { const arr = options.onHalt!(state) return Arr.isReadonlyArrayNonEmpty(arr) ? Arr.of(arr) : emptyArr } } : undefined ), fromChannel )) /** * Accumulates state across the stream, emitting the initial state and each updated state. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.scan(0, (acc, n) => acc + n), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 0, 1, 3, 6 ] * ``` * * @since 2.0.0 * @category Accumulation */ export const scan: { /** * Accumulates state across the stream, emitting the initial state and each updated state. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.scan(0, (acc, n) => acc + n), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 0, 1, 3, 6 ] * ``` * * @since 2.0.0 * @category Accumulation */ (initial: S, f: (s: S, a: A) => S): (self: Stream) => Stream /** * Accumulates state across the stream, emitting the initial state and each updated state. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.scan(0, (acc, n) => acc + n), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 0, 1, 3, 6 ] * ``` * * @since 2.0.0 * @category Accumulation */ (self: Stream, initial: S, f: (s: S, a: A) => S): Stream } = dual(3, ( self: Stream, initial: S, f: (s: S, a: A) => S ): Stream => suspend(() => { let isFirst = true return fromChannel(Channel.mapAccum(self.channel, constant(initial), (state, arr) => { const states = Arr.empty acc + n), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ 0, 1, 3, 6 ] * ``` * * @since 2.0.0 * @category Accumulation */ S>() as Arr.NonEmptyArray if (isFirst) { isFirst = false states.push(state) } for (let index = 0; index < arr.length; index++) { state = f(state, arr[index]) states.push(state) } return [state, Arr.of(states)] })); })) /** * Effectfully accumulates state and emits the initial state plus each accumulated state. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const states = yield* Stream.make(1, 2, 3).pipe( * Stream.scanEffect(0, (sum, n) => Effect.succeed(sum + n)), * Stream.runCollect * ) * yield* Console.log(states) * // Output: [ 0, 1, 3, 6 ] * }) * ``` * * @since 2.0.0 * @category Accumulation */ export const scanEffect: { /** * Effectfully accumulates state and emits the initial state plus each accumulated state. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const states = yield* Stream.make(1, 2, 3).pipe( * Stream.scanEffect(0, (sum, n) => Effect.succeed(sum + n)), * Stream.runCollect * ) * yield* Console.log(states) * // Output: [ 0, 1, 3, 6 ] * }) * ``` * * @since 2.0.0 * @category Accumulation */ (initial: S, f: (s: S, a: A) => Effect.Effect): (self: Stream) => Stream /** * Effectfully accumulates state and emits the initial state plus each accumulated state. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const states = yield* Stream.make(1, 2, 3).pipe( * Stream.scanEffect(0, (sum, n) => Effect.succeed(sum + n)), * Stream.runCollect * ) * yield* Console.log(states) * // Output: [ 0, 1, 3, 6 ] * }) * ``` * * @since 2.0.0 * @category Accumulation */ ( self: Stream, initial: S, f: (s: S, a: A) => Effect.Effect ): Stream } = dual(3, ( self: Stream, initial: S, f: (s: S, a: A) => Effect.Effect ): Stream => self.channel.pipe( Channel.flattenArray, Channel.scanEffect(initial, f), Channel.map(Arr.of), fromChannel )) /** * Drops earlier elements within the debounce window and emits only the latest element after the pause. * * @example * ```ts * import { Console, Duration, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3).pipe( * Stream.concat(Stream.fromEffect(Effect.sleep(Duration.millis(50)).pipe(Effect.as(4)))), * Stream.concat(Stream.make(5)), * Stream.debounce(Duration.millis(30)) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * // Output: [ 3, 5 ] * }) * ``` * * @since 2.0.0 * @category Rate Limiting */ export const debounce: { /** * Drops earlier elements within the debounce window and emits only the latest element after the pause. * * @example * ```ts * import { Console, Duration, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3).pipe( * Stream.concat(Stream.fromEffect(Effect.sleep(Duration.millis(50)).pipe(Effect.as(4)))), * Stream.concat(Stream.make(5)), * Stream.debounce(Duration.millis(30)) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * // Output: [ 3, 5 ] * }) * ``` * * @since 2.0.0 * @category Rate Limiting */ (duration: Duration.Input): (self: Stream) => Stream /** * Drops earlier elements within the debounce window and emits only the latest element after the pause. * * @example * ```ts * import { Console, Duration, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3).pipe( * Stream.concat(Stream.fromEffect(Effect.sleep(Duration.millis(50)).pipe(Effect.as(4)))), * Stream.concat(Stream.make(5)), * Stream.debounce(Duration.millis(30)) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * // Output: [ 3, 5 ] * }) * ``` * * @since 2.0.0 * @category Rate Limiting */ (self: Stream, duration: Duration.Input): Stream } = dual( 2, (self: Stream, duration: Duration.Input): Stream => transformPull( self, Effect.fnUntraced(function*(pull, scope) { const clock = yield* Clock const durationMs = Duration.toMillis(Duration.fromInputUnsafe(duration)) let lastArr: Arr.NonEmptyReadonlyArray | undefined let cause: Cause.Cause | undefined let emitAtMs = Infinity const pullLatch = Latch.makeUnsafe() const emitLatch = Latch.makeUnsafe() const endLatch = Latch.makeUnsafe() yield* pull.pipe( pullLatch.whenOpen, Effect.flatMap((arr) => { emitLatch.openUnsafe() lastArr = arr emitAtMs = clock.currentTimeMillisUnsafe() + durationMs return Effect.void }), Effect.forever({ disableYield: true }), Effect.onError((cause_) => { cause = cause_ emitAtMs = clock.currentTimeMillisUnsafe() emitLatch.openUnsafe() endLatch.openUnsafe() return Effect.void }), Effect.forkIn(scope) ) const sleepLoop = Effect.suspend(function loop(): Pull.Pull, E, void, R> { const now = clock.currentTimeMillisUnsafe() const timeMs = emitAtMs < now ? durationMs : Math.min(durationMs, emitAtMs - now) return Effect.flatMap(Effect.raceFirst(Effect.sleep(timeMs), endLatch.await), () => { const now = clock.currentTimeMillisUnsafe() if (now < emitAtMs) { return loop() } else if (lastArr) { emitLatch.closeUnsafe() pullLatch.closeUnsafe() const eff = Effect.succeed(Arr.of(Arr.lastNonEmpty(lastArr))) lastArr = undefined return eff } else if (cause) { return Effect.failCause(cause!) } return loop() }) }) return Effect.suspend(() => { if (cause) { if (lastArr) { const eff = Effect.succeed(Arr.of(Arr.lastNonEmpty(lastArr))) lastArr = undefined return eff } return Effect.failCause(cause) } pullLatch.openUnsafe() return emitLatch.whenOpen(sleepLoop) }) }) ) ) /** * Delays the arrays of this stream according to the given bandwidth * parameters using the token bucket algorithm. Allows for burst processing by * allowing the bucket to accumulate tokens up to a `units + burst` threshold. * The weight of each array is determined by the effectful `cost` function. * * If using the "enforce" strategy, arrays that do not meet the bandwidth * constraints are dropped. If using the "shape" strategy, arrays are delayed * until they can be emitted without exceeding the bandwidth constraints. * * Defaults to the "shape" strategy. * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const stream = Stream.fromSchedule(Schedule.spaced("50 millis")).pipe( * Stream.take(6), * Stream.throttleEffect({ * cost: (arr) => Effect.succeed(arr.length), * units: 1, * duration: "100 millis", * strategy: "shape" * }) * ) * * Effect.runPromise(Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * })) * // Output: [0, 1, 2, 3, 4, 5] * ``` * * @since 2.0.0 * @category Rate Limiting */ export const throttleEffect: { /** * Delays the arrays of this stream according to the given bandwidth * parameters using the token bucket algorithm. Allows for burst processing by * allowing the bucket to accumulate tokens up to a `units + burst` threshold. * The weight of each array is determined by the effectful `cost` function. * * If using the "enforce" strategy, arrays that do not meet the bandwidth * constraints are dropped. If using the "shape" strategy, arrays are delayed * until they can be emitted without exceeding the bandwidth constraints. * * Defaults to the "shape" strategy. * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const stream = Stream.fromSchedule(Schedule.spaced("50 millis")).pipe( * Stream.take(6), * Stream.throttleEffect({ * cost: (arr) => Effect.succeed(arr.length), * units: 1, * duration: "100 millis", * strategy: "shape" * }) * ) * * Effect.runPromise(Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * })) * // Output: [0, 1, 2, 3, 4, 5] * ``` * * @since 2.0.0 * @category Rate Limiting */ ( options: { readonly cost: (arr: Arr.NonEmptyReadonlyArray) => Effect.Effect readonly units: number readonly duration: Duration.Input readonly burst?: number | undefined readonly strategy?: "enforce" | "shape" | undefined } ): (self: Stream) => Stream /** * Delays the arrays of this stream according to the given bandwidth * parameters using the token bucket algorithm. Allows for burst processing by * allowing the bucket to accumulate tokens up to a `units + burst` threshold. * The weight of each array is determined by the effectful `cost` function. * * If using the "enforce" strategy, arrays that do not meet the bandwidth * constraints are dropped. If using the "shape" strategy, arrays are delayed * until they can be emitted without exceeding the bandwidth constraints. * * Defaults to the "shape" strategy. * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const stream = Stream.fromSchedule(Schedule.spaced("50 millis")).pipe( * Stream.take(6), * Stream.throttleEffect({ * cost: (arr) => Effect.succeed(arr.length), * units: 1, * duration: "100 millis", * strategy: "shape" * }) * ) * * Effect.runPromise(Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * })) * // Output: [0, 1, 2, 3, 4, 5] * ``` * * @since 2.0.0 * @category Rate Limiting */ ( self: Stream, options: { readonly cost: (arr: Arr.NonEmptyReadonlyArray) => Effect.Effect readonly units: number readonly duration: Duration.Input readonly burst?: number | undefined readonly strategy?: "enforce" | "shape" | undefined } ): Stream } = dual( 2, ( self: Stream, options: { readonly cost: (arr: Arr.NonEmptyReadonlyArray) => Effect.Effect readonly units: number readonly duration: Duration.Input readonly burst?: number | undefined readonly strategy?: "enforce" | "shape" | undefined } ): Stream => { const burst = options.burst ?? 0 if (options.strategy === "enforce") { return throttleEnforceEffect(self, options.cost, options.units, options.duration, burst) } return throttleShapeEffect(self, options.cost, options.units, options.duration, burst) } ) const throttleEnforceEffect = ( self: Stream, cost: (arr: Arr.NonEmptyReadonlyArray) => Effect.Effect, units: number, duration: Duration.Input, burst: number ): Stream => transformPull(self, (pull) => Effect.clockWith((clock) => { const durationMs = Duration.toMillis(Duration.fromInputUnsafe(duration)) const max = units + burst < 0 ? Number.POSITIVE_INFINITY : units + burst let tokens = units let timestampMs = clock.currentTimeMillisUnsafe() return Effect.succeed( Effect.flatMap(pull, function loop(arr): Pull.Pull, E | E2, void, R | R2> { return Effect.flatMap(cost(arr), (weight) => { const currentMs = clock.currentTimeMillisUnsafe() const elapsed = currentMs - timestampMs const cycles = elapsed / durationMs const sum = tokens + (cycles * units) const available = sum < 0 ? max : Math.min(sum, max) if (weight <= available) { tokens = available - weight timestampMs = currentMs return Effect.succeed(arr) } // Drop the array and continue return Effect.flatMap(pull, loop) }) }) ) })) const throttleShapeEffect = ( self: Stream, cost: (arr: Arr.NonEmptyReadonlyArray) => Effect.Effect, units: number, duration: Duration.Input, burst: number ): Stream => transformPull(self, (pull) => Effect.clockWith((clock) => { const durationMs = Duration.toMillis(Duration.fromInputUnsafe(duration)) const max = units + burst < 0 ? Number.POSITIVE_INFINITY : units + burst let tokens = units let timestampMs = clock.currentTimeMillisUnsafe() return Effect.succeed(Effect.flatMap(pull, (arr) => Effect.flatMap(cost(arr), (weight) => { const currentMs = clock.currentTimeMillisUnsafe() const elapsed = currentMs - timestampMs const cycles = elapsed / durationMs const sum = tokens + (cycles * units) const available = sum < 0 ? max : Math.min(sum, max) const remaining = available - weight if (remaining >= 0) { tokens = remaining timestampMs = currentMs return Effect.succeed(arr) } // Calculate delay needed const waitCycles = -remaining / units const delayMs = Math.max(0, waitCycles * durationMs) if (delayMs > 0) { return Effect.flatMap(Effect.sleep(delayMs), () => { tokens = remaining timestampMs = currentMs return Effect.succeed(arr) }) } tokens = remaining timestampMs = currentMs return Effect.succeed(arr) }))) })) /** * Delays the arrays of this stream using a token bucket and a per-array cost. * Allows bursts by letting the bucket accumulate up to a `units + burst` * threshold. The weight of each array is determined by the `cost` function. * * If using the "enforce" strategy, arrays that do not meet the bandwidth * constraints are dropped. If using the "shape" strategy, arrays are delayed * until they can be emitted without exceeding the bandwidth constraints. * * Defaults to the "shape" strategy. * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const stream = Stream.fromSchedule(Schedule.spaced("50 millis")).pipe( * Stream.take(6), * Stream.throttle({ * cost: (arr) => arr.length, * units: 1, * duration: "100 millis", * strategy: "shape" * }) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * // Output: [ 0, 1, 2, 3, 4, 5 ] * }) * ``` * * @since 2.0.0 * @category Rate Limiting */ export const throttle: { /** * Delays the arrays of this stream using a token bucket and a per-array cost. * Allows bursts by letting the bucket accumulate up to a `units + burst` * threshold. The weight of each array is determined by the `cost` function. * * If using the "enforce" strategy, arrays that do not meet the bandwidth * constraints are dropped. If using the "shape" strategy, arrays are delayed * until they can be emitted without exceeding the bandwidth constraints. * * Defaults to the "shape" strategy. * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const stream = Stream.fromSchedule(Schedule.spaced("50 millis")).pipe( * Stream.take(6), * Stream.throttle({ * cost: (arr) => arr.length, * units: 1, * duration: "100 millis", * strategy: "shape" * }) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * // Output: [ 0, 1, 2, 3, 4, 5 ] * }) * ``` * * @since 2.0.0 * @category Rate Limiting */ ( options: { readonly cost: (arr: Arr.NonEmptyReadonlyArray) => number readonly units: number readonly duration: Duration.Input readonly burst?: number | undefined readonly strategy?: "enforce" | "shape" | undefined } ): (self: Stream) => Stream /** * Delays the arrays of this stream using a token bucket and a per-array cost. * Allows bursts by letting the bucket accumulate up to a `units + burst` * threshold. The weight of each array is determined by the `cost` function. * * If using the "enforce" strategy, arrays that do not meet the bandwidth * constraints are dropped. If using the "shape" strategy, arrays are delayed * until they can be emitted without exceeding the bandwidth constraints. * * Defaults to the "shape" strategy. * * @example * ```ts * import { Console, Effect, Schedule, Stream } from "effect" * * const stream = Stream.fromSchedule(Schedule.spaced("50 millis")).pipe( * Stream.take(6), * Stream.throttle({ * cost: (arr) => arr.length, * units: 1, * duration: "100 millis", * strategy: "shape" * }) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * // Output: [ 0, 1, 2, 3, 4, 5 ] * }) * ``` * * @since 2.0.0 * @category Rate Limiting */ ( self: Stream, options: { readonly cost: (arr: Arr.NonEmptyReadonlyArray) => number readonly units: number readonly duration: Duration.Input readonly burst?: number | undefined readonly strategy?: "enforce" | "shape" | undefined } ): Stream } = dual( 2, ( self: Stream, options: { readonly cost: (arr: Arr.NonEmptyReadonlyArray) => number readonly units: number readonly duration: Duration.Input readonly burst?: number | undefined readonly strategy?: "enforce" | "shape" | undefined } ): Stream => throttleEffect(self, { ...options, cost: (arr) => Effect.succeed(options.cost(arr)) }) ) /** * Partitions the stream into non-empty arrays of the specified size. * * The final array may be smaller if there are not enough elements to fill it. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const grouped = yield* Stream.range(1, 8).pipe( * Stream.grouped(3), * Stream.runCollect * ) * yield* Console.log(grouped) * }) * * Effect.runPromise(program) * // Output: [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8 ] ] * ``` * * @since 2.0.0 * @category Grouping */ export const grouped: { /** * Partitions the stream into non-empty arrays of the specified size. * * The final array may be smaller if there are not enough elements to fill it. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const grouped = yield* Stream.range(1, 8).pipe( * Stream.grouped(3), * Stream.runCollect * ) * yield* Console.log(grouped) * }) * * Effect.runPromise(program) * // Output: [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8 ] ] * ``` * * @since 2.0.0 * @category Grouping */ (n: number): (self: Stream) => Stream, E, R> /** * Partitions the stream into non-empty arrays of the specified size. * * The final array may be smaller if there are not enough elements to fill it. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const grouped = yield* Stream.range(1, 8).pipe( * Stream.grouped(3), * Stream.runCollect * ) * yield* Console.log(grouped) * }) * * Effect.runPromise(program) * // Output: [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8 ] ] * ``` * * @since 2.0.0 * @category Grouping */ (self: Stream, n: number): Stream, E, R> } = dual( 2, (self: Stream, n: number): Stream, E, R> => chunks(rechunk(self, n)) ) /** * Partitions the stream into arrays, emitting when the chunk size is reached * or the duration passes. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.groupedWithin(2, "5 seconds"), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ [ 1, 2 ], [ 3 ] ] * ``` * * @since 2.0.0 * @category Grouping */ export const groupedWithin: { /** * Partitions the stream into arrays, emitting when the chunk size is reached * or the duration passes. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.groupedWithin(2, "5 seconds"), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ [ 1, 2 ], [ 3 ] ] * ``` * * @since 2.0.0 * @category Grouping */ (chunkSize: number, duration: Duration.Input): (self: Stream) => Stream, E, R> /** * Partitions the stream into arrays, emitting when the chunk size is reached * or the duration passes. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.groupedWithin(2, "5 seconds"), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: [ [ 1, 2 ], [ 3 ] ] * ``` * * @since 2.0.0 * @category Grouping */ (self: Stream, chunkSize: number, duration: Duration.Input): Stream, E, R> } = dual(3, ( self: Stream, chunkSize: number, duration: Duration.Input ): Stream, E, R> => aggregateWithin( self, Sink.take(chunkSize), Schedule.spaced(duration) )) /** * Groups elements into keyed substreams using an effectful classifier. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const grouped = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.groupBy((n) => * Effect.succeed([n % 2 === 0 ? "even" : "odd", n] as const) * ), * Stream.mapEffect( * Effect.fnUntraced(function*([key, stream]) { * return [key, yield* Stream.runCollect(stream)] as const * }), * { concurrency: "unbounded" } * ), * Stream.runCollect * ) * * yield* Console.log(grouped) * }) * * Effect.runPromise(program) * // Output: [ [ "odd", [ 1, 3, 5 ] ], [ "even", [ 2, 4 ] ] ] * ``` * * @since 2.0.0 * @category Grouping */ export const groupBy: { /** * Groups elements into keyed substreams using an effectful classifier. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const grouped = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.groupBy((n) => * Effect.succeed([n % 2 === 0 ? "even" : "odd", n] as const) * ), * Stream.mapEffect( * Effect.fnUntraced(function*([key, stream]) { * return [key, yield* Stream.runCollect(stream)] as const * }), * { concurrency: "unbounded" } * ), * Stream.runCollect * ) * * yield* Console.log(grouped) * }) * * Effect.runPromise(program) * // Output: [ [ "odd", [ 1, 3, 5 ] ], [ "even", [ 2, 4 ] ] ] * ``` * * @since 2.0.0 * @category Grouping */ ( f: (a: NoInfer) => Effect.Effect, options?: { readonly bufferSize?: number | undefined readonly idleTimeToLive?: Duration.Input | undefined } ): (self: Stream) => Stream], E | E2, R | R2> /** * Groups elements into keyed substreams using an effectful classifier. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const grouped = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.groupBy((n) => * Effect.succeed([n % 2 === 0 ? "even" : "odd", n] as const) * ), * Stream.mapEffect( * Effect.fnUntraced(function*([key, stream]) { * return [key, yield* Stream.runCollect(stream)] as const * }), * { concurrency: "unbounded" } * ), * Stream.runCollect * ) * * yield* Console.log(grouped) * }) * * Effect.runPromise(program) * // Output: [ [ "odd", [ 1, 3, 5 ] ], [ "even", [ 2, 4 ] ] ] * ``` * * @since 2.0.0 * @category Grouping */ ( self: Stream, f: (a: NoInfer) => Effect.Effect, options?: { readonly bufferSize?: number | undefined readonly idleTimeToLive?: Duration.Input | undefined } ): Stream], E | E2, R | R2> } = dual((args) => isStream(args[0]), ( self: Stream, f: (a: NoInfer) => Effect.Effect, options?: { readonly bufferSize?: number | undefined readonly idleTimeToLive?: Duration.Input | undefined } ): Stream], E | E2, R | R2> => groupByImpl( self, Effect.fnUntraced(function*(arr, queues, queueMap) { for (let i = 0; i < arr.length; i++) { const [key, value] = yield* f(arr[i]) const oentry = MutableHashMap.get(queueMap, key) const queue = Option.isSome(oentry) ? oentry.value : yield* Effect.scoped(RcMap.get(queues, key)) yield* RcMap.touch(queues, key) yield* Queue.offer(queue, value) } }), options )) /** * Groups elements by a key and emits a stream per key. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const grouped = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.groupByKey((n) => n % 2 === 0 ? "even" : "odd"), * Stream.mapEffect( * ([key, stream]) => * Stream.runCollect(stream).pipe( * Effect.map((values) => [key, values] as const) * ), * { concurrency: "unbounded" } * ), * Stream.runCollect * ) * yield* Console.log(grouped) * }) * * Effect.runPromise(program) * // Output: [ [ "odd", [ 1, 3, 5 ] ], [ "even", [ 2, 4 ] ] ] * ``` * * @since 2.0.0 * @category Grouping */ export const groupByKey: { /** * Groups elements by a key and emits a stream per key. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const grouped = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.groupByKey((n) => n % 2 === 0 ? "even" : "odd"), * Stream.mapEffect( * ([key, stream]) => * Stream.runCollect(stream).pipe( * Effect.map((values) => [key, values] as const) * ), * { concurrency: "unbounded" } * ), * Stream.runCollect * ) * yield* Console.log(grouped) * }) * * Effect.runPromise(program) * // Output: [ [ "odd", [ 1, 3, 5 ] ], [ "even", [ 2, 4 ] ] ] * ``` * * @since 2.0.0 * @category Grouping */ ( f: (a: NoInfer) => K, options?: { readonly bufferSize?: number | undefined readonly idleTimeToLive?: Duration.Input | undefined } ): (self: Stream) => Stream], E, R> /** * Groups elements by a key and emits a stream per key. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const grouped = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.groupByKey((n) => n % 2 === 0 ? "even" : "odd"), * Stream.mapEffect( * ([key, stream]) => * Stream.runCollect(stream).pipe( * Effect.map((values) => [key, values] as const) * ), * { concurrency: "unbounded" } * ), * Stream.runCollect * ) * yield* Console.log(grouped) * }) * * Effect.runPromise(program) * // Output: [ [ "odd", [ 1, 3, 5 ] ], [ "even", [ 2, 4 ] ] ] * ``` * * @since 2.0.0 * @category Grouping */ ( self: Stream, f: (a: NoInfer) => K, options?: { readonly bufferSize?: number | undefined readonly idleTimeToLive?: Duration.Input | undefined } ): Stream], E, R> } = dual((args) => isStream(args[0]), ( self: Stream, f: (a: NoInfer) => K, options?: { readonly bufferSize?: number | undefined readonly idleTimeToLive?: Duration.Input | undefined } ): Stream], E, R> => suspend(() => { const batch = MutableHashMap.empty n % 2 === 0 ? "even" : "odd"), * Stream.mapEffect( * ([key, stream]) => * Stream.runCollect(stream).pipe( * Effect.map((values) => [key, values] as const) * ), * { concurrency: "unbounded" } * ), * Stream.runCollect * ) * yield* Console.log(grouped) * }) * * Effect.runPromise(program) * // Output: [ [ "odd", [ 1, 3, 5 ] ], [ "even", [ 2, 4 ] ] ] * ``` * * @since 2.0.0 * @category Grouping */ K, /** * Groups elements by a key and emits a stream per key. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const grouped = yield* Stream.make(1, 2, 3, 4, 5).pipe( * Stream.groupByKey((n) => n % 2 === 0 ? "even" : "odd"), * Stream.mapEffect( * ([key, stream]) => * Stream.runCollect(stream).pipe( * Effect.map((values) => [key, values] as const) * ), * { concurrency: "unbounded" } * ), * Stream.runCollect * ) * yield* Console.log(grouped) * }) * * Effect.runPromise(program) * // Output: [ [ "odd", [ 1, 3, 5 ] ], [ "even", [ 2, 4 ] ] ] * ``` * * @since 2.0.0 * @category Grouping */ Arr.NonEmptyArray>() return groupByImpl( self, Effect.fnUntraced(function*(arr, queues, queueMap) { for (let i = 0; i < arr.length; i++) { const key = f(arr[i]) const ovalues = MutableHashMap.get(batch, key) if (Option.isNone(ovalues)) { MutableHashMap.set(batch, key, [arr[i]]) } else { ovalues.value.push(arr[i]) } } for (const [key, values] of batch) { const oentry = MutableHashMap.get(queueMap, key) const queue = Option.isSome(oentry) ? oentry.value : yield* Effect.scoped(RcMap.get(queues, key)) yield* RcMap.touch(queues, key) yield* Queue.offerAll(queue, values) } MutableHashMap.clear(batch) }), options ) })) const groupByImpl = ( self: Stream, f: ( arr: Arr.NonEmptyReadonlyArray, queues: RcMap.RcMap>, queueMap: MutableHashMap.MutableHashMap> ) => Effect.Effect, options?: { readonly bufferSize?: number | undefined readonly idleTimeToLive?: Duration.Input | undefined } ): Stream], E | E2, R | R2> => transformPullBracket( self, Effect.fnUntraced(function*(pull, scope, forkedScope) { const out = yield* Queue.unbounded], E | E2 | Cause.Done>() yield* Scope.addFinalizer(scope, Queue.shutdown(out)) const queueMap = MutableHashMap.empty>() const queues = yield* RcMap.make({ lookup: (key: K) => Effect.acquireRelease( Queue.make({ capacity: options?.bufferSize ?? 4096 }).pipe( Effect.tap((queue) => { MutableHashMap.set(queueMap, key, queue) return Queue.offer(out, [key, fromQueue(queue)]) }) ), (queue) => { MutableHashMap.remove(queueMap, key) return Queue.end(queue) } ), idleTimeToLive: options?.idleTimeToLive ?? Duration.infinity }).pipe(Scope.provide(forkedScope)) yield* Effect.whileLoop({ while: constTrue, body: constant(Effect.flatMap(pull, (arr) => f(arr, queues, queueMap))), step: constVoid }).pipe( Effect.catchCause((cause) => Queue.failCause(out, cause)), Effect.forkIn(scope) ) return Queue.takeAll(out) }) ) /** * @since 2.0.0 * @category Grouping */ export const groupAdjacentBy: { /** * @since 2.0.0 * @category Grouping */ (f: (a: NoInfer) => K): (self: Stream) => Stream], E, R> /** * @since 2.0.0 * @category Grouping */ (self: Stream, f: (a: NoInfer) => K): Stream], E, R> } = dual(2, ( self: Stream, f: (a: NoInfer) => K ): Stream], E, R> => transformPull(self, (pull, _scope) => Effect.sync(() => { let currentKey: K = undefined as any let group: Arr.NonEmptyArray | undefined let toEmit = Arr.empty]>() const loop: Pull.Pull< Arr.NonEmptyReadonlyArray]>, E > = pull.pipe( Effect.flatMap((chunk) => { for (let i = 0; i < chunk.length; i++) { const item = chunk[i] const key = f(item) if (group === undefined) { currentKey = key group = [item] continue } else if (Equal.equals(key, currentKey)) { group.push(item) continue } toEmit.push([currentKey, group]) currentKey = key group = [item] } if (Arr.isArrayNonEmpty(toEmit)) { const out = toEmit toEmit = [] return Effect.succeed(out) } return loop }) ) let done = false return Pull.catchDone(Effect.suspend(() => done ? Cause.done() : loop), () => { done = true const out = group group = undefined return out && Arr.isArrayNonEmpty(out) ? Effect.succeed(Arr.of([currentKey, out])) : Cause.done() }) }))) /** * Applies a sink transducer to the stream and emits each sink result. * * @example * ```ts * import { Console, Effect, Sink, Stream } from "effect" * * const program = Effect.gen(function* () { * const result = yield* Stream.make(1, 2, 3, 4).pipe( * Stream.transduce(Sink.take(2)), * Stream.runCollect * ) * * yield* Console.log(result) * // Output: [ [ 1, 2 ], [ 3, 4 ] ] * }) * ``` * * @since 2.0.0 * @category Aggregation */ export const transduce = dual< /** * Applies a sink transducer to the stream and emits each sink result. * * @example * ```ts * import { Console, Effect, Sink, Stream } from "effect" * * const program = Effect.gen(function* () { * const result = yield* Stream.make(1, 2, 3, 4).pipe( * Stream.transduce(Sink.take(2)), * Stream.runCollect * ) * * yield* Console.log(result) * // Output: [ [ 1, 2 ], [ 3, 4 ] ] * }) * ``` * * @since 2.0.0 * @category Aggregation */ (sink: Sink.Sink) => (self: Stream) => Stream, /** * Applies a sink transducer to the stream and emits each sink result. * * @example * ```ts * import { Console, Effect, Sink, Stream } from "effect" * * const program = Effect.gen(function* () { * const result = yield* Stream.make(1, 2, 3, 4).pipe( * Stream.transduce(Sink.take(2)), * Stream.runCollect * ) * * yield* Console.log(result) * // Output: [ [ 1, 2 ], [ 3, 4 ] ] * }) * ``` * * @since 2.0.0 * @category Aggregation */ (self: Stream, sink: Sink.Sink) => Stream >( 2, ( self: Stream, sink: Sink.Sink ): Stream => transformPull(self, (upstream, scope) => Effect.sync(() => { let done: Exit.Exit | E> | undefined let leftover: Arr.NonEmptyReadonlyArray | undefined const upstreamWithLeftover = Effect.suspend(() => { if (leftover !== undefined) { const chunk = leftover leftover = undefined return Effect.succeed(chunk) } return upstream }).pipe( Effect.catch((error) => { done = Exit.fail(error) return Cause.done() }) ) const pull = Effect.map( Effect.suspend(() => sink.transform(upstreamWithLeftover, scope)), ([value, leftover_]) => { leftover = leftover_ return Arr.of(value) } ) return Effect.suspend((): Pull.Pull< Arr.NonEmptyReadonlyArray, E | E2, void, R2 > => done ? done : pull) })) ) /** * Aggregates elements using the provided sink and emits each sink result as a stream element. * * The stream runs the upstream and downstream in separate fibers, so the sink can keep * consuming input while downstream is busy processing the previous output. * * @example * ```ts * import { Console, Effect, Sink, Stream } from "effect" * * Effect.runPromise(Effect.gen(function* () { * const aggregated = yield* Stream.runCollect( * Stream.make(1, 2, 3, 4, 5, 6).pipe( * Stream.aggregate( * Sink.foldUntil(() => 0, 3, (sum, n) => Effect.succeed(sum + n)) * ) * ) * ) * yield* Console.log(aggregated) * })) * // [ 6, 15 ] * ``` * * @since 2.0.0 * @category Aggregation */ export const aggregate: { /** * Aggregates elements using the provided sink and emits each sink result as a stream element. * * The stream runs the upstream and downstream in separate fibers, so the sink can keep * consuming input while downstream is busy processing the previous output. * * @example * ```ts * import { Console, Effect, Sink, Stream } from "effect" * * Effect.runPromise(Effect.gen(function* () { * const aggregated = yield* Stream.runCollect( * Stream.make(1, 2, 3, 4, 5, 6).pipe( * Stream.aggregate( * Sink.foldUntil(() => 0, 3, (sum, n) => Effect.succeed(sum + n)) * ) * ) * ) * yield* Console.log(aggregated) * })) * // [ 6, 15 ] * ``` * * @since 2.0.0 * @category Aggregation */ (sink: Sink.Sink): (self: Stream) => Stream /** * Aggregates elements using the provided sink and emits each sink result as a stream element. * * The stream runs the upstream and downstream in separate fibers, so the sink can keep * consuming input while downstream is busy processing the previous output. * * @example * ```ts * import { Console, Effect, Sink, Stream } from "effect" * * Effect.runPromise(Effect.gen(function* () { * const aggregated = yield* Stream.runCollect( * Stream.make(1, 2, 3, 4, 5, 6).pipe( * Stream.aggregate( * Sink.foldUntil(() => 0, 3, (sum, n) => Effect.succeed(sum + n)) * ) * ) * ) * yield* Console.log(aggregated) * })) * // [ 6, 15 ] * ``` * * @since 2.0.0 * @category Aggregation */ (self: Stream, sink: Sink.Sink): Stream } = dual(2, ( self: Stream, sink: Sink.Sink ): Stream => aggregateWithin(self, sink, Schedule.forever)) /** * Aggregates elements with a sink, emitting each result when the sink completes or the schedule triggers. * * The schedule can flush the current aggregation even if the sink has not finished. * * @example * ```ts * import { Console, Effect, Schedule, Sink, Stream } from "effect" * * Effect.runPromise(Effect.gen(function* () { * const aggregated = yield* Stream.runCollect( * Stream.make(1, 2, 3, 4, 5, 6).pipe( * Stream.aggregateWithin( * Sink.foldUntil(() => 0, 3, (sum, n) => Effect.succeed(sum + n)), * Schedule.spaced("1 minute") * ) * ) * ) * yield* Console.log(aggregated) * })) * // Output: [ 6, 15 ] * ``` * * @since 2.0.0 * @category Aggregation */ export const aggregateWithin: { /** * Aggregates elements with a sink, emitting each result when the sink completes or the schedule triggers. * * The schedule can flush the current aggregation even if the sink has not finished. * * @example * ```ts * import { Console, Effect, Schedule, Sink, Stream } from "effect" * * Effect.runPromise(Effect.gen(function* () { * const aggregated = yield* Stream.runCollect( * Stream.make(1, 2, 3, 4, 5, 6).pipe( * Stream.aggregateWithin( * Sink.foldUntil(() => 0, 3, (sum, n) => Effect.succeed(sum + n)), * Schedule.spaced("1 minute") * ) * ) * ) * yield* Console.log(aggregated) * })) * // Output: [ 6, 15 ] * ``` * * @since 2.0.0 * @category Aggregation */ ( sink: Sink.Sink, schedule: Schedule.Schedule, E3, R3> ): (self: Stream) => Stream /** * Aggregates elements with a sink, emitting each result when the sink completes or the schedule triggers. * * The schedule can flush the current aggregation even if the sink has not finished. * * @example * ```ts * import { Console, Effect, Schedule, Sink, Stream } from "effect" * * Effect.runPromise(Effect.gen(function* () { * const aggregated = yield* Stream.runCollect( * Stream.make(1, 2, 3, 4, 5, 6).pipe( * Stream.aggregateWithin( * Sink.foldUntil(() => 0, 3, (sum, n) => Effect.succeed(sum + n)), * Schedule.spaced("1 minute") * ) * ) * ) * yield* Console.log(aggregated) * })) * // Output: [ 6, 15 ] * ``` * * @since 2.0.0 * @category Aggregation */ ( self: Stream, sink: Sink.Sink, schedule: Schedule.Schedule, E3, R3> ): Stream } = dual(3, ( self: Stream, sink: Sink.Sink, schedule: Schedule.Schedule, E3, R3> ): Stream => fromChannel(Channel.fromTransformBracket(Effect.fnUntraced(function*(_upstream, _, scope) { const pull = yield* Channel.toPullScoped(self.channel, _) const pullLatch = Latch.makeUnsafe(false) const scheduleStep = Symbol() const buffer = yield* Queue.make 0, 3, (sum, n) => Effect.succeed(sum + n)), * Schedule.spaced("1 minute") * ) * ) * ) * yield* Console.log(aggregated) * })) * // Output: [ 6, 15 ] * ``` * * @since 2.0.0 * @category Aggregation */ Arr.NonEmptyReadonlyArray | typeof scheduleStep, /** * Aggregates elements with a sink, emitting each result when the sink completes or the schedule triggers. * * The schedule can flush the current aggregation even if the sink has not finished. * * @example * ```ts * import { Console, Effect, Schedule, Sink, Stream } from "effect" * * Effect.runPromise(Effect.gen(function* () { * const aggregated = yield* Stream.runCollect( * Stream.make(1, 2, 3, 4, 5, 6).pipe( * Stream.aggregateWithin( * Sink.foldUntil(() => 0, 3, (sum, n) => Effect.succeed(sum + n)), * Schedule.spaced("1 minute") * ) * ) * ) * yield* Console.log(aggregated) * })) * // Output: [ 6, 15 ] * ``` * * @since 2.0.0 * @category Aggregation */ E | Cause.Done>({ capacity: 0 }) // upstream -> buffer yield* pull.pipe( pullLatch.whenOpen, Effect.flatMap((arr) => { pullLatch.closeUnsafe() return Queue.offer(buffer, arr) }), Effect.forever, // don't disable autoYield to prevent choking the schedule Effect.catchCause((cause) => Queue.failCause(buffer, cause)), Effect.forkIn(scope) ) // schedule -> buffer let lastOutput = Option.none 0, 3, (sum, n) => Effect.succeed(sum + n)), * Schedule.spaced("1 minute") * ) * ) * ) * yield* Console.log(aggregated) * })) * // Output: [ 6, 15 ] * ``` * * @since 2.0.0 * @category Aggregation */ B>() let leftover: Arr.NonEmptyReadonlyArray | undefined let sinkHasInput = false const step = yield* Schedule.toStepWithSleep(schedule) const stepToBuffer = Effect.suspend(function loop(): Pull.Pull { return step(lastOutput).pipe( Effect.flatMap(() => !sinkHasInput ? loop() : Queue.offer(buffer, scheduleStep)), Effect.flatMap(() => Effect.never), Pull.catchDone(() => Cause.done()) ) }) // buffer -> sink const pullFromBuffer: Pull.Pull< Arr.NonEmptyReadonlyArray, E > = Queue.take(buffer).pipe( Effect.flatMap((arr) => { if (arr === scheduleStep) { return Cause.done() } sinkHasInput = true return Effect.succeed(arr) }) ) const sinkUpstream = Effect.suspend((): Pull.Pull, E> => { if (leftover !== undefined) { const chunk = leftover leftover = undefined sinkHasInput = true return Effect.succeed(chunk) } pullLatch.openUnsafe() return pullFromBuffer }) const catchSinkHalt = Effect.flatMap(([value, leftover_]: Sink.End) => { // ignore the last output if the upstream only pulled a halt if (!sinkHasInput && buffer.state._tag === "Done") return Cause.done() lastOutput = Option.some(value) leftover = leftover_ return Effect.succeed(Arr.of(value)) }) return Effect.suspend(() => { // if the buffer has exited and there is no more data to process if (buffer.state._tag === "Done" && leftover === undefined) { return buffer.state.exit as Exit.Exit | E> } sinkHasInput = leftover !== undefined return Effect.succeed(Effect.suspend(() => sink.transform(sinkUpstream as any, scope))) }).pipe( Effect.flatMap((pull) => Effect.raceFirst(catchSinkHalt(pull), stepToBuffer)) ) })))) /** * Creates a PubSub-backed stream that multicasts the source to all subscribers. * * The returned stream is scoped and uses the provided PubSub capacity and replay settings. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.scoped( * Effect.gen(function* () { * const broadcasted = yield* Stream.broadcast(Stream.fromArray([1, 2, 3]), { * capacity: 8, * replay: 3 * }) * * const [left, right] = yield* Effect.all([ * Stream.runCollect(broadcasted), * Stream.runCollect(broadcasted) * ], { concurrency: "unbounded" }) * * yield* Console.log([left, right]) * }) * ) * * Effect.runPromise(program) * // Output: [[1, 2, 3], [1, 2, 3]] * ``` * * @since 2.0.0 * @category Broadcast */ export const broadcast: { /** * Creates a PubSub-backed stream that multicasts the source to all subscribers. * * The returned stream is scoped and uses the provided PubSub capacity and replay settings. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.scoped( * Effect.gen(function* () { * const broadcasted = yield* Stream.broadcast(Stream.fromArray([1, 2, 3]), { * capacity: 8, * replay: 3 * }) * * const [left, right] = yield* Effect.all([ * Stream.runCollect(broadcasted), * Stream.runCollect(broadcasted) * ], { concurrency: "unbounded" }) * * yield* Console.log([left, right]) * }) * ) * * Effect.runPromise(program) * // Output: [[1, 2, 3], [1, 2, 3]] * ``` * * @since 2.0.0 * @category Broadcast */ ( options: { readonly capacity: "unbounded" readonly replay?: number | undefined } | { readonly capacity: number readonly strategy?: "sliding" | "dropping" | "suspend" | undefined readonly replay?: number | undefined } ): (self: Stream) => Effect.Effect, never, Scope.Scope | R> /** * Creates a PubSub-backed stream that multicasts the source to all subscribers. * * The returned stream is scoped and uses the provided PubSub capacity and replay settings. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.scoped( * Effect.gen(function* () { * const broadcasted = yield* Stream.broadcast(Stream.fromArray([1, 2, 3]), { * capacity: 8, * replay: 3 * }) * * const [left, right] = yield* Effect.all([ * Stream.runCollect(broadcasted), * Stream.runCollect(broadcasted) * ], { concurrency: "unbounded" }) * * yield* Console.log([left, right]) * }) * ) * * Effect.runPromise(program) * // Output: [[1, 2, 3], [1, 2, 3]] * ``` * * @since 2.0.0 * @category Broadcast */ ( self: Stream, options: { readonly capacity: "unbounded" readonly replay?: number | undefined } | { readonly capacity: number readonly strategy?: "sliding" | "dropping" | "suspend" | undefined readonly replay?: number | undefined } ): Effect.Effect, never, Scope.Scope | R> } = dual(2, ( self: Stream, options: { readonly capacity: "unbounded" readonly replay?: number | undefined } | { readonly capacity: number readonly strategy?: "sliding" | "dropping" | "suspend" | undefined readonly replay?: number | undefined } ): Effect.Effect, never, Scope.Scope | R> => Effect.map(toPubSubTake(self, options), fromPubSubTake)) /** * Returns a new Stream that multicasts the original stream, subscribing when the first consumer starts. * * The upstream continues running while there is at least one consumer and is finalized after the last one exits. * If `idleTimeToLive` is set, the upstream is kept alive for that duration so a later subscriber can continue from * the next element instead of restarting. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * Effect.runPromise( * Effect.scoped( * Effect.gen(function*() { * const shared = yield* Stream.make(1, 2, 3).pipe( * Stream.share({ capacity: 16 }) * ) * * const first = yield* shared.pipe(Stream.take(1), Stream.runCollect) * const second = yield* shared.pipe(Stream.take(1), Stream.runCollect) * * yield* Console.log([first, second]) * }) * ) * ) * // output: [[1], [1]] * ``` * * @since 2.0.0 * @category Broadcast */ export const share: { /** * Returns a new Stream that multicasts the original stream, subscribing when the first consumer starts. * * The upstream continues running while there is at least one consumer and is finalized after the last one exits. * If `idleTimeToLive` is set, the upstream is kept alive for that duration so a later subscriber can continue from * the next element instead of restarting. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * Effect.runPromise( * Effect.scoped( * Effect.gen(function*() { * const shared = yield* Stream.make(1, 2, 3).pipe( * Stream.share({ capacity: 16 }) * ) * * const first = yield* shared.pipe(Stream.take(1), Stream.runCollect) * const second = yield* shared.pipe(Stream.take(1), Stream.runCollect) * * yield* Console.log([first, second]) * }) * ) * ) * // output: [[1], [1]] * ``` * * @since 2.0.0 * @category Broadcast */ ( options: { readonly capacity: "unbounded" readonly replay?: number | undefined readonly idleTimeToLive?: Duration.Input | undefined } | { readonly capacity: number readonly strategy?: "sliding" | "dropping" | "suspend" | undefined readonly replay?: number | undefined readonly idleTimeToLive?: Duration.Input | undefined } ): (self: Stream) => Effect.Effect, never, Scope.Scope | R> /** * Returns a new Stream that multicasts the original stream, subscribing when the first consumer starts. * * The upstream continues running while there is at least one consumer and is finalized after the last one exits. * If `idleTimeToLive` is set, the upstream is kept alive for that duration so a later subscriber can continue from * the next element instead of restarting. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * Effect.runPromise( * Effect.scoped( * Effect.gen(function*() { * const shared = yield* Stream.make(1, 2, 3).pipe( * Stream.share({ capacity: 16 }) * ) * * const first = yield* shared.pipe(Stream.take(1), Stream.runCollect) * const second = yield* shared.pipe(Stream.take(1), Stream.runCollect) * * yield* Console.log([first, second]) * }) * ) * ) * // output: [[1], [1]] * ``` * * @since 2.0.0 * @category Broadcast */ ( self: Stream, options: { readonly capacity: "unbounded" readonly replay?: number | undefined readonly idleTimeToLive?: Duration.Input | undefined } | { readonly capacity: number readonly strategy?: "sliding" | "dropping" | "suspend" | undefined readonly replay?: number | undefined readonly idleTimeToLive?: Duration.Input | undefined } ): Effect.Effect, never, Scope.Scope | R> } = dual(2, ( self: Stream, options: { readonly capacity: "unbounded" readonly replay?: number | undefined readonly idleTimeToLive?: Duration.Input | undefined } | { readonly capacity: number readonly strategy?: "sliding" | "dropping" | "suspend" | undefined readonly replay?: number | undefined readonly idleTimeToLive?: Duration.Input | undefined } ): Effect.Effect, never, Scope.Scope | R> => Effect.map( RcRef.make({ acquire: broadcast(self, options), idleTimeToLive: options.idleTimeToLive }), (ref) => unwrap(RcRef.get(ref)) )) /** * Pipes this stream through a channel that consumes and emits chunked elements. * * The channel receives `NonEmptyReadonlyArray` chunks and can transform both the * output elements and error type. * * @example * ```ts * import { Array, Channel, Console, Effect, Stream } from "effect" * * type NumberChunk = readonly [number, ...Array] * * const doubleChunks = Channel.identity().pipe( * Channel.map((chunk) => Array.map(chunk, (n) => n * 2)) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.fromArray([1, 2, 3]).pipe( * Stream.rechunk(2), * Stream.pipeThroughChannel(doubleChunks), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // => [2, 4, 6] * ``` * * @since 2.0.0 * @category Pipe */ export const pipeThroughChannel: { /** * Pipes this stream through a channel that consumes and emits chunked elements. * * The channel receives `NonEmptyReadonlyArray` chunks and can transform both the * output elements and error type. * * @example * ```ts * import { Array, Channel, Console, Effect, Stream } from "effect" * * type NumberChunk = readonly [number, ...Array] * * const doubleChunks = Channel.identity().pipe( * Channel.map((chunk) => Array.map(chunk, (n) => n * 2)) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.fromArray([1, 2, 3]).pipe( * Stream.rechunk(2), * Stream.pipeThroughChannel(doubleChunks), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // => [2, 4, 6] * ``` * * @since 2.0.0 * @category Pipe */ ( channel: Channel.Channel, E2, unknown, Arr.NonEmptyReadonlyArray, E, unknown, R2> ): (self: Stream) => Stream /** * Pipes this stream through a channel that consumes and emits chunked elements. * * The channel receives `NonEmptyReadonlyArray` chunks and can transform both the * output elements and error type. * * @example * ```ts * import { Array, Channel, Console, Effect, Stream } from "effect" * * type NumberChunk = readonly [number, ...Array] * * const doubleChunks = Channel.identity().pipe( * Channel.map((chunk) => Array.map(chunk, (n) => n * 2)) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.fromArray([1, 2, 3]).pipe( * Stream.rechunk(2), * Stream.pipeThroughChannel(doubleChunks), * Stream.runCollect * ) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // => [2, 4, 6] * ``` * * @since 2.0.0 * @category Pipe */ ( self: Stream, channel: Channel.Channel, E2, unknown, Arr.NonEmptyReadonlyArray, E, unknown, R2> ): Stream } = dual(2, ( self: Stream, channel: Channel.Channel, E2, unknown, Arr.NonEmptyReadonlyArray, E, unknown, R2> ): Stream => fromChannel(Channel.pipeTo(self.channel, channel))) /** * Pipes values through the provided channel while preserving this stream's * failures alongside any channel failures. * * Upstream failures are not passed to the channel, so the resulting stream can * fail with either the original stream error or the channel error. * * @example * ```ts * import type { Channel } from "effect" * import { Console, Effect, Stream } from "effect" * * declare const transformChannel: Channel.Channel< * readonly [string, ...Array], * "ChannelError", * unknown, * readonly [number, ...Array], * "StreamError", * unknown, * never * > * * Effect.runPromise(Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3).pipe( * Stream.pipeThroughChannelOrFail(transformChannel), * Stream.runCollect * ) * * yield* Console.log(result) * })) * // Output: * // ["1", "2", "3"] * ``` * * @since 2.0.0 * @category Pipe */ export const pipeThroughChannelOrFail: { /** * Pipes values through the provided channel while preserving this stream's * failures alongside any channel failures. * * Upstream failures are not passed to the channel, so the resulting stream can * fail with either the original stream error or the channel error. * * @example * ```ts * import type { Channel } from "effect" * import { Console, Effect, Stream } from "effect" * * declare const transformChannel: Channel.Channel< * readonly [string, ...Array], * "ChannelError", * unknown, * readonly [number, ...Array], * "StreamError", * unknown, * never * > * * Effect.runPromise(Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3).pipe( * Stream.pipeThroughChannelOrFail(transformChannel), * Stream.runCollect * ) * * yield* Console.log(result) * })) * // Output: * // ["1", "2", "3"] * ``` * * @since 2.0.0 * @category Pipe */ ( channel: Channel.Channel, E2, unknown, Arr.NonEmptyReadonlyArray, E, unknown, R2> ): (self: Stream) => Stream /** * Pipes values through the provided channel while preserving this stream's * failures alongside any channel failures. * * Upstream failures are not passed to the channel, so the resulting stream can * fail with either the original stream error or the channel error. * * @example * ```ts * import type { Channel } from "effect" * import { Console, Effect, Stream } from "effect" * * declare const transformChannel: Channel.Channel< * readonly [string, ...Array], * "ChannelError", * unknown, * readonly [number, ...Array], * "StreamError", * unknown, * never * > * * Effect.runPromise(Effect.gen(function*() { * const result = yield* Stream.make(1, 2, 3).pipe( * Stream.pipeThroughChannelOrFail(transformChannel), * Stream.runCollect * ) * * yield* Console.log(result) * })) * // Output: * // ["1", "2", "3"] * ``` * * @since 2.0.0 * @category Pipe */ ( self: Stream, channel: Channel.Channel, E2, unknown, Arr.NonEmptyReadonlyArray, E, unknown, R2> ): Stream } = dual(2, ( self: Stream, channel: Channel.Channel, E2, unknown, Arr.NonEmptyReadonlyArray, E, unknown, R2> ): Stream => fromChannel(Channel.pipeToOrFail(self.channel, channel))) /** * Pipes the stream through `Sink.toChannel`, emitting only the sink leftovers. * * If the sink completes mid-chunk, the remaining elements become the output stream. * * @example * ```ts * import { Console, Effect, Sink, Stream } from "effect" * * const program = Effect.gen(function*() { * const leftovers = yield* Stream.make(1, 2, 3, 4).pipe( * Stream.pipeThrough(Sink.take(2)), * Stream.runCollect * ) * * yield* Console.log(leftovers) * }) * * Effect.runPromise(program) * //=> [ 3, 4 ] * ``` * * @since 2.0.0 * @category Pipe */ export const pipeThrough: { /** * Pipes the stream through `Sink.toChannel`, emitting only the sink leftovers. * * If the sink completes mid-chunk, the remaining elements become the output stream. * * @example * ```ts * import { Console, Effect, Sink, Stream } from "effect" * * const program = Effect.gen(function*() { * const leftovers = yield* Stream.make(1, 2, 3, 4).pipe( * Stream.pipeThrough(Sink.take(2)), * Stream.runCollect * ) * * yield* Console.log(leftovers) * }) * * Effect.runPromise(program) * //=> [ 3, 4 ] * ``` * * @since 2.0.0 * @category Pipe */ (sink: Sink.Sink): (self: Stream) => Stream /** * Pipes the stream through `Sink.toChannel`, emitting only the sink leftovers. * * If the sink completes mid-chunk, the remaining elements become the output stream. * * @example * ```ts * import { Console, Effect, Sink, Stream } from "effect" * * const program = Effect.gen(function*() { * const leftovers = yield* Stream.make(1, 2, 3, 4).pipe( * Stream.pipeThrough(Sink.take(2)), * Stream.runCollect * ) * * yield* Console.log(leftovers) * }) * * Effect.runPromise(program) * //=> [ 3, 4 ] * ``` * * @since 2.0.0 * @category Pipe */ (self: Stream, sink: Sink.Sink): Stream } = dual( 2, (self: Stream, sink: Sink.Sink): Stream => self.channel.pipe( Channel.pipeToOrFail(Sink.toChannel(sink)), Channel.concatWith(([_, leftover]) => leftover ? Channel.succeed(leftover) : Channel.empty), fromChannel ) ) /** * Collects all elements into an array and emits it as a single element. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3) * * const program = Effect.gen(function*() { * const collected = yield* stream.pipe(Stream.collect, Stream.runCollect) * yield* Console.log(collected[0]) * }) * * Effect.runPromise(program) * // [1, 2, 3] * ``` * * @since 2.0.0 * @category Accumulation */ export const collect = (self: Stream): Stream, E, R> => fromEffect(runCollect(self)) /** * Accumulates elements into a growing array, emitting the cumulative array for each input chunk. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const accumulated = yield* Stream.runCollect( * Stream.fromArray([1, 2, 3]).pipe( * Stream.rechunk(1), * Stream.accumulate * ) * ) * yield* Console.log(accumulated) * }) * * Effect.runPromise(program) * //=> { _id: 'Chunk', values: [ [ 1 ], [ 1, 2 ], [ 1, 2, 3 ] ] } * ``` * * @since 2.0.0 * @category Accumulation */ export const accumulate = (self: Stream): Stream, E, R> => mapAccumArray(self, Arr.empty, (acc, as) => { const combined = Arr.appendAll(acc, as) return [combined, [combined]] }) /** * Emits only elements that differ from the previous one. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.fromIterable([1, 1, 2, 2, 3]).pipe( * Stream.changes, * Stream.runCollect * ) * * yield* Console.log(values) * }) * * Effect.runPromise(program) * // [1, 2, 3] * ``` * * @since 2.0.0 * @category Deduplication */ export const changes = (self: Stream): Stream => changesWith(self, Equal.equals) /** * Returns a stream that only emits elements that are not equal to the previously emitted element, as determined by the specified predicate. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make("A", "a", "B", "b", "b").pipe( * Stream.changesWith((left, right) => left.toLowerCase() === right.toLowerCase()) * ) * * Effect.runPromise( * Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * ) * // ["A", "B"] * ``` * * @since 2.0.0 * @category Deduplication */ export const changesWith: { /** * Returns a stream that only emits elements that are not equal to the previously emitted element, as determined by the specified predicate. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make("A", "a", "B", "b", "b").pipe( * Stream.changesWith((left, right) => left.toLowerCase() === right.toLowerCase()) * ) * * Effect.runPromise( * Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * ) * // ["A", "B"] * ``` * * @since 2.0.0 * @category Deduplication */ (f: (x: A, y: A) => boolean): (self: Stream) => Stream /** * Returns a stream that only emits elements that are not equal to the previously emitted element, as determined by the specified predicate. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make("A", "a", "B", "b", "b").pipe( * Stream.changesWith((left, right) => left.toLowerCase() === right.toLowerCase()) * ) * * Effect.runPromise( * Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * ) * // ["A", "B"] * ``` * * @since 2.0.0 * @category Deduplication */ (self: Stream, f: (x: A, y: A) => boolean): Stream } = dual( 2, (self: Stream, f: (x: A, y: A) => boolean): Stream => transformPull(self, (pull, _scope) => Effect.sync(() => { let first = true let last: A return Effect.flatMap(pull, function loop(arr): Pull.Pull, E> { const out: Array = [] let i = 0 if (first) { first = false last = arr[0] i = 1 out.push(last) } for (; i < arr.length; i++) { const a = arr[i] if (f(a, last)) continue last = a out.push(a) } return Arr.isArrayNonEmpty(out) ? Effect.succeed(out) : Effect.flatMap(pull, loop) }) })) ) /** * Emits only elements that differ from the previous element, using an effectful equality check. * * The predicate runs for each element after the first; returning `true` treats it as equal and skips it. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 1, 2, 2, 3, 3).pipe( * Stream.changesWithEffect((a, b) => Effect.succeed(a === b)) * ) * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // { _id: "Chunk", values: [ 1, 2, 3 ] } * ``` * * @since 2.0.0 * @category Deduplication */ export const changesWithEffect: { /** * Emits only elements that differ from the previous element, using an effectful equality check. * * The predicate runs for each element after the first; returning `true` treats it as equal and skips it. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 1, 2, 2, 3, 3).pipe( * Stream.changesWithEffect((a, b) => Effect.succeed(a === b)) * ) * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // { _id: "Chunk", values: [ 1, 2, 3 ] } * ``` * * @since 2.0.0 * @category Deduplication */ (f: (x: A, y: A) => Effect.Effect): (self: Stream) => Stream /** * Emits only elements that differ from the previous element, using an effectful equality check. * * The predicate runs for each element after the first; returning `true` treats it as equal and skips it. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 1, 2, 2, 3, 3).pipe( * Stream.changesWithEffect((a, b) => Effect.succeed(a === b)) * ) * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // { _id: "Chunk", values: [ 1, 2, 3 ] } * ``` * * @since 2.0.0 * @category Deduplication */ (self: Stream, f: (x: A, y: A) => Effect.Effect): Stream } = dual( 2, ( self: Stream, f: (x: A, y: A) => Effect.Effect ): Stream => transformPull(self, (pull, _scope) => Effect.sync(() => { let first = true let last: A return Effect.flatMap( pull, Effect.fnUntraced(function* loop(arr): Generator< Pull.Pull, Arr.NonEmptyReadonlyArray, any > { const out: Array = [] let i = 0 if (first) { first = false last = arr[0] i = 1 out.push(last) } for (; i < arr.length; i++) { const a = arr[i] if (yield* f(a, last)) continue last = a out.push(a) } return Arr.isArrayNonEmpty(out) ? out : yield* Effect.flatMap(pull, Effect.fnUntraced(loop)) }) ) })) ) /** * Decodes Uint8Array chunks into strings using TextDecoder with an optional encoding. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const encoder = new TextEncoder() * const stream = Stream.make( * encoder.encode("Hello"), * encoder.encode(" World") * ) * * const program = Effect.gen(function*() { * const decoded = yield* stream.pipe( * Stream.decodeText, * Stream.runCollect * ) * yield* Console.log(decoded) * }) * * Effect.runPromise(program) * // ["Hello", " World"] * ``` * * @since 2.0.0 * @category Encoding */ export const decodeText: < Arg extends Stream | { readonly encoding?: string | undefined } | undefined = { readonly encoding?: string | undefined } >( streamOrOptions?: Arg, options?: { readonly encoding?: string | undefined } | undefined ) => [Arg] extends [Stream] ? Stream : (self: Stream) => Stream = dual( (args) => isStream(args[0]), (self: Stream, options?: { readonly encoding?: string | undefined }): Stream => suspend(() => { const decoder = new TextDecoder(options?.encoding) return map(self, (chunk) => decoder.decode(chunk, { stream: true })) }) ) /** * Encodes a stream of strings into UTF-8 `Uint8Array` chunks. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make("Hello", " ", "World") * const program = Effect.gen(function*() { * const encoded = Stream.encodeText(stream) * const chunks = yield* Stream.runCollect(encoded) * const bytes = chunks.map((chunk) => [...chunk]) * yield* Console.log(bytes) * }) * * Effect.runPromise(program) * // [[72, 101, 108, 108, 111], [32], [87, 111, 114, 108, 100]] * ``` * * @since 2.0.0 * @category Encoding */ export const encodeText = (self: Stream): Stream => suspend(() => { const encoder = new TextEncoder() return map(self, (chunk) => encoder.encode(chunk)) }) /** * Splits a stream of strings into lines, handling `\n`, `\r`, and `\r\n` delimiters across chunks. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * Effect.runPromise(Effect.gen(function* () { * const lines = yield* Stream.runCollect( * Stream.make("a\nb\r\n", "c\n").pipe(Stream.splitLines) * ) * yield* Console.log(lines) * })) * // ["a", "b", "c"] * ``` * * @since 2.0.0 * @category Encoding */ export const splitLines = (self: Stream): Stream => self.channel.pipe( Channel.pipeTo(Channel.splitLines()), fromChannel ) /** * Inserts the provided element between emitted elements. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 2, 3, 4).pipe(Stream.intersperse(0)) * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // [1, 0, 2, 0, 3, 0, 4] * ``` * * @since 2.0.0 * @category Sequencing */ export const intersperse: { /** * Inserts the provided element between emitted elements. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 2, 3, 4).pipe(Stream.intersperse(0)) * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // [1, 0, 2, 0, 3, 0, 4] * ``` * * @since 2.0.0 * @category Sequencing */ (element: A2): (self: Stream) => Stream /** * Inserts the provided element between emitted elements. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 2, 3, 4).pipe(Stream.intersperse(0)) * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // [1, 0, 2, 0, 3, 0, 4] * ``` * * @since 2.0.0 * @category Sequencing */ (self: Stream, element: A2): Stream } = dual(2, (self: Stream, element: A2): Stream => mapArray(self, (arr, i) => { const out: Arr.NonEmptyArray = i === 0 ? [] as any : [element] const lastIndex = arr.length - 1 for (let j = 0; j < arr.length; j++) { if (j === lastIndex) { out.push(arr[j]) } else { out.push(arr[j], element) } } return out })) /** * Intersperse stream elements with a middle value, adding a start and end value. * * The start and end values are always emitted, even when the stream is empty. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make("a", "b", "c").pipe( * Stream.intersperseAffixes({ start: "[", middle: ",", end: "]" }) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // [ "[", "a", ",", "b", ",", "c", "]" ] * ``` * * @since 2.0.0 * @category Sequencing */ export const intersperseAffixes: { /** * Intersperse stream elements with a middle value, adding a start and end value. * * The start and end values are always emitted, even when the stream is empty. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make("a", "b", "c").pipe( * Stream.intersperseAffixes({ start: "[", middle: ",", end: "]" }) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // [ "[", "a", ",", "b", ",", "c", "]" ] * ``` * * @since 2.0.0 * @category Sequencing */ (options: { readonly start: A2; readonly middle: A3; readonly end: A4 }): (self: Stream) => Stream /** * Intersperse stream elements with a middle value, adding a start and end value. * * The start and end values are always emitted, even when the stream is empty. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make("a", "b", "c").pipe( * Stream.intersperseAffixes({ start: "[", middle: ",", end: "]" }) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // [ "[", "a", ",", "b", ",", "c", "]" ] * ``` * * @since 2.0.0 * @category Sequencing */ ( self: Stream, options: { readonly start: A2; readonly middle: A3; readonly end: A4 } ): Stream } = dual(2, ( self: Stream, options: { readonly start: A2; readonly middle: A3; readonly end: A4 } ): Stream => succeed(options.start).pipe( concat(intersperse(self, options.middle)), concat(succeed(options.end)) )) /** * Interleaves this stream with the specified stream by alternating pulls from * each stream; when one ends, the remaining values from the other stream are * emitted. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.interleave( * Stream.make(2, 3), * Stream.make(5, 6, 7) * ) * * const program = Effect.gen(function*() { * const collected = yield* Stream.runCollect(stream) * yield* Console.log(collected) * }) * * Effect.runPromise(program) * // [2, 5, 3, 6, 7] * ``` * @since 2.0.0 * @category Merging */ export const interleave: { /** * Interleaves this stream with the specified stream by alternating pulls from * each stream; when one ends, the remaining values from the other stream are * emitted. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.interleave( * Stream.make(2, 3), * Stream.make(5, 6, 7) * ) * * const program = Effect.gen(function*() { * const collected = yield* Stream.runCollect(stream) * yield* Console.log(collected) * }) * * Effect.runPromise(program) * // [2, 5, 3, 6, 7] * ``` * @since 2.0.0 * @category Merging */ (that: Stream): (self: Stream) => Stream /** * Interleaves this stream with the specified stream by alternating pulls from * each stream; when one ends, the remaining values from the other stream are * emitted. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.interleave( * Stream.make(2, 3), * Stream.make(5, 6, 7) * ) * * const program = Effect.gen(function*() { * const collected = yield* Stream.runCollect(stream) * yield* Console.log(collected) * }) * * Effect.runPromise(program) * // [2, 5, 3, 6, 7] * ``` * @since 2.0.0 * @category Merging */ (self: Stream, that: Stream): Stream } = dual( 2, (self: Stream, that: Stream): Stream => interleaveWith( self, that, fromIterable(Iterable.forever([true, false])) ) ) /** * Interleaves two streams deterministically by following a boolean decider stream. * * The decider controls how many elements are pulled; if one side ends, pulls for * that side are ignored. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const left = Stream.make(1, 3, 5) * const right = Stream.make(2, 4, 6) * const decider = Stream.make(true, false, false, true, true) * * const values = yield* Stream.runCollect( * Stream.interleaveWith(left, right, decider) * ) * * yield* Console.log(values) * }) * * Effect.runPromise(program) * // [ 1, 2, 4, 3, 5 ] * ``` * * @since 2.0.0 * @category Merging */ export const interleaveWith: { /** * Interleaves two streams deterministically by following a boolean decider stream. * * The decider controls how many elements are pulled; if one side ends, pulls for * that side are ignored. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const left = Stream.make(1, 3, 5) * const right = Stream.make(2, 4, 6) * const decider = Stream.make(true, false, false, true, true) * * const values = yield* Stream.runCollect( * Stream.interleaveWith(left, right, decider) * ) * * yield* Console.log(values) * }) * * Effect.runPromise(program) * // [ 1, 2, 4, 3, 5 ] * ``` * * @since 2.0.0 * @category Merging */ (that: Stream, decider: Stream): (self: Stream) => Stream /** * Interleaves two streams deterministically by following a boolean decider stream. * * The decider controls how many elements are pulled; if one side ends, pulls for * that side are ignored. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const left = Stream.make(1, 3, 5) * const right = Stream.make(2, 4, 6) * const decider = Stream.make(true, false, false, true, true) * * const values = yield* Stream.runCollect( * Stream.interleaveWith(left, right, decider) * ) * * yield* Console.log(values) * }) * * Effect.runPromise(program) * // [ 1, 2, 4, 3, 5 ] * ``` * * @since 2.0.0 * @category Merging */ ( self: Stream, that: Stream, decider: Stream ): Stream } = dual(3, ( self: Stream, that: Stream, decider: Stream ): Stream => fromChannel(Channel.fromTransform(Effect.fnUntraced(function*(upstream, scope) { const pullDecider = yield* Channel.toTransform(Channel.flattenArray(decider.channel))(upstream, scope) const retry = Symbol() type retry = typeof retry let leftDone = false let rightDone = false const pullLeft = (yield* Channel.toTransform(Channel.flattenArray(self.channel))( upstream, scope )).pipe( Pull.catchDone(() => { leftDone = true return Effect.succeed(retry); }) ) const pullRight = (yield* Channel.toTransform(Channel.flattenArray(that.channel))( upstream, scope )).pipe( Pull.catchDone(() => { rightDone = true return Effect.succeed(retry); }) ) return Effect.gen(function*() { while (true) { if (leftDone && rightDone) { return yield* Cause.done() } const side = yield* pullDecider if (side && leftDone) continue if (!side && rightDone) continue const elem = yield* (side ? pullLeft : pullRight) if (elem === retry) continue return Arr.of(elem) } }) })))) /** * Interrupts the evaluation of this stream when the provided effect * completes. The given effect will be forked as part of this stream, and its * success will be discarded. This combinator will also interrupt any * in-progress element being pulled from upstream. * * If the effect completes with a failure before the stream completes, the * returned stream will emit that failure. * * @example * ```ts * import { Console, Deferred, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const interrupt = yield* Deferred.make() * const stream = Stream.make(1, 2, 3).pipe( * Stream.tap((value) => * value === 2 * ? Deferred.succeed(interrupt, void 0) * : Effect.void * ), * Stream.interruptWhen(Deferred.await(interrupt)) * ) * * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // => [1, 2] * ``` * * @since 2.0.0 * @category Interruption */ export const interruptWhen: { /** * Interrupts the evaluation of this stream when the provided effect * completes. The given effect will be forked as part of this stream, and its * success will be discarded. This combinator will also interrupt any * in-progress element being pulled from upstream. * * If the effect completes with a failure before the stream completes, the * returned stream will emit that failure. * * @example * ```ts * import { Console, Deferred, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const interrupt = yield* Deferred.make() * const stream = Stream.make(1, 2, 3).pipe( * Stream.tap((value) => * value === 2 * ? Deferred.succeed(interrupt, void 0) * : Effect.void * ), * Stream.interruptWhen(Deferred.await(interrupt)) * ) * * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // => [1, 2] * ``` * * @since 2.0.0 * @category Interruption */ (effect: Effect.Effect): (self: Stream) => Stream /** * Interrupts the evaluation of this stream when the provided effect * completes. The given effect will be forked as part of this stream, and its * success will be discarded. This combinator will also interrupt any * in-progress element being pulled from upstream. * * If the effect completes with a failure before the stream completes, the * returned stream will emit that failure. * * @example * ```ts * import { Console, Deferred, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const interrupt = yield* Deferred.make() * const stream = Stream.make(1, 2, 3).pipe( * Stream.tap((value) => * value === 2 * ? Deferred.succeed(interrupt, void 0) * : Effect.void * ), * Stream.interruptWhen(Deferred.await(interrupt)) * ) * * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // => [1, 2] * ``` * * @since 2.0.0 * @category Interruption */ (self: Stream, effect: Effect.Effect): Stream } = dual( 2, (self: Stream, effect: Effect.Effect): Stream => fromChannel(Channel.interruptWhen(self.channel, effect)) ) /** * Halts evaluation after the current element once the provided effect completes; the effect is forked, its success is discarded, failures fail the stream, and it does not interrupt an in-progress pull (use `interruptWhen` for that). * * @example * ```ts * import { Console, Deferred, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const halt = yield* Deferred.make() * const values = yield* Stream.fromArray([1, 2, 3]).pipe( * Stream.tap((value) => value === 2 ? Deferred.succeed(halt, void 0) : Effect.void), * Stream.haltWhen(Deferred.await(halt)), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: * // [1, 2] * ``` * * @since 2.0.0 * @category Interruption */ export const haltWhen: { /** * Halts evaluation after the current element once the provided effect completes; the effect is forked, its success is discarded, failures fail the stream, and it does not interrupt an in-progress pull (use `interruptWhen` for that). * * @example * ```ts * import { Console, Deferred, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const halt = yield* Deferred.make() * const values = yield* Stream.fromArray([1, 2, 3]).pipe( * Stream.tap((value) => value === 2 ? Deferred.succeed(halt, void 0) : Effect.void), * Stream.haltWhen(Deferred.await(halt)), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: * // [1, 2] * ``` * * @since 2.0.0 * @category Interruption */ (effect: Effect.Effect): (self: Stream) => Stream /** * Halts evaluation after the current element once the provided effect completes; the effect is forked, its success is discarded, failures fail the stream, and it does not interrupt an in-progress pull (use `interruptWhen` for that). * * @example * ```ts * import { Console, Deferred, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const halt = yield* Deferred.make() * const values = yield* Stream.fromArray([1, 2, 3]).pipe( * Stream.tap((value) => value === 2 ? Deferred.succeed(halt, void 0) : Effect.void), * Stream.haltWhen(Deferred.await(halt)), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: * // [1, 2] * ``` * * @since 2.0.0 * @category Interruption */ (self: Stream, effect: Effect.Effect): Stream } = dual( 2, (self: Stream, effect: Effect.Effect): Stream => fromChannel(Channel.haltWhen(self.channel, effect)) ) /** * Runs the provided finalizer when the stream exits, passing the exit value. * * @example * ```ts * import { Console, Effect, Exit, Stream } from "effect" * * const stream = Stream.make(1, 2, 3).pipe( * Stream.onExit((exit) => * Exit.isSuccess(exit) * ? Console.log("Stream completed successfully") * : Console.log("Stream failed") * ) * ) * * Effect.runPromise(Effect.gen(function*() { * yield* Stream.runCollect(stream) * })) * // Output: * // Stream completed successfully * ``` * * @since 4.0.0 * @category Finalization */ export const onExit: { /** * Runs the provided finalizer when the stream exits, passing the exit value. * * @example * ```ts * import { Console, Effect, Exit, Stream } from "effect" * * const stream = Stream.make(1, 2, 3).pipe( * Stream.onExit((exit) => * Exit.isSuccess(exit) * ? Console.log("Stream completed successfully") * : Console.log("Stream failed") * ) * ) * * Effect.runPromise(Effect.gen(function*() { * yield* Stream.runCollect(stream) * })) * // Output: * // Stream completed successfully * ``` * * @since 4.0.0 * @category Finalization */ ( finalizer: (exit: Exit.Exit) => Effect.Effect ): (self: Stream) => Stream /** * Runs the provided finalizer when the stream exits, passing the exit value. * * @example * ```ts * import { Console, Effect, Exit, Stream } from "effect" * * const stream = Stream.make(1, 2, 3).pipe( * Stream.onExit((exit) => * Exit.isSuccess(exit) * ? Console.log("Stream completed successfully") * : Console.log("Stream failed") * ) * ) * * Effect.runPromise(Effect.gen(function*() { * yield* Stream.runCollect(stream) * })) * // Output: * // Stream completed successfully * ``` * * @since 4.0.0 * @category Finalization */ ( self: Stream, finalizer: (exit: Exit.Exit) => Effect.Effect ): Stream } = dual(2, ( self: Stream, finalizer: (exit: Exit.Exit) => Effect.Effect ): Stream => fromChannel(Channel.onExit(self.channel, finalizer))) /** * Runs the provided effect when the stream fails, passing the failure cause. * * Note: Unlike `Effect.onError` there is no guarantee that the provided * effect will not be interrupted. * * @example * ```ts * import { Cause, Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 2, 3).pipe( * Stream.concat(Stream.fail("boom")), * Stream.onError((cause) => Console.log(`Stream failed: ${Cause.squash(cause)}`)) * ) * * yield* Stream.runCollect(stream) * }) * * Effect.runPromiseExit(program) * // Output: * // Stream failed: boom * ``` * * @since 2.0.0 * @category Error Handling */ export const onError: { /** * Runs the provided effect when the stream fails, passing the failure cause. * * Note: Unlike `Effect.onError` there is no guarantee that the provided * effect will not be interrupted. * * @example * ```ts * import { Cause, Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 2, 3).pipe( * Stream.concat(Stream.fail("boom")), * Stream.onError((cause) => Console.log(`Stream failed: ${Cause.squash(cause)}`)) * ) * * yield* Stream.runCollect(stream) * }) * * Effect.runPromiseExit(program) * // Output: * // Stream failed: boom * ``` * * @since 2.0.0 * @category Error Handling */ (cleanup: (cause: Cause.Cause) => Effect.Effect): (self: Stream) => Stream /** * Runs the provided effect when the stream fails, passing the failure cause. * * Note: Unlike `Effect.onError` there is no guarantee that the provided * effect will not be interrupted. * * @example * ```ts * import { Cause, Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 2, 3).pipe( * Stream.concat(Stream.fail("boom")), * Stream.onError((cause) => Console.log(`Stream failed: ${Cause.squash(cause)}`)) * ) * * yield* Stream.runCollect(stream) * }) * * Effect.runPromiseExit(program) * // Output: * // Stream failed: boom * ``` * * @since 2.0.0 * @category Error Handling */ ( self: Stream, cleanup: (cause: Cause.Cause) => Effect.Effect ): Stream } = dual(2, ( self: Stream, cleanup: (cause: Cause.Cause) => Effect.Effect ): Stream => fromChannel(Channel.onError(self.channel, cleanup))) /** * Runs the provided effect before this stream starts. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.fromArray([1, 2, 3]).pipe( * Stream.onStart(Console.log("Stream started")) * ) * * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: * // Stream started * // [1, 2, 3] * ``` * * @since 4.0.0 * @category Sequencing */ export const onStart: { /** * Runs the provided effect before this stream starts. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.fromArray([1, 2, 3]).pipe( * Stream.onStart(Console.log("Stream started")) * ) * * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: * // Stream started * // [1, 2, 3] * ``` * * @since 4.0.0 * @category Sequencing */ (onStart: Effect.Effect): (self: Stream) => Stream /** * Runs the provided effect before this stream starts. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.fromArray([1, 2, 3]).pipe( * Stream.onStart(Console.log("Stream started")) * ) * * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Output: * // Stream started * // [1, 2, 3] * ``` * * @since 4.0.0 * @category Sequencing */ (self: Stream, onStart: Effect.Effect): Stream } = dual(2, ( self: Stream, onStart: Effect.Effect ): Stream => fromChannel(Channel.onStart(self.channel, onStart))) /** * Runs the provided effect with the first element emitted by the stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * Effect.runPromise(Effect.gen(function* () { * yield* Stream.fromArray([1, 2, 3]).pipe( * Stream.onFirst((value) => Console.log(`first=${value}`)), * Stream.runDrain * ) * })) * // Output: first=1 * ``` * * @since 4.0.0 * @category Sequencing */ export const onFirst: { /** * Runs the provided effect with the first element emitted by the stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * Effect.runPromise(Effect.gen(function* () { * yield* Stream.fromArray([1, 2, 3]).pipe( * Stream.onFirst((value) => Console.log(`first=${value}`)), * Stream.runDrain * ) * })) * // Output: first=1 * ``` * * @since 4.0.0 * @category Sequencing */ (onFirst: (element: NoInfer) => Effect.Effect): (self: Stream) => Stream /** * Runs the provided effect with the first element emitted by the stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * Effect.runPromise(Effect.gen(function* () { * yield* Stream.fromArray([1, 2, 3]).pipe( * Stream.onFirst((value) => Console.log(`first=${value}`)), * Stream.runDrain * ) * })) * // Output: first=1 * ``` * * @since 4.0.0 * @category Sequencing */ ( self: Stream, onFirst: (element: NoInfer) => Effect.Effect ): Stream } = dual(2, ( self: Stream, onFirst: (element: NoInfer) => Effect.Effect ): Stream => fromChannel(Channel.onFirst(self.channel, (arr) => onFirst(arr[0])))) /** * Runs the provided effect when the stream ends successfully. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.onEnd(Console.log("Stream ended")), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Stream ended * // [1, 2, 3] * ``` * * @since 4.0.0 * @category Sequencing */ export const onEnd: { /** * Runs the provided effect when the stream ends successfully. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.onEnd(Console.log("Stream ended")), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Stream ended * // [1, 2, 3] * ``` * * @since 4.0.0 * @category Sequencing */ (onEnd: Effect.Effect): (self: Stream) => Stream /** * Runs the provided effect when the stream ends successfully. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const values = yield* Stream.make(1, 2, 3).pipe( * Stream.onEnd(Console.log("Stream ended")), * Stream.runCollect * ) * yield* Console.log(values) * }) * * Effect.runPromise(program) * // Stream ended * // [1, 2, 3] * ``` * * @since 4.0.0 * @category Sequencing */ (self: Stream, onEnd: Effect.Effect): Stream } = dual(2, ( self: Stream, onEnd: Effect.Effect ): Stream => fromChannel(Channel.onEnd(self.channel, onEnd))) /** * Executes the provided finalizer after this stream's finalizers run. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.fromArray([1, 2]).pipe( * Stream.ensuring(Effect.orDie(Console.log("cleanup"))) * ) * * const program = Effect.gen(function*() { * const collected = yield* Stream.runCollect(stream) * yield* Console.log(collected) * }) * * Effect.runPromise(program) * //=> cleanup * //=> [1, 2] * ``` * * @since 4.0.0 * @category Finalization */ export const ensuring: { /** * Executes the provided finalizer after this stream's finalizers run. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.fromArray([1, 2]).pipe( * Stream.ensuring(Effect.orDie(Console.log("cleanup"))) * ) * * const program = Effect.gen(function*() { * const collected = yield* Stream.runCollect(stream) * yield* Console.log(collected) * }) * * Effect.runPromise(program) * //=> cleanup * //=> [1, 2] * ``` * * @since 4.0.0 * @category Finalization */ (finalizer: Effect.Effect): (self: Stream) => Stream /** * Executes the provided finalizer after this stream's finalizers run. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.fromArray([1, 2]).pipe( * Stream.ensuring(Effect.orDie(Console.log("cleanup"))) * ) * * const program = Effect.gen(function*() { * const collected = yield* Stream.runCollect(stream) * yield* Console.log(collected) * }) * * Effect.runPromise(program) * //=> cleanup * //=> [1, 2] * ``` * * @since 4.0.0 * @category Finalization */ (self: Stream, finalizer: Effect.Effect): Stream } = dual( 2, (self: Stream, finalizer: Effect.Effect): Stream => fromChannel(Channel.ensuring(self.channel, finalizer)) ) /** * Provides a layer or context to the stream, removing the corresponding * service requirements. Use `options.local` to build the layer every time; by * default, layers are shared between provide calls. * * **Previously Known As:** `provideSomeLayer`, `provideSomeContext`. * * @example * ```ts * import { Console, Effect, Layer, Context, Stream } from "effect" * * class Env extends Context.Service()("Env") {} * * const layer = Layer.succeed(Env)({ name: "Ada" }) * * const stream = Stream.fromEffect( * Effect.gen(function*() { * const env = yield* Effect.service(Env) * return `Hello, ${env.name}` * }) * ) * * const withEnv = stream.pipe(Stream.provide(layer)) * * const program = Stream.runCollect(withEnv).pipe( * Effect.flatMap((values) => Console.log(values)) * ) * * Effect.runPromise(program) * // Output: * // ["Hello, Ada"] * ``` * * @since 4.0.0 * @category Services */ export const provide: { /** * Provides a layer or context to the stream, removing the corresponding * service requirements. Use `options.local` to build the layer every time; by * default, layers are shared between provide calls. * * **Previously Known As:** `provideSomeLayer`, `provideSomeContext`. * * @example * ```ts * import { Console, Effect, Layer, Context, Stream } from "effect" * * class Env extends Context.Service()("Env") {} * * const layer = Layer.succeed(Env)({ name: "Ada" }) * * const stream = Stream.fromEffect( * Effect.gen(function*() { * const env = yield* Effect.service(Env) * return `Hello, ${env.name}` * }) * ) * * const withEnv = stream.pipe(Stream.provide(layer)) * * const program = Stream.runCollect(withEnv).pipe( * Effect.flatMap((values) => Console.log(values)) * ) * * Effect.runPromise(program) * // Output: * // ["Hello, Ada"] * ``` * * @since 4.0.0 * @category Services */ ( layer: Layer.Layer | Context.Context, options?: { readonly local?: boolean | undefined } | undefined ): ( self: Stream ) => Stream | RL> /** * Provides a layer or context to the stream, removing the corresponding * service requirements. Use `options.local` to build the layer every time; by * default, layers are shared between provide calls. * * **Previously Known As:** `provideSomeLayer`, `provideSomeContext`. * * @example * ```ts * import { Console, Effect, Layer, Context, Stream } from "effect" * * class Env extends Context.Service()("Env") {} * * const layer = Layer.succeed(Env)({ name: "Ada" }) * * const stream = Stream.fromEffect( * Effect.gen(function*() { * const env = yield* Effect.service(Env) * return `Hello, ${env.name}` * }) * ) * * const withEnv = stream.pipe(Stream.provide(layer)) * * const program = Stream.runCollect(withEnv).pipe( * Effect.flatMap((values) => Console.log(values)) * ) * * Effect.runPromise(program) * // Output: * // ["Hello, Ada"] * ``` * * @since 4.0.0 * @category Services */ ( self: Stream, layer: Layer.Layer | Context.Context, options?: { readonly local?: boolean | undefined } | undefined ): Stream | RL> } = dual((args) => isStream(args[0]), ( self: Stream, layer: Layer.Layer | Context.Context, options?: { readonly local?: boolean | undefined } | undefined ): Stream | RL> => fromChannel(Channel.provide(self.channel, layer, options))) /** * Provides multiple services to the stream using a context. * * @example * ```ts * import { Console, Effect, Context, Stream } from "effect" * * class Config extends Context.Service()("Config") {} * class Greeter extends Context.Service string }>()("Greeter") {} * * const context = Context.make(Config, { prefix: "Hello" }).pipe( * Context.add(Greeter, { greet: (name: string) => `${name}!` }) * ) * * const stream = Stream.fromEffect( * Effect.gen(function*() { * const config = yield* Effect.service(Config) * const greeter = yield* Effect.service(Greeter) * return greeter.greet(config.prefix) * }) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(Stream.provideContext(stream, context)) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // ["Hello!"] * ``` * * @since 4.0.0 * @category Services */ export const provideContext: { /** * Provides multiple services to the stream using a context. * * @example * ```ts * import { Console, Effect, Context, Stream } from "effect" * * class Config extends Context.Service()("Config") {} * class Greeter extends Context.Service string }>()("Greeter") {} * * const context = Context.make(Config, { prefix: "Hello" }).pipe( * Context.add(Greeter, { greet: (name: string) => `${name}!` }) * ) * * const stream = Stream.fromEffect( * Effect.gen(function*() { * const config = yield* Effect.service(Config) * const greeter = yield* Effect.service(Greeter) * return greeter.greet(config.prefix) * }) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(Stream.provideContext(stream, context)) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // ["Hello!"] * ``` * * @since 4.0.0 * @category Services */ (context: Context.Context): (self: Stream) => Stream> /** * Provides multiple services to the stream using a context. * * @example * ```ts * import { Console, Effect, Context, Stream } from "effect" * * class Config extends Context.Service()("Config") {} * class Greeter extends Context.Service string }>()("Greeter") {} * * const context = Context.make(Config, { prefix: "Hello" }).pipe( * Context.add(Greeter, { greet: (name: string) => `${name}!` }) * ) * * const stream = Stream.fromEffect( * Effect.gen(function*() { * const config = yield* Effect.service(Config) * const greeter = yield* Effect.service(Greeter) * return greeter.greet(config.prefix) * }) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(Stream.provideContext(stream, context)) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // ["Hello!"] * ``` * * @since 4.0.0 * @category Services */ (self: Stream, context: Context.Context): Stream> } = dual( 2, (self: Stream, context: Context.Context): Stream> => fromChannel(Channel.provideContext(self.channel, context)) ) /** * Provides the stream with a single required service, eliminating that * requirement from its environment. * * @example * ```ts * import { Console, Effect, Context, Stream } from "effect" * * class Greeter extends Context.Service string * }>()("Greeter") {} * * const stream = Stream.fromEffect( * Effect.service(Greeter).pipe( * Effect.map((greeter) => greeter.greet("Ada")) * ) * ) * * const program = Effect.gen(function*() { * const collected = yield* Stream.runCollect( * stream.pipe( * Stream.provideService(Greeter, { * greet: (name) => `Hello, ${name}` * }) * ) * ) * yield* Console.log(collected) * }) * * Effect.runPromise(program) * //=> ["Hello, Ada"] * ``` * * @since 4.0.0 * @category Services */ export const provideService: { /** * Provides the stream with a single required service, eliminating that * requirement from its environment. * * @example * ```ts * import { Console, Effect, Context, Stream } from "effect" * * class Greeter extends Context.Service string * }>()("Greeter") {} * * const stream = Stream.fromEffect( * Effect.service(Greeter).pipe( * Effect.map((greeter) => greeter.greet("Ada")) * ) * ) * * const program = Effect.gen(function*() { * const collected = yield* Stream.runCollect( * stream.pipe( * Stream.provideService(Greeter, { * greet: (name) => `Hello, ${name}` * }) * ) * ) * yield* Console.log(collected) * }) * * Effect.runPromise(program) * //=> ["Hello, Ada"] * ``` * * @since 4.0.0 * @category Services */ (key: Context.Key, service: NoInfer): ( self: Stream ) => Stream> /** * Provides the stream with a single required service, eliminating that * requirement from its environment. * * @example * ```ts * import { Console, Effect, Context, Stream } from "effect" * * class Greeter extends Context.Service string * }>()("Greeter") {} * * const stream = Stream.fromEffect( * Effect.service(Greeter).pipe( * Effect.map((greeter) => greeter.greet("Ada")) * ) * ) * * const program = Effect.gen(function*() { * const collected = yield* Stream.runCollect( * stream.pipe( * Stream.provideService(Greeter, { * greet: (name) => `Hello, ${name}` * }) * ) * ) * yield* Console.log(collected) * }) * * Effect.runPromise(program) * //=> ["Hello, Ada"] * ``` * * @since 4.0.0 * @category Services */ (self: Stream, key: Context.Key, service: NoInfer): Stream> } = dual(3, ( self: Stream, key: Context.Key, service: NoInfer ): Stream> => fromChannel(Channel.provideService(self.channel, key, service))) /** * Provides a service to the stream using an effect, removing the requirement and adding the effect's error and environment. * * @example * ```ts * import { Console, Effect, Context, Stream } from "effect" * * class ApiConfig extends Context.Service()("ApiConfig") {} * * const stream = Stream.fromEffect( * Effect.gen(function*() { * const config = yield* Effect.service(ApiConfig) * return config.baseUrl * }) * ) * * const withConfig = stream.pipe( * Stream.provideServiceEffect( * ApiConfig, * Effect.succeed({ baseUrl: "https://example.com" }).pipe( * Effect.tap(() => Console.log("Loading config...")) * ) * ) * ) * * const program = Stream.runCollect(withConfig).pipe( * Effect.flatMap((values) => Console.log(values)) * ) * * Effect.runPromise(program) * // Output: * // Loading config... * // ["https://example.com"] * ``` * * @since 4.0.0 * @category Services */ export const provideServiceEffect: { /** * Provides a service to the stream using an effect, removing the requirement and adding the effect's error and environment. * * @example * ```ts * import { Console, Effect, Context, Stream } from "effect" * * class ApiConfig extends Context.Service()("ApiConfig") {} * * const stream = Stream.fromEffect( * Effect.gen(function*() { * const config = yield* Effect.service(ApiConfig) * return config.baseUrl * }) * ) * * const withConfig = stream.pipe( * Stream.provideServiceEffect( * ApiConfig, * Effect.succeed({ baseUrl: "https://example.com" }).pipe( * Effect.tap(() => Console.log("Loading config...")) * ) * ) * ) * * const program = Stream.runCollect(withConfig).pipe( * Effect.flatMap((values) => Console.log(values)) * ) * * Effect.runPromise(program) * // Output: * // Loading config... * // ["https://example.com"] * ``` * * @since 4.0.0 * @category Services */ (key: Context.Key, service: Effect.Effect, ES, RS>): ( self: Stream ) => Stream | RS> /** * Provides a service to the stream using an effect, removing the requirement and adding the effect's error and environment. * * @example * ```ts * import { Console, Effect, Context, Stream } from "effect" * * class ApiConfig extends Context.Service()("ApiConfig") {} * * const stream = Stream.fromEffect( * Effect.gen(function*() { * const config = yield* Effect.service(ApiConfig) * return config.baseUrl * }) * ) * * const withConfig = stream.pipe( * Stream.provideServiceEffect( * ApiConfig, * Effect.succeed({ baseUrl: "https://example.com" }).pipe( * Effect.tap(() => Console.log("Loading config...")) * ) * ) * ) * * const program = Stream.runCollect(withConfig).pipe( * Effect.flatMap((values) => Console.log(values)) * ) * * Effect.runPromise(program) * // Output: * // Loading config... * // ["https://example.com"] * ``` * * @since 4.0.0 * @category Services */ ( self: Stream, key: Context.Key, service: Effect.Effect, ES, RS> ): Stream | RS> } = dual(3, ( self: Stream, key: Context.Key, service: Effect.Effect, ES, RS> ): Stream | RS> => fromChannel(Channel.provideServiceEffect(self.channel, key, service))) /** * Transforms the stream's required services by mapping the current context * to a new one. * * @example * ```ts * import { Console, Effect, Context, Stream } from "effect" * * class Logger extends Context.Service()("Logger") {} * class Config extends Context.Service()("Config") {} * * const stream = Stream.fromEffect( * Effect.gen(function*() { * const logger = yield* Effect.service(Logger) * const config = yield* Effect.service(Config) * return `${logger.prefix}${config.name}` * }) * ) * * const updated = stream.pipe( * Stream.updateContext((context: Context.Context) => * Context.add(context, Config, { name: "World" }) * ) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(updated) * yield* Console.log(values) * }) * * Effect.runPromise( * Effect.provideService(program, Logger, { prefix: "Hello " }) * ) * //=> [ "Hello World" ] * ``` * * @since 2.0.0 * @category Services */ export const updateContext: { /** * Transforms the stream's required services by mapping the current context * to a new one. * * @example * ```ts * import { Console, Effect, Context, Stream } from "effect" * * class Logger extends Context.Service()("Logger") {} * class Config extends Context.Service()("Config") {} * * const stream = Stream.fromEffect( * Effect.gen(function*() { * const logger = yield* Effect.service(Logger) * const config = yield* Effect.service(Config) * return `${logger.prefix}${config.name}` * }) * ) * * const updated = stream.pipe( * Stream.updateContext((context: Context.Context) => * Context.add(context, Config, { name: "World" }) * ) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(updated) * yield* Console.log(values) * }) * * Effect.runPromise( * Effect.provideService(program, Logger, { prefix: "Hello " }) * ) * //=> [ "Hello World" ] * ``` * * @since 2.0.0 * @category Services */ (f: (context: Context.Context) => Context.Context): ( self: Stream ) => Stream /** * Transforms the stream's required services by mapping the current context * to a new one. * * @example * ```ts * import { Console, Effect, Context, Stream } from "effect" * * class Logger extends Context.Service()("Logger") {} * class Config extends Context.Service()("Config") {} * * const stream = Stream.fromEffect( * Effect.gen(function*() { * const logger = yield* Effect.service(Logger) * const config = yield* Effect.service(Config) * return `${logger.prefix}${config.name}` * }) * ) * * const updated = stream.pipe( * Stream.updateContext((context: Context.Context) => * Context.add(context, Config, { name: "World" }) * ) * ) * * const program = Effect.gen(function*() { * const values = yield* Stream.runCollect(updated) * yield* Console.log(values) * }) * * Effect.runPromise( * Effect.provideService(program, Logger, { prefix: "Hello " }) * ) * //=> [ "Hello World" ] * ``` * * @since 2.0.0 * @category Services */ ( self: Stream, f: (context: Context.Context) => Context.Context ): Stream } = dual(2, ( self: Stream, f: (context: Context.Context) => Context.Context ): Stream => fromChannel(Channel.updateContext(self.channel, f))) /** * Updates a single service in the stream environment by applying a function. * * @example * ```ts * import { Console, Effect, Context, Stream } from "effect" * * class Counter extends Context.Service()("Counter") {} * * const stream = Stream.fromEffect(Effect.service(Counter)).pipe( * Stream.updateService(Counter, (counter) => ({ count: counter.count + 1 })) * ) * * const program = Effect.gen(function*() { * const counters = yield* Stream.runCollect(stream) * yield* Console.log(`Updated count: ${counters[0].count}`) * }) * * Effect.runPromise(Effect.provideService(program, Counter, { count: 0 })) * // Output: Updated count: 1 * ``` * * @since 2.0.0 * @category Services */ export const updateService: { /** * Updates a single service in the stream environment by applying a function. * * @example * ```ts * import { Console, Effect, Context, Stream } from "effect" * * class Counter extends Context.Service()("Counter") {} * * const stream = Stream.fromEffect(Effect.service(Counter)).pipe( * Stream.updateService(Counter, (counter) => ({ count: counter.count + 1 })) * ) * * const program = Effect.gen(function*() { * const counters = yield* Stream.runCollect(stream) * yield* Console.log(`Updated count: ${counters[0].count}`) * }) * * Effect.runPromise(Effect.provideService(program, Counter, { count: 0 })) * // Output: Updated count: 1 * ``` * * @since 2.0.0 * @category Services */ (key: Context.Key, f: (service: NoInfer) => S): ( self: Stream ) => Stream /** * Updates a single service in the stream environment by applying a function. * * @example * ```ts * import { Console, Effect, Context, Stream } from "effect" * * class Counter extends Context.Service()("Counter") {} * * const stream = Stream.fromEffect(Effect.service(Counter)).pipe( * Stream.updateService(Counter, (counter) => ({ count: counter.count + 1 })) * ) * * const program = Effect.gen(function*() { * const counters = yield* Stream.runCollect(stream) * yield* Console.log(`Updated count: ${counters[0].count}`) * }) * * Effect.runPromise(Effect.provideService(program, Counter, { count: 0 })) * // Output: Updated count: 1 * ``` * * @since 2.0.0 * @category Services */ ( self: Stream, key: Context.Key, f: (service: NoInfer) => S ): Stream } = dual(3, ( self: Stream, service: Context.Key, f: (service: NoInfer) => S ): Stream => updateContext(self, (context) => Context.add( context, service, f(Context.get(context, service)) ))) /** * Wraps the stream with a new span for tracing. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.fromArray([1, 2, 3]).pipe(Stream.withSpan("numbers")) * * Effect.runPromise( * Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * ) * // [1, 2, 3] * ``` * * @since 4.0.0 * @category Tracing */ export const withSpan: { /** * Wraps the stream with a new span for tracing. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.fromArray([1, 2, 3]).pipe(Stream.withSpan("numbers")) * * Effect.runPromise( * Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * ) * // [1, 2, 3] * ``` * * @since 4.0.0 * @category Tracing */ (name: string, options?: SpanOptions): (self: Stream) => Stream> /** * Wraps the stream with a new span for tracing. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.fromArray([1, 2, 3]).pipe(Stream.withSpan("numbers")) * * Effect.runPromise( * Effect.gen(function*() { * const values = yield* Stream.runCollect(stream) * yield* Console.log(values) * }) * ) * // [1, 2, 3] * ``` * * @since 4.0.0 * @category Tracing */ (self: Stream, name: string, options?: SpanOptions): Stream> } = function() { const dataFirst = isStream(arguments[0]) const name = dataFirst ? arguments[1] : arguments[0] const options = addSpanStackTrace(dataFirst ? arguments[2] : arguments[1]) if (dataFirst) { const self = arguments[0] as Stream return fromChannel(Channel.withSpan(self.channel, name, options)) } return (self: Stream) => fromChannel(Channel.withSpan(self.channel, name, options)) } as any /** * Provides the entry point for do-notation style stream composition. * * @example * ```ts * import { Console, Effect, Stream, pipe } from "effect" * * const program = pipe( * Stream.Do, * Stream.bind("value", () => Stream.fromArray([1, 2])), * Stream.let("next", ({ value }) => value + 1) * ) * * const effect = Effect.gen(function*() { * const collected = yield* Stream.runCollect(program) * yield* Console.log(collected) * }) * * Effect.runPromise(effect) * //=> [{ value: 1, next: 2 }, { value: 2, next: 3 }] * ``` * * @since 4.0.0 * @category Do Notation */ export const Do: Stream<{}> = succeed({}) const let_: { ( name: Exclude, f: (a: NoInfer) => B ): (self: Stream) => Stream<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E, R> ( self: Stream, name: Exclude, f: (a: NoInfer) => B ): Stream<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E, R> } = dual(3, ( self: Stream, name: Exclude, f: (a: NoInfer) => B ): Stream<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E, R> => map(self, (a) => ({ ...a, [name]: f(a) } as any))) export { /** * Adds a computed field to the current Do-notation record. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.Do.pipe( * Stream.let("x", () => 2), * Stream.let("y", ({ x }) => x * 3) * ) * * const program = Effect.gen(function*() { * const records = yield* Stream.runCollect(stream) * yield* Console.log(records) * }) * * Effect.runPromise(program) * // [{ x: 2, y: 6 }] * ``` * * @since 4.0.0 * @category Do Notation */ let_ as let } /** * Binds the result of a stream to a field in the do-notation record. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Stream.Do.pipe( * Stream.bind("a", () => Stream.make(1, 2)), * Stream.bind("b", ({ a }) => Stream.succeed(a + 1)) * ) * * const result = Stream.runCollect(program) * * Effect.runPromise(Effect.flatMap(result, Console.log)) * // [{ a: 1, b: 2 }, { a: 2, b: 3 }] * ``` * * @since 4.0.0 * @category Do Notation */ export const bind: { /** * Binds the result of a stream to a field in the do-notation record. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Stream.Do.pipe( * Stream.bind("a", () => Stream.make(1, 2)), * Stream.bind("b", ({ a }) => Stream.succeed(a + 1)) * ) * * const result = Stream.runCollect(program) * * Effect.runPromise(Effect.flatMap(result, Console.log)) * // [{ a: 1, b: 2 }, { a: 2, b: 3 }] * ``` * * @since 4.0.0 * @category Do Notation */ ( tag: Exclude, f: (_: NoInfer) => Stream, options?: { readonly concurrency?: number | "unbounded" | undefined readonly bufferSize?: number | undefined } | undefined ): (self: Stream) => Stream<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E2 | E, R2 | R> /** * Binds the result of a stream to a field in the do-notation record. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Stream.Do.pipe( * Stream.bind("a", () => Stream.make(1, 2)), * Stream.bind("b", ({ a }) => Stream.succeed(a + 1)) * ) * * const result = Stream.runCollect(program) * * Effect.runPromise(Effect.flatMap(result, Console.log)) * // [{ a: 1, b: 2 }, { a: 2, b: 3 }] * ``` * * @since 4.0.0 * @category Do Notation */ ( self: Stream, tag: Exclude, f: (_: NoInfer) => Stream, options?: { readonly concurrency?: number | "unbounded" | undefined readonly bufferSize?: number | undefined } | undefined ): Stream<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E | E2, R | R2> } = dual((args) => isStream(args[0]), ( self: Stream, tag: Exclude, f: (_: NoInfer) => Stream, options?: { readonly concurrency?: number | "unbounded" | undefined readonly bufferSize?: number | undefined } | undefined ): Stream<{ [K in N | keyof A]: K extends keyof A ? A[K] : B }, E | E2, R | R2> => flatMap(self, (a) => map(f(a), (b) => ({ ...a, [tag]: b } as any)), options)) /** * Binds an Effect-produced value into the do-notation record for each stream element. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.Do.pipe( * Stream.bind("value", () => Stream.make(1, 2)), * Stream.bindEffect("double", ({ value }) => Effect.succeed(value * 2)) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // [{ value: 1, double: 2 }, { value: 2, double: 4 }] * ``` * * @since 4.0.0 * @category Do Notation */ export const bindEffect: { /** * Binds an Effect-produced value into the do-notation record for each stream element. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.Do.pipe( * Stream.bind("value", () => Stream.make(1, 2)), * Stream.bindEffect("double", ({ value }) => Effect.succeed(value * 2)) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // [{ value: 1, double: 2 }, { value: 2, double: 4 }] * ``` * * @since 4.0.0 * @category Do Notation */ ( tag: Exclude, f: (_: NoInfer) => Effect.Effect, options?: { readonly concurrency?: number | "unbounded" | undefined readonly bufferSize?: number | undefined readonly unordered?: boolean | undefined } ): (self: Stream) => Stream<{ [K in keyof A | N]: K extends keyof A ? A[K] : B }, E | E2, R | R2> /** * Binds an Effect-produced value into the do-notation record for each stream element. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.Do.pipe( * Stream.bind("value", () => Stream.make(1, 2)), * Stream.bindEffect("double", ({ value }) => Effect.succeed(value * 2)) * ) * * const program = Effect.gen(function*() { * const result = yield* Stream.runCollect(stream) * yield* Console.log(result) * }) * * Effect.runPromise(program) * // [{ value: 1, double: 2 }, { value: 2, double: 4 }] * ``` * * @since 4.0.0 * @category Do Notation */ ( self: Stream, tag: Exclude, f: (_: NoInfer) => Effect.Effect, options?: { readonly concurrency?: number | "unbounded" | undefined readonly bufferSize?: number | undefined readonly unordered?: boolean | undefined } ): Stream<{ [K in keyof A | N]: K extends keyof A ? A[K] : B }, E | E2, R | R2> } = dual((args) => isStream(args[0]), ( self: Stream, tag: Exclude, f: (_: NoInfer) => Effect.Effect, options?: { readonly concurrency?: number | "unbounded" | undefined readonly bufferSize?: number | undefined readonly unordered?: boolean | undefined } | undefined ): Stream<{ [K in keyof A | N]: K extends keyof A ? A[K] : B }, E | E2, R | R2> => mapEffect(self, (a) => Effect.map(f(a), (b) => ({ ...a, [tag]: b } as any)), options)) /** * Maps each element into a record keyed by the provided name. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3).pipe(Stream.bindTo("value")) * * const program = Stream.runCollect(stream).pipe(Effect.flatMap(Console.log)) * * Effect.runPromise(program) * // [{ value: 1 }, { value: 2 }, { value: 3 }] * ``` * * @category Do Notation * @since 4.0.0 */ export const bindTo: { /** * Maps each element into a record keyed by the provided name. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3).pipe(Stream.bindTo("value")) * * const program = Stream.runCollect(stream).pipe(Effect.flatMap(Console.log)) * * Effect.runPromise(program) * // [{ value: 1 }, { value: 2 }, { value: 3 }] * ``` * * @category Do Notation * @since 4.0.0 */ (name: N): (self: Stream) => Stream<{ [K in N]: A }, E, R> /** * Maps each element into a record keyed by the provided name. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3).pipe(Stream.bindTo("value")) * * const program = Stream.runCollect(stream).pipe(Effect.flatMap(Console.log)) * * Effect.runPromise(program) * // [{ value: 1 }, { value: 2 }, { value: 3 }] * ``` * * @category Do Notation * @since 4.0.0 */ (self: Stream, name: N): Stream<{ [K in N]: A }, E, R> } = dual(2, ( self: Stream, name: N ): Stream<{ [K in N]: A }, E, R> => map(self, (a) => ({ [name]: a } as any))) /** * Runs a stream with a sink and returns the sink result. * * @example * ```ts * import { Console, Effect, Sink, Stream } from "effect" * * const program = Stream.run(Stream.make(1, 2, 3), Sink.sum) * * Effect.runPromise(Effect.flatMap(program, Console.log)) * // 6 * ``` * * @since 2.0.0 * @category Destructors */ export const run: { /** * Runs a stream with a sink and returns the sink result. * * @example * ```ts * import { Console, Effect, Sink, Stream } from "effect" * * const program = Stream.run(Stream.make(1, 2, 3), Sink.sum) * * Effect.runPromise(Effect.flatMap(program, Console.log)) * // 6 * ``` * * @since 2.0.0 * @category Destructors */ (sink: Sink.Sink): (self: Stream) => Effect.Effect /** * Runs a stream with a sink and returns the sink result. * * @example * ```ts * import { Console, Effect, Sink, Stream } from "effect" * * const program = Stream.run(Stream.make(1, 2, 3), Sink.sum) * * Effect.runPromise(Effect.flatMap(program, Console.log)) * // 6 * ``` * * @since 2.0.0 * @category Destructors */ (self: Stream, sink: Sink.Sink): Effect.Effect } = dual(2, ( self: Stream, sink: Sink.Sink ): Effect.Effect => Effect.scopedWith((scope) => Channel.toPullScoped(self.channel, scope).pipe( Effect.flatMap((upstream) => sink.transform(upstream as any, scope)), Effect.map(([a]) => a) ) )) /** * Runs the stream and collects all elements into an array. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3, 4, 5) * * const program = Effect.gen(function*() { * const collected = yield* Stream.runCollect(stream) * yield* Console.log(collected) * }) * * Effect.runPromise(program) * // [1, 2, 3, 4, 5] * ``` * * @since 2.0.0 * @category Destructors */ export const runCollect = (self: Stream): Effect.Effect, E, R> => Channel.runFold( self.channel, () => [] as Array, (acc, chunk) => { for (let i = 0; i < chunk.length; i++) { acc.push(chunk[i]) } return acc } ) /** * Runs the stream and returns the number of elements emitted. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3, 4, 5) * * const program = Effect.gen(function* () { * const count = yield* Stream.runCount(stream) * yield* Console.log(count) * }) * * Effect.runPromise(program) * // 5 * ``` * * @since 2.0.0 * @category Destructors */ export const runCount = (self: Stream): Effect.Effect => Channel.runFold(self.channel, () => 0, (acc, chunk) => acc + chunk.length) /** * Runs the stream and returns the numeric sum of its elements. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const total = yield* Stream.runSum(Stream.make(1, 2, 3)) * yield* Console.log(total) * }) * * Effect.runPromise(program) * // 6 * ``` * * @since 2.0.0 * @category Destructors */ export const runSum = (self: Stream): Effect.Effect => Channel.runFold(self.channel, () => 0, (acc, chunk) => { for (let i = 0; i < chunk.length; i++) { acc += chunk[i] } return acc }) /** * Runs the stream and folds elements using a pure reducer. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const total = yield* Stream.runFold( * Stream.make(1, 2, 3), * () => 0, * (acc, n) => acc + n * ) * yield* Console.log(total) * }) * * Effect.runPromise(program) * // 6 * ``` * * @since 2.0.0 * @category Destructors */ export const runFold: { /** * Runs the stream and folds elements using a pure reducer. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const total = yield* Stream.runFold( * Stream.make(1, 2, 3), * () => 0, * (acc, n) => acc + n * ) * yield* Console.log(total) * }) * * Effect.runPromise(program) * // 6 * ``` * * @since 2.0.0 * @category Destructors */ (initial: LazyArg, f: (acc: Z, a: A) => Z): ( self: Stream ) => Effect.Effect /** * Runs the stream and folds elements using a pure reducer. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const total = yield* Stream.runFold( * Stream.make(1, 2, 3), * () => 0, * (acc, n) => acc + n * ) * yield* Console.log(total) * }) * * Effect.runPromise(program) * // 6 * ``` * * @since 2.0.0 * @category Destructors */ (self: Stream, initial: LazyArg, f: (acc: Z, a: A) => Z): Effect.Effect } = dual(3, ( self: Stream, initial: LazyArg, f: (acc: Z, a: A) => Z ): Effect.Effect => Channel.runFold(self.channel, initial, (acc, arr) => { for (let i = 0; i < arr.length; i++) { acc = f(acc, arr[i]) } return acc })) /** * Runs the stream and folds elements using an effectful reducer. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const total = yield* Stream.runFoldEffect( * Stream.make(1, 2, 3), * () => 0, * (acc, n) => Effect.succeed(acc + n) * ) * yield* Console.log(total) * }) * * Effect.runPromise(program) * // 6 * ``` * * @since 2.0.0 * @category Destructors */ export const runFoldEffect: { /** * Runs the stream and folds elements using an effectful reducer. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const total = yield* Stream.runFoldEffect( * Stream.make(1, 2, 3), * () => 0, * (acc, n) => Effect.succeed(acc + n) * ) * yield* Console.log(total) * }) * * Effect.runPromise(program) * // 6 * ``` * * @since 2.0.0 * @category Destructors */ (initial: LazyArg, f: (acc: Z, a: A) => Effect.Effect): ( self: Stream ) => Effect.Effect /** * Runs the stream and folds elements using an effectful reducer. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const total = yield* Stream.runFoldEffect( * Stream.make(1, 2, 3), * () => 0, * (acc, n) => Effect.succeed(acc + n) * ) * yield* Console.log(total) * }) * * Effect.runPromise(program) * // 6 * ``` * * @since 2.0.0 * @category Destructors */ ( self: Stream, initial: LazyArg, f: (acc: Z, a: A) => Effect.Effect ): Effect.Effect } = dual(3, ( self: Stream, initial: LazyArg, f: (acc: Z, a: A) => Effect.Effect ): Effect.Effect => Channel.runFoldEffect(self.channel, initial, (acc, arr) => { let i = 0 let s = acc return Effect.map( Effect.whileLoop({ while: () => i < arr.length, body: () => f(s, arr[i]), step(z) { s = z i++ } }), () => s ) })) /** * Runs the stream and returns the first element as an `Option`. * * @example * ```ts * import { Console, Effect, Option, Stream } from "effect" * * const program = Effect.gen(function*() { * const head = yield* Stream.runHead(Stream.make(1, 2, 3)) * yield* Console.log(Option.getOrThrow(head)) * }) * * Effect.runPromise(program) * // 1 * ``` * * @since 2.0.0 * @category Destructors */ export const runHead = (self: Stream): Effect.Effect, E, R> => Effect.map(Channel.runHead(self.channel), Option.map(Arr.getUnsafe(0))) /** * Runs the stream and returns the last element as an `Option`. * * @since 2.0.0 * @category Destructors */ export const runLast = (self: Stream): Effect.Effect, E, R> => Effect.map(Channel.runLast(self.channel), Option.map(Arr.lastNonEmpty)) /** * Runs the provided effectful callback for each element of the stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3) * * const program = Effect.gen(function*() { * yield* Stream.runForEach(stream, (n) => Console.log(`Processing: ${n}`)) * }) * * Effect.runPromise(program) * // Processing: 1 * // Processing: 2 * // Processing: 3 * ``` * * @since 2.0.0 * @category Destructors */ export const runForEach: { /** * Runs the provided effectful callback for each element of the stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3) * * const program = Effect.gen(function*() { * yield* Stream.runForEach(stream, (n) => Console.log(`Processing: ${n}`)) * }) * * Effect.runPromise(program) * // Processing: 1 * // Processing: 2 * // Processing: 3 * ``` * * @since 2.0.0 * @category Destructors */ (f: (a: A) => Effect.Effect): (self: Stream) => Effect.Effect /** * Runs the provided effectful callback for each element of the stream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3) * * const program = Effect.gen(function*() { * yield* Stream.runForEach(stream, (n) => Console.log(`Processing: ${n}`)) * }) * * Effect.runPromise(program) * // Processing: 1 * // Processing: 2 * // Processing: 3 * ``` * * @since 2.0.0 * @category Destructors */ (self: Stream, f: (a: A) => Effect.Effect): Effect.Effect } = dual(2, ( self: Stream, f: (a: A) => Effect.Effect ): Effect.Effect => Channel.runForEach(self.channel, (arr) => { let i = 0 return Effect.whileLoop({ while: () => i < arr.length, body: () => f(arr[i++]), step: constVoid }) })) /** * Runs the stream, applying the effectful predicate to each element and * stopping when it returns `false`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 2, 3, 4, 5) * * yield* Stream.runForEachWhile(stream, (n) => * Effect.gen(function*() { * yield* Console.log(`Processing: ${n}`) * return n < 3 * }) * ) * }) * * Effect.runPromise(program) * // Processing: 1 * // Processing: 2 * // Processing: 3 * ``` * * @since 2.0.0 * @category Destructors */ export const runForEachWhile: { /** * Runs the stream, applying the effectful predicate to each element and * stopping when it returns `false`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 2, 3, 4, 5) * * yield* Stream.runForEachWhile(stream, (n) => * Effect.gen(function*() { * yield* Console.log(`Processing: ${n}`) * return n < 3 * }) * ) * }) * * Effect.runPromise(program) * // Processing: 1 * // Processing: 2 * // Processing: 3 * ``` * * @since 2.0.0 * @category Destructors */ (f: (a: A) => Effect.Effect): (self: Stream) => Effect.Effect /** * Runs the stream, applying the effectful predicate to each element and * stopping when it returns `false`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 2, 3, 4, 5) * * yield* Stream.runForEachWhile(stream, (n) => * Effect.gen(function*() { * yield* Console.log(`Processing: ${n}`) * return n < 3 * }) * ) * }) * * Effect.runPromise(program) * // Processing: 1 * // Processing: 2 * // Processing: 3 * ``` * * @since 2.0.0 * @category Destructors */ (self: Stream, f: (a: A) => Effect.Effect): Effect.Effect } = dual(2, ( self: Stream, f: (a: A) => Effect.Effect ): Effect.Effect => Channel.runForEachWhile(self.channel, (arr) => { let done = false let i = 0 return Effect.map( Effect.whileLoop({ while: () => !done && i < arr.length, body: () => f(arr[i]), step(b) { i++ if (!b) done = true } }), () => done ) })) /** * Consumes the stream in chunks, passing each non-empty array to the callback. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3, 4, 5) * const program = Effect.gen(function*() { * yield* Stream.runForEachArray( * stream, * (chunk) => Console.log(`Processing chunk: ${chunk.join(", ")}`) * ) * }) * * Effect.runPromise(program) * // Processing chunk: 1, 2, 3, 4, 5 * ``` * * @since 2.0.0 * @category Destructors */ export const runForEachArray: { /** * Consumes the stream in chunks, passing each non-empty array to the callback. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3, 4, 5) * const program = Effect.gen(function*() { * yield* Stream.runForEachArray( * stream, * (chunk) => Console.log(`Processing chunk: ${chunk.join(", ")}`) * ) * }) * * Effect.runPromise(program) * // Processing chunk: 1, 2, 3, 4, 5 * ``` * * @since 2.0.0 * @category Destructors */ (f: (a: Arr.NonEmptyReadonlyArray) => Effect.Effect): (self: Stream) => Effect.Effect /** * Consumes the stream in chunks, passing each non-empty array to the callback. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3, 4, 5) * const program = Effect.gen(function*() { * yield* Stream.runForEachArray( * stream, * (chunk) => Console.log(`Processing chunk: ${chunk.join(", ")}`) * ) * }) * * Effect.runPromise(program) * // Processing chunk: 1, 2, 3, 4, 5 * ``` * * @since 2.0.0 * @category Destructors */ ( self: Stream, f: (a: Arr.NonEmptyReadonlyArray) => Effect.Effect ): Effect.Effect } = dual(2, ( self: Stream, f: (a: Arr.NonEmptyReadonlyArray) => Effect.Effect ): Effect.Effect => Channel.runForEach(self.channel, f)) /** * Runs the stream for its effects, discarding emitted elements. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const program = Effect.gen(function*() { * const stream = Stream.make(1, 2, 3).pipe( * Stream.mapEffect((n) => Console.log(`Processing: ${n}`)) * ) * * yield* Stream.runDrain(stream) * }) * * Effect.runPromise(program) * // Processing: 1 * // Processing: 2 * // Processing: 3 * ``` * * @since 2.0.0 * @category Destructors */ export const runDrain = (self: Stream): Effect.Effect => Channel.runDrain(self.channel) /** * Returns a scoped pull for manually consuming the stream's output chunks. * * The pull fails with `Cause.Done` when the stream ends and with the stream * error on failure. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3) * * const program = Effect.scoped( * Effect.gen(function*() { * const pull = yield* Stream.toPull(stream) * const chunk = yield* pull * yield* Console.log(chunk) * }) * ) * * Effect.runPromise(program) * // [1, 2, 3] * ``` * * @since 2.0.0 * @category Destructors */ export const toPull = ( self: Stream ): Effect.Effect, E>, never, R | Scope.Scope> => Channel.toPull(self.channel) /** * Concatenates all emitted strings into a single string. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make("Hello", " ", "World", "!") * const program = Effect.gen(function*() { * const text = yield* Stream.mkString(stream) * yield* Console.log(text) * }) * * Effect.runPromise(program) * // Hello World! * ``` * * @since 2.0.0 * @category Destructors */ export const mkString = (self: Stream): Effect.Effect => Channel.runFold( self.channel, () => "", (acc, chunk) => acc + chunk.join("") ) /** * Concatenates the stream's `Uint8Array` chunks into a single `Uint8Array`. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(new Uint8Array([1, 2]), new Uint8Array([3, 4])) * const program = Effect.gen(function*() { * const bytes = yield* Stream.mkUint8Array(stream) * yield* Console.log([...bytes]) * }) * * Effect.runPromise(program) * // [1, 2, 3, 4] * ``` * * @since 4.0.0 * @category Destructors */ export const mkUint8Array = (self: Stream): Effect.Effect => Effect.map( Channel.runFold( self.channel, (): { bytes: number readonly arrays: Array } => ({ bytes: 0, arrays: [] }), (acc, chunk) => { for (let i = 0; i < chunk.length; i++) { acc.bytes += chunk[i].length acc.arrays.push(chunk[i]) } return acc } ), ({ arrays, bytes }) => { const result = new Uint8Array(bytes) let offset = 0 for (let i = 0; i < arrays.length; i++) { const array = arrays[i] result.set(array, offset) offset += array.length } return result } ) /** * Converts the stream to a `ReadableStream` using the provided services. * * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. * * @example * ```ts * import { Context, Stream } from "effect" * * const stream = Stream.make(1, 2, 3, 4, 5) * const readableStream = Stream.toReadableStreamWith(stream, Context.empty()) * ``` * * @since 2.0.0 * @category Destructors */ export const toReadableStreamWith = dual< /** * Converts the stream to a `ReadableStream` using the provided services. * * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. * * @example * ```ts * import { Context, Stream } from "effect" * * const stream = Stream.make(1, 2, 3, 4, 5) * const readableStream = Stream.toReadableStreamWith(stream, Context.empty()) * ``` * * @since 2.0.0 * @category Destructors */ ( context: Context.Context, options?: { readonly strategy?: QueuingStrategy | undefined } ) => (self: Stream) => ReadableStream, /** * Converts the stream to a `ReadableStream` using the provided services. * * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. * * @example * ```ts * import { Context, Stream } from "effect" * * const stream = Stream.make(1, 2, 3, 4, 5) * const readableStream = Stream.toReadableStreamWith(stream, Context.empty()) * ``` * * @since 2.0.0 * @category Destructors */ ( self: Stream, context: Context.Context, options?: { readonly strategy?: QueuingStrategy | undefined } ) => ReadableStream >( (args) => isStream(args[0]), ( self: Stream, context: Context.Context, options?: { readonly strategy?: QueuingStrategy | undefined } ): ReadableStream => { let currentResolve: (() => void) | undefined = undefined let fiber: Fiber.Fiber | undefined = undefined const latch = Latch.makeUnsafe(false) return new ReadableStream({ start(controller) { fiber = Effect.runFork(Effect.provideContext( runForEachArray(self, (chunk) => latch.whenOpen(Effect.sync(() => { latch.closeUnsafe() for (let i = 0; i < chunk.length; i++) { controller.enqueue(chunk[i]) } currentResolve!() currentResolve = undefined }))), context )) fiber.addObserver((exit) => { if (exit._tag === "Failure") { controller.error(Cause.squash(exit.cause)) } else { controller.close() } }) }, pull() { return new Promise((resolve) => { currentResolve = resolve latch.openUnsafe() }) }, cancel() { if (!fiber) return return Effect.runPromise(Effect.asVoid(Fiber.interrupt(fiber))) } }, options?.strategy) } ) /** * Converts a stream to a `ReadableStream`. * * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. * * @example * ```ts * import { Stream } from "effect" * * const readableStream = Stream.toReadableStream(Stream.make(1, 2, 3)) * const reader = readableStream.getReader() * ``` * * @since 2.0.0 * @category Destructors */ export const toReadableStream: { /** * Converts a stream to a `ReadableStream`. * * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. * * @example * ```ts * import { Stream } from "effect" * * const readableStream = Stream.toReadableStream(Stream.make(1, 2, 3)) * const reader = readableStream.getReader() * ``` * * @since 2.0.0 * @category Destructors */ (options?: { readonly strategy?: QueuingStrategy | undefined }): ( self: Stream ) => ReadableStream /** * Converts a stream to a `ReadableStream`. * * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. * * @example * ```ts * import { Stream } from "effect" * * const readableStream = Stream.toReadableStream(Stream.make(1, 2, 3)) * const reader = readableStream.getReader() * ``` * * @since 2.0.0 * @category Destructors */ ( self: Stream, options?: { readonly strategy?: QueuingStrategy | undefined } ): ReadableStream } = dual( (args) => isStream(args[0]), ( self: Stream, options?: { readonly strategy?: QueuingStrategy | undefined } ): ReadableStream => toReadableStreamWith(self, Context.empty(), options) ) /** * Creates an Effect that builds a ReadableStream from the stream. * * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3, 4, 5) * * const effect = Effect.gen(function*() { * const readableStream = yield* Stream.toReadableStreamEffect(stream) * yield* Console.log(readableStream instanceof ReadableStream) // true * }) * * Effect.runPromise(effect) * ``` * * @since 2.0.0 * @category Destructors */ export const toReadableStreamEffect: { /** * Creates an Effect that builds a ReadableStream from the stream. * * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3, 4, 5) * * const effect = Effect.gen(function*() { * const readableStream = yield* Stream.toReadableStreamEffect(stream) * yield* Console.log(readableStream instanceof ReadableStream) // true * }) * * Effect.runPromise(effect) * ``` * * @since 2.0.0 * @category Destructors */ (options?: { readonly strategy?: QueuingStrategy | undefined }): ( self: Stream ) => Effect.Effect, never, R> /** * Creates an Effect that builds a ReadableStream from the stream. * * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3, 4, 5) * * const effect = Effect.gen(function*() { * const readableStream = yield* Stream.toReadableStreamEffect(stream) * yield* Console.log(readableStream instanceof ReadableStream) // true * }) * * Effect.runPromise(effect) * ``` * * @since 2.0.0 * @category Destructors */ ( self: Stream, options?: { readonly strategy?: QueuingStrategy | undefined } ): Effect.Effect, never, R> } = dual( (args) => isStream(args[0]), ( self: Stream, options?: { readonly strategy?: QueuingStrategy | undefined } ): Effect.Effect, never, R> => Effect.map( Effect.context(), (context) => toReadableStreamWith(self, context, options) ) ) /** * Converts the stream to an `AsyncIterable` using the provided services. * * @example * ```ts * import { Context, Stream } from "effect" * * const stream = Stream.make(1, 2, 3) * const iterable = Stream.toAsyncIterableWith(stream, Context.empty()) * * const collect = async () => { * const results: Array = [] * for await (const value of iterable) { * results.push(value) * } * return results * } * ``` * * @since 2.0.0 * @category Destructors */ export const toAsyncIterableWith: { /** * Converts the stream to an `AsyncIterable` using the provided services. * * @example * ```ts * import { Context, Stream } from "effect" * * const stream = Stream.make(1, 2, 3) * const iterable = Stream.toAsyncIterableWith(stream, Context.empty()) * * const collect = async () => { * const results: Array = [] * for await (const value of iterable) { * results.push(value) * } * return results * } * ``` * * @since 2.0.0 * @category Destructors */ (context: Context.Context): (self: Stream) => AsyncIterable /** * Converts the stream to an `AsyncIterable` using the provided services. * * @example * ```ts * import { Context, Stream } from "effect" * * const stream = Stream.make(1, 2, 3) * const iterable = Stream.toAsyncIterableWith(stream, Context.empty()) * * const collect = async () => { * const results: Array = [] * for await (const value of iterable) { * results.push(value) * } * return results * } * ``` * * @since 2.0.0 * @category Destructors */ (self: Stream, context: Context.Context): AsyncIterable } = dual( 2, ( self: Stream, context: Context.Context ): AsyncIterable => ({ [Symbol.asyncIterator]() { const runPromise = Effect.runPromiseWith(context) const runPromiseExit = Effect.runPromiseExitWith(context) const scope = Scope.makeUnsafe() let pull: Pull.Pull, E, void, R> | undefined let currentIter: Iterator | undefined return { async next(): Promise> { if (currentIter) { const next = currentIter.next() if (!next.done) return next currentIter = undefined } pull ??= await runPromise(Channel.toPullScoped(self.channel, scope)) const exit = await runPromiseExit(pull) if (Exit.isSuccess(exit)) { currentIter = exit.value[Symbol.iterator]() return currentIter.next() } else if (Pull.isDoneCause(exit.cause)) { return { done: true, value: undefined } } throw Cause.squash(exit.cause) }, return(_) { return runPromise(Effect.as( Scope.close(scope, Exit.void), { done: true, value: undefined } )) } } } }) ) /** * Creates an effect that yields an `AsyncIterable` using the current services. * * @example * ```ts * import { Console, Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3) * * const program = Effect.gen(function*() { * const iterable = yield* Stream.toAsyncIterableEffect(stream) * const values = yield* Effect.promise(async () => { * const collected: Array = [] * for await (const value of iterable) { * collected.push(value) * } * return collected * }) * yield* Console.log(values) * }) * * Effect.runPromise(program) * //=> [ 1, 2, 3 ] * ``` * * @since 2.0.0 * @category Destructors */ export const toAsyncIterableEffect = (self: Stream): Effect.Effect, never, R> => Effect.map( Effect.context(), (context) => toAsyncIterableWith(self, context) ) /** * Converts a stream to an `AsyncIterable` for `for await...of` consumption. * * @example * ```ts * import { Effect, Stream } from "effect" * * const stream = Stream.make(1, 2, 3) * * const program = Effect.gen(function* () { * const iterable = Stream.toAsyncIterable(stream) * const results = yield* Effect.promise(async () => { * const values: Array = [] * for await (const value of iterable) { * values.push(value) * } * return values * }) * return results * }) * ``` * * @since 2.0.0 * @category Destructors */ export const toAsyncIterable = (self: Stream): AsyncIterable => toAsyncIterableWith(self, Context.empty()) /** * Runs the stream, publishing elements into the provided PubSub. * * `shutdownOnEnd` controls whether the PubSub is shut down when the stream ends. * It only shuts down when set to `true`. * * @example * ```ts * import { Console, Effect, PubSub, Stream } from "effect" * * const program = Effect.scoped(Effect.gen(function* () { * const pubsub = yield* PubSub.unbounded() * const subscription = yield* PubSub.subscribe(pubsub) * * yield* Stream.runIntoPubSub(Stream.fromIterable([1, 2]), pubsub) * * const first = yield* PubSub.take(subscription) * const second = yield* PubSub.take(subscription) * * yield* Console.log(first) * yield* Console.log(second) * })) * * Effect.runPromise(program) * //=> 1 * //=> 2 * ``` * * @since 2.0.0 * @category Destructors */ export const runIntoPubSub: { /** * Runs the stream, publishing elements into the provided PubSub. * * `shutdownOnEnd` controls whether the PubSub is shut down when the stream ends. * It only shuts down when set to `true`. * * @example * ```ts * import { Console, Effect, PubSub, Stream } from "effect" * * const program = Effect.scoped(Effect.gen(function* () { * const pubsub = yield* PubSub.unbounded() * const subscription = yield* PubSub.subscribe(pubsub) * * yield* Stream.runIntoPubSub(Stream.fromIterable([1, 2]), pubsub) * * const first = yield* PubSub.take(subscription) * const second = yield* PubSub.take(subscription) * * yield* Console.log(first) * yield* Console.log(second) * })) * * Effect.runPromise(program) * //=> 1 * //=> 2 * ``` * * @since 2.0.0 * @category Destructors */ ( pubsub: PubSub.PubSub, options?: { readonly shutdownOnEnd?: boolean | undefined } | undefined ): (self: Stream) => Effect.Effect /** * Runs the stream, publishing elements into the provided PubSub. * * `shutdownOnEnd` controls whether the PubSub is shut down when the stream ends. * It only shuts down when set to `true`. * * @example * ```ts * import { Console, Effect, PubSub, Stream } from "effect" * * const program = Effect.scoped(Effect.gen(function* () { * const pubsub = yield* PubSub.unbounded() * const subscription = yield* PubSub.subscribe(pubsub) * * yield* Stream.runIntoPubSub(Stream.fromIterable([1, 2]), pubsub) * * const first = yield* PubSub.take(subscription) * const second = yield* PubSub.take(subscription) * * yield* Console.log(first) * yield* Console.log(second) * })) * * Effect.runPromise(program) * //=> 1 * //=> 2 * ``` * * @since 2.0.0 * @category Destructors */ ( self: Stream, pubsub: PubSub.PubSub, options?: { readonly shutdownOnEnd?: boolean | undefined } | undefined ): Effect.Effect } = dual((args) => isStream(args[0]), ( self: Stream, pubsub: PubSub.PubSub, options?: { readonly shutdownOnEnd?: boolean | undefined } | undefined ): Effect.Effect => Channel.runIntoPubSubArray(self.channel, pubsub, options)) /** * Converts a stream to a PubSub for concurrent consumption. * * `shutdownOnEnd` indicates whether the PubSub should be shut down when the * stream ends. By default this is `true`. * * @example * ```ts * import { Console, Effect, PubSub, Stream } from "effect" * * const program = Effect.scoped(Effect.gen(function* () { * const pubsub = yield* Stream.fromArray([1, 2]).pipe( * Stream.toPubSub({ capacity: 8 }) * ) * const subscription = yield* PubSub.subscribe(pubsub) * const first = yield* PubSub.take(subscription) * * yield* Console.log(first) * })) * ``` * * @since 2.0.0 * @category Destructors */ export const toPubSub: { /** * Converts a stream to a PubSub for concurrent consumption. * * `shutdownOnEnd` indicates whether the PubSub should be shut down when the * stream ends. By default this is `true`. * * @example * ```ts * import { Console, Effect, PubSub, Stream } from "effect" * * const program = Effect.scoped(Effect.gen(function* () { * const pubsub = yield* Stream.fromArray([1, 2]).pipe( * Stream.toPubSub({ capacity: 8 }) * ) * const subscription = yield* PubSub.subscribe(pubsub) * const first = yield* PubSub.take(subscription) * * yield* Console.log(first) * })) * ``` * * @since 2.0.0 * @category Destructors */ ( 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 } ): (self: Stream) => Effect.Effect, never, R | Scope.Scope> /** * Converts a stream to a PubSub for concurrent consumption. * * `shutdownOnEnd` indicates whether the PubSub should be shut down when the * stream ends. By default this is `true`. * * @example * ```ts * import { Console, Effect, PubSub, Stream } from "effect" * * const program = Effect.scoped(Effect.gen(function* () { * const pubsub = yield* Stream.fromArray([1, 2]).pipe( * Stream.toPubSub({ capacity: 8 }) * ) * const subscription = yield* PubSub.subscribe(pubsub) * const first = yield* PubSub.take(subscription) * * yield* Console.log(first) * })) * ``` * * @since 2.0.0 * @category Destructors */ ( self: Stream, 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, never, R | Scope.Scope> } = dual( 2, ( self: Stream, 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, never, R | Scope.Scope> => Channel.toPubSubArray(self.channel, options) ) /** * Converts a stream to a PubSub for concurrent consumption. * * `Take` values include the stream's end and failure signals. * * @example * ```ts * import { Console, Effect, PubSub, Stream } from "effect" * * const program = Effect.gen(function* () { * const pubsub = yield* Stream.fromArray([1, 2, 3]).pipe( * Stream.toPubSubTake({ capacity: 8 }) * ) * const subscription = yield* PubSub.subscribe(pubsub) * const take = yield* PubSub.take(subscription) * * if (Array.isArray(take)) { * yield* Console.log(take) * } * }) * ``` * * @since 4.0.0 * @category Destructors */ export const toPubSubTake: { /** * Converts a stream to a PubSub for concurrent consumption. * * `Take` values include the stream's end and failure signals. * * @example * ```ts * import { Console, Effect, PubSub, Stream } from "effect" * * const program = Effect.gen(function* () { * const pubsub = yield* Stream.fromArray([1, 2, 3]).pipe( * Stream.toPubSubTake({ capacity: 8 }) * ) * const subscription = yield* PubSub.subscribe(pubsub) * const take = yield* PubSub.take(subscription) * * if (Array.isArray(take)) { * yield* Console.log(take) * } * }) * ``` * * @since 4.0.0 * @category Destructors */ ( options: { readonly capacity: "unbounded" readonly replay?: number | undefined } | { readonly capacity: number readonly strategy?: "dropping" | "sliding" | "suspend" | undefined readonly replay?: number | undefined } ): (self: Stream) => Effect.Effect>, never, R | Scope.Scope> /** * Converts a stream to a PubSub for concurrent consumption. * * `Take` values include the stream's end and failure signals. * * @example * ```ts * import { Console, Effect, PubSub, Stream } from "effect" * * const program = Effect.gen(function* () { * const pubsub = yield* Stream.fromArray([1, 2, 3]).pipe( * Stream.toPubSubTake({ capacity: 8 }) * ) * const subscription = yield* PubSub.subscribe(pubsub) * const take = yield* PubSub.take(subscription) * * if (Array.isArray(take)) { * yield* Console.log(take) * } * }) * ``` * * @since 4.0.0 * @category Destructors */ ( self: Stream, options: { readonly capacity: "unbounded" readonly replay?: number | undefined } | { readonly capacity: number readonly strategy?: "dropping" | "sliding" | "suspend" | undefined readonly replay?: number | undefined } ): Effect.Effect>, never, R | Scope.Scope> } = dual( 2, ( self: Stream, options: { readonly capacity: "unbounded" readonly replay?: number | undefined } | { readonly capacity: number readonly strategy?: "dropping" | "sliding" | "suspend" | undefined readonly replay?: number | undefined } ): Effect.Effect>, never, R | Scope.Scope> => Channel.toPubSubTake(self.channel, options) ) /** * Converts a stream to a Queue for concurrent consumption. * * @example * ```ts * import { Effect, Queue, Stream } from "effect" * * const program = Effect.gen(function* () { * const queue = yield* Stream.toQueue(Stream.fromIterable([1, 2, 3]), { capacity: 8 }) * const chunk = yield* Queue.takeBetween(queue, 1, 3) * return chunk * }) * ``` * * @since 2.0.0 * @category Destructors */ export const toQueue: { /** * Converts a stream to a Queue for concurrent consumption. * * @example * ```ts * import { Effect, Queue, Stream } from "effect" * * const program = Effect.gen(function* () { * const queue = yield* Stream.toQueue(Stream.fromIterable([1, 2, 3]), { capacity: 8 }) * const chunk = yield* Queue.takeBetween(queue, 1, 3) * return chunk * }) * ``` * * @since 2.0.0 * @category Destructors */ ( options: { readonly capacity: "unbounded" } | { readonly capacity: number readonly strategy?: "dropping" | "sliding" | "suspend" | undefined } ): (self: Stream) => Effect.Effect, never, R | Scope.Scope> /** * Converts a stream to a Queue for concurrent consumption. * * @example * ```ts * import { Effect, Queue, Stream } from "effect" * * const program = Effect.gen(function* () { * const queue = yield* Stream.toQueue(Stream.fromIterable([1, 2, 3]), { capacity: 8 }) * const chunk = yield* Queue.takeBetween(queue, 1, 3) * return chunk * }) * ``` * * @since 2.0.0 * @category Destructors */ ( self: Stream, options: { readonly capacity: "unbounded" } | { readonly capacity: number readonly strategy?: "dropping" | "sliding" | "suspend" | undefined } ): Effect.Effect, never, R | Scope.Scope> } = dual( 2, ( self: Stream, options: { readonly capacity: "unbounded" } | { readonly capacity: number readonly strategy?: "dropping" | "sliding" | "suspend" | undefined } ): Effect.Effect, never, R | Scope.Scope> => Channel.toQueueArray(self.channel, options) ) /** * Runs the stream, offering each element to the provided queue and ending it * with `Cause.Done` when the stream completes. * * @example * ```ts * import { Cause, Effect, Queue, Stream } from "effect" * * const program = Effect.gen(function*() { * const queue = yield* Queue.bounded(4) * * yield* Effect.forkChild( * Stream.runIntoQueue(Stream.fromIterable([1, 2, 3]), queue) * ) * * const values = [ * yield* Queue.take(queue), * yield* Queue.take(queue), * yield* Queue.take(queue) * ] * const done = yield* Effect.flip(Queue.take(queue)) * * return { values, done } * }) * ``` * * @since 2.0.0 * @category Destructors */ export const runIntoQueue: { /** * Runs the stream, offering each element to the provided queue and ending it * with `Cause.Done` when the stream completes. * * @example * ```ts * import { Cause, Effect, Queue, Stream } from "effect" * * const program = Effect.gen(function*() { * const queue = yield* Queue.bounded(4) * * yield* Effect.forkChild( * Stream.runIntoQueue(Stream.fromIterable([1, 2, 3]), queue) * ) * * const values = [ * yield* Queue.take(queue), * yield* Queue.take(queue), * yield* Queue.take(queue) * ] * const done = yield* Effect.flip(Queue.take(queue)) * * return { values, done } * }) * ``` * * @since 2.0.0 * @category Destructors */ (queue: Queue.Queue): (self: Stream) => Effect.Effect /** * Runs the stream, offering each element to the provided queue and ending it * with `Cause.Done` when the stream completes. * * @example * ```ts * import { Cause, Effect, Queue, Stream } from "effect" * * const program = Effect.gen(function*() { * const queue = yield* Queue.bounded(4) * * yield* Effect.forkChild( * Stream.runIntoQueue(Stream.fromIterable([1, 2, 3]), queue) * ) * * const values = [ * yield* Queue.take(queue), * yield* Queue.take(queue), * yield* Queue.take(queue) * ] * const done = yield* Effect.flip(Queue.take(queue)) * * return { values, done } * }) * ``` * * @since 2.0.0 * @category Destructors */ (self: Stream, queue: Queue.Queue): Effect.Effect } = dual(2, ( self: Stream, queue: Queue.Queue ): Effect.Effect => Channel.runIntoQueueArray(self.channel, queue))