@onrails/result 0.1.3 → 0.2.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 (68) hide show
  1. package/DESIGN.md +2 -2
  2. package/README.md +0 -3
  3. package/RECIPES.md +16 -15
  4. package/dist/$.cjs +40 -0
  5. package/dist/$.cjs.map +1 -0
  6. package/dist/$.js +36 -0
  7. package/dist/$.js.map +1 -0
  8. package/dist/_.d.cts +2 -0
  9. package/dist/_.d.ts +2 -0
  10. package/dist/async-1fIhfzFu.d.ts +290 -0
  11. package/dist/async-u8U5P2EH.d.cts +290 -0
  12. package/dist/compat/neverthrow.cjs +234 -50
  13. package/dist/compat/neverthrow.cjs.map +1 -1
  14. package/dist/compat/neverthrow.d.cts +2 -2
  15. package/dist/compat/neverthrow.d.ts +2 -2
  16. package/dist/compat/neverthrow.js +231 -47
  17. package/dist/compat/neverthrow.js.map +1 -1
  18. package/dist/extra.cjs +1 -24
  19. package/dist/extra.cjs.map +1 -1
  20. package/dist/extra.d.cts +3 -9
  21. package/dist/extra.d.ts +3 -9
  22. package/dist/extra.js +2 -24
  23. package/dist/extra.js.map +1 -1
  24. package/dist/fluent.cjs +0 -2
  25. package/dist/fluent.cjs.map +1 -1
  26. package/dist/fluent.d.cts +2 -6
  27. package/dist/fluent.d.ts +2 -6
  28. package/dist/fluent.js +0 -2
  29. package/dist/fluent.js.map +1 -1
  30. package/dist/index.cjs +226 -47
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +174 -51
  33. package/dist/index.d.ts +174 -51
  34. package/dist/index.js +227 -44
  35. package/dist/index.js.map +1 -1
  36. package/dist/interop.cjs +209 -23
  37. package/dist/interop.cjs.map +1 -1
  38. package/dist/interop.d.cts +60 -10
  39. package/dist/interop.d.ts +60 -10
  40. package/dist/interop.js +209 -23
  41. package/dist/interop.js.map +1 -1
  42. package/dist/pipe.cjs.map +1 -1
  43. package/dist/pipe.d.cts +7 -5
  44. package/dist/pipe.d.ts +7 -5
  45. package/dist/pipe.js.map +1 -1
  46. package/dist/railway.cjs +205 -24
  47. package/dist/railway.cjs.map +1 -1
  48. package/dist/railway.d.cts +20 -3
  49. package/dist/railway.d.ts +20 -3
  50. package/dist/railway.js +205 -25
  51. package/dist/railway.js.map +1 -1
  52. package/dist/try-gen.cjs.map +1 -1
  53. package/dist/try-gen.d.cts +29 -3
  54. package/dist/try-gen.d.ts +29 -3
  55. package/dist/try-gen.js.map +1 -1
  56. package/dist/types-BQ9vv0nD.d.cts +55 -0
  57. package/dist/types-BQ9vv0nD.d.ts +55 -0
  58. package/dist/validation.cjs.map +1 -1
  59. package/dist/validation.d.cts +1 -1
  60. package/dist/validation.d.ts +1 -1
  61. package/dist/validation.js.map +1 -1
  62. package/package.json +10 -3
  63. package/skills/railway-do-notation/SKILL.md +436 -0
  64. package/skills/result-composition/SKILL.md +325 -0
  65. package/dist/async-CH8Cs8KE.d.cts +0 -161
  66. package/dist/async-DKISqIBc.d.ts +0 -161
  67. package/dist/types-C2Dp1d5J.d.cts +0 -21
  68. package/dist/types-C2Dp1d5J.d.ts +0 -21
package/dist/index.d.ts CHANGED
@@ -1,10 +1,123 @@
1
- export { R as ResultAsync, e as errAsync, f as fromPromise, a as fromSafePromise, o as okAsync, p as parallelTupleAsync, s as sequenceTupleAsync, t as tryAsync } from './async-DKISqIBc.js';
1
+ import { R as ResultAsync } from './async-1fIhfzFu.js';
2
+ import { R as Result, E as Err, O as Ok } from './types-BQ9vv0nD.js';
3
+ export { U as UnexpectedError } from './types-BQ9vv0nD.js';
2
4
  export { InferErr, InferOk, asyncAfter, fromAsync, fromResult } from './interop.js';
3
5
  export { flow } from './pipe.js';
4
- import { R as Result, E as Err, O as Ok } from './types-C2Dp1d5J.js';
5
- export { U as UnexpectedError } from './types-C2Dp1d5J.js';
6
6
  export { $, tryGen, yieldResult } from './try-gen.js';
7
7
 
8
+ /**
9
+ * Lifts a value into an `Ok` async result.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * const r = okAsync(42); // ResultAsync<number, never>
14
+ * ```
15
+ */
16
+ declare const okAsync: typeof ResultAsync.ok;
17
+ /**
18
+ * Lifts a value into an `Err` async result.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * const r = errAsync({ kind: "not_found" as const });
23
+ * ```
24
+ */
25
+ declare const errAsync: typeof ResultAsync.err;
26
+ /**
27
+ * Wraps a `PromiseLike<T>` into a {@link ResultAsync}. Reject reasons go
28
+ * through `onReject` to become typed `Err`s; success becomes `Ok<T>`.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * const body = fromPromise(
33
+ * fetch(url).then((r) => r.text()),
34
+ * (e): NetworkError => ({ kind: "network", cause: String(e) }),
35
+ * );
36
+ * ```
37
+ */
38
+ declare const fromPromise: typeof ResultAsync.fromPromise;
39
+ /**
40
+ * Wraps a `PromiseLike<T>` that **never rejects** into {@link ResultAsync}.
41
+ * Skips the `onReject` mapper. Use only when the promise is provably safe.
42
+ */
43
+ declare const fromSafePromise: typeof ResultAsync.fromSafePromise;
44
+ /**
45
+ * Heterogeneous async tuple combine — branches overlap in wall-clock time.
46
+ * Returns the first `Err` in **input** order (not completion order).
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * const combined = parallelTupleAsync([
51
+ * loadProfile(id),
52
+ * loadMetrics(id),
53
+ * loadFlags(id),
54
+ * ] as const);
55
+ * // ResultAsync<readonly [Profile, Metrics, Flags], …>
56
+ * ```
57
+ */
58
+ declare const parallelTupleAsync: typeof ResultAsync.combineTupleParallel;
59
+ /**
60
+ * Convenience wrapper over {@link fromPromise} with default `Error`
61
+ * normalization. Call without `onReject` to get `ResultAsync<T, Error>`,
62
+ * or pass a custom mapper for a typed error.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * // Default: rejection → Err(Error)
67
+ * const a = tryAsync(db.users.insert(row));
68
+ *
69
+ * // Custom: typed error
70
+ * const b = tryAsync(db.users.insert(row), (e): DbError => ({
71
+ * kind: "db",
72
+ * cause: e,
73
+ * }));
74
+ * ```
75
+ */
76
+ declare function tryAsync<T>(promise: PromiseLike<T>): ResultAsync<T, Error>;
77
+ declare function tryAsync<T, E>(promise: PromiseLike<T>, onReject: (error: unknown) => E): ResultAsync<T, E>;
78
+
79
+ /**
80
+ * Combines a homogeneous array of results into a single result holding an
81
+ * array of the `Ok` values, in input order. Short-circuits on the first `Err`
82
+ * (first failure wins). For heterogeneous tuples that preserve per-index
83
+ * types, use {@link combineTuple}.
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * combine([ok(1), ok(2)]); // Ok([1, 2])
88
+ * combine([ok(1), err("e")]); // Err("e")
89
+ * ```
90
+ */
91
+ declare const combine: <T, E>(results: readonly Result<T, E>[]) => Result<T[], E>;
92
+ /**
93
+ * Heterogeneous tuple combine — like {@link combine} but preserves each
94
+ * branch's `Ok` type by position, so the result destructures type-safely.
95
+ * Short-circuits on the first `Err` in input order (neverthrow-style).
96
+ *
97
+ * @example
98
+ * ```ts
99
+ * const r = combineTuple([ok(1), ok("x")] as const);
100
+ * // Result<readonly [number, string], never>
101
+ * if (isOk(r)) {
102
+ * const [n, s] = r.value; // typed per position
103
+ * }
104
+ * ```
105
+ */
106
+ declare const combineTuple: <const R extends readonly Result<unknown, unknown>[]>(results: R) => CombineTuple<R>;
107
+ type _OkValue<R> = R extends {
108
+ _tag: "Ok";
109
+ readonly value: infer T;
110
+ } ? T : never;
111
+ type _ErrValue<R> = R extends {
112
+ _tag: "Err";
113
+ readonly error: infer E;
114
+ } ? E : never;
115
+ type CombineTuple<R extends readonly Result<unknown, unknown>[]> = Result<{
116
+ [K in keyof R]: _OkValue<R[K]>;
117
+ }, {
118
+ [K in keyof R]: _ErrValue<R[K]>;
119
+ }[number]>;
120
+
8
121
  /**
9
122
  * Lifts a value into the success track.
10
123
  *
@@ -47,11 +160,6 @@ declare const isOk: <T, E>(result: Result<T, E>) => result is Ok<T, E>;
47
160
  * ```
48
161
  */
49
162
  declare const isErr: <T, E>(result: Result<T, E>) => result is Err<T, E>;
50
- /**
51
- * Fantasy Land `of` — alias of {@link ok}.
52
- * @deprecated Use {@link ok} instead.
53
- */
54
- declare const of: <T, E = never>(value: T) => Result<T, E>;
55
163
  /**
56
164
  * Transform the `Ok` value, passing `Err` through unchanged. Dual-form:
57
165
  * call data-first or curried (for use with {@link pipe}).
@@ -152,8 +260,9 @@ declare function tapErr<E>(fn: (error: E) => void): <T>(result: Result<T, E>) =>
152
260
  * 3-args data-first, 2-args curried for {@link pipe}. Returns whatever
153
261
  * the handlers return.
154
262
  *
155
- * For files that also import `match` from `ts-pattern`, the
156
- * collision-free alias {@link matchResult} is identical.
263
+ * For files that also import `match` from `ts-pattern`, use a namespace
264
+ * import (`import * as R from "@onrails/result"` → `R.match`) to dissolve
265
+ * the collision.
157
266
  *
158
267
  * @example
159
268
  * ```ts
@@ -162,21 +271,6 @@ declare function tapErr<E>(fn: (error: E) => void): <T>(result: Result<T, E>) =>
162
271
  */
163
272
  declare function match<T, E, U>(result: Result<T, E>, onOk: (value: T) => U, onErr: (error: E) => U): U;
164
273
  declare function match<T, E, U>(onOk: (value: T) => U, onErr: (error: E) => U): (result: Result<T, E>) => U;
165
- /**
166
- * Collision-free alias for files that also import `match` from ts-pattern.
167
- * @deprecated Namespace import carriers (e.g., `import * as R from "@onrails/result"`) and use canonical {@link match} instead.
168
- */
169
- declare const matchResult: typeof match;
170
- /**
171
- * Curried collapse with named slots — escape valve when positional `match`
172
- * order is unclear at the call site (e.g. when both handlers return the same
173
- * type and a transposed `match(r, onErr, onOk)` would silently compile).
174
- * @deprecated Use curried {@link match}(onOk, onErr)(result) instead.
175
- */
176
- declare const fold: <T, E, U>(handlers: {
177
- readonly ok: (value: T) => U;
178
- readonly err: (error: E) => U;
179
- }) => (result: Result<T, E>) => U;
180
274
  /**
181
275
  * Returns the `Ok` value, or `defaultValue` when the result is `Err`.
182
276
  *
@@ -187,46 +281,75 @@ declare const fold: <T, E, U>(handlers: {
187
281
  */
188
282
  declare const unwrapOr: <T, E>(result: Result<T, E>, defaultValue: T) => T;
189
283
  /**
190
- * Test/assert helper — throws the original Err value when called on Err.
284
+ * Test/assert helper — returns the `Ok` value, or **throws the original `Err`
285
+ * value** when called on an `Err`. This is the assertion tier (RFC 0001 §4):
286
+ * intended for `*.spec.ts` / `*.test.ts`, where throwing fails the test loudly.
287
+ * In business logic prefer {@link match} or {@link unwrapOr}; the lint plugins
288
+ * flag `unwrapOk` outside test files.
289
+ *
290
+ * @returns the unwrapped `Ok` value
291
+ * @throws the carried `Err` value when the result is `Err`
191
292
  *
192
- * @throws when the result is Err — assertion-tier; use {@link match} / {@link unwrapOr} in business logic.
193
- * Allowed in `*.spec.ts` / `*.test.ts`; flagged elsewhere by the plugins.
293
+ * @example
294
+ * ```ts
295
+ * // in a *.spec.ts
296
+ * expect(unwrapOk(ok(5))).toBe(5);
297
+ * expect(() => unwrapOk(err(error))).toThrow(error);
298
+ * ```
194
299
  */
195
300
  declare function unwrapOk<T, E>(result: Result<T, E>): T;
196
301
  /**
197
- * Test/assert helper — throws TypeError when called on Ok.
302
+ * Test/assert helper — returns the `Err` value, or **throws a `TypeError`**
303
+ * when called on an `Ok`. Mirror of {@link unwrapOk} on the error track and
304
+ * part of the same assertion tier (RFC 0001 §4): intended for `*.spec.ts` /
305
+ * `*.test.ts`. Prefer {@link match} / {@link unwrapOr} in business logic; the
306
+ * lint plugins flag `unwrapErr` outside test files.
198
307
  *
199
- * @throws when the result is Ok — assertion-tier; allowed in `*.spec.ts` / `*.test.ts`; flagged elsewhere by the plugins.
308
+ * @returns the unwrapped `Err` value
309
+ * @throws `TypeError` when the result is `Ok`
310
+ *
311
+ * @example
312
+ * ```ts
313
+ * // in a *.spec.ts
314
+ * expect(unwrapErr(err("x"))).toBe("x");
315
+ * expect(() => unwrapErr(ok(5))).toThrow(TypeError);
316
+ * ```
200
317
  */
201
318
  declare function unwrapErr<T, E>(result: Result<T, E>): E;
202
- /** Wrap a throwing sync function — neverthrow `Result.fromThrowable` */
319
+ /**
320
+ * Wraps a throwing sync function, returning a function that produces a
321
+ * {@link Result} instead of throwing. Thrown errors pass through `onThrow` to
322
+ * become a typed `Err`; a normal return becomes `Ok`. The neverthrow analogue
323
+ * is `Result.fromThrowable`.
324
+ *
325
+ * @param fn - the throwing function to wrap
326
+ * @param onThrow - maps a thrown value to the `Err` channel
327
+ * @returns a function with `fn`'s parameters that returns `Result<ReturnType, E>`
328
+ *
329
+ * @example
330
+ * ```ts
331
+ * type ParseError = { kind: "parse"; message: string };
332
+ * const parse = trySync(
333
+ * JSON.parse,
334
+ * (e): ParseError => ({ kind: "parse", message: String(e) }),
335
+ * );
336
+ * parse("{}"); // Ok({})
337
+ * parse("nope"); // Err({ kind: "parse", … })
338
+ * ```
339
+ */
203
340
  declare function trySync<A extends readonly unknown[], T, E>(fn: (...args: A) => T, onThrow: (error: unknown) => E): (...args: A) => Result<T, E>;
204
341
  declare function trySync<F extends (...args: never) => unknown, E>(fn: F, onThrow: (error: unknown) => E): (...args: Parameters<F>) => Result<ReturnType<F>, E>;
205
- /** First failure wins; otherwise collects values in order */
206
- declare const combine: <T, E>(results: readonly Result<T, E>[]) => Result<T[], E>;
207
- /** Tuple-preserving combine (neverthrow-style) */
208
- declare const combineTuple: <const R extends readonly Result<unknown, unknown>[]>(results: R) => CombineTuple<R>;
209
- type _OkValue<R> = R extends {
210
- _tag: "Ok";
211
- readonly value: infer T;
212
- } ? T : never;
213
- type _ErrValue<R> = R extends {
214
- _tag: "Err";
215
- readonly error: infer E;
216
- } ? E : never;
217
- type CombineTuple<R extends readonly Result<unknown, unknown>[]> = Result<{
218
- [K in keyof R]: _OkValue<R[K]>;
219
- }, {
220
- [K in keyof R]: _ErrValue<R[K]>;
221
- }[number]>;
222
342
  /**
223
- * Variadic value-first pipe — threads `value` through up to nine unary fns.
343
+ * Variadic value-first pipe — threads `value` through up to nine unary fns,
344
+ * left-to-right. Use {@link pipe} when you already have a starting value;
345
+ * use {@link flow} to define a reusable composed function with no value yet.
224
346
  *
347
+ * @example
225
348
  * ```ts
226
349
  * pipe(
227
350
  * parseConfig(raw),
228
351
  * map((cfg) => cfg.name),
229
- * flatMap((name) => name ? ok(name) : err({ kind: "empty" })),
352
+ * flatMap((name) => (name ? ok(name) : err({ kind: "empty" as const }))),
230
353
  * tap(log),
231
354
  * );
232
355
  * ```
@@ -241,4 +364,4 @@ declare function pipe<A, B, C, D, E, F, G>(value: A, ab: (a: A) => B, bc: (b: B)
241
364
  declare function pipe<A, B, C, D, E, F, G, H>(value: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H): H;
242
365
  declare function pipe<A, B, C, D, E, F, G, H, I>(value: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D, de: (d: D) => E, ef: (e: E) => F, fg: (f: F) => G, gh: (g: G) => H, hi: (h: H) => I): I;
243
366
 
244
- export { Err, Ok, Result, bimap, combine, combineTuple, err, flatMap, fold, isErr, isOk, map, mapErr, match, matchResult, of, ok, pipe, recover, tap, tapErr, trySync, unwrapErr, unwrapOk, unwrapOr };
367
+ export { Err, Ok, Result, ResultAsync, bimap, combine, combineTuple, err, errAsync, flatMap, fromPromise, fromSafePromise, isErr, isOk, map, mapErr, match, ok, okAsync, parallelTupleAsync, pipe, recover, tap, tapErr, tryAsync, trySync, unwrapErr, unwrapOk, unwrapOr };
package/dist/index.js CHANGED
@@ -9,7 +9,6 @@ var err = (error) => ({
9
9
  });
10
10
  var isOk = (result) => result._tag === "Ok";
11
11
  var isErr = (result) => result._tag === "Err";
12
- var of = ok;
13
12
  var mapImpl = (result, fn) => isOk(result) ? ok(fn(result.value)) : err(result.error);
14
13
  function map(...args) {
15
14
  if (args.length === 2) return mapImpl(args[0], args[1]);
@@ -64,8 +63,6 @@ function match(...args) {
64
63
  const [onOk, onErr] = args;
65
64
  return (result) => matchImpl(result, onOk, onErr);
66
65
  }
67
- var matchResult = match;
68
- var fold = (handlers) => (result) => matchImpl(result, handlers.ok, handlers.err);
69
66
  var unwrapOr = (result, defaultValue) => isOk(result) ? result.value : defaultValue;
70
67
  function unwrapOk(result) {
71
68
  if (isErr(result)) throw result.error;
@@ -84,18 +81,6 @@ function trySync(fn, onThrow) {
84
81
  }
85
82
  };
86
83
  }
87
- var combine = (results) => {
88
- const values = [];
89
- for (const result of results) {
90
- if (isErr(result)) return err(result.error);
91
- values.push(result.value);
92
- }
93
- return ok(values);
94
- };
95
- var combineTuple = (results) => (
96
- // Runtime identical to combine; the cast restores per-index tuple types.
97
- combine(results)
98
- );
99
84
  function pipe(value, ...fns) {
100
85
  let acc = value;
101
86
  for (const fn of fns) {
@@ -142,9 +127,32 @@ var ResultAsync = class _ResultAsync {
142
127
  }
143
128
  run;
144
129
  promise = null;
130
+ /**
131
+ * Lifts an already-settled sync {@link Result} into a {@link ResultAsync}.
132
+ *
133
+ * @example
134
+ * ```ts
135
+ * const ra = ResultAsync.fromResult(ok(42)); // ResultAsync<number, never>
136
+ * ```
137
+ */
145
138
  static fromResult(result) {
146
139
  return new _ResultAsync(async () => result);
147
140
  }
141
+ /**
142
+ * Wraps a `PromiseLike<T>` that may reject. Rejections pass through `onReject`
143
+ * to become a typed `Err`; resolution becomes `Ok<T>`.
144
+ *
145
+ * @param promise - the promise to wrap
146
+ * @param onReject - maps a rejection reason to the `Err` channel
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * const body = ResultAsync.fromPromise(
151
+ * fetch(url).then((r) => r.text()),
152
+ * (e): NetError => ({ kind: "net", cause: String(e) }),
153
+ * );
154
+ * ```
155
+ */
148
156
  static fromPromise(promise, onReject) {
149
157
  return new _ResultAsync(async () => {
150
158
  try {
@@ -154,12 +162,29 @@ var ResultAsync = class _ResultAsync {
154
162
  }
155
163
  });
156
164
  }
165
+ /**
166
+ * Wraps a `PromiseLike<T>` that is **guaranteed not to reject**, skipping the
167
+ * `onReject` mapper. Use only when rejection is provably impossible.
168
+ *
169
+ * @example
170
+ * ```ts
171
+ * const now = ResultAsync.fromSafePromise(Promise.resolve(Date.now()));
172
+ * ```
173
+ */
157
174
  static fromSafePromise(promise) {
158
175
  return new _ResultAsync(async () => ok(await promise));
159
176
  }
160
177
  /**
161
- * Defers work until {@link resolve}. Unlike {@link fromPromise}, nothing runs
162
- * until the `ResultAsync` is resolved (e.g. by `combineTuple` / `combineTupleParallel`).
178
+ * Defers work until {@link resolve}. Unlike {@link fromPromise}, the factory
179
+ * does not run until the `ResultAsync` is resolved (e.g. by `combineTuple` /
180
+ * `combineTupleParallel`). The factory runs at most once — `resolve` memoizes.
181
+ *
182
+ * @example
183
+ * ```ts
184
+ * const insert = ResultAsync.defer(() => db.orders.insert(row));
185
+ * // nothing has run yet
186
+ * const r = await insert; // factory runs exactly once here
187
+ * ```
163
188
  */
164
189
  static defer(fn) {
165
190
  return new _ResultAsync(fn);
@@ -167,14 +192,42 @@ var ResultAsync = class _ResultAsync {
167
192
  static ok(value) {
168
193
  return new _ResultAsync(async () => ok(value));
169
194
  }
195
+ /**
196
+ * Lifts an error into an `Err` async result.
197
+ *
198
+ * @example
199
+ * ```ts
200
+ * const r = ResultAsync.err({ kind: "not_found" as const });
201
+ * ```
202
+ */
170
203
  static err(error) {
171
204
  return new _ResultAsync(async () => err(error));
172
205
  }
173
- /** @see {@link fromAsync} from `@onrails/result/interop` */
206
+ /**
207
+ * Lifts an existing `Promise<Result<T, E>>` (e.g. from interop code) into a
208
+ * {@link ResultAsync}, auto-flattening one level of double-wrapping. A thrown
209
+ * defect is routed through `onDefect`, defaulting to {@link UnexpectedError}.
210
+ *
211
+ * @param promise - a promise that already yields a `Result`
212
+ * @param onDefect - maps an unexpected throw to the `Err` channel
213
+ * @see {@link fromAsync} from `@onrails/result/interop`
214
+ */
174
215
  static fromResultPromise(promise, onDefect) {
175
216
  const mapDefect = onDefect ?? ((error) => new UnexpectedError("Unexpected async defect", error));
176
217
  return new _ResultAsync(() => liftPromiseResult(promise, mapDefect));
177
218
  }
219
+ /**
220
+ * Combines a homogeneous array of async results into one. Resolves
221
+ * **sequentially** in input order, short-circuiting on the first `Err`.
222
+ * For heterogeneous tuples that preserve per-index types, use
223
+ * {@link combineTuple}; for wall-clock overlap, {@link combineTupleParallel}.
224
+ *
225
+ * @example
226
+ * ```ts
227
+ * const all = ResultAsync.combine([loadA(), loadB(), loadC()]);
228
+ * // ResultAsync<Item[], LoadError>
229
+ * ```
230
+ */
178
231
  static combine(results) {
179
232
  return new _ResultAsync(async () => {
180
233
  const values = [];
@@ -188,6 +241,23 @@ var ResultAsync = class _ResultAsync {
188
241
  return ok(values);
189
242
  });
190
243
  }
244
+ /**
245
+ * Heterogeneous async tuple combine — resolves branches **sequentially**
246
+ * (left-to-right), returning the first `Err` in input order. Preserves each
247
+ * branch's `Ok` type by position, so the result destructures type-safely.
248
+ * This is the canonical sequential async combine (replaces the former
249
+ * `sequenceTupleAsync`).
250
+ *
251
+ * @example
252
+ * ```ts
253
+ * const combined = ResultAsync.combineTuple([loadCfg(), loadCatalog()] as const);
254
+ * // ResultAsync<readonly [Cfg, Catalog], CfgError | CatalogError>
255
+ * const r = await combined;
256
+ * if (isOk(r)) {
257
+ * const [cfg, catalog] = r.value; // typed per position
258
+ * }
259
+ * ```
260
+ */
191
261
  static combineTuple(results) {
192
262
  return _ResultAsync.combine(
193
263
  results
@@ -196,18 +266,55 @@ var ResultAsync = class _ResultAsync {
196
266
  /**
197
267
  * Like {@link combineTuple}, but starts every branch before awaiting (wall-clock
198
268
  * parallel for independent IO). On failure, returns the first `Err` in input order.
269
+ *
270
+ * @example
271
+ * ```ts
272
+ * // independent IO — overlap them
273
+ * const combined = ResultAsync.combineTupleParallel([
274
+ * loadProfile(id),
275
+ * loadMetrics(id),
276
+ * ] as const);
277
+ * ```
199
278
  */
200
279
  static combineTupleParallel(results) {
201
280
  return new _ResultAsync(
202
281
  async () => sequenceSettled(await Promise.all(results.map((ra) => ra.resolve())))
203
282
  );
204
283
  }
284
+ /**
285
+ * Transforms the `Ok` value, passing `Err` through unchanged.
286
+ *
287
+ * @example
288
+ * ```ts
289
+ * ResultAsync.ok(2).map((n) => n * 3); // ResultAsync<number> → Ok 6
290
+ * ```
291
+ */
205
292
  map(fn) {
206
293
  return new _ResultAsync(async () => map(await this.resolve(), fn));
207
294
  }
295
+ /**
296
+ * Transforms the `Err` value, passing `Ok` through unchanged — useful for
297
+ * unifying heterogeneous failures into one app-level union.
298
+ *
299
+ * @example
300
+ * ```ts
301
+ * load(id).mapErr((e): AppError => ({ kind: "load", cause: e }));
302
+ * ```
303
+ */
208
304
  mapErr(fn) {
209
305
  return new _ResultAsync(async () => mapErr(await this.resolve(), fn));
210
306
  }
307
+ /**
308
+ * Canonical bind — chains a step that itself returns a `ResultAsync` or sync
309
+ * `Result`, short-circuiting on `Err`. Error types accumulate (`E | F`).
310
+ *
311
+ * @example
312
+ * ```ts
313
+ * authenticate(req)
314
+ * .flatMap((user) => loadProfile(user.id)) // ResultAsync
315
+ * .flatMap((p) => validate(p)); // sync Result also accepted
316
+ * ```
317
+ */
211
318
  flatMap(fn) {
212
319
  return new _ResultAsync(async () => {
213
320
  const first = await this.resolve();
@@ -224,16 +331,28 @@ var ResultAsync = class _ResultAsync {
224
331
  return isCompatLike(next) ? next.inner : next;
225
332
  });
226
333
  }
334
+ /**
335
+ * neverthrow-compat alias of {@link flatMap}. Kept as the documented compat
336
+ * tier; prefer `flatMap` in new code.
337
+ *
338
+ * @example
339
+ * ```ts
340
+ * authenticate(req).andThen((user) => loadProfile(user.id));
341
+ * ```
342
+ */
227
343
  andThen(fn) {
228
344
  return this.flatMap(fn);
229
345
  }
230
346
  /**
231
- * Alias for {@link flatMap}.
232
- * @deprecated Use {@link flatMap} or keep/use compat {@link andThen} instead.
347
+ * Error-channel bind — runs `fn` only on `Err`, swapping in a recovery
348
+ * `ResultAsync` or sync `Result`. `Ok` passes through. The mirror of
349
+ * {@link flatMap} on the error track.
350
+ *
351
+ * @example
352
+ * ```ts
353
+ * loadFromCache(id).recover(() => loadFromOrigin(id));
354
+ * ```
233
355
  */
234
- chain(fn) {
235
- return this.flatMap(fn);
236
- }
237
356
  recover(fn) {
238
357
  return new _ResultAsync(async () => {
239
358
  const first = await this.resolve();
@@ -244,9 +363,27 @@ var ResultAsync = class _ResultAsync {
244
363
  return next instanceof _ResultAsync ? next.resolve() : next;
245
364
  });
246
365
  }
366
+ /**
367
+ * neverthrow-compat alias of {@link recover}. Kept as the documented compat
368
+ * tier; prefer `recover` in new code.
369
+ *
370
+ * @example
371
+ * ```ts
372
+ * loadFromCache(id).orElse(() => loadFromOrigin(id));
373
+ * ```
374
+ */
247
375
  orElse(fn) {
248
376
  return this.recover(fn);
249
377
  }
378
+ /**
379
+ * Runs a side effect on the `Ok` value and passes the result through
380
+ * unchanged; a no-op on `Err`. Mirrors {@link tapErr}.
381
+ *
382
+ * @example
383
+ * ```ts
384
+ * saveUser(u).tap((saved) => analytics.track("user_saved", saved.id));
385
+ * ```
386
+ */
250
387
  tap(fn) {
251
388
  return new _ResultAsync(async () => {
252
389
  const result = await this.resolve();
@@ -256,6 +393,15 @@ var ResultAsync = class _ResultAsync {
256
393
  return result;
257
394
  });
258
395
  }
396
+ /**
397
+ * Runs a side effect on the `Err` value and passes the result through
398
+ * unchanged; a no-op on `Ok`. The error-track mirror of {@link tap}.
399
+ *
400
+ * @example
401
+ * ```ts
402
+ * saveUser(u).tapErr((e) => logger.warn("save failed", e));
403
+ * ```
404
+ */
259
405
  tapErr(fn) {
260
406
  return new _ResultAsync(async () => {
261
407
  const result = await this.resolve();
@@ -265,28 +411,49 @@ var ResultAsync = class _ResultAsync {
265
411
  return result;
266
412
  });
267
413
  }
268
- unwrapOr(defaultValue) {
269
- return this.resolve().then((result) => isErr(result) ? defaultValue : result.value);
270
- }
271
414
  /**
272
- * @deprecated Await the ResultAsync and narrow with {@link isOk} — this method
273
- * re-executes deferred factories and cannot narrow.
415
+ * Settles to the `Ok` value, or `defaultValue` if `Err`. Terminal — returns a
416
+ * plain `Promise`, not a `ResultAsync`.
417
+ *
418
+ * @example
419
+ * ```ts
420
+ * const profile = await loadProfile(id).unwrapOr(guestProfile);
421
+ * ```
274
422
  */
275
- isOk() {
276
- return this.resolve().then((result) => !isErr(result));
423
+ unwrapOr(defaultValue) {
424
+ return this.resolve().then((result) => isErr(result) ? defaultValue : result.value);
277
425
  }
278
426
  /**
279
- * @deprecated Await the ResultAsync and narrow with {@link isErr} — this method
280
- * re-executes deferred factories and cannot narrow.
427
+ * Terminal collapse — folds both tracks into a single awaited value. Returns
428
+ * a plain `Promise`, settling the carrier exactly once.
429
+ *
430
+ * @param onOk - handles the `Ok` value
431
+ * @param onErr - handles the `Err` value
432
+ *
433
+ * @example
434
+ * ```ts
435
+ * const status = await save(row).match(
436
+ * () => 200,
437
+ * (e) => (e.kind === "conflict" ? 409 : 500),
438
+ * );
439
+ * ```
281
440
  */
282
- isErr() {
283
- return this.resolve().then((result) => isErr(result));
284
- }
285
441
  match(onOk, onErr) {
286
442
  return this.resolve().then(
287
443
  (result) => isErr(result) ? onErr(result.error) : onOk(result.value)
288
444
  );
289
445
  }
446
+ /**
447
+ * Settles the carrier to a bare sync {@link Result}, memoizing so the
448
+ * underlying factory runs at most once. Prefer `await ra` (the thenable) or
449
+ * {@link match} in app code; use `resolve` when you need the tagged union back.
450
+ *
451
+ * @example
452
+ * ```ts
453
+ * const r = await load(id).resolve(); // Result<Data, LoadError>
454
+ * if (isOk(r)) use(r.value);
455
+ * ```
456
+ */
290
457
  resolve() {
291
458
  if (!this.promise) {
292
459
  this.promise = this.run();
@@ -312,26 +479,42 @@ function isResultLike(v) {
312
479
  function isCompatLike(v) {
313
480
  return typeof v === "object" && v !== null && "inner" in v && isResultLike(v.inner);
314
481
  }
482
+
483
+ // src/async-lift.ts
315
484
  var okAsync = ResultAsync.ok;
316
485
  var errAsync = ResultAsync.err;
317
486
  var fromPromise = ResultAsync.fromPromise;
318
487
  var fromSafePromise = ResultAsync.fromSafePromise;
319
- var sequenceTupleAsync = ResultAsync.combineTuple;
320
488
  var parallelTupleAsync = ResultAsync.combineTupleParallel;
321
489
  var toError = (error) => error instanceof Error ? error : new Error(String(error));
322
490
  function tryAsync(promise, onReject) {
323
- if (onReject) {
324
- return ResultAsync.fromPromise(promise, onReject);
325
- }
326
- return ResultAsync.fromPromise(promise, toError);
491
+ return onReject ? ResultAsync.fromPromise(promise, onReject) : ResultAsync.fromPromise(promise, toError);
327
492
  }
328
493
 
494
+ // src/collections.ts
495
+ var combine = (results) => {
496
+ const values = [];
497
+ for (const result of results) {
498
+ if (isErr(result)) return err(result.error);
499
+ values.push(result.value);
500
+ }
501
+ return ok(values);
502
+ };
503
+ var combineTuple = (results) => (
504
+ // Runtime identical to combine; the cast restores per-index tuple types.
505
+ combine(results)
506
+ );
507
+
329
508
  // src/interop.ts
330
509
  var fromResult = (result) => ResultAsync.fromResult(result);
331
- var asyncAfter = (result, fn) => fromResult(result).flatMap(fn);
332
- function fromAsync(fn, onDefect) {
333
- return (...args) => ResultAsync.fromResultPromise(fn(...args), onDefect);
510
+ function asyncAfter(resultOrFn, fn) {
511
+ if (fn === void 0) {
512
+ const f = resultOrFn;
513
+ return (result) => fromResult(result).flatMap(f);
514
+ }
515
+ return fromResult(resultOrFn).flatMap(fn);
334
516
  }
517
+ var fromAsync = (fn, onDefect) => (...args) => ResultAsync.fromResultPromise(fn(...args), onDefect);
335
518
 
336
519
  // src/pipe.ts
337
520
  function flow(first, ...rest) {
@@ -370,6 +553,6 @@ var tryGen = (fn) => {
370
553
  }
371
554
  };
372
555
 
373
- export { $, ResultAsync, asyncAfter, bimap, combine, combineTuple, err, errAsync, flatMap, flow, fold, fromAsync, fromPromise, fromResult, fromSafePromise, isErr, isOk, map, mapErr, match, matchResult, of, ok, okAsync, parallelTupleAsync, pipe, recover, sequenceTupleAsync, tap, tapErr, tryAsync, tryGen, trySync, unwrapErr, unwrapOk, unwrapOr, yieldResult };
556
+ export { $, ResultAsync, asyncAfter, bimap, combine, combineTuple, err, errAsync, flatMap, flow, fromAsync, fromPromise, fromResult, fromSafePromise, isErr, isOk, map, mapErr, match, ok, okAsync, parallelTupleAsync, pipe, recover, tap, tapErr, tryAsync, tryGen, trySync, unwrapErr, unwrapOk, unwrapOr, yieldResult };
374
557
  //# sourceMappingURL=index.js.map
375
558
  //# sourceMappingURL=index.js.map