Socket
Socket
Sign inDemoInstall

joi

Package Overview
Dependencies
Maintainers
4
Versions
238
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

joi - npm Package Compare versions

Comparing version 4.2.1 to 4.3.0

16

lib/alternatives.js

@@ -21,3 +21,3 @@ // Load modules

this._inner = [];
this._inner.matches = [];
};

@@ -31,4 +31,4 @@

var errors = [];
for (var i = 0, il = this._inner.length; i < il; ++i) {
var item = this._inner[i];
for (var i = 0, il = this._inner.matches.length; i < il; ++i) {
var item = this._inner.matches[i];
var schema = item.schema;

@@ -68,3 +68,3 @@ if (!schema) {

}
obj._inner.push({ schema: cast });
obj._inner.matches.push({ schema: cast });
}

@@ -81,3 +81,3 @@

Hoek.assert(typeof options === 'object', 'Invalid options');
Hoek.assert(options.is, 'Missing "is" directive');
Hoek.assert(options.hasOwnProperty('is'), 'Missing "is" directive');
Hoek.assert(options.then !== undefined || options.otherwise !== undefined, 'options must have at least one of "then" or "otherwise"');

@@ -105,3 +105,3 @@

obj._inner.push(item);
obj._inner.matches.push(item);

@@ -115,4 +115,4 @@ return obj;

var descriptions = [];
for (var i = 0, il = this._inner.length; i < il; ++i) {
var item = this._inner[i];
for (var i = 0, il = this._inner.matches.length; i < il; ++i) {
var item = this._inner.matches[i];
if (item.schema) {

@@ -119,0 +119,0 @@

@@ -32,6 +32,17 @@ // Load modules

this._settings = null;
this._valids = new internals.Set([undefined]);
this._invalids = new internals.Set([null]);
this._valids = new internals.Set();
this._invalids = new internals.Set();
this._tests = [];
this._flags = {}; // insensitive (false), allowOnly (false), default (undefined), encoding (undefined), allowUnknown (undefined), forbidden (false)
this._refs = [];
this._flags = { /*
presence: 'optional', // optional, required, forbidden, ignore
allowOnly: false,
allowUnknown: undefined,
default: undefined,
forbidden: false,
encoding: undefined,
insensitive: false,
trim: false,
case: undefined // upper, lower
*/};

@@ -43,7 +54,5 @@ this._description = null;

this._examples = [];
this._meta = [];
this._inner = null;
this._renames = [];
this._dependencies = [];
this._refs = [];
this._inner = {}; // Hash of arrays of immutable objects
};

@@ -63,2 +72,3 @@

obj._tests = this._tests.slice();
obj._refs = this._refs.slice();
obj._flags = Hoek.clone(this._flags);

@@ -71,7 +81,10 @@

obj._examples = this._examples.slice();
obj._meta = this._meta.slice();
obj._inner = this._inner ? this._inner.slice() : null;
obj._renames = this._renames.slice();
obj._dependencies = this._dependencies.slice();
obj._refs = this._refs.slice();
obj._inner = {};
var inners = Object.keys(this._inner);
for (var i = 0, il = inners.length; i < il; ++i) {
var key = inners[i];
obj._inner[key] = this._inner[key] ? this._inner[key].slice() : null;
}

@@ -90,5 +103,6 @@ return obj;

obj._settings = obj._settings ? internals.concatSettings(obj._settings, schema._settings) : schema._settings;
obj._valids.merge(schema._valids);
obj._invalids.merge(schema._invalids);
obj._valids.merge(schema._valids, schema._invalids);
obj._invalids.merge(schema._invalids, schema._valids);
obj._tests = obj._tests.concat(schema._tests);
obj._refs = obj._refs.concat(schema._refs);
Hoek.merge(obj._flags, schema._flags);

@@ -101,7 +115,11 @@

obj._examples = obj._examples.concat(schema._examples);
obj._meta = obj._meta.concat(schema._meta);
obj._inner = obj._inner ? (schema._inner ? obj._inner.concat(schema._inner) : obj._inner) : schema._inner;
obj._renames = obj._renames.concat(schema._renames);
obj._dependencies = obj._dependencies.concat(schema._dependencies);
obj._refs = obj._refs.concat(schema._refs);
var inners = Object.keys(schema._inner);
for (var i = 0, il = inners.length; i < il; ++i) {
var key = inners[i];
if (schema._inner[key]) {
obj._inner[key] = (obj._inner[key] ? obj._inner[key].concat(schema._inner[key]) : schema._inner[key].slice());
}
}

@@ -187,4 +205,3 @@ return obj;

var obj = this.clone();
obj._valids.remove(undefined);
obj._invalids.add(undefined);
obj._flags.presence = 'required';
return obj;

@@ -197,4 +214,3 @@ };

var obj = this.clone();
obj._invalids.remove(undefined);
obj._valids.add(undefined);
delete obj._flags.presence; // Defaults to 'optional'
return obj;

@@ -216,3 +232,3 @@ };

var obj = this.clone();
obj._flags.forbidden = true;
obj._flags.presence = 'forbidden';
return obj;

@@ -232,3 +248,5 @@ };

Alternatives = Alternatives || require('./alternatives');
return Alternatives.when(ref, { is: options.is, then: then, otherwise: otherwise });
var obj = Alternatives.when(ref, { is: options.is, then: then, otherwise: otherwise });
obj._flags.presence = 'ignore';
return obj;
};

@@ -266,3 +284,12 @@

internals.Any.prototype.meta = function (meta) {
Hoek.assert(meta !== undefined, 'Meta cannot be undefined');
var obj = this.clone();
obj._meta = obj._meta.concat(meta);
return obj;
};
internals.Any.prototype.example = function (value) {

@@ -311,10 +338,25 @@

// Check forbidden
// Check presence requirements
if (this._flags.forbidden &&
value !== undefined) {
if (this._flags.presence) { // 'required', 'forbidden', or 'ignore'
if (this._flags.presence === 'required' &&
value === undefined) {
errors.push(Errors.create('any.unknown', null, state, options));
return finish();
errors.push(Errors.create('any.required', null, state, options));
return finish();
}
else if (this._flags.presence === 'forbidden') {
if (value === undefined) {
return finish();
}
errors.push(Errors.create('any.unknown', null, state, options));
return finish();
}
}
else { // 'optional'
if (value === undefined) {
return finish();
}
}

@@ -328,3 +370,3 @@ // Check allowed and denied values using the original value

if (this._invalids.has(value, state, options, this._flags.insensitive)) {
errors.push(Errors.create(value === '' ? 'any.empty' : (value === undefined ? 'any.required' : 'any.invalid'), null, state, options));
errors.push(Errors.create(value === '' ? 'any.empty' : 'any.invalid', null, state, options));
if (options.abortEarly ||

@@ -440,2 +482,6 @@ value === undefined) { // No reason to keep validating missing value

if (this._meta.length) {
description.meta = this._meta;
}
if (this._examples.length) {

@@ -480,9 +526,5 @@ description.examples = this._examples;

internals.Set = function (values) {
internals.Set = function () {
this._set = [];
for (var i = 0, il = values.length; i < il; ++i) {
this.add(values[i]);
}
};

@@ -506,7 +548,11 @@

internals.Set.prototype.merge = function (set) {
internals.Set.prototype.merge = function (add, remove) {
for (var i = 0, il = set._set.length; i < il; ++i) {
this.add(set._set[i]);
for (var i = 0, il = add._set.length; i < il; ++i) {
this.add(add._set[i]);
}
for (i = 0, il = remove._set.length; i < il; ++i) {
this.remove(remove._set[i]);
}
};

@@ -624,2 +670,2 @@

return obj;
};
};

@@ -19,2 +19,4 @@ // Load modules

this._type = 'array';
this._inner.inclusions = [];
this._inner.exclusions = [];
};

@@ -43,25 +45,31 @@

result.errors = Array.isArray(result.value) ? null : Errors.create('array.base', null, state, options);
return result;
};
if (!Array.isArray(result.value)) {
result.errors = Errors.create('array.base', null, state, options);
return result;
}
if (this._inner.inclusions.length ||
this._inner.exclusions.length) {
internals.Array.prototype.includes = function () {
for (var v = 0, vl = result.value.length; v < vl; ++v) {
var item = result.value[v];
var isValid = false;
var localState = { key: v, path: (state.path ? state.path + '.' : '') + v, parent: result.value, reference: state.reference };
var inclusions = Hoek.flatten(Array.prototype.slice.call(arguments)).map(function (type) {
// Exclusions
return Cast.schema(type);
});
for (var i = 0, il = this._inner.exclusions.length; i < il; ++i) {
var res = this._inner.exclusions[i]._validate(item, localState, {}); // Not passing options to use defaults
if (!res.errors) {
result.errors = Errors.create('array.excludes', { pos: v }, { key: state.key, path: localState.path }, options);
return result;
}
}
return this._test('includes', inclusions, function (value, state, options) {
// Inclusions
for (var v = 0, vl = value.length; v < vl; ++v) {
var item = value[v];
var isValid = false;
var localState = { key: v, path: (state.path ? state.path + '.' : '') + v, parent: value, reference: state.reference };
for (var i = 0, il = inclusions.length; i < il; ++i) {
var result = inclusions[i]._validate(item, localState, options);
if (!result.errors) {
value[v] = result.value;
for (i = 0, il = this._inner.inclusions.length; i < il; ++i) {
var res = this._inner.inclusions[i]._validate(item, localState, options);
if (!res.errors) {
result.value[v] = res.value;
isValid = true;

@@ -74,39 +82,67 @@ break;

if (il === 1) {
return Errors.create('array.includesOne', { pos: v, reason: result.errors }, { key: state.key, path: localState.path }, options);
result.errors = Errors.create('array.includesOne', { pos: v, reason: res.errors }, { key: state.key, path: localState.path }, options);
return result;
}
}
if (!isValid) {
return Errors.create('array.includes', { pos: v }, { key: state.key, path: localState.path }, options);
if (this._inner.inclusions.length &&
!isValid) {
result.errors = Errors.create('array.includes', { pos: v }, { key: state.key, path: localState.path }, options);
return result;
}
}
}
return null;
});
return result;
};
internals.Array.prototype.excludes = function () {
internals.Array.prototype.describe = function () {
var exclusions = Hoek.flatten(Array.prototype.slice.call(arguments)).map(function (type) {
var description = Any.prototype.describe.call(this);
if (this._inner.inclusions.length) {
description.includes = [];
for (var i = 0, il = this._inner.inclusions.length; i < il; ++i) {
description.includes.push(this._inner.inclusions[i].describe());
}
}
if (this._inner.exclusions.length) {
description.excludes = [];
for (var i = 0, il = this._inner.exclusions.length; i < il; ++i) {
description.excludes.push(this._inner.exclusions[i].describe());
}
}
return description;
};
internals.Array.prototype.includes = function () {
var inclusions = Hoek.flatten(Array.prototype.slice.call(arguments)).map(function (type) {
return Cast.schema(type);
});
return this._test('excludes', exclusions, function (value, state, options) {
var obj = this.clone();
obj._inner.inclusions = obj._inner.inclusions.concat(inclusions);
return obj;
};
for (var v = 0, vl = value.length; v < vl; ++v) {
var item = value[v];
var localState = { key: v, path: (state.path ? state.path + '.' : '') + v, parent: value, reference: state.reference };
for (var i = 0, il = exclusions.length; i < il; ++i) {
var result = exclusions[i]._validate(item, localState, {}); // Not passing options to use defaults
if (!result.errors) {
return Errors.create('array.excludes', { pos: v }, { key: state.key, path: localState.path }, options);
}
}
}
internals.Array.prototype.excludes = function () {
return null;
var exclusions = Hoek.flatten(Array.prototype.slice.call(arguments)).map(function (type) {
return Cast.schema(type);
});
var obj = this.clone();
obj._inner.exclusions = obj._inner.exclusions.concat(exclusions);
return obj;
};

@@ -160,2 +196,2 @@

module.exports = new internals.Array();
module.exports = new internals.Array();

@@ -45,2 +45,4 @@ // Load modules

Hoek.assert(Buffer.isEncoding(encoding), 'Invalid encoding:', encoding);
var obj = this.clone();

@@ -47,0 +49,0 @@ obj._flags.encoding = encoding;

@@ -28,3 +28,3 @@ // Load modules

result.errors = (result.value instanceof Date) ? null : Errors.create('date.base', null, state, options);
result.errors = (result.value instanceof Date && !isNaN(result.value.getTime())) ? null : Errors.create('date.base', null, state, options);
return result;

@@ -31,0 +31,0 @@ };

@@ -85,4 +85,7 @@ // Load modules

guid: 'must be a valid GUID',
hostname: 'must be a valid hostname'
hostname: 'must be a valid hostname',
lowercase: 'must only contain lowercase characters',
uppercase: 'must only contain uppercase characters',
trim: 'must not have leading or trailing whitespace'
}
};

@@ -20,3 +20,6 @@ // Load modules

this._type = 'object';
this._inner = null;
this._inner.children = null;
this._inner.renames = [];
this._inner.dependencies = [];
this._inner.patterns = [];
};

@@ -73,4 +76,4 @@

var renamed = {};
for (var r = 0, rl = this._renames.length; r < rl; ++r) {
var item = this._renames[r];
for (var r = 0, rl = this._inner.renames.length; r < rl; ++r) {
var item = this._inner.renames[r];

@@ -104,7 +107,7 @@ if (!item.options.multiple &&

// Helper.validate dependencies
// Validate dependencies
for (var d = 0, dl = this._dependencies.length; d < dl; ++d) {
var dep = this._dependencies[d];
var err = internals[dep.type](dep.key && value[dep.key], dep.peers, target, { key: dep.key, path: (state.path ? state.path + '.' : '') + dep.key }, options);
for (var d = 0, dl = this._inner.dependencies.length; d < dl; ++d) {
var dep = this._inner.dependencies[d];
var err = internals[dep.type](dep.key !== null && value[dep.key], dep.peers, target, { key: dep.key, path: (state.path ? state.path + '.' : '') + dep.key }, options);
if (err) {

@@ -118,5 +121,5 @@ errors.push(err);

// Helper.validate schema
// Validate schema
if (!this._inner) { // null allows any keys
if (!this._inner.children) { // null allows any keys
return finish();

@@ -128,4 +131,4 @@ }

for (var i = 0, il = this._inner.length; i < il; ++i) {
var child = this._inner[i];
for (var i = 0, il = this._inner.children.length; i < il; ++i) {
var child = this._inner.children[i];
var key = child.key;

@@ -150,3 +153,37 @@ var item = target[key];

// Unknown keys
var unprocessedKeys = Object.keys(unprocessed);
if (unprocessedKeys.length &&
this._inner.patterns.length) {
for (i = 0, il = unprocessedKeys.length; i < il; ++i) {
var key = unprocessedKeys[i];
for (var p = 0, pl = this._inner.patterns.length; p < pl; ++p) {
var pattern = this._inner.patterns[p];
if (pattern.regex.test(key)) {
delete unprocessed[key];
var item = target[key];
var localState = { key: key, path: (state.path ? state.path + '.' : '') + key, parent: target, reference: state.reference };
var result = pattern.rule._validate(item, localState, options);
if (result.errors) {
errors = errors.concat(result.errors);
if (options.abortEarly) {
return finish();
}
}
if (result.value !== undefined) {
target[key] = result.value;
}
}
}
}
unprocessedKeys = Object.keys(unprocessed);
}
if (unprocessedKeys.length) {

@@ -159,2 +196,3 @@ if (options.stripUnknown ||

key = unprocessedKeys[k];
if (options.stripUnknown) {

@@ -199,3 +237,3 @@ delete target[key];

if (!schema) {
obj._inner = null;
obj._inner.children = null;
return obj;

@@ -207,3 +245,3 @@ }

if (!children.length) {
obj._inner = [];
obj._inner.children = [];
return obj;

@@ -213,5 +251,5 @@ }

var topo = new Topo();
if (obj._inner) {
for (var i = 0, il = obj._inner.length; i < il; ++i) {
var child = obj._inner[i];
if (obj._inner.children) {
for (var i = 0, il = obj._inner.children.length; i < il; ++i) {
var child = obj._inner.children[i];
topo.add(child, { after: child._refs, group: child.key });

@@ -228,3 +266,3 @@ }

obj._inner = topo.nodes;
obj._inner.children = topo.nodes;

@@ -288,2 +326,13 @@ return obj;

internals.Object.prototype.pattern = function (regex, schema) {
Hoek.assert(regex instanceof RegExp, 'Invalid regular expression');
Hoek.assert(schema !== undefined, 'Invalid rule');
var obj = this.clone();
obj._inner.patterns.push({ regex: regex, rule: Cast.schema(schema) });
return obj;
};
internals.Object.prototype.with = function (key, peers) {

@@ -331,8 +380,8 @@

Hoek.assert(from, 'Rename missing the from argument');
Hoek.assert(to, 'Rename missing the to argument');
Hoek.assert(typeof from === 'string', 'Rename missing the from argument');
Hoek.assert(typeof to === 'string', 'Rename missing the to argument');
Hoek.assert(to !== from, 'Cannot rename key to same name:', from);
for (var i = 0, il = this._renames.length; i < il; ++i) {
Hoek.assert(this._renames[i].from !== from, 'Cannot rename the same key multiple times');
for (var i = 0, il = this._inner.renames.length; i < il; ++i) {
Hoek.assert(this._inner.renames[i].from !== from, 'Cannot rename the same key multiple times');
}

@@ -342,3 +391,3 @@

obj._renames.push({
obj._inner.renames.push({
from: from,

@@ -361,3 +410,3 @@ to: to,

var obj = this.clone();
obj._dependencies.push({ type: type, key: key, peers: peers });
obj._inner.dependencies.push({ type: type, key: key, peers: peers });
return obj;

@@ -377,2 +426,3 @@ };

parent[peer] === undefined) {
return Errors.create('object.with', { peer: peer }, state, options);

@@ -465,10 +515,12 @@ }

internals.Object.prototype.describe = function () {
internals.Object.prototype.describe = function (shallow) {
var description = Any.prototype.describe.call(this);
if (this._inner) {
if (this._inner.children &&
!shallow) {
description.children = {};
for (var i = 0, il = this._inner.length; i < il; ++i) {
var child = this._inner[i];
for (var i = 0, il = this._inner.children.length; i < il; ++i) {
var child = this._inner.children[i];
description.children[child.key] = child.schema.describe();

@@ -478,4 +530,4 @@ }

if (this._dependencies.length) {
description.dependencies = Hoek.clone(this._dependencies);
if (this._inner.dependencies.length) {
description.dependencies = Hoek.clone(this._inner.dependencies);
}

@@ -506,2 +558,2 @@

module.exports = new internals.Object();
module.exports = new internals.Object();
// Load modules
var Net = require('net');
var Hoek = require('hoek');
var Isemail = require('isemail');
var Any = require('./any');
var Errors = require('./errors');
var Hoek = require('hoek');

@@ -26,2 +27,14 @@

if (typeof value === 'string' &&
options.convert) {
if (this._flags.case) {
value = (this._flags.case === 'upper' ? value.toLocaleUpperCase() : value.toLocaleLowerCase());
}
if (this._flags.trim) {
value = value.trim();
}
}
return {

@@ -45,2 +58,3 @@ value: value,

Hoek.assert(Hoek.isInteger(limit) && limit >= 0, 'limit must be a positive integer');
Hoek.assert(!encoding || Buffer.isEncoding(encoding), 'Invalid encoding:', encoding);

@@ -62,2 +76,3 @@ return this._test('min', limit, function (value, state, options) {

Hoek.assert(Hoek.isInteger(limit) && limit >= 0, 'limit must be a positive integer');
Hoek.assert(!encoding || Buffer.isEncoding(encoding), 'Invalid encoding:', encoding);

@@ -79,2 +94,3 @@ return this._test('max', limit, function (value, state, options) {

Hoek.assert(Hoek.isInteger(limit) && limit >= 0, 'limit must be a positive integer');
Hoek.assert(!encoding || Buffer.isEncoding(encoding), 'Invalid encoding:', encoding);

@@ -136,7 +152,5 @@ return this._test('length', limit, function (value, state, options) {

var regex = /^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/;
return this._test('email', undefined, function (value, state, options) {
if (regex.test(value)) {
if (Isemail(value)) {
return null;

@@ -198,2 +212,56 @@ }

internals.String.prototype.lowercase = function () {
var obj = this._test('lowercase', undefined, function (value, state, options) {
if (options.convert ||
value === value.toLocaleLowerCase()) {
return null;
}
return Errors.create('string.lowercase', null, state, options);
});
obj._flags.case = 'lower';
return obj;
};
internals.String.prototype.uppercase = function (options) {
var obj = this._test('uppercase', undefined, function (value, state, options) {
if (options.convert ||
value === value.toLocaleUpperCase()) {
return null;
}
return Errors.create('string.uppercase', null, state, options);
});
obj._flags.case = 'upper';
return obj;
};
internals.String.prototype.trim = function () {
var obj = this._test('trim', undefined, function (value, state, options) {
if (options.convert ||
value === value.trim()) {
return null;
}
return Errors.create('string.trim', null, state, options);
});
obj._flags.trim = true;
return obj;
};
module.exports = new internals.String();
{
"name": "joi",
"description": "Object schema validation",
"version": "4.2.1",
"version": "4.3.0",
"repository": "git://github.com/spumko/joi",

@@ -16,3 +16,4 @@ "main": "index",

"hoek": "^2.1.x",
"topo": "1.x.x"
"topo": "1.x.x",
"isemail": "0.1.x"
},

@@ -19,0 +20,0 @@ "devDependencies": {

@@ -6,3 +6,3 @@ <a href="https://github.com/spumko"><img src="https://raw.github.com/spumko/spumko/master/images/from.png" align="right" /></a>

Current version: **4.2.x**
Current version: **4.3.x**

@@ -30,2 +30,3 @@ [![Build Status](https://secure.travis-ci.org/spumko/joi.png)](http://travis-ci.org/spumko/joi)

- [`any.tags(tags)`](#anytagstags)
- [`any.meta(meta)`](#anymetameta)
- [`any.example(value)`](#anyexamplevalue)

@@ -63,2 +64,3 @@ - [`any.unit(name)`](#anyunitname)

- [`object.length(limit)`](#objectlengthlimit)
- [`object.pattern(regex, schema)`](#objectpatternregex-schema)
- [`object.and(peers)`](#objectandpeers)

@@ -84,2 +86,5 @@ - [`object.or(peers)`](#objectorpeers)

- [`string.hostname()`](#stringhostname)
- [`string.lowercase()`](#stringlowercase)
- [`string.uppercase()`](#stringuppercase)
- [`string.trim()`](#stringtrim)
- [`alternatives`](#alternatives)

@@ -178,3 +183,3 @@ - [`alternatives.try(schemas)`](#alternativestryschemas)

`validate()` and not using `any.options()`.
- `callback` - the callback method using the signature `function(err, value)` where:
- `callback` - the synchronous callback method using the signature `function(err, value)` where:
- `err` - if validation failed, the error reason, otherwise `null`.

@@ -326,2 +331,11 @@ - `value` - the validated value with any type conversions and other modifiers applied (the input is left unchanged). `value` can be

#### `any.meta(meta)`
Attaches metadata to the key where:
- `meta` - the meta object to attach.
```javascript
var schema = Joi.any().meta({ index: true });
```
#### `any.example(value)`

@@ -369,2 +383,5 @@

Note that if `value` is an object, any changes to the object after `default()` is called will change the reference
and any future assignment.
```javascript

@@ -653,2 +670,14 @@ var schema = {

#### `object.parrern(regex, schema)`
Specify validation rules for unknown keys matching a pattern where:
- `regex` - a regular expression tested against the unknown key names.
- `schema` - the schema object matching keys much validate against.
```javascrip
var schema = Joi.object({
a: Joi.string()
}).pattern(/\w\d/, Joi.boolean());
```
#### `object.and(peers)`

@@ -878,2 +907,29 @@

#### `string.lowercase()`
Requires the string value to be all lowercase. If the validation `convert` option is on (enabled by default), the string
will be forced to lowercase.
```javascript
var schema = Joi.string().lowercase();
```
#### `string.uppercase()`
Requires the string value to be all uppercase. If the validation `convert` option is on (enabled by default), the string
will be forced to uppercase.
```javascript
var schema = Joi.string().uppercase();
```
#### `string.trim()`
Requires the string value to contain no whitespace before or after. If the validation `convert` option is on (enabled by
default), the string will be trimmed.
```javascript
var schema = Joi.string().trim();
```
### `alternatives`

@@ -905,8 +961,8 @@

Adds a conditional alternative schema type based on another key value where:
Adds a conditional alternative schema type based on another key (not the same as `any.when()`) value where:
- `ref` - the key name or [reference](#refkey-options).
- `options` - an object with:
- `is` - the required condition **joi** type.
- `then` - the alternative schema type if the condition is true. Required if `otherwise` is missing.
- `otherwise` - the alternative schema type if the condition is false. Required if `then` is missing.
- `then` - the alternative schema type to **try** if the condition is true. Required if `otherwise` is missing.
- `otherwise` - the alternative schema type to **try** if the condition is false. Required if `then` is missing.

@@ -920,2 +976,27 @@ ```javascript

Note that `when()` only adds additional alternatives to try and does not impact the overall type. Setting
a `required()` rule on a single alternative will not apply to the overall key. For example,
this definition of `a`:
```javascript
var schema = {
a: Joi.alternatives().when('b', { is: true, then: Joi.required() }),
b: Joi.boolean()
};
```
Does not turn `a` into a required key when `b` is `true`. Instead, it tells the validator to try and match the
value to anything that's not `undefined`. However, since `Joi.alternatives()` by itself allows `undefined`, the rule
does not accomplish turning `a` to a required value. This rule is the same as `Joi.alternatives([Joi.required()])`
when `b` is `true` which will allow any value including `undefined`.
To accomplish the desired result above use:
```javascript
var schema = {
a: Joi.when('b', { is: true, then: Joi.required() }),
b: Joi.boolean()
};
```
### `ref(key, [options])`

@@ -922,0 +1003,0 @@

@@ -209,2 +209,15 @@ // Load modules

});
it('validates when empty value', function (done) {
var schema = {
a: Joi.alternatives().when('b', { is: true, then: Joi.required() }),
b: Joi.boolean().default(false)
};
Helper.validate(schema, [
[{ b: false }, true],
[{ b: true }, true] // true because required() only applies to the one alternative
], done);
});
});

@@ -224,9 +237,5 @@

type: 'object',
valids: [undefined],
invalids: [null],
children: {
b: {
type: 'any',
valids: [undefined],
invalids: [null]
type: 'any'
},

@@ -241,5 +250,3 @@ a: [

},
valids: [undefined, 5
],
invalids: [null]
valids: [5]
},

@@ -251,4 +258,4 @@ then: {

},
valids: [undefined, 'x'],
invalids: [null, '']
valids: ['x'],
invalids: ['']
},

@@ -260,4 +267,4 @@ otherwise: {

},
valids: [undefined, 'y'],
invalids: [null, '']
valids: ['y'],
invalids: ['']
}

@@ -270,4 +277,4 @@ },

},
valids: [undefined, 'z'],
invalids: [null, '']
valids: ['z'],
invalids: ['']
}

@@ -292,9 +299,5 @@ ]

type: 'object',
valids: [undefined],
invalids: [null],
children: {
b: {
type: 'any',
valids: [undefined],
invalids: [null]
type: 'any'
},

@@ -309,5 +312,3 @@ a: [

},
valids: [undefined, 5
],
invalids: [null]
valids: [5]
},

@@ -319,4 +320,4 @@ then: {

},
valids: [undefined, 'x'],
invalids: [null, '']
valids: ['x'],
invalids: ['']
}

@@ -329,4 +330,4 @@ },

},
valids: [undefined, 'z'],
invalids: [null, '']
valids: ['z'],
invalids: ['']
}

@@ -351,9 +352,5 @@ ]

type: 'object',
valids: [undefined],
invalids: [null],
children: {
b: {
type: 'any',
valids: [undefined],
invalids: [null]
type: 'any'
},

@@ -368,5 +365,3 @@ a: [

},
valids: [undefined, 5
],
invalids: [null]
valids: [5]
},

@@ -378,4 +373,4 @@ otherwise: {

},
valids: [undefined, 'y'],
invalids: [null, '']
valids: ['y'],
invalids: ['']
}

@@ -388,4 +383,4 @@ },

},
valids: [undefined, 'z'],
invalids: [null, '']
valids: ['z'],
invalids: ['']
}

@@ -392,0 +387,0 @@ ]

@@ -230,2 +230,28 @@ // Load modules

describe('#meta', function () {
it('sets the meta', function (done) {
var meta = { prop: 'val', prop2: 3 };
var b = Joi.meta(meta);
expect(b.describe().meta).to.deep.equal([meta]);
b = b.meta({ other: true });
expect(b.describe().meta).to.deep.equal([meta, {
other: true
}]);
done();
});
it('throws when meta is missing', function (done) {
expect(function () {
Joi.meta();
}).to.throw('Meta cannot be undefined');
done();
});
});
describe('#example', function () {

@@ -329,11 +355,10 @@

[1, true], ['1', true]
], function () {
]);
Helper.validate(a.concat(b), [
[1, true], ['1', false]
], done);
});
Helper.validate(a.concat(b), [
[1, true], ['1', false]
], done);
});
it('merges two schemas (invalid)', function (done) {
it('merges two schemas (valid)', function (done) {

@@ -344,14 +369,15 @@ var a = Joi.string().valid('a');

Helper.validate(a, [
['a', true], ['b', false]
], function () {
['a', true],
['b', false]
]);
Helper.validate(b, [
['b', true], ['a', false]
], function () {
Helper.validate(b, [
['b', true],
['a', false]
]);
Helper.validate(a.concat(b), [
['a', true], ['b', true]
], done);
});
});
Helper.validate(a.concat(b), [
['a', true],
['b', true]
], done);
});

@@ -366,15 +392,34 @@

['b', true], ['a', false]
], function () {
]);
Helper.validate(b, [
['a', true], ['b', false]
], function () {
Helper.validate(b, [
['a', true], ['b', false]
]);
Helper.validate(a.concat(b), [
['a', false], ['b', false]
], done);
});
});
Helper.validate(a.concat(b), [
['a', false], ['b', false]
], done);
});
it('merges two schemas (valid/invalid)', function (done) {
var a = Joi.string().valid('a').invalid('b');
var b = Joi.string().valid('b').invalid('a');
Helper.validate(a, [
['a', true],
['b', false]
]);
Helper.validate(b, [
['b', true],
['a', false]
]);
Helper.validate(a.concat(b), [
['a', false],
['b', true]
], done);
});
it('merges two schemas (tests)', function (done) {

@@ -387,13 +432,11 @@

[4, false], [11, true]
], function () {
]);
Helper.validate(b, [
[6, true], [11, false]
], function () {
Helper.validate(b, [
[6, true], [11, false]
]);
Helper.validate(a.concat(b), [
[4, false], [6, true], [11, false]
], done);
});
});
Helper.validate(a.concat(b), [
[4, false], [6, true], [11, false]
], done);
});

@@ -408,8 +451,7 @@

['a', true], ['A', false], ['b', false]
], function () {
]);
Helper.validate(a.concat(b), [
['a', true], ['A', true], ['b', false]
], done);
});
Helper.validate(a.concat(b), [
['a', true], ['A', true], ['b', false]
], done);
});

@@ -428,5 +470,3 @@

examples: ['a', 'b'],
unit: 'b',
valids: [undefined],
invalids: [null]
unit: 'b'
});

@@ -443,18 +483,15 @@ done();

[{ b: 1 }, true], [{ b: 2 }, true]
], function () {
]);
Helper.validate(b, [
[{ b: 1 }, true], [{ b: 2 }, false]
], function () {
Helper.validate(b, [
[{ b: 1 }, true], [{ b: 2 }, false]
]);
Helper.validate(a.concat(b), [
[{ b: 1 }, true], [{ b: 2 }, false]
], function () {
Helper.validate(a.concat(b), [
[{ b: 1 }, true], [{ b: 2 }, false]
]);
Helper.validate(b.concat(a), [
[{ b: 1 }, true], [{ b: 2 }, false]
], done);
});
});
});
Helper.validate(b.concat(a), [
[{ b: 1 }, true], [{ b: 2 }, false]
], done);
});

@@ -469,18 +506,15 @@

[{}, true], [{ b: 2 }, false]
], function () {
]);
Helper.validate(b, [
[{}, true], [{ b: 2 }, true]
], function () {
Helper.validate(b, [
[{}, true], [{ b: 2 }, true]
]);
Helper.validate(a.concat(b), [
[{}, true], [{ b: 2 }, false]
], function () {
Helper.validate(a.concat(b), [
[{}, true], [{ b: 2 }, false]
]);
Helper.validate(b.concat(a), [
[{}, true], [{ b: 2 }, false]
], done);
});
});
});
Helper.validate(b.concat(a), [
[{}, true], [{ b: 2 }, false]
], done);
});

@@ -495,18 +529,15 @@

[{ a: 1 }, true], [{ b: 2 }, false]
], function () {
]);
Helper.validate(b, [
[{ a: 1 }, false], [{ b: 2 }, true]
], function () {
Helper.validate(b, [
[{ a: 1 }, false], [{ b: 2 }, true]
]);
Helper.validate(a.concat(b), [
[{ a: 1 }, true], [{ b: 2 }, true]
], function () {
Helper.validate(a.concat(b), [
[{ a: 1 }, true], [{ b: 2 }, true]
]);
Helper.validate(b.concat(a), [
[{ a: 1 }, true], [{ b: 2 }, true]
], done);
});
});
});
Helper.validate(b.concat(a), [
[{ a: 1 }, true], [{ b: 2 }, true]
], done);
});

@@ -554,2 +585,19 @@

});
it('merges meta properly', function (done) {
var metaA = { a: 1 };
var metaB = { b: 1 };
var a = Joi.any().meta(metaA);
var b = Joi.any().meta(metaB);
var c = Joi.any();
var d = Joi.any();
expect(a.concat(b)._meta).to.deep.equal([{ a: 1 }, { b: 1 }]);
expect(a.concat(c)._meta).to.deep.equal([metaA]);
expect(b.concat(c)._meta).to.deep.equal([metaB]);
expect(c.concat(d)._meta).to.deep.equal([]);
done();
});
});

@@ -624,2 +672,17 @@

});
it('makes peer required', function (done) {
var schema = {
a: Joi.when('b', { is: 5, then: Joi.required() }),
b: Joi.any()
};
Helper.validate(schema, [
[{ b: 5 }, false],
[{ b: 6 }, true],
[{ a: 'b' }, true],
[{ b: 5, a: 'x' }, true]
], done)
});
});

@@ -650,2 +713,21 @@

describe('#has', function () {
it('compares date to null', function (done) {
var any = Joi.any().clone();
any._valids.add(null);
expect(any._valids.has(new Date())).to.equal(false);
done();
});
it('compares buffer to null', function (done) {
var any = Joi.any().clone();
any._valids.add(null);
expect(any._valids.has(new Buffer(''))).to.equal(false);
done();
});
});
describe('#values', function () {

@@ -655,4 +737,4 @@

var a = Joi.any();
var b = a.required();
var a = Joi.any().valid('x').invalid('y');
var b = a.invalid('x');
expect(a._valids.values().length).to.equal(1);

@@ -670,3 +752,3 @@ expect(b._valids.values().length).to.equal(0);

var b = Joi.any();
var b = Joi.any().allow(undefined);
expect(b._valids.toString(true)).to.equal('undefined');

@@ -673,0 +755,0 @@ done();

@@ -119,2 +119,16 @@ // Load modules

});
it('validates multiple types added in two calls', function (done) {
var schema = Joi.array()
.includes(Joi.number())
.includes(Joi.string());
Helper.validate(schema, [
[[1, 2, 3], true],
[[50, 100, 1000], true],
[[1, 'a', 5, 10], true],
[['joi', 'everydaylowprices', 5000], true]
], done);
});
});

@@ -237,3 +251,3 @@

it('should exclude values when excludes is called', function (done) {
it('excludes values when excludes is called', function (done) {

@@ -247,3 +261,3 @@ Helper.validate(Joi.array().excludes(Joi.string()), [

it('should allow types to be excluded', function (done) {
it('allows types to be excluded', function (done) {

@@ -266,3 +280,3 @@ var schema = Joi.array().excludes(Joi.number());

it('should validate array of Numbers', function (done) {
it('validates array of Numbers', function (done) {

@@ -272,7 +286,8 @@ Helper.validate(Joi.array().includes(Joi.number()), [

[[50, 100, 1000], true],
[['a', 1, 2], false]
[['a', 1, 2], false],
[['1', '2', 4], true]
], done);
});
it('should validate array of mixed Numbers & Strings', function (done) {
it('validates array of mixed Numbers & Strings', function (done) {

@@ -287,3 +302,3 @@ Helper.validate(Joi.array().includes(Joi.number(), Joi.string()), [

it('should validate array of objects with schema', function (done) {
it('validates array of objects with schema', function (done) {

@@ -297,3 +312,3 @@ Helper.validate(Joi.array().includes(Joi.object({ h1: Joi.number().required() })), [

it('should not validate array of unallowed mixed types (Array)', function (done) {
it('errors on array of unallowed mixed types (Array)', function (done) {

@@ -334,3 +349,39 @@ Helper.validate(Joi.array().includes(Joi.number()), [

});
describe('#describe', function () {
it('returns an empty description when no rules are applied', function (done) {
var schema = Joi.array();
var desc = schema.describe();
expect(desc).to.deep.equal({
type: 'array'
});
done();
});
it('returns an includes array only if includes are specified', function (done) {
var schema = Joi.array().includes().max(5);
var desc = schema.describe();
expect(desc.includes).to.not.exist;
done();
});
it('returns a recursively defined array of includes when specified', function (done) {
var schema = Joi.array().includes(Joi.number(), Joi.string()).excludes(Joi.boolean());
var desc = schema.describe();
expect(desc.includes).to.have.length(2);
expect(desc.excludes).to.have.length(1);
expect(desc).to.deep.equal({
type: 'array',
includes: [{ type: 'number' }, { type: 'string', invalids: [''] }],
excludes: [{ type: 'boolean' }]
});
done();
});
});
});
});

@@ -92,2 +92,11 @@ // Load modules

});
it('throws when encoding is invalid', function (done) {
expect(function () {
Joi.binary().encoding('base6');
}).to.throw('Invalid encoding: base6');
done();
});
});

@@ -94,0 +103,0 @@

@@ -83,3 +83,12 @@ // Load modules

});
it('validates only valid dates', function (done) {
Helper.validate(Joi.date(), [
['1-1-2013', true],
['not a valid date', false],
[new Date('not a valid date'), false]
], done);
});
});
});
});

@@ -71,3 +71,3 @@ // Load modules

expect(err).to.exist;
expect(err.annotate()).to.equal('{\n \u001b[41m\"value\"\u001b[0m\u001b[31m [1]: -- missing --\u001b[0m\n}\n\u001b[31m\n[1] value contains an invalid value\u001b[0m');
expect(err.annotate()).to.equal('{\n \u001b[41m\"value\"\u001b[0m\u001b[31m [1]: -- missing --\u001b[0m\n}\n\u001b[31m\n[1] value must be a string\u001b[0m');
done();

@@ -1291,30 +1291,20 @@ });

type: 'object',
valids: [undefined],
invalids: [null],
children: {
sub: {
type: 'object',
valids: [undefined],
invalids: [null],
children: {
email: {
type: 'string',
valids: [undefined],
invalids: [null, ''],
invalids: [''],
rules: [{ name: 'email' }]
},
date: {
type: 'date',
valids: [undefined],
invalids: [null]
type: 'date'
},
child: {
type: 'object',
valids: [undefined],
invalids: [null],
children: {
alphanum: {
type: 'string',
valids: [undefined],
invalids: [null, ''],
invalids: [''],
rules: [{ name: 'alphanum' }]

@@ -1328,10 +1318,7 @@ }

{
type: 'number',
valids: [undefined],
invalids: [null]
type: 'number'
},
{
type: 'string',
valids: [undefined],
invalids: [null, ''],
invalids: [''],
rules: [{ name: 'min', arg: 3 }]

@@ -1342,4 +1329,3 @@ }

type: 'string',
valids: [undefined],
invalids: [null, ''],
invalids: [''],
rules: [{ name: 'max', arg: 3 }]

@@ -1349,8 +1335,10 @@ },

type: 'string',
invalids: [null, '', undefined]
flags: {
presence: 'required'
},
invalids: [ '']
},
xor: {
type: 'string',
valids: [undefined],
invalids: [null, '']
invalids: ['']
},

@@ -1362,11 +1350,14 @@ renamed: {

},
valids: [undefined, '456'],
invalids: [null, '']
valids: ['456'],
invalids: ['']
},
notEmpty: {
type: 'string',
flags: {
presence: 'required'
},
description: 'a',
notes: ['b'],
tags: ['c'],
invalids: [null, '', undefined]
invalids: ['']
}

@@ -1407,5 +1398,3 @@ },

expect(description).to.deep.equal({
type: 'any',
valids: [undefined],
invalids: [null]
type: 'any'
});

@@ -1412,0 +1401,0 @@ done();

@@ -540,2 +540,18 @@ // Load modules

});
it('should be able to rename keys that are empty strings', function (done) {
var schema = Joi.object().rename('', 'notEmpty');
var input = {
'': 'something'
};
schema.validate(input, function (err, value) {
expect(err).to.not.exist;
expect(value['']).to.not.exist;
expect(value.notEmpty).to.equal('something');
done();
});
});
});

@@ -550,8 +566,21 @@

expect(desc).to.deep.equal({
type: 'object',
valids: [undefined],
invalids: [null]
type: 'object'
});
done();
});
it('respects the shallow parameter', function (done) {
var schema = Joi.object({
name: Joi.string(),
child: Joi.object({
name: Joi.string()
})
});
expect(schema.describe(true)).to.not.contain.key('children');
expect(schema.describe()).to.contain.key('children');
done();
});
});

@@ -595,2 +624,44 @@

describe('#pattern', function () {
it('validates unknown keys using a pattern', function (done) {
var schema = Joi.object({
a: Joi.number()
}).pattern(/\d+/, Joi.boolean()).pattern(/\w\w+/, 'x');
Joi.validate({ bb: 'y', 5: 'x' }, schema, { abortEarly: false }, function (err, value) {
expect(err).to.exist;
expect(err.message).to.equal('5 must be a boolean. bb must be one of x');
Helper.validate(schema, [
[{ a: 5 }, true],
[{ a: 'x' }, false],
[{ b: 'x' }, false],
[{ bb: 'x' }, true],
[{ 5: 'x' }, false],
[{ 5: false }, true],
[{ 5: undefined }, true]
], done)
});
});
it('validates unknown keys using a pattern (nested)', function (done) {
var schema = {
x: Joi.object({
a: Joi.number()
}).pattern(/\d+/, Joi.boolean()).pattern(/\w\w+/, 'x')
};
Joi.validate({ x: { bb: 'y', 5: 'x' } }, schema, { abortEarly: false }, function (err, value) {
expect(err).to.exist;
expect(err.message).to.equal('5 must be a boolean. bb must be one of x');
done();
});
});
});
describe('#with', function () {

@@ -619,2 +690,11 @@

});
it('should validate correctly when key is an empty string', function (done) {
var schema = Joi.object().with('', 'b');
Helper.validate(schema, [
[{ c: 'hi', d: 'there' }, true],
]);
done();
});
});

@@ -643,4 +723,15 @@

expect(error).to.equal(true);
done();
});
it('should validate correctly when key is an empty string', function (done) {
var schema = Joi.object().without('', 'b');
Helper.validate(schema, [
[{ a: 'hi', b: 'there' }, true]
]);
done();
});
});

@@ -647,0 +738,0 @@

@@ -239,2 +239,144 @@ // Load modules

describe('#lowercase', function () {
it('only allows strings that are entirely lowercase', function (done) {
var schema = Joi.string().lowercase();
Helper.validateOptions(schema, [
['this is all lowercase', true],
['5', true],
['lower\tcase', true],
['Uppercase', false],
['MixEd cAsE', false],
[1, false]
], { convert: false }, done);
});
it('coerce string to lowercase before validation', function (done) {
var schema = Joi.string().lowercase();
schema.validate('UPPER TO LOWER', function (err, value) {
expect(err).to.not.exist;
expect(value).to.equal('upper to lower');
done();
});
});
it('should work in combination with a trim', function (done) {
var schema = Joi.string().lowercase().trim();
Helper.validate(schema, [
[' abc', true],
[' ABC', true],
['ABC', true],
[1, false]
], done);
});
});
describe('#uppercase', function () {
it('only allow strings that are entirely uppercase', function (done) {
var schema = Joi.string().uppercase();
Helper.validateOptions(schema, [
['THIS IS ALL UPPERCASE', true],
['5', true],
['UPPER\nCASE', true],
['lOWERCASE', false],
['MixEd cAsE', false],
[1, false]
], { convert: false }, done);
});
it('coerce string to uppercase before validation', function (done) {
var schema = Joi.string().uppercase();
schema.validate('lower to upper', function (err, value) {
expect(err).to.not.exist;
expect(value).to.equal('LOWER TO UPPER');
done();
});
});
it('works in combination with a forced trim', function (done) {
var schema = Joi.string().uppercase().trim();
Helper.validate(schema, [
[' abc', true],
[' ABC', true],
['ABC', true],
[1, false]
], done);
});
});
describe('#trim', function () {
it('only allow strings that have no leading or trailing whitespace', function (done) {
var schema = Joi.string().trim();
Helper.validateOptions(schema, [
[' something', false],
['something ', false],
['something\n', false],
['some thing', true],
['something', true]
], { convert: false }, done);
});
it('removes leading and trailing whitespace before validation', function (done) {
var schema = Joi.string().trim();
schema.validate(' trim this ', function (err, value) {
expect(err).to.not.exist;
expect(value).to.equal('trim this');
done();
});
});
it('should work in combination with min', function (done) {
var schema = Joi.string().min(4).trim();
Helper.validate(schema, [
[' a ', false],
['abc ', false],
['abcd ', true]
], done);
});
it('should work in combination with max', function (done) {
var schema = Joi.string().max(4).trim();
Helper.validate(schema, [
[' abcde ', false],
['abc ', true],
['abcd ', true]
], done);
});
it('should work in combination with length', function (done) {
var schema = Joi.string().length(4).trim();
Helper.validate(schema, [
[' ab ', false],
['abc ', false],
['abcd ', true]
], done);
});
it('should work in combination with a case change', function (done) {
var schema = Joi.string().trim().lowercase();
Helper.validate(schema, [
[' abc', true],
[' ABC', true],
['ABC', true]
], done);
});
});
describe('#validate', function () {

@@ -406,4 +548,8 @@

Helper.validate(schema, [
['van@walmartlabs.com', true],
['@iaminvalid.com', false]
['joe@example.com', true],
['"joe"@example.com', true],
['@iaminvalid.com', false],
['joe@[IPv6:2a00:1450:4001:c02::1b]', true],
['12345678901234567890123456789012345678901234567890123456789012345@walmartlabs.com', false],
['123456789012345678901234567890123456789012345678901234567890@12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.12345.toolong.com', false]
], done);

@@ -410,0 +556,0 @@ });

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc