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