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