Code Monkey home page Code Monkey logo

enjoi's People

Contributors

adamschnaare avatar bardzusny avatar dimichgh avatar djmax avatar joescho avatar jojow avatar justinmchase avatar maldimirov avatar morgs32 avatar nava2 avatar nevezide avatar nfstein avatar nlundquist avatar normpng avatar pibi avatar punclev avatar sprootshift avatar tlivings avatar vh avatar yosheeck avatar yujunlong2000 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

enjoi's Issues

Proposal for refactor

While trying to better understand JSON Schema I ended up with this Q/A thread json-schema-org/json-schema-spec#733 which explained a lot in how JSON Schema validation should work.
Taking that into consideration I think enjoi is due for a major refactor. At the moment enjoy validates against e.g. required and properties only if "type": "object". And all validation keywords should be independently taken into consideration and validated against.

I am willing to work on this if you would consider a later PR. Also since it will be a major refactor would you consider me changing the testing library with jest. I've been using it for some time now and it's proven very flexible and versatile.

Can't set a description correctly

My bad if I'm missing something, but I used code similar to the example and my description is not being set:

Enjoi({
  type: 'integer',
  minimum: 0,
  default: 1,
  description: 'a query param',
})

The above gives me something like this when logged out:

{ isJoi: true,
  _type: 'number',
  _settings: null,
  _valids: { _set: [] },
  _invalids: { _set: [ Infinity, -Infinity ] },
  _tests: 
   [ { func: [Function],
       name: 'integer',
       arg: undefined,
       options: undefined },
     { func: [Function], name: 'min', arg: 0, options: undefined } ],
  _refs: [],
  _flags: {},
  _description: null,
  _unit: null,
  _notes: [],
  _tags: [],
  _examples: [],
  _meta: [],
  _inner: {} }

But if I use Joi directly, I can see the description:

Joi.number().integer().min(0).default(1).description('a query param');
{ isJoi: true,
  _type: 'number',
  _settings: null,
  _valids: Set { _set: [] },
  _invalids: Set { _set: [ Infinity, -Infinity ] },
  _tests: 
   [ { func: [Function],
       name: 'integer',
       arg: undefined,
       options: undefined },
     { func: [Function], name: 'min', arg: 0, options: undefined } ],
  _refs: [],
  _flags: { default: 1 },
  _description: 'a query param',
  _unit: null,
  _notes: [],
  _tags: [],
  _examples: [],
  _meta: [],
  _inner: {} }

Am I perhaps doing something wrong, or might this be a bug?

Thank you!

[Feature] Yup support

It would be extremely nice to have this library to support parsing to Yup schema as well. I've a case where I use Joi in the backend and I want to share schemas with frontend. Right now I am using Joi on both sides but Yup is much smaller and works really well with Formik. If I could describe schemas in JSON format and then parse it separately on backend and frontend It would be extremely helpful.

joi and hoek deprecated, moved to @hapi/joi @hapi/hoek

joi and hoek have been deprecated appear to have migrated to @hapi/joi and @hapi/hoek. Didn't see anything about this on the board so I'm posting it here. Did a quick swap out of the import statements and ran npm test which came back clean. Let me know if this has been addressed or you don't want to support the latest joi and hoek. Our security scanning software flagged hoek as having known vulnerabilities already, I'm not sure how valid that is, the software is not great, but we have to move away from enjoi if this isn't changed regardless. Have a nice day. PR here: #75

https://www.npmjs.com/package/joi
https://www.npmjs.com/package/hoek

ps I opened an issue for this earlier and it seems to have dissappeared so there might be a duplicate floating around

Other way around: Joi to JSON

Hi,

Is there a way to transform a Joi object into a JSON-schema? I would like to be able to save a Joi instance into a Database and retrieve it afterwards.

If it is not possible, can you point me towards a library that does it?

Cheers,
lapwat

Basic example is not working

The example from the README is throwing an error. It is saying message: '"name" is required',.

const Joi = require('joi');
const Enjoi = require('enjoi');

const schema = Enjoi.schema({
    type: 'object',
    properties: {
        firstName: {
            description: 'First name.',
            type: 'string'
        },
        lastName: {
            description: 'Last name.',
            type: 'string'
        },
        age: {
            description: 'Age in years',
            type: 'integer',
            minimum: 1
        }
    },
    'required': ['firstName', 'lastName']
});

const { error, value } = schema.validate({firstName: 'John', lastName: 'Doe', age: 45});

Logs

/tmp/node_modules/@hapi/joi/lib/index.js:183
                throw error;
                ^

Error [ValidationError]: {
  "type": "object",
  "base": {
    "isJoi": true,
    "_type": "object",
    "_settings": null,
    "_valids": {
      "_set": {},
      "_hasRef": false
    },
    "_invalids": {
      "_set": {},
      "_hasRef": false
    },
    "_tests": [],
    "_refs": [],
    "_flags": {},
    "_description": null,
    "_unit": null,
    "_notes": [],
    "_tags": [],
    "_examples": [],
    "_meta": [],
    "_inner": {
      "children": null,
      "renames": [],
      "dependencies": [],
      "patterns": []
    },
    "_currentJoi": {
      "isJoi": true,
      "_currentJoi": "[Circular ~.base._currentJoi]",
      "_type": "any",
      "_settings": null,
      "_valids": {
        "_set": {},
        "_hasRef": false
      },
      "_invalids": {
        "_set": {},
        "_hasRef": false
      },
      "_tests": [],
      "_refs": [],
      "_flags": {},
      "_description": null,
      "_unit": null,
      "_notes": [],
      "_tags": [],
      "_examples": [],
      "_meta": [],
      "_inner": {},
      "_binds": {},
      "any": function (...args) {\n\n        Hoek.assert(args.length === 0, 'Joi.any() does not allow arguments.');\n\n        return internals.callWithDefaults.call(this, any, args);\n    },
      "alt": function (...args) {\n\n        return internals.callWithDefaults.call(this, internals.alternatives, args);\n    },
      "alternatives": function (...args) {\n\n        return internals.callWithDefaults.call(this, internals.alternatives, args);\n    },
      "array": function (...args) {\n\n        Hoek.assert(args.length === 0, 'Joi.array() does not allow arguments.');\n\n        return internals.callWithDefaults.call(this, internals.array, args);\n    },
      "bool": function (...args) {\n\n        Hoek.assert(args.length === 0, 'Joi.boolean() does not allow arguments.');\n\n        return internals.callWithDefaults.call(this, internals.boolean, args);\n    },
      "boolean": function (...args) {\n\n        Hoek.assert(args.length === 0, 'Joi.boolean() does not allow arguments.');\n\n        return internals.callWithDefaults.call(this, internals.boolean, args);\n    },
      "binary": function (...args) {\n\n        Hoek.assert(args.length === 0, 'Joi.binary() does not allow arguments.');\n\n        return internals.callWithDefaults.call(this, internals.binary, args);\n    },
      "date": function (...args) {\n\n        Hoek.assert(args.length === 0, 'Joi.date() does not allow arguments.');\n\n        return internals.callWithDefaults.call(this, internals.date, args);\n    },
      "func": function (...args) {\n\n        Hoek.assert(args.length === 0, 'Joi.func() does not allow arguments.');\n\n        return internals.callWithDefaults.call(this, internals.func, args);\n    },
      "number": function (...args) {\n\n        Hoek.assert(args.length === 0, 'Joi.number() does not allow arguments.');\n\n        return internals.callWithDefaults.call(this, internals.number, args);\n    },
      "object": function (...args) {\n\n        return internals.callWithDefaults.call(this, internals.object, args);\n    },
      "string": function (...args) {\n\n        Hoek.assert(args.length === 0, 'Joi.string() does not allow arguments.');\n\n        return internals.callWithDefaults.call(this, internals.string, args);\n    },
      "symbol": function (...args) {\n\n        Hoek.assert(args.length === 0, 'Joi.symbol() does not allow arguments.');\n\n        return internals.callWithDefaults.call(this, internals.symbol, args);\n    },
      "ref": function (...args) {\n\n        return Ref.create(...args);\n    },
      "isRef": function (ref) {\n\n        return Ref.isRef(ref);\n    },
      "validate": function (value, ...args /*, [schema], [options], callback */) {\n\n        const last = args[args.length - 1];\n        const callback = typeof last === 'function' ? last : null;\n\n        const count = args.length - (callback ? 1 : 0);\n        if (count === 0) {\n            return any.validate(value, callback);\n        }\n\n        const options = count === 2 ? args[1] : undefined;\n        const schema = this.compile(args[0]);\n\n        return schema._validateWithOptions(value, options, callback);\n    },
      "describe": function (...args) {\n\n        const schema = args.length ? this.compile(args[0]) : any;\n        return schema.describe();\n    },
      "compile": function (schema) {\n\n        try {\n            return Cast.schema(this, schema);\n        }\n        catch (err) {\n            if (err.hasOwnProperty('path')) {\n                err.message = err.message + '(' + err.path + ')';\n            }\n\n            throw err;\n        }\n    },
      "assert": function (value, schema, message) {\n\n        this.attempt(value, schema, message);\n    },
      "attempt": function (value, schema, message) {\n\n        const result = this.validate(value, schema);\n        const error = result.error;\n        if (error) {\n            if (!message) {\n                if (typeof error.annotate === 'function') {\n                    error.message = error.annotate();\n                }\n\n                throw error;\n            }\n\n            if (!(message instanceof Error)) {\n                if (typeof error.annotate === 'function') {\n                    error.message = `${message} ${error.annotate()}`;\n                }\n\n                throw error;\n            }\n\n            throw message;\n        }\n\n        return result.value;\n    },
      "reach": function (schema, path) {\n\n        Hoek.assert(schema && schema instanceof Any, 'you must provide a joi schema');\n        Hoek.assert(Array.isArray(path) || typeof path === 'string', 'path must be a string or an array of strings');\n\n        const reach = (sourceSchema, schemaPath) => {\n\n            if (!schemaPath.length) {\n                return sourceSchema;\n            }\n\n            const children = sourceSchema._inner.children;\n            if (!children) {\n                return;\n            }\n\n            const key = schemaPath.shift();\n            for (let i = 0; i < children.length; ++i) {\n                const child = children[i];\n                if (child.key === key) {\n                    return reach(child.schema, schemaPath);\n                }\n            }\n        };\n\n        const schemaPath = typeof path === 'string' ? (path ? path.split('.') : []) : path.slice();\n\n        return reach(schema, schemaPath);\n    },
      "lazy": function (...args) {\n\n        return internals.callWithDefaults.call(this, Lazy, args);\n    },
      "defaults": function (fn) {\n\n        Hoek.assert(typeof fn === 'function', 'Defaults must be a function');\n\n        let joi = Object.create(this.any());\n        joi = fn(joi);\n\n        Hoek.assert(joi && joi instanceof this.constructor, 'defaults() must return a schema');\n\n        Object.assign(joi, this, joi.clone()); // Re-add the types from `this` but also keep the settings from joi's potential new defaults\n\n        joi._defaults = (schema) => {\n\n            if (this._defaults) {\n                schema = this._defaults(schema);\n                Hoek.assert(schema instanceof this.constructor, 'defaults() must return a schema');\n            }\n\n            schema = fn(schema);\n            Hoek.assert(schema instanceof this.constructor, 'defaults() must return a schema');\n            return schema;\n        };\n\n        return joi;\n    },
      "bind": function () {\n\n        const joi = Object.create(this);\n\n        joi._binds.forEach((bind) => {\n\n            joi[bind] = joi[bind].bind(joi);\n        });\n\n        return joi;\n    },
      "extend": function (...args) {\n\n        const extensions = Hoek.flatten(args);\n        Hoek.assert(extensions.length > 0, 'You need to provide at least one extension');\n\n        this.assert(extensions, root.extensionsSchema);\n\n        const joi = Object.create(this.any());\n        Object.assign(joi, this);\n        joi._currentJoi = joi;\n        joi._binds = new Set(joi._binds);\n\n        for (let i = 0; i < extensions.length; ++i) {\n            let extension = extensions[i];\n\n            if (typeof extension === 'function') {\n                extension = extension(joi);\n            }\n\n            this.assert(extension, root.extensionSchema);\n\n            const base = (extension.base || this.any()).clone(); // Cloning because we're going to override language afterwards\n            const ctor = base.constructor;\n            const type = class extends ctor { // eslint-disable-line no-loop-func\n\n                constructor() {\n\n                    super();\n                    if (extension.base) {\n                        Object.assign(this, base);\n                    }\n\n                    this._type = extension.name;\n                }\n\n            };\n\n            if (extension.language) {\n                const lang = {\n                    [extension.name]: extension.language\n                };\n                type.prototype._language = Hoek.applyToDefaults(type.prototype._language || (base._settings && base._settings.language) || {}, lang);\n            }\n\n\n            if (extension.coerce) {\n                type.prototype._coerce = function (value, state, options) {\n\n                    if (ctor.prototype._coerce) {\n                        const baseRet = ctor.prototype._coerce.call(this, value, state, options);\n\n                        if (baseRet.errors) {\n                            return baseRet;\n                        }\n\n                        value = baseRet.value;\n                    }\n\n                    const ret = extension.coerce.call(this, value, state, options);\n                    if (ret instanceof Errors.Err) {\n                        return { value, errors: ret };\n                    }\n\n                    return { value: ret };\n                };\n            }\n\n            if (extension.pre) {\n                type.prototype._base = function (value, state, options) {\n\n                    if (ctor.prototype._base) {\n                        const baseRet = ctor.prototype._base.call(this, value, state, options);\n\n                        if (baseRet.errors) {\n                            return baseRet;\n                        }\n\n                        value = baseRet.value;\n                    }\n\n                    const ret = extension.pre.call(this, value, state, options);\n                    if (ret instanceof Errors.Err) {\n                        return { value, errors: ret };\n                    }\n\n                    return { value: ret };\n                };\n            }\n\n            if (extension.rules) {\n                for (let j = 0; j < extension.rules.length; ++j) {\n                    const rule = extension.rules[j];\n                    const ruleArgs = rule.params ?\n                        (rule.params instanceof Any ? rule.params._inner.children.map((k) => k.key) : Object.keys(rule.params)) :\n                        [];\n                    const validateArgs = rule.params ? Cast.schema(this, rule.params) : null;\n\n                    type.prototype[rule.name] = function (...rArgs) { // eslint-disable-line no-loop-func\n\n                        if (rArgs.length > ruleArgs.length) {\n                            throw new Error('Unexpected number of arguments');\n                        }\n\n                        let hasRef = false;\n                        let arg = {};\n\n                        for (let k = 0; k < ruleArgs.length; ++k) {\n                            arg[ruleArgs[k]] = rArgs[k];\n                            if (!hasRef && Ref.isRef(rArgs[k])) {\n                                hasRef = true;\n                            }\n                        }\n\n                        if (validateArgs) {\n                            arg = joi.attempt(arg, validateArgs);\n                        }\n\n                        let schema;\n                        if (rule.validate && !rule.setup) {\n                            const validate = function (value, state, options) {\n\n                                return rule.validate.call(this, arg, value, state, options);\n                            };\n\n                            schema = this._test(rule.name, arg, validate, {\n                                description: rule.description,\n                                hasRef\n                            });\n                        }\n                        else {\n                            schema = this.clone();\n                        }\n\n                        if (rule.setup) {\n                            const newSchema = rule.setup.call(schema, arg);\n                            if (newSchema !== undefined) {\n                                Hoek.assert(newSchema instanceof Any, `Setup of extension Joi.${this._type}().${rule.name}() must return undefined or a Joi object`);\n                                schema = newSchema;\n                            }\n\n                            if (rule.validate) {\n                                const validate = function (value, state, options) {\n\n                                    return rule.validate.call(this, arg, value, state, options);\n                                };\n\n                                schema = schema._test(rule.name, arg, validate, {\n                                    description: rule.description,\n                                    hasRef\n                                });\n                            }\n                        }\n\n                        return schema;\n                    };\n                }\n            }\n\n            if (extension.describe) {\n                type.prototype.describe = function () {\n\n                    const description = ctor.prototype.describe.call(this);\n                    return extension.describe.call(this, description);\n                };\n            }\n\n            const instance = new type();\n            joi[extension.name] = function (...extArgs) {\n\n                return internals.callWithDefaults.call(this, instance, extArgs);\n            };\n\n            joi._binds.add(extension.name);\n        }\n\n        return joi;\n    },
      "extensionSchema": {
        "isJoi": true,
        "_currentJoi": "[Circular ~.base._currentJoi]",
        "_type": "object",
        "_settings": {
          "convert": false
        },
        "_valids": {
          "_set": {},
          "_hasRef": false
        },
        "_invalids": {
          "_set": {},
          "_hasRef": false
        },
        "_tests": [],
        "_refs": [],
        "_flags": {},
        "_description": null,
        "_unit": null,
        "_notes": [],
        "_tags": [],
        "_examples": [],
        "_meta": [],
        "_inner": {
          "children": [
            {
              "key": "base",
              "schema": {
                "isJoi": true,
                "_currentJoi": "[Circular ~.base._currentJoi]",
                "_type": "object",
                "_settings": null,
                "_valids": {
                  "_set": {},
                  "_hasRef": false
                },
                "_invalids": {
                  "_set": {},
                  "_hasRef": false
                },
                "_tests": [
                  {
                    "func": function (value, state, options) {\n\n            if (value instanceof constructor) {\n                return value;\n            }\n\n            return this.createError('object.type', { type: typeData.name, value }, state, options);\n        },
                    "name": "type",
                    "arg": {
                      "name": "Joi object",
                      "ctor": "[class {\n\n    constructor() {\n\n        this.isJoi = true;\n        this._type = 'any';\n        this._settings = null;\n        this._valids = new internals.Set();\n        this._invalids = new internals.Set();\n        this._tests = [];\n        this._refs = [];\n        this._flags = {\n            /*\n             presence: 'optional',                   // optional, required, forbidden, ignore\n             allowOnly: false,\n             allowUnknown: undefined,\n             default: undefined,\n             forbidden: false,\n             encoding: undefined,\n             insensitive: false,\n             trim: false,\n             normalize: undefined,                   // NFC, NFD, NFKC, NFKD\n             case: undefined,                        // upper, lower\n             empty: undefined,\n             func: false,\n             raw: false\n             */\n        };\n\n        this._description = null;\n        this._unit = null;\n        this._notes = [];\n        this._tags = [];\n        this._examples = [];\n        this._meta = [];\n\n        this._inner = {};                           // Hash of arrays of immutable objects\n    }\n\n    _init() {\n\n        return this;\n    }\n\n    get schemaType() {\n\n        return this._type;\n    }\n\n    createError(type, context, state, options, flags = this._flags) {\n\n        return Errors.create(type, context, state, options, flags);\n    }\n\n    createOverrideError(type, context, state, options, message, template) {\n\n        return Errors.create(type, context, state, options, this._flags, message, template);\n    }\n\n    checkOptions(options) {\n\n        Schemas = Schemas || require('../../schemas');\n\n        const result = Schemas.options.validate(options);\n\n        if (result.error) {\n            throw new Error(result.error.details[0].message);\n        }\n    }\n\n    clone() {\n\n        const obj = Object.create(Object.getPrototypeOf(this));\n\n        obj.isJoi = true;\n        obj._currentJoi = this._currentJoi;\n        obj._type = this._type;\n        obj._settings = this._settings;\n        obj._baseType = this._baseType;\n        obj._valids = this._valids.slice();\n        obj._invalids = this._invalids.slice();\n        obj._tests = this._tests.slice();\n        obj._refs = this._refs.slice();\n        obj._flags = Hoek.clone(this._flags);\n\n        obj._description = this._description;\n        obj._unit = this._unit;\n        obj._notes = this._notes.slice();\n        obj._tags = this._tags.slice();\n        obj._examples = this._examples.slice();\n        obj._meta = this._meta.slice();\n\n        obj._inner = {};\n        const inners = Object.keys(this._inner);\n        for (let i = 0; i < inners.length; ++i) {\n            const key = inners[i];\n            obj._inner[key] = this._inner[key] ? this._inner[key].slice() : null;\n        }\n\n        return obj;\n    }\n\n    concat(schema) {\n\n        Hoek.assert(schema instanceof internals.Any, 'Invalid schema object');\n        Hoek.assert(this._type === 'any' || schema._type === 'any' || schema._type === this._type, 'Cannot merge type', this._type, 'with another type:', schema._type);\n\n        let obj = this.clone();\n\n        if (this._type === 'any' && schema._type !== 'any') {\n\n            // Reset values as if we were \"this\"\n            const tmpObj = schema.clone();\n            const keysToRestore = ['_settings', '_valids', '_invalids', '_tests', '_refs', '_flags', '_description', '_unit',\n                '_notes', '_tags', '_examples', '_meta', '_inner'];\n\n            for (let i = 0; i < keysToRestore.length; ++i) {\n                tmpObj[keysToRestore[i]] = obj[keysToRestore[i]];\n            }\n\n            obj = tmpObj;\n        }\n\n        obj._settings = obj._settings ? Settings.concat(obj._settings, schema._settings) : schema._settings;\n        obj._valids.merge(schema._valids, schema._invalids);\n        obj._invalids.merge(schema._invalids, schema._valids);\n        obj._tests.push(...schema._tests);\n        obj._refs.push(...schema._refs);\n        if (obj._flags.empty && schema._flags.empty) {\n            obj._flags.empty = obj._flags.empty.concat(schema._flags.empty);\n            const flags = Object.assign({}, schema._flags);\n            delete flags.empty;\n            Hoek.merge(obj._flags, flags);\n        }\n        else if (schema._flags.empty) {\n            obj._flags.empty = schema._flags.empty;\n            const flags = Object.assign({}, schema._flags);\n            delete flags.empty;\n            Hoek.merge(obj._flags, flags);\n        }\n        else {\n            Hoek.merge(obj._flags, schema._flags);\n        }\n\n        obj._description = schema._description || obj._description;\n        obj._unit = schema._unit || obj._unit;\n        obj._notes.push(...schema._notes);\n        obj._tags.push(...schema._tags);\n        obj._examples.push(...schema._examples);\n        obj._meta.push(...schema._meta);\n\n        const inners = Object.keys(schema._inner);\n        const isObject = obj._type === 'object';\n        for (let i = 0; i < inners.length; ++i) {\n            const key = inners[i];\n            const source = schema._inner[key];\n            if (source) {\n                const target = obj._inner[key];\n                if (target) {\n                    if (isObject && key === 'children') {\n                        const keys = {};\n\n                        for (let j = 0; j < target.length; ++j) {\n                            keys[target[j].key] = j;\n                        }\n\n                        for (let j = 0; j < source.length; ++j) {\n                            const sourceKey = source[j].key;\n                            if (keys[sourceKey] >= 0) {\n                                target[keys[sourceKey]] = {\n                                    key: sourceKey,\n                                    schema: target[keys[sourceKey]].schema.concat(source[j].schema)\n                                };\n                            }\n                            else {\n                                target.push(source[j]);\n                            }\n                        }\n                    }\n                    else {\n                        obj._inner[key] = obj._inner[key].concat(source);\n                    }\n                }\n                else {\n                    obj._inner[key] = source.slice();\n                }\n            }\n        }\n\n        return obj;\n    }\n\n    _test(name, arg, func, options) {\n\n        const obj = this.clone();\n        obj._tests.push({ func, name, arg, options });\n        return obj;\n    }\n\n    _testUnique(name, arg, func, options) {\n\n        const obj = this.clone();\n        obj._tests = obj._tests.filter((test) => test.name !== name);\n        obj._tests.push({ func, name, arg, options });\n        return obj;\n    }\n\n    options(options) {\n\n        Hoek.assert(!options.context, 'Cannot override context');\n        this.checkOptions(options);\n\n        const obj = this.clone();\n        obj._settings = Settings.concat(obj._settings, options);\n        return obj;\n    }\n\n    strict(isStrict) {\n\n        const obj = this.clone();\n\n        const convert = isStrict === undefined ? false : !isStrict;\n        obj._settings = Settings.concat(obj._settings, { convert });\n        return obj;\n    }\n\n    raw(isRaw) {\n\n        const value = isRaw === undefined ? true : isRaw;\n\n        if (this._flags.raw === value) {\n            return this;\n        }\n\n        const obj = this.clone();\n        obj._flags.raw = value;\n        return obj;\n    }\n\n    error(err, options = { self: false }) {\n\n        Hoek.assert(err && (err instanceof Error || typeof err === 'function'), 'Must provide a valid Error object or a function');\n\n        const unknownKeys = Object.keys(options).filter((k) => !['self'].includes(k));\n        Hoek.assert(unknownKeys.length === 0, `Options ${unknownKeys} are unknown`);\n\n        const obj = this.clone();\n        obj._flags.error = err;\n\n        if (options.self) {\n            obj._flags.selfError = true;\n        }\n\n        return obj;\n    }\n\n    allow(...values) {\n\n        const obj = this.clone();\n        values = Hoek.flatten(values);\n        for (let i = 0; i < values.length; ++i) {\n            const value = values[i];\n\n            Hoek.assert(value !== undefined, 'Cannot call allow/valid/invalid with undefined');\n            obj._invalids.remove(value);\n            obj._valids.add(value, obj._refs);\n        }\n\n        return obj;\n    }\n\n    valid(...values) {\n\n        const obj = this.allow(...values);\n        obj._flags.allowOnly = true;\n        return obj;\n    }\n\n    invalid(...values) {\n\n        const obj = this.clone();\n        values = Hoek.flatten(values);\n        for (let i = 0; i < values.length; ++i) {\n            const value = values[i];\n\n            Hoek.assert(value !== undefined, 'Cannot call allow/valid/invalid with undefined');\n            obj._valids.remove(value);\n            obj._invalids.add(value, obj._refs);\n        }\n\n        return obj;\n    }\n\n    required() {\n\n        if (this._flags.presence === 'required') {\n            return this;\n        }\n\n        const obj = this.clone();\n        obj._flags.presence = 'required';\n        return obj;\n    }\n\n    optional() {\n\n        if (this._flags.presence === 'optional') {\n            return this;\n        }\n\n        const obj = this.clone();\n        obj._flags.presence = 'optional';\n        return obj;\n    }\n\n\n    forbidden() {\n\n        if (this._flags.presence === 'forbidden') {\n            return this;\n        }\n\n        const obj = this.clone();\n        obj._flags.presence = 'forbidden';\n        return obj;\n    }\n\n\n    strip() {\n\n        if (this._flags.strip) {\n            return this;\n        }\n\n        const obj = this.clone();\n        obj._flags.strip = true;\n        return obj;\n    }\n\n    applyFunctionToChildren(children, fn, args = [], root) {\n\n        children = [].concat(children);\n\n        if (children.length !== 1 || children[0] !== '') {\n            root = root ? (root + '.') : '';\n\n            const extraChildren = (children[0] === '' ? children.slice(1) : children).map((child) => {\n\n                return root + child;\n            });\n\n            throw new Error('unknown key(s) ' + extraChildren.join(', '));\n        }\n\n        return this[fn](...args);\n    }\n\n    default(value, description) {\n\n        if (typeof value === 'function' &&\n            !Ref.isRef(value)) {\n\n            if (!value.description &&\n                description) {\n\n                value.description = description;\n            }\n\n            if (!this._flags.func) {\n                Hoek.assert(typeof value.description === 'string' && value.description.length > 0, 'description must be provided when default value is a function');\n            }\n        }\n\n        const obj = this.clone();\n        obj._flags.default = value;\n        Ref.push(obj._refs, value);\n        return obj;\n    }\n\n    empty(schema) {\n\n        const obj = this.clone();\n        if (schema === undefined) {\n            delete obj._flags.empty;\n        }\n        else {\n            obj._flags.empty = Cast.schema(this._currentJoi, schema);\n        }\n\n        return obj;\n    }\n\n    when(condition, options) {\n\n        Hoek.assert(options && typeof options === 'object', 'Invalid options');\n        Hoek.assert(options.then !== undefined || options.otherwise !== undefined, 'options must have at least one of \"then\" or \"otherwise\"');\n\n        const then = options.hasOwnProperty('then') ? this.concat(Cast.schema(this._currentJoi, options.then)) : undefined;\n        const otherwise = options.hasOwnProperty('otherwise') ? this.concat(Cast.schema(this._currentJoi, options.otherwise)) : undefined;\n\n        Alternatives = Alternatives || require('../alternatives');\n\n        const alternativeOptions = { then, otherwise };\n        if (Object.prototype.hasOwnProperty.call(options, 'is')) {\n            alternativeOptions.is = options.is;\n        }\n\n        const obj = Alternatives.when(condition, alternativeOptions);\n        obj._flags.presence = 'ignore';\n        obj._baseType = this;\n\n        return obj;\n    }\n\n    description(desc) {\n\n        Hoek.assert(desc && typeof desc === 'string', 'Description must be a non-empty string');\n\n        const obj = this.clone();\n        obj._description = desc;\n        return obj;\n    }\n\n    notes(notes) {\n\n        Hoek.assert(notes && (typeof notes === 'string' || Array.isArray(notes)), 'Notes must be a non-empty string or array');\n\n        const obj = this.clone();\n        obj._notes = obj._notes.concat(notes);\n        return obj;\n    }\n\n    tags(tags) {\n\n        Hoek.assert(tags && (typeof tags === 'string' || Array.isArray(tags)), 'Tags must be a non-empty string or array');\n\n        const obj = this.clone();\n        obj._tags = obj._tags.concat(tags);\n        return obj;\n    }\n\n    meta(meta) {\n\n        Hoek.assert(meta !== undefined, 'Meta cannot be undefined');\n\n        const obj = this.clone();\n        obj._meta = obj._meta.concat(meta);\n        return obj;\n    }\n\n    example(...examples) {\n\n        Hoek.assert(examples.length > 0, 'Missing examples');\n\n        const processed = [];\n        for (let i = 0; i < examples.length; ++i) {\n            const example = [].concat(examples[i]);\n            Hoek.assert(example.length <= 2, `Bad example format at index ${i}`);\n\n            const value = example[0];\n            let options = example[1];\n            if (options !== undefined) {\n                Hoek.assert(options && typeof options === 'object', `Options for example at index ${i} must be an object`);\n                const unknownOptions = Object.keys(options).filter((option) => !['parent', 'context'].includes(option));\n                Hoek.assert(unknownOptions.length === 0, `Unknown example options ${unknownOptions} at index ${i}`);\n            }\n            else {\n                options = {};\n            }\n\n            const localState = new State('', [], options.parent || null);\n            const result = this._validate(value, localState, Settings.concat(internals.defaults, options.context ? { context: options.context } : null));\n            Hoek.assert(!result.errors, `Bad example at index ${i}:`, result.errors && Errors.process(result.errors, value));\n\n            const ex = { value };\n            if (Object.keys(options).length) {\n                ex.options = options;\n            }\n\n            processed.push(ex);\n        }\n\n        const obj = this.clone();\n        obj._examples = processed;\n        return obj;\n    }\n\n    unit(name) {\n\n        Hoek.assert(name && typeof name === 'string', 'Unit name must be a non-empty string');\n\n        const obj = this.clone();\n        obj._unit = name;\n        return obj;\n    }\n\n    _prepareEmptyValue(value) {\n\n        if (typeof value === 'string' && this._flags.trim) {\n            return value.trim();\n        }\n\n        return value;\n    }\n\n    _validate(value, state, options, reference) {\n\n        const originalValue = value;\n\n        // Setup state and settings\n\n        state = state || new State('', [], null, reference);\n\n        if (this._settings) {\n            const isDefaultOptions = options === internals.defaults;\n            if (isDefaultOptions && this._settings[Symbols.settingsCache]) {\n                options = this._settings[Symbols.settingsCache];\n            }\n            else {\n                options = Settings.concat(this._language ? Settings.concat({ language: this._language }, options) : options, this._settings);\n                if (isDefaultOptions) {\n                    this._settings[Symbols.settingsCache] = options;\n                }\n            }\n        }\n        else if (this._language) {\n            options = Settings.concat({ language: this._language }, options);\n        }\n\n        let errors = [];\n\n        if (this._coerce) {\n            const coerced = this._coerce(value, state, options);\n            if (coerced.errors) {\n                value = coerced.value;\n                errors = errors.concat(coerced.errors);\n                return this._finalizeValue(value, originalValue, errors, state, options);                            // Coerced error always aborts early\n            }\n\n            value = coerced.value;\n        }\n\n        if (this._flags.empty && !this._flags.empty._validate(this._prepareEmptyValue(value), null, internals.defaults).errors) {\n            value = undefined;\n        }\n\n        // Check presence requirements\n\n        const presence = this._flags.presence || options.presence;\n        if (presence === 'optional') {\n            if (value === undefined) {\n                const isDeepDefault = this._flags.hasOwnProperty('default') && this._flags.default === undefined;\n                if (isDeepDefault && this._type === 'object') {\n                    value = {};\n                }\n                else {\n                    return this._finalizeValue(value, originalValue, errors, state, options);\n                }\n            }\n        }\n        else if (presence === 'required' &&\n            value === undefined) {\n\n            errors.push(this.createError('any.required', null, state, options));\n            return this._finalizeValue(value, originalValue, errors, state, options);\n        }\n        else if (presence === 'forbidden') {\n            if (value === undefined) {\n                return this._finalizeValue(value, originalValue, errors, state, options);\n            }\n\n            errors.push(this.createError('any.unknown', null, state, options));\n            return this._finalizeValue(value, originalValue, errors, state, options);\n        }\n\n        // Check allowed and denied values using the original value\n\n        let match = this._valids.get(value, state, options, this._flags.insensitive);\n        if (match) {\n            if (options.convert) {\n                value = match.value;\n            }\n\n            return this._finalizeValue(value, originalValue, errors, state, options);\n        }\n\n        if (this._invalids.has(value, state, options, this._flags.insensitive)) {\n            errors.push(this.createError(value === '' ? 'any.empty' : 'any.invalid', { value, invalids: this._invalids.values({ stripUndefined: true }) }, state, options));\n            if (options.abortEarly) {\n\n                return this._finalizeValue(value, originalValue, errors, state, options);\n            }\n        }\n\n        // Convert value and validate type\n\n        if (this._base) {\n            const base = this._base(value, state, options);\n            if (base.errors) {\n                value = base.value;\n                errors = errors.concat(base.errors);\n                return this._finalizeValue(value, originalValue, errors, state, options);                            // Base error always aborts early\n            }\n\n            if (base.value !== value) {\n                value = base.value;\n\n                // Check allowed and denied values using the converted value\n\n                match = this._valids.get(value, state, options, this._flags.insensitive);\n                if (match) {\n                    value = match.value;\n                    return this._finalizeValue(value, originalValue, errors, state, options);\n                }\n\n                if (this._invalids.has(value, state, options, this._flags.insensitive)) {\n                    errors.push(this.createError(value === '' ? 'any.empty' : 'any.invalid', { value, invalids: this._invalids.values({ stripUndefined: true }) }, state, options));\n                    if (options.abortEarly) {\n                        return this._finalizeValue(value, originalValue, errors, state, options);\n                    }\n                }\n            }\n        }\n\n        // Required values did not match\n\n        if (this._flags.allowOnly) {\n            errors.push(this.createError('any.allowOnly', { value, valids: this._valids.values({ stripUndefined: true }) }, state, options));\n            if (options.abortEarly) {\n                return this._finalizeValue(value, originalValue, errors, state, options);\n            }\n        }\n\n        // Validate tests\n\n        for (let i = 0; i < this._tests.length; ++i) {\n            const test = this._tests[i];\n            const ret = test.func.call(this, value, state, options);\n            if (ret instanceof Errors.Err) {\n                errors.push(ret);\n                if (options.abortEarly) {\n                    return this._finalizeValue(value, originalValue, errors, state, options);\n                }\n            }\n            else {\n                value = ret;\n            }\n        }\n\n        return this._finalizeValue(value, originalValue, errors, state, options);\n    }\n\n    _finalizeValue(value, originalValue, errors, state, options) {\n\n        let finalValue;\n\n        if (value !== undefined) {\n            finalValue = this._flags.raw ? originalValue : value;\n        }\n        else if (options.noDefaults) {\n            finalValue = value;\n        }\n        else if (Ref.isRef(this._flags.default)) {\n            finalValue = this._flags.default(state.parent, options);\n        }\n        else if (typeof this._flags.default === 'function' &&\n            !(this._flags.func && !this._flags.default.description)) {\n\n            let args;\n\n            if (state.parent !== null &&\n                this._flags.default.length > 0) {\n\n                args = [Hoek.clone(state.parent), options];\n            }\n\n            const defaultValue = internals._try(this._flags.default, args);\n            finalValue = defaultValue.value;\n            if (defaultValue.error) {\n                errors.push(this.createError('any.default', { error: defaultValue.error }, state, options));\n            }\n        }\n        else {\n            finalValue = Hoek.clone(this._flags.default);\n        }\n\n        if (errors.length &&\n            typeof this._flags.error === 'function' &&\n            (\n                !this._flags.selfError ||\n                errors.some((e) => state.path.length === e.path.length)\n            )\n        ) {\n            const change = this._flags.error.call(this, errors);\n\n            if (typeof change === 'string') {\n                errors = [this.createOverrideError('override', { reason: errors }, state, options, change)];\n            }\n            else {\n                errors = [].concat(change)\n                    .map((err) => {\n\n                        return err instanceof Error ?\n                            err :\n                            this.createOverrideError(err.type || 'override', err.context, state, options, err.message, err.template);\n                    });\n            }\n        }\n\n        return {\n            value: this._flags.strip ? undefined : finalValue,\n            finalValue,\n            errors: errors.length ? errors : null\n        };\n    }\n\n    _validateWithOptions(value, options, callback) {\n\n        if (options) {\n            this.checkOptions(options);\n        }\n\n        const settings = Settings.concat(internals.defaults, options);\n        const result = this._validate(value, null, settings);\n        const errors = Errors.process(result.errors, value);\n\n        if (callback) {\n            return callback(errors, result.value);\n        }\n\n        return {\n            error: errors,\n            value: result.value,\n            then(resolve, reject) {\n\n                if (errors) {\n                    return Promise.reject(errors).catch(reject);\n                }\n\n                return Promise.resolve(result.value).then(resolve);\n            },\n            catch(reject) {\n\n                if (errors) {\n                    return Promise.reject(errors).catch(reject);\n                }\n\n                return Promise.resolve(result.value);\n            }\n        };\n    }\n\n    validate(value, options, callback) {\n\n        if (typeof options === 'function') {\n            return this._validateWithOptions(value, null, options);\n        }\n\n        return this._validateWithOptions(value, options, callback);\n    }\n\n    describe() {\n\n        const description = {\n            type: this._type\n        };\n\n        const flags = Object.keys(this._flags);\n        if (flags.length) {\n            if (['empty', 'default', 'lazy', 'label'].some((flag) => this._flags.hasOwnProperty(flag))) {\n                description.flags = {};\n                for (let i = 0; i < flags.length; ++i) {\n                    const flag = flags[i];\n                    if (flag === 'empty') {\n                        description.flags[flag] = this._flags[flag].describe();\n                    }\n                    else if (flag === 'default') {\n                        if (Ref.isRef(this._flags[flag])) {\n                            description.flags[flag] = this._flags[flag].toString();\n                        }\n                        else if (typeof this._flags[flag] === 'function') {\n                            description.flags[flag] = {\n                                description: this._flags[flag].description,\n                                function   : this._flags[flag]\n                            };\n                        }\n                        else {\n                            description.flags[flag] = this._flags[flag];\n                        }\n                    }\n                    else if (flag === 'lazy' || flag === 'label') {\n                        // We don't want it in the description\n                    }\n                    else {\n                        description.flags[flag] = this._flags[flag];\n                    }\n                }\n            }\n            else {\n                description.flags = this._flags;\n            }\n        }\n\n        if (this._settings) {\n            description.options = Hoek.clone(this._settings);\n        }\n\n        if (this._baseType) {\n            description.base = this._baseType.describe();\n        }\n\n        if (this._description) {\n            description.description = this._description;\n        }\n\n        if (this._notes.length) {\n            description.notes = this._notes;\n        }\n\n        if (this._tags.length) {\n            description.tags = this._tags;\n        }\n\n        if (this._meta.length) {\n            description.meta = this._meta;\n        }\n\n        if (this._examples.length) {\n            description.examples = this._examples;\n        }\n\n        if (this._unit) {\n            description.unit = this._unit;\n        }\n\n        const valids = this._valids.values();\n        if (valids.length) {\n            description.valids = valids.map((v) => {\n\n                return Ref.isRef(v) ? v.toString() : v;\n            });\n        }\n\n        const invalids = this._invalids.values();\n        if (invalids.length) {\n            description.invalids = invalids.map((v) => {\n\n                return Ref.isRef(v) ? v.toString() : v;\n            });\n        }\n\n        description.rules = [];\n\n        for (let i = 0; i < this._tests.length; ++i) {\n            const validator = this._tests[i];\n            const item = { name: validator.name };\n\n            if (validator.arg !== void 0) {\n                item.arg = Ref.isRef(validator.arg) ? validator.arg.toString() : validator.arg;\n            }\n\n            const options = validator.options;\n            if (options) {\n                if (options.hasRef) {\n                    item.arg = {};\n                    const keys = Object.keys(validator.arg);\n                    for (let j = 0; j < keys.length; ++j) {\n                        const key = keys[j];\n                        const value = validator.arg[key];\n                        item.arg[key] = Ref.isRef(value) ? value.toString() : value;\n                    }\n                }\n\n                if (typeof options.description === 'string') {\n                    item.description = options.description;\n                }\n                else if (typeof options.description === 'function') {\n                    item.description = options.description(item.arg);\n                }\n            }\n\n            description.rules.push(item);\n        }\n\n        if (!description.rules.length) {\n            delete description.rules;\n        }\n\n        const label = this._getLabel();\n        if (label) {\n            description.label = label;\n        }\n\n        return description;\n    }\n\n    label(name) {\n\n        Hoek.assert(name && typeof name === 'string', 'Label name must be a non-empty string');\n\n        const obj = this.clone();\n        obj._flags.label = name;\n        return obj;\n    }\n\n    _getLabel(def) {\n\n        return this._flags.label || def;\n    }\n\n}]"
                    }
                  }
                ],
                "_refs": [],
                "_flags": {},
                "_description": null,
                "_unit": null,
                "_notes": [],
                "_tags": [],
                "_examples": [],
                "_meta": [],
                "_inner": {
                  "children": null,
                  "renames": [],
                  "dependencies": [],
                  "patterns": []
                }
              }
            },
            {
              "key": "name",
              "schema": {
                "isJoi": true,
                "_currentJoi": "[Circular ~.base._currentJoi]",
                "_type": "string",
                "_settings": null,
                "_valids": {
                  "_set": {},
                  "_hasRef": false
                },
                "_invalids": {
                  "_set": {},
                  "_hasRef": 0
                },
                "_tests": [],
                "_refs": [],
                "_flags": {
                  "presence": "required"
                },
                "_description": null,
                "_unit": null,
                "_notes": [],
                "_tags": [],
                "_examples": [],
                "_meta": [],
                "_inner": {}
              }
            },
            {
              "key": "coerce",
              "schema": {
                "isJoi": true,
                "_currentJoi": "[Circular ~.base._currentJoi]",
                "_type": "object",
                "_settings": null,
                "_valids": {
                  "_set": {},
                  "_hasRef": false
                },
                "_invalids": {
                  "_set": {},
                  "_hasRef": false
                },
                "_tests": [
                  {
                    "func": function (value, state, options) {\n\n            if (value.length === n) {\n                return value;\n            }\n\n            return this.createError('function.arity', { n }, state, options);\n        },
                    "name": "arity",
                    "arg": 3
                  }
                ],
                "_refs": [],
                "_flags": {
                  "func": true
                },
                "_description": null,
                "_unit": null,
                "_notes": [],
                "_tags": [],
                "_examples": [],
                "_meta": [],
                "_inner": {
                  "children": null,
                  "renames": [],
                  "dependencies": [],
                  "patterns": []
                }
              }
            },
            {
              "key": "pre",
              "schema": {
                "isJoi": true,
                "_currentJoi": "[Circular ~.base._currentJoi]",
                "_type": "object",
                "_settings": null,
                "_valids": {
                  "_set": {},
                  "_hasRef": false
                },
                "_invalids": {
                  "_set": {},
                  "_hasRef": false
                },
                "_tests": [
                  {
                    "func": function (value, state, options) {\n\n            if (value.length === n) {\n                return value;\n            }\n\n            return this.createError('function.arity', { n }, state, options);\n        },
                    "name": "arity",
                    "arg": 3
                  }
                ],
                "_refs": [],
                "_flags": {
                  "func": true
                },
                "_description": null,
                "_unit": null,
                "_notes": [],
                "_tags": [],
                "_examples": [],
                "_meta": [],
                "_inner": {
                  "children": null,
                  "renames": [],
                  "dependencies": [],
                  "patterns": []
                }
              }
            },
            {
              "key": "language",
              "schema": "[Circular ~.base]"
            },
            {
              "key": "describe",
              "schema": {
                "isJoi": true,
                "_currentJoi": "[Circular ~.base._currentJoi]",
                "_type": "object",
                "_settings": null,
                "_valids": {
                  "_set": {},
                  "_hasRef": false
                },
                "_invalids": {
                  "_set": {},
                  "_hasRef": false
                },
                "_tests": [
                  {
                    "func": function (value, state, options) {\n\n            if (value.length === n) {\n                return value;\n            }\n\n            return this.createError('function.arity', { n }, state, options);\n        },
                    "name": "arity",
                    "arg": 1
                  }
                ],
                "_refs": [],
                "_flags": {
                  "func": true
                },
                "_description": null,
                "_unit": null,
                "_notes": [],
                "_tags": [],
                "_examples": [],
                "_meta": [],
                "_inner": {
                  "children": null,
                  "renames": [],
                  "dependencies": [],
                  "patterns": []
                }
              }
            },
            {
              "key": "rules",
              "schema": {
                "isJoi": true,
                "_currentJoi": "[Circular ~.base._currentJoi]",
                "_type": "array",
                "_settings": null,
                "_valids": {
                  "_set": {},
                  "_hasRef": false
                },
                "_invalids": {
                  "_set": {},
                  "_hasRef": false
                },
                "_tests": [],
                "_refs": [],
                "_flags": {
                  "sparse": false
                },
                "_description": null,
                "_unit": null,
                "_notes": [],
                "_tags": [],
                "_examples": [],
                "_meta": [],
                "_inner": {
                  "items": [
                    {
                      "isJoi": true,
                      "_currentJoi": "[Circular ~.base._currentJoi]",
                      "_type": "object",
                      "_settings": null,
                      "_valids": {
                        "_set": {},
                        "_hasRef": false
                      },
                      "_invalids": {
                        "_set": {},
                        "_hasRef": false
                      },
                      "_tests": [],
                      "_refs": [],
                      "_flags": {},
                      "_description": null,
                      "_unit": null,
                      "_notes": [],
                      "_tags": [],
                      "_examples": [],
                      "_meta": [],
                      "_inner": {
                        "children": [
                          {
                            "key": "name",
                            "schema": {
                              "isJoi": true,
                              "_currentJoi": "[Circular ~.base._currentJoi]",
                              "_type": "string",
                              "_settings": null,
                              "_valids": {
                                "_set": {},
                                "_hasRef": false
                              },
                              "_invalids": {
                                "_set": {},
                                "_hasRef": 0
                              },
                              "_tests": [],
                              "_refs": [],
                              "_flags": {
                                "presence": "required"
                              },
                              "_description": null,
                              "_unit": null,
                              "_notes": [],
                              "_tags": [],
                              "_examples": [],
                              "_meta": [],
                              "_inner": {}
                            }
                          },
                          {
                            "key": "setup",
                            "schema": {
                              "isJoi": true,
                              "_currentJoi": "[Circular ~.base._currentJoi]",
                              "_type": "object",
                              "_settings": null,
                              "_valids": {
                                "_set": {},
                                "_hasRef": false
                              },
                              "_invalids": {
                                "_set": {},
                                "_hasRef": false
                              },
                              "_tests": [
                                {
                                  "func": function (value, state, options) {\n\n            if (value.length === n) {\n                return value;\n            }\n\n            return this.createError('function.arity', { n }, state, options);\n        },
                                  "name": "arity",
                                  "arg": 1
                                }
                              ],
                              "_refs": [],
                              "_flags": {
                                "func": true
                              },
                              "_description": null,
                              "_unit": null,
                              "_notes": [],
                              "_tags": [],
                              "_examples": [],
                              "_meta": [],
                              "_inner": {
                                "children": null,
                                "renames": [],
                                "dependencies": [],
                                "patterns": []
                              }
                            }
                          },
                          {
                            "key": "validate",
                            "schema": {
                              "isJoi": true,
                              "_currentJoi": "[Circular ~.base._currentJoi]",
                              "_type": "object",
                              "_settings": null,
                              "_valids": {
                                "_set": {},
                                "_hasRef": false
                              },
                              "_invalids": {
                                "_set": {},
                                "_hasRef": false
                              },
                              "_tests": [
                                {
                                  "func": function (value, state, options) {\n\n            if (value.length === n) {\n                return value;\n            }\n\n            return this.createError('function.arity', { n }, state, options);\n        },
                                  "name": "arity",
                                  "arg": 4
                                }
                              ],
                              "_refs": [],
                              "_flags": {
                                "func": true
                              },
                              "_description": null,
                              "_unit": null,
                              "_notes": [],
                              "_tags": [],
                              "_examples": [],
                              "_meta": [],
                              "_inner": {
                                "children": null,
                                "renames": [],
                                "dependencies": [],
                                "patterns": []
                              }
                            }
                          },
                          {
                            "key": "params",
                            "schema": {
                              "isJoi": true,
                              "_currentJoi": "[Circular ~.base._currentJoi]",
                              "_type": "alternatives",
                              "_settings": null,
                              "_valids": {
                                "_set": {},
                                "_hasRef": false
                              },
                              "_invalids": {
                                "_set": {},
                                "_hasRef": false
                              },
                              "_tests": [],
                              "_refs": [],
                              "_flags": {},
                              "_description": null,
                              "_unit": null,
                              "_notes": [],
                              "_tags": [],
                              "_examples": [],
                              "_meta": [],
                              "_inner": {
                                "matches": [
                                  {
                                    "schema": {
                                      "isJoi": true,
                                      "_currentJoi": "[Circular ~.base._currentJoi]",
                                      "_type": "object",
                                      "_settings": null,
                                      "_valids": {
                                        "_set": {},
                                        "_hasRef": false
                                      },
                                      "_invalids": {
                                        "_set": {},
                                        "_hasRef": false
                                      },
                                      "_tests": [],
                                      "_refs": [],
                                      "_flags": {},
                                      "_description": null,
                                      "_unit": null,
                                      "_notes": [],
                                      "_tags": [],
                                      "_examples": [],
                                      "_meta": [],
                                      "_inner": {
                                        "children": null,
                                        "renames": [],
                                        "dependencies": [],
                                        "patterns": [
                                          {
                                            "regex": {},
                                            "rule": {
                                              "isJoi": true,
                                              "_currentJoi": "[Circular ~.base._currentJoi]",
                                              "_type": "object",
                                              "_settings": null,
                                              "_valids": {
                                                "_set": {},
                                                "_hasRef": false
                                              },
                                              "_invalids": {
                                                "_set": {},
                                                "_hasRef": false
                                              },
                                              "_tests": [
                                                {
                                                  "func": function (value, state, options) {\n\n            if (value instanceof constructor) {\n                return value;\n            }\n\n            return this.createError('object.type', { type: typeData.name, value }, state, options);\n        },
                                                  "name": "type",
                                                  "arg": {
                                                    "name": "Joi object",
                                                    "ctor": "[class {\n\n    constructor() {\n\n        this.isJoi = true;\n        this._type = 'any';\n        this._settings = null;\n        this._valids = new internals.Set();\n        this._invalids = new internals.Set();\n        this._tests = [];\n        this._refs = [];\n        this._flags = {\n            /*\n             presence: 'optional',                   // optional, required, forbidden, ignore\n             allowOnly: false,\n             allowUnknown: undefined,\n             default: undefined,\n             forbidden: false,\n             encoding: undefined,\n             insensitive: false,\n             trim: false,\n             normalize: undefined,                   // NFC, NFD, NFKC, NFKD\n             case: undefined,                        // upper, lower\n             empty: undefined,\n             func: false,\n             raw: false\n             */\n        };\n\n        this._description = null;\n        this._unit = null;\n        this._notes = [];\n        this._tags = [];\n        this._examples = [];\n        this._meta = [];\n\n        this._inner = {};                           // Hash of arrays of immutable objects\n    }\n\n    _init() {\n\n        return this;\n    }\n\n    get schemaType() {\n\n        return this._type;\n    }\n\n    createError(type, context, state, options, flags = this._flags) {\n\n        return Errors.create(type, context, state, options, flags);\n    }\n\n    createOverrideError(type, context, state, options, message, template) {\n\n        return Errors.create(type, context, state, options, this._flags, message, template);\n    }\n\n    checkOptions(options) {\n\n        Schemas = Schemas || require('../../schemas');\n\n        const result = Schemas.options.validate(options);\n\n        if (result.error) {\n            throw new Error(result.error.details[0].message);\n        }\n    }\n\n    clone() {\n\n        const obj = Object.create(Object.getPrototypeOf(this));\n\n        obj.isJoi = true;\n        obj._currentJoi = this._currentJoi;\n        obj._type = this._type;\n        obj._settings = this._settings;\n        obj._baseType = this._baseType;\n        obj._valids = this._valids.slice();\n        obj._invalids = this._invalids.slice();\n        obj._tests = this._tests.slice();\n        obj._refs = this._refs.slice();\n        obj._flags = Hoek.clone(this._flags);\n\n        obj._description = this._description;\n        obj._unit = this._unit;\n        obj._notes = this._notes.slice();\n        obj._tags = this._tags.slice();\n        obj._examples = this._examples.slice();\n        obj._meta = this._meta.slice();\n\n        obj._inner = {};\n        const inners = Object.keys(this._inner);\n        for (let i = 0; i < inners.length; ++i) {\n            const key = inners[i];\n            obj._inner[key] = this._inner[key] ? this._inner[key].slice() : null;\n        }\n\n        return obj;\n    }\n\n    concat(schema) {\n\n        Hoek.assert(schema instanceof internals.Any, 'Invalid schema object');\n        Hoek.assert(this._type === 'any' || schema._type === 'any' || schema._type === this._type, 'Cannot merge type', this._type, 'with another type:', schema._type);\n\n        let obj = this.clone();\n\n        if (this._type === 'any' && schema._type !== 'any') {\n\n            // Reset values as if we were \"this\"\n            const tmpObj = schema.clone();\n            const keysToRestore = ['_settings', '_valids', '_invalids', '_tests', '_refs', '_flags', '_description', '_unit',\n                '_notes', '_tags', '_examples', '_meta', '_inner'];\n\n            for (let i = 0; i < keysToRestore.length; ++i) {\n                tmpObj[keysToRestore[i]] = obj[keysToRestore[i]];\n            }\n\n            obj = tmpObj;\n        }\n\n        obj._settings = obj._settings ? Settings.concat(obj._settings, schema._settings) : schema._settings;\n        obj._valids.merge(schema._valids, schema._invalids);\n        obj._invalids.merge(schema._invalids, schema._valids);\n        obj._tests.push(...schema._tests);\n        obj._refs.push(...schema._refs);\n        if (obj._flags.empty && schema._flags.empty) {\n            obj._flags.empty = obj._flags.empty.concat(schema._flags.empty);\n            const flags = Object.assign({}, schema._flags);\n            delete flags.empty;\n            Hoek.merge(obj._flags, flags);\n        }\n        else if (schema._flags.empty) {\n            obj._flags.empty = schema._flags.empty;\n            const flags = Object.assign({}, schema._flags);\n            delete flags.empty;\n            Hoek.merge(obj._flags, flags);\n        }\n        else {\n            Hoek.merge(obj._flags, schema._flags);\n        }\n\n        obj._description = schema._description || obj._description;\n        obj._unit = schema._unit || obj._unit;\n        obj._notes.push(...schema._notes);\n        obj._tags.push(...schema._tags);\n        obj._examples.push(...schema._examples);\n        obj._meta.push(...schema._meta);\n\n        const inners = Object.keys(schema._inner);\n        const isObject = obj._type === 'object';\n        for (let i = 0; i < inners.length; ++i) {\n            const key = inners[i];\n            const source = schema._inner[key];\n            if (source) {\n                const target = obj._inner[key];\n                if (target) {\n                    if (isObject && key === 'children') {\n                        const keys = {};\n\n                        for (let j = 0; j < target.length; ++j) {\n                            keys[target[j].key] = j;\n                        }\n\n                        for (let j = 0; j < source.length; ++j) {\n                            const sourceKey = source[j].key;\n                            if (keys[sourceKey] >= 0) {\n                                target[keys[sourceKey]] = {\n                                    key: sourceKey,\n                                    schema: target[keys[sourceKey]].schema.concat(source[j].schema)\n                                };\n                            }\n                            else {\n                                target.push(source[j]);\n                            }\n                        }\n                    }\n                    else {\n                        obj._inner[key] = obj._inner[key].concat(source);\n                    }\n                }\n                else {\n                    obj._inner[key] = source.slice();\n                }\n            }\n        }\n\n        return obj;\n    }\n\n    _test(name, arg, func, options) {\n\n        const obj = this.clone();\n        obj._tests.push({ func, name, arg, options });\n        return obj;\n    }\n\n    _testUnique(name, arg, func, options) {\n\n        const obj = this.clone();\n        obj._tests = obj._tests.filter((test) => test.name !== name);\n        obj._tests.push({ func, name, arg, options });\n        return obj;\n    }\n\n    options(options) {\n\n        Hoek.assert(!options.context, 'Cannot override context');\n        this.checkOptions(options);\n\n        const obj = this.clone();\n        obj._settings = Settings.concat(obj._settings, options);\n        return obj;\n    }\n\n    strict(isStrict) {\n\n        const obj = this.clone();\n\n        const convert = isStrict === undefined ? false : !isStrict;\n        obj._settings = Settings.concat(obj._settings, { convert });\n        return obj;\n    }\n\n    raw(isRaw) {\n\n        const value = isRaw === undefined ? true : isRaw;\n\n        if (this._flags.raw === value) {\n            return this;\n        }\n\n        const obj = this.clone();\n        obj._flags.raw = value;\n        return obj;\n    }\n\n    error(err, options = { self: false }) {\n\n        Hoek.assert(err && (err instanceof Error || typeof err === 'function'), 'Must provide a valid Error object or a function');\n\n        const unknownKeys = Object.keys(options).filter((k) => !['self'].includes(k));\n        Hoek.assert(unknownKeys.length === 0, `Options ${unknownKeys} are unknown`);\n\n        const obj = this.clone();\n        obj._flags.error = err;\n\n        if (options.self) {\n            obj._flags.selfError = true;\n        }\n\n        return obj;\n    }\n\n    allow(...values) {\n\n        const obj = this.clone();\n        values = Hoek.flatten(values);\n        for (let i = 0; i < values.length; ++i) {\n            const value = values[i];\n\n            Hoek.assert(value !== undefined, 'Cannot call allow/valid/invalid with undefined');\n            obj._invalids.remove(value);\n            obj._valids.add(value, obj._refs);\n        }\n\n        return obj;\n    }\n\n    valid(...values) {\n\n        const obj = this.allow(...values);\n        obj._flags.allowOnly = true;\n        return obj;\n    }\n\n    invalid(...values) {\n\n        const obj = this.clone();\n        values = Hoek.flatten(values);\n        for (let i = 0; i < values.length; ++i) {\n            const value = values[i];\n\n            Hoek.assert(value !== undefined, 'Cannot call allow/valid/invalid with undefined');\n            obj._valids.remove(value);\n            obj._invalids.add(value, obj._refs);\n        }\n\n        return obj;\n    }\n\n    required() {\n\n        if (this._flags.presence === 'required') {\n            return this;\n        }\n\n        const obj = this.clone();\n        obj._flags.presence = 'required';\n        return obj;\n    }\n\n    optional() {\n\n        if (this._flags.presence === 'optional') {\n            return this;\n        }\n\n        const obj = this.clone();\n        obj._flags.presence = 'optional';\n        return obj;\n    }\n\n\n    forbidden() {\n\n        if (this._flags.presence === 'forbidden') {\n            return this;\n        }\n\n        const obj = this.clone();\n        obj._flags.presence = 'forbidden';\n        return obj;\n    }\n\n\n    strip() {\n\n        if (this._flags.strip) {\n            return this;\n        }\n\n        const obj = this.clone();\n        obj._flags.strip = true;\n        return obj;\n    }\n\n    applyFunctionToChildren(children, fn, args = [], root) {\n\n        children = [].concat(children);\n\n        if (children.length !== 1 || children[0] !== '') {\n            root = root ? (root + '.') : '';\n\n            const extraChildren = (children[0] === '' ? children.slice(1) : children).map((child) => {\n\n                return root + child;\n            });\n\n            throw new Error('unknown key(s) ' + extraChildren.join(', '));\n        }\n\n        return this[fn](...args);\n    }\n\n    default(value, description) {\n\n        if (typeof value === 'function' &&\n            !Ref.isRef(value)) {\n\n            if (!value.description &&\n                description) {\n\n                value.description = description;\n            }\n\n            if (!this._flags.func) {\n                Hoek.assert(typeof value.description === 'string' && value.description.length > 0, 'description must be provided when default value is a function');\n            }\n        }\n\n        const obj = this.clone();\n        obj._flags.default = value;\n        Ref.push(obj._refs, value);\n        return obj;\n    }\n\n    empty(schema) {\n\n        const obj = this.clone();\n        if (schema === undefined) {\n            delete obj._flags.empty;\n        }\n        else {\n            obj._flags.empty = Cast.schema(this._currentJoi, schema);\n        }\n\n        return obj;\n    }\n\n    when(condition, options) {\n\n        Hoek.assert(options && typeof options === 'object', 'Invalid options');\n        Hoek.assert(options.then !== undefined || options.otherwise !== undefined, 'options must have at least one of \"then\" or \"otherwise\"');\n\n        const then = options.hasOwnProperty('then') ? this.concat(Cast.schema(this._currentJoi, options.then)) : undefined;\n        const otherwise = options.hasOwnProperty('otherwise') ? this.concat(Cast.schema(this._currentJoi, options.otherwise)) : undefined;\n\n        Alternatives = Alternatives || require('../alternatives');\n\n        const alternativeOptions = { then, otherwise };\n        if (Object.prototype.hasOwnProperty.call(options, 'is')) {\n            alternativeOptions.is = options.is;\n        }\n\n        const obj = Alternatives.when(condition, alternativeOptions);\n        obj._flags.presence = 'ignore';\n        obj._baseType = this;\n\n        return obj;\n    }\n\n    description(desc) {\n\n        Hoek.assert(desc && typeof desc === 'string', 'Description must be a non-empty string');\n\n        const obj = this.clone();\n        obj._description = desc;\n        return obj;\n    }\n\n    notes(notes) {\n\n        Hoek.assert(notes && (typeof notes === 'string' || Array.isArray(notes)), 'Notes must be a non-empty string or array');\n\n        const obj = this.clone();\n        obj._notes = obj._notes.concat(notes);\n        return obj;\n    }\n\n    tags(tags) {\n\n        Hoek.assert(tags && (typeof tags === 'string' || Array.isArray(tags)), 'Tags must be a non-empty string or array');\n\n        const obj = this.clone();\n        obj._tags = obj._tags.concat(tags);\n        return obj;\n    }\n\n    meta(meta) {\n\n        Hoek.assert(meta !== undefined, 'Meta cannot be undefined');\n\n        const obj = this.clone();\n        obj._meta = obj._meta.concat(meta);\n        return obj;\n    }\n\n    example(...examples) {\n\n        Hoek.assert(examples.length > 0, 'Missing examples');\n\n        const processed = [];\n        for (let i = 0; i < examples.length; ++i) {\n            const example = [].concat(examples[i]);\n            Hoek.assert(example.length <= 2, `Bad example format at index ${i}`);\n\n            const value = example[0];\n            let options = example[1];\n            if (options !== undefined) {\n                Hoek.assert(options && typeof options === 'object', `Options for example at index ${i} must be an object`);\n                const unknownOptions = Object.keys(options).filter((option) => !['parent', 'context'].includes(option));\n                Hoek.assert(unknownOptions.length === 0, `Unknown example options ${unknownOptions} at index ${i}`);\n            }\n            else {\n                options = {};\n            }\n\n            const localState = new State('', [], options.parent || null);\n            const result = this._validate(value, localState, Settings.concat(internals.defaults, options.context ? { context: options.context } : null));\n            Hoek.assert(!result.errors, `Bad example at index ${i}:`, result.errors && Errors.process(result.errors, value));\n\n            const ex = { value };\n            if (Object.keys(options).length) {\n                ex.options = options;\n            }\n\n            processed.push(ex);\n        }\n\n        const obj = this.clone();\n        obj._examples = processed;\n        return obj;\n    }\n\n    unit(name) {\n\n        Hoek.assert(name && typeof name === 'string', 'Unit name must be a non-empty string');\n\n        const obj = this.clone();\n        obj._unit = name;\n        return obj;\n    }\n\n    _prepareEmptyValue(value) {\n\n        if (typeof value === 'string' && this._flags.trim) {\n            return value.trim();\n        }\n\n        return value;\n    }\n\n    _validate(value, state, options, reference) {\n\n        const originalValue = value;\n\n        // Setup state and settings\n\n        state = state || new State('', [], null, reference);\n\n        if (this._settings) {\n            const isDefaultOptions = options === internals.defaults;\n            if (isDefaultOptions && this._settings[Symbols.settingsCache]) {\n                options = this._settings[Symbols.settingsCache];\n            }\n            else {\n                options = Settings.concat(this._language ? Settings.concat({ language: this._language }, options) : options, this._settings);\n                if (isDefaultOptions) {\n                    this._settings[Symbols.settingsCache] = options;\n                }\n            }\n        }\n        else if (this._language) {\n            options = Settings.concat({ language: this._language }, options);\n        }\n\n        let errors = [];\n\n        if (this._coerce) {\n            const coerced = this._coerce(value, state, options);\n            if (coerced.errors) {\n                value = coerced.value;\n                errors = errors.concat(coerced.errors);\n                return this._finalizeValue(value, originalValue, errors, state, options);                            // Coerced error always aborts early\n            }\n\n            value = coerced.value;\n        }\n\n        if (this._flags.empty && !this._flags.empty._validate(this._prepareEmptyValue(value), null, internals.defaults).errors) {\n            value = undefined;\n        }\n\n        // Check presence requirements\n\n        const presence = this._flags.presence || options.presence;\n        if (presence === 'optional') {\n            if (value === undefined) {\n                const isDeepDefault = this._flags.hasOwnProperty('default') && this._flags.default === undefined;\n                if (isDeepDefault && this._type === 'object') {\n                    value = {};\n                }\n                else {\n                    return this._finalizeValue(value, originalValue, errors, state, options);\n                }\n            }\n        }\n        else if (presence === 'required' &&\n            value === undefined) {\n\n            errors.push(this.createError('any.required', null, state, options));\n            return this._finalizeValue(value, originalValue, errors, state, options);\n        }\n        else if (presence === 'forbidden') {\n            if (value === undefined) {\n                return this._finalizeValue(value, originalValue, errors, state, options);\n            }\n\n            errors.push(this.createError('any.unknown', null, state, options));\n            return this._finalizeValue(value, originalValue, errors, state, options);\n        }\n\n        // Check allowed and denied values using the original value\n\n        let match = this._valids.get(value, state, options, this._flags.insensitive);\n        if (match) {\n            if (options.convert) {\n                value = match.value;\n            }\n\n            return this._finalizeValue(value, originalValue, errors, state, options);\n        }\n\n        if (this._invalids.has(value, state, options, this._flags.insensitive)) {\n            errors.push(this.createError(value === '' ? 'any.empty' : 'any.invalid', { value, invalids: this._invalids.values({ stripUndefined: true }) }, state, options));\n            if (options.abortEarly) {\n\n                return this._finalizeValue(value, originalValue, errors, state, options);\n            }\n        }\n\n        // Convert value and validate type\n\n        if (this._base) {\n            const base = this._base(value, state, options);\n            if (base.errors) {\n                value = base.value;\n                errors = errors.concat(base.errors);\n                return this._finalizeValue(value, originalValue, errors, state, options);                            // Base error always aborts early\n            }\n\n            if (base.value !== value) {\n                value = base.value;\n\n                // Check allowed and denied values using the converted value\n\n                match = this._valids.get(value, state, options, this._flags.insensitive);\n                if (match) {\n                    value = match.value;\n                    return this._finalizeValue(value, originalValue, errors, state, options);\n                }\n\n                if (this._invalids.has(value, state, options, this._flags.insensitive)) {\n                    errors.push(this.createError(value === '' ? 'any.empty' : 'any.invalid', { value, invalids: this._invalids.values({ stripUndefined: true }) }, state, options));\n                    if (options.abortEarly) {\n                        return this._finalizeValue(value, originalValue, errors, state, options);\n                    }\n                }\n            }\n        }\n\n        // Required values did not match\n\n        if (this._flags.allowOnly) {\n            errors.push(this.createError('any.allowOnly', { value, valids: this._valids.values({ stripUndefined: true }) }, state, options));\n            if (options.abortEarly) {\n                return this._finalizeValue(value, originalValue, errors, state, options);\n            }\n        }\n\n        // Validate tests\n\n        for (let i = 0; i < this._tests.length; ++i) {\n            const test = this._tests[i];\n            const ret = test.func.call(this, value, state, options);\n            if (ret instanceof Errors.Err) {\n                errors.push(ret);\n                if (options.abortEarly) {\n                    return this._finalizeValue(value, originalValue, errors, state, options);\n                }\n            }\n            else {\n                value = ret;\n            }\n        }\n\n        return this._finalizeValue(value, originalValue, errors, state, options);\n    }\n\n    _finalizeValue(value, originalValue, errors, state, options) {\n\n        let finalValue;\n\n        if (value !== undefined) {\n            finalValue = this._flags.raw ? originalValue : value;\n        }\n        else if (options.noDefaults) {\n            finalValue = value;\n        }\n        else if (Ref.isRef(this._flags.default)) {\n            finalValue = this._flags.default(state.parent, options);\n        }\n        else if (typeof this._flags.default === 'function' &&\n            !(this._flags.func && !this._flags.default.description)) {\n\n            let args;\n\n            if (state.parent !== null &&\n                this._flags.default.length > 0) {\n\n                args = [Hoek.clone(state.parent), options];\n            }\n\n            const defaultValue = internals._try(this._flags.default, args);\n            finalValue = defaultValue.value;\n            if (defaultValue.error) {\n                errors.push(this.createError('any.default', { error: defaultValue.error }, state, options));\n            }\n        }\n        else {\n            finalValue = Hoek.clone(this._flags.default);\n        }\n\n        if (errors.length &&\n            typeof this._flags.error === 'function' &&\n            (\n                !this._flags.selfError ||\n                errors.some((e) => state.path.length === e.path.length)\n            )\n        ) {\n            const change = this._flags.error.call(this, errors);\n\n            if (typeof change === 'string') {\n                errors = [this.createOverrideError('override', { reason: errors }, state, options, change)];\n            }\n            else {\n                errors = [].concat(change)\n                    .map((err) => {\n\n                        return err instanceof Error ?\n                            err :\n                            this.createOverrideError(err.type || 'override', err.context, state, options, err.message, err.template);\n                    });\n            }\n        }\n\n        return {\n            value: this._flags.strip ? undefined : finalValue,\n            finalValue,\n            errors: errors.length ? errors : null\n        };\n    }\n\n    _validateWithOptions(value, options, callback) {\n\n        if (options) {\n            this.checkOptions(options);\n        }\n\n        const settings = Settings.concat(internals.defaults, options);\n        const result = this._validate(value, null, settings);\n        const errors = Errors.process(result.errors, value);\n\n        if (callback) {\n            return callback(errors, result.value);\n        }\n\n        return {\n            error: errors,\n            value: result.value,\n            then(resolve, reject) {\n\n                if (errors) {\n                    return Promise.reject(errors).catch(reject);\n                }\n\n                return Promise.resolve(result.value).then(resolve);\n            },\n            catch(reject) {\n\n                if (errors) {\n                    return Promise.reject(errors).catch(reject);\n                }\n\n                return Promise.resolve(result.value);\n            }\n        };\n    }\n\n    validate(value, options, callback) {\n\n        if (typeof options === 'function') {\n            return this._validateWithOptions(value, null, options);\n        }\n\n        return this._validateWithOptions(value, options, callback);\n    }\n\n    describe() {\n\n        const description = {\n            type: this._type\n        };\n\n        const flags = Object.keys(this._flags);\n        if (flags.length) {\n            if (['empty', 'default', 'lazy', 'label'].some((flag) => this._flags.hasOwnProperty(flag))) {\n                description.flags = {};\n                for (let i = 0; i < flags.length; ++i) {\n                    const flag = flags[i];\n                    if (flag === 'empty') {\n                        description.flags[flag] = this._flags[flag].describe();\n                    }\n                    else if (flag === 'default') {\n                        if (Ref.isRef(this._flags[flag])) {\n                            description.flags[flag] = this._flags[flag].toString();\n                        }\n                        else if (typeof this._flags[flag] === 'function') {\n                            description.flags[flag] = {\n                                description: this._flags[flag].description,\n                                function   : this._flags[flag]\n                            };\n                        }\n                        else {\n                            description.flags[flag] = this._flags[flag];\n                        }\n                    }\n                    else if (flag === 'lazy' || flag === 'label') {\n                        // We don't want it in the description\n                    }\n                    else {\n                        description.flags[flag] = this._flags[flag];\n                    }\n                }\n            }\n            else {\n                description.flags = this._flags;\n            }\n        }\n\n        if (this._settings) {\n            description.options = Hoek.clone(this._settings);\n        }\n\n        if (this._baseType) {\n            description.base = this._baseType.describe();\n        }\n\n        if (this._description) {\n            description.description = this._description;\n        }\n\n        if (this._notes.length) {\n            description.notes = this._notes;\n        }\n\n        if (this._tags.length) {\n            description.tags = this._tags;\n        }\n\n        if (this._meta.length) {\n            description.meta = this._meta;\n        }\n\n        if (this._examples.length) {\n            description.examples = this._examples;\n        }\n\n        if (this._unit) {\n            description.unit = this._unit;\n        }\n\n        const valids = this._valids.values();\n        if (valids.length) {\n            description.valids = valids.map((v) => {\n\n                return Ref.isRef(v) ? v.toString() : v;\n            });\n        }\n\n        const invalids = this._invalids.values();\n        if (invalids.length) {\n            description.invalids = invalids.map((v) => {\n\n                return Ref.isRef(v) ? v.toString() : v;\n            });\n        }\n\n        description.rules = [];\n\n        for (let i = 0; i < this._tests.length; ++i) {\n            const validator = this._tests[i];\n            const item = { name: validator.name };\n\n            if (validator.arg !== void 0) {\n                item.arg = Ref.isRef(validator.arg) ? validator.arg.toString() : validator.arg;\n            }\n\n            const options = validator.options;\n            if (options) {\n                if (options.hasRef) {\n                    item.arg = {};\n                    const keys = Object.keys(validator.arg);\n                    for (let j = 0; j < keys.length; ++j) {\n                        const key = keys[j];\n                        const value = validator.arg[key];\n                        item.arg[key] = Ref.isRef(value) ? value.toString() : value;\n                    }\n                }\n\n                if (typeof options.description === 'string') {\n                    item.description = options.description;\n                }\n                else if (typeof options.description === 'function') {\n                    item.description = options.description(item.arg);\n                }\n            }\n\n            description.rules.push(item);\n        }\n\n        if (!description.rules.length) {\n            delete description.rules;\n        }\n\n        const label = this._getLabel();\n        if (label) {\n            description.label = label;\n        }\n\n        return description;\n    }\n\n    label(name) {\n\n        Hoek.assert(name && typeof name === 'string', 'Label name must be a non-empty string');\n\n        const obj = this.clone();\n        obj._flags.label = name;\n        return obj;\n    }\n\n    _getLabel(def) {\n\n        return this._flags.label || def;\n    }\n\n}]"
                                                  }
                                                }
                                              ],
                                              "_refs": [],
                                              "_flags": {},
                                              "_description": null,
                                              "_unit": null,
                                              "_notes": [],
                                              "_tags": [],
                                              "_examples": [],
                                              "_meta": [],
                                              "_inner": {
                                                "children": null,
                                                "renames": [],
                                                "dependencies": [],
                                                "patterns": []
                                              }
                                            }
                                          }
                                        ]
                                      }
                                    }
                                  },
                                  {
                                    "schema": {
                                      "isJoi": true,
                                      "_currentJoi": "[Circular ~.base._currentJoi]",
                                      "_type": "object",
                                      "_settings": null,
                                      "_valids": {
                                        "_set": {},
                                        "_hasRef": false
                                      },
                                      "_invalids": {
                                        "_set": {},
                                        "_hasRef": false
                                      },
                                      "_tests": [
                                        {
                                          "func": function (value, state, options) {\n\n            if (value instanceof constructor) {\n                return value;\n            }\n\n            return this.createError('object.type', { type: typeData.name, value }, state, options);\n        },
                                          "name": "type",
                                          "arg": {
                                            "name": "Joi object",
                                            "ctor": "[class extends Any {\n\n    constructor() {\n\n        super();\n        this._type = 'object';\n        this._inner.children = null;\n        this._inner.renames = [];\n        this._inner.dependencies = [];\n        this._inner.patterns = [];\n    }\n\n    _init(...args) {\n\n        return args.length ? this.keys(...args) : this;\n    }\n\n    _base(value, state, options) {\n\n        let target = value;\n        const errors = [];\n        const finish = () => {\n\n            return {\n                value: target,\n                errors: errors.length ? errors : null\n            };\n        };\n\n        if (typeof value === 'string' &&\n            options.convert) {\n\n            if (value.length > 1 &&\n                (value[0] === '{' || /^\\s*\\{/.test(value))) {\n\n                try {\n                    value = Bourne.parse(value);\n                }\n                catch (e) { }\n            }\n        }\n\n        const type = this._flags.func ? 'function' : 'object';\n        if (!value ||\n            typeof value !== type ||\n            Array.isArray(value)) {\n\n            errors.push(this.createError(type + '.base', { value }, state, options));\n            return finish();\n        }\n\n        // Skip if there are no other rules to test\n\n        if (!this._inner.renames.length &&\n            !this._inner.dependencies.length &&\n            !this._inner.children &&                    // null allows any keys\n            !this._inner.patterns.length) {\n\n            target = value;\n            return finish();\n        }\n\n        // Ensure target is a local copy (parsed) or shallow copy\n\n        if (target === value) {\n            if (type === 'object') {\n                target = Object.create(Object.getPrototypeOf(value));\n            }\n            else {\n                target = function (...args) {\n\n                    return value.apply(this, args);\n                };\n\n                target.prototype = Hoek.clone(value.prototype);\n            }\n\n            const valueKeys = Object.keys(value);\n            for (let i = 0; i < valueKeys.length; ++i) {\n                target[valueKeys[i]] = value[valueKeys[i]];\n            }\n        }\n        else {\n            target = value;\n        }\n\n        // Rename keys\n\n        const renamed = {};\n        for (let i = 0; i < this._inner.renames.length; ++i) {\n            const rename = this._inner.renames[i];\n\n            if (rename.isRegExp) {\n                const targetKeys = Object.keys(target);\n                const matchedTargetKeys = [];\n\n                for (let j = 0; j < targetKeys.length; ++j) {\n                    if (rename.from.test(targetKeys[j])) {\n                        matchedTargetKeys.push(targetKeys[j]);\n                    }\n                }\n\n                const allUndefined = matchedTargetKeys.every((key) => target[key] === undefined);\n                if (rename.options.ignoreUndefined && allUndefined) {\n                    continue;\n                }\n\n                if (!rename.options.multiple &&\n                    renamed[rename.to]) {\n\n                    errors.push(this.createError('object.rename.regex.multiple', { from: matchedTargetKeys, to: rename.to }, state, options));\n                    if (options.abortEarly) {\n                        return finish();\n                    }\n                }\n\n                if (Object.prototype.hasOwnProperty.call(target, rename.to) &&\n                    !rename.options.override &&\n                    !renamed[rename.to]) {\n\n                    errors.push(this.createError('object.rename.regex.override', { from: matchedTargetKeys, to: rename.to }, state, options));\n                    if (options.abortEarly) {\n                        return finish();\n                    }\n                }\n\n                if (allUndefined) {\n                    delete target[rename.to];\n                }\n                else {\n                    target[rename.to] = target[matchedTargetKeys[matchedTargetKeys.length - 1]];\n                }\n\n                renamed[rename.to] = true;\n\n                if (!rename.options.alias) {\n                    for (let j = 0; j < matchedTargetKeys.length; ++j) {\n                        delete target[matchedTargetKeys[j]];\n                    }\n                }\n            }\n            else {\n                if (rename.options.ignoreUndefined && target[rename.from] === undefined) {\n                    continue;\n                }\n\n                if (!rename.options.multiple &&\n                    renamed[rename.to]) {\n\n                    errors.push(this.createError('object.rename.multiple', { from: rename.from, to: rename.to }, state, options));\n                    if (options.abortEarly) {\n                        return finish();\n                    }\n                }\n\n                if (Object.prototype.hasOwnProperty.call(target, rename.to) &&\n                    !rename.options.override &&\n                    !renamed[rename.to]) {\n\n                    errors.push(this.createError('object.rename.override', { from: rename.from, to: rename.to }, state, options));\n                    if (options.abortEarly) {\n                        return finish();\n                    }\n                }\n\n                if (target[rename.from] === undefined) {\n                    delete target[rename.to];\n                }\n                else {\n                    target[rename.to] = target[rename.from];\n                }\n\n                renamed[rename.to] = true;\n\n                if (!rename.options.alias) {\n                    delete target[rename.from];\n                }\n            }\n        }\n\n        // Validate schema\n\n        if (!this._inner.children &&            // null allows any keys\n            !this._inner.patterns.length &&\n            !this._inner.dependencies.length) {\n\n            return finish();\n        }\n\n        const unprocessed = new Set(Object.keys(target));\n\n        if (this._inner.children) {\n            const stripProps = [];\n\n            for (let i = 0; i < this._inner.children.length; ++i) {\n                const child = this._inner.children[i];\n                const key = child.key;\n                const item = target[key];\n\n                unprocessed.delete(key);\n\n                const localState = new State(key, [...state.path, key], target, state.reference);\n                const result = child.schema._validate(item, localState, options);\n                if (result.errors) {\n                    errors.push(this.createError('object.child', { key, child: child.schema._getLabel(key), reason: result.errors }, localState, options));\n\n                    if (options.abortEarly) {\n                        return finish();\n                    }\n                }\n                else {\n                    if (child.schema._flags.strip || (result.value === undefined && result.value !== item)) {\n                        stripProps.push(key);\n                        target[key] = result.finalValue;\n                    }\n                    else if (result.value !== undefined) {\n                        target[key] = result.value;\n                    }\n                }\n            }\n\n            for (let i = 0; i < stripProps.length; ++i) {\n                delete target[stripProps[i]];\n            }\n        }\n\n        // Unknown keys\n\n        if (unprocessed.size && this._inner.patterns.length) {\n\n            for (const key of unprocessed) {\n                const localState = new State(key, [...state.path, key], target, state.reference);\n                const item = target[key];\n\n                for (let i = 0; i < this._inner.patterns.length; ++i) {\n                    const pattern = this._inner.patterns[i];\n\n                    if (pattern.regex ?\n                        pattern.regex.test(key) :\n                        !pattern.schema._validate(key, state, { ...options, abortEarly:true }).errors) {\n\n                        unprocessed.delete(key);\n\n                        const result = pattern.rule._validate(item, localState, options);\n                        if (result.errors) {\n                            errors.push(this.createError('object.child', {\n                                key,\n                                child: pattern.rule._getLabel(key),\n                                reason: result.errors\n                            }, localState, options));\n\n                            if (options.abortEarly) {\n                                return finish();\n                            }\n                        }\n\n                        target[key] = result.value;\n                    }\n                }\n            }\n        }\n\n        if (unprocessed.size && (this._inner.children || this._inner.patterns.length)) {\n            if ((options.stripUnknown && this._flags.allowUnknown !== true) ||\n                options.skipFunctions) {\n\n                const stripUnknown = options.stripUnknown\n                    ? (options.stripUnknown === true ? true : !!options.stripUnknown.objects)\n                    : false;\n\n\n                for (const key of unprocessed) {\n                    if (stripUnknown) {\n                        delete target[key];\n                        unprocessed.delete(key);\n                    }\n                    else if (typeof target[key] === 'function') {\n                        unprocessed.delete(key);\n                    }\n                }\n            }\n\n            if ((this._flags.allowUnknown !== undefined ? !this._flags.allowUnknown : !options.allowUnknown)) {\n\n                for (const unprocessedKey of unprocessed) {\n                    errors.push(this.createError('object.allowUnknown', { child: unprocessedKey, value: target[unprocessedKey] }, {\n                        key: unprocessedKey,\n                        path: [...state.path, unprocessedKey]\n                    }, options, {}));\n                }\n            }\n        }\n\n        // Validate dependencies\n\n        for (let i = 0; i < this._inner.dependencies.length; ++i) {\n            const dep = this._inner.dependencies[i];\n            const hasKey = dep.key !== null;\n            const splitKey = hasKey && dep.key.split('.');\n            const localState = hasKey ? new State(splitKey[splitKey.length - 1], [...state.path, ...splitKey]) : new State(null, state.path);\n            const err = internals[dep.type].call(this, dep.key, hasKey && Hoek.reach(target, dep.key, { functions: true }), dep.peers, target, localState, options);\n            if (err instanceof Errors.Err) {\n                errors.push(err);\n                if (options.abortEarly) {\n                    return finish();\n                }\n            }\n        }\n\n        return finish();\n    }\n\n    keys(schema) {\n\n        Hoek.assert(schema === null || schema === undefined || typeof schema === 'object', 'Object schema must be a valid object');\n        Hoek.assert(!schema || !(schema instanceof Any), 'Object schema cannot be a joi schema');\n\n        const obj = this.clone();\n\n        if (!schema) {\n            obj._inner.children = null;\n            return obj;\n        }\n\n        const children = Object.keys(schema);\n\n        if (!children.length) {\n            obj._inner.children = [];\n            return obj;\n        }\n\n        const topo = new Topo();\n        if (obj._inner.children) {\n            for (let i = 0; i < obj._inner.children.length; ++i) {\n                const child = obj._inner.children[i];\n\n                // Only add the key if we are not going to replace it later\n                if (!children.includes(child.key)) {\n                    topo.add(child, { after: child._refs, group: child.key });\n                }\n            }\n        }\n\n        for (let i = 0; i < children.length; ++i) {\n            const key = children[i];\n            const child = schema[key];\n            try {\n                const cast = Cast.schema(this._currentJoi, child);\n                topo.add({ key, schema: cast }, { after: cast._refs, group: key });\n            }\n            catch (castErr) {\n                if (castErr.hasOwnProperty('path')) {\n                    castErr.path = key + '.' + castErr.path;\n                }\n                else {\n                    castErr.path = key;\n                }\n\n                throw castErr;\n            }\n        }\n\n        obj._inner.children = topo.nodes;\n\n        return obj;\n    }\n\n    append(schema) {\n        // Skip any changes\n        if (schema === null || schema === undefined || Object.keys(schema).length === 0) {\n            return this;\n        }\n\n        return this.keys(schema);\n    }\n\n    unknown(allow) {\n\n        const value = allow !== false;\n\n        if (this._flags.allowUnknown === value) {\n            return this;\n        }\n\n        const obj = this.clone();\n        obj._flags.allowUnknown = value;\n        return obj;\n    }\n\n    length(limit) {\n\n        Hoek.assert(Number.isSafeInteger(limit) && limit >= 0, 'limit must be a positive integer');\n\n        return this._test('length', limit, function (value, state, options) {\n\n            if (Object.keys(value).length === limit) {\n                return value;\n            }\n\n            return this.createError('object.length', { limit, value }, state, options);\n        });\n    }\n\n    min(limit) {\n\n        Hoek.assert(Number.isSafeInteger(limit) && limit >= 0, 'limit must be a positive integer');\n\n        return this._test('min', limit, function (value, state, options) {\n\n            if (Object.keys(value).length >= limit) {\n                return value;\n            }\n\n            return this.createError('object.min', { limit, value }, state, options);\n        });\n    }\n\n    max(limit) {\n\n        Hoek.assert(Number.isSafeInteger(limit) && limit >= 0, 'limit must be a positive integer');\n\n        return this._test('max', limit, function (value, state, options) {\n\n            if (Object.keys(value).length <= limit) {\n                return value;\n            }\n\n            return this.createError('object.max', { limit, value }, state, options);\n        });\n    }\n\n    pattern(pattern, schema) {\n\n        const isRegExp = pattern instanceof RegExp;\n        Hoek.assert(isRegExp || pattern instanceof Any, 'pattern must be a regex or schema');\n        Hoek.assert(schema !== undefined, 'Invalid rule');\n\n        if (isRegExp) {\n            Hoek.assert(!pattern.flags.includes('g') && !pattern.flags.includes('y'), 'pattern should not use global or sticky mode');\n        }\n\n        try {\n            schema = Cast.schema(this._currentJoi, schema);\n        }\n        catch (castErr) {\n            if (castErr.hasOwnProperty('path')) {\n                castErr.message = `${castErr.message}(${castErr.path})`;\n            }\n\n            throw castErr;\n        }\n\n        const obj = this.clone();\n        if (isRegExp) {\n            obj._inner.patterns.push({ regex: pattern, rule: schema });\n        }\n        else {\n            obj._inner.patterns.push({ schema: pattern, rule: schema });\n        }\n\n        return obj;\n    }\n\n    schema() {\n\n        return this._test('schema', null, function (value, state, options) {\n\n            if (value instanceof Any) {\n                return value;\n            }\n\n            return this.createError('object.schema', null, state, options);\n        });\n    }\n\n    with(key, peers) {\n\n        Hoek.assert(arguments.length === 2, 'Invalid number of arguments, expected 2.');\n\n        return this._dependency('with', key, peers);\n    }\n\n    without(key, peers) {\n\n        Hoek.assert(arguments.length === 2, 'Invalid number of arguments, expected 2.');\n\n        return this._dependency('without', key, peers);\n    }\n\n    xor(...peers) {\n\n        peers = Hoek.flatten(peers);\n        return this._dependency('xor', null, peers);\n    }\n\n    oxor(...peers) {\n\n        return this._dependency('oxor', null, peers);\n    }\n\n    or(...peers) {\n\n        peers = Hoek.flatten(peers);\n        return this._dependency('or', null, peers);\n    }\n\n    and(...peers) {\n\n        peers = Hoek.flatten(peers);\n        return this._dependency('and', null, peers);\n    }\n\n    nand(...peers) {\n\n        peers = Hoek.flatten(peers);\n        return this._dependency('nand', null, peers);\n    }\n\n    requiredKeys(...children) {\n\n        children = Hoek.flatten(children);\n        return this.applyFunctionToChildren(children, 'required');\n    }\n\n    optionalKeys(...children) {\n\n        children = Hoek.flatten(children);\n        return this.applyFunctionToChildren(children, 'optional');\n    }\n\n    forbiddenKeys(...children) {\n\n        children = Hoek.flatten(children);\n        return this.applyFunctionToChildren(children, 'forbidden');\n    }\n\n    rename(from, to, options) {\n\n        Hoek.assert(typeof from === 'string' || from instanceof RegExp, 'Rename missing the from argument');\n        Hoek.assert(typeof to === 'string', 'Rename missing the to argument');\n        Hoek.assert(to !== from, 'Cannot rename key to same name:', from);\n\n        for (let i = 0; i < this._inner.renames.length; ++i) {\n            Hoek.assert(this._inner.renames[i].from !== from, 'Cannot rename the same key multiple times');\n        }\n\n        const obj = this.clone();\n\n        obj._inner.renames.push({\n            from,\n            to,\n            options: Hoek.applyToDefaults(internals.renameDefaults, options || {}),\n            isRegExp: from instanceof RegExp\n        });\n\n        return obj;\n    }\n\n    applyFunctionToChildren(children, fn, args = [], root) {\n\n        children = [].concat(children);\n        Hoek.assert(children.length > 0, 'expected at least one children');\n\n        const groupedChildren = internals.groupChildren(children);\n        let obj;\n\n        if ('' in groupedChildren) {\n            obj = this[fn](...args);\n            delete groupedChildren[''];\n        }\n        else {\n            obj = this.clone();\n        }\n\n        if (obj._inner.children) {\n            root = root ? (root + '.') : '';\n\n            for (let i = 0; i < obj._inner.children.length; ++i) {\n                const child = obj._inner.children[i];\n                const group = groupedChildren[child.key];\n\n                if (group) {\n                    obj._inner.children[i] = {\n                        key: child.key,\n                        _refs: child._refs,\n                        schema: child.schema.applyFunctionToChildren(group, fn, args, root + child.key)\n                    };\n\n                    delete groupedChildren[child.key];\n                }\n            }\n        }\n\n        const remaining = Object.keys(groupedChildren);\n        Hoek.assert(remaining.length === 0, 'unknown key(s)', remaining.join(', '));\n\n        return obj;\n    }\n\n    _dependency(type, key, peers) {\n\n        peers = [].concat(peers);\n        for (let i = 0; i < peers.length; ++i) {\n            Hoek.assert(typeof peers[i] === 'string', type, 'peers must be a string or array of strings');\n        }\n\n        const obj = this.clone();\n        obj._inner.dependencies.push({ type, key, peers });\n        return obj;\n    }\n\n    describe(shallow) {\n\n        const description = super.describe();\n\n        if (description.rules) {\n            for (let i = 0; i < description.rules.length; ++i) {\n                const rule = description.rules[i];\n                // Coverage off for future-proof descriptions, only object().assert() is use right now\n                if (/* $lab:coverage:off$ */rule.arg &&\n                    typeof rule.arg === 'object' &&\n                    rule.arg.schema &&\n                    rule.arg.ref /* $lab:coverage:on$ */) {\n                    rule.arg = {\n                        schema: rule.arg.schema.describe(),\n                        ref: rule.arg.ref.toString()\n                    };\n                }\n            }\n        }\n\n        if (this._inner.children &&\n            !shallow) {\n\n            description.children = {};\n            for (let i = 0; i < this._inner.children.length; ++i) {\n                const child = this._inner.children[i];\n                description.children[child.key] = child.schema.describe();\n            }\n        }\n\n        if (this._inner.dependencies.length) {\n            description.dependencies = Hoek.clone(this._inner.dependencies);\n        }\n\n        if (this._inner.patterns.length) {\n            description.patterns = [];\n\n            for (let i = 0; i < this._inner.patterns.length; ++i) {\n                const pattern = this._inner.patterns[i];\n                if (pattern.regex) {\n                    description.patterns.push({ regex: pattern.regex.toString(), rule: pattern.rule.describe() });\n                }\n                else {\n                    description.patterns.push({ schema: pattern.schema.describe(), rule: pattern.rule.describe() });\n                }\n            }\n        }\n\n        if (this._inner.renames.length > 0) {\n            description.renames = Hoek.clone(this._inner.renames);\n        }\n\n        return description;\n    }\n\n    assert(ref, schema, message) {\n\n        ref = Cast.ref(ref);\n        Hoek.assert(ref.isContext || ref.depth > 1, 'Cannot use assertions for root level references - use direct key rules instead');\n        message = message || 'pass the assertion test';\n        Hoek.assert(typeof message === 'string', 'Message must be a string');\n\n        try {\n            schema = Cast.schema(this._currentJoi, schema);\n        }\n        catch (castErr) {\n            if (castErr.hasOwnProperty('path')) {\n                castErr.message = `${castErr.message}(${castErr.path})`;\n            }\n\n            throw castErr;\n        }\n\n        const key = ref.path[ref.path.length - 1];\n        const path = ref.path.join('.');\n\n        return this._test('assert', { schema, ref }, function (value, state, options) {\n\n            const result = schema._validate(ref(value), null, options, value);\n            if (!result.errors) {\n                return value;\n            }\n\n            const localState = new State(key, ref.path, state.parent, state.reference);\n            return this.createError('object.assert', { ref: path, message }, localState, options);\n        });\n    }\n\n    type(constructor, name = constructor.name) {\n\n        Hoek.assert(typeof constructor === 'function', 'type must be a constructor function');\n        const typeData = {\n            name,\n            ctor: constructor\n        };\n\n        return this._test('type', typeData, function (value, state, options) {\n\n            if (value instanceof constructor) {\n                return value;\n            }\n\n            return this.createError('object.type', { type: typeData.name, value }, state, options);\n        });\n    }\n}]"
                                          }
                                        }
                                      ],
                                      "_refs": [],
                                      "_flags": {},
                                      "_description": null,
                                      "_unit": null,
                                      "_notes": [],
                                      "_tags": [],
                                      "_examples": [],
                                      "_meta": [],
                                      "_inner": {
                                        "children": null,
                                        "renames": [],
                                        "dependencies": [],
                                        "patterns": []
                                      }
                                    }
                                  }
                                ]
                              }
                            }
                          },
                          {
                            "key": "description",
                            "schema": {
                              "isJoi": true,
                              "_currentJoi": "[Circular ~.base._currentJoi]",
                              "_type": "alternatives",
                              "_settings": null,
                              "_valids": {
                                "_set": {},
                                "_hasRef": false
                              },
                              "_invalids": {
                                "_set": {},
                                "_hasRef": false
                              },
                              "_tests": [],
                              "_refs": [],
                              "_flags": {},
                              "_description": null,
                              "_unit": null,
                              "_notes": [],
                              "_tags": [],
                              "_examples": [],
                              "_meta": [],
                              "_inner": {
                                "matches": [
                                  {
                                    "schema": {
                                      "isJoi": true,
                                      "_type": "string",
                                      "_settings": null,
                                      "_valids": {
                                        "_set": {},
                                        "_hasRef": false
                                      },
                                      "_invalids": {
                                        "_set": {},
                                        "_hasRef": 0
                                      },
                                      "_tests": [],
                                      "_refs": [],
                                      "_flags": {},
                                      "_description": null,
                                      "_unit": null,
                                      "_notes": [],
                                      "_tags": [],
                                      "_examples": [],
                                      "_meta": [],
                                      "_inner": {},
                                      "_currentJoi": "[Circular ~.base._currentJoi]"
                                    }
                                  },
                                  {
                                    "schema": {
                                      "isJoi": true,
                                      "_currentJoi": "[Circular ~.base._currentJoi]",
                                      "_type": "object",
                                      "_settings": null,
                                      "_valids": {
                                        "_set": {},
                                        "_hasRef": false
                                      },
                                      "_invalids": {
                                        "_set": {},
                                        "_hasRef": false
                                      },
                                      "_tests": [
                                        {
                                          "func": function (value, state, options) {\n\n            if (value.length === n) {\n                return value;\n            }\n\n            return this.createError('function.arity', { n }, state, options);\n        },
                                          "name": "arity",
                                          "arg": 1
                                        }
                                      ],
                                      "_refs": [],
                                      "_flags": {
                                        "func": true
                                      },
                                      "_description": null,
                                      "_unit": null,
                                      "_notes": [],
                                      "_tags": [],
                                      "_examples": [],
                                      "_meta": [],
                                      "_inner": {
                                        "children": null,
                                        "renames": [],
                                        "dependencies": [],
                                        "patterns": []
                                      }
                                    }
                                  }
                                ]
                              }
                            }
                          }
                        ],
                        "renames": [],
                        "dependencies": [
                          {
                            "type": "or",
                            "key": null,
                            "peers": [
                              "setup",
                              "validate"
                            ]
                          }
                        ],
                        "patterns": []
                      }
                    }
                  ],
                  "ordereds": [],
                  "inclusions": [
                    {
                      "isJoi": true,
                      "_currentJoi": "[Circular ~.base._currentJoi]",
                      "_type": "object",
                      "_settings": null,
                      "_valids": {
                        "_set": {},
                        "_hasRef": false
                      },
                      "_invalids": {
                        "_set": {},
                        "_hasRef": false
                      },
                      "_tests": [],
                      "_refs": [],
                      "_flags": {},
                      "_description": null,
                      "_unit": null,
                      "_notes": [],
                      "_tags": [],
                      "_examples": [],
                      "_meta": [],
                      "_inner": {
                        "children": [
                          {
                            "key": "name",
                            "schema": {
                              "isJoi": true,
                              "_currentJoi": "[Circular ~.base._currentJoi]",
                              "_type": "string",
                              "_settings": null,
                              "_valids": {
                                "_set": {},
                                "_hasRef": false
                              },
                              "_invalids": {
                                "_set": {},
                                "_hasRef": 0
                              },
                              "_tests": [],
                              "_refs": [],
                              "_flags": {
                                "presence": "required"
                              },
                              "_description": null,
                              "_unit": null,
                              "_notes": [],
                              "_tags": [],
                              "_examples": [],
                              "_meta": [],
                              "_inner": {}
                            }
                          },
                          {
                            "key": "setup",
                            "schema": {
                              "isJoi": true,
                              "_currentJoi": "[Circular ~.base._currentJoi]",
                              "_type": "object",
                              "_settings": null,
                              "_valids": {
                                "_set": {},
                                "_hasRef": false
                              },
                              "_invalids": {
                                "_set": {},
                                "_hasRef": false
                              },
                              "_tests": [
                                {
                                  "func": function (value, state, options) {\n\n            if (value.length === n) {\n                return value;\n            }\n\n            return this.createError('function.arity', { n }, state, options);\n        },
                                  "name": "arity",
                                  "arg": 1
                                }
                              ],
                              "_refs": [],
                              "_flags": {
                                "func": true
                              },
                              "_description": null,
                              "_unit": null,
                              "_notes": [],
                              "_tags": [],
                              "_examples": [],
                              "_meta": [],
                              "_inner": {
                                "children": null,
                                "renames": [],
                                "dependencies": [],
                                "patterns": []
                              }
                            }
                          },
                          {
                            "key": "validate",
                            "schema": {
                              "isJoi": true,
                              "_currentJoi": "[Circular ~.base._currentJoi]",
                              "_type": "object",
                              "_settings": null,
                              "_valids": {
                                "_set": {},
                                "_hasRef": false
                              },
                              "_invalids": {
                                "_set": {},
                                "_hasRef": false
                              },
                              "_tests": [
                                {
                                  "func": function (value, state, options) {\n\n            if (value.length === n) {\n                return value;\n            }\n\n            return this.createError('function.arity', { n }, state, options);\n        },
                                  "name": "arity",
                                  "arg": 4
                                }
                              ],
                              "_refs": [],
                              "_flags": {
                                "func": true
                              },
                              "_description": null,
                              "_unit": null,
                              "_notes": [],
                              "_tags": [],
                              "_examples": [],
                              "_meta": [],
                              "_inner": {
                                "children": null,
                                "renames": [],
                                "dependencies": [],
                                "patterns": []
                              }
                            }
                          },
                          {
                            "key": "params",
                            "schema": {
                              "isJoi": true,
                              "_currentJoi": "[Circular ~.base._currentJoi]",
                              "_type": "alternatives",
                              "_settings": null,
                              "_valids": {
                                "_set": {},
                                "_hasRef": false
                              },
                              "_invalids": {
                                "_set": {},
                                "_hasRef": false
                              },
                              "_tests": [],
                              "_refs": [],
                              "_flags": {},
                              "_description": null,
                              "_unit": null,
                              "_notes": [],
                              "_tags": [],
                              "_examples": [],
                              "_meta": [],
                              "_inner": {
                                "matches": [
                                  {
                                    "schema": {
                                      "isJoi": true,
                                      "_currentJoi": "[Circular ~.base._currentJoi]",
                                      "_type": "object",
                                      "_settings": null,
                                      "_valids": {
                                        "_set": {},
                                        "_hasRef": false
                                      },
                                      "_invalids": {
                                        "_set": {},
                                        "_hasRef": false
                                      },
                                      "_tests": [],
                                      "_refs": [],
                                      "_flags": {},
                                      "_description": null,
                                      "_unit": null,
                                      "_notes": [],
                                      "_tags": [],
                                      "_examples": [],
                                      "_meta": [],
                                      "_inner": {
                                        "children": null,
                                        "renames": [],
                                        "dependencies": [],
                                        "patterns": [
                                          {
                                            "regex": {},
                                            "rule": {
                                              "isJoi": true,
                                              "_currentJoi": "[Circular ~.base._currentJoi]",
                                              "_type": "object",
                                              "_settings": null,
                                              "_valids": {
                                                "_set": {},
                                                "_hasRef": false
                                              },
                                              "_invalids": {
                                                "_set": {},
                                                "_hasRef": false
                                              },
                                              "_tests": [
                                                {
                                                  "func": function (value, state, options) {\n\n            if (value instanceof constructor) {\n                return value;\n            }\n\n            return this.createError('object.type', { type: typeData.name, value }, state, options);\n        },
                                                  "name": "type",
                                                  "arg": {
                                                    "name": "Joi object",
                                                    "ctor": "[class {\n\n    constructor() {\n\n        this.isJoi = true;\n        this._type = 'any';\n        this._settings = null;\n        this._valids = new internals.Set();\n        this._invalids = new internals.Set();\n        this._tests = [];\n        this._refs = [];\n        this._flags = {\n            /*\n             presence: 'optional',                   // optional, required, forbidden, ignore\n             allowOnly: false,\n             allowUnknown: undefined,\n             default: undefined,\n             forbidden: false,\n             encoding: undefined,\n             insensitive: false,\n             trim: false,\n             normalize: undefined,                   // NFC, NFD, NFKC, NFKD\n             case: undefined,                        // upper, lower\n             empty: undefined,\n             func: false,\n             raw: false\n             */\n        };\n\n        this._description = null;\n        this._unit = null;\n        this._notes = [];\n        this._tags = [];\n        this._examples = [];\n        this._meta = [];\n\n        this._inner = {};                           // Hash of arrays of immutable objects\n    }\n\n    _init() {\n\n        return this;\n    }\n\n    get schemaType() {\n\n        return this._type;\n    }\n\n    createError(type, context, state, options, flags = this._flags) {\n\n        return Errors.create(type, context, state, options, flags);\n    }\n\n    createOverrideError(type, context, state, options, message, template) {\n\n        return Errors.create(type, context, state, options, this._flags, message, template);\n    }\n\n    checkOptions(options) {\n\n        Schemas = Schemas || require('../../schemas');\n\n        const result = Schemas.options.validate(options);\n\n        if (result.error) {\n            throw new Error(result.error.details[0].message);\n        }\n    }\n\n    clone() {\n\n        const obj = Object.create(Object.getPrototypeOf(this));\n\n        obj.isJoi = true;\n        obj._currentJoi = this._currentJoi;\n        obj._type = this._type;\n        obj._settings = this._settings;\n        obj._baseType = this._baseType;\n        obj._valids = this._valids.slice();\n        obj._invalids = this._invalids.slice();\n        obj._tests = this._tests.slice();\n        obj._refs = this._refs.slice();\n        obj._flags = Hoek.clone(this._flags);\n\n        obj._description = this._description;\n        obj._unit = this._unit;\n        obj._notes = this._notes.slice();\n        obj._tags = this._tags.slice();\n        obj._examples = this._examples.slice();\n        obj._meta = this._meta.slice();\n\n        obj._inner = {};\n        const inners = Object.keys(this._inner);\n        for (let i = 0; i < inners.length; ++i) {\n            const key = inners[i];\n            obj._inner[key] = this._inner[key] ? this._inner[key].slice() : null;\n        }\n\n        return obj;\n    }\n\n    concat(schema) {\n\n        Hoek.assert(schema instanceof internals.Any, 'Invalid schema object');\n        Hoek.assert(this._type === 'any' || schema._type === 'any' || schema._type === this._type, 'Cannot merge type', this._type, 'with another type:', schema._type);\n\n        let obj = this.clone();\n\n        if (this._type === 'any' && schema._type !== 'any') {\n\n            // Reset values as if we were \"this\"\n            const tmpObj = schema.clone();\n            const keysToRestore = ['_settings', '_valids', '_invalids', '_tests', '_refs', '_flags', '_description', '_unit',\n                '_notes', '_tags', '_examples', '_meta', '_inner'];\n\n            for (let i = 0; i < keysToRestore.length; ++i) {\n                tmpObj[keysToRestore[i]] = obj[keysToRestore[i]];\n            }\n\n            obj = tmpObj;\n        }\n\n        obj._settings = obj._settings ? Settings.concat(obj._settings, schema._settings) : schema._settings;\n        obj._valids.merge(schema._valids, schema._invalids);\n        obj._invalids.merge(schema._invalids, schema._valids);\n        obj._tests.push(...schema._tests);\n        obj._refs.push(...schema._refs);\n        if (obj._flags.empty && schema._flags.empty) {\n            obj._flags.empty = obj._flags.empty.concat(schema._flags.empty);\n            const flags = Object.assign({}, schema._flags);\n            delete flags.empty;\n            Hoek.merge(obj._flags, flags);\n        }\n        else if (schema._flags.empty) {\n            obj._flags.empty = schema._flags.empty;\n            const flags = Object.assign({}, schema._flags);\n            delete flags.empty;\n            Hoek.merge(obj._flags, flags);\n        }\n        else {\n            Hoek.merge(obj._flags, schema._flags);\n        }\n\n        obj._description = schema._description || obj._description;\n        obj._unit = schema._unit || obj._unit;\n        obj._notes.push(...schema._notes);\n        obj._tags.push(...schema._tags);\n        obj._examples.push(...schema._examples);\n        obj._meta.push(...schema._meta);\n\n        const inners = Object.keys(schema._inner);\n        const isObject = obj._type === 'object';\n        for (let i = 0; i < inners.length; ++i) {\n            const key = inners[i];\n            const source = schema._inner[key];\n            if (source) {\n                const target = obj._inner[key];\n                if (target) {\n                    if (isObject && key === 'children') {\n                        const keys = {};\n\n                        for (let j = 0; j < target.length; ++j) {\n                            keys[target[j].key] = j;\n                        }\n\n                        for (let j = 0; j < source.length; ++j) {\n                            const sourceKey = source[j].key;\n                            if (keys[sourceKey] >= 0) {\n                                target[keys[sourceKey]] = {\n                                    key: sourceKey,\n                                    schema: target[keys[sourceKey]].schema.concat(source[j].schema)\n                                };\n                            }\n                            else {\n                                target.push(source[j]);\n                            }\n                        }\n                    }\n                    else {\n                        obj._inner[key] = obj._inner[key].concat(source);\n                    }\n                }\n                else {\n                    obj._inner[key] = source.slice();\n                }\n            }\n        }\n\n        return obj;\n    }\n\n    _test(name, arg, func, options) {\n\n        const obj = this.clone();\n        obj._tests.push({ func, name, arg, options });\n        return obj;\n    }\n\n    _testUnique(name, arg, func, options) {\n\n        const obj = this.clone();\n        obj._tests = obj._tests.filter((test) => test.name !== name);\n        obj._tests.push({ func, name, arg, options });\n        return obj;\n    }\n\n    options(options) {\n\n        Hoek.assert(!options.context, 'Cannot override context');\n        this.checkOptions(options);\n\n        const obj = this.clone();\n        obj._settings = Settings.concat(obj._settings, options);\n        return obj;\n    }\n\n    strict(isStrict) {\n\n        const obj = this.clone();\n\n        const convert = isStrict === undefined ? false : !isStrict;\n        obj._settings = Settings.concat(obj._settings, { convert });\n        return obj;\n    }\n\n    raw(isRaw) {\n\n        const value = isRaw === undefined ? true : isRaw;\n\n        if (this._flags.raw === value) {\n            return this;\n        }\n\n        const obj = this.clone();\n        obj._flags.raw = value;\n        return obj;\n    }\n\n    error(err, options = { self: false }) {\n\n        Hoek.assert(err && (err instanceof Error || typeof err === 'function'), 'Must provide a valid Error object or a function');\n\n        const unknownKeys = Object.keys(options).filter((k) => !['self'].includes(k));\n        Hoek.assert(unknownKeys.length === 0, `Options ${unknownKeys} are unknown`);\n\n        const obj = this.clone();\n        obj._flags.error = err;\n\n        if (options.self) {\n            obj._flags.selfError = true;\n        }\n\n        return obj;\n    }\n\n    allow(...values) {\n\n        const obj = this.clone();\n        values = Hoek.flatten(values);\n        for (let i = 0; i < values.length; ++i) {\n            const value = values[i];\n\n            Hoek.assert(value !== undefined, 'Cannot call allow/valid/invalid with undefined');\n            obj._invalids.remove(value);\n            obj._valids.add(value, obj._refs);\n        }\n\n        return obj;\n    }\n\n    valid(...values) {\n\n        const obj = this.allow(...values);\n        obj._flags.allowOnly = true;\n        return obj;\n    }\n\n    invalid(...values) {\n\n        const obj = this.clone();\n        values = Hoek.flatten(values);\n        for (let i = 0; i < values.length; ++i) {\n            const value = values[i];\n\n            Hoek.assert(value !== undefined, 'Cannot call allow/valid/invalid with undefined');\n            obj._valids.remove(value);\n            obj._invalids.add(value, obj._refs);\n        }\n\n        return obj;\n    }\n\n    required() {\n\n        if (this._flags.presence === 'required') {\n            return this;\n        }\n\n        const obj = this.clone();\n        obj._flags.presence = 'required';\n        return obj;\n    }\n\n    optional() {\n\n        if (this._flags.presence === 'optional') {\n            return this;\n        }\n\n        const obj = this.clone();\n        obj._flags.presence = 'optional';\n        return obj;\n    }\n\n\n    forbidden() {\n\n        if (this._flags.presence === 'forbidden') {\n            return this;\n        }\n\n        const obj = this.clone();\n        obj._flags.presence = 'forbidden';\n        return obj;\n    }\n\n\n    strip() {\n\n        if (this._flags.strip) {\n            return this;\n        }\n\n        const obj = this.clone();\n        obj._flags.strip = true;\n        return obj;\n    }\n\n    applyFunctionToChildren(children, fn, args = [], root) {\n\n        children = [].concat(children);\n\n        if (children.length !== 1 || children[0] !== '') {\n            root = root ? (root + '.') : '';\n\n            const extraChildren = (children[0] === '' ? children.slice(1) : children).map((child) => {\n\n                return root + child;\n            });\n\n            throw new Error('unknown key(s) ' + extraChildren.join(', '));\n        }\n\n        return this[fn](...args);\n    }\n\n    default(value, description) {\n\n        if (typeof value === 'function' &&\n            !Ref.isRef(value)) {\n\n            if (!value.description &&\n                description) {\n\n                value.description = description;\n            }\n\n            if (!this._flags.func) {\n                Hoek.assert(typeof value.description === 'string' && value.description.length > 0, 'description must be provided when default value is a function');\n            }\n        }\n\n        const obj = this.clone();\n        obj._flags.default = value;\n        Ref.push(obj._refs, value);\n        return obj;\n    }\n\n    empty(schema) {\n\n        const obj = this.clone();\n        if (schema === undefined) {\n            delete obj._flags.empty;\n        }\n        else {\n            obj._flags.empty = Cast.schema(this._currentJoi, schema);\n        }\n\n        return obj;\n    }\n\n    when(condition, options) {\n\n        Hoek.assert(options && typeof options === 'object', 'Invalid options');\n        Hoek.assert(options.then !== undefined || options.otherwise !== undefined, 'options must have at least one of \"then\" or \"otherwise\"');\n\n        const then = options.hasOwnProperty('then') ? this.concat(Cast.schema(this._currentJoi, options.then)) : undefined;\n        const otherwise = options.hasOwnProperty('otherwise') ? this.concat(Cast.schema(this._currentJoi, options.otherwise)) : undefined;\n\n        Alternatives = Alternatives || require('../alternatives');\n\n        const alternativeOptions = { then, otherwise };\n        if (Object.prototype.hasOwnProperty.call(options, 'is')) {\n            alternativeOptions.is = options.is;\n        }\n\n        const obj = Alternatives.when(condition, alternativeOptions);\n        obj._flags.presence = 'ignore';\n        obj._baseType = this;\n\n        return obj;\n    }\n\n    description(desc) {\n\n        Hoek.assert(desc && typeof desc === 'string', 'Description must be a non-empty string');\n\n        const obj = this.clone();\n        obj._description = desc;\n        return obj;\n    }\n\n    notes(notes) {\n\n        Hoek.assert(notes && (typeof notes === 'string' || Array.isArray(notes)), 'Notes must be a non-empty string or array');\n\n        const obj = this.clone();\n        obj._notes = obj._notes.concat(notes);\n        return obj;\n    }\n\n    tags(tags) {\n\n        Hoek.assert(tags && (typeof tags === 'string' || Array.isArray(tags)), 'Tags must be a non-empty string or array');\n\n        const obj = this.clone();\n        obj._tags = obj._tags.concat(tags);\n        return obj;\n    }\n\n    meta(meta) {\n\n        Hoek.assert(meta !== undefined, 'Meta cannot be undefined');\n\n        const obj = this.clone();\n        obj._meta = obj._meta.concat(meta);\n        return obj;\n    }\n\n    example(...examples) {\n\n        Hoek.assert(examples.length > 0, 'Missing examples');\n\n        const processed = [];\n        for (let i = 0; i < examples.length; ++i) {\n            const example = [].concat(examples[i]);\n            Hoek.assert(example.length <= 2, `Bad example format at index ${i}`);\n\n            const value = example[0];\n            let options = example[1];\n            if (options !== undefined) {\n                Hoek.assert(options && typeof options === 'object', `Options for example at index ${i} must be an object`);\n                const unknownOptions = Object.keys(options).filter((option) => !['parent', 'context'].includes(option));\n                Hoek.assert(unknownOptions.length === 0, `Unknown example options ${unknownOptions} at index ${i}`);\n            }\n            else {\n                options = {};\n            }\n\n            const localState = new State('', [], options.parent || null);\n            const result = this._validate(value, localState, Settings.concat(internals.defaults, options.context ? { context: options.context } : null));\n            Hoek.assert(!result.errors, `Bad example at index ${i}:`, result.errors && Errors.process(result.errors, value));\n\n            const ex = { value };\n            if (Object.keys(options).length) {\n                ex.options = options;\n            }\n\n            processed.push(ex);\n        }\n\n        const obj = this.clone();\n        obj._examples = processed;\n        return obj;\n    }\n\n    unit(name) {\n\n        Hoek.assert(name && typeof name === 'string', 'Unit name must be a non-empty string');\n\n        const obj = this.clone();\n        obj._unit = name;\n        return obj;\n    }\n\n    _prepareEmptyValue(value) {\n\n        if (typeof value === 'string' && this._flags.trim) {\n            return value.trim();\n        }\n\n        return value;\n    }\n\n    _validate(value, state, options, reference) {\n\n        const originalValue = value;\n\n        // Setup state and settings\n\n        state = state || new State('', [], null, reference);\n\n        if (this._settings) {\n            const isDefaultOptions = options === internals.defaults;\n            if (isDefaultOptions && this._settings[Symbols.settingsCache]) {\n                options = this._settings[Symbols.settingsCache];\n            }\n            else {\n                options = Settings.concat(this._language ? Settings.concat({ language: this._language }, options) : options, this._settings);\n                if (isDefaultOptions) {\n                    this._settings[Symbols.settingsCache] = options;\n                }\n            }\n        }\n        else if (this._language) {\n            options = Settings.concat({ language: this._language }, options);\n        }\n\n        let errors = [];\n\n        if (this._coerce) {\n            const coerced = this._coerce(value, state, options);\n            if (coerced.errors) {\n                value = coerced.value;\n                errors = errors.concat(coerced.errors);\n                return this._finalizeValue(value, originalValue, errors, state, options);                            // Coerced error always aborts early\n            }\n\n            value = coerced.value;\n        }\n\n        if (this._flags.empty && !this._flags.empty._validate(this._prepareEmptyValue(value), null, internals.defaults).errors) {\n            value = undefined;\n        }\n\n        // Check presence requirements\n\n        const presence = this._flags.presence || options.presence;\n        if (presence === 'optional') {\n            if (value === undefined) {\n                const isDeepDefault = this._flags.hasOwnProperty('default') && this._flags.default === undefined;\n                if (isDeepDefault && this._type === 'object') {\n                    value = {};\n                }\n                else {\n                    return this._finalizeValue(value, originalValue, errors, state, options);\n                }\n            }\n        }\n        else if (presence === 'required' &&\n            value === undefined) {\n\n            errors.push(this.createError('any.required', null, state, options));\n            return this._finalizeValue(value, originalValue, errors, state, options);\n        }\n        else if (presence === 'forbidden') {\n            if (value === undefined) {\n                return this._finalizeValue(value, originalValue, errors, state, options);\n            }\n\n            errors.push(this.createError('any.unknown', null, state, options));\n            return this._finalizeValue(value, originalValue, errors, state, options);\n        }\n\n        // Check allowed and denied values using the original value\n\n        let match = this._valids.get(value, state, options, this._flags.insensitive);\n        if (match) {\n            if (options.convert) {\n                value = match.value;\n            }\n\n            return this._finalizeValue(value, originalValue, errors, state, options);\n        }\n\n        if (this._invalids.has(value, state, options, this._flags.insensitive)) {\n            errors.push(this.createError(value === '' ? 'any.empty' : 'any.invalid', { value, invalids: this._invalids.values({ stripUndefined: true }) }, state, options));\n            if (options.abortEarly) {\n\n                return this._finalizeValue(value, originalValue, errors, state, options);\n            }\n        }\n\n        // Convert value and validate type\n\n        if (this._base) {\n            const base = this._base(value, state, options);\n            if (base.errors) {\n                value = base.value;\n                errors = errors.concat(base.errors);\n                return this._finalizeValue(value, originalValue, errors, state, options);                            // Base error always aborts early\n            }\n\n            if (base.value !== value) {\n                value = base.value;\n\n                // Check allowed and denied values using the converted value\n\n                match = this._valids.get(value, state, options, this._flags.insensitive);\n                if (match) {\n                    value = match.value;\n                    return this._finalizeValue(value, originalValue, errors, state, options);\n                }\n\n                if (this._invalids.has(value, state, options, this._flags.insensitive)) {\n                    errors.push(this.createError(value === '' ? 'any.empty' : 'any.invalid', { value, invalids: this._invalids.values({ stripUndefined: true }) }, state, options));\n                    if (options.abortEarly) {\n                        return this._finalizeValue(value, originalValue, errors, state, options);\n                    }\n                }\n            }\n        }\n\n        // Required values did not match\n\n        if (this._flags.allowOnly) {\n            errors.push(this.createError('any.allowOnly', { value, valids: this._valids.values({ stripUndefined: true }) }, state, options));\n            if (options.abortEarly) {\n                return this._finalizeValue(value, originalValue, errors, state, options);\n            }\n        }\n\n        // Validate tests\n\n        for (let i = 0; i < this._tests.length; ++i) {\n            const test = this._tests[i];\n            const ret = test.func.call(this, value, state, options);\n            if (ret instanceof Errors.Err) {\n                errors.push(ret);\n                if (options.abortEarly) {\n                    return this._finalizeValue(value, originalValue, errors, state, options);\n                }\n            }\n            else {\n                value = ret;\n            }\n        }\n\n        return this._finalizeValue(value, originalValue, errors, state, options);\n    }\n\n    _finalizeValue(value, originalValue, errors, state, options) {\n\n        let finalValue;\n\n        if (value !== undefined) {\n            finalValue = this._flags.raw ? originalValue : value;\n        }\n        else if (options.noDefaults) {\n            finalValue = value;\n        }\n        else if (Ref.isRef(this._flags.default)) {\n            finalValue = this._flags.default(state.parent, options);\n        }\n        else if (typeof this._flags.default === 'function' &&\n            !(this._flags.func && !this._flags.default.description)) {\n\n            let args;\n\n            if (state.parent !== null &&\n                this._flags.default.length > 0) {\n\n                args = [Hoek.clone(state.parent), options];\n            }\n\n            const defaultValue = internals._try(this._flags.default, args);\n            finalValue = defaultValue.value;\n            if (defaultValue.error) {\n                errors.push(this.createError('any.default', { error: defaultValue.error }, state, options));\n            }\n        }\n        else {\n            finalValue = Hoek.clone(this._flags.default);\n        }\n\n        if (errors.length &&\n            typeof this._flags.error === 'function' &&\n            (\n                !this._flags.selfError ||\n                errors.some((e) => state.path.length === e.path.length)\n            )\n        ) {\n            const change = this._flags.error.call(this, errors);\n\n            if (typeof change === 'string') {\n                errors = [this.createOverrideError('override', { reason: errors }, state, options, change)];\n            }\n            else {\n                errors = [].concat(change)\n                    .map((err) => {\n\n                        return err instanceof Error ?\n                            err :\n                            this.createOverrideError(err.type || 'override', err.context, state, options, err.message, err.template);\n                    });\n            }\n        }\n\n        return {\n            value: this._flags.strip ? undefined : finalValue,\n            finalValue,\n            errors: errors.length ? errors : null\n        };\n    }\n\n    _validateWithOptions(value, options, callback) {\n\n        if (options) {\n            this.checkOptions(options);\n        }\n\n        const settings = Settings.concat(internals.defaults, options);\n        const result = this._validate(value, null, settings);\n        const errors = Errors.process(result.errors, value);\n\n        if (callback) {\n            return callback(errors, result.value);\n        }\n\n        return {\n            error: errors,\n            value: result.value,\n            then(resolve, reject) {\n\n                if (errors) {\n                    return Promise.reject(errors).catch(reject);\n                }\n\n                return Promise.resolve(result.value).then(resolve);\n            },\n            catch(reject) {\n\n                if (errors) {\n                    return Promise.reject(errors).catch(reject);\n                }\n\n                return Promise.resolve(result.value);\n            }\n        };\n    }\n\n    validate(value, options, callback) {\n\n        if (typeof options === 'function') {\n            return this._validateWithOptions(value, null, options);\n        }\n\n        return this._validateWithOptions(value, options, callback);\n    }\n\n    describe() {\n\n        const description = {\n            type: this._type\n        };\n\n        const flags = Object.keys(this._flags);\n        if (flags.length) {\n            if (['empty', 'default', 'lazy', 'label'].some((flag) => this._flags.hasOwnProperty(flag))) {\n                description.flags = {};\n                for (let i = 0; i < flags.length; ++i) {\n                    const flag = flags[i];\n                    if (flag === 'empty') {\n                        description.flags[flag] = this._flags[flag].describe();\n                    }\n                    else if (flag === 'default') {\n                        if (Ref.isRef(this._flags[flag])) {\n                            description.flags[flag] = this._flags[flag].toString();\n                        }\n                        else if (typeof this._flags[flag] === 'function') {\n                            description.flags[flag] = {\n                                description: this._flags[flag].description,\n                                function   : this._flags[flag]\n                            };\n                        }\n                        else {\n                            description.flags[flag] = this._flags[flag];\n                        }\n                    }\n                    else if (flag === 'lazy' || flag === 'label') {\n                        // We don't want it in the description\n                    }\n                    else {\n                        description.flags[flag] = this._flags[flag];\n                    }\n                }\n            }\n            else {\n                description.flags = this._flags;\n            }\n        }\n\n        if (this._settings) {\n            description.options = Hoek.clone(this._settings);\n        }\n\n        if (this._baseType) {\n            description.base = this._baseType.describe();\n        }\n\n        if (this._description) {\n            description.description = this._description;\n        }\n\n        if (this._notes.length) {\n            description.notes = this._notes;\n        }\n\n        if (this._tags.length) {\n            description.tags = this._tags;\n        }\n\n        if (this._meta.length) {\n            description.meta = this._meta;\n        }\n\n        if (this._examples.length) {\n            description.examples = this._examples;\n        }\n\n        if (this._unit) {\n            description.unit = this._unit;\n        }\n\n        const valids = this._valids.values();\n        if (valids.length) {\n            description.valids = valids.map((v) => {\n\n                return Ref.isRef(v) ? v.toString() : v;\n            });\n        }\n\n        const invalids = this._invalids.values();\n        if (invalids.length) {\n            description.invalids = invalids.map((v) => {\n\n                return Ref.isRef(v) ? v.toString() : v;\n            });\n        }\n\n        description.rules = [];\n\n        for (let i = 0; i < this._tests.length; ++i) {\n            const validator = this._tests[i];\n            const item = { name: validator.name };\n\n            if (validator.arg !== void 0) {\n                item.arg = Ref.isRef(validator.arg) ? validator.arg.toString() : validator.arg;\n            }\n\n            const options = validator.options;\n            if (options) {\n                if (options.hasRef) {\n                    item.arg = {};\n                    const keys = Object.keys(validator.arg);\n                    for (let j = 0; j < keys.length; ++j) {\n                        const key = keys[j];\n                        const value = validator.arg[key];\n                        item.arg[key] = Ref.isRef(value) ? value.toString() : value;\n                    }\n                }\n\n                if (typeof options.description === 'string') {\n                    item.description = options.description;\n                }\n                else if (typeof options.description === 'function') {\n                    item.description = options.description(item.arg);\n                }\n            }\n\n            description.rules.push(item);\n        }\n\n        if (!description.rules.length) {\n            delete description.rules;\n        }\n\n        const label = this._getLabel();\n        if (label) {\n            description.label = label;\n        }\n\n        return description;\n    }\n\n    label(name) {\n\n        Hoek.assert(name && typeof name === 'string', 'Label name must be a non-empty string');\n\n        const obj = this.clone();\n        obj._flags.label = name;\n        return obj;\n    }\n\n    _getLabel(def) {\n\n        return this._flags.label || def;\n    }\n\n}]"
                                                  }
                                                }
                                              ],
                                              "_refs": [],
                                              "_flags": {},
                                              "_description": null,
                                              "_unit": null,
                                              "_notes": [],
                                              "_tags": [],
                                              "_examples": [],
                                              "_meta": [],
                                              "_inner": {
                                                "children": null,
                                                "renames": [],
                                                "dependencies": [],
                                                "patterns": []
                                              }
                                            }
                                          }
                                        ]
                                      }
                                    }
                                  },
                                  {
                                    "schema": {
                                      "isJoi": true,
                                      "_currentJoi": "[Circular ~.base._currentJoi]",
                                      "_type": "object",
                                      "_settings": null,
                                      "_valids": {
                                        "_set": {},
                                        "_hasRef": false
                                      },
                                      "_invalids": {
                                        "_set": {},
                                        "_hasRef": false
                                      },
                                      "_tests": [
                                        {
                                          "func": function (value, state, options) {\n\n            if (value instanceof constructor) {\n                return value;\n            }\n\n            return this.createError('object.type', { type: typeData.name, value }, state, options);\n        },
                                          "name": "type",
                                          "arg": {
                                            "name": "Joi object",
                                            "ctor": "[class extends Any {\n\n    constructor() {\n\n        super();\n        this._type = 'object';\n        this._inner.children = null;\n        this._inner.renames = [];\n        this._inner.dependencies = [];\n        this._inner.patterns = [];\n    }\n\n    _init(...args) {\n\n        return args.length ? this.keys(...args) : this;\n    }\n\n    _base(value, state, options) {\n\n        let target = value;\n        const errors = [];\n        const finish = () => {\n\n            return {\n                value: target,\n                errors: errors.length ? errors : null\n            };\n        };\n\n        if (typeof value === 'string' &&\n            options.convert) {\n\n            if (value.length > 1 &&\n                (value[0] === '{' || /^\\s*\\{/.test(value))) {\n\n                try {\n                    value = Bourne.parse(value);\n                }\n                catch (e) { }\n            }\n        }\n\n        const type = this._flags.func ? 'function' : 'object';\n        if (!value ||\n            typeof value !== type ||\n            Array.isArray(value)) {\n\n            errors.push(this.createError(type + '.base', { value }, state, options));\n            return finish();\n        }\n\n        // Skip if there are no other rules to test\n\n        if (!this._inner.renames.length &&\n            !this._inner.dependencies.length &&\n            !this._inner.children &&                    // null allows any keys\n            !this._inner.patterns.length) {\n\n            target = value;\n            return finish();\n        }\n\n        // Ensure target is a local copy (parsed) or shallow copy\n\n        if (target === value) {\n            if (type === 'object') {\n                target = Object.create(Object.getPrototypeOf(value));\n            }\n            else {\n                target = function (...args) {\n\n                    return value.apply(this, args);\n                };\n\n                target.prototype = Hoek.clone(value.prototype);\n            }\n\n            const valueKeys = Object.keys(value);\n            for (let i = 0; i < valueKeys.length; ++i) {\n                target[valueKeys[i]] = value[valueKeys[i]];\n            }\n        }\n        else {\n            target = value;\n        }\n\n        // Rename keys\n\n        const renamed = {};\n        for (let i = 0; i < this._inner.renames.length; ++i) {\n            const rename = this._inner.renames[i];\n\n            if (rename.isRegExp) {\n                const targetKeys = Object.keys(target);\n                const matchedTargetKeys = [];\n\n                for (let j = 0; j < targetKeys.length; ++j) {\n                    if (rename.from.test(targetKeys[j])) {\n                        matchedTargetKeys.push(targetKeys[j]);\n                    }\n                }\n\n                const allUndefined = matchedTargetKeys.every((key) => target[key] === undefined);\n                if (rename.options.ignoreUndefined && allUndefined) {\n                    continue;\n                }\n\n                if (!rename.options.multiple &&\n                    renamed[rename.to]) {\n\n                    errors.push(this.createError('object.rename.regex.multiple', { from: matchedTargetKeys, to: rename.to }, state, options));\n                    if (options.abortEarly) {\n                        return finish();\n                    }\n                }\n\n                if (Object.prototype.hasOwnProperty.call(target, rename.to) &&\n                    !rename.options.override &&\n                    !renamed[rename.to]) {\n\n                    errors.push(this.createError('object.rename.regex.override', { from: matchedTargetKeys, to: rename.to }, state, options));\n                    if (options.abortEarly) {\n                        return finish();\n                    }\n                }\n\n                if (allUndefined) {\n                    delete target[rename.to];\n                }\n                else {\n                    target[rename.to] = target[matchedTargetKeys[matchedTargetKeys.length - 1]];\n                }\n\n                renamed[rename.to] = true;\n\n                if (!rename.options.alias) {\n                    for (let j = 0; j < matchedTargetKeys.length; ++j) {\n                        delete target[matchedTargetKeys[j]];\n                    }\n                }\n            }\n            else {\n                if (rename.options.ignoreUndefined && target[rename.from] === undefined) {\n                    continue;\n                }\n\n                if (!rename.options.multiple &&\n                    renamed[rename.to]) {\n\n                    errors.push(this.createError('object.rename.multiple', { from: rename.from, to: rename.to }, state, options));\n                    if (options.abortEarly) {\n                        return finish();\n                    }\n                }\n\n                if (Object.prototype.hasOwnProperty.call(target, rename.to) &&\n                    !rename.options.override &&\n                    !renamed[rename.to]) {\n\n                    errors.push(this.createError('object.rename.override', { from: rename.from, to: rename.to }, state, options));\n                    if (options.abortEarly) {\n                        return finish();\n                    }\n                }\n\n                if (target[rename.from] === undefined) {\n                    delete target[rename.to];\n                }\n                else {\n                    target[rename.to] = target[rename.from];\n                }\n\n                renamed[rename.to] = true;\n\n                if (!rename.options.alias) {\n                    delete target[rename.from];\n                }\n            }\n        }\n\n        // Validate schema\n\n        if (!this._inner.children &&            // null allows any keys\n            !this._inner.patterns.length &&\n            !this._inner.dependencies.length) {\n\n            return finish();\n        }\n\n        const unprocessed = new Set(Object.keys(target));\n\n        if (this._inner.children) {\n            const stripProps = [];\n\n            for (let i = 0; i < this._inner.children.length; ++i) {\n                const child = this._inner.children[i];\n                const key = child.key;\n                const item = target[key];\n\n                unprocessed.delete(key);\n\n                const localState = new State(key, [...state.path, key], target, state.reference);\n                const result = child.schema._validate(item, localState, options);\n                if (result.errors) {\n                    errors.push(this.createError('object.child', { key, child: child.schema._getLabel(key), reason: result.errors }, localState, options));\n\n                    if (options.abortEarly) {\n                        return finish();\n                    }\n                }\n                else {\n                    if (child.schema._flags.strip || (result.value === undefined && result.value !== item)) {\n                        stripProps.push(key);\n                        target[key] = result.finalValue;\n                    }\n                    else if (result.value !== undefined) {\n                        target[key] = result.value;\n                    }\n                }\n            }\n\n            for (let i = 0; i < stripProps.length; ++i) {\n                delete target[stripProps[i]];\n            }\n        }\n\n        // Unknown keys\n\n        if (unprocessed.size && this._inner.patterns.length) {\n\n            for (const key of unprocessed) {\n                const localState = new State(key, [...state.path, key], target, state.reference);\n                const item = target[key];\n\n                for (let i = 0; i < this._inner.patterns.length; ++i) {\n                    const pattern = this._inner.patterns[i];\n\n                    if (pattern.regex ?\n                        pattern.regex.test(key) :\n                        !pattern.schema._validate(key, state, { ...options, abortEarly:true }).errors) {\n\n                        unprocessed.delete(key);\n\n                        const result = pattern.rule._validate(item, localState, options);\n                        if (result.errors) {\n                            errors.push(this.createError('object.child', {\n                                key,\n                                child: pattern.rule._getLabel(key),\n                                reason: result.errors\n                            }, localState, options));\n\n                            if (options.abortEarly) {\n                                return finish();\n                            }\n                        }\n\n                        target[key] = result.value;\n                    }\n                }\n            }\n        }\n\n        if (unprocessed.size && (this._inner.children || this._inner.patterns.length)) {\n            if ((options.stripUnknown && this._flags.allowUnknown !== true) ||\n                options.skipFunctions) {\n\n                const stripUnknown = options.stripUnknown\n                    ? (options.stripUnknown === true ? true : !!options.stripUnknown.objects)\n                    : false;\n\n\n                for (const key of unprocessed) {\n                    if (stripUnknown) {\n                        delete target[key];\n                        unprocessed.delete(key);\n                    }\n                    else if (typeof target[key] === 'function') {\n                        unprocessed.delete(key);\n                    }\n                }\n            }\n\n            if ((this._flags.allowUnknown !== undefined ? !this._flags.allowUnknown : !options.allowUnknown)) {\n\n                for (const unprocessedKey of unprocessed) {\n                    errors.push(this.createError('object.allowUnknown', { child: unprocessedKey, value: target[unprocessedKey] }, {\n                        key: unprocessedKey,\n                        path: [...state.path, unprocessedKey]\n                    }, options, {}));\n                }\n            }\n        }\n\n        // Validate dependencies\n\n        for (let i = 0; i < this._inner.dependencies.length; ++i) {\n            const dep = this._inner.dependencies[i];\n            const hasKey = dep.key !== null;\n            const splitKey = hasKey && dep.key.split('.');\n            const localState = hasKey ? new State(splitKey[splitKey.length - 1], [...state.path, ...splitKey]) : new State(null, state.path);\n            const err = internals[dep.type].call(this, dep.key, hasKey && Hoek.reach(target, dep.key, { functions: true }), dep.peers, target, localState, options);\n            if (err instanceof Errors.Err) {\n                errors.push(err);\n                if (options.abortEarly) {\n                    return finish();\n                }\n            }\n        }\n\n        return finish();\n    }\n\n    keys(schema) {\n\n        Hoek.assert(schema === null || schema === undefined || typeof schema === 'object', 'Object schema must be a valid object');\n        Hoek.assert(!schema || !(schema instanceof Any), 'Object schema cannot be a joi schema');\n\n        const obj = this.clone();\n\n        if (!schema) {\n            obj._inner.children = null;\n            return obj;\n        }\n\n        const children = Object.keys(schema);\n\n        if (!children.length) {\n            obj._inner.children = [];\n            return obj;\n        }\n\n        const topo = new Topo();\n        if (obj._inner.children) {\n            for (let i = 0; i < obj._inner.children.length; ++i) {\n                const child = obj._inner.children[i];\n\n                // Only add the key if we are not going to replace it later\n                if (!children.includes(child.key)) {\n                    topo.add(child, { after: child._refs, group: child.key });\n                }\n            }\n        }\n\n        for (let i = 0; i < children.length; ++i) {\n            const key = children[i];\n            const child = schema[key];\n            try {\n                const cast = Cast.schema(this._currentJoi, child);\n                topo.add({ key, schema: cast }, { after: cast._refs, group: key });\n            }\n            catch (castErr) {\n                if (castErr.hasOwnProperty('path')) {\n                    castErr.path = key + '.' + castErr.path;\n                }\n                else {\n                    castErr.path = key;\n                }\n\n                throw castErr;\n            }\n        }\n\n        obj._inner.children = topo.nodes;\n\n        return obj;\n    }\n\n    append(schema) {\n        // Skip any changes\n        if (schema === null || schema === undefined || Object.keys(schema).length === 0) {\n            return this;\n        }\n\n        return this.keys(schema);\n    }\n\n    unknown(allow) {\n\n        const value = allow !== false;\n\n        if (this._flags.allowUnknown === value) {\n            return this;\n        }\n\n        const obj = this.clone();\n        obj._flags.allowUnknown = value;\n        return obj;\n    }\n\n    length(limit) {\n\n        Hoek.assert(Number.isSafeInteger(limit) && limit >= 0, 'limit must be a positive integer');\n\n        return this._test('length', limit, function (value, state, options) {\n\n            if (Object.keys(value).length === limit) {\n                return value;\n            }\n\n            return this.createError('object.length', { limit, value }, state, options);\n        });\n    }\n\n    min(limit) {\n\n        Hoek.assert(Number.isSafeInteger(limit) && limit >= 0, 'limit must be a positive integer');\n\n        return this._test('min', limit, function (value, state, options) {\n\n            if (Object.keys(value).length >= limit) {\n                return value;\n            }\n\n            return this.createError('object.min', { limit, value }, state, options);\n        });\n    }\n\n    max(limit) {\n\n        Hoek.assert(Number.isSafeInteger(limit) && limit >= 0, 'limit must be a positive integer');\n\n        return this._test('max', limit, function (value, state, options) {\n\n            if (Object.keys(value).length <= limit) {\n                return value;\n            }\n\n            return this.createError('object.max', { limit, value }, state, options);\n        });\n    }\n\n    pattern(pattern, schema) {\n\n        const isRegExp = pattern instanceof RegExp;\n        Hoek.assert(isRegExp || pattern instanceof Any, 'pattern must be a regex or schema');\n        Hoek.assert(schema !== undefined, 'Invalid rule');\n\n        if (isRegExp) {\n            Hoek.assert(!pattern.flags.includes('g') && !pattern.flags.includes('y'), 'pattern should not use global or sticky mode');\n        }\n\n        try {\n            schema = Cast.schema(this._currentJoi, schema);\n        }\n        catch (castErr) {\n            if (castErr.hasOwnProperty('path')) {\n                castErr.message = `${castErr.message}(${castErr.path})`;\n            }\n\n            throw castErr;\n        }\n\n        const obj = this.clone();\n        if (isRegExp) {\n            obj._inner.patterns.push({ regex: pattern, rule: schema });\n        }\n        else {\n            obj._inner.patterns.push({ schema: pattern, rule: schema });\n        }\n\n        return obj;\n    }\n\n    schema() {\n\n        return this._test('schema', null, function (value, state, options) {\n\n            if (value instanceof Any) {\n                return value;\n            }\n\n            return this.createError('object.schema', null, state, options);\n        });\n    }\n\n    with(key, peers) {\n\n        Hoek.assert(arguments.length === 2, 'Invalid number of arguments, expected 2.');\n\n        return this._dependency('with', key, peers);\n    }\n\n    without(key, peers) {\n\n        Hoek.assert(arguments.length === 2, 'Invalid number of arguments, expected 2.');\n\n        return this._dependency('without', key, peers);\n    }\n\n    xor(...peers) {\n\n        peers = Hoek.flatten(peers);\n        return this._dependency('xor', null, peers);\n    }\n\n    oxor(...peers) {\n\n        return this._dependency('oxor', null, peers);\n    }\n\n    or(...peers) {\n\n        peers = Hoek.flatten(peers);\n        return this._dependency('or', null, peers);\n    }\n\n    and(...peers) {\n\n        peers = Hoek.flatten(peers);\n        return this._dependency('and', null, peers);\n    }\n\n    nand(...peers) {\n\n        peers = Hoek.flatten(peers);\n        return this._dependency('nand', null, peers);\n    }\n\n    requiredKeys(...children) {\n\n        children = Hoek.flatten(children);\n        return this.applyFunctionToChildren(children, 'required');\n    }\n\n    optionalKeys(...children) {\n\n        children = Hoek.flatten(children);\n        return this.applyFunctionToChildren(children, 'optional');\n    }\n\n    forbiddenKeys(...children) {\n\n        children = Hoek.flatten(children);\n        return this.applyFunctionToChildren(children, 'forbidden');\n    }\n\n    rename(from, to, options) {\n\n        Hoek.assert(typeof from === 'string' || from instanceof RegExp, 'Rename missing the from argument');\n        Hoek.assert(typeof to === 'string', 'Rename missing the to argument');\n        Hoek.assert(to !== from, 'Cannot rename key to same name:', from);\n\n        for (let i = 0; i < this._inner.renames.length; ++i) {\n            Hoek.assert(this._inner.renames[i].from !== from, 'Cannot rename the same key multiple times');\n        }\n\n        const obj = this.clone();\n\n        obj._inner.renames.push({\n            from,\n            to,\n            options: Hoek.applyToDefaults(internals.renameDefaults, options || {}),\n            isRegExp: from instanceof RegExp\n        });\n\n        return obj;\n    }\n\n    applyFunctionToChildren(children, fn, args = [], root) {\n\n        children = [].concat(children);\n        Hoek.assert(children.length > 0, 'expected at least one children');\n\n        const groupedChildren = internals.groupChildren(children);\n        let obj;\n\n        if ('' in groupedChildren) {\n            obj = this[fn](...args);\n            delete groupedChildren[''];\n        }\n        else {\n            obj = this.clone();\n        }\n\n        if (obj._inner.children) {\n            root = root ? (root + '.') : '';\n\n            for (let i = 0; i < obj._inner.children.length; ++i) {\n                const child = obj._inner.children[i];\n                const group = groupedChildren[child.key];\n\n                if (group) {\n                    obj._inner.children[i] = {\n                        key: child.key,\n                        _refs: child._refs,\n                        schema: child.schema.applyFunctionToChildren(group, fn, args, root + child.key)\n                    };\n\n                    delete groupedChildren[child.key];\n                }\n            }\n        }\n\n        const remaining = Object.keys(groupedChildren);\n        Hoek.assert(remaining.length === 0, 'unknown key(s)', remaining.join(', '));\n\n        return obj;\n    }\n\n    _dependency(type, key, peers) {\n\n        peers = [].concat(peers);\n        for (let i = 0; i < peers.length; ++i) {\n            Hoek.assert(typeof peers[i] === 'string', type, 'peers must be a string or array of strings');\n        }\n\n        const obj = this.clone();\n        obj._inner.dependencies.push({ type, key, peers });\n        return obj;\n    }\n\n    describe(shallow) {\n\n        const description = super.describe();\n\n        if (description.rules) {\n            for (let i = 0; i < description.rules.length; ++i) {\n                const rule = description.rules[i];\n                // Coverage off for future-proof descriptions, only object().assert() is use right now\n                if (/* $lab:coverage:off$ */rule.arg &&\n                    typeof rule.arg === 'object' &&\n                    rule.arg.schema &&\n                    rule.arg.ref /* $lab:coverage:on$ */) {\n                    rule.arg = {\n                        schema: rule.arg.schema.describe(),\n                        ref: rule.arg.ref.toString()\n                    };\n                }\n            }\n        }\n\n        if (this._inner.children &&\n            !shallow) {\n\n            description.children = {};\n            for (let i = 0; i < this._inner.children.length; ++i) {\n                const child = this._inner.children[i];\n                description.children[child.key] = child.schema.describe();\n            }\n        }\n\n        if (this._inner.dependencies.length) {\n            description.dependencies = Hoek.clone(this._inner.dependencies);\n        }\n\n        if (this._inner.patterns.length) {\n            description.patterns = [];\n\n            for (let i = 0; i < this._inner.patterns.length; ++i) {\n                const pattern = this._inner.patterns[i];\n                if (pattern.regex) {\n                    description.patterns.push({ regex: pattern.regex.toString(), rule: pattern.rule.describe() });\n                }\n                else {\n                    description.patterns.push({ schema: pattern.schema.describe(), rule: pattern.rule.describe() });\n                }\n            }\n        }\n\n        if (this._inner.renames.length > 0) {\n            description.renames = Hoek.clone(this._inner.renames);\n        }\n\n        return description;\n    }\n\n    assert(ref, schema, message) {\n\n        ref = Cast.ref(ref);\n        Hoek.assert(ref.isContext || ref.depth > 1, 'Cannot use assertions for root level references - use direct key rules instead');\n        message = message || 'pass the assertion test';\n        Hoek.assert(typeof message === 'string', 'Message must be a string');\n\n        try {\n            schema = Cast.schema(this._currentJoi, schema);\n        }\n        catch (castErr) {\n            if (castErr.hasOwnProperty('path')) {\n                castErr.message = `${castErr.message}(${castErr.path})`;\n            }\n\n            throw castErr;\n        }\n\n        const key = ref.path[ref.path.length - 1];\n        const path = ref.path.join('.');\n\n        return this._test('assert', { schema, ref }, function (value, state, options) {\n\n            const result = schema._validate(ref(value), null, options, value);\n            if (!result.errors) {\n                return value;\n            }\n\n            const localState = new State(key, ref.path, state.parent, state.reference);\n            return this.createError('object.assert', { ref: path, message }, localState, options);\n        });\n    }\n\n    type(constructor, name = constructor.name) {\n\n        Hoek.assert(typeof constructor === 'function', 'type must be a constructor function');\n        const typeData = {\n            name,\n            ctor: constructor\n        };\n\n        return this._test('type', typeData, function (value, state, options) {\n\n            if (value instanceof constructor) {\n                return value;\n            }\n\n            return this.createError('object.type', { type: typeData.name, value }, state, options);\n        });\n    }\n}]"
                                          }
                                        }
                                      ],
                                      "_refs": [],
                                      "_flags": {},
                                      "_description": null,
                                      "_unit": null,
                                      "_notes": [],
                                      "_tags": [],
                                      "_examples": [],
                                      "_meta": [],
                                      "_inner": {
                                        "children": null,
                                        "renames": [],
                                        "dependencies": [],
                                        "patterns": []
                                      }
                                    }
                                  }
                                ]
                              }
                            }
                          },
                          {
                            "key": "description",
                            "schema": {
                              "isJoi": true,
                              "_currentJoi": "[Circular ~.base._currentJoi]",
                              "_type": "alternatives",
                              "_settings": null,
                              "_valids": {
                                "_set": {},
                                "_hasRef": false
                              },
                              "_invalids": {
                                "_set": {},
                                "_hasRef": false
                              },
                              "_tests": [],
                              "_refs": [],
                              "_flags": {},
                              "_description": null,
                              "_unit": null,
                              "_notes": [],
                              "_tags": [],
                              "_examples": [],
                              "_meta": [],
                              "_inner": {
                                "matches": [
                                  {
                                    "schema": {
                                      "isJoi": true,
                                      "_type": "string",
                                      "_settings": null,
                                      "_valids": {
                                        "_set": {},
                                        "_hasRef": false
                                      },
                                      "_invalids": {
                                        "_set": {},
                                        "_hasRef": 0
                                      },
                                      "_tests": [],
                                      "_refs": [],
                                      "_flags": {},
                                      "_description": null,
                                      "_unit": null,
                                      "_notes": [],
                                      "_tags": [],
                                      "_examples": [],
                                      "_meta": [],
                                      "_inner": {},
                                      "_currentJoi": "[Circular ~.base._currentJoi]"
                                    }
                                  },
                                  {
                                    "schema": {
                                      "isJoi": true,
                                      "_currentJoi": "[Circular ~.base._currentJoi]",
                                      "_type": "object",
                                      "_settings": null,
                                      "_valids": {
                                        "_set": {},
                                        "_hasRef": false
                                      },
                                      "_invalids": {
                                        "_set": {},
                                        "_hasRef": false
                                      },
                                      "_tests": [
                                        {
                                          "func": function (value, state, options) {\n\n            if (value.length === n) {\n                return value;\n            }\n\n            return this.createError('function.arity', { n }, state, options);\n        },
                                          "name": "arity",
                                          "arg": 1
                                        }
                                      ],
                                      "_refs": [],
                                      "_flags": {
                                        "func": true
                                      },
                                      "_description": null,
                                      "_unit": null,
                                      "_notes": [],
                                      "_tags": [],
                                      "_examples": [],
                                      "_meta": [],
                                      "_inner": {
                                        "children": null,
                                        "renames": [],
                                        "dependencies": [],
                                        "patterns": []
                                      }
                                    }
                                  }
                                ]
                              }
                            }
                          }
                        ],
                        "renames": [],
                        "dependencies": [
                          {
                            "type": "or",
                            "key": null,
                            "peers": [
                              "setup",
                              "validate"
                            ]
                          }
                        ],
                        "patterns": []
                      }
                    }
                  ],
                  "exclusions": [],
                  "requireds": []
                }
              }
            }
          ],
          "renames": [],
          "dependencies": [],
          "patterns": []
        }
      },
      "extensionsSchema": {
        "isJoi": true,
        "_currentJoi": "[Circular ~.base._currentJoi]",
        "_type": "array",
        "_settings": {
          "convert": false
        },
        "_valids": {
          "_set": {},
          "_hasRef": false
        },
        "_invalids": {
          "_set": {},
          "_hasRef": false
        },
        "_tests": [],
        "_refs": [],
        "_flags": {
          "sparse": false
        },
        "_description": null,
        "_unit": null,
        "_notes": [],
        "_tags": [],
        "_examples": [],
        "_meta": [],
        "_inner": {
          "items": [
            "[Circular ~.base]",
            {
              "isJoi": true,
              "_currentJoi": "[Circular ~.base._currentJoi]",
              "_type": "object",
              "_settings": null,
              "_valids": {
                "_set": {},
                "_hasRef": false
              },
              "_invalids": {
                "_set": {},
                "_hasRef": false
              },
              "_tests": [
                {
                  "func": function (value, state, options) {\n\n            if (value.length === n) {\n                return value;\n            }\n\n            return this.createError('function.arity', { n }, state, options);\n        },
                  "name": "arity",
                  "arg": 1
                }
              ],
              "_refs": [],
              "_flags": {
                "func": true
              },
              "_description": null,
              "_unit": null,
              "_notes": [],
              "_tags": [],
              "_examples": [],
              "_meta": [],
              "_inner": {
                "children": null,
                "renames": [],
                "dependencies": [],
                "patterns": []
              }
            }
          ],
          "ordereds": [],
          "inclusions": [
            "[Circular ~.base]",
            {
              "isJoi": true,
              "_currentJoi": "[Circular ~.base._currentJoi]",
              "_type": "object",
              "_settings": null,
              "_valids": {
                "_set": {},
                "_hasRef": false
              },
              "_invalids": {
                "_set": {},
                "_hasRef": false
              },
              "_tests": [
                {
                  "func": function (value, state, options) {\n\n            if (value.length === n) {\n                return value;\n            }\n\n            return this.createError('function.arity', { n }, state, options);\n        },
                  "name": "arity",
                  "arg": 1
                }
              ],
              "_refs": [],
              "_flags": {
                "func": true
              },
              "_description": null,
              "_unit": null,
              "_notes": [],
              "_tags": [],
              "_examples": [],
              "_meta": [],
              "_inner": {
                "children": null,
                "renames": [],
                "dependencies": [],
                "patterns": []
              }
            }
          ],
          "exclusions": [],
          "requireds": []
        }
      },
      "version": "15.1.1"
    }
  },
  "coerce": {
    "from": "string",
    "method": "[method(value) {\n                        if (typeof value !== 'string' || (value[0] !== '{' && !/^\\s*\\{/.test(value))) {\n                            return;\n                        }\n\n                        try {\n                            return { value: Bourne.parse(value) };\n                        } catch (ignoreErr) { }\n                    }]"
  },
  ๏ฟฝ[41m"name"๏ฟฝ[0m๏ฟฝ[31m [1]: -- missing --๏ฟฝ[0m
}
๏ฟฝ[31m
[1] "name" is required๏ฟฝ[0m
    at Object.exports.process (/tmp/node_modules/@hapi/joi/lib/errors.js:202:19)
    at internals.Object._validateWithOptions (/tmp/node_modules/@hapi/joi/lib/types/any/index.js:763:31)
    at module.exports.internals.Any.root.validate (/tmp/node_modules/@hapi/joi/lib/index.js:145:23)
    at module.exports.internals.Any.root.attempt (/tmp/node_modules/@hapi/joi/lib/index.js:175:29)
    at module.exports.internals.Any.root.assert (/tmp/node_modules/@hapi/joi/lib/index.js:170:14)
    at module.exports.internals.Any.root.extend (/tmp/node_modules/@hapi/joi/lib/index.js:292:18)
    at new SchemaResolver (/tmp/node_modules/enjoi/lib/resolver.js:13:24)
    at Object.exports.schema (/tmp/node_modules/enjoi/index.js:47:12)
    at Object.<anonymous> (/tmp/main.js:3:20)
    at Module._compile (internal/modules/cjs/loader.js:1123:30) {
  isJoi: true,
  details: [
    {
      message: '"name" is required',
      path: [Array],
      type: 'any.required',
      context: [Object]
    }
  ],
  _object: {
    type: 'object',
    base: {
      isJoi: true,
      _type: 'object',
      _settings: null,
      _valids: [InternalSet],
      _invalids: [InternalSet],
      _tests: [],
      _refs: [],
      _flags: {},
      _description: null,
      _unit: null,
      _notes: [],
      _tags: [],
      _examples: [],
      _meta: [],
      _inner: [Object],
      _currentJoi: [Object]
    },
    coerce: { from: 'string', method: [Function: method] }
  },
  annotate: [Function (anonymous)]
}

Date formatting

const Joi = require('joi');
const Enjoi = require('enjoi');

const schema = Enjoi({
    type: 'object',
    properties: {
        firstName: {
            description: 'First name.',
            type: 'string'
        },
        lastName: {
            description: 'Last name.',
            type: 'string'
        },
        mail: {type: "email"},
        effectiveDate: {type: 'date', minimum: '12-21-2012'},
        age: {
            description: 'Age in years',
            type: 'integer',
            minimum: 1
        }
    }
}, {
    types: {
        email: Joi.string().email(),
        date: Joi.date()
    }
});

const {error} = Joi.validate({firstName: 'John', lastName: 'Doe', age: 9, mail: "[email protected]", effectiveDate: "2010-08-09"}, schema);

console.log(error);

i just need to add minimum and maximum to effectiveDate attribute, i know that i can add it in types to be like that date: Joi.date().min('12-21-2012') but this is a bad soln as maybe i have more than one attribute with type of date ?!!

Resolving of `not` is incorrect

Currently, the not resolution assumes that it is an array

enjoi/lib/resolver.js

Lines 195 to 201 in abdd705

resolveNot(schema) {
Hoek.assert(Util.isArray(schema.not), 'Expected Not to be an array.');
return this.joi.alternatives().conditional(Joi.alternatives().try(...schema.not.map((schema) => {
return this.resolve(schema);
})), { then: this.joi.any().forbidden(), otherwise: this.joi.any() });
}

However, according to OAS, not is an object, not an array. (https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/)

Bug: oneOf type makes field required

Current behavior:
The oneOf type makes a field required.

Proper behavior:
The oneOf type should make no changes to the required state of the field.

Explanation:
The oneOf type is a variation of subschema boolean logic. It's difference to anyOf which works properly in enjoi is that an instance should match exactly one of the subschemas. If it matches two or more - validation fails. But this is only if the instance value is present. That is, if the field is oneOf, but is not included as a required field it should pass validation.

Date validation does not work properly

Current behavior:
The date and date-time string formats validate values according to Joi's built in date type using Joi.date()

Expected behavior:
The validation should be done with different regexes for each of the formats. I.e. using Joi.string().regex()

Explanation:
TLDR: The Joi.date() type validates against ISO8601, which is less restrictive than JSON Schema's RFC3339 date format, rendering it inappropriate.

Details: The JSON Schema specification defines the date string formats to be: date, time or date-time, referring to RFC3339's date and time definitions. RFC3339 in turn refers to ISO8601, but uses only a specified restricted subset of it. Joi.date() validates against all ISO8601 valid formats, which means it will pass more than it has to. Joi.date() even accepts JavaScript Date objects and timestamp numbers.
The proper way to validate is using a Joi string schema with the appropriate regex.

Missing license

This looks like a cool project but I'm unable to use it due to the missing license. Under which license is the code published?

handle cases where `type` keyword is undefined or `type` is an array

JSONSchemas without the type keyword are still valid, eg:

{ format: 'date-time' }

is a valid JSONSchema that matches against any non-string value, or any string value that is date-time formatted.

Additionally, type can be an array of types. In this way a single JSONSchema can do complex validations of multiple types of value without using combination keywords (allOf, etc.). As an example:

{
  type: ['string', 'integer']
  minLength: 5
  maximum: 10
}

The above would successfully match a string with length 5 or greater, or a integer less than 10.

Since these are valid JSONSchema cases they should ideally be supported by Enjoi, but it would require notable changes to how JSONSchemas are translated to Joi. Application of validation keywords is currently tied to the presence of an expected type value.

For cases where there is no explicit type Joi conditional validations would need to be used, something like:

Joi.any().when({ is: Joi.string(), then: Joi.date(), otherwise: Joi.any() })

Version 7.0.0 from npm still uses `@hapi/joi` instead `joi` library

I upgrade to latest version 7.0.0 But I still have conflicts that node_modules/enjoi/index.js links to @hapi/joi also the npm website still specifies @hapi/joi as a dev dependency
image

Am I doing something wrong? I already removed node modules and deleted yarn cache.
Or was there an older version released as 7.0.0 on npm?

Parameter name is undefined

Hi,

If name property is not present in schema, field name in thrown error (by Joi) is undefined.

It may not be a bug (name property is already missing), but during conversion Enjoi may use property key as field name if it is not present.

Sample code:

const Joi = require("joi");
const Enjoi = require("enjoi");

const jsonSchema = {
  title: "User Detailed Output",
  allOf: [
    {
      title: "User Basic Output",
      allOf: [
        {
          type: "object",
          properties: {
            id: { type: "string", format: "uuid", description: "Kaydฤฑn ID'si." },
          },
          required: ["id"],
        },
        {
          title: "User Common",
          type: "object",
          properties: {
            businessUnitId: {
              type: "string",
              format: "uuid",
            },
          },
        },
      ],
    },
    { type: "object" },
  ],
  $schema: "http://json-schema.org/draft-04/schema#",
};

Joi.assert({}, Enjoi.schema(jsonSchema));

Actual Output

ValidationError: {
  "id" [1]: -- missing --
}

[1] "undefined" is required

Expected Output

ValidationError: {
  "id" [1]: -- missing --
}

[1] "id" is required

Allow empty strings.

Hey,

I have a problem where empty string is blocked by Joi (this is the default behavior unless allow('') is stated),

Any ideas where we can configure whether empty strings are allowed or not? maybe a general option such allowEmptyStrings: true ?

Thanks.

"additionalProperties" in "allOf" does not work

Hi,

When additionalProperties is used with allOf, it does not work.

Sample Code

const Joi = require("joi");
const Enjoi = require("enjoi");

const jsonSchema = {
  allOf: [
    {
      allOf: [
        {
          type: "object",
          additionalProperties: false,
          properties: {
            a: { type: "string" },
            b: { type: "string" },
          },
        },
        {
          type: "object",
          properties: {
            c: { type: "string" },
            d: { type: "string" },
          },
        },
      ],
    },
  ],
};

const jsonSchema2 = {
  type: "object",
  properties: {
    a: { type: "string" },
    b: { type: "string" },
  },
};

const result = Joi.validate({ z: "3" }, Enjoi.schema(jsonSchema), { abortEarly: false });
const result2 = Joi.validate({ z: "4" }, Enjoi.schema(jsonSchema2), { abortEarly: false });

console.log(result.error);
console.log(result2.error);

Actual Result

null
{ ValidationError: "z" is not allowed ...}

Expected Result

{ ValidationError: "z" is not allowed ...}
{ ValidationError: "z" is not allowed ...}

Kind Regards,

Allowing empty strings by default for string types

The regularString method makes, by default, an empty string a valid value for almost all string formats. This is a bug, since an empty string is not a valid value for e.g. an ipv4 string (which by the JSON Schema spec should be in "dotted quad" format as per RFC 2673).

Seems like the original reasoning behind this is that by default joi marks an empty string as an invalid value for any string type schema. But as I pointed out this breaks the logic for most string formats.

Schema allows empty strings

By default, the schemas created by enjoi allow empty strings. For example:

const schema = Enjoi({
    type: 'object',
    properties: {
        firstName: {
            description: 'First name.',
            type: 'string'
        },
        lastName: {
            description: 'Last name.',
            type: 'string'
        },
        age: {
            description: 'Age in years',
            type: 'integer',
            minimum: 1
        }
    },
    'required': ['firstName', 'lastName']
});

schema.validate({
  firstName: '',
  lastName: 'Smith'
}); // Does not return an error

Is there a way of disabling this so that the above validation would fail?

Nullable fields

JSON schema seems to support nullable fields through an array of types. e.g. some_property: { type: 'string' } nullable would be some_property: { type: [ 'string', 'null' ] }.

The array is auto joined to 'string,null' and since no type exists this fails.

Doing Enjoiy(..., { types: { 'string,null': Joi.string().allow(null) } }) seems to be a workaround.

Can't pass abortEarly (or other options) down to joi via schema.validate

Hi,

Joi supports abortEarly flag to return only first error that occured when validating the schema. This is by default set to true. This can be passed to joi passed via:

Joi.validate({firstName: 'John', lastName: 'Doe', age: 45}, schema, { abortEarly: false } , function (error, value) {
    error && console.log(error);
});

When using schema.validate() we lose the option to pass in options down to Joi:

schema.validate({firstName: 'John', lastName: 'Doe', age: 45}, function (error, value) {
    error && console.log(error);
});

Without having to add Joi as dependency, is there a away for enjoi to pass options down to joi?

[Feature] Additional joi validation

Description:
Would you consider a PR for the ability to add additional Joi validation to the converted schemas?
Joi supports this using schema concatenation. So it will be something like:

resolve(schema = this.root, joiSchema = this.additionalValidation) {
    if (schema.type) {
        return joiSchema ?
            this.resolveType(schema).concat(joiSchema) :
            this.resolveType(schema);
    }
...

This will give great flexibility to Enjoi by enabling the use of the full potential of Joi.

Background:
In our project we need to have more precise validation than what JSON Schema provides. E.g. for dates we want not only to match the YYYY-MM-DD format from RFC3339 Section 5.6, but also apply the restrictions from Section 5.7 - forbid the 31st day for Feb, Apr, etc.

Error when using allOf

Hi,

enjoi 3.2.4

I got following error when using allOf:

ssertionError [ERR_ASSERTION]: Expected allOf item to be an array or object.
    at new AssertionError (internal/assert.js:268:11)
    at Object.exports.assert (.../node_modules/hoek/lib/index.js:736:11)
    at .../node_modules/enjoi/lib/enjoi.js:187:18
    at Array.map (<anonymous>)
    at resolveAllOf (.../node_modules/enjoi/lib/enjoi.js:185:23)
    at resolve (.../node_modules/enjoi/lib/enjoi.js:38:20)
    at enjoi (.../node_modules/enjoi/lib/enjoi.js:410:12)
    at Object.<anonymous> (.../deneme.js:3:16)
    at Module._compile (internal/modules/cjs/loader.js:689:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)

Here is a minimal sample code: (I tested JsonSchema with a Json Validator and it works as expected)

const Enjoi = require("enjoi");

const schema = Enjoi({
  title: "Organization Input",
  allOf: [
    {
      title: "Organization Common",
      allOf: [
        {
          type: "object",
          properties: {
            name: { type: "string", maxLength: 40 },
            billingAddress: { type: "string", maxLength: 100 }
          },
          required: ["name"]
        },
        {
          type: "object",
          title: "Phone Number",
          properties: { phoneCountryCode: { type: "string", minLength: 1 } },
          required: ["phoneCountryCode"]
        }
      ]
    }
  ]
});

Kind regards,

Enjoi (joi?) is not strict for boolean

Enjoi({ type: 'boolean' }).validate(1, console.log) // valid; value === true
Enjoi({ type: 'boolean' }).validate('1', console.log) // valid; value === true

These should both be invalid or maybe add strict mode to options to enable it?

"abortEarly" does not work with "allOf"

When used with allOf, abortEarly does not work. I suppose it is related to Joi extension for allOf.

I pasted a minimal sample code below:

Example Code

const Joi = require("joi");
const Enjoi = require("enjoi");

const jsonSchema = {
  allOf: [
    {
      allOf: [
        {
          type: "object",
          properties: {
            a: { type: "string" },
            b: { type: "string" },
          },
          required: ["a", "b"],
        },
        {
          type: "object",
          properties: {
            c: { type: "string" },
            d: { type: "string" },
          },
          required: ["d"],
        },
      ],
    },
  ],
};

const jsonSchema2 = {
  type: "object",
  properties: {
    a: { type: "string" },
    b: { type: "string" },
  },
  required: ["a", "b"],
};

const result = Joi.validate({}, Enjoi.schema(jsonSchema), { abortEarly: false });
const result2 = Joi.validate({}, Enjoi.schema(jsonSchema2), { abortEarly: false });

console.log(result.error);
console.log(result2.error);

Actual Result

result.error has single error.
result2.error has multiple errors.

Expected Result

result.error has multiple error.
result2.error has multiple errors.

Kind Regards,

Circular dependency issue in api.json

Having an issue with running a swagger application. The api.json has an object which within itself has an array of the same objects. For example :

"Box": {
"id" : "boxid",
"boxes": {
"type" : "array",
"items" : {
$ref : "BOX";
}
}
}

This is causing the error
enjoi/node_modules/joi/lib/any.js:65
var obj = Object.create(Object.getPrototypeOf(this));
^
RangeError: Maximum call stack size exceeded

Usage of "when" for conditional validations

Thanks for your great library!

I am wondering if it would be possible to define conditional validation rules in a json schema, since I used this feature a lot with Joi.

Taken from their examples page:

const schema = {
    a: Joi.valid('a', 'b', 'other'),
    other: Joi.string()
        .when('a', { is: 'other', then: Joi.required() }),
};

Can such conditional validation rules be expressed via a static schema with enjoi?

Thanks!

Saving Joi schema

I have converted object using enjoi. How do I store it as file now?

const Enjoi       = require('enjoi')
const { convert } = require('json-to-json-schema')
const jsonSchema  = convert({ name:'Bob' })
const joiSchema   = Enjoi(jsonSchema)

// I want to store 'joiSchema' as file so that it would have content similar to this:
//
// const SCHEMA = Joi.object().keys({
//   name : Joi.string().required()
// })


[Bug] anyOf does not mark field as required

Hey there,

thanks for this useful lib, we hope to use it in our system very soon, however we ran into this bug:

Given the following schema:

{
  "type": "object",
  "properties": {
    "one": { "type": "number", "minimum": 0, "maximum": 10 },
    "two": { "type": "number", "minimum": 0, "maximum": 10 }
  },
  "anyOf": [
    { "required": ["one"] },
    { "required": ["two"] }
  ],
  "additionalProperties": false
}

When I run this code:

const enjoiSchema = Enjoi.schema(jsonSchema)
console.log(Joi.validate({}, enjoiSchema).error === null)

I'd expect this to be false, however it returns true. If I take the same schema and run it through ajv or jsonschema, I get the expected results, so I don't think there's anything wrong with the schema.

If I write the same schema directly in Joi, like this:

Joi.object()
  .keys({
    one: Joi.number().positive().min(0).max(10),
    two: Joi.number().positive().min(0).max(10)
  })
  .or('one', 'two')

it also works as expected and does not see the empty object as valid.

Thanks again for building Enjoi, we hope to enjoy it very soon ;-)

schema missing a 'type' or '$ref' or 'enum'

Hi,

JSON schema simplified below throws warning and does not create Joi object.

I'm not sure if this is a bug, or whether object type is default, but I validated schema using several validators and they worked as expected. ('type', '$ref', 'enum' seems not to be mandatory)

Sample Code

const Joi = require("joi");
const Enjoi = require("enjoi");

const jsonSchema = { 
  "$schema": "http://json-schema.org/draft-04/schema#",
  "properties": {
    "page": { "type": "integer" },
    "limit": { "type": "integer" }
  }
}

Joi.assert({ limit: "abc" }, Enjoi.schema(jsonSchema));

Actual Result

schema missing a 'type' or '$ref' or 'enum'

Expected Result

Joi assertion error

Thanks,

Nested Object

Hi there,
I'm trying to do a schema with nested objects and I want to make some fields inside the object required.
As an example,

type: 'object',
person:{
    type: 'object',
    description:{
        firstName:{
               type: 'string',
               length: 2           
                         }
 }
}

How can i make 'firstName' required?
cumps

Joi dependency in wrong section

@hapi/joi is being used in the main code file index.js and therefor should be listed as a dependency instead of a devDependency.

The issue particularly affects you when you are already using the latest version of @hapi/joi in a project. The latest version has breaking changes to the version enjoi needs. Enjoi has no problem using the latest version you may have installed, instead of it's required version ^14. When enjoi tries to run Joi.validate a TypeError is thrown as that function has been removed.

Support UUID validation

To validate UUID (GUID), It is only add the case in the switch of current.format
case 'uuid':
joischema = Joi.string().guid();
break;

This is not supported today.

allow additionalProperties by default

Hey maintainers, reviewing the history of this project I can see that when additionalProperties support was introduced, it was written so that the default value was false. This was done to maintain consistency with the preceding versions of enjoi which had previously always generated validators that errored on additional properties.

However, the JSONSchema specification indicates that additionalProperties default value should be true. As a result, new enjoi users who have large amounts of JSONSchemas they wish to integrate with joi will have this undocumented inconsistency waiting for them. When they discover it, the only way to resolve it is add additionalProperties: true to all objects schemas.

In a large organization, with many schemas belonging to a multitude of teams, updating all object schemas to explicitly state additionalProperties: true in order to use enjoi is a big ask.

Would you consider a PR for a new major version that would change this behavior to be consistent with the JSONSchema spec? We may be able to provide a toggle so that users could still opt into the old non-standard behavior.

Thanks for your work on this library and for considering this request

doesn't handle schemas that use more than one of `type`, `anyOf`, `allOf`

Enjoi doesn't attempt to create Joi validators for valid schemas like:

{
  type: "object",
  required: ['foo'],
  properties: {
    "foo": { type: "string" } 
  },
  oneOf: [
    { 
      type: "object",
      required: ['bar'],
      properties: {
        "bar": { type: "string" } 
      }
    },
    { 
      type: "object",
      properties: {
        "baz": { type: "string" } 
      }    
    }
  ]
}

It will only create the validator for the base object including the foo property. It ignores the additional mutually exclusive properties expressed in the oneOf.

Invalid data like { foo: 'a', bar: 'b', baz: 'c' } passes the validator returned.

Nullable array throws error

#25 added support for array of types but array throws:

 Enjoi({ type: [ 'array', 'null' ] })
TypeError: Cannot read property 'type' of undefined
    at resolve (..../node_modules/enjoi/lib/enjoi.js:21:20)

Undefined is always valid regardless of type

All types are incorrectly reporting undefined as valid. Array is the exception because of #33

Enjoi({ type: 'string' }).validate(undefined, console.log.bind(console, 'string'));
Enjoi({ type: 'array' }).validate(undefined, console.log.bind(console, 'array'));
Enjoi({ type: 'boolean' }).validate(undefined, console.log.bind(console, 'boolean'));
Enjoi({ type: 'number' }).validate(undefined, console.log.bind(console, 'number'));
Enjoi({ type: 'integer' }).validate(undefined, console.log.bind(console, 'integer'));
Enjoi({ type: 'object' }).validate(undefined, console.log.bind(console, 'object'));
Enjoi({ type: 'string' }).validate(undefined, console.log.bind(console, 'string'));

No ValidationError for any of those and the value returned is undefined.

(Sorry for the deluge!)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.