import * as Arr from "../Array.ts" import type * as Cause from "../Cause.ts" import type * as Clock from "../Clock.ts" import type * as Console from "../Console.ts" import * as Context from "../Context.ts" import * as Duration from "../Duration.ts" import type * as Effect from "../Effect.ts" import * as Equal from "../Equal.ts" import type * as Exit from "../Exit.ts" import type * as Fiber from "../Fiber.ts" import * as Filter from "../Filter.ts" import { formatJson } from "../Formatter.ts" import type { LazyArg } from "../Function.ts" import { constant, constFalse, constTrue, constUndefined, constVoid, dual, identity } from "../Function.ts" import * as Hash from "../Hash.ts" import { toJson, toStringUnknown } from "../Inspectable.ts" import * as Iterable from "../Iterable.ts" import type * as _Latch from "../Latch.ts" import type * as Logger from "../Logger.ts" import type * as LogLevel from "../LogLevel.ts" import type * as Metric from "../Metric.ts" import * as Option from "../Option.ts" import * as Order from "../Order.ts" import { pipeArguments } from "../Pipeable.ts" import type * as Predicate from "../Predicate.ts" import { hasProperty, isIterable, isString, isTagged } from "../Predicate.ts" import { currentFiberTypeId, redact } from "../Redactable.ts" import type { StackFrame } from "../References.ts" import * as Result from "../Result.ts" import * as Scheduler from "../Scheduler.ts" import type * as Scope from "../Scope.ts" import * as Tracer from "../Tracer.ts" import type { Concurrency, EqualsWith, ExcludeReason, ExcludeTag, ExtractReason, ExtractTag, NarrowReason, NoInfer, OmitReason, ReasonOf, ReasonTags, Simplify, Tags, unassigned } from "../Types.ts" import { internalCall } from "../Utils.ts" import type { Primitive } from "./core.ts" import { args, causeAnnotate, causeEmpty, causeFromReasons, CauseImpl, constEmptyAnnotations, contA, contAll, contE, evaluate, exitDie, exitFail, exitFailCause, exitSucceed, ExitTypeId, Fail, InterruptorStackTrace, isCause, isDieReason, isEffect, isFailReason, isInterruptReason, isNoSuchElementError, makePrimitive, makePrimitiveProto, NoSuchElementError, ReasonBase, StackTraceKey as CauseStackTrace, TaggedError, withFiber, Yield } from "./core.ts" import * as doNotation from "./doNotation.ts" import * as InternalMetric from "./metric.ts" import { CurrentConcurrency, CurrentErrorReporters, CurrentLogAnnotations, CurrentLogLevel, CurrentLogSpans, CurrentStackFrame, MinimumLogLevel, TracerEnabled, TracerSpanAnnotations, TracerSpanLinks, TracerTimingEnabled } from "./references.ts" import { addSpanStackTrace, type ErrorWithStackTraceLimit, makeStackCleaner } from "./tracer.ts" import { version } from "./version.ts" // ---------------------------------------------------------------------------- // Cause // ---------------------------------------------------------------------------- /** @internal */ export class Interrupt extends ReasonBase<"Interrupt"> implements Cause.Interrupt { readonly fiberId: number | undefined constructor( fiberId: number | undefined, annotations = constEmptyAnnotations ) { super("Interrupt", annotations, "Interrupted") this.fiberId = fiberId } override toString() { return `Interrupt(${this.fiberId})` } toJSON(): unknown { return { _tag: "Interrupt", fiberId: this.fiberId } } [Equal.symbol](that: any): boolean { return ( isInterruptReason(that) && this.fiberId === that.fiberId && this.annotations === that.annotations ) } [Hash.symbol](): number { return Hash.combine(Hash.string(`${this._tag}:${this.fiberId}`))( Hash.random(this.annotations) ) } } /** @internal */ export const makeInterruptReason = (fiberId?: number | undefined): Cause.Interrupt => new Interrupt(fiberId) /** @internal */ export const causeInterrupt = ( fiberId?: number | undefined ): Cause.Cause => new CauseImpl([new Interrupt(fiberId)]) /** @internal */ export const hasFails = (self: Cause.Cause): boolean => self.reasons.some(isFailReason) /** @internal */ export const findFail = (self: Cause.Cause): Result.Result, Cause.Cause> => { const reason = self.reasons.find(isFailReason) return reason ? Result.succeed(reason) : Result.fail(self as Cause.Cause) } /** @internal */ export const findError = (self: Cause.Cause): Result.Result> => { for (let i = 0; i < self.reasons.length; i++) { const reason = self.reasons[i] if (reason._tag === "Fail") { return Result.succeed(reason.error) } } return Result.fail(self as Cause.Cause) } /** @internal */ export const findErrorOption = Filter.toOption(findError) /** @internal */ export const hasDies = (self: Cause.Cause): boolean => self.reasons.some(isDieReason) /** @internal */ export const findDie = (self: Cause.Cause): Result.Result> => { const reason = self.reasons.find(isDieReason) return reason ? Result.succeed(reason) : Result.fail(self) } /** @internal */ export const findDefect = (self: Cause.Cause): Result.Result> => { const reason = self.reasons.find(isDieReason) return reason ? Result.succeed(reason.defect) : Result.fail(self) } /** @internal */ export const hasInterrupts = (self: Cause.Cause): boolean => self.reasons.some(isInterruptReason) /** @internal */ export const findInterrupt = (self: Cause.Cause): Result.Result> => { const reason = self.reasons.find(isInterruptReason) return reason ? Result.succeed(reason) : Result.fail(self) } /** @internal */ export const causeFilterInterruptors = ( self: Cause.Cause ): Result.Result, Cause.Cause> => { let interruptors: Set | undefined for (let i = 0; i < self.reasons.length; i++) { const f = self.reasons[i] if (f._tag !== "Interrupt") continue interruptors ??= new Set() if (f.fiberId !== undefined) { interruptors.add(f.fiberId) } } return interruptors ? Result.succeed(interruptors) : Result.fail(self) } /** @internal */ export const causeInterruptors = (self: Cause.Cause): ReadonlySet => { const result = causeFilterInterruptors(self) return Result.isFailure(result) ? emptySet : result.success } const emptySet = new Set() /** @internal */ export const hasInterruptsOnly = (self: Cause.Cause): boolean => self.reasons.length > 0 && self.reasons.every(isInterruptReason) /** @internal */ export const reasonAnnotations = ( self: Cause.Reason ): Context.Context => Context.makeUnsafe(self.annotations) /** @internal */ export const causeAnnotations = ( self: Cause.Cause ): Context.Context => { const map = new Map() for (const f of self.reasons) { if (f.annotations.size > 0) { for (const [key, value] of f.annotations) { map.set(key, value) } } } return Context.makeUnsafe(map) } /** @internal */ export const causeCombine: { (that: Cause.Cause): (self: Cause.Cause) => Cause.Cause (self: Cause.Cause, that: Cause.Cause): Cause.Cause } = dual( 2, (self: Cause.Cause, that: Cause.Cause): Cause.Cause => { if (self.reasons.length === 0) { return that as Cause.Cause } else if (that.reasons.length === 0) { return self as Cause.Cause } const newCause = new CauseImpl( Arr.union(self.reasons, that.reasons) ) return Equal.equals(self, newCause) ? self : newCause } ) /** @internal */ export const causeMap: { (f: (error: NoInfer) => E2): (self: Cause.Cause) => Cause.Cause (self: Cause.Cause, f: (error: NoInfer) => E2): Cause.Cause } = dual( 2, (self: Cause.Cause, f: (error: NoInfer) => E2): Cause.Cause => { let hasFail = false const failures = self.reasons.map((failure) => { if (isFailReason(failure)) { hasFail = true return new Fail(f(failure.error)) } return failure }) return hasFail ? causeFromReasons(failures) : self as any } ) /** @internal */ export const causePartition = ( self: Cause.Cause ): { readonly Fail: ReadonlyArray> readonly Die: ReadonlyArray readonly Interrupt: ReadonlyArray } => { const obj = { Fail: [] as Array>, Die: [] as Array, Interrupt: [] as Array } for (let i = 0; i < self.reasons.length; i++) { obj[self.reasons[i]._tag].push(self.reasons[i] as any) } return obj } /** @internal */ export const causeSquash = (self: Cause.Cause): unknown => { const partitioned = causePartition(self) if (partitioned.Fail.length > 0) { return partitioned.Fail[0].error } else if (partitioned.Die.length > 0) { return partitioned.Die[0].defect } else if (partitioned.Interrupt.length > 0) { return new globalThis.Error("All fibers interrupted without error") } return new globalThis.Error("Empty cause") } /** @internal */ export const causePrettyErrors = (self: Cause.Cause): Array => { const errors: Array = [] const interrupts: Array = [] if (self.reasons.length === 0) return errors const prevStackLimit = (Error as ErrorWithStackTraceLimit).stackTraceLimit ;(Error as ErrorWithStackTraceLimit) .stackTraceLimit = 1 for (const failure of self.reasons) { if (failure._tag === "Interrupt") { interrupts.push(failure) continue } errors.push( causePrettyError( failure._tag === "Die" ? failure.defect : failure.error as any, failure.annotations ) ) } if (errors.length === 0) { const cause = new Error("The fiber was interrupted by:") cause.name = "InterruptCause" cause.stack = interruptCauseStack(cause, interrupts) const error = new globalThis.Error("All fibers interrupted without error", { cause }) error.name = "InterruptError" error.stack = `${error.name}: ${error.message}` errors.push(causePrettyError(error, interrupts[0].annotations)) } ;(Error as ErrorWithStackTraceLimit).stackTraceLimit = prevStackLimit return errors } /** @internal */ export const causePrettyError = ( original: Record | Error, annotations?: ReadonlyMap ): Error => { const kind = typeof original let error: Error if (original && kind === "object") { error = new globalThis.Error(causePrettyMessage(original), { cause: original.cause ? causePrettyError(original.cause as any) : undefined }) if (typeof original.name === "string") { error.name = original.name } if (typeof original.stack === "string") { error.stack = cleanErrorStack(original.stack, error, annotations) } else { const stack = `${error.name}: ${error.message}` error.stack = annotations ? addStackAnnotations(stack, annotations) : stack } for (const key of Object.keys(original)) { if (!(key in error)) { ;(error as any)[key] = (original as any)[key] } } } else { error = new globalThis.Error( !original ? `Unknown error: ${original}` : kind === "string" ? original as any : formatJson(original) ) } return error } const causePrettyMessage = (u: Record | Error): string => { if (typeof u.message === "string") { return u.message } else if ( typeof u.toString === "function" && u.toString !== Object.prototype.toString && u.toString !== Array.prototype.toString ) { try { return u.toString() } catch { // something's off, rollback to json } } return formatJson(u) } const locationRegExp = /\((.*)\)/g const cleanErrorStack = ( stack: string, error: Error, annotations: ReadonlyMap | undefined ): string => { const message = `${error.name}: ${error.message}` const lines = (stack.startsWith(message) ? stack.slice(message.length) : stack).split("\n") const out: Array = [message] for (let i = 1; i < lines.length; i++) { if (/(?:Generator\.next|~effect\/Effect)/.test(lines[i])) { break } out.push(lines[i]) } return annotations ? addStackAnnotations(out.join("\n"), annotations) : out.join("\n") } const addStackAnnotations = (stack: string, annotations: ReadonlyMap) => { const frame = annotations?.get(CauseStackTrace.key) as StackFrame | undefined if (frame) { stack = `${stack}\n${currentStackTrace(frame)}` } return stack } const interruptCauseStack = (error: Error, interrupts: Array): string => { const out: Array = [`${error.name}: ${error.message}`] for (const current of interrupts) { const fiberId = current.fiberId !== undefined ? `#${current.fiberId}` : "unknown" const frame = current.annotations.get(InterruptorStackTrace.key) as StackFrame | undefined out.push(` at fiber (${fiberId})`) if (frame) out.push(currentStackTrace(frame)) } return out.join("\n") } const currentStackTrace = (frame: StackFrame): string => { const out: Array = [] let current: StackFrame | undefined = frame let i = 0 while (current && i < 10) { const stack = current.stack() if (stack) { const locationMatchAll = stack.matchAll(locationRegExp) let match = false for (const [, location] of locationMatchAll) { match = true out.push(` at ${current.name} (${location})`) } if (!match) { out.push(` at ${current.name} (${stack.replace(/^at /, "")})`) } } else { out.push(` at ${current.name}`) } current = current.parent i++ } return out.join("\n") } /** @internal */ export const causePretty = (cause: Cause.Cause): string => causePrettyErrors(cause).map((e) => e.cause ? `${e.stack} {\n${renderErrorCause(e.cause as Error, " ")}\n}` : e.stack ) .join("\n") const renderErrorCause = (cause: Error, prefix: string) => { const lines = cause.stack!.split("\n") let stack = `${prefix}[cause]: ${lines[0]}` for (let i = 1, len = lines.length; i < len; i++) { stack += `\n${prefix}${lines[i]}` } if (cause.cause) { stack += ` {\n${renderErrorCause(cause.cause as Error, `${prefix} `)}\n${prefix}}` } return stack } // ---------------------------------------------------------------------------- // Fiber // ---------------------------------------------------------------------------- /** @internal */ export const FiberTypeId = `~effect/Fiber/${version}` as const const fiberVariance = { _A: identity, _E: identity } const fiberIdStore = { id: 0 } /** @internal */ export const getCurrentFiber = (): Fiber.Fiber | undefined => (globalThis as any)[currentFiberTypeId] /** @internal */ export class FiberImpl implements Fiber.Fiber { constructor( context: Context.Context, interruptible: boolean = true ) { this[FiberTypeId] = fiberVariance as any this.setContext(context) this.id = ++fiberIdStore.id this.currentOpCount = 0 this.currentLoopCount = 0 this.interruptible = interruptible this._stack = [] this._observers = [] this._exit = undefined this._children = undefined this._interruptedCause = undefined this._yielded = undefined this.runtimeMetrics?.recordFiberStart(this.context) } readonly [FiberTypeId]: Fiber.Fiber.Variance readonly id: number interruptible: boolean currentOpCount: number currentLoopCount: number readonly _stack: Array readonly _observers: Array<(exit: Exit.Exit) => void> _exit: Exit.Exit | undefined _currentExit: Exit.Exit | undefined _children: Set> | undefined _interruptedCause: Cause.Cause | undefined _yielded: Exit.Exit | (() => void) | undefined // set in setContext context!: Context.Context currentScheduler!: Scheduler.Scheduler currentTracerContext: Tracer.Tracer["context"] currentSpan: Tracer.AnySpan | undefined currentLogLevel!: LogLevel.LogLevel minimumLogLevel!: LogLevel.LogLevel currentStackFrame: StackFrame | undefined runtimeMetrics: Metric.FiberRuntimeMetricsService | undefined maxOpsBeforeYield!: number currentPreventYield!: boolean _dispatcher: Scheduler.SchedulerDispatcher | undefined = undefined get currentDispatcher(): Scheduler.SchedulerDispatcher { return this._dispatcher ??= this.currentScheduler.makeDispatcher() } getRef(ref: Context.Reference): X { return Context.getReferenceUnsafe(this.context, ref) } addObserver(cb: (exit: Exit.Exit) => void): () => void { if (this._exit) { cb(this._exit) return constVoid } this._observers.push(cb) return () => { const index = this._observers.indexOf(cb) if (index >= 0) { this._observers.splice(index, 1) } } } interruptUnsafe(fiberId?: number | undefined, annotations?: Context.Context | undefined): void { if (this._exit) { return } let cause = causeInterrupt(fiberId) if (this.currentStackFrame) { cause = causeAnnotate(cause, Context.make(CauseStackTrace, this.currentStackFrame)) } if (annotations) { cause = causeAnnotate(cause, annotations) } this._interruptedCause = this._interruptedCause ? causeCombine(this._interruptedCause, cause) : cause if (this.interruptible) { this.evaluate(failCause(this._interruptedCause) as any) } } pollUnsafe(): Exit.Exit | undefined { return this._exit } evaluate(effect: Primitive): void { if (this._exit) { return } else if (this._yielded !== undefined) { const yielded = this._yielded as () => void this._yielded = undefined yielded() } const exit = this.runLoop(effect) if (exit === Yield) { return } // the interruptChildren middleware is added in Effect.forkChild, so it can be // tree-shaken if not used const interruptChildren = fiberMiddleware.interruptChildren && fiberMiddleware.interruptChildren(this) if (interruptChildren !== undefined) { return this.evaluate(flatMap(interruptChildren, () => exit) as any) } this._exit = exit this.runtimeMetrics?.recordFiberEnd(this.context, this._exit) for (let i = 0; i < this._observers.length; i++) { this._observers[i](exit) } this._observers.length = 0 } runLoop(effect: Primitive): Exit.Exit | Yield { const prevFiber = (globalThis as any)[currentFiberTypeId] ;(globalThis as any)[currentFiberTypeId] = this let yielding = false let current: Primitive | Yield = effect this.currentOpCount = 0 const currentLoop = ++this.currentLoopCount try { while (true) { this.currentOpCount++ if ( !yielding && !this.currentPreventYield && this.currentScheduler.shouldYield(this as any) ) { yielding = true const prev = current current = flatMap(yieldNow, () => prev as any) as any } current = this.currentTracerContext ? this.currentTracerContext(current as any, this) : (current as any)[evaluate](this) if (currentLoop !== this.currentLoopCount) { // another effect has taken over the loop, return Yield } else if (current === Yield) { const yielded = this._yielded! if (ExitTypeId in yielded) { this._yielded = undefined return yielded } return Yield } } } catch (error) { if (!hasProperty(current, evaluate)) { return exitDie(`Fiber.runLoop: Not a valid effect: ${String(current)}`) } return this.runLoop(exitDie(error) as any) } finally { ;(globalThis as any)[currentFiberTypeId] = prevFiber } } getCont(symbol: S): | (Primitive & Record Primitive>) | undefined { while (true) { const op = this._stack.pop() if (!op) return undefined const cont = op[contAll] && op[contAll](this) if (cont) { ;(cont as any)[symbol] = cont return cont as any } if (op[symbol]) return op as any } } yieldWith(value: Exit.Exit | (() => void)): Yield { this._yielded = value return Yield } children(): Set> { return (this._children ??= new Set()) } pipe() { return pipeArguments(this, arguments) } setContext(context: Context.Context): void { this.context = context const scheduler = this.getRef(Scheduler.Scheduler) if (scheduler !== this.currentScheduler) { this.currentScheduler = scheduler this._dispatcher = undefined } this.currentSpan = context.mapUnsafe.get(Tracer.ParentSpanKey) this.currentLogLevel = this.getRef(CurrentLogLevel) this.minimumLogLevel = this.getRef(MinimumLogLevel) this.currentStackFrame = context.mapUnsafe.get(CurrentStackFrame.key) this.maxOpsBeforeYield = this.getRef(Scheduler.MaxOpsBeforeYield) this.currentPreventYield = this.getRef(Scheduler.PreventSchedulerYield) this.runtimeMetrics = context.mapUnsafe.get(InternalMetric.FiberRuntimeMetricsKey) const currentTracer = context.mapUnsafe.get(Tracer.TracerKey) this.currentTracerContext = currentTracer ? currentTracer["context"] : undefined } get currentSpanLocal(): Tracer.Span | undefined { return this.currentSpan?._tag === "Span" ? this.currentSpan : undefined } } const fiberMiddleware = { interruptChildren: undefined as | ((fiber: FiberImpl) => Effect.Effect | undefined) | undefined } const fiberStackAnnotations = (fiber: Fiber.Fiber) => { if (!fiber.currentStackFrame) return undefined const annotations = new Map() annotations.set(CauseStackTrace.key, fiber.currentStackFrame) return Context.makeUnsafe(annotations) } const fiberInterruptChildren = (fiber: FiberImpl) => { if (fiber._children === undefined || fiber._children.size === 0) { return undefined } return fiberInterruptAll(fiber._children) } /** @internal */ export const fiberAwait = ( self: Fiber.Fiber ): Effect.Effect> => { const impl = self as FiberImpl if (impl._exit) return succeed(impl._exit) return callback((resume) => { if (impl._exit) return resume(succeed(impl._exit)) return sync(self.addObserver((exit) => resume(succeed(exit)))) }) } /** @internal */ export const fiberAwaitAll = >( self: Iterable ): Effect.Effect< Array< Exit.Exit< Fiber extends Fiber.Fiber ? _A : never, Fiber extends Fiber.Fiber ? _E : never > > > => callback((resume) => { const iter = self[Symbol.iterator]() as Iterator const exits: Array> = [] let cancel: (() => void) | undefined = undefined function loop() { let result = iter.next() while (!result.done) { if (result.value._exit) { exits.push(result.value._exit) result = iter.next() continue } cancel = result.value.addObserver((exit) => { exits.push(exit) loop() }) return } resume(succeed(exits)) } loop() return sync(() => cancel?.()) }) /** @internal */ export const fiberJoin = (self: Fiber.Fiber): Effect.Effect => { const impl = self as FiberImpl if (impl._exit) return impl._exit return callback((resume) => { if (impl._exit) return resume(impl._exit) return sync(self.addObserver(resume)) }) } /** @internal */ export const fiberJoinAll = >>(self: A): Effect.Effect< Arr.ReadonlyArray.With> ? _A : never>, A extends Fiber.Fiber ? _E : never > => callback((resume) => { const fibers = Array.from(self) if (fibers.length === 0) return resume(succeed(Arr.empty() as any)) const out = new Array(fibers.length) as Arr.NonEmptyArray const cancels = Arr.empty<() => void>() let done = 0 let failed = false for (let i = 0; i < fibers.length; i++) { if (failed) break cancels.push(fibers[i].addObserver((exit) => { done++ if (exit._tag === "Failure") { failed = true cancels.forEach((cancel) => cancel()) return resume(exit as any) } out[i] = exit.value if (done === fibers.length) { resume(succeed(out)) } })) } }) /** @internal */ export const fiberInterrupt = ( self: Fiber.Fiber ): Effect.Effect => withFiber((fiber) => fiberInterruptAs(self, fiber.id)) /** @internal */ export const fiberInterruptAs: { ( fiberId: number | undefined, annotations?: Context.Context | undefined ): (self: Fiber.Fiber) => Effect.Effect ( self: Fiber.Fiber, fiberId: number | undefined, annotations?: Context.Context | undefined ): Effect.Effect } = dual( (args) => hasProperty(args[0], FiberTypeId), ( self: Fiber.Fiber, fiberId: number | undefined, annotations?: Context.Context | undefined ): Effect.Effect => withFiber((parent) => { let ann = fiberStackAnnotations(parent) ann = ann && annotations ? Context.merge(ann, annotations) : ann ?? annotations self.interruptUnsafe(fiberId, ann) return asVoid(fiberAwait(self)) }) ) /** @internal */ export const fiberInterruptAll = >>( fibers: A ): Effect.Effect => withFiber((parent) => { const annotations = fiberStackAnnotations(parent) for (const fiber of fibers) { fiber.interruptUnsafe(parent.id, annotations) } return asVoid(fiberAwaitAll(fibers)) }) /** @internal */ export const fiberInterruptAllAs: { (fiberId: number): >>(fibers: A) => Effect.Effect >>(fibers: A, fiberId: number): Effect.Effect } = dual(2, >>( fibers: A, fiberId: number ): Effect.Effect => withFiber((parent) => { const annotations = fiberStackAnnotations(parent) for (const fiber of fibers) fiber.interruptUnsafe(fiberId, annotations) return asVoid(fiberAwaitAll(fibers)) })) /** @internal */ export const succeed: (value: A) => Effect.Effect = exitSucceed /** @internal */ export const failCause: (cause: Cause.Cause) => Effect.Effect = exitFailCause /** @internal */ export const fail: (error: E) => Effect.Effect = exitFail /** @internal */ export const sync: (thunk: LazyArg) => Effect.Effect = makePrimitive({ op: "Sync", [evaluate](fiber): Primitive | Yield { const value = this[args]() const cont = fiber.getCont(contA) return cont ? cont[contA](value, fiber) : fiber.yieldWith(exitSucceed(value)) } }) /** @internal */ export const suspend: ( evaluate: LazyArg> ) => Effect.Effect = makePrimitive({ op: "Suspend", [evaluate](_fiber) { return this[args]() } }) /** @internal */ export const fromOption: (option: Option.Option) => Effect.Effect = Option.match({ onNone: () => fail(new NoSuchElementError("Effect.fromOption: Option.none")), onSome: succeed }) /** @internal */ export const fromResult: (result: Result.Result) => Effect.Effect = Result.match({ onFailure: fail, onSuccess: succeed }) /** @internal */ export const fromNullishOr = (value: A): Effect.Effect, Cause.NoSuchElementError> => value == null ? fail(new NoSuchElementError()) : succeed(value) /** @internal */ export const yieldNowWith: (priority?: number) => Effect.Effect = makePrimitive({ op: "Yield", [evaluate](fiber) { let resumed = false fiber.currentDispatcher.scheduleTask(() => { if (resumed) return fiber.evaluate(exitVoid as any) }, this[args] ?? 0) return fiber.yieldWith(() => { resumed = true }) } }) /** @internal */ export const yieldNow: Effect.Effect = yieldNowWith(0) /** @internal */ export const succeedSome = (a: A): Effect.Effect> => succeed(Option.some(a)) /** @internal */ export const succeedNone: Effect.Effect> = succeed( Option.none() ) /** @internal */ export const failCauseSync = ( evaluate: LazyArg> ): Effect.Effect => suspend(() => failCause(internalCall(evaluate))) /** @internal */ export const die = (defect: unknown): Effect.Effect => exitDie(defect) /** @internal */ export const failSync = (error: LazyArg): Effect.Effect => suspend(() => fail(internalCall(error))) /** @internal */ const void_: Effect.Effect = succeed(void 0) /** @internal */ export { void_ as void } /** @internal */ const try_ = (options: { try: LazyArg catch: (error: unknown) => E }): Effect.Effect => suspend(() => { try { return succeed(internalCall(options.try)) } catch (err) { return fail(internalCall(() => options.catch(err))) } }) /** @internal */ export { try_ as try } /** @internal */ export const promise = ( evaluate: (signal: AbortSignal) => PromiseLike ): Effect.Effect => callbackOptions(function(resume, signal) { internalCall(() => evaluate(signal!)).then( (a) => resume(succeed(a)), (e) => resume(die(e)) ) }, evaluate.length !== 0) /** @internal */ export const tryPromise = ( options: { readonly try: (signal: AbortSignal) => PromiseLike readonly catch: (error: unknown) => E } | ((signal: AbortSignal) => PromiseLike) ): Effect.Effect => { const f = typeof options === "function" ? options : options.try const catcher = typeof options === "function" ? ((cause: unknown) => new UnknownError(cause, "An error occurred in Effect.tryPromise")) : options.catch return callbackOptions(function(resume, signal) { try { internalCall(() => f(signal!)).then( (a) => resume(succeed(a)), (e) => resume(fail(internalCall(() => catcher(e)) as E)) ) } catch (err) { resume(fail(internalCall(() => catcher(err)) as E)) } }, eval.length !== 0) } /** @internal */ export const withFiberId = ( f: (fiberId: number) => Effect.Effect ): Effect.Effect => withFiber((fiber) => f(fiber.id)) /** @internal */ export const fiber = withFiber(succeed) /** @internal */ export const fiberId = withFiberId(succeed) const callbackOptions: ( register: ( this: Scheduler.Scheduler, resume: (effect: Effect.Effect) => void, signal?: AbortSignal ) => void | Effect.Effect, withSignal: boolean ) => Effect.Effect = makePrimitive({ op: "Async", single: false, [evaluate](fiber) { const register = internalCall(() => this[args][0].bind(fiber.currentScheduler)) let resumed = false let yielded: boolean | Primitive = false const controller = this[args][1] ? new AbortController() : undefined const onCancel = register((effect) => { if (resumed) return resumed = true if (yielded) { fiber.evaluate(effect as any) } else { yielded = effect as any } }, controller?.signal) if (yielded !== false) return yielded yielded = true fiber._yielded = () => { resumed = true } if (controller === undefined && onCancel === undefined) { return Yield } fiber._stack.push( asyncFinalizer(() => { resumed = true controller?.abort() return onCancel ?? exitVoid }) ) return Yield } }) const asyncFinalizer: ( onInterrupt: () => Effect.Effect ) => Primitive = makePrimitive({ op: "AsyncFinalizer", [contAll](fiber) { if (fiber.interruptible) { fiber.interruptible = false fiber._stack.push(setInterruptibleTrue) } }, [contE](cause, _fiber) { return hasInterrupts(cause) ? flatMap(this[args](), () => failCause(cause)) : failCause(cause) } }) /** @internal */ export const callback = ( register: ( this: Scheduler.Scheduler, resume: (effect: Effect.Effect) => void, signal: AbortSignal ) => void | Effect.Effect ): Effect.Effect => callbackOptions(register as any, register.length >= 2) /** @internal */ export const never: Effect.Effect = callback(constVoid) /** @internal */ export const gen = < Self, Eff extends Effect.Effect, AEff >( ...args: | [options: { readonly self: Self }, body: (this: Self) => Generator] | [body: () => Generator] ): Effect.Effect< AEff, [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? E : never, [Eff] extends [never] ? never : [Eff] extends [Effect.Effect] ? R : never > => suspend(() => fromIteratorUnsafe( args.length === 1 ? args[0]() : (args[1].call(args[0].self) as any) ) ) /** @internal */ export const fnUntraced: Effect.fn.Untraced = ( body: Function, ...pipeables: Array ) => { const fn = pipeables.length === 0 ? function(this: any) { return suspend(() => fromIteratorUnsafe(body.apply(this, arguments))) } : function(this: any) { let effect = suspend(() => fromIteratorUnsafe(body.apply(this, arguments))) for (let i = 0; i < pipeables.length; i++) { effect = pipeables[i](effect, ...arguments) } return effect } return defineFunctionLength(body.length, fn) } const defineFunctionLength = (length: number, fn: F): F => Object.defineProperty(fn, "length", { value: length, configurable: true }) const fnStackCleaner = makeStackCleaner(2) /** @internal */ export const fn: typeof Effect.fn = function() { const nameFirst = typeof arguments[0] === "string" const name = nameFirst ? arguments[0] : "Effect.fn" const spanOptions = nameFirst ? arguments[1] : undefined const prevLimit = globalThis.Error.stackTraceLimit globalThis.Error.stackTraceLimit = 2 const defError = new globalThis.Error() globalThis.Error.stackTraceLimit = prevLimit if (nameFirst) { return (body: Function | { readonly self: any }, ...pipeables: Array) => makeFn(name, body, defError, pipeables, nameFirst, spanOptions) } return makeFn( name, arguments[0], defError, Array.prototype.slice.call(arguments, 1), nameFirst, spanOptions ) } as any const makeFn = ( name: string, bodyOrOptions: Function | { readonly self: any }, defError: Error, pipeables: Array, addSpan: boolean, spanOptions: Tracer.SpanOptionsNoTrace | undefined ) => { const body = typeof bodyOrOptions === "function" ? bodyOrOptions : (pipeables.pop()!).bind(bodyOrOptions.self) return defineFunctionLength(body.length, function(this: any, ...args: Array) { let result = suspend(() => { const iter = body.apply(this, arguments) return isEffect(iter) ? iter : fromIteratorUnsafe(iter) }) for (let i = 0; i < pipeables.length; i++) { result = pipeables[i](result, ...args) } if (!isEffect(result)) { return result } const prevLimit = globalThis.Error.stackTraceLimit globalThis.Error.stackTraceLimit = 2 const callError = new globalThis.Error() globalThis.Error.stackTraceLimit = prevLimit return updateService( addSpan ? useSpan(name, spanOptions!, (span) => provideParentSpan(result, span)) : result, CurrentStackFrame, (prev) => ({ name, stack: fnStackCleaner(() => callError.stack), parent: { name: `${name} (definition)`, stack: fnStackCleaner(() => defError.stack), parent: prev } }) ) }) } /** @internal */ export const fnUntracedEager: Effect.fn.Untraced = ( body: Function, ...pipeables: Array ) => defineFunctionLength( body.length, pipeables.length === 0 ? function(this: any) { return fromIteratorEagerUnsafe(() => body.apply(this, arguments)) } : function(this: any) { let effect = fromIteratorEagerUnsafe(() => body.apply(this, arguments)) for (const pipeable of pipeables) { effect = pipeable(effect) } return effect } ) const fromIteratorEagerUnsafe = ( evaluate: () => Iterator> ): Effect.Effect => { try { const iterator = evaluate() let value: any = undefined // Try to resolve synchronously in a loop while (true) { const state = iterator.next(value) if (state.done) { return succeed(state.value) } const primitive = state.value as any if (primitive && primitive._tag === "Success") { value = primitive.value continue } else if (primitive && primitive._tag === "Failure") { return state.value } else { let isFirstExecution = true return suspend(() => { if (isFirstExecution) { isFirstExecution = false return flatMap(state.value, (value) => fromIteratorUnsafe(iterator, value)) } else { return suspend(() => fromIteratorUnsafe(evaluate())) } }) } } } catch (error) { return die(error) } } const fromIteratorUnsafe: ( iterator: Iterator>, initial?: undefined ) => Effect.Effect = makePrimitive({ op: "Iterator", single: false, [contA](value, fiber) { const iter = this[args][0] while (true) { const state = iter.next(value) if (state.done) return succeed(state.value) if (!effectIsExit(state.value)) { fiber._stack.push(this) return state.value } else if (state.value._tag === "Failure") { return state.value } value = state.value.value } }, [evaluate](this: any, fiber: FiberImpl) { return this[contA](this[args][1], fiber) } }) // ---------------------------------------------------------------------------- // mapping & sequencing // ---------------------------------------------------------------------------- /** @internal */ export const as: { ( value: B ): (self: Effect.Effect) => Effect.Effect (self: Effect.Effect, value: B): Effect.Effect } = dual( 2, ( self: Effect.Effect, value: B ): Effect.Effect => { const b = succeed(value) return flatMap(self, (_) => b) } ) /** @internal */ export const asSome = ( self: Effect.Effect ): Effect.Effect, E, R> => map(self, Option.some) /** @internal */ export const flip = ( self: Effect.Effect ): Effect.Effect => matchEffect(self, { onFailure: succeed, onSuccess: fail }) /** @internal */ export const andThen: { ( f: (a: A) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( f: Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, f: (a: A) => Effect.Effect ): Effect.Effect ( self: Effect.Effect, f: Effect.Effect ): Effect.Effect } = dual( 2, ( self: Effect.Effect, f: ((a: A) => Effect.Effect) | Effect.Effect ): Effect.Effect => flatMap(self, (a) => isEffect(f) ? f : internalCall(() => (f as (a: A) => Effect.Effect)(a))) ) /** @internal */ export const tap: { ( f: (a: NoInfer) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( f: Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, f: (a: NoInfer) => Effect.Effect ): Effect.Effect ( self: Effect.Effect, f: Effect.Effect ): Effect.Effect } = dual( 2, ( self: Effect.Effect, f: ((a: A) => Effect.Effect) | Effect.Effect ): Effect.Effect => flatMap(self, (a) => as(isEffect(f) ? f : internalCall(() => (f as (a: A) => Effect.Effect)(a)), a)) ) /** @internal */ export const asVoid = ( self: Effect.Effect ): Effect.Effect => flatMap(self, (_) => exitVoid) /** @internal */ export const sandbox = ( self: Effect.Effect ): Effect.Effect, R> => catchCause(self, fail) /** @internal */ export const raceAll = >( all: Iterable, options?: { readonly onWinner?: (options: { readonly fiber: Fiber.Fiber readonly index: number readonly parentFiber: Fiber.Fiber }) => void } ): Effect.Effect< Effect.Success, Effect.Error, Effect.Services > => withFiber((parent) => callback((resume) => { const effects = Arr.fromIterable(all) const len = effects.length let doneCount = 0 let done = false const fibers = new Set>() const failures: Array> = [] const onExit = (exit: Exit.Exit, fiber: Fiber.Fiber, i: number) => { doneCount++ if (exit._tag === "Failure") { failures.push(...exit.cause.reasons) if (doneCount >= len) { resume(failCause(causeFromReasons(failures))) } return } const isWinner = !done done = true resume( fibers.size === 0 ? exit : flatMap(uninterruptible(fiberInterruptAll(fibers)), () => exit) ) if (isWinner && options?.onWinner) { options.onWinner({ fiber, index: i, parentFiber: parent }) } } for (let i = 0; i < len; i++) { const fiber = forkUnsafe(parent, effects[i], true, true, false) fibers.add(fiber) fiber.addObserver((exit) => { fibers.delete(fiber) onExit(exit, fiber, i) }) if (done) break } return fiberInterruptAll(fibers) }) ) /** @internal */ export const raceAllFirst = >( all: Iterable, options?: { readonly onWinner?: (options: { readonly fiber: Fiber.Fiber readonly index: number readonly parentFiber: Fiber.Fiber }) => void } ): Effect.Effect< Effect.Success, Effect.Error, Effect.Services > => withFiber((parent) => callback((resume) => { let done = false const fibers = new Set>() const onExit = (exit: Exit.Exit) => { done = true resume( fibers.size === 0 ? exit : flatMap(uninterruptible(fiberInterruptAll(fibers)), () => exit) ) } let i = 0 for (const effect of all) { if (done) break const index = i++ const fiber = forkUnsafe(parent, effect, true, true, false) fibers.add(fiber) fiber.addObserver((exit) => { fibers.delete(fiber) const isWinner = !done onExit(exit) if (isWinner && options?.onWinner) { options.onWinner({ fiber, index, parentFiber: parent }) } }) } return fiberInterruptAll(fibers) }) ) /** @internal */ export const race: { ( that: Effect.Effect, options?: { readonly onWinner?: (options: { readonly fiber: Fiber.Fiber readonly index: number readonly parentFiber: Fiber.Fiber }) => void } ): ( self: Effect.Effect ) => Effect.Effect ( self: Effect.Effect, that: Effect.Effect, options?: { readonly onWinner?: (options: { readonly fiber: Fiber.Fiber readonly index: number readonly parentFiber: Fiber.Fiber }) => void } ): Effect.Effect } = dual( (args) => isEffect(args[1]), ( self: Effect.Effect, that: Effect.Effect, options?: { readonly onWinner?: (options: { readonly fiber: Fiber.Fiber readonly index: number readonly parentFiber: Fiber.Fiber }) => void } ): Effect.Effect => raceAll([self, that], options) ) /** @internal */ export const raceFirst: { ( that: Effect.Effect, options?: { readonly onWinner?: (options: { readonly fiber: Fiber.Fiber readonly index: number readonly parentFiber: Fiber.Fiber }) => void } ): ( self: Effect.Effect ) => Effect.Effect ( self: Effect.Effect, that: Effect.Effect, options?: { readonly onWinner?: (options: { readonly fiber: Fiber.Fiber readonly index: number readonly parentFiber: Fiber.Fiber }) => void } ): Effect.Effect } = dual( (args) => isEffect(args[1]), ( self: Effect.Effect, that: Effect.Effect, options?: { readonly onWinner?: (options: { readonly fiber: Fiber.Fiber readonly index: number readonly parentFiber: Fiber.Fiber }) => void } ): Effect.Effect => raceAllFirst([self, that], options) ) /** @internal */ export const flatMap: { ( f: (a: A) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, f: (a: A) => Effect.Effect ): Effect.Effect } = dual( 2, ( self: Effect.Effect, f: (a: A) => Effect.Effect ): Effect.Effect => { const onSuccess = Object.create(OnSuccessProto) onSuccess[args] = self onSuccess[contA] = f.length !== 1 ? (a: A) => f(a) : f return onSuccess } ) const OnSuccessProto = makePrimitiveProto({ op: "OnSuccess", [evaluate](this: any, fiber: FiberImpl): Primitive { fiber._stack.push(this) return this[args] } }) /** @internal */ export const matchCauseEffectEager: { (options: { readonly onFailure: (cause: Cause.Cause) => Effect.Effect readonly onSuccess: (a: A) => Effect.Effect }): ( self: Effect.Effect ) => Effect.Effect ( self: Effect.Effect, options: { readonly onFailure: (cause: Cause.Cause) => Effect.Effect readonly onSuccess: (a: A) => Effect.Effect } ): Effect.Effect } = dual( 2, ( self: Effect.Effect, options: { readonly onFailure: (cause: Cause.Cause) => Effect.Effect readonly onSuccess: (a: A) => Effect.Effect } ): Effect.Effect => { if (effectIsExit(self)) { return self._tag === "Success" ? options.onSuccess(self.value) : options.onFailure(self.cause) } return matchCauseEffect(self, options) } ) /** @internal */ export const effectIsExit = (effect: Effect.Effect): effect is Exit.Exit => ExitTypeId in effect /** @internal */ export const flatMapEager: { ( f: (a: A) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, f: (a: A) => Effect.Effect ): Effect.Effect } = dual( 2, ( self: Effect.Effect, f: (a: A) => Effect.Effect ): Effect.Effect => { if (effectIsExit(self)) { return self._tag === "Success" ? f(self.value) : self as Exit.Exit } return flatMap(self, f) } ) // ---------------------------------------------------------------------------- // mapping & sequencing // ---------------------------------------------------------------------------- /** @internal */ export const flatten = ( self: Effect.Effect, E2, R2> ): Effect.Effect => flatMap(self, identity) /** @internal */ export const map: { ( f: (a: A) => B ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, f: (a: A) => B ): Effect.Effect } = dual( 2, ( self: Effect.Effect, f: (a: A) => B ): Effect.Effect => flatMap(self, (a) => succeed(internalCall(() => f(a)))) ) /** @internal */ export const mapEager: { ( f: (a: A) => B ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, f: (a: A) => B ): Effect.Effect } = dual( 2, ( self: Effect.Effect, f: (a: A) => B ): Effect.Effect => effectIsExit(self) ? exitMap(self, f) : map(self, f) ) /** @internal */ export const mapErrorEager: { ( f: (e: E) => E2 ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, f: (e: E) => E2 ): Effect.Effect } = dual( 2, ( self: Effect.Effect, f: (e: E) => E2 ): Effect.Effect => effectIsExit(self) ? exitMapError(self, f) : mapError(self, f) ) /** @internal */ export const mapBothEager: { ( options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } ): Effect.Effect } = dual( 2, ( self: Effect.Effect, options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } ): Effect.Effect => effectIsExit(self) ? exitMapBoth(self, options) : mapBoth(self, options) ) /** @internal */ export const catchEager: { ( f: (e: NoInfer) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, f: (e: NoInfer) => Effect.Effect ): Effect.Effect } = dual( 2, ( self: Effect.Effect, f: (e: NoInfer) => Effect.Effect ): Effect.Effect => { if (effectIsExit(self)) { if (self._tag === "Success") return self as Exit.Exit const error = findError(self.cause) if (Result.isFailure(error)) return self as Exit.Exit return f(error.success) } return catch_(self, f) } ) // ---------------------------------------------------------------------------- // Exit // ---------------------------------------------------------------------------- /** @internal */ export const exitInterrupt = (fiberId?: number | undefined): Exit.Exit => exitFailCause(causeInterrupt(fiberId)) /** @internal */ export const exitIsSuccess = ( self: Exit.Exit ): self is Exit.Success => self._tag === "Success" /** @internal */ export const exitFilterSuccess = ( self: Exit.Exit ): Result.Result, Exit.Failure> => self._tag === "Success" ? Result.succeed(self as any) : Result.fail(self as any) /** @internal */ export const exitFilterValue = ( self: Exit.Exit ): Result.Result> => self._tag === "Success" ? Result.succeed(self.value) : Result.fail(self as any) /** @internal */ export const exitIsFailure = ( self: Exit.Exit ): self is Exit.Failure => self._tag === "Failure" /** @internal */ export const exitFilterFailure = ( self: Exit.Exit ): Result.Result, Exit.Success> => self._tag === "Failure" ? Result.succeed(self as any) : Result.fail(self as any) /** @internal */ export const exitFilterCause = ( self: Exit.Exit ): Result.Result, Exit.Success> => self._tag === "Failure" ? Result.succeed(self.cause) : Result.fail(self as any) /** @internal */ export const exitFindError = Filter.composePassthrough( exitFilterCause, findError ) /** @internal */ export const exitFindDefect = Filter.composePassthrough( exitFilterCause, findDefect ) /** @internal */ export const exitHasInterrupts = ( self: Exit.Exit ): self is Exit.Failure => self._tag === "Failure" && hasInterrupts(self.cause) /** @internal */ export const exitHasDies = ( self: Exit.Exit ): self is Exit.Failure => self._tag === "Failure" && hasDies(self.cause) /** @internal */ export const exitHasFails = ( self: Exit.Exit ): self is Exit.Failure => self._tag === "Failure" && hasFails(self.cause) /** @internal */ export const exitVoid: Exit.Exit = exitSucceed(void 0) /** @internal */ export const exitMap: { (f: (a: A) => B): (self: Exit.Exit) => Exit.Exit (self: Exit.Exit, f: (a: A) => B): Exit.Exit } = dual( 2, (self: Exit.Exit, f: (a: A) => B): Exit.Exit => self._tag === "Success" ? exitSucceed(f(self.value)) : (self as any) ) /** @internal */ export const exitMapError: { (f: (a: NoInfer) => E2): (self: Exit.Exit) => Exit.Exit (self: Exit.Exit, f: (a: NoInfer) => E2): Exit.Exit } = dual( 2, (self: Exit.Exit, f: (a: NoInfer) => E2): Exit.Exit => { if (self._tag === "Success") return self as Exit.Exit const error = findError(self.cause) if (Result.isFailure(error)) return self as Exit.Exit return exitFail(f(error.success)) } ) /** @internal */ export const exitMapBoth: { ( options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } ): (self: Exit.Exit) => Exit.Exit ( self: Exit.Exit, options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } ): Exit.Exit } = dual( 2, ( self: Exit.Exit, options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } ): Exit.Exit => { if (self._tag === "Success") return exitSucceed(options.onSuccess(self.value)) const error = findError(self.cause) if (Result.isFailure(error)) return self as Exit.Exit return exitFail(options.onFailure(error.success)) } ) /** @internal */ export const exitAs: { (b: B): (self: Exit.Exit) => Exit.Exit (self: Exit.Exit, b: B): Exit.Exit } = dual( 2, (self: Exit.Exit, b: B): Exit.Exit => exitIsSuccess(self) ? exitSucceed(b) : (self as any) ) /** @internal */ export const exitZipRight: { ( that: Exit.Exit ): (self: Exit.Exit) => Exit.Exit ( self: Exit.Exit, that: Exit.Exit ): Exit.Exit } = dual( 2, ( self: Exit.Exit, that: Exit.Exit ): Exit.Exit => (exitIsSuccess(self) ? that : (self as any)) ) /** @internal */ export const exitMatch: { (options: { readonly onSuccess: (a: NoInfer) => X1 readonly onFailure: (cause: Cause.Cause>) => X2 }): (self: Exit.Exit) => X1 | X2 ( self: Exit.Exit, options: { readonly onSuccess: (a: A) => X1 readonly onFailure: (cause: Cause.Cause) => X2 } ): X1 | X2 } = dual( 2, ( self: Exit.Exit, options: { readonly onSuccess: (a: A) => X1 readonly onFailure: (cause: Cause.Cause) => X2 } ): X1 | X2 => exitIsSuccess(self) ? options.onSuccess(self.value) : options.onFailure(self.cause) ) /** @internal */ export const exitAsVoid: (self: Exit.Exit) => Exit.Exit = exitAs(void 0) /** @internal */ export const exitAsVoidAll = >>( exits: I ): Exit.Exit< void, I extends Iterable> ? _E : never > => { const failures: Array> = [] for (const exit of exits) { if (exit._tag === "Failure") { failures.push(...exit.cause.reasons) } } return failures.length === 0 ? exitVoid : exitFailCause(causeFromReasons(failures)) } /** @internal */ export const exitGetSuccess = (self: Exit.Exit): Option.Option => exitIsSuccess(self) ? Option.some(self.value) : Option.none() /** @internal */ export const exitGetCause = (self: Exit.Exit): Option.Option> => exitIsFailure(self) ? Option.some(self.cause) : Option.none() /** @internal */ export const exitFindErrorOption = (self: Exit.Exit): Option.Option => { const error = exitFindError(self) return Result.isFailure(error) ? Option.none() : Option.some(error.success) } // ---------------------------------------------------------------------------- // environment // ---------------------------------------------------------------------------- /** @internal */ export const service = (service: Context.Key): Effect.Effect => service /** @internal */ export const serviceOption = ( service: Context.Key ): Effect.Effect> => withFiber((fiber) => succeed(Context.getOption(fiber.context, service))) /** @internal */ export const serviceOptional = ( service: Context.Key ): Effect.Effect => withFiber((fiber) => fiber.context.mapUnsafe.has(service.key) ? succeed(Context.getUnsafe(fiber.context, service)) : fail(new NoSuchElementError()) ) /** @internal */ export const updateContext: { ( f: (context: Context.Context) => Context.Context> ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, f: (context: Context.Context) => Context.Context> ): Effect.Effect } = dual( 2, ( self: Effect.Effect, f: (context: Context.Context) => Context.Context> ): Effect.Effect => withFiber((fiber) => { const prevContext = fiber.context as Context.Context const nextContext = f(prevContext) if (prevContext === nextContext) return self as any fiber.setContext(nextContext) return onExitPrimitive(self, () => { fiber.setContext(prevContext) return undefined }) }) ) /** @internal */ export const updateService: { ( service: Context.Key, f: (value: A) => A ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, service: Context.Key, f: (value: A) => A ): Effect.Effect } = dual( 3, ( self: Effect.Effect, service: Context.Key, f: (value: A) => A ): Effect.Effect => updateContext(self, (s) => { const prev = Context.getUnsafe(s, service) const next = f(prev) if (prev === next) return s return Context.add(s, service, next) }) ) /** @internal */ export const context = (): Effect.Effect> => getContext as any const getContext = withFiber((fiber) => succeed(fiber.context)) /** @internal */ export const contextWith = ( f: (context: Context.Context) => Effect.Effect ): Effect.Effect => withFiber((fiber) => f(fiber.context as Context.Context)) /** @internal */ export const provideContext: { ( context: Context.Context ): ( self: Effect.Effect ) => Effect.Effect> ( self: Effect.Effect, context: Context.Context ): Effect.Effect> } = dual( 2, ( self: Effect.Effect, context: Context.Context ): Effect.Effect> => { if (effectIsExit(self)) return self as any return updateContext(self, Context.merge(context)) as any } ) /** @internal */ export const provideService: { ( service: Context.Key ): { (implementation: S): (self: Effect.Effect) => Effect.Effect> (self: Effect.Effect, implementation: S): Effect.Effect> } ( key: Context.Key, implementation: S ): ( self: Effect.Effect ) => Effect.Effect> ( self: Effect.Effect, service: Context.Key, implementation: S ): Effect.Effect> } = function(this: any) { if (arguments.length === 1) { return dual(2, (self, impl) => provideServiceImpl(self, arguments[0], impl)) as any } return dual(3, (self, service, impl) => provideServiceImpl(self, service, impl)) .apply(this, arguments as any) as any } const provideServiceImpl = ( self: Effect.Effect, service: Context.Key, implementation: S ): Effect.Effect> => updateContext(self, (s) => { const prev = s.mapUnsafe.get(service.key) if (prev === implementation) return s return Context.add(s, service, implementation) }) as any /** @internal */ export const provideServiceEffect: { ( service: Context.Key, acquire: Effect.Effect ): ( self: Effect.Effect ) => Effect.Effect | R2> ( self: Effect.Effect, service: Context.Key, acquire: Effect.Effect ): Effect.Effect | R2> } = dual( 3, ( self: Effect.Effect, service: Context.Key, acquire: Effect.Effect ): Effect.Effect | R2> => flatMap(acquire, (implementation) => provideService(self, service, implementation)) ) /** @internal */ export const withConcurrency: { ( concurrency: "unbounded" | number ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, concurrency: "unbounded" | number ): Effect.Effect } = provideService(CurrentConcurrency) // ---------------------------------------------------------------------------- // zipping // ---------------------------------------------------------------------------- /** @internal */ export const zip: { ( that: Effect.Effect, options?: { readonly concurrent?: boolean | undefined } | undefined ): ( self: Effect.Effect ) => Effect.Effect<[A, A2], E2 | E, R2 | R> ( self: Effect.Effect, that: Effect.Effect, options?: { readonly concurrent?: boolean | undefined } ): Effect.Effect<[A, A2], E | E2, R | R2> } = dual( (args) => isEffect(args[1]), ( self: Effect.Effect, that: Effect.Effect, options?: { readonly concurrent?: boolean | undefined } ): Effect.Effect<[A, A2], E | E2, R | R2> => zipWith(self, that, (a, a2) => [a, a2], options) ) /** @internal */ export const zipWith: { ( that: Effect.Effect, f: (a: A, b: A2) => B, options?: { readonly concurrent?: boolean | undefined } ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, that: Effect.Effect, f: (a: A, b: A2) => B, options?: { readonly concurrent?: boolean | undefined } ): Effect.Effect } = dual( (args) => isEffect(args[1]), ( self: Effect.Effect, that: Effect.Effect, f: (a: A, b: A2) => B, options?: { readonly concurrent?: boolean | undefined } ): Effect.Effect => options?.concurrent // Use `all` exclusively for concurrent cases, as it introduces additional overhead due to the management of concurrency ? map(all([self, that], { concurrency: 2 }), ([a, a2]) => internalCall(() => f(a, a2))) : flatMap(self, (a) => map(that, (a2) => internalCall(() => f(a, a2)))) ) // ---------------------------------------------------------------------------- // filtering & conditionals // ---------------------------------------------------------------------------- /* @internal */ export const filterOrFail: { ( refinement: Predicate.Refinement, B>, orFailWith: (a: NoInfer) => E2 ): (self: Effect.Effect) => Effect.Effect ( predicate: Predicate.Predicate>, orFailWith: (a: NoInfer) => E2 ): (self: Effect.Effect) => Effect.Effect ( refinement: Predicate.Refinement, B> ): (self: Effect.Effect) => Effect.Effect ( predicate: Predicate.Predicate> ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, refinement: Predicate.Refinement, B>, orFailWith: (a: NoInfer) => E2 ): Effect.Effect ( self: Effect.Effect, predicate: Predicate.Predicate>, orFailWith: (a: NoInfer) => E2 ): Effect.Effect ( self: Effect.Effect, refinement: Predicate.Refinement, B> ): Effect.Effect ( self: Effect.Effect, predicate: Predicate.Predicate> ): Effect.Effect } = dual((args) => isEffect(args[0]), ( self: Effect.Effect, predicate: Predicate.Predicate>, orFailWith?: (a: any) => E2 ): Effect.Effect => filterOrElse( self, predicate as any, orFailWith ? (a: any) => fail(orFailWith(a)) : () => fail(new NoSuchElementError() as E2) )) /** @internal */ export const when: { ( condition: Effect.Effect ): ( self: Effect.Effect ) => Effect.Effect, E | E2, R | R2> ( self: Effect.Effect, condition: Effect.Effect ): Effect.Effect, E | E2, R | R2> } = dual( 2, ( self: Effect.Effect, condition: Effect.Effect ): Effect.Effect, E | E2, R | R2> => flatMap(condition, (pass) => pass ? asSome(self) : succeedNone) ) // ---------------------------------------------------------------------------- // repetition // ---------------------------------------------------------------------------- /** @internal */ export const replicate: { ( n: number ): (self: Effect.Effect) => Array> ( self: Effect.Effect, n: number ): Array> } = dual( 2, ( self: Effect.Effect, n: number ): Array> => Array.from({ length: n }, () => self) ) /** @internal */ export const replicateEffect: { ( n: number, options?: { readonly concurrency?: Concurrency | undefined readonly discard?: false | undefined } ): (self: Effect.Effect) => Effect.Effect, E, R> ( n: number, options: { readonly concurrency?: Concurrency | undefined readonly discard: true } ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, n: number, options?: { readonly concurrency?: Concurrency | undefined readonly discard?: false | undefined } ): Effect.Effect, E, R> ( self: Effect.Effect, n: number, options: { readonly concurrency?: Concurrency | undefined readonly discard: true } ): Effect.Effect } = dual( (args) => isEffect(args[0]), ( self: Effect.Effect, n: number, options: { readonly concurrency?: Concurrency | undefined readonly discard: true } ): Effect.Effect => all(replicate(self, n), options) ) /** @internal */ export const forever: { < Arg extends Effect.Effect | { readonly disableYield?: boolean | undefined } | undefined = { readonly disableYield?: boolean | undefined } >( effectOrOptions: Arg, options?: { readonly disableYield?: boolean | undefined } | undefined ): [Arg] extends [Effect.Effect] ? Effect.Effect : (self: Effect.Effect) => Effect.Effect } = dual((args) => isEffect(args[0]), ( self: Effect.Effect, options?: { readonly disableYield?: boolean | undefined } ): Effect.Effect => whileLoop({ while: constTrue, body: constant(options?.disableYield ? self : flatMap(self, (_) => yieldNow)), step: constVoid }) as any) // ---------------------------------------------------------------------------- // error handling // ---------------------------------------------------------------------------- /** @internal */ export const catchCause: { ( f: (cause: NoInfer>) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, f: (cause: NoInfer>) => Effect.Effect ): Effect.Effect } = dual( 2, ( self: Effect.Effect, f: (cause: NoInfer>) => Effect.Effect ): Effect.Effect => { const onFailure = Object.create(OnFailureProto) onFailure[args] = self onFailure[contE] = f.length !== 1 ? (cause: Cause.Cause) => f(cause) : f return onFailure } ) const OnFailureProto = makePrimitiveProto({ op: "OnFailure", [evaluate](this: any, fiber: FiberImpl): Primitive { fiber._stack.push(this as any) return this[args] } }) /** @internal */ export const catchCauseIf: { ( predicate: Predicate.Predicate>, f: (cause: Cause.Cause) => Effect.Effect ): ( self: Effect.Effect ) => Effect.Effect ( self: Effect.Effect, predicate: Predicate.Predicate>, f: (cause: Cause.Cause) => Effect.Effect ): Effect.Effect } = dual( 3, ( self: Effect.Effect, predicate: Predicate.Predicate>, f: (cause: Cause.Cause) => Effect.Effect ): Effect.Effect => catchCause(self, (cause): Effect.Effect => { if (!predicate(cause)) { return failCause(cause) as any } return internalCall(() => f(cause)) }) ) /** @internal */ export const catchCauseFilter: { >( filter: Filter.Filter, EB, X>, f: (failure: EB, cause: Cause.Cause) => Effect.Effect ): ( self: Effect.Effect ) => Effect.Effect | E2, R | R2> >( self: Effect.Effect, filter: Filter.Filter, EB, X>, f: (failure: EB, cause: Cause.Cause) => Effect.Effect ): Effect.Effect | E2, R | R2> } = dual( 3, >( self: Effect.Effect, filter: Filter.Filter, EB, X>, f: (failure: EB, cause: Cause.Cause) => Effect.Effect ): Effect.Effect | E2, R | R2> => catchCause(self, (cause): Effect.Effect | E2, R2> => { const eb = filter(cause) return Result.isFailure(eb) ? failCause(eb.failure) : internalCall(() => f(eb.success, cause)) }) ) /** @internal */ export const catch_: { ( f: (e: NoInfer) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, f: (e: NoInfer) => Effect.Effect ): Effect.Effect } = dual( 2, ( self: Effect.Effect, f: (a: NoInfer) => Effect.Effect ): Effect.Effect => catchCauseFilter(self, findError as any, (e: any) => f(e)) as any ) /** @internal */ export const catchNoSuchElement = ( self: Effect.Effect ): Effect.Effect, Exclude, R> => matchEffect(self, { onFailure: (error) => isNoSuchElementError(error) ? succeedNone : fail(error as Exclude), onSuccess: succeedSome }) /** @internal */ export const catchDefect: { ( f: (defect: unknown) => Effect.Effect ): ( self: Effect.Effect ) => Effect.Effect ( self: Effect.Effect, f: (defect: unknown) => Effect.Effect ): Effect.Effect } = dual( 2, ( self: Effect.Effect, f: (defect: unknown) => Effect.Effect ): Effect.Effect => catchCauseFilter(self, findDefect as any, f as any) as any ) /** @internal */ export const tapCause: { ( f: (cause: NoInfer>) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, f: (cause: NoInfer>) => Effect.Effect ): Effect.Effect } = dual( 2, ( self: Effect.Effect, f: (cause: NoInfer>) => Effect.Effect ): Effect.Effect => catchCause(self, (cause) => andThen(internalCall(() => f(cause)), failCause(cause))) ) /** @internal */ export const tapCauseIf: { ( predicate: Predicate.Predicate>, f: (cause: Cause.Cause) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, predicate: Predicate.Predicate>, f: (cause: Cause.Cause) => Effect.Effect ): Effect.Effect } = dual( 3, ( self: Effect.Effect, predicate: Predicate.Predicate>, f: (cause: Cause.Cause) => Effect.Effect ): Effect.Effect => catchCauseIf( self, predicate, (cause) => andThen(internalCall(() => f(cause)), failCause(cause)) ) ) /** @internal */ export const tapCauseFilter: { >( filter: Filter.Filter, EB, X>, f: (a: EB, cause: Cause.Cause) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect >( self: Effect.Effect, filter: Filter.Filter, EB, X>, f: (a: EB, cause: Cause.Cause) => Effect.Effect ): Effect.Effect } = dual( 3, >( self: Effect.Effect, filter: Filter.Filter, EB, X>, f: (a: EB, cause: Cause.Cause) => Effect.Effect ): Effect.Effect => catchCause(self, (cause) => { const result = filter(cause) if (Result.isFailure(result)) { return failCause(cause) } return andThen(internalCall(() => f(result.success, cause)), failCause(cause)) }) ) /** @internal */ export const tapError: { ( f: (e: NoInfer) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, f: (e: NoInfer) => Effect.Effect ): Effect.Effect } = dual( 2, ( self: Effect.Effect, f: (e: NoInfer) => Effect.Effect ): Effect.Effect => tapCauseFilter(self, findError as any, (e: any) => f(e)) as any ) /** @internal */ export const tapErrorTag: { | Arr.NonEmptyReadonlyArray>, E, A1, E1, R1>( k: K, f: ( e: ExtractTag, K extends Arr.NonEmptyReadonlyArray ? K[number] : K> ) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect < A, E, R, const K extends Tags | Arr.NonEmptyReadonlyArray>, R1, E1, A1 >( self: Effect.Effect, k: K, f: (e: ExtractTag ? K[number] : K>) => Effect.Effect ): Effect.Effect } = dual( 3, < A, E, R, const K extends Tags | Arr.NonEmptyReadonlyArray>, R1, E1, A1 >( self: Effect.Effect, k: K, f: (e: ExtractTag ? K[number] : K>) => Effect.Effect ): Effect.Effect => { const predicate = Array.isArray(k) ? ((e: E): e is ExtractTag ? K[number] : K> => hasProperty(e, "_tag") && k.includes(e._tag)) : isTagged(k as string) return tapError( self, (error) => predicate(error) ? f(error as ExtractTag ? K[number] : K>) : void_ ) } ) /** @internal */ export const tapDefect: { ( f: (defect: unknown) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, f: (defect: unknown) => Effect.Effect ): Effect.Effect } = dual( 2, ( self: Effect.Effect, f: (defect: unknown) => Effect.Effect ): Effect.Effect => tapCauseFilter(self, findDefect as any, (_: any) => f(_)) as any ) /** @internal */ export const catchIf: { , R3 = never>( refinement: Predicate.Refinement, EB>, f: (e: EB) => Effect.Effect, orElse?: ((e: Exclude) => Effect.Effect) | undefined ): (self: Effect.Effect) => Effect.Effect ( predicate: Predicate.Predicate>, f: (e: NoInfer) => Effect.Effect, orElse?: ((e: NoInfer) => Effect.Effect) | undefined ): (self: Effect.Effect) => Effect.Effect , R3 = never>( self: Effect.Effect, refinement: Predicate.Refinement, f: (e: EB) => Effect.Effect, orElse?: ((e: Exclude) => Effect.Effect) | undefined ): Effect.Effect ( self: Effect.Effect, predicate: Predicate.Predicate, f: (e: E) => Effect.Effect, orElse?: ((e: E) => Effect.Effect) | undefined ): Effect.Effect } = dual( (args) => isEffect(args[0]), ( self: Effect.Effect, predicate: Predicate.Predicate, f: (e: E) => Effect.Effect, orElse?: ((e: E) => Effect.Effect) | undefined ): Effect.Effect => catchCause(self, (cause): Effect.Effect => { const error = findError(cause) if (Result.isFailure(error)) return failCause(error.failure) if (!predicate(error.success)) { return orElse ? internalCall(() => orElse(error.success as any)) : failCause(cause as any as Cause.Cause) } return internalCall(() => f(error.success as any)) }) ) /** @internal */ export const catchFilter: { ( filter: Filter.Filter, EB, X>, f: (e: EB) => Effect.Effect, orElse?: ((e: X) => Effect.Effect) | undefined ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, filter: Filter.Filter, EB, X>, f: (e: EB) => Effect.Effect, orElse?: ((e: X) => Effect.Effect) | undefined ): Effect.Effect } = dual( (args) => isEffect(args[0]), ( self: Effect.Effect, filter: Filter.Filter, EB, X>, f: (e: EB) => Effect.Effect, orElse?: ((e: X) => Effect.Effect) | undefined ): Effect.Effect => catchCause(self, (cause): Effect.Effect => { const error = findError(cause) if (Result.isFailure(error)) return failCause(error.failure) const result = filter(error.success) if (Result.isFailure(result)) { return orElse ? internalCall(() => orElse(result.failure as any)) : failCause(cause as any as Cause.Cause) } return internalCall(() => f(result.success)) }) ) /** @internal */ export const catchTag: { < const K extends Tags | Arr.NonEmptyReadonlyArray>, E, A1, E1, R1, A2 = never, E2 = ExcludeTag ? K[number] : K>, R2 = never >( k: K, f: ( e: ExtractTag, K extends Arr.NonEmptyReadonlyArray ? K[number] : K> ) => Effect.Effect, orElse?: | ((e: ExcludeTag ? K[number] : K>) => Effect.Effect) | undefined ): ( self: Effect.Effect ) => Effect.Effect < A, E, R, const K extends Tags | Arr.NonEmptyReadonlyArray>, R1, E1, A1, A2 = never, E2 = ExcludeTag ? K[number] : K>, R2 = never >( self: Effect.Effect, k: K, f: (e: ExtractTag ? K[number] : K>) => Effect.Effect, orElse?: | ((e: ExcludeTag ? K[number] : K>) => Effect.Effect) | undefined ): Effect.Effect } = dual( (args) => isEffect(args[0]), < A, E, R, const K extends Tags | Arr.NonEmptyReadonlyArray>, R1, E1, A1, A2 = never, E2 = ExcludeTag ? K[number] : K>, R2 = never >( self: Effect.Effect, k: K, f: (e: ExtractTag ? K[number] : K>) => Effect.Effect, orElse?: | ((e: ExcludeTag ? K[number] : K>) => Effect.Effect) | undefined ): Effect.Effect => { const pred = Array.isArray(k) ? ((e: E): e is any => hasProperty(e, "_tag") && k.includes(e._tag)) : isTagged(k as string) return catchIf(self, pred, f, orElse as any) as any } ) /** @internal */ export const catchTags: { < E, Cases extends (E extends { _tag: string } ? { [K in E["_tag"]]+?: (error: Extract) => Effect.Effect } : {}), A2 = never, E2 = Exclude, R2 = never >( cases: Cases, orElse?: ((e: Exclude) => Effect.Effect) | undefined ): (self: Effect.Effect) => Effect.Effect< | A | A2 | { [K in keyof Cases]: Cases[K] extends ((...args: Array) => Effect.Effect) ? A : never }[keyof Cases], | E2 | { [K in keyof Cases]: Cases[K] extends ((...args: Array) => Effect.Effect) ? E : never }[keyof Cases], | R | R2 | { [K in keyof Cases]: Cases[K] extends ((...args: Array) => Effect.Effect) ? R : never }[keyof Cases] > < R, E, A, Cases extends (E extends { _tag: string } ? { [K in E["_tag"]]+?: (error: Extract) => Effect.Effect } : {}), A2 = never, E2 = Exclude, R2 = never >( self: Effect.Effect, cases: Cases, orElse?: ((e: Exclude) => Effect.Effect) | undefined ): Effect.Effect< | A | A2 | { [K in keyof Cases]: Cases[K] extends ((...args: Array) => Effect.Effect) ? A : never }[keyof Cases], | E2 | { [K in keyof Cases]: Cases[K] extends ((...args: Array) => Effect.Effect) ? E : never }[keyof Cases], | R | R2 | { [K in keyof Cases]: Cases[K] extends ((...args: Array) => Effect.Effect) ? R : never }[keyof Cases] > } = dual((args) => isEffect(args[0]), (self: Effect.Effect, cases: Record, orElse: any) => { let keys: Array return catchFilter( self, (e) => { keys ??= Object.keys(cases) return hasProperty(e, "_tag") && isString(e["_tag"]) && keys.includes(e["_tag"]) ? Result.succeed(e) : Result.fail(e) }, (e: any) => internalCall(() => cases[e["_tag"] as string](e)), orElse ) as any }) /** @internal */ export const catchReason: { < K extends Tags, E, RK extends ReasonTags, K>>, A2, E2, R2, A3 = unassigned, E3 = never, R3 = never >( errorTag: K, reasonTag: RK, f: ( reason: ExtractReason, K>, RK>, error: NarrowReason, K>, RK> ) => Effect.Effect, orElse?: | (( reasons: ExcludeReason, K>, RK>, error: OmitReason, K>, RK> ) => Effect.Effect) | undefined ): ( self: Effect.Effect ) => Effect.Effect< A | A2 | Exclude, (A3 extends unassigned ? E : ExcludeTag) | E2 | E3, R | R2 | R3 > < A, E, R, K extends Tags, RK extends ReasonTags>, A2, E2, R2, A3 = unassigned, E3 = never, R3 = never >( self: Effect.Effect, errorTag: K, reasonTag: RK, f: ( reason: ExtractReason, RK>, error: NarrowReason, RK> ) => Effect.Effect, orElse?: | (( reasons: ExcludeReason, RK>, error: OmitReason, RK> ) => Effect.Effect) | undefined ): Effect.Effect< A | A2 | Exclude, (A3 extends unassigned ? E : ExcludeTag) | E2 | E3, R | R2 | R3 > } = dual( (args) => isEffect(args[0]), < A, E, R, K extends Tags, RK extends ReasonTags>, A2, E2, R2, A3 = unassigned, E3 = never, R3 = never >( self: Effect.Effect, errorTag: K, reasonTag: RK, f: (reason: ExtractReason, RK>, error: ExtractTag) => Effect.Effect, orElse?: | (( reasons: ExcludeReason, RK>, error: OmitReason, RK> ) => Effect.Effect) | undefined ): Effect.Effect< A | A2 | Exclude, (A3 extends unassigned ? E : ExcludeTag) | E2 | E3, R | R2 | R3 > => catchIf( self, ((e: any) => isTagged(e, errorTag) && hasProperty(e, "reason")) as any, (e: any): Effect.Effect => { const reason = e.reason as any if (isTagged(reason, reasonTag)) return f(reason as any, e) return orElse ? internalCall(() => orElse(reason, e)) : fail(e) } ) as any ) /** @internal */ export const catchReasons: { < K extends Tags, E, Cases extends { [RK in ReasonTags, K>>]+?: ( reason: ExtractReason, K>, RK>, error: NarrowReason, K>, RK> ) => Effect.Effect }, A2 = unassigned, E2 = never, R2 = never >( errorTag: K, cases: Cases, orElse?: | (( reason: ExcludeReason, K>, Extract>, error: OmitReason, K>, Extract> ) => Effect.Effect) | undefined ): (self: Effect.Effect) => Effect.Effect< | A | Exclude | { [RK in keyof Cases]: Cases[RK] extends (...args: Array) => Effect.Effect ? A : never }[ keyof Cases ], | (A2 extends unassigned ? E : ExcludeTag) | E2 | { [RK in keyof Cases]: Cases[RK] extends (...args: Array) => Effect.Effect ? E : never }[ keyof Cases ], | R | R2 | { [RK in keyof Cases]: Cases[RK] extends (...args: Array) => Effect.Effect ? R : never }[ keyof Cases ] > < A, E, R, K extends Tags, Cases extends { [RK in ReasonTags>]+?: ( reason: ExtractReason, RK>, error: NarrowReason, RK> ) => Effect.Effect }, A2 = unassigned, E2 = never, R2 = never >( self: Effect.Effect, errorTag: K, cases: Cases, orElse?: | (( reason: ExcludeReason, K>, Extract>, error: OmitReason, K>, Extract> ) => Effect.Effect) | undefined ): Effect.Effect< | A | Exclude | { [RK in keyof Cases]: Cases[RK] extends (...args: Array) => Effect.Effect ? A : never }[ keyof Cases ], | (A2 extends unassigned ? E : ExcludeTag) | E2 | { [RK in keyof Cases]: Cases[RK] extends (...args: Array) => Effect.Effect ? E : never }[ keyof Cases ], | R | R2 | { [RK in keyof Cases]: Cases[RK] extends (...args: Array) => Effect.Effect ? R : never }[ keyof Cases ] > } = dual((args) => isEffect(args[0]), (self, errorTag, cases, orElse) => { let keys: Array return catchIf( self, ((e: any) => isTagged(e, errorTag) && hasProperty(e, "reason") && hasProperty(e.reason, "_tag") && isString(e.reason._tag)) as any, (e: any) => { const reason = e.reason keys ??= Object.keys(cases) if (keys.includes(reason._tag)) { return internalCall(() => (cases as any)[reason._tag](reason, e)) } return orElse ? internalCall(() => orElse(reason, e)) : fail(e) } ) }) /** @internal */ export const unwrapReason: { < K extends Effect.TagsWithReason, E >( errorTag: K ): (self: Effect.Effect) => Effect.Effect | ReasonOf>, R> < A, E, R, K extends Effect.TagsWithReason >( self: Effect.Effect, errorTag: K ): Effect.Effect | ReasonOf>, R> } = dual( 2, < A, E, R, K extends Effect.TagsWithReason >( self: Effect.Effect, errorTag: K ): Effect.Effect | ReasonOf>, R> => catchFilter( self, (e: any) => { if (isTagged(e, errorTag) && hasProperty(e, "reason")) { return Result.succeed(e.reason) } return Result.fail(e) }, fail as any ) as any ) /** @internal */ export const mapErrorCause: { ( f: (e: Cause.Cause) => Cause.Cause ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, f: (e: Cause.Cause) => Cause.Cause ): Effect.Effect } = dual( 2, ( self: Effect.Effect, f: (e: Cause.Cause) => Cause.Cause ): Effect.Effect => catchCause(self, (cause) => failCauseSync(() => f(cause))) ) /** @internal */ export const mapError: { ( f: (e: E) => E2 ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, f: (e: E) => E2 ): Effect.Effect } = dual( 2, ( self: Effect.Effect, f: (e: E) => E2 ): Effect.Effect => catch_(self, (error) => failSync(() => f(error))) ) /* @internal */ export const mapBoth: { ( options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } ): Effect.Effect } = dual(2, ( self: Effect.Effect, options: { readonly onFailure: (e: E) => E2; readonly onSuccess: (a: A) => A2 } ): Effect.Effect => matchEffect(self, { onFailure: (e) => failSync(() => options.onFailure(e)), onSuccess: (a) => sync(() => options.onSuccess(a)) })) /** @internal */ export const orDie = ( self: Effect.Effect ): Effect.Effect => catch_(self, die) /** @internal */ export const orElseSucceed: { ( f: LazyArg ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, f: LazyArg ): Effect.Effect } = dual( 2, ( self: Effect.Effect, f: LazyArg ): Effect.Effect => catch_(self, (_) => sync(f)) ) /** @internal */ export const firstSuccessOf = >( effects: Iterable ): Effect.Effect, Effect.Error, Effect.Services> => suspend(() => { const iterator = effects[Symbol.iterator]() let state = iterator.next() if (state.done) { return die(new Error("Received an empty collection of effects")) } function loop(current: IteratorYieldResult): Eff { const next = iterator.next() if (next.done) return current.value return catch_(current.value, (_) => loop(next)) as any } return loop(state) }) /** @internal */ export const eventually = (self: Effect.Effect): Effect.Effect => catch_(self, (_) => flatMap(yieldNow, () => eventually(self))) /** @internal */ export const ignore: < Arg extends Effect.Effect | { readonly log?: boolean | LogLevel.Severity | undefined readonly message?: string | undefined } | undefined = { readonly log?: boolean | LogLevel.Severity | undefined readonly message?: string | undefined } >( effectOrOptions: Arg, options?: { readonly log?: boolean | LogLevel.Severity | undefined readonly message?: string | undefined } | undefined ) => [Arg] extends [Effect.Effect] ? Effect.Effect : (self: Effect.Effect) => Effect.Effect = dual( (args) => isEffect(args[0]), ( self: Effect.Effect, options?: { readonly log?: boolean | LogLevel.Severity | undefined readonly message?: string | undefined } | undefined ): Effect.Effect => { if (!options?.log) { return matchEffect(self, { onFailure: (_) => void_, onSuccess: (_) => void_ }) } const logEffect = logWithLevel(options.log === true ? undefined : options.log) return matchCauseEffect(self, { onFailure(cause) { const failure = findFail(cause) return Result.isFailure(failure) ? failCause(failure.failure) : options.message === undefined ? logEffect(cause) : logEffect(options.message, cause) }, onSuccess: (_) => void_ }) } ) /** @internal */ export const ignoreCause: < Arg extends Effect.Effect | { readonly log?: boolean | LogLevel.Severity | undefined readonly message?: string | undefined } | undefined = { readonly log?: boolean | LogLevel.Severity | undefined readonly message?: string | undefined } >( effectOrOptions: Arg, options?: { readonly log?: boolean | LogLevel.Severity | undefined readonly message?: string | undefined } | undefined ) => [Arg] extends [Effect.Effect] ? Effect.Effect : (self: Effect.Effect) => Effect.Effect = dual( (args) => isEffect(args[0]), ( self: Effect.Effect, options?: { readonly log?: boolean | LogLevel.Severity | undefined readonly message?: string | undefined } | undefined ): Effect.Effect => { if (!options?.log) { return matchCauseEffect(self, { onFailure: (_) => void_, onSuccess: (_) => void_ }) } const logEffect = logWithLevel(options.log === true ? undefined : options.log) return matchCauseEffect(self, { onFailure: (cause) => options.message === undefined ? logEffect(cause) : logEffect(options.message, cause), onSuccess: (_) => void_ }) } ) /** @internal */ export const option = ( self: Effect.Effect ): Effect.Effect, never, R> => match(self, { onFailure: Option.none, onSuccess: Option.some }) /** @internal */ export const result = ( self: Effect.Effect ): Effect.Effect, never, R> => matchEager(self, { onFailure: Result.fail, onSuccess: Result.succeed }) // ---------------------------------------------------------------------------- // pattern matching // ---------------------------------------------------------------------------- /** @internal */ export const matchCauseEffect: { (options: { readonly onFailure: (cause: Cause.Cause) => Effect.Effect readonly onSuccess: (a: A) => Effect.Effect }): ( self: Effect.Effect ) => Effect.Effect ( self: Effect.Effect, options: { readonly onFailure: (cause: Cause.Cause) => Effect.Effect readonly onSuccess: (a: A) => Effect.Effect } ): Effect.Effect } = dual( 2, ( self: Effect.Effect, options: { readonly onFailure: (cause: Cause.Cause) => Effect.Effect readonly onSuccess: (a: A) => Effect.Effect } ): Effect.Effect => { const primitive = Object.create(OnSuccessAndFailureProto) primitive[args] = self primitive[contA] = options.onSuccess.length !== 1 ? (a: A) => options.onSuccess(a) : options.onSuccess primitive[contE] = options.onFailure.length !== 1 ? (cause: Cause.Cause) => options.onFailure(cause) : options.onFailure return primitive } ) const OnSuccessAndFailureProto = makePrimitiveProto({ op: "OnSuccessAndFailure", [evaluate](this: any, fiber: FiberImpl): Primitive { fiber._stack.push(this) return this[args] } }) /** @internal */ export const matchCause: { (options: { readonly onFailure: (cause: Cause.Cause) => A2 readonly onSuccess: (a: A) => A3 }): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, options: { readonly onFailure: (cause: Cause.Cause) => A2 readonly onSuccess: (a: A) => A3 } ): Effect.Effect } = dual( 2, ( self: Effect.Effect, options: { readonly onFailure: (cause: Cause.Cause) => A2 readonly onSuccess: (a: A) => A3 } ): Effect.Effect => matchCauseEffect(self, { onFailure: (cause) => sync(() => options.onFailure(cause)), onSuccess: (value) => sync(() => options.onSuccess(value)) }) ) /** @internal */ export const matchEffect: { (options: { readonly onFailure: (e: E) => Effect.Effect readonly onSuccess: (a: A) => Effect.Effect }): ( self: Effect.Effect ) => Effect.Effect ( self: Effect.Effect, options: { readonly onFailure: (e: E) => Effect.Effect readonly onSuccess: (a: A) => Effect.Effect } ): Effect.Effect } = dual( 2, ( self: Effect.Effect, options: { readonly onFailure: (e: E) => Effect.Effect readonly onSuccess: (a: A) => Effect.Effect } ): Effect.Effect => matchCauseEffect(self, { onFailure: (cause) => { const fail = cause.reasons.find(isFailReason) return fail ? internalCall(() => options.onFailure(fail.error)) : failCause(cause as Cause.Cause) }, onSuccess: options.onSuccess }) ) /** @internal */ export const match: { (options: { readonly onFailure: (error: E) => A2 readonly onSuccess: (value: A) => A3 }): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, options: { readonly onFailure: (error: E) => A2 readonly onSuccess: (value: A) => A3 } ): Effect.Effect } = dual( 2, ( self: Effect.Effect, options: { readonly onFailure: (error: E) => A2 readonly onSuccess: (value: A) => A3 } ): Effect.Effect => matchEffect(self, { onFailure: (error) => sync(() => options.onFailure(error)), onSuccess: (value) => sync(() => options.onSuccess(value)) }) ) /** @internal */ export const matchEager: { (options: { readonly onFailure: (error: E) => A2 readonly onSuccess: (value: A) => A3 }): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, options: { readonly onFailure: (error: E) => A2 readonly onSuccess: (value: A) => A3 } ): Effect.Effect } = dual( 2, ( self: Effect.Effect, options: { readonly onFailure: (error: E) => A2 readonly onSuccess: (value: A) => A3 } ): Effect.Effect => { if (effectIsExit(self)) { if (self._tag === "Success") return exitSucceed(options.onSuccess(self.value)) const error = findError(self.cause) if (Result.isFailure(error)) return self as Exit.Exit return exitSucceed(options.onFailure(error.success)) } return match(self, options) } ) /** @internal */ export const matchCauseEager: { (options: { readonly onFailure: (cause: Cause.Cause) => A2 readonly onSuccess: (value: A) => A3 }): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, options: { readonly onFailure: (cause: Cause.Cause) => A2 readonly onSuccess: (value: A) => A3 } ): Effect.Effect } = dual( 2, ( self: Effect.Effect, options: { readonly onFailure: (cause: Cause.Cause) => A2 readonly onSuccess: (value: A) => A3 } ): Effect.Effect => { if (effectIsExit(self)) { if (self._tag === "Success") return exitSucceed(options.onSuccess(self.value)) return exitSucceed(options.onFailure(self.cause)) } return matchCause(self, options) } ) /** @internal */ export const exit = (self: Effect.Effect): Effect.Effect, never, R> => effectIsExit(self) ? exitSucceed(self) : exitPrimitive(self) const exitPrimitive: (self: Effect.Effect) => Effect.Effect, never, R> = makePrimitive({ op: "Exit", [evaluate](fiber): Primitive { fiber._stack.push(this) return this[args] as any }, [contA](value, _, exit) { return succeed(exit ?? exitSucceed(value)) }, [contE](cause, _, exit) { return succeed(exit ?? exitFailCause(cause)) } }) // ---------------------------------------------------------------------------- // Condition checking // ---------------------------------------------------------------------------- /** @internal */ export const isFailure: (self: Effect.Effect) => Effect.Effect = matchEager({ onFailure: () => true, onSuccess: () => false }) /** @internal */ export const isSuccess: (self: Effect.Effect) => Effect.Effect = matchEager({ onFailure: () => false, onSuccess: () => true }) // ---------------------------------------------------------------------------- // delays & timeouts // ---------------------------------------------------------------------------- /** @internal */ export const delay: { ( duration: Duration.Input ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, duration: Duration.Input ): Effect.Effect } = dual( 2, ( self: Effect.Effect, duration: Duration.Input ): Effect.Effect => andThen(sleep(duration), self) ) /** @internal */ export const timeoutOrElse: { (options: { readonly duration: Duration.Input readonly orElse: LazyArg> }): ( self: Effect.Effect ) => Effect.Effect ( self: Effect.Effect, options: { readonly duration: Duration.Input readonly orElse: LazyArg> } ): Effect.Effect } = dual( 2, ( self: Effect.Effect, options: { readonly duration: Duration.Input readonly orElse: LazyArg> } ): Effect.Effect => raceFirst( self, flatMap(sleep(options.duration), options.orElse) ) ) /** @internal */ export const timeout: { ( duration: Duration.Input ): ( self: Effect.Effect ) => Effect.Effect ( self: Effect.Effect, duration: Duration.Input ): Effect.Effect } = dual( 2, ( self: Effect.Effect, duration: Duration.Input ): Effect.Effect => timeoutOrElse(self, { duration, orElse: () => fail(new TimeoutError()) }) ) /** @internal */ export const timeoutOption: { ( duration: Duration.Input ): ( self: Effect.Effect ) => Effect.Effect, E, R> ( self: Effect.Effect, duration: Duration.Input ): Effect.Effect, E, R> } = dual( 2, ( self: Effect.Effect, duration: Duration.Input ): Effect.Effect, E, R> => raceFirst( asSome(self), as(sleep(duration), Option.none()) ) ) /** @internal */ export const timed = ( self: Effect.Effect ): Effect.Effect<[duration: Duration.Duration, result: A], E, R> => clockWith((clock) => { const start = clock.currentTimeNanosUnsafe() return map(self, (a) => [Duration.nanos(clock.currentTimeNanosUnsafe() - start), a]) }) // ---------------------------------------------------------------------------- // resources & finalization // ---------------------------------------------------------------------------- /** @internal */ export const ScopeTypeId = "~effect/Scope" /** @internal */ export const ScopeCloseableTypeId = "~effect/Scope/Closeable" /** @internal */ export const scopeTag: Context.Service = Context.Service("effect/Scope") /** @internal */ export const scopeClose = (self: Scope.Scope, exit_: Exit.Exit) => suspend(() => scopeCloseUnsafe(self, exit_) ?? void_) /** @internal */ export const scopeCloseUnsafe = (self: Scope.Scope, exit_: Exit.Exit) => { if (self.state._tag === "Closed") return const closed: Scope.State.Closed = { _tag: "Closed", exit: exit_ } if (self.state._tag === "Empty") { self.state = closed return } const { finalizers } = self.state self.state = closed if (finalizers.size === 0) { return } else if (finalizers.size === 1) { return finalizers.values().next().value!(exit_) } return scopeCloseFinalizers(self, finalizers, exit_) } const scopeCloseFinalizers = fnUntraced(function*( self: Scope.Scope, finalizers: Scope.State.Open["finalizers"], exit_: Exit.Exit ) { let exits: Array> = [] const fibers: Array> = [] const arr = Array.from(finalizers.values()) const parent = getCurrentFiber()! for (let i = arr.length - 1; i >= 0; i--) { const finalizer = arr[i] if (self.strategy === "sequential") { exits.push(yield* exit(finalizer(exit_))) } else { fibers.push(forkUnsafe(parent, finalizer(exit_), true, true, "inherit")) } } if (fibers.length > 0) { exits = yield* fiberAwaitAll(fibers) } return yield* exitAsVoidAll(exits) }) /** @internal */ export const scopeFork = (scope: Scope.Scope, finalizerStrategy?: "sequential" | "parallel") => sync(() => scopeForkUnsafe(scope, finalizerStrategy)) /** @internal */ export const scopeForkUnsafe = (scope: Scope.Scope, finalizerStrategy?: "sequential" | "parallel") => { const newScope = scopeMakeUnsafe(finalizerStrategy) if (scope.state._tag === "Closed") { newScope.state = scope.state return newScope } const key = {} scopeAddFinalizerUnsafe(scope, key, (exit) => scopeClose(newScope, exit)) scopeAddFinalizerUnsafe(newScope, key, (_) => sync(() => scopeRemoveFinalizerUnsafe(scope, key))) return newScope } /** @internal */ export const scopeAddFinalizerExit = ( scope: Scope.Scope, finalizer: (exit: Exit.Exit) => Effect.Effect ): Effect.Effect => { return suspend(() => { if (scope.state._tag === "Closed") { return finalizer(scope.state.exit) } scopeAddFinalizerUnsafe(scope, {}, finalizer) return void_ }) } /** @internal */ export const scopeAddFinalizer = ( scope: Scope.Scope, finalizer: Effect.Effect ): Effect.Effect => scopeAddFinalizerExit(scope, constant(finalizer)) /** @internal */ export const scopeAddFinalizerUnsafe = ( scope: Scope.Scope, key: {}, finalizer: (exit: Exit.Exit) => Effect.Effect ): void => { if (scope.state._tag === "Empty") { scope.state = { _tag: "Open", finalizers: new Map([[key, finalizer]]) } } else if (scope.state._tag === "Open") { scope.state.finalizers.set(key, finalizer) } } /** @internal */ export const scopeRemoveFinalizerUnsafe = ( scope: Scope.Scope, key: {} ): void => { if (scope.state._tag === "Open") { scope.state.finalizers.delete(key) } } /** @internal */ export const scopeMakeUnsafe = (finalizerStrategy: "sequential" | "parallel" = "sequential"): Scope.Closeable => ({ [ScopeCloseableTypeId]: ScopeCloseableTypeId, [ScopeTypeId]: ScopeTypeId, strategy: finalizerStrategy, state: constScopeEmpty }) const constScopeEmpty = { _tag: "Empty" } as const /** @internal */ export const scopeMake = (finalizerStrategy?: "sequential" | "parallel"): Effect.Effect => sync(() => scopeMakeUnsafe(finalizerStrategy)) /** @internal */ export const scope: Effect.Effect = scopeTag /** @internal */ export const provideScope: { (value: Scope.Scope): (self: Effect.Effect) => Effect.Effect> (self: Effect.Effect, value: Scope.Scope): Effect.Effect> } = provideService(scopeTag) /** @internal */ export const scoped = (self: Effect.Effect): Effect.Effect> => withFiber((fiber) => { const prev = fiber.context const scope = scopeMakeUnsafe() fiber.setContext(Context.add(fiber.context, scopeTag, scope)) return onExitPrimitive(self, (exit) => { fiber.setContext(prev) return scopeCloseUnsafe(scope, exit) }) }) as any /** @internal */ export const scopeUse: { ( scope: Scope.Closeable ): (self: Effect.Effect) => Effect.Effect> (self: Effect.Effect, scope: Scope.Closeable): Effect.Effect> } = dual( 2, (self: Effect.Effect, scope: Scope.Closeable): Effect.Effect> => onExit(provideScope(self, scope), (exit) => suspend(() => scopeCloseUnsafe(scope, exit) ?? void_)) ) /** @internal */ export const scopedWith = ( f: (scope: Scope.Scope) => Effect.Effect ): Effect.Effect => suspend(() => { const scope = scopeMakeUnsafe() return onExit(f(scope), (exit) => suspend(() => scopeCloseUnsafe(scope, exit) ?? void_)) }) /** @internal */ export const acquireRelease = ( acquire: Effect.Effect, release: (a: A, exit: Exit.Exit) => Effect.Effect, options?: { readonly interruptible?: boolean } ): Effect.Effect => contextWith((context: Context.Context) => uninterruptibleMask((restore) => flatMap( scope, (scope) => tap( options?.interruptible ? restore(acquire) : acquire, (a) => scopeAddFinalizerExit(scope, (exit) => provideContext(release(a, exit), context)) ) ) ) ) /** @internal */ export const addFinalizer = ( finalizer: (exit: Exit.Exit) => Effect.Effect ): Effect.Effect => flatMap( scope, (scope) => contextWith((context: Context.Context) => scopeAddFinalizerExit(scope, (exit) => provideContext(finalizer(exit), context)) ) ) /** @internal */ export const onExitPrimitive: ( self: Effect.Effect, f: (exit: Exit.Exit) => Effect.Effect | undefined, interruptible?: boolean ) => Effect.Effect = makePrimitive({ op: "OnExit", single: false, [evaluate](fiber: FiberImpl) { fiber._stack.push(this) return this[args][0] }, [contAll](fiber) { if (fiber.interruptible && this[args][2] !== true) { fiber._stack.push(setInterruptibleTrue) fiber.interruptible = false } }, [contA](value, _, exit) { exit ??= exitSucceed(value) const eff = this[args][1](exit) return eff ? flatMap(eff, (_) => exit) : exit }, [contE](cause, _, exit) { exit ??= exitFailCause(cause) const eff = this[args][1](exit) return eff ? flatMap(eff, (_) => exit) : exit } }) /** @internal */ export const onExit: { ( f: (exit: Exit.Exit) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, f: (exit: Exit.Exit) => Effect.Effect ): Effect.Effect } = dual(2, onExitPrimitive) /** @internal */ export const ensuring: { ( finalizer: Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, finalizer: Effect.Effect ): Effect.Effect } = dual( 2, ( self: Effect.Effect, finalizer: Effect.Effect ): Effect.Effect => onExit(self, (_) => finalizer) ) /** @internal */ export const onExitIf: { ( predicate: Predicate.Predicate, NoInfer>>, f: (exit: Exit.Exit, NoInfer>) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, predicate: Predicate.Predicate, NoInfer>>, f: (exit: Exit.Exit, NoInfer>) => Effect.Effect ): Effect.Effect } = dual( 3, ( self: Effect.Effect, predicate: Predicate.Predicate, NoInfer>>, f: (exit: Exit.Exit, NoInfer>) => Effect.Effect ): Effect.Effect => onExit(self, (exit) => { if (!predicate(exit)) { return void_ } return f(exit) }) ) /** @internal */ export const onExitFilter: { ( filter: Filter.Filter, NoInfer>, B, X>, f: (b: B, exit: Exit.Exit, NoInfer>) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, filter: Filter.Filter, NoInfer>, B, X>, f: (b: B, exit: Exit.Exit, NoInfer>) => Effect.Effect ): Effect.Effect } = dual( 3, ( self: Effect.Effect, filter: Filter.Filter, NoInfer>, B, X>, f: (b: B, exit: Exit.Exit, NoInfer>) => Effect.Effect ): Effect.Effect => onExit(self, (exit) => { const b = filter(exit) return Result.isFailure(b) ? void_ : f(b.success, exit) }) ) /** @internal */ export const onError: { ( f: (cause: Cause.Cause>) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, f: (cause: Cause.Cause>) => Effect.Effect ): Effect.Effect } = dual( 2, ( self: Effect.Effect, f: (cause: Cause.Cause>) => Effect.Effect ): Effect.Effect => onExitFilter(self, exitFilterCause as any, f as any) as any ) /** @internal */ export const onErrorIf: { ( predicate: Predicate.Predicate>, f: (cause: Cause.Cause) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, predicate: Predicate.Predicate>, f: (cause: Cause.Cause) => Effect.Effect ): Effect.Effect } = dual( 3, ( self: Effect.Effect, predicate: Predicate.Predicate>, f: (cause: Cause.Cause) => Effect.Effect ): Effect.Effect => onExitIf( self, (exit): exit is Exit.Failure => { if (exit._tag !== "Failure") { return false } return predicate(exit.cause) }, (exit) => f((exit as Exit.Failure).cause) ) as any ) /** @internal */ export const onErrorFilter: { ( filter: Filter.Filter, EB, X>, f: (failure: EB, cause: Cause.Cause) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, filter: Filter.Filter, EB, X>, f: (failure: EB, cause: Cause.Cause) => Effect.Effect ): Effect.Effect } = dual( 3, ( self: Effect.Effect, filter: Filter.Filter, EB, X>, f: (failure: EB, cause: Cause.Cause) => Effect.Effect ): Effect.Effect => onExit(self, (exit) => { if (exit._tag !== "Failure") { return void_ } const result = filter(exit.cause) return Result.isFailure(result) ? void_ : f(result.success, exit.cause) }) ) /** @internal */ export const onInterrupt: { ( finalizer: (interruptors: ReadonlySet) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, finalizer: (interruptors: ReadonlySet) => Effect.Effect ): Effect.Effect } = dual( 2, ( self: Effect.Effect, finalizer: (interruptors: ReadonlySet) => Effect.Effect ): Effect.Effect => onErrorFilter(causeFilterInterruptors as any, finalizer)(self) as any ) /** @internal */ export const acquireUseRelease = ( acquire: Effect.Effect, use: (a: Resource) => Effect.Effect, release: (a: Resource, exit: Exit.Exit) => Effect.Effect ): Effect.Effect => uninterruptibleMask((restore) => flatMap(acquire, (a) => onExitPrimitive( restore(use(a)), (exit) => release(a, exit), true )) ) /** @internal */ export const acquireDisposable = ( acquire: Effect.Effect ): Effect.Effect => acquireRelease(acquire, (resource) => hasProperty(resource, Symbol.asyncDispose) ? promise(() => resource[Symbol.asyncDispose]()) : sync(() => resource[Symbol.dispose]())) // ---------------------------------------------------------------------------- // Caching // ---------------------------------------------------------------------------- /** @internal */ export const cachedInvalidateWithTTL: { (timeToLive: Duration.Input): ( self: Effect.Effect ) => Effect.Effect<[Effect.Effect, Effect.Effect]> ( self: Effect.Effect, timeToLive: Duration.Input ): Effect.Effect<[Effect.Effect, Effect.Effect]> } = dual(2, ( self: Effect.Effect, ttl: Duration.Input ): Effect.Effect<[Effect.Effect, Effect.Effect]> => sync(() => { const ttlMillis = Duration.toMillis(Duration.fromInputUnsafe(ttl)) const isFinite = Number.isFinite(ttlMillis) const latch = makeLatchUnsafe(false) let expiresAt = 0 let running = false let exit: Exit.Exit | undefined const wait = flatMap(latch.await, () => exit!) return [ withFiber((fiber) => { const clock = fiber.getRef(ClockRef) const now = isFinite ? clock.currentTimeMillisUnsafe() : 0 if (running || now < expiresAt) return exit ?? wait running = true latch.closeUnsafe() exit = undefined return onExit(self, (exit_) => sync(() => { running = false expiresAt = clock.currentTimeMillisUnsafe() + ttlMillis exit = exit_ latch.openUnsafe() })) }), sync(() => { expiresAt = 0 latch.closeUnsafe() exit = undefined }) ] })) /** @internal */ export const cachedWithTTL: { ( timeToLive: Duration.Input ): (self: Effect.Effect) => Effect.Effect> ( self: Effect.Effect, timeToLive: Duration.Input ): Effect.Effect> } = dual( 2, ( self: Effect.Effect, timeToLive: Duration.Input ): Effect.Effect> => map(cachedInvalidateWithTTL(self, timeToLive), (tuple) => tuple[0]) ) /** @internal */ export const cached = (self: Effect.Effect): Effect.Effect> => cachedWithTTL(self, Duration.infinity) // ---------------------------------------------------------------------------- // interruption // ---------------------------------------------------------------------------- /** @internal */ export const interrupt: Effect.Effect = withFiber((fiber) => failCause(causeInterrupt(fiber.id))) /** @internal */ export const uninterruptible = ( self: Effect.Effect ): Effect.Effect => withFiber((fiber) => { if (!fiber.interruptible) return self fiber.interruptible = false fiber._stack.push(setInterruptibleTrue) return self }) const setInterruptible: (interruptible: boolean) => Primitive = makePrimitive({ op: "SetInterruptible", [contAll](fiber) { fiber.interruptible = this[args] if (fiber._interruptedCause && fiber.interruptible) { return () => failCause(fiber._interruptedCause!) } } }) const setInterruptibleTrue = setInterruptible(true) const setInterruptibleFalse = setInterruptible(false) /** @internal */ export const interruptible = ( self: Effect.Effect ): Effect.Effect => withFiber((fiber) => { if (fiber.interruptible) return self fiber.interruptible = true fiber._stack.push(setInterruptibleFalse) if (fiber._interruptedCause) return failCause(fiber._interruptedCause) return self }) /** @internal */ export const uninterruptibleMask = ( f: ( restore: ( effect: Effect.Effect ) => Effect.Effect ) => Effect.Effect ): Effect.Effect => withFiber((fiber) => { if (!fiber.interruptible) return f(identity) fiber.interruptible = false fiber._stack.push(setInterruptibleTrue) return f(interruptible) }) /** @internal */ export const interruptibleMask = ( f: ( restore: ( effect: Effect.Effect ) => Effect.Effect ) => Effect.Effect ): Effect.Effect => withFiber((fiber) => { if (fiber.interruptible) return f(identity) fiber.interruptible = true fiber._stack.push(setInterruptibleFalse) return f(uninterruptible) }) /** @internal */ export const abortSignal: Effect.Effect = map( acquireRelease( sync(() => new AbortController()), (controller) => sync(() => controller.abort()) ), (_) => _.signal ) // ======================================================================== // collecting & elements // ======================================================================== /** @internal */ export const all = < const Arg extends | Iterable> | Record>, O extends { readonly concurrency?: Concurrency | undefined readonly discard?: boolean | undefined readonly mode?: "default" | "result" | undefined } >( arg: Arg, options?: O ): Effect.All.Return => { if (isIterable(arg)) { return options?.mode === "result" ? (forEach as any)(arg, result, options) : (forEach as any)(arg, identity, options) } else if (options?.discard) { return options.mode === "result" ? (forEach as any)(Object.values(arg), result, options) : (forEach as any)(Object.values(arg), identity, options) } return suspend(() => { const out: Record = {} return as( forEach( Object.entries(arg), ([key, effect]) => map(options?.mode === "result" ? result(effect) : effect, (value) => { out[key] = value }), { discard: true, concurrency: options?.concurrency } ), out ) }) as any } /** @internal */ export const partition: { ( f: (a: A, i: number) => Effect.Effect, options?: { readonly concurrency?: Concurrency | undefined } ): (elements: Iterable) => Effect.Effect<[excluded: Array, satisfying: Array], never, R> ( elements: Iterable, f: (a: A, i: number) => Effect.Effect, options?: { readonly concurrency?: Concurrency | undefined } ): Effect.Effect<[excluded: Array, satisfying: Array], never, R> } = dual( (args) => isIterable(args[0]) && !isEffect(args[0]), ( elements: Iterable, f: (a: A, i: number) => Effect.Effect, options?: { readonly concurrency?: Concurrency | undefined } ): Effect.Effect<[excluded: Array, satisfying: Array], never, R> => map( forEach(elements, (a, i) => result(f(a, i)), options), (results) => Arr.partition(results, identity) ) ) /** @internal */ export const validate: { ( f: (a: A, i: number) => Effect.Effect, options?: { readonly concurrency?: Concurrency | undefined readonly discard?: false | undefined } | undefined ): (elements: Iterable) => Effect.Effect, Arr.NonEmptyArray, R> ( f: (a: A, i: number) => Effect.Effect, options: { readonly concurrency?: Concurrency | undefined readonly discard: true } ): (elements: Iterable) => Effect.Effect, R> ( elements: Iterable, f: (a: A, i: number) => Effect.Effect, options?: { readonly concurrency?: Concurrency | undefined readonly discard?: false | undefined } | undefined ): Effect.Effect, Arr.NonEmptyArray, R> ( elements: Iterable, f: (a: A, i: number) => Effect.Effect, options: { readonly concurrency?: Concurrency | undefined readonly discard: true } ): Effect.Effect, R> } = dual( (args) => isIterable(args[0]) && !isEffect(args[0]), ( elements: Iterable, f: (a: A, i: number) => Effect.Effect, options?: { readonly concurrency?: Concurrency | undefined readonly discard?: boolean | undefined } | undefined ): Effect.Effect | void, Arr.NonEmptyArray, R> => flatMap( partition(elements, f, { concurrency: options?.concurrency }), ([excluded, satisfying]) => { if (Arr.isArrayNonEmpty(excluded)) { return fail(excluded) } return options?.discard ? void_ : succeed(satisfying) } ) ) /** @internal */ export const findFirst: { ( predicate: (a: NoInfer, i: number) => Effect.Effect ): (elements: Iterable) => Effect.Effect, E, R> ( elements: Iterable, predicate: (a: NoInfer, i: number) => Effect.Effect ): Effect.Effect, E, R> } = dual( (args) => isIterable(args[0]) && !isEffect(args[0]), ( elements: Iterable, predicate: (a: A, i: number) => Effect.Effect ): Effect.Effect, E, R> => suspend(() => { const iterator = elements[Symbol.iterator]() const next = iterator.next() if (!next.done) { return findFirstLoop(iterator, 0, predicate, next.value) } return succeed(Option.none()) }) ) const findFirstLoop = ( iterator: Iterator, index: number, predicate: (a: A, i: number) => Effect.Effect, value: A ): Effect.Effect, E, R> => flatMap(predicate(value, index), (keep) => { if (keep) { return succeed(Option.some(value)) } const next = iterator.next() if (!next.done) { return findFirstLoop(iterator, index + 1, predicate, next.value) } return succeed(Option.none()) }) /** @internal */ export const findFirstFilter: { ( filter: (input: NoInfer, i: number) => Effect.Effect, E, R> ): (elements: Iterable) => Effect.Effect, E, R> ( elements: Iterable, filter: (input: NoInfer, i: number) => Effect.Effect, E, R> ): Effect.Effect, E, R> } = dual( (args) => isIterable(args[0]) && !isEffect(args[0]), ( elements: Iterable, filter: (input: A, i: number) => Effect.Effect, E, R> ): Effect.Effect, E, R> => suspend(() => { const iterator = elements[Symbol.iterator]() const next = iterator.next() if (!next.done) { return findFirstFilterLoop(iterator, 0, filter, next.value) } return succeed(Option.none()) }) ) const findFirstFilterLoop = ( iterator: Iterator, index: number, filter: (input: A, i: number) => Effect.Effect, E, R>, value: A ): Effect.Effect, E, R> => flatMap(filter(value, index), (result) => { if (Result.isSuccess(result)) { return succeed(Option.some(result.success)) } const next = iterator.next() if (!next.done) { return findFirstFilterLoop(iterator, index + 1, filter, next.value) } return succeed(Option.none()) }) /** @internal */ export const whileLoop: (options: { readonly while: LazyArg readonly body: LazyArg> readonly step: (a: A) => void }) => Effect.Effect = makePrimitive({ op: "While", [contA](value, fiber) { this[args].step(value) if (this[args].while()) { fiber._stack.push(this) return this[args].body() } return exitVoid }, [evaluate](fiber) { if (this[args].while()) { fiber._stack.push(this) return this[args].body() } return exitVoid } }) /** @internal */ export const forEach: { , const Discard extends boolean = false>( f: (a: Arr.ReadonlyArray.Infer, i: number) => Effect.Effect, options?: { readonly concurrency?: Concurrency | undefined readonly discard?: Discard | undefined } | undefined ): ( self: S ) => Effect.Effect : void, E, R> , const Discard extends boolean = false>( self: S, f: (a: Arr.ReadonlyArray.Infer, i: number) => Effect.Effect, options?: { readonly concurrency?: Concurrency | undefined readonly discard?: Discard | undefined } | undefined ): Effect.Effect : void, E, R> } = dual((args) => typeof args[1] === "function", ( iterable: Iterable, f: (a: A, index: number) => Effect.Effect, options?: { readonly concurrency?: Concurrency | undefined readonly discard?: boolean | undefined } ): Effect.Effect => withFiber((parent) => { const concurrencyOption = options?.concurrency === "inherit" ? parent.getRef(CurrentConcurrency) : (options?.concurrency ?? 1) const concurrency = concurrencyOption === "unbounded" ? Number.POSITIVE_INFINITY : Math.max(1, concurrencyOption) if (concurrency === 1) { return forEachSequential(iterable, f, options) } const items = Arr.fromIterable(iterable) let length = items.length if (length === 0) { return options?.discard ? void_ : succeed([]) } const out: Array | undefined = options?.discard ? undefined : new Array(length) const eff = forEachConcurrent({ f, out }, items, { concurrency }) return eff ? as(eff, out as any) : succeed(out as any) })) const forEachSequential = ( iterable: Iterable, f: (a: A, index: number) => Effect.Effect, options?: { readonly discard?: boolean | undefined } ) => suspend(() => { const out: Array | undefined = options?.discard ? undefined : [] const iterator = iterable[Symbol.iterator]() let state = iterator.next() let index = 0 return as( whileLoop({ while: () => !state.done, body: () => f(state.value!, index++), step: (b) => { if (out) out.push(b) state = iterator.next() } }), out ) }) const iterateEagerImpl = (options: { readonly onItem: (state: S, item: A, index: number) => Effect.Effect readonly step: (state: NoInfer, item: A, exit: Exit.Exit, index: number) => Exit.Exit | void }): ( initialState: S, items: ReadonlyArray, options?: { readonly concurrency?: number | undefined readonly start?: number | undefined readonly end?: number | undefined } ) => Effect.Effect | undefined => { const onItem = options.onItem const step = options.step return ( state: S, items: ReadonlyArray, opts: { readonly concurrency?: number | undefined readonly start?: number | undefined readonly end?: number | undefined } | undefined ): Effect.Effect | undefined => { let index = opts?.start ?? 0 const end = opts?.end ?? items.length const concurrency = opts?.concurrency ?? 1 let done = false let parentFiber: Fiber.Fiber | undefined let fibers: Set> | undefined let resume: ((effect: Effect.Effect) => void) | undefined let interrupted = false let terminal: Exit.Exit | void let effect: Effect.Effect | undefined const go = (): Effect.Effect | undefined => { let paused = false for (; !terminal && index < end; index++) { const item = items[index] const eff = effect ?? onItem(state, item, index) // fast case (already an exit) if (effectIsExit(eff)) { terminal = step(state, item, eff, index) if (terminal) break // Use flatMap for concurrency of 1 } else if (concurrency === 1) { return flatMap(exit(eff), (exit) => { terminal = step(state, item, exit, index) index++ return terminal ?? go() ?? void_ }) // We have an effect, so enter "async" mode } else if (!parentFiber) { return callback((cb) => { parentFiber = getCurrentFiber()! effect = eff resume = cb const result = go() if (result) return cb(result) return suspend(() => { terminal = exitVoid interrupted = true return fibers ? fiberInterruptAll(fibers) : void_ }) }) // Fork the effect with concurrency > 1 } else { // Clear the temporary effect from capturing the parentFiber effect = undefined const fiber = forkUnsafe(parentFiber, eff, true, true, "inherit") if (fiber._exit) { terminal = step(state, item, fiber._exit, index) if (terminal) break continue } // Add the fiber to the Set if (fibers) fibers.add(fiber) else fibers = new Set([fiber]) const currentIndex = index fiber.addObserver((exit) => { fibers!.delete(fiber) if (terminal) { if (!interrupted && exit._tag === "Failure") { for (const reason of exit.cause.reasons) { if (reason._tag === "Interrupt") continue else if (terminal._tag === "Failure") { ;(terminal.cause.reasons as Array).push(reason) } else { terminal = exitFailCause(causeFromReasons([reason])) } } } } else { const result = step(state, item, exit, currentIndex) if (result) { terminal = result._tag === "Failure" ? exitFailCause(causeFromReasons(result.cause.reasons.slice())) : result go() } } if (paused) { const eff = go() if (eff) resume!(eff) } else if (done && fibers!.size === 0) { resume!(terminal ?? void_) } }) // Check if we have reached the concurrency limit if (fibers.size < concurrency) continue paused = true index++ return } } done = true if (terminal) { if (fibers && fibers.size > 0) { const annotations = fiberStackAnnotations(parentFiber!) fibers.forEach((f) => f.interruptUnsafe(parentFiber!.id, annotations)) return } if (resume || terminal._tag === "Failure") { return terminal } } else if (resume) { if (!fibers) { return exitVoid } else if (fibers.size === 0) { resume(void_) } } } return go() } } /** @internal */ export const iterateEager = (): (options: { readonly onItem: (state: S, item: A, index: number) => Effect.Effect readonly step: (state: NoInfer, item: A, exit: Exit.Exit, index: number) => Exit.Exit | void }) => ( initialState: S, items: ReadonlyArray, options?: { readonly concurrency?: number | undefined readonly start?: number | undefined readonly end?: number | undefined } ) => Effect.Effect | undefined => iterateEagerImpl const forEachConcurrent = iterateEagerImpl({ onItem( state: { readonly f: (a: any, i: number) => Effect.Effect readonly out: Array | undefined }, item, index ) { return state.f(item, index) }, step(state, _, exit, index) { if (exit._tag === "Failure") return exit else if (state.out) { state.out[index] = exit.value } } }) /* @internal */ export const filterOrElse: { ( refinement: Predicate.Refinement, B>, orElse: (a: EqualsWith, Exclude, B>>) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( predicate: Predicate.Predicate>, orElse: (a: NoInfer) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, refinement: Predicate.Refinement, orElse: (a: EqualsWith>) => Effect.Effect ): Effect.Effect ( self: Effect.Effect, predicate: Predicate.Predicate>, orElse: (a: NoInfer) => Effect.Effect ): Effect.Effect } = dual(3, ( self: Effect.Effect, predicate: Predicate.Predicate, orElse: (a: A) => Effect.Effect ): Effect.Effect => flatMap( self, (a) => predicate(a) ? succeed(a) : orElse(a) )) /** @internal */ export const filterMapOrElse: { ( filter: Filter.Filter, B, X>, orElse: (x: X) => Effect.Effect ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, filter: Filter.Filter, B, X>, orElse: (x: X) => Effect.Effect ): Effect.Effect } = dual(3, ( self: Effect.Effect, filter: Filter.Filter, B, X>, orElse: (x: X) => Effect.Effect ): Effect.Effect => flatMap( self, (a) => { const result = filter(a) return (Result.isFailure(result) ? orElse(result.failure) : succeed(result.success)) as Effect.Effect } )) /* @internal */ export const filterMapOrFail: { ( filter: Filter.Filter, B, X>, orFailWith: (x: X) => E2 ): (self: Effect.Effect) => Effect.Effect ( filter: Filter.Filter, B, X> ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, filter: Filter.Filter, B, X>, orFailWith: (x: X) => E2 ): Effect.Effect ( self: Effect.Effect, filter: Filter.Filter, B, X> ): Effect.Effect } = dual((args) => isEffect(args[0]), ( self: Effect.Effect, filter: Filter.Filter, B, X>, orFailWith?: (x: X) => E2 ): Effect.Effect => filterMapOrElse( self, filter, orFailWith ? (x: X) => fail(orFailWith(x)) : () => fail(new NoSuchElementError() as E2) )) /** @internal */ export const filter: { ( refinement: Predicate.Refinement, B> ): (elements: Iterable) => Effect.Effect> ( predicate: Predicate.Predicate> ): (elements: Iterable) => Effect.Effect> ( predicate: (a: NoInfer, i: number) => Effect.Effect, options?: { readonly concurrency?: Concurrency | undefined } ): (iterable: Iterable) => Effect.Effect, E, R> ( elements: Iterable, refinement: Predicate.Refinement ): Effect.Effect> ( elements: Iterable, predicate: Predicate.Predicate ): Effect.Effect> ( iterable: Iterable, predicate: (a: NoInfer, i: number) => Effect.Effect, options?: { readonly concurrency?: Concurrency | undefined } ): Effect.Effect, E, R> } = dual( (args) => isIterable(args[0]) && !isEffect(args[0]), ( elements: Iterable, predicate: | Predicate.Predicate | ((a: A, i: number) => Effect.Effect), options?: { readonly concurrency?: Concurrency | undefined } ): Effect.Effect, E, R> => suspend(() => { const out: Array = [] return as( forEach( elements, (a, i) => { const result = (predicate as Function)(a, i) if (typeof result === "boolean") { if (result) out.push(a) return void_ as any } return map(result, (keep) => { if (keep) { out.push(a) } }) }, { discard: true, concurrency: options?.concurrency } ), out ) }) ) /** @internal */ export const filterMap: { ( filter: Filter.Filter, B, X> ): (elements: Iterable) => Effect.Effect> ( elements: Iterable, filter: Filter.Filter, B, X> ): Effect.Effect> } = dual( (args) => isIterable(args[0]) && !isEffect(args[0]), ( elements: Iterable, filter: Filter.Filter ): Effect.Effect> => suspend(() => { const out: Array = [] for (const a of elements) { const result = filter(a) if (Result.isSuccess(result)) { out.push(result.success) } } return succeed(out) }) ) /** @internal */ export const filterMapEffect: { ( filter: Filter.FilterEffect, B, X, E, R>, options?: { readonly concurrency?: Concurrency | undefined } ): (elements: Iterable) => Effect.Effect, E, R> ( elements: Iterable, filter: Filter.FilterEffect, B, X, E, R>, options?: { readonly concurrency?: Concurrency | undefined } ): Effect.Effect, E, R> } = dual( (args) => isIterable(args[0]) && !isEffect(args[0]), ( elements: Iterable, filter: Filter.FilterEffect, options?: { readonly concurrency?: Concurrency | undefined } ): Effect.Effect, E, R> => suspend(() => { const out: Array = [] return as( forEach( elements, (a) => map(filter(a), (result) => { if (Result.isSuccess(result)) { out.push(result.success) } }), { discard: true, concurrency: options?.concurrency } ), out ) }) ) // ---------------------------------------------------------------------------- // do notation // ---------------------------------------------------------------------------- /** @internal */ export const Do: Effect.Effect<{}> = succeed({}) /** @internal */ export const bindTo: { ( name: N ): ( self: Effect.Effect ) => Effect.Effect, E, R> ( self: Effect.Effect, name: N ): Effect.Effect, E, R> } = doNotation.bindTo(map) /** @internal */ export const bind: { , B, E2, R2>( name: N, f: (a: NoInfer) => Effect.Effect ): ( self: Effect.Effect ) => Effect.Effect & Record>, E | E2, R | R2> , E, R, B, E2, R2, N extends string>( self: Effect.Effect, name: N, f: (a: NoInfer) => Effect.Effect ): Effect.Effect & Record>, E | E2, R | R2> } = doNotation.bind(map, flatMap) /** @internal */ const let_: { , B>( name: N, f: (a: NoInfer) => B ): ( self: Effect.Effect ) => Effect.Effect & Record>, E, R> , E, R, B, N extends string>( self: Effect.Effect, name: N, f: (a: NoInfer) => B ): Effect.Effect & Record>, E, R> } = doNotation.let_(map) /** @internal */ export { let_ as let } // ---------------------------------------------------------------------------- // fibers & forking // ---------------------------------------------------------------------------- /** @internal */ export const forkChild: { < Arg extends Effect.Effect | { readonly startImmediately?: boolean | undefined readonly uninterruptible?: boolean | "inherit" | undefined } | undefined = { readonly startImmediately?: boolean | undefined readonly uninterruptible?: boolean | "inherit" | undefined } >( effectOrOptions: Arg, options?: { readonly startImmediately?: boolean | undefined readonly uninterruptible?: boolean | "inherit" | undefined } | undefined ): [Arg] extends [Effect.Effect] ? Effect.Effect, never, _R> : (self: Effect.Effect) => Effect.Effect, never, R> } = dual((args) => isEffect(args[0]), ( self: Effect.Effect, options?: { readonly startImmediately?: boolean readonly uninterruptible?: boolean | "inherit" } ): Effect.Effect, never, R> => withFiber((fiber) => { interruptChildrenPatch() return succeed(forkUnsafe( fiber, self, options?.startImmediately, false, options?.uninterruptible ?? false )) })) /** @internal */ export const forkUnsafe = ( parent: Fiber.Fiber, effect: Effect.Effect, immediate = false, daemon = false, uninterruptible: boolean | "inherit" = false ): FiberImpl => { const interruptible = uninterruptible === "inherit" ? parent.interruptible : !uninterruptible const child = new FiberImpl(parent.context, interruptible) if (immediate) { child.evaluate(effect as any) } else { parent.currentDispatcher.scheduleTask(() => child.evaluate(effect as any), 0) } if (!daemon && !child._exit) { parent.children().add(child) child.addObserver(() => parent._children!.delete(child)) } return child } /** @internal */ export const forkDetach: { < Arg extends Effect.Effect | { readonly startImmediately?: boolean | undefined readonly uninterruptible?: boolean | "inherit" | undefined } | undefined = { readonly startImmediately?: boolean | undefined readonly uninterruptible?: boolean | "inherit" | undefined } >( effectOrOptions: Arg, options?: { readonly startImmediately?: boolean | undefined readonly uninterruptible?: boolean | "inherit" | undefined } | undefined ): [Arg] extends [Effect.Effect] ? Effect.Effect, never, _R> : (self: Effect.Effect) => Effect.Effect, never, R> } = dual((args) => isEffect(args[0]), ( self: Effect.Effect, options?: { readonly startImmediately?: boolean readonly uninterruptible?: boolean | "inherit" | undefined } ): Effect.Effect, never, R> => withFiber((fiber) => succeed(forkUnsafe(fiber, self, options?.startImmediately, true, options?.uninterruptible)))) /** @internal */ export const awaitAllChildren = ( self: Effect.Effect ): Effect.Effect => withFiber((fiber) => { const initialChildren = fiber._children && Arr.fromIterable(fiber._children) return onExit( self, (_) => { let children = fiber._children if (children === undefined || children.size === 0) { return void_ } else if (initialChildren) { children = Iterable.filter( children, (child: FiberImpl) => !initialChildren.includes(child) ) as Set> } return asVoid(fiberAwaitAll(children)) } ) }) /** @internal */ export const forkIn: { ( scope: Scope.Scope, options?: { readonly startImmediately?: boolean | undefined readonly uninterruptible?: boolean | "inherit" | undefined } ): ( self: Effect.Effect ) => Effect.Effect, never, R> ( self: Effect.Effect, scope: Scope.Scope, options?: { readonly startImmediately?: boolean | undefined readonly uninterruptible?: boolean | "inherit" | undefined } ): Effect.Effect, never, R> } = dual( (args) => isEffect(args[0]), ( self: Effect.Effect, scope: Scope.Scope, options?: { readonly startImmediately?: boolean | undefined readonly uninterruptible?: boolean | "inherit" | undefined } ): Effect.Effect, never, R> => withFiber((parent) => { const fiber = forkUnsafe(parent, self, options?.startImmediately, true, options?.uninterruptible) if (!(fiber as FiberImpl)._exit) { if (scope.state._tag !== "Closed") { const key = {} const finalizer = () => withFiberId((interruptor) => interruptor === fiber.id ? void_ : fiberInterrupt(fiber)) scopeAddFinalizerUnsafe(scope, key, finalizer) fiber.addObserver(() => scopeRemoveFinalizerUnsafe(scope, key)) } else { fiber.interruptUnsafe(parent.id, fiberStackAnnotations(parent)) } } return succeed(fiber) }) ) /** @internal */ export const forkScoped: { < Arg extends Effect.Effect | { readonly startImmediately?: boolean | undefined readonly uninterruptible?: boolean | "inherit" | undefined } | undefined = { readonly startImmediately?: boolean | undefined readonly uninterruptible?: boolean | "inherit" | undefined } >( effectOrOptions: Arg, options?: { readonly startImmediately?: boolean | undefined readonly uninterruptible?: boolean | "inherit" | undefined } | undefined ): [Arg] extends [Effect.Effect] ? Effect.Effect, never, _R | Scope.Scope> : (self: Effect.Effect) => Effect.Effect, never, R | Scope.Scope> } = dual((args) => isEffect(args[0]), ( self: Effect.Effect, options?: { readonly startImmediately?: boolean readonly uninterruptible?: boolean | "inherit" } ): Effect.Effect, never, R | Scope.Scope> => flatMap(scope, (scope) => forkIn(self, scope, options))) // ---------------------------------------------------------------------------- // execution // ---------------------------------------------------------------------------- /** @internal */ export const runForkWith = (context: Context.Context) => ( effect: Effect.Effect, options?: Effect.RunOptions | undefined ): Fiber.Fiber => { const fiber = new FiberImpl( options?.scheduler ? Context.add(context, Scheduler.Scheduler, options.scheduler) : context, options?.uninterruptible !== true ) fiber.evaluate(effect as any) if (fiber._exit) return fiber if (options?.signal) { if (options.signal.aborted) { fiber.interruptUnsafe() } else { const abort = () => fiber.interruptUnsafe() options.signal.addEventListener("abort", abort, { once: true }) fiber.addObserver(() => options.signal!.removeEventListener("abort", abort)) } } if (options?.onFiberStart) { options.onFiberStart(fiber) } return fiber } /** @internal */ export const fiberRunIn: { (scope: Scope.Scope): (self: Fiber.Fiber) => Fiber.Fiber ( self: Fiber.Fiber, scope: Scope.Scope ): Fiber.Fiber } = dual(2, ( self: FiberImpl, scope: Scope.Scope ): Fiber.Fiber => { if (self._exit) { return self } else if (scope.state._tag === "Closed") { self.interruptUnsafe(self.id) return self } const key = {} scopeAddFinalizerUnsafe(scope, key, () => fiberInterrupt(self)) self.addObserver(() => scopeRemoveFinalizerUnsafe(scope, key)) return self }) /** @internal */ export const runFork: ( effect: Effect.Effect, options?: Effect.RunOptions | undefined ) => Fiber.Fiber = runForkWith(Context.empty()) /** @internal */ export const runCallbackWith = (context: Context.Context) => { const runFork = runForkWith(context) return ( effect: Effect.Effect, options?: | Effect.RunOptions & { readonly onExit: (exit: Exit.Exit) => void } | undefined ): (interruptor?: number | undefined) => void => { const fiber = runFork(effect, options) if (options?.onExit) { fiber.addObserver(options.onExit) } return (interruptor) => { return fiber.interruptUnsafe(interruptor) } } } /** @internal */ export const runCallback = runCallbackWith(Context.empty()) /** @internal */ export const runPromiseExitWith = (context: Context.Context) => { const runFork = runForkWith(context) return ( effect: Effect.Effect, options?: Effect.RunOptions | undefined ): Promise> => { const fiber = runFork(effect, options) return new Promise((resolve) => { fiber.addObserver((exit) => resolve(exit)) }) } } /** @internal */ export const runPromiseExit = runPromiseExitWith(Context.empty()) /** @internal */ export const runPromiseWith = (context: Context.Context) => { const runPromiseExit = runPromiseExitWith(context) return ( effect: Effect.Effect, options?: | Effect.RunOptions | undefined ): Promise => runPromiseExit(effect, options).then((exit) => { if (exit._tag === "Failure") { throw causeSquash(exit.cause) } return exit.value }) } /** @internal */ export const runPromise: ( effect: Effect.Effect, options?: | Effect.RunOptions | undefined ) => Promise = runPromiseWith(Context.empty()) /** @internal */ export const runSyncExitWith = (context: Context.Context) => { const runFork = runForkWith(context) return (effect: Effect.Effect): Exit.Exit => { if (effectIsExit(effect)) return effect const scheduler = new Scheduler.MixedScheduler("sync") const fiber = runFork(effect, { scheduler }) fiber.currentDispatcher?.flush() return (fiber as FiberImpl)._exit ?? exitDie(new AsyncFiberError(fiber)) } } /** @internal */ export const runSyncExit: (effect: Effect.Effect) => Exit.Exit = runSyncExitWith( Context.empty() ) /** @internal */ export const runSyncWith = (context: Context.Context) => { const runSyncExit = runSyncExitWith(context) return (effect: Effect.Effect): A => { const exit = runSyncExit(effect) if (exit._tag === "Failure") throw causeSquash(exit.cause) return exit.value } } /** @internal */ export const runSync: (effect: Effect.Effect) => A = runSyncWith(Context.empty()) const succeedTrue = succeed(true) const succeedFalse = succeed(false) class Latch implements _Latch.Latch { waiters: Array<(_: Effect.Effect) => void> = [] scheduled = false private isOpen: boolean constructor(isOpen: boolean) { this.isOpen = isOpen } private scheduleUnsafe(fiber: Fiber.Fiber) { if (this.scheduled || this.waiters.length === 0) { return succeedTrue } this.scheduled = true fiber.currentDispatcher.scheduleTask(this.flushWaiters, 0) return succeedTrue } private flushWaiters = () => { this.scheduled = false const waiters = this.waiters this.waiters = [] for (let i = 0; i < waiters.length; i++) { waiters[i](exitVoid) } } open = withFiber((fiber) => { if (this.isOpen) return succeedFalse this.isOpen = true return this.scheduleUnsafe(fiber) }) release = withFiber((fiber) => this.isOpen ? succeedFalse : this.scheduleUnsafe(fiber)) openUnsafe() { if (this.isOpen) return false this.isOpen = true this.flushWaiters() return true } await = callback((resume) => { if (this.isOpen) { return resume(void_) } this.waiters.push(resume) return sync(() => { const index = this.waiters.indexOf(resume) if (index !== -1) { this.waiters.splice(index, 1) } }) }) closeUnsafe() { if (!this.isOpen) return false this.isOpen = false return true } close = sync(() => this.closeUnsafe()) whenOpen = (self: Effect.Effect): Effect.Effect => flatMap(this.await, () => self) } /** @internal */ export const makeLatchUnsafe = (open?: boolean | undefined): _Latch.Latch => new Latch(open ?? false) /** @internal */ export const makeLatch = (open?: boolean | undefined) => sync(() => makeLatchUnsafe(open)) // ---------------------------------------------------------------------------- // Tracer // ---------------------------------------------------------------------------- /** @internal */ export const tracer: Effect.Effect = withFiber((fiber) => succeed(fiber.getRef(Tracer.Tracer))) /** @internal */ export const withTracer: { (tracer: Tracer.Tracer): (effect: Effect.Effect) => Effect.Effect (effect: Effect.Effect, tracer: Tracer.Tracer): Effect.Effect } = dual( 2, (effect: Effect.Effect, tracer: Tracer.Tracer): Effect.Effect => provideService(effect, Tracer.Tracer, tracer) ) /** @internal */ export const withTracerEnabled: { (enabled: boolean): (effect: Effect.Effect) => Effect.Effect (effect: Effect.Effect, enabled: boolean): Effect.Effect } = provideService(TracerEnabled) /** @internal */ export const withTracerTiming: { (enabled: boolean): (effect: Effect.Effect) => Effect.Effect (effect: Effect.Effect, enabled: boolean): Effect.Effect } = provideService(TracerTimingEnabled) const bigint0 = BigInt(0) const NoopSpanProto: Omit = { _tag: "Span", spanId: "noop", traceId: "noop", sampled: false, status: { _tag: "Ended", startTime: bigint0, endTime: bigint0, exit: exitVoid }, attributes: new Map(), links: [], kind: "internal", attribute() {}, event() {}, end() {}, addLinks() {} } /** @internal */ export const noopSpan = (options: { readonly name: string readonly parent: Option.Option readonly annotations: Context.Context }): Tracer.Span => Object.assign(Object.create(NoopSpanProto), options) const filterDisablePropagation = (span: Tracer.AnySpan | undefined): Option.Option => { if (!span) return Option.none() return Context.get(span.annotations, Tracer.DisablePropagation) ? span._tag === "Span" ? filterDisablePropagation(Option.getOrUndefined(span.parent)) : Option.none() : Option.some(span) } /** @internal */ export const makeSpanUnsafe = ( fiber: Fiber.Fiber, name: string, options: Tracer.SpanOptionsNoTrace | undefined ) => { const disablePropagation = !fiber.getRef(TracerEnabled) || (options?.annotations && Context.get(options.annotations, Tracer.DisablePropagation)) const parent = options?.parent !== undefined ? Option.some(options.parent) : options?.root ? Option.none() : filterDisablePropagation(fiber.currentSpan) let span: Tracer.Span if (disablePropagation) { span = noopSpan({ name, parent, annotations: Context.add( options?.annotations ?? Context.empty(), Tracer.DisablePropagation, true ) }) } else { const tracer = fiber.getRef(Tracer.Tracer) const clock = fiber.getRef(ClockRef) const timingEnabled = fiber.getRef(TracerTimingEnabled) const annotationsFromEnv = fiber.getRef(TracerSpanAnnotations) const linksFromEnv = fiber.getRef(TracerSpanLinks) const level = options?.level ?? fiber.getRef(Tracer.CurrentTraceLevel) const links = options?.links !== undefined ? [...linksFromEnv, ...options.links] : linksFromEnv.slice() span = tracer.span({ name, parent, annotations: options?.annotations ?? Context.empty(), links, startTime: timingEnabled ? clock.currentTimeNanosUnsafe() : BigInt(0), kind: options?.kind ?? "internal", root: options?.root ?? Option.isNone(parent), sampled: options?.sampled ?? (Option.isSome(parent) && parent.value.sampled === false ? false : !isLogLevelGreaterThan(fiber.getRef(Tracer.MinimumTraceLevel), level)) }) for (const [key, value] of Object.entries(annotationsFromEnv)) { span.attribute(key, value) } if (options?.attributes !== undefined) { for (const [key, value] of Object.entries(options.attributes)) { span.attribute(key, value) } } } return span } /** @internal */ export const makeSpan = ( name: string, options?: Tracer.SpanOptions ): Effect.Effect => withFiber((fiber) => succeed(makeSpanUnsafe(fiber, name, options))) /** @internal */ export const makeSpanScoped = ( name: string, options?: Tracer.SpanOptionsNoTrace | undefined ): Effect.Effect => uninterruptible( withFiber((fiber) => { const scope = Context.getUnsafe(fiber.context, scopeTag) const span = makeSpanUnsafe(fiber, name, options ?? {}) const clock = fiber.getRef(ClockRef) const timingEnabled = fiber.getRef(TracerTimingEnabled) return as( scopeAddFinalizerExit(scope, (exit) => endSpan(span, exit, clock, timingEnabled)), span ) }) ) /** @internal */ export const withSpanScoped: { ( name: string, options?: Tracer.SpanOptions ): (self: Effect.Effect) => Effect.Effect | Scope.Scope> ( self: Effect.Effect, name: string, options?: Tracer.SpanOptions ): Effect.Effect | Scope.Scope> } = function() { const dataFirst = typeof arguments[0] !== "string" const name = dataFirst ? arguments[1] : arguments[0] const options = addSpanStackTrace(dataFirst ? arguments[2] : arguments[1]) if (dataFirst) { const self = arguments[0] return flatMap( makeSpanScoped(name, options), (span) => withParentSpan(self, span, options) ) } return (self: Effect.Effect) => flatMap( makeSpanScoped(name, options), (span) => withParentSpan(self, span, options) ) } as any const provideSpanStackFrame = (name: string, stack: (() => string | undefined) | undefined) => { stack = typeof stack === "function" ? stack : constUndefined return updateService(CurrentStackFrame, (parent) => ({ name, stack, parent })) } /** @internal */ export const spanAnnotations: Effect.Effect>> = TracerSpanAnnotations /** @internal */ export const spanLinks: Effect.Effect> = TracerSpanLinks /** @internal */ export const linkSpans: { ( span: Tracer.AnySpan | ReadonlyArray, attributes?: Record ): (self: Effect.Effect) => Effect.Effect ( self: Effect.Effect, span: Tracer.AnySpan | ReadonlyArray, attributes?: Record ): Effect.Effect } = dual((args) => isEffect(args[0]), ( self: Effect.Effect, span: Tracer.AnySpan | ReadonlyArray, attributes: Record = {} ): Effect.Effect => { const spans: Array = Array.isArray(span) ? span : [span] const links = spans.map((span): Tracer.SpanLink => ({ span, attributes })) return updateService(self, TracerSpanLinks, (current) => [...current, ...links]) }) /** @internal */ export const endSpan = ( span: Tracer.Span, exit: Exit.Exit, clock: Clock.Clock, timingEnabled: boolean ) => sync(() => { if (span.status._tag === "Ended") return span.end(timingEnabled ? clock.currentTimeNanosUnsafe() : bigint0, exit) }) /** @internal */ export const useSpan: { (name: string, evaluate: (span: Tracer.Span) => Effect.Effect): Effect.Effect ( name: string, options: Tracer.SpanOptionsNoTrace, evaluate: (span: Tracer.Span) => Effect.Effect ): Effect.Effect } = ( name: string, ...args: [evaluate: (span: Tracer.Span) => Effect.Effect] | [ options: any, evaluate: (span: Tracer.Span) => Effect.Effect ] ): Effect.Effect => { const options = args.length === 1 ? undefined : args[0] const evaluate: (span: Tracer.Span) => Effect.Effect = args[args.length - 1] return withFiber((fiber) => { const span = makeSpanUnsafe(fiber, name, options) const clock = fiber.getRef(ClockRef) return onExit(internalCall(() => evaluate(span)), (exit) => sync(() => { if (span.status._tag === "Ended") return span.end(clock.currentTimeNanosUnsafe(), exit) })) }) } const provideParentSpan = provideService(Tracer.ParentSpan) /** @internal */ export const withParentSpan: { ( value: Tracer.AnySpan, options?: Tracer.TraceOptions ): (self: Effect.Effect) => Effect.Effect> ( self: Effect.Effect, value: Tracer.AnySpan, options?: Tracer.TraceOptions ): Effect.Effect> } = function() { const dataFirst = isEffect(arguments[0]) const span: Tracer.AnySpan = dataFirst ? arguments[1] : arguments[0] let options = dataFirst ? arguments[2] : arguments[1] let provideStackFrame: (self: Effect.Effect) => Effect.Effect = identity if (span._tag === "Span") { options = addSpanStackTrace(options) provideStackFrame = provideSpanStackFrame(span.name, options?.captureStackTrace) } if (dataFirst) { return provideParentSpan(provideStackFrame(arguments[0]), span) } return (self: Effect.Effect) => provideParentSpan(provideStackFrame(self), span) } as any /** @internal */ export const withSpan: { >( name: string, options?: Tracer.SpanOptionsNoTrace | ((...args: NoInfer) => Tracer.SpanOptionsNoTrace) | undefined, traceOptions?: Tracer.TraceOptions | undefined ): (self: Effect.Effect, ...args: Args) => Effect.Effect> ( self: Effect.Effect, name: string, options?: Tracer.SpanOptions | undefined ): Effect.Effect> } = function() { const dataFirst = typeof arguments[0] !== "string" const name = dataFirst ? arguments[1] : arguments[0] const traceOptions = addSpanStackTrace(arguments[2]) if (dataFirst) { const self = arguments[0] return useSpan(name, arguments[2], (span) => withParentSpan(self, span, traceOptions)) } const fnArg = typeof arguments[1] === "function" ? arguments[1] : undefined const options = fnArg ? undefined : arguments[1] return (self: Effect.Effect, ...args: any) => useSpan( name, fnArg ? fnArg(...args) : options, (span) => withParentSpan(self, span, traceOptions) ) } as any /** @internal */ export const annotateSpans: { (key: string, value: unknown): (effect: Effect.Effect) => Effect.Effect (values: Record): (effect: Effect.Effect) => Effect.Effect (effect: Effect.Effect, key: string, value: unknown): Effect.Effect (effect: Effect.Effect, values: Record): Effect.Effect } = dual( (args) => isEffect(args[0]), ( effect: Effect.Effect, ...args: [Record] | [key: string, value: unknown] ): Effect.Effect => updateService(effect, TracerSpanAnnotations, (annotations) => { const newAnnotations = { ...annotations } if (args.length === 1) { Object.assign(newAnnotations, args[0]) } else { newAnnotations[args[0]] = args[1] } return newAnnotations }) ) /** @internal */ export const annotateCurrentSpan: { (key: string, value: unknown): Effect.Effect (values: Record): Effect.Effect } = (...args: [Record] | [key: string, value: unknown]) => withFiber((fiber) => { const span = fiber.currentSpanLocal if (span) { if (args.length === 1) { for (const [key, value] of Object.entries(args[0])) { span.attribute(key, value) } } else { span.attribute(args[0], args[1]) } } return void_ }) /** @internal */ export const currentSpan: Effect.Effect = withFiber((fiber) => { const span = fiber.currentSpanLocal return span ? succeed(span) : fail(new NoSuchElementError()) }) /** @internal */ export const currentParentSpan: Effect.Effect = serviceOptional( Tracer.ParentSpan ) // ---------------------------------------------------------------------------- // Clock // ---------------------------------------------------------------------------- /** @internal */ export const ClockRef = Context.Reference("effect/Clock", { defaultValue: (): Clock.Clock => new ClockImpl() }) const MAX_TIMER_MILLIS = 2 ** 31 - 1 class ClockImpl implements Clock.Clock { currentTimeMillisUnsafe(): number { return Date.now() } readonly currentTimeMillis: Effect.Effect = sync(() => this.currentTimeMillisUnsafe()) currentTimeNanosUnsafe(): bigint { return processOrPerformanceNow() } readonly currentTimeNanos: Effect.Effect = sync(() => this.currentTimeNanosUnsafe()) sleep(duration: Duration.Duration): Effect.Effect { const millis = Duration.toMillis(duration) if (millis <= 0) return yieldNow return callback((resume) => { if (millis > MAX_TIMER_MILLIS) return const handle = setTimeout(() => resume(void_), millis) return sync(() => clearTimeout(handle)) }) } } const performanceNowNanos = (function() { const bigint1e6 = BigInt(1_000_000) if (typeof performance === "undefined" || typeof performance.now === "undefined") { return () => BigInt(Date.now()) * bigint1e6 } else if (typeof performance.timeOrigin === "number" && performance.timeOrigin === 0) { return () => BigInt(Math.round(performance.now() * 1_000_000)) } const origin = (BigInt(Date.now()) * bigint1e6) - BigInt(Math.round(performance.now() * 1_000_000)) return () => origin + BigInt(Math.round(performance.now() * 1_000_000)) })() const processOrPerformanceNow = (function() { const processHrtime = typeof process === "object" && "hrtime" in process && typeof process.hrtime.bigint === "function" ? process.hrtime : undefined if (!processHrtime) { return performanceNowNanos } const origin = performanceNowNanos() - processHrtime.bigint() return () => origin + processHrtime.bigint() })() /** @internal */ export const clockWith = (f: (clock: Clock.Clock) => Effect.Effect): Effect.Effect => withFiber((fiber) => f(fiber.getRef(ClockRef))) /** @internal */ export const sleep = (duration: Duration.Input): Effect.Effect => clockWith((clock) => clock.sleep(Duration.fromInputUnsafe(duration))) /** @internal */ export const currentTimeMillis: Effect.Effect = clockWith((clock) => clock.currentTimeMillis) /** @internal */ export const currentTimeNanos: Effect.Effect = clockWith((clock) => clock.currentTimeNanos) // ---------------------------------------------------------------------------- // Errors // ---------------------------------------------------------------------------- /** @internal */ export const TimeoutErrorTypeId = "~effect/Cause/TimeoutError" /** @internal */ export const isTimeoutError = (u: unknown): u is Cause.TimeoutError => hasProperty(u, TimeoutErrorTypeId) /** @internal */ export class TimeoutError extends TaggedError("TimeoutError") { readonly [TimeoutErrorTypeId] = TimeoutErrorTypeId constructor(message?: string) { super({ message } as any) } } /** @internal */ export const IllegalArgumentErrorTypeId = "~effect/Cause/IllegalArgumentError" /** @internal */ export const isIllegalArgumentError = ( u: unknown ): u is Cause.IllegalArgumentError => hasProperty(u, IllegalArgumentErrorTypeId) /** @internal */ export class IllegalArgumentError extends TaggedError("IllegalArgumentError") { readonly [IllegalArgumentErrorTypeId] = IllegalArgumentErrorTypeId constructor(message?: string) { super({ message } as any) } } /** @internal */ export const ExceededCapacityErrorTypeId = "~effect/Cause/ExceededCapacityError" /** @internal */ export const isExceededCapacityError = ( u: unknown ): u is Cause.ExceededCapacityError => hasProperty(u, ExceededCapacityErrorTypeId) /** @internal */ export class ExceededCapacityError extends TaggedError("ExceededCapacityError") { readonly [ExceededCapacityErrorTypeId] = ExceededCapacityErrorTypeId constructor(message?: string) { super({ message } as any) } } /** @internal */ export const AsyncFiberErrorTypeId = "~effect/Cause/AsyncFiberError" /** @internal */ export const isAsyncFiberError = ( u: unknown ): u is Cause.AsyncFiberError => hasProperty(u, AsyncFiberErrorTypeId) /** @internal */ export class AsyncFiberError extends TaggedError("AsyncFiberError")<{ fiber: Fiber.Fiber message: string }> { readonly [AsyncFiberErrorTypeId] = AsyncFiberErrorTypeId constructor(fiber: Fiber.Fiber) { super({ message: "An asynchronous Effect was executed with Effect.runSync", fiber }) } } /** @internal */ export const UnknownErrorTypeId = "~effect/Cause/UnknownError" /** @internal */ export const isUnknownError = ( u: unknown ): u is Cause.UnknownError => hasProperty(u, UnknownErrorTypeId) /** @internal */ export class UnknownError extends TaggedError("UnknownError")<{ cause: unknown message?: string | undefined }> { readonly [UnknownErrorTypeId] = UnknownErrorTypeId constructor(cause: unknown, message?: string) { super({ message, cause } as any) } } // ---------------------------------------------------------------------------- // Console // ---------------------------------------------------------------------------- /** @internal */ export const ConsoleRef = Context.Reference( "effect/Console/CurrentConsole", { defaultValue: (): Console.Console => globalThis.console } ) // ---------------------------------------------------------------------------- // LogLevel // ---------------------------------------------------------------------------- /** @internal */ export const logLevelToOrder = (level: LogLevel.LogLevel) => { switch (level) { case "All": return Number.MIN_SAFE_INTEGER case "Fatal": return 50_000 case "Error": return 40_000 case "Warn": return 30_000 case "Info": return 20_000 case "Debug": return 10_000 case "Trace": return 0 case "None": return Number.MAX_SAFE_INTEGER } } /** @internal */ export const LogLevelOrder = Order.mapInput(Order.Number, logLevelToOrder) /** @internal */ export const isLogLevelGreaterThan = Order.isGreaterThan(LogLevelOrder) // ---------------------------------------------------------------------------- // Logger // ---------------------------------------------------------------------------- /** @internal */ export const CurrentLoggers = Context.Reference< ReadonlySet> >("effect/Loggers/CurrentLoggers", { defaultValue: () => new Set([defaultLogger, tracerLogger]) }) /** @internal */ export const LogToStderr = Context.Reference("effect/Logger/LogToStderr", { defaultValue: constFalse }) /** @internal */ export const annotateLogsScoped: { (key: string, value: unknown): Effect.Effect (values: Record): Effect.Effect } = function() { const entries = typeof arguments[0] === "string" ? [[arguments[0], arguments[1]]] : Object.entries(arguments[0]) return uninterruptible(withFiber((fiber) => { const prev = fiber.getRef(CurrentLogAnnotations) const next = { ...prev } for (let i = 0; i < entries.length; i++) { const [key, value] = entries[i] next[key] = value } fiber.setContext(Context.add(fiber.context, CurrentLogAnnotations, next)) return scopeAddFinalizerExit(Context.getUnsafe(fiber.context, scopeTag), (_) => { const current = fiber.getRef(CurrentLogAnnotations) const next = { ...current } for (let i = 0; i < entries.length; i++) { const [key, value] = entries[i] if (current[key] !== value) continue if (key in prev) { next[key] = prev[key] } else { delete next[key] } } fiber.setContext(Context.add(fiber.context, CurrentLogAnnotations, next)) return void_ }) })) } /** @internal */ export const LoggerTypeId = "~effect/Logger" const LoggerProto = { [LoggerTypeId]: { _Message: identity, _Output: identity }, pipe() { return pipeArguments(this, arguments) } } /** @internal */ export const loggerMake = ( log: (options: Logger.Options) => Output ): Logger.Logger => { const self = Object.create(LoggerProto) self.log = log return self } /** * Sanitize a given string by replacing spaces, equal signs, and double quotes * with underscores. * * @internal */ export const formatLabel = (key: string) => key.replace(/[\s="]/g, "_") /** * Formats a log span into a `