awaitly 1.31.1 → 1.32.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/dist/adapters.cjs +2 -2
- package/dist/adapters.cjs.map +1 -1
- package/dist/adapters.d.cts +3 -1
- package/dist/adapters.d.ts +3 -1
- package/dist/adapters.js +2 -2
- package/dist/adapters.js.map +1 -1
- package/dist/batch.cjs +2 -2
- package/dist/batch.cjs.map +1 -1
- package/dist/batch.d.cts +3 -1
- package/dist/batch.d.ts +3 -1
- package/dist/batch.js +2 -2
- package/dist/batch.js.map +1 -1
- package/dist/circuit-breaker.cjs +2 -2
- package/dist/circuit-breaker.cjs.map +1 -1
- package/dist/circuit-breaker.d.cts +3 -1
- package/dist/circuit-breaker.d.ts +3 -1
- package/dist/circuit-breaker.js +2 -2
- package/dist/circuit-breaker.js.map +1 -1
- package/dist/conditional.d.cts +3 -1
- package/dist/conditional.d.ts +3 -1
- package/dist/core.cjs +2 -2
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +2 -1
- package/dist/core.d.ts +2 -1
- package/dist/core.js +2 -2
- package/dist/core.js.map +1 -1
- package/dist/diagnostics.cjs +3 -3
- package/dist/diagnostics.cjs.map +1 -1
- package/dist/diagnostics.d.cts +3 -1
- package/dist/diagnostics.d.ts +3 -1
- package/dist/diagnostics.js +3 -3
- package/dist/diagnostics.js.map +1 -1
- package/dist/durable.cjs +3 -3
- package/dist/durable.cjs.map +1 -1
- package/dist/durable.d.cts +8 -361
- package/dist/durable.d.ts +8 -361
- package/dist/durable.js +3 -3
- package/dist/durable.js.map +1 -1
- package/dist/engine.cjs +11 -0
- package/dist/engine.cjs.map +1 -0
- package/dist/engine.d.cts +114 -0
- package/dist/engine.d.ts +114 -0
- package/dist/engine.js +11 -0
- package/dist/engine.js.map +1 -0
- package/dist/errors-entry-CMH73Eym.d.cts +339 -0
- package/dist/errors-entry-DOt5UUl4.d.ts +339 -0
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.cts +2 -318
- package/dist/errors.d.ts +2 -318
- package/dist/errors.js +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/fetch.cjs +2 -2
- package/dist/fetch.cjs.map +1 -1
- package/dist/fetch.d.cts +3 -1
- package/dist/fetch.d.ts +3 -1
- package/dist/fetch.js +2 -2
- package/dist/fetch.js.map +1 -1
- package/dist/functional.cjs +1 -1
- package/dist/functional.cjs.map +1 -1
- package/dist/functional.d.cts +2 -0
- package/dist/functional.d.ts +2 -0
- package/dist/functional.js +1 -1
- package/dist/functional.js.map +1 -1
- package/dist/{guards-PU64_GKv.d.cts → guards-B79mP5Q8.d.cts} +3 -3
- package/dist/{guards-B5lgMJq0.d.ts → guards-BUq6NJCM.d.ts} +3 -3
- package/dist/{hitl-Dyiy0R1v.d.cts → hitl-BCqkMAHw.d.cts} +2 -2
- package/dist/{hitl-BjeSm1sJ.d.ts → hitl-BZtPx0aU.d.ts} +2 -2
- package/dist/hitl.cjs +2 -2
- package/dist/hitl.cjs.map +1 -1
- package/dist/hitl.d.cts +9 -6
- package/dist/hitl.d.ts +9 -6
- package/dist/hitl.js +2 -2
- package/dist/hitl.js.map +1 -1
- package/dist/index-BVUAOWGG.d.ts +417 -0
- package/dist/index-CQnpmUcC.d.cts +417 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/otel.d.cts +3 -1
- package/dist/otel.d.ts +3 -1
- package/dist/{persistence-entry-B3ukwbb5.d.ts → persistence-entry-ClFhxd2Q.d.ts} +11 -5
- package/dist/{persistence-entry-CgVzswbg.d.cts → persistence-entry-jflY61az.d.cts} +11 -5
- package/dist/persistence.d.cts +6 -3
- package/dist/persistence.d.ts +6 -3
- package/dist/policies.d.cts +3 -1
- package/dist/policies.d.ts +3 -1
- package/dist/ratelimit.cjs +2 -2
- package/dist/ratelimit.cjs.map +1 -1
- package/dist/ratelimit.d.cts +3 -1
- package/dist/ratelimit.d.ts +3 -1
- package/dist/ratelimit.js +2 -2
- package/dist/ratelimit.js.map +1 -1
- package/dist/reliability.cjs +2 -2
- package/dist/reliability.cjs.map +1 -1
- package/dist/reliability.d.cts +3 -1
- package/dist/reliability.d.ts +3 -1
- package/dist/reliability.js +2 -2
- package/dist/reliability.js.map +1 -1
- package/dist/resolver.cjs +2 -2
- package/dist/resolver.cjs.map +1 -1
- package/dist/resolver.d.cts +5 -3
- package/dist/resolver.d.ts +5 -3
- package/dist/resolver.js +2 -2
- package/dist/resolver.js.map +1 -1
- package/dist/resource.cjs +2 -2
- package/dist/resource.cjs.map +1 -1
- package/dist/resource.d.cts +3 -1
- package/dist/resource.d.ts +3 -1
- package/dist/resource.js +2 -2
- package/dist/resource.js.map +1 -1
- package/dist/result/retry.cjs +1 -1
- package/dist/result/retry.cjs.map +1 -1
- package/dist/result/retry.d.cts +2 -0
- package/dist/result/retry.d.ts +2 -0
- package/dist/result/retry.js +1 -1
- package/dist/result/retry.js.map +1 -1
- package/dist/result.cjs +1 -1
- package/dist/result.cjs.map +1 -1
- package/dist/result.d.cts +8 -23
- package/dist/result.d.ts +8 -23
- package/dist/result.js +1 -1
- package/dist/result.js.map +1 -1
- package/dist/{run-entry-C1uFytM6.d.cts → run-entry-rw7zll_G.d.ts} +16 -45
- package/dist/{run-entry-C1uFytM6.d.ts → run-entry-yQu63Yj9.d.cts} +16 -45
- package/dist/run.cjs +2 -2
- package/dist/run.cjs.map +1 -1
- package/dist/run.d.cts +3 -1
- package/dist/run.d.ts +3 -1
- package/dist/run.js +2 -2
- package/dist/run.js.map +1 -1
- package/dist/saga.cjs +2 -2
- package/dist/saga.cjs.map +1 -1
- package/dist/saga.d.cts +3 -1
- package/dist/saga.d.ts +3 -1
- package/dist/saga.js +2 -2
- package/dist/saga.js.map +1 -1
- package/dist/singleflight.d.cts +3 -1
- package/dist/singleflight.d.ts +3 -1
- package/dist/streaming.cjs +4 -4
- package/dist/streaming.cjs.map +1 -1
- package/dist/streaming.d.cts +5 -3
- package/dist/streaming.d.ts +5 -3
- package/dist/streaming.js +4 -4
- package/dist/streaming.js.map +1 -1
- package/dist/testing.cjs +8 -4
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +87 -4
- package/dist/testing.d.ts +87 -4
- package/dist/testing.js +8 -4
- package/dist/testing.js.map +1 -1
- package/dist/{types-wa_wTOn3.d.ts → types-CuWK5AlK.d.ts} +1 -1
- package/dist/{types-DUWrNIJu.d.cts → types-uR3JpwvF.d.cts} +1 -1
- package/dist/webhook.cjs +2 -2
- package/dist/webhook.cjs.map +1 -1
- package/dist/webhook.d.cts +7 -4
- package/dist/webhook.d.ts +7 -4
- package/dist/webhook.js +2 -2
- package/dist/webhook.js.map +1 -1
- package/dist/workflow.cjs +3 -3
- package/dist/workflow.cjs.map +1 -1
- package/dist/workflow.d.cts +47 -11
- package/dist/workflow.d.ts +47 -11
- package/dist/workflow.js +3 -3
- package/dist/workflow.js.map +1 -1
- package/package.json +15 -1
package/dist/result/retry.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var l=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var y=Object.getOwnPropertyNames;var w=Object.prototype.hasOwnProperty;var C=(n,e)=>{for(var t in e)l(n,t,{get:e[t],enumerable:!0})},x=(n,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of y(e))!w.call(n,o)&&o!==t&&l(n,o,{get:()=>e[o],enumerable:!(s=p(e,o))||s.enumerable});return n};var d=n=>x(l({},"__esModule",{value:!0}),n);var A={};C(A,{tryAsyncRetry:()=>m});module.exports=d(A);function c(n){return{ok:!0,value:n}}function i(n,e){let t=e?.cause;return{ok:!1,error:n,...t!==void 0?{cause:t}:{}}}async function m(n,e,t){let s=typeof e=="function"?e:void 0,u=(typeof e=="function"?t:e).retry,T=r=>{switch(u.backoff){case"linear":return u.delayMs*(r+1);case"exponential":return u.delayMs*2**r;default:return u.delayMs}},k=r=>new Promise(R=>setTimeout(R,r)),E=async()=>{try{return c(await n())}catch(r){return s?i(s(r),{cause:r}):i(r)}},a=await E(),f=u.shouldRetry??(()=>!0);for(let r=0;r<u.times&&!(a.ok||!f(a.error));r++)await k(T(r)),a=await E();return a}0&&(module.exports={tryAsyncRetry});
|
|
2
2
|
//# sourceMappingURL=retry.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/result/retry.ts","../../src/result/index.ts"],"sourcesContent":["/**\n * awaitly/result retry support\n *\n * Retry async operations with configurable backoff without the full workflow engine.\n */\n\nimport type { AsyncResult } from \"./index\";\nimport { ok, err } from \"./index\";\n\n/** Configuration for retry behavior */\nexport type RetryConfig<E = unknown> = {\n /** Number of retry attempts (not including the initial attempt) */\n times: number;\n /** Base delay between retries in milliseconds */\n delayMs: number;\n /** Backoff strategy */\n backoff?: \"constant\" | \"linear\" | \"exponential\";\n /** Predicate to determine if an error should trigger a retry. Defaults to always retry. */\n shouldRetry?: (error: E) => boolean;\n};\n\n/**\n * Wraps an async function that might throw into an AsyncResult, with retry support.\n *\n * @remarks When to use: Wrap async work with retry logic for transient failures without needing the full workflow engine.\n *\n * @example\n * ```typescript\n * const result = await tryAsyncRetry(\n * () => fetch('/api/data').then(r => r.json()),\n * { retry: { times: 3, delayMs: 100, backoff: 'exponential' } }\n * );\n * ```\n */\nexport function tryAsyncRetry<T>(\n fn: () => Promise<T>,\n config: { retry: RetryConfig<unknown> }\n): AsyncResult<T, unknown>;\nexport function tryAsyncRetry<T, E>(\n fn: () => Promise<T>,\n onError: (cause: unknown) => E,\n config: { retry: RetryConfig<E> }\n): AsyncResult<T, E>;\nexport async function tryAsyncRetry<T, E>(\n fn: () => Promise<T>,\n onErrorOrConfig: ((cause: unknown) => E) | { retry: RetryConfig<unknown> },\n maybeConfig?: { retry: RetryConfig<E> }\n): AsyncResult<T, E | unknown> {\n const onError = typeof onErrorOrConfig === \"function\" ? onErrorOrConfig : undefined;\n const config = typeof onErrorOrConfig === \"function\" ? maybeConfig! : onErrorOrConfig;\n const retry = config.retry;\n\n const getDelay = (attempt: number): number => {\n switch (retry.backoff) {\n case \"linear\":\n return retry.delayMs * (attempt + 1);\n case \"exponential\":\n return retry.delayMs * 2 ** attempt;\n case \"constant\":\n default:\n return retry.delayMs;\n }\n };\n\n const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\n\n const execute = async (): AsyncResult<T, E | unknown> => {\n try {\n return ok(await fn());\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n };\n\n let result = await execute();\n const shouldRetryFn = retry.shouldRetry ?? (() => true);\n\n for (let attempt = 0; attempt < retry.times; attempt++) {\n if (result.ok) break;\n if (!shouldRetryFn(result.error as E)) break;\n await sleep(getDelay(attempt));\n result = await execute();\n }\n\n return result;\n}\n","/**\n * awaitly/result (internal)\n *\n * Core Result primitives - minimal bundle for typed error handling.\n * This file is intentionally kept small for optimal tree-shaking.\n * The full orchestration (run, step, etc.) lives in core.ts.\n */\n\n// =============================================================================\n// Core Result Types\n// =============================================================================\n\n/**\n * Represents a successful result.\n * Use `ok(value)` to create instances.\n */\nexport type Ok<T> = {\n ok: true;\n value: T;\n};\n\n/**\n * Represents a failed result.\n * Use `err(error)` to create instances.\n */\nexport type Err<E, C = unknown> = {\n ok: false;\n error: E;\n cause?: C;\n};\n\n/**\n * Represents a successful computation or a failed one.\n */\nexport type Result<T, E = unknown, C = unknown> = Ok<T> | Err<E, C>;\n\n/**\n * A Promise that resolves to a Result.\n */\nexport type AsyncResult<T, E = unknown, C = unknown> = Promise<Result<T, E, C>>;\n\nexport type UnexpectedStepFailureCause =\n | {\n type: \"STEP_FAILURE\";\n origin: \"result\";\n error: unknown;\n cause?: unknown;\n }\n | {\n type: \"STEP_FAILURE\";\n origin: \"throw\";\n error: unknown;\n thrown: unknown;\n };\n\nexport type UnexpectedCause =\n | { type: \"UNCAUGHT_EXCEPTION\"; thrown: unknown }\n | UnexpectedStepFailureCause;\n\n/** Discriminant for UnexpectedError type - use in switch statements */\nexport const UNEXPECTED_ERROR = \"UNEXPECTED_ERROR\" as const;\n\n/** Discriminant for PromiseRejectedError type - use in switch statements */\nexport const PROMISE_REJECTED = \"PROMISE_REJECTED\" as const;\n\n// =============================================================================\n// Named Error Constants (for static analysis)\n// =============================================================================\n\n/**\n * Named error constant for unexpected/unhandled errors.\n * Used by the analyzer when a step doesn't declare errors.\n */\nexport const AWAITLY_UNEXPECTED = \"AWAITLY_UNEXPECTED\" as const;\n\n/**\n * Named error constant for cancelled operations.\n */\nexport const AWAITLY_CANCELLED = \"AWAITLY_CANCELLED\" as const;\n\n/**\n * Named error constant for timed-out operations.\n */\nexport const AWAITLY_TIMEOUT = \"AWAITLY_TIMEOUT\" as const;\n\n// =============================================================================\n// Static Analysis Helpers\n// =============================================================================\n\n/**\n * Helper to create a tuple of string literal tags with preserved literal types.\n * Use this when you need to store error tags in a variable while keeping\n * TypeScript's literal type inference (avoiding widening to string[]).\n *\n * @param t - The string literal tags\n * @returns The same array with preserved literal types\n *\n * @example\n * ```typescript\n * // Without tags() - type widens to string[]\n * const errs = ['CART_NOT_FOUND', 'CART_EMPTY']; // string[]\n *\n * // With tags() - literal types preserved\n * const errs = tags('CART_NOT_FOUND', 'CART_EMPTY'); // readonly ['CART_NOT_FOUND', 'CART_EMPTY']\n *\n * await step('getCart', () => getCart(id), {\n * errors: errs, // Analyzer can extract literal types\n * out: 'cart',\n * });\n * ```\n */\nexport const tags = <const T extends readonly string[]>(...t: T): T => t;\n\nexport type UnexpectedError = {\n type: typeof UNEXPECTED_ERROR;\n cause: UnexpectedCause;\n};\nexport type PromiseRejectedError = { type: typeof PROMISE_REJECTED; cause: unknown };\n/** Cause type for promise rejections in async batch helpers */\nexport type PromiseRejectionCause = { type: \"PROMISE_REJECTION\"; reason: unknown };\nexport type EmptyInputError = { type: \"EMPTY_INPUT\"; message: string };\nexport type MaybeAsyncResult<T, E, C = unknown> = Result<T, E, C> | Promise<Result<T, E, C>>;\n\n// =============================================================================\n// Result Constructors\n// =============================================================================\n\n/**\n * Creates a successful Result.\n *\n * @remarks When to use: Wrap a successful value in a Result for consistent return types.\n */\nexport function ok(): Ok<void>;\nexport function ok<T>(value: T): Ok<T>;\nexport function ok<T>(value?: T): Ok<T | void> {\n return { ok: true as const, value: value as T | void };\n}\n\n/**\n * Creates a failed Result.\n *\n * @remarks When to use: Return a typed failure without throwing so callers can handle it explicitly.\n */\nexport function err<E, C = unknown>(error: E, options?: { cause?: C }): Err<E, C> {\n const cause = options?.cause;\n return { ok: false as const, error, ...(cause !== undefined ? { cause } : {}) } as Err<E, C>;\n}\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\n/**\n * Checks if a Result is successful.\n *\n * @remarks When to use: Prefer functional-style checks or array filtering over `result.ok`.\n */\nexport const isOk = <T, E, C>(r: Result<T, E, C>): r is Ok<T> => r.ok;\n\n/**\n * Checks if a Result is a failure.\n *\n * @remarks When to use: Prefer functional-style checks or array filtering over `result.ok`.\n */\nexport const isErr = <T, E, C>(r: Result<T, E, C>): r is Err<E, C> => !r.ok;\n\n/**\n * Checks if an error is an UnexpectedError.\n *\n * @remarks When to use: Distinguish unexpected failures from your typed error union.\n */\nexport const isUnexpectedError = (e: unknown): e is UnexpectedError =>\n typeof e === \"object\" &&\n e !== null &&\n \"type\" in e &&\n e.type === UNEXPECTED_ERROR;\n\n/**\n * Checks if an error is a PromiseRejectedError.\n */\nexport const isPromiseRejectedError = (e: unknown): e is PromiseRejectedError =>\n typeof e === \"object\" &&\n e !== null &&\n \"type\" in e &&\n e.type === PROMISE_REJECTED;\n\n// =============================================================================\n// Error Matching\n// =============================================================================\n\nexport type MatchErrorHandlers<E extends string, R> = {\n [K in E]: (error: K extends \"UNEXPECTED_ERROR\" ? UnexpectedError : K) => R;\n};\n\n/**\n * Match on string error types with exhaustive checking.\n * Takes an error value (not a Result) and handlers for each error type.\n */\nexport function matchError<E extends string, R>(\n error: E | UnexpectedError,\n handlers: MatchErrorHandlers<E, R>\n): R {\n // Handle UnexpectedError objects\n if (isUnexpectedError(error)) {\n return (handlers as MatchErrorHandlers<\"UNEXPECTED_ERROR\", R>).UNEXPECTED_ERROR(error);\n }\n // Handle the string literal \"UNEXPECTED_ERROR\" - wrap it in an UnexpectedError object\n // to maintain the typed contract that UNEXPECTED_ERROR handler receives an object\n if (error === \"UNEXPECTED_ERROR\") {\n const syntheticError: UnexpectedError = {\n type: UNEXPECTED_ERROR,\n cause: { type: \"UNCAUGHT_EXCEPTION\", thrown: error },\n };\n return (handlers as MatchErrorHandlers<\"UNEXPECTED_ERROR\", R>).UNEXPECTED_ERROR(syntheticError);\n }\n // Cast to the excluded type since we've handled UNEXPECTED_ERROR above\n type StringErrors = Exclude<E, \"UNEXPECTED_ERROR\">;\n return (handlers as unknown as Record<string, (e: string) => R>)[error as StringErrors](error as StringErrors);\n}\n\n// =============================================================================\n// Type Utilities\n// =============================================================================\n\ntype AnyFunction = (...args: never[]) => unknown;\n\n/**\n * Helper to extract the error type from Result or AsyncResult return values.\n * Works even when a function is declared to return a union of both forms.\n */\ntype ErrorOfReturn<R> = Extract<Awaited<R>, { ok: false }> extends { error: infer E }\n ? E\n : never;\n\n/**\n * Extract error type from a single function's return type\n */\nexport type ErrorOf<T extends AnyFunction> = ErrorOfReturn<ReturnType<T>>;\n\n/**\n * Extract union of error types from multiple functions\n */\nexport type Errors<T extends AnyFunction[]> = {\n [K in keyof T]: ErrorOf<T[K]>;\n}[number];\n\n/**\n * Extract value type from Result\n */\nexport type ExtractValue<T> = T extends { ok: true; value: infer U }\n ? U\n : never;\n\n/**\n * Extract error type from Result\n */\nexport type ExtractError<T> = T extends { ok: false; error: infer E }\n ? E\n : never;\n\n/**\n * Extract cause type from Result\n */\nexport type ExtractCause<T> = T extends { ok: false; cause?: infer C }\n ? C\n : never;\n\n/**\n * Helper to extract the cause type from Result or AsyncResult return values.\n * Works even when a function is declared to return a union of both forms.\n */\ntype CauseOfReturn<R> = Extract<Awaited<R>, { ok: false }> extends { cause?: infer C }\n ? C\n : never;\n\n/**\n * Extract cause type from a function's return type\n */\nexport type CauseOf<T extends AnyFunction> = CauseOfReturn<ReturnType<T>>;\n\n// =============================================================================\n// Unwrap Utilities\n// =============================================================================\n\n/**\n * Error thrown when attempting to unwrap an Err result.\n */\nexport class UnwrapError extends Error {\n public readonly error: unknown;\n public readonly cause?: unknown;\n\n constructor(result: Err<unknown, unknown>) {\n const errorStr =\n typeof result.error === \"string\"\n ? result.error\n : JSON.stringify(result.error);\n super(`Attempted to unwrap an Err: ${errorStr}`);\n this.name = \"UnwrapError\";\n this.error = result.error;\n this.cause = result.cause;\n }\n}\n\n/**\n * Extracts the value from an Ok result, or throws UnwrapError if it's an Err.\n *\n * @remarks When to use: Only at boundaries or tests where a failure should be fatal.\n */\nexport const unwrap = <T, E, C>(r: Result<T, E, C>): T => {\n if (r.ok) return r.value;\n throw new UnwrapError(r);\n};\n\n/**\n * Extracts the value from an Ok result, or returns a default value if it's an Err.\n *\n * @remarks When to use: Provide a safe fallback without branching.\n */\nexport const unwrapOr = <T, E, C>(r: Result<T, E, C>, defaultValue: T): T =>\n r.ok ? r.value : defaultValue;\n\n/**\n * Extracts the value from an Ok result, or calls a function to get a default value if it's an Err.\n *\n * @remarks When to use: Compute a fallback from the error (logging, metrics, or derived defaults).\n */\nexport const unwrapOrElse = <T, E, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => T\n): T => (r.ok ? r.value : fn(r.error, r.cause));\n\n/**\n * Alias for `unwrap`. Returns the success value or throws.\n *\n * The Result is already computed; use when you want the value or throw (e.g. at boundaries or in tests).\n *\n * @param r - The Result to unwrap\n * @returns The success value if the Result is successful\n * @throws {UnwrapError} If the Result is an error (includes the error and cause)\n */\nexport const runOrThrow = <T, E, C>(r: Result<T, E, C>): T => unwrap(r);\n\n/**\n * Awaits a Promise of a Result, then returns the success value or rejects.\n *\n * The returned promise **resolves with T** on success and **rejects with UnwrapError** on failure.\n * UnwrapError extends Error and carries the original `error` and `cause` from the Err.\n *\n * @param ar - A Promise or thenable that resolves to a Result\n * @returns A Promise that resolves with the success value or rejects with UnwrapError\n */\nexport const runOrThrowAsync = <T, E, C>(\n ar: PromiseLike<Result<T, E, C>>\n): Promise<T> => Promise.resolve(ar).then(unwrap);\n\n/**\n * Convenience alias for `unwrapOr(r, null)`. Returns the success value or null.\n *\n * @param r - The Result to unwrap\n * @returns The success value if successful, otherwise null\n */\nexport const runOrNull = <T, E, C>(r: Result<T, E, C>): T | null =>\n r.ok ? r.value : null;\n\n/**\n * Convenience alias for `unwrapOr(r, undefined)`. Returns the success value or undefined.\n *\n * @param r - The Result to unwrap\n * @returns The success value if successful, otherwise undefined\n */\nexport const runOrUndefined = <T, E, C>(r: Result<T, E, C>): T | undefined =>\n r.ok ? r.value : undefined;\n\n// =============================================================================\n// Wrapping Functions\n// =============================================================================\n\n/**\n * Wraps a synchronous function that might throw into a Result.\n *\n * @remarks When to use: Wrap sync code that might throw so exceptions become Err values.\n */\nexport function from<T>(fn: () => T): Ok<T> | Err<unknown, unknown>;\nexport function from<T, E>(fn: () => T, onError: (cause: unknown) => E): Ok<T> | Err<E, unknown>;\nexport function from<T, E>(fn: () => T, onError?: (cause: unknown) => E) {\n try {\n return ok(fn());\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n}\n\n/**\n * Wraps a Promise into a Result.\n *\n * @remarks When to use: Wrap a Promise and keep the raw rejection as Err; use tryAsync to map errors.\n */\nexport function fromPromise<T>(promise: Promise<T>): Promise<Ok<T> | Err<unknown, unknown>>;\nexport function fromPromise<T, E>(\n promise: Promise<T>,\n onError: (cause: unknown) => E\n): Promise<Ok<T> | Err<E, unknown>>;\nexport async function fromPromise<T, E>(\n promise: Promise<T>,\n onError?: (cause: unknown) => E\n): Promise<Ok<T> | Err<E | unknown, unknown>> {\n try {\n return ok(await promise);\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n}\n\n/**\n * Wraps an async function that might throw into an AsyncResult.\n *\n * @remarks When to use: Wrap async work and map thrown/rejected values into your typed error union.\n */\nexport function tryAsync<T>(fn: () => Promise<T>): AsyncResult<T, unknown>;\nexport function tryAsync<T, E>(\n fn: () => Promise<T>,\n onError: (cause: unknown) => E\n): AsyncResult<T, E>;\nexport async function tryAsync<T, E>(\n fn: () => Promise<T>,\n onError?: (cause: unknown) => E\n): AsyncResult<T, E | unknown> {\n try {\n return ok(await fn());\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n}\n\n/**\n * Converts a nullable value into a Result.\n *\n * @remarks When to use: Turn null/undefined into a typed error before continuing.\n */\nexport function fromNullable<T, E>(\n value: T | null | undefined,\n onNull: () => E\n): Result<T, E> {\n return value != null ? ok(value) : err(onNull());\n}\n\n// =============================================================================\n// Transformers\n// =============================================================================\n\n/**\n * Transforms the value inside an Ok result.\n *\n * @remarks When to use: Transform only the Ok value while leaving Err untouched.\n */\nexport function map<T, U>(r: Ok<T>, fn: (value: T) => U): Ok<U>;\nexport function map<T, U, E, C>(r: Err<E, C>, fn: (value: T) => U): Err<E, C>;\nexport function map<T, U, E, C>(r: Result<T, E, C>, fn: (value: T) => U): Result<U, E, C>;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function map(r: any, fn: any): any {\n return r.ok ? ok(fn(r.value)) : r;\n}\n\n/**\n * Transforms the error inside an Err result.\n *\n * @remarks When to use: Retype or normalize errors while leaving Ok values unchanged.\n */\nexport function mapError<T, E, F, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => F\n): Result<T, F, C> {\n return r.ok ? r : err(fn(r.error, r.cause), { cause: r.cause });\n}\n\n/**\n * Pattern match on a Result.\n *\n * @remarks When to use: Handle both Ok and Err in a single expression that returns a value.\n */\nexport function match<T, E, C, R>(r: Ok<T>, handlers: { ok: (value: T) => R; err: (error: E, cause?: C) => R }): R;\nexport function match<T, E, C, R>(r: Err<E, C>, handlers: { ok: (value: T) => R; err: (error: E, cause?: C) => R }): R;\nexport function match<T, E, C, R>(r: Result<T, E, C>, handlers: { ok: (value: T) => R; err: (error: E, cause?: C) => R }): R;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function match(r: any, handlers: any): any {\n return r.ok ? handlers.ok(r.value) : handlers.err(r.error, r.cause);\n}\n\n/**\n * Chain Result-returning functions.\n *\n * @remarks When to use: Chain dependent operations that return Result without nested branching.\n */\nexport function andThen<T, U>(r: Ok<T>, fn: (value: T) => Ok<U>): Ok<U>;\nexport function andThen<T, F, C2>(r: Ok<T>, fn: (value: T) => Err<F, C2>): Err<F, C2>;\nexport function andThen<T, U, F, C2>(r: Ok<T>, fn: (value: T) => Result<U, F, C2>): Result<U, F, C2>;\nexport function andThen<T, U, E, F, C1, C2>(r: Err<E, C1>, fn: (value: T) => Result<U, F, C2>): Err<E, C1>;\nexport function andThen<T, U, E, F, C1, C2>(r: Result<T, E, C1>, fn: (value: T) => Result<U, F, C2>): Result<U, E | F, C1 | C2>;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function andThen(r: any, fn: any): any {\n return r.ok ? fn(r.value) : r;\n}\n\n/**\n * Execute a side effect on Ok values.\n *\n * @remarks When to use: Add side effects (logging, metrics) on Ok without changing the Result.\n */\nexport function tap<T, E, C>(\n r: Result<T, E, C>,\n fn: (value: T) => void\n): Result<T, E, C> {\n if (r.ok) fn(r.value);\n return r;\n}\n\n/**\n * Execute a side effect on Err values.\n *\n * @remarks When to use: Add side effects (logging, metrics) on Err without changing the Result.\n */\nexport function tapError<T, E, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => void\n): Result<T, E, C> {\n if (!r.ok) fn(r.error, r.cause);\n return r;\n}\n\n/**\n * Transform value with a function that might throw.\n *\n * @remarks When to use: Transform Ok values with a function that might throw and capture the failure.\n */\nexport function mapTry<T, U, E, F, C>(\n r: Result<T, E, C>,\n fn: (value: T) => U,\n onError: (thrown: unknown) => F\n): Result<U, E | F, C | unknown> {\n if (!r.ok) return r;\n try {\n return ok(fn(r.value));\n } catch (error) {\n return err(onError(error), { cause: error });\n }\n}\n\n/**\n * Transform error with a function that might throw.\n *\n * @remarks When to use: Transform errors when the mapping might throw and you want that captured.\n */\nexport function mapErrorTry<T, E, F, G, C>(\n r: Result<T, E, C>,\n fn: (error: E) => F,\n onError: (thrown: unknown) => G\n): Result<T, F | G, C | unknown> {\n if (r.ok) return r;\n try {\n return err(fn(r.error), { cause: r.cause });\n } catch (error) {\n return err(onError(error), { cause: error });\n }\n}\n\n/**\n * Transform both value and error.\n */\nexport function bimap<T, U, E, F, C>(\n r: Result<T, E, C>,\n onOk: (value: T) => U,\n onErr: (error: E, cause?: C) => F\n): Result<U, F, C> {\n return r.ok ? ok(onOk(r.value)) : err(onErr(r.error, r.cause), { cause: r.cause });\n}\n\n/**\n * Provide an alternative Result if the first is an Err.\n *\n * @remarks When to use: Recover from Err by returning a fallback Result or retyping the error.\n */\nexport function orElse<T, E, E2, C, C2>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => Result<T, E2, C2>\n): Result<T, E2, C | C2> {\n return r.ok ? r : fn(r.error, r.cause);\n}\n\n/**\n * Async version of orElse.\n */\nexport async function orElseAsync<T, E, E2, C, C2>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => Promise<Result<T, E2, C2>>\n): Promise<Result<T, E2, C | C2>> {\n return r.ok ? r : fn(r.error, r.cause);\n}\n\n/**\n * Recover from errors - always returns Ok<T>.\n */\nexport function recover<T, E, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => T\n): Ok<T> {\n return r.ok ? ok(r.value) : ok(fn(r.error, r.cause));\n}\n\n/**\n * Async version of recover - always returns Promise<Ok<T>>.\n */\nexport async function recoverAsync<T, E, C>(\n r: Result<T, E, C> | Promise<Result<T, E, C>>,\n fn: (error: E, cause?: C) => T | Promise<T>\n): Promise<Ok<T>> {\n const resolved = await r;\n if (resolved.ok) return ok(resolved.value);\n return ok(await fn(resolved.error, resolved.cause));\n}\n\n// =============================================================================\n// Result Hydration (Serialization)\n// =============================================================================\n\n/**\n * Hydrate a serialized Result back into a proper Result object.\n */\nexport function hydrate<T, E, C = unknown>(value: unknown): Result<T, E, C> | null {\n if (typeof value !== \"object\" || value === null) return null;\n if (!(\"ok\" in value)) return null;\n\n const obj = value as Record<string, unknown>;\n if (obj.ok === true && \"value\" in obj) {\n return ok(obj.value as T);\n }\n if (obj.ok === false && \"error\" in obj) {\n return err(obj.error as E, { cause: obj.cause as C });\n }\n return null;\n}\n\n/**\n * Type guard to check if a value is a serialized Result.\n */\nexport function isSerializedResult(\n value: unknown\n): value is { ok: boolean; value?: unknown; error?: unknown; cause?: unknown } {\n if (typeof value !== \"object\" || value === null) return false;\n if (!(\"ok\" in value)) return false;\n const obj = value as Record<string, unknown>;\n return (\n (obj.ok === true && \"value\" in obj) ||\n (obj.ok === false && \"error\" in obj)\n );\n}\n\n// =============================================================================\n// Batch Operations\n// =============================================================================\n\ntype AllValues<T extends readonly Result<unknown, unknown, unknown>[]> = {\n [K in keyof T]: T[K] extends Ok<infer V>\n ? V\n : T[K] extends Err<unknown, unknown>\n ? never\n : T[K] extends Result<infer V, unknown, unknown>\n ? V\n : never;\n};\ntype AllErrors<T extends readonly Result<unknown, unknown, unknown>[]> = {\n [K in keyof T]: T[K] extends Ok<unknown>\n ? never\n : T[K] extends Err<infer E, unknown>\n ? E\n : T[K] extends Result<unknown, infer E, unknown>\n ? E\n : never;\n}[number];\ntype AllCauses<T extends readonly Result<unknown, unknown, unknown>[]> = {\n [K in keyof T]: T[K] extends Ok<unknown>\n ? never\n : T[K] extends Err<unknown, infer C>\n ? C\n : T[K] extends Result<unknown, unknown, infer C>\n ? C\n : never;\n}[number];\n\n// Conditional type: returns Ok<...> when there are no errors, Result<...> otherwise\n// Note: We only check AllErrors, not AllCauses - causes only matter when there are errors\ntype AllResult<T extends readonly Result<unknown, unknown, unknown>[]> =\n [AllErrors<T>] extends [never]\n ? Ok<AllValues<T>>\n : Result<AllValues<T>, AllErrors<T>, AllCauses<T>>;\n\n/**\n * Combines multiple Results into a single Result containing an array of values.\n * Returns the first Err encountered, or Ok with all values.\n */\nexport function all<const T extends readonly Result<unknown, unknown, unknown>[]>(\n results: T\n): AllResult<T> {\n const values: unknown[] = [];\n for (const result of results) {\n if (!result.ok) {\n return result as unknown as AllResult<T>;\n }\n values.push(result.value);\n }\n return ok(values) as AllResult<T>;\n}\n\n/**\n * Async version of all - works with Promises of Results.\n */\nexport async function allAsync<\n const T extends readonly (Result<unknown, unknown, unknown> | Promise<Result<unknown, unknown, unknown>>)[]\n>(\n results: T\n): Promise<\n Result<\n { [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> | Promise<Result<infer V, unknown, unknown>> ? V : never },\n | { [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> | Promise<Result<unknown, infer E, unknown>> ? E : never }[number]\n | PromiseRejectedError,\n | { [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> | Promise<Result<unknown, unknown, infer C>> ? C : never }[number]\n | PromiseRejectionCause\n >\n> {\n const values: unknown[] = [];\n for (const resultOrPromise of results) {\n try {\n const r = await resultOrPromise;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (!r.ok) return r as any;\n values.push(r.value);\n } catch (reason) {\n return err(\n { type: PROMISE_REJECTED, cause: reason } as PromiseRejectedError,\n { cause: { type: \"PROMISE_REJECTION\", reason } as PromiseRejectionCause }\n );\n }\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return ok(values) as any;\n}\n\nexport type SettledError<E, C = unknown> = { error: E; cause?: C };\n\n// Conditional type: returns Ok<...> when there are no errors, Result<...> otherwise\ntype AllSettledResult<T extends readonly Result<unknown, unknown, unknown>[]> =\n [AllErrors<T>] extends [never]\n ? Ok<AllValues<T>>\n : Result<AllValues<T>, SettledError<AllErrors<T>, AllCauses<T>>[]>;\n\n/**\n * Collects all Results, returning Ok with values if all succeed,\n * or Err with array of errors if any fail.\n */\nexport function allSettled<const T extends readonly Result<unknown, unknown, unknown>[]>(\n results: T\n): AllSettledResult<T> {\n const values: unknown[] = [];\n const errors: SettledError<unknown>[] = [];\n\n for (const result of results) {\n if (result.ok) {\n values.push(result.value);\n } else {\n errors.push({ error: result.error, cause: result.cause });\n }\n }\n\n if (errors.length > 0) {\n return err(errors) as unknown as AllSettledResult<T>;\n }\n\n return ok(values) as unknown as AllSettledResult<T>;\n}\n\n/**\n * Async version of allSettled.\n */\nexport async function allSettledAsync<\n const T extends readonly (Result<unknown, unknown, unknown> | Promise<Result<unknown, unknown, unknown>>)[]\n>(\n results: T\n): Promise<\n Result<\n { [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> | Promise<Result<infer V, unknown, unknown>> ? V : never },\n SettledError<\n | { [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> | Promise<Result<unknown, infer E, unknown>> ? E : never }[number]\n | PromiseRejectedError,\n | { [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> | Promise<Result<unknown, unknown, infer C>> ? C : never }[number]\n | PromiseRejectionCause\n >[]\n >\n> {\n const settled = await Promise.all(\n results.map((item) =>\n Promise.resolve(item)\n .then((result) => ({ status: \"result\" as const, result }))\n .catch((reason) => ({\n status: \"rejected\" as const,\n error: { type: PROMISE_REJECTED, cause: reason } as PromiseRejectedError,\n cause: { type: \"PROMISE_REJECTION\", reason } as PromiseRejectionCause,\n }))\n )\n );\n\n const values: unknown[] = [];\n const errors: SettledError<unknown, unknown>[] = [];\n\n for (const item of settled) {\n if (item.status === \"rejected\") {\n errors.push({ error: item.error, cause: item.cause });\n } else if (item.result.ok) {\n values.push(item.result.value);\n } else {\n errors.push({ error: item.result.error, cause: item.result.cause });\n }\n }\n\n if (errors.length > 0) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return err(errors) as any;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return ok(values) as any;\n}\n\n/**\n * Partitions Results into { values, errors }.\n */\nexport function partition<T, E, C>(\n results: readonly Result<T, E, C>[]\n): { values: T[]; errors: E[] } {\n const values: T[] = [];\n const errors: E[] = [];\n for (const r of results) {\n if (r.ok) values.push(r.value);\n else errors.push(r.error);\n }\n return { values, errors };\n}\n\n/**\n * Returns the first Ok result, or an EmptyInputError/first Err if all fail.\n */\nexport function any<const T extends readonly Result<unknown, unknown, unknown>[]>(\n results: T\n): T extends readonly []\n ? Err<EmptyInputError, unknown>\n : Result<\n { [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> ? V : never }[number],\n AllErrors<T> | EmptyInputError,\n AllCauses<T>\n >;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function any(results: any): any {\n if (results.length === 0) {\n return err({ type: \"EMPTY_INPUT\", message: \"any() requires at least one Result\" });\n }\n let firstErr: Err<unknown, unknown> | undefined;\n for (const r of results) {\n if (r.ok) return r;\n if (!firstErr) firstErr = r;\n }\n return firstErr;\n}\n\n/**\n * Async version of any - races promises and returns first success.\n */\nexport async function anyAsync<\n const T extends readonly (Result<unknown, unknown, unknown> | Promise<Result<unknown, unknown, unknown>>)[]\n>(\n results: T\n): Promise<\n T extends readonly []\n ? Err<EmptyInputError, unknown>\n : Result<\n { [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> | Promise<Result<infer V, unknown, unknown>> ? V : never }[number],\n | { [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> | Promise<Result<unknown, infer E, unknown>> ? E : never }[number]\n | EmptyInputError\n | PromiseRejectedError,\n | { [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> | Promise<Result<unknown, unknown, infer C>> ? C : never }[number]\n | PromiseRejectionCause\n >\n> {\n if (results.length === 0) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return err({ type: \"EMPTY_INPUT\", message: \"anyAsync() requires at least one Result\" }) as any;\n }\n\n return new Promise((resolve) => {\n let settled = false;\n let pendingCount = results.length;\n let firstError: Err<unknown, unknown> | null = null;\n\n for (const item of results) {\n Promise.resolve(item)\n .catch((reason) =>\n err(\n { type: PROMISE_REJECTED, cause: reason } as PromiseRejectedError,\n { cause: { type: \"PROMISE_REJECTION\", reason } as PromiseRejectionCause }\n )\n )\n .then((result) => {\n if (settled) return;\n\n if (result.ok) {\n settled = true;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n resolve(result as any);\n return;\n }\n\n if (!firstError) firstError = result;\n pendingCount--;\n\n if (pendingCount === 0) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n resolve(firstError as any);\n }\n });\n }\n });\n}\n\n/**\n * Combines exactly two Results into a tuple.\n */\nexport function zip<A, EA, CA, B, EB, CB>(\n a: Result<A, EA, CA>,\n b: Result<B, EB, CB>\n): Result<[A, B], EA | EB, CA | CB> {\n if (!a.ok) return a;\n if (!b.ok) return b;\n return ok([a.value, b.value]);\n}\n\n/**\n * Async version of zip.\n */\nexport async function zipAsync<A, EA, CA, B, EB, CB>(\n a: Result<A, EA, CA> | Promise<Result<A, EA, CA>>,\n b: Result<B, EB, CB> | Promise<Result<B, EB, CB>>\n): Promise<Result<[A, B], EA | EB | PromiseRejectedError, CA | CB | PromiseRejectionCause>> {\n // Wrap rejections into PromiseRejectedError (consistent with allAsync)\n const wrapRejection = <T, E, C>(\n p: Result<T, E, C> | Promise<Result<T, E, C>>\n ): Promise<Result<T, E | PromiseRejectedError, C | PromiseRejectionCause>> =>\n Promise.resolve(p).catch((reason) =>\n err(\n { type: PROMISE_REJECTED, cause: reason } as PromiseRejectedError,\n { cause: { type: \"PROMISE_REJECTION\", reason } as PromiseRejectionCause }\n )\n );\n\n const [ra, rb] = await Promise.all([wrapRejection(a), wrapRejection(b)]);\n return zip(ra, rb);\n}\n\n// =============================================================================\n// Flatten\n// =============================================================================\n\n/**\n * Flattens a nested Result into a single Result.\n *\n * @remarks When to use: Unwrap a Result<Result<T, E1>, E2> into Result<T, E1 | E2> after an operation that returns nested Results.\n */\nexport function flatten<T, E1, C1, E2, C2>(\n result: Result<Result<T, E1, C1>, E2, C2>\n): Result<T, E1 | E2, C1 | C2> {\n if (!result.ok) return result as Err<E2, C2>;\n return result.value;\n}\n\n// =============================================================================\n// Deserialization (improved hydrate)\n// =============================================================================\n\n/** Discriminant for deserialization errors */\nexport const DESERIALIZATION_ERROR = \"DESERIALIZATION_ERROR\" as const;\n\n/** Error type returned when deserialize() receives invalid input */\nexport type DeserializationError = { type: typeof DESERIALIZATION_ERROR; value: unknown };\n\n/**\n * Deserialize a value back into a Result.\n * Returns a typed DeserializationError on invalid input instead of null.\n *\n * @remarks When to use: Rehydrate Results from JSON, RPC, or server actions with type-safe error handling.\n */\nexport function deserialize<T, E, C = unknown>(\n value: unknown\n): Result<T, E | DeserializationError, C> {\n if (typeof value !== \"object\" || value === null) {\n return err({ type: DESERIALIZATION_ERROR, value } as DeserializationError);\n }\n if (!(\"ok\" in value)) {\n return err({ type: DESERIALIZATION_ERROR, value } as DeserializationError);\n }\n\n const obj = value as Record<string, unknown>;\n if (obj.ok === true && \"value\" in obj) {\n return ok(obj.value as T);\n }\n if (obj.ok === false && \"error\" in obj) {\n return err(obj.error as E, { cause: obj.cause as C });\n }\n return err({ type: DESERIALIZATION_ERROR, value } as DeserializationError);\n}\n\n// =============================================================================\n// Serialization\n// =============================================================================\n\n/** A plain serialized form of a Result, safe to JSON.stringify. */\nexport type SerializedResult<T, E> = { ok: true; value: T } | { ok: false; error: E };\n\n/**\n * Serialize a Result to a plain object (inverse of `deserialize`).\n * Strips cause — safe for JSON.stringify, RPC, and server actions.\n *\n * @remarks When to use: Sending Results over the wire or storing them in JSON.\n */\nexport function serialize<T, E>(result: Result<T, E>): SerializedResult<T, E> {\n return result.ok\n ? { ok: true, value: result.value }\n : { ok: false, error: result.error };\n}\n\n// =============================================================================\n// Partial error matching\n// =============================================================================\n\n/**\n * Non-exhaustive error match — handle the errors you care about; let the rest fall through to fallback.\n *\n * @example\n * ```typescript\n * const message = matchErrorPartial(\n * error,\n * { NOT_FOUND: () => 'Resource not found' },\n * (e) => `Unexpected: ${e}`\n * );\n * ```\n */\nexport function matchErrorPartial<E extends string, R>(\n error: E | UnexpectedError,\n handlers: Partial<MatchErrorHandlers<E, R>>,\n fallback: (error: E | UnexpectedError) => R\n): R {\n if (isUnexpectedError(error)) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const h = (handlers as any).UNEXPECTED_ERROR as ((e: UnexpectedError) => R) | undefined;\n return h ? h(error) : fallback(error);\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const h = (handlers as any)[error as string] as ((e: E) => R) | undefined;\n return h ? h(error as E) : fallback(error);\n}\n\n// Retry helper is intentionally NOT re-exported here.\n// Import from the dedicated subpath to keep awaitly/result minimal:\n// import { tryAsyncRetry } from 'awaitly/result/retry';\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,mBAAAE,IAAA,eAAAC,EAAAH,GCsIO,SAASI,EAAMC,EAAyB,CAC7C,MAAO,CAAE,GAAI,GAAe,MAAOA,CAAkB,CACvD,CAOO,SAASC,EAAoBC,EAAUC,EAAoC,CAChF,IAAMC,EAAQD,GAAS,MACvB,MAAO,CAAE,GAAI,GAAgB,MAAAD,EAAO,GAAIE,IAAU,OAAY,CAAE,MAAAA,CAAM,EAAI,CAAC,CAAG,CAChF,CDvGA,eAAsBC,EACpBC,EACAC,EACAC,EAC6B,CAC7B,IAAMC,EAAU,OAAOF,GAAoB,WAAaA,EAAkB,OAEpEG,GADS,OAAOH,GAAoB,WAAaC,EAAeD,GACjD,MAEfI,EAAYC,GAA4B,CAC5C,OAAQF,EAAM,QAAS,CACrB,IAAK,SACH,OAAOA,EAAM,SAAWE,EAAU,GACpC,IAAK,cACH,OAAOF,EAAM,QAAU,GAAKE,EAE9B,QACE,OAAOF,EAAM,OACjB,CACF,EAEMG,EAASC,GAAe,IAAI,QAAeC,GAAY,WAAWA,EAASD,CAAE,CAAC,EAE9EE,EAAU,SAAyC,CACvD,GAAI,CACF,OAAOC,EAAG,MAAMX,EAAG,CAAC,CACtB,OAASY,EAAO,CACd,OAAOT,EAAUU,EAAIV,EAAQS,CAAK,EAAG,CAAE,MAAAA,CAAM,CAAC,EAAIC,EAAID,CAAK,CAC7D,CACF,EAEIE,EAAS,MAAMJ,EAAQ,EACrBK,EAAgBX,EAAM,cAAgB,IAAM,IAElD,QAASE,EAAU,EAAGA,EAAUF,EAAM,OAChC,EAAAU,EAAO,IACP,CAACC,EAAcD,EAAO,KAAU,GAFOR,IAG3C,MAAMC,EAAMF,EAASC,CAAO,CAAC,EAC7BQ,EAAS,MAAMJ,EAAQ,EAGzB,OAAOI,CACT","names":["retry_exports","__export","tryAsyncRetry","__toCommonJS","ok","value","err","error","options","cause","tryAsyncRetry","fn","onErrorOrConfig","maybeConfig","onError","retry","getDelay","attempt","sleep","ms","resolve","execute","ok","cause","err","result","shouldRetryFn"]}
|
|
1
|
+
{"version":3,"sources":["../../src/result/retry.ts","../../src/result/index.ts"],"sourcesContent":["/**\n * awaitly/result retry support\n *\n * Retry async operations with configurable backoff without the full workflow engine.\n */\n\nimport type { AsyncResult } from \"./index\";\nimport { ok, err } from \"./index\";\n\n/** Configuration for retry behavior */\nexport type RetryConfig<E = unknown> = {\n /** Number of retry attempts (not including the initial attempt) */\n times: number;\n /** Base delay between retries in milliseconds */\n delayMs: number;\n /** Backoff strategy */\n backoff?: \"constant\" | \"linear\" | \"exponential\";\n /** Predicate to determine if an error should trigger a retry. Defaults to always retry. */\n shouldRetry?: (error: E) => boolean;\n};\n\n/**\n * Wraps an async function that might throw into an AsyncResult, with retry support.\n *\n * @remarks When to use: Wrap async work with retry logic for transient failures without needing the full workflow engine.\n *\n * @example\n * ```typescript\n * const result = await tryAsyncRetry(\n * () => fetch('/api/data').then(r => r.json()),\n * { retry: { times: 3, delayMs: 100, backoff: 'exponential' } }\n * );\n * ```\n */\nexport function tryAsyncRetry<T>(\n fn: () => Promise<T>,\n config: { retry: RetryConfig<unknown> }\n): AsyncResult<T, unknown>;\nexport function tryAsyncRetry<T, E>(\n fn: () => Promise<T>,\n onError: (cause: unknown) => E,\n config: { retry: RetryConfig<E> }\n): AsyncResult<T, E>;\nexport async function tryAsyncRetry<T, E>(\n fn: () => Promise<T>,\n onErrorOrConfig: ((cause: unknown) => E) | { retry: RetryConfig<unknown> },\n maybeConfig?: { retry: RetryConfig<E> }\n): AsyncResult<T, E | unknown> {\n const onError = typeof onErrorOrConfig === \"function\" ? onErrorOrConfig : undefined;\n const config = typeof onErrorOrConfig === \"function\" ? maybeConfig! : onErrorOrConfig;\n const retry = config.retry;\n\n const getDelay = (attempt: number): number => {\n switch (retry.backoff) {\n case \"linear\":\n return retry.delayMs * (attempt + 1);\n case \"exponential\":\n return retry.delayMs * 2 ** attempt;\n case \"constant\":\n default:\n return retry.delayMs;\n }\n };\n\n const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\n\n const execute = async (): AsyncResult<T, E | unknown> => {\n try {\n return ok(await fn());\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n };\n\n let result = await execute();\n const shouldRetryFn = retry.shouldRetry ?? (() => true);\n\n for (let attempt = 0; attempt < retry.times; attempt++) {\n if (result.ok) break;\n if (!shouldRetryFn(result.error as E)) break;\n await sleep(getDelay(attempt));\n result = await execute();\n }\n\n return result;\n}\n","/**\n * awaitly/result (internal)\n *\n * Core Result primitives - minimal bundle for typed error handling.\n * This file is intentionally kept small for optimal tree-shaking.\n * The full orchestration (run, step, etc.) lives in core.ts.\n */\n\n// =============================================================================\n// Core Result Types\n// =============================================================================\n\n/**\n * Represents a successful result.\n * Use `ok(value)` to create instances.\n */\nexport type Ok<T> = {\n ok: true;\n value: T;\n};\n\n/**\n * Represents a failed result.\n * Use `err(error)` to create instances.\n */\nexport type Err<E, C = unknown> = {\n ok: false;\n error: E;\n cause?: C;\n};\n\n/**\n * Represents a successful computation or a failed one.\n */\nexport type Result<T, E = unknown, C = unknown> = Ok<T> | Err<E, C>;\n\n/**\n * A Promise that resolves to a Result.\n */\nexport type AsyncResult<T, E = unknown, C = unknown> = Promise<Result<T, E, C>>;\n\n/** Discriminant for PromiseRejectedError type - use in switch statements */\nexport const PROMISE_REJECTED = \"PROMISE_REJECTED\" as const;\n\n// =============================================================================\n// Named Error Constants (for static analysis)\n// =============================================================================\n\n/**\n * Named error constant for unexpected/unhandled errors.\n * Used by the analyzer when a step doesn't declare errors.\n */\nexport const AWAITLY_UNEXPECTED = \"AWAITLY_UNEXPECTED\" as const;\n\n/**\n * Named error constant for cancelled operations.\n */\nexport const AWAITLY_CANCELLED = \"AWAITLY_CANCELLED\" as const;\n\n/**\n * Named error constant for timed-out operations.\n */\nexport const AWAITLY_TIMEOUT = \"AWAITLY_TIMEOUT\" as const;\n\n// =============================================================================\n// Static Analysis Helpers\n// =============================================================================\n\n/**\n * Helper to create a tuple of string literal tags with preserved literal types.\n * Use this when you need to store error tags in a variable while keeping\n * TypeScript's literal type inference (avoiding widening to string[]).\n *\n * @param t - The string literal tags\n * @returns The same array with preserved literal types\n *\n * @example\n * ```typescript\n * // Without tags() - type widens to string[]\n * const errs = ['CART_NOT_FOUND', 'CART_EMPTY']; // string[]\n *\n * // With tags() - literal types preserved\n * const errs = tags('CART_NOT_FOUND', 'CART_EMPTY'); // readonly ['CART_NOT_FOUND', 'CART_EMPTY']\n *\n * await step('getCart', () => getCart(id), {\n * errors: errs, // Analyzer can extract literal types\n * out: 'cart',\n * });\n * ```\n */\nexport const tags = <const T extends readonly string[]>(...t: T): T => t;\n\nimport { UnexpectedError } from \"../errors\";\nexport { UnexpectedError };\nexport type PromiseRejectedError = { type: typeof PROMISE_REJECTED; cause: unknown };\n/** Cause type for promise rejections in async batch helpers */\nexport type PromiseRejectionCause = { type: \"PROMISE_REJECTION\"; reason: unknown };\nexport type EmptyInputError = { type: \"EMPTY_INPUT\"; message: string };\nexport type MaybeAsyncResult<T, E, C = unknown> = Result<T, E, C> | Promise<Result<T, E, C>>;\n\n// =============================================================================\n// Result Constructors\n// =============================================================================\n\n/**\n * Creates a successful Result.\n *\n * @remarks When to use: Wrap a successful value in a Result for consistent return types.\n */\nexport function ok(): Ok<void>;\nexport function ok<T>(value: T): Ok<T>;\nexport function ok<T>(value?: T): Ok<T | void> {\n return { ok: true as const, value: value as T | void };\n}\n\n/**\n * Creates a failed Result.\n *\n * @remarks When to use: Return a typed failure without throwing so callers can handle it explicitly.\n */\nexport function err<E, C = unknown>(error: E, options?: { cause?: C }): Err<E, C> {\n const cause = options?.cause;\n return { ok: false as const, error, ...(cause !== undefined ? { cause } : {}) } as Err<E, C>;\n}\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\n/**\n * Checks if a Result is successful.\n *\n * @remarks When to use: Prefer functional-style checks or array filtering over `result.ok`.\n */\nexport const isOk = <T, E, C>(r: Result<T, E, C>): r is Ok<T> => r.ok;\n\n/**\n * Checks if a Result is a failure.\n *\n * @remarks When to use: Prefer functional-style checks or array filtering over `result.ok`.\n */\nexport const isErr = <T, E, C>(r: Result<T, E, C>): r is Err<E, C> => !r.ok;\n\n/**\n * Checks if an error is an UnexpectedError.\n *\n * @remarks When to use: Distinguish unexpected failures from your typed error union.\n */\nexport const isUnexpectedError = (e: unknown): e is UnexpectedError =>\n e instanceof UnexpectedError ||\n (typeof e === \"object\" &&\n e !== null &&\n \"_tag\" in e &&\n (e as { _tag: string })._tag === \"UnexpectedError\");\n\n/**\n * Checks if an error is a PromiseRejectedError.\n */\nexport const isPromiseRejectedError = (e: unknown): e is PromiseRejectedError =>\n typeof e === \"object\" &&\n e !== null &&\n \"type\" in e &&\n e.type === PROMISE_REJECTED;\n\n// =============================================================================\n// Error Matching\n// =============================================================================\n\nexport type MatchErrorHandlers<E extends string, R> = {\n [K in Exclude<E, \"UnexpectedError\">]: (error: K) => R;\n} & {\n UnexpectedError: (error: UnexpectedError) => R;\n};\n\n/**\n * Match on string error types with exhaustive checking.\n * Takes an error value (not a Result) and handlers for each error type.\n */\nexport function matchError<E extends string, R>(\n error: E | UnexpectedError,\n handlers: MatchErrorHandlers<E, R>\n): R {\n // Handle UnexpectedError instances\n if (isUnexpectedError(error)) {\n return handlers.UnexpectedError(error as UnexpectedError);\n }\n // Handle string literal errors\n type StringErrors = Exclude<E, \"UnexpectedError\">;\n return (handlers as unknown as Record<string, (e: string) => R>)[error as StringErrors](error as StringErrors);\n}\n\n// =============================================================================\n// Type Utilities\n// =============================================================================\n\ntype AnyFunction = (...args: never[]) => unknown;\n\n/**\n * Helper to extract the error type from Result or AsyncResult return values.\n * Works even when a function is declared to return a union of both forms.\n */\ntype ErrorOfReturn<R> = Extract<Awaited<R>, { ok: false }> extends { error: infer E }\n ? E\n : never;\n\n/**\n * Extract error type from a single function's return type\n */\nexport type ErrorOf<T extends AnyFunction> = ErrorOfReturn<ReturnType<T>>;\n\n/**\n * Extract union of error types from multiple functions\n */\nexport type Errors<T extends AnyFunction[]> = {\n [K in keyof T]: ErrorOf<T[K]>;\n}[number];\n\n/**\n * Extract value type from Result\n */\nexport type ExtractValue<T> = T extends { ok: true; value: infer U }\n ? U\n : never;\n\n/**\n * Extract error type from Result\n */\nexport type ExtractError<T> = T extends { ok: false; error: infer E }\n ? E\n : never;\n\n/**\n * Extract cause type from Result\n */\nexport type ExtractCause<T> = T extends { ok: false; cause?: infer C }\n ? C\n : never;\n\n/**\n * Helper to extract the cause type from Result or AsyncResult return values.\n * Works even when a function is declared to return a union of both forms.\n */\ntype CauseOfReturn<R> = Extract<Awaited<R>, { ok: false }> extends { cause?: infer C }\n ? C\n : never;\n\n/**\n * Extract cause type from a function's return type\n */\nexport type CauseOf<T extends AnyFunction> = CauseOfReturn<ReturnType<T>>;\n\n// =============================================================================\n// Unwrap Utilities\n// =============================================================================\n\n/**\n * Error thrown when attempting to unwrap an Err result.\n */\nexport class UnwrapError extends Error {\n public readonly error: unknown;\n public readonly cause?: unknown;\n\n constructor(result: Err<unknown, unknown>) {\n const errorStr =\n typeof result.error === \"string\"\n ? result.error\n : JSON.stringify(result.error);\n super(`Attempted to unwrap an Err: ${errorStr}`);\n this.name = \"UnwrapError\";\n this.error = result.error;\n this.cause = result.cause;\n }\n}\n\n/**\n * Extracts the value from an Ok result, or throws UnwrapError if it's an Err.\n *\n * @remarks When to use: Only at boundaries or tests where a failure should be fatal.\n */\nexport const unwrap = <T, E, C>(r: Result<T, E, C>): T => {\n if (r.ok) return r.value;\n throw new UnwrapError(r);\n};\n\n/**\n * Extracts the value from an Ok result, or returns a default value if it's an Err.\n *\n * @remarks When to use: Provide a safe fallback without branching.\n */\nexport const unwrapOr = <T, E, C>(r: Result<T, E, C>, defaultValue: T): T =>\n r.ok ? r.value : defaultValue;\n\n/**\n * Extracts the value from an Ok result, or calls a function to get a default value if it's an Err.\n *\n * @remarks When to use: Compute a fallback from the error (logging, metrics, or derived defaults).\n */\nexport const unwrapOrElse = <T, E, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => T\n): T => (r.ok ? r.value : fn(r.error, r.cause));\n\n/**\n * Alias for `unwrap`. Returns the success value or throws.\n *\n * The Result is already computed; use when you want the value or throw (e.g. at boundaries or in tests).\n *\n * @param r - The Result to unwrap\n * @returns The success value if the Result is successful\n * @throws {UnwrapError} If the Result is an error (includes the error and cause)\n */\nexport const runOrThrow = <T, E, C>(r: Result<T, E, C>): T => unwrap(r);\n\n/**\n * Awaits a Promise of a Result, then returns the success value or rejects.\n *\n * The returned promise **resolves with T** on success and **rejects with UnwrapError** on failure.\n * UnwrapError extends Error and carries the original `error` and `cause` from the Err.\n *\n * @param ar - A Promise or thenable that resolves to a Result\n * @returns A Promise that resolves with the success value or rejects with UnwrapError\n */\nexport const runOrThrowAsync = <T, E, C>(\n ar: PromiseLike<Result<T, E, C>>\n): Promise<T> => Promise.resolve(ar).then(unwrap);\n\n/**\n * Convenience alias for `unwrapOr(r, null)`. Returns the success value or null.\n *\n * @param r - The Result to unwrap\n * @returns The success value if successful, otherwise null\n */\nexport const runOrNull = <T, E, C>(r: Result<T, E, C>): T | null =>\n r.ok ? r.value : null;\n\n/**\n * Convenience alias for `unwrapOr(r, undefined)`. Returns the success value or undefined.\n *\n * @param r - The Result to unwrap\n * @returns The success value if successful, otherwise undefined\n */\nexport const runOrUndefined = <T, E, C>(r: Result<T, E, C>): T | undefined =>\n r.ok ? r.value : undefined;\n\n// =============================================================================\n// Wrapping Functions\n// =============================================================================\n\n/**\n * Wraps a synchronous function that might throw into a Result.\n *\n * @remarks When to use: Wrap sync code that might throw so exceptions become Err values.\n */\nexport function from<T>(fn: () => T): Ok<T> | Err<unknown, unknown>;\nexport function from<T, E>(fn: () => T, onError: (cause: unknown) => E): Ok<T> | Err<E, unknown>;\nexport function from<T, E>(fn: () => T, onError?: (cause: unknown) => E) {\n try {\n return ok(fn());\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n}\n\n/**\n * Wraps a Promise into a Result.\n *\n * @remarks When to use: Wrap a Promise and keep the raw rejection as Err; use tryAsync to map errors.\n */\nexport function fromPromise<T>(promise: Promise<T>): Promise<Ok<T> | Err<unknown, unknown>>;\nexport function fromPromise<T, E>(\n promise: Promise<T>,\n onError: (cause: unknown) => E\n): Promise<Ok<T> | Err<E, unknown>>;\nexport async function fromPromise<T, E>(\n promise: Promise<T>,\n onError?: (cause: unknown) => E\n): Promise<Ok<T> | Err<E | unknown, unknown>> {\n try {\n return ok(await promise);\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n}\n\n/**\n * Wraps an async function that might throw into an AsyncResult.\n *\n * @remarks When to use: Wrap async work and map thrown/rejected values into your typed error union.\n */\nexport function tryAsync<T>(fn: () => Promise<T>): AsyncResult<T, unknown>;\nexport function tryAsync<T, E>(\n fn: () => Promise<T>,\n onError: (cause: unknown) => E\n): AsyncResult<T, E>;\nexport async function tryAsync<T, E>(\n fn: () => Promise<T>,\n onError?: (cause: unknown) => E\n): AsyncResult<T, E | unknown> {\n try {\n return ok(await fn());\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n}\n\n/**\n * Converts a nullable value into a Result.\n *\n * @remarks When to use: Turn null/undefined into a typed error before continuing.\n */\nexport function fromNullable<T, E>(\n value: T | null | undefined,\n onNull: () => E\n): Result<T, E> {\n return value != null ? ok(value) : err(onNull());\n}\n\n// =============================================================================\n// Transformers\n// =============================================================================\n\n/**\n * Transforms the value inside an Ok result.\n *\n * @remarks When to use: Transform only the Ok value while leaving Err untouched.\n */\nexport function map<T, U>(r: Ok<T>, fn: (value: T) => U): Ok<U>;\nexport function map<T, U, E, C>(r: Err<E, C>, fn: (value: T) => U): Err<E, C>;\nexport function map<T, U, E, C>(r: Result<T, E, C>, fn: (value: T) => U): Result<U, E, C>;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function map(r: any, fn: any): any {\n return r.ok ? ok(fn(r.value)) : r;\n}\n\n/**\n * Transforms the error inside an Err result.\n *\n * @remarks When to use: Retype or normalize errors while leaving Ok values unchanged.\n */\nexport function mapError<T, E, F, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => F\n): Result<T, F, C> {\n return r.ok ? r : err(fn(r.error, r.cause), { cause: r.cause });\n}\n\n/**\n * Pattern match on a Result.\n *\n * @remarks When to use: Handle both Ok and Err in a single expression that returns a value.\n */\nexport function match<T, E, C, R>(r: Ok<T>, handlers: { ok: (value: T) => R; err: (error: E, cause?: C) => R }): R;\nexport function match<T, E, C, R>(r: Err<E, C>, handlers: { ok: (value: T) => R; err: (error: E, cause?: C) => R }): R;\nexport function match<T, E, C, R>(r: Result<T, E, C>, handlers: { ok: (value: T) => R; err: (error: E, cause?: C) => R }): R;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function match(r: any, handlers: any): any {\n return r.ok ? handlers.ok(r.value) : handlers.err(r.error, r.cause);\n}\n\n/**\n * Chain Result-returning functions.\n *\n * @remarks When to use: Chain dependent operations that return Result without nested branching.\n */\nexport function andThen<T, U>(r: Ok<T>, fn: (value: T) => Ok<U>): Ok<U>;\nexport function andThen<T, F, C2>(r: Ok<T>, fn: (value: T) => Err<F, C2>): Err<F, C2>;\nexport function andThen<T, U, F, C2>(r: Ok<T>, fn: (value: T) => Result<U, F, C2>): Result<U, F, C2>;\nexport function andThen<T, U, E, F, C1, C2>(r: Err<E, C1>, fn: (value: T) => Result<U, F, C2>): Err<E, C1>;\nexport function andThen<T, U, E, F, C1, C2>(r: Result<T, E, C1>, fn: (value: T) => Result<U, F, C2>): Result<U, E | F, C1 | C2>;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function andThen(r: any, fn: any): any {\n return r.ok ? fn(r.value) : r;\n}\n\n/**\n * Execute a side effect on Ok values.\n *\n * @remarks When to use: Add side effects (logging, metrics) on Ok without changing the Result.\n */\nexport function tap<T, E, C>(\n r: Result<T, E, C>,\n fn: (value: T) => void\n): Result<T, E, C> {\n if (r.ok) fn(r.value);\n return r;\n}\n\n/**\n * Execute a side effect on Err values.\n *\n * @remarks When to use: Add side effects (logging, metrics) on Err without changing the Result.\n */\nexport function tapError<T, E, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => void\n): Result<T, E, C> {\n if (!r.ok) fn(r.error, r.cause);\n return r;\n}\n\n/**\n * Transform value with a function that might throw.\n *\n * @remarks When to use: Transform Ok values with a function that might throw and capture the failure.\n */\nexport function mapTry<T, U, E, F, C>(\n r: Result<T, E, C>,\n fn: (value: T) => U,\n onError: (thrown: unknown) => F\n): Result<U, E | F, C | unknown> {\n if (!r.ok) return r;\n try {\n return ok(fn(r.value));\n } catch (error) {\n return err(onError(error), { cause: error });\n }\n}\n\n/**\n * Transform error with a function that might throw.\n *\n * @remarks When to use: Transform errors when the mapping might throw and you want that captured.\n */\nexport function mapErrorTry<T, E, F, G, C>(\n r: Result<T, E, C>,\n fn: (error: E) => F,\n onError: (thrown: unknown) => G\n): Result<T, F | G, C | unknown> {\n if (r.ok) return r;\n try {\n return err(fn(r.error), { cause: r.cause });\n } catch (error) {\n return err(onError(error), { cause: error });\n }\n}\n\n/**\n * Transform both value and error.\n */\nexport function bimap<T, U, E, F, C>(\n r: Result<T, E, C>,\n onOk: (value: T) => U,\n onErr: (error: E, cause?: C) => F\n): Result<U, F, C> {\n return r.ok ? ok(onOk(r.value)) : err(onErr(r.error, r.cause), { cause: r.cause });\n}\n\n/**\n * Provide an alternative Result if the first is an Err.\n *\n * @remarks When to use: Recover from Err by returning a fallback Result or retyping the error.\n */\nexport function orElse<T, E, E2, C, C2>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => Result<T, E2, C2>\n): Result<T, E2, C | C2> {\n return r.ok ? r : fn(r.error, r.cause);\n}\n\n/**\n * Async version of orElse.\n */\nexport async function orElseAsync<T, E, E2, C, C2>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => Promise<Result<T, E2, C2>>\n): Promise<Result<T, E2, C | C2>> {\n return r.ok ? r : fn(r.error, r.cause);\n}\n\n/**\n * Recover from errors - always returns Ok<T>.\n */\nexport function recover<T, E, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => T\n): Ok<T> {\n return r.ok ? ok(r.value) : ok(fn(r.error, r.cause));\n}\n\n/**\n * Async version of recover - always returns Promise<Ok<T>>.\n */\nexport async function recoverAsync<T, E, C>(\n r: Result<T, E, C> | Promise<Result<T, E, C>>,\n fn: (error: E, cause?: C) => T | Promise<T>\n): Promise<Ok<T>> {\n const resolved = await r;\n if (resolved.ok) return ok(resolved.value);\n return ok(await fn(resolved.error, resolved.cause));\n}\n\n// =============================================================================\n// Result Hydration (Serialization)\n// =============================================================================\n\n/**\n * Hydrate a serialized Result back into a proper Result object.\n */\nexport function hydrate<T, E, C = unknown>(value: unknown): Result<T, E, C> | null {\n if (typeof value !== \"object\" || value === null) return null;\n if (!(\"ok\" in value)) return null;\n\n const obj = value as Record<string, unknown>;\n if (obj.ok === true && \"value\" in obj) {\n return ok(obj.value as T);\n }\n if (obj.ok === false && \"error\" in obj) {\n return err(obj.error as E, { cause: obj.cause as C });\n }\n return null;\n}\n\n/**\n * Type guard to check if a value is a serialized Result.\n */\nexport function isSerializedResult(\n value: unknown\n): value is { ok: boolean; value?: unknown; error?: unknown; cause?: unknown } {\n if (typeof value !== \"object\" || value === null) return false;\n if (!(\"ok\" in value)) return false;\n const obj = value as Record<string, unknown>;\n return (\n (obj.ok === true && \"value\" in obj) ||\n (obj.ok === false && \"error\" in obj)\n );\n}\n\n// =============================================================================\n// Batch Operations\n// =============================================================================\n\ntype AllValues<T extends readonly Result<unknown, unknown, unknown>[]> = {\n [K in keyof T]: T[K] extends Ok<infer V>\n ? V\n : T[K] extends Err<unknown, unknown>\n ? never\n : T[K] extends Result<infer V, unknown, unknown>\n ? V\n : never;\n};\ntype AllErrors<T extends readonly Result<unknown, unknown, unknown>[]> = {\n [K in keyof T]: T[K] extends Ok<unknown>\n ? never\n : T[K] extends Err<infer E, unknown>\n ? E\n : T[K] extends Result<unknown, infer E, unknown>\n ? E\n : never;\n}[number];\ntype AllCauses<T extends readonly Result<unknown, unknown, unknown>[]> = {\n [K in keyof T]: T[K] extends Ok<unknown>\n ? never\n : T[K] extends Err<unknown, infer C>\n ? C\n : T[K] extends Result<unknown, unknown, infer C>\n ? C\n : never;\n}[number];\n\n// Conditional type: returns Ok<...> when there are no errors, Result<...> otherwise\n// Note: We only check AllErrors, not AllCauses - causes only matter when there are errors\ntype AllResult<T extends readonly Result<unknown, unknown, unknown>[]> =\n [AllErrors<T>] extends [never]\n ? Ok<AllValues<T>>\n : Result<AllValues<T>, AllErrors<T>, AllCauses<T>>;\n\n/**\n * Combines multiple Results into a single Result containing an array of values.\n * Returns the first Err encountered, or Ok with all values.\n */\nexport function all<const T extends readonly Result<unknown, unknown, unknown>[]>(\n results: T\n): AllResult<T> {\n const values: unknown[] = [];\n for (const result of results) {\n if (!result.ok) {\n return result as unknown as AllResult<T>;\n }\n values.push(result.value);\n }\n return ok(values) as AllResult<T>;\n}\n\n/**\n * Async version of all - works with Promises of Results.\n */\nexport async function allAsync<\n const T extends readonly (Result<unknown, unknown, unknown> | Promise<Result<unknown, unknown, unknown>>)[]\n>(\n results: T\n): Promise<\n Result<\n { [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> | Promise<Result<infer V, unknown, unknown>> ? V : never },\n | { [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> | Promise<Result<unknown, infer E, unknown>> ? E : never }[number]\n | PromiseRejectedError,\n | { [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> | Promise<Result<unknown, unknown, infer C>> ? C : never }[number]\n | PromiseRejectionCause\n >\n> {\n const values: unknown[] = [];\n for (const resultOrPromise of results) {\n try {\n const r = await resultOrPromise;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (!r.ok) return r as any;\n values.push(r.value);\n } catch (reason) {\n return err(\n { type: PROMISE_REJECTED, cause: reason } as PromiseRejectedError,\n { cause: { type: \"PROMISE_REJECTION\", reason } as PromiseRejectionCause }\n );\n }\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return ok(values) as any;\n}\n\nexport type SettledError<E, C = unknown> = { error: E; cause?: C };\n\n// Conditional type: returns Ok<...> when there are no errors, Result<...> otherwise\ntype AllSettledResult<T extends readonly Result<unknown, unknown, unknown>[]> =\n [AllErrors<T>] extends [never]\n ? Ok<AllValues<T>>\n : Result<AllValues<T>, SettledError<AllErrors<T>, AllCauses<T>>[]>;\n\n/**\n * Collects all Results, returning Ok with values if all succeed,\n * or Err with array of errors if any fail.\n */\nexport function allSettled<const T extends readonly Result<unknown, unknown, unknown>[]>(\n results: T\n): AllSettledResult<T> {\n const values: unknown[] = [];\n const errors: SettledError<unknown>[] = [];\n\n for (const result of results) {\n if (result.ok) {\n values.push(result.value);\n } else {\n errors.push({ error: result.error, cause: result.cause });\n }\n }\n\n if (errors.length > 0) {\n return err(errors) as unknown as AllSettledResult<T>;\n }\n\n return ok(values) as unknown as AllSettledResult<T>;\n}\n\n/**\n * Async version of allSettled.\n */\nexport async function allSettledAsync<\n const T extends readonly (Result<unknown, unknown, unknown> | Promise<Result<unknown, unknown, unknown>>)[]\n>(\n results: T\n): Promise<\n Result<\n { [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> | Promise<Result<infer V, unknown, unknown>> ? V : never },\n SettledError<\n | { [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> | Promise<Result<unknown, infer E, unknown>> ? E : never }[number]\n | PromiseRejectedError,\n | { [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> | Promise<Result<unknown, unknown, infer C>> ? C : never }[number]\n | PromiseRejectionCause\n >[]\n >\n> {\n const settled = await Promise.all(\n results.map((item) =>\n Promise.resolve(item)\n .then((result) => ({ status: \"result\" as const, result }))\n .catch((reason) => ({\n status: \"rejected\" as const,\n error: { type: PROMISE_REJECTED, cause: reason } as PromiseRejectedError,\n cause: { type: \"PROMISE_REJECTION\", reason } as PromiseRejectionCause,\n }))\n )\n );\n\n const values: unknown[] = [];\n const errors: SettledError<unknown, unknown>[] = [];\n\n for (const item of settled) {\n if (item.status === \"rejected\") {\n errors.push({ error: item.error, cause: item.cause });\n } else if (item.result.ok) {\n values.push(item.result.value);\n } else {\n errors.push({ error: item.result.error, cause: item.result.cause });\n }\n }\n\n if (errors.length > 0) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return err(errors) as any;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return ok(values) as any;\n}\n\n/**\n * Partitions Results into { values, errors }.\n */\nexport function partition<T, E, C>(\n results: readonly Result<T, E, C>[]\n): { values: T[]; errors: E[] } {\n const values: T[] = [];\n const errors: E[] = [];\n for (const r of results) {\n if (r.ok) values.push(r.value);\n else errors.push(r.error);\n }\n return { values, errors };\n}\n\n/**\n * Returns the first Ok result, or an EmptyInputError/first Err if all fail.\n */\nexport function any<const T extends readonly Result<unknown, unknown, unknown>[]>(\n results: T\n): T extends readonly []\n ? Err<EmptyInputError, unknown>\n : Result<\n { [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> ? V : never }[number],\n AllErrors<T> | EmptyInputError,\n AllCauses<T>\n >;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function any(results: any): any {\n if (results.length === 0) {\n return err({ type: \"EMPTY_INPUT\", message: \"any() requires at least one Result\" });\n }\n let firstErr: Err<unknown, unknown> | undefined;\n for (const r of results) {\n if (r.ok) return r;\n if (!firstErr) firstErr = r;\n }\n return firstErr;\n}\n\n/**\n * Async version of any - races promises and returns first success.\n */\nexport async function anyAsync<\n const T extends readonly (Result<unknown, unknown, unknown> | Promise<Result<unknown, unknown, unknown>>)[]\n>(\n results: T\n): Promise<\n T extends readonly []\n ? Err<EmptyInputError, unknown>\n : Result<\n { [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> | Promise<Result<infer V, unknown, unknown>> ? V : never }[number],\n | { [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> | Promise<Result<unknown, infer E, unknown>> ? E : never }[number]\n | EmptyInputError\n | PromiseRejectedError,\n | { [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> | Promise<Result<unknown, unknown, infer C>> ? C : never }[number]\n | PromiseRejectionCause\n >\n> {\n if (results.length === 0) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return err({ type: \"EMPTY_INPUT\", message: \"anyAsync() requires at least one Result\" }) as any;\n }\n\n return new Promise((resolve) => {\n let settled = false;\n let pendingCount = results.length;\n let firstError: Err<unknown, unknown> | null = null;\n\n for (const item of results) {\n Promise.resolve(item)\n .catch((reason) =>\n err(\n { type: PROMISE_REJECTED, cause: reason } as PromiseRejectedError,\n { cause: { type: \"PROMISE_REJECTION\", reason } as PromiseRejectionCause }\n )\n )\n .then((result) => {\n if (settled) return;\n\n if (result.ok) {\n settled = true;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n resolve(result as any);\n return;\n }\n\n if (!firstError) firstError = result;\n pendingCount--;\n\n if (pendingCount === 0) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n resolve(firstError as any);\n }\n });\n }\n });\n}\n\n/**\n * Combines exactly two Results into a tuple.\n */\nexport function zip<A, EA, CA, B, EB, CB>(\n a: Result<A, EA, CA>,\n b: Result<B, EB, CB>\n): Result<[A, B], EA | EB, CA | CB> {\n if (!a.ok) return a;\n if (!b.ok) return b;\n return ok([a.value, b.value]);\n}\n\n/**\n * Async version of zip.\n */\nexport async function zipAsync<A, EA, CA, B, EB, CB>(\n a: Result<A, EA, CA> | Promise<Result<A, EA, CA>>,\n b: Result<B, EB, CB> | Promise<Result<B, EB, CB>>\n): Promise<Result<[A, B], EA | EB | PromiseRejectedError, CA | CB | PromiseRejectionCause>> {\n // Wrap rejections into PromiseRejectedError (consistent with allAsync)\n const wrapRejection = <T, E, C>(\n p: Result<T, E, C> | Promise<Result<T, E, C>>\n ): Promise<Result<T, E | PromiseRejectedError, C | PromiseRejectionCause>> =>\n Promise.resolve(p).catch((reason) =>\n err(\n { type: PROMISE_REJECTED, cause: reason } as PromiseRejectedError,\n { cause: { type: \"PROMISE_REJECTION\", reason } as PromiseRejectionCause }\n )\n );\n\n const [ra, rb] = await Promise.all([wrapRejection(a), wrapRejection(b)]);\n return zip(ra, rb);\n}\n\n// =============================================================================\n// Flatten\n// =============================================================================\n\n/**\n * Flattens a nested Result into a single Result.\n *\n * @remarks When to use: Unwrap a Result<Result<T, E1>, E2> into Result<T, E1 | E2> after an operation that returns nested Results.\n */\nexport function flatten<T, E1, C1, E2, C2>(\n result: Result<Result<T, E1, C1>, E2, C2>\n): Result<T, E1 | E2, C1 | C2> {\n if (!result.ok) return result as Err<E2, C2>;\n return result.value;\n}\n\n// =============================================================================\n// Deserialization (improved hydrate)\n// =============================================================================\n\n/** Discriminant for deserialization errors */\nexport const DESERIALIZATION_ERROR = \"DESERIALIZATION_ERROR\" as const;\n\n/** Error type returned when deserialize() receives invalid input */\nexport type DeserializationError = { type: typeof DESERIALIZATION_ERROR; value: unknown };\n\n/**\n * Deserialize a value back into a Result.\n * Returns a typed DeserializationError on invalid input instead of null.\n *\n * @remarks When to use: Rehydrate Results from JSON, RPC, or server actions with type-safe error handling.\n */\nexport function deserialize<T, E, C = unknown>(\n value: unknown\n): Result<T, E | DeserializationError, C> {\n if (typeof value !== \"object\" || value === null) {\n return err({ type: DESERIALIZATION_ERROR, value } as DeserializationError);\n }\n if (!(\"ok\" in value)) {\n return err({ type: DESERIALIZATION_ERROR, value } as DeserializationError);\n }\n\n const obj = value as Record<string, unknown>;\n if (obj.ok === true && \"value\" in obj) {\n return ok(obj.value as T);\n }\n if (obj.ok === false && \"error\" in obj) {\n return err(obj.error as E, { cause: obj.cause as C });\n }\n return err({ type: DESERIALIZATION_ERROR, value } as DeserializationError);\n}\n\n// =============================================================================\n// Serialization\n// =============================================================================\n\n/** A plain serialized form of a Result, safe to JSON.stringify. */\nexport type SerializedResult<T, E> = { ok: true; value: T } | { ok: false; error: E };\n\n/**\n * Serialize a Result to a plain object (inverse of `deserialize`).\n * Strips cause — safe for JSON.stringify, RPC, and server actions.\n *\n * @remarks When to use: Sending Results over the wire or storing them in JSON.\n */\nexport function serialize<T, E>(result: Result<T, E>): SerializedResult<T, E> {\n return result.ok\n ? { ok: true, value: result.value }\n : { ok: false, error: result.error };\n}\n\n// =============================================================================\n// Partial error matching\n// =============================================================================\n\n/**\n * Non-exhaustive error match — handle the errors you care about; let the rest fall through to fallback.\n *\n * @example\n * ```typescript\n * const message = matchErrorPartial(\n * error,\n * { NOT_FOUND: () => 'Resource not found' },\n * (e) => `Unexpected: ${e}`\n * );\n * ```\n */\nexport function matchErrorPartial<E extends string, R>(\n error: E | UnexpectedError,\n handlers: Partial<MatchErrorHandlers<E, R>>,\n fallback: (error: E | UnexpectedError) => R\n): R {\n if (isUnexpectedError(error)) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const h = (handlers as any).UnexpectedError as ((e: UnexpectedError) => R) | undefined;\n return h ? h(error) : fallback(error);\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const h = (handlers as any)[error as string] as ((e: E) => R) | undefined;\n return h ? h(error as E) : fallback(error);\n}\n\n// Retry helper is intentionally NOT re-exported here.\n// Import from the dedicated subpath to keep awaitly/result minimal:\n// import { tryAsyncRetry } from 'awaitly/result/retry';\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,mBAAAE,IAAA,eAAAC,EAAAH,GC+GO,SAASI,EAAMC,EAAyB,CAC7C,MAAO,CAAE,GAAI,GAAe,MAAOA,CAAkB,CACvD,CAOO,SAASC,EAAoBC,EAAUC,EAAoC,CAChF,IAAMC,EAAQD,GAAS,MACvB,MAAO,CAAE,GAAI,GAAgB,MAAAD,EAAO,GAAIE,IAAU,OAAY,CAAE,MAAAA,CAAM,EAAI,CAAC,CAAG,CAChF,CDhFA,eAAsBC,EACpBC,EACAC,EACAC,EAC6B,CAC7B,IAAMC,EAAU,OAAOF,GAAoB,WAAaA,EAAkB,OAEpEG,GADS,OAAOH,GAAoB,WAAaC,EAAeD,GACjD,MAEfI,EAAYC,GAA4B,CAC5C,OAAQF,EAAM,QAAS,CACrB,IAAK,SACH,OAAOA,EAAM,SAAWE,EAAU,GACpC,IAAK,cACH,OAAOF,EAAM,QAAU,GAAKE,EAE9B,QACE,OAAOF,EAAM,OACjB,CACF,EAEMG,EAASC,GAAe,IAAI,QAAeC,GAAY,WAAWA,EAASD,CAAE,CAAC,EAE9EE,EAAU,SAAyC,CACvD,GAAI,CACF,OAAOC,EAAG,MAAMX,EAAG,CAAC,CACtB,OAASY,EAAO,CACd,OAAOT,EAAUU,EAAIV,EAAQS,CAAK,EAAG,CAAE,MAAAA,CAAM,CAAC,EAAIC,EAAID,CAAK,CAC7D,CACF,EAEIE,EAAS,MAAMJ,EAAQ,EACrBK,EAAgBX,EAAM,cAAgB,IAAM,IAElD,QAASE,EAAU,EAAGA,EAAUF,EAAM,OAChC,EAAAU,EAAO,IACP,CAACC,EAAcD,EAAO,KAAU,GAFOR,IAG3C,MAAMC,EAAMF,EAASC,CAAO,CAAC,EAC7BQ,EAAS,MAAMJ,EAAQ,EAGzB,OAAOI,CACT","names":["retry_exports","__export","tryAsyncRetry","__toCommonJS","ok","value","err","error","options","cause","tryAsyncRetry","fn","onErrorOrConfig","maybeConfig","onError","retry","getDelay","attempt","sleep","ms","resolve","execute","ok","cause","err","result","shouldRetryFn"]}
|
package/dist/result/retry.d.cts
CHANGED
package/dist/result/retry.d.ts
CHANGED
package/dist/result/retry.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function i(t){return{ok:!0,value:t}}function s(t,n){let o=n?.cause;return{ok:!1,error:t,...o!==void 0?{cause:o}:{}}}async function y(t,n,o){let a=typeof n=="function"?n:void 0,r=(typeof n=="function"?o:n).retry,
|
|
1
|
+
function i(t){return{ok:!0,value:t}}function s(t,n){let o=n?.cause;return{ok:!1,error:t,...o!==void 0?{cause:o}:{}}}async function y(t,n,o){let a=typeof n=="function"?n:void 0,r=(typeof n=="function"?o:n).retry,E=e=>{switch(r.backoff){case"linear":return r.delayMs*(e+1);case"exponential":return r.delayMs*2**e;default:return r.delayMs}},c=e=>new Promise(k=>setTimeout(k,e)),l=async()=>{try{return i(await t())}catch(e){return a?s(a(e),{cause:e}):s(e)}},u=await l(),T=r.shouldRetry??(()=>!0);for(let e=0;e<r.times&&!(u.ok||!T(u.error));e++)await c(E(e)),u=await l();return u}export{y as tryAsyncRetry};
|
|
2
2
|
//# sourceMappingURL=retry.js.map
|
package/dist/result/retry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/result/index.ts","../../src/result/retry.ts"],"sourcesContent":["/**\n * awaitly/result (internal)\n *\n * Core Result primitives - minimal bundle for typed error handling.\n * This file is intentionally kept small for optimal tree-shaking.\n * The full orchestration (run, step, etc.) lives in core.ts.\n */\n\n// =============================================================================\n// Core Result Types\n// =============================================================================\n\n/**\n * Represents a successful result.\n * Use `ok(value)` to create instances.\n */\nexport type Ok<T> = {\n ok: true;\n value: T;\n};\n\n/**\n * Represents a failed result.\n * Use `err(error)` to create instances.\n */\nexport type Err<E, C = unknown> = {\n ok: false;\n error: E;\n cause?: C;\n};\n\n/**\n * Represents a successful computation or a failed one.\n */\nexport type Result<T, E = unknown, C = unknown> = Ok<T> | Err<E, C>;\n\n/**\n * A Promise that resolves to a Result.\n */\nexport type AsyncResult<T, E = unknown, C = unknown> = Promise<Result<T, E, C>>;\n\nexport type UnexpectedStepFailureCause =\n | {\n type: \"STEP_FAILURE\";\n origin: \"result\";\n error: unknown;\n cause?: unknown;\n }\n | {\n type: \"STEP_FAILURE\";\n origin: \"throw\";\n error: unknown;\n thrown: unknown;\n };\n\nexport type UnexpectedCause =\n | { type: \"UNCAUGHT_EXCEPTION\"; thrown: unknown }\n | UnexpectedStepFailureCause;\n\n/** Discriminant for UnexpectedError type - use in switch statements */\nexport const UNEXPECTED_ERROR = \"UNEXPECTED_ERROR\" as const;\n\n/** Discriminant for PromiseRejectedError type - use in switch statements */\nexport const PROMISE_REJECTED = \"PROMISE_REJECTED\" as const;\n\n// =============================================================================\n// Named Error Constants (for static analysis)\n// =============================================================================\n\n/**\n * Named error constant for unexpected/unhandled errors.\n * Used by the analyzer when a step doesn't declare errors.\n */\nexport const AWAITLY_UNEXPECTED = \"AWAITLY_UNEXPECTED\" as const;\n\n/**\n * Named error constant for cancelled operations.\n */\nexport const AWAITLY_CANCELLED = \"AWAITLY_CANCELLED\" as const;\n\n/**\n * Named error constant for timed-out operations.\n */\nexport const AWAITLY_TIMEOUT = \"AWAITLY_TIMEOUT\" as const;\n\n// =============================================================================\n// Static Analysis Helpers\n// =============================================================================\n\n/**\n * Helper to create a tuple of string literal tags with preserved literal types.\n * Use this when you need to store error tags in a variable while keeping\n * TypeScript's literal type inference (avoiding widening to string[]).\n *\n * @param t - The string literal tags\n * @returns The same array with preserved literal types\n *\n * @example\n * ```typescript\n * // Without tags() - type widens to string[]\n * const errs = ['CART_NOT_FOUND', 'CART_EMPTY']; // string[]\n *\n * // With tags() - literal types preserved\n * const errs = tags('CART_NOT_FOUND', 'CART_EMPTY'); // readonly ['CART_NOT_FOUND', 'CART_EMPTY']\n *\n * await step('getCart', () => getCart(id), {\n * errors: errs, // Analyzer can extract literal types\n * out: 'cart',\n * });\n * ```\n */\nexport const tags = <const T extends readonly string[]>(...t: T): T => t;\n\nexport type UnexpectedError = {\n type: typeof UNEXPECTED_ERROR;\n cause: UnexpectedCause;\n};\nexport type PromiseRejectedError = { type: typeof PROMISE_REJECTED; cause: unknown };\n/** Cause type for promise rejections in async batch helpers */\nexport type PromiseRejectionCause = { type: \"PROMISE_REJECTION\"; reason: unknown };\nexport type EmptyInputError = { type: \"EMPTY_INPUT\"; message: string };\nexport type MaybeAsyncResult<T, E, C = unknown> = Result<T, E, C> | Promise<Result<T, E, C>>;\n\n// =============================================================================\n// Result Constructors\n// =============================================================================\n\n/**\n * Creates a successful Result.\n *\n * @remarks When to use: Wrap a successful value in a Result for consistent return types.\n */\nexport function ok(): Ok<void>;\nexport function ok<T>(value: T): Ok<T>;\nexport function ok<T>(value?: T): Ok<T | void> {\n return { ok: true as const, value: value as T | void };\n}\n\n/**\n * Creates a failed Result.\n *\n * @remarks When to use: Return a typed failure without throwing so callers can handle it explicitly.\n */\nexport function err<E, C = unknown>(error: E, options?: { cause?: C }): Err<E, C> {\n const cause = options?.cause;\n return { ok: false as const, error, ...(cause !== undefined ? { cause } : {}) } as Err<E, C>;\n}\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\n/**\n * Checks if a Result is successful.\n *\n * @remarks When to use: Prefer functional-style checks or array filtering over `result.ok`.\n */\nexport const isOk = <T, E, C>(r: Result<T, E, C>): r is Ok<T> => r.ok;\n\n/**\n * Checks if a Result is a failure.\n *\n * @remarks When to use: Prefer functional-style checks or array filtering over `result.ok`.\n */\nexport const isErr = <T, E, C>(r: Result<T, E, C>): r is Err<E, C> => !r.ok;\n\n/**\n * Checks if an error is an UnexpectedError.\n *\n * @remarks When to use: Distinguish unexpected failures from your typed error union.\n */\nexport const isUnexpectedError = (e: unknown): e is UnexpectedError =>\n typeof e === \"object\" &&\n e !== null &&\n \"type\" in e &&\n e.type === UNEXPECTED_ERROR;\n\n/**\n * Checks if an error is a PromiseRejectedError.\n */\nexport const isPromiseRejectedError = (e: unknown): e is PromiseRejectedError =>\n typeof e === \"object\" &&\n e !== null &&\n \"type\" in e &&\n e.type === PROMISE_REJECTED;\n\n// =============================================================================\n// Error Matching\n// =============================================================================\n\nexport type MatchErrorHandlers<E extends string, R> = {\n [K in E]: (error: K extends \"UNEXPECTED_ERROR\" ? UnexpectedError : K) => R;\n};\n\n/**\n * Match on string error types with exhaustive checking.\n * Takes an error value (not a Result) and handlers for each error type.\n */\nexport function matchError<E extends string, R>(\n error: E | UnexpectedError,\n handlers: MatchErrorHandlers<E, R>\n): R {\n // Handle UnexpectedError objects\n if (isUnexpectedError(error)) {\n return (handlers as MatchErrorHandlers<\"UNEXPECTED_ERROR\", R>).UNEXPECTED_ERROR(error);\n }\n // Handle the string literal \"UNEXPECTED_ERROR\" - wrap it in an UnexpectedError object\n // to maintain the typed contract that UNEXPECTED_ERROR handler receives an object\n if (error === \"UNEXPECTED_ERROR\") {\n const syntheticError: UnexpectedError = {\n type: UNEXPECTED_ERROR,\n cause: { type: \"UNCAUGHT_EXCEPTION\", thrown: error },\n };\n return (handlers as MatchErrorHandlers<\"UNEXPECTED_ERROR\", R>).UNEXPECTED_ERROR(syntheticError);\n }\n // Cast to the excluded type since we've handled UNEXPECTED_ERROR above\n type StringErrors = Exclude<E, \"UNEXPECTED_ERROR\">;\n return (handlers as unknown as Record<string, (e: string) => R>)[error as StringErrors](error as StringErrors);\n}\n\n// =============================================================================\n// Type Utilities\n// =============================================================================\n\ntype AnyFunction = (...args: never[]) => unknown;\n\n/**\n * Helper to extract the error type from Result or AsyncResult return values.\n * Works even when a function is declared to return a union of both forms.\n */\ntype ErrorOfReturn<R> = Extract<Awaited<R>, { ok: false }> extends { error: infer E }\n ? E\n : never;\n\n/**\n * Extract error type from a single function's return type\n */\nexport type ErrorOf<T extends AnyFunction> = ErrorOfReturn<ReturnType<T>>;\n\n/**\n * Extract union of error types from multiple functions\n */\nexport type Errors<T extends AnyFunction[]> = {\n [K in keyof T]: ErrorOf<T[K]>;\n}[number];\n\n/**\n * Extract value type from Result\n */\nexport type ExtractValue<T> = T extends { ok: true; value: infer U }\n ? U\n : never;\n\n/**\n * Extract error type from Result\n */\nexport type ExtractError<T> = T extends { ok: false; error: infer E }\n ? E\n : never;\n\n/**\n * Extract cause type from Result\n */\nexport type ExtractCause<T> = T extends { ok: false; cause?: infer C }\n ? C\n : never;\n\n/**\n * Helper to extract the cause type from Result or AsyncResult return values.\n * Works even when a function is declared to return a union of both forms.\n */\ntype CauseOfReturn<R> = Extract<Awaited<R>, { ok: false }> extends { cause?: infer C }\n ? C\n : never;\n\n/**\n * Extract cause type from a function's return type\n */\nexport type CauseOf<T extends AnyFunction> = CauseOfReturn<ReturnType<T>>;\n\n// =============================================================================\n// Unwrap Utilities\n// =============================================================================\n\n/**\n * Error thrown when attempting to unwrap an Err result.\n */\nexport class UnwrapError extends Error {\n public readonly error: unknown;\n public readonly cause?: unknown;\n\n constructor(result: Err<unknown, unknown>) {\n const errorStr =\n typeof result.error === \"string\"\n ? result.error\n : JSON.stringify(result.error);\n super(`Attempted to unwrap an Err: ${errorStr}`);\n this.name = \"UnwrapError\";\n this.error = result.error;\n this.cause = result.cause;\n }\n}\n\n/**\n * Extracts the value from an Ok result, or throws UnwrapError if it's an Err.\n *\n * @remarks When to use: Only at boundaries or tests where a failure should be fatal.\n */\nexport const unwrap = <T, E, C>(r: Result<T, E, C>): T => {\n if (r.ok) return r.value;\n throw new UnwrapError(r);\n};\n\n/**\n * Extracts the value from an Ok result, or returns a default value if it's an Err.\n *\n * @remarks When to use: Provide a safe fallback without branching.\n */\nexport const unwrapOr = <T, E, C>(r: Result<T, E, C>, defaultValue: T): T =>\n r.ok ? r.value : defaultValue;\n\n/**\n * Extracts the value from an Ok result, or calls a function to get a default value if it's an Err.\n *\n * @remarks When to use: Compute a fallback from the error (logging, metrics, or derived defaults).\n */\nexport const unwrapOrElse = <T, E, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => T\n): T => (r.ok ? r.value : fn(r.error, r.cause));\n\n/**\n * Alias for `unwrap`. Returns the success value or throws.\n *\n * The Result is already computed; use when you want the value or throw (e.g. at boundaries or in tests).\n *\n * @param r - The Result to unwrap\n * @returns The success value if the Result is successful\n * @throws {UnwrapError} If the Result is an error (includes the error and cause)\n */\nexport const runOrThrow = <T, E, C>(r: Result<T, E, C>): T => unwrap(r);\n\n/**\n * Awaits a Promise of a Result, then returns the success value or rejects.\n *\n * The returned promise **resolves with T** on success and **rejects with UnwrapError** on failure.\n * UnwrapError extends Error and carries the original `error` and `cause` from the Err.\n *\n * @param ar - A Promise or thenable that resolves to a Result\n * @returns A Promise that resolves with the success value or rejects with UnwrapError\n */\nexport const runOrThrowAsync = <T, E, C>(\n ar: PromiseLike<Result<T, E, C>>\n): Promise<T> => Promise.resolve(ar).then(unwrap);\n\n/**\n * Convenience alias for `unwrapOr(r, null)`. Returns the success value or null.\n *\n * @param r - The Result to unwrap\n * @returns The success value if successful, otherwise null\n */\nexport const runOrNull = <T, E, C>(r: Result<T, E, C>): T | null =>\n r.ok ? r.value : null;\n\n/**\n * Convenience alias for `unwrapOr(r, undefined)`. Returns the success value or undefined.\n *\n * @param r - The Result to unwrap\n * @returns The success value if successful, otherwise undefined\n */\nexport const runOrUndefined = <T, E, C>(r: Result<T, E, C>): T | undefined =>\n r.ok ? r.value : undefined;\n\n// =============================================================================\n// Wrapping Functions\n// =============================================================================\n\n/**\n * Wraps a synchronous function that might throw into a Result.\n *\n * @remarks When to use: Wrap sync code that might throw so exceptions become Err values.\n */\nexport function from<T>(fn: () => T): Ok<T> | Err<unknown, unknown>;\nexport function from<T, E>(fn: () => T, onError: (cause: unknown) => E): Ok<T> | Err<E, unknown>;\nexport function from<T, E>(fn: () => T, onError?: (cause: unknown) => E) {\n try {\n return ok(fn());\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n}\n\n/**\n * Wraps a Promise into a Result.\n *\n * @remarks When to use: Wrap a Promise and keep the raw rejection as Err; use tryAsync to map errors.\n */\nexport function fromPromise<T>(promise: Promise<T>): Promise<Ok<T> | Err<unknown, unknown>>;\nexport function fromPromise<T, E>(\n promise: Promise<T>,\n onError: (cause: unknown) => E\n): Promise<Ok<T> | Err<E, unknown>>;\nexport async function fromPromise<T, E>(\n promise: Promise<T>,\n onError?: (cause: unknown) => E\n): Promise<Ok<T> | Err<E | unknown, unknown>> {\n try {\n return ok(await promise);\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n}\n\n/**\n * Wraps an async function that might throw into an AsyncResult.\n *\n * @remarks When to use: Wrap async work and map thrown/rejected values into your typed error union.\n */\nexport function tryAsync<T>(fn: () => Promise<T>): AsyncResult<T, unknown>;\nexport function tryAsync<T, E>(\n fn: () => Promise<T>,\n onError: (cause: unknown) => E\n): AsyncResult<T, E>;\nexport async function tryAsync<T, E>(\n fn: () => Promise<T>,\n onError?: (cause: unknown) => E\n): AsyncResult<T, E | unknown> {\n try {\n return ok(await fn());\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n}\n\n/**\n * Converts a nullable value into a Result.\n *\n * @remarks When to use: Turn null/undefined into a typed error before continuing.\n */\nexport function fromNullable<T, E>(\n value: T | null | undefined,\n onNull: () => E\n): Result<T, E> {\n return value != null ? ok(value) : err(onNull());\n}\n\n// =============================================================================\n// Transformers\n// =============================================================================\n\n/**\n * Transforms the value inside an Ok result.\n *\n * @remarks When to use: Transform only the Ok value while leaving Err untouched.\n */\nexport function map<T, U>(r: Ok<T>, fn: (value: T) => U): Ok<U>;\nexport function map<T, U, E, C>(r: Err<E, C>, fn: (value: T) => U): Err<E, C>;\nexport function map<T, U, E, C>(r: Result<T, E, C>, fn: (value: T) => U): Result<U, E, C>;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function map(r: any, fn: any): any {\n return r.ok ? ok(fn(r.value)) : r;\n}\n\n/**\n * Transforms the error inside an Err result.\n *\n * @remarks When to use: Retype or normalize errors while leaving Ok values unchanged.\n */\nexport function mapError<T, E, F, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => F\n): Result<T, F, C> {\n return r.ok ? r : err(fn(r.error, r.cause), { cause: r.cause });\n}\n\n/**\n * Pattern match on a Result.\n *\n * @remarks When to use: Handle both Ok and Err in a single expression that returns a value.\n */\nexport function match<T, E, C, R>(r: Ok<T>, handlers: { ok: (value: T) => R; err: (error: E, cause?: C) => R }): R;\nexport function match<T, E, C, R>(r: Err<E, C>, handlers: { ok: (value: T) => R; err: (error: E, cause?: C) => R }): R;\nexport function match<T, E, C, R>(r: Result<T, E, C>, handlers: { ok: (value: T) => R; err: (error: E, cause?: C) => R }): R;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function match(r: any, handlers: any): any {\n return r.ok ? handlers.ok(r.value) : handlers.err(r.error, r.cause);\n}\n\n/**\n * Chain Result-returning functions.\n *\n * @remarks When to use: Chain dependent operations that return Result without nested branching.\n */\nexport function andThen<T, U>(r: Ok<T>, fn: (value: T) => Ok<U>): Ok<U>;\nexport function andThen<T, F, C2>(r: Ok<T>, fn: (value: T) => Err<F, C2>): Err<F, C2>;\nexport function andThen<T, U, F, C2>(r: Ok<T>, fn: (value: T) => Result<U, F, C2>): Result<U, F, C2>;\nexport function andThen<T, U, E, F, C1, C2>(r: Err<E, C1>, fn: (value: T) => Result<U, F, C2>): Err<E, C1>;\nexport function andThen<T, U, E, F, C1, C2>(r: Result<T, E, C1>, fn: (value: T) => Result<U, F, C2>): Result<U, E | F, C1 | C2>;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function andThen(r: any, fn: any): any {\n return r.ok ? fn(r.value) : r;\n}\n\n/**\n * Execute a side effect on Ok values.\n *\n * @remarks When to use: Add side effects (logging, metrics) on Ok without changing the Result.\n */\nexport function tap<T, E, C>(\n r: Result<T, E, C>,\n fn: (value: T) => void\n): Result<T, E, C> {\n if (r.ok) fn(r.value);\n return r;\n}\n\n/**\n * Execute a side effect on Err values.\n *\n * @remarks When to use: Add side effects (logging, metrics) on Err without changing the Result.\n */\nexport function tapError<T, E, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => void\n): Result<T, E, C> {\n if (!r.ok) fn(r.error, r.cause);\n return r;\n}\n\n/**\n * Transform value with a function that might throw.\n *\n * @remarks When to use: Transform Ok values with a function that might throw and capture the failure.\n */\nexport function mapTry<T, U, E, F, C>(\n r: Result<T, E, C>,\n fn: (value: T) => U,\n onError: (thrown: unknown) => F\n): Result<U, E | F, C | unknown> {\n if (!r.ok) return r;\n try {\n return ok(fn(r.value));\n } catch (error) {\n return err(onError(error), { cause: error });\n }\n}\n\n/**\n * Transform error with a function that might throw.\n *\n * @remarks When to use: Transform errors when the mapping might throw and you want that captured.\n */\nexport function mapErrorTry<T, E, F, G, C>(\n r: Result<T, E, C>,\n fn: (error: E) => F,\n onError: (thrown: unknown) => G\n): Result<T, F | G, C | unknown> {\n if (r.ok) return r;\n try {\n return err(fn(r.error), { cause: r.cause });\n } catch (error) {\n return err(onError(error), { cause: error });\n }\n}\n\n/**\n * Transform both value and error.\n */\nexport function bimap<T, U, E, F, C>(\n r: Result<T, E, C>,\n onOk: (value: T) => U,\n onErr: (error: E, cause?: C) => F\n): Result<U, F, C> {\n return r.ok ? ok(onOk(r.value)) : err(onErr(r.error, r.cause), { cause: r.cause });\n}\n\n/**\n * Provide an alternative Result if the first is an Err.\n *\n * @remarks When to use: Recover from Err by returning a fallback Result or retyping the error.\n */\nexport function orElse<T, E, E2, C, C2>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => Result<T, E2, C2>\n): Result<T, E2, C | C2> {\n return r.ok ? r : fn(r.error, r.cause);\n}\n\n/**\n * Async version of orElse.\n */\nexport async function orElseAsync<T, E, E2, C, C2>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => Promise<Result<T, E2, C2>>\n): Promise<Result<T, E2, C | C2>> {\n return r.ok ? r : fn(r.error, r.cause);\n}\n\n/**\n * Recover from errors - always returns Ok<T>.\n */\nexport function recover<T, E, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => T\n): Ok<T> {\n return r.ok ? ok(r.value) : ok(fn(r.error, r.cause));\n}\n\n/**\n * Async version of recover - always returns Promise<Ok<T>>.\n */\nexport async function recoverAsync<T, E, C>(\n r: Result<T, E, C> | Promise<Result<T, E, C>>,\n fn: (error: E, cause?: C) => T | Promise<T>\n): Promise<Ok<T>> {\n const resolved = await r;\n if (resolved.ok) return ok(resolved.value);\n return ok(await fn(resolved.error, resolved.cause));\n}\n\n// =============================================================================\n// Result Hydration (Serialization)\n// =============================================================================\n\n/**\n * Hydrate a serialized Result back into a proper Result object.\n */\nexport function hydrate<T, E, C = unknown>(value: unknown): Result<T, E, C> | null {\n if (typeof value !== \"object\" || value === null) return null;\n if (!(\"ok\" in value)) return null;\n\n const obj = value as Record<string, unknown>;\n if (obj.ok === true && \"value\" in obj) {\n return ok(obj.value as T);\n }\n if (obj.ok === false && \"error\" in obj) {\n return err(obj.error as E, { cause: obj.cause as C });\n }\n return null;\n}\n\n/**\n * Type guard to check if a value is a serialized Result.\n */\nexport function isSerializedResult(\n value: unknown\n): value is { ok: boolean; value?: unknown; error?: unknown; cause?: unknown } {\n if (typeof value !== \"object\" || value === null) return false;\n if (!(\"ok\" in value)) return false;\n const obj = value as Record<string, unknown>;\n return (\n (obj.ok === true && \"value\" in obj) ||\n (obj.ok === false && \"error\" in obj)\n );\n}\n\n// =============================================================================\n// Batch Operations\n// =============================================================================\n\ntype AllValues<T extends readonly Result<unknown, unknown, unknown>[]> = {\n [K in keyof T]: T[K] extends Ok<infer V>\n ? V\n : T[K] extends Err<unknown, unknown>\n ? never\n : T[K] extends Result<infer V, unknown, unknown>\n ? V\n : never;\n};\ntype AllErrors<T extends readonly Result<unknown, unknown, unknown>[]> = {\n [K in keyof T]: T[K] extends Ok<unknown>\n ? never\n : T[K] extends Err<infer E, unknown>\n ? E\n : T[K] extends Result<unknown, infer E, unknown>\n ? E\n : never;\n}[number];\ntype AllCauses<T extends readonly Result<unknown, unknown, unknown>[]> = {\n [K in keyof T]: T[K] extends Ok<unknown>\n ? never\n : T[K] extends Err<unknown, infer C>\n ? C\n : T[K] extends Result<unknown, unknown, infer C>\n ? C\n : never;\n}[number];\n\n// Conditional type: returns Ok<...> when there are no errors, Result<...> otherwise\n// Note: We only check AllErrors, not AllCauses - causes only matter when there are errors\ntype AllResult<T extends readonly Result<unknown, unknown, unknown>[]> =\n [AllErrors<T>] extends [never]\n ? Ok<AllValues<T>>\n : Result<AllValues<T>, AllErrors<T>, AllCauses<T>>;\n\n/**\n * Combines multiple Results into a single Result containing an array of values.\n * Returns the first Err encountered, or Ok with all values.\n */\nexport function all<const T extends readonly Result<unknown, unknown, unknown>[]>(\n results: T\n): AllResult<T> {\n const values: unknown[] = [];\n for (const result of results) {\n if (!result.ok) {\n return result as unknown as AllResult<T>;\n }\n values.push(result.value);\n }\n return ok(values) as AllResult<T>;\n}\n\n/**\n * Async version of all - works with Promises of Results.\n */\nexport async function allAsync<\n const T extends readonly (Result<unknown, unknown, unknown> | Promise<Result<unknown, unknown, unknown>>)[]\n>(\n results: T\n): Promise<\n Result<\n { [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> | Promise<Result<infer V, unknown, unknown>> ? V : never },\n | { [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> | Promise<Result<unknown, infer E, unknown>> ? E : never }[number]\n | PromiseRejectedError,\n | { [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> | Promise<Result<unknown, unknown, infer C>> ? C : never }[number]\n | PromiseRejectionCause\n >\n> {\n const values: unknown[] = [];\n for (const resultOrPromise of results) {\n try {\n const r = await resultOrPromise;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (!r.ok) return r as any;\n values.push(r.value);\n } catch (reason) {\n return err(\n { type: PROMISE_REJECTED, cause: reason } as PromiseRejectedError,\n { cause: { type: \"PROMISE_REJECTION\", reason } as PromiseRejectionCause }\n );\n }\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return ok(values) as any;\n}\n\nexport type SettledError<E, C = unknown> = { error: E; cause?: C };\n\n// Conditional type: returns Ok<...> when there are no errors, Result<...> otherwise\ntype AllSettledResult<T extends readonly Result<unknown, unknown, unknown>[]> =\n [AllErrors<T>] extends [never]\n ? Ok<AllValues<T>>\n : Result<AllValues<T>, SettledError<AllErrors<T>, AllCauses<T>>[]>;\n\n/**\n * Collects all Results, returning Ok with values if all succeed,\n * or Err with array of errors if any fail.\n */\nexport function allSettled<const T extends readonly Result<unknown, unknown, unknown>[]>(\n results: T\n): AllSettledResult<T> {\n const values: unknown[] = [];\n const errors: SettledError<unknown>[] = [];\n\n for (const result of results) {\n if (result.ok) {\n values.push(result.value);\n } else {\n errors.push({ error: result.error, cause: result.cause });\n }\n }\n\n if (errors.length > 0) {\n return err(errors) as unknown as AllSettledResult<T>;\n }\n\n return ok(values) as unknown as AllSettledResult<T>;\n}\n\n/**\n * Async version of allSettled.\n */\nexport async function allSettledAsync<\n const T extends readonly (Result<unknown, unknown, unknown> | Promise<Result<unknown, unknown, unknown>>)[]\n>(\n results: T\n): Promise<\n Result<\n { [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> | Promise<Result<infer V, unknown, unknown>> ? V : never },\n SettledError<\n | { [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> | Promise<Result<unknown, infer E, unknown>> ? E : never }[number]\n | PromiseRejectedError,\n | { [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> | Promise<Result<unknown, unknown, infer C>> ? C : never }[number]\n | PromiseRejectionCause\n >[]\n >\n> {\n const settled = await Promise.all(\n results.map((item) =>\n Promise.resolve(item)\n .then((result) => ({ status: \"result\" as const, result }))\n .catch((reason) => ({\n status: \"rejected\" as const,\n error: { type: PROMISE_REJECTED, cause: reason } as PromiseRejectedError,\n cause: { type: \"PROMISE_REJECTION\", reason } as PromiseRejectionCause,\n }))\n )\n );\n\n const values: unknown[] = [];\n const errors: SettledError<unknown, unknown>[] = [];\n\n for (const item of settled) {\n if (item.status === \"rejected\") {\n errors.push({ error: item.error, cause: item.cause });\n } else if (item.result.ok) {\n values.push(item.result.value);\n } else {\n errors.push({ error: item.result.error, cause: item.result.cause });\n }\n }\n\n if (errors.length > 0) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return err(errors) as any;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return ok(values) as any;\n}\n\n/**\n * Partitions Results into { values, errors }.\n */\nexport function partition<T, E, C>(\n results: readonly Result<T, E, C>[]\n): { values: T[]; errors: E[] } {\n const values: T[] = [];\n const errors: E[] = [];\n for (const r of results) {\n if (r.ok) values.push(r.value);\n else errors.push(r.error);\n }\n return { values, errors };\n}\n\n/**\n * Returns the first Ok result, or an EmptyInputError/first Err if all fail.\n */\nexport function any<const T extends readonly Result<unknown, unknown, unknown>[]>(\n results: T\n): T extends readonly []\n ? Err<EmptyInputError, unknown>\n : Result<\n { [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> ? V : never }[number],\n AllErrors<T> | EmptyInputError,\n AllCauses<T>\n >;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function any(results: any): any {\n if (results.length === 0) {\n return err({ type: \"EMPTY_INPUT\", message: \"any() requires at least one Result\" });\n }\n let firstErr: Err<unknown, unknown> | undefined;\n for (const r of results) {\n if (r.ok) return r;\n if (!firstErr) firstErr = r;\n }\n return firstErr;\n}\n\n/**\n * Async version of any - races promises and returns first success.\n */\nexport async function anyAsync<\n const T extends readonly (Result<unknown, unknown, unknown> | Promise<Result<unknown, unknown, unknown>>)[]\n>(\n results: T\n): Promise<\n T extends readonly []\n ? Err<EmptyInputError, unknown>\n : Result<\n { [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> | Promise<Result<infer V, unknown, unknown>> ? V : never }[number],\n | { [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> | Promise<Result<unknown, infer E, unknown>> ? E : never }[number]\n | EmptyInputError\n | PromiseRejectedError,\n | { [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> | Promise<Result<unknown, unknown, infer C>> ? C : never }[number]\n | PromiseRejectionCause\n >\n> {\n if (results.length === 0) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return err({ type: \"EMPTY_INPUT\", message: \"anyAsync() requires at least one Result\" }) as any;\n }\n\n return new Promise((resolve) => {\n let settled = false;\n let pendingCount = results.length;\n let firstError: Err<unknown, unknown> | null = null;\n\n for (const item of results) {\n Promise.resolve(item)\n .catch((reason) =>\n err(\n { type: PROMISE_REJECTED, cause: reason } as PromiseRejectedError,\n { cause: { type: \"PROMISE_REJECTION\", reason } as PromiseRejectionCause }\n )\n )\n .then((result) => {\n if (settled) return;\n\n if (result.ok) {\n settled = true;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n resolve(result as any);\n return;\n }\n\n if (!firstError) firstError = result;\n pendingCount--;\n\n if (pendingCount === 0) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n resolve(firstError as any);\n }\n });\n }\n });\n}\n\n/**\n * Combines exactly two Results into a tuple.\n */\nexport function zip<A, EA, CA, B, EB, CB>(\n a: Result<A, EA, CA>,\n b: Result<B, EB, CB>\n): Result<[A, B], EA | EB, CA | CB> {\n if (!a.ok) return a;\n if (!b.ok) return b;\n return ok([a.value, b.value]);\n}\n\n/**\n * Async version of zip.\n */\nexport async function zipAsync<A, EA, CA, B, EB, CB>(\n a: Result<A, EA, CA> | Promise<Result<A, EA, CA>>,\n b: Result<B, EB, CB> | Promise<Result<B, EB, CB>>\n): Promise<Result<[A, B], EA | EB | PromiseRejectedError, CA | CB | PromiseRejectionCause>> {\n // Wrap rejections into PromiseRejectedError (consistent with allAsync)\n const wrapRejection = <T, E, C>(\n p: Result<T, E, C> | Promise<Result<T, E, C>>\n ): Promise<Result<T, E | PromiseRejectedError, C | PromiseRejectionCause>> =>\n Promise.resolve(p).catch((reason) =>\n err(\n { type: PROMISE_REJECTED, cause: reason } as PromiseRejectedError,\n { cause: { type: \"PROMISE_REJECTION\", reason } as PromiseRejectionCause }\n )\n );\n\n const [ra, rb] = await Promise.all([wrapRejection(a), wrapRejection(b)]);\n return zip(ra, rb);\n}\n\n// =============================================================================\n// Flatten\n// =============================================================================\n\n/**\n * Flattens a nested Result into a single Result.\n *\n * @remarks When to use: Unwrap a Result<Result<T, E1>, E2> into Result<T, E1 | E2> after an operation that returns nested Results.\n */\nexport function flatten<T, E1, C1, E2, C2>(\n result: Result<Result<T, E1, C1>, E2, C2>\n): Result<T, E1 | E2, C1 | C2> {\n if (!result.ok) return result as Err<E2, C2>;\n return result.value;\n}\n\n// =============================================================================\n// Deserialization (improved hydrate)\n// =============================================================================\n\n/** Discriminant for deserialization errors */\nexport const DESERIALIZATION_ERROR = \"DESERIALIZATION_ERROR\" as const;\n\n/** Error type returned when deserialize() receives invalid input */\nexport type DeserializationError = { type: typeof DESERIALIZATION_ERROR; value: unknown };\n\n/**\n * Deserialize a value back into a Result.\n * Returns a typed DeserializationError on invalid input instead of null.\n *\n * @remarks When to use: Rehydrate Results from JSON, RPC, or server actions with type-safe error handling.\n */\nexport function deserialize<T, E, C = unknown>(\n value: unknown\n): Result<T, E | DeserializationError, C> {\n if (typeof value !== \"object\" || value === null) {\n return err({ type: DESERIALIZATION_ERROR, value } as DeserializationError);\n }\n if (!(\"ok\" in value)) {\n return err({ type: DESERIALIZATION_ERROR, value } as DeserializationError);\n }\n\n const obj = value as Record<string, unknown>;\n if (obj.ok === true && \"value\" in obj) {\n return ok(obj.value as T);\n }\n if (obj.ok === false && \"error\" in obj) {\n return err(obj.error as E, { cause: obj.cause as C });\n }\n return err({ type: DESERIALIZATION_ERROR, value } as DeserializationError);\n}\n\n// =============================================================================\n// Serialization\n// =============================================================================\n\n/** A plain serialized form of a Result, safe to JSON.stringify. */\nexport type SerializedResult<T, E> = { ok: true; value: T } | { ok: false; error: E };\n\n/**\n * Serialize a Result to a plain object (inverse of `deserialize`).\n * Strips cause — safe for JSON.stringify, RPC, and server actions.\n *\n * @remarks When to use: Sending Results over the wire or storing them in JSON.\n */\nexport function serialize<T, E>(result: Result<T, E>): SerializedResult<T, E> {\n return result.ok\n ? { ok: true, value: result.value }\n : { ok: false, error: result.error };\n}\n\n// =============================================================================\n// Partial error matching\n// =============================================================================\n\n/**\n * Non-exhaustive error match — handle the errors you care about; let the rest fall through to fallback.\n *\n * @example\n * ```typescript\n * const message = matchErrorPartial(\n * error,\n * { NOT_FOUND: () => 'Resource not found' },\n * (e) => `Unexpected: ${e}`\n * );\n * ```\n */\nexport function matchErrorPartial<E extends string, R>(\n error: E | UnexpectedError,\n handlers: Partial<MatchErrorHandlers<E, R>>,\n fallback: (error: E | UnexpectedError) => R\n): R {\n if (isUnexpectedError(error)) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const h = (handlers as any).UNEXPECTED_ERROR as ((e: UnexpectedError) => R) | undefined;\n return h ? h(error) : fallback(error);\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const h = (handlers as any)[error as string] as ((e: E) => R) | undefined;\n return h ? h(error as E) : fallback(error);\n}\n\n// Retry helper is intentionally NOT re-exported here.\n// Import from the dedicated subpath to keep awaitly/result minimal:\n// import { tryAsyncRetry } from 'awaitly/result/retry';\n","/**\n * awaitly/result retry support\n *\n * Retry async operations with configurable backoff without the full workflow engine.\n */\n\nimport type { AsyncResult } from \"./index\";\nimport { ok, err } from \"./index\";\n\n/** Configuration for retry behavior */\nexport type RetryConfig<E = unknown> = {\n /** Number of retry attempts (not including the initial attempt) */\n times: number;\n /** Base delay between retries in milliseconds */\n delayMs: number;\n /** Backoff strategy */\n backoff?: \"constant\" | \"linear\" | \"exponential\";\n /** Predicate to determine if an error should trigger a retry. Defaults to always retry. */\n shouldRetry?: (error: E) => boolean;\n};\n\n/**\n * Wraps an async function that might throw into an AsyncResult, with retry support.\n *\n * @remarks When to use: Wrap async work with retry logic for transient failures without needing the full workflow engine.\n *\n * @example\n * ```typescript\n * const result = await tryAsyncRetry(\n * () => fetch('/api/data').then(r => r.json()),\n * { retry: { times: 3, delayMs: 100, backoff: 'exponential' } }\n * );\n * ```\n */\nexport function tryAsyncRetry<T>(\n fn: () => Promise<T>,\n config: { retry: RetryConfig<unknown> }\n): AsyncResult<T, unknown>;\nexport function tryAsyncRetry<T, E>(\n fn: () => Promise<T>,\n onError: (cause: unknown) => E,\n config: { retry: RetryConfig<E> }\n): AsyncResult<T, E>;\nexport async function tryAsyncRetry<T, E>(\n fn: () => Promise<T>,\n onErrorOrConfig: ((cause: unknown) => E) | { retry: RetryConfig<unknown> },\n maybeConfig?: { retry: RetryConfig<E> }\n): AsyncResult<T, E | unknown> {\n const onError = typeof onErrorOrConfig === \"function\" ? onErrorOrConfig : undefined;\n const config = typeof onErrorOrConfig === \"function\" ? maybeConfig! : onErrorOrConfig;\n const retry = config.retry;\n\n const getDelay = (attempt: number): number => {\n switch (retry.backoff) {\n case \"linear\":\n return retry.delayMs * (attempt + 1);\n case \"exponential\":\n return retry.delayMs * 2 ** attempt;\n case \"constant\":\n default:\n return retry.delayMs;\n }\n };\n\n const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\n\n const execute = async (): AsyncResult<T, E | unknown> => {\n try {\n return ok(await fn());\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n };\n\n let result = await execute();\n const shouldRetryFn = retry.shouldRetry ?? (() => true);\n\n for (let attempt = 0; attempt < retry.times; attempt++) {\n if (result.ok) break;\n if (!shouldRetryFn(result.error as E)) break;\n await sleep(getDelay(attempt));\n result = await execute();\n }\n\n return result;\n}\n"],"mappings":"AAsIO,SAASA,EAAMC,EAAyB,CAC7C,MAAO,CAAE,GAAI,GAAe,MAAOA,CAAkB,CACvD,CAOO,SAASC,EAAoBC,EAAUC,EAAoC,CAChF,IAAMC,EAAQD,GAAS,MACvB,MAAO,CAAE,GAAI,GAAgB,MAAAD,EAAO,GAAIE,IAAU,OAAY,CAAE,MAAAA,CAAM,EAAI,CAAC,CAAG,CAChF,CCvGA,eAAsBC,EACpBC,EACAC,EACAC,EAC6B,CAC7B,IAAMC,EAAU,OAAOF,GAAoB,WAAaA,EAAkB,OAEpEG,GADS,OAAOH,GAAoB,WAAaC,EAAeD,GACjD,MAEfI,EAAYC,GAA4B,CAC5C,OAAQF,EAAM,QAAS,CACrB,IAAK,SACH,OAAOA,EAAM,SAAWE,EAAU,GACpC,IAAK,cACH,OAAOF,EAAM,QAAU,GAAKE,EAE9B,QACE,OAAOF,EAAM,OACjB,CACF,EAEMG,EAASC,GAAe,IAAI,QAAeC,GAAY,WAAWA,EAASD,CAAE,CAAC,EAE9EE,EAAU,SAAyC,CACvD,GAAI,CACF,OAAOC,EAAG,MAAMX,EAAG,CAAC,CACtB,OAASY,EAAO,CACd,OAAOT,EAAUU,EAAIV,EAAQS,CAAK,EAAG,CAAE,MAAAA,CAAM,CAAC,EAAIC,EAAID,CAAK,CAC7D,CACF,EAEIE,EAAS,MAAMJ,EAAQ,EACrBK,EAAgBX,EAAM,cAAgB,IAAM,IAElD,QAASE,EAAU,EAAGA,EAAUF,EAAM,OAChC,EAAAU,EAAO,IACP,CAACC,EAAcD,EAAO,KAAU,GAFOR,IAG3C,MAAMC,EAAMF,EAASC,CAAO,CAAC,EAC7BQ,EAAS,MAAMJ,EAAQ,EAGzB,OAAOI,CACT","names":["ok","value","err","error","options","cause","tryAsyncRetry","fn","onErrorOrConfig","maybeConfig","onError","retry","getDelay","attempt","sleep","ms","resolve","execute","ok","cause","err","result","shouldRetryFn"]}
|
|
1
|
+
{"version":3,"sources":["../../src/result/index.ts","../../src/result/retry.ts"],"sourcesContent":["/**\n * awaitly/result (internal)\n *\n * Core Result primitives - minimal bundle for typed error handling.\n * This file is intentionally kept small for optimal tree-shaking.\n * The full orchestration (run, step, etc.) lives in core.ts.\n */\n\n// =============================================================================\n// Core Result Types\n// =============================================================================\n\n/**\n * Represents a successful result.\n * Use `ok(value)` to create instances.\n */\nexport type Ok<T> = {\n ok: true;\n value: T;\n};\n\n/**\n * Represents a failed result.\n * Use `err(error)` to create instances.\n */\nexport type Err<E, C = unknown> = {\n ok: false;\n error: E;\n cause?: C;\n};\n\n/**\n * Represents a successful computation or a failed one.\n */\nexport type Result<T, E = unknown, C = unknown> = Ok<T> | Err<E, C>;\n\n/**\n * A Promise that resolves to a Result.\n */\nexport type AsyncResult<T, E = unknown, C = unknown> = Promise<Result<T, E, C>>;\n\n/** Discriminant for PromiseRejectedError type - use in switch statements */\nexport const PROMISE_REJECTED = \"PROMISE_REJECTED\" as const;\n\n// =============================================================================\n// Named Error Constants (for static analysis)\n// =============================================================================\n\n/**\n * Named error constant for unexpected/unhandled errors.\n * Used by the analyzer when a step doesn't declare errors.\n */\nexport const AWAITLY_UNEXPECTED = \"AWAITLY_UNEXPECTED\" as const;\n\n/**\n * Named error constant for cancelled operations.\n */\nexport const AWAITLY_CANCELLED = \"AWAITLY_CANCELLED\" as const;\n\n/**\n * Named error constant for timed-out operations.\n */\nexport const AWAITLY_TIMEOUT = \"AWAITLY_TIMEOUT\" as const;\n\n// =============================================================================\n// Static Analysis Helpers\n// =============================================================================\n\n/**\n * Helper to create a tuple of string literal tags with preserved literal types.\n * Use this when you need to store error tags in a variable while keeping\n * TypeScript's literal type inference (avoiding widening to string[]).\n *\n * @param t - The string literal tags\n * @returns The same array with preserved literal types\n *\n * @example\n * ```typescript\n * // Without tags() - type widens to string[]\n * const errs = ['CART_NOT_FOUND', 'CART_EMPTY']; // string[]\n *\n * // With tags() - literal types preserved\n * const errs = tags('CART_NOT_FOUND', 'CART_EMPTY'); // readonly ['CART_NOT_FOUND', 'CART_EMPTY']\n *\n * await step('getCart', () => getCart(id), {\n * errors: errs, // Analyzer can extract literal types\n * out: 'cart',\n * });\n * ```\n */\nexport const tags = <const T extends readonly string[]>(...t: T): T => t;\n\nimport { UnexpectedError } from \"../errors\";\nexport { UnexpectedError };\nexport type PromiseRejectedError = { type: typeof PROMISE_REJECTED; cause: unknown };\n/** Cause type for promise rejections in async batch helpers */\nexport type PromiseRejectionCause = { type: \"PROMISE_REJECTION\"; reason: unknown };\nexport type EmptyInputError = { type: \"EMPTY_INPUT\"; message: string };\nexport type MaybeAsyncResult<T, E, C = unknown> = Result<T, E, C> | Promise<Result<T, E, C>>;\n\n// =============================================================================\n// Result Constructors\n// =============================================================================\n\n/**\n * Creates a successful Result.\n *\n * @remarks When to use: Wrap a successful value in a Result for consistent return types.\n */\nexport function ok(): Ok<void>;\nexport function ok<T>(value: T): Ok<T>;\nexport function ok<T>(value?: T): Ok<T | void> {\n return { ok: true as const, value: value as T | void };\n}\n\n/**\n * Creates a failed Result.\n *\n * @remarks When to use: Return a typed failure without throwing so callers can handle it explicitly.\n */\nexport function err<E, C = unknown>(error: E, options?: { cause?: C }): Err<E, C> {\n const cause = options?.cause;\n return { ok: false as const, error, ...(cause !== undefined ? { cause } : {}) } as Err<E, C>;\n}\n\n// =============================================================================\n// Type Guards\n// =============================================================================\n\n/**\n * Checks if a Result is successful.\n *\n * @remarks When to use: Prefer functional-style checks or array filtering over `result.ok`.\n */\nexport const isOk = <T, E, C>(r: Result<T, E, C>): r is Ok<T> => r.ok;\n\n/**\n * Checks if a Result is a failure.\n *\n * @remarks When to use: Prefer functional-style checks or array filtering over `result.ok`.\n */\nexport const isErr = <T, E, C>(r: Result<T, E, C>): r is Err<E, C> => !r.ok;\n\n/**\n * Checks if an error is an UnexpectedError.\n *\n * @remarks When to use: Distinguish unexpected failures from your typed error union.\n */\nexport const isUnexpectedError = (e: unknown): e is UnexpectedError =>\n e instanceof UnexpectedError ||\n (typeof e === \"object\" &&\n e !== null &&\n \"_tag\" in e &&\n (e as { _tag: string })._tag === \"UnexpectedError\");\n\n/**\n * Checks if an error is a PromiseRejectedError.\n */\nexport const isPromiseRejectedError = (e: unknown): e is PromiseRejectedError =>\n typeof e === \"object\" &&\n e !== null &&\n \"type\" in e &&\n e.type === PROMISE_REJECTED;\n\n// =============================================================================\n// Error Matching\n// =============================================================================\n\nexport type MatchErrorHandlers<E extends string, R> = {\n [K in Exclude<E, \"UnexpectedError\">]: (error: K) => R;\n} & {\n UnexpectedError: (error: UnexpectedError) => R;\n};\n\n/**\n * Match on string error types with exhaustive checking.\n * Takes an error value (not a Result) and handlers for each error type.\n */\nexport function matchError<E extends string, R>(\n error: E | UnexpectedError,\n handlers: MatchErrorHandlers<E, R>\n): R {\n // Handle UnexpectedError instances\n if (isUnexpectedError(error)) {\n return handlers.UnexpectedError(error as UnexpectedError);\n }\n // Handle string literal errors\n type StringErrors = Exclude<E, \"UnexpectedError\">;\n return (handlers as unknown as Record<string, (e: string) => R>)[error as StringErrors](error as StringErrors);\n}\n\n// =============================================================================\n// Type Utilities\n// =============================================================================\n\ntype AnyFunction = (...args: never[]) => unknown;\n\n/**\n * Helper to extract the error type from Result or AsyncResult return values.\n * Works even when a function is declared to return a union of both forms.\n */\ntype ErrorOfReturn<R> = Extract<Awaited<R>, { ok: false }> extends { error: infer E }\n ? E\n : never;\n\n/**\n * Extract error type from a single function's return type\n */\nexport type ErrorOf<T extends AnyFunction> = ErrorOfReturn<ReturnType<T>>;\n\n/**\n * Extract union of error types from multiple functions\n */\nexport type Errors<T extends AnyFunction[]> = {\n [K in keyof T]: ErrorOf<T[K]>;\n}[number];\n\n/**\n * Extract value type from Result\n */\nexport type ExtractValue<T> = T extends { ok: true; value: infer U }\n ? U\n : never;\n\n/**\n * Extract error type from Result\n */\nexport type ExtractError<T> = T extends { ok: false; error: infer E }\n ? E\n : never;\n\n/**\n * Extract cause type from Result\n */\nexport type ExtractCause<T> = T extends { ok: false; cause?: infer C }\n ? C\n : never;\n\n/**\n * Helper to extract the cause type from Result or AsyncResult return values.\n * Works even when a function is declared to return a union of both forms.\n */\ntype CauseOfReturn<R> = Extract<Awaited<R>, { ok: false }> extends { cause?: infer C }\n ? C\n : never;\n\n/**\n * Extract cause type from a function's return type\n */\nexport type CauseOf<T extends AnyFunction> = CauseOfReturn<ReturnType<T>>;\n\n// =============================================================================\n// Unwrap Utilities\n// =============================================================================\n\n/**\n * Error thrown when attempting to unwrap an Err result.\n */\nexport class UnwrapError extends Error {\n public readonly error: unknown;\n public readonly cause?: unknown;\n\n constructor(result: Err<unknown, unknown>) {\n const errorStr =\n typeof result.error === \"string\"\n ? result.error\n : JSON.stringify(result.error);\n super(`Attempted to unwrap an Err: ${errorStr}`);\n this.name = \"UnwrapError\";\n this.error = result.error;\n this.cause = result.cause;\n }\n}\n\n/**\n * Extracts the value from an Ok result, or throws UnwrapError if it's an Err.\n *\n * @remarks When to use: Only at boundaries or tests where a failure should be fatal.\n */\nexport const unwrap = <T, E, C>(r: Result<T, E, C>): T => {\n if (r.ok) return r.value;\n throw new UnwrapError(r);\n};\n\n/**\n * Extracts the value from an Ok result, or returns a default value if it's an Err.\n *\n * @remarks When to use: Provide a safe fallback without branching.\n */\nexport const unwrapOr = <T, E, C>(r: Result<T, E, C>, defaultValue: T): T =>\n r.ok ? r.value : defaultValue;\n\n/**\n * Extracts the value from an Ok result, or calls a function to get a default value if it's an Err.\n *\n * @remarks When to use: Compute a fallback from the error (logging, metrics, or derived defaults).\n */\nexport const unwrapOrElse = <T, E, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => T\n): T => (r.ok ? r.value : fn(r.error, r.cause));\n\n/**\n * Alias for `unwrap`. Returns the success value or throws.\n *\n * The Result is already computed; use when you want the value or throw (e.g. at boundaries or in tests).\n *\n * @param r - The Result to unwrap\n * @returns The success value if the Result is successful\n * @throws {UnwrapError} If the Result is an error (includes the error and cause)\n */\nexport const runOrThrow = <T, E, C>(r: Result<T, E, C>): T => unwrap(r);\n\n/**\n * Awaits a Promise of a Result, then returns the success value or rejects.\n *\n * The returned promise **resolves with T** on success and **rejects with UnwrapError** on failure.\n * UnwrapError extends Error and carries the original `error` and `cause` from the Err.\n *\n * @param ar - A Promise or thenable that resolves to a Result\n * @returns A Promise that resolves with the success value or rejects with UnwrapError\n */\nexport const runOrThrowAsync = <T, E, C>(\n ar: PromiseLike<Result<T, E, C>>\n): Promise<T> => Promise.resolve(ar).then(unwrap);\n\n/**\n * Convenience alias for `unwrapOr(r, null)`. Returns the success value or null.\n *\n * @param r - The Result to unwrap\n * @returns The success value if successful, otherwise null\n */\nexport const runOrNull = <T, E, C>(r: Result<T, E, C>): T | null =>\n r.ok ? r.value : null;\n\n/**\n * Convenience alias for `unwrapOr(r, undefined)`. Returns the success value or undefined.\n *\n * @param r - The Result to unwrap\n * @returns The success value if successful, otherwise undefined\n */\nexport const runOrUndefined = <T, E, C>(r: Result<T, E, C>): T | undefined =>\n r.ok ? r.value : undefined;\n\n// =============================================================================\n// Wrapping Functions\n// =============================================================================\n\n/**\n * Wraps a synchronous function that might throw into a Result.\n *\n * @remarks When to use: Wrap sync code that might throw so exceptions become Err values.\n */\nexport function from<T>(fn: () => T): Ok<T> | Err<unknown, unknown>;\nexport function from<T, E>(fn: () => T, onError: (cause: unknown) => E): Ok<T> | Err<E, unknown>;\nexport function from<T, E>(fn: () => T, onError?: (cause: unknown) => E) {\n try {\n return ok(fn());\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n}\n\n/**\n * Wraps a Promise into a Result.\n *\n * @remarks When to use: Wrap a Promise and keep the raw rejection as Err; use tryAsync to map errors.\n */\nexport function fromPromise<T>(promise: Promise<T>): Promise<Ok<T> | Err<unknown, unknown>>;\nexport function fromPromise<T, E>(\n promise: Promise<T>,\n onError: (cause: unknown) => E\n): Promise<Ok<T> | Err<E, unknown>>;\nexport async function fromPromise<T, E>(\n promise: Promise<T>,\n onError?: (cause: unknown) => E\n): Promise<Ok<T> | Err<E | unknown, unknown>> {\n try {\n return ok(await promise);\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n}\n\n/**\n * Wraps an async function that might throw into an AsyncResult.\n *\n * @remarks When to use: Wrap async work and map thrown/rejected values into your typed error union.\n */\nexport function tryAsync<T>(fn: () => Promise<T>): AsyncResult<T, unknown>;\nexport function tryAsync<T, E>(\n fn: () => Promise<T>,\n onError: (cause: unknown) => E\n): AsyncResult<T, E>;\nexport async function tryAsync<T, E>(\n fn: () => Promise<T>,\n onError?: (cause: unknown) => E\n): AsyncResult<T, E | unknown> {\n try {\n return ok(await fn());\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n}\n\n/**\n * Converts a nullable value into a Result.\n *\n * @remarks When to use: Turn null/undefined into a typed error before continuing.\n */\nexport function fromNullable<T, E>(\n value: T | null | undefined,\n onNull: () => E\n): Result<T, E> {\n return value != null ? ok(value) : err(onNull());\n}\n\n// =============================================================================\n// Transformers\n// =============================================================================\n\n/**\n * Transforms the value inside an Ok result.\n *\n * @remarks When to use: Transform only the Ok value while leaving Err untouched.\n */\nexport function map<T, U>(r: Ok<T>, fn: (value: T) => U): Ok<U>;\nexport function map<T, U, E, C>(r: Err<E, C>, fn: (value: T) => U): Err<E, C>;\nexport function map<T, U, E, C>(r: Result<T, E, C>, fn: (value: T) => U): Result<U, E, C>;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function map(r: any, fn: any): any {\n return r.ok ? ok(fn(r.value)) : r;\n}\n\n/**\n * Transforms the error inside an Err result.\n *\n * @remarks When to use: Retype or normalize errors while leaving Ok values unchanged.\n */\nexport function mapError<T, E, F, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => F\n): Result<T, F, C> {\n return r.ok ? r : err(fn(r.error, r.cause), { cause: r.cause });\n}\n\n/**\n * Pattern match on a Result.\n *\n * @remarks When to use: Handle both Ok and Err in a single expression that returns a value.\n */\nexport function match<T, E, C, R>(r: Ok<T>, handlers: { ok: (value: T) => R; err: (error: E, cause?: C) => R }): R;\nexport function match<T, E, C, R>(r: Err<E, C>, handlers: { ok: (value: T) => R; err: (error: E, cause?: C) => R }): R;\nexport function match<T, E, C, R>(r: Result<T, E, C>, handlers: { ok: (value: T) => R; err: (error: E, cause?: C) => R }): R;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function match(r: any, handlers: any): any {\n return r.ok ? handlers.ok(r.value) : handlers.err(r.error, r.cause);\n}\n\n/**\n * Chain Result-returning functions.\n *\n * @remarks When to use: Chain dependent operations that return Result without nested branching.\n */\nexport function andThen<T, U>(r: Ok<T>, fn: (value: T) => Ok<U>): Ok<U>;\nexport function andThen<T, F, C2>(r: Ok<T>, fn: (value: T) => Err<F, C2>): Err<F, C2>;\nexport function andThen<T, U, F, C2>(r: Ok<T>, fn: (value: T) => Result<U, F, C2>): Result<U, F, C2>;\nexport function andThen<T, U, E, F, C1, C2>(r: Err<E, C1>, fn: (value: T) => Result<U, F, C2>): Err<E, C1>;\nexport function andThen<T, U, E, F, C1, C2>(r: Result<T, E, C1>, fn: (value: T) => Result<U, F, C2>): Result<U, E | F, C1 | C2>;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function andThen(r: any, fn: any): any {\n return r.ok ? fn(r.value) : r;\n}\n\n/**\n * Execute a side effect on Ok values.\n *\n * @remarks When to use: Add side effects (logging, metrics) on Ok without changing the Result.\n */\nexport function tap<T, E, C>(\n r: Result<T, E, C>,\n fn: (value: T) => void\n): Result<T, E, C> {\n if (r.ok) fn(r.value);\n return r;\n}\n\n/**\n * Execute a side effect on Err values.\n *\n * @remarks When to use: Add side effects (logging, metrics) on Err without changing the Result.\n */\nexport function tapError<T, E, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => void\n): Result<T, E, C> {\n if (!r.ok) fn(r.error, r.cause);\n return r;\n}\n\n/**\n * Transform value with a function that might throw.\n *\n * @remarks When to use: Transform Ok values with a function that might throw and capture the failure.\n */\nexport function mapTry<T, U, E, F, C>(\n r: Result<T, E, C>,\n fn: (value: T) => U,\n onError: (thrown: unknown) => F\n): Result<U, E | F, C | unknown> {\n if (!r.ok) return r;\n try {\n return ok(fn(r.value));\n } catch (error) {\n return err(onError(error), { cause: error });\n }\n}\n\n/**\n * Transform error with a function that might throw.\n *\n * @remarks When to use: Transform errors when the mapping might throw and you want that captured.\n */\nexport function mapErrorTry<T, E, F, G, C>(\n r: Result<T, E, C>,\n fn: (error: E) => F,\n onError: (thrown: unknown) => G\n): Result<T, F | G, C | unknown> {\n if (r.ok) return r;\n try {\n return err(fn(r.error), { cause: r.cause });\n } catch (error) {\n return err(onError(error), { cause: error });\n }\n}\n\n/**\n * Transform both value and error.\n */\nexport function bimap<T, U, E, F, C>(\n r: Result<T, E, C>,\n onOk: (value: T) => U,\n onErr: (error: E, cause?: C) => F\n): Result<U, F, C> {\n return r.ok ? ok(onOk(r.value)) : err(onErr(r.error, r.cause), { cause: r.cause });\n}\n\n/**\n * Provide an alternative Result if the first is an Err.\n *\n * @remarks When to use: Recover from Err by returning a fallback Result or retyping the error.\n */\nexport function orElse<T, E, E2, C, C2>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => Result<T, E2, C2>\n): Result<T, E2, C | C2> {\n return r.ok ? r : fn(r.error, r.cause);\n}\n\n/**\n * Async version of orElse.\n */\nexport async function orElseAsync<T, E, E2, C, C2>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => Promise<Result<T, E2, C2>>\n): Promise<Result<T, E2, C | C2>> {\n return r.ok ? r : fn(r.error, r.cause);\n}\n\n/**\n * Recover from errors - always returns Ok<T>.\n */\nexport function recover<T, E, C>(\n r: Result<T, E, C>,\n fn: (error: E, cause?: C) => T\n): Ok<T> {\n return r.ok ? ok(r.value) : ok(fn(r.error, r.cause));\n}\n\n/**\n * Async version of recover - always returns Promise<Ok<T>>.\n */\nexport async function recoverAsync<T, E, C>(\n r: Result<T, E, C> | Promise<Result<T, E, C>>,\n fn: (error: E, cause?: C) => T | Promise<T>\n): Promise<Ok<T>> {\n const resolved = await r;\n if (resolved.ok) return ok(resolved.value);\n return ok(await fn(resolved.error, resolved.cause));\n}\n\n// =============================================================================\n// Result Hydration (Serialization)\n// =============================================================================\n\n/**\n * Hydrate a serialized Result back into a proper Result object.\n */\nexport function hydrate<T, E, C = unknown>(value: unknown): Result<T, E, C> | null {\n if (typeof value !== \"object\" || value === null) return null;\n if (!(\"ok\" in value)) return null;\n\n const obj = value as Record<string, unknown>;\n if (obj.ok === true && \"value\" in obj) {\n return ok(obj.value as T);\n }\n if (obj.ok === false && \"error\" in obj) {\n return err(obj.error as E, { cause: obj.cause as C });\n }\n return null;\n}\n\n/**\n * Type guard to check if a value is a serialized Result.\n */\nexport function isSerializedResult(\n value: unknown\n): value is { ok: boolean; value?: unknown; error?: unknown; cause?: unknown } {\n if (typeof value !== \"object\" || value === null) return false;\n if (!(\"ok\" in value)) return false;\n const obj = value as Record<string, unknown>;\n return (\n (obj.ok === true && \"value\" in obj) ||\n (obj.ok === false && \"error\" in obj)\n );\n}\n\n// =============================================================================\n// Batch Operations\n// =============================================================================\n\ntype AllValues<T extends readonly Result<unknown, unknown, unknown>[]> = {\n [K in keyof T]: T[K] extends Ok<infer V>\n ? V\n : T[K] extends Err<unknown, unknown>\n ? never\n : T[K] extends Result<infer V, unknown, unknown>\n ? V\n : never;\n};\ntype AllErrors<T extends readonly Result<unknown, unknown, unknown>[]> = {\n [K in keyof T]: T[K] extends Ok<unknown>\n ? never\n : T[K] extends Err<infer E, unknown>\n ? E\n : T[K] extends Result<unknown, infer E, unknown>\n ? E\n : never;\n}[number];\ntype AllCauses<T extends readonly Result<unknown, unknown, unknown>[]> = {\n [K in keyof T]: T[K] extends Ok<unknown>\n ? never\n : T[K] extends Err<unknown, infer C>\n ? C\n : T[K] extends Result<unknown, unknown, infer C>\n ? C\n : never;\n}[number];\n\n// Conditional type: returns Ok<...> when there are no errors, Result<...> otherwise\n// Note: We only check AllErrors, not AllCauses - causes only matter when there are errors\ntype AllResult<T extends readonly Result<unknown, unknown, unknown>[]> =\n [AllErrors<T>] extends [never]\n ? Ok<AllValues<T>>\n : Result<AllValues<T>, AllErrors<T>, AllCauses<T>>;\n\n/**\n * Combines multiple Results into a single Result containing an array of values.\n * Returns the first Err encountered, or Ok with all values.\n */\nexport function all<const T extends readonly Result<unknown, unknown, unknown>[]>(\n results: T\n): AllResult<T> {\n const values: unknown[] = [];\n for (const result of results) {\n if (!result.ok) {\n return result as unknown as AllResult<T>;\n }\n values.push(result.value);\n }\n return ok(values) as AllResult<T>;\n}\n\n/**\n * Async version of all - works with Promises of Results.\n */\nexport async function allAsync<\n const T extends readonly (Result<unknown, unknown, unknown> | Promise<Result<unknown, unknown, unknown>>)[]\n>(\n results: T\n): Promise<\n Result<\n { [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> | Promise<Result<infer V, unknown, unknown>> ? V : never },\n | { [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> | Promise<Result<unknown, infer E, unknown>> ? E : never }[number]\n | PromiseRejectedError,\n | { [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> | Promise<Result<unknown, unknown, infer C>> ? C : never }[number]\n | PromiseRejectionCause\n >\n> {\n const values: unknown[] = [];\n for (const resultOrPromise of results) {\n try {\n const r = await resultOrPromise;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if (!r.ok) return r as any;\n values.push(r.value);\n } catch (reason) {\n return err(\n { type: PROMISE_REJECTED, cause: reason } as PromiseRejectedError,\n { cause: { type: \"PROMISE_REJECTION\", reason } as PromiseRejectionCause }\n );\n }\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return ok(values) as any;\n}\n\nexport type SettledError<E, C = unknown> = { error: E; cause?: C };\n\n// Conditional type: returns Ok<...> when there are no errors, Result<...> otherwise\ntype AllSettledResult<T extends readonly Result<unknown, unknown, unknown>[]> =\n [AllErrors<T>] extends [never]\n ? Ok<AllValues<T>>\n : Result<AllValues<T>, SettledError<AllErrors<T>, AllCauses<T>>[]>;\n\n/**\n * Collects all Results, returning Ok with values if all succeed,\n * or Err with array of errors if any fail.\n */\nexport function allSettled<const T extends readonly Result<unknown, unknown, unknown>[]>(\n results: T\n): AllSettledResult<T> {\n const values: unknown[] = [];\n const errors: SettledError<unknown>[] = [];\n\n for (const result of results) {\n if (result.ok) {\n values.push(result.value);\n } else {\n errors.push({ error: result.error, cause: result.cause });\n }\n }\n\n if (errors.length > 0) {\n return err(errors) as unknown as AllSettledResult<T>;\n }\n\n return ok(values) as unknown as AllSettledResult<T>;\n}\n\n/**\n * Async version of allSettled.\n */\nexport async function allSettledAsync<\n const T extends readonly (Result<unknown, unknown, unknown> | Promise<Result<unknown, unknown, unknown>>)[]\n>(\n results: T\n): Promise<\n Result<\n { [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> | Promise<Result<infer V, unknown, unknown>> ? V : never },\n SettledError<\n | { [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> | Promise<Result<unknown, infer E, unknown>> ? E : never }[number]\n | PromiseRejectedError,\n | { [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> | Promise<Result<unknown, unknown, infer C>> ? C : never }[number]\n | PromiseRejectionCause\n >[]\n >\n> {\n const settled = await Promise.all(\n results.map((item) =>\n Promise.resolve(item)\n .then((result) => ({ status: \"result\" as const, result }))\n .catch((reason) => ({\n status: \"rejected\" as const,\n error: { type: PROMISE_REJECTED, cause: reason } as PromiseRejectedError,\n cause: { type: \"PROMISE_REJECTION\", reason } as PromiseRejectionCause,\n }))\n )\n );\n\n const values: unknown[] = [];\n const errors: SettledError<unknown, unknown>[] = [];\n\n for (const item of settled) {\n if (item.status === \"rejected\") {\n errors.push({ error: item.error, cause: item.cause });\n } else if (item.result.ok) {\n values.push(item.result.value);\n } else {\n errors.push({ error: item.result.error, cause: item.result.cause });\n }\n }\n\n if (errors.length > 0) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return err(errors) as any;\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return ok(values) as any;\n}\n\n/**\n * Partitions Results into { values, errors }.\n */\nexport function partition<T, E, C>(\n results: readonly Result<T, E, C>[]\n): { values: T[]; errors: E[] } {\n const values: T[] = [];\n const errors: E[] = [];\n for (const r of results) {\n if (r.ok) values.push(r.value);\n else errors.push(r.error);\n }\n return { values, errors };\n}\n\n/**\n * Returns the first Ok result, or an EmptyInputError/first Err if all fail.\n */\nexport function any<const T extends readonly Result<unknown, unknown, unknown>[]>(\n results: T\n): T extends readonly []\n ? Err<EmptyInputError, unknown>\n : Result<\n { [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> ? V : never }[number],\n AllErrors<T> | EmptyInputError,\n AllCauses<T>\n >;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function any(results: any): any {\n if (results.length === 0) {\n return err({ type: \"EMPTY_INPUT\", message: \"any() requires at least one Result\" });\n }\n let firstErr: Err<unknown, unknown> | undefined;\n for (const r of results) {\n if (r.ok) return r;\n if (!firstErr) firstErr = r;\n }\n return firstErr;\n}\n\n/**\n * Async version of any - races promises and returns first success.\n */\nexport async function anyAsync<\n const T extends readonly (Result<unknown, unknown, unknown> | Promise<Result<unknown, unknown, unknown>>)[]\n>(\n results: T\n): Promise<\n T extends readonly []\n ? Err<EmptyInputError, unknown>\n : Result<\n { [K in keyof T]: T[K] extends Result<infer V, unknown, unknown> | Promise<Result<infer V, unknown, unknown>> ? V : never }[number],\n | { [K in keyof T]: T[K] extends Result<unknown, infer E, unknown> | Promise<Result<unknown, infer E, unknown>> ? E : never }[number]\n | EmptyInputError\n | PromiseRejectedError,\n | { [K in keyof T]: T[K] extends Result<unknown, unknown, infer C> | Promise<Result<unknown, unknown, infer C>> ? C : never }[number]\n | PromiseRejectionCause\n >\n> {\n if (results.length === 0) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return err({ type: \"EMPTY_INPUT\", message: \"anyAsync() requires at least one Result\" }) as any;\n }\n\n return new Promise((resolve) => {\n let settled = false;\n let pendingCount = results.length;\n let firstError: Err<unknown, unknown> | null = null;\n\n for (const item of results) {\n Promise.resolve(item)\n .catch((reason) =>\n err(\n { type: PROMISE_REJECTED, cause: reason } as PromiseRejectedError,\n { cause: { type: \"PROMISE_REJECTION\", reason } as PromiseRejectionCause }\n )\n )\n .then((result) => {\n if (settled) return;\n\n if (result.ok) {\n settled = true;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n resolve(result as any);\n return;\n }\n\n if (!firstError) firstError = result;\n pendingCount--;\n\n if (pendingCount === 0) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n resolve(firstError as any);\n }\n });\n }\n });\n}\n\n/**\n * Combines exactly two Results into a tuple.\n */\nexport function zip<A, EA, CA, B, EB, CB>(\n a: Result<A, EA, CA>,\n b: Result<B, EB, CB>\n): Result<[A, B], EA | EB, CA | CB> {\n if (!a.ok) return a;\n if (!b.ok) return b;\n return ok([a.value, b.value]);\n}\n\n/**\n * Async version of zip.\n */\nexport async function zipAsync<A, EA, CA, B, EB, CB>(\n a: Result<A, EA, CA> | Promise<Result<A, EA, CA>>,\n b: Result<B, EB, CB> | Promise<Result<B, EB, CB>>\n): Promise<Result<[A, B], EA | EB | PromiseRejectedError, CA | CB | PromiseRejectionCause>> {\n // Wrap rejections into PromiseRejectedError (consistent with allAsync)\n const wrapRejection = <T, E, C>(\n p: Result<T, E, C> | Promise<Result<T, E, C>>\n ): Promise<Result<T, E | PromiseRejectedError, C | PromiseRejectionCause>> =>\n Promise.resolve(p).catch((reason) =>\n err(\n { type: PROMISE_REJECTED, cause: reason } as PromiseRejectedError,\n { cause: { type: \"PROMISE_REJECTION\", reason } as PromiseRejectionCause }\n )\n );\n\n const [ra, rb] = await Promise.all([wrapRejection(a), wrapRejection(b)]);\n return zip(ra, rb);\n}\n\n// =============================================================================\n// Flatten\n// =============================================================================\n\n/**\n * Flattens a nested Result into a single Result.\n *\n * @remarks When to use: Unwrap a Result<Result<T, E1>, E2> into Result<T, E1 | E2> after an operation that returns nested Results.\n */\nexport function flatten<T, E1, C1, E2, C2>(\n result: Result<Result<T, E1, C1>, E2, C2>\n): Result<T, E1 | E2, C1 | C2> {\n if (!result.ok) return result as Err<E2, C2>;\n return result.value;\n}\n\n// =============================================================================\n// Deserialization (improved hydrate)\n// =============================================================================\n\n/** Discriminant for deserialization errors */\nexport const DESERIALIZATION_ERROR = \"DESERIALIZATION_ERROR\" as const;\n\n/** Error type returned when deserialize() receives invalid input */\nexport type DeserializationError = { type: typeof DESERIALIZATION_ERROR; value: unknown };\n\n/**\n * Deserialize a value back into a Result.\n * Returns a typed DeserializationError on invalid input instead of null.\n *\n * @remarks When to use: Rehydrate Results from JSON, RPC, or server actions with type-safe error handling.\n */\nexport function deserialize<T, E, C = unknown>(\n value: unknown\n): Result<T, E | DeserializationError, C> {\n if (typeof value !== \"object\" || value === null) {\n return err({ type: DESERIALIZATION_ERROR, value } as DeserializationError);\n }\n if (!(\"ok\" in value)) {\n return err({ type: DESERIALIZATION_ERROR, value } as DeserializationError);\n }\n\n const obj = value as Record<string, unknown>;\n if (obj.ok === true && \"value\" in obj) {\n return ok(obj.value as T);\n }\n if (obj.ok === false && \"error\" in obj) {\n return err(obj.error as E, { cause: obj.cause as C });\n }\n return err({ type: DESERIALIZATION_ERROR, value } as DeserializationError);\n}\n\n// =============================================================================\n// Serialization\n// =============================================================================\n\n/** A plain serialized form of a Result, safe to JSON.stringify. */\nexport type SerializedResult<T, E> = { ok: true; value: T } | { ok: false; error: E };\n\n/**\n * Serialize a Result to a plain object (inverse of `deserialize`).\n * Strips cause — safe for JSON.stringify, RPC, and server actions.\n *\n * @remarks When to use: Sending Results over the wire or storing them in JSON.\n */\nexport function serialize<T, E>(result: Result<T, E>): SerializedResult<T, E> {\n return result.ok\n ? { ok: true, value: result.value }\n : { ok: false, error: result.error };\n}\n\n// =============================================================================\n// Partial error matching\n// =============================================================================\n\n/**\n * Non-exhaustive error match — handle the errors you care about; let the rest fall through to fallback.\n *\n * @example\n * ```typescript\n * const message = matchErrorPartial(\n * error,\n * { NOT_FOUND: () => 'Resource not found' },\n * (e) => `Unexpected: ${e}`\n * );\n * ```\n */\nexport function matchErrorPartial<E extends string, R>(\n error: E | UnexpectedError,\n handlers: Partial<MatchErrorHandlers<E, R>>,\n fallback: (error: E | UnexpectedError) => R\n): R {\n if (isUnexpectedError(error)) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const h = (handlers as any).UnexpectedError as ((e: UnexpectedError) => R) | undefined;\n return h ? h(error) : fallback(error);\n }\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const h = (handlers as any)[error as string] as ((e: E) => R) | undefined;\n return h ? h(error as E) : fallback(error);\n}\n\n// Retry helper is intentionally NOT re-exported here.\n// Import from the dedicated subpath to keep awaitly/result minimal:\n// import { tryAsyncRetry } from 'awaitly/result/retry';\n","/**\n * awaitly/result retry support\n *\n * Retry async operations with configurable backoff without the full workflow engine.\n */\n\nimport type { AsyncResult } from \"./index\";\nimport { ok, err } from \"./index\";\n\n/** Configuration for retry behavior */\nexport type RetryConfig<E = unknown> = {\n /** Number of retry attempts (not including the initial attempt) */\n times: number;\n /** Base delay between retries in milliseconds */\n delayMs: number;\n /** Backoff strategy */\n backoff?: \"constant\" | \"linear\" | \"exponential\";\n /** Predicate to determine if an error should trigger a retry. Defaults to always retry. */\n shouldRetry?: (error: E) => boolean;\n};\n\n/**\n * Wraps an async function that might throw into an AsyncResult, with retry support.\n *\n * @remarks When to use: Wrap async work with retry logic for transient failures without needing the full workflow engine.\n *\n * @example\n * ```typescript\n * const result = await tryAsyncRetry(\n * () => fetch('/api/data').then(r => r.json()),\n * { retry: { times: 3, delayMs: 100, backoff: 'exponential' } }\n * );\n * ```\n */\nexport function tryAsyncRetry<T>(\n fn: () => Promise<T>,\n config: { retry: RetryConfig<unknown> }\n): AsyncResult<T, unknown>;\nexport function tryAsyncRetry<T, E>(\n fn: () => Promise<T>,\n onError: (cause: unknown) => E,\n config: { retry: RetryConfig<E> }\n): AsyncResult<T, E>;\nexport async function tryAsyncRetry<T, E>(\n fn: () => Promise<T>,\n onErrorOrConfig: ((cause: unknown) => E) | { retry: RetryConfig<unknown> },\n maybeConfig?: { retry: RetryConfig<E> }\n): AsyncResult<T, E | unknown> {\n const onError = typeof onErrorOrConfig === \"function\" ? onErrorOrConfig : undefined;\n const config = typeof onErrorOrConfig === \"function\" ? maybeConfig! : onErrorOrConfig;\n const retry = config.retry;\n\n const getDelay = (attempt: number): number => {\n switch (retry.backoff) {\n case \"linear\":\n return retry.delayMs * (attempt + 1);\n case \"exponential\":\n return retry.delayMs * 2 ** attempt;\n case \"constant\":\n default:\n return retry.delayMs;\n }\n };\n\n const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\n\n const execute = async (): AsyncResult<T, E | unknown> => {\n try {\n return ok(await fn());\n } catch (cause) {\n return onError ? err(onError(cause), { cause }) : err(cause);\n }\n };\n\n let result = await execute();\n const shouldRetryFn = retry.shouldRetry ?? (() => true);\n\n for (let attempt = 0; attempt < retry.times; attempt++) {\n if (result.ok) break;\n if (!shouldRetryFn(result.error as E)) break;\n await sleep(getDelay(attempt));\n result = await execute();\n }\n\n return result;\n}\n"],"mappings":"AA+GO,SAASA,EAAMC,EAAyB,CAC7C,MAAO,CAAE,GAAI,GAAe,MAAOA,CAAkB,CACvD,CAOO,SAASC,EAAoBC,EAAUC,EAAoC,CAChF,IAAMC,EAAQD,GAAS,MACvB,MAAO,CAAE,GAAI,GAAgB,MAAAD,EAAO,GAAIE,IAAU,OAAY,CAAE,MAAAA,CAAM,EAAI,CAAC,CAAG,CAChF,CChFA,eAAsBC,EACpBC,EACAC,EACAC,EAC6B,CAC7B,IAAMC,EAAU,OAAOF,GAAoB,WAAaA,EAAkB,OAEpEG,GADS,OAAOH,GAAoB,WAAaC,EAAeD,GACjD,MAEfI,EAAYC,GAA4B,CAC5C,OAAQF,EAAM,QAAS,CACrB,IAAK,SACH,OAAOA,EAAM,SAAWE,EAAU,GACpC,IAAK,cACH,OAAOF,EAAM,QAAU,GAAKE,EAE9B,QACE,OAAOF,EAAM,OACjB,CACF,EAEMG,EAASC,GAAe,IAAI,QAAeC,GAAY,WAAWA,EAASD,CAAE,CAAC,EAE9EE,EAAU,SAAyC,CACvD,GAAI,CACF,OAAOC,EAAG,MAAMX,EAAG,CAAC,CACtB,OAASY,EAAO,CACd,OAAOT,EAAUU,EAAIV,EAAQS,CAAK,EAAG,CAAE,MAAAA,CAAM,CAAC,EAAIC,EAAID,CAAK,CAC7D,CACF,EAEIE,EAAS,MAAMJ,EAAQ,EACrBK,EAAgBX,EAAM,cAAgB,IAAM,IAElD,QAASE,EAAU,EAAGA,EAAUF,EAAM,OAChC,EAAAU,EAAO,IACP,CAACC,EAAcD,EAAO,KAAU,GAFOR,IAG3C,MAAMC,EAAMF,EAASC,CAAO,CAAC,EAC7BQ,EAAS,MAAMJ,EAAQ,EAGzB,OAAOI,CACT","names":["ok","value","err","error","options","cause","tryAsyncRetry","fn","onErrorOrConfig","maybeConfig","onError","retry","getDelay","attempt","sleep","ms","resolve","execute","ok","cause","err","result","shouldRetryFn"]}
|
package/dist/result.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var g=Object.defineProperty;var F=Object.getOwnPropertyDescriptor;var j=Object.getOwnPropertyNames;var b=Object.prototype.hasOwnProperty;var B=(e,r)=>{for(var n in r)g(e,n,{get:r[n],enumerable:!0})},S=(e,r,n,t)=>{if(r&&typeof r=="object"||typeof r=="function")for(let o of j(r))!b.call(e,o)&&o!==n&&g(e,o,{get:()=>r[o],enumerable:!(t=F(r,o))||t.enumerable});return e};var H=e=>S(g({},"__esModule",{value:!0}),e);var Ke={};B(Ke,{AWAITLY_CANCELLED:()=>V,AWAITLY_TIMEOUT:()=>M,AWAITLY_UNEXPECTED:()=>$,DESERIALIZATION_ERROR:()=>R,PROMISE_REJECTED:()=>l,UnexpectedError:()=>d,UnwrapError:()=>x,all:()=>ge,allAsync:()=>we,allSettled:()=>ye,allSettledAsync:()=>Ce,andThen:()=>ae,any:()=>Pe,anyAsync:()=>Ae,bimap:()=>Te,deserialize:()=>he,err:()=>s,flatten:()=>Oe,from:()=>ee,fromNullable:()=>te,fromPromise:()=>re,hydrate:()=>Re,isErr:()=>D,isOk:()=>z,isPromiseRejectedError:()=>J,isSerializedResult:()=>xe,isUnexpectedError:()=>w,map:()=>oe,mapError:()=>se,mapErrorTry:()=>le,mapTry:()=>ce,match:()=>ue,matchError:()=>Y,matchErrorPartial:()=>_e,ok:()=>a,orElse:()=>de,orElseAsync:()=>ke,partition:()=>me,recover:()=>pe,recoverAsync:()=>fe,runOrNull:()=>X,runOrThrow:()=>Z,runOrThrowAsync:()=>q,runOrUndefined:()=>Q,serialize:()=>Ue,tags:()=>L,tap:()=>ie,tapError:()=>Ee,tryAsync:()=>ne,unwrap:()=>y,unwrapOr:()=>W,unwrapOrElse:()=>G,zip:()=>I,zipAsync:()=>ve});module.exports=H(Ke);var T=class extends Error{_tag};function E(e,r){return class extends T{_tag=e;constructor(n,t){let o=r?.message?r.message(n??{}):e;if(super(o),this.name=e,Object.setPrototypeOf(this,new.target.prototype),n&&typeof n=="object"){let{_tag:u,name:i,message:k,stack:p,...c}=n,f=Object.prototype.hasOwnProperty.call(c,"cause"),N=f?c.cause:void 0;f&&delete c.cause;let C=t?.cause!==void 0;if(f&&C)throw new TypeError("TaggedError: cannot provide 'cause' in props when also setting ErrorOptions.cause");Object.assign(this,c),f&&(this.cause=N),C&&(this.cause=t?.cause)}else t?.cause!==void 0&&(this.cause=t.cause)}}}Object.defineProperty(E,Symbol.hasInstance,{value:e=>e instanceof T});(o=>{function e(u){return u instanceof Error}o.isError=e;function r(u){return u instanceof T}o.isTaggedError=r;function n(u,i){let k=u._tag,p=i[k];return p(u)}o.match=n;function t(u,i,k){let p=u._tag,c=i[p];return c?c(u):k(u)}o.matchPartial=t})(E||={});var m=class extends E("TimeoutError",{message:r=>r.operation?`TimeoutError: ${r.operation} timed out after ${r.ms}ms`:`TimeoutError: Operation timed out after ${r.ms}ms`}){},P=class extends E("RetryExhaustedError",{message:r=>r.operation?`RetryExhaustedError: ${r.operation} failed after ${r.attempts} attempts`:`RetryExhaustedError: Operation failed after ${r.attempts} attempts`}){},A=class extends E("RateLimitError",{message:r=>r.limiterName?`RateLimitError: Rate limit exceeded for ${r.limiterName}${r.retryAfterMs?`, retry after ${r.retryAfterMs}ms`:""}`:`RateLimitError: Rate limit exceeded${r.retryAfterMs?`, retry after ${r.retryAfterMs}ms`:""}`}){},v=class extends E("CircuitBreakerOpenError",{message:r=>`CircuitBreakerOpenError: Circuit ${r.circuitName} is ${r.state??"OPEN"}${r.retryAfterMs?`, retry after ${Math.ceil(r.retryAfterMs/1e3)}s`:""}`}){},O=class extends E("ValidationError",{message:r=>`ValidationError: Invalid ${r.field} - ${r.reason}`}){},h=class extends E("NotFoundError",{message:r=>r.id?`NotFoundError: ${r.resource} with id ${r.id} not found`:`NotFoundError: ${r.resource} not found`}){},U=class extends E("UnauthorizedError",{message:r=>r.reason?`UnauthorizedError: ${r.reason}`:r.action&&r.resource?`UnauthorizedError: Not authorized to ${r.action} ${r.resource}`:"UnauthorizedError: Access denied"}){},_=class extends E("NetworkError",{message:r=>r.url?`NetworkError: ${r.reason} (${r.url})`:`NetworkError: ${r.reason}`}){},K=class extends E("CompensationError",{message:r=>`CompensationError: Failed to compensate step ${r.step}`}){},d=class extends E("UnexpectedError",{message:r=>`UnexpectedError: ${r.cause instanceof Error?r.cause.message:String(r.cause??"unknown")}`}){};var l="PROMISE_REJECTED",$="AWAITLY_UNEXPECTED",V="AWAITLY_CANCELLED",M="AWAITLY_TIMEOUT",L=(...e)=>e;function a(e){return{ok:!0,value:e}}function s(e,r){let n=r?.cause;return{ok:!1,error:e,...n!==void 0?{cause:n}:{}}}var z=e=>e.ok,D=e=>!e.ok,w=e=>e instanceof d||typeof e=="object"&&e!==null&&"_tag"in e&&e._tag==="UnexpectedError",J=e=>typeof e=="object"&&e!==null&&"type"in e&&e.type===l;function Y(e,r){return w(e)?r.UnexpectedError(e):r[e](e)}var x=class extends Error{error;cause;constructor(r){let n=typeof r.error=="string"?r.error:JSON.stringify(r.error);super(`Attempted to unwrap an Err: ${n}`),this.name="UnwrapError",this.error=r.error,this.cause=r.cause}},y=e=>{if(e.ok)return e.value;throw new x(e)},W=(e,r)=>e.ok?e.value:r,G=(e,r)=>e.ok?e.value:r(e.error,e.cause),Z=e=>y(e),q=e=>Promise.resolve(e).then(y),X=e=>e.ok?e.value:null,Q=e=>e.ok?e.value:void 0;function ee(e,r){try{return a(e())}catch(n){return r?s(r(n),{cause:n}):s(n)}}async function re(e,r){try{return a(await e)}catch(n){return r?s(r(n),{cause:n}):s(n)}}async function ne(e,r){try{return a(await e())}catch(n){return r?s(r(n),{cause:n}):s(n)}}function te(e,r){return e!=null?a(e):s(r())}function oe(e,r){return e.ok?a(r(e.value)):e}function se(e,r){return e.ok?e:s(r(e.error,e.cause),{cause:e.cause})}function ue(e,r){return e.ok?r.ok(e.value):r.err(e.error,e.cause)}function ae(e,r){return e.ok?r(e.value):e}function ie(e,r){return e.ok&&r(e.value),e}function Ee(e,r){return e.ok||r(e.error,e.cause),e}function ce(e,r,n){if(!e.ok)return e;try{return a(r(e.value))}catch(t){return s(n(t),{cause:t})}}function le(e,r,n){if(e.ok)return e;try{return s(r(e.error),{cause:e.cause})}catch(t){return s(n(t),{cause:t})}}function Te(e,r,n){return e.ok?a(r(e.value)):s(n(e.error,e.cause),{cause:e.cause})}function de(e,r){return e.ok?e:r(e.error,e.cause)}async function ke(e,r){return e.ok?e:r(e.error,e.cause)}function pe(e,r){return e.ok?a(e.value):a(r(e.error,e.cause))}async function fe(e,r){let n=await e;return n.ok?a(n.value):a(await r(n.error,n.cause))}function Re(e){if(typeof e!="object"||e===null||!("ok"in e))return null;let r=e;return r.ok===!0&&"value"in r?a(r.value):r.ok===!1&&"error"in r?s(r.error,{cause:r.cause}):null}function xe(e){if(typeof e!="object"||e===null||!("ok"in e))return!1;let r=e;return r.ok===!0&&"value"in r||r.ok===!1&&"error"in r}function ge(e){let r=[];for(let n of e){if(!n.ok)return n;r.push(n.value)}return a(r)}async function we(e){let r=[];for(let n of e)try{let t=await n;if(!t.ok)return t;r.push(t.value)}catch(t){return s({type:l,cause:t},{cause:{type:"PROMISE_REJECTION",reason:t}})}return a(r)}function ye(e){let r=[],n=[];for(let t of e)t.ok?r.push(t.value):n.push({error:t.error,cause:t.cause});return n.length>0?s(n):a(r)}async function Ce(e){let r=await Promise.all(e.map(o=>Promise.resolve(o).then(u=>({status:"result",result:u})).catch(u=>({status:"rejected",error:{type:l,cause:u},cause:{type:"PROMISE_REJECTION",reason:u}})))),n=[],t=[];for(let o of r)o.status==="rejected"?t.push({error:o.error,cause:o.cause}):o.result.ok?n.push(o.result.value):t.push({error:o.result.error,cause:o.result.cause});return t.length>0?s(t):a(n)}function me(e){let r=[],n=[];for(let t of e)t.ok?r.push(t.value):n.push(t.error);return{values:r,errors:n}}function Pe(e){if(e.length===0)return s({type:"EMPTY_INPUT",message:"any() requires at least one Result"});let r;for(let n of e){if(n.ok)return n;r||(r=n)}return r}async function Ae(e){return e.length===0?s({type:"EMPTY_INPUT",message:"anyAsync() requires at least one Result"}):new Promise(r=>{let n=!1,t=e.length,o=null;for(let u of e)Promise.resolve(u).catch(i=>s({type:l,cause:i},{cause:{type:"PROMISE_REJECTION",reason:i}})).then(i=>{if(!n){if(i.ok){n=!0,r(i);return}o||(o=i),t--,t===0&&r(o)}})})}function I(e,r){return e.ok?r.ok?a([e.value,r.value]):r:e}async function ve(e,r){let n=u=>Promise.resolve(u).catch(i=>s({type:l,cause:i},{cause:{type:"PROMISE_REJECTION",reason:i}})),[t,o]=await Promise.all([n(e),n(r)]);return I(t,o)}function Oe(e){return e.ok?e.value:e}var R="DESERIALIZATION_ERROR";function he(e){if(typeof e!="object"||e===null)return s({type:R,value:e});if(!("ok"in e))return s({type:R,value:e});let r=e;return r.ok===!0&&"value"in r?a(r.value):r.ok===!1&&"error"in r?s(r.error,{cause:r.cause}):s({type:R,value:e})}function Ue(e){return e.ok?{ok:!0,value:e.value}:{ok:!1,error:e.error}}function _e(e,r,n){if(w(e)){let o=r.UnexpectedError;return o?o(e):n(e)}let t=r[e];return t?t(e):n(e)}0&&(module.exports={AWAITLY_CANCELLED,AWAITLY_TIMEOUT,AWAITLY_UNEXPECTED,DESERIALIZATION_ERROR,PROMISE_REJECTED,UnexpectedError,UnwrapError,all,allAsync,allSettled,allSettledAsync,andThen,any,anyAsync,bimap,deserialize,err,flatten,from,fromNullable,fromPromise,hydrate,isErr,isOk,isPromiseRejectedError,isSerializedResult,isUnexpectedError,map,mapError,mapErrorTry,mapTry,match,matchError,matchErrorPartial,ok,orElse,orElseAsync,partition,recover,recoverAsync,runOrNull,runOrThrow,runOrThrowAsync,runOrUndefined,serialize,tags,tap,tapError,tryAsync,unwrap,unwrapOr,unwrapOrElse,zip,zipAsync});
|
|
2
2
|
//# sourceMappingURL=result.cjs.map
|