UNPKG

veffect

Version:

powerful TypeScript validation library built on the robust foundation of Effect combining exceptional type safety, high performance, and developer experience. Taking inspiration from Effect's functional principles, VEffect delivers a balanced approach tha

1,372 lines (1,328 loc) 280 kB
import * as Cause from "../Cause.js" import type * as Channel from "../Channel.js" import * as Chunk from "../Chunk.js" import * as Clock from "../Clock.js" import * as Context from "../Context.js" import * as Deferred from "../Deferred.js" import * as Duration from "../Duration.js" import * as Effect from "../Effect.js" import * as Either from "../Either.js" import * as Equal from "../Equal.js" import * as Exit from "../Exit.js" import * as Fiber from "../Fiber.js" import type { LazyArg } from "../Function.js" import { constTrue, dual, identity, pipe } from "../Function.js" import * as Layer from "../Layer.js" import * as MergeDecision from "../MergeDecision.js" import * as Option from "../Option.js" import type * as Order from "../Order.js" import { pipeArguments } from "../Pipeable.js" import { hasProperty, isTagged, type Predicate, type Refinement } from "../Predicate.js" import * as PubSub from "../PubSub.js" import * as Queue from "../Queue.js" import * as Ref from "../Ref.js" import * as Runtime from "../Runtime.js" import * as Schedule from "../Schedule.js" import * as Scope from "../Scope.js" import type * as Sink from "../Sink.js" import type * as Stream from "../Stream.js" import type * as Emit from "../StreamEmit.js" import * as HaltStrategy from "../StreamHaltStrategy.js" import type * as Take from "../Take.js" import type * as Tracer from "../Tracer.js" import * as Tuple from "../Tuple.js" import type { MergeRecord, NoInfer } from "../Types.js" import * as channel from "./channel.js" import * as channelExecutor from "./channel/channelExecutor.js" import * as MergeStrategy from "./channel/mergeStrategy.js" import * as singleProducerAsyncInput from "./channel/singleProducerAsyncInput.js" import * as core from "./core-stream.js" import { RingBuffer } from "./ringBuffer.js" import * as _sink from "./sink.js" import * as DebounceState from "./stream/debounceState.js" import * as emit from "./stream/emit.js" import * as haltStrategy from "./stream/haltStrategy.js" import * as Handoff from "./stream/handoff.js" import * as HandoffSignal from "./stream/handoffSignal.js" import * as pull from "./stream/pull.js" import * as SinkEndReason from "./stream/sinkEndReason.js" import * as ZipAllState from "./stream/zipAllState.js" import * as ZipChunksState from "./stream/zipChunksState.js" import * as InternalTake from "./take.js" /** @internal */ const StreamSymbolKey = "effect/Stream" /** @internal */ export const StreamTypeId: Stream.StreamTypeId = Symbol.for( StreamSymbolKey ) as Stream.StreamTypeId /** @internal */ const streamVariance = { _R: (_: never) => _, _E: (_: never) => _, _A: (_: never) => _ } /** @internal */ export class StreamImpl<out A, out E = never, out R = never> implements Stream.Stream<A, E, R> { readonly [StreamTypeId] = streamVariance constructor( readonly channel: Channel.Channel<Chunk.Chunk<A>, unknown, E, unknown, unknown, unknown, R> ) { } pipe() { return pipeArguments(this, arguments) } } /** @internal */ export const isStream = (u: unknown): u is Stream.Stream<unknown, unknown, unknown> => hasProperty(u, StreamTypeId) || Effect.isEffect(u) /** @internal */ export const DefaultChunkSize = 4096 /** @internal */ export const accumulate = <A, E, R>(self: Stream.Stream<A, E, R>): Stream.Stream<Chunk.Chunk<A>, E, R> => chunks(accumulateChunks(self)) /** @internal */ export const accumulateChunks = <A, E, R>(self: Stream.Stream<A, E, R>): Stream.Stream<A, E, R> => { const accumulator = ( s: Chunk.Chunk<A> ): Channel.Channel<Chunk.Chunk<A>, Chunk.Chunk<A>, E, E, void, unknown> => core.readWith({ onInput: (input: Chunk.Chunk<A>) => { const next = Chunk.appendAll(s, input) return core.flatMap( core.write(next), () => accumulator(next) ) }, onFailure: core.fail, onDone: () => core.unit }) return new StreamImpl(core.pipeTo(toChannel(self), accumulator(Chunk.empty()))) } /** @internal */ export const acquireRelease = <A, E, R, R2, X>( acquire: Effect.Effect<A, E, R>, release: (resource: A, exit: Exit.Exit<unknown, unknown>) => Effect.Effect<X, never, R2> ): Stream.Stream<A, E, R | R2> => scoped(Effect.acquireRelease(acquire, release)) /** @internal */ export const aggregate = dual< <B, A, A2, E2, R2>( sink: Sink.Sink<B, A | A2, A2, E2, R2> ) => <E, R>(self: Stream.Stream<A, E, R>) => Stream.Stream<B, E2 | E, R2 | R>, <A, E, R, B, A2, E2, R2>( self: Stream.Stream<A, E, R>, sink: Sink.Sink<B, A | A2, A2, E2, R2> ) => Stream.Stream<B, E2 | E, R2 | R> >( 2, <A, E, R, B, A2, E2, R2>( self: Stream.Stream<A, E, R>, sink: Sink.Sink<B, A | A2, A2, E2, R2> ): Stream.Stream<B, E2 | E, R2 | R> => aggregateWithin(self, sink, Schedule.forever) ) /** @internal */ export const aggregateWithin = dual< <B, A, A2, E2, R2, C, R3>( sink: Sink.Sink<B, A | A2, A2, E2, R2>, schedule: Schedule.Schedule<C, Option.Option<B>, R3> ) => <E, R>(self: Stream.Stream<A, E, R>) => Stream.Stream<B, E2 | E, R2 | R3 | R>, <A, E, R, B, A2, E2, R2, C, R3>( self: Stream.Stream<A, E, R>, sink: Sink.Sink<B, A | A2, A2, E2, R2>, schedule: Schedule.Schedule<C, Option.Option<B>, R3> ) => Stream.Stream<B, E2 | E, R2 | R3 | R> >( 3, <A, E, R, B, A2, E2, R2, C, R3>( self: Stream.Stream<A, E, R>, sink: Sink.Sink<B, A | A2, A2, E2, R2>, schedule: Schedule.Schedule<C, Option.Option<B>, R3> ): Stream.Stream<B, E2 | E, R2 | R3 | R> => filterMap( aggregateWithinEither(self, sink, schedule), (_) => Either.match(_, { onLeft: Option.none, onRight: Option.some }) ) ) /** @internal */ export const aggregateWithinEither = dual< <B, A, A2, E2, R2, C, R3>( sink: Sink.Sink<B, A | A2, A2, E2, R2>, schedule: Schedule.Schedule<C, Option.Option<B>, R3> ) => <E, R>(self: Stream.Stream<A, E, R>) => Stream.Stream<Either.Either<B, C>, E2 | E, R2 | R3 | R>, <A, E, R, B, A2, E2, R2, C, R3>( self: Stream.Stream<A, E, R>, sink: Sink.Sink<B, A | A2, A2, E2, R2>, schedule: Schedule.Schedule<C, Option.Option<B>, R3> ) => Stream.Stream<Either.Either<B, C>, E2 | E, R2 | R3 | R> >( 3, <A, E, R, B, A2, E2, R2, C, R3>( self: Stream.Stream<A, E, R>, sink: Sink.Sink<B, A | A2, A2, E2, R2>, schedule: Schedule.Schedule<C, Option.Option<B>, R3> ): Stream.Stream<Either.Either<B, C>, E2 | E, R2 | R3 | R> => { const layer = Effect.all([ Handoff.make<HandoffSignal.HandoffSignal<A, E | E2>>(), Ref.make<SinkEndReason.SinkEndReason>(SinkEndReason.ScheduleEnd), Ref.make(Chunk.empty<A | A2>()), Schedule.driver(schedule), Ref.make(false), Ref.make(false) ]) return pipe( fromEffect(layer), flatMap(([handoff, sinkEndReason, sinkLeftovers, scheduleDriver, consumed, endAfterEmit]) => { const handoffProducer: Channel.Channel<never, Chunk.Chunk<A>, never, E | E2, unknown, unknown> = core .readWithCause({ onInput: (input: Chunk.Chunk<A>) => core.flatMap( core.fromEffect(pipe( handoff, Handoff.offer<HandoffSignal.HandoffSignal<A, E | E2>>(HandoffSignal.emit(input)), Effect.when(() => Chunk.isNonEmpty(input)) )), () => handoffProducer ), onFailure: (cause) => core.fromEffect( Handoff.offer<HandoffSignal.HandoffSignal<A, E | E2>>( handoff, HandoffSignal.halt(cause) ) ), onDone: () => core.fromEffect( Handoff.offer<HandoffSignal.HandoffSignal<A, E | E2>>( handoff, HandoffSignal.end(SinkEndReason.UpstreamEnd) ) ) }) const handoffConsumer: Channel.Channel<Chunk.Chunk<A | A2>, unknown, E | E2, unknown, void, unknown> = pipe( Ref.getAndSet(sinkLeftovers, Chunk.empty()), Effect.flatMap((leftovers) => { if (Chunk.isNonEmpty(leftovers)) { return pipe( Ref.set(consumed, true), Effect.zipRight(Effect.succeed(pipe( core.write(leftovers), core.flatMap(() => handoffConsumer) ))) ) } return pipe( Handoff.take(handoff), Effect.map((signal) => { switch (signal._tag) { case HandoffSignal.OP_EMIT: { return pipe( core.fromEffect(Ref.set(consumed, true)), channel.zipRight(core.write(signal.elements)), channel.zipRight(core.fromEffect(Ref.get(endAfterEmit))), core.flatMap((bool) => bool ? core.unit : handoffConsumer) ) } case HandoffSignal.OP_HALT: { return core.failCause(signal.cause) } case HandoffSignal.OP_END: { if (signal.reason._tag === SinkEndReason.OP_SCHEDULE_END) { return pipe( Ref.get(consumed), Effect.map((bool) => bool ? core.fromEffect( pipe( Ref.set(sinkEndReason, SinkEndReason.ScheduleEnd), Effect.zipRight(Ref.set(endAfterEmit, true)) ) ) : pipe( core.fromEffect( pipe( Ref.set(sinkEndReason, SinkEndReason.ScheduleEnd), Effect.zipRight(Ref.set(endAfterEmit, true)) ) ), core.flatMap(() => handoffConsumer) ) ), channel.unwrap ) } return pipe( Ref.set<SinkEndReason.SinkEndReason>(sinkEndReason, signal.reason), Effect.zipRight(Ref.set(endAfterEmit, true)), core.fromEffect ) } } }) ) }), channel.unwrap ) const timeout = (lastB: Option.Option<B>): Effect.Effect<C, Option.Option<never>, R2 | R3> => scheduleDriver.next(lastB) const scheduledAggregator = ( sinkFiber: Fiber.RuntimeFiber<readonly [Chunk.Chunk<Chunk.Chunk<A | A2>>, B], E | E2>, scheduleFiber: Fiber.RuntimeFiber<C, Option.Option<never>>, scope: Scope.Scope ): Channel.Channel<Chunk.Chunk<Either.Either<B, C>>, unknown, E | E2, unknown, unknown, unknown, R2 | R3> => { const forkSink = pipe( Ref.set(consumed, false), Effect.zipRight(Ref.set(endAfterEmit, false)), Effect.zipRight( pipe( handoffConsumer, channel.pipeToOrFail(_sink.toChannel(sink)), core.collectElements, channelExecutor.run, Effect.forkIn(scope) ) ) ) const handleSide = ( leftovers: Chunk.Chunk<Chunk.Chunk<A | A2>>, b: B, c: Option.Option<C> ): Channel.Channel<Chunk.Chunk<Either.Either<B, C>>, unknown, E | E2, unknown, unknown, unknown, R2 | R3> => pipe( Ref.set(sinkLeftovers, Chunk.flatten(leftovers)), Effect.zipRight( Effect.map(Ref.get(sinkEndReason), (reason) => { switch (reason._tag) { case SinkEndReason.OP_SCHEDULE_END: { return pipe( Effect.all([ Ref.get(consumed), forkSink, pipe(timeout(Option.some(b)), Effect.forkIn(scope)) ]), Effect.map(([wasConsumed, sinkFiber, scheduleFiber]) => { const toWrite = pipe( c, Option.match({ onNone: (): Chunk.Chunk<Either.Either<B, C>> => Chunk.of(Either.right(b)), onSome: (c): Chunk.Chunk<Either.Either<B, C>> => Chunk.make(Either.right(b), Either.left(c)) }) ) if (wasConsumed) { return pipe( core.write(toWrite), core.flatMap(() => scheduledAggregator(sinkFiber, scheduleFiber, scope)) ) } return scheduledAggregator(sinkFiber, scheduleFiber, scope) }), channel.unwrap ) } case SinkEndReason.OP_UPSTREAM_END: { return pipe( Ref.get(consumed), Effect.map((wasConsumed) => wasConsumed ? core.write(Chunk.of<Either.Either<B, C>>(Either.right(b))) : core.unit ), channel.unwrap ) } } }) ), channel.unwrap ) return channel.unwrap( Effect.raceWith(Fiber.join(sinkFiber), Fiber.join(scheduleFiber), { onSelfDone: (sinkExit, _) => pipe( Fiber.interrupt(scheduleFiber), Effect.zipRight(pipe( Effect.suspend(() => sinkExit), Effect.map(([leftovers, b]) => handleSide(leftovers, b, Option.none())) )) ), onOtherDone: (scheduleExit, _) => Effect.matchCauseEffect(Effect.suspend(() => scheduleExit), { onFailure: (cause) => Either.match( Cause.failureOrCause(cause), { onLeft: () => pipe( handoff, Handoff.offer<HandoffSignal.HandoffSignal<A, E | E2>>( HandoffSignal.end(SinkEndReason.ScheduleEnd) ), Effect.forkDaemon, Effect.zipRight( pipe( Fiber.join(sinkFiber), Effect.map(([leftovers, b]) => handleSide(leftovers, b, Option.none())) ) ) ), onRight: (cause) => pipe( handoff, Handoff.offer<HandoffSignal.HandoffSignal<A, E | E2>>( HandoffSignal.halt(cause) ), Effect.forkDaemon, Effect.zipRight( pipe( Fiber.join(sinkFiber), Effect.map(([leftovers, b]) => handleSide(leftovers, b, Option.none())) ) ) ) } ), onSuccess: (c) => pipe( handoff, Handoff.offer<HandoffSignal.HandoffSignal<A, E | E2>>( HandoffSignal.end(SinkEndReason.ScheduleEnd) ), Effect.forkDaemon, Effect.zipRight( pipe( Fiber.join(sinkFiber), Effect.map(([leftovers, b]) => handleSide(leftovers, b, Option.some(c))) ) ) ) }) }) ) } return unwrapScoped( pipe( self, toChannel, core.pipeTo(handoffProducer), channelExecutor.run, Effect.forkScoped, Effect.zipRight( pipe( handoffConsumer, channel.pipeToOrFail(_sink.toChannel(sink)), core.collectElements, channelExecutor.run, Effect.forkScoped, Effect.flatMap((sinkFiber) => pipe( Effect.forkScoped(timeout(Option.none())), Effect.flatMap((scheduleFiber) => pipe( Effect.scope, Effect.map((scope) => new StreamImpl( scheduledAggregator(sinkFiber, scheduleFiber, scope) ) ) ) ) ) ) ) ) ) ) }) ) } ) /** @internal */ export const as = dual< <B>(value: B) => <A, E, R>(self: Stream.Stream<A, E, R>) => Stream.Stream<B, E, R>, <A, E, R, B>(self: Stream.Stream<A, E, R>, value: B) => Stream.Stream<B, E, R> >(2, <A, E, R, B>(self: Stream.Stream<A, E, R>, value: B): Stream.Stream<B, E, R> => map(self, () => value)) /** @internal */ export const _async = <A, E = never, R = never>( register: ( emit: Emit.Emit<R, E, A, void> ) => Effect.Effect<void, never, R> | void, outputBuffer = 16 ): Stream.Stream<A, E, R> => Effect.acquireRelease( Queue.bounded<Take.Take<A, E>>(outputBuffer), (queue) => Queue.shutdown(queue) ).pipe( Effect.flatMap((output) => Effect.runtime<R>().pipe( Effect.flatMap((runtime) => Effect.sync(() => { const runPromiseExit = Runtime.runPromiseExit(runtime) const canceler = register(emit.make<R, E, A, void>((resume) => InternalTake.fromPull(resume).pipe( Effect.flatMap((take) => Queue.offer(output, take)), Effect.asUnit, runPromiseExit ).then((exit) => { if (Exit.isFailure(exit)) { if (!Cause.isInterrupted(exit.cause)) { throw Cause.squash(exit.cause) } } }) )) return canceler }) ), Effect.map((value) => { const loop: Channel.Channel<Chunk.Chunk<A>, unknown, E, unknown, void, unknown> = Queue.take(output).pipe( Effect.flatMap((take) => InternalTake.done(take)), Effect.match({ onFailure: (maybeError) => core.fromEffect(Queue.shutdown(output)).pipe( channel.zipRight(Option.match(maybeError, { onNone: () => core.unit, onSome: (error) => core.fail(error) })) ), onSuccess: (chunk) => core.write(chunk).pipe(core.flatMap(() => loop)) }), channel.unwrap ) return fromChannel(loop).pipe(ensuring(value ?? Effect.unit)) }) ) ), unwrapScoped ) /** @internal */ export const asyncEffect = <A, E = never, R = never>( register: (emit: Emit.Emit<R, E, A, void>) => Effect.Effect<unknown, E, R>, outputBuffer = 16 ): Stream.Stream<A, E, R> => pipe( Effect.acquireRelease( Queue.bounded<Take.Take<A, E>>(outputBuffer), (queue) => Queue.shutdown(queue) ), Effect.flatMap((output) => pipe( Effect.runtime<R>(), Effect.flatMap((runtime) => pipe( register( emit.make((k) => pipe( InternalTake.fromPull(k), Effect.flatMap((take) => Queue.offer(output, take)), Effect.asUnit, Runtime.runPromiseExit(runtime) ).then((exit) => { if (Exit.isFailure(exit)) { if (!Cause.isInterrupted(exit.cause)) { throw Cause.squash(exit.cause) } } }) ) ), Effect.map(() => { const loop: Channel.Channel<Chunk.Chunk<A>, unknown, E, unknown, void, unknown> = pipe( Queue.take(output), Effect.flatMap(InternalTake.done), Effect.match({ onFailure: (maybeError) => pipe( core.fromEffect(Queue.shutdown(output)), channel.zipRight(Option.match(maybeError, { onNone: () => core.unit, onSome: core.fail })) ), onSuccess: (chunk) => pipe(core.write(chunk), core.flatMap(() => loop)) }), channel.unwrap ) return loop }) ) ) ) ), channel.unwrapScoped, fromChannel ) /** @internal */ export const asyncScoped = <A, E = never, R = never>( register: (emit: Emit.Emit<R, E, A, void>) => Effect.Effect<unknown, E, R | Scope.Scope>, outputBuffer = 16 ): Stream.Stream<A, E, Exclude<R, Scope.Scope>> => pipe( Effect.acquireRelease( Queue.bounded<Take.Take<A, E>>(outputBuffer), (queue) => Queue.shutdown(queue) ), Effect.flatMap((output) => pipe( Effect.runtime<R>(), Effect.flatMap((runtime) => pipe( register( emit.make((k) => pipe( InternalTake.fromPull(k), Effect.flatMap((take) => Queue.offer(output, take)), Effect.asUnit, Runtime.runPromiseExit(runtime) ).then((exit) => { if (Exit.isFailure(exit)) { if (!Cause.isInterrupted(exit.cause)) { throw Cause.squash(exit.cause) } } }) ) ), Effect.zipRight(Ref.make(false)), Effect.flatMap((ref) => pipe( Ref.get(ref), Effect.map((isDone) => isDone ? pull.end() : pipe( Queue.take(output), Effect.flatMap(InternalTake.done), Effect.onError(() => pipe( Ref.set(ref, true), Effect.zipRight(Queue.shutdown(output)) ) ) ) ) ) ) ) ) ) ), scoped, flatMap(repeatEffectChunkOption) ) /** @internal */ export const branchAfter = dual< <A, A2, E2, R2>( n: number, f: (input: Chunk.Chunk<A>) => Stream.Stream<A2, E2, R2> ) => <E, R>(self: Stream.Stream<A, E, R>) => Stream.Stream<A2, E2 | E, R2 | R>, <A, E, R, A2, E2, R2>( self: Stream.Stream<A, E, R>, n: number, f: (input: Chunk.Chunk<A>) => Stream.Stream<A2, E2, R2> ) => Stream.Stream<A2, E2 | E, R2 | R> >( 3, <A, E, R, A2, E2, R2>( self: Stream.Stream<A, E, R>, n: number, f: (input: Chunk.Chunk<A>) => Stream.Stream<A2, E2, R2> ) => suspend(() => { const buffering = ( acc: Chunk.Chunk<A> ): Channel.Channel<Chunk.Chunk<A2>, Chunk.Chunk<A>, E2, never, unknown, unknown, R | R2> => core.readWith({ onInput: (input) => { const nextSize = acc.length + input.length if (nextSize >= n) { const [b1, b2] = pipe(input, Chunk.splitAt(n - acc.length)) return running(pipe(acc, Chunk.appendAll(b1)), b2) } return buffering(pipe(acc, Chunk.appendAll(input))) }, onFailure: core.fail, onDone: () => running(acc, Chunk.empty()) }) const running = ( prefix: Chunk.Chunk<A>, leftover: Chunk.Chunk<A> ): Channel.Channel<Chunk.Chunk<A2>, Chunk.Chunk<A>, E2, never, unknown, unknown, R | R2> => core.pipeTo( channel.zipRight( core.write(leftover), channel.identityChannel() ), toChannel(f(prefix)) ) return new StreamImpl(pipe(toChannel(self), channel.pipeToOrFail(buffering(Chunk.empty<A>())))) }) ) /** @internal */ export const broadcast = dual< <N extends number>( n: N, maximumLag: number ) => <A, E, R>( self: Stream.Stream<A, E, R> ) => Effect.Effect<Stream.Stream.DynamicTuple<Stream.Stream<A, E>, N>, never, Scope.Scope | R>, <A, E, R, N extends number>( self: Stream.Stream<A, E, R>, n: N, maximumLag: number ) => Effect.Effect<Stream.Stream.DynamicTuple<Stream.Stream<A, E>, N>, never, Scope.Scope | R> >(3, <A, E, R, N extends number>( self: Stream.Stream<A, E, R>, n: N, maximumLag: number ): Effect.Effect<Stream.Stream.DynamicTuple<Stream.Stream<A, E>, N>, never, Scope.Scope | R> => pipe( self, broadcastedQueues(n, maximumLag), Effect.map((tuple) => tuple.map((queue) => flattenTake(fromQueue(queue, { shutdown: true }))) as Stream.Stream.DynamicTuple< Stream.Stream<A, E>, N > ) )) /** @internal */ export const broadcastDynamic = dual< ( maximumLag: number ) => <A, E, R>(self: Stream.Stream<A, E, R>) => Effect.Effect<Stream.Stream<A, E>, never, Scope.Scope | R>, <A, E, R>( self: Stream.Stream<A, E, R>, maximumLag: number ) => Effect.Effect<Stream.Stream<A, E>, never, Scope.Scope | R> >(2, <A, E, R>( self: Stream.Stream<A, E, R>, maximumLag: number ): Effect.Effect<Stream.Stream<A, E>, never, Scope.Scope | R> => pipe( self, broadcastedQueuesDynamic(maximumLag), Effect.map((effect) => flattenTake(flatMap(scoped(effect), fromQueue))) )) /** @internal */ export const broadcastedQueues = dual< <N extends number>( n: N, maximumLag: number ) => <A, E, R>( self: Stream.Stream<A, E, R> ) => Effect.Effect<Stream.Stream.DynamicTuple<Queue.Dequeue<Take.Take<A, E>>, N>, never, Scope.Scope | R>, <A, E, R, N extends number>( self: Stream.Stream<A, E, R>, n: N, maximumLag: number ) => Effect.Effect<Stream.Stream.DynamicTuple<Queue.Dequeue<Take.Take<A, E>>, N>, never, Scope.Scope | R> >(3, <A, E, R, N extends number>( self: Stream.Stream<A, E, R>, n: N, maximumLag: number ): Effect.Effect<Stream.Stream.DynamicTuple<Queue.Dequeue<Take.Take<A, E>>, N>, never, Scope.Scope | R> => Effect.flatMap(PubSub.bounded<Take.Take<A, E>>(maximumLag), (pubsub) => pipe( Effect.all(Array.from({ length: n }, () => PubSub.subscribe(pubsub))) as Effect.Effect< Stream.Stream.DynamicTuple<Queue.Dequeue<Take.Take<A, E>>, N>, never, R >, Effect.tap(() => Effect.forkScoped(runIntoPubSubScoped(self, pubsub))) ))) /** @internal */ export const broadcastedQueuesDynamic = dual< ( maximumLag: number ) => <A, E, R>( self: Stream.Stream<A, E, R> ) => Effect.Effect<Effect.Effect<Queue.Dequeue<Take.Take<A, E>>, never, Scope.Scope>, never, Scope.Scope | R>, <A, E, R>( self: Stream.Stream<A, E, R>, maximumLag: number ) => Effect.Effect<Effect.Effect<Queue.Dequeue<Take.Take<A, E>>, never, Scope.Scope>, never, Scope.Scope | R> >(2, <A, E, R>( self: Stream.Stream<A, E, R>, maximumLag: number ): Effect.Effect<Effect.Effect<Queue.Dequeue<Take.Take<A, E>>, never, Scope.Scope>, never, Scope.Scope | R> => Effect.map(toPubSub(self, maximumLag), PubSub.subscribe)) /** @internal */ export const buffer = dual< ( options: { readonly capacity: "unbounded" } | { readonly capacity: number readonly strategy?: "dropping" | "sliding" | "suspend" | undefined } ) => <A, E, R>(self: Stream.Stream<A, E, R>) => Stream.Stream<A, E, R>, <A, E, R>( self: Stream.Stream<A, E, R>, options: { readonly capacity: "unbounded" } | { readonly capacity: number readonly strategy?: "dropping" | "sliding" | "suspend" | undefined } ) => Stream.Stream<A, E, R> >(2, <A, E, R>( self: Stream.Stream<A, E, R>, options: { readonly capacity: "unbounded" } | { readonly capacity: number readonly strategy?: "dropping" | "sliding" | "suspend" | undefined } ): Stream.Stream<A, E, R> => { if (options.capacity === "unbounded") { return bufferUnbounded(self) } else if (options.strategy === "dropping") { return bufferDropping(self, options.capacity) } else if (options.strategy === "sliding") { return bufferSliding(self, options.capacity) } const queue = toQueueOfElements(self, options) return new StreamImpl( channel.unwrapScoped( Effect.map(queue, (queue) => { const process: Channel.Channel<Chunk.Chunk<A>, unknown, E, unknown, void, unknown> = pipe( core.fromEffect(Queue.take(queue)), core.flatMap(Exit.match({ onFailure: (cause) => pipe( Cause.flipCauseOption(cause), Option.match({ onNone: () => core.unit, onSome: core.failCause }) ), onSuccess: (value) => core.flatMap(core.write(Chunk.of(value)), () => process) })) ) return process }) ) ) }) /** @internal */ export const bufferChunks = dual< (options: { readonly capacity: number readonly strategy?: "dropping" | "sliding" | "suspend" | undefined }) => <A, E, R>(self: Stream.Stream<A, E, R>) => Stream.Stream<A, E, R>, <A, E, R>(self: Stream.Stream<A, E, R>, options: { readonly capacity: number readonly strategy?: "dropping" | "sliding" | "suspend" | undefined }) => Stream.Stream<A, E, R> >(2, <A, E, R>(self: Stream.Stream<A, E, R>, options: { readonly capacity: number readonly strategy?: "dropping" | "sliding" | "suspend" | undefined }): Stream.Stream<A, E, R> => { if (options.strategy === "dropping") { return bufferChunksDropping(self, options.capacity) } else if (options.strategy === "sliding") { return bufferChunksSliding(self, options.capacity) } const queue = toQueue(self, options) return new StreamImpl( channel.unwrapScoped( Effect.map(queue, (queue) => { const process: Channel.Channel<Chunk.Chunk<A>, unknown, E, unknown, void, unknown> = pipe( core.fromEffect(Queue.take(queue)), core.flatMap(InternalTake.match({ onEnd: () => core.unit, onFailure: core.failCause, onSuccess: (value) => pipe(core.write(value), core.flatMap(() => process)) })) ) return process }) ) ) }) const bufferChunksDropping = dual< (capacity: number) => <A, E, R>(self: Stream.Stream<A, E, R>) => Stream.Stream<A, E, R>, <A, E, R>(self: Stream.Stream<A, E, R>, capacity: number) => Stream.Stream<A, E, R> >(2, <A, E, R>(self: Stream.Stream<A, E, R>, capacity: number): Stream.Stream<A, E, R> => { const queue = Effect.acquireRelease( Queue.dropping<readonly [Take.Take<A, E>, Deferred.Deferred<void>]>(capacity), (queue) => Queue.shutdown(queue) ) return new StreamImpl(bufferSignal(queue, toChannel(self))) }) const bufferChunksSliding = dual< (capacity: number) => <A, E, R>(self: Stream.Stream<A, E, R>) => Stream.Stream<A, E, R>, <A, E, R>(self: Stream.Stream<A, E, R>, capacity: number) => Stream.Stream<A, E, R> >(2, <A, E, R>(self: Stream.Stream<A, E, R>, capacity: number): Stream.Stream<A, E, R> => { const queue = Effect.acquireRelease( Queue.sliding<readonly [Take.Take<A, E>, Deferred.Deferred<void>]>(capacity), (queue) => Queue.shutdown(queue) ) return new StreamImpl(bufferSignal(queue, toChannel(self))) }) const bufferDropping = dual< (capacity: number) => <A, E, R>(self: Stream.Stream<A, E, R>) => Stream.Stream<A, E, R>, <A, E, R>(self: Stream.Stream<A, E, R>, capacity: number) => Stream.Stream<A, E, R> >(2, <A, E, R>(self: Stream.Stream<A, E, R>, capacity: number): Stream.Stream<A, E, R> => { const queue = Effect.acquireRelease( Queue.dropping<readonly [Take.Take<A, E>, Deferred.Deferred<void>]>(capacity), (queue) => Queue.shutdown(queue) ) return new StreamImpl(bufferSignal(queue, toChannel(rechunk(1)(self)))) }) const bufferSliding = dual< (capacity: number) => <A, E, R>(self: Stream.Stream<A, E, R>) => Stream.Stream<A, E, R>, <A, E, R>(self: Stream.Stream<A, E, R>, capacity: number) => Stream.Stream<A, E, R> >(2, <A, E, R>(self: Stream.Stream<A, E, R>, capacity: number): Stream.Stream<A, E, R> => { const queue = Effect.acquireRelease( Queue.sliding<readonly [Take.Take<A, E>, Deferred.Deferred<void>]>(capacity), (queue) => Queue.shutdown(queue) ) return new StreamImpl(bufferSignal(queue, toChannel(pipe(self, rechunk(1))))) }) const bufferUnbounded = <A, E, R>(self: Stream.Stream<A, E, R>): Stream.Stream<A, E, R> => { const queue = toQueue(self, { strategy: "unbounded" }) return new StreamImpl( channel.unwrapScoped( Effect.map(queue, (queue) => { const process: Channel.Channel<Chunk.Chunk<A>, unknown, E, unknown, void, unknown> = pipe( core.fromEffect(Queue.take(queue)), core.flatMap(InternalTake.match({ onEnd: () => core.unit, onFailure: core.failCause, onSuccess: (value) => core.flatMap(core.write(value), () => process) })) ) return process }) ) ) } const bufferSignal = <A, E, R>( scoped: Effect.Effect<Queue.Queue<readonly [Take.Take<A, E>, Deferred.Deferred<void>]>, never, Scope.Scope>, bufferChannel: Channel.Channel<Chunk.Chunk<A>, unknown, E, unknown, void, unknown, R> ): Channel.Channel<Chunk.Chunk<A>, unknown, E, unknown, void, unknown, R> => { const producer = ( queue: Queue.Queue<readonly [Take.Take<A, E>, Deferred.Deferred<void>]>, ref: Ref.Ref<Deferred.Deferred<void>> ): Channel.Channel<never, Chunk.Chunk<A>, never, E, unknown, unknown, R> => { const terminate = (take: Take.Take<A, E>): Channel.Channel<never, Chunk.Chunk<A>, never, E, unknown, unknown, R> => pipe( Ref.get(ref), Effect.tap(Deferred.await), Effect.zipRight(Deferred.make<void>()), Effect.flatMap((deferred) => pipe( Queue.offer(queue, [take, deferred] as const), Effect.zipRight(Ref.set(ref, deferred)), Effect.zipRight(Deferred.await(deferred)) ) ), Effect.asUnit, core.fromEffect ) return core.readWithCause({ onInput: (input: Chunk.Chunk<A>) => pipe( Deferred.make<void>(), Effect.flatMap( (deferred) => pipe( Queue.offer(queue, [InternalTake.chunk(input), deferred] as const), Effect.flatMap((added) => pipe(Ref.set(ref, deferred), Effect.when(() => added))) ) ), Effect.asUnit, core.fromEffect, core.flatMap(() => producer(queue, ref)) ), onFailure: (error) => terminate(InternalTake.failCause(error)), onDone: () => terminate(InternalTake.end) }) } const consumer = ( queue: Queue.Queue<readonly [Take.Take<A, E>, Deferred.Deferred<void>]> ): Channel.Channel<Chunk.Chunk<A>, unknown, E, unknown, void, unknown, R> => { const process: Channel.Channel<Chunk.Chunk<A>, unknown, E, unknown, void, unknown> = pipe( core.fromEffect(Queue.take(queue)), core.flatMap(([take, deferred]) => channel.zipRight( core.fromEffect(Deferred.succeed(deferred, void 0)), InternalTake.match(take, { onEnd: () => core.unit, onFailure: core.failCause, onSuccess: (value) => pipe(core.write(value), core.flatMap(() => process)) }) ) ) ) return process } return channel.unwrapScoped( pipe( scoped, Effect.flatMap((queue) => pipe( Deferred.make<void>(), Effect.tap((start) => Deferred.succeed(start, void 0)), Effect.flatMap((start) => pipe( Ref.make(start), Effect.flatMap((ref) => pipe( bufferChannel, core.pipeTo(producer(queue, ref)), channelExecutor.runScoped, Effect.forkScoped ) ), Effect.as(consumer(queue)) ) ) ) ) ) ) } /** @internal */ export const catchAll = dual< <E, A2, E2, R2>( f: (error: E) => Stream.Stream<A2, E2, R2> ) => <A, R>(self: Stream.Stream<A, E, R>) => Stream.Stream<A2 | A, E2, R2 | R>, <A, E, R, A2, E2, R2>( self: Stream.Stream<A, E, R>, f: (error: E) => Stream.Stream<A2, E2, R2> ) => Stream.Stream<A2 | A, E2, R2 | R> >(2, <A, E, R, A2, E2, R2>( self: Stream.Stream<A, E, R>, f: (error: E) => Stream.Stream<A2, E2, R2> ): Stream.Stream<A2 | A, E2, R2 | R> => catchAllCause(self, (cause) => Either.match(Cause.failureOrCause(cause), { onLeft: f, onRight: failCause }))) /** @internal */ export const catchAllCause = dual< <E, A2, E2, R2>( f: (cause: Cause.Cause<E>) => Stream.Stream<A2, E2, R2> ) => <A, R>(self: Stream.Stream<A, E, R>) => Stream.Stream<A2 | A, E2, R2 | R>, <A, E, R, A2, E2, R2>( self: Stream.Stream<A, E, R>, f: (cause: Cause.Cause<E>) => Stream.Stream<A2, E2, R2> ) => Stream.Stream<A2 | A, E2, R2 | R> >( 2, <A, E, R, A2, E2, R2>( self: Stream.Stream<A, E, R>, f: (cause: Cause.Cause<E>) => Stream.Stream<A2, E2, R2> ): Stream.Stream<A | A2, E2, R | R2> => new StreamImpl<A | A2, E2, R | R2>(pipe(toChannel(self), core.catchAllCause((cause) => toChannel(f(cause))))) ) /** @internal */ export const catchSome = dual< <E, A2, E2, R2>( pf: (error: E) => Option.Option<Stream.Stream<A2, E2, R2>> ) => <A, R>(self: Stream.Stream<A, E, R>) => Stream.Stream<A2 | A, E | E2, R2 | R>, <A, E, R, A2, E2, R2>( self: Stream.Stream<A, E, R>, pf: (error: E) => Option.Option<Stream.Stream<A2, E2, R2>> ) => Stream.Stream<A2 | A, E | E2, R2 | R> >( 2, <A, E, R, A2, E2, R2>( self: Stream.Stream<A, E, R>, pf: (error: E) => Option.Option<Stream.Stream<A2, E2, R2>> ): Stream.Stream<A2 | A, E | E2, R2 | R> => pipe(self, catchAll((error) => pipe(pf(error), Option.getOrElse(() => fail<E | E2>(error))))) ) /** @internal */ export const catchSomeCause = dual< <E, A2, E2, R2>( pf: (cause: Cause.Cause<E>) => Option.Option<Stream.Stream<A2, E2, R2>> ) => <A, R>(self: Stream.Stream<A, E, R>) => Stream.Stream<A2 | A, E | E2, R2 | R>, <A, E, R, A2, E2, R2>( self: Stream.Stream<A, E, R>, pf: (cause: Cause.Cause<E>) => Option.Option<Stream.Stream<A2, E2, R2>> ) => Stream.Stream<A2 | A, E | E2, R2 | R> >( 2, <A, E, R, A2, E2, R2>( self: Stream.Stream<A, E, R>, pf: (cause: Cause.Cause<E>) => Option.Option<Stream.Stream<A2, E2, R2>> ): Stream.Stream<A2 | A, E | E2, R2 | R> => pipe(self, catchAllCause((cause) => pipe(pf(cause), Option.getOrElse(() => failCause<E | E2>(cause))))) ) /* @internal */ export const catchTag = dual< <K extends E["_tag"] & string, E extends { _tag: string }, A1, E1, R1>( k: K, f: (e: Extract<E, { _tag: K }>) => Stream.Stream<A1, E1, R1> ) => <A, R>(self: Stream.Stream<A, E, R>) => Stream.Stream<A | A1, Exclude<E, { _tag: K }> | E1, R | R1>, <A, E extends { _tag: string }, R, K extends E["_tag"] & string, A1, E1, R1>( self: Stream.Stream<A, E, R>, k: K, f: (e: Extract<E, { _tag: K }>) => Stream.Stream<A1, E1, R1> ) => Stream.Stream<A | A1, Exclude<E, { _tag: K }> | E1, R | R1> >(3, (self, k, f) => catchAll(self, (e) => { if ("_tag" in e && e["_tag"] === k) { return f(e as any) } return fail(e as any) })) /** @internal */ export const catchTags: { < E extends { _tag: string }, Cases extends { [K in E["_tag"]]+?: (error: Extract<E, { _tag: K }>) => Stream.Stream<any, any, any> } >( cases: Cases ): <A, R>(self: Stream.Stream<A, E, R>) => Stream.Stream< | A | { [K in keyof Cases]: Cases[K] extends ((...args: Array<any>) => Stream.Stream.Variance<infer A, infer _E, infer _R>) ? A : never }[keyof Cases], | Exclude<E, { _tag: keyof Cases }> | { [K in keyof Cases]: Cases[K] extends ((...args: Array<any>) => Stream.Stream.Variance<infer _A, infer E, infer _R>) ? E : never }[keyof Cases], | R | { [K in keyof Cases]: Cases[K] extends ((...args: Array<any>) => Stream.Stream.Variance<infer _A, infer _E, infer R>) ? R : never }[keyof Cases] > < A, E extends { _tag: string }, R, Cases extends { [K in E["_tag"]]+?: (error: Extract<E, { _tag: K }>) => Stream.Stream<any, any, any> } >( self: Stream.Stream<A, E, R>, cases: Cases ): Stream.Stream< | A | { [K in keyof Cases]: Cases[K] extends ((...args: Array<any>) => Stream.Stream.Variance<infer _R, infer _E, infer A>) ? A : never }[keyof Cases], | Exclude<E, { _tag: keyof Cases }> | { [K in keyof Cases]: Cases[K] extends ((...args: Array<any>) => Stream.Stream.Variance<infer _R, infer E, infer _A>) ? E : never }[keyof Cases], | R | { [K in keyof Cases]: Cases[K] extends ((...args: Array<any>) => Stream.Stream.Variance<infer R, infer _E, infer _A>) ? R : never }[keyof Cases] > } = dual(2, (self, cases) => catchAll(self, (e: any) => { const keys = Object.keys(cases) if ("_tag" in e && keys.includes(e["_tag"])) { return cases[e["_tag"]](e as any) } return fail(e as any) })) /** @internal */ export const changes = <A, E, R>(self: Stream.Stream<A, E, R>): Stream.Stream<A, E, R> => pipe(self, changesWith((x, y) => Equal.equals(y)(x))) /** @internal */ export const changesWith = dual< <A>(f: (x: A, y: A) => boolean) => <E, R>(self: Stream.Stream<A, E, R>) => Stream.Stream<A, E, R>, <A, E, R>(self: Stream.Stream<A, E, R>, f: (x: A, y: A) => boolean) => Stream.Stream<A, E, R> >(2, <A, E, R>(self: Stream.Stream<A, E, R>, f: (x: A, y: A) => boolean): Stream.Stream<A, E, R> => { const writer = ( last: Option.Option<A> ): Channel.Channel<Chunk.Chunk<A>, Chunk.Chunk<A>, E, E, void, unknown> => core.readWithCause({ onInput: (input: Chunk.Chunk<A>) => { const [newLast, newChunk] = Chunk.reduce( input, [last, Chunk.empty<A>()] as const, ([option, outputs], output) => { if (Option.isSome(option) && f(option.value, output)) { return [Option.some(output), outputs] as const } return [Option.some(output), pipe(outputs, Chunk.append(output))] as const } ) return core.flatMap( core.write(newChunk), () => writer(newLast) ) }, onFailure: core.failCause, onDone: () => core.unit }) return new StreamImpl(pipe(toChannel(self), core.pipeTo(writer(Option.none())))) }) /** @internal */ export const changesWithEffect = dual< <A, E2, R2>( f: (x: A, y: A) => Effect.Effect<boolean, E2, R2> ) => <E, R>(self: Stream.Stream<A, E, R>) => Stream.Stream<A, E2 | E, R2 | R>, <A, E, R, E2, R2>( self: Stream.Stream<A, E, R>, f: (x: A, y: A) => Effect.Effect<boolean, E2, R2> ) => Stream.Stream<A, E2 | E, R2 | R> >( 2, <A, E, R, E2, R2>( self: Stream.Stream<A, E, R>, f: (x: A, y: A) => Effect.Effect<boolean, E2, R2> ): Stream.Stream<A, E2 | E, R2 | R> => { const writer = ( last: Option.Option<A> ): Channel.Channel<Chunk.Chunk<A>, Chunk.Chunk<A>, E | E2, E | E2, void, unknown, R | R2> => core.readWithCause({ onInput: (input: Chunk.Chunk<A>) => pipe( input, Effect.reduce([last, Chunk.empty<A>()] as const, ([option, outputs], output) => { if (Option.isSome(option)) { return pipe( f(option.value, output), Effect.map((bool) => bool ? [Option.some(output), outputs] as const : [Option.some(output), pipe(outputs, Chunk.append(output))] as const ) ) } return Effect.succeed( [ Option.some(output), pipe(outputs, Chunk.append(output)) ] as const ) }), core.fromEffect, core.flatMap(([newLast, newChunk]) => pipe( core.write(newChunk), core.flatMap(() => writer(newLast)) ) ) ), onFailure: core.failCause, onDone: () => core.unit }) return new StreamImpl(pipe(toChannel(self), core.pipeTo(writer(Option.none())))) } ) /** @internal */ export const chunks = <A, E, R>(self: Stream.Stream<A, E, R>): Stream.Stream<Chunk.Chunk<A>, E, R> => pipe(self, mapChunks(Chunk.of)) /** @internal */ export const chunksWith = dual< <A, E, R, A2, E2, R2>( f: (stream: Stream.Stream<Chunk.Chunk<A>, E, R>) => Stream.Stream<Chunk.Chunk<A2>, E2, R2> ) => (self: Stream.Stream<A, E, R>) => Stream.Stream<A2, E | E2, R | R2>, <A, E, R, A2, E2, R2>( self: Stream.Stream<A, E, R>, f: (stream: Stream.Stream<Chunk.Chunk<A>, E, R>) => Stream.Stream<Chunk.Chunk<A2>, E2, R2> ) => Stream.Stream<A2, E | E2, R | R2> >( 2, <A, E, R, A2, E2, R2>( self: Stream.Stream<A, E, R>, f: (stream: Stream.Stream<Chunk.Chunk<A>, E, R>) => Stream.Stream<Chunk.Chunk<A2>, E2, R2> ): Stream.Stream<A2, E | E2, R | R2> => flattenChunks(f(chunks(self))) ) const unsome = <A, E, R>(effect: Effect.Effect<A, Option.Option<E>, R>): Effect.Effect<Option.Option<A>, E, R> => Effect.catchAll( Effect.asSome(effect), (o) => o._tag === "None" ? Effect.succeedNone : Effect.fail(o.value) ) /** @internal */ export const combine = dual< <A2, E2, R2, S, R3, E, A, R4, R5, A3>( that: Stream.Stream<A2, E2, R2>, s: S, f: ( s: S, pullLeft: Effect.Effect<A, Option.Option<E>, R3>, pullRight: Effect.Effect<A2, Option.Option<E2>, R4> ) => Effect.Effect<Exit.Exit<readonly [A3, S], Option.Option<E2 | E>>, never, R5> ) => <R>(self: Stream.Stream<A, E, R>) => Stream.Stream<A3, E2 | E, R2 | R3 | R4 | R5 | R>, <R, A2, E2, R2, S, R3, E, A, R4, R5, A3>( self: Stream.Stream<A, E, R>, that: Stream.Stream<A2, E2, R2>, s: S, f: ( s: S, pullLeft: Effect.Effect<A, Option.Option<E>, R3>, pullRight: Effect.Effect<A2, Option.Option<E2>, R4> ) => Effect.Effect<Exit.Exit<readonly [A3, S], Option.Option<E2 | E>>, never, R5> ) => Stream.Stream<A3, E2 | E, R2 | R3 | R4 | R5 | R> >(4, <R, A2, E2, R2, S, R3, E, A, R4, R5, A3>( self: Stream.Stream<A, E, R>, that: Stream.Stream<A2, E2, R2>, s: S, f: ( s: S, pullLeft: Effect.Effect<A, Option.Option<E>, R3>, pullRight: Effect.Effect<A2, Option.Option<E2>, R4> ) => Effect.Effect<Exit.Exit<readonly [A3, S], Option.Option<E2 | E>>, never, R5> ): Stream.Stream<A3, E2 | E, R2 | R3 | R4 | R5 | R> => { const producer = <Err, Elem>( handoff: Handoff.Handoff<Exit.Exit<Elem, Option.Option<Err>>>, latch: Handoff.Handoff<void> ): Channel.Channel<never, Elem, never, Err, unknown, unknown, R> => pipe( core.fromEffect(Handoff.take(latch)), channel.zipRight(core.readWithCause({ onInput: (input) => core.flatMap( core.fromEffect(pipe( handoff, Handoff.offer<Exit.Exit<Elem, Option.Option<Err>>>(Exit.succeed(input)) )), () => producer(handoff, latch) ), onFailure: (cause) => core.fromEffect( Handoff.offer<Exit.Exit<Elem, Option.Option<Err>>>( handoff, Exit.failCause(pipe(cause, Cause.map(Option.some))) ) ), onDone: () => core.flatMap( core.fromEffect( Handoff.offer<Exit.Exit<Elem, Option.Option<Err>>>( handoff, Exit.fail(Option.none()) ) ), () => producer(handoff, latch) ) })) ) return new StreamImpl( channel.unwrapScoped( Effect.gen(function*($) { const left = yield* $(Handoff.make<Exit.Exit<A, Option.Option<E>>>()) const right = yield* $(Handoff.make<Exit.Exit<A2, Option.Option<E2>>>()) const latchL = yield* $(Handoff.make<void>()) const latchR = yield* $(Handoff.make<void>()) yield* $( toChannel(self), channel.concatMap(channel.writeChunk), core.pipeTo(producer(left, latchL)), channelExecutor.runScoped, Effect.forkScoped ) yield* $(