1/**
2 * Describes effectful sources that emit values over time.
3 *
4 * A `Stream<A, E, R>` can emit many `A` values, fail with `E`, and require
5 * services `R` while it is being consumed. Streams are useful for data that is
6 * pulled in steps, such as values from collections, queues, pubsubs, schedules,
7 * callbacks, async iterables, or platform streams. The APIs here cover the full
8 * stream lifecycle: create a stream, transform or combine it, control buffering
9 * and timing, handle failures, and finally consume it.
10 *
11 * @since 2.0.0
12 */
13// @effect-diagnostics returnEffectInGen:off
14import * as Arr from "./Array.ts"
15import * as ByteSize from "./ByteSize.ts"
16import * as Cause from "./Cause.ts"
17import * as Channel from "./Channel.ts"
18import { Clock } from "./Clock.ts"
19import * as Context from "./Context.ts"
20import * as Duration from "./Duration.ts"
21import * as Effect from "./Effect.ts"
22import * as Equal from "./Equal.ts"
23import * as ExecutionPlan from "./ExecutionPlan.ts"
24import * as Exit from "./Exit.ts"
25import * as Fiber from "./Fiber.ts"
26import type * as Filter from "./Filter.ts"
27import type { LazyArg } from "./Function.ts"
28import { constant, constTrue, constVoid, dual, identity } from "./Function.ts"
29import type { TypeLambda } from "./HKT.ts"
30import * as Count from "./internal/count.ts"
31import * as internalExecutionPlan from "./internal/executionPlan.ts"
32import * as internal from "./internal/stream.ts"
33import { addSpanStackTrace } from "./internal/tracer.ts"
34import * as Iterable from "./Iterable.ts"
35import * as Latch from "./Latch.ts"
36import type * as Layer from "./Layer.ts"
37import type { Severity } from "./LogLevel.ts"
38import * as MutableHashMap from "./MutableHashMap.ts"
39import * as MutableList from "./MutableList.ts"
40import * as Option from "./Option.ts"
41import type { Pipeable } from "./Pipeable.ts"
42import type { Predicate, Refinement } from "./Predicate.ts"
43import { hasProperty, isNotUndefined, isTagged } from "./Predicate.ts"
44import * as PubSub from "./PubSub.ts"
45import * as Pull from "./Pull.ts"
46import * as Queue from "./Queue.ts"
47import * as RcMap from "./RcMap.ts"
48import * as RcRef from "./RcRef.ts"
49import * as Result from "./Result.ts"
50import * as Schedule from "./Schedule.ts"
51import * as Scope from "./Scope.ts"
52import * as Sink from "./Sink.ts"
53import { isString } from "./String.ts"
54import type * as Take from "./Take.ts"
55import type { ParentSpan, SpanOptions } from "./Tracer.ts"
56import type {
57 Concurrency,
58 Covariant,
59 ExcludeReason,
60 ExcludeTag,
61 ExtractReason,
62 ExtractTag,
63 NarrowReason,
64 NoInfer,
65 OmitReason,
66 ReasonOf,
67 ReasonTags,
68 Simplify,
69 Tags,
70 TupleOf,
71 unassigned
72} from "./Types.ts"
73import type * as Unify from "./Unify.ts"
74
75/**
76 * String literal type used as the unique brand for `Stream` values.
77 *
78 * @category type IDs
79 * @since 4.0.0
80 */
81export type TypeId = "~effect/Stream"
82
83/**
84 * Runtime identifier stored on `Stream` values and used by `isStream` to
85 * recognize them.
86 *
87 * **Details**
88 *
89 * This marker is part of the runtime representation of `Stream` values. Prefer
90 * `isStream` when narrowing unknown values.
91 *
92 * @see {@link isStream} for the public guard that checks this identifier
93 *
94 * @category type IDs
95 * @since 4.0.0
96 */
97export const TypeId: TypeId = "~effect/Stream"
98
99/**
100 * A `Stream<A, E, R>` describes a program that can emit many `A` values, fail
101 * with `E`, and require `R`.
102 *
103 * **Details**
104 *
105 * Streams are pull-based with backpressure and emit chunks to amortize effect
106 * evaluation. They support monadic composition and error handling similar to
107 * `Effect`, adapted for multiple values.
108 *
109 * **Example** (Creating and consuming streams)
110 *
111 * ```ts import.meta.vitest
112 * import { Effect, Stream } from "effect"
113 *
114 * const values = await Effect.runPromise(
115 * Stream.make(1, 2, 3).pipe(
116 * Stream.map((n) => n * 2),
117 * Stream.runCollect
118 * )
119 * )
120 * values // => [2, 4, 6]
121 * ```
122 *
123 * @category models
124 * @since 2.0.0
125 */
126export interface Stream<out A, out E = never, out R = never> extends Variance<A, E, R>, Pipeable {
127 readonly channel: Channel.Channel<Arr.NonEmptyReadonlyArray<A>, E, void, unknown, unknown, unknown, R>
128 [Unify.typeSymbol]?: unknown
129 [Unify.unifySymbol]?: StreamUnify<this>
130 [Unify.ignoreSymbol]?: StreamUnifyIgnore
131}
132
133/**
134 * Type-level unification hook for Stream within the Effect type system.
135 *
136 * @category models
137 * @since 2.0.0
138 */
139export interface StreamUnify<A extends { [Unify.typeSymbol]?: any }> extends Effect.EffectUnify<A> {
140 Stream?: () => A[Unify.typeSymbol] extends Stream<infer A0, infer E0, infer R0> | infer _ ? Stream<A0, E0, R0> : never
141}
142
143/**
144 * Type-level marker that excludes Stream from unification.
145 *
146 * @category models
147 * @since 2.0.0
148 */
149export interface StreamUnifyIgnore {
150 Effect?: true
151}
152
153/**
154 * Type lambda for Stream used in higher-kinded type operations.
155 *
156 * **Example** (Using the stream type lambda)
157 *
158 * ```ts import.meta.vitest
159 * import { Effect, HKT, Stream } from "effect"
160 *
161 * // Create a Stream type using the type lambda
162 * type NumberStream = HKT.Kind<Stream.StreamTypeLambda, never, never, string, number>
163 * // Equivalent to: Stream<number, string, never>
164 * const stream: NumberStream = Stream.make(1, 2, 3)
165 * await Effect.runPromise(Stream.runCollect(stream)) // => [1, 2, 3]
166 * ```
167 *
168 * @category utility types
169 * @since 2.0.0
170 */
171export interface StreamTypeLambda extends TypeLambda {
172 readonly type: Stream<this["Target"], this["Out1"], this["Out2"]>
173}
174
175/**
176 * Type-level variance marker for `Stream`.
177 *
178 * **Details**
179 *
180 * The emitted value `A`, error `E`, and service requirement `R` type
181 * parameters are covariant.
182 *
183 * @category models
184 * @since 2.0.0
185 */
186export interface Variance<out A, out E, out R> {
187 readonly [TypeId]: VarianceStruct<A, E, R>
188}
189
190/**
191 * Structural encoding used by `Variance` to record each `Stream` type
192 * parameter's variance.
193 *
194 * **Details**
195 *
196 * `_A`, `_E`, and `_R` are covariant markers.
197 *
198 * @category models
199 * @since 3.4.0
200 */
201export interface VarianceStruct<out A, out E, out R> {
202 readonly _A: Covariant<A>
203 readonly _E: Covariant<E>
204 readonly _R: Covariant<R>
205}
206
207/**
208 * Extract the success type from a Stream type.
209 *
210 * **Example** (Extracting the success type from a Stream type)
211 *
212 * ```ts import.meta.vitest
213 * import { Stream } from "effect"
214 *
215 * type NumberStream = Stream.Stream<number, string, never>
216 * type SuccessType = Stream.Success<NumberStream>
217 * const value: SuccessType = 42
218 * ```
219 *
220 * @category utility types
221 * @since 3.4.0
222 */
223export type Success<T> = T extends Stream<infer _A, infer _E, infer _R> ? _A : never
224
225/**
226 * Extract the error type from a Stream type.
227 *
228 * **Example** (Extracting the error type from a Stream type)
229 *
230 * ```ts import.meta.vitest
231 * import { Stream } from "effect"
232 *
233 * type NumberStream = Stream.Stream<number, string, never>
234 * type ErrorType = Stream.Error<NumberStream>
235 * const error: ErrorType = "boom"
236 * ```
237 *
238 * @category utility types
239 * @since 3.4.0
240 */
241export type Error<T> = T extends Stream<infer _A, infer _E, infer _R> ? _E : never
242
243/**
244 * Extract the services type from a Stream type.
245 *
246 * **Example** (Extracting the services type from a Stream type)
247 *
248 * ```ts import.meta.vitest
249 * import { Stream } from "effect"
250 *
251 * interface Database {
252 * query: (sql: string) => unknown
253 * }
254 * type NumberStream = Stream.Stream<number, string, { db: Database }>
255 * type RequiredServices = Stream.Services<NumberStream>
256 * const services: RequiredServices = { db: { query: (sql) => sql } }
257 * services.db.query("SELECT 1") // => "SELECT 1"
258 * ```
259 *
260 * @category utility types
261 * @since 4.0.0
262 */
263export type Services<T> = T extends Stream<infer _A, infer _E, infer _R> ? _R : never
264
265/**
266 * Checks whether a value is a Stream.
267 *
268 * **Example** (Checking whether a value is a Stream)
269 *
270 * ```ts import.meta.vitest
271 * import { Stream } from "effect"
272 *
273 * Stream.isStream(Stream.make(1, 2, 3)) // => true
274 * Stream.isStream({ data: [1, 2, 3] }) // => false
275 * ```
276 *
277 * @category guards
278 * @since 4.0.0
279 */
280export const isStream = (u: unknown): u is Stream<unknown, unknown, unknown> => hasProperty(u, TypeId)
281
282/**
283 * The default chunk size used by Stream constructors and combinators.
284 *
285 * **Example** (Reading the default chunk size)
286 *
287 * ```ts import.meta.vitest
288 * import { Stream } from "effect"
289 *
290 * Stream.DefaultChunkSize // => 4096
291 * ```
292 *
293 * @category constants
294 * @since 2.0.0
295 */
296export const DefaultChunkSize: number = Channel.DefaultChunkSize
297
298/**
299 * Describes how merged streams decide when to halt.
300 *
301 * @category models
302 * @since 4.0.0
303 */
304export type HaltStrategy = Channel.HaltStrategy
305
306/**
307 * Creates a stream from a array-emitting `Channel`.
308 *
309 * **Example** (Creating a stream from an array-emitting channel)
310 *
311 * ```ts import.meta.vitest
312 * import { Channel, Effect, Stream } from "effect"
313 *
314 * const channel = Channel.succeed([1, 2, 3] as const)
315 * const stream = Stream.fromChannel(channel)
316 * await Effect.runPromise(Stream.runCollect(stream)) // => [1, 2, 3]
317 * ```
318 *
319 * @category constructors
320 * @since 2.0.0
321 */
322export const fromChannel: <Arr extends Arr.NonEmptyReadonlyArray<any>, E, R>(
323 channel: Channel.Channel<Arr, E, void, unknown, unknown, unknown, R>
324) => Stream<Arr extends Arr.NonEmptyReadonlyArray<infer A> ? A : never, E, R> = internal.fromChannel
325
326/**
327 * Creates a stream from an effect.
328 *
329 * **Example** (Creating a stream from an effect)
330 *
331 * ```ts import.meta.vitest
332 * import { Effect, Stream } from "effect"
333 *
334 * const stream = Stream.fromEffect(Effect.succeed(42))
335 * await Effect.runPromise(Stream.runCollect(stream)) // => [42]
336 * ```
337 *
338 * @category constructors
339 * @since 2.0.0
340 */
341export const fromEffect = <A, E, R>(effect: Effect.Effect<A, E, R>): Stream<A, E, R> =>
342 fromChannel(Channel.fromEffect(Effect.map(effect, Arr.of)))
343
344/**
345 * Accesses a service from the context and emits it as a single element.
346 *
347 * **Example** (Accessing a service as a stream)
348 *
349 * ```ts import.meta.vitest
350 * import { Context, Effect, Stream } from "effect"
351 *
352 * class Greeter extends Context.Service<Greeter, {
353 * readonly greet: (name: string) => string
354 * }>()("Greeter") {}
355 *
356 * const stream = Stream.service(Greeter).pipe(
357 * Stream.map((greeter) => greeter.greet("World"))
358 * )
359 *
360 * await Effect.runPromise(
361 * stream.pipe(
362 * Stream.provideService(Greeter, {
363 * greet: (name) => `Hello, ${name}!`
364 * }),
365 * Stream.runCollect
366 * )
367 * ) // => ["Hello, World!"]
368 * ```
369 *
370 * @category accessors
371 * @since 4.0.0
372 */
373export const service = <I, S>(service: Context.Key<I, S>): Stream<S, never, I> => fromEffect(Effect.service(service))
374
375/**
376 * Optionally accesses a service from the context and emits the result as a
377 * single element.
378 *
379 * **When to use**
380 *
381 * Use when you need a stream that emits an optional service from the context
382 * without requiring that service to be present.
383 *
384 * **Example** (Accessing an optional service as a stream)
385 *
386 * ```ts import.meta.vitest
387 * import { Context, Effect, Option, Stream } from "effect"
388 *
389 * class Greeter extends Context.Service<Greeter, {
390 * readonly greet: (name: string) => string
391 * }>()("Greeter") {}
392 *
393 * const stream = Stream.serviceOption(Greeter).pipe(
394 * Stream.map((maybeGreeter) =>
395 * Option.match(maybeGreeter, {
396 * onNone: () => "No greeter",
397 * onSome: (greeter) => greeter.greet("World")
398 * })
399 * )
400 * )
401 *
402 * await Effect.runPromise(
403 * stream.pipe(
404 * Stream.provideService(Greeter, {
405 * greet: (name) => `Hello, ${name}!`
406 * }),
407 * Stream.runCollect
408 * )
409 * ) // => ["Hello, World!"]
410 * ```
411 *
412 * @category accessors
413 * @since 4.0.0
414 */
415export const serviceOption = <I, S>(service: Context.Key<I, S>): Stream<Option.Option<S>> =>
416 fromEffect(Effect.serviceOption(service))
417
418/**
419 * Creates a stream that runs the effect and emits no elements.
420 *
421 * **Example** (Draining an effect into a stream)
422 *
423 * ```ts import.meta.vitest
424 * import { Effect, Stream } from "effect"
425 *
426 * let drained = false
427 * await Effect.runPromise(
428 * Stream.fromEffectDrain(Effect.sync(() => {
429 * drained = true
430 * })).pipe(Stream.runDrain)
431 * )
432 * drained // => true
433 * ```
434 *
435 * @category constructors
436 * @since 4.0.0
437 */
438export const fromEffectDrain = <A, E, R>(effect: Effect.Effect<A, E, R>): Stream<never, E, R> =>
439 fromPull(Effect.succeed(Effect.flatMap(effect, () => Cause.done())))
440
441/**
442 * Creates a stream from an effect producing a value of type `A` which repeats forever.
443 *
444 * **Example** (Repeating an effect forever)
445 *
446 * ```ts import.meta.vitest
447 * import { Effect, Stream } from "effect"
448 *
449 * let n = 0
450 * const stream = Stream.fromEffectRepeat(Effect.sync(() => ++n)).pipe(Stream.take(5))
451 * await Effect.runPromise(Stream.runCollect(stream)) // => [1, 2, 3, 4, 5]
452 * ```
453 *
454 * @category constructors
455 * @since 4.0.0
456 */
457export const fromEffectRepeat = <A, E, R>(effect: Effect.Effect<A, E, R>): Stream<A, Pull.ExcludeDone<E>, R> =>
458 fromPull(Effect.succeed(Effect.map(effect, Arr.of)))
459
460/**
461 * Creates a stream from an effect producing a value of type `A`, which is
462 * repeated using the specified schedule.
463 *
464 * **Example** (Repeating an effect with a schedule)
465 *
466 * ```ts import.meta.vitest
467 * import { Effect, Schedule, Stream } from "effect"
468 *
469 * const stream = Stream.fromEffectSchedule(Effect.succeed("ping"), Schedule.recurs(2))
470 * await Effect.runPromise(Stream.runCollect(stream)) // => ["ping", "ping", "ping"]
471 * ```
472 *
473 * @category constructors
474 * @since 4.0.0
475 */
476export const fromEffectSchedule = <A, E, R, X, AS extends A, ES, RS>(
477 effect: Effect.Effect<A, E, R>,
478 schedule: Schedule.Schedule<X, AS, ES, RS>
479): Stream<A, E | ES, R | RS> =>
480 fromPull(Effect.gen(function*() {
481 const step = yield* Schedule.toStepWithMetadata(schedule)
482 let s = yield* Effect.provideService(effect, Schedule.CurrentMetadata, Schedule.CurrentMetadata.defaultValue())
483 let initial = true
484 const pull = Effect.suspend(() => step(s as AS)).pipe(
485 Effect.flatMap((meta) => Effect.provideService(effect, Schedule.CurrentMetadata, meta)),
486 Effect.map((next) => {
487 s = next
488 return Arr.of(next)
489 })
490 ) as Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E | ES, void, R | RS>
491 return Effect.suspend(() => {
492 if (initial) {
493 initial = false
494 return Effect.succeed(Arr.of(s))
495 }
496 return pull
497 })
498 }))
499
500/**
501 * Creates a stream that emits `void` immediately once, then emits another
502 * `void` after each specified interval.
503 *
504 * **Example** (Emitting ticks on an interval)
505 *
506 * ```ts import.meta.vitest
507 * import { Effect, Stream } from "effect"
508 *
509 * await Effect.runPromise(Stream.tick(0).pipe(Stream.take(3), Stream.runCollect)) // => [undefined, undefined, undefined]
510 * ```
511 *
512 * @category constructors
513 * @since 2.0.0
514 */
515export const tick = (interval: Duration.Input): Stream<void> =>
516 fromPull(Effect.sync(() => {
517 let first = true
518 const effect = Effect.succeed(Arr.of<void>(undefined))
519 const delayed = Effect.delay(effect, interval)
520 return Effect.suspend(() => {
521 if (first) {
522 first = false
523 return effect
524 }
525 return delayed
526 })
527 }))
528
529/**
530 * Creates a stream from a pull effect, such as one produced by `Stream.toPull`.
531 *
532 * **Details**
533 *
534 * A pull effect yields chunks on demand and completes when the upstream stream ends.
535 * See `Stream.toPull` for a matching producer.
536 *
537 * **Example** (Creating a stream from a pull effect)
538 *
539 * ```ts import.meta.vitest
540 * import { Effect, Stream } from "effect"
541 *
542 * const program = Effect.scoped(
543 * Effect.gen(function*() {
544 * const source = Stream.make(1, 2, 3)
545 * const pull = yield* Stream.toPull(source)
546 * const stream = Stream.fromPull(Effect.succeed(pull))
547 * return yield* Stream.runCollect(stream)
548 * })
549 * )
550 *
551 * await Effect.runPromise(program) // => [1, 2, 3]
552 * ```
553 *
554 * @category constructors
555 * @since 2.0.0
556 */
557export const fromPull = <A, E, R, EX, RX>(
558 pull: Effect.Effect<Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E, void, R>, EX, RX>
559): Stream<A, Pull.ExcludeDone<E> | EX, R | RX> => fromChannel(Channel.fromPull(pull))
560
561/**
562 * Derives a stream by transforming its pull effect.
563 *
564 * **Example** (Transforming a pull effect)
565 *
566 * ```ts import.meta.vitest
567 * import { Effect, Stream } from "effect"
568 *
569 * const stream = Stream.make(1, 2, 3)
570 *
571 * const transformed = Stream.transformPull(stream, (pull) => Effect.succeed(pull))
572 *
573 * await Effect.runPromise(Stream.runCollect(transformed)) // => [1, 2, 3]
574 * ```
575 *
576 * @category constructors
577 * @since 4.0.0
578 */
579export const transformPull = <A, E, R, B, E2, R2, EX, RX>(
580 self: Stream<A, E, R>,
581 f: (pull: Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E, void>, scope: Scope.Scope) => Effect.Effect<
582 Pull.Pull<Arr.NonEmptyReadonlyArray<B>, E2, void, R2>,
583 EX,
584 RX
585 >
586): Stream<B, EX | Pull.ExcludeDone<E2>, R | R2 | RX> =>
587 fromChannel(
588 Channel.fromTransform((_, scope) =>
589 Effect.flatMap(Channel.toPullScoped(self.channel, scope), (pull) => f(pull as any, scope))
590 )
591 )
592
593/**
594 * Transforms a stream by effectfully transforming its pull effect.
595 *
596 * **Details**
597 *
598 * A forked scope is also provided to the transformation function, which is
599 * closed once the resulting stream has finished processing.
600 *
601 * **Example** (Transforming a stream by effectfully transforming its pull effect)
602 *
603 * ```ts import.meta.vitest
604 * import { Effect, Scope, Stream } from "effect"
605 *
606 * const finalized: Array<boolean> = []
607 * const stream = Stream.make(1, 2, 3)
608 *
609 * const transformed = Stream.transformPullBracket(
610 * stream,
611 * (pull, _scope, forkedScope) =>
612 * Effect.gen(function*() {
613 * yield* Scope.addFinalizer(forkedScope, Effect.sync(() => finalized.push(true)))
614 * return pull
615 * })
616 * )
617 *
618 * await Effect.runPromise(Stream.runCollect(transformed)) // => [1, 2, 3]
619 * finalized // => [true]
620 * ```
621 *
622 * @category constructors
623 * @since 4.0.0
624 */
625export const transformPullBracket = <A, E, R, B, E2, R2, EX, RX>(
626 self: Stream<A, E, R>,
627 f: (
628 pull: Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E, void, R>,
629 scope: Scope.Scope,
630 forkedScope: Scope.Scope
631 ) => Effect.Effect<
632 Pull.Pull<Arr.NonEmptyReadonlyArray<B>, E2, void, R2>,
633 EX,
634 RX
635 >
636): Stream<B, EX | Pull.ExcludeDone<E2>, R | R2 | RX> =>
637 fromChannel(
638 Channel.fromTransformBracket((_, scope, forkedScope) =>
639 Effect.flatMap(Channel.toPullScoped(self.channel, scope), (pull) => f(pull, scope, forkedScope))
640 )
641 )
642
643/**
644 * Creates a channel from a stream.
645 *
646 * **Example** (Converting a stream to a channel)
647 *
648 * ```ts import.meta.vitest
649 * import { Channel, Effect, Stream } from "effect"
650 *
651 * const channel = Stream.toChannel(Stream.make(1, 2, 3))
652 * const values = await Effect.runPromise(Channel.runCollect(channel))
653 * values.flat() // => [1, 2, 3]
654 * ```
655 *
656 * @category destructors
657 * @since 2.0.0
658 */
659export const toChannel = <A, E, R>(
660 stream: Stream<A, E, R>
661): Channel.Channel<Arr.NonEmptyReadonlyArray<A>, E, void, unknown, unknown, unknown, R> => stream.channel
662
663/**
664 * Creates a stream from a callback that can emit values into a queue.
665 *
666 * **When to use**
667 *
668 * Use when you need callback-based code to emit stream values by offering to a
669 * `Queue`, or signal stream completion through the `Queue` module APIs.
670 *
671 * By default it uses an "unbounded" buffer size.
672 * You can customize the buffer size and strategy by passing an object as the
673 * second argument with the `bufferSize` and `strategy` fields.
674 *
675 * **Example** (Creating a stream from a callback that can emit values into a queue)
676 *
677 * ```ts import.meta.vitest
678 * import { Effect, Queue, Stream } from "effect"
679 *
680 * const stream = Stream.callback<number>((queue) =>
681 * Effect.sync(() => {
682 * // Emit values to the stream
683 * Queue.offerUnsafe(queue, 1)
684 * Queue.offerUnsafe(queue, 2)
685 * Queue.offerUnsafe(queue, 3)
686 * // Signal completion
687 * Queue.endUnsafe(queue)
688 * })
689 * )
690 *
691 * await Effect.runPromise(Stream.runCollect(stream)) // => [1, 2, 3]
692 * ```
693 *
694 * @category constructors
695 * @since 4.0.0
696 */
697export const callback = <A, E = never, R = never>(
698 f: (queue: Queue.Queue<A, E | Cause.Done>) => Effect.Effect<unknown, E, R | Scope.Scope>,
699 options?: {
700 readonly bufferSize?: number | undefined
701 readonly strategy?: "sliding" | "dropping" | "suspend" | undefined
702 }
703): Stream<A, E, Exclude<R, Scope.Scope>> => fromChannel(Channel.callbackArray(f, options))
704
705/**
706 * Creates an empty stream.
707 *
708 * **Example** (Creating an empty stream)
709 *
710 * ```ts import.meta.vitest
711 * import { Effect, Stream } from "effect"
712 *
713 * await Effect.runPromise(Stream.runCollect(Stream.empty)) // => []
714 * ```
715 *
716 * @category constructors
717 * @since 2.0.0
718 */
719export const empty: Stream<never> = fromChannel(Channel.empty)
720
721/**
722 * Creates a single-valued pure stream.
723 *
724 * **Example** (Creating a single-valued pure stream)
725 *
726 * ```ts import.meta.vitest
727 * import { Effect, Stream } from "effect"
728 *
729 * await Effect.runPromise(Stream.runCollect(Stream.succeed(3))) // => [3]
730 * ```
731 *
732 * @category constructors
733 * @since 2.0.0
734 */
735export const succeed = <A>(value: A): Stream<A> => fromChannel(Channel.succeed(Arr.of(value)))
736
737/**
738 * Creates a stream from a sequence of values.
739 *
740 * **Example** (Creating a stream from a sequence of values)
741 *
742 * ```ts import.meta.vitest
743 * import { Effect, Stream } from "effect"
744 *
745 * const stream = Stream.make(1, 2, 3)
746 *
747 * await Effect.runPromise(Stream.runCollect(stream)) // => [1, 2, 3]
748 * ```
749 *
750 * @category constructors
751 * @since 2.0.0
752 */
753export const make = <const As extends ReadonlyArray<any>>(...values: As): Stream<As[number]> => fromArray(values)
754
755/**
756 * Creates a stream that synchronously evaluates a function and emits the result as a single value.
757 *
758 * **Details**
759 *
760 * The function is evaluated each time the stream is run.
761 *
762 * **Example** (Evaluating a value synchronously)
763 *
764 * ```ts import.meta.vitest
765 * import { Effect, Stream } from "effect"
766 *
767 * await Effect.runPromise(Stream.sync(() => 2 + 1).pipe(Stream.runCollect)) // => [3]
768 * ```
769 *
770 * @category constructors
771 * @since 2.0.0
772 */
773export const sync = <A>(evaluate: LazyArg<A>): Stream<A> => fromChannel(Channel.sync(() => Arr.of(evaluate())))
774
775/**
776 * Creates a lazily constructed stream.
777 *
778 * **Details**
779 *
780 * The stream factory is evaluated each time the stream is run.
781 *
782 * **Example** (Creating a lazily constructed stream)
783 *
784 * ```ts import.meta.vitest
785 * import { Effect, Stream } from "effect"
786 *
787 * await Effect.runPromise(Stream.suspend(() => Stream.make(1, 2, 3)).pipe(Stream.runCollect)) // => [1, 2, 3]
788 * ```
789 *
790 * @category constructors
791 * @since 2.0.0
792 */
793export const suspend = <A, E, R>(stream: LazyArg<Stream<A, E, R>>): Stream<A, E, R> =>
794 fromChannel(Channel.suspend(() => stream().channel))
795
796/**
797 * Terminates with the specified error.
798 *
799 * **Example** (Failing a stream)
800 *
801 * ```ts import.meta.vitest
802 * import { Effect, Exit, Stream } from "effect"
803 *
804 * await Effect.runPromise(Effect.exit(Stream.runCollect(Stream.fail("Uh oh!")))) // => Exit.fail("Uh oh!")
805 * ```
806 *
807 * @category constructors
808 * @since 2.0.0
809 */
810export const fail = <E>(error: E): Stream<never, E> => fromChannel(Channel.fail(error))
811
812/**
813 * Terminates with the specified lazily evaluated error.
814 *
815 * **Example** (Failing a stream lazily)
816 *
817 * ```ts import.meta.vitest
818 * import { Effect, Exit, Stream } from "effect"
819 *
820 * const stream = Stream.failSync(() => "Uh oh!")
821 *
822 * await Effect.runPromise(Stream.runCollect(stream).pipe(Effect.exit)) // => Exit.fail("Uh oh!")
823 * ```
824 *
825 * @category constructors
826 * @since 2.0.0
827 */
828export const failSync = <E>(evaluate: LazyArg<E>): Stream<never, E> => fromChannel(Channel.failSync(evaluate))
829
830/**
831 * Creates a stream that fails with the specified `Cause`.
832 *
833 * **Example** (Failing with a cause)
834 *
835 * ```ts import.meta.vitest
836 * import { Cause, Effect, Stream } from "effect"
837 *
838 * const stream = Stream.failCause(Cause.fail("Database connection failed")).pipe(
839 * Stream.catchCause(() => Stream.succeed("recovered"))
840 * )
841 *
842 * await Effect.runPromise(Stream.runCollect(stream)) // => ["recovered"]
843 * ```
844 *
845 * @category constructors
846 * @since 2.0.0
847 */
848export const failCause = <E>(cause: Cause.Cause<E>): Stream<never, E> => fromChannel(Channel.failCause(cause))
849
850/**
851 * The stream that dies with the specified defect.
852 *
853 * **Example** (Dying with a defect)
854 *
855 * ```ts import.meta.vitest
856 * import { Cause, Effect, Exit, Stream } from "effect"
857 *
858 * const defect = new Error("Boom")
859 * const stream = Stream.die(defect)
860 *
861 * await Effect.runPromise(Effect.exit(Stream.runCollect(stream))) // => Exit.failCause(Cause.die(defect))
862 * ```
863 *
864 * @category constructors
865 * @since 2.0.0
866 */
867export const die = (defect: unknown): Stream<never> => fromChannel(Channel.die(defect))
868
869/**
870 * The stream that always fails with the specified lazily evaluated `Cause`.
871 *
872 * **Example** (Failing with a lazy cause)
873 *
874 * ```ts import.meta.vitest
875 * import { Cause, Effect, Exit, Stream } from "effect"
876 *
877 * const stream = Stream.failCauseSync(() =>
878 * Cause.fail("Connection timeout after retries")
879 * )
880 *
881 * await Effect.runPromise(Stream.runCollect(stream).pipe(Effect.exit)) // => Exit.fail("Connection timeout after retries")
882 * ```
883 *
884 * @category constructors
885 * @since 2.0.0
886 */
887export const failCauseSync = <E>(evaluate: LazyArg<Cause.Cause<E>>): Stream<never, E> =>
888 fromChannel(Channel.failCauseSync(evaluate))
889
890/**
891 * Creates a stream that consumes values from an iterator.
892 *
893 * **Details**
894 *
895 * The `maxChunkSize` parameter controls how many values are pulled per chunk.
896 * Finite fractional values are rounded down, while `NaN` and non-positive
897 * values are treated as `1`.
898 *
899 * **Example** (Consuming values from an iterator)
900 *
901 * ```ts import.meta.vitest
902 * import { Effect, Stream } from "effect"
903 *
904 * function* numbers() {
905 * yield 1
906 * yield 2
907 * yield 3
908 * }
909 *
910 * const stream = Stream.fromIteratorSucceed(numbers())
911 *
912 * const program = Effect.gen(function* () {
913 * const values = yield* Stream.runCollect(stream)
914 * values // => [1, 2, 3]
915 * })
916 *
917 * await Effect.runPromise(program)
918 * ```
919 *
920 * @category constructors
921 * @since 2.0.0
922 */
923export const fromIteratorSucceed = <A>(iterator: IterableIterator<A>, maxChunkSize?: number): Stream<A> =>
924 fromChannel(Channel.fromIteratorArray(() => iterator, maxChunkSize))
925
926/**
927 * Creates a new `Stream` from an iterable collection of values.
928 *
929 * **Details**
930 *
931 * - `chunkSize`: Maximum number of values emitted per chunk. Finite fractional
932 * values are rounded down, while `NaN` and non-positive values are treated as
933 * `1`.
934 *
935 * **Example** (Creating a stream from an iterable)
936 *
937 * ```ts import.meta.vitest
938 * import { Effect, Stream } from "effect"
939 *
940 * const numbers = [1, 2, 3]
941 *
942 * const program = Effect.gen(function*() {
943 * const stream = Stream.fromIterable(numbers)
944 * const values = yield* Stream.runCollect(stream)
945 * values // => [1, 2, 3]
946 * })
947 *
948 * await Effect.runPromise(program)
949 * ```
950 *
951 * @category constructors
952 * @since 2.0.0
953 */
954export const fromIterable = <A>(
955 iterable: Iterable<A>,
956 options?: {
957 readonly chunkSize?: number | undefined
958 }
959): Stream<A> =>
960 Array.isArray(iterable) && options?.chunkSize === undefined
961 ? fromArray(iterable)
962 : fromChannel(Channel.fromIterableArray(iterable, options?.chunkSize))
963
964/**
965 * Creates a stream from an effect producing an iterable of values.
966 *
967 * **When to use**
968 *
969 * Use when the iterable must be acquired from an Effect before the stream emits,
970 * and acquisition services or failures should be part of the stream.
971 *
972 * **Example** (Creating a stream from an iterable effect)
973 *
974 * ```ts import.meta.vitest
975 * import { Context, Effect, Stream } from "effect"
976 *
977 * class UserRepo extends Context.Service<UserRepo, {
978 * readonly list: Effect.Effect<ReadonlyArray<string>>
979 * }>()("UserRepo") {}
980 *
981 * const listUsers = Effect.service(UserRepo).pipe(
982 * Effect.andThen((repo) => repo.list)
983 * )
984 *
985 * const stream = Stream.fromIterableEffect(listUsers)
986 *
987 * const program = Effect.gen(function*() {
988 * const users = yield* stream.pipe(
989 * Stream.provideService(UserRepo, {
990 * list: Effect.succeed(["user1", "user2"])
991 * }),
992 * Stream.runCollect
993 * )
994 * users // => ["user1", "user2"]
995 * })
996 *
997 * await Effect.runPromise(program)
998 * ```
999 *
1000 * @category constructors
1001 * @since 2.0.0
1002 */
1003export const fromIterableEffect = <A, E, R>(iterable: Effect.Effect<Iterable<A>, E, R>): Stream<A, E, R> =>
1004 unwrap(Effect.map(iterable, fromIterable))
1005
1006/**
1007 * Creates a stream by repeatedly running an effect that yields an iterable of values.
1008 *
1009 * **Example** (Repeating an iterable effect)
1010 *
1011 * ```ts import.meta.vitest
1012 * import { Effect, Stream } from "effect"
1013 *
1014 * const program = Effect.gen(function*() {
1015 * const stream = Stream.fromIterableEffectRepeat(Effect.succeed([1, 2])).pipe(
1016 * Stream.take(5)
1017 * )
1018 * const values = yield* Stream.runCollect(stream)
1019 * values // => [1, 2, 1, 2, 1]
1020 * })
1021 *
1022 * await Effect.runPromise(program)
1023 * ```
1024 *
1025 * @category constructors
1026 * @since 4.0.0
1027 */
1028export const fromIterableEffectRepeat = <A, E, R>(
1029 iterable: Effect.Effect<Iterable<A>, E, R>
1030): Stream<A, Pull.ExcludeDone<E>, R> => flatMap(fromEffectRepeat(iterable), fromIterable)
1031
1032/**
1033 * Creates a stream from an array of values.
1034 *
1035 * **Example** (Creating a stream from an array of values)
1036 *
1037 * ```ts import.meta.vitest
1038 * import { Effect, Stream } from "effect"
1039 *
1040 * const program = Effect.gen(function*() {
1041 * const stream = Stream.fromArray([1, 2, 3])
1042 * const values = yield* Stream.runCollect(stream)
1043 * values // => [1, 2, 3]
1044 * })
1045 *
1046 * await Effect.runPromise(program)
1047 * ```
1048 *
1049 * @category constructors
1050 * @since 4.0.0
1051 */
1052export const fromArray = <A>(array: ReadonlyArray<A>): Stream<A> =>
1053 Arr.isReadonlyArrayNonEmpty(array) ? fromChannel(Channel.succeed(array)) : empty
1054
1055/**
1056 * Creates a stream from an effect that produces an array of values.
1057 *
1058 * **When to use**
1059 *
1060 * Use when the array must be acquired from an Effect before the stream emits,
1061 * and acquisition services or failures should be part of the stream.
1062 *
1063 * **Example** (Creating a stream from an effect that produces an array of values)
1064 *
1065 * ```ts import.meta.vitest
1066 * import { Effect, Stream } from "effect"
1067 *
1068 * const program = Effect.gen(function*() {
1069 * const stream = Stream.fromArrayEffect(Effect.succeed(["Ada", "Grace"]))
1070 * const values = yield* Stream.runCollect(stream)
1071 * values // => ["Ada", "Grace"]
1072 * })
1073 *
1074 * await Effect.runPromise(program)
1075 * ```
1076 *
1077 * @category constructors
1078 * @since 4.0.0
1079 */
1080export const fromArrayEffect = <A, E, R>(
1081 effect: Effect.Effect<ReadonlyArray<A>, E, R>
1082): Stream<A, Pull.ExcludeDone<E>, R> => unwrap(Effect.map(effect, fromArray)) as any
1083
1084/**
1085 * Creates a stream from an arbitrary number of arrays.
1086 *
1087 * **Example** (Creating a stream from an arbitrary number of arrays)
1088 *
1089 * ```ts import.meta.vitest
1090 * import { Effect, Stream } from "effect"
1091 *
1092 * const program = Effect.gen(function*() {
1093 * const stream = Stream.fromArrays([1, 2], [3, 4])
1094 * const values = yield* Stream.runCollect(stream)
1095 * values // => [1, 2, 3, 4]
1096 * })
1097 *
1098 * await Effect.runPromise(program)
1099 * ```
1100 *
1101 * @category constructors
1102 * @since 4.0.0
1103 */
1104export const fromArrays = <Arr extends ReadonlyArray<ReadonlyArray<any>>>(
1105 ...arrays: Arr
1106): Stream<Arr[number][number]> => fromChannel(Channel.fromArray(Arr.filter(arrays, Arr.isReadonlyArrayNonEmpty)))
1107
1108/**
1109 * Creates a stream that pulls values from a `Queue.Dequeue`.
1110 *
1111 * **Details**
1112 *
1113 * The stream emits non-empty batches of queued values and ends when the queue
1114 * fails with `Cause.Done`; other queue failures are propagated.
1115 *
1116 * **Example** (Creating a stream from a queue of values)
1117 *
1118 * ```ts import.meta.vitest
1119 * import { Cause, Effect, Queue, Stream } from "effect"
1120 *
1121 * const program = Effect.gen(function*() {
1122 * const queue = yield* Queue.unbounded<number, Cause.Done>()
1123 * yield* Queue.offer(queue, 1)
1124 * yield* Queue.offer(queue, 2)
1125 * yield* Queue.offer(queue, 3)
1126 * yield* Queue.end(queue)
1127 *
1128 * const stream = Stream.fromQueue(queue)
1129 * const values = yield* Stream.runCollect(stream)
1130 * values // => [1, 2, 3]
1131 * })
1132 *
1133 * await Effect.runPromise(program)
1134 * ```
1135 *
1136 * @category constructors
1137 * @since 2.0.0
1138 */
1139export const fromQueue = <A, E>(queue: Queue.Dequeue<A, E>): Stream<A, Exclude<E, Cause.Done>> =>
1140 fromChannel(Channel.fromQueueArray(queue))
1141
1142/**
1143 * Creates a stream from a subscription to a `PubSub`.
1144 *
1145 * **Example** (Creating a stream from a subscription to a PubSub)
1146 *
1147 * ```ts import.meta.vitest
1148 * import { Effect, Fiber, PubSub, Stream } from "effect"
1149 *
1150 * const program = Effect.gen(function*() {
1151 * const pubsub = yield* PubSub.unbounded<number>({ replay: 3 })
1152 *
1153 * const fiber = yield* Stream.fromPubSub(pubsub).pipe(
1154 * Stream.take(3),
1155 * Stream.runCollect,
1156 * Effect.forkChild
1157 * )
1158 *
1159 * yield* PubSub.publish(pubsub, 1)
1160 * yield* PubSub.publish(pubsub, 2)
1161 * yield* PubSub.publish(pubsub, 3)
1162 *
1163 * const values = yield* Fiber.join(fiber)
1164 * values // => [1, 2, 3]
1165 * })
1166 *
1167 * await Effect.runPromise(program)
1168 * ```
1169 *
1170 * @category constructors
1171 * @since 2.0.0
1172 */
1173export const fromPubSub = <A>(pubsub: PubSub.PubSub<A>): Stream<A> => fromChannel(Channel.fromPubSubArray(pubsub))
1174
1175/**
1176 * Creates a stream from a PubSub of `Take` values.
1177 *
1178 * **Details**
1179 *
1180 * `Take` values include end and failure signals.
1181 *
1182 * **Example** (Creating a stream from PubSub takes)
1183 *
1184 * ```ts import.meta.vitest
1185 * import { Effect, Exit, PubSub, Stream, Take } from "effect"
1186 *
1187 * const program = Effect.gen(function*() {
1188 * const pubsub = yield* PubSub.unbounded<Take.Take<number, string>>({
1189 * replay: 3
1190 * })
1191 *
1192 * yield* PubSub.publish(pubsub, [1])
1193 * yield* PubSub.publish(pubsub, [2])
1194 * yield* PubSub.publish(pubsub, Exit.succeed<void>(undefined))
1195 *
1196 * const values = yield* Stream.fromPubSubTake(pubsub).pipe(Stream.runCollect)
1197 * values // => [1, 2]
1198 * })
1199 *
1200 * await Effect.runPromise(program)
1201 * ```
1202 *
1203 * @category constructors
1204 * @since 4.0.0
1205 */
1206export const fromPubSubTake = <A, E>(pubsub: PubSub.PubSub<Take.Take<A, E>>): Stream<A, E> =>
1207 fromChannel(Channel.fromPubSubTake(pubsub))
1208
1209/**
1210 * Creates a stream from a lazily supplied Web `ReadableStream`.
1211 *
1212 * **Details**
1213 *
1214 * The stream reads from a `ReadableStreamDefaultReader`, maps read failures
1215 * with `onError`, and closes the reader when the stream finalizes. By default
1216 * the reader is canceled; set `releaseLockOnEnd` to release the lock instead.
1217 *
1218 * **Example** (Creating a stream from a ReadableStream)
1219 *
1220 * ```ts import.meta.vitest
1221 * import { Data, Effect, Stream } from "effect"
1222 *
1223 * class StreamError extends Data.TaggedError("StreamError")<{ readonly cause: unknown }> {}
1224 *
1225 * const readableStream = new ReadableStream({
1226 * start(controller) {
1227 * controller.enqueue(1)
1228 * controller.enqueue(2)
1229 * controller.enqueue(3)
1230 * controller.close()
1231 * }
1232 * })
1233 *
1234 * const program = Effect.gen(function*() {
1235 * const stream = Stream.fromReadableStream({
1236 * evaluate: () => readableStream,
1237 * onError: (cause) => new StreamError({ cause })
1238 * })
1239 * const values = yield* Stream.runCollect(stream)
1240 * values // => [1, 2, 3]
1241 * })
1242 *
1243 * await Effect.runPromise(program)
1244 * ```
1245 *
1246 * @category constructors
1247 * @since 2.0.0
1248 */
1249export const fromReadableStream = <A, E>(
1250 options: {
1251 readonly evaluate: LazyArg<ReadableStream<A>>
1252 readonly onError: (error: unknown) => E
1253 readonly releaseLockOnEnd?: boolean | undefined
1254 }
1255): Stream<A, E> => fromChannel(Channel.fromReadableStream(options))
1256
1257/**
1258 * Creates a stream from an AsyncIterable.
1259 *
1260 * **Example** (Creating a stream from an AsyncIterable)
1261 *
1262 * ```ts import.meta.vitest
1263 * import { Data, Effect, Stream } from "effect"
1264 *
1265 * class StreamError extends Data.TaggedError("StreamError")<{ readonly cause: unknown }> {}
1266 *
1267 * const iterable = (async function*() {
1268 * yield 1
1269 * yield 2
1270 * yield 3
1271 * })()
1272 *
1273 * await Effect.runPromise(Effect.gen(function*() {
1274 * const stream = Stream.fromAsyncIterable(iterable, (cause) => new StreamError({ cause }))
1275 * const values = yield* Stream.runCollect(stream)
1276 * values // => [1, 2, 3]
1277 * }))
1278 *
1279 * ```
1280 *
1281 * @category constructors
1282 * @since 2.0.0
1283 */
1284export const fromAsyncIterable = <A, E>(
1285 iterable: AsyncIterable<A>,
1286 onError: (error: unknown) => E
1287): Stream<A, E> => fromChannel(Channel.fromAsyncIterableArray(iterable, onError))
1288
1289/**
1290 * Creates a stream that emits each output of a schedule that does not require input,
1291 * for as long as the schedule continues.
1292 *
1293 * **Example** (Creating a stream from a schedule)
1294 *
1295 * ```ts import.meta.vitest
1296 * import { Effect, Schedule, Stream } from "effect"
1297 *
1298 * const program = Effect.gen(function*() {
1299 * const schedule = Schedule.recurs(3)
1300 * const stream = Stream.fromSchedule(schedule)
1301 * const values = yield* Stream.runCollect(stream)
1302 * values // => [0, 1, 2]
1303 * })
1304 *
1305 * await Effect.runPromise(program)
1306 * ```
1307 *
1308 * @category constructors
1309 * @since 2.0.0
1310 */
1311export const fromSchedule = <O, E, R>(schedule: Schedule.Schedule<O, unknown, E, R>): Stream<O, E, R> =>
1312 fromPull(
1313 Effect.map(
1314 Schedule.toStepWithSleep(schedule),
1315 (step) => Pull.catchDone(Effect.map(step(void 0), Arr.of), () => Cause.done())
1316 )
1317 )
1318
1319/**
1320 * Creates a stream from a PubSub subscription.
1321 *
1322 * **When to use**
1323 *
1324 * Use when you already have a `PubSub.Subscription` and want to expose its
1325 * messages as a `Stream`, with `Stream.take` or cancellation controlling how
1326 * many values are consumed.
1327 *
1328 * **Example** (Creating a stream from a PubSub subscription)
1329 *
1330 * ```ts import.meta.vitest
1331 * import { Effect, PubSub, Stream } from "effect"
1332 *
1333 * const program = Effect.scoped(Effect.gen(function*() {
1334 * const pubsub = yield* PubSub.unbounded<number>()
1335 * const subscription = yield* PubSub.subscribe(pubsub)
1336 *
1337 * yield* PubSub.publish(pubsub, 1)
1338 * yield* PubSub.publish(pubsub, 2)
1339 *
1340 * const stream = Stream.fromSubscription(subscription)
1341 * const values = yield* stream.pipe(Stream.take(2), Stream.runCollect)
1342 * values // => [1, 2]
1343 * }))
1344 *
1345 * await Effect.runPromise(program)
1346 * ```
1347 *
1348 * @category constructors
1349 * @since 4.0.0
1350 */
1351export const fromSubscription = <A>(pubsub: PubSub.Subscription<A>): Stream<A> =>
1352 fromChannel(Channel.fromSubscriptionArray(pubsub))
1353
1354/**
1355 * Interface representing an event listener target.
1356 *
1357 * @category models
1358 * @since 3.4.0
1359 */
1360export interface EventListener<A = unknown> {
1361 addEventListener(
1362 event: string,
1363 f: (event: A) => void,
1364 options?: {
1365 readonly capture?: boolean
1366 readonly passive?: boolean
1367 readonly once?: boolean
1368 readonly signal?: AbortSignal
1369 } | boolean
1370 ): void
1371 removeEventListener(
1372 event: string,
1373 f: (event: A) => void,
1374 options?: {
1375 readonly capture?: boolean
1376 } | boolean
1377 ): void
1378}
1379
1380/**
1381 * Creates a stream from an event listener.
1382 *
1383 * **Example** (Creating a stream from an event listener)
1384 *
1385 * ```ts import.meta.vitest
1386 * import { Effect, Stream } from "effect"
1387 *
1388 * class NumberTarget implements Stream.EventListener<number> {
1389 * addEventListener(event: string, f: (event: number) => void) {
1390 * if (event === "data") {
1391 * f(1)
1392 * f(2)
1393 * f(3)
1394 * }
1395 * }
1396 * removeEventListener(_event: string, _f: (event: number) => void) {}
1397 * }
1398 *
1399 * await Effect.runPromise(Effect.gen(function*() {
1400 * const stream = Stream.fromEventListener(new NumberTarget(), "data").pipe(
1401 * Stream.take(3)
1402 * )
1403 * const values = yield* Stream.runCollect(stream)
1404 * values // => [1, 2, 3]
1405 * }))
1406 *
1407 * ```
1408 *
1409 * @category constructors
1410 * @since 3.1.0
1411 */
1412export const fromEventListener = <A = unknown>(
1413 target: EventListener,
1414 type: string,
1415 options?: boolean | {
1416 readonly capture?: boolean
1417 readonly passive?: boolean
1418 readonly once?: boolean
1419 readonly bufferSize?: number | undefined
1420 } | undefined
1421): Stream<A> =>
1422 callback<A>((queue) => {
1423 const once = typeof options === "object" && options.once
1424
1425 function emit(event: any) {
1426 Queue.offerUnsafe(queue, event)
1427 if (once) Queue.endUnsafe(queue)
1428 }
1429 return Effect.acquireRelease(
1430 Effect.sync(() => target.addEventListener(type, emit, options)),
1431 () => Effect.sync(() => target.removeEventListener(type, emit, options))
1432 )
1433 }, { bufferSize: typeof options === "object" ? options.bufferSize : undefined })
1434
1435/**
1436 * Creates a stream by repeatedly applying an effectful step function to a
1437 * state.
1438 *
1439 * **Details**
1440 *
1441 * Each `readonly [value, nextState]` result emits `value` and continues with
1442 * `nextState`; returning `undefined` ends the stream.
1443 *
1444 * **Example** (Unfolding stream state)
1445 *
1446 * ```ts import.meta.vitest
1447 * import { Effect, Stream } from "effect"
1448 *
1449 * const program = Effect.gen(function*() {
1450 * const stream = Stream.unfold(1, (n) => Effect.succeed([n, n + 1] as const))
1451 * const values = yield* Stream.runCollect(stream.pipe(Stream.take(5)))
1452 * values // => [ 1, 2, 3, 4, 5 ]
1453 * })
1454 *
1455 * await Effect.runPromise(program)
1456 * ```
1457 *
1458 * @category constructors
1459 * @since 2.0.0
1460 */
1461export const unfold = <S, A, E, R>(
1462 s: S,
1463 f: (s: S) => Effect.Effect<readonly [A, S] | undefined, E, R>
1464): Stream<A, E, R> =>
1465 fromPull(Effect.sync(() => {
1466 let state = s
1467 return Effect.flatMap(Effect.suspend(() => f(state)), (next) => {
1468 if (next === undefined) return Cause.done()
1469 state = next[1]
1470 return Effect.succeed(Arr.of(next[0]))
1471 })
1472 }))
1473
1474/**
1475 * Creates a stream by repeatedly evaluating an effectful page function.
1476 *
1477 * **When to use**
1478 *
1479 * Use to consume paginated APIs where each step returns a batch of values
1480 * together with an optional next state.
1481 *
1482 * **Details**
1483 *
1484 * This is similar to {@link unfold}, but each step can emit zero or more values
1485 * and independently decide whether another state should be requested.
1486 *
1487 * **Example** (Paginating stream state)
1488 *
1489 * ```ts import.meta.vitest
1490 * import { Effect, Option, Stream } from "effect"
1491 *
1492 * const stream = Stream.paginate(0, (n: number) =>
1493 * Effect.succeed(
1494 * [
1495 * [n],
1496 * n < 3 ? Option.some(n + 1) : Option.none<number>()
1497 * ] as const
1498 * ))
1499 *
1500 * await Effect.runPromise(Stream.runCollect(stream)) // => [0, 1, 2, 3]
1501 * ```
1502 *
1503 * @category constructors
1504 * @since 2.0.0
1505 */
1506export const paginate = <S, A, E = never, R = never>(
1507 s: S,
1508 f: (
1509 s: S
1510 ) => Effect.Effect<readonly [ReadonlyArray<A>, Option.Option<S>], E, R>
1511): Stream<A, E, R> =>
1512 fromPull(Effect.sync(() => {
1513 let state = s
1514 let done = false
1515 return Effect.suspend(function loop(): Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E, void, R> {
1516 if (done) return Cause.done()
1517 return Effect.flatMap(f(state), ([a, s]) => {
1518 if (Option.isNone(s)) {
1519 done = true
1520 } else {
1521 state = s.value
1522 }
1523 if (!Arr.isReadonlyArrayNonEmpty(a)) return loop()
1524 return Effect.succeed(a)
1525 })
1526 })
1527 }))
1528
1529/**
1530 * Creates an infinite stream by repeatedly applying a function to a seed value.
1531 *
1532 * **Example** (Iterating from a seed value)
1533 *
1534 * ```ts import.meta.vitest
1535 * import { Effect, Stream } from "effect"
1536 *
1537 * const stream = Stream.iterate(1, (n) => n + 1).pipe(Stream.take(3))
1538 *
1539 * const program = Effect.gen(function* () {
1540 * const values = yield* Stream.runCollect(stream)
1541 * values // => [ 1, 2, 3 ]
1542 * })
1543 *
1544 * await Effect.runPromise(program)
1545 * ```
1546 *
1547 * @category constructors
1548 * @since 2.0.0
1549 */
1550export const iterate = <A>(value: A, next: (value: A) => A): Stream<A> =>
1551 unfold(value, (a) => Effect.succeed([a, next(a)]))
1552
1553/**
1554 * Constructs a stream from a range of integers, including both endpoints.
1555 *
1556 * **Details**
1557 *
1558 * If the provided `min` is greater than `max`, the stream will not emit any
1559 * values. Finite fractional `chunkSize` values are rounded down, while `NaN`
1560 * and non-positive values are treated as `1`.
1561 *
1562 * **Example** (Creating a numeric range)
1563 *
1564 * ```ts import.meta.vitest
1565 * import { Effect, Stream } from "effect"
1566 *
1567 * const program = Effect.gen(function*() {
1568 * const values = yield* Stream.range(1, 5).pipe(Stream.runCollect)
1569 * values // => [ 1, 2, 3, 4, 5 ]
1570 * })
1571 *
1572 * await Effect.runPromise(program)
1573 * ```
1574 *
1575 * @category constructors
1576 * @since 2.0.0
1577 */
1578export const range = (
1579 min: number,
1580 max: number,
1581 chunkSize = Channel.DefaultChunkSize
1582): Stream<number> =>
1583 min > max ? empty : fromPull(Effect.sync(() => {
1584 const size = Count.normalizeNonEmpty(chunkSize)
1585 let start = min
1586 let done = false
1587 return Effect.suspend(() => {
1588 if (done) return Cause.done()
1589 const remaining = max - start + 1
1590 if (remaining > size) {
1591 const chunk = Arr.range(start, start + size - 1)
1592 start += size
1593 return Effect.succeed(chunk)
1594 }
1595 const chunk = Arr.range(start, start + remaining - 1)
1596 done = true
1597 return Effect.succeed(chunk)
1598 })
1599 }))
1600
1601/**
1602 * The stream that never produces any value or fails with any error.
1603 *
1604 * **Example** (Creating a never-ending stream)
1605 *
1606 * ```ts import.meta.vitest
1607 * import { Effect, Stream } from "effect"
1608 *
1609 * const program = Stream.never.pipe(
1610 * Stream.take(0),
1611 * Stream.runCollect
1612 * )
1613 *
1614 * await Effect.runPromise(program) // => []
1615 * ```
1616 *
1617 * @category constructors
1618 * @since 2.0.0
1619 */
1620export const never: Stream<never> = fromChannel(Channel.never)
1621
1622/**
1623 * Creates a stream produced from an `Effect`.
1624 *
1625 * **Example** (Unwrapping a stream effect)
1626 *
1627 * ```ts import.meta.vitest
1628 * import { Effect, Stream } from "effect"
1629 *
1630 * const effect = Effect.succeed(Stream.make(1, 2, 3))
1631 *
1632 * const stream = Stream.unwrap(effect)
1633 *
1634 * const program = Effect.gen(function*() {
1635 * const chunk = yield* Stream.runCollect(stream)
1636 * chunk // => [ 1, 2, 3 ]
1637 * })
1638 * await Effect.runPromise(program)
1639 * ```
1640 *
1641 * @category constructors
1642 * @since 2.0.0
1643 */
1644export const unwrap = <A, E2, R2, E, R>(
1645 effect: Effect.Effect<Stream<A, E2, R2>, E, R>
1646): Stream<A, E | E2, R2 | Exclude<R, Scope.Scope>> => fromChannel(Channel.unwrap(Effect.map(effect, toChannel)))
1647
1648/**
1649 * Runs a stream that requires `Scope` in a managed scope, ensuring its
1650 * finalizers are run when the stream completes.
1651 *
1652 * **Example** (Scoping a stream)
1653 *
1654 * ```ts import.meta.vitest
1655 * import { Effect, Stream } from "effect"
1656 *
1657 * const events: Array<string> = []
1658 * const stream = Stream.scoped(
1659 * Stream.fromEffect(
1660 * Effect.acquireRelease(
1661 * Effect.sync(() => {
1662 * events.push("acquire")
1663 * return "resource"
1664 * }),
1665 * () => Effect.sync(() => events.push("release"))
1666 * )
1667 * )
1668 * )
1669 *
1670 * await Effect.runPromise(Stream.runCollect(stream)) // => ["resource"]
1671 * events // => ["acquire", "release"]
1672 * ```
1673 *
1674 * @category resource management
1675 * @since 2.0.0
1676 */
1677export const scoped = <A, E, R>(
1678 self: Stream<A, E, R>
1679): Stream<A, E, Exclude<R, Scope.Scope>> => fromChannel(Channel.scoped(self.channel))
1680
1681/**
1682 * Transforms the elements of this stream using the supplied function.
1683 *
1684 * **Example** (Mapping stream values)
1685 *
1686 * ```ts import.meta.vitest
1687 * import { Effect, Option, Stream } from "effect"
1688 *
1689 * const stream = Stream.fromArray([1, 2, 3]).pipe(Stream.map((n, i) => n + i))
1690 * await Effect.runPromise(Stream.runCollect(stream)) // => [1, 3, 5]
1691 * ```
1692 *
1693 * @category mapping
1694 * @since 2.0.0
1695 */
1696export const map: {
1697 <A, B>(f: (a: A, i: number) => B): <E, R>(self: Stream<A, E, R>) => Stream<B, E, R>
1698 <A, E, R, B>(self: Stream<A, E, R>, f: (a: A, i: number) => B): Stream<B, E, R>
1699} = dual(2, <A, E, R, B>(self: Stream<A, E, R>, f: (a: A, i: number) => B): Stream<B, E, R> =>
1700 suspend(() => {
1701 let i = 0
1702 return fromChannel(Channel.map(
1703 self.channel,
1704 Arr.map((o) => f(o, i++))
1705 ))
1706 }))
1707
1708/**
1709 * Replaces every element of the stream with the provided constant value.
1710 *
1711 * **Example** (Replacing stream elements)
1712 *
1713 * ```ts import.meta.vitest
1714 * import { Effect, Stream } from "effect"
1715 *
1716 * const program = Effect.gen(function*() {
1717 * const values = yield* Stream.make(1, 2, 3).pipe(
1718 * Stream.as("x"),
1719 * Stream.runCollect
1720 * )
1721 * values // => [ 'x', 'x', 'x' ]
1722 * })
1723 *
1724 * await Effect.runPromise(program)
1725 * ```
1726 *
1727 * @see {@link map} for deriving the replacement value from each element
1728 *
1729 * @category mapping
1730 * @since 4.0.0
1731 */
1732export const as: {
1733 <B>(value: B): <A, E, R>(self: Stream<A, E, R>) => Stream<B, E, R>
1734 <A, E, R, B>(self: Stream<A, E, R>, value: B): Stream<B, E, R>
1735} = dual(2, <A, E, R, B>(self: Stream<A, E, R>, value: B): Stream<B, E, R> => map(self, () => value))
1736
1737/**
1738 * Maps both the failure and success channels of a stream.
1739 *
1740 * **Example** (Mapping both the failure and success channels of a stream)
1741 *
1742 * ```ts import.meta.vitest
1743 * import { Effect, Stream } from "effect"
1744 *
1745 * const mapper = {
1746 * onElement: (value: number) => value * 2,
1747 * onError: (error: string) => `error: ${error}`
1748 * }
1749 *
1750 * const program = Effect.gen(function*() {
1751 * const success = yield* Stream.make(1, 2).pipe(
1752 * Stream.mapBoth(mapper),
1753 * Stream.runCollect
1754 * )
1755 * success // => [ 2, 4 ]
1756 *
1757 * const failure = yield* Stream.fail("boom").pipe(
1758 * Stream.mapBoth(mapper),
1759 * Stream.catch((error: string) => Stream.succeed(error)),
1760 * Stream.runCollect
1761 * )
1762 * failure // => [ 'error: boom' ]
1763 * })
1764 *
1765 * await Effect.runPromise(program)
1766 * ```
1767 *
1768 * @category mapping
1769 * @since 2.0.0
1770 */
1771export const mapBoth: {
1772 <A, A2, E, E2>(
1773 options: { readonly onElement: (a: A) => A2; readonly onError: (e: E) => E2 }
1774 ): <R>(self: Stream<A, E, R>) => Stream<A2, E2, R>
1775 <A, E, R, A2, E2>(
1776 self: Stream<A, E, R>,
1777 options: { readonly onElement: (a: A) => A2; readonly onError: (e: E) => E2 }
1778 ): Stream<A2, E2, R>
1779} = dual(2, <A, E, R, A2, E2>(
1780 self: Stream<A, E, R>,
1781 options: { readonly onElement: (a: A) => A2; readonly onError: (e: E) => E2 }
1782): Stream<A2, E2, R> =>
1783 self.pipe(
1784 map(options.onElement),
1785 mapError(options.onError)
1786 ))
1787
1788/**
1789 * Transforms each emitted chunk using the provided function, which receives the chunk and its index.
1790 *
1791 * **Example** (Mapping stream chunks)
1792 *
1793 * ```ts import.meta.vitest
1794 * import { Array, Effect, Stream } from "effect"
1795 *
1796 * const program = Effect.gen(function*() {
1797 * const result = yield* Stream.make(1, 2, 3, 4).pipe(
1798 * Stream.rechunk(2),
1799 * Stream.mapArray((chunk, index) => Array.map(chunk, (n) => n + index)),
1800 * Stream.runCollect
1801 * )
1802 * result // => [ 1, 2, 4, 5 ]
1803 * })
1804 *
1805 * await Effect.runPromise(program)
1806 * ```
1807 *
1808 * @category mapping
1809 * @since 4.0.0
1810 */
1811export const mapArray: {
1812 <A, B>(
1813 f: (a: Arr.NonEmptyReadonlyArray<A>, i: number) => Arr.NonEmptyReadonlyArray<B>
1814 ): <E, R>(self: Stream<A, E, R>) => Stream<B, E, R>
1815 <A, E, R, B>(
1816 self: Stream<A, E, R>,
1817 f: (a: Arr.NonEmptyReadonlyArray<A>, i: number) => Arr.NonEmptyReadonlyArray<B>
1818 ): Stream<B, E, R>
1819} = dual(2, <A, E, R, B>(
1820 self: Stream<A, E, R>,
1821 f: (a: Arr.NonEmptyReadonlyArray<A>, i: number) => Arr.NonEmptyReadonlyArray<B>
1822): Stream<B, E, R> => fromChannel(Channel.map(self.channel, f)))
1823
1824/**
1825 * Maps over elements of the stream with the specified effectful function.
1826 *
1827 * **When to use**
1828 *
1829 * Use when each stream element transformation needs an Effect, service
1830 * dependency, failure channel, or configured concurrency.
1831 *
1832 * **Example** (Effectfully mapping stream values)
1833 *
1834 * ```ts import.meta.vitest
1835 * import { Effect, Stream } from "effect"
1836 *
1837 * const events: Array<string> = []
1838 * const stream = Stream.make(1, 2, 3)
1839 *
1840 * const mappedStream = stream.pipe(
1841 * Stream.mapEffect((n) =>
1842 * Effect.sync(() => {
1843 * events.push(`Processing: ${n}`)
1844 * return n * 2
1845 * })
1846 * )
1847 * )
1848 *
1849 * const program = Effect.gen(function*() {
1850 * const result = yield* Stream.runCollect(mappedStream)
1851 * result // => [2, 4, 6]
1852 * })
1853 *
1854 * await Effect.runPromise(program)
1855 * events // => ["Processing: 1", "Processing: 2", "Processing: 3"]
1856 * ```
1857 *
1858 * @category mapping
1859 * @since 2.0.0
1860 */
1861export const mapEffect: {
1862 <A, A2, E2, R2>(
1863 f: (a: A, i: number) => Effect.Effect<A2, E2, R2>,
1864 options?: {
1865 readonly concurrency?: Concurrency | undefined
1866 readonly unordered?: boolean | undefined
1867 } | undefined
1868 ): <E, R>(self: Stream<A, E, R>) => Stream<A2, E2 | E, R2 | R>
1869 <A, E, R, A2, E2, R2>(
1870 self: Stream<A, E, R>,
1871 f: (a: A, i: number) => Effect.Effect<A2, E2, R2>,
1872 options?: {
1873 readonly concurrency?: Concurrency | undefined
1874 readonly unordered?: boolean | undefined
1875 } | undefined
1876 ): Stream<A2, E | E2, R | R2>
1877} = dual((args) => isStream(args[0]), <A, E, R, A2, E2, R2>(
1878 self: Stream<A, E, R>,
1879 f: (a: A, i: number) => Effect.Effect<A2, E2, R2>,
1880 options?: {
1881 readonly concurrency?: Concurrency | undefined
1882 readonly unordered?: boolean | undefined
1883 } | undefined
1884): Stream<A2, E | E2, R | R2> =>
1885 self.channel.pipe(
1886 Channel.flattenArray,
1887 Channel.mapEffect(f, options),
1888 Channel.map(Arr.of),
1889 fromChannel
1890 ))
1891
1892/**
1893 * Flattens a stream of `Effect` values into a stream of their results.
1894 *
1895 * **When to use**
1896 *
1897 * Use when stream elements already are effects and their successes should become
1898 * stream elements while their failures enter the stream error channel.
1899 *
1900 * **Example** (Flattening a stream of Effect values into a stream of their results)
1901 *
1902 * ```ts import.meta.vitest
1903 * import { Effect, Stream } from "effect"
1904 *
1905 * const stream = Stream.make(Effect.succeed(1), Effect.succeed(2), Effect.succeed(3))
1906 *
1907 * const program = Effect.gen(function*() {
1908 * const result = yield* Stream.runCollect(stream.pipe(Stream.flattenEffect()))
1909 * result // => [ 1, 2, 3 ]
1910 * })
1911 *
1912 * await Effect.runPromise(program)
1913 * ```
1914 *
1915 * @category sequencing
1916 * @since 2.0.0
1917 */
1918export const flattenEffect: <
1919 Arg extends Stream<Effect.Effect<any, any, any>, any, any> | {
1920 readonly concurrency?: Concurrency | undefined
1921 readonly unordered?: boolean | undefined
1922 } | undefined = {
1923 readonly concurrency?: Concurrency | undefined
1924 readonly unordered?: boolean | undefined
1925 }
1926>(
1927 selfOrOptions?: Arg,
1928 options?: {
1929 readonly concurrency?: Concurrency | undefined
1930 readonly unordered?: boolean | undefined
1931 } | undefined
1932) => [Arg] extends [Stream<Effect.Effect<infer _A, infer _EX, infer _RX>, infer _E, infer _R>] ?
1933 Stream<_A, _EX | _E, _RX | _R>
1934 : <A, EX, RX, E, R>(self: Stream<Effect.Effect<A, EX, RX>, E, R>) => Stream<A, EX | E, RX | R> = dual(
1935 (args) => isStream(args[0]),
1936 <A, E, R, EX, RX>(
1937 self: Stream<Effect.Effect<A, EX, RX>, E, R>,
1938 options?: {
1939 readonly concurrency?: Concurrency | undefined
1940 readonly unordered?: boolean | undefined
1941 } | undefined
1942 ): Stream<A, EX | E, RX | R> => mapEffect(self, identity, options)
1943 )
1944
1945/**
1946 * Maps over non-empty array chunks emitted by the stream effectfully.
1947 *
1948 * **When to use**
1949 *
1950 * Use when transformation needs to see and replace each non-empty emitted chunk
1951 * effectfully instead of mapping individual stream elements.
1952 *
1953 * **Example** (Effectfully mapping stream chunks)
1954 *
1955 * ```ts import.meta.vitest
1956 * import { Array, Effect, Stream } from "effect"
1957 *
1958 * const program = Effect.gen(function*() {
1959 * const result = yield* Stream.fromArray([1, 2, 3, 4]).pipe(
1960 * Stream.rechunk(2),
1961 * Stream.mapArrayEffect((chunk, index) =>
1962 * Effect.succeed(Array.map(chunk, (n) => n + index * 10))
1963 * ),
1964 * Stream.runCollect
1965 * )
1966 * result // => [ 1, 2, 13, 14 ]
1967 * })
1968 *
1969 * await Effect.runPromise(program)
1970 * ```
1971 *
1972 * @category mapping
1973 * @since 4.0.0
1974 */
1975export const mapArrayEffect: {
1976 <A, B, E2, R2>(
1977 f: (a: Arr.NonEmptyReadonlyArray<A>, i: number) => Effect.Effect<Arr.NonEmptyReadonlyArray<B>, E2, R2>
1978 ): <E, R>(self: Stream<A, E, R>) => Stream<B, E | E2, R | R2>
1979 <A, E, R, B, E2, R2>(
1980 self: Stream<A, E, R>,
1981 f: (a: Arr.NonEmptyReadonlyArray<A>, i: number) => Effect.Effect<Arr.NonEmptyReadonlyArray<B>, E2, R2>
1982 ): Stream<B, E | E2, R | R2>
1983} = dual(2, <A, E, R, B, E2, R2>(
1984 self: Stream<A, E, R>,
1985 f: (a: Arr.NonEmptyReadonlyArray<A>, i: number) => Effect.Effect<Arr.NonEmptyReadonlyArray<B>, E2, R2>
1986): Stream<B, E | E2, R | R2> => fromChannel(Channel.mapEffect(self.channel, f)))
1987
1988/**
1989 * Lifts failures and successes into a `Result`, yielding a stream that cannot fail.
1990 *
1991 * **Details**
1992 *
1993 * The stream ends after the first failure, emitting a `Result.fail` value.
1994 *
1995 * **Example** (Converting failures to results)
1996 *
1997 * ```ts import.meta.vitest
1998 * import { Effect, Result, Stream } from "effect"
1999 *
2000 * const program = Effect.gen(function*() {
2001 * const results = yield* Stream.make(1, 2).pipe(
2002 * Stream.concat(Stream.fail("boom")),
2003 * Stream.result,
2004 * Stream.map(Result.match({
2005 * onFailure: (error) => `failure: ${error}`,
2006 * onSuccess: (value) => `success: ${value}`
2007 * })),
2008 * Stream.runCollect
2009 * )
2010 * results // => [ 'success: 1', 'success: 2', 'failure: boom' ]
2011 * })
2012 *
2013 * await Effect.runPromise(program)
2014 * ```
2015 *
2016 * @category error handling
2017 * @since 4.0.0
2018 */
2019export const result = <A, E, R>(self: Stream<A, E, R>): Stream<Result.Result<A, E>, never, R> =>
2020 self.pipe(
2021 map(Result.succeed),
2022 catch_((e) => succeed(Result.fail(e)))
2023 )
2024
2025/**
2026 * Runs the provided effect for each element while preserving the elements.
2027 *
2028 * **Example** (Tapping stream values)
2029 *
2030 * ```ts import.meta.vitest
2031 * import { Effect, Stream } from "effect"
2032 *
2033 * const events: Array<string> = []
2034 * const program = Effect.gen(function*() {
2035 * const result = yield* Stream.fromArray([1, 2, 3]).pipe(
2036 * Stream.tap((n) => Effect.sync(() => events.push(`before mapping: ${n}`))),
2037 * Stream.map((n) => n * 2),
2038 * Stream.tap((n) => Effect.sync(() => events.push(`after mapping: ${n}`))),
2039 * Stream.runCollect
2040 * )
2041 *
2042 * result // => [2, 4, 6]
2043 * })
2044 *
2045 * await Effect.runPromise(program)
2046 * events // => ["before mapping: 1", "after mapping: 2", "before mapping: 2", "after mapping: 4", "before mapping: 3", "after mapping: 6"]
2047 * ```
2048 *
2049 * @category sequencing
2050 * @since 2.0.0
2051 */
2052export const tap: {
2053 <A, X, E2, R2>(
2054 f: (a: NoInfer<A>) => Effect.Effect<X, E2, R2>,
2055 options?: {
2056 readonly concurrency?: Concurrency | undefined
2057 } | undefined
2058 ): <E, R>(self: Stream<A, E, R>) => Stream<A, E2 | E, R2 | R>
2059 <A, E, R, X, E2, R2>(
2060 self: Stream<A, E, R>,
2061 f: (a: NoInfer<A>) => Effect.Effect<X, E2, R2>,
2062 options?: {
2063 readonly concurrency?: Concurrency | undefined
2064 } | undefined
2065 ): Stream<A, E | E2, R | R2>
2066} = dual((args) => isStream(args[0]), <A, E, R, X, E2, R2>(
2067 self: Stream<A, E, R>,
2068 f: (a: NoInfer<A>) => Effect.Effect<X, E2, R2>,
2069 options?: {
2070 readonly concurrency?: Concurrency | undefined
2071 } | undefined
2072): Stream<A, E | E2, R | R2> =>
2073 mapEffect(
2074 self,
2075 (a) => Effect.as(f(a), a),
2076 options
2077 ))
2078
2079/**
2080 * Returns a stream that effectfully "peeks" at elements and failures.
2081 *
2082 * **Example** (Tapping values and errors)
2083 *
2084 * ```ts import.meta.vitest
2085 * import { Effect, Stream } from "effect"
2086 *
2087 * const events: Array<string> = []
2088 * const program = Effect.gen(function*() {
2089 * const stream = Stream.make(1, 2).pipe(
2090 * Stream.concat(Stream.fail("boom")),
2091 * Stream.tapBoth({
2092 * onElement: (value) => Effect.sync(() => events.push(`seen: ${value}`)),
2093 * onError: (error) => Effect.sync(() => events.push(`error: ${error}`))
2094 * }),
2095 * Stream.catch(() => Stream.make(3))
2096 * )
2097 * const result = yield* Stream.runCollect(stream)
2098 * result // => [1, 2, 3]
2099 * })
2100 *
2101 * await Effect.runPromise(program)
2102 * events // => ["seen: 1", "seen: 2", "error: boom"]
2103 * ```
2104 *
2105 * @category sequencing
2106 * @since 2.0.0
2107 */
2108export const tapBoth: {
2109 <A, E, X, E2, R2, Y, E3, R3>(
2110 options: {
2111 readonly onElement: (a: NoInfer<A>) => Effect.Effect<X, E2, R2>
2112 readonly onError: (a: NoInfer<E>) => Effect.Effect<Y, E3, R3>
2113 readonly concurrency?: Concurrency | undefined
2114 }
2115 ): <R>(self: Stream<A, E, R>) => Stream<A, E | E2 | E3, R | R2 | R3>
2116 <A, E, R, X, E2, R2, Y, E3, R3>(
2117 self: Stream<A, E, R>,
2118 options: {
2119 readonly onElement: (a: NoInfer<A>) => Effect.Effect<X, E2, R2>
2120 readonly onError: (a: NoInfer<E>) => Effect.Effect<Y, E3, R3>
2121 readonly concurrency?: Concurrency | undefined
2122 }
2123 ): Stream<A, E | E2 | E3, R | R2 | R3>
2124} = dual(2, <A, E, R, X, E2, R2, Y, E3, R3>(
2125 self: Stream<A, E, R>,
2126 options: {
2127 readonly onElement: (a: NoInfer<A>) => Effect.Effect<X, E2, R2>
2128 readonly onError: (a: NoInfer<E>) => Effect.Effect<Y, E3, R3>
2129 readonly concurrency?: Concurrency | undefined
2130 }
2131): Stream<A, E | E2 | E3, R | R2 | R3> =>
2132 self.pipe(
2133 tapError(options.onError),
2134 tap(options.onElement, { concurrency: options.concurrency })
2135 ))
2136
2137/**
2138 * Runs a sink for all stream elements while still emitting them downstream.
2139 *
2140 * **Example** (Tapping values with a sink)
2141 *
2142 * ```ts import.meta.vitest
2143 * import { Effect, Ref, Sink, Stream } from "effect"
2144 *
2145 * const program = Effect.gen(function*() {
2146 * const seen = yield* Ref.make<Array<number>>([])
2147 * const sink = Sink.forEach((value: number) =>
2148 * Ref.update(seen, (items) => [...items, value])
2149 * )
2150 * const result = yield* Stream.make(1, 2, 3).pipe(
2151 * Stream.tapSink(sink),
2152 * Stream.runCollect
2153 * )
2154 * const tapped = yield* Ref.get(seen)
2155 * tapped // => [ 1, 2, 3 ]
2156 * result // => [ 1, 2, 3 ]
2157 * })
2158 *
2159 * await Effect.runPromise(program)
2160 * ```
2161 *
2162 * @category sequencing
2163 * @since 2.0.0
2164 */
2165export const tapSink: {
2166 <A, E2, R2>(sink: Sink.Sink<unknown, A, unknown, E2, R2>): <E, R>(self: Stream<A, E, R>) => Stream<A, E2 | E, R2 | R>
2167 <A, E, R, E2, R2>(self: Stream<A, E, R>, sink: Sink.Sink<unknown, A, unknown, E2, R2>): Stream<A, E | E2, R | R2>
2168} = dual(
2169 2,
2170 <A, E, R, E2, R2>(
2171 self: Stream<A, E, R>,
2172 sink: Sink.Sink<unknown, A, unknown, E2, R2>
2173 ): Stream<A, E | E2, R | R2> =>
2174 transformPullBracket(
2175 self,
2176 Effect.fnUntraced(function*(pull, _, scope) {
2177 const upstreamLatch = Latch.makeUnsafe()
2178 const sinkLatch = Latch.makeUnsafe()
2179 let chunk: Arr.NonEmptyReadonlyArray<A> | undefined = undefined
2180 let causeSink: Cause.Cause<E2> | undefined = undefined
2181 let sinkDone = false
2182 let streamDone = false
2183
2184 const sinkUpstream = upstreamLatch.whenOpen(Effect.suspend(() => {
2185 if (chunk) {
2186 const arr = chunk!
2187 chunk = undefined
2188 if (!streamDone) upstreamLatch.closeUnsafe()
2189 return Effect.as(sinkLatch.open, arr)
2190 }
2191 return Cause.done()
2192 }))
2193
2194 yield* Effect.suspend(() => sink.transform(sinkUpstream, scope)).pipe(
2195 (eff) =>
2196 Effect.onExitPrimitive(eff, (exit) => {
2197 sinkDone = true
2198 if (Exit.isFailure(exit)) {
2199 causeSink = exit.cause
2200 }
2201 return sinkLatch.open
2202 }, true),
2203 Effect.forkIn(scope)
2204 )
2205
2206 const pullAndOffer = pull.pipe(
2207 Effect.flatMap((chunk_) => {
2208 chunk = chunk_
2209 sinkLatch.closeUnsafe()
2210 upstreamLatch.openUnsafe()
2211 return Effect.as(sinkLatch.await, chunk_)
2212 }),
2213 Pull.catchDone(() => {
2214 streamDone = true
2215 sinkLatch.closeUnsafe()
2216 upstreamLatch.openUnsafe()
2217 return Effect.flatMap(sinkLatch.await, () => Cause.done())
2218 })
2219 )
2220
2221 return Effect.suspend((): Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E | E2, void, R> => {
2222 if (causeSink) {
2223 return Effect.failCause(causeSink)
2224 } else if (sinkDone) {
2225 return pull
2226 }
2227 return pullAndOffer
2228 })
2229 })
2230 )
2231)
2232
2233/**
2234 * Maps each element to a stream and flattens the resulting streams.
2235 *
2236 * **Details**
2237 *
2238 * With the default sequential concurrency, inner streams are concatenated in
2239 * input order. When `concurrency` is greater than `1` or `"unbounded"`,
2240 * multiple inner streams may run at the same time and their outputs are merged
2241 * as they arrive.
2242 *
2243 * **Example** (Flat mapping stream values)
2244 *
2245 * ```ts import.meta.vitest
2246 * import { Effect, Stream } from "effect"
2247 *
2248 * const program = Effect.gen(function*() {
2249 * const values = yield* Stream.make(1, 2, 3).pipe(
2250 * Stream.flatMap((n) => Stream.make(n, n * 2)),
2251 * Stream.runCollect
2252 * )
2253 * values // => [ 1, 2, 2, 4, 3, 6 ]
2254 * })
2255 *
2256 * await Effect.runPromise(program)
2257 * ```
2258 *
2259 * @category sequencing
2260 * @since 2.0.0
2261 */
2262export const flatMap: {
2263 <A, A2, E2, R2>(
2264 f: (a: A) => Stream<A2, E2, R2>,
2265 options?: {
2266 readonly concurrency?: Concurrency | undefined
2267 readonly bufferSize?: number | undefined
2268 } | undefined
2269 ): <E, R>(self: Stream<A, E, R>) => Stream<A2, E2 | E, R2 | R>
2270 <A, E, R, A2, E2, R2>(
2271 self: Stream<A, E, R>,
2272 f: (a: A) => Stream<A2, E2, R2>,
2273 options?: {
2274 readonly concurrency?: Concurrency | undefined
2275 readonly bufferSize?: number | undefined
2276 } | undefined
2277 ): Stream<A2, E | E2, R | R2>
2278} = dual((args) => isStream(args[0]), <A, E, R, A2, E2, R2>(
2279 self: Stream<A, E, R>,
2280 f: (a: A) => Stream<A2, E2, R2>,
2281 options?: {
2282 readonly concurrency?: Concurrency | undefined
2283 readonly bufferSize?: number | undefined
2284 } | undefined
2285): Stream<A2, E | E2, R | R2> =>
2286 self.channel.pipe(
2287 Channel.flattenArray,
2288 Channel.flatMap((a) => f(a).channel, options),
2289 fromChannel
2290 ))
2291
2292/**
2293 * Switches to the latest stream produced by the mapping function, interrupting
2294 * the previous stream when a new element arrives.
2295 *
2296 * **Example** (Switching to the latest stream)
2297 *
2298 * ```ts import.meta.vitest
2299 * import { Effect, Stream } from "effect"
2300 *
2301 * const program = Stream.make(1, 2, 3).pipe(
2302 * Stream.switchMap((n) => (n === 3 ? Stream.make(n) : Stream.never)),
2303 * Stream.runCollect
2304 * )
2305 *
2306 * await Effect.runPromise(Effect.gen(function*() {
2307 * const result = yield* program
2308 * result // => [ 3 ]
2309 * }))
2310 * ```
2311 *
2312 * @category sequencing
2313 * @since 4.0.0
2314 */
2315export const switchMap: {
2316 <A, A2, E2, R2>(
2317 f: (a: A) => Stream<A2, E2, R2>,
2318 options?: {
2319 readonly concurrency?: Concurrency | undefined
2320 readonly bufferSize?: number | undefined
2321 } | undefined
2322 ): <E, R>(self: Stream<A, E, R>) => Stream<A2, E2 | E, R2 | R>
2323 <A, E, R, A2, E2, R2>(
2324 self: Stream<A, E, R>,
2325 f: (a: A) => Stream<A2, E2, R2>,
2326 options?: {
2327 readonly concurrency?: Concurrency | undefined
2328 readonly bufferSize?: number | undefined
2329 } | undefined
2330 ): Stream<A2, E | E2, R | R2>
2331} = dual((args) => isStream(args[0]), <A, E, R, A2, E2, R2>(
2332 self: Stream<A, E, R>,
2333 f: (a: A) => Stream<A2, E2, R2>,
2334 options?: {
2335 readonly concurrency?: Concurrency | undefined
2336 readonly bufferSize?: number | undefined
2337 } | undefined
2338): Stream<A2, E | E2, R | R2> =>
2339 self.channel.pipe(
2340 Channel.flattenArray,
2341 Channel.switchMap((a) => f(a).channel, options),
2342 fromChannel
2343 ))
2344
2345/**
2346 * Flattens a stream of streams into a single stream.
2347 *
2348 * **Details**
2349 *
2350 * With the default sequential concurrency, inner streams are concatenated in
2351 * strict order. When `concurrency` is greater than `1` or `"unbounded"`,
2352 * multiple inner streams may run at the same time and their outputs are merged
2353 * as they arrive.
2354 *
2355 * **Example** (Flattening nested streams)
2356 *
2357 * ```ts import.meta.vitest
2358 * import { Effect, Stream } from "effect"
2359 *
2360 * const streamOfStreams = Stream.make(
2361 * Stream.make(1, 2),
2362 * Stream.make(3, 4),
2363 * Stream.make(5, 6)
2364 * )
2365 *
2366 * const program = Effect.gen(function*() {
2367 * const values = yield* Stream.runCollect(Stream.flatten(streamOfStreams))
2368 * values // => [ 1, 2, 3, 4, 5, 6 ]
2369 * })
2370 *
2371 * await Effect.runPromise(program)
2372 * ```
2373 *
2374 * @category sequencing
2375 * @since 2.0.0
2376 */
2377export const flatten: <
2378 Arg extends Stream<Stream<any, any, any>, any, any> | {
2379 readonly concurrency?: Concurrency | undefined
2380 readonly bufferSize?: number | undefined
2381 } | undefined = {
2382 readonly concurrency?: Concurrency | undefined
2383 readonly bufferSize?: number | undefined
2384 }
2385>(
2386 selfOrOptions?: Arg,
2387 options?: {
2388 readonly concurrency?: Concurrency | undefined
2389 readonly bufferSize?: number | undefined
2390 } | undefined
2391) => [Arg] extends [Stream<Stream<infer _A, infer _E, infer _R>, infer _E2, infer _R2>] ? Stream<_A, _E | _E2, _R | _R2>
2392 : <A, E, R, E2, R2>(self: Stream<Stream<A, E, R>, E2, R2>) => Stream<A, E | E2, R | R2> = dual(
2393 (args) => isStream(args[0]),
2394 <A, E, R, E2, R2>(
2395 self: Stream<Stream<A, E, R>, E2, R2>,
2396 options?: {
2397 readonly concurrency?: Concurrency | undefined
2398 readonly bufferSize?: number | undefined
2399 } | undefined
2400 ): Stream<A, E | E2, R | R2> => flatMap(self, identity, options)
2401 )
2402
2403/**
2404 * Flattens a stream of non-empty arrays into a stream of elements.
2405 *
2406 * **Example** (Flattening a stream of non-empty arrays into a stream of elements)
2407 *
2408 * ```ts import.meta.vitest
2409 * import { Array, Effect, Stream } from "effect"
2410 *
2411 * const stream = Stream.make(Array.make(1, 2), Array.make(3))
2412 *
2413 * const program = Effect.gen(function* () {
2414 * const result = yield* Stream.runCollect(Stream.flattenArray(stream))
2415 * result // => [ 1, 2, 3 ]
2416 * })
2417 *
2418 * await Effect.runPromise(program)
2419 * ```
2420 *
2421 * @category sequencing
2422 * @since 4.0.0
2423 */
2424export const flattenArray = <A, E, R>(self: Stream<Arr.NonEmptyReadonlyArray<A>, E, R>): Stream<A, E, R> =>
2425 fromChannel(Channel.flattenArray(self.channel))
2426
2427/**
2428 * Converts this stream to one that runs its effects but emits no elements.
2429 *
2430 * **Example** (Draining stream values)
2431 *
2432 * ```ts import.meta.vitest
2433 * import { Effect, Stream } from "effect"
2434 *
2435 * const program = Effect.gen(function*() {
2436 * const result = yield* Stream.range(1, 6).pipe(Stream.drain, Stream.runCollect)
2437 * result // => []
2438 * })
2439 *
2440 * await Effect.runPromise(program)
2441 * ```
2442 *
2443 * @category sequencing
2444 * @since 2.0.0
2445 */
2446export const drain = <A, E, R>(self: Stream<A, E, R>): Stream<never, E, R> => fromChannel(Channel.drain(self.channel))
2447
2448/**
2449 * Runs the provided stream in the background while this stream runs, interrupting it
2450 * when this stream completes and failing if the background stream fails or defects.
2451 *
2452 * **Example** (Draining a stream in the background)
2453 *
2454 * ```ts import.meta.vitest
2455 * import { Effect, Stream } from "effect"
2456 *
2457 * const events: Array<string> = []
2458 * const foreground = Stream.make(1, 2)
2459 * const background = Stream.fromEffect(Effect.sync(() => events.push("background task")))
2460 *
2461 * const program = Effect.gen(function*() {
2462 * const values = yield* foreground.pipe(
2463 * Stream.drainFork(background),
2464 * Stream.runCollect
2465 * )
2466 * values // => [1, 2]
2467 * })
2468 *
2469 * await Effect.runPromise(program)
2470 * events // => ["background task"]
2471 * ```
2472 *
2473 * @category sequencing
2474 * @since 2.0.0
2475 */
2476export const drainFork: {
2477 <A2, E2, R2>(that: Stream<A2, E2, R2>): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E2 | E, R2 | R>
2478 <A, E, R, A2, E2, R2>(self: Stream<A, E, R>, that: Stream<A2, E2, R2>): Stream<A, E | E2, R | R2>
2479} = dual(
2480 2,
2481 <A, E, R, A2, E2, R2>(self: Stream<A, E, R>, that: Stream<A2, E2, R2>): Stream<A, E | E2, R | R2> =>
2482 mergeEffect(self, runDrain(that))
2483)
2484
2485/**
2486 * Repeats the entire stream according to the provided schedule.
2487 *
2488 * **Example** (Repeating a stream on a schedule)
2489 *
2490 * ```ts import.meta.vitest
2491 * import { Effect, Schedule, Stream } from "effect"
2492 *
2493 * const program = Effect.gen(function* () {
2494 * const result = yield* Stream.make(1).pipe(
2495 * Stream.repeat(Schedule.recurs(4)),
2496 * Stream.runCollect
2497 * )
2498 *
2499 * result // => [ 1, 1, 1, 1, 1 ]
2500 * })
2501 *
2502 * await Effect.runPromise(program)
2503 * ```
2504 *
2505 * @category repetition
2506 * @since 2.0.0
2507 */
2508export const repeat: {
2509 <B, E2, R2>(
2510 schedule:
2511 | Schedule.Schedule<B, void, E2, R2>
2512 | ((
2513 $: <SO, SE, SR>(_: Schedule.Schedule<SO, void, SE, SR>) => Schedule.Schedule<SO, void, SE, SR>
2514 ) => Schedule.Schedule<B, void, E2, R2>)
2515 ): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E | E2, R2 | R>
2516 <A, E, R, B, E2, R2>(
2517 self: Stream<A, E, R>,
2518 schedule:
2519 | Schedule.Schedule<B, void, E2, R2>
2520 | ((
2521 $: <SO, SE, SR>(_: Schedule.Schedule<SO, void, SE, SR>) => Schedule.Schedule<SO, void, SE, SR>
2522 ) => Schedule.Schedule<B, void, E2, R2>)
2523 ): Stream<A, E | E2, R | R2>
2524} = dual(2, <A, E, R, B, E2, R2>(
2525 self: Stream<A, E, R>,
2526 schedule:
2527 | Schedule.Schedule<B, void, E2, R2>
2528 | ((
2529 $: <SO, SE, SR>(_: Schedule.Schedule<SO, void, SE, SR>) => Schedule.Schedule<SO, void, SE, SR>
2530 ) => Schedule.Schedule<B, void, E2, R2>)
2531): Stream<A, E | E2, R | R2> => fromChannel(Channel.repeat(self.channel, schedule)))
2532
2533/**
2534 * Schedules the stream's elements according to the provided schedule.
2535 *
2536 * **Example** (Scheduling stream elements)
2537 *
2538 * ```ts import.meta.vitest
2539 * import { Effect, Schedule, Stream } from "effect"
2540 *
2541 * const program = Effect.gen(function*() {
2542 * const result = yield* Stream.make(1, 2, 3).pipe(
2543 * Stream.schedule(Schedule.recurs(3)),
2544 * Stream.runCollect
2545 * )
2546 *
2547 * result // => [ 1, 2, 3 ]
2548 * })
2549 *
2550 * await Effect.runPromise(program)
2551 * ```
2552 *
2553 * @category rate limiting
2554 * @since 2.0.0
2555 */
2556export const schedule: {
2557 <X, E2, R2, A>(
2558 schedule: Schedule.Schedule<X, NoInfer<A>, E2, R2>
2559 ): <E, R>(self: Stream<A, E, R>) => Stream<A, E | E2, R2 | R>
2560 <A, E, R, X, E2, R2>(
2561 self: Stream<A, E, R>,
2562 schedule: Schedule.Schedule<X, NoInfer<A>, E2, R2>
2563 ): Stream<A, E | E2, R | R2>
2564} = dual(2, <A, E, R, X, E2, R2>(
2565 self: Stream<A, E, R>,
2566 schedule: Schedule.Schedule<X, NoInfer<A>, E2, R2>
2567): Stream<A, E | E2, R | R2> =>
2568 self.channel.pipe(
2569 Channel.flattenArray,
2570 Channel.schedule(schedule),
2571 Channel.map(Arr.of),
2572 fromChannel
2573 ))
2574
2575/**
2576 * Ends the stream if it does not produce a value within the specified duration.
2577 *
2578 * **Example** (Timing out a stream)
2579 *
2580 * ```ts import.meta.vitest
2581 * import { Effect, Stream } from "effect"
2582 *
2583 * const program = Effect.gen(function*() {
2584 * const values = yield* Stream.make(1).pipe(
2585 * Stream.concat(Stream.never),
2586 * Stream.timeout("1 second"),
2587 * Stream.runCollect
2588 * )
2589 * values // => [ 1 ]
2590 * })
2591 *
2592 * await Effect.runPromise(program)
2593 * ```
2594 *
2595 * @category delays & timeouts
2596 * @since 2.0.0
2597 */
2598export const timeout: {
2599 (duration: Duration.Input): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E, R>
2600 <A, E, R>(self: Stream<A, E, R>, duration: Duration.Input): Stream<A, E, R>
2601} = dual(
2602 2,
2603 <A, E, R>(self: Stream<A, E, R>, duration: Duration.Input): Stream<A, E, R> =>
2604 timeoutOrElse(self, {
2605 duration,
2606 orElse: () => empty
2607 })
2608)
2609
2610/**
2611 * Switches to a fallback stream if this stream does not emit a value within
2612 * the specified duration.
2613 *
2614 * **When to use**
2615 *
2616 * Use when a stream should continue with another stream if an upstream pull
2617 * waits longer than the allowed duration.
2618 *
2619 * **Details**
2620 *
2621 * The timeout is checked for each pull. A zero duration uses `orElse`
2622 * immediately, while an infinite duration leaves the original stream
2623 * unchanged.
2624 *
2625 * **Gotchas**
2626 *
2627 * The fallback stream is not timed after the switch.
2628 *
2629 * @see {@link timeout} for ending the stream instead of switching to a fallback stream
2630 *
2631 * @category delays & timeouts
2632 * @since 4.0.0
2633 */
2634export const timeoutOrElse: {
2635 <B, E2, R2>(options: {
2636 readonly duration: Duration.Input
2637 readonly orElse: () => Stream<B, E2, R2>
2638 }): <A, E, R>(self: Stream<A, E, R>) => Stream<A | B, E | E2, R | R2>
2639 <A, E, R, B, E2, R2>(
2640 self: Stream<A, E, R>,
2641 options: {
2642 readonly duration: Duration.Input
2643 readonly orElse: () => Stream<B, E2, R2>
2644 }
2645 ): Stream<A | B, E | E2, R | R2>
2646} = dual(
2647 2,
2648 <A, E, R, B, E2, R2>(
2649 self: Stream<A, E, R>,
2650 options: {
2651 readonly duration: Duration.Input
2652 readonly orElse: () => Stream<B, E2, R2>
2653 }
2654 ): Stream<A | B, E | E2, R | R2> => {
2655 const duration = Duration.fromInputUnsafe(options.duration)
2656 if (!Duration.isFinite(duration)) return self
2657 if (Duration.isZero(duration)) return suspend(options.orElse)
2658 const timeoutSymbol = Symbol()
2659 return catchCause(
2660 suspend(() => {
2661 const parent = Fiber.getCurrent()!
2662 const clock = parent.getRef(Clock)
2663 const durationMs = Duration.toMillis(duration)
2664 let deadline: number | undefined = undefined
2665 const latch = Latch.makeUnsafe(false)
2666 return merge(
2667 transformPull(self, (pull, _scope) =>
2668 Effect.suspend(() => {
2669 deadline = clock.currentTimeMillisUnsafe() + durationMs
2670 latch.openUnsafe()
2671 return pull
2672 }).pipe(
2673 Effect.map((arr) => {
2674 latch.closeUnsafe()
2675 deadline = undefined
2676 return arr
2677 }),
2678 Effect.succeed
2679 )),
2680 fromEffectDrain(Effect.gen(function*() {
2681 while (true) {
2682 yield* latch.await
2683 if (deadline === undefined) continue
2684 yield* Effect.sleep(deadline - clock.currentTimeMillisUnsafe())
2685 if (deadline === undefined) continue
2686 const remaining = deadline - clock.currentTimeMillisUnsafe()
2687 if (remaining > 0) continue
2688 return yield* Effect.die(timeoutSymbol)
2689 }
2690 })),
2691 { haltStrategy: "left" }
2692 )
2693 }),
2694 (cause): Stream<B, E | E2, R2> => {
2695 const isTimeout = cause.reasons.find((r) => r._tag === "Die" && r.defect === timeoutSymbol)
2696 if (isTimeout) return options.orElse()
2697 return failCause(cause as Cause.Cause<E>)
2698 }
2699 )
2700 }
2701)
2702
2703/**
2704 * Repeats each element of the stream according to the provided schedule,
2705 * including the original emission.
2706 *
2707 * **Example** (Repeating stream elements)
2708 *
2709 * ```ts import.meta.vitest
2710 * import { Effect, Schedule, Stream } from "effect"
2711 *
2712 * const program = Effect.gen(function*() {
2713 * const values = yield* Stream.make("A", "B", "C").pipe(
2714 * Stream.repeatElements(Schedule.recurs(1)),
2715 * Stream.runCollect
2716 * )
2717 * values // => [ 'A', 'A', 'B', 'B', 'C', 'C' ]
2718 * })
2719 *
2720 * await Effect.runPromise(program)
2721 * ```
2722 *
2723 * @category repetition
2724 * @since 2.0.0
2725 */
2726export const repeatElements: {
2727 <B, E2, R2>(
2728 schedule: Schedule.Schedule<B, unknown, E2, R2>
2729 ): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E | E2, R2 | R>
2730 <A, E, R, B, E2, R2>(
2731 self: Stream<A, E, R>,
2732 schedule: Schedule.Schedule<B, unknown, E2, R2>
2733 ): Stream<A, E | E2, R | R2>
2734} = dual(
2735 2,
2736 <A, E, R, B, E2, R2>(
2737 self: Stream<A, E, R>,
2738 schedule: Schedule.Schedule<B, unknown, E2, R2>
2739 ): Stream<A, E | E2, R | R2> =>
2740 fromChannel(Channel.fromTransform((upstream, scope) =>
2741 Effect.map(
2742 Channel.toTransform(Channel.flattenArray(self.channel))(upstream, scope),
2743 (pullElement) => {
2744 let pullRepeat: Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E | E2, void, R | R2> | undefined = undefined
2745
2746 const pull: Pull.Pull<
2747 Arr.NonEmptyReadonlyArray<A>,
2748 E,
2749 void,
2750 R | R2
2751 > = Effect.gen(function*() {
2752 const element = yield* pullElement
2753 const chunk = Arr.of(element)
2754 const step = yield* Schedule.toStepWithSleep(schedule)
2755 pullRepeat = step(element).pipe(
2756 Effect.as(chunk),
2757 Pull.catchDone((_) => {
2758 pullRepeat = undefined
2759 return pull
2760 })
2761 )
2762 return chunk
2763 })
2764
2765 return Effect.suspend(() => pullRepeat ?? pull)
2766 }
2767 )
2768 ))
2769)
2770
2771/**
2772 * Repeats this stream forever.
2773 *
2774 * **Example** (Repeating a stream forever)
2775 *
2776 * ```ts import.meta.vitest
2777 * import { Effect, Stream } from "effect"
2778 *
2779 * const stream = Stream.make("A", "B").pipe(
2780 * Stream.forever,
2781 * Stream.take(5)
2782 * )
2783 *
2784 * const program = Effect.gen(function*() {
2785 * const output = yield* Stream.runCollect(stream)
2786 * output // => [ 'A', 'B', 'A', 'B', 'A' ]
2787 * })
2788 *
2789 * await Effect.runPromise(program)
2790 * ```
2791 *
2792 * @category repetition
2793 * @since 2.0.0
2794 */
2795export const forever = <A, E, R>(self: Stream<A, E, R>): Stream<A, E, R> => fromChannel(Channel.forever(self.channel))
2796
2797/**
2798 * Flattens the iterables emitted by this stream into the stream's structure.
2799 *
2800 * **Example** (Flattening iterable values)
2801 *
2802 * ```ts import.meta.vitest
2803 * import { Effect, Stream } from "effect"
2804 *
2805 * const program = Effect.gen(function*() {
2806 * const stream = Stream.make([1, 2], [3, 4]).pipe(Stream.flattenIterable)
2807 * const values = yield* Stream.runCollect(stream)
2808 * values // => [ 1, 2, 3, 4 ]
2809 * })
2810 *
2811 * await Effect.runPromise(program)
2812 * ```
2813 *
2814 * @category sequencing
2815 * @since 4.0.0
2816 */
2817export const flattenIterable = <A, E, R>(self: Stream<Iterable<A>, E, R>): Stream<A, E, R> =>
2818 flatMap(self, fromIterable)
2819
2820/**
2821 * Unwraps `Take` values, emitting elements from non-empty arrays and ending or
2822 * failing when the `Exit` signals completion.
2823 *
2824 * **Example** (Flattening Take values)
2825 *
2826 * ```ts import.meta.vitest
2827 * import { Array, Effect, Exit, Stream } from "effect"
2828 *
2829 * const program = Effect.gen(function*() {
2830 * const takes = Stream.make(
2831 * Array.make(1, 2),
2832 * Array.make(3),
2833 * Exit.succeed<void>(undefined)
2834 * )
2835 *
2836 * const values = yield* Stream.flattenTake(takes).pipe(Stream.runCollect)
2837 * values // => [ 1, 2, 3 ]
2838 * })
2839 *
2840 * await Effect.runPromise(program)
2841 * ```
2842 *
2843 * @category sequencing
2844 * @since 2.0.0
2845 */
2846export const flattenTake = <A, E, E2, R>(self: Stream<Take.Take<A, E>, E2, R>): Stream<A, E | E2, R> =>
2847 self.channel.pipe(
2848 Channel.flattenArray,
2849 Channel.flattenTake,
2850 fromChannel
2851 )
2852
2853/**
2854 * Concatenates two streams, emitting all elements from the first stream
2855 * followed by all elements from the second stream.
2856 *
2857 * **Example** (Concatenating streams)
2858 *
2859 * ```ts import.meta.vitest
2860 * import { Effect, Stream } from "effect"
2861 *
2862 * const stream = Stream.concat(Stream.make(1, 2, 3), Stream.make(4, 5, 6))
2863 *
2864 * await Effect.runPromise(Effect.gen(function*() {
2865 * const values = yield* Stream.runCollect(stream)
2866 * values // => [ 1, 2, 3, 4, 5, 6 ]
2867 * }))
2868 * ```
2869 *
2870 * @category sequencing
2871 * @since 2.0.0
2872 */
2873export const concat: {
2874 <A2, E2, R2>(that: Stream<A2, E2, R2>): <A, E, R>(self: Stream<A, E, R>) => Stream<A | A2, E | E2, R | R2>
2875 <A, E, R, A2, E2, R2>(self: Stream<A, E, R>, that: Stream<A2, E2, R2>): Stream<A | A2, E | E2, R | R2>
2876} = dual(
2877 2,
2878 <A, E, R, A2, E2, R2>(self: Stream<A, E, R>, that: Stream<A2, E2, R2>): Stream<A | A2, E | E2, R | R2> => {
2879 const stream = fromChannel(
2880 Channel.flatten(Channel.fromIterator(() => concatChannels<A | A2, E | E2, R | R2>(self, that)))
2881 )
2882 concatParts.set(stream, [self, that])
2883 return stream
2884 }
2885)
2886
2887// The operands of each `concat` result, so a chain of `concat` calls can run as
2888// a single layer instead of one layer per call
2889const concatParts = new WeakMap<Stream<any, any, any>, readonly [Stream<any, any, any>, Stream<any, any, any>]>()
2890
2891// Yields the channel of each non-`concat` stream in a chain, in order
2892function* concatChannels<A, E, R>(self: Stream<A, E, R>, that: Stream<A, E, R>) {
2893 const stack = [that, self]
2894 while (stack.length > 0) {
2895 const stream = stack.pop()!
2896 const parts = concatParts.get(stream)
2897 if (parts === undefined) yield stream.channel
2898 else stack.push(parts[1], parts[0])
2899 }
2900}
2901
2902/**
2903 * Prepends the values from the provided iterable before the stream's elements.
2904 *
2905 * **Example** (Prepending values)
2906 *
2907 * ```ts import.meta.vitest
2908 * import { Effect, Stream } from "effect"
2909 *
2910 * const program = Effect.gen(function*() {
2911 * const values = yield* Stream.make(3, 4).pipe(
2912 * Stream.prepend([1, 2]),
2913 * Stream.runCollect
2914 * )
2915 *
2916 * values // => [ 1, 2, 3, 4 ]
2917 * })
2918 *
2919 * await Effect.runPromise(program)
2920 * ```
2921 *
2922 * @category sequencing
2923 * @since 2.0.0
2924 */
2925export const prepend: {
2926 <B>(values: Iterable<B>): <A, E, R>(self: Stream<A, E, R>) => Stream<B | A, E, R>
2927 <A, E, R, B>(self: Stream<A, E, R>, values: Iterable<B>): Stream<A | B, E, R>
2928} = dual(2, <A, E, R, B>(
2929 self: Stream<A, E, R>,
2930 values: Iterable<B>
2931): Stream<A | B, E, R> => concat(fromIterable(values), self))
2932
2933/**
2934 * Merges two streams, emitting elements from both as they arrive.
2935 *
2936 * **Details**
2937 *
2938 * By default, the merged stream ends when both streams end. Use
2939 * `haltStrategy` to change the termination behavior.
2940 *
2941 * **Example** (Merging stream values)
2942 *
2943 * ```ts import.meta.vitest
2944 * import { Effect, Stream } from "effect"
2945 *
2946 * const fast = Stream.make(1, 2, 3)
2947 * const slow = Stream.fromEffect(Effect.delay(Effect.succeed(4), "50 millis"))
2948 *
2949 * const program = Effect.gen(function*() {
2950 * const result = yield* Stream.runCollect(Stream.merge(fast, slow))
2951 * result // => [ 1, 2, 3, 4 ]
2952 * })
2953 *
2954 * await Effect.runPromise(program)
2955 * ```
2956 *
2957 * @category merging
2958 * @since 2.0.0
2959 */
2960export const merge: {
2961 <A2, E2, R2>(
2962 that: Stream<A2, E2, R2>,
2963 options?: {
2964 readonly haltStrategy?: HaltStrategy | undefined
2965 } | undefined
2966 ): <A, E, R>(self: Stream<A, E, R>) => Stream<A | A2, E | E2, R | R2>
2967 <A, E, R, A2, E2, R2>(
2968 self: Stream<A, E, R>,
2969 that: Stream<A2, E2, R2>,
2970 options?: {
2971 readonly haltStrategy?: HaltStrategy | undefined
2972 } | undefined
2973 ): Stream<A | A2, E | E2, R | R2>
2974} = dual(
2975 (args) => isStream(args[0]) && isStream(args[1]),
2976 <A, E, R, A2, E2, R2>(
2977 self: Stream<A, E, R>,
2978 that: Stream<A2, E2, R2>,
2979 options?: {
2980 readonly haltStrategy?: HaltStrategy | undefined
2981 } | undefined
2982 ): Stream<A | A2, E | E2, R | R2> => fromChannel(Channel.merge(toChannel(self), toChannel(that), options))
2983)
2984
2985/**
2986 * Merges this stream with a background effect, keeping the stream's elements.
2987 *
2988 * **When to use**
2989 *
2990 * Use when an effect should run concurrently for the lifetime of a stream while
2991 * only the stream's elements remain in the output.
2992 *
2993 * **Details**
2994 *
2995 * The effect runs concurrently, fails the stream if it fails, and is interrupted
2996 * when the stream completes.
2997 *
2998 * **Example** (Merging with a background effect)
2999 *
3000 * ```ts import.meta.vitest
3001 * import { Effect, Stream } from "effect"
3002 *
3003 * const events: Array<string> = []
3004 * const program = Effect.gen(function*() {
3005 * const values = yield* Stream.make(1, 2, 3).pipe(
3006 * Stream.mergeEffect(Effect.sync(() => events.push("side task"))),
3007 * Stream.runCollect
3008 * )
3009 *
3010 * values // => [1, 2, 3]
3011 * })
3012 *
3013 * await Effect.runPromise(program)
3014 * events // => ["side task"]
3015 * ```
3016 *
3017 * @category merging
3018 * @since 4.0.0
3019 */
3020export const mergeEffect: {
3021 <A2, E2, R2>(effect: Effect.Effect<A2, E2, R2>): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E2 | E, R2 | R>
3022 <A, E, R, A2, E2, R2>(self: Stream<A, E, R>, effect: Effect.Effect<A2, E2, R2>): Stream<A, E | E2, R | R2>
3023} = dual(
3024 2,
3025 <A, E, R, A2, E2, R2>(self: Stream<A, E, R>, effect: Effect.Effect<A2, E2, R2>): Stream<A, E | E2, R | R2> =>
3026 self.channel.pipe(
3027 Channel.mergeEffect(effect),
3028 fromChannel
3029 )
3030)
3031
3032/**
3033 * Merges this stream and the specified stream together, tagging values from the
3034 * left stream as `Result.succeed` and values from the right stream as `Result.fail`.
3035 *
3036 * **When to use**
3037 *
3038 * Use when values from both streams should be emitted and downstream code needs
3039 * left values wrapped as successful `Result` values and right values wrapped as
3040 * failed `Result` values.
3041 *
3042 * **Example** (Merging streams into results)
3043 *
3044 * ```ts import.meta.vitest
3045 * import { Effect, Result, Stream } from "effect"
3046 *
3047 * const left = Stream.fromEffect(Effect.succeed("left"))
3048 * const right = Stream.fromEffect(Effect.delay(Effect.succeed("right"), "10 millis"))
3049 *
3050 * const merged = left.pipe(
3051 * Stream.mergeResult(right),
3052 * Stream.map(
3053 * Result.match({
3054 * onFailure: (value) => `right:${value}`,
3055 * onSuccess: (value) => `left:${value}`
3056 * })
3057 * )
3058 * )
3059 *
3060 * const program = Effect.gen(function*() {
3061 * const result = yield* Stream.runCollect(merged)
3062 * result // => [ 'left:left', 'right:right' ]
3063 * })
3064 *
3065 * await Effect.runPromise(program)
3066 * ```
3067 *
3068 * @category merging
3069 * @since 4.0.0
3070 */
3071export const mergeResult: {
3072 <A2, E2, R2>(
3073 that: Stream<A2, E2, R2>
3074 ): <A, E, R>(self: Stream<A, E, R>) => Stream<Result.Result<A, A2>, E2 | E, R2 | R>
3075 <A, E, R, A2, E2, R2>(self: Stream<A, E, R>, that: Stream<A2, E2, R2>): Stream<Result.Result<A, A2>, E | E2, R | R2>
3076} = dual(
3077 2,
3078 <A, E, R, A2, E2, R2>(
3079 self: Stream<A, E, R>,
3080 that: Stream<A2, E2, R2>
3081 ): Stream<Result.Result<A, A2>, E | E2, R | R2> =>
3082 merge(
3083 map(self, Result.succeed),
3084 map(that, Result.fail)
3085 )
3086)
3087
3088/**
3089 * Merges two streams while emitting only the values from the left stream.
3090 *
3091 * **When to use**
3092 *
3093 * Use when the right stream is needed for its effects or failures, but downstream
3094 * consumers should only receive values from the left stream.
3095 *
3096 * **Details**
3097 *
3098 * The right stream still runs for its effects, and any failures from the right
3099 * stream are propagated. The merged stream completes when the left stream
3100 * completes, interrupting the right stream.
3101 *
3102 * **Example** (Merging streams while keeping left values)
3103 *
3104 * ```ts import.meta.vitest
3105 * import { Effect, Stream } from "effect"
3106 *
3107 * const program = Effect.gen(function*() {
3108 * const left = Stream.make(1, 2)
3109 * const right = Stream.make("a", "b")
3110 * const values = yield* left.pipe(Stream.mergeLeft(right), Stream.runCollect)
3111 * values // => [ 1, 2 ]
3112 * })
3113 *
3114 * await Effect.runPromise(program)
3115 * ```
3116 *
3117 * @category merging
3118 * @since 2.0.0
3119 */
3120export const mergeLeft: {
3121 <AR, ER, RR>(right: Stream<AR, ER, RR>): <AL, EL, RL>(left: Stream<AL, EL, RL>) => Stream<AL, ER | EL, RR | RL>
3122 <AL, EL, RL, AR, ER, RR>(left: Stream<AL, EL, RL>, right: Stream<AR, ER, RR>): Stream<AL, EL | ER, RL | RR>
3123} = dual(
3124 2,
3125 <AL, EL, RL, AR, ER, RR>(left: Stream<AL, EL, RL>, right: Stream<AR, ER, RR>): Stream<AL, EL | ER, RL | RR> =>
3126 mergeEffect(left, runDrain(right))
3127)
3128
3129/**
3130 * Merges this stream and the specified stream together, emitting only the
3131 * values from the right stream while the left stream runs for its effects.
3132 *
3133 * **When to use**
3134 *
3135 * Use when the left stream is needed for its effects or failures, but downstream
3136 * consumers should only receive values from the right stream.
3137 *
3138 * **Details**
3139 *
3140 * The merged stream ends when the right stream completes, interrupting the
3141 * left stream. Failures from the left stream still fail the merged stream.
3142 *
3143 * **Example** (Merging streams while keeping right values)
3144 *
3145 * ```ts import.meta.vitest
3146 * import { Effect, Stream } from "effect"
3147 *
3148 * const left = Stream.make("left-1", "left-2").pipe(
3149 * Stream.tap(() => Effect.sync(() => undefined))
3150 * )
3151 * const right = Stream.make(1, 2)
3152 *
3153 * const merged = Stream.mergeRight(left, right)
3154 *
3155 * const program = Effect.gen(function*() {
3156 * const result = yield* Stream.runCollect(merged)
3157 * result // => [ 1, 2 ]
3158 * })
3159 *
3160 * await Effect.runPromise(program)
3161 * ```
3162 *
3163 * @category merging
3164 * @since 2.0.0
3165 */
3166export const mergeRight: {
3167 <AR, ER, RR>(right: Stream<AR, ER, RR>): <AL, EL, RL>(left: Stream<AL, EL, RL>) => Stream<AR, ER | EL, RR | RL>
3168 <AL, EL, RL, AR, ER, RR>(left: Stream<AL, EL, RL>, right: Stream<AR, ER, RR>): Stream<AR, EL | ER, RL | RR>
3169} = dual(
3170 2,
3171 <AL, EL, RL, AR, ER, RR>(left: Stream<AL, EL, RL>, right: Stream<AR, ER, RR>): Stream<AR, EL | ER, RL | RR> =>
3172 mergeEffect(right, runDrain(left))
3173)
3174
3175/**
3176 * Merges a collection of streams, running up to the specified number concurrently.
3177 *
3178 * **When to use**
3179 *
3180 * Use to merge an iterable of already-created streams while bounding how many
3181 * inner streams may run at the same time.
3182 *
3183 * **Details**
3184 *
3185 * The `concurrency` option is required and may be a number or `"unbounded"`.
3186 * `bufferSize` controls buffering between inner streams, and outputs are
3187 * emitted as they arrive under concurrent merging.
3188 *
3189 * **Example** (Merging streams with bounded concurrency)
3190 *
3191 * ```ts import.meta.vitest
3192 * import { Effect, Stream } from "effect"
3193 *
3194 * const streams = [
3195 * Stream.fromEffect(Effect.delay(Effect.succeed("A"), "20 millis")),
3196 * Stream.fromEffect(Effect.delay(Effect.succeed("B"), "10 millis"))
3197 * ]
3198 *
3199 * const program = Effect.gen(function*() {
3200 * const values = yield* Stream.mergeAll(streams, { concurrency: 2 }).pipe(
3201 * Stream.runCollect
3202 * )
3203 * values // => [ 'B', 'A' ]
3204 * })
3205 *
3206 * await Effect.runPromise(program)
3207 * ```
3208 *
3209 * @see {@link merge} for merging exactly two streams and choosing a halt strategy
3210 * @see {@link flatten} for flattening a stream that already emits streams
3211 *
3212 * @category merging
3213 * @since 2.0.0
3214 */
3215export const mergeAll: {
3216 (
3217 options: {
3218 readonly concurrency: Concurrency
3219 readonly bufferSize?: number | undefined
3220 }
3221 ): <A, E, R>(streams: Iterable<Stream<A, E, R>>) => Stream<A, E, R>
3222 <A, E, R>(
3223 streams: Iterable<Stream<A, E, R>>,
3224 options: {
3225 readonly concurrency: Concurrency
3226 readonly bufferSize?: number | undefined
3227 }
3228 ): Stream<A, E, R>
3229} = dual(2, <A, E, R>(
3230 streams: Iterable<Stream<A, E, R>>,
3231 options: {
3232 readonly concurrency: Concurrency
3233 readonly bufferSize?: number | undefined
3234 }
3235): Stream<A, E, R> => flatten(fromIterable(streams), options))
3236
3237/**
3238 * Creates the cartesian product of two streams, running the `right` stream for
3239 * each element in the `left` stream.
3240 *
3241 * **Details**
3242 *
3243 * See also `Stream.zip` for the more common point-wise variant.
3244 *
3245 * **Example** (Computing cartesian products)
3246 *
3247 * ```ts import.meta.vitest
3248 * import { Effect, Stream } from "effect"
3249 *
3250 * const program = Effect.gen(function*() {
3251 * const left = Stream.make(1, 2)
3252 * const right = Stream.make("a", "b")
3253 * const values = yield* Stream.runCollect(Stream.cross(left, right))
3254 * values // => [ [ 1, 'a' ], [ 1, 'b' ], [ 2, 'a' ], [ 2, 'b' ] ]
3255 * })
3256 *
3257 * await Effect.runPromise(program)
3258 * ```
3259 *
3260 * @category zipping
3261 * @since 2.0.0
3262 */
3263export const cross: {
3264 <AR, ER, RR>(right: Stream<AR, ER, RR>): <AL, EL, RL>(left: Stream<AL, EL, RL>) => Stream<[AL, AR], EL | ER, RL | RR>
3265 <AL, EL, RL, AR, ER, RR>(left: Stream<AL, EL, RL>, right: Stream<AR, ER, RR>): Stream<[AL, AR], EL | ER, RL | RR>
3266} = dual(2, <AL, EL, RL, AR, ER, RR>(
3267 left: Stream<AL, EL, RL>,
3268 right: Stream<AR, ER, RR>
3269): Stream<[AL, AR], EL | ER, RL | RR> => crossWith(left, right, (l, r) => [l, r]))
3270
3271/**
3272 * Creates a cartesian product of elements from two streams using a function.
3273 *
3274 * **Details**
3275 *
3276 * The `right` stream is rerun for every element in the `left` stream.
3277 *
3278 * See also `Stream.zipWith` for the more common point-wise variant.
3279 *
3280 * **Example** (Combining cartesian products)
3281 *
3282 * ```ts import.meta.vitest
3283 * import { Effect, Stream } from "effect"
3284 *
3285 * const program = Effect.gen(function*() {
3286 * const left = Stream.make(1, 2)
3287 * const right = Stream.make("a", "b")
3288 * const combined = Stream.crossWith(left, right, (n, s) => `${n}-${s}`)
3289 * const result = yield* Stream.runCollect(combined)
3290 * result // => [ '1-a', '1-b', '2-a', '2-b' ]
3291 * })
3292 *
3293 * await Effect.runPromise(program)
3294 * ```
3295 *
3296 * @category zipping
3297 * @since 2.0.0
3298 */
3299export const crossWith: {
3300 <AR, ER, RR, AL, A>(
3301 right: Stream<AR, ER, RR>,
3302 f: (left: AL, right: AR) => A
3303 ): <EL, RL>(left: Stream<AL, EL, RL>) => Stream<A, EL | ER, RL | RR>
3304 <AL, EL, RL, AR, ER, RR, A>(
3305 left: Stream<AL, EL, RL>,
3306 right: Stream<AR, ER, RR>,
3307 f: (left: AL, right: AR) => A
3308 ): Stream<A, EL | ER, RL | RR>
3309} = dual(3, <AL, EL, RL, AR, ER, RR, A>(
3310 left: Stream<AL, EL, RL>,
3311 right: Stream<AR, ER, RR>,
3312 f: (left: AL, right: AR) => A
3313): Stream<A, EL | ER, RL | RR> => flatMap(left, (l) => map(right, (r) => f(l, r))))
3314
3315/**
3316 * Zips two streams point-wise with a combining function, ending when either stream ends.
3317 *
3318 * **Example** (Zipping streams with a function)
3319 *
3320 * ```ts import.meta.vitest
3321 * import { Effect, Stream } from "effect"
3322 *
3323 * const stream1 = Stream.make(1, 2, 3, 4, 5, 6)
3324 * const stream2 = Stream.make("a", "b", "c")
3325 *
3326 * const zipped = Stream.zipWith(stream1, stream2, (n, s) => `${n}-${s}`)
3327 *
3328 * const program = Effect.gen(function*() {
3329 * const result = yield* Stream.runCollect(zipped)
3330 * result // => [ '1-a', '2-b', '3-c' ]
3331 * })
3332 *
3333 * await Effect.runPromise(program)
3334 * ```
3335 *
3336 * @category zipping
3337 * @since 2.0.0
3338 */
3339export const zipWith: {
3340 <AR, ER, RR, AL, A>(
3341 right: Stream<AR, ER, RR>,
3342 f: (left: AL, right: AR) => A
3343 ): <EL, RL>(left: Stream<AL, EL, RL>) => Stream<A, EL | ER, RL | RR>
3344 <AL, EL, RL, AR, ER, RR, A>(
3345 left: Stream<AL, EL, RL>,
3346 right: Stream<AR, ER, RR>,
3347 f: (left: AL, right: AR) => A
3348 ): Stream<A, EL | ER, RL | RR>
3349} = dual(3, <AL, EL, RL, AR, ER, RR, A>(
3350 left: Stream<AL, EL, RL>,
3351 right: Stream<AR, ER, RR>,
3352 f: (left: AL, right: AR) => A
3353): Stream<A, EL | ER, RL | RR> => zipWithArray(left, right, zipArrays(f)))
3354
3355const zipArrays = <AL, AR, A>(
3356 f: (left: AL, right: AR) => A
3357) =>
3358(
3359 leftArr: Arr.NonEmptyReadonlyArray<AL>,
3360 rightArr: Arr.NonEmptyReadonlyArray<AR>
3361) => {
3362 const minLength = Math.min(leftArr.length, rightArr.length)
3363 const result: Arr.NonEmptyArray<A> = [] as any
3364
3365 for (let i = 0; i < minLength; i++) {
3366 result.push(f(leftArr[i], rightArr[i]))
3367 }
3368
3369 return [result, leftArr.slice(minLength), rightArr.slice(minLength)] as const
3370}
3371
3372/**
3373 * Zips two streams by applying a function to non-empty arrays of elements.
3374 *
3375 * **Details**
3376 *
3377 * The function returns output plus leftover arrays that carry into the next pull.
3378 *
3379 * **Example** (Zipping stream chunks)
3380 *
3381 * ```ts import.meta.vitest
3382 * import { Array, Effect, Stream } from "effect"
3383 *
3384 * const left = Stream.fromArrays([1, 2, 3], [4, 5])
3385 * const right = Stream.fromArrays(["a", "b"], ["c", "d", "e"])
3386 *
3387 * const zipped = Stream.zipWithArray(left, right, (leftChunk, rightChunk) => {
3388 * const minLength = Math.min(leftChunk.length, rightChunk.length)
3389 * const output = Array.makeBy(minLength, (i) => [leftChunk[i], rightChunk[i]] as const)
3390 *
3391 * return [output, leftChunk.slice(minLength), rightChunk.slice(minLength)]
3392 * })
3393 *
3394 * const program = Effect.gen(function*() {
3395 * const result = yield* Stream.runCollect(zipped)
3396 * result // => [ [ 1, 'a' ], [ 2, 'b' ], [ 3, 'c' ], [ 4, 'd' ], [ 5, 'e' ] ]
3397 * })
3398 *
3399 * await Effect.runPromise(program)
3400 * ```
3401 *
3402 * @category zipping
3403 * @since 4.0.0
3404 */
3405export const zipWithArray: {
3406 <AR, ER, RR, AL, A>(
3407 right: Stream<AR, ER, RR>,
3408 f: (
3409 left: Arr.NonEmptyReadonlyArray<AL>,
3410 right: Arr.NonEmptyReadonlyArray<AR>
3411 ) => readonly [
3412 output: Arr.NonEmptyReadonlyArray<A>,
3413 leftoverLeft: ReadonlyArray<AL>,
3414 leftoverRight: ReadonlyArray<AR>
3415 ]
3416 ): <EL, RL>(left: Stream<AL, EL, RL>) => Stream<A, EL | ER, RL | RR>
3417 <AL, EL, RL, AR, ER, RR, A>(
3418 left: Stream<AL, EL, RL>,
3419 right: Stream<AR, ER, RR>,
3420 f: (
3421 left: Arr.NonEmptyReadonlyArray<AL>,
3422 right: Arr.NonEmptyReadonlyArray<AR>
3423 ) => readonly [
3424 output: Arr.NonEmptyReadonlyArray<A>,
3425 leftoverLeft: ReadonlyArray<AL>,
3426 leftoverRight: ReadonlyArray<AR>
3427 ]
3428 ): Stream<A, EL | ER, RL | RR>
3429} = dual(3, <AL, EL, RL, AR, ER, RR, A>(
3430 left: Stream<AL, EL, RL>,
3431 right: Stream<AR, ER, RR>,
3432 f: (
3433 left: Arr.NonEmptyReadonlyArray<AL>,
3434 right: Arr.NonEmptyReadonlyArray<AR>
3435 ) => readonly [
3436 output: Arr.NonEmptyReadonlyArray<A>,
3437 leftoverLeft: ReadonlyArray<AL>,
3438 leftoverRight: ReadonlyArray<AR>
3439 ]
3440): Stream<A, EL | ER, RL | RR> =>
3441 fromChannel(Channel.fromTransformBracket(Effect.fnUntraced(function*(_, scope) {
3442 const pullLeft = yield* Channel.toPullScoped(left.channel, scope)
3443 const pullRight = yield* Channel.toPullScoped(right.channel, scope)
3444 const pullBoth = Effect.gen(function*() {
3445 const fiberLeft = yield* Effect.forkIn(pullLeft, scope)
3446 const fiberRight = yield* Effect.forkIn(pullRight, scope)
3447 return (yield* Fiber.joinAll([fiberLeft, fiberRight])) as [
3448 Arr.NonEmptyReadonlyArray<AL>,
3449 Arr.NonEmptyReadonlyArray<AR>
3450 ]
3451 })
3452
3453 type State =
3454 | { _tag: "PullBoth" }
3455 | { _tag: "PullLeft"; rightArray: Arr.NonEmptyReadonlyArray<AR> }
3456 | { _tag: "PullRight"; leftArray: Arr.NonEmptyReadonlyArray<AL> }
3457 let state: State = { _tag: "PullBoth" }
3458
3459 const pull: Effect.Effect<
3460 Arr.NonEmptyReadonlyArray<A>,
3461 EL | ER | Cause.Done,
3462 RL | RR
3463 > = Effect.gen(function*() {
3464 const [left, right] = state._tag === "PullBoth"
3465 ? yield* pullBoth
3466 : state._tag === "PullLeft"
3467 ? [yield* pullLeft, state.rightArray]
3468 : [state.leftArray, yield* pullRight]
3469 const result = f(left, right)
3470 if (Arr.isReadonlyArrayNonEmpty(result[1])) {
3471 state = { _tag: "PullRight", leftArray: result[1] }
3472 } else if (Arr.isReadonlyArrayNonEmpty(result[2])) {
3473 state = { _tag: "PullLeft", rightArray: result[2] }
3474 } else {
3475 state = { _tag: "PullBoth" }
3476 }
3477 return result[0]
3478 })
3479
3480 return pull
3481 }))))
3482
3483/**
3484 * Zips this stream with another point-wise and emits tuples of elements from
3485 * both streams. The new stream ends when either stream ends.
3486 *
3487 * **Example** (Zipping streams)
3488 *
3489 * ```ts import.meta.vitest
3490 * import { Effect, Stream } from "effect"
3491 *
3492 * const stream1 = Stream.make(1, 2, 3)
3493 * const stream2 = Stream.make("a", "b", "c")
3494 *
3495 * const zipped = Stream.zip(stream1, stream2)
3496 *
3497 * const program = Effect.gen(function*() {
3498 * const result = yield* Stream.runCollect(zipped)
3499 * result // => [ [ 1, 'a' ], [ 2, 'b' ], [ 3, 'c' ] ]
3500 * })
3501 *
3502 * await Effect.runPromise(program)
3503 * ```
3504 *
3505 * @category zipping
3506 * @since 2.0.0
3507 */
3508export const zip: {
3509 <A2, E2, R2>(that: Stream<A2, E2, R2>): <A, E, R>(self: Stream<A, E, R>) => Stream<[A, A2], E2 | E, R2 | R>
3510 <A, E, R, A2, E2, R2>(self: Stream<A, E, R>, that: Stream<A2, E2, R2>): Stream<[A, A2], E | E2, R | R2>
3511} = dual(
3512 2,
3513 <A, E, R, A2, E2, R2>(
3514 self: Stream<A, E, R>,
3515 that: Stream<A2, E2, R2>
3516 ): Stream<[A, A2], E | E2, R | R2> => zipWith(self, that, (a, a2) => [a, a2])
3517)
3518
3519/**
3520 * Zips this stream with another point-wise and keeps only the values from
3521 * the left stream.
3522 *
3523 * **Details**
3524 *
3525 * The resulting stream ends when either side ends.
3526 *
3527 * **Example** (Zipping streams while keeping left values)
3528 *
3529 * ```ts import.meta.vitest
3530 * import { Effect, Stream } from "effect"
3531 *
3532 * const stream1 = Stream.make(1, 2, 3, 4)
3533 * const stream2 = Stream.make("a", "b")
3534 *
3535 * const program = Effect.gen(function*() {
3536 * const result = yield* Stream.zipLeft(stream1, stream2).pipe(Stream.runCollect)
3537 * result // => [ 1, 2 ]
3538 * })
3539 *
3540 * await Effect.runPromise(program)
3541 * ```
3542 *
3543 * @category zipping
3544 * @since 2.0.0
3545 */
3546export const zipLeft: {
3547 <AR, ER, RR>(right: Stream<AR, ER, RR>): <AL, EL, RL>(left: Stream<AL, EL, RL>) => Stream<AL, ER | EL, RR | RL>
3548 <AL, EL, RL, AR, ER, RR>(left: Stream<AL, EL, RL>, right: Stream<AR, ER, RR>): Stream<AL, EL | ER, RL | RR>
3549} = dual(
3550 2,
3551 <AL, EL, RL, AR, ER, RR>(
3552 left: Stream<AL, EL, RL>,
3553 right: Stream<AR, ER, RR>
3554 ): Stream<AL, EL | ER, RL | RR> =>
3555 zipWithArray(left, right, (leftArr, rightArr) => {
3556 const minLength = Math.min(leftArr.length, rightArr.length)
3557 const output = leftArr.slice(0, minLength) as Arr.NonEmptyArray<AL>
3558 const leftoverLeft = leftArr.slice(minLength)
3559 const leftoverRight = rightArr.slice(minLength)
3560
3561 return [output, leftoverLeft, leftoverRight] as const
3562 })
3563)
3564
3565/**
3566 * Zips this stream with another point-wise, keeping only right values and ending when either stream ends.
3567 *
3568 * **Example** (Zipping streams while keeping right values)
3569 *
3570 * ```ts import.meta.vitest
3571 * import { Effect, Stream } from "effect"
3572 *
3573 * const stream1 = Stream.make(1, 2)
3574 * const stream2 = Stream.make("a", "b", "c", "d")
3575 *
3576 * const program = Effect.gen(function*() {
3577 * const result = yield* Stream.zipRight(stream1, stream2).pipe(Stream.runCollect)
3578 * result // => [ 'a', 'b' ]
3579 * })
3580 *
3581 * await Effect.runPromise(program)
3582 * ```
3583 *
3584 * @category zipping
3585 * @since 2.0.0
3586 */
3587export const zipRight: {
3588 <AR, ER, RR>(right: Stream<AR, ER, RR>): <AL, EL, RL>(left: Stream<AL, EL, RL>) => Stream<AR, ER | EL, RR | RL>
3589 <AL, EL, RL, AR, ER, RR>(left: Stream<AL, EL, RL>, right: Stream<AR, ER, RR>): Stream<AR, EL | ER, RL | RR>
3590} = dual(
3591 2,
3592 <AL, EL, RL, AR, ER, RR>(
3593 left: Stream<AL, EL, RL>,
3594 right: Stream<AR, ER, RR>
3595 ): Stream<AR, EL | ER, RL | RR> =>
3596 zipWithArray(left, right, (leftArr, rightArr) => {
3597 const minLength = Math.min(leftArr.length, rightArr.length)
3598 const output = rightArr.slice(0, minLength) as Arr.NonEmptyArray<AR>
3599 const leftoverLeft = leftArr.slice(minLength)
3600 const leftoverRight = rightArr.slice(minLength)
3601
3602 return [output, leftoverLeft, leftoverRight] as const
3603 })
3604)
3605
3606/**
3607 * Zips this stream with another point-wise and emits tuples of elements from
3608 * both streams, flattening the left tuple.
3609 *
3610 * **Details**
3611 *
3612 * The new stream will end when one of the sides ends.
3613 *
3614 * **Example** (Zipping and flattening tuples)
3615 *
3616 * ```ts import.meta.vitest
3617 * import { Effect, Stream } from "effect"
3618 *
3619 * const program = Effect.gen(function*() {
3620 * const stream1 = Stream.make(
3621 * [1, "a"] as const,
3622 * [2, "b"] as const,
3623 * [3, "c"] as const
3624 * )
3625 * const stream2 = Stream.make("x", "y", "z")
3626 * const result = yield* Stream.zipFlatten(stream1, stream2).pipe(Stream.runCollect)
3627 *
3628 * result // => [ [ 1, 'a', 'x' ], [ 2, 'b', 'y' ], [ 3, 'c', 'z' ] ]
3629 * })
3630 *
3631 * await Effect.runPromise(program)
3632 * ```
3633 *
3634 * @category zipping
3635 * @since 2.0.0
3636 */
3637export const zipFlatten: {
3638 <A2, E2, R2>(
3639 that: Stream<A2, E2, R2>
3640 ): <A extends ReadonlyArray<any>, E, R>(self: Stream<A, E, R>) => Stream<[...A, A2], E2 | E, R2 | R>
3641 <A extends ReadonlyArray<any>, E, R, A2, E2, R2>(
3642 self: Stream<A, E, R>,
3643 that: Stream<A2, E2, R2>
3644 ): Stream<[...A, A2], E | E2, R | R2>
3645} = dual(
3646 2,
3647 <A extends ReadonlyArray<any>, E, R, A2, E2, R2>(
3648 self: Stream<A, E, R>,
3649 that: Stream<A2, E2, R2>
3650 ): Stream<[...A, A2], E | E2, R | R2> => zipWith(self, that, (a, a2) => [...a, a2])
3651)
3652
3653/**
3654 * Zips this stream together with the index of elements.
3655 *
3656 * **Example** (Zipping elements with indices)
3657 *
3658 * ```ts import.meta.vitest
3659 * import { Effect, Stream } from "effect"
3660 *
3661 * const program = Effect.gen(function*() {
3662 * const indexed = yield* Stream.make("a", "b", "c", "d").pipe(
3663 * Stream.zipWithIndex,
3664 * Stream.runCollect
3665 * )
3666 * indexed // => [ [ 'a', 0 ], [ 'b', 1 ], [ 'c', 2 ], [ 'd', 3 ] ]
3667 * })
3668 *
3669 * await Effect.runPromise(program)
3670 * ```
3671 *
3672 * @category zipping
3673 * @since 2.0.0
3674 */
3675export const zipWithIndex = <A, E, R>(self: Stream<A, E, R>): Stream<[A, number], E, R> => map(self, (a, i) => [a, i])
3676
3677/**
3678 * Zips each element with the next element, pairing the final element with
3679 * `Option.none()`.
3680 *
3681 * **Example** (Zipping elements with next values)
3682 *
3683 * ```ts import.meta.vitest
3684 * import { Effect, Option, Stream } from "effect"
3685 *
3686 * const stream = Stream.zipWithNext(Stream.make(1, 2, 3, 4))
3687 *
3688 * await Effect.runPromise(Effect.gen(function*() {
3689 * const values = yield* Stream.runCollect(stream)
3690 * values // => [[1, Option.some(2)], [2, Option.some(3)], [3, Option.some(4)], [4, Option.none()]]
3691 * }))
3692 * ```
3693 *
3694 * @category zipping
3695 * @since 2.0.0
3696 */
3697export const zipWithNext = <A, E, R>(self: Stream<A, E, R>): Stream<[A, Option.Option<A>], E, R> =>
3698 mapAccumArray(self, Option.none<A>, (acc, arr) => {
3699 let i = 0
3700 if (acc._tag === "None") {
3701 i = 1
3702 acc = Option.some(arr[0]) as Option.Some<A>
3703 }
3704 const pairs = Arr.empty<[A, Option.Option<A>]>()
3705 for (; i < arr.length; i++) {
3706 const value = acc.value
3707 acc = Option.some(arr[i]) as Option.Some<A>
3708 pairs.push([value, acc])
3709 }
3710 return [acc, pairs]
3711 }, {
3712 onHalt(state) {
3713 return state._tag === "Some" ? [[state.value, Option.none<A>()]] : []
3714 }
3715 })
3716
3717/**
3718 * Zips each element with its previous element, starting with `None`.
3719 *
3720 * **Example** (Zipping elements with previous values)
3721 *
3722 * ```ts import.meta.vitest
3723 * import { Effect, Option, Stream } from "effect"
3724 *
3725 * const stream = Stream.zipWithPrevious(Stream.make(1, 2, 3, 4))
3726 *
3727 * const program = Effect.gen(function*() {
3728 * const result = yield* Stream.runCollect(stream)
3729 * result // => [[Option.none(), 1], [Option.some(1), 2], [Option.some(2), 3], [Option.some(3), 4]]
3730 * })
3731 *
3732 * await Effect.runPromise(program)
3733 * ```
3734 *
3735 * @category zipping
3736 * @since 2.0.0
3737 */
3738export const zipWithPrevious = <A, E, R>(self: Stream<A, E, R>): Stream<[Option.Option<A>, A], E, R> =>
3739 mapAccumArray(self, Option.none<A>, (acc, arr) => {
3740 const pairs = Arr.empty<[Option.Option<A>, A]>()
3741 for (let i = 0; i < arr.length; i++) {
3742 const value = arr[i]
3743 pairs.push([acc, value])
3744 acc = Option.some(arr[i])
3745 }
3746 return [acc, pairs]
3747 })
3748
3749/**
3750 * Zips each element with its previous and next values.
3751 *
3752 * **Example** (Zipping elements with neighbors)
3753 *
3754 * ```ts import.meta.vitest
3755 * import { Console, Effect, Option, Stream } from "effect"
3756 *
3757 * const program = Effect.gen(function*() {
3758 * const values = yield* Stream.make(1, 2, 3).pipe(
3759 * Stream.zipWithPreviousAndNext,
3760 * Stream.runCollect
3761 * )
3762 * values // => [[Option.none(), 1, Option.some(2)], [Option.some(1), 2, Option.some(3)], [Option.some(2), 3, Option.none()]]
3763 * })
3764 *
3765 * await Effect.runPromise(program)
3766 * ```
3767 *
3768 * @category zipping
3769 * @since 2.0.0
3770 */
3771export const zipWithPreviousAndNext = <A, E, R>(
3772 self: Stream<A, E, R>
3773): Stream<[Option.Option<A>, A, Option.Option<A>], E, R> =>
3774 mapAccumArray(self, () => ({
3775 prev: Option.none<A>(),
3776 current: Option.none<A>()
3777 }), (acc, arr) => {
3778 let i = 0
3779 let current: A
3780 if (acc.current._tag === "None") {
3781 i = 1
3782 current = arr[0]
3783 acc.current = Option.some(current)
3784 } else {
3785 current = acc.current.value
3786 }
3787 const pairs = Arr.empty<[Option.Option<A>, A, Option.Option<A>]>()
3788 for (; i < arr.length; i++) {
3789 const element = arr[i]
3790 acc.current = Option.some(element) as Option.Some<A>
3791 pairs.push([acc.prev, current, acc.current])
3792 acc.prev = Option.some(current)
3793 current = element
3794 }
3795 return [acc, pairs]
3796 }, {
3797 onHalt(acc) {
3798 return acc.current._tag === "Some" ? [[acc.prev, acc.current.value, Option.none<A>()]] : []
3799 }
3800 })
3801
3802/**
3803 * Zips multiple streams so that when a value is emitted by any stream, it is
3804 * combined with the latest values from the other streams to produce a result.
3805 *
3806 * **When to use**
3807 *
3808 * Use when each stream should contribute its latest value after all streams have
3809 * emitted at least once.
3810 *
3811 * **Gotchas**
3812 *
3813 * Note: tracking the latest value is done on a per-array basis. That means
3814 * that emitted elements that are not the last value in arrays will never be
3815 * used for zipping.
3816 *
3817 * **Example** (Zipping latest values from many streams)
3818 *
3819 * ```ts import.meta.vitest
3820 * import { Effect, Stream } from "effect"
3821 *
3822 * const stream = Stream.zipLatestAll(
3823 * Stream.make(1, 2, 3).pipe(Stream.rechunk(1)),
3824 * Stream.make("a", "b", "c").pipe(Stream.rechunk(1)),
3825 * Stream.make(true, false, true).pipe(Stream.rechunk(1))
3826 * )
3827 *
3828 * const program = Effect.gen(function*() {
3829 * const result = yield* Stream.runCollect(stream)
3830 * result // => [[1, "a", true], [2, "a", true], [2, "b", true], [2, "b", false], [3, "b", false], [3, "c", false], [3, "c", true]]
3831 * })
3832 *
3833 * await Effect.runPromise(program)
3834 * ```
3835 *
3836 * @category zipping
3837 * @since 3.3.0
3838 */
3839export const zipLatestAll = <T extends ReadonlyArray<Stream<any, any, any>>>(
3840 ...streams: T
3841): Stream<
3842 [T[number]] extends [never] ? never
3843 : { [K in keyof T]: T[K] extends Stream<infer A, infer _E, infer _R> ? A : never },
3844 [T[number]] extends [never] ? never : T[number] extends Stream<infer _A, infer _E, infer _R> ? _E : never,
3845 [T[number]] extends [never] ? never : T[number] extends Stream<infer _A, infer _E, infer _R> ? _R : never
3846> =>
3847 fromChannel(Channel.suspend(() => {
3848 const latest: Array<any> = []
3849 const emitted = new Set<number>()
3850 const readyLatch = Latch.makeUnsafe()
3851 return Channel.mergeAll(
3852 Channel.fromArray(
3853 streams.map((s, i) =>
3854 s.channel.pipe(
3855 Channel.flattenArray,
3856 Channel.mapEffect((a) => {
3857 latest[i] = a
3858 if (!emitted.has(i)) {
3859 emitted.add(i)
3860 if (emitted.size < streams.length) {
3861 return readyLatch.await as Effect.Effect<undefined>
3862 }
3863 return Effect.as(readyLatch.open, Arr.of(latest.slice()))
3864 }
3865 return Effect.succeed(Arr.of(latest.slice()))
3866 }),
3867 Channel.filter(isNotUndefined)
3868 )
3869 )
3870 ),
3871 {
3872 concurrency: "unbounded",
3873 bufferSize: 0
3874 }
3875 )
3876 })) as any
3877
3878/**
3879 * Combines two streams by emitting each new element with the latest value from the other stream.
3880 *
3881 * **When to use**
3882 *
3883 * Use when two streams should start emitting combined pairs after both have
3884 * produced at least one value.
3885 *
3886 * **Gotchas**
3887 *
3888 * Note: tracking the latest value is done on a per-array basis. That means
3889 * that emitted elements that are not the last value in arrays will never be
3890 * used for zipping.
3891 *
3892 * **Example** (Zipping latest values)
3893 *
3894 * ```ts import.meta.vitest
3895 * import { Effect, Stream } from "effect"
3896 *
3897 * const program = Effect.gen(function*() {
3898 * const result = yield* Stream.zipLatest(
3899 * Stream.make(1),
3900 * Stream.make("a")
3901 * ).pipe(Stream.runCollect)
3902 *
3903 * result // => [ [ 1, 'a' ] ]
3904 * })
3905 * await Effect.runPromise(program)
3906 * ```
3907 *
3908 * @category zipping
3909 * @since 2.0.0
3910 */
3911export const zipLatest: {
3912 <AR, ER, RR>(
3913 right: Stream<AR, ER, RR>
3914 ): <AL, EL, RL>(left: Stream<AL, EL, RL>) => Stream<[AL, AR], EL | ER, RL | RR>
3915 <AL, EL, RL, AR, ER, RR>(
3916 left: Stream<AL, EL, RL>,
3917 right: Stream<AR, ER, RR>
3918 ): Stream<[AL, AR], EL | ER, RL | RR>
3919} = dual(
3920 2,
3921 <AL, EL, RL, AR, ER, RR>(
3922 left: Stream<AL, EL, RL>,
3923 right: Stream<AR, ER, RR>
3924 ): Stream<[AL, AR], EL | ER, RL | RR> => zipLatestAll(left, right)
3925)
3926
3927/**
3928 * Combines the latest values from both streams whenever either emits, using
3929 * the provided function.
3930 *
3931 * **When to use**
3932 *
3933 * Use when two streams should start emitting custom combined values after both
3934 * have produced at least one value.
3935 *
3936 * **Gotchas**
3937 *
3938 * Note: tracking the latest value is done on a per-array basis. That means
3939 * that emitted elements that are not the last value in arrays will never be
3940 * used for zipping.
3941 *
3942 * **Example** (Zipping latest values with a function)
3943 *
3944 * ```ts import.meta.vitest
3945 * import { Effect, Stream } from "effect"
3946 *
3947 * await Effect.runPromise(Effect.gen(function*() {
3948 * const result = yield* Stream.make(1, 2, 3).pipe(
3949 * Stream.rechunk(1),
3950 * Stream.zipLatestWith(
3951 * Stream.make(10, 20).pipe(Stream.rechunk(1)),
3952 * (n, m) => n + m
3953 * ),
3954 * Stream.runCollect
3955 * )
3956 *
3957 * result // => [ 11, 12, 22, 23 ]
3958 * }))
3959 * ```
3960 *
3961 * @category zipping
3962 * @since 2.0.0
3963 */
3964export const zipLatestWith: {
3965 <AR, ER, RR, AL, A>(
3966 right: Stream<AR, ER, RR>,
3967 f: (left: AL, right: AR) => A
3968 ): <EL, RL>(left: Stream<AL, EL, RL>) => Stream<A, EL | ER, RL | RR>
3969 <AL, EL, RL, AR, ER, RR, A>(
3970 left: Stream<AL, EL, RL>,
3971 right: Stream<AR, ER, RR>,
3972 f: (left: AL, right: AR) => A
3973 ): Stream<A, EL | ER, RL | RR>
3974} = dual(
3975 3,
3976 <AL, EL, RL, AR, ER, RR, A>(
3977 left: Stream<AL, EL, RL>,
3978 right: Stream<AR, ER, RR>,
3979 f: (left: AL, right: AR) => A
3980 ): Stream<A, EL | ER, RL | RR> => map(zipLatestAll(left, right), ([a, a2]) => f(a, a2))
3981)
3982
3983/**
3984 * Runs all streams concurrently until one stream emits its first value, then
3985 * mirrors that winning stream and interrupts the rest.
3986 *
3987 * **Details**
3988 *
3989 * Failures or completion from losing streams before a winner is chosen are
3990 * ignored unless every stream fails or completes before emitting. After a
3991 * winner is chosen, that stream's later failures are propagated.
3992 *
3993 * **Example** (Racing multiple streams)
3994 *
3995 * ```ts import.meta.vitest
3996 * import { Effect, Schedule, Stream } from "effect"
3997 *
3998 * const program = Effect.gen(function*() {
3999 * const result = yield* Stream.raceAll(
4000 * Stream.empty,
4001 * Stream.make(0, 1, 2)
4002 * ).pipe(Stream.runCollect)
4003 * result // => [ 0, 1, 2 ]
4004 * })
4005 *
4006 * await Effect.runPromise(program)
4007 * ```
4008 *
4009 * @category racing
4010 * @since 3.5.0
4011 */
4012export const raceAll = <S extends ReadonlyArray<Stream<any, any, any>>>(
4013 ...streams: S
4014): Stream<Success<S[number]>, Error<S[number]>, Services<S[number]>> =>
4015 fromChannel(Channel.fromTransform((_, scope) =>
4016 Effect.sync(() => {
4017 let winner:
4018 | Pull.Pull<Arr.NonEmptyReadonlyArray<Success<S[number]>>, Error<S[number]>, void, Services<S[number]>>
4019 | undefined
4020 const race = Effect.raceAll(streams.map((stream) => {
4021 const childScope = Scope.forkUnsafe(scope)
4022 return Channel.toPullScoped(stream.channel, childScope).pipe(
4023 Effect.flatMap((pull) => Effect.zip(Effect.succeed(pull), pull)),
4024 Effect.onExit((exit) => {
4025 if (exit._tag === "Success") {
4026 if (winner) {
4027 return Scope.close(childScope, exit)
4028 }
4029 winner = exit.value[0]
4030 return Effect.void
4031 }
4032 return Scope.close(childScope, exit)
4033 }),
4034 Effect.map(([, chunk]) => chunk)
4035 )
4036 }))
4037 return Effect.suspend(() => winner ?? race)
4038 })
4039 ))
4040
4041/**
4042 * Runs both streams concurrently until one stream emits its first value, then
4043 * mirrors that winning stream and interrupts the other.
4044 *
4045 * **Details**
4046 *
4047 * A failure or completion from one side before the other side emits does not
4048 * win the race unless both sides fail or complete before emitting. After a
4049 * winner is chosen, that stream's later failures are propagated.
4050 *
4051 * **Example** (Racing two streams)
4052 *
4053 * ```ts import.meta.vitest
4054 * import { Effect, Stream } from "effect"
4055 *
4056 * const stream = Stream.race(
4057 * Stream.empty,
4058 * Stream.make(0, 1, 2)
4059 * )
4060 *
4061 * const program = Effect.gen(function*() {
4062 * const result = yield* Stream.runCollect(stream)
4063 * result // => [ 0, 1, 2 ]
4064 * })
4065 *
4066 * await Effect.runPromise(program)
4067 * ```
4068 *
4069 * @category racing
4070 * @since 3.7.0
4071 */
4072export const race: {
4073 <AR, ER, RR>(
4074 right: Stream<AR, ER, RR>
4075 ): <AL, EL, RL>(left: Stream<AL, EL, RL>) => Stream<AL | AR, EL | ER, RL | RR>
4076 <AL, EL, RL, AR, ER, RR>(
4077 left: Stream<AL, EL, RL>,
4078 right: Stream<AR, ER, RR>
4079 ): Stream<AL | AR, EL | ER, RL | RR>
4080} = dual(2, <AL, EL, RL, AR, ER, RR>(
4081 left: Stream<AL, EL, RL>,
4082 right: Stream<AR, ER, RR>
4083): Stream<AL | AR, EL | ER, RL | RR> => raceAll(left, right))
4084
4085/**
4086 * Filters a stream to the elements that satisfy a predicate.
4087 *
4088 * **Example** (Filtering stream values)
4089 *
4090 * ```ts import.meta.vitest
4091 * import { Effect, Stream } from "effect"
4092 *
4093 * const program = Effect.gen(function*() {
4094 * const stream = Stream.make(1, 2, 3, 4).pipe(
4095 * Stream.filter((n) => n % 2 === 0)
4096 * )
4097 * const values = yield* Stream.runCollect(stream)
4098 * values // => [ 2, 4 ]
4099 * })
4100 *
4101 * await Effect.runPromise(program)
4102 * ```
4103 *
4104 * @category filtering
4105 * @since 2.0.0
4106 */
4107export const filter: {
4108 <A, B extends A>(refinement: Refinement<NoInfer<A>, B>): <E, R>(self: Stream<A, E, R>) => Stream<B, E, R>
4109 <A>(predicate: Predicate<NoInfer<A>>): <E, R>(self: Stream<A, E, R>) => Stream<A, E, R>
4110 <A, E, R, B extends A>(self: Stream<A, E, R>, refinement: Refinement<A, B>): Stream<B, E, R>
4111 <A, E, R>(
4112 self: Stream<A, E, R>,
4113 predicate: Predicate<A>
4114 ): Stream<A, E, R>
4115} = dual(
4116 2,
4117 <A, E, R>(
4118 self: Stream<A, E, R>,
4119 predicate: Predicate<A>
4120 ): Stream<A, E, R> => fromChannel(Channel.filterArray(toChannel(self), predicate))
4121)
4122
4123/**
4124 * Filters and maps stream elements in one pass using a `Filter`.
4125 *
4126 * **When to use**
4127 *
4128 * Use to keep only stream elements accepted by a `Filter` and emit each filter
4129 * success value.
4130 *
4131 * **Details**
4132 *
4133 * `Result.succeed` values are emitted and `Result.fail` values are skipped.
4134 *
4135 * @see {@link filter} for keeping original elements with a boolean predicate or refinement
4136 * @see {@link filterMapEffect} for an effectful `Filter`
4137 * @see {@link partition} for consuming both filter success and failure values
4138 *
4139 * @category filtering
4140 * @since 2.0.0
4141 */
4142export const filterMap: {
4143 <A, B, X>(
4144 filter: Filter.Filter<NoInfer<A>, B, X>
4145 ): <E, R>(self: Stream<A, E, R>) => Stream<B, E, R>
4146 <A, E, R, B, X>(
4147 self: Stream<A, E, R>,
4148 filter: Filter.Filter<A, B, X>
4149 ): Stream<B, E, R>
4150} = dual(
4151 2,
4152 <A, E, R, B, X>(
4153 self: Stream<A, E, R>,
4154 filter: Filter.Filter<A, B, X>
4155 ): Stream<B, E, R> => fromChannel(Channel.filterMapArray(toChannel(self), filter))
4156)
4157
4158/**
4159 * Filters elements in a single pass effectfully.
4160 *
4161 * **Example** (Effectfully filtering stream values)
4162 *
4163 * ```ts import.meta.vitest
4164 * import { Effect, Stream } from "effect"
4165 *
4166 * const stream = Stream.make(1, 2, 3, 4).pipe(Stream.filterEffect((n) => Effect.succeed(n > 2)))
4167 *
4168 * const program = Effect.gen(function*() {
4169 * const result = yield* Stream.runCollect(stream)
4170 * result // => [ 3, 4 ]
4171 * })
4172 *
4173 * await Effect.runPromise(program)
4174 * ```
4175 *
4176 * @category filtering
4177 * @since 2.0.0
4178 */
4179export const filterEffect: {
4180 <A, EX, RX>(
4181 predicate: (a: NoInfer<A>, i: number) => Effect.Effect<boolean, EX, RX>
4182 ): <E, R>(self: Stream<A, E, R>) => Stream<A, E | EX, R | RX>
4183 <A, E, R, EX, RX>(
4184 self: Stream<A, E, R>,
4185 predicate: (a: NoInfer<A>, i: number) => Effect.Effect<boolean, EX, RX>
4186 ): Stream<A, E | EX, R | RX>
4187} = dual(
4188 2,
4189 <A, E, R, EX, RX>(
4190 self: Stream<A, E, R>,
4191 predicate: (a: NoInfer<A>, i: number) => Effect.Effect<boolean, EX, RX>
4192 ): Stream<A, E | EX, R | RX> => fromChannel(Channel.filterArrayEffect(toChannel(self), predicate))
4193)
4194
4195/**
4196 * Filters and maps elements in one pass effectfully using a `FilterEffect`.
4197 *
4198 * **When to use**
4199 *
4200 * Use to apply effectful logic that can reject stream elements or emit
4201 * transformed values before they continue downstream.
4202 *
4203 * **Details**
4204 *
4205 * `Result.succeed` values are emitted, `Result.fail` values are skipped, and
4206 * effect failures fail the stream.
4207 *
4208 * @see {@link filterMap} for the synchronous `Filter` variant
4209 * @see {@link filterEffect} for effectfully keeping original elements
4210 * @see {@link mapEffect} for effectfully transforming every element
4211 *
4212 * @category filtering
4213 * @since 2.0.0
4214 */
4215export const filterMapEffect: {
4216 <A, B, X, EX, RX>(
4217 filter: Filter.FilterEffect<NoInfer<A>, B, X, EX, RX>
4218 ): <E, R>(self: Stream<A, E, R>) => Stream<B, E | EX, R | RX>
4219 <A, E, R, B, X, EX, RX>(
4220 self: Stream<A, E, R>,
4221 filter: Filter.FilterEffect<A, B, X, EX, RX>
4222 ): Stream<B, E | EX, R | RX>
4223} = dual(
4224 2,
4225 <A, E, R, B, X, EX, RX>(
4226 self: Stream<A, E, R>,
4227 filter: Filter.FilterEffect<A, B, X, EX, RX>
4228 ): Stream<B, E | EX, R | RX> => fromChannel(Channel.filterMapArrayEffect(toChannel(self), filter))
4229)
4230
4231/**
4232 * Partitions a stream using a `Filter` and exposes passing and failing values
4233 * as scoped queues.
4234 *
4235 * **Details**
4236 *
4237 * The queues are backed by a fiber in the current scope and should be consumed
4238 * while that scope remains open. Each queue fails with the stream error or
4239 * `Cause.Done` when the source ends.
4240 *
4241 * **Example** (Partitioning a stream into queues)
4242 *
4243 * ```ts import.meta.vitest
4244 * import { Effect, Result, Stream } from "effect"
4245 *
4246 * const program = Effect.gen(function*() {
4247 * const [passes, fails] = yield* Stream.make(1, 2, 3, 4).pipe(
4248 * Stream.partitionQueue((n) => n % 2 === 0 ? Result.succeed(n) : Result.fail(n))
4249 * )
4250 *
4251 * const passValues = yield* Stream.fromQueue(passes).pipe(Stream.runCollect)
4252 * const failValues = yield* Stream.fromQueue(fails).pipe(Stream.runCollect)
4253 *
4254 * passValues // => [ 2, 4 ]
4255 * failValues // => [ 1, 3 ]
4256 * })
4257 *
4258 * await Effect.runPromise(Effect.scoped(program))
4259 * ```
4260 *
4261 * @category filtering
4262 * @since 4.0.0
4263 */
4264export const partitionQueue: {
4265 <A, Pass, Fail>(filter: Filter.Filter<NoInfer<A>, Pass, Fail>, options?: {
4266 readonly capacity?: number | "unbounded" | undefined
4267 }): <E, R>(self: Stream<A, E, R>) => Effect.Effect<
4268 [
4269 passes: Queue.Dequeue<Pass, E | Cause.Done>,
4270 fails: Queue.Dequeue<Fail, E | Cause.Done>
4271 ],
4272 never,
4273 R | Scope.Scope
4274 >
4275 <A, E, R, Pass, Fail>(
4276 self: Stream<A, E, R>,
4277 filter: Filter.Filter<NoInfer<A>, Pass, Fail>,
4278 options?: {
4279 readonly capacity?: number | "unbounded" | undefined
4280 }
4281 ): Effect.Effect<
4282 [
4283 passes: Queue.Dequeue<Pass, E | Cause.Done>,
4284 fails: Queue.Dequeue<Fail, E | Cause.Done>
4285 ],
4286 never,
4287 R | Scope.Scope
4288 >
4289} = dual(
4290 (args) => isStream(args[0]),
4291 Effect.fnUntraced(
4292 function*<A, E, R, Pass, Fail>(
4293 self: Stream<A, E, R>,
4294 filter: Filter.Filter<NoInfer<A>, Pass, Fail>,
4295 options?: {
4296 readonly capacity?: number | "unbounded" | undefined
4297 }
4298 ): Effect.fn.Return<
4299 [
4300 passes: Queue.Dequeue<Pass, E | Cause.Done>,
4301 fails: Queue.Dequeue<Fail, E | Cause.Done>
4302 ],
4303 never,
4304 R | Scope.Scope
4305 > {
4306 const scope = yield* Effect.scope
4307 const pull = yield* Channel.toPullScoped(self.channel, scope)
4308 const capacity = options?.capacity === "unbounded" ? undefined : options?.capacity ?? DefaultChunkSize
4309 const passes = yield* Queue.make<Pass, E | Cause.Done>({ capacity })
4310 const fails = yield* Queue.make<Fail, E | Cause.Done>({ capacity })
4311
4312 yield* Effect.gen(function*() {
4313 while (true) {
4314 const chunk = yield* pull
4315 const excluded: Array<Fail> = []
4316 const satisfying: Array<Pass> = []
4317 for (let i = 0; i < chunk.length; i++) {
4318 const result = filter(chunk[i] as NoInfer<A>)
4319 if (Result.isFailure(result)) {
4320 excluded.push(result.failure)
4321 } else {
4322 satisfying.push(result.success)
4323 }
4324 }
4325 let passFiber: Fiber.Fiber<any> | undefined = undefined
4326 if (satisfying.length > 0) {
4327 const leftover = Queue.offerAllUnsafe(passes, satisfying)
4328 if (leftover.length > 0) {
4329 passFiber = yield* Effect.forkChild(Queue.offerAll(passes, leftover))
4330 }
4331 }
4332 if (excluded.length > 0) {
4333 const leftover = Queue.offerAllUnsafe(fails, excluded)
4334 if (leftover.length > 0) {
4335 yield* Queue.offerAll(fails, leftover)
4336 }
4337 }
4338 if (passFiber) yield* Fiber.join(passFiber)
4339 }
4340 }).pipe(
4341 Effect.onError((cause) => {
4342 Queue.failCauseUnsafe(passes, cause)
4343 Queue.failCauseUnsafe(fails, cause)
4344 return Effect.void
4345 }),
4346 Effect.forkIn(scope)
4347 )
4348
4349 return [passes, fails]
4350 }
4351 )
4352)
4353
4354/**
4355 * Splits a stream with an effectful `Filter`, returning scoped streams for
4356 * filter successes and failures.
4357 *
4358 * **When to use**
4359 *
4360 * Use when you need to classify each stream element with an effectful `Filter`
4361 * and consume both passing and failing mapped values as streams.
4362 *
4363 * **Details**
4364 *
4365 * The returned streams are backed by queues in the current scope and should be
4366 * consumed while that scope remains open. The first stream emits success values
4367 * from the filter, and the second emits failure values.
4368 *
4369 * @see {@link partition} for the pure `Filter` variant
4370 * @see {@link partitionQueue} for the lower-level queue result
4371 * @see {@link filterMapEffect} for effectful filtering that discards failed filter results
4372 *
4373 * @category filtering
4374 * @since 4.0.0
4375 */
4376export const partitionEffect: {
4377 <A, Pass, Fail, EX, RX>(filter: Filter.FilterEffect<NoInfer<A>, Pass, Fail, EX, RX>, options?: {
4378 readonly capacity?: number | "unbounded" | undefined
4379 readonly concurrency?: Concurrency | undefined
4380 }): <E, R>(self: Stream<A, E, R>) => Effect.Effect<
4381 [
4382 passes: Stream<Pass, E | EX>,
4383 fails: Stream<Fail, E | EX>
4384 ],
4385 never,
4386 R | RX | Scope.Scope
4387 >
4388 <A, E, R, Pass, Fail, EX, RX>(
4389 self: Stream<A, E, R>,
4390 filter: Filter.FilterEffect<NoInfer<A>, Pass, Fail, EX, RX>,
4391 options?: {
4392 readonly capacity?: number | "unbounded" | undefined
4393 readonly concurrency?: Concurrency | undefined
4394 }
4395 ): Effect.Effect<
4396 [
4397 passes: Stream<Pass, E | EX>,
4398 fails: Stream<Fail, E | EX>
4399 ],
4400 never,
4401 R | RX | Scope.Scope
4402 >
4403} = dual(
4404 (args) => isStream(args[0]),
4405 <A, E, R, Pass, Fail, EX, RX>(
4406 self: Stream<A, E, R>,
4407 filter: Filter.FilterEffect<NoInfer<A>, Pass, Fail, EX, RX>,
4408 options?: {
4409 readonly capacity?: number | "unbounded" | undefined
4410 readonly concurrency?: Concurrency | undefined
4411 }
4412 ): Effect.Effect<
4413 [
4414 passes: Stream<Pass, E | EX>,
4415 fails: Stream<Fail, E | EX>
4416 ],
4417 never,
4418 R | RX | Scope.Scope
4419 > =>
4420 Effect.map(
4421 partitionQueue<Result.Result<Pass, Fail>, E | EX, R | RX, Pass, Fail>(
4422 mapEffect(self, (a) => filter(a as NoInfer<A>), options),
4423 (result) => result,
4424 options
4425 ),
4426 ([passes, fails]) => [fromQueue(passes), fromQueue(fails)] as const
4427 )
4428)
4429
4430/**
4431 * Splits a stream into scoped passing and failing substreams using a
4432 * `Filter`.
4433 *
4434 * **Details**
4435 *
4436 * The returned streams are backed by queues in the current scope and should be
4437 * consumed while that scope remains open. The faster stream may advance up to
4438 * `capacity` elements ahead of the slower one. The first stream emits the
4439 * filter successes and the second emits the filter failures. The default
4440 * capacity is 16.
4441 *
4442 * **Example** (Partitioning a stream)
4443 *
4444 * ```ts import.meta.vitest
4445 * import { Effect, Result, Stream } from "effect"
4446 *
4447 * const program = Effect.gen(function*() {
4448 * const [passes, fails] = yield* Stream.partition(
4449 * Stream.make(1, 2, 3, 4),
4450 * (n) => n % 2 === 0 ? Result.succeed(n) : Result.fail(n)
4451 * )
4452 * const evens = yield* Stream.runCollect(passes)
4453 * const odds = yield* Stream.runCollect(fails)
4454 * evens // => [ 2, 4 ]
4455 * odds // => [ 1, 3 ]
4456 * })
4457 * await Effect.runPromise(Effect.scoped(program))
4458 * ```
4459 *
4460 * @category filtering
4461 * @since 2.0.0
4462 */
4463export const partition: {
4464 <A, Pass, Fail>(
4465 filter: Filter.Filter<NoInfer<A>, Pass, Fail>,
4466 options?: {
4467 readonly capacity?: number | "unbounded" | undefined
4468 }
4469 ): <E, R>(
4470 self: Stream<A, E, R>
4471 ) => Effect.Effect<
4472 [passes: Stream<Pass, E>, fails: Stream<Fail, E>],
4473 never,
4474 R | Scope.Scope
4475 >
4476 <A, E, R, Pass, Fail>(
4477 self: Stream<A, E, R>,
4478 filter: Filter.Filter<NoInfer<A>, Pass, Fail>,
4479 options?: {
4480 readonly capacity?: number | "unbounded" | undefined
4481 }
4482 ): Effect.Effect<
4483 [passes: Stream<Pass, E>, fails: Stream<Fail, E>],
4484 never,
4485 R | Scope.Scope
4486 >
4487} = dual(
4488 (args) => isStream(args[0]),
4489 <A, E, R, Pass, Fail>(
4490 self: Stream<A, E, R>,
4491 filter: Filter.Filter<NoInfer<A>, Pass, Fail>,
4492 options?: {
4493 readonly capacity?: number | "unbounded" | undefined
4494 }
4495 ): Effect.Effect<
4496 [passes: Stream<Pass, E>, fails: Stream<Fail, E>],
4497 never,
4498 R | Scope.Scope
4499 > =>
4500 Effect.map(
4501 partitionQueue(self, filter, { capacity: options?.capacity ?? 16 }),
4502 ([passes, fails]) => [fromQueue(passes), fromQueue(fails)] as const
4503 )
4504)
4505
4506/**
4507 * Returns the specified stream if the given condition is satisfied, otherwise
4508 * returns an empty stream.
4509 *
4510 * **Example** (Conditionally keeping a stream)
4511 *
4512 * ```ts import.meta.vitest
4513 * import { Effect, Stream } from "effect"
4514 *
4515 * const program = Effect.gen(function*() {
4516 * const result = yield* Stream.runCollect(
4517 * Stream.when(Stream.make(1, 2, 3), Effect.succeed(false))
4518 * )
4519 * result // => []
4520 * })
4521 *
4522 * await Effect.runPromise(program)
4523 * ```
4524 *
4525 * @category filtering
4526 * @since 2.0.0
4527 */
4528export const when: {
4529 <EX = never, RX = never>(
4530 test: Effect.Effect<boolean, EX, RX>
4531 ): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E | EX, R | RX>
4532 <A, E, R, EX = never, RX = never>(
4533 self: Stream<A, E, R>,
4534 test: Effect.Effect<boolean, EX, RX>
4535 ): Stream<A, E | EX, R | RX>
4536} = dual(2, <A, E, R, EX = never, RX = never>(
4537 self: Stream<A, E, R>,
4538 test: Effect.Effect<boolean, EX, RX>
4539): Stream<A, E | EX, R | RX> =>
4540 test.pipe(
4541 Effect.map((pass) => pass ? self : empty),
4542 unwrap
4543 ))
4544
4545/**
4546 * Runs a sink to peel off enough elements to produce a value and returns that
4547 * value with the remaining stream in a scope.
4548 *
4549 * **Details**
4550 *
4551 * The returned stream is only valid within the scope.
4552 *
4553 * **Example** (Peeling a stream with a sink)
4554 *
4555 * ```ts import.meta.vitest
4556 * import { Effect, Sink, Stream } from "effect"
4557 *
4558 * const stream = Stream.fromArrays([1, 2, 3], [4, 5, 6])
4559 * const sink = Sink.take<number>(3)
4560 *
4561 * const program = Effect.scoped(
4562 * Effect.gen(function*() {
4563 * const [peeled, rest] = yield* Stream.peel(stream, sink)
4564 * const remaining = yield* Stream.runCollect(rest)
4565 * const result = [peeled, remaining] // => [[1, 2, 3], [4, 5, 6]]
4566 * })
4567 * )
4568 *
4569 * await Effect.runPromise(program)
4570 * ```
4571 *
4572 * @category destructors
4573 * @since 2.0.0
4574 */
4575export const peel: {
4576 <A2, A, E2, R2>(
4577 sink: Sink.Sink<A2, A, A, E2, R2>
4578 ): <E, R>(self: Stream<A, E, R>) => Effect.Effect<[A2, Stream<A, E, never>], E2 | E, Scope.Scope | R2 | R>
4579 <A, E, R, A2, E2, R2>(
4580 self: Stream<A, E, R>,
4581 sink: Sink.Sink<A2, A, A, E2, R2>
4582 ): Effect.Effect<[A2, Stream<A, E, never>], E | E2, Scope.Scope | R | R2>
4583} = dual(
4584 2,
4585 Effect.fnUntraced(function*<A, E, R, A2, E2, R2>(
4586 self: Stream<A, E, R>,
4587 sink: Sink.Sink<A2, A, A, E2, R2>
4588 ): Effect.fn.Return<[A2, Stream<A, E, never>], E | E2, Scope.Scope | R | R2> {
4589 let cause: Cause.Cause<E | Cause.Done<void>> | undefined = undefined
4590 const originalPull = yield* Channel.toPull(self.channel)
4591 const pull: Pull.Pull<
4592 Arr.NonEmptyReadonlyArray<A>,
4593 E
4594 > = Effect.catchCause(originalPull, (cause_) => {
4595 cause = cause_
4596 return Effect.failCause(cause_)
4597 })
4598
4599 let stream = fromPull(Effect.succeed(pull)) as Stream<A, E>
4600 const leftover = yield* run(stream, sink)
4601 if (cause) return [leftover, empty]
4602
4603 stream = fromPull(Effect.succeed(originalPull))
4604 return [leftover, stream]
4605 })
4606)
4607
4608/**
4609 * Buffers up to `capacity` elements so a faster producer can progress
4610 * independently of a slower consumer.
4611 *
4612 * **Details**
4613 *
4614 * Finite buffers use the configured queue strategy: `"suspend"` applies
4615 * backpressure, while `"dropping"` and `"sliding"` may discard elements when
4616 * the buffer is full. This combinator destroys chunking; use `Stream.rechunk`
4617 * afterward if you need fixed chunk sizes.
4618 *
4619 * **Example** (Buffering stream elements)
4620 *
4621 * ```ts import.meta.vitest
4622 * import { Effect, Stream } from "effect"
4623 *
4624 * const program = Effect.gen(function*() {
4625 * const values = yield* Stream.make(1, 2, 3).pipe(
4626 * Stream.buffer({ capacity: 1 }),
4627 * Stream.runCollect
4628 * )
4629 * values // => [ 1, 2, 3 ]
4630 * })
4631 *
4632 * await Effect.runPromise(program)
4633 * ```
4634 *
4635 * @category buffering
4636 * @since 2.0.0
4637 */
4638export const buffer: {
4639 (
4640 options: { readonly capacity: "unbounded" } | {
4641 readonly capacity: number
4642 readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
4643 }
4644 ): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E, R>
4645 <A, E, R>(
4646 self: Stream<A, E, R>,
4647 options: { readonly capacity: "unbounded" } | {
4648 readonly capacity: number
4649 readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
4650 }
4651 ): Stream<A, E, R>
4652} = dual(2, <A, E, R>(
4653 self: Stream<A, E, R>,
4654 options: { readonly capacity: "unbounded" } | {
4655 readonly capacity: number
4656 readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
4657 }
4658): Stream<A, E, R> => fromChannel(Channel.bufferArray(self.channel, options)))
4659
4660/**
4661 * Allows a faster producer to progress independently of a slower consumer by
4662 * buffering up to `capacity` chunks in a queue.
4663 *
4664 * **Details**
4665 *
4666 * Finite buffers use the configured queue strategy: `"suspend"` applies
4667 * backpressure, while `"dropping"` and `"sliding"` may discard chunks when the
4668 * buffer is full. This combinator preserves chunking and is best with
4669 * power-of-2 capacities.
4670 *
4671 * **Example** (Buffering stream chunks)
4672 *
4673 * ```ts import.meta.vitest
4674 * import { Effect, Stream } from "effect"
4675 *
4676 * const program = Effect.gen(function*() {
4677 * const result = yield* Stream.fromArrays([1, 2], [3, 4]).pipe(
4678 * Stream.bufferArray({ capacity: 2 }),
4679 * Stream.runCollect
4680 * )
4681 * result // => [ 1, 2, 3, 4 ]
4682 * })
4683 *
4684 * await Effect.runPromise(program)
4685 * ```
4686 *
4687 * @category buffering
4688 * @since 4.0.0
4689 */
4690export const bufferArray: {
4691 (
4692 options: { readonly capacity: "unbounded" } | {
4693 readonly capacity: number
4694 readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
4695 }
4696 ): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E, R>
4697 <A, E, R>(
4698 self: Stream<A, E, R>,
4699 options: { readonly capacity: "unbounded" } | {
4700 readonly capacity: number
4701 readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
4702 }
4703 ): Stream<A, E, R>
4704} = dual(2, <A, E, R>(
4705 self: Stream<A, E, R>,
4706 options: { readonly capacity: "unbounded" } | {
4707 readonly capacity: number
4708 readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
4709 }
4710): Stream<A, E, R> => fromChannel(Channel.buffer(self.channel, options)))
4711
4712/**
4713 * Switches over to the stream produced by the provided function in case this
4714 * one fails. Allows recovery from all causes of failure, including
4715 * interruption if the stream is uninterruptible.
4716 *
4717 * **Example** (Catching stream causes)
4718 *
4719 * ```ts import.meta.vitest
4720 * import { Effect, Stream } from "effect"
4721 *
4722 * const stream = Stream.make(1, 2).pipe(
4723 * Stream.concat(Stream.fail("Oops!")),
4724 * Stream.concat(Stream.make(3, 4))
4725 * )
4726 *
4727 * const recovered = stream.pipe(
4728 * Stream.catchCause(() => Stream.make(999))
4729 * )
4730 *
4731 * const program = Effect.gen(function*() {
4732 * const values = yield* Stream.runCollect(recovered)
4733 * values // => [ 1, 2, 999 ]
4734 * })
4735 *
4736 * await Effect.runPromise(program)
4737 * ```
4738 *
4739 * @category error handling
4740 * @since 2.0.0
4741 */
4742export const catchCause: {
4743 <E, A2, E2, R2>(
4744 f: (cause: Cause.Cause<E>) => Stream<A2, E2, R2>
4745 ): <A, R>(self: Stream<A, E, R>) => Stream<A | A2, E2, R2 | R>
4746 <A, E, R, A2, E2, R2>(
4747 self: Stream<A, E, R>,
4748 f: (cause: Cause.Cause<E>) => Stream<A2, E2, R2>
4749 ): Stream<A | A2, E2, R | R2>
4750} = dual(2, <A, E, R, A2, E2, R2>(
4751 self: Stream<A, E, R>,
4752 f: (cause: Cause.Cause<E>) => Stream<A2, E2, R2>
4753): Stream<A | A2, E2, R | R2> =>
4754 self.channel.pipe(
4755 Channel.catchCause((cause) => f(cause).channel),
4756 fromChannel
4757 ))
4758
4759/**
4760 * Recovers from defects using the provided function.
4761 *
4762 * **Details**
4763 *
4764 * Typed failures and interruptions are not caught.
4765 *
4766 * **Example** (Recovering from a defect)
4767 *
4768 * ```ts import.meta.vitest
4769 * import { Effect, Stream } from "effect"
4770 *
4771 * const stream = Stream.die("boom").pipe(
4772 * Stream.catchDefect((defect) => Stream.succeed(`recovered: ${defect}`))
4773 * )
4774 *
4775 * const result = Effect.runSync(Stream.runCollect(stream))
4776 * result // => ["recovered: boom"]
4777 * ```
4778 *
4779 * @category error handling
4780 * @since 4.0.0
4781 */
4782export const catchDefect: {
4783 <A2, E2, R2>(
4784 f: (defect: unknown) => Stream<A2, E2, R2>
4785 ): <A, E, R>(self: Stream<A, E, R>) => Stream<A | A2, E | E2, R | R2>
4786 <A, E, R, A2, E2, R2>(
4787 self: Stream<A, E, R>,
4788 f: (defect: unknown) => Stream<A2, E2, R2>
4789 ): Stream<A | A2, E | E2, R | R2>
4790} = dual(2, <A, E, R, A2, E2, R2>(
4791 self: Stream<A, E, R>,
4792 f: (defect: unknown) => Stream<A2, E2, R2>
4793): Stream<A | A2, E | E2, R | R2> =>
4794 self.channel.pipe(
4795 Channel.catchDefect((defect) => f(defect).channel),
4796 fromChannel
4797 ))
4798
4799/**
4800 * Runs an effect when the stream fails without changing its values or error,
4801 * unless the tap effect itself fails.
4802 *
4803 * **Example** (Tapping stream causes)
4804 *
4805 * ```ts import.meta.vitest
4806 * import { Cause, Effect, Stream } from "effect"
4807 *
4808 * const observations: Array<boolean> = []
4809 * const stream = Stream.make(1, 2).pipe(
4810 * Stream.concat(Stream.fail("boom")),
4811 * Stream.tapCause((cause) => Effect.sync(() => observations.push(Cause.isReason(cause)))),
4812 * Stream.catch(() => Stream.succeed(0))
4813 * )
4814 *
4815 * const program = Effect.gen(function* () {
4816 * const result = yield* Stream.runCollect(stream)
4817 * result // => [1, 2, 0]
4818 * })
4819 *
4820 * await Effect.runPromise(program)
4821 * observations // => [false]
4822 * ```
4823 *
4824 * @category error handling
4825 * @since 2.0.0
4826 */
4827export const tapCause: {
4828 <E, A2, E2, R2>(
4829 f: (cause: Cause.Cause<E>) => Effect.Effect<A2, E2, R2>
4830 ): <A, R>(self: Stream<A, E, R>) => Stream<A, E | E2, R2 | R>
4831 <A, E, R, A2, E2, R2>(
4832 self: Stream<A, E, R>,
4833 f: (cause: Cause.Cause<E>) => Effect.Effect<A2, E2, R2>
4834 ): Stream<A, E | E2, R | R2>
4835} = dual(2, <A, E, R, A2, E2, R2>(
4836 self: Stream<A, E, R>,
4837 f: (cause: Cause.Cause<E>) => Effect.Effect<A2, E2, R2>
4838): Stream<A, E | E2, R | R2> =>
4839 self.channel.pipe(
4840 Channel.tapCause(f),
4841 fromChannel
4842 ))
4843
4844const catch_: {
4845 <E, A2, E2, R2>(
4846 f: (error: E) => Stream<A2, E2, R2>
4847 ): <A, R>(self: Stream<A, E, R>) => Stream<A | A2, E2, R2 | R>
4848 <A, E, R, A2, E2, R2>(
4849 self: Stream<A, E, R>,
4850 f: (error: E) => Stream<A2, E2, R2>
4851 ): Stream<A | A2, E2, R | R2>
4852} = dual(2, <A, E, R, A2, E2, R2>(
4853 self: Stream<A, E, R>,
4854 f: (error: E) => Stream<A2, E2, R2>
4855): Stream<A | A2, E2, R | R2> => fromChannel(Channel.catch(self.channel, (error) => f(error).channel)))
4856
4857export {
4858 /**
4859 * Switches over to the stream produced by the provided function if this one fails.
4860 *
4861 * **Example** (Catching stream failures)
4862 *
4863 * ```ts import.meta.vitest
4864 * import { Effect, Stream } from "effect"
4865 *
4866 * const stream = Stream.make(1, 2).pipe(
4867 * Stream.concat(Stream.fail("Oops!")),
4868 * Stream.catch(() => Stream.make(999))
4869 * )
4870 *
4871 * const program = Effect.gen(function*() {
4872 * const values = yield* Stream.runCollect(stream)
4873 * values // => [ 1, 2, 999 ]
4874 * })
4875 *
4876 * await Effect.runPromise(program)
4877 * ```
4878 *
4879 * @category error handling
4880 * @since 4.0.0
4881 */
4882 catch_ as catch
4883}
4884
4885/**
4886 * Peeks at errors effectfully without changing the stream unless the tap fails.
4887 *
4888 * **Example** (Effectfully peeking at errors)
4889 *
4890 * ```ts import.meta.vitest
4891 * import { Effect, Stream } from "effect"
4892 *
4893 * const errors: Array<string> = []
4894 * const stream = Stream.make(1, 2).pipe(
4895 * Stream.concat(Stream.fail("boom")),
4896 * Stream.tapError((error) => Effect.sync(() => errors.push(error))),
4897 * Stream.catch(() => Stream.make(999))
4898 * )
4899 *
4900 * const program = Effect.gen(function*() {
4901 * const values = yield* Stream.runCollect(stream)
4902 * values // => [1, 2, 999]
4903 * })
4904 *
4905 * await Effect.runPromise(program)
4906 * errors // => ["boom"]
4907 * ```
4908 *
4909 * @category error handling
4910 * @since 2.0.0
4911 */
4912export const tapError: {
4913 <E, A2, E2, R2>(
4914 f: (error: E) => Effect.Effect<A2, E2, R2>
4915 ): <A, R>(self: Stream<A, E, R>) => Stream<A, E | E2, R2 | R>
4916 <A, E, R, A2, E2, R2>(
4917 self: Stream<A, E, R>,
4918 f: (error: E) => Effect.Effect<A2, E2, R2>
4919 ): Stream<A, E | E2, R | R2>
4920} = dual(2, <A, E, R, A2, E2, R2>(
4921 self: Stream<A, E, R>,
4922 f: (error: E) => Effect.Effect<A2, E2, R2>
4923): Stream<A, E | E2, R | R2> =>
4924 self.channel.pipe(
4925 Channel.tapError(f),
4926 fromChannel
4927 ))
4928
4929/**
4930 * Peeks at errors with a matching `_tag` effectfully without changing the
4931 * stream unless the tap fails.
4932 *
4933 * **Example** (Effectfully peeking at a tagged error)
4934 *
4935 * ```ts import.meta.vitest
4936 * import { Data, Effect, Stream } from "effect"
4937 *
4938 * class NetworkError extends Data.TaggedError("NetworkError")<{
4939 * statusCode: number
4940 * }> {}
4941 *
4942 * class ValidationError extends Data.TaggedError("ValidationError")<{
4943 * field: string
4944 * }> {}
4945 *
4946 * const seen: Array<number> = []
4947 * const stream: Stream.Stream<number, NetworkError | ValidationError> = Stream.fail(
4948 * new NetworkError({ statusCode: 504 })
4949 * )
4950 *
4951 * const program = stream.pipe(
4952 * Stream.tapErrorTag("NetworkError", (error) => Effect.sync(() => seen.push(error.statusCode))),
4953 * Stream.catch(() => Stream.make(0)),
4954 * Stream.runCollect
4955 * )
4956 *
4957 * await Effect.runPromise(program) // => [ 0 ]
4958 * seen // => [ 504 ]
4959 * ```
4960 *
4961 * @see {@link tapError} for peeking at every typed error
4962 * @see {@link catchTag} for recovering from a tagged error
4963 *
4964 * @category error handling
4965 * @since 4.0.0
4966 */
4967export const tapErrorTag: {
4968 <const K extends Tags<E> | Arr.NonEmptyReadonlyArray<Tags<E>>, E, A1, E1, R1>(
4969 k: K,
4970 f: (
4971 e: ExtractTag<NoInfer<E>, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>
4972 ) => Effect.Effect<A1, E1, R1>
4973 ): <A, R>(self: Stream<A, E, R>) => Stream<A, E | E1, R1 | R>
4974 <
4975 A,
4976 E,
4977 R,
4978 const K extends Tags<E> | Arr.NonEmptyReadonlyArray<Tags<E>>,
4979 R1,
4980 E1,
4981 A1
4982 >(
4983 self: Stream<A, E, R>,
4984 k: K,
4985 f: (e: ExtractTag<E, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>) => Effect.Effect<A1, E1, R1>
4986 ): Stream<A, E | E1, R | R1>
4987} = dual(
4988 3,
4989 <
4990 A,
4991 E,
4992 R,
4993 const K extends Tags<E> | Arr.NonEmptyReadonlyArray<Tags<E>>,
4994 R1,
4995 E1,
4996 A1
4997 >(
4998 self: Stream<A, E, R>,
4999 k: K,
5000 f: (e: ExtractTag<E, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>) => Effect.Effect<A1, E1, R1>
5001 ): Stream<A, E | E1, R | R1> => {
5002 const predicate = Array.isArray(k)
5003 ? ((e: E): e is ExtractTag<E, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K> =>
5004 hasProperty(e, "_tag") && k.includes(e._tag))
5005 : isTagged(k as string)
5006 return tapError(
5007 self,
5008 (error) =>
5009 predicate(error)
5010 ? f(error as ExtractTag<E, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>)
5011 : Effect.void
5012 )
5013 }
5014)
5015
5016/**
5017 * Peeks at defects effectfully without changing the stream unless the tap
5018 * fails.
5019 *
5020 * **Example** (Effectfully peeking at defects)
5021 *
5022 * ```ts import.meta.vitest
5023 * import { Effect, Stream } from "effect"
5024 *
5025 * const defects: Array<unknown> = []
5026 * const stream = Stream.make(1, 2).pipe(
5027 * Stream.concat(Stream.die("boom")),
5028 * Stream.tapDefect((defect) => Effect.sync(() => defects.push(defect))),
5029 * Stream.catchCause(() => Stream.make(3))
5030 * )
5031 *
5032 * const program = Effect.gen(function*() {
5033 * const values = yield* Stream.runCollect(stream)
5034 * values // => [ 1, 2, 3 ]
5035 * })
5036 *
5037 * await Effect.runPromise(program)
5038 * defects // => [ 'boom' ]
5039 * ```
5040 *
5041 * @see {@link tapCause} for peeking at the full failure cause
5042 * @see {@link catchDefect} for recovering from defects
5043 *
5044 * @category error handling
5045 * @since 4.0.0
5046 */
5047export const tapDefect: {
5048 <B, E2, R2>(
5049 f: (defect: unknown) => Effect.Effect<B, E2, R2>
5050 ): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E | E2, R | R2>
5051 <A, E, R, B, E2, R2>(
5052 self: Stream<A, E, R>,
5053 f: (defect: unknown) => Effect.Effect<B, E2, R2>
5054 ): Stream<A, E | E2, R | R2>
5055} = dual(2, <A, E, R, B, E2, R2>(
5056 self: Stream<A, E, R>,
5057 f: (defect: unknown) => Effect.Effect<B, E2, R2>
5058): Stream<A, E | E2, R | R2> =>
5059 tapCause(self, (cause) => {
5060 const defect = Cause.findDefect(cause)
5061 return Result.isSuccess(defect) ? f(defect.success) : Effect.void
5062 }))
5063
5064/**
5065 * Recovers from errors that match a predicate by switching to a recovery stream.
5066 *
5067 * **Details**
5068 *
5069 * When a failure matches the filter, the stream switches to the recovery
5070 * stream. Non-matching failures propagate downstream, so the error type is
5071 * preserved unless the filter narrows it.
5072 *
5073 * **Example** (Catching matching failures)
5074 *
5075 * ```ts import.meta.vitest
5076 * import { Effect, Stream } from "effect"
5077 *
5078 * const stream = Stream.make(1, 2).pipe(
5079 * Stream.concat(Stream.fail(42)),
5080 * Stream.catchIf(
5081 * (error): error is 42 => error === 42,
5082 * () => Stream.make(999)
5083 * )
5084 * )
5085 *
5086 * const program = Effect.gen(function*() {
5087 * const values = yield* Stream.runCollect(stream)
5088 * values // => [ 1, 2, 999 ]
5089 * })
5090 *
5091 * await Effect.runPromise(program)
5092 * ```
5093 *
5094 * @category error handling
5095 * @since 4.0.0
5096 */
5097export const catchIf: {
5098 <E, EB extends E, A2, E2, R2, A3 = unassigned, E3 = never, R3 = never>(
5099 refinement: Refinement<NoInfer<E>, EB>,
5100 f: (e: EB) => Stream<A2, E2, R2>,
5101 orElse?: ((e: Exclude<E, EB>) => Stream<A3, E3, R3>) | undefined
5102 ): <A, R>(
5103 self: Stream<A, E, R>
5104 ) => Stream<A | A2 | Exclude<A3, unassigned>, E2 | E3 | (A3 extends unassigned ? Exclude<E, EB> : never), R | R2 | R3>
5105 <E, A2, E2, R2, A3 = unassigned, E3 = never, R3 = never>(
5106 predicate: Predicate<NoInfer<E>>,
5107 f: (e: NoInfer<E>) => Stream<A2, E2, R2>,
5108 orElse?: ((e: NoInfer<E>) => Stream<A3, E3, R3>) | undefined
5109 ): <A, R>(
5110 self: Stream<A, E, R>
5111 ) => Stream<A | A2 | Exclude<A3, unassigned>, E2 | E3 | (A3 extends unassigned ? E : never), R | R2 | R3>
5112 <A, E, R, EB extends E, A2, E2, R2, A3 = unassigned, E3 = never, R3 = never>(
5113 self: Stream<A, E, R>,
5114 refinement: Refinement<E, EB>,
5115 f: (e: EB) => Stream<A2, E2, R2>,
5116 orElse?: ((e: Exclude<E, EB>) => Stream<A3, E3, R3>) | undefined
5117 ): Stream<A | A2 | Exclude<A3, unassigned>, E2 | E3 | (A3 extends unassigned ? Exclude<E, EB> : never), R | R2 | R3>
5118 <A, E, R, A2, E2, R2, A3 = unassigned, E3 = never, R3 = never>(
5119 self: Stream<A, E, R>,
5120 predicate: Predicate<E>,
5121 f: (e: E) => Stream<A2, E2, R2>,
5122 orElse?: ((e: E) => Stream<A3, E3, R3>) | undefined
5123 ): Stream<A | A2 | Exclude<A3, unassigned>, E2 | E3 | (A3 extends unassigned ? E : never), R | R2 | R3>
5124} = dual((args) => isStream(args[0]), <
5125 A,
5126 E,
5127 R,
5128 A2,
5129 E2,
5130 R2,
5131 A3 = never,
5132 E3 = E,
5133 R3 = never
5134>(
5135 self: Stream<A, E, R>,
5136 predicate: Predicate<E>,
5137 f: (failure: E) => Stream<A2, E2, R2>,
5138 orElse?: ((failure: E) => Stream<A3, E3, R3>) | undefined
5139): Stream<A | A2 | A3, E2 | E3, R | R2 | R3> =>
5140 fromChannel(
5141 Channel.catchIf(
5142 toChannel(self),
5143 predicate,
5144 (e) => f(e).channel,
5145 orElse && ((e) => orElse(e).channel)
5146 )
5147 ))
5148
5149/**
5150 * Recovers from errors that match a `Filter` by switching to a recovery
5151 * stream.
5152 *
5153 * **When to use**
5154 *
5155 * Use to recover from stream errors with a reusable `Filter` when matching can
5156 * also narrow or transform the error before choosing the recovery stream.
5157 *
5158 * **Details**
5159 *
5160 * Successful filter results are passed to `f`. Failed filter results go to
5161 * `orElse` when provided; otherwise the filter failure is re-failed.
5162 *
5163 * @see {@link catchIf} for predicate or refinement based recovery
5164 * @see {@link catchTag} for `_tag` based recovery from one tagged error
5165 * @see {@link catchTags} for `_tag` based recovery from multiple tagged errors
5166 * @see {@link catchCauseFilter} for filtering full causes
5167 *
5168 * @category error handling
5169 * @since 4.0.0
5170 */
5171export const catchFilter: {
5172 <E, EB, A2, E2, R2, X, A3 = unassigned, E3 = never, R3 = never>(
5173 filter: Filter.Filter<NoInfer<E>, EB, X>,
5174 f: (failure: EB) => Stream<A2, E2, R2>,
5175 orElse?: ((failure: X) => Stream<A3, E3, R3>) | undefined
5176 ): <A, R>(
5177 self: Stream<A, E, R>
5178 ) => Stream<A | A2 | Exclude<A3, unassigned>, E2 | E3 | (A3 extends unassigned ? X : never), R | R2 | R3>
5179 <A, E, R, EB, A2, E2, R2, X, A3 = unassigned, E3 = never, R3 = never>(
5180 self: Stream<A, E, R>,
5181 filter: Filter.Filter<NoInfer<E>, EB, X>,
5182 f: (failure: EB) => Stream<A2, E2, R2>,
5183 orElse?: ((failure: X) => Stream<A3, E3, R3>) | undefined
5184 ): Stream<A | A2 | Exclude<A3, unassigned>, E2 | E3 | (A3 extends unassigned ? X : never), R | R2 | R3>
5185} = dual((args) => isStream(args[0]), <
5186 A,
5187 E,
5188 R,
5189 EB,
5190 A2,
5191 E2,
5192 R2,
5193 X,
5194 A3 = never,
5195 E3 = X,
5196 R3 = never
5197>(
5198 self: Stream<A, E, R>,
5199 filter: Filter.Filter<NoInfer<E>, EB, X>,
5200 f: (failure: EB) => Stream<A2, E2, R2>,
5201 orElse?: ((failure: X) => Stream<A3, E3, R3>) | undefined
5202): Stream<A | A2 | A3, E2 | E3, R | R2 | R3> =>
5203 fromChannel(
5204 Channel.catchFilter(
5205 toChannel(self),
5206 filter,
5207 (e) => f(e).channel,
5208 orElse && ((e) => orElse(e).channel)
5209 )
5210 ))
5211
5212/**
5213 * Recovers from failures whose `_tag` matches the provided value by switching to
5214 * the stream returned by `f`.
5215 *
5216 * **When to use**
5217 *
5218 * Use when you need to handle a specific error case from a stream whose error
5219 * type is a tagged union with a readonly `_tag` field.
5220 *
5221 * **Example** (Catching tagged failures)
5222 *
5223 * ```ts import.meta.vitest
5224 * import { Data, Effect, Stream } from "effect"
5225 *
5226 * class HttpError extends Data.TaggedError("HttpError")<{ message: string }> {}
5227 *
5228 * const stream = Stream.fail(new HttpError({ message: "timeout" }))
5229 *
5230 * const recovered = Stream.catchTag(stream, "HttpError", (error) =>
5231 * Stream.make(`Recovered: ${error.message}`)
5232 * )
5233 *
5234 * const program = Effect.gen(function*() {
5235 * const values = yield* Stream.runCollect(recovered)
5236 * values // => [ 'Recovered: timeout' ]
5237 * })
5238 *
5239 * await Effect.runPromise(program)
5240 * ```
5241 *
5242 * @category error handling
5243 * @since 2.0.0
5244 */
5245export const catchTag: {
5246 <
5247 const K extends Tags<E> | Arr.NonEmptyReadonlyArray<Tags<E>>,
5248 E,
5249 A1,
5250 E1,
5251 R1,
5252 A2 = unassigned,
5253 E2 = never,
5254 R2 = never
5255 >(
5256 k: K,
5257 f: (
5258 e: ExtractTag<NoInfer<E>, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>
5259 ) => Stream<A1, E1, R1>,
5260 orElse?:
5261 | ((e: ExcludeTag<E, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>) => Stream<A2, E2, R2>)
5262 | undefined
5263 ): <A, R>(
5264 self: Stream<A, E, R>
5265 ) => Stream<
5266 A | A1 | Exclude<A2, unassigned>,
5267 | E1
5268 | E2
5269 | (A2 extends unassigned ? ExcludeTag<E, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K> : never),
5270 R | R1 | R2
5271 >
5272 <
5273 A,
5274 E,
5275 R,
5276 const K extends Tags<E> | Arr.NonEmptyReadonlyArray<Tags<E>>,
5277 R1,
5278 E1,
5279 A1,
5280 A2 = unassigned,
5281 E2 = never,
5282 R2 = never
5283 >(
5284 self: Stream<A, E, R>,
5285 k: K,
5286 f: (e: ExtractTag<E, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>) => Stream<A1, E1, R1>,
5287 orElse?:
5288 | ((e: ExcludeTag<E, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>) => Stream<A2, E2, R2>)
5289 | undefined
5290 ): Stream<
5291 A | A1 | Exclude<A2, unassigned>,
5292 | E1
5293 | E2
5294 | (A2 extends unassigned ? ExcludeTag<E, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K> : never),
5295 R | R1 | R2
5296 >
5297} = dual(
5298 (args) => isStream(args[0]),
5299 <
5300 A,
5301 E,
5302 R,
5303 const K extends Tags<E> | Arr.NonEmptyReadonlyArray<Tags<E>>,
5304 R1,
5305 E1,
5306 A1,
5307 A2 = never,
5308 E2 = ExcludeTag<E, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>,
5309 R2 = never
5310 >(
5311 self: Stream<A, E, R>,
5312 k: K,
5313 f: (e: ExtractTag<E, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>) => Stream<A1, E1, R1>,
5314 orElse?:
5315 | ((e: ExcludeTag<E, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>) => Stream<A2, E2, R2>)
5316 | undefined
5317 ): Stream<A1 | A | A2, E1 | E2, R1 | R | R2> => {
5318 const pred = Array.isArray(k)
5319 ? ((e: E): e is any => hasProperty(e, "_tag") && k.includes(e._tag))
5320 : isTagged(k as string)
5321 return catchIf(self, pred, f, orElse as any) as any
5322 }
5323)
5324
5325/**
5326 * Switches to a recovery stream based on matching `_tag` handlers.
5327 *
5328 * **Example** (Catching tagged failures with handlers)
5329 *
5330 * ```ts import.meta.vitest
5331 * import { Effect, Stream } from "effect"
5332 *
5333 * class NotFound {
5334 * readonly _tag = "NotFound"
5335 * constructor(readonly resource: string) {}
5336 * }
5337 *
5338 * class Unauthorized {
5339 * readonly _tag = "Unauthorized"
5340 * constructor(readonly user: string) {}
5341 * }
5342 *
5343 * const stream = Stream.fail(new NotFound("profile"))
5344 *
5345 * const program = Effect.gen(function* () {
5346 * const result = yield* stream.pipe(
5347 * Stream.catchTags({
5348 * NotFound: () => Stream.succeed("fallback"),
5349 * Unauthorized: () => Stream.succeed("login")
5350 * }),
5351 * Stream.runCollect
5352 * )
5353 * result // => [ 'fallback' ]
5354 * })
5355 *
5356 * await Effect.runPromise(program)
5357 * ```
5358 *
5359 * @category error handling
5360 * @since 2.0.0
5361 */
5362export const catchTags: {
5363 <
5364 E,
5365 Cases extends
5366 & { [K in Extract<E, { _tag: string }>["_tag"]]+?: ((error: Extract<E, { _tag: K }>) => Stream<any, any, any>) }
5367 & (unknown extends E ? {} : { [K in Exclude<keyof Cases, Extract<E, { _tag: string }>["_tag"]>]: never }),
5368 A2 = unassigned,
5369 E2 = never,
5370 R2 = never
5371 >(
5372 cases: Cases,
5373 orElse?: ((e: Exclude<E, { _tag: keyof Cases }>) => Stream<A2, E2, R2>) | undefined
5374 ): <A, R>(self: Stream<A, E, R>) => Stream<
5375 | A
5376 | Exclude<A2, unassigned>
5377 | {
5378 [K in keyof Cases]: Cases[K] extends ((...args: Array<any>) => Stream<infer A, any, any>) ? A : never
5379 }[keyof Cases],
5380 | E2
5381 | (A2 extends unassigned ? Exclude<E, { _tag: keyof Cases }> : never)
5382 | {
5383 [K in keyof Cases]: Cases[K] extends ((...args: Array<any>) => Stream<any, infer E, any>) ? E : never
5384 }[keyof Cases],
5385 | R
5386 | R2
5387 | {
5388 [K in keyof Cases]: Cases[K] extends ((...args: Array<any>) => Stream<any, any, infer R>) ? R : never
5389 }[keyof Cases]
5390 >
5391 <
5392 R,
5393 E,
5394 A,
5395 Cases extends
5396 & { [K in Extract<E, { _tag: string }>["_tag"]]+?: ((error: Extract<E, { _tag: K }>) => Stream<any, any, any>) }
5397 & (unknown extends E ? {} : { [K in Exclude<keyof Cases, Extract<E, { _tag: string }>["_tag"]>]: never }),
5398 A2 = unassigned,
5399 E2 = never,
5400 R2 = never
5401 >(
5402 self: Stream<A, E, R>,
5403 cases: Cases,
5404 orElse?: ((e: Exclude<E, { _tag: keyof Cases }>) => Stream<A2, E2, R2>) | undefined
5405 ): Stream<
5406 | A
5407 | Exclude<A2, unassigned>
5408 | {
5409 [K in keyof Cases]: Cases[K] extends ((...args: Array<any>) => Stream<infer A, any, any>) ? A : never
5410 }[keyof Cases],
5411 | E2
5412 | (A2 extends unassigned ? Exclude<E, { _tag: keyof Cases }> : never)
5413 | {
5414 [K in keyof Cases]: Cases[K] extends ((...args: Array<any>) => Stream<any, infer E, any>) ? E : never
5415 }[keyof Cases],
5416 | R
5417 | R2
5418 | {
5419 [K in keyof Cases]: Cases[K] extends ((...args: Array<any>) => Stream<any, any, infer R>) ? R : never
5420 }[keyof Cases]
5421 >
5422} = dual((args) => isStream(args[0]), (self, cases, orElse) => {
5423 let keys: Array<string>
5424 return catchFilter(
5425 self,
5426 (e: any) => {
5427 keys ??= Object.keys(cases)
5428 return hasProperty(e, "_tag") && isString(e["_tag"]) && keys.includes(e["_tag"])
5429 ? Result.succeed(e)
5430 : Result.fail(e)
5431 },
5432 (e: any) => cases[e["_tag"] as string](e),
5433 orElse
5434 )
5435})
5436
5437/**
5438 * Catches a specific reason within a tagged error.
5439 *
5440 * **When to use**
5441 *
5442 * Use to handle nested error causes without removing the parent error
5443 * from the error channel.
5444 *
5445 * **Details**
5446 *
5447 * The handler receives the unwrapped reason.
5448 *
5449 * **Example** (Catching a tagged error reason)
5450 *
5451 * ```ts import.meta.vitest
5452 * import { Data, Effect, Stream } from "effect"
5453 *
5454 * class RateLimitError extends Data.TaggedError("RateLimitError")<{
5455 * retryAfter: number
5456 * }> {}
5457 *
5458 * class QuotaExceededError extends Data.TaggedError("QuotaExceededError")<{
5459 * limit: number
5460 * }> {}
5461 *
5462 * class AiError extends Data.TaggedError("AiError")<{
5463 * reason: RateLimitError | QuotaExceededError
5464 * }> {}
5465 *
5466 * const stream = Stream.fail(
5467 * new AiError({ reason: new RateLimitError({ retryAfter: 60 }) })
5468 * )
5469 *
5470 * const program = Effect.gen(function*() {
5471 * const values = yield* stream.pipe(
5472 * Stream.catchReason("AiError", "RateLimitError", (reason) =>
5473 * Stream.succeed(`retry: ${reason.retryAfter}`)
5474 * ),
5475 * Stream.runCollect
5476 * )
5477 * values // => [ 'retry: 60' ]
5478 * })
5479 *
5480 * await Effect.runPromise(program)
5481 * ```
5482 *
5483 * @category error handling
5484 * @since 4.0.0
5485 */
5486export const catchReason: {
5487 <
5488 K extends Tags<E>,
5489 E,
5490 RK extends ReasonTags<ExtractTag<NoInfer<E>, K>>,
5491 A2,
5492 E2,
5493 R2,
5494 A3 = unassigned,
5495 E3 = never,
5496 R3 = never
5497 >(
5498 errorTag: K,
5499 reasonTag: RK,
5500 f: (
5501 reason: ExtractReason<ExtractTag<NoInfer<E>, K>, RK>,
5502 error: NarrowReason<ExtractTag<NoInfer<E>, K>, RK>
5503 ) => Stream<A2, E2, R2>,
5504 orElse?:
5505 | ((
5506 reason: ExcludeReason<ExtractTag<NoInfer<E>, K>, RK>,
5507 error: OmitReason<ExtractTag<NoInfer<E>, K>, RK>
5508 ) => Stream<A3, E3, R3>)
5509 | undefined
5510 ): <A, R>(
5511 self: Stream<A, E, R>
5512 ) => Stream<
5513 A | A2 | Exclude<A3, unassigned>,
5514 ExcludeTag<E, K> | E2 | E3 | (A3 extends unassigned ? ExtractTag<E, K> : never),
5515 R | R2 | R3
5516 >
5517 <
5518 A,
5519 E,
5520 R,
5521 K extends Tags<E>,
5522 RK extends ReasonTags<ExtractTag<E, K>>,
5523 A2,
5524 E2,
5525 R2,
5526 A3 = unassigned,
5527 E3 = never,
5528 R3 = never
5529 >(
5530 self: Stream<A, E, R>,
5531 errorTag: K,
5532 reasonTag: RK,
5533 f: (reason: ExtractReason<ExtractTag<E, K>, RK>, error: NarrowReason<ExtractTag<E, K>, RK>) => Stream<A2, E2, R2>,
5534 orElse?:
5535 | ((reason: ExcludeReason<ExtractTag<E, K>, RK>, error: OmitReason<ExtractTag<E, K>, RK>) => Stream<A3, E3, R3>)
5536 | undefined
5537 ): Stream<
5538 A | A2 | Exclude<A3, unassigned>,
5539 ExcludeTag<E, K> | E2 | E3 | (A3 extends unassigned ? ExtractTag<E, K> : never),
5540 R | R2 | R3
5541 >
5542} = dual(
5543 (args) => isStream(args[0]),
5544 <
5545 A,
5546 E,
5547 R,
5548 K extends Tags<E>,
5549 RK extends ReasonTags<ExtractTag<E, K>>,
5550 A2,
5551 E2,
5552 R2,
5553 A3 = unassigned,
5554 E3 = never,
5555 R3 = never
5556 >(
5557 self: Stream<A, E, R>,
5558 errorTag: K,
5559 reasonTag: RK,
5560 f: (reason: ExtractReason<ExtractTag<E, K>, RK>, error: NarrowReason<ExtractTag<E, K>, RK>) => Stream<A2, E2, R2>,
5561 orElse?:
5562 | ((reason: ExcludeReason<ExtractTag<E, K>, RK>, error: OmitReason<ExtractTag<E, K>, RK>) => Stream<A3, E3, R3>)
5563 | undefined
5564 ): Stream<
5565 A | A2 | Exclude<A3, unassigned>,
5566 ExcludeTag<E, K> | E2 | E3 | (A3 extends unassigned ? ExtractTag<E, K> : never),
5567 R | R2 | R3
5568 > =>
5569 fromChannel(
5570 Channel.catchReason(
5571 toChannel(self),
5572 errorTag,
5573 reasonTag,
5574 (reason, error) => f(reason, error).channel,
5575 orElse && ((reason, error) => orElse(reason, error).channel)
5576 )
5577 ) as any
5578)
5579
5580/**
5581 * Catches multiple reasons within a tagged error using an object of handlers.
5582 *
5583 * **Example** (Catching tagged error reasons)
5584 *
5585 * ```ts import.meta.vitest
5586 * import { Data, Effect, Stream } from "effect"
5587 *
5588 * class RateLimitError extends Data.TaggedError("RateLimitError")<{
5589 * retryAfter: number
5590 * }> {}
5591 *
5592 * class QuotaExceededError extends Data.TaggedError("QuotaExceededError")<{
5593 * limit: number
5594 * }> {}
5595 *
5596 * class AiError extends Data.TaggedError("AiError")<{
5597 * reason: RateLimitError | QuotaExceededError
5598 * }> {}
5599 *
5600 * const stream = Stream.fail(
5601 * new AiError({ reason: new RateLimitError({ retryAfter: 60 }) })
5602 * )
5603 *
5604 * const program = Effect.gen(function*() {
5605 * const values = yield* stream.pipe(
5606 * Stream.catchReasons("AiError", {
5607 * RateLimitError: (reason) => Stream.succeed(`retry: ${reason.retryAfter}`),
5608 * QuotaExceededError: (reason) => Stream.succeed(`quota: ${reason.limit}`)
5609 * }),
5610 * Stream.runCollect
5611 * )
5612 * values // => [ 'retry: 60' ]
5613 * })
5614 *
5615 * await Effect.runPromise(program)
5616 * ```
5617 *
5618 * @category error handling
5619 * @since 4.0.0
5620 */
5621export const catchReasons: {
5622 <
5623 K extends Tags<E>,
5624 E,
5625 Cases extends {
5626 [RK in ReasonTags<ExtractTag<NoInfer<E>, K>>]+?: (
5627 reason: ExtractReason<ExtractTag<NoInfer<E>, K>, RK>,
5628 error: NarrowReason<ExtractTag<NoInfer<E>, K>, RK>
5629 ) => Stream<any, any, any>
5630 },
5631 A2 = unassigned,
5632 E2 = never,
5633 R2 = never
5634 >(
5635 errorTag: K,
5636 cases: Cases,
5637 orElse?:
5638 | ((
5639 reason: ExcludeReason<ExtractTag<NoInfer<E>, K>, Extract<keyof Cases, string>>,
5640 error: OmitReason<ExtractTag<NoInfer<E>, K>, Extract<keyof Cases, string>>
5641 ) => Stream<A2, E2, R2>)
5642 | undefined
5643 ): <A, R>(self: Stream<A, E, R>) => Stream<
5644 | A
5645 | Exclude<A2, unassigned>
5646 | {
5647 [RK in keyof Cases]: Cases[RK] extends (...args: Array<any>) => Stream<infer A, any, any> ? A : never
5648 }[keyof Cases],
5649 | ExcludeTag<E, K>
5650 | E2
5651 | (A2 extends unassigned ? ExtractTag<E, K> : never)
5652 | {
5653 [RK in keyof Cases]: Cases[RK] extends (...args: Array<any>) => Stream<any, infer E, any> ? E : never
5654 }[keyof Cases],
5655 | R
5656 | R2
5657 | {
5658 [RK in keyof Cases]: Cases[RK] extends (...args: Array<any>) => Stream<any, any, infer R> ? R : never
5659 }[keyof Cases]
5660 >
5661 <
5662 A,
5663 E,
5664 R,
5665 K extends Tags<E>,
5666 Cases extends {
5667 [RK in ReasonTags<ExtractTag<E, K>>]+?: (
5668 reason: ExtractReason<ExtractTag<E, K>, RK>,
5669 error: NarrowReason<ExtractTag<E, K>, RK>
5670 ) => Stream<any, any, any>
5671 },
5672 A2 = unassigned,
5673 E2 = never,
5674 R2 = never
5675 >(
5676 self: Stream<A, E, R>,
5677 errorTag: K,
5678 cases: Cases,
5679 orElse?:
5680 | ((
5681 reason: ExcludeReason<ExtractTag<NoInfer<E>, K>, Extract<keyof Cases, string>>,
5682 error: OmitReason<ExtractTag<NoInfer<E>, K>, Extract<keyof Cases, string>>
5683 ) => Stream<A2, E2, R2>)
5684 | undefined
5685 ): Stream<
5686 | A
5687 | Exclude<A2, unassigned>
5688 | {
5689 [RK in keyof Cases]: Cases[RK] extends (...args: Array<any>) => Stream<infer A, any, any> ? A : never
5690 }[keyof Cases],
5691 | ExcludeTag<E, K>
5692 | E2
5693 | (A2 extends unassigned ? ExtractTag<E, K> : never)
5694 | {
5695 [RK in keyof Cases]: Cases[RK] extends (...args: Array<any>) => Stream<any, infer E, any> ? E : never
5696 }[keyof Cases],
5697 | R
5698 | R2
5699 | {
5700 [RK in keyof Cases]: Cases[RK] extends (...args: Array<any>) => Stream<any, any, infer R> ? R : never
5701 }[keyof Cases]
5702 >
5703} = dual((args) => isStream(args[0]), (self, errorTag, cases, orElse) => {
5704 const handlers: Record<string, (reason: any, error: any) => Channel.Channel<any, any, any, any, any, any, any>> =
5705 Object.create(null)
5706 for (const key of Object.keys(cases)) {
5707 const handler = (cases as any)[key]
5708 handlers[key] = (reason, error) => handler(reason, error).channel
5709 }
5710 const orElseHandler = orElse && ((reason: any, error: any) => orElse(reason, error).channel)
5711 return fromChannel(
5712 Channel.catchReasons(self.channel, errorTag as any, handlers as any, orElseHandler as any) as Channel.Channel<
5713 Arr.NonEmptyReadonlyArray<any>,
5714 any,
5715 void,
5716 unknown,
5717 unknown,
5718 unknown,
5719 any
5720 >
5721 ) as any
5722})
5723
5724/**
5725 * Promotes nested reason errors into the stream error channel, replacing the
5726 * parent error.
5727 *
5728 * **Example** (Extracting the reason from a tagged error)
5729 *
5730 * ```ts import.meta.vitest
5731 * import { Data, Effect, Stream } from "effect"
5732 *
5733 * class RateLimitError extends Data.TaggedError("RateLimitError")<{
5734 * retryAfter: number
5735 * }> {}
5736 *
5737 * class QuotaExceededError extends Data.TaggedError("QuotaExceededError")<{
5738 * limit: number
5739 * }> {}
5740 *
5741 * class AiError extends Data.TaggedError("AiError")<{
5742 * reason: RateLimitError | QuotaExceededError
5743 * }> {}
5744 *
5745 * const stream: Stream.Stream<string, AiError> = Stream.fail(
5746 * new AiError({ reason: new RateLimitError({ retryAfter: 30 }) })
5747 * )
5748 *
5749 * // Before: Stream<string, AiError>
5750 * // After: Stream<string, RateLimitError | QuotaExceededError>
5751 * const unwrapped = stream.pipe(Stream.unwrapReason("AiError"))
5752 *
5753 * const program = Effect.gen(function*() {
5754 * const error = yield* Effect.flip(Stream.runCollect(unwrapped))
5755 * error._tag // => "RateLimitError"
5756 * })
5757 *
5758 * await Effect.runPromise(program)
5759 * ```
5760 *
5761 * @see {@link catchReason} for recovering from a specific reason
5762 * @see {@link catchReasons} for handling several reasons at once
5763 *
5764 * @category error handling
5765 * @since 4.0.0
5766 */
5767export const unwrapReason: {
5768 <
5769 K extends Effect.TagsWithReason<E>,
5770 E
5771 >(
5772 errorTag: K
5773 ): <A, R>(self: Stream<A, E, R>) => Stream<A, ExcludeTag<E, K> | ReasonOf<ExtractTag<E, K>>, R>
5774 <
5775 A,
5776 E,
5777 R,
5778 K extends Effect.TagsWithReason<E>
5779 >(
5780 self: Stream<A, E, R>,
5781 errorTag: K
5782 ): Stream<A, ExcludeTag<E, K> | ReasonOf<ExtractTag<E, K>>, R>
5783} = dual(
5784 2,
5785 <
5786 A,
5787 E,
5788 R,
5789 K extends Effect.TagsWithReason<E>
5790 >(
5791 self: Stream<A, E, R>,
5792 errorTag: K
5793 ): Stream<A, ExcludeTag<E, K> | ReasonOf<ExtractTag<E, K>>, R> =>
5794 catchFilter(
5795 self,
5796 (e: any) => {
5797 if (isTagged(e, errorTag) && hasProperty(e, "reason")) {
5798 return Result.succeed(e.reason)
5799 }
5800 return Result.fail(e)
5801 },
5802 fail as any
5803 ) as any
5804)
5805
5806/**
5807 * Transforms the errors emitted by this stream using `f`.
5808 *
5809 * **Example** (Mapping stream errors)
5810 *
5811 * ```ts import.meta.vitest
5812 * import { Effect, Stream } from "effect"
5813 *
5814 * const program = Effect.gen(function*() {
5815 * const result = yield* Stream.fail("bad").pipe(
5816 * Stream.mapError((error) => `mapped: ${error}`),
5817 * Stream.catch((error) => Stream.make(`recovered from ${error}`)),
5818 * Stream.runCollect
5819 * )
5820 * result // => [ 'recovered from mapped: bad' ]
5821 * })
5822 *
5823 * await Effect.runPromise(program)
5824 * ```
5825 *
5826 * @category error handling
5827 * @since 2.0.0
5828 */
5829export const mapError: {
5830 <E, E2>(f: (error: E) => E2): <A, R>(self: Stream<A, E, R>) => Stream<A, E2, R>
5831 <A, E, R, E2>(self: Stream<A, E, R>, f: (error: E) => E2): Stream<A, E2, R>
5832} = dual(2, <A, E, R, E2>(
5833 self: Stream<A, E, R>,
5834 f: (error: E) => E2
5835): Stream<A, E2, R> => fromChannel(Channel.mapError(self.channel, f)))
5836
5837/**
5838 * Recovers from stream failures by filtering the `Cause` and switching to a recovery stream.
5839 * Non-matching causes are re-emitted as failures.
5840 *
5841 * **Example** (Catching matching causes)
5842 *
5843 * ```ts import.meta.vitest
5844 * import { Cause, Effect, Stream } from "effect"
5845 *
5846 * const program = Effect.gen(function*() {
5847 * const failingStream = Stream.fail("NetworkError")
5848 * const recovered = Stream.catchCauseIf(
5849 * failingStream,
5850 * (cause) => Cause.hasFails(cause),
5851 * (cause) => Stream.make(`Recovered: ${Cause.squash(cause)}`)
5852 * )
5853 *
5854 * const output = yield* Stream.runCollect(recovered)
5855 * output // => [ 'Recovered: NetworkError' ]
5856 * })
5857 *
5858 * await Effect.runPromise(program)
5859 * ```
5860 *
5861 * @category error handling
5862 * @since 4.0.0
5863 */
5864export const catchCauseIf: {
5865 <E, A2, E2, R2>(
5866 predicate: Predicate<Cause.Cause<E>>,
5867 f: (cause: Cause.Cause<E>) => Stream<A2, E2, R2>
5868 ): <A, R>(
5869 self: Stream<A, E, R>
5870 ) => Stream<A | A2, E | E2, R2 | R>
5871 <A, E, R, A2, E2, R2>(
5872 self: Stream<A, E, R>,
5873 predicate: Predicate<Cause.Cause<E>>,
5874 f: (cause: Cause.Cause<E>) => Stream<A2, E2, R2>
5875 ): Stream<A | A2, E | E2, R | R2>
5876} = dual(3, <A, E, R, A2, E2, R2>(
5877 self: Stream<A, E, R>,
5878 predicate: Predicate<Cause.Cause<E>>,
5879 f: (cause: Cause.Cause<E>) => Stream<A2, E2, R2>
5880): Stream<A | A2, E | E2, R | R2> =>
5881 fromChannel(
5882 Channel.catchCauseIf(
5883 self.channel,
5884 predicate,
5885 (cause) => f(cause).channel
5886 )
5887 ))
5888
5889/**
5890 * Recovers from stream failures by filtering the `Cause` and switching to a
5891 * recovery stream.
5892 *
5893 * **When to use**
5894 *
5895 * Use when you need to recover a stream only from causes selected by a
5896 * `Filter`, while giving the recovery both the selected value and the original
5897 * `Cause`.
5898 *
5899 * **Details**
5900 *
5901 * The filter is applied to the full `Cause`. A successful filter result is
5902 * passed to `f` together with the original cause; a failed filter result
5903 * re-fails with the residual cause.
5904 *
5905 * @see {@link catchCauseIf} for predicate-based cause selection
5906 * @see {@link catchFilter} for filtering typed error values instead of full causes
5907 * @see {@link catchCause} for recovering from every cause without filtering
5908 *
5909 * @category error handling
5910 * @since 4.0.0
5911 */
5912export const catchCauseFilter: {
5913 <E, EB, A2, E2, R2, X extends Cause.Cause<any>>(
5914 filter: Filter.Filter<Cause.Cause<E>, EB, X>,
5915 f: (failure: EB, cause: Cause.Cause<E>) => Stream<A2, E2, R2>
5916 ): <A, R>(
5917 self: Stream<A, E, R>
5918 ) => Stream<A | A2, Cause.Cause.Error<X> | E2, R2 | R>
5919 <A, E, R, EB, A2, E2, R2, X extends Cause.Cause<any>>(
5920 self: Stream<A, E, R>,
5921 filter: Filter.Filter<Cause.Cause<E>, EB, X>,
5922 f: (failure: EB, cause: Cause.Cause<E>) => Stream<A2, E2, R2>
5923 ): Stream<A | A2, Cause.Cause.Error<X> | E2, R | R2>
5924} = dual(3, <A, E, R, EB, A2, E2, R2, X extends Cause.Cause<any>>(
5925 self: Stream<A, E, R>,
5926 filter: Filter.Filter<Cause.Cause<E>, EB, X>,
5927 f: (failure: EB, cause: Cause.Cause<E>) => Stream<A2, E2, R2>
5928): Stream<A | A2, Cause.Cause.Error<X> | E2, R | R2> =>
5929 fromChannel(
5930 Channel.catchCauseFilter(
5931 self.channel,
5932 filter,
5933 (failure, cause) => f(failure, cause).channel
5934 )
5935 ))
5936
5937/**
5938 * Switches to a fallback stream if this stream is empty.
5939 *
5940 * **Example** (Switching on empty streams)
5941 *
5942 * ```ts import.meta.vitest
5943 * import { Effect, Stream } from "effect"
5944 *
5945 * const program = Effect.gen(function*() {
5946 * const values = yield* Stream.empty.pipe(
5947 * Stream.orElseIfEmpty(() => Stream.make(1, 2)),
5948 * Stream.runCollect
5949 * )
5950 * values // => [ 1, 2 ]
5951 * })
5952 *
5953 * await Effect.runPromise(program)
5954 * ```
5955 *
5956 * @category error handling
5957 * @since 2.0.0
5958 */
5959export const orElseIfEmpty: {
5960 <E, A2, E2, R2>(
5961 orElse: LazyArg<Stream<A2, E2, R2>>
5962 ): <A, R>(self: Stream<A, E, R>) => Stream<A | A2, E | E2, R | R2>
5963 <A, E, R, A2, E2, R2>(
5964 self: Stream<A, E, R>,
5965 orElse: LazyArg<Stream<A2, E2, R2>>
5966 ): Stream<A | A2, E | E2, R | R2>
5967} = dual(2, <A, E, R, A2, E2, R2>(
5968 self: Stream<A, E, R>,
5969 orElse: LazyArg<Stream<A2, E2, R2>>
5970): Stream<A | A2, E | E2, R | R2> =>
5971 fromChannel(Channel.orElseIfEmpty(
5972 self.channel,
5973 (_) => toChannel(orElse())
5974 )))
5975
5976/**
5977 * Returns a stream that emits a fallback value when this stream fails.
5978 *
5979 * **Example** (Recovering with a fallback value)
5980 *
5981 * ```ts import.meta.vitest
5982 * import { Effect, Stream } from "effect"
5983 *
5984 * const program = Effect.gen(function*() {
5985 * const stream = Stream.fail("NetworkError").pipe(
5986 * Stream.orElseSucceed((error) => `Recovered: ${error}`)
5987 * )
5988 *
5989 * const values = yield* Stream.runCollect(stream)
5990 * values // => [ 'Recovered: NetworkError' ]
5991 * })
5992 *
5993 * await Effect.runPromise(program)
5994 * ```
5995 *
5996 * @category error handling
5997 * @since 2.0.0
5998 */
5999export const orElseSucceed: {
6000 <E, A2>(
6001 f: (error: E) => A2
6002 ): <A, R>(self: Stream<A, E, R>) => Stream<A | A2, never, R>
6003 <A, E, R, A2>(
6004 self: Stream<A, E, R>,
6005 f: (error: E) => A2
6006 ): Stream<A | A2, never, R>
6007} = dual(2, <A, E, R, A2>(
6008 self: Stream<A, E, R>,
6009 f: (error: E) => A2
6010): Stream<A | A2, never, R> => catch_(self, (e) => succeed(f(e))))
6011
6012/**
6013 * Turns typed failures into defects, making the stream infallible.
6014 *
6015 * **Example** (Turning failures into defects)
6016 *
6017 * ```ts import.meta.vitest
6018 * import { Effect, Stream } from "effect"
6019 *
6020 * const program = Effect.gen(function*() {
6021 * const values = yield* Stream.make(1, 2, 3).pipe(
6022 * Stream.orDie,
6023 * Stream.runCollect
6024 * )
6025 *
6026 * values // => [ 1, 2, 3 ]
6027 * })
6028 *
6029 * await Effect.runPromise(program)
6030 * ```
6031 *
6032 * @category error handling
6033 * @since 2.0.0
6034 */
6035export const orDie = <A, E, R>(self: Stream<A, E, R>): Stream<A, never, R> => fromChannel(Channel.orDie(self.channel))
6036
6037/**
6038 * Ignores failures and ends the stream on error.
6039 *
6040 * **When to use**
6041 *
6042 * Use when you want a failing stream to end gracefully rather than propagate
6043 * the error.
6044 *
6045 * **Details**
6046 *
6047 * The `log` option controls whether the failure is logged before the stream
6048 * terminates.
6049 *
6050 * **Example** (Ignoring stream failures)
6051 *
6052 * ```ts import.meta.vitest
6053 * import { Effect, Stream } from "effect"
6054 *
6055 * const program = Effect.gen(function*() {
6056 * const values = yield* Stream.make(1, 2, 3).pipe(
6057 * Stream.concat(Stream.fail("boom")),
6058 * Stream.ignore,
6059 * Stream.runCollect
6060 * )
6061 * values // => [ 1, 2, 3 ]
6062 * })
6063 *
6064 * await Effect.runPromise(program)
6065 * ```
6066 *
6067 * **Example** (Configuring ignore logging)
6068 *
6069 * ```ts import.meta.vitest
6070 * import { Effect, Stream } from "effect"
6071 *
6072 * await Effect.runPromise(Effect.gen(function*() {
6073 * const values = yield* Stream.fail("boom").pipe(
6074 * Stream.ignore({ log: false }),
6075 * Stream.runCollect
6076 * )
6077 * values // => []
6078 * }))
6079 *
6080 * ```
6081 *
6082 * @see {@link ignoreCause} for a variant that also ignores defects, not just typed failures
6083 *
6084 * @category error handling
6085 * @since 4.0.0
6086 */
6087export const ignore: <
6088 Arg extends Stream<any, any, any> | {
6089 readonly log?: boolean | Severity | undefined
6090 } | undefined
6091>(
6092 selfOrOptions: Arg,
6093 options?: {
6094 readonly log?: boolean | Severity | undefined
6095 } | undefined
6096) => [Arg] extends [Stream<infer A, infer _E, infer R>] ? Stream<A, never, R>
6097 : <A, E, R>(self: Stream<A, E, R>) => Stream<A, never, R> = dual(
6098 (args) => isStream(args[0]),
6099 <A, E, R>(
6100 self: Stream<A, E, R>,
6101 options?: {
6102 readonly log?: boolean | Severity | undefined
6103 } | undefined
6104 ): Stream<A, never, R> => fromChannel(Channel.ignore(self.channel, options))
6105 )
6106
6107/**
6108 * Ignores the stream's failure cause, including defects, and ends the stream.
6109 *
6110 * **When to use**
6111 *
6112 * Use when you need to silently suppress a stream's entire failure cause,
6113 * including both typed errors and defects, rather than propagate it downstream.
6114 *
6115 * **Example** (Ignoring stream failure causes)
6116 *
6117 * ```ts import.meta.vitest
6118 * import { Effect, Stream } from "effect"
6119 *
6120 * await Effect.runPromise(Effect.gen(function*() {
6121 * const values = yield* Stream.make(1, 2).pipe(
6122 * Stream.concat(Stream.die("boom")),
6123 * Stream.ignoreCause({ log: false }),
6124 * Stream.runCollect
6125 * )
6126 * values // => [1, 2]
6127 * }))
6128 *
6129 * ```
6130 *
6131 * @see {@link ignore} to ignore only typed failures without suppressing defects
6132 *
6133 * @category error handling
6134 * @since 4.0.0
6135 */
6136export const ignoreCause: <
6137 Arg extends Stream<any, any, any> | {
6138 readonly log?: boolean | Severity | undefined
6139 } | undefined
6140>(
6141 streamOrOptions: Arg,
6142 options?: {
6143 readonly log?: boolean | Severity | undefined
6144 } | undefined
6145) => [Arg] extends [Stream<infer A, infer _E, infer R>] ? Stream<A, never, R>
6146 : <A, E, R>(self: Stream<A, E, R>) => Stream<A, never, R> = dual(
6147 (args) => isStream(args[0]),
6148 <A, E, R>(
6149 self: Stream<A, E, R>,
6150 options?: { readonly log?: boolean | Severity | undefined } | undefined
6151 ): Stream<A, never, R> => fromChannel(Channel.ignoreCause(self.channel, options))
6152 )
6153
6154/**
6155 * Retries the stream according to the given schedule when it fails.
6156 *
6157 * **Details**
6158 *
6159 * This retries the entire stream, so will re-execute all of the stream's
6160 * acquire operations.
6161 *
6162 * The schedule is reset as soon as the first element passes through the
6163 * stream again.
6164 *
6165 * **Example** (Retrying stream failures)
6166 *
6167 * ```ts import.meta.vitest
6168 * import { Effect, Schedule, Stream } from "effect"
6169 *
6170 * const program = Effect.gen(function*() {
6171 * const values = yield* Stream.make(1).pipe(
6172 * Stream.concat(Stream.fail("boom")),
6173 * Stream.retry(Schedule.recurs(1)),
6174 * Stream.take(2),
6175 * Stream.runCollect
6176 * )
6177 *
6178 * values // => [ 1, 1 ]
6179 * })
6180 *
6181 * await Effect.runPromise(program)
6182 * ```
6183 *
6184 * @category error handling
6185 * @since 2.0.0
6186 */
6187export const retry: {
6188 <E, X, E2, R2>(
6189 policy:
6190 | Schedule.Schedule<X, NoInfer<E>, E2, R2>
6191 | ((
6192 $: <SO, SE, SR>(_: Schedule.Schedule<SO, NoInfer<E>, SE, SR>) => Schedule.Schedule<SO, E, SE, SR>
6193 ) => Schedule.Schedule<X, NoInfer<E>, E2, R2>)
6194 ): <A, R>(self: Stream<A, E, R>) => Stream<A, E | E2, R2 | R>
6195 <A, E, R, X, E2, R2>(
6196 self: Stream<A, E, R>,
6197 policy:
6198 | Schedule.Schedule<X, NoInfer<E>, E2, R2>
6199 | ((
6200 $: <SO, SE, SR>(_: Schedule.Schedule<SO, NoInfer<E>, SE, SR>) => Schedule.Schedule<SO, E, SE, SR>
6201 ) => Schedule.Schedule<X, NoInfer<E>, E2, R2>)
6202 ): Stream<A, E | E2, R2 | R>
6203} = dual(
6204 2,
6205 <A, E, R, X, E2, R2>(
6206 self: Stream<A, E, R>,
6207 policy:
6208 | Schedule.Schedule<X, NoInfer<E>, E2, R2>
6209 | ((
6210 $: <SO, SE, SR>(_: Schedule.Schedule<SO, NoInfer<E>, SE, SR>) => Schedule.Schedule<SO, E, SE, SR>
6211 ) => Schedule.Schedule<X, NoInfer<E>, E2, R2>)
6212 ): Stream<A, E | E2, R2 | R> => fromChannel(Channel.retry(self.channel, policy))
6213)
6214
6215const retryWithoutReset = <A, E, R, X, E2, R2>(
6216 self: Stream<A, E, R>,
6217 schedule: Schedule.Schedule<X, E, E2, R2>
6218): Stream<A, E | E2, R | R2> =>
6219 unwrap(Effect.map(Schedule.toStepWithMetadata(schedule), (step) => {
6220 let meta = Schedule.CurrentMetadata.defaultValue()
6221 const loop = (): Stream<A, E | E2, R | R2> =>
6222 catch_(
6223 provideServiceEffect(self, Schedule.CurrentMetadata, Effect.sync(() => meta)),
6224 (error) =>
6225 unwrap(Pull.catchDone(
6226 Effect.map(step(error), (meta_) => {
6227 meta = meta_
6228 return unwrap(Effect.as(Effect.yieldNow, loop()))
6229 }),
6230 () => Effect.succeed(fail(error))
6231 ))
6232 )
6233 return loop()
6234 }))
6235
6236/**
6237 * Applies an `ExecutionPlan` to a stream, retrying with step-provided resources
6238 * until it succeeds or the plan is exhausted.
6239 *
6240 * **Details**
6241 *
6242 * By default, a failing step can fallback even after emitting elements; set
6243 * `preventFallbackOnPartialStream` to fail instead of mixing partial output with
6244 * a later fallback.
6245 *
6246 * Attempts can be observed from outside the stream by passing
6247 * `options.onEvent`, which receives an `ExecutionPlan.Event` before each
6248 * attempt and after it settles; see `Effect.withExecutionPlan` for the handler
6249 * semantics. When a downstream consumer stops pulling early (for example
6250 * `Stream.take` outside the plan), the truncated attempt reports
6251 * `AttemptSuccess`: the consumer stopped, not the source.
6252 *
6253 * **Example** (Applying an execution plan)
6254 *
6255 * ```ts import.meta.vitest
6256 * import { Context, Effect, ExecutionPlan, Layer, Stream } from "effect"
6257 *
6258 * class Service extends Context.Service<Service>()("Service", {
6259 * make: Effect.succeed({
6260 * stream: Stream.fail("A") as Stream.Stream<number, string>
6261 * })
6262 * }) {
6263 * static Bad = Layer.succeed(Service, Service.of({ stream: Stream.fail("A") }))
6264 * static Good = Layer.succeed(Service, Service.of({ stream: Stream.make(1, 2, 3) }))
6265 * }
6266 *
6267 * const plan = ExecutionPlan.make(
6268 * { provide: Service.Bad },
6269 * { provide: Service.Good }
6270 * )
6271 *
6272 * const stream = Stream.unwrap(Effect.map(Service, (_) => _.stream))
6273 *
6274 * const program = Effect.gen(function*() {
6275 * const items = yield* stream.pipe(Stream.withExecutionPlan(plan), Stream.runCollect)
6276 * items // => [ 1, 2, 3 ]
6277 * })
6278 *
6279 * await Effect.runPromise(program)
6280 * ```
6281 *
6282 * @category error handling
6283 * @since 3.16.0
6284 */
6285export const withExecutionPlan: {
6286 <Input, R2, Provides, PolicyE, RX = never>(
6287 policy: ExecutionPlan.ExecutionPlan<{ provides: Provides; input: Input; error: PolicyE; requirements: R2 }>,
6288 options?: {
6289 readonly preventFallbackOnPartialStream?: boolean | undefined
6290 readonly onEvent?: ((event: ExecutionPlan.Event<Input | PolicyE>) => Effect.Effect<void, never, RX>) | undefined
6291 }
6292 ): <A, E extends Input, R>(self: Stream<A, E, R>) => Stream<A, E | PolicyE, R2 | Exclude<R, Provides> | RX>
6293 <A, E extends Input, R, R2, Input, Provides, PolicyE, RX = never>(
6294 self: Stream<A, E, R>,
6295 policy: ExecutionPlan.ExecutionPlan<{ provides: Provides; input: Input; error: PolicyE; requirements: R2 }>,
6296 options?: {
6297 readonly preventFallbackOnPartialStream?: boolean | undefined
6298 readonly onEvent?: ((event: ExecutionPlan.Event<E | PolicyE>) => Effect.Effect<void, never, RX>) | undefined
6299 }
6300 ): Stream<A, E | PolicyE, R2 | Exclude<R, Provides> | RX>
6301} = dual((args) => isStream(args[0]), <A, E extends Input, R, R2, Input, Provides, PolicyE, RX>(
6302 self: Stream<A, E, R>,
6303 policy: ExecutionPlan.ExecutionPlan<{
6304 provides: Provides
6305 input: Input
6306 error: PolicyE
6307 requirements: R2
6308 }>,
6309 options?: {
6310 readonly preventFallbackOnPartialStream?: boolean | undefined
6311 readonly onEvent?: ((event: ExecutionPlan.Event<E | PolicyE>) => Effect.Effect<void, never, RX>) | undefined
6312 }
6313): Stream<A, E | PolicyE, R2 | Exclude<R, Provides> | RX> =>
6314 suspend(() => {
6315 const preventFallbackOnPartialStream = options?.preventFallbackOnPartialStream ?? false
6316 let i = 0
6317 let meta: ExecutionPlan.Metadata = {
6318 attempt: 0,
6319 stepIndex: 0
6320 }
6321 const provideMeta = provideServiceEffect(
6322 ExecutionPlan.CurrentMetadata,
6323 Effect.sync(() => {
6324 meta = {
6325 attempt: meta.attempt + 1,
6326 stepIndex: i
6327 }
6328 return meta
6329 })
6330 )
6331 const emitter = options?.onEvent === undefined
6332 ? undefined
6333 : internalExecutionPlan.makeEventEmitter(options.onEvent, () => meta)
6334 let attemptState: internalExecutionPlan.AttemptState | undefined
6335 const instrument: (attempt: Stream<A, E | PolicyE, any>) => Stream<A, E | PolicyE, any> = emitter === undefined
6336 ? identity
6337 : (attempt) =>
6338 onExit(
6339 onStart(
6340 attempt,
6341 Effect.map(emitter.begin, (state) => {
6342 attemptState = state
6343 })
6344 ),
6345 (exit) =>
6346 Effect.suspend(() => {
6347 if (attemptState === undefined) return Effect.void
6348 const state = attemptState
6349 attemptState = undefined
6350 return emitter.end(state, exit)
6351 })
6352 )
6353 let lastError = Option.none<E | PolicyE>()
6354 const loop: Stream<
6355 A,
6356 E | PolicyE,
6357 R2 | Exclude<R, Provides>
6358 > = suspend(() => {
6359 const step = policy.steps[i]
6360 if (!step) {
6361 return fail(Option.getOrThrow(lastError))
6362 }
6363
6364 let nextStream: Stream<A, E | PolicyE, R2 | Exclude<R, Provides>> = provideMeta(
6365 instrument(provide(self, step.provide))
6366 )
6367 let receivedElements = false
6368
6369 if (Option.isSome(lastError)) {
6370 const error = lastError.value
6371 let attempted = false
6372 const wrapped = nextStream
6373 // ensure the schedule is applied at least once
6374 nextStream = suspend(() => {
6375 if (attempted) return wrapped
6376 attempted = true
6377 return fail(error)
6378 })
6379 nextStream = retryWithoutReset(nextStream, internalExecutionPlan.scheduleFromStep(step, false) as any)
6380 } else {
6381 const schedule = internalExecutionPlan.scheduleFromStep(step, true)
6382 nextStream = schedule ? retryWithoutReset(nextStream, schedule as any) : nextStream
6383 }
6384
6385 return catch_(
6386 preventFallbackOnPartialStream ?
6387 onFirst(nextStream, (_) => {
6388 receivedElements = true
6389 return Effect.void
6390 }) :
6391 nextStream,
6392 (error) => {
6393 i++
6394 if (preventFallbackOnPartialStream && receivedElements) {
6395 return fail(error)
6396 }
6397 lastError = Option.some(error)
6398 return loop
6399 }
6400 )
6401 })
6402 return loop
6403 }))
6404
6405/**
6406 * Takes the first `n` elements from this stream.
6407 *
6408 * **Details**
6409 *
6410 * Finite fractional values of `n` are rounded down. `NaN` and non-positive
6411 * values return `Stream.empty` without evaluating the source stream.
6412 *
6413 * **Example** (Taking values from the left)
6414 *
6415 * ```ts import.meta.vitest
6416 * import { Effect, Stream } from "effect"
6417 *
6418 * const program = Effect.gen(function*() {
6419 * const values = yield* Stream.make(1, 2, 3, 4, 5).pipe(
6420 * Stream.take(3),
6421 * Stream.runCollect
6422 * )
6423 * values // => [ 1, 2, 3 ]
6424 * })
6425 *
6426 * await Effect.runPromise(program)
6427 * ```
6428 *
6429 * @category filtering
6430 * @since 2.0.0
6431 */
6432export const take: {
6433 (n: number): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E, R>
6434 <A, E, R>(self: Stream<A, E, R>, n: number): Stream<A, E, R>
6435} = dual(
6436 2,
6437 <A, E, R>(self: Stream<A, E, R>, n: number): Stream<A, E, R> => {
6438 const count = Count.normalize(n)
6439 return count === 0 ? empty : takeUntil(self, (_, i) => i === (count - 1))
6440 }
6441)
6442
6443/**
6444 * Emits byte chunks until the configured limit would be exceeded, then drops
6445 * the crossing chunk and switches to a fallback stream.
6446 *
6447 * **Example** (Truncating at a byte limit)
6448 *
6449 * ```ts import.meta.vitest
6450 * import { ByteSize, Effect, Stream } from "effect"
6451 *
6452 * const program = Stream.make(
6453 * new Uint8Array([1, 2]),
6454 * new Uint8Array([3, 4, 5])
6455 * ).pipe(
6456 * Stream.limitBytes(ByteSize.bytes(4), () => Stream.empty),
6457 * Stream.runCollect,
6458 * Effect.map((chunks) => chunks.map((chunk) => [...chunk]))
6459 * )
6460 *
6461 * await Effect.runPromise(program) // => [[1, 2]]
6462 * ```
6463 *
6464 * @category filtering
6465 * @since 4.0.0
6466 */
6467export const limitBytes: {
6468 <E, R>(
6469 bytes: ByteSize.Input,
6470 onLimitReached: LazyArg<Stream<Uint8Array, E, R>>
6471 ): (self: Stream<Uint8Array, E, R>) => Stream<Uint8Array, E, R>
6472 <E, R>(
6473 self: Stream<Uint8Array, E, R>,
6474 bytes: ByteSize.Input,
6475 onLimitReached: LazyArg<Stream<Uint8Array, E, R>>
6476 ): Stream<Uint8Array, E, R>
6477} = dual(3, <E, R>(
6478 self: Stream<Uint8Array, E, R>,
6479 bytes: ByteSize.Input,
6480 onLimitReached: LazyArg<Stream<Uint8Array, E, R>>
6481): Stream<Uint8Array, E, R> =>
6482 suspend(() => {
6483 const limit = ByteSize.fromInputUnsafe(bytes)
6484 let size = BigInt(0)
6485 let limitReached = false
6486 return concat(
6487 takeWhile(self, (chunk) => {
6488 const nextSize = size + BigInt(chunk.length)
6489 if (nextSize > limit) {
6490 limitReached = true
6491 return false
6492 }
6493 size = nextSize
6494 return true
6495 }),
6496 suspend(() => limitReached ? onLimitReached() : empty)
6497 )
6498 }))
6499
6500/**
6501 * Keeps the last `n` elements from this stream.
6502 *
6503 * **Details**
6504 *
6505 * Finite fractional values of `n` are rounded down. `NaN` and non-positive
6506 * values return `Stream.empty` without evaluating the source stream.
6507 *
6508 * **Example** (Taking elements from the right)
6509 *
6510 * ```ts import.meta.vitest
6511 * import { Effect, Stream } from "effect"
6512 *
6513 * const program = Effect.gen(function*() {
6514 * const values = yield* Stream.range(1, 6).pipe(
6515 * Stream.takeRight(3),
6516 * Stream.runCollect
6517 * )
6518 * values // => [ 4, 5, 6 ]
6519 * })
6520 *
6521 * await Effect.runPromise(program)
6522 * ```
6523 *
6524 * @category filtering
6525 * @since 2.0.0
6526 */
6527export const takeRight: {
6528 (n: number): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E, R>
6529 <A, E, R>(self: Stream<A, E, R>, n: number): Stream<A, E, R>
6530} = dual(
6531 2,
6532 <A, E, R>(self: Stream<A, E, R>, n: number): Stream<A, E, R> => {
6533 const count = Count.normalize(n)
6534 if (count === 0) return empty
6535 return mapAccumArray(self, MutableList.make<A>, (list, arr) => {
6536 MutableList.appendAll(list, arr)
6537 if (list.length > count) {
6538 MutableList.takeNVoid(list, list.length - count)
6539 }
6540 return [list, emptyArr]
6541 }, {
6542 onHalt(list) {
6543 return MutableList.takeAll(list)
6544 }
6545 })
6546 }
6547)
6548
6549/**
6550 * Takes elements until the predicate matches.
6551 *
6552 * **Details**
6553 *
6554 * When `excludeLast` is `true`, the matching element is dropped.
6555 *
6556 * **Example** (Taking until a predicate matches)
6557 *
6558 * ```ts import.meta.vitest
6559 * import { Effect, Stream } from "effect"
6560 *
6561 * const stream = Stream.range(1, 5)
6562 *
6563 * const program = Effect.gen(function*() {
6564 * const inclusive = yield* stream.pipe(
6565 * Stream.takeUntil((n) => n % 3 === 0),
6566 * Stream.runCollect
6567 * )
6568 * inclusive // => [ 1, 2, 3 ]
6569 *
6570 * const exclusive = yield* stream.pipe(
6571 * Stream.takeUntil((n) => n % 3 === 0, { excludeLast: true }),
6572 * Stream.runCollect
6573 * )
6574 * exclusive // => [ 1, 2 ]
6575 * })
6576 * await Effect.runPromise(program)
6577 * ```
6578 *
6579 * @category filtering
6580 * @since 2.0.0
6581 */
6582export const takeUntil: {
6583 <A>(predicate: (a: NoInfer<A>, n: number) => boolean, options?: {
6584 readonly excludeLast?: boolean | undefined
6585 }): <E, R>(self: Stream<A, E, R>) => Stream<A, E, R>
6586 <A, E, R>(self: Stream<A, E, R>, predicate: (a: A, n: number) => boolean, options?: {
6587 readonly excludeLast?: boolean | undefined
6588 }): Stream<A, E, R>
6589} = dual(
6590 (args) => isStream(args[0]),
6591 <A, E, R>(self: Stream<A, E, R>, predicate: (a: A, n: number) => boolean, options?: {
6592 readonly excludeLast?: boolean | undefined
6593 }): Stream<A, E, R> =>
6594 transformPull(self, (pull, _scope) =>
6595 Effect.sync(() => {
6596 let i = 0
6597 let done = false
6598 const pump: Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E, void, R> = Effect.flatMap(
6599 Effect.suspend(() => done ? Cause.done() : pull),
6600 (chunk) => {
6601 const index = chunk.findIndex((a) => predicate(a, i++))
6602 if (index >= 0) {
6603 done = true
6604 const arr = chunk.slice(0, options?.excludeLast ? index : index + 1)
6605 return Arr.isReadonlyArrayNonEmpty(arr) ? Effect.succeed(arr) : Cause.done()
6606 }
6607 return Effect.succeed(chunk)
6608 }
6609 )
6610 return pump
6611 }))
6612)
6613
6614/**
6615 * Takes stream elements until an effectful predicate returns `true`.
6616 *
6617 * **When to use**
6618 *
6619 * Use when the stopping condition needs an Effect or service and predicate
6620 * failure should fail the stream.
6621 *
6622 * **Example** (Taking until an effectful predicate matches)
6623 *
6624 * ```ts import.meta.vitest
6625 * import { Effect, Stream } from "effect"
6626 *
6627 * const program = Effect.gen(function*() {
6628 * const result = yield* Stream.range(1, 5).pipe(
6629 * Stream.takeUntilEffect((n) => Effect.succeed(n % 3 === 0)),
6630 * Stream.runCollect
6631 * )
6632 * result // => [ 1, 2, 3 ]
6633 * })
6634 *
6635 * await Effect.runPromise(program)
6636 * ```
6637 *
6638 * @category filtering
6639 * @since 2.0.0
6640 */
6641export const takeUntilEffect: {
6642 <A, E2, R2>(
6643 predicate: (a: NoInfer<A>, n: number) => Effect.Effect<boolean, E2, R2>,
6644 options?: {
6645 readonly excludeLast?: boolean | undefined
6646 }
6647 ): <E, R>(self: Stream<A, E, R>) => Stream<A, E2 | E, R2 | R>
6648 <A, E, R, E2, R2>(
6649 self: Stream<A, E, R>,
6650 predicate: (a: A, n: number) => Effect.Effect<boolean, E2, R2>,
6651 options?: {
6652 readonly excludeLast?: boolean | undefined
6653 }
6654 ): Stream<A, E | E2, R | R2>
6655} = dual((args) => isStream(args[0]), <A, E, R, E2, R2>(
6656 self: Stream<A, E, R>,
6657 predicate: (a: A, n: number) => Effect.Effect<boolean, E2, R2>,
6658 options?: {
6659 readonly excludeLast?: boolean | undefined
6660 }
6661): Stream<A, E | E2, R | R2> =>
6662 transformPull(self, (pull, _scope) =>
6663 Effect.sync(() => {
6664 let i = 0
6665 let done = false
6666 return Effect.gen(function*() {
6667 if (done) return yield* Cause.done()
6668 const chunk = yield* pull
6669 for (let j = 0; j < chunk.length; j++) {
6670 if (yield* predicate(chunk[j], i++)) {
6671 done = true
6672 const arr = chunk.slice(0, options?.excludeLast ? j : j + 1)
6673 return Arr.isReadonlyArrayNonEmpty(arr) ? arr : yield* Cause.done()
6674 }
6675 }
6676 return chunk
6677 })
6678 })))
6679
6680/**
6681 * Takes the longest initial prefix of elements that satisfy the predicate.
6682 *
6683 * **Example** (Taking while a predicate holds)
6684 *
6685 * ```ts import.meta.vitest
6686 * import { Effect, Stream } from "effect"
6687 *
6688 * const stream = Stream.range(1, 5).pipe(
6689 * Stream.takeWhile((n) => n % 3 !== 0)
6690 * )
6691 *
6692 * const program = Effect.gen(function*() {
6693 * const result = yield* Stream.runCollect(stream)
6694 * result // => [ 1, 2 ]
6695 * })
6696 *
6697 * await Effect.runPromise(program)
6698 * ```
6699 *
6700 * @category filtering
6701 * @since 2.0.0
6702 */
6703export const takeWhile: {
6704 <A, B extends A>(refinement: (a: NoInfer<A>, n: number) => a is B): <E, R>(self: Stream<A, E, R>) => Stream<B, E, R>
6705 <A>(predicate: (a: NoInfer<A>, n: number) => boolean): <E, R>(self: Stream<A, E, R>) => Stream<A, E, R>
6706 <A, E, R, B extends A>(self: Stream<A, E, R>, refinement: (a: NoInfer<A>, n: number) => a is B): Stream<B, E, R>
6707 <A, E, R>(self: Stream<A, E, R>, predicate: (a: NoInfer<A>, n: number) => boolean): Stream<A, E, R>
6708} = dual(
6709 2,
6710 <A, E, R>(
6711 self: Stream<A, E, R>,
6712 predicate: (a: A, n: number) => boolean
6713 ): Stream<A, E, R> =>
6714 transformPull(self, (pull, _scope) =>
6715 Effect.sync(() => {
6716 let i = 0
6717 let done = false
6718 const pump: Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E, void, R> = Effect.flatMap(
6719 Effect.suspend(() => done ? Cause.done() : pull),
6720 (chunk) => {
6721 const out: Array<A> = []
6722 for (let j = 0; j < chunk.length; j++) {
6723 if (!predicate(chunk[j], i++)) {
6724 done = true
6725 break
6726 }
6727 out.push(chunk[j])
6728 }
6729 return Arr.isReadonlyArrayNonEmpty(out) ? Effect.succeed(out) : done ? Cause.done() : pump
6730 }
6731 )
6732 return pump
6733 }))
6734)
6735
6736/**
6737 * Takes the longest initial prefix accepted by a `Filter` and emits the
6738 * filter's success values.
6739 *
6740 * **When to use**
6741 *
6742 * Use to keep the leading stream elements that a `Filter` accepts, emit the
6743 * filter's success values, and stop at the first filter failure.
6744 *
6745 * **Details**
6746 *
6747 * The stream stops at the first `Result.fail` returned by the filter.
6748 *
6749 * @see {@link takeWhile} for keeping original elements with a boolean predicate or refinement
6750 * @see {@link filterMap} for filtering across the whole stream instead of only the leading prefix
6751 * @see {@link dropWhileFilter} for dropping the accepted prefix and keeping the remaining original elements
6752 *
6753 * @category filtering
6754 * @since 4.0.0
6755 */
6756export const takeWhileFilter: {
6757 <A, B, X>(f: Filter.Filter<NoInfer<A>, B, X>): <E, R>(self: Stream<A, E, R>) => Stream<B, E, R>
6758 <A, E, R, B, X>(self: Stream<A, E, R>, f: Filter.Filter<NoInfer<A>, B, X>): Stream<B, E, R>
6759} = dual(
6760 2,
6761 <A, E, R, B, X>(
6762 self: Stream<A, E, R>,
6763 filter: Filter.Filter<NoInfer<A>, B, X>
6764 ): Stream<B, E, R> =>
6765 transformPull(self, (pull, _scope) =>
6766 Effect.sync(() => {
6767 let done = false
6768 const pump: Pull.Pull<Arr.NonEmptyReadonlyArray<B>, E, void, R> = Effect.flatMap(
6769 Effect.suspend(() => done ? Cause.done() : pull),
6770 (chunk) => {
6771 const out: Array<B> = []
6772 for (let j = 0; j < chunk.length; j++) {
6773 const result = filter(chunk[j])
6774 if (Result.isFailure(result)) {
6775 done = true
6776 break
6777 }
6778 out.push(result.success)
6779 }
6780 return Arr.isReadonlyArrayNonEmpty(out) ? Effect.succeed(out) : done ? Cause.done() : pump
6781 }
6782 )
6783 return pump
6784 }))
6785)
6786
6787/**
6788 * Takes elements from the stream while the effectful predicate is `true`.
6789 *
6790 * **When to use**
6791 *
6792 * Use when the leading-prefix predicate needs an Effect or service and the
6793 * stream should stop before the first false result.
6794 *
6795 * **Example** (Effectfully taking while a predicate holds)
6796 *
6797 * ```ts import.meta.vitest
6798 * import { Effect, Stream } from "effect"
6799 *
6800 * const program = Effect.gen(function*() {
6801 * const result = yield* Stream.range(1, 5).pipe(
6802 * Stream.takeWhileEffect((n) => Effect.succeed(n % 3 !== 0)),
6803 * Stream.runCollect
6804 * )
6805 * result // => [ 1, 2 ]
6806 * })
6807 *
6808 * await Effect.runPromise(program)
6809 * ```
6810 *
6811 * @category filtering
6812 * @since 4.0.0
6813 */
6814export const takeWhileEffect: {
6815 <A, E2, R2>(
6816 predicate: (a: NoInfer<A>, n: number) => Effect.Effect<boolean, E2, R2>
6817 ): <E, R>(self: Stream<A, E, R>) => Stream<A, E | E2, R | R2>
6818 <A, E, R, E2, R2>(
6819 self: Stream<A, E, R>,
6820 predicate: (a: NoInfer<A>, n: number) => Effect.Effect<boolean, E2, R2>
6821 ): Stream<A, E | E2, R | R2>
6822} = dual(2, <A, E, R, E2, R2>(
6823 self: Stream<A, E, R>,
6824 predicate: (a: NoInfer<A>, n: number) => Effect.Effect<boolean, E2, R2>
6825) =>
6826 takeUntilEffect(self, (a, n) =>
6827 Effect.map(
6828 predicate(a, n),
6829 (b) => !b
6830 ), { excludeLast: true }))
6831
6832/**
6833 * Drops the first `n` elements from this stream.
6834 *
6835 * **Details**
6836 *
6837 * Finite fractional values of `n` are rounded down. `NaN` and non-positive
6838 * values return the source stream unchanged.
6839 *
6840 * **Example** (Dropping values from the left)
6841 *
6842 * ```ts import.meta.vitest
6843 * import { Effect, Stream } from "effect"
6844 *
6845 * const stream = Stream.make(1, 2, 3, 4, 5)
6846 * const result = Stream.drop(stream, 2)
6847 *
6848 * const program = Effect.gen(function*() {
6849 * const items = yield* Stream.runCollect(result)
6850 * items // => [ 3, 4, 5 ]
6851 * })
6852 *
6853 * await Effect.runPromise(program)
6854 * ```
6855 *
6856 * @category filtering
6857 * @since 2.0.0
6858 */
6859export const drop: {
6860 (n: number): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E, R>
6861 <A, E, R>(self: Stream<A, E, R>, n: number): Stream<A, E, R>
6862} = dual(
6863 2,
6864 <A, E, R>(self: Stream<A, E, R>, n: number): Stream<A, E, R> => {
6865 const count = Count.normalize(n)
6866 if (count === 0) return self
6867 return transformPull(self, (pull, _scope) =>
6868 Effect.sync(() => {
6869 let dropped = 0
6870 const pump: Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E, void, R> = pull.pipe(
6871 Effect.flatMap((chunk) => {
6872 if (dropped >= count) return Effect.succeed(chunk)
6873 dropped += chunk.length
6874 if (dropped <= count) return pump
6875 return Effect.succeed(chunk.slice(count - dropped) as Arr.NonEmptyArray<A>)
6876 })
6877 )
6878 return pump
6879 }))
6880 }
6881)
6882
6883/**
6884 * Drops elements until the specified predicate evaluates to `true`, then drops
6885 * that matching element.
6886 *
6887 * **Example** (Dropping until a predicate matches)
6888 *
6889 * ```ts import.meta.vitest
6890 * import { Effect, Stream } from "effect"
6891 *
6892 * const stream = Stream.make(1, 2, 3, 4, 5)
6893 * const result = Stream.dropUntil(stream, (n) => n >= 3)
6894 *
6895 * await Effect.runPromise(Effect.gen(function*() {
6896 * const output = yield* Stream.runCollect(result)
6897 * output // => [ 4, 5 ]
6898 * }))
6899 * ```
6900 *
6901 * @category filtering
6902 * @since 2.0.0
6903 */
6904export const dropUntil: {
6905 <A>(predicate: (a: NoInfer<A>, index: number) => boolean): <E, R>(self: Stream<A, E, R>) => Stream<A, E, R>
6906 <A, E, R>(self: Stream<A, E, R>, predicate: (a: NoInfer<A>, index: number) => boolean): Stream<A, E, R>
6907} = dual(2, <A, E, R>(
6908 self: Stream<A, E, R>,
6909 predicate: (a: NoInfer<A>, index: number) => boolean
6910): Stream<A, E, R> => drop(dropWhile(self, (a, i) => !predicate(a, i)), 1))
6911
6912/**
6913 * Drops all elements of the stream until the specified effectful predicate
6914 * evaluates to `true`.
6915 *
6916 * **When to use**
6917 *
6918 * Use when dropping the leading prefix requires an Effect or service and the
6919 * first matching element should also be dropped.
6920 *
6921 * **Example** (Dropping until an effectful predicate matches)
6922 *
6923 * ```ts import.meta.vitest
6924 * import { Effect, Stream } from "effect"
6925 *
6926 * const program = Effect.gen(function*() {
6927 * const result = yield* Stream.range(1, 5).pipe(
6928 * Stream.dropUntilEffect((n) => Effect.succeed(n % 3 === 0)),
6929 * Stream.runCollect
6930 * )
6931 * result // => [ 4, 5 ]
6932 * })
6933 *
6934 * await Effect.runPromise(program)
6935 * ```
6936 *
6937 * @category filtering
6938 * @since 2.0.0
6939 */
6940export const dropUntilEffect: {
6941 <A, E2, R2>(
6942 predicate: (a: NoInfer<A>, index: number) => Effect.Effect<boolean, E2, R2>
6943 ): <E, R>(self: Stream<A, E, R>) => Stream<A, E2 | E, R2 | R>
6944 <A, E, R, E2, R2>(
6945 self: Stream<A, E, R>,
6946 predicate: (a: NoInfer<A>, index: number) => Effect.Effect<boolean, E2, R2>
6947 ): Stream<A, E | E2, R | R2>
6948} = dual(2, <A, E, R, E2, R2>(
6949 self: Stream<A, E, R>,
6950 predicate: (a: NoInfer<A>, index: number) => Effect.Effect<boolean, E2, R2>
6951): Stream<A, E | E2, R | R2> =>
6952 drop(
6953 dropWhileEffect(
6954 self,
6955 (a, i) => Effect.map(predicate(a, i), (b) => !b)
6956 ),
6957 1
6958 ))
6959
6960/**
6961 * Drops elements from the stream while the specified predicate evaluates to `true`.
6962 *
6963 * **Example** (Dropping while a predicate holds)
6964 *
6965 * ```ts import.meta.vitest
6966 * import { Effect, Stream } from "effect"
6967 *
6968 * const program = Effect.gen(function*() {
6969 * const values = yield* Stream.make(1, 2, 3, 4, 5).pipe(
6970 * Stream.dropWhile((n) => n < 3),
6971 * Stream.runCollect
6972 * )
6973 * values // => [ 3, 4, 5 ]
6974 * })
6975 *
6976 * await Effect.runPromise(program)
6977 * ```
6978 *
6979 * @category filtering
6980 * @since 2.0.0
6981 */
6982export const dropWhile: {
6983 <A>(predicate: (a: NoInfer<A>, index: number) => boolean): <E, R>(self: Stream<A, E, R>) => Stream<A, E, R>
6984 <A, E, R>(self: Stream<A, E, R>, predicate: (a: NoInfer<A>, index: number) => boolean): Stream<A, E, R>
6985} = dual(2, <A, E, R>(
6986 self: Stream<A, E, R>,
6987 predicate: (a: A, index: number) => boolean
6988): Stream<A, E, R> =>
6989 transformPull(self, (pull, _scope) =>
6990 Effect.sync(() => {
6991 let dropping = true
6992 let index = 0
6993 const filtered: Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E> = Effect.flatMap(pull, (arr) => {
6994 const found = arr.findIndex((a) => !predicate(a, index++))
6995 if (found === -1) return filtered
6996 dropping = false
6997 return Effect.succeed(arr.slice(found) as Arr.NonEmptyArray<A>)
6998 })
6999 return Effect.suspend(() => dropping ? filtered : pull)
7000 })))
7001
7002/**
7003 * Drops elements while the filter succeeds.
7004 *
7005 * **When to use**
7006 *
7007 * Use when you need to remove a leading stream prefix based on a synchronous
7008 * `Filter` result while preserving the remaining original stream elements.
7009 *
7010 * **Details**
7011 *
7012 * `Result.succeed` drops the current element. The first `Result.fail` stops
7013 * dropping, emits that original element, and the rest of the source stream is
7014 * emitted without further filtering.
7015 *
7016 * @see {@link dropWhile} for boolean predicate prefix dropping
7017 * @see {@link takeWhileFilter} for keeping the accepted prefix as filter success values
7018 * @see {@link dropWhileEffect} for effectful predicate prefix dropping
7019 *
7020 * @category filtering
7021 * @since 4.0.0
7022 */
7023export const dropWhileFilter: {
7024 <A, B, X>(filter: Filter.Filter<NoInfer<A>, B, X>): <E, R>(self: Stream<A, E, R>) => Stream<A, E, R>
7025 <A, E, R, B, X>(self: Stream<A, E, R>, filter: Filter.Filter<NoInfer<A>, B, X>): Stream<A, E, R>
7026} = dual(2, <A, E, R, B, X>(
7027 self: Stream<A, E, R>,
7028 filter: Filter.Filter<NoInfer<A>, B, X>
7029): Stream<A, E, R> =>
7030 transformPull(self, (pull, _scope) =>
7031 Effect.sync(() => {
7032 let dropping = true
7033 const filtered: Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E> = Effect.flatMap(pull, (arr) => {
7034 const found = arr.findIndex((a) => Result.isFailure(filter(a)))
7035 if (found === -1) return filtered
7036 dropping = false
7037 return Effect.succeed(arr.slice(found) as Arr.NonEmptyArray<A>)
7038 })
7039 return Effect.suspend(() => dropping ? filtered : pull)
7040 })))
7041
7042/**
7043 * Drops elements while the specified effectful predicate evaluates to `true`.
7044 *
7045 * **Example** (Effectfully dropping while a predicate holds)
7046 *
7047 * ```ts import.meta.vitest
7048 * import { Effect, Stream } from "effect"
7049 *
7050 * const program = Effect.gen(function*() {
7051 * const result = yield* Stream.make(1, 2, 3, 4, 5).pipe(
7052 * Stream.dropWhileEffect((n) => Effect.succeed(n < 3)),
7053 * Stream.runCollect
7054 * )
7055 * result // => [ 3, 4, 5 ]
7056 * })
7057 *
7058 * await Effect.runPromise(program)
7059 * ```
7060 *
7061 * @category filtering
7062 * @since 2.0.0
7063 */
7064export const dropWhileEffect: {
7065 <A, E2, R2>(
7066 predicate: (a: NoInfer<A>, index: number) => Effect.Effect<boolean, E2, R2>
7067 ): <E, R>(self: Stream<A, E, R>) => Stream<A, E2 | E, R2 | R>
7068 <A, E, R, E2, R2>(
7069 self: Stream<A, E, R>,
7070 predicate: (a: A, index: number) => Effect.Effect<boolean, E2, R2>
7071 ): Stream<A, E | E2, R | R2>
7072} = dual(2, <A, E, R, E2, R2>(
7073 self: Stream<A, E, R>,
7074 predicate: (a: NoInfer<A>, index: number) => Effect.Effect<boolean, E2, R2>
7075): Stream<A, E | E2, R | R2> =>
7076 transformPull(self, (pull, _scope) =>
7077 Effect.sync(() => {
7078 let dropping = true
7079 let index = 0
7080 const filtered: Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E | E2, void, R2> = Effect.gen(function*() {
7081 while (true) {
7082 const arr = yield* pull
7083 for (let i = 0; i < arr.length; i++) {
7084 const drop = yield* predicate(arr[i], index++)
7085 if (drop) continue
7086 dropping = false
7087 return arr.slice(i) as Arr.NonEmptyArray<A>
7088 }
7089 }
7090 })
7091 return Effect.suspend((): Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E | E2, void, R | R2> =>
7092 dropping ? filtered : pull
7093 )
7094 })))
7095
7096/**
7097 * Drops the last specified number of elements from this stream.
7098 *
7099 * **Details**
7100 *
7101 * Keeps the last `n` elements in memory to drop them on completion. Finite
7102 * fractional values of `n` are rounded down. `NaN` and non-positive values
7103 * return the source stream unchanged.
7104 *
7105 * **Example** (Dropping values from the right)
7106 *
7107 * ```ts import.meta.vitest
7108 * import { Effect, Stream } from "effect"
7109 *
7110 * const program = Effect.gen(function*() {
7111 * const result = yield* Stream.make(1, 2, 3, 4, 5).pipe(
7112 * Stream.dropRight(2),
7113 * Stream.runCollect
7114 * )
7115 * result // => [ 1, 2, 3 ]
7116 * })
7117 *
7118 * await Effect.runPromise(program)
7119 * ```
7120 *
7121 * @category filtering
7122 * @since 2.0.0
7123 */
7124export const dropRight: {
7125 (n: number): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E, R>
7126 <A, E, R>(self: Stream<A, E, R>, n: number): Stream<A, E, R>
7127} = dual(
7128 2,
7129 <A, E, R>(self: Stream<A, E, R>, n: number): Stream<A, E, R> => {
7130 const count = Count.normalize(n)
7131 if (count === 0) return self
7132 return transformPull(self, (pull, _scope) =>
7133 Effect.sync(() => {
7134 const list = MutableList.make<A>()
7135 const emit: Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E> = Effect.flatMap(pull, (arr) => {
7136 MutableList.appendAllUnsafe(list, arr)
7137 const toTake = list.length - count
7138 const items = MutableList.takeN(list, toTake)
7139 return Arr.isArrayNonEmpty(items) ? Effect.succeed(items) : emit
7140 })
7141 return emit
7142 }))
7143 }
7144)
7145
7146/**
7147 * Exposes the underlying chunks as a stream of non-empty arrays.
7148 *
7149 * **Example** (Exposing stream chunks)
7150 *
7151 * ```ts import.meta.vitest
7152 * import { Effect, Stream } from "effect"
7153 *
7154 * const program = Effect.gen(function*() {
7155 * const chunks = yield* Stream.make(1, 2, 3, 4).pipe(
7156 * Stream.rechunk(2),
7157 * Stream.chunks,
7158 * Stream.runCollect
7159 * )
7160 * chunks // => [ [ 1, 2 ], [ 3, 4 ] ]
7161 * })
7162 *
7163 * await Effect.runPromise(program)
7164 * ```
7165 *
7166 * @category grouping
7167 * @since 2.0.0
7168 */
7169export const chunks = <A, E, R>(self: Stream<A, E, R>): Stream<Arr.NonEmptyReadonlyArray<A>, E, R> =>
7170 self.channel.pipe(
7171 Channel.map(Arr.of),
7172 fromChannel
7173 )
7174
7175/**
7176 * Groups the stream into arrays of the specified size, preserving element order.
7177 *
7178 * **Details**
7179 *
7180 * Finite fractional sizes are rounded down. `NaN` and non-positive sizes are
7181 * treated as `1`.
7182 *
7183 * **Example** (Rechunking stream elements)
7184 *
7185 * ```ts import.meta.vitest
7186 * import { Effect, Stream } from "effect"
7187 *
7188 * const program = Effect.gen(function*() {
7189 * const result = yield* Stream.make(1, 2, 3, 4, 5).pipe(
7190 * Stream.rechunk(2),
7191 * Stream.chunks,
7192 * Stream.runCollect
7193 * )
7194 * result // => [ [ 1, 2 ], [ 3, 4 ], [ 5 ] ]
7195 * })
7196 *
7197 * await Effect.runPromise(program)
7198 * ```
7199 *
7200 * @category grouping
7201 * @since 2.0.0
7202 */
7203export const rechunk: {
7204 (size: number): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E, R>
7205 <A, E, R>(self: Stream<A, E, R>, size: number): Stream<A, E, R>
7206} = dual(2, <A, E, R>(self: Stream<A, E, R>, target: number): Stream<A, E, R> => {
7207 target = Count.normalizeNonEmpty(target)
7208 return transformPull(self, (pull, _scope) =>
7209 Effect.sync(() => {
7210 let chunk = Arr.empty<A>() as Arr.NonEmptyArray<A>
7211 let index = 0
7212 let current: Arr.NonEmptyReadonlyArray<A> | undefined
7213 let done = false
7214
7215 return Effect.suspend(function loop(): Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E, void, R> {
7216 if (done) return Cause.done()
7217 else if (current === undefined) {
7218 return Effect.flatMap(pull, (arr) => {
7219 if (chunk.length === 0 && arr.length === target) {
7220 return Effect.succeed(arr)
7221 } else if (chunk.length + arr.length < target) {
7222 for (let i = 0; i < arr.length; i++) {
7223 chunk.push(arr[i])
7224 }
7225 return loop()
7226 }
7227 current = arr
7228 return loop()
7229 })
7230 }
7231 for (; index < current.length;) {
7232 chunk.push(current[index++])
7233 if (chunk.length === target) {
7234 const result = chunk
7235 chunk = [] as any
7236 return Effect.succeed(result)
7237 }
7238 }
7239 index = 0
7240 current = undefined
7241 return loop()
7242 }).pipe(
7243 Pull.catchDone(() => {
7244 if (chunk.length === 0) return Cause.done()
7245 const result = chunk
7246 done = true
7247 chunk = [] as any
7248 return Effect.succeed(result)
7249 })
7250 )
7251 }))
7252})
7253
7254/**
7255 * Emits a sliding window of `n` elements.
7256 *
7257 * **Details**
7258 *
7259 * Finite fractional window sizes are rounded down. `NaN` and non-positive
7260 * sizes are treated as `1`.
7261 *
7262 * **Example** (Emitting sliding windows)
7263 *
7264 * ```ts import.meta.vitest
7265 * import { Effect, pipe, Stream } from "effect"
7266 *
7267 * await Effect.runPromise(Effect.gen(function*() {
7268 * const result = yield* pipe(
7269 * Stream.make(1, 2, 3, 4, 5),
7270 * Stream.sliding(2),
7271 * Stream.runCollect
7272 * )
7273 * result // => [ [ 1, 2 ], [ 2, 3 ], [ 3, 4 ], [ 4, 5 ] ]
7274 * }))
7275 * ```
7276 *
7277 * @category grouping
7278 * @since 2.0.0
7279 */
7280export const sliding: {
7281 (chunkSize: number): <A, E, R>(self: Stream<A, E, R>) => Stream<Arr.NonEmptyReadonlyArray<A>, E, R>
7282 <A, E, R>(self: Stream<A, E, R>, chunkSize: number): Stream<Arr.NonEmptyReadonlyArray<A>, E, R>
7283} = dual(
7284 2,
7285 <A, E, R>(self: Stream<A, E, R>, chunkSize: number): Stream<Arr.NonEmptyReadonlyArray<A>, E, R> =>
7286 slidingSize(self, chunkSize, 1)
7287)
7288
7289/**
7290 * Emits sliding windows of `chunkSize` elements, advancing by `stepSize`.
7291 *
7292 * **Details**
7293 *
7294 * Finite fractional window and step sizes are rounded down. `NaN` and
7295 * non-positive sizes are treated as `1`.
7296 *
7297 * **Example** (Emitting sliding windows with a step size)
7298 *
7299 * ```ts import.meta.vitest
7300 * import { Effect, Stream } from "effect"
7301 *
7302 * const program = Effect.gen(function*() {
7303 * const chunks = yield* Stream.make(1, 2, 3, 4, 5).pipe(
7304 * Stream.slidingSize(3, 2),
7305 * Stream.runCollect
7306 * )
7307 * chunks // => [ [ 1, 2, 3 ], [ 3, 4, 5 ] ]
7308 * })
7309 *
7310 * await Effect.runPromise(program)
7311 * ```
7312 *
7313 * @category grouping
7314 * @since 2.0.0
7315 */
7316export const slidingSize: {
7317 (chunkSize: number, stepSize: number): <A, E, R>(self: Stream<A, E, R>) => Stream<Arr.NonEmptyReadonlyArray<A>, E, R>
7318 <A, E, R>(self: Stream<A, E, R>, chunkSize: number, stepSize: number): Stream<Arr.NonEmptyReadonlyArray<A>, E, R>
7319} = dual(
7320 3,
7321 <A, E, R>(self: Stream<A, E, R>, chunkSize: number, stepSize: number): Stream<Arr.NonEmptyReadonlyArray<A>, E, R> => {
7322 const windowSize = Count.normalizeNonEmpty(chunkSize)
7323 const step = Count.normalizeNonEmpty(stepSize)
7324 return transformPull(self, (upstream, _scope) =>
7325 Effect.sync(() => {
7326 let cause: Cause.Cause<E | Cause.Done> | null = null
7327 const list = MutableList.make<A>()
7328 let emitted = false
7329 let skip = 0
7330 const pull: Pull.Pull<
7331 Arr.NonEmptyReadonlyArray<Arr.NonEmptyReadonlyArray<A>>,
7332 E | Cause.Done
7333 > = Effect.matchCauseEffect(upstream, {
7334 onSuccess(arr) {
7335 MutableList.appendAllUnsafe(list, arr)
7336 if (skip > 0) {
7337 const length = list.length
7338 MutableList.takeNVoid(list, skip)
7339 skip = Math.max(0, skip - length)
7340 }
7341 if (list.length < windowSize) return pull
7342 emitted = true
7343 const chunks = [] as any as Arr.NonEmptyArray<Arr.NonEmptyReadonlyArray<A>>
7344 while (list.length >= windowSize) {
7345 if (windowSize === step) {
7346 chunks.push(MutableList.takeN(list, windowSize) as any)
7347 } else {
7348 chunks.push(MutableList.toArrayN(list, windowSize) as any)
7349 const length = list.length
7350 MutableList.takeNVoid(list, step)
7351 skip = Math.max(0, step - length)
7352 }
7353 }
7354 return Effect.succeed(chunks)
7355 },
7356 onFailure(cause_) {
7357 if (emitted) MutableList.takeNVoid(list, windowSize - step)
7358 if (list.length === 0) return Effect.failCause(cause_)
7359 cause = cause_
7360 return Effect.succeed(Arr.of(MutableList.takeAll(list) as any))
7361 }
7362 })
7363
7364 return Effect.suspend(() => cause ? Effect.failCause(cause) : pull)
7365 }))
7366 }
7367)
7368
7369/**
7370 * Splits the stream into non-empty groups whenever the predicate matches.
7371 *
7372 * **Details**
7373 *
7374 * Matching elements act as delimiters and are not included in the output.
7375 *
7376 * **Example** (Splitting on matching values)
7377 *
7378 * ```ts import.meta.vitest
7379 * import { Effect, Stream } from "effect"
7380 *
7381 * const program = Effect.gen(function*() {
7382 * const result = yield* Stream.range(0, 9).pipe(
7383 * Stream.split((n) => n % 4 === 0),
7384 * Stream.runCollect
7385 * )
7386 * result // => [ [ 1, 2, 3 ], [ 5, 6, 7 ], [ 9 ] ]
7387 * })
7388 *
7389 * await Effect.runPromise(program)
7390 * ```
7391 *
7392 * @category grouping
7393 * @since 2.0.0
7394 */
7395export const split: {
7396 <A, B extends A>(
7397 refinement: Refinement<NoInfer<A>, B>
7398 ): <E, R>(self: Stream<A, E, R>) => Stream<Arr.NonEmptyReadonlyArray<Exclude<A, B>>, E, R>
7399 <A>(predicate: Predicate<NoInfer<A>>): <E, R>(self: Stream<A, E, R>) => Stream<Arr.NonEmptyReadonlyArray<A>, E, R>
7400 <A, E, R, B extends A>(
7401 self: Stream<A, E, R>,
7402 refinement: Refinement<A, B>
7403 ): Stream<Arr.NonEmptyReadonlyArray<Exclude<A, B>>, E, R>
7404 <A, E, R>(self: Stream<A, E, R>, predicate: Predicate<A>): Stream<Arr.NonEmptyReadonlyArray<A>, E, R>
7405} = dual(2, <A, E, R>(
7406 self: Stream<A, E, R>,
7407 predicate: Predicate<NoInfer<A>>
7408): Stream<Arr.NonEmptyReadonlyArray<A>, E, R> =>
7409 mapAccumArray(self, Arr.empty<A>, (acc, arr) => {
7410 const out = Arr.empty<Arr.NonEmptyReadonlyArray<A>>()
7411 for (let i = 0; i < arr.length; i++) {
7412 if (predicate(arr[i])) {
7413 if (Arr.isArrayNonEmpty(acc)) {
7414 out.push(acc)
7415 acc = []
7416 }
7417 } else {
7418 acc.push(arr[i])
7419 }
7420 }
7421 return [acc, out]
7422 }, {
7423 onHalt(arr) {
7424 return Arr.isArrayNonEmpty(arr) ? Arr.of(arr) : emptyArr
7425 }
7426 }))
7427
7428/**
7429 * Combines elements from this stream and the specified stream by repeatedly
7430 * applying a stateful function that can pull from either side.
7431 *
7432 * **Details**
7433 *
7434 * Where possible, prefer `Stream.combineArray` for a more efficient
7435 * implementation.
7436 *
7437 * **Example** (Combining streams with state)
7438 *
7439 * ```ts import.meta.vitest
7440 * import { Effect, Stream } from "effect"
7441 *
7442 * const stream = Stream.combine(
7443 * Stream.make("A", "B", "C"),
7444 * Stream.make(1, 2, 3),
7445 * () => true,
7446 * (takeLeft, pullLeft, pullRight) =>
7447 * takeLeft
7448 * ? Effect.map(pullLeft, (value) => [`L:${value}`, false] as const)
7449 * : Effect.map(pullRight, (value) => [`R:${value}`, true] as const)
7450 * )
7451 *
7452 * const program = Effect.gen(function*() {
7453 * const output = yield* Stream.runCollect(stream)
7454 * output // => [ 'L:A', 'R:1', 'L:B', 'R:2', 'L:C', 'R:3' ]
7455 * })
7456 *
7457 * await Effect.runPromise(program)
7458 * ```
7459 *
7460 * @category merging
7461 * @since 2.0.0
7462 */
7463export const combine: {
7464 <A2, E2, R2, S, E, A, A3, E3, R3>(
7465 that: Stream<A2, E2, R2>,
7466 s: LazyArg<S>,
7467 f: (
7468 s: S,
7469 pullLeft: Pull.Pull<A, E, void>,
7470 pullRight: Pull.Pull<A2, E2, void>
7471 ) => Effect.Effect<readonly [A3, S], E3, R3>
7472 ): <R>(self: Stream<A, E, R>) => Stream<A3, E3, R2 | R3 | R>
7473 <A, E, R, A2, E2, R2, S, A3, E3, R3>(
7474 self: Stream<A, E, R>,
7475 that: Stream<A2, E2, R2>,
7476 s: LazyArg<S>,
7477 f: (
7478 s: S,
7479 pullLeft: Pull.Pull<A, E, void>,
7480 pullRight: Pull.Pull<A2, E2, void>
7481 ) => Effect.Effect<readonly [A3, S], E3, R3>
7482 ): Stream<A3, E3, R | R2 | R3>
7483} = dual(4, <A, E, R, A2, E2, R2, S, A3, E3, R3>(
7484 self: Stream<A, E, R>,
7485 that: Stream<A2, E2, R2>,
7486 s: LazyArg<S>,
7487 f: (
7488 s: S,
7489 pullLeft: Pull.Pull<A, E, void>,
7490 pullRight: Pull.Pull<A2, E2, void>
7491 ) => Effect.Effect<readonly [A3, S], E3, R3>
7492): Stream<A3, E3, R | R2 | R3> =>
7493 Channel.combine(
7494 Channel.flattenArray(self.channel),
7495 Channel.flattenArray(that.channel),
7496 s,
7497 f
7498 ).pipe(
7499 Channel.map(Arr.of),
7500 fromChannel
7501 ))
7502
7503/**
7504 * Combines two streams chunk-by-chunk with a stateful pull function.
7505 *
7506 * **When to use**
7507 *
7508 * Use to coordinate pulling chunks from two streams when each emitted chunk
7509 * depends on both sides and local state.
7510 *
7511 * **Details**
7512 *
7513 * The combining function receives the current state and pull functions for the
7514 * left and right streams. It returns the next non-empty chunk together with the
7515 * next state.
7516 *
7517 * **Example** (Combining stream chunks with state)
7518 *
7519 * ```ts import.meta.vitest
7520 * import { Effect, Stream } from "effect"
7521 *
7522 * const stream = Stream.make(1, 2).pipe(
7523 * Stream.combineArray(
7524 * Stream.make(10, 20),
7525 * () => true,
7526 * (useLeft, pullLeft, pullRight) =>
7527 * Effect.gen(function*() {
7528 * const array = useLeft ? yield* pullLeft : yield* pullRight
7529 * return [array, !useLeft] as const
7530 * })
7531 * )
7532 * )
7533 *
7534 * const program = Effect.gen(function*() {
7535 * const values = yield* Stream.runCollect(stream)
7536 * values // => [ 1, 2, 10, 20 ]
7537 * })
7538 *
7539 * await Effect.runPromise(program)
7540 * ```
7541 *
7542 * @category merging
7543 * @since 4.0.0
7544 */
7545export const combineArray: {
7546 <A2, E2, R2, S, E, A, A3, E3, R3>(
7547 that: Stream<A2, E2, R2>,
7548 s: LazyArg<S>,
7549 f: (
7550 s: S,
7551 pullLeft: Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E, void>,
7552 pullRight: Pull.Pull<Arr.NonEmptyReadonlyArray<A2>, E2, void>
7553 ) => Effect.Effect<readonly [Arr.NonEmptyReadonlyArray<A3>, S], E3, R3>
7554 ): <R>(self: Stream<A, E, R>) => Stream<A3, Pull.ExcludeDone<E3>, R2 | R3 | R>
7555 <R, A2, E2, R2, S, E, A, A3, E3, R3>(
7556 self: Stream<A, E, R>,
7557 that: Stream<A2, E2, R2>,
7558 s: LazyArg<S>,
7559 f: (
7560 s: S,
7561 pullLeft: Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E, void>,
7562 pullRight: Pull.Pull<Arr.NonEmptyReadonlyArray<A2>, E2, void>
7563 ) => Effect.Effect<readonly [Arr.NonEmptyReadonlyArray<A3>, S], E3, R3>
7564 ): Stream<A3, Pull.ExcludeDone<E3>, R | R2 | R3>
7565} = dual(4, <R, A2, E2, R2, S, E, A, A3, E3, R3>(
7566 self: Stream<A, E, R>,
7567 that: Stream<A2, E2, R2>,
7568 s: LazyArg<S>,
7569 f: (
7570 s: S,
7571 pullLeft: Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E, void>,
7572 pullRight: Pull.Pull<Arr.NonEmptyReadonlyArray<A2>, E2, void>
7573 ) => Effect.Effect<readonly [Arr.NonEmptyReadonlyArray<A3>, S], E3, R3>
7574): Stream<A3, Pull.ExcludeDone<E3>, R | R2 | R3> =>
7575 fromChannel(Channel.combine(
7576 self.channel,
7577 that.channel,
7578 s,
7579 f
7580 )))
7581
7582/**
7583 * Maps elements statefully, emitting zero or more outputs per input.
7584 *
7585 * **Example** (Statefully mapping stream values)
7586 *
7587 * ```ts import.meta.vitest
7588 * import { Console, Effect, Stream } from "effect"
7589 *
7590 * const program = Effect.gen(function*() {
7591 * const totals = yield* Stream.make(0, 1, 2, 3, 4, 5, 6).pipe(
7592 * Stream.mapAccum(() => 0, (total, n) => {
7593 * const next = total + n
7594 * return [next, [next]] as const
7595 * }),
7596 * Stream.runCollect
7597 * )
7598 *
7599 * totals // => [0, 1, 3, 6, 10, 15, 21]
7600 * })
7601 *
7602 * await Effect.runPromise(program)
7603 * ```
7604 *
7605 * @category mapping
7606 * @since 2.0.0
7607 */
7608export const mapAccum: {
7609 <S, A, B>(
7610 initial: LazyArg<S>,
7611 f: (s: S, a: A) => readonly [state: S, values: ReadonlyArray<B>],
7612 options?: {
7613 readonly onHalt?: ((state: S) => ReadonlyArray<B>) | undefined
7614 }
7615 ): <E, R>(self: Stream<A, E, R>) => Stream<B, E, R>
7616 <A, E, R, S, B>(
7617 self: Stream<A, E, R>,
7618 initial: LazyArg<S>,
7619 f: (s: S, a: A) => readonly [state: S, values: ReadonlyArray<B>],
7620 options?: {
7621 readonly onHalt?: ((state: S) => ReadonlyArray<B>) | undefined
7622 }
7623 ): Stream<B, E, R>
7624} = dual((args) => isStream(args[0]), <A, E, R, S, B>(
7625 self: Stream<A, E, R>,
7626 initial: LazyArg<S>,
7627 f: (s: S, a: A) => readonly [state: S, values: ReadonlyArray<B>],
7628 options?: {
7629 readonly onHalt?: ((state: S) => ReadonlyArray<B>) | undefined
7630 }
7631): Stream<B, E, R> =>
7632 fromChannel(Channel.mapAccum(
7633 self.channel,
7634 initial,
7635 (state, arr) => {
7636 const acc = Arr.empty<B>()
7637 for (let index = 0; index < arr.length; index++) {
7638 const [newState, values] = f(state, arr[index])
7639 state = newState
7640 acc.push(...values)
7641 }
7642 return [state, Arr.isArrayNonEmpty(acc) ? Arr.of(acc) : emptyArr]
7643 },
7644 options?.onHalt ?
7645 {
7646 onHalt(state) {
7647 const arr = options.onHalt!(state)
7648 return Arr.isReadonlyArrayNonEmpty(arr) ? Arr.of(arr) : emptyArr
7649 }
7650 } :
7651 undefined
7652 )))
7653
7654/**
7655 * Maps over non-empty chunk arrays statefully, emitting zero or more values per chunk.
7656 *
7657 * **Details**
7658 *
7659 * The mapping function runs once per chunk and the state is threaded across chunks.
7660 *
7661 * **Example** (Statefully mapping stream chunks)
7662 *
7663 * ```ts import.meta.vitest
7664 * import { Effect, Stream } from "effect"
7665 *
7666 * const program = Effect.gen(function*() {
7667 * const output = yield* Stream.make(1, 2, 3, 4, 5, 6).pipe(
7668 * Stream.rechunk(2),
7669 * Stream.mapAccumArray(() => 0, (sum: number, chunk) => {
7670 * const next = chunk.reduce((acc, n) => acc + n, sum)
7671 * return [next, [next]]
7672 * }),
7673 * Stream.runCollect
7674 * )
7675 * output // => [ 3, 10, 21 ]
7676 * })
7677 *
7678 * await Effect.runPromise(program)
7679 * ```
7680 *
7681 * @category mapping
7682 * @since 4.0.0
7683 */
7684export const mapAccumArray: {
7685 <S, A, B>(
7686 initial: LazyArg<S>,
7687 f: (s: S, a: Arr.NonEmptyReadonlyArray<A>) => readonly [state: S, values: ReadonlyArray<B>],
7688 options?: {
7689 readonly onHalt?: ((state: S) => ReadonlyArray<B>) | undefined
7690 }
7691 ): <E, R>(self: Stream<A, E, R>) => Stream<B, E, R>
7692 <A, E, R, S, B>(
7693 self: Stream<A, E, R>,
7694 initial: LazyArg<S>,
7695 f: (s: S, a: Arr.NonEmptyReadonlyArray<A>) => readonly [state: S, values: ReadonlyArray<B>],
7696 options?: {
7697 readonly onHalt?: ((state: S) => ReadonlyArray<B>) | undefined
7698 }
7699 ): Stream<B, E, R>
7700} = dual((args) => isStream(args[0]), <A, E, R, S, B>(
7701 self: Stream<A, E, R>,
7702 initial: LazyArg<S>,
7703 f: (s: S, a: Arr.NonEmptyReadonlyArray<A>) => readonly [state: S, values: ReadonlyArray<B>],
7704 options?: {
7705 readonly onHalt?: ((state: S) => ReadonlyArray<B>) | undefined
7706 }
7707): Stream<B, E, R> =>
7708 fromChannel(Channel.mapAccum(
7709 self.channel,
7710 initial,
7711 (state, arr) => {
7712 const [newState, values] = f(state, arr)
7713 state = newState
7714 return [state, Arr.isReadonlyArrayNonEmpty(values) ? Arr.of(values) : emptyArr]
7715 },
7716 options?.onHalt ?
7717 {
7718 onHalt(state) {
7719 const arr = options.onHalt!(state)
7720 return Arr.isReadonlyArrayNonEmpty(arr) ? Arr.of(arr) : emptyArr
7721 }
7722 } :
7723 undefined
7724 )))
7725
7726const emptyArr = Arr.empty<never>()
7727
7728/**
7729 * Maps each element statefully and effectfully, emitting zero or more output
7730 * values per input.
7731 *
7732 * **When to use**
7733 *
7734 * Use when stateful element mapping needs Effects or can fail while emitting
7735 * zero or more values per input element.
7736 *
7737 * **Details**
7738 *
7739 * The mapping effect receives the current state and element, then returns the
7740 * next state plus the values to emit. The state is threaded through the
7741 * stream.
7742 *
7743 * **Example** (Effectfully mapping stream values with state)
7744 *
7745 * ```ts import.meta.vitest
7746 * import { Effect, Stream } from "effect"
7747 *
7748 * const program = Effect.gen(function*() {
7749 * const result = yield* Stream.make(1, 1, 1).pipe(
7750 * Stream.mapAccumEffect(() => 0, (total, n) =>
7751 * Effect.succeed([total + n, [total + n]])
7752 * ),
7753 * Stream.runCollect
7754 * )
7755 *
7756 * result // => [ 1, 2, 3 ]
7757 * })
7758 *
7759 * await Effect.runPromise(program)
7760 * ```
7761 *
7762 * @category mapping
7763 * @since 2.0.0
7764 */
7765export const mapAccumEffect: {
7766 <S, A, B, E2, R2>(
7767 initial: LazyArg<S>,
7768 f: (s: S, a: A) => Effect.Effect<readonly [state: S, values: ReadonlyArray<B>], E2, R2>,
7769 options?: {
7770 readonly onHalt?: ((state: S) => ReadonlyArray<B>) | undefined
7771 }
7772 ): <E, R>(self: Stream<A, E, R>) => Stream<B, E | E2, R | R2>
7773 <A, E, R, S, B, E2, R2>(
7774 self: Stream<A, E, R>,
7775 initial: LazyArg<S>,
7776 f: (s: S, a: A) => Effect.Effect<readonly [state: S, values: ReadonlyArray<B>], E2, R2>,
7777 options?: {
7778 readonly onHalt?: ((state: S) => ReadonlyArray<B>) | undefined
7779 }
7780 ): Stream<B, E | E2, R | R2>
7781} = dual((args) => isStream(args[0]), <A, E, R, S, B, E2, R2>(
7782 self: Stream<A, E, R>,
7783 initial: LazyArg<S>,
7784 f: (s: S, a: A) => Effect.Effect<readonly [state: S, values: ReadonlyArray<B>], E2, R2>,
7785 options?: {
7786 readonly onHalt?: ((state: S) => ReadonlyArray<B>) | undefined
7787 }
7788): Stream<B, E | E2, R | R2> =>
7789 self.channel.pipe(
7790 Channel.flattenArray,
7791 Channel.mapAccum(
7792 initial,
7793 (state, a) =>
7794 Effect.map(
7795 f(state, a),
7796 ([state, values]) => [
7797 state,
7798 Arr.isReadonlyArrayNonEmpty(values) ? Arr.of(values) : Arr.empty<Arr.NonEmptyReadonlyArray<B>>()
7799 ]
7800 ),
7801 options?.onHalt ?
7802 {
7803 onHalt(state) {
7804 const arr = options.onHalt!(state)
7805 return Arr.isReadonlyArrayNonEmpty(arr) ? Arr.of(arr) : emptyArr
7806 }
7807 } :
7808 undefined
7809 ),
7810 fromChannel
7811 ))
7812
7813/**
7814 * Maps each non-empty input chunk statefully and effectfully, emitting zero or
7815 * more output values per chunk.
7816 *
7817 * **When to use**
7818 *
7819 * Use when stateful mapping should process each emitted non-empty chunk with an
7820 * Effect instead of each element separately.
7821 *
7822 * **Details**
7823 *
7824 * The mapping effect receives the current state and chunk, then returns the
7825 * next state plus the values to emit. The state is threaded across chunks.
7826 *
7827 * **Example** (Effectfully mapping stream chunks with state)
7828 *
7829 * ```ts import.meta.vitest
7830 * import { Effect, Stream } from "effect"
7831 *
7832 * const program = Effect.gen(function*() {
7833 * const totals = yield* Stream.make(1, 2, 3, 4).pipe(
7834 * Stream.rechunk(2),
7835 * Stream.mapAccumArrayEffect(() => 0, (total, chunk) =>
7836 * Effect.gen(function*() {
7837 * const next = chunk.reduce((sum, value) => sum + value, total)
7838 * return [next, [next]] as const
7839 * })
7840 * ),
7841 * Stream.runCollect
7842 * )
7843 * totals // => [ 3, 10 ]
7844 * })
7845 *
7846 * await Effect.runPromise(program)
7847 * ```
7848 *
7849 * @category mapping
7850 * @since 4.0.0
7851 */
7852export const mapAccumArrayEffect: {
7853 <S, A, B, E2, R2>(
7854 initial: LazyArg<S>,
7855 f: (s: S, a: Arr.NonEmptyReadonlyArray<A>) => Effect.Effect<readonly [state: S, values: ReadonlyArray<B>], E2, R2>,
7856 options?: {
7857 readonly onHalt?: ((state: S) => ReadonlyArray<B>) | undefined
7858 }
7859 ): <E, R>(self: Stream<A, E, R>) => Stream<B, E | E2, R | R2>
7860 <A, E, R, S, B, E2, R2>(
7861 self: Stream<A, E, R>,
7862 initial: LazyArg<S>,
7863 f: (s: S, a: Arr.NonEmptyReadonlyArray<A>) => Effect.Effect<readonly [state: S, values: ReadonlyArray<B>], E2, R2>,
7864 options?: {
7865 readonly onHalt?: ((state: S) => ReadonlyArray<B>) | undefined
7866 }
7867 ): Stream<B, E | E2, R | R2>
7868} = dual((args) => isStream(args[0]), <A, E, R, S, B, E2, R2>(
7869 self: Stream<A, E, R>,
7870 initial: LazyArg<S>,
7871 f: (s: S, a: Arr.NonEmptyReadonlyArray<A>) => Effect.Effect<readonly [state: S, values: ReadonlyArray<B>], E2, R2>,
7872 options?: {
7873 readonly onHalt?: ((state: S) => ReadonlyArray<B>) | undefined
7874 }
7875): Stream<B, E | E2, R | R2> =>
7876 self.channel.pipe(
7877 Channel.mapAccum(
7878 initial,
7879 (state, a) =>
7880 Effect.map(
7881 f(state, a),
7882 ([state, values]) => [
7883 state,
7884 Arr.isReadonlyArrayNonEmpty(values) ? Arr.of(values) : emptyArr
7885 ]
7886 ),
7887 options?.onHalt ?
7888 {
7889 onHalt(state) {
7890 const arr = options.onHalt!(state)
7891 return Arr.isReadonlyArrayNonEmpty(arr) ? Arr.of(arr) : emptyArr
7892 }
7893 } :
7894 undefined
7895 ),
7896 fromChannel
7897 ))
7898
7899/**
7900 * Accumulates state across the stream, emitting the initial state and each updated state.
7901 *
7902 * **Example** (Scanning stream state)
7903 *
7904 * ```ts import.meta.vitest
7905 * import { Effect, Stream } from "effect"
7906 *
7907 * const program = Effect.gen(function*() {
7908 * const values = yield* Stream.make(1, 2, 3).pipe(
7909 * Stream.scan(() => 0, (acc, n) => acc + n),
7910 * Stream.runCollect
7911 * )
7912 * values // => [ 0, 1, 3, 6 ]
7913 * })
7914 *
7915 * await Effect.runPromise(program)
7916 * ```
7917 *
7918 * @category accumulation
7919 * @since 2.0.0
7920 */
7921export const scan: {
7922 <S, A>(
7923 initial: LazyArg<S>,
7924 f: (s: S, a: A) => S
7925 ): <E, R>(self: Stream<A, E, R>) => Stream<S, E, R>
7926 <A, E, R, S>(
7927 self: Stream<A, E, R>,
7928 initial: LazyArg<S>,
7929 f: (s: S, a: A) => S
7930 ): Stream<S, E, R>
7931} = dual(3, <A, E, R, S>(
7932 self: Stream<A, E, R>,
7933 initial: LazyArg<S>,
7934 f: (s: S, a: A) => S
7935): Stream<S, E, R> =>
7936 suspend(() => {
7937 const seed = initial()
7938 return concat(
7939 succeed(seed),
7940 fromChannel(Channel.mapAccum(self.channel, () => seed, (state, arr) => {
7941 const states = Arr.empty<S>() as Arr.NonEmptyArray<S>
7942 for (let index = 0; index < arr.length; index++) {
7943 state = f(state, arr[index])
7944 states.push(state)
7945 }
7946 return [state, Arr.of(states)]
7947 }))
7948 )
7949 }))
7950
7951/**
7952 * Accumulates state effectfully and emits the initial state plus each accumulated state.
7953 *
7954 * **Example** (Effectfully scanning stream state)
7955 *
7956 * ```ts import.meta.vitest
7957 * import { Effect, Stream } from "effect"
7958 *
7959 * const program = Effect.gen(function*() {
7960 * const states = yield* Stream.make(1, 2, 3).pipe(
7961 * Stream.scanEffect(() => 0, (sum, n) => Effect.succeed(sum + n)),
7962 * Stream.runCollect
7963 * )
7964 * states // => [ 0, 1, 3, 6 ]
7965 * })
7966 * await Effect.runPromise(program)
7967 * ```
7968 *
7969 * @category accumulation
7970 * @since 2.0.0
7971 */
7972export const scanEffect: {
7973 <S, A, E2, R2>(
7974 initial: LazyArg<S>,
7975 f: (s: S, a: A) => Effect.Effect<S, E2, R2>
7976 ): <E, R>(self: Stream<A, E, R>) => Stream<S, E | E2, R | R2>
7977 <A, E, R, S, E2, R2>(
7978 self: Stream<A, E, R>,
7979 initial: LazyArg<S>,
7980 f: (s: S, a: A) => Effect.Effect<S, E2, R2>
7981 ): Stream<S, E | E2, R | R2>
7982} = dual(3, <A, E, R, S, E2, R2>(
7983 self: Stream<A, E, R>,
7984 initial: LazyArg<S>,
7985 f: (s: S, a: A) => Effect.Effect<S, E2, R2>
7986): Stream<S, E | E2, R | R2> =>
7987 suspend(() =>
7988 self.channel.pipe(
7989 Channel.flattenArray,
7990 Channel.scanEffect(initial(), f),
7991 Channel.map(Arr.of),
7992 fromChannel
7993 )
7994 ))
7995
7996/**
7997 * Drops earlier elements within the debounce window and emits only the latest element after the pause.
7998 *
7999 * **Example** (Debouncing stream elements)
8000 *
8001 * ```ts import.meta.vitest
8002 * import { Duration, Effect, Stream } from "effect"
8003 *
8004 * const stream = Stream.make(1, 2, 3).pipe(Stream.debounce(Duration.zero))
8005 *
8006 * const program = Effect.gen(function*() {
8007 * const values = yield* Stream.runCollect(stream)
8008 * values // => [ 3 ]
8009 * })
8010 * await Effect.runPromise(program)
8011 * ```
8012 *
8013 * @category rate limiting
8014 * @since 2.0.0
8015 */
8016export const debounce: {
8017 (duration: Duration.Input): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E, R>
8018 <A, E, R>(self: Stream<A, E, R>, duration: Duration.Input): Stream<A, E, R>
8019} = dual(
8020 2,
8021 <A, E, R>(self: Stream<A, E, R>, duration: Duration.Input): Stream<A, E, R> =>
8022 transformPull(
8023 self,
8024 Effect.fnUntraced(function*(pull, scope) {
8025 const clock = yield* Clock
8026 const durationMs = Duration.toMillis(Duration.fromInputUnsafe(duration))
8027 let lastArr: Arr.NonEmptyReadonlyArray<A> | undefined
8028 let cause: Cause.Cause<Cause.Done | E> | undefined
8029 let emitAtMs = Infinity
8030 const pullLatch = Latch.makeUnsafe()
8031 const emitLatch = Latch.makeUnsafe()
8032 const endLatch = Latch.makeUnsafe()
8033
8034 yield* pull.pipe(
8035 pullLatch.whenOpen,
8036 Effect.flatMap((arr) => {
8037 emitLatch.openUnsafe()
8038 lastArr = arr
8039 emitAtMs = clock.currentTimeMillisUnsafe() + durationMs
8040 return Effect.void
8041 }),
8042 Effect.forever({ disableYield: true }),
8043 Effect.onError((cause_) => {
8044 cause = cause_
8045 emitAtMs = clock.currentTimeMillisUnsafe()
8046 emitLatch.openUnsafe()
8047 endLatch.openUnsafe()
8048 return Effect.void
8049 }),
8050 Effect.forkIn(scope)
8051 )
8052
8053 const sleepLoop = Effect.suspend(function loop(): Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E, void, R> {
8054 const now = clock.currentTimeMillisUnsafe()
8055 const timeMs = emitAtMs < now ? durationMs : Math.min(durationMs, emitAtMs - now)
8056 return Effect.flatMap(Effect.raceFirst(Effect.sleep(timeMs), endLatch.await), () => {
8057 const now = clock.currentTimeMillisUnsafe()
8058 if (now < emitAtMs) {
8059 return loop()
8060 } else if (lastArr) {
8061 emitLatch.closeUnsafe()
8062 pullLatch.closeUnsafe()
8063 const eff = Effect.succeed(Arr.of(Arr.lastNonEmpty(lastArr)))
8064 lastArr = undefined
8065 return eff
8066 } else if (cause) {
8067 return Effect.failCause(cause!)
8068 }
8069 return loop()
8070 })
8071 })
8072
8073 return Effect.suspend(() => {
8074 if (cause) {
8075 if (lastArr) {
8076 const eff = Effect.succeed(Arr.of(Arr.lastNonEmpty(lastArr)))
8077 lastArr = undefined
8078 return eff
8079 }
8080 return Effect.failCause(cause)
8081 }
8082 pullLatch.openUnsafe()
8083 return emitLatch.whenOpen(sleepLoop)
8084 })
8085 })
8086 )
8087)
8088
8089/**
8090 * Rate-limits stream chunks with an effectful cost function.
8091 *
8092 * **When to use**
8093 *
8094 * Use to throttle chunks when computing each chunk's cost requires an effect.
8095 *
8096 * **Details**
8097 *
8098 * Uses a token bucket. The bucket can accumulate up to `units + burst` tokens,
8099 * and each chunk consumes the cost returned by the effectful `cost` function.
8100 *
8101 * If using the "enforce" strategy, arrays that do not meet the bandwidth
8102 * constraints are dropped. If using the "shape" strategy, arrays are delayed
8103 * until they can be emitted without exceeding the bandwidth constraints.
8104 *
8105 * Defaults to the "shape" strategy.
8106 *
8107 * **Example** (Throttling stream chunks effectfully)
8108 *
8109 * ```ts import.meta.vitest
8110 * import { Effect, Stream } from "effect"
8111 *
8112 * const stream = Stream.range(0, 5).pipe(
8113 * Stream.rechunk(1),
8114 * Stream.throttleEffect({
8115 * cost: (arr) => Effect.succeed(arr.length),
8116 * units: 1,
8117 * duration: 0,
8118 * strategy: "shape"
8119 * })
8120 * )
8121 *
8122 * await Effect.runPromise(Effect.gen(function*() {
8123 * const result = yield* Stream.runCollect(stream)
8124 * result // => [ 0, 1, 2, 3, 4, 5 ]
8125 * }))
8126 * ```
8127 *
8128 * @category rate limiting
8129 * @since 2.0.0
8130 */
8131export const throttleEffect: {
8132 <A, E2, R2>(options: {
8133 readonly cost: (arr: Arr.NonEmptyReadonlyArray<A>) => Effect.Effect<number, E2, R2>
8134 readonly units: number
8135 readonly duration: Duration.Input
8136 readonly burst?: number | undefined
8137 readonly strategy?: "enforce" | "shape" | undefined
8138 }): <E, R>(self: Stream<A, E, R>) => Stream<A, E2 | E, R2 | R>
8139 <A, E, R, E2, R2>(
8140 self: Stream<A, E, R>,
8141 options: {
8142 readonly cost: (arr: Arr.NonEmptyReadonlyArray<A>) => Effect.Effect<number, E2, R2>
8143 readonly units: number
8144 readonly duration: Duration.Input
8145 readonly burst?: number | undefined
8146 readonly strategy?: "enforce" | "shape" | undefined
8147 }
8148 ): Stream<A, E | E2, R | R2>
8149} = dual(
8150 2,
8151 <A, E, R, E2, R2>(
8152 self: Stream<A, E, R>,
8153 options: {
8154 readonly cost: (arr: Arr.NonEmptyReadonlyArray<A>) => Effect.Effect<number, E2, R2>
8155 readonly units: number
8156 readonly duration: Duration.Input
8157 readonly burst?: number | undefined
8158 readonly strategy?: "enforce" | "shape" | undefined
8159 }
8160 ): Stream<A, E | E2, R | R2> => {
8161 const burst = options.burst ?? 0
8162 if (options.strategy === "enforce") {
8163 return throttleEnforceEffect(self, options.cost, options.units, options.duration, burst)
8164 }
8165 return throttleShapeEffect(self, options.cost, options.units, options.duration, burst)
8166 }
8167)
8168
8169const throttleEnforceEffect = <A, E, R, E2, R2>(
8170 self: Stream<A, E, R>,
8171 cost: (arr: Arr.NonEmptyReadonlyArray<A>) => Effect.Effect<number, E2, R2>,
8172 units: number,
8173 duration: Duration.Input,
8174 burst: number
8175): Stream<A, E | E2, R | R2> =>
8176 transformPull(self, (pull) =>
8177 Effect.clockWith((clock) => {
8178 const durationMs = Duration.toMillis(Duration.fromInputUnsafe(duration))
8179 const max = units + burst < 0 ? Number.POSITIVE_INFINITY : units + burst
8180 let tokens = units
8181 let timestampMs = clock.currentTimeMillisUnsafe()
8182
8183 return Effect.succeed(
8184 Effect.flatMap(pull, function loop(arr): Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E | E2, void, R | R2> {
8185 return Effect.flatMap(cost(arr), (weight) => {
8186 const currentMs = clock.currentTimeMillisUnsafe()
8187 const elapsed = currentMs - timestampMs
8188 const cycles = elapsed / durationMs
8189 const sum = tokens + (cycles * units)
8190 const available = sum < 0 ? max : Math.min(sum, max)
8191
8192 if (weight <= available) {
8193 tokens = available - weight
8194 timestampMs = currentMs
8195 return Effect.succeed(arr)
8196 }
8197
8198 // Drop the array and continue
8199 return Effect.flatMap(pull, loop)
8200 })
8201 })
8202 )
8203 }))
8204
8205const throttleShapeEffect = <A, E, R, E2, R2>(
8206 self: Stream<A, E, R>,
8207 cost: (arr: Arr.NonEmptyReadonlyArray<A>) => Effect.Effect<number, E2, R2>,
8208 units: number,
8209 duration: Duration.Input,
8210 burst: number
8211): Stream<A, E | E2, R | R2> =>
8212 transformPull(self, (pull) =>
8213 Effect.clockWith((clock) => {
8214 const durationMs = Duration.toMillis(Duration.fromInputUnsafe(duration))
8215 const max = units + burst < 0 ? Number.POSITIVE_INFINITY : units + burst
8216 let tokens = units
8217 let timestampNanos = clock.monotonicTimeNanosUnsafe()
8218
8219 return Effect.succeed(Effect.flatMap(pull, (arr) =>
8220 Effect.flatMap(cost(arr), (weight) => {
8221 const currentNanos = clock.monotonicTimeNanosUnsafe()
8222 const elapsed = Number(currentNanos - timestampNanos) / 1_000_000
8223 const cycles = elapsed / durationMs
8224 const sum = tokens + (cycles * units)
8225 const available = Math.min(sum, max)
8226 const remaining = available - weight
8227
8228 if (remaining >= 0) {
8229 tokens = remaining
8230 timestampNanos = currentNanos
8231 return Effect.succeed(arr)
8232 }
8233
8234 const waitCycles = -remaining / units
8235 const delayMs = Math.max(0, waitCycles * durationMs)
8236
8237 if (delayMs > 0) {
8238 return Effect.flatMap(Effect.sleep(delayMs), () => {
8239 tokens = remaining
8240 timestampNanos = currentNanos
8241 return Effect.succeed(arr)
8242 })
8243 }
8244
8245 tokens = remaining
8246 timestampNanos = currentNanos
8247 return Effect.succeed(arr)
8248 })))
8249 }))
8250
8251/**
8252 * Rate-limits stream chunks with a synchronous cost function.
8253 *
8254 * **When to use**
8255 *
8256 * Use to throttle chunks when each chunk's cost can be computed synchronously.
8257 *
8258 * **Details**
8259 *
8260 * Uses a token bucket. The bucket can accumulate up to `units + burst` tokens,
8261 * and each chunk consumes the cost returned by `cost`.
8262 *
8263 * If using the "enforce" strategy, arrays that do not meet the bandwidth
8264 * constraints are dropped. If using the "shape" strategy, arrays are delayed
8265 * until they can be emitted without exceeding the bandwidth constraints.
8266 *
8267 * Defaults to the "shape" strategy.
8268 *
8269 * **Example** (Throttling stream chunks)
8270 *
8271 * ```ts import.meta.vitest
8272 * import { Effect, Stream } from "effect"
8273 *
8274 * const stream = Stream.range(0, 5).pipe(
8275 * Stream.rechunk(1),
8276 * Stream.throttle({
8277 * cost: (arr) => arr.length,
8278 * units: 1,
8279 * duration: 0,
8280 * strategy: "shape"
8281 * })
8282 * )
8283 *
8284 * const program = Effect.gen(function*() {
8285 * const values = yield* Stream.runCollect(stream)
8286 * values // => [ 0, 1, 2, 3, 4, 5 ]
8287 * })
8288 * await Effect.runPromise(program)
8289 * ```
8290 *
8291 * @category rate limiting
8292 * @since 2.0.0
8293 */
8294export const throttle: {
8295 <A>(options: {
8296 readonly cost: (arr: Arr.NonEmptyReadonlyArray<A>) => number
8297 readonly units: number
8298 readonly duration: Duration.Input
8299 readonly burst?: number | undefined
8300 readonly strategy?: "enforce" | "shape" | undefined
8301 }): <E, R>(self: Stream<A, E, R>) => Stream<A, E, R>
8302 <A, E, R>(
8303 self: Stream<A, E, R>,
8304 options: {
8305 readonly cost: (arr: Arr.NonEmptyReadonlyArray<A>) => number
8306 readonly units: number
8307 readonly duration: Duration.Input
8308 readonly burst?: number | undefined
8309 readonly strategy?: "enforce" | "shape" | undefined
8310 }
8311 ): Stream<A, E, R>
8312} = dual(
8313 2,
8314 <A, E, R>(
8315 self: Stream<A, E, R>,
8316 options: {
8317 readonly cost: (arr: Arr.NonEmptyReadonlyArray<A>) => number
8318 readonly units: number
8319 readonly duration: Duration.Input
8320 readonly burst?: number | undefined
8321 readonly strategy?: "enforce" | "shape" | undefined
8322 }
8323 ): Stream<A, E, R> =>
8324 throttleEffect(self, {
8325 ...options,
8326 cost: (arr) => Effect.succeed(options.cost(arr))
8327 })
8328)
8329
8330/**
8331 * Partitions the stream into non-empty arrays of the specified size.
8332 *
8333 * **Details**
8334 *
8335 * The final array may be smaller if there are not enough elements to fill it.
8336 * Finite fractional sizes are rounded down. `NaN` and non-positive sizes are
8337 * treated as `1`.
8338 *
8339 * **Example** (Grouping elements by size)
8340 *
8341 * ```ts import.meta.vitest
8342 * import { Effect, Stream } from "effect"
8343 *
8344 * const program = Effect.gen(function*() {
8345 * const grouped = yield* Stream.range(1, 8).pipe(
8346 * Stream.grouped(3),
8347 * Stream.runCollect
8348 * )
8349 * grouped // => [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8 ] ]
8350 * })
8351 *
8352 * await Effect.runPromise(program)
8353 * ```
8354 *
8355 * @category grouping
8356 * @since 2.0.0
8357 */
8358export const grouped: {
8359 (n: number): <A, E, R>(self: Stream<A, E, R>) => Stream<Arr.NonEmptyReadonlyArray<A>, E, R>
8360 <A, E, R>(self: Stream<A, E, R>, n: number): Stream<Arr.NonEmptyReadonlyArray<A>, E, R>
8361} = dual(
8362 2,
8363 <A, E, R>(self: Stream<A, E, R>, n: number): Stream<Arr.NonEmptyReadonlyArray<A>, E, R> => chunks(rechunk(self, n))
8364)
8365
8366/**
8367 * Partitions the stream into arrays, emitting when the chunk size is reached
8368 * or the duration passes.
8369 *
8370 * **Details**
8371 *
8372 * The duration starts when the first element of a group arrives, so an idle
8373 * stream does not wake up. Finite fractional chunk sizes are rounded down.
8374 * `NaN` and non-positive sizes are treated as `1`.
8375 *
8376 * **Example** (Grouping elements by size or time)
8377 *
8378 * ```ts import.meta.vitest
8379 * import { Effect, Stream } from "effect"
8380 *
8381 * const program = Effect.gen(function*() {
8382 * const values = yield* Stream.make(1, 2, 3).pipe(
8383 * Stream.groupedWithin(2, "5 seconds"),
8384 * Stream.runCollect
8385 * )
8386 * values // => [ [ 1, 2 ], [ 3 ] ]
8387 * })
8388 *
8389 * await Effect.runPromise(program)
8390 * ```
8391 *
8392 * @category grouping
8393 * @since 2.0.0
8394 */
8395export const groupedWithin: {
8396 (
8397 chunkSize: number,
8398 duration: Duration.Input
8399 ): <A, E, R>(self: Stream<A, E, R>) => Stream<Array<A>, E, R>
8400 <A, E, R>(self: Stream<A, E, R>, chunkSize: number, duration: Duration.Input): Stream<Array<A>, E, R>
8401} = dual(3, <A, E, R>(
8402 self: Stream<A, E, R>,
8403 chunkSize: number,
8404 duration: Duration.Input
8405): Stream<Array<A>, E, R> =>
8406 aggregateWithin(
8407 self,
8408 Sink.take(Count.normalizeNonEmpty(chunkSize)),
8409 Schedule.spaced(duration)
8410 ))
8411
8412/**
8413 * Groups elements into keyed substreams using an effectful classifier.
8414 *
8415 * **Example** (Grouping elements into keyed substreams using an effectful classifier)
8416 *
8417 * ```ts import.meta.vitest
8418 * import { Effect, Stream } from "effect"
8419 *
8420 * const program = Effect.gen(function*() {
8421 * const grouped = yield* Stream.make(1, 2, 3, 4, 5).pipe(
8422 * Stream.groupBy((n) =>
8423 * Effect.succeed([n % 2 === 0 ? "even" : "odd", n] as const)
8424 * ),
8425 * Stream.mapEffect(
8426 * Effect.fnUntraced(function*([key, stream]) {
8427 * return [key, yield* Stream.runCollect(stream)] as const
8428 * }),
8429 * { concurrency: "unbounded" }
8430 * ),
8431 * Stream.runCollect
8432 * )
8433 *
8434 * grouped // => [ [ 'odd', [ 1, 3, 5 ] ], [ 'even', [ 2, 4 ] ] ]
8435 * })
8436 *
8437 * await Effect.runPromise(program)
8438 * ```
8439 *
8440 * @category grouping
8441 * @since 2.0.0
8442 */
8443export const groupBy: {
8444 <A, K, V, E2, R2>(
8445 f: (a: NoInfer<A>) => Effect.Effect<readonly [K, V], E2, R2>,
8446 options?: {
8447 readonly bufferSize?: number | undefined
8448 readonly idleTimeToLive?: Duration.Input | undefined
8449 }
8450 ): <E, R>(self: Stream<A, E, R>) => Stream<readonly [K, Stream<V>], E | E2, R | R2>
8451 <A, E, R, K, V, E2, R2>(
8452 self: Stream<A, E, R>,
8453 f: (a: NoInfer<A>) => Effect.Effect<readonly [K, V], E2, R2>,
8454 options?: {
8455 readonly bufferSize?: number | undefined
8456 readonly idleTimeToLive?: Duration.Input | undefined
8457 }
8458 ): Stream<readonly [K, Stream<V>], E | E2, R | R2>
8459} = dual((args) => isStream(args[0]), <A, E, R, K, V, E2, R2>(
8460 self: Stream<A, E, R>,
8461 f: (a: NoInfer<A>) => Effect.Effect<readonly [K, V], E2, R2>,
8462 options?: {
8463 readonly bufferSize?: number | undefined
8464 readonly idleTimeToLive?: Duration.Input | undefined
8465 }
8466): Stream<readonly [K, Stream<V>], E | E2, R | R2> =>
8467 groupByImpl(
8468 self,
8469 Effect.fnUntraced(function*(arr, queues, queueMap) {
8470 for (let i = 0; i < arr.length; i++) {
8471 const [key, value] = yield* f(arr[i])
8472 const oentry = MutableHashMap.get(queueMap, key)
8473 const queue = Option.isSome(oentry)
8474 ? oentry.value
8475 : yield* Effect.scoped(RcMap.get(queues, key))
8476 yield* RcMap.touch(queues, key)
8477 yield* Queue.offer(queue, value)
8478 }
8479 }),
8480 options
8481 ))
8482
8483/**
8484 * Groups elements by a key and emits a stream per key.
8485 *
8486 * **Example** (Grouping elements by key)
8487 *
8488 * ```ts import.meta.vitest
8489 * import { Effect, Stream } from "effect"
8490 *
8491 * const program = Effect.gen(function*() {
8492 * const grouped = yield* Stream.make(1, 2, 3, 4, 5).pipe(
8493 * Stream.groupByKey((n) => n % 2 === 0 ? "even" : "odd"),
8494 * Stream.mapEffect(
8495 * ([key, stream]) =>
8496 * Stream.runCollect(stream).pipe(
8497 * Effect.map((values) => [key, values] as const)
8498 * ),
8499 * { concurrency: "unbounded" }
8500 * ),
8501 * Stream.runCollect
8502 * )
8503 * grouped // => [ [ 'odd', [ 1, 3, 5 ] ], [ 'even', [ 2, 4 ] ] ]
8504 * })
8505 *
8506 * await Effect.runPromise(program)
8507 * ```
8508 *
8509 * @category grouping
8510 * @since 2.0.0
8511 */
8512export const groupByKey: {
8513 <A, K>(
8514 f: (a: NoInfer<A>) => K,
8515 options?: {
8516 readonly bufferSize?: number | undefined
8517 readonly idleTimeToLive?: Duration.Input | undefined
8518 }
8519 ): <E, R>(self: Stream<A, E, R>) => Stream<readonly [K, Stream<A>], E, R>
8520 <A, E, R, K>(
8521 self: Stream<A, E, R>,
8522 f: (a: NoInfer<A>) => K,
8523 options?: {
8524 readonly bufferSize?: number | undefined
8525 readonly idleTimeToLive?: Duration.Input | undefined
8526 }
8527 ): Stream<readonly [K, Stream<A>], E, R>
8528} = dual((args) => isStream(args[0]), <A, E, R, K>(
8529 self: Stream<A, E, R>,
8530 f: (a: NoInfer<A>) => K,
8531 options?: {
8532 readonly bufferSize?: number | undefined
8533 readonly idleTimeToLive?: Duration.Input | undefined
8534 }
8535): Stream<readonly [K, Stream<A>], E, R> =>
8536 suspend(() => {
8537 const batch = MutableHashMap.empty<K, Arr.NonEmptyArray<A>>()
8538 return groupByImpl(
8539 self,
8540 Effect.fnUntraced(function*(arr, queues, queueMap) {
8541 for (let i = 0; i < arr.length; i++) {
8542 const key = f(arr[i])
8543 const ovalues = MutableHashMap.get(batch, key)
8544 if (Option.isNone(ovalues)) {
8545 MutableHashMap.set(batch, key, [arr[i]])
8546 } else {
8547 ovalues.value.push(arr[i])
8548 }
8549 }
8550 for (const [key, values] of batch) {
8551 const oentry = MutableHashMap.get(queueMap, key)
8552 const queue = Option.isSome(oentry)
8553 ? oentry.value
8554 : yield* Effect.scoped(RcMap.get(queues, key))
8555 yield* RcMap.touch(queues, key)
8556 yield* Queue.offerAll(queue, values)
8557 }
8558 MutableHashMap.clear(batch)
8559 }),
8560 options
8561 )
8562 }))
8563
8564const groupByImpl = <A, E, R, K, V, E2, R2>(
8565 self: Stream<A, E, R>,
8566 f: (
8567 arr: Arr.NonEmptyReadonlyArray<A>,
8568 queues: RcMap.RcMap<K, Queue.Queue<V, Cause.Done>>,
8569 queueMap: MutableHashMap.MutableHashMap<K, Queue.Queue<V, Cause.Done>>
8570 ) => Effect.Effect<void, E2, R2>,
8571 options?: {
8572 readonly bufferSize?: number | undefined
8573 readonly idleTimeToLive?: Duration.Input | undefined
8574 }
8575): Stream<readonly [K, Stream<V>], E | E2, R | R2> =>
8576 transformPullBracket(
8577 self,
8578 Effect.fnUntraced(function*(pull, scope, forkedScope) {
8579 const out = yield* Queue.unbounded<readonly [K, Stream<V>], E | E2 | Cause.Done<void>>()
8580 yield* Scope.addFinalizer(scope, Queue.shutdown(out))
8581
8582 const queueMap = MutableHashMap.empty<K, Queue.Queue<V, Cause.Done>>()
8583 const queues = yield* RcMap.make({
8584 lookup: (key: K) =>
8585 Effect.acquireRelease(
8586 Queue.make<V, Cause.Done>({ capacity: options?.bufferSize ?? 4096 }).pipe(
8587 Effect.tap((queue) => {
8588 MutableHashMap.set(queueMap, key, queue)
8589 return Queue.offer(out, [key, fromQueue(queue)])
8590 })
8591 ),
8592 (queue) => {
8593 MutableHashMap.remove(queueMap, key)
8594 return Queue.end(queue)
8595 }
8596 ),
8597 idleTimeToLive: options?.idleTimeToLive ?? Duration.infinity
8598 }).pipe(Scope.provide(forkedScope))
8599
8600 yield* Effect.whileLoop({
8601 while: constTrue,
8602 body: constant(Effect.flatMap(pull, (arr) => f(arr, queues, queueMap))),
8603 step: constVoid
8604 }).pipe(
8605 Effect.catchCause((cause) => Queue.failCause(out, cause)),
8606 Effect.forkIn(scope)
8607 )
8608
8609 return Queue.takeAll(out)
8610 })
8611 )
8612
8613/**
8614 * Groups consecutive elements that have equal keys into non-empty arrays.
8615 *
8616 * **When to use**
8617 *
8618 * Use when you already have a stream ordered by the grouping key and want to
8619 * emit each consecutive run as a non-empty array while keeping later
8620 * non-adjacent runs separate.
8621 *
8622 * **Details**
8623 *
8624 * The key is computed with `f`; adjacent elements whose keys are equal by
8625 * `Equal.equals` are emitted as one `[key, group]`. Later non-adjacent runs
8626 * with the same key are emitted separately.
8627 *
8628 * @see {@link groupByKey} for grouping all elements with the same key across the stream
8629 * @see {@link groupBy} for custom grouped stream construction
8630 *
8631 * @category grouping
8632 * @since 2.0.0
8633 */
8634export const groupAdjacentBy: {
8635 <A, K>(
8636 f: (a: NoInfer<A>) => K
8637 ): <E, R>(self: Stream<A, E, R>) => Stream<readonly [K, Arr.NonEmptyArray<A>], E, R>
8638 <A, E, R, K>(
8639 self: Stream<A, E, R>,
8640 f: (a: NoInfer<A>) => K
8641 ): Stream<readonly [K, Arr.NonEmptyArray<A>], E, R>
8642} = dual(2, <A, E, R, K>(
8643 self: Stream<A, E, R>,
8644 f: (a: NoInfer<A>) => K
8645): Stream<readonly [K, Arr.NonEmptyArray<A>], E, R> =>
8646 transformPull(self, (pull, _scope) =>
8647 Effect.sync(() => {
8648 let currentKey: K = undefined as any
8649 let group: Arr.NonEmptyArray<A> | undefined
8650 let toEmit = Arr.empty<readonly [K, Arr.NonEmptyArray<A>]>()
8651 const loop: Pull.Pull<
8652 Arr.NonEmptyReadonlyArray<readonly [K, Arr.NonEmptyArray<A>]>,
8653 E
8654 > = pull.pipe(
8655 Effect.flatMap((chunk) => {
8656 for (let i = 0; i < chunk.length; i++) {
8657 const item = chunk[i]
8658 const key = f(item)
8659 if (group === undefined) {
8660 currentKey = key
8661 group = [item]
8662 continue
8663 } else if (Equal.equals(key, currentKey)) {
8664 group.push(item)
8665 continue
8666 }
8667 toEmit.push([currentKey, group])
8668 currentKey = key
8669 group = [item]
8670 }
8671 if (Arr.isArrayNonEmpty(toEmit)) {
8672 const out = toEmit
8673 toEmit = []
8674 return Effect.succeed(out)
8675 }
8676 return loop
8677 })
8678 )
8679 let done = false
8680 return Pull.catchDone(Effect.suspend(() => done ? Cause.done() : loop), () => {
8681 done = true
8682 const out = group
8683 group = undefined
8684 return out && Arr.isArrayNonEmpty(out) ? Effect.succeed(Arr.of([currentKey, out])) : Cause.done()
8685 })
8686 })))
8687
8688/**
8689 * Applies a sink transducer to the stream and emits each sink result.
8690 *
8691 * **Example** (Transducing with a sink)
8692 *
8693 * ```ts import.meta.vitest
8694 * import { Effect, Sink, Stream } from "effect"
8695 *
8696 * const program = Effect.gen(function* () {
8697 * const result = yield* Stream.make(1, 2, 3, 4).pipe(
8698 * Stream.transduce(Sink.take(2)),
8699 * Stream.runCollect
8700 * )
8701 *
8702 * result // => [ [ 1, 2 ], [ 3, 4 ], [] ]
8703 * })
8704 * await Effect.runPromise(program)
8705 * ```
8706 *
8707 * @category aggregation
8708 * @since 2.0.0
8709 */
8710export const transduce: {
8711 <A2, A, E2, R2>(
8712 sink: Sink.Sink<A2, A, A, E2, R2>
8713 ): <E, R>(self: Stream<A, E, R>) => Stream<A2, E2 | E, R2 | R>
8714 <A, E, R, A2, E2, R2>(
8715 self: Stream<A, E, R>,
8716 sink: Sink.Sink<A2, A, A, E2, R2>
8717 ): Stream<A2, E2 | E, R2 | R>
8718} = dual(
8719 2,
8720 <A, E, R, A2, E2, R2>(
8721 self: Stream<A, E, R>,
8722 sink: Sink.Sink<A2, A, A, E2, R2>
8723 ): Stream<A2, E2 | E, R2 | R> =>
8724 transformPull(self, (upstream, scope) =>
8725 Effect.sync(() => {
8726 let done: Exit.Exit<never, Cause.Done<void> | E> | undefined
8727 let leftover: Arr.NonEmptyReadonlyArray<A> | undefined
8728 const upstreamWithLeftover = Effect.suspend(() => {
8729 if (leftover !== undefined) {
8730 const chunk = leftover
8731 leftover = undefined
8732 return Effect.succeed(chunk)
8733 }
8734 return upstream
8735 }).pipe(
8736 Effect.catch((error) => {
8737 done = Exit.fail(error)
8738 return Cause.done()
8739 })
8740 )
8741 const pull = Effect.map(
8742 Effect.suspend(() => sink.transform(upstreamWithLeftover, scope)),
8743 ([value, leftover_]) => {
8744 leftover = leftover_
8745 return Arr.of(value)
8746 }
8747 )
8748 return Effect.suspend((): Pull.Pull<
8749 Arr.NonEmptyReadonlyArray<A2>,
8750 E | E2,
8751 void,
8752 R2
8753 > => done ? done : pull)
8754 }))
8755)
8756
8757/**
8758 * Aggregates elements using the provided sink and emits each sink result as a stream element.
8759 *
8760 * **Details**
8761 *
8762 * The stream runs the upstream and downstream in separate fibers, so the sink can keep
8763 * consuming input while downstream is busy processing the previous output.
8764 *
8765 * **Example** (Aggregating with a sink)
8766 *
8767 * ```ts import.meta.vitest
8768 * import { Effect, Sink, Stream } from "effect"
8769 *
8770 * await Effect.runPromise(Effect.gen(function* () {
8771 * const aggregated = yield* Stream.runCollect(
8772 * Stream.make(1, 2, 3, 4, 5, 6).pipe(
8773 * Stream.aggregate(
8774 * Sink.foldUntil(() => 0, 3, (sum, n) => Effect.succeed(sum + n))
8775 * )
8776 * )
8777 * )
8778 * aggregated // => [ 6, 15 ]
8779 * }))
8780 * ```
8781 *
8782 * @category aggregation
8783 * @since 2.0.0
8784 */
8785export const aggregate: {
8786 <B, A, A2, E2, R2>(
8787 sink: Sink.Sink<B, A | A2, A2, E2, R2>
8788 ): <E, R>(self: Stream<A, E, R>) => Stream<B, E2 | E, R2 | R>
8789 <A, E, R, B, A2, E2, R2>(
8790 self: Stream<A, E, R>,
8791 sink: Sink.Sink<B, A | A2, A2, E2, R2>
8792 ): Stream<B, E | E2, R | R2>
8793} = dual(2, <A, E, R, B, A2, E2, R2>(
8794 self: Stream<A, E, R>,
8795 sink: Sink.Sink<B, A | A2, A2, E2, R2>
8796): Stream<B, E | E2, R | R2> => aggregateWithin(self, sink, Schedule.forever))
8797
8798/**
8799 * Aggregates elements with a sink, emitting each result when the sink completes or the schedule triggers.
8800 *
8801 * **Details**
8802 *
8803 * The schedule can flush the current aggregation even if the sink has not finished.
8804 * It is stepped at most once per aggregation, when the aggregation receives its
8805 * first element, with the previous aggregation's output as input. An aggregation
8806 * the sink completes immediately, such as one built from leftovers, may finish
8807 * without stepping the schedule.
8808 *
8809 * Time between aggregations is hidden from the schedule, so an idle stream does
8810 * not run it. Time-based schedules such as `Schedule.fixed` and
8811 * `Schedule.upTo({ duration })` only count time spent aggregating; use
8812 * `Schedule.spaced` for a flush timer.
8813 *
8814 * When the schedule ends, the stream emits the current aggregation and any sink
8815 * leftovers, then ends without pulling more from upstream.
8816 *
8817 * **Example** (Aggregating with a sink and schedule)
8818 *
8819 * ```ts import.meta.vitest
8820 * import { Effect, Schedule, Sink, Stream } from "effect"
8821 *
8822 * await Effect.runPromise(Effect.gen(function* () {
8823 * const aggregated = yield* Stream.runCollect(
8824 * Stream.make(1, 2, 3, 4, 5, 6).pipe(
8825 * Stream.aggregateWithin(
8826 * Sink.foldUntil(() => 0, 3, (sum, n) => Effect.succeed(sum + n)),
8827 * Schedule.forever
8828 * )
8829 * )
8830 * )
8831 * aggregated // => [ 6, 15 ]
8832 * }))
8833 * ```
8834 *
8835 * @category aggregation
8836 * @since 2.0.0
8837 */
8838export const aggregateWithin: {
8839 <B, A, A2, E2, R2, C, E3, R3>(
8840 sink: Sink.Sink<B, A | A2, A2, E2, R2>,
8841 schedule: Schedule.Schedule<C, Option.Option<B>, E3, R3>
8842 ): <E, R>(self: Stream<A, E, R>) => Stream<B, E2 | E | E3, R2 | R3 | R>
8843 <A, E, R, B, A2, E2, R2, C, E3, R3>(
8844 self: Stream<A, E, R>,
8845 sink: Sink.Sink<B, A | A2, A2, E2, R2>,
8846 schedule: Schedule.Schedule<C, Option.Option<B>, E3, R3>
8847 ): Stream<B, E | E2 | E3, R | R2 | R3>
8848} = dual(3, <A, E, R, B, A2, E2, R2, C, E3, R3>(
8849 self: Stream<A, E, R>,
8850 sink: Sink.Sink<B, A | A2, A2, E2, R2>,
8851 schedule: Schedule.Schedule<C, Option.Option<B>, E3, R3>
8852): Stream<B, E | E2 | E3, R | R2 | R3> =>
8853 fromChannel(Channel.fromTransformBracket(Effect.fnUntraced(function*(_upstream, _, scope) {
8854 const pull = yield* Channel.toPullScoped(self.channel, _)
8855
8856 const pullLatch = Latch.makeUnsafe(false)
8857 const scheduleStep = Symbol()
8858 const buffer = yield* Queue.make<Arr.NonEmptyReadonlyArray<A> | typeof scheduleStep, E | Cause.Done<void>>({
8859 capacity: 0
8860 })
8861
8862 yield* pull.pipe(
8863 pullLatch.whenOpen,
8864 Effect.flatMap((arr) => {
8865 pullLatch.closeUnsafe()
8866 return Queue.offer(buffer, arr)
8867 }),
8868 Effect.forever, // Keep autoYield enabled so the schedule can run.
8869 Effect.catchCause((cause) => Queue.failCause(buffer, cause)),
8870 Effect.forkIn(scope)
8871 )
8872
8873 // The schedule clock excludes time between aggregations.
8874 const clock = yield* Clock
8875 const step = yield* Schedule.toStep(schedule)
8876 const hasInput = Latch.makeUnsafe(false)
8877 let scheduleTime = clock.currentTimeMillisUnsafe()
8878 let openedAt = 0
8879 let lastOutput = Option.none<B>()
8880 let leftover: Arr.NonEmptyReadonlyArray<A2> | undefined
8881 const onInput = <X>(chunk: X): Effect.Effect<X> => {
8882 if (hasInput.openUnsafe()) {
8883 openedAt = clock.currentTimeMillisUnsafe()
8884 }
8885 return Effect.succeed(chunk)
8886 }
8887 const stepToBuffer: Pull.Pull<never, E3, void, R3> = hasInput.await.pipe(
8888 Effect.flatMap(() => step(scheduleTime, lastOutput)),
8889 Effect.flatMap(([, delay]) => Effect.sleep(delay)),
8890 Effect.flatMap(() => Queue.offer(buffer, scheduleStep)),
8891 // End input so the sink drains leftovers without further upstream pulls.
8892 Pull.catchDone(() => Queue.end(buffer)),
8893 Effect.flatMap(() => Effect.never)
8894 )
8895
8896 const sinkUpstream = Effect.suspend((): Pull.Pull<Arr.NonEmptyReadonlyArray<A | A2>, E> => {
8897 if (leftover !== undefined) {
8898 const chunk = leftover
8899 leftover = undefined
8900 return onInput(chunk)
8901 }
8902 if (buffer.state._tag === "Open") {
8903 pullLatch.openUnsafe()
8904 }
8905 return Effect.flatMap(Queue.take(buffer), (arr) => arr === scheduleStep ? Cause.done() : onInput(arr))
8906 })
8907 const emit = Effect.flatMap(([value, leftover_]: Sink.End<B, A2>) => {
8908 if (hasInput.isOpen()) {
8909 scheduleTime += clock.currentTimeMillisUnsafe() - openedAt
8910 } else if (buffer.state._tag === "Done") {
8911 return Cause.done()
8912 }
8913 lastOutput = Option.some(value)
8914 leftover = leftover_
8915 return Effect.succeed(Arr.of(value))
8916 })
8917
8918 return Effect.suspend((): Pull.Pull<Arr.NonEmptyReadonlyArray<B>, E | E2 | E3, void, R2 | R3> => {
8919 if (buffer.state._tag === "Done" && leftover === undefined) {
8920 return buffer.state.exit as Exit.Exit<never, Cause.Done<void> | E>
8921 }
8922 hasInput.closeUnsafe()
8923 const pull = emit(Effect.suspend(() => sink.transform(sinkUpstream as any, scope)))
8924 return buffer.state._tag === "Open" ? Effect.raceFirst(pull, stepToBuffer) : pull
8925 })
8926 }))))
8927
8928/**
8929 * Creates a fixed-size tuple of streams that each emit
8930 * the same elements as the source stream.
8931 *
8932 * **Details**
8933 *
8934 * The source stream starts after all downstream streams have been subscribed.
8935 * With the default suspend strategy, the source can only advance `capacity`
8936 * chunks ahead of the slowest downstream stream. If a downstream stream is
8937 * interrupted, it unsubscribes from the broadcast so it no longer contributes
8938 * backpressure. Finite fractional values of `n` are rounded down. `NaN` and
8939 * non-positive values create no downstream streams.
8940 *
8941 * **Example** (Broadcasting to two consumers)
8942 *
8943 * ```ts import.meta.vitest
8944 * import { Effect, Stream } from "effect"
8945 *
8946 * const program = Effect.scoped(
8947 * Effect.gen(function*() {
8948 * const [left, right] = yield* Stream.make(1, 2, 3).pipe(
8949 * Stream.broadcastN({ n: 2, capacity: 8 })
8950 * )
8951 *
8952 * const values = yield* Effect.all([
8953 * Stream.runCollect(left),
8954 * Stream.runCollect(right)
8955 * ], { concurrency: "unbounded" })
8956 *
8957 * values // => [ [ 1, 2, 3 ], [ 1, 2, 3 ] ]
8958 * })
8959 * )
8960 *
8961 * await Effect.runPromise(program)
8962 * ```
8963 *
8964 * @category broadcasting
8965 * @since 4.0.0
8966 */
8967export const broadcastN: {
8968 <const N extends number>(
8969 options: {
8970 readonly n: N
8971 readonly capacity: "unbounded"
8972 readonly replay?: number | undefined
8973 } | {
8974 readonly n: N
8975 readonly capacity: number
8976 readonly strategy?: "sliding" | "dropping" | "suspend" | undefined
8977 readonly replay?: number | undefined
8978 }
8979 ): <A, E, R>(self: Stream<A, E, R>) => Effect.Effect<TupleOf<N, Stream<A, E>>, never, Scope.Scope | R>
8980 <A, E, R, const N extends number>(
8981 self: Stream<A, E, R>,
8982 options: {
8983 readonly n: N
8984 readonly capacity: "unbounded"
8985 readonly replay?: number | undefined
8986 } | {
8987 readonly capacity: number
8988 readonly n: N
8989 readonly strategy?: "sliding" | "dropping" | "suspend" | undefined
8990 readonly replay?: number | undefined
8991 }
8992 ): Effect.Effect<TupleOf<N, Stream<A, E>>, never, Scope.Scope | R>
8993} = dual(
8994 2,
8995 Effect.fnUntraced(function*<A, E, R, const N extends number>(
8996 self: Stream<A, E, R>,
8997 options: {
8998 readonly n: N
8999 readonly capacity: "unbounded"
9000 readonly replay?: number | undefined
9001 } | {
9002 readonly n: N
9003 readonly capacity: number
9004 readonly strategy?: "sliding" | "dropping" | "suspend" | undefined
9005 readonly replay?: number | undefined
9006 }
9007 ) {
9008 const n = Count.normalize(options.n)
9009 const pubsub = yield* makePubSub<Take.Take<A, E>>(options)
9010 const streams = new Array(n)
9011 const parentScope = yield* Scope.Scope
9012 for (let i = 0; i < n; i++) {
9013 const scope = Scope.forkUnsafe(parentScope)
9014 const subscription = yield* PubSub.subscribe(pubsub).pipe(
9015 Effect.provideService(Scope.Scope, scope)
9016 )
9017 streams[i] = Channel.fromEffectTake(PubSub.take(subscription)).pipe(
9018 Channel.onExit((exit) => Scope.close(scope, exit)),
9019 fromChannel
9020 )
9021 }
9022 yield* Channel.runForEach(self.channel, (value) => PubSub.publish(pubsub, value)).pipe(
9023 Effect.onExit((exit) => PubSub.end(pubsub, exit)),
9024 Effect.forkScoped
9025 )
9026 return streams as TupleOf<N, Stream<A, E>>
9027 })
9028)
9029
9030const makePubSub = <A>(
9031 options: {
9032 readonly capacity: "unbounded"
9033 readonly replay?: number | undefined
9034 } | {
9035 readonly capacity: number
9036 readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
9037 readonly replay?: number | undefined
9038 }
9039) =>
9040 Effect.acquireRelease(
9041 options.capacity === "unbounded"
9042 ? PubSub.unbounded<A>(options)
9043 : options.strategy === "dropping"
9044 ? PubSub.dropping<A>(options)
9045 : options.strategy === "sliding"
9046 ? PubSub.sliding<A>(options)
9047 : PubSub.bounded<A>(options),
9048 PubSub.shutdown
9049 )
9050
9051/**
9052 * Creates a PubSub-backed stream that multicasts the source to all subscribers.
9053 *
9054 * **Details**
9055 *
9056 * The returned stream is scoped and uses the provided PubSub capacity and replay settings.
9057 *
9058 * **Example** (Broadcasting a stream)
9059 *
9060 * ```ts import.meta.vitest
9061 * import { Effect, Stream } from "effect"
9062 *
9063 * const program = Effect.scoped(
9064 * Effect.gen(function* () {
9065 * const broadcasted = yield* Stream.broadcast(Stream.fromArray([1, 2, 3]), {
9066 * capacity: 8,
9067 * replay: 3
9068 * })
9069 *
9070 * const [left, right] = yield* Effect.all([
9071 * Stream.runCollect(broadcasted),
9072 * Stream.runCollect(broadcasted)
9073 * ], { concurrency: "unbounded" })
9074 *
9075 * const result = [left, right] // => [[1, 2, 3], [1, 2, 3]]
9076 * })
9077 * )
9078 *
9079 * await Effect.runPromise(program)
9080 * ```
9081 *
9082 * @category broadcasting
9083 * @since 2.0.0
9084 */
9085export const broadcast: {
9086 (
9087 options: {
9088 readonly capacity: "unbounded"
9089 readonly replay?: number | undefined
9090 } | {
9091 readonly capacity: number
9092 readonly strategy?: "sliding" | "dropping" | "suspend" | undefined
9093 readonly replay?: number | undefined
9094 }
9095 ): <A, E, R>(self: Stream<A, E, R>) => Effect.Effect<Stream<A, E>, never, Scope.Scope | R>
9096 <A, E, R>(
9097 self: Stream<A, E, R>,
9098 options: {
9099 readonly capacity: "unbounded"
9100 readonly replay?: number | undefined
9101 } | {
9102 readonly capacity: number
9103 readonly strategy?: "sliding" | "dropping" | "suspend" | undefined
9104 readonly replay?: number | undefined
9105 }
9106 ): Effect.Effect<Stream<A, E>, never, Scope.Scope | R>
9107} = dual(2, <A, E, R>(
9108 self: Stream<A, E, R>,
9109 options: {
9110 readonly capacity: "unbounded"
9111 readonly replay?: number | undefined
9112 } | {
9113 readonly capacity: number
9114 readonly strategy?: "sliding" | "dropping" | "suspend" | undefined
9115 readonly replay?: number | undefined
9116 }
9117): Effect.Effect<Stream<A, E>, never, Scope.Scope | R> => Effect.map(toPubSubTake(self, options), fromPubSubTake))
9118
9119/**
9120 * Returns a new Stream that multicasts the original stream, subscribing when the first consumer starts.
9121 *
9122 * **Details**
9123 *
9124 * The upstream continues running while there is at least one consumer and is finalized after the last one exits.
9125 * If `idleTimeToLive` is set, the upstream is kept alive for that duration so a later subscriber can continue from
9126 * the next element instead of restarting.
9127 *
9128 * **Example** (Sharing a stream)
9129 *
9130 * ```ts import.meta.vitest
9131 * import { Deferred, Effect, Ref, Stream } from "effect"
9132 *
9133 * const result = await Effect.runPromise(
9134 * Effect.scoped(
9135 * Effect.gen(function*() {
9136 * const firstReady = yield* Deferred.make<void>()
9137 * const secondReady = yield* Deferred.make<void>()
9138 * const acquisitions = yield* Ref.make(0)
9139 * const source = Stream.fromEffect(Ref.update(acquisitions, (n) => n + 1)).pipe(
9140 * Stream.drain,
9141 * Stream.concat(Stream.make(0)),
9142 * Stream.concat(
9143 * Stream.fromEffect(Effect.all([Deferred.await(firstReady), Deferred.await(secondReady)])).pipe(Stream.drain)
9144 * ),
9145 * Stream.concat(Stream.make(1, 2, 3))
9146 * )
9147 * const shared = yield* Stream.share(source, { capacity: 16, replay: 1 })
9148 * const consume = (ready: Deferred.Deferred<void>) =>
9149 * shared.pipe(
9150 * Stream.tap((value) => value === 0 ? Deferred.succeed(ready, void 0) : Effect.void),
9151 * Stream.filter((value) => value !== 0),
9152 * Stream.runCollect
9153 * )
9154 *
9155 * const values = yield* Effect.all([consume(firstReady), consume(secondReady)], { concurrency: "unbounded" })
9156 * return { values, acquisitions: yield* Ref.get(acquisitions) }
9157 * })
9158 * )
9159 * )
9160 * result // => { values: [[1, 2, 3], [1, 2, 3]], acquisitions: 1 }
9161 * ```
9162 *
9163 * @category broadcasting
9164 * @since 3.8.0
9165 */
9166export const share: {
9167 (
9168 options: {
9169 readonly capacity: "unbounded"
9170 readonly replay?: number | undefined
9171 readonly idleTimeToLive?: Duration.Input | undefined
9172 } | {
9173 readonly capacity: number
9174 readonly strategy?: "sliding" | "dropping" | "suspend" | undefined
9175 readonly replay?: number | undefined
9176 readonly idleTimeToLive?: Duration.Input | undefined
9177 }
9178 ): <A, E, R>(self: Stream<A, E, R>) => Effect.Effect<Stream<A, E>, never, Scope.Scope | R>
9179 <A, E, R>(
9180 self: Stream<A, E, R>,
9181 options: {
9182 readonly capacity: "unbounded"
9183 readonly replay?: number | undefined
9184 readonly idleTimeToLive?: Duration.Input | undefined
9185 } | {
9186 readonly capacity: number
9187 readonly strategy?: "sliding" | "dropping" | "suspend" | undefined
9188 readonly replay?: number | undefined
9189 readonly idleTimeToLive?: Duration.Input | undefined
9190 }
9191 ): Effect.Effect<Stream<A, E>, never, Scope.Scope | R>
9192} = dual(2, <A, E, R>(
9193 self: Stream<A, E, R>,
9194 options: {
9195 readonly capacity: "unbounded"
9196 readonly replay?: number | undefined
9197 readonly idleTimeToLive?: Duration.Input | undefined
9198 } | {
9199 readonly capacity: number
9200 readonly strategy?: "sliding" | "dropping" | "suspend" | undefined
9201 readonly replay?: number | undefined
9202 readonly idleTimeToLive?: Duration.Input | undefined
9203 }
9204): Effect.Effect<Stream<A, E>, never, Scope.Scope | R> =>
9205 Effect.map(
9206 RcRef.make({
9207 acquire: broadcast(self, options),
9208 idleTimeToLive: options.idleTimeToLive
9209 }),
9210 (ref) => unwrap(RcRef.get(ref))
9211 ))
9212
9213/**
9214 * Pipes this stream through a channel that consumes and emits chunked elements.
9215 *
9216 * **Details**
9217 *
9218 * The channel receives `NonEmptyReadonlyArray` chunks and can transform both the
9219 * output elements and error type.
9220 *
9221 * **Example** (Piping through a channel)
9222 *
9223 * ```ts import.meta.vitest
9224 * import { Array, Channel, Effect, Stream } from "effect"
9225 *
9226 * type NumberChunk = readonly [number, ...Array<number>]
9227 *
9228 * const doubleChunks = Channel.identity<NumberChunk, never, unknown>().pipe(
9229 * Channel.map((chunk) => Array.map(chunk, (n) => n * 2))
9230 * )
9231 *
9232 * const program = Effect.gen(function*() {
9233 * const result = yield* Stream.fromArray([1, 2, 3]).pipe(
9234 * Stream.rechunk(2),
9235 * Stream.pipeThroughChannel(doubleChunks),
9236 * Stream.runCollect
9237 * )
9238 * result // => [ 2, 4, 6 ]
9239 * })
9240 *
9241 * await Effect.runPromise(program)
9242 * ```
9243 *
9244 * @category sequencing
9245 * @since 2.0.0
9246 */
9247export const pipeThroughChannel: {
9248 <R2, E, E2, A, A2>(
9249 channel: Channel.Channel<Arr.NonEmptyReadonlyArray<A2>, E2, unknown, Arr.NonEmptyReadonlyArray<A>, E, unknown, R2>
9250 ): <R>(self: Stream<A, E, R>) => Stream<A2, E2, R2 | R>
9251 <R, R2, E, E2, A, A2>(
9252 self: Stream<A, E, R>,
9253 channel: Channel.Channel<Arr.NonEmptyReadonlyArray<A2>, E2, unknown, Arr.NonEmptyReadonlyArray<A>, E, unknown, R2>
9254 ): Stream<A2, E2, R | R2>
9255} = dual(2, <R, R2, E, E2, A, A2>(
9256 self: Stream<A, E, R>,
9257 channel: Channel.Channel<Arr.NonEmptyReadonlyArray<A2>, E2, unknown, Arr.NonEmptyReadonlyArray<A>, E, unknown, R2>
9258): Stream<A2, E2, R | R2> => fromChannel(Channel.pipeTo(self.channel, channel)))
9259
9260/**
9261 * Pipes values through the provided channel while preserving this stream's
9262 * failures alongside any channel failures.
9263 *
9264 * **Details**
9265 *
9266 * Upstream failures are not passed to the channel, so the resulting stream can
9267 * fail with either the original stream error or the channel error.
9268 *
9269 * **Example** (Piping through a channel with failures)
9270 *
9271 * ```ts import.meta.vitest
9272 * import { Array, Channel, Effect, Stream } from "effect"
9273 *
9274 * type NumberChunk = readonly [number, ...Array<number>]
9275 *
9276 * const stringifyChunks = Channel.identity<NumberChunk, "StreamError", unknown>().pipe(
9277 * Channel.map((chunk) => Array.map(chunk, String))
9278 * )
9279 *
9280 * await Effect.runPromise(Effect.gen(function*() {
9281 * const result = yield* Stream.make(1, 2, 3).pipe(
9282 * Stream.rechunk(2),
9283 * Stream.pipeThroughChannelOrFail(stringifyChunks),
9284 * Stream.runCollect
9285 * )
9286 *
9287 * result // => ["1", "2", "3"]
9288 * }))
9289 * ```
9290 *
9291 * @category sequencing
9292 * @since 2.0.0
9293 */
9294export const pipeThroughChannelOrFail: {
9295 <R2, E, E2, A, A2>(
9296 channel: Channel.Channel<Arr.NonEmptyReadonlyArray<A2>, E2, unknown, Arr.NonEmptyReadonlyArray<A>, E, unknown, R2>
9297 ): <R>(self: Stream<A, E, R>) => Stream<A2, E | E2, R2 | R>
9298 <R, R2, E, E2, A, A2>(
9299 self: Stream<A, E, R>,
9300 channel: Channel.Channel<Arr.NonEmptyReadonlyArray<A2>, E2, unknown, Arr.NonEmptyReadonlyArray<A>, E, unknown, R2>
9301 ): Stream<A2, E | E2, R | R2>
9302} = dual(2, <R, R2, E, E2, A, A2>(
9303 self: Stream<A, E, R>,
9304 channel: Channel.Channel<Arr.NonEmptyReadonlyArray<A2>, E2, unknown, Arr.NonEmptyReadonlyArray<A>, E, unknown, R2>
9305): Stream<A2, E | E2, R | R2> => fromChannel(Channel.pipeToOrFail(self.channel, channel)))
9306
9307/**
9308 * Pipes the stream through `Sink.toChannel`, emitting only the sink leftovers.
9309 *
9310 * **Details**
9311 *
9312 * If the sink completes mid-chunk, the remaining elements become the output stream.
9313 *
9314 * **Example** (Piping through a sink)
9315 *
9316 * ```ts import.meta.vitest
9317 * import { Effect, Sink, Stream } from "effect"
9318 *
9319 * const program = Effect.gen(function*() {
9320 * const leftovers = yield* Stream.make(1, 2, 3, 4).pipe(
9321 * Stream.pipeThrough(Sink.take(2)),
9322 * Stream.runCollect
9323 * )
9324 *
9325 * leftovers // => [ 3, 4 ]
9326 * })
9327 *
9328 * await Effect.runPromise(program)
9329 * ```
9330 *
9331 * @category sequencing
9332 * @since 2.0.0
9333 */
9334export const pipeThrough: {
9335 <A2, A, L, E2, R2>(sink: Sink.Sink<A2, A, L, E2, R2>): <E, R>(self: Stream<A, E, R>) => Stream<L, E2 | E, R2 | R>
9336 <A, E, R, A2, L, E2, R2>(self: Stream<A, E, R>, sink: Sink.Sink<A2, A, L, E2, R2>): Stream<L, E | E2, R | R2>
9337} = dual(
9338 2,
9339 <A, E, R, A2, L, E2, R2>(self: Stream<A, E, R>, sink: Sink.Sink<A2, A, L, E2, R2>): Stream<L, E | E2, R | R2> =>
9340 self.channel.pipe(
9341 Channel.pipeToOrFail(Sink.toChannel(sink)),
9342 Channel.concatWith(([_, leftover]) => leftover ? Channel.succeed(leftover) : Channel.empty),
9343 fromChannel
9344 )
9345)
9346
9347/**
9348 * Collects all elements into an array and emits it as a single element.
9349 *
9350 * **Example** (Collecting values into a stream element)
9351 *
9352 * ```ts import.meta.vitest
9353 * import { Effect, Stream } from "effect"
9354 *
9355 * const stream = Stream.make(1, 2, 3)
9356 *
9357 * const program = Effect.gen(function*() {
9358 * const collected = yield* stream.pipe(Stream.collect, Stream.runCollect)
9359 * collected[0] // => [ 1, 2, 3 ]
9360 * })
9361 *
9362 * await Effect.runPromise(program)
9363 * ```
9364 *
9365 * @category accumulation
9366 * @since 4.0.0
9367 */
9368export const collect = <A, E, R>(self: Stream<A, E, R>): Stream<Array<A>, E, R> => fromEffect(runCollect(self))
9369
9370/**
9371 * Accumulates elements into a growing array, emitting the cumulative array for each input chunk.
9372 *
9373 * **Example** (Accumulating stream elements)
9374 *
9375 * ```ts import.meta.vitest
9376 * import { Effect, Stream } from "effect"
9377 *
9378 * const program = Effect.gen(function*() {
9379 * const accumulated = yield* Stream.runCollect(
9380 * Stream.fromArray([1, 2, 3]).pipe(
9381 * Stream.rechunk(1),
9382 * Stream.accumulate
9383 * )
9384 * )
9385 * accumulated // => [ [ 1 ], [ 1, 2 ], [ 1, 2, 3 ] ]
9386 * })
9387 *
9388 * await Effect.runPromise(program)
9389 * ```
9390 *
9391 * @category accumulation
9392 * @since 2.0.0
9393 */
9394export const accumulate = <A, E, R>(self: Stream<A, E, R>): Stream<Arr.NonEmptyArray<A>, E, R> =>
9395 mapAccumArray(self, Arr.empty<A>, (acc, as) => {
9396 const combined = Arr.appendAll(acc, as)
9397 return [combined, [combined]]
9398 })
9399
9400/**
9401 * Emits only elements that differ from the previous one.
9402 *
9403 * **Example** (Emitting changed values)
9404 *
9405 * ```ts import.meta.vitest
9406 * import { Effect, Stream } from "effect"
9407 *
9408 * const program = Effect.gen(function*() {
9409 * const values = yield* Stream.fromIterable([1, 1, 2, 2, 3]).pipe(
9410 * Stream.changes,
9411 * Stream.runCollect
9412 * )
9413 *
9414 * values // => [ 1, 2, 3 ]
9415 * })
9416 *
9417 * await Effect.runPromise(program)
9418 * ```
9419 *
9420 * @category deduplication
9421 * @since 2.0.0
9422 */
9423export const changes = <A, E, R>(self: Stream<A, E, R>): Stream<A, E, R> => changesWith(self, Equal.equals)
9424
9425/**
9426 * Returns a stream that only emits elements that are not equal to the previously emitted element, as determined by the specified predicate.
9427 *
9428 * **Example** (Emitting values that changed by equivalence)
9429 *
9430 * ```ts import.meta.vitest
9431 * import { Effect, Stream } from "effect"
9432 *
9433 * const stream = Stream.make("A", "a", "B", "b", "b").pipe(
9434 * Stream.changesWith((left, right) => left.toLowerCase() === right.toLowerCase())
9435 * )
9436 *
9437 * await Effect.runPromise(
9438 * Effect.gen(function*() {
9439 * const values = yield* Stream.runCollect(stream)
9440 * values // => [ 'A', 'B' ]
9441 * })
9442 * )
9443 * ```
9444 *
9445 * @category deduplication
9446 * @since 2.0.0
9447 */
9448export const changesWith: {
9449 <A>(f: (x: A, y: A) => boolean): <E, R>(self: Stream<A, E, R>) => Stream<A, E, R>
9450 <A, E, R>(self: Stream<A, E, R>, f: (x: A, y: A) => boolean): Stream<A, E, R>
9451} = dual(
9452 2,
9453 <A, E, R>(self: Stream<A, E, R>, f: (x: A, y: A) => boolean): Stream<A, E, R> =>
9454 transformPull(self, (pull, _scope) =>
9455 Effect.sync(() => {
9456 let first = true
9457 let last: A
9458 return Effect.flatMap(pull, function loop(arr): Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E> {
9459 const out: Array<A> = []
9460 let i = 0
9461 if (first) {
9462 first = false
9463 last = arr[0]
9464 i = 1
9465 out.push(last)
9466 }
9467 for (; i < arr.length; i++) {
9468 const a = arr[i]
9469 if (f(a, last)) continue
9470 last = a
9471 out.push(a)
9472 }
9473 return Arr.isArrayNonEmpty(out) ? Effect.succeed(out) : Effect.flatMap(pull, loop)
9474 })
9475 }))
9476)
9477
9478/**
9479 * Emits only elements that differ from the previous element, using an effectful equality check.
9480 *
9481 * **Details**
9482 *
9483 * The predicate runs for each element after the first; returning `true` treats it as equal and skips it.
9484 *
9485 * **Example** (Effectfully emitting changed values)
9486 *
9487 * ```ts import.meta.vitest
9488 * import { Effect, Stream } from "effect"
9489 *
9490 * const program = Effect.gen(function*() {
9491 * const stream = Stream.make(1, 1, 2, 2, 3, 3).pipe(
9492 * Stream.changesWithEffect((a, b) => Effect.succeed(a === b))
9493 * )
9494 * const result = yield* Stream.runCollect(stream)
9495 * result // => [ 1, 2, 3 ]
9496 * })
9497 *
9498 * await Effect.runPromise(program)
9499 * ```
9500 *
9501 * @category deduplication
9502 * @since 2.0.0
9503 */
9504export const changesWithEffect: {
9505 <A, E2, R2>(
9506 f: (x: A, y: A) => Effect.Effect<boolean, E2, R2>
9507 ): <E, R>(self: Stream<A, E, R>) => Stream<A, E | E2, R | R2>
9508 <A, E, R, E2, R2>(
9509 self: Stream<A, E, R>,
9510 f: (x: A, y: A) => Effect.Effect<boolean, E2, R2>
9511 ): Stream<A, E | E2, R | R2>
9512} = dual(
9513 2,
9514 <A, E, R, E2, R2>(
9515 self: Stream<A, E, R>,
9516 f: (x: A, y: A) => Effect.Effect<boolean, E2, R2>
9517 ): Stream<A, E | E2, R | R2> =>
9518 transformPull(self, (pull, _scope) =>
9519 Effect.sync(() => {
9520 let first = true
9521 let last: A
9522 return Effect.flatMap(
9523 pull,
9524 Effect.fnUntraced(function* loop(arr): Generator<
9525 Pull.Pull<any, E | E2, void, R2>,
9526 Arr.NonEmptyReadonlyArray<A>,
9527 any
9528 > {
9529 const out: Array<A> = []
9530 let i = 0
9531 if (first) {
9532 first = false
9533 last = arr[0]
9534 i = 1
9535 out.push(last)
9536 }
9537 for (; i < arr.length; i++) {
9538 const a = arr[i]
9539 if (yield* f(a, last)) continue
9540 last = a
9541 out.push(a)
9542 }
9543 return Arr.isArrayNonEmpty(out) ? out : yield* Effect.flatMap(pull, Effect.fnUntraced(loop))
9544 })
9545 )
9546 }))
9547)
9548
9549/**
9550 * Decodes Uint8Array chunks into strings using TextDecoder with an optional encoding.
9551 *
9552 * **Example** (Decoding Uint8Array chunks into strings using TextDecoder with an optional encoding)
9553 *
9554 * ```ts import.meta.vitest
9555 * import { Effect, Stream } from "effect"
9556 *
9557 * const encoder = new TextEncoder()
9558 * const stream = Stream.make(
9559 * encoder.encode("Hello"),
9560 * encoder.encode(" World")
9561 * )
9562 *
9563 * const program = Effect.gen(function*() {
9564 * const decoded = yield* stream.pipe(
9565 * Stream.decodeText,
9566 * Stream.runCollect
9567 * )
9568 * decoded // => [ 'Hello', ' World' ]
9569 * })
9570 *
9571 * await Effect.runPromise(program)
9572 * ```
9573 *
9574 * @category text
9575 * @since 2.0.0
9576 */
9577export const decodeText: <
9578 Arg extends Stream<Uint8Array, any, any> | {
9579 readonly encoding?: string | undefined
9580 } | undefined = {
9581 readonly encoding?: string | undefined
9582 }
9583>(
9584 streamOrOptions?: Arg,
9585 options?: {
9586 readonly encoding?: string | undefined
9587 } | undefined
9588) => [Arg] extends [Stream<Uint8Array, infer _E, infer _R>] ? Stream<string, _E, _R>
9589 : <E, R>(self: Stream<Uint8Array, E, R>) => Stream<string, E, R> = dual(
9590 (args) => isStream(args[0]),
9591 <E, R>(self: Stream<Uint8Array, E, R>, options?: {
9592 readonly encoding?: string | undefined
9593 }): Stream<string, E, R> =>
9594 suspend(() => {
9595 const decoder = new TextDecoder(options?.encoding)
9596 return map(self, (chunk) => decoder.decode(chunk, { stream: true }))
9597 })
9598 )
9599
9600/**
9601 * Encodes a stream of strings into UTF-8 `Uint8Array` chunks.
9602 *
9603 * **Example** (Encoding a stream of strings into UTF-8 Uint8Array chunks)
9604 *
9605 * ```ts import.meta.vitest
9606 * import { Effect, Stream } from "effect"
9607 *
9608 * const stream = Stream.make("Hello", " ", "World")
9609 * const program = Effect.gen(function*() {
9610 * const encoded = Stream.encodeText(stream)
9611 * const chunks = yield* Stream.runCollect(encoded)
9612 * const bytes = chunks.map((chunk) => [...chunk])
9613 * bytes // => [ [ 72, 101, 108, 108, 111 ], [ 32 ], [ 87, 111, 114, 108, 100 ] ]
9614 * })
9615 *
9616 * await Effect.runPromise(program)
9617 * ```
9618 *
9619 * @category text
9620 * @since 2.0.0
9621 */
9622export const encodeText = <E, R>(self: Stream<string, E, R>): Stream<Uint8Array, E, R> =>
9623 suspend(() => {
9624 const encoder = new TextEncoder()
9625 return map(self, (chunk) => encoder.encode(chunk))
9626 })
9627
9628/**
9629 * Splits a stream of strings into lines, handling `\n`, `\r`, and `\r\n` delimiters across chunks.
9630 *
9631 * **Example** (Splitting streamed text into lines)
9632 *
9633 * ```ts import.meta.vitest
9634 * import { Effect, Stream } from "effect"
9635 *
9636 * await Effect.runPromise(Effect.gen(function* () {
9637 * const lines = yield* Stream.runCollect(
9638 * Stream.make("a\nb\r\n", "c\n").pipe(Stream.splitLines)
9639 * )
9640 * lines // => [ 'a', 'b', 'c' ]
9641 * }))
9642 * ```
9643 *
9644 * @category text
9645 * @since 2.0.0
9646 */
9647export const splitLines = <E, R>(self: Stream<string, E, R>): Stream<string, E, R> =>
9648 self.channel.pipe(
9649 Channel.pipeTo(Channel.splitLines()),
9650 fromChannel
9651 )
9652
9653/**
9654 * Inserts the provided element between emitted elements.
9655 *
9656 * **Example** (Interspersing stream elements)
9657 *
9658 * ```ts import.meta.vitest
9659 * import { Console, Effect, Stream } from "effect"
9660 *
9661 * const program = Effect.gen(function*() {
9662 * const stream = Stream.make(1, 2, 3, 4).pipe(Stream.intersperse(0))
9663 * const result = yield* Stream.runCollect(stream)
9664 * result // => [1, 0, 2, 0, 3, 0, 4]
9665 * })
9666 *
9667 * await Effect.runPromise(program)
9668 * ```
9669 *
9670 * @category sequencing
9671 * @since 2.0.0
9672 */
9673export const intersperse: {
9674 <A2>(element: A2): <A, E, R>(self: Stream<A, E, R>) => Stream<A2 | A, E, R>
9675 <A, E, R, A2>(self: Stream<A, E, R>, element: A2): Stream<A | A2, E, R>
9676} = dual(2, <A, E, R, A2>(self: Stream<A, E, R>, element: A2): Stream<A | A2, E, R> =>
9677 mapArray(self, (arr, i) => {
9678 const out: Arr.NonEmptyArray<A | A2> = i === 0 ? [] as any : [element]
9679 const lastIndex = arr.length - 1
9680 for (let j = 0; j < arr.length; j++) {
9681 if (j === lastIndex) {
9682 out.push(arr[j])
9683 } else {
9684 out.push(arr[j], element)
9685 }
9686 }
9687 return out
9688 }))
9689
9690/**
9691 * Adds a start value, middle value, and end value around stream elements.
9692 *
9693 * **Details**
9694 *
9695 * The start and end values are always emitted, even when the stream is empty.
9696 *
9697 * **Example** (Interspersing stream affixes)
9698 *
9699 * ```ts import.meta.vitest
9700 * import { Console, Effect, Stream } from "effect"
9701 *
9702 * const stream = Stream.make("a", "b", "c").pipe(
9703 * Stream.intersperseAffixes({ start: "[", middle: ",", end: "]" })
9704 * )
9705 *
9706 * const program = Effect.gen(function*() {
9707 * const result = yield* Stream.runCollect(stream)
9708 * result // => ["[", "a", ",", "b", ",", "c", "]"]
9709 * })
9710 *
9711 * await Effect.runPromise(program)
9712 * ```
9713 *
9714 * @category sequencing
9715 * @since 2.0.0
9716 */
9717export const intersperseAffixes: {
9718 <A2, A3, A4>(
9719 options: { readonly start: A2; readonly middle: A3; readonly end: A4 }
9720 ): <A, E, R>(self: Stream<A, E, R>) => Stream<A2 | A3 | A4 | A, E, R>
9721 <A, E, R, A2, A3, A4>(
9722 self: Stream<A, E, R>,
9723 options: { readonly start: A2; readonly middle: A3; readonly end: A4 }
9724 ): Stream<A | A2 | A3 | A4, E, R>
9725} = dual(2, <A, E, R, A2, A3, A4>(
9726 self: Stream<A, E, R>,
9727 options: { readonly start: A2; readonly middle: A3; readonly end: A4 }
9728): Stream<A | A2 | A3 | A4, E, R> =>
9729 succeed(options.start).pipe(
9730 concat(intersperse(self, options.middle)),
9731 concat(succeed(options.end))
9732 ))
9733
9734/**
9735 * Interleaves this stream with the specified stream by alternating pulls from
9736 * each stream; when one ends, the remaining values from the other stream are
9737 * emitted.
9738 *
9739 * **Example** (Interleaving streams)
9740 *
9741 * ```ts import.meta.vitest
9742 * import { Effect, Stream } from "effect"
9743 *
9744 * const stream = Stream.interleave(
9745 * Stream.make(2, 3),
9746 * Stream.make(5, 6, 7)
9747 * )
9748 *
9749 * const program = Effect.gen(function*() {
9750 * const collected = yield* Stream.runCollect(stream)
9751 * collected // => [ 2, 5, 3, 6, 7 ]
9752 * })
9753 *
9754 * await Effect.runPromise(program)
9755 * ```
9756 *
9757 * @category merging
9758 * @since 2.0.0
9759 */
9760export const interleave: {
9761 <A2, E2, R2>(that: Stream<A2, E2, R2>): <A, E, R>(self: Stream<A, E, R>) => Stream<A2 | A, E2 | E, R2 | R>
9762 <A, E, R, A2, E2, R2>(self: Stream<A, E, R>, that: Stream<A2, E2, R2>): Stream<A | A2, E | E2, R | R2>
9763} = dual(
9764 2,
9765 <A, E, R, A2, E2, R2>(self: Stream<A, E, R>, that: Stream<A2, E2, R2>): Stream<A | A2, E | E2, R | R2> =>
9766 interleaveWith(
9767 self,
9768 that,
9769 fromIterable(Iterable.forever([true, false]))
9770 )
9771)
9772
9773/**
9774 * Interleaves two streams deterministically by following a boolean decider stream.
9775 *
9776 * **Details**
9777 *
9778 * The decider controls how many elements are pulled; if one side ends, pulls for
9779 * that side are ignored.
9780 *
9781 * **Example** (Interleaving two streams deterministically by following a boolean decider stream)
9782 *
9783 * ```ts import.meta.vitest
9784 * import { Effect, Stream } from "effect"
9785 *
9786 * const program = Effect.gen(function*() {
9787 * const left = Stream.make(1, 3, 5)
9788 * const right = Stream.make(2, 4, 6)
9789 * const decider = Stream.make(true, false, false, true, true)
9790 *
9791 * const values = yield* Stream.runCollect(
9792 * Stream.interleaveWith(left, right, decider)
9793 * )
9794 *
9795 * values // => [ 1, 2, 4, 3, 5 ]
9796 * })
9797 *
9798 * await Effect.runPromise(program)
9799 * ```
9800 *
9801 * @category merging
9802 * @since 2.0.0
9803 */
9804export const interleaveWith: {
9805 <A2, E2, R2, E3, R3>(
9806 that: Stream<A2, E2, R2>,
9807 decider: Stream<boolean, E3, R3>
9808 ): <A, E, R>(self: Stream<A, E, R>) => Stream<A2 | A, E2 | E3 | E, R2 | R3 | R>
9809 <A, E, R, A2, E2, R2, E3, R3>(
9810 self: Stream<A, E, R>,
9811 that: Stream<A2, E2, R2>,
9812 decider: Stream<boolean, E3, R3>
9813 ): Stream<A | A2, E | E2 | E3, R | R2 | R3>
9814} = dual(3, <A, E, R, A2, E2, R2, E3, R3>(
9815 self: Stream<A, E, R>,
9816 that: Stream<A2, E2, R2>,
9817 decider: Stream<boolean, E3, R3>
9818): Stream<A | A2, E | E2 | E3, R | R2 | R3> =>
9819 fromChannel(Channel.fromTransform(Effect.fnUntraced(function*(upstream, scope) {
9820 const pullDecider = yield* Channel.toTransform(Channel.flattenArray(decider.channel))(upstream, scope)
9821 const retry = Symbol()
9822 type retry = typeof retry
9823 let leftDone = false
9824 let rightDone = false
9825 const pullLeft = (yield* Channel.toTransform(Channel.flattenArray(self.channel))(
9826 upstream,
9827 scope
9828 )).pipe(
9829 Pull.catchDone(() => {
9830 leftDone = true
9831 return Effect.succeed<retry>(retry)
9832 })
9833 )
9834 const pullRight = (yield* Channel.toTransform(Channel.flattenArray(that.channel))(
9835 upstream,
9836 scope
9837 )).pipe(
9838 Pull.catchDone(() => {
9839 rightDone = true
9840 return Effect.succeed<retry>(retry)
9841 })
9842 )
9843
9844 return Effect.gen(function*() {
9845 while (true) {
9846 if (leftDone && rightDone) {
9847 return yield* Cause.done()
9848 }
9849 const side = yield* pullDecider
9850 if (side && leftDone) continue
9851 if (!side && rightDone) continue
9852 const elem = yield* (side ? pullLeft : pullRight)
9853 if (elem === retry) continue
9854 return Arr.of(elem)
9855 }
9856 })
9857 }))))
9858
9859/**
9860 * Interrupts the evaluation of this stream when the provided effect
9861 * completes. The given effect will be forked as part of this stream, and its
9862 * success will be discarded. This combinator will also interrupt any
9863 * in-progress element being pulled from upstream.
9864 *
9865 * **Details**
9866 *
9867 * If the effect completes with a failure before the stream completes, the
9868 * returned stream will emit that failure.
9869 *
9870 * **Example** (Interrupting when an effect completes)
9871 *
9872 * ```ts import.meta.vitest
9873 * import { Deferred, Effect, Stream } from "effect"
9874 *
9875 * const program = Effect.gen(function*() {
9876 * const interrupt = yield* Deferred.make<void>()
9877 * const stream = Stream.make(1, 2, 3).pipe(
9878 * Stream.tap((value) =>
9879 * value === 2
9880 * ? Deferred.succeed(interrupt, void 0)
9881 * : Effect.void
9882 * ),
9883 * Stream.interruptWhen(Deferred.await(interrupt))
9884 * )
9885 *
9886 * const result = yield* Stream.runCollect(stream)
9887 * result // => [ 1 ]
9888 * })
9889 *
9890 * await Effect.runPromise(program)
9891 * ```
9892 *
9893 * @category interruption
9894 * @since 2.0.0
9895 */
9896export const interruptWhen: {
9897 <X, E2, R2>(effect: Effect.Effect<X, E2, R2>): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E2 | E, R2 | R>
9898 <A, E, R, X, E2, R2>(self: Stream<A, E, R>, effect: Effect.Effect<X, E2, R2>): Stream<A, E | E2, R | R2>
9899} = dual(
9900 2,
9901 <A, E, R, X, E2, R2>(self: Stream<A, E, R>, effect: Effect.Effect<X, E2, R2>): Stream<A, E | E2, R | R2> =>
9902 fromChannel(Channel.interruptWhen(self.channel, effect))
9903)
9904
9905/**
9906 * Stops a stream after the current pull when an effect completes.
9907 *
9908 * **When to use**
9909 *
9910 * Use to stop before the next pull after an external signal completes.
9911 *
9912 * **Details**
9913 *
9914 * The effect is forked, its success value is discarded, and its failure fails
9915 * the stream.
9916 *
9917 * **Gotchas**
9918 *
9919 * This does not interrupt or truncate an in-progress pull. A pull may emit
9920 * multiple elements in a single chunk, in which case the entire chunk is
9921 * emitted. Use {@link interruptWhen} when the stream should be interrupted
9922 * immediately.
9923 *
9924 * **Example** (Halting a stream after an effect completes)
9925 *
9926 * ```ts import.meta.vitest
9927 * import { Deferred, Effect, Stream } from "effect"
9928 *
9929 * const program = Effect.gen(function*() {
9930 * const halt = yield* Deferred.make<void>()
9931 * const values = yield* Stream.fromArray([1, 2, 3]).pipe(
9932 * Stream.tap((value) => value === 2 ? Deferred.succeed(halt, void 0) : Effect.void),
9933 * Stream.haltWhen(Deferred.await(halt)),
9934 * Stream.runCollect
9935 * )
9936 * values // => [ 1, 2 ]
9937 * })
9938 *
9939 * await Effect.runPromise(program)
9940 * ```
9941 *
9942 * @category interruption
9943 * @since 2.0.0
9944 */
9945export const haltWhen: {
9946 <X, E2, R2>(effect: Effect.Effect<X, E2, R2>): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E2 | E, R2 | R>
9947 <A, E, R, X, E2, R2>(self: Stream<A, E, R>, effect: Effect.Effect<X, E2, R2>): Stream<A, E | E2, R | R2>
9948} = dual(
9949 2,
9950 <A, E, R, X, E2, R2>(self: Stream<A, E, R>, effect: Effect.Effect<X, E2, R2>): Stream<A, E | E2, R | R2> =>
9951 fromChannel(Channel.haltWhen(self.channel, effect))
9952)
9953
9954/**
9955 * Runs the provided finalizer when the stream exits, passing the exit value.
9956 *
9957 * **Example** (Running a finalizer on exit)
9958 *
9959 * ```ts import.meta.vitest
9960 * import { Effect, Exit, Stream } from "effect"
9961 *
9962 * const exits: Array<string> = []
9963 * const stream = Stream.make(1, 2, 3).pipe(
9964 * Stream.onExit((exit) =>
9965 * Exit.isSuccess(exit)
9966 * ? Effect.sync(() => exits.push("success"))
9967 * : Effect.sync(() => exits.push("failure"))
9968 * )
9969 * )
9970 *
9971 * await Effect.runPromise(Effect.gen(function*() {
9972 * yield* Stream.runCollect(stream)
9973 * }))
9974 * exits // => ["success"]
9975 * ```
9976 *
9977 * @category resource management
9978 * @since 4.0.0
9979 */
9980export const onExit: {
9981 <E, R2>(
9982 finalizer: (exit: Exit.Exit<unknown, E>) => Effect.Effect<unknown, never, R2>
9983 ): <A, R>(self: Stream<A, E, R>) => Stream<A, E, R | R2>
9984 <A, E, R, R2>(
9985 self: Stream<A, E, R>,
9986 finalizer: (exit: Exit.Exit<unknown, E>) => Effect.Effect<unknown, never, R2>
9987 ): Stream<A, E, R | R2>
9988} = dual(2, <A, E, R, R2>(
9989 self: Stream<A, E, R>,
9990 finalizer: (exit: Exit.Exit<unknown, E>) => Effect.Effect<unknown, never, R2>
9991): Stream<A, E, R | R2> => fromChannel(Channel.onExit(self.channel, finalizer)))
9992
9993/**
9994 * Runs the provided effect when the stream fails, passing the failure cause.
9995 *
9996 * **Gotchas**
9997 *
9998 * Note: Unlike `Effect.onError` there is no guarantee that the provided
9999 * effect will not be interrupted.
10000 *
10001 * **Example** (Running an effect on errors)
10002 *
10003 * ```ts import.meta.vitest
10004 * import { Cause, Effect, Stream } from "effect"
10005 *
10006 * const errors: Array<string> = []
10007 * const program = Effect.gen(function*() {
10008 * const stream = Stream.make(1, 2, 3).pipe(
10009 * Stream.concat(Stream.fail("boom")),
10010 * Stream.onError((cause) => Effect.sync(() => errors.push(String(Cause.squash(cause)))))
10011 * )
10012 *
10013 * yield* Stream.runCollect(stream)
10014 * })
10015 *
10016 * await Effect.runPromise(Effect.exit(program))
10017 * errors // => ["boom"]
10018 * ```
10019 *
10020 * @category resource management
10021 * @since 2.0.0
10022 */
10023export const onError: {
10024 <E, X, R2>(
10025 cleanup: (cause: Cause.Cause<E>) => Effect.Effect<X, never, R2>
10026 ): <A, R>(self: Stream<A, E, R>) => Stream<A, E, R2 | R>
10027 <A, E, R, X, R2>(
10028 self: Stream<A, E, R>,
10029 cleanup: (cause: Cause.Cause<E>) => Effect.Effect<X, never, R2>
10030 ): Stream<A, E, R | R2>
10031} = dual(2, <A, E, R, X, R2>(
10032 self: Stream<A, E, R>,
10033 cleanup: (cause: Cause.Cause<E>) => Effect.Effect<X, never, R2>
10034): Stream<A, E, R | R2> => fromChannel(Channel.onError(self.channel, cleanup)))
10035
10036/**
10037 * Runs the provided effect before this stream starts.
10038 *
10039 * **Example** (Running an effect on start)
10040 *
10041 * ```ts import.meta.vitest
10042 * import { Effect, Stream } from "effect"
10043 *
10044 * const events: Array<string> = []
10045 * const program = Effect.gen(function*() {
10046 * const stream = Stream.fromArray([1, 2, 3]).pipe(
10047 * Stream.onStart(Effect.sync(() => events.push("started")))
10048 * )
10049 *
10050 * const values = yield* Stream.runCollect(stream)
10051 * values // => [1, 2, 3]
10052 * })
10053 *
10054 * await Effect.runPromise(program)
10055 * events // => ["started"]
10056 * ```
10057 *
10058 * @category sequencing
10059 * @since 3.6.0
10060 */
10061export const onStart: {
10062 <X, EX, RX>(
10063 onStart: Effect.Effect<X, EX, RX>
10064 ): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E | EX, R | RX>
10065 <A, E, R, X, EX, RX>(
10066 self: Stream<A, E, R>,
10067 onStart: Effect.Effect<X, EX, RX>
10068 ): Stream<A, E | EX, R | RX>
10069} = dual(2, <A, E, R, X, EX, RX>(
10070 self: Stream<A, E, R>,
10071 onStart: Effect.Effect<X, EX, RX>
10072): Stream<A, E | EX, R | RX> => fromChannel(Channel.onStart(self.channel, onStart)))
10073
10074/**
10075 * Runs the provided effect with the first element emitted by the stream.
10076 *
10077 * **Example** (Running an effect on the first value)
10078 *
10079 * ```ts import.meta.vitest
10080 * import { Effect, Stream } from "effect"
10081 *
10082 * const first: Array<number> = []
10083 * await Effect.runPromise(Effect.gen(function* () {
10084 * yield* Stream.fromArray([1, 2, 3]).pipe(
10085 * Stream.onFirst((value) => Effect.sync(() => first.push(value))),
10086 * Stream.runDrain
10087 * )
10088 * }))
10089 * first // => [1]
10090 * ```
10091 *
10092 * @category sequencing
10093 * @since 4.0.0
10094 */
10095export const onFirst: {
10096 <A, X, EX, RX>(
10097 onFirst: (element: NoInfer<A>) => Effect.Effect<X, EX, RX>
10098 ): <E, R>(self: Stream<A, E, R>) => Stream<A, E | EX, R | RX>
10099 <A, E, R, X, EX, RX>(
10100 self: Stream<A, E, R>,
10101 onFirst: (element: NoInfer<A>) => Effect.Effect<X, EX, RX>
10102 ): Stream<A, E | EX, R | RX>
10103} = dual(2, <A, E, R, X, EX, RX>(
10104 self: Stream<A, E, R>,
10105 onFirst: (element: NoInfer<A>) => Effect.Effect<X, EX, RX>
10106): Stream<A, E | EX, R | RX> => fromChannel(Channel.onFirst(self.channel, (arr) => onFirst(arr[0]))))
10107
10108/**
10109 * Runs the provided effect when the stream ends successfully.
10110 *
10111 * **Example** (Running an effect on end)
10112 *
10113 * ```ts import.meta.vitest
10114 * import { Effect, Stream } from "effect"
10115 *
10116 * const events: Array<string> = []
10117 * const program = Effect.gen(function*() {
10118 * const values = yield* Stream.make(1, 2, 3).pipe(
10119 * Stream.onEnd(Effect.sync(() => events.push("ended"))),
10120 * Stream.runCollect
10121 * )
10122 * values // => [1, 2, 3]
10123 * })
10124 *
10125 * await Effect.runPromise(program)
10126 * events // => ["ended"]
10127 * ```
10128 *
10129 * @category sequencing
10130 * @since 3.6.0
10131 */
10132export const onEnd: {
10133 <X, EX, RX>(
10134 onEnd: Effect.Effect<X, EX, RX>
10135 ): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E | EX, R | RX>
10136 <A, E, R, X, EX, RX>(
10137 self: Stream<A, E, R>,
10138 onEnd: Effect.Effect<X, EX, RX>
10139 ): Stream<A, E | EX, R | RX>
10140} = dual(2, <A, E, R, X, EX, RX>(
10141 self: Stream<A, E, R>,
10142 onEnd: Effect.Effect<X, EX, RX>
10143): Stream<A, E | EX, R | RX> => fromChannel(Channel.onEnd(self.channel, onEnd)))
10144
10145/**
10146 * Executes the provided finalizer after this stream's finalizers run.
10147 *
10148 * **Example** (Ensuring finalization)
10149 *
10150 * ```ts import.meta.vitest
10151 * import { Effect, Stream } from "effect"
10152 *
10153 * const events: Array<string> = []
10154 * const stream = Stream.fromArray([1, 2]).pipe(
10155 * Stream.ensuring(Effect.sync(() => events.push("cleanup")))
10156 * )
10157 *
10158 * const program = Effect.gen(function*() {
10159 * const collected = yield* Stream.runCollect(stream)
10160 * collected // => [1, 2]
10161 * })
10162 *
10163 * await Effect.runPromise(program)
10164 * events // => ["cleanup"]
10165 * ```
10166 *
10167 * @category resource management
10168 * @since 2.0.0
10169 */
10170export const ensuring: {
10171 <R2>(finalizer: Effect.Effect<unknown, never, R2>): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E, R | R2>
10172 <A, E, R, R2>(self: Stream<A, E, R>, finalizer: Effect.Effect<unknown, never, R2>): Stream<A, E, R | R2>
10173} = dual(
10174 2,
10175 <A, E, R, R2>(self: Stream<A, E, R>, finalizer: Effect.Effect<unknown, never, R2>): Stream<A, E, R | R2> =>
10176 fromChannel(Channel.ensuring(self.channel, finalizer))
10177)
10178
10179/**
10180 * Provides a layer or context to the stream, removing the corresponding
10181 * service requirements. Use `options.local` to build the layer every time; by
10182 * default, layers are shared between provide calls.
10183 *
10184 * **Example** (Providing stream requirements)
10185 *
10186 * ```ts import.meta.vitest
10187 * import { Console, Context, Effect, Layer, Stream } from "effect"
10188 *
10189 * class Env extends Context.Service<Env, { readonly name: string }>()("Env") {}
10190 *
10191 * const layer = Layer.succeed(Env)({ name: "Ada" })
10192 *
10193 * const stream = Stream.fromEffect(
10194 * Effect.gen(function*() {
10195 * const env = yield* Effect.service(Env)
10196 * return `Hello, ${env.name}`
10197 * })
10198 * )
10199 *
10200 * const withEnv = stream.pipe(Stream.provide(layer))
10201 *
10202 * await Effect.runPromise(Stream.runCollect(withEnv)) // => ["Hello, Ada"]
10203 * ```
10204 *
10205 * @category providing services
10206 * @since 4.0.0
10207 */
10208export const provide: {
10209 <AL, EL = never, RL = never>(
10210 layer: Layer.Layer<AL, EL, RL> | Context.Context<AL>,
10211 options?: {
10212 readonly local?: boolean | undefined
10213 } | undefined
10214 ): <A, E, R>(
10215 self: Stream<A, E, R>
10216 ) => Stream<A, E | EL, Exclude<R, AL> | RL>
10217 <A, E, R, AL, EL = never, RL = never>(
10218 self: Stream<A, E, R>,
10219 layer: Layer.Layer<AL, EL, RL> | Context.Context<AL>,
10220 options?: {
10221 readonly local?: boolean | undefined
10222 } | undefined
10223 ): Stream<A, E | EL, Exclude<R, AL> | RL>
10224} = dual((args) => isStream(args[0]), <A, E, R, AL, EL = never, RL = never>(
10225 self: Stream<A, E, R>,
10226 layer: Layer.Layer<AL, EL, RL> | Context.Context<AL>,
10227 options?: {
10228 readonly local?: boolean | undefined
10229 } | undefined
10230): Stream<A, E | EL, Exclude<R, AL> | RL> => fromChannel(Channel.provide(self.channel, layer, options)))
10231
10232/**
10233 * Provides multiple services to the stream using a context.
10234 *
10235 * **Example** (Providing multiple services to the stream using a context)
10236 *
10237 * ```ts import.meta.vitest
10238 * import { Context, Effect, Stream } from "effect"
10239 *
10240 * class Config extends Context.Service<Config, { readonly prefix: string }>()("Config") {}
10241 * class Greeter extends Context.Service<Greeter, { greet: (name: string) => string }>()("Greeter") {}
10242 *
10243 * const context = Context.make(Config, { prefix: "Hello" }).pipe(
10244 * Context.add(Greeter, { greet: (name: string) => `${name}!` })
10245 * )
10246 *
10247 * const stream = Stream.fromEffect(
10248 * Effect.gen(function*() {
10249 * const config = yield* Effect.service(Config)
10250 * const greeter = yield* Effect.service(Greeter)
10251 * return greeter.greet(config.prefix)
10252 * })
10253 * )
10254 *
10255 * const program = Effect.gen(function*() {
10256 * const result = yield* Stream.runCollect(Stream.provideContext(stream, context))
10257 * result // => [ 'Hello!' ]
10258 * })
10259 *
10260 * await Effect.runPromise(program)
10261 * ```
10262 *
10263 * @category providing services
10264 * @since 2.0.0
10265 */
10266export const provideContext: {
10267 <R2>(context: Context.Context<R2>): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E, Exclude<R, R2>>
10268 <A, E, R, R2>(self: Stream<A, E, R>, context: Context.Context<R2>): Stream<A, E, Exclude<R, R2>>
10269} = dual(
10270 2,
10271 <A, E, R, R2>(self: Stream<A, E, R>, context: Context.Context<R2>): Stream<A, E, Exclude<R, R2>> =>
10272 fromChannel(Channel.provideContext(self.channel, context))
10273)
10274
10275/**
10276 * Provides the stream with a single required service, eliminating that
10277 * requirement from its environment.
10278 *
10279 * **Example** (Providing a stream service)
10280 *
10281 * ```ts import.meta.vitest
10282 * import { Context, Effect, Stream } from "effect"
10283 *
10284 * class Greeter extends Context.Service<Greeter, {
10285 * greet: (name: string) => string
10286 * }>()("Greeter") {}
10287 *
10288 * const stream = Stream.fromEffect(
10289 * Effect.service(Greeter).pipe(
10290 * Effect.map((greeter) => greeter.greet("Ada"))
10291 * )
10292 * )
10293 *
10294 * const program = Effect.gen(function*() {
10295 * const collected = yield* Stream.runCollect(
10296 * stream.pipe(
10297 * Stream.provideService(Greeter, {
10298 * greet: (name) => `Hello, ${name}`
10299 * })
10300 * )
10301 * )
10302 * collected // => [ 'Hello, Ada' ]
10303 * })
10304 *
10305 * await Effect.runPromise(program)
10306 * ```
10307 *
10308 * @category providing services
10309 * @since 2.0.0
10310 */
10311export const provideService: {
10312 <I, S>(
10313 key: Context.Key<I, S>,
10314 service: NoInfer<S>
10315 ): <A, E, R>(
10316 self: Stream<A, E, R>
10317 ) => Stream<A, E, Exclude<R, I>>
10318 <A, E, R, I, S>(
10319 self: Stream<A, E, R>,
10320 key: Context.Key<I, S>,
10321 service: NoInfer<S>
10322 ): Stream<A, E, Exclude<R, I>>
10323} = dual(3, <A, E, R, I, S>(
10324 self: Stream<A, E, R>,
10325 key: Context.Key<I, S>,
10326 service: NoInfer<S>
10327): Stream<A, E, Exclude<R, I>> => fromChannel(Channel.provideService(self.channel, key, service)))
10328
10329/**
10330 * Provides a service to the stream using an effect, removing the requirement and adding the effect's error and environment.
10331 *
10332 * **Example** (Providing a stream service effectfully)
10333 *
10334 * ```ts import.meta.vitest
10335 * import { Context, Effect, Stream } from "effect"
10336 *
10337 * class ApiConfig extends Context.Service<ApiConfig, { readonly baseUrl: string }>()("ApiConfig") {}
10338 *
10339 * const stream = Stream.fromEffect(
10340 * Effect.gen(function*() {
10341 * const config = yield* Effect.service(ApiConfig)
10342 * return config.baseUrl
10343 * })
10344 * )
10345 *
10346 * const events: Array<string> = []
10347 * const withConfig = stream.pipe(
10348 * Stream.provideServiceEffect(
10349 * ApiConfig,
10350 * Effect.succeed({ baseUrl: "https://example.com" }).pipe(
10351 * Effect.tap(() => Effect.sync(() => events.push("loading")))
10352 * )
10353 * )
10354 * )
10355 *
10356 * await Effect.runPromise(Stream.runCollect(withConfig)) // => ["https://example.com"]
10357 * events // => ["loading"]
10358 * ```
10359 *
10360 * @category providing services
10361 * @since 2.0.0
10362 */
10363export const provideServiceEffect: {
10364 <I, S, ES, RS>(
10365 key: Context.Key<I, S>,
10366 service: Effect.Effect<NoInfer<S>, ES, RS>
10367 ): <A, E, R>(
10368 self: Stream<A, E, R>
10369 ) => Stream<A, E | ES, Exclude<R, I> | RS>
10370 <A, E, R, I, S, ES, RS>(
10371 self: Stream<A, E, R>,
10372 key: Context.Key<I, S>,
10373 service: Effect.Effect<NoInfer<S>, ES, RS>
10374 ): Stream<A, E | ES, Exclude<R, I> | RS>
10375} = dual(3, <A, E, R, I, S, ES, RS>(
10376 self: Stream<A, E, R>,
10377 key: Context.Key<I, S>,
10378 service: Effect.Effect<NoInfer<S>, ES, RS>
10379): Stream<A, E | ES, Exclude<R, I> | RS> => fromChannel(Channel.provideServiceEffect(self.channel, key, service)))
10380
10381/**
10382 * Transforms the stream's required services by mapping the current context
10383 * to a new one.
10384 *
10385 * **Example** (Updating the stream context)
10386 *
10387 * ```ts import.meta.vitest
10388 * import { Context, Effect, Stream } from "effect"
10389 *
10390 * class Logger extends Context.Service<Logger, { prefix: string }>()("Logger") {}
10391 * class Config extends Context.Service<Config, { name: string }>()("Config") {}
10392 *
10393 * const stream = Stream.fromEffect(
10394 * Effect.gen(function*() {
10395 * const logger = yield* Effect.service(Logger)
10396 * const config = yield* Effect.service(Config)
10397 * return `${logger.prefix}${config.name}`
10398 * })
10399 * )
10400 *
10401 * const updated = stream.pipe(
10402 * Stream.updateContext((context: Context.Context<Logger>) =>
10403 * Context.add(context, Config, { name: "World" })
10404 * )
10405 * )
10406 *
10407 * const program = Effect.gen(function*() {
10408 * const values = yield* Stream.runCollect(updated)
10409 * values // => [ 'Hello World' ]
10410 * })
10411 *
10412 * await Effect.runPromise(
10413 * Effect.provideService(program, Logger, { prefix: "Hello " })
10414 * )
10415 * ```
10416 *
10417 * @category providing services
10418 * @since 4.0.0
10419 */
10420export const updateContext: {
10421 <R, R2>(
10422 f: (context: Context.Context<R2>) => Context.Context<R>
10423 ): <A, E>(
10424 self: Stream<A, E, R>
10425 ) => Stream<A, E, R2>
10426 <A, E, R, R2>(
10427 self: Stream<A, E, R>,
10428 f: (context: Context.Context<R2>) => Context.Context<R>
10429 ): Stream<A, E, R2>
10430} = dual(2, <A, E, R, R2>(
10431 self: Stream<A, E, R>,
10432 f: (context: Context.Context<R2>) => Context.Context<R>
10433): Stream<A, E, R2> => fromChannel(Channel.updateContext(self.channel, f)))
10434
10435/**
10436 * Updates a single service in the stream environment by applying a function.
10437 *
10438 * **Example** (Updating a stream service)
10439 *
10440 * ```ts import.meta.vitest
10441 * import { Context, Effect, Stream } from "effect"
10442 *
10443 * class Counter extends Context.Service<Counter, { count: number }>()("Counter") {}
10444 *
10445 * const stream = Stream.fromEffect(Effect.service(Counter)).pipe(
10446 * Stream.updateService(Counter, (counter) => ({ count: counter.count + 1 }))
10447 * )
10448 *
10449 * const program = Effect.gen(function*() {
10450 * const counters = yield* Stream.runCollect(stream)
10451 * const message = `Updated count: ${counters[0].count}` // => "Updated count: 1"
10452 * })
10453 *
10454 * await Effect.runPromise(Effect.provideService(program, Counter, { count: 0 }))
10455 * ```
10456 *
10457 * @category providing services
10458 * @since 2.0.0
10459 */
10460export const updateService: {
10461 <I, S>(
10462 key: Context.Key<I, S>,
10463 f: (service: NoInfer<S>) => S
10464 ): <A, E, R>(
10465 self: Stream<A, E, R>
10466 ) => Stream<A, E, R | I>
10467 <A, E, R, I, S>(
10468 self: Stream<A, E, R>,
10469 key: Context.Key<I, S>,
10470 f: (service: NoInfer<S>) => S
10471 ): Stream<A, E, R | I>
10472} = dual(3, <A, E, R, I, S>(
10473 self: Stream<A, E, R>,
10474 service: Context.Key<I, S>,
10475 f: (service: NoInfer<S>) => S
10476): Stream<A, E, R | I> =>
10477 updateContext(self, (context) =>
10478 Context.add(
10479 context,
10480 service,
10481 f(Context.get(context, service))
10482 )))
10483
10484/**
10485 * Wraps the stream with a new span for tracing.
10486 *
10487 * **Example** (Wrapping a stream in a span)
10488 *
10489 * ```ts import.meta.vitest
10490 * import { Effect, Stream } from "effect"
10491 *
10492 * const stream = Stream.fromArray([1, 2, 3]).pipe(Stream.withSpan("numbers"))
10493 *
10494 * await Effect.runPromise(
10495 * Effect.gen(function*() {
10496 * const values = yield* Stream.runCollect(stream)
10497 * values // => [ 1, 2, 3 ]
10498 * })
10499 * )
10500 * ```
10501 *
10502 * @category tracing
10503 * @since 2.0.0
10504 */
10505export const withSpan: {
10506 (name: string, options?: SpanOptions): <A, E, R>(self: Stream<A, E, R>) => Stream<A, E, Exclude<R, ParentSpan>>
10507 <A, E, R>(self: Stream<A, E, R>, name: string, options?: SpanOptions): Stream<A, E, Exclude<R, ParentSpan>>
10508} = function() {
10509 const dataFirst = isStream(arguments[0])
10510 const name = dataFirst ? arguments[1] : arguments[0]
10511 const options = addSpanStackTrace(dataFirst ? arguments[2] : arguments[1])
10512 if (dataFirst) {
10513 const self = arguments[0] as Stream<any, any, any>
10514 return fromChannel(Channel.withSpan(self.channel, name, options))
10515 }
10516 return (self: Stream<any, any, any>) => fromChannel(Channel.withSpan(self.channel, name, options))
10517} as any
10518
10519/**
10520 * Provides the entry point for do-notation style stream composition.
10521 *
10522 * **Example** (Starting stream do notation)
10523 *
10524 * ```ts import.meta.vitest
10525 * import { Effect, pipe, Stream } from "effect"
10526 *
10527 * const program = pipe(
10528 * Stream.Do,
10529 * Stream.bind("value", () => Stream.fromArray([1, 2])),
10530 * Stream.let("next", ({ value }) => value + 1)
10531 * )
10532 *
10533 * const effect = Effect.gen(function*() {
10534 * const collected = yield* Stream.runCollect(program)
10535 * collected // => [ { value: 1, next: 2 }, { value: 2, next: 3 } ]
10536 * })
10537 *
10538 * await Effect.runPromise(effect)
10539 * ```
10540 *
10541 * @category do notation
10542 * @since 2.0.0
10543 */
10544export const Do: Stream<{}> = succeed({})
10545
10546const let_: {
10547 <N extends string, A extends Record<string, any>, B>(
10548 name: N,
10549 f: (a: NoInfer<A>) => B
10550 ): <E, R>(self: Stream<A, E, R>) => Stream<Simplify<Omit<A, N> & Record<N, B>>, E, R>
10551 <A extends Record<string, any>, E, R, B, N extends string>(
10552 self: Stream<A, E, R>,
10553 name: N,
10554 f: (a: NoInfer<A>) => B
10555 ): Stream<Simplify<Omit<A, N> & Record<N, B>>, E, R>
10556} = dual(3, <A extends Record<string, any>, E, R, B, N extends string>(
10557 self: Stream<A, E, R>,
10558 name: N,
10559 f: (a: NoInfer<A>) => B
10560): Stream<Simplify<Omit<A, N> & Record<N, B>>, E, R> => map(self, (a) => ({ ...a, [name]: f(a) } as any)))
10561export {
10562 /**
10563 * Adds a computed field to the current Do-notation record.
10564 *
10565 * **Example** (Adding a computed field)
10566 *
10567 * ```ts import.meta.vitest
10568 * import { Effect, Stream } from "effect"
10569 *
10570 * const stream = Stream.Do.pipe(
10571 * Stream.let("x", () => 2),
10572 * Stream.let("y", ({ x }) => x * 3)
10573 * )
10574 *
10575 * const program = Effect.gen(function*() {
10576 * const records = yield* Stream.runCollect(stream)
10577 * records // => [ { x: 2, y: 6 } ]
10578 * })
10579 *
10580 * await Effect.runPromise(program)
10581 * ```
10582 *
10583 * @category do notation
10584 * @since 2.0.0
10585 */
10586 let_ as let
10587}
10588
10589/**
10590 * Binds the result of a stream to a field in the do-notation record.
10591 *
10592 * **Example** (Binding a stream value)
10593 *
10594 * ```ts import.meta.vitest
10595 * import { Effect, Stream } from "effect"
10596 *
10597 * const program = Stream.Do.pipe(
10598 * Stream.bind("a", () => Stream.make(1, 2)),
10599 * Stream.bind("b", ({ a }) => Stream.succeed(a + 1))
10600 * )
10601 *
10602 * const result = Stream.runCollect(program)
10603 *
10604 * await Effect.runPromise(result) // => [{ a: 1, b: 2 }, { a: 2, b: 3 }]
10605 * ```
10606 *
10607 * @category do notation
10608 * @since 2.0.0
10609 */
10610export const bind: {
10611 <N extends string, A extends Record<string, any>, B, E2, R2>(
10612 name: N,
10613 f: (a: NoInfer<A>) => Stream<B, E2, R2>,
10614 options?: {
10615 readonly concurrency?: Concurrency | undefined
10616 readonly bufferSize?: number | undefined
10617 } | undefined
10618 ): <E, R>(self: Stream<A, E, R>) => Stream<Simplify<Omit<A, N> & Record<N, B>>, E2 | E, R2 | R>
10619 <A extends Record<string, any>, E, R, B, E2, R2, N extends string>(
10620 self: Stream<A, E, R>,
10621 name: N,
10622 f: (a: NoInfer<A>) => Stream<B, E2, R2>,
10623 options?: {
10624 readonly concurrency?: Concurrency | undefined
10625 readonly bufferSize?: number | undefined
10626 } | undefined
10627 ): Stream<Simplify<Omit<A, N> & Record<N, B>>, E | E2, R | R2>
10628} = dual((args) => isStream(args[0]), <A extends Record<string, any>, E, R, B, E2, R2, N extends string>(
10629 self: Stream<A, E, R>,
10630 name: N,
10631 f: (a: NoInfer<A>) => Stream<B, E2, R2>,
10632 options?: {
10633 readonly concurrency?: Concurrency | undefined
10634 readonly bufferSize?: number | undefined
10635 } | undefined
10636): Stream<Simplify<Omit<A, N> & Record<N, B>>, E | E2, R | R2> =>
10637 flatMap(self, (a) => map(f(a), (b) => ({ ...a, [name]: b } as any)), options))
10638
10639/**
10640 * Binds an Effect-produced value into the do-notation record for each stream element.
10641 *
10642 * **Example** (Binding an effect value)
10643 *
10644 * ```ts import.meta.vitest
10645 * import { Effect, Stream } from "effect"
10646 *
10647 * const stream = Stream.Do.pipe(
10648 * Stream.bind("value", () => Stream.make(1, 2)),
10649 * Stream.bindEffect("double", ({ value }) => Effect.succeed(value * 2))
10650 * )
10651 *
10652 * const program = Effect.gen(function*() {
10653 * const result = yield* Stream.runCollect(stream)
10654 * result // => [ { value: 1, double: 2 }, { value: 2, double: 4 } ]
10655 * })
10656 *
10657 * await Effect.runPromise(program)
10658 * ```
10659 *
10660 * @category do notation
10661 * @since 2.0.0
10662 */
10663export const bindEffect: {
10664 <N extends string, A extends Record<string, any>, B, E2, R2>(
10665 name: N,
10666 f: (a: NoInfer<A>) => Effect.Effect<B, E2, R2>,
10667 options?: {
10668 readonly concurrency?: Concurrency | undefined
10669 readonly bufferSize?: number | undefined
10670 readonly unordered?: boolean | undefined
10671 } | undefined
10672 ): <E, R>(self: Stream<A, E, R>) => Stream<Simplify<Omit<A, N> & Record<N, B>>, E | E2, R | R2>
10673 <A extends Record<string, any>, E, R, B, E2, R2, N extends string>(
10674 self: Stream<A, E, R>,
10675 name: N,
10676 f: (a: NoInfer<A>) => Effect.Effect<B, E2, R2>,
10677 options?: {
10678 readonly concurrency?: Concurrency | undefined
10679 readonly bufferSize?: number | undefined
10680 readonly unordered?: boolean | undefined
10681 } | undefined
10682 ): Stream<Simplify<Omit<A, N> & Record<N, B>>, E | E2, R | R2>
10683} = dual((args) => isStream(args[0]), <A extends Record<string, any>, E, R, B, E2, R2, N extends string>(
10684 self: Stream<A, E, R>,
10685 name: N,
10686 f: (a: NoInfer<A>) => Effect.Effect<B, E2, R2>,
10687 options?: {
10688 readonly concurrency?: Concurrency | undefined
10689 readonly bufferSize?: number | undefined
10690 readonly unordered?: boolean | undefined
10691 } | undefined
10692): Stream<Simplify<Omit<A, N> & Record<N, B>>, E | E2, R | R2> =>
10693 mapEffect(self, (a) => Effect.map(f(a), (b) => ({ ...a, [name]: b } as any)), options))
10694
10695/**
10696 * Maps each element into a record keyed by the provided name.
10697 *
10698 * **Example** (Binding values to a record key)
10699 *
10700 * ```ts import.meta.vitest
10701 * import { Effect, Stream } from "effect"
10702 *
10703 * const stream = Stream.make(1, 2, 3).pipe(Stream.bindTo("value"))
10704 *
10705 * await Effect.runPromise(Stream.runCollect(stream)) // => [{ value: 1 }, { value: 2 }, { value: 3 }]
10706 * ```
10707 *
10708 * @category do notation
10709 * @since 2.0.0
10710 */
10711export const bindTo: {
10712 <N extends string>(name: N): <A, E, R>(self: Stream<A, E, R>) => Stream<{ [K in N]: A }, E, R>
10713 <A, E, R, N extends string>(self: Stream<A, E, R>, name: N): Stream<{ [K in N]: A }, E, R>
10714} = dual(2, <A, E, R, N extends string>(
10715 self: Stream<A, E, R>,
10716 name: N
10717): Stream<{ [K in N]: A }, E, R> => map(self, (a) => ({ [name]: a } as any)))
10718
10719/**
10720 * Runs a stream with a sink and returns the sink result.
10721 *
10722 * **Example** (Running a stream with a sink)
10723 *
10724 * ```ts import.meta.vitest
10725 * import { Effect, Sink, Stream } from "effect"
10726 *
10727 * const program = Stream.run(Stream.make(1, 2, 3), Sink.sum)
10728 *
10729 * await Effect.runPromise(program) // => 6
10730 * ```
10731 *
10732 * @category destructors
10733 * @since 2.0.0
10734 */
10735export const run: {
10736 <A2, A, L, E2, R2>(
10737 sink: Sink.Sink<A2, A, L, E2, R2>
10738 ): <E, R>(self: Stream<A, E, R>) => Effect.Effect<A2, E2 | E, R | R2>
10739 <A, E, R, L, A2, E2, R2>(
10740 self: Stream<A, E, R>,
10741 sink: Sink.Sink<A2, A, L, E2, R2>
10742 ): Effect.Effect<A2, E | E2, R | R2>
10743} = dual(2, <A, E, R, L, A2, E2, R2>(
10744 self: Stream<A, E, R>,
10745 sink: Sink.Sink<A2, A, L, E2, R2>
10746): Effect.Effect<A2, E | E2, R | R2> =>
10747 Effect.scopedWith((scope) =>
10748 Channel.toPullScoped(self.channel, scope).pipe(
10749 Effect.flatMap((upstream) => sink.transform(upstream as any, scope)),
10750 Effect.map(([a]) => a)
10751 )
10752 ))
10753
10754/**
10755 * Runs the stream and collects all elements into an array.
10756 *
10757 * **Example** (Collecting stream values)
10758 *
10759 * ```ts import.meta.vitest
10760 * import { Effect, Stream } from "effect"
10761 *
10762 * const stream = Stream.make(1, 2, 3, 4, 5)
10763 *
10764 * const program = Effect.gen(function*() {
10765 * const collected = yield* Stream.runCollect(stream)
10766 * collected // => [ 1, 2, 3, 4, 5 ]
10767 * })
10768 *
10769 * await Effect.runPromise(program)
10770 * ```
10771 *
10772 * @category destructors
10773 * @since 2.0.0
10774 */
10775export const runCollect = <A, E, R>(self: Stream<A, E, R>): Effect.Effect<Array<A>, E, R> =>
10776 Channel.runFold(
10777 self.channel,
10778 () => [] as Array<A>,
10779 (acc, chunk) => {
10780 for (let i = 0; i < chunk.length; i++) {
10781 acc.push(chunk[i])
10782 }
10783 return acc
10784 }
10785 )
10786
10787/**
10788 * Runs the stream and returns the number of elements emitted.
10789 *
10790 * **Example** (Counting stream values)
10791 *
10792 * ```ts import.meta.vitest
10793 * import { Effect, Stream } from "effect"
10794 *
10795 * const stream = Stream.make(1, 2, 3, 4, 5)
10796 *
10797 * const program = Effect.gen(function* () {
10798 * const count = yield* Stream.runCount(stream)
10799 * count // => 5
10800 * })
10801 *
10802 * await Effect.runPromise(program)
10803 * ```
10804 *
10805 * @category destructors
10806 * @since 2.0.0
10807 */
10808export const runCount = <A, E, R>(self: Stream<A, E, R>): Effect.Effect<number, E, R> =>
10809 Channel.runFold(self.channel, () => 0, (acc, chunk) => acc + chunk.length)
10810
10811/**
10812 * Runs the stream and returns the numeric sum of its elements.
10813 *
10814 * **Example** (Summing stream values)
10815 *
10816 * ```ts import.meta.vitest
10817 * import { Effect, Stream } from "effect"
10818 *
10819 * const program = Effect.gen(function*() {
10820 * const total = yield* Stream.runSum(Stream.make(1, 2, 3))
10821 * total // => 6
10822 * })
10823 *
10824 * await Effect.runPromise(program)
10825 * ```
10826 *
10827 * @category destructors
10828 * @since 2.0.0
10829 */
10830export const runSum = <E, R>(self: Stream<number, E, R>): Effect.Effect<number, E, R> =>
10831 Channel.runFold(self.channel, () => 0, (acc, chunk) => {
10832 for (let i = 0; i < chunk.length; i++) {
10833 acc += chunk[i]
10834 }
10835 return acc
10836 })
10837
10838/**
10839 * Runs the stream and folds elements using a pure reducer.
10840 *
10841 * **Example** (Folding stream values)
10842 *
10843 * ```ts import.meta.vitest
10844 * import { Effect, Stream } from "effect"
10845 *
10846 * const program = Effect.gen(function*() {
10847 * const total = yield* Stream.runFold(
10848 * Stream.make(1, 2, 3),
10849 * () => 0,
10850 * (acc, n) => acc + n
10851 * )
10852 * total // => 6
10853 * })
10854 *
10855 * await Effect.runPromise(program)
10856 * ```
10857 *
10858 * @category destructors
10859 * @since 2.0.0
10860 */
10861export const runFold: {
10862 <Z, A>(
10863 initial: LazyArg<Z>,
10864 f: (acc: Z, a: A) => Z
10865 ): <E, R>(
10866 self: Stream<A, E, R>
10867 ) => Effect.Effect<Z, E, R>
10868 <A, E, R, Z>(
10869 self: Stream<A, E, R>,
10870 initial: LazyArg<Z>,
10871 f: (acc: Z, a: A) => Z
10872 ): Effect.Effect<Z, E, R>
10873} = dual(3, <A, E, R, Z>(
10874 self: Stream<A, E, R>,
10875 initial: LazyArg<Z>,
10876 f: (acc: Z, a: A) => Z
10877): Effect.Effect<Z, E, R> =>
10878 Channel.runFold(self.channel, initial, (acc, arr) => {
10879 for (let i = 0; i < arr.length; i++) {
10880 acc = f(acc, arr[i])
10881 }
10882 return acc
10883 }))
10884
10885/**
10886 * Runs the stream and folds elements using an effectful reducer.
10887 *
10888 * **When to use**
10889 *
10890 * Use when reducing stream elements needs Effects, services, or failures in the
10891 * reducer.
10892 *
10893 * **Example** (Effectfully folding stream values)
10894 *
10895 * ```ts import.meta.vitest
10896 * import { Effect, Stream } from "effect"
10897 *
10898 * const program = Effect.gen(function*() {
10899 * const total = yield* Stream.runFoldEffect(
10900 * Stream.make(1, 2, 3),
10901 * () => 0,
10902 * (acc, n) => Effect.succeed(acc + n)
10903 * )
10904 * total // => 6
10905 * })
10906 *
10907 * await Effect.runPromise(program)
10908 * ```
10909 *
10910 * @category destructors
10911 * @since 2.0.0
10912 */
10913export const runFoldEffect: {
10914 <Z, A, EX, RX>(
10915 initial: LazyArg<Z>,
10916 f: (acc: Z, a: A) => Effect.Effect<Z, EX, RX>
10917 ): <E, R>(
10918 self: Stream<A, E, R>
10919 ) => Effect.Effect<Z, E | EX, R | RX>
10920 <A, E, R, Z, EX, RX>(
10921 self: Stream<A, E, R>,
10922 initial: LazyArg<Z>,
10923 f: (acc: Z, a: A) => Effect.Effect<Z, EX, RX>
10924 ): Effect.Effect<Z, E | EX, R | RX>
10925} = dual(3, <A, E, R, Z, EX, RX>(
10926 self: Stream<A, E, R>,
10927 initial: LazyArg<Z>,
10928 f: (acc: Z, a: A) => Effect.Effect<Z, EX, RX>
10929): Effect.Effect<Z, E | EX, R | RX> =>
10930 Channel.runFoldEffect(self.channel, initial, (acc, arr) => {
10931 let i = 0
10932 let s = acc
10933 return Effect.map(
10934 Effect.whileLoop({
10935 while: () => i < arr.length,
10936 body: () => f(s, arr[i]),
10937 step(z) {
10938 s = z
10939 i++
10940 }
10941 }),
10942 () => s
10943 )
10944 }))
10945
10946/**
10947 * Runs the stream and returns the first element as an `Option`.
10948 *
10949 * **Example** (Getting the first stream value)
10950 *
10951 * ```ts import.meta.vitest
10952 * import { Effect, Option, Stream } from "effect"
10953 *
10954 * const program = Effect.gen(function*() {
10955 * const head = yield* Stream.runHead(Stream.make(1, 2, 3))
10956 * Option.getOrThrow(head) // => 1
10957 * })
10958 *
10959 * await Effect.runPromise(program)
10960 * ```
10961 *
10962 * @category destructors
10963 * @since 2.0.0
10964 */
10965export const runHead = <A, E, R>(self: Stream<A, E, R>): Effect.Effect<Option.Option<A>, E, R> =>
10966 Effect.map(Channel.runHead(self.channel), Option.map(Arr.getUnsafe(0)))
10967
10968/**
10969 * Runs the stream and returns the last element as an `Option`.
10970 *
10971 * **When to use**
10972 *
10973 * Use to consume a finite stream when only the final emitted element matters.
10974 *
10975 * **Details**
10976 *
10977 * `Option.some` contains the last emitted element. `Option.none` means the
10978 * stream completed without emitting.
10979 *
10980 * **Gotchas**
10981 *
10982 * The returned effect waits for the stream to complete before it can produce a
10983 * value.
10984 *
10985 * @see {@link runHead} for consuming only the first emitted element
10986 * @see {@link runCollect} for collecting every emitted element
10987 * @see {@link runDrain} for consuming the stream while discarding emitted elements
10988 *
10989 * @category destructors
10990 * @since 2.0.0
10991 */
10992export const runLast = <A, E, R>(self: Stream<A, E, R>): Effect.Effect<Option.Option<A>, E, R> =>
10993 Effect.map(Channel.runLast(self.channel), Option.map(Arr.lastNonEmpty))
10994
10995/**
10996 * Runs the provided effectful callback for each element of the stream.
10997 *
10998 * **Example** (Running an effect for each value)
10999 *
11000 * ```ts import.meta.vitest
11001 * import { Effect, Stream } from "effect"
11002 *
11003 * const stream = Stream.make(1, 2, 3)
11004 * const values: Array<string> = []
11005 *
11006 * const program = Effect.gen(function*() {
11007 * yield* Stream.runForEach(stream, (n) => Effect.sync(() => values.push(`Processing: ${n}`)))
11008 * })
11009 *
11010 * await Effect.runPromise(program)
11011 * values // => ["Processing: 1", "Processing: 2", "Processing: 3"]
11012 * ```
11013 *
11014 * @category destructors
11015 * @since 2.0.0
11016 */
11017export const runForEach: {
11018 <A, X, E2, R2>(
11019 f: (a: A) => Effect.Effect<X, E2, R2>
11020 ): <E, R>(self: Stream<A, E, R>) => Effect.Effect<void, E2 | E, R2 | R>
11021 <A, E, R, X, E2, R2>(
11022 self: Stream<A, E, R>,
11023 f: (a: A) => Effect.Effect<X, E2, R2>
11024 ): Effect.Effect<void, E | E2, R | R2>
11025} = dual(2, <A, E, R, X, E2, R2>(
11026 self: Stream<A, E, R>,
11027 f: (a: A) => Effect.Effect<X, E2, R2>
11028): Effect.Effect<void, E | E2, R | R2> =>
11029 Channel.runForEach(self.channel, (arr) => {
11030 let i = 0
11031 return Effect.whileLoop({
11032 while: () => i < arr.length,
11033 body: () => f(arr[i++]),
11034 step: constVoid
11035 })
11036 }))
11037
11038/**
11039 * Runs the stream, applying the effectful predicate to each element and
11040 * stopping when it returns `false`.
11041 *
11042 * **Example** (Running effects while a predicate holds)
11043 *
11044 * ```ts import.meta.vitest
11045 * import { Effect, Stream } from "effect"
11046 *
11047 * const values: Array<number> = []
11048 * const program = Effect.gen(function*() {
11049 * const stream = Stream.make(1, 2, 3, 4, 5)
11050 *
11051 * yield* Stream.runForEachWhile(stream, (n) =>
11052 * Effect.gen(function*() {
11053 * yield* Effect.sync(() => values.push(n))
11054 * return n < 3
11055 * })
11056 * )
11057 * })
11058 *
11059 * await Effect.runPromise(program)
11060 * values // => [1, 2, 3]
11061 * ```
11062 *
11063 * @category destructors
11064 * @since 2.0.0
11065 */
11066export const runForEachWhile: {
11067 <A, E2, R2>(
11068 f: (a: A) => Effect.Effect<boolean, E2, R2>
11069 ): <E, R>(self: Stream<A, E, R>) => Effect.Effect<void, E2 | E, R2 | R>
11070 <A, E, R, E2, R2>(
11071 self: Stream<A, E, R>,
11072 f: (a: A) => Effect.Effect<boolean, E2, R2>
11073 ): Effect.Effect<void, E | E2, R | R2>
11074} = dual(2, <A, E, R, E2, R2>(
11075 self: Stream<A, E, R>,
11076 f: (a: A) => Effect.Effect<boolean, E2, R2>
11077): Effect.Effect<void, E | E2, R | R2> =>
11078 Channel.runForEachWhile(self.channel, (arr) => {
11079 let done = false
11080 let i = 0
11081 return Effect.map(
11082 Effect.whileLoop({
11083 while: () => !done && i < arr.length,
11084 body: () => f(arr[i]),
11085 step(b) {
11086 i++
11087 if (!b) done = true
11088 }
11089 }),
11090 () => !done
11091 )
11092 }))
11093
11094/**
11095 * Consumes the stream in chunks, passing each non-empty array to the callback.
11096 *
11097 * **Example** (Consuming stream chunks)
11098 *
11099 * ```ts import.meta.vitest
11100 * import { Effect, Stream } from "effect"
11101 *
11102 * const stream = Stream.make(1, 2, 3, 4, 5)
11103 * const chunks: Array<string> = []
11104 * const program = Effect.gen(function*() {
11105 * yield* Stream.runForEachArray(
11106 * stream,
11107 * (chunk) => Effect.sync(() => chunks.push(chunk.join(", ")))
11108 * )
11109 * })
11110 *
11111 * await Effect.runPromise(program)
11112 * chunks // => ["1, 2, 3, 4, 5"]
11113 * ```
11114 *
11115 * @category destructors
11116 * @since 4.0.0
11117 */
11118export const runForEachArray: {
11119 <A, X, E2, R2>(
11120 f: (a: Arr.NonEmptyReadonlyArray<A>) => Effect.Effect<X, E2, R2>
11121 ): <E, R>(self: Stream<A, E, R>) => Effect.Effect<void, E2 | E, R2 | R>
11122 <A, E, R, X, E2, R2>(
11123 self: Stream<A, E, R>,
11124 f: (a: Arr.NonEmptyReadonlyArray<A>) => Effect.Effect<X, E2, R2>
11125 ): Effect.Effect<void, E | E2, R | R2>
11126} = dual(2, <A, E, R, X, E2, R2>(
11127 self: Stream<A, E, R>,
11128 f: (a: Arr.NonEmptyReadonlyArray<A>) => Effect.Effect<X, E2, R2>
11129): Effect.Effect<void, E | E2, R | R2> => Channel.runForEach(self.channel, f))
11130
11131/**
11132 * Runs the stream for its effects, discarding emitted elements.
11133 *
11134 * **Example** (Draining a stream run)
11135 *
11136 * ```ts import.meta.vitest
11137 * import { Effect, Stream } from "effect"
11138 *
11139 * const values: Array<number> = []
11140 * const program = Effect.gen(function*() {
11141 * const stream = Stream.make(1, 2, 3).pipe(
11142 * Stream.mapEffect((n) => Effect.sync(() => values.push(n)))
11143 * )
11144 *
11145 * yield* Stream.runDrain(stream)
11146 * })
11147 *
11148 * await Effect.runPromise(program)
11149 * values // => [1, 2, 3]
11150 * ```
11151 *
11152 * @category destructors
11153 * @since 2.0.0
11154 */
11155export const runDrain = <A, E, R>(self: Stream<A, E, R>): Effect.Effect<void, E, R> => Channel.runDrain(self.channel)
11156
11157/**
11158 * Returns a scoped pull for manually consuming the stream's output chunks.
11159 *
11160 * **Details**
11161 *
11162 * The pull fails with `Cause.Done` when the stream ends and with the stream
11163 * error on failure.
11164 *
11165 * **Example** (Creating a scoped pull)
11166 *
11167 * ```ts import.meta.vitest
11168 * import { Effect, Stream } from "effect"
11169 *
11170 * const stream = Stream.make(1, 2, 3)
11171 *
11172 * const program = Effect.scoped(
11173 * Effect.gen(function*() {
11174 * const pull = yield* Stream.toPull(stream)
11175 * const chunk = yield* pull
11176 * chunk // => [ 1, 2, 3 ]
11177 * })
11178 * )
11179 *
11180 * await Effect.runPromise(program)
11181 * ```
11182 *
11183 * @category destructors
11184 * @since 2.0.0
11185 */
11186export const toPull = <A, E, R>(
11187 self: Stream<A, E, R>
11188): Effect.Effect<Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E>, never, R | Scope.Scope> => Channel.toPull(self.channel)
11189
11190/**
11191 * Concatenates all emitted strings into a single string.
11192 *
11193 * **Example** (Joining strings from a stream)
11194 *
11195 * ```ts import.meta.vitest
11196 * import { Effect, Stream } from "effect"
11197 *
11198 * const stream = Stream.make("Hello", " ", "World", "!")
11199 * const program = Effect.gen(function*() {
11200 * const text = yield* Stream.mkString(stream)
11201 * text // => "Hello World!"
11202 * })
11203 *
11204 * await Effect.runPromise(program)
11205 * ```
11206 *
11207 * @category destructors
11208 * @since 2.0.0
11209 */
11210export const mkString = <E, R>(self: Stream<string, E, R>): Effect.Effect<string, E, R> =>
11211 Channel.runFold(
11212 self.channel,
11213 () => "",
11214 (acc, chunk) => acc + chunk.join("")
11215 )
11216
11217/**
11218 * Concatenates the stream's `Uint8Array` chunks into a single `ArrayBuffer`.
11219 *
11220 * **Gotchas**
11221 *
11222 * This materializes the full content in memory. The source stream must not
11223 * reuse or mutate emitted buffers, which are retained until collection completes.
11224 *
11225 * **Example** (Joining byte chunks into an ArrayBuffer)
11226 *
11227 * ```ts import.meta.vitest
11228 * import { Effect, Stream } from "effect"
11229 *
11230 * const program = Stream.make(
11231 * new Uint8Array([1, 2]),
11232 * new Uint8Array([3, 4])
11233 * ).pipe(
11234 * Stream.mkArrayBuffer,
11235 * Effect.map((buffer) => [...new Uint8Array(buffer)])
11236 * )
11237 *
11238 * await Effect.runPromise(program) // => [1, 2, 3, 4]
11239 * ```
11240 *
11241 * @category destructors
11242 * @since 4.0.0
11243 */
11244export const mkArrayBuffer = <E, R>(self: Stream<Uint8Array, E, R>): Effect.Effect<ArrayBuffer, E, R> =>
11245 Effect.map(Channel.mkUint8Array(self.channel), (bytes) => bytes.buffer)
11246
11247/**
11248 * Concatenates the stream's `Uint8Array` chunks into a single `Uint8Array`.
11249 *
11250 * **Gotchas**
11251 *
11252 * This materializes the full content in memory. The source stream must not
11253 * reuse or mutate emitted buffers, which are retained until collection completes.
11254 *
11255 * **Example** (Joining Uint8Array chunks)
11256 *
11257 * ```ts import.meta.vitest
11258 * import { Effect, Stream } from "effect"
11259 *
11260 * const stream = Stream.make(new Uint8Array([1, 2]), new Uint8Array([3, 4]))
11261 * const program = Effect.gen(function*() {
11262 * const bytes = yield* Stream.mkUint8Array(stream)
11263 * const values = Array.from(bytes) // => [1, 2, 3, 4]
11264 * })
11265 *
11266 * await Effect.runPromise(program)
11267 * ```
11268 *
11269 * @category destructors
11270 * @since 4.0.0
11271 */
11272export const mkUint8Array = <E, R>(self: Stream<Uint8Array, E, R>): Effect.Effect<Uint8Array, E, R> =>
11273 Channel.mkUint8Array(self.channel)
11274
11275/**
11276 * Converts the stream to a `ReadableStream` using the provided services.
11277 *
11278 * **When to use**
11279 *
11280 * Use when bridging to Web Streams and you already have the `Context` required
11281 * to run the stream outside an `Effect`.
11282 *
11283 * **Details**
11284 *
11285 * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream.
11286 *
11287 * **Example** (Converting to a ReadableStream with services)
11288 *
11289 * ```ts import.meta.vitest
11290 * import { Context, Stream } from "effect"
11291 *
11292 * const stream = Stream.make(1, 2, 3, 4, 5)
11293 * const readableStream = Stream.toReadableStreamWith(stream, Context.empty())
11294 * const values = await Array.fromAsync(readableStream)
11295 * values // => [ 1, 2, 3, 4, 5 ]
11296 * ```
11297 *
11298 * @category destructors
11299 * @since 4.0.0
11300 */
11301export const toReadableStreamWith: {
11302 <A, XR>(
11303 context: Context.Context<XR>,
11304 options?: { readonly strategy?: QueuingStrategy<A> | undefined }
11305 ): <E, R extends XR>(self: Stream<A, E, R>) => ReadableStream<A>
11306 <A, E, XR, R extends XR>(
11307 self: Stream<A, E, R>,
11308 context: Context.Context<XR>,
11309 options?: { readonly strategy?: QueuingStrategy<A> | undefined }
11310 ): ReadableStream<A>
11311} = dual(
11312 (args) => isStream(args[0]),
11313 <A, E, XR, R extends XR>(
11314 self: Stream<A, E, R>,
11315 context: Context.Context<XR>,
11316 options?: { readonly strategy?: QueuingStrategy<A> | undefined }
11317 ): ReadableStream<A> => {
11318 let currentResolve: (() => void) | undefined = undefined
11319 let fiber: Fiber.Fiber<void, E> | undefined = undefined
11320 const latch = Latch.makeUnsafe(false)
11321
11322 return new ReadableStream<A>({
11323 start(controller) {
11324 fiber = Effect.runFork(Effect.provideContext(
11325 runForEachArray(self, (chunk) =>
11326 latch.whenOpen(Effect.sync(() => {
11327 latch.closeUnsafe()
11328 for (let i = 0; i < chunk.length; i++) {
11329 controller.enqueue(chunk[i])
11330 }
11331 currentResolve!()
11332 currentResolve = undefined
11333 }))),
11334 context
11335 ))
11336 fiber.addObserver((exit) => {
11337 if (exit._tag === "Failure") {
11338 controller.error(Cause.squash(exit.cause))
11339 } else {
11340 controller.close()
11341 }
11342 })
11343 },
11344 pull() {
11345 return new Promise<void>((resolve) => {
11346 currentResolve = resolve
11347 latch.openUnsafe()
11348 })
11349 },
11350 cancel() {
11351 if (!fiber) return
11352 return Effect.runPromise(Effect.asVoid(Fiber.interrupt(fiber)))
11353 }
11354 }, options?.strategy)
11355 }
11356)
11357
11358/**
11359 * Converts a stream to a `ReadableStream`.
11360 *
11361 * **Details**
11362 *
11363 * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream.
11364 *
11365 * **Example** (Converting a stream to a ReadableStream)
11366 *
11367 * ```ts import.meta.vitest
11368 * import { Stream } from "effect"
11369 *
11370 * const readableStream = Stream.toReadableStream(Stream.make(1, 2, 3))
11371 * const values = await Array.fromAsync(readableStream)
11372 * values // => [ 1, 2, 3 ]
11373 * ```
11374 *
11375 * @category destructors
11376 * @since 2.0.0
11377 */
11378export const toReadableStream: {
11379 <A>(
11380 options?: { readonly strategy?: QueuingStrategy<A> | undefined }
11381 ): <E>(
11382 self: Stream<A, E>
11383 ) => ReadableStream<A>
11384 <A, E>(
11385 self: Stream<A, E>,
11386 options?: { readonly strategy?: QueuingStrategy<A> | undefined }
11387 ): ReadableStream<A>
11388} = dual(
11389 (args) => isStream(args[0]),
11390 <A, E>(
11391 self: Stream<A, E>,
11392 options?: { readonly strategy?: QueuingStrategy<A> | undefined }
11393 ): ReadableStream<A> => toReadableStreamWith(self, Context.empty(), options)
11394)
11395
11396/**
11397 * Creates an Effect that builds a ReadableStream from the stream.
11398 *
11399 * **When to use**
11400 *
11401 * Use when bridging to Web Streams from inside an `Effect` so the required
11402 * services can be captured from the current context.
11403 *
11404 * **Details**
11405 *
11406 * See https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream.
11407 *
11408 * **Example** (Creating a ReadableStream effect)
11409 *
11410 * ```ts import.meta.vitest
11411 * import { Effect, Stream } from "effect"
11412 *
11413 * const stream = Stream.make(1, 2, 3, 4, 5)
11414 *
11415 * const effect = Effect.gen(function*() {
11416 * const readableStream = yield* Stream.toReadableStreamEffect(stream)
11417 * readableStream instanceof ReadableStream // => true
11418 * })
11419 *
11420 * await Effect.runPromise(effect)
11421 * ```
11422 *
11423 * @category destructors
11424 * @since 2.0.0
11425 */
11426export const toReadableStreamEffect: {
11427 <A>(
11428 options?: { readonly strategy?: QueuingStrategy<A> | undefined }
11429 ): <E, R>(
11430 self: Stream<A, E, R>
11431 ) => Effect.Effect<ReadableStream<A>, never, R>
11432 <A, E, R>(
11433 self: Stream<A, E, R>,
11434 options?: { readonly strategy?: QueuingStrategy<A> | undefined }
11435 ): Effect.Effect<ReadableStream<A>, never, R>
11436} = dual(
11437 (args) => isStream(args[0]),
11438 <A, E, R>(
11439 self: Stream<A, E, R>,
11440 options?: { readonly strategy?: QueuingStrategy<A> | undefined }
11441 ): Effect.Effect<ReadableStream<A>, never, R> =>
11442 Effect.map(
11443 Effect.context<R>(),
11444 (context) => toReadableStreamWith(self, context, options)
11445 )
11446)
11447
11448/**
11449 * Converts the stream to an `AsyncIterable` using the provided services.
11450 *
11451 * **When to use**
11452 *
11453 * Use when converting outside an Effect and you already have the `Context`
11454 * needed to run the stream.
11455 *
11456 * **Example** (Converting to an AsyncIterable with services)
11457 *
11458 * ```ts import.meta.vitest
11459 * import { Context, Stream } from "effect"
11460 *
11461 * const stream = Stream.make(1, 2, 3)
11462 * const iterable = Stream.toAsyncIterableWith(stream, Context.empty())
11463 *
11464 * await Array.fromAsync(iterable) // => [1, 2, 3]
11465 * ```
11466 *
11467 * @category destructors
11468 * @since 4.0.0
11469 */
11470export const toAsyncIterableWith: {
11471 <XR>(context: Context.Context<XR>): <A, E, R extends XR>(self: Stream<A, E, R>) => AsyncIterable<A>
11472 <A, E, XR, R extends XR>(
11473 self: Stream<A, E, R>,
11474 context: Context.Context<XR>
11475 ): AsyncIterable<A>
11476} = dual(
11477 2,
11478 <A, E, XR, R extends XR>(
11479 self: Stream<A, E, R>,
11480 context: Context.Context<XR>
11481 ): AsyncIterable<A> => ({
11482 [Symbol.asyncIterator]() {
11483 const runPromise = Effect.runPromiseWith(context)
11484 const runFork = Effect.runForkWith(context)
11485 const scope = Scope.makeUnsafe()
11486 let pull: Pull.Pull<Arr.NonEmptyReadonlyArray<A>, E, void, R> | undefined
11487 let currentIter: Iterator<A> | undefined
11488 let currentFiber: Fiber.Fiber<Arr.NonEmptyReadonlyArray<A>, E | Cause.Done<void>> | undefined
11489 let closePromise: Promise<IteratorResult<A>> | undefined
11490 const close = (exit: Exit.Exit<unknown, unknown>): Promise<IteratorResult<A>> => {
11491 if (closePromise) return closePromise
11492 const fiber = currentFiber
11493 closePromise = runPromise(Effect.as(
11494 Effect.andThen(
11495 fiber ? Fiber.interrupt(fiber) : Effect.void,
11496 Scope.close(scope, exit)
11497 ),
11498 { done: true, value: undefined }
11499 ))
11500 return closePromise
11501 }
11502 const closeAndReportError = async (exit: Exit.Exit<unknown, unknown>): Promise<void> => {
11503 try {
11504 await close(exit)
11505 } catch (error) {
11506 await runPromise(Effect.logError("Suppressed error while closing Stream async iterator", error))
11507 }
11508 }
11509 return {
11510 async next(): Promise<IteratorResult<A>> {
11511 if (closePromise) return closePromise
11512 if (currentIter) {
11513 const next = currentIter.next()
11514 if (!next.done) return next
11515 currentIter = undefined
11516 }
11517 const fiber = runFork(
11518 pull ??
11519 Effect.flatMap(Channel.toPullScoped(self.channel, scope), (nextPull) => {
11520 pull = nextPull
11521 return nextPull
11522 })
11523 )
11524 currentFiber = fiber
11525 const exit = await runPromise(Fiber.await(fiber))
11526 if (currentFiber === fiber) {
11527 currentFiber = undefined
11528 }
11529 if (Exit.isSuccess(exit)) {
11530 currentIter = exit.value[Symbol.iterator]()
11531 return currentIter.next()
11532 } else if (Pull.isDoneCause(exit.cause)) {
11533 return close(Exit.void)
11534 }
11535 if (closePromise && Cause.hasInterruptsOnly(exit.cause)) {
11536 return closePromise
11537 }
11538 await closeAndReportError(exit)
11539 throw Cause.squash(exit.cause)
11540 },
11541 return() {
11542 return close(Exit.void)
11543 },
11544 async throw(error) {
11545 await closeAndReportError(Exit.die(error))
11546 throw error
11547 }
11548 }
11549 }
11550 })
11551)
11552
11553/**
11554 * Creates an effect that yields an `AsyncIterable` using the current services.
11555 *
11556 * **When to use**
11557 *
11558 * Use when the `AsyncIterable` should be created inside Effect with the current
11559 * context supplying the stream's services.
11560 *
11561 * **Example** (Creating an AsyncIterable effect)
11562 *
11563 * ```ts import.meta.vitest
11564 * import { Effect, Stream } from "effect"
11565 *
11566 * const stream = Stream.make(1, 2, 3)
11567 *
11568 * const program = Effect.gen(function*() {
11569 * const iterable = yield* Stream.toAsyncIterableEffect(stream)
11570 * return yield* Effect.promise(() => Array.fromAsync(iterable))
11571 * })
11572 *
11573 * await Effect.runPromise(program) // => [1, 2, 3]
11574 * ```
11575 *
11576 * @category destructors
11577 * @since 3.15.0
11578 */
11579export const toAsyncIterableEffect = <A, E, R>(self: Stream<A, E, R>): Effect.Effect<AsyncIterable<A>, never, R> =>
11580 Effect.map(
11581 Effect.context<R>(),
11582 (context) => toAsyncIterableWith(self, context)
11583 )
11584
11585/**
11586 * Converts a stream to an `AsyncIterable` for `for await...of` consumption.
11587 *
11588 * **Example** (Converting to an async iterable)
11589 *
11590 * ```ts import.meta.vitest
11591 * import { Stream } from "effect"
11592 *
11593 * const stream = Stream.make(1, 2, 3)
11594 *
11595 * await Array.fromAsync(Stream.toAsyncIterable(stream)) // => [1, 2, 3]
11596 * ```
11597 *
11598 * @category destructors
11599 * @since 3.15.0
11600 */
11601export const toAsyncIterable = <A, E>(self: Stream<A, E>): AsyncIterable<A> =>
11602 toAsyncIterableWith(self, Context.empty())
11603
11604/**
11605 * Runs the stream, publishing elements into the provided PubSub.
11606 *
11607 * **Details**
11608 *
11609 * `shutdownOnEnd` controls whether the PubSub is shut down when the stream ends.
11610 * It only shuts down when set to `true`.
11611 *
11612 * **Example** (Running a stream into a PubSub)
11613 *
11614 * ```ts import.meta.vitest
11615 * import { Effect, PubSub, Stream } from "effect"
11616 *
11617 * const program = Effect.scoped(Effect.gen(function* () {
11618 * const pubsub = yield* PubSub.unbounded<number>()
11619 * const subscription = yield* PubSub.subscribe(pubsub)
11620 *
11621 * yield* Stream.runIntoPubSub(Stream.fromIterable([1, 2]), pubsub)
11622 *
11623 * const first = yield* PubSub.take(subscription)
11624 * const second = yield* PubSub.take(subscription)
11625 *
11626 * first // => 1
11627 * second // => 2
11628 * }))
11629 *
11630 * await Effect.runPromise(program)
11631 * ```
11632 *
11633 * @category destructors
11634 * @since 2.0.0
11635 */
11636export const runIntoPubSub: {
11637 <A>(
11638 pubsub: PubSub.PubSub<A>,
11639 options?: {
11640 readonly shutdownOnEnd?: boolean | undefined
11641 } | undefined
11642 ): <E, R>(self: Stream<A, E, R>) => Effect.Effect<void, E, R>
11643 <A, E, R>(
11644 self: Stream<A, E, R>,
11645 pubsub: PubSub.PubSub<A>,
11646 options?: {
11647 readonly shutdownOnEnd?: boolean | undefined
11648 } | undefined
11649 ): Effect.Effect<void, E, R>
11650} = dual((args) => isStream(args[0]), <A, E, R>(
11651 self: Stream<A, E, R>,
11652 pubsub: PubSub.PubSub<A>,
11653 options?: {
11654 readonly shutdownOnEnd?: boolean | undefined
11655 } | undefined
11656): Effect.Effect<void, E, R> => Channel.runIntoPubSubArray(self.channel, pubsub, options))
11657
11658/**
11659 * Converts a stream to a PubSub of emitted values for concurrent consumption.
11660 *
11661 * **Details**
11662 *
11663 * `shutdownOnEnd` indicates whether the PubSub should be shut down when the
11664 * stream ends. By default this is `true`.
11665 *
11666 * **Example** (Converting a stream to a PubSub for concurrent consumption)
11667 *
11668 * ```ts import.meta.vitest
11669 * import { Effect, PubSub, Stream } from "effect"
11670 *
11671 * const program = Effect.scoped(Effect.gen(function* () {
11672 * const pubsub = yield* Stream.fromArray([1, 2]).pipe(
11673 * Stream.toPubSub({ capacity: 8 })
11674 * )
11675 * const subscription = yield* PubSub.subscribe(pubsub)
11676 * const first = yield* PubSub.take(subscription)
11677 *
11678 * first // => 1
11679 * }))
11680 * await Effect.runPromise(program)
11681 * ```
11682 *
11683 * @category destructors
11684 * @since 2.0.0
11685 */
11686export const toPubSub: {
11687 (
11688 options: {
11689 readonly capacity: "unbounded"
11690 readonly replay?: number | undefined
11691 readonly shutdownOnEnd?: boolean | undefined
11692 } | {
11693 readonly capacity: number
11694 readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
11695 readonly replay?: number | undefined
11696 readonly shutdownOnEnd?: boolean | undefined
11697 }
11698 ): <A, E, R>(self: Stream<A, E, R>) => Effect.Effect<PubSub.PubSub<A>, never, R | Scope.Scope>
11699 <A, E, R>(
11700 self: Stream<A, E, R>,
11701 options: {
11702 readonly capacity: "unbounded"
11703 readonly replay?: number | undefined
11704 readonly shutdownOnEnd?: boolean | undefined
11705 } | {
11706 readonly capacity: number
11707 readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
11708 readonly replay?: number | undefined
11709 readonly shutdownOnEnd?: boolean | undefined
11710 }
11711 ): Effect.Effect<PubSub.PubSub<A>, never, R | Scope.Scope>
11712} = dual(
11713 2,
11714 <A, E, R>(
11715 self: Stream<A, E, R>,
11716 options: {
11717 readonly capacity: "unbounded"
11718 readonly replay?: number | undefined
11719 readonly shutdownOnEnd?: boolean | undefined
11720 } | {
11721 readonly capacity: number
11722 readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
11723 readonly replay?: number | undefined
11724 readonly shutdownOnEnd?: boolean | undefined
11725 }
11726 ): Effect.Effect<PubSub.PubSub<A>, never, R | Scope.Scope> => Channel.toPubSubArray(self.channel, options)
11727)
11728
11729/**
11730 * Converts a stream to a PubSub of `Take` values for concurrent consumption.
11731 *
11732 * **Details**
11733 *
11734 * Chunks are published as `Take` values. When the stream ends, the PubSub is
11735 * ended with the stream's `Exit`, so every subscriber, including one that
11736 * subscribes later, observes completion or failure after its buffered chunks.
11737 *
11738 * **Example** (Converting to a PubSub of takes)
11739 *
11740 * ```ts import.meta.vitest
11741 * import { Effect, PubSub, Stream } from "effect"
11742 *
11743 * const program = Effect.gen(function* () {
11744 * const pubsub = yield* Stream.fromArray([1, 2, 3]).pipe(
11745 * Stream.toPubSubTake({ capacity: 8 })
11746 * )
11747 * const subscription = yield* PubSub.subscribe(pubsub)
11748 * const take = yield* PubSub.take(subscription)
11749 *
11750 * if (Array.isArray(take)) {
11751 * take // => [ 1, 2, 3 ]
11752 * }
11753 * })
11754 * await Effect.runPromise(Effect.scoped(program))
11755 * ```
11756 *
11757 * @category destructors
11758 * @since 4.0.0
11759 */
11760export const toPubSubTake: {
11761 (
11762 options: {
11763 readonly capacity: "unbounded"
11764 readonly replay?: number | undefined
11765 } | {
11766 readonly capacity: number
11767 readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
11768 readonly replay?: number | undefined
11769 }
11770 ): <A, E, R>(self: Stream<A, E, R>) => Effect.Effect<PubSub.PubSub<Take.Take<A, E>>, never, R | Scope.Scope>
11771 <A, E, R>(
11772 self: Stream<A, E, R>,
11773 options: {
11774 readonly capacity: "unbounded"
11775 readonly replay?: number | undefined
11776 } | {
11777 readonly capacity: number
11778 readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
11779 readonly replay?: number | undefined
11780 }
11781 ): Effect.Effect<PubSub.PubSub<Take.Take<A, E>>, never, R | Scope.Scope>
11782} = dual(
11783 2,
11784 <A, E, R>(
11785 self: Stream<A, E, R>,
11786 options: {
11787 readonly capacity: "unbounded"
11788 readonly replay?: number | undefined
11789 } | {
11790 readonly capacity: number
11791 readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
11792 readonly replay?: number | undefined
11793 }
11794 ): Effect.Effect<PubSub.PubSub<Take.Take<A, E>>, never, R | Scope.Scope> =>
11795 Channel.toPubSubTake(self.channel, options)
11796)
11797
11798/**
11799 * Creates a scoped dequeue that is fed by the stream for concurrent
11800 * consumption.
11801 *
11802 * **Details**
11803 *
11804 * Elements are offered to the queue as the stream runs. Stream completion is
11805 * signaled with `Cause.Done`, stream failures fail the queue, and the queue is
11806 * shut down when the surrounding scope closes.
11807 *
11808 * **Example** (Converting a stream to a Queue for concurrent consumption)
11809 *
11810 * ```ts import.meta.vitest
11811 * import { Effect, Queue, Stream } from "effect"
11812 *
11813 * const program = Effect.gen(function* () {
11814 * const queue = yield* Stream.toQueue(Stream.fromIterable([1, 2, 3]), { capacity: 8 })
11815 * const chunk = yield* Queue.takeBetween(queue, 1, 3)
11816 * chunk // => [ 1, 2, 3 ]
11817 * })
11818 * await Effect.runPromise(Effect.scoped(program))
11819 * ```
11820 *
11821 * @category destructors
11822 * @since 2.0.0
11823 */
11824export const toQueue: {
11825 (
11826 options: {
11827 readonly capacity: "unbounded"
11828 } | {
11829 readonly capacity: number
11830 readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
11831 }
11832 ): <A, E, R>(self: Stream<A, E, R>) => Effect.Effect<Queue.Dequeue<A, E | Cause.Done>, never, R | Scope.Scope>
11833 <A, E, R>(
11834 self: Stream<A, E, R>,
11835 options: {
11836 readonly capacity: "unbounded"
11837 } | {
11838 readonly capacity: number
11839 readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
11840 }
11841 ): Effect.Effect<Queue.Dequeue<A, E | Cause.Done>, never, R | Scope.Scope>
11842} = dual(
11843 2,
11844 <A, E, R>(
11845 self: Stream<A, E, R>,
11846 options: {
11847 readonly capacity: "unbounded"
11848 } | {
11849 readonly capacity: number
11850 readonly strategy?: "dropping" | "sliding" | "suspend" | undefined
11851 }
11852 ): Effect.Effect<Queue.Dequeue<A, E | Cause.Done>, never, R | Scope.Scope> =>
11853 Channel.toQueueArray(self.channel, options)
11854)
11855
11856/**
11857 * Runs the stream, offering each element to the provided queue and ending it
11858 * with `Cause.Done` when the stream completes.
11859 *
11860 * **Example** (Running a stream into a queue)
11861 *
11862 * ```ts import.meta.vitest
11863 * import { Cause, Effect, Queue, Stream } from "effect"
11864 *
11865 * const program = Effect.gen(function*() {
11866 * const queue = yield* Queue.bounded<number, Cause.Done>(4)
11867 *
11868 * yield* Effect.forkChild(
11869 * Stream.runIntoQueue(Stream.fromIterable([1, 2, 3]), queue)
11870 * )
11871 *
11872 * const values = [
11873 * yield* Queue.take(queue),
11874 * yield* Queue.take(queue),
11875 * yield* Queue.take(queue)
11876 * ]
11877 * const done = yield* Effect.flip(Queue.take(queue))
11878 *
11879 * values // => [ 1, 2, 3 ]
11880 * done._tag === "Done" // => true
11881 * })
11882 * await Effect.runPromise(program)
11883 * ```
11884 *
11885 * @category destructors
11886 * @since 2.0.0
11887 */
11888export const runIntoQueue: {
11889 <A, E>(queue: Queue.Queue<A, E | Cause.Done>): <R>(self: Stream<A, E, R>) => Effect.Effect<void, never, R>
11890 <A, E, R>(self: Stream<A, E, R>, queue: Queue.Queue<A, E | Cause.Done>): Effect.Effect<void, never, R>
11891} = dual(2, <A, E, R>(
11892 self: Stream<A, E, R>,
11893 queue: Queue.Queue<A, E | Cause.Done>
11894): Effect.Effect<void, never, R> => Channel.runIntoQueueArray(self.channel, queue))