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