@onrails/result 0.1.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DESIGN.md +11 -19
- package/README.md +14 -62
- package/RECIPES.md +12 -25
- package/dist/async-BR3AX0ck.d.cts +326 -0
- package/dist/async-SZ7BEl0g.d.ts +326 -0
- package/dist/compat/neverthrow.cjs +266 -94
- package/dist/compat/neverthrow.cjs.map +1 -1
- package/dist/compat/neverthrow.d.cts +4 -4
- package/dist/compat/neverthrow.d.ts +4 -4
- package/dist/compat/neverthrow.js +263 -91
- package/dist/compat/neverthrow.js.map +1 -1
- package/dist/extra.cjs +2 -25
- package/dist/extra.cjs.map +1 -1
- package/dist/extra.d.cts +7 -19
- package/dist/extra.d.ts +7 -19
- package/dist/extra.js +3 -25
- package/dist/extra.js.map +1 -1
- package/dist/fluent.cjs +55 -29
- package/dist/fluent.cjs.map +1 -1
- package/dist/fluent.d.cts +38 -11
- package/dist/fluent.d.ts +38 -11
- package/dist/fluent.js +55 -29
- package/dist/fluent.js.map +1 -1
- package/dist/index.cjs +290 -131
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +294 -69
- package/dist/index.d.ts +294 -69
- package/dist/index.js +287 -127
- package/dist/index.js.map +1 -1
- package/dist/pipe.cjs.map +1 -1
- package/dist/pipe.d.cts +7 -5
- package/dist/pipe.d.ts +7 -5
- package/dist/pipe.js.map +1 -1
- package/dist/railway.cjs +293 -188
- package/dist/railway.cjs.map +1 -1
- package/dist/railway.d.cts +26 -180
- package/dist/railway.d.ts +26 -180
- package/dist/railway.js +294 -178
- package/dist/railway.js.map +1 -1
- package/dist/try-gen.cjs +1 -1
- package/dist/try-gen.cjs.map +1 -1
- package/dist/try-gen.d.cts +29 -3
- package/dist/try-gen.d.ts +29 -3
- package/dist/try-gen.js +1 -1
- package/dist/try-gen.js.map +1 -1
- package/dist/types-BQ9vv0nD.d.cts +55 -0
- package/dist/types-BQ9vv0nD.d.ts +55 -0
- package/package.json +5 -18
- package/skills/railway-do-notation/SKILL.md +397 -0
- package/skills/result-composition/SKILL.md +323 -0
- package/dist/$.cjs +0 -40
- package/dist/$.cjs.map +0 -1
- package/dist/$.js +0 -36
- package/dist/$.js.map +0 -1
- package/dist/_.d.cts +0 -2
- package/dist/_.d.ts +0 -2
- package/dist/async-CH8Cs8KE.d.cts +0 -161
- package/dist/async-DKISqIBc.d.ts +0 -161
- package/dist/interop.cjs +0 -253
- package/dist/interop.cjs.map +0 -1
- package/dist/interop.d.cts +0 -35
- package/dist/interop.d.ts +0 -35
- package/dist/interop.js +0 -249
- package/dist/interop.js.map +0 -1
- package/dist/types-C2Dp1d5J.d.cts +0 -21
- package/dist/types-C2Dp1d5J.d.ts +0 -21
- package/dist/validation.cjs +0 -53
- package/dist/validation.cjs.map +0 -1
- package/dist/validation.d.cts +0 -72
- package/dist/validation.d.ts +0 -72
- package/dist/validation.js +0 -48
- package/dist/validation.js.map +0 -1
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: result-composition
|
|
3
|
+
description: Primitive composition patterns for `@onrails/result` and `@onrails/maybe` — dual-form transforms, variadic `pipe`, variadic `flow`, point-free pipelines, the closure ladder. Use when writing or refactoring TypeScript that composes `Result` / `Maybe` transforms, when nesting `flatMap` calls feels noisy, when a reusable composed function would be cleaner than a wrapped expression, or when a user asks about "pipe", "flow", "compose", "point-free", "data-last", "dual-form", "curried Result", "tacit", "Ramda-style", or "nested flatMap". Do NOT use for full named-step service workflows — see the `railway-do-notation` skill for the workflow-builder layer above this one.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Primitive composition in `@onrails/result`
|
|
7
|
+
|
|
8
|
+
This skill covers how to compose `Result` / `Maybe` transforms at the primitive layer — before reaching for a workflow builder. The dual-form API + variadic `pipe` / `flow` give you Ramda-shape point-free composition with full TS inference.
|
|
9
|
+
|
|
10
|
+
For named multi-step service workflows (Drizzle ETL, parallel enrichment, required nullable fields), escalate to the **`railway-do-notation`** skill.
|
|
11
|
+
|
|
12
|
+
For worked examples of the patterns below, see [`packages/result/RECIPES.md`](../../RECIPES.md).
|
|
13
|
+
|
|
14
|
+
## The dual-form contract
|
|
15
|
+
|
|
16
|
+
Every transform in `@onrails/result` and `@onrails/maybe` accepts **two call shapes**:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
map(result, fn); // data-first — best inference for one-shot inline calls
|
|
20
|
+
map(fn)(result); // data-last (curried) — feeds pipe(...) and flow(...)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Arity at the call site picks the overload. Same applies to `mapErr`, `bimap`, `flatMap`, `recover`, `tap`, `tapErr`, `match`, and the sync→async bridge `asyncAfter` (`asyncAfter(result, fn)` data-first, `asyncAfter(fn)` data-last).
|
|
24
|
+
|
|
25
|
+
`match` and `bimap` use 3-args data-first, 2-args curried:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
match(result, onOk, onErr); // data-first
|
|
29
|
+
match(onOk, onErr)(result); // data-last
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Decision tree
|
|
33
|
+
|
|
34
|
+
Use the smallest tool that makes the code clear:
|
|
35
|
+
|
|
36
|
+
| Shape | Reach for |
|
|
37
|
+
|-------|-----------|
|
|
38
|
+
| One-shot inline transform | `map(r, fn)` — data-first; TS infers `T` from `r` |
|
|
39
|
+
| 3+ sequential steps from a starting value | `pipe(value, map(fn), flatMap(g), ...)` |
|
|
40
|
+
| Defining a reusable composed function | `flow(map(fn), flatMap(g), ...)` returning `(value) => Result<…>` |
|
|
41
|
+
| Method-chain in expression position | `r.map(fn).flatMap(g)` on `ResultAsync` (class method) |
|
|
42
|
+
| 4+ named domain steps, mixed sync/async, nullable DB rows | **stop — use `railway-do-notation` skill** |
|
|
43
|
+
|
|
44
|
+
## `pipe` vs `flow` — the only difference
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
pipe(value, f, g, h) === h(g(f(value))); // value applied immediately
|
|
48
|
+
flow(f, g, h)(value) === h(g(f(value))); // returns a reusable function
|
|
49
|
+
pipe(x, ...fns) === flow(...fns)(x); // formal identity
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Both are **left-to-right** (like Ramda's `R.pipe`, fp-ts `flow`, Effect `flow`). Not right-to-left `compose`.
|
|
53
|
+
|
|
54
|
+
Use `pipe` when you have the starting value at the call site. Use `flow` when you want to define a function once and apply it later.
|
|
55
|
+
|
|
56
|
+
## The closure ladder
|
|
57
|
+
|
|
58
|
+
The decision between `flow`, `pipe`, and an outer HOF wrapper depends on **what each step needs to read**:
|
|
59
|
+
|
|
60
|
+
| Step needs to read… | Shape |
|
|
61
|
+
|---------------------|-------|
|
|
62
|
+
| Nothing from outside the carrier value | `flow(step1, step2, ...)` — pure point-free |
|
|
63
|
+
| Per-call configuration that doesn't change with the data | `(cfg) => flow(step1(cfg), step2(cfg), ...)` — outer HOF closes over config |
|
|
64
|
+
| The original entry value mid-pipeline (e.g. retry coordinate) | `(input) => pipe(input, step1, step2, ...)` — closure over input, use `pipe` |
|
|
65
|
+
|
|
66
|
+
Each row up adds one closure layer. Picking the right row keeps the code as point-free as the actual data flow allows — no more, no less.
|
|
67
|
+
|
|
68
|
+
## Layer 1 — Direct dual-form calls
|
|
69
|
+
|
|
70
|
+
For one or two transforms, just call the dual-form fn directly:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { map, flatMap, ok, err } from "@onrails/result";
|
|
74
|
+
|
|
75
|
+
const trimmed = map(parsedConfig, (cfg) => cfg.name.trim());
|
|
76
|
+
const validated = flatMap(trimmed, (name) =>
|
|
77
|
+
name.length > 0 ? ok(name) : err({ kind: "empty" as const }),
|
|
78
|
+
);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
This is already clear. Don't wrap two calls in a `pipe` just to be consistent.
|
|
82
|
+
|
|
83
|
+
## Layer 2 — Variadic `pipe`
|
|
84
|
+
|
|
85
|
+
Reach for `pipe(value, ...fns)` when:
|
|
86
|
+
|
|
87
|
+
- 3+ sequential steps starting from a known value
|
|
88
|
+
- the chain reads top-to-bottom with each line doing one obvious thing
|
|
89
|
+
- intermediate results don't need to be named
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { pipe, map, flatMap, recover, tap } from "@onrails/result";
|
|
93
|
+
|
|
94
|
+
const greeting = pipe(
|
|
95
|
+
parseConfig(raw),
|
|
96
|
+
map((cfg) => cfg.user),
|
|
97
|
+
flatMap((u) => (u.name ? ok(u.name) : err({ kind: "empty" as const }))),
|
|
98
|
+
recover((e) => (e.kind === "empty" ? ok("anon") : err(e))),
|
|
99
|
+
tap((name) => log.info({ msg: "resolved", name })),
|
|
100
|
+
);
|
|
101
|
+
// Result<string, ParseError>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`asyncAfter` is dual-form too — its data-last call is the sync→async bridge that drops straight into `pipe`, so the step stays point-free:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
pipe(
|
|
108
|
+
validated,
|
|
109
|
+
asyncAfter((v) => tryAsync(persist(v))), // Result → ResultAsync, point-free
|
|
110
|
+
);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
And `pipe` still slots arbitrary `(prev) => next` lambdas when a step is genuinely one-off and has no curried form:
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
pipe(
|
|
117
|
+
parseConfig(raw),
|
|
118
|
+
map((cfg) => cfg.users),
|
|
119
|
+
(r) => r.map((users) => users.slice(0, 10)), // ad-hoc reshape, no point-free gain
|
|
120
|
+
);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Don't fight the railway — reach for the data-last form when it exists; embed a plain lambda only when the step is truly bespoke.
|
|
124
|
+
|
|
125
|
+
## Layer 3 — Variadic `flow`
|
|
126
|
+
|
|
127
|
+
Use `flow(...fns)` to **define reusable composed functions** without mentioning the data:
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
import { flow } from "@onrails/result/pipe";
|
|
131
|
+
import { flatMap, map, type Result } from "@onrails/result";
|
|
132
|
+
|
|
133
|
+
// Result-track mini-pipeline — takes raw, returns Result.
|
|
134
|
+
const parseAndValidate = flow(parseJson, flatMap(validateSchema));
|
|
135
|
+
|
|
136
|
+
// Value-track mini-pipeline — takes Ok value, returns Result. Lifted by `flatMap`.
|
|
137
|
+
const enrichAndPersist = flow(addTimestamp, persist);
|
|
138
|
+
|
|
139
|
+
// flow is associative — flow(flow(a, b), c) === flow(a, b, c).
|
|
140
|
+
const ingest = flow(parseAndValidate, flatMap(enrichAndPersist));
|
|
141
|
+
// (raw: string) => Result<Saved, ParseError | SchemaError | DbError>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`flow` shines when the same composition is reused across call sites, or when naming the mini-pipeline (`parseAndValidate`) clarifies intent.
|
|
145
|
+
|
|
146
|
+
## Layer 4 — Strategy-parametrised flows (HOF + `flow`)
|
|
147
|
+
|
|
148
|
+
When a step needs configuration that doesn't change per-call, take it on an outer factory. Inner `flow` stays point-free over the data:
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import { flow } from "@onrails/result/pipe";
|
|
152
|
+
import { map, recover, ok, err } from "@onrails/result";
|
|
153
|
+
|
|
154
|
+
type FetchConfig = {
|
|
155
|
+
readonly fallback?: Body;
|
|
156
|
+
readonly rethrow: (e: FetchError) => boolean;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const fetchWith = (cfg: FetchConfig) =>
|
|
160
|
+
flow(
|
|
161
|
+
fetchSync,
|
|
162
|
+
recover((e: FetchError) =>
|
|
163
|
+
cfg.rethrow(e) || !cfg.fallback ? err(e) : ok(cfg.fallback),
|
|
164
|
+
),
|
|
165
|
+
map((body: Body) => body.byteLength),
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const fetchOrEmpty = fetchWith({ fallback: emptyBody, rethrow: (e) => e.kind === "fatal" });
|
|
169
|
+
// (url: string) => Result<number, FetchError>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The outer fn captures *what varies between definitions*; the inner `flow` captures *what varies per-call*. Same shape as recipe #10 in `RECIPES.md`.
|
|
173
|
+
|
|
174
|
+
## Anti-patterns
|
|
175
|
+
|
|
176
|
+
### 1. Compose direction confusion
|
|
177
|
+
|
|
178
|
+
`flow` is **left-to-right**. The first fn is the entry point, not the last:
|
|
179
|
+
|
|
180
|
+
```ts
|
|
181
|
+
// Right:
|
|
182
|
+
flow(fetchSync, recover(handler)); // (url) => recover(handler)(fetchSync(url))
|
|
183
|
+
|
|
184
|
+
// Wrong (Ramda compose habit):
|
|
185
|
+
flow(recover(handler), fetchSync); // recover called first with `url`, type-fails
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
If you want right-to-left `compose`, build it on top in one line — but in practice, just use `flow` and read top-to-bottom.
|
|
189
|
+
|
|
190
|
+
### 2. Closure-over-input in `flow`
|
|
191
|
+
|
|
192
|
+
`flow(...)` has no lexical access to the eventual call argument. If a step needs to reference the entry value (e.g. retry with the original URL inside `recover`), you must wrap `flow` in a function-of-input:
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
// Wrong — `url` is undefined in the recover closure:
|
|
196
|
+
const fetchWithBackoff = flow(
|
|
197
|
+
fetchSync,
|
|
198
|
+
recover((e) => fetchSync(url)), // `url` is not in scope
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
// Right — wrap so recover closes over the outer parameter:
|
|
202
|
+
const fetchWithBackoff = (url: string) =>
|
|
203
|
+
pipe(
|
|
204
|
+
url,
|
|
205
|
+
fetchSync,
|
|
206
|
+
recover((e) => fetchSync(url)),
|
|
207
|
+
);
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Decide via the closure ladder above: any step that looks back at the original input forces row 3 (wrap in `(input) => pipe(input, ...)`).
|
|
211
|
+
|
|
212
|
+
### 3. Point-free where a step needs the same value three times
|
|
213
|
+
|
|
214
|
+
When one intermediate value is referenced multiple times in branchy logic, `flatMap` chains start carrying ambient state and reading inverted. Drop into `tryGen` — `$(result)` unwraps the Ok value or short-circuits the whole block with the first Err (Rust's `?` for sync code).
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
import { tryGen, $ } from "@onrails/result/try-gen";
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**a) Linear unwrap — same value reused downstream.** The point-free version would thread `user` through every closure; `$` names it once.
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
const ingest = (raw: string) =>
|
|
224
|
+
pipe(
|
|
225
|
+
raw,
|
|
226
|
+
parseUser,
|
|
227
|
+
(validated) =>
|
|
228
|
+
tryGen(() => {
|
|
229
|
+
const user = $(validated); // unwrap or short-circuit
|
|
230
|
+
const enriched = $(enrichWithAcl(user)); // reads user
|
|
231
|
+
const persisted = $(persistSync(enriched));
|
|
232
|
+
return ok({ user: persisted, at: Date.now() });
|
|
233
|
+
}),
|
|
234
|
+
mapErr((e) => ({ kind: "ingest" as const, cause: e })),
|
|
235
|
+
);
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**b) Branch on an unwrapped value — early `return err(...)`.** This is the case `flatMap` reads worst: a guard between two unwraps.
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
const authorize = (req: Request, postId: string) =>
|
|
242
|
+
tryGen(() => {
|
|
243
|
+
const user = $(authenticate(req));
|
|
244
|
+
const post = $(fetchPost(postId));
|
|
245
|
+
if (post.authorId !== user.id && !user.isAdmin) {
|
|
246
|
+
return err({ kind: "forbidden" as const }); // early exit, no nesting
|
|
247
|
+
}
|
|
248
|
+
return ok(post);
|
|
249
|
+
});
|
|
250
|
+
// Result<Post, AuthError | FetchError | { kind: "forbidden" }>
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**c) Conditional unwrap — `$` inside a branch.** Only the taken branch unwraps; the error union still accumulates both sides.
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
const resolve = (raw: string, opts: { strict: boolean }) =>
|
|
257
|
+
tryGen(() => {
|
|
258
|
+
const cfg = $(parseConfig(raw));
|
|
259
|
+
const name = cfg.name ?? (opts.strict ? $(err({ kind: "missing" as const })) : "anon");
|
|
260
|
+
return ok({ ...cfg, name });
|
|
261
|
+
});
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**d) Loop with `$` — accumulate or bail on first failure.** A `for` loop with `$` is far clearer than `combine` + manual reduce when each iteration depends on the last.
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
const applyAll = (state: State, steps: readonly Step[]) =>
|
|
268
|
+
tryGen(() => {
|
|
269
|
+
let acc = state;
|
|
270
|
+
for (const step of steps) {
|
|
271
|
+
acc = $(applyStep(acc, step)); // first Err aborts the loop + the block
|
|
272
|
+
}
|
|
273
|
+
return ok(acc);
|
|
274
|
+
});
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
`tryGen` is a sync island. Use it when:
|
|
278
|
+
|
|
279
|
+
- the same value is referenced 3+ times
|
|
280
|
+
- conditional branching makes the `flatMap` chain feel inverted
|
|
281
|
+
- you want `?`-style early returns without method-chaining
|
|
282
|
+
- a `Result`-returning loop body must bail on first failure
|
|
283
|
+
|
|
284
|
+
### 4. Inference noise mid-chain
|
|
285
|
+
|
|
286
|
+
If a `pipe` accumulates two or more `as const` casts between steps, TS is telling you the carrier type is fighting back. Either:
|
|
287
|
+
|
|
288
|
+
- annotate the carrier type explicitly with a typed intermediate variable, or
|
|
289
|
+
- revert that step to a named one-shot and continue the pipe after it.
|
|
290
|
+
|
|
291
|
+
Point-free should reduce noise, not add it.
|
|
292
|
+
|
|
293
|
+
## When to escalate
|
|
294
|
+
|
|
295
|
+
When you find yourself reaching for **any** of:
|
|
296
|
+
|
|
297
|
+
- named context carrying values forward across many steps
|
|
298
|
+
- mixed sync + async boundaries with `fromResult` / `asyncAfter` plumbing on every line
|
|
299
|
+
- nullable Drizzle rows that must become required values
|
|
300
|
+
- independent async branches that should run in parallel and merge by name
|
|
301
|
+
|
|
302
|
+
…**stop and switch to the `railway-do-notation` skill**. `Railway.*` is designed for that territory and will read better than any amount of `pipe`/`flow` plumbing.
|
|
303
|
+
|
|
304
|
+
## When NOT to go point-free
|
|
305
|
+
|
|
306
|
+
`flow` and `pipe` are tools, not goals. Pipelines should read top-to-bottom with each step doing one obvious thing. Reach for `pipe` when:
|
|
307
|
+
|
|
308
|
+
- 3+ sequential steps
|
|
309
|
+
- each step has a clear *what*, not a clever *how*
|
|
310
|
+
- errors compose cleanly via `mapErr` / `recover`
|
|
311
|
+
|
|
312
|
+
Avoid point-free when:
|
|
313
|
+
|
|
314
|
+
- the pipeline branches on the value mid-flight in non-obvious ways — use `tryGen` or escalate to `Railway`
|
|
315
|
+
- a single step needs three different references to the same intermediate value — name it with `flatMap((x) => { ... })` and stop pretending it's anonymous
|
|
316
|
+
- TypeScript inference gets noisy (multiple `as const` casts mid-chain) — annotate or revert
|
|
317
|
+
|
|
318
|
+
The dual-form API lets you mix both styles freely: start point-free, drop to a named step when clarity beats compression.
|
|
319
|
+
|
|
320
|
+
## See also
|
|
321
|
+
|
|
322
|
+
- [`packages/result/RECIPES.md`](../../RECIPES.md) — 13 worked recipes covering parser builders, ETL pipelines, strategy-parametrised flows, validator ladders, Maybe → Result crossings, parallel sub-workflows, `tryGen` escape hatches, async pipelines, and Railway workflow pipelines.
|
|
323
|
+
- [`railway-do-notation`](../railway-do-notation/SKILL.md) — workflow-builder layer above this one. Use when named context wins over positional plumbing.
|
package/dist/$.cjs
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// src/result.ts
|
|
4
|
-
var err = (error) => ({
|
|
5
|
-
_tag: "Err",
|
|
6
|
-
error
|
|
7
|
-
});
|
|
8
|
-
var isErr = (result) => result._tag === "Err";
|
|
9
|
-
|
|
10
|
-
// src/try-gen.ts
|
|
11
|
-
var ErrSignal = class {
|
|
12
|
-
constructor(error) {
|
|
13
|
-
this.error = error;
|
|
14
|
-
}
|
|
15
|
-
error;
|
|
16
|
-
_tag = "ErrSignal";
|
|
17
|
-
};
|
|
18
|
-
var yieldResult = (result) => {
|
|
19
|
-
if (isErr(result)) {
|
|
20
|
-
throw new ErrSignal(result.error);
|
|
21
|
-
}
|
|
22
|
-
return result.value;
|
|
23
|
-
};
|
|
24
|
-
var $ = yieldResult;
|
|
25
|
-
var tryGen = (fn) => {
|
|
26
|
-
try {
|
|
27
|
-
return fn();
|
|
28
|
-
} catch (error) {
|
|
29
|
-
if (error instanceof ErrSignal) {
|
|
30
|
-
return err(error.error);
|
|
31
|
-
}
|
|
32
|
-
throw error;
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
exports.$ = $;
|
|
37
|
-
exports.tryGen = tryGen;
|
|
38
|
-
exports.yieldResult = yieldResult;
|
|
39
|
-
//# sourceMappingURL=$.cjs.map
|
|
40
|
-
//# sourceMappingURL=$.cjs.map
|
package/dist/$.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/result.ts","../src/try-gen.ts"],"names":[],"mappings":";;;AA2BO,IAAM,GAAA,GAAM,CAAyB,KAAA,MAA4B;AAAA,EACtE,IAAA,EAAM,KAAA;AAAA,EACN;AACF,CAAA,CAAA;AAwBO,IAAM,KAAA,GAAQ,CAAO,MAAA,KAA8C,MAAA,CAAO,IAAA,KAAS,KAAA;;;AClD1F,IAAM,YAAN,MAAmB;AAAA,EAEjB,YAAqB,KAAA,EAAU;AAAV,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AAAA,EAAW;AAAA,EAAX,KAAA;AAAA,EADZ,IAAA,GAAO,WAAA;AAElB,CAAA;AAMO,IAAM,WAAA,GAAc,CAAO,MAAA,KAA4B;AAC5D,EAAA,IAAI,KAAA,CAAM,MAAM,CAAA,EAAG;AACjB,IAAA,MAAM,IAAI,SAAA,CAAU,MAAA,CAAO,KAAK,CAAA;AAAA,EAClC;AACA,EAAA,OAAO,MAAA,CAAO,KAAA;AAChB;AAEO,IAAM,CAAA,GAAI;AAcV,IAAM,MAAA,GAAS,CAAO,EAAA,KAAyC;AACpE,EAAA,IAAI;AACF,IAAA,OAAO,EAAA,EAAG;AAAA,EACZ,SAAS,KAAA,EAAO;AACd,IAAA,IAAI,iBAAiB,SAAA,EAAW;AAC9B,MAAA,OAAO,GAAA,CAAI,MAAM,KAAK,CAAA;AAAA,IACxB;AACA,IAAA,MAAM,KAAA;AAAA,EACR;AACF","file":"$.cjs","sourcesContent":["import type { Err, Ok, Result } from \"./types.js\";\n\nexport type { Err, Ok, Result } from \"./types.js\";\n\n/**\n * Lifts a value into the success track.\n *\n * @example\n * ```ts\n * const r = ok(42); // Result<number, never>\n * const typed: Result<number, \"parse\"> = ok(1);\n * ```\n */\nexport const ok = <T, E = never>(value: T): Result<T, E> => ({\n _tag: \"Ok\",\n value,\n});\n\n/**\n * Lifts a value into the error track.\n *\n * @example\n * ```ts\n * const r = err({ kind: \"parse\", message: \"bad json\" });\n * // Result<never, { kind: \"parse\"; message: string }>\n * ```\n */\nexport const err = <T = never, E = unknown>(error: E): Result<T, E> => ({\n _tag: \"Err\",\n error,\n});\n\n/**\n * Type-narrowing predicate: returns `true` when the result is `Ok`.\n *\n * @example\n * ```ts\n * if (isOk(r)) {\n * console.log(r.value); // narrowed to Ok branch\n * }\n * ```\n */\nexport const isOk = <T, E>(result: Result<T, E>): result is Ok<T, E> => result._tag === \"Ok\";\n\n/**\n * Type-narrowing predicate: returns `true` when the result is `Err`.\n *\n * @example\n * ```ts\n * if (isErr(r)) {\n * metrics.inc(\"error\", { kind: r.error.kind });\n * }\n * ```\n */\nexport const isErr = <T, E>(result: Result<T, E>): result is Err<T, E> => result._tag === \"Err\";\n\n/**\n * Fantasy Land `of` — alias of {@link ok}.\n * @deprecated Use {@link ok} instead.\n */\nexport const of = ok;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Dual-form helpers — each export accepts either shape:\n// data-first: `map(result, fn)`\n// curried: `map(fn)(result)`\n// Arity at the call site selects the overload.\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst mapImpl = <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 * Transform the `Ok` value, passing `Err` through unchanged. Dual-form:\n * call data-first or curried (for use with {@link pipe}).\n *\n * @example\n * ```ts\n * map(ok(2), (n) => n * 3); // Ok 6 — data-first\n * pipe(ok(\"x\"), map((s) => s.length));// Ok 1 — curried\n * ```\n */\nexport function map<T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E>;\nexport function map<T, U>(fn: (value: T) => U): <E>(result: Result<T, E>) => Result<U, E>;\nexport function map(\n ...args: [Result<unknown, unknown>, (value: unknown) => unknown] | [(value: unknown) => unknown]\n): unknown {\n if (args.length === 2) return mapImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => mapImpl(result, fn);\n}\n\nconst mapErrImpl = <T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F> =>\n isErr(result) ? err(fn(result.error)) : ok(result.value);\n\n/**\n * Transform the `Err` value, passing `Ok` through unchanged. Useful for\n * unifying heterogeneous failure types into one app-level union.\n *\n * @example\n * ```ts\n * type AppError = { kind: \"http\"; status: number } | { kind: \"parse\" };\n * pipe(\n * fetchSync(url), // Result<Body, { status: number }>\n * mapErr((e): AppError => ({ kind: \"http\", status: e.status })),\n * );\n * ```\n */\nexport function mapErr<T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F>;\nexport function mapErr<E, F>(fn: (error: E) => F): <T>(result: Result<T, E>) => Result<T, F>;\nexport function mapErr(\n ...args: [Result<unknown, unknown>, (error: unknown) => unknown] | [(error: unknown) => unknown]\n): unknown {\n if (args.length === 2) return mapErrImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => mapErrImpl(result, fn);\n}\n\nconst bimapImpl = <T, U, E, F>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n): Result<U, F> => (isOk(result) ? ok(onOk(result.value)) : err(onErr(result.error)));\n\n/**\n * Transform both tracks at once — `Ok` via `onOk`, `Err` via `onErr`.\n * Equivalent to `mapErr(onErr)(map(onOk)(result))` but in one pass.\n *\n * @example\n * ```ts\n * bimap(parsed, (cfg) => cfg.name, (e) => ({ kind: \"input\", cause: e }));\n * ```\n */\nexport function bimap<T, U, E, F>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n): Result<U, F>;\nexport function bimap<T, U, E, F>(\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n): (result: Result<T, E>) => Result<U, F>;\nexport function bimap(\n ...args:\n | [Result<unknown, unknown>, (value: unknown) => unknown, (error: unknown) => unknown]\n | [(value: unknown) => unknown, (error: unknown) => unknown]\n): unknown {\n if (args.length === 3) return bimapImpl(args[0], args[1], args[2]);\n const [onOk, onErr] = args;\n return (result: Result<unknown, unknown>) => bimapImpl(result, onOk, onErr);\n}\n\nconst flatMapImpl = <T, U, E, F>(\n result: Result<T, E>,\n fn: (value: T) => Result<U, F>,\n): Result<U, E | F> => (isOk(result) ? fn(result.value) : err(result.error));\n\n/**\n * Canonical bind (Fantasy Land `chain`). Chains a Result-returning step,\n * widening the error union to `E | F`. Short-circuits on `Err`.\n *\n * @example\n * ```ts\n * flatMap(parseInput(raw), (data) =>\n * data.id != null ? ok(data) : err({ kind: \"missing_id\" as const }),\n * );\n * // Result<Data, ParseError | { kind: \"missing_id\" }>\n * ```\n */\nexport function flatMap<T, U, E, F>(\n result: Result<T, E>,\n fn: (value: T) => Result<U, F>,\n): Result<U, E | F>;\nexport function flatMap<T, U, F>(\n fn: (value: T) => Result<U, F>,\n): <E>(result: Result<T, E>) => Result<U, E | F>;\nexport function flatMap(\n ...args:\n | [Result<unknown, unknown>, (value: unknown) => Result<unknown, unknown>]\n | [(value: unknown) => Result<unknown, unknown>]\n): unknown {\n if (args.length === 2) return flatMapImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => flatMapImpl(result, fn);\n}\n\nconst recoverImpl = <T, E, F>(\n result: Result<T, E>,\n fn: (error: E) => Result<T, F>,\n): Result<T, F> => (isErr(result) ? fn(result.error) : ok(result.value));\n\n/**\n * Error-track bind — runs `fn` only when the result is `Err`, allowing\n * a failed workflow to recover to `Ok` or remap the failure. Mirror of\n * {@link flatMap} on the error channel.\n *\n * @example\n * ```ts\n * recover(networkResult, (e) =>\n * e.kind === \"rate_limit\" ? ok(cachedBody) : err(e),\n * );\n * ```\n */\nexport function recover<T, E, F>(\n result: Result<T, E>,\n fn: (error: E) => Result<T, F>,\n): Result<T, F>;\nexport function recover<T, E, F>(\n fn: (error: E) => Result<T, F>,\n): (result: Result<T, E>) => Result<T, F>;\nexport function recover(\n ...args:\n | [Result<unknown, unknown>, (error: unknown) => Result<unknown, unknown>]\n | [(error: unknown) => Result<unknown, unknown>]\n): unknown {\n if (args.length === 2) return recoverImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => recoverImpl(result, fn);\n}\n\nconst tapImpl = <T, E>(result: Result<T, E>, fn: (value: T) => void): Result<T, E> => {\n if (isOk(result)) fn(result.value);\n return result;\n};\n\n/**\n * Observe the `Ok` value for side effects (logging, metrics) without\n * changing the carried value. Passes `Err` through untouched.\n *\n * @example\n * ```ts\n * pipe(\n * parseConfig(raw),\n * tap((cfg) => log.info({ msg: \"parsed\", name: cfg.name })),\n * flatMap(validate),\n * );\n * ```\n */\nexport function tap<T, E>(result: Result<T, E>, fn: (value: T) => void): Result<T, E>;\nexport function tap<T>(fn: (value: T) => void): <E>(result: Result<T, E>) => Result<T, E>;\nexport function tap(\n ...args: [Result<unknown, unknown>, (value: unknown) => void] | [(value: unknown) => void]\n): unknown {\n if (args.length === 2) return tapImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => tapImpl(result, fn);\n}\n\nconst tapErrImpl = <T, E>(result: Result<T, E>, fn: (error: E) => void): Result<T, E> => {\n if (isErr(result)) fn(result.error);\n return result;\n};\n\n/**\n * Observe the `Err` value for side effects (logging, metrics) without\n * changing the carried error. Passes `Ok` through untouched.\n *\n * @example\n * ```ts\n * pipe(\n * loadUser(id),\n * tapErr((e) => metrics.inc(\"user.load.fail\", { kind: e.kind })),\n * );\n * ```\n */\nexport function tapErr<T, E>(result: Result<T, E>, fn: (error: E) => void): Result<T, E>;\nexport function tapErr<E>(fn: (error: E) => void): <T>(result: Result<T, E>) => Result<T, E>;\nexport function tapErr(\n ...args: [Result<unknown, unknown>, (error: unknown) => void] | [(error: unknown) => void]\n): unknown {\n if (args.length === 2) return tapErrImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => tapErrImpl(result, fn);\n}\n\nconst matchImpl = <T, E, U>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => U,\n): U => (isOk(result) ? onOk(result.value) : onErr(result.error));\n\n/**\n * Terminal collapse — fold both tracks into a single value. Dual-form:\n * 3-args data-first, 2-args curried for {@link pipe}. Returns whatever\n * the handlers return.\n *\n * For files that also import `match` from `ts-pattern`, the\n * collision-free alias {@link matchResult} is identical.\n *\n * @example\n * ```ts\n * const html = match(parsed, (cfg) => render(cfg), (e) => renderError(e));\n * ```\n */\nexport function match<T, E, U>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => U,\n): U;\nexport function match<T, E, U>(\n onOk: (value: T) => U,\n onErr: (error: E) => U,\n): (result: Result<T, E>) => U;\nexport function match(\n ...args:\n | [Result<unknown, unknown>, (value: unknown) => unknown, (error: unknown) => unknown]\n | [(value: unknown) => unknown, (error: unknown) => unknown]\n): unknown {\n if (args.length === 3) return matchImpl(args[0], args[1], args[2]);\n const [onOk, onErr] = args;\n return (result: Result<unknown, unknown>) => matchImpl(result, onOk, onErr);\n}\n\n/**\n * Collision-free alias for files that also import `match` from ts-pattern.\n * @deprecated Namespace import carriers (e.g., `import * as R from \"@onrails/result\"`) and use canonical {@link match} instead.\n */\nexport const matchResult = match;\n\n/**\n * Curried collapse with named slots — escape valve when positional `match`\n * order is unclear at the call site (e.g. when both handlers return the same\n * type and a transposed `match(r, onErr, onOk)` would silently compile).\n * @deprecated Use curried {@link match}(onOk, onErr)(result) instead.\n */\nexport const fold =\n <T, E, U>(handlers: { readonly ok: (value: T) => U; readonly err: (error: E) => U }) =>\n (result: Result<T, E>): U =>\n matchImpl(result, handlers.ok, handlers.err);\n\n/**\n * Returns the `Ok` value, or `defaultValue` when the result is `Err`.\n *\n * @example\n * ```ts\n * unwrapOr(parsedSetting, \"default-value\");\n * ```\n */\nexport const unwrapOr = <T, E>(result: Result<T, E>, defaultValue: T): T =>\n isOk(result) ? result.value : defaultValue;\n\n/**\n * Test/assert helper — throws the original Err value when called on Err.\n *\n * @throws when the result is Err — assertion-tier; use {@link match} / {@link unwrapOr} in business logic.\n * Allowed in `*.spec.ts` / `*.test.ts`; flagged elsewhere by the plugins.\n */\nexport function unwrapOk<T, E>(result: Result<T, E>): T {\n if (isErr(result)) throw result.error;\n return result.value;\n}\n\n/**\n * Test/assert helper — throws TypeError when called on Ok.\n *\n * @throws when the result is Ok — assertion-tier; allowed in `*.spec.ts` / `*.test.ts`; flagged elsewhere by the plugins.\n */\nexport function unwrapErr<T, E>(result: Result<T, E>): E {\n if (isOk(result)) throw new TypeError(\"unwrapErr called on Ok\");\n return result.error;\n}\n\n/** Wrap a throwing sync function — neverthrow `Result.fromThrowable` */\nexport function trySync<A extends readonly unknown[], T, E>(\n fn: (...args: A) => T,\n onThrow: (error: unknown) => E,\n): (...args: A) => Result<T, E>;\nexport function trySync<F extends (...args: never) => unknown, E>(\n fn: F,\n onThrow: (error: unknown) => E,\n): (...args: Parameters<F>) => Result<ReturnType<F>, E>;\nexport function trySync(\n fn: (...args: never) => unknown,\n onThrow: (error: unknown) => unknown,\n): (...args: never) => Result<unknown, unknown> {\n return (...args: never) => {\n try {\n return ok(fn(...args));\n } catch (error) {\n return err(onThrow(error));\n }\n };\n}\n\n/** First failure wins; otherwise collects values in order */\nexport const combine = <T, E>(results: readonly Result<T, E>[]): Result<T[], E> => {\n const values: T[] = [];\n for (const result of results) {\n if (isErr(result)) return err(result.error);\n values.push(result.value);\n }\n return ok(values);\n};\n\n/** Tuple-preserving combine (neverthrow-style) */\nexport const combineTuple = <const R extends readonly Result<unknown, unknown>[]>(\n results: R,\n): CombineTuple<R> =>\n // Runtime identical to combine; the cast restores per-index tuple types.\n combine(results as readonly Result<unknown, unknown>[]) as CombineTuple<R>;\n\ntype _OkValue<R> = R extends { _tag: \"Ok\"; readonly value: infer T } ? T : never;\ntype _ErrValue<R> = R extends { _tag: \"Err\"; readonly error: infer E } ? E : never;\n\ntype CombineTuple<R extends readonly Result<unknown, unknown>[]> = Result<\n { [K in keyof R]: _OkValue<R[K]> },\n { [K in keyof R]: _ErrValue<R[K]> }[number]\n>;\n\n/**\n * Variadic value-first pipe — threads `value` through up to nine unary fns.\n *\n * ```ts\n * pipe(\n * parseConfig(raw),\n * map((cfg) => cfg.name),\n * flatMap((name) => name ? ok(name) : err({ kind: \"empty\" })),\n * tap(log),\n * );\n * ```\n */\nexport function pipe<A>(value: A): A;\nexport function pipe<A, B>(value: A, ab: (a: A) => B): B;\nexport function pipe<A, B, C>(value: A, ab: (a: A) => B, bc: (b: B) => C): C;\nexport function pipe<A, B, C, D>(value: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D): D;\nexport function pipe<A, B, C, D, E>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n): E;\nexport function pipe<A, B, C, D, E, F>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n): F;\nexport function pipe<A, B, C, D, E, F, G>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n): G;\nexport function pipe<A, B, C, D, E, F, G, H>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n): H;\nexport function pipe<A, B, C, D, E, F, G, H, I>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n hi: (h: H) => I,\n): I;\nexport function pipe(value: unknown, ...fns: ReadonlyArray<(x: unknown) => unknown>): unknown {\n let acc = value;\n for (const fn of fns) {\n acc = fn(acc);\n }\n return acc;\n}\n","import { err, isErr } from \"./result.js\";\nimport type { Result } from \"./types.js\";\n\n/** Internal control-flow signal — not part of public error types */\nclass ErrSignal<E> {\n readonly _tag = \"ErrSignal\" as const;\n constructor(readonly error: E) {}\n}\n\n/**\n * Unwrap a {@link Result} inside {@link tryGen} (Rust `?` for sync code).\n * Prefer {@link flatMap} / {@link fluent} for long pipelines.\n */\nexport const yieldResult = <T, E>(result: Result<T, E>): T => {\n if (isErr(result)) {\n throw new ErrSignal(result.error);\n }\n return result.value;\n};\n\nexport const $ = yieldResult;\n\n/**\n * Run a block that uses {@link yieldResult}; returns the final `Result` or the first Err.\n *\n * @example\n * ```ts\n * const out = tryGen(() => {\n * const a = $(parseA());\n * const b = $(parseB());\n * return ok(a + b);\n * });\n * ```\n */\nexport const tryGen = <T, E>(fn: () => Result<T, E>): Result<T, E> => {\n try {\n return fn();\n } catch (error) {\n if (error instanceof ErrSignal) {\n return err(error.error);\n }\n throw error;\n }\n};\n"]}
|
package/dist/$.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// src/result.ts
|
|
2
|
-
var err = (error) => ({
|
|
3
|
-
_tag: "Err",
|
|
4
|
-
error
|
|
5
|
-
});
|
|
6
|
-
var isErr = (result) => result._tag === "Err";
|
|
7
|
-
|
|
8
|
-
// src/try-gen.ts
|
|
9
|
-
var ErrSignal = class {
|
|
10
|
-
constructor(error) {
|
|
11
|
-
this.error = error;
|
|
12
|
-
}
|
|
13
|
-
error;
|
|
14
|
-
_tag = "ErrSignal";
|
|
15
|
-
};
|
|
16
|
-
var yieldResult = (result) => {
|
|
17
|
-
if (isErr(result)) {
|
|
18
|
-
throw new ErrSignal(result.error);
|
|
19
|
-
}
|
|
20
|
-
return result.value;
|
|
21
|
-
};
|
|
22
|
-
var $ = yieldResult;
|
|
23
|
-
var tryGen = (fn) => {
|
|
24
|
-
try {
|
|
25
|
-
return fn();
|
|
26
|
-
} catch (error) {
|
|
27
|
-
if (error instanceof ErrSignal) {
|
|
28
|
-
return err(error.error);
|
|
29
|
-
}
|
|
30
|
-
throw error;
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export { $, tryGen, yieldResult };
|
|
35
|
-
//# sourceMappingURL=$.js.map
|
|
36
|
-
//# sourceMappingURL=$.js.map
|
package/dist/$.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/result.ts","../src/try-gen.ts"],"names":[],"mappings":";AA2BO,IAAM,GAAA,GAAM,CAAyB,KAAA,MAA4B;AAAA,EACtE,IAAA,EAAM,KAAA;AAAA,EACN;AACF,CAAA,CAAA;AAwBO,IAAM,KAAA,GAAQ,CAAO,MAAA,KAA8C,MAAA,CAAO,IAAA,KAAS,KAAA;;;AClD1F,IAAM,YAAN,MAAmB;AAAA,EAEjB,YAAqB,KAAA,EAAU;AAAV,IAAA,IAAA,CAAA,KAAA,GAAA,KAAA;AAAA,EAAW;AAAA,EAAX,KAAA;AAAA,EADZ,IAAA,GAAO,WAAA;AAElB,CAAA;AAMO,IAAM,WAAA,GAAc,CAAO,MAAA,KAA4B;AAC5D,EAAA,IAAI,KAAA,CAAM,MAAM,CAAA,EAAG;AACjB,IAAA,MAAM,IAAI,SAAA,CAAU,MAAA,CAAO,KAAK,CAAA;AAAA,EAClC;AACA,EAAA,OAAO,MAAA,CAAO,KAAA;AAChB;AAEO,IAAM,CAAA,GAAI;AAcV,IAAM,MAAA,GAAS,CAAO,EAAA,KAAyC;AACpE,EAAA,IAAI;AACF,IAAA,OAAO,EAAA,EAAG;AAAA,EACZ,SAAS,KAAA,EAAO;AACd,IAAA,IAAI,iBAAiB,SAAA,EAAW;AAC9B,MAAA,OAAO,GAAA,CAAI,MAAM,KAAK,CAAA;AAAA,IACxB;AACA,IAAA,MAAM,KAAA;AAAA,EACR;AACF","file":"$.js","sourcesContent":["import type { Err, Ok, Result } from \"./types.js\";\n\nexport type { Err, Ok, Result } from \"./types.js\";\n\n/**\n * Lifts a value into the success track.\n *\n * @example\n * ```ts\n * const r = ok(42); // Result<number, never>\n * const typed: Result<number, \"parse\"> = ok(1);\n * ```\n */\nexport const ok = <T, E = never>(value: T): Result<T, E> => ({\n _tag: \"Ok\",\n value,\n});\n\n/**\n * Lifts a value into the error track.\n *\n * @example\n * ```ts\n * const r = err({ kind: \"parse\", message: \"bad json\" });\n * // Result<never, { kind: \"parse\"; message: string }>\n * ```\n */\nexport const err = <T = never, E = unknown>(error: E): Result<T, E> => ({\n _tag: \"Err\",\n error,\n});\n\n/**\n * Type-narrowing predicate: returns `true` when the result is `Ok`.\n *\n * @example\n * ```ts\n * if (isOk(r)) {\n * console.log(r.value); // narrowed to Ok branch\n * }\n * ```\n */\nexport const isOk = <T, E>(result: Result<T, E>): result is Ok<T, E> => result._tag === \"Ok\";\n\n/**\n * Type-narrowing predicate: returns `true` when the result is `Err`.\n *\n * @example\n * ```ts\n * if (isErr(r)) {\n * metrics.inc(\"error\", { kind: r.error.kind });\n * }\n * ```\n */\nexport const isErr = <T, E>(result: Result<T, E>): result is Err<T, E> => result._tag === \"Err\";\n\n/**\n * Fantasy Land `of` — alias of {@link ok}.\n * @deprecated Use {@link ok} instead.\n */\nexport const of = ok;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Dual-form helpers — each export accepts either shape:\n// data-first: `map(result, fn)`\n// curried: `map(fn)(result)`\n// Arity at the call site selects the overload.\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst mapImpl = <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 * Transform the `Ok` value, passing `Err` through unchanged. Dual-form:\n * call data-first or curried (for use with {@link pipe}).\n *\n * @example\n * ```ts\n * map(ok(2), (n) => n * 3); // Ok 6 — data-first\n * pipe(ok(\"x\"), map((s) => s.length));// Ok 1 — curried\n * ```\n */\nexport function map<T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E>;\nexport function map<T, U>(fn: (value: T) => U): <E>(result: Result<T, E>) => Result<U, E>;\nexport function map(\n ...args: [Result<unknown, unknown>, (value: unknown) => unknown] | [(value: unknown) => unknown]\n): unknown {\n if (args.length === 2) return mapImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => mapImpl(result, fn);\n}\n\nconst mapErrImpl = <T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F> =>\n isErr(result) ? err(fn(result.error)) : ok(result.value);\n\n/**\n * Transform the `Err` value, passing `Ok` through unchanged. Useful for\n * unifying heterogeneous failure types into one app-level union.\n *\n * @example\n * ```ts\n * type AppError = { kind: \"http\"; status: number } | { kind: \"parse\" };\n * pipe(\n * fetchSync(url), // Result<Body, { status: number }>\n * mapErr((e): AppError => ({ kind: \"http\", status: e.status })),\n * );\n * ```\n */\nexport function mapErr<T, E, F>(result: Result<T, E>, fn: (error: E) => F): Result<T, F>;\nexport function mapErr<E, F>(fn: (error: E) => F): <T>(result: Result<T, E>) => Result<T, F>;\nexport function mapErr(\n ...args: [Result<unknown, unknown>, (error: unknown) => unknown] | [(error: unknown) => unknown]\n): unknown {\n if (args.length === 2) return mapErrImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => mapErrImpl(result, fn);\n}\n\nconst bimapImpl = <T, U, E, F>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n): Result<U, F> => (isOk(result) ? ok(onOk(result.value)) : err(onErr(result.error)));\n\n/**\n * Transform both tracks at once — `Ok` via `onOk`, `Err` via `onErr`.\n * Equivalent to `mapErr(onErr)(map(onOk)(result))` but in one pass.\n *\n * @example\n * ```ts\n * bimap(parsed, (cfg) => cfg.name, (e) => ({ kind: \"input\", cause: e }));\n * ```\n */\nexport function bimap<T, U, E, F>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n): Result<U, F>;\nexport function bimap<T, U, E, F>(\n onOk: (value: T) => U,\n onErr: (error: E) => F,\n): (result: Result<T, E>) => Result<U, F>;\nexport function bimap(\n ...args:\n | [Result<unknown, unknown>, (value: unknown) => unknown, (error: unknown) => unknown]\n | [(value: unknown) => unknown, (error: unknown) => unknown]\n): unknown {\n if (args.length === 3) return bimapImpl(args[0], args[1], args[2]);\n const [onOk, onErr] = args;\n return (result: Result<unknown, unknown>) => bimapImpl(result, onOk, onErr);\n}\n\nconst flatMapImpl = <T, U, E, F>(\n result: Result<T, E>,\n fn: (value: T) => Result<U, F>,\n): Result<U, E | F> => (isOk(result) ? fn(result.value) : err(result.error));\n\n/**\n * Canonical bind (Fantasy Land `chain`). Chains a Result-returning step,\n * widening the error union to `E | F`. Short-circuits on `Err`.\n *\n * @example\n * ```ts\n * flatMap(parseInput(raw), (data) =>\n * data.id != null ? ok(data) : err({ kind: \"missing_id\" as const }),\n * );\n * // Result<Data, ParseError | { kind: \"missing_id\" }>\n * ```\n */\nexport function flatMap<T, U, E, F>(\n result: Result<T, E>,\n fn: (value: T) => Result<U, F>,\n): Result<U, E | F>;\nexport function flatMap<T, U, F>(\n fn: (value: T) => Result<U, F>,\n): <E>(result: Result<T, E>) => Result<U, E | F>;\nexport function flatMap(\n ...args:\n | [Result<unknown, unknown>, (value: unknown) => Result<unknown, unknown>]\n | [(value: unknown) => Result<unknown, unknown>]\n): unknown {\n if (args.length === 2) return flatMapImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => flatMapImpl(result, fn);\n}\n\nconst recoverImpl = <T, E, F>(\n result: Result<T, E>,\n fn: (error: E) => Result<T, F>,\n): Result<T, F> => (isErr(result) ? fn(result.error) : ok(result.value));\n\n/**\n * Error-track bind — runs `fn` only when the result is `Err`, allowing\n * a failed workflow to recover to `Ok` or remap the failure. Mirror of\n * {@link flatMap} on the error channel.\n *\n * @example\n * ```ts\n * recover(networkResult, (e) =>\n * e.kind === \"rate_limit\" ? ok(cachedBody) : err(e),\n * );\n * ```\n */\nexport function recover<T, E, F>(\n result: Result<T, E>,\n fn: (error: E) => Result<T, F>,\n): Result<T, F>;\nexport function recover<T, E, F>(\n fn: (error: E) => Result<T, F>,\n): (result: Result<T, E>) => Result<T, F>;\nexport function recover(\n ...args:\n | [Result<unknown, unknown>, (error: unknown) => Result<unknown, unknown>]\n | [(error: unknown) => Result<unknown, unknown>]\n): unknown {\n if (args.length === 2) return recoverImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => recoverImpl(result, fn);\n}\n\nconst tapImpl = <T, E>(result: Result<T, E>, fn: (value: T) => void): Result<T, E> => {\n if (isOk(result)) fn(result.value);\n return result;\n};\n\n/**\n * Observe the `Ok` value for side effects (logging, metrics) without\n * changing the carried value. Passes `Err` through untouched.\n *\n * @example\n * ```ts\n * pipe(\n * parseConfig(raw),\n * tap((cfg) => log.info({ msg: \"parsed\", name: cfg.name })),\n * flatMap(validate),\n * );\n * ```\n */\nexport function tap<T, E>(result: Result<T, E>, fn: (value: T) => void): Result<T, E>;\nexport function tap<T>(fn: (value: T) => void): <E>(result: Result<T, E>) => Result<T, E>;\nexport function tap(\n ...args: [Result<unknown, unknown>, (value: unknown) => void] | [(value: unknown) => void]\n): unknown {\n if (args.length === 2) return tapImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => tapImpl(result, fn);\n}\n\nconst tapErrImpl = <T, E>(result: Result<T, E>, fn: (error: E) => void): Result<T, E> => {\n if (isErr(result)) fn(result.error);\n return result;\n};\n\n/**\n * Observe the `Err` value for side effects (logging, metrics) without\n * changing the carried error. Passes `Ok` through untouched.\n *\n * @example\n * ```ts\n * pipe(\n * loadUser(id),\n * tapErr((e) => metrics.inc(\"user.load.fail\", { kind: e.kind })),\n * );\n * ```\n */\nexport function tapErr<T, E>(result: Result<T, E>, fn: (error: E) => void): Result<T, E>;\nexport function tapErr<E>(fn: (error: E) => void): <T>(result: Result<T, E>) => Result<T, E>;\nexport function tapErr(\n ...args: [Result<unknown, unknown>, (error: unknown) => void] | [(error: unknown) => void]\n): unknown {\n if (args.length === 2) return tapErrImpl(args[0], args[1]);\n const fn = args[0];\n return (result: Result<unknown, unknown>) => tapErrImpl(result, fn);\n}\n\nconst matchImpl = <T, E, U>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => U,\n): U => (isOk(result) ? onOk(result.value) : onErr(result.error));\n\n/**\n * Terminal collapse — fold both tracks into a single value. Dual-form:\n * 3-args data-first, 2-args curried for {@link pipe}. Returns whatever\n * the handlers return.\n *\n * For files that also import `match` from `ts-pattern`, the\n * collision-free alias {@link matchResult} is identical.\n *\n * @example\n * ```ts\n * const html = match(parsed, (cfg) => render(cfg), (e) => renderError(e));\n * ```\n */\nexport function match<T, E, U>(\n result: Result<T, E>,\n onOk: (value: T) => U,\n onErr: (error: E) => U,\n): U;\nexport function match<T, E, U>(\n onOk: (value: T) => U,\n onErr: (error: E) => U,\n): (result: Result<T, E>) => U;\nexport function match(\n ...args:\n | [Result<unknown, unknown>, (value: unknown) => unknown, (error: unknown) => unknown]\n | [(value: unknown) => unknown, (error: unknown) => unknown]\n): unknown {\n if (args.length === 3) return matchImpl(args[0], args[1], args[2]);\n const [onOk, onErr] = args;\n return (result: Result<unknown, unknown>) => matchImpl(result, onOk, onErr);\n}\n\n/**\n * Collision-free alias for files that also import `match` from ts-pattern.\n * @deprecated Namespace import carriers (e.g., `import * as R from \"@onrails/result\"`) and use canonical {@link match} instead.\n */\nexport const matchResult = match;\n\n/**\n * Curried collapse with named slots — escape valve when positional `match`\n * order is unclear at the call site (e.g. when both handlers return the same\n * type and a transposed `match(r, onErr, onOk)` would silently compile).\n * @deprecated Use curried {@link match}(onOk, onErr)(result) instead.\n */\nexport const fold =\n <T, E, U>(handlers: { readonly ok: (value: T) => U; readonly err: (error: E) => U }) =>\n (result: Result<T, E>): U =>\n matchImpl(result, handlers.ok, handlers.err);\n\n/**\n * Returns the `Ok` value, or `defaultValue` when the result is `Err`.\n *\n * @example\n * ```ts\n * unwrapOr(parsedSetting, \"default-value\");\n * ```\n */\nexport const unwrapOr = <T, E>(result: Result<T, E>, defaultValue: T): T =>\n isOk(result) ? result.value : defaultValue;\n\n/**\n * Test/assert helper — throws the original Err value when called on Err.\n *\n * @throws when the result is Err — assertion-tier; use {@link match} / {@link unwrapOr} in business logic.\n * Allowed in `*.spec.ts` / `*.test.ts`; flagged elsewhere by the plugins.\n */\nexport function unwrapOk<T, E>(result: Result<T, E>): T {\n if (isErr(result)) throw result.error;\n return result.value;\n}\n\n/**\n * Test/assert helper — throws TypeError when called on Ok.\n *\n * @throws when the result is Ok — assertion-tier; allowed in `*.spec.ts` / `*.test.ts`; flagged elsewhere by the plugins.\n */\nexport function unwrapErr<T, E>(result: Result<T, E>): E {\n if (isOk(result)) throw new TypeError(\"unwrapErr called on Ok\");\n return result.error;\n}\n\n/** Wrap a throwing sync function — neverthrow `Result.fromThrowable` */\nexport function trySync<A extends readonly unknown[], T, E>(\n fn: (...args: A) => T,\n onThrow: (error: unknown) => E,\n): (...args: A) => Result<T, E>;\nexport function trySync<F extends (...args: never) => unknown, E>(\n fn: F,\n onThrow: (error: unknown) => E,\n): (...args: Parameters<F>) => Result<ReturnType<F>, E>;\nexport function trySync(\n fn: (...args: never) => unknown,\n onThrow: (error: unknown) => unknown,\n): (...args: never) => Result<unknown, unknown> {\n return (...args: never) => {\n try {\n return ok(fn(...args));\n } catch (error) {\n return err(onThrow(error));\n }\n };\n}\n\n/** First failure wins; otherwise collects values in order */\nexport const combine = <T, E>(results: readonly Result<T, E>[]): Result<T[], E> => {\n const values: T[] = [];\n for (const result of results) {\n if (isErr(result)) return err(result.error);\n values.push(result.value);\n }\n return ok(values);\n};\n\n/** Tuple-preserving combine (neverthrow-style) */\nexport const combineTuple = <const R extends readonly Result<unknown, unknown>[]>(\n results: R,\n): CombineTuple<R> =>\n // Runtime identical to combine; the cast restores per-index tuple types.\n combine(results as readonly Result<unknown, unknown>[]) as CombineTuple<R>;\n\ntype _OkValue<R> = R extends { _tag: \"Ok\"; readonly value: infer T } ? T : never;\ntype _ErrValue<R> = R extends { _tag: \"Err\"; readonly error: infer E } ? E : never;\n\ntype CombineTuple<R extends readonly Result<unknown, unknown>[]> = Result<\n { [K in keyof R]: _OkValue<R[K]> },\n { [K in keyof R]: _ErrValue<R[K]> }[number]\n>;\n\n/**\n * Variadic value-first pipe — threads `value` through up to nine unary fns.\n *\n * ```ts\n * pipe(\n * parseConfig(raw),\n * map((cfg) => cfg.name),\n * flatMap((name) => name ? ok(name) : err({ kind: \"empty\" })),\n * tap(log),\n * );\n * ```\n */\nexport function pipe<A>(value: A): A;\nexport function pipe<A, B>(value: A, ab: (a: A) => B): B;\nexport function pipe<A, B, C>(value: A, ab: (a: A) => B, bc: (b: B) => C): C;\nexport function pipe<A, B, C, D>(value: A, ab: (a: A) => B, bc: (b: B) => C, cd: (c: C) => D): D;\nexport function pipe<A, B, C, D, E>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n): E;\nexport function pipe<A, B, C, D, E, F>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n): F;\nexport function pipe<A, B, C, D, E, F, G>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n): G;\nexport function pipe<A, B, C, D, E, F, G, H>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n): H;\nexport function pipe<A, B, C, D, E, F, G, H, I>(\n value: A,\n ab: (a: A) => B,\n bc: (b: B) => C,\n cd: (c: C) => D,\n de: (d: D) => E,\n ef: (e: E) => F,\n fg: (f: F) => G,\n gh: (g: G) => H,\n hi: (h: H) => I,\n): I;\nexport function pipe(value: unknown, ...fns: ReadonlyArray<(x: unknown) => unknown>): unknown {\n let acc = value;\n for (const fn of fns) {\n acc = fn(acc);\n }\n return acc;\n}\n","import { err, isErr } from \"./result.js\";\nimport type { Result } from \"./types.js\";\n\n/** Internal control-flow signal — not part of public error types */\nclass ErrSignal<E> {\n readonly _tag = \"ErrSignal\" as const;\n constructor(readonly error: E) {}\n}\n\n/**\n * Unwrap a {@link Result} inside {@link tryGen} (Rust `?` for sync code).\n * Prefer {@link flatMap} / {@link fluent} for long pipelines.\n */\nexport const yieldResult = <T, E>(result: Result<T, E>): T => {\n if (isErr(result)) {\n throw new ErrSignal(result.error);\n }\n return result.value;\n};\n\nexport const $ = yieldResult;\n\n/**\n * Run a block that uses {@link yieldResult}; returns the final `Result` or the first Err.\n *\n * @example\n * ```ts\n * const out = tryGen(() => {\n * const a = $(parseA());\n * const b = $(parseB());\n * return ok(a + b);\n * });\n * ```\n */\nexport const tryGen = <T, E>(fn: () => Result<T, E>): Result<T, E> => {\n try {\n return fn();\n } catch (error) {\n if (error instanceof ErrSignal) {\n return err(error.error);\n }\n throw error;\n }\n};\n"]}
|
package/dist/_.d.cts
DELETED
package/dist/_.d.ts
DELETED
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import { R as Result, U as UnexpectedError } from './types-C2Dp1d5J.cjs';
|
|
2
|
-
|
|
3
|
-
type PromiseFactory<T, E> = () => Promise<Result<T, E>>;
|
|
4
|
-
type AsyncOk<R> = R extends ResultAsync<infer T, unknown> ? T : never;
|
|
5
|
-
type AsyncErr<R> = R extends ResultAsync<unknown, infer E> ? E : never;
|
|
6
|
-
type CombineTupleAsync<R extends readonly ResultAsync<unknown, unknown>[]> = ResultAsync<{
|
|
7
|
-
[K in keyof R]: AsyncOk<R[K]>;
|
|
8
|
-
}, {
|
|
9
|
-
[K in keyof R]: AsyncErr<R[K]>;
|
|
10
|
-
}[number]>;
|
|
11
|
-
/**
|
|
12
|
-
* Async result — public API never exposes `Promise<Result<…>>` directly;
|
|
13
|
-
* await via `.resolve()` or `.match()`.
|
|
14
|
-
*/
|
|
15
|
-
declare class ResultAsync<T, E> {
|
|
16
|
-
protected readonly run: PromiseFactory<T, E>;
|
|
17
|
-
private promise;
|
|
18
|
-
protected constructor(run: PromiseFactory<T, E>);
|
|
19
|
-
static fromResult<T, E>(result: Result<T, E>): ResultAsync<T, E>;
|
|
20
|
-
static fromPromise<T, E>(promise: PromiseLike<T>, onReject: (error: unknown) => E): ResultAsync<T, E>;
|
|
21
|
-
static fromSafePromise<T, E = never>(promise: PromiseLike<T>): ResultAsync<T, E>;
|
|
22
|
-
/**
|
|
23
|
-
* Defers work until {@link resolve}. Unlike {@link fromPromise}, nothing runs
|
|
24
|
-
* until the `ResultAsync` is resolved (e.g. by `combineTuple` / `combineTupleParallel`).
|
|
25
|
-
*/
|
|
26
|
-
static defer<T, E>(fn: () => Promise<Result<T, E>>): ResultAsync<T, E>;
|
|
27
|
-
static ok<T>(value: T): ResultAsync<T, never>;
|
|
28
|
-
static ok<T, E>(value: T): ResultAsync<T, E>;
|
|
29
|
-
static err<T = never, E = unknown>(error: E): ResultAsync<T, E>;
|
|
30
|
-
/** @see {@link fromAsync} from `@onrails/result/interop` */
|
|
31
|
-
static fromResultPromise<T, E>(promise: Promise<Result<T, E>>, onDefect?: (error: unknown) => E | UnexpectedError): ResultAsync<T, E | UnexpectedError>;
|
|
32
|
-
static combine<T, E>(results: readonly ResultAsync<T, E>[]): ResultAsync<T[], E>;
|
|
33
|
-
static combineTuple<const R extends readonly ResultAsync<unknown, unknown>[]>(results: R): CombineTupleAsync<R>;
|
|
34
|
-
/**
|
|
35
|
-
* Like {@link combineTuple}, but starts every branch before awaiting (wall-clock
|
|
36
|
-
* parallel for independent IO). On failure, returns the first `Err` in input order.
|
|
37
|
-
*/
|
|
38
|
-
static combineTupleParallel<const R extends readonly ResultAsync<unknown, unknown>[]>(results: R): CombineTupleAsync<R>;
|
|
39
|
-
map<U>(fn: (value: T) => U): ResultAsync<U, E>;
|
|
40
|
-
mapErr<F>(fn: (error: E) => F): ResultAsync<T, F>;
|
|
41
|
-
flatMap<U, F = E>(fn: (value: T) => ResultAsync<U, F> | Result<U, F> | {
|
|
42
|
-
inner: Result<U, F>;
|
|
43
|
-
}): ResultAsync<U, E | F>;
|
|
44
|
-
andThen<U, F = E>(fn: (value: T) => ResultAsync<U, F> | Result<U, F> | {
|
|
45
|
-
inner: Result<U, F>;
|
|
46
|
-
}): ResultAsync<U, E | F>;
|
|
47
|
-
/**
|
|
48
|
-
* Alias for {@link flatMap}.
|
|
49
|
-
* @deprecated Use {@link flatMap} or keep/use compat {@link andThen} instead.
|
|
50
|
-
*/
|
|
51
|
-
chain<U, F = E>(fn: (value: T) => ResultAsync<U, F> | Result<U, F> | {
|
|
52
|
-
inner: Result<U, F>;
|
|
53
|
-
}): ResultAsync<U, E | F>;
|
|
54
|
-
recover<F>(fn: (error: E) => ResultAsync<T, F> | Result<T, F>): ResultAsync<T, F>;
|
|
55
|
-
orElse<F>(fn: (error: E) => ResultAsync<T, F> | Result<T, F>): ResultAsync<T, F>;
|
|
56
|
-
tap(fn: (value: T) => void): ResultAsync<T, E>;
|
|
57
|
-
tapErr(fn: (error: E) => void): ResultAsync<T, E>;
|
|
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
|
-
*/
|
|
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
|
-
*/
|
|
68
|
-
isErr(): Promise<boolean>;
|
|
69
|
-
match<U1, U2 = U1>(onOk: (value: T) => U1, onErr: (error: E) => U2): Promise<U1 | U2>;
|
|
70
|
-
resolve(): Promise<Result<T, E>>;
|
|
71
|
-
/**
|
|
72
|
-
* Thenable shim — `await ra` resolves to a bare tagged-union `Result<T, E>`.
|
|
73
|
-
* Narrow with `isOk(r)` / `isErr(r)` to read `.value` / `.error`.
|
|
74
|
-
*/
|
|
75
|
-
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>;
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Lifts a value into an `Ok` async result.
|
|
79
|
-
*
|
|
80
|
-
* @example
|
|
81
|
-
* ```ts
|
|
82
|
-
* const r = okAsync(42); // ResultAsync<number, never>
|
|
83
|
-
* ```
|
|
84
|
-
*/
|
|
85
|
-
declare const okAsync: typeof ResultAsync.ok;
|
|
86
|
-
/**
|
|
87
|
-
* Lifts a value into an `Err` async result.
|
|
88
|
-
*
|
|
89
|
-
* @example
|
|
90
|
-
* ```ts
|
|
91
|
-
* const r = errAsync({ kind: "not_found" as const });
|
|
92
|
-
* ```
|
|
93
|
-
*/
|
|
94
|
-
declare const errAsync: typeof ResultAsync.err;
|
|
95
|
-
/**
|
|
96
|
-
* Wraps a `PromiseLike<T>` into a {@link ResultAsync}. Reject reasons go
|
|
97
|
-
* through `onReject` to become typed `Err`s; success becomes `Ok<T>`.
|
|
98
|
-
*
|
|
99
|
-
* @example
|
|
100
|
-
* ```ts
|
|
101
|
-
* const body = fromPromise(
|
|
102
|
-
* fetch(url).then((r) => r.text()),
|
|
103
|
-
* (e): NetworkError => ({ kind: "network", cause: String(e) }),
|
|
104
|
-
* );
|
|
105
|
-
* ```
|
|
106
|
-
*/
|
|
107
|
-
declare const fromPromise: typeof ResultAsync.fromPromise;
|
|
108
|
-
/**
|
|
109
|
-
* Wraps a `PromiseLike<T>` that **never rejects** into {@link ResultAsync}.
|
|
110
|
-
* Skips the `onReject` mapper. Use only when the promise is provably safe.
|
|
111
|
-
*/
|
|
112
|
-
declare const fromSafePromise: typeof ResultAsync.fromSafePromise;
|
|
113
|
-
/**
|
|
114
|
-
* Heterogeneous async tuple combine — left-to-right, short-circuit on
|
|
115
|
-
* first `Err` in input order. Preserves tuple positions so destructuring
|
|
116
|
-
* stays type-safe.
|
|
117
|
-
*
|
|
118
|
-
* @example
|
|
119
|
-
* ```ts
|
|
120
|
-
* const combined = sequenceTupleAsync([loadCfg(), loadCatalog()] as const);
|
|
121
|
-
* // ResultAsync<readonly [Cfg, Catalog], CfgError | CatalogError>
|
|
122
|
-
* ```
|
|
123
|
-
* @deprecated Use static {@link ResultAsync.combineTuple} instead.
|
|
124
|
-
*/
|
|
125
|
-
declare const sequenceTupleAsync: typeof ResultAsync.combineTuple;
|
|
126
|
-
/**
|
|
127
|
-
* Heterogeneous async tuple combine — branches overlap in wall-clock time.
|
|
128
|
-
* Returns the first `Err` in **input** order (not completion order).
|
|
129
|
-
*
|
|
130
|
-
* @example
|
|
131
|
-
* ```ts
|
|
132
|
-
* const combined = parallelTupleAsync([
|
|
133
|
-
* loadProfile(id),
|
|
134
|
-
* loadMetrics(id),
|
|
135
|
-
* loadFlags(id),
|
|
136
|
-
* ] as const);
|
|
137
|
-
* // ResultAsync<readonly [Profile, Metrics, Flags], …>
|
|
138
|
-
* ```
|
|
139
|
-
*/
|
|
140
|
-
declare const parallelTupleAsync: typeof ResultAsync.combineTupleParallel;
|
|
141
|
-
/**
|
|
142
|
-
* Convenience wrapper over {@link fromPromise} with default `Error`
|
|
143
|
-
* normalization. Call without `onReject` to get `ResultAsync<T, Error>`,
|
|
144
|
-
* or pass a custom mapper for a typed error.
|
|
145
|
-
*
|
|
146
|
-
* @example
|
|
147
|
-
* ```ts
|
|
148
|
-
* // Default: rejection → Err(Error)
|
|
149
|
-
* const a = tryAsync(db.users.insert(row));
|
|
150
|
-
*
|
|
151
|
-
* // Custom: typed error
|
|
152
|
-
* const b = tryAsync(db.users.insert(row), (e): DbError => ({
|
|
153
|
-
* kind: "db",
|
|
154
|
-
* cause: e,
|
|
155
|
-
* }));
|
|
156
|
-
* ```
|
|
157
|
-
*/
|
|
158
|
-
declare function tryAsync<T>(promise: PromiseLike<T>): ResultAsync<T, Error>;
|
|
159
|
-
declare function tryAsync<T, E>(promise: PromiseLike<T>, onReject: (error: unknown) => E): ResultAsync<T, E>;
|
|
160
|
-
|
|
161
|
-
export { ResultAsync as R, fromSafePromise as a, errAsync as e, fromPromise as f, okAsync as o, parallelTupleAsync as p, sequenceTupleAsync as s, tryAsync as t };
|