decoders 2.0.0-beta11 → 2.0.0-beta12

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/Decoder.d.ts CHANGED
@@ -11,7 +11,7 @@ export type DecodeFn<T, I = unknown> = (
11
11
  ) => DecodeResult<T>;
12
12
 
13
13
  export interface Decoder<T> {
14
- verify(blob: unknown, formatterFn?: (ann: Annotation) => string): T;
14
+ verify(blob: unknown, formatterFn?: (ann: Annotation) => string | Error): T;
15
15
  value(blob: unknown): T | undefined;
16
16
  decode(blob: unknown): DecodeResult<T>;
17
17
  refine<N extends T>(predicate: (value: T) => value is N, msg: string): Decoder<N>;
package/Decoder.js CHANGED
@@ -64,10 +64,20 @@ function define(decodeFn) {
64
64
  if (result.ok) {
65
65
  return result.value;
66
66
  } else {
67
- var _err = new Error('\n' + formatter(result.error));
67
+ // Formatters may return a string or an error for convenience of
68
+ // writing them. If it already returns an Error, throw it
69
+ // unmodified. If it returns a string, wrap it in a "Decoding
70
+ // error" instance from it and throw that.
71
+ var strOrErr = formatter(result.error);
68
72
 
69
- _err.name = 'Decoding error';
70
- throw _err;
73
+ if (typeof strOrErr === 'string') {
74
+ var _err = new Error('\n' + strOrErr);
75
+
76
+ _err.name = 'Decoding error';
77
+ throw _err;
78
+ } else {
79
+ throw strOrErr;
80
+ }
71
81
  }
72
82
  }
73
83
  /**
package/Decoder.js.flow CHANGED
@@ -32,7 +32,7 @@ export type DecodeFn<T, I = mixed> = (
32
32
  export type DecoderType<D> = $Call<<T>(Decoder<T>) => T, D>;
33
33
 
34
34
  export type Decoder<T> = {|
35
- verify(blob: mixed, formatterFn?: (Annotation) => string): T,
35
+ verify(blob: mixed, formatterFn?: (Annotation) => string | Error): T,
36
36
  value(blob: mixed): T | void,
37
37
  decode(blob: mixed): DecodeResult<T>,
38
38
  refine(predicateFn: (value: T) => boolean, errmsg: string): Decoder<T>,
@@ -88,14 +88,26 @@ export function define<T>(decodeFn: DecodeFn<T>): Decoder<T> {
88
88
  * it. When accepted, returns the decoded `T` value directly. Otherwise
89
89
  * fail with a runtime error.
90
90
  */
91
- function verify(blob: mixed, formatter: (Annotation) => string = formatInline): T {
91
+ function verify(
92
+ blob: mixed,
93
+ formatter: (Annotation) => string | Error = formatInline,
94
+ ): T {
92
95
  const result = decode(blob);
93
96
  if (result.ok) {
94
97
  return result.value;
95
98
  } else {
96
- const err = new Error('\n' + formatter(result.error));
97
- err.name = 'Decoding error';
98
- throw err;
99
+ // Formatters may return a string or an error for convenience of
100
+ // writing them. If it already returns an Error, throw it
101
+ // unmodified. If it returns a string, wrap it in a "Decoding
102
+ // error" instance from it and throw that.
103
+ const strOrErr = formatter(result.error);
104
+ if (typeof strOrErr === 'string') {
105
+ const err = new Error('\n' + strOrErr);
106
+ err.name = 'Decoding error';
107
+ throw err;
108
+ } else {
109
+ throw strOrErr;
110
+ }
99
111
  }
100
112
  }
101
113
 
package/Decoder.mjs CHANGED
@@ -57,10 +57,20 @@ export function define(decodeFn) {
57
57
  if (result.ok) {
58
58
  return result.value;
59
59
  } else {
60
- var _err = new Error('\n' + formatter(result.error));
60
+ // Formatters may return a string or an error for convenience of
61
+ // writing them. If it already returns an Error, throw it
62
+ // unmodified. If it returns a string, wrap it in a "Decoding
63
+ // error" instance from it and throw that.
64
+ var strOrErr = formatter(result.error);
61
65
 
62
- _err.name = 'Decoding error';
63
- throw _err;
66
+ if (typeof strOrErr === 'string') {
67
+ var _err = new Error('\n' + strOrErr);
68
+
69
+ _err.name = 'Decoding error';
70
+ throw _err;
71
+ } else {
72
+ throw strOrErr;
73
+ }
64
74
  }
65
75
  }
66
76
  /**
package/lib/basics.d.ts CHANGED
@@ -3,8 +3,32 @@ import { Decoder, Scalar } from '../Decoder';
3
3
  export const null_: Decoder<null>;
4
4
  export const undefined_: Decoder<undefined>;
5
5
  export function optional<T>(decoder: Decoder<T>): Decoder<T | undefined>;
6
+ export function optional<T, V extends Scalar>(
7
+ decoder: Decoder<T>,
8
+ defaultValue: V,
9
+ ): Decoder<NonNullable<T> | V>;
10
+ export function optional<T, V>(
11
+ decoder: Decoder<T>,
12
+ defaultValue: V,
13
+ ): Decoder<NonNullable<T> | V>;
6
14
  export function nullable<T>(decoder: Decoder<T>): Decoder<T | null>;
15
+ export function nullable<T, V extends Scalar>(
16
+ decoder: Decoder<T>,
17
+ defaultValue: V,
18
+ ): Decoder<NonNullable<T> | V>;
19
+ export function nullable<T, V>(
20
+ decoder: Decoder<T>,
21
+ defaultValue: V,
22
+ ): Decoder<NonNullable<T> | V>;
7
23
  export function maybe<T>(decoder: Decoder<T>): Decoder<T | null | undefined>;
24
+ export function maybe<T, V extends Scalar>(
25
+ decoder: Decoder<T>,
26
+ defaultValue: V,
27
+ ): Decoder<NonNullable<T> | V>;
28
+ export function maybe<T, V>(
29
+ decoder: Decoder<T>,
30
+ defaultValue: V,
31
+ ): Decoder<NonNullable<T> | V>;
8
32
  export function constant<T extends Scalar>(value: T): Decoder<T>;
9
33
  export function always<T extends Scalar>(value: T): Decoder<T>;
10
34
  export function always<T>(value: T): Decoder<T>;
package/lib/basics.js CHANGED
@@ -3,12 +3,7 @@
3
3
  exports.__esModule = true;
4
4
  exports.always = always;
5
5
  exports.constant = constant;
6
- exports.hardcoded = void 0;
7
- exports.maybe = maybe;
8
- exports.null_ = exports.mixed = void 0;
9
- exports.nullable = nullable;
10
- exports.optional = optional;
11
- exports.unknown = exports.undefined_ = void 0;
6
+ exports.unknown = exports.undefined_ = exports.optional = exports.nullable = exports.null_ = exports.mixed = exports.maybe = exports.hardcoded = void 0;
12
7
 
13
8
  var _Decoder = require("../Decoder");
14
9
 
@@ -28,40 +23,70 @@ exports.null_ = null_;
28
23
  var undefined_ = (0, _Decoder.define)(function (blob, ok, err) {
29
24
  return blob === undefined ? ok(blob) : err('Must be undefined');
30
25
  });
31
- /**
32
- * Accepts whatever the given decoder accepts, or `undefined`.
33
- */
34
-
35
26
  exports.undefined_ = undefined_;
27
+ var undefined_or_null = (0, _Decoder.define)(function (blob, ok, err) {
28
+ return blob === undefined || blob === null ? ok(blob) : // Combine error message into a single line for readability
29
+ err('Must be undefined or null');
30
+ });
36
31
 
37
- function optional(decoder) {
38
- return (0, _unions.either)(undefined_, decoder);
32
+ /**
33
+ * Accepts whatever the given decoder accepts, or `null`, or `undefined`.
34
+ */
35
+ function _maybeish(emptyCase) {
36
+ function _inner(decoder
37
+ /* defaultValue */
38
+ ) {
39
+ var _arguments = arguments;
40
+ var rv = (0, _unions.either)(emptyCase, decoder);
41
+ return (// If a default value is provided...
42
+ arguments.length >= 2 ? // ...then return the default value
43
+ rv.transform(function (value) {
44
+ return value != null ? value : _arguments[1];
45
+ }) : // Otherwise the "normal" empty case
46
+ rv
47
+ );
48
+ }
49
+
50
+ return _inner;
39
51
  }
40
52
  /**
41
53
  * Accepts whatever the given decoder accepts, or `null`.
54
+ *
55
+ * If a default value is explicitly provided, return that instead in the `null`
56
+ * case.
42
57
  */
43
58
 
44
59
 
45
- function nullable(decoder) {
46
- return (0, _unions.either)(null_, decoder);
47
- }
60
+ var nullable = _maybeish(null_);
61
+ /**
62
+ * Accepts whatever the given decoder accepts, or `undefined`.
63
+ *
64
+ * If a default value is explicitly provided, return that instead in the
65
+ * `undefined` case.
66
+ */
48
67
 
49
- var undefined_or_null = (0, _Decoder.define)(function (blob, ok, err) {
50
- return blob === undefined || blob === null ? ok(blob) : // Combine error message into a single line for readability
51
- err('Must be undefined or null');
52
- });
68
+
69
+ exports.nullable = nullable;
70
+
71
+ var optional = _maybeish(undefined_);
53
72
  /**
54
73
  * Accepts whatever the given decoder accepts, or `null`, or `undefined`.
74
+ *
75
+ * If a default value is explicitly provided, return that instead in the
76
+ * `null`/`undefined` case.
55
77
  */
56
78
 
57
- function maybe(decoder) {
58
- return (0, _unions.either)(undefined_or_null, decoder);
59
- }
79
+
80
+ exports.optional = optional;
81
+
82
+ var maybe = _maybeish(undefined_or_null);
60
83
  /**
61
84
  * Accepts only the given constant value.
62
85
  */
63
86
 
64
87
 
88
+ exports.maybe = maybe;
89
+
65
90
  function constant(value) {
66
91
  return (0, _Decoder.define)(function (blob, ok, err) {
67
92
  return blob === value ? ok(value) : err("Must be constant " + String(value));
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { define } from '../Decoder';
4
4
  import { either } from './unions';
5
+ import type { _Any } from '../_utils';
5
6
  import type { Decoder, Scalar } from '../Decoder';
6
7
 
7
8
  /**
@@ -18,33 +19,60 @@ export const undefined_: Decoder<void> = define((blob, ok, err) =>
18
19
  blob === undefined ? ok(blob) : err('Must be undefined'),
19
20
  );
20
21
 
22
+ const undefined_or_null: Decoder<null | void> = define((blob, ok, err) =>
23
+ blob === undefined || blob === null
24
+ ? ok(blob)
25
+ : // Combine error message into a single line for readability
26
+ err('Must be undefined or null'),
27
+ );
28
+
29
+ interface Maybeish<E> {
30
+ <T>(decoder: Decoder<T>): Decoder<E | T>;
31
+ <T, V>(decoder: Decoder<T>, defaultValue: V): Decoder<$NonMaybeType<T> | V>;
32
+ }
33
+
21
34
  /**
22
- * Accepts whatever the given decoder accepts, or `undefined`.
35
+ * Accepts whatever the given decoder accepts, or `null`, or `undefined`.
23
36
  */
24
- export function optional<T>(decoder: Decoder<T>): Decoder<void | T> {
25
- return either(undefined_, decoder);
37
+ function _maybeish<E>(emptyCase: Decoder<E>): Maybeish<E> {
38
+ function _inner(decoder /* defaultValue */) {
39
+ const rv = either(emptyCase, decoder);
40
+ return (
41
+ // If a default value is provided...
42
+ arguments.length >= 2
43
+ ? // ...then return the default value
44
+ rv.transform((value) => value ?? arguments[1])
45
+ : // Otherwise the "normal" empty case
46
+ rv
47
+ );
48
+ }
49
+
50
+ return (_inner: _Any);
26
51
  }
27
52
 
28
53
  /**
29
54
  * Accepts whatever the given decoder accepts, or `null`.
55
+ *
56
+ * If a default value is explicitly provided, return that instead in the `null`
57
+ * case.
30
58
  */
31
- export function nullable<T>(decoder: Decoder<T>): Decoder<null | T> {
32
- return either(null_, decoder);
33
- }
59
+ export const nullable: Maybeish<null> = _maybeish(null_);
34
60
 
35
- const undefined_or_null: Decoder<null | void> = define((blob, ok, err) =>
36
- blob === undefined || blob === null
37
- ? ok(blob)
38
- : // Combine error message into a single line for readability
39
- err('Must be undefined or null'),
40
- );
61
+ /**
62
+ * Accepts whatever the given decoder accepts, or `undefined`.
63
+ *
64
+ * If a default value is explicitly provided, return that instead in the
65
+ * `undefined` case.
66
+ */
67
+ export const optional: Maybeish<void> = _maybeish(undefined_);
41
68
 
42
69
  /**
43
70
  * Accepts whatever the given decoder accepts, or `null`, or `undefined`.
71
+ *
72
+ * If a default value is explicitly provided, return that instead in the
73
+ * `null`/`undefined` case.
44
74
  */
45
- export function maybe<T>(decoder: Decoder<T>): Decoder<T | null | void> {
46
- return either(undefined_or_null, decoder);
47
- }
75
+ export const maybe: Maybeish<null | void> = _maybeish(undefined_or_null);
48
76
 
49
77
  /**
50
78
  * Accepts only the given constant value.
package/lib/basics.mjs CHANGED
@@ -14,31 +14,56 @@ export var null_ = define(function (blob, ok, err) {
14
14
  export var undefined_ = define(function (blob, ok, err) {
15
15
  return blob === undefined ? ok(blob) : err('Must be undefined');
16
16
  });
17
+ var undefined_or_null = define(function (blob, ok, err) {
18
+ return blob === undefined || blob === null ? ok(blob) : // Combine error message into a single line for readability
19
+ err('Must be undefined or null');
20
+ });
21
+
17
22
  /**
18
- * Accepts whatever the given decoder accepts, or `undefined`.
23
+ * Accepts whatever the given decoder accepts, or `null`, or `undefined`.
19
24
  */
25
+ function _maybeish(emptyCase) {
26
+ function _inner(decoder
27
+ /* defaultValue */
28
+ ) {
29
+ var _arguments = arguments;
30
+ var rv = either(emptyCase, decoder);
31
+ return (// If a default value is provided...
32
+ arguments.length >= 2 ? // ...then return the default value
33
+ rv.transform(function (value) {
34
+ return value != null ? value : _arguments[1];
35
+ }) : // Otherwise the "normal" empty case
36
+ rv
37
+ );
38
+ }
20
39
 
21
- export function optional(decoder) {
22
- return either(undefined_, decoder);
40
+ return _inner;
23
41
  }
24
42
  /**
25
43
  * Accepts whatever the given decoder accepts, or `null`.
44
+ *
45
+ * If a default value is explicitly provided, return that instead in the `null`
46
+ * case.
26
47
  */
27
48
 
28
- export function nullable(decoder) {
29
- return either(null_, decoder);
30
- }
31
- var undefined_or_null = define(function (blob, ok, err) {
32
- return blob === undefined || blob === null ? ok(blob) : // Combine error message into a single line for readability
33
- err('Must be undefined or null');
34
- });
49
+
50
+ export var nullable = _maybeish(null_);
51
+ /**
52
+ * Accepts whatever the given decoder accepts, or `undefined`.
53
+ *
54
+ * If a default value is explicitly provided, return that instead in the
55
+ * `undefined` case.
56
+ */
57
+
58
+ export var optional = _maybeish(undefined_);
35
59
  /**
36
60
  * Accepts whatever the given decoder accepts, or `null`, or `undefined`.
61
+ *
62
+ * If a default value is explicitly provided, return that instead in the
63
+ * `null`/`undefined` case.
37
64
  */
38
65
 
39
- export function maybe(decoder) {
40
- return either(undefined_or_null, decoder);
41
- }
66
+ export var maybe = _maybeish(undefined_or_null);
42
67
  /**
43
68
  * Accepts only the given constant value.
44
69
  */
@@ -2,8 +2,8 @@
2
2
 
3
3
  import { annotateObject, merge, updateText } from '../annotate';
4
4
  import { define } from '../Decoder';
5
- import type { _Any } from '../_utils';
6
5
  import type { Annotation } from '../annotate';
6
+ import type { _Any as AnyDecoder } from '../_utils';
7
7
  import type { Decoder, DecodeResult } from '../Decoder';
8
8
 
9
9
  function subtract(xs: Set<string>, ys: Set<string>): Set<string> {
@@ -52,7 +52,7 @@ export const pojo: Decoder<{| [string]: mixed |}> = define((blob, ok, err) =>
52
52
  * Accepts objects with fields matching the given decoders. Extra fields that
53
53
  * exist on the input object are ignored and will not be returned.
54
54
  */
55
- export function object<O: { +[field: string]: Decoder<_Any>, ... }>(
55
+ export function object<O: { +[field: string]: AnyDecoder, ... }>(
56
56
  decodersByKey: O,
57
57
  ): Decoder<$ObjMap<O, <T>(Decoder<T>) => T>> {
58
58
  // Compute this set at decoder definition time
@@ -134,7 +134,7 @@ export function object<O: { +[field: string]: Decoder<_Any>, ... }>(
134
134
  * Like `object()`, but will reject inputs that contain extra fields that are
135
135
  * not specified explicitly.
136
136
  */
137
- export function exact<O: { +[field: string]: Decoder<_Any>, ... }>(
137
+ export function exact<O: { +[field: string]: AnyDecoder, ... }>(
138
138
  decodersByKey: O,
139
139
  ): Decoder<$ObjMap<$Exact<O>, <T>(Decoder<T>) => T>> {
140
140
  // Compute this set at decoder definition time
@@ -160,7 +160,7 @@ export function exact<O: { +[field: string]: Decoder<_Any>, ... }>(
160
160
  * Like `object()`, but will pass through any extra fields on the input object
161
161
  * unvalidated that will thus be of `unknown` type statically.
162
162
  */
163
- export function inexact<O: { +[field: string]: Decoder<_Any> }>(
163
+ export function inexact<O: { +[field: string]: AnyDecoder }>(
164
164
  decodersByKey: O,
165
165
  ): Decoder<$ObjMap<O, <T>(Decoder<T>) => T> & { +[string]: mixed }> {
166
166
  return pojo.then((plainObj) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "decoders",
3
- "version": "2.0.0-beta11",
3
+ "version": "2.0.0-beta12",
4
4
  "description": "Elegant and battle-tested validation library for type-safe input data (for TypeScript and Flow)",
5
5
  "license": "MIT",
6
6
  "repository": {