diff options
Diffstat (limited to 'node_modules/joi/lib/index.js')
| -rw-r--r-- | node_modules/joi/lib/index.js | 434 |
1 files changed, 434 insertions, 0 deletions
diff --git a/node_modules/joi/lib/index.js b/node_modules/joi/lib/index.js new file mode 100644 index 00000000..24022fdf --- /dev/null +++ b/node_modules/joi/lib/index.js @@ -0,0 +1,434 @@ +'use strict'; + +// Load modules + +const Hoek = require('hoek'); +const Any = require('./types/any'); +const Cast = require('./cast'); +const Errors = require('./errors'); +const Lazy = require('./types/lazy'); +const Ref = require('./ref'); + + +// Declare internals + +const internals = { + alternatives: require('./types/alternatives'), + array: require('./types/array'), + boolean: require('./types/boolean'), + binary: require('./types/binary'), + date: require('./types/date'), + func: require('./types/func'), + number: require('./types/number'), + object: require('./types/object'), + string: require('./types/string') +}; + +internals.applyDefaults = function (schema) { + + Hoek.assert(this, 'Must be invoked on a Joi instance.'); + + if (this._defaults) { + schema = this._defaults(schema); + } + + schema._currentJoi = this; + + return schema; +}; + +internals.root = function () { + + const any = new Any(); + + const root = any.clone(); + Any.prototype._currentJoi = root; + root._currentJoi = root; + + root.any = function () { + + Hoek.assert(arguments.length === 0, 'Joi.any() does not allow arguments.'); + + return internals.applyDefaults.call(this, any); + }; + + root.alternatives = root.alt = function () { + + const alternatives = internals.applyDefaults.call(this, internals.alternatives); + return arguments.length ? alternatives.try.apply(alternatives, arguments) : alternatives; + }; + + root.array = function () { + + Hoek.assert(arguments.length === 0, 'Joi.array() does not allow arguments.'); + + return internals.applyDefaults.call(this, internals.array); + }; + + root.boolean = root.bool = function () { + + Hoek.assert(arguments.length === 0, 'Joi.boolean() does not allow arguments.'); + + return internals.applyDefaults.call(this, internals.boolean); + }; + + root.binary = function () { + + Hoek.assert(arguments.length === 0, 'Joi.binary() does not allow arguments.'); + + return internals.applyDefaults.call(this, internals.binary); + }; + + root.date = function () { + + Hoek.assert(arguments.length === 0, 'Joi.date() does not allow arguments.'); + + return internals.applyDefaults.call(this, internals.date); + }; + + root.func = function () { + + Hoek.assert(arguments.length === 0, 'Joi.func() does not allow arguments.'); + + return internals.applyDefaults.call(this, internals.func); + }; + + root.number = function () { + + Hoek.assert(arguments.length === 0, 'Joi.number() does not allow arguments.'); + + return internals.applyDefaults.call(this, internals.number); + }; + + root.object = function () { + + const object = internals.applyDefaults.call(this, internals.object); + return arguments.length ? object.keys.apply(object, arguments) : object; + }; + + root.string = function () { + + Hoek.assert(arguments.length === 0, 'Joi.string() does not allow arguments.'); + + return internals.applyDefaults.call(this, internals.string); + }; + + root.ref = function () { + + return Ref.create.apply(null, arguments); + }; + + root.isRef = function (ref) { + + return Ref.isRef(ref); + }; + + root.validate = function (value /*, [schema], [options], callback */) { + + const last = arguments[arguments.length - 1]; + const callback = typeof last === 'function' ? last : null; + + const count = arguments.length - (callback ? 1 : 0); + if (count === 1) { + return any.validate(value, callback); + } + + const options = count === 3 ? arguments[2] : {}; + const schema = root.compile(arguments[1]); + + return schema._validateWithOptions(value, options, callback); + }; + + root.describe = function () { + + const schema = arguments.length ? root.compile(arguments[0]) : any; + return schema.describe(); + }; + + root.compile = function (schema) { + + try { + return Cast.schema(this, schema); + } + catch (err) { + if (err.hasOwnProperty('path')) { + err.message = err.message + '(' + err.path + ')'; + } + throw err; + } + }; + + root.assert = function (value, schema, message) { + + root.attempt(value, schema, message); + }; + + root.attempt = function (value, schema, message) { + + const result = root.validate(value, schema); + const error = result.error; + if (error) { + if (!message) { + if (typeof error.annotate === 'function') { + error.message = error.annotate(); + } + throw error; + } + + if (!(message instanceof Error)) { + if (typeof error.annotate === 'function') { + error.message = `${message} ${error.annotate()}`; + } + throw error; + } + + throw message; + } + + return result.value; + }; + + root.reach = function (schema, path) { + + Hoek.assert(schema && schema instanceof Any, 'you must provide a joi schema'); + Hoek.assert(typeof path === 'string', 'path must be a string'); + + if (path === '') { + return schema; + } + + const parts = path.split('.'); + const children = schema._inner.children; + if (!children) { + return; + } + + const key = parts[0]; + for (let i = 0; i < children.length; ++i) { + const child = children[i]; + if (child.key === key) { + return this.reach(child.schema, path.substr(key.length + 1)); + } + } + }; + + root.lazy = function (fn) { + + return Lazy.set(fn); + }; + + root.defaults = function (fn) { + + Hoek.assert(typeof fn === 'function', 'Defaults must be a function'); + + let joi = Object.create(this.any()); + joi = fn(joi); + + Hoek.assert(joi && joi instanceof this.constructor, 'defaults() must return a schema'); + + Object.assign(joi, this, joi.clone()); // Re-add the types from `this` but also keep the settings from joi's potential new defaults + + joi._defaults = (schema) => { + + if (this._defaults) { + schema = this._defaults(schema); + Hoek.assert(schema instanceof this.constructor, 'defaults() must return a schema'); + } + + schema = fn(schema); + Hoek.assert(schema instanceof this.constructor, 'defaults() must return a schema'); + return schema; + }; + + return joi; + }; + + root.extend = function () { + + const extensions = Hoek.flatten(Array.prototype.slice.call(arguments)); + Hoek.assert(extensions.length > 0, 'You need to provide at least one extension'); + + this.assert(extensions, root.extensionsSchema); + + const joi = Object.create(this.any()); + Object.assign(joi, this); + + for (let i = 0; i < extensions.length; ++i) { + let extension = extensions[i]; + + if (typeof extension === 'function') { + extension = extension(joi); + } + + this.assert(extension, root.extensionSchema); + + const base = (extension.base || this.any()).clone(); // Cloning because we're going to override language afterwards + const ctor = base.constructor; + const type = class extends ctor { // eslint-disable-line no-loop-func + + constructor() { + + super(); + if (extension.base) { + Object.assign(this, base); + } + + this._type = extension.name; + + if (extension.language) { + this._settings = this._settings || { language: {} }; + this._settings.language = Hoek.applyToDefaults(this._settings.language, { + [extension.name]: extension.language + }); + } + } + + }; + + if (extension.coerce) { + type.prototype._coerce = function (value, state, options) { + + if (ctor.prototype._coerce) { + const baseRet = ctor.prototype._coerce.call(this, value, state, options); + + if (baseRet.errors) { + return baseRet; + } + + value = baseRet.value; + } + + const ret = extension.coerce.call(this, value, state, options); + if (ret instanceof Errors.Err) { + return { value, errors: ret }; + } + + return { value: ret }; + }; + } + if (extension.pre) { + type.prototype._base = function (value, state, options) { + + if (ctor.prototype._base) { + const baseRet = ctor.prototype._base.call(this, value, state, options); + + if (baseRet.errors) { + return baseRet; + } + + value = baseRet.value; + } + + const ret = extension.pre.call(this, value, state, options); + if (ret instanceof Errors.Err) { + return { value, errors: ret }; + } + + return { value: ret }; + }; + } + + if (extension.rules) { + for (let j = 0; j < extension.rules.length; ++j) { + const rule = extension.rules[j]; + const ruleArgs = rule.params ? + (rule.params instanceof Any ? rule.params._inner.children.map((k) => k.key) : Object.keys(rule.params)) : + []; + const validateArgs = rule.params ? Cast.schema(this, rule.params) : null; + + type.prototype[rule.name] = function () { // eslint-disable-line no-loop-func + + if (arguments.length > ruleArgs.length) { + throw new Error('Unexpected number of arguments'); + } + + const args = Array.prototype.slice.call(arguments); + let hasRef = false; + let arg = {}; + + for (let k = 0; k < ruleArgs.length; ++k) { + arg[ruleArgs[k]] = args[k]; + if (!hasRef && Ref.isRef(args[k])) { + hasRef = true; + } + } + + if (validateArgs) { + arg = joi.attempt(arg, validateArgs); + } + + let schema; + if (rule.validate) { + const validate = function (value, state, options) { + + return rule.validate.call(this, arg, value, state, options); + }; + + schema = this._test(rule.name, arg, validate, { + description: rule.description, + hasRef + }); + } + else { + schema = this.clone(); + } + + if (rule.setup) { + const newSchema = rule.setup.call(schema, arg); + if (newSchema !== undefined) { + Hoek.assert(newSchema instanceof Any, `Setup of extension Joi.${this._type}().${rule.name}() must return undefined or a Joi object`); + schema = newSchema; + } + } + + return schema; + }; + } + } + + if (extension.describe) { + type.prototype.describe = function () { + + const description = ctor.prototype.describe.call(this); + return extension.describe.call(this, description); + }; + } + + const instance = new type(); + joi[extension.name] = function () { + + return internals.applyDefaults.call(this, instance); + }; + } + + return joi; + }; + + root.extensionSchema = internals.object.keys({ + base: internals.object.type(Any, 'Joi object'), + name: internals.string.required(), + coerce: internals.func.arity(3), + pre: internals.func.arity(3), + language: internals.object, + describe: internals.func.arity(1), + rules: internals.array.items(internals.object.keys({ + name: internals.string.required(), + setup: internals.func.arity(1), + validate: internals.func.arity(4), + params: [ + internals.object.pattern(/.*/, internals.object.type(Any, 'Joi object')), + internals.object.type(internals.object.constructor, 'Joi object') + ], + description: [internals.string, internals.func.arity(1)] + }).or('setup', 'validate')) + }).strict(); + + root.extensionsSchema = internals.array.items([internals.object, internals.func.arity(1)]).strict(); + + root.version = require('../package.json').version; + + return root; +}; + + +module.exports = internals.root(); |
