284 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			284 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| const Assert = require('@hapi/hoek/lib/assert');
 | |
| const Clone = require('@hapi/hoek/lib/clone');
 | |
| 
 | |
| const Cache = require('./cache');
 | |
| const Common = require('./common');
 | |
| const Compile = require('./compile');
 | |
| const Errors = require('./errors');
 | |
| const Extend = require('./extend');
 | |
| const Manifest = require('./manifest');
 | |
| const Ref = require('./ref');
 | |
| const Template = require('./template');
 | |
| const Trace = require('./trace');
 | |
| 
 | |
| let Schemas;
 | |
| 
 | |
| 
 | |
| const internals = {
 | |
|     types: {
 | |
|         alternatives: require('./types/alternatives'),
 | |
|         any: require('./types/any'),
 | |
|         array: require('./types/array'),
 | |
|         boolean: require('./types/boolean'),
 | |
|         date: require('./types/date'),
 | |
|         function: require('./types/function'),
 | |
|         link: require('./types/link'),
 | |
|         number: require('./types/number'),
 | |
|         object: require('./types/object'),
 | |
|         string: require('./types/string'),
 | |
|         symbol: require('./types/symbol')
 | |
|     },
 | |
|     aliases: {
 | |
|         alt: 'alternatives',
 | |
|         bool: 'boolean',
 | |
|         func: 'function'
 | |
|     }
 | |
| };
 | |
| 
 | |
| 
 | |
| if (Buffer) {                                                           // $lab:coverage:ignore$
 | |
|     internals.types.binary = require('./types/binary');
 | |
| }
 | |
| 
 | |
| 
 | |
| internals.root = function () {
 | |
| 
 | |
|     const root = {
 | |
|         _types: new Set(Object.keys(internals.types))
 | |
|     };
 | |
| 
 | |
|     // Types
 | |
| 
 | |
|     for (const type of root._types) {
 | |
|         root[type] = function (...args) {
 | |
| 
 | |
|             Assert(!args.length || ['alternatives', 'link', 'object'].includes(type), 'The', type, 'type does not allow arguments');
 | |
|             return internals.generate(this, internals.types[type], args);
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     // Shortcuts
 | |
| 
 | |
|     for (const method of ['allow', 'custom', 'disallow', 'equal', 'exist', 'forbidden', 'invalid', 'not', 'only', 'optional', 'options', 'prefs', 'preferences', 'required', 'strip', 'valid', 'when']) {
 | |
|         root[method] = function (...args) {
 | |
| 
 | |
|             return this.any()[method](...args);
 | |
|         };
 | |
|     }
 | |
| 
 | |
|     // Methods
 | |
| 
 | |
|     Object.assign(root, internals.methods);
 | |
| 
 | |
|     // Aliases
 | |
| 
 | |
|     for (const alias in internals.aliases) {
 | |
|         const target = internals.aliases[alias];
 | |
|         root[alias] = root[target];
 | |
|     }
 | |
| 
 | |
|     root.x = root.expression;
 | |
| 
 | |
|     // Trace
 | |
| 
 | |
|     if (Trace.setup) {                                          // $lab:coverage:ignore$
 | |
|         Trace.setup(root);
 | |
|     }
 | |
| 
 | |
|     return root;
 | |
| };
 | |
| 
 | |
| 
 | |
| internals.methods = {
 | |
| 
 | |
|     ValidationError: Errors.ValidationError,
 | |
|     version: Common.version,
 | |
|     cache: Cache.provider,
 | |
| 
 | |
|     assert(value, schema, ...args /* [message], [options] */) {
 | |
| 
 | |
|         internals.assert(value, schema, true, args);
 | |
|     },
 | |
| 
 | |
|     attempt(value, schema, ...args /* [message], [options] */) {
 | |
| 
 | |
|         return internals.assert(value, schema, false, args);
 | |
|     },
 | |
| 
 | |
|     build(desc) {
 | |
| 
 | |
|         Assert(typeof Manifest.build === 'function', 'Manifest functionality disabled');
 | |
|         return Manifest.build(this, desc);
 | |
|     },
 | |
| 
 | |
|     checkPreferences(prefs) {
 | |
| 
 | |
|         Common.checkPreferences(prefs);
 | |
|     },
 | |
| 
 | |
|     compile(schema, options) {
 | |
| 
 | |
|         return Compile.compile(this, schema, options);
 | |
|     },
 | |
| 
 | |
|     defaults(modifier) {
 | |
| 
 | |
|         Assert(typeof modifier === 'function', 'modifier must be a function');
 | |
| 
 | |
|         const joi = Object.assign({}, this);
 | |
|         for (const type of joi._types) {
 | |
|             const schema = modifier(joi[type]());
 | |
|             Assert(Common.isSchema(schema), 'modifier must return a valid schema object');
 | |
| 
 | |
|             joi[type] = function (...args) {
 | |
| 
 | |
|                 return internals.generate(this, schema, args);
 | |
|             };
 | |
|         }
 | |
| 
 | |
|         return joi;
 | |
|     },
 | |
| 
 | |
|     expression(...args) {
 | |
| 
 | |
|         return new Template(...args);
 | |
|     },
 | |
| 
 | |
|     extend(...extensions) {
 | |
| 
 | |
|         Common.verifyFlat(extensions, 'extend');
 | |
| 
 | |
|         Schemas = Schemas || require('./schemas');
 | |
| 
 | |
|         Assert(extensions.length, 'You need to provide at least one extension');
 | |
|         this.assert(extensions, Schemas.extensions);
 | |
| 
 | |
|         const joi = Object.assign({}, this);
 | |
|         joi._types = new Set(joi._types);
 | |
| 
 | |
|         for (let extension of extensions) {
 | |
|             if (typeof extension === 'function') {
 | |
|                 extension = extension(joi);
 | |
|             }
 | |
| 
 | |
|             this.assert(extension, Schemas.extension);
 | |
| 
 | |
|             const expanded = internals.expandExtension(extension, joi);
 | |
|             for (const item of expanded) {
 | |
|                 Assert(joi[item.type] === undefined || joi._types.has(item.type), 'Cannot override name', item.type);
 | |
| 
 | |
|                 const base = item.base || this.any();
 | |
|                 const schema = Extend.type(base, item);
 | |
| 
 | |
|                 joi._types.add(item.type);
 | |
|                 joi[item.type] = function (...args) {
 | |
| 
 | |
|                     return internals.generate(this, schema, args);
 | |
|                 };
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return joi;
 | |
|     },
 | |
| 
 | |
|     isError: Errors.ValidationError.isError,
 | |
|     isExpression: Template.isTemplate,
 | |
|     isRef: Ref.isRef,
 | |
|     isSchema: Common.isSchema,
 | |
| 
 | |
|     in(...args) {
 | |
| 
 | |
|         return Ref.in(...args);
 | |
|     },
 | |
| 
 | |
|     override: Common.symbols.override,
 | |
| 
 | |
|     ref(...args) {
 | |
| 
 | |
|         return Ref.create(...args);
 | |
|     },
 | |
| 
 | |
|     types() {
 | |
| 
 | |
|         const types = {};
 | |
|         for (const type of this._types) {
 | |
|             types[type] = this[type]();
 | |
|         }
 | |
| 
 | |
|         for (const target in internals.aliases) {
 | |
|             types[target] = this[target]();
 | |
|         }
 | |
| 
 | |
|         return types;
 | |
|     }
 | |
| };
 | |
| 
 | |
| 
 | |
| // Helpers
 | |
| 
 | |
| internals.assert = function (value, schema, annotate, args /* [message], [options] */) {
 | |
| 
 | |
|     const message = args[0] instanceof Error || typeof args[0] === 'string' ? args[0] : null;
 | |
|     const options = message !== null ? args[1] : args[0];
 | |
|     const result = schema.validate(value, Common.preferences({ errors: { stack: true } }, options || {}));
 | |
| 
 | |
|     let error = result.error;
 | |
|     if (!error) {
 | |
|         return result.value;
 | |
|     }
 | |
| 
 | |
|     if (message instanceof Error) {
 | |
|         throw message;
 | |
|     }
 | |
| 
 | |
|     const display = annotate && typeof error.annotate === 'function' ? error.annotate() : error.message;
 | |
| 
 | |
|     if (error instanceof Errors.ValidationError === false) {
 | |
|         error = Clone(error);
 | |
|     }
 | |
| 
 | |
|     error.message = message ? `${message} ${display}` : display;
 | |
|     throw error;
 | |
| };
 | |
| 
 | |
| 
 | |
| internals.generate = function (root, schema, args) {
 | |
| 
 | |
|     Assert(root, 'Must be invoked on a Joi instance.');
 | |
| 
 | |
|     schema.$_root = root;
 | |
| 
 | |
|     if (!schema._definition.args ||
 | |
|         !args.length) {
 | |
| 
 | |
|         return schema;
 | |
|     }
 | |
| 
 | |
|     return schema._definition.args(schema, ...args);
 | |
| };
 | |
| 
 | |
| 
 | |
| internals.expandExtension = function (extension, joi) {
 | |
| 
 | |
|     if (typeof extension.type === 'string') {
 | |
|         return [extension];
 | |
|     }
 | |
| 
 | |
|     const extended = [];
 | |
|     for (const type of joi._types) {
 | |
|         if (extension.type.test(type)) {
 | |
|             const item = Object.assign({}, extension);
 | |
|             item.type = type;
 | |
|             item.base = joi[type]();
 | |
|             extended.push(item);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return extended;
 | |
| };
 | |
| 
 | |
| 
 | |
| module.exports = internals.root();
 |