decoders 2.0.0-beta9 → 2.0.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.
Files changed (136) hide show
  1. package/CHANGELOG.md +56 -29
  2. package/Decoder.d.ts +94 -0
  3. package/Decoder.js +222 -0
  4. package/Decoder.js.flow +286 -0
  5. package/Decoder.mjs +215 -0
  6. package/README.md +122 -1507
  7. package/_utils.d.ts +0 -1
  8. package/_utils.js +11 -17
  9. package/_utils.js.flow +13 -17
  10. package/_utils.mjs +10 -14
  11. package/format.d.ts +4 -2
  12. package/format.js +1 -1
  13. package/format.js.flow +3 -1
  14. package/format.mjs +1 -1
  15. package/index.d.ts +29 -31
  16. package/index.js +62 -84
  17. package/index.js.flow +30 -48
  18. package/index.mjs +11 -36
  19. package/{core → lib}/_helpers.d.ts +0 -0
  20. package/lib/arrays.d.ts +59 -0
  21. package/lib/arrays.js +139 -0
  22. package/lib/arrays.js.flow +138 -0
  23. package/lib/arrays.mjs +124 -0
  24. package/lib/basics.d.ts +93 -0
  25. package/lib/basics.js +144 -0
  26. package/lib/basics.js.flow +124 -0
  27. package/lib/basics.mjs +120 -0
  28. package/lib/booleans.d.ts +16 -0
  29. package/lib/booleans.js +35 -0
  30. package/lib/booleans.js.flow +22 -0
  31. package/lib/booleans.mjs +25 -0
  32. package/lib/dates.d.ts +15 -0
  33. package/lib/dates.js +44 -0
  34. package/lib/dates.js.flow +40 -0
  35. package/lib/dates.mjs +34 -0
  36. package/lib/json.d.ts +35 -0
  37. package/lib/json.js +55 -0
  38. package/lib/json.js.flow +50 -0
  39. package/lib/json.mjs +40 -0
  40. package/lib/numbers.d.ts +31 -0
  41. package/lib/numbers.js +51 -0
  42. package/lib/numbers.js.flow +48 -0
  43. package/lib/numbers.mjs +41 -0
  44. package/lib/objects.d.ts +75 -0
  45. package/{core/object.js → lib/objects.js} +78 -85
  46. package/{core/object.js.flow → lib/objects.js.flow} +89 -102
  47. package/{core/object.mjs → lib/objects.mjs} +77 -82
  48. package/lib/strings.d.ts +56 -0
  49. package/lib/strings.js +101 -0
  50. package/lib/strings.js.flow +90 -0
  51. package/lib/strings.mjs +82 -0
  52. package/lib/unions.d.ts +55 -0
  53. package/lib/unions.js +160 -0
  54. package/{core/either.js.flow → lib/unions.js.flow} +67 -17
  55. package/lib/unions.mjs +146 -0
  56. package/lib/utilities.d.ts +34 -0
  57. package/lib/utilities.js +75 -0
  58. package/lib/utilities.js.flow +65 -0
  59. package/lib/utilities.mjs +60 -0
  60. package/package.json +64 -17
  61. package/result.d.ts +0 -23
  62. package/result.js +0 -68
  63. package/result.js.flow +0 -72
  64. package/result.mjs +0 -54
  65. package/_guard.d.ts +0 -7
  66. package/_guard.js +0 -22
  67. package/_guard.js.flow +0 -20
  68. package/_guard.mjs +0 -15
  69. package/_types.d.ts +0 -13
  70. package/_types.js +0 -1
  71. package/_types.js.flow +0 -20
  72. package/_types.mjs +0 -0
  73. package/core/array.d.ts +0 -8
  74. package/core/array.js +0 -115
  75. package/core/array.js.flow +0 -107
  76. package/core/array.mjs +0 -100
  77. package/core/boolean.d.ts +0 -5
  78. package/core/boolean.js +0 -40
  79. package/core/boolean.js.flow +0 -27
  80. package/core/boolean.mjs +0 -28
  81. package/core/composition.d.ts +0 -18
  82. package/core/composition.js +0 -82
  83. package/core/composition.js.flow +0 -74
  84. package/core/composition.mjs +0 -70
  85. package/core/constants.d.ts +0 -11
  86. package/core/constants.js +0 -65
  87. package/core/constants.js.flow +0 -44
  88. package/core/constants.mjs +0 -46
  89. package/core/date.d.ts +0 -4
  90. package/core/date.js +0 -42
  91. package/core/date.js.flow +0 -38
  92. package/core/date.mjs +0 -28
  93. package/core/describe.d.ts +0 -3
  94. package/core/describe.js +0 -22
  95. package/core/describe.js.flow +0 -17
  96. package/core/describe.mjs +0 -16
  97. package/core/dispatch.d.ts +0 -8
  98. package/core/dispatch.js +0 -60
  99. package/core/dispatch.js.flow +0 -59
  100. package/core/dispatch.mjs +0 -52
  101. package/core/either.d.ts +0 -66
  102. package/core/either.js +0 -101
  103. package/core/either.mjs +0 -90
  104. package/core/fail.d.ts +0 -3
  105. package/core/fail.js +0 -17
  106. package/core/fail.js.flow +0 -12
  107. package/core/fail.mjs +0 -11
  108. package/core/instanceOf.d.ts +0 -3
  109. package/core/instanceOf.js +0 -15
  110. package/core/instanceOf.js.flow +0 -20
  111. package/core/instanceOf.mjs +0 -8
  112. package/core/json.d.ts +0 -11
  113. package/core/json.js +0 -31
  114. package/core/json.js.flow +0 -28
  115. package/core/json.mjs +0 -15
  116. package/core/lazy.d.ts +0 -3
  117. package/core/lazy.js +0 -16
  118. package/core/lazy.js.flow +0 -15
  119. package/core/lazy.mjs +0 -11
  120. package/core/number.d.ts +0 -6
  121. package/core/number.js +0 -36
  122. package/core/number.js.flow +0 -40
  123. package/core/number.mjs +0 -25
  124. package/core/object.d.ts +0 -38
  125. package/core/optional.d.ts +0 -5
  126. package/core/optional.js +0 -50
  127. package/core/optional.js.flow +0 -41
  128. package/core/optional.mjs +0 -38
  129. package/core/string.d.ts +0 -13
  130. package/core/string.js +0 -80
  131. package/core/string.js.flow +0 -72
  132. package/core/string.mjs +0 -58
  133. package/core/tuple.d.ts +0 -30
  134. package/core/tuple.js +0 -54
  135. package/core/tuple.js.flow +0 -51
  136. package/core/tuple.mjs +0 -45
@@ -0,0 +1,31 @@
1
+ import { Decoder } from '../Decoder';
2
+
3
+ /**
4
+ * Accepts any valid ``number`` value.
5
+ *
6
+ * This also accepts special values like `NaN` and `Infinity`. Unless you
7
+ * want to deliberately accept those, you'll likely want to use the
8
+ * `number` decoder instead.
9
+ */
10
+ export const anyNumber: Decoder<number>;
11
+
12
+ /**
13
+ * Accepts finite numbers (can be integer or float values). Values `NaN`,
14
+ * or positive and negative `Infinity` will get rejected.
15
+ */
16
+ export const number: Decoder<number>;
17
+
18
+ /**
19
+ * Accepts only finite whole numbers.
20
+ */
21
+ export const integer: Decoder<number>;
22
+
23
+ /**
24
+ * Accepts only positive finite numbers.
25
+ */
26
+ export const positiveNumber: Decoder<number>;
27
+
28
+ /**
29
+ * Accepts only positive finite whole numbers.
30
+ */
31
+ export const positiveInteger: Decoder<number>;
package/lib/numbers.js ADDED
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+
3
+ exports.__esModule = true;
4
+ exports.positiveNumber = exports.positiveInteger = exports.number = exports.integer = exports.anyNumber = void 0;
5
+
6
+ var _Decoder = require("../Decoder");
7
+
8
+ /**
9
+ * Accepts any valid ``number`` value.
10
+ *
11
+ * This also accepts special values like `NaN` and `Infinity`. Unless you
12
+ * want to deliberately accept those, you'll likely want to use the
13
+ * `number` decoder instead.
14
+ */
15
+ var anyNumber = (0, _Decoder.define)(function (blob, ok, err) {
16
+ return typeof blob === 'number' ? ok(blob) : err('Must be number');
17
+ });
18
+ /**
19
+ * Accepts finite numbers (can be integer or float values). Values `NaN`,
20
+ * or positive and negative `Infinity` will get rejected.
21
+ */
22
+
23
+ exports.anyNumber = anyNumber;
24
+ var number = anyNumber.refine(function (n) {
25
+ return Number.isFinite(n);
26
+ }, 'Number must be finite');
27
+ /**
28
+ * Accepts only finite whole numbers.
29
+ */
30
+
31
+ exports.number = number;
32
+ var integer = number.refine(function (n) {
33
+ return Number.isInteger(n);
34
+ }, 'Number must be an integer');
35
+ /**
36
+ * Accepts only positive finite numbers.
37
+ */
38
+
39
+ exports.integer = integer;
40
+ var positiveNumber = number.refine(function (n) {
41
+ return n >= 0;
42
+ }, 'Number must be positive');
43
+ /**
44
+ * Accepts only positive finite whole numbers.
45
+ */
46
+
47
+ exports.positiveNumber = positiveNumber;
48
+ var positiveInteger = integer.refine(function (n) {
49
+ return n >= 0;
50
+ }, 'Number must be positive');
51
+ exports.positiveInteger = positiveInteger;
@@ -0,0 +1,48 @@
1
+ // @flow strict
2
+
3
+ import { define } from '../Decoder';
4
+ import type { Decoder } from '../Decoder';
5
+
6
+ /**
7
+ * Accepts any valid ``number`` value.
8
+ *
9
+ * This also accepts special values like `NaN` and `Infinity`. Unless you
10
+ * want to deliberately accept those, you'll likely want to use the
11
+ * `number` decoder instead.
12
+ */
13
+ export const anyNumber: Decoder<number> = define((blob, ok, err) =>
14
+ typeof blob === 'number' ? ok(blob) : err('Must be number'),
15
+ );
16
+
17
+ /**
18
+ * Accepts finite numbers (can be integer or float values). Values `NaN`,
19
+ * or positive and negative `Infinity` will get rejected.
20
+ */
21
+ export const number: Decoder<number> = anyNumber.refine(
22
+ (n) => Number.isFinite(n),
23
+ 'Number must be finite',
24
+ );
25
+
26
+ /**
27
+ * Accepts only finite whole numbers.
28
+ */
29
+ export const integer: Decoder<number> = number.refine(
30
+ (n) => Number.isInteger(n),
31
+ 'Number must be an integer',
32
+ );
33
+
34
+ /**
35
+ * Accepts only positive finite numbers.
36
+ */
37
+ export const positiveNumber: Decoder<number> = number.refine(
38
+ (n) => n >= 0,
39
+ 'Number must be positive',
40
+ );
41
+
42
+ /**
43
+ * Accepts only positive finite whole numbers.
44
+ */
45
+ export const positiveInteger: Decoder<number> = integer.refine(
46
+ (n) => n >= 0,
47
+ 'Number must be positive',
48
+ );
@@ -0,0 +1,41 @@
1
+ import { define } from '../Decoder.mjs';
2
+
3
+ /**
4
+ * Accepts any valid ``number`` value.
5
+ *
6
+ * This also accepts special values like `NaN` and `Infinity`. Unless you
7
+ * want to deliberately accept those, you'll likely want to use the
8
+ * `number` decoder instead.
9
+ */
10
+ export var anyNumber = define(function (blob, ok, err) {
11
+ return typeof blob === 'number' ? ok(blob) : err('Must be number');
12
+ });
13
+ /**
14
+ * Accepts finite numbers (can be integer or float values). Values `NaN`,
15
+ * or positive and negative `Infinity` will get rejected.
16
+ */
17
+
18
+ export var number = anyNumber.refine(function (n) {
19
+ return Number.isFinite(n);
20
+ }, 'Number must be finite');
21
+ /**
22
+ * Accepts only finite whole numbers.
23
+ */
24
+
25
+ export var integer = number.refine(function (n) {
26
+ return Number.isInteger(n);
27
+ }, 'Number must be an integer');
28
+ /**
29
+ * Accepts only positive finite numbers.
30
+ */
31
+
32
+ export var positiveNumber = number.refine(function (n) {
33
+ return n >= 0;
34
+ }, 'Number must be positive');
35
+ /**
36
+ * Accepts only positive finite whole numbers.
37
+ */
38
+
39
+ export var positiveInteger = integer.refine(function (n) {
40
+ return n >= 0;
41
+ }, 'Number must be positive');
@@ -0,0 +1,75 @@
1
+ /// <reference lib="es6" />
2
+
3
+ import { Decoder, DecoderType } from '../Decoder';
4
+ import { AllowImplicit } from './_helpers';
5
+
6
+ export type ObjectDecoderType<T> = AllowImplicit<{
7
+ [key in keyof T]: DecoderType<T[key]>;
8
+ }>;
9
+
10
+ /**
11
+ * Accepts any "plain old JavaScript object", but doesn't validate its keys or
12
+ * values further.
13
+ */
14
+ export const pojo: Decoder<{ [key: string]: unknown }>;
15
+
16
+ /**
17
+ * Accepts objects with fields matching the given decoders. Extra fields that
18
+ * exist on the input object are ignored and will not be returned.
19
+ */
20
+ export function object(decodersByKey: Record<any, never>): Decoder<Record<string, never>>;
21
+ export function object<O extends { [key: string]: Decoder<any> }>(
22
+ decodersByKey: O,
23
+ ): Decoder<{ [K in keyof ObjectDecoderType<O>]: ObjectDecoderType<O>[K] }>;
24
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
25
+ // This is basically just equivalent to:
26
+ // ObjectDecoderType<O>
27
+ //
28
+ // But by "resolving" this with a mapped type, we remove the helper
29
+ // type names from the inferred type here, making this much easier to
30
+ // work with while developing.
31
+
32
+ /**
33
+ * Like `object()`, but will reject inputs that contain extra fields that are
34
+ * not specified explicitly.
35
+ */
36
+ export function exact(decodersByKey: Record<any, never>): Decoder<Record<string, never>>;
37
+ export function exact<O extends { [key: string]: Decoder<any> }>(
38
+ decodersByKey: O,
39
+ ): Decoder<{ [K in keyof ObjectDecoderType<O>]: ObjectDecoderType<O>[K] }>;
40
+ // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
41
+ // Ditto (see above)
42
+
43
+ /**
44
+ * Like `object()`, but will pass through any extra fields on the input object
45
+ * unvalidated that will thus be of `unknown` type statically.
46
+ */
47
+ export function inexact(
48
+ decodersByKey: Record<any, never>,
49
+ ): Decoder<{ [extra: string]: unknown }>;
50
+ export function inexact<O extends { [key: string]: Decoder<any> }>(
51
+ decodersByKey: O,
52
+ ): Decoder<
53
+ { [K in keyof ObjectDecoderType<O>]: ObjectDecoderType<O>[K] } & {
54
+ [extra: string]: unknown;
55
+ }
56
+ >;
57
+
58
+ /**
59
+ * Accepts objects where all values match the given decoder, and returns the
60
+ * result as a `{ [string]: T }`.
61
+ *
62
+ * The main difference between `object()` and `dict()` is that you'd typically
63
+ * use `object()` if this is a record-like object, where all field names are
64
+ * known and the values are heterogeneous. Whereas with `dict()` the keys are
65
+ * typically dynamic and the values homogeneous, like in a dictionary,
66
+ * a lookup table, or a cache.
67
+ */
68
+ export function dict<T>(decoder: Decoder<T>): Decoder<{ [key: string]: T }>;
69
+
70
+ /**
71
+ * Similar to `dict()`, but returns the result as a `Map<string, T>` (an [ES6
72
+ * Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map))
73
+ * instead.
74
+ */
75
+ export function mapping<T>(decoder: Decoder<T>): Decoder<Map<string, T>>;
@@ -10,19 +10,10 @@ exports.pojo = void 0;
10
10
 
11
11
  var _annotate = require("../annotate");
12
12
 
13
- var _composition = require("./composition");
14
-
15
- var _result = require("../result");
13
+ var _Decoder = require("../Decoder");
16
14
 
17
15
  function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
18
16
 
19
- function isPojo(o) {
20
- return o !== null && o !== undefined && typeof o === 'object' && // This still seems to be the only reliable way to determine whether
21
- // something is a pojo... ¯\_(ツ)_/¯
22
- // $FlowFixMe[method-unbinding]
23
- Object.prototype.toString.call(o) === '[object Object]';
24
- }
25
-
26
17
  function subtract(xs, ys) {
27
18
  var result = new Set();
28
19
  xs.forEach(function (x) {
@@ -32,9 +23,17 @@ function subtract(xs, ys) {
32
23
  });
33
24
  return result;
34
25
  }
26
+ /**
27
+ * Accepts any "plain old JavaScript object", but doesn't validate its keys or
28
+ * values further.
29
+ */
35
30
 
36
- var pojo = function pojo(blob) {
37
- return isPojo(blob) ? (0, _result.ok)( // NOTE:
31
+
32
+ var pojo = (0, _Decoder.define)(function (blob, ok, err) {
33
+ return blob !== null && blob !== undefined && typeof blob === 'object' && // This still seems to be the only reliable way to determine whether
34
+ // something is a pojo... ¯\_(ツ)_/¯
35
+ // $FlowFixMe[method-unbinding]
36
+ Object.prototype.toString.call(blob) === '[object Object]' ? ok( // NOTE:
38
37
  // Since Flow 0.98, typeof o === 'object' refines to
39
38
  // {| +[string]: mixed |}
40
39
  // instead of
@@ -48,46 +47,31 @@ var pojo = function pojo(blob) {
48
47
  // way to turn a read-only Object to a writeable one in ES6 seems
49
48
  // to be to use object-spread. (Going off this benchmark:
50
49
  // https://thecodebarbarian.com/object-assign-vs-object-spread.html)
51
- _extends({}, blob)) : (0, _result.err)((0, _annotate.annotate)(blob, 'Must be an object'));
52
- };
50
+ _extends({}, blob)) : err('Must be an object');
51
+ });
53
52
  /**
54
- * Given a mapping of fields-to-decoders, builds a decoder for an object type.
55
- *
56
- * For example, given decoders for a number and a string, we can construct an
57
- * "object description" like so:
58
- *
59
- * { id: number, name: string }
60
- *
61
- * Which is of type:
62
- *
63
- * { id: Decoder<number>, name: Decoder<string> }
64
- *
65
- * Passing this to object() will produce the following return type:
66
- *
67
- * Decoder<{ id: number, name: string }>
68
- *
69
- * Put simply: it'll "peel off" all of the nested Decoders, puts them together
70
- * in an object, and wraps it in a Decoder<...>.
53
+ * Accepts objects with fields matching the given decoders. Extra fields that
54
+ * exist on the input object are ignored and will not be returned.
71
55
  */
72
56
 
73
-
74
57
  exports.pojo = pojo;
75
58
 
76
- function object(mapping) {
77
- var known = new Set(Object.keys(mapping));
78
- return (0, _composition.compose)(pojo, function (blob) {
79
- var actual = new Set(Object.keys(blob)); // At this point, "missing" will also include all fields that may
80
- // validly be optional. We'll let the underlying decoder decide and
59
+ function object(decodersByKey) {
60
+ // Compute this set at decoder definition time
61
+ var knownKeys = new Set(Object.keys(decodersByKey));
62
+ return pojo.then(function (plainObj, ok, err) {
63
+ var actualKeys = new Set(Object.keys(plainObj)); // At this point, "missingKeys" will also include all fields that may
64
+ // validly be optional. We'll let the underlying decoder decide and
81
65
  // remove the key from this missing set if the decoder accepts the
82
66
  // value.
83
67
 
84
- var missing = subtract(known, actual);
68
+ var missingKeys = subtract(knownKeys, actualKeys);
85
69
  var record = {};
86
70
  var errors = null;
87
- Object.keys(mapping).forEach(function (key) {
88
- var decoder = mapping[key];
89
- var rawValue = blob[key];
90
- var result = decoder(rawValue);
71
+ Object.keys(decodersByKey).forEach(function (key) {
72
+ var decoder = decodersByKey[key];
73
+ var rawValue = plainObj[key];
74
+ var result = decoder.decode(rawValue);
91
75
 
92
76
  if (result.ok) {
93
77
  var value = result.value;
@@ -98,7 +82,7 @@ function object(mapping) {
98
82
  // tracker
99
83
 
100
84
 
101
- missing["delete"](key);
85
+ missingKeys["delete"](key);
102
86
  } else {
103
87
  var ann = result.error; // Keep track of the annotation, but don't return just yet. We
104
88
  // want to collect more error information.
@@ -108,7 +92,7 @@ function object(mapping) {
108
92
  // undefined. This covers explicit undefineds to be
109
93
  // treated the same as implicit undefineds (aka missing
110
94
  // keys).
111
- missing.add(key);
95
+ missingKeys.add(key);
112
96
  } else {
113
97
  if (errors === null) {
114
98
  errors = {};
@@ -122,53 +106,59 @@ function object(mapping) {
122
106
  // object. Lastly, any fields that are missing should be annotated on
123
107
  // the outer object itself.
124
108
 
125
- if (errors || missing.size > 0) {
126
- var objAnn = (0, _annotate.annotateObject)(blob);
109
+ if (errors || missingKeys.size > 0) {
110
+ var objAnn = (0, _annotate.annotateObject)(plainObj);
127
111
 
128
112
  if (errors) {
129
113
  objAnn = (0, _annotate.merge)(objAnn, errors);
130
114
  }
131
115
 
132
- if (missing.size > 0) {
133
- var errMsg = Array.from(missing).map(function (key) {
116
+ if (missingKeys.size > 0) {
117
+ var errMsg = Array.from(missingKeys).map(function (key) {
134
118
  return "\"" + key + "\"";
135
119
  }).join(', ');
136
- var pluralized = missing.size > 1 ? 'keys' : 'key';
120
+ var pluralized = missingKeys.size > 1 ? 'keys' : 'key';
137
121
  objAnn = (0, _annotate.updateText)(objAnn, "Missing " + pluralized + ": " + errMsg);
138
122
  }
139
123
 
140
- return (0, _result.err)(objAnn);
124
+ return err(objAnn);
141
125
  }
142
126
 
143
- return (0, _result.ok)(record);
127
+ return ok(record);
144
128
  });
145
129
  }
130
+ /**
131
+ * Like `object()`, but will reject inputs that contain extra fields that are
132
+ * not specified explicitly.
133
+ */
146
134
 
147
- function exact(mapping) {
148
- // Check the inputted object for any superfluous keys
149
- var allowed = new Set(Object.keys(mapping));
150
- var checked = (0, _composition.compose)(pojo, function (blob) {
151
- var actual = new Set(Object.keys(blob));
152
- var superfluous = subtract(actual, allowed);
153
135
 
154
- if (superfluous.size > 0) {
155
- return (0, _result.err)((0, _annotate.annotate)(blob, "Superfluous keys: " + Array.from(superfluous).join(', ')));
156
- }
136
+ function exact(decodersByKey) {
137
+ // Compute this set at decoder definition time
138
+ var allowedKeys = new Set(Object.keys(decodersByKey)); // Check the inputted object for any unexpected extra keys
157
139
 
158
- return (0, _result.ok)(blob);
140
+ var checked = pojo.reject(function (plainObj) {
141
+ var actualKeys = new Set(Object.keys(plainObj));
142
+ var extraKeys = subtract(actualKeys, allowedKeys);
143
+ return extraKeys.size > 0 ? "Unexpected extra keys: " + Array.from(extraKeys).join(', ') : // Don't reject
144
+ null;
159
145
  }); // Defer to the "object" decoder for doing the real decoding work. Since
160
146
  // we made sure there are no superfluous keys in this structure, it's now
161
147
  // safe to force-cast it to an $Exact<> type.
162
148
 
163
- var decoder = object(mapping);
164
- return (0, _composition.compose)(checked, decoder);
149
+ return checked.then(object(decodersByKey).decode);
165
150
  }
151
+ /**
152
+ * Like `object()`, but will pass through any extra fields on the input object
153
+ * unvalidated that will thus be of `unknown` type statically.
154
+ */
166
155
 
167
- function inexact(mapping) {
168
- return (0, _composition.compose)(pojo, function (blob) {
169
- var allkeys = new Set(Object.keys(blob));
170
- var decoder = (0, _composition.transform)(object(mapping), function (safepart) {
171
- var safekeys = new Set(Object.keys(mapping)); // To account for hard-coded keys that aren't part of the input
156
+
157
+ function inexact(decodersByKey) {
158
+ return pojo.then(function (plainObj) {
159
+ var allkeys = new Set(Object.keys(plainObj));
160
+ var decoder = object(decodersByKey).transform(function (safepart) {
161
+ var safekeys = new Set(Object.keys(decodersByKey)); // To account for hard-coded keys that aren't part of the input
172
162
 
173
163
  safekeys.forEach(function (k) {
174
164
  return allkeys.add(k);
@@ -182,26 +172,33 @@ function inexact(mapping) {
182
172
  rv[k] = value;
183
173
  }
184
174
  } else {
185
- rv[k] = blob[k];
175
+ rv[k] = plainObj[k];
186
176
  }
187
177
  });
188
178
  return rv;
189
179
  });
190
- return decoder(blob);
180
+ return decoder.decode(plainObj);
191
181
  });
192
182
  }
193
183
  /**
194
- * Like mapping(), but returns an object rather than a Map instance.
184
+ * Accepts objects where all values match the given decoder, and returns the
185
+ * result as a `{ [string]: T }`.
186
+ *
187
+ * The main difference between `object()` and `dict()` is that you'd typically
188
+ * use `object()` if this is a record-like object, where all field names are
189
+ * known and the values are heterogeneous. Whereas with `dict()` the keys are
190
+ * typically dynamic and the values homogeneous, like in a dictionary,
191
+ * a lookup table, or a cache.
195
192
  */
196
193
 
197
194
 
198
195
  function dict(decoder) {
199
- return (0, _composition.compose)(pojo, function (blob) {
196
+ return pojo.then(function (plainObj, ok, err) {
200
197
  var rv = {};
201
198
  var errors = null;
202
- Object.keys(blob).forEach(function (key) {
203
- var value = blob[key];
204
- var result = decoder(value);
199
+ Object.keys(plainObj).forEach(function (key) {
200
+ var value = plainObj[key];
201
+ var result = decoder.decode(value);
205
202
 
206
203
  if (result.ok) {
207
204
  if (errors === null) {
@@ -219,25 +216,21 @@ function dict(decoder) {
219
216
  });
220
217
 
221
218
  if (errors !== null) {
222
- return (0, _result.err)((0, _annotate.merge)((0, _annotate.annotateObject)(blob), errors));
219
+ return err((0, _annotate.merge)((0, _annotate.annotateObject)(plainObj), errors));
223
220
  } else {
224
- return (0, _result.ok)(rv);
221
+ return ok(rv);
225
222
  }
226
223
  });
227
224
  }
228
225
  /**
229
- * Given an object, will decode a Map of string keys to whatever values.
230
- *
231
- * For example, given a decoder for a Person, we can verify a Person lookup
232
- * table structure (of type Map<string, Person>) like so:
233
- *
234
- * mapping(person)
235
- *
226
+ * Similar to `dict()`, but returns the result as a `Map<string, T>` (an [ES6
227
+ * Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map))
228
+ * instead.
236
229
  */
237
230
 
238
231
 
239
232
  function mapping(decoder) {
240
- return (0, _composition.transform)(dict(decoder), function (obj) {
233
+ return dict(decoder).transform(function (obj) {
241
234
  return new Map( // This is effectively Object.entries(obj), but in a way that Flow
242
235
  // will know the types are okay
243
236
  Object.keys(obj).map(function (key) {