@nicolastoulemont/std 0.7.2 → 0.8.0
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 +570 -168
- package/dist/adt/index.d.mts +1 -1
- package/dist/adt/index.mjs +1 -1
- package/dist/adt-CzdkjlUM.mjs +2 -0
- package/dist/adt-CzdkjlUM.mjs.map +1 -0
- package/dist/brand/index.d.mts +1 -1
- package/dist/brand/index.mjs +1 -1
- package/dist/brand-DZgGDrAe.mjs +2 -0
- package/dist/brand-DZgGDrAe.mjs.map +1 -0
- package/dist/brand.types-B3NDX1vo.d.mts +62 -0
- package/dist/brand.types-B3NDX1vo.d.mts.map +1 -0
- package/dist/context/index.d.mts +1 -1
- package/dist/context/index.mjs +1 -1
- package/dist/{context-CCHj1nab.mjs → context-0xDbwtpx.mjs} +2 -2
- package/dist/context-0xDbwtpx.mjs.map +1 -0
- package/dist/{context-r8ESJiFn.d.mts → context-B2dWloPl.d.mts} +2 -18
- package/dist/context-B2dWloPl.d.mts.map +1 -0
- package/dist/data/index.d.mts +1 -1
- package/dist/data/index.mjs +1 -1
- package/dist/{data-BLXO4XwS.mjs → data-BHYPdqWZ.mjs} +2 -2
- package/dist/{data-BLXO4XwS.mjs.map → data-BHYPdqWZ.mjs.map} +1 -1
- package/dist/{discriminator.types-CTURejXz.d.mts → discriminator.types-C-ygT2S1.d.mts} +1 -1
- package/dist/discriminator.types-C-ygT2S1.d.mts.map +1 -0
- package/dist/{dual-CZhzZslG.mjs → dual-fN6OUwN_.mjs} +1 -1
- package/dist/{dual-CZhzZslG.mjs.map → dual-fN6OUwN_.mjs.map} +1 -1
- package/dist/duration/index.d.mts +2 -0
- package/dist/duration/index.mjs +1 -0
- package/dist/duration-CYoDHcOR.mjs +2 -0
- package/dist/duration-CYoDHcOR.mjs.map +1 -0
- package/dist/either/index.d.mts +1 -1
- package/dist/either/index.mjs +1 -1
- package/dist/{either-BMLPfvMl.mjs → either-G7uOu4Ar.mjs} +2 -2
- package/dist/either-G7uOu4Ar.mjs.map +1 -0
- package/dist/{equality-CoyUHWh9.mjs → equality-BX6BUidG.mjs} +1 -1
- package/dist/{equality-CoyUHWh9.mjs.map → equality-BX6BUidG.mjs.map} +1 -1
- package/dist/{flow-D8_tllWl.mjs → flow-CNyLsPGb.mjs} +1 -1
- package/dist/flow-CNyLsPGb.mjs.map +1 -0
- package/dist/functions/index.d.mts +1 -1
- package/dist/functions/index.mjs +1 -1
- package/dist/functions-ByAk682_.mjs +2 -0
- package/dist/functions-ByAk682_.mjs.map +1 -0
- package/dist/fx/index.d.mts +1 -1
- package/dist/fx/index.mjs +1 -1
- package/dist/fx-DUXDxwsU.mjs +2 -0
- package/dist/fx-DUXDxwsU.mjs.map +1 -0
- package/dist/{fx.runtime-DclEDyjY.mjs → fx.runtime-jQxh77s3.mjs} +2 -2
- package/dist/{fx.runtime-DclEDyjY.mjs.map → fx.runtime-jQxh77s3.mjs.map} +1 -1
- package/dist/{fx.types-DeEWEltG.d.mts → fx.types-BdN1EWxr.d.mts} +1 -1
- package/dist/{fx.types-DeEWEltG.d.mts.map → fx.types-BdN1EWxr.d.mts.map} +1 -1
- package/dist/{fx.types-Bg-Mmdm5.mjs → fx.types-DyQVgTS8.mjs} +1 -1
- package/dist/{fx.types-Bg-Mmdm5.mjs.map → fx.types-DyQVgTS8.mjs.map} +1 -1
- package/dist/{index-DXbYlSnB.d.mts → index-BA0EsFxS.d.mts} +5 -74
- package/dist/index-BA0EsFxS.d.mts.map +1 -0
- package/dist/{index-UzMbg1dh.d.mts → index-BqJ1GWAF.d.mts} +20 -56
- package/dist/index-BqJ1GWAF.d.mts.map +1 -0
- package/dist/index-BsPOcZk9.d.mts +96 -0
- package/dist/index-BsPOcZk9.d.mts.map +1 -0
- package/dist/{index-DEAWPlcI.d.mts → index-CIvNgjsx.d.mts} +24 -56
- package/dist/index-CIvNgjsx.d.mts.map +1 -0
- package/dist/{index-B_iY5tq0.d.mts → index-CNTYbcY9.d.mts} +1 -21
- package/dist/index-CNTYbcY9.d.mts.map +1 -0
- package/dist/index-Ctg7XUOs.d.mts +36 -0
- package/dist/index-Ctg7XUOs.d.mts.map +1 -0
- package/dist/{index-Cq2IFito.d.mts → index-Ctqe1fD1.d.mts} +3 -17
- package/dist/index-Ctqe1fD1.d.mts.map +1 -0
- package/dist/{index-B_wWGszy.d.mts → index-D7mFNjot.d.mts} +1 -5
- package/dist/index-D7mFNjot.d.mts.map +1 -0
- package/dist/{index-CUZn-ohG.d.mts → index-D8rDE60Y.d.mts} +23 -54
- package/dist/index-D8rDE60Y.d.mts.map +1 -0
- package/dist/{index-By6dNRc4.d.mts → index-DR7hzXU4.d.mts} +3 -23
- package/dist/{index-By6dNRc4.d.mts.map → index-DR7hzXU4.d.mts.map} +1 -1
- package/dist/{index-DKS1g1oC.d.mts → index-DfQGXBQI.d.mts} +54 -45
- package/dist/index-DfQGXBQI.d.mts.map +1 -0
- package/dist/{index-BNQ9xSAz.d.mts → index-MsJqfQu0.d.mts} +64 -70
- package/dist/index-MsJqfQu0.d.mts.map +1 -0
- package/dist/{index-CGiLfREk.d.mts → index-UINIHFuh.d.mts} +39 -15
- package/dist/index-UINIHFuh.d.mts.map +1 -0
- package/dist/{index-BiiE8NS7.d.mts → index-crtzMG48.d.mts} +14 -23
- package/dist/index-crtzMG48.d.mts.map +1 -0
- package/dist/{index-B1-tBzc0.d.mts → index-dCRymj_g.d.mts} +23 -71
- package/dist/{index-B1-tBzc0.d.mts.map → index-dCRymj_g.d.mts.map} +1 -1
- package/dist/index-uE3S3Krx.d.mts +245 -0
- package/dist/index-uE3S3Krx.d.mts.map +1 -0
- package/dist/index.d.mts +21 -19
- package/dist/index.mjs +1 -1
- package/dist/layer/index.d.mts +1 -1
- package/dist/layer/index.mjs +1 -1
- package/dist/{layer-BttmtDrs.mjs → layer-CKtH7TRL.mjs} +2 -2
- package/dist/layer-CKtH7TRL.mjs.map +1 -0
- package/dist/{layer.types-DgpCIsk_.d.mts → layer.types-BB0MrvLg.d.mts} +4 -4
- package/dist/{layer.types-DgpCIsk_.d.mts.map → layer.types-BB0MrvLg.d.mts.map} +1 -1
- package/dist/multithread/index.d.mts +1 -1
- package/dist/multithread/index.mjs +1 -1
- package/dist/{multithread-xUUh4eLn.mjs → multithread-Cyc8Bz45.mjs} +2 -2
- package/dist/multithread-Cyc8Bz45.mjs.map +1 -0
- package/dist/option/index.d.mts +1 -1
- package/dist/option/index.mjs +1 -1
- package/dist/{option-Tfbo4wty.mjs → option-C2iCxAuJ.mjs} +2 -2
- package/dist/option-C2iCxAuJ.mjs.map +1 -0
- package/dist/{option.types-D1mm0zUb.mjs → option.types-CbY_swma.mjs} +1 -1
- package/dist/{option.types-D1mm0zUb.mjs.map → option.types-CbY_swma.mjs.map} +1 -1
- package/dist/{option.types-qPevEZQd.d.mts → option.types-D9hrKcfa.d.mts} +3 -3
- package/dist/{option.types-qPevEZQd.d.mts.map → option.types-D9hrKcfa.d.mts.map} +1 -1
- package/dist/order/index.d.mts +1 -1
- package/dist/order/index.mjs +1 -1
- package/dist/order-BXOBEKvB.mjs +2 -0
- package/dist/order-BXOBEKvB.mjs.map +1 -0
- package/dist/{pipeable-rfqacPxZ.d.mts → pipeable-BIrevC0D.d.mts} +1 -1
- package/dist/{pipeable-rfqacPxZ.d.mts.map → pipeable-BIrevC0D.d.mts.map} +1 -1
- package/dist/pipeable-Dp1_23zH.mjs +2 -0
- package/dist/{pipeable-COGyGMUV.mjs.map → pipeable-Dp1_23zH.mjs.map} +1 -1
- package/dist/predicate/index.d.mts +1 -1
- package/dist/predicate/index.mjs +1 -1
- package/dist/{predicate-DUhhQqWY.mjs → predicate-D_1SsIi4.mjs} +2 -2
- package/dist/predicate-D_1SsIi4.mjs.map +1 -0
- package/dist/provide/index.d.mts +1 -1
- package/dist/provide/index.mjs +1 -1
- package/dist/{provide-BmSM3Ruy.mjs → provide--yZE8x-n.mjs} +2 -2
- package/dist/provide--yZE8x-n.mjs.map +1 -0
- package/dist/queue/index.d.mts +1 -1
- package/dist/queue/index.mjs +1 -1
- package/dist/{queue-Sg6KJerl.mjs → queue-apiEOlRD.mjs} +2 -2
- package/dist/queue-apiEOlRD.mjs.map +1 -0
- package/dist/{queue.types-CD2LOu37.d.mts → queue.types-B-l5XYbU.d.mts} +1 -1
- package/dist/{queue.types-CD2LOu37.d.mts.map → queue.types-B-l5XYbU.d.mts.map} +1 -1
- package/dist/result/index.d.mts +1 -1
- package/dist/result/index.mjs +1 -1
- package/dist/{result-BEzV0DYC.mjs → result-D3VY0qBG.mjs} +2 -2
- package/dist/result-D3VY0qBG.mjs.map +1 -0
- package/dist/{result.types-_xDAei3-.d.mts → result.types-BKzChyWY.d.mts} +3 -3
- package/dist/{result.types-_xDAei3-.d.mts.map → result.types-BKzChyWY.d.mts.map} +1 -1
- package/dist/schedule/index.d.mts +1 -1
- package/dist/schedule/index.mjs +1 -1
- package/dist/schedule-B9K_2Z21.d.mts +183 -0
- package/dist/schedule-B9K_2Z21.d.mts.map +1 -0
- package/dist/schedule-C6iN3oMt.mjs +2 -0
- package/dist/schedule-C6iN3oMt.mjs.map +1 -0
- package/dist/schema/index.d.mts +2 -0
- package/dist/schema/index.mjs +1 -0
- package/dist/schema-D87TVF_b.mjs +2 -0
- package/dist/schema-D87TVF_b.mjs.map +1 -0
- package/dist/schema.shared-CI4eydjX.mjs +2 -0
- package/dist/schema.shared-CI4eydjX.mjs.map +1 -0
- package/dist/schema.types-CFzzx4bw.d.mts +45 -0
- package/dist/schema.types-CFzzx4bw.d.mts.map +1 -0
- package/dist/scope/index.d.mts +1 -1
- package/dist/scope/index.mjs +1 -1
- package/dist/{scope-CZdp4wKX.d.mts → scope-CuM3CzwG.d.mts} +3 -9
- package/dist/scope-CuM3CzwG.d.mts.map +1 -0
- package/dist/{scope-D_kzd1nT.mjs → scope-gVt4PESc.mjs} +2 -2
- package/dist/scope-gVt4PESc.mjs.map +1 -0
- package/dist/service/index.d.mts +1 -1
- package/dist/service/index.mjs +1 -1
- package/dist/{service-3PYQTUdH.mjs → service-CWAIEH46.mjs} +2 -2
- package/dist/service-CWAIEH46.mjs.map +1 -0
- package/dist/{service-DrXU7KJG.d.mts → service-D8mr0wwg.d.mts} +2 -8
- package/dist/service-D8mr0wwg.d.mts.map +1 -0
- package/dist/{service-resolution-C19smeaO.mjs → service-resolution-BefYr4nR.mjs} +1 -1
- package/dist/{service-resolution-C19smeaO.mjs.map → service-resolution-BefYr4nR.mjs.map} +1 -1
- package/package.json +9 -1
- package/dist/adt-DajUZvJe.mjs +0 -2
- package/dist/adt-DajUZvJe.mjs.map +0 -1
- package/dist/brand-Bia3Vj6l.mjs +0 -2
- package/dist/brand-Bia3Vj6l.mjs.map +0 -1
- package/dist/context-CCHj1nab.mjs.map +0 -1
- package/dist/context-r8ESJiFn.d.mts.map +0 -1
- package/dist/data.tagged-error.types-CGiKD-ES.d.mts +0 -29
- package/dist/data.tagged-error.types-CGiKD-ES.d.mts.map +0 -1
- package/dist/discriminator.types-CTURejXz.d.mts.map +0 -1
- package/dist/either-BMLPfvMl.mjs.map +0 -1
- package/dist/flow-D8_tllWl.mjs.map +0 -1
- package/dist/functions-BkevX2Dw.mjs +0 -2
- package/dist/functions-BkevX2Dw.mjs.map +0 -1
- package/dist/fx-K-a9Smhn.mjs +0 -2
- package/dist/fx-K-a9Smhn.mjs.map +0 -1
- package/dist/index-7Lv982Om.d.mts +0 -217
- package/dist/index-7Lv982Om.d.mts.map +0 -1
- package/dist/index-BNQ9xSAz.d.mts.map +0 -1
- package/dist/index-B_iY5tq0.d.mts.map +0 -1
- package/dist/index-B_wWGszy.d.mts.map +0 -1
- package/dist/index-BiiE8NS7.d.mts.map +0 -1
- package/dist/index-CGiLfREk.d.mts.map +0 -1
- package/dist/index-CUZn-ohG.d.mts.map +0 -1
- package/dist/index-Cq2IFito.d.mts.map +0 -1
- package/dist/index-DEAWPlcI.d.mts.map +0 -1
- package/dist/index-DKS1g1oC.d.mts.map +0 -1
- package/dist/index-DXbYlSnB.d.mts.map +0 -1
- package/dist/index-UzMbg1dh.d.mts.map +0 -1
- package/dist/layer-BttmtDrs.mjs.map +0 -1
- package/dist/multithread-xUUh4eLn.mjs.map +0 -1
- package/dist/option-Tfbo4wty.mjs.map +0 -1
- package/dist/order-D5c4QChk.mjs +0 -2
- package/dist/order-D5c4QChk.mjs.map +0 -1
- package/dist/pipeable-COGyGMUV.mjs +0 -2
- package/dist/predicate-DUhhQqWY.mjs.map +0 -1
- package/dist/provide-BmSM3Ruy.mjs.map +0 -1
- package/dist/queue-Sg6KJerl.mjs.map +0 -1
- package/dist/result-BEzV0DYC.mjs.map +0 -1
- package/dist/schedule-C6tjcJ1O.mjs +0 -2
- package/dist/schedule-C6tjcJ1O.mjs.map +0 -1
- package/dist/schedule-DlX2Dg69.d.mts +0 -144
- package/dist/schedule-DlX2Dg69.d.mts.map +0 -1
- package/dist/scope-CZdp4wKX.d.mts.map +0 -1
- package/dist/scope-D_kzd1nT.mjs.map +0 -1
- package/dist/service-3PYQTUdH.mjs.map +0 -1
- package/dist/service-DrXU7KJG.d.mts.map +0 -1
- /package/dist/{chunk-C934ptG5.mjs → chunk-oQKkju2G.mjs} +0 -0
- /package/dist/{option-CBCwzF0L.mjs → option-CXXiA1w-.mjs} +0 -0
- /package/dist/{result-B5WbPg8C.mjs → result-xFLfwriM.mjs} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"queue-Sg6KJerl.mjs","names":["Data.TaggedError","options"],"sources":["../src/queue/queue.ts"],"sourcesContent":["/**\n * Promise-based work queue with bounded/unbounded capacity and shutdown modes.\n *\n * **Mental model**\n * - A queue coordinates concurrent task execution with backpressure.\n * - Enqueue returns a promise for each task result.\n *\n * **Common tasks**\n * - Create queues via `Queue.make`, `Queue.bounded`, or `Queue.unbounded`.\n * - Pause/resume processing and await idle state.\n * - Shutdown with drain or abort semantics.\n *\n * **Gotchas**\n * - Bounded queues can block enqueue when capacity is reached.\n * - Aborted queues reject queued and future tasks.\n *\n * **Quickstart**\n *\n * @example\n * ```ts\n * import { Queue } from \"@nicolastoulemont/std\"\n *\n * const queue = Queue.make()\n * const pending = queue.pending\n * // => 0\n * ```\n *\n * @module\n */\nimport { Data } from \"../data\"\nimport type {\n Concurrency,\n Queue as QueueType,\n QueueBoundedOptions,\n QueueOptions,\n QueueShutdownMode,\n QueueShutdownOptions,\n QueueTask,\n} from \"./queue.types\"\n\n/* oxlint-disable max-classes-per-file -- Queue exports dedicated error classes for precise instanceof checks at call sites. */\n/**\n * Error raised when enqueueing into a non-open queue.\n *\n * @example\n * ```ts\n * import { Queue } from \"@nicolastoulemont/std\"\n *\n * const error = new Queue.QueueClosedError({ state: \"draining\" })\n * // => { _tag: \"QueueClosedError\", state: \"draining\" }\n * ```\n *\n * @category Models\n */\nexport class QueueClosedError extends Data.TaggedError(\"QueueClosedError\")<{ state: \"draining\" | \"aborted\" }> {}\n/**\n * Error raised when queue processing is aborted.\n *\n * @example\n * ```ts\n * import { Queue } from \"@nicolastoulemont/std\"\n *\n * const error = new Queue.QueueAbortedError({ reason: \"manual\" })\n * // => { _tag: \"QueueAbortedError\", reason: \"manual\" }\n * ```\n *\n * @category Models\n */\nexport class QueueAbortedError extends Data.TaggedError(\"QueueAbortedError\")<{ reason?: unknown }> {}\n/**\n * Error raised when a specific task is aborted.\n *\n * @example\n * ```ts\n * import { Queue } from \"@nicolastoulemont/std\"\n *\n * const error = new Queue.QueueTaskAbortedError({ reason: \"signal\" })\n * // => { _tag: \"QueueTaskAbortedError\", reason: \"signal\" }\n * ```\n *\n * @category Models\n */\nexport class QueueTaskAbortedError extends Data.TaggedError(\"QueueTaskAbortedError\")<{ reason?: unknown }> {}\n\ntype QueueState = \"open\" | \"draining\" | \"aborted\" | \"closed\"\n\ntype TaskEntry<A> = {\n readonly task: QueueTask<A>\n resolve(value: A | PromiseLike<A>): void\n reject(reason: unknown): void\n readonly signal: AbortSignal | undefined\n}\n\ntype CapacityWaiter = {\n resolve(): void\n reject(reason: Error): void\n readonly signal: AbortSignal | undefined\n}\n\nconst asConcurrencyLimit = (concurrency: Concurrency | undefined): number => {\n const resolved = concurrency ?? \"unbounded\"\n if (resolved === \"unbounded\") {\n return Number.POSITIVE_INFINITY\n }\n\n if (!Number.isInteger(resolved) || resolved <= 0) {\n throw new RangeError(`Queue concurrency must be a positive integer or \"unbounded\", received: ${String(resolved)}`)\n }\n\n return resolved\n}\n\nconst assertMaxSize = (maxSize: number) => {\n if (maxSize === Number.POSITIVE_INFINITY) {\n return\n }\n\n if (!Number.isInteger(maxSize) || maxSize < 0) {\n throw new RangeError(`Queue maxSize must be an integer >= 0, received: ${String(maxSize)}`)\n }\n}\n\nconst queueTaskAbortedError = (signal: AbortSignal): QueueTaskAbortedError =>\n new QueueTaskAbortedError({ reason: signal.reason })\n\nconst queueClosedError = (state: \"draining\" | \"aborted\"): QueueClosedError => new QueueClosedError({ state })\n\nconst toError = (reason: unknown): Error =>\n reason instanceof Error ? reason : new Error(\"Queue rejection reason was not an Error\", { cause: reason })\n\nconst composeSignals = (queueSignal: AbortSignal, taskSignal: AbortSignal | undefined): AbortSignal => {\n if (taskSignal === undefined) {\n return queueSignal\n }\n\n if (taskSignal.aborted) {\n return taskSignal\n }\n\n if (queueSignal.aborted) {\n return queueSignal\n }\n\n const composite = new AbortController()\n\n const onQueueAbort = () => {\n composite.abort(queueSignal.reason)\n taskSignal.removeEventListener(\"abort\", onTaskAbort)\n }\n\n const onTaskAbort = () => {\n composite.abort(taskSignal.reason)\n queueSignal.removeEventListener(\"abort\", onQueueAbort)\n }\n\n queueSignal.addEventListener(\"abort\", onQueueAbort, { once: true })\n taskSignal.addEventListener(\"abort\", onTaskAbort, { once: true })\n\n composite.signal.addEventListener(\n \"abort\",\n () => {\n queueSignal.removeEventListener(\"abort\", onQueueAbort)\n taskSignal.removeEventListener(\"abort\", onTaskAbort)\n },\n { once: true },\n )\n\n return composite.signal\n}\n\n/**\n * Check if a value is a `Queue`.\n *\n * @example\n * ```ts\n * import { Queue } from \"@nicolastoulemont/std\"\n *\n * const queue = Queue.make()\n * const isQueue = Queue.is(queue)\n * // => true\n * ```\n *\n * @category Guards\n */\nexport const is = (value: unknown): value is QueueType =>\n typeof value === \"object\" && value !== null && \"_tag\" in value && value._tag === \"Queue\"\n\nconst createQueue = (maxSize: number, options?: QueueOptions): QueueType => {\n assertMaxSize(maxSize)\n\n const concurrencyLimit = asConcurrencyLimit(options?.concurrency)\n let state: QueueState = \"open\"\n let closeState: \"draining\" | \"aborted\" | undefined\n let paused = options?.autoStart === false\n let pending = 0\n let draining = false\n\n const queuedTasks: TaskEntry<unknown>[] = []\n const capacityWaiters: CapacityWaiter[] = []\n const idleWaiters = new Set<() => void>()\n\n const queueAbortController = new AbortController()\n\n let shutdownResolver: (() => void) | undefined\n let shutdownPromise: Promise<void> | undefined\n\n const resolveIdleIfNeeded = () => {\n if (pending !== 0 || queuedTasks.length > 0) {\n return\n }\n\n for (const resolve of idleWaiters) {\n resolve()\n }\n idleWaiters.clear()\n\n if (state === \"draining\" || state === \"aborted\") {\n state = \"closed\"\n shutdownResolver?.()\n shutdownResolver = undefined\n }\n }\n\n const rejectCapacityWaiters = (reason: Error) => {\n if (capacityWaiters.length === 0) {\n return\n }\n\n const waiters = capacityWaiters.splice(0)\n for (const waiter of waiters) {\n waiter.reject(reason)\n }\n }\n\n const releaseCapacityWaiters = () => {\n if (!Number.isFinite(maxSize) || capacityWaiters.length === 0) {\n return\n }\n\n while (capacityWaiters.length > 0 && queuedTasks.length < maxSize && state === \"open\") {\n const waiter = capacityWaiters.shift()\n if (waiter !== undefined) {\n waiter.resolve()\n }\n }\n }\n\n const applyAbortToQueuedTasks = (reason: unknown) => {\n if (queuedTasks.length === 0) {\n return\n }\n\n const error = new QueueAbortedError({ reason })\n const tasks = queuedTasks.splice(0)\n for (const entry of tasks) {\n entry.reject(error)\n }\n }\n\n const waitForCapacity = (signal: AbortSignal | undefined): Promise<void> => {\n if (!Number.isFinite(maxSize)) {\n return Promise.resolve()\n }\n\n if (queuedTasks.length < maxSize) {\n return Promise.resolve()\n }\n\n return new Promise<void>((resolve, reject) => {\n const waiter: CapacityWaiter = {\n resolve: () => {\n if (signal !== undefined && onAbort !== undefined) {\n signal.removeEventListener(\"abort\", onAbort)\n }\n resolve()\n },\n reject: (error) => {\n if (signal !== undefined && onAbort !== undefined) {\n signal.removeEventListener(\"abort\", onAbort)\n }\n reject(error)\n },\n signal,\n }\n\n const onAbort =\n signal === undefined\n ? undefined\n : () => {\n const index = capacityWaiters.indexOf(waiter)\n if (index !== -1) {\n capacityWaiters.splice(index, 1)\n }\n reject(queueTaskAbortedError(signal))\n }\n\n if (signal !== undefined && signal.aborted) {\n reject(queueTaskAbortedError(signal))\n return\n }\n\n if (signal !== undefined && onAbort !== undefined) {\n signal.addEventListener(\"abort\", onAbort, { once: true })\n }\n\n capacityWaiters.push(waiter)\n })\n }\n\n const closeIfNeeded = () => {\n if ((state === \"draining\" || state === \"aborted\") && pending === 0 && queuedTasks.length === 0) {\n resolveIdleIfNeeded()\n }\n }\n\n const runTask = <A>(entry: TaskEntry<A>) => {\n pending += 1\n\n const taskSignal = composeSignals(queueAbortController.signal, entry.signal)\n\n let taskResult: Promise<A>\n if (taskSignal.aborted) {\n taskResult = Promise.reject(queueTaskAbortedError(taskSignal))\n } else {\n try {\n taskResult = Promise.resolve(entry.task({ signal: taskSignal }))\n } catch (error) {\n taskResult = Promise.reject(toError(error))\n }\n }\n\n void taskResult\n .then(\n (value) => {\n entry.resolve(value)\n return undefined\n },\n (error: unknown) => {\n entry.reject(toError(error))\n return undefined\n },\n )\n .finally(() => {\n pending -= 1\n releaseCapacityWaiters()\n drainQueue()\n resolveIdleIfNeeded()\n })\n }\n\n const drainQueue = () => {\n if (draining) {\n return\n }\n\n draining = true\n try {\n if (state === \"aborted\") {\n closeIfNeeded()\n return\n }\n\n while (!paused && pending < concurrencyLimit && queuedTasks.length > 0) {\n const entry = queuedTasks.shift()\n if (entry === undefined) {\n break\n }\n releaseCapacityWaiters()\n runTask(entry)\n }\n\n closeIfNeeded()\n } finally {\n draining = false\n }\n }\n\n const enqueueErrorForState = (): QueueClosedError => {\n if (closeState !== undefined) {\n return queueClosedError(closeState)\n }\n\n if (state === \"aborted\") {\n return queueClosedError(\"aborted\")\n }\n\n if (state === \"draining\") {\n return queueClosedError(\"draining\")\n }\n\n if (state === \"closed\") {\n return queueClosedError(closeState ?? \"draining\")\n }\n\n return queueClosedError(\"draining\")\n }\n\n const queue: QueueType = {\n _tag: \"Queue\",\n\n get pending(): number {\n return pending\n },\n\n get size(): number {\n return queuedTasks.length\n },\n\n get isPaused(): boolean {\n return paused\n },\n\n get isShutdown(): boolean {\n return state !== \"open\"\n },\n\n enqueue<A>(task: QueueTask<A>, options?: { readonly signal?: AbortSignal }): Promise<A> {\n const signal = options?.signal\n\n if (signal !== undefined && signal.aborted) {\n return Promise.reject(queueTaskAbortedError(signal))\n }\n\n if (state !== \"open\") {\n return Promise.reject(enqueueErrorForState())\n }\n\n const enqueueTask = () =>\n new Promise<A>((resolve, reject) => {\n const entry: TaskEntry<A> = {\n task,\n resolve,\n reject,\n signal,\n }\n\n queuedTasks.push(entry)\n drainQueue()\n })\n\n if (!Number.isFinite(maxSize) || queuedTasks.length < maxSize) {\n return enqueueTask()\n }\n\n return waitForCapacity(signal).then(() => {\n if (signal !== undefined && signal.aborted) {\n throw queueTaskAbortedError(signal)\n }\n\n if (state !== \"open\") {\n throw enqueueErrorForState()\n }\n\n return enqueueTask()\n })\n },\n\n pause(): void {\n paused = true\n },\n\n resume(): void {\n if (!paused) {\n return\n }\n\n paused = false\n drainQueue()\n },\n\n awaitIdle(): Promise<void> {\n if (pending === 0 && queuedTasks.length === 0) {\n return Promise.resolve()\n }\n\n return new Promise<void>((resolve) => {\n idleWaiters.add(resolve)\n })\n },\n\n shutdown(options?: QueueShutdownOptions): Promise<void> {\n const mode: QueueShutdownMode = options?.mode ?? \"drain\"\n\n if (shutdownPromise !== undefined && (state === \"draining\" || state === \"aborted\" || state === \"closed\")) {\n if (mode === \"abort\" && closeState !== \"aborted\") {\n closeState = \"aborted\"\n state = \"aborted\"\n queueAbortController.abort(options?.reason)\n applyAbortToQueuedTasks(options?.reason)\n rejectCapacityWaiters(queueClosedError(\"aborted\"))\n closeIfNeeded()\n }\n\n return shutdownPromise\n }\n\n shutdownPromise =\n shutdownPromise ??\n new Promise<void>((resolve) => {\n shutdownResolver = resolve\n })\n\n if (mode === \"abort\") {\n closeState = \"aborted\"\n state = \"aborted\"\n paused = false\n queueAbortController.abort(options?.reason)\n applyAbortToQueuedTasks(options?.reason)\n rejectCapacityWaiters(queueClosedError(\"aborted\"))\n } else {\n closeState = \"draining\"\n if (state === \"open\") {\n state = \"draining\"\n }\n paused = false\n rejectCapacityWaiters(queueClosedError(\"draining\"))\n }\n\n drainQueue()\n resolveIdleIfNeeded()\n\n return shutdownPromise\n },\n }\n\n return queue\n}\n\n/**\n * Create a queue with default unbounded capacity.\n *\n * @example\n * ```ts\n * import { Queue } from \"@nicolastoulemont/std\"\n *\n * const queue = Queue.make()\n * const pending = queue.pending\n * // => 0\n * ```\n *\n * @category Constructors\n */\nexport const make = (options?: QueueOptions): QueueType => createQueue(Number.POSITIVE_INFINITY, options)\n\n/**\n * Create a queue with bounded capacity.\n *\n * @example\n * ```ts\n * import { Queue } from \"@nicolastoulemont/std\"\n *\n * const queue = Queue.bounded(2)\n * const size = queue.size\n * // => 0\n * ```\n *\n * @category Constructors\n */\nexport const bounded = (maxSize: number, options?: QueueBoundedOptions): QueueType => createQueue(maxSize, options)\n\n/**\n * Create a queue with explicit unbounded capacity.\n *\n * @example\n * ```ts\n * import { Queue } from \"@nicolastoulemont/std\"\n *\n * const queue = Queue.unbounded()\n * const paused = queue.isPaused\n * // => false\n * ```\n *\n * @category Constructors\n */\nexport const unbounded = (options?: QueueOptions): QueueType => createQueue(Number.POSITIVE_INFINITY, options)\n\n/* oxlint-enable max-classes-per-file */\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"result-BEzV0DYC.mjs","names":["values"],"sources":["../src/result/result.ts"],"sourcesContent":["/**\n * Synchronous success/error primitives with typed channels (`Ok` / `Err`).\n *\n * **Mental model**\n * - `Result<A, E>` makes success and failure explicit.\n * - Use `flatMap` to sequence fallible operations without throwing.\n *\n * **Common tasks**\n * - Construct with `Result.ok`, `Result.err`, `Result.fromTry`.\n * - Transform with `Result.map`, `Result.mapErr`, `Result.flatMap`.\n * - Handle with `Result.match` or `Result.unwrapOr`.\n *\n * **Gotchas**\n * - `Result.err` short-circuits in `Fx.gen`.\n * - Keep domain errors in the `E` channel instead of throwing.\n *\n * **Quickstart**\n *\n * @example\n * ```ts\n * import { Result } from \"@nicolastoulemont/std\"\n *\n * const parsed = Result.fromTry(() => Number.parseInt(\"42\", 10))\n * const doubled = Result.map(parsed, (value) => value * 2)\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 type {\n FromTryOptions,\n Result as ResultType,\n ResultAll,\n ResultFlatMap,\n ResultFromTry,\n ResultMap,\n ResultMapErr,\n ResultMatch,\n ResultTap,\n ResultOrElse,\n ResultFilter,\n ResultUnwrapOr,\n ResultUnwrapOrElse,\n} from \"./result.types\"\n\n/**\n * Re-exported `Result` type.\n *\n * @example\n * ```ts\n * import type { Result } from \"@nicolastoulemont/std\"\n *\n * type Example = Result.Result<unknown, unknown>\n * ```\n *\n * @category Re-exports\n */\nexport type Result<T, E> = ResultType<T, E>\n\n/* oxlint-disable no-unsafe-type-assertion -- Result constructors encode Fx phantom channels and iterator contracts via type-level markers. */\n\n// ============================================================================\n// Constructors\n// ============================================================================\n\n/**\n * Create a successful Result.\n *\n * @param value - The success value\n * @returns A Result with _tag: \"Ok\"\n *\n * @example\n * ```ts\n * import { Result } from \"@nicolastoulemont/std\"\n *\n * const value = Result.ok(42)\n * // => { _tag: \"Ok\", value: 42 }\n * ```\n *\n * @category Constructors\n */\nexport const ok = <T>(value: T): ResultType<T, never> => {\n const result: ResultType<T, never> = {\n _tag: \"Ok\",\n value,\n pipe: pipeMethod,\n [FxTypeId]: {\n _A: () => value,\n _E: () => undefined as never,\n _R: () => undefined as never,\n },\n // oxlint-disable-next-line require-yield\n *[Symbol.iterator](): Generator<ResultType<T, never>, T, unknown> {\n return value\n },\n }\n return result\n}\n\n/**\n * Create an error Result.\n *\n * @param error - The error value\n * @returns A Result with _tag: \"Err\"\n *\n * @example\n * ```ts\n * import { Result } from \"@nicolastoulemont/std\"\n *\n * const value = Result.err(\"boom\")\n * // => { _tag: \"Err\", error: \"boom\" }\n * ```\n *\n * @category Constructors\n */\nexport const err = <E>(error: E): ResultType<never, E> => {\n const result: ResultType<never, E> = {\n _tag: \"Err\",\n error,\n pipe: pipeMethod,\n [FxTypeId]: {\n _A: () => undefined as never,\n _E: () => error,\n _R: () => undefined as never,\n },\n *[Symbol.iterator](): Generator<ResultType<never, E>, never, unknown> {\n yield result\n throw new Error(\"Unreachable: Fx.gen should short-circuit on error\")\n },\n }\n return result\n}\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\n/**\n * Check if a Result is successful.\n *\n * @param result - The Result to check\n * @returns true if the Result is Ok\n *\n * @example\n * ```ts\n * import { Result } from \"@nicolastoulemont/std\"\n *\n * const isOk = Result.isOk(Result.ok(1))\n * // => true\n * ```\n *\n * @category Guards\n */\nexport const isOk = <T, E>(result: ResultType<T, E>): result is Extract<ResultType<T, E>, { _tag: \"Ok\" }> =>\n result._tag === \"Ok\"\n\n/**\n * Check if a Result is an error.\n *\n * @param result - The Result to check\n * @returns true if the Result is an error\n *\n * @example\n * ```ts\n * import { Result } from \"@nicolastoulemont/std\"\n *\n * const isErr = Result.isErr(Result.err(\"boom\"))\n * // => true\n * ```\n *\n * @category Guards\n */\nexport const isErr = <T, E>(result: ResultType<T, E>): result is Extract<ResultType<T, E>, { _tag: \"Err\" }> =>\n result._tag === \"Err\"\n\n// ============================================================================\n// Transformations (curried for pipe)\n// ============================================================================\n\n/**\n * Transform the success value of a Result.\n * If the Result is an error, it passes through unchanged.\n *\n * Supports both data-first and data-last calling styles:\n * - Data-first: `map(result, fn)`\n * - Data-last: `pipe(result, map(fn))`\n *\n * Supports both sync and async functions:\n * - Sync fn: returns Result<U, E>\n * - Async fn: returns Promise<Result<U, E>>\n *\n * @param fn - Function to transform the success value\n * @returns A function that takes a Result and returns a new Result\n *\n * @example\n * ```ts\n * import { Result } from \"@nicolastoulemont/std\"\n *\n * const input = Result.ok(2)\n * const dataFirst = Result.map(input, (n) => n + 1)\n * // => { _tag: \"Ok\", value: 3 }\n *\n * const dataLast = Result.map((n: number) => n + 1)(input)\n * // => { _tag: \"Ok\", 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: ResultMap = dual(2, (result: ResultType<unknown, unknown>, fn: (value: unknown) => unknown) => {\n if (result._tag === \"Err\") return result as any\n const mapped = fn(result.value)\n if (isPromise(mapped)) {\n return Promise.resolve(mapped).then(ok) as any\n }\n return ok(mapped) as any\n})\n/* oxlint-enable no-explicit-any, no-unsafe-return, no-unsafe-type-assertion */\n\n/**\n * Transform the error value of a Result.\n * If the Result is successful, it passes through unchanged.\n *\n * Supports both data-first and data-last calling styles:\n * - Data-first: `mapErr(result, fn)`\n * - Data-last: `pipe(result, mapErr(fn))`\n *\n * Supports both sync and async functions:\n * - Sync fn: returns Result<T, F>\n * - Async fn: returns Promise<Result<T, F>>\n *\n * @param fn - Function to transform the error value\n * @returns A function that takes a Result and returns a new Result\n *\n * @example\n * ```ts\n * import { Result } from \"@nicolastoulemont/std\"\n *\n * const input = Result.err(\"missing\")\n * const dataFirst = Result.mapErr(input, (msg) => msg.toUpperCase())\n * // => { _tag: \"Err\", error: \"MISSING\" }\n *\n * const dataLast = Result.mapErr((msg: string) => msg.toUpperCase())(input)\n * // => { _tag: \"Err\", error: \"MISSING\" }\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 mapErr: ResultMapErr = dual(2, (result: ResultType<unknown, unknown>, fn: (error: unknown) => unknown) => {\n if (result._tag === \"Ok\") return result as any\n const mapped = fn(result.error)\n if (isPromise(mapped)) {\n return Promise.resolve(mapped).then(err) as any\n }\n return err(mapped) as any\n})\n/* oxlint-enable no-explicit-any, no-unsafe-return, no-unsafe-type-assertion */\n\n/**\n * Chain operations that return Results.\n * If the Result is an error, it passes through unchanged.\n *\n * Supports both data-first and data-last calling styles:\n * - Data-first: `flatMap(result, fn)`\n * - Data-last: `pipe(result, flatMap(fn))`\n *\n * Supports both sync and async functions:\n * - Sync fn: returns Result<U, E | E2>\n * - Async fn: returns Promise<Result<U, E | E2>>\n *\n * @param fn - Function that takes a value and returns a Result (or Promise<Result>)\n * @returns A function that takes a Result and returns a new Result\n *\n * @example\n * ```ts\n * import { Result } from \"@nicolastoulemont/std\"\n *\n * const input = Result.ok(2)\n * const dataFirst = Result.flatMap(input, (n) => Result.ok(n + 1))\n * // => { _tag: \"Ok\", value: 3 }\n *\n * const dataLast = Result.flatMap((n: number) => Result.ok(n + 1))(input)\n * // => { _tag: \"Ok\", 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: ResultFlatMap = dual(2, (result: ResultType<unknown, unknown>, fn: (value: unknown) => unknown) =>\n result._tag === \"Ok\" ? fn(result.value) : result,\n)\n\n/**\n * Perform a side effect on the success value without modifying the Result.\n * Useful for debugging, logging, or other side effects in a pipeline.\n * If the Result is an error, the function is not called.\n *\n * Supports both data-first and data-last calling styles:\n * - Data-first: `tap(result, fn)`\n * - Data-last: `pipe(result, tap(fn))`\n *\n * Supports both sync and async functions:\n * - Sync fn: returns Result<T, E>\n * - Async fn: returns Promise<Result<T, E>>\n *\n * @param fn - Function to call with the success value (return value is ignored)\n * @returns A function that takes a Result and returns the same Result\n *\n * @example\n * ```ts\n * import { Result } from \"@nicolastoulemont/std\"\n *\n * let seen = 0\n * const input = Result.ok(2)\n * const dataFirst = Result.tap(input, (n) => {\n * seen = n\n * })\n * // => { _tag: \"Ok\", value: 2 }\n *\n * const dataLast = Result.tap((n: number) => {\n * seen = n\n * })(input)\n * // => { _tag: \"Ok\", 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: ResultTap = dual(2, (result: ResultType<unknown, unknown>, fn: (value: unknown) => unknown) => {\n if (result._tag === \"Err\") return result as any\n const sideEffect = fn(result.value)\n if (isPromise(sideEffect)) {\n return Promise.resolve(sideEffect).then(() => result) as any\n }\n return result as any\n})\n/* oxlint-enable no-explicit-any, no-unsafe-return, no-unsafe-type-assertion */\n\n/**\n * Recover from an error by providing an alternative Result.\n * If the Result is successful, it passes through unchanged.\n *\n * Supports both data-first and data-last calling styles:\n * - Data-first: `orElse(result, fn)`\n * - Data-last: `pipe(result, orElse(fn))`\n *\n * Supports both sync and async functions:\n * - Sync fn: returns Result<T | U, E2>\n * - Async fn: returns Promise<Result<T | U, E2>>\n *\n * @param fn - Function that takes the error and returns an alternative Result\n * @returns A function that takes a Result and returns a new Result\n *\n * @example\n * ```ts\n * import { Result } from \"@nicolastoulemont/std\"\n *\n * const input = Result.err(\"missing\") as Result.Result<number, string>\n * const dataFirst = Result.orElse(input, () => Result.ok(0))\n * // => { _tag: \"Ok\", value: 0 }\n *\n * const dataLast = Result.orElse(() => Result.ok(0))(input)\n * // => { _tag: \"Ok\", value: 0 }\n * ```\n *\n * @category Error Handling\n */\n// oxlint-disable-next-line no-explicit-any, no-unsafe-return, no-unsafe-type-assertion -- Required for overloaded return types\nexport const orElse: ResultOrElse = dual(2, (result: ResultType<unknown, unknown>, fn: (error: unknown) => unknown) =>\n result._tag === \"Ok\" ? result : fn(result.error),\n)\n\n/**\n * Filter a successful Result based on a predicate.\n * If the predicate returns false, converts the success to an error.\n * If the Result is already an error, it passes through unchanged.\n *\n * Supports both data-first and data-last calling styles:\n * - Data-first: `filter(result, predicate, onFail)`\n * - Data-last: `pipe(result, filter(predicate, onFail))`\n *\n * @param predicate - Function that returns true to keep the value\n * @param onFail - Function that creates the error when predicate fails\n * @returns A function that takes a Result and returns a new Result\n *\n * @example\n * ```ts\n * import { Result } from \"@nicolastoulemont/std\"\n *\n * const input = Result.ok(3)\n * const dataFirst = Result.filter(input, (n) => n > 0, (n) => `${n} must be positive`)\n * // => { _tag: \"Ok\", value: 3 }\n *\n * const dataLast = Result.filter((n: number) => n > 0, (n) => `${n} must be positive`)(input)\n * // => { _tag: \"Ok\", value: 3 }\n * ```\n *\n * @category Filtering\n */\nexport const filter: ResultFilter = dual(\n 3,\n (\n result: ResultType<unknown, unknown>,\n predicate: (value: unknown) => boolean,\n onFail: (value: unknown) => unknown,\n ) => {\n if (result._tag === \"Err\") return result\n return predicate(result.value) ? result : err(onFail(result.value))\n },\n)\n\n// ============================================================================\n// Combinators\n// ============================================================================\n\n/**\n * Combine multiple Results into a single Result.\n * Supports both array and object inputs.\n *\n * - If all Results are ok, returns ok with all values\n * - If any Result is an error, returns the first error (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 { Result } from \"@nicolastoulemont/std\"\n *\n * const combined = Result.all([Result.ok(1), Result.ok(2)] as const)\n * // => { _tag: \"Ok\", 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: ResultAll = (results: any): any => {\n if (Array.isArray(results)) {\n const values: unknown[] = []\n for (const result of results) {\n if (result._tag === \"Err\") return result\n values.push(result.value)\n }\n return ok(values)\n }\n\n const values: Record<string, unknown> = {}\n for (const [key, result] of Object.entries(results)) {\n const r = result as ResultType<unknown, unknown>\n if (r._tag === \"Err\") return result\n values[key] = r.value\n }\n return ok(values)\n}\n/* oxlint-enable no-explicit-any, no-unsafe-return, no-unsafe-member-access, strict-boolean-expressions, no-unsafe-type-assertion, no-unsafe-argument */\n\n// ============================================================================\n// Extraction\n// ============================================================================\n\n/**\n * Get the success value or a default value.\n *\n * Supports both data-first and data-last calling styles:\n * - Data-first: `unwrapOr(result, defaultValue)`\n * - Data-last: `pipe(result, unwrapOr(defaultValue))`\n *\n * Uses NoInfer to prevent type inference from the default value.\n *\n * @param defaultValue - Value to return if the Result is an error\n * @returns A function that takes a Result and returns the value or default\n *\n * @example\n * ```ts\n * import { Result } from \"@nicolastoulemont/std\"\n *\n * const input = Result.err(\"missing\") as Result.Result<number, string>\n * const dataFirst = Result.unwrapOr(input, 0)\n * // => 0\n *\n * const dataLast = Result.unwrapOr(0)(input)\n * // => 0\n * ```\n *\n * @category Getters\n */\nexport const unwrapOr: ResultUnwrapOr = dual(\n 2,\n <T, E>(result: ResultType<T, E>, defaultValue: NoInfer<T>): T => (result._tag === \"Ok\" ? result.value : defaultValue),\n)\n\n/**\n * Get the success value or compute a value from the error.\n *\n * Supports both data-first and data-last calling styles:\n * - Data-first: `unwrapOrElse(result, fn)`\n * - Data-last: `pipe(result, unwrapOrElse(fn))`\n *\n * @param fn - Function to compute a value from the error\n * @returns A function that takes a Result and returns the value or computed value\n *\n * @example\n * ```ts\n * import { Result } from \"@nicolastoulemont/std\"\n *\n * const input = Result.err(\"missing\") as Result.Result<number, string>\n * const dataFirst = Result.unwrapOrElse(input, () => 0)\n * // => 0\n *\n * const dataLast = Result.unwrapOrElse(() => 0)(input)\n * // => 0\n * ```\n *\n * @category Getters\n */\nexport const unwrapOrElse: ResultUnwrapOrElse = dual(\n 2,\n <T, E>(result: ResultType<T, E>, fn: (error: E) => T): T => (result._tag === \"Ok\" ? result.value : fn(result.error)),\n)\n\n/**\n * Pattern match on a Result, handling both success and error cases.\n *\n * Supports both data-first and data-last calling styles:\n * - Data-first: `match(result, { Ok: ..., Err: ... })`\n * - Data-last: `pipe(result, match({ Ok: ..., Err: ... }))`\n *\n * @param handlers - Object with `Ok` and `Err` handlers\n * @returns A function that takes a Result and returns the handler result\n *\n * @example\n * ```ts\n * import { Result } from \"@nicolastoulemont/std\"\n *\n * const input = Result.ok(2) as Result.Result<number, string>\n * const dataFirst = Result.match(input, {\n * Ok: (value) => `ok:${value}` ,\n * Err: (error) => `err:${error}` ,\n * })\n * // => \"ok:2\"\n *\n * const dataLast = Result.match({\n * Ok: (value: number) => `ok:${value}` ,\n * Err: (error: string) => `err:${error}` ,\n * })(input)\n * // => \"ok:2\"\n * ```\n *\n * @category Pattern Matching\n */\nexport const match: ResultMatch = dual(\n 2,\n <T, E, U>(result: ResultType<T, E>, handlers: { Ok: (value: T) => U; Err: (error: E) => U }): U =>\n result._tag === \"Ok\" ? handlers.Ok(result.value) : handlers.Err(result.error),\n)\n\n// ============================================================================\n// Error Boundary\n// ============================================================================\n\n/**\n * Wrap a function that might throw into a Result.\n * Supports both sync and async functions with automatic type inference.\n *\n * Can be called with a simple callback or an object with `try` and `catch`:\n * - Simple: `fromTry(() => riskyOp())` — errors are wrapped as `Error`\n * - Object: `fromTry({ try: () => riskyOp(), catch: (e) => mapError(e) })` — custom error mapping\n *\n * @param fnOrOptions - Function that might throw, or object with `try` and `catch`\n * @returns A Result with the return value or the caught/mapped error\n *\n * @example\n * ```ts\n * import { Result } from \"@nicolastoulemont/std\"\n *\n * const parsed = Result.fromTry(() => Number.parseInt(\"42\", 10))\n * // => { _tag: \"Ok\", value: 42 }\n * ```\n *\n * @category Constructors\n */\n/* oxlint-disable no-explicit-any, no-unsafe-type-assertion -- Required for overloaded return type */\nexport const fromTry: ResultFromTry = (fnOrOptions: (() => unknown) | FromTryOptions<unknown, unknown>): any => {\n const fn = typeof fnOrOptions === \"function\" ? fnOrOptions : fnOrOptions.try\n const catchFn =\n typeof fnOrOptions === \"function\"\n ? (e: unknown) => (e instanceof Error ? e : new Error(String(e)))\n : fnOrOptions.catch\n const finallyFn = typeof fnOrOptions === \"function\" ? undefined : fnOrOptions.finally\n\n let isAsync = false\n try {\n const result = fn()\n if (isPromise(result)) {\n isAsync = true\n const promise = Promise.resolve(result)\n .then(ok)\n .catch((e: unknown) => err(catchFn(e)))\n return finallyFn ? promise.finally(finallyFn) : promise\n }\n return ok(result)\n } catch (e) {\n return err(catchFn(e))\n } finally {\n if (finallyFn && !isAsync) {\n finallyFn()\n }\n }\n}\n/* oxlint-enable no-explicit-any, no-unsafe-type-assertion */\n\nconst try_ = fromTry\n\nexport {\n /**\n * Alias of {@link fromTry}.\n *\n * @example\n * ```ts\n * import { Result } from \"@nicolastoulemont/std\"\n *\n * const parsed = Result.try(() => JSON.parse(\"{\\\"ok\\\":true}\"))\n * ```\n *\n * @category Constructors\n */\n try_ as try,\n}\n\n/* oxlint-enable no-unsafe-type-assertion */\n"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{t as e}from"./chunk-C934ptG5.mjs";var t=e({exponential:()=>l,fixed:()=>c,recurs:()=>s});const n=e=>{if(!Number.isInteger(e)||e<0)throw RangeError(`Schedule retry count must be an integer >= 0, received: ${String(e)}`)},r=(e,t)=>{if(!Number.isFinite(t)||t<0)throw RangeError(`Schedule ${e} must be a finite number >= 0, received: ${String(t)}`)},i=(e,t)=>{if(!Number.isFinite(t)||t<=0)throw RangeError(`Schedule ${e} must be a finite number > 0, received: ${String(t)}`)},a=(e,t)=>({_tag:`RetrySchedule`,_sync:!0,maxRetries:e,delayForAttempt:t}),o=(e,t)=>({_tag:`RetrySchedule`,_sync:!1,maxRetries:e,delayForAttempt:t}),s=e=>(n(e),a(e,()=>0)),c=e=>(n(e.times),r(`delayMs`,e.delayMs),o(e.times,()=>e.delayMs)),l=e=>{n(e.times),r(`baseDelayMs`,e.baseDelayMs);let t=e.factor??2;return i(`factor`,t),e.maxDelayMs!==void 0&&r(`maxDelayMs`,e.maxDelayMs),o(e.times,n=>{let r=e.baseDelayMs*t**(n-1);return e.maxDelayMs===void 0?r:Math.min(r,e.maxDelayMs)})};export{t};
|
|
2
|
-
//# sourceMappingURL=schedule-C6tjcJ1O.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"schedule-C6tjcJ1O.mjs","names":[],"sources":["../src/schedule/schedule.ts"],"sourcesContent":["/**\n * Retry schedule constructors used by `Fx.retry`.\n *\n * **Mental model**\n * - A schedule defines retry count and per-attempt delay.\n * - Sync schedules have no async delay requirements.\n *\n * **Common tasks**\n * - Use `Schedule.recurs` for immediate retries.\n * - Use `Schedule.fixed` for constant-delay retries.\n * - Use `Schedule.exponential` for backoff.\n *\n * **Gotchas**\n * - `times` is the number of retries after the first attempt.\n * - Delay values must be finite and non-negative.\n *\n * **Quickstart**\n *\n * @example\n * ```ts\n * import { Schedule } from \"@nicolastoulemont/std\"\n *\n * const schedule = Schedule.recurs(2)\n * const delay = schedule.delayForAttempt(1)\n * // => 0\n * ```\n *\n * @module\n */\n/**\n * Retry schedule used by Fx.retry.\n *\n * A schedule defines:\n * - how many retries are allowed after the first attempt\n * - how long to wait before each retry attempt\n *\n * @example\n * ```ts\n * import { Schedule } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Schedule\n * ```\n *\n * @category Models\n */\nexport type RetrySchedule = SyncRetrySchedule | AsyncRetrySchedule\n\n/**\n * Schedule that never introduces async delays.\n *\n * @example\n * ```ts\n * import { Schedule } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Schedule\n * ```\n *\n * @category Models\n */\nexport type SyncRetrySchedule = {\n readonly _tag: \"RetrySchedule\"\n readonly _sync: true\n readonly maxRetries: number\n readonly delayForAttempt: (retryAttempt: number) => number\n}\n\n/**\n * Schedule that may delay retries, requiring async execution.\n *\n * @example\n * ```ts\n * import { Schedule } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Schedule\n * ```\n *\n * @category Models\n */\nexport type AsyncRetrySchedule = {\n readonly _tag: \"RetrySchedule\"\n readonly _sync: false\n readonly maxRetries: number\n readonly delayForAttempt: (retryAttempt: number) => number\n}\n\ntype FixedScheduleOptions = {\n readonly times: number\n readonly delayMs: number\n}\n\ntype ExponentialScheduleOptions = {\n readonly times: number\n readonly baseDelayMs: number\n readonly factor?: number | undefined\n readonly maxDelayMs?: number | undefined\n}\n\nconst validateRetryCount = (times: number) => {\n if (!Number.isInteger(times) || times < 0) {\n throw new RangeError(`Schedule retry count must be an integer >= 0, received: ${String(times)}`)\n }\n}\n\nconst validateNonNegativeFinite = (name: string, value: number) => {\n if (!Number.isFinite(value) || value < 0) {\n throw new RangeError(`Schedule ${name} must be a finite number >= 0, received: ${String(value)}`)\n }\n}\n\nconst validatePositiveFinite = (name: string, value: number) => {\n if (!Number.isFinite(value) || value <= 0) {\n throw new RangeError(`Schedule ${name} must be a finite number > 0, received: ${String(value)}`)\n }\n}\n\nconst makeSyncSchedule = (\n maxRetries: number,\n delayForAttempt: (retryAttempt: number) => number,\n): SyncRetrySchedule => ({\n _tag: \"RetrySchedule\",\n _sync: true,\n maxRetries,\n delayForAttempt,\n})\n\nconst makeAsyncSchedule = (\n maxRetries: number,\n delayForAttempt: (retryAttempt: number) => number,\n): AsyncRetrySchedule => ({\n _tag: \"RetrySchedule\",\n _sync: false,\n maxRetries,\n delayForAttempt,\n})\n\n/**\n * Retry immediately up to `times` times.\n *\n * @example\n * ```ts\n * import { Schedule } from \"@nicolastoulemont/std\"\n *\n * const schedule = Schedule.recurs(2)\n * const delay = schedule.delayForAttempt(1)\n * // => 0\n * ```\n *\n * @category Constructors\n */\nexport const recurs = (times: number): SyncRetrySchedule => {\n validateRetryCount(times)\n return makeSyncSchedule(times, () => 0)\n}\n\n/**\n * Retry with a fixed delay between attempts.\n *\n * @example\n * ```ts\n * import { Schedule } from \"@nicolastoulemont/std\"\n *\n * const schedule = Schedule.fixed({ times: 2, delayMs: 100 })\n * const delay = schedule.delayForAttempt(2)\n * // => 100\n * ```\n *\n * @category Constructors\n */\nexport const fixed = (options: FixedScheduleOptions): AsyncRetrySchedule => {\n validateRetryCount(options.times)\n validateNonNegativeFinite(\"delayMs\", options.delayMs)\n\n return makeAsyncSchedule(options.times, () => options.delayMs)\n}\n\n/**\n * Retry with exponential backoff:\n * `baseDelayMs * factor^(retryAttempt - 1)`, optionally capped by `maxDelayMs`.\n *\n * @example\n * ```ts\n * import { Schedule } from \"@nicolastoulemont/std\"\n *\n * const schedule = Schedule.exponential({ times: 3, baseDelayMs: 100 })\n * const delay = schedule.delayForAttempt(3)\n * // => 400\n * ```\n *\n * @category Constructors\n */\nexport const exponential = (options: ExponentialScheduleOptions): AsyncRetrySchedule => {\n validateRetryCount(options.times)\n validateNonNegativeFinite(\"baseDelayMs\", options.baseDelayMs)\n\n const factor = options.factor ?? 2\n validatePositiveFinite(\"factor\", factor)\n\n if (options.maxDelayMs !== undefined) {\n validateNonNegativeFinite(\"maxDelayMs\", options.maxDelayMs)\n }\n\n return makeAsyncSchedule(options.times, (retryAttempt) => {\n const delay = options.baseDelayMs * Math.pow(factor, retryAttempt - 1)\n return options.maxDelayMs === undefined ? delay : Math.min(delay, options.maxDelayMs)\n })\n}\n"],"mappings":"+FAiGA,MAAM,EAAsB,GAAkB,CAC5C,GAAI,CAAC,OAAO,UAAU,EAAM,EAAI,EAAQ,EACtC,MAAU,WAAW,2DAA2D,OAAO,EAAM,GAAG,EAI9F,GAA6B,EAAc,IAAkB,CACjE,GAAI,CAAC,OAAO,SAAS,EAAM,EAAI,EAAQ,EACrC,MAAU,WAAW,YAAY,EAAK,2CAA2C,OAAO,EAAM,GAAG,EAI/F,GAA0B,EAAc,IAAkB,CAC9D,GAAI,CAAC,OAAO,SAAS,EAAM,EAAI,GAAS,EACtC,MAAU,WAAW,YAAY,EAAK,0CAA0C,OAAO,EAAM,GAAG,EAI9F,GACJ,EACA,KACuB,CACvB,KAAM,gBACN,MAAO,GACP,aACA,kBACD,EAEK,GACJ,EACA,KACwB,CACxB,KAAM,gBACN,MAAO,GACP,aACA,kBACD,EAgBY,EAAU,IACrB,EAAmB,EAAM,CAClB,EAAiB,MAAa,EAAE,EAiB5B,EAAS,IACpB,EAAmB,EAAQ,MAAM,CACjC,EAA0B,UAAW,EAAQ,QAAQ,CAE9C,EAAkB,EAAQ,UAAa,EAAQ,QAAQ,EAkBnD,EAAe,GAA4D,CACtF,EAAmB,EAAQ,MAAM,CACjC,EAA0B,cAAe,EAAQ,YAAY,CAE7D,IAAM,EAAS,EAAQ,QAAU,EAOjC,OANA,EAAuB,SAAU,EAAO,CAEpC,EAAQ,aAAe,IAAA,IACzB,EAA0B,aAAc,EAAQ,WAAW,CAGtD,EAAkB,EAAQ,MAAQ,GAAiB,CACxD,IAAM,EAAQ,EAAQ,YAAuB,IAAQ,EAAe,GACpE,OAAO,EAAQ,aAAe,IAAA,GAAY,EAAQ,KAAK,IAAI,EAAO,EAAQ,WAAW,EACrF"}
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
declare namespace schedule_d_exports {
|
|
2
|
-
export { AsyncRetrySchedule, RetrySchedule, SyncRetrySchedule, exponential, fixed, recurs };
|
|
3
|
-
}
|
|
4
|
-
/**
|
|
5
|
-
* Retry schedule constructors used by `Fx.retry`.
|
|
6
|
-
*
|
|
7
|
-
* **Mental model**
|
|
8
|
-
* - A schedule defines retry count and per-attempt delay.
|
|
9
|
-
* - Sync schedules have no async delay requirements.
|
|
10
|
-
*
|
|
11
|
-
* **Common tasks**
|
|
12
|
-
* - Use `Schedule.recurs` for immediate retries.
|
|
13
|
-
* - Use `Schedule.fixed` for constant-delay retries.
|
|
14
|
-
* - Use `Schedule.exponential` for backoff.
|
|
15
|
-
*
|
|
16
|
-
* **Gotchas**
|
|
17
|
-
* - `times` is the number of retries after the first attempt.
|
|
18
|
-
* - Delay values must be finite and non-negative.
|
|
19
|
-
*
|
|
20
|
-
* **Quickstart**
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* ```ts
|
|
24
|
-
* import { Schedule } from "@nicolastoulemont/std"
|
|
25
|
-
*
|
|
26
|
-
* const schedule = Schedule.recurs(2)
|
|
27
|
-
* const delay = schedule.delayForAttempt(1)
|
|
28
|
-
* // => 0
|
|
29
|
-
* ```
|
|
30
|
-
*
|
|
31
|
-
* @module
|
|
32
|
-
*/
|
|
33
|
-
/**
|
|
34
|
-
* Retry schedule used by Fx.retry.
|
|
35
|
-
*
|
|
36
|
-
* A schedule defines:
|
|
37
|
-
* - how many retries are allowed after the first attempt
|
|
38
|
-
* - how long to wait before each retry attempt
|
|
39
|
-
*
|
|
40
|
-
* @example
|
|
41
|
-
* ```ts
|
|
42
|
-
* import { Schedule } from "@nicolastoulemont/std"
|
|
43
|
-
*
|
|
44
|
-
* type Example = typeof Schedule
|
|
45
|
-
* ```
|
|
46
|
-
*
|
|
47
|
-
* @category Models
|
|
48
|
-
*/
|
|
49
|
-
type RetrySchedule = SyncRetrySchedule | AsyncRetrySchedule;
|
|
50
|
-
/**
|
|
51
|
-
* Schedule that never introduces async delays.
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* ```ts
|
|
55
|
-
* import { Schedule } from "@nicolastoulemont/std"
|
|
56
|
-
*
|
|
57
|
-
* type Example = typeof Schedule
|
|
58
|
-
* ```
|
|
59
|
-
*
|
|
60
|
-
* @category Models
|
|
61
|
-
*/
|
|
62
|
-
type SyncRetrySchedule = {
|
|
63
|
-
readonly _tag: "RetrySchedule";
|
|
64
|
-
readonly _sync: true;
|
|
65
|
-
readonly maxRetries: number;
|
|
66
|
-
readonly delayForAttempt: (retryAttempt: number) => number;
|
|
67
|
-
};
|
|
68
|
-
/**
|
|
69
|
-
* Schedule that may delay retries, requiring async execution.
|
|
70
|
-
*
|
|
71
|
-
* @example
|
|
72
|
-
* ```ts
|
|
73
|
-
* import { Schedule } from "@nicolastoulemont/std"
|
|
74
|
-
*
|
|
75
|
-
* type Example = typeof Schedule
|
|
76
|
-
* ```
|
|
77
|
-
*
|
|
78
|
-
* @category Models
|
|
79
|
-
*/
|
|
80
|
-
type AsyncRetrySchedule = {
|
|
81
|
-
readonly _tag: "RetrySchedule";
|
|
82
|
-
readonly _sync: false;
|
|
83
|
-
readonly maxRetries: number;
|
|
84
|
-
readonly delayForAttempt: (retryAttempt: number) => number;
|
|
85
|
-
};
|
|
86
|
-
type FixedScheduleOptions = {
|
|
87
|
-
readonly times: number;
|
|
88
|
-
readonly delayMs: number;
|
|
89
|
-
};
|
|
90
|
-
type ExponentialScheduleOptions = {
|
|
91
|
-
readonly times: number;
|
|
92
|
-
readonly baseDelayMs: number;
|
|
93
|
-
readonly factor?: number | undefined;
|
|
94
|
-
readonly maxDelayMs?: number | undefined;
|
|
95
|
-
};
|
|
96
|
-
/**
|
|
97
|
-
* Retry immediately up to `times` times.
|
|
98
|
-
*
|
|
99
|
-
* @example
|
|
100
|
-
* ```ts
|
|
101
|
-
* import { Schedule } from "@nicolastoulemont/std"
|
|
102
|
-
*
|
|
103
|
-
* const schedule = Schedule.recurs(2)
|
|
104
|
-
* const delay = schedule.delayForAttempt(1)
|
|
105
|
-
* // => 0
|
|
106
|
-
* ```
|
|
107
|
-
*
|
|
108
|
-
* @category Constructors
|
|
109
|
-
*/
|
|
110
|
-
declare const recurs: (times: number) => SyncRetrySchedule;
|
|
111
|
-
/**
|
|
112
|
-
* Retry with a fixed delay between attempts.
|
|
113
|
-
*
|
|
114
|
-
* @example
|
|
115
|
-
* ```ts
|
|
116
|
-
* import { Schedule } from "@nicolastoulemont/std"
|
|
117
|
-
*
|
|
118
|
-
* const schedule = Schedule.fixed({ times: 2, delayMs: 100 })
|
|
119
|
-
* const delay = schedule.delayForAttempt(2)
|
|
120
|
-
* // => 100
|
|
121
|
-
* ```
|
|
122
|
-
*
|
|
123
|
-
* @category Constructors
|
|
124
|
-
*/
|
|
125
|
-
declare const fixed: (options: FixedScheduleOptions) => AsyncRetrySchedule;
|
|
126
|
-
/**
|
|
127
|
-
* Retry with exponential backoff:
|
|
128
|
-
* `baseDelayMs * factor^(retryAttempt - 1)`, optionally capped by `maxDelayMs`.
|
|
129
|
-
*
|
|
130
|
-
* @example
|
|
131
|
-
* ```ts
|
|
132
|
-
* import { Schedule } from "@nicolastoulemont/std"
|
|
133
|
-
*
|
|
134
|
-
* const schedule = Schedule.exponential({ times: 3, baseDelayMs: 100 })
|
|
135
|
-
* const delay = schedule.delayForAttempt(3)
|
|
136
|
-
* // => 400
|
|
137
|
-
* ```
|
|
138
|
-
*
|
|
139
|
-
* @category Constructors
|
|
140
|
-
*/
|
|
141
|
-
declare const exponential: (options: ExponentialScheduleOptions) => AsyncRetrySchedule;
|
|
142
|
-
//#endregion
|
|
143
|
-
export { SyncRetrySchedule as n, schedule_d_exports as r, RetrySchedule as t };
|
|
144
|
-
//# sourceMappingURL=schedule-DlX2Dg69.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"schedule-DlX2Dg69.d.mts","names":[],"sources":["../src/schedule/schedule.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;AA6CA;AAcA;AAmBA;AAKC;AAEwB;AAgEzB;AAmBA;AAsBA;;;;;;;;;;;;;;;;;;;;;;;;;;;KAjJY,aAAA,GAAgB,oBAAoB;;;;;;;;;;;;;KAcpC,iBAAA;;;;;;;;;;;;;;;;;;KAmBA,kBAAA;;;;;;KAOP,oBAAA;;;;KAKA,0BAAA;;;;;;;;;;;;;;;;;;;;cA2DQ,2BAA0B;;;;;;;;;;;;;;;cAmB1B,iBAAkB,yBAAuB;;;;;;;;;;;;;;;;cAsBzC,uBAAwB,+BAA6B"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scope-CZdp4wKX.d.mts","names":[],"sources":["../src/scope/scope.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;;AAuDA;;;;;AAgByC,KAhB7B,YAAA,GAgB6B;EAAe;;;AA4BxD;AA8HA;;;EAA8B,YAAA,CAAA,SAAA,EAAA,GAAA,GAlKE,MAkKF,CAAA,IAAA,CAAA,GAlKiB,OAkKjB,CAAA,IAAA,CAAA,CAAA,EAlKiC,MAkKjC,CAAA,IAAA,CAAA;EAAO;;;;;;cA1JvB,2BAA2B,eAAe;;;;;;;UAQ9C;;;;;;;;;;;;;;;;;;;iBAoBM,IAAA,CAAA,GAAQ;;;;;;;;;;;;;;KA8HZ,kBAAkB,QAAQ,GAAG"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scope-D_kzd1nT.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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"service-3PYQTUdH.mjs","names":["tag"],"sources":["../src/service/service.ts"],"sourcesContent":["/**\n * Service tag constructors for `Fx` dependency injection.\n *\n * **Mental model**\n * - A service tag is both a runtime lookup key and a typed requirement marker.\n * - `Service()` and `tag()` create values that can be yielded in `Fx.gen`.\n *\n * **Common tasks**\n * - Define class-based services with `Service`.\n * - Define interface-only tags with `tag`.\n *\n * **Gotchas**\n * - Service keys must be globally unique in a context.\n * - Yielding a service tag requires the service to be provided.\n *\n * **Quickstart**\n *\n * @example\n * ```ts\n * import { Service } from \"@nicolastoulemont/std\"\n *\n * const Logger = Service.Service<{ log: (message: string) => void }>()(\"Logger\")\n * // => service class with key \"Logger\"\n * ```\n *\n * @module\n */\nimport { FxTypeId } from \"../fx/fx.types\"\nimport type { Fx } from \"../fx/fx.types\"\n\n/* oxlint-disable no-unsafe-type-assertion -- Service tags carry phantom generic metadata at runtime and require explicit type assertions. */\n\n// ============================================================================\n// Service Class Types\n// ============================================================================\n\n/**\n * The interface returned by Service()().\n * Acts as both a type tag and a runtime lookup key.\n * Implements Fx protocol with E = never (no errors) and R = Self (requires itself).\n * When yielded, yields itself (as Fx) for proper type inference.\n *\n * @example\n * ```ts\n * import { Service } from \"@nicolastoulemont/std\"\n *\n * type Example = typeof Service\n * ```\n *\n * @category Models\n */\nexport type ServiceClass<Self, Key extends string = string> = Fx<Self, never, Self> & {\n readonly _tag: \"Service\"\n readonly key: Key\n // Phantom type\n readonly _Self: Self\n\n /**\n * Yielding the service class in a gen computation\n * returns the service instance from context.\n * Yields the ServiceClass itself (which implements Fx) for type inference.\n */\n [Symbol.iterator](): Generator<ServiceClass<Self, Key>, Self, unknown>\n}\n\n// ============================================================================\n// Service Factory\n// ============================================================================\n\n/**\n * Define a service with a unique key.\n * The returned class acts as both a type and a runtime tag for lookup.\n *\n * Usage follows a double-invocation pattern:\n * - First call provides the Self type parameter\n * - Second call provides the unique key\n *\n * @example\n * ```ts\n * import { Service } from \"@nicolastoulemont/std\"\n *\n * const Logger = Service.Service<string>()(\"Logger\")\n * // => service class with key \"Logger\"\n * ```\n *\n * @category Utilities\n */\nexport function Service<Self>(): <Key extends string>(key: Key) => ServiceClass<Self, Key> & (new () => Self) {\n return <Key extends string>(key: Key): ServiceClass<Self, Key> & (new () => Self) => {\n // Create the base class with proper prototype\n // oxlint-disable-next-line typescript/no-extraneous-class\n const ServiceBase = class {\n static readonly _tag = \"Service\" as const\n static readonly key: Key = key\n static readonly _Self: Self = undefined as unknown as Self\n\n static readonly [FxTypeId] = {\n _A: () => undefined as unknown as Self,\n _E: () => undefined as never,\n _R: () => undefined as unknown as Self,\n };\n\n /**\n * Yielding the class returns the class itself (for type inference).\n * The runtime intercepts this and provides the actual service instance.\n */\n static *[Symbol.iterator](): Generator<typeof ServiceBase, Self, Self> {\n // Yield the class itself - it implements Fx<Self, never, Self>\n // The runtime will recognize ServiceClass and inject the service\n const serviceInstance = yield ServiceBase\n return serviceInstance\n }\n }\n\n // Return with correct typing\n return ServiceBase as unknown as ServiceClass<Self, Key> & (new () => Self)\n }\n}\n\n/**\n * Create a service tag without class syntax.\n * Useful for simple services that don't need class inheritance.\n *\n * @example\n * ```ts\n * import { Service } from \"@nicolastoulemont/std\"\n *\n * const Logger = Service.tag<string>(\"Logger\")\n * // => plain service tag with key \"Logger\"\n * ```\n *\n * @category Utilities\n */\nexport function tag<S>(key: string): ServiceClass<S> {\n const tag: ServiceClass<S> = {\n _tag: \"Service\",\n key,\n _Self: undefined as unknown as S,\n [FxTypeId]: {\n _A: () => undefined as unknown as S,\n _E: () => undefined as never,\n _R: () => undefined as unknown as S,\n },\n *[Symbol.iterator](): Generator<ServiceClass<S>, S, S> {\n // Yield the tag itself - it implements Fx<S, never, S>\n // The runtime will recognize ServiceClass and inject the service\n const serviceInstance = yield tag\n return serviceInstance\n },\n }\n return tag\n}\n\n/* oxlint-enable no-unsafe-type-assertion */\n"],"mappings":"mHAuFA,SAAgB,GAA8F,CAC5G,MAA4B,IAAyD,CAGnF,IAAM,EAAc,KAAM,CACxB,OAAgB,KAAO,UACvB,OAAgB,IAAW,EAC3B,OAAgB,MAAc,IAAA,GAE9B,OAAiB,GAAY,CAC3B,OAAU,IAAA,GACV,OAAU,IAAA,GACV,OAAU,IAAA,GACX,CAMD,QAAS,OAAO,WAAuD,CAIrE,OADwB,MAAM,IAMlC,OAAO,GAkBX,SAAgB,EAAO,EAA8B,CACnD,IAAMA,EAAuB,CAC3B,KAAM,UACN,MACA,MAAO,IAAA,IACN,GAAW,CACV,OAAU,IAAA,GACV,OAAU,IAAA,GACV,OAAU,IAAA,GACX,CACD,EAAE,OAAO,WAA8C,CAIrD,OADwB,MAAMA,GAGjC,CACD,OAAOA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"service-DrXU7KJG.d.mts","names":[],"sources":["../src/service/service.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;;;;AAmDA;;;;;;;AAWoD,KAXxC,YAWwC,CAAA,IAAA,EAAA,YAAA,MAAA,GAAA,MAAA,CAAA,GAXU,EAWV,CAXa,IAWb,EAAA,KAAA,EAX0B,IAW1B,CAAA,GAAA;EAAnB,SAAA,IAAA,EAAA,SAAA;EAAyB,SAAA,GAAA,EAT1C,GAS0C;EAAnC,SAAA,KAAA,EAPL,IAOK;EAAS;AAyBhC;;;;EAAmE,CAAA,MAAA,CAAA,QAAA,GAAA,EAzB5C,SAyB4C,CAzBlC,YAyBkC,CAzBrB,IAyBqB,EAzBf,GAyBe,CAAA,EAzBT,IAyBS,EAAA,OAAA,CAAA;CAAqC;;AA8CxG;;;;;;;;;;;;;;;;;iBA9CgB,2CAA2C,QAAQ,aAAa,MAAM,kBAAkB;;;;;;;;;;;;;;;iBA8CxF,qBAAqB,aAAa"}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|