decoders 1.25.5 → 2.0.0-beta2
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 +23 -3
- package/_esm/_guard.js +15 -0
- package/_esm/_guard.js.flow +20 -0
- package/_esm/_types.js +0 -0
- package/_esm/_types.js.flow +20 -0
- package/_esm/_utils.js +93 -0
- package/_esm/_utils.js.flow +97 -0
- package/_esm/annotate.js +144 -0
- package/_esm/annotate.js.flow +218 -0
- package/_esm/core/array.js +91 -0
- package/{array.js.flow → _esm/core/array.js.flow} +22 -25
- package/_esm/core/boolean.js +28 -0
- package/{boolean.js.flow → _esm/core/boolean.js.flow} +8 -7
- package/_esm/core/composition.js +42 -0
- package/{utils.js.flow → _esm/core/composition.js.flow} +7 -22
- package/_esm/core/constants.js +46 -0
- package/{constants.js.flow → _esm/core/constants.js.flow} +13 -12
- package/_esm/core/date.js +28 -0
- package/{date.js.flow → _esm/core/date.js.flow} +9 -7
- package/{describe.js → _esm/core/describe.js} +5 -11
- package/{describe.js.flow → _esm/core/describe.js.flow} +4 -4
- package/{dispatch.js → _esm/core/dispatch.js} +8 -15
- package/{dispatch.js.flow → _esm/core/dispatch.js.flow} +6 -5
- package/_esm/core/either.js +90 -0
- package/{either.js.flow → _esm/core/either.js.flow} +55 -35
- package/_esm/core/fail.js +11 -0
- package/_esm/core/fail.js.flow +12 -0
- package/_esm/core/instanceOf.js +8 -0
- package/{instanceOf.js.flow → _esm/core/instanceOf.js.flow} +7 -8
- package/_esm/core/json.js +15 -0
- package/{json.js.flow → _esm/core/json.js.flow} +4 -4
- package/_esm/core/lazy.js +11 -0
- package/{lazy.js.flow → _esm/core/lazy.js.flow} +1 -1
- package/_esm/core/mapping.js +54 -0
- package/_esm/core/mapping.js.flow +54 -0
- package/_esm/core/number.js +25 -0
- package/{number.js.flow → _esm/core/number.js.flow} +10 -11
- package/_esm/core/object.js +175 -0
- package/{object.js.flow → _esm/core/object.js.flow} +44 -52
- package/_esm/core/optional.js +38 -0
- package/{optional.js.flow → _esm/core/optional.js.flow} +6 -7
- package/{string.js → _esm/core/string.js} +18 -35
- package/{string.js.flow → _esm/core/string.js.flow} +13 -12
- package/_esm/core/tuple.js +155 -0
- package/{tuple.js.flow → _esm/core/tuple.js.flow} +62 -63
- package/_esm/format/index.js +2 -0
- package/_esm/format/index.js.flow +4 -0
- package/_esm/format/inline.js +137 -0
- package/_esm/format/inline.js.flow +122 -0
- package/_esm/format/short.js +4 -0
- package/_esm/format/short.js.flow +8 -0
- package/_esm/index.js +37 -0
- package/_esm/index.js.flow +56 -0
- package/_esm/result.js +139 -0
- package/_esm/result.js.flow +166 -0
- package/_guard.js +26 -0
- package/_guard.js.flow +20 -0
- package/_types.js +1 -0
- package/_types.js.flow +20 -0
- package/_utils.js +108 -0
- package/_utils.js.flow +97 -0
- package/annotate.js +161 -0
- package/annotate.js.flow +218 -0
- package/core/array.js +108 -0
- package/core/array.js.flow +103 -0
- package/core/boolean.js +44 -0
- package/core/boolean.js.flow +29 -0
- package/core/composition.js +56 -0
- package/core/composition.js.flow +43 -0
- package/core/constants.js +69 -0
- package/core/constants.js.flow +46 -0
- package/core/date.js +46 -0
- package/core/date.js.flow +40 -0
- package/core/describe.js +26 -0
- package/core/describe.js.flow +17 -0
- package/core/dispatch.js +62 -0
- package/core/dispatch.js.flow +58 -0
- package/core/either.js +117 -0
- package/core/either.js.flow +151 -0
- package/core/fail.js +21 -0
- package/core/fail.js.flow +12 -0
- package/core/instanceOf.js +19 -0
- package/core/instanceOf.js.flow +20 -0
- package/{json.js → core/json.js} +3 -5
- package/core/json.js.flow +28 -0
- package/{lazy.js → core/lazy.js} +1 -3
- package/core/lazy.js.flow +15 -0
- package/core/mapping.js +67 -0
- package/core/mapping.js.flow +54 -0
- package/core/number.js +40 -0
- package/core/number.js.flow +34 -0
- package/core/object.js +194 -0
- package/core/object.js.flow +203 -0
- package/core/optional.js +54 -0
- package/core/optional.js.flow +41 -0
- package/core/string.js +98 -0
- package/core/string.js.flow +82 -0
- package/core/tuple.js +173 -0
- package/core/tuple.js.flow +220 -0
- package/format/index.js +12 -0
- package/format/index.js.flow +4 -0
- package/format/inline.js +146 -0
- package/format/inline.js.flow +122 -0
- package/format/short.js +10 -0
- package/format/short.js.flow +8 -0
- package/index.js +118 -395
- package/index.js.flow +24 -24
- package/package.json +2 -6
- package/result.js +172 -0
- package/result.js.flow +166 -0
- package/array.d.ts +0 -5
- package/array.js +0 -133
- package/boolean.d.ts +0 -5
- package/boolean.js +0 -42
- package/constants.d.ts +0 -11
- package/constants.js +0 -67
- package/date.d.ts +0 -4
- package/date.js +0 -42
- package/describe.d.ts +0 -3
- package/dispatch.d.ts +0 -8
- package/either.d.ts +0 -61
- package/either.js +0 -85
- package/fail.d.ts +0 -3
- package/fail.js +0 -19
- package/fail.js.flow +0 -13
- package/guard.d.ts +0 -7
- package/guard.js +0 -30
- package/guard.js.flow +0 -36
- package/helpers.d.ts +0 -79
- package/index.d.ts +0 -38
- package/instanceOf.d.ts +0 -3
- package/instanceOf.js +0 -17
- package/json.d.ts +0 -11
- package/lazy.d.ts +0 -3
- package/mapping.d.ts +0 -4
- package/mapping.js +0 -113
- package/mapping.js.flow +0 -71
- package/number.d.ts +0 -6
- package/number.js +0 -38
- package/object.d.ts +0 -33
- package/object.js +0 -254
- package/optional.d.ts +0 -5
- package/optional.js +0 -52
- package/string.d.ts +0 -7
- package/tuple.d.ts +0 -30
- package/tuple.js +0 -199
- package/types.d.ts +0 -18
- package/types.js +0 -5
- package/types.js.flow +0 -26
- package/utils.d.ts +0 -13
- package/utils.js +0 -70
package/_esm/index.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Elm-like JSON decoders, for use with Flow.
|
|
3
|
+
* See http://elmplayground.com/decoding-json-in-elm-1 for an introduction.
|
|
4
|
+
*
|
|
5
|
+
* Why? All JSON responses coming from our API endpoints are just that: free-form
|
|
6
|
+
* JSON data. To Flow, the only type classification possilbe is "any" -- effectively
|
|
7
|
+
* turning off all type checks for anything related to JSON. To the receiving end
|
|
8
|
+
* (our frontend), the structure of that data is completely opaque to any type
|
|
9
|
+
* checkers since JSON values can be anything: an object, an array, null, a string,
|
|
10
|
+
* a bool, etc. Our type system is not a runtime type system, so we need a way of
|
|
11
|
+
* "converting" an any-type JSON value into a type that we want to work with in our
|
|
12
|
+
* frontend code base.
|
|
13
|
+
*
|
|
14
|
+
* Elm's solution to this problem is to define composable decoders: functions that
|
|
15
|
+
* take anything and either fail with an error, or guarantee to return the expected
|
|
16
|
+
* type. In our case, it's fine to fail with a runtime error.
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
export { guard } from './_guard';
|
|
20
|
+
export { compose, map, predicate } from './core/composition';
|
|
21
|
+
export { array, nonEmptyArray, poja } from './core/array';
|
|
22
|
+
export { boolean, numericBoolean, truthy } from './core/boolean';
|
|
23
|
+
export { constant, hardcoded, mixed, null_, undefined_, unknown } from './core/constants';
|
|
24
|
+
export { date, iso8601 } from './core/date';
|
|
25
|
+
export { describe } from './core/describe';
|
|
26
|
+
export { dispatch } from './core/dispatch';
|
|
27
|
+
export { either, either3, either4, either5, either6, either7, either8, either9, oneOf } from './core/either';
|
|
28
|
+
export { fail } from './core/fail';
|
|
29
|
+
export { instanceOf } from './core/instanceOf';
|
|
30
|
+
export { json, jsonObject, jsonArray } from './core/json';
|
|
31
|
+
export { lazy } from './core/lazy';
|
|
32
|
+
export { mapping, dict } from './core/mapping';
|
|
33
|
+
export { integer, number, positiveInteger, positiveNumber } from './core/number';
|
|
34
|
+
export { exact, inexact, object, pojo } from './core/object';
|
|
35
|
+
export { maybe, nullable, optional } from './core/optional';
|
|
36
|
+
export { email, nonEmptyString, regex, string, url } from './core/string';
|
|
37
|
+
export { tuple1, tuple2, tuple3, tuple4, tuple5, tuple6 } from './core/tuple';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// @flow strict
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Elm-like JSON decoders, for use with Flow.
|
|
5
|
+
* See http://elmplayground.com/decoding-json-in-elm-1 for an introduction.
|
|
6
|
+
*
|
|
7
|
+
* Why? All JSON responses coming from our API endpoints are just that: free-form
|
|
8
|
+
* JSON data. To Flow, the only type classification possilbe is "any" -- effectively
|
|
9
|
+
* turning off all type checks for anything related to JSON. To the receiving end
|
|
10
|
+
* (our frontend), the structure of that data is completely opaque to any type
|
|
11
|
+
* checkers since JSON values can be anything: an object, an array, null, a string,
|
|
12
|
+
* a bool, etc. Our type system is not a runtime type system, so we need a way of
|
|
13
|
+
* "converting" an any-type JSON value into a type that we want to work with in our
|
|
14
|
+
* frontend code base.
|
|
15
|
+
*
|
|
16
|
+
* Elm's solution to this problem is to define composable decoders: functions that
|
|
17
|
+
* take anything and either fail with an error, or guarantee to return the expected
|
|
18
|
+
* type. In our case, it's fine to fail with a runtime error.
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
export type { Decoder, Guard } from './_types';
|
|
22
|
+
export type { DecoderType, GuardType } from './_types';
|
|
23
|
+
|
|
24
|
+
export type { JSONValue, JSONObject, JSONArray } from './core/json';
|
|
25
|
+
|
|
26
|
+
export { guard } from './_guard';
|
|
27
|
+
|
|
28
|
+
export { compose, map, predicate } from './core/composition';
|
|
29
|
+
|
|
30
|
+
export { array, nonEmptyArray, poja } from './core/array';
|
|
31
|
+
export { boolean, numericBoolean, truthy } from './core/boolean';
|
|
32
|
+
export { constant, hardcoded, mixed, null_, undefined_, unknown } from './core/constants';
|
|
33
|
+
export { date, iso8601 } from './core/date';
|
|
34
|
+
export { describe } from './core/describe';
|
|
35
|
+
export { dispatch } from './core/dispatch';
|
|
36
|
+
export {
|
|
37
|
+
either,
|
|
38
|
+
either3,
|
|
39
|
+
either4,
|
|
40
|
+
either5,
|
|
41
|
+
either6,
|
|
42
|
+
either7,
|
|
43
|
+
either8,
|
|
44
|
+
either9,
|
|
45
|
+
oneOf,
|
|
46
|
+
} from './core/either';
|
|
47
|
+
export { fail } from './core/fail';
|
|
48
|
+
export { instanceOf } from './core/instanceOf';
|
|
49
|
+
export { json, jsonObject, jsonArray } from './core/json';
|
|
50
|
+
export { lazy } from './core/lazy';
|
|
51
|
+
export { mapping, dict } from './core/mapping';
|
|
52
|
+
export { integer, number, positiveInteger, positiveNumber } from './core/number';
|
|
53
|
+
export { exact, inexact, object, pojo } from './core/object';
|
|
54
|
+
export { maybe, nullable, optional } from './core/optional';
|
|
55
|
+
export { email, nonEmptyString, regex, string, url } from './core/string';
|
|
56
|
+
export { tuple1, tuple2, tuple3, tuple4, tuple5, tuple6 } from './core/tuple';
|
package/_esm/result.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result <value> <error>
|
|
3
|
+
* = Ok <value>
|
|
4
|
+
* | Err <error>
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Create a new Result instance representing a successful computation.
|
|
9
|
+
*/
|
|
10
|
+
export function ok(value) {
|
|
11
|
+
return {
|
|
12
|
+
type: 'ok',
|
|
13
|
+
value: value
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Create a new Result instance representing a failed computation.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export function err(error) {
|
|
21
|
+
return {
|
|
22
|
+
type: 'err',
|
|
23
|
+
error: error
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function toString(result) {
|
|
27
|
+
return result.type === 'ok' ? "Ok(" + String(result.value) + ")" : "Err(" + String(result.error) + ")";
|
|
28
|
+
}
|
|
29
|
+
export function isOk(result) {
|
|
30
|
+
return result.type === 'ok';
|
|
31
|
+
}
|
|
32
|
+
export function isErr(result) {
|
|
33
|
+
return result.type === 'err';
|
|
34
|
+
}
|
|
35
|
+
export function withDefault(result, defaultValue) {
|
|
36
|
+
return result.type === 'ok' ? result.value : defaultValue;
|
|
37
|
+
}
|
|
38
|
+
export function value(result) {
|
|
39
|
+
return result.type === 'ok' ? result.value : undefined;
|
|
40
|
+
}
|
|
41
|
+
export function errValue(result) {
|
|
42
|
+
return result.type === 'err' ? result.error : undefined;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Unwrap the value from this Result instance if this is an "Ok" result.
|
|
46
|
+
* Otherwise, will throw the "Err" error via a runtime exception.
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
export function unwrap(result) {
|
|
50
|
+
if (result.type === 'ok') {
|
|
51
|
+
return result.value;
|
|
52
|
+
} else {
|
|
53
|
+
throw result.error;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export function expect(result, message) {
|
|
57
|
+
if (result.type === 'ok') {
|
|
58
|
+
return result.value;
|
|
59
|
+
} else {
|
|
60
|
+
throw message instanceof Error ? message : new Error(message);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export function dispatch(result, okCallback, errCallback) {
|
|
64
|
+
return result.type === 'ok' ? okCallback(result.value) : errCallback(result.error);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* If the given result is OK, defers to the other result. Otherwise returns the
|
|
68
|
+
* error result.
|
|
69
|
+
*
|
|
70
|
+
* It's like saying A && B, but on Result.
|
|
71
|
+
*
|
|
72
|
+
* Examples:
|
|
73
|
+
*
|
|
74
|
+
* Result.ok(42) && Result.ok('hi') // => Ok('hi')
|
|
75
|
+
* Result.err('boo') && Result.ok('hi') // => Err('boo')
|
|
76
|
+
* Result.ok(42) && Result.err('boo') // => Err('boo')
|
|
77
|
+
* Result.err('boo') && Result.err('boo') // => Err('boo')
|
|
78
|
+
*
|
|
79
|
+
*/
|
|
80
|
+
// export function and<T, E, T2>(
|
|
81
|
+
// result1: Result<T, E>,
|
|
82
|
+
// result2: Result<T2, E>,
|
|
83
|
+
// ): Result<T2, E> {
|
|
84
|
+
// return result1.type === 'ok' ? result2 : result1;
|
|
85
|
+
// }
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* If the given result is OK, return that result. Otherwise, defers to the
|
|
89
|
+
* other result.
|
|
90
|
+
*
|
|
91
|
+
* It's like saying A || B, but on Result.
|
|
92
|
+
*
|
|
93
|
+
* Examples:
|
|
94
|
+
*
|
|
95
|
+
* Result.ok(42) || Result.ok('hi') // => Ok(42)
|
|
96
|
+
* Result.err('boo') || Result.ok('hi') // => Ok('hi')
|
|
97
|
+
* Result.ok(42) || Result.err('boo') // => Ok(42)
|
|
98
|
+
* Result.err('bleh') || Result.err('boo') // => Err('boo')
|
|
99
|
+
*
|
|
100
|
+
*/
|
|
101
|
+
// export function or<T, E, E2>(
|
|
102
|
+
// result1: Result<T, E>,
|
|
103
|
+
// result2: Result<T, E2>,
|
|
104
|
+
// ): Result<T, E2> {
|
|
105
|
+
// return result1.type === 'ok' ? result1 : result2;
|
|
106
|
+
// }
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Like .and(), aka &&, but the second argument gets evaluated lazily only if
|
|
110
|
+
* the first result is an Ok result. If so, it has access to the Ok value from
|
|
111
|
+
* the first argument.
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
export function andThen(result1, lazyResult2) {
|
|
115
|
+
return result1.type === 'ok' ? lazyResult2(result1.value) : result1;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Like .or(), aka ||, but the second argument gets evaluated lazily only if
|
|
119
|
+
* the first result is an Err result. If so, it has access to the Err value
|
|
120
|
+
* from the first argument.
|
|
121
|
+
*/
|
|
122
|
+
|
|
123
|
+
export function orElse(result1, lazyResult2) {
|
|
124
|
+
return result1.type === 'ok' ? result1 : lazyResult2(result1.error);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Transform an Ok result. Will not touch Err results.
|
|
128
|
+
*/
|
|
129
|
+
|
|
130
|
+
export function map(result, mapper) {
|
|
131
|
+
return result.type === 'ok' ? ok(mapper(result.value)) : result;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Transform an Err value. Will not touch Ok results.
|
|
135
|
+
*/
|
|
136
|
+
|
|
137
|
+
export function mapError(result, mapper) {
|
|
138
|
+
return result.type === 'ok' ? result : err(mapper(result.error));
|
|
139
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// @flow strict
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Result <value> <error>
|
|
5
|
+
* = Ok <value>
|
|
6
|
+
* | Err <error>
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
type Ok<+T> = {| +type: 'ok', +value: T |};
|
|
10
|
+
type Err<+E> = {| +type: 'err', +error: E |};
|
|
11
|
+
|
|
12
|
+
export type Result<+T, +E> = Ok<T> | Err<E>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Create a new Result instance representing a successful computation.
|
|
16
|
+
*/
|
|
17
|
+
export function ok<T>(value: T): Ok<T> {
|
|
18
|
+
return { type: 'ok', value };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Create a new Result instance representing a failed computation.
|
|
23
|
+
*/
|
|
24
|
+
export function err<E>(error: E): Err<E> {
|
|
25
|
+
return { type: 'err', error };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function toString(result: Result<mixed, mixed>): string {
|
|
29
|
+
return result.type === 'ok'
|
|
30
|
+
? `Ok(${String(result.value)})`
|
|
31
|
+
: `Err(${String(result.error)})`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function isOk(result: Result<mixed, mixed>): boolean {
|
|
35
|
+
return result.type === 'ok';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function isErr(result: Result<mixed, mixed>): boolean {
|
|
39
|
+
return result.type === 'err';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function withDefault<T>(result: Result<T, mixed>, defaultValue: T): T {
|
|
43
|
+
return result.type === 'ok' ? result.value : defaultValue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function value<T>(result: Result<T, mixed>): void | T {
|
|
47
|
+
return result.type === 'ok' ? result.value : undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function errValue<E>(result: Result<mixed, E>): void | E {
|
|
51
|
+
return result.type === 'err' ? result.error : undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Unwrap the value from this Result instance if this is an "Ok" result.
|
|
56
|
+
* Otherwise, will throw the "Err" error via a runtime exception.
|
|
57
|
+
*/
|
|
58
|
+
export function unwrap<T>(result: Result<T, mixed>): T {
|
|
59
|
+
if (result.type === 'ok') {
|
|
60
|
+
return result.value;
|
|
61
|
+
} else {
|
|
62
|
+
throw result.error;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function expect<T>(result: Result<T, mixed>, message: string | Error): T {
|
|
67
|
+
if (result.type === 'ok') {
|
|
68
|
+
return result.value;
|
|
69
|
+
} else {
|
|
70
|
+
throw message instanceof Error ? message : new Error(message);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function dispatch<T, E, O>(
|
|
75
|
+
result: Result<T, E>,
|
|
76
|
+
okCallback: (value: T) => O,
|
|
77
|
+
errCallback: (error: E) => O,
|
|
78
|
+
): O {
|
|
79
|
+
return result.type === 'ok' ? okCallback(result.value) : errCallback(result.error);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* If the given result is OK, defers to the other result. Otherwise returns the
|
|
84
|
+
* error result.
|
|
85
|
+
*
|
|
86
|
+
* It's like saying A && B, but on Result.
|
|
87
|
+
*
|
|
88
|
+
* Examples:
|
|
89
|
+
*
|
|
90
|
+
* Result.ok(42) && Result.ok('hi') // => Ok('hi')
|
|
91
|
+
* Result.err('boo') && Result.ok('hi') // => Err('boo')
|
|
92
|
+
* Result.ok(42) && Result.err('boo') // => Err('boo')
|
|
93
|
+
* Result.err('boo') && Result.err('boo') // => Err('boo')
|
|
94
|
+
*
|
|
95
|
+
*/
|
|
96
|
+
// export function and<T, E, T2>(
|
|
97
|
+
// result1: Result<T, E>,
|
|
98
|
+
// result2: Result<T2, E>,
|
|
99
|
+
// ): Result<T2, E> {
|
|
100
|
+
// return result1.type === 'ok' ? result2 : result1;
|
|
101
|
+
// }
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* If the given result is OK, return that result. Otherwise, defers to the
|
|
105
|
+
* other result.
|
|
106
|
+
*
|
|
107
|
+
* It's like saying A || B, but on Result.
|
|
108
|
+
*
|
|
109
|
+
* Examples:
|
|
110
|
+
*
|
|
111
|
+
* Result.ok(42) || Result.ok('hi') // => Ok(42)
|
|
112
|
+
* Result.err('boo') || Result.ok('hi') // => Ok('hi')
|
|
113
|
+
* Result.ok(42) || Result.err('boo') // => Ok(42)
|
|
114
|
+
* Result.err('bleh') || Result.err('boo') // => Err('boo')
|
|
115
|
+
*
|
|
116
|
+
*/
|
|
117
|
+
// export function or<T, E, E2>(
|
|
118
|
+
// result1: Result<T, E>,
|
|
119
|
+
// result2: Result<T, E2>,
|
|
120
|
+
// ): Result<T, E2> {
|
|
121
|
+
// return result1.type === 'ok' ? result1 : result2;
|
|
122
|
+
// }
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Like .and(), aka &&, but the second argument gets evaluated lazily only if
|
|
126
|
+
* the first result is an Ok result. If so, it has access to the Ok value from
|
|
127
|
+
* the first argument.
|
|
128
|
+
*/
|
|
129
|
+
export function andThen<T, E, T2>(
|
|
130
|
+
result1: Result<T, E>,
|
|
131
|
+
lazyResult2: (value: T) => Result<T2, E>,
|
|
132
|
+
): Result<T2, E> {
|
|
133
|
+
return result1.type === 'ok' ? lazyResult2(result1.value) : result1;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Like .or(), aka ||, but the second argument gets evaluated lazily only if
|
|
138
|
+
* the first result is an Err result. If so, it has access to the Err value
|
|
139
|
+
* from the first argument.
|
|
140
|
+
*/
|
|
141
|
+
export function orElse<T, E, E2>(
|
|
142
|
+
result1: Result<T, E>,
|
|
143
|
+
lazyResult2: (errValue: E) => Result<T, E2>,
|
|
144
|
+
): Result<T, E2> {
|
|
145
|
+
return result1.type === 'ok' ? result1 : lazyResult2(result1.error);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Transform an Ok result. Will not touch Err results.
|
|
150
|
+
*/
|
|
151
|
+
export function map<T, E, T2>(
|
|
152
|
+
result: Result<T, E>,
|
|
153
|
+
mapper: (value: T) => T2,
|
|
154
|
+
): Result<T2, E> {
|
|
155
|
+
return result.type === 'ok' ? ok(mapper(result.value)) : result;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Transform an Err value. Will not touch Ok results.
|
|
160
|
+
*/
|
|
161
|
+
export function mapError<T, E, E2>(
|
|
162
|
+
result: Result<T, E>,
|
|
163
|
+
mapper: (error: E) => E2,
|
|
164
|
+
): Result<T, E2> {
|
|
165
|
+
return result.type === 'ok' ? result : err(mapper(result.error));
|
|
166
|
+
}
|
package/_guard.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.guard = guard;
|
|
5
|
+
|
|
6
|
+
var Result = _interopRequireWildcard(require("./result"));
|
|
7
|
+
|
|
8
|
+
var _format = require("./format");
|
|
9
|
+
|
|
10
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
11
|
+
|
|
12
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
13
|
+
|
|
14
|
+
function guard(decoder, formatter) {
|
|
15
|
+
if (formatter === void 0) {
|
|
16
|
+
formatter = _format.formatInline;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return function (blob) {
|
|
20
|
+
return Result.unwrap(Result.mapError(decoder(blob), function (annotation) {
|
|
21
|
+
var err = new Error('\n' + formatter(annotation));
|
|
22
|
+
err.name = 'Decoding error';
|
|
23
|
+
return err;
|
|
24
|
+
}));
|
|
25
|
+
};
|
|
26
|
+
}
|
package/_guard.js.flow
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// @flow strict
|
|
2
|
+
|
|
3
|
+
import * as Result from './result';
|
|
4
|
+
import { formatInline } from './format';
|
|
5
|
+
import type { Annotation } from './annotate';
|
|
6
|
+
import type { Decoder, Guard } from './_types';
|
|
7
|
+
|
|
8
|
+
export function guard<T>(
|
|
9
|
+
decoder: Decoder<T>,
|
|
10
|
+
formatter: (Annotation) => string = formatInline,
|
|
11
|
+
): Guard<T> {
|
|
12
|
+
return (blob: mixed) =>
|
|
13
|
+
Result.unwrap(
|
|
14
|
+
Result.mapError(decoder(blob), (annotation) => {
|
|
15
|
+
const err = new Error('\n' + formatter(annotation));
|
|
16
|
+
err.name = 'Decoding error';
|
|
17
|
+
return err;
|
|
18
|
+
}),
|
|
19
|
+
);
|
|
20
|
+
}
|
package/_types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
package/_types.js.flow
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// @flow strict
|
|
2
|
+
|
|
3
|
+
import type { Annotation } from './annotate';
|
|
4
|
+
import type { Result } from './result';
|
|
5
|
+
|
|
6
|
+
export type Scalar = string | number | boolean | symbol | void | null;
|
|
7
|
+
|
|
8
|
+
export type Predicate<T> = (T) => boolean;
|
|
9
|
+
export type DecodeResult<T> = Result<T, Annotation>;
|
|
10
|
+
|
|
11
|
+
export type Decoder<T, F = mixed> = (F) => DecodeResult<T>;
|
|
12
|
+
export type Guard<T> = (mixed) => T;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A "type function" which informs Flow about how a type will be modified at runtime.
|
|
16
|
+
* Read this as "given a Guard of type T, I can produce a value of type T". This
|
|
17
|
+
* definition helps construct $ObjMap types.
|
|
18
|
+
*/
|
|
19
|
+
export type DecoderType = <T>(Decoder<T>) => T;
|
|
20
|
+
export type GuardType = <T>(Guard<T>) => T;
|
package/_utils.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.INDENT = void 0;
|
|
5
|
+
exports.asDate = asDate;
|
|
6
|
+
exports.indent = indent;
|
|
7
|
+
exports.isDate = isDate;
|
|
8
|
+
exports.isMultiline = isMultiline;
|
|
9
|
+
exports.summarize = summarize;
|
|
10
|
+
// $FlowFixMe[unclear-type] - deliberate casting
|
|
11
|
+
// Two spaces of indentation
|
|
12
|
+
var INDENT = ' ';
|
|
13
|
+
/**
|
|
14
|
+
* `x instanceof Date` checks are unreliable across stack frames (that information
|
|
15
|
+
* might get lost by the JS runtime), so we'll have to reside to more runtime
|
|
16
|
+
* inspection checks.
|
|
17
|
+
*
|
|
18
|
+
* Taken from https://stackoverflow.com/a/44198641
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
exports.INDENT = INDENT;
|
|
22
|
+
|
|
23
|
+
function isDate(value) {
|
|
24
|
+
return !!value && // $FlowFixMe[method-unbinding]
|
|
25
|
+
Object.prototype.toString.call(value) === '[object Date]' && !isNaN(value);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Is value is a valid Date instance, then return that. If not, then return
|
|
29
|
+
* null.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
function asDate(value) {
|
|
34
|
+
return isDate(value) ? value : null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function isMultiline(s) {
|
|
38
|
+
return s.indexOf('\n') >= 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function indent(s, prefix) {
|
|
42
|
+
if (prefix === void 0) {
|
|
43
|
+
prefix = INDENT;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (isMultiline(s)) {
|
|
47
|
+
return s.split('\n').map(function (line) {
|
|
48
|
+
return prefix + line;
|
|
49
|
+
}).join('\n');
|
|
50
|
+
} else {
|
|
51
|
+
return prefix + s;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Walks the annotation tree and emits the annotation's key path within the
|
|
56
|
+
* object tree, and the message as a series of messages (array of strings).
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
function summarize(ann, keypath) {
|
|
61
|
+
if (keypath === void 0) {
|
|
62
|
+
keypath = [];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
var result = [];
|
|
66
|
+
|
|
67
|
+
if (ann.type === 'array') {
|
|
68
|
+
var items = ann.items;
|
|
69
|
+
var index = 0;
|
|
70
|
+
items.forEach(function (ann) {
|
|
71
|
+
summarize(ann, [].concat(keypath, [index++])).forEach(function (item) {
|
|
72
|
+
return (// Collect to results
|
|
73
|
+
result.push(item)
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
} else if (ann.type === 'object') {
|
|
78
|
+
var fields = ann.fields;
|
|
79
|
+
Object.keys(fields).forEach(function (key) {
|
|
80
|
+
var value = fields[key];
|
|
81
|
+
summarize(value, [].concat(keypath, [key])).forEach(function (item) {
|
|
82
|
+
return (// Collect to results
|
|
83
|
+
result.push(item)
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
var text = ann.text;
|
|
90
|
+
|
|
91
|
+
if (!text) {
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
var prefix;
|
|
96
|
+
|
|
97
|
+
if (keypath.length === 0) {
|
|
98
|
+
prefix = '';
|
|
99
|
+
} else if (keypath.length === 1) {
|
|
100
|
+
prefix = typeof keypath[0] === 'number' ? "Value at index " + keypath[0] + ": " : "Value at key " + JSON.stringify(keypath[0]) + ": ";
|
|
101
|
+
} else {
|
|
102
|
+
prefix = "Value at keypath " + keypath.map(function (x) {
|
|
103
|
+
return x.toString();
|
|
104
|
+
}).join('.') + ": ";
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return [].concat(result, [prefix + text]);
|
|
108
|
+
}
|
package/_utils.js.flow
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// @flow strict
|
|
2
|
+
|
|
3
|
+
import type { Annotation } from './annotate';
|
|
4
|
+
|
|
5
|
+
// $FlowFixMe[unclear-type] - deliberate casting
|
|
6
|
+
type cast = any;
|
|
7
|
+
|
|
8
|
+
// Two spaces of indentation
|
|
9
|
+
export const INDENT = ' ';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* `x instanceof Date` checks are unreliable across stack frames (that information
|
|
13
|
+
* might get lost by the JS runtime), so we'll have to reside to more runtime
|
|
14
|
+
* inspection checks.
|
|
15
|
+
*
|
|
16
|
+
* Taken from https://stackoverflow.com/a/44198641
|
|
17
|
+
*/
|
|
18
|
+
export function isDate(value: mixed): boolean {
|
|
19
|
+
return (
|
|
20
|
+
!!value &&
|
|
21
|
+
// $FlowFixMe[method-unbinding]
|
|
22
|
+
Object.prototype.toString.call(value) === '[object Date]' &&
|
|
23
|
+
!isNaN(value)
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Is value is a valid Date instance, then return that. If not, then return
|
|
29
|
+
* null.
|
|
30
|
+
*/
|
|
31
|
+
export function asDate(value: mixed): Date | null {
|
|
32
|
+
return isDate(value) ? ((value: cast): Date) : null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function isMultiline(s: string): boolean {
|
|
36
|
+
return s.indexOf('\n') >= 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function indent(s: string, prefix: string = INDENT): string {
|
|
40
|
+
if (isMultiline(s)) {
|
|
41
|
+
return s
|
|
42
|
+
.split('\n')
|
|
43
|
+
.map((line) => prefix + line)
|
|
44
|
+
.join('\n');
|
|
45
|
+
} else {
|
|
46
|
+
return prefix + s;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Walks the annotation tree and emits the annotation's key path within the
|
|
52
|
+
* object tree, and the message as a series of messages (array of strings).
|
|
53
|
+
*/
|
|
54
|
+
export function summarize(
|
|
55
|
+
ann: Annotation,
|
|
56
|
+
keypath: $ReadOnlyArray<number | string> = [],
|
|
57
|
+
): Array<string> {
|
|
58
|
+
const result: Array<string> = [];
|
|
59
|
+
|
|
60
|
+
if (ann.type === 'array') {
|
|
61
|
+
const items = ann.items;
|
|
62
|
+
let index = 0;
|
|
63
|
+
items.forEach((ann) => {
|
|
64
|
+
summarize(ann, [...keypath, index++]).forEach((item) =>
|
|
65
|
+
// Collect to results
|
|
66
|
+
result.push(item),
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
} else if (ann.type === 'object') {
|
|
70
|
+
const fields = ann.fields;
|
|
71
|
+
Object.keys(fields).forEach((key) => {
|
|
72
|
+
const value = fields[key];
|
|
73
|
+
summarize(value, [...keypath, key]).forEach((item) =>
|
|
74
|
+
// Collect to results
|
|
75
|
+
result.push(item),
|
|
76
|
+
);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const text = ann.text;
|
|
81
|
+
if (!text) {
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let prefix: string;
|
|
86
|
+
if (keypath.length === 0) {
|
|
87
|
+
prefix = '';
|
|
88
|
+
} else if (keypath.length === 1) {
|
|
89
|
+
prefix =
|
|
90
|
+
typeof keypath[0] === 'number'
|
|
91
|
+
? `Value at index ${keypath[0]}: `
|
|
92
|
+
: `Value at key ${JSON.stringify(keypath[0])}: `;
|
|
93
|
+
} else {
|
|
94
|
+
prefix = `Value at keypath ${keypath.map((x) => x.toString()).join('.')}: `;
|
|
95
|
+
}
|
|
96
|
+
return [...result, prefix + text];
|
|
97
|
+
}
|