nalloc 0.0.1 → 0.0.3
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 +22 -7
- package/build/option.cjs.map +1 -1
- package/build/option.d.ts +22 -1
- package/build/option.js +17 -8
- package/build/option.js.map +1 -1
- package/build/result.cjs +137 -48
- package/build/result.cjs.map +1 -1
- package/build/result.d.ts +91 -50
- package/build/result.js +112 -35
- 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 +322 -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 +39 -9
- package/src/result.ts +236 -106
- 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,22 @@ 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,
|
|
49
|
+
fromPromise
|
|
48
50
|
} from '../result.js';
|
|
49
51
|
import { ok, err, isOk, isErr, optionOf as optOf, none } from '../types.js';
|
|
50
|
-
import { formatResult, inspectResult } from '../devtools.js';
|
|
51
52
|
|
|
52
53
|
describe('Result', () => {
|
|
53
54
|
describe('constructors', () => {
|
|
@@ -57,10 +58,9 @@ describe('Result', () => {
|
|
|
57
58
|
expect(result).toBe(42);
|
|
58
59
|
});
|
|
59
60
|
|
|
60
|
-
it('ok
|
|
61
|
+
it('ok throws when passed an Err', () => {
|
|
61
62
|
const error = err('test error');
|
|
62
|
-
expect(() => ok(error)).
|
|
63
|
-
expect(isErr(ok(error))).toBe(true);
|
|
63
|
+
expect(() => ok(error)).toThrow('ok() cannot wrap an Err value');
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
it('err creates Err result', () => {
|
|
@@ -81,17 +81,6 @@ describe('Result', () => {
|
|
|
81
81
|
expect((failure as any).error.message).toBe('failed');
|
|
82
82
|
});
|
|
83
83
|
|
|
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
84
|
});
|
|
96
85
|
|
|
97
86
|
describe('type guards', () => {
|
|
@@ -249,8 +238,11 @@ describe('Result', () => {
|
|
|
249
238
|
expect(unwrap(ok(42))).toBe(42);
|
|
250
239
|
});
|
|
251
240
|
|
|
252
|
-
it('throws for Err', () => {
|
|
253
|
-
|
|
241
|
+
it('throws error value for Err', () => {
|
|
242
|
+
const e = err('error');
|
|
243
|
+
let caught: unknown;
|
|
244
|
+
try { unwrap(e); } catch (x) { caught = x; }
|
|
245
|
+
expect(caught).toBe('error');
|
|
254
246
|
});
|
|
255
247
|
});
|
|
256
248
|
|
|
@@ -430,6 +422,12 @@ describe('Result', () => {
|
|
|
430
422
|
expect(isErr(result)).toBe(true);
|
|
431
423
|
expect((result as any).error).toBe('first');
|
|
432
424
|
});
|
|
425
|
+
|
|
426
|
+
it('returns second Err when first is Ok', () => {
|
|
427
|
+
const result = zip(ok(1), err('second'));
|
|
428
|
+
expect(isErr(result)).toBe(true);
|
|
429
|
+
expect((result as any).error).toBe('second');
|
|
430
|
+
});
|
|
433
431
|
});
|
|
434
432
|
|
|
435
433
|
describe('zipWith', () => {
|
|
@@ -443,6 +441,12 @@ describe('Result', () => {
|
|
|
443
441
|
expect(isErr(result)).toBe(true);
|
|
444
442
|
expect((result as any).error).toBe('fail');
|
|
445
443
|
});
|
|
444
|
+
|
|
445
|
+
it('returns Err from right when left is Ok', () => {
|
|
446
|
+
const result = zipWith(ok(2), err('right'), (a, b) => a + b);
|
|
447
|
+
expect(isErr(result)).toBe(true);
|
|
448
|
+
expect((result as any).error).toBe('right');
|
|
449
|
+
});
|
|
446
450
|
});
|
|
447
451
|
|
|
448
452
|
describe('flatten', () => {
|
|
@@ -594,70 +598,14 @@ describe('Result', () => {
|
|
|
594
598
|
expect((result as any).error).toBe('boom');
|
|
595
599
|
});
|
|
596
600
|
|
|
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
601
|
});
|
|
623
602
|
|
|
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);
|
|
603
|
+
describe('control helpers', () => {
|
|
604
|
+
it('unwrapOrReturn returns value for Ok', () => {
|
|
605
|
+
const value = unwrapOrReturn(ok(42), () => 'fallback');
|
|
606
|
+
expect(value).toBe(42);
|
|
641
607
|
});
|
|
642
608
|
|
|
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);
|
|
657
|
-
});
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
describe('control helpers', () => {
|
|
661
609
|
it('unwrapOrReturn returns fallback for Err', () => {
|
|
662
610
|
const value = unwrapOrReturn(err('oops'), () => 'fallback');
|
|
663
611
|
expect(value).toBe('fallback');
|
|
@@ -669,11 +617,19 @@ describe('Result', () => {
|
|
|
669
617
|
expect(result.id).toBe(1);
|
|
670
618
|
});
|
|
671
619
|
|
|
620
|
+
it('assertOk throws on Err with default message', () => {
|
|
621
|
+
expect(() => assertOk(err('fail'))).toThrow('Expected Ok result');
|
|
622
|
+
});
|
|
623
|
+
|
|
672
624
|
it('assertErr narrows failure', () => {
|
|
673
625
|
const result = err('oops');
|
|
674
626
|
assertErr(result);
|
|
675
627
|
expect(result.error).toBe('oops');
|
|
676
628
|
});
|
|
629
|
+
|
|
630
|
+
it('assertErr throws on Ok with default message', () => {
|
|
631
|
+
expect(() => assertErr(ok(42))).toThrow('Expected Err result');
|
|
632
|
+
});
|
|
677
633
|
});
|
|
678
634
|
|
|
679
635
|
describe('collections', () => {
|
|
@@ -705,17 +661,292 @@ describe('Result', () => {
|
|
|
705
661
|
expect(oks).toEqual([1, 2]);
|
|
706
662
|
expect(errs).toEqual(['a']);
|
|
707
663
|
});
|
|
664
|
+
|
|
665
|
+
it('partitionAsync handles rejected promises as errors', async () => {
|
|
666
|
+
const promises = [
|
|
667
|
+
Promise.resolve(ok(1)),
|
|
668
|
+
Promise.reject(new Error('rejected')),
|
|
669
|
+
Promise.resolve(ok(2)),
|
|
670
|
+
];
|
|
671
|
+
const [oks, errs] = await partitionAsync(promises);
|
|
672
|
+
expect(oks).toEqual([1, 2]);
|
|
673
|
+
expect(errs).toHaveLength(1);
|
|
674
|
+
expect((errs[0] as Error).message).toBe('rejected');
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
it('any returns first Ok at start', () => {
|
|
678
|
+
const outcome = any([ok(1), err('a'), err('b')]);
|
|
679
|
+
expect(isOk(outcome)).toBe(true);
|
|
680
|
+
expect(unwrap(outcome)).toBe(1);
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
it('any returns first Ok at end', () => {
|
|
684
|
+
const outcome = any([err('a'), err('b'), ok(3)]);
|
|
685
|
+
expect(isOk(outcome)).toBe(true);
|
|
686
|
+
expect(unwrap(outcome)).toBe(3);
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
it('any returns empty errors for empty array', () => {
|
|
690
|
+
const outcome = any([]);
|
|
691
|
+
expect(isErr(outcome)).toBe(true);
|
|
692
|
+
expect((outcome as any).error).toEqual([]);
|
|
693
|
+
});
|
|
708
694
|
});
|
|
709
695
|
|
|
710
|
-
describe('
|
|
711
|
-
it('
|
|
712
|
-
|
|
713
|
-
expect(
|
|
696
|
+
describe('tryCatchMaybePromise', () => {
|
|
697
|
+
it('returns Ok for sync success', () => {
|
|
698
|
+
const result = tryCatchMaybePromise(() => 42);
|
|
699
|
+
expect(isOk(result)).toBe(true);
|
|
700
|
+
expect(result).toBe(42);
|
|
714
701
|
});
|
|
715
702
|
|
|
716
|
-
it('
|
|
717
|
-
|
|
718
|
-
|
|
703
|
+
it('returns Err for sync throw', () => {
|
|
704
|
+
const result = tryCatchMaybePromise(() => {
|
|
705
|
+
throw new Error('sync error');
|
|
706
|
+
});
|
|
707
|
+
expect(isErr(result)).toBe(true);
|
|
708
|
+
expect((result as any).error.message).toBe('sync error');
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
it('returns Ok for async success', async () => {
|
|
712
|
+
const result = await tryCatchMaybePromise(() => Promise.resolve(42));
|
|
713
|
+
expect(isOk(result)).toBe(true);
|
|
714
|
+
expect(result).toBe(42);
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
it('returns Err for async rejection', async () => {
|
|
718
|
+
const result = await tryCatchMaybePromise(() => Promise.reject(new Error('async error')));
|
|
719
|
+
expect(isErr(result)).toBe(true);
|
|
720
|
+
expect((result as any).error.message).toBe('async error');
|
|
721
|
+
});
|
|
722
|
+
|
|
723
|
+
it('uses onError mapper for sync throw', () => {
|
|
724
|
+
const result = tryCatchMaybePromise(
|
|
725
|
+
() => { throw new Error('fail'); },
|
|
726
|
+
(e) => `mapped: ${(e as Error).message}`
|
|
727
|
+
);
|
|
728
|
+
expect(isErr(result)).toBe(true);
|
|
729
|
+
expect((result as any).error).toBe('mapped: fail');
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
it('uses onError mapper for async rejection', async () => {
|
|
733
|
+
const result = await tryCatchMaybePromise(
|
|
734
|
+
() => Promise.reject(new Error('fail')),
|
|
735
|
+
(e) => `mapped: ${(e as Error).message}`
|
|
736
|
+
);
|
|
737
|
+
expect(isErr(result)).toBe(true);
|
|
738
|
+
expect((result as any).error).toBe('mapped: fail');
|
|
719
739
|
});
|
|
720
740
|
});
|
|
741
|
+
|
|
742
|
+
describe('settleMaybePromise', () => {
|
|
743
|
+
it('returns sync array for all sync values', () => {
|
|
744
|
+
const result = settleMaybePromise([1, 2, 3]);
|
|
745
|
+
expect(result).not.toBeInstanceOf(Promise);
|
|
746
|
+
expect(result).toEqual([1, 2, 3]);
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
it('returns Promise for mixed sync/async values', async () => {
|
|
750
|
+
const result = settleMaybePromise([1, Promise.resolve(2), 3]);
|
|
751
|
+
expect(result).toBeInstanceOf(Promise);
|
|
752
|
+
expect(await result).toEqual([1, 2, 3]);
|
|
753
|
+
});
|
|
754
|
+
|
|
755
|
+
it('converts rejected promises to Err', async () => {
|
|
756
|
+
const result = await settleMaybePromise([1, Promise.reject('fail'), 3]);
|
|
757
|
+
expect(isOk(result[0])).toBe(true);
|
|
758
|
+
expect(isErr(result[1])).toBe(true);
|
|
759
|
+
expect((result[1] as any).error).toBe('fail');
|
|
760
|
+
expect(isOk(result[2])).toBe(true);
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
it('handles all async values', async () => {
|
|
764
|
+
const result = await settleMaybePromise([
|
|
765
|
+
Promise.resolve(1),
|
|
766
|
+
Promise.resolve(2)
|
|
767
|
+
]);
|
|
768
|
+
expect(result).toEqual([1, 2]);
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
it('returns empty array for empty input', () => {
|
|
772
|
+
const result = settleMaybePromise([]);
|
|
773
|
+
expect(result).not.toBeInstanceOf(Promise);
|
|
774
|
+
expect(result).toEqual([]);
|
|
775
|
+
});
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
describe('partitionMaybePromise', () => {
|
|
779
|
+
it('returns sync partition for all sync values', () => {
|
|
780
|
+
const result = partitionMaybePromise([ok(1), err('a'), ok(2)]);
|
|
781
|
+
expect(result).not.toBeInstanceOf(Promise);
|
|
782
|
+
expect(result).toEqual([[1, 2], ['a']]);
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
it('returns Promise for mixed sync/async values', async () => {
|
|
786
|
+
const result = partitionMaybePromise([ok(1), Promise.resolve(err('a')), ok(2)]);
|
|
787
|
+
expect(result).toBeInstanceOf(Promise);
|
|
788
|
+
expect(await result).toEqual([[1, 2], ['a']]);
|
|
789
|
+
});
|
|
790
|
+
|
|
791
|
+
it('handles all async values', async () => {
|
|
792
|
+
const result = await partitionMaybePromise([
|
|
793
|
+
Promise.resolve(ok(1)),
|
|
794
|
+
Promise.resolve(err('a'))
|
|
795
|
+
]);
|
|
796
|
+
expect(result).toEqual([[1], ['a']]);
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
it('handles rejected promises as errors', async () => {
|
|
800
|
+
const result = await partitionMaybePromise([
|
|
801
|
+
ok(1),
|
|
802
|
+
Promise.reject(new Error('rejected'))
|
|
803
|
+
]);
|
|
804
|
+
expect(result[0]).toEqual([1]);
|
|
805
|
+
expect(result[1]).toHaveLength(1);
|
|
806
|
+
expect((result[1][0] as Error).message).toBe('rejected');
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
it('returns empty arrays for empty input', () => {
|
|
810
|
+
const result = partitionMaybePromise([]);
|
|
811
|
+
expect(result).not.toBeInstanceOf(Promise);
|
|
812
|
+
expect(result).toEqual([[], []]);
|
|
813
|
+
});
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
describe('filterOk', () => {
|
|
817
|
+
it('extracts Ok values', () => {
|
|
818
|
+
expect(filterOk([ok(1), err('a'), ok(2)])).toEqual([1, 2]);
|
|
819
|
+
});
|
|
820
|
+
|
|
821
|
+
it('returns empty for all Err', () => {
|
|
822
|
+
expect(filterOk([err('a'), err('b')])).toEqual([]);
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
it('returns empty for empty input', () => {
|
|
826
|
+
expect(filterOk([])).toEqual([]);
|
|
827
|
+
});
|
|
828
|
+
});
|
|
829
|
+
|
|
830
|
+
describe('filterErr', () => {
|
|
831
|
+
it('extracts Err values', () => {
|
|
832
|
+
expect(filterErr([ok(1), err('a'), ok(2), err('b')])).toEqual(['a', 'b']);
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
it('returns empty for all Ok', () => {
|
|
836
|
+
expect(filterErr([ok(1), ok(2)])).toEqual([]);
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
it('returns empty for empty input', () => {
|
|
840
|
+
expect(filterErr([])).toEqual([]);
|
|
841
|
+
});
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
describe('safeTry', () => {
|
|
845
|
+
it('returns Ok for successful execution', () => {
|
|
846
|
+
const result = safeTry(() => {
|
|
847
|
+
const a = unwrap(ok(10));
|
|
848
|
+
const b = unwrap(ok(5));
|
|
849
|
+
return a + b;
|
|
850
|
+
});
|
|
851
|
+
expect(isOk(result)).toBe(true);
|
|
852
|
+
expect(result).toBe(15);
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
it('returns Err when unwrap throws', () => {
|
|
856
|
+
const e = err('failed');
|
|
857
|
+
const result = safeTry(() => {
|
|
858
|
+
unwrap(e);
|
|
859
|
+
return 42;
|
|
860
|
+
});
|
|
861
|
+
expect(isErr(result)).toBe(true);
|
|
862
|
+
expect((result as { error: string }).error).toBe('failed');
|
|
863
|
+
});
|
|
864
|
+
|
|
865
|
+
it('catches regular errors', () => {
|
|
866
|
+
const result = safeTry(() => {
|
|
867
|
+
throw new Error('boom');
|
|
868
|
+
});
|
|
869
|
+
expect(isErr(result)).toBe(true);
|
|
870
|
+
expect((result as { error: Error }).error.message).toBe('boom');
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
it('propagates Err through chain', () => {
|
|
874
|
+
const parse = (s: string) => s === 'bad' ? err('parse error') : ok(Number(s));
|
|
875
|
+
const result = safeTry(() => {
|
|
876
|
+
const a = unwrap(parse('10'));
|
|
877
|
+
const b = unwrap(parse('bad'));
|
|
878
|
+
const c = unwrap(parse('5'));
|
|
879
|
+
return a + b + c;
|
|
880
|
+
});
|
|
881
|
+
expect(isErr(result)).toBe(true);
|
|
882
|
+
expect((result as { error: string }).error).toBe('parse error');
|
|
883
|
+
});
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
describe('safeTryAsync', () => {
|
|
887
|
+
it('returns Ok for successful async execution', async () => {
|
|
888
|
+
const result = await safeTryAsync(async () => {
|
|
889
|
+
const a = unwrap(ok(10));
|
|
890
|
+
const b = unwrap(await Promise.resolve(ok(5)));
|
|
891
|
+
return a + b;
|
|
892
|
+
});
|
|
893
|
+
expect(isOk(result)).toBe(true);
|
|
894
|
+
expect(result).toBe(15);
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
it('returns Err when unwrap throws in async', async () => {
|
|
898
|
+
const e = err('async failed');
|
|
899
|
+
const result = await safeTryAsync(async () => {
|
|
900
|
+
unwrap(e);
|
|
901
|
+
return 42;
|
|
902
|
+
});
|
|
903
|
+
expect(isErr(result)).toBe(true);
|
|
904
|
+
expect((result as { error: string }).error).toBe('async failed');
|
|
905
|
+
});
|
|
906
|
+
|
|
907
|
+
it('catches rejected promises', async () => {
|
|
908
|
+
const result = await safeTryAsync(async () => {
|
|
909
|
+
await Promise.reject(new Error('rejected'));
|
|
910
|
+
return 42;
|
|
911
|
+
});
|
|
912
|
+
expect(isErr(result)).toBe(true);
|
|
913
|
+
expect((result as { error: Error }).error.message).toBe('rejected');
|
|
914
|
+
});
|
|
915
|
+
});
|
|
916
|
+
|
|
917
|
+
describe('fromPromise', () => {
|
|
918
|
+
it('returns Ok for resolved promise', async () => {
|
|
919
|
+
const result = await fromPromise(Promise.resolve(42));
|
|
920
|
+
expect(isOk(result)).toBe(true);
|
|
921
|
+
expect(result).toBe(42);
|
|
922
|
+
});
|
|
923
|
+
|
|
924
|
+
it('returns Err for rejected promise', async () => {
|
|
925
|
+
const result = await fromPromise(Promise.reject(new Error('boom')));
|
|
926
|
+
expect(isErr(result)).toBe(true);
|
|
927
|
+
expect((result as { error: Error }).error.message).toBe('boom');
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
it('uses onError mapper for rejected promise', async () => {
|
|
931
|
+
const result = await fromPromise(
|
|
932
|
+
Promise.reject(new Error('boom')),
|
|
933
|
+
(e) => `mapped: ${(e as Error).message}`,
|
|
934
|
+
);
|
|
935
|
+
expect(isErr(result)).toBe(true);
|
|
936
|
+
expect((result as { error: string }).error).toBe('mapped: boom');
|
|
937
|
+
});
|
|
938
|
+
|
|
939
|
+
it('handles Ok(null) as valid success', async () => {
|
|
940
|
+
const result = await fromPromise(Promise.resolve(null));
|
|
941
|
+
expect(isOk(result)).toBe(true);
|
|
942
|
+
expect(result).toBe(null);
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
it('returns Err with unknown type when no onError provided', async () => {
|
|
946
|
+
const result = await fromPromise(Promise.reject('string error'));
|
|
947
|
+
expect(isErr(result)).toBe(true);
|
|
948
|
+
expect((result as { error: string }).error).toBe('string error');
|
|
949
|
+
});
|
|
950
|
+
});
|
|
951
|
+
|
|
721
952
|
});
|
|
@@ -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';
|