validator-fns 3.0.0-next.1 → 3.0.0-next.3

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/README.md CHANGED
@@ -10,9 +10,11 @@ Small asynchronous validation library. Tries to be as small as possible and use
10
10
 
11
11
  - [Install](#install)
12
12
  - [Usage](#usage)
13
+ - [Migrating from 2.x](#migrating-from-2x)
13
14
  - [API Reference](#api-reference)
14
15
  - [Validation result](#validation-result)
15
16
  - [Validation error message](#validation-error-message)
17
+ - [Required and optional values](#required-and-optional-values)
16
18
  - [Validation types](#validation-types)
17
19
  - [Validation tests](#validation-tests)
18
20
  - [Custom validation](#custom-validation)
@@ -30,7 +32,9 @@ yarn add validator-fns
30
32
 
31
33
  ## Usage
32
34
 
33
- This is a fairly exhaustive example of what's possible with `validator-fns`.
35
+ > **Upgrading from 2.x?** Every validator is now required unless you wrap it in `optional()`, and `required()` is gone. See [Migrating from 2.x](#migrating-from-2x).
36
+
37
+ Every type validator rejects a missing value unless it's wrapped in `optional()`. This is a fairly exhaustive example of what's possible with `validator-fns`.
34
38
 
35
39
  <details><summary>example</summary>
36
40
 
@@ -38,6 +42,7 @@ This is a fairly exhaustive example of what's possible with `validator-fns`.
38
42
  import {
39
43
  array,
40
44
  boolean,
45
+ createValidator,
41
46
  date,
42
47
  email,
43
48
  exact,
@@ -50,7 +55,7 @@ import {
50
55
  number,
51
56
  object,
52
57
  oneOf,
53
- required,
58
+ optional,
54
59
  string,
55
60
  url,
56
61
  valid,
@@ -64,7 +69,7 @@ const TEN_DAYS = ONE_DAY * 10;
64
69
  const now = new Date();
65
70
  const tenDaysFromNow = new Date(now.getTime() + TEN_DAYS);
66
71
 
67
- function customValidator(value, field) {
72
+ const customValidator = createValidator(async (value, field) => {
68
73
  if (value === 'hello') {
69
74
  return valid({ value, field });
70
75
  }
@@ -73,24 +78,23 @@ function customValidator(value, field) {
73
78
  value,
74
79
  field,
75
80
  });
76
- }
81
+ });
77
82
 
78
83
  const validate = object({
79
- firstName: string(
80
- { default: '', trim: true },
81
- required('First name is required.'),
82
- ),
84
+ firstName: string({
85
+ trim: true,
86
+ requiredMessage: 'First name is required.',
87
+ }),
83
88
  emailAddress: string(
84
- required('Email address is required.'),
89
+ { requiredMessage: 'Email address is required.' },
85
90
  email('Must be a valid email address.'),
86
91
  ),
87
92
  age: number(
88
- { round: 'floor' },
89
- required('Age is required.'),
93
+ { round: 'floor', requiredMessage: 'Age is required.' },
90
94
  integer('Age must be a whole number.'),
91
95
  min(18, 'Must be at least {min} years old.'),
92
96
  ),
93
- homepage: string(url('Must be a valid URL.', ['http:', 'https:'])),
97
+ homepage: optional(string(url('Must be a valid URL.', ['http:', 'https:']))),
94
98
  fruit: string(
95
99
  { default: 'apple' },
96
100
  oneOf(
@@ -99,9 +103,8 @@ const validate = object({
99
103
  ),
100
104
  ),
101
105
  favoriteCarMakers: array(
102
- { default: [] },
103
106
  string(
104
- required('Car brand is required.'),
107
+ { requiredMessage: 'Car brand is required.' },
105
108
  oneOf(
106
109
  [
107
110
  'BMW',
@@ -119,19 +122,24 @@ const validate = object({
119
122
  'Must be a known car brand.',
120
123
  ),
121
124
  ),
125
+ { default: [] },
122
126
  min(2, 'Must pick at least two.'),
123
127
  max(4, 'Must pick at most four.'),
124
128
  ),
125
- startDate: date(
126
- minDate(now, 'Must be on or after today.'),
127
- maxDate(tenDaysFromNow, 'Must be at most ten days from now.'),
129
+ startDate: optional(
130
+ date(
131
+ minDate(now, 'Must be on or after today.'),
132
+ maxDate(tenDaysFromNow, 'Must be at most ten days from now.'),
133
+ ),
128
134
  ),
129
- optIn: boolean(required('Must decide to opt in or out.')),
135
+ optIn: boolean({ requiredMessage: 'Must decide to opt in or out.' }),
130
136
  notRobot: number(
131
- required('Must enter the correct answer.'),
137
+ { requiredMessage: 'Must enter the correct answer.' },
132
138
  exact(42, 'This is the answer.'),
133
139
  ),
134
- postalCode: string(matches(/^[0-9]{5}$/, 'Must be a five-digit number.')),
140
+ postalCode: optional(
141
+ string(matches(/^[0-9]{5}$/, 'Must be a five-digit number.')),
142
+ ),
135
143
  custom: customValidator,
136
144
  });
137
145
 
@@ -161,6 +169,60 @@ if (result.state === 'valid') {
161
169
 
162
170
  </details>
163
171
 
172
+ ## Migrating from 2.x
173
+
174
+ Version 3 makes every type validator required by default. In 2.x, a validator without `required()` accepted `undefined`, `null` and the other missing values; in 3.x, it rejects them with `'Required.'`.
175
+
176
+ 1. **Move each `required(message)` into the config as `requiredMessage`**, merging it into a config object that's already there:
177
+
178
+ ```js
179
+ // 2.x
180
+ string(required('Enter a name.'), min(2, 'Too short.'));
181
+ number({ round: 'floor' }, required('Enter your age.'));
182
+ array(string(), required('Pick some.'));
183
+
184
+ // 3.x
185
+ string({ requiredMessage: 'Enter a name.' }, min(2, 'Too short.'));
186
+ number({ round: 'floor', requiredMessage: 'Enter your age.' });
187
+ array(string(), { requiredMessage: 'Pick some.' });
188
+ ```
189
+
190
+ 2. **Wrap each validator that had no `required()` in `optional()`**, or it now rejects a missing value:
191
+
192
+ ```js
193
+ // 2.x
194
+ string(email('Must be an email.'));
195
+
196
+ // 3.x
197
+ optional(string(email('Must be an email.')));
198
+ ```
199
+
200
+ 3. **Replace `required(message, true)`** (nullable): `optional()` accepts `null`, as well as the other missing values.
201
+ 4. **Move `array()`'s config after the item validator**: `array(config, item, ...tests)` becomes `array(item, config, ...tests)`, as in `object(schema, config, ...tests)`. `array(config)` without an item validator stays. Passing the config first with an item validator throws a `TypeError`:
202
+
203
+ ```js
204
+ // 2.x
205
+ array({ default: [] }, string(), min(2, 'Pick two.'));
206
+
207
+ // 3.x
208
+ array(string(), { default: [] }, min(2, 'Pick two.'));
209
+ ```
210
+
211
+ 5. **Check `object()` calls with a second argument**: it's a config object or the first object-level test, as in `object(schema, { requiredMessage }, ...tests)`. A missing object now fails with its `requiredMessage` instead of reporting each field as missing; wrap it in `optional()` to accept one.
212
+ 6. **Replace `standard()`**: remove it around built-in validators, which are Standard Schemas themselves, and change `standard(fn)` to `createValidator(fn)`:
213
+
214
+ ```js
215
+ // 2.x
216
+ standard(object({ email: string() }));
217
+ standard(async (value, field) => valid({ value, field }));
218
+
219
+ // 3.x
220
+ object({ email: string() });
221
+ createValidator(async (value, field) => valid({ value, field }));
222
+ ```
223
+
224
+ 7. **In TypeScript**, a valid result's `value` no longer includes `null` or `undefined` unless the validator is wrapped in `optional()`, and `object()` makes only `optional()` keys optional. `valid()` takes a value of the validator's type, so a custom validator that returns `null` or `undefined` needs them in its type, such as `ValidatorTest<string | undefined>`. `object()` takes one type parameter, the schema, so explicit type arguments such as `object<Schema, Value, Errors>(…)` become `object<Schema>(…)`, or can be left out. `check()`'s `path` must name properties of the parsed object, and an empty path `[]` is a type error; leave the path out instead. Validation tests are typed `ValidationTest<T, E>` and take the validator's value type, so a custom test that takes another type, or a test called directly with `null`, is a type error. A message function passed to `invalid()` gets `value` as `unknown`, and one that reads extra parameters needs them in `extras`.
225
+
164
226
  ## API Reference
165
227
 
166
228
  ### Validation result
@@ -185,26 +247,59 @@ The `message` argument in the validation tests must be a static string, a templa
185
247
  | template string | `'Password must be at least {min} characters.'` |
186
248
  | function | `` ({ value, field, max }) => `${field} must be less than ${max} but was ${value}.` `` |
187
249
 
188
- The parsed `value` is always provided. If available, `field` is also provided. Depending on the validator, additional fields may be available.
250
+ The parsed `value` is always provided. If available, `field` is also provided. Depending on the validator, additional fields may be available, such as `min` for `min()`.
251
+
252
+ In TypeScript, a message function's parameters are typed: `ValidatorMessage<T, X>` gets the value and field, `X & ValidatorMessageParameters<T>`, where `X` holds the test's extra parameters. Each test's exported parameters type, such as `MinValidatorMessageParameters`, still describes everything a message gets, so a message annotated with it fits.
253
+
254
+ ### Required and optional values
255
+
256
+ Every validation type rejects a missing value: `undefined`, `null`, an empty string `''`, `NaN`, or an invalid date. It checks after parsing and `default`, so `string({ trim: true })` treats `' '` as missing, `number()` treats a blank string as missing, and a `default` fills a missing value before the check. The tests only run on present values.
257
+
258
+ The message is `'Required.'` unless the config sets `requiredMessage`, which takes a [validation error message](#validation-error-message):
259
+
260
+ ```js
261
+ await string()('');
262
+ // { state: 'invalid', message: 'Required.', value: '', ... }
263
+
264
+ await number({ requiredMessage: '{field} is required.' })(undefined, 'age');
265
+ // { state: 'invalid', message: 'age is required.', ... }
266
+ ```
267
+
268
+ #### optional(validator)
269
+
270
+ Returns a validator that accepts a missing value. The missing value skips the tests and becomes the result's `value` as parsed, so `optional(number())('')` is valid with `NaN`. Present values go through `validator` as usual. The validator passed in is unchanged.
271
+
272
+ ```js
273
+ const nickname = optional(string(min(2, 'Too short.')));
274
+
275
+ await nickname(undefined); // { state: 'valid', value: undefined, ... }
276
+ await nickname('a'); // { state: 'invalid', message: 'Too short.', ... }
277
+ ```
278
+
279
+ For a custom validator, `optional()` accepts `undefined` and `null` without calling it, and passes every other value to it.
280
+
281
+ In TypeScript, a validator's value type is `T`, and `optional()` adds `null` and `undefined`. In `object()`, a key is optional when its validator's value type includes `undefined`, so `object({ name: string(), age: optional(number()) })` is typed `{ name: string; age?: number | null | undefined }`.
189
282
 
190
283
  ### Validation types
191
284
 
192
- A **validation type** is expected to parse and prepare the value for the provided validation tests. For example, the `string` validation type ensures the provided value is a string, `null` or `undefined`. It must be a function that takes a value of any type and returns a promise which resolves to a validation result (see above).
285
+ A **validation type** is expected to parse and prepare the value for the provided validation tests. For example, the `string` validation type ensures the provided value is a string, and rejects a missing value unless wrapped in [`optional()`](#optionalvalidator). It must be a function that takes a value of any type and returns a promise which resolves to a validation result (see above).
193
286
 
194
- - [array(config?, itemTest, ...tests)](#arrayconfig-itemtest-tests)
287
+ - [array(itemTest?, config?, ...tests)](#arrayitemtest-config-tests)
195
288
  - [boolean(config?, ...tests)](#booleanconfig-tests)
196
289
  - [date(config?, ...tests)](#dateconfig-tests)
197
290
  - [number(config?, ...tests)](#numberconfig-tests)
198
- - [object(schema, ...tests)](#objectschema-tests)
291
+ - [object(schema, config?, ...tests)](#objectschema-config-tests)
199
292
  - [string(config?, ...tests)](#stringconfig-tests)
200
293
 
201
- #### array(config?, itemTest, ...tests)
294
+ Every validation type, and every validator `optional()` returns, is also a [Standard Schema][standard-schema], so libraries that accept one can use it directly. See [createValidator(fn)](#createvalidatorfn) for how results map to issues. In TypeScript, their type is `TypeValidator<T, E>`: a `ValidatorTest<T, E>` with a `~standard` property.
202
295
 
203
- Casts value to an array, `null` or `undefined` if using the default date parser. `config` is optional. `itemTest` is the validation type or test to be applied on each value within the array value. All remaining `tests` are applied to the array as a whole.
296
+ #### array(itemTest?, config?, ...tests)
297
+
298
+ Casts value to an array, or keeps `null` or `undefined` if using the default array parser. `itemTest` is the validation type or test to be applied on each value within the array value. `config` is optional and comes after `itemTest`, as `object()`'s does. All remaining `tests` are applied to the array as a whole. Without `itemTest`, `array(config?)` keeps the items as they are and takes no tests.
204
299
 
205
300
  A valid result's `value` holds each item as `itemTest` returned it, so `array(number())` turns `['1', '2.5']` into `[1, 2.5]`, and `array(string({ trim: true }))` trims each item.
206
301
 
207
- Supported validation tests: `max`, `min`, and `required`.
302
+ Supported validation tests: `max` and `min`.
208
303
 
209
304
  For invalid results, errors are in the `errors` property. It is an array of type:
210
305
 
@@ -216,40 +311,43 @@ Where valid entries are represented as `null` and invalid entries with the error
216
311
 
217
312
  <details><summary>config</summary>
218
313
 
219
- | option | type | description |
220
- | :-------- | :------- | :-------------------------------------------------------------------------------------- |
221
- | `default` | string | Optional. Value used if the parser returns `undefined`, `null`, or an empty array `[]`. |
222
- | `parser` | function | Optional. Used to customize how a value is converted into a string. |
314
+ | option | type | description |
315
+ | :---------------- | :----------------- | :-------------------------------------------------------------------------------------- |
316
+ | `default` | string | Optional. Value used if the parser returns `undefined`, `null`, or an empty array `[]`. |
317
+ | `requiredMessage` | string or function | Optional. Message for a missing value. Defaults to `'Required.'`. |
318
+ | `parser` | function | Optional. Used to customize how a value is converted into a string. |
223
319
 
224
320
  </details>
225
321
 
226
322
  #### boolean(config?, ...tests)
227
323
 
228
- Casts value to a boolean, `null` or `undefined` if using the default boolean parser. `config` is optional.
324
+ Casts value to a boolean, or keeps `null` or `undefined` if using the default boolean parser. `config` is optional.
229
325
 
230
- Supported validation tests: `oneOf` and `required`.
326
+ Supported validation tests: `oneOf`.
231
327
 
232
328
  <details><summary>config</summary>
233
329
 
234
- | option | type | description |
235
- | :-------- | :------- | :------------------------------------------------------------------- |
236
- | `default` | boolean | Optional. Value used if the parser returns `undefined` or `null`. |
237
- | `parser` | function | Optional. Used to customize how a value is converted into a boolean. |
330
+ | option | type | description |
331
+ | :---------------- | :----------------- | :------------------------------------------------------------------- |
332
+ | `default` | boolean | Optional. Value used if the parser returns `undefined` or `null`. |
333
+ | `requiredMessage` | string or function | Optional. Message for a missing value. Defaults to `'Required.'`. |
334
+ | `parser` | function | Optional. Used to customize how a value is converted into a boolean. |
238
335
 
239
336
  </details>
240
337
 
241
338
  #### date(config?, ...tests)
242
339
 
243
- Casts value to a date, `null` or `undefined` if using the default date parser. `config` is optional.
340
+ Casts value to a date, or keeps `null` or `undefined` if using the default date parser. A string that isn't ISO 8601, such as `'tomorrow'`, and input that isn't a `Date`, number, or string, such as `true` or `{}`, give an invalid result with the message `Failed to parse value to date.`. A blank string and an invalid `Date` become an invalid date, which counts as missing. `config` is optional.
244
341
 
245
- Supported validation tests: `maxDate`, `minDate`, `oneOf`, and `required`.
342
+ Supported validation tests: `maxDate`, `minDate`, and `oneOf`.
246
343
 
247
344
  <details><summary>config</summary>
248
345
 
249
- | option | type | description |
250
- | :-------- | :------- | :---------------------------------------------------------------------------------- |
251
- | `default` | date | Optional. Value used if the parser returns `undefined`, `null`, or an invalid date. |
252
- | `parser` | function | Optional. Used to customize how a value is converted into a date. |
346
+ | option | type | description |
347
+ | :---------------- | :----------------- | :---------------------------------------------------------------------------------- |
348
+ | `default` | date | Optional. Value used if the parser returns `undefined`, `null`, or an invalid date. |
349
+ | `requiredMessage` | string or function | Optional. Message for a missing value. Defaults to `'Required.'`. |
350
+ | `parser` | function | Optional. Used to customize how a value is converted into a date. |
253
351
 
254
352
  The default parser only accepts dates, numbers, and a subset of [ISO 8601][iso8601] formatted strings (excludes week formats). If you want to allow other or more complex formats you should use a third-party library such as [date-fns][date-fns].
255
353
 
@@ -257,37 +355,38 @@ The default parser only accepts dates, numbers, and a subset of [ISO 8601][iso86
257
355
 
258
356
  #### number(config?, ...tests)
259
357
 
260
- Casts value to a number, `null` or `undefined` if using the default number parser. `config` is optional.
358
+ Casts value to a number, or keeps `null` or `undefined` if using the default number parser. `config` is optional.
261
359
 
262
- The default parser accepts numbers, bigints, dates (as their timestamp), strings that start with a number (read with `parseFloat`, so `'12px'` is `12`), and `Number` or `String` objects holding one. A blank string (`''` or only whitespace) gives `NaN`, which `required` treats as empty and `default` replaces. Any other input gives an invalid result with the message `Failed to parse value to number.`: strings without a leading number, such as `'abc'`, booleans, symbols, and objects other than the ones above, arrays and functions included.
360
+ The default parser accepts numbers, bigints, dates (as their timestamp), strings that start with a number (read with `parseFloat`, so `'12px'` is `12`), and `Number` or `String` objects holding one. A blank string (`''` or only whitespace) gives `NaN`, which counts as missing and which `default` replaces. Any other input gives an invalid result with the message `Failed to parse value to number.`: strings without a leading number, such as `'abc'`, booleans, symbols, and objects other than the ones above, arrays and functions included.
263
361
 
264
- Supported validation tests: `exact`, `integer`, `max`, `min`, `oneOf`, and `required`.
362
+ Supported validation tests: `exact`, `integer`, `max`, `min`, and `oneOf`.
265
363
 
266
364
  <details><summary>config</summary>
267
365
 
268
- | option | type | description |
269
- | :-------- | :------- | :----------------------------------------------------------------------------------------- |
270
- | `round` | string | Optional. Rounding method to use, one of `nearest`, `ceil`, `floor` |
271
- | `default` | number | Optional. Value used if the parser returns `undefined`, `null` or an invalid number `NaN`. |
272
- | `parser` | function | Optional. Used to customize how a value is converted into a number. |
366
+ | option | type | description |
367
+ | :---------------- | :----------------- | :----------------------------------------------------------------------------------------- |
368
+ | `round` | string | Optional. Rounding method to use, one of `nearest`, `ceil`, `floor` |
369
+ | `default` | number | Optional. Value used if the parser returns `undefined`, `null` or an invalid number `NaN`. |
370
+ | `requiredMessage` | string or function | Optional. Message for a missing value. Defaults to `'Required.'`. |
371
+ | `parser` | function | Optional. Used to customize how a value is converted into a number. |
273
372
 
274
373
  </details>
275
374
 
276
- #### object(schema, ...tests)
375
+ #### object(schema, config?, ...tests)
277
376
 
278
- The only validation type that does not cast the entered value, meaning it does not use a parser. There's also no default fallback value option here. Instead, the schema is an object where its properties are validation types such as `string`, `array`, `number`, etc. Nested `object` validators are allowed, but not recommended due to their complexity and potential performance hit.
377
+ The only validation type that does not cast the entered value, meaning it does not use a parser. There's also no default fallback value option here. `config` is optional; when the second argument is a function, it's the first of the `tests`. A missing object, `undefined` or `null`, fails with `config.requiredMessage`, or `'Required.'`, without checking its fields. Instead, the schema is an object where its properties are validation types such as `string`, `array`, `number`, etc. Nested `object` validators are allowed, but not recommended due to their complexity and potential performance hit.
279
378
 
280
379
  For invalid results, errors are in the `errors` property. It matches the provided object with the same keys and strings or string records as values that contain the error messages.
281
380
 
282
- In TypeScript, every field of the valid value is optional and can be `null`, as in `{ name?: string | null }`: a field's validator passes `null` and `undefined` through unless a test such as `required` rejects them.
381
+ In TypeScript, a field of the valid value is required unless its validator is wrapped in `optional()`, as in `{ name: string; age?: number | null | undefined }`. A missing optional field is still set on the value, as `undefined`.
283
382
 
284
383
  The remaining `tests` are object-level tests. They run in order on the parsed object, only once every property is valid, and the first failure becomes the result. Use [`check`](#checkpath-test-message) to compare fields and put the error on one of them:
285
384
 
286
385
  ```ts
287
386
  const signup = object(
288
387
  {
289
- password: string(required('Required.')),
290
- confirm: string(required('Required.')),
388
+ password: string(),
389
+ confirm: string(),
291
390
  },
292
391
  check('confirm', (value) => value.confirm === value.password, 'Must match.'),
293
392
  );
@@ -298,29 +397,40 @@ await signup({ password: 'secret', confirm: 'other' });
298
397
 
299
398
  A field's own error takes precedence: object-level tests don't run while any property is invalid, so they always compare valid, parsed values.
300
399
 
400
+ <details><summary>config</summary>
401
+
402
+ | option | type | description |
403
+ | :---------------- | :----------------- | :----------------------------------------------------------------- |
404
+ | `requiredMessage` | string or function | Optional. Message for a missing object. Defaults to `'Required.'`. |
405
+
406
+ </details>
407
+
301
408
  To use this with a library such as [Formik][formik], you can use the resulting `errors` property. It is a key-value structure with the schema property keys and their validation errors if any. Besides that, the object validation results are the same as described above.
302
409
 
303
410
  #### string(config?, ...tests)
304
411
 
305
- Casts value to a string, `null` or `undefined` if using the default string parser. `config` is optional.
412
+ Casts value to a string, or keeps `null` or `undefined` if using the default string parser. `config` is optional.
306
413
 
307
414
  The default parser accepts strings, numbers, bigints, booleans, dates (as an ISO string), and `String`, `Number`, or `Boolean` objects. Any other object, including arrays and functions, and symbols give an invalid result with the message `Failed to parse value to string.`
308
415
 
309
- Supported validation tests: `email`, `exact`, `matches`, `max`, `min`, `oneOf`, `required`, `url`, and `uuid`.
416
+ Supported validation tests: `email`, `exact`, `matches`, `max`, `min`, `oneOf`, `url`, and `uuid`.
310
417
 
311
418
  <details><summary>config</summary>
312
419
 
313
- | option | type | description |
314
- | :-------- | :------- | :--------------------------------------------------------------------------------------- |
315
- | `trim` | boolean | Optional. If true, clears whitespace from the start and end of the string. |
316
- | `default` | string | Optional. Value used if the parser returns `undefined`, `null`, or an empty string `''`. |
317
- | `parser` | function | Optional. Used to customize how a value is converted into a string. |
420
+ | option | type | description |
421
+ | :---------------- | :----------------- | :--------------------------------------------------------------------------------------- |
422
+ | `trim` | boolean | Optional. If true, clears whitespace from the start and end of the string. |
423
+ | `default` | string | Optional. Value used if the parser returns `undefined`, `null`, or an empty string `''`. |
424
+ | `requiredMessage` | string or function | Optional. Message for a missing value. Defaults to `'Required.'`. |
425
+ | `parser` | function | Optional. Used to customize how a value is converted into a string. |
318
426
 
319
427
  </details>
320
428
 
321
429
  ### Validation tests
322
430
 
323
- A **validation test** is a simplified version of a validation type. It accepts a value of a specific type including `null` and `undefined` and then returns a promise that resolves to a validation result. You can use a validation test directly without the validation type, but it's not recommended as you lose the parsing step and you can't apply multiple tests to the same field as easily.
431
+ A **validation test** is a simplified version of a validation type. It accepts a value of a specific type and returns a promise that resolves to a validation result. Validation types only run tests on present values, so a built-in test fails `null` and `undefined` when it's called with them directly. An empty string is a string like any other: `min(1, …)` fails it and `max(1, …)` accepts it. You can use a validation test directly without the validation type, but it's not recommended as you lose the parsing step and you can't apply multiple tests to the same field as easily.
432
+
433
+ In TypeScript, a test's type is `ValidationTest<T, E>`, a function that takes a value of type `T`. A validation type takes tests on its value type: `string()` takes `min()`, which checks strings, numbers and arrays, but not a test on booleans. A custom test that takes `unknown` fits every validation type.
324
434
 
325
435
  - [check(path?, test, message)](#checkpath-test-message)
326
436
  - [email(message)](#emailmessage)
@@ -332,7 +442,6 @@ A **validation test** is a simplified version of a validation type. It accepts a
332
442
  - [min(limit, message, exclusive?)](#minlimit-message-exclusive)
333
443
  - [minDate(limit, message, exclusive?)](#mindatelimit-message-exclusive)
334
444
  - [oneOf(values, message)](#oneofvalues-message)
335
- - [required(message, nullable?)](#requiredmessage-nullable)
336
445
  - [url(message, protocols?)](#urlmessage-protocols)
337
446
  - [uuid(message)](#uuidmessage)
338
447
 
@@ -340,6 +449,8 @@ A **validation test** is a simplified version of a validation type. It accepts a
340
449
 
341
450
  An object-level test for `object`. It fails when `test` returns `false` for the parsed object. With a `path`, the message is the error at that property in the object's `errors`; a list of names reaches into nested objects, such as `['shipping', 'zip']`. Without a `path`, the message is the object's own `message`. The parsed object is provided to the message as `value`.
342
451
 
452
+ In TypeScript, `path` is checked against the parsed object's type, so a misspelled property is a type error, and the test's error type is the errors it produces, such as `{ shipping?: { zip?: string } }`. A path ends at an array, a date or a function.
453
+
343
454
  <details><summary>arguments</summary>
344
455
 
345
456
  | name | type | description |
@@ -469,19 +580,6 @@ Ensures value is one of the specified values. Works with strings, arrays, boolea
469
580
 
470
581
  </details>
471
582
 
472
- #### required(message, nullable?)
473
-
474
- Ensures value is **not** one of: `undefined`, `null`, `NaN`, an empty string, or an invalid date. Works with all types.
475
-
476
- <details><summary>arguments</summary>
477
-
478
- | name | type | description |
479
- | :--------- | :----------------- | :-------------------------------- |
480
- | `message` | string or function | The validation error message. |
481
- | `nullable` | boolean | Optional. If true, allows `null`. |
482
-
483
- </details>
484
-
485
583
  #### url(message, protocols?)
486
584
 
487
585
  Ensures value is a valid URL with an allowed protocol. Works with strings. `protocols` defaults to `['http:', 'https:']`, so `javascript:` and `data:` URLs, which can run script when used as a link, are invalid. A list replaces the default, so `['mailto:']` allows only `mailto:`. `protocols` is provided to the message.
@@ -511,11 +609,39 @@ Ensures the value is a UUID as defined by [RFC 9562](https://www.rfc-editor.org/
511
609
 
512
610
  Custom validation types and validators can be created and used as needed. There are some helpers that, while used internally, are considered part of the public API.
513
611
 
612
+ - [createValidator(fn)](#createvalidatorfn)
514
613
  - [formatMessage(template, params)](#formatmessagetemplate-params)
515
614
  - [createTypeValidatorTest(defaultConfig, applyConfig)](#createtypevalidatortestdefaultconfig-applyconfig)
516
615
  - [valid({ value, field })](#valid-value-field-)
517
616
  - [invalid({ message, value, field, errors, extras })](#invalid-message-value-field-errors-extras-)
518
- - [standard(validator)](#standardvalidator)
617
+
618
+ #### createValidator(fn)
619
+
620
+ Makes a custom validator from `fn`, an async function that takes a value and a field name and returns a result made with `valid()` or `invalid()`. The validator gives the same results as `fn`, works with `optional()` and inside `object()` and `array()`, and is a [Standard Schema][standard-schema] like the validation types. `fn` itself is unchanged. A validator that's already a Standard Schema from this library, such as `string()`, is returned as it is. In TypeScript, its type is `TypeValidator<T, E>`, with `T` and `E` inferred from `fn`'s return type.
621
+
622
+ `fn` must return a promise. A synchronous function is a type error.
623
+
624
+ ```ts
625
+ const hello = createValidator(async (value, field) =>
626
+ value === 'hello'
627
+ ? valid({ value, field })
628
+ : invalid({ message: 'Must be "hello".', value, field }),
629
+ );
630
+
631
+ await hello['~standard'].validate('hi');
632
+ // { issues: [{ message: 'Must be "hello".' }] }
633
+
634
+ const schema = object({ email: string(email('Must be an email.')) });
635
+ await schema['~standard'].validate({ email: 'nope' });
636
+ // { issues: [{ message: 'Must be an email.', path: ['email'] }] }
637
+ ```
638
+
639
+ How a validator's results map to Standard Schema results:
640
+
641
+ - `validate` always returns a promise, because every validator here is async. Libraries that only accept synchronous schemas can't use it.
642
+ - A valid result gives `{ value }`, the parsed value. Its type is the validator's value type, which includes `null` and `undefined` only for `optional()` validators.
643
+ - An invalid result gives one issue per error message. Object keys and array indexes in `errors` form each issue's `path`, and a non-empty `message`, such as an array-level error, is an issue with no path. An error with no message gets `'Invalid value.'`.
644
+ - An `object` field that fails both an array-level test and item tests keeps only the array-level message, because `object` stores a field's message in place of its errors, so only that message becomes an issue.
519
645
 
520
646
  #### formatMessage(template, params)
521
647
 
@@ -523,7 +649,7 @@ Formats a string for use as an error message. `template` can be a plain string w
523
649
 
524
650
  #### createTypeValidatorTest(defaultConfig, applyConfig)
525
651
 
526
- Useful helper for simple validation types such as strings, numbers, etc. `defaultConfig` is the configuration object that the `applyConfig` function will use when parsing the value being validated. At least this should include a `parser` function.
652
+ Useful helper for simple validation types such as strings, numbers, etc. `defaultConfig` is the configuration object that the `applyConfig` function will use when parsing the value being validated. At least this should include a `parser` function. The resulting validator is required by default, takes `requiredMessage` in its config, works with `optional()`, and is a Standard Schema, like the built-in ones. Its return type is a `TypeValidator`, which is still a `ValidatorTest`, so factories typed with `ValidatorFactory` keep working.
527
653
 
528
654
  The validation tests passed to the resulting validator report their errors as strings, as the built-in ones do; a test whose `errors` has another type is a type error.
529
655
 
@@ -562,27 +688,13 @@ const result = await money({ cents: true })('12.5');
562
688
 
563
689
  #### valid({ value, field })
564
690
 
565
- Formats a successful validation result. `value` is the parsed value and `field` is the name of the object schema property.
691
+ Formats a successful validation result. `value` is the parsed value and `field` is the name of the object schema property. In TypeScript, `value` has the validator's value type, so a custom validator that can return `null` or `undefined` needs them in that type.
566
692
 
567
693
  #### invalid({ message, value, field, errors, extras })
568
694
 
569
695
  Formats a failed validation result. `message` is the same as the `formatMessage`'s template. `value` and `field` is the same as above, `errors` is additional error details used by `array` and `object` validators, and `extras` are optional extra parameters that can be used in the message.
570
696
 
571
- #### standard(validator)
572
-
573
- Adds the [Standard Schema][standard-schema] interface to a validator, for libraries that accept any Standard Schema. It returns a new validator with a `~standard` property and leaves the one passed in unchanged.
574
-
575
- ```ts
576
- const schema = standard(object({ email: string(email('Must be an email.')) }));
577
-
578
- await schema['~standard'].validate({ email: 'nope' });
579
- // { issues: [{ message: 'Must be an email.', path: ['email'] }] }
580
- ```
581
-
582
- - `validate` always returns a promise, because every validator here is async. Libraries that only accept synchronous schemas can't use it.
583
- - A valid result gives `{ value }`, the parsed value. Its type is the validator's value type or `null` or `undefined`, as in a valid result.
584
- - An invalid result gives one issue per error message. Object keys and array indexes in `errors` form each issue's `path`, and a non-empty `message`, such as an array-level error, is an issue with no path. An error with no message gets `'Invalid value.'`.
585
- - An `object` field that fails both an array-level test and item tests keeps only the array-level message, because `object` stores a field's message in place of its errors, so only that message becomes an issue.
697
+ In TypeScript, a message function gets `value` as `unknown`, since `invalid()` takes any value. A message that reads extra parameters needs `extras` with them; without `extras`, it's a type error.
586
698
 
587
699
  ## Supported environments
588
700
 
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});function e(e){return e.message||e.errors===void 0?e.message:e.errors}function t(e){return Object.prototype.toString.call(e)===`[object Object]`}function n(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function r(e){return!!Reflect.get(Object(e),Symbol.iterator)}const i=/{(\w+)}/g;function a(e,t){return typeof e==`string`?e.replaceAll(i,(e,n)=>String(Reflect.get(t,n))):e(t)}async function o(e){return s(e)}function s({value:e,field:t}){return{state:`valid`,value:e,field:t}}async function c(e){return l(e)}function l({errors:e,field:t,message:n,value:r,extras:i}){return{state:`invalid`,message:a(n,{...i,value:r,field:t}),value:r,field:t,errors:e}}function u(e){return Array.isArray(e)||e==null?e:r(e)?[...e]:[]}function d(e,t){let n=e.parser(t);return e.default!==void 0&&(n==null||n.length===0)&&(n=e.default),n}async function f(e,t,n){for(let r of e){let e=await r(t,n);if(e.state===`invalid`)return e}}async function p(t,n,r){let i=!0,a=[];if(!n||!t)return{isValid:i,errors:a,values:n};let o=[];for(let[s,c]of n.entries()){let n=await t(c,`${r||``}[${s}]`);o.push(n.value),n.state===`invalid`?(i=!1,a.push(e(n))):a.push(null)}return{isValid:i,errors:a,values:o}}function m(e,...t){let n=typeof e==`function`?[e,...t]:t,r={parser:u,...typeof e==`function`?void 0:e};return async(e,t)=>{let i=d(r,e),[a,...s]=n,l=await f(s,i,t),u=await p(a,i,t);return!l&&u.isValid?o({value:u.values,field:t}):c({message:l?l.message:``,value:e,field:t,errors:u.errors})}}const h=/* @__PURE__ */ new WeakMap;function g(e,t){return(n,...r)=>{let i=e,a=r;return n!==void 0&&(typeof n==`function`?a=[n,...r]:i={...e,...n}),async(e,n)=>{try{let r=t(e,i);for(let e of a){let t=h.get(e),i;if(i=t?t(r,n):await e(r,n),i.state===`invalid`)return i}return s({value:r,field:n})}catch(t){return c({message:t instanceof Error?t.message:String(t),value:e,field:n})}}}}function _(e,t,n){let r=(r,i)=>e(r,i)?s({value:r,field:i}):l({message:t,value:r,field:i,extras:n?n(r):void 0}),i=async(e,t)=>r(e,t);return h.set(i,r),i}function v(e){return e==null||typeof e==`boolean`?e:!!e}function y(e,t){let n=t.parser(e);return t.default!==void 0&&n==null&&(n=t.default),n}const b=g({parser:v},y),x=/* @__PURE__ */ new Date(``),S=/^(\d{4})(?:-([0-2]\d)(?:-([0-3]\d)(?:[ T]([0-2]\d):([0-5]\d)(?::([0-5]\d)(\.\d+)?(Z|[+-][0-2]\d:?[0-5]\d)?)?)?)?)?$/,C=/^([+-])([0-2]\d):?([0-5]\d)$/;function w(e){let t=C.exec(e);if(e===`Z`||!t)return 0;let[,n,r,i]=t;return(n===`-`?-1:1)*(Number(r)*60+Number(i))}const T=[()=>0,e=>Number(e),(e=`1`)=>Number.parseInt(e,10)-1,(e=`1`)=>Number.parseInt(e,10),(e=`0`)=>Number.parseInt(e,10),(e=`0`)=>Number.parseInt(e,10),(e=`0`)=>Number.parseInt(e,10),(e=`0`)=>Number.parseFloat(e)*1e3];function E(e){return e==null||e instanceof Date?e:typeof e==`number`?new Date(e):typeof e==`string`?D(e):x}function D(e){let t=S.exec(e);if(t===null)return x;let[,n=0,r=0,i=0,a=0,o=0,s=0,c=0]=T.map((e,n)=>e(t[n])),l=t[8];return l?new Date(Date.UTC(n,r,i,a,o+w(l),s,c)):new Date(n,r,i,a,o,s,c)}function O(e,t){let n=t.parser(e);return t.default!==void 0&&(n==null||Number.isNaN(n.getTime()))&&(n=t.default),n}function k(e,t,n){let r=E(e);return _(e=>r!=null&&(e==null||e instanceof Date&&(n?e>r:e>=r)),t,()=>({min:r,limit:r,exclusive:n}))}function ee(e,t,n){let r=E(e);return _(e=>r!=null&&(e==null||e instanceof Date&&(n?e<r:e<=r)),t,()=>({max:r,limit:r,exclusive:n}))}const te=g({parser:E},O),ne=[e=>String.prototype.valueOf.call(e),e=>Number.prototype.valueOf.call(e),e=>Boolean.prototype.valueOf.call(e)];function A(e){if(typeof e!=`object`||!e)return e;for(let t of ne)try{return t(e)}catch{}return e}function j(e){let t=A(e);switch(typeof t){case`string`:case`number`:case`bigint`:case`boolean`:return String(t);default:return}}function M(e){if(e==null||typeof e==`number`)return e;if(e instanceof Date)return e.getTime();let t=j(e),n=t===void 0?NaN:Number.parseFloat(t);if(Number.isNaN(n)&&(t===void 0||t.trim()!==``))throw TypeError(`Failed to parse value to number.`);return n}function N(e,t){let n=t.parser(e);if(t.default!==void 0&&(n==null||Number.isNaN(n))&&(n=t.default),t.round&&typeof n==`number`)switch(t.round){case`nearest`:n=Math.round(n);break;case`ceil`:n=Math.ceil(n);break;case`floor`:n=Math.floor(n);break;default:throw Error(`Unknown round type: ${String(t.round)}`)}return n}function P(e){return async(t,n)=>t==null||typeof t==`number`&&Number.isFinite(t)&&Math.floor(t)===t?o({value:t,field:n}):c({message:e,value:t,field:n})}const F=g({parser:M},N);function I(...e){let[t,n,r]=e.length===2?[[],...e]:e,i=typeof t==`string`?[t]:t;return async(e,t)=>{let s=e;return n(s)?o({value:s,field:t}):i.length===0?c({message:r,value:s,field:t}):c({message:``,value:s,field:t,errors:i.reduceRight((e,t)=>({[t]:e}),a(r,{value:s,field:t}))})}}function L(e,t,n){Object.defineProperty(e,t,{value:n,enumerable:!0,writable:!0,configurable:!0})}async function R(e,t,n){for(let r of e){let e=await r(t,n);if(e.state===`invalid`)return e}}function z(t,...r){if(typeof t!=`object`||!t)throw TypeError("`properties` must be a configuration object");return async(i,a)=>{let s=Object(i),l={},u={},d=!0;for(let r in t)if(n(t,r)){let i=t[r],a=n(s,r)?Reflect.get(s,r):void 0,o=await(typeof i==`function`?i(a,r):c({field:r,message:`No validator set`,value:a}));L(u,r,o.value),o.state===`invalid`&&(d=!1,L(l,r,e(o)))}if(!d)return c({message:``,value:i,field:a,errors:l});let f=await R(r,u,a);return f?c({message:f.message,value:i,field:a,errors:f.errors}):o({value:u,field:a})}}const B=`Invalid value.`;function V(e,t,n){if(typeof e==`string`)n.push({message:e||B,path:t});else if(Array.isArray(e))for(let[r,i]of e.entries())i!==null&&V(i,[...t,r],n);else if(typeof e==`object`&&e)for(let r of Object.keys(e))V(Reflect.get(e,r),[...t,r],n);else n.push({message:B,path:t})}function H(e){return Object.assign((t,n)=>e(t,n),{"~standard":{version:1,vendor:`validator-fns`,validate:async t=>{let n=await e(t);if(n.state===`valid`)return{value:n.value};let r=n.message?[{message:n.message}]:[];return n.errors!==void 0&&V(n.errors,[],r),{issues:r.length>0?r:[{message:B}]}}}})}function U(e,t){return _(e=>t&&e===null||e!=null&&(typeof e!=`string`||e!==``)&&!(typeof e==`number`&&Number.isNaN(e))&&!(e instanceof Date&&Number.isNaN(e.valueOf())),e)}function W(e,t){let n=e;return _(t=>t==null||n.includes(t)||t instanceof Date&&e.some(e=>e.valueOf()===t.valueOf()),t,()=>({values:e}))}function G(e){return typeof e==`string`||Array.isArray(e)?e.length:e}function K(e,t,n){return _(t=>{let r=G(t);return r==null||t===``||(n?r<e:r<=e)},t,t=>({max:e,limit:e,amount:G(t),exclusive:n}))}function q(e,t,n){return _(t=>{let r=G(t);return r==null||t===``||(n?r>e:r>=e)},t,t=>({min:e,limit:e,amount:G(t),exclusive:n}))}function J(e,t){return _(t=>{let n=G(t);return n==null||t===``||n===e},t,t=>({amount:G(t),limit:e,exact:e}))}const Y=/^[\w!#$%&'*+./=?^`{|}~-]+@[\dA-Za-z](?:[\dA-Za-z-]{0,61}[\dA-Za-z])?(?:\.[\dA-Za-z](?:[\dA-Za-z-]{0,61}[\dA-Za-z])?)*$/,X=/^(?:[\da-f]{8}-[\da-f]{4}-[1-8][\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}|0{8}-0{4}-0{4}-0{4}-0{12}|f{8}-f{4}-f{4}-f{4}-f{12})$/i;function Z(e){if(e==null||typeof e==`string`)return e;if(e instanceof Date)return e.toISOString();let t=j(e);if(t===void 0)throw TypeError(`Failed to parse value to string.`);return t}function Q(e,t){let n=t.parser(e);return t.default!==void 0&&(n==null||n===``)&&(n=t.default),t.trim&&typeof n==`string`&&(n=n.trim()),n}function $(e,t){let n=new RegExp(e);return _(e=>(n.lastIndex=0,e==null||e===``||typeof e==`string`&&n.test(e)),t,()=>({pattern:e}))}function re(e){return $(Y,e)}function ie(e){return $(X,e)}function ae(e,t=[`http:`,`https:`]){return _(e=>{try{let n;if(e==null||e===``||typeof e==`string`&&(n=new URL(e)))return!n||t.includes(n.protocol)}catch{}return!1},e,()=>({protocols:t}))}const oe=g({parser:Z},Q);exports.applyArrayConfig=d,exports.applyBooleanConfig=y,exports.applyDateConfig=O,exports.applyNumberConfig=N,exports.applyStringConfig=Q,exports.array=m,exports.boolean=b,exports.check=I,exports.createTypeValidatorTest=g,exports.createValidatorTest=_,exports.date=te,exports.email=re,exports.exact=J,exports.formatMessage=a,exports.hasOwnProperty=n,exports.integer=P,exports.invalid=c,exports.invalidDate=x,exports.isObject=t,exports.matches=$,exports.max=K,exports.maxDate=ee,exports.min=q,exports.minDate=k,exports.number=F,exports.object=z,exports.oneOf=W,exports.parseArray=u,exports.parseBoolean=v,exports.parseDate=E,exports.parseNumber=M,exports.parseString=Z,exports.required=U,exports.standard=H,exports.string=oe,exports.url=ae,exports.uuid=ie,exports.valid=o;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});async function e(e,t,n){for(let r of e){let e=await r(t,n);if(e.state===`invalid`)return e}}function t(e){return e.message||e.errors===void 0?e.message:e.errors}async function n(e,n,r){let i=!0,a=[],o=[];for(let[s,c]of n.entries()){let n=await e(c,`${r||``}[${s}]`);n.state===`invalid`?(i=!1,a.push(t(n))):(o.push(n.value),a.push(null))}return{isValid:i,errors:a,values:o}}async function r(e){return{isValid:!0,errors:[],values:e}}function i(e){return Object.prototype.toString.call(e)===`[object Object]`}function a(e,t){return Object.prototype.hasOwnProperty.call(e,t)}function o(e,t,n){Object.defineProperty(e,t,{value:n,enumerable:!0,writable:!0,configurable:!0})}function s(e,t){let n={};return o(n,e,t),n}function c(e,t){return e.reduceRight((e,t)=>({[t]:e}),t)}function l(e){return!!Reflect.get(Object(e),Symbol.iterator)}const u=/{(\w+)}/g;function d(e,t){return typeof e==`string`?e.replaceAll(u,(e,n)=>String(Reflect.get(t,n))):e(t)}async function f(e){return p(e)}function p({value:e,field:t}){return{state:`valid`,value:e,field:t}}function m({errors:e,field:t,message:n,value:r}){return{state:`invalid`,message:n,value:r,field:t,errors:e}}const h=`Invalid value.`;function g(e,t,n){if(typeof e==`string`)n.push({message:e||h,path:t});else if(Array.isArray(e))for(let[r,i]of e.entries())i!==null&&g(i,[...t,r],n);else if(typeof e==`object`&&e)for(let r of Object.keys(e))g(Reflect.get(e,r),[...t,r],n);else n.push({message:h,path:t})}const _=/* @__PURE__ */ new WeakSet;function ee(e){return _.has(e)}function v(e){let t=Object.assign(e,{"~standard":{version:1,vendor:`validator-fns`,validate:async t=>{let n=await e(t);if(n.state===`valid`)return{value:n.value};let r=n.message?[{message:n.message}]:[];return n.errors!==void 0&&g(n.errors,[],r),{issues:r.length>0?r:[{message:h}]}}}});return _.add(t),t}function te(e){return ee(e)?e:v((t,n)=>e(t,n))}const y=Symbol(`optional`);function ne(e){return e===``||typeof e==`number`&&Number.isNaN(e)||e instanceof Date&&Number.isNaN(e.getTime())}function b(e,t){let n=e===void 0?`Required.`:e,r=(e,t)=>m({message:d(n,{value:e,field:t}),value:e,field:t}),i=(e,t)=>p({value:e,field:t}),a=(e,n)=>t(e,n,r),o=(e,n)=>t(e,n,i);return Object.assign(v(a),{[y]:v(o)})}function re(e){let t=e[y];return t===void 0?v(async(t,n)=>t==null?p({value:t,field:n}):e(t,n)):t}function x(e){return Array.isArray(e)||e==null?e:l(e)?[...e]:[]}function S(e,t){let n=e.parser(t);return e.default!==void 0&&(n==null||n.length===0)&&(n=e.default),n}function C(t,n,r){let i=typeof n==`function`?[n,...r]:r,a={parser:x,...typeof n==`function`?void 0:n};return b(a.requiredMessage,async(n,r,o)=>{let s=S(a,n);if(s==null)return o(s,r);let c=await e(i,s,r),l=await t(s,r);return!c&&l.isValid?f({value:l.values,field:r}):m({message:c?c.message:``,value:n,field:r,errors:l.errors})})}function w(e,t,...i){if(typeof e==`function`)return C((t,r)=>n(e,t,r),t,i);if(t!==void 0)throw TypeError(`array() takes the item validator first: array(item, config?, ...tests)`);return C(r,e,[])}const T=Symbol(`sync`);function E(e,t){return(n,...r)=>{let i=e,a=r;return n!==void 0&&(typeof n==`function`?a=[n,...r]:i={...e,...n}),b(i.requiredMessage,async(e,n,r)=>{try{let o=t(e,i);if(o==null||ne(o))return r(o,n);for(let e of a){let t=e[T],r;if(r=t?t(o,n):await e(o,n),r.state===`invalid`)return r}return p({value:o,field:n})}catch(t){return m({message:t instanceof Error?t.message:String(t),value:e,field:n})}})}}function D(e,...t){let n=(e,n)=>t.length===1?d(t[0],{value:e,field:n}):d(t[0],{...t[1](e),value:e,field:n}),r=(t,r)=>t!=null&&e(t,r)?p({value:t,field:r}):m({message:n(t,r),value:t,field:r});return Object.assign(async(e,t)=>r(e,t),{[T]:r})}function O(e){return e==null||typeof e==`boolean`?e:!!e}function k(e,t){let n=t.parser(e);return t.default!==void 0&&n==null&&(n=t.default),n}const A=E({parser:O},k),j=/* @__PURE__ */ new Date(``),M=/^(\d{4})(?:-([0-2]\d)(?:-([0-3]\d)(?:[ T]([0-2]\d):([0-5]\d)(?::([0-5]\d)(\.\d+)?(Z|[+-][0-2]\d:?[0-5]\d)?)?)?)?)?$/,N=/^([+-])([0-2]\d):?([0-5]\d)$/;function ie(e){let t=N.exec(e);if(e===`Z`||!t)return 0;let[,n,r,i]=t;return(n===`-`?-1:1)*(Number(r)*60+Number(i))}const ae=[()=>0,e=>Number(e),(e=`1`)=>Number.parseInt(e,10)-1,(e=`1`)=>Number.parseInt(e,10),(e=`0`)=>Number.parseInt(e,10),(e=`0`)=>Number.parseInt(e,10),(e=`0`)=>Number.parseInt(e,10),(e=`0`)=>Number.parseFloat(e)*1e3],P=`Failed to parse value to date.`;function F(e){if(e==null||e instanceof Date)return e;if(typeof e==`number`)return new Date(e);if(typeof e!=`string`)throw TypeError(P);return e.trim()===``?j:I(e)}function I(e){let t=M.exec(e);if(t===null)throw TypeError(P);let[,n=0,r=0,i=0,a=0,o=0,s=0,c=0]=ae.map((e,n)=>e(t[n])),l=t[8];return l?new Date(Date.UTC(n,r,i,a,o+ie(l),s,c)):new Date(n,r,i,a,o,s,c)}function L(e,t){let n=t.parser(e);return t.default!==void 0&&(n==null||Number.isNaN(n.getTime()))&&(n=t.default),n}function R(e,t,n){let r=F(e);return D(e=>r!=null&&(n?e>r:e>=r),t,()=>({min:r,limit:r,exclusive:n}))}function z(e,t,n){let r=F(e);return D(e=>r!=null&&(n?e<r:e<=r),t,()=>({max:r,limit:r,exclusive:n}))}const B=E({parser:F},L),V=[e=>String.prototype.valueOf.call(e),e=>Number.prototype.valueOf.call(e),e=>Boolean.prototype.valueOf.call(e)];function H(e){if(typeof e!=`object`||!e)return e;for(let t of V)try{return t(e)}catch{}return e}function U(e){let t=H(e);switch(typeof t){case`string`:case`number`:case`bigint`:case`boolean`:return String(t);default:return}}function W(e){if(e==null||typeof e==`number`)return e;if(e instanceof Date)return e.getTime();let t=U(e),n=t===void 0?NaN:Number.parseFloat(t);if(Number.isNaN(n)&&(t===void 0||t.trim()!==``))throw TypeError(`Failed to parse value to number.`);return n}function G(e,t){let n=t.parser(e);if(t.default!==void 0&&(n==null||Number.isNaN(n))&&(n=t.default),t.round&&typeof n==`number`)switch(t.round){case`nearest`:n=Math.round(n);break;case`ceil`:n=Math.ceil(n);break;case`floor`:n=Math.floor(n);break;default:throw Error(`Unknown round type: ${String(t.round)}`)}return n}function K(e){return D(e=>Number.isFinite(e)&&Math.floor(e)===e,e)}const q=E({parser:W},G);function J(e,t,n){return async(r,i)=>{if(e(r))return p({value:r,field:i});let a=d(t,{value:r,field:i});return m(n?{message:``,value:r,field:i,errors:n(a)}:{message:a,value:r,field:i})}}function oe(...e){if(e.length===2)return J(e[0],e[1]);let[t,n,r]=e;return typeof t==`string`?J(n,r,e=>s(t,e)):J(n,r,e=>c(t,e))}function se(e){return e}function ce(n,r,...i){if(typeof n!=`object`||!n)throw TypeError("`properties` must be a configuration object");let s=typeof r==`function`?[r,...i]:i;return b(typeof r==`object`?r.requiredMessage:void 0,async(r,i,c)=>{if(r==null)return c(r,i);let l=Object(r),u={},d={},p=!0;for(let e in n)if(a(n,e)){let r=n[e],i=a(l,e)?Reflect.get(l,e):void 0,s=await(typeof r==`function`?r(i,e):m({field:e,message:`No validator set`,value:i}));o(d,e,s.value),s.state===`invalid`&&(p=!1,o(u,e,t(s)))}if(!p)return m({message:``,value:r,field:i,errors:u});let h=se(d),g=await e(s,h,i);return g?m({message:g.message,value:r,field:i,errors:g.errors}):f({value:h,field:i})})}function le(e,t){return D(t=>e.includes(t)||t instanceof Date&&e.some(e=>e.valueOf()===t.valueOf()),t,()=>({values:e}))}function Y(e){return typeof e==`number`?e:e==null?NaN:e.length}function ue(e,t,n){return D(t=>{let r=Y(t);return n?r<e:r<=e},t,t=>({max:e,limit:e,amount:Y(t),exclusive:n}))}function X(e,t,n){return D(t=>{let r=Y(t);return n?r>e:r>=e},t,t=>({min:e,limit:e,amount:Y(t),exclusive:n}))}function de(e,t){return D(t=>Y(t)===e,t,t=>({amount:Y(t),limit:e,exact:e}))}async function fe(e){let{value:t,field:n,errors:r}=e;return m({message:`extras`in e?d(e.message,{...e.extras,value:t,field:n}):d(e.message,{value:t,field:n}),value:t,field:n,errors:r})}const pe=/^[\w!#$%&'*+./=?^`{|}~-]+@[\dA-Za-z](?:[\dA-Za-z-]{0,61}[\dA-Za-z])?(?:\.[\dA-Za-z](?:[\dA-Za-z-]{0,61}[\dA-Za-z])?)*$/,me=/^(?:[\da-f]{8}-[\da-f]{4}-[1-8][\da-f]{3}-[89ab][\da-f]{3}-[\da-f]{12}|0{8}-0{4}-0{4}-0{4}-0{12}|f{8}-f{4}-f{4}-f{4}-f{12})$/i;function Z(e){if(e==null||typeof e==`string`)return e;if(e instanceof Date)return e.toISOString();let t=U(e);if(t===void 0)throw TypeError(`Failed to parse value to string.`);return t}function Q(e,t){let n=t.parser(e);return t.default!==void 0&&(n==null||n===``)&&(n=t.default),t.trim&&typeof n==`string`&&(n=n.trim()),n}function $(e,t){let n=new RegExp(e);return D(e=>(n.lastIndex=0,n.test(e)),t,()=>({pattern:e}))}function he(e){return $(pe,e)}function ge(e){return $(me,e)}function _e(e,t=[`http:`,`https:`]){return D(e=>{try{return t.includes(new URL(e).protocol)}catch{return!1}},e,()=>({protocols:t}))}const ve=E({parser:Z},Q);exports.applyArrayConfig=S,exports.applyBooleanConfig=k,exports.applyDateConfig=L,exports.applyNumberConfig=G,exports.applyStringConfig=Q,exports.array=w,exports.boolean=A,exports.check=oe,exports.createTypeValidatorTest=E,exports.createValidator=te,exports.createValidatorTest=D,exports.date=B,exports.email=he,exports.exact=de,exports.formatMessage=d,exports.hasOwnProperty=a,exports.integer=K,exports.invalid=fe,exports.invalidDate=j,exports.isObject=i,exports.matches=$,exports.max=ue,exports.maxDate=z,exports.min=X,exports.minDate=R,exports.number=q,exports.object=ce,exports.oneOf=le,exports.optional=re,exports.parseArray=x,exports.parseBoolean=O,exports.parseDate=F,exports.parseNumber=W,exports.parseString=Z,exports.string=ve,exports.url=_e,exports.uuid=ge,exports.valid=f;
2
2
  //# sourceMappingURL=index.cjs.map