| import * as Arr from "../Array.js"; | |
| import * as Context from "../Context.js"; | |
| import * as Duration from "../Duration.js"; | |
| import * as Equal from "../Equal.js"; | |
| import * as Filter from "../Filter.js"; | |
| import { formatJson } from "../Formatter.js"; | |
| import { constant, constFalse, constTrue, constUndefined, constVoid, dual, identity } from "../Function.js"; | |
| import * as Hash from "../Hash.js"; | |
| import { toJson, toStringUnknown } from "../Inspectable.js"; | |
| import * as Iterable from "../Iterable.js"; | |
| import * as Option from "../Option.js"; | |
| import * as Order from "../Order.js"; | |
| import { pipeArguments } from "../Pipeable.js"; | |
| import { hasProperty, isIterable, isString, isTagged } from "../Predicate.js"; | |
| import { currentFiberTypeId, redact } from "../Redactable.js"; | |
| import * as Result from "../Result.js"; | |
| import * as Scheduler from "../Scheduler.js"; | |
| import * as Tracer from "../Tracer.js"; | |
| import { internalCall } from "../Utils.js"; | |
| 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.js"; | |
| import * as doNotation from "./doNotation.js"; | |
| import * as InternalMetric from "./metric.js"; | |
| import { CurrentConcurrency, CurrentErrorReporters, CurrentLogAnnotations, CurrentLogLevel, CurrentLogSpans, CurrentStackFrame, MinimumLogLevel, TracerEnabled, TracerSpanAnnotations, TracerSpanLinks, TracerTimingEnabled } from "./references.js"; | |
| import { addSpanStackTrace, makeStackCleaner } from "./tracer.js"; | |
| import { version } from "./version.js"; | |
| // ---------------------------------------------------------------------------- | |
| // Cause | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export class Interrupt extends ReasonBase { | |
| fiberId; | |
| constructor(fiberId, annotations = constEmptyAnnotations) { | |
| super("Interrupt", annotations, "Interrupted"); | |
| this.fiberId = fiberId; | |
| } | |
| toString() { | |
| return `Interrupt(${this.fiberId})`; | |
| } | |
| toJSON() { | |
| return { | |
| _tag: "Interrupt", | |
| fiberId: this.fiberId | |
| }; | |
| } | |
| [Equal.symbol](that) { | |
| return isInterruptReason(that) && this.fiberId === that.fiberId && this.annotations === that.annotations; | |
| } | |
| [Hash.symbol]() { | |
| return Hash.combine(Hash.string(`${this._tag}:${this.fiberId}`))(Hash.random(this.annotations)); | |
| } | |
| } | |
| /** @internal */ | |
| export const makeInterruptReason = fiberId => new Interrupt(fiberId); | |
| /** @internal */ | |
| export const causeInterrupt = fiberId => new CauseImpl([new Interrupt(fiberId)]); | |
| /** @internal */ | |
| export const hasFails = self => self.reasons.some(isFailReason); | |
| /** @internal */ | |
| export const findFail = self => { | |
| const reason = self.reasons.find(isFailReason); | |
| return reason ? Result.succeed(reason) : Result.fail(self); | |
| }; | |
| /** @internal */ | |
| export const findError = self => { | |
| 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); | |
| }; | |
| /** @internal */ | |
| export const findErrorOption = /*#__PURE__*/Filter.toOption(findError); | |
| /** @internal */ | |
| export const hasDies = self => self.reasons.some(isDieReason); | |
| /** @internal */ | |
| export const findDie = self => { | |
| const reason = self.reasons.find(isDieReason); | |
| return reason ? Result.succeed(reason) : Result.fail(self); | |
| }; | |
| /** @internal */ | |
| export const findDefect = self => { | |
| const reason = self.reasons.find(isDieReason); | |
| return reason ? Result.succeed(reason.defect) : Result.fail(self); | |
| }; | |
| /** @internal */ | |
| export const hasInterrupts = self => self.reasons.some(isInterruptReason); | |
| /** @internal */ | |
| export const findInterrupt = self => { | |
| const reason = self.reasons.find(isInterruptReason); | |
| return reason ? Result.succeed(reason) : Result.fail(self); | |
| }; | |
| /** @internal */ | |
| export const causeFilterInterruptors = self => { | |
| let interruptors; | |
| 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 => { | |
| const result = causeFilterInterruptors(self); | |
| return Result.isFailure(result) ? emptySet : result.success; | |
| }; | |
| const emptySet = /*#__PURE__*/new Set(); | |
| /** @internal */ | |
| export const hasInterruptsOnly = self => self.reasons.length > 0 && self.reasons.every(isInterruptReason); | |
| /** @internal */ | |
| export const reasonAnnotations = self => Context.makeUnsafe(self.annotations); | |
| /** @internal */ | |
| export const causeAnnotations = self => { | |
| 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 = /*#__PURE__*/dual(2, (self, that) => { | |
| if (self.reasons.length === 0) { | |
| return that; | |
| } else if (that.reasons.length === 0) { | |
| return self; | |
| } | |
| const newCause = new CauseImpl(Arr.union(self.reasons, that.reasons)); | |
| return Equal.equals(self, newCause) ? self : newCause; | |
| }); | |
| /** @internal */ | |
| export const causeMap = /*#__PURE__*/dual(2, (self, f) => { | |
| 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; | |
| }); | |
| /** @internal */ | |
| export const causePartition = self => { | |
| const obj = { | |
| Fail: [], | |
| Die: [], | |
| Interrupt: [] | |
| }; | |
| for (let i = 0; i < self.reasons.length; i++) { | |
| obj[self.reasons[i]._tag].push(self.reasons[i]); | |
| } | |
| return obj; | |
| }; | |
| /** @internal */ | |
| export const causeSquash = self => { | |
| 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 => { | |
| const errors = []; | |
| const interrupts = []; | |
| if (self.reasons.length === 0) return errors; | |
| const prevStackLimit = Error.stackTraceLimit; | |
| Error.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, 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.stackTraceLimit = prevStackLimit; | |
| return errors; | |
| }; | |
| /** @internal */ | |
| export const causePrettyError = (original, annotations) => { | |
| const kind = typeof original; | |
| let error; | |
| if (original && kind === "object") { | |
| error = new globalThis.Error(causePrettyMessage(original), { | |
| cause: original.cause ? causePrettyError(original.cause) : 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[key] = original[key]; | |
| } | |
| } | |
| } else { | |
| error = new globalThis.Error(!original ? `Unknown error: ${original}` : kind === "string" ? original : formatJson(original)); | |
| } | |
| return error; | |
| }; | |
| const causePrettyMessage = u => { | |
| 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, error, annotations) => { | |
| const message = `${error.name}: ${error.message}`; | |
| const lines = (stack.startsWith(message) ? stack.slice(message.length) : stack).split("\n"); | |
| const out = [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, annotations) => { | |
| const frame = annotations?.get(CauseStackTrace.key); | |
| if (frame) { | |
| stack = `${stack}\n${currentStackTrace(frame)}`; | |
| } | |
| return stack; | |
| }; | |
| const interruptCauseStack = (error, interrupts) => { | |
| const out = [`${error.name}: ${error.message}`]; | |
| for (const current of interrupts) { | |
| const fiberId = current.fiberId !== undefined ? `#${current.fiberId}` : "unknown"; | |
| const frame = current.annotations.get(InterruptorStackTrace.key); | |
| out.push(` at fiber (${fiberId})`); | |
| if (frame) out.push(currentStackTrace(frame)); | |
| } | |
| return out.join("\n"); | |
| }; | |
| const currentStackTrace = frame => { | |
| const out = []; | |
| let current = 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 => causePrettyErrors(cause).map(e => e.cause ? `${e.stack} {\n${renderErrorCause(e.cause, " ")}\n}` : e.stack).join("\n"); | |
| const renderErrorCause = (cause, prefix) => { | |
| 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, `${prefix} `)}\n${prefix}}`; | |
| } | |
| return stack; | |
| }; | |
| // ---------------------------------------------------------------------------- | |
| // Fiber | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const FiberTypeId = `~effect/Fiber/${version}`; | |
| const fiberVariance = { | |
| _A: identity, | |
| _E: identity | |
| }; | |
| const fiberIdStore = { | |
| id: 0 | |
| }; | |
| /** @internal */ | |
| export const getCurrentFiber = () => globalThis[currentFiberTypeId]; | |
| /** @internal */ | |
| export class FiberImpl { | |
| constructor(context, interruptible = true) { | |
| this[FiberTypeId] = fiberVariance; | |
| 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); | |
| } | |
| [FiberTypeId]; | |
| id; | |
| interruptible; | |
| currentOpCount; | |
| currentLoopCount; | |
| _stack; | |
| _observers; | |
| _exit; | |
| _currentExit; | |
| _children; | |
| _interruptedCause; | |
| _yielded; | |
| // set in setContext | |
| context; | |
| currentScheduler; | |
| currentTracerContext; | |
| currentSpan; | |
| currentLogLevel; | |
| minimumLogLevel; | |
| currentStackFrame; | |
| runtimeMetrics; | |
| maxOpsBeforeYield; | |
| currentPreventYield; | |
| _dispatcher = undefined; | |
| get currentDispatcher() { | |
| return this._dispatcher ??= this.currentScheduler.makeDispatcher(); | |
| } | |
| getRef(ref) { | |
| return Context.getReferenceUnsafe(this.context, ref); | |
| } | |
| addObserver(cb) { | |
| 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, annotations) { | |
| 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)); | |
| } | |
| } | |
| pollUnsafe() { | |
| return this._exit; | |
| } | |
| evaluate(effect) { | |
| if (this._exit) { | |
| return; | |
| } else if (this._yielded !== undefined) { | |
| const yielded = this._yielded; | |
| 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)); | |
| } | |
| 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) { | |
| const prevFiber = globalThis[currentFiberTypeId]; | |
| globalThis[currentFiberTypeId] = this; | |
| let yielding = false; | |
| let current = effect; | |
| this.currentOpCount = 0; | |
| const currentLoop = ++this.currentLoopCount; | |
| try { | |
| while (true) { | |
| this.currentOpCount++; | |
| if (!yielding && !this.currentPreventYield && this.currentScheduler.shouldYield(this)) { | |
| yielding = true; | |
| const prev = current; | |
| current = flatMap(yieldNow, () => prev); | |
| } | |
| current = this.currentTracerContext ? this.currentTracerContext(current, this) : current[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)); | |
| } finally { | |
| ; | |
| globalThis[currentFiberTypeId] = prevFiber; | |
| } | |
| } | |
| getCont(symbol) { | |
| while (true) { | |
| const op = this._stack.pop(); | |
| if (!op) return undefined; | |
| const cont = op[contAll] && op[contAll](this); | |
| if (cont) { | |
| ; | |
| cont[symbol] = cont; | |
| return cont; | |
| } | |
| if (op[symbol]) return op; | |
| } | |
| } | |
| yieldWith(value) { | |
| this._yielded = value; | |
| return Yield; | |
| } | |
| children() { | |
| return this._children ??= new Set(); | |
| } | |
| pipe() { | |
| return pipeArguments(this, arguments); | |
| } | |
| setContext(context) { | |
| 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() { | |
| return this.currentSpan?._tag === "Span" ? this.currentSpan : undefined; | |
| } | |
| } | |
| const fiberMiddleware = { | |
| interruptChildren: undefined | |
| }; | |
| const fiberStackAnnotations = fiber => { | |
| if (!fiber.currentStackFrame) return undefined; | |
| const annotations = new Map(); | |
| annotations.set(CauseStackTrace.key, fiber.currentStackFrame); | |
| return Context.makeUnsafe(annotations); | |
| }; | |
| const fiberInterruptChildren = fiber => { | |
| if (fiber._children === undefined || fiber._children.size === 0) { | |
| return undefined; | |
| } | |
| return fiberInterruptAll(fiber._children); | |
| }; | |
| /** @internal */ | |
| export const fiberAwait = self => { | |
| const impl = self; | |
| 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 => callback(resume => { | |
| const iter = self[Symbol.iterator](); | |
| const exits = []; | |
| let cancel = 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 => { | |
| const impl = self; | |
| 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 => callback(resume => { | |
| const fibers = Array.from(self); | |
| if (fibers.length === 0) return resume(succeed(Arr.empty())); | |
| const out = new Array(fibers.length); | |
| const cancels = Arr.empty(); | |
| 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); | |
| } | |
| out[i] = exit.value; | |
| if (done === fibers.length) { | |
| resume(succeed(out)); | |
| } | |
| })); | |
| } | |
| }); | |
| /** @internal */ | |
| export const fiberInterrupt = self => withFiber(fiber => fiberInterruptAs(self, fiber.id)); | |
| /** @internal */ | |
| export const fiberInterruptAs = /*#__PURE__*/dual(args => hasProperty(args[0], FiberTypeId), (self, fiberId, annotations) => 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 => withFiber(parent => { | |
| const annotations = fiberStackAnnotations(parent); | |
| for (const fiber of fibers) { | |
| fiber.interruptUnsafe(parent.id, annotations); | |
| } | |
| return asVoid(fiberAwaitAll(fibers)); | |
| }); | |
| /** @internal */ | |
| export const fiberInterruptAllAs = /*#__PURE__*/dual(2, (fibers, fiberId) => withFiber(parent => { | |
| const annotations = fiberStackAnnotations(parent); | |
| for (const fiber of fibers) fiber.interruptUnsafe(fiberId, annotations); | |
| return asVoid(fiberAwaitAll(fibers)); | |
| })); | |
| /** @internal */ | |
| export const succeed = exitSucceed; | |
| /** @internal */ | |
| export const failCause = exitFailCause; | |
| /** @internal */ | |
| export const fail = exitFail; | |
| /** @internal */ | |
| export const sync = /*#__PURE__*/makePrimitive({ | |
| op: "Sync", | |
| [evaluate](fiber) { | |
| const value = this[args](); | |
| const cont = fiber.getCont(contA); | |
| return cont ? cont[contA](value, fiber) : fiber.yieldWith(exitSucceed(value)); | |
| } | |
| }); | |
| /** @internal */ | |
| export const suspend = /*#__PURE__*/makePrimitive({ | |
| op: "Suspend", | |
| [evaluate](_fiber) { | |
| return this[args](); | |
| } | |
| }); | |
| /** @internal */ | |
| export const fromOption = /*#__PURE__*/Option.match({ | |
| onNone: () => fail(new NoSuchElementError("Effect.fromOption: Option.none")), | |
| onSome: succeed | |
| }); | |
| /** @internal */ | |
| export const fromResult = /*#__PURE__*/Result.match({ | |
| onFailure: fail, | |
| onSuccess: succeed | |
| }); | |
| /** @internal */ | |
| export const fromNullishOr = value => value == null ? fail(new NoSuchElementError()) : succeed(value); | |
| /** @internal */ | |
| export const yieldNowWith = /*#__PURE__*/makePrimitive({ | |
| op: "Yield", | |
| [evaluate](fiber) { | |
| let resumed = false; | |
| fiber.currentDispatcher.scheduleTask(() => { | |
| if (resumed) return; | |
| fiber.evaluate(exitVoid); | |
| }, this[args] ?? 0); | |
| return fiber.yieldWith(() => { | |
| resumed = true; | |
| }); | |
| } | |
| }); | |
| /** @internal */ | |
| export const yieldNow = /*#__PURE__*/yieldNowWith(0); | |
| /** @internal */ | |
| export const succeedSome = a => succeed(Option.some(a)); | |
| /** @internal */ | |
| export const succeedNone = /*#__PURE__*/succeed(/*#__PURE__*/Option.none()); | |
| /** @internal */ | |
| export const failCauseSync = evaluate => suspend(() => failCause(internalCall(evaluate))); | |
| /** @internal */ | |
| export const die = defect => exitDie(defect); | |
| /** @internal */ | |
| export const failSync = error => suspend(() => fail(internalCall(error))); | |
| /** @internal */ | |
| const void_ = /*#__PURE__*/succeed(void 0); | |
| /** @internal */ | |
| export { void_ as void }; | |
| /** @internal */ | |
| const try_ = options => 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 => callbackOptions(function (resume, signal) { | |
| internalCall(() => evaluate(signal)).then(a => resume(succeed(a)), e => resume(die(e))); | |
| }, evaluate.length !== 0); | |
| /** @internal */ | |
| export const tryPromise = options => { | |
| const f = typeof options === "function" ? options : options.try; | |
| const catcher = typeof options === "function" ? cause => 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))))); | |
| } catch (err) { | |
| resume(fail(internalCall(() => catcher(err)))); | |
| } | |
| }, eval.length !== 0); | |
| }; | |
| /** @internal */ | |
| export const withFiberId = f => withFiber(fiber => f(fiber.id)); | |
| /** @internal */ | |
| export const fiber = /*#__PURE__*/withFiber(succeed); | |
| /** @internal */ | |
| export const fiberId = /*#__PURE__*/withFiberId(succeed); | |
| const callbackOptions = /*#__PURE__*/makePrimitive({ | |
| op: "Async", | |
| single: false, | |
| [evaluate](fiber) { | |
| const register = internalCall(() => this[args][0].bind(fiber.currentScheduler)); | |
| let resumed = false; | |
| let yielded = false; | |
| const controller = this[args][1] ? new AbortController() : undefined; | |
| const onCancel = register(effect => { | |
| if (resumed) return; | |
| resumed = true; | |
| if (yielded) { | |
| fiber.evaluate(effect); | |
| } else { | |
| yielded = effect; | |
| } | |
| }, 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 = /*#__PURE__*/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 => callbackOptions(register, register.length >= 2); | |
| /** @internal */ | |
| export const never = /*#__PURE__*/callback(constVoid); | |
| /** @internal */ | |
| export const gen = (...args) => suspend(() => fromIteratorUnsafe(args.length === 1 ? args[0]() : args[1].call(args[0].self))); | |
| /** @internal */ | |
| export const fnUntraced = (body, ...pipeables) => { | |
| const fn = pipeables.length === 0 ? function () { | |
| return suspend(() => fromIteratorUnsafe(body.apply(this, arguments))); | |
| } : function () { | |
| 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, fn) => Object.defineProperty(fn, "length", { | |
| value: length, | |
| configurable: true | |
| }); | |
| const fnStackCleaner = /*#__PURE__*/makeStackCleaner(2); | |
| /** @internal */ | |
| export const 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, ...pipeables) => makeFn(name, body, defError, pipeables, nameFirst, spanOptions); | |
| } | |
| return makeFn(name, arguments[0], defError, Array.prototype.slice.call(arguments, 1), nameFirst, spanOptions); | |
| }; | |
| const makeFn = (name, bodyOrOptions, defError, pipeables, addSpan, spanOptions) => { | |
| const body = typeof bodyOrOptions === "function" ? bodyOrOptions : pipeables.pop().bind(bodyOrOptions.self); | |
| return defineFunctionLength(body.length, function (...args) { | |
| 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 = (body, ...pipeables) => defineFunctionLength(body.length, pipeables.length === 0 ? function () { | |
| return fromIteratorEagerUnsafe(() => body.apply(this, arguments)); | |
| } : function () { | |
| let effect = fromIteratorEagerUnsafe(() => body.apply(this, arguments)); | |
| for (const pipeable of pipeables) { | |
| effect = pipeable(effect); | |
| } | |
| return effect; | |
| }); | |
| const fromIteratorEagerUnsafe = evaluate => { | |
| try { | |
| const iterator = evaluate(); | |
| let value = 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; | |
| 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 = /*#__PURE__*/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](fiber) { | |
| return this[contA](this[args][1], fiber); | |
| } | |
| }); | |
| // ---------------------------------------------------------------------------- | |
| // mapping & sequencing | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const as = /*#__PURE__*/dual(2, (self, value) => { | |
| const b = succeed(value); | |
| return flatMap(self, _ => b); | |
| }); | |
| /** @internal */ | |
| export const asSome = self => map(self, Option.some); | |
| /** @internal */ | |
| export const flip = self => matchEffect(self, { | |
| onFailure: succeed, | |
| onSuccess: fail | |
| }); | |
| /** @internal */ | |
| export const andThen = /*#__PURE__*/dual(2, (self, f) => flatMap(self, a => isEffect(f) ? f : internalCall(() => f(a)))); | |
| /** @internal */ | |
| export const tap = /*#__PURE__*/dual(2, (self, f) => flatMap(self, a => as(isEffect(f) ? f : internalCall(() => f(a)), a))); | |
| /** @internal */ | |
| export const asVoid = self => flatMap(self, _ => exitVoid); | |
| /** @internal */ | |
| export const sandbox = self => catchCause(self, fail); | |
| /** @internal */ | |
| export const raceAll = (all, options) => 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 = []; | |
| const onExit = (exit, fiber, i) => { | |
| 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, options) => withFiber(parent => callback(resume => { | |
| let done = false; | |
| const fibers = new Set(); | |
| const onExit = 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 = /*#__PURE__*/dual(args => isEffect(args[1]), (self, that, options) => raceAll([self, that], options)); | |
| /** @internal */ | |
| export const raceFirst = /*#__PURE__*/dual(args => isEffect(args[1]), (self, that, options) => raceAllFirst([self, that], options)); | |
| /** @internal */ | |
| export const flatMap = /*#__PURE__*/dual(2, (self, f) => { | |
| const onSuccess = Object.create(OnSuccessProto); | |
| onSuccess[args] = self; | |
| onSuccess[contA] = f.length !== 1 ? a => f(a) : f; | |
| return onSuccess; | |
| }); | |
| const OnSuccessProto = /*#__PURE__*/makePrimitiveProto({ | |
| op: "OnSuccess", | |
| [evaluate](fiber) { | |
| fiber._stack.push(this); | |
| return this[args]; | |
| } | |
| }); | |
| /** @internal */ | |
| export const matchCauseEffectEager = /*#__PURE__*/dual(2, (self, options) => { | |
| if (effectIsExit(self)) { | |
| return self._tag === "Success" ? options.onSuccess(self.value) : options.onFailure(self.cause); | |
| } | |
| return matchCauseEffect(self, options); | |
| }); | |
| /** @internal */ | |
| export const effectIsExit = effect => ExitTypeId in effect; | |
| /** @internal */ | |
| export const flatMapEager = /*#__PURE__*/dual(2, (self, f) => { | |
| if (effectIsExit(self)) { | |
| return self._tag === "Success" ? f(self.value) : self; | |
| } | |
| return flatMap(self, f); | |
| }); | |
| // ---------------------------------------------------------------------------- | |
| // mapping & sequencing | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const flatten = self => flatMap(self, identity); | |
| /** @internal */ | |
| export const map = /*#__PURE__*/dual(2, (self, f) => flatMap(self, a => succeed(internalCall(() => f(a))))); | |
| /** @internal */ | |
| export const mapEager = /*#__PURE__*/dual(2, (self, f) => effectIsExit(self) ? exitMap(self, f) : map(self, f)); | |
| /** @internal */ | |
| export const mapErrorEager = /*#__PURE__*/dual(2, (self, f) => effectIsExit(self) ? exitMapError(self, f) : mapError(self, f)); | |
| /** @internal */ | |
| export const mapBothEager = /*#__PURE__*/dual(2, (self, options) => effectIsExit(self) ? exitMapBoth(self, options) : mapBoth(self, options)); | |
| /** @internal */ | |
| export const catchEager = /*#__PURE__*/dual(2, (self, f) => { | |
| if (effectIsExit(self)) { | |
| if (self._tag === "Success") return self; | |
| const error = findError(self.cause); | |
| if (Result.isFailure(error)) return self; | |
| return f(error.success); | |
| } | |
| return catch_(self, f); | |
| }); | |
| // ---------------------------------------------------------------------------- | |
| // Exit | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const exitInterrupt = fiberId => exitFailCause(causeInterrupt(fiberId)); | |
| /** @internal */ | |
| export const exitIsSuccess = self => self._tag === "Success"; | |
| /** @internal */ | |
| export const exitFilterSuccess = self => self._tag === "Success" ? Result.succeed(self) : Result.fail(self); | |
| /** @internal */ | |
| export const exitFilterValue = self => self._tag === "Success" ? Result.succeed(self.value) : Result.fail(self); | |
| /** @internal */ | |
| export const exitIsFailure = self => self._tag === "Failure"; | |
| /** @internal */ | |
| export const exitFilterFailure = self => self._tag === "Failure" ? Result.succeed(self) : Result.fail(self); | |
| /** @internal */ | |
| export const exitFilterCause = self => self._tag === "Failure" ? Result.succeed(self.cause) : Result.fail(self); | |
| /** @internal */ | |
| export const exitFindError = /*#__PURE__*/Filter.composePassthrough(exitFilterCause, findError); | |
| /** @internal */ | |
| export const exitFindDefect = /*#__PURE__*/Filter.composePassthrough(exitFilterCause, findDefect); | |
| /** @internal */ | |
| export const exitHasInterrupts = self => self._tag === "Failure" && hasInterrupts(self.cause); | |
| /** @internal */ | |
| export const exitHasDies = self => self._tag === "Failure" && hasDies(self.cause); | |
| /** @internal */ | |
| export const exitHasFails = self => self._tag === "Failure" && hasFails(self.cause); | |
| /** @internal */ | |
| export const exitVoid = /*#__PURE__*/exitSucceed(void 0); | |
| /** @internal */ | |
| export const exitMap = /*#__PURE__*/dual(2, (self, f) => self._tag === "Success" ? exitSucceed(f(self.value)) : self); | |
| /** @internal */ | |
| export const exitMapError = /*#__PURE__*/dual(2, (self, f) => { | |
| if (self._tag === "Success") return self; | |
| const error = findError(self.cause); | |
| if (Result.isFailure(error)) return self; | |
| return exitFail(f(error.success)); | |
| }); | |
| /** @internal */ | |
| export const exitMapBoth = /*#__PURE__*/dual(2, (self, options) => { | |
| if (self._tag === "Success") return exitSucceed(options.onSuccess(self.value)); | |
| const error = findError(self.cause); | |
| if (Result.isFailure(error)) return self; | |
| return exitFail(options.onFailure(error.success)); | |
| }); | |
| /** @internal */ | |
| export const exitAs = /*#__PURE__*/dual(2, (self, b) => exitIsSuccess(self) ? exitSucceed(b) : self); | |
| /** @internal */ | |
| export const exitZipRight = /*#__PURE__*/dual(2, (self, that) => exitIsSuccess(self) ? that : self); | |
| /** @internal */ | |
| export const exitMatch = /*#__PURE__*/dual(2, (self, options) => exitIsSuccess(self) ? options.onSuccess(self.value) : options.onFailure(self.cause)); | |
| /** @internal */ | |
| export const exitAsVoid = /*#__PURE__*/exitAs(void 0); | |
| /** @internal */ | |
| export const exitAsVoidAll = exits => { | |
| const failures = []; | |
| 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 => exitIsSuccess(self) ? Option.some(self.value) : Option.none(); | |
| /** @internal */ | |
| export const exitGetCause = self => exitIsFailure(self) ? Option.some(self.cause) : Option.none(); | |
| /** @internal */ | |
| export const exitFindErrorOption = self => { | |
| const error = exitFindError(self); | |
| return Result.isFailure(error) ? Option.none() : Option.some(error.success); | |
| }; | |
| // ---------------------------------------------------------------------------- | |
| // environment | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const service = service => service; | |
| /** @internal */ | |
| export const serviceOption = service => withFiber(fiber => succeed(Context.getOption(fiber.context, service))); | |
| /** @internal */ | |
| export const serviceOptional = service => withFiber(fiber => fiber.context.mapUnsafe.has(service.key) ? succeed(Context.getUnsafe(fiber.context, service)) : fail(new NoSuchElementError())); | |
| /** @internal */ | |
| export const updateContext = /*#__PURE__*/dual(2, (self, f) => withFiber(fiber => { | |
| const prevContext = fiber.context; | |
| const nextContext = f(prevContext); | |
| if (prevContext === nextContext) return self; | |
| fiber.setContext(nextContext); | |
| return onExitPrimitive(self, () => { | |
| fiber.setContext(prevContext); | |
| return undefined; | |
| }); | |
| })); | |
| /** @internal */ | |
| export const updateService = /*#__PURE__*/dual(3, (self, service, f) => 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 = () => getContext; | |
| const getContext = /*#__PURE__*/withFiber(fiber => succeed(fiber.context)); | |
| /** @internal */ | |
| export const contextWith = f => withFiber(fiber => f(fiber.context)); | |
| /** @internal */ | |
| export const provideContext = /*#__PURE__*/dual(2, (self, context) => { | |
| if (effectIsExit(self)) return self; | |
| return updateContext(self, Context.merge(context)); | |
| }); | |
| /** @internal */ | |
| export const provideService = function () { | |
| if (arguments.length === 1) { | |
| return dual(2, (self, impl) => provideServiceImpl(self, arguments[0], impl)); | |
| } | |
| return dual(3, (self, service, impl) => provideServiceImpl(self, service, impl)).apply(this, arguments); | |
| }; | |
| const provideServiceImpl = (self, service, implementation) => updateContext(self, s => { | |
| const prev = s.mapUnsafe.get(service.key); | |
| if (prev === implementation) return s; | |
| return Context.add(s, service, implementation); | |
| }); | |
| /** @internal */ | |
| export const provideServiceEffect = /*#__PURE__*/dual(3, (self, service, acquire) => flatMap(acquire, implementation => provideService(self, service, implementation))); | |
| /** @internal */ | |
| export const withConcurrency = /*#__PURE__*/provideService(CurrentConcurrency); | |
| // ---------------------------------------------------------------------------- | |
| // zipping | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const zip = /*#__PURE__*/dual(args => isEffect(args[1]), (self, that, options) => zipWith(self, that, (a, a2) => [a, a2], options)); | |
| /** @internal */ | |
| export const zipWith = /*#__PURE__*/dual(args => isEffect(args[1]), (self, that, f, options) => 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 = /*#__PURE__*/dual(args => isEffect(args[0]), (self, predicate, orFailWith) => filterOrElse(self, predicate, orFailWith ? a => fail(orFailWith(a)) : () => fail(new NoSuchElementError()))); | |
| /** @internal */ | |
| export const when = /*#__PURE__*/dual(2, (self, condition) => flatMap(condition, pass => pass ? asSome(self) : succeedNone)); | |
| // ---------------------------------------------------------------------------- | |
| // repetition | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const replicate = /*#__PURE__*/dual(2, (self, n) => Array.from({ | |
| length: n | |
| }, () => self)); | |
| /** @internal */ | |
| export const replicateEffect = /*#__PURE__*/dual(args => isEffect(args[0]), (self, n, options) => all(replicate(self, n), options)); | |
| /** @internal */ | |
| export const forever = /*#__PURE__*/dual(args => isEffect(args[0]), (self, options) => whileLoop({ | |
| while: constTrue, | |
| body: constant(options?.disableYield ? self : flatMap(self, _ => yieldNow)), | |
| step: constVoid | |
| })); | |
| // ---------------------------------------------------------------------------- | |
| // error handling | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const catchCause = /*#__PURE__*/dual(2, (self, f) => { | |
| const onFailure = Object.create(OnFailureProto); | |
| onFailure[args] = self; | |
| onFailure[contE] = f.length !== 1 ? cause => f(cause) : f; | |
| return onFailure; | |
| }); | |
| const OnFailureProto = /*#__PURE__*/makePrimitiveProto({ | |
| op: "OnFailure", | |
| [evaluate](fiber) { | |
| fiber._stack.push(this); | |
| return this[args]; | |
| } | |
| }); | |
| /** @internal */ | |
| export const catchCauseIf = /*#__PURE__*/dual(3, (self, predicate, f) => catchCause(self, cause => { | |
| if (!predicate(cause)) { | |
| return failCause(cause); | |
| } | |
| return internalCall(() => f(cause)); | |
| })); | |
| /** @internal */ | |
| export const catchCauseFilter = /*#__PURE__*/dual(3, (self, filter, f) => catchCause(self, cause => { | |
| const eb = filter(cause); | |
| return Result.isFailure(eb) ? failCause(eb.failure) : internalCall(() => f(eb.success, cause)); | |
| })); | |
| /** @internal */ | |
| export const catch_ = /*#__PURE__*/dual(2, (self, f) => catchCauseFilter(self, findError, e => f(e))); | |
| /** @internal */ | |
| export const catchNoSuchElement = self => matchEffect(self, { | |
| onFailure: error => isNoSuchElementError(error) ? succeedNone : fail(error), | |
| onSuccess: succeedSome | |
| }); | |
| /** @internal */ | |
| export const catchDefect = /*#__PURE__*/dual(2, (self, f) => catchCauseFilter(self, findDefect, f)); | |
| /** @internal */ | |
| export const tapCause = /*#__PURE__*/dual(2, (self, f) => catchCause(self, cause => andThen(internalCall(() => f(cause)), failCause(cause)))); | |
| /** @internal */ | |
| export const tapCauseIf = /*#__PURE__*/dual(3, (self, predicate, f) => catchCauseIf(self, predicate, cause => andThen(internalCall(() => f(cause)), failCause(cause)))); | |
| /** @internal */ | |
| export const tapCauseFilter = /*#__PURE__*/dual(3, (self, filter, f) => 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 = /*#__PURE__*/dual(2, (self, f) => tapCauseFilter(self, findError, e => f(e))); | |
| /** @internal */ | |
| export const tapErrorTag = /*#__PURE__*/dual(3, (self, k, f) => { | |
| const predicate = Array.isArray(k) ? e => hasProperty(e, "_tag") && k.includes(e._tag) : isTagged(k); | |
| return tapError(self, error => predicate(error) ? f(error) : void_); | |
| }); | |
| /** @internal */ | |
| export const tapDefect = /*#__PURE__*/dual(2, (self, f) => tapCauseFilter(self, findDefect, _ => f(_))); | |
| /** @internal */ | |
| export const catchIf = /*#__PURE__*/dual(args => isEffect(args[0]), (self, predicate, f, orElse) => catchCause(self, cause => { | |
| const error = findError(cause); | |
| if (Result.isFailure(error)) return failCause(error.failure); | |
| if (!predicate(error.success)) { | |
| return orElse ? internalCall(() => orElse(error.success)) : failCause(cause); | |
| } | |
| return internalCall(() => f(error.success)); | |
| })); | |
| /** @internal */ | |
| export const catchFilter = /*#__PURE__*/dual(args => isEffect(args[0]), (self, filter, f, orElse) => catchCause(self, cause => { | |
| 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)) : failCause(cause); | |
| } | |
| return internalCall(() => f(result.success)); | |
| })); | |
| /** @internal */ | |
| export const catchTag = /*#__PURE__*/dual(args => isEffect(args[0]), (self, k, f, orElse) => { | |
| const pred = Array.isArray(k) ? e => hasProperty(e, "_tag") && k.includes(e._tag) : isTagged(k); | |
| return catchIf(self, pred, f, orElse); | |
| }); | |
| /** @internal */ | |
| export const catchTags = /*#__PURE__*/dual(args => isEffect(args[0]), (self, cases, orElse) => { | |
| let keys; | |
| 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 => internalCall(() => cases[e["_tag"]](e)), orElse); | |
| }); | |
| /** @internal */ | |
| export const catchReason = /*#__PURE__*/dual(args => isEffect(args[0]), (self, errorTag, reasonTag, f, orElse) => catchIf(self, e => isTagged(e, errorTag) && hasProperty(e, "reason"), e => { | |
| const reason = e.reason; | |
| if (isTagged(reason, reasonTag)) return f(reason, e); | |
| return orElse ? internalCall(() => orElse(reason, e)) : fail(e); | |
| })); | |
| /** @internal */ | |
| export const catchReasons = /*#__PURE__*/dual(args => isEffect(args[0]), (self, errorTag, cases, orElse) => { | |
| let keys; | |
| return catchIf(self, e => isTagged(e, errorTag) && hasProperty(e, "reason") && hasProperty(e.reason, "_tag") && isString(e.reason._tag), e => { | |
| const reason = e.reason; | |
| keys ??= Object.keys(cases); | |
| if (keys.includes(reason._tag)) { | |
| return internalCall(() => cases[reason._tag](reason, e)); | |
| } | |
| return orElse ? internalCall(() => orElse(reason, e)) : fail(e); | |
| }); | |
| }); | |
| /** @internal */ | |
| export const unwrapReason = /*#__PURE__*/dual(2, (self, errorTag) => catchFilter(self, e => { | |
| if (isTagged(e, errorTag) && hasProperty(e, "reason")) { | |
| return Result.succeed(e.reason); | |
| } | |
| return Result.fail(e); | |
| }, fail)); | |
| /** @internal */ | |
| export const mapErrorCause = /*#__PURE__*/dual(2, (self, f) => catchCause(self, cause => failCauseSync(() => f(cause)))); | |
| /** @internal */ | |
| export const mapError = /*#__PURE__*/dual(2, (self, f) => catch_(self, error => failSync(() => f(error)))); | |
| /* @internal */ | |
| export const mapBoth = /*#__PURE__*/dual(2, (self, options) => matchEffect(self, { | |
| onFailure: e => failSync(() => options.onFailure(e)), | |
| onSuccess: a => sync(() => options.onSuccess(a)) | |
| })); | |
| /** @internal */ | |
| export const orDie = self => catch_(self, die); | |
| /** @internal */ | |
| export const orElseSucceed = /*#__PURE__*/dual(2, (self, f) => catch_(self, _ => sync(f))); | |
| /** @internal */ | |
| export const firstSuccessOf = effects => 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) { | |
| const next = iterator.next(); | |
| if (next.done) return current.value; | |
| return catch_(current.value, _ => loop(next)); | |
| } | |
| return loop(state); | |
| }); | |
| /** @internal */ | |
| export const eventually = self => catch_(self, _ => flatMap(yieldNow, () => eventually(self))); | |
| /** @internal */ | |
| export const ignore = /*#__PURE__*/dual(args => isEffect(args[0]), (self, options) => { | |
| 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 = /*#__PURE__*/dual(args => isEffect(args[0]), (self, options) => { | |
| 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 => match(self, { | |
| onFailure: Option.none, | |
| onSuccess: Option.some | |
| }); | |
| /** @internal */ | |
| export const result = self => matchEager(self, { | |
| onFailure: Result.fail, | |
| onSuccess: Result.succeed | |
| }); | |
| // ---------------------------------------------------------------------------- | |
| // pattern matching | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const matchCauseEffect = /*#__PURE__*/dual(2, (self, options) => { | |
| const primitive = Object.create(OnSuccessAndFailureProto); | |
| primitive[args] = self; | |
| primitive[contA] = options.onSuccess.length !== 1 ? a => options.onSuccess(a) : options.onSuccess; | |
| primitive[contE] = options.onFailure.length !== 1 ? cause => options.onFailure(cause) : options.onFailure; | |
| return primitive; | |
| }); | |
| const OnSuccessAndFailureProto = /*#__PURE__*/makePrimitiveProto({ | |
| op: "OnSuccessAndFailure", | |
| [evaluate](fiber) { | |
| fiber._stack.push(this); | |
| return this[args]; | |
| } | |
| }); | |
| /** @internal */ | |
| export const matchCause = /*#__PURE__*/dual(2, (self, options) => matchCauseEffect(self, { | |
| onFailure: cause => sync(() => options.onFailure(cause)), | |
| onSuccess: value => sync(() => options.onSuccess(value)) | |
| })); | |
| /** @internal */ | |
| export const matchEffect = /*#__PURE__*/dual(2, (self, options) => matchCauseEffect(self, { | |
| onFailure: cause => { | |
| const fail = cause.reasons.find(isFailReason); | |
| return fail ? internalCall(() => options.onFailure(fail.error)) : failCause(cause); | |
| }, | |
| onSuccess: options.onSuccess | |
| })); | |
| /** @internal */ | |
| export const match = /*#__PURE__*/dual(2, (self, options) => matchEffect(self, { | |
| onFailure: error => sync(() => options.onFailure(error)), | |
| onSuccess: value => sync(() => options.onSuccess(value)) | |
| })); | |
| /** @internal */ | |
| export const matchEager = /*#__PURE__*/dual(2, (self, options) => { | |
| if (effectIsExit(self)) { | |
| if (self._tag === "Success") return exitSucceed(options.onSuccess(self.value)); | |
| const error = findError(self.cause); | |
| if (Result.isFailure(error)) return self; | |
| return exitSucceed(options.onFailure(error.success)); | |
| } | |
| return match(self, options); | |
| }); | |
| /** @internal */ | |
| export const matchCauseEager = /*#__PURE__*/dual(2, (self, options) => { | |
| 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 => effectIsExit(self) ? exitSucceed(self) : exitPrimitive(self); | |
| const exitPrimitive = /*#__PURE__*/makePrimitive({ | |
| op: "Exit", | |
| [evaluate](fiber) { | |
| fiber._stack.push(this); | |
| return this[args]; | |
| }, | |
| [contA](value, _, exit) { | |
| return succeed(exit ?? exitSucceed(value)); | |
| }, | |
| [contE](cause, _, exit) { | |
| return succeed(exit ?? exitFailCause(cause)); | |
| } | |
| }); | |
| // ---------------------------------------------------------------------------- | |
| // Condition checking | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const isFailure = /*#__PURE__*/matchEager({ | |
| onFailure: () => true, | |
| onSuccess: () => false | |
| }); | |
| /** @internal */ | |
| export const isSuccess = /*#__PURE__*/matchEager({ | |
| onFailure: () => false, | |
| onSuccess: () => true | |
| }); | |
| // ---------------------------------------------------------------------------- | |
| // delays & timeouts | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const delay = /*#__PURE__*/dual(2, (self, duration) => andThen(sleep(duration), self)); | |
| /** @internal */ | |
| export const timeoutOrElse = /*#__PURE__*/dual(2, (self, options) => raceFirst(self, flatMap(sleep(options.duration), options.orElse))); | |
| /** @internal */ | |
| export const timeout = /*#__PURE__*/dual(2, (self, duration) => timeoutOrElse(self, { | |
| duration, | |
| orElse: () => fail(new TimeoutError()) | |
| })); | |
| /** @internal */ | |
| export const timeoutOption = /*#__PURE__*/dual(2, (self, duration) => raceFirst(asSome(self), as(sleep(duration), Option.none()))); | |
| /** @internal */ | |
| export const timed = self => 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 = /*#__PURE__*/Context.Service("effect/Scope"); | |
| /** @internal */ | |
| export const scopeClose = (self, exit_) => suspend(() => scopeCloseUnsafe(self, exit_) ?? void_); | |
| /** @internal */ | |
| export const scopeCloseUnsafe = (self, exit_) => { | |
| if (self.state._tag === "Closed") return; | |
| const 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 = /*#__PURE__*/fnUntraced(function* (self, finalizers, exit_) { | |
| let exits = []; | |
| const fibers = []; | |
| 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, finalizerStrategy) => sync(() => scopeForkUnsafe(scope, finalizerStrategy)); | |
| /** @internal */ | |
| export const scopeForkUnsafe = (scope, finalizerStrategy) => { | |
| 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, finalizer) => { | |
| return suspend(() => { | |
| if (scope.state._tag === "Closed") { | |
| return finalizer(scope.state.exit); | |
| } | |
| scopeAddFinalizerUnsafe(scope, {}, finalizer); | |
| return void_; | |
| }); | |
| }; | |
| /** @internal */ | |
| export const scopeAddFinalizer = (scope, finalizer) => scopeAddFinalizerExit(scope, constant(finalizer)); | |
| /** @internal */ | |
| export const scopeAddFinalizerUnsafe = (scope, key, finalizer) => { | |
| 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, key) => { | |
| if (scope.state._tag === "Open") { | |
| scope.state.finalizers.delete(key); | |
| } | |
| }; | |
| /** @internal */ | |
| export const scopeMakeUnsafe = (finalizerStrategy = "sequential") => ({ | |
| [ScopeCloseableTypeId]: ScopeCloseableTypeId, | |
| [ScopeTypeId]: ScopeTypeId, | |
| strategy: finalizerStrategy, | |
| state: constScopeEmpty | |
| }); | |
| const constScopeEmpty = { | |
| _tag: "Empty" | |
| }; | |
| /** @internal */ | |
| export const scopeMake = finalizerStrategy => sync(() => scopeMakeUnsafe(finalizerStrategy)); | |
| /** @internal */ | |
| export const scope = scopeTag; | |
| /** @internal */ | |
| export const provideScope = /*#__PURE__*/provideService(scopeTag); | |
| /** @internal */ | |
| export const scoped = self => 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); | |
| }); | |
| }); | |
| /** @internal */ | |
| export const scopeUse = /*#__PURE__*/dual(2, (self, scope) => onExit(provideScope(self, scope), exit => suspend(() => scopeCloseUnsafe(scope, exit) ?? void_))); | |
| /** @internal */ | |
| export const scopedWith = f => suspend(() => { | |
| const scope = scopeMakeUnsafe(); | |
| return onExit(f(scope), exit => suspend(() => scopeCloseUnsafe(scope, exit) ?? void_)); | |
| }); | |
| /** @internal */ | |
| export const acquireRelease = (acquire, release, options) => contextWith(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 => flatMap(scope, scope => contextWith(context => scopeAddFinalizerExit(scope, exit => provideContext(finalizer(exit), context)))); | |
| /** @internal */ | |
| export const onExitPrimitive = /*#__PURE__*/makePrimitive({ | |
| op: "OnExit", | |
| single: false, | |
| [evaluate](fiber) { | |
| 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 = /*#__PURE__*/dual(2, onExitPrimitive); | |
| /** @internal */ | |
| export const ensuring = /*#__PURE__*/dual(2, (self, finalizer) => onExit(self, _ => finalizer)); | |
| /** @internal */ | |
| export const onExitIf = /*#__PURE__*/dual(3, (self, predicate, f) => onExit(self, exit => { | |
| if (!predicate(exit)) { | |
| return void_; | |
| } | |
| return f(exit); | |
| })); | |
| /** @internal */ | |
| export const onExitFilter = /*#__PURE__*/dual(3, (self, filter, f) => onExit(self, exit => { | |
| const b = filter(exit); | |
| return Result.isFailure(b) ? void_ : f(b.success, exit); | |
| })); | |
| /** @internal */ | |
| export const onError = /*#__PURE__*/dual(2, (self, f) => onExitFilter(self, exitFilterCause, f)); | |
| /** @internal */ | |
| export const onErrorIf = /*#__PURE__*/dual(3, (self, predicate, f) => onExitIf(self, exit => { | |
| if (exit._tag !== "Failure") { | |
| return false; | |
| } | |
| return predicate(exit.cause); | |
| }, exit => f(exit.cause))); | |
| /** @internal */ | |
| export const onErrorFilter = /*#__PURE__*/dual(3, (self, filter, f) => 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 = /*#__PURE__*/dual(2, (self, finalizer) => onErrorFilter(causeFilterInterruptors, finalizer)(self)); | |
| /** @internal */ | |
| export const acquireUseRelease = (acquire, use, release) => uninterruptibleMask(restore => flatMap(acquire, a => onExitPrimitive(restore(use(a)), exit => release(a, exit), true))); | |
| /** @internal */ | |
| export const acquireDisposable = acquire => acquireRelease(acquire, resource => hasProperty(resource, Symbol.asyncDispose) ? promise(() => resource[Symbol.asyncDispose]()) : sync(() => resource[Symbol.dispose]())); | |
| // ---------------------------------------------------------------------------- | |
| // Caching | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const cachedInvalidateWithTTL = /*#__PURE__*/dual(2, (self, ttl) => 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; | |
| 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 = /*#__PURE__*/dual(2, (self, timeToLive) => map(cachedInvalidateWithTTL(self, timeToLive), tuple => tuple[0])); | |
| /** @internal */ | |
| export const cached = self => cachedWithTTL(self, Duration.infinity); | |
| // ---------------------------------------------------------------------------- | |
| // interruption | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const interrupt = /*#__PURE__*/withFiber(fiber => failCause(causeInterrupt(fiber.id))); | |
| /** @internal */ | |
| export const uninterruptible = self => withFiber(fiber => { | |
| if (!fiber.interruptible) return self; | |
| fiber.interruptible = false; | |
| fiber._stack.push(setInterruptibleTrue); | |
| return self; | |
| }); | |
| const setInterruptible = /*#__PURE__*/makePrimitive({ | |
| op: "SetInterruptible", | |
| [contAll](fiber) { | |
| fiber.interruptible = this[args]; | |
| if (fiber._interruptedCause && fiber.interruptible) { | |
| return () => failCause(fiber._interruptedCause); | |
| } | |
| } | |
| }); | |
| const setInterruptibleTrue = /*#__PURE__*/setInterruptible(true); | |
| const setInterruptibleFalse = /*#__PURE__*/setInterruptible(false); | |
| /** @internal */ | |
| export const interruptible = self => 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 => withFiber(fiber => { | |
| if (!fiber.interruptible) return f(identity); | |
| fiber.interruptible = false; | |
| fiber._stack.push(setInterruptibleTrue); | |
| return f(interruptible); | |
| }); | |
| /** @internal */ | |
| export const interruptibleMask = f => withFiber(fiber => { | |
| if (fiber.interruptible) return f(identity); | |
| fiber.interruptible = true; | |
| fiber._stack.push(setInterruptibleFalse); | |
| return f(uninterruptible); | |
| }); | |
| /** @internal */ | |
| export const abortSignal = /*#__PURE__*/map(/*#__PURE__*/acquireRelease(/*#__PURE__*/sync(() => new AbortController()), controller => sync(() => controller.abort())), _ => _.signal); | |
| // ======================================================================== | |
| // collecting & elements | |
| // ======================================================================== | |
| /** @internal */ | |
| export const all = (arg, options) => { | |
| if (isIterable(arg)) { | |
| return options?.mode === "result" ? forEach(arg, result, options) : forEach(arg, identity, options); | |
| } else if (options?.discard) { | |
| return options.mode === "result" ? forEach(Object.values(arg), result, options) : forEach(Object.values(arg), identity, options); | |
| } | |
| return suspend(() => { | |
| const out = {}; | |
| 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); | |
| }); | |
| }; | |
| /** @internal */ | |
| export const partition = /*#__PURE__*/dual(args => isIterable(args[0]) && !isEffect(args[0]), (elements, f, options) => map(forEach(elements, (a, i) => result(f(a, i)), options), results => Arr.partition(results, identity))); | |
| /** @internal */ | |
| export const validate = /*#__PURE__*/dual(args => isIterable(args[0]) && !isEffect(args[0]), (elements, f, options) => 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 = /*#__PURE__*/dual(args => isIterable(args[0]) && !isEffect(args[0]), (elements, predicate) => 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, index, predicate, value) => 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 = /*#__PURE__*/dual(args => isIterable(args[0]) && !isEffect(args[0]), (elements, filter) => 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, index, filter, value) => 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 = /*#__PURE__*/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 = /*#__PURE__*/dual(args => typeof args[1] === "function", (iterable, f, options) => 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 = options?.discard ? undefined : new Array(length); | |
| const eff = forEachConcurrent({ | |
| f, | |
| out | |
| }, items, { | |
| concurrency | |
| }); | |
| return eff ? as(eff, out) : succeed(out); | |
| })); | |
| const forEachSequential = (iterable, f, options) => suspend(() => { | |
| const out = 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 => { | |
| const onItem = options.onItem; | |
| const step = options.step; | |
| return (state, items, opts) => { | |
| let index = opts?.start ?? 0; | |
| const end = opts?.end ?? items.length; | |
| const concurrency = opts?.concurrency ?? 1; | |
| let done = false; | |
| let parentFiber; | |
| let fibers; | |
| let resume; | |
| let interrupted = false; | |
| let terminal; | |
| let effect; | |
| const go = () => { | |
| 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.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 = () => iterateEagerImpl; | |
| const forEachConcurrent = /*#__PURE__*/iterateEagerImpl({ | |
| onItem(state, 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 = /*#__PURE__*/dual(3, (self, predicate, orElse) => flatMap(self, a => predicate(a) ? succeed(a) : orElse(a))); | |
| /** @internal */ | |
| export const filterMapOrElse = /*#__PURE__*/dual(3, (self, filter, orElse) => flatMap(self, a => { | |
| const result = filter(a); | |
| return Result.isFailure(result) ? orElse(result.failure) : succeed(result.success); | |
| })); | |
| /* @internal */ | |
| export const filterMapOrFail = /*#__PURE__*/dual(args => isEffect(args[0]), (self, filter, orFailWith) => filterMapOrElse(self, filter, orFailWith ? x => fail(orFailWith(x)) : () => fail(new NoSuchElementError()))); | |
| /** @internal */ | |
| export const filter = /*#__PURE__*/dual(args => isIterable(args[0]) && !isEffect(args[0]), (elements, predicate, options) => suspend(() => { | |
| const out = []; | |
| return as(forEach(elements, (a, i) => { | |
| const result = predicate(a, i); | |
| if (typeof result === "boolean") { | |
| if (result) out.push(a); | |
| return void_; | |
| } | |
| return map(result, keep => { | |
| if (keep) { | |
| out.push(a); | |
| } | |
| }); | |
| }, { | |
| discard: true, | |
| concurrency: options?.concurrency | |
| }), out); | |
| })); | |
| /** @internal */ | |
| export const filterMap = /*#__PURE__*/dual(args => isIterable(args[0]) && !isEffect(args[0]), (elements, filter) => suspend(() => { | |
| const out = []; | |
| for (const a of elements) { | |
| const result = filter(a); | |
| if (Result.isSuccess(result)) { | |
| out.push(result.success); | |
| } | |
| } | |
| return succeed(out); | |
| })); | |
| /** @internal */ | |
| export const filterMapEffect = /*#__PURE__*/dual(args => isIterable(args[0]) && !isEffect(args[0]), (elements, filter, options) => suspend(() => { | |
| const out = []; | |
| 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 = /*#__PURE__*/succeed({}); | |
| /** @internal */ | |
| export const bindTo = /*#__PURE__*/doNotation.bindTo(map); | |
| /** @internal */ | |
| export const bind = /*#__PURE__*/doNotation.bind(map, flatMap); | |
| /** @internal */ | |
| const let_ = /*#__PURE__*/doNotation.let_(map); | |
| /** @internal */ | |
| export { let_ as let }; | |
| // ---------------------------------------------------------------------------- | |
| // fibers & forking | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const forkChild = /*#__PURE__*/dual(args => isEffect(args[0]), (self, options) => withFiber(fiber => { | |
| interruptChildrenPatch(); | |
| return succeed(forkUnsafe(fiber, self, options?.startImmediately, false, options?.uninterruptible ?? false)); | |
| })); | |
| /** @internal */ | |
| export const forkUnsafe = (parent, effect, immediate = false, daemon = false, uninterruptible = false) => { | |
| const interruptible = uninterruptible === "inherit" ? parent.interruptible : !uninterruptible; | |
| const child = new FiberImpl(parent.context, interruptible); | |
| if (immediate) { | |
| child.evaluate(effect); | |
| } else { | |
| parent.currentDispatcher.scheduleTask(() => child.evaluate(effect), 0); | |
| } | |
| if (!daemon && !child._exit) { | |
| parent.children().add(child); | |
| child.addObserver(() => parent._children.delete(child)); | |
| } | |
| return child; | |
| }; | |
| /** @internal */ | |
| export const forkDetach = /*#__PURE__*/dual(args => isEffect(args[0]), (self, options) => withFiber(fiber => succeed(forkUnsafe(fiber, self, options?.startImmediately, true, options?.uninterruptible)))); | |
| /** @internal */ | |
| export const awaitAllChildren = self => 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 => !initialChildren.includes(child)); | |
| } | |
| return asVoid(fiberAwaitAll(children)); | |
| }); | |
| }); | |
| /** @internal */ | |
| export const forkIn = /*#__PURE__*/dual(args => isEffect(args[0]), (self, scope, options) => withFiber(parent => { | |
| const fiber = forkUnsafe(parent, self, options?.startImmediately, true, options?.uninterruptible); | |
| if (!fiber._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 = /*#__PURE__*/dual(args => isEffect(args[0]), (self, options) => flatMap(scope, scope => forkIn(self, scope, options))); | |
| // ---------------------------------------------------------------------------- | |
| // execution | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const runForkWith = context => (effect, options) => { | |
| const fiber = new FiberImpl(options?.scheduler ? Context.add(context, Scheduler.Scheduler, options.scheduler) : context, options?.uninterruptible !== true); | |
| fiber.evaluate(effect); | |
| 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 = /*#__PURE__*/dual(2, (self, scope) => { | |
| 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 = /*#__PURE__*/runForkWith(/*#__PURE__*/Context.empty()); | |
| /** @internal */ | |
| export const runCallbackWith = context => { | |
| const runFork = runForkWith(context); | |
| return (effect, options) => { | |
| const fiber = runFork(effect, options); | |
| if (options?.onExit) { | |
| fiber.addObserver(options.onExit); | |
| } | |
| return interruptor => { | |
| return fiber.interruptUnsafe(interruptor); | |
| }; | |
| }; | |
| }; | |
| /** @internal */ | |
| export const runCallback = /*#__PURE__*/runCallbackWith(/*#__PURE__*/Context.empty()); | |
| /** @internal */ | |
| export const runPromiseExitWith = context => { | |
| const runFork = runForkWith(context); | |
| return (effect, options) => { | |
| const fiber = runFork(effect, options); | |
| return new Promise(resolve => { | |
| fiber.addObserver(exit => resolve(exit)); | |
| }); | |
| }; | |
| }; | |
| /** @internal */ | |
| export const runPromiseExit = /*#__PURE__*/runPromiseExitWith(/*#__PURE__*/Context.empty()); | |
| /** @internal */ | |
| export const runPromiseWith = context => { | |
| const runPromiseExit = runPromiseExitWith(context); | |
| return (effect, options) => runPromiseExit(effect, options).then(exit => { | |
| if (exit._tag === "Failure") { | |
| throw causeSquash(exit.cause); | |
| } | |
| return exit.value; | |
| }); | |
| }; | |
| /** @internal */ | |
| export const runPromise = /*#__PURE__*/runPromiseWith(/*#__PURE__*/Context.empty()); | |
| /** @internal */ | |
| export const runSyncExitWith = context => { | |
| const runFork = runForkWith(context); | |
| return effect => { | |
| if (effectIsExit(effect)) return effect; | |
| const scheduler = new Scheduler.MixedScheduler("sync"); | |
| const fiber = runFork(effect, { | |
| scheduler | |
| }); | |
| fiber.currentDispatcher?.flush(); | |
| return fiber._exit ?? exitDie(new AsyncFiberError(fiber)); | |
| }; | |
| }; | |
| /** @internal */ | |
| export const runSyncExit = /*#__PURE__*/runSyncExitWith(/*#__PURE__*/Context.empty()); | |
| /** @internal */ | |
| export const runSyncWith = context => { | |
| const runSyncExit = runSyncExitWith(context); | |
| return effect => { | |
| const exit = runSyncExit(effect); | |
| if (exit._tag === "Failure") throw causeSquash(exit.cause); | |
| return exit.value; | |
| }; | |
| }; | |
| /** @internal */ | |
| export const runSync = /*#__PURE__*/runSyncWith(/*#__PURE__*/Context.empty()); | |
| const succeedTrue = /*#__PURE__*/succeed(true); | |
| const succeedFalse = /*#__PURE__*/succeed(false); | |
| class Latch { | |
| waiters = []; | |
| scheduled = false; | |
| isOpen; | |
| constructor(isOpen) { | |
| this.isOpen = isOpen; | |
| } | |
| scheduleUnsafe(fiber) { | |
| if (this.scheduled || this.waiters.length === 0) { | |
| return succeedTrue; | |
| } | |
| this.scheduled = true; | |
| fiber.currentDispatcher.scheduleTask(this.flushWaiters, 0); | |
| return succeedTrue; | |
| } | |
| flushWaiters = () => { | |
| this.scheduled = false; | |
| const waiters = this.waiters; | |
| this.waiters = []; | |
| for (let i = 0; i < waiters.length; i++) { | |
| waiters[i](exitVoid); | |
| } | |
| }; | |
| open = /*#__PURE__*/withFiber(fiber => { | |
| if (this.isOpen) return succeedFalse; | |
| this.isOpen = true; | |
| return this.scheduleUnsafe(fiber); | |
| }); | |
| release = /*#__PURE__*/withFiber(fiber => this.isOpen ? succeedFalse : this.scheduleUnsafe(fiber)); | |
| openUnsafe() { | |
| if (this.isOpen) return false; | |
| this.isOpen = true; | |
| this.flushWaiters(); | |
| return true; | |
| } | |
| await = /*#__PURE__*/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 = /*#__PURE__*/sync(() => this.closeUnsafe()); | |
| whenOpen = self => flatMap(this.await, () => self); | |
| } | |
| /** @internal */ | |
| export const makeLatchUnsafe = open => new Latch(open ?? false); | |
| /** @internal */ | |
| export const makeLatch = open => sync(() => makeLatchUnsafe(open)); | |
| // ---------------------------------------------------------------------------- | |
| // Tracer | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const tracer = /*#__PURE__*/withFiber(fiber => succeed(fiber.getRef(Tracer.Tracer))); | |
| /** @internal */ | |
| export const withTracer = /*#__PURE__*/dual(2, (effect, tracer) => provideService(effect, Tracer.Tracer, tracer)); | |
| /** @internal */ | |
| export const withTracerEnabled = /*#__PURE__*/provideService(TracerEnabled); | |
| /** @internal */ | |
| export const withTracerTiming = /*#__PURE__*/provideService(TracerTimingEnabled); | |
| const bigint0 = /*#__PURE__*/BigInt(0); | |
| const NoopSpanProto = { | |
| _tag: "Span", | |
| spanId: "noop", | |
| traceId: "noop", | |
| sampled: false, | |
| status: { | |
| _tag: "Ended", | |
| startTime: bigint0, | |
| endTime: bigint0, | |
| exit: exitVoid | |
| }, | |
| attributes: /*#__PURE__*/new Map(), | |
| links: [], | |
| kind: "internal", | |
| attribute() {}, | |
| event() {}, | |
| end() {}, | |
| addLinks() {} | |
| }; | |
| /** @internal */ | |
| export const noopSpan = options => Object.assign(Object.create(NoopSpanProto), options); | |
| const filterDisablePropagation = span => { | |
| 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, name, options) => { | |
| 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; | |
| 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, options) => withFiber(fiber => succeed(makeSpanUnsafe(fiber, name, options))); | |
| /** @internal */ | |
| export const makeSpanScoped = (name, options) => 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 = 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 => flatMap(makeSpanScoped(name, options), span => withParentSpan(self, span, options)); | |
| }; | |
| const provideSpanStackFrame = (name, stack) => { | |
| stack = typeof stack === "function" ? stack : constUndefined; | |
| return updateService(CurrentStackFrame, parent => ({ | |
| name, | |
| stack, | |
| parent | |
| })); | |
| }; | |
| /** @internal */ | |
| export const spanAnnotations = TracerSpanAnnotations; | |
| /** @internal */ | |
| export const spanLinks = TracerSpanLinks; | |
| /** @internal */ | |
| export const linkSpans = /*#__PURE__*/dual(args => isEffect(args[0]), (self, span, attributes = {}) => { | |
| const spans = Array.isArray(span) ? span : [span]; | |
| const links = spans.map(span => ({ | |
| span, | |
| attributes | |
| })); | |
| return updateService(self, TracerSpanLinks, current => [...current, ...links]); | |
| }); | |
| /** @internal */ | |
| export const endSpan = (span, exit, clock, timingEnabled) => sync(() => { | |
| if (span.status._tag === "Ended") return; | |
| span.end(timingEnabled ? clock.currentTimeNanosUnsafe() : bigint0, exit); | |
| }); | |
| /** @internal */ | |
| export const useSpan = (name, ...args) => { | |
| const options = args.length === 1 ? undefined : args[0]; | |
| const evaluate = 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 = /*#__PURE__*/provideService(Tracer.ParentSpan); | |
| /** @internal */ | |
| export const withParentSpan = function () { | |
| const dataFirst = isEffect(arguments[0]); | |
| const span = dataFirst ? arguments[1] : arguments[0]; | |
| let options = dataFirst ? arguments[2] : arguments[1]; | |
| let provideStackFrame = identity; | |
| if (span._tag === "Span") { | |
| options = addSpanStackTrace(options); | |
| provideStackFrame = provideSpanStackFrame(span.name, options?.captureStackTrace); | |
| } | |
| if (dataFirst) { | |
| return provideParentSpan(provideStackFrame(arguments[0]), span); | |
| } | |
| return self => provideParentSpan(provideStackFrame(self), span); | |
| }; | |
| /** @internal */ | |
| export const withSpan = 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, ...args) => useSpan(name, fnArg ? fnArg(...args) : options, span => withParentSpan(self, span, traceOptions)); | |
| }; | |
| /** @internal */ | |
| export const annotateSpans = /*#__PURE__*/dual(args => isEffect(args[0]), (effect, ...args) => 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 = (...args) => 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 = /*#__PURE__*/withFiber(fiber => { | |
| const span = fiber.currentSpanLocal; | |
| return span ? succeed(span) : fail(new NoSuchElementError()); | |
| }); | |
| /** @internal */ | |
| export const currentParentSpan = /*#__PURE__*/serviceOptional(Tracer.ParentSpan); | |
| // ---------------------------------------------------------------------------- | |
| // Clock | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const ClockRef = /*#__PURE__*/Context.Reference("effect/Clock", { | |
| defaultValue: () => new ClockImpl() | |
| }); | |
| const MAX_TIMER_MILLIS = 2 ** 31 - 1; | |
| class ClockImpl { | |
| currentTimeMillisUnsafe() { | |
| return Date.now(); | |
| } | |
| currentTimeMillis = /*#__PURE__*/sync(() => this.currentTimeMillisUnsafe()); | |
| currentTimeNanosUnsafe() { | |
| return processOrPerformanceNow(); | |
| } | |
| currentTimeNanos = /*#__PURE__*/sync(() => this.currentTimeNanosUnsafe()); | |
| sleep(duration) { | |
| 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 = /*#__PURE__*/function () { | |
| const bigint1e6 = /*#__PURE__*/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 = /*#__PURE__*/BigInt(/*#__PURE__*/Date.now()) * bigint1e6 - /*#__PURE__*/BigInt(/*#__PURE__*/Math.round(/*#__PURE__*/performance.now() * 1_000_000)); | |
| return () => origin + BigInt(Math.round(performance.now() * 1_000_000)); | |
| }(); | |
| const processOrPerformanceNow = /*#__PURE__*/function () { | |
| const processHrtime = typeof process === "object" && "hrtime" in process && typeof process.hrtime.bigint === "function" ? process.hrtime : undefined; | |
| if (!processHrtime) { | |
| return performanceNowNanos; | |
| } | |
| const origin = /*#__PURE__*/performanceNowNanos() - /*#__PURE__*/processHrtime.bigint(); | |
| return () => origin + processHrtime.bigint(); | |
| }(); | |
| /** @internal */ | |
| export const clockWith = f => withFiber(fiber => f(fiber.getRef(ClockRef))); | |
| /** @internal */ | |
| export const sleep = duration => clockWith(clock => clock.sleep(Duration.fromInputUnsafe(duration))); | |
| /** @internal */ | |
| export const currentTimeMillis = /*#__PURE__*/clockWith(clock => clock.currentTimeMillis); | |
| /** @internal */ | |
| export const currentTimeNanos = /*#__PURE__*/clockWith(clock => clock.currentTimeNanos); | |
| // ---------------------------------------------------------------------------- | |
| // Errors | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const TimeoutErrorTypeId = "~effect/Cause/TimeoutError"; | |
| /** @internal */ | |
| export const isTimeoutError = u => hasProperty(u, TimeoutErrorTypeId); | |
| /** @internal */ | |
| export class TimeoutError extends /*#__PURE__*/TaggedError("TimeoutError") { | |
| [TimeoutErrorTypeId] = TimeoutErrorTypeId; | |
| constructor(message) { | |
| super({ | |
| message | |
| }); | |
| } | |
| } | |
| /** @internal */ | |
| export const IllegalArgumentErrorTypeId = "~effect/Cause/IllegalArgumentError"; | |
| /** @internal */ | |
| export const isIllegalArgumentError = u => hasProperty(u, IllegalArgumentErrorTypeId); | |
| /** @internal */ | |
| export class IllegalArgumentError extends /*#__PURE__*/TaggedError("IllegalArgumentError") { | |
| [IllegalArgumentErrorTypeId] = IllegalArgumentErrorTypeId; | |
| constructor(message) { | |
| super({ | |
| message | |
| }); | |
| } | |
| } | |
| /** @internal */ | |
| export const ExceededCapacityErrorTypeId = "~effect/Cause/ExceededCapacityError"; | |
| /** @internal */ | |
| export const isExceededCapacityError = u => hasProperty(u, ExceededCapacityErrorTypeId); | |
| /** @internal */ | |
| export class ExceededCapacityError extends /*#__PURE__*/TaggedError("ExceededCapacityError") { | |
| [ExceededCapacityErrorTypeId] = ExceededCapacityErrorTypeId; | |
| constructor(message) { | |
| super({ | |
| message | |
| }); | |
| } | |
| } | |
| /** @internal */ | |
| export const AsyncFiberErrorTypeId = "~effect/Cause/AsyncFiberError"; | |
| /** @internal */ | |
| export const isAsyncFiberError = u => hasProperty(u, AsyncFiberErrorTypeId); | |
| /** @internal */ | |
| export class AsyncFiberError extends /*#__PURE__*/TaggedError("AsyncFiberError") { | |
| [AsyncFiberErrorTypeId] = AsyncFiberErrorTypeId; | |
| constructor(fiber) { | |
| super({ | |
| message: "An asynchronous Effect was executed with Effect.runSync", | |
| fiber | |
| }); | |
| } | |
| } | |
| /** @internal */ | |
| export const UnknownErrorTypeId = "~effect/Cause/UnknownError"; | |
| /** @internal */ | |
| export const isUnknownError = u => hasProperty(u, UnknownErrorTypeId); | |
| /** @internal */ | |
| export class UnknownError extends /*#__PURE__*/TaggedError("UnknownError") { | |
| [UnknownErrorTypeId] = UnknownErrorTypeId; | |
| constructor(cause, message) { | |
| super({ | |
| message, | |
| cause | |
| }); | |
| } | |
| } | |
| // ---------------------------------------------------------------------------- | |
| // Console | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const ConsoleRef = /*#__PURE__*/Context.Reference("effect/Console/CurrentConsole", { | |
| defaultValue: () => globalThis.console | |
| }); | |
| // ---------------------------------------------------------------------------- | |
| // LogLevel | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const logLevelToOrder = level => { | |
| 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 = /*#__PURE__*/Order.mapInput(Order.Number, logLevelToOrder); | |
| /** @internal */ | |
| export const isLogLevelGreaterThan = /*#__PURE__*/Order.isGreaterThan(LogLevelOrder); | |
| // ---------------------------------------------------------------------------- | |
| // Logger | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const CurrentLoggers = /*#__PURE__*/Context.Reference("effect/Loggers/CurrentLoggers", { | |
| defaultValue: () => new Set([defaultLogger, tracerLogger]) | |
| }); | |
| /** @internal */ | |
| export const LogToStderr = /*#__PURE__*/Context.Reference("effect/Logger/LogToStderr", { | |
| defaultValue: constFalse | |
| }); | |
| /** @internal */ | |
| export const annotateLogsScoped = 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 => { | |
| 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 => key.replace(/[\s="]/g, "_"); | |
| /** | |
| * Formats a log span into a `<label>=<value>ms` string. | |
| * | |
| * @internal | |
| */ | |
| export const formatLogSpan = (self, now) => { | |
| const label = formatLabel(self[0]); | |
| return `${label}=${now - self[1]}ms`; | |
| }; | |
| /** @internal */ | |
| export const structuredMessage = u => { | |
| switch (typeof u) { | |
| case "bigint": | |
| case "function": | |
| case "symbol": | |
| { | |
| return String(u); | |
| } | |
| default: | |
| { | |
| return toJson(u); | |
| } | |
| } | |
| }; | |
| /** @internal */ | |
| export const logWithLevel = level => (...message) => { | |
| let cause = undefined; | |
| for (let i = 0, len = message.length; i < len; i++) { | |
| const msg = message[i]; | |
| if (isCause(msg)) { | |
| if (cause) { | |
| ; | |
| message.splice(i, 1); | |
| } else { | |
| message = message.slice(0, i).concat(message.slice(i + 1)); | |
| } | |
| cause = cause ? causeFromReasons(cause.reasons.concat(msg.reasons)) : msg; | |
| i--; | |
| } | |
| } | |
| if (cause === undefined) { | |
| cause = causeEmpty; | |
| } | |
| return withFiber(fiber => { | |
| const logLevel = level ?? fiber.currentLogLevel; | |
| if (isLogLevelGreaterThan(fiber.minimumLogLevel, logLevel)) { | |
| return void_; | |
| } | |
| const clock = fiber.getRef(ClockRef); | |
| const loggers = fiber.getRef(CurrentLoggers); | |
| if (loggers.size > 0) { | |
| const date = new Date(clock.currentTimeMillisUnsafe()); | |
| for (const logger of loggers) { | |
| logger.log({ | |
| cause, | |
| fiber, | |
| date, | |
| logLevel, | |
| message | |
| }); | |
| } | |
| } | |
| return void_; | |
| }); | |
| }; | |
| const withColor = (text, ...colors) => { | |
| let out = ""; | |
| for (let i = 0; i < colors.length; i++) { | |
| out += `\x1b[${colors[i]}m`; | |
| } | |
| return out + text + "\x1b[0m"; | |
| }; | |
| const withColorNoop = (text, ..._colors) => text; | |
| const colors = { | |
| bold: "1", | |
| red: "31", | |
| green: "32", | |
| yellow: "33", | |
| blue: "34", | |
| cyan: "36", | |
| white: "37", | |
| gray: "90", | |
| black: "30", | |
| bgBrightRed: "101" | |
| }; | |
| const logLevelColors = { | |
| None: [], | |
| All: [], | |
| Trace: [colors.gray], | |
| Debug: [colors.blue], | |
| Info: [colors.green], | |
| Warn: [colors.yellow], | |
| Error: [colors.red], | |
| Fatal: [colors.bgBrightRed, colors.black] | |
| }; | |
| const logLevelStyle = { | |
| None: "", | |
| All: "", | |
| Trace: "color:gray", | |
| Debug: "color:blue", | |
| Info: "color:green", | |
| Warn: "color:orange", | |
| Error: "color:red", | |
| Fatal: "background-color:red;color:white" | |
| }; | |
| const defaultDateFormat = date => `${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}:${date.getSeconds().toString().padStart(2, "0")}.${date.getMilliseconds().toString().padStart(3, "0")}`; | |
| /** @internal */ | |
| export const consolePretty = options => { | |
| // evaluated lazily so the module-level bundle stays free of `process` | |
| // property accesses, which bundlers must retain as possible side effects | |
| const hasProcessStdout = typeof process === "object" && process !== null && typeof process.stdout === "object" && process.stdout !== null; | |
| const processStdoutIsTTY = hasProcessStdout && process.stdout.isTTY === true; | |
| const hasProcessStdoutOrDeno = hasProcessStdout || "Deno" in globalThis; | |
| const mode_ = options?.mode ?? "auto"; | |
| const mode = mode_ === "auto" ? hasProcessStdoutOrDeno ? "tty" : "browser" : mode_; | |
| const isBrowser = mode === "browser"; | |
| const showColors = typeof options?.colors === "boolean" ? options.colors : processStdoutIsTTY || isBrowser; | |
| const formatDate = options?.formatDate ?? defaultDateFormat; | |
| return isBrowser ? prettyLoggerBrowser({ | |
| colors: showColors, | |
| formatDate | |
| }) : prettyLoggerTty({ | |
| colors: showColors, | |
| formatDate | |
| }); | |
| }; | |
| const prettyLoggerTty = options => { | |
| const processIsBun = typeof process === "object" && "isBun" in process && process.isBun === true; | |
| const color = options.colors ? withColor : withColorNoop; | |
| return loggerMake(({ | |
| cause, | |
| date, | |
| fiber, | |
| logLevel, | |
| message: message_ | |
| }) => { | |
| const console = fiber.getRef(ConsoleRef); | |
| // oxlint-disable-next-line no-console | |
| const log = fiber.getRef(LogToStderr) ? console.error : console.log; | |
| const message = Array.isArray(message_) ? message_.slice() : [message_]; | |
| let firstLine = color(`[${options.formatDate(date)}]`, colors.white) + ` ${color(logLevel.toUpperCase(), ...logLevelColors[logLevel])}` + ` (#${fiber.id})`; | |
| const now = date.getTime(); | |
| const spans = fiber.getRef(CurrentLogSpans); | |
| for (const span of spans) { | |
| firstLine += " " + formatLogSpan(span, now); | |
| } | |
| firstLine += ":"; | |
| let messageIndex = 0; | |
| if (message.length > 0) { | |
| const firstMaybeString = structuredMessage(message[0]); | |
| if (typeof firstMaybeString === "string") { | |
| firstLine += " " + color(firstMaybeString, colors.bold, colors.cyan); | |
| messageIndex++; | |
| } | |
| } | |
| log(firstLine); | |
| // oxlint-disable-next-line no-console | |
| if (!processIsBun) console.group(); | |
| if (cause.reasons.length > 0) { | |
| log(causePretty(cause)); | |
| } | |
| if (messageIndex < message.length) { | |
| for (; messageIndex < message.length; messageIndex++) { | |
| log(redact(message[messageIndex])); | |
| } | |
| } | |
| const annotations = fiber.getRef(CurrentLogAnnotations); | |
| for (const [key, value] of Object.entries(annotations)) { | |
| log(color(`${key}:`, colors.bold, colors.white), redact(value)); | |
| } | |
| // oxlint-disable-next-line no-console | |
| if (!processIsBun) console.groupEnd(); | |
| }); | |
| }; | |
| const prettyLoggerBrowser = options => { | |
| const color = options.colors ? "%c" : ""; | |
| return loggerMake(({ | |
| cause, | |
| date, | |
| fiber, | |
| logLevel, | |
| message: message_ | |
| }) => { | |
| const console = fiber.getRef(ConsoleRef); | |
| const message = Array.isArray(message_) ? message_.slice() : [message_]; | |
| let firstLine = `${color}[${options.formatDate(date)}]`; | |
| const firstParams = []; | |
| if (options.colors) { | |
| firstParams.push("color:gray"); | |
| } | |
| firstLine += ` ${color}${logLevel.toUpperCase()}${color} (#${fiber.id})`; | |
| if (options.colors) { | |
| firstParams.push(logLevelStyle[logLevel], ""); | |
| } | |
| const now = date.getTime(); | |
| const spans = fiber.getRef(CurrentLogSpans); | |
| for (const span of spans) { | |
| firstLine += " " + formatLogSpan(span, now); | |
| } | |
| firstLine += ":"; | |
| let messageIndex = 0; | |
| if (message.length > 0) { | |
| const firstMaybeString = structuredMessage(message[0]); | |
| if (typeof firstMaybeString === "string") { | |
| firstLine += ` ${color}${firstMaybeString}`; | |
| if (options.colors) { | |
| firstParams.push("color:deepskyblue"); | |
| } | |
| messageIndex++; | |
| } | |
| } | |
| // oxlint-disable-next-line no-console | |
| console.groupCollapsed(firstLine, ...firstParams); | |
| if (cause.reasons.length > 0) { | |
| // oxlint-disable-next-line no-console | |
| console.error(causePretty(cause)); | |
| } | |
| if (messageIndex < message.length) { | |
| for (; messageIndex < message.length; messageIndex++) { | |
| // oxlint-disable-next-line no-console | |
| console.log(redact(message[messageIndex])); | |
| } | |
| } | |
| const annotations = fiber.getRef(CurrentLogAnnotations); | |
| for (const [key, value] of Object.entries(annotations)) { | |
| const redacted = redact(value); | |
| if (options.colors) { | |
| // oxlint-disable-next-line no-console | |
| console.log(`%c${key}:`, "color:gray", redacted); | |
| } else { | |
| // oxlint-disable-next-line no-console | |
| console.log(`${key}:`, redacted); | |
| } | |
| } | |
| // oxlint-disable-next-line no-console | |
| console.groupEnd(); | |
| }); | |
| }; | |
| /** @internal */ | |
| export const defaultLogger = /*#__PURE__*/loggerMake(({ | |
| cause, | |
| date, | |
| fiber, | |
| logLevel, | |
| message | |
| }) => { | |
| const message_ = Array.isArray(message) ? message.slice() : [message]; | |
| if (cause.reasons.length > 0) { | |
| message_.push(causePretty(cause)); | |
| } | |
| const now = date.getTime(); | |
| const spans = fiber.getRef(CurrentLogSpans); | |
| let spanString = ""; | |
| for (const span of spans) { | |
| spanString += ` ${formatLogSpan(span, now)}`; | |
| } | |
| const annotations = fiber.getRef(CurrentLogAnnotations); | |
| if (Object.keys(annotations).length > 0) { | |
| message_.push(annotations); | |
| } | |
| const console = fiber.getRef(ConsoleRef); | |
| // oxlint-disable-next-line no-console | |
| const log = fiber.getRef(LogToStderr) ? console.error : console.log; | |
| log(`[${defaultDateFormat(date)}] ${logLevel.toUpperCase()} (#${fiber.id})${spanString}:`, ...message_); | |
| }); | |
| /** @internal */ | |
| export const tracerLogger = /*#__PURE__*/loggerMake(({ | |
| cause, | |
| fiber, | |
| logLevel, | |
| message | |
| }) => { | |
| const clock = fiber.getRef(ClockRef); | |
| const annotations = fiber.getRef(CurrentLogAnnotations); | |
| const span = fiber.currentSpan; | |
| if (span === undefined || span._tag === "ExternalSpan") return; | |
| const attributes = {}; | |
| for (const [key, value] of Object.entries(annotations)) { | |
| attributes[key] = value; | |
| } | |
| attributes["effect.fiberId"] = fiber.id; | |
| attributes["effect.logLevel"] = logLevel.toUpperCase(); | |
| if (cause.reasons.length > 0) { | |
| attributes["effect.cause"] = causePretty(cause); | |
| } | |
| span.event(toStringUnknown(Array.isArray(message) && message.length === 1 ? message[0] : message), clock.currentTimeNanosUnsafe(), attributes); | |
| }); | |
| /** @internal */ | |
| export function interruptChildrenPatch() { | |
| fiberMiddleware.interruptChildren ??= fiberInterruptChildren; | |
| } | |
| /** @internal */ | |
| const undefined_ = /*#__PURE__*/succeed(undefined); | |
| /** @internal */ | |
| export { undefined_ as undefined }; | |
| // ---------------------------------------------------------------------------- | |
| // ErrorReporter | |
| // ---------------------------------------------------------------------------- | |
| /** @internal */ | |
| export const withErrorReporting = /*#__PURE__*/dual(args => isEffect(args[0]), (self, options) => onError(self, cause => withFiber(fiber => { | |
| reportCauseUnsafe(fiber, cause, options?.defectsOnly); | |
| return void_; | |
| }))); | |
| /** @internal */ | |
| export const reportCauseUnsafe = (fiber, cause, defectsOnly) => { | |
| const reporters = fiber.getRef(CurrentErrorReporters); | |
| if (reporters.size === 0) return; | |
| if (defectsOnly && !hasDies(cause)) return; | |
| const opts = { | |
| cause, | |
| fiber, | |
| timestamp: fiber.getRef(ClockRef).currentTimeNanosUnsafe() | |
| }; | |
| reporters.forEach(reporter => reporter.report(opts)); | |
| }; | |
| //# sourceMappingURL=effect.js.map |
Xet Storage Details
- Size:
- 110 kB
- Xet hash:
- 3ddacc50e47d68669a0293df146b924106060b1fc24ae42eae5152159336c42e
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.