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

230 lines (203 loc) 7.17 kB
import type * as ChildExecutorDecision from "../../ChildExecutorDecision.js" import * as Effect from "../../Effect.js" import * as Exit from "../../Exit.js" import { pipe } from "../../Function.js" import type * as UpstreamPullRequest from "../../UpstreamPullRequest.js" import type * as UpstreamPullStrategy from "../../UpstreamPullStrategy.js" import type { ErasedChannel, ErasedExecutor } from "./channelExecutor.js" /** @internal */ export interface Subexecutor<in out R> { close(exit: Exit.Exit<unknown, unknown>): Effect.Effect<unknown, never, R> | undefined enqueuePullFromChild(child: PullFromChild<R>): Subexecutor<R> } /** @internal */ export type Primitive<Env> = PullFromChild<Env> | PullFromUpstream<Env> | DrainChildExecutors<Env> | Emit<Env> /** @internal */ export const OP_PULL_FROM_CHILD = "PullFromChild" as const /** @internal */ export type OP_PULL_FROM_CHILD = typeof OP_PULL_FROM_CHILD /** @internal */ export const OP_PULL_FROM_UPSTREAM = "PullFromUpstream" as const /** @internal */ export type OP_PULL_FROM_UPSTREAM = typeof OP_PULL_FROM_UPSTREAM /** @internal */ export const OP_DRAIN_CHILD_EXECUTORS = "DrainChildExecutors" as const /** @internal */ export type OP_DRAIN_CHILD_EXECUTORS = typeof OP_DRAIN_CHILD_EXECUTORS /** @internal */ export const OP_EMIT = "Emit" as const /** @internal */ export type OP_EMIT = typeof OP_EMIT /** * Execute the `childExecutor` and on each emitted value, decide what to do by * `onEmit`. * * @internal */ export class PullFromChild<in out R> implements Subexecutor<R> { readonly _tag: OP_PULL_FROM_CHILD = OP_PULL_FROM_CHILD constructor( readonly childExecutor: ErasedExecutor<R>, readonly parentSubexecutor: Subexecutor<R>, readonly onEmit: (value: unknown) => ChildExecutorDecision.ChildExecutorDecision ) { } close(exit: Exit.Exit<unknown, unknown>): Effect.Effect<unknown, never, R> | undefined { const fin1 = this.childExecutor.close(exit) const fin2 = this.parentSubexecutor.close(exit) if (fin1 !== undefined && fin2 !== undefined) { return Effect.zipWith( Effect.exit(fin1), Effect.exit(fin2), (exit1, exit2) => pipe(exit1, Exit.zipRight(exit2)) ) } else if (fin1 !== undefined) { return fin1 } else if (fin2 !== undefined) { return fin2 } else { return undefined } } enqueuePullFromChild(_child: PullFromChild<R>): Subexecutor<R> { return this } } /** * Execute `upstreamExecutor` and for each emitted element, spawn a child * channel and continue with processing it by `PullFromChild`. * * @internal */ export class PullFromUpstream<in out R> implements Subexecutor<R> { readonly _tag: OP_PULL_FROM_UPSTREAM = OP_PULL_FROM_UPSTREAM constructor( readonly upstreamExecutor: ErasedExecutor<R>, readonly createChild: (value: unknown) => ErasedChannel<R>, readonly lastDone: unknown, readonly activeChildExecutors: ReadonlyArray<PullFromChild<R> | undefined>, readonly combineChildResults: (x: unknown, y: unknown) => unknown, readonly combineWithChildResult: (x: unknown, y: unknown) => unknown, readonly onPull: ( request: UpstreamPullRequest.UpstreamPullRequest<unknown> ) => UpstreamPullStrategy.UpstreamPullStrategy<unknown>, readonly onEmit: (value: unknown) => ChildExecutorDecision.ChildExecutorDecision ) { } close(exit: Exit.Exit<unknown, unknown>): Effect.Effect<unknown, never, R> | undefined { const fin1 = this.upstreamExecutor.close(exit) const fins = [ ...this.activeChildExecutors.map((child) => child !== undefined ? child.childExecutor.close(exit) : undefined ), fin1 ] const result = fins.reduce( (acc: Effect.Effect<Exit.Exit<unknown, unknown>, never, R> | undefined, next) => { if (acc !== undefined && next !== undefined) { return Effect.zipWith( acc, Effect.exit(next), (exit1, exit2) => Exit.zipRight(exit1, exit2) ) } else if (acc !== undefined) { return acc } else if (next !== undefined) { return Effect.exit(next) } else { return undefined } }, undefined ) return result === undefined ? result : result } enqueuePullFromChild(child: PullFromChild<R>): Subexecutor<R> { return new PullFromUpstream( this.upstreamExecutor, this.createChild, this.lastDone, [...this.activeChildExecutors, child], this.combineChildResults, this.combineWithChildResult, this.onPull, this.onEmit ) } } /** * Transformed from `PullFromUpstream` when upstream has finished but there * are still active child executors. * * @internal */ export class DrainChildExecutors<in out R> implements Subexecutor<R> { readonly _tag: OP_DRAIN_CHILD_EXECUTORS = OP_DRAIN_CHILD_EXECUTORS constructor( readonly upstreamExecutor: ErasedExecutor<R>, readonly lastDone: unknown, readonly activeChildExecutors: ReadonlyArray<PullFromChild<R> | undefined>, readonly upstreamDone: Exit.Exit<unknown, unknown>, readonly combineChildResults: (x: unknown, y: unknown) => unknown, readonly combineWithChildResult: (x: unknown, y: unknown) => unknown, readonly onPull: ( request: UpstreamPullRequest.UpstreamPullRequest<unknown> ) => UpstreamPullStrategy.UpstreamPullStrategy<unknown> ) { } close(exit: Exit.Exit<unknown, unknown>): Effect.Effect<unknown, never, R> | undefined { const fin1 = this.upstreamExecutor.close(exit) const fins = [ ...this.activeChildExecutors.map((child) => (child !== undefined ? child.childExecutor.close(exit) : undefined) ), fin1 ] const result = fins.reduce( (acc: Effect.Effect<Exit.Exit<unknown, unknown>, never, R> | undefined, next) => { if (acc !== undefined && next !== undefined) { return Effect.zipWith( acc, Effect.exit(next), (exit1, exit2) => Exit.zipRight(exit1, exit2) ) } else if (acc !== undefined) { return acc } else if (next !== undefined) { return Effect.exit(next) } else { return undefined } }, undefined ) return result === undefined ? result : result } enqueuePullFromChild(child: PullFromChild<R>): Subexecutor<R> { return new DrainChildExecutors( this.upstreamExecutor, this.lastDone, [...this.activeChildExecutors, child], this.upstreamDone, this.combineChildResults, this.combineWithChildResult, this.onPull ) } } /** @internal */ export class Emit<in out R> implements Subexecutor<R> { readonly _tag: OP_EMIT = OP_EMIT constructor(readonly value: unknown, readonly next: Subexecutor<R>) { } close(exit: Exit.Exit<unknown, unknown>): Effect.Effect<unknown, never, R> | undefined { const result = this.next.close(exit) return result === undefined ? result : result } enqueuePullFromChild(_child: PullFromChild<R>): Subexecutor<R> { return this } }