UNPKG

ember-z-schema

Version:
1,380 lines (1,225 loc) 111 kB
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ZSchema = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ // shim for using process in browser var process = module.exports = {}; // cached from whatever global is present so that test runners that stub it // don't break things. But we need to wrap it in a try catch in case it is // wrapped in strict mode code which doesn't define any globals. It's inside a // function because try/catches deoptimize in certain engines. var cachedSetTimeout; var cachedClearTimeout; function defaultSetTimout() { throw new Error('setTimeout has not been defined'); } function defaultClearTimeout () { throw new Error('clearTimeout has not been defined'); } (function () { try { if (typeof setTimeout === 'function') { cachedSetTimeout = setTimeout; } else { cachedSetTimeout = defaultSetTimout; } } catch (e) { cachedSetTimeout = defaultSetTimout; } try { if (typeof clearTimeout === 'function') { cachedClearTimeout = clearTimeout; } else { cachedClearTimeout = defaultClearTimeout; } } catch (e) { cachedClearTimeout = defaultClearTimeout; } } ()) function runTimeout(fun) { if (cachedSetTimeout === setTimeout) { //normal enviroments in sane situations return setTimeout(fun, 0); } // if setTimeout wasn't available but was latter defined if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { cachedSetTimeout = setTimeout; return setTimeout(fun, 0); } try { // when when somebody has screwed with setTimeout but no I.E. maddness return cachedSetTimeout(fun, 0); } catch(e){ try { // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally return cachedSetTimeout.call(null, fun, 0); } catch(e){ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error return cachedSetTimeout.call(this, fun, 0); } } } function runClearTimeout(marker) { if (cachedClearTimeout === clearTimeout) { //normal enviroments in sane situations return clearTimeout(marker); } // if clearTimeout wasn't available but was latter defined if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { cachedClearTimeout = clearTimeout; return clearTimeout(marker); } try { // when when somebody has screwed with setTimeout but no I.E. maddness return cachedClearTimeout(marker); } catch (e){ try { // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally return cachedClearTimeout.call(null, marker); } catch (e){ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. // Some versions of I.E. have different rules for clearTimeout vs setTimeout return cachedClearTimeout.call(this, marker); } } } var queue = []; var draining = false; var currentQueue; var queueIndex = -1; function cleanUpNextTick() { if (!draining || !currentQueue) { return; } draining = false; if (currentQueue.length) { queue = currentQueue.concat(queue); } else { queueIndex = -1; } if (queue.length) { drainQueue(); } } function drainQueue() { if (draining) { return; } var timeout = runTimeout(cleanUpNextTick); draining = true; var len = queue.length; while(len) { currentQueue = queue; queue = []; while (++queueIndex < len) { if (currentQueue) { currentQueue[queueIndex].run(); } } queueIndex = -1; len = queue.length; } currentQueue = null; draining = false; runClearTimeout(timeout); } process.nextTick = function (fun) { var args = new Array(arguments.length - 1); if (arguments.length > 1) { for (var i = 1; i < arguments.length; i++) { args[i - 1] = arguments[i]; } } queue.push(new Item(fun, args)); if (queue.length === 1 && !draining) { runTimeout(drainQueue); } }; // v8 likes predictible objects function Item(fun, array) { this.fun = fun; this.array = array; } Item.prototype.run = function () { this.fun.apply(null, this.array); }; process.title = 'browser'; process.browser = true; process.env = {}; process.argv = []; process.version = ''; // empty string to avoid regexp issues process.versions = {}; function noop() {} process.on = noop; process.addListener = noop; process.once = noop; process.off = noop; process.removeListener = noop; process.removeAllListeners = noop; process.emit = noop; process.binding = function (name) { throw new Error('process.binding is not supported'); }; process.cwd = function () { return '/' }; process.chdir = function (dir) { throw new Error('process.chdir is not supported'); }; process.umask = function() { return 0; }; },{}],2:[function(require,module,exports){ "use strict"; module.exports = { INVALID_TYPE: "Expected type {0} but found type {1}", INVALID_FORMAT: "Object didn't pass validation for format {0}: {1}", ENUM_MISMATCH: "No enum match for: {0}", ANY_OF_MISSING: "Data does not match any schemas from 'anyOf'", ONE_OF_MISSING: "Data does not match any schemas from 'oneOf'", ONE_OF_MULTIPLE: "Data is valid against more than one schema from 'oneOf'", NOT_PASSED: "Data matches schema from 'not'", // Array errors ARRAY_LENGTH_SHORT: "Array is too short ({0}), minimum {1}", ARRAY_LENGTH_LONG: "Array is too long ({0}), maximum {1}", ARRAY_UNIQUE: "Array items are not unique (indexes {0} and {1})", ARRAY_ADDITIONAL_ITEMS: "Additional items not allowed", // Numeric errors MULTIPLE_OF: "Value {0} is not a multiple of {1}", MINIMUM: "Value {0} is less than minimum {1}", MINIMUM_EXCLUSIVE: "Value {0} is equal or less than exclusive minimum {1}", MAXIMUM: "Value {0} is greater than maximum {1}", MAXIMUM_EXCLUSIVE: "Value {0} is equal or greater than exclusive maximum {1}", // Object errors OBJECT_PROPERTIES_MINIMUM: "Too few properties defined ({0}), minimum {1}", OBJECT_PROPERTIES_MAXIMUM: "Too many properties defined ({0}), maximum {1}", OBJECT_MISSING_REQUIRED_PROPERTY: "Missing required property: {0}", OBJECT_ADDITIONAL_PROPERTIES: "Additional properties not allowed: {0}", OBJECT_DEPENDENCY_KEY: "Dependency failed - key must exist: {0} (due to key: {1})", // String errors MIN_LENGTH: "String is too short ({0} chars), minimum {1}", MAX_LENGTH: "String is too long ({0} chars), maximum {1}", PATTERN: "String does not match pattern {0}: {1}", // Schema validation errors KEYWORD_TYPE_EXPECTED: "Keyword '{0}' is expected to be of type '{1}'", KEYWORD_UNDEFINED_STRICT: "Keyword '{0}' must be defined in strict mode", KEYWORD_UNEXPECTED: "Keyword '{0}' is not expected to appear in the schema", KEYWORD_MUST_BE: "Keyword '{0}' must be {1}", KEYWORD_DEPENDENCY: "Keyword '{0}' requires keyword '{1}'", KEYWORD_PATTERN: "Keyword '{0}' is not a valid RegExp pattern: {1}", KEYWORD_VALUE_TYPE: "Each element of keyword '{0}' array must be a '{1}'", UNKNOWN_FORMAT: "There is no validation function for format '{0}'", CUSTOM_MODE_FORCE_PROPERTIES: "{0} must define at least one property if present", // Remote errors REF_UNRESOLVED: "Reference has not been resolved during compilation: {0}", UNRESOLVABLE_REFERENCE: "Reference could not be resolved: {0}", SCHEMA_NOT_REACHABLE: "Validator was not able to read schema with uri: {0}", SCHEMA_TYPE_EXPECTED: "Schema is expected to be of type 'object'", SCHEMA_NOT_AN_OBJECT: "Schema is not an object: {0}", ASYNC_TIMEOUT: "{0} asynchronous task(s) have timed out after {1} ms", PARENT_SCHEMA_VALIDATION_FAILED: "Schema failed to validate against its parent schema, see inner errors for details.", REMOTE_NOT_VALID: "Remote reference didn't compile successfully: {0}" }; },{}],3:[function(require,module,exports){ /*jshint maxlen: false*/ var validator = window.validator; var FormatValidators = { "date": function (date) { if (typeof date !== "string") { return true; } // full-date from http://tools.ietf.org/html/rfc3339#section-5.6 var matches = /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/.exec(date); if (matches === null) { return false; } // var year = matches[1]; // var month = matches[2]; // var day = matches[3]; if (matches[2] < "01" || matches[2] > "12" || matches[3] < "01" || matches[3] > "31") { return false; } return true; }, "date-time": function (dateTime) { if (typeof dateTime !== "string") { return true; } // date-time from http://tools.ietf.org/html/rfc3339#section-5.6 var s = dateTime.toLowerCase().split("t"); if (!FormatValidators.date(s[0])) { return false; } var matches = /^([0-9]{2}):([0-9]{2}):([0-9]{2})(.[0-9]+)?(z|([+-][0-9]{2}:[0-9]{2}))$/.exec(s[1]); if (matches === null) { return false; } // var hour = matches[1]; // var minute = matches[2]; // var second = matches[3]; // var fraction = matches[4]; // var timezone = matches[5]; if (matches[1] > "23" || matches[2] > "59" || matches[3] > "59") { return false; } return true; }, "email": function (email) { if (typeof email !== "string") { return true; } return validator.isEmail(email, { "require_tld": true }); }, "hostname": function (hostname) { if (typeof hostname !== "string") { return true; } /* http://json-schema.org/latest/json-schema-validation.html#anchor114 A string instance is valid against this attribute if it is a valid representation for an Internet host name, as defined by RFC 1034, section 3.1 [RFC1034]. http://tools.ietf.org/html/rfc1034#section-3.5 <digit> ::= any one of the ten digits 0 through 9 var digit = /[0-9]/; <letter> ::= any one of the 52 alphabetic characters A through Z in upper case and a through z in lower case var letter = /[a-zA-Z]/; <let-dig> ::= <letter> | <digit> var letDig = /[0-9a-zA-Z]/; <let-dig-hyp> ::= <let-dig> | "-" var letDigHyp = /[-0-9a-zA-Z]/; <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str> var ldhStr = /[-0-9a-zA-Z]+/; <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ] var label = /[a-zA-Z](([-0-9a-zA-Z]+)?[0-9a-zA-Z])?/; <subdomain> ::= <label> | <subdomain> "." <label> var subdomain = /^[a-zA-Z](([-0-9a-zA-Z]+)?[0-9a-zA-Z])?(\.[a-zA-Z](([-0-9a-zA-Z]+)?[0-9a-zA-Z])?)*$/; <domain> ::= <subdomain> | " " var domain = null; */ var valid = /^[a-zA-Z](([-0-9a-zA-Z]+)?[0-9a-zA-Z])?(\.[a-zA-Z](([-0-9a-zA-Z]+)?[0-9a-zA-Z])?)*$/.test(hostname); if (valid) { // the sum of all label octets and label lengths is limited to 255. if (hostname.length > 255) { return false; } // Each node has a label, which is zero to 63 octets in length var labels = hostname.split("."); for (var i = 0; i < labels.length; i++) { if (labels[i].length > 63) { return false; } } } return valid; }, "host-name": function (hostname) { return FormatValidators.hostname.call(this, hostname); }, "ipv4": function (ipv4) { if (typeof ipv4 !== "string") { return true; } return validator.isIP(ipv4, 4); }, "ipv6": function (ipv6) { if (typeof ipv6 !== "string") { return true; } return validator.isIP(ipv6, 6); }, "regex": function (str) { try { RegExp(str); return true; } catch (e) { return false; } }, "uri": function (uri) { if (this.options.strictUris) { return FormatValidators["strict-uri"].apply(this, arguments); } // https://github.com/zaggino/z-schema/issues/18 // RegExp from http://tools.ietf.org/html/rfc3986#appendix-B return typeof uri !== "string" || RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?").test(uri); }, "strict-uri": function (uri) { return typeof uri !== "string" || validator.isURL(uri); } }; module.exports = FormatValidators; },{}],4:[function(require,module,exports){ "use strict"; var FormatValidators = require("./FormatValidators"), Report = require("./Report"), Utils = require("./Utils"); var JsonValidators = { multipleOf: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.1.1.2 if (typeof json !== "number") { return; } if (Utils.whatIs(json / schema.multipleOf) !== "integer") { report.addError("MULTIPLE_OF", [json, schema.multipleOf], null, schema.description); } }, maximum: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.1.2.2 if (typeof json !== "number") { return; } if (schema.exclusiveMaximum !== true) { if (json > schema.maximum) { report.addError("MAXIMUM", [json, schema.maximum], null, schema.description); } } else { if (json >= schema.maximum) { report.addError("MAXIMUM_EXCLUSIVE", [json, schema.maximum], null, schema.description); } } }, exclusiveMaximum: function () { // covered in maximum }, minimum: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.1.3.2 if (typeof json !== "number") { return; } if (schema.exclusiveMinimum !== true) { if (json < schema.minimum) { report.addError("MINIMUM", [json, schema.minimum], null, schema.description); } } else { if (json <= schema.minimum) { report.addError("MINIMUM_EXCLUSIVE", [json, schema.minimum], null, schema.description); } } }, exclusiveMinimum: function () { // covered in minimum }, maxLength: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.2.1.2 if (typeof json !== "string") { return; } if (Utils.ucs2decode(json).length > schema.maxLength) { report.addError("MAX_LENGTH", [json.length, schema.maxLength], null, schema.description); } }, minLength: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.2.2.2 if (typeof json !== "string") { return; } if (Utils.ucs2decode(json).length < schema.minLength) { report.addError("MIN_LENGTH", [json.length, schema.minLength], null, schema.description); } }, pattern: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.2.3.2 if (typeof json !== "string") { return; } if (RegExp(schema.pattern).test(json) === false) { report.addError("PATTERN", [schema.pattern, json], null, schema.description); } }, additionalItems: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.3.1.2 if (!Array.isArray(json)) { return; } // if the value of "additionalItems" is boolean value false and the value of "items" is an array, // the json is valid if its size is less than, or equal to, the size of "items". if (schema.additionalItems === false && Array.isArray(schema.items)) { if (json.length > schema.items.length) { report.addError("ARRAY_ADDITIONAL_ITEMS", null, null, schema.description); } } }, items: function () { /*report, schema, json*/ // covered in additionalItems }, maxItems: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.3.2.2 if (!Array.isArray(json)) { return; } if (json.length > schema.maxItems) { report.addError("ARRAY_LENGTH_LONG", [json.length, schema.maxItems], null, schema.description); } }, minItems: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.3.3.2 if (!Array.isArray(json)) { return; } if (json.length < schema.minItems) { report.addError("ARRAY_LENGTH_SHORT", [json.length, schema.minItems], null, schema.description); } }, uniqueItems: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.3.4.2 if (!Array.isArray(json)) { return; } if (schema.uniqueItems === true) { var matches = []; if (Utils.isUniqueArray(json, matches) === false) { report.addError("ARRAY_UNIQUE", matches, null, schema.description); } } }, maxProperties: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.4.1.2 if (Utils.whatIs(json) !== "object") { return; } var keysCount = Object.keys(json).length; if (keysCount > schema.maxProperties) { report.addError("OBJECT_PROPERTIES_MAXIMUM", [keysCount, schema.maxProperties], null, schema.description); } }, minProperties: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.4.2.2 if (Utils.whatIs(json) !== "object") { return; } var keysCount = Object.keys(json).length; if (keysCount < schema.minProperties) { report.addError("OBJECT_PROPERTIES_MINIMUM", [keysCount, schema.minProperties], null, schema.description); } }, required: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.4.3.2 if (Utils.whatIs(json) !== "object") { return; } var idx = schema.required.length; while (idx--) { var requiredPropertyName = schema.required[idx]; if (json[requiredPropertyName] === undefined) { report.addError("OBJECT_MISSING_REQUIRED_PROPERTY", [requiredPropertyName], null, schema.description); } } }, additionalProperties: function (report, schema, json) { // covered in properties and patternProperties if (schema.properties === undefined && schema.patternProperties === undefined) { return JsonValidators.properties.call(this, report, schema, json); } }, patternProperties: function (report, schema, json) { // covered in properties if (schema.properties === undefined) { return JsonValidators.properties.call(this, report, schema, json); } }, properties: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.4.4.2 if (Utils.whatIs(json) !== "object") { return; } var properties = schema.properties !== undefined ? schema.properties : {}; var patternProperties = schema.patternProperties !== undefined ? schema.patternProperties : {}; if (schema.additionalProperties === false) { // The property set of the json to validate. var s = Object.keys(json); // The property set from "properties". var p = Object.keys(properties); // The property set from "patternProperties". var pp = Object.keys(patternProperties); // remove from "s" all elements of "p", if any; s = Utils.difference(s, p); // for each regex in "pp", remove all elements of "s" which this regex matches. var idx = pp.length; while (idx--) { var regExp = RegExp(pp[idx]), idx2 = s.length; while (idx2--) { if (regExp.test(s[idx2]) === true) { s.splice(idx2, 1); } } } // Validation of the json succeeds if, after these two steps, set "s" is empty. if (s.length > 0) { // assumeAdditional can be an array of allowed properties var idx3 = this.options.assumeAdditional.length; if (idx3) { while (idx3--) { var io = s.indexOf(this.options.assumeAdditional[idx3]); if (io !== -1) { s.splice(io, 1); } } } if (s.length > 0) { report.addError("OBJECT_ADDITIONAL_PROPERTIES", [s], null, schema.description); } } } }, dependencies: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.4.5.2 if (Utils.whatIs(json) !== "object") { return; } var keys = Object.keys(schema.dependencies), idx = keys.length; while (idx--) { // iterate all dependencies var dependencyName = keys[idx]; if (json[dependencyName]) { var dependencyDefinition = schema.dependencies[dependencyName]; if (Utils.whatIs(dependencyDefinition) === "object") { // if dependency is a schema, validate against this schema exports.validate.call(this, report, dependencyDefinition, json); } else { // Array // if dependency is an array, object needs to have all properties in this array var idx2 = dependencyDefinition.length; while (idx2--) { var requiredPropertyName = dependencyDefinition[idx2]; if (json[requiredPropertyName] === undefined) { report.addError("OBJECT_DEPENDENCY_KEY", [requiredPropertyName, dependencyName], null, schema.description); } } } } } }, enum: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.1.2 var match = false, idx = schema.enum.length; while (idx--) { if (Utils.areEqual(json, schema.enum[idx])) { match = true; break; } } if (match === false) { report.addError("ENUM_MISMATCH", [json], null, schema.description); } }, /* type: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.2.2 // type is handled before this is called so ignore }, */ allOf: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.3.2 var idx = schema.allOf.length; while (idx--) { var validateResult = exports.validate.call(this, report, schema.allOf[idx], json); if (this.options.breakOnFirstError && validateResult === false) { break; } } }, anyOf: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.4.2 var subReports = [], passed = false, idx = schema.anyOf.length; while (idx-- && passed === false) { var subReport = new Report(report); subReports.push(subReport); passed = exports.validate.call(this, subReport, schema.anyOf[idx], json); } if (passed === false) { report.addError("ANY_OF_MISSING", undefined, subReports, schema.description); } }, oneOf: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.5.2 var passes = 0, subReports = [], idx = schema.oneOf.length; while (idx--) { var subReport = new Report(report, { maxErrors: 1 }); subReports.push(subReport); if (exports.validate.call(this, subReport, schema.oneOf[idx], json) === true) { passes++; } } if (passes === 0) { report.addError("ONE_OF_MISSING", undefined, subReports, schema.description); } else if (passes > 1) { report.addError("ONE_OF_MULTIPLE", null, null, schema.description); } }, not: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.6.2 var subReport = new Report(report); if (exports.validate.call(this, subReport, schema.not, json) === true) { report.addError("NOT_PASSED", null, null, schema.description); } }, definitions: function () { /*report, schema, json*/ // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.7.2 // nothing to do here }, format: function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.7.2 var formatValidatorFn = FormatValidators[schema.format]; if (typeof formatValidatorFn === "function") { if (formatValidatorFn.length === 2) { // async report.addAsyncTask(formatValidatorFn, [json], function (result) { if (result !== true) { report.addError("INVALID_FORMAT", [schema.format, json], null, schema.description); } }); } else { // sync if (formatValidatorFn.call(this, json) !== true) { report.addError("INVALID_FORMAT", [schema.format, json], null, schema.description); } } } else if (this.options.ignoreUnknownFormats !== true) { report.addError("UNKNOWN_FORMAT", [schema.format], null, schema.description); } } }; var recurseArray = function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.8.2 var idx = json.length; // If "items" is an array, this situation, the schema depends on the index: // if the index is less than, or equal to, the size of "items", // the child instance must be valid against the corresponding schema in the "items" array; // otherwise, it must be valid against the schema defined by "additionalItems". if (Array.isArray(schema.items)) { while (idx--) { // equal to doesnt make sense here if (idx < schema.items.length) { report.path.push(idx.toString()); exports.validate.call(this, report, schema.items[idx], json[idx]); report.path.pop(); } else { // might be boolean, so check that it's an object if (typeof schema.additionalItems === "object") { report.path.push(idx.toString()); exports.validate.call(this, report, schema.additionalItems, json[idx]); report.path.pop(); } } } } else if (typeof schema.items === "object") { // If items is a schema, then the child instance must be valid against this schema, // regardless of its index, and regardless of the value of "additionalItems". while (idx--) { report.path.push(idx.toString()); exports.validate.call(this, report, schema.items, json[idx]); report.path.pop(); } } }; var recurseObject = function (report, schema, json) { // http://json-schema.org/latest/json-schema-validation.html#rfc.section.8.3 // If "additionalProperties" is absent, it is considered present with an empty schema as a value. // In addition, boolean value true is considered equivalent to an empty schema. var additionalProperties = schema.additionalProperties; if (additionalProperties === true || additionalProperties === undefined) { additionalProperties = {}; } // p - The property set from "properties". var p = schema.properties ? Object.keys(schema.properties) : []; // pp - The property set from "patternProperties". Elements of this set will be called regexes for convenience. var pp = schema.patternProperties ? Object.keys(schema.patternProperties) : []; // m - The property name of the child. var keys = Object.keys(json), idx = keys.length; while (idx--) { var m = keys[idx], propertyValue = json[m]; // s - The set of schemas for the child instance. var s = []; // 1. If set "p" contains value "m", then the corresponding schema in "properties" is added to "s". if (p.indexOf(m) !== -1) { s.push(schema.properties[m]); } // 2. For each regex in "pp", if it matches "m" successfully, the corresponding schema in "patternProperties" is added to "s". var idx2 = pp.length; while (idx2--) { var regexString = pp[idx2]; if (RegExp(regexString).test(m) === true) { s.push(schema.patternProperties[regexString]); } } // 3. The schema defined by "additionalProperties" is added to "s" if and only if, at this stage, "s" is empty. if (s.length === 0 && additionalProperties !== false) { s.push(additionalProperties); } // we are passing tests even without this assert because this is covered by properties check // if s is empty in this stage, no additionalProperties are allowed // report.expect(s.length !== 0, 'E001', m); // Instance property value must pass all schemas from s idx2 = s.length; while (idx2--) { report.path.push(m); exports.validate.call(this, report, s[idx2], propertyValue); report.path.pop(); } } }; exports.validate = function (report, schema, json) { report.commonErrorMessage = "JSON_OBJECT_VALIDATION_FAILED"; // check if schema is an object var to = Utils.whatIs(schema); if (to !== "object") { report.addError("SCHEMA_NOT_AN_OBJECT", [to], null, schema.description); return false; } // check if schema is empty, everything is valid against empty schema var keys = Object.keys(schema); if (keys.length === 0) { return true; } // this method can be called recursively, so we need to remember our root var isRoot = false; if (!report.rootSchema) { report.rootSchema = schema; isRoot = true; } // follow schema.$ref keys if (schema.$ref !== undefined) { // avoid infinite loop with maxRefs var maxRefs = 99; while (schema.$ref && maxRefs > 0) { if (!schema.__$refResolved) { report.addError("REF_UNRESOLVED", [schema.$ref], null, schema.description); break; } else if (schema.__$refResolved === schema) { break; } else { schema = schema.__$refResolved; keys = Object.keys(schema); } maxRefs--; } if (maxRefs === 0) { throw new Error("Circular dependency by $ref references!"); } } // type checking first // http://json-schema.org/latest/json-schema-validation.html#rfc.section.5.5.2.2 var jsonType = Utils.whatIs(json); if (schema.type) { if (typeof schema.type === "string") { if (jsonType !== schema.type && (jsonType !== "integer" || schema.type !== "number")) { report.addError("INVALID_TYPE", [schema.type, jsonType], null, schema.description); if (this.options.breakOnFirstError) { return false; } } } else { if (schema.type.indexOf(jsonType) === -1 && (jsonType !== "integer" || schema.type.indexOf("number") === -1)) { report.addError("INVALID_TYPE", [schema.type, jsonType], null, schema.description); if (this.options.breakOnFirstError) { return false; } } } } // now iterate all the keys in schema and execute validation methods var idx = keys.length; while (idx--) { if (JsonValidators[keys[idx]]) { JsonValidators[keys[idx]].call(this, report, schema, json); if (report.errors.length && this.options.breakOnFirstError) { break; } } } if (report.errors.length === 0 || this.options.breakOnFirstError === false) { if (jsonType === "array") { recurseArray.call(this, report, schema, json); } else if (jsonType === "object") { recurseObject.call(this, report, schema, json); } } if (typeof this.options.customValidator === "function") { this.options.customValidator(report, schema, json); } // we don't need the root pointer anymore if (isRoot) { report.rootSchema = undefined; } // return valid just to be able to break at some code points return report.errors.length === 0; }; },{"./FormatValidators":3,"./Report":6,"./Utils":10}],5:[function(require,module,exports){ // Number.isFinite polyfill // http://people.mozilla.org/~jorendorff/es6-draft.html#sec-number.isfinite if (typeof Number.isFinite !== "function") { Number.isFinite = function isFinite(value) { // 1. If Type(number) is not Number, return false. if (typeof value !== "number") { return false; } // 2. If number is NaN, +∞, or −∞, return false. if (value !== value || value === Infinity || value === -Infinity) { return false; } // 3. Otherwise, return true. return true; }; } },{}],6:[function(require,module,exports){ (function (process){ "use strict"; var get = _.get; var Errors = require("./Errors"); var Utils = require("./Utils"); function Report(parentOrOptions, reportOptions) { this.parentReport = parentOrOptions instanceof Report ? parentOrOptions : undefined; this.options = parentOrOptions instanceof Report ? parentOrOptions.options : parentOrOptions || {}; this.reportOptions = reportOptions || {}; this.errors = []; this.path = []; this.asyncTasks = []; } Report.prototype.isValid = function () { if (this.asyncTasks.length > 0) { throw new Error("Async tasks pending, can't answer isValid"); } return this.errors.length === 0; }; Report.prototype.addAsyncTask = function (fn, args, asyncTaskResultProcessFn) { this.asyncTasks.push([fn, args, asyncTaskResultProcessFn]); }; Report.prototype.processAsyncTasks = function (timeout, callback) { var validationTimeout = timeout || 2000, tasksCount = this.asyncTasks.length, idx = tasksCount, timedOut = false, self = this; function finish() { process.nextTick(function () { var valid = self.errors.length === 0, err = valid ? undefined : self.errors; callback(err, valid); }); } function respond(asyncTaskResultProcessFn) { return function (asyncTaskResult) { if (timedOut) { return; } asyncTaskResultProcessFn(asyncTaskResult); if (--tasksCount === 0) { finish(); } }; } if (tasksCount === 0 || this.errors.length > 0) { finish(); return; } while (idx--) { var task = this.asyncTasks[idx]; task[0].apply(null, task[1].concat(respond(task[2]))); } setTimeout(function () { if (tasksCount > 0) { timedOut = true; self.addError("ASYNC_TIMEOUT", [tasksCount, validationTimeout]); callback(self.errors, false); } }, validationTimeout); }; Report.prototype.getPath = function (returnPathAsString) { var path = []; if (this.parentReport) { path = path.concat(this.parentReport.path); } path = path.concat(this.path); if (returnPathAsString !== true) { // Sanitize the path segments (http://tools.ietf.org/html/rfc6901#section-4) path = "#/" + path.map(function (segment) { if (Utils.isAbsoluteUri(segment)) { return "uri(" + segment + ")"; } return segment.replace(/\~/g, "~0").replace(/\//g, "~1"); }).join("/"); } return path; }; Report.prototype.getSchemaId = function () { if (!this.rootSchema) { return null; } // get the error path as an array var path = []; if (this.parentReport) { path = path.concat(this.parentReport.path); } path = path.concat(this.path); // try to find id in the error path while (path.length > 0) { var obj = get(this.rootSchema, path); if (obj && obj.id) { return obj.id; } path.pop(); } // return id of the root return this.rootSchema.id; }; Report.prototype.hasError = function (errorCode, params) { var idx = this.errors.length; while (idx--) { if (this.errors[idx].code === errorCode) { // assume match var match = true; // check the params too var idx2 = this.errors[idx].params.length; while (idx2--) { if (this.errors[idx].params[idx2] !== params[idx2]) { match = false; } } // if match, return true if (match) { return match; } } } return false; }; Report.prototype.addError = function (errorCode, params, subReports, schemaDescription) { if (!errorCode) { throw new Error("No errorCode passed into addError()"); } this.addCustomError(errorCode, Errors[errorCode], params, subReports, schemaDescription); }; Report.prototype.addCustomError = function (errorCode, errorMessage, params, subReports, schemaDescription) { if (this.errors.length >= this.reportOptions.maxErrors) { return; } if (!errorMessage) { throw new Error("No errorMessage known for code " + errorCode); } params = params || []; var idx = params.length; while (idx--) { var whatIs = Utils.whatIs(params[idx]); var param = (whatIs === "object" || whatIs === "null") ? JSON.stringify(params[idx]) : params[idx]; errorMessage = errorMessage.replace("{" + idx + "}", param); } var err = { code: errorCode, params: params, message: errorMessage, path: this.getPath(this.options.reportPathAsArray), schemaId: this.getSchemaId() }; if (schemaDescription) { err.description = schemaDescription; } if (subReports != null) { if (!Array.isArray(subReports)) { subReports = [subReports]; } err.inner = []; idx = subReports.length; while (idx--) { var subReport = subReports[idx], idx2 = subReport.errors.length; while (idx2--) { err.inner.push(subReport.errors[idx2]); } } if (err.inner.length === 0) { err.inner = undefined; } } this.errors.push(err); }; module.exports = Report; }).call(this,require('_process')) },{"./Errors":2,"./Utils":10,"_process":1}],7:[function(require,module,exports){ "use strict"; var isequal = _.isEqual; var Report = require("./Report"); var SchemaCompilation = require("./SchemaCompilation"); var SchemaValidation = require("./SchemaValidation"); var Utils = require("./Utils"); function decodeJSONPointer(str) { // http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07#section-3 return decodeURIComponent(str).replace(/~[0-1]/g, function (x) { return x === "~1" ? "/" : "~"; }); } function getRemotePath(uri) { var io = uri.indexOf("#"); return io === -1 ? uri : uri.slice(0, io); } function getQueryPath(uri) { var io = uri.indexOf("#"); var res = io === -1 ? undefined : uri.slice(io + 1); // WARN: do not slice slash, #/ means take root and go down from it // if (res && res[0] === "/") { res = res.slice(1); } return res; } function findId(schema, id) { // process only arrays and objects if (typeof schema !== "object" || schema === null) { return; } // no id means root so return itself if (!id) { return schema; } if (schema.id) { if (schema.id === id || schema.id[0] === "#" && schema.id.substring(1) === id) { return schema; } } var idx, result; if (Array.isArray(schema)) { idx = schema.length; while (idx--) { result = findId(schema[idx], id); if (result) { return result; } } } else { var keys = Object.keys(schema); idx = keys.length; while (idx--) { var k = keys[idx]; if (k.indexOf("__$") === 0) { continue; } result = findId(schema[k], id); if (result) { return result; } } } } exports.cacheSchemaByUri = function (uri, schema) { var remotePath = getRemotePath(uri); if (remotePath) { this.cache[remotePath] = schema; } }; exports.removeFromCacheByUri = function (uri) { var remotePath = getRemotePath(uri); if (remotePath) { delete this.cache[remotePath]; } }; exports.checkCacheForUri = function (uri) { var remotePath = getRemotePath(uri); return remotePath ? this.cache[remotePath] != null : false; }; exports.getSchema = function (report, schema) { if (typeof schema === "object") { schema = exports.getSchemaByReference.call(this, report, schema); } if (typeof schema === "string") { schema = exports.getSchemaByUri.call(this, report, schema); } return schema; }; exports.getSchemaByReference = function (report, key) { var i = this.referenceCache.length; while (i--) { if (isequal(this.referenceCache[i][0], key)) { return this.referenceCache[i][1]; } } // not found var schema = Utils.cloneDeep(key); this.referenceCache.push([key, schema]); return schema; }; exports.getSchemaByUri = function (report, uri, root) { var remotePath = getRemotePath(uri), queryPath = getQueryPath(uri), result = remotePath ? this.cache[remotePath] : root; if (result && remotePath) { // we need to avoid compiling schemas in a recursive loop var compileRemote = result !== root; // now we need to compile and validate resolved schema (in case it's not already) if (compileRemote) { report.path.push(remotePath); var remoteReport = new Report(report); if (SchemaCompilation.compileSchema.call(this, remoteReport, result)) { SchemaValidation.validateSchema.call(this, remoteReport, result); } var remoteReportIsValid = remoteReport.isValid(); if (!remoteReportIsValid) { report.addError("REMOTE_NOT_VALID", [uri], remoteReport); } report.path.pop(); if (!remoteReportIsValid) { return undefined; } } } if (result && queryPath) { var parts = queryPath.split("/"); for (var idx = 0, lim = parts.length; result && idx < lim; idx++) { var key = decodeJSONPointer(parts[idx]); if (idx === 0) { // it's an id result = findId(result, key); } else { // it's a path behind id result = result[key]; } } } return result; }; exports.getRemotePath = getRemotePath; },{"./Report":6,"./SchemaCompilation":8,"./SchemaValidation":9,"./Utils":10}],8:[function(require,module,exports){ "use strict"; var Report = require("./Report"); var SchemaCache = require("./SchemaCache"); var Utils = require("./Utils"); function mergeReference(scope, ref) { if (Utils.isAbsoluteUri(ref)) { return ref; } var joinedScope = scope.join(""), isScopeAbsolute = Utils.isAbsoluteUri(joinedScope), isScopeRelative = Utils.isRelativeUri(joinedScope), isRefRelative = Utils.isRelativeUri(ref), toRemove; if (isScopeAbsolute && isRefRelative) { toRemove = joinedScope.match(/\/[^\/]*$/); if (toRemove) { joinedScope = joinedScope.slice(0, toRemove.index + 1); } } else if (isScopeRelative && isRefRelative) { joinedScope = ""; } else { toRemove = joinedScope.match(/[^#/]+$/); if (toRemove) { joinedScope = joinedScope.slice(0, toRemove.index); } } var res = joinedScope + ref; res = res.replace(/##/, "#"); return res; } function collectReferences(obj, results, scope, path) { results = results || []; scope = scope || []; path = path || []; if (typeof obj !== "object" || obj === null) { return results; } if (typeof obj.id === "string") { scope.push(obj.id); } if (typeof obj.$ref === "string" && typeof obj.__$refResolved === "undefined") { results.push({ ref: mergeReference(scope, obj.$ref), key: "$ref", obj: obj, path: path.slice(0) }); } if (typeof obj.$schema === "string" && typeof obj.__$schemaResolved === "undefined") { results.push({ ref: mergeReference(scope, obj.$schema), key: "$schema", obj: obj, path: path.slice(0) }); } var idx; if (Array.isArray(obj)) { idx = obj.length; while (idx--) { path.push(idx.toString()); collectReferences(obj[idx], results, scope, path); path.pop(); } } else { var keys = Object.keys(obj); idx = keys.length; while (idx--) { // do not recurse through resolved references and other z-schema props if (keys[idx].indexOf("__$") === 0) { continue; } path.push(keys[idx]); collectReferences(obj[