@onrails/result 0.1.4 → 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.
- package/DESIGN.md +2 -2
- package/README.md +0 -3
- package/dist/$.cjs.map +1 -1
- package/dist/$.js.map +1 -1
- package/dist/_.d.cts +1 -1
- package/dist/_.d.ts +1 -1
- package/dist/async-1fIhfzFu.d.ts +290 -0
- package/dist/async-u8U5P2EH.d.cts +290 -0
- package/dist/compat/neverthrow.cjs +234 -50
- package/dist/compat/neverthrow.cjs.map +1 -1
- package/dist/compat/neverthrow.d.cts +2 -2
- package/dist/compat/neverthrow.d.ts +2 -2
- package/dist/compat/neverthrow.js +231 -47
- package/dist/compat/neverthrow.js.map +1 -1
- package/dist/extra.cjs +1 -24
- package/dist/extra.cjs.map +1 -1
- package/dist/extra.d.cts +3 -9
- package/dist/extra.d.ts +3 -9
- package/dist/extra.js +2 -24
- package/dist/extra.js.map +1 -1
- package/dist/fluent.cjs +0 -2
- package/dist/fluent.cjs.map +1 -1
- package/dist/fluent.d.cts +2 -6
- package/dist/fluent.d.ts +2 -6
- package/dist/fluent.js +0 -2
- package/dist/fluent.js.map +1 -1
- package/dist/index.cjs +220 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +174 -51
- package/dist/index.d.ts +174 -51
- package/dist/index.js +221 -44
- package/dist/index.js.map +1 -1
- package/dist/interop.cjs +203 -23
- package/dist/interop.cjs.map +1 -1
- package/dist/interop.d.cts +48 -8
- package/dist/interop.d.ts +48 -8
- package/dist/interop.js +203 -23
- package/dist/interop.js.map +1 -1
- package/dist/pipe.cjs.map +1 -1
- package/dist/pipe.d.cts +7 -5
- package/dist/pipe.d.ts +7 -5
- package/dist/pipe.js.map +1 -1
- package/dist/railway.cjs +203 -24
- package/dist/railway.cjs.map +1 -1
- package/dist/railway.d.cts +2 -2
- package/dist/railway.d.ts +2 -2
- package/dist/railway.js +203 -24
- package/dist/railway.js.map +1 -1
- package/dist/try-gen.cjs.map +1 -1
- package/dist/try-gen.d.cts +29 -3
- package/dist/try-gen.d.ts +29 -3
- package/dist/try-gen.js.map +1 -1
- package/dist/types-BQ9vv0nD.d.cts +55 -0
- package/dist/types-BQ9vv0nD.d.ts +55 -0
- package/dist/validation.cjs.map +1 -1
- package/dist/validation.d.cts +1 -1
- package/dist/validation.d.ts +1 -1
- package/dist/validation.js.map +1 -1
- package/package.json +5 -3
- package/skills/railway-do-notation/SKILL.md +436 -0
- package/skills/result-composition/SKILL.md +325 -0
- package/dist/async-CH8Cs8KE.d.cts +0 -161
- package/dist/async-DKISqIBc.d.ts +0 -161
- package/dist/types-C2Dp1d5J.d.cts +0 -21
- package/dist/types-C2Dp1d5J.d.ts +0 -21
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,123 @@
|
|
|
1
|
-
|
|
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`,
|
|
156
|
-
*
|
|
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 —
|
|
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
|
-
* @
|
|
193
|
-
*
|
|
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 —
|
|
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
|
-
* @
|
|
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
|
-
/**
|
|
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,
|
|
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},
|
|
162
|
-
* until the `ResultAsync` is resolved (e.g. by `combineTuple` /
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
232
|
-
*
|
|
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
|
-
*
|
|
273
|
-
*
|
|
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
|
-
|
|
276
|
-
return this.resolve().then((result) =>
|
|
423
|
+
unwrapOr(defaultValue) {
|
|
424
|
+
return this.resolve().then((result) => isErr(result) ? defaultValue : result.value);
|
|
277
425
|
}
|
|
278
426
|
/**
|
|
279
|
-
*
|
|
280
|
-
*
|
|
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,20 +479,32 @@ 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
|
-
|
|
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
510
|
function asyncAfter(resultOrFn, fn) {
|
|
@@ -335,9 +514,7 @@ function asyncAfter(resultOrFn, fn) {
|
|
|
335
514
|
}
|
|
336
515
|
return fromResult(resultOrFn).flatMap(fn);
|
|
337
516
|
}
|
|
338
|
-
|
|
339
|
-
return (...args) => ResultAsync.fromResultPromise(fn(...args), onDefect);
|
|
340
|
-
}
|
|
517
|
+
var fromAsync = (fn, onDefect) => (...args) => ResultAsync.fromResultPromise(fn(...args), onDefect);
|
|
341
518
|
|
|
342
519
|
// src/pipe.ts
|
|
343
520
|
function flow(first, ...rest) {
|
|
@@ -376,6 +553,6 @@ var tryGen = (fn) => {
|
|
|
376
553
|
}
|
|
377
554
|
};
|
|
378
555
|
|
|
379
|
-
export { $, ResultAsync, asyncAfter, bimap, combine, combineTuple, err, errAsync, flatMap, flow,
|
|
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 };
|
|
380
557
|
//# sourceMappingURL=index.js.map
|
|
381
558
|
//# sourceMappingURL=index.js.map
|