@onrails/result 0.3.1 → 0.4.1
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 +4 -3
- package/README.md +166 -13
- package/RECIPES.md +6 -4
- 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} +42 -58
- package/dist/{async-DcXR7k9K.d.cts → async.d.ts} +42 -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 -28
- package/dist/fluent.cjs.map +1 -1
- package/dist/fluent.d.cts +4 -34
- package/dist/fluent.d.ts +4 -34
- package/dist/fluent.js +16 -28
- 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 +12 -0
- package/dist/internal/ctor.d.ts +12 -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/lock.d.cts +16 -0
- package/dist/internal/lock.d.ts +16 -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 +315 -0
- package/dist/result.d.ts +315 -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 { Locked } from "./internal/lock.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,13 @@ 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
|
|
101
|
+
static ok<const T>(value: T): ResultAsync<Locked<T>, never>;
|
|
133
102
|
static ok<T, E>(value: T): ResultAsync<T, E>;
|
|
134
103
|
/**
|
|
135
104
|
* Fantasy Land `pure` — alias of {@link ResultAsync.ok}. One lift name
|
|
136
105
|
* shared across the trio (`of` / `Maybe.of` / `ResultAsync.of`).
|
|
137
106
|
*/
|
|
138
|
-
static of<T>(value: T): ResultAsync<T
|
|
107
|
+
static of<const T>(value: T): ResultAsync<Locked<T>, never>;
|
|
139
108
|
static of<T, E>(value: T): ResultAsync<T, E>;
|
|
140
109
|
/**
|
|
141
110
|
* Lifts an error into an `Err` async result.
|
|
@@ -145,7 +114,8 @@ declare class ResultAsync<T, E> {
|
|
|
145
114
|
* const r = ResultAsync.err({ kind: "not_found" as const });
|
|
146
115
|
* ```
|
|
147
116
|
*/
|
|
148
|
-
static err<
|
|
117
|
+
static err<const E>(error: E): ResultAsync<never, Locked<E>>;
|
|
118
|
+
static err<T, E>(error: E): ResultAsync<T, E>;
|
|
149
119
|
/**
|
|
150
120
|
* Lifts an existing `Promise<Result<T, E>>` (e.g. from interop code) into a
|
|
151
121
|
* {@link ResultAsync}. A thrown defect is routed through `onDefect`,
|
|
@@ -179,7 +149,7 @@ declare class ResultAsync<T, E> {
|
|
|
179
149
|
*
|
|
180
150
|
* @example
|
|
181
151
|
* ```ts
|
|
182
|
-
* const combined = ResultAsync.combineTuple([loadCfg(), loadCatalog()]
|
|
152
|
+
* const combined = ResultAsync.combineTuple([loadCfg(), loadCatalog()]);
|
|
183
153
|
* // ResultAsync<readonly [Cfg, Catalog], CfgError | CatalogError>
|
|
184
154
|
* const r = await combined;
|
|
185
155
|
* if (isOk(r)) {
|
|
@@ -187,7 +157,7 @@ declare class ResultAsync<T, E> {
|
|
|
187
157
|
* }
|
|
188
158
|
* ```
|
|
189
159
|
*/
|
|
190
|
-
static combineTuple<const R extends readonly
|
|
160
|
+
static combineTuple<const R extends readonly unknown[]>(results: R, ..._guard: AsyncTupleGuard<R>): CombineTupleAsync<R>;
|
|
191
161
|
/**
|
|
192
162
|
* Like {@link combineTuple}, but starts every branch before awaiting (wall-clock
|
|
193
163
|
* parallel for independent IO). On failure, returns the first `Err` in input order.
|
|
@@ -198,10 +168,10 @@ declare class ResultAsync<T, E> {
|
|
|
198
168
|
* const combined = ResultAsync.combineTupleParallel([
|
|
199
169
|
* loadProfile(id),
|
|
200
170
|
* loadMetrics(id),
|
|
201
|
-
* ]
|
|
171
|
+
* ]);
|
|
202
172
|
* ```
|
|
203
173
|
*/
|
|
204
|
-
static combineTupleParallel<const R extends readonly
|
|
174
|
+
static combineTupleParallel<const R extends readonly unknown[]>(results: R, ..._guard: AsyncTupleGuard<R>): CombineTupleAsync<R>;
|
|
205
175
|
/**
|
|
206
176
|
* Transforms the `Ok` value, passing `Err` through unchanged.
|
|
207
177
|
*
|
|
@@ -221,6 +191,16 @@ declare class ResultAsync<T, E> {
|
|
|
221
191
|
* ```
|
|
222
192
|
*/
|
|
223
193
|
mapErr<F>(fn: (error: E) => F): ResultAsync<T, F>;
|
|
194
|
+
/**
|
|
195
|
+
* Transforms both tracks in one step — the async mirror of the sync
|
|
196
|
+
* `bimap`. Exactly one of `onOk` / `onErr` runs.
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```ts
|
|
200
|
+
* load(id).bimap(toDto, (e): AppError => ({ kind: "load", cause: e }));
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
bimap<U, F>(onOk: (value: T) => U, onErr: (error: E) => F): ResultAsync<U, F>;
|
|
224
204
|
/**
|
|
225
205
|
* Canonical bind — chains a step that itself returns a `ResultAsync` or sync
|
|
226
206
|
* `Result`, short-circuiting on `Err`. Error types accumulate (`E | F`).
|
|
@@ -328,5 +308,9 @@ declare class ResultAsync<T, E> {
|
|
|
328
308
|
*/
|
|
329
309
|
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
310
|
}
|
|
331
|
-
|
|
332
|
-
|
|
311
|
+
export {};
|
|
312
|
+
/**
|
|
313
|
+
* The free-function lift helpers (`okAsync`, `errAsync`, `fromPromise`,
|
|
314
|
+
* `fromSafePromise`, `fromResult`, `fromAsync`, `asyncAfter`, `tryAsync`)
|
|
315
|
+
* live in `./async-lift.ts` and are re-exported from the package index.
|
|
316
|
+
*/
|