@onrails/result 0.4.0 → 0.4.2
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 +1 -1
- package/README.md +168 -12
- package/RECIPES.md +11 -9
- package/dist/async-fns.cjs +52 -0
- package/dist/async-fns.cjs.map +1 -0
- package/dist/async-fns.d.cts +143 -0
- package/dist/async-fns.d.ts +143 -0
- package/dist/async-fns.js +42 -0
- package/dist/async-fns.js.map +1 -0
- package/dist/async-lift.d.cts +109 -0
- package/dist/async-lift.d.ts +109 -0
- package/dist/{async-Ci6WSL5-.d.ts → async.d.cts} +45 -58
- package/dist/{async-DcXR7k9K.d.cts → async.d.ts} +45 -58
- package/dist/collections.d.cts +105 -0
- package/dist/collections.d.ts +105 -0
- package/dist/compat/neverthrow.cjs +57 -45
- package/dist/compat/neverthrow.cjs.map +1 -1
- package/dist/compat/neverthrow.d.cts +21 -24
- package/dist/compat/neverthrow.d.ts +21 -24
- package/dist/compat/neverthrow.js +56 -44
- package/dist/compat/neverthrow.js.map +1 -1
- package/dist/extra.cjs +3 -1
- package/dist/extra.cjs.map +1 -1
- package/dist/extra.d.cts +11 -14
- package/dist/extra.d.ts +11 -14
- package/dist/extra.js +3 -1
- package/dist/extra.js.map +1 -1
- package/dist/fluent.cjs +15 -13
- package/dist/fluent.cjs.map +1 -1
- package/dist/fluent.d.cts +4 -7
- package/dist/fluent.d.ts +4 -7
- package/dist/fluent.js +15 -13
- package/dist/fluent.js.map +1 -1
- package/dist/index.cjs +77 -52
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -490
- package/dist/index.d.ts +28 -490
- package/dist/index.js +77 -53
- package/dist/index.js.map +1 -1
- package/dist/internal/ctor.d.cts +11 -0
- package/dist/internal/ctor.d.ts +11 -0
- package/dist/internal/dual.d.cts +42 -0
- package/dist/internal/dual.d.ts +42 -0
- package/dist/internal/infer.d.cts +40 -0
- package/dist/internal/infer.d.ts +40 -0
- package/dist/internal/payload.d.cts +17 -0
- package/dist/internal/payload.d.ts +17 -0
- package/dist/internal/to-error.d.cts +2 -0
- package/dist/internal/to-error.d.ts +2 -0
- package/dist/pipe.cjs +8 -0
- package/dist/pipe.cjs.map +1 -1
- package/dist/pipe.d.cts +58 -11
- package/dist/pipe.d.ts +58 -11
- package/dist/pipe.js +8 -1
- package/dist/pipe.js.map +1 -1
- package/dist/railway.cjs +87 -63
- package/dist/railway.cjs.map +1 -1
- package/dist/railway.d.cts +49 -19
- package/dist/railway.d.ts +49 -19
- package/dist/railway.js +87 -63
- package/dist/railway.js.map +1 -1
- package/dist/result.d.cts +317 -0
- package/dist/result.d.ts +317 -0
- package/dist/try-gen.cjs +3 -3
- package/dist/try-gen.cjs.map +1 -1
- package/dist/try-gen.d.cts +4 -7
- package/dist/try-gen.d.ts +4 -7
- package/dist/try-gen.js +3 -3
- package/dist/try-gen.js.map +1 -1
- package/dist/{types-BQ9vv0nD.d.cts → types.d.cts} +6 -8
- package/dist/{types-BQ9vv0nD.d.ts → types.d.ts} +6 -8
- package/package.json +69 -24
- package/skills/railway-do-notation/SKILL.md +9 -9
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@onrails/result/async` — data-last twins of the {@link ResultAsync} methods,
|
|
3
|
+
* so an async railway composes with `pipe` / `flow` the same way a sync one does.
|
|
4
|
+
*
|
|
5
|
+
* `ResultAsync` is a class, so dot-chaining is always available and remains the
|
|
6
|
+
* shortest form for a one-off chain. Reach for this module when you want the
|
|
7
|
+
* sync side's other two syntaxes on the async carrier:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import * as RA from "@onrails/result/async";
|
|
11
|
+
* import { pipe } from "@onrails/result";
|
|
12
|
+
* import { flow } from "@onrails/result/pipe";
|
|
13
|
+
*
|
|
14
|
+
* // value-first pipe
|
|
15
|
+
* pipe(loadUser(id), RA.flatMap(loadOrders), RA.map((os) => os.length));
|
|
16
|
+
*
|
|
17
|
+
* // reusable point-free pipeline — no value yet
|
|
18
|
+
* const orderCount = flow(loadUser, RA.flatMap(loadOrders), RA.map((os) => os.length));
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* Every export mirrors one `ResultAsync` method exactly, in both forms:
|
|
22
|
+
* data-first (`RA.map(ra, fn)`) and curried (`RA.map(fn)`). Import as a
|
|
23
|
+
* namespace — the names deliberately match the sync core, and `test/parity.spec.ts`
|
|
24
|
+
* enforces that the mirror stays complete.
|
|
25
|
+
*/
|
|
26
|
+
import type { ResultAsync } from "./async.js";
|
|
27
|
+
import type { Result } from "./types.js";
|
|
28
|
+
/** A step may hand back either carrier; the async one absorbs a sync `Result`. */
|
|
29
|
+
type Step<U, F> = ResultAsync<U, F> | Result<U, F>;
|
|
30
|
+
/**
|
|
31
|
+
* Transforms the `Ok` value, passing `Err` through unchanged.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* pipe(loadUser(id), map((u) => u.name)); // ResultAsync<string, LoadError>
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare const map: {
|
|
39
|
+
<T, U, E>(ra: ResultAsync<T, E>, fn: (value: T) => U): ResultAsync<U, E>;
|
|
40
|
+
<T, U>(fn: (value: T) => U): <E>(ra: ResultAsync<T, E>) => ResultAsync<U, E>;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Transforms the `Err` value, passing `Ok` through unchanged — useful for
|
|
44
|
+
* unifying heterogeneous failures into one app-level union.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* pipe(loadUser(id), mapErr((e): AppError => ({ kind: "load", cause: e })));
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare const mapErr: {
|
|
52
|
+
<T, E, F>(ra: ResultAsync<T, E>, fn: (error: E) => F): ResultAsync<T, F>;
|
|
53
|
+
<E, F>(fn: (error: E) => F): <T>(ra: ResultAsync<T, E>) => ResultAsync<T, F>;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Transforms both tracks in one step. Exactly one of `onOk` / `onErr` runs.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* pipe(loadUser(id), bimap(toDto, (e): AppError => ({ kind: "load", cause: e })));
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export declare const bimap: {
|
|
64
|
+
<T, U, E, F>(ra: ResultAsync<T, E>, onOk: (value: T) => U, onErr: (error: E) => F): ResultAsync<U, F>;
|
|
65
|
+
<T, U, E, F>(onOk: (value: T) => U, onErr: (error: E) => F): (ra: ResultAsync<T, E>) => ResultAsync<U, F>;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Canonical bind — chains a step returning a `ResultAsync` or a sync `Result`,
|
|
69
|
+
* short-circuiting on `Err`. Error types accumulate (`E | F`).
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* pipe(authenticate(req), flatMap((user) => loadProfile(user.id)));
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export declare const flatMap: {
|
|
77
|
+
<T, U, E, F>(ra: ResultAsync<T, E>, fn: (value: T) => Step<U, F>): ResultAsync<U, E | F>;
|
|
78
|
+
<T, U, F>(fn: (value: T) => Step<U, F>): <E>(ra: ResultAsync<T, E>) => ResultAsync<U, E | F>;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* Error-track bind — runs `fn` only on `Err`, letting a failed workflow
|
|
82
|
+
* recover to `Ok` or remap the failure.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* pipe(loadUser(id), recover((e) => (e === "notFound" ? okAsync(guest) : errAsync(e))));
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export declare const recover: {
|
|
90
|
+
<T, E, F>(ra: ResultAsync<T, E>, fn: (error: E) => Step<T, F>): ResultAsync<T, F>;
|
|
91
|
+
<T, E, F>(fn: (error: E) => Step<T, F>): (ra: ResultAsync<T, E>) => ResultAsync<T, F>;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Observes the `Ok` value for side effects without changing it.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```ts
|
|
98
|
+
* pipe(loadUser(id), tap((u) => log.info({ msg: "loaded", id: u.id })));
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export declare const tap: {
|
|
102
|
+
<T, E>(ra: ResultAsync<T, E>, fn: (value: T) => void): ResultAsync<T, E>;
|
|
103
|
+
<T>(fn: (value: T) => void): <E>(ra: ResultAsync<T, E>) => ResultAsync<T, E>;
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Observes the `Err` value for side effects without changing it.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```ts
|
|
110
|
+
* pipe(loadUser(id), tapErr((e) => metrics.inc("user.load.fail", { kind: e.kind })));
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export declare const tapErr: {
|
|
114
|
+
<T, E>(ra: ResultAsync<T, E>, fn: (error: E) => void): ResultAsync<T, E>;
|
|
115
|
+
<E>(fn: (error: E) => void): <T>(ra: ResultAsync<T, E>) => ResultAsync<T, E>;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Terminal collapse — folds both tracks into a single awaited value, settling
|
|
119
|
+
* the carrier exactly once. Ends the pipeline, so it returns a plain `Promise`.
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```ts
|
|
123
|
+
* const status = await pipe(save(row), match(() => 200, (e) => (e.kind === "conflict" ? 409 : 500)));
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
export declare const match: {
|
|
127
|
+
<T, E, U1, U2 = U1>(ra: ResultAsync<T, E>, onOk: (value: T) => U1, onErr: (error: E) => U2): Promise<U1 | U2>;
|
|
128
|
+
<T, E, U1, U2 = U1>(onOk: (value: T) => U1, onErr: (error: E) => U2): (ra: ResultAsync<T, E>) => Promise<U1 | U2>;
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Terminal — resolves to the `Ok` value, or `defaultValue` on `Err`. The
|
|
132
|
+
* fallback may differ from `T`; the result widens to `T | U`.
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts
|
|
136
|
+
* const profile = await pipe(loadProfile(id), unwrapOr(guestProfile));
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
export declare const unwrapOr: {
|
|
140
|
+
<T, E, U>(ra: ResultAsync<T, E>, defaultValue: U): Promise<T | U>;
|
|
141
|
+
<U>(defaultValue: U): <T, E>(ra: ResultAsync<T, E>) => Promise<T | U>;
|
|
142
|
+
};
|
|
143
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
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
|
+
|
|
8
|
+
// src/async-fns.ts
|
|
9
|
+
var map = dual(2, (ra, fn) => ra.map(fn));
|
|
10
|
+
var mapErr = dual(
|
|
11
|
+
2,
|
|
12
|
+
(ra, fn) => ra.mapErr(fn)
|
|
13
|
+
);
|
|
14
|
+
var bimap = dual(
|
|
15
|
+
3,
|
|
16
|
+
(ra, onOk, onErr) => ra.bimap(onOk, onErr)
|
|
17
|
+
);
|
|
18
|
+
var flatMap = dual(
|
|
19
|
+
2,
|
|
20
|
+
(ra, fn) => ra.flatMap(fn)
|
|
21
|
+
);
|
|
22
|
+
var recover = dual(
|
|
23
|
+
2,
|
|
24
|
+
(ra, fn) => ra.recover(fn)
|
|
25
|
+
);
|
|
26
|
+
var tap = dual(2, (ra, fn) => ra.tap(fn));
|
|
27
|
+
var tapErr = dual(
|
|
28
|
+
2,
|
|
29
|
+
(ra, fn) => ra.tapErr(fn)
|
|
30
|
+
);
|
|
31
|
+
var match = dual(
|
|
32
|
+
3,
|
|
33
|
+
(ra, onOk, onErr) => ra.match(onOk, onErr)
|
|
34
|
+
);
|
|
35
|
+
var unwrapOr = dual(
|
|
36
|
+
2,
|
|
37
|
+
(ra, defaultValue) => ra.unwrapOr(defaultValue)
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
export { bimap, flatMap, map, mapErr, match, recover, tap, tapErr, unwrapOr };
|
|
41
|
+
//# sourceMappingURL=async-fns.js.map
|
|
42
|
+
//# sourceMappingURL=async-fns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/internal/dual.ts","../src/async-fns.ts"],"names":[],"mappings":";AA0CO,IAAM,IAAA,GAAO,CAAI,KAAA,EAAe,IAAA,KAA2C;AAMhF,EAAA,MAAM,GAAA,GAAM,IAAA;AACZ,EAAA,MAAM,UAAA,GAAa,CAAA,GAAI,IAAA,KACrB,IAAA,CAAK,UAAU,KAAA,GAAQ,GAAA,CAAI,GAAG,IAAI,IAAI,CAAC,IAAA,KAAkB,GAAA,CAAI,IAAA,EAAM,GAAG,IAAI,CAAA;AAG5E,EAAA,OAAO,UAAA;AACT,CAAA;;;ACbO,IAAM,GAAA,GAGT,KAAK,CAAA,EAAG,CAAU,IAAuB,EAAA,KAA2C,EAAA,CAAG,GAAA,CAAI,EAAE,CAAC;AAW3F,IAAM,MAAA,GAGT,IAAA;AAAA,EACF,CAAA;AAAA,EACA,CAAU,EAAA,EAAuB,EAAA,KAA2C,EAAA,CAAG,OAAO,EAAE;AAC1F;AAUO,IAAM,KAAA,GAUT,IAAA;AAAA,EACF,CAAA;AAAA,EACA,CACE,EAAA,EACA,IAAA,EACA,UACsB,EAAA,CAAG,KAAA,CAAM,MAAM,KAAK;AAC9C;AAWO,IAAM,OAAA,GAGT,IAAA;AAAA,EACF,CAAA;AAAA,EACA,CAAa,EAAA,EAAuB,EAAA,KAClC,EAAA,CAAG,QAAQ,EAAE;AACjB;AAWO,IAAM,OAAA,GAGT,IAAA;AAAA,EACF,CAAA;AAAA,EACA,CAAU,EAAA,EAAuB,EAAA,KAC/B,EAAA,CAAG,QAAQ,EAAE;AACjB;AAUO,IAAM,GAAA,GAGT,KAAK,CAAA,EAAG,CAAO,IAAuB,EAAA,KAA8C,EAAA,CAAG,GAAA,CAAI,EAAE,CAAC;AAU3F,IAAM,MAAA,GAGT,IAAA;AAAA,EACF,CAAA;AAAA,EACA,CAAO,EAAA,EAAuB,EAAA,KAA8C,EAAA,CAAG,OAAO,EAAE;AAC1F;AAWO,IAAM,KAAA,GAUT,IAAA;AAAA,EACF,CAAA;AAAA,EACA,CACE,EAAA,EACA,IAAA,EACA,UACqB,EAAA,CAAG,KAAA,CAAM,MAAM,KAAK;AAC7C;AAWO,IAAM,QAAA,GAGT,IAAA;AAAA,EACF,CAAA;AAAA,EACA,CAAU,EAAA,EAAuB,YAAA,KAAoC,EAAA,CAAG,SAAS,YAAY;AAC/F","file":"async-fns.js","sourcesContent":["/**\n * @onrails/result internal — owning module for the **dual-form idiom**.\n *\n * Every dual-form transform in this package (`map`, `flatMap`, `match`, …)\n * accepts either shape:\n *\n * data-first: `map(result, fn)` — full arity, runs immediately\n * curried: `map(fn)(result)` — arity minus the leading `self`, returns\n * a unary function awaiting the data\n *\n * {@link dual} is the single arity dispatcher those transforms derive from;\n * hand-rolled `args.length` checks are not allowed elsewhere.\n *\n * NOTE: a twin copy of this module lives at `packages/maybe/src/internal/dual.ts`\n * — keep the two in sync when editing.\n *\n * Internal only: not listed in package.json `exports` or tsup entries.\n */\n\n/**\n * Derive a dual-form (data-first / curried) function from a single\n * data-first `body`.\n *\n * When called with `args.length >= arity` the wrapper runs `body(...args)`\n * (data-first). With fewer arguments it returns `(self) => body(self, ...args)`\n * — the curried, data-last-`self` form used with `pipe`/`flow`.\n *\n * The public type is supplied entirely by the caller: annotate the exported\n * const with the exact overload set and let `F` be inferred from it.\n *\n * @example\n * ```ts\n * export const map: {\n * <T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E>;\n * <T, U>(fn: (value: T) => U): <E>(result: Result<T, E>) => Result<U, E>;\n * } = dual(\n * 2,\n * <T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E> =>\n * isOk(result) ? ok(fn(result.value)) : err(result.error),\n * );\n * ```\n */\nexport const dual = <F>(arity: number, body: (...args: never[]) => unknown): F => {\n // `(...args: never[]) => unknown` is the top function type, so every typed\n // body is assignable to the parameter — but it cannot be invoked with\n // `unknown` arguments. Re-view it through the variadic shape the dispatcher\n // actually forwards; the constraint is that `body` only ever receives the\n // arguments the caller's overload annotation admitted.\n const run = body as (...args: readonly unknown[]) => unknown;\n const dispatcher = (...args: readonly unknown[]): unknown =>\n args.length >= arity ? run(...args) : (self: unknown) => run(self, ...args);\n // The caller's overload annotation on the exported const supplies the real\n // public type; internally the dispatcher stays `unknown`-typed.\n return dispatcher as F;\n};\n","/**\n * `@onrails/result/async` — data-last twins of the {@link ResultAsync} methods,\n * so an async railway composes with `pipe` / `flow` the same way a sync one does.\n *\n * `ResultAsync` is a class, so dot-chaining is always available and remains the\n * shortest form for a one-off chain. Reach for this module when you want the\n * sync side's other two syntaxes on the async carrier:\n *\n * ```ts\n * import * as RA from \"@onrails/result/async\";\n * import { pipe } from \"@onrails/result\";\n * import { flow } from \"@onrails/result/pipe\";\n *\n * // value-first pipe\n * pipe(loadUser(id), RA.flatMap(loadOrders), RA.map((os) => os.length));\n *\n * // reusable point-free pipeline — no value yet\n * const orderCount = flow(loadUser, RA.flatMap(loadOrders), RA.map((os) => os.length));\n * ```\n *\n * Every export mirrors one `ResultAsync` method exactly, in both forms:\n * data-first (`RA.map(ra, fn)`) and curried (`RA.map(fn)`). Import as a\n * namespace — the names deliberately match the sync core, and `test/parity.spec.ts`\n * enforces that the mirror stays complete.\n */\n\nimport type { ResultAsync } from \"./async.js\";\nimport { dual } from \"./internal/dual.js\";\nimport type { Result } from \"./types.js\";\n\n/** A step may hand back either carrier; the async one absorbs a sync `Result`. */\ntype Step<U, F> = ResultAsync<U, F> | Result<U, F>;\n\n/**\n * Transforms the `Ok` value, passing `Err` through unchanged.\n *\n * @example\n * ```ts\n * pipe(loadUser(id), map((u) => u.name)); // ResultAsync<string, LoadError>\n * ```\n */\nexport const map: {\n <T, U, E>(ra: ResultAsync<T, E>, fn: (value: T) => U): ResultAsync<U, E>;\n <T, U>(fn: (value: T) => U): <E>(ra: ResultAsync<T, E>) => ResultAsync<U, E>;\n} = dual(2, <T, U, E>(ra: ResultAsync<T, E>, fn: (value: T) => U): ResultAsync<U, E> => ra.map(fn));\n\n/**\n * Transforms the `Err` value, passing `Ok` through unchanged — useful for\n * unifying heterogeneous failures into one app-level union.\n *\n * @example\n * ```ts\n * pipe(loadUser(id), mapErr((e): AppError => ({ kind: \"load\", cause: e })));\n * ```\n */\nexport const mapErr: {\n <T, E, F>(ra: ResultAsync<T, E>, fn: (error: E) => F): ResultAsync<T, F>;\n <E, F>(fn: (error: E) => F): <T>(ra: ResultAsync<T, E>) => ResultAsync<T, F>;\n} = dual(\n 2,\n <T, E, F>(ra: ResultAsync<T, E>, fn: (error: E) => F): ResultAsync<T, F> => ra.mapErr(fn),\n);\n\n/**\n * Transforms both tracks in one step. Exactly one of `onOk` / `onErr` runs.\n *\n * @example\n * ```ts\n * pipe(loadUser(id), bimap(toDto, (e): AppError => ({ kind: \"load\", cause: e })));\n * ```\n */\nexport const bimap: {\n <T, U, E, F>(\n ra: ResultAsync<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n ): ResultAsync<U, F>;\n <T, U, E, F>(\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n ): (ra: ResultAsync<T, E>) => ResultAsync<U, F>;\n} = dual(\n 3,\n <T, U, E, F>(\n ra: ResultAsync<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n ): ResultAsync<U, F> => ra.bimap(onOk, onErr),\n);\n\n/**\n * Canonical bind — chains a step returning a `ResultAsync` or a sync `Result`,\n * short-circuiting on `Err`. Error types accumulate (`E | F`).\n *\n * @example\n * ```ts\n * pipe(authenticate(req), flatMap((user) => loadProfile(user.id)));\n * ```\n */\nexport const flatMap: {\n <T, U, E, F>(ra: ResultAsync<T, E>, fn: (value: T) => Step<U, F>): ResultAsync<U, E | F>;\n <T, U, F>(fn: (value: T) => Step<U, F>): <E>(ra: ResultAsync<T, E>) => ResultAsync<U, E | F>;\n} = dual(\n 2,\n <T, U, E, F>(ra: ResultAsync<T, E>, fn: (value: T) => Step<U, F>): ResultAsync<U, E | F> =>\n ra.flatMap(fn),\n);\n\n/**\n * Error-track bind — runs `fn` only on `Err`, letting a failed workflow\n * recover to `Ok` or remap the failure.\n *\n * @example\n * ```ts\n * pipe(loadUser(id), recover((e) => (e === \"notFound\" ? okAsync(guest) : errAsync(e))));\n * ```\n */\nexport const recover: {\n <T, E, F>(ra: ResultAsync<T, E>, fn: (error: E) => Step<T, F>): ResultAsync<T, F>;\n <T, E, F>(fn: (error: E) => Step<T, F>): (ra: ResultAsync<T, E>) => ResultAsync<T, F>;\n} = dual(\n 2,\n <T, E, F>(ra: ResultAsync<T, E>, fn: (error: E) => Step<T, F>): ResultAsync<T, F> =>\n ra.recover(fn),\n);\n\n/**\n * Observes the `Ok` value for side effects without changing it.\n *\n * @example\n * ```ts\n * pipe(loadUser(id), tap((u) => log.info({ msg: \"loaded\", id: u.id })));\n * ```\n */\nexport const tap: {\n <T, E>(ra: ResultAsync<T, E>, fn: (value: T) => void): ResultAsync<T, E>;\n <T>(fn: (value: T) => void): <E>(ra: ResultAsync<T, E>) => ResultAsync<T, E>;\n} = dual(2, <T, E>(ra: ResultAsync<T, E>, fn: (value: T) => void): ResultAsync<T, E> => ra.tap(fn));\n\n/**\n * Observes the `Err` value for side effects without changing it.\n *\n * @example\n * ```ts\n * pipe(loadUser(id), tapErr((e) => metrics.inc(\"user.load.fail\", { kind: e.kind })));\n * ```\n */\nexport const tapErr: {\n <T, E>(ra: ResultAsync<T, E>, fn: (error: E) => void): ResultAsync<T, E>;\n <E>(fn: (error: E) => void): <T>(ra: ResultAsync<T, E>) => ResultAsync<T, E>;\n} = dual(\n 2,\n <T, E>(ra: ResultAsync<T, E>, fn: (error: E) => void): ResultAsync<T, E> => ra.tapErr(fn),\n);\n\n/**\n * Terminal collapse — folds both tracks into a single awaited value, settling\n * the carrier exactly once. Ends the pipeline, so it returns a plain `Promise`.\n *\n * @example\n * ```ts\n * const status = await pipe(save(row), match(() => 200, (e) => (e.kind === \"conflict\" ? 409 : 500)));\n * ```\n */\nexport const match: {\n <T, E, U1, U2 = U1>(\n ra: ResultAsync<T, E>,\n onOk: (value: T) => U1,\n onErr: (error: E) => U2,\n ): Promise<U1 | U2>;\n <T, E, U1, U2 = U1>(\n onOk: (value: T) => U1,\n onErr: (error: E) => U2,\n ): (ra: ResultAsync<T, E>) => Promise<U1 | U2>;\n} = dual(\n 3,\n <T, E, U1, U2>(\n ra: ResultAsync<T, E>,\n onOk: (value: T) => U1,\n onErr: (error: E) => U2,\n ): Promise<U1 | U2> => ra.match(onOk, onErr),\n);\n\n/**\n * Terminal — resolves to the `Ok` value, or `defaultValue` on `Err`. The\n * fallback may differ from `T`; the result widens to `T | U`.\n *\n * @example\n * ```ts\n * const profile = await pipe(loadProfile(id), unwrapOr(guestProfile));\n * ```\n */\nexport const unwrapOr: {\n <T, E, U>(ra: ResultAsync<T, E>, defaultValue: U): Promise<T | U>;\n <U>(defaultValue: U): <T, E>(ra: ResultAsync<T, E>) => Promise<T | U>;\n} = dual(\n 2,\n <T, E, U>(ra: ResultAsync<T, E>, defaultValue: U): Promise<T | U> => ra.unwrapOr(defaultValue),\n);\n"]}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @onrails/result — THE lift module. Every "value → {@link ResultAsync}"
|
|
3
|
+
* entry point is defined exactly once, here: `okAsync`, `errAsync`,
|
|
4
|
+
* `fromPromise`, `fromSafePromise`, `fromResult`, `fromAsync`, `asyncAfter`,
|
|
5
|
+
* `tryAsync`.
|
|
6
|
+
*/
|
|
7
|
+
import { ResultAsync } from "./async.js";
|
|
8
|
+
import type { InferErr, InferOk } from "./internal/infer.js";
|
|
9
|
+
import type { Result, UnexpectedError } from "./types.js";
|
|
10
|
+
/**
|
|
11
|
+
* Lifts a value into an `Ok` async result.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const r = okAsync(42); // ResultAsync<42, never> — literal locked
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare const okAsync: typeof ResultAsync.ok;
|
|
19
|
+
/**
|
|
20
|
+
* Lifts a value into an `Err` async result.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* const r = errAsync({ kind: "not_found" as const });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare const errAsync: typeof ResultAsync.err;
|
|
28
|
+
/**
|
|
29
|
+
* Wraps a `PromiseLike<T>` into a {@link ResultAsync}. Reject reasons go
|
|
30
|
+
* through `onReject` to become typed `Err`s; success becomes `Ok<T>`.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* const body = fromPromise(
|
|
35
|
+
* fetch(url).then((r) => r.text()),
|
|
36
|
+
* (e): NetworkError => ({ kind: "network", cause: String(e) }),
|
|
37
|
+
* );
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare const fromPromise: typeof ResultAsync.fromPromise;
|
|
41
|
+
/**
|
|
42
|
+
* Wraps a `PromiseLike<T>` that **never rejects** into {@link ResultAsync}.
|
|
43
|
+
* Skips the `onReject` mapper. Use only when the promise is provably safe.
|
|
44
|
+
*/
|
|
45
|
+
export declare const fromSafePromise: typeof ResultAsync.fromSafePromise;
|
|
46
|
+
/**
|
|
47
|
+
* Lifts an already-settled sync {@link Result} into a {@link ResultAsync}, so
|
|
48
|
+
* it can be chained alongside async steps in a railway.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* const ra = fromResult(ok(1)); // ResultAsync<number, never>
|
|
53
|
+
* await fromResult(err(error)).resolve(); // Err(error)
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export declare const fromResult: typeof ResultAsync.fromResult;
|
|
57
|
+
/**
|
|
58
|
+
* Bind a sync {@link Result} into an async step without widening defects.
|
|
59
|
+
* Dual-form: data-first for one-shots, data-last (curried) for `pipe`/`flow`.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* asyncAfter(result, (u) => tryAsync(db.insert(u), toErr)); // data-first
|
|
64
|
+
* asyncAfter((u) => tryAsync(db.insert(u), toErr)); // data-last
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare const asyncAfter: {
|
|
68
|
+
<T, U, E, F>(result: Result<T, E>, fn: (value: T) => ResultAsync<U, F>): ResultAsync<U, E | F>;
|
|
69
|
+
<T, U, F>(fn: (value: T) => ResultAsync<U, F>): <E>(result: Result<T, E>) => ResultAsync<U, E | F>;
|
|
70
|
+
};
|
|
71
|
+
type AnyResult = Result<unknown, unknown>;
|
|
72
|
+
/**
|
|
73
|
+
* Lifts a `(...args) => Promise<Result<T, E>>` function (typical of interop or
|
|
74
|
+
* boundary code) into one returning `(...args) => ResultAsync<T, E>`. Expected
|
|
75
|
+
* `Err`s pass through; an unexpected promise rejection (a defect) is routed via
|
|
76
|
+
* `onDefect`, defaulting to {@link UnexpectedError} and widening the error union.
|
|
77
|
+
*
|
|
78
|
+
* @param fn - a function returning a promise that already yields a `Result`
|
|
79
|
+
* @param onDefect - maps an unexpected rejection to the `Err` channel
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* const loadUser = fromAsync(
|
|
84
|
+
* (id: string): Promise<Result<User, NotFound>> => api.getUser(id),
|
|
85
|
+
* );
|
|
86
|
+
* const ra = loadUser("u1"); // ResultAsync<User, NotFound | UnexpectedError>
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export declare const fromAsync: <A extends readonly unknown[], R extends AnyResult>(fn: (...args: A) => Promise<R>, onDefect?: (error: unknown) => InferErr<R> | UnexpectedError) => ((...args: A) => ResultAsync<InferOk<R>, InferErr<R> | UnexpectedError>);
|
|
90
|
+
/**
|
|
91
|
+
* Convenience wrapper over {@link fromPromise} with default `Error`
|
|
92
|
+
* normalization. Call without `onReject` to get `ResultAsync<T, Error>`,
|
|
93
|
+
* or pass a custom mapper for a typed error.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```ts
|
|
97
|
+
* // Default: rejection → Err(Error)
|
|
98
|
+
* const a = tryAsync(db.users.insert(row));
|
|
99
|
+
*
|
|
100
|
+
* // Custom: typed error
|
|
101
|
+
* const b = tryAsync(db.users.insert(row), (e): DbError => ({
|
|
102
|
+
* kind: "db",
|
|
103
|
+
* cause: e,
|
|
104
|
+
* }));
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
export declare function tryAsync<T>(promise: PromiseLike<T>): ResultAsync<T, Error>;
|
|
108
|
+
export declare function tryAsync<T, E>(promise: PromiseLike<T>, onReject: (error: unknown) => E): ResultAsync<T, E>;
|
|
109
|
+
export {};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @onrails/result — THE lift module. Every "value → {@link ResultAsync}"
|
|
3
|
+
* entry point is defined exactly once, here: `okAsync`, `errAsync`,
|
|
4
|
+
* `fromPromise`, `fromSafePromise`, `fromResult`, `fromAsync`, `asyncAfter`,
|
|
5
|
+
* `tryAsync`.
|
|
6
|
+
*/
|
|
7
|
+
import { ResultAsync } from "./async.js";
|
|
8
|
+
import type { InferErr, InferOk } from "./internal/infer.js";
|
|
9
|
+
import type { Result, UnexpectedError } from "./types.js";
|
|
10
|
+
/**
|
|
11
|
+
* Lifts a value into an `Ok` async result.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const r = okAsync(42); // ResultAsync<42, never> — literal locked
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare const okAsync: typeof ResultAsync.ok;
|
|
19
|
+
/**
|
|
20
|
+
* Lifts a value into an `Err` async result.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* const r = errAsync({ kind: "not_found" as const });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare const errAsync: typeof ResultAsync.err;
|
|
28
|
+
/**
|
|
29
|
+
* Wraps a `PromiseLike<T>` into a {@link ResultAsync}. Reject reasons go
|
|
30
|
+
* through `onReject` to become typed `Err`s; success becomes `Ok<T>`.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* const body = fromPromise(
|
|
35
|
+
* fetch(url).then((r) => r.text()),
|
|
36
|
+
* (e): NetworkError => ({ kind: "network", cause: String(e) }),
|
|
37
|
+
* );
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare const fromPromise: typeof ResultAsync.fromPromise;
|
|
41
|
+
/**
|
|
42
|
+
* Wraps a `PromiseLike<T>` that **never rejects** into {@link ResultAsync}.
|
|
43
|
+
* Skips the `onReject` mapper. Use only when the promise is provably safe.
|
|
44
|
+
*/
|
|
45
|
+
export declare const fromSafePromise: typeof ResultAsync.fromSafePromise;
|
|
46
|
+
/**
|
|
47
|
+
* Lifts an already-settled sync {@link Result} into a {@link ResultAsync}, so
|
|
48
|
+
* it can be chained alongside async steps in a railway.
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* const ra = fromResult(ok(1)); // ResultAsync<number, never>
|
|
53
|
+
* await fromResult(err(error)).resolve(); // Err(error)
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export declare const fromResult: typeof ResultAsync.fromResult;
|
|
57
|
+
/**
|
|
58
|
+
* Bind a sync {@link Result} into an async step without widening defects.
|
|
59
|
+
* Dual-form: data-first for one-shots, data-last (curried) for `pipe`/`flow`.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* asyncAfter(result, (u) => tryAsync(db.insert(u), toErr)); // data-first
|
|
64
|
+
* asyncAfter((u) => tryAsync(db.insert(u), toErr)); // data-last
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare const asyncAfter: {
|
|
68
|
+
<T, U, E, F>(result: Result<T, E>, fn: (value: T) => ResultAsync<U, F>): ResultAsync<U, E | F>;
|
|
69
|
+
<T, U, F>(fn: (value: T) => ResultAsync<U, F>): <E>(result: Result<T, E>) => ResultAsync<U, E | F>;
|
|
70
|
+
};
|
|
71
|
+
type AnyResult = Result<unknown, unknown>;
|
|
72
|
+
/**
|
|
73
|
+
* Lifts a `(...args) => Promise<Result<T, E>>` function (typical of interop or
|
|
74
|
+
* boundary code) into one returning `(...args) => ResultAsync<T, E>`. Expected
|
|
75
|
+
* `Err`s pass through; an unexpected promise rejection (a defect) is routed via
|
|
76
|
+
* `onDefect`, defaulting to {@link UnexpectedError} and widening the error union.
|
|
77
|
+
*
|
|
78
|
+
* @param fn - a function returning a promise that already yields a `Result`
|
|
79
|
+
* @param onDefect - maps an unexpected rejection to the `Err` channel
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* const loadUser = fromAsync(
|
|
84
|
+
* (id: string): Promise<Result<User, NotFound>> => api.getUser(id),
|
|
85
|
+
* );
|
|
86
|
+
* const ra = loadUser("u1"); // ResultAsync<User, NotFound | UnexpectedError>
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export declare const fromAsync: <A extends readonly unknown[], R extends AnyResult>(fn: (...args: A) => Promise<R>, onDefect?: (error: unknown) => InferErr<R> | UnexpectedError) => ((...args: A) => ResultAsync<InferOk<R>, InferErr<R> | UnexpectedError>);
|
|
90
|
+
/**
|
|
91
|
+
* Convenience wrapper over {@link fromPromise} with default `Error`
|
|
92
|
+
* normalization. Call without `onReject` to get `ResultAsync<T, Error>`,
|
|
93
|
+
* or pass a custom mapper for a typed error.
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```ts
|
|
97
|
+
* // Default: rejection → Err(Error)
|
|
98
|
+
* const a = tryAsync(db.users.insert(row));
|
|
99
|
+
*
|
|
100
|
+
* // Custom: typed error
|
|
101
|
+
* const b = tryAsync(db.users.insert(row), (e): DbError => ({
|
|
102
|
+
* kind: "db",
|
|
103
|
+
* cause: e,
|
|
104
|
+
* }));
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
export declare function tryAsync<T>(promise: PromiseLike<T>): ResultAsync<T, Error>;
|
|
108
|
+
export declare function tryAsync<T, E>(promise: PromiseLike<T>, onReject: (error: unknown) => E): ResultAsync<T, E>;
|
|
109
|
+
export {};
|
|
@@ -1,56 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* (tagged union) or a {@link ResultAsync} (class carrier), resolving to
|
|
8
|
-
* `never` for anything else. Every module that needs to extract a payload
|
|
9
|
-
* type imports from here; do not redeclare local copies.
|
|
10
|
-
*
|
|
11
|
-
* Internal only: not listed in package.json `exports` or tsup entries.
|
|
12
|
-
* The package index re-exports the pair under the same names.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Extracts the `Ok` value type from a {@link Result} or {@link ResultAsync},
|
|
17
|
-
* resolving to `never` for any other type.
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```ts
|
|
21
|
-
* type T = InferOk<Result<number, string>>; // number
|
|
22
|
-
* type U = InferOk<ResultAsync<User, AppError>>; // User
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
type InferOk<R> = R extends {
|
|
26
|
-
_tag: "Ok";
|
|
27
|
-
readonly value: infer T;
|
|
28
|
-
} ? T : R extends ResultAsync<infer T, unknown> ? T : never;
|
|
1
|
+
import type { InferErr, InferOk } from "./internal/infer.js";
|
|
2
|
+
import type { Payload } from "./internal/payload.js";
|
|
3
|
+
import type { Result } from "./types.js";
|
|
4
|
+
import { UnexpectedError } from "./types.js";
|
|
5
|
+
type PromiseFactory<T, E> = () => Promise<Result<T, E>>;
|
|
6
|
+
type AnyResultAsync = ResultAsync<unknown, unknown>;
|
|
29
7
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* type E = InferErr<Result<number, string>>; // string
|
|
36
|
-
* type F = InferErr<ResultAsync<User, AppError>>; // AppError
|
|
37
|
-
* ```
|
|
8
|
+
* Mirrors the sync `CombineTuple` in `./collections.ts`: the element check
|
|
9
|
+
* lives in the RETURN type, never in the parameter constraint. A
|
|
10
|
+
* `ResultAsync<unknown, unknown>` constraint contextually types the arguments
|
|
11
|
+
* and widens `okAsync(1)`'s defaulted `E = never` to `unknown`, so call sites
|
|
12
|
+
* would need hoisted intermediates to keep their error union.
|
|
38
13
|
*/
|
|
39
|
-
type
|
|
40
|
-
_tag: "Err";
|
|
41
|
-
readonly error: infer E;
|
|
42
|
-
} ? E : R extends ResultAsync<unknown, infer E> ? E : never;
|
|
43
|
-
|
|
44
|
-
type PromiseFactory<T, E> = () => Promise<Result<T, E>>;
|
|
45
|
-
type CombineTupleAsync<R extends readonly ResultAsync<unknown, unknown>[]> = ResultAsync<{
|
|
14
|
+
type CombineTupleAsync<R extends readonly unknown[]> = R[number] extends AnyResultAsync ? ResultAsync<{
|
|
46
15
|
[K in keyof R]: InferOk<R[K]>;
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
|
|
16
|
+
}, InferErr<R[number]>> : never;
|
|
17
|
+
/** Mirrors `TupleGuard` in `./collections.ts` — see the note there. */
|
|
18
|
+
type AsyncTupleGuard<R extends readonly unknown[]> = R[number] extends AnyResultAsync ? [] : [error: "expected an array of ResultAsync values"];
|
|
50
19
|
/**
|
|
51
20
|
* Async railway carrier. Wraps a deferred `Promise<Result<T, E>>` and exposes
|
|
52
|
-
* the same dual-track transforms as the sync {@link Result} — `map`, `
|
|
53
|
-
* `recover`, `tap`, `match`. The public API never surfaces `Promise<Result<…>>`
|
|
21
|
+
* the same dual-track transforms as the sync {@link Result} — `map`, `mapErr`,
|
|
22
|
+
* `bimap`, `flatMap`, `recover`, `tap`, `match`. The public API never surfaces `Promise<Result<…>>`
|
|
54
23
|
* directly: `await` the instance (it is thenable) or call {@link resolve} /
|
|
55
24
|
* {@link match} to settle it.
|
|
56
25
|
*
|
|
@@ -69,7 +38,7 @@ type CombineTupleAsync<R extends readonly ResultAsync<unknown, unknown>[]> = Res
|
|
|
69
38
|
* const r = await user; // Result<Profile, AppError>
|
|
70
39
|
* ```
|
|
71
40
|
*/
|
|
72
|
-
declare class ResultAsync<T, E> {
|
|
41
|
+
export declare class ResultAsync<T, E> {
|
|
73
42
|
protected readonly run: PromiseFactory<T, E>;
|
|
74
43
|
private promise;
|
|
75
44
|
protected constructor(run: PromiseFactory<T, E>);
|
|
@@ -129,13 +98,15 @@ declare class ResultAsync<T, E> {
|
|
|
129
98
|
* const r = ResultAsync.ok(42); // ResultAsync<number, never>
|
|
130
99
|
* ```
|
|
131
100
|
*/
|
|
132
|
-
static ok<T>(value: T): ResultAsync<T, never>;
|
|
101
|
+
static ok<const T extends Payload[]>(value: T): ResultAsync<T, never>;
|
|
102
|
+
static ok<const T>(value: T): ResultAsync<T, never>;
|
|
133
103
|
static ok<T, E>(value: T): ResultAsync<T, E>;
|
|
134
104
|
/**
|
|
135
105
|
* Fantasy Land `pure` — alias of {@link ResultAsync.ok}. One lift name
|
|
136
106
|
* shared across the trio (`of` / `Maybe.of` / `ResultAsync.of`).
|
|
137
107
|
*/
|
|
138
|
-
static of<T>(value: T): ResultAsync<T, never>;
|
|
108
|
+
static of<const T extends Payload[]>(value: T): ResultAsync<T, never>;
|
|
109
|
+
static of<const T>(value: T): ResultAsync<T, never>;
|
|
139
110
|
static of<T, E>(value: T): ResultAsync<T, E>;
|
|
140
111
|
/**
|
|
141
112
|
* Lifts an error into an `Err` async result.
|
|
@@ -145,7 +116,9 @@ declare class ResultAsync<T, E> {
|
|
|
145
116
|
* const r = ResultAsync.err({ kind: "not_found" as const });
|
|
146
117
|
* ```
|
|
147
118
|
*/
|
|
148
|
-
static err<
|
|
119
|
+
static err<const E extends Payload[]>(error: E): ResultAsync<never, E>;
|
|
120
|
+
static err<const E>(error: E): ResultAsync<never, E>;
|
|
121
|
+
static err<T, E>(error: E): ResultAsync<T, E>;
|
|
149
122
|
/**
|
|
150
123
|
* Lifts an existing `Promise<Result<T, E>>` (e.g. from interop code) into a
|
|
151
124
|
* {@link ResultAsync}. A thrown defect is routed through `onDefect`,
|
|
@@ -179,7 +152,7 @@ declare class ResultAsync<T, E> {
|
|
|
179
152
|
*
|
|
180
153
|
* @example
|
|
181
154
|
* ```ts
|
|
182
|
-
* const combined = ResultAsync.combineTuple([loadCfg(), loadCatalog()]
|
|
155
|
+
* const combined = ResultAsync.combineTuple([loadCfg(), loadCatalog()]);
|
|
183
156
|
* // ResultAsync<readonly [Cfg, Catalog], CfgError | CatalogError>
|
|
184
157
|
* const r = await combined;
|
|
185
158
|
* if (isOk(r)) {
|
|
@@ -187,7 +160,7 @@ declare class ResultAsync<T, E> {
|
|
|
187
160
|
* }
|
|
188
161
|
* ```
|
|
189
162
|
*/
|
|
190
|
-
static combineTuple<const R extends readonly
|
|
163
|
+
static combineTuple<const R extends readonly unknown[]>(results: R, ..._guard: AsyncTupleGuard<R>): CombineTupleAsync<R>;
|
|
191
164
|
/**
|
|
192
165
|
* Like {@link combineTuple}, but starts every branch before awaiting (wall-clock
|
|
193
166
|
* parallel for independent IO). On failure, returns the first `Err` in input order.
|
|
@@ -198,10 +171,10 @@ declare class ResultAsync<T, E> {
|
|
|
198
171
|
* const combined = ResultAsync.combineTupleParallel([
|
|
199
172
|
* loadProfile(id),
|
|
200
173
|
* loadMetrics(id),
|
|
201
|
-
* ]
|
|
174
|
+
* ]);
|
|
202
175
|
* ```
|
|
203
176
|
*/
|
|
204
|
-
static combineTupleParallel<const R extends readonly
|
|
177
|
+
static combineTupleParallel<const R extends readonly unknown[]>(results: R, ..._guard: AsyncTupleGuard<R>): CombineTupleAsync<R>;
|
|
205
178
|
/**
|
|
206
179
|
* Transforms the `Ok` value, passing `Err` through unchanged.
|
|
207
180
|
*
|
|
@@ -221,6 +194,16 @@ declare class ResultAsync<T, E> {
|
|
|
221
194
|
* ```
|
|
222
195
|
*/
|
|
223
196
|
mapErr<F>(fn: (error: E) => F): ResultAsync<T, F>;
|
|
197
|
+
/**
|
|
198
|
+
* Transforms both tracks in one step — the async mirror of the sync
|
|
199
|
+
* `bimap`. Exactly one of `onOk` / `onErr` runs.
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* ```ts
|
|
203
|
+
* load(id).bimap(toDto, (e): AppError => ({ kind: "load", cause: e }));
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
bimap<U, F>(onOk: (value: T) => U, onErr: (error: E) => F): ResultAsync<U, F>;
|
|
224
207
|
/**
|
|
225
208
|
* Canonical bind — chains a step that itself returns a `ResultAsync` or sync
|
|
226
209
|
* `Result`, short-circuiting on `Err`. Error types accumulate (`E | F`).
|
|
@@ -328,5 +311,9 @@ declare class ResultAsync<T, E> {
|
|
|
328
311
|
*/
|
|
329
312
|
then<R1 = Result<T, E>, R2 = never>(onfulfilled?: ((value: Result<T, E>) => R1 | PromiseLike<R1>) | undefined | null, onrejected?: ((reason: unknown) => R2 | PromiseLike<R2>) | undefined | null): Promise<R1 | R2>;
|
|
330
313
|
}
|
|
331
|
-
|
|
332
|
-
|
|
314
|
+
export {};
|
|
315
|
+
/**
|
|
316
|
+
* The free-function lift helpers (`okAsync`, `errAsync`, `fromPromise`,
|
|
317
|
+
* `fromSafePromise`, `fromResult`, `fromAsync`, `asyncAfter`, `tryAsync`)
|
|
318
|
+
* live in `./async-lift.ts` and are re-exported from the package index.
|
|
319
|
+
*/
|