decoders 2.0.0-beta7 → 2.0.0-beta8
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 +10 -0
- package/README.md +728 -343
- package/core/array.js +2 -2
- package/core/array.js.flow +1 -4
- package/core/array.mjs +2 -2
- package/core/composition.d.ts +6 -4
- package/core/composition.js +5 -3
- package/core/composition.js.flow +9 -4
- package/core/composition.mjs +5 -3
- package/core/dispatch.d.ts +3 -3
- package/core/dispatch.js +7 -7
- package/core/dispatch.js.flow +6 -6
- package/core/dispatch.mjs +6 -6
- package/core/mapping.js +20 -16
- package/core/mapping.js.flow +23 -15
- package/core/mapping.mjs +20 -16
- package/core/number.js +8 -8
- package/core/number.js.flow +15 -9
- package/core/number.mjs +7 -7
- package/core/string.d.ts +4 -1
- package/core/string.js +17 -43
- package/core/string.js.flow +14 -35
- package/core/string.mjs +12 -39
- package/core/tuple.js +2 -2
- package/core/tuple.js.flow +1 -4
- package/core/tuple.mjs +2 -2
- package/index.d.ts +2 -2
- package/index.js +3 -2
- package/index.js.flow +2 -2
- package/index.mjs +2 -2
- package/package.json +6 -6
package/core/array.js
CHANGED
|
@@ -98,7 +98,7 @@ function array(decoder) {
|
|
|
98
98
|
|
|
99
99
|
|
|
100
100
|
function nonEmptyArray(decoder) {
|
|
101
|
-
return (0, _composition.
|
|
101
|
+
return (0, _composition.predicate)(array(decoder), function (arr) {
|
|
102
102
|
return arr.length > 0;
|
|
103
|
-
}, 'Must be non-empty array')
|
|
103
|
+
}, 'Must be non-empty array');
|
|
104
104
|
}
|
package/core/array.js.flow
CHANGED
|
@@ -96,8 +96,5 @@ export function array<T>(decoder: Decoder<T>): Decoder<Array<T>> {
|
|
|
96
96
|
* empty arrays.
|
|
97
97
|
*/
|
|
98
98
|
export function nonEmptyArray<T>(decoder: Decoder<T>): Decoder<Array<T>> {
|
|
99
|
-
return
|
|
100
|
-
array(decoder),
|
|
101
|
-
predicate((arr) => arr.length > 0, 'Must be non-empty array'),
|
|
102
|
-
);
|
|
99
|
+
return predicate(array(decoder), (arr) => arr.length > 0, 'Must be non-empty array');
|
|
103
100
|
}
|
package/core/array.mjs
CHANGED
|
@@ -85,7 +85,7 @@ export function array(decoder) {
|
|
|
85
85
|
*/
|
|
86
86
|
|
|
87
87
|
export function nonEmptyArray(decoder) {
|
|
88
|
-
return
|
|
88
|
+
return predicate(array(decoder), function (arr) {
|
|
89
89
|
return arr.length > 0;
|
|
90
|
-
}, 'Must be non-empty array')
|
|
90
|
+
}, 'Must be non-empty array');
|
|
91
91
|
}
|
package/core/composition.d.ts
CHANGED
|
@@ -2,11 +2,13 @@ import { Decoder } from '../_types';
|
|
|
2
2
|
|
|
3
3
|
export function map<T, V>(decoder: Decoder<T>, mapper: (value: T) => V): Decoder<V>;
|
|
4
4
|
export function compose<T, V>(decoder: Decoder<T>, next: Decoder<V, T>): Decoder<V>;
|
|
5
|
-
export function predicate<T
|
|
6
|
-
|
|
5
|
+
export function predicate<T, N extends T>(
|
|
6
|
+
decoder: Decoder<T>,
|
|
7
|
+
predicate: (value: T) => value is N,
|
|
7
8
|
msg: string,
|
|
8
|
-
): Decoder<
|
|
9
|
+
): Decoder<N>;
|
|
9
10
|
export function predicate<T>(
|
|
11
|
+
decoder: Decoder<T>,
|
|
10
12
|
predicate: (value: T) => boolean,
|
|
11
13
|
msg: string,
|
|
12
|
-
): Decoder<T
|
|
14
|
+
): Decoder<T>;
|
package/core/composition.js
CHANGED
|
@@ -45,8 +45,10 @@ function compose(decoder, next) {
|
|
|
45
45
|
*/
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
function predicate(predicateFn, msg) {
|
|
49
|
-
return function (
|
|
50
|
-
return
|
|
48
|
+
function predicate(decoder, predicateFn, msg) {
|
|
49
|
+
return function (blob) {
|
|
50
|
+
return (0, _result.andThen)(decoder(blob), function (value) {
|
|
51
|
+
return predicateFn(value) ? (0, _result.ok)(value) : (0, _result.err)((0, _annotate.annotate)(value, msg));
|
|
52
|
+
});
|
|
51
53
|
};
|
|
52
54
|
}
|
package/core/composition.js.flow
CHANGED
|
@@ -36,8 +36,13 @@ export function compose<T, V>(decoder: Decoder<T>, next: Decoder<V, T>): Decoder
|
|
|
36
36
|
* Factory function returning a Decoder<T>, given a predicate function that
|
|
37
37
|
* accepts/rejects the input of type T.
|
|
38
38
|
*/
|
|
39
|
-
export function predicate<T>(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
export function predicate<T>(
|
|
40
|
+
decoder: Decoder<T>,
|
|
41
|
+
predicateFn: (T) => boolean,
|
|
42
|
+
msg: string,
|
|
43
|
+
): Decoder<T> {
|
|
44
|
+
return (blob: mixed) =>
|
|
45
|
+
andThen(decoder(blob), (value) =>
|
|
46
|
+
predicateFn(value) ? ok(value) : err(annotate(value, msg)),
|
|
47
|
+
);
|
|
43
48
|
}
|
package/core/composition.mjs
CHANGED
|
@@ -35,8 +35,10 @@ export function compose(decoder, next) {
|
|
|
35
35
|
* accepts/rejects the input of type T.
|
|
36
36
|
*/
|
|
37
37
|
|
|
38
|
-
export function predicate(predicateFn, msg) {
|
|
39
|
-
return function (
|
|
40
|
-
return
|
|
38
|
+
export function predicate(decoder, predicateFn, msg) {
|
|
39
|
+
return function (blob) {
|
|
40
|
+
return andThen(decoder(blob), function (value) {
|
|
41
|
+
return predicateFn(value) ? ok(value) : err(annotate(value, msg));
|
|
42
|
+
});
|
|
41
43
|
};
|
|
42
44
|
}
|
package/core/dispatch.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Decoder, DecoderType } from '../_types';
|
|
2
2
|
|
|
3
|
-
export type
|
|
3
|
+
export type Values<T extends object> = T[keyof T];
|
|
4
4
|
|
|
5
|
-
export function
|
|
5
|
+
export function disjointUnion<O extends { [key: string]: Decoder<any> }>(
|
|
6
6
|
field: string,
|
|
7
7
|
mapping: O,
|
|
8
|
-
): Decoder
|
|
8
|
+
): Decoder<Values<{ [key in keyof O]: DecoderType<O[key]> }>>;
|
package/core/dispatch.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
exports.__esModule = true;
|
|
4
|
-
exports.
|
|
4
|
+
exports.disjointUnion = disjointUnion;
|
|
5
5
|
|
|
6
6
|
var _result = require("../result");
|
|
7
7
|
|
|
@@ -31,20 +31,20 @@ var _either = require("./either");
|
|
|
31
31
|
* Then these two decoders are equivalent:
|
|
32
32
|
*
|
|
33
33
|
* const shape = either(rectangle, circle)
|
|
34
|
-
* const shape =
|
|
34
|
+
* const shape = disjointUnion('type', { rectangle, circle })
|
|
35
35
|
*
|
|
36
36
|
* Will be of type Decoder<Rectangle | Circle>.
|
|
37
37
|
*
|
|
38
|
-
* But
|
|
39
|
-
*
|
|
40
|
-
*
|
|
38
|
+
* But `disjointUnion` will typically be more runtime-efficient. The reason is
|
|
39
|
+
* that it will first do minimal work to "look ahead" into the `type` field
|
|
40
|
+
* here, and based on that value, pick the decoder to invoke.
|
|
41
41
|
*
|
|
42
42
|
* The `either` version will simply try to invoke each decoder, until it finds
|
|
43
43
|
* one that matches.
|
|
44
44
|
*
|
|
45
|
-
* Also, the error messages will be less ambiguous using `
|
|
45
|
+
* Also, the error messages will be less ambiguous using `disjointUnion()`.
|
|
46
46
|
*/
|
|
47
|
-
function
|
|
47
|
+
function disjointUnion(field, mapping) {
|
|
48
48
|
var _object;
|
|
49
49
|
|
|
50
50
|
var base = (0, _object2.object)((_object = {}, _object[field] = (0, _either.oneOf)(Object.keys(mapping)), _object));
|
package/core/dispatch.js.flow
CHANGED
|
@@ -30,20 +30,20 @@ type anything = any;
|
|
|
30
30
|
* Then these two decoders are equivalent:
|
|
31
31
|
*
|
|
32
32
|
* const shape = either(rectangle, circle)
|
|
33
|
-
* const shape =
|
|
33
|
+
* const shape = disjointUnion('type', { rectangle, circle })
|
|
34
34
|
*
|
|
35
35
|
* Will be of type Decoder<Rectangle | Circle>.
|
|
36
36
|
*
|
|
37
|
-
* But
|
|
38
|
-
*
|
|
39
|
-
*
|
|
37
|
+
* But `disjointUnion` will typically be more runtime-efficient. The reason is
|
|
38
|
+
* that it will first do minimal work to "look ahead" into the `type` field
|
|
39
|
+
* here, and based on that value, pick the decoder to invoke.
|
|
40
40
|
*
|
|
41
41
|
* The `either` version will simply try to invoke each decoder, until it finds
|
|
42
42
|
* one that matches.
|
|
43
43
|
*
|
|
44
|
-
* Also, the error messages will be less ambiguous using `
|
|
44
|
+
* Also, the error messages will be less ambiguous using `disjointUnion()`.
|
|
45
45
|
*/
|
|
46
|
-
export function
|
|
46
|
+
export function disjointUnion<O: { +[field: string]: Decoder<anything>, ... }>(
|
|
47
47
|
field: string,
|
|
48
48
|
mapping: O,
|
|
49
49
|
): Decoder<$Values<$ObjMap<O, DecoderType>>> {
|
package/core/dispatch.mjs
CHANGED
|
@@ -24,20 +24,20 @@ import { oneOf } from './either.mjs';
|
|
|
24
24
|
* Then these two decoders are equivalent:
|
|
25
25
|
*
|
|
26
26
|
* const shape = either(rectangle, circle)
|
|
27
|
-
* const shape =
|
|
27
|
+
* const shape = disjointUnion('type', { rectangle, circle })
|
|
28
28
|
*
|
|
29
29
|
* Will be of type Decoder<Rectangle | Circle>.
|
|
30
30
|
*
|
|
31
|
-
* But
|
|
32
|
-
*
|
|
33
|
-
*
|
|
31
|
+
* But `disjointUnion` will typically be more runtime-efficient. The reason is
|
|
32
|
+
* that it will first do minimal work to "look ahead" into the `type` field
|
|
33
|
+
* here, and based on that value, pick the decoder to invoke.
|
|
34
34
|
*
|
|
35
35
|
* The `either` version will simply try to invoke each decoder, until it finds
|
|
36
36
|
* one that matches.
|
|
37
37
|
*
|
|
38
|
-
* Also, the error messages will be less ambiguous using `
|
|
38
|
+
* Also, the error messages will be less ambiguous using `disjointUnion()`.
|
|
39
39
|
*/
|
|
40
|
-
export function
|
|
40
|
+
export function disjointUnion(field, mapping) {
|
|
41
41
|
var _object;
|
|
42
42
|
|
|
43
43
|
var base = object((_object = {}, _object[field] = oneOf(Object.keys(mapping)), _object));
|
package/core/mapping.js
CHANGED
|
@@ -13,17 +13,11 @@ var _result = require("../result");
|
|
|
13
13
|
var _object = require("./object");
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* For example, given a decoder for a Person, we can verify a Person lookup
|
|
19
|
-
* table structure (of type Map<string, Person>) like so:
|
|
20
|
-
*
|
|
21
|
-
* mapping(person)
|
|
22
|
-
*
|
|
16
|
+
* Like mapping(), but returns an object rather than a Map instance.
|
|
23
17
|
*/
|
|
24
|
-
function
|
|
18
|
+
function dict(decoder) {
|
|
25
19
|
return (0, _composition.compose)(_object.pojo, function (blob) {
|
|
26
|
-
var
|
|
20
|
+
var rv = {};
|
|
27
21
|
var errors = null;
|
|
28
22
|
Object.keys(blob).forEach(function (key) {
|
|
29
23
|
var value = blob[key];
|
|
@@ -31,10 +25,10 @@ function mapping(decoder) {
|
|
|
31
25
|
|
|
32
26
|
if (result.type === 'ok') {
|
|
33
27
|
if (errors === null) {
|
|
34
|
-
|
|
28
|
+
rv[key] = result.value;
|
|
35
29
|
}
|
|
36
30
|
} else {
|
|
37
|
-
|
|
31
|
+
rv = {}; // Clear the success value so it can get garbage collected early
|
|
38
32
|
|
|
39
33
|
if (errors === null) {
|
|
40
34
|
errors = {};
|
|
@@ -47,17 +41,27 @@ function mapping(decoder) {
|
|
|
47
41
|
if (errors !== null) {
|
|
48
42
|
return (0, _result.err)((0, _annotate.merge)((0, _annotate.annotateObject)(blob), errors));
|
|
49
43
|
} else {
|
|
50
|
-
return (0, _result.ok)(
|
|
44
|
+
return (0, _result.ok)(rv);
|
|
51
45
|
}
|
|
52
46
|
});
|
|
53
47
|
}
|
|
54
48
|
/**
|
|
55
|
-
*
|
|
49
|
+
* Given an object, will decode a Map of string keys to whatever values.
|
|
50
|
+
*
|
|
51
|
+
* For example, given a decoder for a Person, we can verify a Person lookup
|
|
52
|
+
* table structure (of type Map<string, Person>) like so:
|
|
53
|
+
*
|
|
54
|
+
* mapping(person)
|
|
55
|
+
*
|
|
56
56
|
*/
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
function
|
|
60
|
-
return (0, _composition.map)(
|
|
61
|
-
return Object.
|
|
59
|
+
function mapping(decoder) {
|
|
60
|
+
return (0, _composition.map)(dict(decoder), function (obj) {
|
|
61
|
+
return new Map( // This is effectively Object.entries(obj), but in a way that Flow
|
|
62
|
+
// will know the types are okay
|
|
63
|
+
Object.keys(obj).map(function (key) {
|
|
64
|
+
return [key, obj[key]];
|
|
65
|
+
}));
|
|
62
66
|
});
|
|
63
67
|
}
|
package/core/mapping.js.flow
CHANGED
|
@@ -9,17 +9,11 @@ import type { Annotation } from '../annotate';
|
|
|
9
9
|
import type { Decoder } from '../_types';
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* For example, given a decoder for a Person, we can verify a Person lookup
|
|
15
|
-
* table structure (of type Map<string, Person>) like so:
|
|
16
|
-
*
|
|
17
|
-
* mapping(person)
|
|
18
|
-
*
|
|
12
|
+
* Like mapping(), but returns an object rather than a Map instance.
|
|
19
13
|
*/
|
|
20
|
-
export function
|
|
14
|
+
export function dict<T>(decoder: Decoder<T>): Decoder<{ [string]: T }> {
|
|
21
15
|
return compose(pojo, (blob: { +[key: string]: mixed }) => {
|
|
22
|
-
let
|
|
16
|
+
let rv: { [key: string]: T } = {};
|
|
23
17
|
let errors: { [key: string]: Annotation } | null = null;
|
|
24
18
|
|
|
25
19
|
Object.keys(blob).forEach((key: string) => {
|
|
@@ -27,10 +21,10 @@ export function mapping<T>(decoder: Decoder<T>): Decoder<Map<string, T>> {
|
|
|
27
21
|
const result = decoder(value);
|
|
28
22
|
if (result.type === 'ok') {
|
|
29
23
|
if (errors === null) {
|
|
30
|
-
|
|
24
|
+
rv[key] = result.value;
|
|
31
25
|
}
|
|
32
26
|
} else {
|
|
33
|
-
|
|
27
|
+
rv = {}; // Clear the success value so it can get garbage collected early
|
|
34
28
|
if (errors === null) {
|
|
35
29
|
errors = {};
|
|
36
30
|
}
|
|
@@ -41,14 +35,28 @@ export function mapping<T>(decoder: Decoder<T>): Decoder<Map<string, T>> {
|
|
|
41
35
|
if (errors !== null) {
|
|
42
36
|
return err(merge(annotateObject(blob), errors));
|
|
43
37
|
} else {
|
|
44
|
-
return ok(
|
|
38
|
+
return ok(rv);
|
|
45
39
|
}
|
|
46
40
|
});
|
|
47
41
|
}
|
|
48
42
|
|
|
49
43
|
/**
|
|
50
|
-
*
|
|
44
|
+
* Given an object, will decode a Map of string keys to whatever values.
|
|
45
|
+
*
|
|
46
|
+
* For example, given a decoder for a Person, we can verify a Person lookup
|
|
47
|
+
* table structure (of type Map<string, Person>) like so:
|
|
48
|
+
*
|
|
49
|
+
* mapping(person)
|
|
50
|
+
*
|
|
51
51
|
*/
|
|
52
|
-
export function
|
|
53
|
-
return map(
|
|
52
|
+
export function mapping<T>(decoder: Decoder<T>): Decoder<Map<string, T>> {
|
|
53
|
+
return map(
|
|
54
|
+
dict(decoder),
|
|
55
|
+
(obj) =>
|
|
56
|
+
new Map(
|
|
57
|
+
// This is effectively Object.entries(obj), but in a way that Flow
|
|
58
|
+
// will know the types are okay
|
|
59
|
+
Object.keys(obj).map((key) => [key, obj[key]]),
|
|
60
|
+
),
|
|
61
|
+
);
|
|
54
62
|
}
|
package/core/mapping.mjs
CHANGED
|
@@ -5,17 +5,11 @@ import { merge } from '../annotate.mjs';
|
|
|
5
5
|
import { pojo } from './object.mjs';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* For example, given a decoder for a Person, we can verify a Person lookup
|
|
11
|
-
* table structure (of type Map<string, Person>) like so:
|
|
12
|
-
*
|
|
13
|
-
* mapping(person)
|
|
14
|
-
*
|
|
8
|
+
* Like mapping(), but returns an object rather than a Map instance.
|
|
15
9
|
*/
|
|
16
|
-
export function
|
|
10
|
+
export function dict(decoder) {
|
|
17
11
|
return compose(pojo, function (blob) {
|
|
18
|
-
var
|
|
12
|
+
var rv = {};
|
|
19
13
|
var errors = null;
|
|
20
14
|
Object.keys(blob).forEach(function (key) {
|
|
21
15
|
var value = blob[key];
|
|
@@ -23,10 +17,10 @@ export function mapping(decoder) {
|
|
|
23
17
|
|
|
24
18
|
if (result.type === 'ok') {
|
|
25
19
|
if (errors === null) {
|
|
26
|
-
|
|
20
|
+
rv[key] = result.value;
|
|
27
21
|
}
|
|
28
22
|
} else {
|
|
29
|
-
|
|
23
|
+
rv = {}; // Clear the success value so it can get garbage collected early
|
|
30
24
|
|
|
31
25
|
if (errors === null) {
|
|
32
26
|
errors = {};
|
|
@@ -39,16 +33,26 @@ export function mapping(decoder) {
|
|
|
39
33
|
if (errors !== null) {
|
|
40
34
|
return err(merge(annotateObject(blob), errors));
|
|
41
35
|
} else {
|
|
42
|
-
return ok(
|
|
36
|
+
return ok(rv);
|
|
43
37
|
}
|
|
44
38
|
});
|
|
45
39
|
}
|
|
46
40
|
/**
|
|
47
|
-
*
|
|
41
|
+
* Given an object, will decode a Map of string keys to whatever values.
|
|
42
|
+
*
|
|
43
|
+
* For example, given a decoder for a Person, we can verify a Person lookup
|
|
44
|
+
* table structure (of type Map<string, Person>) like so:
|
|
45
|
+
*
|
|
46
|
+
* mapping(person)
|
|
47
|
+
*
|
|
48
48
|
*/
|
|
49
49
|
|
|
50
|
-
export function
|
|
51
|
-
return map(
|
|
52
|
-
return Object.
|
|
50
|
+
export function mapping(decoder) {
|
|
51
|
+
return map(dict(decoder), function (obj) {
|
|
52
|
+
return new Map( // This is effectively Object.entries(obj), but in a way that Flow
|
|
53
|
+
// will know the types are okay
|
|
54
|
+
Object.keys(obj).map(function (key) {
|
|
55
|
+
return [key, obj[key]];
|
|
56
|
+
}));
|
|
53
57
|
});
|
|
54
58
|
}
|
package/core/number.js
CHANGED
|
@@ -5,10 +5,10 @@ exports.positiveNumber = exports.positiveInteger = exports.number = exports.inte
|
|
|
5
5
|
|
|
6
6
|
var _annotate = require("../annotate");
|
|
7
7
|
|
|
8
|
-
var _composition = require("./composition");
|
|
9
|
-
|
|
10
8
|
var _result = require("../result");
|
|
11
9
|
|
|
10
|
+
var _composition = require("./composition");
|
|
11
|
+
|
|
12
12
|
var anyNumber = function anyNumber(blob) {
|
|
13
13
|
return typeof blob === 'number' && !Number.isNaN(blob) ? (0, _result.ok)(blob) : (0, _result.err)((0, _annotate.annotate)(blob, 'Must be number'));
|
|
14
14
|
};
|
|
@@ -21,16 +21,16 @@ var isFinite = function isFinite(n) {
|
|
|
21
21
|
return Number.isFinite(n);
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
-
var number = (0, _composition.
|
|
24
|
+
var number = (0, _composition.predicate)(anyNumber, isFinite, 'Number must be finite');
|
|
25
25
|
exports.number = number;
|
|
26
|
-
var positiveNumber = (0, _composition.
|
|
26
|
+
var positiveNumber = (0, _composition.predicate)(number, function (n) {
|
|
27
27
|
return n >= 0;
|
|
28
|
-
}, 'Number must be positive')
|
|
28
|
+
}, 'Number must be positive'); // Integers
|
|
29
29
|
|
|
30
30
|
exports.positiveNumber = positiveNumber;
|
|
31
|
-
var integer = (0, _composition.
|
|
31
|
+
var integer = (0, _composition.predicate)(number, isInteger, 'Number must be an integer');
|
|
32
32
|
exports.integer = integer;
|
|
33
|
-
var positiveInteger = (0, _composition.
|
|
33
|
+
var positiveInteger = (0, _composition.predicate)(integer, function (n) {
|
|
34
34
|
return n >= 0;
|
|
35
|
-
}, 'Number must be positive')
|
|
35
|
+
}, 'Number must be positive');
|
|
36
36
|
exports.positiveInteger = positiveInteger;
|
package/core/number.js.flow
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// @flow strict
|
|
2
2
|
|
|
3
3
|
import { annotate } from '../annotate';
|
|
4
|
-
import { compose, predicate } from './composition';
|
|
5
4
|
import { err, ok } from '../result';
|
|
5
|
+
import { predicate } from './composition';
|
|
6
6
|
import type { Decoder } from '../_types';
|
|
7
7
|
|
|
8
8
|
const anyNumber: Decoder<number> = (blob: mixed) => {
|
|
@@ -14,21 +14,27 @@ const anyNumber: Decoder<number> = (blob: mixed) => {
|
|
|
14
14
|
const isInteger = (n: number) => Number.isInteger(n);
|
|
15
15
|
const isFinite = (n: number) => Number.isFinite(n);
|
|
16
16
|
|
|
17
|
-
export const number: Decoder<number> =
|
|
17
|
+
export const number: Decoder<number> = predicate(
|
|
18
18
|
anyNumber,
|
|
19
|
-
|
|
19
|
+
isFinite,
|
|
20
|
+
'Number must be finite',
|
|
20
21
|
);
|
|
21
|
-
|
|
22
|
+
|
|
23
|
+
export const positiveNumber: Decoder<number> = predicate(
|
|
22
24
|
number,
|
|
23
|
-
|
|
25
|
+
(n) => n >= 0,
|
|
26
|
+
'Number must be positive',
|
|
24
27
|
);
|
|
25
28
|
|
|
26
29
|
// Integers
|
|
27
|
-
export const integer: Decoder<number> =
|
|
30
|
+
export const integer: Decoder<number> = predicate(
|
|
28
31
|
number,
|
|
29
|
-
|
|
32
|
+
isInteger,
|
|
33
|
+
'Number must be an integer',
|
|
30
34
|
);
|
|
31
|
-
|
|
35
|
+
|
|
36
|
+
export const positiveInteger: Decoder<number> = predicate(
|
|
32
37
|
integer,
|
|
33
|
-
|
|
38
|
+
(n) => n >= 0,
|
|
39
|
+
'Number must be positive',
|
|
34
40
|
);
|
package/core/number.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { annotate } from '../annotate.mjs';
|
|
2
|
-
import { compose, predicate } from './composition.mjs';
|
|
3
2
|
import { err, ok } from '../result.mjs';
|
|
3
|
+
import { predicate } from './composition.mjs';
|
|
4
4
|
|
|
5
5
|
var anyNumber = function anyNumber(blob) {
|
|
6
6
|
return typeof blob === 'number' && !Number.isNaN(blob) ? ok(blob) : err(annotate(blob, 'Must be number'));
|
|
@@ -14,12 +14,12 @@ var isFinite = function isFinite(n) {
|
|
|
14
14
|
return Number.isFinite(n);
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
export var number =
|
|
18
|
-
export var positiveNumber =
|
|
17
|
+
export var number = predicate(anyNumber, isFinite, 'Number must be finite');
|
|
18
|
+
export var positiveNumber = predicate(number, function (n) {
|
|
19
19
|
return n >= 0;
|
|
20
|
-
}, 'Number must be positive')
|
|
20
|
+
}, 'Number must be positive'); // Integers
|
|
21
21
|
|
|
22
|
-
export var integer =
|
|
23
|
-
export var positiveInteger =
|
|
22
|
+
export var integer = predicate(number, isInteger, 'Number must be an integer');
|
|
23
|
+
export var positiveInteger = predicate(integer, function (n) {
|
|
24
24
|
return n >= 0;
|
|
25
|
-
}, 'Number must be positive')
|
|
25
|
+
}, 'Number must be positive');
|
package/core/string.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
|
|
1
3
|
import { Decoder } from '../_types';
|
|
2
4
|
|
|
3
5
|
export const string: Decoder<string>;
|
|
4
6
|
export const nonEmptyString: Decoder<string>;
|
|
5
7
|
export function regex(regex: RegExp, msg: string): Decoder<string>;
|
|
6
8
|
export const email: Decoder<string>;
|
|
7
|
-
export
|
|
9
|
+
export const url: Decoder<URL>;
|
|
10
|
+
export const httpsUrl: Decoder<URL>;
|
package/core/string.js
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
exports.__esModule = true;
|
|
4
|
-
exports.nonEmptyString = exports.email = void 0;
|
|
4
|
+
exports.nonEmptyString = exports.httpsUrl = exports.email = void 0;
|
|
5
5
|
exports.regex = regex;
|
|
6
6
|
exports.url = exports.string = void 0;
|
|
7
7
|
|
|
8
8
|
var _annotate = require("../annotate");
|
|
9
9
|
|
|
10
|
-
var
|
|
10
|
+
var _either = require("./either");
|
|
11
11
|
|
|
12
12
|
var _result = require("../result");
|
|
13
13
|
|
|
14
|
+
var _instanceOf = require("./instanceOf");
|
|
15
|
+
|
|
16
|
+
var _composition = require("./composition");
|
|
17
|
+
|
|
14
18
|
/** Match groups in this regex:
|
|
15
19
|
* \1 - the scheme
|
|
16
20
|
* \2 - the username/password (optional)
|
|
@@ -18,9 +22,7 @@ var _result = require("../result");
|
|
|
18
22
|
* \4 - the port (optional)
|
|
19
23
|
* \5 - the path (optional)
|
|
20
24
|
*/
|
|
21
|
-
var url_re = /^([A-Za-z]{3,9}(?:[+][A-Za-z]{3,9})?):\/\/(?:([-;:&=+$,\w]+)@)?(?:([A-Za-z0-9.-]+)(?::([0-9]{2,5}))?)(\/(?:[-+~%/.,\w]*)?(?:\?[-+=&;%@.,\w]*)?(?:#[.,!/\w]*)?)?$/;
|
|
22
|
-
|
|
23
|
-
var DEFAULT_SCHEMES = ['https'];
|
|
25
|
+
var url_re = /^([A-Za-z]{3,9}(?:[+][A-Za-z]{3,9})?):\/\/(?:([-;:&=+$,\w]+)@)?(?:([A-Za-z0-9.-]+)(?::([0-9]{2,5}))?)(\/(?:[-+~%/.,\w]*)?(?:\?[-+=&;%@.,\w]*)?(?:#[.,!/\w]*)?)?$/;
|
|
24
26
|
/**
|
|
25
27
|
* Decoder that only returns Ok for string inputs. Err otherwise.
|
|
26
28
|
*/
|
|
@@ -44,9 +46,9 @@ var nonEmptyString = regex(/\S/, 'Must be non-empty string');
|
|
|
44
46
|
exports.nonEmptyString = nonEmptyString;
|
|
45
47
|
|
|
46
48
|
function regex(regex, msg) {
|
|
47
|
-
return (0, _composition.
|
|
49
|
+
return (0, _composition.predicate)(string, function (s) {
|
|
48
50
|
return regex.test(s);
|
|
49
|
-
}, msg)
|
|
51
|
+
}, msg);
|
|
50
52
|
}
|
|
51
53
|
/**
|
|
52
54
|
* Decoder that only returns Ok for string inputs that match the almost perfect
|
|
@@ -55,40 +57,12 @@ function regex(regex, msg) {
|
|
|
55
57
|
|
|
56
58
|
|
|
57
59
|
var email = regex(/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, 'Must be email');
|
|
58
|
-
/**
|
|
59
|
-
* Decoder that only returns Ok for string inputs that match URLs of the
|
|
60
|
-
* expected scheme. Defaults to only accept HTTPS URLs. Err otherwise.
|
|
61
|
-
*
|
|
62
|
-
* Variants that can be used:
|
|
63
|
-
*
|
|
64
|
-
* - url() accepts only https:// URLs
|
|
65
|
-
* - url([]) accepts any URL scheme
|
|
66
|
-
* - url(['http']) accepts only HTTP
|
|
67
|
-
* - url(['https', 'git+ssh']) accepts both https:// and git+ssh:// URLs
|
|
68
|
-
*/
|
|
69
|
-
|
|
70
60
|
exports.email = email;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (!matches) {
|
|
81
|
-
return (0, _result.err)((0, _annotate.annotate)(value, 'Must be URL'));
|
|
82
|
-
} else {
|
|
83
|
-
var scheme = matches[1];
|
|
84
|
-
|
|
85
|
-
if (schemes.length === 0 || schemes.includes(scheme.toLowerCase())) {
|
|
86
|
-
return (0, _result.ok)(value);
|
|
87
|
-
} else {
|
|
88
|
-
return (0, _result.err)((0, _annotate.annotate)(value, "URL scheme must be any of: " + schemes.join(', ')));
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
exports.url = url;
|
|
61
|
+
var url = (0, _either.either)((0, _composition.map)(regex(url_re, 'Must be URL'), function (value) {
|
|
62
|
+
return new URL(value);
|
|
63
|
+
}), (0, _instanceOf.instanceOf)(URL));
|
|
64
|
+
exports.url = url;
|
|
65
|
+
var httpsUrl = (0, _composition.predicate)(url, function (value) {
|
|
66
|
+
return value.protocol === 'https:';
|
|
67
|
+
}, 'Must be an HTTPS URL');
|
|
68
|
+
exports.httpsUrl = httpsUrl;
|