@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/index.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,68 +18,47 @@ var err = (error) => ({
11
18
  });
12
19
  var isOk = (result) => result._tag === "Ok";
13
20
  var isErr = (result) => result._tag === "Err";
14
- var of = ok;
15
- var mapImpl = (result, fn) => isOk(result) ? ok(fn(result.value)) : err(result.error);
16
- function map(...args) {
17
- if (args.length === 2) return mapImpl(args[0], args[1]);
18
- const fn = args[0];
19
- return (result) => mapImpl(result, fn);
20
- }
21
- var mapErrImpl = (result, fn) => isErr(result) ? err(fn(result.error)) : ok(result.value);
22
- function mapErr(...args) {
23
- if (args.length === 2) return mapErrImpl(args[0], args[1]);
24
- const fn = args[0];
25
- return (result) => mapErrImpl(result, fn);
26
- }
27
- var bimapImpl = (result, onOk, onErr) => isOk(result) ? ok(onOk(result.value)) : err(onErr(result.error));
28
- function bimap(...args) {
29
- if (args.length === 3) return bimapImpl(args[0], args[1], args[2]);
30
- const [onOk, onErr] = args;
31
- return (result) => bimapImpl(result, onOk, onErr);
32
- }
33
- var flatMapImpl = (result, fn) => isOk(result) ? fn(result.value) : err(result.error);
34
- function flatMap(...args) {
35
- if (args.length === 2) return flatMapImpl(args[0], args[1]);
36
- const fn = args[0];
37
- return (result) => flatMapImpl(result, fn);
38
- }
39
- var recoverImpl = (result, fn) => isErr(result) ? fn(result.error) : ok(result.value);
40
- function recover(...args) {
41
- if (args.length === 2) return recoverImpl(args[0], args[1]);
42
- const fn = args[0];
43
- return (result) => recoverImpl(result, fn);
44
- }
45
- var tapImpl = (result, fn) => {
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) => {
46
42
  if (isOk(result)) fn(result.value);
47
43
  return result;
48
- };
49
- function tap(...args) {
50
- if (args.length === 2) return tapImpl(args[0], args[1]);
51
- const fn = args[0];
52
- return (result) => tapImpl(result, fn);
53
- }
54
- var tapErrImpl = (result, fn) => {
44
+ });
45
+ var tapErr = dual(2, (result, fn) => {
55
46
  if (isErr(result)) fn(result.error);
56
47
  return result;
57
- };
58
- function tapErr(...args) {
59
- if (args.length === 2) return tapErrImpl(args[0], args[1]);
60
- const fn = args[0];
61
- return (result) => tapErrImpl(result, fn);
62
- }
63
- var matchImpl = (result, onOk, onErr) => isOk(result) ? onOk(result.value) : onErr(result.error);
64
- function match(...args) {
65
- if (args.length === 3) return matchImpl(args[0], args[1], args[2]);
66
- const [onOk, onErr] = args;
67
- return (result) => matchImpl(result, onOk, onErr);
68
- }
69
- var matchResult = match;
70
- var fold = (handlers) => (result) => matchImpl(result, handlers.ok, handlers.err);
71
- var unwrapOr = (result, defaultValue) => isOk(result) ? result.value : defaultValue;
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
+ );
72
57
  function unwrapOk(result) {
73
58
  if (isErr(result)) throw result.error;
74
59
  return result.value;
75
60
  }
61
+ var unwrap = unwrapOk;
76
62
  function unwrapErr(result) {
77
63
  if (isOk(result)) throw new TypeError("unwrapErr called on Ok");
78
64
  return result.error;
@@ -86,18 +72,6 @@ function trySync(fn, onThrow) {
86
72
  }
87
73
  };
88
74
  }
89
- var combine = (results) => {
90
- const values = [];
91
- for (const result of results) {
92
- if (isErr(result)) return err(result.error);
93
- values.push(result.value);
94
- }
95
- return ok(values);
96
- };
97
- var combineTuple = (results) => (
98
- // Runtime identical to combine; the cast restores per-index tuple types.
99
- combine(results)
100
- );
101
75
  function pipe(value, ...fns) {
102
76
  let acc = value;
103
77
  for (const fn of fns) {
@@ -117,17 +91,6 @@ var UnexpectedError = class extends Error {
117
91
  };
118
92
 
119
93
  // src/async.ts
120
- var liftPromiseResult = async (promise, onDefect) => {
121
- try {
122
- const result = await promise;
123
- if (isErr(result)) {
124
- return result;
125
- }
126
- return isResultLike(result.value) ? result.value : result;
127
- } catch (error) {
128
- return err(onDefect(error));
129
- }
130
- };
131
94
  var sequenceSettled = (settled) => {
132
95
  const values = [];
133
96
  for (const result of settled) {
@@ -144,9 +107,32 @@ var ResultAsync = class _ResultAsync {
144
107
  }
145
108
  run;
146
109
  promise = null;
110
+ /**
111
+ * Lifts an already-settled sync {@link Result} into a {@link ResultAsync}.
112
+ *
113
+ * @example
114
+ * ```ts
115
+ * const ra = ResultAsync.fromResult(ok(42)); // ResultAsync<number, never>
116
+ * ```
117
+ */
147
118
  static fromResult(result) {
148
119
  return new _ResultAsync(async () => result);
149
120
  }
121
+ /**
122
+ * Wraps a `PromiseLike<T>` that may reject. Rejections pass through `onReject`
123
+ * to become a typed `Err`; resolution becomes `Ok<T>`.
124
+ *
125
+ * @param promise - the promise to wrap
126
+ * @param onReject - maps a rejection reason to the `Err` channel
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * const body = ResultAsync.fromPromise(
131
+ * fetch(url).then((r) => r.text()),
132
+ * (e): NetError => ({ kind: "net", cause: String(e) }),
133
+ * );
134
+ * ```
135
+ */
150
136
  static fromPromise(promise, onReject) {
151
137
  return new _ResultAsync(async () => {
152
138
  try {
@@ -156,12 +142,29 @@ var ResultAsync = class _ResultAsync {
156
142
  }
157
143
  });
158
144
  }
145
+ /**
146
+ * Wraps a `PromiseLike<T>` that is **guaranteed not to reject**, skipping the
147
+ * `onReject` mapper. Use only when rejection is provably impossible.
148
+ *
149
+ * @example
150
+ * ```ts
151
+ * const now = ResultAsync.fromSafePromise(Promise.resolve(Date.now()));
152
+ * ```
153
+ */
159
154
  static fromSafePromise(promise) {
160
155
  return new _ResultAsync(async () => ok(await promise));
161
156
  }
162
157
  /**
163
- * Defers work until {@link resolve}. Unlike {@link fromPromise}, nothing runs
164
- * until the `ResultAsync` is resolved (e.g. by `combineTuple` / `combineTupleParallel`).
158
+ * Defers work until {@link resolve}. Unlike {@link fromPromise}, the factory
159
+ * does not run until the `ResultAsync` is resolved (e.g. by `combineTuple` /
160
+ * `combineTupleParallel`). The factory runs at most once — `resolve` memoizes.
161
+ *
162
+ * @example
163
+ * ```ts
164
+ * const insert = ResultAsync.defer(() => db.orders.insert(row));
165
+ * // nothing has run yet
166
+ * const r = await insert; // factory runs exactly once here
167
+ * ```
165
168
  */
166
169
  static defer(fn) {
167
170
  return new _ResultAsync(fn);
@@ -169,14 +172,49 @@ var ResultAsync = class _ResultAsync {
169
172
  static ok(value) {
170
173
  return new _ResultAsync(async () => ok(value));
171
174
  }
175
+ /**
176
+ * Lifts an error into an `Err` async result.
177
+ *
178
+ * @example
179
+ * ```ts
180
+ * const r = ResultAsync.err({ kind: "not_found" as const });
181
+ * ```
182
+ */
172
183
  static err(error) {
173
184
  return new _ResultAsync(async () => err(error));
174
185
  }
175
- /** @see {@link fromAsync} from `@onrails/result/interop` */
186
+ /**
187
+ * Lifts an existing `Promise<Result<T, E>>` (e.g. from interop code) into a
188
+ * {@link ResultAsync}. A thrown defect is routed through `onDefect`,
189
+ * defaulting to {@link UnexpectedError}. The `Ok` value passes through
190
+ * verbatim — even when it happens to be Result-shaped.
191
+ *
192
+ * @param promise - a promise that already yields a `Result`
193
+ * @param onDefect - maps an unexpected throw to the `Err` channel
194
+ * @see {@link fromAsync} from `@onrails/result`
195
+ */
176
196
  static fromResultPromise(promise, onDefect) {
177
197
  const mapDefect = onDefect ?? ((error) => new UnexpectedError("Unexpected async defect", error));
178
- return new _ResultAsync(() => liftPromiseResult(promise, mapDefect));
198
+ return new _ResultAsync(async () => {
199
+ try {
200
+ return await promise;
201
+ } catch (error) {
202
+ return err(mapDefect(error));
203
+ }
204
+ });
179
205
  }
206
+ /**
207
+ * Combines a homogeneous array of async results into one. Resolves
208
+ * **sequentially** in input order, short-circuiting on the first `Err`.
209
+ * For heterogeneous tuples that preserve per-index types, use
210
+ * {@link combineTuple}; for wall-clock overlap, {@link combineTupleParallel}.
211
+ *
212
+ * @example
213
+ * ```ts
214
+ * const all = ResultAsync.combine([loadA(), loadB(), loadC()]);
215
+ * // ResultAsync<Item[], LoadError>
216
+ * ```
217
+ */
180
218
  static combine(results) {
181
219
  return new _ResultAsync(async () => {
182
220
  const values = [];
@@ -190,6 +228,23 @@ var ResultAsync = class _ResultAsync {
190
228
  return ok(values);
191
229
  });
192
230
  }
231
+ /**
232
+ * Heterogeneous async tuple combine — resolves branches **sequentially**
233
+ * (left-to-right), returning the first `Err` in input order. Preserves each
234
+ * branch's `Ok` type by position, so the result destructures type-safely.
235
+ * This is the canonical sequential async combine (replaces the former
236
+ * `sequenceTupleAsync`).
237
+ *
238
+ * @example
239
+ * ```ts
240
+ * const combined = ResultAsync.combineTuple([loadCfg(), loadCatalog()] as const);
241
+ * // ResultAsync<readonly [Cfg, Catalog], CfgError | CatalogError>
242
+ * const r = await combined;
243
+ * if (isOk(r)) {
244
+ * const [cfg, catalog] = r.value; // typed per position
245
+ * }
246
+ * ```
247
+ */
193
248
  static combineTuple(results) {
194
249
  return _ResultAsync.combine(
195
250
  results
@@ -198,18 +253,55 @@ var ResultAsync = class _ResultAsync {
198
253
  /**
199
254
  * Like {@link combineTuple}, but starts every branch before awaiting (wall-clock
200
255
  * parallel for independent IO). On failure, returns the first `Err` in input order.
256
+ *
257
+ * @example
258
+ * ```ts
259
+ * // independent IO — overlap them
260
+ * const combined = ResultAsync.combineTupleParallel([
261
+ * loadProfile(id),
262
+ * loadMetrics(id),
263
+ * ] as const);
264
+ * ```
201
265
  */
202
266
  static combineTupleParallel(results) {
203
267
  return new _ResultAsync(
204
268
  async () => sequenceSettled(await Promise.all(results.map((ra) => ra.resolve())))
205
269
  );
206
270
  }
271
+ /**
272
+ * Transforms the `Ok` value, passing `Err` through unchanged.
273
+ *
274
+ * @example
275
+ * ```ts
276
+ * ResultAsync.ok(2).map((n) => n * 3); // ResultAsync<number> → Ok 6
277
+ * ```
278
+ */
207
279
  map(fn) {
208
280
  return new _ResultAsync(async () => map(await this.resolve(), fn));
209
281
  }
282
+ /**
283
+ * Transforms the `Err` value, passing `Ok` through unchanged — useful for
284
+ * unifying heterogeneous failures into one app-level union.
285
+ *
286
+ * @example
287
+ * ```ts
288
+ * load(id).mapErr((e): AppError => ({ kind: "load", cause: e })); // ResultAsync<T, AppError>
289
+ * ```
290
+ */
210
291
  mapErr(fn) {
211
292
  return new _ResultAsync(async () => mapErr(await this.resolve(), fn));
212
293
  }
294
+ /**
295
+ * Canonical bind — chains a step that itself returns a `ResultAsync` or sync
296
+ * `Result`, short-circuiting on `Err`. Error types accumulate (`E | F`).
297
+ *
298
+ * @example
299
+ * ```ts
300
+ * authenticate(req)
301
+ * .flatMap((user) => loadProfile(user.id)) // ResultAsync
302
+ * .flatMap((p) => validate(p)); // sync Result also accepted
303
+ * ```
304
+ */
213
305
  flatMap(fn) {
214
306
  return new _ResultAsync(async () => {
215
307
  const first = await this.resolve();
@@ -217,25 +309,31 @@ var ResultAsync = class _ResultAsync {
217
309
  return first;
218
310
  }
219
311
  const next = fn(first.value);
220
- if (next instanceof _ResultAsync) {
221
- return next.resolve();
222
- }
223
- if (isResultLike(next)) {
224
- return next;
225
- }
226
- return isCompatLike(next) ? next.inner : next;
312
+ return next instanceof _ResultAsync ? next.resolve() : next;
227
313
  });
228
314
  }
315
+ /**
316
+ * neverthrow-compat alias of {@link flatMap}. Kept as the documented compat
317
+ * tier; prefer {@link flatMap} in new code.
318
+ *
319
+ * @example
320
+ * ```ts
321
+ * authenticate(req).andThen((user) => loadProfile(user.id));
322
+ * ```
323
+ */
229
324
  andThen(fn) {
230
325
  return this.flatMap(fn);
231
326
  }
232
327
  /**
233
- * Alias for {@link flatMap}.
234
- * @deprecated Use {@link flatMap} or keep/use compat {@link andThen} instead.
328
+ * Error-channel bind — runs `fn` only on `Err`, swapping in a recovery
329
+ * `ResultAsync` or sync `Result`. `Ok` passes through. The mirror of
330
+ * {@link flatMap} on the error track.
331
+ *
332
+ * @example
333
+ * ```ts
334
+ * loadFromCache(id).recover(() => loadFromOrigin(id)); // ResultAsync<T, F>
335
+ * ```
235
336
  */
236
- chain(fn) {
237
- return this.flatMap(fn);
238
- }
239
337
  recover(fn) {
240
338
  return new _ResultAsync(async () => {
241
339
  const first = await this.resolve();
@@ -246,9 +344,27 @@ var ResultAsync = class _ResultAsync {
246
344
  return next instanceof _ResultAsync ? next.resolve() : next;
247
345
  });
248
346
  }
347
+ /**
348
+ * neverthrow-compat alias of {@link recover}. Kept as the documented compat
349
+ * tier; prefer {@link recover} in new code.
350
+ *
351
+ * @example
352
+ * ```ts
353
+ * loadFromCache(id).orElse(() => loadFromOrigin(id));
354
+ * ```
355
+ */
249
356
  orElse(fn) {
250
357
  return this.recover(fn);
251
358
  }
359
+ /**
360
+ * Runs a side effect on the `Ok` value and passes the result through
361
+ * unchanged; a no-op on `Err`. Mirrors {@link tapErr}.
362
+ *
363
+ * @example
364
+ * ```ts
365
+ * saveUser(u).tap((saved) => analytics.track("user_saved", saved.id));
366
+ * ```
367
+ */
252
368
  tap(fn) {
253
369
  return new _ResultAsync(async () => {
254
370
  const result = await this.resolve();
@@ -258,6 +374,15 @@ var ResultAsync = class _ResultAsync {
258
374
  return result;
259
375
  });
260
376
  }
377
+ /**
378
+ * Runs a side effect on the `Err` value and passes the result through
379
+ * unchanged; a no-op on `Ok`. The error-track mirror of {@link tap}.
380
+ *
381
+ * @example
382
+ * ```ts
383
+ * saveUser(u).tapErr((e) => logger.warn("save failed", e));
384
+ * ```
385
+ */
261
386
  tapErr(fn) {
262
387
  return new _ResultAsync(async () => {
263
388
  const result = await this.resolve();
@@ -267,28 +392,49 @@ var ResultAsync = class _ResultAsync {
267
392
  return result;
268
393
  });
269
394
  }
270
- unwrapOr(defaultValue) {
271
- return this.resolve().then((result) => isErr(result) ? defaultValue : result.value);
272
- }
273
395
  /**
274
- * @deprecated Await the ResultAsync and narrow with {@link isOk} — this method
275
- * re-executes deferred factories and cannot narrow.
396
+ * Settles to the `Ok` value, or `defaultValue` if `Err`. Terminal — returns a
397
+ * plain `Promise`, not a `ResultAsync`.
398
+ *
399
+ * @example
400
+ * ```ts
401
+ * const profile = await loadProfile(id).unwrapOr(guestProfile);
402
+ * ```
276
403
  */
277
- isOk() {
278
- return this.resolve().then((result) => !isErr(result));
404
+ unwrapOr(defaultValue) {
405
+ return this.resolve().then((result) => isErr(result) ? defaultValue : result.value);
279
406
  }
280
407
  /**
281
- * @deprecated Await the ResultAsync and narrow with {@link isErr} — this method
282
- * re-executes deferred factories and cannot narrow.
408
+ * Terminal collapse — folds both tracks into a single awaited value. Returns
409
+ * a plain `Promise`, settling the carrier exactly once.
410
+ *
411
+ * @param onOk - handles the `Ok` value
412
+ * @param onErr - handles the `Err` value
413
+ *
414
+ * @example
415
+ * ```ts
416
+ * const status = await save(row).match(
417
+ * () => 200,
418
+ * (e) => (e.kind === "conflict" ? 409 : 500),
419
+ * );
420
+ * ```
283
421
  */
284
- isErr() {
285
- return this.resolve().then((result) => isErr(result));
286
- }
287
422
  match(onOk, onErr) {
288
423
  return this.resolve().then(
289
424
  (result) => isErr(result) ? onErr(result.error) : onOk(result.value)
290
425
  );
291
426
  }
427
+ /**
428
+ * Settles the carrier to a bare sync {@link Result}, memoizing so the
429
+ * underlying factory runs at most once. Prefer `await ra` (the thenable) or
430
+ * {@link match} in app code; use `resolve` when you need the tagged union back.
431
+ *
432
+ * @example
433
+ * ```ts
434
+ * const r = await load(id).resolve(); // Result<Data, LoadError>
435
+ * if (isOk(r)) use(r.value);
436
+ * ```
437
+ */
292
438
  resolve() {
293
439
  if (!this.promise) {
294
440
  this.promise = this.run();
@@ -308,37 +454,51 @@ var ResultAsync = class _ResultAsync {
308
454
  );
309
455
  }
310
456
  };
311
- function isResultLike(v) {
312
- return typeof v === "object" && v !== null && "_tag" in v && (v._tag === "Ok" || v._tag === "Err");
313
- }
314
- function isCompatLike(v) {
315
- return typeof v === "object" && v !== null && "inner" in v && isResultLike(v.inner);
316
- }
457
+
458
+ // src/async-lift.ts
317
459
  var okAsync = ResultAsync.ok;
318
460
  var errAsync = ResultAsync.err;
319
461
  var fromPromise = ResultAsync.fromPromise;
320
462
  var fromSafePromise = ResultAsync.fromSafePromise;
321
- var sequenceTupleAsync = ResultAsync.combineTuple;
322
- var parallelTupleAsync = ResultAsync.combineTupleParallel;
463
+ var fromResult = ResultAsync.fromResult;
464
+ var asyncAfter = dual(
465
+ 2,
466
+ (result, fn) => fromResult(result).flatMap(fn)
467
+ );
468
+ var fromAsync = (fn, onDefect) => (...args) => ResultAsync.fromResultPromise(fn(...args), onDefect);
323
469
  var toError = (error) => error instanceof Error ? error : new Error(String(error));
324
470
  function tryAsync(promise, onReject) {
325
- if (onReject) {
326
- return ResultAsync.fromPromise(promise, onReject);
327
- }
328
- return ResultAsync.fromPromise(promise, toError);
471
+ return onReject ? ResultAsync.fromPromise(promise, onReject) : ResultAsync.fromPromise(promise, toError);
329
472
  }
330
473
 
331
- // src/interop.ts
332
- var fromResult = (result) => ResultAsync.fromResult(result);
333
- function asyncAfter(resultOrFn, fn) {
334
- if (fn === void 0) {
335
- const f = resultOrFn;
336
- return (result) => fromResult(result).flatMap(f);
474
+ // src/collections.ts
475
+ var combine = (results) => {
476
+ const values = [];
477
+ for (const result of results) {
478
+ if (isErr(result)) return err(result.error);
479
+ values.push(result.value);
480
+ }
481
+ return ok(values);
482
+ };
483
+ var combineTuple = (results) => (
484
+ // Runtime identical to combine; the cast restores per-index tuple types.
485
+ combine(results)
486
+ );
487
+ function validateAll(results, combineErrors) {
488
+ const values = [];
489
+ const errors = [];
490
+ for (const result of results) {
491
+ if (isErr(result)) {
492
+ errors.push(result.error);
493
+ } else {
494
+ values.push(result.value);
495
+ }
337
496
  }
338
- return fromResult(resultOrFn).flatMap(fn);
497
+ if (errors.length === 0) return ok(values);
498
+ return err(combineErrors ? errors.reduce(combineErrors) : errors);
339
499
  }
340
- function fromAsync(fn, onDefect) {
341
- return (...args) => ResultAsync.fromResultPromise(fn(...args), onDefect);
500
+ function validateTuple(results, combineErrors) {
501
+ return combineErrors ? validateAll(results, combineErrors) : validateAll(results);
342
502
  }
343
503
 
344
504
  // src/pipe.ts
@@ -380,6 +540,7 @@ var tryGen = (fn) => {
380
540
 
381
541
  exports.$ = $;
382
542
  exports.ResultAsync = ResultAsync;
543
+ exports.UnexpectedError = UnexpectedError;
383
544
  exports.asyncAfter = asyncAfter;
384
545
  exports.bimap = bimap;
385
546
  exports.combine = combine;
@@ -388,7 +549,6 @@ exports.err = err;
388
549
  exports.errAsync = errAsync;
389
550
  exports.flatMap = flatMap;
390
551
  exports.flow = flow;
391
- exports.fold = fold;
392
552
  exports.fromAsync = fromAsync;
393
553
  exports.fromPromise = fromPromise;
394
554
  exports.fromResult = fromResult;
@@ -398,22 +558,21 @@ exports.isOk = isOk;
398
558
  exports.map = map;
399
559
  exports.mapErr = mapErr;
400
560
  exports.match = match;
401
- exports.matchResult = matchResult;
402
- exports.of = of;
403
561
  exports.ok = ok;
404
562
  exports.okAsync = okAsync;
405
- exports.parallelTupleAsync = parallelTupleAsync;
406
563
  exports.pipe = pipe;
407
564
  exports.recover = recover;
408
- exports.sequenceTupleAsync = sequenceTupleAsync;
409
565
  exports.tap = tap;
410
566
  exports.tapErr = tapErr;
411
567
  exports.tryAsync = tryAsync;
412
568
  exports.tryGen = tryGen;
413
569
  exports.trySync = trySync;
570
+ exports.unwrap = unwrap;
414
571
  exports.unwrapErr = unwrapErr;
415
572
  exports.unwrapOk = unwrapOk;
416
573
  exports.unwrapOr = unwrapOr;
574
+ exports.validateAll = validateAll;
575
+ exports.validateTuple = validateTuple;
417
576
  exports.yieldResult = yieldResult;
418
577
  //# sourceMappingURL=index.cjs.map
419
578
  //# sourceMappingURL=index.cjs.map