@onrails/result 0.1.4 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DESIGN.md +2 -2
- package/README.md +0 -3
- package/dist/$.cjs.map +1 -1
- package/dist/$.js.map +1 -1
- package/dist/_.d.cts +1 -1
- package/dist/_.d.ts +1 -1
- package/dist/async-1fIhfzFu.d.ts +290 -0
- package/dist/async-u8U5P2EH.d.cts +290 -0
- package/dist/compat/neverthrow.cjs +234 -50
- package/dist/compat/neverthrow.cjs.map +1 -1
- package/dist/compat/neverthrow.d.cts +2 -2
- package/dist/compat/neverthrow.d.ts +2 -2
- package/dist/compat/neverthrow.js +231 -47
- package/dist/compat/neverthrow.js.map +1 -1
- package/dist/extra.cjs +1 -24
- package/dist/extra.cjs.map +1 -1
- package/dist/extra.d.cts +3 -9
- package/dist/extra.d.ts +3 -9
- package/dist/extra.js +2 -24
- package/dist/extra.js.map +1 -1
- package/dist/fluent.cjs +0 -2
- package/dist/fluent.cjs.map +1 -1
- package/dist/fluent.d.cts +2 -6
- package/dist/fluent.d.ts +2 -6
- package/dist/fluent.js +0 -2
- package/dist/fluent.js.map +1 -1
- package/dist/index.cjs +220 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +174 -51
- package/dist/index.d.ts +174 -51
- package/dist/index.js +221 -44
- package/dist/index.js.map +1 -1
- package/dist/interop.cjs +203 -23
- package/dist/interop.cjs.map +1 -1
- package/dist/interop.d.cts +48 -8
- package/dist/interop.d.ts +48 -8
- package/dist/interop.js +203 -23
- package/dist/interop.js.map +1 -1
- package/dist/pipe.cjs.map +1 -1
- package/dist/pipe.d.cts +7 -5
- package/dist/pipe.d.ts +7 -5
- package/dist/pipe.js.map +1 -1
- package/dist/railway.cjs +203 -24
- package/dist/railway.cjs.map +1 -1
- package/dist/railway.d.cts +2 -2
- package/dist/railway.d.ts +2 -2
- package/dist/railway.js +203 -24
- package/dist/railway.js.map +1 -1
- package/dist/try-gen.cjs.map +1 -1
- package/dist/try-gen.d.cts +29 -3
- package/dist/try-gen.d.ts +29 -3
- package/dist/try-gen.js.map +1 -1
- package/dist/types-BQ9vv0nD.d.cts +55 -0
- package/dist/types-BQ9vv0nD.d.ts +55 -0
- package/dist/validation.cjs.map +1 -1
- package/dist/validation.d.cts +1 -1
- package/dist/validation.d.ts +1 -1
- package/dist/validation.js.map +1 -1
- package/package.json +5 -3
- package/skills/railway-do-notation/SKILL.md +436 -0
- package/skills/result-composition/SKILL.md +325 -0
- package/dist/async-CH8Cs8KE.d.cts +0 -161
- package/dist/async-DKISqIBc.d.ts +0 -161
- package/dist/types-C2Dp1d5J.d.cts +0 -21
- package/dist/types-C2Dp1d5J.d.ts +0 -21
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import { R as Result, U as UnexpectedError } from './types-BQ9vv0nD.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 railway carrier. Wraps a deferred `Promise<Result<T, E>>` and exposes
|
|
13
|
+
* the same dual-track transforms as the sync {@link Result} — `map`, `flatMap`,
|
|
14
|
+
* `recover`, `tap`, `match`. The public API never surfaces `Promise<Result<…>>`
|
|
15
|
+
* directly: `await` the instance (it is thenable) or call {@link resolve} /
|
|
16
|
+
* {@link match} to settle it.
|
|
17
|
+
*
|
|
18
|
+
* Construct via the static factories ({@link ok}, {@link err}, {@link fromPromise},
|
|
19
|
+
* {@link defer}, …) rather than `new` — the constructor is `protected`.
|
|
20
|
+
*
|
|
21
|
+
* @typeParam T - the `Ok` value type
|
|
22
|
+
* @typeParam E - the `Err` error type
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const user = ResultAsync.fromPromise(api.getUser(id), toAppError)
|
|
27
|
+
* .map((u) => u.profile)
|
|
28
|
+
* .recover(() => ResultAsync.ok(guestProfile));
|
|
29
|
+
*
|
|
30
|
+
* const r = await user; // Result<Profile, AppError>
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
declare class ResultAsync<T, E> {
|
|
34
|
+
protected readonly run: PromiseFactory<T, E>;
|
|
35
|
+
private promise;
|
|
36
|
+
protected constructor(run: PromiseFactory<T, E>);
|
|
37
|
+
/**
|
|
38
|
+
* Lifts an already-settled sync {@link Result} into a {@link ResultAsync}.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* const ra = ResultAsync.fromResult(ok(42)); // ResultAsync<number, never>
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
static fromResult<T, E>(result: Result<T, E>): ResultAsync<T, E>;
|
|
46
|
+
/**
|
|
47
|
+
* Wraps a `PromiseLike<T>` that may reject. Rejections pass through `onReject`
|
|
48
|
+
* to become a typed `Err`; resolution becomes `Ok<T>`.
|
|
49
|
+
*
|
|
50
|
+
* @param promise - the promise to wrap
|
|
51
|
+
* @param onReject - maps a rejection reason to the `Err` channel
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* const body = ResultAsync.fromPromise(
|
|
56
|
+
* fetch(url).then((r) => r.text()),
|
|
57
|
+
* (e): NetError => ({ kind: "net", cause: String(e) }),
|
|
58
|
+
* );
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
static fromPromise<T, E>(promise: PromiseLike<T>, onReject: (error: unknown) => E): ResultAsync<T, E>;
|
|
62
|
+
/**
|
|
63
|
+
* Wraps a `PromiseLike<T>` that is **guaranteed not to reject**, skipping the
|
|
64
|
+
* `onReject` mapper. Use only when rejection is provably impossible.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* const now = ResultAsync.fromSafePromise(Promise.resolve(Date.now()));
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
static fromSafePromise<T, E = never>(promise: PromiseLike<T>): ResultAsync<T, E>;
|
|
72
|
+
/**
|
|
73
|
+
* Defers work until {@link resolve}. Unlike {@link fromPromise}, the factory
|
|
74
|
+
* does not run until the `ResultAsync` is resolved (e.g. by `combineTuple` /
|
|
75
|
+
* `combineTupleParallel`). The factory runs at most once — `resolve` memoizes.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* const insert = ResultAsync.defer(() => db.orders.insert(row));
|
|
80
|
+
* // nothing has run yet
|
|
81
|
+
* const r = await insert; // factory runs exactly once here
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
static defer<T, E>(fn: () => Promise<Result<T, E>>): ResultAsync<T, E>;
|
|
85
|
+
/**
|
|
86
|
+
* Lifts a value into an `Ok` async result.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* const r = ResultAsync.ok(42); // ResultAsync<number, never>
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
static ok<T>(value: T): ResultAsync<T, never>;
|
|
94
|
+
static ok<T, E>(value: T): ResultAsync<T, E>;
|
|
95
|
+
/**
|
|
96
|
+
* Lifts an error into an `Err` async result.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* const r = ResultAsync.err({ kind: "not_found" as const });
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
static err<T = never, E = unknown>(error: E): ResultAsync<T, E>;
|
|
104
|
+
/**
|
|
105
|
+
* Lifts an existing `Promise<Result<T, E>>` (e.g. from interop code) into a
|
|
106
|
+
* {@link ResultAsync}, auto-flattening one level of double-wrapping. A thrown
|
|
107
|
+
* defect is routed through `onDefect`, defaulting to {@link UnexpectedError}.
|
|
108
|
+
*
|
|
109
|
+
* @param promise - a promise that already yields a `Result`
|
|
110
|
+
* @param onDefect - maps an unexpected throw to the `Err` channel
|
|
111
|
+
* @see {@link fromAsync} from `@onrails/result/interop`
|
|
112
|
+
*/
|
|
113
|
+
static fromResultPromise<T, E>(promise: Promise<Result<T, E>>, onDefect?: (error: unknown) => E | UnexpectedError): ResultAsync<T, E | UnexpectedError>;
|
|
114
|
+
/**
|
|
115
|
+
* Combines a homogeneous array of async results into one. Resolves
|
|
116
|
+
* **sequentially** in input order, short-circuiting on the first `Err`.
|
|
117
|
+
* For heterogeneous tuples that preserve per-index types, use
|
|
118
|
+
* {@link combineTuple}; for wall-clock overlap, {@link combineTupleParallel}.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* const all = ResultAsync.combine([loadA(), loadB(), loadC()]);
|
|
123
|
+
* // ResultAsync<Item[], LoadError>
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
static combine<T, E>(results: readonly ResultAsync<T, E>[]): ResultAsync<T[], E>;
|
|
127
|
+
/**
|
|
128
|
+
* Heterogeneous async tuple combine — resolves branches **sequentially**
|
|
129
|
+
* (left-to-right), returning the first `Err` in input order. Preserves each
|
|
130
|
+
* branch's `Ok` type by position, so the result destructures type-safely.
|
|
131
|
+
* This is the canonical sequential async combine (replaces the former
|
|
132
|
+
* `sequenceTupleAsync`).
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts
|
|
136
|
+
* const combined = ResultAsync.combineTuple([loadCfg(), loadCatalog()] as const);
|
|
137
|
+
* // ResultAsync<readonly [Cfg, Catalog], CfgError | CatalogError>
|
|
138
|
+
* const r = await combined;
|
|
139
|
+
* if (isOk(r)) {
|
|
140
|
+
* const [cfg, catalog] = r.value; // typed per position
|
|
141
|
+
* }
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
static combineTuple<const R extends readonly ResultAsync<unknown, unknown>[]>(results: R): CombineTupleAsync<R>;
|
|
145
|
+
/**
|
|
146
|
+
* Like {@link combineTuple}, but starts every branch before awaiting (wall-clock
|
|
147
|
+
* parallel for independent IO). On failure, returns the first `Err` in input order.
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```ts
|
|
151
|
+
* // independent IO — overlap them
|
|
152
|
+
* const combined = ResultAsync.combineTupleParallel([
|
|
153
|
+
* loadProfile(id),
|
|
154
|
+
* loadMetrics(id),
|
|
155
|
+
* ] as const);
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
static combineTupleParallel<const R extends readonly ResultAsync<unknown, unknown>[]>(results: R): CombineTupleAsync<R>;
|
|
159
|
+
/**
|
|
160
|
+
* Transforms the `Ok` value, passing `Err` through unchanged.
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```ts
|
|
164
|
+
* ResultAsync.ok(2).map((n) => n * 3); // ResultAsync<number> → Ok 6
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
map<U>(fn: (value: T) => U): ResultAsync<U, E>;
|
|
168
|
+
/**
|
|
169
|
+
* Transforms the `Err` value, passing `Ok` through unchanged — useful for
|
|
170
|
+
* unifying heterogeneous failures into one app-level union.
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* ```ts
|
|
174
|
+
* load(id).mapErr((e): AppError => ({ kind: "load", cause: e }));
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
mapErr<F>(fn: (error: E) => F): ResultAsync<T, F>;
|
|
178
|
+
/**
|
|
179
|
+
* Canonical bind — chains a step that itself returns a `ResultAsync` or sync
|
|
180
|
+
* `Result`, short-circuiting on `Err`. Error types accumulate (`E | F`).
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```ts
|
|
184
|
+
* authenticate(req)
|
|
185
|
+
* .flatMap((user) => loadProfile(user.id)) // ResultAsync
|
|
186
|
+
* .flatMap((p) => validate(p)); // sync Result also accepted
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
flatMap<U, F = E>(fn: (value: T) => ResultAsync<U, F> | Result<U, F> | {
|
|
190
|
+
inner: Result<U, F>;
|
|
191
|
+
}): ResultAsync<U, E | F>;
|
|
192
|
+
/**
|
|
193
|
+
* neverthrow-compat alias of {@link flatMap}. Kept as the documented compat
|
|
194
|
+
* tier; prefer `flatMap` in new code.
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```ts
|
|
198
|
+
* authenticate(req).andThen((user) => loadProfile(user.id));
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
201
|
+
andThen<U, F = E>(fn: (value: T) => ResultAsync<U, F> | Result<U, F> | {
|
|
202
|
+
inner: Result<U, F>;
|
|
203
|
+
}): ResultAsync<U, E | F>;
|
|
204
|
+
/**
|
|
205
|
+
* Error-channel bind — runs `fn` only on `Err`, swapping in a recovery
|
|
206
|
+
* `ResultAsync` or sync `Result`. `Ok` passes through. The mirror of
|
|
207
|
+
* {@link flatMap} on the error track.
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```ts
|
|
211
|
+
* loadFromCache(id).recover(() => loadFromOrigin(id));
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
recover<F>(fn: (error: E) => ResultAsync<T, F> | Result<T, F>): ResultAsync<T, F>;
|
|
215
|
+
/**
|
|
216
|
+
* neverthrow-compat alias of {@link recover}. Kept as the documented compat
|
|
217
|
+
* tier; prefer `recover` in new code.
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```ts
|
|
221
|
+
* loadFromCache(id).orElse(() => loadFromOrigin(id));
|
|
222
|
+
* ```
|
|
223
|
+
*/
|
|
224
|
+
orElse<F>(fn: (error: E) => ResultAsync<T, F> | Result<T, F>): ResultAsync<T, F>;
|
|
225
|
+
/**
|
|
226
|
+
* Runs a side effect on the `Ok` value and passes the result through
|
|
227
|
+
* unchanged; a no-op on `Err`. Mirrors {@link tapErr}.
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```ts
|
|
231
|
+
* saveUser(u).tap((saved) => analytics.track("user_saved", saved.id));
|
|
232
|
+
* ```
|
|
233
|
+
*/
|
|
234
|
+
tap(fn: (value: T) => void): ResultAsync<T, E>;
|
|
235
|
+
/**
|
|
236
|
+
* Runs a side effect on the `Err` value and passes the result through
|
|
237
|
+
* unchanged; a no-op on `Ok`. The error-track mirror of {@link tap}.
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```ts
|
|
241
|
+
* saveUser(u).tapErr((e) => logger.warn("save failed", e));
|
|
242
|
+
* ```
|
|
243
|
+
*/
|
|
244
|
+
tapErr(fn: (error: E) => void): ResultAsync<T, E>;
|
|
245
|
+
/**
|
|
246
|
+
* Settles to the `Ok` value, or `defaultValue` if `Err`. Terminal — returns a
|
|
247
|
+
* plain `Promise`, not a `ResultAsync`.
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```ts
|
|
251
|
+
* const profile = await loadProfile(id).unwrapOr(guestProfile);
|
|
252
|
+
* ```
|
|
253
|
+
*/
|
|
254
|
+
unwrapOr<U>(defaultValue: U): Promise<T | U>;
|
|
255
|
+
/**
|
|
256
|
+
* Terminal collapse — folds both tracks into a single awaited value. Returns
|
|
257
|
+
* a plain `Promise`, settling the carrier exactly once.
|
|
258
|
+
*
|
|
259
|
+
* @param onOk - handles the `Ok` value
|
|
260
|
+
* @param onErr - handles the `Err` value
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* ```ts
|
|
264
|
+
* const status = await save(row).match(
|
|
265
|
+
* () => 200,
|
|
266
|
+
* (e) => (e.kind === "conflict" ? 409 : 500),
|
|
267
|
+
* );
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
match<U1, U2 = U1>(onOk: (value: T) => U1, onErr: (error: E) => U2): Promise<U1 | U2>;
|
|
271
|
+
/**
|
|
272
|
+
* Settles the carrier to a bare sync {@link Result}, memoizing so the
|
|
273
|
+
* underlying factory runs at most once. Prefer `await ra` (the thenable) or
|
|
274
|
+
* {@link match} in app code; use `resolve` when you need the tagged union back.
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```ts
|
|
278
|
+
* const r = await load(id).resolve(); // Result<Data, LoadError>
|
|
279
|
+
* if (isOk(r)) use(r.value);
|
|
280
|
+
* ```
|
|
281
|
+
*/
|
|
282
|
+
resolve(): Promise<Result<T, E>>;
|
|
283
|
+
/**
|
|
284
|
+
* Thenable shim — `await ra` resolves to a bare tagged-union `Result<T, E>`.
|
|
285
|
+
* Narrow with `isOk(r)` / `isErr(r)` to read `.value` / `.error`.
|
|
286
|
+
*/
|
|
287
|
+
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>;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export { ResultAsync as R };
|