From 5f6ba88b4b848d93e363a5d454734872ca999a46 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Jul 2021 22:23:07 +0000 Subject: [PATCH 1/2] Bump jsonschema from 1.2.6 to 1.4.0 Bumps [jsonschema](https://github.com/tdegrunt/jsonschema) from 1.2.6 to 1.4.0. - [Release notes](https://github.com/tdegrunt/jsonschema/releases) - [Commits](https://github.com/tdegrunt/jsonschema/compare/v1.2.6...v1.4.0) --- updated-dependencies: - dependency-name: jsonschema dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 11 +++++++---- package.json | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index e55f4b68be..7d18b74240 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "github-linguist": "^2.4.3", "glob": "^7.1.7", "js-yaml": "^4.1.0", - "jsonschema": "1.2.6", + "jsonschema": "1.4.0", "long": "^4.0.0", "md5": "^2.2.1", "path": "^0.12.7", @@ -3600,8 +3600,9 @@ } }, "node_modules/jsonschema": { - "version": "1.2.6", - "license": "MIT", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.0.tgz", + "integrity": "sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw==", "engines": { "node": "*" } @@ -8409,7 +8410,9 @@ } }, "jsonschema": { - "version": "1.2.6" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.0.tgz", + "integrity": "sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw==" }, "just-extend": { "version": "4.1.0", diff --git a/package.json b/package.json index dc3a5ddb91..1d21fd2291 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "github-linguist": "^2.4.3", "glob": "^7.1.7", "js-yaml": "^4.1.0", - "jsonschema": "1.2.6", + "jsonschema": "1.4.0", "long": "^4.0.0", "md5": "^2.2.1", "path": "^0.12.7", From 67954db0cf6d75edd3d6aaec6a9650ef348639f7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 27 Jul 2021 22:26:34 +0000 Subject: [PATCH 2/2] Update checked-in dependencies --- node_modules/.package-lock.json | 5 +- node_modules/jsonschema/README.md | 227 ++++++++++++++++--- node_modules/jsonschema/lib/attribute.js | 264 +++++++++++++++++------ node_modules/jsonschema/lib/helpers.js | 99 ++++++--- node_modules/jsonschema/lib/index.d.ts | 19 +- node_modules/jsonschema/lib/index.js | 1 + node_modules/jsonschema/lib/scan.js | 9 +- node_modules/jsonschema/lib/validator.js | 52 +++-- node_modules/jsonschema/package.json | 12 +- 9 files changed, 525 insertions(+), 163 deletions(-) diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index 89e7cbad7c..7100ff08b6 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -3546,8 +3546,9 @@ } }, "node_modules/jsonschema": { - "version": "1.2.6", - "license": "MIT", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.0.tgz", + "integrity": "sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw==", "engines": { "node": "*" } diff --git a/node_modules/jsonschema/README.md b/node_modules/jsonschema/README.md index 3de393d638..fc8ca3ca8a 100644 --- a/node_modules/jsonschema/README.md +++ b/node_modules/jsonschema/README.md @@ -1,18 +1,22 @@ [![Build Status](https://secure.travis-ci.org/tdegrunt/jsonschema.svg)](http://travis-ci.org/tdegrunt/jsonschema) # jsonschema + [JSON schema](http://json-schema.org/) validator, which is designed to be fast and simple to use. The latest IETF published draft is v6, this library is mostly v4 compatible. ## Contributing & bugs + Please fork the repository, make the changes in your fork and include tests. Once you're done making changes, send in a pull request. ### Bug reports + Please include a test which shows why the code fails. ## Usage ### Simple + Simple object validation using JSON schemas. ```javascript @@ -78,6 +82,7 @@ v.addSchema(addressSchema, '/SimpleAddress'); console.log(v.validate(p, schema)); ``` ### Example for Array schema + ```json var arraySchema = { "type": "array", @@ -95,21 +100,42 @@ For a comprehensive, annotated example illustrating all possible validation opti ## Features ### Definitions + All schema definitions are supported, $schema is ignored. ### Types + All types are supported +### Handling `undefined` + +`undefined` is not a value known to JSON, and by default, the validator treats it as if it is not invalid. i.e., it will return valid. + +```javascript +var res = validate(undefined, {type: 'string'}); +res.valid // true +``` + +This behavior may be changed with the "required" option: + +```javascript +var res = validate(undefined, {type: 'string'}, {required: true}); +res.valid // false +``` + ### Formats + #### Disabling the format keyword. You may disable format validation by providing `disableFormat: true` to the validator options. #### String Formats + All formats are supported, phone numbers are expected to follow the [E.123](http://en.wikipedia.org/wiki/E.123) standard. #### Custom Formats + You may add your own custom format functions. Format functions accept the input being validated and return a boolean value. If the returned value is `true`, then validation succeeds. If the returned value is `false`, then validation fails. @@ -133,27 +159,86 @@ validator.validate('foo', {type: 'string', format: 'myFormat'}).valid; // false ``` ### Results -The first error found will be thrown as an `Error` object if `options.throwError` is `true`. Otherwise all results will be appended to the `result.errors` array which also contains the success flag `result.valid`. -When `oneOf` or `anyOf` validations fail, errors that caused any of the sub-schemas referenced therein to fail are not reported, unless `options.nestedErrors` is truthy. This option may be useful when troubleshooting validation errors in complex schemas. +By default, results will be returned in a `ValidatorResult` object with the following properties: + +* `instance`: any. +* `schema`: Schema. +* `errors`: ValidationError[]. +* `valid`: boolean. + +Each item in `errors` is a `ValidationError` with the following properties: + +* path: array. An array of property keys or array offsets, indicating where inside objects or arrays the instance was found. +* property: string. Describes the property path. Starts with `instance`, and is delimited with a dot (`.`). +* message: string. A human-readable message for debugging use. Provided in English and subject to change. +* schema: object. The schema containing the keyword that failed +* instance: any. The instance that failed +* name: string. The keyword within the schema that failed. +* argument: any. Provides information about the keyword that failed. + +The validator can be configured to throw in the event of a validation error: + +* If the `throwFirst` option is set, the validator will terminate validation at the first encountered error and throw a `ValidatorResultError` object. + +* If the `throwAll` option is set, the validator will throw a `ValidatorResultError` object after the entire instance has been validated. + +* If the `throwError` option is set, it will throw at the first encountered validation error (like `throwFirst`), but the `ValidationError` object itself will be thrown. Note that, despite the name, this does not inherit from Error like `ValidatorResultError` does. + +The `ValidatorResultError` object has the same properties as `ValidatorResult` and additionally inherits from Error. + +#### "nestedErrors" option + +When `oneOf` or `anyOf` validations fail, errors that caused any of the sub-schemas referenced therein to fail are normally suppressed, because it is not necessary to fix all of them. And in the case of `oneOf`, it would itself be an error to fix all of the listed errors. + +This behavior may be configured with `options.nestedErrors`. If truthy, it will emit all the errors from the subschemas. This option may be useful when troubleshooting validation errors in complex schemas: + +```javascript +var schema = { + oneOf: [ + { type: 'string', minLength: 32, maxLength: 32 }, + { type: 'string', maxLength: 16 }, + { type: 'number' }, + ] +}; +var validator = new Validator(); +var result = validator.validate('This string is 28 chars long', schema, {nestedErrors: true}); + +// result.toString() reads out: +// 0: instance does not meet minimum length of 32 +// 1: instance does not meet maximum length of 16 +// 2: instance is not of a type(s) number +// 3: instance is not exactly one from [subschema 0],[subschema 1],[subschema 2] +``` + +#### Localizing Error Messages + +To provide localized, human-readable errors, use the `name` string as a translation key. Feel free to open an issue for support relating to localizing error messages. For example: + +``` +var localized = result.errors.map(function(err){ + return localeService.translate(err.name); +}); +``` + +### Custom keywords -### Custom properties -Specify your own JSON Schema properties with the validator.attributes property: +Specify your own JSON Schema keywords with the validator.attributes property: ```javascript validator.attributes.contains = function validateContains(instance, schema, options, ctx) { - if(typeof instance!='string') return; - if(typeof schema.contains!='string') throw new jsonschema.SchemaError('"contains" expects a string', schema); + if(typeof instance !== 'string') return; + if(typeof schema.contains !== 'string') throw new jsonschema.SchemaError('"contains" expects a string', schema); if(instance.indexOf(schema.contains)<0){ return 'does not contain the string ' + JSON.stringify(schema.contains); } } -var result = validator.validate("i am an instance", { type:"string", contains: "i am" }); +var result = validator.validate("I am an instance", { type:"string", contains: "I am" }); // result.valid === true; ``` The instance passes validation if the function returns nothing. A single validation error is produced -if the fuction returns a string. Any number of errors (maybe none at all) may be returned by passing a +if the function returns a string. Any number of errors (maybe none at all) may be returned by passing a `ValidatorResult` object, which may be used like so: ```javascript @@ -165,6 +250,7 @@ if the fuction returns a string. Any number of errors (maybe none at all) may be ``` ### Dereferencing schemas + Sometimes you may want to download schemas from remote sources, like a database, or over HTTP. When importing a schema, unknown references are inserted into the `validator.unresolvedRefs` Array. Asynchronously shift elements off this array and import them: @@ -184,44 +270,119 @@ function importNextSchema(){ importNextSchema(); ``` -### Pre-Property Validation Hook -If some processing of properties is required prior to validation a function may be passed via the options parameter of the validate function. For example, say you needed to perform type coercion for some properties: +### Default base URI + +Schemas should typically have an `id` with an absolute, full URI. However if the schema you are using contains only relative URI references, the `base` option will be used to resolve these. + +This following example would throw a `SchemaError` if the `base` option were unset: + +```javascript +var result = validate(["Name"], { + id: "/schema.json", + type: "array", + items: { $ref: "http://example.com/schema.json#/definitions/item" }, + definitions: { + item: { type: "string" }, + }, +}, { base: 'http://example.com/' }); +``` + +### Rewrite Hook + +The `rewrite` option lets you change the value of an instance after it has successfully been validated. This will mutate the `instance` passed to the validate function. This can be useful for unmarshalling data and parsing it into native instances, such as changing a string to a `Date` instance. + +The `rewrite` option accepts a function with the following arguments: + +* instance: any +* schema: object +* options: object +* ctx: object +* return value: any new value for the instance + +The value may be removed by returning `undefined`. +If you don't want to change the value, call `return instance`. + +Here is an example that can convert a property expecting a date into a Date instance: ```javascript -const coercionHook = function (instance, property, schema, options, ctx) { - var value = instance[property]; +const schema = { + properties: { + date: {id: 'http://example.com/date', type: 'string'}, + }, +}; - // Skip nulls and undefineds - if (value === null || typeof value == 'undefined') { - return; +const value = { + date: '2020-09-30T23:39:27.060Z', +}; + +function unmarshall(instance, schema){ + if(schema.id === 'http://example.com/date'){ + return new Date(instance); } + return instance; +} - // If the schema declares a type and the property fails type validation. - if (schema.type && this.attributes.type.call(this, instance, schema, options, ctx.makeChild(schema, property))) { - var types = Array.isArray(schema.type) ? schema.type : [schema.type]; - var coerced = undefined; - - // Go through the declared types until we find something that we can - // coerce the value into. - for (var i = 0; typeof coerced == 'undefined' && i < types.length; i++) { - // If we support coercion to this type - if (lib.coercions[types[i]]) { - // ...attempt it. - coerced = lib.coercions[types[i]](value); - } +const v = new Validator(); +const res = v.validate(value, schema, {rewrite: unmarshall}); + +assert(res.instance.date instanceof Date); +``` + + +### Pre-Property Validation Hook + +If some processing of properties is required prior to validation a function may be passed via the options parameter of the validate function. For example, say you needed to perform type coercion for some properties: + +```javascript +// See examples/coercion.js +function preValidateProperty(object, key, schema, options, ctx) { + var value = object[key]; + if (typeof value === 'undefined') return; + + // Test if the schema declares a type, but the type keyword fails validation + if (schema.type && validator.attributes.type.call(validator, value, schema, options, ctx.makeChild(schema, key))) { + // If the type is "number" but the instance is not a number, cast it + if(schema.type==='number' && typeof value!=='number'){ + object[key] = parseFloat(value); + return; } - // If we got a successful coercion we modify the property of the instance. - if (typeof coerced != 'undefined') { - instance[property] = coerced; + // If the type is "string" but the instance is not a string, cast it + if(schema.type==='string' && typeof value!=='string'){ + object[key] = String(value).toString(); + return; } } -}.bind(validator) +}; // And now, to actually perform validation with the coercion hook! -v.validate(instance, schema, { preValidateProperty: coercionHook }); +v.validate(instance, schema, { preValidateProperty }); +``` + +### Skip validation of certain keywords + +Use the "skipAttributes" option to skip validation of certain keywords. Provide an array of keywords to ignore. + +For skipping the "format" keyword, see the disableFormat option. + +### Fail on unknown keywords + +By default, JSON Schema is supposed to ignore unknown schema keywords. + +You can change this behavior to require that all keywords used in a schema have a defined behavior, by using setting the "allowUnknownAttributes" option to false. + +This example will throw a `SchemaError`: + +```javascript +var schema = { + type: "string", + format: "email", + example: "foo", +}; +var result = validate("Name", schema, { allowUnknownAttributes: false }); ``` ## Tests + Uses [JSON Schema Test Suite](https://github.com/json-schema/JSON-Schema-Test-Suite) as well as our own tests. You'll need to update and init the git submodules: diff --git a/node_modules/jsonschema/lib/attribute.js b/node_modules/jsonschema/lib/attribute.js index 1a2577ee0f..415a25abea 100644 --- a/node_modules/jsonschema/lib/attribute.js +++ b/node_modules/jsonschema/lib/attribute.js @@ -16,13 +16,13 @@ attribute.ignoreProperties = { 'description': true, 'title': true, // arguments to other properties - 'exclusiveMinimum': true, - 'exclusiveMaximum': true, 'additionalItems': true, + 'then': true, + 'else': true, // special-handled properties '$schema': true, '$ref': true, - 'extends': true + 'extends': true, }; /** @@ -47,7 +47,9 @@ validators.type = function validateType (instance, schema, options, ctx) { var types = Array.isArray(schema.type) ? schema.type : [schema.type]; if (!types.some(this.testType.bind(this, instance, schema, options, ctx))) { var list = types.map(function (v) { - return v.id && ('<' + v.id + '>') || (v+''); + if(!v) return; + var id = v.$id || v.id; + return id ? ('<' + id + '>') : (v+''); }); result.addError({ name: 'type', @@ -60,9 +62,12 @@ validators.type = function validateType (instance, schema, options, ctx) { function testSchemaNoThrow(instance, options, ctx, callback, schema){ var throwError = options.throwError; + var throwAll = options.throwAll; options.throwError = false; + options.throwAll = false; var res = this.validateSchema(instance, schema, options, ctx); options.throwError = throwError; + options.throwAll = throwAll; if (!res.valid && callback instanceof Function) { callback(res); @@ -91,9 +96,11 @@ validators.anyOf = function validateAnyOf (instance, schema, options, ctx) { if (!schema.anyOf.some( testSchemaNoThrow.bind( this, instance, options, ctx, function(res){inner.importErrors(res);} - ))) { + ))) { var list = schema.anyOf.map(function (v, i) { - return (v.id && ('<' + v.id + '>')) || (v.title && JSON.stringify(v.title)) || (v['$ref'] && ('<' + v['$ref'] + '>')) || '[subschema '+i+']'; + var id = v.$id || v.id; + if(id) return '<' + id + '>'; + return(v.title && JSON.stringify(v.title)) || (v['$ref'] && ('<' + v['$ref'] + '>')) || '[subschema '+i+']'; }); if (options.nestedErrors) { result.importErrors(inner); @@ -128,7 +135,8 @@ validators.allOf = function validateAllOf (instance, schema, options, ctx) { schema.allOf.forEach(function(v, i){ var valid = self.validateSchema(instance, v, options, ctx); if(!valid.valid){ - var msg = (v.id && ('<' + v.id + '>')) || (v.title && JSON.stringify(v.title)) || (v['$ref'] && ('<' + v['$ref'] + '>')) || '[subschema '+i+']'; + var id = v.$id || v.id; + var msg = id || (v.title && JSON.stringify(v.title)) || (v['$ref'] && ('<' + v['$ref'] + '>')) || '[subschema '+i+']'; result.addError({ name: 'allOf', argument: { id: msg, length: valid.errors.length, valid: valid }, @@ -161,9 +169,10 @@ validators.oneOf = function validateOneOf (instance, schema, options, ctx) { var count = schema.oneOf.filter( testSchemaNoThrow.bind( this, instance, options, ctx, function(res) {inner.importErrors(res);} - ) ).length; + ) ).length; var list = schema.oneOf.map(function (v, i) { - return (v.id && ('<' + v.id + '>')) || (v.title && JSON.stringify(v.title)) || (v['$ref'] && ('<' + v['$ref'] + '>')) || '[subschema '+i+']'; + var id = v.$id || v.id; + return id || (v.title && JSON.stringify(v.title)) || (v['$ref'] && ('<' + v['$ref'] + '>')) || '[subschema '+i+']'; }); if (count!==1) { if (options.nestedErrors) { @@ -178,6 +187,70 @@ validators.oneOf = function validateOneOf (instance, schema, options, ctx) { return result; }; +/** + * Validates "then" or "else" depending on the result of validating "if" + * @param instance + * @param schema + * @param options + * @param ctx + * @return {String|null} + */ +validators.if = function validateIf (instance, schema, options, ctx) { + // Ignore undefined instances + if (instance === undefined) return null; + if (!helpers.isSchema(schema.if)) throw new Error('Expected "if" keyword to be a schema'); + var ifValid = testSchemaNoThrow.call(this, instance, options, ctx, null, schema.if); + var result = new ValidatorResult(instance, schema, options, ctx); + var res; + if(ifValid){ + if (schema.then === undefined) return; + if (!helpers.isSchema(schema.then)) throw new Error('Expected "then" keyword to be a schema'); + res = this.validateSchema(instance, schema.then, options, ctx.makeChild(schema.then)); + result.importErrors(res); + }else{ + if (schema.else === undefined) return; + if (!helpers.isSchema(schema.else)) throw new Error('Expected "else" keyword to be a schema'); + res = this.validateSchema(instance, schema.else, options, ctx.makeChild(schema.else)); + result.importErrors(res); + } + return result; +}; + +function getEnumerableProperty(object, key){ + // Determine if `key` shows up in `for(var key in object)` + // First test Object.hasOwnProperty.call as an optimization: that guarantees it does + if(Object.hasOwnProperty.call(object, key)) return object[key]; + // Test `key in object` as an optimization; false means it won't + if(!(key in object)) return; + while( (object = Object.getPrototypeOf(object)) ){ + if(Object.propertyIsEnumerable.call(object, key)) return object[key]; + } +} + +/** + * Validates propertyNames + * @param instance + * @param schema + * @param options + * @param ctx + * @return {String|null|ValidatorResult} + */ +validators.propertyNames = function validatePropertyNames (instance, schema, options, ctx) { + if(!this.types.object(instance)) return; + var result = new ValidatorResult(instance, schema, options, ctx); + var subschema = schema.propertyNames!==undefined ? schema.propertyNames : {}; + if(!helpers.isSchema(subschema)) throw new SchemaError('Expected "propertyNames" to be a schema (object or boolean)'); + + for (var property in instance) { + if(getEnumerableProperty(instance, property) !== undefined){ + var res = this.validateSchema(property, subschema, options, ctx.makeChild(subschema)); + result.importErrors(res); + } + } + + return result; +}; + /** * Validates properties * @param instance @@ -191,12 +264,17 @@ validators.properties = function validateProperties (instance, schema, options, var result = new ValidatorResult(instance, schema, options, ctx); var properties = schema.properties || {}; for (var property in properties) { + var subschema = properties[property]; + if(subschema===undefined){ + continue; + }else if(subschema===null){ + throw new SchemaError('Unexpected null, expected schema in "properties"'); + } if (typeof options.preValidateProperty == 'function') { - options.preValidateProperty(instance, property, properties[property], options, ctx); + options.preValidateProperty(instance, property, subschema, options, ctx); } - - var prop = Object.hasOwnProperty.call(instance, property) ? instance[property] : undefined; - var res = this.validateSchema(prop, properties[property], options, ctx.makeChild(properties[property], property)); + var prop = getEnumerableProperty(instance, property); + var res = this.validateSchema(prop, subschema, options, ctx.makeChild(subschema, property)); if(res.instance !== result.instance[property]) result.instance[property] = res.instance; result.importErrors(res); } @@ -206,7 +284,7 @@ validators.properties = function validateProperties (instance, schema, options, /** * Test a specific property within in instance against the additionalProperties schema attribute * This ignores properties with definitions in the properties schema attribute, but no other attributes. - * If too many more types of property-existance tests pop up they may need their own class of tests (like `type` has) + * If too many more types of property-existence tests pop up they may need their own class of tests (like `type` has) * @private * @return {boolean} */ @@ -219,7 +297,7 @@ function testAdditionalProperty (instance, schema, options, ctx, property, resul result.addError({ name: 'additionalProperties', argument: property, - message: "additionalProperty " + JSON.stringify(property) + " exists in instance when not allowed", + message: "is not allowed to have the additional property " + JSON.stringify(property), }); } else { var additionalProperties = schema.additionalProperties || {}; @@ -250,17 +328,29 @@ validators.patternProperties = function validatePatternProperties (instance, sch for (var property in instance) { var test = true; for (var pattern in patternProperties) { - var expr = new RegExp(pattern); - if (!expr.test(property)) { + var subschema = patternProperties[pattern]; + if(subschema===undefined){ + continue; + }else if(subschema===null){ + throw new SchemaError('Unexpected null, expected schema in "patternProperties"'); + } + try { + var regexp = new RegExp(pattern, 'u'); + } catch(_e) { + // In the event the stricter handling causes an error, fall back on the forgiving handling + // DEPRECATED + regexp = new RegExp(pattern); + } + if (!regexp.test(property)) { continue; } test = false; if (typeof options.preValidateProperty == 'function') { - options.preValidateProperty(instance, property, patternProperties[pattern], options, ctx); + options.preValidateProperty(instance, property, subschema, options, ctx); } - var res = this.validateSchema(instance[property], patternProperties[pattern], options, ctx.makeChild(patternProperties[pattern], property)); + var res = this.validateSchema(instance[property], subschema, options, ctx.makeChild(subschema, property)); if(res.instance !== result.instance[property]) result.instance[property] = res.instance; result.importErrors(res); } @@ -308,7 +398,7 @@ validators.minProperties = function validateMinProperties (instance, schema, opt name: 'minProperties', argument: schema.minProperties, message: "does not meet minimum property length of " + schema.minProperties, - }) + }); } return result; }; @@ -375,18 +465,22 @@ validators.items = function validateItems (instance, schema, options, ctx) { validators.minimum = function validateMinimum (instance, schema, options, ctx) { if (!this.types.number(instance)) return; var result = new ValidatorResult(instance, schema, options, ctx); - var valid = true; if (schema.exclusiveMinimum && schema.exclusiveMinimum === true) { - valid = instance > schema.minimum; + if(!(instance > schema.minimum)){ + result.addError({ + name: 'minimum', + argument: schema.minimum, + message: "must be greater than " + schema.minimum, + }); + } } else { - valid = instance >= schema.minimum; - } - if (!valid) { - result.addError({ - name: 'minimum', - argument: schema.minimum, - message: "must have a minimum value of " + schema.minimum, - }); + if(!(instance >= schema.minimum)){ + result.addError({ + name: 'minimum', + argument: schema.minimum, + message: "must be greater than or equal to " + schema.minimum, + }); + } } return result; }; @@ -400,17 +494,65 @@ validators.minimum = function validateMinimum (instance, schema, options, ctx) { validators.maximum = function validateMaximum (instance, schema, options, ctx) { if (!this.types.number(instance)) return; var result = new ValidatorResult(instance, schema, options, ctx); - var valid; if (schema.exclusiveMaximum && schema.exclusiveMaximum === true) { - valid = instance < schema.maximum; + if(!(instance < schema.maximum)){ + result.addError({ + name: 'maximum', + argument: schema.maximum, + message: "must be less than " + schema.maximum, + }); + } } else { - valid = instance <= schema.maximum; + if(!(instance <= schema.maximum)){ + result.addError({ + name: 'maximum', + argument: schema.maximum, + message: "must be less than or equal to " + schema.maximum, + }); + } } + return result; +}; + +/** + * Validates the number form of exclusiveMinimum when the type of the instance value is a number. + * @param instance + * @param schema + * @return {String|null} + */ +validators.exclusiveMinimum = function validateExclusiveMinimum (instance, schema, options, ctx) { + // Support the boolean form of exclusiveMinimum, which is handled by the "minimum" keyword. + if(typeof schema.exclusiveMaximum === 'boolean') return; + if (!this.types.number(instance)) return; + var result = new ValidatorResult(instance, schema, options, ctx); + var valid = instance > schema.exclusiveMinimum; if (!valid) { result.addError({ - name: 'maximum', - argument: schema.maximum, - message: "must have a maximum value of " + schema.maximum, + name: 'exclusiveMinimum', + argument: schema.exclusiveMinimum, + message: "must be strictly greater than " + schema.exclusiveMinimum, + }); + } + return result; +}; + +/** + * Validates the number form of exclusiveMaximum when the type of the instance value is a number. + * @param instance + * @param schema + * @return {String|null} + */ +validators.exclusiveMaximum = function validateExclusiveMaximum (instance, schema, options, ctx) { + // Support the boolean form of exclusiveMaximum, which is handled by the "maximum" keyword. + if(typeof schema.exclusiveMaximum === 'boolean') return; + if (!this.types.number(instance)) return; + var result = new ValidatorResult(instance, schema, options, ctx); + var valid = instance < schema.exclusiveMaximum; + if (!valid) { + result.addError({ + name: 'exclusiveMaximum', + argument: schema.exclusiveMaximum, + message: "must be strictly less than " + schema.exclusiveMaximum, }); } return result; @@ -444,7 +586,7 @@ var validateMultipleOfOrDivisbleBy = function validateMultipleOfOrDivisbleBy (in result.addError({ name: validationType, argument: validationArgument, - message: errorMessage + JSON.stringify(validationArgument) + message: errorMessage + JSON.stringify(validationArgument), }); } @@ -458,7 +600,7 @@ var validateMultipleOfOrDivisbleBy = function validateMultipleOfOrDivisbleBy (in * @return {String|null} */ validators.multipleOf = function validateMultipleOf (instance, schema, options, ctx) { - return validateMultipleOfOrDivisbleBy.call(this, instance, schema, options, ctx, "multipleOf", "is not a multiple of (divisible by) "); + return validateMultipleOfOrDivisbleBy.call(this, instance, schema, options, ctx, "multipleOf", "is not a multiple of (divisible by) "); }; /** @@ -480,14 +622,14 @@ validators.divisibleBy = function validateDivisibleBy (instance, schema, options validators.required = function validateRequired (instance, schema, options, ctx) { var result = new ValidatorResult(instance, schema, options, ctx); if (instance === undefined && schema.required === true) { - // A boolean form is implemented for reverse-compatability with schemas written against older drafts + // A boolean form is implemented for reverse-compatibility with schemas written against older drafts result.addError({ name: 'required', - message: "is required" + message: "is required", }); } else if (this.types.object(instance) && Array.isArray(schema.required)) { schema.required.forEach(function(n){ - if(instance[n]===undefined){ + if(getEnumerableProperty(instance, n)===undefined){ result.addError({ name: 'required', argument: n, @@ -508,7 +650,15 @@ validators.required = function validateRequired (instance, schema, options, ctx) validators.pattern = function validatePattern (instance, schema, options, ctx) { if (!this.types.string(instance)) return; var result = new ValidatorResult(instance, schema, options, ctx); - if (!instance.match(schema.pattern)) { + var pattern = schema.pattern; + try { + var regexp = new RegExp(pattern, 'u'); + } catch(_e) { + // In the event the stricter handling causes an error, fall back on the forgiving handling + // DEPRECATED + regexp = new RegExp(pattern); + } + if (!instance.match(regexp)) { result.addError({ name: 'pattern', argument: schema.pattern, @@ -633,32 +783,6 @@ validators.maxItems = function validateMaxItems (instance, schema, options, ctx) return result; }; -/** - * Validates that every item in an instance array is unique, when instance is an array - * @param instance - * @param schema - * @param options - * @param ctx - * @return {String|null|ValidatorResult} - */ -validators.uniqueItems = function validateUniqueItems (instance, schema, options, ctx) { - if (!this.types.array(instance)) return; - var result = new ValidatorResult(instance, schema, options, ctx); - function testArrays (v, i, a) { - for (var j = i + 1; j < a.length; j++) if (helpers.deepCompareStrict(v, a[j])) { - return false; - } - return true; - } - if (!instance.every(testArrays)) { - result.addError({ - name: 'uniqueItems', - message: "contains duplicate item", - }); - } - return result; -}; - /** * Deep compares arrays for duplicates * @param v @@ -683,6 +807,7 @@ function testArrays (v, i, a) { * @return {String|null} */ validators.uniqueItems = function validateUniqueItems (instance, schema, options, ctx) { + if (schema.uniqueItems!==true) return; if (!this.types.array(instance)) return; var result = new ValidatorResult(instance, schema, options, ctx); if (!instance.every(testArrays)) { @@ -806,7 +931,8 @@ validators.not = validators.disallow = function validateNot (instance, schema, o if(!Array.isArray(notTypes)) notTypes=[notTypes]; notTypes.forEach(function (type) { if (self.testType(instance, schema, options, ctx, type)) { - var schemaId = type && type.id && ('<' + type.id + '>') || type; + var id = type && (type.$id || type.id); + var schemaId = id || type; result.addError({ name: 'not', argument: schemaId, diff --git a/node_modules/jsonschema/lib/helpers.js b/node_modules/jsonschema/lib/helpers.js index 93ad0e0417..508432f5ee 100644 --- a/node_modules/jsonschema/lib/helpers.js +++ b/node_modules/jsonschema/lib/helpers.js @@ -2,21 +2,23 @@ var uri = require('url'); -var ValidationError = exports.ValidationError = function ValidationError (message, instance, schema, propertyPath, name, argument) { - if (propertyPath) { - this.property = propertyPath; +var ValidationError = exports.ValidationError = function ValidationError (message, instance, schema, path, name, argument) { + if(Array.isArray(path)){ + this.path = path; + this.property = path.reduce(function(sum, item){ + return sum + makeSuffix(item); + }, 'instance'); + }else if(path !== undefined){ + this.property = path; } if (message) { this.message = message; } if (schema) { - if (schema.id) { - this.schema = schema.id; - } else { - this.schema = schema; - } + var id = schema.$id || schema.id; + this.schema = id || schema; } - if (instance) { + if (instance !== undefined) { this.instance = instance; } this.name = name; @@ -31,27 +33,33 @@ ValidationError.prototype.toString = function toString() { var ValidatorResult = exports.ValidatorResult = function ValidatorResult(instance, schema, options, ctx) { this.instance = instance; this.schema = schema; + this.options = options; + this.path = ctx.path; this.propertyPath = ctx.propertyPath; this.errors = []; this.throwError = options && options.throwError; + this.throwFirst = options && options.throwFirst; + this.throwAll = options && options.throwAll; this.disableFormat = options && options.disableFormat === true; }; ValidatorResult.prototype.addError = function addError(detail) { var err; if (typeof detail == 'string') { - err = new ValidationError(detail, this.instance, this.schema, this.propertyPath); + err = new ValidationError(detail, this.instance, this.schema, this.path); } else { if (!detail) throw new Error('Missing error detail'); if (!detail.message) throw new Error('Missing error message'); if (!detail.name) throw new Error('Missing validator type'); - err = new ValidationError(detail.message, this.instance, this.schema, this.propertyPath, detail.name, detail.argument); + err = new ValidationError(detail.message, this.instance, this.schema, this.path, detail.name, detail.argument); } - if (this.throwError) { + this.errors.push(err); + if (this.throwFirst) { + throw new ValidatorResultError(this); + }else if(this.throwError){ throw err; } - this.errors.push(err); return err; }; @@ -74,6 +82,20 @@ Object.defineProperty(ValidatorResult.prototype, "valid", { get: function() { return !this.errors.length; } }); +module.exports.ValidatorResultError = ValidatorResultError; +function ValidatorResultError(result) { + if(Error.captureStackTrace){ + Error.captureStackTrace(this, ValidatorResultError); + } + this.instance = result.instance; + this.schema = result.schema; + this.options = result.options; + this.errors = result.errors; +} +ValidatorResultError.prototype = new Error(); +ValidatorResultError.prototype.constructor = ValidatorResultError; +ValidatorResultError.prototype.name = "Validation Error"; + /** * Describes a problem with a Schema which prevents validation of an instance * @name SchemaError @@ -86,14 +108,22 @@ var SchemaError = exports.SchemaError = function SchemaError (msg, schema) { Error.captureStackTrace(this, SchemaError); }; SchemaError.prototype = Object.create(Error.prototype, - { constructor: {value: SchemaError, enumerable: false} - , name: {value: 'SchemaError', enumerable: false} + { + constructor: {value: SchemaError, enumerable: false}, + name: {value: 'SchemaError', enumerable: false}, }); -var SchemaContext = exports.SchemaContext = function SchemaContext (schema, options, propertyPath, base, schemas) { +var SchemaContext = exports.SchemaContext = function SchemaContext (schema, options, path, base, schemas) { this.schema = schema; this.options = options; - this.propertyPath = propertyPath; + if(Array.isArray(path)){ + this.path = path; + this.propertyPath = path.reduce(function(sum, item){ + return sum + makeSuffix(item); + }, 'instance'); + }else{ + this.propertyPath = path; + } this.base = base; this.schemas = schemas; }; @@ -103,14 +133,15 @@ SchemaContext.prototype.resolve = function resolve (target) { }; SchemaContext.prototype.makeChild = function makeChild(schema, propertyName){ - var propertyPath = (propertyName===undefined) ? this.propertyPath : this.propertyPath+makeSuffix(propertyName); - var base = uri.resolve(this.base, schema.id||''); - var ctx = new SchemaContext(schema, this.options, propertyPath, base, Object.create(this.schemas)); - if(schema.id && !ctx.schemas[base]){ + var path = (propertyName===undefined) ? this.path : this.path.concat([propertyName]); + var id = schema.$id || schema.id; + var base = uri.resolve(this.base, id||''); + var ctx = new SchemaContext(schema, this.options, path, base, Object.create(this.schemas)); + if(id && !ctx.schemas[base]){ ctx.schemas[base] = schema; } return ctx; -} +}; var FORMAT_REGEXPS = exports.FORMAT_REGEXPS = { 'date-time': /^\d{4}-(?:0[0-9]{1}|1[0-2]{1})-(3[01]|0[1-9]|[12][0-9])[tT ](2[0-4]|[01][0-9]):([0-5][0-9]):(60|[0-5][0-9])(\.\d+)?([zZ]|[+-]([0-5][0-9]):(60|[0-5][0-9]))$/, @@ -120,7 +151,11 @@ var FORMAT_REGEXPS = exports.FORMAT_REGEXPS = { 'email': /^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/, 'ip-address': /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/, 'ipv6': /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-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-Fa-f]{1,4}:){5}(((:[0-9A-Fa-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-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-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-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-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-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-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-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-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-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-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*$/, + + // TODO: A more accurate regular expression for "uri" goes: + // [A-Za-z][+\-.0-9A-Za-z]*:((/(/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?)?)?#(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*|(/(/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?[/?]|[!$&-.0-;=?-Z_a-z~])|/?%[0-9A-Fa-f]{2}|[!$&-.0-;=?-Z_a-z~])(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*(#(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*)?|/(/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+(:\d*)?|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?:\d*|\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)?)? 'uri': /^[a-zA-Z][a-zA-Z0-9+-.]*:[^\s]*$/, + 'uri-reference': /^(((([A-Za-z][+\-.0-9A-Za-z]*(:%[0-9A-Fa-f]{2}|:[!$&-.0-;=?-Z_a-z~]|[/?])|\?)(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*|([A-Za-z][+\-.0-9A-Za-z]*:?)?)|([A-Za-z][+\-.0-9A-Za-z]*:)?\/((%[0-9A-Fa-f]{2}|\/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?[/?]|[!$&-.0-;=?-Z_a-z~])(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*|(\/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?)?))#(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*|(([A-Za-z][+\-.0-9A-Za-z]*)?%[0-9A-Fa-f]{2}|[!$&-.0-9;=@_~]|[A-Za-z][+\-.0-9A-Za-z]*[!$&-*,;=@_~])(%[0-9A-Fa-f]{2}|[!$&-.0-9;=@-Z_a-z~])*((([/?](%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*)?#|[/?])(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*)?|([A-Za-z][+\-.0-9A-Za-z]*(:%[0-9A-Fa-f]{2}|:[!$&-.0-;=?-Z_a-z~]|[/?])|\?)(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*|([A-Za-z][+\-.0-9A-Za-z]*:)?\/((%[0-9A-Fa-f]{2}|\/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)(:\d*)?[/?]|[!$&-.0-;=?-Z_a-z~])(%[0-9A-Fa-f]{2}|[!$&-;=?-Z_a-z~])*|\/((%[0-9A-Fa-f]{2}|[!$&-.0-9;=A-Z_a-z~])+(:\d*)?|(\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?:\d*|\[(([Vv][0-9A-Fa-f]+\.[!$&-.0-;=A-Z_a-z~]+)?|[.0-:A-Fa-f]+)\])?)?|[A-Za-z][+\-.0-9A-Za-z]*:?)?$/, 'color': /^(#?([0-9A-Fa-f]{3}){1,2}\b|aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow|(rgb\(\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*,\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*,\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*\))|(rgb\(\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*\)))$/, @@ -143,7 +178,7 @@ var FORMAT_REGEXPS = exports.FORMAT_REGEXPS = { return result; }, 'style': /\s*(.+?):\s*([^;]+);?/, - 'phone': /^\+(?:[0-9] ?){6,14}[0-9]$/ + 'phone': /^\+(?:[0-9] ?){6,14}[0-9]$/, }; FORMAT_REGEXPS.regexp = FORMAT_REGEXPS.regex; @@ -212,10 +247,10 @@ exports.deepCompareStrict = function deepCompareStrict (a, b) { function deepMerger (target, dst, e, i) { if (typeof e === 'object') { - dst[i] = deepMerge(target[i], e) + dst[i] = deepMerge(target[i], e); } else { if (target.indexOf(e) === -1) { - dst.push(e) + dst.push(e); } } } @@ -232,7 +267,7 @@ function copyistWithDeepMerge (target, src, dst, key) { if (!target[key]) { dst[key] = src[key]; } else { - dst[key] = deepMerge(target[key], src[key]) + dst[key] = deepMerge(target[key], src[key]); } } } @@ -253,7 +288,7 @@ function deepMerge (target, src) { } return dst; -}; +} module.exports.deepMerge = deepMerge; @@ -284,9 +319,9 @@ function pathEncoder (v) { * @return {String} */ exports.encodePath = function encodePointer(a){ - // ~ must be encoded explicitly because hacks - // the slash is encoded by encodeURIComponent - return a.map(pathEncoder).join(''); + // ~ must be encoded explicitly because hacks + // the slash is encoded by encodeURIComponent + return a.map(pathEncoder).join(''); }; @@ -323,3 +358,7 @@ exports.getDecimalPlaces = function getDecimalPlaces(number) { return decimalPlaces; }; +exports.isSchema = function isSchema(val){ + return (typeof val === 'object' && val) || (typeof val === 'boolean'); +}; + diff --git a/node_modules/jsonschema/lib/index.d.ts b/node_modules/jsonschema/lib/index.d.ts index 056d2cbec8..5acb33b7af 100644 --- a/node_modules/jsonschema/lib/index.d.ts +++ b/node_modules/jsonschema/lib/index.d.ts @@ -29,6 +29,7 @@ export declare class ValidatorResult { export declare class ValidationError { constructor(message?: string, instance?: any, schema?: Schema, propertyPath?: any, name?: string, argument?: any); + path: (string|number)[]; property: string; message: string; schema: string|Schema; @@ -48,6 +49,7 @@ export declare class SchemaError extends Error{ export declare function validate(instance: any, schema: any, options?: Options): ValidatorResult export interface Schema { + $id?: string id?: string $schema?: string $ref?: string @@ -55,9 +57,9 @@ export interface Schema { description?: string multipleOf?: number maximum?: number - exclusiveMaximum?: boolean + exclusiveMaximum?: number | boolean minimum?: number - exclusiveMinimum?: boolean + exclusiveMinimum?: number | boolean maxLength?: number minLength?: number pattern?: string | RegExp @@ -82,6 +84,7 @@ export interface Schema { dependencies?: { [name: string]: Schema | string[] } + const?: any 'enum'?: any[] type?: string | string[] format?: string @@ -89,21 +92,31 @@ export interface Schema { anyOf?: Schema[] oneOf?: Schema[] not?: Schema + if?: Schema + then?: Schema + else?: Schema } export interface Options { skipAttributes?: string[]; allowUnknownAttributes?: boolean; + preValidateProperty?: PreValidatePropertyFunction; rewrite?: RewriteFunction; - propertyName?: string; base?: string; throwError?: boolean; + throwFirst?: boolean; + throwAll?: boolean; + nestedErrors?: boolean; } export interface RewriteFunction { (instance: any, schema: Schema, options: Options, ctx: SchemaContext): any; } +export interface PreValidatePropertyFunction { + (instance: any, key: string, schema: Schema, options: Options, ctx: SchemaContext): any; +} + export interface SchemaContext { schema: Schema; options: Options; diff --git a/node_modules/jsonschema/lib/index.js b/node_modules/jsonschema/lib/index.js index c68bb2c492..98c57453ca 100644 --- a/node_modules/jsonschema/lib/index.js +++ b/node_modules/jsonschema/lib/index.js @@ -3,6 +3,7 @@ var Validator = module.exports.Validator = require('./validator'); module.exports.ValidatorResult = require('./helpers').ValidatorResult; +module.exports.ValidatorResultError = require('./helpers').ValidatorResultError; module.exports.ValidationError = require('./helpers').ValidationError; module.exports.SchemaError = require('./helpers').SchemaError; module.exports.SchemaScanResult = require('./scan').SchemaScanResult; diff --git a/node_modules/jsonschema/lib/scan.js b/node_modules/jsonschema/lib/scan.js index d05307d041..26f6b336dc 100644 --- a/node_modules/jsonschema/lib/scan.js +++ b/node_modules/jsonschema/lib/scan.js @@ -1,3 +1,4 @@ +"use strict"; var urilib = require('url'); var helpers = require('./helpers'); @@ -23,13 +24,14 @@ module.exports.scan = function scan(base, schema){ ref[resolvedUri] = ref[resolvedUri] ? ref[resolvedUri]+1 : 0; return; } - var ourBase = schema.id ? urilib.resolve(baseuri, schema.id) : baseuri; + var id = schema.$id || schema.id; + var ourBase = id ? urilib.resolve(baseuri, id) : baseuri; if (ourBase) { // If there's no fragment, append an empty one if(ourBase.indexOf('#')<0) ourBase += '#'; if(found[ourBase]){ if(!helpers.deepCompareStrict(found[ourBase], schema)){ - throw new Error('Schema <'+schema+'> already exists with different definition'); + throw new Error('Schema <'+ourBase+'> already exists with different definition'); } return found[ourBase]; } @@ -68,7 +70,6 @@ module.exports.scan = function scan(base, schema){ var found = {}; var ref = {}; - var schemaUri = base; scanSchema(base, schema); return new SchemaScanResult(found, ref); -} +}; diff --git a/node_modules/jsonschema/lib/validator.js b/node_modules/jsonschema/lib/validator.js index 43dd43ae0c..cce16946ef 100644 --- a/node_modules/jsonschema/lib/validator.js +++ b/node_modules/jsonschema/lib/validator.js @@ -6,6 +6,7 @@ var attribute = require('./attribute'); var helpers = require('./helpers'); var scanSchema = require('./scan').scan; var ValidatorResult = helpers.ValidatorResult; +var ValidatorResultError = helpers.ValidatorResultError; var SchemaError = helpers.SchemaError; var SchemaContext = helpers.SchemaContext; //var anonymousBase = 'vnd.jsonschema:///'; @@ -49,13 +50,15 @@ Validator.prototype.addSchema = function addSchema (schema, base) { return null; } var scan = scanSchema(base||anonymousBase, schema); - var ourUri = base || schema.id; + var ourUri = base || schema.$id || schema.id; for(var uri in scan.id){ this.schemas[uri] = scan.id[uri]; } for(var uri in scan.ref){ + // If this schema is already defined, it will be filtered out by the next step this.unresolvedRefs.push(uri); } + // Remove newly defined schemas from unresolvedRefs this.unresolvedRefs = this.unresolvedRefs.filter(function(uri){ return typeof self.schemas[uri]==='undefined'; }); @@ -103,14 +106,18 @@ Validator.prototype.getSchema = function getSchema (urn) { * @return {Array} */ Validator.prototype.validate = function validate (instance, schema, options, ctx) { + if((typeof schema !== 'boolean' && typeof schema !== 'object') || schema === null){ + throw new SchemaError('Expected `schema` to be an object or boolean'); + } if (!options) { options = {}; } - var propertyName = options.propertyName || 'instance'; + // This section indexes subschemas in the provided schema, so they don't need to be added with Validator#addSchema // This will work so long as the function at uri.resolve() will resolve a relative URI to a relative URI - var base = urilib.resolve(options.base||anonymousBase, schema.id||''); + var id = schema.$id || schema.id; + var base = urilib.resolve(options.base||anonymousBase, id||''); if(!ctx){ - ctx = new SchemaContext(schema, options, propertyName, base, Object.create(this.schemas)); + ctx = new SchemaContext(schema, options, [], base, Object.create(this.schemas)); if (!ctx.schemas[base]) { ctx.schemas[base] = schema; } @@ -120,14 +127,18 @@ Validator.prototype.validate = function validate (instance, schema, options, ctx ctx.schemas[n] = sch; } } - if (schema) { - var result = this.validateSchema(instance, schema, options, ctx); - if (!result) { - throw new Error('Result undefined'); - } + if(options.required && instance===undefined){ + var result = new ValidatorResult(instance, schema, options, ctx); + result.addError('is required, but is undefined'); return result; } - throw new SchemaError('no schema specified', schema); + var result = this.validateSchema(instance, schema, options, ctx); + if (!result) { + throw new Error('Result undefined'); + }else if(options.throwAll && result.errors.length){ + throw new ValidatorResultError(result); + } + return result; }; /** @@ -152,7 +163,7 @@ function shouldResolve(schema) { Validator.prototype.validateSchema = function validateSchema (instance, schema, options, ctx) { var result = new ValidatorResult(instance, schema, options, ctx); - // Support for the true/false schemas + // Support for the true/false schemas if(typeof schema==='boolean') { if(schema===true){ // `true` is always valid @@ -180,10 +191,10 @@ Validator.prototype.validateSchema = function validateSchema (instance, schema, } // If passed a string argument, load that schema URI - var switchSchema; - if (switchSchema = shouldResolve(schema)) { + var switchSchema = shouldResolve(schema); + if (switchSchema) { var resolved = this.resolve(schema, switchSchema, ctx); - var subctx = new SchemaContext(resolved.subschema, options, ctx.propertyPath, resolved.switchSchema, ctx.schemas); + var subctx = new SchemaContext(resolved.subschema, options, ctx.path, resolved.switchSchema, ctx.schemas); return this.validateSchema(instance, resolved.subschema, options, subctx); } @@ -220,7 +231,7 @@ Validator.prototype.validateSchema = function validateSchema (instance, schema, */ Validator.prototype.schemaTraverser = function schemaTraverser (schemaobj, s) { schemaobj.schema = helpers.deepMerge(schemaobj.schema, this.superResolve(s, schemaobj.ctx)); -} +}; /** * @private @@ -229,12 +240,12 @@ Validator.prototype.schemaTraverser = function schemaTraverser (schemaobj, s) { * @returns Object schema or resolved schema */ Validator.prototype.superResolve = function superResolve (schema, ctx) { - var ref; - if(ref = shouldResolve(schema)) { + var ref = shouldResolve(schema); + if(ref) { return this.resolve(schema, ref, ctx).subschema; } return schema; -} +}; /** * @private @@ -275,6 +286,11 @@ Validator.prototype.resolve = function resolve (schema, switchSchema, ctx) { * @return {boolean} */ Validator.prototype.testType = function validateType (instance, schema, options, ctx, type) { + if(type===undefined){ + return; + }else if(type===null){ + throw new SchemaError('Unexpected null in "type" keyword'); + } if (typeof this.types[type] == 'function') { return this.types[type].call(this, instance); } diff --git a/node_modules/jsonschema/package.json b/node_modules/jsonschema/package.json index 8d384e2d45..16f1792b8a 100644 --- a/node_modules/jsonschema/package.json +++ b/node_modules/jsonschema/package.json @@ -1,7 +1,7 @@ { "author": "Tom de Grunt ", "name": "jsonschema", - "version": "1.2.6", + "version": "1.4.0", "license": "MIT", "dependencies": {}, "contributors": [ @@ -9,12 +9,15 @@ "name": "Austin Wright" } ], - "main": "./lib", + "main": "./lib/index.js", "typings": "./lib/index.d.ts", "devDependencies": { + "@stryker-mutator/core": "^4.0.0", + "@stryker-mutator/mocha-runner": "^4.0.0", + "chai": "~4.2.0", + "eslint": "^7.7.0", "json-metaschema": "^1.2.0", - "mocha": "~3", - "chai": "~1.5.0" + "mocha": "~8.1.1" }, "optionalDependencies": {}, "engines": { @@ -33,6 +36,7 @@ }, "description": "A fast and easy to use JSON Schema validator", "scripts": { + "stryker": "stryker run", "test": "./node_modules/.bin/mocha -R spec" } } \ No newline at end of file