joi 8.3.0 → 9.0.0-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.
package/README.md CHANGED
@@ -95,4 +95,4 @@ When validating a schema:
95
95
  * Rules are defined in an additive fashion and evaluated in order after whitelist and blacklist checks.
96
96
 
97
97
  # API
98
- See the [API Reference](https://github.com/hapijs/joi/blob/v8.3.0/API.md).
98
+ See the [API Reference](https://github.com/hapijs/joi/blob/v9.0.0-0/API.md).
@@ -0,0 +1,52 @@
1
+ 'use strict';
2
+
3
+ // Load modules
4
+
5
+ const Joi = require('../');
6
+
7
+ // Delcare internals
8
+
9
+ const internals = {};
10
+
11
+
12
+ // This is an example of a survey to obtain the reputation of Parisians
13
+ // It contains examples of how to conditionally require keys based on values of other keys
14
+
15
+ // This is a valid value for integer rating 1 - 5
16
+ const intRating = Joi.number().integer().min(1).max(5);
17
+
18
+ const schema = Joi.object().keys({
19
+ // Do you know any French people? yes or no (required)
20
+ q1: Joi.boolean().required(),
21
+ // Do you know any Parisians? yes or no (required if answered yes in q1)
22
+ q2: Joi.boolean()
23
+ .when('q1', { is: true, then: Joi.required() }),
24
+ // How many french in paris do you know? 1-6, 6-10, 11-50 or 50+ (required if answered yes in q2)
25
+ q3: Joi.string()
26
+ .when('q2', { is: true, then: Joi.valid('1-5', '6-10', '11-50', '50+').required() }),
27
+ // Rate 20% of most friendly Parisians, from how many people you know answered in q3, individually on 1-5 rating
28
+ q4: Joi.array()
29
+ .when('q3', {is: '1-5', then: Joi.array().min(0).max(1).items(intRating).required() })
30
+ .when('q3', {is: '6-10', then: Joi.array().min(1).max(2).items(intRating).required() })
31
+ .when('q3', {is: '11-50', then: Joi.array().min(2).max(10).items(intRating).required() })
32
+ .when('q3', {is: '50+', then: Joi.array().min(10).items(intRating).required() }),
33
+ // Rate remaining 80% of Parisians, from how many people you know answered in q3, individually on 1-5 rating
34
+ q5: Joi.array()
35
+ .when('q3', {is: '1-5', then: Joi.array().min(1).max(4).items(intRating).required() })
36
+ .when('q3', {is: '6-10', then: Joi.array().min(4).max(8).items(intRating).required() })
37
+ .when('q3', {is: '11-50', then: Joi.array().min(8).max(40).items(intRating).required() })
38
+ .when('q3', {is: '50+', then: Joi.array().min(40).items(intRating).required().required() }),
39
+ // Rate the reputation of Parisians in general, 1-5 rating
40
+ q6: intRating.required()
41
+ });
42
+
43
+ const response = {
44
+ q1: true,
45
+ q2: true,
46
+ q3: '1-5',
47
+ q4: [5],
48
+ q5: [1],
49
+ q6: 2
50
+ };
51
+
52
+ Joi.assert(response, schema);
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ // Load modules
4
+
5
+ const Joi = require('../');
6
+
7
+
8
+ // Declare internals
9
+
10
+ const internals = {};
11
+
12
+
13
+ const schema = Joi.object().options({ abortEarly: false }).keys({
14
+ email: Joi.string().email().required().label('User Email'),
15
+ password: Joi.string().min(8).required(),
16
+ 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'),
17
+ first_name: Joi.string().required(),
18
+ last_name: Joi.string().required(),
19
+ company: Joi.string().optional()
20
+ });
21
+
22
+
23
+ const data = {
24
+ email: 'not_a_valid_email_to_show_custom_label',
25
+ password: 'abcd1234',
26
+ password_confirmation: 'abc1',
27
+ first_name: 'Joe',
28
+ last_name: 'Doe'
29
+ };
30
+
31
+ Joi.assert(data, schema);
@@ -0,0 +1,26 @@
1
+ 'use strict';
2
+
3
+ // Load modules
4
+
5
+ const Joi = require('../');
6
+
7
+
8
+ // Declare internals
9
+
10
+ const internals = {};
11
+
12
+
13
+ const schema = {
14
+ type: Joi.string().required(),
15
+ subtype: Joi.alternatives()
16
+ .when('type', {is: 'video', then: Joi.valid('mp4', 'wav')})
17
+ .when('type', {is: 'audio', then: Joi.valid('mp3')})
18
+ .when('type', {is: 'image', then: Joi.valid('jpg', 'png')})
19
+ .when('type', {is: 'pdf' , then: Joi.valid('document')})
20
+ };
21
+
22
+
23
+ Joi.assert({ type: 'video', subtype: 'mp4' }, schema); // Pass
24
+ Joi.assert({ type: 'video', subtype: 'wav' }, schema); // Pass
25
+ Joi.assert({ type: 'other', subtype: 'something' }, schema); // Fail
26
+ Joi.assert({ type: 'audio', subtype: 'mp4' }, schema); // Fail
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ // Load modules
4
+ const Joi = require('../');
5
+
6
+ const now = new Date();
7
+ const javascriptTimestamp = now.getTime();
8
+ const unixTimestamp = now.getTime() / 1000;
9
+
10
+ const schema = Joi.object().options({ abortEarly: false }).keys({
11
+ javascript1: Joi.date().timestamp(),
12
+ javascript2: Joi.date().timestamp('javascript'),
13
+ unix: Joi.date().timestamp('unix')
14
+ });
15
+
16
+ const data = {
17
+ javascript1: javascriptTimestamp,
18
+ javascript2: javascriptTimestamp,
19
+ unix: unixTimestamp
20
+ };
21
+
22
+ Joi.assert(data, schema);
@@ -0,0 +1,49 @@
1
+ 'use strict';
2
+
3
+ // Load modules
4
+
5
+ const Toc = require('markdown-toc');
6
+ const Fs = require('fs');
7
+ const Package = require('./package.json');
8
+
9
+ // Declare internals
10
+
11
+ const internals = {
12
+ api: {
13
+ filename: './API.md',
14
+ contents: Fs.readFileSync('./API.md', 'utf8')
15
+ },
16
+ readme: {
17
+ filename: './README.md',
18
+ contents: Fs.readFileSync('./README.md', 'utf8')
19
+ }
20
+ };
21
+
22
+ internals.generateToc = function () {
23
+
24
+ const tocOptions = {
25
+ bullets: '-',
26
+ slugify: function (text) {
27
+
28
+ return text.toLowerCase()
29
+ .replace(/\s/g, '-')
30
+ .replace(/[^\w-]/g, '');
31
+ }
32
+ };
33
+
34
+ const api = Toc.insert(internals.api.contents, tocOptions)
35
+ .replace(/<!-- version -->(.|\n)*<!-- versionstop -->/, `<!-- version -->\n# ${Package.version} API Reference\n<!-- versionstop -->`);
36
+
37
+ Fs.writeFileSync(internals.api.filename, api);
38
+ };
39
+
40
+ internals.generateLink = function () {
41
+ // create absolute URL for versioned docs
42
+ const readme = internals.readme.contents
43
+ .replace(/\[API Reference\]\(.*\)/gi, `[API Reference](${Package.homepage || ''}/blob/v${Package.version}/${internals.api.filename.substr(2)})`);
44
+
45
+ Fs.writeFileSync(internals.readme.filename, readme);
46
+ };
47
+
48
+ internals.generateToc();
49
+ internals.generateLink();
package/images/joi.png ADDED
Binary file
Binary file
package/lib/any.js CHANGED
@@ -32,26 +32,37 @@ internals.defaults = {
32
32
 
33
33
  internals.checkOptions = function (options) {
34
34
 
35
- const Joi = require('../');
35
+ const optionType = {
36
+ abortEarly: 'boolean',
37
+ convert: 'boolean',
38
+ allowUnknown: 'boolean',
39
+ skipFunctions: 'boolean',
40
+ stripUnknown: 'boolean',
41
+ language: 'object',
42
+ presence: ['string', 'required', 'optional', 'forbidden', 'ignore'],
43
+ raw: 'boolean',
44
+ context: 'object',
45
+ strip: 'boolean',
46
+ noDefaults: 'boolean'
47
+ };
48
+
49
+ const keys = Object.keys(options);
50
+ for (let i = 0; i < keys.length; ++i) {
51
+ const key = keys[i];
52
+ const opt = optionType[key];
53
+ let type = opt;
54
+ let values = null;
36
55
 
37
- const optionsSchema = Joi.object({
38
- abortEarly: Joi.boolean(),
39
- convert: Joi.boolean(),
40
- allowUnknown: Joi.boolean(),
41
- skipFunctions: Joi.boolean(),
42
- stripUnknown: [Joi.boolean(), Joi.object({ arrays: Joi.boolean(), objects: Joi.boolean() }).or('arrays', 'objects')],
43
- language: Joi.object(),
44
- presence: Joi.string().only('required', 'optional', 'forbidden', 'ignore'),
45
- raw: Joi.boolean(),
46
- context: Joi.object(),
47
- strip: Joi.boolean(),
48
- noDefaults: Joi.boolean(),
49
- error: Joi.object()
50
- }).strict();
56
+ if (Array.isArray(opt)) {
57
+ type = opt[0];
58
+ values = opt.slice(1);
59
+ }
51
60
 
52
- const result = optionsSchema.validate(options);
53
- if (result.error) {
54
- throw new Error(result.error.details[0].message);
61
+ Hoek.assert(type, 'unknown key ' + key);
62
+ Hoek.assert(typeof options[key] === type, key + ' should be of type ' + type);
63
+ if (values) {
64
+ Hoek.assert(values.indexOf(options[key]) >= 0, key + ' should be one of ' + values.join(', '));
65
+ }
55
66
  }
56
67
  };
57
68
 
@@ -67,8 +78,7 @@ module.exports = internals.Any = function () {
67
78
  this._invalids = new internals.Set();
68
79
  this._tests = [];
69
80
  this._refs = [];
70
- this._flags = {
71
- /*
81
+ this._flags = { /*
72
82
  presence: 'optional', // optional, required, forbidden, ignore
73
83
  allowOnly: false,
74
84
  allowUnknown: undefined,
@@ -80,8 +90,7 @@ module.exports = internals.Any = function () {
80
90
  case: undefined, // upper, lower
81
91
  empty: undefined,
82
92
  func: false
83
- */
84
- };
93
+ */ };
85
94
 
86
95
  this._description = null;
87
96
  this._unit = null;
@@ -208,10 +217,10 @@ internals.Any.prototype.concat = function (schema) {
208
217
  };
209
218
 
210
219
 
211
- internals.Any.prototype._test = function (name, arg, func) {
220
+ internals.Any.prototype._test = function (name, arg, func, options) {
212
221
 
213
222
  const obj = this.clone();
214
- obj._tests.push({ func, name, arg });
223
+ obj._tests.push({ func, name, arg, options });
215
224
  return obj;
216
225
  };
217
226
 
@@ -245,17 +254,6 @@ internals.Any.prototype.raw = function (isRaw) {
245
254
  };
246
255
 
247
256
 
248
- internals.Any.prototype.error = function (err) {
249
-
250
- Hoek.assert(err && err instanceof Error, 'Must provide a valid Error object');
251
-
252
- const obj = this.clone();
253
- obj._settings = obj._settings || {};
254
- obj._settings.error = err;
255
- return obj;
256
- };
257
-
258
-
259
257
  internals.Any.prototype._allow = function () {
260
258
 
261
259
  const values = Hoek.flatten(Array.prototype.slice.call(arguments));
@@ -394,7 +392,7 @@ internals.Any.prototype.when = function (ref, options) {
394
392
  Alternatives = Alternatives || require('./alternatives');
395
393
  const obj = Alternatives.when(ref, { is: options.is, then, otherwise });
396
394
  obj._flags.presence = 'ignore';
397
- obj._settings = internals.concatSettings(obj._settings, { baseType: this });
395
+ obj._settings = internals.concatSettings({ baseType: this }, obj._settings);
398
396
 
399
397
  return obj;
400
398
  };
@@ -625,13 +623,16 @@ internals.Any.prototype._validate = function (value, state, options, reference)
625
623
 
626
624
  for (let i = 0; i < this._tests.length; ++i) {
627
625
  const test = this._tests[i];
628
- const err = test.func.call(this, value, state, options);
629
- if (err) {
630
- errors.push(err);
626
+ const ret = test.func.call(this, value, state, options);
627
+ if (ret && ret.isJoi) {
628
+ errors.push(ret);
631
629
  if (options.abortEarly) {
632
630
  return finish();
633
631
  }
634
632
  }
633
+ else if (ret !== null) {
634
+ value = ret;
635
+ }
635
636
  }
636
637
 
637
638
  return finish();
@@ -677,7 +678,7 @@ internals.Any.prototype.describe = function () {
677
678
 
678
679
  const flags = Object.keys(this._flags);
679
680
  if (flags.length) {
680
- if (this._flags.hasOwnProperty('empty') || this._flags.hasOwnProperty('default') || this._flags.lazy) {
681
+ if (this._flags.hasOwnProperty('empty') || this._flags.hasOwnProperty('default')) {
681
682
  description.flags = {};
682
683
  for (let i = 0; i < flags.length; ++i) {
683
684
  const flag = flags[i];
@@ -695,9 +696,6 @@ internals.Any.prototype.describe = function () {
695
696
  description.flags[flag] = this._flags[flag];
696
697
  }
697
698
  }
698
- else if (flag === 'lazy') {
699
- // We don't want it in the description
700
- }
701
699
  else {
702
700
  description.flags[flag] = this._flags[flag];
703
701
  }
@@ -753,9 +751,31 @@ internals.Any.prototype.describe = function () {
753
751
  for (let i = 0; i < this._tests.length; ++i) {
754
752
  const validator = this._tests[i];
755
753
  const item = { name: validator.name };
754
+
756
755
  if (validator.arg !== void 0) {
757
756
  item.arg = Ref.isRef(validator.arg) ? validator.arg.toString() : validator.arg;
758
757
  }
758
+
759
+ const options = validator.options;
760
+ if (options) {
761
+ if (options.hasRef) {
762
+ item.arg = {};
763
+ const keys = Object.keys(validator.arg);
764
+ for (let j = 0; j < keys.length; ++j) {
765
+ const key = keys[j];
766
+ const value = validator.arg[key];
767
+ item.arg[key] = Ref.isRef(value) ? value.toString() : value;
768
+ }
769
+ }
770
+
771
+ if (typeof options.description === 'string') {
772
+ item.description = options.description;
773
+ }
774
+ else if (typeof options.description === 'function') {
775
+ item.description = options.description(item.arg);
776
+ }
777
+ }
778
+
759
779
  description.rules.push(item);
760
780
  }
761
781
 
@@ -771,7 +791,6 @@ internals.Any.prototype.describe = function () {
771
791
  return description;
772
792
  };
773
793
 
774
-
775
794
  internals.Any.prototype.label = function (name) {
776
795
 
777
796
  Hoek.assert(name && typeof name === 'string', 'Label name must be a non-empty string');
@@ -779,7 +798,8 @@ internals.Any.prototype.label = function (name) {
779
798
  const obj = this.clone();
780
799
  const options = { language: { label: name } };
781
800
 
782
- obj._settings = internals.concatSettings(obj._settings, options);
801
+ // If language.label is set, it should override this label
802
+ obj._settings = internals.concatSettings(options, obj._settings);
783
803
  return obj;
784
804
  };
785
805
 
package/lib/array.js CHANGED
@@ -206,10 +206,6 @@ internals.checkItems = function (items, wasArray, state, options) {
206
206
 
207
207
  // Inclusions
208
208
 
209
- const stripUnknown = options.stripUnknown
210
- ? (options.stripUnknown === true ? true : !!options.stripUnknown.arrays)
211
- : false;
212
-
213
209
  jl = inclusions.length;
214
210
  for (let j = 0; j < jl; ++j) {
215
211
  const inclusion = inclusions[j];
@@ -238,7 +234,7 @@ internals.checkItems = function (items, wasArray, state, options) {
238
234
 
239
235
  // Return the actual error if only one inclusion defined
240
236
  if (jl === 1) {
241
- if (stripUnknown) {
237
+ if (options.stripUnknown) {
242
238
  internals.fastSplice(items, i);
243
239
  --i;
244
240
  --il;
@@ -262,7 +258,7 @@ internals.checkItems = function (items, wasArray, state, options) {
262
258
  }
263
259
 
264
260
  if (this._inner.inclusions.length && !isValid) {
265
- if (stripUnknown) {
261
+ if (options.stripUnknown) {
266
262
  internals.fastSplice(items, i);
267
263
  --i;
268
264
  --il;
package/lib/boolean.js CHANGED
@@ -30,8 +30,15 @@ internals.Boolean.prototype._base = function (value, state, options) {
30
30
  options.convert) {
31
31
 
32
32
  const lower = value.toLowerCase();
33
- result.value = (lower === 'true' || lower === 'yes' || lower === 'on' ? true
34
- : (lower === 'false' || lower === 'no' || lower === 'off' ? false : value));
33
+ result.value = (lower === 'true' || lower === 'yes' || lower === 'on' || lower === '1' ? true
34
+ : (lower === 'false' || lower === 'no' || lower === 'off' || lower === '0' ? false : value));
35
+ }
36
+
37
+ if (typeof value === 'number' &&
38
+ options.convert) {
39
+
40
+ result.value = (value === 1 ? true
41
+ : (value === 0 ? false : value));
35
42
  }
36
43
 
37
44
  result.errors = (typeof result.value === 'boolean') ? null : this.createError('boolean.base', null, state, options);
package/lib/errors.js CHANGED
@@ -115,10 +115,6 @@ exports.process = function (errors, object) {
115
115
  for (let i = 0; i < localErrors.length; ++i) {
116
116
  const item = localErrors[i];
117
117
 
118
- if (item.options.error) {
119
- return item.options.error;
120
- }
121
-
122
118
  const detail = {
123
119
  message: item.toString(),
124
120
  path: internals.getPath(item),
@@ -131,12 +127,8 @@ exports.process = function (errors, object) {
131
127
  }
132
128
 
133
129
  // Do not push intermediate errors, we're only interested in leafs
134
-
135
130
  if (item.context.reason && item.context.reason.length) {
136
- const override = processErrors(item.context.reason, item.path);
137
- if (override) {
138
- return override;
139
- }
131
+ processErrors(item.context.reason, item.path);
140
132
  }
141
133
  else {
142
134
  details.push(detail);
@@ -144,10 +136,7 @@ exports.process = function (errors, object) {
144
136
  }
145
137
  };
146
138
 
147
- const override = processErrors(errors);
148
- if (override) {
149
- return override;
150
- }
139
+ processErrors(errors);
151
140
 
152
141
  const error = new Error(message);
153
142
  error.isJoi = true;
package/lib/index.js CHANGED
@@ -5,7 +5,6 @@
5
5
  const Hoek = require('hoek');
6
6
  const Any = require('./any');
7
7
  const Cast = require('./cast');
8
- const Lazy = require('./lazy');
9
8
  const Ref = require('./ref');
10
9
 
11
10
 
@@ -173,13 +172,153 @@ internals.root = function () {
173
172
  }
174
173
  };
175
174
 
176
- root.lazy = function (fn) {
175
+ root.extend = function () {
177
176
 
178
- return Lazy.set(fn);
177
+ const extensions = Hoek.flatten(Array.prototype.slice.call(arguments));
178
+ Hoek.assert(extensions.length > 0, 'You need to provide at least one extension');
179
+
180
+ this.assert(extensions, internals.extensionsSchema);
181
+
182
+ const joi = Object.assign({}, this);
183
+
184
+ for (let i = 0; i < extensions.length; ++i) {
185
+ const extension = extensions[i];
186
+ const base = (extension.base || this.any()).clone(); // Cloning because we're going to override language afterwards
187
+ const ctor = base.constructor;
188
+ const type = function () { // eslint-disable-line no-loop-func
189
+
190
+ ctor.call(this);
191
+
192
+ if (extension.base) {
193
+ Object.assign(this, base);
194
+ }
195
+
196
+ this._type = extension.name;
197
+
198
+ if (extension.language) {
199
+ this._settings = this._settings || { language: {} };
200
+ this._settings.language = Hoek.applyToDefaults(this._settings.language, {
201
+ [extension.name]: extension.language
202
+ });
203
+ }
204
+ };
205
+ Hoek.inherits(type, ctor);
206
+
207
+ if (extension.pre) {
208
+ type.prototype._base = function (value, state, options) {
209
+
210
+ if (ctor.prototype._base) {
211
+ const baseRet = ctor.prototype._base.call(this, value, state, options);
212
+
213
+ if (baseRet.errors) {
214
+ return baseRet;
215
+ }
216
+
217
+ value = baseRet.value;
218
+ }
219
+
220
+ const ret = extension.pre.call(this, value, state, options);
221
+ if (ret && ret.isJoi) {
222
+ return { value, errors: ret };
223
+ }
224
+ else if (ret !== null) {
225
+ return { value: ret };
226
+ }
227
+
228
+ return { value };
229
+ };
230
+ }
231
+
232
+ if (extension.rules) {
233
+ for (let j = 0; j < extension.rules.length; ++j) {
234
+ const rule = extension.rules[j];
235
+ const ruleArgs = rule.params ?
236
+ (rule.params instanceof Any ? rule.params._inner.children.map((k) => k.key) : Object.keys(rule.params)) :
237
+ [];
238
+ const validateArgs = rule.params ? Cast.schema(rule.params) : null;
239
+
240
+ type.prototype[rule.name] = function () { // eslint-disable-line no-loop-func
241
+
242
+ if (arguments.length > ruleArgs.length) {
243
+ throw new Error('Unexpected number of arguments');
244
+ }
245
+
246
+ const args = Array.prototype.slice.call(arguments);
247
+ let hasRef = false;
248
+ const arg = {};
249
+
250
+ for (let k = 0; k < ruleArgs.length; ++k) {
251
+ arg[ruleArgs[k]] = args[k];
252
+ if (!hasRef && Ref.isRef(args[k])) {
253
+ hasRef = true;
254
+ }
255
+ }
256
+
257
+ if (validateArgs) {
258
+ joi.assert(arg, validateArgs);
259
+ }
260
+
261
+ let schema;
262
+ if (rule.validate) {
263
+ const validate = rule.validate.bind(this, arg);
264
+
265
+ schema = this._test(rule.name, arg, validate, {
266
+ description: rule.description,
267
+ hasRef
268
+ });
269
+ }
270
+ else {
271
+ schema = this.clone();
272
+ }
273
+
274
+ if (rule.setup) {
275
+ rule.setup.call(schema, arg);
276
+ }
277
+
278
+ return schema;
279
+ };
280
+ }
281
+ }
282
+
283
+ if (extension.describe) {
284
+ type.prototype.describe = function () {
285
+
286
+ const description = ctor.prototype.describe.call(this);
287
+ return extension.describe.call(this, description);
288
+ };
289
+ }
290
+
291
+ const instance = new type();
292
+ joi[extension.name] = function () {
293
+
294
+ return instance;
295
+ };
296
+ }
297
+
298
+ return joi;
179
299
  };
180
300
 
181
301
  return root;
182
302
  };
183
303
 
184
304
 
305
+ internals.extensionsSchema = internals.array.items(internals.object.keys({
306
+ base: internals.object.type(Any, 'Joi object'),
307
+ name: internals.string.required(),
308
+ pre: internals.object._func().arity(3),
309
+ language: internals.object,
310
+ describe: internals.object._func().arity(1),
311
+ rules: internals.array.items(internals.object.keys({
312
+ name: internals.string.required(),
313
+ setup: internals.object._func().arity(1),
314
+ validate: internals.object._func().arity(4),
315
+ params: [
316
+ internals.object.pattern(/.*/, internals.object.type(Any, 'Joi object')),
317
+ internals.object.type(internals.object.constructor, 'Joi object')
318
+ ],
319
+ description: [internals.string, internals.object._func().arity(1)]
320
+ }).or('setup', 'validate'))
321
+ })).strict();
322
+
323
+
185
324
  module.exports = internals.root();
package/lib/language.js CHANGED
@@ -68,11 +68,8 @@ exports.errors = {
68
68
  base: 'must be a Function',
69
69
  arity: 'must have an arity of {{n}}',
70
70
  minArity: 'must have an arity greater or equal to {{n}}',
71
- maxArity: 'must have an arity lesser or equal to {{n}}'
72
- },
73
- lazy: {
74
- base: '!!schema error: lazy schema must be set',
75
- schema: '!!schema error: lazy schema function must return a schema'
71
+ maxArity: 'must have an arity lesser or equal to {{n}}',
72
+ ref: 'must be a Joi reference'
76
73
  },
77
74
  object: {
78
75
  base: 'must be an object',