You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					624 lines
				
				18 KiB
			
		
		
			
		
	
	
					624 lines
				
				18 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								var genobj = require('generate-object-property')
							 | 
						||
| 
								 | 
							
								var genfun = require('generate-function')
							 | 
						||
| 
								 | 
							
								var jsonpointer = require('jsonpointer')
							 | 
						||
| 
								 | 
							
								var xtend = require('xtend')
							 | 
						||
| 
								 | 
							
								var formats = require('./formats')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var get = function(obj, additionalSchemas, ptr) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var visit = function(sub) {
							 | 
						||
| 
								 | 
							
								    if (sub && sub.id === ptr) return sub
							 | 
						||
| 
								 | 
							
								    if (typeof sub !== 'object' || !sub) return null
							 | 
						||
| 
								 | 
							
								    return Object.keys(sub).reduce(function(res, k) {
							 | 
						||
| 
								 | 
							
								      return res || visit(sub[k])
							 | 
						||
| 
								 | 
							
								    }, null)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var res = visit(obj)
							 | 
						||
| 
								 | 
							
								  if (res) return res
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  ptr = ptr.replace(/^#/, '')
							 | 
						||
| 
								 | 
							
								  ptr = ptr.replace(/\/$/, '')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  try {
							 | 
						||
| 
								 | 
							
								    return jsonpointer.get(obj, decodeURI(ptr))
							 | 
						||
| 
								 | 
							
								  } catch (err) {
							 | 
						||
| 
								 | 
							
								    var end = ptr.indexOf('#')
							 | 
						||
| 
								 | 
							
								    var other
							 | 
						||
| 
								 | 
							
								    // external reference
							 | 
						||
| 
								 | 
							
								    if (end !== 0) {
							 | 
						||
| 
								 | 
							
								      // fragment doesn't exist.
							 | 
						||
| 
								 | 
							
								      if (end === -1) {
							 | 
						||
| 
								 | 
							
								        other = additionalSchemas[ptr]
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        var ext = ptr.slice(0, end)
							 | 
						||
| 
								 | 
							
								        other = additionalSchemas[ext]
							 | 
						||
| 
								 | 
							
								        var fragment = ptr.slice(end).replace(/^#/, '')
							 | 
						||
| 
								 | 
							
								        try {
							 | 
						||
| 
								 | 
							
								          return jsonpointer.get(other, fragment)
							 | 
						||
| 
								 | 
							
								        } catch (err) {}
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      other = additionalSchemas[ptr]
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    return other || null
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var types = {}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								types.any = function() {
							 | 
						||
| 
								 | 
							
								  return 'true'
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								types.null = function(name) {
							 | 
						||
| 
								 | 
							
								  return name+' === null'
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								types.boolean = function(name) {
							 | 
						||
| 
								 | 
							
								  return 'typeof '+name+' === "boolean"'
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								types.array = function(name) {
							 | 
						||
| 
								 | 
							
								  return 'Array.isArray('+name+')'
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								types.object = function(name) {
							 | 
						||
| 
								 | 
							
								  return 'typeof '+name+' === "object" && '+name+' && !Array.isArray('+name+')'
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								types.number = function(name) {
							 | 
						||
| 
								 | 
							
								  return 'typeof '+name+' === "number" && isFinite('+name+')'
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								types.integer = function(name) {
							 | 
						||
| 
								 | 
							
								  return 'typeof '+name+' === "number" && (Math.floor('+name+') === '+name+' || '+name+' > 9007199254740992 || '+name+' < -9007199254740992)'
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								types.string = function(name) {
							 | 
						||
| 
								 | 
							
								  return 'typeof '+name+' === "string"'
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var unique = function(array, len) {
							 | 
						||
| 
								 | 
							
								  len = Math.min(len === -1 ? array.length : len, array.length)
							 | 
						||
| 
								 | 
							
								  var list = []
							 | 
						||
| 
								 | 
							
								  for (var i = 0; i < len; i++) {
							 | 
						||
| 
								 | 
							
								    list.push(typeof array[i] === 'object' ? JSON.stringify(array[i]) : array[i])
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  for (var i = 1; i < list.length; i++) {
							 | 
						||
| 
								 | 
							
								    if (list.indexOf(list[i]) !== i) return false
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return true
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var isMultipleOf = function(name, multipleOf) {
							 | 
						||
| 
								 | 
							
								  var res;
							 | 
						||
| 
								 | 
							
								  var factor = ((multipleOf | 0) !== multipleOf) ? Math.pow(10, multipleOf.toString().split('.').pop().length) : 1
							 | 
						||
| 
								 | 
							
								  if (factor > 1) {
							 | 
						||
| 
								 | 
							
								    var factorName = ((name | 0) !== name) ? Math.pow(10, name.toString().split('.').pop().length) : 1
							 | 
						||
| 
								 | 
							
								    if (factorName > factor) res = true
							 | 
						||
| 
								 | 
							
								    else res = Math.round(factor * name) % (factor * multipleOf)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  else res = name % multipleOf;
							 | 
						||
| 
								 | 
							
								  return !res;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var testLimitedRegex = function (r, s, maxLength) {
							 | 
						||
| 
								 | 
							
								  if (maxLength > -1 && s.length > maxLength) return true
							 | 
						||
| 
								 | 
							
								  return r.test(s)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var compile = function(schema, cache, root, reporter, opts) {
							 | 
						||
| 
								 | 
							
								  var fmts = opts ? xtend(formats, opts.formats) : formats
							 | 
						||
| 
								 | 
							
								  var scope = {unique:unique, formats:fmts, isMultipleOf:isMultipleOf, testLimitedRegex:testLimitedRegex}
							 | 
						||
| 
								 | 
							
								  var verbose = opts ? !!opts.verbose : false;
							 | 
						||
| 
								 | 
							
								  var greedy = opts && opts.greedy !== undefined ?
							 | 
						||
| 
								 | 
							
								    opts.greedy : false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var syms = {}
							 | 
						||
| 
								 | 
							
								  var allocated = []
							 | 
						||
| 
								 | 
							
								  var gensym = function(name) {
							 | 
						||
| 
								 | 
							
								    var res = name+(syms[name] = (syms[name] || 0)+1)
							 | 
						||
| 
								 | 
							
								    allocated.push(res)
							 | 
						||
| 
								 | 
							
								    return res
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var formatName = function(field) {
							 | 
						||
| 
								 | 
							
								    var s = JSON.stringify(field)
							 | 
						||
| 
								 | 
							
								    try {
							 | 
						||
| 
								 | 
							
								      var pattern = /\[([^\[\]"]+)\]/
							 | 
						||
| 
								 | 
							
								      while (pattern.test(s)) s = s.replace(pattern, replacer)
							 | 
						||
| 
								 | 
							
								      return s
							 | 
						||
| 
								 | 
							
								    } catch (_) {
							 | 
						||
| 
								 | 
							
								      return JSON.stringify(field)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    function replacer (match, v) {
							 | 
						||
| 
								 | 
							
								      if (allocated.indexOf(v) === -1) throw new Error('Unreplaceable')
							 | 
						||
| 
								 | 
							
								      return '." + ' + v + ' + "'
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var reversePatterns = {}
							 | 
						||
| 
								 | 
							
								  var patterns = function(p) {
							 | 
						||
| 
								 | 
							
								    if (reversePatterns[p]) return reversePatterns[p]
							 | 
						||
| 
								 | 
							
								    var n = gensym('pattern')
							 | 
						||
| 
								 | 
							
								    scope[n] = new RegExp(p)
							 | 
						||
| 
								 | 
							
								    reversePatterns[p] = n
							 | 
						||
| 
								 | 
							
								    return n
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var vars = ['i','j','k','l','m','n','o','p','q','r','s','t','u','v','x','y','z']
							 | 
						||
| 
								 | 
							
								  var genloop = function() {
							 | 
						||
| 
								 | 
							
								    var v = vars.shift()
							 | 
						||
| 
								 | 
							
								    vars.push(v+v[0])
							 | 
						||
| 
								 | 
							
								    allocated.push(v)
							 | 
						||
| 
								 | 
							
								    return v
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var visit = function(name, node, reporter, filter, schemaPath) {
							 | 
						||
| 
								 | 
							
								    var properties = node.properties
							 | 
						||
| 
								 | 
							
								    var type = node.type
							 | 
						||
| 
								 | 
							
								    var tuple = false
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (Array.isArray(node.items)) { // tuple type
							 | 
						||
| 
								 | 
							
								      properties = {}
							 | 
						||
| 
								 | 
							
								      node.items.forEach(function(item, i) {
							 | 
						||
| 
								 | 
							
								        properties[i] = item
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								      type = 'array'
							 | 
						||
| 
								 | 
							
								      tuple = true
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var indent = 0
							 | 
						||
| 
								 | 
							
								    var error = function(msg, prop, value) {
							 | 
						||
| 
								 | 
							
								      validate('errors++')
							 | 
						||
| 
								 | 
							
								      if (reporter === true) {
							 | 
						||
| 
								 | 
							
								        validate('if (validate.errors === null) validate.errors = []')
							 | 
						||
| 
								 | 
							
								        if (verbose) {
							 | 
						||
| 
								 | 
							
								          validate(
							 | 
						||
| 
								 | 
							
								            'validate.errors.push({field:%s,message:%s,value:%s,type:%s,schemaPath:%s})',
							 | 
						||
| 
								 | 
							
								            formatName(prop || name),
							 | 
						||
| 
								 | 
							
								            JSON.stringify(msg),
							 | 
						||
| 
								 | 
							
								            value || name,
							 | 
						||
| 
								 | 
							
								            JSON.stringify(type),
							 | 
						||
| 
								 | 
							
								            JSON.stringify(schemaPath)
							 | 
						||
| 
								 | 
							
								          )
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								          validate('validate.errors.push({field:%s,message:%s})', formatName(prop || name), JSON.stringify(msg))
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.required === true) {
							 | 
						||
| 
								 | 
							
								      indent++
							 | 
						||
| 
								 | 
							
								      validate('if (%s === undefined) {', name)
							 | 
						||
| 
								 | 
							
								      error('is required')
							 | 
						||
| 
								 | 
							
								      validate('} else {')
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      indent++
							 | 
						||
| 
								 | 
							
								      validate('if (%s !== undefined) {', name)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    var valid = [].concat(type)
							 | 
						||
| 
								 | 
							
								      .map(function(t) {
							 | 
						||
| 
								 | 
							
								        if (t && !types.hasOwnProperty(t)) {
							 | 
						||
| 
								 | 
							
								          throw new Error('Unknown type: ' + t)
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return types[t || 'any'](name)
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								      .join(' || ') || 'true'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (valid !== 'true') {
							 | 
						||
| 
								 | 
							
								      indent++
							 | 
						||
| 
								 | 
							
								      validate('if (!(%s)) {', valid)
							 | 
						||
| 
								 | 
							
								      error('is the wrong type')
							 | 
						||
| 
								 | 
							
								      validate('} else {')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (tuple) {
							 | 
						||
| 
								 | 
							
								      if (node.additionalItems === false) {
							 | 
						||
| 
								 | 
							
								        validate('if (%s.length > %d) {', name, node.items.length)
							 | 
						||
| 
								 | 
							
								        error('has additional items')
							 | 
						||
| 
								 | 
							
								        validate('}')
							 | 
						||
| 
								 | 
							
								      } else if (node.additionalItems) {
							 | 
						||
| 
								 | 
							
								        var i = genloop()
							 | 
						||
| 
								 | 
							
								        validate('for (var %s = %d; %s < %s.length; %s++) {', i, node.items.length, i, name, i)
							 | 
						||
| 
								 | 
							
								        visit(name+'['+i+']', node.additionalItems, reporter, filter, schemaPath.concat('additionalItems'))
							 | 
						||
| 
								 | 
							
								        validate('}')
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.format && fmts[node.format]) {
							 | 
						||
| 
								 | 
							
								      if (type !== 'string' && formats[node.format]) validate('if (%s) {', types.string(name))
							 | 
						||
| 
								 | 
							
								      var n = gensym('format')
							 | 
						||
| 
								 | 
							
								      scope[n] = fmts[node.format]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (typeof scope[n] === 'function') validate('if (!%s(%s)) {', n, name)
							 | 
						||
| 
								 | 
							
								      else validate('if (!testLimitedRegex(%s, %s, %d)) {', n, name, typeof node.maxLength === 'undefined' ? -1 : node.maxLength)
							 | 
						||
| 
								 | 
							
								      error('must be '+node.format+' format')
							 | 
						||
| 
								 | 
							
								      validate('}')
							 | 
						||
| 
								 | 
							
								      if (type !== 'string' && formats[node.format]) validate('}')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (Array.isArray(node.required)) {
							 | 
						||
| 
								 | 
							
								      var n = gensym('missing')
							 | 
						||
| 
								 | 
							
								      validate('var %s = 0', n)
							 | 
						||
| 
								 | 
							
								      var checkRequired = function (req) {
							 | 
						||
| 
								 | 
							
								        var prop = genobj(name, req);
							 | 
						||
| 
								 | 
							
								        validate('if (%s === undefined) {', prop)
							 | 
						||
| 
								 | 
							
								        error('is required', prop)
							 | 
						||
| 
								 | 
							
								        validate('%s++', n)
							 | 
						||
| 
								 | 
							
								        validate('}')
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      validate('if ((%s)) {', type !== 'object' ? types.object(name) : 'true')
							 | 
						||
| 
								 | 
							
								      node.required.map(checkRequired)
							 | 
						||
| 
								 | 
							
								      validate('}');
							 | 
						||
| 
								 | 
							
								      if (!greedy) {
							 | 
						||
| 
								 | 
							
								        validate('if (%s === 0) {', n)
							 | 
						||
| 
								 | 
							
								        indent++
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.uniqueItems) {
							 | 
						||
| 
								 | 
							
								      if (type !== 'array') validate('if (%s) {', types.array(name))
							 | 
						||
| 
								 | 
							
								      validate('if (!(unique(%s, %d))) {', name, node.maxItems || -1)
							 | 
						||
| 
								 | 
							
								      error('must be unique')
							 | 
						||
| 
								 | 
							
								      validate('}')
							 | 
						||
| 
								 | 
							
								      if (type !== 'array') validate('}')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.enum) {
							 | 
						||
| 
								 | 
							
								      var complex = node.enum.some(function(e) {
							 | 
						||
| 
								 | 
							
								        return typeof e === 'object'
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var compare = complex ?
							 | 
						||
| 
								 | 
							
								        function(e) {
							 | 
						||
| 
								 | 
							
								          return 'JSON.stringify('+name+')'+' !== JSON.stringify('+JSON.stringify(e)+')'
							 | 
						||
| 
								 | 
							
								        } :
							 | 
						||
| 
								 | 
							
								        function(e) {
							 | 
						||
| 
								 | 
							
								          return name+' !== '+JSON.stringify(e)
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      validate('if (%s) {', node.enum.map(compare).join(' && ') || 'false')
							 | 
						||
| 
								 | 
							
								      error('must be an enum value')
							 | 
						||
| 
								 | 
							
								      validate('}')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.dependencies) {
							 | 
						||
| 
								 | 
							
								      if (type !== 'object') validate('if (%s) {', types.object(name))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      Object.keys(node.dependencies).forEach(function(key) {
							 | 
						||
| 
								 | 
							
								        var deps = node.dependencies[key]
							 | 
						||
| 
								 | 
							
								        if (typeof deps === 'string') deps = [deps]
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        var exists = function(k) {
							 | 
						||
| 
								 | 
							
								          return genobj(name, k) + ' !== undefined'
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (Array.isArray(deps)) {
							 | 
						||
| 
								 | 
							
								          validate('if (%s !== undefined && !(%s)) {', genobj(name, key), deps.map(exists).join(' && ') || 'true')
							 | 
						||
| 
								 | 
							
								          error('dependencies not set')
							 | 
						||
| 
								 | 
							
								          validate('}')
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        if (typeof deps === 'object') {
							 | 
						||
| 
								 | 
							
								          validate('if (%s !== undefined) {', genobj(name, key))
							 | 
						||
| 
								 | 
							
								          visit(name, deps, reporter, filter, schemaPath.concat(['dependencies', key]))
							 | 
						||
| 
								 | 
							
								          validate('}')
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (type !== 'object') validate('}')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.additionalProperties || node.additionalProperties === false) {
							 | 
						||
| 
								 | 
							
								      if (type !== 'object') validate('if (%s) {', types.object(name))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var i = genloop()
							 | 
						||
| 
								 | 
							
								      var keys = gensym('keys')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var toCompare = function(p) {
							 | 
						||
| 
								 | 
							
								        return keys+'['+i+'] !== '+JSON.stringify(p)
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var toTest = function(p) {
							 | 
						||
| 
								 | 
							
								        return '!'+patterns(p)+'.test('+keys+'['+i+'])'
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var additionalProp = Object.keys(properties || {}).map(toCompare)
							 | 
						||
| 
								 | 
							
								        .concat(Object.keys(node.patternProperties || {}).map(toTest))
							 | 
						||
| 
								 | 
							
								        .join(' && ') || 'true'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      validate('var %s = Object.keys(%s)', keys, name)
							 | 
						||
| 
								 | 
							
								        ('for (var %s = 0; %s < %s.length; %s++) {', i, i, keys, i)
							 | 
						||
| 
								 | 
							
								          ('if (%s) {', additionalProp)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (node.additionalProperties === false) {
							 | 
						||
| 
								 | 
							
								        if (filter) validate('delete %s', name+'['+keys+'['+i+']]')
							 | 
						||
| 
								 | 
							
								        error('has additional properties', null, JSON.stringify(name+'.') + ' + ' + keys + '['+i+']')
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        visit(name+'['+keys+'['+i+']]', node.additionalProperties, reporter, filter, schemaPath.concat(['additionalProperties']))
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      validate
							 | 
						||
| 
								 | 
							
								          ('}')
							 | 
						||
| 
								 | 
							
								        ('}')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (type !== 'object') validate('}')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.$ref) {
							 | 
						||
| 
								 | 
							
								      var sub = get(root, opts && opts.schemas || {}, node.$ref)
							 | 
						||
| 
								 | 
							
								      if (sub) {
							 | 
						||
| 
								 | 
							
								        var fn = cache[node.$ref]
							 | 
						||
| 
								 | 
							
								        if (!fn) {
							 | 
						||
| 
								 | 
							
								          cache[node.$ref] = function proxy(data) {
							 | 
						||
| 
								 | 
							
								            return fn(data)
							 | 
						||
| 
								 | 
							
								          }
							 | 
						||
| 
								 | 
							
								          fn = compile(sub, cache, root, false, opts)
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        var n = gensym('ref')
							 | 
						||
| 
								 | 
							
								        scope[n] = fn
							 | 
						||
| 
								 | 
							
								        validate('if (!(%s(%s))) {', n, name)
							 | 
						||
| 
								 | 
							
								        error('referenced schema does not match')
							 | 
						||
| 
								 | 
							
								        validate('}')
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.not) {
							 | 
						||
| 
								 | 
							
								      var prev = gensym('prev')
							 | 
						||
| 
								 | 
							
								      validate('var %s = errors', prev)
							 | 
						||
| 
								 | 
							
								      visit(name, node.not, false, filter, schemaPath.concat('not'))
							 | 
						||
| 
								 | 
							
								      validate('if (%s === errors) {', prev)
							 | 
						||
| 
								 | 
							
								      error('negative schema matches')
							 | 
						||
| 
								 | 
							
								      validate('} else {')
							 | 
						||
| 
								 | 
							
								        ('errors = %s', prev)
							 | 
						||
| 
								 | 
							
								      ('}')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.items && !tuple) {
							 | 
						||
| 
								 | 
							
								      if (type !== 'array') validate('if (%s) {', types.array(name))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      var i = genloop()
							 | 
						||
| 
								 | 
							
								      validate('for (var %s = 0; %s < %s.length; %s++) {', i, i, name, i)
							 | 
						||
| 
								 | 
							
								      visit(name+'['+i+']', node.items, reporter, filter, schemaPath.concat('items'))
							 | 
						||
| 
								 | 
							
								      validate('}')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (type !== 'array') validate('}')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.patternProperties) {
							 | 
						||
| 
								 | 
							
								      if (type !== 'object') validate('if (%s) {', types.object(name))
							 | 
						||
| 
								 | 
							
								      var keys = gensym('keys')
							 | 
						||
| 
								 | 
							
								      var i = genloop()
							 | 
						||
| 
								 | 
							
								      validate
							 | 
						||
| 
								 | 
							
								        ('var %s = Object.keys(%s)', keys, name)
							 | 
						||
| 
								 | 
							
								        ('for (var %s = 0; %s < %s.length; %s++) {', i, i, keys, i)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      Object.keys(node.patternProperties).forEach(function(key) {
							 | 
						||
| 
								 | 
							
								        var p = patterns(key)
							 | 
						||
| 
								 | 
							
								        validate('if (%s.test(%s)) {', p, keys+'['+i+']')
							 | 
						||
| 
								 | 
							
								        visit(name+'['+keys+'['+i+']]', node.patternProperties[key], reporter, filter, schemaPath.concat(['patternProperties', key]))
							 | 
						||
| 
								 | 
							
								        validate('}')
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      validate('}')
							 | 
						||
| 
								 | 
							
								      if (type !== 'object') validate('}')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.pattern) {
							 | 
						||
| 
								 | 
							
								      var p = patterns(node.pattern)
							 | 
						||
| 
								 | 
							
								      if (type !== 'string') validate('if (%s) {', types.string(name))
							 | 
						||
| 
								 | 
							
								      validate('if (!(testLimitedRegex(%s, %s, %d))) {', p, name, typeof node.maxLength === 'undefined' ? -1 : node.maxLength)
							 | 
						||
| 
								 | 
							
								      error('pattern mismatch')
							 | 
						||
| 
								 | 
							
								      validate('}')
							 | 
						||
| 
								 | 
							
								      if (type !== 'string') validate('}')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.allOf) {
							 | 
						||
| 
								 | 
							
								      node.allOf.forEach(function(sch, key) {
							 | 
						||
| 
								 | 
							
								        visit(name, sch, reporter, filter, schemaPath.concat(['allOf', key]))
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.anyOf && node.anyOf.length) {
							 | 
						||
| 
								 | 
							
								      var prev = gensym('prev')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      node.anyOf.forEach(function(sch, i) {
							 | 
						||
| 
								 | 
							
								        if (i === 0) {
							 | 
						||
| 
								 | 
							
								          validate('var %s = errors', prev)
							 | 
						||
| 
								 | 
							
								        } else {
							 | 
						||
| 
								 | 
							
								          validate('if (errors !== %s) {', prev)
							 | 
						||
| 
								 | 
							
								            ('errors = %s', prev)
							 | 
						||
| 
								 | 
							
								        }
							 | 
						||
| 
								 | 
							
								        visit(name, sch, false, false, schemaPath)
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								      node.anyOf.forEach(function(sch, i) {
							 | 
						||
| 
								 | 
							
								        if (i) validate('}')
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								      validate('if (%s !== errors) {', prev)
							 | 
						||
| 
								 | 
							
								      error('no schemas match')
							 | 
						||
| 
								 | 
							
								      validate('}')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.oneOf && node.oneOf.length) {
							 | 
						||
| 
								 | 
							
								      var prev = gensym('prev')
							 | 
						||
| 
								 | 
							
								      var passes = gensym('passes')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      validate
							 | 
						||
| 
								 | 
							
								        ('var %s = errors', prev)
							 | 
						||
| 
								 | 
							
								        ('var %s = 0', passes)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      node.oneOf.forEach(function(sch, i) {
							 | 
						||
| 
								 | 
							
								        visit(name, sch, false, false, schemaPath)
							 | 
						||
| 
								 | 
							
								        validate('if (%s === errors) {', prev)
							 | 
						||
| 
								 | 
							
								          ('%s++', passes)
							 | 
						||
| 
								 | 
							
								        ('} else {')
							 | 
						||
| 
								 | 
							
								          ('errors = %s', prev)
							 | 
						||
| 
								 | 
							
								        ('}')
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      validate('if (%s !== 1) {', passes)
							 | 
						||
| 
								 | 
							
								      error('no (or more than one) schemas match')
							 | 
						||
| 
								 | 
							
								      validate('}')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.multipleOf !== undefined) {
							 | 
						||
| 
								 | 
							
								      if (type !== 'number' && type !== 'integer') validate('if (%s) {', types.number(name))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      validate('if (!isMultipleOf(%s, %d)) {', name, node.multipleOf)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      error('has a remainder')
							 | 
						||
| 
								 | 
							
								      validate('}')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (type !== 'number' && type !== 'integer') validate('}')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.maxProperties !== undefined) {
							 | 
						||
| 
								 | 
							
								      if (type !== 'object') validate('if (%s) {', types.object(name))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      validate('if (Object.keys(%s).length > %d) {', name, node.maxProperties)
							 | 
						||
| 
								 | 
							
								      error('has more properties than allowed')
							 | 
						||
| 
								 | 
							
								      validate('}')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (type !== 'object') validate('}')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.minProperties !== undefined) {
							 | 
						||
| 
								 | 
							
								      if (type !== 'object') validate('if (%s) {', types.object(name))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      validate('if (Object.keys(%s).length < %d) {', name, node.minProperties)
							 | 
						||
| 
								 | 
							
								      error('has less properties than allowed')
							 | 
						||
| 
								 | 
							
								      validate('}')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (type !== 'object') validate('}')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.maxItems !== undefined) {
							 | 
						||
| 
								 | 
							
								      if (type !== 'array') validate('if (%s) {', types.array(name))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      validate('if (%s.length > %d) {', name, node.maxItems)
							 | 
						||
| 
								 | 
							
								      error('has more items than allowed')
							 | 
						||
| 
								 | 
							
								      validate('}')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (type !== 'array') validate('}')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.minItems !== undefined) {
							 | 
						||
| 
								 | 
							
								      if (type !== 'array') validate('if (%s) {', types.array(name))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      validate('if (%s.length < %d) {', name, node.minItems)
							 | 
						||
| 
								 | 
							
								      error('has less items than allowed')
							 | 
						||
| 
								 | 
							
								      validate('}')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (type !== 'array') validate('}')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.maxLength !== undefined) {
							 | 
						||
| 
								 | 
							
								      if (type !== 'string') validate('if (%s) {', types.string(name))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      validate('if (%s.length > %d) {', name, node.maxLength)
							 | 
						||
| 
								 | 
							
								      error('has longer length than allowed')
							 | 
						||
| 
								 | 
							
								      validate('}')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (type !== 'string') validate('}')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.minLength !== undefined) {
							 | 
						||
| 
								 | 
							
								      if (type !== 'string') validate('if (%s) {', types.string(name))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      validate('if (%s.length < %d) {', name, node.minLength)
							 | 
						||
| 
								 | 
							
								      error('has less length than allowed')
							 | 
						||
| 
								 | 
							
								      validate('}')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (type !== 'string') validate('}')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.minimum !== undefined) {
							 | 
						||
| 
								 | 
							
								      if (type !== 'number' && type !== 'integer') validate('if (%s) {', types.number(name))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      validate('if (%s %s %d) {', name, node.exclusiveMinimum ? '<=' : '<', node.minimum)
							 | 
						||
| 
								 | 
							
								      error('is less than minimum')
							 | 
						||
| 
								 | 
							
								      validate('}')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (type !== 'number' && type !== 'integer') validate('}')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (node.maximum !== undefined) {
							 | 
						||
| 
								 | 
							
								      if (type !== 'number' && type !== 'integer') validate('if (%s) {', types.number(name))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      validate('if (%s %s %d) {', name, node.exclusiveMaximum ? '>=' : '>', node.maximum)
							 | 
						||
| 
								 | 
							
								      error('is more than maximum')
							 | 
						||
| 
								 | 
							
								      validate('}')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								      if (type !== 'number' && type !== 'integer') validate('}')
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (properties) {
							 | 
						||
| 
								 | 
							
								      Object.keys(properties).forEach(function(p) {
							 | 
						||
| 
								 | 
							
								        if (Array.isArray(type) && type.indexOf('null') !== -1) validate('if (%s !== null) {', name)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        visit(
							 | 
						||
| 
								 | 
							
								          genobj(name, p),
							 | 
						||
| 
								 | 
							
								          properties[p],
							 | 
						||
| 
								 | 
							
								          reporter,
							 | 
						||
| 
								 | 
							
								          filter,
							 | 
						||
| 
								 | 
							
								          schemaPath.concat(tuple ? p : ['properties', p])
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        if (Array.isArray(type) && type.indexOf('null') !== -1) validate('}')
							 | 
						||
| 
								 | 
							
								      })
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    while (indent--) validate('}')
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var validate = genfun
							 | 
						||
| 
								 | 
							
								    ('function validate(data) {')
							 | 
						||
| 
								 | 
							
								      // Since undefined is not a valid JSON value, we coerce to null and other checks will catch this
							 | 
						||
| 
								 | 
							
								      ('if (data === undefined) data = null')
							 | 
						||
| 
								 | 
							
								      ('validate.errors = null')
							 | 
						||
| 
								 | 
							
								      ('var errors = 0')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  visit('data', schema, reporter, opts && opts.filter, [])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  validate
							 | 
						||
| 
								 | 
							
								      ('return errors === 0')
							 | 
						||
| 
								 | 
							
								    ('}')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  validate = validate.toFunction(scope)
							 | 
						||
| 
								 | 
							
								  validate.errors = null
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  if (Object.defineProperty) {
							 | 
						||
| 
								 | 
							
								    Object.defineProperty(validate, 'error', {
							 | 
						||
| 
								 | 
							
								      get: function() {
							 | 
						||
| 
								 | 
							
								        if (!validate.errors) return ''
							 | 
						||
| 
								 | 
							
								        return validate.errors.map(function(err) {
							 | 
						||
| 
								 | 
							
								          return err.field + ' ' + err.message;
							 | 
						||
| 
								 | 
							
								        }).join('\n')
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								    })
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  validate.toJSON = function() {
							 | 
						||
| 
								 | 
							
								    return schema
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return validate
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports = function(schema, opts) {
							 | 
						||
| 
								 | 
							
								  if (typeof schema === 'string') schema = JSON.parse(schema)
							 | 
						||
| 
								 | 
							
								  return compile(schema, {}, schema, true, opts)
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								module.exports.filter = function(schema, opts) {
							 | 
						||
| 
								 | 
							
								  var validate = module.exports(schema, xtend(opts, {filter: true}))
							 | 
						||
| 
								 | 
							
								  return function(sch) {
							 | 
						||
| 
								 | 
							
								    validate(sch)
							 | 
						||
| 
								 | 
							
								    return sch
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 |