decoders 2.0.0 → 2.0.1
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 +11 -0
- package/Decoder.js +41 -148
- package/Decoder.js.flow +1 -1
- package/Decoder.mjs +38 -145
- package/README.md +1 -1
- package/_utils.js +50 -61
- package/_utils.js.flow +18 -4
- package/_utils.mjs +41 -52
- package/annotate.js +79 -72
- package/annotate.mjs +62 -58
- package/format.js +64 -74
- package/format.js.flow +11 -11
- package/format.mjs +58 -68
- package/index.js +66 -66
- package/index.mjs +11 -11
- package/lib/arrays.js +47 -89
- package/lib/arrays.mjs +38 -78
- package/lib/basics.js +59 -109
- package/lib/basics.mjs +49 -94
- package/lib/booleans.js +14 -24
- package/lib/booleans.mjs +9 -19
- package/lib/dates.js +18 -34
- package/lib/dates.mjs +12 -27
- package/lib/json.d.ts +3 -3
- package/lib/json.js +20 -42
- package/lib/json.js.flow +2 -2
- package/lib/json.mjs +13 -35
- package/lib/numbers.js +19 -39
- package/lib/numbers.mjs +11 -31
- package/lib/objects.d.ts +11 -10
- package/lib/objects.js +84 -163
- package/lib/objects.js.flow +4 -12
- package/lib/objects.mjs +74 -150
- package/lib/strings.js +41 -84
- package/lib/strings.mjs +25 -67
- package/lib/unions.d.ts +1 -1
- package/lib/unions.js +52 -116
- package/lib/unions.js.flow +1 -1
- package/lib/unions.mjs +46 -109
- package/lib/utilities.d.ts +7 -1
- package/lib/utilities.js +23 -50
- package/lib/utilities.mjs +15 -38
- package/package.json +1 -1
- package/result.js +9 -22
- package/result.mjs +5 -17
package/lib/json.js.flow
CHANGED
|
@@ -15,12 +15,12 @@ export type JSONObject = { [string]: JSONValue };
|
|
|
15
15
|
export type JSONArray = Array<JSONValue>;
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
18
|
+
* Accepts objects that contain only valid JSON values.
|
|
19
19
|
*/
|
|
20
20
|
export const jsonObject: Decoder<JSONObject> = lazy(() => dict(json));
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
23
|
+
* Accepts arrays that contain only valid JSON values.
|
|
24
24
|
*/
|
|
25
25
|
export const jsonArray: Decoder<JSONArray> = lazy(() => array(json));
|
|
26
26
|
|
package/lib/json.mjs
CHANGED
|
@@ -1,40 +1,18 @@
|
|
|
1
|
-
import { array } from './arrays.mjs'
|
|
2
|
-
import { boolean as _boolean } from './booleans.mjs'
|
|
3
|
-
import { dict } from './objects.mjs'
|
|
4
|
-
import { either } from './unions.mjs'
|
|
5
|
-
import { lazy } from './utilities.mjs'
|
|
6
|
-
import { null_ } from './basics.mjs'
|
|
7
|
-
import { number } from './numbers.mjs'
|
|
8
|
-
import { string } from './strings.mjs'
|
|
1
|
+
import { array } from './arrays.mjs'
|
|
2
|
+
import { boolean as _boolean } from './booleans.mjs'
|
|
3
|
+
import { dict } from './objects.mjs'
|
|
4
|
+
import { either } from './unions.mjs'
|
|
5
|
+
import { lazy } from './utilities.mjs'
|
|
6
|
+
import { null_ } from './basics.mjs'
|
|
7
|
+
import { number } from './numbers.mjs'
|
|
8
|
+
import { string } from './strings.mjs'
|
|
9
9
|
|
|
10
|
-
/**
|
|
11
|
-
* Like `json`, but will only decode when the JSON value is an object.
|
|
12
|
-
*/
|
|
13
10
|
export var jsonObject = lazy(function () {
|
|
14
|
-
return dict(json)
|
|
15
|
-
})
|
|
16
|
-
/**
|
|
17
|
-
* Like `json`, but will only decode when the JSON value is an array.
|
|
18
|
-
*/
|
|
11
|
+
return dict(json)
|
|
12
|
+
})
|
|
19
13
|
|
|
20
14
|
export var jsonArray = lazy(function () {
|
|
21
|
-
return array(json)
|
|
22
|
-
})
|
|
23
|
-
/**
|
|
24
|
-
* Accepts any value that's a valid JSON value.
|
|
25
|
-
*
|
|
26
|
-
* In other words: any value returned by `JSON.parse()` should decode without
|
|
27
|
-
* failure.
|
|
28
|
-
*
|
|
29
|
-
* ```typescript
|
|
30
|
-
* type JSONValue =
|
|
31
|
-
* | null
|
|
32
|
-
* | string
|
|
33
|
-
* | number
|
|
34
|
-
* | boolean
|
|
35
|
-
* | { [string]: JSONValue }
|
|
36
|
-
* | JSONValue[]
|
|
37
|
-
* ```
|
|
38
|
-
*/
|
|
15
|
+
return array(json)
|
|
16
|
+
})
|
|
39
17
|
|
|
40
|
-
export var json = either(null_, string, number, _boolean, jsonObject, jsonArray).describe('Must be valid JSON value')
|
|
18
|
+
export var json = either(null_, string, number, _boolean, jsonObject, jsonArray).describe('Must be valid JSON value')
|
package/lib/numbers.js
CHANGED
|
@@ -1,51 +1,31 @@
|
|
|
1
|
-
|
|
1
|
+
'use strict'
|
|
2
2
|
|
|
3
|
-
exports.__esModule = true
|
|
4
|
-
exports.positiveNumber = exports.positiveInteger = exports.number = exports.integer = exports.anyNumber = void 0
|
|
3
|
+
exports.__esModule = true
|
|
4
|
+
exports.positiveNumber = exports.positiveInteger = exports.number = exports.integer = exports.anyNumber = void 0
|
|
5
5
|
|
|
6
|
-
var _Decoder = require(
|
|
6
|
+
var _Decoder = require('../Decoder')
|
|
7
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
8
|
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
|
-
*/
|
|
9
|
+
return typeof blob === 'number' ? ok(blob) : err('Must be number')
|
|
10
|
+
})
|
|
22
11
|
|
|
23
|
-
exports.anyNumber = anyNumber
|
|
12
|
+
exports.anyNumber = anyNumber
|
|
24
13
|
var number = anyNumber.refine(function (n) {
|
|
25
|
-
return Number.isFinite(n)
|
|
26
|
-
}, 'Number must be finite')
|
|
27
|
-
/**
|
|
28
|
-
* Accepts only finite whole numbers.
|
|
29
|
-
*/
|
|
14
|
+
return Number.isFinite(n)
|
|
15
|
+
}, 'Number must be finite')
|
|
30
16
|
|
|
31
|
-
exports.number = number
|
|
17
|
+
exports.number = number
|
|
32
18
|
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
|
-
*/
|
|
19
|
+
return Number.isInteger(n)
|
|
20
|
+
}, 'Number must be an integer')
|
|
38
21
|
|
|
39
|
-
exports.integer = integer
|
|
22
|
+
exports.integer = integer
|
|
40
23
|
var positiveNumber = number.refine(function (n) {
|
|
41
|
-
return n >= 0
|
|
42
|
-
}, 'Number must be positive')
|
|
43
|
-
/**
|
|
44
|
-
* Accepts only positive finite whole numbers.
|
|
45
|
-
*/
|
|
24
|
+
return n >= 0
|
|
25
|
+
}, 'Number must be positive')
|
|
46
26
|
|
|
47
|
-
exports.positiveNumber = positiveNumber
|
|
27
|
+
exports.positiveNumber = positiveNumber
|
|
48
28
|
var positiveInteger = integer.refine(function (n) {
|
|
49
|
-
return n >= 0
|
|
50
|
-
}, 'Number must be positive')
|
|
51
|
-
exports.positiveInteger = positiveInteger
|
|
29
|
+
return n >= 0
|
|
30
|
+
}, 'Number must be positive')
|
|
31
|
+
exports.positiveInteger = positiveInteger
|
package/lib/numbers.mjs
CHANGED
|
@@ -1,41 +1,21 @@
|
|
|
1
|
-
import { define } from '../Decoder.mjs'
|
|
1
|
+
import { define } from '../Decoder.mjs'
|
|
2
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
3
|
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
|
-
*/
|
|
4
|
+
return typeof blob === 'number' ? ok(blob) : err('Must be number')
|
|
5
|
+
})
|
|
17
6
|
|
|
18
7
|
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
|
-
*/
|
|
8
|
+
return Number.isFinite(n)
|
|
9
|
+
}, 'Number must be finite')
|
|
24
10
|
|
|
25
11
|
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
|
-
*/
|
|
12
|
+
return Number.isInteger(n)
|
|
13
|
+
}, 'Number must be an integer')
|
|
31
14
|
|
|
32
15
|
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
|
-
*/
|
|
16
|
+
return n >= 0
|
|
17
|
+
}, 'Number must be positive')
|
|
38
18
|
|
|
39
19
|
export var positiveInteger = integer.refine(function (n) {
|
|
40
|
-
return n >= 0
|
|
41
|
-
}, 'Number must be positive')
|
|
20
|
+
return n >= 0
|
|
21
|
+
}, 'Number must be positive')
|
package/lib/objects.d.ts
CHANGED
|
@@ -11,14 +11,14 @@ export type ObjectDecoderType<T> = AllowImplicit<{
|
|
|
11
11
|
* Accepts any "plain old JavaScript object", but doesn't validate its keys or
|
|
12
12
|
* values further.
|
|
13
13
|
*/
|
|
14
|
-
export const pojo: Decoder<
|
|
14
|
+
export const pojo: Decoder<Record<string, unknown>>;
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Accepts objects with fields matching the given decoders. Extra fields that
|
|
18
18
|
* exist on the input object are ignored and will not be returned.
|
|
19
19
|
*/
|
|
20
20
|
export function object(decodersByKey: Record<any, never>): Decoder<Record<string, never>>;
|
|
21
|
-
export function object<O extends
|
|
21
|
+
export function object<O extends Record<string, Decoder<any>>>(
|
|
22
22
|
decodersByKey: O,
|
|
23
23
|
): Decoder<{ [K in keyof ObjectDecoderType<O>]: ObjectDecoderType<O>[K] }>;
|
|
24
24
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
@@ -34,7 +34,7 @@ export function object<O extends { [key: string]: Decoder<any> }>(
|
|
|
34
34
|
* not specified explicitly.
|
|
35
35
|
*/
|
|
36
36
|
export function exact(decodersByKey: Record<any, never>): Decoder<Record<string, never>>;
|
|
37
|
-
export function exact<O extends
|
|
37
|
+
export function exact<O extends Record<string, Decoder<any>>>(
|
|
38
38
|
decodersByKey: O,
|
|
39
39
|
): Decoder<{ [K in keyof ObjectDecoderType<O>]: ObjectDecoderType<O>[K] }>;
|
|
40
40
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
@@ -46,18 +46,19 @@ export function exact<O extends { [key: string]: Decoder<any> }>(
|
|
|
46
46
|
*/
|
|
47
47
|
export function inexact(
|
|
48
48
|
decodersByKey: Record<any, never>,
|
|
49
|
-
): Decoder<
|
|
50
|
-
export function inexact<O extends
|
|
49
|
+
): Decoder<Record<string, unknown>>;
|
|
50
|
+
export function inexact<O extends Record<string, Decoder<any>>>(
|
|
51
51
|
decodersByKey: O,
|
|
52
52
|
): Decoder<
|
|
53
|
-
{ [K in keyof ObjectDecoderType<O>]: ObjectDecoderType<O>[K] } &
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
{ [K in keyof ObjectDecoderType<O>]: ObjectDecoderType<O>[K] } & Record<
|
|
54
|
+
string,
|
|
55
|
+
unknown
|
|
56
|
+
>
|
|
56
57
|
>;
|
|
57
58
|
|
|
58
59
|
/**
|
|
59
60
|
* Accepts objects where all values match the given decoder, and returns the
|
|
60
|
-
* result as a `
|
|
61
|
+
* result as a `Record<string, T>`.
|
|
61
62
|
*
|
|
62
63
|
* The main difference between `object()` and `dict()` is that you'd typically
|
|
63
64
|
* use `object()` if this is a record-like object, where all field names are
|
|
@@ -65,7 +66,7 @@ export function inexact<O extends { [key: string]: Decoder<any> }>(
|
|
|
65
66
|
* typically dynamic and the values homogeneous, like in a dictionary,
|
|
66
67
|
* a lookup table, or a cache.
|
|
67
68
|
*/
|
|
68
|
-
export function dict<T>(decoder: Decoder<T>): Decoder<
|
|
69
|
+
export function dict<T>(decoder: Decoder<T>): Decoder<Record<string, T>>;
|
|
69
70
|
|
|
70
71
|
/**
|
|
71
72
|
* Similar to `dict()`, but returns the result as a `Map<string, T>` (an [ES6
|
package/lib/objects.js
CHANGED
|
@@ -1,240 +1,161 @@
|
|
|
1
|
-
|
|
1
|
+
'use strict'
|
|
2
2
|
|
|
3
|
-
exports.__esModule = true
|
|
4
|
-
exports.dict = dict
|
|
5
|
-
exports.exact = exact
|
|
6
|
-
exports.inexact = inexact
|
|
7
|
-
exports.mapping = mapping
|
|
8
|
-
exports.object = object
|
|
9
|
-
exports.pojo = void 0
|
|
3
|
+
exports.__esModule = true
|
|
4
|
+
exports.dict = dict
|
|
5
|
+
exports.exact = exact
|
|
6
|
+
exports.inexact = inexact
|
|
7
|
+
exports.mapping = mapping
|
|
8
|
+
exports.object = object
|
|
9
|
+
exports.pojo = void 0
|
|
10
10
|
|
|
11
|
-
var _annotate = require(
|
|
11
|
+
var _annotate = require('../annotate')
|
|
12
12
|
|
|
13
|
-
var _Decoder = require(
|
|
14
|
-
|
|
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); }
|
|
16
|
-
|
|
17
|
-
function subtract(xs, ys) {
|
|
18
|
-
var result = new Set();
|
|
19
|
-
xs.forEach(function (x) {
|
|
20
|
-
if (!ys.has(x)) {
|
|
21
|
-
result.add(x);
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
return result;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Accepts any "plain old JavaScript object", but doesn't validate its keys or
|
|
28
|
-
* values further.
|
|
29
|
-
*/
|
|
13
|
+
var _Decoder = require('../Decoder')
|
|
30
14
|
|
|
15
|
+
var _utils = require('../_utils')
|
|
31
16
|
|
|
32
17
|
var pojo = (0, _Decoder.define)(function (blob, ok, err) {
|
|
33
|
-
return blob !== null && blob !== undefined && typeof blob === 'object' &&
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// Since Flow 0.98, typeof o === 'object' refines to
|
|
38
|
-
// {| +[string]: mixed |}
|
|
39
|
-
// instead of
|
|
40
|
-
// {| [string]: mixed |}
|
|
41
|
-
//
|
|
42
|
-
// For rationale, see https://github.com/facebook/flow/issues/7685.
|
|
43
|
-
// In this case, we don't want to output a read-only version of
|
|
44
|
-
// the object because it's up to the user of decoders to
|
|
45
|
-
// determine what they want to do with the decoded output. If they
|
|
46
|
-
// want to write items into the array, that's fine! The fastest
|
|
47
|
-
// way to turn a read-only Object to a writeable one in ES6 seems
|
|
48
|
-
// to be to use object-spread. (Going off this benchmark:
|
|
49
|
-
// https://thecodebarbarian.com/object-assign-vs-object-spread.html)
|
|
50
|
-
_extends({}, blob)) : err('Must be an object');
|
|
51
|
-
});
|
|
52
|
-
/**
|
|
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.
|
|
55
|
-
*/
|
|
56
|
-
|
|
57
|
-
exports.pojo = pojo;
|
|
18
|
+
return blob !== null && blob !== undefined && typeof blob === 'object' && Object.prototype.toString.call(blob) === '[object Object]' ? ok(blob) : err('Must be an object')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
exports.pojo = pojo
|
|
58
22
|
|
|
59
23
|
function object(decodersByKey) {
|
|
60
|
-
|
|
61
|
-
var knownKeys = new Set(Object.keys(decodersByKey));
|
|
24
|
+
var knownKeys = new Set(Object.keys(decodersByKey))
|
|
62
25
|
return pojo.then(function (plainObj, ok, err) {
|
|
63
|
-
var actualKeys = new Set(Object.keys(plainObj))
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
var missingKeys = subtract(knownKeys, actualKeys);
|
|
69
|
-
var record = {};
|
|
70
|
-
var errors = null;
|
|
26
|
+
var actualKeys = new Set(Object.keys(plainObj))
|
|
27
|
+
|
|
28
|
+
var missingKeys = (0, _utils.subtract)(knownKeys, actualKeys)
|
|
29
|
+
var record = {}
|
|
30
|
+
var errors = null
|
|
71
31
|
Object.keys(decodersByKey).forEach(function (key) {
|
|
72
|
-
var decoder = decodersByKey[key]
|
|
73
|
-
var rawValue = plainObj[key]
|
|
74
|
-
var result = decoder.decode(rawValue)
|
|
32
|
+
var decoder = decodersByKey[key]
|
|
33
|
+
var rawValue = plainObj[key]
|
|
34
|
+
var result = decoder.decode(rawValue)
|
|
75
35
|
|
|
76
36
|
if (result.ok) {
|
|
77
|
-
var value = result.value
|
|
37
|
+
var value = result.value
|
|
78
38
|
|
|
79
39
|
if (value !== undefined) {
|
|
80
|
-
record[key] = value
|
|
81
|
-
}
|
|
82
|
-
// tracker
|
|
83
|
-
|
|
40
|
+
record[key] = value
|
|
41
|
+
}
|
|
84
42
|
|
|
85
|
-
missingKeys[
|
|
43
|
+
missingKeys['delete'](key)
|
|
86
44
|
} else {
|
|
87
|
-
var ann = result.error
|
|
88
|
-
// want to collect more error information.
|
|
45
|
+
var ann = result.error
|
|
89
46
|
|
|
90
47
|
if (rawValue === undefined) {
|
|
91
|
-
|
|
92
|
-
// undefined. This covers explicit undefineds to be
|
|
93
|
-
// treated the same as implicit undefineds (aka missing
|
|
94
|
-
// keys).
|
|
95
|
-
missingKeys.add(key);
|
|
48
|
+
missingKeys.add(key)
|
|
96
49
|
} else {
|
|
97
50
|
if (errors === null) {
|
|
98
|
-
errors = {}
|
|
51
|
+
errors = {}
|
|
99
52
|
}
|
|
100
53
|
|
|
101
|
-
errors[key] = ann
|
|
54
|
+
errors[key] = ann
|
|
102
55
|
}
|
|
103
56
|
}
|
|
104
|
-
})
|
|
105
|
-
// report. First of all, we want to report any inline errors in this
|
|
106
|
-
// object. Lastly, any fields that are missing should be annotated on
|
|
107
|
-
// the outer object itself.
|
|
57
|
+
})
|
|
108
58
|
|
|
109
59
|
if (errors || missingKeys.size > 0) {
|
|
110
|
-
var objAnn = (0, _annotate.annotateObject)(plainObj)
|
|
60
|
+
var objAnn = (0, _annotate.annotateObject)(plainObj)
|
|
111
61
|
|
|
112
62
|
if (errors) {
|
|
113
|
-
objAnn = (0, _annotate.merge)(objAnn, errors)
|
|
63
|
+
objAnn = (0, _annotate.merge)(objAnn, errors)
|
|
114
64
|
}
|
|
115
65
|
|
|
116
66
|
if (missingKeys.size > 0) {
|
|
117
|
-
var errMsg = Array.from(missingKeys)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
67
|
+
var errMsg = Array.from(missingKeys)
|
|
68
|
+
.map(function (key) {
|
|
69
|
+
return '"' + key + '"'
|
|
70
|
+
})
|
|
71
|
+
.join(', ')
|
|
72
|
+
var pluralized = missingKeys.size > 1 ? 'keys' : 'key'
|
|
73
|
+
objAnn = (0, _annotate.updateText)(objAnn, 'Missing ' + pluralized + ': ' + errMsg)
|
|
122
74
|
}
|
|
123
75
|
|
|
124
|
-
return err(objAnn)
|
|
76
|
+
return err(objAnn)
|
|
125
77
|
}
|
|
126
78
|
|
|
127
|
-
return ok(record)
|
|
128
|
-
})
|
|
79
|
+
return ok(record)
|
|
80
|
+
})
|
|
129
81
|
}
|
|
130
|
-
/**
|
|
131
|
-
* Like `object()`, but will reject inputs that contain extra fields that are
|
|
132
|
-
* not specified explicitly.
|
|
133
|
-
*/
|
|
134
|
-
|
|
135
82
|
|
|
136
83
|
function exact(decodersByKey) {
|
|
137
|
-
|
|
138
|
-
var allowedKeys = new Set(Object.keys(decodersByKey)); // Check the inputted object for any unexpected extra keys
|
|
84
|
+
var allowedKeys = new Set(Object.keys(decodersByKey))
|
|
139
85
|
|
|
140
86
|
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 ?
|
|
144
|
-
|
|
145
|
-
}); // Defer to the "object" decoder for doing the real decoding work. Since
|
|
146
|
-
// we made sure there are no superfluous keys in this structure, it's now
|
|
147
|
-
// safe to force-cast it to an $Exact<> type.
|
|
148
|
-
|
|
149
|
-
return checked.then(object(decodersByKey).decode);
|
|
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
|
-
*/
|
|
87
|
+
var actualKeys = new Set(Object.keys(plainObj))
|
|
88
|
+
var extraKeys = (0, _utils.subtract)(actualKeys, allowedKeys)
|
|
89
|
+
return extraKeys.size > 0 ? 'Unexpected extra keys: ' + Array.from(extraKeys).join(', ') : null
|
|
90
|
+
})
|
|
155
91
|
|
|
92
|
+
return checked.then(object(decodersByKey).decode)
|
|
93
|
+
}
|
|
156
94
|
|
|
157
95
|
function inexact(decodersByKey) {
|
|
158
96
|
return pojo.then(function (plainObj) {
|
|
159
|
-
var allkeys = new Set(Object.keys(plainObj))
|
|
97
|
+
var allkeys = new Set(Object.keys(plainObj))
|
|
160
98
|
var decoder = object(decodersByKey).transform(function (safepart) {
|
|
161
|
-
var safekeys = new Set(Object.keys(decodersByKey))
|
|
99
|
+
var safekeys = new Set(Object.keys(decodersByKey))
|
|
162
100
|
|
|
163
101
|
safekeys.forEach(function (k) {
|
|
164
|
-
return allkeys.add(k)
|
|
165
|
-
})
|
|
166
|
-
var rv = {}
|
|
102
|
+
return allkeys.add(k)
|
|
103
|
+
})
|
|
104
|
+
var rv = {}
|
|
167
105
|
allkeys.forEach(function (k) {
|
|
168
106
|
if (safekeys.has(k)) {
|
|
169
|
-
var value = safepart[k]
|
|
107
|
+
var value = safepart[k]
|
|
170
108
|
|
|
171
109
|
if (value !== undefined) {
|
|
172
|
-
rv[k] = value
|
|
110
|
+
rv[k] = value
|
|
173
111
|
}
|
|
174
112
|
} else {
|
|
175
|
-
rv[k] = plainObj[k]
|
|
113
|
+
rv[k] = plainObj[k]
|
|
176
114
|
}
|
|
177
|
-
})
|
|
178
|
-
return rv
|
|
179
|
-
})
|
|
180
|
-
return decoder.decode(plainObj)
|
|
181
|
-
})
|
|
115
|
+
})
|
|
116
|
+
return rv
|
|
117
|
+
})
|
|
118
|
+
return decoder.decode(plainObj)
|
|
119
|
+
})
|
|
182
120
|
}
|
|
183
|
-
/**
|
|
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.
|
|
192
|
-
*/
|
|
193
|
-
|
|
194
121
|
|
|
195
122
|
function dict(decoder) {
|
|
196
123
|
return pojo.then(function (plainObj, ok, err) {
|
|
197
|
-
var rv = {}
|
|
198
|
-
var errors = null
|
|
124
|
+
var rv = {}
|
|
125
|
+
var errors = null
|
|
199
126
|
Object.keys(plainObj).forEach(function (key) {
|
|
200
|
-
var value = plainObj[key]
|
|
201
|
-
var result = decoder.decode(value)
|
|
127
|
+
var value = plainObj[key]
|
|
128
|
+
var result = decoder.decode(value)
|
|
202
129
|
|
|
203
130
|
if (result.ok) {
|
|
204
131
|
if (errors === null) {
|
|
205
|
-
rv[key] = result.value
|
|
132
|
+
rv[key] = result.value
|
|
206
133
|
}
|
|
207
134
|
} else {
|
|
208
|
-
rv = {}
|
|
135
|
+
rv = {}
|
|
209
136
|
|
|
210
137
|
if (errors === null) {
|
|
211
|
-
errors = {}
|
|
138
|
+
errors = {}
|
|
212
139
|
}
|
|
213
140
|
|
|
214
|
-
errors[key] = result.error
|
|
141
|
+
errors[key] = result.error
|
|
215
142
|
}
|
|
216
|
-
})
|
|
143
|
+
})
|
|
217
144
|
|
|
218
145
|
if (errors !== null) {
|
|
219
|
-
return err((0, _annotate.merge)((0, _annotate.annotateObject)(plainObj), errors))
|
|
146
|
+
return err((0, _annotate.merge)((0, _annotate.annotateObject)(plainObj), errors))
|
|
220
147
|
} else {
|
|
221
|
-
return ok(rv)
|
|
148
|
+
return ok(rv)
|
|
222
149
|
}
|
|
223
|
-
})
|
|
150
|
+
})
|
|
224
151
|
}
|
|
225
|
-
/**
|
|
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.
|
|
229
|
-
*/
|
|
230
|
-
|
|
231
152
|
|
|
232
153
|
function mapping(decoder) {
|
|
233
154
|
return dict(decoder).transform(function (obj) {
|
|
234
|
-
return new Map(
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
})
|
|
240
|
-
}
|
|
155
|
+
return new Map(
|
|
156
|
+
Object.keys(obj).map(function (key) {
|
|
157
|
+
return [key, obj[key]]
|
|
158
|
+
})
|
|
159
|
+
)
|
|
160
|
+
})
|
|
161
|
+
}
|
package/lib/objects.js.flow
CHANGED
|
@@ -2,20 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
import { annotateObject, merge, updateText } from '../annotate';
|
|
4
4
|
import { define } from '../Decoder';
|
|
5
|
+
import { subtract } from '../_utils';
|
|
5
6
|
import type { Annotation } from '../annotate';
|
|
6
7
|
import type { _Any as AnyDecoder } from '../_utils';
|
|
7
8
|
import type { Decoder, DecodeResult } from '../Decoder';
|
|
8
9
|
|
|
9
|
-
function subtract(xs: Set<string>, ys: Set<string>): Set<string> {
|
|
10
|
-
const result = new Set();
|
|
11
|
-
xs.forEach((x) => {
|
|
12
|
-
if (!ys.has(x)) {
|
|
13
|
-
result.add(x);
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
|
-
return result;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
10
|
/**
|
|
20
11
|
* Accepts any "plain old JavaScript object", but doesn't validate its keys or
|
|
21
12
|
* values further.
|
|
@@ -43,7 +34,8 @@ export const pojo: Decoder<{| [string]: mixed |}> = define((blob, ok, err) =>
|
|
|
43
34
|
// way to turn a read-only Object to a writeable one in ES6 seems
|
|
44
35
|
// to be to use object-spread. (Going off this benchmark:
|
|
45
36
|
// https://thecodebarbarian.com/object-assign-vs-object-spread.html)
|
|
46
|
-
|
|
37
|
+
// $FlowFixMe[incompatible-variance]
|
|
38
|
+
blob,
|
|
47
39
|
)
|
|
48
40
|
: err('Must be an object'),
|
|
49
41
|
);
|
|
@@ -192,7 +184,7 @@ export function inexact<O: { +[field: string]: AnyDecoder }>(
|
|
|
192
184
|
|
|
193
185
|
/**
|
|
194
186
|
* Accepts objects where all values match the given decoder, and returns the
|
|
195
|
-
* result as a `
|
|
187
|
+
* result as a `Record<string, T>`.
|
|
196
188
|
*
|
|
197
189
|
* The main difference between `object()` and `dict()` is that you'd typically
|
|
198
190
|
* use `object()` if this is a record-like object, where all field names are
|