decoders 2.0.0-beta5 → 2.0.0-beta9

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.
Files changed (139) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/NotSupportedTSVersion.d.ts +1 -0
  3. package/README.md +934 -387
  4. package/_guard.d.ts +7 -0
  5. package/_guard.js +2 -6
  6. package/_guard.js.flow +3 -3
  7. package/{_esm/_guard.js → _guard.mjs} +3 -3
  8. package/_types.d.ts +13 -0
  9. package/{_esm/_types.js → _types.mjs} +0 -0
  10. package/_utils.d.ts +10 -0
  11. package/_utils.js +1 -1
  12. package/_utils.js.flow +3 -3
  13. package/{_esm/_utils.js → _utils.mjs} +1 -1
  14. package/annotate.d.ts +62 -0
  15. package/{_esm/annotate.js → annotate.mjs} +0 -0
  16. package/core/_helpers.d.ts +79 -0
  17. package/core/array.d.ts +8 -0
  18. package/core/array.js +19 -12
  19. package/core/array.js.flow +15 -11
  20. package/{_esm/core/array.js → core/array.mjs} +19 -10
  21. package/core/boolean.d.ts +5 -0
  22. package/core/boolean.js +5 -9
  23. package/core/boolean.js.flow +5 -7
  24. package/{_esm/core/boolean.js → core/boolean.mjs} +7 -7
  25. package/core/composition.d.ts +18 -0
  26. package/core/composition.js +41 -15
  27. package/core/composition.js.flow +41 -10
  28. package/core/composition.mjs +70 -0
  29. package/core/constants.d.ts +11 -0
  30. package/core/constants.js +6 -10
  31. package/core/constants.js.flow +7 -9
  32. package/{_esm/core/constants.js → core/constants.mjs} +7 -7
  33. package/core/date.d.ts +4 -0
  34. package/core/date.js +5 -9
  35. package/core/date.js.flow +4 -6
  36. package/{_esm/core/date.js → core/date.mjs} +7 -7
  37. package/core/describe.d.ts +3 -0
  38. package/core/describe.js +2 -6
  39. package/core/describe.js.flow +2 -2
  40. package/{_esm/core/describe.js → core/describe.mjs} +3 -3
  41. package/core/dispatch.d.ts +8 -0
  42. package/core/dispatch.js +11 -13
  43. package/core/dispatch.js.flow +13 -12
  44. package/{_esm/core/dispatch.js → core/dispatch.mjs} +12 -11
  45. package/core/either.d.ts +66 -0
  46. package/core/either.js +34 -50
  47. package/core/either.js.flow +40 -86
  48. package/core/either.mjs +90 -0
  49. package/core/fail.d.ts +3 -0
  50. package/core/fail.js +2 -6
  51. package/core/fail.js.flow +2 -2
  52. package/{_esm/core/fail.js → core/fail.mjs} +3 -3
  53. package/core/instanceOf.d.ts +3 -0
  54. package/core/instanceOf.js +2 -6
  55. package/core/instanceOf.js.flow +3 -3
  56. package/core/instanceOf.mjs +8 -0
  57. package/core/json.d.ts +11 -0
  58. package/core/json.js +3 -3
  59. package/core/json.js.flow +3 -3
  60. package/core/json.mjs +15 -0
  61. package/core/lazy.d.ts +3 -0
  62. package/{_esm/core/lazy.js → core/lazy.mjs} +0 -0
  63. package/core/number.d.ts +6 -0
  64. package/core/number.js +9 -13
  65. package/core/number.js.flow +18 -12
  66. package/core/number.mjs +25 -0
  67. package/core/object.d.ts +38 -0
  68. package/core/object.js +66 -13
  69. package/core/object.js.flow +84 -28
  70. package/{_esm/core/object.js → core/object.mjs} +64 -11
  71. package/core/optional.d.ts +5 -0
  72. package/core/optional.js +4 -8
  73. package/core/optional.js.flow +3 -3
  74. package/{_esm/core/optional.js → core/optional.mjs} +6 -6
  75. package/core/string.d.ts +13 -0
  76. package/core/string.js +31 -49
  77. package/core/string.js.flow +29 -39
  78. package/core/string.mjs +58 -0
  79. package/core/tuple.d.ts +30 -0
  80. package/core/tuple.js +30 -149
  81. package/core/tuple.js.flow +33 -197
  82. package/core/tuple.mjs +45 -0
  83. package/format.d.ts +4 -0
  84. package/{format/inline.js → format.js} +6 -1
  85. package/{_esm/format/inline.js.flow → format.js.flow} +6 -2
  86. package/{_esm/format/inline.js → format.mjs} +4 -1
  87. package/index.d.ts +42 -0
  88. package/index.js +33 -42
  89. package/index.js.flow +17 -18
  90. package/{_esm/index.js → index.mjs} +18 -19
  91. package/package.json +15 -3
  92. package/result.d.ts +39 -0
  93. package/result.js +9 -90
  94. package/result.js.flow +11 -87
  95. package/result.mjs +81 -0
  96. package/_esm/_guard.js.flow +0 -20
  97. package/_esm/_types.js.flow +0 -20
  98. package/_esm/_utils.js.flow +0 -97
  99. package/_esm/annotate.js.flow +0 -218
  100. package/_esm/core/array.js.flow +0 -103
  101. package/_esm/core/boolean.js.flow +0 -29
  102. package/_esm/core/composition.js +0 -42
  103. package/_esm/core/composition.js.flow +0 -43
  104. package/_esm/core/constants.js.flow +0 -46
  105. package/_esm/core/date.js.flow +0 -40
  106. package/_esm/core/describe.js.flow +0 -17
  107. package/_esm/core/dispatch.js.flow +0 -58
  108. package/_esm/core/either.js +0 -90
  109. package/_esm/core/either.js.flow +0 -151
  110. package/_esm/core/fail.js.flow +0 -12
  111. package/_esm/core/instanceOf.js +0 -8
  112. package/_esm/core/instanceOf.js.flow +0 -20
  113. package/_esm/core/json.js +0 -15
  114. package/_esm/core/json.js.flow +0 -28
  115. package/_esm/core/lazy.js.flow +0 -15
  116. package/_esm/core/mapping.js +0 -54
  117. package/_esm/core/mapping.js.flow +0 -54
  118. package/_esm/core/number.js +0 -25
  119. package/_esm/core/number.js.flow +0 -34
  120. package/_esm/core/object.js.flow +0 -203
  121. package/_esm/core/optional.js.flow +0 -41
  122. package/_esm/core/string.js +0 -76
  123. package/_esm/core/string.js.flow +0 -82
  124. package/_esm/core/tuple.js +0 -155
  125. package/_esm/core/tuple.js.flow +0 -215
  126. package/_esm/format/index.js +0 -2
  127. package/_esm/format/index.js.flow +0 -4
  128. package/_esm/format/short.js +0 -4
  129. package/_esm/format/short.js.flow +0 -8
  130. package/_esm/index.js.flow +0 -63
  131. package/_esm/result.js +0 -148
  132. package/_esm/result.js.flow +0 -174
  133. package/core/mapping.js +0 -67
  134. package/core/mapping.js.flow +0 -54
  135. package/format/index.js +0 -12
  136. package/format/index.js.flow +0 -4
  137. package/format/inline.js.flow +0 -122
  138. package/format/short.js +0 -10
  139. package/format/short.js.flow +0 -8
@@ -1,8 +1,8 @@
1
1
  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); }
2
2
 
3
- import * as Result from '../result';
4
- import { annotate, annotateObject, merge, updateText } from '../annotate';
5
- import { compose, map } from './composition';
3
+ import { annotate, annotateObject, merge, updateText } from '../annotate.mjs';
4
+ import { compose, transform } from './composition.mjs';
5
+ import { err, ok } from '../result.mjs';
6
6
 
7
7
  function isPojo(o) {
8
8
  return o !== null && o !== undefined && typeof o === 'object' && // This still seems to be the only reliable way to determine whether
@@ -22,7 +22,7 @@ function subtract(xs, ys) {
22
22
  }
23
23
 
24
24
  export var pojo = function pojo(blob) {
25
- return isPojo(blob) ? Result.ok( // NOTE:
25
+ return isPojo(blob) ? ok( // NOTE:
26
26
  // Since Flow 0.98, typeof o === 'object' refines to
27
27
  // {| +[string]: mixed |}
28
28
  // instead of
@@ -36,7 +36,7 @@ export var pojo = function pojo(blob) {
36
36
  // way to turn a read-only Object to a writeable one in ES6 seems
37
37
  // to be to use object-spread. (Going off this benchmark:
38
38
  // https://thecodebarbarian.com/object-assign-vs-object-spread.html)
39
- _extends({}, blob)) : Result.err(annotate(blob, 'Must be an object'));
39
+ _extends({}, blob)) : err(annotate(blob, 'Must be an object'));
40
40
  };
41
41
  /**
42
42
  * Given a mapping of fields-to-decoders, builds a decoder for an object type.
@@ -74,7 +74,7 @@ export function object(mapping) {
74
74
  var rawValue = blob[key];
75
75
  var result = decoder(rawValue);
76
76
 
77
- if (result.type === 'ok') {
77
+ if (result.ok) {
78
78
  var value = result.value;
79
79
 
80
80
  if (value !== undefined) {
@@ -122,10 +122,10 @@ export function object(mapping) {
122
122
  objAnn = updateText(objAnn, "Missing " + pluralized + ": " + errMsg);
123
123
  }
124
124
 
125
- return Result.err(objAnn);
125
+ return err(objAnn);
126
126
  }
127
127
 
128
- return Result.ok(record);
128
+ return ok(record);
129
129
  });
130
130
  }
131
131
  export function exact(mapping) {
@@ -136,10 +136,10 @@ export function exact(mapping) {
136
136
  var superfluous = subtract(actual, allowed);
137
137
 
138
138
  if (superfluous.size > 0) {
139
- return Result.err(annotate(blob, "Superfluous keys: " + Array.from(superfluous).join(', ')));
139
+ return err(annotate(blob, "Superfluous keys: " + Array.from(superfluous).join(', ')));
140
140
  }
141
141
 
142
- return Result.ok(blob);
142
+ return ok(blob);
143
143
  }); // Defer to the "object" decoder for doing the real decoding work. Since
144
144
  // we made sure there are no superfluous keys in this structure, it's now
145
145
  // safe to force-cast it to an $Exact<> type.
@@ -150,7 +150,7 @@ export function exact(mapping) {
150
150
  export function inexact(mapping) {
151
151
  return compose(pojo, function (blob) {
152
152
  var allkeys = new Set(Object.keys(blob));
153
- var decoder = map(object(mapping), function (safepart) {
153
+ var decoder = transform(object(mapping), function (safepart) {
154
154
  var safekeys = new Set(Object.keys(mapping)); // To account for hard-coded keys that aren't part of the input
155
155
 
156
156
  safekeys.forEach(function (k) {
@@ -172,4 +172,57 @@ export function inexact(mapping) {
172
172
  });
173
173
  return decoder(blob);
174
174
  });
175
+ }
176
+ /**
177
+ * Like mapping(), but returns an object rather than a Map instance.
178
+ */
179
+
180
+ export function dict(decoder) {
181
+ return compose(pojo, function (blob) {
182
+ var rv = {};
183
+ var errors = null;
184
+ Object.keys(blob).forEach(function (key) {
185
+ var value = blob[key];
186
+ var result = decoder(value);
187
+
188
+ if (result.ok) {
189
+ if (errors === null) {
190
+ rv[key] = result.value;
191
+ }
192
+ } else {
193
+ rv = {}; // Clear the success value so it can get garbage collected early
194
+
195
+ if (errors === null) {
196
+ errors = {};
197
+ }
198
+
199
+ errors[key] = result.error;
200
+ }
201
+ });
202
+
203
+ if (errors !== null) {
204
+ return err(merge(annotateObject(blob), errors));
205
+ } else {
206
+ return ok(rv);
207
+ }
208
+ });
209
+ }
210
+ /**
211
+ * Given an object, will decode a Map of string keys to whatever values.
212
+ *
213
+ * For example, given a decoder for a Person, we can verify a Person lookup
214
+ * table structure (of type Map<string, Person>) like so:
215
+ *
216
+ * mapping(person)
217
+ *
218
+ */
219
+
220
+ export function mapping(decoder) {
221
+ return transform(dict(decoder), function (obj) {
222
+ return new Map( // This is effectively Object.entries(obj), but in a way that Flow
223
+ // will know the types are okay
224
+ Object.keys(obj).map(function (key) {
225
+ return [key, obj[key]];
226
+ }));
227
+ });
175
228
  }
@@ -0,0 +1,5 @@
1
+ import { Decoder } from '../_types';
2
+
3
+ export function optional<T>(decoder: Decoder<T>): Decoder<T | undefined>;
4
+ export function nullable<T>(decoder: Decoder<T>): Decoder<T | null>;
5
+ export function maybe<T>(decoder: Decoder<T>): Decoder<T | null | undefined>;
package/core/optional.js CHANGED
@@ -5,17 +5,13 @@ exports.maybe = maybe;
5
5
  exports.nullable = nullable;
6
6
  exports.optional = optional;
7
7
 
8
- var Result = _interopRequireWildcard(require("../result"));
9
-
10
8
  var _annotate = require("../annotate");
11
9
 
12
10
  var _either = require("./either");
13
11
 
14
- var _constants = require("./constants");
15
-
16
- 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); }
12
+ var _result = require("../result");
17
13
 
18
- 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; }
14
+ var _constants = require("./constants");
19
15
 
20
16
  /**
21
17
  * Builds a Decoder that returns Ok for either `undefined` or `T` values,
@@ -41,8 +37,8 @@ function nullable(decoder) {
41
37
 
42
38
 
43
39
  var undefined_or_null = function undefined_or_null(blob) {
44
- return blob === undefined || blob === null ? Result.ok(blob) : // Combine error message into a single line
45
- Result.err((0, _annotate.annotate)(blob, 'Must be undefined or null'));
40
+ return blob === undefined || blob === null ? (0, _result.ok)(blob) : // Combine error message into a single line
41
+ (0, _result.err)((0, _annotate.annotate)(blob, 'Must be undefined or null'));
46
42
  };
47
43
  /**
48
44
  * Decoder that only returns Ok for `null` or `undefined` inputs.
@@ -1,8 +1,8 @@
1
1
  // @flow strict
2
2
 
3
- import * as Result from '../result';
4
3
  import { annotate } from '../annotate';
5
4
  import { either } from './either';
5
+ import { err, ok } from '../result';
6
6
  import { null_, undefined_ } from './constants';
7
7
  import type { Decoder } from '../_types';
8
8
 
@@ -29,9 +29,9 @@ export function nullable<T>(decoder: Decoder<T>): Decoder<null | T> {
29
29
  */
30
30
  const undefined_or_null: Decoder<null | void> = (blob: mixed) =>
31
31
  blob === undefined || blob === null
32
- ? Result.ok(blob)
32
+ ? ok(blob)
33
33
  : // Combine error message into a single line
34
- Result.err(annotate(blob, 'Must be undefined or null'));
34
+ err(annotate(blob, 'Must be undefined or null'));
35
35
 
36
36
  /**
37
37
  * Decoder that only returns Ok for `null` or `undefined` inputs.
@@ -1,7 +1,7 @@
1
- import * as Result from '../result';
2
- import { annotate } from '../annotate';
3
- import { either } from './either';
4
- import { null_, undefined_ } from './constants';
1
+ import { annotate } from '../annotate.mjs';
2
+ import { either } from './either.mjs';
3
+ import { err, ok } from '../result.mjs';
4
+ import { null_, undefined_ } from './constants.mjs';
5
5
 
6
6
  /**
7
7
  * Builds a Decoder that returns Ok for either `undefined` or `T` values,
@@ -25,8 +25,8 @@ export function nullable(decoder) {
25
25
  */
26
26
 
27
27
  var undefined_or_null = function undefined_or_null(blob) {
28
- return blob === undefined || blob === null ? Result.ok(blob) : // Combine error message into a single line
29
- Result.err(annotate(blob, 'Must be undefined or null'));
28
+ return blob === undefined || blob === null ? ok(blob) : // Combine error message into a single line
29
+ err(annotate(blob, 'Must be undefined or null'));
30
30
  };
31
31
  /**
32
32
  * Decoder that only returns Ok for `null` or `undefined` inputs.
@@ -0,0 +1,13 @@
1
+ /// <reference lib="dom" />
2
+
3
+ import { Decoder } from '../_types';
4
+
5
+ export const string: Decoder<string>;
6
+ export const nonEmptyString: Decoder<string>;
7
+ export function regex(regex: RegExp, msg: string): Decoder<string>;
8
+ export const email: Decoder<string>;
9
+ export const url: Decoder<URL>;
10
+ export const httpsUrl: Decoder<URL>;
11
+ export const uuid: Decoder<string>;
12
+ export const uuidv1: Decoder<string>;
13
+ export const uuidv4: Decoder<string>;
package/core/string.js CHANGED
@@ -1,19 +1,19 @@
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
- exports.url = exports.string = void 0;
7
-
8
- var Result = _interopRequireWildcard(require("../result"));
6
+ exports.uuidv4 = exports.uuidv1 = exports.uuid = exports.url = exports.string = void 0;
9
7
 
10
8
  var _annotate = require("../annotate");
11
9
 
12
- var _composition = require("./composition");
10
+ var _either = require("./either");
13
11
 
14
- 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); }
12
+ var _result = require("../result");
15
13
 
16
- 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; }
14
+ var _instanceOf = require("./instanceOf");
15
+
16
+ var _composition = require("./composition");
17
17
 
18
18
  /** Match groups in this regex:
19
19
  * \1 - the scheme
@@ -22,15 +22,13 @@ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj &&
22
22
  * \4 - the port (optional)
23
23
  * \5 - the path (optional)
24
24
  */
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]*)?)?$/; // The URL schemes the url() decoder accepts by default
26
-
27
- 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]*)?)?$/;
28
26
  /**
29
27
  * Decoder that only returns Ok for string inputs. Err otherwise.
30
28
  */
31
29
 
32
30
  var string = function string(blob) {
33
- return typeof blob === 'string' ? Result.ok(blob) : Result.err((0, _annotate.annotate)(blob, 'Must be string'));
31
+ return typeof blob === 'string' ? (0, _result.ok)(blob) : (0, _result.err)((0, _annotate.annotate)(blob, 'Must be string'));
34
32
  };
35
33
  /**
36
34
  * Decoder that only returns Ok for non-empty string inputs. Err otherwise.
@@ -48,9 +46,9 @@ var nonEmptyString = regex(/\S/, 'Must be non-empty string');
48
46
  exports.nonEmptyString = nonEmptyString;
49
47
 
50
48
  function regex(regex, msg) {
51
- return (0, _composition.compose)(string, (0, _composition.predicate)(function (s) {
49
+ return (0, _composition.predicate)(string, function (s) {
52
50
  return regex.test(s);
53
- }, msg));
51
+ }, msg);
54
52
  }
55
53
  /**
56
54
  * Decoder that only returns Ok for string inputs that match the almost perfect
@@ -59,40 +57,24 @@ function regex(regex, msg) {
59
57
 
60
58
 
61
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');
62
- /**
63
- * Decoder that only returns Ok for string inputs that match URLs of the
64
- * expected scheme. Defaults to only accept HTTPS URLs. Err otherwise.
65
- *
66
- * Variants that can be used:
67
- *
68
- * - url() accepts only https:// URLs
69
- * - url([]) accepts any URL scheme
70
- * - url(['http']) accepts only HTTP
71
- * - url(['https', 'git+ssh']) accepts both https:// and git+ssh:// URLs
72
- */
73
-
74
60
  exports.email = email;
75
-
76
- var url = function url(schemes) {
77
- if (schemes === void 0) {
78
- schemes = DEFAULT_SCHEMES;
79
- }
80
-
81
- return (0, _composition.compose)(string, function (value) {
82
- var matches = value.match(url_re);
83
-
84
- if (!matches) {
85
- return Result.err((0, _annotate.annotate)(value, 'Must be URL'));
86
- } else {
87
- var scheme = matches[1];
88
-
89
- if (schemes.length === 0 || schemes.includes(scheme.toLowerCase())) {
90
- return Result.ok(value);
91
- } else {
92
- return Result.err((0, _annotate.annotate)(value, "URL scheme must be any of: " + schemes.join(', ')));
93
- }
94
- }
95
- });
96
- };
97
-
98
- exports.url = url;
61
+ var url = (0, _either.either)((0, _composition.transform)(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;
69
+ var uuid = regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, 'Must be uuid');
70
+ exports.uuid = uuid;
71
+ var uuidv1 = // https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_1_(date-time_and_MAC_address)
72
+ (0, _composition.predicate)(uuid, function (value) {
73
+ return value[14] === '1';
74
+ }, 'Must be uuidv1');
75
+ exports.uuidv1 = uuidv1;
76
+ var uuidv4 = // https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
77
+ (0, _composition.predicate)(uuid, function (value) {
78
+ return value[14] === '4';
79
+ }, 'Must be uuidv4');
80
+ exports.uuidv4 = uuidv4;
@@ -1,8 +1,10 @@
1
1
  // @flow strict
2
2
 
3
- import * as Result from '../result';
4
3
  import { annotate } from '../annotate';
5
- import { compose, predicate } from './composition';
4
+ import { either } from './either';
5
+ import { err, ok } from '../result';
6
+ import { instanceOf } from './instanceOf';
7
+ import { predicate, transform } from './composition';
6
8
  import type { Decoder } from '../_types';
7
9
 
8
10
  /** Match groups in this regex:
@@ -15,16 +17,11 @@ import type { Decoder } from '../_types';
15
17
  const url_re =
16
18
  /^([A-Za-z]{3,9}(?:[+][A-Za-z]{3,9})?):\/\/(?:([-;:&=+$,\w]+)@)?(?:([A-Za-z0-9.-]+)(?::([0-9]{2,5}))?)(\/(?:[-+~%/.,\w]*)?(?:\?[-+=&;%@.,\w]*)?(?:#[.,!/\w]*)?)?$/;
17
19
 
18
- // The URL schemes the url() decoder accepts by default
19
- const DEFAULT_SCHEMES = ['https'];
20
-
21
20
  /**
22
21
  * Decoder that only returns Ok for string inputs. Err otherwise.
23
22
  */
24
23
  export const string: Decoder<string> = (blob: mixed) => {
25
- return typeof blob === 'string'
26
- ? Result.ok(blob)
27
- : Result.err(annotate(blob, 'Must be string'));
24
+ return typeof blob === 'string' ? ok(blob) : err(annotate(blob, 'Must be string'));
28
25
  };
29
26
 
30
27
  /**
@@ -38,10 +35,7 @@ export const nonEmptyString: Decoder<string> = regex(/\S/, 'Must be non-empty st
38
35
  * before testing the regex.
39
36
  */
40
37
  export function regex(regex: RegExp, msg: string): Decoder<string> {
41
- return compose(
42
- string,
43
- predicate((s) => regex.test(s), msg),
44
- );
38
+ return predicate(string, (s) => regex.test(s), msg);
45
39
  }
46
40
 
47
41
  /**
@@ -53,30 +47,26 @@ export const email: Decoder<string> = regex(
53
47
  'Must be email',
54
48
  );
55
49
 
56
- /**
57
- * Decoder that only returns Ok for string inputs that match URLs of the
58
- * expected scheme. Defaults to only accept HTTPS URLs. Err otherwise.
59
- *
60
- * Variants that can be used:
61
- *
62
- * - url() accepts only https:// URLs
63
- * - url([]) accepts any URL scheme
64
- * - url(['http']) accepts only HTTP
65
- * - url(['https', 'git+ssh']) accepts both https:// and git+ssh:// URLs
66
- */
67
- export const url = (schemes: $ReadOnlyArray<string> = DEFAULT_SCHEMES): Decoder<string> =>
68
- compose(string, (value: string) => {
69
- const matches = value.match(url_re);
70
- if (!matches) {
71
- return Result.err(annotate(value, 'Must be URL'));
72
- } else {
73
- const scheme = matches[1];
74
- if (schemes.length === 0 || schemes.includes(scheme.toLowerCase())) {
75
- return Result.ok(value);
76
- } else {
77
- return Result.err(
78
- annotate(value, `URL scheme must be any of: ${schemes.join(', ')}`),
79
- );
80
- }
81
- }
82
- });
50
+ export const url: Decoder<URL> = either(
51
+ transform(regex(url_re, 'Must be URL'), (value) => new URL(value)),
52
+ instanceOf(URL),
53
+ );
54
+
55
+ export const httpsUrl: Decoder<URL> = predicate(
56
+ url,
57
+ (value) => value.protocol === 'https:',
58
+ 'Must be an HTTPS URL',
59
+ );
60
+
61
+ export const uuid: Decoder<string> = regex(
62
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
63
+ 'Must be uuid',
64
+ );
65
+
66
+ export const uuidv1: Decoder<string> =
67
+ // https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_1_(date-time_and_MAC_address)
68
+ predicate(uuid, (value) => value[14] === '1', 'Must be uuidv1');
69
+
70
+ export const uuidv4: Decoder<string> =
71
+ // https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
72
+ predicate(uuid, (value) => value[14] === '4', 'Must be uuidv4');
@@ -0,0 +1,58 @@
1
+ import { annotate } from '../annotate.mjs';
2
+ import { either } from './either.mjs';
3
+ import { err, ok } from '../result.mjs';
4
+ import { instanceOf } from './instanceOf.mjs';
5
+ import { predicate, transform } from './composition.mjs';
6
+
7
+ /** Match groups in this regex:
8
+ * \1 - the scheme
9
+ * \2 - the username/password (optional)
10
+ * \3 - the host
11
+ * \4 - the port (optional)
12
+ * \5 - the path (optional)
13
+ */
14
+ 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]*)?)?$/;
15
+ /**
16
+ * Decoder that only returns Ok for string inputs. Err otherwise.
17
+ */
18
+
19
+ export var string = function string(blob) {
20
+ return typeof blob === 'string' ? ok(blob) : err(annotate(blob, 'Must be string'));
21
+ };
22
+ /**
23
+ * Decoder that only returns Ok for non-empty string inputs. Err otherwise.
24
+ */
25
+
26
+ export var nonEmptyString = regex(/\S/, 'Must be non-empty string');
27
+ /**
28
+ * Decoder that only returns Ok for string inputs that match the regular
29
+ * expression. Err otherwise. Will always validate that the input is a string
30
+ * before testing the regex.
31
+ */
32
+
33
+ export function regex(regex, msg) {
34
+ return predicate(string, function (s) {
35
+ return regex.test(s);
36
+ }, msg);
37
+ }
38
+ /**
39
+ * Decoder that only returns Ok for string inputs that match the almost perfect
40
+ * email regex, taken from http://emailregex.com. Err otherwise.
41
+ */
42
+
43
+ export 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');
44
+ export var url = either(transform(regex(url_re, 'Must be URL'), function (value) {
45
+ return new URL(value);
46
+ }), instanceOf(URL));
47
+ export var httpsUrl = predicate(url, function (value) {
48
+ return value.protocol === 'https:';
49
+ }, 'Must be an HTTPS URL');
50
+ export var uuid = regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, 'Must be uuid');
51
+ export var uuidv1 = // https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_1_(date-time_and_MAC_address)
52
+ predicate(uuid, function (value) {
53
+ return value[14] === '1';
54
+ }, 'Must be uuidv1');
55
+ export var uuidv4 = // https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random)
56
+ predicate(uuid, function (value) {
57
+ return value[14] === '4';
58
+ }, 'Must be uuidv4');
@@ -0,0 +1,30 @@
1
+ import { Decoder } from '../_types';
2
+
3
+ export function tuple<A>(a: Decoder<A>): Decoder<[A]>;
4
+ export function tuple<A, B>(a: Decoder<A>, b: Decoder<B>): Decoder<[A, B]>;
5
+ export function tuple<A, B, C>(
6
+ a: Decoder<A>,
7
+ b: Decoder<B>,
8
+ c: Decoder<C>,
9
+ ): Decoder<[A, B, C]>;
10
+ export function tuple<A, B, C, D>(
11
+ a: Decoder<A>,
12
+ b: Decoder<B>,
13
+ c: Decoder<C>,
14
+ d: Decoder<D>,
15
+ ): Decoder<[A, B, C, D]>;
16
+ export function tuple<A, B, C, D, E>(
17
+ a: Decoder<A>,
18
+ b: Decoder<B>,
19
+ c: Decoder<C>,
20
+ d: Decoder<D>,
21
+ e: Decoder<E>,
22
+ ): Decoder<[A, B, C, D, E]>;
23
+ export function tuple<A, B, C, D, E, F>(
24
+ a: Decoder<A>,
25
+ b: Decoder<B>,
26
+ c: Decoder<C>,
27
+ d: Decoder<D>,
28
+ e: Decoder<E>,
29
+ f: Decoder<F>,
30
+ ): Decoder<[A, B, C, D, E, F]>;