decoders 1.26.0-beta2 → 2.0.0-beta11
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 +52 -6
- package/Decoder.d.ts +28 -0
- package/Decoder.js +213 -0
- package/Decoder.js.flow +238 -0
- package/Decoder.mjs +206 -0
- package/NotSupportedTSVersion.d.ts +1 -0
- package/README.md +105 -961
- package/_utils.d.ts +9 -0
- package/_utils.js +102 -0
- package/_utils.js.flow +93 -0
- package/_utils.mjs +89 -0
- package/annotate.d.ts +62 -0
- package/annotate.js +161 -0
- package/annotate.js.flow +218 -0
- package/annotate.mjs +144 -0
- package/format.d.ts +4 -0
- package/format.js +151 -0
- package/format.js.flow +126 -0
- package/format.mjs +140 -0
- package/index.d.ts +40 -0
- package/index.js +90 -0
- package/index.js.flow +44 -0
- package/index.mjs +11 -0
- package/{ts/helpers.d.ts → lib/_helpers.d.ts} +0 -0
- package/lib/arrays.d.ts +37 -0
- package/lib/arrays.js +138 -0
- package/lib/arrays.js.flow +138 -0
- package/lib/arrays.mjs +123 -0
- package/lib/basics.d.ts +14 -0
- package/lib/basics.js +109 -0
- package/lib/basics.js.flow +85 -0
- package/lib/basics.mjs +85 -0
- package/{ts/boolean.d.ts → lib/booleans.d.ts} +1 -1
- package/lib/booleans.js +35 -0
- package/lib/booleans.js.flow +22 -0
- package/lib/booleans.mjs +25 -0
- package/{ts/date.d.ts → lib/dates.d.ts} +1 -1
- package/lib/dates.js +44 -0
- package/lib/dates.js.flow +40 -0
- package/lib/dates.mjs +34 -0
- package/{ts → lib}/json.d.ts +1 -1
- package/lib/json.js +55 -0
- package/lib/json.js.flow +50 -0
- package/lib/json.mjs +40 -0
- package/{ts/number.d.ts → lib/numbers.d.ts} +2 -1
- package/lib/numbers.js +52 -0
- package/lib/numbers.js.flow +49 -0
- package/lib/numbers.mjs +42 -0
- package/{ts/object.d.ts → lib/objects.d.ts} +11 -6
- package/lib/objects.js +240 -0
- package/lib/objects.js.flow +246 -0
- package/lib/objects.mjs +223 -0
- package/lib/strings.d.ts +13 -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 +78 -0
- package/lib/unions.js +161 -0
- package/lib/unions.js.flow +158 -0
- package/lib/unions.mjs +145 -0
- package/lib/utilities.d.ts +10 -0
- package/lib/utilities.js +94 -0
- package/lib/utilities.js.flow +84 -0
- package/lib/utilities.mjs +79 -0
- package/package.json +79 -28
- package/result.d.ts +16 -0
- package/result.js +34 -0
- package/result.js.flow +26 -0
- package/result.mjs +27 -0
- package/cjs/array.js +0 -133
- package/cjs/array.js.flow +0 -106
- package/cjs/boolean.js +0 -42
- package/cjs/boolean.js.flow +0 -28
- package/cjs/constants.js +0 -67
- package/cjs/constants.js.flow +0 -45
- package/cjs/date.js +0 -42
- package/cjs/date.js.flow +0 -38
- package/cjs/describe.js +0 -22
- package/cjs/describe.js.flow +0 -17
- package/cjs/dispatch.js +0 -58
- package/cjs/dispatch.js.flow +0 -57
- package/cjs/either.js +0 -85
- package/cjs/either.js.flow +0 -131
- package/cjs/fail.js +0 -19
- package/cjs/fail.js.flow +0 -13
- package/cjs/guard.js +0 -30
- package/cjs/guard.js.flow +0 -36
- package/cjs/index.js +0 -397
- package/cjs/index.js.flow +0 -56
- package/cjs/instanceOf.js +0 -17
- package/cjs/instanceOf.js.flow +0 -21
- package/cjs/json.js +0 -33
- package/cjs/json.js.flow +0 -28
- package/cjs/lazy.js +0 -18
- package/cjs/lazy.js.flow +0 -15
- package/cjs/mapping.js +0 -113
- package/cjs/mapping.js.flow +0 -71
- package/cjs/number.js +0 -38
- package/cjs/number.js.flow +0 -35
- package/cjs/object.js +0 -254
- package/cjs/object.js.flow +0 -211
- package/cjs/optional.js +0 -52
- package/cjs/optional.js.flow +0 -42
- package/cjs/string.js +0 -93
- package/cjs/string.js.flow +0 -81
- package/cjs/tuple.js +0 -199
- package/cjs/tuple.js.flow +0 -221
- package/cjs/types.js +0 -3
- package/cjs/types.js.flow +0 -26
- package/cjs/utils.js +0 -70
- package/cjs/utils.js.flow +0 -58
- package/es/index.js +0 -1039
- package/ts/array.d.ts +0 -5
- package/ts/constants.d.ts +0 -11
- package/ts/describe.d.ts +0 -3
- package/ts/dispatch.d.ts +0 -8
- package/ts/either.d.ts +0 -61
- package/ts/fail.d.ts +0 -3
- package/ts/guard.d.ts +0 -7
- package/ts/index.d.ts +0 -38
- package/ts/instanceOf.d.ts +0 -3
- package/ts/lazy.d.ts +0 -3
- package/ts/mapping.d.ts +0 -4
- package/ts/optional.d.ts +0 -5
- package/ts/string.d.ts +0 -7
- package/ts/tuple.d.ts +0 -30
- package/ts/types.d.ts +0 -18
- package/ts/utils.d.ts +0 -13
package/lib/json.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.jsonObject = exports.jsonArray = exports.json = void 0;
|
|
5
|
+
|
|
6
|
+
var _arrays = require("./arrays");
|
|
7
|
+
|
|
8
|
+
var _booleans = require("./booleans");
|
|
9
|
+
|
|
10
|
+
var _objects = require("./objects");
|
|
11
|
+
|
|
12
|
+
var _unions = require("./unions");
|
|
13
|
+
|
|
14
|
+
var _utilities = require("./utilities");
|
|
15
|
+
|
|
16
|
+
var _basics = require("./basics");
|
|
17
|
+
|
|
18
|
+
var _numbers = require("./numbers");
|
|
19
|
+
|
|
20
|
+
var _strings = require("./strings");
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Like `json`, but will only decode when the JSON value is an object.
|
|
24
|
+
*/
|
|
25
|
+
var jsonObject = (0, _utilities.lazy)(function () {
|
|
26
|
+
return (0, _objects.dict)(json);
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* Like `json`, but will only decode when the JSON value is an array.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
exports.jsonObject = jsonObject;
|
|
33
|
+
var jsonArray = (0, _utilities.lazy)(function () {
|
|
34
|
+
return (0, _arrays.array)(json);
|
|
35
|
+
});
|
|
36
|
+
/**
|
|
37
|
+
* Accepts any value that's a valid JSON value.
|
|
38
|
+
*
|
|
39
|
+
* In other words: any value returned by `JSON.parse()` should decode without
|
|
40
|
+
* failure.
|
|
41
|
+
*
|
|
42
|
+
* ```typescript
|
|
43
|
+
* type JSONValue =
|
|
44
|
+
* | null
|
|
45
|
+
* | string
|
|
46
|
+
* | number
|
|
47
|
+
* | boolean
|
|
48
|
+
* | { [string]: JSONValue }
|
|
49
|
+
* | JSONValue[]
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
exports.jsonArray = jsonArray;
|
|
54
|
+
var json = (0, _unions.either)(_basics.null_, _strings.string, _numbers.number, _booleans["boolean"], jsonObject, jsonArray).describe('Must be valid JSON value');
|
|
55
|
+
exports.json = json;
|
package/lib/json.js.flow
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// @flow strict
|
|
2
|
+
|
|
3
|
+
import { array } from './arrays';
|
|
4
|
+
import { boolean } from './booleans';
|
|
5
|
+
import { dict } from './objects';
|
|
6
|
+
import { either } from './unions';
|
|
7
|
+
import { lazy } from './utilities';
|
|
8
|
+
import { null_ } from './basics';
|
|
9
|
+
import { number } from './numbers';
|
|
10
|
+
import { string } from './strings';
|
|
11
|
+
import type { Decoder } from '../Decoder';
|
|
12
|
+
|
|
13
|
+
export type JSONValue = null | string | number | boolean | JSONObject | JSONArray;
|
|
14
|
+
export type JSONObject = { [string]: JSONValue };
|
|
15
|
+
export type JSONArray = Array<JSONValue>;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Like `json`, but will only decode when the JSON value is an object.
|
|
19
|
+
*/
|
|
20
|
+
export const jsonObject: Decoder<JSONObject> = lazy(() => dict(json));
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Like `json`, but will only decode when the JSON value is an array.
|
|
24
|
+
*/
|
|
25
|
+
export const jsonArray: Decoder<JSONArray> = lazy(() => array(json));
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Accepts any value that's a valid JSON value.
|
|
29
|
+
*
|
|
30
|
+
* In other words: any value returned by `JSON.parse()` should decode without
|
|
31
|
+
* failure.
|
|
32
|
+
*
|
|
33
|
+
* ```typescript
|
|
34
|
+
* type JSONValue =
|
|
35
|
+
* | null
|
|
36
|
+
* | string
|
|
37
|
+
* | number
|
|
38
|
+
* | boolean
|
|
39
|
+
* | { [string]: JSONValue }
|
|
40
|
+
* | JSONValue[]
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export const json: Decoder<JSONValue> = either(
|
|
44
|
+
null_,
|
|
45
|
+
string,
|
|
46
|
+
number,
|
|
47
|
+
boolean,
|
|
48
|
+
jsonObject,
|
|
49
|
+
jsonArray,
|
|
50
|
+
).describe('Must be valid JSON value');
|
package/lib/json.mjs
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
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
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Like `json`, but will only decode when the JSON value is an object.
|
|
12
|
+
*/
|
|
13
|
+
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
|
+
*/
|
|
19
|
+
|
|
20
|
+
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
|
+
*/
|
|
39
|
+
|
|
40
|
+
export var json = either(null_, string, number, _boolean, jsonObject, jsonArray).describe('Must be valid JSON value');
|
package/lib/numbers.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
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 want
|
|
12
|
+
* to deliberately accept those, you'll likely want to use the `number` decoder
|
|
13
|
+
* 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
|
|
20
|
+
* `NaN`,
|
|
21
|
+
* or positive and negative `Infinity` will get rejected.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
exports.anyNumber = anyNumber;
|
|
25
|
+
var number = anyNumber.refine(function (n) {
|
|
26
|
+
return Number.isFinite(n);
|
|
27
|
+
}, 'Number must be finite');
|
|
28
|
+
/**
|
|
29
|
+
* Accepts only finite whole numbers.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
exports.number = number;
|
|
33
|
+
var integer = number.refine(function (n) {
|
|
34
|
+
return Number.isInteger(n);
|
|
35
|
+
}, 'Number must be an integer');
|
|
36
|
+
/**
|
|
37
|
+
* Accepts only positive finite numbers.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
exports.integer = integer;
|
|
41
|
+
var positiveNumber = number.refine(function (n) {
|
|
42
|
+
return n >= 0;
|
|
43
|
+
}, 'Number must be positive');
|
|
44
|
+
/**
|
|
45
|
+
* Accepts only positive finite whole numbers.
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
exports.positiveNumber = positiveNumber;
|
|
49
|
+
var positiveInteger = integer.refine(function (n) {
|
|
50
|
+
return n >= 0;
|
|
51
|
+
}, 'Number must be positive');
|
|
52
|
+
exports.positiveInteger = positiveInteger;
|
|
@@ -0,0 +1,49 @@
|
|
|
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 want
|
|
10
|
+
* to deliberately accept those, you'll likely want to use the `number` decoder
|
|
11
|
+
* 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
|
|
19
|
+
* `NaN`,
|
|
20
|
+
* or positive and negative `Infinity` will get rejected.
|
|
21
|
+
*/
|
|
22
|
+
export const number: Decoder<number> = anyNumber.refine(
|
|
23
|
+
(n) => Number.isFinite(n),
|
|
24
|
+
'Number must be finite',
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Accepts only finite whole numbers.
|
|
29
|
+
*/
|
|
30
|
+
export const integer: Decoder<number> = number.refine(
|
|
31
|
+
(n) => Number.isInteger(n),
|
|
32
|
+
'Number must be an integer',
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Accepts only positive finite numbers.
|
|
37
|
+
*/
|
|
38
|
+
export const positiveNumber: Decoder<number> = number.refine(
|
|
39
|
+
(n) => n >= 0,
|
|
40
|
+
'Number must be positive',
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Accepts only positive finite whole numbers.
|
|
45
|
+
*/
|
|
46
|
+
export const positiveInteger: Decoder<number> = integer.refine(
|
|
47
|
+
(n) => n >= 0,
|
|
48
|
+
'Number must be positive',
|
|
49
|
+
);
|
package/lib/numbers.mjs
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
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 want
|
|
7
|
+
* to deliberately accept those, you'll likely want to use the `number` decoder
|
|
8
|
+
* 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
|
|
15
|
+
* `NaN`,
|
|
16
|
+
* or positive and negative `Infinity` will get rejected.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export var number = anyNumber.refine(function (n) {
|
|
20
|
+
return Number.isFinite(n);
|
|
21
|
+
}, 'Number must be finite');
|
|
22
|
+
/**
|
|
23
|
+
* Accepts only finite whole numbers.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
export var integer = number.refine(function (n) {
|
|
27
|
+
return Number.isInteger(n);
|
|
28
|
+
}, 'Number must be an integer');
|
|
29
|
+
/**
|
|
30
|
+
* Accepts only positive finite numbers.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
export var positiveNumber = number.refine(function (n) {
|
|
34
|
+
return n >= 0;
|
|
35
|
+
}, 'Number must be positive');
|
|
36
|
+
/**
|
|
37
|
+
* Accepts only positive finite whole numbers.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
export var positiveInteger = integer.refine(function (n) {
|
|
41
|
+
return n >= 0;
|
|
42
|
+
}, 'Number must be positive');
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/// <reference lib="es6" />
|
|
2
|
+
|
|
3
|
+
import { Decoder, DecoderType } from '../Decoder';
|
|
4
|
+
import { AllowImplicit } from './_helpers';
|
|
3
5
|
|
|
4
6
|
export type ObjectDecoderType<T> = AllowImplicit<{
|
|
5
|
-
[key in keyof T]:
|
|
7
|
+
[key in keyof T]: DecoderType<T[key]>;
|
|
6
8
|
}>;
|
|
7
9
|
|
|
8
10
|
export const pojo: Decoder<{ [key: string]: unknown }>;
|
|
9
11
|
|
|
10
12
|
export function object<O extends { [key: string]: Decoder<any> }>(
|
|
11
|
-
|
|
13
|
+
decodersByKey: O,
|
|
12
14
|
): Decoder<{ [K in keyof ObjectDecoderType<O>]: ObjectDecoderType<O>[K] }>;
|
|
13
15
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
14
16
|
// This is basically just equivalent to:
|
|
@@ -19,15 +21,18 @@ export function object<O extends { [key: string]: Decoder<any> }>(
|
|
|
19
21
|
// work with while developing.
|
|
20
22
|
|
|
21
23
|
export function exact<O extends { [key: string]: Decoder<any> }>(
|
|
22
|
-
|
|
24
|
+
decodersByKey: O,
|
|
23
25
|
): Decoder<{ [K in keyof ObjectDecoderType<O>]: ObjectDecoderType<O>[K] }>;
|
|
24
26
|
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
25
27
|
// Ditto (see above)
|
|
26
28
|
|
|
27
29
|
export function inexact<O extends { [key: string]: Decoder<any> }>(
|
|
28
|
-
|
|
30
|
+
decodersByKey: O,
|
|
29
31
|
): Decoder<
|
|
30
32
|
{ [K in keyof ObjectDecoderType<O>]: ObjectDecoderType<O>[K] } & {
|
|
31
33
|
[extra: string]: unknown;
|
|
32
34
|
}
|
|
33
35
|
>;
|
|
36
|
+
|
|
37
|
+
export function mapping<T>(decoder: Decoder<T>): Decoder<Map<string, T>>;
|
|
38
|
+
export function dict<T>(decoder: Decoder<T>): Decoder<{ [key: string]: T }>;
|
package/lib/objects.js
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"use strict";
|
|
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;
|
|
10
|
+
|
|
11
|
+
var _annotate = require("../annotate");
|
|
12
|
+
|
|
13
|
+
var _Decoder = require("../Decoder");
|
|
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
|
+
*/
|
|
30
|
+
|
|
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:
|
|
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;
|
|
58
|
+
|
|
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
|
|
65
|
+
// remove the key from this missing set if the decoder accepts the
|
|
66
|
+
// value.
|
|
67
|
+
|
|
68
|
+
var missingKeys = subtract(knownKeys, actualKeys);
|
|
69
|
+
var record = {};
|
|
70
|
+
var errors = null;
|
|
71
|
+
Object.keys(decodersByKey).forEach(function (key) {
|
|
72
|
+
var decoder = decodersByKey[key];
|
|
73
|
+
var rawValue = plainObj[key];
|
|
74
|
+
var result = decoder.decode(rawValue);
|
|
75
|
+
|
|
76
|
+
if (result.ok) {
|
|
77
|
+
var value = result.value;
|
|
78
|
+
|
|
79
|
+
if (value !== undefined) {
|
|
80
|
+
record[key] = value;
|
|
81
|
+
} // If this succeeded, remove the key from the missing keys
|
|
82
|
+
// tracker
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
missingKeys["delete"](key);
|
|
86
|
+
} else {
|
|
87
|
+
var ann = result.error; // Keep track of the annotation, but don't return just yet. We
|
|
88
|
+
// want to collect more error information.
|
|
89
|
+
|
|
90
|
+
if (rawValue === undefined) {
|
|
91
|
+
// Explicitly add it to the missing set if the value is
|
|
92
|
+
// undefined. This covers explicit undefineds to be
|
|
93
|
+
// treated the same as implicit undefineds (aka missing
|
|
94
|
+
// keys).
|
|
95
|
+
missingKeys.add(key);
|
|
96
|
+
} else {
|
|
97
|
+
if (errors === null) {
|
|
98
|
+
errors = {};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
errors[key] = ann;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}); // Deal with errors now. There are two classes of errors we want to
|
|
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.
|
|
108
|
+
|
|
109
|
+
if (errors || missingKeys.size > 0) {
|
|
110
|
+
var objAnn = (0, _annotate.annotateObject)(plainObj);
|
|
111
|
+
|
|
112
|
+
if (errors) {
|
|
113
|
+
objAnn = (0, _annotate.merge)(objAnn, errors);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (missingKeys.size > 0) {
|
|
117
|
+
var errMsg = Array.from(missingKeys).map(function (key) {
|
|
118
|
+
return "\"" + key + "\"";
|
|
119
|
+
}).join(', ');
|
|
120
|
+
var pluralized = missingKeys.size > 1 ? 'keys' : 'key';
|
|
121
|
+
objAnn = (0, _annotate.updateText)(objAnn, "Missing " + pluralized + ": " + errMsg);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return err(objAnn);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return ok(record);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Like `object()`, but will reject inputs that contain extra fields that are
|
|
132
|
+
* not specified explicitly.
|
|
133
|
+
*/
|
|
134
|
+
|
|
135
|
+
|
|
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
|
|
139
|
+
|
|
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;
|
|
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
|
+
*/
|
|
155
|
+
|
|
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
|
|
162
|
+
|
|
163
|
+
safekeys.forEach(function (k) {
|
|
164
|
+
return allkeys.add(k);
|
|
165
|
+
});
|
|
166
|
+
var rv = {};
|
|
167
|
+
allkeys.forEach(function (k) {
|
|
168
|
+
if (safekeys.has(k)) {
|
|
169
|
+
var value = safepart[k];
|
|
170
|
+
|
|
171
|
+
if (value !== undefined) {
|
|
172
|
+
rv[k] = value;
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
rv[k] = plainObj[k];
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
return rv;
|
|
179
|
+
});
|
|
180
|
+
return decoder.decode(plainObj);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
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
|
+
|
|
195
|
+
function dict(decoder) {
|
|
196
|
+
return pojo.then(function (plainObj, ok, err) {
|
|
197
|
+
var rv = {};
|
|
198
|
+
var errors = null;
|
|
199
|
+
Object.keys(plainObj).forEach(function (key) {
|
|
200
|
+
var value = plainObj[key];
|
|
201
|
+
var result = decoder.decode(value);
|
|
202
|
+
|
|
203
|
+
if (result.ok) {
|
|
204
|
+
if (errors === null) {
|
|
205
|
+
rv[key] = result.value;
|
|
206
|
+
}
|
|
207
|
+
} else {
|
|
208
|
+
rv = {}; // Clear the success value so it can get garbage collected early
|
|
209
|
+
|
|
210
|
+
if (errors === null) {
|
|
211
|
+
errors = {};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
errors[key] = result.error;
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
if (errors !== null) {
|
|
219
|
+
return err((0, _annotate.merge)((0, _annotate.annotateObject)(plainObj), errors));
|
|
220
|
+
} else {
|
|
221
|
+
return ok(rv);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
}
|
|
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
|
+
|
|
232
|
+
function mapping(decoder) {
|
|
233
|
+
return dict(decoder).transform(function (obj) {
|
|
234
|
+
return new Map( // This is effectively Object.entries(obj), but in a way that Flow
|
|
235
|
+
// will know the types are okay
|
|
236
|
+
Object.keys(obj).map(function (key) {
|
|
237
|
+
return [key, obj[key]];
|
|
238
|
+
}));
|
|
239
|
+
});
|
|
240
|
+
}
|