|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- '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();
|