decoders 2.0.0-beta8 → 2.0.0-beta9

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.
Files changed (56) hide show
  1. package/CHANGELOG.md +11 -2
  2. package/README.md +234 -72
  3. package/_utils.js +1 -1
  4. package/_utils.js.flow +3 -3
  5. package/_utils.mjs +1 -1
  6. package/core/array.d.ts +3 -0
  7. package/core/array.js +12 -1
  8. package/core/array.js.flow +9 -2
  9. package/core/array.mjs +11 -2
  10. package/core/boolean.js +3 -3
  11. package/core/boolean.js.flow +2 -2
  12. package/core/boolean.mjs +2 -2
  13. package/core/composition.d.ts +5 -1
  14. package/core/composition.js +33 -5
  15. package/core/composition.js.flow +31 -5
  16. package/core/composition.mjs +31 -5
  17. package/core/date.js +3 -3
  18. package/core/date.js.flow +2 -2
  19. package/core/date.mjs +2 -2
  20. package/core/dispatch.d.ts +1 -1
  21. package/core/dispatch.js +8 -6
  22. package/core/dispatch.js.flow +9 -8
  23. package/core/dispatch.mjs +6 -5
  24. package/core/either.d.ts +63 -58
  25. package/core/either.js +30 -42
  26. package/core/either.js.flow +38 -84
  27. package/core/either.mjs +34 -34
  28. package/core/instanceOf.d.ts +1 -1
  29. package/core/json.js +3 -3
  30. package/core/json.js.flow +3 -3
  31. package/core/json.mjs +3 -3
  32. package/core/object.d.ts +5 -0
  33. package/core/object.js +59 -2
  34. package/core/object.js.flow +77 -21
  35. package/core/object.mjs +56 -3
  36. package/core/string.d.ts +3 -0
  37. package/core/string.js +15 -3
  38. package/core/string.js.flow +15 -2
  39. package/core/string.mjs +12 -3
  40. package/core/tuple.d.ts +28 -28
  41. package/core/tuple.js +28 -151
  42. package/core/tuple.js.flow +31 -191
  43. package/core/tuple.mjs +27 -141
  44. package/index.d.ts +17 -18
  45. package/index.js +33 -43
  46. package/index.js.flow +17 -18
  47. package/index.mjs +8 -9
  48. package/package.json +1 -1
  49. package/result.d.ts +2 -2
  50. package/result.js +9 -9
  51. package/result.js.flow +11 -11
  52. package/result.mjs +9 -9
  53. package/core/mapping.d.ts +0 -6
  54. package/core/mapping.js +0 -67
  55. package/core/mapping.js.flow +0 -62
  56. package/core/mapping.mjs +0 -58
package/CHANGELOG.md CHANGED
@@ -10,9 +10,12 @@ Potentially breaking changes:
10
10
  - Drop support for Flow versions below 0.142.0
11
11
  - Drop support for TypeScript versions below 4.1.0
12
12
  - Drop all package dependencies
13
+ - Renamed decoders:
14
+ - `map` → `transform` - see
15
+ [migration instructions](./MIGRATING-v2.md#map-is-now-transform)
16
+ - `dispatch` → `taggedUnion` - see
17
+ [migration instructions](./MIGRATING-v2.md#dispatch-is-now-taggedUnion)
13
18
  - Decoders that have changed:
14
- - `dispatch` has been renamed to `disjointUnion` - see
15
- [migration instructions](./MIGRATING-v2.md#dispatch-is-now-disjointUnion)
16
19
  - API of `guard` has changed (but only if you used its undocumented second argument
17
20
  😉) - see [migration instructions](./MIGRATING-v2.md#changes-to-the-guard-api)
18
21
  - API of `predicate` has changed - see
@@ -24,6 +27,12 @@ New features:
24
27
 
25
28
  - Include ES modules in published NPM builds (yay tree-shaking! 🍃)
26
29
  - Much smaller total bundle size
30
+ - New decoders:
31
+ - [`prep()`](https://nvie.com/decoders/api#prep)
32
+ - [`set()`](https://nvie.com/decoders/api#set)
33
+ - [`uuid`](https://nvie.com/decoders/api#uuid)
34
+ - [`uuidv1`](https://nvie.com/decoders/api#uuidv1)
35
+ - [`uuidv4`](https://nvie.com/decoders/api#uuidv4)
27
36
  - Better error messages for nested `either`s
28
37
  - Guard API now has a simpler way to specify formatters
29
38
 
package/README.md CHANGED
@@ -68,14 +68,92 @@ And then, you can use it to decode values:
68
68
  ... })
69
69
  ```
70
70
 
71
+ ## Understanding decoders and guards
72
+
73
+ At the heart, a decoder is a function that will take _any_ unsafe input, verify it, and
74
+ either return an "ok" or an annotated "err" result. It will never throw an error when
75
+ called.
76
+
77
+ A guard is a convenience wrapper which will use the decoder
78
+
71
79
  ## API
72
80
 
73
81
  The decoders package consists of a few building blocks:
74
82
 
83
+ - [Guards](#guards)
75
84
  - [Primitives](#primitives)
76
85
  - [Compositions](#compositions)
77
86
  - [Building custom decoders](#building-custom-decoders)
78
87
 
88
+ ### Guards
89
+
90
+ <a name="guard" href="#guard">#</a> <b>guard</b>(decoder: <i>Decoder&lt;T&gt;</i>,
91
+ formatter?: <i>Annotation => string</i>): <i>Guard&lt;T&gt;</i>
92
+ [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/_guard.js 'Source')
93
+
94
+ Turns any given `Decoder<T>` into a `Guard<T>`.
95
+
96
+ A guard works like a decoder, but will either:
97
+
98
+ - Return the decoded value (aka the happy path)
99
+ - Or throw an exception
100
+
101
+ So a Guard bypasses the intermediate "Result" type that decoders output. An "ok" result
102
+ will get returned, an "err" result will be formatted into an error message and thrown.
103
+
104
+ The typical usage is that you keep composing decoders until you have one decoder for your
105
+ entire input object, and then use a guard to wrap that outer decoder. Decoders can be
106
+ composed to build larger decoders. Guards cannot be composed.
107
+
108
+ #### Formatting error messsages
109
+
110
+ By default, `guard()` will use the `formatInline` error formatter. You can pass another
111
+ built-in formatter as the second argument, or provide your own. (This will require
112
+ understanding the internal `Annotation` datastructure that decoders uses for error
113
+ reporting.)
114
+
115
+ Built-in formatters are:
116
+
117
+ - `formatInline` (default) — will echo back the input object and inline error messages
118
+ smartly. Example:
119
+
120
+ ```typescript
121
+ import { array, guard, object, string } from 'decoders';
122
+ import { formatInline } from 'decoders/format';
123
+
124
+ const mydecoder = array(object({ name: string, age: number }));
125
+
126
+ const defaultGuard = guard(mydecoder, formatInline);
127
+ defaultGuard([{ name: 'Alice', age: '33' }]);
128
+ ```
129
+
130
+ Will throw the following error message:
131
+
132
+ ```text
133
+ Decoding error:
134
+ [
135
+ {
136
+ name: 'Alice',
137
+ age: '33',
138
+ ^^^^ Must be number
139
+ },
140
+ ]
141
+ ```
142
+
143
+ - `formatShort` — will report the _path_ into the object where the error happened.
144
+ Example:
145
+
146
+ ```typescript
147
+ import { formatShort } from 'decoders/format';
148
+ const customGuard = guard(mydecoder, formatShort);
149
+ ```
150
+
151
+ Will throw the following error message:
152
+
153
+ ```text
154
+ Decoding error: Value at keypath 0.age: Must be number
155
+ ```
156
+
79
157
  ### Primitives
80
158
 
81
159
  <a name="number" href="#number">#</a> <b>number</b>: <i>Decoder&lt;number&gt;</i>
@@ -319,6 +397,65 @@ const gitUrl: Decoder<URL> = predicate(
319
397
 
320
398
  ---
321
399
 
400
+ <a name="uuid" href="#uuid">#</a> <b>uuid</b>: <i>Decoder&lt;string&gt;</i>
401
+ [(source)](https://github.com/nvie/decoders/blob/main/src/core/string.js 'Source')
402
+
403
+ Accepts strings that are valid [UUIDs][wiki-uuid] (universally unique identifier).
404
+
405
+ <!-- prettier-ignore-start -->
406
+ ```javascript
407
+ const verify = guard(uuid);
408
+
409
+ // 👍
410
+ verify('123e4567-e89b-12d3-a456-426614174000') === '123e4567-e89b-12d3-a456-426614174000'
411
+ verify('123E4567-E89B-12D3-A456-426614174000') === '123E4567-E89B-12D3-A456-426614174000'
412
+
413
+ // 👎
414
+ verify('123E4567E89B12D3A456426614174000'); // throws
415
+ verify('abcdefgh-ijkl-mnop-qrst-uvwxyz012345'); // throws
416
+ ```
417
+ <!-- prettier-ignore-end -->
418
+
419
+ ---
420
+
421
+ <a name="uuidv1" href="#uuidv1">#</a> <b>uuidv1</b>: <i>Decoder&lt;string&gt;</i>
422
+ [(source)](https://github.com/nvie/decoders/blob/main/src/core/string.js 'Source')
423
+
424
+ Like `uuid`, but only accepts [UUIDv1s][wiki-uuidv1] strings.
425
+
426
+ <!-- prettier-ignore-start -->
427
+ ```javascript
428
+ const verify = guard(uuidv1);
429
+
430
+ // 👍
431
+ verify('123e4567-e89b-12d3-a456-426614174000') === '123e4567-e89b-42d3-a456-426614174000'
432
+
433
+ // 👎
434
+ verify('123e4567-e89b-42d3-a456-426614174000') // throws
435
+ ```
436
+ <!-- prettier-ignore-end -->
437
+
438
+ ---
439
+
440
+ <a name="uuidv4" href="#uuidv4">#</a> <b>uuidv4</b>: <i>Decoder&lt;string&gt;</i>
441
+ [(source)](https://github.com/nvie/decoders/blob/main/src/core/string.js 'Source')
442
+
443
+ Like `uuid`, but only accepts [UUIDv4s][wiki-uuidv4] strings.
444
+
445
+ <!-- prettier-ignore-start -->
446
+ ```javascript
447
+ const verify = guard(uuidv4);
448
+
449
+ // 👍
450
+ verify('123e4567-e89b-42d3-a456-426614174000') === '123e4567-e89b-42d3-a456-426614174000'
451
+
452
+ // 👎
453
+ verify('123e4567-e89b-12d3-a456-426614174000') // throws
454
+ ```
455
+ <!-- prettier-ignore-end -->
456
+
457
+ ---
458
+
322
459
  <a name="boolean" href="#boolean">#</a> <b>boolean</b>: <i>Decoder&lt;boolean&gt;</i>
323
460
  [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/boolean.js 'Source')
324
461
 
@@ -459,7 +596,7 @@ verify('hello world'); // throws
459
596
  ---
460
597
 
461
598
  <a name="undefined_" href="#undefined_">#</a> <b>undefined\_</b>:
462
- <i>Decoder&lt;void&gt;</i>
599
+ <i>Decoder&lt;undefined&gt;</i>
463
600
  [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/constants.js 'Source')
464
601
 
465
602
  Accepts only the literal `undefined` value.
@@ -567,15 +704,13 @@ verify({ a: 'foo', b: 'bar' }); // throws
567
704
 
568
705
  <a name="unknown" href="#unknown">#</a> <b>unknown</b>: <i>Decoder&lt;unknown&gt;</i>
569
706
  [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/constants.js 'Source')
570
- <a name="mixed" href="#mixed">#</a> <b>unknown</b>: <i>Decoder&lt;mixed&gt;</i>
707
+ <a name="mixed" href="#mixed">#</a> <b>mixed</b>: <i>Decoder&lt;mixed&gt;</i>
571
708
  [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/constants.js 'Source')<br />
572
709
 
573
710
  Accepts any value and returns it unchanged. Useful for situation in which you don't know
574
711
  or expect a specific type. Of course, the downside is that you won't know the type of the
575
712
  value statically and you'll have to further refine it yourself.
576
713
 
577
- (Unknown is called `mixed` in Flow.)
578
-
579
714
  ```javascript
580
715
  const verify = guard(mixed);
581
716
 
@@ -591,14 +726,14 @@ verify([1, 2]) === [1, 2];
591
726
 
592
727
  ### Compositions
593
728
 
594
- Composite decoders are "higher order" decoders that can build new decoders from existing
595
- decoders that can already decode a "subtype". Examples are: if you already have a decoder
596
- for a `Point` (= `Decoder<Point>`), then you can use `array()` to automatically build a
597
- decoder for arrays of points: `array(pointDecoder)`, which will be of type
598
- `Decoder<Array<Point>>`.
729
+ Composite decoders can build new decoders from existing decoders that can already decode a
730
+ "subtype". Examples are: if you already have a `string` decoder (of type
731
+ `Decoder<string>`), then you can use `array(string)` to automatically build a decoder for
732
+ arrays of strings, which will be of type `Decoder<Array<string>>`.
599
733
 
600
734
  <a name="optional" href="#optional">#</a>
601
- <b>optional</b><i>&lt;T&gt;</i>(<i>Decoder&lt;T&gt;</i>): <i>Decoder&lt;T | void&gt;</i>
735
+ <b>optional</b><i>&lt;T&gt;</i>(<i>Decoder&lt;T&gt;</i>): <i>Decoder&lt;T |
736
+ undefined&gt;</i>
602
737
  [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/optional.js 'Source')
603
738
 
604
739
  Accepts only the literal value `undefined`, or whatever the given decoder accepts.
@@ -752,38 +887,17 @@ verify('hi'); // throws
752
887
 
753
888
  ---
754
889
 
755
- <a name="tuple1" href="#tuple1">#</a>
756
- <b>tuple1</b><i>&lt;T1&gt;</i>(<i>Decoder&lt;T1&gt;</i>): <i>Decoder&lt;[T1]&gt;</i>
757
- [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/tuple.js 'Source')<br />
758
- <a name="tuple2" href="#tuple2">#</a> <b>tuple2</b><i>&lt;T1,
759
- T2&gt;</i>(<i>Decoder&lt;T1&gt;</i>, <i>Decoder&lt;T2&gt;</i>): <i>Decoder&lt;[T1,
760
- T2]&gt;</i>
761
- [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/tuple.js 'Source')<br />
762
- <a name="tuple3" href="#tuple3">#</a> <b>tuple3</b><i>&lt;T1, T2,
763
- T3&gt;</i>(<i>Decoder&lt;T1&gt;</i>, <i>Decoder&lt;T2&gt;</i>, <i>Decoder&lt;T3&gt;</i>):
764
- <i>Decoder&lt;[T1, T2, T3]&gt;</i>
765
- [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/tuple.js 'Source')<br />
766
- <a name="tuple4" href="#tuple4">#</a> <b>tuple4</b><i>&lt;T1, T2, T3,
767
- T4&gt;</i>(<i>Decoder&lt;T1&gt;</i>, <i>Decoder&lt;T2&gt;</i>, <i>Decoder&lt;T3&gt;</i>,
768
- <i>Decoder&lt;T4&gt;</i>): <i>Decoder&lt;[T1, T2, T3, T4]&gt;</i>
769
- [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/tuple.js 'Source')<br />
770
- <a name="tuple5" href="#tuple5">#</a> <b>tuple5</b><i>&lt;T1, T2, T3, T4,
771
- T5&gt;</i>(<i>Decoder&lt;T1&gt;</i>, <i>Decoder&lt;T2&gt;</i>, <i>Decoder&lt;T3&gt;</i>,
772
- <i>Decoder&lt;T3&gt;</i>, <i>Decoder&lt;T4&gt;</i>, <i>Decoder&lt;T5&gt;</i>):
773
- <i>Decoder&lt;[T1, T2, T3, T4, T5]&gt;</i>
774
- [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/tuple.js 'Source')<br />
775
- <a name="tuple6" href="#tuple6">#</a> <b>tuple6</b><i>&lt;T1, T2, T3, T4, T5,
776
- T6&gt;</i>(<i>Decoder&lt;T1&gt;</i>, <i>Decoder&lt;T2&gt;</i>, <i>Decoder&lt;T3&gt;</i>,
777
- <i>Decoder&lt;T4&gt;</i>, <i>Decoder&lt;T5&gt;</i>, <i>Decoder&lt;T6&gt;</i>):
778
- <i>Decoder&lt;[T1, T2, T3, T4, T5, T6]&gt;</i>
890
+ <a name="tuple" href="#tuple">#</a> <b>tuple</b><i>&lt;A, B, C,
891
+ ...&gt;</i>(<i>Decoder&lt;A&gt;</i>, <i>Decoder&lt;B&gt;</i>, <i>Decoder&lt;C&gt;</i>):
892
+ <i>Decoder&lt;[A, B, C, ...]&gt;</i>
779
893
  [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/tuple.js 'Source')
780
894
 
781
- Accepts a _n_-tuple (an array with exactly _n_ items) of values accepted by the _n_ given
895
+ Accepts a tuple (an array with exactly _n_ items) of values accepted by the _n_ given
782
896
  decoders.
783
897
 
784
898
  <!-- prettier-ignore-start -->
785
899
  ```javascript
786
- const verify = guard(tuple2(string, number));
900
+ const verify = guard(tuple(string, number));
787
901
 
788
902
  // 👍
789
903
  verify(['hello', 1.2]) === ['hello', 1.2];
@@ -797,6 +911,26 @@ verify(['a', 1, 'c']); // throws, too many items
797
911
 
798
912
  ---
799
913
 
914
+ <a name="set" href="#set">#</a> <b>set</b><i>&lt;T&gt;</i>(<i>Decoder&lt;T&gt;</i>):
915
+ <i>Decoder&lt;Set&lt;T&gt;&gt;</i>
916
+ [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/array.js 'Source')
917
+
918
+ Similar to [`array`](#array), but returns the result as an ES6 Set.
919
+
920
+ <!-- prettier-ignore-start -->
921
+ ```javascript
922
+ const verify = guard(set(string));
923
+
924
+ // 👍
925
+ verify(['hello', 'world']) // ≈ new Set(['hello', 'world']);
926
+
927
+ // 👎
928
+ verify(['hello', 1.2]); // throws
929
+ ```
930
+ <!-- prettier-ignore-end -->
931
+
932
+ ---
933
+
800
934
  <a name="object" href="#object">#</a> <b>object</b><i>&lt;O: { [field: string]:
801
935
  Decoder&lt;any&gt; }&gt;</i>(mapping: O): <i>Decoder&lt;{ ... }&gt;</i>
802
936
  [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/object.js 'Source')
@@ -913,7 +1047,7 @@ verify(null); // throws
913
1047
 
914
1048
  <a name="dict" href="#dict">#</a> <b>dict</b><i>&lt;T&gt;</i>(<i>Decoder&lt;T&gt;</i>):
915
1049
  <i>Decoder&lt;{ [string]: &lt;T&gt;}&gt;</i>
916
- [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/mapping.js 'Source')
1050
+ [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/object.js 'Source')
917
1051
 
918
1052
  Accepts objects where all values match the given decoder, and returns the result as a
919
1053
  `{ [string]: T }`.
@@ -943,7 +1077,7 @@ verify({
943
1077
  <a name="mapping" href="#mapping">#</a>
944
1078
  <b>mapping</b><i>&lt;T&gt;</i>(<i>Decoder&lt;T&gt;</i>): <i>Decoder&lt;Map&lt;string,
945
1079
  T&gt;&gt;</i>
946
- [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/mapping.js 'Source')
1080
+ [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/object.js 'Source')
947
1081
 
948
1082
  Like `dict()`, but returns the result as a `Map<string, T>` instead.
949
1083
 
@@ -1042,22 +1176,13 @@ verify(null); // throws
1042
1176
 
1043
1177
  ---
1044
1178
 
1045
- <a name="either" href="#either">#</a> <b>either</b><i>&lt;T1,
1046
- T2&gt;</i>(<i>Decoder&lt;T1&gt;</i>, <i>Decoder&lt;T2&gt;</i>): <i>Decoder&lt;T1 |
1047
- T2&gt;</i><br />
1048
- [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/either.js 'Source')<br />
1049
- <a name="either2" href="#either2">#</a> <b>either2</b><i>&lt;T1,
1050
- T2&gt;</i>(<i>Decoder&lt;T1&gt;</i>, <i>Decoder&lt;T2&gt;</i>): <i>Decoder&lt;T1 |
1051
- T2&gt;</i><br />
1052
- [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/either.js 'Source')<br />
1053
- <a name="either3" href="#either3">#</a> <b>either3</b><i>&lt;T1, T2,
1054
- T3&gt;</i>(<i>Decoder&lt;T1&gt;</i>, <i>Decoder&lt;T2&gt;</i>, <i>Decoder&lt;T3&gt;</i>):
1055
- <i>Decoder&lt;T1 | T2 | T3&gt;</i>
1179
+ <a name="either" href="#either">#</a> <b>either</b><i>&lt;A, B, C,
1180
+ ...&gt;</i>(<i>Decoder&lt;A&gt;</i>, <i>Decoder&lt;B&gt;</i>, <i>Decoder&lt;C&gt;</i>,
1181
+ ...): <i>Decoder&lt;A | B | C | ...&gt;</i><br />
1056
1182
  [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/either.js 'Source')<br />
1057
- ...
1058
1183
 
1059
- Accepts any value that is accepted by any of the given decoders. Eithers exist for arities
1060
- up until 9 (`either`, `either3`, `either4`, ..., `either9`).
1184
+ Accepts any value that is accepted by any of the given decoders. The decoders are
1185
+ attempted on the input in given order. The first one that accepts the input "wins".
1061
1186
 
1062
1187
  <!-- prettier-ignore-start -->
1063
1188
  ```javascript
@@ -1072,17 +1197,21 @@ verify(false); // throws
1072
1197
  ```
1073
1198
  <!-- prettier-ignore-end -->
1074
1199
 
1200
+ **NOTE to Flow users:** In Flow, there is a max of 16 arguments with this construct. (This
1201
+ is no problem in TypeScript.) If you hit the 16 argument limit, you can work around that
1202
+ by stacking, e.g. do `either(<15 arguments here>, either(...))`.
1203
+
1075
1204
  ---
1076
1205
 
1077
- <a name="disjointUnion" href="#disjointUnion">#</a> <b>disjointUnion</b><i>&lt;O: {
1078
- [field: string]: (Decoder&lt;T&gt; | Decoder&lt;V&gt; | ...) }&gt;</i>(field: string,
1079
- mapping: O): <i>Decoder&lt;T | V | ...&gt;</i>
1206
+ <a name="taggedUnion" href="#taggedUnion">#</a> <b>taggedUnion</b><i>&lt;O: { [field:
1207
+ string]: (Decoder&lt;T&gt; | Decoder&lt;V&gt; | ...) }&gt;</i>(field: string, mapping: O):
1208
+ <i>Decoder&lt;T | V | ...&gt;</i>
1080
1209
  [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/dispatch.js 'Source')
1081
1210
 
1082
1211
  **NOTE:** In decoders@1.x, this was called `dispatch()`.
1083
1212
 
1084
- Like the `either` family, but only for building unions of object types with a common field
1085
- (like a `type` field) that lets you distinguish members.
1213
+ Like `either`, but only for building unions of object types with a common field (like a
1214
+ `type` field) that lets you distinguish members.
1086
1215
 
1087
1216
  The following two decoders are effectively equivalent:
1088
1217
 
@@ -1092,14 +1221,14 @@ type Circle = { __type: 'circle', cx: number, cy: number, r: number };
1092
1221
  // ^^^^^^
1093
1222
  // Field that defines which decoder to pick
1094
1223
  // vvvvvv
1095
- const shape1: Decoder<Rect | Circle> = disjointUnion('__type', { rect, circle });
1224
+ const shape1: Decoder<Rect | Circle> = taggedUnion('__type', { rect, circle });
1096
1225
  const shape2: Decoder<Rect | Circle> = either(rect, circle);
1097
1226
  ```
1098
1227
 
1099
- But using `disjointUnion()` will typically be more runtime-efficient than using
1100
- `either()`. The reason is that `disjointUnion()` will first do minimal work to "look
1101
- ahead" into the `type` field here, and based on that value, pick which decoder to invoke.
1102
- Error messages will then also be tailored to the specific decoder.
1228
+ But using `taggedUnion()` will typically be more runtime-efficient than using `either()`.
1229
+ The reason is that `taggedUnion()` will first do minimal work to "look ahead" into the
1230
+ `type` field here, and based on that value, pick which decoder to invoke. Error messages
1231
+ will then also be tailored to the specific decoder.
1103
1232
 
1104
1233
  The `either()` version will instead try each decoder in turn until it finds one that
1105
1234
  matches. If none of the alternatives match, it needs to report all errors, which is
@@ -1167,8 +1296,9 @@ verify(3); // throws
1167
1296
 
1168
1297
  ---
1169
1298
 
1170
- <a name="map" href="#map">#</a> <b>map</b><i>&lt;T, V&gt;</i>(<i>Decoder&lt;T&gt;</i>,
1171
- <i>&lt;T&gt;</i> =&gt; <i>&lt;V&gt;</i>): <i>Decoder&lt;V&gt;</i>
1299
+ <a name="transform" href="#transform">#</a> <b>transform</b><i>&lt;T,
1300
+ V&gt;</i>(<i>Decoder&lt;T&gt;</i>, <i>&lt;T&gt;</i> =&gt; <i>&lt;V&gt;</i>):
1301
+ <i>Decoder&lt;V&gt;</i>
1172
1302
  [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/utils.js 'Source')<br />
1173
1303
 
1174
1304
  Accepts any value the given decoder accepts, and on success, will call the mapper value
@@ -1177,7 +1307,7 @@ fail using the error message as the failure reason.
1177
1307
 
1178
1308
  <!-- prettier-ignore-start -->
1179
1309
  ```javascript
1180
- const upper = map(string, (s) => s.toUpperCase());
1310
+ const upper = transform(string, (s) => s.toUpperCase());
1181
1311
  const verify = guard(upper);
1182
1312
 
1183
1313
  // 👍
@@ -1207,7 +1337,7 @@ recommended to rely on this decoder directly for normal usage.
1207
1337
  <a name="predicate" href="#predicate">#</a>
1208
1338
  <b>predicate</b><i>&lt;T&gt;</i>(<i>Decoder&lt;T&gt;</i>, <i>&lt;T&gt; => boolean</i>,
1209
1339
  string): <i>Decoder&lt;T&gt;</i>
1210
- [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/predicate.js 'Source')<br />
1340
+ [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/composition.js 'Source')<br />
1211
1341
 
1212
1342
  Accepts values that are accepted by the decoder _and_ also pass the predicate function.
1213
1343
 
@@ -1215,7 +1345,7 @@ Accepts values that are accepted by the decoder _and_ also pass the predicate fu
1215
1345
  ```typescript
1216
1346
  const odd = predicate(
1217
1347
  number,
1218
- (n) => n % 2 === 1,
1348
+ (n) => n % 2 !== 0,
1219
1349
  'Must be odd'
1220
1350
  );
1221
1351
  const verify = guard(odd);
@@ -1234,6 +1364,38 @@ predicate][type-predicates], then this will be reflected in the return type, too
1234
1364
 
1235
1365
  ---
1236
1366
 
1367
+ <a name="prep" href="#prep">#</a> <b>prep</b><i>&lt;T, I&gt;</i>(<i>unknown => I</i>,
1368
+ <i>Decoder&lt;T, I&gt;</i>): <i>Decoder&lt;T&gt;</i>
1369
+ [&lt;&gt;](https://github.com/nvie/decoders/blob/main/src/core/composition.js 'Source')<br />
1370
+
1371
+ Pre-process the raw data input before passing it into the decoder. This gives you the
1372
+ ability to arbitrarily customize the input on the fly before passing it to the decoder. Of
1373
+ course, the input value at that point is still of `unknown` type, so you will have to deal
1374
+ with that accordingly.
1375
+
1376
+ <!-- prettier-ignore-start -->
1377
+ ```typescript
1378
+ const verify = prep(
1379
+ // Will convert any input to an int first, before feeding it to
1380
+ // positiveInteger. This will effectively also allow numeric strings
1381
+ // to be accepted (and returned) as integers. If this ever throws,
1382
+ // then the error message will be what gets annotated on the input.
1383
+ x => parseInt(x),
1384
+ positiveInteger,
1385
+ );
1386
+
1387
+ // 👍
1388
+ verify(42) === 42;
1389
+ verify('3') === 3;
1390
+
1391
+ // 👎
1392
+ verify('-3'); // throws: not a positive number
1393
+ verify('hi'); // throws: not a number
1394
+ ```
1395
+ <!-- prettier-ignore-end -->
1396
+
1397
+ ---
1398
+
1237
1399
  <a name="describe" href="#describe">#</a>
1238
1400
  <b>describe</b><i>&lt;T&gt;</i>(<i>Decoder&lt;T&gt;</i>, <i>string</i>):
1239
1401
  <i>Decoder&lt;T&gt;</i>
@@ -1304,13 +1466,13 @@ And a runtime input of:
1304
1466
  | | Extra properties | Output value | Inferred type |
1305
1467
  | ---------------- | ---------------- | ------------------------------ | ------------------------------------------- |
1306
1468
  | `object(thing)` | discarded | `{a: "hi", b: 42}` | `{a: string, b: number}` |
1307
- | `exact(thing)` | not allowed | ⚡️ Runtime error | `{a: string, b: number}` |
1469
+ | `exact(thing)` | not allowed | n/a (rejected) | `{a: string, b: number}` |
1308
1470
  | `inexact(thing)` | retained | `{a: "hi", b: 42, c: "extra"}` | `{a: string, b: number, [string]: unknown}` |
1309
1471
 
1310
1472
  ### Building custom decoders
1311
1473
 
1312
- There are two main building blocks for defining your own custom decoders: `map()` and
1313
- `compose()`.
1474
+ There are two main building blocks for defining your own custom decoders: `transform()`
1475
+ and `compose()`.
1314
1476
 
1315
1477
  There are roughly 3 use cases that you will want to use:
1316
1478
 
@@ -1325,7 +1487,7 @@ There are roughly 3 use cases that you will want to use:
1325
1487
  To read one type from the input, but return another, use:
1326
1488
 
1327
1489
  ```js
1328
- const numericString: Decoder<number> = map(
1490
+ const numericString: Decoder<number> = transform(
1329
1491
  // At runtime, expect to read a string...
1330
1492
  string,
1331
1493
  // ...but return it as a number
@@ -1336,7 +1498,7 @@ const numericString: Decoder<number> = map(
1336
1498
  To read one type, but change its value before returning:
1337
1499
 
1338
1500
  ```js
1339
- const upperCase: Decoder<string> = map(string, (s) => s.toUpperCase());
1501
+ const upperCase: Decoder<string> = transform(string, (s) => s.toUpperCase());
1340
1502
  ```
1341
1503
 
1342
1504
  **WARNING:** While you can map anything to anything, it's typically **NOT A GOOD IDEA to
package/_utils.js CHANGED
@@ -7,7 +7,7 @@ exports.indent = indent;
7
7
  exports.isDate = isDate;
8
8
  exports.isMultiline = isMultiline;
9
9
  exports.summarize = summarize;
10
- // $FlowFixMe[unclear-type] - deliberate casting
10
+ // $FlowFixMe[unclear-type] - deliberate use of `any`
11
11
  // Two spaces of indentation
12
12
  var INDENT = ' ';
13
13
  /**
package/_utils.js.flow CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  import type { Annotation } from './annotate';
4
4
 
5
- // $FlowFixMe[unclear-type] - deliberate casting
6
- type cast = any;
5
+ // $FlowFixMe[unclear-type] - deliberate use of `any`
6
+ export type _Any = any;
7
7
 
8
8
  // Two spaces of indentation
9
9
  export const INDENT = ' ';
@@ -29,7 +29,7 @@ export function isDate(value: mixed): boolean {
29
29
  * null.
30
30
  */
31
31
  export function asDate(value: mixed): Date | null {
32
- return isDate(value) ? ((value: cast): Date) : null;
32
+ return isDate(value) ? ((value: _Any): Date) : null;
33
33
  }
34
34
 
35
35
  export function isMultiline(s: string): boolean {
package/_utils.mjs CHANGED
@@ -1,4 +1,4 @@
1
- // $FlowFixMe[unclear-type] - deliberate casting
1
+ // $FlowFixMe[unclear-type] - deliberate use of `any`
2
2
  // Two spaces of indentation
3
3
  export var INDENT = ' ';
4
4
  /**
package/core/array.d.ts CHANGED
@@ -1,5 +1,8 @@
1
+ /// <reference lib="es6" />
2
+
1
3
  import { Decoder } from '../_types';
2
4
 
3
5
  export const poja: Decoder<unknown[]>;
4
6
  export function array<T>(decoder: Decoder<T>): Decoder<T[]>;
5
7
  export function nonEmptyArray<T>(decoder: Decoder<T>): Decoder<[T, ...T[]]>;
8
+ export function set<T>(decoder: Decoder<T>): Decoder<Set<T>>;
package/core/array.js CHANGED
@@ -4,6 +4,7 @@ exports.__esModule = true;
4
4
  exports.array = array;
5
5
  exports.nonEmptyArray = nonEmptyArray;
6
6
  exports.poja = void 0;
7
+ exports.set = set;
7
8
 
8
9
  var _annotate = require("../annotate");
9
10
 
@@ -47,7 +48,7 @@ function all(items, blobs) {
47
48
  for (var index = 0; index < items.length; ++index) {
48
49
  var result = items[index];
49
50
 
50
- if (result.type === 'ok') {
51
+ if (result.ok) {
51
52
  results.push(result.value);
52
53
  } else {
53
54
  var ann = result.error; // Rewrite the annotation to include the index information, and inject it into the original blob
@@ -101,4 +102,14 @@ function nonEmptyArray(decoder) {
101
102
  return (0, _composition.predicate)(array(decoder), function (arr) {
102
103
  return arr.length > 0;
103
104
  }, 'Must be non-empty array');
105
+ }
106
+ /**
107
+ * Similar to `array()`, but returns the result as an ES6 Set.
108
+ */
109
+
110
+
111
+ function set(decoder) {
112
+ return (0, _composition.transform)(array(decoder), function (items) {
113
+ return new Set(items);
114
+ });
104
115
  }
@@ -1,7 +1,7 @@
1
1
  // @flow strict
2
2
 
3
3
  import { annotate } from '../annotate';
4
- import { compose, predicate } from './composition';
4
+ import { compose, predicate, transform } from './composition';
5
5
  import { err, ok } from '../result';
6
6
  import type { Decoder, DecodeResult } from '../_types';
7
7
 
@@ -40,7 +40,7 @@ function all<T>(
40
40
  const results: Array<T> = [];
41
41
  for (let index = 0; index < items.length; ++index) {
42
42
  const result = items[index];
43
- if (result.type === 'ok') {
43
+ if (result.ok) {
44
44
  results.push(result.value);
45
45
  } else {
46
46
  const ann = result.error;
@@ -98,3 +98,10 @@ export function array<T>(decoder: Decoder<T>): Decoder<Array<T>> {
98
98
  export function nonEmptyArray<T>(decoder: Decoder<T>): Decoder<Array<T>> {
99
99
  return predicate(array(decoder), (arr) => arr.length > 0, 'Must be non-empty array');
100
100
  }
101
+
102
+ /**
103
+ * Similar to `array()`, but returns the result as an ES6 Set.
104
+ */
105
+ export function set<T>(decoder: Decoder<T>): Decoder<Set<T>> {
106
+ return transform(array(decoder), (items) => new Set(items));
107
+ }
package/core/array.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { annotate } from '../annotate.mjs';
2
- import { compose, predicate } from './composition.mjs';
2
+ import { compose, predicate, transform } from './composition.mjs';
3
3
  import { err, ok } from '../result.mjs';
4
4
 
5
5
  /**
@@ -35,7 +35,7 @@ function all(items, blobs) {
35
35
  for (var index = 0; index < items.length; ++index) {
36
36
  var result = items[index];
37
37
 
38
- if (result.type === 'ok') {
38
+ if (result.ok) {
39
39
  results.push(result.value);
40
40
  } else {
41
41
  var ann = result.error; // Rewrite the annotation to include the index information, and inject it into the original blob
@@ -88,4 +88,13 @@ export function nonEmptyArray(decoder) {
88
88
  return predicate(array(decoder), function (arr) {
89
89
  return arr.length > 0;
90
90
  }, 'Must be non-empty array');
91
+ }
92
+ /**
93
+ * Similar to `array()`, but returns the result as an ES6 Set.
94
+ */
95
+
96
+ export function set(decoder) {
97
+ return transform(array(decoder), function (items) {
98
+ return new Set(items);
99
+ });
91
100
  }