joi 10.5.2 → 10.6.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
@@ -125,7 +125,7 @@ When validating a schema:
125
125
  * Rules are defined in an additive fashion and evaluated in order after whitelist and blacklist checks.
126
126
 
127
127
  # API
128
- See the [API Reference](https://github.com/hapijs/joi/blob/v10.5.2/API.md).
128
+ See the [API Reference](https://github.com/hapijs/joi/blob/v10.6.0/API.md).
129
129
 
130
130
  # Browsers
131
131
 
package/lib/index.js CHANGED
@@ -330,7 +330,11 @@ internals.root = function () {
330
330
  }
331
331
 
332
332
  if (rule.setup) {
333
- rule.setup.call(schema, arg);
333
+ const newSchema = rule.setup.call(schema, arg);
334
+ if (newSchema !== undefined) {
335
+ Hoek.assert(newSchema instanceof Any, `Setup of extension Joi.${this._type}().${rule.name}() must return undefined or a Joi object`);
336
+ schema = newSchema;
337
+ }
334
338
  }
335
339
 
336
340
  return schema;
package/lib/language.js CHANGED
@@ -42,6 +42,7 @@ exports.errors = {
42
42
  length: 'must contain {{limit}} items',
43
43
  ordered: 'at position {{pos}} fails because {{reason}}',
44
44
  orderedLength: 'at position {{pos}} fails because array must contain at most {{limit}} items',
45
+ ref: 'references "{{ref}}" which is not a positive integer',
45
46
  sparse: 'must not be a sparse array',
46
47
  unique: 'position {{pos}} contains a duplicate value'
47
48
  },
@@ -4,6 +4,7 @@
4
4
 
5
5
  const Any = require('../any');
6
6
  const Cast = require('../../cast');
7
+ const Ref = require('../../ref');
7
8
  const Hoek = require('hoek');
8
9
 
9
10
 
@@ -390,11 +391,25 @@ internals.Array = class extends Any {
390
391
 
391
392
  min(limit) {
392
393
 
393
- Hoek.assert(Number.isSafeInteger(limit) && limit >= 0, 'limit must be a positive integer');
394
+ const isRef = Ref.isRef(limit);
395
+
396
+ Hoek.assert((Number.isSafeInteger(limit) && limit >= 0) || isRef, 'limit must be a positive integer or reference');
394
397
 
395
398
  return this._test('min', limit, function (value, state, options) {
396
399
 
397
- if (value.length >= limit) {
400
+ let compareTo;
401
+ if (isRef) {
402
+ compareTo = limit(state.reference || state.parent, options);
403
+
404
+ if (!(Number.isSafeInteger(compareTo) && compareTo >= 0)) {
405
+ return this.createError('array.ref', { ref: limit.key }, state, options);
406
+ }
407
+ }
408
+ else {
409
+ compareTo = limit;
410
+ }
411
+
412
+ if (value.length >= compareTo) {
398
413
  return value;
399
414
  }
400
415
 
@@ -404,11 +419,25 @@ internals.Array = class extends Any {
404
419
 
405
420
  max(limit) {
406
421
 
407
- Hoek.assert(Number.isSafeInteger(limit) && limit >= 0, 'limit must be a positive integer');
422
+ const isRef = Ref.isRef(limit);
423
+
424
+ Hoek.assert((Number.isSafeInteger(limit) && limit >= 0) || isRef, 'limit must be a positive integer or reference');
408
425
 
409
426
  return this._test('max', limit, function (value, state, options) {
410
427
 
411
- if (value.length <= limit) {
428
+ let compareTo;
429
+ if (isRef) {
430
+ compareTo = limit(state.reference || state.parent, options);
431
+
432
+ if (!(Number.isSafeInteger(compareTo) && compareTo >= 0)) {
433
+ return this.createError('array.ref', { ref: limit.key }, state, options);
434
+ }
435
+ }
436
+ else {
437
+ compareTo = limit;
438
+ }
439
+
440
+ if (value.length <= compareTo) {
412
441
  return value;
413
442
  }
414
443
 
@@ -418,11 +447,25 @@ internals.Array = class extends Any {
418
447
 
419
448
  length(limit) {
420
449
 
421
- Hoek.assert(Number.isSafeInteger(limit) && limit >= 0, 'limit must be a positive integer');
450
+ const isRef = Ref.isRef(limit);
451
+
452
+ Hoek.assert((Number.isSafeInteger(limit) && limit >= 0) || isRef, 'limit must be a positive integer or reference');
422
453
 
423
454
  return this._test('length', limit, function (value, state, options) {
424
455
 
425
- if (value.length === limit) {
456
+ let compareTo;
457
+ if (isRef) {
458
+ compareTo = limit(state.reference || state.parent, options);
459
+
460
+ if (!(Number.isSafeInteger(compareTo) && compareTo >= 0)) {
461
+ return this.createError('array.ref', { ref: limit.key }, state, options);
462
+ }
463
+ }
464
+ else {
465
+ compareTo = limit;
466
+ }
467
+
468
+ if (value.length === compareTo) {
426
469
  return value;
427
470
  }
428
471
 
@@ -15,7 +15,17 @@ const Ip = require('./ip');
15
15
 
16
16
  const internals = {
17
17
  uriRegex: Uri.createUriRegex(),
18
- ipRegex: Ip.createIpRegex(['ipv4', 'ipv6', 'ipvfuture'], 'optional')
18
+ ipRegex: Ip.createIpRegex(['ipv4', 'ipv6', 'ipvfuture'], 'optional'),
19
+ guidBrackets: {
20
+ '{': '}', '[': ']', '(': ')', '': ''
21
+ },
22
+ guidVersions: {
23
+ uuidv1: '1',
24
+ uuidv2: '2',
25
+ uuidv3: '3',
26
+ uuidv4: '4',
27
+ uuidv5: '5'
28
+ }
19
29
  };
20
30
 
21
31
  internals.String = class extends Any {
@@ -326,19 +336,7 @@ internals.String = class extends Any {
326
336
 
327
337
  guid(guidOptions) {
328
338
 
329
- const brackets = {
330
- '{': '}', '[': ']', '(': ')', '': ''
331
- };
332
-
333
- const uuids = {
334
- 'uuidv1': '1',
335
- 'uuidv2': '2',
336
- 'uuidv3': '3',
337
- 'uuidv4': '4',
338
- 'uuidv5': '5'
339
- };
340
-
341
- const versions = [];
339
+ let versionNumbers = '';
342
340
 
343
341
  if (guidOptions && guidOptions.version) {
344
342
  if (!Array.isArray(guidOptions.version)) {
@@ -346,50 +344,36 @@ internals.String = class extends Any {
346
344
  }
347
345
 
348
346
  Hoek.assert(guidOptions.version.length >= 1, 'version must have at least 1 valid version specified');
347
+ const versions = new Set();
349
348
 
350
349
  for (let i = 0; i < guidOptions.version.length; ++i) {
351
350
  let version = guidOptions.version[i];
352
351
  Hoek.assert(typeof version === 'string', 'version at position ' + i + ' must be a string');
353
352
  version = version.toLowerCase();
354
- Hoek.assert(uuids[version], 'version at position ' + i + ' must be one of ' + Object.keys(uuids).join(', '));
355
- Hoek.assert(versions.indexOf(version) === -1, 'version at position ' + i + ' must not be a duplicate.');
356
- versions.push(version);
353
+ const versionNumber = internals.guidVersions[version];
354
+ Hoek.assert(versionNumber, 'version at position ' + i + ' must be one of ' + Object.keys(internals.guidVersions).join(', '));
355
+ Hoek.assert(!(versions.has(versionNumber)), 'version at position ' + i + ' must not be a duplicate.');
356
+
357
+ versionNumbers += versionNumber;
358
+ versions.add(versionNumber);
357
359
  }
358
360
  }
359
361
 
360
- const regex = /^([\[{\(]?)([0-9A-F]{8})([:-]?)([0-9A-F]{4})([:-]?)([0-9A-F]{4})([:-]?)([0-9A-F]{4})([:-]?)([0-9A-F]{12})([\]}\)]?)$/i;
362
+ const guidRegex = new RegExp(`^([\\[{\\(]?)[0-9A-F]{8}([:-]?)[0-9A-F]{4}\\2?[${versionNumbers || '0-9A-F'}][0-9A-F]{3}\\2?[${versionNumbers ? '89AB' : '0-9A-F'}][0-9A-F]{3}\\2?[0-9A-F]{12}([\\]}\\)]?)$`, 'i');
361
363
 
362
364
  return this._test('guid', guidOptions, function (value, state, options) {
363
365
 
364
- const results = regex.exec(value);
366
+ const results = guidRegex.exec(value);
365
367
 
366
368
  if (!results) {
367
369
  return this.createError('string.guid', { value }, state, options);
368
370
  }
369
371
 
370
372
  // Matching braces
371
- if (brackets[results[1]] !== results[11]) {
372
- return this.createError('string.guid', { value }, state, options);
373
- }
374
-
375
- // Matching separators
376
- if (results[3] !== results[5] || results[3] !== results[7] || results[3] !== results[9]) {
373
+ if (internals.guidBrackets[results[1]] !== results[results.length - 1]) {
377
374
  return this.createError('string.guid', { value }, state, options);
378
375
  }
379
376
 
380
- // Specific UUID versions
381
- if (versions.length) {
382
- const validVersions = versions.some((uuidVersion) => {
383
-
384
- return results[6][0] === uuids[uuidVersion];
385
- });
386
-
387
- // Valid version and 89AB check
388
- if (!(validVersions && /[89AB]/i.test(results[8][0]))) {
389
- return this.createError('string.guid', { value }, state, options);
390
- }
391
- }
392
-
393
377
  return value;
394
378
  });
395
379
  }
@@ -408,9 +392,26 @@ internals.String = class extends Any {
408
392
  });
409
393
  }
410
394
 
411
- base64() {
395
+ base64(base64Options) {
396
+
397
+ base64Options = base64Options || {};
398
+
399
+ // Validation.
400
+ Hoek.assert(typeof base64Options === 'object', 'base64 options must be an object');
401
+ Hoek.assert(typeof base64Options.paddingRequired === 'undefined' || typeof base64Options.paddingRequired === 'boolean',
402
+ 'paddingRequired must be boolean');
403
+
404
+ // Determine if padding is required.
405
+ const paddingRequired = base64Options.paddingRequired === false ?
406
+ base64Options.paddingRequired
407
+ : base64Options.paddingRequired || true;
412
408
 
413
- const regex = /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/;
409
+ // Set validation based on preference.
410
+ const regex = paddingRequired ?
411
+ // Padding is required.
412
+ /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/
413
+ // Padding is optional.
414
+ : /^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}(==)?|[A-Za-z0-9+\/]{3}=?)?$/;
414
415
 
415
416
  return this._test('base64', regex, function (value, state, options) {
416
417
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "joi",
3
3
  "description": "Object schema validation",
4
- "version": "10.5.2",
4
+ "version": "10.6.0",
5
5
  "homepage": "https://github.com/hapijs/joi",
6
6
  "repository": "git://github.com/hapijs/joi",
7
7
  "main": "lib/index.js",