From d20da898199ea982c4defbd51a4d1e88e9d216bb Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Thu, 26 Oct 2017 18:10:10 +0100 Subject: [PATCH 01/48] feat: formats time and date-time allow leap second, closes #594 --- lib/compile/formats.js | 8 +++++--- spec/tests/rules/format.json | 27 ++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/lib/compile/formats.js b/lib/compile/formats.js index b3a1541a6..96dafa509 100644 --- a/lib/compile/formats.js +++ b/lib/compile/formats.js @@ -32,8 +32,8 @@ formats.fast = { // date: http://tools.ietf.org/html/rfc3339#section-5.6 date: /^\d\d\d\d-[0-1]\d-[0-3]\d$/, // date-time: http://tools.ietf.org/html/rfc3339#section-5.6 - time: /^[0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:z|[+-]\d\d:\d\d)?$/i, - 'date-time': /^\d\d\d\d-[0-1]\d-[0-3]\d[t\s][0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:z|[+-]\d\d:\d\d)$/i, + time: /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d:\d\d)?$/i, + 'date-time': /^\d\d\d\d-[0-1]\d-[0-3]\d[t\s](?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d:\d\d)$/i, // uri: https://github.com/mafintosh/is-my-json-valid/blob/master/formats.js uri: /^(?:[a-z][a-z0-9+-.]*)(?::|\/)\/?[^\s]*$/i, 'uri-reference': /^(?:(?:[a-z][a-z0-9+-.]*:)?\/\/)?[^\s]*$/i, @@ -97,7 +97,9 @@ function time(str, full) { var minute = matches[2]; var second = matches[3]; var timeZone = matches[5]; - return hour <= 23 && minute <= 59 && second <= 59 && (!full || timeZone); + return ((hour <= 23 && minute <= 59 && second <= 59) || + (hour == 23 && minute == 59 && second == 60)) && + (!full || timeZone); } diff --git a/spec/tests/rules/format.json b/spec/tests/rules/format.json index 4eb9c9ded..dc385b575 100644 --- a/spec/tests/rules/format.json +++ b/spec/tests/rules/format.json @@ -511,9 +511,19 @@ "valid": true }, { - "description": "not valid time", + "description": "an invalid time format", "data": "12.34.56", "valid": false + }, + { + "description": "an invalid time", + "data": "12:34:67", + "valid": false + }, + { + "description": "a valid time (leap second)", + "data": "23:59:60", + "valid": true } ] }, @@ -535,6 +545,21 @@ "description": "an invalid date-time string (additional part)", "data": "1963-06-19T12:13:14ZTinvalid", "valid": false + }, + { + "description": "an invalid date-time string (invalid date)", + "data": "1963-20-19T12:13:14Z", + "valid": false + }, + { + "description": "an invalid date-time string (invalid time)", + "data": "1963-06-19T12:13:67Z", + "valid": false + }, + { + "description": "a valid date-time string (leap second)", + "data": "2016-12-31T23:59:60Z", + "valid": true } ] }, From e6aa9e08555fdfee000c2ced9c44b5d2d99bb595 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Thu, 26 Oct 2017 18:35:09 +0100 Subject: [PATCH 02/48] feat: format json-pointer only means string now, added format json-pointer-uri-fragment, closes #589 --- lib/compile/formats.js | 5 ++++- spec/tests/rules/format.json | 26 ++++++++++++++++---------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/compile/formats.js b/lib/compile/formats.js index 96dafa509..a2fcebae5 100644 --- a/lib/compile/formats.js +++ b/lib/compile/formats.js @@ -16,7 +16,8 @@ var URITEMPLATE = /^(?:(?:[^\x00-\x20"'<>%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@| // var URL = /^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!10(?:\.\d{1,3}){3})(?!127(?:\.\d{1,3}){3})(?!169\.254(?:\.\d{1,3}){2})(?!192\.168(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u{00a1}-\u{ffff}0-9]+-?)*[a-z\u{00a1}-\u{ffff}0-9]+)(?:\.(?:[a-z\u{00a1}-\u{ffff}0-9]+-?)*[a-z\u{00a1}-\u{ffff}0-9]+)*(?:\.(?:[a-z\u{00a1}-\u{ffff}]{2,})))(?::\d{2,5})?(?:\/[^\s]*)?$/iu; var URL = /^(?:(?:http[s\u017F]?|ftp):\/\/)(?:(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+(?::(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?@)?(?:(?!10(?:\.[0-9]{1,3}){3})(?!127(?:\.[0-9]{1,3}){3})(?!169\.254(?:\.[0-9]{1,3}){2})(?!192\.168(?:\.[0-9]{1,3}){2})(?!172\.(?:1[6-9]|2[0-9]|3[01])(?:\.[0-9]{1,3}){2})(?:[1-9][0-9]?|1[0-9][0-9]|2[01][0-9]|22[0-3])(?:\.(?:1?[0-9]{1,2}|2[0-4][0-9]|25[0-5])){2}(?:\.(?:[1-9][0-9]?|1[0-9][0-9]|2[0-4][0-9]|25[0-4]))|(?:(?:(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-?)*(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)(?:\.(?:(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-?)*(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)*(?:\.(?:(?:[KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){2,})))(?::[0-9]{2,5})?(?:\/(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?$/i; var UUID = /^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i; -var JSON_POINTER = /^(?:\/(?:[^~/]|~0|~1)*)*$|^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i; +var JSON_POINTER = /^(?:\/(?:[^~/]|~0|~1)*)*$/; +var JSON_POINTER_URI_FRAGMENT = /^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i; var RELATIVE_JSON_POINTER = /^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/; @@ -54,6 +55,7 @@ formats.fast = { // JSON-pointer: https://tools.ietf.org/html/rfc6901 // uri fragment: https://tools.ietf.org/html/rfc3986#appendix-A 'json-pointer': JSON_POINTER, + 'json-pointer-uri-fragment': JSON_POINTER_URI_FRAGMENT, // relative JSON-pointer: http://tools.ietf.org/html/draft-luff-relative-json-pointer-00 'relative-json-pointer': RELATIVE_JSON_POINTER }; @@ -74,6 +76,7 @@ formats.full = { regex: regex, uuid: UUID, 'json-pointer': JSON_POINTER, + 'json-pointer-uri-fragment': JSON_POINTER_URI_FRAGMENT, 'relative-json-pointer': RELATIVE_JSON_POINTER }; diff --git a/spec/tests/rules/format.json b/spec/tests/rules/format.json index dc385b575..226608bd4 100644 --- a/spec/tests/rules/format.json +++ b/spec/tests/rules/format.json @@ -593,11 +593,6 @@ "data": "/foo/bar~0/baz~1/%a", "valid": true }, - { - "description": "a valid JSON-pointer as uri fragment", - "data": "#/foo/%25a", - "valid": true - }, { "description": "empty string is valid", "data": "", @@ -613,11 +608,6 @@ "data": "/foo/bar~", "valid": false }, - { - "description": "not a valid JSON-pointer as uri fragment (% not URL-encoded)", - "data": "#/foo/%a", - "valid": false - }, { "description": "valid JSON-pointer with empty segment", "data": "/foo//bar", @@ -627,6 +617,22 @@ "description": "valid JSON-pointer with the last empty segment", "data": "/foo/bar/", "valid": true + } + ] + }, + { + "description": "validation of JSON-pointer URI fragment strings", + "schema": {"format": "json-pointer-uri-fragment"}, + "tests": [ + { + "description": "a valid JSON-pointer as uri fragment", + "data": "#/foo/%25a", + "valid": true + }, + { + "description": "not a valid JSON-pointer as uri fragment (% not URL-encoded)", + "data": "#/foo/%a", + "valid": false }, { "description": "valid JSON-pointer with empty segment as uri fragment", From 05f722605f4ea5d11716f807195aa5130e745835 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 28 Oct 2017 15:13:20 +0100 Subject: [PATCH 03/48] refactor: move the list of rules to the folder with rules --- lib/compile/_rules.js | 31 ------------------------------- lib/compile/rules.js | 2 +- lib/dotjs/index.js | 31 +++++++++++++++++++++++++++++++ package.json | 2 +- 4 files changed, 33 insertions(+), 33 deletions(-) delete mode 100644 lib/compile/_rules.js create mode 100644 lib/dotjs/index.js diff --git a/lib/compile/_rules.js b/lib/compile/_rules.js deleted file mode 100644 index 3fe69973a..000000000 --- a/lib/compile/_rules.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict'; - -//all requires must be explicit because browserify won't work with dynamic requires -module.exports = { - '$ref': require('../dotjs/ref'), - allOf: require('../dotjs/allOf'), - anyOf: require('../dotjs/anyOf'), - const: require('../dotjs/const'), - contains: require('../dotjs/contains'), - dependencies: require('../dotjs/dependencies'), - 'enum': require('../dotjs/enum'), - format: require('../dotjs/format'), - items: require('../dotjs/items'), - maximum: require('../dotjs/_limit'), - minimum: require('../dotjs/_limit'), - maxItems: require('../dotjs/_limitItems'), - minItems: require('../dotjs/_limitItems'), - maxLength: require('../dotjs/_limitLength'), - minLength: require('../dotjs/_limitLength'), - maxProperties: require('../dotjs/_limitProperties'), - minProperties: require('../dotjs/_limitProperties'), - multipleOf: require('../dotjs/multipleOf'), - not: require('../dotjs/not'), - oneOf: require('../dotjs/oneOf'), - pattern: require('../dotjs/pattern'), - properties: require('../dotjs/properties'), - propertyNames: require('../dotjs/propertyNames'), - required: require('../dotjs/required'), - uniqueItems: require('../dotjs/uniqueItems'), - validate: require('../dotjs/validate') -}; diff --git a/lib/compile/rules.js b/lib/compile/rules.js index eaeab77fa..bbcce6228 100644 --- a/lib/compile/rules.js +++ b/lib/compile/rules.js @@ -1,6 +1,6 @@ 'use strict'; -var ruleModules = require('./_rules') +var ruleModules = require('../dotjs') , toHash = require('./util').toHash; module.exports = function rules() { diff --git a/lib/dotjs/index.js b/lib/dotjs/index.js new file mode 100644 index 000000000..17b7d7419 --- /dev/null +++ b/lib/dotjs/index.js @@ -0,0 +1,31 @@ +'use strict'; + +//all requires must be explicit because browserify won't work with dynamic requires +module.exports = { + '$ref': require('./ref'), + allOf: require('./allOf'), + anyOf: require('./anyOf'), + const: require('./const'), + contains: require('./contains'), + dependencies: require('./dependencies'), + 'enum': require('./enum'), + format: require('./format'), + items: require('./items'), + maximum: require('./_limit'), + minimum: require('./_limit'), + maxItems: require('./_limitItems'), + minItems: require('./_limitItems'), + maxLength: require('./_limitLength'), + minLength: require('./_limitLength'), + maxProperties: require('./_limitProperties'), + minProperties: require('./_limitProperties'), + multipleOf: require('./multipleOf'), + not: require('./not'), + oneOf: require('./oneOf'), + pattern: require('./pattern'), + properties: require('./properties'), + propertyNames: require('./propertyNames'), + required: require('./required'), + uniqueItems: require('./uniqueItems'), + validate: require('./validate') +}; diff --git a/package.json b/package.json index aadd462db..c5b734767 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "bundle-nodent": "node ./scripts/bundle.js nodent", "bundle-all": "del-cli dist && npm run bundle && npm run bundle-regenerator && npm run bundle-nodent", "bundle-beautify": "node ./scripts/bundle.js js-beautify", - "build": "del-cli lib/dotjs/*.js && node scripts/compile-dots.js", + "build": "del-cli lib/dotjs/*.js '!lib/dotjs/index.js' && node scripts/compile-dots.js", "test-karma": "karma start --single-run --browsers PhantomJS", "test-browser": "del-cli .browser && npm run bundle-all && scripts/prepare-tests && npm run test-karma", "test": "npm run jshint && npm run eslint && npm run test-ts && npm run build && npm run test-cov && if-node-version 4 npm run test-browser", From 9845928e7a658e0c1f2bebe069c09bb7f8a1023d Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 28 Oct 2017 16:51:18 +0100 Subject: [PATCH 04/48] feat: reserve keyword $comment, closes #587 --- lib/compile/rules.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/compile/rules.js b/lib/compile/rules.js index 6460f36b6..bea8b57d3 100644 --- a/lib/compile/rules.js +++ b/lib/compile/rules.js @@ -20,8 +20,8 @@ module.exports = function rules() { var ALL = [ 'type' ]; var KEYWORDS = [ - 'additionalItems', '$schema', '$id', 'id', 'title', - 'description', 'default', 'definitions' + 'additionalItems', '$schema', '$id', 'id', '$comment', + 'title', 'description', 'default', 'definitions' ]; var TYPES = [ 'number', 'integer', 'string', 'array', 'object', 'boolean', 'null' ]; RULES.all = toHash(ALL); From b4561767ff7cea8dc38bb53fba83af79bbd96015 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 28 Oct 2017 20:35:02 +0100 Subject: [PATCH 05/48] feat: if/then/else keyword, closes #586 --- lib/compile/rules.js | 7 +- lib/dot/errors.def | 3 + lib/dot/if.jst | 76 ++++++++++++++++++++++ lib/dotjs/index.js | 1 + spec/errors.spec.js | 72 +++++++++++++++++++++ spec/tests/rules/if.json | 134 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 290 insertions(+), 3 deletions(-) create mode 100644 lib/dot/if.jst create mode 100644 spec/tests/rules/if.json diff --git a/lib/compile/rules.js b/lib/compile/rules.js index bea8b57d3..63b5a0899 100644 --- a/lib/compile/rules.js +++ b/lib/compile/rules.js @@ -15,13 +15,14 @@ module.exports = function rules() { { type: 'object', rules: [ 'maxProperties', 'minProperties', 'required', 'dependencies', 'propertyNames', { 'properties': ['additionalProperties', 'patternProperties'] } ] }, - { rules: [ '$ref', 'const', 'enum', 'not', 'anyOf', 'oneOf', 'allOf' ] } + { rules: [ '$ref', 'const', 'enum', 'not', 'anyOf', 'oneOf', 'allOf', 'if' ] } ]; var ALL = [ 'type' ]; var KEYWORDS = [ - 'additionalItems', '$schema', '$id', 'id', '$comment', - 'title', 'description', 'default', 'definitions' + '$schema', '$id', 'id', '$comment', + 'title', 'description', 'default', 'definitions', + 'additionalItems', 'then', 'else' ]; var TYPES = [ 'number', 'integer', 'string', 'array', 'object', 'boolean', 'null' ]; RULES.all = toHash(ALL); diff --git a/lib/dot/errors.def b/lib/dot/errors.def index b79646fc2..16e1191c5 100644 --- a/lib/dot/errors.def +++ b/lib/dot/errors.def @@ -101,6 +101,7 @@ dependencies: "'should have {{? $deps.length == 1 }}property {{= it.util.escapeQuotes($deps[0]) }}{{??}}properties {{= it.util.escapeQuotes($deps.join(\", \")) }}{{?}} when property {{= it.util.escapeQuotes($property) }} is present'", 'enum': "'should be equal to one of the allowed values'", format: "'should match format \"{{#def.concatSchemaEQ}}\"'", + 'if': "'should match \"' + {{=$ifClause}} + '\" schema'", _limit: "'should be {{=$opStr}} {{#def.appendSchema}}", _exclusiveLimit: "'{{=$exclusiveKeyword}} should be boolean'", _limitItems: "'should NOT have {{?$keyword=='maxItems'}}more{{??}}less{{?}} than {{#def.concatSchema}} items'", @@ -137,6 +138,7 @@ dependencies: "validate.schema{{=$schemaPath}}", 'enum': "validate.schema{{=$schemaPath}}", format: "{{#def.schemaRefOrQS}}", + 'if': "validate.schema{{=$schemaPath}}", _limit: "{{#def.schemaRefOrVal}}", _exclusiveLimit: "validate.schema{{=$schemaPath}}", _limitItems: "{{#def.schemaRefOrVal}}", @@ -172,6 +174,7 @@ dependencies: "{ property: '{{= it.util.escapeQuotes($property) }}', missingProperty: '{{=$missingProperty}}', depsCount: {{=$deps.length}}, deps: '{{= it.util.escapeQuotes($deps.length==1 ? $deps[0] : $deps.join(\", \")) }}' }", 'enum': "{ allowedValues: schema{{=$lvl}} }", format: "{ format: {{#def.schemaValueQS}} }", + 'if': "{ failingKeyword: {{=$ifClause}} }", _limit: "{ comparison: {{=$opExpr}}, limit: {{=$schemaValue}}, exclusive: {{=$exclusive}} }", _exclusiveLimit: "{}", _limitItems: "{ limit: {{=$schemaValue}} }", diff --git a/lib/dot/if.jst b/lib/dot/if.jst new file mode 100644 index 000000000..ba20b220f --- /dev/null +++ b/lib/dot/if.jst @@ -0,0 +1,76 @@ +{{# def.definitions }} +{{# def.errors }} +{{# def.setupKeyword }} +{{# def.setupNextLevel }} + + +{{## def.validateIfClause:_clause: + {{ + $it.schema = it.schema['_clause']; + $it.schemaPath = it.schemaPath + '._clause'; + $it.errSchemaPath = it.errSchemaPath + '/_clause'; + }} + {{# def.insertSubschemaCode }} + {{=$valid}} = {{=$nextValid}}; + {{? $thenPresent && $elsePresent }} + {{ $ifClause = 'ifClause' + $lvl; }} + var {{=$ifClause}} = '_clause'; + {{??}} + {{ $ifClause = '\'_clause\''; }} + {{?}} +#}} + +{{ + var $thenSch = it.schema['then'] + , $elseSch = it.schema['else'] + , $thenPresent = $thenSch !== undefined && {{# def.nonEmptySchema:$thenSch }} + , $elsePresent = $elseSch !== undefined && {{# def.nonEmptySchema:$elseSch }} + , $currentBaseId = $it.baseId; +}} + +{{? $thenPresent || $elsePresent }} + {{ + var $ifClause; + $it.createErrors = false; + $it.schema = $schema; + $it.schemaPath = $schemaPath; + $it.errSchemaPath = $errSchemaPath; + }} + var {{=$errs}} = errors; + var {{=$valid}} = true; + + {{# def.setCompositeRule }} + {{# def.insertSubschemaCode }} + {{ $it.createErrors = true; }} + {{# def.resetErrors }} + + {{? $thenPresent }} + if ({{=$nextValid}}) { + {{# def.validateIfClause:then }} + } + {{? $elsePresent }} + else { + {{?}} + {{??}} + if (!{{=$nextValid}}) { + {{?}} + + {{? $elsePresent }} + {{# def.validateIfClause:else }} + } + {{?}} + + {{# def.resetCompositeRule }} + + if (!{{=$valid}}) { + {{# def.extraError:'if' }} + } + {{? $breakOnError }} else { {{?}} + + {{# def.cleanUp }} +{{??}} + {{? $breakOnError }} + if (true) { + {{?}} +{{?}} + diff --git a/lib/dotjs/index.js b/lib/dotjs/index.js index 17b7d7419..b5531ed8f 100644 --- a/lib/dotjs/index.js +++ b/lib/dotjs/index.js @@ -10,6 +10,7 @@ module.exports = { dependencies: require('./dependencies'), 'enum': require('./enum'), format: require('./format'), + 'if': require('./if'), items: require('./items'), maximum: require('./_limit'), minimum: require('./_limit'), diff --git a/spec/errors.spec.js b/spec/errors.spec.js index c852b5566..2663d1e8d 100644 --- a/spec/errors.spec.js +++ b/spec/errors.spec.js @@ -674,6 +674,78 @@ describe('Validation errors', function () { }); + describe('if/then/else errors', function() { + it('if/then/else should include failing keyword in message and params', function() { + var schema = { + 'if': { maximum: 10 }, + 'then': { multipleOf: 2 }, + 'else': { multipleOf: 5 } + }; + + [ajv, fullAjv].forEach(function (_ajv) { + var validate = _ajv.compile(schema); + shouldBeValid(validate, 8); + shouldBeValid(validate, 15); + + shouldBeInvalid(validate, 7, 2); + testError('should match "then" schema', {failingKeyword: 'then'}); + + shouldBeInvalid(validate, 17, 2); + testError('should match "else" schema', {failingKeyword: 'else'}); + + function testError(message, params) { + var err = validate.errors[1]; + shouldBeError(err, 'if', '#/if', '', message, params); + } + }); + }); + + it('if/then should include failing keyword in message and params', function() { + var schema = { + 'if': { maximum: 10 }, + 'then': { multipleOf: 2 } + }; + + [ajv, fullAjv].forEach(function (_ajv) { + var validate = _ajv.compile(schema); + shouldBeValid(validate, 8); + shouldBeValid(validate, 11); + shouldBeValid(validate, 12); + + shouldBeInvalid(validate, 7, 2); + testError('should match "then" schema', {failingKeyword: 'then'}); + + function testError(message, params) { + var err = validate.errors[1]; + shouldBeError(err, 'if', '#/if', '', message, params); + } + }); + }); + + it('if/else should include failing keyword in message and params', function() { + var schema = { + 'if': { maximum: 10 }, + 'else': { multipleOf: 5 } + }; + + [ajv, fullAjv].forEach(function (_ajv) { + var validate = _ajv.compile(schema); + shouldBeValid(validate, 7); + shouldBeValid(validate, 8); + shouldBeValid(validate, 15); + + shouldBeInvalid(validate, 17, 2); + testError('should match "else" schema', {failingKeyword: 'else'}); + + function testError(message, params) { + var err = validate.errors[1]; + shouldBeError(err, 'if', '#/if', '', message, params); + } + }); + }); + }); + + function testSchema1(schema, schemaPathPrefix) { _testSchema1(ajv, schema, schemaPathPrefix); _testSchema1(ajvJP, schema, schemaPathPrefix); diff --git a/spec/tests/rules/if.json b/spec/tests/rules/if.json new file mode 100644 index 000000000..a5bd307b2 --- /dev/null +++ b/spec/tests/rules/if.json @@ -0,0 +1,134 @@ +[ + { + "description": "if/then keyword validation", + "schema": { + "if": { "minimum": 10 }, + "then": { "multipleOf": 2 } + }, + "tests": [ + { + "description": ">= 10 and even is valid", + "data": 12, + "valid": true + }, + { + "description": ">= 10 and odd is invalid", + "data": 11, + "valid": false + }, + { + "description": "< 10 is valid", + "data": 9, + "valid": true + } + ] + }, + { + "description": "if/then/else keyword validation", + "schema": { + "if": { "maximum": 10 }, + "then": { "multipleOf": 2 }, + "else": { "multipleOf": 5 } + }, + "tests": [ + { + "description": "<=10 and even is valid", + "data": 8, + "valid": true + }, + { + "description": "<=10 and odd is invalid", + "data": 7, + "valid": false + }, + { + "description": ">10 and mulitple of 5 is valid", + "data": 15, + "valid": true + }, + { + "description": ">10 and not mulitple of 5 is invalid", + "data": 17, + "valid": false + } + ] + }, + { + "description": "if keyword with id in sibling subschema", + "schema": { + "id": "http://example.com/base_if", + "if": { + "id": "http://example.com/if", + "minimum": 10 + }, + "then": { "$ref": "#/definitions/def" }, + "definitions": { + "def": { "multipleOf": 2 } + } + }, + "tests": [ + { + "description": ">= 10 and even is valid", + "data": 12, + "valid": true + }, + { + "description": ">= 10 and odd is invalid", + "data": 11, + "valid": false + }, + { + "description": "< 10 is valid", + "data": 9, + "valid": true + } + ] + }, + { + "description": "then/else without if should be ignored", + "schema": { + "then": { "multipleOf": 2 }, + "else": { "multipleOf": 5 } + }, + "tests": [ + { + "description": "even is valid", + "data": 8, + "valid": true + }, + { + "description": "odd is valid", + "data": 7, + "valid": true + }, + { + "description": "mulitple of 5 is valid", + "data": 15, + "valid": true + }, + { + "description": "not mulitple of 5 is valid", + "data": 17, + "valid": true + } + ] + }, + { + "description": "if without then/else should be ignored", + "schema": { + "if": { "maximum": 10 } + }, + "tests": [ + { + "description": "<=10 is valid", + "data": 8, + "valid": true + }, + { + "description": ">10 is valid", + "data": 15, + "valid": true + } + ] + } +] From 00a42aba2b07d4fd7a8cade80cd9981023b95102 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 28 Oct 2017 21:24:01 +0100 Subject: [PATCH 06/48] docs: plugin convention, closes #524 --- README.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 9528a874d..0879146b5 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,7 @@ ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); - [Methods](#api) - [Options](#options) - [Validation errors](#validation-errors) +- [Plugins](#plugins) - [Related packages](#related-packages) - [Packages using Ajv](#some-packages-using-ajv) - [Tests, Contributing, History, License](#tests) @@ -1230,15 +1231,26 @@ Properties of `params` object in errors depend on the keyword that failed valida - custom keywords (in case keyword definition doesn't create errors) - property `keyword` (the keyword name). +## Plugins + +Ajv can be extended with plugins that add custom keywords, formats or functions to process generated code. When such plugin is published as npm package it is recommended that it follows these conventions: + +- it exports a function +- this function accepts ajv instance as the first parameter and returns the same instance to allow chaining +- this function can accept an optional configuration as the second parameter + +If you have published a useful plugin please submit a PR to add it to the next section. + + ## Related packages -- [ajv-async](https://github.com/epoberezkin/ajv-async) - configure async validation mode +- [ajv-async](https://github.com/epoberezkin/ajv-async) - plugin to configure async validation mode - [ajv-cli](https://github.com/jessedc/ajv-cli) - command line interface -- [ajv-errors](https://github.com/epoberezkin/ajv-errors) - custom error messages +- [ajv-errors](https://github.com/epoberezkin/ajv-errors) - plugin for custom error messages - [ajv-i18n](https://github.com/epoberezkin/ajv-i18n) - internationalised error messages -- [ajv-istanbul](https://github.com/epoberezkin/ajv-istanbul) - instrument generated validation code to measure test coverage of your schemas -- [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) - custom validation keywords (if/then/else, select, typeof, etc.) -- [ajv-merge-patch](https://github.com/epoberezkin/ajv-merge-patch) - keywords $merge and $patch +- [ajv-istanbul](https://github.com/epoberezkin/ajv-istanbul) - plugin to instrument generated validation code to measure test coverage of your schemas +- [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) - plugin with custom validation keywords (if/then/else, select, typeof, etc.) +- [ajv-merge-patch](https://github.com/epoberezkin/ajv-merge-patch) - plugin with keywords $merge and $patch - [ajv-pack](https://github.com/epoberezkin/ajv-pack) - produces a compact module exporting validation functions From 5e17f9b4eed702e016d8b810e19a40852104b342 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 29 Oct 2017 18:20:24 +0000 Subject: [PATCH 07/48] feat: faster uniqueItems when items are scalars of the same type, closes #608 --- lib/dot/uniqueItems.jst | 34 +++++++++++++++++++++++-------- spec/errors.spec.js | 30 +++++++++++++++++++++++++++ spec/tests/rules/uniqueItems.json | 26 +++++++++++++++++++++++ 3 files changed, 81 insertions(+), 9 deletions(-) create mode 100644 spec/tests/rules/uniqueItems.json diff --git a/lib/dot/uniqueItems.jst b/lib/dot/uniqueItems.jst index dfc42b03b..28327509f 100644 --- a/lib/dot/uniqueItems.jst +++ b/lib/dot/uniqueItems.jst @@ -14,18 +14,34 @@ else { {{?}} - var {{=$valid}} = true; - if ({{=$data}}.length > 1) { - var i = {{=$data}}.length, j; - outer: - for (;i--;) { - for (j = i; j--;) { - if (equal({{=$data}}[i], {{=$data}}[j])) { + var i = {{=$data}}.length + , {{=$valid}} = true + , j; + if (i > 1) { + {{ var $itemType = it.schema.items && it.schema.items.type; }} + {{? !$itemType || $itemType == 'object' || $itemType == 'array' }} + outer: + for (;i--;) { + for (j = i; j--;) { + if (equal({{=$data}}[i], {{=$data}}[j])) { + {{=$valid}} = false; + break outer; + } + } + } + {{??}} + var itemIndices = {}, item; + for (;i--;) { + var item = {{=$data}}[i]; + if (typeof item != '{{=$itemType}}') continue; + if (itemIndices[item] !== undefined) { {{=$valid}} = false; - break outer; + j = itemIndices[item]; + break; } + itemIndices[item] = i; } - } + {{?}} } {{? $isData }} } {{?}} diff --git a/spec/errors.spec.js b/spec/errors.spec.js index 2663d1e8d..436085eec 100644 --- a/spec/errors.spec.js +++ b/spec/errors.spec.js @@ -746,6 +746,36 @@ describe('Validation errors', function () { }); + describe('uniqueItems errors', function() { + it('should not return uniqueItems error when non-unique items are of a different type than required', function() { + var schema = { + items: {type: 'number'}, + uniqueItems: true + }; + + [ajvJP, fullAjv].forEach(function (_ajv) { + var validate = _ajv.compile(schema); + shouldBeValid(validate, [1, 2, 3]); + + shouldBeInvalid(validate, [1, 2, 2]); + shouldBeError(validate.errors[0], 'uniqueItems', '#/uniqueItems', '', + 'should NOT have duplicate items (items ## 2 and 1 are identical)', + {i: 1, j: 2}); + + var expectedErrors = _ajv._opts.allErrors ? 2 : 1; + shouldBeInvalid(validate, [1, "2", "2", 2], expectedErrors); + testTypeError(0, '/1'); + if (expectedErrors == 2) testTypeError(1, '/2'); + + function testTypeError(i, dataPath) { + var err = validate.errors[i]; + shouldBeError(err, 'type', '#/items/type', dataPath, 'should be number'); + } + }); + }); + }); + + function testSchema1(schema, schemaPathPrefix) { _testSchema1(ajv, schema, schemaPathPrefix); _testSchema1(ajvJP, schema, schemaPathPrefix); diff --git a/spec/tests/rules/uniqueItems.json b/spec/tests/rules/uniqueItems.json new file mode 100644 index 000000000..f2ce5d024 --- /dev/null +++ b/spec/tests/rules/uniqueItems.json @@ -0,0 +1,26 @@ +[ + { + "description": "uniqueItems with algorithm using hash", + "schema": { + "items": { "type": "string" }, + "uniqueItems": true + }, + "tests": [ + { + "description": "array of unique strings are valid", + "data": ["foo", "bar", "baz"], + "valid": true + }, + { + "description": "array of non-unique strings are invalid", + "data": ["foo", "bar", "bar"], + "valid": false + }, + { + "description": "array with non-strings is invalid", + "data": ["1", 2], + "valid": false + } + ] + } +] From 2e95b0531f5e8c1c1cf4fd8930d21bb80b56e191 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 29 Oct 2017 18:56:28 +0000 Subject: [PATCH 08/48] feat: "contains" and "uniqueItems" should be validated after type coercion, closes #611 --- lib/compile/rules.js | 2 +- spec/coercion.spec.js | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/compile/rules.js b/lib/compile/rules.js index 63b5a0899..0f53db793 100644 --- a/lib/compile/rules.js +++ b/lib/compile/rules.js @@ -11,7 +11,7 @@ module.exports = function rules() { { type: 'string', rules: [ 'maxLength', 'minLength', 'pattern', 'format' ] }, { type: 'array', - rules: [ 'maxItems', 'minItems', 'uniqueItems', 'contains', 'items' ] }, + rules: [ 'maxItems', 'minItems', 'items', 'contains', 'uniqueItems' ] }, { type: 'object', rules: [ 'maxProperties', 'minProperties', 'required', 'dependencies', 'propertyNames', { 'properties': ['additionalProperties', 'patternProperties'] } ] }, diff --git a/spec/coercion.spec.js b/spec/coercion.spec.js index cc197c2ce..0aa5abf99 100644 --- a/spec/coercion.spec.js +++ b/spec/coercion.spec.js @@ -416,6 +416,39 @@ describe('Type coercion', function () { }); + it('should check "uniqueItems" after coercion', function() { + var schema = { + items: {type: 'number'}, + uniqueItems: true + }; + + instances.forEach(function (_ajv) { + var validate = _ajv.compile(schema); + validate([1, '2', 3]). should.equal(true); + + validate([1, '2', 2]). should.equal(false); + validate.errors.length .should.equal(1); + validate.errors[0].keyword .should.equal('uniqueItems'); + }); + }); + + + it('should check "contains" after coercion', function() { + var schema = { + items: {type: 'number'}, + contains: {const: 2} + }; + + instances.forEach(function (_ajv) { + var validate = _ajv.compile(schema); + validate([1, '2', 3]). should.equal(true); + + validate([1, '3', 4]). should.equal(false); + validate.errors.pop().keyword .should.equal('contains'); + }); + }); + + function testRules(rules, cb) { for (var toType in rules) { for (var fromType in rules[toType]) { From a68e9a7535deaf25163996996f1ea8885b5fe093 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 29 Oct 2017 20:51:08 +0000 Subject: [PATCH 09/48] feat: improve oneOf error reporting, #573 --- lib/dot/errors.def | 2 +- lib/dot/oneOf.jst | 24 +++++++++++++++++------- spec/errors.spec.js | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/lib/dot/errors.def b/lib/dot/errors.def index 16e1191c5..562052c1a 100644 --- a/lib/dot/errors.def +++ b/lib/dot/errors.def @@ -182,7 +182,7 @@ _limitProperties:"{ limit: {{=$schemaValue}} }", multipleOf: "{ multipleOf: {{=$schemaValue}} }", not: "{}", - oneOf: "{}", + oneOf: "{ passingSchemas: {{=$passingSchemas}} }", pattern: "{ pattern: {{#def.schemaValueQS}} }", patternGroups: "{ reason: '{{=$reason}}', limit: {{=$limit}}, pattern: '{{=it.util.escapeQuotes($pgProperty)}}' }", propertyNames: "{ propertyName: '{{=$invalidName}}' }", diff --git a/lib/dot/oneOf.jst b/lib/dot/oneOf.jst index 59a435549..bcce2c6ed 100644 --- a/lib/dot/oneOf.jst +++ b/lib/dot/oneOf.jst @@ -3,11 +3,17 @@ {{# def.setupKeyword }} {{# def.setupNextLevel }} -var {{=$errs}} = errors; -var prevValid{{=$lvl}} = false; -var {{=$valid}} = false; +{{ + var $currentBaseId = $it.baseId + , $prevValid = 'prevValid' + $lvl + , $passingSchemas = 'passingSchemas' + $lvl; +}} + +var {{=$errs}} = errors + , {{=$prevValid}} = false + , {{=$valid}} = false + , {{=$passingSchemas}} = null; -{{ var $currentBaseId = $it.baseId; }} {{# def.setCompositeRule }} {{~ $schema:$sch:$i }} @@ -24,13 +30,17 @@ var {{=$valid}} = false; {{?}} {{? $i }} - if ({{=$nextValid}} && prevValid{{=$lvl}}) + if ({{=$nextValid}} && {{=$prevValid}}) { {{=$valid}} = false; - else { + {{=$passingSchemas}} = [{{=$passingSchemas}}, {{=$i}}]; + } else { {{ $closingBraces += '}'; }} {{?}} - if ({{=$nextValid}}) {{=$valid}} = prevValid{{=$lvl}} = true; + if ({{=$nextValid}}) { + {{=$valid}} = {{=$prevValid}} = true; + {{=$passingSchemas}} = {{=$i}}; + } {{~}} {{# def.resetCompositeRule }} diff --git a/spec/errors.spec.js b/spec/errors.spec.js index 436085eec..384cbcef5 100644 --- a/spec/errors.spec.js +++ b/spec/errors.spec.js @@ -539,6 +539,39 @@ describe('Validation errors', function () { validate(1.5) .should.equal(true); } }); + + it('should return passing schemas in error params', function() { + var schema = { + oneOf: [ + { type: 'number' }, + { type: 'integer' }, + { const: 1.5 } + ] + }; + + test(ajv); + test(fullAjv); + + function test(_ajv) { + var validate = _ajv.compile(schema); + validate(1) .should.equal(false); + var err = validate.errors.pop(); + err.keyword .should.equal('oneOf'); + err.params .should.eql({passingSchemas: [0, 1]}); + + validate(1.5) .should.equal(false); + err = validate.errors.pop(); + err.keyword .should.equal('oneOf'); + err.params .should.eql({passingSchemas: [0, 2]}); + + validate(2.5) .should.equal(true); + + validate('foo') .should.equal(false); + err = validate.errors.pop(); + err.keyword .should.equal('oneOf'); + err.params .should.eql({passingSchemas: null}); + } + }); }); From 51992848fb976ac9cd324560d6972a7a017c55b7 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 29 Oct 2017 21:04:14 +0000 Subject: [PATCH 10/48] docs: oneOf error parameters, closes #573 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0879146b5..b4dd2bc4a 100644 --- a/README.md +++ b/README.md @@ -1228,6 +1228,7 @@ Properties of `params` object in errors depend on the keyword that failed valida - `uniqueItems` - properties `i` and `j` (indices of duplicate items). - `enum` - property `allowedValues` pointing to the array of values (the schema of the keyword). - `$ref` - property `ref` with the referenced schema URI. +- `oneOf` - property `passingSchemas` (array of indices of passing schemas, null if no schema passes). - custom keywords (in case keyword definition doesn't create errors) - property `keyword` (the keyword name). From 1d44f1451a960f8e1163f3ad0443bcc86733a895 Mon Sep 17 00:00:00 2001 From: Brendan Abbott Date: Fri, 3 Nov 2017 02:18:41 +1000 Subject: [PATCH 11/48] Add leap year support for date-time/date formats in full mode --- CONTRIBUTING.md | 2 +- lib/compile/formats.js | 25 ++++++++++++++++++++----- spec/issues.spec.js | 24 ++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d6872665e..ff77bc0bf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -63,7 +63,7 @@ Please include as much details as possible. #### Browser and compatibility issues -[Create an issue](https://github.com/epoberezkin/ajv/issues/new?labels=compatibility&body=**The%20version%20of%20Ajv%20you%20are%20using**%0A%0A**The%20environment%20you%20have%20the%20problem%20with.**%0A%0A**Your%20code%20(please%20make%20it%20as%20small%20as%20possible%20to%20reproduce%20the%20issue).**%0A%0A**If%20your%20issue%20is%20in%20the%20browser,%20please%20list%20the%20other%20packages%20loaded%20in%20the%20page%20in%20the%20order%20they%20are%20loaded.%20Please%20check%20if%20the%20issue%20gets%20resolved%20(or%20results%20change)%20if%20you%20move%20Ajv%20bundle%20closer%20to%20the%20top.**%0A%0A**Results%20in%20node.js%20v4.**%0A%0A**Results%20and%20error%20messages%20in%20your%20platform.**%0A%0A) to report a compatibility problem that only happens in a particular environemnt (when your code works correctly in node.js v4 in linux systems but fails in some other environment). +[Create an issue](https://github.com/epoberezkin/ajv/issues/new?labels=compatibility&body=**The%20version%20of%20Ajv%20you%20are%20using**%0A%0A**The%20environment%20you%20have%20the%20problem%20with.**%0A%0A**Your%20code%20(please%20make%20it%20as%20small%20as%20possible%20to%20reproduce%20the%20issue).**%0A%0A**If%20your%20issue%20is%20in%20the%20browser,%20please%20list%20the%20other%20packages%20loaded%20in%20the%20page%20in%20the%20order%20they%20are%20loaded.%20Please%20check%20if%20the%20issue%20gets%20resolved%20(or%20results%20change)%20if%20you%20move%20Ajv%20bundle%20closer%20to%20the%20top.**%0A%0A**Results%20in%20node.js%20v4.**%0A%0A**Results%20and%20error%20messages%20in%20your%20platform.**%0A%0A) to report a compatibility problem that only happens in a particular environment (when your code works correctly in node.js v4 in linux systems but fails in some other environment). Please include this information: diff --git a/lib/compile/formats.js b/lib/compile/formats.js index a2fcebae5..0354f7aea 100644 --- a/lib/compile/formats.js +++ b/lib/compile/formats.js @@ -2,8 +2,8 @@ var util = require('./util'); -var DATE = /^\d\d\d\d-(\d\d)-(\d\d)$/; -var DAYS = [0,31,29,31,30,31,30,31,31,30,31,30,31]; +var DATE = /^(\d\d\d\d)-(\d\d)-(\d\d)$/; +var DAYS = [0,31,28,31,30,31,30,31,31,30,31,30,31]; var TIME = /^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d:\d\d)?$/i; var HOSTNAME = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*$/i; var URI = /^(?:[a-z][a-z0-9+\-.]*:)(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i; @@ -81,14 +81,29 @@ formats.full = { }; +function isLeapYear(year) { + // https://tools.ietf.org/html/rfc3339#appendix-C + return (year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)); +} + + function date(str) { // full-date from http://tools.ietf.org/html/rfc3339#section-5.6 var matches = str.match(DATE); if (!matches) return false; - var month = +matches[1]; - var day = +matches[2]; - return month >= 1 && month <= 12 && day >= 1 && day <= DAYS[month]; + var year = +matches[1]; + var month = +matches[2]; + var day = +matches[3]; + + // Check month and day first... + var isValid = month >= 1 && month <= 12 && day >= 1; + + // ...if valid, then check day, taking leap years into consideration + if (isValid === true) + isValid = day <= (month === 2 && !isLeapYear(year) ? DAYS[month] : 29); + + return isValid; } diff --git a/spec/issues.spec.js b/spec/issues.spec.js index e0ba9fc60..52d53eea5 100644 --- a/spec/issues.spec.js +++ b/spec/issues.spec.js @@ -625,3 +625,27 @@ describe('issue #533, throwing missing ref exception with option missingRefs: "i }); }); }); + +describe('full date format validation should understand leap years', function () { + it('should handle year leaps as date-time', function() { + var ajv = new Ajv({ format: 'full' }); + + var schema = { format: 'date-time' }; + var validDateTime = '2016-02-29T00:00:00Z'; + var invalidDateTime = '2017-02-29T00:00:00Z'; + + ajv.validate(schema, validDateTime) .should.equal(true); + ajv.validate(schema, invalidDateTime) .should.equal(false); + }); + + it('should handle year leaps as date', function() { + var ajv = new Ajv({ format: 'full' }); + + var schema = { format: 'date' }; + var validDate = '2016-02-29'; + var invalidDate = '2017-02-29'; + + ajv.validate(schema, validDate) .should.equal(true); + ajv.validate(schema, invalidDate) .should.equal(false); + }); +}); From 27855e1b72e5830e23f7719406cfdb8be5dcd024 Mon Sep 17 00:00:00 2001 From: Brendan Abbott Date: Fri, 3 Nov 2017 21:35:38 +1000 Subject: [PATCH 12/48] Fix logic error for non leap years and months, add more tests --- lib/compile/formats.js | 9 +++++++-- spec/issues.spec.js | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/lib/compile/formats.js b/lib/compile/formats.js index 0354f7aea..f4894bb15 100644 --- a/lib/compile/formats.js +++ b/lib/compile/formats.js @@ -100,8 +100,13 @@ function date(str) { var isValid = month >= 1 && month <= 12 && day >= 1; // ...if valid, then check day, taking leap years into consideration - if (isValid === true) - isValid = day <= (month === 2 && !isLeapYear(year) ? DAYS[month] : 29); + if (isValid === true) { + var maxDaysInMonth = DAYS[month]; + + if (month === 2 && isLeapYear(year)) maxDaysInMonth = 29; + + isValid = day <= maxDaysInMonth; + } return isValid; } diff --git a/spec/issues.spec.js b/spec/issues.spec.js index 52d53eea5..45ec6e927 100644 --- a/spec/issues.spec.js +++ b/spec/issues.spec.js @@ -627,6 +627,24 @@ describe('issue #533, throwing missing ref exception with option missingRefs: "i }); describe('full date format validation should understand leap years', function () { + it('should handle non leap year affected dates with date-time', function() { + var ajv = new Ajv({ format: 'full' }); + + var schema = { format: 'date-time' }; + var validDateTime = '2016-01-31T00:00:00Z'; + + ajv.validate(schema, validDateTime).should.equal(true); + }); + + it('should handle non leap year affected dates with date', function () { + var ajv = new Ajv({ format: 'full' }); + + var schema = { format: 'date' }; + var validDate = '2016-11-30'; + + ajv.validate(schema, validDate).should.equal(true); + }); + it('should handle year leaps as date-time', function() { var ajv = new Ajv({ format: 'full' }); From e65673d1cb34da473ef168666b32c369941441a1 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 4 Nov 2017 10:51:58 +0000 Subject: [PATCH 13/48] refactor: leap year --- lib/compile/formats.js | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/lib/compile/formats.js b/lib/compile/formats.js index f4894bb15..ab63a81a8 100644 --- a/lib/compile/formats.js +++ b/lib/compile/formats.js @@ -83,7 +83,7 @@ formats.full = { function isLeapYear(year) { // https://tools.ietf.org/html/rfc3339#appendix-C - return (year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)); + return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); } @@ -96,19 +96,8 @@ function date(str) { var month = +matches[2]; var day = +matches[3]; - // Check month and day first... - var isValid = month >= 1 && month <= 12 && day >= 1; - - // ...if valid, then check day, taking leap years into consideration - if (isValid === true) { - var maxDaysInMonth = DAYS[month]; - - if (month === 2 && isLeapYear(year)) maxDaysInMonth = 29; - - isValid = day <= maxDaysInMonth; - } - - return isValid; + return month >= 1 && month <= 12 && day >= 1 && + day <= (month == 2 && isLeapYear(year) ? 29 : DAYS[month]); } From c9548d2c746c9037b17de3561e521d8054374a29 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 5 Nov 2017 21:14:31 +0000 Subject: [PATCH 14/48] refactor: remove compilation of async schemas to generator functions, closes #585 --- .travis.yml | 2 +- README.md | 94 +++++-------------------------------- karma.conf.js | 1 - lib/ajv.js | 7 +-- lib/compile/index.js | 3 -- lib/dot/custom.jst | 4 +- lib/dot/format.jst | 4 +- lib/dot/ref.jst | 2 +- lib/dot/validate.jst | 30 +++--------- package.json | 8 +--- spec/ajv_async_instances.js | 72 ++-------------------------- spec/async_validate.spec.js | 85 +++++++++------------------------ 12 files changed, 54 insertions(+), 258 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0ada0bbab..7f940ba86 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,8 @@ before_script: node_js: - "4" - "6" - - "7" - "8" + - "9" after_script: - codeclimate-test-reporter < coverage/lcov.info - coveralls < coverage/lcov.info diff --git a/README.md b/README.md index b4dd2bc4a..cf91fbf2d 100644 --- a/README.md +++ b/README.md @@ -490,17 +490,11 @@ If your schema uses asynchronous formats/keywords or refers to some schema that __Please note__: all asynchronous subschemas that are referenced from the current or other schemas should have `"$async": true` keyword as well, otherwise the schema compilation will fail. -Validation function for an asynchronous custom format/keyword should return a promise that resolves with `true` or `false` (or rejects with `new Ajv.ValidationError(errors)` if you want to return custom errors from the keyword function). Ajv compiles asynchronous schemas to either [es7 async functions](http://tc39.github.io/ecmascript-asyncawait/) that can optionally be transpiled with [nodent](https://github.com/MatAtBread/nodent) or with [regenerator](https://github.com/facebook/regenerator) or to [generator functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*) that can be optionally transpiled with regenerator as well. You can also supply any other transpiler as a function. See [Options](#options). +Validation function for an asynchronous custom format/keyword should return a promise that resolves with `true` or `false` (or rejects with `new Ajv.ValidationError(errors)` if you want to return custom errors from the keyword function). -The compiled validation function has `$async: true` property (if the schema is asynchronous), so you can differentiate these functions if you are using both synchronous and asynchronous schemas. - -If you are using generators, the compiled validation function can be either wrapped with [co](https://github.com/tj/co) (default) or returned as generator function, that can be used directly, e.g. in [koa](http://koajs.com/) 1.0. `co` is a small library, it is included in Ajv (both as npm dependency and in the browser bundle). - -Async functions are currently supported in Chrome 55, Firefox 52, Node.js 7 (with --harmony-async-await) and MS Edge 13 (with flag). +Ajv compiles asynchronous schemas to [es7 async functions](http://tc39.github.io/ecmascript-asyncawait/) that can optionally be transpiled with [nodent](https://github.com/MatAtBread/nodent). Async functions are supported in Node.js 7+ and all modern browsers. You can also supply any other transpiler as a function via `processCode` option. See [Options](#options). -Generator functions are currently supported in Chrome, Firefox and Node.js. - -If you are using Ajv in other browsers or in older versions of Node.js you should use one of available transpiling options. All provided async modes use global Promise class. If your platform does not have Promise you should use a polyfill that defines it. +The compiled validation function has `$async: true` property (if the schema is asynchronous), so you can differentiate these functions if you are using both synchronous and asynchronous schemas. Validation result will be a promise that resolves with validated data or rejects with an exception `Ajv.ValidationError` that contains the array of validation errors in `errors` property. @@ -508,21 +502,8 @@ Validation result will be a promise that resolves with validated data or rejects Example: ```javascript -/** - * Default mode is non-transpiled generator function wrapped with `co`. - * Using package ajv-async (https://github.com/epoberezkin/ajv-async) - * you can auto-detect the best async mode. - * In this case, without "async" and "transpile" options - * (or with option {async: true}) - * Ajv will choose the first supported/installed option in this order: - * 1. native async function - * 2. native generator function wrapped with co - * 3. es7 async functions transpiled with nodent - * 4. es7 async functions transpiled with regenerator - */ - -var setupAsync = require('ajv-async'); -var ajv = setupAsync(new Ajv); +var ajv = new Ajv; +// require('ajv-async')(ajv); ajv.addKeyword('idExists', { async: true, @@ -571,39 +552,23 @@ validate({ userId: 1, postId: 19 }) To use a transpiler you should separately install it (or load its bundle in the browser). -Ajv npm package includes minified browser bundles of regenerator and nodent in dist folder. +Ajv npm package includes minified browser bundle of regenerator and nodent in dist folder. #### Using nodent ```javascript -var setupAsync = require('ajv-async'); -var ajv = new Ajv({ /* async: 'es7', */ transpile: 'nodent' }); -setupAsync(ajv); -var validate = ajv.compile(schema); // transpiled es7 async function -validate(data).then(successFunc).catch(errorFunc); -``` - -`npm install nodent` or use `nodent.min.js` from dist folder of npm package. - - -#### Using regenerator - -```javascript -var setupAsync = require('ajv-async'); -var ajv = new Ajv({ /* async: 'es7', */ transpile: 'regenerator' }); -setupAsync(ajv); +var ajv = new Ajv; +require('ajv-async')(ajv); var validate = ajv.compile(schema); // transpiled es7 async function validate(data).then(successFunc).catch(errorFunc); ``` -`npm install regenerator` or use `regenerator.min.js` from dist folder of npm package. - #### Using other transpilers ```javascript -var ajv = new Ajv({ async: 'es7', processCode: transpileFunc }); +var ajv = new Ajv({ processCode: transpileFunc }); var validate = ajv.compile(schema); // transpiled es7 async function validate(data).then(successFunc).catch(errorFunc); ``` @@ -611,26 +576,6 @@ validate(data).then(successFunc).catch(errorFunc); See [Options](#options). -#### Comparison of async modes - -|mode|transpile
speed*|run-time
speed*|bundle
size| -|---|:-:|:-:|:-:| -|es7 async
(native)|-|0.75|-| -|generators
(native)|-|1.0|-| -|es7.nodent|1.35|1.1|215Kb| -|es7.regenerator|1.0|2.7|1109Kb| -|regenerator|1.0|3.2|1109Kb| - -\* Relative performance in Node.js 7.x — smaller is better. - -[nodent](https://github.com/MatAtBread/nodent) has several advantages: - -- much smaller browser bundle than regenerator -- almost the same performance of generated code as native generators in Node.js and the latest Chrome -- much better performance than native generators in other browsers -- works in IE 9 (regenerator does not) - - ## Filtering data With [option `removeAdditional`](#options) (added by [andyscott](https://github.com/andyscott)) you can filter data during the validation. @@ -1062,7 +1007,6 @@ Defaults: useDefaults: false, coerceTypes: false, // asynchronous validation options: - async: 'co*', transpile: undefined, // requires ajv-async package // advanced options: meta: true, @@ -1135,24 +1079,10 @@ Defaults: ##### Asynchronous validation options -- _async_: determines how Ajv compiles asynchronous schemas (see [Asynchronous validation](#asynchronous-validation)) to functions. Option values: - - `"*"` / `"co*"` (default) - compile to generator function ("co*" - wrapped with `co.wrap`). If generators are not supported and you don't provide `processCode` option (or `transpile` option if you use [ajv-async](https://github.com/epoberezkin/ajv-async) package), the exception will be thrown when async schema is compiled. - - `"es7"` - compile to es7 async function. Unless your platform supports them you need to provide `processCode` or `transpile` option. According to [compatibility table](http://kangax.github.io/compat-table/es7/)) async functions are supported by: - - Firefox 52, - - Chrome 55, - - Node.js 7 (with `--harmony-async-await`), - - MS Edge 13 (with flag). - - `undefined`/`true` - auto-detect async mode. It requires [ajv-async](https://github.com/epoberezkin/ajv-async) package. If `transpile` option is not passed, ajv-async will choose the first of supported/installed async/transpile modes in this order: - - "es7" (native async functions), - - "co*" (native generators with co.wrap), - - "es7"/"nodent", - - "co*"/"regenerator" during the creation of the Ajv instance. - - If none of the options is available the exception will be thrown. - _transpile_: Requires [ajv-async](https://github.com/epoberezkin/ajv-async) package. It determines whether Ajv transpiles compiled asynchronous validation function. Option values: - - `"nodent"` - transpile with [nodent](https://github.com/MatAtBread/nodent). If nodent is not installed, the exception will be thrown. nodent can only transpile es7 async functions; it will enforce this mode. - - `"regenerator"` - transpile with [regenerator](https://github.com/facebook/regenerator). If regenerator is not installed, the exception will be thrown. - - a function - this function should accept the code of validation function as a string and return transpiled code. This option allows you to use any other transpiler you prefer. If you are passing a function, you can simply pass it to `processCode` option without using ajv-async. + - `undefined` (default) - transpile with [nodent](https://github.com/MatAtBread/nodent) if async functions are not supported. + - `true` - always transpile with nodent. + - `false` - do not transpile; if async functions are not supported an exception will be thrown. ##### Advanced options diff --git a/karma.conf.js b/karma.conf.js index 1ffca9b0f..91cde5510 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -17,7 +17,6 @@ module.exports = function(config) { files: [ 'dist/ajv.min.js', 'node_modules/chai/chai.js', - 'dist/regenerator.min.js', 'dist/nodent.min.js', 'node_modules/bluebird/js/browser/bluebird.core.min.js', '.browser/*.spec.js' diff --git a/lib/ajv.js b/lib/ajv.js index ab187b88e..c18ed4305 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -9,8 +9,7 @@ var compileSchema = require('./compile') , rules = require('./compile/rules') , $dataMetaSchema = require('./$data') , patternGroups = require('./patternGroups') - , util = require('./compile/util') - , co = require('co'); + , util = require('./compile/util'); module.exports = Ajv; @@ -98,9 +97,7 @@ function validate(schemaKeyRef, data) { } var valid = v(data); - if (v.$async === true) - return this._opts.async == '*' ? co(valid) : valid; - this.errors = v.errors; + if (v.$async !== true) this.errors = v.errors; return valid; } diff --git a/lib/compile/index.js b/lib/compile/index.js index e6d15f469..e09e5286e 100644 --- a/lib/compile/index.js +++ b/lib/compile/index.js @@ -11,7 +11,6 @@ var validateGenerator = require('../dotjs/validate'); * Functions below are used inside compiled validations function */ -var co = require('co'); var ucs2length = util.ucs2length; var equal = require('fast-deep-equal'); @@ -123,7 +122,6 @@ function compile(schema, root, localRefs, baseId) { 'refVal', 'defaults', 'customRules', - 'co', 'equal', 'ucs2length', 'ValidationError', @@ -138,7 +136,6 @@ function compile(schema, root, localRefs, baseId) { refVal, defaults, customRules, - co, equal, ucs2length, ValidationError diff --git a/lib/dot/custom.jst b/lib/dot/custom.jst index 402028e6b..d30588fb0 100644 --- a/lib/dot/custom.jst +++ b/lib/dot/custom.jst @@ -112,13 +112,13 @@ var {{=$valid}}; {{# def.storeDefOut:def_callRuleValidate }} {{? $rDef.errors === false }} - {{=$valid}} = {{? $asyncKeyword }}{{=it.yieldAwait}}{{?}}{{= def_callRuleValidate }}; + {{=$valid}} = {{? $asyncKeyword }}await {{?}}{{= def_callRuleValidate }}; {{??}} {{? $asyncKeyword }} {{ $ruleErrs = 'customErrors' + $lvl; }} var {{=$ruleErrs}} = null; try { - {{=$valid}} = {{=it.yieldAwait}}{{= def_callRuleValidate }}; + {{=$valid}} = await {{= def_callRuleValidate }}; } catch (e) { {{=$valid}} = false; if (e instanceof ValidationError) {{=$ruleErrs}} = e.errors; diff --git a/lib/dot/format.jst b/lib/dot/format.jst index 074d16c31..d303fe0cc 100644 --- a/lib/dot/format.jst +++ b/lib/dot/format.jst @@ -24,7 +24,7 @@ ({{=$format}} && {{=$formatType}} == '{{=$ruleType}}' && !(typeof {{=$format}} == 'function' ? {{? it.async}} - (async{{=$lvl}} ? {{=it.yieldAwait}} {{=$format}}({{=$data}}) : {{=$format}}({{=$data}})) + (async{{=$lvl}} ? await {{=$format}}({{=$data}}) : {{=$format}}({{=$data}})) {{??}} {{=$format}}({{=$data}}) {{?}} @@ -97,7 +97,7 @@ if (!it.async) throw new Error('async format in sync schema'); var $formatRef = 'formats' + it.util.getProperty($schema) + '.validate'; }} - if (!({{=it.yieldAwait}} {{=$formatRef}}({{=$data}}))) { + if (!(await {{=$formatRef}}({{=$data}}))) { {{??}} if (!{{# def.checkFormat }}) { {{?}} diff --git a/lib/dot/ref.jst b/lib/dot/ref.jst index 4a0889686..eb807523c 100644 --- a/lib/dot/ref.jst +++ b/lib/dot/ref.jst @@ -65,7 +65,7 @@ {{ if (!it.async) throw new Error('async schema referenced by sync schema'); }} {{? $breakOnError }} var {{=$valid}}; {{?}} try { - {{=it.yieldAwait}} {{=__callValidate}}; + await {{=__callValidate}}; {{? $breakOnError }} {{=$valid}} = true; {{?}} } catch (e) { if (!(e instanceof ValidationError)) throw e; diff --git a/lib/dot/validate.jst b/lib/dot/validate.jst index ef1e35a64..3a508c626 100644 --- a/lib/dot/validate.jst +++ b/lib/dot/validate.jst @@ -21,29 +21,11 @@ }} {{? it.isTop }} - {{? $async }} - {{ - it.async = true; - var $es7 = it.opts.async == 'es7'; - it.yieldAwait = $es7 ? 'await' : 'yield'; - }} - {{?}} - - var validate = - {{? $async }} - {{? $es7 }} - (async function - {{??}} - {{? it.opts.async != '*'}}co.wrap{{?}}(function* - {{?}} - {{??}} - (function + var validate = {{?$async}}{{it.async = true;}}async {{?}}function(data, dataPath, parentData, parentDataProperty, rootData) { + 'use strict'; + {{? $id && (it.opts.sourceCode || it.opts.processCode) }} + {{= '/\*# sourceURL=' + $id + ' */' }} {{?}} - (data, dataPath, parentData, parentDataProperty, rootData) { - 'use strict'; - {{? $id && (it.opts.sourceCode || it.opts.processCode) }} - {{= '/\*# sourceURL=' + $id + ' */' }} - {{?}} {{?}} {{? typeof it.schema == 'boolean' || !($refKeywords || it.schema.$ref) }} @@ -70,7 +52,7 @@ {{?}} {{? it.isTop}} - }); + }; return validate; {{?}} @@ -237,7 +219,7 @@ validate.errors = vErrors; {{ /* don't edit, used in replace */ }} return errors === 0; {{ /* don't edit, used in replace */ }} {{?}} - }); + }; return validate; {{??}} diff --git a/package.json b/package.json index c5b734767..d57608f89 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,8 @@ "test-cov": "nyc npm run test-spec", "test-ts": "tsc --target ES5 --noImplicitAny lib/ajv.d.ts", "bundle": "node ./scripts/bundle.js . Ajv pure_getters", - "bundle-regenerator": "node ./scripts/bundle.js regenerator", "bundle-nodent": "node ./scripts/bundle.js nodent", - "bundle-all": "del-cli dist && npm run bundle && npm run bundle-regenerator && npm run bundle-nodent", + "bundle-all": "del-cli dist && npm run bundle && npm run bundle-nodent", "bundle-beautify": "node ./scripts/bundle.js js-beautify", "build": "del-cli lib/dotjs/*.js '!lib/dotjs/index.js' && node scripts/compile-dots.js", "test-karma": "karma start --single-run --browsers PhantomJS", @@ -63,13 +62,12 @@ "homepage": "https://github.com/epoberezkin/ajv", "tonicExampleFilename": ".tonic_example.js", "dependencies": { - "co": "^4.6.0", "fast-deep-equal": "^1.0.0", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.3.0" }, "devDependencies": { - "ajv-async": "^0.1.0", + "ajv-async": "^1.0.0-beta.0", "bluebird": "^3.1.5", "brfs": "^1.4.3", "browserify": "^14.1.0", @@ -90,11 +88,9 @@ "karma-phantomjs-launcher": "^1.0.0", "karma-sauce-launcher": "^1.1.0", "mocha": "^4.0.0", - "nodent": "^3.0.17", "nyc": "^11.0.2", "phantomjs-prebuilt": "^2.1.4", "pre-commit": "^1.1.1", - "regenerator": "0.10.0", "require-globify": "^1.3.0", "typescript": "^2.0.3", "uglify-js": "^3.1.5", diff --git a/spec/ajv_async_instances.js b/spec/ajv_async_instances.js index 85854f37c..73fa36b52 100644 --- a/spec/ajv_async_instances.js +++ b/spec/ajv_async_instances.js @@ -6,91 +6,27 @@ var Ajv = require('./ajv') module.exports = getAjvInstances; - var firstTime = true; -var isBrowser = typeof window == 'object'; -var fullTest = isBrowser || !process.env.AJV_FAST_TEST; - function getAjvInstances(opts) { opts = opts || {}; var instances = []; var options = [ {}, - { async: true }, - { async: 'co*' }, - { async: 'es7' }, - { async: 'es7', transpile: 'nodent' }, - { async: 'co*', allErrors: true }, - { async: 'es7', allErrors: true }, - { async: 'es7', transpile: 'nodent', allErrors: true } + { transpile: true }, + { allErrors: true }, + { transpile: true, allErrors: true } ]; - var ua; - try { ua = window.navigator.userAgent.toLowerCase(); } catch(e) {} - - // regenerator does not work in IE9 - if (!(ua && /msie\s9/.test(ua))) { - options = options.concat([ - { async: '*', transpile: 'regenerator' }, - { async: '*', transpile: 'regenerator', allErrors: true } - ]); - } - - if (fullTest) { - options = options.concat([ - { async: '*' }, - { allErrors: true }, - { async: true, allErrors: true }, - { async: '*', allErrors: true } - ]); - - if (!(ua && /msie\s9/.test(ua))) { - options = options.concat([ - { async: 'co*', transpile: 'regenerator' }, - { async: 'co*', transpile: 'regenerator', allErrors: true } - ]); - } - - // es7 functions transpiled with regenerator are excluded from test in Safari/Firefox/Edge/IE9. - // They fail in IE9 and emit multiple 'uncaught exception' warnings in Safari/Firefox/Edge anc cause remote tests to disconnect. - if (!(ua && ((/safari/.test(ua) && !/chrome|phantomjs/.test(ua)) || /firefox|edge|msie\s9/.test(ua)))) { - options = options.concat([ - { transpile: 'regenerator' }, - { async: true, transpile: 'regenerator' }, - { async: 'es7', transpile: 'regenerator' }, - { transpile: 'regenerator', allErrors: true }, - { async: true, transpile: 'regenerator', allErrors: true }, - { async: 'es7', transpile: 'regenerator', allErrors: true } - ]); - } - } - - // options = options.filter(function (_opts) { - // return _opts.transpile == 'nodent'; - // }); - - // var i = 10, repeatOptions = []; - // while (i--) repeatOptions = repeatOptions.concat(options); - // options = repeatOptions; - options.forEach(function (_opts) { util.copy(opts, _opts); var ajv = getAjv(_opts); if (ajv) instances.push(ajv); }); - if (firstTime) { - var asyncModes = []; - instances.forEach(function (ajv) { - if (!ajv._opts.async) return; - var t = ajv._opts.transpile; - var mode = ajv._opts.async + (t === true ? '' : '.' + t); - if (asyncModes.indexOf(mode) == -1) asyncModes.push(mode); - }); - console.log('Testing', instances.length, 'ajv instances:', asyncModes.join(',')); + console.log('Testing', instances.length, 'ajv instances:'); firstTime = false; } diff --git a/spec/async_validate.spec.js b/spec/async_validate.spec.js index fdfdf5dde..9ba6a2b64 100644 --- a/spec/async_validate.spec.js +++ b/spec/async_validate.spec.js @@ -3,9 +3,7 @@ var Ajv = require('./ajv') , Promise = require('./promise') , getAjvInstances = require('./ajv_async_instances') - , should = require('./chai').should() - , co = require('co') - , setupAsync = require('ajv-async'); + , should = require('./chai').should(); describe('async schemas, formats and keywords', function() { @@ -17,12 +15,6 @@ describe('async schemas, formats and keywords', function() { ajv = instances[0]; }); - function useCo(_ajv) { - var async = _ajv._opts.async; - return async == 'es7' || async == 'co*' ? identity : co; - } - - function identity(x) { return x; } describe('async schemas without async elements', function() { it('should return result as promise', function() { @@ -36,12 +28,11 @@ describe('async schemas, formats and keywords', function() { function test(_ajv) { var validate = _ajv.compile(schema); - var _co = useCo(_ajv); return Promise.all([ - shouldBeValid( _co(validate('abc')), 'abc' ), - shouldBeInvalid( _co(validate('abcd')) ), - shouldBeInvalid( _co(validate(1)) ), + shouldBeValid( validate('abc'), 'abc' ), + shouldBeInvalid( validate('abcd') ), + shouldBeInvalid( validate(1) ), ]); } }); @@ -149,11 +140,10 @@ describe('async schemas, formats and keywords', function() { }; var validate = _ajv.compile(schema); - var _co = useCo(_ajv); return Promise.all([ - shouldBeInvalid(_co(validate({ userId: 5, postId: 10 })), [ 'id not found in table posts' ]), - shouldBeInvalid(_co(validate({ userId: 9, postId: 25 })), [ 'id not found in table users' ]) + shouldBeInvalid( validate({ userId: 5, postId: 10 }), [ 'id not found in table posts' ] ), + shouldBeInvalid( validate({ userId: 9, postId: 25 }), [ 'id not found in table users' ] ) ]); })); }); @@ -214,14 +204,13 @@ describe('async schemas, formats and keywords', function() { return repeat(function() { return Promise.all(instances.map(function (_ajv) { var validate = _ajv.compile(schema); - var _co = useCo(_ajv); var validData = { word: 'tomorrow' }; return Promise.all([ - shouldBeValid( _co(validate(validData)), validData ), - shouldBeInvalid( _co(validate({ word: 'manana' })) ), - shouldBeInvalid( _co(validate({ word: 1 })) ), - shouldThrow( _co(validate({ word: 'today' })), 'unknown word' ) + shouldBeValid( validate(validData), validData ), + shouldBeInvalid( validate({ word: 'manana' }) ), + shouldBeInvalid( validate({ word: 1 }) ), + shouldThrow( validate({ word: 'today' }), 'unknown word' ) ]); })); }); }); @@ -340,22 +329,21 @@ describe('async schemas, formats and keywords', function() { return repeat(function() { return Promise.all(instances.map(function (_ajv) { if (refSchema) try { _ajv.addSchema(refSchema); } catch(e) {} var validate = _ajv.compile(schema); - var _co = useCo(_ajv); var data; return Promise.all([ - shouldBeValid( _co(validate(data = { foo: 'tomorrow' })), data ), - shouldBeInvalid( _co(validate({ foo: 'manana' })) ), - shouldBeInvalid( _co(validate({ foo: 1 })) ), - shouldThrow( _co(validate({ foo: 'today' })), 'unknown word' ), - shouldBeValid( _co(validate(data = { foo: { foo: 'tomorrow' }})), data ), - shouldBeInvalid( _co(validate({ foo: { foo: 'manana' }})) ), - shouldBeInvalid( _co(validate({ foo: { foo: 1 }})) ), - shouldThrow( _co(validate({ foo: { foo: 'today' }})), 'unknown word' ), - shouldBeValid( _co(validate(data = { foo: { foo: { foo: 'tomorrow' }}})), data ), - shouldBeInvalid( _co(validate({ foo: { foo: { foo: 'manana' }}})) ), - shouldBeInvalid( _co(validate({ foo: { foo: { foo: 1 }}})) ), - shouldThrow( _co(validate({ foo: { foo: { foo: 'today' }}})), 'unknown word' ) + shouldBeValid( validate(data = { foo: 'tomorrow' }), data ), + shouldBeInvalid( validate({ foo: 'manana' }) ), + shouldBeInvalid( validate({ foo: 1 }) ), + shouldThrow( validate({ foo: 'today' }), 'unknown word' ), + shouldBeValid( validate(data = { foo: { foo: 'tomorrow' }}), data ), + shouldBeInvalid( validate({ foo: { foo: 'manana' }}) ), + shouldBeInvalid( validate({ foo: { foo: 1 }}) ), + shouldThrow( validate({ foo: { foo: 'today' }}), 'unknown word' ), + shouldBeValid( validate(data = { foo: { foo: { foo: 'tomorrow' }}}), data ), + shouldBeInvalid( validate({ foo: { foo: { foo: 'manana' }}}) ), + shouldBeInvalid( validate({ foo: { foo: { foo: 1 }}}) ), + shouldThrow( validate({ foo: { foo: { foo: 'today' }}}), 'unknown word' ) ]); })); }); } @@ -373,35 +361,6 @@ describe('async schemas, formats and keywords', function() { }); -describe('async/transpile option', function() { - it('should throw error with unknown async option', function() { - shouldThrowFunc('bad async mode: es8', function() { - setupAsync(new Ajv({ async: 'es8' })); - }); - }); - - - it('should throw error with unknown transpile option', function() { - shouldThrowFunc('bad transpiler: babel', function() { - setupAsync(new Ajv({ transpile: 'babel' })); - }); - - shouldThrowFunc('bad transpiler: [object Object]', function() { - setupAsync(new Ajv({ transpile: {} })); - }); - }); - - - it('should set async option to es7 if tranpiler is nodent', function() { - var ajv1 = setupAsync(new Ajv({ transpile: 'nodent' })); - ajv1._opts.async .should.equal('es7'); - - var ajv2 = setupAsync(new Ajv({ async: '*', transpile: 'nodent' })); - ajv2._opts.async .should.equal('es7'); - }); -}); - - function checkWordOnServer(str) { return str == 'tomorrow' ? Promise.resolve(true) : str == 'manana' ? Promise.resolve(false) From b6d927980b7af7151501ac9faa29e8a2c691f6c4 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 5 Nov 2017 22:02:43 +0000 Subject: [PATCH 15/48] feat: draft-07 meta-schema --- README.md | 13 +- lib/ajv.js | 4 +- lib/patternGroups.js | 2 +- lib/refs/$data.json | 2 +- lib/refs/json-schema-draft-07.json | 167 ++++++++++++++++++ spec/ajv.spec.js | 4 +- spec/issues.spec.js | 14 +- spec/options.spec.js | 10 +- spec/resolve.spec.js | 4 +- .../issues/170_ref_and_id_in_sibling.json | 12 +- spec/tests/issues/27_recursive_reference.json | 2 +- .../issues/63_id_property_not_in_schema.json | 2 +- ...70_1_recursive_hash_ref_in_remote_ref.json | 2 +- 13 files changed, 203 insertions(+), 35 deletions(-) create mode 100644 lib/refs/json-schema-draft-07.json diff --git a/README.md b/README.md index cf91fbf2d..e9fcef801 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,15 @@ The fastest JSON Schema validator for Node.js and browser with draft 6 support. ## Using version 5 -[JSON Schema draft-06](https://trac.tools.ietf.org/html/draft-wright-json-schema-validation-01) is published. +[JSON Schema draft-07 WIP](http://json-schema.org/work-in-progress/WIP-jsonschema-validation.html) is published. -[Ajv version 5.0.0](https://github.com/epoberezkin/ajv/releases/tag/5.0.0) that supports draft-06 is released. It may require either migrating your schemas or updating your code (to continue using draft-04 and v5 schemas). +[Ajv version 6.0.0-beta.0](https://github.com/epoberezkin/ajv/releases/tag/6.0.0-beta.0) that supports draft-07 is released. It may require either migrating your schemas or updating your code (to continue using draft-04 and v5 schemas, draft-06 schemas will be supported without changes). -__Please note__: To use Ajv with draft-04 schemas you need to explicitly add meta-schema to the validator instance: +__Please note__: To use Ajv with draft-04 (or draft-06) schemas you need to explicitly add meta-schema to the validator instance: ```javascript ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); +// ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json')); ``` @@ -180,7 +181,7 @@ CLI is available as a separate npm package [ajv-cli](https://github.com/jessedc/ - compiling JSON-schemas to test their validity - BETA: generating standalone module exporting a validation function to be used without Ajv (using [ajv-pack](https://github.com/epoberezkin/ajv-pack)) -- migrate schemas to draft-06 (using [json-schema-migrate](https://github.com/epoberezkin/json-schema-migrate)) +- migrate schemas to draft-07 (using [json-schema-migrate](https://github.com/epoberezkin/json-schema-migrate)) - validating data file(s) against JSON-schema - testing expected validity of data against JSON-schema - referenced schemas @@ -871,7 +872,7 @@ By default the schema is validated against meta-schema before it is added, and i Adds meta schema(s) that can be used to validate other schemas. That function should be used instead of `addSchema` because there may be instance options that would compile a meta schema incorrectly (at the moment it is `removeAdditional` option). -There is no need to explicitly add draft 6 meta schema (http://json-schema.org/draft-06/schema and http://json-schema.org/schema) - it is added by default, unless option `meta` is set to `false`. You only need to use it if you have a changed meta-schema that you want to use to validate your schemas. See `validateSchema`. +There is no need to explicitly add draft-07 meta schema (http://json-schema.org/draft-07/schema) - it is added by default, unless option `meta` is set to `false`. You only need to use it if you have a changed meta-schema that you want to use to validate your schemas. See `validateSchema`. ##### .validateSchema(Object schema) -> Boolean @@ -1046,7 +1047,7 @@ Defaults: ##### Referenced schema options - _schemaId_: this option defines which keywords are used as schema URI. Option value: - - `"$id"` (recommended) - only use `$id` keyword as schema URI (as specified in JSON Schema draft-06), ignore `id` keyword (if it is present a warning will be logged). + - `"$id"` (recommended) - only use `$id` keyword as schema URI (as specified in JSON Schema draft-06/07), ignore `id` keyword (if it is present a warning will be logged). - `"id"` - only use `id` keyword as schema URI (as specified in JSON Schema draft-04), ignore `$id` keyword (if it is present a warning will be logged). - `undefined` (default) - use both `$id` and `id` keywords as schema URI. If both are present (in the same schema object) and different the exception will be thrown during schema compilation. - _missingRefs_: handling of missing referenced schemas. Option values: diff --git a/lib/ajv.js b/lib/ajv.js index c18ed4305..1827f71db 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -37,7 +37,7 @@ Ajv.ValidationError = errorClasses.Validation; Ajv.MissingRefError = errorClasses.MissingRef; Ajv.$dataMetaSchema = $dataMetaSchema; -var META_SCHEMA_ID = 'http://json-schema.org/draft-06/schema'; +var META_SCHEMA_ID = 'http://json-schema.org/draft-07/schema'; var META_IGNORE_OPTIONS = [ 'removeAdditional', 'useDefaults', 'coerceTypes' ]; var META_SUPPORT_DATA = ['/properties']; @@ -437,7 +437,7 @@ function addDraft6MetaSchema(self) { self.addMetaSchema($dataSchema, $dataSchema.$id, true); } if (self._opts.meta === false) return; - var metaSchema = require('./refs/json-schema-draft-06.json'); + var metaSchema = require('./refs/json-schema-draft-07.json'); if (self._opts.$data) metaSchema = $dataMetaSchema(metaSchema, META_SUPPORT_DATA); self.addMetaSchema(metaSchema, META_SCHEMA_ID, true); self._refs['http://json-schema.org/schema'] = META_SCHEMA_ID; diff --git a/lib/patternGroups.js b/lib/patternGroups.js index 531a8d004..79abc2a69 100644 --- a/lib/patternGroups.js +++ b/lib/patternGroups.js @@ -1,6 +1,6 @@ 'use strict'; -var META_SCHEMA_ID = 'http://json-schema.org/draft-06/schema'; +var META_SCHEMA_ID = 'http://json-schema.org/draft-07/schema'; module.exports = function (ajv) { var defaultMeta = ajv._opts.defaultMeta; diff --git a/lib/refs/$data.json b/lib/refs/$data.json index 4a2edec55..7bc6aca33 100644 --- a/lib/refs/$data.json +++ b/lib/refs/$data.json @@ -1,5 +1,5 @@ { - "$schema": "http://json-schema.org/draft-06/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/$data.json#", "description": "Meta-schema for $data reference (JSON-schema extension proposal)", "type": "object", diff --git a/lib/refs/json-schema-draft-07.json b/lib/refs/json-schema-draft-07.json new file mode 100644 index 000000000..554c5d010 --- /dev/null +++ b/lib/refs/json-schema-draft-07.json @@ -0,0 +1,167 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://json-schema.org/draft-07/schema#", + "title": "Core schema meta-schema", + "definitions": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "allOf": [ + { "$ref": "#/definitions/nonNegativeInteger" }, + { "default": 0 } + ] + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "default": [] + } + }, + "type": ["object", "boolean"], + "properties": { + "$id": { + "type": "string", + "format": "uri-reference" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "$comment": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": {}, + "readOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": {} + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { "$ref": "#/definitions/nonNegativeInteger" }, + "minLength": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "additionalItems": { "$ref": "#" }, + "items": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/schemaArray" } + ], + "default": {} + }, + "maxItems": { "$ref": "#/definitions/nonNegativeInteger" }, + "minItems": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "contains": { "$ref": "#" }, + "maxProperties": { "$ref": "#/definitions/nonNegativeInteger" }, + "minProperties": { "$ref": "#/definitions/nonNegativeIntegerDefault0" }, + "required": { "$ref": "#/definitions/stringArray" }, + "additionalProperties": { "$ref": "#" }, + "definitions": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "propertyNames": { "format": "regex" }, + "default": {} + }, + "dependencies": { + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/definitions/stringArray" } + ] + } + }, + "propertyNames": { "$ref": "#" }, + "const": {}, + "enum": { + "type": "array", + "minItems": 1, + "uniqueItems": true + }, + "type": { + "anyOf": [ + { "$ref": "#/definitions/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/definitions/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + }, + "format": { "type": "string" }, + "contentMediaType": { "type": "string" }, + "contentEncoding": { "type": "string" }, + "if": {"$ref": "#"}, + "then": {"$ref": "#"}, + "else": {"$ref": "#"}, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" } + }, + "default": {} +} diff --git a/spec/ajv.spec.js b/spec/ajv.spec.js index c580f3e97..ebee4259e 100644 --- a/spec/ajv.spec.js +++ b/spec/ajv.spec.js @@ -460,7 +460,7 @@ describe('Ajv', function () { describe('validateSchema method', function() { it('should validate schema against meta-schema', function() { var valid = ajv.validateSchema({ - $schema: 'http://json-schema.org/draft-06/schema#', + $schema: 'http://json-schema.org/draft-07/schema#', type: 'number' }); @@ -468,7 +468,7 @@ describe('Ajv', function () { should.equal(ajv.errors, null); valid = ajv.validateSchema({ - $schema: 'http://json-schema.org/draft-06/schema#', + $schema: 'http://json-schema.org/draft-07/schema#', type: 'wrong_type' }); diff --git a/spec/issues.spec.js b/spec/issues.spec.js index 45ec6e927..6b0d1b9bf 100644 --- a/spec/issues.spec.js +++ b/spec/issues.spec.js @@ -256,7 +256,7 @@ describe('issue #210, mutual recursive $refs that are schema fragments', functio describe('issue #240, mutually recursive fragment refs reference a common schema', function() { var apiSchema = { - $schema: 'http://json-schema.org/draft-06/schema#', + $schema: 'http://json-schema.org/draft-07/schema#', id: 'schema://api.schema#', resource: { id: '#resource', @@ -274,7 +274,7 @@ describe('issue #240, mutually recursive fragment refs reference a common schema }; var domainSchema = { - $schema: 'http://json-schema.org/draft-06/schema#', + $schema: 'http://json-schema.org/draft-07/schema#', id: 'schema://domain.schema#', properties: { data: { @@ -290,7 +290,7 @@ describe('issue #240, mutually recursive fragment refs reference a common schema var ajv = new Ajv; var librarySchema = { - $schema: 'http://json-schema.org/draft-06/schema#', + $schema: 'http://json-schema.org/draft-07/schema#', id: 'schema://library.schema#', properties: { name: { type: 'string' }, @@ -322,7 +322,7 @@ describe('issue #240, mutually recursive fragment refs reference a common schema }; var catalogItemSchema = { - $schema: 'http://json-schema.org/draft-06/schema#', + $schema: 'http://json-schema.org/draft-07/schema#', id: 'schema://catalog_item.schema#', properties: { name: { type: 'string' }, @@ -351,7 +351,7 @@ describe('issue #240, mutually recursive fragment refs reference a common schema }; var catalogItemResourceIdentifierSchema = { - $schema: 'http://json-schema.org/draft-06/schema#', + $schema: 'http://json-schema.org/draft-07/schema#', id: 'schema://catalog_item_resource_identifier.schema#', allOf: [ { @@ -381,7 +381,7 @@ describe('issue #240, mutually recursive fragment refs reference a common schema var ajv = new Ajv; var librarySchema = { - $schema: 'http://json-schema.org/draft-06/schema#', + $schema: 'http://json-schema.org/draft-07/schema#', id: 'schema://library.schema#', properties: { name: { type: 'string' }, @@ -413,7 +413,7 @@ describe('issue #240, mutually recursive fragment refs reference a common schema }; var catalogItemSchema = { - $schema: 'http://json-schema.org/draft-06/schema#', + $schema: 'http://json-schema.org/draft-07/schema#', id: 'schema://catalog_item.schema#', properties: { name: { type: 'string' }, diff --git a/spec/options.spec.js b/spec/options.spec.js index dc58f4267..ac7518000 100644 --- a/spec/options.spec.js +++ b/spec/options.spec.js @@ -278,7 +278,7 @@ describe('Ajv Options', function () { testOptionMeta(new Ajv({ meta: true })); function testOptionMeta(ajv) { - ajv.getSchema('http://json-schema.org/draft-06/schema') .should.be.a('function'); + ajv.getSchema('http://json-schema.org/draft-07/schema') .should.be.a('function'); ajv.validateSchema({ type: 'integer' }) .should.equal(true); ajv.validateSchema({ type: 123 }) .should.equal(false); should.not.throw(function() { ajv.addSchema({ type: 'integer' }); }); @@ -288,7 +288,7 @@ describe('Ajv Options', function () { it('should throw if meta: false and validateSchema: true', function() { var ajv = new Ajv({ meta: false }); - should.not.exist(ajv.getSchema('http://json-schema.org/draft-06/schema')); + should.not.exist(ajv.getSchema('http://json-schema.org/draft-07/schema')); should.not.throw(function() { ajv.addSchema({ type: 'wrong_type' }, 'integer'); }); }); @@ -327,7 +327,7 @@ describe('Ajv Options', function () { it('should use option meta as default meta schema', function() { var meta = { - $schema: 'http://json-schema.org/draft-06/schema', + $schema: 'http://json-schema.org/draft-07/schema', properties: { myKeyword: { type: 'boolean' } } @@ -336,7 +336,7 @@ describe('Ajv Options', function () { ajv.validateSchema({ myKeyword: true }) .should.equal(true); ajv.validateSchema({ myKeyword: 2 }) .should.equal(false); ajv.validateSchema({ - $schema: 'http://json-schema.org/draft-06/schema', + $schema: 'http://json-schema.org/draft-07/schema', myKeyword: 2 }) .should.equal(true); @@ -1117,7 +1117,7 @@ describe('Ajv Options', function () { }); - describe('patternGroups without draft-06 meta-schema', function() { + describe('patternGroups without draft-07 meta-schema', function() { it('should use default meta-schema', function() { var ajv = new Ajv({ patternGroups: true, diff --git a/spec/resolve.spec.js b/spec/resolve.spec.js index d1033d987..2db89565c 100644 --- a/spec/resolve.spec.js +++ b/spec/resolve.spec.js @@ -219,7 +219,7 @@ describe('resolve', function () { var ajv = new Ajv({ verbose: true }); var schemaMessage = { - $schema: "http://json-schema.org/draft-06/schema#", + $schema: "http://json-schema.org/draft-07/schema#", id: "http://e.com/message.json#", type: "object", required: ["header"], @@ -235,7 +235,7 @@ describe('resolve', function () { // header schema var schemaHeader = { - $schema: "http://json-schema.org/draft-06/schema#", + $schema: "http://json-schema.org/draft-07/schema#", id: "http://e.com/header.json#", type: "object", properties: { diff --git a/spec/tests/issues/170_ref_and_id_in_sibling.json b/spec/tests/issues/170_ref_and_id_in_sibling.json index eb4876850..6bfecd562 100644 --- a/spec/tests/issues/170_ref_and_id_in_sibling.json +++ b/spec/tests/issues/170_ref_and_id_in_sibling.json @@ -18,7 +18,7 @@ } }, { - "$schema": "http://json-schema.org/draft-06/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://example.com/base_object_2", "type": "object", "properties": { @@ -71,7 +71,7 @@ } }, { - "$schema": "http://json-schema.org/draft-06/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://example.com/base_array_2", "type": "array", "items": [ @@ -117,7 +117,7 @@ } }, { - "$schema": "http://json-schema.org/draft-06/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://example.com/base_anyof_2", "anyOf": [ { @@ -167,7 +167,7 @@ } }, { - "$schema": "http://json-schema.org/draft-06/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://example.com/base_oneof_2", "oneOf": [ { @@ -218,7 +218,7 @@ } }, { - "$schema": "http://json-schema.org/draft-06/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://example.com/base_allof_2", "allOf": [ { @@ -265,7 +265,7 @@ } }, { - "$schema": "http://json-schema.org/draft-06/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "$id": "http://example.com/base_dependencies_2", "type": "object", "dependencies": { diff --git a/spec/tests/issues/27_recursive_reference.json b/spec/tests/issues/27_recursive_reference.json index fb9a542cc..f2059cccb 100644 --- a/spec/tests/issues/27_recursive_reference.json +++ b/spec/tests/issues/27_recursive_reference.json @@ -30,7 +30,7 @@ } }, { - "$schema": "http://json-schema.org/draft-06/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "$id": "testrec_2", "type": "object", "properties": { diff --git a/spec/tests/issues/63_id_property_not_in_schema.json b/spec/tests/issues/63_id_property_not_in_schema.json index 8d9d36e63..371b18c25 100644 --- a/spec/tests/issues/63_id_property_not_in_schema.json +++ b/spec/tests/issues/63_id_property_not_in_schema.json @@ -11,7 +11,7 @@ { "type" : "object", "properties": { - "title": { "$ref": "http://json-schema.org/draft-06/schema#/properties/title" } + "title": { "$ref": "http://json-schema.org/draft-07/schema#/properties/title" } } } ], diff --git a/spec/tests/issues/70_1_recursive_hash_ref_in_remote_ref.json b/spec/tests/issues/70_1_recursive_hash_ref_in_remote_ref.json index 6f2c21166..8a53325f9 100644 --- a/spec/tests/issues/70_1_recursive_hash_ref_in_remote_ref.json +++ b/spec/tests/issues/70_1_recursive_hash_ref_in_remote_ref.json @@ -19,7 +19,7 @@ }, { "$id": "http://example.com/my_schema_2.json", - "$ref": "http://json-schema.org/draft-06/schema#/definitions/nonNegativeIntegerDefault0" + "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeIntegerDefault0" } ], "tests": [ From 9abe4e78a9904ff0fa2b97855bd1d3620fc42f78 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 5 Nov 2017 22:08:36 +0000 Subject: [PATCH 16/48] refactor: rename $data.js and $data.json without $, closes #526 --- lib/ajv.js | 4 ++-- lib/{$data.js => data.js} | 2 +- lib/keyword.js | 2 +- lib/refs/{$data.json => data.json} | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename lib/{$data.js => data.js} (96%) rename lib/refs/{$data.json => data.json} (94%) diff --git a/lib/ajv.js b/lib/ajv.js index 1827f71db..6eb97c6b6 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -7,7 +7,7 @@ var compileSchema = require('./compile') , stableStringify = require('fast-json-stable-stringify') , formats = require('./compile/formats') , rules = require('./compile/rules') - , $dataMetaSchema = require('./$data') + , $dataMetaSchema = require('./data') , patternGroups = require('./patternGroups') , util = require('./compile/util'); @@ -433,7 +433,7 @@ function addFormat(name, format) { function addDraft6MetaSchema(self) { var $dataSchema; if (self._opts.$data) { - $dataSchema = require('./refs/$data.json'); + $dataSchema = require('./refs/data.json'); self.addMetaSchema($dataSchema, $dataSchema.$id, true); } if (self._opts.meta === false) return; diff --git a/lib/$data.js b/lib/data.js similarity index 96% rename from lib/$data.js rename to lib/data.js index 60cfc2d8d..5f1ad85ef 100644 --- a/lib/$data.js +++ b/lib/data.js @@ -38,7 +38,7 @@ module.exports = function (metaSchema, keywordsJsonPointers) { keywords[key] = { anyOf: [ schema, - { $ref: 'https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/$data.json#' } + { $ref: 'https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/data.json#' } ] }; } diff --git a/lib/keyword.js b/lib/keyword.js index 85e64c600..341811a36 100644 --- a/lib/keyword.js +++ b/lib/keyword.js @@ -50,7 +50,7 @@ function addKeyword(keyword, definition) { metaSchema = { anyOf: [ metaSchema, - { '$ref': 'https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/$data.json#' } + { '$ref': 'https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/data.json#' } ] }; } diff --git a/lib/refs/$data.json b/lib/refs/data.json similarity index 94% rename from lib/refs/$data.json rename to lib/refs/data.json index 7bc6aca33..f17b4142d 100644 --- a/lib/refs/$data.json +++ b/lib/refs/data.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/$data.json#", + "$id": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/data.json#", "description": "Meta-schema for $data reference (JSON-schema extension proposal)", "type": "object", "required": [ "$data" ], From 1e6b1a829c179de569db8c1c1752c73486434a84 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 6 Nov 2017 07:38:49 +0000 Subject: [PATCH 17/48] 6.0.0-beta.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d57608f89..d3b10d3c6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "5.3.0", + "version": "6.0.0-beta.0", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From bc10289883b0b7473e59ccbba517b5fead32c3f7 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 6 Nov 2017 09:35:46 +0000 Subject: [PATCH 18/48] fix: added missing nodent --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index d3b10d3c6..43eaa1deb 100644 --- a/package.json +++ b/package.json @@ -88,6 +88,7 @@ "karma-phantomjs-launcher": "^1.0.0", "karma-sauce-launcher": "^1.1.0", "mocha": "^4.0.0", + "nodent": "^3.1.3", "nyc": "^11.0.2", "phantomjs-prebuilt": "^2.1.4", "pre-commit": "^1.1.1", From a90e574fb6b4bf473151784951ff350bf2f5471e Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 6 Nov 2017 09:37:08 +0000 Subject: [PATCH 19/48] 6.0.0-beta.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 43eaa1deb..6ac14f7af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.0.0-beta.0", + "version": "6.0.0-beta.1", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From 482c80c49c2b932f1882c1419e78e562542c6607 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 6 Nov 2017 15:54:46 +0000 Subject: [PATCH 20/48] docs: readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e9fcef801..9419fe0f1 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ The fastest JSON Schema validator for Node.js and browser with draft 6 support. [![Gitter](https://img.shields.io/gitter/room/ajv-validator/ajv.svg)](https://gitter.im/ajv-validator/ajv) -## Using version 5 +## Using version 6 [JSON Schema draft-07 WIP](http://json-schema.org/work-in-progress/WIP-jsonschema-validation.html) is published. From ceefaa2a050e20bdcd4624c3a69e36164f8b5661 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Wed, 8 Nov 2017 21:52:03 +0000 Subject: [PATCH 21/48] refactor: remove nodent, use ajv-async bundle instead --- README.md | 6 +++--- karma.conf.js | 2 +- karma.sauce.js | 2 +- package.json | 9 +++------ spec/ajv-async.js | 3 +++ spec/ajv_async_instances.js | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) create mode 100644 spec/ajv-async.js diff --git a/README.md b/README.md index 9419fe0f1..fb38aed4f 100644 --- a/README.md +++ b/README.md @@ -551,9 +551,7 @@ validate({ userId: 1, postId: 19 }) ### Using transpilers with asynchronous validation functions. -To use a transpiler you should separately install it (or load its bundle in the browser). - -Ajv npm package includes minified browser bundle of regenerator and nodent in dist folder. +[ajv-async](https://github.com/epoberezkin/ajv-async) uses [nodent](https://github.com/MatAtBread/nodent) to transpile async functions. To use another transpiler you should separately install it (or load its bundle in the browser). #### Using nodent @@ -561,6 +559,8 @@ Ajv npm package includes minified browser bundle of regenerator and nodent in di ```javascript var ajv = new Ajv; require('ajv-async')(ajv); +// in the browser if you want to load ajv-async bundle separately you can: +// window.ajvAsync(ajv); var validate = ajv.compile(schema); // transpiled es7 async function validate(data).then(successFunc).catch(errorFunc); ``` diff --git a/karma.conf.js b/karma.conf.js index 91cde5510..ab2d0defd 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -17,7 +17,7 @@ module.exports = function(config) { files: [ 'dist/ajv.min.js', 'node_modules/chai/chai.js', - 'dist/nodent.min.js', + 'node_modules/ajv-async/dist/ajv-async.min.js', 'node_modules/bluebird/js/browser/bluebird.core.min.js', '.browser/*.spec.js' ], diff --git a/karma.sauce.js b/karma.sauce.js index 1316ab0e5..18e85d6f6 100644 --- a/karma.sauce.js +++ b/karma.sauce.js @@ -112,7 +112,7 @@ module.exports = function(config) { files: [ 'dist/ajv.min.js', 'node_modules/chai/chai.js', - 'dist/nodent.min.js', + 'node_modules/ajv-async/dist/ajv-async.min.js', 'node_modules/bluebird/js/browser/bluebird.core.min.js', '.browser/*.spec.js' ], diff --git a/package.json b/package.json index 6ac14f7af..d0182fc8f 100644 --- a/package.json +++ b/package.json @@ -19,15 +19,13 @@ "test-debug": "mocha spec/*.spec.js --debug-brk -R spec", "test-cov": "nyc npm run test-spec", "test-ts": "tsc --target ES5 --noImplicitAny lib/ajv.d.ts", - "bundle": "node ./scripts/bundle.js . Ajv pure_getters", - "bundle-nodent": "node ./scripts/bundle.js nodent", - "bundle-all": "del-cli dist && npm run bundle && npm run bundle-nodent", + "bundle": "del-cli dist && node ./scripts/bundle.js . Ajv pure_getters", "bundle-beautify": "node ./scripts/bundle.js js-beautify", "build": "del-cli lib/dotjs/*.js '!lib/dotjs/index.js' && node scripts/compile-dots.js", "test-karma": "karma start --single-run --browsers PhantomJS", - "test-browser": "del-cli .browser && npm run bundle-all && scripts/prepare-tests && npm run test-karma", + "test-browser": "del-cli .browser && npm run bundle && scripts/prepare-tests && npm run test-karma", "test": "npm run jshint && npm run eslint && npm run test-ts && npm run build && npm run test-cov && if-node-version 4 npm run test-browser", - "prepublish": "npm run build && npm run bundle-all", + "prepublish": "npm run build && npm run bundle", "watch": "watch 'npm run build' ./lib/dot" }, "nyc": { @@ -88,7 +86,6 @@ "karma-phantomjs-launcher": "^1.0.0", "karma-sauce-launcher": "^1.1.0", "mocha": "^4.0.0", - "nodent": "^3.1.3", "nyc": "^11.0.2", "phantomjs-prebuilt": "^2.1.4", "pre-commit": "^1.1.1", diff --git a/spec/ajv-async.js b/spec/ajv-async.js new file mode 100644 index 000000000..d14125691 --- /dev/null +++ b/spec/ajv-async.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = typeof window == 'object' ? window.ajvAsync : require('' + 'ajv-async'); diff --git a/spec/ajv_async_instances.js b/spec/ajv_async_instances.js index 73fa36b52..8facd3637 100644 --- a/spec/ajv_async_instances.js +++ b/spec/ajv_async_instances.js @@ -2,7 +2,7 @@ var Ajv = require('./ajv') , util = require('../lib/compile/util') - , setupAsync = require('ajv-async'); + , setupAsync = require('./ajv-async'); module.exports = getAjvInstances; From 19c8a967b89cbfd7ae5e819c58993ccdea2012b6 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Thu, 9 Nov 2017 20:34:00 +0000 Subject: [PATCH 22/48] docs: add if/then/else, remove switch --- KEYWORDS.md | 72 ++++++++++++++++++++--------------------------------- 1 file changed, 27 insertions(+), 45 deletions(-) diff --git a/KEYWORDS.md b/KEYWORDS.md index c53e15e95..b8554166c 100644 --- a/KEYWORDS.md +++ b/KEYWORDS.md @@ -10,7 +10,7 @@ The keywords and their values define what rules the data should satisfy to be va - [type](#type) - [Keywords for numbers](#keywords-for-numbers) - - [maximum / minimum and exclusiveMaximum / exclusiveMinimum](#maximum--minimum-and-exclusivemaximum--exclusiveminimum) (CHANGED in draft 6) + - [maximum / minimum and exclusiveMaximum / exclusiveMinimum](#maximum--minimum-and-exclusivemaximum--exclusiveminimum) (CHANGED in draft-06) - [multipleOf](#multipleof) - [Keywords for strings](#keywords-for-strings) - [maxLength/minLength](#maxlength--minlength) @@ -22,7 +22,7 @@ The keywords and their values define what rules the data should satisfy to be va - [uniqueItems](#uniqueitems) - [items](#items) - [additionalItems](#additionalitems) - - [contains](#contains) (NEW in draft 6) + - [contains](#contains) (added in draft-06) - [Keywords for objects](#keywords-for-objects) - [maxProperties/minProperties](#maxproperties--minproperties) - [required](#required) @@ -30,18 +30,18 @@ The keywords and their values define what rules the data should satisfy to be va - [patternProperties](#patternproperties) - [additionalProperties](#additionalproperties) - [dependencies](#dependencies) - - [propertyNames](#propertynames) (NEW in draft 6) + - [propertyNames](#propertynames) (added in draft-06) - [patternGroups](#patterngroups-deprecated) (deprecated) - [patternRequired](#patternrequired-proposed) (proposed) - [Keywords for all types](#keywords-for-all-types) - [enum](#enum) - - [const](#const) (NEW in draft 6) + - [const](#const) (added in draft-06) - [Compound keywords](#compound-keywords) - [not](#not) - [oneOf](#oneof) - [anyOf](#anyof) - [allOf](#allof) - - [switch](#switch-proposed) (proposed) + - [if/then/else](#ifthenelse) (NEW in draft-07) @@ -830,29 +830,15 @@ _invalid_: `1.5`, `2.5`, `4`, `4.5`, `5`, `5.5`, any non-number -### `switch` (proposed) +### `if`/`then`/`else` -Defined in [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) package. - -The value of the keyword is the array of if/then clauses. Each clause is the object with the following properties: +These keywords allow to implement conditional validation. Their values should be valid JSON-schemas (object or boolean). -- `if` (optional) - the value is JSON-schema -- `then` (required) - the value is JSON-schema or boolean -- `continue` (optional) - the value is boolean +If `if` keyword is absent, the validation succeds. -The validation process is dynamic; all clauses are executed sequentially in the following way: +If the data is valid against the sub-schema in `if` keyword, then the validation result is equal to the result of data validation against the sub-schema in `then` keyword (if `then` is absent, the validation succeeds). -1. `if`: - 1. `if` property is JSON-schema according to which the data is: - 1. valid => go to step 2. - 2. invalid => go to the NEXT clause, if this was the last clause the validation of `switch` SUCCEEDS. - 2. `if` property is absent => go to step 2. -2. `then`: - 1. `then` property is `true` or it is JSON-schema according to which the data is valid => go to step 3. - 2. `then` property is `false` or it is JSON-schema according to which the data is invalid => the validation of `switch` FAILS. -3. `continue`: - 1. `continue` property is `true` => go to the NEXT clause, if this was the last clause the validation of `switch` SUCCEEDS. - 2. `continue` property is `false` or absent => validation of `switch` SUCCEEDS. +If the data is invalid against the sub-schema in `if` keyword, then the validation result is equal to the result of data validation against the sub-schema in `else` keyword (if `else` is absent, the validation succeeds). __Examples__ @@ -861,29 +847,24 @@ __Examples__ ```json { - "switch": [ - { - "if": { "properties": { "power": { "minimum": 9000 } } }, - "then": { "required": [ "disbelief" ] }, - "continue": true - }, - { "then": { "required": [ "confidence" ] } } - ] + "if": { "properties": { "power": { "minimum": 9000 } } }, + "then": { "required": [ "disbelief" ] }, + "else": { "required": [ "confidence" ] } } ``` _valid_: - - `{ "power": 9000, "disbelief": true, "confidence": true }` - - `{ "confidence": true }` + - `{ "power": 10000, "disbelief": true }` + - `{}` - `{ "power": 1000, "confidence": true }` + - any non-object _invalid_: - - `{ "power": 9000 }` (`disbelief` & `confidence` are required) - - `{ "power": 9000, "disbelief": true }` (`confidence` is always required) - - `{ "power": 1000 }` - - `{}` + - `{ "power": 10000 }` (`disbelief` is required) + - `{ "power": 10000, "confidence": true }` (`disbelief` is required) + - `{ "power": 1000 }` (`confidence` is required) 2. _schema_: @@ -891,13 +872,14 @@ __Examples__ ```json { "type": "integer", - "switch": [ - { "if": { "not": { "minimum": 1 } }, "then": false }, - { "if": { "maximum": 10 }, "then": true }, - { "if": { "maximum": 100 }, "then": { "multipleOf": 10 } }, - { "if": { "maximum": 1000 }, "then": { "multipleOf": 100 } }, - { "then": false } - ] + "minimum": 1, + "maximum": 1000, + "if": { "minimum": 100 }, + "then": { "multipleOf": 100 }, + "else": { + "if": { "minimum": 10 }, + "then": { "multipleOf": 10 } + } } ``` From d97543f327b61ff72b0fea1848cd012d2c96d3d8 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Thu, 9 Nov 2017 20:37:31 +0000 Subject: [PATCH 23/48] docs: draft-07 --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fb38aed4f..a55aa5313 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # Ajv: Another JSON Schema Validator -The fastest JSON Schema validator for Node.js and browser with draft 6 support. +The fastest JSON Schema validator for Node.js and browser with draft-07 support. [![Build Status](https://travis-ci.org/epoberezkin/ajv.svg?branch=master)](https://travis-ci.org/epoberezkin/ajv) @@ -20,11 +20,11 @@ The fastest JSON Schema validator for Node.js and browser with draft 6 support. [Ajv version 6.0.0-beta.0](https://github.com/epoberezkin/ajv/releases/tag/6.0.0-beta.0) that supports draft-07 is released. It may require either migrating your schemas or updating your code (to continue using draft-04 and v5 schemas, draft-06 schemas will be supported without changes). -__Please note__: To use Ajv with draft-04 (or draft-06) schemas you need to explicitly add meta-schema to the validator instance: +__Please note__: To use Ajv with draft-06 (or draft-04) schemas you need to explicitly add the meta-schema(s) to the validator instance: ```javascript -ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); -// ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json')); +ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json')); +// ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); ``` From dd884fe93d409a345369ddb84f914536d73f6ac4 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 10 Nov 2017 21:41:27 +0000 Subject: [PATCH 24/48] feat: option $comment, closes #609 --- README.md | 5 ++ lib/compile/rules.js | 12 +++-- lib/dot/comment.jst | 9 ++++ lib/dot/validate.jst | 4 ++ lib/dotjs/index.js | 1 + spec/.eslintrc.yml | 1 + spec/options.spec.js | 88 +++++++++++++++++++++++++++++++++++ spec/tests/rules/comment.json | 40 ++++++++++++++++ 8 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 lib/dot/comment.jst create mode 100644 spec/tests/rules/comment.json diff --git a/README.md b/README.md index a55aa5313..296ddc62c 100644 --- a/README.md +++ b/README.md @@ -991,6 +991,7 @@ Defaults: $data: false, allErrors: false, verbose: false, + $comment: false, // NEW in Ajv version 6.0 jsonPointers: false, uniqueItems: true, unicode: true, @@ -1032,6 +1033,10 @@ Defaults: - _$data_: support [$data references](#data-reference). Draft 6 meta-schema that is added by default will be extended to allow them. If you want to use another meta-schema you need to use $dataMetaSchema method to add support for $data reference. See [API](#api). - _allErrors_: check all rules collecting all errors. Default is to return after the first error. - _verbose_: include the reference to the part of the schema (`schema` and `parentSchema`) and validated data in errors (false by default). +- _$comment_ (NEW in Ajv version 6.0): log or pass the value of `$comment` keyword to a function. Option values: + - `false` (default): ignore $comment keyword. + - `true`: log the keyword value to console. + - function: pass the keyword value, its schema path and root schema to the specified function - _jsonPointers_: set `dataPath` property of errors using [JSON Pointers](https://tools.ietf.org/html/rfc6901) instead of JavaScript property access notation. - _uniqueItems_: validate `uniqueItems` keyword (true by default). - _unicode_: calculate correct length of strings with unicode pairs (true by default). Pass `false` to use `.length` of strings that is faster, but gives "incorrect" lengths of strings with unicode pairs - each unicode pair is counted as two characters. diff --git a/lib/compile/rules.js b/lib/compile/rules.js index 0f53db793..505088282 100644 --- a/lib/compile/rules.js +++ b/lib/compile/rules.js @@ -18,10 +18,11 @@ module.exports = function rules() { { rules: [ '$ref', 'const', 'enum', 'not', 'anyOf', 'oneOf', 'allOf', 'if' ] } ]; - var ALL = [ 'type' ]; + var ALL = [ 'type', '$comment' ]; var KEYWORDS = [ - '$schema', '$id', 'id', '$comment', - 'title', 'description', 'default', 'definitions', + '$schema', '$id', 'id', + 'title', 'description', + 'default', 'definitions', 'additionalItems', 'then', 'else' ]; var TYPES = [ 'number', 'integer', 'string', 'array', 'object', 'boolean', 'null' ]; @@ -49,6 +50,11 @@ module.exports = function rules() { return rule; }); + RULES.all.$comment = { + keyword: '$comment', + code: ruleModules.$comment + }; + if (group.type) RULES.types[group.type] = group; }); diff --git a/lib/dot/comment.jst b/lib/dot/comment.jst new file mode 100644 index 000000000..f95915035 --- /dev/null +++ b/lib/dot/comment.jst @@ -0,0 +1,9 @@ +{{# def.definitions }} +{{# def.setupKeyword }} + +{{ var $comment = it.util.toQuotedString($schema); }} +{{? it.opts.$comment === true }} + console.log({{=$comment}}); +{{?? typeof it.opts.$comment == 'function' }} + self._opts.$comment({{=$comment}}, {{=it.util.toQuotedString($errSchemaPath)}}, validate.root.schema); +{{?}} diff --git a/lib/dot/validate.jst b/lib/dot/validate.jst index 3a508c626..43a5edc1e 100644 --- a/lib/dot/validate.jst +++ b/lib/dot/validate.jst @@ -127,6 +127,10 @@ {{?}} {{?}} +{{? it.schema.$comment && it.opts.$comment }} + {{= it.RULES.all.$comment.code(it, '$comment') }} +{{?}} + {{? $typeSchema }} {{? it.opts.coerceTypes }} {{ var $coerceToTypes = it.util.coerceToTypes(it.opts.coerceTypes, $typeSchema); }} diff --git a/lib/dotjs/index.js b/lib/dotjs/index.js index b5531ed8f..2fb1b00ef 100644 --- a/lib/dotjs/index.js +++ b/lib/dotjs/index.js @@ -5,6 +5,7 @@ module.exports = { '$ref': require('./ref'), allOf: require('./allOf'), anyOf: require('./anyOf'), + '$comment': require('./comment'), const: require('./const'), contains: require('./contains'), dependencies: require('./dependencies'), diff --git a/spec/.eslintrc.yml b/spec/.eslintrc.yml index d2d4eda16..f9c66d538 100644 --- a/spec/.eslintrc.yml +++ b/spec/.eslintrc.yml @@ -8,3 +8,4 @@ globals: it: false before: false beforeEach: false + afterEach: false diff --git a/spec/options.spec.js b/spec/options.spec.js index ac7518000..220516e7f 100644 --- a/spec/options.spec.js +++ b/spec/options.spec.js @@ -1219,4 +1219,92 @@ describe('Ajv Options', function () { }); }); }); + + + describe('$comment', function() { + describe('= true', function() { + var logCalls, consoleLog; + + beforeEach(function () { + consoleLog = console.log; + console.log = log; + }); + + afterEach(function () { + console.log = consoleLog; + }); + + function log() { + logCalls.push(Array.prototype.slice.call(arguments)); + } + + it('should log the text from $comment keyword', function() { + var schema = { + properties: { + foo: {$comment: 'property foo'}, + bar: {$comment: 'property bar', type: 'integer'} + } + }; + + var ajv = new Ajv({$comment: true}); + var fullAjv = new Ajv({allErrors: true, $comment: true}); + + [ajv, fullAjv].forEach(function (_ajv) { + var validate = _ajv.compile(schema); + + test({}, true, []); + test({foo: 1}, true, [['property foo']]); + test({foo: 1, bar: 2}, true, [['property foo'], ['property bar']]); + test({foo: 1, bar: 'baz'}, false, [['property foo'], ['property bar']]); + + function test(data, valid, expectedLogCalls) { + logCalls = []; + validate(data) .should.equal(valid); + logCalls .should.eql(expectedLogCalls); + } + }); + + console.log = consoleLog; + }); + }); + + describe('function hook', function() { + var hookCalls; + + function hook() { + hookCalls.push(Array.prototype.slice.call(arguments)); + } + + it('should pass the text from $comment keyword to the hook', function() { + var schema = { + properties: { + foo: {$comment: 'property foo'}, + bar: {$comment: 'property bar', type: 'integer'} + } + }; + + var ajv = new Ajv({$comment: hook}); + var fullAjv = new Ajv({allErrors: true, $comment: hook}); + + [ajv, fullAjv].forEach(function (_ajv) { + var validate = _ajv.compile(schema); + + test({}, true, []); + test({foo: 1}, true, [['property foo', '#/properties/foo/$comment', schema]]); + test({foo: 1, bar: 2}, true, + [['property foo', '#/properties/foo/$comment', schema], + ['property bar', '#/properties/bar/$comment', schema]]); + test({foo: 1, bar: 'baz'}, false, + [['property foo', '#/properties/foo/$comment', schema], + ['property bar', '#/properties/bar/$comment', schema]]); + + function test(data, valid, expectedHookCalls) { + hookCalls = []; + validate(data) .should.equal(valid); + hookCalls .should.eql(expectedHookCalls); + } + }); + }); + }); + }); }); diff --git a/spec/tests/rules/comment.json b/spec/tests/rules/comment.json new file mode 100644 index 000000000..5be030e4f --- /dev/null +++ b/spec/tests/rules/comment.json @@ -0,0 +1,40 @@ +[ + { + "description": "$comment keyword", + "schema": { + "$comment": "test" + }, + "tests": [ + { + "description": "any value is valid", + "data": 1, + "valid": true + } + ] + }, + { + "description": "$comment keyword in subschemas", + "schema": { + "type": "object", + "properties": { + "foo": { + "$comment": "test" + } + } + }, + "tests": [ + { + "description": "empty object is valid", + "data": {}, + "valid": true + }, + { + "description": "any value of property foo is valid object is valid", + "data": { + "foo": 1 + }, + "valid": true + } + ] + } +] From 9ce4d2351f4e2bc246ea5888e612c518dcce9304 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 12 Nov 2017 08:57:13 +0000 Subject: [PATCH 25/48] =?UTF-8?q?feat:=20reserve=20annotation=20keywords,?= =?UTF-8?q?=20closes=C2=A0#619?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 16 ++++++++++++++++ lib/compile/rules.js | 7 ++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 296ddc62c..7b7ddfab9 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json')); - [Command line interface](#command-line-interface) - Validation - [Keywords](#validation-keywords) + - [Annotation keywords](#annotation-keywords) - [Formats](#formats) - [Combining schemas with $ref](#ref) - [$data reference](#data-reference) @@ -212,6 +213,21 @@ With [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) package Ajv als See [JSON Schema validation keywords](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md) for more details. +## Annotation keywords + +JSON Schema specification defines several annotation keywords that describe schema itself but do not perform any validation. + +- `title` and `description`: information about the data represented by that schema +- `$comment` (NEW in draft-07): information for developers. With option `$comment` Ajv logs or passes the comment string to the user-supplied function. See [Options](#options). +- `default`: a default value of the data instance, see [Assigning defaults](#assigning-defaults). +- `examples` (NEW in draft-07): an array of data instances. Ajv does not check the validity of these instances against the schema. +- `readOnly` and `writeOnly` (NEW in draft-07): marks data-instance as read-only or write-only in relation to the source of the data (database, api, etc.). +- `contentEncoding`: [RFC 2045](https://tools.ietf.org/html/rfc2045#section-6.1 ), e.g., "base64". +- `contentMediaType`: [RFC 2046](https://tools.ietf.org/html/rfc2046), e.g., "image/png". + +__Please note__: Ajv does not implement validation of the keywords `examples`, `contentEncoding` and `contentMediaType` but it reserves them. If you want to create a plugin that implements some of them, it should remove these kewords from the instance. + + ## Formats The following formats are supported for string validation with "format" keyword: diff --git a/lib/compile/rules.js b/lib/compile/rules.js index 505088282..66f196a93 100644 --- a/lib/compile/rules.js +++ b/lib/compile/rules.js @@ -20,9 +20,10 @@ module.exports = function rules() { var ALL = [ 'type', '$comment' ]; var KEYWORDS = [ - '$schema', '$id', 'id', - 'title', 'description', - 'default', 'definitions', + '$schema', '$id', 'id', '$data', 'title', + 'description', 'default', 'definitions', + 'examples', 'readOnly', 'writeOnly', + 'contentMediaType', 'contentEncoding', 'additionalItems', 'then', 'else' ]; var TYPES = [ 'number', 'integer', 'string', 'array', 'object', 'boolean', 'null' ]; From b816ed654867fbf3c835cff8d34ffef51dcde134 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 12 Nov 2017 09:25:37 +0000 Subject: [PATCH 26/48] docs: note on formats iri etc., closes #588 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7b7ddfab9..cfa8bbd83 100644 --- a/README.md +++ b/README.md @@ -247,6 +247,8 @@ The following formats are supported for string validation with "format" keyword: - _json-pointer_: JSON-pointer according to [RFC6901](https://tools.ietf.org/html/rfc6901). - _relative-json-pointer_: relative JSON-pointer according to [this draft](http://tools.ietf.org/html/draft-luff-relative-json-pointer-00). +__Please note__: JSON Schema draft-07 also defines formats `iri`, `iri-reference`, `idn-hostname` and `idn-email` for URLs, hostnames and emails with international characters. Ajv does not implement these formats. If you create Ajv plugin that implements them please make a PR here to mention this plugin. + There are two modes of format validation: `fast` and `full`. This mode affects formats `date`, `time`, `date-time`, `uri`, `email`, and `hostname`. See [Options](#options) for details. You can add additional formats and replace any of the formats above using [addFormat](#api-addformat) method. From 9a0bf759cfdebd3f0d06533fa35d70b51b5a666a Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 12 Nov 2017 09:58:46 +0000 Subject: [PATCH 27/48] 6.0.0-beta.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d0182fc8f..b0e049c1b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.0.0-beta.1", + "version": "6.0.0-beta.2", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From 6fc873e60cfbf550c09e900ca315cb51b97aa63a Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 12 Nov 2017 10:10:13 +0000 Subject: [PATCH 28/48] docs: add if/then/else, remove switch --- KEYWORDS.md | 2 +- README.md | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/KEYWORDS.md b/KEYWORDS.md index b8554166c..b861d2345 100644 --- a/KEYWORDS.md +++ b/KEYWORDS.md @@ -10,7 +10,7 @@ The keywords and their values define what rules the data should satisfy to be va - [type](#type) - [Keywords for numbers](#keywords-for-numbers) - - [maximum / minimum and exclusiveMaximum / exclusiveMinimum](#maximum--minimum-and-exclusivemaximum--exclusiveminimum) (CHANGED in draft-06) + - [maximum / minimum and exclusiveMaximum / exclusiveMinimum](#maximum--minimum-and-exclusivemaximum--exclusiveminimum) (changed in draft-06) - [multipleOf](#multipleof) - [Keywords for strings](#keywords-for-strings) - [maxLength/minLength](#maxlength--minlength) diff --git a/README.md b/README.md index cfa8bbd83..9bdfbf5f5 100644 --- a/README.md +++ b/README.md @@ -202,11 +202,10 @@ Ajv supports all validation keywords from draft 4 of JSON-schema standard: - [for arrays](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#keywords-for-arrays) - maxItems, minItems, uniqueItems, items, additionalItems, [contains](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#contains) - [for objects](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#keywords-for-objects) - maxProperties, minProperties, required, properties, patternProperties, additionalProperties, dependencies, [propertyNames](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#propertynames) - [for all types](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#keywords-for-all-types) - enum, [const](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#const) -- [compound keywords](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#compound-keywords) - not, oneOf, anyOf, allOf +- [compound keywords](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#compound-keywords) - not, oneOf, anyOf, allOf, [if/then/else](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#ifthenelse) With [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) package Ajv also supports validation keywords from [JSON Schema extension proposals](https://github.com/json-schema/json-schema/wiki/v5-Proposals) for JSON-schema standard: -- [switch](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#switch-proposed) - conditional validation with a sequence of if/then clauses - [patternRequired](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#patternrequired-proposed) - like `required` but with patterns that some property should match. - [formatMaximum, formatMinimum, formatExclusiveMaximum, formatExclusiveMinimum](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#formatmaximum--formatminimum-and-exclusiveformatmaximum--exclusiveformatminimum-proposed) - setting limits for date, time, etc. From b68b269bb457e162cdb61cc9f0b3512ca0f350f2 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Mon, 20 Nov 2017 21:04:45 +0000 Subject: [PATCH 29/48] test: update browsers in saucelabs tests --- karma.sauce.js | 48 ++++++++++++------------------------------------ 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/karma.sauce.js b/karma.sauce.js index 18e85d6f6..fac172e36 100644 --- a/karma.sauce.js +++ b/karma.sauce.js @@ -22,29 +22,18 @@ module.exports = function(config) { browserName: 'chrome', version: '27' }, - // 'SL_Chrome_37': { - // base: 'SauceLabs', - // browserName: 'chrome', - // version: '37' - // }, 'SL_Chrome': { base: 'SauceLabs', browserName: 'chrome' }, - 'SL_InternetExplorer_9': { - base: 'SauceLabs', - browserName: 'internet explorer', - version: '9' - }, 'SL_InternetExplorer_10': { base: 'SauceLabs', browserName: 'internet explorer', version: '10' }, - 'SL_InternetExplorer_11': { + 'SL_InternetExplorer': { base: 'SauceLabs', - browserName: 'internet explorer', - version: '11' // default + browserName: 'internet explorer' }, 'SL_MicrosoftEdge': { base: 'SauceLabs', @@ -55,45 +44,32 @@ module.exports = function(config) { browserName: 'firefox', version: '17' }, - // 'SL_FireFox_24': { - // base: 'SauceLabs', - // browserName: 'firefox', - // version: '24' - // }, 'SL_FireFox': { base: 'SauceLabs', browserName: 'firefox' }, - 'SL_Safari_5': { + 'SL_Safari_7': { base: 'SauceLabs', browserName: 'safari', - version: '5' // default + version: '7' }, - // 'SL_Safari_7': { - // base: 'SauceLabs', - // browserName: 'safari', - // version: '7' - // }, - 'SL_Safari_9': { + 'SL_Safari': { base: 'SauceLabs', - browserName: 'safari', - version: '9' + browserName: 'safari' }, 'SL_iPhone_8': { base: 'SauceLabs', browserName: 'iphone', version: '8.4' }, - 'SL_iPhone_9': { + 'SL_iPhone': { base: 'SauceLabs', - browserName: 'iphone', - version: '9.2' + browserName: 'iphone' + }, + 'SL_Android': { + base: 'SauceLabs', + browserName: 'android' } - // 'SL_Android_4': { - // base: 'SauceLabs', - // browserName: 'android', - // version: '4' - // } }; From 05e2b38d67b3771631b5c4229bad77eaa641e209 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Fri, 24 Nov 2017 22:27:56 +0000 Subject: [PATCH 30/48] fix: "default" should work inside "then" and "else", closes #635 --- lib/dot/if.jst | 3 +-- spec/errors.spec.js | 56 ++++++++++++++++++++++++-------------------- spec/options.spec.js | 31 ++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 28 deletions(-) diff --git a/lib/dot/if.jst b/lib/dot/if.jst index ba20b220f..7ccc9b7f7 100644 --- a/lib/dot/if.jst +++ b/lib/dot/if.jst @@ -43,6 +43,7 @@ {{# def.insertSubschemaCode }} {{ $it.createErrors = true; }} {{# def.resetErrors }} + {{# def.resetCompositeRule }} {{? $thenPresent }} if ({{=$nextValid}}) { @@ -60,8 +61,6 @@ } {{?}} - {{# def.resetCompositeRule }} - if (!{{=$valid}}) { {{# def.extraError:'if' }} } diff --git a/spec/errors.spec.js b/spec/errors.spec.js index 384cbcef5..2c2ee4dc5 100644 --- a/spec/errors.spec.js +++ b/spec/errors.spec.js @@ -708,6 +708,8 @@ describe('Validation errors', function () { describe('if/then/else errors', function() { + var validate, numErrors; + it('if/then/else should include failing keyword in message and params', function() { var schema = { 'if': { maximum: 10 }, @@ -716,20 +718,15 @@ describe('Validation errors', function () { }; [ajv, fullAjv].forEach(function (_ajv) { - var validate = _ajv.compile(schema); + prepareTest(_ajv, schema); shouldBeValid(validate, 8); shouldBeValid(validate, 15); - shouldBeInvalid(validate, 7, 2); - testError('should match "then" schema', {failingKeyword: 'then'}); + shouldBeInvalid(validate, 7, numErrors); + testIfError('then', 2); - shouldBeInvalid(validate, 17, 2); - testError('should match "else" schema', {failingKeyword: 'else'}); - - function testError(message, params) { - var err = validate.errors[1]; - shouldBeError(err, 'if', '#/if', '', message, params); - } + shouldBeInvalid(validate, 17, numErrors); + testIfError('else', 5); }); }); @@ -740,18 +737,13 @@ describe('Validation errors', function () { }; [ajv, fullAjv].forEach(function (_ajv) { - var validate = _ajv.compile(schema); + prepareTest(_ajv, schema); shouldBeValid(validate, 8); shouldBeValid(validate, 11); shouldBeValid(validate, 12); - shouldBeInvalid(validate, 7, 2); - testError('should match "then" schema', {failingKeyword: 'then'}); - - function testError(message, params) { - var err = validate.errors[1]; - shouldBeError(err, 'if', '#/if', '', message, params); - } + shouldBeInvalid(validate, 7, numErrors); + testIfError('then', 2); }); }); @@ -762,20 +754,32 @@ describe('Validation errors', function () { }; [ajv, fullAjv].forEach(function (_ajv) { - var validate = _ajv.compile(schema); + prepareTest(_ajv, schema); shouldBeValid(validate, 7); shouldBeValid(validate, 8); shouldBeValid(validate, 15); - shouldBeInvalid(validate, 17, 2); - testError('should match "else" schema', {failingKeyword: 'else'}); - - function testError(message, params) { - var err = validate.errors[1]; - shouldBeError(err, 'if', '#/if', '', message, params); - } + shouldBeInvalid(validate, 17, numErrors); + testIfError('else', 5); }); }); + + function prepareTest(_ajv, schema) { + validate = _ajv.compile(schema); + numErrors = _ajv._opts.allErrors ? 2 : 1; + } + + function testIfError(ifClause, multipleOf) { + var err = validate.errors[0]; + shouldBeError(err, 'multipleOf', '#/' + ifClause + '/multipleOf', '', + 'should be multiple of ' + multipleOf, {multipleOf: multipleOf}); + + if (numErrors == 2) { + err = validate.errors[1]; + shouldBeError(err, 'if', '#/if', '', + 'should match "' + ifClause + '" schema', {failingKeyword: ifClause}); + } + } }); diff --git a/spec/options.spec.js b/spec/options.spec.js index 55069ffb9..84a5d64f5 100644 --- a/spec/options.spec.js +++ b/spec/options.spec.js @@ -593,6 +593,37 @@ describe('Ajv Options', function () { } }); + it('should apply default in "then" subschema (issue #635)', function() { + test(new Ajv({ useDefaults: true })); + test(new Ajv({ useDefaults: true, allErrors: true })); + + function test(ajv) { + var schema = { + if: { required: ['foo'] }, + then: { + properties: { + bar: { default: 2 } + } + }, + else: { + properties: { + foo: { default: 1 } + } + } + }; + + var validate = ajv.compile(schema); + + var data = {}; + validate(data) .should.equal(true); + data .should.eql({foo: 1}); + + data = {foo: 1}; + validate(data) .should.equal(true); + data .should.eql({foo: 1, bar: 2}); + } + }); + describe('useDefaults: by value / by reference', function() { describe('using by value', function() { From 4687ed3465faf881b1e9b4308014646c3d10acd0 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 25 Nov 2017 20:23:52 +0000 Subject: [PATCH 31/48] docs: corrections --- CONTRIBUTING.md | 10 ++--- CUSTOM.md | 6 +-- FAQ.md | 6 +-- KEYWORDS.md | 38 +++++++++---------- LICENSE | 2 +- README.md | 34 ++++++++--------- lib/refs/data.json | 2 +- .../issues/27_1_recursive_raml_schema.json | 4 +- 8 files changed, 50 insertions(+), 52 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ff77bc0bf..dd78a8a1e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,7 +7,7 @@ Thank you for your help making Ajv better! Every contribution is appreciated. If - [Bug reports](#bug-reports) - [Change proposals](#changes) - [Browser and compatibility issues](#compatibility) - - [JSON schema standard](#json-schema) + - [JSON Schema standard](#json-schema) - [Ajv usage questions](#usage) - [Code](#code) - [Development](#development) @@ -33,7 +33,7 @@ Please make sure to include the following information in the issue: 1. What version of Ajv are you using? Does the issue happen if you use the latest version? 2. Ajv options object (see https://github.com/epoberezkin/ajv#options). -3. JSON schema and the data you are validating (please make it as small as possible to reproduce the issue). +3. JSON Schema and the data you are validating (please make it as small as possible to reproduce the issue). 4. Your code (please use `options`, `schema` and `data` as variables). 5. Validation result, data AFTER validation, error messages. 6. What results did you expect? @@ -75,11 +75,9 @@ Please include this information: 6. Results and error messages in your platform. -#### Using JSON schema standard +#### Using JSON Schema standard -Ajv implements JSON schema standard draft 4 and the proposed extensions for the next version of the standard (available when you use the option `v5: true`). - -If the issue is related to using v5 extensions please submit it as a [bug report](https://github.com/epoberezkin/ajv/issues/new). +Ajv implements JSON Schema standard draft-04 and draft-06/07. If it is a general issue related to using the standard keywords included in JSON Schema or implementing some advanced validation logic please ask the question on [Stack Overflow](http://stackoverflow.com/questions/ask?tags=jsonschema,ajv) (my account is [esp](http://stackoverflow.com/users/1816503/esp)) or submitting the question to [JSON-Schema.org](https://github.com/json-schema-org/json-schema-spec/issues/new). Please mention @epoberezkin. diff --git a/CUSTOM.md b/CUSTOM.md index f487ee4f2..6a5cbe8f1 100644 --- a/CUSTOM.md +++ b/CUSTOM.md @@ -39,7 +39,7 @@ This way to define keywords is useful for: __Please note__: In cases when validation flow is different depending on the schema and you have to use `if`s, this way to define keywords will have worse performance than compiled keyword returning different validation functions depending on the schema. -Example. `constant` keyword (a synonym for draft6 keyword `const`, it is equivalent to `enum` keyword with one item): +Example. `constant` keyword (a synonym for draft-06 keyword `const`, it is equivalent to `enum` keyword with one item): ```javascript ajv.addKeyword('constant', { @@ -88,7 +88,7 @@ The access to the parent data object and the current property name allow to crea The function should return validation result as boolean. It can return an array of validation errors via `.errors` property of itself (otherwise a standard error will be used). -In some cases it is the best approach to define keywords, but it has the performance cost of an extra function call during validation. If keyword logic can be expressed via some other JSON-schema then `macro` keyword definition is more efficient (see below). +In some cases it is the best approach to define keywords, but it has the performance cost of an extra function call during validation. If keyword logic can be expressed via some other JSON Schema then `macro` keyword definition is more efficient (see below). All custom keywords types can have an optional `metaSchema` property in their definitions. It is a schema against which the value of keyword will be validated during schema compilation. @@ -134,7 +134,7 @@ See note on custom errors and asynchronous keywords in the previous section. "Macro" function is called during schema compilation. It is passed schema, parent schema and [schema compilation context](#schema-compilation-context) and it should return another schema that will be applied to the data in addition to the original schema. -It is the most efficient approach (in cases when the keyword logic can be expressed with another JSON-schema) because it is usually easy to implement and there is no extra function call during validation. +It is the most efficient approach (in cases when the keyword logic can be expressed with another JSON Schema) because it is usually easy to implement and there is no extra function call during validation. In addition to the errors from the expanded schema macro keyword will add its own error in case validation fails. diff --git a/FAQ.md b/FAQ.md index 41c985a47..6e8e8a3f7 100644 --- a/FAQ.md +++ b/FAQ.md @@ -29,7 +29,7 @@ No. In many cases there is a module responsible for the validation in the applic Doing this would create a precedent where validated data is used in error messages, creating a vulnerability (e.g., when ajv is used to validate API data/parameters and error messages are logged). -Since the property name is already in the params object, in an application you can modify messages in any way you need. ajv-errors package will allow to modify messages as well - templating is [not there yet](https://github.com/epoberezkin/ajv-errors/issues/4), though. +Since the property name is already in the params object, in an application you can modify messages in any way you need. ajv-errors package allows modifying messages as well. ## Additional properties inside compound keywords anyOf, oneOf, etc. @@ -39,7 +39,7 @@ See [#127](https://github.com/epoberezkin/ajv/issues/127), [#129](https://github ##### Why the keyword `additionalProperties: false` fails validation when some properties are "declared" inside a subschema in `anyOf`/etc.? -The keyword `additionalProperties` creates the restriction on validated data based on its own value (`false` or schema object) and on the keywords `properties` and `patternProperties` in the SAME schema object. JSON-schema validators must NOT take into account properties used in other schema objects. +The keyword `additionalProperties` creates the restriction on validated data based on its own value (`false` or schema object) and on the keywords `properties` and `patternProperties` in the SAME schema object. JSON Schema validators must NOT take into account properties used in other schema objects. While you can expect that the schema below would allow the objects either with properties `foo` and `bar` or with properties `foo` and `baz` and all other properties will be prohibited, this schema will only allow objects with one property `foo` (an empty object and any non-objects will also be valid): @@ -84,5 +84,5 @@ There were many conversations about the meaning of `$ref` in [JSON Schema GitHub There are two possible approaches: -1. Write code to traverse schema and replace every `$ref` with the referenced schema. An additional limitation is that `"$ref"` inside keywords "properties", "patternProperties" and "dependencies" means property name (or pattern) rather than the reference to another schema. +1. Traverse schema (e.g. with json-schema-traverse) and replace every `$ref` with the referenced schema. 2. Use a specially constructed JSON Schema with a [custom keyword](https://github.com/epoberezkin/ajv/blob/master/CUSTOM.md) to traverse and modify your schema. diff --git a/KEYWORDS.md b/KEYWORDS.md index 5f886c275..0c280de00 100644 --- a/KEYWORDS.md +++ b/KEYWORDS.md @@ -1,7 +1,7 @@ # JSON Schema validation keywords -In a simple way, JSON schema is an object with validation keywords. +In a simple way, JSON Schema is an object with validation keywords. The keywords and their values define what rules the data should satisfy to be valid. @@ -75,7 +75,7 @@ __Examples__ _invalid_: `[]`, `{}`, `null`, `true` -All examples above are JSON schemas that only require data to be of certain type to be valid. +All examples above are JSON Schemas that only require data to be of certain type to be valid. Most other keywords apply only to a particular type of data. If the data is of different type, the keyword will not apply and the data will be considered valid. @@ -88,11 +88,11 @@ Most other keywords apply only to a particular type of data. If the data is of d The value of keyword `maximum` (`minimum`) should be a number. This value is the maximum (minimum) allowed value for the data to be valid. -Draft 4: The value of keyword `exclusiveMaximum` (`exclusiveMinimum`) should be a boolean value. These keyword cannot be used without `maximum` (`minimum`). If this keyword value is equal to `true`, the data should not be equal to the value in `maximum` (`minimum`) keyword to be valid. +Draft-04: The value of keyword `exclusiveMaximum` (`exclusiveMinimum`) should be a boolean value. These keyword cannot be used without `maximum` (`minimum`). If this keyword value is equal to `true`, the data should not be equal to the value in `maximum` (`minimum`) keyword to be valid. -Draft 6: The value of keyword `exclusiveMaximum` (`exclusiveMinimum`) should be a number. This value is the exclusive maximum (minimum) allowed value for the data to be valid (the data equal to this keyword value is invalid). +Draft-06/07: The value of keyword `exclusiveMaximum` (`exclusiveMinimum`) should be a number. This value is the exclusive maximum (minimum) allowed value for the data to be valid (the data equal to this keyword value is invalid). -Ajv supports both draft 4 and draft 6 syntaxes. +Ajv supports both draft-04 and draft-06/07 syntaxes. __Examples__ @@ -112,8 +112,8 @@ __Examples__ 3. _schema_: - draft 4: `{ "minimum": 5, "exclusiveMinimum": true }` - draft 6: `{ "exclusiveMinimum": 5 }` + draft-04: `{ "minimum": 5, "exclusiveMinimum": true }` + draft-06/07: `{ "exclusiveMinimum": 5 }` _valid_: `6`, `7`, any non-number (`"abc"`, `[]`, `{}`, `null`, `true`) @@ -365,7 +365,7 @@ __Examples__ ### `contains` -The value of the keyword is a JSON-schema. The array is valid if it contains at least one item that is valid according to this schema. +The value of the keyword is a JSON Schema. The array is valid if it contains at least one item that is valid according to this schema. __Example__ @@ -424,7 +424,7 @@ _invalid_: `{}`, `{"a": 1}`, `{"c": 3, "d":4}` ### `properties` -The value of the keyword should be a map with keys equal to data object properties. Each value in the map should be a JSON schema. For data object to be valid the corresponding values in data object properties should be valid according to these schemas. +The value of the keyword should be a map with keys equal to data object properties. Each value in the map should be a JSON Schema. For data object to be valid the corresponding values in data object properties should be valid according to these schemas. __Please note__: `properties` keyword does not require that the properties mentioned in it are present in the object (see examples). @@ -451,7 +451,7 @@ _invalid_: `{"foo": 1}`, `{"foo": "a", "bar": 1}` ### `patternProperties` -The value of this keyword should be a map where keys should be regular expressions and the values should be JSON schemas. For data object to be valid the values in data object properties that match regular expression(s) should be valid according to the corresponding schema(s). +The value of this keyword should be a map where keys should be regular expressions and the values should be JSON Schemas. For data object to be valid the values in data object properties that match regular expression(s) should be valid according to the corresponding schema(s). When the value in data object property matches multiple regular expressions it should be valid according to all the schemas for all matched regular expressions. @@ -478,7 +478,7 @@ _invalid_: `{"foo": 1}`, `{"foo": "a", "bar": "b"}` ### `additionalProperties` -The value of the keyword should be either a boolean or a JSON schema. +The value of the keyword should be either a boolean or a JSON Schema. If the value is `true` the keyword is ignored. @@ -552,7 +552,7 @@ __Examples__ ### `dependencies` -The value of the keyword is a map with keys equal to data object properties. Each value in the map should be either an array of unique property names ("property dependency") or a JSON schema ("schema dependency"). +The value of the keyword is a map with keys equal to data object properties. Each value in the map should be either an array of unique property names ("property dependency") or a JSON Schema ("schema dependency"). For property dependency, if the data object contains a property that is a key in the keyword value, then to be valid the data object should also contain all properties from the array of properties. @@ -596,7 +596,7 @@ __Examples__ ### `propertyNames` -The value of this keyword is a JSON schema. +The value of this keyword is a JSON Schema. For data object to be valid each property name in this object should be valid according to this schema. @@ -623,7 +623,7 @@ This keyword is only provided for backward compatibility, it will be removed in The value of this keyword should be a map where keys should be regular expressions and the values should be objects with the following properties: -- `schema` (required) - should be a JSON schema. For data object to be valid the values in data object properties that match regular expression(s) should be valid according to the corresponding `schema`(s). +- `schema` (required) - should be a JSON Schema. For data object to be valid the values in data object properties that match regular expression(s) should be valid according to the corresponding `schema`(s). - `maximum` / `minimum` (optional) - should be integers. For data object to be valid the number of properties that match regular expression(s) should be within limits set by `minimum`(s) and `maximum`(s). @@ -732,7 +732,7 @@ _invalid_: `{ "foo": 1 }`, `{ "bar": 1 }`, `{ "foo": 1, "bar": 2 }` ### `not` -The value of the keyword should be a JSON schema. The data is valid if it is invalid according to this schema. +The value of the keyword should be a JSON Schema. The data is valid if it is invalid according to this schema. __Examples__ @@ -763,7 +763,7 @@ __Examples__ ### `oneOf` -The value of the keyword should be an array of JSON schemas. The data is valid if it matches exactly one JSON schema from this array. Validators have to validate data against all schemas to establish validity according to this keyword. +The value of the keyword should be an array of JSON Schemas. The data is valid if it matches exactly one JSON Schema from this array. Validators have to validate data against all schemas to establish validity according to this keyword. __Example__ @@ -786,7 +786,7 @@ _invalid_: `2`, `3`, `4.5`, `5.5` ### `anyOf` -The value of the keyword should be an array of JSON schemas. The data is valid if it is valid according to one or more JSON schemas in this array. Validators only need to validate data against schemas in order until the first schema matches (or until all schemas have been tried). For this reason validating against this keyword is faster than against "oneOf" keyword in most cases. +The value of the keyword should be an array of JSON Schemas. The data is valid if it is valid according to one or more JSON Schemas in this array. Validators only need to validate data against schemas in order until the first schema matches (or until all schemas have been tried). For this reason validating against this keyword is faster than against "oneOf" keyword in most cases. __Example__ @@ -809,7 +809,7 @@ _invalid_: `4.5`, `5.5` ### `allOf` -The value of the keyword should be an array of JSON schemas. The data is valid if it is valid according to all JSON schemas in this array. +The value of the keyword should be an array of JSON Schemas. The data is valid if it is valid according to all JSON Schemas in this array. __Example__ @@ -832,7 +832,7 @@ _invalid_: `1.5`, `2.5`, `4`, `4.5`, `5`, `5.5`, any non-number ### `if`/`then`/`else` -These keywords allow to implement conditional validation. Their values should be valid JSON-schemas (object or boolean). +These keywords allow to implement conditional validation. Their values should be valid JSON Schemas (object or boolean). If `if` keyword is absent, the validation succeds. diff --git a/LICENSE b/LICENSE index 810539685..96ee71998 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 Evgeny Poberezkin +Copyright (c) 2015-2017 Evgeny Poberezkin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 37af0a0b8..d7e1b497c 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json')); ## Performance -Ajv generates code using [doT templates](https://github.com/olado/doT) to turn JSON schemas into super-fast validation functions that are efficient for v8 optimization. +Ajv generates code using [doT templates](https://github.com/olado/doT) to turn JSON Schemas into super-fast validation functions that are efficient for v8 optimization. Currently Ajv is the fastest and the most standard compliant validator according to these benchmarks: @@ -79,12 +79,12 @@ Performance of different validators by [json-schema-benchmark](https://github.co ## Features -- Ajv implements full JSON Schema [draft 6](http://json-schema.org/) and draft 4 standards: +- Ajv implements full JSON Schema [draft-06/07](http://json-schema.org/) and draft-04 standards: - all validation keywords (see [JSON Schema validation keywords](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md)) - full support of remote refs (remote schemas have to be added with `addSchema` or compiled to be available) - support of circular references between schemas - correct string lengths for strings with unicode pairs (can be turned off) - - [formats](#formats) defined by JSON Schema draft 4 standard and custom formats (can be turned off) + - [formats](#formats) defined by JSON Schema draft-07 standard and custom formats (can be turned off) - [validates schemas against meta-schema](#api-validateschema) - supports [browsers](#using-in-browser) and Node.js 0.10-8.x - [asynchronous loading](#asynchronous-schema-compilation) of referenced schemas during compilation @@ -97,7 +97,7 @@ Performance of different validators by [json-schema-benchmark](https://github.co - [custom keywords](#defining-custom-keywords) - draft-6 keywords `const`, `contains` and `propertyNames` - draft-6 boolean schemas (`true`/`false` as a schema to always pass/fail). -- keywords `switch`, `patternRequired`, `formatMaximum` / `formatMinimum` and `formatExclusiveMaximum` / `formatExclusiveMinimum` from [JSON-schema extension proposals](https://github.com/json-schema/json-schema/wiki/v5-Proposals) with [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) package +- keywords `switch`, `patternRequired`, `formatMaximum` / `formatMinimum` and `formatExclusiveMaximum` / `formatExclusiveMinimum` from [JSON Schema extension proposals](https://github.com/json-schema/json-schema/wiki/v5-Proposals) with [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) package - [$data reference](#data-reference) to use values from the validated data as values for the schema keywords - [asynchronous validation](#asynchronous-validation) of custom formats and keywords @@ -186,11 +186,11 @@ __Please note__: some frameworks, e.g. Dojo, may redefine global require in such CLI is available as a separate npm package [ajv-cli](https://github.com/jessedc/ajv-cli). It supports: -- compiling JSON-schemas to test their validity +- compiling JSON Schemas to test their validity - BETA: generating standalone module exporting a validation function to be used without Ajv (using [ajv-pack](https://github.com/epoberezkin/ajv-pack)) - migrate schemas to draft-07 (using [json-schema-migrate](https://github.com/epoberezkin/json-schema-migrate)) -- validating data file(s) against JSON-schema -- testing expected validity of data against JSON-schema +- validating data file(s) against JSON Schema +- testing expected validity of data against JSON Schema - referenced schemas - custom meta-schemas - files in JSON and JavaScript format @@ -200,7 +200,7 @@ CLI is available as a separate npm package [ajv-cli](https://github.com/jessedc/ ## Validation keywords -Ajv supports all validation keywords from draft 4 of JSON-schema standard: +Ajv supports all validation keywords from draft-07 of JSON Schema standard: - [type](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#type) - [for numbers](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#keywords-for-numbers) - maximum, minimum, exclusiveMaximum, exclusiveMinimum, multipleOf @@ -210,7 +210,7 @@ Ajv supports all validation keywords from draft 4 of JSON-schema standard: - [for all types](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#keywords-for-all-types) - enum, [const](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#const) - [compound keywords](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#compound-keywords) - not, oneOf, anyOf, allOf, [if/then/else](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#ifthenelse) -With [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) package Ajv also supports validation keywords from [JSON Schema extension proposals](https://github.com/json-schema/json-schema/wiki/v5-Proposals) for JSON-schema standard: +With [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) package Ajv also supports validation keywords from [JSON Schema extension proposals](https://github.com/json-schema/json-schema/wiki/v5-Proposals) for JSON Schema standard: - [patternRequired](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#patternrequired-proposed) - like `required` but with patterns that some property should match. - [formatMaximum, formatMinimum, formatExclusiveMaximum, formatExclusiveMinimum](https://github.com/epoberezkin/ajv/blob/master/KEYWORDS.md#formatmaximum--formatminimum-and-exclusiveformatmaximum--exclusiveformatminimum-proposed) - setting limits for date, time, etc. @@ -369,7 +369,7 @@ var validData = { ## $merge and $patch keywords -With the package [ajv-merge-patch](https://github.com/epoberezkin/ajv-merge-patch) you can use the keywords `$merge` and `$patch` that allow extending JSON-schemas with patches using formats [JSON Merge Patch (RFC 7396)](https://tools.ietf.org/html/rfc7396) and [JSON Patch (RFC 6902)](https://tools.ietf.org/html/rfc6902). +With the package [ajv-merge-patch](https://github.com/epoberezkin/ajv-merge-patch) you can use the keywords `$merge` and `$patch` that allow extending JSON Schemas with patches using formats [JSON Merge Patch (RFC 7396)](https://tools.ietf.org/html/rfc7396) and [JSON Patch (RFC 6902)](https://tools.ietf.org/html/rfc6902). To add keywords `$merge` and `$patch` to Ajv instance use this code: @@ -443,7 +443,7 @@ The advantages of using custom keywords are: If a keyword is used only for side-effects and its validation result is pre-defined, use option `valid: true/false` in keyword definition to simplify both generated code (no error handling in case of `valid: true`) and your keyword functions (no need to return any validation result). -The concerns you have to be aware of when extending JSON-schema standard with custom keywords are the portability and understanding of your schemas. You will have to support these custom keywords on other platforms and to properly document these keywords so that everybody can understand them in your schemas. +The concerns you have to be aware of when extending JSON Schema standard with custom keywords are the portability and understanding of your schemas. You will have to support these custom keywords on other platforms and to properly document these keywords so that everybody can understand them in your schemas. You can define custom keywords with [addKeyword](#api-addkeyword) method. Keywords are defined on the `ajv` instance level - new instances will not have previously defined keywords. @@ -960,14 +960,14 @@ Custom formats can be also added via `formats` option. Add custom validation keyword to Ajv instance. -Keyword should be different from all standard JSON schema keywords and different from previously defined keywords. There is no way to redefine keywords or to remove keyword definition from the instance. +Keyword should be different from all standard JSON Schema keywords and different from previously defined keywords. There is no way to redefine keywords or to remove keyword definition from the instance. Keyword must start with a letter, `_` or `$`, and may continue with letters, numbers, `_`, `$`, or `-`. It is recommended to use an application-specific prefix for keywords to avoid current and future name collisions. Example Keywords: - `"xyz-example"`: valid, and uses prefix for the xyz project to avoid name collisions. -- `"example"`: valid, but not recommended as it could collide with future versions of JSON schema etc. +- `"example"`: valid, but not recommended as it could collide with future versions of JSON Schema etc. - `"3-example"`: invalid as numbers are not allowed to be the first character in a keyword Keyword definition is an object with the following properties: @@ -1078,7 +1078,7 @@ Defaults: - _unknownFormats_: handling of unknown formats. Option values: - `true` (default) - if an unknown format is encountered the exception is thrown during schema compilation. If `format` keyword value is [$data reference](#data-reference) and it is unknown the validation will fail. - `[String]` - an array of unknown format names that will be ignored. This option can be used to allow usage of third party schemas with format(s) for which you don't have definitions, but still fail if another unknown format is used. If `format` keyword value is [$data reference](#data-reference) and it is not in this array the validation will fail. - - `"ignore"` - to log warning during schema compilation and always pass validation (the default behaviour in versions before 5.0.0). This option is not recommended, as it allows to mistype format name and it won't be validated without any error message. This behaviour is required by JSON-schema specification. + - `"ignore"` - to log warning during schema compilation and always pass validation (the default behaviour in versions before 5.0.0). This option is not recommended, as it allows to mistype format name and it won't be validated without any error message. This behaviour is required by JSON Schema specification. - _schemas_: an array or object of schemas that will be added to the instance. In case you pass the array the schemas must have IDs in them. When the object is passed the method `addSchema(value, key)` will be called for each schema in this object. - _logger_: sets the logging method. Default is the global `console` object that should have methods `log`, `warn` and `error`. Option values: - custom logger - it should have methods `log`, `warn` and `error`. If any of these methods is missing an exception will be thrown. @@ -1130,7 +1130,7 @@ Defaults: ##### Advanced options - _meta_: add [meta-schema](http://json-schema.org/documentation.html) so it can be used by other schemas (true by default). If an object is passed, it will be used as the default meta-schema for schemas that have no `$schema` keyword. This default meta-schema MUST have `$schema` keyword. -- _validateSchema_: validate added/compiled schemas against meta-schema (true by default). `$schema` property in the schema can either be http://json-schema.org/schema or http://json-schema.org/draft-04/schema or absent (draft-4 meta-schema will be used) or can be a reference to the schema previously added with `addMetaSchema` method. Option values: +- _validateSchema_: validate added/compiled schemas against meta-schema (true by default). `$schema` property in the schema can be http://json-schema.org/draft-07/schema or absent (draft-07 meta-schema will be used) or can be a reference to the schema previously added with `addMetaSchema` method. Option values: - `true` (default) - if the validation fails, throw the exception. - `"log"` - if the validation fails, log error. - `false` - skip schema validation. @@ -1234,7 +1234,7 @@ If you have published a useful plugin please submit a PR to add it to the next s - [osprey-method-handler](https://github.com/mulesoft-labs/osprey-method-handler) - Express middleware for validating requests and responses based on a RAML method object, used in [osprey](https://github.com/mulesoft/osprey) - validating API proxy generated from a RAML definition - [har-validator](https://github.com/ahmadnassri/har-validator) - HTTP Archive (HAR) validator - [jsoneditor](https://github.com/josdejong/jsoneditor) - a web-based tool to view, edit, format, and validate JSON http://jsoneditoronline.org -- [JSON Schema Lint](https://github.com/nickcmaynard/jsonschemalint) - a web tool to validate JSON/YAML document against a single JSON-schema http://jsonschemalint.com +- [JSON Schema Lint](https://github.com/nickcmaynard/jsonschemalint) - a web tool to validate JSON/YAML document against a single JSON Schema http://jsonschemalint.com - [objection](https://github.com/vincit/objection.js) - SQL-friendly ORM for Node.js - [table](https://github.com/gajus/table) - formats data into a string table - [ripple-lib](https://github.com/ripple/ripple-lib) - a JavaScript API for interacting with [Ripple](https://ripple.com) in Node.js and the browser @@ -1243,7 +1243,7 @@ If you have published a useful plugin please submit a PR to add it to the next s - [react-form-controlled](https://github.com/seeden/react-form-controlled) - React controlled form components with validation - [rabbitmq-schema](https://github.com/tjmehta/rabbitmq-schema) - a schema definition module for RabbitMQ graphs and messages - [@query/schema](https://www.npmjs.com/package/@query/schema) - stream filtering with a URI-safe query syntax parsing to JSON Schema -- [chai-ajv-json-schema](https://github.com/peon374/chai-ajv-json-schema) - chai plugin to us JSON-schema with expect in mocha tests +- [chai-ajv-json-schema](https://github.com/peon374/chai-ajv-json-schema) - chai plugin to us JSON Schema with expect in mocha tests - [grunt-jsonschema-ajv](https://github.com/SignpostMarv/grunt-jsonschema-ajv) - Grunt plugin for validating files against JSON Schema - [extract-text-webpack-plugin](https://github.com/webpack-contrib/extract-text-webpack-plugin) - extract text from bundle into a file - [electron-builder](https://github.com/electron-userland/electron-builder) - a solution to package and build a ready for distribution Electron app diff --git a/lib/refs/data.json b/lib/refs/data.json index f17b4142d..87a2d1437 100644 --- a/lib/refs/data.json +++ b/lib/refs/data.json @@ -1,7 +1,7 @@ { "$schema": "http://json-schema.org/draft-07/schema#", "$id": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/data.json#", - "description": "Meta-schema for $data reference (JSON-schema extension proposal)", + "description": "Meta-schema for $data reference (JSON Schema extension proposal)", "type": "object", "required": [ "$data" ], "properties": { diff --git a/spec/tests/issues/27_1_recursive_raml_schema.json b/spec/tests/issues/27_1_recursive_raml_schema.json index 4ae4089e9..51706e3b6 100644 --- a/spec/tests/issues/27_1_recursive_raml_schema.json +++ b/spec/tests/issues/27_1_recursive_raml_schema.json @@ -1,8 +1,8 @@ [ { - "description": "JSON schema for a standard RAML object (#27)", + "description": "JSON Schema for a standard RAML object (#27)", "schema": { - "title": "A JSON schema for a standard RAML object", + "title": "A JSON Schema for a standard RAML object", "$schema": "http://json-schema.org/draft-04/schema#", "type": "object", "required": [ From 18b4c3f3a4f818e4d1093c5badde9e4e9e77f325 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 25 Nov 2017 20:37:02 +0000 Subject: [PATCH 32/48] docs: update draft-07 url --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d7e1b497c..ab640ec95 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ The fastest JSON Schema validator for Node.js and browser with draft-07 support. ## Using version 6 -[JSON Schema draft-07 WIP](http://json-schema.org/work-in-progress/WIP-jsonschema-validation.html) is published. +[JSON Schema draft-07](http://json-schema.org/latest/json-schema-validation.html) is published. [Ajv version 6.0.0-beta.0](https://github.com/epoberezkin/ajv/releases/tag/6.0.0-beta.0) that supports draft-07 is released. It may require either migrating your schemas or updating your code (to continue using draft-04 and v5 schemas, draft-06 schemas will be supported without changes). From e15f543dbda7fc539817f2b9b2820f6db2e3986f Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 26 Nov 2017 10:11:55 +0000 Subject: [PATCH 33/48] refactor: remove patternGroups keyword, #614 --- KEYWORDS.md | 36 ----- lib/ajv.d.ts | 10 +- lib/ajv.js | 2 - lib/dot/errors.def | 3 - lib/dot/properties.jst | 83 ---------- lib/dot/validate.jst | 3 - lib/patternGroups.js | 36 ----- spec/extras.spec.js | 1 - spec/extras/patternGroups.json | 271 --------------------------------- spec/options.spec.js | 70 --------- 10 files changed, 2 insertions(+), 513 deletions(-) delete mode 100644 lib/patternGroups.js delete mode 100644 spec/extras/patternGroups.json diff --git a/KEYWORDS.md b/KEYWORDS.md index 0c280de00..e5b330e3d 100644 --- a/KEYWORDS.md +++ b/KEYWORDS.md @@ -31,7 +31,6 @@ The keywords and their values define what rules the data should satisfy to be va - [additionalProperties](#additionalproperties) - [dependencies](#dependencies) - [propertyNames](#propertynames) (added in draft-06) - - [patternGroups](#patterngroups-deprecated) (deprecated) - [patternRequired](#patternrequired-proposed) (proposed) - [Keywords for all types](#keywords-for-all-types) - [enum](#enum) @@ -617,41 +616,6 @@ _invalid_: `{"foo": "any value"}` -### `patternGroups` (deprecated) - -This keyword is only provided for backward compatibility, it will be removed in the next major version. To use it, pass option `patternGroups: true`. - -The value of this keyword should be a map where keys should be regular expressions and the values should be objects with the following properties: - -- `schema` (required) - should be a JSON Schema. For data object to be valid the values in data object properties that match regular expression(s) should be valid according to the corresponding `schema`(s). -- `maximum` / `minimum` (optional) - should be integers. For data object to be valid the number of properties that match regular expression(s) should be within limits set by `minimum`(s) and `maximum`(s). - - -__Example__ - -_schema_: - -```json -{ - "patternGroups": { - "^[a-z]+$": { - "minimum": 1, - "schema": { "type": "string" } - }, - "^[0-9]+$": { - "minimum": 1, - "schema": { "type": "integer" } - } - } -} -``` - -_valid_: `{ "foo": "bar", "1": "2" }`, any non-object - -_invalid_: `{}`, `{ "foo": "bar" }`, `{ "1": "2" }` - - - ### `patternRequired` (proposed) Defined in [ajv-keywords](https://github.com/epoberezkin/ajv-keywords) package. diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index b687f7814..d34b7f9f1 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -225,8 +225,8 @@ declare namespace ajv { DependenciesParams | FormatParams | ComparisonParams | MultipleOfParams | PatternParams | RequiredParams | TypeParams | UniqueItemsParams | CustomParams | - PatternGroupsParams | PatternRequiredParams | - PropertyNamesParams | SwitchParams | NoParams | EnumParams; + PatternRequiredParams | PropertyNamesParams | + SwitchParams | NoParams | EnumParams; interface RefParams { ref: string; @@ -282,12 +282,6 @@ declare namespace ajv { keyword: string; } - interface PatternGroupsParams { - reason: string; - limit: number; - pattern: string; - } - interface PatternRequiredParams { missingPattern: string; } diff --git a/lib/ajv.js b/lib/ajv.js index 4d93848fc..531d814ef 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -8,7 +8,6 @@ var compileSchema = require('./compile') , formats = require('./compile/formats') , rules = require('./compile/rules') , $dataMetaSchema = require('./data') - , patternGroups = require('./patternGroups') , util = require('./compile/util'); module.exports = Ajv; @@ -74,7 +73,6 @@ function Ajv(opts) { addDraft6MetaSchema(this); if (typeof opts.meta == 'object') this.addMetaSchema(opts.meta); addInitialSchemas(this); - if (opts.patternGroups) patternGroups(this); } diff --git a/lib/dot/errors.def b/lib/dot/errors.def index 562052c1a..855b8821d 100644 --- a/lib/dot/errors.def +++ b/lib/dot/errors.def @@ -111,7 +111,6 @@ not: "'should NOT be valid'", oneOf: "'should match exactly one schema in oneOf'", pattern: "'should match pattern \"{{#def.concatSchemaEQ}}\"'", - patternGroups: "'should NOT have {{=$moreOrLess}} than {{=$limit}} properties matching pattern \"{{=it.util.escapeQuotes($pgProperty)}}\"'", propertyNames: "'property name \\'{{=$invalidName}}\\' is invalid'", required: "'{{? it.opts._errorDataPathProperty }}is a required property{{??}}should have required property \\'{{=$missingProperty}}\\'{{?}}'", type: "'should be {{? $typeIsArray }}{{= $typeSchema.join(\",\") }}{{??}}{{=$typeSchema}}{{?}}'", @@ -148,7 +147,6 @@ not: "validate.schema{{=$schemaPath}}", oneOf: "validate.schema{{=$schemaPath}}", pattern: "{{#def.schemaRefOrQS}}", - patternGroups: "validate.schema{{=$schemaPath}}", propertyNames: "validate.schema{{=$schemaPath}}", required: "validate.schema{{=$schemaPath}}", type: "validate.schema{{=$schemaPath}}", @@ -184,7 +182,6 @@ not: "{}", oneOf: "{ passingSchemas: {{=$passingSchemas}} }", pattern: "{ pattern: {{#def.schemaValueQS}} }", - patternGroups: "{ reason: '{{=$reason}}', limit: {{=$limit}}, pattern: '{{=it.util.escapeQuotes($pgProperty)}}' }", propertyNames: "{ propertyName: '{{=$invalidName}}' }", required: "{ missingProperty: '{{=$missingProperty}}' }", type: "{ type: '{{? $typeIsArray }}{{= $typeSchema.join(\",\") }}{{??}}{{=$typeSchema}}{{?}}' }", diff --git a/lib/dot/properties.jst b/lib/dot/properties.jst index 8d56324b7..dc8ab7bae 100644 --- a/lib/dot/properties.jst +++ b/lib/dot/properties.jst @@ -44,11 +44,6 @@ var $required = it.schema.required; if ($required && !(it.opts.v5 && $required.$data) && $required.length < it.opts.loopRequired) var $requiredHash = it.util.toHash($required); - - if (it.opts.patternGroups) { - var $pgProperties = it.schema.patternGroups || {} - , $pgPropertyKeys = Object.keys($pgProperties); - } }} @@ -76,11 +71,6 @@ var {{=$nextValid}} = true; || {{= it.usePattern($pProperty) }}.test({{=$key}}) {{~}} {{?}} - {{? it.opts.patternGroups && $pgPropertyKeys.length }} - {{~ $pgPropertyKeys:$pgProperty:$i }} - || {{= it.usePattern($pgProperty) }}.test({{=$key}}) - {{~}} - {{?}} ); if (isAdditional{{=$lvl}}) { @@ -246,79 +236,6 @@ var {{=$nextValid}} = true; {{?}} -{{? it.opts.patternGroups && $pgPropertyKeys.length }} - {{~ $pgPropertyKeys:$pgProperty }} - {{ - var $pgSchema = $pgProperties[$pgProperty] - , $sch = $pgSchema.schema; - }} - - {{? {{# def.nonEmptySchema:$sch}} }} - {{ - $it.schema = $sch; - $it.schemaPath = it.schemaPath + '.patternGroups' + it.util.getProperty($pgProperty) + '.schema'; - $it.errSchemaPath = it.errSchemaPath + '/patternGroups/' - + it.util.escapeFragment($pgProperty) - + '/schema'; - }} - - var pgPropCount{{=$lvl}} = 0; - - {{# def.iterateProperties }} - if ({{= it.usePattern($pgProperty) }}.test({{=$key}})) { - pgPropCount{{=$lvl}}++; - - {{ - $it.errorPath = it.util.getPathExpr(it.errorPath, $key, it.opts.jsonPointers); - var $passData = $data + '[' + $key + ']'; - $it.dataPathArr[$dataNxt] = $key; - }} - - {{# def.generateSubschemaCode }} - {{# def.optimizeValidate }} - - {{? $breakOnError }} if (!{{=$nextValid}}) break; {{?}} - } - {{? $breakOnError }} else {{=$nextValid}} = true; {{?}} - } - - {{# def.ifResultValid }} - - {{ - var $pgMin = $pgSchema.minimum - , $pgMax = $pgSchema.maximum; - }} - {{? $pgMin !== undefined || $pgMax !== undefined }} - var {{=$valid}} = true; - - {{ var $currErrSchemaPath = $errSchemaPath; }} - - {{? $pgMin !== undefined }} - {{ var $limit = $pgMin, $reason = 'minimum', $moreOrLess = 'less'; }} - {{=$valid}} = pgPropCount{{=$lvl}} >= {{=$pgMin}}; - {{ $errSchemaPath = it.errSchemaPath + '/patternGroups/minimum'; }} - {{# def.checkError:'patternGroups' }} - {{? $pgMax !== undefined }} - else - {{?}} - {{?}} - - {{? $pgMax !== undefined }} - {{ var $limit = $pgMax, $reason = 'maximum', $moreOrLess = 'more'; }} - {{=$valid}} = pgPropCount{{=$lvl}} <= {{=$pgMax}}; - {{ $errSchemaPath = it.errSchemaPath + '/patternGroups/maximum'; }} - {{# def.checkError:'patternGroups' }} - {{?}} - - {{ $errSchemaPath = $currErrSchemaPath; }} - - {{# def.ifValid }} - {{?}} - {{?}} {{ /* def.nonEmptySchema */ }} - {{~}} -{{?}} - - {{? $breakOnError }} {{= $closingBraces }} if ({{=$errs}} == errors) { diff --git a/lib/dot/validate.jst b/lib/dot/validate.jst index 5918f7339..27393cf30 100644 --- a/lib/dot/validate.jst +++ b/lib/dot/validate.jst @@ -162,9 +162,6 @@ {{ $closingBraces2 += '}'; }} {{?}} {{??}} - {{? it.opts.v5 && it.schema.patternGroups }} - {{ it.logger.warn('keyword "patternGroups" is deprecated and disabled. Use option patternGroups: true to enable.'); }} - {{?}} {{~ it.RULES:$rulesGroup }} {{? $shouldUseGroup($rulesGroup) }} {{? $rulesGroup.type }} diff --git a/lib/patternGroups.js b/lib/patternGroups.js deleted file mode 100644 index 79abc2a69..000000000 --- a/lib/patternGroups.js +++ /dev/null @@ -1,36 +0,0 @@ -'use strict'; - -var META_SCHEMA_ID = 'http://json-schema.org/draft-07/schema'; - -module.exports = function (ajv) { - var defaultMeta = ajv._opts.defaultMeta; - var metaSchemaRef = typeof defaultMeta == 'string' - ? { $ref: defaultMeta } - : ajv.getSchema(META_SCHEMA_ID) - ? { $ref: META_SCHEMA_ID } - : {}; - - ajv.addKeyword('patternGroups', { - // implemented in properties.jst - metaSchema: { - type: 'object', - additionalProperties: { - type: 'object', - required: [ 'schema' ], - properties: { - maximum: { - type: 'integer', - minimum: 0 - }, - minimum: { - type: 'integer', - minimum: 0 - }, - schema: metaSchemaRef - }, - additionalProperties: false - } - } - }); - ajv.RULES.all.properties.implements.push('patternGroups'); -}; diff --git a/spec/extras.spec.js b/spec/extras.spec.js index dcf1713e2..15c51a6bc 100644 --- a/spec/extras.spec.js +++ b/spec/extras.spec.js @@ -8,7 +8,6 @@ var jsonSchemaTest = require('json-schema-test') var instances = getAjvInstances(options, { $data: true, - patternGroups: true, unknownFormats: ['allowedUnknown'] }); diff --git a/spec/extras/patternGroups.json b/spec/extras/patternGroups.json deleted file mode 100644 index 94eea5ba1..000000000 --- a/spec/extras/patternGroups.json +++ /dev/null @@ -1,271 +0,0 @@ -[ - { - "description": "patternGroups validates properties matching a regex (equivalent to the test from draft 4)", - "schema": { - "patternGroups": { - "f.*o": { - "schema": {"type": "integer"} - } - } - }, - "tests": [ - { - "description": "a single valid match is valid", - "data": {"foo": 1}, - "valid": true - }, - { - "description": "multiple valid matches is valid", - "data": {"foo": 1, "foooooo" : 2}, - "valid": true - }, - { - "description": "a single invalid match is invalid", - "data": {"foo": "bar", "fooooo": 2}, - "valid": false - }, - { - "description": "multiple invalid matches is invalid", - "data": {"foo": "bar", "foooooo" : "baz"}, - "valid": false - }, - { - "description": "ignores non-objects", - "data": 12, - "valid": true - } - ] - }, - { - "description": "multiple simultaneous patternGroups are validated (equivalent to the test from draft 4)", - "schema": { - "patternGroups": { - "a*": { - "schema": {"type": "integer"} - }, - "aaa*": { - "schema": {"maximum": 20} - } - } - }, - "tests": [ - { - "description": "a single valid match is valid", - "data": {"a": 21}, - "valid": true - }, - { - "description": "a simultaneous match is valid", - "data": {"aaaa": 18}, - "valid": true - }, - { - "description": "multiple matches is valid", - "data": {"a": 21, "aaaa": 18}, - "valid": true - }, - { - "description": "an invalid due to one is invalid", - "data": {"a": "bar"}, - "valid": false - }, - { - "description": "an invalid due to the other is invalid", - "data": {"aaaa": 31}, - "valid": false - }, - { - "description": "an invalid due to both is invalid", - "data": {"aaa": "foo", "aaaa": 31}, - "valid": false - } - ] - }, - { - "description": "regexes in patternGroups are not anchored by default and are case sensitive (equivalent to the test from draft 4)", - "schema": { - "patternGroups": { - "[0-9]{2,}": { - "schema": { "type": "boolean" } - }, - "X_": { - "schema": { "type": "string" } - } - } - }, - "tests": [ - { - "description": "non recognized members are ignored", - "data": { "answer 1": "42" }, - "valid": true - }, - { - "description": "recognized members are accounted for", - "data": { "a31b": null }, - "valid": false - }, - { - "description": "regexes are case sensitive", - "data": { "a_x_3": 3 }, - "valid": true - }, - { - "description": "regexes are case sensitive, 2", - "data": { "a_X_3": 3 }, - "valid": false - } - ] - }, - { - "description": - "patternGroups validates that the number of properties matching a regex is within limit", - "schema": { - "patternGroups": { - "f.*o": { - "schema": {"type": "integer"}, - "minimum": 1, - "maximum": 2 - } - } - }, - "tests": [ - { - "description": "a single valid match is valid", - "data": {"foo": 1}, - "valid": true - }, - { - "description": "2 valid matches are valid", - "data": {"foo": 1, "foooo" : 2}, - "valid": true - }, - { - "description": "no valid matches are invalid", - "data": {}, - "valid": false - }, - { - "description": "more than 2 valid matches are invalid", - "data": {"foo": 1, "foooo" : 2, "foooooo" : 3}, - "valid": false - }, - { - "description": "sinlge invalid match is invalid", - "data": {"foo": 1, "foooooo" : "baz"}, - "valid": false - } - ] - }, - { - "description": "multiple simultaneous patternGroups are validated for number of matching properties", - "schema": { - "patternGroups": { - "a*": { - "schema": {"type": "integer"}, - "minimum": 1 - }, - "aaa*": { - "schema": {"maximum": 20}, - "maximum": 1 - } - } - }, - "tests": [ - { - "description": "a single first match is valid", - "data": {"a": 21}, - "valid": true - }, - { - "description": "no first match is invalid", - "data": {}, - "valid": false - }, - { - "description": "simultaneous match is valid", - "data": {"aaaa": 18}, - "valid": true - }, - { - "description": "multiple matches is valid", - "data": {"a": 21, "aaaa": 18}, - "valid": true - }, - { - "description": "two second matches are invalid", - "data": {"aaa": 17, "aaaa": 18}, - "valid": false - }, - { - "description": "invalid due to the first is invalid", - "data": {"a": "bar"}, - "valid": false - }, - { - "description": "invalid due to the second is invalid", - "data": {"a": 21, "aaaa": 31}, - "valid": false - }, - { - "description": "invalid due to both is invalid", - "data": {"a": "foo", "aaaa": 31}, - "valid": false - } - ] - }, - { - "description": "properties, patternGroups, additionalProperties interaction (equivalent to the test from draft 4)", - "schema": { - "properties": { - "foo": {"type": "array", "maxItems": 3}, - "bar": {"type": "array"} - }, - "patternGroups": { - "f.o": { "schema": {"minItems": 2} } - }, - "additionalProperties": {"type": "integer"} - }, - "tests": [ - { - "description": "property validates property", - "data": {"foo": [1, 2]}, - "valid": true - }, - { - "description": "property invalidates property", - "data": {"foo": [1, 2, 3, 4]}, - "valid": false - }, - { - "description": "patternGroups invalidates property", - "data": {"foo": []}, - "valid": false - }, - { - "description": "patternGroups validates nonproperty", - "data": {"fxo": [1, 2]}, - "valid": true - }, - { - "description": "patternGroups invalidates nonproperty", - "data": {"fxo": []}, - "valid": false - }, - { - "description": "additionalProperty ignores property", - "data": {"bar": []}, - "valid": true - }, - { - "description": "additionalProperty validates others", - "data": {"quux": 3}, - "valid": true - }, - { - "description": "additionalProperty invalidates others", - "data": {"quux": "foo"}, - "valid": false - } - ] - } -] diff --git a/spec/options.spec.js b/spec/options.spec.js index 84a5d64f5..5ae18dd26 100644 --- a/spec/options.spec.js +++ b/spec/options.spec.js @@ -222,21 +222,6 @@ describe('Ajv Options', function () { test(schema, obj, proto); }); - it('should only validate own properties with patternGroups', function() { - ajv = new Ajv({ allErrors: true, patternGroups: true }); - ajvOP = new Ajv({ ownProperties: true, allErrors: true, patternGroups: true }); - - var schema = { - patternGroups: { - 'f.*o': { schema: { type: 'integer' } } - } - }; - - var obj = { fooo: 1 }; - var proto = { foo: 'not a number' }; - test(schema, obj, proto); - }); - it('should only validate own properties with propertyNames', function() { var schema = { propertyNames: { @@ -1148,61 +1133,6 @@ describe('Ajv Options', function () { }); - describe('patternGroups without draft-07 meta-schema', function() { - it('should use default meta-schema', function() { - var ajv = new Ajv({ - patternGroups: true, - meta: require('../lib/refs/json-schema-draft-04.json') - }); - - ajv.compile({ - patternGroups: { - '^foo': { - schema: { type: 'number' }, - minimum: 1 - } - } - }); - - should.throw(function() { - ajv.compile({ - patternGroups: { - '^foo': { - schema: { type: 'wrong_type' }, - minimum: 1 - } - } - }); - }); - }); - - it('should not use meta-schema if not available', function() { - var ajv = new Ajv({ - patternGroups: true, - meta: false - }); - - ajv.compile({ - patternGroups: { - '^foo': { - schema: { type: 'number' }, - minimum: 1 - } - } - }); - - ajv.compile({ - patternGroups: { - '^foo': { - schema: { type: 'wrong_type' }, - minimum: 1 - } - } - }); - }); - }); - - describe('schemaId', function() { describe('= undefined (default)', function() { it('should throw if both id and $id are available and different', function() { From 7e3d645ea2e0301fa0c211c4d477baf4204850bc Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 26 Nov 2017 10:13:23 +0000 Subject: [PATCH 34/48] refactor: remove v5 meta-schema, closes #614 --- lib/refs/json-schema-v5.json | 250 ----------------------------------- 1 file changed, 250 deletions(-) delete mode 100644 lib/refs/json-schema-v5.json diff --git a/lib/refs/json-schema-v5.json b/lib/refs/json-schema-v5.json deleted file mode 100644 index cc679a459..000000000 --- a/lib/refs/json-schema-v5.json +++ /dev/null @@ -1,250 +0,0 @@ -{ - "id": "https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-v5.json#", - "$schema": "http://json-schema.org/draft-04/schema#", - "description": "Core schema meta-schema (v5 proposals - deprecated)", - "definitions": { - "schemaArray": { - "type": "array", - "minItems": 1, - "items": { "$ref": "#" } - }, - "positiveInteger": { - "type": "integer", - "minimum": 0 - }, - "positiveIntegerDefault0": { - "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ] - }, - "simpleTypes": { - "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] - }, - "stringArray": { - "type": "array", - "items": { "type": "string" }, - "minItems": 1, - "uniqueItems": true - }, - "$data": { - "type": "object", - "required": [ "$data" ], - "properties": { - "$data": { - "type": "string", - "anyOf": [ - { "format": "relative-json-pointer" }, - { "format": "json-pointer" } - ] - } - }, - "additionalProperties": false - } - }, - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uri" - }, - "$schema": { - "type": "string", - "format": "uri" - }, - "title": { - "type": "string" - }, - "description": { - "type": "string" - }, - "default": {}, - "multipleOf": { - "anyOf": [ - { - "type": "number", - "minimum": 0, - "exclusiveMinimum": true - }, - { "$ref": "#/definitions/$data" } - ] - }, - "maximum": { - "anyOf": [ - { "type": "number" }, - { "$ref": "#/definitions/$data" } - ] - }, - "exclusiveMaximum": { - "anyOf": [ - { - "type": "boolean", - "default": false - }, - { "$ref": "#/definitions/$data" } - ] - }, - "minimum": { - "anyOf": [ - { "type": "number" }, - { "$ref": "#/definitions/$data" } - ] - }, - "exclusiveMinimum": { - "anyOf": [ - { - "type": "boolean", - "default": false - }, - { "$ref": "#/definitions/$data" } - ] - }, - "maxLength": { - "anyOf": [ - { "$ref": "#/definitions/positiveInteger" }, - { "$ref": "#/definitions/$data" } - ] - }, - "minLength": { - "anyOf": [ - { "$ref": "#/definitions/positiveIntegerDefault0" }, - { "$ref": "#/definitions/$data" } - ] - }, - "pattern": { - "anyOf": [ - { - "type": "string", - "format": "regex" - }, - { "$ref": "#/definitions/$data" } - ] - }, - "additionalItems": { - "anyOf": [ - { "type": "boolean" }, - { "$ref": "#" }, - { "$ref": "#/definitions/$data" } - ], - "default": {} - }, - "items": { - "anyOf": [ - { "$ref": "#" }, - { "$ref": "#/definitions/schemaArray" } - ], - "default": {} - }, - "maxItems": { - "anyOf": [ - { "$ref": "#/definitions/positiveInteger" }, - { "$ref": "#/definitions/$data" } - ] - }, - "minItems": { - "anyOf": [ - { "$ref": "#/definitions/positiveIntegerDefault0" }, - { "$ref": "#/definitions/$data" } - ] - }, - "uniqueItems": { - "anyOf": [ - { - "type": "boolean", - "default": false - }, - { "$ref": "#/definitions/$data" } - ] - }, - "maxProperties": { - "anyOf": [ - { "$ref": "#/definitions/positiveInteger" }, - { "$ref": "#/definitions/$data" } - ] - }, - "minProperties": { - "anyOf": [ - { "$ref": "#/definitions/positiveIntegerDefault0" }, - { "$ref": "#/definitions/$data" } - ] - }, - "required": { - "anyOf": [ - { "$ref": "#/definitions/stringArray" }, - { "$ref": "#/definitions/$data" } - ] - }, - "additionalProperties": { - "anyOf": [ - { "type": "boolean" }, - { "$ref": "#" }, - { "$ref": "#/definitions/$data" } - ], - "default": {} - }, - "definitions": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "default": {} - }, - "properties": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "default": {} - }, - "patternProperties": { - "type": "object", - "additionalProperties": { "$ref": "#" }, - "default": {} - }, - "dependencies": { - "type": "object", - "additionalProperties": { - "anyOf": [ - { "$ref": "#" }, - { "$ref": "#/definitions/stringArray" } - ] - } - }, - "enum": { - "anyOf": [ - { - "type": "array", - "minItems": 1, - "uniqueItems": true - }, - { "$ref": "#/definitions/$data" } - ] - }, - "type": { - "anyOf": [ - { "$ref": "#/definitions/simpleTypes" }, - { - "type": "array", - "items": { "$ref": "#/definitions/simpleTypes" }, - "minItems": 1, - "uniqueItems": true - } - ] - }, - "allOf": { "$ref": "#/definitions/schemaArray" }, - "anyOf": { "$ref": "#/definitions/schemaArray" }, - "oneOf": { "$ref": "#/definitions/schemaArray" }, - "not": { "$ref": "#" }, - "format": { - "anyOf": [ - { "type": "string" }, - { "$ref": "#/definitions/$data" } - ] - }, - "constant": { - "anyOf": [ - {}, - { "$ref": "#/definitions/$data" } - ] - }, - "contains": { "$ref": "#" } - }, - "dependencies": { - "exclusiveMaximum": [ "maximum" ], - "exclusiveMinimum": [ "minimum" ] - }, - "default": {} -} From c232bb3e4bb86b2d4f944911626835f85dd20288 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 26 Nov 2017 10:22:25 +0000 Subject: [PATCH 35/48] docs: shorter npm badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ab640ec95..d868af03c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ The fastest JSON Schema validator for Node.js and browser with draft-07 support. [![Build Status](https://travis-ci.org/epoberezkin/ajv.svg?branch=master)](https://travis-ci.org/epoberezkin/ajv) -[![npm version](https://badge.fury.io/js/ajv.svg)](https://www.npmjs.com/package/ajv) +[![npm](https://img.shields.io/npm/v/ajv.svg)](https://www.npmjs.com/package/ajv) [![npm@beta](https://img.shields.io/npm/v/ajv/beta.svg)](https://github.com/epoberezkin/ajv/tree/beta) [![npm downloads](https://img.shields.io/npm/dm/ajv.svg)](https://www.npmjs.com/package/ajv) [![Coverage Status](https://coveralls.io/repos/epoberezkin/ajv/badge.svg?branch=master&service=github)](https://coveralls.io/github/epoberezkin/ajv?branch=master) From e9dc9cb3233278601082acc2587207a7d4e0ad7d Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 26 Nov 2017 10:32:41 +0000 Subject: [PATCH 36/48] docs: type for "if" keyword error params --- lib/ajv.d.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/ajv.d.ts b/lib/ajv.d.ts index d34b7f9f1..97f5636f0 100644 --- a/lib/ajv.d.ts +++ b/lib/ajv.d.ts @@ -226,7 +226,7 @@ declare namespace ajv { MultipleOfParams | PatternParams | RequiredParams | TypeParams | UniqueItemsParams | CustomParams | PatternRequiredParams | PropertyNamesParams | - SwitchParams | NoParams | EnumParams; + IfParams | SwitchParams | NoParams | EnumParams; interface RefParams { ref: string; @@ -290,6 +290,10 @@ declare namespace ajv { propertyName: string; } + interface IfParams { + failingKeyword: string; + } + interface SwitchParams { caseIndex: number; } From c71fcbf68b7365a16827734b0a01d750d620718b Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 26 Nov 2017 10:34:12 +0000 Subject: [PATCH 37/48] 6.0.0-rc.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b0e049c1b..e6ce203ad 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.0.0-beta.2", + "version": "6.0.0-rc.0", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From 850d9045747073cd4ccd42d1469ef94d74131cb5 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 26 Nov 2017 17:55:28 +0000 Subject: [PATCH 38/48] docs: fix release url --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d868af03c..04e35ff5e 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ The fastest JSON Schema validator for Node.js and browser with draft-07 support. [JSON Schema draft-07](http://json-schema.org/latest/json-schema-validation.html) is published. -[Ajv version 6.0.0-beta.0](https://github.com/epoberezkin/ajv/releases/tag/6.0.0-beta.0) that supports draft-07 is released. It may require either migrating your schemas or updating your code (to continue using draft-04 and v5 schemas, draft-06 schemas will be supported without changes). +[Ajv version 6.0.0-beta.0](https://github.com/epoberezkin/ajv/releases/tag/v6.0.0-beta.0) that supports draft-07 is released. It may require either migrating your schemas or updating your code (to continue using draft-04 and v5 schemas, draft-06 schemas will be supported without changes). __Please note__: To use Ajv with draft-06 (or draft-04) schemas you need to explicitly add the meta-schema(s) to the validator instance: From b8d36fc0b0703716875abc47a5fd951a6010e070 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Dec 2017 10:24:27 +0000 Subject: [PATCH 39/48] fix: recursive ref to async sub-schema, closes #612 --- lib/compile/index.js | 2 +- lib/dot/ref.jst | 2 +- spec/async_validate.spec.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/compile/index.js b/lib/compile/index.js index f59893e2c..8af3a365f 100644 --- a/lib/compile/index.js +++ b/lib/compile/index.js @@ -221,7 +221,7 @@ function compile(schema, root, localRefs, baseId) { function resolvedRef(refVal, code) { return typeof refVal == 'object' || typeof refVal == 'boolean' ? { code: code, schema: refVal, inline: true } - : { code: code, $async: refVal && refVal.$async }; + : { code: code, $async: refVal && !!refVal.$async }; } function usePattern(regexStr) { diff --git a/lib/dot/ref.jst b/lib/dot/ref.jst index 8a62d812b..253e3507c 100644 --- a/lib/dot/ref.jst +++ b/lib/dot/ref.jst @@ -50,7 +50,7 @@ {{?}} {{??}} {{ - $async = $refVal.$async === true; + $async = $refVal.$async === true || (it.async && $refVal.$async !== false); $refCode = $refVal.code; }} {{?}} diff --git a/spec/async_validate.spec.js b/spec/async_validate.spec.js index 0e11dba18..039483a0c 100644 --- a/spec/async_validate.spec.js +++ b/spec/async_validate.spec.js @@ -239,7 +239,7 @@ describe('async schemas, formats and keywords', function() { return recursiveTest(schema); }); - it.skip('should validate recursive ref to async sub-schema, issue #612', function() { + it('should validate recursive ref to async sub-schema, issue #612', function() { var schema = { $async: true, type: 'object', From ce62f117ee1e485aca2bef662615f936e1449c19 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Dec 2017 12:32:45 +0000 Subject: [PATCH 40/48] feat: use only $id by default, closes #641 --- README.md | 6 +- lib/ajv.js | 4 +- spec/ajv.spec.js | 52 +++++----- spec/async.spec.js | 48 +++++----- spec/async_validate.spec.js | 10 +- spec/coercion.spec.js | 4 +- spec/issues.spec.js | 43 ++++----- spec/json-schema.spec.js | 4 +- spec/options.spec.js | 91 +++++++++++------- spec/remotes/hyper-schema.json | 170 +++++++-------------------------- spec/resolve.spec.js | 42 ++++---- spec/schema-tests.spec.js | 2 +- 12 files changed, 197 insertions(+), 279 deletions(-) diff --git a/README.md b/README.md index 04e35ff5e..daa8b6e3e 100644 --- a/README.md +++ b/README.md @@ -1033,7 +1033,7 @@ Defaults: schemas: {}, logger: undefined, // referenced schema options: - schemaId: undefined // recommended '$id' + schemaId: '$id', missingRefs: true, extendRefs: 'ignore', // recommended 'fail' loadSchema: undefined, // function(uri: string): Promise {} @@ -1088,9 +1088,9 @@ Defaults: ##### Referenced schema options - _schemaId_: this option defines which keywords are used as schema URI. Option value: - - `"$id"` (recommended) - only use `$id` keyword as schema URI (as specified in JSON Schema draft-06/07), ignore `id` keyword (if it is present a warning will be logged). + - `"$id"` (default) - only use `$id` keyword as schema URI (as specified in JSON Schema draft-06/07), ignore `id` keyword (if it is present a warning will be logged). - `"id"` - only use `id` keyword as schema URI (as specified in JSON Schema draft-04), ignore `$id` keyword (if it is present a warning will be logged). - - `undefined` (default) - use both `$id` and `id` keywords as schema URI. If both are present (in the same schema object) and different the exception will be thrown during schema compilation. + - `"auto"` - use both `$id` and `id` keywords as schema URI. If both are present (in the same schema object) and different the exception will be thrown during schema compilation. - _missingRefs_: handling of missing referenced schemas. Option values: - `true` (default) - if the reference cannot be resolved during compilation the exception is thrown. The thrown error has properties `missingRef` (with hash fragment) and `missingSchema` (without it). Both properties are resolved relative to the current base id (usually schema id, unless it was substituted). - `"ignore"` - to log error during compilation and always pass validation. diff --git a/lib/ajv.js b/lib/ajv.js index 2c1b8905d..b37950867 100644 --- a/lib/ajv.js +++ b/lib/ajv.js @@ -374,9 +374,9 @@ function _compile(schemaObj, root) { function chooseGetId(opts) { switch (opts.schemaId) { - case '$id': return _get$Id; + case 'auto': return _get$IdOrId; case 'id': return _getId; - default: return _get$IdOrId; + default: return _get$Id; } } diff --git a/spec/ajv.spec.js b/spec/ajv.spec.js index 7eec3b6b7..8c5e23a84 100644 --- a/spec/ajv.spec.js +++ b/spec/ajv.spec.js @@ -33,9 +33,9 @@ describe('Ajv', function () { }); it('should throw if different schema has the same id', function() { - ajv.compile({ id: '//e.com/int.json', type: 'integer' }); + ajv.compile({ $id: '//e.com/int.json', type: 'integer' }); should.throw(function() { - ajv.compile({ id: '//e.com/int.json', type: 'integer', minimum: 1 }); + ajv.compile({ $id: '//e.com/int.json', type: 'integer', minimum: 1 }); }); }); @@ -74,24 +74,24 @@ describe('Ajv', function () { }); it('should validate against previously compiled schema by id (also see addSchema)', function() { - ajv.validate({ id: '//e.com/int.json', type: 'integer' }, 1) .should.equal(true); + ajv.validate({ $id: '//e.com/int.json', type: 'integer' }, 1) .should.equal(true); ajv.validate('//e.com/int.json', 1) .should.equal(true); ajv.validate('//e.com/int.json', '1') .should.equal(false); - ajv.compile({ id: '//e.com/str.json', type: 'string' }) .should.be.a('function'); + ajv.compile({ $id: '//e.com/str.json', type: 'string' }) .should.be.a('function'); ajv.validate('//e.com/str.json', 'a') .should.equal(true); ajv.validate('//e.com/str.json', 1) .should.equal(false); }); it('should throw exception if no schema with ref', function() { - ajv.validate({ id: 'integer', type: 'integer' }, 1) .should.equal(true); + ajv.validate({ $id: 'integer', type: 'integer' }, 1) .should.equal(true); ajv.validate('integer', 1) .should.equal(true); should.throw(function() { ajv.validate('string', 'foo'); }); }); it('should validate schema fragment by ref', function() { ajv.addSchema({ - "id": "http://e.com/types.json", + "$id": "http://e.com/types.json", "definitions": { "int": { "type": "integer" }, "str": { "type": "string" } @@ -104,10 +104,10 @@ describe('Ajv', function () { it('should return schema fragment by id', function() { ajv.addSchema({ - "id": "http://e.com/types.json", + "$id": "http://e.com/types.json", "definitions": { - "int": { "id": "#int", "type": "integer" }, - "str": { "id": "#str", "type": "string" } + "int": { "$id": "#int", "type": "integer" }, + "str": { "$id": "#str", "type": "string" } } }); @@ -137,13 +137,13 @@ describe('Ajv', function () { }); it('should add and compile schema with id', function() { - ajv.addSchema({ id: '//e.com/int.json', type: 'integer' }); + ajv.addSchema({ $id: '//e.com/int.json', type: 'integer' }); ajv.validate('//e.com/int.json', 1) .should.equal(true); ajv.validate('//e.com/int.json', '1') .should.equal(false); }); it('should normalize schema keys and ids', function() { - ajv.addSchema({ id: '//e.com/int.json#', type: 'integer' }, 'int#'); + ajv.addSchema({ $id: '//e.com/int.json#', type: 'integer' }, 'int#'); ajv.validate('int', 1) .should.equal(true); ajv.validate('int', '1') .should.equal(false); ajv.validate('//e.com/int.json', 1) .should.equal(true); @@ -156,8 +156,8 @@ describe('Ajv', function () { it('should add and compile array of schemas with ids', function() { ajv.addSchema([ - { id: '//e.com/int.json', type: 'integer' }, - { id: '//e.com/str.json', type: 'string' } + { $id: '//e.com/int.json', type: 'integer' }, + { $id: '//e.com/str.json', type: 'string' } ]); var validate0 = ajv.getSchema('//e.com/int.json'); @@ -210,7 +210,7 @@ describe('Ajv', function () { it('should throw if schema id is not a string', function() { try { - ajv.addSchema({ id: 1, type: 'integer' }); + ajv.addSchema({ $id: 1, type: 'integer' }); throw new Error('should have throw exception'); } catch(e) { e.message .should.equal('schema id must be string'); @@ -233,7 +233,7 @@ describe('Ajv', function () { }); it('should return compiled schema by id or ref', function() { - ajv.addSchema({ id: '//e.com/int.json', type: 'integer' }); + ajv.addSchema({ $id: '//e.com/int.json', type: 'integer' }); var validate = ajv.getSchema('//e.com/int.json'); validate(1) .should.equal(true); validate('1') .should.equal(false); @@ -252,7 +252,7 @@ describe('Ajv', function () { it('should return schema fragment by ref', function() { ajv.addSchema({ - "id": "http://e.com/types.json", + "$id": "http://e.com/types.json", "definitions": { "int": { "type": "integer" }, "str": { "type": "string" } @@ -266,7 +266,7 @@ describe('Ajv', function () { it('should return schema fragment by ref with protocol-relative URIs', function() { ajv.addSchema({ - "id": "//e.com/types.json", + "$id": "//e.com/types.json", "definitions": { "int": { "type": "integer" }, "str": { "type": "string" } @@ -280,10 +280,10 @@ describe('Ajv', function () { it('should return schema fragment by id', function() { ajv.addSchema({ - "id": "http://e.com/types.json", + "$id": "http://e.com/types.json", "definitions": { - "int": { "id": "#int", "type": "integer" }, - "str": { "id": "#str", "type": "string" } + "int": { "$id": "#int", "type": "integer" }, + "str": { "$id": "#str", "type": "string" } } }); @@ -310,7 +310,7 @@ describe('Ajv', function () { }); it('should remove schema by id', function() { - var schema = { id: '//e.com/int.json', type: 'integer' } + var schema = { $id: '//e.com/int.json', type: 'integer' } , str = stableStringify(schema); ajv.addSchema(schema); @@ -333,11 +333,11 @@ describe('Ajv', function () { }); it('should remove schema with id by schema object', function() { - var schema = { id: '//e.com/int.json', type: 'integer' } + var schema = { $id: '//e.com/int.json', type: 'integer' } , str = stableStringify(schema); ajv.addSchema(schema); ajv._cache.get(str) .should.be.an('object'); - ajv.removeSchema({ id: '//e.com/int.json', type: 'integer' }); + ajv.removeSchema({ $id: '//e.com/int.json', type: 'integer' }); // should.not.exist(ajv.getSchema('//e.com/int.json')); should.not.exist(ajv._cache.get(str)); }); @@ -350,7 +350,7 @@ describe('Ajv', function () { }); it('should remove all schemas but meta-schemas if called without an arguments', function() { - var schema1 = { id: '//e.com/int.json', type: 'integer' } + var schema1 = { $id: '//e.com/int.json', type: 'integer' } , str1 = stableStringify(schema1); ajv.addSchema(schema1); ajv._cache.get(str1) .should.be.an('object'); @@ -366,12 +366,12 @@ describe('Ajv', function () { }); it('should remove all schemas but meta-schemas with key/id matching pattern', function() { - var schema1 = { id: '//e.com/int.json', type: 'integer' } + var schema1 = { $id: '//e.com/int.json', type: 'integer' } , str1 = stableStringify(schema1); ajv.addSchema(schema1); ajv._cache.get(str1) .should.be.an('object'); - var schema2 = { id: 'str.json', type: 'string' } + var schema2 = { $id: 'str.json', type: 'string' } , str2 = stableStringify(schema2); ajv.addSchema(schema2, '//e.com/str.json'); ajv._cache.get(str2) .should.be.an('object'); diff --git a/spec/async.spec.js b/spec/async.spec.js index 5363e32dd..bcac5a459 100644 --- a/spec/async.spec.js +++ b/spec/async.spec.js @@ -10,50 +10,50 @@ describe('compileAsync method', function() { var SCHEMAS = { "http://example.com/object.json": { - "id": "http://example.com/object.json", + "$id": "http://example.com/object.json", "properties": { "a": { "type": "string" }, "b": { "$ref": "int2plus.json" } } }, "http://example.com/int2plus.json": { - "id": "http://example.com/int2plus.json", + "$id": "http://example.com/int2plus.json", "type": "integer", "minimum": 2 }, "http://example.com/tree.json": { - "id": "http://example.com/tree.json", + "$id": "http://example.com/tree.json", "type": "array", "items": { "$ref": "leaf.json" } }, "http://example.com/leaf.json": { - "id": "http://example.com/leaf.json", + "$id": "http://example.com/leaf.json", "properties": { "name": { "type": "string" }, "subtree": { "$ref": "tree.json" } } }, "http://example.com/recursive.json": { - "id": "http://example.com/recursive.json", + "$id": "http://example.com/recursive.json", "properties": { "b": { "$ref": "parent.json" } }, "required": ["b"] }, "http://example.com/invalid.json": { - "id": "http://example.com/recursive.json", + "$id": "http://example.com/recursive.json", "properties": { "invalid": { "type": "number" } }, "required": "invalid" }, "http://example.com/foobar.json": { - "id": "http://example.com/foobar.json", + "$id": "http://example.com/foobar.json", "$schema": "http://example.com/foobar_meta.json", "myFooBar": "foo" }, "http://example.com/foobar_meta.json": { - "id": "http://example.com/foobar_meta.json", + "$id": "http://example.com/foobar_meta.json", "type": "object", "properties": { "myFooBar": { @@ -71,7 +71,7 @@ describe('compileAsync method', function() { it('should compile schemas loading missing schemas with options.loadSchema function', function() { var schema = { - "id": "http://example.com/parent.json", + "$id": "http://example.com/parent.json", "properties": { "a": { "$ref": "object.json" } } @@ -87,7 +87,7 @@ describe('compileAsync method', function() { it('should compile schemas loading missing schemas and return function via callback', function (done) { var schema = { - "id": "http://example.com/parent.json", + "$id": "http://example.com/parent.json", "properties": { "a": { "$ref": "object.json" } } @@ -105,7 +105,7 @@ describe('compileAsync method', function() { it('should correctly load schemas when missing reference has JSON path', function() { var schema = { - "id": "http://example.com/parent.json", + "$id": "http://example.com/parent.json", "properties": { "a": { "$ref": "object.json#/properties/b" } } @@ -121,7 +121,7 @@ describe('compileAsync method', function() { it('should correctly compile with remote schemas that have mutual references', function() { var schema = { - "id": "http://example.com/root.json", + "$id": "http://example.com/root.json", "properties": { "tree": { "$ref": "tree.json" } } @@ -143,7 +143,7 @@ describe('compileAsync method', function() { it('should correctly compile with remote schemas that reference the compiled schema', function() { var schema = { - "id": "http://example.com/parent.json", + "$id": "http://example.com/parent.json", "properties": { "a": { "$ref": "recursive.json" } } @@ -161,7 +161,7 @@ describe('compileAsync method', function() { it('should resolve reference containing "properties" segment with the same property (issue #220)', function() { var schema = { - "id": "http://example.com/parent.json", + "$id": "http://example.com/parent.json", "properties": { "a": { "$ref": "object.json#/properties/a" @@ -206,7 +206,7 @@ describe('compileAsync method', function() { it('should return compiled schema on the next tick if there are no references (#51)', function() { var schema = { - "id": "http://example.com/int2plus.json", + "$id": "http://example.com/int2plus.json", "type": "integer", "minimum": 2 }; @@ -238,7 +238,7 @@ describe('compileAsync method', function() { it('should queue calls so only one compileAsync executes at a time (#52)', function() { var schema = { - "id": "http://example.com/parent.json", + "$id": "http://example.com/parent.json", "properties": { "a": { "$ref": "object.json" } } @@ -261,7 +261,7 @@ describe('compileAsync method', function() { it('should throw exception if loadSchema is not passed', function (done) { var schema = { - "id": "http://example.com/int2plus.json", + "$id": "http://example.com/int2plus.json", "type": "integer", "minimum": 2 }; @@ -281,7 +281,7 @@ describe('compileAsync method', function() { describe('should return error via callback', function() { it('if passed schema is invalid', function (done) { var invalidSchema = { - "id": "http://example.com/int2plus.json", + "$id": "http://example.com/int2plus.json", "type": "integer", "minimum": "invalid" }; @@ -290,7 +290,7 @@ describe('compileAsync method', function() { it('if loaded schema is invalid', function (done) { var schema = { - "id": "http://example.com/parent.json", + "$id": "http://example.com/parent.json", "properties": { "a": { "$ref": "invalid.json" } } @@ -300,7 +300,7 @@ describe('compileAsync method', function() { it('if required schema is loaded but the reference cannot be resolved', function (done) { var schema = { - "id": "http://example.com/parent.json", + "$id": "http://example.com/parent.json", "properties": { "a": { "$ref": "object.json#/definitions/not_found" } } @@ -310,7 +310,7 @@ describe('compileAsync method', function() { it('if loadSchema returned error', function (done) { var schema = { - "id": "http://example.com/parent.json", + "$id": "http://example.com/parent.json", "properties": { "a": { "$ref": "object.json" } } @@ -346,7 +346,7 @@ describe('compileAsync method', function() { describe('should return error via promise', function() { it('if passed schema is invalid', function() { var invalidSchema = { - "id": "http://example.com/int2plus.json", + "$id": "http://example.com/int2plus.json", "type": "integer", "minimum": "invalid" }; @@ -355,7 +355,7 @@ describe('compileAsync method', function() { it('if loaded schema is invalid', function() { var schema = { - "id": "http://example.com/parent.json", + "$id": "http://example.com/parent.json", "properties": { "a": { "$ref": "invalid.json" } } @@ -365,7 +365,7 @@ describe('compileAsync method', function() { it('if required schema is loaded but the reference cannot be resolved', function() { var schema = { - "id": "http://example.com/parent.json", + "$id": "http://example.com/parent.json", "properties": { "a": { "$ref": "object.json#/definitions/not_found" } } diff --git a/spec/async_validate.spec.js b/spec/async_validate.spec.js index 039483a0c..04d47cece 100644 --- a/spec/async_validate.spec.js +++ b/spec/async_validate.spec.js @@ -291,7 +291,7 @@ describe('async schemas, formats and keywords', function() { it('should validate refs between two async schemas', function() { var schemaObj = { - id: 'http://e.com/obj.json#', + $id: 'http://e.com/obj.json#', $async: true, type: 'object', properties: { @@ -300,7 +300,7 @@ describe('async schemas, formats and keywords', function() { }; var schemaWord = { - id: 'http://e.com/word.json#', + $id: 'http://e.com/word.json#', $async: true, anyOf: [ { @@ -316,7 +316,7 @@ describe('async schemas, formats and keywords', function() { it('should fail compilation if sync schema references async schema', function() { var schema = { - id: 'http://e.com/obj.json#', + $id: 'http://e.com/obj.json#', type: 'object', properties: { foo: { $ref: 'http://e.com/word.json#' } @@ -324,7 +324,7 @@ describe('async schemas, formats and keywords', function() { }; var schemaWord = { - id: 'http://e.com/word.json#', + $id: 'http://e.com/word.json#', $async: true, anyOf: [ { @@ -345,7 +345,7 @@ describe('async schemas, formats and keywords', function() { ajv.compile(schema); }); - schema.id = 'http://e.com/obj2.json#'; + schema.$id = 'http://e.com/obj2.json#'; schema.$async = true; ajv.compile(schema); diff --git a/spec/coercion.spec.js b/spec/coercion.spec.js index 0aa5abf99..42f2f586d 100644 --- a/spec/coercion.spec.js +++ b/spec/coercion.spec.js @@ -365,10 +365,10 @@ describe('Type coercion', function () { }; var schemaRecursive2 = { - id: 'http://e.com/schema.json#', + $id: 'http://e.com/schema.json#', definitions: { foo: { - id: 'http://e.com/foo.json#', + $id: 'http://e.com/foo.json#', type: [ 'object', 'number' ], properties: { foo: { $ref: '#' } diff --git a/spec/issues.spec.js b/spec/issues.spec.js index 6b0d1b9bf..ee181bb94 100644 --- a/spec/issues.spec.js +++ b/spec/issues.spec.js @@ -19,7 +19,7 @@ describe('issue #8: schema with shared references', function() { }; var schema = { - id: 'obj.json#', + $id: 'obj.json#', type: 'object', properties: { foo: propertySchema, @@ -51,7 +51,7 @@ describe('issue #50: references with "definitions"', function () { var ajv = new Ajv; ajv[method]({ - id: 'http://example.com/test/person.json#', + $id: 'http://example.com/test/person.json#', definitions: { name: { type: 'string' } }, @@ -62,7 +62,7 @@ describe('issue #50: references with "definitions"', function () { }); ajv[method]({ - id: 'http://example.com/test/employee.json#', + $id: 'http://example.com/test/employee.json#', type: 'object', properties: { person: { $ref: '/test/person.json#' }, @@ -109,7 +109,7 @@ describe('issue #182, NaN validation', function() { describe('issue #204, options schemas and $data used together', function() { it('should use v5 metaschemas by default', function() { var ajv = new Ajv({ - schemas: [{id: 'str', type: 'string'}], + schemas: [{$id: 'str', type: 'string'}], $data: true }); @@ -184,7 +184,7 @@ describe('issue #210, mutual recursive $refs that are schema fragments', functio var ajv = new Ajv; ajv.addSchema({ - "id" : "foo", + "$id" : "foo", "definitions": { "bar": { "properties": { @@ -200,7 +200,7 @@ describe('issue #210, mutual recursive $refs that are schema fragments', functio }); ajv.addSchema({ - "id" : "boo", + "$id" : "boo", "type": "object", "required": ["quux"], "properties": { @@ -218,7 +218,7 @@ describe('issue #210, mutual recursive $refs that are schema fragments', functio var ajv = new Ajv; ajv.addSchema({ - "id" : "foo", + "$id" : "foo", "definitions": { "bar": { "properties": { @@ -234,7 +234,7 @@ describe('issue #210, mutual recursive $refs that are schema fragments', functio }); ajv.addSchema({ - "id" : "boo", + "$id" : "boo", "definitions": { "buu": { "type": "object", @@ -257,15 +257,15 @@ describe('issue #210, mutual recursive $refs that are schema fragments', functio describe('issue #240, mutually recursive fragment refs reference a common schema', function() { var apiSchema = { $schema: 'http://json-schema.org/draft-07/schema#', - id: 'schema://api.schema#', + $id: 'schema://api.schema#', resource: { - id: '#resource', + $id: '#resource', properties: { id: { type: 'string' } } }, resourceIdentifier: { - id: '#resource_identifier', + $id: '#resource_identifier', properties: { id: { type: 'string' }, type: { type: 'string' } @@ -275,7 +275,7 @@ describe('issue #240, mutually recursive fragment refs reference a common schema var domainSchema = { $schema: 'http://json-schema.org/draft-07/schema#', - id: 'schema://domain.schema#', + $id: 'schema://domain.schema#', properties: { data: { oneOf: [ @@ -291,7 +291,7 @@ describe('issue #240, mutually recursive fragment refs reference a common schema var librarySchema = { $schema: 'http://json-schema.org/draft-07/schema#', - id: 'schema://library.schema#', + $id: 'schema://library.schema#', properties: { name: { type: 'string' }, links: { @@ -305,7 +305,7 @@ describe('issue #240, mutually recursive fragment refs reference a common schema }, definitions: { resource_identifier: { - id: '#resource_identifier', + $id: '#resource_identifier', allOf: [ { properties: { @@ -323,7 +323,7 @@ describe('issue #240, mutually recursive fragment refs reference a common schema var catalogItemSchema = { $schema: 'http://json-schema.org/draft-07/schema#', - id: 'schema://catalog_item.schema#', + $id: 'schema://catalog_item.schema#', properties: { name: { type: 'string' }, links: { @@ -334,7 +334,7 @@ describe('issue #240, mutually recursive fragment refs reference a common schema }, definitions: { resource_identifier: { - id: '#resource_identifier', + $id: '#resource_identifier', allOf: [ { properties: { @@ -352,7 +352,7 @@ describe('issue #240, mutually recursive fragment refs reference a common schema var catalogItemResourceIdentifierSchema = { $schema: 'http://json-schema.org/draft-07/schema#', - id: 'schema://catalog_item_resource_identifier.schema#', + $id: 'schema://catalog_item_resource_identifier.schema#', allOf: [ { properties: { @@ -382,7 +382,7 @@ describe('issue #240, mutually recursive fragment refs reference a common schema var librarySchema = { $schema: 'http://json-schema.org/draft-07/schema#', - id: 'schema://library.schema#', + $id: 'schema://library.schema#', properties: { name: { type: 'string' }, links: { @@ -396,7 +396,7 @@ describe('issue #240, mutually recursive fragment refs reference a common schema }, definitions: { resource_identifier: { - id: '#resource_identifier', + $id: '#resource_identifier', allOf: [ { properties: { @@ -414,7 +414,7 @@ describe('issue #240, mutually recursive fragment refs reference a common schema var catalogItemSchema = { $schema: 'http://json-schema.org/draft-07/schema#', - id: 'schema://catalog_item.schema#', + $id: 'schema://catalog_item.schema#', properties: { name: { type: 'string' }, links: { @@ -425,7 +425,7 @@ describe('issue #240, mutually recursive fragment refs reference a common schema }, definitions: { resource_identifier: { - id: '#resource_identifier', + $id: '#resource_identifier', allOf: [ { properties: { @@ -463,7 +463,6 @@ describe('issue #240, mutually recursive fragment refs reference a common schema describe('issue #259, support validating [meta-]schemas against themselves', function() { it('should add schema before validation if "id" is the same as "$schema"', function() { var ajv = new Ajv; - ajv.addMetaSchema(require('../lib/refs/json-schema-draft-04.json')); var hyperSchema = require('./remotes/hyper-schema.json'); ajv.addMetaSchema(hyperSchema); }); diff --git a/spec/json-schema.spec.js b/spec/json-schema.spec.js index bde31a2d9..2ceb6403c 100644 --- a/spec/json-schema.spec.js +++ b/spec/json-schema.spec.js @@ -12,11 +12,11 @@ var remoteRefs = { 'http://localhost:1234/folder/folderInteger.json': require('./JSON-Schema-Test-Suite/remotes/folder/folderInteger.json'), }; -runTest(getAjvInstances(options, {meta: false}), 4, typeof window == 'object' +runTest(getAjvInstances(options, {meta: false, schemaId: 'id'}), 4, typeof window == 'object' ? suite(require('./JSON-Schema-Test-Suite/tests/draft4/{**/,}*.json', {mode: 'list'})) : './JSON-Schema-Test-Suite/tests/draft4/{**/,}*.json'); -runTest(getAjvInstances(options), 6, typeof window == 'object' +runTest(getAjvInstances(options, {schemaId: 'auto'}), 6, typeof window == 'object' ? suite(require('./JSON-Schema-Test-Suite/tests/draft6/{**/,}*.json', {mode: 'list'})) : './JSON-Schema-Test-Suite/tests/draft6/{**/,}*.json'); diff --git a/spec/options.spec.js b/spec/options.spec.js index 5ae18dd26..616a4e280 100644 --- a/spec/options.spec.js +++ b/spec/options.spec.js @@ -11,7 +11,7 @@ describe('Ajv Options', function () { var ajv = new Ajv({ removeAdditional: 'all' }); ajv.addSchema({ - id: '//test/fooBar', + $id: '//test/fooBar', properties: { foo: { type: 'string' }, bar: { type: 'string' } } }); @@ -30,7 +30,7 @@ describe('Ajv Options', function () { var ajv = new Ajv({ removeAdditional: true }); ajv.addSchema({ - id: '//test/fooBar', + $id: '//test/fooBar', properties: { foo: { type: 'string' }, bar: { type: 'string' } }, additionalProperties: false }); @@ -50,7 +50,7 @@ describe('Ajv Options', function () { var ajv = new Ajv({ removeAdditional: 'failing' }); ajv.addSchema({ - id: '//test/fooBar', + $id: '//test/fooBar', properties: { foo: { type: 'string' }, bar: { type: 'string' } }, additionalProperties: { type: 'string' } }); @@ -66,7 +66,7 @@ describe('Ajv Options', function () { object.should.not.have.property('fizz'); ajv.addSchema({ - id: '//test/fooBar2', + $id: '//test/fooBar2', properties: { foo: { type: 'string' }, bar: { type: 'string' } }, additionalProperties: { type: 'string', pattern: '^to-be-', maxLength: 10 } }); @@ -347,9 +347,9 @@ describe('Ajv Options', function () { it('should add schemas from array', function() { var ajv = new Ajv({ schemas: [ - { id: 'int', type: 'integer' }, - { id: 'str', type: 'string' }, - { id: 'obj', properties: { int: { $ref: 'int' }, str: { $ref: 'str' } } } + { $id: 'int', type: 'integer' }, + { $id: 'str', type: 'string' }, + { $id: 'obj', properties: { int: { $ref: 'int' }, str: { $ref: 'str' } } } ]}); ajv.validate('obj', { int: 123, str: 'foo' }) .should.equal(true); @@ -669,26 +669,26 @@ describe('Ajv Options', function () { describe('compile and validate', function() { it('should add schema', function() { - var schema = { id: 'str', type: 'string' }; + var schema = { $id: 'str', type: 'string' }; var validate = ajv.compile(schema); validate('abc') .should.equal(true); validate(1) .should.equal(false); ajv.getSchema('str') .should.equal(validate); - schema = { id: 'int', type: 'integer' }; + schema = { $id: 'int', type: 'integer' }; ajv.validate(schema, 1) .should.equal(true); ajv.validate(schema, 'abc') .should.equal(false); ajv.getSchema('int') .should.be.a('function'); }); it('should throw with duplicate ID', function() { - ajv.compile({ id: 'str', type: 'string' }); + ajv.compile({ $id: 'str', type: 'string' }); should.throw(function() { - ajv.compile({ id: 'str', minLength: 2 }); + ajv.compile({ $id: 'str', minLength: 2 }); }); - var schema = { id: 'int', type: 'integer' }; - var schema2 = { id: 'int', minimum: 0 }; + var schema = { $id: 'int', type: 'integer' }; + var schema2 = { $id: 'int', minimum: 0 }; ajv.validate(schema, 1) .should.equal(true); should.throw(function() { ajv.validate(schema2, 1); @@ -708,26 +708,26 @@ describe('Ajv Options', function () { describe('compile and validate', function() { it('should NOT add schema', function() { - var schema = { id: 'str', type: 'string' }; + var schema = { $id: 'str', type: 'string' }; var validate = ajv.compile(schema); validate('abc') .should.equal(true); validate(1) .should.equal(false); should.equal(ajv.getSchema('str'), undefined); - schema = { id: 'int', type: 'integer' }; + schema = { $id: 'int', type: 'integer' }; ajv.validate(schema, 1) .should.equal(true); ajv.validate(schema, 'abc') .should.equal(false); should.equal(ajv.getSchema('int'), undefined); }); it('should NOT throw with duplicate ID', function() { - ajv.compile({ id: 'str', type: 'string' }); + ajv.compile({ $id: 'str', type: 'string' }); should.not.throw(function() { - ajv.compile({ id: 'str', minLength: 2 }); + ajv.compile({ $id: 'str', minLength: 2 }); }); - var schema = { id: 'int', type: 'integer' }; - var schema2 = { id: 'int', minimum: 0 }; + var schema = { $id: 'int', type: 'integer' }; + var schema2 = { $id: 'int', minimum: 0 }; ajv.validate(schema, 1) .should.equal(true); should.not.throw(function() { ajv.validate(schema2, 1) .should.equal(true); @@ -1134,21 +1134,20 @@ describe('Ajv Options', function () { describe('schemaId', function() { - describe('= undefined (default)', function() { - it('should throw if both id and $id are available and different', function() { - var ajv = new Ajv; + describe('= "$id" (default)', function() { + it('should use $id and ignore id', function() { + test(new Ajv); + test(new Ajv({schemaId: '$id'})); - ajv.compile({ - id: 'mySchema', - $id: 'mySchema' - }); + function test(ajv) { + ajv.addSchema({ $id: 'mySchema1', type: 'string' }); + var validate = ajv.getSchema('mySchema1'); + validate('foo') .should.equal(true); + validate(1) .should.equal(false); - should.throw(function() { - ajv.compile({ - id: 'mySchema1', - $id: 'mySchema2' - }); - }); + validate = ajv.compile({ id: 'mySchema2', type: 'string' }); + should.not.exist(ajv.getSchema('mySchema2')); + } }); }); @@ -1166,17 +1165,35 @@ describe('Ajv Options', function () { }); }); - describe('= "$id"', function() { - it('should use $id and ignore id', function() { - var ajv = new Ajv({schemaId: '$id'}); + describe('= "auto"', function() { + it('should use both id and $id', function() { + var ajv = new Ajv({schemaId: 'auto'}); ajv.addSchema({ $id: 'mySchema1', type: 'string' }); var validate = ajv.getSchema('mySchema1'); validate('foo') .should.equal(true); validate(1) .should.equal(false); - validate = ajv.compile({ id: 'mySchema2', type: 'string' }); - should.not.exist(ajv.getSchema('mySchema2')); + ajv.addSchema({ id: 'mySchema2', type: 'string' }); + validate = ajv.getSchema('mySchema2'); + validate('foo') .should.equal(true); + validate(1) .should.equal(false); + }); + + it('should throw if both id and $id are available and different', function() { + var ajv = new Ajv({schemaId: 'auto'}); + + ajv.compile({ + id: 'mySchema', + $id: 'mySchema' + }); + + should.throw(function() { + ajv.compile({ + id: 'mySchema1', + $id: 'mySchema2' + }); + }); }); }); }); diff --git a/spec/remotes/hyper-schema.json b/spec/remotes/hyper-schema.json index fee0cfb7e..349ee2de9 100644 --- a/spec/remotes/hyper-schema.json +++ b/spec/remotes/hyper-schema.json @@ -1,167 +1,69 @@ { - "$schema": "http://json-schema.org/draft-04/hyper-schema#", - "id": "http://json-schema.org/draft-04/hyper-schema#", + "$schema": "http://json-schema.org/draft-07/hyper-schema#", + "$id": "http://json-schema.org/draft-07/hyper-schema#", "title": "JSON Hyper-Schema", - "allOf": [ - { - "$ref": "http://json-schema.org/draft-04/schema#" - } - ], - "properties": { - "additionalItems": { - "anyOf": [ - { - "type": "boolean" - }, - { - "$ref": "#" - } - ] - }, - "additionalProperties": { - "anyOf": [ - { - "type": "boolean" - }, + "definitions": { + "schemaArray": { + "allOf": [ + { "$ref": "http://json-schema.org/draft-07/schema#/definitions/schemaArray" }, { - "$ref": "#" + "items": { "$ref": "#" } } ] - }, + } + }, + "allOf": [ { "$ref": "http://json-schema.org/draft-07/schema#" } ], + "properties": { + "additionalItems": { "$ref": "#" }, + "additionalProperties": { "$ref": "#"}, "dependencies": { "additionalProperties": { "anyOf": [ - { - "$ref": "#" - }, - { - "type": "array" - } + { "$ref": "#" }, + { "type": "array" } ] } }, "items": { "anyOf": [ - { - "$ref": "#" - }, - { - "$ref": "#/definitions/schemaArray" - } + { "$ref": "#" }, + { "$ref": "#/definitions/schemaArray" } ] }, "definitions": { - "additionalProperties": { - "$ref": "#" - } + "additionalProperties": { "$ref": "#" } }, "patternProperties": { - "additionalProperties": { - "$ref": "#" - } + "additionalProperties": { "$ref": "#" } }, "properties": { - "additionalProperties": { - "$ref": "#" - } - }, - "allOf": { - "$ref": "#/definitions/schemaArray" - }, - "anyOf": { - "$ref": "#/definitions/schemaArray" - }, - "oneOf": { - "$ref": "#/definitions/schemaArray" - }, - "not": { - "$ref": "#" - }, + "additionalProperties": { "$ref": "#" } + }, + "if": {"$ref": "#"}, + "then": {"$ref": "#"}, + "else": {"$ref": "#"}, + "allOf": { "$ref": "#/definitions/schemaArray" }, + "anyOf": { "$ref": "#/definitions/schemaArray" }, + "oneOf": { "$ref": "#/definitions/schemaArray" }, + "not": { "$ref": "#" }, + "contains": { "$ref": "#" }, + "propertyNames": { "$ref": "#" }, - "links": { - "type": "array", - "items": { - "$ref": "#/definitions/linkDescription" - } - }, - "fragmentResolution": { - "type": "string" - }, - "media": { - "type": "object", - "properties": { - "type": { - "description": "A media type, as described in RFC 2046", - "type": "string" - }, - "binaryEncoding": { - "description": "A content encoding scheme, as described in RFC 2045", - "type": "string" - } - } - }, - "pathStart": { - "description": "Instances' URIs must start with this value for this schema to apply to them", + "base": { "type": "string", - "format": "uri" - } - }, - "definitions": { - "schemaArray": { + "format": "uri-template" + }, + "links": { "type": "array", "items": { - "$ref": "#" - } - }, - "linkDescription": { - "title": "Link Description Object", - "type": "object", - "required": [ "href", "rel" ], - "properties": { - "href": { - "description": "a URI template, as defined by RFC 6570, with the addition of the $, ( and ) characters for pre-processing", - "type": "string" - }, - "rel": { - "description": "relation to the target resource of the link", - "type": "string" - }, - "title": { - "description": "a title for the link", - "type": "string" - }, - "targetSchema": { - "description": "JSON Schema describing the link target", - "$ref": "#" - }, - "mediaType": { - "description": "media type (as defined by RFC 2046) describing the link target", - "type": "string" - }, - "method": { - "description": "method for requesting the target of the link (e.g. for HTTP this might be \"GET\" or \"DELETE\")", - "type": "string" - }, - "encType": { - "description": "The media type in which to submit data along with the request", - "type": "string", - "default": "application/json" - }, - "schema": { - "description": "Schema describing the data to submit along with the request", - "$ref": "#" - } + "$ref": "http://json-schema.org/draft-07/hyper-schema#/links" } } }, "links": [ { "rel": "self", - "href": "{+id}" - }, - { - "rel": "full", - "href": "{+($ref)}" + "href": "{+%24id}" } ] } diff --git a/spec/resolve.spec.js b/spec/resolve.spec.js index 2db89565c..1aa00177e 100644 --- a/spec/resolve.spec.js +++ b/spec/resolve.spec.js @@ -20,28 +20,28 @@ describe('resolve', function () { it('should resolve ids in schema', function() { // Example from http://json-schema.org/latest/json-schema-core.html#anchor29 var schema = { - "id": "http://x.y.z/rootschema.json#", + "$id": "http://x.y.z/rootschema.json#", "schema1": { - "id": "#foo", + "$id": "#foo", "description": "schema1", "type": "integer" }, "schema2": { - "id": "otherschema.json", + "$id": "otherschema.json", "description": "schema2", "nested": { - "id": "#bar", + "$id": "#bar", "description": "nested", "type": "string" }, "alsonested": { - "id": "t/inner.json#a", + "$id": "t/inner.json#a", "description": "alsonested", "type": "boolean" } }, "schema3": { - "id": "some://where.else/completely#", + "$id": "some://where.else/completely#", "description": "schema3", "type": "null" }, @@ -65,13 +65,13 @@ describe('resolve', function () { it('should throw if the same id resolves to two different schemas', function() { instances.forEach(function (ajv) { ajv.compile({ - "id": "http://example.com/1.json", + "$id": "http://example.com/1.json", "type": "integer" }); should.throw(function() { ajv.compile({ "additionalProperties": { - "id": "http://example.com/1.json", + "$id": "http://example.com/1.json", "type": "string" } }); @@ -80,11 +80,11 @@ describe('resolve', function () { should.throw(function() { ajv.compile({ "items": { - "id": "#int", + "$id": "#int", "type": "integer" }, "additionalProperties": { - "id": "#int", + "$id": "#int", "type": "string" } }); @@ -98,7 +98,7 @@ describe('resolve', function () { it('should resolve fragment', function() { instances.forEach(function(ajv) { var schema = { - "id": "//e.com/types", + "$id": "//e.com/types", "definitions": { "int": { "type": "integer" } } @@ -166,7 +166,7 @@ describe('resolve', function () { instances.forEach(function (ajv) { try { ajv.compile({ - "id": opts.baseId, + "$id": opts.baseId, "properties": { "a": { "$ref": opts.ref } } }); } catch(e) { @@ -180,14 +180,14 @@ describe('resolve', function () { describe('inline referenced schemas without refs in them', function() { var schemas = [ - { id: 'http://e.com/obj.json#', + { $id: 'http://e.com/obj.json#', properties: { a: { $ref: 'int.json#' } } }, - { id: 'http://e.com/int.json#', + { $id: 'http://e.com/int.json#', type: 'integer', minimum: 2, maximum: 4 }, - { id: 'http://e.com/obj1.json#', + { $id: 'http://e.com/obj1.json#', definitions: { int: { type: 'integer', minimum: 2, maximum: 4 } }, properties: { a: { $ref: '#/definitions/int' } } }, - { id: 'http://e.com/list.json#', + { $id: 'http://e.com/list.json#', items: { $ref: 'obj.json#' } } ]; @@ -220,7 +220,7 @@ describe('resolve', function () { var schemaMessage = { $schema: "http://json-schema.org/draft-07/schema#", - id: "http://e.com/message.json#", + $id: "http://e.com/message.json#", type: "object", required: ["header"], properties: { @@ -236,7 +236,7 @@ describe('resolve', function () { // header schema var schemaHeader = { $schema: "http://json-schema.org/draft-07/schema#", - id: "http://e.com/header.json#", + $id: "http://e.com/header.json#", type: "object", properties: { version: { @@ -270,14 +270,14 @@ describe('resolve', function () { var v = ajv.getSchema('http://e.com/message.json#'); v(validMessage) .should.equal(true); - v.schema.id .should.equal('http://e.com/message.json#'); + v.schema.$id .should.equal('http://e.com/message.json#'); v(invalidMessage) .should.equal(false); v.errors .should.have.length(1); - v.schema.id .should.equal('http://e.com/message.json#'); + v.schema.$id .should.equal('http://e.com/message.json#'); v(validMessage) .should.equal(true); - v.schema.id .should.equal('http://e.com/message.json#'); + v.schema.$id .should.equal('http://e.com/message.json#'); }); diff --git a/spec/schema-tests.spec.js b/spec/schema-tests.spec.js index bcca994c9..e6e5c3ac0 100644 --- a/spec/schema-tests.spec.js +++ b/spec/schema-tests.spec.js @@ -6,7 +6,7 @@ var jsonSchemaTest = require('json-schema-test') , suite = require('./browser_test_suite') , after = require('./after_test'); -var instances = getAjvInstances(options, {unknownFormats: ['allowedUnknown']}); +var instances = getAjvInstances(options, {schemaId: 'auto', unknownFormats: ['allowedUnknown']}); var remoteRefs = { 'http://localhost:1234/integer.json': require('./JSON-Schema-Test-Suite/remotes/integer.json'), From 846e080c1781bcd2532bfabb274b6167a20753f1 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Dec 2017 13:25:19 +0000 Subject: [PATCH 41/48] test: add JSON-Schema-Test-Suite draft-07 tests --- spec/json-schema.spec.js | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/spec/json-schema.spec.js b/spec/json-schema.spec.js index 327691727..f71a0049d 100644 --- a/spec/json-schema.spec.js +++ b/spec/json-schema.spec.js @@ -13,14 +13,24 @@ var remoteRefs = { 'http://localhost:1234/name.json': require('./JSON-Schema-Test-Suite/remotes/name.json') }; +var SKIP = { + 4: ['optional/zeroTerminatedFloats'], + 7: ['optional/content', 'optional/format'] +}; + + runTest(getAjvInstances(options, {meta: false, schemaId: 'id'}), 4, typeof window == 'object' ? suite(require('./JSON-Schema-Test-Suite/tests/draft4/{**/,}*.json', {mode: 'list'})) : './JSON-Schema-Test-Suite/tests/draft4/{**/,}*.json'); -runTest(getAjvInstances(options, {meta: false, schemaId: 'auto', format: 'full'}), 6, typeof window == 'object' +runTest(getAjvInstances(options, {meta: false, format: 'full'}), 6, typeof window == 'object' ? suite(require('./JSON-Schema-Test-Suite/tests/draft6/{**/,}*.json', {mode: 'list'})) : './JSON-Schema-Test-Suite/tests/draft6/{**/,}*.json'); +runTest(getAjvInstances(options, {format: 'full'}), 7, typeof window == 'object' + ? suite(require('./JSON-Schema-Test-Suite/tests/draft7/{**/,}*.json', {mode: 'list'})) + : './JSON-Schema-Test-Suite/tests/draft7/{**/,}*.json'); + function runTest(instances, draft, tests) { instances.forEach(function (ajv) { @@ -40,17 +50,8 @@ function runTest(instances, draft, tests) { jsonSchemaTest(instances, { description: 'JSON-Schema Test Suite draft-0' + draft + ': ' + instances.length + ' ajv instances with different options', suites: {tests: tests}, - only: [ - // 'type', 'not', 'allOf', 'anyOf', 'oneOf', 'enum', - // 'maximum', 'minimum', 'multipleOf', 'maxLength', 'minLength', 'pattern', - // 'properties', 'patternProperties', 'additionalProperties', - // 'dependencies', 'required', - // 'maxProperties', 'minProperties', 'maxItems', 'minItems', - // 'items', 'additionalItems', 'uniqueItems', - // 'optional/format', 'optional/bignum', - // 'ref', 'refRemote', 'definitions', - ], - skip: ['optional/zeroTerminatedFloats'], + only: [], + skip: SKIP[draft], assert: require('./chai').assert, afterError: after.error, afterEach: after.each, From a38c000e723ea393a63853d485e64a4513e50ee8 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Dec 2017 17:40:01 +0000 Subject: [PATCH 42/48] test: update JSON-Schema-Test-Suite --- package.json | 2 +- spec/JSON-Schema-Test-Suite | 2 +- spec/json-schema.spec.js | 8 +++++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index a0c762066..dd3bb79c0 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "if-node-version": "^1.0.0", "js-beautify": "^1.7.3", "jshint": "^2.9.4", - "json-schema-test": "^1.3.0", + "json-schema-test": "^2.0.0", "karma": "^1.0.0", "karma-chrome-launcher": "^2.0.0", "karma-mocha": "^1.1.1", diff --git a/spec/JSON-Schema-Test-Suite b/spec/JSON-Schema-Test-Suite index 870b9f7eb..86f965e53 160000 --- a/spec/JSON-Schema-Test-Suite +++ b/spec/JSON-Schema-Test-Suite @@ -1 +1 @@ -Subproject commit 870b9f7ebd35a238ba9d609424db51c7ca027d4a +Subproject commit 86f965e53dda0b6c57e70ddd726243e1e061cf84 diff --git a/spec/json-schema.spec.js b/spec/json-schema.spec.js index f71a0049d..3b1a3db1b 100644 --- a/spec/json-schema.spec.js +++ b/spec/json-schema.spec.js @@ -15,7 +15,13 @@ var remoteRefs = { var SKIP = { 4: ['optional/zeroTerminatedFloats'], - 7: ['optional/content', 'optional/format'] + 7: [ + 'optional/content', + 'format/idn-email', + 'format/idn-hostname', + 'format/iri', + 'format/iri-reference' + ] }; From d25f31b1bb9095229756b87851ef44cf10328045 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Dec 2017 18:01:27 +0000 Subject: [PATCH 43/48] fix: "fast" versions of uri/uri-reference formats to pass JSON-Schema-Test-Suite draft-06/07 --- lib/compile/formats.js | 4 ++-- spec/json-schema.spec.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/compile/formats.js b/lib/compile/formats.js index ab63a81a8..97aca1a15 100644 --- a/lib/compile/formats.js +++ b/lib/compile/formats.js @@ -36,8 +36,8 @@ formats.fast = { time: /^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d:\d\d)?$/i, 'date-time': /^\d\d\d\d-[0-1]\d-[0-3]\d[t\s](?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d:\d\d)$/i, // uri: https://github.com/mafintosh/is-my-json-valid/blob/master/formats.js - uri: /^(?:[a-z][a-z0-9+-.]*)(?::|\/)\/?[^\s]*$/i, - 'uri-reference': /^(?:(?:[a-z][a-z0-9+-.]*:)?\/\/)?[^\s]*$/i, + uri: /^(?:[a-z][a-z0-9+-.]*:)(?:\/?\/)?[^\s]*$/i, + 'uri-reference': /^(?:(?:[a-z][a-z0-9+-.]*:)?\/?\/)?(?:[^\\\s#][^\s#]*)?(?:#[^\\\s]*)?$/i, 'uri-template': URITEMPLATE, url: URL, // email (sources from jsen validator): diff --git a/spec/json-schema.spec.js b/spec/json-schema.spec.js index 3b1a3db1b..48bce9618 100644 --- a/spec/json-schema.spec.js +++ b/spec/json-schema.spec.js @@ -29,11 +29,11 @@ runTest(getAjvInstances(options, {meta: false, schemaId: 'id'}), 4, typeof windo ? suite(require('./JSON-Schema-Test-Suite/tests/draft4/{**/,}*.json', {mode: 'list'})) : './JSON-Schema-Test-Suite/tests/draft4/{**/,}*.json'); -runTest(getAjvInstances(options, {meta: false, format: 'full'}), 6, typeof window == 'object' +runTest(getAjvInstances(options, {meta: false}), 6, typeof window == 'object' ? suite(require('./JSON-Schema-Test-Suite/tests/draft6/{**/,}*.json', {mode: 'list'})) : './JSON-Schema-Test-Suite/tests/draft6/{**/,}*.json'); -runTest(getAjvInstances(options, {format: 'full'}), 7, typeof window == 'object' +runTest(getAjvInstances(options), 7, typeof window == 'object' ? suite(require('./JSON-Schema-Test-Suite/tests/draft7/{**/,}*.json', {mode: 'list'})) : './JSON-Schema-Test-Suite/tests/draft7/{**/,}*.json'); From 41074e8e6a97d6ef9edf9f89eaab91beda5ee766 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Dec 2017 18:31:03 +0000 Subject: [PATCH 44/48] test: change test schemas to draft-07 --- spec/remotes/bar.json | 2 +- spec/remotes/buu.json | 2 +- spec/remotes/first.json | 2 +- spec/remotes/foo.json | 2 +- spec/remotes/node.json | 2 +- spec/remotes/scope_change.json | 6 +- spec/remotes/second.json | 2 +- spec/remotes/tree.json | 2 +- spec/schema-tests.spec.js | 7 +- .../13_root_ref_in_ref_in_remote_ref.json | 21 ++--- .../issues/14_ref_in_remote_ref_with_id.json | 34 +++----- .../issues/170_ref_and_id_in_sibling.json | 36 ++++----- spec/tests/issues/1_ids_in_refs.json | 6 +- .../issues/27_1_recursive_raml_schema.json | 2 +- spec/tests/issues/27_recursive_reference.json | 6 +- .../issues/63_id_property_not_in_schema.json | 18 ++--- ...70_1_recursive_hash_ref_in_remote_ref.json | 20 ++--- spec/tests/issues/70_swagger_schema.json | 80 +++++++++---------- spec/tests/schemas/advanced.json | 4 +- spec/tests/schemas/basic.json | 5 +- spec/tests/schemas/complex.json | 16 ++-- spec/tests/schemas/complex3.json | 18 ++--- 22 files changed, 126 insertions(+), 167 deletions(-) diff --git a/spec/remotes/bar.json b/spec/remotes/bar.json index 96ad644f7..cc3039186 100644 --- a/spec/remotes/bar.json +++ b/spec/remotes/bar.json @@ -1,4 +1,4 @@ { - "id": "http://localhost:1234/bar.json", + "$id": "http://localhost:1234/bar.json", "type": "string" } diff --git a/spec/remotes/buu.json b/spec/remotes/buu.json index 3f0f9af20..f3d905c4a 100644 --- a/spec/remotes/buu.json +++ b/spec/remotes/buu.json @@ -1,5 +1,5 @@ { - "id": "http://localhost:1234/buu.json", + "$id": "http://localhost:1234/buu.json", "definitions": { "buu": { "type": "object", diff --git a/spec/remotes/first.json b/spec/remotes/first.json index 7d414663f..9fdb8d486 100644 --- a/spec/remotes/first.json +++ b/spec/remotes/first.json @@ -1,4 +1,4 @@ { - "id": "http://localhost:1234/first.json", + "$id": "http://localhost:1234/first.json", "type": "string" } diff --git a/spec/remotes/foo.json b/spec/remotes/foo.json index b9a00dd42..9e565666f 100644 --- a/spec/remotes/foo.json +++ b/spec/remotes/foo.json @@ -1,5 +1,5 @@ { - "id": "http://localhost:1234/foo.json", + "$id": "http://localhost:1234/foo.json", "type": "object", "properties": { "bar": { "$ref": "bar.json" } diff --git a/spec/remotes/node.json b/spec/remotes/node.json index d592c85ed..41120c1ef 100644 --- a/spec/remotes/node.json +++ b/spec/remotes/node.json @@ -1,5 +1,5 @@ { - "id": "http://localhost:1234/node.json", + "$id": "http://localhost:1234/node.json", "description": "node", "type": "object", "properties": { diff --git a/spec/remotes/scope_change.json b/spec/remotes/scope_change.json index 74ccbe853..67991f971 100644 --- a/spec/remotes/scope_change.json +++ b/spec/remotes/scope_change.json @@ -1,8 +1,8 @@ { - "id": "http://localhost:1234/scope_change.json", + "$id": "http://localhost:1234/scope_change.json", "definitions": { "foo": { - "id": "http://localhost:1234/scope_foo.json", + "$id": "http://localhost:1234/scope_foo.json", "definitions": { "bar": { "type": "string" @@ -10,7 +10,7 @@ } }, "baz": { - "id": "folder/", + "$id": "folder/", "type": "array", "items": { "$ref": "folderInteger.json" }, "bar": { diff --git a/spec/remotes/second.json b/spec/remotes/second.json index 06b4bc63e..56e32ebfd 100644 --- a/spec/remotes/second.json +++ b/spec/remotes/second.json @@ -1,5 +1,5 @@ { - "id": "http://localhost:1234/second.json", + "$id": "http://localhost:1234/second.json", "type": "object", "properties": { "first": { "$ref": "first.json" } diff --git a/spec/remotes/tree.json b/spec/remotes/tree.json index ba9d4cbf8..39df56141 100644 --- a/spec/remotes/tree.json +++ b/spec/remotes/tree.json @@ -1,5 +1,5 @@ { - "id": "http://localhost:1234/tree.json", + "$id": "http://localhost:1234/tree.json", "description": "tree of nodes", "type": "object", "properties": { diff --git a/spec/schema-tests.spec.js b/spec/schema-tests.spec.js index e6e5c3ac0..a07605710 100644 --- a/spec/schema-tests.spec.js +++ b/spec/schema-tests.spec.js @@ -6,7 +6,7 @@ var jsonSchemaTest = require('json-schema-test') , suite = require('./browser_test_suite') , after = require('./after_test'); -var instances = getAjvInstances(options, {schemaId: 'auto', unknownFormats: ['allowedUnknown']}); +var instances = getAjvInstances(options, {unknownFormats: ['allowedUnknown']}); var remoteRefs = { 'http://localhost:1234/integer.json': require('./JSON-Schema-Test-Suite/remotes/integer.json'), @@ -36,9 +36,7 @@ jsonSchemaTest(instances, { ? suite(require('./tests/{**/,}*.json', {mode: 'list'})) : './tests/{**/,}*.json' }, - only: [ - // 'schemas/complex', 'schemas/basic', 'schemas/advanced', - ], + only: [], assert: require('./chai').assert, afterError: after.error, afterEach: after.each, @@ -48,7 +46,6 @@ jsonSchemaTest(instances, { function addRemoteRefs(ajv) { - ajv.addMetaSchema(require('../lib/refs/json-schema-draft-04.json')); for (var id in remoteRefs) ajv.addSchema(remoteRefs[id], id); ajv.addSchema(remoteRefsWithIds); } diff --git a/spec/tests/issues/13_root_ref_in_ref_in_remote_ref.json b/spec/tests/issues/13_root_ref_in_ref_in_remote_ref.json index 5947cc590..a55625abb 100644 --- a/spec/tests/issues/13_root_ref_in_ref_in_remote_ref.json +++ b/spec/tests/issues/13_root_ref_in_ref_in_remote_ref.json @@ -1,22 +1,13 @@ [ { "description": "root ref in remote ref (#13)", - "schemas": [ - { - "id": "http://localhost:1234/issue13_1", - "type": "object", - "properties": { - "name": { "$ref": "name.json#/definitions/orNull" } - } - }, - { - "$id": "http://localhost:1234/issue13_2", - "type": "object", - "properties": { - "name": { "$ref": "name.json#/definitions/orNull" } - } + "schema": { + "$id": "http://localhost:1234/issue13", + "type": "object", + "properties": { + "name": { "$ref": "name.json#/definitions/orNull" } } - ], + }, "tests": [ { "description": "string is valid", diff --git a/spec/tests/issues/14_ref_in_remote_ref_with_id.json b/spec/tests/issues/14_ref_in_remote_ref_with_id.json index ae9f3f802..57b474a4b 100644 --- a/spec/tests/issues/14_ref_in_remote_ref_with_id.json +++ b/spec/tests/issues/14_ref_in_remote_ref_with_id.json @@ -1,18 +1,11 @@ [ { "description": "ref in remote ref with ids", - "schemas": [ - { - "id": "http://localhost:1234/issue14a_1.json", - "type": "array", - "items": { "$ref": "foo.json" } - }, - { - "$id": "http://localhost:1234/issue14a_2.json", - "type": "array", - "items": { "$ref": "foo.json" } - } - ], + "schema": { + "$id": "http://localhost:1234/issue14a.json", + "type": "array", + "items": { "$ref": "foo.json" } + }, "tests": [ { "description": "string is valid", @@ -36,18 +29,11 @@ }, { "description": "remote ref in definitions in remote ref with ids (#14)", - "schemas": [ - { - "id": "http://localhost:1234/issue14b_1.json", - "type": "array", - "items": { "$ref": "buu.json#/definitions/buu" } - }, - { - "$id": "http://localhost:1234/issue14b_2.json", - "type": "array", - "items": { "$ref": "buu.json#/definitions/buu" } - } - ], + "schema": { + "$id": "http://localhost:1234/issue14b.json", + "type": "array", + "items": { "$ref": "buu.json#/definitions/buu" } + }, "tests": [ { "description": "string is valid", diff --git a/spec/tests/issues/170_ref_and_id_in_sibling.json b/spec/tests/issues/170_ref_and_id_in_sibling.json index 6bfecd562..bb08d8294 100644 --- a/spec/tests/issues/170_ref_and_id_in_sibling.json +++ b/spec/tests/issues/170_ref_and_id_in_sibling.json @@ -3,12 +3,12 @@ "description": "sibling property has id (#170)", "schemas": [ { - "$schema": "http://json-schema.org/draft-04/schema#", - "id": "http://example.com/base_object_1", + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://example.com/base_object_1", "type": "object", "properties": { "title": { - "id": "http://example.com/title", + "$id": "http://example.com/title", "type": "string" }, "file": { "$ref": "#/definitions/file-entry" } @@ -56,12 +56,12 @@ "description": "sibling item has id", "schemas": [ { - "$schema": "http://json-schema.org/draft-04/schema#", - "id": "http://example.com/base_array_1", + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://example.com/base_array_1", "type": "array", "items": [ { - "id": "http://example.com/0", + "$id": "http://example.com/0", "type": "string" }, { "$ref": "#/definitions/file-entry" } @@ -103,11 +103,11 @@ "description": "sibling schema in anyOf has id", "schemas": [ { - "$schema": "http://json-schema.org/draft-04/schema#", - "id": "http://example.com/base_anyof_1", + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://example.com/base_anyof_1", "anyOf": [ { - "id": "http://example.com/0", + "$id": "http://example.com/0", "type": "number" }, { "$ref": "#/definitions/def" } @@ -153,11 +153,11 @@ "description": "sibling schema in oneOf has id", "schemas": [ { - "$schema": "http://json-schema.org/draft-04/schema#", - "id": "http://example.com/base_oneof_1", + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://example.com/base_oneof_1", "oneOf": [ { - "id": "http://example.com/0", + "$id": "http://example.com/0", "type": "number" }, { "$ref": "#/definitions/def" } @@ -203,11 +203,11 @@ "description": "sibling schema in allOf has id", "schemas": [ { - "$schema": "http://json-schema.org/draft-04/schema#", - "id": "http://example.com/base_allof_1", + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://example.com/base_allof_1", "allOf": [ { - "id": "http://example.com/0", + "$id": "http://example.com/0", "type": "string", "maxLength": 3 }, @@ -250,12 +250,12 @@ "description": "sibling schema in dependencies has id", "schemas": [ { - "$schema": "http://json-schema.org/draft-04/schema#", - "id": "http://example.com/base_dependencies_1", + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "http://example.com/base_dependencies_1", "type": "object", "dependencies": { "foo": { - "id": "http://example.com/foo", + "$id": "http://example.com/foo", "required": [ "bar" ] }, "bar": { "$ref": "#/definitions/def" } diff --git a/spec/tests/issues/1_ids_in_refs.json b/spec/tests/issues/1_ids_in_refs.json index 5c6eed1f1..579e7d072 100644 --- a/spec/tests/issues/1_ids_in_refs.json +++ b/spec/tests/issues/1_ids_in_refs.json @@ -5,7 +5,7 @@ { "definitions": { "int": { - "id": "#int", + "$id": "#int", "type": "integer" } }, @@ -30,10 +30,10 @@ "description": "IDs in refs with root id", "schemas": [ { - "id": "http://example.com/int_1.json", + "$id": "http://example.com/int_1.json", "definitions": { "int": { - "id": "#int", + "$id": "#int", "type": "integer" } }, diff --git a/spec/tests/issues/27_1_recursive_raml_schema.json b/spec/tests/issues/27_1_recursive_raml_schema.json index 51706e3b6..e2146b2d9 100644 --- a/spec/tests/issues/27_1_recursive_raml_schema.json +++ b/spec/tests/issues/27_1_recursive_raml_schema.json @@ -3,7 +3,7 @@ "description": "JSON Schema for a standard RAML object (#27)", "schema": { "title": "A JSON Schema for a standard RAML object", - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "required": [ "title" diff --git a/spec/tests/issues/27_recursive_reference.json b/spec/tests/issues/27_recursive_reference.json index f2059cccb..6da686979 100644 --- a/spec/tests/issues/27_recursive_reference.json +++ b/spec/tests/issues/27_recursive_reference.json @@ -3,12 +3,12 @@ "description": "Recursive reference (#27)", "schemas": [ { - "$schema": "http://json-schema.org/draft-04/schema#", - "id": "testrec_1", + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "testrec_1", "type": "object", "properties": { "layout": { - "id": "layout", + "$id": "layout", "type": "object", "properties": { "layout": { "type": "string" }, diff --git a/spec/tests/issues/63_id_property_not_in_schema.json b/spec/tests/issues/63_id_property_not_in_schema.json index 371b18c25..ce425cc3f 100644 --- a/spec/tests/issues/63_id_property_not_in_schema.json +++ b/spec/tests/issues/63_id_property_not_in_schema.json @@ -1,20 +1,12 @@ [ { "description": "id property in referenced schema in object that is not a schema (#63)", - "schemas": [ - { - "type" : "object", - "properties": { - "title": { "$ref": "http://json-schema.org/draft-04/schema#/properties/title" } - } - }, - { - "type" : "object", - "properties": { - "title": { "$ref": "http://json-schema.org/draft-07/schema#/properties/title" } - } + "schema": { + "type" : "object", + "properties": { + "title": { "$ref": "http://json-schema.org/draft-07/schema#/properties/title" } } - ], + }, "tests": [ { "description": "empty object is valid", diff --git a/spec/tests/issues/70_1_recursive_hash_ref_in_remote_ref.json b/spec/tests/issues/70_1_recursive_hash_ref_in_remote_ref.json index 8a53325f9..651e3d019 100644 --- a/spec/tests/issues/70_1_recursive_hash_ref_in_remote_ref.json +++ b/spec/tests/issues/70_1_recursive_hash_ref_in_remote_ref.json @@ -2,7 +2,7 @@ { "description": "hash ref inside hash ref in remote ref (#70, was passing)", "schema": { - "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeIntegerDefault0" }, "tests": [ { "data": 1, "valid": true, "description": "positive integer is valid" }, @@ -12,16 +12,10 @@ }, { "description": "hash ref inside hash ref in remote ref with id (#70, was passing)", - "schemas": [ - { - "id": "http://example.com/my_schema_1.json", - "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" - }, - { - "$id": "http://example.com/my_schema_2.json", - "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeIntegerDefault0" - } - ], + "schema": { + "$id": "http://example.com/my_schema_2.json", + "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeIntegerDefault0" + }, "tests": [ { "data": 1, "valid": true, "description": "positive integer is valid" }, { "data": 0, "valid": true, "description": "zero is valid" }, @@ -32,7 +26,7 @@ "description": "local hash ref with remote hash ref without inner hash ref (#70, was passing)", "schema": { "definitions": { - "a": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" } + "a": { "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeIntegerDefault0" } }, "properties": { "b": { "$ref": "#/definitions/a" } @@ -48,7 +42,7 @@ "description": "local hash ref with remote hash ref that has inner hash ref (#70)", "schema": { "definitions": { - "a": { "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" } + "a": { "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeIntegerDefault0" } }, "properties": { "b": { "$ref": "#/definitions/a" } diff --git a/spec/tests/issues/70_swagger_schema.json b/spec/tests/issues/70_swagger_schema.json index 0c97800bc..3c9a2c80d 100644 --- a/spec/tests/issues/70_swagger_schema.json +++ b/spec/tests/issues/70_swagger_schema.json @@ -3,8 +3,8 @@ "description": "Swagger api schema does not compile (#70)", "schema": { "title": "A JSON Schema for Swagger 2.0 API.", - "id": "http://swagger.io/v2/schema.json#", - "$schema": "http://json-schema.org/draft-04/schema#", + "$id": "http://swagger.io/v2/schema.json#", + "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "required": [ "swagger", @@ -935,58 +935,58 @@ "type": "string" }, "title": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/title" + "$ref": "http://json-schema.org/draft-07/schema#/properties/title" }, "description": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/description" + "$ref": "http://json-schema.org/draft-07/schema#/properties/description" }, "default": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/default" + "$ref": "http://json-schema.org/draft-07/schema#/properties/default" }, "multipleOf": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf" + "$ref": "http://json-schema.org/draft-07/schema#/properties/multipleOf" }, "maximum": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/maximum" + "$ref": "http://json-schema.org/draft-07/schema#/properties/maximum" }, "exclusiveMaximum": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum" + "$ref": "http://json-schema.org/draft-07/schema#/properties/exclusiveMaximum" }, "minimum": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/minimum" + "$ref": "http://json-schema.org/draft-07/schema#/properties/minimum" }, "exclusiveMinimum": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum" + "$ref": "http://json-schema.org/draft-07/schema#/properties/exclusiveMinimum" }, "maxLength": { - "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeInteger" }, "minLength": { - "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeIntegerDefault0" }, "pattern": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/pattern" + "$ref": "http://json-schema.org/draft-07/schema#/properties/pattern" }, "maxItems": { - "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeInteger" }, "minItems": { - "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeIntegerDefault0" }, "uniqueItems": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems" + "$ref": "http://json-schema.org/draft-07/schema#/properties/uniqueItems" }, "maxProperties": { - "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeInteger" }, "minProperties": { - "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeIntegerDefault0" }, "required": { - "$ref": "http://json-schema.org/draft-04/schema#/definitions/stringArray" + "$ref": "http://json-schema.org/draft-07/schema#/definitions/stringArray" }, "enum": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/enum" + "$ref": "http://json-schema.org/draft-07/schema#/properties/enum" }, "additionalProperties": { "anyOf": [ @@ -1000,7 +1000,7 @@ "default": {} }, "type": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/type" + "$ref": "http://json-schema.org/draft-07/schema#/properties/type" }, "items": { "anyOf": [ @@ -1064,16 +1064,16 @@ "type": "string" }, "title": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/title" + "$ref": "http://json-schema.org/draft-07/schema#/properties/title" }, "description": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/description" + "$ref": "http://json-schema.org/draft-07/schema#/properties/description" }, "default": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/default" + "$ref": "http://json-schema.org/draft-07/schema#/properties/default" }, "required": { - "$ref": "http://json-schema.org/draft-04/schema#/definitions/stringArray" + "$ref": "http://json-schema.org/draft-07/schema#/definitions/stringArray" }, "type": { "type": "string", @@ -1534,49 +1534,49 @@ "default": "csv" }, "title": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/title" + "$ref": "http://json-schema.org/draft-07/schema#/properties/title" }, "description": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/description" + "$ref": "http://json-schema.org/draft-07/schema#/properties/description" }, "default": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/default" + "$ref": "http://json-schema.org/draft-07/schema#/properties/default" }, "multipleOf": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/multipleOf" + "$ref": "http://json-schema.org/draft-07/schema#/properties/multipleOf" }, "maximum": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/maximum" + "$ref": "http://json-schema.org/draft-07/schema#/properties/maximum" }, "exclusiveMaximum": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMaximum" + "$ref": "http://json-schema.org/draft-07/schema#/properties/exclusiveMaximum" }, "minimum": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/minimum" + "$ref": "http://json-schema.org/draft-07/schema#/properties/minimum" }, "exclusiveMinimum": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/exclusiveMinimum" + "$ref": "http://json-schema.org/draft-07/schema#/properties/exclusiveMinimum" }, "maxLength": { - "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeInteger" }, "minLength": { - "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeIntegerDefault0" }, "pattern": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/pattern" + "$ref": "http://json-schema.org/draft-07/schema#/properties/pattern" }, "maxItems": { - "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveInteger" + "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeInteger" }, "minItems": { - "$ref": "http://json-schema.org/draft-04/schema#/definitions/positiveIntegerDefault0" + "$ref": "http://json-schema.org/draft-07/schema#/definitions/nonNegativeIntegerDefault0" }, "uniqueItems": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/uniqueItems" + "$ref": "http://json-schema.org/draft-07/schema#/properties/uniqueItems" }, "enum": { - "$ref": "http://json-schema.org/draft-04/schema#/properties/enum" + "$ref": "http://json-schema.org/draft-07/schema#/properties/enum" }, "jsonReference": { "type": "object", diff --git a/spec/tests/schemas/advanced.json b/spec/tests/schemas/advanced.json index 12e94fc1f..7c6cea9ad 100644 --- a/spec/tests/schemas/advanced.json +++ b/spec/tests/schemas/advanced.json @@ -2,7 +2,7 @@ { "description": "advanced schema from z-schema benchmark (https://github.com/zaggino/z-schema)", "schema": { - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "/": { "$ref": "#/definitions/entry" } @@ -14,7 +14,7 @@ "required": [ "/" ], "definitions": { "entry": { - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "description": "schema for an fstab entry", "type": "object", "required": [ "storage" ], diff --git a/spec/tests/schemas/basic.json b/spec/tests/schemas/basic.json index 1ab3c4a71..a65006908 100644 --- a/spec/tests/schemas/basic.json +++ b/spec/tests/schemas/basic.json @@ -2,7 +2,7 @@ { "description": "basic schema from z-schema benchmark (https://github.com/zaggino/z-schema)", "schema": { - "$schema": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-07/schema#", "title": "Product set", "type": "array", "items": { @@ -18,8 +18,7 @@ }, "price": { "type": "number", - "minimum": 0, - "exclusiveMinimum": true + "exclusiveMinimum": 0 }, "tags": { "type": "array", diff --git a/spec/tests/schemas/complex.json b/spec/tests/schemas/complex.json index 68a95619b..46d15446b 100644 --- a/spec/tests/schemas/complex.json +++ b/spec/tests/schemas/complex.json @@ -7,17 +7,17 @@ "minItems": 1, "definitions": { "base58": { - "id": "#base58", + "$id": "#base58", "type": "string", "pattern": "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$" }, "hex": { - "id": "#hex", + "$id": "#hex", "type": "string", "pattern": "^[0123456789A-Fa-f]+$" }, "tx_id": { - "id": "#tx_id", + "$id": "#tx_id", "allOf": [ { "$ref": "#hex" }, { @@ -27,7 +27,7 @@ ] }, "address": { - "id": "#address", + "$id": "#address", "allOf": [ { "$ref": "#base58" }, { @@ -37,7 +37,7 @@ ] }, "signature": { - "id": "#signature", + "$id": "#signature", "allOf": [ { "$ref": "#hex" }, { @@ -47,7 +47,7 @@ ] }, "transaction": { - "id": "#transaction", + "$id": "#transaction", "additionalProperties": false, "required": [ "metadata", @@ -108,7 +108,7 @@ } }, "input": { - "id": "#input", + "$id": "#input", "type": "object", "additionalProperties": false, "required": [ @@ -134,7 +134,7 @@ } }, "output": { - "id": "#output", + "$id": "#output", "type": "object", "additionalProperties": false, "required": [ diff --git a/spec/tests/schemas/complex3.json b/spec/tests/schemas/complex3.json index 13d6375df..1591d7814 100644 --- a/spec/tests/schemas/complex3.json +++ b/spec/tests/schemas/complex3.json @@ -2,23 +2,23 @@ { "description": "complex schema from jsck benchmark (https://github.com/pandastrike/jsck)", "schema": { - "id": "http://example.com/complex3.json", + "$id": "http://example.com/complex3.json", "type": "array", "items": { "$ref": "#transaction" }, "minItems": 1, "definitions": { "base58": { - "id": "#base58", + "$id": "#base58", "type": "string", "pattern": "^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$" }, "hex": { - "id": "#hex", + "$id": "#hex", "type": "string", "pattern": "^[0123456789A-Fa-f]+$" }, "tx_id": { - "id": "#tx_id", + "$id": "#tx_id", "allOf": [ { "$ref": "#hex" }, { @@ -28,7 +28,7 @@ ] }, "address": { - "id": "#address", + "$id": "#address", "allOf": [ { "$ref": "#base58" }, { @@ -38,7 +38,7 @@ ] }, "signature": { - "id": "#signature", + "$id": "#signature", "allOf": [ { "$ref": "#hex" }, { @@ -48,7 +48,7 @@ ] }, "transaction": { - "id": "#transaction", + "$id": "#transaction", "additionalProperties": false, "required": [ "metadata", @@ -109,7 +109,7 @@ } }, "input": { - "id": "#input", + "$id": "#input", "type": "object", "additionalProperties": false, "required": [ @@ -135,7 +135,7 @@ } }, "output": { - "id": "#output", + "$id": "#output", "type": "object", "additionalProperties": false, "required": [ From 85839c278d0685f3cb329c3c43ba9111a24d003f Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Dec 2017 18:54:49 +0000 Subject: [PATCH 45/48] docs: note on using draft-04 schemas --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index daa8b6e3e..655f87166 100644 --- a/README.md +++ b/README.md @@ -20,11 +20,19 @@ The fastest JSON Schema validator for Node.js and browser with draft-07 support. [Ajv version 6.0.0-beta.0](https://github.com/epoberezkin/ajv/releases/tag/v6.0.0-beta.0) that supports draft-07 is released. It may require either migrating your schemas or updating your code (to continue using draft-04 and v5 schemas, draft-06 schemas will be supported without changes). -__Please note__: To use Ajv with draft-06 (or draft-04) schemas you need to explicitly add the meta-schema(s) to the validator instance: +__Please note__: To use Ajv with draft-06 schemas you need to explicitly add the meta-schema to the validator instance: ```javascript ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json')); -// ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); +``` + +To use Ajv with draft-04 schemas in addition to explicitely adding meta-schema you also need to use option schemaId: + +```javascript +var ajv = new Ajv({schemaId: 'id'}); +// If you want to use both draft-04 and draft-06/07 schemas: +// var ajv = new Ajv({schemaId: 'auto'}); +ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); ``` From dad7132adf7780662ca4fa3c676170e9bb1e9d9e Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sat, 2 Dec 2017 19:35:57 +0000 Subject: [PATCH 46/48] test: fix browser tests, replace id with $id --- spec/ajv.spec.js | 4 ++-- spec/async.spec.js | 2 +- spec/browser_test_suite.js | 2 ++ spec/errors.spec.js | 4 ++-- spec/options.spec.js | 4 +++- spec/tests/rules/if.json | 4 ++-- 6 files changed, 12 insertions(+), 8 deletions(-) diff --git a/spec/ajv.spec.js b/spec/ajv.spec.js index 8c5e23a84..e3bf766b7 100644 --- a/spec/ajv.spec.js +++ b/spec/ajv.spec.js @@ -27,8 +27,8 @@ describe('Ajv', function () { }); it('should cache compiled functions for the same schema', function() { - var v1 = ajv.compile({ id: '//e.com/int.json', type: 'integer', minimum: 1 }); - var v2 = ajv.compile({ id: '//e.com/int.json', minimum: 1, type: 'integer' }); + var v1 = ajv.compile({ $id: '//e.com/int.json', type: 'integer', minimum: 1 }); + var v2 = ajv.compile({ $id: '//e.com/int.json', minimum: 1, type: 'integer' }); v1 .should.equal(v2); }); diff --git a/spec/async.spec.js b/spec/async.spec.js index bcac5a459..e6fcba940 100644 --- a/spec/async.spec.js +++ b/spec/async.spec.js @@ -375,7 +375,7 @@ describe('compileAsync method', function() { it('if loadSchema returned error', function() { var schema = { - "id": "http://example.com/parent.json", + "$id": "http://example.com/parent.json", "properties": { "a": { "$ref": "object.json" } } diff --git a/spec/browser_test_suite.js b/spec/browser_test_suite.js index 91334330a..40e138c7c 100644 --- a/spec/browser_test_suite.js +++ b/spec/browser_test_suite.js @@ -2,6 +2,8 @@ module.exports = function (suite) { suite.forEach(function (file) { + if (file.name.indexOf('optional/format') == 0) + file.name = file.name.replace('optional/', ''); file.test = file.module; }); return suite; diff --git a/spec/errors.spec.js b/spec/errors.spec.js index 2c2ee4dc5..0a7a5fb57 100644 --- a/spec/errors.spec.js +++ b/spec/errors.spec.js @@ -410,7 +410,7 @@ describe('Validation errors', function () { it('"items" errors should include item index without quotes in dataPath (#48)', function() { var schema1 = { - id: 'schema1', + $id: 'schema1', type: 'array', items: { type: 'integer', @@ -445,7 +445,7 @@ describe('Validation errors', function () { shouldBeError(fullValidate.errors[1], 'minimum', '#/items/minimum', '/3', 'should be >= 10'); var schema2 = { - id: 'schema2', + $id: 'schema2', type: 'array', items: [{ minimum: 10 }, { minimum: 9 }, { minimum: 12 }] }; diff --git a/spec/options.spec.js b/spec/options.spec.js index 616a4e280..f0f949dcc 100644 --- a/spec/options.spec.js +++ b/spec/options.spec.js @@ -1153,7 +1153,9 @@ describe('Ajv Options', function () { describe('= "id"', function() { it('should use id and ignore $id', function() { - var ajv = new Ajv({schemaId: 'id'}); + var ajv = new Ajv({schemaId: 'id', meta: false}); + ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-04.json')); + ajv._opts.defaultMeta = 'http://json-schema.org/draft-04/schema#'; ajv.addSchema({ id: 'mySchema1', type: 'string' }); var validate = ajv.getSchema('mySchema1'); diff --git a/spec/tests/rules/if.json b/spec/tests/rules/if.json index a5bd307b2..87e9e7bc2 100644 --- a/spec/tests/rules/if.json +++ b/spec/tests/rules/if.json @@ -56,9 +56,9 @@ { "description": "if keyword with id in sibling subschema", "schema": { - "id": "http://example.com/base_if", + "$id": "http://example.com/base_if", "if": { - "id": "http://example.com/if", + "$id": "http://example.com/if", "minimum": 10 }, "then": { "$ref": "#/definitions/def" }, From 0cecf17b774e6129b8fe6145e21e21c488e46a2f Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 3 Dec 2017 17:35:58 +0000 Subject: [PATCH 47/48] 6.0.0-rc.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index dd3bb79c0..5a6a9da70 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.0.0-rc.0", + "version": "6.0.0-rc.1", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", From c56519b9f485ad8743c36a4d2f4767419459135e Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin Date: Sun, 7 Jan 2018 12:58:38 +0000 Subject: [PATCH 48/48] 6.0.0 --- .gitignore | 2 ++ package.json | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index e2a293b92..b7f774a0d 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,5 @@ lib/dotjs/*.js # bundles dist/ + +package-lock.json diff --git a/package.json b/package.json index 3e2e27c92..0fb67aa98 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ajv", - "version": "6.0.0-rc.1", + "version": "6.0.0", "description": "Another JSON Schema Validator", "main": "lib/ajv.js", "typings": "lib/ajv.d.ts", @@ -65,7 +65,7 @@ "json-schema-traverse": "^0.3.0" }, "devDependencies": { - "ajv-async": "^1.0.0-beta.0", + "ajv-async": "^1.0.0", "bluebird": "^3.1.5", "brfs": "^1.4.3", "browserify": "^15.0.0",