Socket
Socket
Sign inDemoInstall

joi

Package Overview
Dependencies
Maintainers
3
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 1.0.0 to 1.1.0

test/languages/en-US.json

34

languages/en-US.json
{
"base": {
"unknown": "the key ({{key}}) is not allowed",
"without": "the value of {{key}} must exist without {{value}}",
"invalid": "the value of {{key}} is not allowed to be {{value}}",
"rename": {
"allowMult": "allowMult false and already renamed: {{key}}, {{value}}",
"allowOverwrite": "allowOverwrite false and target exists: {{key}}, {{value}}"
},
"validate": {
"allowOnly": "the value of {{key}} must be one of {{value}}"
}
},
"array": {
"base": "the value of {{key}} must be an Array"
"base": "the value of {{key}} must be an Array",
"includes": "the values(s) {{value}} in array {{key}} must be of the following type(s) {{validTypes}}",
"excludes": "the values supplied to array {{key}} must contain {{validTypes}}"
},

@@ -11,3 +25,21 @@ "number": {

"int": "the value of {{key}} must be an integer"
},
"boolean": {
"base": "the value of {{key}} must be a boolean"
},
"function": {
"base": "the value of {{key}} must be a Function"
},
"object": {
"base": "the value of {{key}} must be an object",
"allowOtherKeys": "the key ({{key}}) is not allowed"
},
"string": {
"base": "the value of {{key}} must be a string",
"min": "the value of {{key}} must be at least {{value}} characters long",
"max": "the value of {{key}} must be less than (or equal to) {{value}} characters long",
"regex": "the value of {{key}} must match the RegExp {{value}}",
"date": "the value of {{key}} must be a valid JavaScript Date format",
"email": "The value of {{key}} must be a valid email"
}
}

4

lib/errors.js

@@ -30,5 +30,5 @@ // Load modules

internals.Errors.prototype.addLocalized = function (type, key, value, path) {
internals.Errors.prototype.addLocalized = function (type, key, replacements, path) {
var message = this._messages.print(type, key, value);
var message = this._messages.print(type, key, replacements);
Utils.assert(message, 'No message resource found in languages');

@@ -35,0 +35,0 @@

@@ -23,10 +23,30 @@ // Load modules

internals.joiConfig = {
skipFunctions: 'boolean',
saveConversions: 'boolean',
skipConversions: 'boolean',
languagePath: 'string'
};
exports.validate = function (object, config) {
var self = this;
var settings = Utils.clone(exports.settings);
var skipFunctions = this.settings.skipFunctions;
var errors = new Errors(object, config);
if (!(config instanceof Types.Base)) {
var names = Object.keys(config);
for (var i = 0, il = names.length; i < il; ++ i) {
var name = names[i];
if (typeof config[name] === internals.joiConfig[name]) {
settings[name] = config[name];
delete config[name];
}
}
}
var skipFunctions = settings.skipFunctions;
var errors = new Errors(object, settings);
var processConfig = function () {

@@ -54,3 +74,3 @@

if ((!skipFunctions || unprocessedValueType !== 'function') && unprocessedValueType !== 'undefined') {
errors.add('the key (' + unprocessedKey + ') is not allowed', unprocessedKey);
errors.addLocalized('base.unknown', unprocessedKey, null, unprocessedKey);
}

@@ -65,3 +85,3 @@ });

if (Array.isArray(keyConfig)) {
var localErrors = new Errors(object, config);
var localErrors = new Errors(object, settings);
for (var i = 0, il = keyConfig.length; i < il; ++i) {

@@ -75,3 +95,3 @@ converted = convertType(keyConfig[i], key, value);

localErrors._values.map(function(error) {
localErrors._values.map(function (error) {

@@ -93,5 +113,5 @@ errors.add(error.message, error.path);

var converter = T && T().convert || null;
if (!self.settings.skipConversions && typeof converter === 'function') {
if (!settings.skipConversions && typeof converter === 'function') {
value = converter(value);
if (self.settings.saveConversions && key !== null) {
if (settings.saveConversions && key !== null) {
object[key] = value;

@@ -98,0 +118,0 @@ }

@@ -9,3 +9,5 @@ // Load modules

var internals = {};
var internals = {
templatePattern: /\{\{\s*([^\s}]+?)\s*\}\}/ig
};

@@ -21,8 +23,10 @@

internals.Messages.prototype.print = function (chain, key, value) {
internals.Messages.prototype.print = function (chain, key, replacements) {
var message = Utils.reach(this._resources, chain);
return message && /\{\{[ ]*key[ ]*\}\}/ig.test(message) ?
message.replace(/\{\{[ ]*key[ ]*\}\}/ig, key).replace(/\{\{[ ]*value[ ]*\}\}/ig, value) :
key;
return message ? message.replace(internals.templatePattern, function (match, name) {
var replacement = name === 'key' ? key : Utils.reach(replacements, name);
return replacement;
}) : key;
};

@@ -37,5 +37,4 @@ // Load modules

if (typeof value === 'string') {
// first check with isNaN, because JSON.parse convert number string to number!
if(!isNaN(value)) {
if (!isNaN(value)) {
//given value is a string which could be converted to number, just return it.

@@ -73,3 +72,5 @@ return value;

if (!result) {
errors.addLocalized('array.base', key, value, keyPath);
errors.addLocalized('array.base', key, {
value: value
}, keyPath);
}

@@ -117,3 +118,3 @@

for (var i = 0; i < allowedTypes.length; i++) {
for (var i = 0, il = allowedTypes.length; i < il; ++i) {
// For each validator specified

@@ -123,6 +124,6 @@

var validatorIsValid = true;
for (var j = 0; j < validators.length; j++) {
for (var j = 0, jl = validators.length; j < jl; ++j) {
// For each input supplied
for (m = 0; m < values.length; m++) {
for (var m = 0, ml = values.length; m < ml; ++m) {
var value = values[m];

@@ -144,3 +145,3 @@ var result = validators[j](value, obj, key, errors);

for (m = 0; m < values.length; m++) {
for (m = 0, ml = values.length; m < ml; ++m) {
if (!validatedValuesTable.hasOwnProperty(m)) {

@@ -157,4 +158,7 @@ invalidValues.push(values[m]);

var vals = invalidValues.map(function (d) { return JSON.stringify(d); }).join(', ').replace(/[\']/g, '\'');
var msg = 'the value(s) ' + vals + ' in array ' + key + ' must be of the following type(s) ' + Sys.inspect(validTypes);
errors.add(msg, keyPath);
errors.addLocalized('array.includes', key, {
value: vals,
validTypes: Sys.inspect(validTypes)
}, keyPath);
}

@@ -176,3 +180,2 @@ return valueIsValid;

var self = this;
var subElement = (new internals.ArrayType.super_()).getDataKey();
var args = Array.prototype.slice.call(arguments);

@@ -199,3 +202,5 @@

if (components.indexOf(true) >= 0) {
errors.add('the values supplied to array ' + key + ' must contain ' + JSON.stringify(validTypes), keyPath);
errors.addLocalized('array.excludes', key, {
validTypes: Sys.inspect(validTypes)
}, keyPath);
return false;

@@ -213,3 +218,5 @@ }

errors.add('the values supplied to array ' + key + ' must contain ' + JSON.stringify(validTypes), keyPath);
errors.addLocalized('array.excludes', key, {
validTypes: Sys.inspect(validTypes)
}, keyPath);

@@ -216,0 +223,0 @@ return false;

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

for (var i = 0, li = arguments.length; i < li; i++) {
Utils.assert(typeof arguments[i] === 'string', 'arguments must be a string')
Utils.assert(typeof arguments[i] === 'string', 'arguments must be a string');
}

@@ -230,3 +230,3 @@ this.add('with', this._with(Array.prototype.slice.call(arguments)), arguments);

for (var i = 0, li = arguments.length; i < li; i++) {
Utils.assert(typeof arguments[i] === 'string', 'arguments must be a string')
Utils.assert(typeof arguments[i] === 'string', 'arguments must be a string');
}

@@ -252,3 +252,5 @@ if (this.__modifiers.has('required')) {

if (!result) {
errors.add('the value of ' + key + ' must exist without ' + peers, keyPath);
errors.addLocalized('base.without', key, {
value: peers
}, keyPath);
}

@@ -288,7 +290,9 @@

errors = errors || {};
errors.add = errors.add || function () { };
errors.addLocalized = errors.addLocalized || function () { };
var renamed = errors._renamed || {};
if (options.allowMult === false && to in renamed) {
errors.add('allowMult false and already renamed', keyPath);
errors.addLocalized('base.rename.allowMult', key, {
value: to
}, keyPath);
return false;

@@ -298,3 +302,5 @@ }

if (options.allowOverwrite === false && obj.hasOwnProperty(to)) {
errors.add('allowOverwrite false and target exists: ' + key + ', ' + to, keyPath);
errors.addLocalized('base.rename.allowOverwrite', key, {
value: to
}, keyPath);
return false;

@@ -385,3 +391,5 @@ }

}
errors.add('the value of ' + key + ' is not allowed to be ' + invalidValue, keyPath);
errors.addLocalized('base.invalid', key, {
value: invalidValue
}, keyPath);
if (this.options.shortCircuit === true) {

@@ -409,3 +417,5 @@ return status;

errors.add('the value of ' + key + ' must be one of ' + valids.join(', '), keyPath);
errors.addLocalized('base.validate.allowOnly', key, {
value: valids.join(', ')
}, keyPath);

@@ -412,0 +422,0 @@ if (this.options.shortCircuit === true) {

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

if (!result) {
errors.add('the value of ' + key + ' must be a boolean', keyPath);
errors.addLocalized('boolean.base', key, null, keyPath);
}

@@ -50,0 +50,0 @@

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

internals.FunctionType.prototype._base = function() {
internals.FunctionType.prototype._base = function () {

@@ -39,3 +39,3 @@ return function(value, obj, key, errors, keyPath) {

if (!result) {
errors.add('the value of ' + key + ' must be a Function', keyPath);
errors.addLocalized('function.base', key, null, keyPath);
}

@@ -42,0 +42,0 @@

@@ -46,3 +46,5 @@ // Load modules

if ((typeof value !== 'number' && typeof value !== 'string') || isNaN(+value)) {
errors.addLocalized('number.base', key, value, keyPath);
errors.addLocalized('number.base', key, {
value: value
}, keyPath);
return false;

@@ -71,3 +73,5 @@ }

if (!result) {
errors.addLocalized('number.min', key, value, keyPath);
errors.addLocalized('number.min', key, {
value: value
}, keyPath);
}

@@ -95,3 +99,5 @@

if (!result) {
errors.addLocalized('number.max', key, value, keyPath);
errors.addLocalized('number.max', key, {
value: value
}, keyPath);
}

@@ -116,3 +122,5 @@ return result;

if (!result) {
errors.addLocalized('number.int', key, value, keyPath);
errors.addLocalized('number.int', key, {
value: value
}, keyPath);
}

@@ -139,3 +147,5 @@

if (!result) {
errors.addLocalized('number.float', key, value, keyPath);
errors.addLocalized('number.float', key, {
value: value
}, keyPath);
}

@@ -142,0 +152,0 @@

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

if (typeof value !== 'object') {
errors.add('the value of ' + key + ' must be an object', keyPath);
errors.addLocalized('object.base', key, null, keyPath);
return false;

@@ -123,3 +123,3 @@ }

if (unprocessedValueType !== 'function' && unprocessedValueType !== 'undefined') {
errors.add('the key (' + unprocessedKey + ') is not allowed', (topKeyPath ? (topKeyPath + '.' + unprocessedKey) : unprocessedKey));
errors.addLocalized('object.allowOtherKeys', unprocessedKey, null, (topKeyPath ? (topKeyPath + '.' + unprocessedKey) : unprocessedKey));

@@ -126,0 +126,0 @@ if (self.options.shortCircuit === true) {

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

if (!result) {
errors.add('the value of ' + key + ' must be a string', keyPath);
errors.addLocalized('string.base', key, null, keyPath);
}

@@ -75,3 +75,5 @@

if (!result) {
errors.add('the value of ' + key + ' must be at least ' + n + ' characters long', keyPath);
errors.addLocalized('string.min', key, {
value: n
}, keyPath);
}

@@ -101,3 +103,5 @@

if (!result) {
errors.add('the value of ' + key + ' must be less than (or equal to) ' + n + ' characters long', keyPath);
errors.addLocalized('string.max', key, {
value: n
}, keyPath);
}

@@ -125,3 +129,5 @@

if (!result) {
errors.add('the value of ' + key + ' must match the RegExp ' + n.toString(), keyPath);
errors.addLocalized('string.regex', key, {
value: n.toString()
}, keyPath);
}

@@ -145,3 +151,3 @@ return result;

errors = errors || {};
errors.add = errors.add || function () { };
errors.addLocalized = errors.addLocalized || function () { };
value = (isNaN(Number(value)) === false) ? +value : value;

@@ -152,3 +158,3 @@ var converted = new Date(value);

if (!result) {
errors.add('the value of ' + key + ' must be a valid JavaScript Date format', keyPath);
errors.addLocalized('string.date', key, null, keyPath);
}

@@ -189,3 +195,3 @@ return result;

if (!result) {
errors.add('The value of ' + key + ' must be a valid email', keyPath);
errors.addLocalized('string.email', key, null, keyPath);
}

@@ -192,0 +198,0 @@ return result;

{
"name": "joi",
"description": "Object schema validation",
"version": "1.0.0",
"version": "1.1.0",
"repository": "git://github.com/spumko/joi",

@@ -15,3 +15,3 @@ "main": "index",

"dependencies": {
"hoek": "0.9.x"
"hoek": "1.0.x"
},

@@ -18,0 +18,0 @@ "devDependencies": {

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

#### Custom Messages
Joi error messages can be updated and replaced with localized versions. Use the `languagePath` option to specify a file path to a JSON file that contains error messages. Each message supports a mustache style template with the following keys:
- `{{key}}` - the schema property that fails validation
- `{{value}}` - the invalid value assigned to the key
#### Skip Functions

@@ -464,3 +456,29 @@

### Local
All global options may be overridden by specifying the option directly on the schema object.
#### Custom Messages
Joi error messages can be updated and replaced with localized versions. Use the `languagePath` option to specify a file path to a JSON file that contains error messages. Each message supports a mustache style template with the following keys:
- `{{key}}` - the schema property that fails validation
- `{{value}}` - the invalid value assigned to the key
- `{{validTypes}}` - an optional list of acceptable inputs
```javascript
var S = Joi.Types.String();
var config = {
username: S().alphanum().min(3).max(30),
password: S().regex(/[a-zA-Z0-9]{3,30}/),
languagePath: Path.join(__dirname, 'languages', 'en-US.json')
};
Joi.validate({
username: 'ab',
password: '1'
}, config);
```
### Type-Specific

@@ -478,7 +496,7 @@

schema.nickname.validate('o', null, null, errors) // => populates errors with all failing constraints
// alternative way
var input = { amount: 2.5 };
var schema = { amount: T.Number().integer().min(3).max(5).noShortCircuit() };
Joi.validate(input, schema);

@@ -485,0 +503,0 @@

// Load modules
var Lab = require('lab');
var Path = require('path');
var Joi = require('../lib');

@@ -83,3 +84,8 @@

expect(Joi.validate({ txt: 'test' }, config5)).to.be.null;
expect(Joi.validate({ upc: null, txt: null }, config5)).to.not.be.null;
var err = Joi.validate({ upc: null, txt: null }, config5);
expect(err).to.not.be.null;
expect(err.message).to.contain('the value of txt must exist without upc');
expect(err.message).to.contain('the value of upc must exist without txt');
expect(Joi.validate({ txt: 'test', upc: 'test' }, config5)).to.not.be.null;

@@ -107,3 +113,8 @@ expect(Joi.validate({ txt: 'test', upc: null }, config5)).to.be.null;

expect(Joi.validate({ upc: null, txt: null }, config6)).to.not.be.null;
expect(Joi.validate({ txt: 'test', upc: 'test' }, config6)).to.not.be.null;
var err = Joi.validate({ txt: 'test', upc: 'test' }, config6);
expect(err).to.not.be.null;
expect(err.message).to.contain('the value of txt must exist without upc');
expect(err.message).to.contain('the value of upc must exist without txt');
expect(Joi.validate({ txt: 'test', upc: null }, config6)).to.be.null;

@@ -135,3 +146,9 @@ expect(Joi.validate({ txt: 'test', upc: '' }, config6)).to.be.null;

expect(Joi.validate({ auth: { mode: 'none' } }, config)).to.not.be.null;
var err = Joi.validate({ auth: { mode: 'none' } }, config);
expect(err).to.not.be.null;
expect(err.message).to.contain('the value of mode must be one of undefined, try, optional, required, null');
expect(err.message).to.contain('the value of auth must be a string');
expect(err.message).to.contain('the value of auth must be a boolean');
expect(Joi.validate({ auth: { mode: 'try' } }, config)).to.be.null;

@@ -148,4 +165,11 @@ expect(Joi.validate({ something: undefined }, config)).to.be.null;

expect(Joi.validate({ auth: { mode: 'try' } }, T.Object())).to.be.null;
expect(Joi.validate(true, T.Object())).to.not.be.null;
expect(Joi.validate(true, T.String())).to.not.be.null;
var err = Joi.validate(true, T.Object());
expect(err).to.not.be.null;
expect(err.message).to.contain('the value of <root> must be an object');
err = Joi.validate(true, T.String());
expect(err).to.not.be.null;
expect(err.message).to.contain('the value of <root> must be a string');
expect(Joi.validate('test@test.com', T.String().email())).to.be.null;

@@ -171,2 +195,22 @@ expect(Joi.validate({ param: 'item'}, T.Object({ param: T.String().required() }, true))).to.be.null;

it('should support setting the saveConversions setting locally', function (done) {
expect(Joi.settings.saveConversions).to.equal(false);
var config = {
a: T.Number(),
saveConversions: true
};
var original = { a: '5' };
var validated = { a: 5 };
expect(Joi.validate(original, config)).to.be.null;
expect(validated).to.deep.equal(original);
expect(Joi.settings.saveConversions).to.equal(false);
done();
});
it('should not alter valid top level objects when saveConversions setting is enabled', function (done) {

@@ -199,6 +243,14 @@ Joi.settings.saveConversions = true;

expect(Joi.validate({ foo: 'bar' }, T.Object({}))).to.exist;
expect(Joi.validate({ foo: 'bar' }, {})).to.exist;
expect(Joi.validate({ foo: 'bar' }, { other: T.Number() })).to.exist;
var err = Joi.validate({ foo: 'bar' }, T.Object({}));
expect(err).to.exist;
expect(err.message).to.contain('the key (foo) is not allowed');
err = Joi.validate({ foo: 'bar' }, {});
expect(err).to.exist;
expect(err.message).to.contain('the key (foo) is not allowed');
err = Joi.validate({ foo: 'bar' }, { other: T.Number() });
expect(err).to.exist;
expect(err.message).to.contain('the key (foo) is not allowed');
done();

@@ -215,5 +267,10 @@ });

expect(Joi.validate({ auth: { unknown: true } }, config)).to.not.be.null;
expect(Joi.validate({ something: false }, config)).to.not.be.null;
var err = Joi.validate({ auth: { unknown: true } }, config);
expect(err).to.not.be.null;
expect(err.message).to.contain('the key (unknown) is not allowed');
err = Joi.validate({ something: false }, config);
expect(err).to.not.be.null;
expect(err.message).to.contain('the key (something) is not allowed');
done();

@@ -274,5 +331,13 @@ });

expect(Joi.validate({}, config)).to.not.be.null;
var err = Joi.validate({}, config);
expect(err).to.not.be.null;
expect(err.message).to.contain('the value of module is not allowed to be undefined');
expect(Joi.validate({ module: 'test' }, config)).to.be.null;
expect(Joi.validate({ module: {} }, config)).to.not.be.null;
err = Joi.validate({ module: {} }, config);
expect(err).to.not.be.null;
expect(err.message).to.contain('the value of compile is not allowed to be undefined');
expect(err.message).to.contain('the value of module must be a string');
expect(Joi.validate({ module: { compile: function () { } } }, config)).to.be.null;

@@ -359,15 +424,2 @@

it('should fail validation when the wrong types are supplied', function (done) {
var obj = {
a: 'a',
b: 'a',
c: 'joe@example.com'
};
var err = Joi.validate(obj, config1);
expect(err).to.exist;
done();
});
it('should fail validation when missing a required parameter', function (done) {

@@ -499,2 +551,3 @@

expect(err).to.exist;
expect(err.message).to.contain('the key (func) is not allowed');
done();

@@ -587,2 +640,44 @@ });

it('should support custom errors when validating types', function (done) {
var input = {
email: 'invalid-email',
date: 'invalid-date',
alphanum: '\b\n\f\r\t',
min: 'ab',
max: 'abcd',
required: 'hello',
xor: '123',
renamed: '456',
notEmpty: ''
};
var schema = {
email: T.String().email(),
date: T.String().date(),
alphanum: T.String().alphanum(),
min: T.String().min(3),
max: T.String().max(3),
required: T.String().required().without('xor'),
xor: T.String().without('required'),
renamed: T.String().rename('required'),
notEmpty: T.String().required(),
languagePath: Path.join(__dirname, 'languages', 'en-US.json')
};
var err = Joi.validate(input, schema);
expect(err).to.exist;
expect(err.message).to.contain('The `email` field must be a valid e-mail address.');
expect(err.message).to.contain('The `date` field must be a valid date.');
expect(err.message).to.contain('The `alphanum` field failed one or more validation constraints.');
expect(err.message).to.contain('The `min` field must be at least 3 characters long.');
expect(err.message).to.contain('The `max` field may not exceed 3 characters.');
expect(err.message).to.contain('The `required` field must be omitted if `xor` is specified.');
expect(err.message).to.contain('`required` is already assigned to the `renamed` field.');
expect(err.message).to.contain('Invalid value for `notEmpty`: `empty`.');
done();
});
internals.routeSchema = {

@@ -589,0 +684,0 @@ method: T.String().invalid('head').required(),

@@ -57,3 +57,5 @@ // Load modules

var messages = new Messages();
var result = messages.print('number.max', 'my key', 'my value');
var result = messages.print('number.max', 'my key', {
value: 'my value'
});

@@ -60,0 +62,0 @@ expect(result).to.contain('my key');

@@ -223,2 +223,3 @@ // Load modules

expect(err).to.exist;
expect(err.message).to.include('the value of arr must be an integer');
done();

@@ -225,0 +226,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