nalloc 0.0.1 → 0.0.2
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 +124 -38
- package/build/index.cjs +12 -68
- package/build/index.cjs.map +1 -1
- package/build/index.d.ts +1 -4
- package/build/index.js +1 -3
- package/build/index.js.map +1 -1
- package/build/iter.cjs +105 -0
- package/build/iter.cjs.map +1 -0
- package/build/iter.d.ts +61 -0
- package/build/iter.js +78 -0
- package/build/iter.js.map +1 -0
- package/build/option.cjs +19 -5
- package/build/option.cjs.map +1 -1
- package/build/option.d.ts +22 -1
- package/build/option.js +14 -6
- package/build/option.js.map +1 -1
- package/build/result.cjs +125 -54
- package/build/result.cjs.map +1 -1
- package/build/result.d.ts +83 -53
- package/build/result.js +100 -38
- package/build/result.js.map +1 -1
- package/build/safe.cjs +34 -15
- package/build/safe.cjs.map +1 -1
- package/build/safe.d.ts +4 -27
- package/build/safe.js +3 -14
- package/build/safe.js.map +1 -1
- package/build/types.cjs +38 -7
- package/build/types.cjs.map +1 -1
- package/build/types.d.ts +26 -4
- package/build/types.js +23 -7
- package/build/types.js.map +1 -1
- package/build/unsafe.cjs +14 -61
- package/build/unsafe.cjs.map +1 -1
- package/build/unsafe.d.ts +2 -27
- package/build/unsafe.js +2 -9
- package/build/unsafe.js.map +1 -1
- package/package.json +13 -16
- package/src/__tests__/index.ts +42 -0
- package/src/__tests__/iter.ts +218 -0
- package/src/__tests__/option.ts +48 -19
- package/src/__tests__/result.ts +286 -91
- package/src/__tests__/result.types.ts +3 -22
- package/src/__tests__/safe.ts +9 -15
- package/src/__tests__/unsafe.ts +11 -12
- package/src/index.ts +1 -18
- package/src/iter.ts +129 -0
- package/src/option.ts +36 -7
- package/src/result.ts +216 -113
- package/src/safe.ts +5 -42
- package/src/types.ts +52 -14
- package/src/unsafe.ts +2 -47
- package/build/devtools.cjs +0 -79
- package/build/devtools.cjs.map +0 -1
- package/build/devtools.d.ts +0 -82
- package/build/devtools.js +0 -43
- package/build/devtools.js.map +0 -1
- package/build/testing.cjs +0 -111
- package/build/testing.cjs.map +0 -1
- package/build/testing.d.ts +0 -85
- package/build/testing.js +0 -81
- package/build/testing.js.map +0 -1
- package/src/__tests__/tooling.ts +0 -86
- package/src/devtools.ts +0 -97
- package/src/testing.ts +0 -159
package/src/__tests__/result.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
2
|
import {
|
|
3
3
|
of,
|
|
4
|
-
ofAsync,
|
|
5
4
|
isSomeErr,
|
|
6
5
|
map,
|
|
7
6
|
mapErr,
|
|
@@ -34,20 +33,21 @@ import {
|
|
|
34
33
|
isOkAnd,
|
|
35
34
|
isErrAnd,
|
|
36
35
|
tryCatch,
|
|
37
|
-
tryAsync,
|
|
38
|
-
fromPromise,
|
|
39
|
-
mapAsync,
|
|
40
|
-
andThenAsync,
|
|
41
|
-
matchAsync,
|
|
42
36
|
unwrapOrReturn,
|
|
43
37
|
assertOk,
|
|
44
38
|
assertErr,
|
|
45
39
|
all,
|
|
46
40
|
any,
|
|
47
|
-
partitionAsync
|
|
41
|
+
partitionAsync,
|
|
42
|
+
tryCatchMaybePromise,
|
|
43
|
+
settleMaybePromise,
|
|
44
|
+
partitionMaybePromise,
|
|
45
|
+
filterOk,
|
|
46
|
+
filterErr,
|
|
47
|
+
safeTry,
|
|
48
|
+
safeTryAsync
|
|
48
49
|
} from '../result.js';
|
|
49
50
|
import { ok, err, isOk, isErr, optionOf as optOf, none } from '../types.js';
|
|
50
|
-
import { formatResult, inspectResult } from '../devtools.js';
|
|
51
51
|
|
|
52
52
|
describe('Result', () => {
|
|
53
53
|
describe('constructors', () => {
|
|
@@ -57,10 +57,9 @@ describe('Result', () => {
|
|
|
57
57
|
expect(result).toBe(42);
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
-
it('ok
|
|
60
|
+
it('ok throws when passed an Err', () => {
|
|
61
61
|
const error = err('test error');
|
|
62
|
-
expect(() => ok(error)).
|
|
63
|
-
expect(isErr(ok(error))).toBe(true);
|
|
62
|
+
expect(() => ok(error)).toThrow('ok() cannot wrap an Err value');
|
|
64
63
|
});
|
|
65
64
|
|
|
66
65
|
it('err creates Err result', () => {
|
|
@@ -81,17 +80,6 @@ describe('Result', () => {
|
|
|
81
80
|
expect((failure as any).error.message).toBe('failed');
|
|
82
81
|
});
|
|
83
82
|
|
|
84
|
-
it('ofAsync catches async errors', async () => {
|
|
85
|
-
const success = await ofAsync(async () => 42);
|
|
86
|
-
expect(isOk(success)).toBe(true);
|
|
87
|
-
expect(unwrap(success)).toBe(42);
|
|
88
|
-
|
|
89
|
-
const failure = await ofAsync(async () => {
|
|
90
|
-
throw new Error('async failed');
|
|
91
|
-
});
|
|
92
|
-
expect(isErr(failure)).toBe(true);
|
|
93
|
-
expect((failure as any).error.message).toBe('async failed');
|
|
94
|
-
});
|
|
95
83
|
});
|
|
96
84
|
|
|
97
85
|
describe('type guards', () => {
|
|
@@ -249,8 +237,11 @@ describe('Result', () => {
|
|
|
249
237
|
expect(unwrap(ok(42))).toBe(42);
|
|
250
238
|
});
|
|
251
239
|
|
|
252
|
-
it('throws for Err', () => {
|
|
253
|
-
|
|
240
|
+
it('throws error value for Err', () => {
|
|
241
|
+
const e = err('error');
|
|
242
|
+
let caught: unknown;
|
|
243
|
+
try { unwrap(e); } catch (x) { caught = x; }
|
|
244
|
+
expect(caught).toBe('error');
|
|
254
245
|
});
|
|
255
246
|
});
|
|
256
247
|
|
|
@@ -430,6 +421,12 @@ describe('Result', () => {
|
|
|
430
421
|
expect(isErr(result)).toBe(true);
|
|
431
422
|
expect((result as any).error).toBe('first');
|
|
432
423
|
});
|
|
424
|
+
|
|
425
|
+
it('returns second Err when first is Ok', () => {
|
|
426
|
+
const result = zip(ok(1), err('second'));
|
|
427
|
+
expect(isErr(result)).toBe(true);
|
|
428
|
+
expect((result as any).error).toBe('second');
|
|
429
|
+
});
|
|
433
430
|
});
|
|
434
431
|
|
|
435
432
|
describe('zipWith', () => {
|
|
@@ -443,6 +440,12 @@ describe('Result', () => {
|
|
|
443
440
|
expect(isErr(result)).toBe(true);
|
|
444
441
|
expect((result as any).error).toBe('fail');
|
|
445
442
|
});
|
|
443
|
+
|
|
444
|
+
it('returns Err from right when left is Ok', () => {
|
|
445
|
+
const result = zipWith(ok(2), err('right'), (a, b) => a + b);
|
|
446
|
+
expect(isErr(result)).toBe(true);
|
|
447
|
+
expect((result as any).error).toBe('right');
|
|
448
|
+
});
|
|
446
449
|
});
|
|
447
450
|
|
|
448
451
|
describe('flatten', () => {
|
|
@@ -594,70 +597,14 @@ describe('Result', () => {
|
|
|
594
597
|
expect((result as any).error).toBe('boom');
|
|
595
598
|
});
|
|
596
599
|
|
|
597
|
-
it('tryAsync maps async rejection', async () => {
|
|
598
|
-
const result = await tryAsync<number, string>(
|
|
599
|
-
async () => {
|
|
600
|
-
throw new Error('boom');
|
|
601
|
-
},
|
|
602
|
-
error => (error as Error).message,
|
|
603
|
-
);
|
|
604
|
-
expect(isErr(result)).toBe(true);
|
|
605
|
-
expect((result as any).error).toBe('boom');
|
|
606
|
-
});
|
|
607
|
-
|
|
608
|
-
it('fromPromise resolves to Ok', async () => {
|
|
609
|
-
const result = await fromPromise(Promise.resolve(5));
|
|
610
|
-
expect(isOk(result)).toBe(true);
|
|
611
|
-
expect(unwrap(result)).toBe(5);
|
|
612
|
-
});
|
|
613
|
-
|
|
614
|
-
it('fromPromise maps rejection', async () => {
|
|
615
|
-
const result = await fromPromise(
|
|
616
|
-
Promise.reject(new Error('fail')),
|
|
617
|
-
error => (error as Error).message.toUpperCase(),
|
|
618
|
-
);
|
|
619
|
-
expect(isErr(result)).toBe(true);
|
|
620
|
-
expect((result as any).error).toBe('FAIL');
|
|
621
|
-
});
|
|
622
600
|
});
|
|
623
601
|
|
|
624
|
-
describe('
|
|
625
|
-
it('
|
|
626
|
-
const
|
|
627
|
-
expect(
|
|
628
|
-
expect(unwrap(result)).toBe(6);
|
|
629
|
-
});
|
|
630
|
-
|
|
631
|
-
it('mapAsync returns Err unchanged', async () => {
|
|
632
|
-
const result = await mapAsync(err('nope'), async (value: number) => value * 3);
|
|
633
|
-
expect(isErr(result)).toBe(true);
|
|
634
|
-
expect((result as any).error).toBe('nope');
|
|
635
|
-
});
|
|
636
|
-
|
|
637
|
-
it('andThenAsync chains Ok value', async () => {
|
|
638
|
-
const result = await andThenAsync(ok(2), async value => ok(value * 4));
|
|
639
|
-
expect(isOk(result)).toBe(true);
|
|
640
|
-
expect(unwrap(result)).toBe(8);
|
|
641
|
-
});
|
|
642
|
-
|
|
643
|
-
it('matchAsync selects branch', async () => {
|
|
644
|
-
const okValue = await matchAsync(
|
|
645
|
-
ok(5),
|
|
646
|
-
async value => value * 2,
|
|
647
|
-
async () => 0,
|
|
648
|
-
);
|
|
649
|
-
expect(okValue).toBe(10);
|
|
650
|
-
|
|
651
|
-
const errValue = await matchAsync(
|
|
652
|
-
err('fail'),
|
|
653
|
-
async () => 0,
|
|
654
|
-
async error => error.length,
|
|
655
|
-
);
|
|
656
|
-
expect(errValue).toBe(4);
|
|
602
|
+
describe('control helpers', () => {
|
|
603
|
+
it('unwrapOrReturn returns value for Ok', () => {
|
|
604
|
+
const value = unwrapOrReturn(ok(42), () => 'fallback');
|
|
605
|
+
expect(value).toBe(42);
|
|
657
606
|
});
|
|
658
|
-
});
|
|
659
607
|
|
|
660
|
-
describe('control helpers', () => {
|
|
661
608
|
it('unwrapOrReturn returns fallback for Err', () => {
|
|
662
609
|
const value = unwrapOrReturn(err('oops'), () => 'fallback');
|
|
663
610
|
expect(value).toBe('fallback');
|
|
@@ -669,11 +616,19 @@ describe('Result', () => {
|
|
|
669
616
|
expect(result.id).toBe(1);
|
|
670
617
|
});
|
|
671
618
|
|
|
619
|
+
it('assertOk throws on Err with default message', () => {
|
|
620
|
+
expect(() => assertOk(err('fail'))).toThrow('Expected Ok result');
|
|
621
|
+
});
|
|
622
|
+
|
|
672
623
|
it('assertErr narrows failure', () => {
|
|
673
624
|
const result = err('oops');
|
|
674
625
|
assertErr(result);
|
|
675
626
|
expect(result.error).toBe('oops');
|
|
676
627
|
});
|
|
628
|
+
|
|
629
|
+
it('assertErr throws on Ok with default message', () => {
|
|
630
|
+
expect(() => assertErr(ok(42))).toThrow('Expected Err result');
|
|
631
|
+
});
|
|
677
632
|
});
|
|
678
633
|
|
|
679
634
|
describe('collections', () => {
|
|
@@ -705,17 +660,257 @@ describe('Result', () => {
|
|
|
705
660
|
expect(oks).toEqual([1, 2]);
|
|
706
661
|
expect(errs).toEqual(['a']);
|
|
707
662
|
});
|
|
663
|
+
|
|
664
|
+
it('partitionAsync handles rejected promises as errors', async () => {
|
|
665
|
+
const promises = [
|
|
666
|
+
Promise.resolve(ok(1)),
|
|
667
|
+
Promise.reject(new Error('rejected')),
|
|
668
|
+
Promise.resolve(ok(2)),
|
|
669
|
+
];
|
|
670
|
+
const [oks, errs] = await partitionAsync(promises);
|
|
671
|
+
expect(oks).toEqual([1, 2]);
|
|
672
|
+
expect(errs).toHaveLength(1);
|
|
673
|
+
expect((errs[0] as Error).message).toBe('rejected');
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
it('any returns first Ok at start', () => {
|
|
677
|
+
const outcome = any([ok(1), err('a'), err('b')]);
|
|
678
|
+
expect(isOk(outcome)).toBe(true);
|
|
679
|
+
expect(unwrap(outcome)).toBe(1);
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
it('any returns first Ok at end', () => {
|
|
683
|
+
const outcome = any([err('a'), err('b'), ok(3)]);
|
|
684
|
+
expect(isOk(outcome)).toBe(true);
|
|
685
|
+
expect(unwrap(outcome)).toBe(3);
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
it('any returns empty errors for empty array', () => {
|
|
689
|
+
const outcome = any([]);
|
|
690
|
+
expect(isErr(outcome)).toBe(true);
|
|
691
|
+
expect((outcome as any).error).toEqual([]);
|
|
692
|
+
});
|
|
708
693
|
});
|
|
709
694
|
|
|
710
|
-
describe('
|
|
711
|
-
it('
|
|
712
|
-
|
|
713
|
-
expect(
|
|
695
|
+
describe('tryCatchMaybePromise', () => {
|
|
696
|
+
it('returns Ok for sync success', () => {
|
|
697
|
+
const result = tryCatchMaybePromise(() => 42);
|
|
698
|
+
expect(isOk(result)).toBe(true);
|
|
699
|
+
expect(result).toBe(42);
|
|
714
700
|
});
|
|
715
701
|
|
|
716
|
-
it('
|
|
717
|
-
|
|
718
|
-
|
|
702
|
+
it('returns Err for sync throw', () => {
|
|
703
|
+
const result = tryCatchMaybePromise(() => {
|
|
704
|
+
throw new Error('sync error');
|
|
705
|
+
});
|
|
706
|
+
expect(isErr(result)).toBe(true);
|
|
707
|
+
expect((result as any).error.message).toBe('sync error');
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
it('returns Ok for async success', async () => {
|
|
711
|
+
const result = await tryCatchMaybePromise(() => Promise.resolve(42));
|
|
712
|
+
expect(isOk(result)).toBe(true);
|
|
713
|
+
expect(result).toBe(42);
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
it('returns Err for async rejection', async () => {
|
|
717
|
+
const result = await tryCatchMaybePromise(() => Promise.reject(new Error('async error')));
|
|
718
|
+
expect(isErr(result)).toBe(true);
|
|
719
|
+
expect((result as any).error.message).toBe('async error');
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
it('uses onError mapper for sync throw', () => {
|
|
723
|
+
const result = tryCatchMaybePromise(
|
|
724
|
+
() => { throw new Error('fail'); },
|
|
725
|
+
(e) => `mapped: ${(e as Error).message}`
|
|
726
|
+
);
|
|
727
|
+
expect(isErr(result)).toBe(true);
|
|
728
|
+
expect((result as any).error).toBe('mapped: fail');
|
|
729
|
+
});
|
|
730
|
+
|
|
731
|
+
it('uses onError mapper for async rejection', async () => {
|
|
732
|
+
const result = await tryCatchMaybePromise(
|
|
733
|
+
() => Promise.reject(new Error('fail')),
|
|
734
|
+
(e) => `mapped: ${(e as Error).message}`
|
|
735
|
+
);
|
|
736
|
+
expect(isErr(result)).toBe(true);
|
|
737
|
+
expect((result as any).error).toBe('mapped: fail');
|
|
738
|
+
});
|
|
739
|
+
});
|
|
740
|
+
|
|
741
|
+
describe('settleMaybePromise', () => {
|
|
742
|
+
it('returns sync array for all sync values', () => {
|
|
743
|
+
const result = settleMaybePromise([1, 2, 3]);
|
|
744
|
+
expect(result).not.toBeInstanceOf(Promise);
|
|
745
|
+
expect(result).toEqual([1, 2, 3]);
|
|
746
|
+
});
|
|
747
|
+
|
|
748
|
+
it('returns Promise for mixed sync/async values', async () => {
|
|
749
|
+
const result = settleMaybePromise([1, Promise.resolve(2), 3]);
|
|
750
|
+
expect(result).toBeInstanceOf(Promise);
|
|
751
|
+
expect(await result).toEqual([1, 2, 3]);
|
|
752
|
+
});
|
|
753
|
+
|
|
754
|
+
it('converts rejected promises to Err', async () => {
|
|
755
|
+
const result = await settleMaybePromise([1, Promise.reject('fail'), 3]);
|
|
756
|
+
expect(isOk(result[0])).toBe(true);
|
|
757
|
+
expect(isErr(result[1])).toBe(true);
|
|
758
|
+
expect((result[1] as any).error).toBe('fail');
|
|
759
|
+
expect(isOk(result[2])).toBe(true);
|
|
760
|
+
});
|
|
761
|
+
|
|
762
|
+
it('handles all async values', async () => {
|
|
763
|
+
const result = await settleMaybePromise([
|
|
764
|
+
Promise.resolve(1),
|
|
765
|
+
Promise.resolve(2)
|
|
766
|
+
]);
|
|
767
|
+
expect(result).toEqual([1, 2]);
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
it('returns empty array for empty input', () => {
|
|
771
|
+
const result = settleMaybePromise([]);
|
|
772
|
+
expect(result).not.toBeInstanceOf(Promise);
|
|
773
|
+
expect(result).toEqual([]);
|
|
774
|
+
});
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
describe('partitionMaybePromise', () => {
|
|
778
|
+
it('returns sync partition for all sync values', () => {
|
|
779
|
+
const result = partitionMaybePromise([ok(1), err('a'), ok(2)]);
|
|
780
|
+
expect(result).not.toBeInstanceOf(Promise);
|
|
781
|
+
expect(result).toEqual([[1, 2], ['a']]);
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
it('returns Promise for mixed sync/async values', async () => {
|
|
785
|
+
const result = partitionMaybePromise([ok(1), Promise.resolve(err('a')), ok(2)]);
|
|
786
|
+
expect(result).toBeInstanceOf(Promise);
|
|
787
|
+
expect(await result).toEqual([[1, 2], ['a']]);
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
it('handles all async values', async () => {
|
|
791
|
+
const result = await partitionMaybePromise([
|
|
792
|
+
Promise.resolve(ok(1)),
|
|
793
|
+
Promise.resolve(err('a'))
|
|
794
|
+
]);
|
|
795
|
+
expect(result).toEqual([[1], ['a']]);
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
it('handles rejected promises as errors', async () => {
|
|
799
|
+
const result = await partitionMaybePromise([
|
|
800
|
+
ok(1),
|
|
801
|
+
Promise.reject(new Error('rejected'))
|
|
802
|
+
]);
|
|
803
|
+
expect(result[0]).toEqual([1]);
|
|
804
|
+
expect(result[1]).toHaveLength(1);
|
|
805
|
+
expect((result[1][0] as Error).message).toBe('rejected');
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
it('returns empty arrays for empty input', () => {
|
|
809
|
+
const result = partitionMaybePromise([]);
|
|
810
|
+
expect(result).not.toBeInstanceOf(Promise);
|
|
811
|
+
expect(result).toEqual([[], []]);
|
|
812
|
+
});
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
describe('filterOk', () => {
|
|
816
|
+
it('extracts Ok values', () => {
|
|
817
|
+
expect(filterOk([ok(1), err('a'), ok(2)])).toEqual([1, 2]);
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
it('returns empty for all Err', () => {
|
|
821
|
+
expect(filterOk([err('a'), err('b')])).toEqual([]);
|
|
822
|
+
});
|
|
823
|
+
|
|
824
|
+
it('returns empty for empty input', () => {
|
|
825
|
+
expect(filterOk([])).toEqual([]);
|
|
719
826
|
});
|
|
720
827
|
});
|
|
828
|
+
|
|
829
|
+
describe('filterErr', () => {
|
|
830
|
+
it('extracts Err values', () => {
|
|
831
|
+
expect(filterErr([ok(1), err('a'), ok(2), err('b')])).toEqual(['a', 'b']);
|
|
832
|
+
});
|
|
833
|
+
|
|
834
|
+
it('returns empty for all Ok', () => {
|
|
835
|
+
expect(filterErr([ok(1), ok(2)])).toEqual([]);
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
it('returns empty for empty input', () => {
|
|
839
|
+
expect(filterErr([])).toEqual([]);
|
|
840
|
+
});
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
describe('safeTry', () => {
|
|
844
|
+
it('returns Ok for successful execution', () => {
|
|
845
|
+
const result = safeTry(() => {
|
|
846
|
+
const a = unwrap(ok(10));
|
|
847
|
+
const b = unwrap(ok(5));
|
|
848
|
+
return a + b;
|
|
849
|
+
});
|
|
850
|
+
expect(isOk(result)).toBe(true);
|
|
851
|
+
expect(result).toBe(15);
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
it('returns Err when unwrap throws', () => {
|
|
855
|
+
const e = err('failed');
|
|
856
|
+
const result = safeTry(() => {
|
|
857
|
+
unwrap(e);
|
|
858
|
+
return 42;
|
|
859
|
+
});
|
|
860
|
+
expect(isErr(result)).toBe(true);
|
|
861
|
+
expect((result as { error: string }).error).toBe('failed');
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
it('catches regular errors', () => {
|
|
865
|
+
const result = safeTry(() => {
|
|
866
|
+
throw new Error('boom');
|
|
867
|
+
});
|
|
868
|
+
expect(isErr(result)).toBe(true);
|
|
869
|
+
expect((result as { error: Error }).error.message).toBe('boom');
|
|
870
|
+
});
|
|
871
|
+
|
|
872
|
+
it('propagates Err through chain', () => {
|
|
873
|
+
const parse = (s: string) => s === 'bad' ? err('parse error') : ok(Number(s));
|
|
874
|
+
const result = safeTry(() => {
|
|
875
|
+
const a = unwrap(parse('10'));
|
|
876
|
+
const b = unwrap(parse('bad'));
|
|
877
|
+
const c = unwrap(parse('5'));
|
|
878
|
+
return a + b + c;
|
|
879
|
+
});
|
|
880
|
+
expect(isErr(result)).toBe(true);
|
|
881
|
+
expect((result as { error: string }).error).toBe('parse error');
|
|
882
|
+
});
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
describe('safeTryAsync', () => {
|
|
886
|
+
it('returns Ok for successful async execution', async () => {
|
|
887
|
+
const result = await safeTryAsync(async () => {
|
|
888
|
+
const a = unwrap(ok(10));
|
|
889
|
+
const b = unwrap(await Promise.resolve(ok(5)));
|
|
890
|
+
return a + b;
|
|
891
|
+
});
|
|
892
|
+
expect(isOk(result)).toBe(true);
|
|
893
|
+
expect(result).toBe(15);
|
|
894
|
+
});
|
|
895
|
+
|
|
896
|
+
it('returns Err when unwrap throws in async', async () => {
|
|
897
|
+
const e = err('async failed');
|
|
898
|
+
const result = await safeTryAsync(async () => {
|
|
899
|
+
unwrap(e);
|
|
900
|
+
return 42;
|
|
901
|
+
});
|
|
902
|
+
expect(isErr(result)).toBe(true);
|
|
903
|
+
expect((result as { error: string }).error).toBe('async failed');
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
it('catches rejected promises', async () => {
|
|
907
|
+
const result = await safeTryAsync(async () => {
|
|
908
|
+
await Promise.reject(new Error('rejected'));
|
|
909
|
+
return 42;
|
|
910
|
+
});
|
|
911
|
+
expect(isErr(result)).toBe(true);
|
|
912
|
+
expect((result as { error: Error }).error.message).toBe('rejected');
|
|
913
|
+
});
|
|
914
|
+
});
|
|
915
|
+
|
|
721
916
|
});
|
|
@@ -24,10 +24,7 @@ import {
|
|
|
24
24
|
isOkAnd,
|
|
25
25
|
isErrAnd,
|
|
26
26
|
of,
|
|
27
|
-
ofAsync,
|
|
28
27
|
tryCatch,
|
|
29
|
-
tryAsync,
|
|
30
|
-
fromPromise,
|
|
31
28
|
unwrapOrReturn,
|
|
32
29
|
assertOk,
|
|
33
30
|
assertErr,
|
|
@@ -162,17 +159,11 @@ const tryResult = of(() => {
|
|
|
162
159
|
});
|
|
163
160
|
assert<Equals<typeof tryResult, Result<number, unknown>>>;
|
|
164
161
|
|
|
165
|
-
const tryResultTyped = of
|
|
162
|
+
const tryResultTyped = of(() => {
|
|
166
163
|
if (Math.random() > 0.5) throw new Error("oops");
|
|
167
164
|
return 42;
|
|
168
165
|
});
|
|
169
|
-
assert<Equals<typeof tryResultTyped, Result<number,
|
|
170
|
-
|
|
171
|
-
const asyncResult = ofAsync<number>(async () => {
|
|
172
|
-
return 42;
|
|
173
|
-
});
|
|
174
|
-
assert<Equals<typeof asyncResult, Promise<Result<number, unknown>>>>;
|
|
175
|
-
|
|
166
|
+
assert<Equals<typeof tryResultTyped, Result<number, unknown>>>;
|
|
176
167
|
|
|
177
168
|
|
|
178
169
|
const unwrapOrResult = unwrapOr(err<string>("error") as Result<number, string>, 42);
|
|
@@ -214,16 +205,6 @@ const tryCatchTyped = tryCatch<number, string>(() => {
|
|
|
214
205
|
}, error => (error as Error).message);
|
|
215
206
|
assert<Equals<typeof tryCatchTyped, Result<number, string>>>;
|
|
216
207
|
|
|
217
|
-
const tryAsyncResult = tryAsync(async () => 1);
|
|
218
|
-
assert<Equals<typeof tryAsyncResult, Promise<Result<number, unknown>>>>;
|
|
219
|
-
|
|
220
|
-
const tryAsyncTyped = tryAsync<number, string>(async () => {
|
|
221
|
-
throw new Error('boom');
|
|
222
|
-
}, error => (error as Error).message);
|
|
223
|
-
assert<Equals<typeof tryAsyncTyped, Promise<Result<number, string>>>>;
|
|
224
|
-
|
|
225
|
-
const fromPromiseResult = fromPromise(Promise.resolve(1));
|
|
226
|
-
assert<Equals<typeof fromPromiseResult, Promise<Result<number, unknown>>>>;
|
|
227
208
|
|
|
228
209
|
const unwrapFallback = unwrapOrReturn(ok(1) as Result<number, string>, () => 'fallback');
|
|
229
210
|
assert<Equals<typeof unwrapFallback, number | string>>;
|
|
@@ -237,7 +218,7 @@ assertErr(maybeErr);
|
|
|
237
218
|
const narrowedErr: Err<string> = maybeErr;
|
|
238
219
|
|
|
239
220
|
const allResult = all([ok(1), ok(2)]);
|
|
240
|
-
assert<Equals<typeof allResult, Result<number[], unknown>>>;
|
|
221
|
+
assert<Equals<typeof allResult, Result<readonly number[], unknown>>>;
|
|
241
222
|
|
|
242
223
|
const anyResult = any([err('a'), ok(3)]);
|
|
243
224
|
assert<Equals<typeof anyResult, Result<number, string[]>>>;
|
package/src/__tests__/safe.ts
CHANGED
|
@@ -1,24 +1,18 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import
|
|
3
|
-
import { err } from '../types.js';
|
|
2
|
+
import { ok, some, err, isSome } from '../types.js';
|
|
4
3
|
|
|
5
|
-
describe('
|
|
6
|
-
it('
|
|
7
|
-
expect(
|
|
8
|
-
expect('none' in safe).toBe(true);
|
|
9
|
-
expect(safe.ok).toBeDefined();
|
|
10
|
-
expect(safe.err).toBeDefined();
|
|
11
|
-
expect(safe.Option).toBeDefined();
|
|
12
|
-
expect(safe.Result).toBeDefined();
|
|
4
|
+
describe('safe constructors', () => {
|
|
5
|
+
it('some validates non-nullable input', () => {
|
|
6
|
+
expect(isSome(some(42))).toBe(true);
|
|
13
7
|
});
|
|
14
8
|
|
|
15
|
-
it('
|
|
16
|
-
expect(() =>
|
|
17
|
-
expect(() =>
|
|
9
|
+
it('some throws on null/undefined', () => {
|
|
10
|
+
expect(() => some(null as any)).toThrow('some() requires a non-nullable value');
|
|
11
|
+
expect(() => some(undefined as any)).toThrow('some() requires a non-nullable value');
|
|
18
12
|
});
|
|
19
13
|
|
|
20
|
-
it('
|
|
14
|
+
it('ok throws on Err', () => {
|
|
21
15
|
const error = err('test');
|
|
22
|
-
expect(() =>
|
|
16
|
+
expect(() => ok(error)).toThrow('ok() cannot wrap an Err value');
|
|
23
17
|
});
|
|
24
18
|
});
|
package/src/__tests__/unsafe.ts
CHANGED
|
@@ -1,24 +1,23 @@
|
|
|
1
|
-
import { describe,
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
2
|
import * as unsafe from '../unsafe.js';
|
|
3
|
-
import { err } from '../types.js';
|
|
3
|
+
import { err, isErr, isNone } from '../types.js';
|
|
4
4
|
|
|
5
|
-
describe('
|
|
6
|
-
it('exports
|
|
5
|
+
describe('unsafe module', () => {
|
|
6
|
+
it('exports constructors', () => {
|
|
7
7
|
expect(unsafe.some).toBeDefined();
|
|
8
|
-
expect('none' in unsafe).toBe(true);
|
|
9
8
|
expect(unsafe.ok).toBeDefined();
|
|
10
|
-
expect(unsafe.err).toBeDefined();
|
|
11
|
-
expect(unsafe.Option).toBeDefined();
|
|
12
|
-
expect(unsafe.Result).toBeDefined();
|
|
13
9
|
});
|
|
14
10
|
|
|
15
|
-
it('
|
|
11
|
+
it('some does not validate nullish values', () => {
|
|
16
12
|
expect(() => unsafe.some(null)).not.toThrow();
|
|
17
13
|
expect(() => unsafe.some(undefined)).not.toThrow();
|
|
14
|
+
expect(isNone(unsafe.some(null))).toBe(true);
|
|
15
|
+
expect(isNone(unsafe.some(undefined))).toBe(true);
|
|
18
16
|
});
|
|
19
17
|
|
|
20
|
-
it('
|
|
21
|
-
const
|
|
22
|
-
expect(() => unsafe.ok(
|
|
18
|
+
it('ok does not validate Err payloads', () => {
|
|
19
|
+
const payload = err('unsafe payload');
|
|
20
|
+
expect(() => unsafe.ok(payload)).not.toThrow();
|
|
21
|
+
expect(isErr(unsafe.ok(payload))).toBe(true);
|
|
23
22
|
});
|
|
24
23
|
});
|
package/src/index.ts
CHANGED
|
@@ -1,18 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
export type {
|
|
3
|
-
Some,
|
|
4
|
-
None,
|
|
5
|
-
Ok,
|
|
6
|
-
Err,
|
|
7
|
-
Option as OptionType,
|
|
8
|
-
OptionValue,
|
|
9
|
-
IsOption,
|
|
10
|
-
InferSome,
|
|
11
|
-
Result as ResultType,
|
|
12
|
-
ResultValue,
|
|
13
|
-
ResultErrorType,
|
|
14
|
-
IsResult,
|
|
15
|
-
InferErr,
|
|
16
|
-
} from './types.js';
|
|
17
|
-
export * as Option from './option.js';
|
|
18
|
-
export * as Result from './result.js';
|
|
1
|
+
export * from './safe.js';
|