@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
package/dist/index.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
// src/internal/dual.ts
|
|
2
|
+
var dual = (arity, body) => {
|
|
3
|
+
const run = body;
|
|
4
|
+
const dispatcher = (...args) => args.length >= arity ? run(...args) : (self) => run(self, ...args);
|
|
5
|
+
return dispatcher;
|
|
6
|
+
};
|
|
7
|
+
|
|
1
8
|
// src/result.ts
|
|
2
9
|
var ok = (value) => ({
|
|
3
10
|
_tag: "Ok",
|
|
@@ -9,68 +16,47 @@ var err = (error) => ({
|
|
|
9
16
|
});
|
|
10
17
|
var isOk = (result) => result._tag === "Ok";
|
|
11
18
|
var isErr = (result) => result._tag === "Err";
|
|
12
|
-
var
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
if (args.length === 2) return flatMapImpl(args[0], args[1]);
|
|
34
|
-
const fn = args[0];
|
|
35
|
-
return (result) => flatMapImpl(result, fn);
|
|
36
|
-
}
|
|
37
|
-
var recoverImpl = (result, fn) => isErr(result) ? fn(result.error) : ok(result.value);
|
|
38
|
-
function recover(...args) {
|
|
39
|
-
if (args.length === 2) return recoverImpl(args[0], args[1]);
|
|
40
|
-
const fn = args[0];
|
|
41
|
-
return (result) => recoverImpl(result, fn);
|
|
42
|
-
}
|
|
43
|
-
var tapImpl = (result, fn) => {
|
|
19
|
+
var map = dual(
|
|
20
|
+
2,
|
|
21
|
+
(result, fn) => isOk(result) ? ok(fn(result.value)) : err(result.error)
|
|
22
|
+
);
|
|
23
|
+
var mapErr = dual(
|
|
24
|
+
2,
|
|
25
|
+
(result, fn) => isErr(result) ? err(fn(result.error)) : ok(result.value)
|
|
26
|
+
);
|
|
27
|
+
var bimap = dual(
|
|
28
|
+
3,
|
|
29
|
+
(result, onOk, onErr) => isOk(result) ? ok(onOk(result.value)) : err(onErr(result.error))
|
|
30
|
+
);
|
|
31
|
+
var flatMap = dual(
|
|
32
|
+
2,
|
|
33
|
+
(result, fn) => isOk(result) ? fn(result.value) : err(result.error)
|
|
34
|
+
);
|
|
35
|
+
var recover = dual(
|
|
36
|
+
2,
|
|
37
|
+
(result, fn) => isErr(result) ? fn(result.error) : ok(result.value)
|
|
38
|
+
);
|
|
39
|
+
var tap = dual(2, (result, fn) => {
|
|
44
40
|
if (isOk(result)) fn(result.value);
|
|
45
41
|
return result;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
if (args.length === 2) return tapImpl(args[0], args[1]);
|
|
49
|
-
const fn = args[0];
|
|
50
|
-
return (result) => tapImpl(result, fn);
|
|
51
|
-
}
|
|
52
|
-
var tapErrImpl = (result, fn) => {
|
|
42
|
+
});
|
|
43
|
+
var tapErr = dual(2, (result, fn) => {
|
|
53
44
|
if (isErr(result)) fn(result.error);
|
|
54
45
|
return result;
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const [onOk, onErr] = args;
|
|
65
|
-
return (result) => matchImpl(result, onOk, onErr);
|
|
66
|
-
}
|
|
67
|
-
var matchResult = match;
|
|
68
|
-
var fold = (handlers) => (result) => matchImpl(result, handlers.ok, handlers.err);
|
|
69
|
-
var unwrapOr = (result, defaultValue) => isOk(result) ? result.value : defaultValue;
|
|
46
|
+
});
|
|
47
|
+
var match = dual(
|
|
48
|
+
3,
|
|
49
|
+
(result, onOk, onErr) => isOk(result) ? onOk(result.value) : onErr(result.error)
|
|
50
|
+
);
|
|
51
|
+
var unwrapOr = dual(
|
|
52
|
+
2,
|
|
53
|
+
(result, defaultValue) => isOk(result) ? result.value : defaultValue
|
|
54
|
+
);
|
|
70
55
|
function unwrapOk(result) {
|
|
71
56
|
if (isErr(result)) throw result.error;
|
|
72
57
|
return result.value;
|
|
73
58
|
}
|
|
59
|
+
var unwrap = unwrapOk;
|
|
74
60
|
function unwrapErr(result) {
|
|
75
61
|
if (isOk(result)) throw new TypeError("unwrapErr called on Ok");
|
|
76
62
|
return result.error;
|
|
@@ -84,18 +70,6 @@ function trySync(fn, onThrow) {
|
|
|
84
70
|
}
|
|
85
71
|
};
|
|
86
72
|
}
|
|
87
|
-
var combine = (results) => {
|
|
88
|
-
const values = [];
|
|
89
|
-
for (const result of results) {
|
|
90
|
-
if (isErr(result)) return err(result.error);
|
|
91
|
-
values.push(result.value);
|
|
92
|
-
}
|
|
93
|
-
return ok(values);
|
|
94
|
-
};
|
|
95
|
-
var combineTuple = (results) => (
|
|
96
|
-
// Runtime identical to combine; the cast restores per-index tuple types.
|
|
97
|
-
combine(results)
|
|
98
|
-
);
|
|
99
73
|
function pipe(value, ...fns) {
|
|
100
74
|
let acc = value;
|
|
101
75
|
for (const fn of fns) {
|
|
@@ -115,17 +89,6 @@ var UnexpectedError = class extends Error {
|
|
|
115
89
|
};
|
|
116
90
|
|
|
117
91
|
// src/async.ts
|
|
118
|
-
var liftPromiseResult = async (promise, onDefect) => {
|
|
119
|
-
try {
|
|
120
|
-
const result = await promise;
|
|
121
|
-
if (isErr(result)) {
|
|
122
|
-
return result;
|
|
123
|
-
}
|
|
124
|
-
return isResultLike(result.value) ? result.value : result;
|
|
125
|
-
} catch (error) {
|
|
126
|
-
return err(onDefect(error));
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
92
|
var sequenceSettled = (settled) => {
|
|
130
93
|
const values = [];
|
|
131
94
|
for (const result of settled) {
|
|
@@ -142,9 +105,32 @@ var ResultAsync = class _ResultAsync {
|
|
|
142
105
|
}
|
|
143
106
|
run;
|
|
144
107
|
promise = null;
|
|
108
|
+
/**
|
|
109
|
+
* Lifts an already-settled sync {@link Result} into a {@link ResultAsync}.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```ts
|
|
113
|
+
* const ra = ResultAsync.fromResult(ok(42)); // ResultAsync<number, never>
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
145
116
|
static fromResult(result) {
|
|
146
117
|
return new _ResultAsync(async () => result);
|
|
147
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Wraps a `PromiseLike<T>` that may reject. Rejections pass through `onReject`
|
|
121
|
+
* to become a typed `Err`; resolution becomes `Ok<T>`.
|
|
122
|
+
*
|
|
123
|
+
* @param promise - the promise to wrap
|
|
124
|
+
* @param onReject - maps a rejection reason to the `Err` channel
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```ts
|
|
128
|
+
* const body = ResultAsync.fromPromise(
|
|
129
|
+
* fetch(url).then((r) => r.text()),
|
|
130
|
+
* (e): NetError => ({ kind: "net", cause: String(e) }),
|
|
131
|
+
* );
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
148
134
|
static fromPromise(promise, onReject) {
|
|
149
135
|
return new _ResultAsync(async () => {
|
|
150
136
|
try {
|
|
@@ -154,12 +140,29 @@ var ResultAsync = class _ResultAsync {
|
|
|
154
140
|
}
|
|
155
141
|
});
|
|
156
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Wraps a `PromiseLike<T>` that is **guaranteed not to reject**, skipping the
|
|
145
|
+
* `onReject` mapper. Use only when rejection is provably impossible.
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```ts
|
|
149
|
+
* const now = ResultAsync.fromSafePromise(Promise.resolve(Date.now()));
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
157
152
|
static fromSafePromise(promise) {
|
|
158
153
|
return new _ResultAsync(async () => ok(await promise));
|
|
159
154
|
}
|
|
160
155
|
/**
|
|
161
|
-
* Defers work until {@link resolve}. Unlike {@link fromPromise},
|
|
162
|
-
* until the `ResultAsync` is resolved (e.g. by `combineTuple` /
|
|
156
|
+
* Defers work until {@link resolve}. Unlike {@link fromPromise}, the factory
|
|
157
|
+
* does not run until the `ResultAsync` is resolved (e.g. by `combineTuple` /
|
|
158
|
+
* `combineTupleParallel`). The factory runs at most once — `resolve` memoizes.
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* const insert = ResultAsync.defer(() => db.orders.insert(row));
|
|
163
|
+
* // nothing has run yet
|
|
164
|
+
* const r = await insert; // factory runs exactly once here
|
|
165
|
+
* ```
|
|
163
166
|
*/
|
|
164
167
|
static defer(fn) {
|
|
165
168
|
return new _ResultAsync(fn);
|
|
@@ -167,14 +170,49 @@ var ResultAsync = class _ResultAsync {
|
|
|
167
170
|
static ok(value) {
|
|
168
171
|
return new _ResultAsync(async () => ok(value));
|
|
169
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Lifts an error into an `Err` async result.
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```ts
|
|
178
|
+
* const r = ResultAsync.err({ kind: "not_found" as const });
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
170
181
|
static err(error) {
|
|
171
182
|
return new _ResultAsync(async () => err(error));
|
|
172
183
|
}
|
|
173
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* Lifts an existing `Promise<Result<T, E>>` (e.g. from interop code) into a
|
|
186
|
+
* {@link ResultAsync}. A thrown defect is routed through `onDefect`,
|
|
187
|
+
* defaulting to {@link UnexpectedError}. The `Ok` value passes through
|
|
188
|
+
* verbatim — even when it happens to be Result-shaped.
|
|
189
|
+
*
|
|
190
|
+
* @param promise - a promise that already yields a `Result`
|
|
191
|
+
* @param onDefect - maps an unexpected throw to the `Err` channel
|
|
192
|
+
* @see {@link fromAsync} from `@onrails/result`
|
|
193
|
+
*/
|
|
174
194
|
static fromResultPromise(promise, onDefect) {
|
|
175
195
|
const mapDefect = onDefect ?? ((error) => new UnexpectedError("Unexpected async defect", error));
|
|
176
|
-
return new _ResultAsync(() =>
|
|
196
|
+
return new _ResultAsync(async () => {
|
|
197
|
+
try {
|
|
198
|
+
return await promise;
|
|
199
|
+
} catch (error) {
|
|
200
|
+
return err(mapDefect(error));
|
|
201
|
+
}
|
|
202
|
+
});
|
|
177
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* Combines a homogeneous array of async results into one. Resolves
|
|
206
|
+
* **sequentially** in input order, short-circuiting on the first `Err`.
|
|
207
|
+
* For heterogeneous tuples that preserve per-index types, use
|
|
208
|
+
* {@link combineTuple}; for wall-clock overlap, {@link combineTupleParallel}.
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* ```ts
|
|
212
|
+
* const all = ResultAsync.combine([loadA(), loadB(), loadC()]);
|
|
213
|
+
* // ResultAsync<Item[], LoadError>
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
178
216
|
static combine(results) {
|
|
179
217
|
return new _ResultAsync(async () => {
|
|
180
218
|
const values = [];
|
|
@@ -188,6 +226,23 @@ var ResultAsync = class _ResultAsync {
|
|
|
188
226
|
return ok(values);
|
|
189
227
|
});
|
|
190
228
|
}
|
|
229
|
+
/**
|
|
230
|
+
* Heterogeneous async tuple combine — resolves branches **sequentially**
|
|
231
|
+
* (left-to-right), returning the first `Err` in input order. Preserves each
|
|
232
|
+
* branch's `Ok` type by position, so the result destructures type-safely.
|
|
233
|
+
* This is the canonical sequential async combine (replaces the former
|
|
234
|
+
* `sequenceTupleAsync`).
|
|
235
|
+
*
|
|
236
|
+
* @example
|
|
237
|
+
* ```ts
|
|
238
|
+
* const combined = ResultAsync.combineTuple([loadCfg(), loadCatalog()] as const);
|
|
239
|
+
* // ResultAsync<readonly [Cfg, Catalog], CfgError | CatalogError>
|
|
240
|
+
* const r = await combined;
|
|
241
|
+
* if (isOk(r)) {
|
|
242
|
+
* const [cfg, catalog] = r.value; // typed per position
|
|
243
|
+
* }
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
191
246
|
static combineTuple(results) {
|
|
192
247
|
return _ResultAsync.combine(
|
|
193
248
|
results
|
|
@@ -196,18 +251,55 @@ var ResultAsync = class _ResultAsync {
|
|
|
196
251
|
/**
|
|
197
252
|
* Like {@link combineTuple}, but starts every branch before awaiting (wall-clock
|
|
198
253
|
* parallel for independent IO). On failure, returns the first `Err` in input order.
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```ts
|
|
257
|
+
* // independent IO — overlap them
|
|
258
|
+
* const combined = ResultAsync.combineTupleParallel([
|
|
259
|
+
* loadProfile(id),
|
|
260
|
+
* loadMetrics(id),
|
|
261
|
+
* ] as const);
|
|
262
|
+
* ```
|
|
199
263
|
*/
|
|
200
264
|
static combineTupleParallel(results) {
|
|
201
265
|
return new _ResultAsync(
|
|
202
266
|
async () => sequenceSettled(await Promise.all(results.map((ra) => ra.resolve())))
|
|
203
267
|
);
|
|
204
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* Transforms the `Ok` value, passing `Err` through unchanged.
|
|
271
|
+
*
|
|
272
|
+
* @example
|
|
273
|
+
* ```ts
|
|
274
|
+
* ResultAsync.ok(2).map((n) => n * 3); // ResultAsync<number> → Ok 6
|
|
275
|
+
* ```
|
|
276
|
+
*/
|
|
205
277
|
map(fn) {
|
|
206
278
|
return new _ResultAsync(async () => map(await this.resolve(), fn));
|
|
207
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* Transforms the `Err` value, passing `Ok` through unchanged — useful for
|
|
282
|
+
* unifying heterogeneous failures into one app-level union.
|
|
283
|
+
*
|
|
284
|
+
* @example
|
|
285
|
+
* ```ts
|
|
286
|
+
* load(id).mapErr((e): AppError => ({ kind: "load", cause: e })); // ResultAsync<T, AppError>
|
|
287
|
+
* ```
|
|
288
|
+
*/
|
|
208
289
|
mapErr(fn) {
|
|
209
290
|
return new _ResultAsync(async () => mapErr(await this.resolve(), fn));
|
|
210
291
|
}
|
|
292
|
+
/**
|
|
293
|
+
* Canonical bind — chains a step that itself returns a `ResultAsync` or sync
|
|
294
|
+
* `Result`, short-circuiting on `Err`. Error types accumulate (`E | F`).
|
|
295
|
+
*
|
|
296
|
+
* @example
|
|
297
|
+
* ```ts
|
|
298
|
+
* authenticate(req)
|
|
299
|
+
* .flatMap((user) => loadProfile(user.id)) // ResultAsync
|
|
300
|
+
* .flatMap((p) => validate(p)); // sync Result also accepted
|
|
301
|
+
* ```
|
|
302
|
+
*/
|
|
211
303
|
flatMap(fn) {
|
|
212
304
|
return new _ResultAsync(async () => {
|
|
213
305
|
const first = await this.resolve();
|
|
@@ -215,25 +307,31 @@ var ResultAsync = class _ResultAsync {
|
|
|
215
307
|
return first;
|
|
216
308
|
}
|
|
217
309
|
const next = fn(first.value);
|
|
218
|
-
|
|
219
|
-
return next.resolve();
|
|
220
|
-
}
|
|
221
|
-
if (isResultLike(next)) {
|
|
222
|
-
return next;
|
|
223
|
-
}
|
|
224
|
-
return isCompatLike(next) ? next.inner : next;
|
|
310
|
+
return next instanceof _ResultAsync ? next.resolve() : next;
|
|
225
311
|
});
|
|
226
312
|
}
|
|
313
|
+
/**
|
|
314
|
+
* neverthrow-compat alias of {@link flatMap}. Kept as the documented compat
|
|
315
|
+
* tier; prefer {@link flatMap} in new code.
|
|
316
|
+
*
|
|
317
|
+
* @example
|
|
318
|
+
* ```ts
|
|
319
|
+
* authenticate(req).andThen((user) => loadProfile(user.id));
|
|
320
|
+
* ```
|
|
321
|
+
*/
|
|
227
322
|
andThen(fn) {
|
|
228
323
|
return this.flatMap(fn);
|
|
229
324
|
}
|
|
230
325
|
/**
|
|
231
|
-
*
|
|
232
|
-
*
|
|
326
|
+
* Error-channel bind — runs `fn` only on `Err`, swapping in a recovery
|
|
327
|
+
* `ResultAsync` or sync `Result`. `Ok` passes through. The mirror of
|
|
328
|
+
* {@link flatMap} on the error track.
|
|
329
|
+
*
|
|
330
|
+
* @example
|
|
331
|
+
* ```ts
|
|
332
|
+
* loadFromCache(id).recover(() => loadFromOrigin(id)); // ResultAsync<T, F>
|
|
333
|
+
* ```
|
|
233
334
|
*/
|
|
234
|
-
chain(fn) {
|
|
235
|
-
return this.flatMap(fn);
|
|
236
|
-
}
|
|
237
335
|
recover(fn) {
|
|
238
336
|
return new _ResultAsync(async () => {
|
|
239
337
|
const first = await this.resolve();
|
|
@@ -244,9 +342,27 @@ var ResultAsync = class _ResultAsync {
|
|
|
244
342
|
return next instanceof _ResultAsync ? next.resolve() : next;
|
|
245
343
|
});
|
|
246
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* neverthrow-compat alias of {@link recover}. Kept as the documented compat
|
|
347
|
+
* tier; prefer {@link recover} in new code.
|
|
348
|
+
*
|
|
349
|
+
* @example
|
|
350
|
+
* ```ts
|
|
351
|
+
* loadFromCache(id).orElse(() => loadFromOrigin(id));
|
|
352
|
+
* ```
|
|
353
|
+
*/
|
|
247
354
|
orElse(fn) {
|
|
248
355
|
return this.recover(fn);
|
|
249
356
|
}
|
|
357
|
+
/**
|
|
358
|
+
* Runs a side effect on the `Ok` value and passes the result through
|
|
359
|
+
* unchanged; a no-op on `Err`. Mirrors {@link tapErr}.
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* ```ts
|
|
363
|
+
* saveUser(u).tap((saved) => analytics.track("user_saved", saved.id));
|
|
364
|
+
* ```
|
|
365
|
+
*/
|
|
250
366
|
tap(fn) {
|
|
251
367
|
return new _ResultAsync(async () => {
|
|
252
368
|
const result = await this.resolve();
|
|
@@ -256,6 +372,15 @@ var ResultAsync = class _ResultAsync {
|
|
|
256
372
|
return result;
|
|
257
373
|
});
|
|
258
374
|
}
|
|
375
|
+
/**
|
|
376
|
+
* Runs a side effect on the `Err` value and passes the result through
|
|
377
|
+
* unchanged; a no-op on `Ok`. The error-track mirror of {@link tap}.
|
|
378
|
+
*
|
|
379
|
+
* @example
|
|
380
|
+
* ```ts
|
|
381
|
+
* saveUser(u).tapErr((e) => logger.warn("save failed", e));
|
|
382
|
+
* ```
|
|
383
|
+
*/
|
|
259
384
|
tapErr(fn) {
|
|
260
385
|
return new _ResultAsync(async () => {
|
|
261
386
|
const result = await this.resolve();
|
|
@@ -265,28 +390,49 @@ var ResultAsync = class _ResultAsync {
|
|
|
265
390
|
return result;
|
|
266
391
|
});
|
|
267
392
|
}
|
|
268
|
-
unwrapOr(defaultValue) {
|
|
269
|
-
return this.resolve().then((result) => isErr(result) ? defaultValue : result.value);
|
|
270
|
-
}
|
|
271
393
|
/**
|
|
272
|
-
*
|
|
273
|
-
*
|
|
394
|
+
* Settles to the `Ok` value, or `defaultValue` if `Err`. Terminal — returns a
|
|
395
|
+
* plain `Promise`, not a `ResultAsync`.
|
|
396
|
+
*
|
|
397
|
+
* @example
|
|
398
|
+
* ```ts
|
|
399
|
+
* const profile = await loadProfile(id).unwrapOr(guestProfile);
|
|
400
|
+
* ```
|
|
274
401
|
*/
|
|
275
|
-
|
|
276
|
-
return this.resolve().then((result) =>
|
|
402
|
+
unwrapOr(defaultValue) {
|
|
403
|
+
return this.resolve().then((result) => isErr(result) ? defaultValue : result.value);
|
|
277
404
|
}
|
|
278
405
|
/**
|
|
279
|
-
*
|
|
280
|
-
*
|
|
406
|
+
* Terminal collapse — folds both tracks into a single awaited value. Returns
|
|
407
|
+
* a plain `Promise`, settling the carrier exactly once.
|
|
408
|
+
*
|
|
409
|
+
* @param onOk - handles the `Ok` value
|
|
410
|
+
* @param onErr - handles the `Err` value
|
|
411
|
+
*
|
|
412
|
+
* @example
|
|
413
|
+
* ```ts
|
|
414
|
+
* const status = await save(row).match(
|
|
415
|
+
* () => 200,
|
|
416
|
+
* (e) => (e.kind === "conflict" ? 409 : 500),
|
|
417
|
+
* );
|
|
418
|
+
* ```
|
|
281
419
|
*/
|
|
282
|
-
isErr() {
|
|
283
|
-
return this.resolve().then((result) => isErr(result));
|
|
284
|
-
}
|
|
285
420
|
match(onOk, onErr) {
|
|
286
421
|
return this.resolve().then(
|
|
287
422
|
(result) => isErr(result) ? onErr(result.error) : onOk(result.value)
|
|
288
423
|
);
|
|
289
424
|
}
|
|
425
|
+
/**
|
|
426
|
+
* Settles the carrier to a bare sync {@link Result}, memoizing so the
|
|
427
|
+
* underlying factory runs at most once. Prefer `await ra` (the thenable) or
|
|
428
|
+
* {@link match} in app code; use `resolve` when you need the tagged union back.
|
|
429
|
+
*
|
|
430
|
+
* @example
|
|
431
|
+
* ```ts
|
|
432
|
+
* const r = await load(id).resolve(); // Result<Data, LoadError>
|
|
433
|
+
* if (isOk(r)) use(r.value);
|
|
434
|
+
* ```
|
|
435
|
+
*/
|
|
290
436
|
resolve() {
|
|
291
437
|
if (!this.promise) {
|
|
292
438
|
this.promise = this.run();
|
|
@@ -306,37 +452,51 @@ var ResultAsync = class _ResultAsync {
|
|
|
306
452
|
);
|
|
307
453
|
}
|
|
308
454
|
};
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
}
|
|
312
|
-
function isCompatLike(v) {
|
|
313
|
-
return typeof v === "object" && v !== null && "inner" in v && isResultLike(v.inner);
|
|
314
|
-
}
|
|
455
|
+
|
|
456
|
+
// src/async-lift.ts
|
|
315
457
|
var okAsync = ResultAsync.ok;
|
|
316
458
|
var errAsync = ResultAsync.err;
|
|
317
459
|
var fromPromise = ResultAsync.fromPromise;
|
|
318
460
|
var fromSafePromise = ResultAsync.fromSafePromise;
|
|
319
|
-
var
|
|
320
|
-
var
|
|
461
|
+
var fromResult = ResultAsync.fromResult;
|
|
462
|
+
var asyncAfter = dual(
|
|
463
|
+
2,
|
|
464
|
+
(result, fn) => fromResult(result).flatMap(fn)
|
|
465
|
+
);
|
|
466
|
+
var fromAsync = (fn, onDefect) => (...args) => ResultAsync.fromResultPromise(fn(...args), onDefect);
|
|
321
467
|
var toError = (error) => error instanceof Error ? error : new Error(String(error));
|
|
322
468
|
function tryAsync(promise, onReject) {
|
|
323
|
-
|
|
324
|
-
return ResultAsync.fromPromise(promise, onReject);
|
|
325
|
-
}
|
|
326
|
-
return ResultAsync.fromPromise(promise, toError);
|
|
469
|
+
return onReject ? ResultAsync.fromPromise(promise, onReject) : ResultAsync.fromPromise(promise, toError);
|
|
327
470
|
}
|
|
328
471
|
|
|
329
|
-
// src/
|
|
330
|
-
var
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
472
|
+
// src/collections.ts
|
|
473
|
+
var combine = (results) => {
|
|
474
|
+
const values = [];
|
|
475
|
+
for (const result of results) {
|
|
476
|
+
if (isErr(result)) return err(result.error);
|
|
477
|
+
values.push(result.value);
|
|
478
|
+
}
|
|
479
|
+
return ok(values);
|
|
480
|
+
};
|
|
481
|
+
var combineTuple = (results) => (
|
|
482
|
+
// Runtime identical to combine; the cast restores per-index tuple types.
|
|
483
|
+
combine(results)
|
|
484
|
+
);
|
|
485
|
+
function validateAll(results, combineErrors) {
|
|
486
|
+
const values = [];
|
|
487
|
+
const errors = [];
|
|
488
|
+
for (const result of results) {
|
|
489
|
+
if (isErr(result)) {
|
|
490
|
+
errors.push(result.error);
|
|
491
|
+
} else {
|
|
492
|
+
values.push(result.value);
|
|
493
|
+
}
|
|
335
494
|
}
|
|
336
|
-
|
|
495
|
+
if (errors.length === 0) return ok(values);
|
|
496
|
+
return err(combineErrors ? errors.reduce(combineErrors) : errors);
|
|
337
497
|
}
|
|
338
|
-
function
|
|
339
|
-
return (
|
|
498
|
+
function validateTuple(results, combineErrors) {
|
|
499
|
+
return combineErrors ? validateAll(results, combineErrors) : validateAll(results);
|
|
340
500
|
}
|
|
341
501
|
|
|
342
502
|
// src/pipe.ts
|
|
@@ -376,6 +536,6 @@ var tryGen = (fn) => {
|
|
|
376
536
|
}
|
|
377
537
|
};
|
|
378
538
|
|
|
379
|
-
export { $, ResultAsync, asyncAfter, bimap, combine, combineTuple, err, errAsync, flatMap, flow,
|
|
539
|
+
export { $, ResultAsync, UnexpectedError, asyncAfter, bimap, combine, combineTuple, err, errAsync, flatMap, flow, fromAsync, fromPromise, fromResult, fromSafePromise, isErr, isOk, map, mapErr, match, ok, okAsync, pipe, recover, tap, tapErr, tryAsync, tryGen, trySync, unwrap, unwrapErr, unwrapOk, unwrapOr, validateAll, validateTuple, yieldResult };
|
|
380
540
|
//# sourceMappingURL=index.js.map
|
|
381
541
|
//# sourceMappingURL=index.js.map
|