react / wstein / node_modules / jest-cli / node_modules / jsdom / node_modules / request / node_modules / har-validator / node_modules / is-my-json-valid / index.js
81151 viewsvar genobj = require('generate-object-property')1var genfun = require('generate-function')2var jsonpointer = require('jsonpointer')3var xtend = require('xtend')4var formats = require('./formats')56var get = function(obj, additionalSchemas, ptr) {7if (/^https?:\/\//.test(ptr)) return null89var visit = function(sub) {10if (sub && sub.id === ptr) return sub11if (typeof sub !== 'object' || !sub) return null12return Object.keys(sub).reduce(function(res, k) {13return res || visit(sub[k])14}, null)15}1617var res = visit(obj)18if (res) return res1920ptr = ptr.replace(/^#/, '')21ptr = ptr.replace(/\/$/, '')2223try {24return jsonpointer.get(obj, decodeURI(ptr))25} catch (err) {26var end = ptr.indexOf('#')27var other28// external reference29if (end !== 0) {30// fragment doesn't exist.31if (end === -1) {32other = additionalSchemas[ptr]33} else {34var ext = ptr.slice(0, end)35other = additionalSchemas[ext]36var fragment = ptr.slice(end).replace(/^#/, '')37try {38return jsonpointer.get(other, fragment)39} catch (err) {}40}41} else {42other = additionalSchemas[ptr]43}44return other || null45}46}4748var formatName = function(field) {49field = JSON.stringify(field)50var pattern = /\[([^\[\]"]+)\]/51while (pattern.test(field)) field = field.replace(pattern, '."+$1+"')52return field53}5455var types = {}5657types.any = function() {58return 'true'59}6061types.null = function(name) {62return name+' === null'63}6465types.boolean = function(name) {66return 'typeof '+name+' === "boolean"'67}6869types.array = function(name) {70return 'Array.isArray('+name+')'71}7273types.object = function(name) {74return 'typeof '+name+' === "object" && '+name+' && !Array.isArray('+name+')'75}7677types.number = function(name) {78return 'typeof '+name+' === "number"'79}8081types.integer = function(name) {82return 'typeof '+name+' === "number" && (Math.floor('+name+') === '+name+' || '+name+' > 9007199254740992 || '+name+' < -9007199254740992)'83}8485types.string = function(name) {86return 'typeof '+name+' === "string"'87}8889var unique = function(array) {90var list = []91for (var i = 0; i < array.length; i++) {92list.push(typeof array[i] === 'object' ? JSON.stringify(array[i]) : array[i])93}94for (var i = 1; i < list.length; i++) {95if (list.indexOf(list[i]) !== i) return false96}97return true98}99100var toType = function(node) {101return node.type102}103104var compile = function(schema, cache, root, reporter, opts) {105var fmts = opts ? xtend(formats, opts.formats) : formats106var scope = {unique:unique, formats:fmts}107var verbose = opts ? !!opts.verbose : false;108var greedy = opts && opts.greedy !== undefined ?109opts.greedy : false;110111var syms = {}112var gensym = function(name) {113return name+(syms[name] = (syms[name] || 0)+1)114}115116var reversePatterns = {}117var patterns = function(p) {118if (reversePatterns[p]) return reversePatterns[p]119var n = gensym('pattern')120scope[n] = new RegExp(p)121reversePatterns[p] = n122return n123}124125var vars = ['i','j','k','l','m','n','o','p','q','r','s','t','u','v','x','y','z']126var genloop = function() {127var v = vars.shift()128vars.push(v+v[0])129return v130}131132var visit = function(name, node, reporter, filter) {133var properties = node.properties134var type = node.type135var tuple = false136137if (Array.isArray(node.items)) { // tuple type138properties = {}139node.items.forEach(function(item, i) {140properties[i] = item141})142type = 'array'143tuple = true144}145146var indent = 0147var error = function(msg, prop, value) {148validate('errors++')149if (reporter === true) {150validate('if (validate.errors === null) validate.errors = []')151if (verbose) {152validate('validate.errors.push({field:%s,message:%s,value:%s})', formatName(prop || name), JSON.stringify(msg), value || name)153} else {154validate('validate.errors.push({field:%s,message:%s})', formatName(prop || name), JSON.stringify(msg))155}156}157}158159if (node.required === true) {160indent++161validate('if (%s === undefined) {', name)162error('is required')163validate('} else {')164} else {165indent++166validate('if (%s !== undefined) {', name)167}168169var valid = [].concat(type)170.map(function(t) {171return types[t || 'any'](name)172})173.join(' || ') || 'true'174175if (valid !== 'true') {176indent++177validate('if (!(%s)) {', valid)178error('is the wrong type')179validate('} else {')180}181182if (tuple) {183if (node.additionalItems === false) {184validate('if (%s.length > %d) {', name, node.items.length)185error('has additional items')186validate('}')187} else if (node.additionalItems) {188var i = genloop()189validate('for (var %s = %d; %s < %s.length; %s++) {', i, node.items.length, i, name, i)190visit(name+'['+i+']', node.additionalItems, reporter, filter)191validate('}')192}193}194195if (node.format && fmts[node.format]) {196if (type !== 'string' && formats[node.format]) validate('if (%s) {', types.string(name))197var n = gensym('format')198scope[n] = fmts[node.format]199200if (typeof scope[n] === 'function') validate('if (!%s(%s)) {', n, name)201else validate('if (!%s.test(%s)) {', n, name)202error('must be '+node.format+' format')203validate('}')204if (type !== 'string' && formats[node.format]) validate('}')205}206207if (Array.isArray(node.required)) {208var isUndefined = function(req) {209return genobj(name, req) + ' === undefined'210}211212var checkRequired = function (req) {213var prop = genobj(name, req);214validate('if (%s === undefined) {', prop)215error('is required', prop)216validate('missing++')217validate('}')218}219validate('if ((%s)) {', type !== 'object' ? types.object(name) : 'true')220validate('var missing = 0')221node.required.map(checkRequired)222validate('}');223if (!greedy) {224validate('if (missing === 0) {')225indent++226}227}228229if (node.uniqueItems) {230if (type !== 'array') validate('if (%s) {', types.array(name))231validate('if (!(unique(%s))) {', name)232error('must be unique')233validate('}')234if (type !== 'array') validate('}')235}236237if (node.enum) {238var complex = node.enum.some(function(e) {239return typeof e === 'object'240})241242var compare = complex ?243function(e) {244return 'JSON.stringify('+name+')'+' !== JSON.stringify('+JSON.stringify(e)+')'245} :246function(e) {247return name+' !== '+JSON.stringify(e)248}249250validate('if (%s) {', node.enum.map(compare).join(' && ') || 'false')251error('must be an enum value')252validate('}')253}254255if (node.dependencies) {256if (type !== 'object') validate('if (%s) {', types.object(name))257258Object.keys(node.dependencies).forEach(function(key) {259var deps = node.dependencies[key]260if (typeof deps === 'string') deps = [deps]261262var exists = function(k) {263return genobj(name, k) + ' !== undefined'264}265266if (Array.isArray(deps)) {267validate('if (%s !== undefined && !(%s)) {', genobj(name, key), deps.map(exists).join(' && ') || 'true')268error('dependencies not set')269validate('}')270}271if (typeof deps === 'object') {272validate('if (%s !== undefined) {', genobj(name, key))273visit(name, deps, reporter, filter)274validate('}')275}276})277278if (type !== 'object') validate('}')279}280281if (node.additionalProperties || node.additionalProperties === false) {282if (type !== 'object') validate('if (%s) {', types.object(name))283284var i = genloop()285var keys = gensym('keys')286287var toCompare = function(p) {288return keys+'['+i+'] !== '+JSON.stringify(p)289}290291var toTest = function(p) {292return '!'+patterns(p)+'.test('+keys+'['+i+'])'293}294295var additionalProp = Object.keys(properties || {}).map(toCompare)296.concat(Object.keys(node.patternProperties || {}).map(toTest))297.join(' && ') || 'true'298299validate('var %s = Object.keys(%s)', keys, name)300('for (var %s = 0; %s < %s.length; %s++) {', i, i, keys, i)301('if (%s) {', additionalProp)302303if (node.additionalProperties === false) {304if (filter) validate('delete %s', name+'['+keys+'['+i+']]')305error('has additional properties', null, JSON.stringify(name+'.') + ' + ' + keys + '['+i+']')306} else {307visit(name+'['+keys+'['+i+']]', node.additionalProperties, reporter, filter)308}309310validate311('}')312('}')313314if (type !== 'object') validate('}')315}316317if (node.$ref) {318var sub = get(root, opts && opts.schemas || {}, node.$ref)319if (sub) {320var fn = cache[node.$ref]321if (!fn) {322cache[node.$ref] = function proxy(data) {323return fn(data)324}325fn = compile(sub, cache, root, false, opts)326}327var n = gensym('ref')328scope[n] = fn329validate('if (!(%s(%s))) {', n, name)330error('referenced schema does not match')331validate('}')332}333}334335if (node.not) {336var prev = gensym('prev')337validate('var %s = errors', prev)338visit(name, node.not, false, filter)339validate('if (%s === errors) {', prev)340error('negative schema matches')341validate('} else {')342('errors = %s', prev)343('}')344}345346if (node.items && !tuple) {347if (type !== 'array') validate('if (%s) {', types.array(name))348349var i = genloop()350validate('for (var %s = 0; %s < %s.length; %s++) {', i, i, name, i)351visit(name+'['+i+']', node.items, reporter, filter)352validate('}')353354if (type !== 'array') validate('}')355}356357if (node.patternProperties) {358if (type !== 'object') validate('if (%s) {', types.object(name))359var keys = gensym('keys')360var i = genloop()361validate362('var %s = Object.keys(%s)', keys, name)363('for (var %s = 0; %s < %s.length; %s++) {', i, i, keys, i)364365Object.keys(node.patternProperties).forEach(function(key) {366var p = patterns(key)367validate('if (%s.test(%s)) {', p, keys+'['+i+']')368visit(name+'['+keys+'['+i+']]', node.patternProperties[key], reporter, filter)369validate('}')370})371372validate('}')373if (type !== 'object') validate('}')374}375376if (node.pattern) {377var p = patterns(node.pattern)378if (type !== 'string') validate('if (%s) {', types.string(name))379validate('if (!(%s.test(%s))) {', p, name)380error('pattern mismatch')381validate('}')382if (type !== 'string') validate('}')383}384385if (node.allOf) {386node.allOf.forEach(function(sch) {387visit(name, sch, reporter, filter)388})389}390391if (node.anyOf && node.anyOf.length) {392var prev = gensym('prev')393394node.anyOf.forEach(function(sch, i) {395if (i === 0) {396validate('var %s = errors', prev)397} else {398validate('if (errors !== %s) {', prev)399('errors = %s', prev)400}401visit(name, sch, false, false)402})403node.anyOf.forEach(function(sch, i) {404if (i) validate('}')405})406validate('if (%s !== errors) {', prev)407error('no schemas match')408validate('}')409}410411if (node.oneOf && node.oneOf.length) {412var prev = gensym('prev')413var passes = gensym('passes')414415validate416('var %s = errors', prev)417('var %s = 0', passes)418419node.oneOf.forEach(function(sch, i) {420visit(name, sch, false, false)421validate('if (%s === errors) {', prev)422('%s++', passes)423('} else {')424('errors = %s', prev)425('}')426})427428validate('if (%s !== 1) {', passes)429error('no (or more than one) schemas match')430validate('}')431}432433if (node.multipleOf !== undefined) {434if (type !== 'number' && type !== 'integer') validate('if (%s) {', types.number(name))435436var factor = ((node.multipleOf | 0) !== node.multipleOf) ? Math.pow(10, node.multipleOf.toString().split('.').pop().length) : 1437if (factor > 1) validate('if ((%d*%s) % %d) {', factor, name, factor*node.multipleOf)438else validate('if (%s % %d) {', name, node.multipleOf)439440error('has a remainder')441validate('}')442443if (type !== 'number' && type !== 'integer') validate('}')444}445446if (node.maxProperties !== undefined) {447if (type !== 'object') validate('if (%s) {', types.object(name))448449validate('if (Object.keys(%s).length > %d) {', name, node.maxProperties)450error('has more properties than allowed')451validate('}')452453if (type !== 'object') validate('}')454}455456if (node.minProperties !== undefined) {457if (type !== 'object') validate('if (%s) {', types.object(name))458459validate('if (Object.keys(%s).length < %d) {', name, node.minProperties)460error('has less properties than allowed')461validate('}')462463if (type !== 'object') validate('}')464}465466if (node.maxItems !== undefined) {467if (type !== 'array') validate('if (%s) {', types.array(name))468469validate('if (%s.length > %d) {', name, node.maxItems)470error('has more items than allowed')471validate('}')472473if (type !== 'array') validate('}')474}475476if (node.minItems !== undefined) {477if (type !== 'array') validate('if (%s) {', types.array(name))478479validate('if (%s.length < %d) {', name, node.minItems)480error('has less items than allowed')481validate('}')482483if (type !== 'array') validate('}')484}485486if (node.maxLength !== undefined) {487if (type !== 'string') validate('if (%s) {', types.string(name))488489validate('if (%s.length > %d) {', name, node.maxLength)490error('has longer length than allowed')491validate('}')492493if (type !== 'string') validate('}')494}495496if (node.minLength !== undefined) {497if (type !== 'string') validate('if (%s) {', types.string(name))498499validate('if (%s.length < %d) {', name, node.minLength)500error('has less length than allowed')501validate('}')502503if (type !== 'string') validate('}')504}505506if (node.minimum !== undefined) {507validate('if (%s %s %d) {', name, node.exclusiveMinimum ? '<=' : '<', node.minimum)508error('is less than minimum')509validate('}')510}511512if (node.maximum !== undefined) {513validate('if (%s %s %d) {', name, node.exclusiveMaximum ? '>=' : '>', node.maximum)514error('is more than maximum')515validate('}')516}517518if (properties) {519Object.keys(properties).forEach(function(p) {520visit(genobj(name, p), properties[p], reporter, filter)521})522}523524while (indent--) validate('}')525}526527var validate = genfun528('function validate(data) {')529('validate.errors = null')530('var errors = 0')531532visit('data', schema, reporter, opts && opts.filter)533534validate535('return errors === 0')536('}')537538validate = validate.toFunction(scope)539validate.errors = null540541validate.__defineGetter__('error', function() {542if (!validate.errors) return ''543return validate.errors544.map(function(err) {545return err.field+' '+err.message546})547.join('\n')548})549550validate.toJSON = function() {551return schema552}553554return validate555}556557module.exports = function(schema, opts) {558if (typeof schema === 'string') schema = JSON.parse(schema)559return compile(schema, {}, schema, true, opts)560}561562module.exports.filter = function(schema, opts) {563var validate = module.exports(schema, xtend(opts, {filter: true}))564return function(sch) {565validate(sch)566return sch567}568}569570571