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