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.
- package/CHANGELOG.md +56 -29
- package/Decoder.d.ts +94 -0
- package/Decoder.js +222 -0
- package/Decoder.js.flow +286 -0
- package/Decoder.mjs +215 -0
- package/README.md +122 -1507
- package/_utils.d.ts +0 -1
- package/_utils.js +11 -17
- package/_utils.js.flow +13 -17
- package/_utils.mjs +10 -14
- package/format.d.ts +4 -2
- package/format.js +1 -1
- package/format.js.flow +3 -1
- package/format.mjs +1 -1
- package/index.d.ts +29 -31
- package/index.js +62 -84
- package/index.js.flow +30 -48
- package/index.mjs +11 -36
- package/{core → lib}/_helpers.d.ts +0 -0
- package/lib/arrays.d.ts +59 -0
- package/lib/arrays.js +139 -0
- package/lib/arrays.js.flow +138 -0
- package/lib/arrays.mjs +124 -0
- package/lib/basics.d.ts +93 -0
- package/lib/basics.js +144 -0
- package/lib/basics.js.flow +124 -0
- package/lib/basics.mjs +120 -0
- package/lib/booleans.d.ts +16 -0
- package/lib/booleans.js +35 -0
- package/lib/booleans.js.flow +22 -0
- package/lib/booleans.mjs +25 -0
- package/lib/dates.d.ts +15 -0
- package/lib/dates.js +44 -0
- package/lib/dates.js.flow +40 -0
- package/lib/dates.mjs +34 -0
- package/lib/json.d.ts +35 -0
- package/lib/json.js +55 -0
- package/lib/json.js.flow +50 -0
- package/lib/json.mjs +40 -0
- package/lib/numbers.d.ts +31 -0
- package/lib/numbers.js +51 -0
- package/lib/numbers.js.flow +48 -0
- package/lib/numbers.mjs +41 -0
- package/lib/objects.d.ts +75 -0
- package/{core/object.js → lib/objects.js} +78 -85
- package/{core/object.js.flow → lib/objects.js.flow} +89 -102
- package/{core/object.mjs → lib/objects.mjs} +77 -82
- package/lib/strings.d.ts +56 -0
- package/lib/strings.js +101 -0
- package/lib/strings.js.flow +90 -0
- package/lib/strings.mjs +82 -0
- package/lib/unions.d.ts +55 -0
- package/lib/unions.js +160 -0
- package/{core/either.js.flow → lib/unions.js.flow} +67 -17
- package/lib/unions.mjs +146 -0
- package/lib/utilities.d.ts +34 -0
- package/lib/utilities.js +75 -0
- package/lib/utilities.js.flow +65 -0
- package/lib/utilities.mjs +60 -0
- package/package.json +64 -17
- package/result.d.ts +0 -23
- package/result.js +0 -68
- package/result.js.flow +0 -72
- package/result.mjs +0 -54
- package/_guard.d.ts +0 -7
- package/_guard.js +0 -22
- package/_guard.js.flow +0 -20
- package/_guard.mjs +0 -15
- package/_types.d.ts +0 -13
- package/_types.js +0 -1
- package/_types.js.flow +0 -20
- package/_types.mjs +0 -0
- package/core/array.d.ts +0 -8
- package/core/array.js +0 -115
- package/core/array.js.flow +0 -107
- package/core/array.mjs +0 -100
- package/core/boolean.d.ts +0 -5
- package/core/boolean.js +0 -40
- package/core/boolean.js.flow +0 -27
- package/core/boolean.mjs +0 -28
- package/core/composition.d.ts +0 -18
- package/core/composition.js +0 -82
- package/core/composition.js.flow +0 -74
- package/core/composition.mjs +0 -70
- package/core/constants.d.ts +0 -11
- package/core/constants.js +0 -65
- package/core/constants.js.flow +0 -44
- package/core/constants.mjs +0 -46
- package/core/date.d.ts +0 -4
- package/core/date.js +0 -42
- package/core/date.js.flow +0 -38
- package/core/date.mjs +0 -28
- package/core/describe.d.ts +0 -3
- package/core/describe.js +0 -22
- package/core/describe.js.flow +0 -17
- package/core/describe.mjs +0 -16
- package/core/dispatch.d.ts +0 -8
- package/core/dispatch.js +0 -60
- package/core/dispatch.js.flow +0 -59
- package/core/dispatch.mjs +0 -52
- package/core/either.d.ts +0 -66
- package/core/either.js +0 -101
- package/core/either.mjs +0 -90
- package/core/fail.d.ts +0 -3
- package/core/fail.js +0 -17
- package/core/fail.js.flow +0 -12
- package/core/fail.mjs +0 -11
- package/core/instanceOf.d.ts +0 -3
- package/core/instanceOf.js +0 -15
- package/core/instanceOf.js.flow +0 -20
- package/core/instanceOf.mjs +0 -8
- package/core/json.d.ts +0 -11
- package/core/json.js +0 -31
- package/core/json.js.flow +0 -28
- package/core/json.mjs +0 -15
- package/core/lazy.d.ts +0 -3
- package/core/lazy.js +0 -16
- package/core/lazy.js.flow +0 -15
- package/core/lazy.mjs +0 -11
- package/core/number.d.ts +0 -6
- package/core/number.js +0 -36
- package/core/number.js.flow +0 -40
- package/core/number.mjs +0 -25
- package/core/object.d.ts +0 -38
- package/core/optional.d.ts +0 -5
- package/core/optional.js +0 -50
- package/core/optional.js.flow +0 -41
- package/core/optional.mjs +0 -38
- package/core/string.d.ts +0 -13
- package/core/string.js +0 -80
- package/core/string.js.flow +0 -72
- package/core/string.mjs +0 -58
- package/core/tuple.d.ts +0 -30
- package/core/tuple.js +0 -54
- package/core/tuple.js.flow +0 -51
- package/core/tuple.mjs +0 -45
package/lib/numbers.d.ts
ADDED
|
@@ -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
|
+
);
|
package/lib/numbers.mjs
ADDED
|
@@ -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');
|
package/lib/objects.d.ts
ADDED
|
@@ -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
|
|
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
|
-
|
|
37
|
-
|
|
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)) :
|
|
52
|
-
};
|
|
50
|
+
_extends({}, blob)) : err('Must be an object');
|
|
51
|
+
});
|
|
53
52
|
/**
|
|
54
|
-
*
|
|
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(
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
|
68
|
+
var missingKeys = subtract(knownKeys, actualKeys);
|
|
85
69
|
var record = {};
|
|
86
70
|
var errors = null;
|
|
87
|
-
Object.keys(
|
|
88
|
-
var decoder =
|
|
89
|
-
var rawValue =
|
|
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
|
-
|
|
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
|
-
|
|
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 ||
|
|
126
|
-
var objAnn = (0, _annotate.annotateObject)(
|
|
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 (
|
|
133
|
-
var errMsg = Array.from(
|
|
116
|
+
if (missingKeys.size > 0) {
|
|
117
|
+
var errMsg = Array.from(missingKeys).map(function (key) {
|
|
134
118
|
return "\"" + key + "\"";
|
|
135
119
|
}).join(', ');
|
|
136
|
-
var pluralized =
|
|
120
|
+
var pluralized = missingKeys.size > 1 ? 'keys' : 'key';
|
|
137
121
|
objAnn = (0, _annotate.updateText)(objAnn, "Missing " + pluralized + ": " + errMsg);
|
|
138
122
|
}
|
|
139
123
|
|
|
140
|
-
return
|
|
124
|
+
return err(objAnn);
|
|
141
125
|
}
|
|
142
126
|
|
|
143
|
-
return
|
|
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
|
-
|
|
155
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
var
|
|
171
|
-
|
|
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] =
|
|
175
|
+
rv[k] = plainObj[k];
|
|
186
176
|
}
|
|
187
177
|
});
|
|
188
178
|
return rv;
|
|
189
179
|
});
|
|
190
|
-
return decoder(
|
|
180
|
+
return decoder.decode(plainObj);
|
|
191
181
|
});
|
|
192
182
|
}
|
|
193
183
|
/**
|
|
194
|
-
*
|
|
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 (
|
|
196
|
+
return pojo.then(function (plainObj, ok, err) {
|
|
200
197
|
var rv = {};
|
|
201
198
|
var errors = null;
|
|
202
|
-
Object.keys(
|
|
203
|
-
var value =
|
|
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
|
|
219
|
+
return err((0, _annotate.merge)((0, _annotate.annotateObject)(plainObj), errors));
|
|
223
220
|
} else {
|
|
224
|
-
return
|
|
221
|
+
return ok(rv);
|
|
225
222
|
}
|
|
226
223
|
});
|
|
227
224
|
}
|
|
228
225
|
/**
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
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 (
|
|
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) {
|