joi 4.6.2 → 4.9.0

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 (66) hide show
  1. package/.c9/.nakignore +17 -0
  2. package/.c9/metadata/tab0 +1 -0
  3. package/.c9/metadata/workspace/.c9/project.settings +1 -0
  4. package/.c9/metadata/workspace/.travis.yml +1 -0
  5. package/.c9/metadata/workspace/Makefile +1 -0
  6. package/.c9/metadata/workspace/README.md +1 -0
  7. package/.c9/metadata/workspace/lib/any.js +1 -0
  8. package/.c9/metadata/workspace/lib/array.js +1 -0
  9. package/.c9/metadata/workspace/lib/binary.js +1 -0
  10. package/.c9/metadata/workspace/lib/boolean.js +1 -0
  11. package/.c9/metadata/workspace/lib/cast.js +1 -0
  12. package/.c9/metadata/workspace/lib/date.js +1 -0
  13. package/.c9/metadata/workspace/lib/errors.js +1 -0
  14. package/.c9/metadata/workspace/lib/function.js +1 -0
  15. package/.c9/metadata/workspace/lib/index.js +1 -0
  16. package/.c9/metadata/workspace/lib/language.js +1 -0
  17. package/.c9/metadata/workspace/lib/number.js +1 -0
  18. package/.c9/metadata/workspace/lib/object.js +1 -0
  19. package/.c9/metadata/workspace/lib/string.js +1 -0
  20. package/.c9/metadata/workspace/package.json +1 -0
  21. package/.c9/metadata/workspace/test/alternatives.js +1 -0
  22. package/.c9/metadata/workspace/test/any.js +1 -0
  23. package/.c9/metadata/workspace/test/array.js +1 -0
  24. package/.c9/metadata/workspace/test/binary.js +1 -0
  25. package/.c9/metadata/workspace/test/boolean.js +1 -0
  26. package/.c9/metadata/workspace/test/date.js +1 -0
  27. package/.c9/metadata/workspace/test/errors.js +1 -0
  28. package/.c9/metadata/workspace/test/function.js +1 -0
  29. package/.c9/metadata/workspace/test/helper.js +1 -0
  30. package/.c9/metadata/workspace/test/index.js +1 -0
  31. package/.c9/metadata/workspace/test/number.js +1 -0
  32. package/.c9/metadata/workspace/test/object.js +1 -0
  33. package/.c9/metadata/workspace/test/ref.js +1 -0
  34. package/.c9/metadata/workspace/test/string.js +1 -0
  35. package/.c9/project.settings +34 -0
  36. package/.travis.yml +1 -0
  37. package/Makefile +3 -3
  38. package/README.md +162 -31
  39. package/examples/conditionalRequire.js +43 -0
  40. package/examples/customMessage.js +22 -0
  41. package/examples/multipleWhen.js +17 -0
  42. package/lib/any.js +35 -24
  43. package/lib/array.js +25 -0
  44. package/lib/date.js +64 -9
  45. package/lib/errors.js +18 -2
  46. package/lib/index.js +3 -2
  47. package/lib/language.js +17 -6
  48. package/lib/number.js +50 -0
  49. package/lib/object.js +69 -5
  50. package/lib/string.js +27 -6
  51. package/package.json +5 -3
  52. package/test/alternatives.js +12 -10
  53. package/test/any.js +64 -15
  54. package/test/array.js +55 -16
  55. package/test/binary.js +13 -11
  56. package/test/boolean.js +9 -7
  57. package/test/date.js +143 -20
  58. package/test/errors.js +26 -15
  59. package/test/function.js +7 -5
  60. package/test/helper.js +7 -5
  61. package/test/index.js +116 -73
  62. package/test/number.js +125 -12
  63. package/test/object.js +192 -43
  64. package/test/ref.js +22 -20
  65. package/test/string.js +255 -106
  66. package/AUTHORS +0 -3
package/README.md CHANGED
@@ -2,20 +2,20 @@
2
2
 
3
3
  Object schema description language and validator for JavaScript objects.
4
4
 
5
- Current version: **4.6.x**
5
+ Current version: **4.7.x**
6
6
 
7
7
  [![Build Status](https://secure.travis-ci.org/hapijs/joi.png)](http://travis-ci.org/hapijs/joi)
8
8
 
9
- Lead Maintainer: [Eran Hammer](https://github.com/hueniverse)
9
+ Lead Maintainer: [Nicolas Morel](https://github.com/marsup)
10
10
 
11
11
  ## Table of Contents
12
12
 
13
13
  <img src="https://raw.github.com/hapijs/joi/master/images/validation.png" align="right" />
14
14
  - [Example](#example)
15
15
  - [Usage](#usage)
16
- - [`validate(value, schema, [options], callback)`](#validatevalue-schema-options-callback)
16
+ - [`validate(value, schema, [options], [callback])`](#validatevalue-schema-options-callback)
17
17
  - [`compile(schema)`](#compileschema)
18
- - [`assert(value, schema)`](#assertvalue-schema)
18
+ - [`assert(value, schema, [message])`](#assertvalue-schema-message)
19
19
  - [`any`](#any)
20
20
  - [`any.allow(value)`](#anyallowvalue)
21
21
  - [`any.valid(value)`](#anyvalidvalue)
@@ -34,12 +34,14 @@ Lead Maintainer: [Eran Hammer](https://github.com/hueniverse)
34
34
  - [`any.default(value)`](#anydefaultvalue)
35
35
  - [`any.concat(schema)`](#anyconcatschema)
36
36
  - [`any.when(ref, options)`](#anywhenref-options)
37
+ - [`any.label(name)`](#anylabelname)
37
38
  - [`array`](#array)
38
39
  - [`array.includes(type)`](#arrayincludestype)
39
40
  - [`array.excludes(type)`](#arrayexcludestype)
40
41
  - [`array.min(limit)`](#arrayminlimit)
41
42
  - [`array.max(limit)`](#arraymaxlimit)
42
43
  - [`array.length(limit)`](#arraylengthlimit)
44
+ - [`array.unique()`](#arrayunique)
43
45
  - [`binary`](#binary)
44
46
  - [`binary.encoding(encoding)`](#binaryencodingencoding)
45
47
  - [`binary.min(limit)`](#binaryminlimit)
@@ -49,11 +51,16 @@ Lead Maintainer: [Eran Hammer](https://github.com/hueniverse)
49
51
  - [`date`](#date)
50
52
  - [`date.min(date)`](#datemindate)
51
53
  - [`date.max(date)`](#datemaxdate)
54
+ - [`date.format(format)`](#dateformatformat)
55
+ - [`date.iso()`](#dateiso)
52
56
  - [`func`](#func)
53
57
  - [`number`](#number)
54
58
  - [`number.min(limit)`](#numberminlimit)
55
59
  - [`number.max(limit)`](#numbermaxlimit)
60
+ - [`number.greater(limit)`](#numbergreaterlimit)
61
+ - [`number.less(limit)`](#numberlesslimit)
56
62
  - [`number.integer()`](#numberinteger)
63
+ - [`number.precision(limit)`](#numberprecisionlimit)
57
64
  - [`object`](#object)
58
65
  - [`object.keys([schema])`](#objectkeysschema)
59
66
  - [`object.min(limit)`](#objectminlimit)
@@ -61,24 +68,26 @@ Lead Maintainer: [Eran Hammer](https://github.com/hueniverse)
61
68
  - [`object.length(limit)`](#objectlengthlimit)
62
69
  - [`object.pattern(regex, schema)`](#objectpatternregex-schema)
63
70
  - [`object.and(peers)`](#objectandpeers)
71
+ - [`object.nand(peers)`](#objectnandpeers)
64
72
  - [`object.or(peers)`](#objectorpeers)
65
73
  - [`object.xor(peers)`](#objectxorpeers)
66
74
  - [`object.with(key, peers)`](#objectwithkey-peers)
67
75
  - [`object.without(key, peers)`](#objectwithoutkey-peers)
68
76
  - [`object.rename(from, to, [options])`](#objectrenamefrom-to-options)
69
- - [`object.assert(ref, schema, message)`](#objectassertref-schema-message)
77
+ - [`object.assert(ref, schema, [message])`](#objectassertref-schema-message)
70
78
  - [`object.unknown([allow])`](#objectunknownallow)
79
+ - [`object.type(constructor, [name])`](#objecttypeconstructorname)
71
80
  - [`string`](#string)
72
81
  - [`string.insensitive()`](#stringinsensitive)
73
82
  - [`string.min(limit, [encoding])`](#stringminlimit-encoding)
74
83
  - [`string.max(limit, [encoding])`](#stringmaxlimit-encoding)
84
+ - [`string.creditCard()`](#stringcreditCard)
75
85
  - [`string.length(limit, [encoding])`](#stringlengthlimit-encoding)
76
- - [`string.regex(pattern)`](#stringregexpattern)
86
+ - [`string.regex(pattern, [name])`](#stringregexpattern)
77
87
  - [`string.alphanum()`](#stringalphanum)
78
88
  - [`string.token()`](#stringtoken)
79
89
  - [`string.email()`](#stringemail)
80
90
  - [`string.guid()`](#stringguid)
81
- - [`string.isoDate()`](#stringisodate)
82
91
  - [`string.hostname()`](#stringhostname)
83
92
  - [`string.lowercase()`](#stringlowercase)
84
93
  - [`string.uppercase()`](#stringuppercase)
@@ -162,7 +171,7 @@ When validating a schema:
162
171
  * Strings are utf-8 encoded by default.
163
172
  * Rules are defined in an additive fashion and evaluated in order after whitelist and blacklist checks.
164
173
 
165
- ### `validate(value, schema, [options], callback)`
174
+ ### `validate(value, schema, [options], [callback])`
166
175
 
167
176
  Validates a value using the given schema and options where:
168
177
  - `value` - the value being validated.
@@ -173,13 +182,16 @@ Validates a value using the given schema and options where:
173
182
  - `allowUnknown` - when `true`, allows object to contain unknown keys which are ignored. Defaults to `false`.
174
183
  - `skipFunctions` - when `true`, ignores unknown keys with a function value. Defaults to `false`.
175
184
  - `stripUnknown` - when `true`, unknown keys are deleted (only when value is an object). Defaults to `false`.
176
- - `language` - overrides individual error messages. Defaults to no override (`{}`).
185
+ - `language` - overrides individual error messages, when `'label'` is set, it overrides the key name in the error message. Defaults to no override (`{}`).
186
+ - `presence` - sets the default presence requirements. Supported modes: `'optional'`, `'required'`, and `'forbidden'`.
187
+ Defaults to `'optional'`.
177
188
  - `context` - provides an external data set to be used in [references](#refkey-options). Can only be set as an external option to
178
189
  `validate()` and not using `any.options()`.
179
- - `callback` - the synchronous callback method using the signature `function(err, value)` where:
190
+ - `callback` - the optional synchronous callback method using the signature `function(err, value)` where:
180
191
  - `err` - if validation failed, the error reason, otherwise `null`.
181
192
  - `value` - the validated value with any type conversions and other modifiers applied (the input is left unchanged). `value` can be
182
- incomplete if validation failed and `abortEarly` is `true`.
193
+ incomplete if validation failed and `abortEarly` is `true`. If callback is not provided, then returns an object with error
194
+ and value properties.
183
195
 
184
196
  ```javascript
185
197
  var schema = {
@@ -193,6 +205,11 @@ var value = {
193
205
  Joi.validate(value, schema, function (err, value) { });
194
206
  // err -> null
195
207
  // value.a -> 123 (number, not string)
208
+
209
+ // or
210
+ var result = Joi.validate(value, schema);
211
+ // result.error -> null
212
+ // result.value -> { "a" : 123 }
196
213
  ```
197
214
 
198
215
  ### `compile(schema)`
@@ -219,11 +236,12 @@ var schema = Joi.alternatives().try([
219
236
  ]);
220
237
  ```
221
238
 
222
- ### `assert(value, schema)`
239
+ ### `assert(value, schema, [message])`
223
240
 
224
241
  Validates a value against a schema and throws if validation fails where:
225
242
  - `value` - the value to validate.
226
243
  - `schema` - the schema object.
244
+ - `message` - optional message sting prefix added in front of the error message.
227
245
 
228
246
  ```javascript
229
247
  Joi.assert('x', Joi.number());
@@ -302,7 +320,7 @@ Marks a key as forbidden which will not allow any value except `undefined`. Used
302
320
 
303
321
  ```javascript
304
322
  var schema = {
305
- a: Joi.any.forbidden()
323
+ a: Joi.any().forbidden()
306
324
  };
307
325
  ```
308
326
 
@@ -422,6 +440,27 @@ var schema = {
422
440
  };
423
441
  ```
424
442
 
443
+ Alternatively, if you want to specify a specific type such as `string`, `array`, etc, you can do so like this:
444
+
445
+ ```javascript
446
+ var schema = {
447
+ a: Joi.valid('a', 'b', 'other'),
448
+ other: Joi.string()
449
+ .when('a', { is: 'other', then: Joi.required() }),
450
+ };
451
+ ```
452
+
453
+ #### `any.label(name)`
454
+
455
+ Overrides the key name in error messages.
456
+ - `name` - the name of the key.
457
+
458
+ ```javascript
459
+ var schema = {
460
+ first_name: Joi.string().label('First Name')
461
+ };
462
+ ```
463
+
425
464
  ### `array`
426
465
 
427
466
  Generates a schema object that matches an array data type.
@@ -478,6 +517,14 @@ Specifies the exact number of items in the array where:
478
517
  var schema = Joi.array().length(5);
479
518
  ```
480
519
 
520
+ #### `array.unique()`
521
+
522
+ Requires the array values to be unique. Only works for literals (numbers and strings), all other types are ignored.
523
+
524
+ ```javascript
525
+ var schema = Joi.array().unique();
526
+ ```
527
+
481
528
  ### `boolean`
482
529
 
483
530
  Generates a schema object that matches a boolean data type (as well as the strings 'true', 'false', 'yes', and 'no'). Can also be called via `bool()`.
@@ -555,6 +602,12 @@ Specifies the oldest date allowed where:
555
602
  var schema = Joi.date().min('1-1-1974');
556
603
  ```
557
604
 
605
+ Note: `'now'` can be passed in lieu of `date` so as to always compare relatively to the current date, allowing to explicitly ensure a date is either in the past or in the future.
606
+
607
+ ```javascript
608
+ var schema = Joi.date().min('now');
609
+ ```
610
+
558
611
  #### `date.max(date)`
559
612
 
560
613
  Specifies the latest date allowed where:
@@ -564,6 +617,29 @@ Specifies the latest date allowed where:
564
617
  var schema = Joi.date().max('12-31-2020');
565
618
  ```
566
619
 
620
+ Note: `'now'` can be passed in lieu of `date` so as to always compare relatively to the current date, allowing to explicitly ensure a date is either in the past or in the future.
621
+
622
+ ```javascript
623
+ var schema = Joi.date().max('now');
624
+ ```
625
+
626
+ #### `date.format(format)`
627
+
628
+ Specifies the allowed date format:
629
+ - `format` - string or array of strings that follow the `moment.js` [format](http://momentjs.com/docs/#/parsing/string-format/).
630
+
631
+ ```javascript
632
+ var schema = Joi.date().format('YYYY/MM/DD');
633
+ ```
634
+
635
+ #### `date.iso()`
636
+
637
+ Requires the string value to be in valid ISO 8601 date format.
638
+
639
+ ```javascript
640
+ var schema = Joi.date().iso();
641
+ ```
642
+
567
643
  ### `func`
568
644
 
569
645
  Generates a schema object that matches a function type.
@@ -604,6 +680,22 @@ Specifies the maximum value where:
604
680
  var schema = Joi.number().max(10);
605
681
  ```
606
682
 
683
+ #### `number.greater(limit)`
684
+
685
+ Specifies that the value must be greater than `limit`.
686
+
687
+ ```javascript
688
+ var schema = Joi.number().greater(5);
689
+ ```
690
+
691
+ #### `number.less(limit)`
692
+
693
+ Specifies that the value must be less than `limit`.
694
+
695
+ ```javascript
696
+ var schema = Joi.number().less(10);
697
+ ```
698
+
607
699
  #### `number.integer()`
608
700
 
609
701
  Requires the number to be an integer (no floating point).
@@ -612,6 +704,15 @@ Requires the number to be an integer (no floating point).
612
704
  var schema = Joi.number().integer();
613
705
  ```
614
706
 
707
+ #### `number.precision(limit)`
708
+
709
+ Specifies the maximum number of decimal places where:
710
+ - `limit` - the maximum number of decimal places allowed.
711
+
712
+ ```javascript
713
+ var schema = Joi.number().precision(2);
714
+ ```
715
+
615
716
  ### `object`
616
717
 
617
718
  Generates a schema object that matches an object data type (as well as JSON strings that parsed into objects). Defaults
@@ -630,17 +731,21 @@ object.validate({ a: 5 }, function (err, value) { });
630
731
 
631
732
  #### `object.keys([schema])`
632
733
 
633
- Sets the allowed object keys where:
634
- - `schema` - optional object where each key is assinged a **joi** type object. If `schema` is `{}` no keys allowed.
734
+ Sets or extends the allowed object keys where:
735
+ - `schema` - optional object where each key is assigned a **joi** type object. If `schema` is `{}` no keys allowed.
635
736
  If `schema` is `null` or `undefined`, any key allowed. If `schema` is an object with keys, the keys are added to any
636
737
  previously defined keys (but narrows the selection if all keys previously allowed). Defaults to 'undefined' which
637
738
  allows any child key.
638
739
 
639
740
  ```javascript
640
- var object = Joi.object().keys({
641
- a: Joi.number()
741
+ var base = Joi.object().keys({
742
+ a: Joi.number(),
642
743
  b: Joi.string()
643
744
  });
745
+ // Validate keys a, b and c.
746
+ var extended = base.keys({
747
+ c: Joi.boolean()
748
+ });
644
749
  ```
645
750
 
646
751
  #### `object.min(limit)`
@@ -696,6 +801,20 @@ var schema = Joi.object().keys({
696
801
  }).and('a', 'b');
697
802
  ```
698
803
 
804
+ #### `object.nand(peers)`
805
+
806
+ Defines a relationship between keys where not all peers can be present at the
807
+ same time where:
808
+ - `peers` - the key names of which if one present, the others may not all be present. `peers` can be a single string value, an
809
+ array of string values, or each peer provided as an argument.
810
+
811
+ ```javascript
812
+ var schema = Joi.object().keys({
813
+ a: Joi.any(),
814
+ b: Joi.any()
815
+ }).nand('a', 'b');
816
+ ```
817
+
699
818
  #### `object.or(peers)`
700
819
 
701
820
  Defines a relationship between keys where one of the peers is required (and more than one is allowed) where:
@@ -771,13 +890,13 @@ var object = Joi.object().keys({
771
890
  object.validate({ b: 5 }, function (err, value) { });
772
891
  ```
773
892
 
774
- #### `object.assert(ref, schema, message)`
893
+ #### `object.assert(ref, schema, [message])`
775
894
 
776
895
  Verifies an assertion where:
777
896
  - `ref` - the key name or [reference](#refkey-options).
778
897
  - `schema` - the validation rules required to satisfy the assertion. If the `schema` includes references, they are resolved against
779
898
  the object value, not the value of the `ref` target.
780
- - `message` - human-readable message used when the assertion fails.
899
+ - `message` - optional human-readable message used when the assertion fails. Defaults to 'failed to pass the assertion test'.
781
900
 
782
901
  ```javascript
783
902
  var schema = Joi.object().keys({
@@ -800,6 +919,16 @@ Overrides the handling of unknown keys for the scope of the current object only
800
919
  var schema = Joi.object({ a: Joi.any() }).unknown();
801
920
  ```
802
921
 
922
+ #### `object.type(constructor, [name])`
923
+
924
+ Requires the object to be an instance of a given constructor where:
925
+ - `constructor` - the constructor function that the object must be an instance of.
926
+ - `name` - an alternate name to use in validation errors. This is useful when the constructor function does not have a name.
927
+
928
+ ```javascript
929
+ var schema = Joi.object().type(RegExp);
930
+ ```
931
+
803
932
  ### `string`
804
933
 
805
934
  Generates a schema object that matches a string data type. Note that empty strings are not allowed by default and must be enabled with `allow('')`.
@@ -839,6 +968,15 @@ Specifies the maximum number of string characters where:
839
968
  var schema = Joi.string().max(10);
840
969
  ```
841
970
 
971
+ #### `string.creditCard()`
972
+
973
+ Requires the number to be a credit card number (Using [Lunh
974
+ Algorithm](http://en.wikipedia.org/wiki/Luhn_algorithm)).
975
+
976
+ ```javascript
977
+ var schema = Joi.string().creditCard();
978
+ ```
979
+
842
980
  #### `string.length(limit, [encoding])`
843
981
 
844
982
  Specifies the exact string length required where:
@@ -849,10 +987,11 @@ Specifies the exact string length required where:
849
987
  var schema = Joi.string().length(5);
850
988
  ```
851
989
 
852
- #### `string.regex(pattern)`
990
+ #### `string.regex(pattern, [name])`
853
991
 
854
992
  Defines a regular expression rule where:
855
993
  - `pattern` - a regular expression object the string value must match against.
994
+ - `name` - optional name for patterns (useful with multiple patterns). Defaults to 'required'.
856
995
 
857
996
  ```javascript
858
997
  var schema = Joi.string().regex(/^[abc]+$/);
@@ -890,14 +1029,6 @@ Requires the string value to be a valid GUID.
890
1029
  var schema = Joi.string().guid();
891
1030
  ```
892
1031
 
893
- #### `string.isoDate()`
894
-
895
- Requires the string value to be in valid ISO 8601 date format.
896
-
897
- ```javascript
898
- var schema = Joi.string().isoDate();
899
- ```
900
-
901
1032
  #### `string.hostname()`
902
1033
 
903
1034
  Requires the string value to be a valid hostname as per [RFC1123](http://tools.ietf.org/html/rfc1123).
@@ -947,7 +1078,7 @@ var alt = Joi.alternatives().try(Joi.number(), Joi.string());
947
1078
  // Same as [Joi.number(), Joi.string()]
948
1079
  ```
949
1080
 
950
- #### `alternatives.try(schemas)``
1081
+ #### `alternatives.try(schemas)`
951
1082
 
952
1083
  Adds an alternative schema type for attempting to match against the validated value where:
953
1084
  - `schema` - an array of alternative **joi** types. Also supports providing each type as a separate argument.
@@ -1003,8 +1134,8 @@ var schema = {
1003
1134
  Generates a reference to the value of the named key. References are resolved at validation time and in order of dependency
1004
1135
  so that if one key validation depends on another, the dependent key is validated second after the reference is validated.
1005
1136
  References support the following arguments:
1006
- - `key` - the reference target. References cannot point up the object tree, only to siebling keys, but they can point to
1007
- their siebling's children (e.g. 'a.b.c') using the `.` separator. If a `key` starts with `$` is signifies a context reference
1137
+ - `key` - the reference target. References cannot point up the object tree, only to sibling keys, but they can point to
1138
+ their siblings' children (e.g. 'a.b.c') using the `.` separator. If a `key` starts with `$` is signifies a context reference
1008
1139
  which is looked up in the `context` option object.
1009
1140
  - `options` - optional settings:
1010
1141
  - `separator` - overrides the default `.` hierarchy separator.
@@ -0,0 +1,43 @@
1
+ // This is an example of a survey to obtain the reputation of Parisians
2
+ // It contains examples of how to conditionally require keys based on values of other keys
3
+
4
+ var Joi = require("../");
5
+
6
+ // This is a valid value for integer rating 1 - 5
7
+ var intRating = Joi.number().integer().min(1).max(5);
8
+
9
+ var schema = Joi.object().keys({
10
+ // Do you know any French people? yes or no (required)
11
+ q1: Joi.boolean().required(),
12
+ // Do you know any Parisians? yes or no (required if answered yes in q1)
13
+ q2: Joi.boolean()
14
+ .when('q1', { is: true, then: Joi.required() }),
15
+ // How many french in paris do you know? 1-6, 6-10, 11-50 or 50+ (required if answered yes in q2)
16
+ q3: Joi.string()
17
+ .when('q2', { is: true, then: Joi.valid('1-5', '6-10', '11-50', '50+').required() }),
18
+ // Rate 20% of most friendly Parisians, from how many people you know answered in q3, individually on 1-5 rating
19
+ q4: Joi.array()
20
+ .when('q3', {is: '1-5', then: Joi.array().min(0).max(1).includes(intRating).required() })
21
+ .when('q3', {is: '6-10', then: Joi.array().min(1).max(2).includes(intRating).required() })
22
+ .when('q3', {is: '11-50', then: Joi.array().min(2).max(10).includes(intRating).required() })
23
+ .when('q3', {is: '50+' , then: Joi.array().min(10).includes(intRating).required() }),
24
+ // Rate remaining 80% of Parisians, from how many people you know answered in q3, individually on 1-5 rating
25
+ q5: Joi.array()
26
+ .when('q3', {is: '1-5', then: Joi.array().min(1).max(4).includes(intRating).required() })
27
+ .when('q3', {is: '6-10', then: Joi.array().min(4).max(8).includes(intRating).required() })
28
+ .when('q3', {is: '11-50', then: Joi.array().min(8).max(40).includes(intRating).required() })
29
+ .when('q3', {is: '50+' , then: Joi.array().min(40).includes(intRating).required().required() }),
30
+ // Rate the reputation of Parisians in general, 1-5 rating
31
+ q6: intRating.required()
32
+ });
33
+
34
+ var response = {
35
+ q1: true,
36
+ q2: true,
37
+ q3: '1-5',
38
+ q4: [5],
39
+ q4: [1],
40
+ q6: 2
41
+ };
42
+
43
+ Joi.assert(response, schema);
@@ -0,0 +1,22 @@
1
+ var Joi = require('../');
2
+
3
+
4
+ var schema = Joi.object().options({ abortEarly: false }).keys({
5
+ email: Joi.string().email().required().label('User Email'),
6
+ password: Joi.string().min(8).required(),
7
+ password_confirmation: Joi.any().valid(Joi.ref('password')).required().options({ language: { any: { allowOnly: 'must match password' }, label: 'Password Confirmation' } }).label('This label is not used because language.label takes precedence'),
8
+ first_name: Joi.string().required(),
9
+ last_name: Joi.string().required(),
10
+ company: Joi.string().optional()
11
+ });
12
+
13
+
14
+ var data = {
15
+ email: 'not_a_valid_email_to_show_custom_label',
16
+ password: 'abcd1234',
17
+ password_confirmation: 'abc1',
18
+ first_name: 'Joe',
19
+ last_name: 'Doe'
20
+ };
21
+
22
+ Joi.assert(data, schema);
@@ -0,0 +1,17 @@
1
+ var Joi = require('../');
2
+
3
+
4
+ var schema = {
5
+ type: Joi.string().required(),
6
+ subtype: Joi.alternatives()
7
+ .when('type', {is: 'video', then: Joi.valid('mp4', 'wav')})
8
+ .when('type', {is: 'audio', then: Joi.valid('mp3')})
9
+ .when('type', {is: 'image', then: Joi.valid('jpg', 'png')})
10
+ .when('type', {is: 'pdf' , then: Joi.valid('document')})
11
+ };
12
+
13
+
14
+ Joi.assert({ type: 'video', subtype: 'mp4' }, schema); // Pass
15
+ Joi.assert({ type: 'video', subtype: 'wav' }, schema); // Pass
16
+ Joi.assert({ type: 'other', subtype: 'something' }, schema); // Fail
17
+ Joi.assert({ type: 'audio', subtype: 'mp4' }, schema); // Fail
package/lib/any.js CHANGED
@@ -19,7 +19,8 @@ internals.defaults = {
19
19
  allowUnknown: false,
20
20
  skipFunctions: false,
21
21
  stripUnknown: false,
22
- language: {}
22
+ language: {},
23
+ presence: 'optional'
23
24
  // context: null
24
25
  };
25
26
 
@@ -43,7 +44,7 @@ module.exports = internals.Any = function () {
43
44
  insensitive: false,
44
45
  trim: false,
45
46
  case: undefined // upper, lower
46
- */};
47
+ */ };
47
48
 
48
49
  this._description = null;
49
50
  this._unit = null;
@@ -61,8 +62,7 @@ internals.Any.prototype.isImmutable = true; // Prevents Hoek from deep cloni
61
62
 
62
63
  internals.Any.prototype.clone = function () {
63
64
 
64
- var obj = {};
65
- obj.__proto__ = Object.getPrototypeOf(this);
65
+ var obj = Object.create(Object.getPrototypeOf(this));
66
66
 
67
67
  obj.isJoi = true;
68
68
  obj._type = this._type;
@@ -207,24 +207,24 @@ internals.Any.prototype.required = internals.Any.prototype.exist = function () {
207
207
  internals.Any.prototype.optional = function () {
208
208
 
209
209
  var obj = this.clone();
210
- delete obj._flags.presence; // Defaults to 'optional'
210
+ obj._flags.presence = 'optional';
211
211
  return obj;
212
212
  };
213
213
 
214
214
 
215
- internals.Any.prototype.default = function (value) {
215
+ internals.Any.prototype.forbidden = function () {
216
216
 
217
217
  var obj = this.clone();
218
- obj._flags.default = value;
219
- Ref.push(obj._refs, value);
218
+ obj._flags.presence = 'forbidden';
220
219
  return obj;
221
220
  };
222
221
 
223
222
 
224
- internals.Any.prototype.forbidden = function () {
223
+ internals.Any.prototype.default = function (value) {
225
224
 
226
225
  var obj = this.clone();
227
- obj._flags.presence = 'forbidden';
226
+ obj._flags.default = value;
227
+ Ref.push(obj._refs, value);
228
228
  return obj;
229
229
  };
230
230
 
@@ -329,26 +329,25 @@ internals.Any.prototype._validate = function (value, state, options, reference)
329
329
 
330
330
  // Check presence requirements
331
331
 
332
- if (this._flags.presence) { // 'required', 'forbidden', or 'ignore'
333
- if (this._flags.presence === 'required' &&
334
- value === undefined) {
335
-
336
- errors.push(Errors.create('any.required', null, state, options));
332
+ var presence = this._flags.presence || options.presence;
333
+ if (presence === 'optional') {
334
+ if (value === undefined) {
337
335
  return finish();
338
336
  }
339
- else if (this._flags.presence === 'forbidden') {
340
- if (value === undefined) {
341
- return finish();
342
- }
337
+ }
338
+ else if (presence === 'required' &&
339
+ value === undefined) {
343
340
 
344
- errors.push(Errors.create('any.unknown', null, state, options));
345
- return finish();
346
- }
341
+ errors.push(Errors.create('any.required', null, state, options));
342
+ return finish();
347
343
  }
348
- else { // 'optional'
344
+ else if (presence === 'forbidden') {
349
345
  if (value === undefined) {
350
346
  return finish();
351
347
  }
348
+
349
+ errors.push(Errors.create('any.unknown', null, state, options));
350
+ return finish();
352
351
  }
353
352
 
354
353
  // Check allowed and denied values using the original value
@@ -496,7 +495,7 @@ internals.Any.prototype.describe = function () {
496
495
  for (var i = 0, il = this._tests.length; i < il; ++i) {
497
496
  var validator = this._tests[i];
498
497
  var item = { name: validator.name };
499
- if (validator.arg) {
498
+ if (validator.arg !== void 0) {
500
499
  item.arg = validator.arg;
501
500
  }
502
501
  description.rules.push(item);
@@ -509,6 +508,18 @@ internals.Any.prototype.describe = function () {
509
508
  return description;
510
509
  };
511
510
 
511
+ internals.Any.prototype.label = function (name) {
512
+
513
+ Hoek.assert(name && typeof name === 'string', 'Label name must be a non-empty string');
514
+
515
+ var obj = this.clone();
516
+ var options = { language: { label: name } };
517
+
518
+ // If language.label is set, it should override this label
519
+ obj._settings = internals.concatSettings(options, obj._settings);
520
+ return obj;
521
+ };
522
+
512
523
 
513
524
  // Set
514
525
 
package/lib/array.js CHANGED
@@ -189,4 +189,29 @@ internals.Array.prototype.length = function (limit) {
189
189
  };
190
190
 
191
191
 
192
+ internals.Array.prototype.unique = function () {
193
+
194
+ return this._test('unique', undefined, function (value, state, options) {
195
+
196
+ var found = {
197
+ string: {},
198
+ number: {}
199
+ };
200
+
201
+ for (var i = 0, il = value.length; i < il; ++i) {
202
+ var item = value[i];
203
+ var type = typeof item;
204
+ var records = found[type];
205
+ if (records) {
206
+ if (records[item]) {
207
+ return Errors.create('array.unique', { pos: i }, state, options);
208
+ }
209
+
210
+ records[item] = true;
211
+ }
212
+ }
213
+ });
214
+ };
215
+
216
+
192
217
  module.exports = new internals.Array();