Skip to content

Instantly share code, notes, and snippets.

@glynnbird
Last active July 23, 2020 14:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save glynnbird/87e5e8ec01a04b4982c25c2bbda8d3ab to your computer and use it in GitHub Desktop.
Save glynnbird/87e5e8ec01a04b4982c25c2bbda8d3ab to your computer and use it in GitHub Desktop.
CouchDB JSON Schema validator VDU function
{
"_id": "_design/validate",
"views": {
"lib": {
"validator": "\nclass URL { constructor(u) { this.url = u; } hash() { return this.url } toString() { return this.url }} \n function deepCompareStrict(a, b) {\n const typeofa = typeof a;\n if (typeofa !== typeof b) {\n return false;\n }\n if (Array.isArray(a)) {\n if (!Array.isArray(b)) {\n return false;\n }\n const length = a.length;\n if (length !== b.length) {\n return false;\n }\n for (let i = 0; i < length; i++) {\n if (!deepCompareStrict(a[i], b[i])) {\n return false;\n }\n }\n return true;\n }\n if (typeofa === 'object') {\n if (!a || !b) {\n return a === b;\n }\n const aKeys = Object.keys(a);\n const bKeys = Object.keys(b);\n const length = aKeys.length;\n if (length !== bKeys.length) {\n return false;\n }\n for (const k of aKeys) {\n if (!deepCompareStrict(a[k], b[k])) {\n return false;\n }\n }\n return true;\n }\n return a === b;\n}\n\nfunction encodePointer(p) {\n return encodeURI(escapePointer(p));\n}\nfunction escapePointer(p) {\n return p.replace(/~/g, '~0').replace(/\\//g, '~1');\n}\n\nconst schemaKeyword = {\n additionalItems: true,\n unevaluatedItems: true,\n items: true,\n contains: true,\n additionalProperties: true,\n unevaluatedProperties: true,\n propertyNames: true,\n not: true,\n if: true,\n then: true,\n else: true\n};\nconst schemaArrayKeyword = {\n items: true,\n allOf: true,\n anyOf: true,\n oneOf: true\n};\nconst schemaMapKeyword = {\n $defs: true,\n definitions: true,\n properties: true,\n patternProperties: true,\n dependentSchemas: true\n};\nconst ignoredKeyword = {\n id: true,\n $id: true,\n $ref: true,\n $schema: true,\n $anchor: true,\n $vocabulary: true,\n $comment: true,\n default: true,\n enum: true,\n const: true,\n required: true,\n type: true,\n maximum: true,\n minimum: true,\n exclusiveMaximum: true,\n exclusiveMinimum: true,\n multipleOf: true,\n maxLength: true,\n minLength: true,\n pattern: true,\n format: true,\n maxItems: true,\n minItems: true,\n uniqueItems: true,\n maxProperties: true,\n minProperties: true\n};\nlet initialBaseURI = typeof self !== 'undefined' && self.location\n ? new URL(self.location.origin + self.location.pathname + location.search)\n : new URL('https://github.com/cfworker');\nfunction dereference(schema, lookup = Object.create(null), baseURI = initialBaseURI, basePointer = '') {\n if (schema && typeof schema === 'object' && !Array.isArray(schema)) {\n const id = schema.$id || schema.id;\n if (id) {\n const url = new URL(id, baseURI);\n if (url.hash.length > 1) {\n lookup[url.href] = schema;\n }\n else {\n url.hash = '';\n if (basePointer === '') {\n baseURI = url;\n }\n else {\n dereference(schema, lookup, baseURI);\n }\n }\n }\n }\n else if (schema !== true && schema !== false) {\n return lookup;\n }\n const schemaURI = baseURI.href + (basePointer ? '#' + basePointer : '');\n if (lookup[schemaURI] !== undefined) {\n throw new Error(`Duplicate schema URI \"${schemaURI}\".`);\n }\n lookup[schemaURI] = schema;\n if (schema === true || schema === false) {\n return lookup;\n }\n if (schema.__absolute_uri__ === undefined) {\n Object.defineProperty(schema, '__absolute_uri__', {\n enumerable: false,\n value: schemaURI\n });\n }\n if (schema.$ref && schema.__absolute_ref__ === undefined) {\n const url = new URL(schema.$ref, baseURI);\n url.hash = url.hash;\n Object.defineProperty(schema, '__absolute_ref__', {\n enumerable: false,\n value: url.href\n });\n }\n if (schema.$anchor) {\n const url = new URL('#' + schema.$anchor, baseURI);\n lookup[url.href] = schema;\n }\n for (let key in schema) {\n if (ignoredKeyword[key]) {\n continue;\n }\n const keyBase = `${basePointer}/${encodePointer(key)}`;\n const subSchema = schema[key];\n if (Array.isArray(subSchema)) {\n if (schemaArrayKeyword[key]) {\n const length = subSchema.length;\n for (let i = 0; i < length; i++) {\n dereference(subSchema[i], lookup, baseURI, `${keyBase}/${i}`);\n }\n }\n }\n else if (schemaMapKeyword[key]) {\n for (let subKey in subSchema) {\n dereference(subSchema[subKey], lookup, baseURI, `${keyBase}/${encodePointer(subKey)}`);\n }\n }\n else {\n dereference(subSchema, lookup, baseURI, keyBase);\n }\n }\n return lookup;\n}\n\nconst DATE = /^(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)$/;\nconst DAYS = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];\nconst TIME = /^(\\d\\d):(\\d\\d):(\\d\\d)(\\.\\d+)?(z|[+-]\\d\\d(?::?\\d\\d)?)?$/i;\nconst HOSTNAME = /^(?=.{1,253}\\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\\.?$/i;\nconst URI = /^(?:[a-z][a-z0-9+\\-.]*:)(?:\\/?\\/(?:(?:[a-z0-9\\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\\.[a-z0-9\\-._~!$&'()*+,;=:]+)\\]|(?:(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)|(?:[a-z0-9\\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\\d*)?(?:\\/(?:[a-z0-9\\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\\/(?:(?:[a-z0-9\\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\\/(?:[a-z0-9\\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\\/(?:[a-z0-9\\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\\?(?:[a-z0-9\\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;\nconst URIREF = /^(?:[a-z][a-z0-9+\\-.]*:)?(?:\\/?\\/(?:(?:[a-z0-9\\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\\.[a-z0-9\\-._~!$&'()*+,;=:]+)\\]|(?:(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)|(?:[a-z0-9\\-._~!$&'\"()*+,;=]|%[0-9a-f]{2})*)(?::\\d*)?(?:\\/(?:[a-z0-9\\-._~!$&'\"()*+,;=:@]|%[0-9a-f]{2})*)*|\\/(?:(?:[a-z0-9\\-._~!$&'\"()*+,;=:@]|%[0-9a-f]{2})+(?:\\/(?:[a-z0-9\\-._~!$&'\"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\\-._~!$&'\"()*+,;=:@]|%[0-9a-f]{2})+(?:\\/(?:[a-z0-9\\-._~!$&'\"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\\?(?:[a-z0-9\\-._~!$&'\"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\\-._~!$&'\"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i;\nconst URITEMPLATE = /^(?:(?:[^\\x00-\\x20\"'<>%\\\\^`{|}]|%[0-9a-f]{2})|\\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\\*)?)*\\})*$/i;\nconst URL_ = /^(?:(?:https?|ftp):\\/\\/)(?:\\S+(?::\\S*)?@)?(?:(?!10(?:\\.\\d{1,3}){3})(?!127(?:\\.\\d{1,3}){3})(?!169\\.254(?:\\.\\d{1,3}){2})(?!192\\.168(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u{00a1}-\\u{ffff}0-9]+-?)*[a-z\\u{00a1}-\\u{ffff}0-9]+)(?:\\.(?:[a-z\\u{00a1}-\\u{ffff}0-9]+-?)*[a-z\\u{00a1}-\\u{ffff}0-9]+)*(?:\\.(?:[a-z\\u{00a1}-\\u{ffff}]{2,})))(?::\\d{2,5})?(?:\\/[^\\s]*)?$/iu;\nconst UUID = /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i;\nconst JSON_POINTER = /^(?:\\/(?:[^~/]|~0|~1)*)*$/;\nconst JSON_POINTER_URI_FRAGMENT = /^#(?:\\/(?:[a-z0-9_\\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i;\nconst RELATIVE_JSON_POINTER = /^(?:0|[1-9][0-9]*)(?:#|(?:\\/(?:[^~/]|~0|~1)*)*)$/;\nconst FASTDATE = /^\\d\\d\\d\\d-[0-1]\\d-[0-3]\\d$/;\nconst FASTTIME = /^(?:[0-2]\\d:[0-5]\\d:[0-5]\\d|23:59:60)(?:\\.\\d+)?(?:z|[+-]\\d\\d(?::?\\d\\d)?)?$/i;\nconst FASTDATETIME = /^\\d\\d\\d\\d-[0-1]\\d-[0-3]\\d[t\\s](?:[0-2]\\d:[0-5]\\d:[0-5]\\d|23:59:60)(?:\\.\\d+)?(?:z|[+-]\\d\\d(?::?\\d\\d)?)$/i;\nconst FASTURI = /^(?:[a-z][a-z0-9+-.]*:)(?:\\/?\\/)?[^\\s]*$/i;\nconst FASTURIREFERENCE = /^(?:(?:[a-z][a-z0-9+-.]*:)?\\/?\\/)?(?:[^\\\\\\s#][^\\s#]*)?(?:#[^\\\\\\s]*)?$/i;\nconst FASTEMAIL = /^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i;\nconst EMAIL = /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i;\nconst IPV4 = /^(?:(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$/;\nconst IPV6 = /^\\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(?:%.+)?\\s*$/i;\nconst DURATION = /^P(?!$)(\\d+(?:\\.\\d+)?Y)?(\\d+(?:\\.\\d+)?M)?(\\d+(?:\\.\\d+)?W)?(\\d+(?:\\.\\d+)?D)?(T(?=\\d)(\\d+(?:\\.\\d+)?H)?(\\d+(?:\\.\\d+)?M)?(\\d+(?:\\.\\d+)?S)?)?$/;\nfunction bind(r) {\n return r.test.bind(r);\n}\nconst fullFormat = {\n date,\n time: time.bind(undefined, false),\n 'date-time': date_time,\n duration: bind(DURATION),\n uri,\n 'uri-reference': bind(URIREF),\n 'uri-template': bind(URITEMPLATE),\n url: bind(URL_),\n email: bind(EMAIL),\n hostname: bind(HOSTNAME),\n ipv4: bind(IPV4),\n ipv6: bind(IPV6),\n regex: regex,\n uuid: bind(UUID),\n 'json-pointer': bind(JSON_POINTER),\n 'json-pointer-uri-fragment': bind(JSON_POINTER_URI_FRAGMENT),\n 'relative-json-pointer': bind(RELATIVE_JSON_POINTER)\n};\nconst fastFormat = {\n ...fullFormat,\n date: bind(FASTDATE),\n time: bind(FASTTIME),\n 'date-time': bind(FASTDATETIME),\n uri: bind(FASTURI),\n 'uri-reference': bind(FASTURIREFERENCE),\n email: bind(FASTEMAIL)\n};\nfunction isLeapYear(year) {\n return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);\n}\nfunction date(str) {\n const matches = str.match(DATE);\n if (!matches)\n return false;\n const year = +matches[1];\n const month = +matches[2];\n const day = +matches[3];\n return (month >= 1 &&\n month <= 12 &&\n day >= 1 &&\n day <= (month == 2 && isLeapYear(year) ? 29 : DAYS[month]));\n}\nfunction time(full, str) {\n const matches = str.match(TIME);\n if (!matches)\n return false;\n const hour = +matches[1];\n const minute = +matches[2];\n const second = +matches[3];\n const timeZone = !!matches[5];\n return (((hour <= 23 && minute <= 59 && second <= 59) ||\n (hour == 23 && minute == 59 && second == 60)) &&\n (!full || timeZone));\n}\nconst DATE_TIME_SEPARATOR = /t|\\s/i;\nfunction date_time(str) {\n const dateTime = str.split(DATE_TIME_SEPARATOR);\n return dateTime.length == 2 && date(dateTime[0]) && time(true, dateTime[1]);\n}\nconst NOT_URI_FRAGMENT = /\\/|:/;\nfunction uri(str) {\n return NOT_URI_FRAGMENT.test(str) && URI.test(str);\n}\nconst Z_ANCHOR = /[^\\\\]\\\\Z/;\nfunction regex(str) {\n if (Z_ANCHOR.test(str))\n return false;\n try {\n new RegExp(str);\n return true;\n }\n catch (e) {\n return false;\n }\n}\n\nfunction ucs2length(s) {\n let result = 0;\n let length = s.length;\n let index = 0;\n let charCode;\n while (index < length) {\n result++;\n charCode = s.charCodeAt(index++);\n if (charCode >= 0xd800 && charCode <= 0xdbff && index < length) {\n charCode = s.charCodeAt(index);\n if ((charCode & 0xfc00) == 0xdc00) {\n index++;\n }\n }\n }\n return result;\n}\n\nfunction validate(instance, schema, draft = '2019-09', lookup = dereference(schema), recursiveAnchor = null, instanceLocation = '#', schemaLocation = '#', evaluated) {\n if (schema === true) {\n return { valid: true, errors: [] };\n }\n if (schema === false) {\n return {\n valid: false,\n errors: [\n {\n instanceLocation,\n keyword: 'false',\n keywordLocation: instanceLocation,\n error: 'False boolean schema.'\n }\n ]\n };\n }\n const rawInstanceType = typeof instance;\n let instanceType;\n switch (rawInstanceType) {\n case 'boolean':\n case 'number':\n case 'string':\n instanceType = rawInstanceType;\n break;\n case 'object':\n if (instance === null) {\n instanceType = 'null';\n }\n else if (Array.isArray(instance)) {\n instanceType = 'array';\n evaluated = evaluated || { items: -1 };\n }\n else {\n instanceType = 'object';\n evaluated = evaluated || { properties: Object.create(null) };\n }\n break;\n default:\n throw new Error(`Instances of \"${rawInstanceType}\" type are not supported.`);\n }\n const { $ref, $recursiveRef, $recursiveAnchor, type: $type, const: $const, enum: $enum, required: $required, not: $not, anyOf: $anyOf, allOf: $allOf, oneOf: $oneOf, if: $if, then: $then, else: $else, format: $format, properties: $properties, patternProperties: $patternProperties, additionalProperties: $additionalProperties, unevaluatedProperties: $unevaluatedProperties, minProperties: $minProperties, maxProperties: $maxProperties, propertyNames: $propertyNames, dependentRequired: $dependentRequired, dependentSchemas: $dependentSchemas, dependencies: $dependencies, items: $items, additionalItems: $additionalItems, unevaluatedItems: $unevaluatedItems, contains: $contains, minContains: $minContains, maxContains: $maxContains, minItems: $minItems, maxItems: $maxItems, uniqueItems: $uniqueItems, minimum: $minimum, maximum: $maximum, exclusiveMinimum: $exclusiveMinimum, exclusiveMaximum: $exclusiveMaximum, multipleOf: $multipleOf, minLength: $minLength, maxLength: $maxLength, pattern: $pattern, __absolute_ref__ } = schema;\n const errors = [];\n if ($ref !== undefined) {\n const uri = __absolute_ref__ || $ref;\n const refSchema = lookup[uri];\n if (refSchema === undefined) {\n let message = `Unresolved $ref \"${$ref}\".`;\n if (__absolute_ref__ && __absolute_ref__ !== $ref) {\n message += ` Absolute URI \"${__absolute_ref__}\".`;\n }\n message += `\\nKnown schemas:\\n- ${Object.keys(lookup).join('\\n- ')}`;\n throw new Error(message);\n }\n const keywordLocation = `${schemaLocation}/$ref`;\n const result = validate(instance, refSchema, draft, lookup, recursiveAnchor, instanceLocation, keywordLocation, evaluated);\n if (!result.valid) {\n errors.push({\n instanceLocation,\n keyword: '$ref',\n keywordLocation,\n error: 'A subschema had errors.'\n }, ...result.errors);\n }\n if (draft === '4' || draft === '7') {\n return { valid: errors.length === 0, errors };\n }\n }\n if ($recursiveAnchor === true && recursiveAnchor === null) {\n recursiveAnchor = schema;\n }\n if ($recursiveRef === '#') {\n const keywordLocation = `${schemaLocation}/$recursiveRef`;\n const result = validate(instance, recursiveAnchor === null ? schema : recursiveAnchor, draft, lookup, recursiveAnchor, instanceLocation, keywordLocation, evaluated);\n if (!result.valid) {\n errors.push({\n instanceLocation,\n keyword: '$recursiveRef',\n keywordLocation,\n error: 'A subschema had errors.'\n }, ...result.errors);\n }\n }\n if (Array.isArray($type)) {\n let length = $type.length;\n let valid = false;\n for (let i = 0; i < length; i++) {\n if (instanceType === $type[i] ||\n ($type[i] === 'integer' &&\n instanceType === 'number' &&\n instance % 1 === 0 &&\n instance === instance)) {\n valid = true;\n break;\n }\n }\n if (!valid) {\n errors.push({\n instanceLocation,\n keyword: 'type',\n keywordLocation: `${schemaLocation}/type`,\n error: `Instance type \"${instanceType}\" is invalid. Expected \"${$type.join('\", \"')}\".`\n });\n }\n }\n else if ($type === 'integer') {\n if (instanceType !== 'number' || instance % 1 || instance !== instance) {\n errors.push({\n instanceLocation,\n keyword: 'type',\n keywordLocation: `${schemaLocation}/type`,\n error: `Instance type \"${instanceType}\" is invalid. Expected \"${$type}\".`\n });\n }\n }\n else if ($type !== undefined && instanceType !== $type) {\n errors.push({\n instanceLocation,\n keyword: 'type',\n keywordLocation: `${schemaLocation}/type`,\n error: `Instance type \"${instanceType}\" is invalid. Expected \"${$type}\".`\n });\n }\n if ($const !== undefined) {\n if (instanceType === 'object' || instanceType === 'array') {\n if (!deepCompareStrict(instance, $const)) {\n errors.push({\n instanceLocation,\n keyword: 'const',\n keywordLocation: `${schemaLocation}/const`,\n error: `Instance does not match ${JSON.stringify($const)}.`\n });\n }\n }\n else if (instance !== $const) {\n errors.push({\n instanceLocation,\n keyword: 'const',\n keywordLocation: `${schemaLocation}/const`,\n error: `Instance does not match ${JSON.stringify($const)}.`\n });\n }\n }\n if ($enum !== undefined) {\n if (instanceType === 'object' || instanceType === 'array') {\n if (!$enum.some(value => deepCompareStrict(instance, value))) {\n errors.push({\n instanceLocation,\n keyword: 'enum',\n keywordLocation: `${schemaLocation}/enum`,\n error: `Instance does not match any of ${JSON.stringify($enum)}.`\n });\n }\n }\n else if (!$enum.some(value => instance === value)) {\n errors.push({\n instanceLocation,\n keyword: 'enum',\n keywordLocation: `${schemaLocation}/enum`,\n error: `Instance does not match any of ${JSON.stringify($enum)}.`\n });\n }\n }\n if ($not !== undefined) {\n const keywordLocation = `${schemaLocation}/not`;\n const result = validate(instance, $not, draft, lookup, recursiveAnchor, instanceLocation, keywordLocation);\n if (result.valid) {\n errors.push({\n instanceLocation,\n keyword: 'not',\n keywordLocation,\n error: 'Instance matched \"not\" schema.'\n });\n }\n }\n if ($anyOf !== undefined) {\n const keywordLocation = `${schemaLocation}/anyOf`;\n const errorsLength = errors.length;\n let anyValid = false;\n for (let i = 0; i < $anyOf.length; i++) {\n const subSchema = $anyOf[i];\n const result = validate(instance, subSchema, draft, lookup, recursiveAnchor, instanceLocation, `${keywordLocation}/${i}`, evaluated);\n errors.push(...result.errors);\n anyValid = anyValid || result.valid;\n }\n if (anyValid) {\n errors.length = errorsLength;\n }\n else {\n errors.splice(errorsLength, 0, {\n instanceLocation,\n keyword: 'anyOf',\n keywordLocation,\n error: 'Instance does not match any subschemas.'\n });\n }\n }\n if ($allOf !== undefined) {\n const keywordLocation = `${schemaLocation}/allOf`;\n const errorsLength = errors.length;\n let allValid = true;\n for (let i = 0; i < $allOf.length; i++) {\n const subSchema = $allOf[i];\n const result = validate(instance, subSchema, draft, lookup, recursiveAnchor, instanceLocation, `${keywordLocation}/${i}`, evaluated);\n errors.push(...result.errors);\n allValid = allValid && result.valid;\n }\n if (allValid) {\n errors.length = errorsLength;\n }\n else {\n errors.splice(errorsLength, 0, {\n instanceLocation,\n keyword: 'allOf',\n keywordLocation,\n error: `Instance does not match every subschema.`\n });\n }\n }\n if ($oneOf !== undefined) {\n const keywordLocation = `${schemaLocation}/oneOf`;\n const errorsLength = errors.length;\n const matches = $oneOf.filter((subSchema, i) => {\n const result = validate(instance, subSchema, draft, lookup, recursiveAnchor, instanceLocation, `${keywordLocation}/${i}`, evaluated);\n errors.push(...result.errors);\n return result.valid;\n }).length;\n if (matches === 1) {\n errors.length = errorsLength;\n }\n else {\n errors.splice(errorsLength, 0, {\n instanceLocation,\n keyword: 'oneOf',\n keywordLocation,\n error: `Instance does not match exactly one subschema (${matches} matches).`\n });\n }\n }\n if ($if !== undefined) {\n const keywordLocation = `${schemaLocation}/if`;\n const conditionResult = validate(instance, $if, draft, lookup, recursiveAnchor, instanceLocation, keywordLocation, evaluated).valid;\n if (conditionResult) {\n if ($then !== undefined) {\n const thenResult = validate(instance, $then, draft, lookup, recursiveAnchor, instanceLocation, `${schemaLocation}/then`, evaluated);\n if (!thenResult.valid) {\n errors.push({\n instanceLocation,\n keyword: 'if',\n keywordLocation,\n error: `Instance does not match \"then\" schema.`\n }, ...thenResult.errors);\n }\n }\n }\n else if ($else !== undefined) {\n const elseResult = validate(instance, $else, draft, lookup, recursiveAnchor, instanceLocation, `${schemaLocation}/else`, evaluated);\n if (!elseResult.valid) {\n errors.push({\n instanceLocation,\n keyword: 'if',\n keywordLocation,\n error: `Instance does not match \"else\" schema.`\n }, ...elseResult.errors);\n }\n }\n }\n if (instanceType === 'object') {\n if ($required !== undefined) {\n for (const key of $required) {\n if (!(key in instance)) {\n errors.push({\n instanceLocation,\n keyword: 'required',\n keywordLocation: `${schemaLocation}/required`,\n error: `Instance does not have required property \"${key}\".`\n });\n }\n }\n }\n const keys = Object.keys(instance);\n if ($minProperties !== undefined && keys.length < $minProperties) {\n errors.push({\n instanceLocation,\n keyword: 'minProperties',\n keywordLocation: `${schemaLocation}/minProperties`,\n error: `Instance does not have at least ${$minProperties} properties.`\n });\n }\n if ($maxProperties !== undefined && keys.length > $maxProperties) {\n errors.push({\n instanceLocation,\n keyword: 'maxProperties',\n keywordLocation: `${schemaLocation}/maxProperties`,\n error: `Instance does not have at least ${$maxProperties} properties.`\n });\n }\n if ($propertyNames !== undefined) {\n const keywordLocation = `${schemaLocation}/propertyNames`;\n for (const key in instance) {\n const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`;\n const result = validate(key, $propertyNames, draft, lookup, recursiveAnchor, subInstancePointer, keywordLocation);\n if (!result.valid) {\n errors.push({\n instanceLocation,\n keyword: 'propertyNames',\n keywordLocation,\n error: `Property name \"${key}\" does not match schema.`\n }, ...result.errors);\n }\n }\n }\n if ($dependentRequired !== undefined) {\n const keywordLocation = `${schemaLocation}/dependantRequired`;\n for (const key in $dependentRequired) {\n if (key in instance) {\n const required = $dependentRequired[key];\n for (const dependantKey of required) {\n if (!(dependantKey in instance)) {\n errors.push({\n instanceLocation,\n keyword: 'dependentRequired',\n keywordLocation,\n error: `Instance has \"${key}\" but does not have \"${dependantKey}\".`\n });\n }\n }\n }\n }\n }\n if ($dependentSchemas !== undefined) {\n for (const key in $dependentSchemas) {\n const keywordLocation = `${schemaLocation}/dependentSchemas`;\n if (key in instance) {\n const result = validate(instance, $dependentSchemas[key], draft, lookup, recursiveAnchor, instanceLocation, `${keywordLocation}/${encodePointer(key)}`, evaluated);\n if (!result.valid) {\n errors.push({\n instanceLocation,\n keyword: 'dependentSchemas',\n keywordLocation,\n error: `Instance has \"${key}\" but does not match dependant schema.`\n }, ...result.errors);\n }\n }\n }\n }\n if ($dependencies !== undefined) {\n const keywordLocation = `${schemaLocation}/dependencies`;\n for (const key in $dependencies) {\n if (key in instance) {\n const propsOrSchema = $dependencies[key];\n if (Array.isArray(propsOrSchema)) {\n for (const dependantKey of propsOrSchema) {\n if (!(dependantKey in instance)) {\n errors.push({\n instanceLocation,\n keyword: 'dependencies',\n keywordLocation,\n error: `Instance has \"${key}\" but does not have \"${dependantKey}\".`\n });\n }\n }\n }\n else {\n const result = validate(instance, propsOrSchema, draft, lookup, recursiveAnchor, instanceLocation, `${keywordLocation}/${encodePointer(key)}`);\n if (!result.valid) {\n errors.push({\n instanceLocation,\n keyword: 'dependencies',\n keywordLocation,\n error: `Instance has \"${key}\" but does not match dependant schema.`\n }, ...result.errors);\n }\n }\n }\n }\n }\n const thisEvaluated = Object.create(null);\n if (!evaluated || !evaluated.properties) {\n throw new Error('evaluated.properties should be an object');\n }\n let stop = false;\n if ($properties !== undefined) {\n const keywordLocation = `${schemaLocation}/properties`;\n for (const key in $properties) {\n if (!(key in instance)) {\n continue;\n }\n const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`;\n const result = validate(instance[key], $properties[key], draft, lookup, recursiveAnchor, subInstancePointer, `${keywordLocation}/${encodePointer(key)}`);\n if (result.valid) {\n evaluated.properties[key] = thisEvaluated[key] = true;\n }\n else {\n stop = true;\n errors.push({\n instanceLocation,\n keyword: 'properties',\n keywordLocation,\n error: `Property \"${key}\" does not match schema.`\n }, ...result.errors);\n break;\n }\n }\n }\n if (!stop && $patternProperties !== undefined) {\n const keywordLocation = `${schemaLocation}/patternProperties`;\n for (const pattern in $patternProperties) {\n const regex = new RegExp(pattern);\n const subSchema = $patternProperties[pattern];\n for (const key in instance) {\n if (!regex.test(key)) {\n continue;\n }\n const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`;\n const result = validate(instance[key], subSchema, draft, lookup, recursiveAnchor, subInstancePointer, `${keywordLocation}/${encodePointer(pattern)}`);\n if (result.valid) {\n evaluated.properties[key] = thisEvaluated[key] = true;\n }\n else {\n stop = true;\n errors.push({\n instanceLocation,\n keyword: 'patternProperties',\n keywordLocation,\n error: `Property \"${key}\" matches pattern \"${pattern}\" but does not match associated schema.`\n }, ...result.errors);\n }\n }\n }\n }\n if (!stop && $additionalProperties !== undefined) {\n const keywordLocation = `${schemaLocation}/additionalProperties`;\n for (const key in instance) {\n if (thisEvaluated[key]) {\n continue;\n }\n const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`;\n const result = validate(instance[key], $additionalProperties, draft, lookup, recursiveAnchor, subInstancePointer, keywordLocation);\n if (result.valid) {\n evaluated.properties[key] = true;\n }\n else {\n stop = true;\n errors.push({\n instanceLocation,\n keyword: 'additionalProperties',\n keywordLocation,\n error: `Property \"${key}\" does not match additional properties schema.`\n }, ...result.errors);\n }\n }\n }\n else if (!stop && $unevaluatedProperties !== undefined) {\n const keywordLocation = `${schemaLocation}/unevaluatedProperties`;\n for (const key in instance) {\n if (!evaluated.properties[key]) {\n const subInstancePointer = `${instanceLocation}/${encodePointer(key)}`;\n const result = validate(instance[key], $unevaluatedProperties, draft, lookup, recursiveAnchor, subInstancePointer, keywordLocation);\n if (result.valid) {\n evaluated.properties[key] = true;\n }\n else {\n errors.push({\n instanceLocation,\n keyword: 'unevaluatedProperties',\n keywordLocation,\n error: `Property \"${key}\" does not match unevaluated properties schema.`\n }, ...result.errors);\n }\n }\n }\n }\n }\n else if (instanceType === 'array') {\n if ($maxItems !== undefined && instance.length > $maxItems) {\n errors.push({\n instanceLocation,\n keyword: 'maxItems',\n keywordLocation: `${schemaLocation}/maxItems`,\n error: `Array has too many items (${instance.length} > ${$maxItems}).`\n });\n }\n if ($minItems !== undefined && instance.length < $minItems) {\n errors.push({\n instanceLocation,\n keyword: 'minItems',\n keywordLocation: `${schemaLocation}/minItems`,\n error: `Array has too few items (${instance.length} < ${$minItems}).`\n });\n }\n if (!evaluated || evaluated.items === undefined) {\n throw new Error('evaluated.items should be a number');\n }\n const length = instance.length;\n let i = 0;\n let stop = false;\n if ($items !== undefined) {\n const keywordLocation = `${schemaLocation}/items`;\n if (Array.isArray($items)) {\n const length2 = Math.min($items.length, length);\n for (; i < length2; i++) {\n const result = validate(instance[i], $items[i], draft, lookup, recursiveAnchor, `${instanceLocation}/${i}`, `${keywordLocation}/${i}`);\n if (!result.valid) {\n stop = true;\n errors.push({\n instanceLocation,\n keyword: 'items',\n keywordLocation,\n error: `Items did not match schema.`\n }, ...result.errors);\n break;\n }\n }\n }\n else {\n for (; i < length; i++) {\n const result = validate(instance[i], $items, draft, lookup, recursiveAnchor, `${instanceLocation}/${i}`, keywordLocation);\n if (!result.valid) {\n stop = true;\n errors.push({\n instanceLocation,\n keyword: 'items',\n keywordLocation,\n error: `Items did not match schema.`\n }, ...result.errors);\n break;\n }\n }\n }\n evaluated.items = Math.max(i, evaluated.items);\n if (!stop && $additionalItems !== undefined) {\n const keywordLocation = `${schemaLocation}/additionalItems`;\n for (; i < length; i++) {\n const result = validate(instance[i], $additionalItems, draft, lookup, recursiveAnchor, `${instanceLocation}/${i}`, keywordLocation);\n if (!result.valid) {\n stop = true;\n errors.push({\n instanceLocation,\n keyword: 'additionalItems',\n keywordLocation,\n error: `Items did not match additional items schema.`\n }, ...result.errors);\n }\n }\n evaluated.items = Math.max(i, evaluated.items);\n }\n }\n if (!stop && $unevaluatedItems !== undefined) {\n const keywordLocation = `${schemaLocation}/unevaluatedItems`;\n for (i = Math.max(evaluated.items, 0); i < length; i++) {\n const result = validate(instance[i], $unevaluatedItems, draft, lookup, recursiveAnchor, `${instanceLocation}/${i}`, keywordLocation);\n if (!result.valid) {\n errors.push({\n instanceLocation,\n keyword: 'unevaluatedItems',\n keywordLocation,\n error: `Items did not match unevaluated items schema.`\n }, ...result.errors);\n }\n }\n evaluated.items = Math.max(i, evaluated.items);\n }\n if ($contains !== undefined) {\n if (length === 0 && $minContains === undefined) {\n errors.push({\n instanceLocation,\n keyword: 'contains',\n keywordLocation: `${schemaLocation}/contains`,\n error: `Array is empty. It must contain at least one item matching the schema.`\n });\n }\n else if ($minContains !== undefined && length < $minContains) {\n errors.push({\n instanceLocation,\n keyword: 'minContains',\n keywordLocation: `${schemaLocation}/minContains`,\n error: `Array has less items (${length}) than minContains (${$minContains}).`\n });\n }\n else {\n const keywordLocation = `${schemaLocation}/contains`;\n const errorsLength = errors.length;\n let contained = 0;\n for (let i = 0; i < length; i++) {\n const result = validate(instance[i], $contains, draft, lookup, recursiveAnchor, `${instanceLocation}/${i}`, keywordLocation);\n if (result.valid) {\n contained++;\n if ($minContains === undefined && $maxContains === undefined) {\n break;\n }\n }\n else {\n errors.push(...result.errors);\n }\n }\n if (contained >= ($minContains || 0)) {\n errors.length = errorsLength;\n }\n if ($minContains === undefined &&\n $maxContains === undefined &&\n contained === 0) {\n errors.splice(errorsLength, 0, {\n instanceLocation,\n keyword: 'contains',\n keywordLocation,\n error: `Array does not contain item matching schema.`\n });\n }\n else if ($minContains !== undefined && contained < $minContains) {\n errors.push({\n instanceLocation,\n keyword: 'minContains',\n keywordLocation: `${schemaLocation}/minContains`,\n error: `Array must contain at least ${$minContains} items matching schema. Only ${contained} items were found.`\n });\n }\n else if ($maxContains !== undefined && contained > $maxContains) {\n errors.push({\n instanceLocation,\n keyword: 'maxContains',\n keywordLocation: `${schemaLocation}/maxContains`,\n error: `Array may contain at most ${$maxContains} items matching schema. ${contained} items were found.`\n });\n }\n }\n }\n if ($uniqueItems) {\n for (let j = 0; j < length; j++) {\n const a = instance[j];\n const ao = typeof a === 'object' && a !== null;\n for (let k = 0; k < length; k++) {\n if (j === k) {\n continue;\n }\n const b = instance[k];\n const bo = typeof b === 'object' && b !== null;\n if (a === b || (ao && bo && deepCompareStrict(a, b))) {\n errors.push({\n instanceLocation,\n keyword: 'uniqueItems',\n keywordLocation: `${schemaLocation}/uniqueItems`,\n error: `Duplicate items at indexes ${j} and ${k}.`\n });\n j = Number.MAX_SAFE_INTEGER;\n k = Number.MAX_SAFE_INTEGER;\n }\n }\n }\n }\n }\n else if (instanceType === 'number') {\n if (draft === '4') {\n if ($minimum !== undefined &&\n (($exclusiveMinimum === true && instance <= $minimum) ||\n instance < $minimum)) {\n errors.push({\n instanceLocation,\n keyword: 'minimum',\n keywordLocation: `${schemaLocation}/minimum`,\n error: `${instance} is less than ${$exclusiveMinimum ? 'or equal to ' : ''} ${$minimum}.`\n });\n }\n if ($maximum !== undefined &&\n (($exclusiveMaximum === true && instance >= $maximum) ||\n instance > $maximum)) {\n errors.push({\n instanceLocation,\n keyword: 'maximum',\n keywordLocation: `${schemaLocation}/maximum`,\n error: `${instance} is greater than ${$exclusiveMaximum ? 'or equal to ' : ''} ${$maximum}.`\n });\n }\n }\n else {\n if ($minimum !== undefined && instance < $minimum) {\n errors.push({\n instanceLocation,\n keyword: 'minimum',\n keywordLocation: `${schemaLocation}/minimum`,\n error: `${instance} is less than ${$minimum}.`\n });\n }\n if ($maximum !== undefined && instance > $maximum) {\n errors.push({\n instanceLocation,\n keyword: 'maximum',\n keywordLocation: `${schemaLocation}/maximum`,\n error: `${instance} is greater than ${$maximum}.`\n });\n }\n if ($exclusiveMinimum !== undefined && instance <= $exclusiveMinimum) {\n errors.push({\n instanceLocation,\n keyword: 'exclusiveMinimum',\n keywordLocation: `${schemaLocation}/exclusiveMinimum`,\n error: `${instance} is less than ${$exclusiveMinimum}.`\n });\n }\n if ($exclusiveMaximum !== undefined && instance >= $exclusiveMaximum) {\n errors.push({\n instanceLocation,\n keyword: 'exclusiveMaximum',\n keywordLocation: `${schemaLocation}/exclusiveMaximum`,\n error: `${instance} is greater than or equal to ${$exclusiveMaximum}.`\n });\n }\n }\n if ($multipleOf !== undefined) {\n const division = instance / $multipleOf;\n if (division !== Math.floor(division)) {\n errors.push({\n instanceLocation,\n keyword: 'multipleOf',\n keywordLocation: `${schemaLocation}/multipleOf`,\n error: `${instance} is not a multiple of ${$multipleOf}.`\n });\n }\n }\n }\n else if (instanceType === 'string') {\n const length = $minLength === undefined && $maxLength === undefined\n ? 0\n : ucs2length(instance);\n if ($minLength !== undefined && length < $minLength) {\n errors.push({\n instanceLocation,\n keyword: 'minLength',\n keywordLocation: `${schemaLocation}/minLength`,\n error: `String is too short (${length} < ${$minLength}).`\n });\n }\n if ($maxLength !== undefined && length > $maxLength) {\n errors.push({\n instanceLocation,\n keyword: 'maxLength',\n keywordLocation: `${schemaLocation}/maxLength`,\n error: `String is too long (${length} > ${$minLength}).`\n });\n }\n if ($pattern !== undefined && !new RegExp($pattern).test(instance)) {\n errors.push({\n instanceLocation,\n keyword: 'pattern',\n keywordLocation: `${schemaLocation}/pattern`,\n error: `String does not match pattern.`\n });\n }\n if ($format !== undefined &&\n fastFormat[$format] &&\n !fastFormat[$format](instance)) {\n errors.push({\n instanceLocation,\n keyword: 'format',\n keywordLocation: `${schemaLocation}/format`,\n error: `String does not match format \"${$format}\".`\n });\n }\n }\n return { valid: errors.length === 0, errors };\n}\n\nclass Validator {\n constructor(schema, draft = '2019-09') {\n this.schema = schema;\n this.draft = draft;\n this.lookup = dereference(schema);\n }\n validate(instance) {\n return validate(instance, this.schema, this.draft, this.lookup);\n }\n addSchema(schema, id) {\n if (id) {\n schema = { ...schema, $id: id };\n }\n dereference(schema, this.lookup);\n }\n}\n\nexports['Validator'] = Validator",
"person": "module.exports = {$id:'http://glynnbird.com/person', $schema: 'http://json-schema.org/schema#', type: 'object', properties: {_id: { type: 'string' }, _rev: { type: 'string' }, type: { type: 'string', enum: ['user'] }, name: { type: 'string' }, email: { type: 'string', format: 'email' },password: { type: 'string' }, salt: { type: 'string' }, active: { type: 'boolean' }, email_verified: { type: 'boolean' }, address: { type: 'string' }, joined: { type: 'string', format: 'date-time'}}, additionalProperties: false, required: ['type', 'name', 'email', 'password', 'salt', 'active', 'joined']}"
}
},
"validate_doc_update": "function (newdoc) { var Validator = require('views/lib/validator').Validator; var schema = require('views/lib/person'); var validator = new Validator(schema); var r = validator.validate(newdoc); if (!r.valid) { throw({'forbidden':'schema does not match'})} }"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment