unthrown 0.3.0 → 1.1.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/README.md +2 -2
- package/dist/index.cjs +211 -58
- package/dist/index.d.cts +168 -47
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +168 -47
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +207 -56
- package/dist/index.mjs.map +1 -1
- package/docs/index.md +197 -121
- package/package.json +3 -2
package/dist/index.mjs
CHANGED
|
@@ -32,7 +32,7 @@ var UnwrapError = class extends Error {
|
|
|
32
32
|
*/
|
|
33
33
|
var Res = class {
|
|
34
34
|
map(f) {
|
|
35
|
-
if (this.tag !== "Ok") return this;
|
|
35
|
+
if (this.tag !== "Ok") return passThrough(this);
|
|
36
36
|
try {
|
|
37
37
|
return okRes(f(this.value));
|
|
38
38
|
} catch (cause) {
|
|
@@ -40,7 +40,7 @@ var Res = class {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
flatMap(f) {
|
|
43
|
-
if (this.tag !== "Ok") return this;
|
|
43
|
+
if (this.tag !== "Ok") return passThrough(this);
|
|
44
44
|
try {
|
|
45
45
|
return f(this.value);
|
|
46
46
|
} catch (cause) {
|
|
@@ -60,17 +60,41 @@ var Res = class {
|
|
|
60
60
|
if (this.tag !== "Ok") return this;
|
|
61
61
|
try {
|
|
62
62
|
const r = f(this.value);
|
|
63
|
-
return r.tag === "Ok" ? this : r;
|
|
63
|
+
return r.tag === "Ok" ? this : passThrough(r);
|
|
64
|
+
} catch (cause) {
|
|
65
|
+
return defectRes(cause);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
bind(name, f) {
|
|
69
|
+
if (this.tag !== "Ok") return passThrough(this);
|
|
70
|
+
try {
|
|
71
|
+
const r = f(this.value);
|
|
72
|
+
if (r.tag !== "Ok") return passThrough(r);
|
|
73
|
+
return okRes({
|
|
74
|
+
...scopeOf(this.value),
|
|
75
|
+
[name]: r.value
|
|
76
|
+
});
|
|
77
|
+
} catch (cause) {
|
|
78
|
+
return defectRes(cause);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
let(name, f) {
|
|
82
|
+
if (this.tag !== "Ok") return passThrough(this);
|
|
83
|
+
try {
|
|
84
|
+
return okRes({
|
|
85
|
+
...scopeOf(this.value),
|
|
86
|
+
[name]: f(this.value)
|
|
87
|
+
});
|
|
64
88
|
} catch (cause) {
|
|
65
89
|
return defectRes(cause);
|
|
66
90
|
}
|
|
67
91
|
}
|
|
68
92
|
as(value) {
|
|
69
|
-
if (this.tag !== "Ok") return this;
|
|
93
|
+
if (this.tag !== "Ok") return passThrough(this);
|
|
70
94
|
return okRes(value);
|
|
71
95
|
}
|
|
72
96
|
mapErr(f) {
|
|
73
|
-
if (this.tag !== "Err") return this;
|
|
97
|
+
if (this.tag !== "Err") return passThrough(this);
|
|
74
98
|
try {
|
|
75
99
|
return errRes(f(this.error));
|
|
76
100
|
} catch (cause) {
|
|
@@ -78,7 +102,7 @@ var Res = class {
|
|
|
78
102
|
}
|
|
79
103
|
}
|
|
80
104
|
orElse(f) {
|
|
81
|
-
if (this.tag !== "Err") return this;
|
|
105
|
+
if (this.tag !== "Err") return passThrough(this);
|
|
82
106
|
try {
|
|
83
107
|
return f(this.error);
|
|
84
108
|
} catch (cause) {
|
|
@@ -86,7 +110,7 @@ var Res = class {
|
|
|
86
110
|
}
|
|
87
111
|
}
|
|
88
112
|
recover(f) {
|
|
89
|
-
if (this.tag !== "Err") return this;
|
|
113
|
+
if (this.tag !== "Err") return passThrough(this);
|
|
90
114
|
try {
|
|
91
115
|
return okRes(f(this.error));
|
|
92
116
|
} catch (cause) {
|
|
@@ -102,6 +126,15 @@ var Res = class {
|
|
|
102
126
|
return defectRes(cause);
|
|
103
127
|
}
|
|
104
128
|
}
|
|
129
|
+
flatTapErr(f) {
|
|
130
|
+
if (this.tag !== "Err") return this;
|
|
131
|
+
try {
|
|
132
|
+
const r = f(this.error);
|
|
133
|
+
return r.tag === "Ok" ? this : passThrough(r);
|
|
134
|
+
} catch (cause) {
|
|
135
|
+
return defectRes(cause);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
105
138
|
recoverDefect(f) {
|
|
106
139
|
if (this.tag !== "Defect") return this;
|
|
107
140
|
try {
|
|
@@ -207,6 +240,56 @@ function defectRes(cause) {
|
|
|
207
240
|
});
|
|
208
241
|
}
|
|
209
242
|
/**
|
|
243
|
+
* Type guard: is `x` a {@link Result} (any of `Ok` / `Err` / `Defect`)?
|
|
244
|
+
*
|
|
245
|
+
* @remarks
|
|
246
|
+
* Unlike {@link isOk} / {@link isErr} / {@link isDefect}, which narrow a value
|
|
247
|
+
* already known to be a `Result`, this narrows from `unknown` — useful at an
|
|
248
|
+
* untyped boundary. It checks the value carries the `Result` prototype, so a
|
|
249
|
+
* look-alike plain object (`{ tag: "Ok" }`) is **not** matched. An `AsyncResult`
|
|
250
|
+
* is not a `Result` and returns `false`.
|
|
251
|
+
*
|
|
252
|
+
* @returns `true` when `x` is a `Result` produced by this library.
|
|
253
|
+
*/
|
|
254
|
+
function isResult(x) {
|
|
255
|
+
return x instanceof Res;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Reuse a non-matching variant (an `Err` or `Defect`) as a differently-typed
|
|
259
|
+
* `Result`, with no runtime work. Sound because the passed-through variant
|
|
260
|
+
* carries no value of the changed success type, so retyping it is a no-op — only
|
|
261
|
+
* the phantom type parameter moves. This is the single sanctioned home for that
|
|
262
|
+
* assertion (the same one boxed applies inline at every pass-through); every
|
|
263
|
+
* combinator's short-circuit branch funnels through here instead of casting.
|
|
264
|
+
*
|
|
265
|
+
* @internal
|
|
266
|
+
*/
|
|
267
|
+
function passThrough(self) {
|
|
268
|
+
return self;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Validate that a `bind`/`let` scope is a real (non-null) object before merging a
|
|
272
|
+
* key into it.
|
|
273
|
+
*
|
|
274
|
+
* @remarks
|
|
275
|
+
* Do-notation accumulates an **object** scope: a chain starts at `Do()` (an
|
|
276
|
+
* empty object) and every `bind`/`let` returns an object, so in typed code the
|
|
277
|
+
* scope is always an object. The method lives on the general `Result` surface,
|
|
278
|
+
* though, so a primitive `Ok` (e.g. `Ok(5).bind(...)`, or a chain whose value was
|
|
279
|
+
* `map`-ped away from its scope) could reach it. Rather than let `{ ...5 }`
|
|
280
|
+
* silently collapse to `{}` and drop the prior scope, we throw here — the
|
|
281
|
+
* surrounding `try` turns it into a {@link Defect}, surfacing the misuse as the
|
|
282
|
+
* bug it is (a defect is a bug, not an absent value). A `this: object` constraint
|
|
283
|
+
* was rejected: TypeScript does not hard-enforce a constraint inferred solely
|
|
284
|
+
* from `this`, and it breaks `AsyncRes implements AsyncResult`.
|
|
285
|
+
*
|
|
286
|
+
* @internal
|
|
287
|
+
*/
|
|
288
|
+
function scopeOf(value) {
|
|
289
|
+
if (typeof value !== "object" || value === null) throw new TypeError("bind/let requires an object scope — start a do-chain with Do()");
|
|
290
|
+
return value;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
210
293
|
* The sole runtime implementation of {@link AsyncResult}: wraps a
|
|
211
294
|
* `Promise<Result>` constructed never to reject. Operates on the public `Result`
|
|
212
295
|
* union (via `tag`), never on `Res` internals. Never re-exported from `index.ts`.
|
|
@@ -223,7 +306,7 @@ var AsyncRes = class AsyncRes {
|
|
|
223
306
|
}
|
|
224
307
|
map(f) {
|
|
225
308
|
return new AsyncRes(this.promise.then((r) => {
|
|
226
|
-
if (r.tag !== "Ok") return r;
|
|
309
|
+
if (r.tag !== "Ok") return passThrough(r);
|
|
227
310
|
try {
|
|
228
311
|
return okRes(f(r.value));
|
|
229
312
|
} catch (cause) {
|
|
@@ -233,7 +316,7 @@ var AsyncRes = class AsyncRes {
|
|
|
233
316
|
}
|
|
234
317
|
flatMap(f) {
|
|
235
318
|
return new AsyncRes(this.promise.then(async (r) => {
|
|
236
|
-
if (r.tag !== "Ok") return r;
|
|
319
|
+
if (r.tag !== "Ok") return passThrough(r);
|
|
237
320
|
try {
|
|
238
321
|
return await f(r.value);
|
|
239
322
|
} catch (cause) {
|
|
@@ -254,21 +337,49 @@ var AsyncRes = class AsyncRes {
|
|
|
254
337
|
}
|
|
255
338
|
flatTap(f) {
|
|
256
339
|
return new AsyncRes(this.promise.then(async (r) => {
|
|
257
|
-
if (r.tag !== "Ok") return r;
|
|
340
|
+
if (r.tag !== "Ok") return passThrough(r);
|
|
341
|
+
try {
|
|
342
|
+
const inner = await f(r.value);
|
|
343
|
+
return inner.tag === "Ok" ? r : passThrough(inner);
|
|
344
|
+
} catch (cause) {
|
|
345
|
+
return defectRes(cause);
|
|
346
|
+
}
|
|
347
|
+
}));
|
|
348
|
+
}
|
|
349
|
+
bind(name, f) {
|
|
350
|
+
return new AsyncRes(this.promise.then(async (r) => {
|
|
351
|
+
if (r.tag !== "Ok") return passThrough(r);
|
|
258
352
|
try {
|
|
259
353
|
const inner = await f(r.value);
|
|
260
|
-
|
|
354
|
+
if (inner.tag !== "Ok") return passThrough(inner);
|
|
355
|
+
return okRes({
|
|
356
|
+
...scopeOf(r.value),
|
|
357
|
+
[name]: inner.value
|
|
358
|
+
});
|
|
359
|
+
} catch (cause) {
|
|
360
|
+
return defectRes(cause);
|
|
361
|
+
}
|
|
362
|
+
}));
|
|
363
|
+
}
|
|
364
|
+
let(name, f) {
|
|
365
|
+
return new AsyncRes(this.promise.then((r) => {
|
|
366
|
+
if (r.tag !== "Ok") return passThrough(r);
|
|
367
|
+
try {
|
|
368
|
+
return okRes({
|
|
369
|
+
...scopeOf(r.value),
|
|
370
|
+
[name]: f(r.value)
|
|
371
|
+
});
|
|
261
372
|
} catch (cause) {
|
|
262
373
|
return defectRes(cause);
|
|
263
374
|
}
|
|
264
375
|
}));
|
|
265
376
|
}
|
|
266
377
|
as(value) {
|
|
267
|
-
return new AsyncRes(this.promise.then((r) => r.tag === "Ok" ? okRes(value) : r));
|
|
378
|
+
return new AsyncRes(this.promise.then((r) => r.tag === "Ok" ? okRes(value) : passThrough(r)));
|
|
268
379
|
}
|
|
269
380
|
mapErr(f) {
|
|
270
381
|
return new AsyncRes(this.promise.then((r) => {
|
|
271
|
-
if (r.tag !== "Err") return r;
|
|
382
|
+
if (r.tag !== "Err") return passThrough(r);
|
|
272
383
|
try {
|
|
273
384
|
return errRes(f(r.error));
|
|
274
385
|
} catch (cause) {
|
|
@@ -278,7 +389,7 @@ var AsyncRes = class AsyncRes {
|
|
|
278
389
|
}
|
|
279
390
|
orElse(f) {
|
|
280
391
|
return new AsyncRes(this.promise.then(async (r) => {
|
|
281
|
-
if (r.tag !== "Err") return r;
|
|
392
|
+
if (r.tag !== "Err") return passThrough(r);
|
|
282
393
|
try {
|
|
283
394
|
return await f(r.error);
|
|
284
395
|
} catch (cause) {
|
|
@@ -288,7 +399,7 @@ var AsyncRes = class AsyncRes {
|
|
|
288
399
|
}
|
|
289
400
|
recover(f) {
|
|
290
401
|
return new AsyncRes(this.promise.then((r) => {
|
|
291
|
-
if (r.tag !== "Err") return r;
|
|
402
|
+
if (r.tag !== "Err") return passThrough(r);
|
|
292
403
|
try {
|
|
293
404
|
return okRes(f(r.error));
|
|
294
405
|
} catch (cause) {
|
|
@@ -307,6 +418,17 @@ var AsyncRes = class AsyncRes {
|
|
|
307
418
|
}
|
|
308
419
|
}));
|
|
309
420
|
}
|
|
421
|
+
flatTapErr(f) {
|
|
422
|
+
return new AsyncRes(this.promise.then(async (r) => {
|
|
423
|
+
if (r.tag !== "Err") return passThrough(r);
|
|
424
|
+
try {
|
|
425
|
+
const inner = await f(r.error);
|
|
426
|
+
return inner.tag === "Ok" ? passThrough(r) : passThrough(inner);
|
|
427
|
+
} catch (cause) {
|
|
428
|
+
return defectRes(cause);
|
|
429
|
+
}
|
|
430
|
+
}));
|
|
431
|
+
}
|
|
310
432
|
recoverDefect(f) {
|
|
311
433
|
return new AsyncRes(this.promise.then(async (r) => {
|
|
312
434
|
if (r.tag !== "Defect") return r;
|
|
@@ -364,11 +486,11 @@ var AsyncRes = class AsyncRes {
|
|
|
364
486
|
*
|
|
365
487
|
* @example
|
|
366
488
|
* ```ts
|
|
367
|
-
* import {
|
|
368
|
-
*
|
|
489
|
+
* import { Ok } from "unthrown";
|
|
490
|
+
* Ok(42).unwrap(); // 42
|
|
369
491
|
* ```
|
|
370
492
|
*/
|
|
371
|
-
function
|
|
493
|
+
function Ok(value) {
|
|
372
494
|
return okRes(value);
|
|
373
495
|
}
|
|
374
496
|
/**
|
|
@@ -379,11 +501,11 @@ function ok(value) {
|
|
|
379
501
|
*
|
|
380
502
|
* @example
|
|
381
503
|
* ```ts
|
|
382
|
-
* import {
|
|
383
|
-
*
|
|
504
|
+
* import { Err } from "unthrown";
|
|
505
|
+
* Err("not_found").unwrapErr(); // "not_found"
|
|
384
506
|
* ```
|
|
385
507
|
*/
|
|
386
|
-
function
|
|
508
|
+
function Err(error) {
|
|
387
509
|
return errRes(error);
|
|
388
510
|
}
|
|
389
511
|
/**
|
|
@@ -419,24 +541,24 @@ function isDefect(r) {
|
|
|
419
541
|
}
|
|
420
542
|
//#endregion
|
|
421
543
|
//#region src/defect.ts
|
|
422
|
-
const DEFECT = Symbol("unthrown/
|
|
544
|
+
const DEFECT = Symbol("unthrown/Defect");
|
|
423
545
|
/**
|
|
424
546
|
* Wrap a cause as a {@link Defect} — the value you return from a `qualify`
|
|
425
547
|
* function when a failure is **not** a modeled domain error.
|
|
426
548
|
*
|
|
427
549
|
* @param cause - the original thrown/rejected value.
|
|
428
|
-
* @returns an opaque
|
|
550
|
+
* @returns an opaque Defect marker carrying `cause`.
|
|
429
551
|
*
|
|
430
552
|
* @example
|
|
431
553
|
* ```ts
|
|
432
|
-
* import { fromPromise,
|
|
554
|
+
* import { fromPromise, Defect } from "unthrown";
|
|
433
555
|
*
|
|
434
556
|
* const user = fromPromise(fetchUser(id), (cause) =>
|
|
435
|
-
* cause instanceof NotFoundError ? cause :
|
|
557
|
+
* cause instanceof NotFoundError ? cause : Defect(cause),
|
|
436
558
|
* );
|
|
437
559
|
* ```
|
|
438
560
|
*/
|
|
439
|
-
function
|
|
561
|
+
function Defect(cause) {
|
|
440
562
|
return {
|
|
441
563
|
[DEFECT]: true,
|
|
442
564
|
cause
|
|
@@ -453,13 +575,40 @@ function isDefectMarker(x) {
|
|
|
453
575
|
return typeof x === "object" && x !== null && x[DEFECT] === true;
|
|
454
576
|
}
|
|
455
577
|
//#endregion
|
|
578
|
+
//#region src/do.ts
|
|
579
|
+
/**
|
|
580
|
+
* Start a do-notation chain with an empty object scope, grown step by step with
|
|
581
|
+
* `bind` (for `Result`-returning steps) and `let` (for pure values).
|
|
582
|
+
*
|
|
583
|
+
* @remarks
|
|
584
|
+
* Capitalised because `do` is a reserved word. Each step receives the scope
|
|
585
|
+
* accumulated so far; the error types union across `bind`s, and a throw in any
|
|
586
|
+
* step becomes a `Defect`. To go asynchronous, lift the chain with `toAsync()`
|
|
587
|
+
* (then a `bind` may return an `AsyncResult`).
|
|
588
|
+
*
|
|
589
|
+
* @example
|
|
590
|
+
* ```ts
|
|
591
|
+
* import { Do, Ok } from "unthrown";
|
|
592
|
+
*
|
|
593
|
+
* const result = Do()
|
|
594
|
+
* .bind("user", () => findUser(id)) // Result<User, NotFound>
|
|
595
|
+
* .bind("org", ({ user }) => findOrg(user.orgId)) // Result<Org, NotFound>
|
|
596
|
+
* .let("label", ({ user, org }) => `${user.name} @ ${org.name}`)
|
|
597
|
+
* .map(({ user, org, label }) => render(user, org, label));
|
|
598
|
+
* // Result<View, NotFound>
|
|
599
|
+
* ```
|
|
600
|
+
*/
|
|
601
|
+
function Do() {
|
|
602
|
+
return Ok({});
|
|
603
|
+
}
|
|
604
|
+
//#endregion
|
|
456
605
|
//#region src/interop.ts
|
|
457
606
|
/**
|
|
458
607
|
* Bridge a nullable value into a {@link Result}: absence becomes a **modeled**
|
|
459
608
|
* `Err`. The sanctioned alternative to an `Option` type.
|
|
460
609
|
*
|
|
461
610
|
* @remarks
|
|
462
|
-
* `null` and `undefined` map to `
|
|
611
|
+
* `null` and `undefined` map to `Err(onAbsent())`; any other value (including
|
|
463
612
|
* falsy ones like `0`, `""`, `false`) maps to `Ok`.
|
|
464
613
|
*
|
|
465
614
|
* @typeParam T - the (nullable) value type.
|
|
@@ -474,7 +623,7 @@ function isDefectMarker(x) {
|
|
|
474
623
|
* ```
|
|
475
624
|
*/
|
|
476
625
|
function fromNullable(value, onAbsent) {
|
|
477
|
-
return value === null || value === void 0 ?
|
|
626
|
+
return value === null || value === void 0 ? Err(onAbsent()) : Ok(value);
|
|
478
627
|
}
|
|
479
628
|
/**
|
|
480
629
|
* Wrap a throwing synchronous function so it returns a {@link Result} instead of
|
|
@@ -482,14 +631,14 @@ function fromNullable(value, onAbsent) {
|
|
|
482
631
|
*
|
|
483
632
|
* @remarks
|
|
484
633
|
* `qualify` **must** triage every thrown cause into a modeled error `E` or a
|
|
485
|
-
* {@link Defect} (via {@link
|
|
634
|
+
* {@link Defect} (via {@link Defect}) — there is no path that leaves `unknown`
|
|
486
635
|
* in `E`. A throw inside `qualify` itself is treated as a `Defect`.
|
|
487
636
|
*
|
|
488
637
|
* The modeled error type is `Exclude<R, Defect>` — the `Defect` arm of
|
|
489
638
|
* `qualify`'s return is **subtracted** from `E`, never inferred into it. So a
|
|
490
|
-
* `qualify` that returns *only* `
|
|
639
|
+
* `qualify` that returns *only* `Defect(cause)` yields `E = never` (a Defect is
|
|
491
640
|
* out-of-band and must not pollute the error channel); reach for
|
|
492
|
-
* {@link fromSafePromise} when every failure is a
|
|
641
|
+
* {@link fromSafePromise} when every failure is a Defect.
|
|
493
642
|
*
|
|
494
643
|
* @typeParam A - the wrapped function's argument tuple.
|
|
495
644
|
* @typeParam T - the wrapped function's return type.
|
|
@@ -501,8 +650,8 @@ function fromNullable(value, onAbsent) {
|
|
|
501
650
|
*
|
|
502
651
|
* @example
|
|
503
652
|
* ```ts
|
|
504
|
-
* import { fromThrowable,
|
|
505
|
-
* const parse = fromThrowable(JSON.parse, (cause) =>
|
|
653
|
+
* import { fromThrowable, Defect } from "unthrown";
|
|
654
|
+
* const parse = fromThrowable(JSON.parse, (cause) => Defect(cause));
|
|
506
655
|
* parse("{}").unwrap();
|
|
507
656
|
* ```
|
|
508
657
|
*/
|
|
@@ -510,7 +659,7 @@ function fromThrowable(fn, qualify) {
|
|
|
510
659
|
const triage = qualify;
|
|
511
660
|
return (...args) => {
|
|
512
661
|
try {
|
|
513
|
-
return
|
|
662
|
+
return Ok(fn(...args));
|
|
514
663
|
} catch (cause) {
|
|
515
664
|
return qualifyToResult(cause, triage);
|
|
516
665
|
}
|
|
@@ -528,8 +677,8 @@ function fromThrowable(fn, qualify) {
|
|
|
528
677
|
*
|
|
529
678
|
* The modeled error type is `Exclude<R, Defect>` — the `Defect` arm of
|
|
530
679
|
* `qualify`'s return is **subtracted** from `E`, never inferred into it. So a
|
|
531
|
-
* `qualify` that returns *only* `
|
|
532
|
-
* rejection is a
|
|
680
|
+
* `qualify` that returns *only* `Defect(cause)` yields `E = never`; when every
|
|
681
|
+
* rejection is a Defect, prefer {@link fromSafePromise}.
|
|
533
682
|
*
|
|
534
683
|
* @typeParam T - the resolved value type.
|
|
535
684
|
* @typeParam R - `qualify`'s return type; the modeled error `E` is
|
|
@@ -539,9 +688,9 @@ function fromThrowable(fn, qualify) {
|
|
|
539
688
|
*
|
|
540
689
|
* @example
|
|
541
690
|
* ```ts
|
|
542
|
-
* import { fromPromise,
|
|
691
|
+
* import { fromPromise, Defect } from "unthrown";
|
|
543
692
|
* const user = await fromPromise(fetchUser(id), (cause) =>
|
|
544
|
-
* cause instanceof NotFoundError ? ("not_found" as const) :
|
|
693
|
+
* cause instanceof NotFoundError ? ("not_found" as const) : Defect(cause),
|
|
545
694
|
* );
|
|
546
695
|
* ```
|
|
547
696
|
*/
|
|
@@ -585,7 +734,7 @@ function foldArray(results) {
|
|
|
585
734
|
for (const r of results) if (r.tag === "Defect") firstDefect ??= r;
|
|
586
735
|
else if (r.tag === "Err") firstErr ??= r;
|
|
587
736
|
else values.push(r.value);
|
|
588
|
-
return firstDefect ?? firstErr ??
|
|
737
|
+
return firstDefect ?? firstErr ?? Ok(values);
|
|
589
738
|
}
|
|
590
739
|
/**
|
|
591
740
|
* Fold a record of settled `Result`s with the same rules, else `Ok` of the
|
|
@@ -606,7 +755,7 @@ function foldRecord(results) {
|
|
|
606
755
|
writable: true,
|
|
607
756
|
configurable: true
|
|
608
757
|
});
|
|
609
|
-
return firstDefect ?? firstErr ??
|
|
758
|
+
return firstDefect ?? firstErr ?? Ok(values);
|
|
610
759
|
}
|
|
611
760
|
/**
|
|
612
761
|
* Collect a tuple/array of {@link Result}s into a single `Result` of all their
|
|
@@ -615,16 +764,16 @@ function foldRecord(results) {
|
|
|
615
764
|
* @remarks
|
|
616
765
|
* Short-circuits on the **first** `Err` (later entries are not inspected for
|
|
617
766
|
* their error); any `Defect` present **dominates**, winning even over an earlier
|
|
618
|
-
* `Err`. A **fixed tuple** keeps its positional types — `all([
|
|
767
|
+
* `Err`. A **fixed tuple** keeps its positional types — `all([Ok(1), Ok("a")])`
|
|
619
768
|
* is `Result<[number, string], …>` — while a **dynamic array** `Result<T, E>[]`
|
|
620
769
|
* collapses to `Result<T[], E>` with no cast. For a **record** keyed by name,
|
|
621
770
|
* use {@link allFromDict}.
|
|
622
771
|
*
|
|
623
772
|
* @example
|
|
624
773
|
* ```ts
|
|
625
|
-
* import { all,
|
|
626
|
-
* all([
|
|
627
|
-
* all([
|
|
774
|
+
* import { all, Ok } from "unthrown";
|
|
775
|
+
* all([Ok(1), Ok("a"), Ok(true)]).unwrap(); // [1, "a", true] (typed [number, string, boolean])
|
|
776
|
+
* all([Ok(1), Ok(2)] as Result<number, never>[]).unwrap(); // number[]
|
|
628
777
|
* ```
|
|
629
778
|
*/
|
|
630
779
|
function all(results) {
|
|
@@ -642,8 +791,8 @@ function all(results) {
|
|
|
642
791
|
*
|
|
643
792
|
* @example
|
|
644
793
|
* ```ts
|
|
645
|
-
* import { allFromDict,
|
|
646
|
-
* allFromDict({ id:
|
|
794
|
+
* import { allFromDict, Ok } from "unthrown";
|
|
795
|
+
* allFromDict({ id: Ok(1), name: Ok("ada") }).unwrap(); // { id: 1, name: "ada" }
|
|
647
796
|
* ```
|
|
648
797
|
*/
|
|
649
798
|
function allFromDict(results) {
|
|
@@ -696,29 +845,30 @@ function allFromDictAsync(results) {
|
|
|
696
845
|
//#region src/facade.ts
|
|
697
846
|
/**
|
|
698
847
|
* Companion object grouping the standalone entry points under a single,
|
|
699
|
-
* discoverable namespace: {@link Result.
|
|
700
|
-
* {@link Result.
|
|
848
|
+
* discoverable namespace: {@link Result.Ok}, {@link Result.Err},
|
|
849
|
+
* {@link Result.Defect}, {@link Result.fromNullable}, {@link Result.fromThrowable},
|
|
701
850
|
* {@link Result.fromPromise}, {@link Result.fromSafePromise}, {@link Result.all},
|
|
702
851
|
* {@link Result.allAsync}, {@link Result.allFromDict},
|
|
703
852
|
* {@link Result.allFromDictAsync}, {@link Result.isOk}, {@link Result.isErr},
|
|
704
|
-
* {@link Result.isDefect}.
|
|
853
|
+
* {@link Result.isDefect}, {@link Result.isResult}.
|
|
705
854
|
*
|
|
706
855
|
* @remarks
|
|
707
856
|
* Purely additive sugar — each member **is** the corresponding free function.
|
|
708
857
|
* The free functions remain the primary, tree-shakeable API; importing only
|
|
709
|
-
* `{
|
|
858
|
+
* `{ Ok }` never pulls this object in. The value `Result` and the type
|
|
710
859
|
* {@link Result} share one name (the companion-object pattern).
|
|
711
860
|
*
|
|
712
861
|
* @example
|
|
713
862
|
* ```ts
|
|
714
863
|
* import { Result } from "unthrown";
|
|
715
|
-
* Result.
|
|
864
|
+
* Result.Ok(1).flatMap((n) => Result.Ok(n + 1)).unwrap(); // 2
|
|
716
865
|
* ```
|
|
717
866
|
*/
|
|
718
867
|
const Result = {
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
868
|
+
Ok,
|
|
869
|
+
Err,
|
|
870
|
+
Defect,
|
|
871
|
+
Do,
|
|
722
872
|
fromNullable,
|
|
723
873
|
fromThrowable,
|
|
724
874
|
fromPromise,
|
|
@@ -729,7 +879,8 @@ const Result = {
|
|
|
729
879
|
allFromDictAsync,
|
|
730
880
|
isOk,
|
|
731
881
|
isErr,
|
|
732
|
-
isDefect
|
|
882
|
+
isDefect,
|
|
883
|
+
isResult
|
|
733
884
|
};
|
|
734
885
|
//#endregion
|
|
735
886
|
//#region src/tagged.ts
|
|
@@ -799,6 +950,6 @@ function matchTags(result, handlers) {
|
|
|
799
950
|
});
|
|
800
951
|
}
|
|
801
952
|
//#endregion
|
|
802
|
-
export { Result, TaggedError, UnwrapError, all, allAsync, allFromDict, allFromDictAsync,
|
|
953
|
+
export { Defect, Do, Err, Ok, Result, TaggedError, UnwrapError, all, allAsync, allFromDict, allFromDictAsync, fromNullable, fromPromise, fromSafePromise, fromThrowable, isDefect, isErr, isOk, isResult, matchTags };
|
|
803
954
|
|
|
804
955
|
//# sourceMappingURL=index.mjs.map
|