@onrails/result 0.1.4 → 0.3.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.
Files changed (72) hide show
  1. package/DESIGN.md +11 -19
  2. package/README.md +14 -62
  3. package/RECIPES.md +12 -25
  4. package/dist/async-BR3AX0ck.d.cts +326 -0
  5. package/dist/async-SZ7BEl0g.d.ts +326 -0
  6. package/dist/compat/neverthrow.cjs +266 -94
  7. package/dist/compat/neverthrow.cjs.map +1 -1
  8. package/dist/compat/neverthrow.d.cts +4 -4
  9. package/dist/compat/neverthrow.d.ts +4 -4
  10. package/dist/compat/neverthrow.js +263 -91
  11. package/dist/compat/neverthrow.js.map +1 -1
  12. package/dist/extra.cjs +2 -25
  13. package/dist/extra.cjs.map +1 -1
  14. package/dist/extra.d.cts +7 -19
  15. package/dist/extra.d.ts +7 -19
  16. package/dist/extra.js +3 -25
  17. package/dist/extra.js.map +1 -1
  18. package/dist/fluent.cjs +55 -29
  19. package/dist/fluent.cjs.map +1 -1
  20. package/dist/fluent.d.cts +38 -11
  21. package/dist/fluent.d.ts +38 -11
  22. package/dist/fluent.js +55 -29
  23. package/dist/fluent.js.map +1 -1
  24. package/dist/index.cjs +290 -131
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +294 -69
  27. package/dist/index.d.ts +294 -69
  28. package/dist/index.js +287 -127
  29. package/dist/index.js.map +1 -1
  30. package/dist/pipe.cjs.map +1 -1
  31. package/dist/pipe.d.cts +7 -5
  32. package/dist/pipe.d.ts +7 -5
  33. package/dist/pipe.js.map +1 -1
  34. package/dist/railway.cjs +293 -188
  35. package/dist/railway.cjs.map +1 -1
  36. package/dist/railway.d.cts +26 -180
  37. package/dist/railway.d.ts +26 -180
  38. package/dist/railway.js +294 -178
  39. package/dist/railway.js.map +1 -1
  40. package/dist/try-gen.cjs +1 -1
  41. package/dist/try-gen.cjs.map +1 -1
  42. package/dist/try-gen.d.cts +29 -3
  43. package/dist/try-gen.d.ts +29 -3
  44. package/dist/try-gen.js +1 -1
  45. package/dist/try-gen.js.map +1 -1
  46. package/dist/types-BQ9vv0nD.d.cts +55 -0
  47. package/dist/types-BQ9vv0nD.d.ts +55 -0
  48. package/package.json +5 -18
  49. package/skills/railway-do-notation/SKILL.md +397 -0
  50. package/skills/result-composition/SKILL.md +323 -0
  51. package/dist/$.cjs +0 -40
  52. package/dist/$.cjs.map +0 -1
  53. package/dist/$.js +0 -36
  54. package/dist/$.js.map +0 -1
  55. package/dist/_.d.cts +0 -2
  56. package/dist/_.d.ts +0 -2
  57. package/dist/async-CH8Cs8KE.d.cts +0 -161
  58. package/dist/async-DKISqIBc.d.ts +0 -161
  59. package/dist/interop.cjs +0 -253
  60. package/dist/interop.cjs.map +0 -1
  61. package/dist/interop.d.cts +0 -35
  62. package/dist/interop.d.ts +0 -35
  63. package/dist/interop.js +0 -249
  64. package/dist/interop.js.map +0 -1
  65. package/dist/types-C2Dp1d5J.d.cts +0 -21
  66. package/dist/types-C2Dp1d5J.d.ts +0 -21
  67. package/dist/validation.cjs +0 -53
  68. package/dist/validation.cjs.map +0 -1
  69. package/dist/validation.d.cts +0 -72
  70. package/dist/validation.d.ts +0 -72
  71. package/dist/validation.js +0 -48
  72. package/dist/validation.js.map +0 -1
package/dist/extra.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/result.ts","../src/extra.ts"],"names":[],"mappings":";AAaO,IAAM,EAAA,GAAK,CAAe,KAAA,MAA4B;AAAA,EAC3D,IAAA,EAAM,IAAA;AAAA,EACN;AACF,CAAA,CAAA;AAWO,IAAM,GAAA,GAAM,CAAyB,KAAA,MAA4B;AAAA,EACtE,IAAA,EAAM,KAAA;AAAA,EACN;AACF,CAAA,CAAA;AAYO,IAAM,IAAA,GAAO,CAAO,MAAA,KAA6C,MAAA,CAAO,IAAA,KAAS,IAAA;AAYjF,IAAM,KAAA,GAAQ,CAAO,MAAA,KAA8C,MAAA,CAAO,IAAA,KAAS,KAAA;AA2UnF,IAAM,OAAA,GAAU,CAAO,OAAA,KAAqD;AACjF,EAAA,MAAM,SAAc,EAAC;AACrB,EAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,IAAA,IAAI,MAAM,MAAM,CAAA,EAAG,OAAO,GAAA,CAAI,OAAO,KAAK,CAAA;AAC1C,IAAA,MAAA,CAAO,IAAA,CAAK,OAAO,KAAK,CAAA;AAAA,EAC1B;AACA,EAAA,OAAO,GAAG,MAAM,CAAA;AAClB,CAAA;;;ACzWO,IAAM,gBAAgB,OAAU;AAAA,EACrC,QAAA,EAAU,CAAI,MAAA,KAA6C,MAAA;AAAA,EAC3D,aAAA,EAAe,CAAI,MAAA,KACjB;AACJ,CAAA;AAGO,IAAM,OAAA,GAAU,CACrB,KAAA,EACA,IAAA,KACqC,MAAM,IAAA,KAAS;AAG/C,IAAM,UAAA,GACX,CACE,IAAA,EACA,EAAA,KAEF,CAAI,MAAA,KAA2C;AAC7C,EAAA,IAAI,MAAM,MAAM,CAAA,IAAK,QAAQ,MAAA,CAAO,KAAA,EAAO,IAAI,CAAA,EAAG;AAChD,IAAA,OAAO,EAAE,IAAA,EAAM,KAAA,EAAO,OAAO,EAAA,CAAG,MAAA,CAAO,KAAK,CAAA,EAAE;AAAA,EAChD;AACA,EAAA,OAAO,MAAA;AACT;AAOK,IAAM,OAAA,GAAU;AAGhB,IAAM,KAAA,GAAQ,CAAO,OAAA,KAA8C,OAAA,CAAQ,MAAM,IAAI","file":"extra.js","sourcesContent":["import type { Err, Ok, Result } from \"./types.js\";\n\nexport type { Err, Ok, Result } from \"./types.js\";\n\n/**\n * Lifts a value into the success track.\n *\n * @example\n * ```ts\n * const r = ok(42); // Result<number, never>\n * const typed: Result<number, \"parse\"> = ok(1);\n * ```\n */\nexport const ok = <T, E = never>(value: T): Result<T, E> => ({\n _tag: \"Ok\",\n value,\n});\n\n/**\n * Lifts a value into the error track.\n *\n * @example\n * ```ts\n * const r = err({ kind: \"parse\", message: \"bad json\" });\n * // Result<never, { kind: \"parse\"; message: string }>\n * ```\n */\nexport const err = <T = never, E = unknown>(error: E): Result<T, E> => ({\n _tag: \"Err\",\n error,\n});\n\n/**\n * Type-narrowing predicate: returns `true` when the result is `Ok`.\n *\n * @example\n * ```ts\n * if (isOk(r)) {\n * console.log(r.value); // narrowed to Ok branch\n * }\n * ```\n */\nexport const isOk = <T, E>(result: Result<T, E>): result is Ok<T, E> => result._tag === \"Ok\";\n\n/**\n * Type-narrowing predicate: returns `true` when the result is `Err`.\n *\n * @example\n * ```ts\n * if (isErr(r)) {\n * metrics.inc(\"error\", { kind: r.error.kind });\n * }\n * ```\n */\nexport const isErr = <T, E>(result: Result<T, E>): result is Err<T, E> => result._tag === \"Err\";\n\n/**\n * Fantasy Land `of` — alias of {@link ok}.\n * @deprecated Use {@link ok} instead.\n */\nexport const of = ok;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Dual-form helpers — each export accepts either shape:\n// data-first: `map(result, fn)`\n// curried: `map(fn)(result)`\n// Arity at the call site selects the overload.\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst mapImpl = <T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E> =>\n isOk(result) ? ok(fn(result.value)) : err(result.error);\n\n/**\n * Transform the `Ok` value, passing `Err` through unchanged. Dual-form:\n * call data-first or curried (for use with {@link pipe}).\n *\n * @example\n * ```ts\n * map(ok(2), (n) => n * 3); // Ok 6 — data-first\n * pipe(ok(\"x\"), map((s) => s.length));// Ok 1 — curried\n * ```\n */\nexport function map<T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E>;\nexport function map<T, U>(fn: (value: T) => U): <E>(result: Result<T, E>) => Result<U, E>;\nexport function map(\n ...args: [Result<unknown, unknown>, (value: unknown) => unknown] | [(value: unknown) => unknown]\n): unknown {\n if (args.length === 2) return mapImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => mapImpl(result, fn);\n}\n\nconst mapErrImpl = <T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F> =>\n isErr(result) ? err(fn(result.error)) : ok(result.value);\n\n/**\n * Transform the `Err` value, passing `Ok` through unchanged. Useful for\n * unifying heterogeneous failure types into one app-level union.\n *\n * @example\n * ```ts\n * type AppError = { kind: \"http\"; status: number } | { kind: \"parse\" };\n * pipe(\n * fetchSync(url), // Result<Body, { status: number }>\n * mapErr((e): AppError => ({ kind: \"http\", status: e.status })),\n * );\n * ```\n */\nexport function mapErr<T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F>;\nexport function mapErr<E, F>(fn: (error: E) => F): <T>(result: Result<T, E>) => Result<T, F>;\nexport function mapErr(\n ...args: [Result<unknown, unknown>, (error: unknown) => unknown] | [(error: unknown) => unknown]\n): unknown {\n if (args.length === 2) return mapErrImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => mapErrImpl(result, fn);\n}\n\nconst bimapImpl = <T, U, E, F>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n): Result<U, F> => (isOk(result) ? ok(onOk(result.value)) : err(onErr(result.error)));\n\n/**\n * Transform both tracks at once — `Ok` via `onOk`, `Err` via `onErr`.\n * Equivalent to `mapErr(onErr)(map(onOk)(result))` but in one pass.\n *\n * @example\n * ```ts\n * bimap(parsed, (cfg) => cfg.name, (e) => ({ kind: \"input\", cause: e }));\n * ```\n */\nexport function bimap<T, U, E, F>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n): Result<U, F>;\nexport function bimap<T, U, E, F>(\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n): (result: Result<T, E>) => Result<U, F>;\nexport function bimap(\n ...args:\n | [Result<unknown, unknown>, (value: unknown) => unknown, (error: unknown) => unknown]\n | [(value: unknown) => unknown, (error: unknown) => unknown]\n): unknown {\n if (args.length === 3) return bimapImpl(args[0], args[1], args[2]);\n const [onOk, onErr] = args;\n return (result: Result<unknown, unknown>) => bimapImpl(result, onOk, onErr);\n}\n\nconst flatMapImpl = <T, U, E, F>(\n result: Result<T, E>,\n fn: (value: T) => Result<U, F>,\n): Result<U, E | F> => (isOk(result) ? fn(result.value) : err(result.error));\n\n/**\n * Canonical bind (Fantasy Land `chain`). Chains a Result-returning step,\n * widening the error union to `E | F`. Short-circuits on `Err`.\n *\n * @example\n * ```ts\n * flatMap(parseInput(raw), (data) =>\n * data.id != null ? ok(data) : err({ kind: \"missing_id\" as const }),\n * );\n * // Result<Data, ParseError | { kind: \"missing_id\" }>\n * ```\n */\nexport function flatMap<T, U, E, F>(\n result: Result<T, E>,\n fn: (value: T) => Result<U, F>,\n): Result<U, E | F>;\nexport function flatMap<T, U, F>(\n fn: (value: T) => Result<U, F>,\n): <E>(result: Result<T, E>) => Result<U, E | F>;\nexport function flatMap(\n ...args:\n | [Result<unknown, unknown>, (value: unknown) => Result<unknown, unknown>]\n | [(value: unknown) => Result<unknown, unknown>]\n): unknown {\n if (args.length === 2) return flatMapImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => flatMapImpl(result, fn);\n}\n\nconst recoverImpl = <T, E, F>(\n result: Result<T, E>,\n fn: (error: E) => Result<T, F>,\n): Result<T, F> => (isErr(result) ? fn(result.error) : ok(result.value));\n\n/**\n * Error-track bind — runs `fn` only when the result is `Err`, allowing\n * a failed workflow to recover to `Ok` or remap the failure. Mirror of\n * {@link flatMap} on the error channel.\n *\n * @example\n * ```ts\n * recover(networkResult, (e) =>\n * e.kind === \"rate_limit\" ? ok(cachedBody) : err(e),\n * );\n * ```\n */\nexport function recover<T, E, F>(\n result: Result<T, E>,\n fn: (error: E) => Result<T, F>,\n): Result<T, F>;\nexport function recover<T, E, F>(\n fn: (error: E) => Result<T, F>,\n): (result: Result<T, E>) => Result<T, F>;\nexport function recover(\n ...args:\n | [Result<unknown, unknown>, (error: unknown) => Result<unknown, unknown>]\n | [(error: unknown) => Result<unknown, unknown>]\n): unknown {\n if (args.length === 2) return recoverImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => recoverImpl(result, fn);\n}\n\nconst tapImpl = <T, E>(result: Result<T, E>, fn: (value: T) => void): Result<T, E> => {\n if (isOk(result)) fn(result.value);\n return result;\n};\n\n/**\n * Observe the `Ok` value for side effects (logging, metrics) without\n * changing the carried value. Passes `Err` through untouched.\n *\n * @example\n * ```ts\n * pipe(\n * parseConfig(raw),\n * tap((cfg) => log.info({ msg: \"parsed\", name: cfg.name })),\n * flatMap(validate),\n * );\n * ```\n */\nexport function tap<T, E>(result: Result<T, E>, fn: (value: T) => void): Result<T, E>;\nexport function tap<T>(fn: (value: T) => void): <E>(result: Result<T, E>) => Result<T, E>;\nexport function tap(\n ...args: [Result<unknown, unknown>, (value: unknown) => void] | [(value: unknown) => void]\n): unknown {\n if (args.length === 2) return tapImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => tapImpl(result, fn);\n}\n\nconst tapErrImpl = <T, E>(result: Result<T, E>, fn: (error: E) => void): Result<T, E> => {\n if (isErr(result)) fn(result.error);\n return result;\n};\n\n/**\n * Observe the `Err` value for side effects (logging, metrics) without\n * changing the carried error. Passes `Ok` through untouched.\n *\n * @example\n * ```ts\n * pipe(\n * loadUser(id),\n * tapErr((e) => metrics.inc(\"user.load.fail\", { kind: e.kind })),\n * );\n * ```\n */\nexport function tapErr<T, E>(result: Result<T, E>, fn: (error: E) => void): Result<T, E>;\nexport function tapErr<E>(fn: (error: E) => void): <T>(result: Result<T, E>) => Result<T, E>;\nexport function tapErr(\n ...args: [Result<unknown, unknown>, (error: unknown) => void] | [(error: unknown) => void]\n): unknown {\n if (args.length === 2) return tapErrImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => tapErrImpl(result, fn);\n}\n\nconst matchImpl = <T, E, U>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => U,\n): U => (isOk(result) ? onOk(result.value) : onErr(result.error));\n\n/**\n * Terminal collapse — fold both tracks into a single value. Dual-form:\n * 3-args data-first, 2-args curried for {@link pipe}. Returns whatever\n * the handlers return.\n *\n * For files that also import `match` from `ts-pattern`, the\n * collision-free alias {@link matchResult} is identical.\n *\n * @example\n * ```ts\n * const html = match(parsed, (cfg) => render(cfg), (e) => renderError(e));\n * ```\n */\nexport function match<T, E, U>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => U,\n): U;\nexport function match<T, E, U>(\n onOk: (value: T) => U,\n onErr: (error: E) => U,\n): (result: Result<T, E>) => U;\nexport function match(\n ...args:\n | [Result<unknown, unknown>, (value: unknown) => unknown, (error: unknown) => unknown]\n | [(value: unknown) => unknown, (error: unknown) => unknown]\n): unknown {\n if (args.length === 3) return matchImpl(args[0], args[1], args[2]);\n const [onOk, onErr] = args;\n return (result: Result<unknown, unknown>) => matchImpl(result, onOk, onErr);\n}\n\n/**\n * Collision-free alias for files that also import `match` from ts-pattern.\n * @deprecated Namespace import carriers (e.g., `import * as R from \"@onrails/result\"`) and use canonical {@link match} instead.\n */\nexport const matchResult = match;\n\n/**\n * Curried collapse with named slots — escape valve when positional `match`\n * order is unclear at the call site (e.g. when both handlers return the same\n * type and a transposed `match(r, onErr, onOk)` would silently compile).\n * @deprecated Use curried {@link match}(onOk, onErr)(result) instead.\n */\nexport const fold =\n <T, E, U>(handlers: { readonly ok: (value: T) => U; readonly err: (error: E) => U }) =>\n (result: Result<T, E>): U =>\n matchImpl(result, handlers.ok, handlers.err);\n\n/**\n * Returns the `Ok` value, or `defaultValue` when the result is `Err`.\n *\n * @example\n * ```ts\n * unwrapOr(parsedSetting, \"default-value\");\n * ```\n */\nexport const unwrapOr = <T, E>(result: Result<T, E>, defaultValue: T): T =>\n isOk(result) ? result.value : defaultValue;\n\n/**\n * Test/assert helper — throws the original Err value when called on Err.\n *\n * @throws when the result is Err — assertion-tier; use {@link match} / {@link unwrapOr} in business logic.\n * Allowed in `*.spec.ts` / `*.test.ts`; flagged elsewhere by the plugins.\n */\nexport function unwrapOk<T, E>(result: Result<T, E>): T {\n if (isErr(result)) throw result.error;\n return result.value;\n}\n\n/**\n * Test/assert helper — throws TypeError when called on Ok.\n *\n * @throws when the result is Ok — assertion-tier; allowed in `*.spec.ts` / `*.test.ts`; flagged elsewhere by the plugins.\n */\nexport function unwrapErr<T, E>(result: Result<T, E>): E {\n if (isOk(result)) throw new TypeError(\"unwrapErr called on Ok\");\n return result.error;\n}\n\n/** Wrap a throwing sync function — neverthrow `Result.fromThrowable` */\nexport function trySync<A extends readonly unknown[], T, E>(\n fn: (...args: A) => T,\n onThrow: (error: unknown) => E,\n): (...args: A) => Result<T, E>;\nexport function trySync<F extends (...args: never) => unknown, E>(\n fn: F,\n onThrow: (error: unknown) => E,\n): (...args: Parameters<F>) => Result<ReturnType<F>, E>;\nexport function trySync(\n fn: (...args: never) => unknown,\n onThrow: (error: unknown) => unknown,\n): (...args: never) => Result<unknown, unknown> {\n return (...args: never) => {\n try {\n return ok(fn(...args));\n } catch (error) {\n return err(onThrow(error));\n }\n };\n}\n\n/** First failure wins; otherwise collects values in order */\nexport const combine = <T, E>(results: readonly Result<T, E>[]): Result<T[], E> => {\n const values: T[] = [];\n for (const result of results) {\n if (isErr(result)) return err(result.error);\n values.push(result.value);\n }\n return ok(values);\n};\n\n/** Tuple-preserving combine (neverthrow-style) */\nexport const combineTuple = <const R extends readonly Result<unknown, unknown>[]>(\n results: R,\n): CombineTuple<R> =>\n // Runtime identical to combine; the cast restores per-index tuple types.\n combine(results as readonly Result<unknown, unknown>[]) as CombineTuple<R>;\n\ntype _OkValue<R> = R extends { _tag: \"Ok\"; readonly value: infer T } ? T : never;\ntype _ErrValue<R> = R extends { _tag: \"Err\"; readonly error: infer E } ? E : never;\n\ntype CombineTuple<R extends readonly Result<unknown, unknown>[]> = Result<\n { [K in keyof R]: _OkValue<R[K]> },\n { [K in keyof R]: _ErrValue<R[K]> }[number]\n>;\n\n/**\n * Variadic value-first pipe — threads `value` through up to nine unary fns.\n *\n * ```ts\n * pipe(\n * parseConfig(raw),\n * map((cfg) => cfg.name),\n * flatMap((name) => name ? ok(name) : err({ kind: \"empty\" })),\n * tap(log),\n * );\n * ```\n */\nexport function pipe<A>(value: A): A;\nexport function pipe<A, B>(value: A, ab: (a: A) => B): B;\nexport function pipe<A, B, C>(value: A, ab: (a: A) => B, bc: (b: B) => C): C;\nexport function pipe<A, B, C, D>(value: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D): D;\nexport function pipe<A, B, C, D, E>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n): E;\nexport function pipe<A, B, C, D, E, F>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n): F;\nexport function pipe<A, B, C, D, E, F, G>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n): G;\nexport function pipe<A, B, C, D, E, F, G, H>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n): H;\nexport function pipe<A, B, C, D, E, F, G, H, I>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n hi: (h: H) => I,\n): I;\nexport function pipe(value: unknown, ...fns: ReadonlyArray<(x: unknown) => unknown>): unknown {\n let acc = value;\n for (const fn of fns) {\n acc = fn(acc);\n }\n return acc;\n}\n","/**\n * @onrails/result/extra — tagged-error helpers.\n */\n\nimport type { ResultAsync } from \"./async.js\";\nimport { combine, isErr, isOk } from \"./result.js\";\nimport type { Result } from \"./types.js\";\n\n/** Extract error type from a {@link Result} */\nexport type ErrOf<R> = R extends { _tag: \"Err\"; readonly error: infer E } ? E : never;\n\n/** Extract success type from a {@link Result} */\nexport type OkOf<R> = R extends { _tag: \"Ok\"; readonly value: infer T } ? T : never;\n\n/** Union of error types from a tuple/readonly array of results */\nexport type UnionErrors<R extends readonly Result<unknown, unknown>[]> = {\n [K in keyof R]: ErrOf<R[K]>;\n}[number];\n\n/** Manual union when TS fails to infer multi-step errors (neverthrow #603) */\nexport type AccumulateErrors<Errors extends readonly unknown[]> = Errors[number];\n\n/**\n * Declare the error union for a pipeline when inference only picks the first step.\n *\n * @example\n * ```ts\n * const errors = declareErrors<ParseError | NetworkError>();\n * const step = errors.annotate(parseThing());\n * ```\n */\nexport const declareErrors = <E>() => ({\n annotate: <T>(result: Result<T, unknown>): Result<T, E> => result as Result<T, E>,\n annotateAsync: <T>(result: ResultAsync<T, unknown>): ResultAsync<T, E> =>\n result as ResultAsync<T, E>,\n});\n\n/** Narrow an error by `kind` when using discriminated unions */\nexport const hasKind = <E extends { kind: string }, K extends E[\"kind\"]>(\n error: E,\n kind: K,\n): error is Extract<E, { kind: K }> => error.kind === kind;\n\n/** Map only errors matching `kind`, leave others unchanged */\nexport const mapErrKind =\n <E extends { kind: string }, K extends E[\"kind\"], F>(\n kind: K,\n fn: (error: Extract<E, { kind: K }>) => F,\n ) =>\n <T>(result: Result<T, E>): Result<T, E | F> => {\n if (isErr(result) && hasKind(result.error, kind)) {\n return { _tag: \"Err\", error: fn(result.error) };\n }\n return result;\n };\n\n/**\n * Collect values when all Ok; otherwise first Err encountered.\n *\n * @deprecated Identical to {@link combine}; removed in the next major.\n */\nexport const collect = combine;\n\n/** True when no result is Err */\nexport const allOk = <T, E>(results: readonly Result<T, E>[]): boolean => results.every(isOk);\n"]}
1
+ {"version":3,"sources":["../src/result.ts","../src/extra.ts"],"names":[],"mappings":";AA2CO,IAAM,IAAA,GAAO,CAAO,MAAA,KAA6C,MAAA,CAAO,IAAA,KAAS,IAAA;AAYjF,IAAM,KAAA,GAAQ,CAAO,MAAA,KAA8C,MAAA,CAAO,IAAA,KAAS,KAAA;;;ACvBnF,IAAM,gBAAgB,OAAU;AAAA,EACrC,QAAA,EAAU,CAAI,MAAA,KAA6C,MAAA;AAAA,EAC3D,aAAA,EAAe,CAAI,MAAA,KACjB;AACJ,CAAA;AAGO,IAAM,OAAA,GAAU,CACrB,KAAA,EACA,IAAA,KACqC,MAAM,IAAA,KAAS;AAG/C,IAAM,UAAA,GACX,CACE,IAAA,EACA,EAAA,KAEF,CAAI,MAAA,KACF,KAAA,CAAM,MAAM,CAAA,IAAK,OAAA,CAAQ,MAAA,CAAO,OAAO,IAAI,CAAA,GACvC,EAAE,IAAA,EAAM,KAAA,EAAO,OAAO,EAAA,CAAG,MAAA,CAAO,KAAK,CAAA,EAAE,GACvC;AAGD,IAAM,KAAA,GAAQ,CAAO,OAAA,KAA8C,OAAA,CAAQ,MAAM,IAAI","file":"extra.js","sourcesContent":["import { dual } from \"./internal/dual.js\";\nimport type { Err, Ok, Result } from \"./types.js\";\n\nexport type { Err, Ok, Result } from \"./types.js\";\n\n/**\n * Lifts a value into the success track.\n *\n * @example\n * ```ts\n * const r = ok(42); // Result<number, never>\n * const typed: Result<number, \"parse\"> = ok(1);\n * ```\n */\nexport const ok = <T, E = never>(value: T): Result<T, E> => ({\n _tag: \"Ok\",\n value,\n});\n\n/**\n * Lifts a value into the error track.\n *\n * @example\n * ```ts\n * const r = err({ kind: \"parse\", message: \"bad json\" });\n * // Result<never, { kind: \"parse\"; message: string }>\n * ```\n */\nexport const err = <T = never, E = unknown>(error: E): Result<T, E> => ({\n _tag: \"Err\",\n error,\n});\n\n/**\n * Type-narrowing predicate: returns `true` when the result is `Ok`.\n *\n * @example\n * ```ts\n * if (isOk(r)) {\n * console.log(r.value); // narrowed to Ok branch\n * }\n * ```\n */\nexport const isOk = <T, E>(result: Result<T, E>): result is Ok<T, E> => result._tag === \"Ok\";\n\n/**\n * Type-narrowing predicate: returns `true` when the result is `Err`.\n *\n * @example\n * ```ts\n * if (isErr(r)) {\n * metrics.inc(\"error\", { kind: r.error.kind });\n * }\n * ```\n */\nexport const isErr = <T, E>(result: Result<T, E>): result is Err<T, E> => result._tag === \"Err\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Dual-form helpers — each export accepts either shape:\n// data-first: `map(result, fn)`\n// curried: `map(fn)(result)`\n// Arity dispatch is derived from the internal `dual` combinator\n// (src/internal/dual.ts); the const annotation carries the public overloads.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Transform the `Ok` value, passing `Err` through unchanged. Dual-form:\n * call data-first or curried (for use with {@link pipe}).\n *\n * @example\n * ```ts\n * map(ok(2), (n) => n * 3); // Ok 6 — data-first\n * pipe(ok(\"x\"), map((s) => s.length));// Ok 1 — curried\n * ```\n */\nexport const map: {\n <T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E>;\n <T, U>(fn: (value: T) => U): <E>(result: Result<T, E>) => Result<U, E>;\n} = dual(\n 2,\n <T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E> =>\n isOk(result) ? ok(fn(result.value)) : err(result.error),\n);\n\n/**\n * Transform the `Err` value, passing `Ok` through unchanged. Useful for\n * unifying heterogeneous failure types into one app-level union.\n *\n * @example\n * ```ts\n * type AppError = { kind: \"http\"; status: number } | { kind: \"parse\" };\n * pipe(\n * fetchSync(url), // Result<Body, { status: number }>\n * mapErr((e): AppError => ({ kind: \"http\", status: e.status })),\n * );\n * ```\n */\nexport const mapErr: {\n <T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F>;\n <E, F>(fn: (error: E) => F): <T>(result: Result<T, E>) => Result<T, F>;\n} = dual(\n 2,\n <T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F> =>\n isErr(result) ? err(fn(result.error)) : ok(result.value),\n);\n\n/**\n * Transform both tracks at once — `Ok` via `onOk`, `Err` via `onErr`.\n * Equivalent to `mapErr(onErr)(map(onOk)(result))` but in one pass.\n *\n * @example\n * ```ts\n * bimap(parsed, (cfg) => cfg.name, (e) => ({ kind: \"input\", cause: e }));\n * ```\n */\nexport const bimap: {\n <T, U, E, F>(result: Result<T, E>, onOk: (value: T) => U, onErr: (error: E) => F): Result<U, F>;\n <T, U, E, F>(\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n ): (result: Result<T, E>) => Result<U, F>;\n} = dual(\n 3,\n <T, U, E, F>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n ): Result<U, F> => (isOk(result) ? ok(onOk(result.value)) : err(onErr(result.error))),\n);\n\n/**\n * Canonical bind (Fantasy Land `chain`). Chains a Result-returning step,\n * widening the error union to `E | F`. Short-circuits on `Err`.\n *\n * @example\n * ```ts\n * flatMap(parseInput(raw), (data) =>\n * data.id != null ? ok(data) : err({ kind: \"missing_id\" as const }),\n * );\n * // Result<Data, ParseError | { kind: \"missing_id\" }>\n * ```\n */\nexport const flatMap: {\n <T, U, E, F>(result: Result<T, E>, fn: (value: T) => Result<U, F>): Result<U, E | F>;\n <T, U, F>(fn: (value: T) => Result<U, F>): <E>(result: Result<T, E>) => Result<U, E | F>;\n} = dual(\n 2,\n <T, U, E, F>(result: Result<T, E>, fn: (value: T) => Result<U, F>): Result<U, E | F> =>\n isOk(result) ? fn(result.value) : err(result.error),\n);\n\n/**\n * Error-track bind — runs `fn` only when the result is `Err`, allowing\n * a failed workflow to recover to `Ok` or remap the failure. Mirror of\n * {@link flatMap} on the error channel.\n *\n * @example\n * ```ts\n * recover(networkResult, (e) =>\n * e.kind === \"rate_limit\" ? ok(cachedBody) : err(e),\n * );\n * ```\n */\nexport const recover: {\n <T, E, F>(result: Result<T, E>, fn: (error: E) => Result<T, F>): Result<T, F>;\n <T, E, F>(fn: (error: E) => Result<T, F>): (result: Result<T, E>) => Result<T, F>;\n} = dual(\n 2,\n <T, E, F>(result: Result<T, E>, fn: (error: E) => Result<T, F>): Result<T, F> =>\n isErr(result) ? fn(result.error) : ok(result.value),\n);\n\n/**\n * Observe the `Ok` value for side effects (logging, metrics) without\n * changing the carried value. Passes `Err` through untouched.\n *\n * @example\n * ```ts\n * pipe(\n * parseConfig(raw),\n * tap((cfg) => log.info({ msg: \"parsed\", name: cfg.name })),\n * flatMap(validate),\n * );\n * ```\n */\nexport const tap: {\n <T, E>(result: Result<T, E>, fn: (value: T) => void): Result<T, E>;\n <T>(fn: (value: T) => void): <E>(result: Result<T, E>) => Result<T, E>;\n} = dual(2, <T, E>(result: Result<T, E>, fn: (value: T) => void): Result<T, E> => {\n if (isOk(result)) fn(result.value);\n return result;\n});\n\n/**\n * Observe the `Err` value for side effects (logging, metrics) without\n * changing the carried error. Passes `Ok` through untouched.\n *\n * @example\n * ```ts\n * pipe(\n * loadUser(id),\n * tapErr((e) => metrics.inc(\"user.load.fail\", { kind: e.kind })),\n * );\n * ```\n */\nexport const tapErr: {\n <T, E>(result: Result<T, E>, fn: (error: E) => void): Result<T, E>;\n <E>(fn: (error: E) => void): <T>(result: Result<T, E>) => Result<T, E>;\n} = dual(2, <T, E>(result: Result<T, E>, fn: (error: E) => void): Result<T, E> => {\n if (isErr(result)) fn(result.error);\n return result;\n});\n\n/**\n * Terminal collapse — fold both tracks into a single value. Dual-form:\n * 3-args data-first, 2-args curried for {@link pipe}. Returns whatever\n * the handlers return.\n *\n * For files that also import `match` from `ts-pattern`, use a namespace\n * import (`import * as R from \"@onrails/result\"` → `R.match`) to dissolve\n * the collision.\n *\n * @example\n * ```ts\n * const html = match(parsed, (cfg) => render(cfg), (e) => renderError(e));\n * ```\n */\nexport const match: {\n <T, E, U>(result: Result<T, E>, onOk: (value: T) => U, onErr: (error: E) => U): U;\n <T, E, U>(onOk: (value: T) => U, onErr: (error: E) => U): (result: Result<T, E>) => U;\n} = dual(\n 3,\n <T, E, U>(result: Result<T, E>, onOk: (value: T) => U, onErr: (error: E) => U): U =>\n isOk(result) ? onOk(result.value) : onErr(result.error),\n);\n\n/**\n * Returns the `Ok` value, or `defaultValue` when the result is `Err`.\n *\n * @example\n * ```ts\n * unwrapOr(parsedSetting, \"default-value\");\n * ```\n */\nexport const unwrapOr: {\n <T, E>(result: Result<T, E>, defaultValue: T): T;\n <T>(defaultValue: T): <E>(result: Result<T, E>) => T;\n} = dual(\n 2,\n <T, E>(result: Result<T, E>, defaultValue: T): T => (isOk(result) ? result.value : defaultValue),\n);\n\n/**\n * Test/assert helper — returns the `Ok` value, or **throws the original `Err`\n * value** when called on an `Err`. This is the assertion tier (RFC 0001 §4):\n * intended for `*.spec.ts` / `*.test.ts`, where throwing fails the test loudly.\n * In business logic prefer {@link match} or {@link unwrapOr}; the lint plugins\n * flag `unwrapOk` outside test files.\n *\n * @returns the unwrapped `Ok` value\n * @throws the carried `Err` value when the result is `Err`\n *\n * @example\n * ```ts\n * // in a *.spec.ts\n * expect(unwrapOk(ok(5))).toBe(5);\n * expect(() => unwrapOk(err(error))).toThrow(error);\n * ```\n */\nexport function unwrapOk<T, E>(result: Result<T, E>): T {\n if (isErr(result)) throw result.error;\n return result.value;\n}\n\n/** Alias of {@link unwrapOk} for syntax cohesion with `@onrails/maybe`. */\nexport const unwrap = unwrapOk;\n\n/**\n * Test/assert helper — returns the `Err` value, or **throws a `TypeError`**\n * when called on an `Ok`. Mirror of {@link unwrapOk} on the error track and\n * part of the same assertion tier (RFC 0001 §4): intended for `*.spec.ts` /\n * `*.test.ts`. Prefer {@link match} / {@link unwrapOr} in business logic; the\n * lint plugins flag `unwrapErr` outside test files.\n *\n * @returns the unwrapped `Err` value\n * @throws `TypeError` when the result is `Ok`\n *\n * @example\n * ```ts\n * // in a *.spec.ts\n * expect(unwrapErr(err(\"x\"))).toBe(\"x\");\n * expect(() => unwrapErr(ok(5))).toThrow(TypeError);\n * ```\n */\nexport function unwrapErr<T, E>(result: Result<T, E>): E {\n if (isOk(result)) throw new TypeError(\"unwrapErr called on Ok\");\n return result.error;\n}\n\n/**\n * Wraps a throwing sync function, returning a function that produces a\n * {@link Result} instead of throwing. Thrown errors pass through `onThrow` to\n * become a typed `Err`; a normal return becomes `Ok`. The neverthrow analogue\n * is `Result.fromThrowable`.\n *\n * @param fn - the throwing function to wrap\n * @param onThrow - maps a thrown value to the `Err` channel\n * @returns a function with `fn`'s parameters that returns `Result<ReturnType, E>`\n *\n * @example\n * ```ts\n * type ParseError = { kind: \"parse\"; message: string };\n * const parse = trySync(\n * JSON.parse,\n * (e): ParseError => ({ kind: \"parse\", message: String(e) }),\n * );\n * parse(\"{}\"); // Ok({})\n * parse(\"nope\"); // Err({ kind: \"parse\", … })\n * ```\n */\nexport function trySync<A extends readonly unknown[], T, E>(\n fn: (...args: A) => T,\n onThrow: (error: unknown) => E,\n): (...args: A) => Result<T, E>;\nexport function trySync<F extends (...args: never) => unknown, E>(\n fn: F,\n onThrow: (error: unknown) => E,\n): (...args: Parameters<F>) => Result<ReturnType<F>, E>;\nexport function trySync(\n fn: (...args: never) => unknown,\n onThrow: (error: unknown) => unknown,\n): (...args: never) => Result<unknown, unknown> {\n return (...args: never) => {\n try {\n return ok(fn(...args));\n } catch (error) {\n return err(onThrow(error));\n }\n };\n}\n\n/**\n * Variadic value-first pipe — threads `value` through up to nine unary fns,\n * left-to-right. Use {@link pipe} when you already have a starting value;\n * use {@link flow} to define a reusable composed function with no value yet.\n *\n * @example\n * ```ts\n * pipe(\n * parseConfig(raw),\n * map((cfg) => cfg.name),\n * flatMap((name) => (name ? ok(name) : err({ kind: \"empty\" as const }))),\n * tap(log),\n * );\n * ```\n */\nexport function pipe<A>(value: A): A;\nexport function pipe<A, B>(value: A, ab: (a: A) => B): B;\nexport function pipe<A, B, C>(value: A, ab: (a: A) => B, bc: (b: B) => C): C;\nexport function pipe<A, B, C, D>(value: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D): D;\nexport function pipe<A, B, C, D, E>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n): E;\nexport function pipe<A, B, C, D, E, F>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n): F;\nexport function pipe<A, B, C, D, E, F, G>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n): G;\nexport function pipe<A, B, C, D, E, F, G, H>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n): H;\nexport function pipe<A, B, C, D, E, F, G, H, I>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n hi: (h: H) => I,\n): I;\nexport function pipe(value: unknown, ...fns: ReadonlyArray<(x: unknown) => unknown>): unknown {\n let acc = value;\n for (const fn of fns) {\n acc = fn(acc);\n }\n return acc;\n}\n","/**\n * @onrails/result/extra — tagged-error helpers.\n */\n\nimport type { ResultAsync } from \"./async.js\";\nimport type { InferErr, InferOk } from \"./internal/infer.js\";\nimport { isErr, isOk } from \"./result.js\";\nimport type { Result } from \"./types.js\";\n\n/** Extract error type from a {@link Result} — alias of {@link InferErr} */\nexport type ErrOf<R> = InferErr<R>;\n\n/** Extract success type from a {@link Result} — alias of {@link InferOk} */\nexport type OkOf<R> = InferOk<R>;\n\n/** Union of error types from a tuple/readonly array of results */\nexport type UnionErrors<R extends readonly Result<unknown, unknown>[]> = {\n [K in keyof R]: ErrOf<R[K]>;\n}[number];\n\n/** Manual union when TS fails to infer multi-step errors (neverthrow #603) */\nexport type AccumulateErrors<Errors extends readonly unknown[]> = Errors[number];\n\n/**\n * Declare the error union for a pipeline when inference only picks the first step.\n *\n * @example\n * ```ts\n * const errors = declareErrors<ParseError | NetworkError>();\n * const step = errors.annotate(parseThing());\n * ```\n */\nexport const declareErrors = <E>() => ({\n annotate: <T>(result: Result<T, unknown>): Result<T, E> => result as Result<T, E>,\n annotateAsync: <T>(result: ResultAsync<T, unknown>): ResultAsync<T, E> =>\n result as ResultAsync<T, E>,\n});\n\n/** Narrow an error by `kind` when using discriminated unions */\nexport const hasKind = <E extends { kind: string }, K extends E[\"kind\"]>(\n error: E,\n kind: K,\n): error is Extract<E, { kind: K }> => error.kind === kind;\n\n/** Map only errors matching `kind`, leave others unchanged */\nexport const mapErrKind =\n <E extends { kind: string }, K extends E[\"kind\"], F>(\n kind: K,\n fn: (error: Extract<E, { kind: K }>) => F,\n ) =>\n <T>(result: Result<T, E>): Result<T, E | F> =>\n isErr(result) && hasKind(result.error, kind)\n ? { _tag: \"Err\", error: fn(result.error) }\n : result;\n\n/** True when no result is Err */\nexport const allOk = <T, E>(results: readonly Result<T, E>[]): boolean => results.every(isOk);\n"]}
package/dist/fluent.cjs CHANGED
@@ -1,5 +1,12 @@
1
1
  'use strict';
2
2
 
3
+ // src/internal/dual.ts
4
+ var dual = (arity, body) => {
5
+ const run = body;
6
+ const dispatcher = (...args) => args.length >= arity ? run(...args) : (self) => run(self, ...args);
7
+ return dispatcher;
8
+ };
9
+
3
10
  // src/result.ts
4
11
  var ok = (value) => ({
5
12
  _tag: "Ok",
@@ -11,41 +18,55 @@ var err = (error) => ({
11
18
  });
12
19
  var isOk = (result) => result._tag === "Ok";
13
20
  var isErr = (result) => result._tag === "Err";
14
- var mapImpl = (result, fn) => isOk(result) ? ok(fn(result.value)) : err(result.error);
15
- function map(...args) {
16
- if (args.length === 2) return mapImpl(args[0], args[1]);
17
- const fn = args[0];
18
- return (result) => mapImpl(result, fn);
19
- }
20
- var mapErrImpl = (result, fn) => isErr(result) ? err(fn(result.error)) : ok(result.value);
21
- function mapErr(...args) {
22
- if (args.length === 2) return mapErrImpl(args[0], args[1]);
23
- const fn = args[0];
24
- return (result) => mapErrImpl(result, fn);
25
- }
26
- var flatMapImpl = (result, fn) => isOk(result) ? fn(result.value) : err(result.error);
27
- function flatMap(...args) {
28
- if (args.length === 2) return flatMapImpl(args[0], args[1]);
29
- const fn = args[0];
30
- return (result) => flatMapImpl(result, fn);
31
- }
32
- var matchImpl = (result, onOk, onErr) => isOk(result) ? onOk(result.value) : onErr(result.error);
33
- function match(...args) {
34
- if (args.length === 3) return matchImpl(args[0], args[1], args[2]);
35
- const [onOk, onErr] = args;
36
- return (result) => matchImpl(result, onOk, onErr);
37
- }
38
- var unwrapOr = (result, defaultValue) => isOk(result) ? result.value : defaultValue;
21
+ var map = dual(
22
+ 2,
23
+ (result, fn) => isOk(result) ? ok(fn(result.value)) : err(result.error)
24
+ );
25
+ var mapErr = dual(
26
+ 2,
27
+ (result, fn) => isErr(result) ? err(fn(result.error)) : ok(result.value)
28
+ );
29
+ var bimap = dual(
30
+ 3,
31
+ (result, onOk, onErr) => isOk(result) ? ok(onOk(result.value)) : err(onErr(result.error))
32
+ );
33
+ var flatMap = dual(
34
+ 2,
35
+ (result, fn) => isOk(result) ? fn(result.value) : err(result.error)
36
+ );
37
+ var recover = dual(
38
+ 2,
39
+ (result, fn) => isErr(result) ? fn(result.error) : ok(result.value)
40
+ );
41
+ var tap = dual(2, (result, fn) => {
42
+ if (isOk(result)) fn(result.value);
43
+ return result;
44
+ });
45
+ var tapErr = dual(2, (result, fn) => {
46
+ if (isErr(result)) fn(result.error);
47
+ return result;
48
+ });
49
+ var match = dual(
50
+ 3,
51
+ (result, onOk, onErr) => isOk(result) ? onOk(result.value) : onErr(result.error)
52
+ );
53
+ var unwrapOr = dual(
54
+ 2,
55
+ (result, defaultValue) => isOk(result) ? result.value : defaultValue
56
+ );
39
57
 
40
58
  // src/fluent.ts
41
59
  var fluent = (result) => ({
42
60
  result,
43
61
  map: (fn) => fluent(map(result, fn)),
44
62
  mapErr: (fn) => fluent(mapErr(result, fn)),
63
+ bimap: (onOk, onErr) => fluent(bimap(result, onOk, onErr)),
45
64
  flatMap: (fn) => fluent(flatMap(result, fn)),
46
65
  andThen: (fn) => fluent(flatMap(result, fn)),
47
- chain: (fn) => fluent(flatMap(result, fn)),
48
- match: (onOk, onErr) => match(onOk, onErr)(result),
66
+ recover: (fn) => fluent(recover(result, fn)),
67
+ tap: (fn) => fluent(tap(result, fn)),
68
+ tapErr: (fn) => fluent(tapErr(result, fn)),
69
+ match: (onOk, onErr) => match(result, onOk, onErr),
49
70
  unwrapOr: (defaultValue) => unwrapOr(result, defaultValue)
50
71
  });
51
72
  var fluentAsync = (resultAsync) => ({
@@ -54,8 +75,13 @@ var fluentAsync = (resultAsync) => ({
54
75
  mapErr: (fn) => fluentAsync(resultAsync.mapErr(fn)),
55
76
  flatMap: (fn) => fluentAsync(resultAsync.flatMap(fn)),
56
77
  andThen: (fn) => fluentAsync(resultAsync.andThen(fn)),
57
- chain: (fn) => fluentAsync(resultAsync.chain(fn)),
58
- match: (onOk, onErr) => resultAsync.match(onOk, onErr)
78
+ recover: (fn) => fluentAsync(resultAsync.recover(fn)),
79
+ orElse: (fn) => fluentAsync(resultAsync.orElse(fn)),
80
+ tap: (fn) => fluentAsync(resultAsync.tap(fn)),
81
+ tapErr: (fn) => fluentAsync(resultAsync.tapErr(fn)),
82
+ match: (onOk, onErr) => resultAsync.match(onOk, onErr),
83
+ unwrapOr: (defaultValue) => resultAsync.unwrapOr(defaultValue),
84
+ resolve: () => resultAsync.resolve()
59
85
  });
60
86
 
61
87
  exports.fluent = fluent;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/result.ts","../src/fluent.ts"],"names":[],"mappings":"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","file":"fluent.cjs","sourcesContent":["import type { Err, Ok, Result } from \"./types.js\";\n\nexport type { Err, Ok, Result } from \"./types.js\";\n\n/**\n * Lifts a value into the success track.\n *\n * @example\n * ```ts\n * const r = ok(42); // Result<number, never>\n * const typed: Result<number, \"parse\"> = ok(1);\n * ```\n */\nexport const ok = <T, E = never>(value: T): Result<T, E> => ({\n _tag: \"Ok\",\n value,\n});\n\n/**\n * Lifts a value into the error track.\n *\n * @example\n * ```ts\n * const r = err({ kind: \"parse\", message: \"bad json\" });\n * // Result<never, { kind: \"parse\"; message: string }>\n * ```\n */\nexport const err = <T = never, E = unknown>(error: E): Result<T, E> => ({\n _tag: \"Err\",\n error,\n});\n\n/**\n * Type-narrowing predicate: returns `true` when the result is `Ok`.\n *\n * @example\n * ```ts\n * if (isOk(r)) {\n * console.log(r.value); // narrowed to Ok branch\n * }\n * ```\n */\nexport const isOk = <T, E>(result: Result<T, E>): result is Ok<T, E> => result._tag === \"Ok\";\n\n/**\n * Type-narrowing predicate: returns `true` when the result is `Err`.\n *\n * @example\n * ```ts\n * if (isErr(r)) {\n * metrics.inc(\"error\", { kind: r.error.kind });\n * }\n * ```\n */\nexport const isErr = <T, E>(result: Result<T, E>): result is Err<T, E> => result._tag === \"Err\";\n\n/**\n * Fantasy Land `of` — alias of {@link ok}.\n * @deprecated Use {@link ok} instead.\n */\nexport const of = ok;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Dual-form helpers — each export accepts either shape:\n// data-first: `map(result, fn)`\n// curried: `map(fn)(result)`\n// Arity at the call site selects the overload.\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst mapImpl = <T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E> =>\n isOk(result) ? ok(fn(result.value)) : err(result.error);\n\n/**\n * Transform the `Ok` value, passing `Err` through unchanged. Dual-form:\n * call data-first or curried (for use with {@link pipe}).\n *\n * @example\n * ```ts\n * map(ok(2), (n) => n * 3); // Ok 6 — data-first\n * pipe(ok(\"x\"), map((s) => s.length));// Ok 1 — curried\n * ```\n */\nexport function map<T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E>;\nexport function map<T, U>(fn: (value: T) => U): <E>(result: Result<T, E>) => Result<U, E>;\nexport function map(\n ...args: [Result<unknown, unknown>, (value: unknown) => unknown] | [(value: unknown) => unknown]\n): unknown {\n if (args.length === 2) return mapImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => mapImpl(result, fn);\n}\n\nconst mapErrImpl = <T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F> =>\n isErr(result) ? err(fn(result.error)) : ok(result.value);\n\n/**\n * Transform the `Err` value, passing `Ok` through unchanged. Useful for\n * unifying heterogeneous failure types into one app-level union.\n *\n * @example\n * ```ts\n * type AppError = { kind: \"http\"; status: number } | { kind: \"parse\" };\n * pipe(\n * fetchSync(url), // Result<Body, { status: number }>\n * mapErr((e): AppError => ({ kind: \"http\", status: e.status })),\n * );\n * ```\n */\nexport function mapErr<T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F>;\nexport function mapErr<E, F>(fn: (error: E) => F): <T>(result: Result<T, E>) => Result<T, F>;\nexport function mapErr(\n ...args: [Result<unknown, unknown>, (error: unknown) => unknown] | [(error: unknown) => unknown]\n): unknown {\n if (args.length === 2) return mapErrImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => mapErrImpl(result, fn);\n}\n\nconst bimapImpl = <T, U, E, F>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n): Result<U, F> => (isOk(result) ? ok(onOk(result.value)) : err(onErr(result.error)));\n\n/**\n * Transform both tracks at once — `Ok` via `onOk`, `Err` via `onErr`.\n * Equivalent to `mapErr(onErr)(map(onOk)(result))` but in one pass.\n *\n * @example\n * ```ts\n * bimap(parsed, (cfg) => cfg.name, (e) => ({ kind: \"input\", cause: e }));\n * ```\n */\nexport function bimap<T, U, E, F>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n): Result<U, F>;\nexport function bimap<T, U, E, F>(\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n): (result: Result<T, E>) => Result<U, F>;\nexport function bimap(\n ...args:\n | [Result<unknown, unknown>, (value: unknown) => unknown, (error: unknown) => unknown]\n | [(value: unknown) => unknown, (error: unknown) => unknown]\n): unknown {\n if (args.length === 3) return bimapImpl(args[0], args[1], args[2]);\n const [onOk, onErr] = args;\n return (result: Result<unknown, unknown>) => bimapImpl(result, onOk, onErr);\n}\n\nconst flatMapImpl = <T, U, E, F>(\n result: Result<T, E>,\n fn: (value: T) => Result<U, F>,\n): Result<U, E | F> => (isOk(result) ? fn(result.value) : err(result.error));\n\n/**\n * Canonical bind (Fantasy Land `chain`). Chains a Result-returning step,\n * widening the error union to `E | F`. Short-circuits on `Err`.\n *\n * @example\n * ```ts\n * flatMap(parseInput(raw), (data) =>\n * data.id != null ? ok(data) : err({ kind: \"missing_id\" as const }),\n * );\n * // Result<Data, ParseError | { kind: \"missing_id\" }>\n * ```\n */\nexport function flatMap<T, U, E, F>(\n result: Result<T, E>,\n fn: (value: T) => Result<U, F>,\n): Result<U, E | F>;\nexport function flatMap<T, U, F>(\n fn: (value: T) => Result<U, F>,\n): <E>(result: Result<T, E>) => Result<U, E | F>;\nexport function flatMap(\n ...args:\n | [Result<unknown, unknown>, (value: unknown) => Result<unknown, unknown>]\n | [(value: unknown) => Result<unknown, unknown>]\n): unknown {\n if (args.length === 2) return flatMapImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => flatMapImpl(result, fn);\n}\n\nconst recoverImpl = <T, E, F>(\n result: Result<T, E>,\n fn: (error: E) => Result<T, F>,\n): Result<T, F> => (isErr(result) ? fn(result.error) : ok(result.value));\n\n/**\n * Error-track bind — runs `fn` only when the result is `Err`, allowing\n * a failed workflow to recover to `Ok` or remap the failure. Mirror of\n * {@link flatMap} on the error channel.\n *\n * @example\n * ```ts\n * recover(networkResult, (e) =>\n * e.kind === \"rate_limit\" ? ok(cachedBody) : err(e),\n * );\n * ```\n */\nexport function recover<T, E, F>(\n result: Result<T, E>,\n fn: (error: E) => Result<T, F>,\n): Result<T, F>;\nexport function recover<T, E, F>(\n fn: (error: E) => Result<T, F>,\n): (result: Result<T, E>) => Result<T, F>;\nexport function recover(\n ...args:\n | [Result<unknown, unknown>, (error: unknown) => Result<unknown, unknown>]\n | [(error: unknown) => Result<unknown, unknown>]\n): unknown {\n if (args.length === 2) return recoverImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => recoverImpl(result, fn);\n}\n\nconst tapImpl = <T, E>(result: Result<T, E>, fn: (value: T) => void): Result<T, E> => {\n if (isOk(result)) fn(result.value);\n return result;\n};\n\n/**\n * Observe the `Ok` value for side effects (logging, metrics) without\n * changing the carried value. Passes `Err` through untouched.\n *\n * @example\n * ```ts\n * pipe(\n * parseConfig(raw),\n * tap((cfg) => log.info({ msg: \"parsed\", name: cfg.name })),\n * flatMap(validate),\n * );\n * ```\n */\nexport function tap<T, E>(result: Result<T, E>, fn: (value: T) => void): Result<T, E>;\nexport function tap<T>(fn: (value: T) => void): <E>(result: Result<T, E>) => Result<T, E>;\nexport function tap(\n ...args: [Result<unknown, unknown>, (value: unknown) => void] | [(value: unknown) => void]\n): unknown {\n if (args.length === 2) return tapImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => tapImpl(result, fn);\n}\n\nconst tapErrImpl = <T, E>(result: Result<T, E>, fn: (error: E) => void): Result<T, E> => {\n if (isErr(result)) fn(result.error);\n return result;\n};\n\n/**\n * Observe the `Err` value for side effects (logging, metrics) without\n * changing the carried error. Passes `Ok` through untouched.\n *\n * @example\n * ```ts\n * pipe(\n * loadUser(id),\n * tapErr((e) => metrics.inc(\"user.load.fail\", { kind: e.kind })),\n * );\n * ```\n */\nexport function tapErr<T, E>(result: Result<T, E>, fn: (error: E) => void): Result<T, E>;\nexport function tapErr<E>(fn: (error: E) => void): <T>(result: Result<T, E>) => Result<T, E>;\nexport function tapErr(\n ...args: [Result<unknown, unknown>, (error: unknown) => void] | [(error: unknown) => void]\n): unknown {\n if (args.length === 2) return tapErrImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => tapErrImpl(result, fn);\n}\n\nconst matchImpl = <T, E, U>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => U,\n): U => (isOk(result) ? onOk(result.value) : onErr(result.error));\n\n/**\n * Terminal collapse — fold both tracks into a single value. Dual-form:\n * 3-args data-first, 2-args curried for {@link pipe}. Returns whatever\n * the handlers return.\n *\n * For files that also import `match` from `ts-pattern`, the\n * collision-free alias {@link matchResult} is identical.\n *\n * @example\n * ```ts\n * const html = match(parsed, (cfg) => render(cfg), (e) => renderError(e));\n * ```\n */\nexport function match<T, E, U>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => U,\n): U;\nexport function match<T, E, U>(\n onOk: (value: T) => U,\n onErr: (error: E) => U,\n): (result: Result<T, E>) => U;\nexport function match(\n ...args:\n | [Result<unknown, unknown>, (value: unknown) => unknown, (error: unknown) => unknown]\n | [(value: unknown) => unknown, (error: unknown) => unknown]\n): unknown {\n if (args.length === 3) return matchImpl(args[0], args[1], args[2]);\n const [onOk, onErr] = args;\n return (result: Result<unknown, unknown>) => matchImpl(result, onOk, onErr);\n}\n\n/**\n * Collision-free alias for files that also import `match` from ts-pattern.\n * @deprecated Namespace import carriers (e.g., `import * as R from \"@onrails/result\"`) and use canonical {@link match} instead.\n */\nexport const matchResult = match;\n\n/**\n * Curried collapse with named slots — escape valve when positional `match`\n * order is unclear at the call site (e.g. when both handlers return the same\n * type and a transposed `match(r, onErr, onOk)` would silently compile).\n * @deprecated Use curried {@link match}(onOk, onErr)(result) instead.\n */\nexport const fold =\n <T, E, U>(handlers: { readonly ok: (value: T) => U; readonly err: (error: E) => U }) =>\n (result: Result<T, E>): U =>\n matchImpl(result, handlers.ok, handlers.err);\n\n/**\n * Returns the `Ok` value, or `defaultValue` when the result is `Err`.\n *\n * @example\n * ```ts\n * unwrapOr(parsedSetting, \"default-value\");\n * ```\n */\nexport const unwrapOr = <T, E>(result: Result<T, E>, defaultValue: T): T =>\n isOk(result) ? result.value : defaultValue;\n\n/**\n * Test/assert helper — throws the original Err value when called on Err.\n *\n * @throws when the result is Err — assertion-tier; use {@link match} / {@link unwrapOr} in business logic.\n * Allowed in `*.spec.ts` / `*.test.ts`; flagged elsewhere by the plugins.\n */\nexport function unwrapOk<T, E>(result: Result<T, E>): T {\n if (isErr(result)) throw result.error;\n return result.value;\n}\n\n/**\n * Test/assert helper — throws TypeError when called on Ok.\n *\n * @throws when the result is Ok — assertion-tier; allowed in `*.spec.ts` / `*.test.ts`; flagged elsewhere by the plugins.\n */\nexport function unwrapErr<T, E>(result: Result<T, E>): E {\n if (isOk(result)) throw new TypeError(\"unwrapErr called on Ok\");\n return result.error;\n}\n\n/** Wrap a throwing sync function — neverthrow `Result.fromThrowable` */\nexport function trySync<A extends readonly unknown[], T, E>(\n fn: (...args: A) => T,\n onThrow: (error: unknown) => E,\n): (...args: A) => Result<T, E>;\nexport function trySync<F extends (...args: never) => unknown, E>(\n fn: F,\n onThrow: (error: unknown) => E,\n): (...args: Parameters<F>) => Result<ReturnType<F>, E>;\nexport function trySync(\n fn: (...args: never) => unknown,\n onThrow: (error: unknown) => unknown,\n): (...args: never) => Result<unknown, unknown> {\n return (...args: never) => {\n try {\n return ok(fn(...args));\n } catch (error) {\n return err(onThrow(error));\n }\n };\n}\n\n/** First failure wins; otherwise collects values in order */\nexport const combine = <T, E>(results: readonly Result<T, E>[]): Result<T[], E> => {\n const values: T[] = [];\n for (const result of results) {\n if (isErr(result)) return err(result.error);\n values.push(result.value);\n }\n return ok(values);\n};\n\n/** Tuple-preserving combine (neverthrow-style) */\nexport const combineTuple = <const R extends readonly Result<unknown, unknown>[]>(\n results: R,\n): CombineTuple<R> =>\n // Runtime identical to combine; the cast restores per-index tuple types.\n combine(results as readonly Result<unknown, unknown>[]) as CombineTuple<R>;\n\ntype _OkValue<R> = R extends { _tag: \"Ok\"; readonly value: infer T } ? T : never;\ntype _ErrValue<R> = R extends { _tag: \"Err\"; readonly error: infer E } ? E : never;\n\ntype CombineTuple<R extends readonly Result<unknown, unknown>[]> = Result<\n { [K in keyof R]: _OkValue<R[K]> },\n { [K in keyof R]: _ErrValue<R[K]> }[number]\n>;\n\n/**\n * Variadic value-first pipe — threads `value` through up to nine unary fns.\n *\n * ```ts\n * pipe(\n * parseConfig(raw),\n * map((cfg) => cfg.name),\n * flatMap((name) => name ? ok(name) : err({ kind: \"empty\" })),\n * tap(log),\n * );\n * ```\n */\nexport function pipe<A>(value: A): A;\nexport function pipe<A, B>(value: A, ab: (a: A) => B): B;\nexport function pipe<A, B, C>(value: A, ab: (a: A) => B, bc: (b: B) => C): C;\nexport function pipe<A, B, C, D>(value: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D): D;\nexport function pipe<A, B, C, D, E>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n): E;\nexport function pipe<A, B, C, D, E, F>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n): F;\nexport function pipe<A, B, C, D, E, F, G>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n): G;\nexport function pipe<A, B, C, D, E, F, G, H>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n): H;\nexport function pipe<A, B, C, D, E, F, G, H, I>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n hi: (h: H) => I,\n): I;\nexport function pipe(value: unknown, ...fns: ReadonlyArray<(x: unknown) => unknown>): unknown {\n let acc = value;\n for (const fn of fns) {\n acc = fn(acc);\n }\n return acc;\n}\n","import type { ResultAsync } from \"./async.js\";\nimport { flatMap, map, mapErr, match, unwrapOr } from \"./result.js\";\nimport type { Result } from \"./types.js\";\n\n/** Opt-in dot chaining over a sync {@link Result} */\nexport type FluentResult<T, E> = {\n readonly result: Result<T, E>;\n map: <U>(fn: (value: T) => U) => FluentResult<U, E>;\n mapErr: <F>(fn: (error: E) => F) => FluentResult<T, F>;\n flatMap: <U>(fn: (value: T) => Result<U, E>) => FluentResult<U, E>;\n andThen: <U>(fn: (value: T) => Result<U, E>) => FluentResult<U, E>;\n /** @deprecated Use {@link flatMap} or compat {@link andThen} instead. */\n chain: <U>(fn: (value: T) => Result<U, E>) => FluentResult<U, E>;\n match: <U>(onOk: (value: T) => U, onErr: (error: E) => U) => U;\n unwrapOr: (defaultValue: T) => T;\n};\n\nexport const fluent = <T, E>(result: Result<T, E>): FluentResult<T, E> => ({\n result,\n map: (fn) => fluent(map(result, fn)),\n mapErr: (fn) => fluent(mapErr(result, fn)),\n flatMap: (fn) => fluent(flatMap(result, fn)),\n andThen: (fn) => fluent(flatMap(result, fn)),\n chain: (fn) => fluent(flatMap(result, fn)),\n match: (onOk, onErr) => match(onOk, onErr)(result),\n unwrapOr: (defaultValue) => unwrapOr(result, defaultValue),\n});\n\n/** Opt-in dot chaining over {@link ResultAsync} */\nexport type FluentResultAsync<T, E> = {\n readonly resultAsync: ResultAsync<T, E>;\n map: <U>(fn: (value: T) => U) => FluentResultAsync<U, E>;\n mapErr: <F>(fn: (error: E) => F) => FluentResultAsync<T, F>;\n flatMap: <U>(fn: (value: T) => ResultAsync<U, E>) => FluentResultAsync<U, E>;\n andThen: <U>(fn: (value: T) => ResultAsync<U, E>) => FluentResultAsync<U, E>;\n /** @deprecated Use {@link flatMap} or compat {@link andThen} instead. */\n chain: <U>(fn: (value: T) => ResultAsync<U, E>) => FluentResultAsync<U, E>;\n match: <U>(onOk: (value: T) => U, onErr: (error: E) => U) => Promise<U>;\n};\n\nexport const fluentAsync = <T, E>(resultAsync: ResultAsync<T, E>): FluentResultAsync<T, E> => ({\n resultAsync,\n map: (fn) => fluentAsync(resultAsync.map(fn)),\n mapErr: (fn) => fluentAsync(resultAsync.mapErr(fn)),\n flatMap: (fn) => fluentAsync(resultAsync.flatMap(fn)),\n andThen: (fn) => fluentAsync(resultAsync.andThen(fn)),\n chain: (fn) => fluentAsync(resultAsync.chain(fn)),\n match: (onOk, onErr) => resultAsync.match(onOk, onErr),\n});\n"]}
1
+ {"version":3,"sources":["../src/internal/dual.ts","../src/result.ts","../src/fluent.ts"],"names":[],"mappings":"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","file":"fluent.cjs","sourcesContent":["/**\n * @onrails/result internal — owning module for the **dual-form idiom**.\n *\n * Every dual-form transform in this package (`map`, `flatMap`, `match`, …)\n * accepts either shape:\n *\n * data-first: `map(result, fn)` — full arity, runs immediately\n * curried: `map(fn)(result)` — arity minus the leading `self`, returns\n * a unary function awaiting the data\n *\n * {@link dual} is the single arity dispatcher those transforms derive from;\n * hand-rolled `args.length` checks are not allowed elsewhere.\n *\n * NOTE: a twin copy of this module lives at `packages/maybe/src/internal/dual.ts`\n * — keep the two in sync when editing.\n *\n * Internal only: not listed in package.json `exports` or tsup entries.\n */\n\n/**\n * Derive a dual-form (data-first / curried) function from a single\n * data-first `body`.\n *\n * When called with `args.length >= arity` the wrapper runs `body(...args)`\n * (data-first). With fewer arguments it returns `(self) => body(self, ...args)`\n * — the curried, data-last-`self` form used with `pipe`/`flow`.\n *\n * The public type is supplied entirely by the caller: annotate the exported\n * const with the exact overload set and let `F` be inferred from it.\n *\n * @example\n * ```ts\n * export const map: {\n * <T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E>;\n * <T, U>(fn: (value: T) => U): <E>(result: Result<T, E>) => Result<U, E>;\n * } = dual(\n * 2,\n * <T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E> =>\n * isOk(result) ? ok(fn(result.value)) : err(result.error),\n * );\n * ```\n */\nexport const dual = <F>(arity: number, body: (...args: never[]) => unknown): F => {\n // `(...args: never[]) => unknown` is the top function type, so every typed\n // body is assignable to the parameter — but it cannot be invoked with\n // `unknown` arguments. Re-view it through the variadic shape the dispatcher\n // actually forwards; the constraint is that `body` only ever receives the\n // arguments the caller's overload annotation admitted.\n const run = body as (...args: readonly unknown[]) => unknown;\n const dispatcher = (...args: readonly unknown[]): unknown =>\n args.length >= arity ? run(...args) : (self: unknown) => run(self, ...args);\n // The caller's overload annotation on the exported const supplies the real\n // public type; internally the dispatcher stays `unknown`-typed.\n return dispatcher as F;\n};\n","import { dual } from \"./internal/dual.js\";\nimport type { Err, Ok, Result } from \"./types.js\";\n\nexport type { Err, Ok, Result } from \"./types.js\";\n\n/**\n * Lifts a value into the success track.\n *\n * @example\n * ```ts\n * const r = ok(42); // Result<number, never>\n * const typed: Result<number, \"parse\"> = ok(1);\n * ```\n */\nexport const ok = <T, E = never>(value: T): Result<T, E> => ({\n _tag: \"Ok\",\n value,\n});\n\n/**\n * Lifts a value into the error track.\n *\n * @example\n * ```ts\n * const r = err({ kind: \"parse\", message: \"bad json\" });\n * // Result<never, { kind: \"parse\"; message: string }>\n * ```\n */\nexport const err = <T = never, E = unknown>(error: E): Result<T, E> => ({\n _tag: \"Err\",\n error,\n});\n\n/**\n * Type-narrowing predicate: returns `true` when the result is `Ok`.\n *\n * @example\n * ```ts\n * if (isOk(r)) {\n * console.log(r.value); // narrowed to Ok branch\n * }\n * ```\n */\nexport const isOk = <T, E>(result: Result<T, E>): result is Ok<T, E> => result._tag === \"Ok\";\n\n/**\n * Type-narrowing predicate: returns `true` when the result is `Err`.\n *\n * @example\n * ```ts\n * if (isErr(r)) {\n * metrics.inc(\"error\", { kind: r.error.kind });\n * }\n * ```\n */\nexport const isErr = <T, E>(result: Result<T, E>): result is Err<T, E> => result._tag === \"Err\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Dual-form helpers — each export accepts either shape:\n// data-first: `map(result, fn)`\n// curried: `map(fn)(result)`\n// Arity dispatch is derived from the internal `dual` combinator\n// (src/internal/dual.ts); the const annotation carries the public overloads.\n// ─────────────────────────────────────────────────────────────────────────────\n\n/**\n * Transform the `Ok` value, passing `Err` through unchanged. Dual-form:\n * call data-first or curried (for use with {@link pipe}).\n *\n * @example\n * ```ts\n * map(ok(2), (n) => n * 3); // Ok 6 — data-first\n * pipe(ok(\"x\"), map((s) => s.length));// Ok 1 — curried\n * ```\n */\nexport const map: {\n <T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E>;\n <T, U>(fn: (value: T) => U): <E>(result: Result<T, E>) => Result<U, E>;\n} = dual(\n 2,\n <T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E> =>\n isOk(result) ? ok(fn(result.value)) : err(result.error),\n);\n\n/**\n * Transform the `Err` value, passing `Ok` through unchanged. Useful for\n * unifying heterogeneous failure types into one app-level union.\n *\n * @example\n * ```ts\n * type AppError = { kind: \"http\"; status: number } | { kind: \"parse\" };\n * pipe(\n * fetchSync(url), // Result<Body, { status: number }>\n * mapErr((e): AppError => ({ kind: \"http\", status: e.status })),\n * );\n * ```\n */\nexport const mapErr: {\n <T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F>;\n <E, F>(fn: (error: E) => F): <T>(result: Result<T, E>) => Result<T, F>;\n} = dual(\n 2,\n <T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F> =>\n isErr(result) ? err(fn(result.error)) : ok(result.value),\n);\n\n/**\n * Transform both tracks at once — `Ok` via `onOk`, `Err` via `onErr`.\n * Equivalent to `mapErr(onErr)(map(onOk)(result))` but in one pass.\n *\n * @example\n * ```ts\n * bimap(parsed, (cfg) => cfg.name, (e) => ({ kind: \"input\", cause: e }));\n * ```\n */\nexport const bimap: {\n <T, U, E, F>(result: Result<T, E>, onOk: (value: T) => U, onErr: (error: E) => F): Result<U, F>;\n <T, U, E, F>(\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n ): (result: Result<T, E>) => Result<U, F>;\n} = dual(\n 3,\n <T, U, E, F>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n ): Result<U, F> => (isOk(result) ? ok(onOk(result.value)) : err(onErr(result.error))),\n);\n\n/**\n * Canonical bind (Fantasy Land `chain`). Chains a Result-returning step,\n * widening the error union to `E | F`. Short-circuits on `Err`.\n *\n * @example\n * ```ts\n * flatMap(parseInput(raw), (data) =>\n * data.id != null ? ok(data) : err({ kind: \"missing_id\" as const }),\n * );\n * // Result<Data, ParseError | { kind: \"missing_id\" }>\n * ```\n */\nexport const flatMap: {\n <T, U, E, F>(result: Result<T, E>, fn: (value: T) => Result<U, F>): Result<U, E | F>;\n <T, U, F>(fn: (value: T) => Result<U, F>): <E>(result: Result<T, E>) => Result<U, E | F>;\n} = dual(\n 2,\n <T, U, E, F>(result: Result<T, E>, fn: (value: T) => Result<U, F>): Result<U, E | F> =>\n isOk(result) ? fn(result.value) : err(result.error),\n);\n\n/**\n * Error-track bind — runs `fn` only when the result is `Err`, allowing\n * a failed workflow to recover to `Ok` or remap the failure. Mirror of\n * {@link flatMap} on the error channel.\n *\n * @example\n * ```ts\n * recover(networkResult, (e) =>\n * e.kind === \"rate_limit\" ? ok(cachedBody) : err(e),\n * );\n * ```\n */\nexport const recover: {\n <T, E, F>(result: Result<T, E>, fn: (error: E) => Result<T, F>): Result<T, F>;\n <T, E, F>(fn: (error: E) => Result<T, F>): (result: Result<T, E>) => Result<T, F>;\n} = dual(\n 2,\n <T, E, F>(result: Result<T, E>, fn: (error: E) => Result<T, F>): Result<T, F> =>\n isErr(result) ? fn(result.error) : ok(result.value),\n);\n\n/**\n * Observe the `Ok` value for side effects (logging, metrics) without\n * changing the carried value. Passes `Err` through untouched.\n *\n * @example\n * ```ts\n * pipe(\n * parseConfig(raw),\n * tap((cfg) => log.info({ msg: \"parsed\", name: cfg.name })),\n * flatMap(validate),\n * );\n * ```\n */\nexport const tap: {\n <T, E>(result: Result<T, E>, fn: (value: T) => void): Result<T, E>;\n <T>(fn: (value: T) => void): <E>(result: Result<T, E>) => Result<T, E>;\n} = dual(2, <T, E>(result: Result<T, E>, fn: (value: T) => void): Result<T, E> => {\n if (isOk(result)) fn(result.value);\n return result;\n});\n\n/**\n * Observe the `Err` value for side effects (logging, metrics) without\n * changing the carried error. Passes `Ok` through untouched.\n *\n * @example\n * ```ts\n * pipe(\n * loadUser(id),\n * tapErr((e) => metrics.inc(\"user.load.fail\", { kind: e.kind })),\n * );\n * ```\n */\nexport const tapErr: {\n <T, E>(result: Result<T, E>, fn: (error: E) => void): Result<T, E>;\n <E>(fn: (error: E) => void): <T>(result: Result<T, E>) => Result<T, E>;\n} = dual(2, <T, E>(result: Result<T, E>, fn: (error: E) => void): Result<T, E> => {\n if (isErr(result)) fn(result.error);\n return result;\n});\n\n/**\n * Terminal collapse — fold both tracks into a single value. Dual-form:\n * 3-args data-first, 2-args curried for {@link pipe}. Returns whatever\n * the handlers return.\n *\n * For files that also import `match` from `ts-pattern`, use a namespace\n * import (`import * as R from \"@onrails/result\"` → `R.match`) to dissolve\n * the collision.\n *\n * @example\n * ```ts\n * const html = match(parsed, (cfg) => render(cfg), (e) => renderError(e));\n * ```\n */\nexport const match: {\n <T, E, U>(result: Result<T, E>, onOk: (value: T) => U, onErr: (error: E) => U): U;\n <T, E, U>(onOk: (value: T) => U, onErr: (error: E) => U): (result: Result<T, E>) => U;\n} = dual(\n 3,\n <T, E, U>(result: Result<T, E>, onOk: (value: T) => U, onErr: (error: E) => U): U =>\n isOk(result) ? onOk(result.value) : onErr(result.error),\n);\n\n/**\n * Returns the `Ok` value, or `defaultValue` when the result is `Err`.\n *\n * @example\n * ```ts\n * unwrapOr(parsedSetting, \"default-value\");\n * ```\n */\nexport const unwrapOr: {\n <T, E>(result: Result<T, E>, defaultValue: T): T;\n <T>(defaultValue: T): <E>(result: Result<T, E>) => T;\n} = dual(\n 2,\n <T, E>(result: Result<T, E>, defaultValue: T): T => (isOk(result) ? result.value : defaultValue),\n);\n\n/**\n * Test/assert helper — returns the `Ok` value, or **throws the original `Err`\n * value** when called on an `Err`. This is the assertion tier (RFC 0001 §4):\n * intended for `*.spec.ts` / `*.test.ts`, where throwing fails the test loudly.\n * In business logic prefer {@link match} or {@link unwrapOr}; the lint plugins\n * flag `unwrapOk` outside test files.\n *\n * @returns the unwrapped `Ok` value\n * @throws the carried `Err` value when the result is `Err`\n *\n * @example\n * ```ts\n * // in a *.spec.ts\n * expect(unwrapOk(ok(5))).toBe(5);\n * expect(() => unwrapOk(err(error))).toThrow(error);\n * ```\n */\nexport function unwrapOk<T, E>(result: Result<T, E>): T {\n if (isErr(result)) throw result.error;\n return result.value;\n}\n\n/** Alias of {@link unwrapOk} for syntax cohesion with `@onrails/maybe`. */\nexport const unwrap = unwrapOk;\n\n/**\n * Test/assert helper — returns the `Err` value, or **throws a `TypeError`**\n * when called on an `Ok`. Mirror of {@link unwrapOk} on the error track and\n * part of the same assertion tier (RFC 0001 §4): intended for `*.spec.ts` /\n * `*.test.ts`. Prefer {@link match} / {@link unwrapOr} in business logic; the\n * lint plugins flag `unwrapErr` outside test files.\n *\n * @returns the unwrapped `Err` value\n * @throws `TypeError` when the result is `Ok`\n *\n * @example\n * ```ts\n * // in a *.spec.ts\n * expect(unwrapErr(err(\"x\"))).toBe(\"x\");\n * expect(() => unwrapErr(ok(5))).toThrow(TypeError);\n * ```\n */\nexport function unwrapErr<T, E>(result: Result<T, E>): E {\n if (isOk(result)) throw new TypeError(\"unwrapErr called on Ok\");\n return result.error;\n}\n\n/**\n * Wraps a throwing sync function, returning a function that produces a\n * {@link Result} instead of throwing. Thrown errors pass through `onThrow` to\n * become a typed `Err`; a normal return becomes `Ok`. The neverthrow analogue\n * is `Result.fromThrowable`.\n *\n * @param fn - the throwing function to wrap\n * @param onThrow - maps a thrown value to the `Err` channel\n * @returns a function with `fn`'s parameters that returns `Result<ReturnType, E>`\n *\n * @example\n * ```ts\n * type ParseError = { kind: \"parse\"; message: string };\n * const parse = trySync(\n * JSON.parse,\n * (e): ParseError => ({ kind: \"parse\", message: String(e) }),\n * );\n * parse(\"{}\"); // Ok({})\n * parse(\"nope\"); // Err({ kind: \"parse\", … })\n * ```\n */\nexport function trySync<A extends readonly unknown[], T, E>(\n fn: (...args: A) => T,\n onThrow: (error: unknown) => E,\n): (...args: A) => Result<T, E>;\nexport function trySync<F extends (...args: never) => unknown, E>(\n fn: F,\n onThrow: (error: unknown) => E,\n): (...args: Parameters<F>) => Result<ReturnType<F>, E>;\nexport function trySync(\n fn: (...args: never) => unknown,\n onThrow: (error: unknown) => unknown,\n): (...args: never) => Result<unknown, unknown> {\n return (...args: never) => {\n try {\n return ok(fn(...args));\n } catch (error) {\n return err(onThrow(error));\n }\n };\n}\n\n/**\n * Variadic value-first pipe — threads `value` through up to nine unary fns,\n * left-to-right. Use {@link pipe} when you already have a starting value;\n * use {@link flow} to define a reusable composed function with no value yet.\n *\n * @example\n * ```ts\n * pipe(\n * parseConfig(raw),\n * map((cfg) => cfg.name),\n * flatMap((name) => (name ? ok(name) : err({ kind: \"empty\" as const }))),\n * tap(log),\n * );\n * ```\n */\nexport function pipe<A>(value: A): A;\nexport function pipe<A, B>(value: A, ab: (a: A) => B): B;\nexport function pipe<A, B, C>(value: A, ab: (a: A) => B, bc: (b: B) => C): C;\nexport function pipe<A, B, C, D>(value: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D): D;\nexport function pipe<A, B, C, D, E>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n): E;\nexport function pipe<A, B, C, D, E, F>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n): F;\nexport function pipe<A, B, C, D, E, F, G>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n): G;\nexport function pipe<A, B, C, D, E, F, G, H>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n): H;\nexport function pipe<A, B, C, D, E, F, G, H, I>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n hi: (h: H) => I,\n): I;\nexport function pipe(value: unknown, ...fns: ReadonlyArray<(x: unknown) => unknown>): unknown {\n let acc = value;\n for (const fn of fns) {\n acc = fn(acc);\n }\n return acc;\n}\n","import type { ResultAsync } from \"./async.js\";\nimport { bimap, flatMap, map, mapErr, match, recover, tap, tapErr, unwrapOr } from \"./result.js\";\nimport type { Result } from \"./types.js\";\n\n/** Opt-in dot chaining over a sync {@link Result} */\nexport type FluentResult<T, E> = {\n readonly result: Result<T, E>;\n map: <U>(fn: (value: T) => U) => FluentResult<U, E>;\n mapErr: <F>(fn: (error: E) => F) => FluentResult<T, F>;\n bimap: <U, F>(onOk: (value: T) => U, onErr: (error: E) => F) => FluentResult<U, F>;\n flatMap: <U, F = E>(fn: (value: T) => Result<U, F>) => FluentResult<U, E | F>;\n andThen: <U, F = E>(fn: (value: T) => Result<U, F>) => FluentResult<U, E | F>;\n recover: <F>(fn: (error: E) => Result<T, F>) => FluentResult<T, F>;\n tap: (fn: (value: T) => void) => FluentResult<T, E>;\n tapErr: (fn: (error: E) => void) => FluentResult<T, E>;\n\n match: <U>(onOk: (value: T) => U, onErr: (error: E) => U) => U;\n unwrapOr: (defaultValue: T) => T;\n};\n\n/**\n * Wrap a {@link Result} for opt-in dot chaining — app-edge sugar. Keep the\n * data-first helpers ({@link map}, {@link flatMap}) in library internals.\n *\n * Mirrors every core transform; `test/parity.spec.ts` enforces the mirror.\n *\n * @example\n * ```ts\n * fluent(ok(2)).map((n) => n * 3).unwrapOr(0); // 6\n * ```\n */\nexport const fluent = <T, E>(result: Result<T, E>): FluentResult<T, E> => ({\n result,\n map: (fn) => fluent(map(result, fn)),\n mapErr: (fn) => fluent(mapErr(result, fn)),\n bimap: (onOk, onErr) => fluent(bimap(result, onOk, onErr)),\n flatMap: (fn) => fluent(flatMap(result, fn)),\n andThen: (fn) => fluent(flatMap(result, fn)),\n recover: (fn) => fluent(recover(result, fn)),\n tap: (fn) => fluent(tap(result, fn)),\n tapErr: (fn) => fluent(tapErr(result, fn)),\n\n match: (onOk, onErr) => match(result, onOk, onErr),\n unwrapOr: (defaultValue) => unwrapOr(result, defaultValue),\n});\n\n/** Opt-in dot chaining over {@link ResultAsync} */\nexport type FluentResultAsync<T, E> = {\n readonly resultAsync: ResultAsync<T, E>;\n map: <U>(fn: (value: T) => U) => FluentResultAsync<U, E>;\n mapErr: <F>(fn: (error: E) => F) => FluentResultAsync<T, F>;\n flatMap: <U, F = E>(\n fn: (value: T) => ResultAsync<U, F> | Result<U, F>,\n ) => FluentResultAsync<U, E | F>;\n andThen: <U, F = E>(\n fn: (value: T) => ResultAsync<U, F> | Result<U, F>,\n ) => FluentResultAsync<U, E | F>;\n recover: <F>(fn: (error: E) => ResultAsync<T, F> | Result<T, F>) => FluentResultAsync<T, F>;\n orElse: <F>(fn: (error: E) => ResultAsync<T, F> | Result<T, F>) => FluentResultAsync<T, F>;\n tap: (fn: (value: T) => void) => FluentResultAsync<T, E>;\n tapErr: (fn: (error: E) => void) => FluentResultAsync<T, E>;\n\n match: <U1, U2 = U1>(onOk: (value: T) => U1, onErr: (error: E) => U2) => Promise<U1 | U2>;\n unwrapOr: <U>(defaultValue: U) => Promise<T | U>;\n resolve: () => Promise<Result<T, E>>;\n};\n\n/**\n * Wrap a {@link ResultAsync} for opt-in dot chaining — the async counterpart of\n * {@link fluent}. App-edge sugar; await a terminal (`match`, `unwrapOr`,\n * `resolve`) to leave the fluent world.\n *\n * @example\n * ```ts\n * fluentAsync(okAsync(2)).map((n) => n * 3); // FluentResultAsync<number, never>\n * ```\n */\nexport const fluentAsync = <T, E>(resultAsync: ResultAsync<T, E>): FluentResultAsync<T, E> => ({\n resultAsync,\n map: (fn) => fluentAsync(resultAsync.map(fn)),\n mapErr: (fn) => fluentAsync(resultAsync.mapErr(fn)),\n flatMap: (fn) => fluentAsync(resultAsync.flatMap(fn)),\n andThen: (fn) => fluentAsync(resultAsync.andThen(fn)),\n recover: (fn) => fluentAsync(resultAsync.recover(fn)),\n orElse: (fn) => fluentAsync(resultAsync.orElse(fn)),\n tap: (fn) => fluentAsync(resultAsync.tap(fn)),\n tapErr: (fn) => fluentAsync(resultAsync.tapErr(fn)),\n\n match: (onOk, onErr) => resultAsync.match(onOk, onErr),\n unwrapOr: (defaultValue) => resultAsync.unwrapOr(defaultValue),\n resolve: () => resultAsync.resolve(),\n});\n"]}
package/dist/fluent.d.cts CHANGED
@@ -1,30 +1,57 @@
1
- import { R as ResultAsync } from './async-CH8Cs8KE.cjs';
2
- import { R as Result } from './types-C2Dp1d5J.cjs';
1
+ import { R as ResultAsync } from './async-BR3AX0ck.cjs';
2
+ import { R as Result } from './types-BQ9vv0nD.cjs';
3
3
 
4
4
  /** Opt-in dot chaining over a sync {@link Result} */
5
5
  type FluentResult<T, E> = {
6
6
  readonly result: Result<T, E>;
7
7
  map: <U>(fn: (value: T) => U) => FluentResult<U, E>;
8
8
  mapErr: <F>(fn: (error: E) => F) => FluentResult<T, F>;
9
- flatMap: <U>(fn: (value: T) => Result<U, E>) => FluentResult<U, E>;
10
- andThen: <U>(fn: (value: T) => Result<U, E>) => FluentResult<U, E>;
11
- /** @deprecated Use {@link flatMap} or compat {@link andThen} instead. */
12
- chain: <U>(fn: (value: T) => Result<U, E>) => FluentResult<U, E>;
9
+ bimap: <U, F>(onOk: (value: T) => U, onErr: (error: E) => F) => FluentResult<U, F>;
10
+ flatMap: <U, F = E>(fn: (value: T) => Result<U, F>) => FluentResult<U, E | F>;
11
+ andThen: <U, F = E>(fn: (value: T) => Result<U, F>) => FluentResult<U, E | F>;
12
+ recover: <F>(fn: (error: E) => Result<T, F>) => FluentResult<T, F>;
13
+ tap: (fn: (value: T) => void) => FluentResult<T, E>;
14
+ tapErr: (fn: (error: E) => void) => FluentResult<T, E>;
13
15
  match: <U>(onOk: (value: T) => U, onErr: (error: E) => U) => U;
14
16
  unwrapOr: (defaultValue: T) => T;
15
17
  };
18
+ /**
19
+ * Wrap a {@link Result} for opt-in dot chaining — app-edge sugar. Keep the
20
+ * data-first helpers ({@link map}, {@link flatMap}) in library internals.
21
+ *
22
+ * Mirrors every core transform; `test/parity.spec.ts` enforces the mirror.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * fluent(ok(2)).map((n) => n * 3).unwrapOr(0); // 6
27
+ * ```
28
+ */
16
29
  declare const fluent: <T, E>(result: Result<T, E>) => FluentResult<T, E>;
17
30
  /** Opt-in dot chaining over {@link ResultAsync} */
18
31
  type FluentResultAsync<T, E> = {
19
32
  readonly resultAsync: ResultAsync<T, E>;
20
33
  map: <U>(fn: (value: T) => U) => FluentResultAsync<U, E>;
21
34
  mapErr: <F>(fn: (error: E) => F) => FluentResultAsync<T, F>;
22
- flatMap: <U>(fn: (value: T) => ResultAsync<U, E>) => FluentResultAsync<U, E>;
23
- andThen: <U>(fn: (value: T) => ResultAsync<U, E>) => FluentResultAsync<U, E>;
24
- /** @deprecated Use {@link flatMap} or compat {@link andThen} instead. */
25
- chain: <U>(fn: (value: T) => ResultAsync<U, E>) => FluentResultAsync<U, E>;
26
- match: <U>(onOk: (value: T) => U, onErr: (error: E) => U) => Promise<U>;
35
+ flatMap: <U, F = E>(fn: (value: T) => ResultAsync<U, F> | Result<U, F>) => FluentResultAsync<U, E | F>;
36
+ andThen: <U, F = E>(fn: (value: T) => ResultAsync<U, F> | Result<U, F>) => FluentResultAsync<U, E | F>;
37
+ recover: <F>(fn: (error: E) => ResultAsync<T, F> | Result<T, F>) => FluentResultAsync<T, F>;
38
+ orElse: <F>(fn: (error: E) => ResultAsync<T, F> | Result<T, F>) => FluentResultAsync<T, F>;
39
+ tap: (fn: (value: T) => void) => FluentResultAsync<T, E>;
40
+ tapErr: (fn: (error: E) => void) => FluentResultAsync<T, E>;
41
+ match: <U1, U2 = U1>(onOk: (value: T) => U1, onErr: (error: E) => U2) => Promise<U1 | U2>;
42
+ unwrapOr: <U>(defaultValue: U) => Promise<T | U>;
43
+ resolve: () => Promise<Result<T, E>>;
27
44
  };
45
+ /**
46
+ * Wrap a {@link ResultAsync} for opt-in dot chaining — the async counterpart of
47
+ * {@link fluent}. App-edge sugar; await a terminal (`match`, `unwrapOr`,
48
+ * `resolve`) to leave the fluent world.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * fluentAsync(okAsync(2)).map((n) => n * 3); // FluentResultAsync<number, never>
53
+ * ```
54
+ */
28
55
  declare const fluentAsync: <T, E>(resultAsync: ResultAsync<T, E>) => FluentResultAsync<T, E>;
29
56
 
30
57
  export { type FluentResult, type FluentResultAsync, fluent, fluentAsync };
package/dist/fluent.d.ts CHANGED
@@ -1,30 +1,57 @@
1
- import { R as ResultAsync } from './async-DKISqIBc.js';
2
- import { R as Result } from './types-C2Dp1d5J.js';
1
+ import { R as ResultAsync } from './async-SZ7BEl0g.js';
2
+ import { R as Result } from './types-BQ9vv0nD.js';
3
3
 
4
4
  /** Opt-in dot chaining over a sync {@link Result} */
5
5
  type FluentResult<T, E> = {
6
6
  readonly result: Result<T, E>;
7
7
  map: <U>(fn: (value: T) => U) => FluentResult<U, E>;
8
8
  mapErr: <F>(fn: (error: E) => F) => FluentResult<T, F>;
9
- flatMap: <U>(fn: (value: T) => Result<U, E>) => FluentResult<U, E>;
10
- andThen: <U>(fn: (value: T) => Result<U, E>) => FluentResult<U, E>;
11
- /** @deprecated Use {@link flatMap} or compat {@link andThen} instead. */
12
- chain: <U>(fn: (value: T) => Result<U, E>) => FluentResult<U, E>;
9
+ bimap: <U, F>(onOk: (value: T) => U, onErr: (error: E) => F) => FluentResult<U, F>;
10
+ flatMap: <U, F = E>(fn: (value: T) => Result<U, F>) => FluentResult<U, E | F>;
11
+ andThen: <U, F = E>(fn: (value: T) => Result<U, F>) => FluentResult<U, E | F>;
12
+ recover: <F>(fn: (error: E) => Result<T, F>) => FluentResult<T, F>;
13
+ tap: (fn: (value: T) => void) => FluentResult<T, E>;
14
+ tapErr: (fn: (error: E) => void) => FluentResult<T, E>;
13
15
  match: <U>(onOk: (value: T) => U, onErr: (error: E) => U) => U;
14
16
  unwrapOr: (defaultValue: T) => T;
15
17
  };
18
+ /**
19
+ * Wrap a {@link Result} for opt-in dot chaining — app-edge sugar. Keep the
20
+ * data-first helpers ({@link map}, {@link flatMap}) in library internals.
21
+ *
22
+ * Mirrors every core transform; `test/parity.spec.ts` enforces the mirror.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * fluent(ok(2)).map((n) => n * 3).unwrapOr(0); // 6
27
+ * ```
28
+ */
16
29
  declare const fluent: <T, E>(result: Result<T, E>) => FluentResult<T, E>;
17
30
  /** Opt-in dot chaining over {@link ResultAsync} */
18
31
  type FluentResultAsync<T, E> = {
19
32
  readonly resultAsync: ResultAsync<T, E>;
20
33
  map: <U>(fn: (value: T) => U) => FluentResultAsync<U, E>;
21
34
  mapErr: <F>(fn: (error: E) => F) => FluentResultAsync<T, F>;
22
- flatMap: <U>(fn: (value: T) => ResultAsync<U, E>) => FluentResultAsync<U, E>;
23
- andThen: <U>(fn: (value: T) => ResultAsync<U, E>) => FluentResultAsync<U, E>;
24
- /** @deprecated Use {@link flatMap} or compat {@link andThen} instead. */
25
- chain: <U>(fn: (value: T) => ResultAsync<U, E>) => FluentResultAsync<U, E>;
26
- match: <U>(onOk: (value: T) => U, onErr: (error: E) => U) => Promise<U>;
35
+ flatMap: <U, F = E>(fn: (value: T) => ResultAsync<U, F> | Result<U, F>) => FluentResultAsync<U, E | F>;
36
+ andThen: <U, F = E>(fn: (value: T) => ResultAsync<U, F> | Result<U, F>) => FluentResultAsync<U, E | F>;
37
+ recover: <F>(fn: (error: E) => ResultAsync<T, F> | Result<T, F>) => FluentResultAsync<T, F>;
38
+ orElse: <F>(fn: (error: E) => ResultAsync<T, F> | Result<T, F>) => FluentResultAsync<T, F>;
39
+ tap: (fn: (value: T) => void) => FluentResultAsync<T, E>;
40
+ tapErr: (fn: (error: E) => void) => FluentResultAsync<T, E>;
41
+ match: <U1, U2 = U1>(onOk: (value: T) => U1, onErr: (error: E) => U2) => Promise<U1 | U2>;
42
+ unwrapOr: <U>(defaultValue: U) => Promise<T | U>;
43
+ resolve: () => Promise<Result<T, E>>;
27
44
  };
45
+ /**
46
+ * Wrap a {@link ResultAsync} for opt-in dot chaining — the async counterpart of
47
+ * {@link fluent}. App-edge sugar; await a terminal (`match`, `unwrapOr`,
48
+ * `resolve`) to leave the fluent world.
49
+ *
50
+ * @example
51
+ * ```ts
52
+ * fluentAsync(okAsync(2)).map((n) => n * 3); // FluentResultAsync<number, never>
53
+ * ```
54
+ */
28
55
  declare const fluentAsync: <T, E>(resultAsync: ResultAsync<T, E>) => FluentResultAsync<T, E>;
29
56
 
30
57
  export { type FluentResult, type FluentResultAsync, fluent, fluentAsync };
package/dist/fluent.js CHANGED
@@ -1,3 +1,10 @@
1
+ // src/internal/dual.ts
2
+ var dual = (arity, body) => {
3
+ const run = body;
4
+ const dispatcher = (...args) => args.length >= arity ? run(...args) : (self) => run(self, ...args);
5
+ return dispatcher;
6
+ };
7
+
1
8
  // src/result.ts
2
9
  var ok = (value) => ({
3
10
  _tag: "Ok",
@@ -9,41 +16,55 @@ var err = (error) => ({
9
16
  });
10
17
  var isOk = (result) => result._tag === "Ok";
11
18
  var isErr = (result) => result._tag === "Err";
12
- var mapImpl = (result, fn) => isOk(result) ? ok(fn(result.value)) : err(result.error);
13
- function map(...args) {
14
- if (args.length === 2) return mapImpl(args[0], args[1]);
15
- const fn = args[0];
16
- return (result) => mapImpl(result, fn);
17
- }
18
- var mapErrImpl = (result, fn) => isErr(result) ? err(fn(result.error)) : ok(result.value);
19
- function mapErr(...args) {
20
- if (args.length === 2) return mapErrImpl(args[0], args[1]);
21
- const fn = args[0];
22
- return (result) => mapErrImpl(result, fn);
23
- }
24
- var flatMapImpl = (result, fn) => isOk(result) ? fn(result.value) : err(result.error);
25
- function flatMap(...args) {
26
- if (args.length === 2) return flatMapImpl(args[0], args[1]);
27
- const fn = args[0];
28
- return (result) => flatMapImpl(result, fn);
29
- }
30
- var matchImpl = (result, onOk, onErr) => isOk(result) ? onOk(result.value) : onErr(result.error);
31
- function match(...args) {
32
- if (args.length === 3) return matchImpl(args[0], args[1], args[2]);
33
- const [onOk, onErr] = args;
34
- return (result) => matchImpl(result, onOk, onErr);
35
- }
36
- var unwrapOr = (result, defaultValue) => isOk(result) ? result.value : defaultValue;
19
+ var map = dual(
20
+ 2,
21
+ (result, fn) => isOk(result) ? ok(fn(result.value)) : err(result.error)
22
+ );
23
+ var mapErr = dual(
24
+ 2,
25
+ (result, fn) => isErr(result) ? err(fn(result.error)) : ok(result.value)
26
+ );
27
+ var bimap = dual(
28
+ 3,
29
+ (result, onOk, onErr) => isOk(result) ? ok(onOk(result.value)) : err(onErr(result.error))
30
+ );
31
+ var flatMap = dual(
32
+ 2,
33
+ (result, fn) => isOk(result) ? fn(result.value) : err(result.error)
34
+ );
35
+ var recover = dual(
36
+ 2,
37
+ (result, fn) => isErr(result) ? fn(result.error) : ok(result.value)
38
+ );
39
+ var tap = dual(2, (result, fn) => {
40
+ if (isOk(result)) fn(result.value);
41
+ return result;
42
+ });
43
+ var tapErr = dual(2, (result, fn) => {
44
+ if (isErr(result)) fn(result.error);
45
+ return result;
46
+ });
47
+ var match = dual(
48
+ 3,
49
+ (result, onOk, onErr) => isOk(result) ? onOk(result.value) : onErr(result.error)
50
+ );
51
+ var unwrapOr = dual(
52
+ 2,
53
+ (result, defaultValue) => isOk(result) ? result.value : defaultValue
54
+ );
37
55
 
38
56
  // src/fluent.ts
39
57
  var fluent = (result) => ({
40
58
  result,
41
59
  map: (fn) => fluent(map(result, fn)),
42
60
  mapErr: (fn) => fluent(mapErr(result, fn)),
61
+ bimap: (onOk, onErr) => fluent(bimap(result, onOk, onErr)),
43
62
  flatMap: (fn) => fluent(flatMap(result, fn)),
44
63
  andThen: (fn) => fluent(flatMap(result, fn)),
45
- chain: (fn) => fluent(flatMap(result, fn)),
46
- match: (onOk, onErr) => match(onOk, onErr)(result),
64
+ recover: (fn) => fluent(recover(result, fn)),
65
+ tap: (fn) => fluent(tap(result, fn)),
66
+ tapErr: (fn) => fluent(tapErr(result, fn)),
67
+ match: (onOk, onErr) => match(result, onOk, onErr),
47
68
  unwrapOr: (defaultValue) => unwrapOr(result, defaultValue)
48
69
  });
49
70
  var fluentAsync = (resultAsync) => ({
@@ -52,8 +73,13 @@ var fluentAsync = (resultAsync) => ({
52
73
  mapErr: (fn) => fluentAsync(resultAsync.mapErr(fn)),
53
74
  flatMap: (fn) => fluentAsync(resultAsync.flatMap(fn)),
54
75
  andThen: (fn) => fluentAsync(resultAsync.andThen(fn)),
55
- chain: (fn) => fluentAsync(resultAsync.chain(fn)),
56
- match: (onOk, onErr) => resultAsync.match(onOk, onErr)
76
+ recover: (fn) => fluentAsync(resultAsync.recover(fn)),
77
+ orElse: (fn) => fluentAsync(resultAsync.orElse(fn)),
78
+ tap: (fn) => fluentAsync(resultAsync.tap(fn)),
79
+ tapErr: (fn) => fluentAsync(resultAsync.tapErr(fn)),
80
+ match: (onOk, onErr) => resultAsync.match(onOk, onErr),
81
+ unwrapOr: (defaultValue) => resultAsync.unwrapOr(defaultValue),
82
+ resolve: () => resultAsync.resolve()
57
83
  });
58
84
 
59
85
  export { fluent, fluentAsync };