@nicolastoulemont/std 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -6
- package/dist/context-CCHj1nab.mjs.map +1 -1
- package/dist/data/index.d.mts +1 -1
- package/dist/data/index.mjs +1 -1
- package/dist/{data-CJxw6al9.mjs → data-BLXO4XwS.mjs} +2 -2
- package/dist/{data-CJxw6al9.mjs.map → data-BLXO4XwS.mjs.map} +1 -1
- package/dist/data.tagged-error.types-CGiKD-ES.d.mts +29 -0
- package/dist/data.tagged-error.types-CGiKD-ES.d.mts.map +1 -0
- package/dist/either/index.d.mts +1 -1
- package/dist/either/index.mjs +1 -1
- package/dist/{either-6BwadiFj.mjs → either-BMLPfvMl.mjs} +2 -2
- package/dist/either-BMLPfvMl.mjs.map +1 -0
- package/dist/fx/index.d.mts +1 -1
- package/dist/fx/index.mjs +1 -1
- package/dist/{fx-BzxLbf1Q.mjs → fx-K-a9Smhn.mjs} +2 -2
- package/dist/fx-K-a9Smhn.mjs.map +1 -0
- package/dist/{fx.runtime-BcC6yMSy.mjs → fx.runtime-DclEDyjY.mjs} +2 -2
- package/dist/{fx.runtime-BcC6yMSy.mjs.map → fx.runtime-DclEDyjY.mjs.map} +1 -1
- package/dist/{index-DSsDFLGw.d.mts → index-B1-tBzc0.d.mts} +2 -2
- package/dist/{index-DSsDFLGw.d.mts.map → index-B1-tBzc0.d.mts.map} +1 -1
- package/dist/{index-CugDqdx6.d.mts → index-BNQ9xSAz.d.mts} +2 -2
- package/dist/{index-CugDqdx6.d.mts.map → index-BNQ9xSAz.d.mts.map} +1 -1
- package/dist/index-By6dNRc4.d.mts.map +1 -1
- package/dist/{index-DaTvFhZ8.d.mts → index-CGiLfREk.d.mts} +37 -37
- package/dist/{index-DaTvFhZ8.d.mts.map → index-CGiLfREk.d.mts.map} +1 -1
- package/dist/{index-CCo85AdC.d.mts → index-Cq2IFito.d.mts} +2 -2
- package/dist/{index-CCo85AdC.d.mts.map → index-Cq2IFito.d.mts.map} +1 -1
- package/dist/index-DEAWPlcI.d.mts.map +1 -1
- package/dist/{index-Dm2dFysv.d.mts → index-UzMbg1dh.d.mts} +2 -2
- package/dist/{index-Dm2dFysv.d.mts.map → index-UzMbg1dh.d.mts.map} +1 -1
- package/dist/index.d.mts +5 -5
- package/dist/index.mjs +1 -1
- package/dist/layer-BttmtDrs.mjs.map +1 -1
- package/dist/multithread-xUUh4eLn.mjs.map +1 -1
- package/dist/option/index.d.mts +1 -1
- package/dist/option/index.mjs +1 -1
- package/dist/{option-Qt1H-u7c.mjs → option-Tfbo4wty.mjs} +2 -2
- package/dist/option-Tfbo4wty.mjs.map +1 -0
- package/dist/option.types-D1mm0zUb.mjs +2 -0
- package/dist/option.types-D1mm0zUb.mjs.map +1 -0
- package/dist/{option.types-DlAb6Sr0.d.mts → option.types-qPevEZQd.d.mts} +5 -10
- package/dist/option.types-qPevEZQd.d.mts.map +1 -0
- package/dist/order-D5c4QChk.mjs.map +1 -1
- package/dist/provide/index.mjs +1 -1
- package/dist/{provide-B_SqJpCd.mjs → provide-C2cWOx2B.mjs} +2 -2
- package/dist/provide-C2cWOx2B.mjs.map +1 -0
- package/dist/queue/index.d.mts +1 -1
- package/dist/queue/index.mjs +1 -1
- package/dist/{queue-CG5izEBS.mjs → queue-Sg6KJerl.mjs} +2 -2
- package/dist/{queue-CG5izEBS.mjs.map → queue-Sg6KJerl.mjs.map} +1 -1
- package/dist/result-BEzV0DYC.mjs.map +1 -1
- package/dist/result.types-_xDAei3-.d.mts.map +1 -1
- package/dist/scope/index.mjs +1 -1
- package/dist/{scope-qwL1VUh2.mjs → scope-D_kzd1nT.mjs} +2 -2
- package/dist/scope-D_kzd1nT.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/data.tagged-error.types-CLlJJ3n5.d.mts +0 -50
- package/dist/data.tagged-error.types-CLlJJ3n5.d.mts.map +0 -1
- package/dist/either-6BwadiFj.mjs.map +0 -1
- package/dist/fx-BzxLbf1Q.mjs.map +0 -1
- package/dist/option-Qt1H-u7c.mjs.map +0 -1
- package/dist/option.types-DRUm2QiI.mjs +0 -2
- package/dist/option.types-DRUm2QiI.mjs.map +0 -1
- package/dist/option.types-DlAb6Sr0.d.mts.map +0 -1
- package/dist/provide-B_SqJpCd.mjs.map +0 -1
- package/dist/scope-qwL1VUh2.mjs.map +0 -1
package/dist/fx-BzxLbf1Q.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"fx-BzxLbf1Q.mjs","names":["gen","result","makeQueue","gen","result","resultOk","resultErr","resultTry","optionFromNullable"],"sources":["../src/fx/foreach.ts","../src/fx/fx.ts"],"sourcesContent":["import { NoSuchElementError } from \"../option/option.types\"\nimport { make as makeQueue, QueueAbortedError, QueueClosedError, QueueTaskAbortedError } from \"../queue/queue\"\nimport type { Concurrency } from \"../queue/queue.types\"\nimport { asServiceRequest } from \"../service/service-resolution\"\nimport { dual } from \"../shared/dual\"\nimport { FxTypeId } from \"./fx.types\"\nimport type { AsyncFx, FxError, FxRequirements, FxValue, FxYield, RunnableFx } from \"./fx.types\"\n\n/* oxlint-disable no-await-in-loop -- Fx.forEach coordinates child iterators and queue scheduling sequentially at specific yield points. */\n\ntype ForEachValue<Discard extends boolean, B> = Discard extends true ? void : Array<B>\n\ntype FxForEachOptions<Discard extends boolean = false> = {\n readonly concurrency?: Concurrency\n readonly discard?: Discard\n}\n\ntype ForEachFx<\n F extends (a: never, i: number) => RunnableFx<unknown, unknown, unknown>,\n Discard extends boolean,\n> = AsyncFx<ForEachValue<Discard, FxValue<ReturnType<F>>>, FxError<ReturnType<F>>, FxRequirements<ReturnType<F>>>\n\ntype FxForEach = {\n <A, F extends (a: A, i: number) => RunnableFx<unknown, unknown, unknown>, const Discard extends boolean = false>(\n self: Iterable<A>,\n f: F,\n options?: FxForEachOptions<Discard>,\n ): ForEachFx<F, Discard>\n <A, F extends (a: A, i: number) => RunnableFx<unknown, unknown, unknown>, const Discard extends boolean = false>(\n f: F,\n options?: FxForEachOptions<Discard>,\n ): (self: Iterable<A>) => ForEachFx<F, Discard>\n}\n\ntype ServiceRequestEntry<E, R> = {\n readonly yielded: FxYield<E, R>\n readonly signal: AbortSignal\n readonly resolve: (value: unknown) => void\n readonly reject: (reason: Error) => void\n}\n\ntype ResultErrShape = {\n readonly _tag: \"Err\"\n readonly error: unknown\n}\n\nclass TypedFailure<E> extends Error {\n readonly _tag = \"TypedFailure\"\n\n constructor(readonly error: E) {\n super(\"TypedFailure\")\n this.name = \"TypedFailure\"\n }\n}\n\nconst isTypedFailure = <E>(value: unknown): value is TypedFailure<E> => value instanceof TypedFailure\n\nconst isResultErr = (value: unknown): value is ResultErrShape =>\n typeof value === \"object\" && value !== null && \"_tag\" in value && value._tag === \"Err\" && \"error\" in value\n\nconst toFailureValue = (yielded: unknown): unknown => {\n if (yielded instanceof NoSuchElementError) {\n return yielded\n }\n\n if (isResultErr(yielded)) {\n return yielded.error\n }\n\n return yielded\n}\n\nconst isIterable = (value: unknown): value is Iterable<unknown> =>\n value !== null &&\n value !== undefined &&\n typeof (value as { [Symbol.iterator]?: unknown })[Symbol.iterator] === \"function\"\n\nconst queueTaskAbortedError = (signal: AbortSignal): QueueTaskAbortedError =>\n new QueueTaskAbortedError({ reason: signal.reason })\n\nconst toError = (reason: unknown): Error =>\n reason instanceof Error ? reason : new Error(\"Fx.forEach defect was not an Error\", { cause: reason })\n\nconst unreachableType = (): never => {\n throw new Error(\"Unreachable: Fx type marker should never execute at runtime\")\n}\n\nconst typeMarker = <A>(): (() => A) => unreachableType\n\nconst runMappedFx = async <B, E, R>(\n fx: RunnableFx<B, E, R>,\n signal: AbortSignal,\n requestService: (yielded: FxYield<E, R>, signal: AbortSignal) => Promise<unknown>,\n): Promise<B> => {\n if (fx._tag === \"AsyncFx\") {\n const gen = fx[Symbol.asyncIterator]()\n let result = await gen.next()\n\n while (result.done !== true) {\n if (signal.aborted) {\n throw queueTaskAbortedError(signal)\n }\n\n const yielded = result.value\n const serviceRequest = asServiceRequest(yielded)\n if (serviceRequest !== undefined) {\n const injected = await requestService(yielded, signal)\n result = await gen.next(injected)\n continue\n }\n\n // oxlint-disable-next-line typescript-eslint/no-unsafe-type-assertion -- failure channel values come from generic E at runtime.\n throw new TypedFailure<E>(toFailureValue(yielded) as E)\n }\n\n return result.value\n }\n\n const gen = fx[Symbol.iterator]()\n let result = gen.next()\n\n while (result.done !== true) {\n if (signal.aborted) {\n throw queueTaskAbortedError(signal)\n }\n\n const yielded = result.value\n const serviceRequest = asServiceRequest(yielded)\n if (serviceRequest !== undefined) {\n const injected = await requestService(yielded, signal)\n result = gen.next(injected)\n continue\n }\n\n // oxlint-disable-next-line typescript-eslint/no-unsafe-type-assertion -- failure channel values come from generic E at runtime.\n throw new TypedFailure<E>(toFailureValue(yielded) as E)\n }\n\n return result.value\n}\n\nconst fxForEach = dual(\n (args) => isIterable(args[0]) && typeof args[1] === \"function\",\n <A, B, E, R>(\n self: Iterable<A>,\n f: (a: A, i: number) => RunnableFx<B, E, R>,\n options?: FxForEachOptions<boolean>,\n ): AsyncFx<Array<B> | void, E, R> => {\n const items = [...self]\n const discard = options?.discard === true\n\n const program: AsyncFx<Array<B> | void, E, R> = {\n _tag: \"AsyncFx\",\n [FxTypeId]: {\n _A: typeMarker<Array<B> | void>(),\n _E: typeMarker<E>(),\n _R: typeMarker<R>(),\n },\n async *[Symbol.asyncIterator](): AsyncGenerator<FxYield<E, R>, Array<B> | void, unknown> {\n if (items.length === 0) {\n return discard ? undefined : []\n }\n\n const queue = makeQueue({ concurrency: options?.concurrency ?? \"unbounded\" })\n const results: Array<B> | undefined = discard ? undefined : []\n\n let remaining = items.length\n let firstFailure: E | undefined\n let firstDefect: Error | undefined\n let queueAborted = false\n\n const serviceRequests: ServiceRequestEntry<E, R>[] = []\n const eventWaiters = new Set<() => void>()\n\n const notifyEvent = () => {\n for (const resolve of eventWaiters) {\n resolve()\n }\n eventWaiters.clear()\n }\n\n const waitForEvent = () =>\n new Promise<void>((resolve) => {\n eventWaiters.add(resolve)\n })\n\n const requestService = (yielded: FxYield<E, R>, signal: AbortSignal): Promise<unknown> => {\n if (signal.aborted) {\n return Promise.reject(queueTaskAbortedError(signal))\n }\n\n return new Promise<unknown>((resolve, reject) => {\n const entry: ServiceRequestEntry<E, R> = {\n yielded,\n signal,\n resolve: (value) => {\n signal.removeEventListener(\"abort\", onAbort)\n resolve(value)\n },\n reject: (reason) => {\n signal.removeEventListener(\"abort\", onAbort)\n reject(reason)\n },\n }\n\n const onAbort = () => {\n const index = serviceRequests.indexOf(entry)\n if (index !== -1) {\n serviceRequests.splice(index, 1)\n }\n reject(queueTaskAbortedError(signal))\n }\n\n signal.addEventListener(\"abort\", onAbort, { once: true })\n serviceRequests.push(entry)\n notifyEvent()\n })\n }\n\n const rejectPendingServiceRequests = (reason: Error) => {\n const pending = serviceRequests.splice(0)\n for (const request of pending) {\n request.reject(reason)\n }\n }\n\n const abortQueue = (reason: unknown) => {\n if (queueAborted) {\n return\n }\n\n queueAborted = true\n void queue.shutdown({ mode: \"abort\", reason }).catch(() => {\n // no-op: fail-fast cleanup should never interrupt parent error handling\n })\n }\n\n const onTaskFailure = (error: unknown) => {\n if (isTypedFailure<E>(error)) {\n if (firstFailure === undefined && firstDefect === undefined) {\n firstFailure = error.error\n abortQueue(error.error)\n }\n return\n }\n\n if (\n (error instanceof QueueAbortedError ||\n error instanceof QueueTaskAbortedError ||\n error instanceof QueueClosedError) &&\n (firstFailure !== undefined || firstDefect !== undefined)\n ) {\n return\n }\n\n if (firstDefect === undefined && firstFailure === undefined) {\n const defect = toError(error)\n firstDefect = defect\n abortQueue(defect)\n }\n }\n\n const onTaskSettled = () => {\n remaining -= 1\n notifyEvent()\n }\n\n for (const [index, item] of items.entries()) {\n void queue\n .enqueue(async ({ signal }) => {\n const value = await runMappedFx(f(item, index), signal, requestService)\n if (results !== undefined) {\n results[index] = value\n }\n })\n .catch(onTaskFailure)\n .finally(onTaskSettled)\n }\n\n while (true) {\n if (firstDefect !== undefined) {\n rejectPendingServiceRequests(firstDefect)\n throw new Error(\"Fx.forEach defect\", { cause: firstDefect })\n }\n\n if (firstFailure !== undefined) {\n rejectPendingServiceRequests(new QueueAbortedError({ reason: firstFailure }))\n yield firstFailure\n throw new Error(\"Unreachable: Fx runtime short-circuits on first failure\")\n }\n\n const request = serviceRequests.shift()\n if (request !== undefined) {\n if (request.signal.aborted) {\n request.reject(queueTaskAbortedError(request.signal))\n continue\n }\n\n try {\n const injected = yield request.yielded\n request.resolve(injected)\n } catch (error) {\n request.reject(toError(error))\n }\n continue\n }\n\n if (remaining === 0) {\n break\n }\n\n await waitForEvent()\n }\n\n return discard ? undefined : results\n },\n }\n\n return program\n },\n) as FxForEach\n\nexport { fxForEach as forEach }\n\n/* oxlint-enable no-await-in-loop */\n","/**\n * Lightweight effect runtime with typed success, error, and requirement channels.\n *\n * **Mental model**\n * - `Fx<A, E, R>` models computations that may fail and require services.\n * - Build with `Fx.gen`, run with `Fx.run`, and compose with `Layer`/`Provide`.\n *\n * **Common tasks**\n * - Create workflows with `Fx.gen`.\n * - Execute with `Fx.run` or `Fx.fn`.\n * - Retry with `Fx.retry` and match outcomes with `Fx.match`.\n *\n * **Gotchas**\n * - Dependencies (`R`) must be provided before execution.\n * - `Fx.run` returns `Exit`, not raw values.\n *\n * **Quickstart**\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * const program = Fx.gen(function* () {\n * const value = yield* Fx.ok(2)\n * return value + 1\n * })\n * const exit = Fx.run(program)\n * // => { _tag: \"Ok\", value: 3 }\n * ```\n *\n * @module\n */\nimport { fromNullable as optionFromNullable } from \"../option/option\"\nimport { ok as resultOk, err as resultErr, fromTry as resultTry } from \"../result/result\"\nimport type { RetrySchedule, SyncRetrySchedule } from \"../schedule/schedule\"\nimport { asServiceRequest } from \"../service/service-resolution\"\nimport { dual } from \"../shared/dual\"\nimport type { Exit as ExitType } from \"./exit\"\nimport { forEach as fxForEach } from \"./foreach\"\nimport { runSync, runAsync } from \"./fx.runtime\"\nimport { FxTypeId } from \"./fx.types\"\nimport type { RunnableFx, SyncFx, AsyncFx, FxYield, Fx as FxProtocol, FxGenerator, AsyncFxGenerator } from \"./fx.types\"\nexport {\n /**\n * `Exit` utilities namespace for `Fx.run` outcomes.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * const exit = Fx.Exit.ok(1)\n * // => { _tag: \"Ok\", value: 1 }\n * ```\n *\n * @category Re-exports\n */\n Exit,\n} from \"./exit\"\nexport {\n /**\n * Type guard for Fx service requests.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * const request = { _tag: \"ServiceRequest\", serviceKey: \"Logger\" } as Fx.ServiceRequest<unknown>\n * const isRequest = Fx.isServiceRequest(request)\n * // => true\n * ```\n *\n * @category Guards\n */\n isServiceRequest,\n} from \"./fx.types\"\nexport {\n /**\n * Runtime unique symbol identifying Fx values.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * const marker = { [Fx.FxTypeId]: true }\n * // => Object.getOwnPropertySymbols(marker).includes(Fx.FxTypeId)\n * ```\n *\n * @category Re-exports\n */\n FxTypeId,\n} from \"./fx.types\"\nexport {\n /**\n * Re-exported core Fx protocol type.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Fx.Fx<unknown, unknown, unknown>\n * ```\n *\n * @category Re-exports\n */\n type Fx,\n} from \"./fx.types\"\nexport {\n /**\n * Re-exported synchronous Fx type.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Fx.SyncFx<unknown, unknown, unknown>\n * ```\n *\n * @category Re-exports\n */\n type SyncFx,\n} from \"./fx.types\"\nexport {\n /**\n * Re-exported asynchronous Fx type.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Fx.AsyncFx<unknown, unknown, unknown>\n * ```\n *\n * @category Re-exports\n */\n type AsyncFx,\n} from \"./fx.types\"\nexport {\n /**\n * Re-exported runnable Fx union type.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Fx.RunnableFx<unknown, unknown, unknown>\n * ```\n *\n * @category Re-exports\n */\n type RunnableFx,\n} from \"./fx.types\"\nexport {\n /**\n * Re-exported service-request protocol type.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Fx.ServiceRequest<unknown>\n * ```\n *\n * @category Re-exports\n */\n type ServiceRequest,\n} from \"./fx.types\"\nexport {\n /**\n * Re-exported helper extracting Fx success type.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Fx.FxValue<Fx.SyncFx<number>>\n * ```\n *\n * @category Re-exports\n */\n type FxValue,\n} from \"./fx.types\"\nexport {\n /**\n * Re-exported helper extracting Fx error type.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Fx.FxError<Fx.SyncFx<number, string>>\n * ```\n *\n * @category Re-exports\n */\n type FxError,\n} from \"./fx.types\"\nexport {\n /**\n * Re-exported helper extracting Fx requirements type.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Fx.FxRequirements<Fx.SyncFx<number, never, { readonly svc: unknown }>>\n * ```\n *\n * @category Re-exports\n */\n type FxRequirements,\n} from \"./fx.types\"\nexport {\n /**\n * Re-exported generator yield union for Fx internals.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Fx.FxYield<unknown, unknown>\n * ```\n *\n * @category Re-exports\n */\n type FxYield,\n} from \"./fx.types\"\nexport {\n /**\n * Re-exported sync generator signature used by `Fx.gen`.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Fx.FxGenerator<unknown, unknown, unknown>\n * ```\n *\n * @category Re-exports\n */\n type FxGenerator,\n} from \"./fx.types\"\nexport {\n /**\n * Re-exported async generator signature used by `Fx.gen`.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Fx.AsyncFxGenerator<unknown, unknown, unknown>\n * ```\n *\n * @category Re-exports\n */\n type AsyncFxGenerator,\n} from \"./fx.types\"\nexport {\n /**\n * Re-exported sync Fx yieldable protocol type.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Fx.FxYieldable<unknown, unknown, unknown>\n * ```\n *\n * @category Re-exports\n */\n type FxYieldable,\n} from \"./fx.types\"\nexport {\n /**\n * Re-exported async Fx yieldable protocol type.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Fx.AsyncFxYieldable<unknown, unknown, unknown>\n * ```\n *\n * @category Re-exports\n */\n type AsyncFxYieldable,\n} from \"./fx.types\"\n\n/* oxlint-disable no-unsafe-type-assertion, no-unsafe-assignment, no-unsafe-call, no-unsafe-member-access, no-unsafe-return, no-unsafe-argument, no-unnecessary-type-assertion -- Fx runtime adapters intentionally traverse erased generator channels and rehydrate typed effects through controlled dynamic boundaries. */\n\n// ============================================================================\n// Fx.run - Execute computations\n// ============================================================================\n\n/**\n * Execute a sync Fx computation and return the Exit.\n * Catches unexpected thrown exceptions and wraps them as Defect.\n * Only available when R = never (all dependencies provided).\n *\n * @param fx - The sync computation to execute\n * @returns Exit<A, E>\n */\nfunction fxRun<A, E>(fx: SyncFx<A, E>): ExitType<A, E>\n\n/**\n * Execute an async Fx computation and return a Promise of the Exit.\n * Catches unexpected thrown exceptions and wraps them as Defect.\n * Only available when R = never (all dependencies provided).\n *\n * @param fx - The async computation to execute\n * @returns Promise<Exit<A, E>>\n */\nfunction fxRun<A, E>(fx: AsyncFx<A, E>): Promise<ExitType<A, E>>\n\n/**\n * Execute an Fx computation (union type).\n * Returns Exit for sync, Promise<Exit> for async.\n * Catches unexpected thrown exceptions and wraps them as Defect.\n */\nfunction fxRun<A, E>(fx: RunnableFx<A, E>): ExitType<A, E> | Promise<ExitType<A, E>>\n\n/**\n * Implementation of Fx.run.\n * Detects sync vs async and executes appropriately.\n */\nfunction fxRun<A, E>(fx: RunnableFx<A, E>): ExitType<A, E> | Promise<ExitType<A, E>> {\n if (fx._tag === \"AsyncFx\") {\n return runAsync(fx)\n }\n return runSync(fx)\n}\n\n// ============================================================================\n// Fx.retry - Retry failed computations\n// ============================================================================\n\nconst sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => {\n setTimeout(resolve, ms)\n })\n\nconst sleepIfNeeded = async (ms: number): Promise<void> => {\n if (ms <= 0) {\n return\n }\n await sleep(ms)\n}\n\nconst closeSyncAttempt = (gen: Generator<unknown, unknown, unknown>) => {\n if (typeof gen.return === \"function\") {\n gen.return(undefined)\n }\n}\n\nconst closeAsyncAttempt = async (\n gen: Generator<unknown, unknown, unknown> | AsyncGenerator<unknown, unknown, unknown>,\n) => {\n if (typeof gen.return === \"function\") {\n await gen.return(undefined)\n }\n}\n\nconst makeSyncRetryFx = <A, E, R>(fx: SyncFx<A, E, R>, schedule: SyncRetrySchedule): SyncFx<A, E, R> => ({\n _tag: \"SyncFx\",\n [FxTypeId]: {\n _A: () => undefined as unknown as A,\n _E: () => undefined as unknown as E,\n _R: () => undefined as unknown as R,\n },\n *[Symbol.iterator](): Generator<FxYield<E, R>, A, unknown> {\n let retryAttempt = 0\n attemptLoop: while (true) {\n const gen = fx[Symbol.iterator]()\n let result = gen.next()\n\n while (result.done !== true) {\n const yielded = result.value\n const serviceRequest = asServiceRequest(yielded)\n if (serviceRequest !== undefined) {\n const injected = yield yielded as FxYield<E, R>\n result = gen.next(injected)\n continue\n }\n\n retryAttempt += 1\n if (retryAttempt <= schedule.maxRetries) {\n closeSyncAttempt(gen)\n continue attemptLoop\n }\n\n // Forward the terminal typed failure to the outer runtime.\n yield yielded as FxYield<E, R>\n return undefined as never\n }\n\n return result.value\n }\n },\n})\n\n/* oxlint-disable no-await-in-loop -- retry execution is intentionally sequential across attempts and iterator steps. */\nconst makeAsyncRetryFx = <A, E, R>(fx: RunnableFx<A, E, R>, schedule: RetrySchedule): AsyncFx<A, E, R> => ({\n _tag: \"AsyncFx\",\n [FxTypeId]: {\n _A: () => undefined as unknown as A,\n _E: () => undefined as unknown as E,\n _R: () => undefined as unknown as R,\n },\n async *[Symbol.asyncIterator](): AsyncGenerator<FxYield<E, R>, A, unknown> {\n let retryAttempt = 0\n\n attemptLoop: while (true) {\n if (fx._tag === \"AsyncFx\") {\n const gen = fx[Symbol.asyncIterator]()\n // Sequential async iterator protocol: each step depends on the previous one.\n let result = await gen.next()\n\n while (result.done !== true) {\n const yielded = result.value\n const serviceRequest = asServiceRequest(yielded)\n if (serviceRequest !== undefined) {\n const injected = yield yielded as FxYield<E, R>\n // oxlint-disable-next-line no-await-in-loop\n result = await gen.next(injected)\n continue\n }\n\n retryAttempt += 1\n if (retryAttempt <= schedule.maxRetries) {\n const delayMs = schedule.delayForAttempt(retryAttempt)\n await sleepIfNeeded(delayMs)\n await closeAsyncAttempt(gen)\n continue attemptLoop\n }\n\n // Forward the terminal typed failure to the outer runtime.\n yield yielded as FxYield<E, R>\n return undefined as never\n }\n\n return result.value\n }\n\n const gen = fx[Symbol.iterator]()\n let result = gen.next()\n\n while (result.done !== true) {\n const yielded = result.value\n const serviceRequest = asServiceRequest(yielded)\n if (serviceRequest !== undefined) {\n const injected = yield yielded as FxYield<E, R>\n result = gen.next(injected)\n continue\n }\n\n retryAttempt += 1\n if (retryAttempt <= schedule.maxRetries) {\n const delayMs = schedule.delayForAttempt(retryAttempt)\n await sleepIfNeeded(delayMs)\n await closeAsyncAttempt(gen)\n continue attemptLoop\n }\n\n // Forward the terminal typed failure to the outer runtime.\n yield yielded as FxYield<E, R>\n return undefined as never\n }\n\n return result.value\n }\n },\n})\n/* oxlint-enable no-await-in-loop */\n\n/**\n * Retry a computation according to the given schedule.\n *\n * Retries only typed failures (Err channel). Defects are not retried.\n */\ntype RetryResult<Fx extends RunnableFx<unknown, unknown, unknown>, S extends RetrySchedule> =\n Fx extends AsyncFx<infer A, infer E, infer R>\n ? AsyncFx<A, E, R>\n : S extends SyncRetrySchedule\n ? Fx extends SyncFx<infer A, infer E, infer R>\n ? SyncFx<A, E, R>\n : never\n : Fx extends SyncFx<infer A, infer E, infer R>\n ? AsyncFx<A, E, R>\n : never\n\nconst fxRetry: {\n <Fx extends RunnableFx<unknown, unknown, unknown>, S extends RetrySchedule>(fx: Fx, schedule: S): RetryResult<Fx, S>\n <S extends RetrySchedule>(\n schedule: S,\n ): <Fx extends RunnableFx<unknown, unknown, unknown>>(fx: Fx) => RetryResult<Fx, S>\n} = dual(\n 2,\n <Fx extends RunnableFx<unknown, unknown, unknown>, S extends RetrySchedule>(\n fx: Fx,\n schedule: S,\n ): RetryResult<Fx, S> => {\n if (fx._tag === \"AsyncFx\" || !schedule._sync) {\n return makeAsyncRetryFx(fx, schedule) as RetryResult<Fx, S>\n }\n return makeSyncRetryFx(fx as SyncFx<unknown, unknown, unknown>, schedule as SyncRetrySchedule) as RetryResult<Fx, S>\n },\n)\n\n// ============================================================================\n// Fx.gen - Create composable computations\n// ============================================================================\n\ntype FxGenError<Yielded> = Yielded extends FxProtocol<unknown, infer E, unknown> ? E : Yielded\ntype FxGenRequirements<Yielded> = Yielded extends FxProtocol<unknown, unknown, infer R> ? R : never\n\n/**\n * Create a composable computation from a sync generator function.\n * Returns a SyncFx that can be stored, passed around, and executed later.\n *\n * @template Yielded - The type of values yielded by the generator\n * @template A - The success value type\n * @param generatorFn - A function that returns a sync generator\n * @returns SyncFx<A, FxGenError<Yielded>, FxGenRequirements<Yielded>>\n *\n * @example\n * ```ts\n * const getUser = (id: string) => Fx.gen(function* () {\n * const db = yield* Database\n * const logger = yield* Logger\n *\n * logger.info(`Fetching user ${id}`)\n * return yield* db.query<User>(`SELECT * FROM users WHERE id = '${id}'`)\n * })\n * // Type: SyncFx<User[], DatabaseError, Database | Logger>\n *\n * // Execute later with dependencies provided\n * const exit = pipe(getUser(\"123\"), Provide.layer(AppLayer)).run()\n * ```\n */\nfunction fxGen<Yielded, A>(\n generatorFn: () => Generator<Yielded, A, unknown>,\n): SyncFx<A, FxGenError<Yielded>, FxGenRequirements<Yielded>>\n\n/**\n * Create a composable computation from an async generator function.\n * Returns an AsyncFx that can be stored, passed around, and executed later.\n *\n * @template Yielded - The type of values yielded by the generator\n * @template A - The success value type\n * @param generatorFn - A function that returns an async generator\n * @returns AsyncFx<A, FxGenError<Yielded>, FxGenRequirements<Yielded>>\n *\n * @example\n * ```ts\n * const fetchData = (url: string) => Fx.gen(async function* () {\n * const logger = yield* Logger\n *\n * logger.info(`Fetching ${url}`)\n * const response = await fetch(url)\n * return yield* Result.try(async () => await response.json())\n * })\n * // Type: AsyncFx<unknown, Error, Logger>\n * ```\n */\nfunction fxGen<Yielded, A>(\n generatorFn: () => AsyncGenerator<Yielded, A, unknown>,\n): AsyncFx<A, FxGenError<Yielded>, FxGenRequirements<Yielded>>\n\n/**\n * Create a composable computation from a sync generator function that takes parameters.\n * Returns a factory function that, when called with parameters, produces a SyncFx.\n *\n * @template P - The parameter types (must be non-empty)\n * @template Yielded - The type of values yielded by the generator\n * @template A - The success value type\n * @param generatorFn - A function that takes parameters and returns a sync generator\n * @returns A factory function that produces SyncFx<A, FxGenError<Yielded>, FxGenRequirements<Yielded>>\n *\n * @example\n * ```ts\n * const getUser = Fx.gen(function* ({userId}: {userId: string}) {\n * const db = yield* Database\n * return yield* db.query(`SELECT * FROM users WHERE id = '${userId}'`)\n * })\n * // Type: (params: {userId: string}) => SyncFx<...>\n *\n * const exit = Fx.run(pipe(getUser({userId: \"123\"}), Provide.layer(DbLayer)))\n * ```\n */\nfunction fxGen<P extends [unknown, ...unknown[]], Yielded, A>(\n generatorFn: (...params: P) => Generator<Yielded, A, unknown>,\n): (...params: P) => SyncFx<A, FxGenError<Yielded>, FxGenRequirements<Yielded>>\n\n/**\n * Create a composable computation from an async generator function that takes parameters.\n * Returns a factory function that, when called with parameters, produces an AsyncFx.\n *\n * @template P - The parameter types (must be non-empty)\n * @template Yielded - The type of values yielded by the generator\n * @template A - The success value type\n * @param generatorFn - A function that takes parameters and returns an async generator\n * @returns A factory function that produces AsyncFx<A, FxGenError<Yielded>, FxGenRequirements<Yielded>>\n *\n * @example\n * ```ts\n * const fetchUser = Fx.gen(async function* ({url}: {url: string}) {\n * const response = await fetch(url)\n * return yield* Result.try(async () => await response.json())\n * })\n * // Type: (params: {url: string}) => AsyncFx<...>\n * ```\n */\nfunction fxGen<P extends [unknown, ...unknown[]], Yielded, A>(\n generatorFn: (...params: P) => AsyncGenerator<Yielded, A, unknown>,\n): (...params: P) => AsyncFx<A, FxGenError<Yielded>, FxGenRequirements<Yielded>>\n\n/**\n * Implementation of unified Fx.gen function.\n * Detects sync vs async generator and returns appropriate computation type.\n * For parameterized generators (length > 0), returns a factory function.\n */\n// oxlint-disable-next-line no-explicit-any\nfunction fxGen(generatorFn: (...args: any[]) => any): any {\n if (generatorFn.length > 0) {\n return (...params: unknown[]) => fxGen(() => generatorFn(...params))\n }\n\n // Check if we're dealing with async generator\n const testGen = generatorFn()\n const isAsync = Symbol.asyncIterator in testGen\n\n if (isAsync) {\n return {\n _tag: \"AsyncFx\",\n [FxTypeId]: {\n _A: () => undefined,\n _E: () => undefined,\n _R: () => undefined,\n },\n // oxlint-disable-next-line no-explicit-any\n async *[Symbol.asyncIterator](): AsyncGenerator<any, any, unknown> {\n const gen = generatorFn()\n let result = await gen.next()\n\n while (result.done !== true) {\n // Pass through yields (errors or services as Fx values)\n const injected = yield result.value\n // oxlint-disable-next-line no-await-in-loop\n result = await gen.next(injected)\n }\n\n return result.value\n },\n }\n }\n\n // Sync generator\n return {\n _tag: \"SyncFx\",\n [FxTypeId]: {\n _A: () => undefined,\n _E: () => undefined,\n _R: () => undefined,\n },\n // oxlint-disable-next-line no-explicit-any\n *[Symbol.iterator](): Generator<any, any, unknown> {\n const gen = generatorFn()\n let result = gen.next()\n\n while (result.done !== true) {\n const injected = yield result.value\n result = gen.next(injected)\n }\n\n return result.value\n },\n }\n}\n\n// ============================================================================\n// Fx.fn - Execute computations immediately\n// ============================================================================\n\ntype FxRunResult<F extends RunnableFx<unknown, unknown, unknown>> =\n F extends AsyncFx<infer A, infer E, unknown>\n ? Promise<ExitType<A, E>>\n : F extends SyncFx<infer A, infer E, unknown>\n ? ExitType<A, E>\n : never\n\n/**\n * Execute a sync computation immediately and return the Exit.\n * Catches unexpected thrown exceptions and wraps them as Defect.\n * For computations without service dependencies.\n *\n * @template A - The success value type\n * @template E - The error type\n * @param generatorFn - A function that returns a sync generator\n * @returns Exit<A, E>\n *\n * @example\n * ```ts\n * const exit = Fx.fn(function* () {\n * const a = yield* Result.ok(10)\n * const b = yield* Result.ok(20)\n * return a + b\n * })\n * // Type: Exit<number, never>\n * // exit = { _tag: \"Ok\", value: 30 }\n * ```\n */\nfunction fxFn<A, E>(generatorFn: () => FxGenerator<A, E, never>): ExitType<A, E>\n\n/**\n * Execute an async computation immediately and return a Promise of the Exit.\n * Catches unexpected thrown exceptions and wraps them as Defect.\n * For computations without service dependencies.\n *\n * @template A - The success value type\n * @template E - The error type\n * @param generatorFn - A function that returns an async generator\n * @returns Promise<Exit<A, E>>\n *\n * @example\n * ```ts\n * const exit = await Fx.fn(async function* () {\n * const response = await fetch(\"/api/users\")\n * const data = yield* Result.try(async () => await response.json())\n * return data\n * })\n * // Type: Promise<Exit<unknown, Error>>\n * ```\n */\nfunction fxFn<A, E>(generatorFn: () => AsyncFxGenerator<A, E, never>): Promise<ExitType<A, E>>\n\n/**\n * Execute a sync computation with service dependencies immediately.\n * Catches unexpected thrown exceptions and wraps them as Defect.\n * The second argument is required when the computation has service requirements (R ≠ never).\n *\n * @template A - The success value type\n * @template E - The error type from the computation\n * @template R - The required services type\n * @param generatorFn - A function that returns a sync generator with service requirements\n * @param provider - A function that provides the services (e.g., Provide.layer(AppLayer))\n * @returns Exit result from running the provided Fx (sync or async depending on provider output)\n *\n * @example\n * ```ts\n * const exit = Fx.fn(function* () {\n * const config = yield* Config\n * const logger = yield* Logger\n * logger.info(`DB URL: ${config.dbUrl}`)\n * return config.dbUrl\n * }, Provide.layer(AppLayer))\n * // Type: Exit<string, never>\n * ```\n */\nfunction fxFn<A, E, R, ProvidedFx extends RunnableFx<unknown, unknown, unknown>>(\n generatorFn: () => FxGenerator<A, E, R>,\n provider: (fx: SyncFx<A, E, R>) => ProvidedFx,\n): FxRunResult<ProvidedFx>\n\n/**\n * Execute an async computation with service dependencies immediately.\n * Catches unexpected thrown exceptions and wraps them as Defect.\n * The second argument is required when the computation has service requirements (R ≠ never).\n *\n * @template A - The success value type\n * @template E - The error type from the computation\n * @template R - The required services type\n * @param generatorFn - A function that returns an async generator with service requirements\n * @param provider - A function that provides the services (e.g., Provide.layer(AppLayer))\n * @returns Exit result from running the provided Fx (sync or async depending on provider output)\n *\n * @example\n * ```ts\n * const exit = await Fx.fn(async function* () {\n * const config = yield* Config\n * await doAsyncWork()\n * return config.dbUrl\n * }, Provide.layer(AppLayer))\n * // Type: Promise<Exit<string, never>>\n * ```\n */\nfunction fxFn<A, E, R, ProvidedFx extends RunnableFx<unknown, unknown, unknown>>(\n generatorFn: () => AsyncFxGenerator<A, E, R>,\n provider: (fx: AsyncFx<A, E, R>) => ProvidedFx,\n): FxRunResult<ProvidedFx>\n\n/**\n * Implementation of Fx.fn.\n * Reuses fxGen() for Fx creation and fxRun() for execution to ensure consistent behavior.\n */\nfunction fxFn<A, E, E2, R>(\n generatorFn: () => FxGenerator<A, E, R> | AsyncFxGenerator<A, E, R>,\n provider?: ((fx: SyncFx<A, E, R>) => RunnableFx<A, E | E2>) | ((fx: AsyncFx<A, E, R>) => RunnableFx<A, E | E2>),\n): ExitType<A, E | E2> | Promise<ExitType<A, E | E2>> {\n // Create the Fx using fxGen (single source of truth for Fx creation)\n // Type cast needed because fxGen infers E/R from yielded Fx values,\n // while fxFn receives E/R as explicit type parameters\n // oxlint-disable-next-line no-explicit-any\n const fx = fxGen(generatorFn as any) as RunnableFx<A, E, R>\n\n if (provider) {\n // Apply provider and run\n const provided =\n fx._tag === \"AsyncFx\"\n ? (provider as (fx: AsyncFx<A, E, R>) => RunnableFx<A, E | E2>)(fx)\n : (provider as (fx: SyncFx<A, E, R>) => RunnableFx<A, E | E2>)(fx)\n return fxRun(provided as RunnableFx<A, E | E2>) as ExitType<A, E | E2> | Promise<ExitType<A, E | E2>>\n }\n\n // No provider - run directly (R must be never at the type level)\n return fxRun(fx as RunnableFx<A, E | E2>) as ExitType<A, E | E2> | Promise<ExitType<A, E | E2>>\n}\n\n// ============================================================================\n// Fx.ok / Fx.err / Fx.option - Convenience aliases\n// ============================================================================\n\n/**\n * Convenience alias for Result.ok.\n * Creates a successful Result that can be yielded in an Fx computation.\n *\n * @param value - The success value\n * @returns Result<T, never>\n *\n * @example\n * ```ts\n * const workflow = Fx.gen(function* () {\n * const value = yield* Fx.ok(42)\n * return value * 2\n * })\n * ```\n */\nconst fxOk = resultOk\n\n/**\n * Convenience alias for Result.err.\n * Creates an error Result that can be yielded in an Fx computation.\n *\n * @param error - The error value\n * @returns Result<never, E>\n *\n * @example\n * ```ts\n * const workflow = Fx.gen(function* () {\n * const config = yield* Config\n *\n * if (!config.dbUrl) {\n * return yield* Fx.err(new ValidationError({ field: \"dbUrl\" }))\n * }\n *\n * return config.dbUrl\n * })\n * ```\n */\nconst fxErr = resultErr\n\n/**\n * Convenience alias for Result.try.\n * Creates a Result that can be yielded in an Fx computation.\n *\n * @param fn - A function that returns a value or throws an error\n * @returns Result<T, E>\n *\n * @example\n * ```ts\n * const result = Fx.try(() => {\n * return 42\n * })\n *\n * const workflow = Fx.gen(function* () {\n * const result = yield* Fx.try(() => {\n * return 42\n * })\n * return result\n * })\n * ```\n */\nconst fxTry = resultTry\n\n/**\n * Convenience alias for Option.fromNullable.\n * Converts nullable values into an Option that can be yielded in an Fx computation.\n *\n * @param value - A possibly nullish value\n * @returns Option<NonNullable<T>>\n *\n * @example\n * ```ts\n * const workflow = Fx.gen(function* () {\n * const apiKey = yield* Fx.option(request.headers[\"x-api-key\"])\n * return apiKey\n * })\n * ```\n */\nconst fxOption = optionFromNullable\n\n// ============================================================================\n// Fx.match - Universal pattern matching\n// ============================================================================\n\n/**\n * Handler functions for matching on a tagged union by `_tag` discriminator.\n * Each handler receives the narrowed variant corresponding to its tag key.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Fx\n * ```\n *\n * @category Type-level\n */\nexport type TaggedMatch<T extends { readonly _tag: string }, R> = {\n readonly [K in T[\"_tag\"]]: (variant: Extract<T, { readonly _tag: K }>) => R\n}\n\n/**\n * Generic pattern matcher for any `_tag`-based discriminated union.\n *\n * Works with Exit, tagged errors, or any type with a `readonly _tag` discriminator.\n * Each handler receives the full narrowed variant object for its tag.\n *\n * Supports both data-first and data-last (pipe) calling styles.\n *\n * @example\n * ```ts\n * // Match on Exit\n * Fx.match(exit, {\n * Ok: (ok) => ok.value,\n * Err: (err) =>\n * Fx.match(err.error, {\n * DatabaseError: (e) => e.message,\n * NotFoundError: (e) => e.resourceId,\n * }),\n * Defect: (d) => String(d.defect),\n * })\n *\n * // Data-last with pipe\n * pipe(exit, Fx.match({\n * Ok: (ok) => ok.value,\n * Err: (err) => \"error\",\n * Defect: (d) => \"defect\",\n * }))\n * ```\n */\nconst fxMatch: {\n <T extends { readonly _tag: string }, R>(value: T, handlers: TaggedMatch<T, R>): R\n <T extends { readonly _tag: string }, R>(handlers: TaggedMatch<T, R>): (value: T) => R\n} = dual(\n 2,\n // oxlint-disable-next-line no-explicit-any\n (value: any, handlers: any): any => handlers[value._tag](value),\n)\n\n// ============================================================================\n// Exports\n// ============================================================================\n\n/**\n * Build typed computations from generator functions.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * const program = Fx.gen(function* () {\n * const value = yield* Fx.ok(2)\n * return value + 1\n * })\n * const exit = Fx.run(program)\n * // => { _tag: \"Ok\", value: 3 }\n * ```\n *\n * @category Constructors\n */\nexport const gen = fxGen\n\n/**\n * Run a generator function immediately and return its `Exit`.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * const exit = Fx.fn(function* () {\n * return yield* Fx.ok(2)\n * })\n * // => { _tag: \"Ok\", value: 2 }\n * ```\n *\n * @category Utilities\n */\nexport const fn = fxFn\n\n/**\n * Execute an existing `Fx` value and produce an `Exit`.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * const exit = Fx.run(Fx.gen(function* () {\n * return 1\n * }))\n * // => { _tag: \"Ok\", value: 1 }\n * ```\n *\n * @category Utilities\n */\nexport const run = fxRun\n\n/**\n * Retry failed computations according to a schedule.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * import { Schedule } from \"@nicolastoulemont/std\"\n * \n * const retried = Fx.retry(\n * Fx.gen(function* () {\n * return yield* Fx.err(\"boom\")\n * }),\n * Schedule.recurs(2),\n * )\n * const exit = Fx.run(retried)\n * // => { _tag: \"Err\", error: \"boom\" }\n * ```\n *\n * @category Error Handling\n */\nexport const retry = fxRetry\n\n/**\n * Run an effectful function for each item in a collection.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * const fx = Fx.forEach([1, 2, 3], (n) => Fx.gen(function* () {\n * return n * 2\n * }))\n * // => AsyncFx<readonly number[], ...>\n * ```\n *\n * @category Sequencing\n */\nexport const forEach = fxForEach\n\n/**\n * Alias for `Result.ok` in `Fx` namespace.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * const value = Fx.ok(42)\n * // => { _tag: \"Ok\", value: 42 }\n * ```\n *\n * @category Constructors\n */\nexport const ok = fxOk\n\n/**\n * Alias for `Result.err` in `Fx` namespace.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * const value = Fx.err(\"boom\")\n * // => { _tag: \"Err\", error: \"boom\" }\n * ```\n *\n * @category Constructors\n */\nexport const err = fxErr\n\n/**\n * Alias for `Option.fromNullable` in `Fx` namespace.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * const value = Fx.option(\"token\")\n * // => { _tag: \"Some\", value: \"token\" }\n * ```\n *\n * @category Conversions\n */\nexport const option = fxOption\n\nconst try_ = fxTry\n\nexport {\n /**\n * Alias for `Result.fromTry` in `Fx` namespace.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * const parsed = Fx.try(() => Number.parseInt(\"42\", 10))\n * // => { _tag: \"Ok\", value: 42 }\n * ```\n *\n * @category Constructors\n */\n try_ as try,\n}\n\n/**\n * Pattern matching helper for tagged unions.\n *\n * @example\n * ```ts\n * import { Fx } from \"@nicolastoulemont/std\"\n *\n * const value = Fx.match({ _tag: \"Ok\", value: 2 } as const, {\n * Ok: (ok) => ok.value + 1,\n * })\n * // => 3\n * ```\n *\n * @category Pattern Matching\n */\nexport const match = fxMatch\n\n/* oxlint-enable no-unsafe-type-assertion, no-unsafe-assignment, no-unsafe-call, no-unsafe-member-access, no-unsafe-return, no-unsafe-argument, no-unnecessary-type-assertion */\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"option-Qt1H-u7c.mjs","names":["NoSuchElementErrorCtor","values"],"sources":["../src/option/option.ts"],"sourcesContent":["/**\n * Optional value primitives for modeling presence (`Some`) and absence (`None`).\n *\n * **Mental model**\n * - `Option<A>` is either a value or no value.\n * - Prefer `Option` when missing data is expected and not exceptional.\n *\n * **Common tasks**\n * - Construct with `Option.some`, `Option.none`, `Option.fromNullable`.\n * - Transform with `Option.map`, `Option.flatMap`, `Option.filter`.\n * - Extract with `Option.unwrapOr`, `Option.match`.\n *\n * **Gotchas**\n * - `Option.none` short-circuits in `Fx.gen`.\n * - Use `Option.match` for exhaustive branch handling.\n *\n * **Quickstart**\n *\n * @example\n * ```ts\n * import { Option } from \"@nicolastoulemont/std\"\n *\n * const name = Option.fromNullable(\"Ada\")\n * const upper = Option.map(name, (value) => value.toUpperCase())\n * ```\n *\n * @module\n */\nimport { FxTypeId } from \"../fx/fx.types\"\nimport { dual } from \"../shared/dual\"\nimport { isPromise } from \"../shared/is-promise\"\nimport { pipeMethod } from \"../shared/pipeable\"\nimport { NoSuchElementError as NoSuchElementErrorCtor } from \"./option.types\"\nimport type {\n Option as OptionType,\n OptionAll,\n OptionFlatMap,\n OptionMap,\n OptionMatch,\n OptionTap,\n OptionOrElse,\n OptionFilter,\n OptionUnwrapOr,\n OptionUnwrapOrElse,\n FromTryReturn,\n} from \"./option.types\"\n\n/**\n * Re-exported error type used when yielding `Option.none()` in `Fx.gen`.\n *\n * @example\n * ```ts\n * import { Option } from \"@nicolastoulemont/std\"\n *\n * const error = new Option.NoSuchElementError()\n * // => error.name === \"NoSuchElementError\"\n * ```\n *\n * @category Re-exports\n */\nexport const NoSuchElementError = NoSuchElementErrorCtor\n\n/**\n * Re-exported `NoSuchElementError` instance type.\n *\n * @example\n * ```ts\n * import type { Option } from \"@nicolastoulemont/std\"\n *\n * type Example = Option.NoSuchElementError\n * ```\n *\n * @category Re-exports\n */\nexport type NoSuchElementError = InstanceType<typeof NoSuchElementErrorCtor>\n\n/**\n * Re-exported `Option` type.\n *\n * @example\n * ```ts\n * import type { Option } from \"@nicolastoulemont/std\"\n *\n * type Example = Option.Option<unknown>\n * ```\n *\n * @category Re-exports\n */\nexport type Option<T> = OptionType<T>\n\n/* oxlint-disable no-unsafe-type-assertion -- Option runtime values carry erased Fx phantom channels that require explicit marker assertions. */\n\n// ============================================================================\n// Constructors\n// ============================================================================\n\n/**\n * Create an Option with a value.\n *\n * @param value - The value to wrap\n * @returns An Option with `_tag: \"Some\"`\n *\n * @example\n * ```ts\n * import { Option } from \"@nicolastoulemont/std\"\n *\n * const value = Option.some(42)\n * // => { _tag: \"Some\", value: 42 }\n * ```\n *\n * @category Constructors\n */\nexport const some = <T>(value: T): OptionType<T> => ({\n _tag: \"Some\",\n value,\n [FxTypeId]: {\n _A: () => value,\n _E: () => undefined as never,\n _R: () => undefined as never,\n },\n pipe: pipeMethod,\n // oxlint-disable-next-line require-yield\n *[Symbol.iterator](): Generator<NoSuchElementError, T, unknown> {\n return value\n },\n})\n\n/**\n * Create an Option with no value.\n *\n * @returns An Option with `_tag: \"None\"`\n *\n * @example\n * ```ts\n * import { Option } from \"@nicolastoulemont/std\"\n *\n * const value = Option.none<number>()\n * // => { _tag: \"None\" }\n * ```\n *\n * @category Constructors\n */\nexport const none = <T = never>(): OptionType<T> => ({\n _tag: \"None\",\n [FxTypeId]: {\n _A: () => undefined as never,\n _E: () => undefined as unknown as NoSuchElementError,\n _R: () => undefined as never,\n },\n pipe: pipeMethod,\n *[Symbol.iterator](): Generator<NoSuchElementError, never, unknown> {\n yield new NoSuchElementError()\n throw new Error(\"Unreachable: Fx.gen should short-circuit on NoSuchElementError\")\n },\n})\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\n/**\n * Check if an Option has a value.\n *\n * @param option - The Option to check\n * @returns true if the Option is some\n *\n * @example\n * ```ts\n * import { Option } from \"@nicolastoulemont/std\"\n *\n * const value = Option.isSome(Option.some(1))\n * // => true\n * ```\n *\n * @category Guards\n */\nexport const isSome = <T>(option: OptionType<T>): option is Extract<OptionType<T>, { _tag: \"Some\" }> =>\n option._tag === \"Some\"\n\n/**\n * isNone utility.\n *\n * @example\n * ```ts\n * import { Option } from \"@nicolastoulemont/std\"\n *\n * const value = Option.isNone(Option.none())\n * // => true\n * ```\n *\n * @category Guards\n */\nexport const isNone = <T>(option: OptionType<T>): option is Extract<OptionType<T>, { _tag: \"None\" }> =>\n option._tag === \"None\"\n\n// ============================================================================\n// Transformations (curried for pipe)\n// ============================================================================\n\n/**\n * Transform the value of an Option.\n * If the Option is none, it passes through unchanged.\n *\n * Supports both data-first and data-last calling styles:\n * - Data-first: `map(option, fn)`\n * - Data-last: `pipe(option, map(fn))`\n *\n * Supports both sync and async functions:\n * - Sync fn: returns Option<U>\n * - Async fn: returns Promise<Option<U>>\n *\n * @param fn - Function to transform the value\n * @returns A function that takes an Option and returns a new Option\n *\n * @example\n * ```ts\n * import { Option } from \"@nicolastoulemont/std\"\n *\n * const input = Option.some(2)\n * const dataFirst = Option.map(input, (n) => n + 1)\n * // => { _tag: \"Some\", value: 3 }\n * \n * const dataLast = Option.map((n: number) => n + 1)(input)\n * // => { _tag: \"Some\", value: 3 }\n * ```\n *\n * @category Mapping\n */\n/* oxlint-disable no-explicit-any, no-unsafe-return, no-unsafe-type-assertion -- Required for overloaded return types in curried functions */\nexport const map: OptionMap = dual(2, (option: OptionType<unknown>, fn: (value: unknown) => unknown) => {\n if (option._tag === \"None\") return option as any\n const mapped = fn(option.value)\n if (isPromise(mapped)) {\n return Promise.resolve(mapped).then(some) as any\n }\n return some(mapped) as any\n})\n/* oxlint-enable no-explicit-any, no-unsafe-return, no-unsafe-type-assertion */\n\n/**\n * Chain operations that return Options.\n * If the Option is none, it passes through unchanged.\n *\n * Supports both data-first and data-last calling styles:\n * - Data-first: `flatMap(option, fn)`\n * - Data-last: `pipe(option, flatMap(fn))`\n *\n * Supports both sync and async functions:\n * - Sync fn: returns Option<U>\n * - Async fn: returns Promise<Option<U>>\n *\n * @param fn - Function that takes a value and returns an Option (or Promise<Option>)\n * @returns A function that takes an Option and returns a new Option\n *\n * @example\n * ```ts\n * import { Option } from \"@nicolastoulemont/std\"\n *\n * const input = Option.some(2)\n * const dataFirst = Option.flatMap(input, (n) => Option.some(n + 1))\n * // => { _tag: \"Some\", value: 3 }\n * \n * const dataLast = Option.flatMap((n: number) => Option.some(n + 1))(input)\n * // => { _tag: \"Some\", value: 3 }\n * ```\n *\n * @category Sequencing\n */\n// oxlint-disable-next-line no-explicit-any, no-unsafe-return, no-unsafe-type-assertion -- Required for overloaded return types\nexport const flatMap: OptionFlatMap = dual(2, (option: OptionType<unknown>, fn: (value: unknown) => unknown) =>\n option._tag === \"Some\" ? fn(option.value) : option,\n)\n\n/**\n * Perform a side effect on the value without modifying the Option.\n * Useful for debugging, logging, or other side effects in a pipeline.\n * If the Option is none, the function is not called.\n *\n * Supports both data-first and data-last calling styles:\n * - Data-first: `tap(option, fn)`\n * - Data-last: `pipe(option, tap(fn))`\n *\n * Supports both sync and async functions:\n * - Sync fn: returns Option<T>\n * - Async fn: returns Promise<Option<T>>\n *\n * @param fn - Function to call with the value (return value is ignored)\n * @returns A function that takes an Option and returns the same Option\n *\n * @example\n * ```ts\n * import { Option } from \"@nicolastoulemont/std\"\n *\n * let seen = 0\n * const input = Option.some(2)\n * const dataFirst = Option.tap(input, (n) => {\n * seen = n\n * })\n * // => { _tag: \"Some\", value: 2 }\n * \n * const dataLast = Option.tap((n: number) => {\n * seen = n\n * })(input)\n * // => { _tag: \"Some\", value: 2 }\n * ```\n *\n * @category Sequencing\n */\n/* oxlint-disable no-explicit-any, no-unsafe-return, no-unsafe-type-assertion -- Required for overloaded return types in curried functions */\nexport const tap: OptionTap = dual(2, (option: OptionType<unknown>, fn: (value: unknown) => unknown) => {\n if (option._tag === \"None\") return option as any\n const sideEffect = fn(option.value)\n if (isPromise(sideEffect)) {\n return Promise.resolve(sideEffect).then(() => option) as any\n }\n return option as any\n})\n/* oxlint-enable no-explicit-any, no-unsafe-return, no-unsafe-type-assertion */\n\n/**\n * Recover from none by providing an alternative Option.\n * If the Option is some, it passes through unchanged.\n *\n * Supports both data-first and data-last calling styles:\n * - Data-first: `orElse(option, fn)`\n * - Data-last: `pipe(option, orElse(fn))`\n *\n * Supports both sync and async functions:\n * - Sync fn: returns Option<T | U>\n * - Async fn: returns Promise<Option<T | U>>\n *\n * @param fn - Function that returns an alternative Option\n * @returns A function that takes an Option and returns a new Option\n *\n * @example\n * ```ts\n * import { Option } from \"@nicolastoulemont/std\"\n *\n * const input = Option.none<number>()\n * const dataFirst = Option.orElse(input, () => Option.some(0))\n * // => { _tag: \"Some\", value: 0 }\n * \n * const dataLast = Option.orElse(() => Option.some(0))(input)\n * // => { _tag: \"Some\", value: 0 }\n * ```\n *\n * @category Combining\n */\n// oxlint-disable-next-line no-explicit-any, no-unsafe-return, no-unsafe-type-assertion -- Required for overloaded return types\nexport const orElse: OptionOrElse = dual(2, (option: OptionType<unknown>, fn: () => unknown) =>\n option._tag === \"Some\" ? option : fn(),\n)\n\n/**\n * Filter an Option based on a predicate.\n * If the predicate returns false, converts to none.\n * If the Option is already none, it passes through unchanged.\n *\n * Supports both data-first and data-last calling styles:\n * - Data-first: `filter(option, predicate)`\n * - Data-last: `pipe(option, filter(predicate))`\n *\n * @param predicate - Function that returns true to keep the value\n * @returns A function that takes an Option and returns a new Option\n *\n * @example\n * ```ts\n * import { Option } from \"@nicolastoulemont/std\"\n *\n * const input = Option.some(3)\n * const dataFirst = Option.filter(input, (n) => n > 0)\n * // => { _tag: \"Some\", value: 3 }\n * \n * const dataLast = Option.filter((n: number) => n > 0)(input)\n * // => { _tag: \"Some\", value: 3 }\n * ```\n *\n * @category Filtering\n */\nexport const filter: OptionFilter = dual(2, (option: OptionType<unknown>, predicate: (value: unknown) => boolean) => {\n if (option._tag === \"None\") return option\n return predicate(option.value) ? option : none()\n})\n\n// ============================================================================\n// Combinators\n// ============================================================================\n\n/**\n * Combine multiple Options into a single Option.\n * Supports both array and object inputs.\n *\n * - If all Options are some, returns some with all values\n * - If any Option is none, returns none (short-circuits)\n *\n * For arrays of 1-6 elements, tuple types are inferred automatically.\n * For longer arrays, use `as const` to preserve tuple structure.\n *\n * @example\n * ```ts\n * import { Option } from \"@nicolastoulemont/std\"\n *\n * const combined = Option.all([Option.some(1), Option.some(2)] as const)\n * // => { _tag: \"Some\", value: [1, 2] }\n * ```\n *\n * @category Combining\n */\n/* oxlint-disable no-explicit-any, no-unsafe-return, no-unsafe-member-access, strict-boolean-expressions, no-unsafe-type-assertion, no-unsafe-argument -- Required for handling union types in overloaded function */\nexport const all: OptionAll = (options: any): any => {\n if (Array.isArray(options)) {\n const values: unknown[] = []\n for (const option of options) {\n if (option._tag === \"None\") return option\n values.push(option.value)\n }\n return some(values)\n }\n\n const values: Record<string, unknown> = {}\n for (const [key, option] of Object.entries(options)) {\n const o = option as OptionType<unknown>\n if (o._tag === \"None\") return option\n values[key] = o.value\n }\n return some(values)\n}\n/* oxlint-enable no-explicit-any, no-unsafe-return, no-unsafe-member-access, strict-boolean-expressions, no-unsafe-type-assertion */\n\n// ============================================================================\n// Extraction\n// ============================================================================\n\n/**\n * Get the value or a default value.\n *\n * Supports both data-first and data-last calling styles:\n * - Data-first: `unwrapOr(option, defaultValue)`\n * - Data-last: `pipe(option, unwrapOr(defaultValue))`\n *\n * Uses NoInfer to prevent type inference from the default value.\n *\n * @param defaultValue - Value to return if the Option is none\n * @returns A function that takes an Option and returns the value or default\n *\n * @example\n * ```ts\n * import { Option } from \"@nicolastoulemont/std\"\n *\n * const input = Option.none<number>()\n * const dataFirst = Option.unwrapOr(input, 0)\n * // => 0\n * \n * const dataLast = Option.unwrapOr(0)(input)\n * // => 0\n * ```\n *\n * @category Getters\n */\nexport const unwrapOr: OptionUnwrapOr = dual(\n 2,\n <T>(option: OptionType<T>, defaultValue: NoInfer<T>): T => (option._tag === \"Some\" ? option.value : defaultValue),\n)\n\n/**\n * Get the value or compute a value.\n *\n * Supports both data-first and data-last calling styles:\n * - Data-first: `unwrapOrElse(option, fn)`\n * - Data-last: `pipe(option, unwrapOrElse(fn))`\n *\n * @param fn - Function to compute a value\n * @returns A function that takes an Option and returns the value or computed value\n *\n * @example\n * ```ts\n * import { Option } from \"@nicolastoulemont/std\"\n *\n * const input = Option.none<number>()\n * const dataFirst = Option.unwrapOrElse(input, () => 0)\n * // => 0\n *\n * const dataLast = Option.unwrapOrElse(() => 0)(input)\n * // => 0\n * ```\n *\n * @category Getters\n */\nexport const unwrapOrElse: OptionUnwrapOrElse = dual(\n 2,\n <T>(option: OptionType<T>, fn: () => T): T => (option._tag === \"Some\" ? option.value : fn()),\n)\n\n/**\n * Pattern match on an Option, handling both some and none cases.\n *\n * Supports both data-first and data-last calling styles:\n * - Data-first: `match(option, { Some: ..., None: ... })`\n * - Data-last: `pipe(option, match({ Some: ..., None: ... }))`\n *\n * @param handlers - Object with `Some` and `None` handlers\n * @returns A function that takes an Option and returns the handler result\n *\n * @example\n * ```ts\n * import { Option } from \"@nicolastoulemont/std\"\n *\n * const input = Option.some(2)\n * const dataFirst = Option.match(input, {\n * Some: (value) => `some:${value}` ,\n * None: () => \"none\" ,\n * })\n * // => \"some:2\"\n * \n * const dataLast = Option.match({\n * Some: (value: number) => `some:${value}` ,\n * None: () => \"none\" ,\n * })(input)\n * // => \"some:2\"\n * ```\n *\n * @category Pattern Matching\n */\nexport const match: OptionMatch = dual(\n 2,\n <T, U>(option: OptionType<T>, handlers: { Some: (value: T) => U; None: () => U }): U =>\n option._tag === \"Some\" ? handlers.Some(option.value) : handlers.None(),\n)\n\n// ============================================================================\n// Conversions\n// ============================================================================\n\n/**\n * Convert a nullable value to an Option.\n * `null` and `undefined` become `Option.none()`, other values become `Option.some(value)`.\n * The return type narrows to `Option<NonNullable<T>>`.\n *\n * @param value - The value to convert\n * @returns An Option\n *\n * @example\n * ```ts\n * fromNullable(42) // { _tag: \"Some\", value: 42 }\n * fromNullable(null) // { _tag: \"None\" }\n * fromNullable(undefined) // { _tag: \"None\" }\n *\n * const maybeName = \"Alice\" as string | undefined\n * const option = fromNullable(maybeName) // Option<string>\n * ```\n */\nconst isNonNullable = <T>(value: T): value is NonNullable<T> => value !== null && value !== undefined\n\n/**\n * fromNullable utility.\n *\n * @example\n * ```ts\n * import { Option } from \"@nicolastoulemont/std\"\n *\n * const value = Option.fromNullable(\"Ada\")\n * // => { _tag: \"Some\", value: \"Ada\" }\n * ```\n *\n * @category Conversions\n */\nexport const fromNullable = <T>(value: T): OptionType<NonNullable<T>> => (isNonNullable(value) ? some(value) : none())\n\n/**\n * Wrap a function that might throw into an Option.\n * Supports both sync and async functions with automatic type inference.\n *\n * - Sync fn: returns Option<T>\n * - Async fn: returns Promise<Option<T>>\n *\n * **Note on `any` return types:** If your function returns `any` (e.g., `JSON.parse`),\n * you should add an explicit return type annotation. This is because TypeScript's\n * conditional types cannot determine if `any` is a Promise or not, resulting in a\n * union type `Promise<Option<unknown>> | Option<any>` that won't work\n * with Option utilities like `isSome` or `isNone`.\n *\n * @param fn - Function that might throw (sync or async)\n * @returns An Option with the return value or none\n *\n * @example\n * ```ts\n * import { Option } from \"@nicolastoulemont/std\"\n *\n * const parsed = Option.fromTry(() => Number.parseInt(\"42\", 10))\n * // => { _tag: \"Some\", value: 42 }\n * ```\n *\n * @category Conversions\n */\n/* oxlint-disable no-explicit-any, no-unsafe-type-assertion -- Required for conditional return type */\nexport const fromTry = <T>(fn: () => T): FromTryReturn<T> => {\n try {\n const result = fn()\n if (isPromise(result)) {\n return Promise.resolve(result)\n .then(some)\n .catch(() => none()) as unknown as FromTryReturn<T>\n }\n return some(result) as unknown as FromTryReturn<T>\n } catch {\n return none() as unknown as FromTryReturn<T>\n }\n}\n\n/* oxlint-enable no-unsafe-type-assertion */\n"],"mappings":"qeA4DA,MAAa,EAAqBA,EAoDrB,EAAW,IAA6B,CACnD,KAAM,OACN,SACC,GAAW,CACV,OAAU,EACV,OAAU,IAAA,GACV,OAAU,IAAA,GACX,CACD,KAAM,EAEN,EAAE,OAAO,WAAuD,CAC9D,OAAO,GAEV,EAiBY,OAAwC,CACnD,KAAM,QACL,GAAW,CACV,OAAU,IAAA,GACV,OAAU,IAAA,GACV,OAAU,IAAA,GACX,CACD,KAAM,EACN,EAAE,OAAO,WAA2D,CAElE,MADA,MAAM,IAAI,EACA,MAAM,iEAAiE,EAEpF,EAsBY,EAAa,GACxB,EAAO,OAAS,OAeL,EAAa,GACxB,EAAO,OAAS,OAoCL,EAAiB,EAAK,GAAI,EAA6B,IAAoC,CACtG,GAAI,EAAO,OAAS,OAAQ,OAAO,EACnC,IAAM,EAAS,EAAG,EAAO,MAAM,CAI/B,OAHI,EAAU,EAAO,CACZ,QAAQ,QAAQ,EAAO,CAAC,KAAK,EAAK,CAEpC,EAAK,EAAO,EACnB,CAiCW,EAAyB,EAAK,GAAI,EAA6B,IAC1E,EAAO,OAAS,OAAS,EAAG,EAAO,MAAM,CAAG,EAC7C,CAsCY,EAAiB,EAAK,GAAI,EAA6B,IAAoC,CACtG,GAAI,EAAO,OAAS,OAAQ,OAAO,EACnC,IAAM,EAAa,EAAG,EAAO,MAAM,CAInC,OAHI,EAAU,EAAW,CAChB,QAAQ,QAAQ,EAAW,CAAC,SAAW,EAAO,CAEhD,GACP,CAiCW,EAAuB,EAAK,GAAI,EAA6B,IACxE,EAAO,OAAS,OAAS,EAAS,GAAI,CACvC,CA4BY,EAAuB,EAAK,GAAI,EAA6B,IACpE,EAAO,OAAS,QACb,EAAU,EAAO,MAAM,CADK,EACO,GAAM,CAChD,CA2BW,EAAkB,GAAsB,CACnD,GAAI,MAAM,QAAQ,EAAQ,CAAE,CAC1B,IAAMC,EAAoB,EAAE,CAC5B,IAAK,IAAM,KAAU,EAAS,CAC5B,GAAI,EAAO,OAAS,OAAQ,OAAO,EACnC,EAAO,KAAK,EAAO,MAAM,CAE3B,OAAO,EAAKA,EAAO,CAGrB,IAAM,EAAkC,EAAE,CAC1C,IAAK,GAAM,CAAC,EAAK,KAAW,OAAO,QAAQ,EAAQ,CAAE,CACnD,IAAM,EAAI,EACV,GAAI,EAAE,OAAS,OAAQ,OAAO,EAC9B,EAAO,GAAO,EAAE,MAElB,OAAO,EAAK,EAAO,EAkCR,EAA2B,EACtC,GACI,EAAuB,IAAiC,EAAO,OAAS,OAAS,EAAO,MAAQ,EACrG,CA0BY,EAAmC,EAC9C,GACI,EAAuB,IAAoB,EAAO,OAAS,OAAS,EAAO,MAAQ,GAAI,CAC5F,CAgCY,EAAqB,EAChC,GACO,EAAuB,IAC5B,EAAO,OAAS,OAAS,EAAS,KAAK,EAAO,MAAM,CAAG,EAAS,MAAM,CACzE,CAwBK,EAAoB,GAAsC,GAAU,KAe7D,EAAmB,GAA0C,EAAc,EAAM,CAAG,EAAK,EAAM,CAAG,GAAM,CA6BxG,EAAc,GAAkC,CAC3D,GAAI,CACF,IAAM,EAAS,GAAI,CAMnB,OALI,EAAU,EAAO,CACZ,QAAQ,QAAQ,EAAO,CAC3B,KAAK,EAAK,CACV,UAAY,GAAM,CAAC,CAEjB,EAAK,EAAO,MACb,CACN,OAAO,GAAM"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./fx.types-Bg-Mmdm5.mjs";var t=class extends Error{_tag=`NoSuchElementError`;[e]={_A:()=>void 0,_E:()=>this,_R:()=>void 0};constructor(){super(`Option is None - no element present`),this.name=`NoSuchElementError`}};export{t};
|
|
2
|
-
//# sourceMappingURL=option.types-DRUm2QiI.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"option.types-DRUm2QiI.mjs","names":[],"sources":["../src/option/option.types.ts"],"sourcesContent":["import { FxTypeId } from \"../fx/fx.types\"\nimport type { Fx } from \"../fx/fx.types\"\nimport type { Pipeable } from \"../shared/pipeable\"\n\n/* oxlint-disable no-unsafe-type-assertion -- Option types expose Fx phantom channels through compile-time-only marker fields. */\n\n// ============================================================================\n// NoSuchElementError\n// ============================================================================\n\n/**\n * Error thrown when attempting to access a value from None.\n * Used as the error type when Option is yielded in Fx.gen().\n *\n * When you yield* Option.none() in Fx.gen(), the computation short-circuits\n * with this error, which becomes part of the error union type.\n *\n * Implements Fx protocol so it can be yielded in Fx.gen() directly.\n */\nexport class NoSuchElementError extends Error implements Fx<never, NoSuchElementError> {\n readonly _tag = \"NoSuchElementError\"\n\n readonly [FxTypeId] = {\n _A: () => undefined as never,\n _E: () => this as NoSuchElementError,\n _R: () => undefined as never,\n }\n\n constructor() {\n super(\"Option is None - no element present\")\n this.name = \"NoSuchElementError\"\n }\n}\n\n// ============================================================================\n// Option Variant Types\n// ============================================================================\n\n/**\n * Represents an Option with a value.\n */\ntype Some<T> = {\n readonly _tag: \"Some\"\n readonly value: T\n}\n\n/**\n * Represents an Option with no value.\n */\ntype None = {\n readonly _tag: \"None\"\n}\n\n// ============================================================================\n// Option Type\n// ============================================================================\n\n/**\n * A discriminated union representing either a value (Some) or absence (None).\n * Uses _tag discriminator for unified API compatibility.\n * Compatible with ts-pattern for exhaustive matching.\n *\n * Implements Fx protocol: Option<T> is Fx<T, NoSuchElementError, never>\n * - Yielding Some in Fx.gen() returns the value\n * - Yielding None in Fx.gen() short-circuits with NoSuchElementError\n *\n * When yielded in a generator, yields NoSuchElementError (which implements Fx)\n * for proper type inference in both gen() and Fx.gen().\n */\nexport type Option<T> = (Some<T> | None) &\n Fx<T, NoSuchElementError> &\n Pipeable & {\n [Symbol.iterator](): Generator<NoSuchElementError, T, unknown>\n }\n\n/**\n * Compute the return type of `fromTry` based on whether fn returns a Promise.\n * - If fn returns Promise<T>, result is Promise<Option<T>>\n * - If fn returns T, result is Option<T>\n */\nexport type FromTryReturn<T> = T extends Promise<infer U> ? Promise<Option<U>> : Option<T>\n\n// ============================================================================\n// All/Combine types\n// ============================================================================\n\n/**\n * Extract the value type from an Option.\n * Uses structural matching to extract the value type from the Some branch.\n */\ntype OptionValue<O> = O extends { readonly _tag: \"Some\"; readonly value: infer T } ? T : never\n\n/**\n * Overloaded all() type with explicit tuple arities for better inference.\n * Provides tuple overloads for 1-6 elements so `as const` is not required.\n */\nexport type OptionAll = {\n // Tuple overloads (1-6 elements)\n <A>(options: readonly [Option<A>]): Option<[A]>\n <A, B>(options: readonly [Option<A>, Option<B>]): Option<[A, B]>\n <A, B, C>(options: readonly [Option<A>, Option<B>, Option<C>]): Option<[A, B, C]>\n <A, B, C, D>(options: readonly [Option<A>, Option<B>, Option<C>, Option<D>]): Option<[A, B, C, D]>\n <A, B, C, D, E>(options: readonly [Option<A>, Option<B>, Option<C>, Option<D>, Option<E>]): Option<[A, B, C, D, E]>\n <A, B, C, D, E, F>(\n options: readonly [Option<A>, Option<B>, Option<C>, Option<D>, Option<E>, Option<F>],\n ): Option<[A, B, C, D, E, F]>\n\n // Array fallback (longer tuples need `as const`)\n <T extends readonly Option<unknown>[]>(options: T): Option<{ -readonly [K in keyof T]: OptionValue<T[K]> }>\n\n // Object form\n <T extends Record<string, Option<unknown>>>(options: T): Option<{ -readonly [K in keyof T]: OptionValue<T[K]> }>\n}\n\n// ============================================================================\n// Function Interfaces for improved pipe inference\n// ============================================================================\n\n/**\n * Interface for flatMap - using method syntax improves generic inference in pipe chains.\n * Supports both data-first and data-last (curried) calling styles via dual().\n * Note: Async overload must come first for proper overload resolution.\n */\nexport type OptionFlatMap = {\n // Data-first overloads (async first)\n <A, B>(option: Option<A>, fn: (value: A) => Promise<Option<B>>): Promise<Option<B>>\n <A, B>(option: Option<A>, fn: (value: A) => Option<B>): Option<B>\n // Data-last overloads (curried for pipe)\n <A, B>(fn: (value: A) => Promise<Option<B>>): (option: Option<A>) => Promise<Option<B>>\n <A, B>(fn: (value: A) => Option<B>): (option: Option<A>) => Option<B>\n}\n\n/**\n * Interface for map function.\n * Supports both data-first and data-last (curried) calling styles via dual().\n * Note: Async overload must come first for proper overload resolution.\n */\nexport type OptionMap = {\n // Data-first overloads (async first)\n <A, B>(option: Option<A>, fn: (value: A) => Promise<B>): Promise<Option<B>>\n <A, B>(option: Option<A>, fn: (value: A) => B): Option<B>\n // Data-last overloads (curried for pipe)\n <A, B>(fn: (value: A) => Promise<B>): (option: Option<A>) => Promise<Option<B>>\n <A, B>(fn: (value: A) => B): (option: Option<A>) => Option<B>\n}\n\n/* oxlint-enable no-unsafe-type-assertion */\n\n/**\n * Interface for tap function.\n * Supports both data-first and data-last (curried) calling styles via dual().\n */\nexport type OptionTap = {\n // Data-first overload\n <A, R>(option: Option<A>, fn: (value: A) => R): R extends Promise<unknown> ? Promise<Option<A>> : Option<A>\n // Data-last overload (curried for pipe)\n <A, R>(fn: (value: A) => R): (option: Option<A>) => R extends Promise<unknown> ? Promise<Option<A>> : Option<A>\n}\n\n/**\n * Interface for orElse function.\n * Supports both data-first and data-last (curried) calling styles via dual().\n * Note: Async overload must come first for proper overload resolution.\n */\nexport type OptionOrElse = {\n // Data-first overloads (async first)\n <A, B>(option: Option<A>, fn: () => Promise<Option<B>>): Promise<Option<A | B>>\n <A, B>(option: Option<A>, fn: () => Option<B>): Option<A | B>\n // Data-last overloads (curried for pipe)\n <A, B>(fn: () => Promise<Option<B>>): (option: Option<A>) => Promise<Option<A | B>>\n <A, B>(fn: () => Option<B>): (option: Option<A>) => Option<A | B>\n}\n\n/**\n * Interface for filter function.\n * Supports both data-first and data-last (curried) calling styles via dual().\n */\nexport type OptionFilter = {\n // Data-first overload\n <A>(option: Option<A>, predicate: (value: A) => boolean): Option<A>\n // Data-last overload (curried for pipe)\n <A>(predicate: (value: A) => boolean): (option: Option<A>) => Option<A>\n}\n\n/**\n * Interface for unwrapOr function.\n * Supports both data-first and data-last (curried) calling styles via dual().\n * Uses NoInfer to prevent type inference from the default value.\n */\nexport type OptionUnwrapOr = {\n // Data-first overload\n <T>(option: Option<T>, defaultValue: NoInfer<T>): T\n // Data-last overload (curried for pipe)\n <T>(defaultValue: NoInfer<T>): (option: Option<T>) => T\n}\n\n/**\n * Interface for unwrapOrElse function.\n * Supports both data-first and data-last (curried) calling styles via dual().\n */\nexport type OptionUnwrapOrElse = {\n // Data-first overload\n <T>(option: Option<T>, fn: () => T): T\n // Data-last overload (curried for pipe)\n <T>(fn: () => T): (option: Option<T>) => T\n}\n\n/**\n * Interface for match function.\n * Supports both data-first and data-last (curried) calling styles via dual().\n */\nexport type OptionMatch = {\n // Data-first overload\n <T, U>(option: Option<T>, handlers: { Some: (value: T) => U; None: () => U }): U\n // Data-last overload (curried for pipe)\n <T, U>(handlers: { Some: (value: T) => U; None: () => U }): (option: Option<T>) => U\n}\n"],"mappings":"4CAmBA,IAAa,EAAb,cAAwC,KAA+C,CACrF,KAAgB,qBAEhB,CAAU,GAAY,CACpB,OAAU,IAAA,GACV,OAAU,KACV,OAAU,IAAA,GACX,CAED,aAAc,CACZ,MAAM,sCAAsC,CAC5C,KAAK,KAAO"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"option.types-DlAb6Sr0.d.mts","names":[],"sources":["../src/option/option.types.ts"],"sourcesContent":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"provide-B_SqJpCd.mjs","names":["layer","service","makeScope","layer","fxGen","fxResult","service"],"sources":["../src/provide/memo-map.ts","../src/provide/provide.ts"],"sourcesContent":["import type { Context } from \"../context/context\"\nimport { FxTypeId } from \"../fx/fx.types\"\nimport type { RunnableFx, SyncFx, AsyncFx, FxYield } from \"../fx/fx.types\"\nimport type { Layer } from \"../layer/layer.types\"\nimport { ok } from \"../result/result\"\nimport type { ScopeService } from \"../scope/scope\"\nimport { asServiceRequest } from \"../service/service-resolution\"\n\n/* oxlint-disable no-unsafe-type-assertion, only-throw-error -- MemoMap bridges erased generic Fx channels and re-emits typed domain errors from generator boundaries. */\n\n// ============================================================================\n// MemoMap\n// ============================================================================\n\ntype AnyLayer = Layer<unknown, unknown, unknown>\n\n/**\n * MemoMap caches built layers so each layer is only built once.\n * Multiple dependents share the same service instance.\n *\n * This implements the \"service singleton\" pattern where a service\n * is instantiated once and shared across all consumers.\n *\n * @example\n * ```ts\n * // Database is built once, shared by DocumentService and TeamService\n * const AppLayer = Layer.merge(\n * ConfigLive,\n * LoggerLive,\n * DatabaseLive, // Built once\n * DocumentServiceLive, // Uses shared Database\n * TeamServiceLive, // Uses same Database instance\n * )\n * ```\n */\nexport class MemoMap {\n private cache = new Map<\n AnyLayer,\n {\n promise?: Promise<Context<unknown>>\n context?: Context<unknown>\n building?: boolean\n }\n >()\n\n /**\n * Get a cached context or build the layer if not cached.\n *\n * @param layer - The layer to get or build\n * @param scope - The scope for resource management\n * @param deps - Context containing the layer's dependencies\n * @returns A RunnableFx producing the layer's context\n */\n getOrBuild<ROut, E, RIn>(\n layer: Layer<ROut, E, RIn>,\n scope: ScopeService,\n deps: Context<RIn>,\n ): RunnableFx<Context<ROut>, E> {\n const cached = this.cache.get(layer as AnyLayer)\n\n // Already built - return cached context\n if (cached?.context) {\n return {\n _tag: \"SyncFx\",\n [FxTypeId]: {\n _A: () => cached.context as Context<ROut>,\n _E: () => undefined as unknown as E,\n _R: () => undefined as never,\n },\n run: () => ok(cached.context as Context<ROut>),\n // oxlint-disable-next-line require-yield\n *[Symbol.iterator](): Generator<FxYield<E, never>, Context<ROut>, unknown> {\n return cached.context as Context<ROut>\n },\n } as SyncFx<Context<ROut>, E>\n }\n\n // Building in progress (async) - wait for it\n if (cached?.promise) {\n return {\n _tag: \"AsyncFx\",\n [FxTypeId]: {\n _A: () => undefined as unknown as Context<ROut>,\n _E: () => undefined as unknown as E,\n _R: () => undefined as never,\n },\n run: async () => {\n const ctx = await cached.promise!\n return ok(ctx as Context<ROut>)\n },\n // oxlint-disable-next-line require-yield\n async *[Symbol.asyncIterator](): AsyncGenerator<FxYield<E, never>, Context<ROut>, unknown> {\n const ctx = await cached.promise!\n return ctx as Context<ROut>\n },\n } as AsyncFx<Context<ROut>, E>\n }\n\n // Need to build - mark as building\n this.cache.set(layer as AnyLayer, { building: true })\n\n // Build the layer with provided dependencies\n const buildFx = layer.build(this, scope)\n const cacheRef = this.cache\n\n // Determine if async\n if (buildFx._tag === \"AsyncFx\") {\n // Create a promise for the build\n const buildPromise = (async () => {\n const gen = buildFx[Symbol.asyncIterator]()\n let result = await gen.next()\n\n // Resolve service requests from deps\n while (result.done !== true) {\n const yielded = result.value\n const serviceRequest = asServiceRequest(yielded)\n if (serviceRequest === undefined) {\n // It's an error - rethrow\n throw yielded\n }\n const service = deps._services.get(serviceRequest.key)\n if (service === undefined) {\n throw new Error(`Service \"${serviceRequest.key}\" not found during layer build`)\n }\n // oxlint-disable-next-line no-await-in-loop\n result = await gen.next(service)\n }\n\n const ctx = result.value\n cacheRef.set(layer as AnyLayer, { context: ctx })\n return ctx\n })()\n\n this.cache.set(layer as AnyLayer, { promise: buildPromise })\n\n return {\n _tag: \"AsyncFx\",\n [FxTypeId]: {\n _A: () => undefined as unknown as Context<ROut>,\n _E: () => undefined as unknown as E,\n _R: () => undefined as never,\n },\n run: async () => {\n try {\n const ctx = await buildPromise\n return ok(ctx)\n } catch (e) {\n return {\n ok: false,\n error: e as E,\n *[Symbol.iterator]() {\n yield e as E\n throw new Error(\"Unreachable\")\n },\n }\n }\n },\n async *[Symbol.asyncIterator](): AsyncGenerator<FxYield<E, never>, Context<ROut>, unknown> {\n try {\n const ctx = await buildPromise\n return ctx\n } catch (e) {\n yield e as E\n throw new Error(\"Unreachable\", { cause: e })\n }\n },\n } as AsyncFx<Context<ROut>, E>\n }\n\n // Sync build\n return {\n _tag: \"SyncFx\",\n [FxTypeId]: {\n _A: () => undefined as unknown as Context<ROut>,\n _E: () => undefined as unknown as E,\n _R: () => undefined as never,\n },\n run: () => {\n const gen = buildFx[Symbol.iterator]()\n let result = gen.next()\n\n // Resolve service requests from deps\n while (result.done !== true) {\n const yielded = result.value\n const serviceRequest = asServiceRequest(yielded)\n if (serviceRequest === undefined) {\n // It's an error\n return {\n ok: false,\n error: yielded as E,\n *[Symbol.iterator]() {\n yield yielded as E\n throw new Error(\"Unreachable\")\n },\n }\n }\n const service = deps._services.get(serviceRequest.key)\n if (service === undefined) {\n throw new Error(`Service \"${serviceRequest.key}\" not found during layer build`)\n }\n result = gen.next(service)\n }\n\n const ctx = result.value\n cacheRef.set(layer as AnyLayer, { context: ctx })\n return ok(ctx)\n },\n *[Symbol.iterator](): Generator<FxYield<E, never>, Context<ROut>, unknown> {\n const gen = buildFx[Symbol.iterator]()\n let result = gen.next()\n\n // Resolve service requests from deps\n while (result.done !== true) {\n const yielded = result.value\n const serviceRequest = asServiceRequest(yielded)\n if (serviceRequest === undefined) {\n // It's an error - yield it\n yield yielded as E\n throw new Error(\"Unreachable\")\n }\n const service = deps._services.get(serviceRequest.key)\n if (service === undefined) {\n throw new Error(`Service \"${serviceRequest.key}\" not found during layer build`)\n }\n result = gen.next(service)\n }\n\n cacheRef.set(layer as AnyLayer, { context: result.value })\n return result.value\n },\n } as SyncFx<Context<ROut>, E>\n }\n\n /**\n * Clear the cache.\n * Useful for testing.\n */\n clear(): void {\n this.cache.clear()\n }\n\n /**\n * Get the number of cached layers.\n */\n get size(): number {\n return this.cache.size\n }\n}\n\n/* oxlint-enable no-unsafe-type-assertion, only-throw-error */\n","import type { Context } from \"../context/context\"\nimport { runSync, runAsync } from \"../fx/fx.runtime\"\nimport { FxTypeId } from \"../fx/fx.types\"\nimport type { RunnableFx, SyncFx, AsyncFx, FxYield } from \"../fx/fx.types\"\nimport type { Layer } from \"../layer/layer.types\"\nimport { ok } from \"../result/result\"\nimport { make as makeScope } from \"../scope/scope\"\nimport type { ServiceClass } from \"../service/service\"\nimport { asServiceRequest, resolveService } from \"../service/service-resolution\"\nimport { MemoMap } from \"./memo-map\"\n\n/* oxlint-disable no-unsafe-type-assertion -- layer/service re-thread generic Fx channels through runtime service injection points. */\n\n// ============================================================================\n// Provide Layer\n// ============================================================================\n\n/**\n * Extract the value type from an Fx.\n */\ntype FxValueType<F> = F extends RunnableFx<infer A, unknown, unknown> ? A : never\n\n/**\n * Extract the error type from an Fx.\n */\ntype FxErrorType<F> = F extends RunnableFx<unknown, infer E, unknown> ? E : never\n\n/**\n * Type for the result of provide based on layer sync and fx type.\n * Uses infer to extract A and E from the actual Fx type.\n * - Sync layer + SyncFx → SyncFx\n * - Sync layer + AsyncFx → AsyncFx\n * - Async layer + any fx → AsyncFx\n * - Unknown sync layer + any fx → RunnableFx (fallback)\n */\ntype ProvideResult<\n E2,\n LayerSync extends boolean,\n Fx extends RunnableFx<unknown, unknown, unknown>,\n> = LayerSync extends true\n ? Fx[\"_tag\"] extends \"SyncFx\"\n ? SyncFx<FxValueType<Fx>, FxErrorType<Fx> | E2>\n : AsyncFx<FxValueType<Fx>, FxErrorType<Fx> | E2>\n : LayerSync extends false\n ? AsyncFx<FxValueType<Fx>, FxErrorType<Fx> | E2>\n : RunnableFx<FxValueType<Fx>, FxErrorType<Fx> | E2>\n\n/**\n * Provide a layer to an Fx computation, resolving all service requirements.\n * Creates a new scope for resource management.\n *\n * Type preservation:\n * - Sync layer + SyncFx → SyncFx\n * - Sync layer + AsyncFx → AsyncFx\n * - Async layer + any fx → AsyncFx\n * - Unknown sync layer + any fx → RunnableFx (fallback)\n *\n * @param layer - The layer providing services (must have no external requirements)\n * @returns A function that takes an Fx and returns an Fx with dependencies resolved\n *\n * @example\n * ```ts\n * import { Provide } from \"@nicolastoulemont/std\"\n *\n * import { Fx, Layer, Service } from \"@nicolastoulemont/std\"\n * \n * const Port = Service.tag<number>(\"Port\")\n * const portLayer = Layer.ok(Port, 3000)\n * const readPort = Fx.gen(function* () {\n * return yield* Port\n * })\n * const provided = Provide.layer(portLayer)(readPort)\n * // => Fx with Port dependency resolved\n * ```\n *\n * @category Utilities\n */\nexport function layer<ROut, E2, LayerSync extends boolean>(\n layer: Layer<ROut, E2, never, LayerSync>,\n): <Fx extends RunnableFx<unknown, unknown, ROut>>(fx: Fx) => ProvideResult<E2, LayerSync, Fx>\nexport function layer<ROut, E2>(layer: Layer<ROut, E2>) {\n return <A, E, R extends ROut>(fx: RunnableFx<A, E, R>): RunnableFx<A, E | E2> => {\n // Determine if the layer or fx is async\n const memoMap = new MemoMap()\n const scope = makeScope()\n const layerBuildFx = layer.build(memoMap, scope)\n\n if (layerBuildFx._tag === \"AsyncFx\" || fx._tag === \"AsyncFx\") {\n return {\n _tag: \"AsyncFx\",\n [FxTypeId]: {\n _A: () => undefined as unknown as A,\n _E: () => undefined as unknown as E | E2,\n _R: () => undefined as never,\n },\n async *[Symbol.asyncIterator](): AsyncGenerator<FxYield<E | E2, Exclude<R, ROut>>, A, unknown> {\n try {\n // Build the layer\n let ctx: Context<ROut>\n\n if (layerBuildFx._tag === \"AsyncFx\") {\n const gen = layerBuildFx[Symbol.asyncIterator]()\n let result = await gen.next()\n\n while (result.done !== true) {\n yield result.value as E2\n // oxlint-disable-next-line no-await-in-loop\n result = await gen.next(undefined)\n }\n ctx = result.value\n } else {\n const gen = layerBuildFx[Symbol.iterator]()\n let result = gen.next()\n\n while (result.done !== true) {\n yield result.value as E2\n result = gen.next(undefined)\n }\n ctx = result.value\n }\n\n // Run the fx with the context\n if (fx._tag === \"AsyncFx\") {\n const fxGen = fx[Symbol.asyncIterator]()\n let fxResult = await fxGen.next()\n\n while (fxResult.done !== true) {\n const yielded = fxResult.value\n\n const service = resolveService(ctx, yielded)\n if (service !== undefined) {\n // oxlint-disable-next-line no-await-in-loop\n fxResult = await fxGen.next(service)\n continue\n }\n\n // Pass through (error or unsatisfied service)\n const value = yield yielded as FxYield<E, Exclude<R, ROut>>\n // oxlint-disable-next-line no-await-in-loop\n fxResult = await fxGen.next(value)\n }\n\n return fxResult.value\n }\n\n // Sync fx\n const fxGen = fx[Symbol.iterator]()\n let fxResult = fxGen.next()\n\n while (fxResult.done !== true) {\n const yielded = fxResult.value\n\n const service = resolveService(ctx, yielded)\n if (service !== undefined) {\n fxResult = fxGen.next(service)\n continue\n }\n\n const value = yield yielded as FxYield<E, Exclude<R, ROut>>\n fxResult = fxGen.next(value)\n }\n\n return fxResult.value\n } finally {\n const closeResult = scope.close(ok(undefined))\n if (closeResult._tag === \"AsyncFx\") {\n await runAsync(closeResult)\n } else {\n runSync(closeResult)\n }\n }\n },\n } as AsyncFx<A, E | E2>\n }\n\n // Both sync\n return {\n _tag: \"SyncFx\",\n [FxTypeId]: {\n _A: () => undefined as unknown as A,\n _E: () => undefined as unknown as E | E2,\n _R: () => undefined as never,\n },\n *[Symbol.iterator](): Generator<FxYield<E | E2, Exclude<R, ROut>>, A, unknown> {\n try {\n // Build the layer\n const layerGen = layerBuildFx[Symbol.iterator]()\n let layerResult = layerGen.next()\n\n while (layerResult.done !== true) {\n yield layerResult.value as E2\n layerResult = layerGen.next(undefined)\n }\n\n const ctx = layerResult.value\n\n // Run the fx with the context\n const fxGen = fx[Symbol.iterator]()\n let fxResult = fxGen.next()\n\n while (fxResult.done !== true) {\n const yielded = fxResult.value\n\n const service = resolveService(ctx, yielded)\n if (service !== undefined) {\n fxResult = fxGen.next(service)\n continue\n }\n\n const value = yield yielded as FxYield<E, Exclude<R, ROut>>\n fxResult = fxGen.next(value)\n }\n\n return fxResult.value\n } finally {\n const closeResult = scope.close(ok(undefined))\n // In sync-only branch, closeResult is always SyncFx\n runSync(closeResult as SyncFx<void>)\n }\n },\n } as SyncFx<A, E | E2>\n }\n}\n\n// ============================================================================\n// Provide Context\n// ============================================================================\n\n/**\n * Provide a context directly to an Fx computation.\n *\n * @param ctx - The context containing services\n * @returns A function that takes an Fx and returns an Fx with dependencies resolved\n *\n * @example\n * ```ts\n * import { Provide } from \"@nicolastoulemont/std\"\n *\n * import { Context, Fx, Service } from \"@nicolastoulemont/std\"\n * \n * const Port = Service.tag<number>(\"Port\")\n * const ctx = Context.make(Port, 3000)\n * const readPort = Fx.gen(function* () {\n * return yield* Port\n * })\n * const provided = Provide.context(ctx)(readPort)\n * // => Fx runnable without Port requirement\n * ```\n *\n * @category Utilities\n */\nexport const context =\n <R>(ctx: Context<R>) =>\n <A, E>(fx: RunnableFx<A, E, R>): RunnableFx<A, E> => {\n if (fx._tag === \"AsyncFx\") {\n return {\n _tag: \"AsyncFx\",\n [FxTypeId]: {\n _A: () => undefined as unknown as A,\n _E: () => undefined as unknown as E,\n _R: () => undefined as never,\n },\n async *[Symbol.asyncIterator](): AsyncGenerator<FxYield<E, never>, A, unknown> {\n const gen = fx[Symbol.asyncIterator]()\n let result = await gen.next()\n\n while (result.done !== true) {\n const yielded = result.value\n\n const serviceRequest = asServiceRequest(yielded)\n if (serviceRequest === undefined) {\n yield yielded as E\n // oxlint-disable-next-line no-await-in-loop\n result = await gen.next(undefined)\n continue\n }\n const service = ctx._services.get(serviceRequest.key)\n if (service === undefined) {\n throw new Error(`Service \"${serviceRequest.key}\" not found`)\n }\n // oxlint-disable-next-line no-await-in-loop\n result = await gen.next(service)\n }\n\n return result.value\n },\n } as AsyncFx<A, E>\n }\n\n return {\n _tag: \"SyncFx\",\n [FxTypeId]: {\n _A: () => undefined as unknown as A,\n _E: () => undefined as unknown as E,\n _R: () => undefined as never,\n },\n *[Symbol.iterator](): Generator<FxYield<E, never>, A, unknown> {\n const gen = fx[Symbol.iterator]()\n let result = gen.next()\n\n while (result.done !== true) {\n const yielded = result.value\n\n const serviceRequest = asServiceRequest(yielded)\n if (serviceRequest === undefined) {\n yield yielded as E\n result = gen.next(undefined)\n continue\n }\n const service = ctx._services.get(serviceRequest.key)\n if (service === undefined) {\n throw new Error(`Service \"${serviceRequest.key}\" not found`)\n }\n result = gen.next(service)\n }\n\n return result.value\n },\n } as SyncFx<A, E>\n }\n\n// ============================================================================\n// Provide Service\n// ============================================================================\n\n/**\n * Provide a single service to an Fx computation.\n *\n * @param service - The service class (tag)\n * @param impl - The service implementation\n * @returns A function that takes an Fx and returns an Fx with the service provided\n *\n * @example\n * ```ts\n * import { Provide } from \"@nicolastoulemont/std\"\n *\n * import { Fx, Service } from \"@nicolastoulemont/std\"\n * \n * const Port = Service.tag<number>(\"Port\")\n * const readPort = Fx.gen(function* () {\n * return yield* Port\n * })\n * const provided = Provide.service(Port, 3000)(readPort)\n * // => Fx runnable without Port requirement\n * ```\n *\n * @category Utilities\n */\nexport const service =\n <S>(service: ServiceClass<S>, impl: S) =>\n <A, E, R>(fx: RunnableFx<A, E, R | S>): RunnableFx<A, E, Exclude<R, S>> => {\n if (fx._tag === \"AsyncFx\") {\n return {\n _tag: \"AsyncFx\",\n [FxTypeId]: {\n _A: () => undefined as unknown as A,\n _E: () => undefined as unknown as E,\n _R: () => undefined as unknown as Exclude<R, S>,\n },\n async *[Symbol.asyncIterator](): AsyncGenerator<FxYield<E, Exclude<R, S>>, A, unknown> {\n const gen = fx[Symbol.asyncIterator]()\n let result = await gen.next()\n\n while (result.done !== true) {\n const yielded = result.value\n\n const serviceRequest = asServiceRequest(yielded)\n if (serviceRequest?.key === service.key) {\n // oxlint-disable-next-line no-await-in-loop\n result = await gen.next(impl)\n } else {\n const value = yield yielded as FxYield<E, Exclude<R, S>>\n // oxlint-disable-next-line no-await-in-loop\n result = await gen.next(value)\n }\n }\n\n return result.value\n },\n } as AsyncFx<A, E, Exclude<R, S>>\n }\n\n return {\n _tag: \"SyncFx\",\n [FxTypeId]: {\n _A: () => undefined as unknown as A,\n _E: () => undefined as unknown as E,\n _R: () => undefined as unknown as Exclude<R, S>,\n },\n *[Symbol.iterator](): Generator<FxYield<E, Exclude<R, S>>, A, unknown> {\n const gen = fx[Symbol.iterator]()\n let result = gen.next()\n\n while (result.done !== true) {\n const yielded = result.value\n\n const serviceRequest = asServiceRequest(yielded)\n if (serviceRequest?.key === service.key) {\n result = gen.next(impl)\n } else {\n const value = yield yielded as FxYield<E, Exclude<R, S>>\n result = gen.next(value)\n }\n }\n\n return result.value\n },\n } as SyncFx<A, E, Exclude<R, S>>\n }\n\n/* oxlint-enable no-unsafe-type-assertion */\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scope-qwL1VUh2.mjs","names":[],"sources":["../src/scope/scope.ts"],"sourcesContent":["/**\n * Scope lifecycle management for registering and running finalizers.\n *\n * **Mental model**\n * - A scope tracks cleanup actions and executes them in LIFO order.\n * - `Layer.scoped` and `Provide.layer` rely on scope semantics.\n *\n * **Common tasks**\n * - Create scopes with `Scope.make`.\n * - Register cleanup with `scope.addFinalizer`.\n * - Close scopes with `scope.close`.\n *\n * **Gotchas**\n * - Child scopes close before parent finalizers.\n * - Async finalizers force async scope close.\n *\n * **Quickstart**\n *\n * @example\n * ```ts\n * import { Scope } from \"@nicolastoulemont/std\"\n *\n * import { Result } from \"@nicolastoulemont/std\"\n * \n * const scope = Scope.make()\n * const closeFx = scope.close(Result.ok(undefined))\n * // => SyncFx<void> | AsyncFx<void>\n * ```\n *\n * @module\n */\nimport { runSync, runAsync } from \"../fx/fx.runtime\"\nimport { FxTypeId } from \"../fx/fx.types\"\nimport type { SyncFx, AsyncFx, FxYield } from \"../fx/fx.types\"\nimport type { Result } from \"../result/result.types\"\n\n/* oxlint-disable no-unsafe-type-assertion -- Scope encodes Fx phantom type slots and sync/async branch narrowing via assertions. */\n\n// ============================================================================\n// Scope Service\n// ============================================================================\n\n/**\n * Scope manages resource lifecycles with finalizers.\n * Finalizers run in LIFO (Last In, First Out) order when the scope closes.\n *\n * @example\n * ```ts\n * import { Scope } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Scope\n * ```\n *\n * @category Type-level\n */\nexport type ScopeService = {\n /**\n * Add a finalizer to be run when the scope closes.\n * Finalizers are run in LIFO order.\n *\n * @param finalizer - A function that returns an Fx to run on cleanup\n * @returns An Fx that completes when the finalizer is registered\n */\n addFinalizer(finalizer: () => SyncFx<void> | AsyncFx<void>): SyncFx<void>\n\n /**\n * Close the scope, running all finalizers in LIFO order.\n *\n * @param exit - The exit status of the computation\n * @returns An Fx that completes when all finalizers have run\n */\n close(exit: Result<unknown, unknown>): SyncFx<void> | AsyncFx<void>\n\n /**\n * Fork a child scope.\n * The child scope will be closed when the parent scope closes.\n *\n * @returns A new child scope\n */\n fork(): ScopeService\n}\n\n/**\n * Create a new Scope instance.\n * This is the concrete implementation of the ScopeService interface.\n *\n * @example\n * ```ts\n * import { Scope } from \"@nicolastoulemont/std\"\n *\n * import { Result } from \"@nicolastoulemont/std\"\n * \n * const scope = Scope.make()\n * const closeFx = scope.close(Result.ok(undefined))\n * // => SyncFx<void> | AsyncFx<void>\n * ```\n *\n * @category Constructors\n */\nexport function make(): ScopeService {\n type Finalizer = () => SyncFx<void> | AsyncFx<void>\n const finalizers: Finalizer[] = []\n let closed = false\n const children: ScopeService[] = []\n\n const scope: ScopeService = {\n addFinalizer(finalizer: Finalizer): SyncFx<void> {\n return {\n _tag: \"SyncFx\",\n [FxTypeId]: {\n _A: () => undefined as void,\n _E: () => undefined as never,\n _R: () => undefined as never,\n },\n // oxlint-disable-next-line require-yield\n *[Symbol.iterator](): Generator<FxYield<never, never>, void, unknown> {\n if (!closed) {\n finalizers.push(finalizer)\n }\n },\n } as SyncFx<void>\n },\n\n close(exit: Result<unknown, unknown>): SyncFx<void> | AsyncFx<void> {\n // Check if any finalizer is async\n const hasAsyncFinalizer = finalizers.some((f) => {\n const fx = f()\n return fx._tag === \"AsyncFx\"\n })\n\n if (hasAsyncFinalizer) {\n return {\n _tag: \"AsyncFx\",\n [FxTypeId]: {\n _A: () => undefined as void,\n _E: () => undefined as never,\n _R: () => undefined as never,\n },\n // oxlint-disable-next-line require-yield\n async *[Symbol.asyncIterator](): AsyncGenerator<FxYield<never, never>, void, unknown> {\n if (closed) return\n closed = true\n\n // Close children first\n for (const child of [...children].toReversed()) {\n const childClose = child.close(exit)\n if (childClose._tag === \"AsyncFx\") {\n // oxlint-disable-next-line no-await-in-loop\n await runAsync(childClose)\n } else {\n runSync(childClose)\n }\n }\n\n // Run finalizers in LIFO order\n const reversed = [...finalizers].toReversed()\n for (const finalizer of reversed) {\n const fx = finalizer()\n if (fx._tag === \"AsyncFx\") {\n // oxlint-disable-next-line no-await-in-loop\n await runAsync(fx)\n } else {\n runSync(fx)\n }\n }\n },\n } as AsyncFx<void>\n }\n\n // Sync version\n return {\n _tag: \"SyncFx\",\n [FxTypeId]: {\n _A: () => undefined as void,\n _E: () => undefined as never,\n _R: () => undefined as never,\n },\n // oxlint-disable-next-line require-yield\n *[Symbol.iterator](): Generator<FxYield<never, never>, void, unknown> {\n if (closed) return\n closed = true\n\n // Close children first\n for (const child of [...children].toReversed()) {\n const childClose = child.close(exit)\n runSync(childClose as SyncFx<void>)\n }\n\n // Run finalizers in LIFO order\n const reversed = [...finalizers].toReversed()\n for (const finalizer of reversed) {\n const fx = finalizer()\n runSync(fx as SyncFx<void>)\n }\n },\n } as SyncFx<void>\n },\n\n fork(): ScopeService {\n const child = make()\n children.push(child)\n return child\n },\n }\n\n return scope\n}\n\n// ============================================================================\n// Helper Types\n// ============================================================================\n\n/**\n * Type utility to exclude Scope from requirements.\n * Used by Layer.scoped to auto-provide Scope.\n *\n * @example\n * ```ts\n * import { Scope } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Scope\n * ```\n *\n * @category Type-level\n */\nexport type ExcludeScope<R> = Exclude<R, ScopeService>\n\n/* oxlint-enable no-unsafe-type-assertion */\n"],"mappings":"sJAmGA,SAAgB,GAAqB,CAEnC,IAAM,EAA0B,EAAE,CAC9B,EAAS,GACP,EAA2B,EAAE,CAsGnC,MApG4B,CAC1B,aAAa,EAAoC,CAC/C,MAAO,CACL,KAAM,UACL,GAAW,CACV,OAAU,IAAA,GACV,OAAU,IAAA,GACV,OAAU,IAAA,GACX,CAED,EAAE,OAAO,WAA6D,CAC/D,GACH,EAAW,KAAK,EAAU,EAG/B,EAGH,MAAM,EAA8D,CA+ClE,OA7C0B,EAAW,KAAM,GAC9B,GAAG,CACJ,OAAS,UACnB,CAGO,CACL,KAAM,WACL,GAAW,CACV,OAAU,IAAA,GACV,OAAU,IAAA,GACV,OAAU,IAAA,GACX,CAED,OAAQ,OAAO,gBAAuE,CACpF,GAAI,EAAQ,OACZ,EAAS,GAGT,IAAK,IAAM,IAAS,CAAC,GAAG,EAAS,CAAC,YAAY,CAAE,CAC9C,IAAM,EAAa,EAAM,MAAM,EAAK,CAChC,EAAW,OAAS,UAEtB,MAAM,EAAS,EAAW,CAE1B,EAAQ,EAAW,CAKvB,IAAM,EAAW,CAAC,GAAG,EAAW,CAAC,YAAY,CAC7C,IAAK,IAAM,KAAa,EAAU,CAChC,IAAM,EAAK,GAAW,CAClB,EAAG,OAAS,UAEd,MAAM,EAAS,EAAG,CAElB,EAAQ,EAAG,GAIlB,CAII,CACL,KAAM,UACL,GAAW,CACV,OAAU,IAAA,GACV,OAAU,IAAA,GACV,OAAU,IAAA,GACX,CAED,EAAE,OAAO,WAA6D,CACpE,GAAI,EAAQ,OACZ,EAAS,GAGT,IAAK,IAAM,IAAS,CAAC,GAAG,EAAS,CAAC,YAAY,CAE5C,EADmB,EAAM,MAAM,EAAK,CACD,CAIrC,IAAM,EAAW,CAAC,GAAG,EAAW,CAAC,YAAY,CAC7C,IAAK,IAAM,KAAa,EAEtB,EADW,GAAW,CACK,EAGhC,EAGH,MAAqB,CACnB,IAAM,EAAQ,GAAM,CAEpB,OADA,EAAS,KAAK,EAAM,CACb,GAEV"}
|