@onrails/result 0.1.3 → 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.
Files changed (68) hide show
  1. package/DESIGN.md +2 -2
  2. package/README.md +0 -3
  3. package/RECIPES.md +16 -15
  4. package/dist/$.cjs +40 -0
  5. package/dist/$.cjs.map +1 -0
  6. package/dist/$.js +36 -0
  7. package/dist/$.js.map +1 -0
  8. package/dist/_.d.cts +2 -0
  9. package/dist/_.d.ts +2 -0
  10. package/dist/async-1fIhfzFu.d.ts +290 -0
  11. package/dist/async-u8U5P2EH.d.cts +290 -0
  12. package/dist/compat/neverthrow.cjs +234 -50
  13. package/dist/compat/neverthrow.cjs.map +1 -1
  14. package/dist/compat/neverthrow.d.cts +2 -2
  15. package/dist/compat/neverthrow.d.ts +2 -2
  16. package/dist/compat/neverthrow.js +231 -47
  17. package/dist/compat/neverthrow.js.map +1 -1
  18. package/dist/extra.cjs +1 -24
  19. package/dist/extra.cjs.map +1 -1
  20. package/dist/extra.d.cts +3 -9
  21. package/dist/extra.d.ts +3 -9
  22. package/dist/extra.js +2 -24
  23. package/dist/extra.js.map +1 -1
  24. package/dist/fluent.cjs +0 -2
  25. package/dist/fluent.cjs.map +1 -1
  26. package/dist/fluent.d.cts +2 -6
  27. package/dist/fluent.d.ts +2 -6
  28. package/dist/fluent.js +0 -2
  29. package/dist/fluent.js.map +1 -1
  30. package/dist/index.cjs +226 -47
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +174 -51
  33. package/dist/index.d.ts +174 -51
  34. package/dist/index.js +227 -44
  35. package/dist/index.js.map +1 -1
  36. package/dist/interop.cjs +209 -23
  37. package/dist/interop.cjs.map +1 -1
  38. package/dist/interop.d.cts +60 -10
  39. package/dist/interop.d.ts +60 -10
  40. package/dist/interop.js +209 -23
  41. package/dist/interop.js.map +1 -1
  42. package/dist/pipe.cjs.map +1 -1
  43. package/dist/pipe.d.cts +7 -5
  44. package/dist/pipe.d.ts +7 -5
  45. package/dist/pipe.js.map +1 -1
  46. package/dist/railway.cjs +205 -24
  47. package/dist/railway.cjs.map +1 -1
  48. package/dist/railway.d.cts +20 -3
  49. package/dist/railway.d.ts +20 -3
  50. package/dist/railway.js +205 -25
  51. package/dist/railway.js.map +1 -1
  52. package/dist/try-gen.cjs.map +1 -1
  53. package/dist/try-gen.d.cts +29 -3
  54. package/dist/try-gen.d.ts +29 -3
  55. package/dist/try-gen.js.map +1 -1
  56. package/dist/types-BQ9vv0nD.d.cts +55 -0
  57. package/dist/types-BQ9vv0nD.d.ts +55 -0
  58. package/dist/validation.cjs.map +1 -1
  59. package/dist/validation.d.cts +1 -1
  60. package/dist/validation.d.ts +1 -1
  61. package/dist/validation.js.map +1 -1
  62. package/package.json +10 -3
  63. package/skills/railway-do-notation/SKILL.md +436 -0
  64. package/skills/result-composition/SKILL.md +325 -0
  65. package/dist/async-CH8Cs8KE.d.cts +0 -161
  66. package/dist/async-DKISqIBc.d.ts +0 -161
  67. package/dist/types-C2Dp1d5J.d.cts +0 -21
  68. 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 };