@onrails/result 0.1.2 → 0.1.3
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 +0 -1
- package/README.md +10 -17
- package/RECIPES.md +143 -21
- package/dist/{async-CCA1yK8q.d.cts → async-CH8Cs8KE.d.cts} +14 -0
- package/dist/{async-DH_-dNIo.d.ts → async-DKISqIBc.d.ts} +14 -0
- package/dist/compat/neverthrow.cjs +30 -12
- package/dist/compat/neverthrow.cjs.map +1 -1
- package/dist/compat/neverthrow.d.cts +1 -1
- package/dist/compat/neverthrow.d.ts +1 -1
- package/dist/compat/neverthrow.js +30 -12
- package/dist/compat/neverthrow.js.map +1 -1
- package/dist/extra.cjs +17 -10
- package/dist/extra.cjs.map +1 -1
- package/dist/extra.d.cts +10 -2
- package/dist/extra.d.ts +10 -2
- package/dist/extra.js +17 -10
- package/dist/extra.js.map +1 -1
- package/dist/fluent.cjs.map +1 -1
- package/dist/fluent.d.cts +3 -1
- package/dist/fluent.d.ts +3 -1
- package/dist/fluent.js.map +1 -1
- package/dist/index.cjs +30 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -5
- package/dist/index.d.ts +21 -5
- package/dist/index.js +30 -12
- package/dist/index.js.map +1 -1
- package/dist/interop.cjs +30 -12
- package/dist/interop.cjs.map +1 -1
- package/dist/interop.d.cts +1 -1
- package/dist/interop.d.ts +1 -1
- package/dist/interop.js +30 -12
- package/dist/interop.js.map +1 -1
- package/dist/railway.cjs +30 -12
- package/dist/railway.cjs.map +1 -1
- package/dist/railway.d.cts +1 -1
- package/dist/railway.d.ts +1 -1
- package/dist/railway.js +30 -12
- package/dist/railway.js.map +1 -1
- package/dist/try-gen.cjs.map +1 -1
- package/dist/try-gen.js.map +1 -1
- package/dist/validation.cjs.map +1 -1
- package/dist/validation.js.map +1 -1
- package/package.json +1 -6
- package/dist/mcp.cjs +0 -273
- package/dist/mcp.cjs.map +0 -1
- package/dist/mcp.d.cts +0 -62
- package/dist/mcp.d.ts +0 -62
- package/dist/mcp.js +0 -265
- package/dist/mcp.js.map +0 -1
package/DESIGN.md
CHANGED
|
@@ -96,7 +96,6 @@ Use `flatMap` for dependent checks where later checks need earlier successful va
|
|
|
96
96
|
| `@onrails/result/fluent` | Dot-style chains |
|
|
97
97
|
| `@onrails/result/extra` | Error-type utilities |
|
|
98
98
|
| `@onrails/result/interop` | Promise/ResultAsync boundary helpers |
|
|
99
|
-
| `@onrails/result/mcp` | MCP / HTTP boundary helpers |
|
|
100
99
|
| `@onrails/result/pipe` | Variadic `flow` for point-free composition |
|
|
101
100
|
| `@onrails/result/railway` | Named workflow builder and reusable step factories |
|
|
102
101
|
| `@onrails/result/try-gen` | Sync generator-style `Result` sugar |
|
package/README.md
CHANGED
|
@@ -132,13 +132,18 @@ else console.error(r.error);
|
|
|
132
132
|
|
|
133
133
|
## Match and unwrap helpers
|
|
134
134
|
|
|
135
|
-
`
|
|
135
|
+
`match` is the canonical positional fold. If a file imports `match` from `@onrails/pattern` or `ts-pattern`, resolve the collision by using namespace imports:
|
|
136
136
|
|
|
137
137
|
```ts
|
|
138
|
-
import
|
|
138
|
+
import * as R from "@onrails/result";
|
|
139
139
|
import { match } from "ts-pattern";
|
|
140
|
+
|
|
141
|
+
R.match(result, onOk, onErr);
|
|
140
142
|
```
|
|
141
143
|
|
|
144
|
+
> [!NOTE]
|
|
145
|
+
> `matchResult` is deprecated and will be removed in the next major version.
|
|
146
|
+
|
|
142
147
|
`unwrapOk` and `unwrapErr` are test/assertion helpers. Prefer `match`, `isOk`, or `isErr` in production control flow.
|
|
143
148
|
|
|
144
149
|
```ts
|
|
@@ -147,17 +152,6 @@ import { unwrapOk } from "@onrails/result";
|
|
|
147
152
|
expect(unwrapOk(parseConfig(raw))).toEqual(expected);
|
|
148
153
|
```
|
|
149
154
|
|
|
150
|
-
## MCP / HTTP boundaries
|
|
151
|
-
|
|
152
|
-
```ts
|
|
153
|
-
import { toToolResponseAsync, unwrapFetchResultAsync } from "@onrails/result/mcp";
|
|
154
|
-
|
|
155
|
-
const ra = unwrapFetchResultAsync(
|
|
156
|
-
client.GET("/tokens/{id}"),
|
|
157
|
-
({ error, response }) => new PrintrApiError(response.status, detail),
|
|
158
|
-
);
|
|
159
|
-
return toToolResponseAsync(ra);
|
|
160
|
-
```
|
|
161
155
|
|
|
162
156
|
## `tryGen` — sync `?`
|
|
163
157
|
|
|
@@ -173,12 +167,12 @@ const out = tryGen(() => {
|
|
|
173
167
|
});
|
|
174
168
|
```
|
|
175
169
|
|
|
176
|
-
Use `
|
|
170
|
+
Use `ResultAsync.combineTuple` (or `parallelTupleAsync` when branches should overlap) when combining heterogeneous async results and destructuring the result:
|
|
177
171
|
|
|
178
172
|
```ts
|
|
179
|
-
import {
|
|
173
|
+
import { ResultAsync } from "@onrails/result";
|
|
180
174
|
|
|
181
|
-
const combined =
|
|
175
|
+
const combined = ResultAsync.combineTuple([
|
|
182
176
|
loadSettings(),
|
|
183
177
|
loadModelCatalog(),
|
|
184
178
|
] as const);
|
|
@@ -314,7 +308,6 @@ import { ResultAsync, Result, ok, err, okAsync, errAsync } from "@onrails/result
|
|
|
314
308
|
| `@onrails/result/fluent` | `fluent()`, `fluentAsync()` |
|
|
315
309
|
| `@onrails/result/extra` | Error-type utilities |
|
|
316
310
|
| `@onrails/result/interop` | `fromAsync`, `fromResult`, `asyncAfter` |
|
|
317
|
-
| `@onrails/result/mcp` | MCP / openapi-fetch helpers |
|
|
318
311
|
| `@onrails/result/pipe` | `flow` (variadic point-free composition) |
|
|
319
312
|
| `@onrails/result/railway` | `Railway`, `railway`, named workflow helpers |
|
|
320
313
|
| `@onrails/result/try-gen` | `tryGen`, `yieldResult`, `$` |
|
package/RECIPES.md
CHANGED
|
@@ -11,6 +11,68 @@ map(fn)(result); // curried, data-last — feeds `pipe(...)` and `flow(...)`
|
|
|
11
11
|
|
|
12
12
|
See [README.md](./README.md) for the API reference and decision tree.
|
|
13
13
|
|
|
14
|
+
## Composition Guidelines (The Four Tiers)
|
|
15
|
+
|
|
16
|
+
To keep codebases readable and consistent, follow the four-tier decision guideline when choosing how to compose operations. Use the simplest pattern that fits your workflow:
|
|
17
|
+
|
|
18
|
+
| Tier | Use Case | Recommended Pattern |
|
|
19
|
+
| ---- | -------- | ------------------- |
|
|
20
|
+
| **1** | 1–2 steps, linear | direct data-first calls or method chains |
|
|
21
|
+
| **2** | 3+ steps, linear | `pipe` or `flow` |
|
|
22
|
+
| **3** | branchy, value reused | `tryGen` escape hatch |
|
|
23
|
+
| **4** | 4+ named steps, mixed IO | `Railway` or `railway()` steps |
|
|
24
|
+
|
|
25
|
+
`/fluent` is documented as app-edge sugar only — never in library or service internals.
|
|
26
|
+
|
|
27
|
+
### Tier 1: 1–2 steps, linear (Direct data-first call or method chain)
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { map } from "@onrails/result";
|
|
31
|
+
|
|
32
|
+
const normalized = map(rawResult, normalize);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Tier 2: 3+ steps, linear (pipe / flow)
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { flow, map, flatMap, recover } from "@onrails/result";
|
|
39
|
+
|
|
40
|
+
const process = flow(
|
|
41
|
+
map(normalize),
|
|
42
|
+
flatMap(validate),
|
|
43
|
+
recover(fallback)
|
|
44
|
+
);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Tier 3: Branchy, value reused (tryGen)
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { tryGen, $ } from "@onrails/result/try-gen";
|
|
51
|
+
import { ok, err } from "@onrails/result";
|
|
52
|
+
|
|
53
|
+
const stepResult = tryGen(() => {
|
|
54
|
+
const user = $(authenticate(req));
|
|
55
|
+
const post = $(fetchPost(postId));
|
|
56
|
+
if (post.authorId !== user.id && !user.isAdmin) {
|
|
57
|
+
return err({ kind: "unauthorized" as const });
|
|
58
|
+
}
|
|
59
|
+
return ok(post);
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Tier 4: 4+ named steps, mixed IO (Railway context builder)
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { Railway } from "@onrails/result/railway";
|
|
67
|
+
|
|
68
|
+
const workflow = Railway
|
|
69
|
+
.fromSync("id", () => IdSchema.parse(raw), toError)
|
|
70
|
+
.fromPromise("row", ({ id }) => db.profiles.find(id), toError)
|
|
71
|
+
.require("profile", "row", ({ id }) => ({ kind: "not_found" as const, id }))
|
|
72
|
+
.derive("normalized", ({ profile }) => normalizeProfile(profile))
|
|
73
|
+
.select(({ normalized }) => normalized);
|
|
74
|
+
```
|
|
75
|
+
|
|
14
76
|
---
|
|
15
77
|
|
|
16
78
|
## 1. Reusable parser builder via `flow`
|
|
@@ -26,16 +88,8 @@ type SchemaError = { kind: "schema"; field: string };
|
|
|
26
88
|
|
|
27
89
|
const parseJsonWith = <T>(schema: { parse: (x: unknown) => T }) =>
|
|
28
90
|
flow(
|
|
29
|
-
trySync(
|
|
30
|
-
|
|
31
|
-
(e): ParseError => ({ kind: "parse", message: String(e) }),
|
|
32
|
-
),
|
|
33
|
-
flatMap((data: unknown) =>
|
|
34
|
-
trySync(
|
|
35
|
-
() => schema.parse(data),
|
|
36
|
-
(e): SchemaError => ({ kind: "schema", field: String(e) }),
|
|
37
|
-
)(),
|
|
38
|
-
),
|
|
91
|
+
trySync(JSON.parse, (e): ParseError => ({ kind: "parse", message: String(e) })),
|
|
92
|
+
flatMap(trySync(schema.parse, (e): SchemaError => ({ kind: "schema", field: String(e) }))),
|
|
39
93
|
);
|
|
40
94
|
|
|
41
95
|
const parseUser = parseJsonWith(UserSchema);
|
|
@@ -167,7 +221,7 @@ const buildSummary = (userId: string) =>
|
|
|
167
221
|
// ResultAsync<Summary, ProfileError | MetricsError>
|
|
168
222
|
```
|
|
169
223
|
|
|
170
|
-
`parallelTupleAsync` preserves tuple positions, so destructuring stays type-safe. Use `
|
|
224
|
+
`parallelTupleAsync` preserves tuple positions, so destructuring stays type-safe. Use static `ResultAsync.combineTuple` if branches must run left-to-right.
|
|
171
225
|
|
|
172
226
|
---
|
|
173
227
|
|
|
@@ -181,24 +235,24 @@ import { flatMap, recover, ok, err } from "@onrails/result";
|
|
|
181
235
|
|
|
182
236
|
type LengthError = { kind: "len"; min: number };
|
|
183
237
|
type CharsError = { kind: "chars"; bad: string };
|
|
238
|
+
type TooShortError = { kind: "too_short"; min: number };
|
|
184
239
|
|
|
185
|
-
const requireMin = (min: number) =>
|
|
186
|
-
|
|
240
|
+
const requireMin = (min: number) => (s: string) =>
|
|
241
|
+
s.length >= min ? ok(s) : err({ kind: "len" as const, min });
|
|
187
242
|
|
|
188
|
-
const requireAscii =
|
|
189
|
-
(s:
|
|
190
|
-
/^[\x20-\x7e]*$/.test(s) ? ok(s) : err({ kind: "chars" as const, bad: s }),
|
|
191
|
-
);
|
|
243
|
+
const requireAscii = (s: string) =>
|
|
244
|
+
/^[\x20-\x7e]*$/.test(s) ? ok(s) : err({ kind: "chars" as const, bad: s });
|
|
192
245
|
|
|
193
246
|
const validateUsername = flow(
|
|
194
247
|
(raw: string) => ok(raw.trim()),
|
|
195
248
|
flatMap(requireMin(3)),
|
|
196
249
|
flatMap(requireAscii),
|
|
197
|
-
recover(
|
|
198
|
-
e
|
|
250
|
+
recover(
|
|
251
|
+
(e: LengthError | CharsError): Result<string, TooShortError | CharsError> =>
|
|
252
|
+
e.kind === "len" ? err({ kind: "too_short" as const, min: e.min }) : err(e),
|
|
199
253
|
),
|
|
200
254
|
);
|
|
201
|
-
// (raw: string) => Result<string,
|
|
255
|
+
// (raw: string) => Result<string, TooShortError | CharsError>
|
|
202
256
|
```
|
|
203
257
|
|
|
204
258
|
`requireMin(3)` is a curried factory — `flow` strings it into the pipeline alongside `requireAscii`. None of the inner steps mention the value.
|
|
@@ -301,7 +355,12 @@ import { map, recover, ok, err, type Result } from "@onrails/result";
|
|
|
301
355
|
|
|
302
356
|
type FetchError = { kind: "network" } | { kind: "fatal"; message: string };
|
|
303
357
|
|
|
304
|
-
|
|
358
|
+
type FetchConfig = {
|
|
359
|
+
readonly fallback?: Body;
|
|
360
|
+
readonly rethrow: (e: FetchError) => boolean;
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const fetchWith = (cfg: FetchConfig) =>
|
|
305
364
|
flow(
|
|
306
365
|
fetchSync,
|
|
307
366
|
recover((e: FetchError) =>
|
|
@@ -350,6 +409,69 @@ Each sub-flow has a clear purpose; the top-level `ingest` reads as a sentence. E
|
|
|
350
409
|
|
|
351
410
|
---
|
|
352
411
|
|
|
412
|
+
## 12. Async pipelines via `ResultAsync` composition
|
|
413
|
+
|
|
414
|
+
When composing functions that return `ResultAsync` values, `flow` chains them by wrapping the async transforms in arrow functions. The resulting pipeline remains point-free over the input.
|
|
415
|
+
|
|
416
|
+
```ts
|
|
417
|
+
import { flow } from "@onrails/result/pipe";
|
|
418
|
+
import { type ResultAsync } from "@onrails/result";
|
|
419
|
+
|
|
420
|
+
type Profile = { id: string; name: string };
|
|
421
|
+
type Metrics = { score: number };
|
|
422
|
+
type Summary = { name: string; score: number };
|
|
423
|
+
|
|
424
|
+
declare const fetchProfile: (id: string) => ResultAsync<Profile, Error>;
|
|
425
|
+
declare const fetchMetrics: (p: Profile) => ResultAsync<Metrics, Error>;
|
|
426
|
+
declare const formatSummary: (p: Profile, m: Metrics) => Summary;
|
|
427
|
+
|
|
428
|
+
const loadSummary = flow(
|
|
429
|
+
fetchProfile,
|
|
430
|
+
(ra) => ra.flatMap((profile) =>
|
|
431
|
+
fetchMetrics(profile).map((metrics) => formatSummary(profile, metrics))
|
|
432
|
+
),
|
|
433
|
+
);
|
|
434
|
+
// (id: string) => ResultAsync<Summary, Error>
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
---
|
|
438
|
+
|
|
439
|
+
## 13. Functional Railway pipelines (`railway` + named steps)
|
|
440
|
+
|
|
441
|
+
Use `railway(input, ...steps)` from `@onrails/result/railway` to build a multi-step async pipeline using point-free reusable step wrappers. If any step is async, the entire pipeline resolves to a `ResultAsync`.
|
|
442
|
+
|
|
443
|
+
```ts
|
|
444
|
+
import { railway, parseWith, fromPromiseNamed, deriveNamed, select } from "@onrails/result/railway";
|
|
445
|
+
import { type ResultAsync } from "@onrails/result";
|
|
446
|
+
|
|
447
|
+
type Dashboard = { title: string };
|
|
448
|
+
|
|
449
|
+
type IdContext = { readonly id: string };
|
|
450
|
+
type ProfileContext = { readonly profile: Profile };
|
|
451
|
+
type TitleContext = { readonly title: string };
|
|
452
|
+
|
|
453
|
+
declare const IdSchema: { parse: (x: unknown) => string };
|
|
454
|
+
declare const fetchProfile: (id: string) => Promise<Profile>;
|
|
455
|
+
declare const toError: (e: unknown) => Error;
|
|
456
|
+
|
|
457
|
+
const loadDashboard = (rawId: unknown): ResultAsync<Dashboard, Error> =>
|
|
458
|
+
railway(
|
|
459
|
+
rawId,
|
|
460
|
+
parseWith(IdSchema, toError).as("id"),
|
|
461
|
+
fromPromiseNamed(
|
|
462
|
+
"profile",
|
|
463
|
+
({ id }: IdContext) => fetchProfile(id),
|
|
464
|
+
toError,
|
|
465
|
+
),
|
|
466
|
+
deriveNamed("title", ({ profile }: ProfileContext) =>
|
|
467
|
+
profile.name.toUpperCase(),
|
|
468
|
+
),
|
|
469
|
+
select(({ title }: TitleContext) => ({ title })),
|
|
470
|
+
);
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
353
475
|
## When NOT to go point-free
|
|
354
476
|
|
|
355
477
|
Pipelines should read top-to-bottom and each step should do one obvious thing. Reach for `pipe` when:
|
|
@@ -14,6 +14,7 @@ type CombineTupleAsync<R extends readonly ResultAsync<unknown, unknown>[]> = Res
|
|
|
14
14
|
*/
|
|
15
15
|
declare class ResultAsync<T, E> {
|
|
16
16
|
protected readonly run: PromiseFactory<T, E>;
|
|
17
|
+
private promise;
|
|
17
18
|
protected constructor(run: PromiseFactory<T, E>);
|
|
18
19
|
static fromResult<T, E>(result: Result<T, E>): ResultAsync<T, E>;
|
|
19
20
|
static fromPromise<T, E>(promise: PromiseLike<T>, onReject: (error: unknown) => E): ResultAsync<T, E>;
|
|
@@ -43,6 +44,10 @@ declare class ResultAsync<T, E> {
|
|
|
43
44
|
andThen<U, F = E>(fn: (value: T) => ResultAsync<U, F> | Result<U, F> | {
|
|
44
45
|
inner: Result<U, F>;
|
|
45
46
|
}): ResultAsync<U, E | F>;
|
|
47
|
+
/**
|
|
48
|
+
* Alias for {@link flatMap}.
|
|
49
|
+
* @deprecated Use {@link flatMap} or keep/use compat {@link andThen} instead.
|
|
50
|
+
*/
|
|
46
51
|
chain<U, F = E>(fn: (value: T) => ResultAsync<U, F> | Result<U, F> | {
|
|
47
52
|
inner: Result<U, F>;
|
|
48
53
|
}): ResultAsync<U, E | F>;
|
|
@@ -51,7 +56,15 @@ declare class ResultAsync<T, E> {
|
|
|
51
56
|
tap(fn: (value: T) => void): ResultAsync<T, E>;
|
|
52
57
|
tapErr(fn: (error: E) => void): ResultAsync<T, E>;
|
|
53
58
|
unwrapOr<U>(defaultValue: U): Promise<T | U>;
|
|
59
|
+
/**
|
|
60
|
+
* @deprecated Await the ResultAsync and narrow with {@link isOk} — this method
|
|
61
|
+
* re-executes deferred factories and cannot narrow.
|
|
62
|
+
*/
|
|
54
63
|
isOk(): Promise<boolean>;
|
|
64
|
+
/**
|
|
65
|
+
* @deprecated Await the ResultAsync and narrow with {@link isErr} — this method
|
|
66
|
+
* re-executes deferred factories and cannot narrow.
|
|
67
|
+
*/
|
|
55
68
|
isErr(): Promise<boolean>;
|
|
56
69
|
match<U1, U2 = U1>(onOk: (value: T) => U1, onErr: (error: E) => U2): Promise<U1 | U2>;
|
|
57
70
|
resolve(): Promise<Result<T, E>>;
|
|
@@ -107,6 +120,7 @@ declare const fromSafePromise: typeof ResultAsync.fromSafePromise;
|
|
|
107
120
|
* const combined = sequenceTupleAsync([loadCfg(), loadCatalog()] as const);
|
|
108
121
|
* // ResultAsync<readonly [Cfg, Catalog], CfgError | CatalogError>
|
|
109
122
|
* ```
|
|
123
|
+
* @deprecated Use static {@link ResultAsync.combineTuple} instead.
|
|
110
124
|
*/
|
|
111
125
|
declare const sequenceTupleAsync: typeof ResultAsync.combineTuple;
|
|
112
126
|
/**
|
|
@@ -14,6 +14,7 @@ type CombineTupleAsync<R extends readonly ResultAsync<unknown, unknown>[]> = Res
|
|
|
14
14
|
*/
|
|
15
15
|
declare class ResultAsync<T, E> {
|
|
16
16
|
protected readonly run: PromiseFactory<T, E>;
|
|
17
|
+
private promise;
|
|
17
18
|
protected constructor(run: PromiseFactory<T, E>);
|
|
18
19
|
static fromResult<T, E>(result: Result<T, E>): ResultAsync<T, E>;
|
|
19
20
|
static fromPromise<T, E>(promise: PromiseLike<T>, onReject: (error: unknown) => E): ResultAsync<T, E>;
|
|
@@ -43,6 +44,10 @@ declare class ResultAsync<T, E> {
|
|
|
43
44
|
andThen<U, F = E>(fn: (value: T) => ResultAsync<U, F> | Result<U, F> | {
|
|
44
45
|
inner: Result<U, F>;
|
|
45
46
|
}): ResultAsync<U, E | F>;
|
|
47
|
+
/**
|
|
48
|
+
* Alias for {@link flatMap}.
|
|
49
|
+
* @deprecated Use {@link flatMap} or keep/use compat {@link andThen} instead.
|
|
50
|
+
*/
|
|
46
51
|
chain<U, F = E>(fn: (value: T) => ResultAsync<U, F> | Result<U, F> | {
|
|
47
52
|
inner: Result<U, F>;
|
|
48
53
|
}): ResultAsync<U, E | F>;
|
|
@@ -51,7 +56,15 @@ declare class ResultAsync<T, E> {
|
|
|
51
56
|
tap(fn: (value: T) => void): ResultAsync<T, E>;
|
|
52
57
|
tapErr(fn: (error: E) => void): ResultAsync<T, E>;
|
|
53
58
|
unwrapOr<U>(defaultValue: U): Promise<T | U>;
|
|
59
|
+
/**
|
|
60
|
+
* @deprecated Await the ResultAsync and narrow with {@link isOk} — this method
|
|
61
|
+
* re-executes deferred factories and cannot narrow.
|
|
62
|
+
*/
|
|
54
63
|
isOk(): Promise<boolean>;
|
|
64
|
+
/**
|
|
65
|
+
* @deprecated Await the ResultAsync and narrow with {@link isErr} — this method
|
|
66
|
+
* re-executes deferred factories and cannot narrow.
|
|
67
|
+
*/
|
|
55
68
|
isErr(): Promise<boolean>;
|
|
56
69
|
match<U1, U2 = U1>(onOk: (value: T) => U1, onErr: (error: E) => U2): Promise<U1 | U2>;
|
|
57
70
|
resolve(): Promise<Result<T, E>>;
|
|
@@ -107,6 +120,7 @@ declare const fromSafePromise: typeof ResultAsync.fromSafePromise;
|
|
|
107
120
|
* const combined = sequenceTupleAsync([loadCfg(), loadCatalog()] as const);
|
|
108
121
|
* // ResultAsync<readonly [Cfg, Catalog], CfgError | CatalogError>
|
|
109
122
|
* ```
|
|
123
|
+
* @deprecated Use static {@link ResultAsync.combineTuple} instead.
|
|
110
124
|
*/
|
|
111
125
|
declare const sequenceTupleAsync: typeof ResultAsync.combineTuple;
|
|
112
126
|
/**
|
|
@@ -89,6 +89,7 @@ var ResultAsync = class _ResultAsync {
|
|
|
89
89
|
this.run = run;
|
|
90
90
|
}
|
|
91
91
|
run;
|
|
92
|
+
promise = null;
|
|
92
93
|
static fromResult(result) {
|
|
93
94
|
return new _ResultAsync(async () => result);
|
|
94
95
|
}
|
|
@@ -150,14 +151,14 @@ var ResultAsync = class _ResultAsync {
|
|
|
150
151
|
);
|
|
151
152
|
}
|
|
152
153
|
map(fn) {
|
|
153
|
-
return new _ResultAsync(async () => map(await this.
|
|
154
|
+
return new _ResultAsync(async () => map(await this.resolve(), fn));
|
|
154
155
|
}
|
|
155
156
|
mapErr(fn) {
|
|
156
|
-
return new _ResultAsync(async () => mapErr(await this.
|
|
157
|
+
return new _ResultAsync(async () => mapErr(await this.resolve(), fn));
|
|
157
158
|
}
|
|
158
159
|
flatMap(fn) {
|
|
159
160
|
return new _ResultAsync(async () => {
|
|
160
|
-
const first = await this.
|
|
161
|
+
const first = await this.resolve();
|
|
161
162
|
if (isErr(first)) {
|
|
162
163
|
return first;
|
|
163
164
|
}
|
|
@@ -174,12 +175,16 @@ var ResultAsync = class _ResultAsync {
|
|
|
174
175
|
andThen(fn) {
|
|
175
176
|
return this.flatMap(fn);
|
|
176
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* Alias for {@link flatMap}.
|
|
180
|
+
* @deprecated Use {@link flatMap} or keep/use compat {@link andThen} instead.
|
|
181
|
+
*/
|
|
177
182
|
chain(fn) {
|
|
178
183
|
return this.flatMap(fn);
|
|
179
184
|
}
|
|
180
185
|
recover(fn) {
|
|
181
186
|
return new _ResultAsync(async () => {
|
|
182
|
-
const first = await this.
|
|
187
|
+
const first = await this.resolve();
|
|
183
188
|
if (!isErr(first)) {
|
|
184
189
|
return first;
|
|
185
190
|
}
|
|
@@ -192,7 +197,7 @@ var ResultAsync = class _ResultAsync {
|
|
|
192
197
|
}
|
|
193
198
|
tap(fn) {
|
|
194
199
|
return new _ResultAsync(async () => {
|
|
195
|
-
const result = await this.
|
|
200
|
+
const result = await this.resolve();
|
|
196
201
|
if (!isErr(result)) {
|
|
197
202
|
fn(result.value);
|
|
198
203
|
}
|
|
@@ -201,7 +206,7 @@ var ResultAsync = class _ResultAsync {
|
|
|
201
206
|
}
|
|
202
207
|
tapErr(fn) {
|
|
203
208
|
return new _ResultAsync(async () => {
|
|
204
|
-
const result = await this.
|
|
209
|
+
const result = await this.resolve();
|
|
205
210
|
if (isErr(result)) {
|
|
206
211
|
fn(result.error);
|
|
207
212
|
}
|
|
@@ -209,19 +214,32 @@ var ResultAsync = class _ResultAsync {
|
|
|
209
214
|
});
|
|
210
215
|
}
|
|
211
216
|
unwrapOr(defaultValue) {
|
|
212
|
-
return this.
|
|
217
|
+
return this.resolve().then((result) => isErr(result) ? defaultValue : result.value);
|
|
213
218
|
}
|
|
219
|
+
/**
|
|
220
|
+
* @deprecated Await the ResultAsync and narrow with {@link isOk} — this method
|
|
221
|
+
* re-executes deferred factories and cannot narrow.
|
|
222
|
+
*/
|
|
214
223
|
isOk() {
|
|
215
|
-
return this.
|
|
224
|
+
return this.resolve().then((result) => !isErr(result));
|
|
216
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* @deprecated Await the ResultAsync and narrow with {@link isErr} — this method
|
|
228
|
+
* re-executes deferred factories and cannot narrow.
|
|
229
|
+
*/
|
|
217
230
|
isErr() {
|
|
218
|
-
return this.
|
|
231
|
+
return this.resolve().then((result) => isErr(result));
|
|
219
232
|
}
|
|
220
233
|
match(onOk, onErr) {
|
|
221
|
-
return this.
|
|
234
|
+
return this.resolve().then(
|
|
235
|
+
(result) => isErr(result) ? onErr(result.error) : onOk(result.value)
|
|
236
|
+
);
|
|
222
237
|
}
|
|
223
238
|
resolve() {
|
|
224
|
-
|
|
239
|
+
if (!this.promise) {
|
|
240
|
+
this.promise = this.run();
|
|
241
|
+
}
|
|
242
|
+
return this.promise;
|
|
225
243
|
}
|
|
226
244
|
/**
|
|
227
245
|
* Thenable shim — `await ra` resolves to a bare tagged-union `Result<T, E>`.
|
|
@@ -229,7 +247,7 @@ var ResultAsync = class _ResultAsync {
|
|
|
229
247
|
*/
|
|
230
248
|
// biome-ignore lint/suspicious/noThenProperty: makes ResultAsync awaitable
|
|
231
249
|
then(onfulfilled, onrejected) {
|
|
232
|
-
return this.
|
|
250
|
+
return this.resolve().then(
|
|
233
251
|
// Safe: without onfulfilled, R1 stays at its Result<T, E> default.
|
|
234
252
|
(r) => onfulfilled ? onfulfilled(r) : r,
|
|
235
253
|
onrejected ?? void 0
|